Lesson 12 of 19
In Progress

Implementing Liquidation and Collateral Functionality in your Code

In the previous article, we covered the concepts of liquidation and collateral and how they work in the AAVE protocol. In this article, we’ll look at how to implement these concepts in your code using the AAVE protocol’s smart contracts and libraries.

Prerequisites

Before you begin, make sure that you have completed the following tasks:

  • Set up a local AAVE test environment, as described in the “Setting up a local AAVE test environment” article.
  • Install the necessary dependencies and tools, as described in the “Installing dependencies and tools” article.
  • Familiarize yourself with the AAVE protocol’s architecture and key components, as described in the “Overview of the AAVE protocol architecture” article.

Implementing Liquidation

To implement liquidation functionality in your code, you’ll need to use the AAVE protocol’s LendingPool and LendingPoolCore smart contracts. These contracts provide a variety of methods for interacting with the AAVE protocol and managing loans and collateral.

Here are some examples of common tasks that you can perform using these contracts:

  • Check a borrower’s current collateralization ratio
  • Check whether a borrower’s loan is at risk of liquidation
  • Calculate the amount of collateral that a borrower needs to add to their loan to meet the minimum collateralization ratio
  • Trigger liquidation for a borrower’s loan

To interact with these contracts, you can use the aave-js library, which provides a simple, easy-to-use interface for working with the AAVE protocol.

Checking a Borrower’s Current Collateralization Ratio

To check a borrower’s current collateralization ratio, you can use the getBorrowerCollateralizationRatio() method of the LendingPoolCore contract. This method takes a borrower’s account address and a loan asset address as arguments and returns the borrower’s current collateralization ratio for the loan.

Here’s an example of how to use this method in your code:

const Web3 = require('web3');
const { LendingPoolCore, LendingPool } = require('aave-js');

// Connect to the local AAVE test network
const web3 = new Web3(new Web3.providers.HttpProvider('http://localhost:8545'));

// Set the contract addresses
const lendingPoolCoreAddress = '0x24a42fD28C976A61Df5D00D0599C34c4f90748c8';
const lendingPoolAddress = '0x80fB784B7eD66730e8b1DBd9820aFD29931aab03';

// Create instances of the LendingPoolCore and LendingPool contracts
const lendingPoolCore = new LendingPoolCore(web3, lendingPoolCoreAddress);
const lendingPool = new LendingPool(web3, lendingPoolAddress);

async function isLoanAtRiskOfLiquidation(borrower, loanAsset, collateralAsset) {
  return await lendingPoolCore.methods.isLoanAtRiskOfLiquidation(borrower, loanAsset, collateralAsset).call();
}

// Check whether a borrower's loan is at risk of liquidation
const borrower = '0x5E5c5B5f5d5e5F605d6C5b5c5B5f5e5E5f6B5c5D';
const loanAsset = '0x6B175474E89094C44Da98b954EedeAC495481C82';
const collateralAsset = '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2';

const isAtRisk = await isLoanAtRiskOfLiquidation(borrower, loanAsset, collateralAsset);
if (isAtRisk) {
  console.log('The borrower\'s loan is at risk of liquidation.');
} else {
  console.log('The borrower\'s loan is not at risk of liquidation.');
}

Calculating the Amount of Collateral Needed

To calculate the amount of collateral that a borrower needs to add to their loan to meet the minimum collateralization ratio, you can use the getAmountOfCollateralNeeded() method of the LendingPoolCore contract. This method takes a borrower’s account address, a loan asset address, and a collateral asset address as arguments and returns the amount of collateral that the borrower needs to add to their loan.

Here’s an example of how to use this method in your code:

const Web3 = require('web3');
const { LendingPoolCore, LendingPool } = require('aave-js');

// Connect to the local AAVE test network
const web3 = new Web3(new Web3.providers.HttpProvider('http://localhost:8545'));

// Set the contract addresses
const lendingPoolCoreAddress = '0x24a42fD28C976A61Df5D00D0599C34c4f90748c8';
const lendingPoolAddress = '0x80fB784B7eD66730e8b1DBd9820aFD29931aab03';

// Create instances of the LendingPoolCore and LendingPool contracts
const lendingPoolCore = new LendingPoolCore(web3, lendingPoolCoreAddress);
const lendingPool = new LendingPool(web3, lendingPoolAddress);

async function getAmountOfCollateralNeeded(borrower, loanAsset, collateralAsset) {
  return await lendingPoolCore.methods.getAmountOfCollateralNeeded(borrower, loanAsset, collateralAsset).call();
}

// Calculate the amount of collateral needed to meet the minimum collateralization ratio
const borrower = '0x5E5c5B5f5d5e5F605d6C5b5c5B5f5e5E5f6B5c5D';
const loanAsset = '0x6B175474E89094C44Da98b954EedeAC495481C82';
const collateralAsset = '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2';

const collateralNeeded = await getAmountOfCollateralNeeded(borrower, loanAsset, collateralAsset);
console.log(`The borrower needs to add ${collateralNeeded} ${collateralAsset} as collateral to their loan.`);

Triggering Liquidation

To trigger liquidation for a borrower’s loan, you can use the liquidateBorrower() method of the LendingPoolCore contract. This method takes a borrower’s account address, a loan asset address, and a collateral asset address as arguments and initiates the liquidation process for the borrower’s loan.

Before calling this method, you’ll need to make sure that the borrower’s loan is actually at risk of liquidation. You can use the isLoanAtRiskOfLiquidation() method, as described in the previous section, to check this.

Here’s an example of how to use the liquidateBorrower() method in your code:

const Web3 = require('web3');
const { LendingPoolCore, LendingPool } = require('aave-js');

// Connect to the local AAVE test network
const web3 = new Web3(new Web3.providers.HttpProvider('http://localhost:8545'));

// Set the contract addresses
const lendingPoolCoreAddress = '0x24a42fD28C976A61Df5D00D0599C34c4f90748c8';
const lendingPoolAddress = '0x80fB784B7eD66730e8b1DBd9820aFD29931aab03';

// Create instances of the LendingPoolCore and LendingPool contracts
const lendingPoolCore = new LendingPoolCore(web3, lendingPoolCoreAddress);
const lendingPool = new LendingPool(web3, lendingPoolAddress);

async function isLoanAtRiskOfLiquidation(borrower, loanAsset, collateralAsset) {
  return await lendingPoolCore.methods.isLoanAtRiskOfLiquidation(borrower, loanAsset, collateralAsset).call();
}

async function liquidateBorrower(borrower, loanAsset, collateralAsset) {
  return await lendingPoolCore.methods.liquidateBorrower(borrower, loanAsset, collateralAsset).send({ from: '0x5E5c5B5f5d5e5F605d6C5b5c5B5f5e5E5f6B5c5D' });
}

// Check whether a borrower's loan is at risk of liquidation
const borrower = '0x5E5c5B5f5d5e5F605d6C5b5c5B5f5e5E5f6B5c5D';
const loanAsset = '0x6B175474E89094C44Da98b954EedeAC495481C82';
const collateralAsset = '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2';

const isAtRisk = await isLoanAtRiskOfLiquidation(borrower, loanAsset, collateralAsset);
if (isAtRisk) {
  // Trigger liquidation for the borrower's loan
  await liquidateBorrower(borrower, loanAsset, collateralAsset);
  console.log('The borrower\'s loan has been liquidated.');
} else {
  console.log('The borrower\'s loan is not at risk of liquidation and cannot be liquidated.');
}

Note that triggering liquidation is a complex process that involves multiple smart contracts and can take some time to complete. It’s also important to note that liquidation is not guaranteed to succeed and may fail if the borrower is able to add enough collateral to their loan before the liquidation process is completed.

To monitor the progress of a liquidation, you can use the LiquidationCallErrored event of the LendingPoolCore contract. This event is emitted when a liquidation call has failed, either because the borrower was able to add enough collateral to their loan or because of an error in the liquidation process.

Here’s an example of how to listen for the LiquidationCallErrored event:

const Web3 = require('web3');
const { LendingPoolCore, LendingPool } = require('aave-js');

// Connect to the local AAVE test network
const web3 = new Web3(new Web3.providers.HttpProvider('http://localhost:8545'));

// Set the contract addresses
const lendingPoolCoreAddress = '0x24a42fD28C976A61Df5D00D0599C34c4f90748c8';
const lendingPoolAddress = '0x80fB784B7eD66730e8b1DBd9820aFD29931aab03';

// Create instances of the LendingPoolCore and LendingPool contracts
const lendingPoolCore = new LendingPoolCore(web3, lendingPoolCoreAddress);
const lendingPool = new LendingPool(web3, lendingPoolAddress);

// Listen for the LiquidationCallErrored event
lendingPoolCore.events.LiquidationCallErrored({}, (error, event) => {
  if (error) {
    console.error(error);
  } else {
    console.log(`Liquidation call errored for borrower ${event.returnValues.borrower} on loan asset ${event.returnValues.loanAsset} with collateral asset ${event.returnValues.collateralAsset}.`);
  }
});

Selling the Collateral

Once a liquidation is successful, the AAVE protocol will automatically sell the borrower’s collateral asset to repay the borrower’s loan. The AAVE protocol uses a dynamic auction mechanism to determine the selling price of the collateral asset, which is designed to maximize the recovery value of the loan while minimizing the impact on the market price of the collateral asset.

To monitor the progress of a collateral sale, you can use the LiquidationSucceeded event of the LendingPoolCore contract. This event is emitted when a liquidation is successfully completed and the borrower’s collateral has been sold.

Here’s an example of how to listen for the LiquidationSucceeded event:

const Web3 = require('web3');
const { LendingPoolCore, LendingPool } = require('aave-js');

// Connect to the local AAVE test network
const web3 = new Web3(new Web3.providers.HttpProvider('http://localhost:8545'));

// Set the contract addresses
const lendingPoolCoreAddress = '0x24a42fD28C976A61Df5D00D0599C34c4f90748c8';
const lendingPoolAddress = '0x80fB784B7eD66730e8b1DBd9820aFD29931aab03';

// Create instances of the LendingPoolCore and
const Web3 = require('web3');
const { LendingPoolCore, LendingPool } = require('aave-js');

// Connect to the local AAVE test network
const web3 = new Web3(new Web3.providers.HttpProvider('http://localhost:8545'));

// Set the contract addresses
const lendingPoolCoreAddress = '0x24a42fD28C976A61Df5D00D0599C34c4f90748c8';
const lendingPoolAddress = '0x80fB784B7eD66730e8b1DBd9820aFD29931aab03';

// Create instances of the LendingPoolCore and LendingPool contracts
const lendingPoolCore = new LendingPoolCore(web3, lendingPoolCoreAddress);
const lendingPool = new LendingPool(web3, lendingPoolAddress);

// Listen for the LiquidationCallErrored event
lendingPoolCore.events.LiquidationCallErrored({}, (error, event) => {
  if (error) {
    console.error(error);
  } else {
    console.log(`Liquidation call errored for borrower ${event.returnValues.borrower} on loan asset ${event.returnValues.loanAsset} with collateral asset ${event.returnValues.collateralAsset}.`);
  }
});

// Listen for the LiquidationSucceeded event
lendingPoolCore.events.LiquidationSucceeded({}, (error, event) => {
  if (error) {
    console.error(error);
  } else {
    console.log(`Liquidation succeeded for borrower ${event.returnValues.borrower} on loan asset ${event.returnValues.loanAsset} with collateral asset ${event.returnValues.collateralAsset}.`);
  }
});

Note that the collateral sale is also a complex process that involves multiple smart contracts and can take some time to complete. It’s also important to note that the collateral sale is not guaranteed to succeed and may fail if there are errors in the sale process or if the collateral asset is not being sold at a sufficient price.

In the event that the collateral sale fails, the borrower’s collateral will be returned to them and the borrower will be required to repay their loan manually. The borrower can then try to add more collateral to their loan and avoid liquidation, or they can try to negotiate a new repayment plan with the AAVE protocol.

Implementing Liquidation and Collateral Functionality in Your Code

Now that you understand the basics of liquidation and collateral in the AAVE protocol, you can start implementing these features in your code.

Here’s an example of how you can use the aave-js library to implement lending and borrowing functionality with liquidation and collateral in your code:

const Web3 = require('web3');
const { LendingPoolCore, LendingPool, ERC20 } = require('aave-js');

// Connect to the local AAVE test network
const web3 = new Web3(new Web3.providers.HttpProvider('http://localhost:8545'));

// Set the contract addresses
const lendingPoolCoreAddress = '0x24a42fD28C976A61Df5D00D0599C34c4f90748c8';
const lendingPoolAddress = '0x80fB784B7eD66730e8b1DBd9820aFD29931aab03';

// Create instances of the LendingPoolCore and LendingPool contracts
const lendingPoolCore = new LendingPoolCore(web3, lendingPoolCoreAddress);
const lendingPool = new LendingPool(web3, lendingPoolAddress);

// Set the borrower's address and the amount of the loan
const borrower = '0xB7F3989A5b5F3A5e5A5A5eF01DDA48C9d5D543B8';
const loanAmount = '100000000000000000'; // 100 DAI

// Set the loan asset and the collateral asset
const loanAsset = '0x6b175474e89094c44da98b954eedeac495271d0f'; // DAI
const collateralAsset = '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2'; // WETH

// Calculate the amount of collateral needed for the loan
const collateralAmount = await lendingPool.calculateCollateralAmount(loanAmount, loanAsset, collateralAsset);

// Approve the lendingPool to spend the collateral asset on behalf of the borrower
const collateralERC20 = new ERC20(web3, collateralAsset);
await collateralERC20.approve(lendingPoolAddress, collateralAmount, { from: borrower });

// Borrow the loan asset using the variable rate model and the collateral asset
await lendingPool.borrowVariable(loanAmount, loanAsset, collateralAmount, collateralAsset, { from: borrower });

// Check the borrower's current collateralization ratio
const collateralizationRatio = await lendingPool.getCollateralizationRatio(borrower, loanAsset, collateralAsset);
console.log(`Current collateralization ratio: ${collateralizationRatio}`);

// Check if the borrower is below the liquidation threshold
const isBelowLiquidationThreshold = await lendingPoolCore.isBelowLiquidationThreshold(borrower, loanAsset, collateralAsset);
console.log(`Is below liquidation threshold: ${isBelowLiquidationThreshold}`);

// If the borrower is below the liquidation threshold, trigger liquidation
if (isBelowLiquidationThreshold) {
  await lendingPoolCore.liquidateBorrower(borrower, loanAsset, collateralAsset, { from: borrower });
}

Note that this is just a basic example of how you can implement lending and borrowing functionality with liquidation and collateral in your code. In a real-world application, you may want to add additional error handling and safety measures to prevent errors and ensure that your application is secure and reliable.

Conclusion

In this lesson, we covered the key concepts and functionality of liquidation and collateral in the AAVE protocol. We learned about how the AAVE protocol uses these features to manage risk and ensure that borrowers can always repay their loans. We also saw how to use the aave-js library to interact with the AAVE protocol and implement lending and borrowing functionality with liquidation and collateral in our code.

By understanding these concepts and being able to implement them in your code, you can build more advanced and sophisticated applications on top of the AAVE protocol. You can use these features to build lending platforms, margin trading platforms, and other DeFi applications that require the ability to lend and borrow assets with collateral.

With this knowledge, you are now well-equipped to start building your own applications on top of the AAVE protocol and leverage the power of DeFi to create new financial products and services.

Exercises

To review these concepts, we will go through a series of exercises designed to test your understanding and apply what you have learned.

Write a function that takes a borrower’s address and a loan asset as input, and returns the borrower’s current collateralization ratio for that loan asset.

To get a borrower’s current collateralization ratio for a particular loan asset, you can use the getCollateralizationRatio() method of the LendingPool contract. Here is an example of how you can implement this function:

async function getCollateralizationRatio(borrower, loanAsset) {
  // Create an instance of the LendingPool contract
  const lendingPool = new LendingPool(web3, lendingPoolAddress);

  // Call the getCollateralizationRatio() method
  const collateralizationRatio = await lendingPool.getCollateralizationRatio(borrower, loanAsset);

  return collateralizationRatio;
}

Write a function that takes a borrower’s address, a loan asset, and a collateral asset as input, and returns the amount of collateral needed for the borrower to reach the liquidation threshold.

To calculate the amount of collateral needed for a borrower to reach the liquidation threshold, you can use the calculateAmountToReachLiquidationThreshold() method of the LendingPool contract. Here is an example of how you can implement this function:

async function getCollateralNeededForLiquidationThreshold(borrower, loanAsset, collateralAsset) {
  // Create an instance of the LendingPool contract
  const lendingPool = new LendingPool(web3, lendingPoolAddress);

  // Call the calculateAmountToReachLiquidationThreshold() method
  const collateralNeeded = await lendingPool.calculateAmountToReachLiquidationThreshold(borrower, loanAsset, collateralAsset);

  return collateralNeeded;
}

Write a function that takes a borrower’s address, a loan asset, and a collateral asset as input, and triggers liquidation for the borrower if they are below the liquidation threshold.

To trigger liquidation for a borrower if they are below the liquidation threshold, you can use the isBelowLiquidationThreshold() and liquidateBorrower() methods of the LendingPoolCore contract. Here is an example of how you can implement this function:

async function triggerLiquidation(borrower, loanAsset, collateralAsset) {
  // Create an instance of the LendingPoolCore contract
  const lendingPoolCore = new LendingPoolCore(web3, lendingPoolCoreAddress);

  // Check if the borrower is below the liquidation threshold
  const isBelowLiquidationThreshold = await lendingPoolCore.isBelowLiquidationThreshold(borrower, loanAsset, collateralAsset);

  // If the borrower is below the liquidation threshold, trigger liquidation
  if (isBelowLiquidationThreshold) {
    await lendingPoolCore.liquidateBorrower(borrower, loanAsset, collateralAsset);
  }
}

Write a function that takes a borrower’s address and a loan asset as input, and returns the total amount of fees paid by the borrower for that loan asset.

To get the total amount of fees paid by a borrower for a particular loan asset, you can use the getTotalBorrowerFees() method of the LendingPool contract. Here is an example of how you can implement this function:

async function getTotalBorrowerFees(borrower, loanAsset) {
  // Create an instance of the LendingPool contract
  const lendingPool = new LendingPool(web3, lendingPoolAddress);

  // Call the getTotalBorrowerFees() method
  const totalFees = await lendingPool.getTotalBorrowerFees(borrower, loanAsset);

  return totalFees;
}

Write a function that takes a borrower’s address and a loan asset as input, and returns the total amount of interest paid by the borrower for that loan asset.

To get the total amount of interest paid by a borrower for a particular loan asset, you can use the getTotalBorrowerInterest() method of the LendingPool contract. Here is an example of how you can implement this function:

async function getTotalBorrowerInterest(borrower, loanAsset) {
  // Create an instance of the LendingPool contract
  const lendingPool = new LendingPool(web3, lendingPoolAddress);

  // Call the getTotalBorrowerInterest() method
  const totalInterest = await lendingPool.getTotalBorrowerInterest(borrower, loanAsset);

  return totalInterest;
}