From 166e2f3f2e1eec7685cee3ba4a4c63e2f2cd8db7 Mon Sep 17 00:00:00 2001 From: Corey Rice Date: Thu, 12 Dec 2024 16:09:10 -0300 Subject: [PATCH] refactor: read underlying decimals to calculate exchange rate --- subgraphs/etherfi-promo/schema.graphql | 10 +++++- .../etherfi-promo/src/mappings/vToken.ts | 31 +++++++++++-------- subgraphs/etherfi-promo/src/operations/get.ts | 15 ++++++++- .../etherfi-promo/src/operations/update.ts | 5 +-- .../etherfi-promo/tests/VToken/index.test.ts | 2 +- subgraphs/etherfi-promo/tests/VToken/mocks.ts | 18 ++++++++++- 6 files changed, 62 insertions(+), 19 deletions(-) diff --git a/subgraphs/etherfi-promo/schema.graphql b/subgraphs/etherfi-promo/schema.graphql index d0316bbf..c9814948 100644 --- a/subgraphs/etherfi-promo/schema.graphql +++ b/subgraphs/etherfi-promo/schema.graphql @@ -10,8 +10,12 @@ type TVL @entity { Entity to iterate over suppliers """ type Supply @entity { - "Token Address" + "VToken Address" id: Bytes! + "Address of the asset" + underlyingAddress: Bytes! + "Decimals of the asset" + underlyingDecimals: Int! suppliers: [SupplierAccount!]! @derivedFrom(field: "token") } @@ -21,6 +25,10 @@ Entity to iterate over borrowers type Borrow @entity { "Token Address" id: Bytes! + "Address of the asset" + underlyingAddress: Bytes! + "Decimals of the asset" + underlyingDecimals: Int! borrowers: [BorrowerAccount!]! @derivedFrom(field: "token") } diff --git a/subgraphs/etherfi-promo/src/mappings/vToken.ts b/subgraphs/etherfi-promo/src/mappings/vToken.ts index a7c55c42..992c4e0b 100644 --- a/subgraphs/etherfi-promo/src/mappings/vToken.ts +++ b/subgraphs/etherfi-promo/src/mappings/vToken.ts @@ -7,27 +7,28 @@ import { getBorrow, getSupply } from '../operations/get'; import { getOrCreateBorrowerAccount, getOrCreateSupplierAccount } from '../operations/getOrCreate'; import { updateBorrowerAccount, updateSupplierAccount, updateTvl } from '../operations/update'; import exponentToBigDecimal from '../utilities/exponentToBigDecimal'; -import exponentToBigInt from '../utilities/exponentToBigInt'; export function handleMint(event: Mint): void { const minter = event.params.minter; const supplierAccount = getOrCreateSupplierAccount(minter, event.address); + const vToken = getSupply(event.address); updateSupplierAccount( minter, event.address, supplierAccount.effective_balance.plus( - event.params.mintAmount.toBigDecimal().div(exponentToBigDecimal(18)), + event.params.mintAmount.toBigDecimal().div(exponentToBigDecimal(vToken.underlyingDecimals)), ), ); } export function handleBorrow(event: Borrow): void { const borrower = event.params.borrower; + const vToken = getBorrow(event.address); getOrCreateBorrowerAccount(borrower, event.address); updateBorrowerAccount( borrower, event.address, - event.params.accountBorrows.toBigDecimal().div(exponentToBigDecimal(18)), + event.params.accountBorrows.toBigDecimal().div(exponentToBigDecimal(vToken.underlyingDecimals)), ); } @@ -37,6 +38,7 @@ export function handleTransfer(event: Transfer): void { const fromAccountAddress = event.params.from; // If the to account is the vToken address we assume it was a redeem const toAccountAddress = event.params.to; + const vToken = getSupply(event.address); if ( fromAccountAddress.notEqual(event.address) && @@ -45,25 +47,23 @@ export function handleTransfer(event: Transfer): void { ) { const vTokenContract = VTokenContract.bind(event.address); const exchangeRateMantissa = vTokenContract.exchangeRateCurrent(); + const amountUnderlying = exchangeRateMantissa .times(event.params.amount) - .div(exponentToBigInt(18)); + .toBigDecimal() + .div(exponentToBigDecimal(18 + vToken.underlyingDecimals)); const fromAccount = getOrCreateSupplierAccount(fromAccountAddress, event.address); updateSupplierAccount( fromAccountAddress, event.address, - fromAccount.effective_balance.minus( - amountUnderlying.toBigDecimal().div(exponentToBigDecimal(18)), - ), + fromAccount.effective_balance.minus(amountUnderlying), ); // To const toAccount = getOrCreateSupplierAccount(toAccountAddress, event.address); updateSupplierAccount( toAccountAddress, event.address, - toAccount.effective_balance.plus( - amountUnderlying.toBigDecimal().div(exponentToBigDecimal(18)), - ), + toAccount.effective_balance.plus(amountUnderlying), ); } } @@ -72,23 +72,28 @@ export function handleAccrueInterest(event: AccrueInterest): void { const supply = getSupply(event.address); supply.suppliers.load().forEach(supplier => { const vTokenContract = VTokenContract.bind(Address.fromBytes(supplier.token)); + const vToken = getSupply(Address.fromBytes(supplier.token)); const exchangeRateMantissa = vTokenContract.exchangeRateCurrent(); const vTokenBalance = vTokenContract.balanceOf(Address.fromBytes(supplier.address)); - const amountUnderlying = exchangeRateMantissa.times(vTokenBalance).div(exponentToBigInt(18)); - supplier.effective_balance = amountUnderlying.toBigDecimal().div(exponentToBigDecimal(18)); + const amountUnderlying = exchangeRateMantissa + .times(vTokenBalance) + .toBigDecimal() + .div(exponentToBigDecimal(18 + vToken.underlyingDecimals)); + supplier.effective_balance = amountUnderlying; supplier.save(); }); const borrow = getBorrow(event.address); borrow.borrowers.load().forEach(borrower => { const vTokenContract = VTokenContract.bind(Address.fromBytes(borrower.token)); + const vToken = getBorrow(Address.fromBytes(borrower.token)); const underlyingBorrowBalance = vTokenContract.borrowBalanceCurrent( Address.fromBytes(borrower.address), ); borrower.effective_balance = underlyingBorrowBalance .toBigDecimal() - .div(exponentToBigDecimal(18)); + .div(exponentToBigDecimal(vToken.underlyingDecimals)); borrower.save(); }); diff --git a/subgraphs/etherfi-promo/src/operations/get.ts b/subgraphs/etherfi-promo/src/operations/get.ts index fb83dc35..56fa8fc8 100644 --- a/subgraphs/etherfi-promo/src/operations/get.ts +++ b/subgraphs/etherfi-promo/src/operations/get.ts @@ -1,5 +1,5 @@ import { Address } from '@graphprotocol/graph-ts'; - +import { VToken } from '../../generated/vWeETH/VToken'; import { Borrow, BorrowerAccount, SupplierAccount, Supply, TVL } from '../../generated/schema'; import { zeroBigDecimal } from '../constants'; import { getPositionId } from '../utilities/ids'; @@ -19,6 +19,13 @@ export const getSupply = (tokenAddress: Address): Supply => { let supply = Supply.load(tokenAddress); if (!supply) { supply = new Supply(tokenAddress); + + const vTokenContract = VToken.bind(tokenAddress); + const underlyingAddress = vTokenContract.underlying(); + const erc20 = VToken.bind(underlyingAddress); + + supply.underlyingAddress = underlyingAddress; + supply.underlyingDecimals = erc20.decimals(); } supply.save(); return supply; @@ -36,6 +43,12 @@ export const getBorrow = (tokenAddress: Address): Borrow => { let borrow = Borrow.load(tokenAddress); if (!borrow) { borrow = new Borrow(tokenAddress); + const vTokenContract = VToken.bind(tokenAddress); + const underlyingAddress = vTokenContract.underlying(); + const erc20 = VToken.bind(underlyingAddress); + + borrow.underlyingAddress = underlyingAddress; + borrow.underlyingDecimals = erc20.decimals(); } borrow.save(); return borrow; diff --git a/subgraphs/etherfi-promo/src/operations/update.ts b/subgraphs/etherfi-promo/src/operations/update.ts index 477587b6..5f7cb9e9 100644 --- a/subgraphs/etherfi-promo/src/operations/update.ts +++ b/subgraphs/etherfi-promo/src/operations/update.ts @@ -3,7 +3,7 @@ import { Address, BigDecimal, ethereum } from '@graphprotocol/graph-ts'; import { BorrowerAccount, SupplierAccount, TVL } from '../../generated/schema'; import { ERC20 as ERC20Contract } from '../../generated/vWeETH/ERC20'; import { VToken as VTokenContract } from '../../generated/vWeETH/VToken'; -import { getBorrowerAccount, getSupplierAccount, getTvl } from './get'; +import { getBorrowerAccount, getSupplierAccount, getTvl, getSupply } from './get'; import exponentToBigDecimal from '../utilities/exponentToBigDecimal'; export function updateSupplierAccount( @@ -31,6 +31,7 @@ export function updateBorrowerAccount( export function updateTvl(event: ethereum.Event): TVL { const vTokenContract = VTokenContract.bind(event.address); const underlyingAddress = vTokenContract.underlying(); + const vToken = getSupply(event.address); const underlyingContract = ERC20Contract.bind(underlyingAddress); const tvl = getTvl(event.address); const totalBorrows = vTokenContract.totalBorrowsCurrent(); @@ -40,7 +41,7 @@ export function updateTvl(event: ethereum.Event): TVL { .plus(totalBorrows) .minus(totalReserves) .toBigDecimal() - .div(exponentToBigDecimal(18)); + .div(exponentToBigDecimal(vToken.underlyingDecimals)); tvl.save(); return tvl; } diff --git a/subgraphs/etherfi-promo/tests/VToken/index.test.ts b/subgraphs/etherfi-promo/tests/VToken/index.test.ts index d5c2b918..8fc346a9 100644 --- a/subgraphs/etherfi-promo/tests/VToken/index.test.ts +++ b/subgraphs/etherfi-promo/tests/VToken/index.test.ts @@ -37,7 +37,7 @@ const cleanup = (): void => { }; beforeAll(() => { - createVBep20Mock(vTokenAddress, exchangeRateCurrent); + createVBep20Mock(vTokenAddress, underlyingAddress, exchangeRateCurrent); }); afterEach(() => { diff --git a/subgraphs/etherfi-promo/tests/VToken/mocks.ts b/subgraphs/etherfi-promo/tests/VToken/mocks.ts index 5fe00d0a..3df10d16 100644 --- a/subgraphs/etherfi-promo/tests/VToken/mocks.ts +++ b/subgraphs/etherfi-promo/tests/VToken/mocks.ts @@ -5,12 +5,24 @@ export const mockPriceOracleAddress = Address.fromString( '0xb0b0000000000000000000000000000000000000', ); -export const createVBep20Mock = (contractAddress: Address, exchangeRateCurrent: BigInt): void => { +export const createVBep20Mock = ( + contractAddress: Address, + underlyingAddress: Address, + exchangeRateCurrent: BigInt, +): void => { createMockedFunction( contractAddress, 'exchangeRateCurrent', 'exchangeRateCurrent():(uint256)', ).returns([ethereum.Value.fromUnsignedBigInt(exchangeRateCurrent)]); + + createMockedFunction(contractAddress, 'underlying', 'underlying():(address)').returns([ + ethereum.Value.fromAddress(underlyingAddress), + ]); + + createMockedFunction(underlyingAddress, 'decimals', 'decimals():(uint8)').returns([ + ethereum.Value.fromI32(18), + ]); }; export const createBep20Mock = ( @@ -21,6 +33,10 @@ export const createBep20Mock = ( createMockedFunction(contractAddress, 'balanceOf', 'balanceOf(address):(uint256)') .withArgs([ethereum.Value.fromAddress(accountAddress)]) .returns([ethereum.Value.fromUnsignedBigInt(balanceOf)]); + + createMockedFunction(contractAddress, 'decimals', 'decimals():(uint8)').returns([ + ethereum.Value.fromI32(18), + ]); }; export const createAccountVTokenBalanceOfMock = (