Credit Delegation

Native Credit Delegation (CD) is a new feature in Aave v2. It allows a depositor to deposit funds in the protocol to earn interest, and delegate borrowing power (i.e. their credit) to other users. The enforcement of the loan and its terms are agreed upon between the depositor and borrowers, which can be either off-chain via legal agreements or on-chain via smart contracts.

This enables:

  • The depositor (delegator) to earn extra yield on top of the yield they already earn from the protocol,

  • The borrowers (delegatees) to access an uncollateralized loan.

Follow the below steps to create your first Credit Delegation.

TL;DR: A starter example contract can be found on Kovan etherscan and Github code examples repo, with a twitter discussion here.

In the following guide, we refer to borrower and delegatee. However credit delegation allows the delegation to multiple users, so it can also be read as borrowers and delegatees.

Approving the delegation

The approveDelegation() method must be called by the depositor (delegator), approving the borrower (delegatee) a certain amount.

This is done for each debt token that needs to be delegated.

The depositor (delegator) does not need to already have deposited funds in the protocol to approveDelegation(). However, before the borrower (delegatee) executes borrow(), there must be sufficient collateral assigned to the depositor (delegator) in the protocol.

Solidity
Web3.js
Solidity
// Import relevant interfaces
import './IAaveProtocolDataProvider.sol';
import './IDebtToken.sol';
// ... beginning of your contract. Constructors etc...
// Within a relevant function in your contract:
// Get the Protocol Data Provider
IAaveProtocolDataProvider provider = IAaveProtocolDataProvider(address(INSERT_DATA_PROVIDER_ADDRESS));
// Get the relevant debt token address
(, address stableDebtTokenAddress, address variableDebtTokenAddress) = provider.getReserveTokensAddresses(INSERT_ASSET_ADDRESS);
// Relevant details for credit delegation
address borrower = address(INSERT_BORROWER_ADDRESS);
uint256 amountInWei = INSERT_DELEGATED_AMOUNT;
// For stable debt tokens
IDebtToken(stableDebtTokenAddress).approveDelegation(borrower, amountInWei);
// For variable debt tokens
IDebtToken(variableDebtTokenAddress).approveDelegation(borrower, amountInWei);
Web3.js
// Import relevant ABIs
import IAaveProtocolDataProviderABI from './IAaveProtocolDataProviderAbi.json'
import IDebtTokenABI from './IDebtToken.json'
// ... beginning of your code
// Within a relevant function in your code:
// Get the Aave Protocol Data Provider
const provider = new web3.eth.Contract(IAaveProtocolDataProviderABI, INSERT_DATA_PROVIDER_ADDRESS)
// Get the relevant debt token address
const tokenDetails = await provider.methods
.getReserveTokensAddresses(INSERT_ASSET_ADDRESS)
.call()
.catch((e) => {
throw Error(`Error getting token details: ${e.message}`)
})
// Relevant details for credit delegation
const borrower = INSERT_BORROWER_ADDRESS
const amountInWei = INSERT_DELEGATED_AMOUNT
// For stable debt tokens
const stableDebtContract = new web3.eth.Contract(IDebtTokenABI, tokenDetails.stableDebtTokenAddress)
await stableDebtContract.methods
.approveDelegation(borrower, amountInWei)
.send()
.catch((e) => {
throw Error(`Error approving delegation: ${e.message}`)
})
// For variable debt tokens
const variableDebtContract = new web3.eth.Contract(IDebtTokenABI, tokenDetails.variableDebtTokenAddress)
await variableDebtContract.methods
.approveDelegation(borrower, amountInWei)
.send()
.catch((e) => {
throw Error(`Error approving delegation: ${e.message}`)
})

Borrowing the credit

The borrower (delegatee) calls the borrow() method on the LendingPool, using the depositor's (delegator's) address in final parameter onBehalfOf.

Solidity
Web3.js
Solidity
// Import relevant interfaces
import './IAddressesProvider.sol';
import './ILendingPool.sol';
// ... beginning of your contract. Constructors etc...
// Within a relevant function in your contract:
// Get the latest LendingPool contract for the relevant market
IAddressesProvider provider = IAddressesProvider(address(INSERT_ADDRESSES_PROVIDER_ADDRESS));
ILendingPool lendingPool = ILendingPool(provider.getLendingPool());
// Borrow the relevant amount
address assetToBorrow = address(INSERT_ASSET_ADDRESS); // E.g. the address for Dai
uint256 amountToBorrowInWei = INSERT_AMOUNT; // must be equal to or less than the amount delegated to the borrower
uint256 interestRateMode = INSERT_INTEREST_RATE_MODE; // must be of the same type as the debt token that is delegated. I.e. stable = 1, variable = 2.
uint16 referralCode = INSERT_REFERRAL_CODE;
address delegatorAddress = INSERT_DELEGATOR_ADDRESS;
lendingPool.borrow(assetToBorrow, amountToBorrowInWei, interestRateMode, referralCode, delegatorAddress);
Web3.js
// Import relevant ABIs
import IAddressProviderABI from './IAddressesProviderAbi.json'
import ILendingPoolABI from './ILendingPool.json'
// ... beginning of your code
// Within a relevant function in your code:
// Get the latest LendingPool contract for the relevant market
const provider = new web3.eth.Contract(IAddressProviderABI, INSERT_ADDRESSES_PROVIDER_ADDRESS)
const lendingPoolAddress = await provider.methods
.getLendingPool()
.call()
.catch((e) => {
throw Error(`Error getting lendingPool address: ${e.message}`)
})
const lendingPoolContract = new web3.eth.Contract(ILendingPoolABI, lendingPoolAddress)
// Borrow the relevant amount
const assetToBorrow = INSERT_ASSET_ADDRESS; // E.g. the address for Dai
const amountToBorrowInWei = INSERT_AMOUNT; // must be equal to or less than the amount delegated to the borrower
const interestRateMode = INSERT_INTERST_RATE_MODE; // must be of the same type as the debt token that is delegated. I.e. stable = 1, variable = 2.
const referralCode = INSERT_REFERRAL_CODE;
const delegatorAddress = INSERT_DELEGATOR_ADDRESS;
await lendingPoolContract.methods
.borrow(
assetToBorrow,
amountToBorrowInWei,
interestRateMode,
referralCode,
delegatorAddress
)
.send()
.catch((e) => {
throw Error(`Error borrowing: ${e.message}`)
})

Repaying the credit

The borrower (delegatee) can also call repay() at anytime to repay their uncollateralized loan, passing in the depositor's (delegator's) address as the final parameter onBehalfOf.

Solidity
Web3.js
Solidity
// Import relevant interfaces
import './IAddressesProvider.sol';
import './ILendingPool.sol';
// ... beginning of your contract. Constructors etc...
// Within a relevant function in your contract:
// Get the latest LendingPool contract for the relevant market
IAddressesProvider provider = IAddressesProvider(address(INSERT_ADDRESSES_PROVIDER_ADDRESS));
ILendingPool lendingPool = ILendingPool(provider.getLendingPool());
// Repay the relevant amount
address assetToRepay = address(INSERT_ASSET_ADDRESS); // E.g. the address for Dai
uint256 amountToRepayInWei = INSERT_AMOUNT; // must be equal to or less than the amount delegated to the borrower=
uint256 interestRateMode = INSERT_INTEREST_RATE_MODE; // must be of the same type as the debt token that is delegated. I.e. stable = 1, variable = 2.
address delegatorAddress = INSERT_DELEGATOR_ADDRESS;
lendingPool.repay(assetToRepay, amountToRepayInWei, interestRateMode, delegatorAddress);
Web3.js
// Import relevant ABIs
import IAddressProviderABI from './IAddressesProviderAbi.json'
import ILendingPoolABI from './ILendingPool.json'
// ... beginning of your code
// Within a relevant function in your code:
// Get the latest LendingPool contract for the relevant market
const provider = new web3.eth.Contract(IAddressProviderABI, INSERT_ADDRESSES_PROVIDER_ADDRESS)
const lendingPoolAddress = await provider.methods
.getLendingPool()
.call()
.catch((e) => {
throw Error(`Error getting lendingPool address: ${e.message}`)
})
const lendingPoolContract = new web3.eth.Contract(ILendingPoolABI, lendingPoolAddress)
// Borrow the relevant amount
const assetToRepay = INSERT_ASSET_ADDRESS; // E.g. the address for Dai
const amountToRepayInWei = INSERT_AMOUNT; // must be equal to or less than the amount delegated to the borrower
const delegatorAddress = INSERT_DELEGATOR_ADDRESS;
await lendingPoolContract.methods
.repay(
assetToRepay,
amountToRepayInWei,
delegatorAddress
)
.send()
.catch((e) => {
throw Error(`Error repaying: ${e.message}`)
})

Increasing the credit delegation

To increase a borrower's available credit, the approveDelegation() method should be called again, setting the total amount that the borrower (delegatee) should now have access to, including any outstanding borrow amounts they have already performed.

This is done for each debt token that requires an increase.

Solidity
Web3.js
Solidity
// Import relevant interfaces
import './IAaveProtocolDataProvider.sol';
import './IDebtToken.sol';
// ... beginning of your contract. Constructors etc...
// Within a relevant function in your contract:
// Get the Protocol Data Provider
IAaveProtocolDataProvider provider = IAaveProtocolDataProvider(address(INSERT_DATA_PROVIDER_ADDRESS));
// Get the relevant debt token address
(, address stableDebtTokenAddress, address variableDebtTokenAddress) = provider.getReserveTokensAddresses(INSERT_ASSET_ADDRESS);
// Relevant details for credit delegation
address borrower = address(INSERT_BORROWER_ADDRESS);
uint256 amountInWei = INSERT_DELEGATED_AMOUNT; // This is the total amount to be delegated (not just the increase)
// For stable debt tokens
IDebtToken(stableDebtTokenAddress).approveDelegation(borrower, amountInWei);
// For variable debt tokens
IDebtToken(variableDebtTokenAddress).approveDelegation(borrower, amountInWei);
Web3.js
// Import relevant ABIs
import IAaveProtocolDataProviderABI from './IAaveProtocolDataProviderAbi.json'
import IDebtTokenABI from './IDebtToken.json'
// ... beginning of your code
// Within a relevant function in your code:
// Get the Aave Protocol Data Provider
const provider = new web3.eth.Contract(IAaveProtocolDataProviderABI, INSERT_DATA_PROVIDER_ADDRESS)
// Get the relevant debt token address
const tokenDetails = await provider.methods
.getReserveTokensAddresses(INSERT_ASSET_ADDRESS)
.call()
.catch((e) => {
throw Error(`Error getting token details: ${e.message}`)
})
// Relevant details for credit delegation
const borrower = INSERT_BORROWER_ADDRESS
const amountInWei = INSERT_DELEGATED_AMOUNT // This is the total amount to be delegated (not just the increase)
// For stable debt tokens
const stableDebtContract = new web3.eth.Contract(IDebtTokenABI, tokenDetails.stableDebtTokenAddress)
await stableDebtContract.methods
.approveDelegation(borrower, amountInWei)
.send()
.catch((e) => {
throw Error(`Error approving delegation: ${e.message}`)
})
// For variable debt tokens
const variableDebtContract = new web3.eth.Contract(IDebtTokenABI, tokenDetails.variableDebtTokenAddress)
await variableDebtContract.methods
.approveDelegation(borrower, amountInWei)
.send()
.catch((e) => {
throw Error(`Error approving delegation: ${e.message}`)
})

Checking the amount delegated

To check the current allowance of a user, simply call borrowAllowance() passing in the depositor (delegator) and borrower (delegatee) addresses.

This is done for each debt token that is delegated.

Solidity
Web3.js
Solidity
// Import relevant interfaces
import './IAaveProtocolDataProvider.sol';
import './IDebtToken.sol';
// ... beginning of your contract. Constructors etc...
// Within a relevant function in your contract:
// Get the Protocol Data Provider
IAaveProtocolDataProvider provider = IAaveProtocolDataProvider(address(INSERT_DATA_PROVIDER_ADDRESS));
// Get the relevant debt token address
(, address stableDebtTokenAddress, address variableDebtTokenAddress) = provider.getReserveTokensAddresses(INSERT_ASSET_ADDRESS);
// Relevant details for credit delegation
address delegator = address(INSERT_DEPOSITOR_ADDRESS)
address delegatee = address(INSERT_BORROWER_ADDRESS);
// For stable debt tokens
uint256 stableAllowance = IDebtToken(stableDebtTokenAddress).borrowAllowance(delegator, delegatee);
// For variable debt tokens
uint256 variableAllowance = IDebtToken(variableDebtTokenAddress).borrowAllowance(delegator, delegatee);
Web3.js
// Import relevant ABIs
import IAaveProtocolDataProviderABI from './IAaveProtocolDataProviderAbi.json'
import IDebtTokenABI from './IDebtToken.json'
// ... beginning of your code
// Within a relevant function in your code:
// Get the Aave Protocol Data Provider
const provider = new web3.eth.Contract(IAaveProtocolDataProviderABI, INSERT_DATA_PROVIDER_ADDRESS)
// Get the relevant debt token address
const tokenDetails = await provider.methods
.getReserveTokensAddresses(INSERT_ASSET_ADDRESS)
.call()
.catch((e) => {
throw Error(`Error getting token details: ${e.message}`)
})
// Relevant details for credit delegation
const delegator = INSERT_DEPOSITOR_ADDRESS
const delegatee = INSERT_BORROWER_ADDRESS
// For stable debt tokens
const stableDebtContract = new web3.eth.Contract(IDebtTokenABI, tokenDetails.stableDebtTokenAddress)
const stableAllowance = await stableDebtContract.methods
.borrowAllowance(delegator, delegatee)
.call()
.catch((e) => {
throw Error(`Error approving delegation: ${e.message}`)
})
// For variable debt tokens
const variableDebtContract = new web3.eth.Contract(IDebtTokenABI, tokenDetails.variableDebtTokenAddress)
const variableAllowance = await variableDebtContract.methods
.borrowAllowance(delegator, delegatee)
.call()
.catch((e) => {
throw Error(`Error approving delegation: ${e.message}`)
})