Maybe α: AAVE V2 Liquidations

Here’s what I wish I knew about this weird creditor

If you have an understanding already, just skip to the bottom. Otherwise, there is info here that may help you make or improve your own bot.

Note: The open-sourcing of this research is funded by BuidlGuidl & in a general sense, Public Goods Funding. I’ll be open-sourcing my work on this topic soon on my Github, it will be accompanied by a post here, so..

Subscribe

Simply put, an AAVE V2 Liquidation is:

  • A user has previously supplied Collateral and Borrowed an asset. The borrowed asset could be the same as the supplied asset.

  • The health value of their position has moved below 1.

  • A liquidator sends a tx that repays a Borrowed (debt) asset and chooses which Collateral to receive.

The liquidator receives the Collateral plus the liquidation bonus, a percentage determined per asset by the AAVE protocol, that varies per asset.

To help collect the necessary data, AAVE offers a js package that queries its Subgraph. I personally opted to listen for events emitted from v2 Lending Pool.

Tips for reducing down to profitability:

  • We need to clear a Collateral > the debtToCover, and select whichever has the highest liquidation bonus in order to maximize profit.

  • Lodash (uniqBy, maxBy, filter) will be your bff for combing through this data.

  • We need to know exactly how much collateral we are receiving, and it is helpful to know exactly the debt to be covered.

const largestDebt: Debt = _.maxBy(
      liqParams[0],
      function (o: { debtPriceEth: number }) {
        return o.debtPriceEth;
      }
    );

    // Filter by amounts greater than debtToCover
    var collatGreaterThan: Collat[] = _.filter(
      liqParams[1],
      function (o: { collatPriceEth: number }) {
        if (o.collatPriceEth >= largestDebt.debtPriceEth / 2) {
          return o.collatPriceEth;
        }
      }
    );

    // Finds the most profitable collateral to liquidate
    const largestCollat: Collat = _.maxBy(
      collatGreaterThan,
      function (o: { liquidationBonusMultiplier: number }) {
        return o.liquidationBonusMultiplier;
      }
    );

Lodash makes combing our data EZY.

The last on this list was the most challenging to solve. AAVE didn’t seem to have any view functions to get the exact debtToCover or collatReceived, and the docs instead point you toward a set of formulas,.

Also, depending on which conversions you use via Ethers.js or otherwise, you could end up dropping some granularity along the way:

Surely, nothing could go wrong?
Surely, nothing could go wrong?

After many botched calculations, I instead deployed a provider contract that grabs these params directly from AAVE. If you’d like to use that to skip the headache of bigint calculations, here’s the ABI. I forgot to verify the contract, sorry to the eth-sdk users, but it’s pretty easy to rig this up with Typechain, and then..

const liqInfo = await liqData.liquidationCallData(
      largestCollat.collatT,
      largestDebt.debtT,
      address,
      ethers.constants.MaxUint256,
      false
    );

A call to “liquidationCallData()” returns debtToCover (at index [0]), collatToReceive (at index [1], and a string which alerts us if any errors occured at index [2].

And just like that, you know exactly how much debtToCover and collatReceived for a particular liquidation call. I’ll cover estimation and execution in the next post.

These are just tools/discoveries along my path of building software. I am in no way a financial expert, or traditionally trained in anything, and I’m most certainly an inexperienced writer. If you have questions, ask me on twitter or visit the BuidlGuidl telegram.