From 34df4fb690e85c5728b9b5a9aa3cd2138c6059c6 Mon Sep 17 00:00:00 2001 From: Corey Rice Date: Tue, 31 Oct 2023 15:04:03 -0300 Subject: [PATCH 1/4] feat: add supply and borrower count to core pool --- subgraphs/venus/schema.graphql | 36 +++-------- subgraphs/venus/src/mappings/vToken.ts | 72 +++++++++++++++------- subgraphs/venus/src/operations/create.ts | 4 ++ subgraphs/venus/src/utilities/ids.ts | 3 + subgraphs/venus/tests/VToken/index.test.ts | 20 +++++- 5 files changed, 83 insertions(+), 52 deletions(-) diff --git a/subgraphs/venus/schema.graphql b/subgraphs/venus/schema.graphql index ca95039c..f3990b9d 100644 --- a/subgraphs/venus/schema.graphql +++ b/subgraphs/venus/schema.graphql @@ -67,6 +67,15 @@ type Market @entity { totalXvsDistributedMantissa: BigInt! "vToken decimal length" vTokenDecimals: Int! + + "Number of accounts currently supplying to this market" + supplierCount: BigInt! + + "Number of accounts with reasonable borrow positions in this market (excludes accounts with dust (potentially left over from liquidations))" + borrowerCountAdjusted: BigInt! + + "Number of accounts currently borrowing from this market" + borrowerCount: BigInt! } """ @@ -84,17 +93,6 @@ type Account @entity { countLiquidator: Int! "True if user has ever borrowed" hasBorrowed: Boolean! - - # The following values are added by the JS Wrapper, and must be calculated with the most up - # to date values based on the block delta for market.exchangeRate and market.borrowIndex - # They do not need to be in the schema, but they will show up in the explorer playground - - # "If less than 1, the account can be liquidated - # health: BigDecimal! - # "Total assets supplied by user" - # totalBorrowValueInEth: BigDecimal! - # "Total assets borrowed from user" - # totalCollateralValueInEth: BigDecimal! } """ @@ -131,22 +129,6 @@ type AccountVToken @entity { totalUnderlyingRepaid: BigDecimal! "Current borrow balance stored in contract (exclusive of interest since accrualBlockNumber)" storedBorrowBalance: BigDecimal! - - # The following values are added by the JS Wrapper, and must be calculated with the most up - # to date values based on the block delta for market.exchangeRate and market.borrowIndex - # They do not need to be in the schema, but they will show up in the explorer playground - - # supplyBalanceUnderlying: BigDecimal! - # FORMULA: supplyBalanceUnderlying = vTokenBalance * market.exchangeRate - - # lifetimeSupplyInterestAccrued: BigDecimal! - # FORMULA: lifetimeSupplyInterestAccrued = supplyBalanceUnderlying - totalUnderlyingSupplied + totalUnderlyingRedeemed - - # borrowBalanceUnderlying: BigDecimal! - # FORMULA: borrowBalanceUnderlying = storedBorrowBalance * market.borrowIndex / accountBorrowIndex - - # lifetimeBorrowInterestAccrued: BigDecimal! - # FORMULA: lifetimeBorrowInterestAccrued = borrowBalanceUnderlying - totalUnderlyingBorrowed + totalUnderlyingRepaid } """ diff --git a/subgraphs/venus/src/mappings/vToken.ts b/subgraphs/venus/src/mappings/vToken.ts index 35b3a129..d4b7e2ff 100644 --- a/subgraphs/venus/src/mappings/vToken.ts +++ b/subgraphs/venus/src/mappings/vToken.ts @@ -1,5 +1,7 @@ /* eslint-disable prefer-const */ // to satisfy AS compiler +import { BigInt } from '@graphprotocol/graph-ts'; + import { Account, BorrowEvent, @@ -21,12 +23,14 @@ import { RepayBorrow, Transfer, } from '../../generated/templates/VToken/VToken'; +import { oneBigInt, zeroBigInt32 } from '../constants'; import { nullAddress } from '../constants/addresses'; import { createAccount } from '../operations/create'; import { createMarket } from '../operations/create'; import { updateCommonVTokenStats } from '../operations/update'; import { updateMarket } from '../operations/update'; import { exponentToBigDecimal } from '../utilities/exponentToBigDecimal'; +import { getMarketId, getTransactionId } from '../utilities/ids'; /* Account supplies assets into market and receives vTokens in exchange * @@ -46,10 +50,7 @@ export const handleMint = (event: Mint): void => { if (!market) { market = createMarket(event.address.toHexString()); } - let mintID = event.transaction.hash - .toHexString() - .concat('-') - .concat(event.transactionLogIndex.toString()); + let mintId = getTransactionId(event.transaction.hash, event.transactionLogIndex); const vTokenDecimals = market.vTokenDecimals; @@ -62,7 +63,7 @@ export const handleMint = (event: Mint): void => { .div(exponentToBigDecimal(market.underlyingDecimals)) .truncate(market.underlyingDecimals); - let mint = new MintEvent(mintID); + let mint = new MintEvent(mintId); mint.amount = vTokenAmount; mint.to = event.params.minter; mint.from = event.address; @@ -71,6 +72,10 @@ export const handleMint = (event: Mint): void => { mint.vTokenSymbol = market.symbol; mint.underlyingAmount = underlyingAmount; mint.save(); + + if (event.params.mintTokens.equals(event.params.totalSupply)) { + market.supplierCount = market.supplierCount.plus(oneBigInt); + } }; /* Account supplies vTokens into market and receives underlying asset in exchange @@ -114,6 +119,12 @@ export const handleRedeem = (event: Redeem): void => { redeem.vTokenSymbol = market.symbol; redeem.underlyingAmount = underlyingAmount; redeem.save(); + + if (event.params.totalSupply.equals(zeroBigInt32)) { + // if the current balance is 0 then the user has withdrawn all their assets from this market + market.supplierCount = market.supplierCount.minus(oneBigInt); + market.save(); + } }; /* Borrow assets from the protocol. All values either BNB or BEP20 @@ -186,6 +197,13 @@ export const handleBorrow = (event: Borrow): void => { borrow.blockTime = event.block.timestamp.toI32(); borrow.underlyingSymbol = market.underlyingSymbol; borrow.save(); + + if (event.params.accountBorrows == event.params.borrowAmount) { + // if both the accountBorrows and the borrowAmount are the same, it means the account is a new borrower + market.borrowerCount = market.borrowerCount.plus(oneBigInt); + market.borrowerCountAdjusted = market.borrowerCountAdjusted.plus(oneBigInt); + market.save(); + } }; /* Repay some amount borrowed. Anyone can repay anyones balance @@ -262,6 +280,17 @@ export const handleRepayBorrow = (event: RepayBorrow): void => { repay.underlyingSymbol = market.underlyingSymbol; repay.payer = event.params.payer; repay.save(); + + if (event.params.accountBorrows.equals(zeroBigInt32)) { + market.borrowerCount = market.borrowerCount.minus(oneBigInt); + market.borrowerCountAdjusted = market.borrowerCountAdjusted.minus(oneBigInt); + market.save(); + } else if (event.params.accountBorrows.le(new BigInt(10))) { + // Sometimes a liquidator will leave dust behind. If this happens we'll adjust count + // because the position only exists due to a technicality + market.borrowerCountAdjusted = market.borrowerCountAdjusted.minus(oneBigInt); + market.save(); + } }; /* @@ -353,10 +382,10 @@ export const handleLiquidateBorrow = (event: LiquidateBorrow): void => { export const handleTransfer = (event: Transfer): void => { // We only updateMarket() if accrual block number is not up to date. This will only happen // with normal transfers, since mint, redeem, and seize transfers will already run updateMarket() - let marketID = event.address.toHexString(); - let market = Market.load(marketID); + let marketId = getMarketId(event.address); + let market = Market.load(marketId); if (!market) { - market = createMarket(marketID); + market = createMarket(marketId); } if (market.accrualBlockNumber != event.block.number.toI32()) { market = updateMarket(event.address, event.block.number.toI32(), event.block.timestamp.toI32()); @@ -366,11 +395,11 @@ export const handleTransfer = (event: Transfer): void => { // Checking if the tx is FROM the vToken contract (i.e. this will not run when minting) // If so, it is a mint, and we don't need to run these calculations - let accountFromID = event.params.from.toHex(); - if (accountFromID != nullAddress.toHex()) { - let accountFrom = Account.load(accountFromID); + let accountFromId = event.params.from.toHex(); + if (accountFromId != nullAddress.toHex()) { + let accountFrom = Account.load(accountFromId); if (accountFrom == null) { - createAccount(accountFromID); + createAccount(accountFromId); } // Update vTokenStats common for all events, and return the stats to update unique @@ -378,7 +407,7 @@ export const handleTransfer = (event: Transfer): void => { let vTokenStatsFrom = updateCommonVTokenStats( market.id, market.symbol, - accountFromID, + accountFromId, event.transaction.hash, event.block.timestamp, event.block.number, @@ -401,11 +430,11 @@ export const handleTransfer = (event: Transfer): void => { // If so, we ignore it. this leaves an edge case, where someone who accidentally sends // vTokens to a vToken contract, where it will not get recorded. Right now it would // be messy to include, so we are leaving it out for now TODO fix this in future - let accountToID = event.params.to.toHex(); - if (accountToID != marketID) { - let accountTo = Account.load(accountToID); + let accountToId = event.params.to.toHex(); + if (accountToId != marketId) { + let accountTo = Account.load(accountToId); if (accountTo == null) { - createAccount(accountToID); + createAccount(accountToId); } // Update vTokenStats common for all events, and return the stats to update unique @@ -413,7 +442,7 @@ export const handleTransfer = (event: Transfer): void => { let vTokenStatsTo = updateCommonVTokenStats( market.id, market.symbol, - accountToID, + accountToId, event.transaction.hash, event.block.timestamp, event.block.number, @@ -432,12 +461,9 @@ export const handleTransfer = (event: Transfer): void => { vTokenStatsTo.save(); } - let transferID = event.transaction.hash - .toHexString() - .concat('-') - .concat(event.transactionLogIndex.toString()); + let transferId = getTransactionId(event.transaction.hash, event.transactionLogIndex); - let transfer = new TransferEvent(transferID); + let transfer = new TransferEvent(transferId); transfer.amount = event.params.amount.toBigDecimal().div(exponentToBigDecimal(vTokenDecimals)); transfer.to = event.params.to; transfer.from = event.params.from; diff --git a/subgraphs/venus/src/operations/create.ts b/subgraphs/venus/src/operations/create.ts index f975b5be..422ff0d1 100644 --- a/subgraphs/venus/src/operations/create.ts +++ b/subgraphs/venus/src/operations/create.ts @@ -100,5 +100,9 @@ export function createMarket(marketAddress: string): Market { market.reserveFactor = reserveFactor.reverted ? BigInt.fromI32(0) : reserveFactor.value; market.totalXvsDistributedMantissa = zeroBigInt32; + market.supplierCount = zeroBigInt32; + market.borrowerCount = zeroBigInt32; + market.borrowerCountAdjusted = zeroBigInt32; + return market; } diff --git a/subgraphs/venus/src/utilities/ids.ts b/subgraphs/venus/src/utilities/ids.ts index ca39c4f7..fc3748ea 100644 --- a/subgraphs/venus/src/utilities/ids.ts +++ b/subgraphs/venus/src/utilities/ids.ts @@ -12,6 +12,9 @@ export const getMarketId = (vTokenAddress: Address): string => export const getAccountVTokenId = (marketAddress: Address, accountAddress: Address): string => joinIds([marketAddress.toHexString(), accountAddress.toHexString()]); +export const getTransactionId = (transactionHash: Bytes, logIndex: BigInt): string => + joinIds([transactionHash.toHexString(), logIndex.toString()]); + export const getAccountVTokenTransactionId = ( accountAddress: Address, transactionHash: Bytes, diff --git a/subgraphs/venus/tests/VToken/index.test.ts b/subgraphs/venus/tests/VToken/index.test.ts index 8a704b78..3fc099a1 100644 --- a/subgraphs/venus/tests/VToken/index.test.ts +++ b/subgraphs/venus/tests/VToken/index.test.ts @@ -449,6 +449,8 @@ describe('VToken', () => { handleMint(mintEvent); + assert.fieldEquals('Market', aaaTokenAddress.toHex(), 'supplierCount', '2'); + const supplier02 = user2Address; mintEvent = createMintEvent( aaaTokenAddress, @@ -460,6 +462,7 @@ describe('VToken', () => { createAccountVTokenBalanceOfMock(aaaTokenAddress, supplier02, mintTokens); handleMint(mintEvent); + assert.fieldEquals('Market', aaaTokenAddress.toHex(), 'supplierCount', '3'); let redeemEvent = createRedeemEvent( aaaTokenAddress, @@ -471,6 +474,7 @@ describe('VToken', () => { createAccountVTokenBalanceOfMock(aaaTokenAddress, supplier02, zeroBigInt32); handleRedeem(redeemEvent); + assert.fieldEquals('Market', aaaTokenAddress.toHex(), 'supplierCount', '2'); redeemEvent = createRedeemEvent( aaaTokenAddress, @@ -482,6 +486,7 @@ describe('VToken', () => { createAccountVTokenBalanceOfMock(aaaTokenAddress, supplier01, halfMintTokens); handleRedeem(redeemEvent); + assert.fieldEquals('Market', aaaTokenAddress.toHex(), 'supplierCount', '1'); redeemEvent = createRedeemEvent( aaaTokenAddress, @@ -493,6 +498,7 @@ describe('VToken', () => { createAccountVTokenBalanceOfMock(aaaTokenAddress, supplier01, zeroBigInt32); handleRedeem(redeemEvent); + assert.fieldEquals('Market', aaaTokenAddress.toHex(), 'supplierCount', '0'); }); test('registers increase and decrease in the market borrower count', () => { @@ -515,6 +521,8 @@ describe('VToken', () => { ); handleBorrow(borrowEvent); + assert.fieldEquals('Market', aaaTokenAddress.toHex(), 'borrowerCount', '2'); + assert.fieldEquals('Market', aaaTokenAddress.toHex(), 'borrowerCountAdjusted', '2'); const borrower02 = user2Address; borrowEvent = createBorrowEvent( @@ -526,6 +534,8 @@ describe('VToken', () => { ); handleBorrow(borrowEvent); + assert.fieldEquals('Market', aaaTokenAddress.toHex(), 'borrowerCount', '3'); + assert.fieldEquals('Market', aaaTokenAddress.toHex(), 'borrowerCountAdjusted', '3'); let repayEvent = createRepayBorrowEvent( aaaTokenAddress, @@ -537,6 +547,8 @@ describe('VToken', () => { ); handleRepayBorrow(repayEvent); + assert.fieldEquals('Market', aaaTokenAddress.toHex(), 'borrowerCount', '2'); + assert.fieldEquals('Market', aaaTokenAddress.toHex(), 'borrowerCountAdjusted', '2'); repayEvent = createRepayBorrowEvent( aaaTokenAddress, @@ -548,17 +560,21 @@ describe('VToken', () => { ); handleRepayBorrow(repayEvent); + assert.fieldEquals('Market', aaaTokenAddress.toHex(), 'borrowerCount', '1'); + assert.fieldEquals('Market', aaaTokenAddress.toHex(), 'borrowerCountAdjusted', '1'); repayEvent = createRepayBorrowEvent( aaaTokenAddress, borrower01, borrower01, halfBorrowAmountTokens, - zeroBigInt32, - zeroBigInt32, + oneBigInt, + oneBigInt, ); handleRepayBorrow(repayEvent); + assert.fieldEquals('Market', aaaTokenAddress.toHex(), 'borrowerCount', '1'); + assert.fieldEquals('Market', aaaTokenAddress.toHex(), 'borrowerCountAdjusted', '1'); }); }); }); From 877d7f4f4a25ffa6953cebb835f109dffbd9093d Mon Sep 17 00:00:00 2001 From: Corey Rice Date: Wed, 1 Nov 2023 16:18:26 -0300 Subject: [PATCH 2/4] feat: support v1 and v2 mint and redeem events --- package.json | 1 - subgraphs/venus/package.json | 4 + subgraphs/venus/schema.graphql | 16 +- subgraphs/venus/src/constants/index.ts | 20 +- subgraphs/venus/src/mappings/vToken.ts | 136 +++-- subgraphs/venus/src/operations/create.ts | 45 +- subgraphs/venus/template.yaml | 40 +- subgraphs/venus/tests/VToken/events.ts | 143 ++++- subgraphs/venus/tests/VToken/index.test.ts | 655 +++++++++++---------- yarn.lock | 30 +- 10 files changed, 697 insertions(+), 393 deletions(-) diff --git a/package.json b/package.json index d569a1b1..41b9dfe2 100644 --- a/package.json +++ b/package.json @@ -50,7 +50,6 @@ "@venusprotocol/governance-contracts": "^1.3.0", "@venusprotocol/isolated-pools": "2.1.0-dev.2", "@venusprotocol/oracle": "^1.7.3-dev.1", - "@venusprotocol/venus-protocol": "3.1.0", "assemblyscript": "0.19.23", "chai": "^4.3.6", "eslint": "^8.25.0", diff --git a/subgraphs/venus/package.json b/subgraphs/venus/package.json index bd5c618b..d393c9dc 100644 --- a/subgraphs/venus/package.json +++ b/subgraphs/venus/package.json @@ -23,5 +23,9 @@ "prepare:bsc": "npx mustache config/bsc.json template.yaml > subgraph.yaml && npx mustache config/bsc.json src/constants/config-template > src/constants/config.ts", "test": "graph test", "test:integration": "true" + }, + "dependencies": { + "@venusprotocol/venus-protocol": "3.1.0", + "@venusprotocol/venus-protocol-orig-events": "npm:@venusprotocol/venus-protocol@2.2.1" } } diff --git a/subgraphs/venus/schema.graphql b/subgraphs/venus/schema.graphql index f3990b9d..30270f79 100644 --- a/subgraphs/venus/schema.graphql +++ b/subgraphs/venus/schema.graphql @@ -198,7 +198,7 @@ type MintEvent implements VTokenTransfer @entity { "Transaction hash concatenated with log index" id: ID! "vTokens transferred" - amount: BigDecimal! + amountMantissa: BigInt! "Account that received tokens (minter)" to: Bytes! "Account that sent tokens (VToken contract)" @@ -207,10 +207,10 @@ type MintEvent implements VTokenTransfer @entity { blockNumber: Int! "Block time" blockTime: Int! - "Symbol of the vToken transferred" - vTokenSymbol: String! + "Address of the vToken transferred" + vTokenAddress: Bytes! "Underlying token amount transferred" - underlyingAmount: BigDecimal + underlyingAmountMantissa: BigInt! } """ @@ -221,7 +221,7 @@ type RedeemEvent implements VTokenTransfer @entity { "Transaction hash concatenated with log index" id: ID! "vTokens transferred" - amount: BigDecimal! + amountMantissa: BigInt! "Account that received tokens (VToken contract)" to: Bytes! "Account that sent tokens (redeemer)" @@ -230,10 +230,10 @@ type RedeemEvent implements VTokenTransfer @entity { blockNumber: Int! "Block time" blockTime: Int! - "Symbol of the vToken transferred" - vTokenSymbol: String! + "Address of the vToken transferred" + vTokenAddress: Bytes! "Underlying token amount transferred" - underlyingAmount: BigDecimal + underlyingAmountMantissa: BigInt! } """ diff --git a/subgraphs/venus/src/constants/index.ts b/subgraphs/venus/src/constants/index.ts index 6de4314d..41c14c61 100644 --- a/subgraphs/venus/src/constants/index.ts +++ b/subgraphs/venus/src/constants/index.ts @@ -19,14 +19,16 @@ export const zeroBigDecimal = BigDecimal.fromString('0'); export const zeroBigInt32 = BigInt.fromString('0'); export const oneBigInt = BigInt.fromString('1'); +export const DUST_THRESHOLD = BigInt.fromString('10'); + export const Actions = [ - MINT, - REDEEM, - BORROW, - REPAY, - SEIZE, - LIQUIDATE, - TRANSFER, - ENTER_MARKET, - EXIT_MARKET, + 'MINT', + 'REDEEM', + 'BORROW', + 'REPAY', + 'SEIZE', + 'LIQUIDATE', + 'TRANSFER', + 'ENTER_MARKET', + 'EXIT_MARKET', ]; diff --git a/subgraphs/venus/src/mappings/vToken.ts b/subgraphs/venus/src/mappings/vToken.ts index d4b7e2ff..ee59f570 100644 --- a/subgraphs/venus/src/mappings/vToken.ts +++ b/subgraphs/venus/src/mappings/vToken.ts @@ -1,14 +1,10 @@ /* eslint-disable prefer-const */ // to satisfy AS compiler -import { BigInt } from '@graphprotocol/graph-ts'; - import { Account, BorrowEvent, LiquidationEvent, Market, - MintEvent, - RedeemEvent, RepayEvent, TransferEvent, } from '../../generated/schema'; @@ -16,17 +12,29 @@ import { AccrueInterest, Borrow, LiquidateBorrow, - Mint, + MintBehalf as MintBehalfV1, + Mint as MintV1, NewMarketInterestRateModel, NewReserveFactor, - Redeem, + Redeem as RedeemV1, RepayBorrow, Transfer, } from '../../generated/templates/VToken/VToken'; -import { oneBigInt, zeroBigInt32 } from '../constants'; +import { VToken as VTokenContract } from '../../generated/templates/VToken/VToken'; +import { + Mint, + MintBehalf, + Redeem, +} from '../../generated/templates/VTokenUpdatedEvents/VTokenUpdatedEvents'; +import { DUST_THRESHOLD, oneBigInt, zeroBigInt32 } from '../constants'; import { nullAddress } from '../constants/addresses'; -import { createAccount } from '../operations/create'; -import { createMarket } from '../operations/create'; +import { + createAccount, + createMarket, + createMintBehalfEvent, + createMintEvent, + createRedeemEvent, +} from '../operations/create'; import { updateCommonVTokenStats } from '../operations/update'; import { updateMarket } from '../operations/update'; import { exponentToBigDecimal } from '../utilities/exponentToBigDecimal'; @@ -50,31 +58,26 @@ export const handleMint = (event: Mint): void => { if (!market) { market = createMarket(event.address.toHexString()); } - let mintId = getTransactionId(event.transaction.hash, event.transactionLogIndex); - const vTokenDecimals = market.vTokenDecimals; + createMintEvent(event); - let vTokenAmount = event.params.mintTokens - .toBigDecimal() - .div(exponentToBigDecimal(vTokenDecimals)) - .truncate(vTokenDecimals); - let underlyingAmount = event.params.mintAmount - .toBigDecimal() - .div(exponentToBigDecimal(market.underlyingDecimals)) - .truncate(market.underlyingDecimals); + if (event.params.mintTokens.equals(event.params.totalSupply)) { + market.supplierCount = market.supplierCount.plus(oneBigInt); + market.save(); + } +}; - let mint = new MintEvent(mintId); - mint.amount = vTokenAmount; - mint.to = event.params.minter; - mint.from = event.address; - mint.blockNumber = event.block.number.toI32(); - mint.blockTime = event.block.timestamp.toI32(); - mint.vTokenSymbol = market.symbol; - mint.underlyingAmount = underlyingAmount; - mint.save(); +export const handleMintBehalf = (event: MintBehalf): void => { + let market = Market.load(event.address.toHexString()); + if (!market) { + market = createMarket(event.address.toHexString()); + } + + createMintBehalfEvent(event); if (event.params.mintTokens.equals(event.params.totalSupply)) { market.supplierCount = market.supplierCount.plus(oneBigInt); + market.save(); } }; @@ -95,30 +98,8 @@ export const handleRedeem = (event: Redeem): void => { if (!market) { market = createMarket(event.address.toHexString()); } - let vTokenDecimals = market.vTokenDecimals; - let redeemID = event.transaction.hash - .toHexString() - .concat('-') - .concat(event.transactionLogIndex.toString()); - let vTokenAmount = event.params.redeemTokens - .toBigDecimal() - .div(exponentToBigDecimal(vTokenDecimals)) - .truncate(vTokenDecimals); - let underlyingAmount = event.params.redeemAmount - .toBigDecimal() - .div(exponentToBigDecimal(market.underlyingDecimals)) - .truncate(market.underlyingDecimals); - - let redeem = new RedeemEvent(redeemID); - redeem.amount = vTokenAmount; - redeem.to = event.address; - redeem.from = event.params.redeemer; - redeem.blockNumber = event.block.number.toI32(); - redeem.blockTime = event.block.timestamp.toI32(); - redeem.vTokenSymbol = market.symbol; - redeem.underlyingAmount = underlyingAmount; - redeem.save(); + createRedeemEvent(event); if (event.params.totalSupply.equals(zeroBigInt32)) { // if the current balance is 0 then the user has withdrawn all their assets from this market @@ -285,7 +266,7 @@ export const handleRepayBorrow = (event: RepayBorrow): void => { market.borrowerCount = market.borrowerCount.minus(oneBigInt); market.borrowerCountAdjusted = market.borrowerCountAdjusted.minus(oneBigInt); market.save(); - } else if (event.params.accountBorrows.le(new BigInt(10))) { + } else if (event.params.accountBorrows.le(DUST_THRESHOLD)) { // Sometimes a liquidator will leave dust behind. If this happens we'll adjust count // because the position only exists due to a technicality market.borrowerCountAdjusted = market.borrowerCountAdjusted.minus(oneBigInt); @@ -496,3 +477,54 @@ export function handleNewMarketInterestRateModel(event: NewMarketInterestRateMod market.interestRateModelAddress = event.params.newInterestRateModel; market.save(); } + +export function handleMintV1(event: MintV1): void { + let market = Market.load(event.address.toHexString()); + if (!market) { + market = createMarket(event.address.toHexString()); + } + + createMintEvent(event); + + const vTokenContract = VTokenContract.bind(event.address); + let totalSupply = vTokenContract.balanceOf(event.params.minter); + + if (event.params.mintTokens.equals(totalSupply)) { + market.supplierCount = market.supplierCount.plus(oneBigInt); + market.save(); + } +} + +export function handleMintBehalfV1(event: MintBehalfV1): void { + let market = Market.load(event.address.toHexString()); + if (!market) { + market = createMarket(event.address.toHexString()); + } + + createMintBehalfEvent(event); + + const vTokenContract = VTokenContract.bind(event.address); + let totalSupply = vTokenContract.balanceOf(event.params.receiver); + + if (event.params.mintTokens.equals(totalSupply)) { + market.supplierCount = market.supplierCount.plus(oneBigInt); + market.save(); + } +} + +export function handleRedeemV1(event: RedeemV1): void { + let market = Market.load(event.address.toHexString()); + if (!market) { + market = createMarket(event.address.toHexString()); + } + createRedeemEvent(event); + + const vTokenContract = VTokenContract.bind(event.address); + let totalSupply = vTokenContract.balanceOf(event.params.redeemer); + + if (totalSupply.equals(zeroBigInt32)) { + // if the current balance is 0 then the user has withdrawn all their assets from this market + market.supplierCount = market.supplierCount.minus(oneBigInt); + market.save(); + } +} diff --git a/subgraphs/venus/src/operations/create.ts b/subgraphs/venus/src/operations/create.ts index 422ff0d1..30a0c6f4 100644 --- a/subgraphs/venus/src/operations/create.ts +++ b/subgraphs/venus/src/operations/create.ts @@ -1,12 +1,13 @@ import { Address, BigDecimal, BigInt, log } from '@graphprotocol/graph-ts'; -import { Account, AccountVToken, Market } from '../../generated/schema'; +import { Account, AccountVToken, Market, MintEvent, RedeemEvent } from '../../generated/schema'; import { BEP20 } from '../../generated/templates/VToken/BEP20'; import { VBep20Storage } from '../../generated/templates/VToken/VBep20Storage'; import { VToken } from '../../generated/templates/VToken/VToken'; import { zeroBigDecimal, zeroBigInt32 } from '../constants'; import { nullAddress, vBnbAddress } from '../constants/addresses'; import { getUnderlyingPrice } from '../utilities/getUnderlyingPrice'; +import { getTransactionId } from '../utilities/ids'; export function createAccountVToken( accountVTokenId: string, @@ -106,3 +107,45 @@ export function createMarket(marketAddress: string): Market { return market; } + +export function createMintEvent(event: E): void { + const mintId = getTransactionId(event.transaction.hash, event.transactionLogIndex); + + const mint = new MintEvent(mintId); + mint.amountMantissa = event.params.mintTokens; + mint.to = event.params.minter; + mint.from = event.address; + mint.blockNumber = event.block.number.toI32(); + mint.blockTime = event.block.timestamp.toI32(); + mint.vTokenAddress = event.address; + mint.underlyingAmountMantissa = event.params.mintAmount; + mint.save(); +} + +export function createMintBehalfEvent(event: E): void { + const mintId = getTransactionId(event.transaction.hash, event.transactionLogIndex); + + const mint = new MintEvent(mintId); + mint.amountMantissa = event.params.mintTokens; + mint.to = event.params.receiver; + mint.from = event.address; + mint.blockNumber = event.block.number.toI32(); + mint.blockTime = event.block.timestamp.toI32(); + mint.vTokenAddress = event.address; + mint.underlyingAmountMantissa = event.params.mintAmount; + mint.save(); +} + +export function createRedeemEvent(event: E): void { + const redeemId = getTransactionId(event.transaction.hash, event.transactionLogIndex); + + const redeem = new RedeemEvent(redeemId); + redeem.amountMantissa = event.params.redeemTokens; + redeem.to = event.address; + redeem.from = event.params.redeemer; + redeem.blockNumber = event.block.number.toI32(); + redeem.blockTime = event.block.timestamp.toI32(); + redeem.vTokenAddress = event.address; + redeem.underlyingAmountMantissa = event.params.redeemAmount; + redeem.save(); +} diff --git a/subgraphs/venus/template.yaml b/subgraphs/venus/template.yaml index 001f0d8c..58c94338 100644 --- a/subgraphs/venus/template.yaml +++ b/subgraphs/venus/template.yaml @@ -64,9 +64,9 @@ templates: - VTokenInfo abis: - name: VToken - file: ../../node_modules/@venusprotocol/venus-protocol/artifacts/contracts/Tokens/VTokens/VBep20.sol/VBep20.json + file: ../../node_modules/@venusprotocol/venus-protocol-orig-events/artifacts/contracts/Tokens/VTokens/VBep20.sol/VBep20.json - name: VBep20Storage - file: ../../node_modules/@venusprotocol/venus-protocol/artifacts/contracts/Tokens/VTokens/VTokenInterfaces.sol/VBep20Storage.json + file: ../../node_modules/@venusprotocol/venus-protocol-orig-events/artifacts/contracts/Tokens/VTokens/VTokenInterfaces.sol/VBep20Storage.json - name: PriceOracle file: ../../node_modules/@venusprotocol/oracle/artifacts/contracts/ResilientOracle.sol/ResilientOracle.json - name: BEP20 @@ -74,10 +74,12 @@ templates: - name: Comptroller file: ../../node_modules/@venusprotocol/venus-protocol/artifacts/contracts/Comptroller/Comptroller.sol/Comptroller.json eventHandlers: - - event: Mint(address,uint256,uint256,uint256) - handler: handleMint - - event: Redeem(address,uint256,uint256,uint256) - handler: handleRedeem + - event: Mint(address,uint256,uint256) + handler: handleMintV1 + - event: MintBehalf(address,address,uint256,uint256) + handler: handleMintV1 + - event: Redeem(address,uint256,uint256) + handler: handleRedeemV1 - event: Borrow(address,uint256,uint256,uint256) handler: handleBorrow - event: RepayBorrow(address,address,uint256,uint256,uint256) @@ -92,3 +94,29 @@ templates: handler: handleTransfer - event: NewMarketInterestRateModel(address,address) handler: handleNewMarketInterestRateModel + - name: VTokenUpdatedEvents + kind: ethereum/contract + network: {{ network }} + source: + abi: VTokenUpdatedEvents + mapping: + kind: ethereum/events + apiVersion: 0.0.5 + language: wasm/assemblyscript + file: ./src/mappings/vToken.ts + entities: + - User + - Market + - VTokenInfo + abis: + - name: VTokenUpdatedEvents + file: ../../node_modules/@venusprotocol/venus-protocol/artifacts/contracts/Tokens/VTokens/VBep20.sol/VBep20.json + - name: VBep20Storage + file: ../../node_modules/@venusprotocol/venus-protocol/artifacts/contracts/Tokens/VTokens/VTokenInterfaces.sol/VBep20Storage.json + eventHandlers: + - event: Mint(address,uint256,uint256,uint256) + handler: handleMint + - event: MintBehalf(address,address,uint256,uint256,uint256) + handler: handleMint + - event: Redeem(address,uint256,uint256,uint256) + handler: handleRedeem \ No newline at end of file diff --git a/subgraphs/venus/tests/VToken/events.ts b/subgraphs/venus/tests/VToken/events.ts index fe09d8af..d02511ae 100644 --- a/subgraphs/venus/tests/VToken/events.ts +++ b/subgraphs/venus/tests/VToken/events.ts @@ -7,14 +7,20 @@ import { Approval as ApprovalEvent, Borrow as BorrowEvent, LiquidateBorrow as LiquidateBorrowEvent, - Mint as MintEvent, + MintBehalf as MintBehalfEventV1, + Mint as MintEventV1, NewComptroller as NewComptrollerEvent, NewMarketInterestRateModel as NewMarketInterestRateModelEvent, NewReserveFactor as NewReserveFactorEvent, - Redeem as RedeemEvent, + Redeem as RedeemEventV1, RepayBorrow as RepayBorrowEvent, Transfer as TransferEvent, } from '../../generated/templates/VToken/VToken'; +import { + MintBehalf as MintBehalfEvent, + Mint as MintEvent, + Redeem as RedeemEvent, +} from '../../generated/templates/VTokenUpdatedEvents/VTokenUpdatedEvents'; export const createMarketListedEvent = (vTokenAddress: Address): MarketListedEvent => { const event = changetype(newMockEvent()); @@ -377,3 +383,136 @@ export const createNewComptrollerEvent = ( return event; }; + +export const createMintEventV1 = ( + vTokenAddress: Address, + minterAddress: Address, + mintAmount: BigInt, + mintTokens: BigInt, +): MintEventV1 => { + const event = changetype(newMockEvent()); + event.address = vTokenAddress; + event.parameters = []; + + const minterParam = new ethereum.EventParam('minter', ethereum.Value.fromAddress(minterAddress)); + event.parameters.push(minterParam); + + const mintAmountParam = new ethereum.EventParam( + 'mintAmount', + ethereum.Value.fromUnsignedBigInt(mintAmount), + ); + event.parameters.push(mintAmountParam); + + const mintTokensParam = new ethereum.EventParam( + 'mintTokens', + ethereum.Value.fromUnsignedBigInt(mintTokens), + ); + event.parameters.push(mintTokensParam); + + return event; +}; +export const createMintBehalfEvent = ( + vTokenAddress: Address, + payerAddress: Address, + receiverAddress: Address, + mintAmount: BigInt, + mintTokens: BigInt, + totalSupply: BigInt, +): MintBehalfEvent => { + const event = changetype(newMockEvent()); + event.address = vTokenAddress; + event.parameters = []; + + const payerParam = new ethereum.EventParam('minter', ethereum.Value.fromAddress(payerAddress)); + event.parameters.push(payerParam); + + const receiverParam = new ethereum.EventParam( + 'minter', + ethereum.Value.fromAddress(receiverAddress), + ); + event.parameters.push(receiverParam); + + const mintAmountParam = new ethereum.EventParam( + 'mintAmount', + ethereum.Value.fromUnsignedBigInt(mintAmount), + ); + event.parameters.push(mintAmountParam); + + const mintTokensParam = new ethereum.EventParam( + 'mintTokens', + ethereum.Value.fromUnsignedBigInt(mintTokens), + ); + event.parameters.push(mintTokensParam); + + const totalSupplyParam = new ethereum.EventParam( + 'totalSupply', + ethereum.Value.fromUnsignedBigInt(totalSupply), + ); + event.parameters.push(totalSupplyParam); + + return event; +}; +export const createMintBehalfEventV1 = ( + vTokenAddress: Address, + payerAddress: Address, + receiverAddress: Address, + mintAmount: BigInt, + mintTokens: BigInt, +): MintBehalfEventV1 => { + const event = changetype(newMockEvent()); + event.address = vTokenAddress; + event.parameters = []; + + const payerParam = new ethereum.EventParam('minter', ethereum.Value.fromAddress(payerAddress)); + event.parameters.push(payerParam); + + const receiverParam = new ethereum.EventParam( + 'minter', + ethereum.Value.fromAddress(receiverAddress), + ); + event.parameters.push(receiverParam); + + const mintAmountParam = new ethereum.EventParam( + 'mintAmount', + ethereum.Value.fromUnsignedBigInt(mintAmount), + ); + event.parameters.push(mintAmountParam); + + const mintTokensParam = new ethereum.EventParam( + 'mintTokens', + ethereum.Value.fromUnsignedBigInt(mintTokens), + ); + event.parameters.push(mintTokensParam); + return event; +}; + +export const createRedeemEventV1 = ( + vTokenAddress: Address, + redeemerAddress: Address, + redeemAmount: BigInt, + redeemTokens: BigInt, +): RedeemEventV1 => { + const event = changetype(newMockEvent()); + event.address = vTokenAddress; + event.parameters = []; + + const redeemerParam = new ethereum.EventParam( + 'redeemer', + ethereum.Value.fromAddress(redeemerAddress), + ); + event.parameters.push(redeemerParam); + + const redeemAmountParam = new ethereum.EventParam( + 'redeemAmount', + ethereum.Value.fromUnsignedBigInt(redeemAmount), + ); + event.parameters.push(redeemAmountParam); + + const redeemTokensParam = new ethereum.EventParam( + 'redeemTokens', + ethereum.Value.fromUnsignedBigInt(redeemTokens), + ); + event.parameters.push(redeemTokensParam); + + return event; +}; diff --git a/subgraphs/venus/tests/VToken/index.test.ts b/subgraphs/venus/tests/VToken/index.test.ts index 3fc099a1..c9c0d26d 100644 --- a/subgraphs/venus/tests/VToken/index.test.ts +++ b/subgraphs/venus/tests/VToken/index.test.ts @@ -18,9 +18,13 @@ import { handleBorrow, handleLiquidateBorrow, handleMint, + handleMintBehalf, + handleMintBehalfV1, + handleMintV1, handleNewMarketInterestRateModel, handleNewReserveFactor, handleRedeem, + handleRedeemV1, handleRepayBorrow, handleTransfer, } from '../../src/mappings/vToken'; @@ -31,10 +35,14 @@ import { createBorrowEvent, createLiquidateBorrowEvent, createMarketListedEvent, + createMintBehalfEvent, + createMintBehalfEventV1, createMintEvent, + createMintEventV1, createNewMarketInterestRateModelEvent, createNewReserveFactorEvent, createRedeemEvent, + createRedeemEventV1, createRepayBorrowEvent, createTransferEvent, } from './events'; @@ -44,6 +52,8 @@ import { createMarketMock, createPriceOracleMock, createVBep20AndUnderlyingMock const tokenAddress = Address.fromString('0x0000000000000000000000000000000000000b0b'); const user1Address = Address.fromString('0x0000000000000000000000000000000000000101'); const user2Address = Address.fromString('0x0000000000000000000000000000000000000202'); +const user3Address = Address.fromString('0x0000000000000000000000000000000000000303'); +const user4Address = Address.fromString('0x0000000000000000000000000000000000000404'); const aaaTokenAddress = Address.fromString('0x0000000000000000000000000000000000000aaa'); const interestRateModelAddress = Address.fromString('0x594942C0e62eC577889777424CD367545C796A74'); @@ -268,313 +278,344 @@ describe('VToken', () => { if (!market) { return; } + }); + + test('registers accrue interest event', () => { + /** Constants */ + const cashPrior = BigInt.fromString('1246205398726345'); + const interestAccumulated = BigInt.fromI32(26454); + const borrowIndex = BigInt.fromI32(1); + const totalBorrows = BigInt.fromString('62197468301'); + + /** Setup test */ + const accrueInterestEvent = createAccrueInterestEvent( + aaaTokenAddress, + cashPrior, + interestAccumulated, + borrowIndex, + totalBorrows, + ); + + /** Fire Event */ + handleAccrueInterest(accrueInterestEvent); + + const assertMarketDocument = (key: string, value: string): void => { + assert.fieldEquals('Market', aaaTokenAddress.toHexString(), key, value); + }; + + assertMarketDocument('accrualBlockNumber', '999'); + assertMarketDocument('blockTimestamp', accrueInterestEvent.block.timestamp.toString()); + assertMarketDocument('exchangeRateMantissa', '365045823500000000000000'); + assertMarketDocument('borrowIndexMantissa', '300000000000000000000'); + assertMarketDocument('reservesMantissa', '5128924555022289393'); + assertMarketDocument('totalBorrowsMantissa', '2641234234636158123'); + assertMarketDocument('cashMantissa', '1418171344423412457'); + assertMarketDocument('borrowRateMantissa', '12678493'); + assertMarketDocument('supplyRateMantissa', '12678493'); + }); + + test('registers new reserve factor', () => { + const oldReserveFactor = BigInt.fromI64(12462053079875); + const newReserveFactor = BigInt.fromI64(37035970026454); + const reserveFactorEvent = createNewReserveFactorEvent( + aaaTokenAddress, + oldReserveFactor, + newReserveFactor, + ); + + handleNewReserveFactor(reserveFactorEvent); + assert.fieldEquals('Market', aaaTokenAddress.toHex(), 'id', aaaTokenAddress.toHexString()); + assert.fieldEquals( + 'Market', + aaaTokenAddress.toHex(), + 'reserveFactor', + newReserveFactor.toString(), + ); + }); + + test('registers transfer from event', () => { + /** Constants */ + const from = user1Address; // 101 + const to = aaaTokenAddress; + const amount = BigInt.fromString('146205398726345'); + const balanceOf = BigInt.fromString('262059874253345'); + + /** Setup test */ + const transferEvent = createTransferEvent(aaaTokenAddress, from, to, amount); + createMockedFunction( + aaaTokenAddress, + 'getAccountSnapshot', + 'getAccountSnapshot(address):(uint256,uint256,uint256,uint256)', + ) + .withArgs([ethereum.Value.fromAddress(from)]) + .returns([ + ethereum.Value.fromSignedBigInt(zeroBigInt32), + ethereum.Value.fromSignedBigInt(balanceOf), + ethereum.Value.fromSignedBigInt(zeroBigInt32), + ethereum.Value.fromSignedBigInt(oneBigInt), + ]); + + /** Fire Event */ + handleTransfer(transferEvent); + + const accountVTokenId = getAccountVTokenId(aaaTokenAddress, from); + + /** AccountVToken */ + assert.fieldEquals( + 'AccountVToken', + accountVTokenId, + 'accrualBlockNumber', + transferEvent.block.number.toString(), + ); + + assert.fieldEquals('AccountVToken', accountVTokenId, 'accountBorrowIndexMantissa', '0'); + + assert.fieldEquals( + 'AccountVToken', + accountVTokenId, + 'totalUnderlyingRedeemedMantissa', + '53371670178204461670107500000000000000', + ); + }); + + test('registers transfer to event', () => { + /** Constants */ + const amount = BigInt.fromString('5246205398726345'); + const from = aaaTokenAddress; + const to = user2Address; + const balanceOf = BigInt.fromString('262059874253345'); + + /** Setup test */ + const transferEvent = createTransferEvent(aaaTokenAddress, from, to, amount); + createAccountVTokenBalanceOfMock( + aaaTokenAddress, + aaaTokenAddress, // something is wrong with this test + balanceOf, + ); + createMockedFunction( + aaaTokenAddress, + 'getAccountSnapshot', + 'getAccountSnapshot(address):(uint256,uint256,uint256,uint256)', + ) + .withArgs([ethereum.Value.fromAddress(to)]) + .returns([ + ethereum.Value.fromSignedBigInt(zeroBigInt32), + ethereum.Value.fromSignedBigInt(balanceOf), + ethereum.Value.fromSignedBigInt(zeroBigInt32), + ethereum.Value.fromSignedBigInt(oneBigInt), + ]); + + /** Fire Event */ + handleTransfer(transferEvent); + + const accountVTokenId = getAccountVTokenId(aaaTokenAddress, to); + + /** AccountVToken */ + assert.fieldEquals( + 'AccountVToken', + accountVTokenId, + 'accrualBlockNumber', + transferEvent.block.number.toString(), + ); + + assert.fieldEquals('AccountVToken', accountVTokenId, 'accountBorrowIndexMantissa', '0'); + }); + + test('registers new interest rate model', () => { + const oldInterestRateModel = Address.fromString('0x0000000000000000000000000000000000000e0e'); + const newInterestRateModel = Address.fromString('0x0000000000000000000000000000000000000f0f'); + const newMarketInterestRateModelEvent = createNewMarketInterestRateModelEvent( + aaaTokenAddress, + oldInterestRateModel, + newInterestRateModel, + ); + + handleNewMarketInterestRateModel(newMarketInterestRateModelEvent); + assert.fieldEquals('Market', aaaTokenAddress.toHex(), 'id', aaaTokenAddress.toHexString()); + assert.fieldEquals( + 'Market', + aaaTokenAddress.toHex(), + 'interestRateModelAddress', + newInterestRateModel.toHexString(), + ); + }); + + test('registers increase and decrease in the market supplier count', () => { + const market = getMarket(aaaTokenAddress); + assert.assertNotNull(market); + if (!market) { + return; + } + assert.fieldEquals('Market', market.id, 'supplierCount', '0'); + + const actualMintAmount = BigInt.fromI64(12); + const halfActualMintAmount = actualMintAmount.div(BigInt.fromI64(2)); + const mintTokens = BigInt.fromI64(10); + const accountBalance = mintTokens; + const halfMintTokens = mintTokens.div(BigInt.fromI64(2)); + + const supplier01 = user1Address; + const mintEvent = createMintEvent( + aaaTokenAddress, + supplier01, + actualMintAmount, + mintTokens, + accountBalance, + ); + + handleMint(mintEvent); + + assert.fieldEquals('Market', market.id, 'supplierCount', '1'); + + const supplier02 = user2Address; + const mintEventV1 = createMintEventV1( + aaaTokenAddress, + supplier02, + actualMintAmount, + mintTokens, + ); + createAccountVTokenBalanceOfMock(aaaTokenAddress, supplier02, mintTokens); + + handleMintV1(mintEventV1); + assert.fieldEquals('Market', market.id, 'supplierCount', '2'); + + const supplier03 = user3Address; + const mintBehalfEvent = createMintBehalfEvent( + aaaTokenAddress, + supplier01, + supplier03, + actualMintAmount, + mintTokens, + accountBalance, + ); + createAccountVTokenBalanceOfMock(aaaTokenAddress, supplier03, mintTokens); + + handleMintBehalf(mintBehalfEvent); + assert.fieldEquals('Market', market.id, 'supplierCount', '3'); + + const supplier04 = user4Address; + const mintBehalfEventV1 = createMintBehalfEventV1( + aaaTokenAddress, + supplier01, + supplier04, + actualMintAmount, + mintTokens, + ); + createAccountVTokenBalanceOfMock(aaaTokenAddress, supplier04, mintTokens); + + handleMintBehalfV1(mintBehalfEventV1); + assert.fieldEquals('Market', market.id, 'supplierCount', '4'); + + let redeemEvent = createRedeemEvent( + aaaTokenAddress, + supplier02, + actualMintAmount, + mintTokens, + zeroBigInt32, + ); + createAccountVTokenBalanceOfMock(aaaTokenAddress, supplier02, zeroBigInt32); + + handleRedeem(redeemEvent); + assert.fieldEquals('Market', market.id, 'supplierCount', '3'); + + const redeemEventV1 = createRedeemEventV1( + aaaTokenAddress, + supplier01, + halfActualMintAmount, + halfMintTokens, + ); + createAccountVTokenBalanceOfMock(aaaTokenAddress, supplier01, halfMintTokens); + + handleRedeemV1(redeemEventV1); + assert.fieldEquals('Market', market.id, 'supplierCount', '3'); + + redeemEvent = createRedeemEvent( + aaaTokenAddress, + supplier01, + halfActualMintAmount, + halfMintTokens, + zeroBigInt32, + ); + createAccountVTokenBalanceOfMock(aaaTokenAddress, supplier01, zeroBigInt32); + + handleRedeem(redeemEvent); + assert.fieldEquals('Market', market.id, 'supplierCount', '2'); + }); + + test('registers increase and decrease in the market borrower count', () => { + const market = getMarket(aaaTokenAddress); + assert.assertNotNull(market); + if (!market) { + return; + } + assert.fieldEquals('Market', market.id, 'borrowerCount', '0'); + + const borrowAmount = BigInt.fromI64(22); + const partialBorrowAmountWei = borrowAmount.div(BigInt.fromI64(2)); + + const borrower01 = user1Address; + let borrowEvent = createBorrowEvent( + aaaTokenAddress, + borrower01, + borrowAmount, + borrowAmount, + borrowAmount, + ); + + handleBorrow(borrowEvent); + assert.fieldEquals('Market', aaaTokenAddress.toHex(), 'borrowerCount', '1'); + assert.fieldEquals('Market', aaaTokenAddress.toHex(), 'borrowerCountAdjusted', '1'); + + const borrower02 = user2Address; + borrowEvent = createBorrowEvent( + aaaTokenAddress, + borrower02, + borrowAmount, + borrowAmount, + borrowAmount, + ); + + handleBorrow(borrowEvent); + assert.fieldEquals('Market', aaaTokenAddress.toHex(), 'borrowerCount', '2'); + assert.fieldEquals('Market', aaaTokenAddress.toHex(), 'borrowerCountAdjusted', '2'); + + let repayEvent = createRepayBorrowEvent( + aaaTokenAddress, + borrower02, + borrower02, + borrowAmount, + zeroBigInt32, + zeroBigInt32, + ); + + handleRepayBorrow(repayEvent); + assert.fieldEquals('Market', aaaTokenAddress.toHex(), 'borrowerCount', '1'); + assert.fieldEquals('Market', aaaTokenAddress.toHex(), 'borrowerCountAdjusted', '1'); + + repayEvent = createRepayBorrowEvent( + aaaTokenAddress, + borrower01, + borrower01, + partialBorrowAmountWei, + partialBorrowAmountWei, + partialBorrowAmountWei, + ); + + handleRepayBorrow(repayEvent); + assert.fieldEquals('Market', aaaTokenAddress.toHex(), 'borrowerCount', '1'); + assert.fieldEquals('Market', aaaTokenAddress.toHex(), 'borrowerCountAdjusted', '1'); + + repayEvent = createRepayBorrowEvent( + aaaTokenAddress, + borrower01, + borrower01, + oneBigInt, + partialBorrowAmountWei.minus(oneBigInt), + partialBorrowAmountWei.minus(oneBigInt), + ); - test('registers accrue interest event', () => { - /** Constants */ - const cashPrior = BigInt.fromString('1246205398726345'); - const interestAccumulated = BigInt.fromI32(26454); - const borrowIndex = BigInt.fromI32(1); - const totalBorrows = BigInt.fromString('62197468301'); - - /** Setup test */ - const accrueInterestEvent = createAccrueInterestEvent( - aaaTokenAddress, - cashPrior, - interestAccumulated, - borrowIndex, - totalBorrows, - ); - - /** Fire Event */ - handleAccrueInterest(accrueInterestEvent); - - const assertMarketDocument = (key: string, value: string): void => { - assert.fieldEquals('Market', aaaTokenAddress.toHexString(), key, value); - }; - - assertMarketDocument('accrualBlockNumber', '999'); - assertMarketDocument('blockTimestamp', accrueInterestEvent.block.timestamp.toString()); - assertMarketDocument('exchangeRateMantissa', '365045823500000000000000'); - assertMarketDocument('borrowIndexMantissa', '300000000000000000000'); - assertMarketDocument('reservesMantissa', '5128924555022289393'); - assertMarketDocument('totalBorrowsMantissa', '2641234234636158123'); - assertMarketDocument('cashMantissa', '1418171344423412457'); - assertMarketDocument('borrowRateMantissa', '12678493'); - assertMarketDocument('supplyRateMantissa', '12678493'); - }); - - test('registers new reserve factor', () => { - const oldReserveFactor = BigInt.fromI64(12462053079875); - const newReserveFactor = BigInt.fromI64(37035970026454); - const reserveFactorEvent = createNewReserveFactorEvent( - aaaTokenAddress, - oldReserveFactor, - newReserveFactor, - ); - - handleNewReserveFactor(reserveFactorEvent); - assert.fieldEquals('Market', aaaTokenAddress.toHex(), 'id', aaaTokenAddress.toHexString()); - assert.fieldEquals( - 'Market', - aaaTokenAddress.toHex(), - 'reserveFactor', - newReserveFactor.toString(), - ); - }); - - test('registers transfer from event', () => { - /** Constants */ - const from = user1Address; // 101 - const to = aaaTokenAddress; - const amount = BigInt.fromString('146205398726345'); - const balanceOf = BigInt.fromString('262059874253345'); - - /** Setup test */ - const transferEvent = createTransferEvent(aaaTokenAddress, from, to, amount); - createMockedFunction( - aaaTokenAddress, - 'getAccountSnapshot', - 'getAccountSnapshot(address):(uint256,uint256,uint256,uint256)', - ) - .withArgs([ethereum.Value.fromAddress(from)]) - .returns([ - ethereum.Value.fromSignedBigInt(zeroBigInt32), - ethereum.Value.fromSignedBigInt(balanceOf), - ethereum.Value.fromSignedBigInt(zeroBigInt32), - ethereum.Value.fromSignedBigInt(oneBigInt), - ]); - - /** Fire Event */ - handleTransfer(transferEvent); - - const accountVTokenId = getAccountVTokenId(aaaTokenAddress, from); - - /** AccountVToken */ - assert.fieldEquals( - 'AccountVToken', - accountVTokenId, - 'accrualBlockNumber', - transferEvent.block.number.toString(), - ); - - assert.fieldEquals('AccountVToken', accountVTokenId, 'accountBorrowIndexMantissa', '0'); - - assert.fieldEquals( - 'AccountVToken', - accountVTokenId, - 'totalUnderlyingRedeemedMantissa', - '53371670178204461670107500000000000000', - ); - }); - - test('registers transfer to event', () => { - /** Constants */ - const amount = BigInt.fromString('5246205398726345'); - const from = aaaTokenAddress; - const to = user2Address; - const balanceOf = BigInt.fromString('262059874253345'); - - /** Setup test */ - const transferEvent = createTransferEvent(aaaTokenAddress, from, to, amount); - createMockedFunction( - aaaTokenAddress, - 'getAccountSnapshot', - 'getAccountSnapshot(address):(uint256,uint256,uint256,uint256)', - ) - .withArgs([ethereum.Value.fromAddress(to)]) - .returns([ - ethereum.Value.fromSignedBigInt(zeroBigInt32), - ethereum.Value.fromSignedBigInt(balanceOf), - ethereum.Value.fromSignedBigInt(zeroBigInt32), - ethereum.Value.fromSignedBigInt(oneBigInt), - ]); - - /** Fire Event */ - handleTransfer(transferEvent); - - const accountVTokenId = getAccountVTokenId(aaaTokenAddress, to); - - /** AccountVToken */ - assert.fieldEquals( - 'AccountVToken', - accountVTokenId, - 'accrualBlockNumber', - transferEvent.block.number.toString(), - ); - - assert.fieldEquals('AccountVToken', accountVTokenId, 'accountBorrowIndexMantissa', '0'); - }); - - test('registers new interest rate model', () => { - const oldInterestRateModel = Address.fromString('0x0000000000000000000000000000000000000e0e'); - const newInterestRateModel = Address.fromString('0x0000000000000000000000000000000000000f0f'); - const newMarketInterestRateModelEvent = createNewMarketInterestRateModelEvent( - aaaTokenAddress, - oldInterestRateModel, - newInterestRateModel, - ); - - handleNewMarketInterestRateModel(newMarketInterestRateModelEvent); - assert.fieldEquals('Market', aaaTokenAddress.toHex(), 'id', aaaTokenAddress.toHexString()); - assert.fieldEquals( - 'Market', - aaaTokenAddress.toHex(), - 'interestRateModelAddress', - newInterestRateModel.toHexString(), - ); - }); - - test('registers increase and decrease in the market supplier count', () => { - const market = getMarket(aaaTokenAddress); - assert.assertNotNull(market); - if (!market) { - return; - } - - const actualMintAmount = BigInt.fromI64(12); - const halfActualMintAmount = actualMintAmount.div(BigInt.fromI64(2)); - const mintTokens = BigInt.fromI64(10); - const accountBalance = mintTokens; - const halfMintTokens = mintTokens.div(BigInt.fromI64(2)); - - const supplier01 = user1Address; - let mintEvent = createMintEvent( - aaaTokenAddress, - supplier01, - actualMintAmount, - mintTokens, - accountBalance, - ); - createAccountVTokenBalanceOfMock(aaaTokenAddress, supplier01, mintTokens); - - handleMint(mintEvent); - - assert.fieldEquals('Market', aaaTokenAddress.toHex(), 'supplierCount', '2'); - - const supplier02 = user2Address; - mintEvent = createMintEvent( - aaaTokenAddress, - supplier02, - actualMintAmount, - mintTokens, - accountBalance, - ); - createAccountVTokenBalanceOfMock(aaaTokenAddress, supplier02, mintTokens); - - handleMint(mintEvent); - assert.fieldEquals('Market', aaaTokenAddress.toHex(), 'supplierCount', '3'); - - let redeemEvent = createRedeemEvent( - aaaTokenAddress, - supplier02, - actualMintAmount, - mintTokens, - zeroBigInt32, - ); - createAccountVTokenBalanceOfMock(aaaTokenAddress, supplier02, zeroBigInt32); - - handleRedeem(redeemEvent); - assert.fieldEquals('Market', aaaTokenAddress.toHex(), 'supplierCount', '2'); - - redeemEvent = createRedeemEvent( - aaaTokenAddress, - supplier01, - halfActualMintAmount, - halfMintTokens, - halfMintTokens, - ); - createAccountVTokenBalanceOfMock(aaaTokenAddress, supplier01, halfMintTokens); - - handleRedeem(redeemEvent); - assert.fieldEquals('Market', aaaTokenAddress.toHex(), 'supplierCount', '1'); - - redeemEvent = createRedeemEvent( - aaaTokenAddress, - supplier01, - halfActualMintAmount, - halfMintTokens, - zeroBigInt32, - ); - createAccountVTokenBalanceOfMock(aaaTokenAddress, supplier01, zeroBigInt32); - - handleRedeem(redeemEvent); - assert.fieldEquals('Market', aaaTokenAddress.toHex(), 'supplierCount', '0'); - }); - - test('registers increase and decrease in the market borrower count', () => { - const market = getMarket(aaaTokenAddress); - assert.assertNotNull(market); - if (!market) { - return; - } - - const borrowAmount = BigInt.fromI64(10); - const halfBorrowAmountTokens = borrowAmount.div(BigInt.fromI64(2)); - - const borrower01 = user1Address; - let borrowEvent = createBorrowEvent( - aaaTokenAddress, - borrower01, - borrowAmount, - borrowAmount, - borrowAmount, - ); - - handleBorrow(borrowEvent); - assert.fieldEquals('Market', aaaTokenAddress.toHex(), 'borrowerCount', '2'); - assert.fieldEquals('Market', aaaTokenAddress.toHex(), 'borrowerCountAdjusted', '2'); - - const borrower02 = user2Address; - borrowEvent = createBorrowEvent( - aaaTokenAddress, - borrower02, - borrowAmount, - borrowAmount, - borrowAmount, - ); - - handleBorrow(borrowEvent); - assert.fieldEquals('Market', aaaTokenAddress.toHex(), 'borrowerCount', '3'); - assert.fieldEquals('Market', aaaTokenAddress.toHex(), 'borrowerCountAdjusted', '3'); - - let repayEvent = createRepayBorrowEvent( - aaaTokenAddress, - borrower02, - borrower02, - borrowAmount, - zeroBigInt32, - zeroBigInt32, - ); - - handleRepayBorrow(repayEvent); - assert.fieldEquals('Market', aaaTokenAddress.toHex(), 'borrowerCount', '2'); - assert.fieldEquals('Market', aaaTokenAddress.toHex(), 'borrowerCountAdjusted', '2'); - - repayEvent = createRepayBorrowEvent( - aaaTokenAddress, - borrower01, - borrower01, - halfBorrowAmountTokens, - halfBorrowAmountTokens, - halfBorrowAmountTokens, - ); - - handleRepayBorrow(repayEvent); - assert.fieldEquals('Market', aaaTokenAddress.toHex(), 'borrowerCount', '1'); - assert.fieldEquals('Market', aaaTokenAddress.toHex(), 'borrowerCountAdjusted', '1'); - - repayEvent = createRepayBorrowEvent( - aaaTokenAddress, - borrower01, - borrower01, - halfBorrowAmountTokens, - oneBigInt, - oneBigInt, - ); - - handleRepayBorrow(repayEvent); - assert.fieldEquals('Market', aaaTokenAddress.toHex(), 'borrowerCount', '1'); - assert.fieldEquals('Market', aaaTokenAddress.toHex(), 'borrowerCountAdjusted', '1'); - }); + handleRepayBorrow(repayEvent); + assert.fieldEquals('Market', aaaTokenAddress.toHex(), 'borrowerCount', '1'); + assert.fieldEquals('Market', aaaTokenAddress.toHex(), 'borrowerCountAdjusted', '0'); }); }); diff --git a/yarn.lock b/yarn.lock index ddda6f48..9baeb27e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4309,6 +4309,20 @@ __metadata: languageName: node linkType: hard +"@venusprotocol/venus-protocol-orig-events@npm:@venusprotocol/venus-protocol@2.2.1": + version: 2.2.1 + resolution: "@venusprotocol/venus-protocol@npm:2.2.1" + dependencies: + "@openzeppelin/contracts": ^4.8.3 + "@openzeppelin/contracts-upgradeable": ^4.8.0 + dotenv: ^16.0.1 + module-alias: ^2.2.2 + peerDependencies: + hardhat: ^2.10.1 + checksum: 797c6f141af571d6448a70c607dea92b8db24bc5b31c97ae13c67ae411873718b66394b37c3abf86154107a3ff5299e717864f0ea40e4bf5ed9c12ea286b0cea + languageName: node + linkType: hard + "@venusprotocol/venus-protocol@npm:0.7.0": version: 0.7.0 resolution: "@venusprotocol/venus-protocol@npm:0.7.0" @@ -6534,6 +6548,12 @@ __metadata: languageName: node linkType: hard +"core-pool-abis@workspace:packages/core-pool-abis": + version: 0.0.0-use.local + resolution: "core-pool-abis@workspace:packages/core-pool-abis" + languageName: unknown + linkType: soft + "core-util-is@npm:1.0.2": version: 1.0.2 resolution: "core-util-is@npm:1.0.2" @@ -14860,7 +14880,6 @@ __metadata: "@venusprotocol/governance-contracts": ^1.3.0 "@venusprotocol/isolated-pools": 2.1.0-dev.2 "@venusprotocol/oracle": ^1.7.3-dev.1 - "@venusprotocol/venus-protocol": 3.1.0 assemblyscript: 0.19.23 chai: ^4.3.6 eslint: ^8.25.0 @@ -15942,12 +15961,6 @@ __metadata: languageName: node linkType: hard -"venus-cross-chain-governance-subgraph@workspace:subgraphs/cross-chain-governance": - version: 0.0.0-use.local - resolution: "venus-cross-chain-governance-subgraph@workspace:subgraphs/cross-chain-governance" - languageName: unknown - linkType: soft - "venus-governance-abis@workspace:packages/venus-governance-abis": version: 0.0.0-use.local resolution: "venus-governance-abis@workspace:packages/venus-governance-abis" @@ -15975,6 +15988,9 @@ __metadata: "venus-subgraph@workspace:subgraphs/venus": version: 0.0.0-use.local resolution: "venus-subgraph@workspace:subgraphs/venus" + dependencies: + "@venusprotocol/venus-protocol": 3.1.0 + "@venusprotocol/venus-protocol-orig-events": "npm:@venusprotocol/venus-protocol@2.2.1" languageName: unknown linkType: soft From ec47f8931f627a3d3aea6252ebb5584f55396a4b Mon Sep 17 00:00:00 2001 From: Corey Rice Date: Thu, 2 Nov 2023 14:32:36 -0300 Subject: [PATCH 3/4] refactor: save vToken event amounts as mantissa --- subgraphs/venus/schema.graphql | 18 +++++------ subgraphs/venus/src/mappings/vToken.ts | 43 +++++--------------------- 2 files changed, 16 insertions(+), 45 deletions(-) diff --git a/subgraphs/venus/schema.graphql b/subgraphs/venus/schema.graphql index 30270f79..7dadc7f8 100644 --- a/subgraphs/venus/schema.graphql +++ b/subgraphs/venus/schema.graphql @@ -177,7 +177,7 @@ type TransferEvent implements VTokenTransfer @entity { "Transaction hash concatenated with log index" id: ID! "vTokens transferred" - amount: BigDecimal! + amountMantissa: BigInt! "Account that received tokens" to: Bytes! "Account that sent tokens" @@ -244,7 +244,7 @@ type LiquidationEvent implements VTokenTransfer @entity { "Transaction hash concatenated with log index" id: ID! "vTokens seized" - amount: BigDecimal! + amountMantissa: BigInt! "Liquidator receiving tokens" to: Bytes! "Account being liquidated (borrower)" @@ -258,7 +258,7 @@ type LiquidationEvent implements VTokenTransfer @entity { "Symbol of the underlying asset repaid through liquidation" underlyingSymbol: String! "Underlying vToken amount that was repaid by liquidator" - underlyingRepayAmount: BigDecimal! + underlyingRepayAmountMantissa: BigInt! } """ @@ -269,9 +269,9 @@ interface UnderlyingTransfer { "Transaction hash concatenated with log index" id: ID! "Amount of underlying borrowed" - amount: BigDecimal! + amountMantissa: BigInt! "Total borrows of this asset the account has" - accountBorrows: BigDecimal! + accountBorrowsMantissa: BigInt! "Account that borrowed the tokens" borrower: Bytes! "Block number" @@ -289,9 +289,9 @@ type BorrowEvent implements UnderlyingTransfer @entity { "Transaction hash concatenated with log index" id: ID! "Amount of underlying borrowed" - amount: BigDecimal! + amountMantissa: BigInt! "Total borrows of this asset the account has" - accountBorrows: BigDecimal! + accountBorrowsMantissa: BigInt! "Account that borrowed the tokens" borrower: Bytes! "Block number" @@ -310,9 +310,9 @@ type RepayEvent implements UnderlyingTransfer @entity { "Transaction hash concatenated with log index" id: ID! "Amount of underlying repaid" - amount: BigDecimal! + amountMantissa: BigInt! "Total borrows of this asset the account has" - accountBorrows: BigDecimal! + accountBorrowsMantissa: BigInt! "Account that borrowed the tokens" borrower: Bytes! "Block number" diff --git a/subgraphs/venus/src/mappings/vToken.ts b/subgraphs/venus/src/mappings/vToken.ts index ee59f570..090f0e19 100644 --- a/subgraphs/venus/src/mappings/vToken.ts +++ b/subgraphs/venus/src/mappings/vToken.ts @@ -160,19 +160,9 @@ export const handleBorrow = (event: Borrow): void => { .concat('-') .concat(event.transactionLogIndex.toString()); - let borrowAmount = event.params.borrowAmount - .toBigDecimal() - .div(exponentToBigDecimal(market.underlyingDecimals)) - .truncate(market.underlyingDecimals); - - let accountBorrows = event.params.accountBorrows - .toBigDecimal() - .div(exponentToBigDecimal(market.underlyingDecimals)) - .truncate(market.underlyingDecimals); - let borrow = new BorrowEvent(borrowID); - borrow.amount = borrowAmount; - borrow.accountBorrows = accountBorrows; + borrow.amountMantissa = event.params.borrowAmount; + borrow.accountBorrowsMantissa = event.params.accountBorrows; borrow.borrower = event.params.borrower; borrow.blockNumber = event.block.number.toI32(); borrow.blockTime = event.block.timestamp.toI32(); @@ -242,19 +232,9 @@ export const handleRepayBorrow = (event: RepayBorrow): void => { .concat('-') .concat(event.transactionLogIndex.toString()); - let repayAmount = event.params.repayAmount - .toBigDecimal() - .div(exponentToBigDecimal(market.underlyingDecimals)) - .truncate(market.underlyingDecimals); - - let accountBorrows = event.params.accountBorrows - .toBigDecimal() - .div(exponentToBigDecimal(market.underlyingDecimals)) - .truncate(market.underlyingDecimals); - let repay = new RepayEvent(repayID); - repay.amount = repayAmount; - repay.accountBorrows = accountBorrows; + repay.amountMantissa = event.params.repayAmount; + repay.accountBorrowsMantissa = event.params.accountBorrows; repay.borrower = event.params.borrower; repay.blockNumber = event.block.number.toI32(); repay.blockTime = event.block.timestamp.toI32(); @@ -323,24 +303,15 @@ export const handleLiquidateBorrow = (event: LiquidateBorrow): void => { .toHexString() .concat('-') .concat(event.transactionLogIndex.toString()); - let vTokenDecimals = marketRepayToken.vTokenDecimals; - let vTokenAmount = event.params.seizeTokens - .toBigDecimal() - .div(exponentToBigDecimal(vTokenDecimals)) - .truncate(vTokenDecimals); - let underlyingRepayAmount = event.params.repayAmount - .toBigDecimal() - .div(exponentToBigDecimal(marketRepayToken.underlyingDecimals)) - .truncate(marketRepayToken.underlyingDecimals); let liquidation = new LiquidationEvent(mintID); - liquidation.amount = vTokenAmount; + liquidation.amountMantissa = event.params.seizeTokens; liquidation.to = event.params.liquidator; liquidation.from = event.params.borrower; liquidation.blockNumber = event.block.number.toI32(); liquidation.blockTime = event.block.timestamp.toI32(); liquidation.underlyingSymbol = marketRepayToken.underlyingSymbol; - liquidation.underlyingRepayAmount = underlyingRepayAmount; + liquidation.underlyingRepayAmountMantissa = event.params.repayAmount; liquidation.vTokenSymbol = marketVTokenLiquidated.symbol; liquidation.save(); }; @@ -445,7 +416,7 @@ export const handleTransfer = (event: Transfer): void => { let transferId = getTransactionId(event.transaction.hash, event.transactionLogIndex); let transfer = new TransferEvent(transferId); - transfer.amount = event.params.amount.toBigDecimal().div(exponentToBigDecimal(vTokenDecimals)); + transfer.amountMantissa = event.params.amount; transfer.to = event.params.to; transfer.from = event.params.from; transfer.blockNumber = event.block.number.toI32(); From b19bcf215144606d129a9e2771a22afafeb83d1d Mon Sep 17 00:00:00 2001 From: Corey Rice Date: Thu, 2 Nov 2023 14:37:26 -0300 Subject: [PATCH 4/4] refactor: vToken amounts as mantissa --- subgraphs/isolated-pools/schema.graphql | 6 +-- .../isolated-pools/src/mappings/vToken.ts | 2 - .../src/operations/getOrCreate.ts | 6 +-- .../isolated-pools/src/operations/update.ts | 17 +------ .../isolated-pools/src/utilities/index.ts | 1 + .../isolated-pools/tests/VToken/index.test.ts | 2 +- subgraphs/venus/schema.graphql | 12 ++--- subgraphs/venus/src/mappings/comptroller.ts | 5 +- subgraphs/venus/src/mappings/vToken.ts | 48 +++++++------------ subgraphs/venus/src/operations/create.ts | 16 +++---- subgraphs/venus/tests/Comptroller.test.ts | 2 +- subgraphs/venus/tests/VToken/index.test.ts | 2 +- 12 files changed, 43 insertions(+), 76 deletions(-) diff --git a/subgraphs/isolated-pools/schema.graphql b/subgraphs/isolated-pools/schema.graphql index af84bec5..d0e29212 100644 --- a/subgraphs/isolated-pools/schema.graphql +++ b/subgraphs/isolated-pools/schema.graphql @@ -183,12 +183,12 @@ type AccountVToken @entity { "True if user is entered, false if they are exited" enteredMarket: Boolean! - "Total amount of underling redeemed" - totalUnderlyingRedeemedMantissa: BigDecimal! + "Total amount of underlying redeemed" + totalUnderlyingRedeemedMantissa: BigInt! "The value of the borrow index upon users last interaction" accountBorrowIndexMantissa: BigInt! "Total amount underlying repaid" - totalUnderlyingRepaidMantissa: BigDecimal! + totalUnderlyingRepaidMantissa: BigInt! } """ diff --git a/subgraphs/isolated-pools/src/mappings/vToken.ts b/subgraphs/isolated-pools/src/mappings/vToken.ts index db16ba13..1b6ba2fe 100644 --- a/subgraphs/isolated-pools/src/mappings/vToken.ts +++ b/subgraphs/isolated-pools/src/mappings/vToken.ts @@ -258,8 +258,6 @@ export function handleTransfer(event: Transfer): void { event.logIndex, event.params.amount, market.exchangeRateMantissa, - market.underlyingDecimals, - market.vTokenDecimals, ); } diff --git a/subgraphs/isolated-pools/src/operations/getOrCreate.ts b/subgraphs/isolated-pools/src/operations/getOrCreate.ts index b8772029..47b375b8 100644 --- a/subgraphs/isolated-pools/src/operations/getOrCreate.ts +++ b/subgraphs/isolated-pools/src/operations/getOrCreate.ts @@ -13,7 +13,7 @@ import { } from '../../generated/schema'; import { Comptroller } from '../../generated/templates/Pool/Comptroller'; import { RewardsDistributor as RewardDistributorContract } from '../../generated/templates/RewardsDistributor/RewardsDistributor'; -import { zeroBigDecimal, zeroBigInt32 } from '../constants'; +import { zeroBigInt32 } from '../constants'; import { getAccountVTokenId, getAccountVTokenTransactionId, @@ -108,9 +108,9 @@ export const getOrCreateAccountVToken = ( accountVToken.accountSupplyBalanceMantissa = suppliedAmountMantissa; accountVToken.accountBorrowBalanceMantissa = borrowedAmountMantissa; - accountVToken.totalUnderlyingRedeemedMantissa = zeroBigDecimal; + accountVToken.totalUnderlyingRedeemedMantissa = zeroBigInt32; accountVToken.accountBorrowIndexMantissa = zeroBigInt32; - accountVToken.totalUnderlyingRepaidMantissa = zeroBigDecimal; + accountVToken.totalUnderlyingRepaidMantissa = zeroBigInt32; } return accountVToken; }; diff --git a/subgraphs/isolated-pools/src/operations/update.ts b/subgraphs/isolated-pools/src/operations/update.ts index 746e4bcd..e48c6673 100644 --- a/subgraphs/isolated-pools/src/operations/update.ts +++ b/subgraphs/isolated-pools/src/operations/update.ts @@ -3,11 +3,7 @@ import { Address, BigInt, Bytes } from '@graphprotocol/graph-ts'; import { PoolMetadataUpdatedNewMetadataStruct } from '../../generated/PoolRegistry/PoolRegistry'; import { AccountVToken, Market } from '../../generated/schema'; import { VToken } from '../../generated/templates/VToken/VToken'; -import { - exponentToBigDecimal, - getExchangeRateBigDecimal, - valueOrNotAvailableIntIfReverted, -} from '../utilities'; +import { exponentToBigInt, valueOrNotAvailableIntIfReverted } from '../utilities'; import { getTokenPriceInCents } from '../utilities'; import { getOrCreateMarket } from './getOrCreate'; import { @@ -118,17 +114,8 @@ export const updateAccountVTokenTransferFrom = ( logIndex: BigInt, amount: BigInt, exchangeRate: BigInt, - underlyingDecimals: i32, - vTokenDecimals: i32, ): AccountVToken => { - const exchangeRateBigDecimal = getExchangeRateBigDecimal( - exchangeRate, - underlyingDecimals, - vTokenDecimals, - ); - const amountUnderlyingMantissa = exchangeRateBigDecimal - .times(exponentToBigDecimal(underlyingDecimals)) - .times(amount.toBigDecimal()); + const amountUnderlyingMantissa = exchangeRate.div(exponentToBigInt(18)).times(amount); const accountVToken = updateAccountVToken( marketAddress, diff --git a/subgraphs/isolated-pools/src/utilities/index.ts b/subgraphs/isolated-pools/src/utilities/index.ts index 2a14a150..c454360c 100644 --- a/subgraphs/isolated-pools/src/utilities/index.ts +++ b/subgraphs/isolated-pools/src/utilities/index.ts @@ -1,4 +1,5 @@ export { default as getExchangeRateBigDecimal } from './getExchangeRateBigDecimal'; export { default as getTokenPriceInCents } from './getTokenPriceInCents'; export { default as exponentToBigDecimal } from './exponentToBigDecimal'; +export { default as exponentToBigInt } from './exponentToBigInt'; export { default as valueOrNotAvailableIntIfReverted } from './valueOrNotAvailableIntIfReverted'; diff --git a/subgraphs/isolated-pools/tests/VToken/index.test.ts b/subgraphs/isolated-pools/tests/VToken/index.test.ts index b468dbc2..5321ef02 100644 --- a/subgraphs/isolated-pools/tests/VToken/index.test.ts +++ b/subgraphs/isolated-pools/tests/VToken/index.test.ts @@ -661,7 +661,7 @@ describe('VToken', () => { 'AccountVToken', accountVTokenId, 'totalUnderlyingRedeemedMantissa', - '53371670178204461670107500000000000000', + '53371549778058610525', ); }); diff --git a/subgraphs/venus/schema.graphql b/subgraphs/venus/schema.graphql index 7dadc7f8..7d32796b 100644 --- a/subgraphs/venus/schema.graphql +++ b/subgraphs/venus/schema.graphql @@ -24,7 +24,7 @@ type Market @entity { "The vToken contract balance of BEP20 or BNB" cashMantissa: BigInt! "Collateral factor determining how much one can borrow" - collateralFactor: BigDecimal! + collateralFactorMantissa: BigInt! "Exchange rate of tokens / vTokens" exchangeRateMantissa: BigInt! "Address of the interest rate model" @@ -116,19 +116,19 @@ type AccountVToken @entity { enteredMarket: Boolean! "VToken balance of the user" - vTokenBalance: BigDecimal! + vTokenBalanceMantissa: BigInt! "Total amount of underlying supplied" totalUnderlyingSuppliedMantissa: BigInt! - "Total amount of underling redeemed" + "Total amount of underlying redeemed" totalUnderlyingRedeemedMantissa: BigInt! "The value of the borrow index upon users last interaction" accountBorrowIndexMantissa: BigInt! "Total amount underlying borrowed, exclusive of interest" - totalUnderlyingBorrowed: BigDecimal! + totalUnderlyingBorrowedMantissa: BigInt! "Total amount underlying repaid" - totalUnderlyingRepaid: BigDecimal! + totalUnderlyingRepaidMantissa: BigInt! "Current borrow balance stored in contract (exclusive of interest since accrualBlockNumber)" - storedBorrowBalance: BigDecimal! + storedBorrowBalanceMantissa: BigInt! } """ diff --git a/subgraphs/venus/src/mappings/comptroller.ts b/subgraphs/venus/src/mappings/comptroller.ts index 3c471609..f599f61a 100644 --- a/subgraphs/venus/src/mappings/comptroller.ts +++ b/subgraphs/venus/src/mappings/comptroller.ts @@ -14,7 +14,6 @@ import { } from '../../generated/Comptroller/Comptroller'; import { Account, Market } from '../../generated/schema'; import { VToken } from '../../generated/templates'; -import { mantissaFactorBigDecimal } from '../constants'; import { createAccount, createMarket } from '../operations/create'; import { getOrCreateComptroller } from '../operations/getOrCreate'; import { updateCommonVTokenStats } from '../operations/update'; @@ -110,9 +109,7 @@ export const handleNewCollateralFactor = (event: NewCollateralFactor): void => { // sources can source from the contract creation block and not the time the // comptroller adds the market, we can avoid this altogether if (market != null) { - market.collateralFactor = event.params.newCollateralFactorMantissa - .toBigDecimal() - .div(mantissaFactorBigDecimal); + market.collateralFactorMantissa = event.params.newCollateralFactorMantissa; market.save(); } }; diff --git a/subgraphs/venus/src/mappings/vToken.ts b/subgraphs/venus/src/mappings/vToken.ts index 090f0e19..e095b0a0 100644 --- a/subgraphs/venus/src/mappings/vToken.ts +++ b/subgraphs/venus/src/mappings/vToken.ts @@ -37,7 +37,7 @@ import { } from '../operations/create'; import { updateCommonVTokenStats } from '../operations/update'; import { updateMarket } from '../operations/update'; -import { exponentToBigDecimal } from '../utilities/exponentToBigDecimal'; +import { exponentToBigInt } from '../utilities/exponentToBigInt'; import { getMarketId, getTransactionId } from '../utilities/ids'; /* Account supplies assets into market and receives vTokens in exchange @@ -142,17 +142,12 @@ export const handleBorrow = (event: Borrow): void => { event.logIndex, ); - let borrowAmountBD = event.params.borrowAmount - .toBigDecimal() - .div(exponentToBigDecimal(market.underlyingDecimals)); - - vTokenStats.storedBorrowBalance = event.params.accountBorrows - .toBigDecimal() - .div(exponentToBigDecimal(market.underlyingDecimals)) - .truncate(market.underlyingDecimals); + vTokenStats.storedBorrowBalanceMantissa = event.params.accountBorrows; vTokenStats.accountBorrowIndexMantissa = market.borrowIndexMantissa; - vTokenStats.totalUnderlyingBorrowed = vTokenStats.totalUnderlyingBorrowed.plus(borrowAmountBD); + vTokenStats.totalUnderlyingBorrowedMantissa = vTokenStats.totalUnderlyingBorrowedMantissa.plus( + event.params.borrowAmount, + ); vTokenStats.save(); let borrowID = event.transaction.hash @@ -214,17 +209,12 @@ export const handleRepayBorrow = (event: RepayBorrow): void => { event.logIndex, ); - let repayAmountBD = event.params.repayAmount - .toBigDecimal() - .div(exponentToBigDecimal(market.underlyingDecimals)); - - vTokenStats.storedBorrowBalance = event.params.accountBorrows - .toBigDecimal() - .div(exponentToBigDecimal(market.underlyingDecimals)) - .truncate(market.underlyingDecimals); + vTokenStats.storedBorrowBalanceMantissa = event.params.accountBorrows; vTokenStats.accountBorrowIndexMantissa = market.borrowIndexMantissa; - vTokenStats.totalUnderlyingRepaid = vTokenStats.totalUnderlyingRepaid.plus(repayAmountBD); + vTokenStats.totalUnderlyingRepaidMantissa = vTokenStats.totalUnderlyingRepaidMantissa.plus( + event.params.repayAmount, + ); vTokenStats.save(); let repayID = event.transaction.hash @@ -342,8 +332,10 @@ export const handleTransfer = (event: Transfer): void => { if (market.accrualBlockNumber != event.block.number.toI32()) { market = updateMarket(event.address, event.block.number.toI32(), event.block.timestamp.toI32()); } - let vTokenDecimals = market.vTokenDecimals; - let amountUnderlying = market.exchangeRateMantissa.times(event.params.amount); + + let amountUnderlying = market.exchangeRateMantissa + .times(event.params.amount) + .div(exponentToBigInt(18)); // Checking if the tx is FROM the vToken contract (i.e. this will not run when minting) // If so, it is a mint, and we don't need to run these calculations @@ -366,11 +358,8 @@ export const handleTransfer = (event: Transfer): void => { event.logIndex, ); - vTokenStatsFrom.vTokenBalance = vTokenStatsFrom.vTokenBalance.minus( - event.params.amount - .toBigDecimal() - .div(exponentToBigDecimal(vTokenDecimals)) - .truncate(vTokenDecimals), + vTokenStatsFrom.vTokenBalanceMantissa = vTokenStatsFrom.vTokenBalanceMantissa.minus( + event.params.amount, ); vTokenStatsFrom.totalUnderlyingRedeemedMantissa = @@ -401,11 +390,8 @@ export const handleTransfer = (event: Transfer): void => { event.logIndex, ); - vTokenStatsTo.vTokenBalance = vTokenStatsTo.vTokenBalance.plus( - event.params.amount - .toBigDecimal() - .div(exponentToBigDecimal(vTokenDecimals)) - .truncate(vTokenDecimals), + vTokenStatsTo.vTokenBalanceMantissa = vTokenStatsTo.vTokenBalanceMantissa.plus( + event.params.amount, ); vTokenStatsTo.totalUnderlyingSuppliedMantissa = diff --git a/subgraphs/venus/src/operations/create.ts b/subgraphs/venus/src/operations/create.ts index 30a0c6f4..5c79e661 100644 --- a/subgraphs/venus/src/operations/create.ts +++ b/subgraphs/venus/src/operations/create.ts @@ -1,10 +1,10 @@ -import { Address, BigDecimal, BigInt, log } from '@graphprotocol/graph-ts'; +import { Address, BigInt, log } from '@graphprotocol/graph-ts'; import { Account, AccountVToken, Market, MintEvent, RedeemEvent } from '../../generated/schema'; import { BEP20 } from '../../generated/templates/VToken/BEP20'; import { VBep20Storage } from '../../generated/templates/VToken/VBep20Storage'; import { VToken } from '../../generated/templates/VToken/VToken'; -import { zeroBigDecimal, zeroBigInt32 } from '../constants'; +import { zeroBigInt32 } from '../constants'; import { nullAddress, vBnbAddress } from '../constants/addresses'; import { getUnderlyingPrice } from '../utilities/getUnderlyingPrice'; import { getTransactionId } from '../utilities/ids'; @@ -22,16 +22,14 @@ export function createAccountVToken( accountVToken.accrualBlockNumber = BigInt.fromI32(0); // we need to set an initial real onchain value to this otherwise it will never be accurate const vTokenContract = BEP20.bind(Address.fromString(marketId)); - accountVToken.vTokenBalance = new BigDecimal( - vTokenContract.balanceOf(Address.fromString(account)), - ); + accountVToken.vTokenBalanceMantissa = vTokenContract.balanceOf(Address.fromString(account)); accountVToken.totalUnderlyingSuppliedMantissa = zeroBigInt32; accountVToken.totalUnderlyingRedeemedMantissa = zeroBigInt32; accountVToken.accountBorrowIndexMantissa = zeroBigInt32; - accountVToken.totalUnderlyingBorrowed = zeroBigDecimal; - accountVToken.totalUnderlyingRepaid = zeroBigDecimal; - accountVToken.storedBorrowBalance = zeroBigDecimal; + accountVToken.totalUnderlyingBorrowedMantissa = zeroBigInt32; + accountVToken.totalUnderlyingRepaidMantissa = zeroBigInt32; + accountVToken.storedBorrowBalanceMantissa = zeroBigInt32; accountVToken.enteredMarket = false; return accountVToken; } @@ -83,7 +81,7 @@ export function createMarket(marketAddress: string): Market { market.borrowRateMantissa = zeroBigInt32; market.cashMantissa = zeroBigInt32; - market.collateralFactor = zeroBigDecimal; + market.collateralFactorMantissa = zeroBigInt32; market.exchangeRateMantissa = zeroBigInt32; market.interestRateModelAddress = interestRateModelAddress.reverted ? nullAddress diff --git a/subgraphs/venus/tests/Comptroller.test.ts b/subgraphs/venus/tests/Comptroller.test.ts index 15d9e624..85b2d82d 100644 --- a/subgraphs/venus/tests/Comptroller.test.ts +++ b/subgraphs/venus/tests/Comptroller.test.ts @@ -57,7 +57,7 @@ describe('handleMarketListing', () => { assertMarketDocument('underlyingPriceCents', '0'); assertMarketDocument('borrowRateMantissa', '0'); assertMarketDocument('cashMantissa', '0'); - assertMarketDocument('collateralFactor', '0'); + assertMarketDocument('collateralFactorMantissa', '0'); assertMarketDocument('exchangeRateMantissa', '0'); assertMarketDocument('interestRateModelAddress', interestRateModelAddress.toHex()); assertMarketDocument('name', 'Venus BNB'); diff --git a/subgraphs/venus/tests/VToken/index.test.ts b/subgraphs/venus/tests/VToken/index.test.ts index c9c0d26d..df4abd43 100644 --- a/subgraphs/venus/tests/VToken/index.test.ts +++ b/subgraphs/venus/tests/VToken/index.test.ts @@ -374,7 +374,7 @@ describe('VToken', () => { 'AccountVToken', accountVTokenId, 'totalUnderlyingRedeemedMantissa', - '53371670178204461670107500000000000000', + '53371670178204461670', ); });