Skip to main content

Wrap up

Full code

/src/adapters/aave-v2/ethereum.ts
import {
Balance,
BalancesContext,
BaseContext,
Contract,
GetBalancesHandler,
} from "@lib/adapter";
import { resolveBalances } from "@lib/balance";
import { call } from "@lib/call";
import { getERC20BalanceOf } from "@lib/erc20";
import { multicall } from "@lib/multicall";

const abi = {
getReservesList: {
inputs: [],
name: 'getReservesList',
outputs: [{ internalType: 'address[]', name: '', type: 'address[]' }],
stateMutability: 'view',
type: 'function',
},
getReserveData: {
inputs: [{ internalType: 'address', name: 'asset', type: 'address' }],
name: 'getReserveData',
outputs: [
{
components: [
{
components: [{ internalType: 'uint256', name: 'data', type: 'uint256' }],
internalType: 'struct DataTypes.ReserveConfigurationMap',
name: 'configuration',
type: 'tuple',
},
{
internalType: 'uint128',
name: 'liquidityIndex',
type: 'uint128',
},
{
internalType: 'uint128',
name: 'variableBorrowIndex',
type: 'uint128',
},
{
internalType: 'uint128',
name: 'currentLiquidityRate',
type: 'uint128',
},
{
internalType: 'uint128',
name: 'currentVariableBorrowRate',
type: 'uint128',
},
{
internalType: 'uint128',
name: 'currentStableBorrowRate',
type: 'uint128',
},
{
internalType: 'uint40',
name: 'lastUpdateTimestamp',
type: 'uint40',
},
{
internalType: 'address',
name: 'aTokenAddress',
type: 'address',
},
{
internalType: 'address',
name: 'stableDebtTokenAddress',
type: 'address',
},
{
internalType: 'address',
name: 'variableDebtTokenAddress',
type: 'address',
},
{
internalType: 'address',
name: 'interestRateStrategyAddress',
type: 'address',
},
{ internalType: 'uint8', name: 'id', type: 'uint8' },
],
internalType: 'struct DataTypes.ReserveData',
name: '',
type: 'tuple',
},
],
stateMutability: 'view',
type: 'function',
},
getUserAccountData: {
inputs: [{ internalType: 'address', name: 'user', type: 'address' }],
name: 'getUserAccountData',
outputs: [
{
internalType: 'uint256',
name: 'totalCollateralBase',
type: 'uint256',
},
{ internalType: 'uint256', name: 'totalDebtBase', type: 'uint256' },
{
internalType: 'uint256',
name: 'availableBorrowsBase',
type: 'uint256',
},
{
internalType: 'uint256',
name: 'currentLiquidationThreshold',
type: 'uint256',
},
{ internalType: 'uint256', name: 'ltv', type: 'uint256' },
{ internalType: 'uint256', name: 'healthFactor', type: 'uint256' },
],
stateMutability: 'view',
type: 'function',
},
} as const

async function getLendingPoolContracts(ctx: BaseContext, lendingPool: Contract) {
const contracts: Contract[] = []

const reservesList = await call({
ctx,
target: lendingPool.address,
abi: abi.getReservesList,
})

const reservesDataRes = await multicall({
ctx,
calls: reservesList.map(
(reserveTokenAddress) => ({ target: lendingPool.address, params: [reserveTokenAddress] } as const),
),
abi: abi.getReserveData,
})

for (let i = 0; i < reservesDataRes.length; i++) {
const reserveDataRes = reservesDataRes[i]
if (!reserveDataRes.success) {
continue
}

const underlyingToken = reserveDataRes.input.params[0]
const aToken = reserveDataRes.output.aTokenAddress
const stableDebtToken = reserveDataRes.output.stableDebtTokenAddress
const variableDebtToken = reserveDataRes.output.variableDebtTokenAddress

contracts.push(
{
chain: ctx.chain,
address: aToken,
underlyings: [underlyingToken],
category: 'lend',
},
{
chain: ctx.chain,
address: stableDebtToken,
underlyings: [underlyingToken],
category: 'borrow',
stable: true,
},
{
chain: ctx.chain,
address: variableDebtToken,
underlyings: [underlyingToken],
category: 'borrow',
stable: false,
},
)
}

return contracts
}

async function getLendingPoolBalances(ctx: BalancesContext, contracts: Contract[]) {
const balances: Balance[] = await getERC20BalanceOf(ctx, contracts as Token[])

// use the same amount for underlyings
for (const balance of balances) {
if (balance.amount > 0n && balance.underlyings) {
balance.underlyings[0] = { ...balance.underlyings[0], amount: balance.amount }
}
}

return balances
}

const lendingPool: Contract = {
name: "Lending Pool",
address: "0x7d2768de32b0b80b7a3454c06bdac94a69ddc7a9",
chain: "ethereum",
};

export const getContracts = async (ctx: BaseContext) => {
const pools = await getLendingPoolContracts(ctx, lendingPool);

return {
contracts: {
pools,
},
};
};

export const getBalances: GetBalancesHandler<typeof getContracts> = async (
ctx,
contracts
) => {
const balances = await resolveBalances<typeof getContracts>(ctx, contracts, {
pools: getLendingPoolBalances,
});

return {
balances,
};
};

Results

To test the adapter locally, run:

pnpm run adapter aave-v2 ethereum 0x777777c9898d384f785ee44acfe945efdff5f3e0

(or any address of your choice). You should see lending and borrowing balances displayed in a table.