diff --git a/README.md b/README.md index b9596d9b..335006e5 100644 --- a/README.md +++ b/README.md @@ -8,10 +8,6 @@ The following subgraphs are deployed to the BSC mainnet You can also run this subgraph locally, if you wish. Instructions for that can be found in [The Graph Documentation](https://thegraph.com/docs/quick-start). -### ABI - -The ABI used is `vtoken.json`. It is a stripped down version of the full abi provided by Venus, that satisfies the calls we need to make for both vBNB and vBEP20 contracts. This way we can use 1 ABI file, and one mapping for vBNB and vBEP20. - ## Getting started with querying Below are a few ways to show how to query the Venus V2 Subgraph for data. The queries show most of the information that is queryable, but there are many other filtering options that can be used, just check out the [querying api](https://github.com/graphprotocol/graph-node/blob/master/docs/graphql-api.md). diff --git a/package.json b/package.json index 0677faf9..a06137fa 100644 --- a/package.json +++ b/package.json @@ -30,7 +30,7 @@ "pretty": "prettier '**/*.ts' --write", "test": "yarn workspaces foreach run test", "test:integration": "yarn workspaces foreach run test:integration", - "postinstall": "patch-package && ./copy_contracts.sh" + "postinstall": "./copy_contracts.sh" }, "devDependencies": { "@graphprotocol/client-cli": "^3.0.0", @@ -51,7 +51,7 @@ "@venusprotocol/governance-contracts": "^1.0.0", "@venusprotocol/isolated-pools": "1.0.0", "@venusprotocol/oracle": "^1.4.1", - "@venusprotocol/venus-protocol": "^0.6.0", + "@venusprotocol/venus-protocol": "^3.0.0-dev.7", "assemblyscript": "0.19.23", "chai": "^4.3.6", "eslint": "^8.25.0", @@ -69,7 +69,6 @@ "matchstick-as": "^0.5.0", "module-alias": "^2.2.2", "mustache": "^4.2.0", - "patch-package": "6.5.1", "prettier": "^2.5.1", "prettier-airbnb-config": "^1.0.0", "solidity-coverage": "^0.7.21", diff --git a/patches/@venusprotocol+venus-protocol+0.6.0.patch b/patches/@venusprotocol+venus-protocol+0.6.0.patch deleted file mode 100644 index 33896302..00000000 --- a/patches/@venusprotocol+venus-protocol+0.6.0.patch +++ /dev/null @@ -1,111 +0,0 @@ -diff --git a/node_modules/@venusprotocol/venus-protocol/contracts/Comptroller/Comptroller.sol b/node_modules/@venusprotocol/venus-protocol/contracts/Comptroller/Comptroller.sol -index 918388f..233b6fc 100644 ---- a/node_modules/@venusprotocol/venus-protocol/contracts/Comptroller/Comptroller.sol -+++ b/node_modules/@venusprotocol/venus-protocol/contracts/Comptroller/Comptroller.sol -@@ -15,7 +15,7 @@ import "./Unitroller.sol"; - * @title Venus's Comptroller Contract - * @author Venus - */ --contract Comptroller is ComptrollerV10Storage, ComptrollerInterfaceG2, ComptrollerErrorReporter, ExponentialNoError { -+contract CorePoolComptroller is ComptrollerV10Storage, ComptrollerInterfaceG2, ComptrollerErrorReporter, ExponentialNoError { - /// @notice Emitted when an admin supports a market - event MarketListed(VToken vToken); - -diff --git a/node_modules/@venusprotocol/venus-protocol/contracts/Comptroller/ComptrollerInterface.sol b/node_modules/@venusprotocol/venus-protocol/contracts/Comptroller/ComptrollerInterface.sol -index 4c0ce6c..f18c3a5 100644 ---- a/node_modules/@venusprotocol/venus-protocol/contracts/Comptroller/ComptrollerInterface.sol -+++ b/node_modules/@venusprotocol/venus-protocol/contracts/Comptroller/ComptrollerInterface.sol -@@ -1,6 +1,6 @@ - pragma solidity ^0.5.16; - --import "../Tokens/VTokens/VToken.sol"; -+import { CorePoolVToken as VToken } from "../Tokens/VTokens/VToken.sol"; - import "../Oracle/PriceOracle.sol"; - - contract ComptrollerInterfaceG1 { -diff --git a/node_modules/@venusprotocol/venus-protocol/contracts/Lens/ComptrollerLens.sol b/node_modules/@venusprotocol/venus-protocol/contracts/Lens/ComptrollerLens.sol -index b5a296a..7b104ef 100644 ---- a/node_modules/@venusprotocol/venus-protocol/contracts/Lens/ComptrollerLens.sol -+++ b/node_modules/@venusprotocol/venus-protocol/contracts/Lens/ComptrollerLens.sol -@@ -6,7 +6,8 @@ import "../Tokens/VTokens/VToken.sol"; - import "../Tokens/EIP20Interface.sol"; - import "../Oracle/PriceOracle.sol"; - import "../Utils/ErrorReporter.sol"; --import "../Comptroller/Comptroller.sol"; -+import { ComptrollerLensInterface } from "../Comptroller/ComptrollerLensInterface.sol"; -+import { CorePoolComptroller as Comptroller, ComptrollerErrorReporter, ExponentialNoError } from "../Comptroller/Comptroller.sol"; - import "../Tokens/VAI/VAIControllerInterface.sol"; - - contract ComptrollerLens is ComptrollerLensInterface, ComptrollerErrorReporter, ExponentialNoError { -diff --git a/node_modules/@venusprotocol/venus-protocol/contracts/Lens/SnapshotLens.sol b/node_modules/@venusprotocol/venus-protocol/contracts/Lens/SnapshotLens.sol -index d44d94a..826ce00 100644 ---- a/node_modules/@venusprotocol/venus-protocol/contracts/Lens/SnapshotLens.sol -+++ b/node_modules/@venusprotocol/venus-protocol/contracts/Lens/SnapshotLens.sol -@@ -1,9 +1,9 @@ - pragma solidity ^0.5.16; - pragma experimental ABIEncoderV2; - --import "../Tokens/VTokens/VToken.sol"; -+import { CorePoolVToken as VToken } from "../Tokens/VTokens/VToken.sol"; - import "../Utils/SafeMath.sol"; --import "../Comptroller/Comptroller.sol"; -+import { CorePoolComptroller as Comptroller } from "../Comptroller/Comptroller.sol"; - import "../Tokens/EIP20Interface.sol"; - import "../Tokens/VTokens/VBep20.sol"; - -diff --git a/node_modules/@venusprotocol/venus-protocol/contracts/Tokens/VAI/VAIController.sol b/node_modules/@venusprotocol/venus-protocol/contracts/Tokens/VAI/VAIController.sol -index 2f472d8..9655f39 100644 ---- a/node_modules/@venusprotocol/venus-protocol/contracts/Tokens/VAI/VAIController.sol -+++ b/node_modules/@venusprotocol/venus-protocol/contracts/Tokens/VAI/VAIController.sol -@@ -4,9 +4,9 @@ import "../../Oracle/PriceOracle.sol"; - import "../../Utils/ErrorReporter.sol"; - import "../../Utils/Exponential.sol"; - import "../../Comptroller/ComptrollerStorage.sol"; --import "../../Comptroller/Comptroller.sol"; -+import { CorePoolComptroller as Comptroller } from "../../Comptroller/Comptroller.sol"; - import "../../Governance/IAccessControlManager.sol"; --import "../VTokens/VToken.sol"; -+import { CorePoolVToken as VToken } from "../VTokens/VToken.sol"; - import "./VAIControllerStorage.sol"; - import "./VAIUnitroller.sol"; - import "./VAI.sol"; -diff --git a/node_modules/@venusprotocol/venus-protocol/contracts/Tokens/VTokens/VToken.sol b/node_modules/@venusprotocol/venus-protocol/contracts/Tokens/VTokens/VToken.sol -index 3cc3e70..e7be892 100644 ---- a/node_modules/@venusprotocol/venus-protocol/contracts/Tokens/VTokens/VToken.sol -+++ b/node_modules/@venusprotocol/venus-protocol/contracts/Tokens/VTokens/VToken.sol -@@ -13,7 +13,7 @@ import "./VTokenInterfaces.sol"; - * @notice Abstract base for VTokens - * @author Venus - */ --contract VToken is VTokenInterface, Exponential, TokenErrorReporter { -+contract CorePoolVToken is VTokenInterface, Exponential, TokenErrorReporter { - /** - * @notice Initialize the money market - * @param comptroller_ The address of the Comptroller -diff --git a/node_modules/@venusprotocol/venus-protocol/contracts/test/ComptrollerHarness.sol b/node_modules/@venusprotocol/venus-protocol/contracts/test/ComptrollerHarness.sol -index f5495f4..362cb61 100644 ---- a/node_modules/@venusprotocol/venus-protocol/contracts/test/ComptrollerHarness.sol -+++ b/node_modules/@venusprotocol/venus-protocol/contracts/test/ComptrollerHarness.sol -@@ -1,6 +1,8 @@ - pragma solidity ^0.5.16; - --import "../Comptroller/Comptroller.sol"; -+import { UnitrollerAdminStorage } from "../Comptroller/ComptrollerStorage.sol"; -+import { Unitroller } from "../Comptroller/Unitroller.sol"; -+import { CorePoolComptroller as Comptroller } from "../Comptroller/Comptroller.sol"; - import "../Oracle/PriceOracle.sol"; - - contract ComptrollerKovan is Comptroller { -diff --git a/node_modules/@venusprotocol/venus-protocol/contracts/test/ComptrollerScenario.sol b/node_modules/@venusprotocol/venus-protocol/contracts/test/ComptrollerScenario.sol -index 8c247aa..1c6e3db 100644 ---- a/node_modules/@venusprotocol/venus-protocol/contracts/test/ComptrollerScenario.sol -+++ b/node_modules/@venusprotocol/venus-protocol/contracts/test/ComptrollerScenario.sol -@@ -1,6 +1,7 @@ - pragma solidity ^0.5.16; - --import "../Comptroller/Comptroller.sol"; -+import { CorePoolComptroller as Comptroller } from "../Comptroller/Comptroller.sol"; -+import { CorePoolVToken as VToken } from "../Tokens/VTokens/VToken.sol"; - - contract ComptrollerScenario is Comptroller { - uint public blockNumber; diff --git a/subgraphs/isolated-pools/schema.graphql b/subgraphs/isolated-pools/schema.graphql index abe70f5e..7793fabf 100644 --- a/subgraphs/isolated-pools/schema.graphql +++ b/subgraphs/isolated-pools/schema.graphql @@ -78,7 +78,7 @@ type Market @entity { "Borrow rate per block" borrowRateMantissa: BigInt! "The vToken contract balance of BEP20 or BNB" - cash: BigDecimal! + cashMantissa: BigInt! "Collateral factor determining how much one can borrow" collateralFactorMantissa: BigInt! "Exchange rate of tokens / vTokens" @@ -102,9 +102,9 @@ type Market @entity { "Max token borrow amount allowed" borrowCapMantissa: BigInt! "Total borrowed" - treasuryTotalBorrowsMantissa: BigInt! + totalBorrowsMantissa: BigInt! "Total supplied" - treasuryTotalSupplyMantissa: BigInt! + totalSupplyMantissa: BigInt! # Fields that are not in Venus api "Block the market is updated to" @@ -115,8 +115,8 @@ type Market @entity { borrowIndexMantissa: BigInt! "The factor determining interest that goes to reserves" reserveFactorMantissa: BigInt! - "Underlying token price in USD" - underlyingPriceUsd: BigDecimal! + "Underlying token price in USD cents" + underlyingPriceCents: BigInt! "Underlying token decimal length" underlyingDecimals: Int! "vToken decimal length" diff --git a/subgraphs/isolated-pools/src/operations/create.ts b/subgraphs/isolated-pools/src/operations/create.ts index 269b753f..567b770f 100644 --- a/subgraphs/isolated-pools/src/operations/create.ts +++ b/subgraphs/isolated-pools/src/operations/create.ts @@ -36,7 +36,7 @@ import { zeroBigInt32, } from '../constants'; import { poolLensAddress, poolRegistryAddress } from '../constants/addresses'; -import { getTokenPriceInUsd } from '../utilities'; +import { getTokenPriceInCents } from '../utilities'; import exponentToBigDecimal from '../utilities/exponentToBigDecimal'; import { getAccountVTokenId, @@ -100,21 +100,17 @@ export function createMarket( market.symbol = vTokenContract.symbol(); const underlyingDecimals = underlyingContract.decimals(); - const underlyingValue = getTokenPriceInUsd(comptroller, vTokenAddress, underlyingDecimals); + const underlyingValue = getTokenPriceInCents(comptroller, vTokenAddress, underlyingDecimals); market.underlyingAddress = underlyingAddress; market.underlyingName = underlyingContract.name(); market.underlyingSymbol = underlyingContract.symbol(); - market.underlyingPriceUsd = underlyingValue; + market.underlyingPriceCents = underlyingValue; market.underlyingDecimals = underlyingDecimals; market.vTokenDecimals = vTokenContract.decimals(); market.borrowRateMantissa = vTokenContract.borrowRatePerBlock(); - market.cash = vTokenContract - .getCash() - .toBigDecimal() - .div(exponentToBigDecimal(market.underlyingDecimals)) - .truncate(market.underlyingDecimals); + market.cashMantissa = vTokenContract.getCash(); market.exchangeRateMantissa = vTokenContract.exchangeRateStored(); @@ -129,8 +125,8 @@ export function createMarket( market.reserveFactorMantissa = vTokenContract.reserveFactorMantissa(); - market.treasuryTotalBorrowsMantissa = vTokenContract.totalBorrows(); - market.treasuryTotalSupplyMantissa = vTokenContract.totalSupply(); + market.totalBorrowsMantissa = vTokenContract.totalBorrows(); + market.totalSupplyMantissa = vTokenContract.totalSupply(); market.badDebtMantissa = vTokenContract.badDebt(); diff --git a/subgraphs/isolated-pools/src/operations/update.ts b/subgraphs/isolated-pools/src/operations/update.ts index 8ddae88a..d709809a 100644 --- a/subgraphs/isolated-pools/src/operations/update.ts +++ b/subgraphs/isolated-pools/src/operations/update.ts @@ -8,7 +8,7 @@ import { getExchangeRateBigDecimal, valueOrNotAvailableIntIfReverted, } from '../utilities'; -import { getTokenPriceInUsd } from '../utilities'; +import { getTokenPriceInCents } from '../utilities'; import { getOrCreateMarket } from './getOrCreate'; import { getOrCreateAccount, @@ -171,12 +171,12 @@ export const updateMarket = ( } const marketContract = VToken.bind(vTokenAddress); - const tokenPriceUsd = getTokenPriceInUsd( + const tokenPriceCents = getTokenPriceInCents( marketContract.comptroller(), vTokenAddress, market.underlyingDecimals, ); - market.underlyingPriceUsd = tokenPriceUsd.truncate(market.underlyingDecimals); + market.underlyingPriceCents = tokenPriceCents; market.accrualBlockNumber = valueOrNotAvailableIntIfReverted( marketContract.try_accrualBlockNumber(), @@ -192,10 +192,7 @@ export const updateMarket = ( market.reservesMantissa = valueOrNotAvailableIntIfReverted(marketContract.try_totalReserves()); const cashBigInt = valueOrNotAvailableIntIfReverted(marketContract.try_getCash()); - market.cash = cashBigInt - .toBigDecimal() - .div(exponentToBigDecimal(market.underlyingDecimals)) - .truncate(market.underlyingDecimals); + market.cashMantissa = cashBigInt; // calling supplyRatePerBlock & borrowRatePerBlock can fail due to external reasons, so we fall back to 0 in case of an error market.borrowRateMantissa = valueOrNotAvailableIntIfReverted( @@ -205,12 +202,8 @@ export const updateMarket = ( marketContract.try_supplyRatePerBlock(), ); - market.treasuryTotalBorrowsMantissa = valueOrNotAvailableIntIfReverted( - marketContract.try_totalBorrows(), - ); - market.treasuryTotalSupplyMantissa = valueOrNotAvailableIntIfReverted( - marketContract.try_totalSupply(), - ); + market.totalBorrowsMantissa = valueOrNotAvailableIntIfReverted(marketContract.try_totalBorrows()); + market.totalSupplyMantissa = valueOrNotAvailableIntIfReverted(marketContract.try_totalSupply()); market.save(); return market as Market; diff --git a/subgraphs/isolated-pools/src/utilities/exponentToBigInt.ts b/subgraphs/isolated-pools/src/utilities/exponentToBigInt.ts new file mode 100644 index 00000000..dd63f053 --- /dev/null +++ b/subgraphs/isolated-pools/src/utilities/exponentToBigInt.ts @@ -0,0 +1,11 @@ +import { BigInt } from '@graphprotocol/graph-ts'; + +function exponentToBigInt(decimals: i32): BigInt { + let bd = BigInt.fromString('1'); + for (let i = 0; i < decimals; i++) { + bd = bd.times(BigInt.fromString('10')); + } + return bd; +} + +export default exponentToBigInt; diff --git a/subgraphs/isolated-pools/src/utilities/getTokenPriceInUsd.ts b/subgraphs/isolated-pools/src/utilities/getTokenPriceInCents.ts similarity index 68% rename from subgraphs/isolated-pools/src/utilities/getTokenPriceInUsd.ts rename to subgraphs/isolated-pools/src/utilities/getTokenPriceInCents.ts index a402936c..67761bf1 100644 --- a/subgraphs/isolated-pools/src/utilities/getTokenPriceInUsd.ts +++ b/subgraphs/isolated-pools/src/utilities/getTokenPriceInCents.ts @@ -1,20 +1,20 @@ -import { Address, BigDecimal } from '@graphprotocol/graph-ts'; +import { Address, BigInt } from '@graphprotocol/graph-ts'; import { PriceOracle } from '../../generated/templates/VToken/PriceOracle'; -import { NOT_AVAILABLE_BIG_DECIMAL } from '../constants'; +import { NOT_AVAILABLE_BIG_INT } from '../constants'; import { getPool } from '../operations/get'; -import exponentToBigDecimal from '../utilities/exponentToBigDecimal'; +import exponentToBigInt from './exponentToBigInt'; import valueOrNotAvailableIntIfReverted from './valueOrNotAvailableIntIfReverted'; // Used for all vBEP20 contracts -const getTokenPrice = ( +const getTokenPriceInCents = ( poolAddress: Address, eventAddress: Address, underlyingDecimals: i32, -): BigDecimal => { +): BigInt => { const pool = getPool(poolAddress); // will return NOT_AVAILABLE if the price cannot be fetched - let underlyingPrice = NOT_AVAILABLE_BIG_DECIMAL; + let underlyingPrice = NOT_AVAILABLE_BIG_INT; if (pool && pool.priceOracleAddress) { const oracleAddress = Address.fromBytes(pool.priceOracleAddress); /* PriceOracle2 is used from starting of Comptroller. @@ -23,16 +23,16 @@ const getTokenPrice = ( * Note this returns the value without factoring in token decimals and wei, so we must divide * the number by (bnbDecimals - tokenDecimals) and again by the mantissa. */ - const mantissaDecimalFactor = exponentToBigDecimal(36 - underlyingDecimals); + const mantissaDecimalFactor = exponentToBigInt(36 - underlyingDecimals); const priceOracle = PriceOracle.bind(oracleAddress); const underlyingPriceBigInt = valueOrNotAvailableIntIfReverted( priceOracle.try_getUnderlyingPrice(eventAddress), ); - underlyingPrice = underlyingPriceBigInt.toBigDecimal().div(mantissaDecimalFactor); + underlyingPrice = underlyingPriceBigInt.div(mantissaDecimalFactor); } return underlyingPrice; }; -export default getTokenPrice; +export default getTokenPriceInCents; diff --git a/subgraphs/isolated-pools/src/utilities/index.ts b/subgraphs/isolated-pools/src/utilities/index.ts index 1689a0ff..2a14a150 100644 --- a/subgraphs/isolated-pools/src/utilities/index.ts +++ b/subgraphs/isolated-pools/src/utilities/index.ts @@ -1,4 +1,4 @@ export { default as getExchangeRateBigDecimal } from './getExchangeRateBigDecimal'; -export { default as getTokenPriceInUsd } from './getTokenPriceInUsd'; +export { default as getTokenPriceInCents } from './getTokenPriceInCents'; export { default as exponentToBigDecimal } from './exponentToBigDecimal'; 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 ff3b86e8..b77af785 100644 --- a/subgraphs/isolated-pools/tests/VToken/index.test.ts +++ b/subgraphs/isolated-pools/tests/VToken/index.test.ts @@ -477,12 +477,12 @@ describe('VToken', () => { assertMarketDocument('accrualBlockNumber', '999'); assertMarketDocument('blockTimestamp', accrueInterestEvent.block.timestamp.toString()); - assertMarketDocument('treasuryTotalSupplyMantissa', '36504567163409'); + assertMarketDocument('totalSupplyMantissa', '36504567163409'); assertMarketDocument('exchangeRateMantissa', '365045823500000000000000'); assertMarketDocument('borrowIndexMantissa', '300000000000000000000'); assertMarketDocument('reservesMantissa', '5128924555022289393'); - assertMarketDocument('treasuryTotalBorrowsMantissa', '2641234234636158123'); - assertMarketDocument('cash', '1.418171344423412457'); + assertMarketDocument('totalBorrowsMantissa', '2641234234636158123'); + assertMarketDocument('cashMantissa', '1418171344423412457'); assertMarketDocument('borrowRateMantissa', '12678493'); assertMarketDocument('supplyRateMantissa', '12678493'); }); diff --git a/subgraphs/isolated-pools/tests/integration/pool.ts b/subgraphs/isolated-pools/tests/integration/pool.ts index 660bf47a..68b6fd18 100644 --- a/subgraphs/isolated-pools/tests/integration/pool.ts +++ b/subgraphs/isolated-pools/tests/integration/pool.ts @@ -22,7 +22,7 @@ describe('Pools', function () { '0xe7f1725e7734ce288f8367e1bb143e90bb3f0512', ]; const underlyingSymbols = ['BSW', 'BNX']; - const underlyingPricesUSD = ['0.208', '159.99']; + const underlyingPricesCents = ['208', '15999']; const interestRateModelAddresses = [ '0xbf5a316f4303e13ae92c56d2d8c9f7629bef5c6e', '0xb955b6c65ff69bfe07a557aa385055282b8a5ea3', @@ -135,7 +135,7 @@ describe('Pools', function () { markets.forEach((m, idx) => { expect(m.pool.id).to.equal(pool.id); expect(m.borrowRateMantissa).to.equal('0'); - expect(m.cash).to.equal('1'); + expect(m.cashMantissa).to.equal('1'); expect(m.collateralFactorMantissa).to.equal('700000000000000000'); expect(m.exchangeRateMantissa).to.equal('10000000000000000000000000000'); expect(m.interestRateModelAddress).to.equal(interestRateModelAddresses[idx]); @@ -152,7 +152,7 @@ describe('Pools', function () { expect(m.blockTimestamp).to.not.be.equal('0'); expect(m.borrowIndexMantissa).to.equal('1000000000000000000'); expect(m.reserveFactorMantissa).to.equal('0'); - expect(m.underlyingPriceUsd).to.equal(underlyingPricesUSD[idx]); + expect(m.underlyingPriceCents).to.equal(underlyingPricesCents[idx]); expect(m.underlyingDecimals).to.equal(18); }); }); diff --git a/subgraphs/isolated-pools/tests/integration/queries/marketByIdQuery.graphql b/subgraphs/isolated-pools/tests/integration/queries/marketByIdQuery.graphql index 86e66884..7f7ebc53 100644 --- a/subgraphs/isolated-pools/tests/integration/queries/marketByIdQuery.graphql +++ b/subgraphs/isolated-pools/tests/integration/queries/marketByIdQuery.graphql @@ -6,7 +6,7 @@ query MarketById($id: ID!) { } badDebtMantissa borrowRateMantissa - cash + cashMantissa collateralFactorMantissa exchangeRateMantissa interestRateModelAddress @@ -16,7 +16,7 @@ query MarketById($id: ID!) { symbol underlyingAddress underlyingName - underlyingPriceUsd + underlyingPriceCents underlyingSymbol borrowCapMantissa supplyCapMantissa @@ -24,7 +24,6 @@ query MarketById($id: ID!) { blockTimestamp borrowIndexMantissa reserveFactorMantissa - underlyingPriceUsd underlyingDecimals supplierCount borrowerCount diff --git a/subgraphs/isolated-pools/tests/integration/queries/marketsQuery.graphql b/subgraphs/isolated-pools/tests/integration/queries/marketsQuery.graphql index 273cfebb..f9436083 100644 --- a/subgraphs/isolated-pools/tests/integration/queries/marketsQuery.graphql +++ b/subgraphs/isolated-pools/tests/integration/queries/marketsQuery.graphql @@ -6,7 +6,7 @@ query Markets { } badDebtMantissa borrowRateMantissa - cash + cashMantissa collateralFactorMantissa exchangeRateMantissa interestRateModelAddress @@ -22,7 +22,7 @@ query Markets { blockTimestamp borrowIndexMantissa reserveFactorMantissa - underlyingPriceUsd + underlyingPriceCents underlyingDecimals supplyCapMantissa accessControlManagerAddress diff --git a/subgraphs/venus/README.md b/subgraphs/venus/README.md index b116475e..265751cb 100644 --- a/subgraphs/venus/README.md +++ b/subgraphs/venus/README.md @@ -8,10 +8,6 @@ This subgraph can be found on The Graph Hosted Service at https://thegraph.com/e You can also run this subgraph locally, if you wish. Instructions for that can be found in [The Graph Documentation](https://thegraph.com/docs/en/developer/quick-start/). -### ABI - -The ABI used is `vtoken.json`. It is a stripped down version of the full abi provided by Venus, that satisfies the calls we need to make for both vBNB and vBEP20 contracts. This way we can use 1 ABI file, and one mapping for vBNB and vBEP20. - ## Getting started with querying Below are a few ways to show how to query the Venus V2 Subgraph for data. The queries show most of the information that is queryable, but there are many other filtering options that can be used, just check out the [querying api](https://github.com/graphprotocol/graph-node/blob/master/docs/graphql-api.md). diff --git a/subgraphs/venus/package.json b/subgraphs/venus/package.json index 942ed3c8..34a45bbb 100644 --- a/subgraphs/venus/package.json +++ b/subgraphs/venus/package.json @@ -11,12 +11,12 @@ ], "scripts": { "codegen": "graph codegen", - "create:local": "graph create venusprotocol/venus-subgraph --node http://127.0.0.1:8020", - "build:local": "graph build --ipfs http://localhost:5001", - "build:mainnet": "graph build --ipfs https://api.thegraph.com/ipfs/ ", - "deploy:local": "npx mustache config/local.json template.yaml > subgraph.yaml && graph deploy venusprotocol/venus-subgraph --debug --ipfs http://localhost:5001 --node http://127.0.0.1:8020/", - "deploy:testnet": "npx mustache config/testnet.json template.yaml > subgraph.yaml && graph deploy venusprotocol/venus-subgraph-chapel --debug --ipfs https://api.thegraph.com/ipfs/ --node https://api.thegraph.com/deploy/", - "deploy:mainnet": "npx mustache config/mainnet.json template.yaml > subgraph.yaml && graph deploy venusprotocol/venus-subgraph --debug --ipfs https://api.thegraph.com/ipfs/ --node https://api.thegraph.com/deploy/", + "create:local": "LOCAL=true npx graph create venusprotocol/venus-subgraph --node http://127.0.0.1:8020/", + "build:local": "LOCAL=true npx graph build --ipfs http://127.0.0.1:5001", + "build:mainnet": "npx graph build --ipfs https://api.thegraph.com/ipfs/ ", + "deploy:local": "LOCAL=true npx mustache config/local.json template.yaml > subgraph.yaml && npx graph deploy venusprotocol/venus-isolated-pools --debug --ipfs http://127.0.0.1:5001 --node http://127.0.0.1:8020/", + "deploy:testnet": "npx mustache config/testnet.json template.yaml > subgraph.yaml && npx graph deploy venusprotocol/venus-isolated-pools-chapel --debug --ipfs https://api.thegraph.com/ipfs/ --node https://api.thegraph.com/deploy/", + "deploy:mainnet": "npx mustache config/mainnet.json template.yaml > subgraph.yaml && npx graph deploy venusprotocol/venus-isolated-pools --debug --ipfs https://api.thegraph.com/ipfs/ --node https://api.thegraph.com/deploy/", "prepare:local": "npx mustache config/local.json template.yaml > subgraph.yaml", "prepare:testnet": "npx mustache config/testnet.json template.yaml > subgraph.yaml", "prepare:mainnet": "npx mustache config/mainnet.json template.yaml > subgraph.yaml", diff --git a/subgraphs/venus/schema.graphql b/subgraphs/venus/schema.graphql index da274d04..ca95039c 100644 --- a/subgraphs/venus/schema.graphql +++ b/subgraphs/venus/schema.graphql @@ -20,35 +20,33 @@ Market stores all high level variables for a vToken market type Market @entity { #Fields that match Venus API "Borrow rate per block" - borrowRate: BigDecimal! + borrowRateMantissa: BigInt! "The vToken contract balance of BEP20 or BNB" - cash: BigDecimal! + cashMantissa: BigInt! "Collateral factor determining how much one can borrow" collateralFactor: BigDecimal! "Exchange rate of tokens / vTokens" - exchangeRate: BigDecimal! + exchangeRateMantissa: BigInt! "Address of the interest rate model" interestRateModelAddress: Bytes! "Name of the vToken" name: String! "Reserves stored in the contract" - reservesWei: BigInt! + reservesMantissa: BigInt! "Supply rate per block" - supplyRate: BigDecimal! + supplyRateMantissa: BigInt! "VToken symbol" symbol: String! "VToken address" id: ID! "Borrows in the market" - totalBorrows: BigDecimal! + totalBorrowsMantissa: BigInt! "VToken supply. VTokens have 8 decimals" - totalSupply: BigDecimal! + totalSupplyMantissa: BigInt! "Underlying token address" underlyingAddress: Bytes! "Underlying token name" underlyingName: String! - "Underlying price of token in BNB (ex. 0.007 DAI)" - underlyingPrice: BigDecimal! "Underlying token symbol" underlyingSymbol: String! @@ -58,15 +56,17 @@ type Market @entity { "Timestamp the market was most recently updated" blockTimestamp: Int! "The history of the markets borrow index return (Think S&P 500)" - borrowIndex: BigDecimal! + borrowIndexMantissa: BigInt! "The factor determining interest that goes to reserves" reserveFactor: BigInt! - "Underlying token price in USD" - underlyingPriceUSD: BigDecimal! + "Underlying token price in USD cents" + underlyingPriceCents: BigInt! "Underlying token decimal length" underlyingDecimals: Int! "Total XVS Distributed for this market" totalXvsDistributedMantissa: BigInt! + "vToken decimal length" + vTokenDecimals: Int! } """ @@ -120,11 +120,11 @@ type AccountVToken @entity { "VToken balance of the user" vTokenBalance: BigDecimal! "Total amount of underlying supplied" - totalUnderlyingSupplied: BigDecimal! + totalUnderlyingSuppliedMantissa: BigInt! "Total amount of underling redeemed" - totalUnderlyingRedeemed: BigDecimal! + totalUnderlyingRedeemedMantissa: BigInt! "The value of the borrow index upon users last interaction" - accountBorrowIndex: BigDecimal! + accountBorrowIndexMantissa: BigInt! "Total amount underlying borrowed, exclusive of interest" totalUnderlyingBorrowed: BigDecimal! "Total amount underlying repaid" diff --git a/subgraphs/venus/src/constants/index.ts b/subgraphs/venus/src/constants/index.ts index 1672fa9f..ccad98aa 100644 --- a/subgraphs/venus/src/constants/index.ts +++ b/subgraphs/venus/src/constants/index.ts @@ -1,3 +1,31 @@ -import { BigInt } from '@graphprotocol/graph-ts'; +import { BigDecimal, BigInt } from '@graphprotocol/graph-ts'; +import { exponentToBigDecimal } from '../utilities'; +export const BORROW = 'BORROW'; +export const MINT = 'MINT'; +export const REDEEM = 'REDEEM'; +export const REPAY = 'REPAY'; +export const SEIZE = 'SEIZE'; +export const LIQUIDATE = 'LIQUIDATE'; +export const TRANSFER = 'TRANSFER'; +export const ENTER_MARKET = 'ENTER_MARKET'; +export const EXIT_MARKET = 'EXIT_MARKET'; + +export const mantissaFactor = 18; +export const mantissaFactorBigDecimal: BigDecimal = exponentToBigDecimal(mantissaFactor); + +export const zeroBigDecimal = BigDecimal.fromString('0'); export const zeroBigInt32 = BigInt.fromString('0'); +export const oneBigInt = BigInt.fromString('1'); + +export const Actions = [ + MINT, + REDEEM, + BORROW, + REPAY, + SEIZE, + LIQUIDATE, + TRANSFER, + ENTER_MARKET, + EXIT_MARKET, +]; diff --git a/subgraphs/venus/src/mappings/comptroller.ts b/subgraphs/venus/src/mappings/comptroller.ts index 574c6c30..ae9e5633 100644 --- a/subgraphs/venus/src/mappings/comptroller.ts +++ b/subgraphs/venus/src/mappings/comptroller.ts @@ -18,7 +18,7 @@ import { createAccount, createMarket } from '../operations/create'; import { getOrCreateComptroller } from '../operations/getOrCreate'; import { updateCommonVTokenStats } from '../operations/update'; import { ensureComptrollerSynced } from '../utilities'; -import { mantissaFactorBD } from '../utilities/exponentToBigDecimal'; +import { mantissaFactorBigDecimal } from '../constants'; export const handleMarketListed = (event: MarketListed): void => { // Dynamically index all new listed tokens @@ -112,7 +112,7 @@ export const handleNewCollateralFactor = (event: NewCollateralFactor): void => { if (market != null) { market.collateralFactor = event.params.newCollateralFactorMantissa .toBigDecimal() - .div(mantissaFactorBD); + .div(mantissaFactorBigDecimal); market.save(); } }; diff --git a/subgraphs/venus/src/mappings/vtoken.ts b/subgraphs/venus/src/mappings/vToken.ts similarity index 92% rename from subgraphs/venus/src/mappings/vtoken.ts rename to subgraphs/venus/src/mappings/vToken.ts index c2c5ec34..35b3a129 100644 --- a/subgraphs/venus/src/mappings/vtoken.ts +++ b/subgraphs/venus/src/mappings/vToken.ts @@ -26,11 +26,7 @@ import { createAccount } from '../operations/create'; import { createMarket } from '../operations/create'; import { updateCommonVTokenStats } from '../operations/update'; import { updateMarket } from '../operations/update'; -import { - exponentToBigDecimal, - vTokenDecimals, - vTokenDecimalsBD, -} from '../utilities/exponentToBigDecimal'; +import { exponentToBigDecimal } from '../utilities/exponentToBigDecimal'; /* Account supplies assets into market and receives vTokens in exchange * @@ -55,9 +51,11 @@ export const handleMint = (event: Mint): void => { .concat('-') .concat(event.transactionLogIndex.toString()); + const vTokenDecimals = market.vTokenDecimals; + let vTokenAmount = event.params.mintTokens .toBigDecimal() - .div(vTokenDecimalsBD) + .div(exponentToBigDecimal(vTokenDecimals)) .truncate(vTokenDecimals); let underlyingAmount = event.params.mintAmount .toBigDecimal() @@ -92,6 +90,7 @@ export const handleRedeem = (event: Redeem): void => { if (!market) { market = createMarket(event.address.toHexString()); } + let vTokenDecimals = market.vTokenDecimals; let redeemID = event.transaction.hash .toHexString() .concat('-') @@ -99,7 +98,7 @@ export const handleRedeem = (event: Redeem): void => { let vTokenAmount = event.params.redeemTokens .toBigDecimal() - .div(vTokenDecimalsBD) + .div(exponentToBigDecimal(vTokenDecimals)) .truncate(vTokenDecimals); let underlyingAmount = event.params.redeemAmount .toBigDecimal() @@ -160,7 +159,7 @@ export const handleBorrow = (event: Borrow): void => { .div(exponentToBigDecimal(market.underlyingDecimals)) .truncate(market.underlyingDecimals); - vTokenStats.accountBorrowIndex = market.borrowIndex; + vTokenStats.accountBorrowIndexMantissa = market.borrowIndexMantissa; vTokenStats.totalUnderlyingBorrowed = vTokenStats.totalUnderlyingBorrowed.plus(borrowAmountBD); vTokenStats.save(); @@ -235,7 +234,7 @@ export const handleRepayBorrow = (event: RepayBorrow): void => { .div(exponentToBigDecimal(market.underlyingDecimals)) .truncate(market.underlyingDecimals); - vTokenStats.accountBorrowIndex = market.borrowIndex; + vTokenStats.accountBorrowIndexMantissa = market.borrowIndexMantissa; vTokenStats.totalUnderlyingRepaid = vTokenStats.totalUnderlyingRepaid.plus(repayAmountBD); vTokenStats.save(); @@ -314,10 +313,10 @@ export const handleLiquidateBorrow = (event: LiquidateBorrow): void => { .toHexString() .concat('-') .concat(event.transactionLogIndex.toString()); - + let vTokenDecimals = marketRepayToken.vTokenDecimals; let vTokenAmount = event.params.seizeTokens .toBigDecimal() - .div(vTokenDecimalsBD) + .div(exponentToBigDecimal(vTokenDecimals)) .truncate(vTokenDecimals); let underlyingRepayAmount = event.params.repayAmount .toBigDecimal() @@ -362,11 +361,8 @@ 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 amountUnderlying = market.exchangeRate.times( - event.params.amount.toBigDecimal().div(vTokenDecimalsBD), - ); - let amountUnderylingTruncated = amountUnderlying.truncate(market.underlyingDecimals); + let vTokenDecimals = market.vTokenDecimals; + let amountUnderlying = market.exchangeRateMantissa.times(event.params.amount); // 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 @@ -390,11 +386,14 @@ export const handleTransfer = (event: Transfer): void => { ); vTokenStatsFrom.vTokenBalance = vTokenStatsFrom.vTokenBalance.minus( - event.params.amount.toBigDecimal().div(vTokenDecimalsBD).truncate(vTokenDecimals), + event.params.amount + .toBigDecimal() + .div(exponentToBigDecimal(vTokenDecimals)) + .truncate(vTokenDecimals), ); - vTokenStatsFrom.totalUnderlyingRedeemed = - vTokenStatsFrom.totalUnderlyingRedeemed.plus(amountUnderylingTruncated); + vTokenStatsFrom.totalUnderlyingRedeemedMantissa = + vTokenStatsFrom.totalUnderlyingRedeemedMantissa.plus(amountUnderlying); vTokenStatsFrom.save(); } @@ -422,11 +421,14 @@ export const handleTransfer = (event: Transfer): void => { ); vTokenStatsTo.vTokenBalance = vTokenStatsTo.vTokenBalance.plus( - event.params.amount.toBigDecimal().div(vTokenDecimalsBD).truncate(vTokenDecimals), + event.params.amount + .toBigDecimal() + .div(exponentToBigDecimal(vTokenDecimals)) + .truncate(vTokenDecimals), ); - vTokenStatsTo.totalUnderlyingSupplied = - vTokenStatsTo.totalUnderlyingSupplied.plus(amountUnderylingTruncated); + vTokenStatsTo.totalUnderlyingSuppliedMantissa = + vTokenStatsTo.totalUnderlyingSuppliedMantissa.plus(amountUnderlying); vTokenStatsTo.save(); } @@ -436,7 +438,7 @@ export const handleTransfer = (event: Transfer): void => { .concat(event.transactionLogIndex.toString()); let transfer = new TransferEvent(transferID); - transfer.amount = event.params.amount.toBigDecimal().div(vTokenDecimalsBD); + transfer.amount = event.params.amount.toBigDecimal().div(exponentToBigDecimal(vTokenDecimals)); transfer.to = event.params.to; transfer.from = event.params.from; transfer.blockNumber = event.block.number.toI32(); diff --git a/subgraphs/venus/src/operations/create.ts b/subgraphs/venus/src/operations/create.ts index ee282e28..f975b5be 100644 --- a/subgraphs/venus/src/operations/create.ts +++ b/subgraphs/venus/src/operations/create.ts @@ -2,51 +2,52 @@ import { Address, BigDecimal, BigInt, log } from '@graphprotocol/graph-ts'; import { Account, AccountVToken, Market } 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 { zeroBigInt32 } from '../constants'; +import { zeroBigDecimal, zeroBigInt32 } from '../constants'; import { nullAddress, vBnbAddress } from '../constants/addresses'; -import { zeroBD } from '../utilities/exponentToBigDecimal'; import { getUnderlyingPrice } from '../utilities/getUnderlyingPrice'; -export const createAccountVToken = ( - vTokenStatsID: string, +export function createAccountVToken( + accountVTokenId: string, symbol: string, account: string, - marketID: string, -): AccountVToken => { - const vTokenStats = new AccountVToken(vTokenStatsID); - vTokenStats.symbol = symbol; - vTokenStats.market = marketID; - vTokenStats.account = account; - vTokenStats.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)); - vTokenStats.vTokenBalance = new BigDecimal(vTokenContract.balanceOf(Address.fromString(account))); - // log.debug('[createAccountVToken] vTokenBalance: {}, account: {}, vToken: {}', [vTokenStats.vTokenBalance.toString(), account, marketID]); + marketId: string, +): AccountVToken { + const accountVToken = new AccountVToken(accountVTokenId); + accountVToken.symbol = symbol; + accountVToken.market = marketId; + accountVToken.account = account; + 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)), + ); - vTokenStats.totalUnderlyingSupplied = zeroBD; - vTokenStats.totalUnderlyingRedeemed = zeroBD; - vTokenStats.accountBorrowIndex = zeroBD; - vTokenStats.totalUnderlyingBorrowed = zeroBD; - vTokenStats.totalUnderlyingRepaid = zeroBD; - vTokenStats.storedBorrowBalance = zeroBD; - vTokenStats.enteredMarket = false; - return vTokenStats; -}; + accountVToken.totalUnderlyingSuppliedMantissa = zeroBigInt32; + accountVToken.totalUnderlyingRedeemedMantissa = zeroBigInt32; + accountVToken.accountBorrowIndexMantissa = zeroBigInt32; + accountVToken.totalUnderlyingBorrowed = zeroBigDecimal; + accountVToken.totalUnderlyingRepaid = zeroBigDecimal; + accountVToken.storedBorrowBalance = zeroBigDecimal; + accountVToken.enteredMarket = false; + return accountVToken; +} -export const createAccount = (accountID: string): Account => { - const account = new Account(accountID); +export function createAccount(accountId: string): Account { + const account = new Account(accountId); account.countLiquidated = 0; account.countLiquidator = 0; account.hasBorrowed = false; account.save(); return account; -}; +} -export const createMarket = (marketAddress: string): Market => { +export function createMarket(marketAddress: string): Market { let market: Market; const contract = VToken.bind(Address.fromString(marketAddress)); + const marketBep20Storage = VBep20Storage.bind(Address.fromString(marketAddress)); log.debug('[createMarket] market address: {}', [marketAddress]); @@ -55,14 +56,13 @@ export const createMarket = (marketAddress: string): Market => { market = new Market(marketAddress); market.underlyingAddress = nullAddress; market.underlyingDecimals = 18; - market.underlyingPrice = BigDecimal.fromString('1'); market.underlyingName = 'Binance Coin'; market.underlyingSymbol = 'BNB'; - market.underlyingPriceUSD = zeroBD; + market.underlyingPriceCents = zeroBigInt32; // It is all other VBEP20 contracts } else { market = new Market(marketAddress); - market.underlyingAddress = contract.underlying(); + market.underlyingAddress = marketBep20Storage.underlying(); log.debug('[createMarket] market underlying address: {}', [ market.underlyingAddress.toHexString(), ]); @@ -71,33 +71,34 @@ export const createMarket = (marketAddress: string): Market => { market.underlyingName = underlyingContract.name(); market.underlyingSymbol = underlyingContract.symbol(); - const underlyingValue = getUnderlyingPrice(market.id, market.underlyingDecimals); - market.underlyingPrice = underlyingValue.underlyingPrice; - market.underlyingPriceUSD = underlyingValue.underlyingPriceUsd; + const underlyingPriceCents = getUnderlyingPrice(market.id, market.underlyingDecimals); + market.underlyingPriceCents = underlyingPriceCents; } + market.vTokenDecimals = contract.decimals(); + const interestRateModelAddress = contract.try_interestRateModel(); const reserveFactor = contract.try_reserveFactorMantissa(); - market.borrowRate = zeroBD; - market.cash = zeroBD; - market.collateralFactor = zeroBD; - market.exchangeRate = zeroBD; + market.borrowRateMantissa = zeroBigInt32; + market.cashMantissa = zeroBigInt32; + market.collateralFactor = zeroBigDecimal; + market.exchangeRateMantissa = zeroBigInt32; market.interestRateModelAddress = interestRateModelAddress.reverted ? nullAddress : interestRateModelAddress.value; market.name = contract.name(); - market.reservesWei = BigInt.fromI32(0); - market.supplyRate = zeroBD; + market.reservesMantissa = BigInt.fromI32(0); + market.supplyRateMantissa = zeroBigInt32; market.symbol = contract.symbol(); - market.totalBorrows = zeroBD; - market.totalSupply = zeroBD; + market.totalBorrowsMantissa = zeroBigInt32; + market.totalSupplyMantissa = zeroBigInt32; market.accrualBlockNumber = 0; market.blockTimestamp = 0; - market.borrowIndex = zeroBD; + market.borrowIndexMantissa = zeroBigInt32; market.reserveFactor = reserveFactor.reverted ? BigInt.fromI32(0) : reserveFactor.value; market.totalXvsDistributedMantissa = zeroBigInt32; return market; -}; +} diff --git a/subgraphs/venus/src/operations/get.ts b/subgraphs/venus/src/operations/get.ts new file mode 100644 index 00000000..7b64ce5b --- /dev/null +++ b/subgraphs/venus/src/operations/get.ts @@ -0,0 +1,13 @@ +import { Address, log } from '@graphprotocol/graph-ts'; + +import { Market } from '../../generated/schema'; +import { getMarketId } from '../utilities/ids' + +export const getMarket = (vTokenAddress: Address): Market | null => { + const id = getMarketId(vTokenAddress); + const market = Market.load(id); + if (!market) { + log.error('Market {} not found', [id]); + } + return market; +}; diff --git a/subgraphs/venus/src/operations/update.ts b/subgraphs/venus/src/operations/update.ts index 5a65edb5..59047fcf 100644 --- a/subgraphs/venus/src/operations/update.ts +++ b/subgraphs/venus/src/operations/update.ts @@ -2,35 +2,29 @@ import { Address, BigInt, Bytes, log } from '@graphprotocol/graph-ts'; import { AccountVToken, Market } from '../../generated/schema'; import { VToken } from '../../generated/templates/VToken/VToken'; +import { zeroBigInt32 } from '../constants'; import { createMarket } from '../operations/create'; -import { - exponentToBigDecimal, - mantissaFactor, - mantissaFactorBD, - vTokenDecimalsBD, - zeroBD, -} from '../utilities/exponentToBigDecimal'; import { getUnderlyingPrice } from '../utilities/getUnderlyingPrice'; import { createAccountVToken } from './create'; import { getOrCreateAccountVTokenTransaction } from './getOrCreate'; export const updateCommonVTokenStats = ( - marketID: string, + marketId: string, marketSymbol: string, - accountID: string, + accountId: string, txHash: Bytes, timestamp: BigInt, blockNumber: BigInt, logIndex: BigInt, ): AccountVToken => { - const vTokenStatsID = marketID.concat('-').concat(accountID); - let vTokenStats = AccountVToken.load(vTokenStatsID); - if (vTokenStats == null) { - vTokenStats = createAccountVToken(vTokenStatsID, marketSymbol, accountID, marketID); + const accountVTokenId = marketId.concat('-').concat(accountId); + let accountVToken = AccountVToken.load(accountVTokenId); + if (accountVToken == null) { + accountVToken = createAccountVToken(accountVTokenId, marketSymbol, accountId, marketId); } - getOrCreateAccountVTokenTransaction(vTokenStatsID, txHash, timestamp, blockNumber, logIndex); - vTokenStats.accrualBlockNumber = blockNumber; - return vTokenStats as AccountVToken; + getOrCreateAccountVTokenTransaction(accountVTokenId, txHash, timestamp, blockNumber, logIndex); + accountVToken.accrualBlockNumber = blockNumber; + return accountVToken as AccountVToken; }; export const updateMarket = ( @@ -38,26 +32,23 @@ export const updateMarket = ( blockNumber: i32, blockTimestamp: i32, ): Market => { - const marketID = marketAddress.toHexString(); - let market = Market.load(marketID); + const marketId = marketAddress.toHexString(); + let market = Market.load(marketId) as Market; if (market == null) { - log.debug('[updateMarket] market null: {}, creating...', [marketAddress.toHexString()]); - market = createMarket(marketID); + market = createMarket(marketId); } // Only updateMarket if it has not been updated this block if (market.accrualBlockNumber != blockNumber) { const contractAddress = Address.fromString(market.id); const contract = VToken.bind(contractAddress); - market.accrualBlockNumber = contract.accrualBlockNumber().toI32(); market.blockTimestamp = blockTimestamp; - const underlyingValue = getUnderlyingPrice(market.id, market.underlyingDecimals); - market.underlyingPrice = underlyingValue.underlyingPrice; - market.underlyingPriceUSD = underlyingValue.underlyingPriceUsd; + const underlyingPriceCents = getUnderlyingPrice(market.id, market.underlyingDecimals); + market.underlyingPriceCents = underlyingPriceCents; - market.totalSupply = contract.totalSupply().toBigDecimal().div(vTokenDecimalsBD); + market.totalSupplyMantissa = contract.totalSupply(); /* Exchange rate explanation In Practice @@ -72,43 +63,24 @@ export const updateMarket = ( const exchangeRateStored = contract.try_exchangeRateStored(); if (exchangeRateStored.reverted) { log.error('***CALL FAILED*** : vBEP20 supplyRatePerBlock() reverted', []); - market.exchangeRate = zeroBD; + market.exchangeRateMantissa = zeroBigInt32; } else { - market.exchangeRate = exchangeRateStored.value - .toBigDecimal() - .div(exponentToBigDecimal(market.underlyingDecimals)) - .times(vTokenDecimalsBD) - .div(mantissaFactorBD) - .truncate(mantissaFactor); + market.exchangeRateMantissa = exchangeRateStored.value; } - market.borrowIndex = contract - .borrowIndex() - .toBigDecimal() - .div(mantissaFactorBD) - .truncate(mantissaFactor); + market.borrowIndexMantissa = contract.borrowIndex(); + + market.reservesMantissa = contract.totalReserves(); + market.totalBorrowsMantissa = contract.totalBorrows(); - market.reservesWei = contract.totalReserves(); - market.totalBorrows = contract - .totalBorrows() - .toBigDecimal() - .div(exponentToBigDecimal(market.underlyingDecimals)) - .truncate(market.underlyingDecimals); - market.cash = contract - .getCash() - .toBigDecimal() - .div(exponentToBigDecimal(market.underlyingDecimals)) - .truncate(market.underlyingDecimals); + market.cashMantissa = contract.getCash(); // Must convert to BigDecimal, and remove 10^18 that is used for Exp in Venus Solidity const borrowRatePerBlock = contract.try_borrowRatePerBlock(); if (borrowRatePerBlock.reverted) { log.error('***CALL FAILED*** : vBEP20 supplyRatePerBlock() reverted', []); - market.exchangeRate = zeroBD; + market.exchangeRateMantissa = zeroBigInt32; } else { - market.borrowRate = borrowRatePerBlock.value - .toBigDecimal() - .div(mantissaFactorBD) - .truncate(mantissaFactor); + market.borrowRateMantissa = borrowRatePerBlock.value; } // This fails on only the first call to cZRX. It is unclear why, but otherwise it works. @@ -116,12 +88,9 @@ export const updateMarket = ( const supplyRatePerBlock = contract.try_supplyRatePerBlock(); if (supplyRatePerBlock.reverted) { log.info('***CALL FAILED*** : vBEP20 supplyRatePerBlock() reverted', []); - market.supplyRate = zeroBD; + market.supplyRateMantissa = zeroBigInt32; } else { - market.supplyRate = supplyRatePerBlock.value - .toBigDecimal() - .div(mantissaFactorBD) - .truncate(mantissaFactor); + market.supplyRateMantissa = supplyRatePerBlock.value; } market.save(); } diff --git a/subgraphs/venus/src/utilities/exponentToBigDecimal.ts b/subgraphs/venus/src/utilities/exponentToBigDecimal.ts index b71f7ef2..bd0b695f 100644 --- a/subgraphs/venus/src/utilities/exponentToBigDecimal.ts +++ b/subgraphs/venus/src/utilities/exponentToBigDecimal.ts @@ -9,9 +9,3 @@ export function exponentToBigDecimal(decimals: i32): BigDecimal { } return bd; } - -export let mantissaFactor = 18; -export let vTokenDecimals = 8; -export let mantissaFactorBD: BigDecimal = exponentToBigDecimal(mantissaFactor); -export let vTokenDecimalsBD: BigDecimal = exponentToBigDecimal(vTokenDecimals); -export let zeroBD = BigDecimal.fromString('0'); diff --git a/subgraphs/venus/src/utilities/exponentToBigInt.ts b/subgraphs/venus/src/utilities/exponentToBigInt.ts new file mode 100644 index 00000000..5f8c8000 --- /dev/null +++ b/subgraphs/venus/src/utilities/exponentToBigInt.ts @@ -0,0 +1,11 @@ +/* eslint-disable prefer-const */ +import { BigInt } from '@graphprotocol/graph-ts'; + +// Writting this as an arrow function, produces a type signature compile error +export function exponentToBigInt(decimals: i32): BigInt { + let bd = BigInt.fromI32(1); + for (let i = 0; i < decimals; i++) { + bd = bd.times(BigInt.fromI32(10)); + } + return bd; +} diff --git a/subgraphs/venus/src/utilities/getBnbPriceInUsd.ts b/subgraphs/venus/src/utilities/getBnbPriceInUsd.ts index 3cb70d48..d9d51242 100644 --- a/subgraphs/venus/src/utilities/getBnbPriceInUsd.ts +++ b/subgraphs/venus/src/utilities/getBnbPriceInUsd.ts @@ -1,15 +1,18 @@ import { Address, BigDecimal } from '@graphprotocol/graph-ts'; import { PriceOracle } from '../../generated/templates/VToken/PriceOracle'; +import { mantissaFactorBigDecimal } from '../constants'; import { vBnbAddress } from '../constants/addresses'; import { getOrCreateComptroller } from '../operations/getOrCreate'; -import { mantissaFactorBD } from './exponentToBigDecimal'; export function getBnbPriceInUsd(): BigDecimal { const comptroller = getOrCreateComptroller(); const oracleAddress = Address.fromBytes(comptroller.priceOracle); const oracle = PriceOracle.bind(oracleAddress); - const bnbPriceInUSD = oracle.getUnderlyingPrice(vBnbAddress).toBigDecimal().div(mantissaFactorBD); + const bnbPriceInUSD = oracle + .getUnderlyingPrice(vBnbAddress) + .toBigDecimal() + .div(mantissaFactorBigDecimal); return bnbPriceInUSD; } diff --git a/subgraphs/venus/src/utilities/getTokenPrice.ts b/subgraphs/venus/src/utilities/getTokenPriceCents.ts similarity index 64% rename from subgraphs/venus/src/utilities/getTokenPrice.ts rename to subgraphs/venus/src/utilities/getTokenPriceCents.ts index 339b4feb..e8161adb 100644 --- a/subgraphs/venus/src/utilities/getTokenPrice.ts +++ b/subgraphs/venus/src/utilities/getTokenPriceCents.ts @@ -1,15 +1,15 @@ -import { Address, BigDecimal, log } from '@graphprotocol/graph-ts'; +import { Address, BigInt, log } from '@graphprotocol/graph-ts'; import { PriceOracle } from '../../generated/templates/VToken/PriceOracle'; import { getOrCreateComptroller } from '../operations/getOrCreate'; -import { exponentToBigDecimal } from './exponentToBigDecimal'; +import { exponentToBigInt } from './exponentToBigInt'; // Used for all vBEP20 contracts -export function getTokenPrice(eventAddress: Address, underlyingDecimals: i32): BigDecimal { +export function getTokenPriceCents(eventAddress: Address, underlyingDecimals: i32): BigInt { const comptroller = getOrCreateComptroller(); if (!comptroller.priceOracle) { log.debug('[getTokenPrice] empty price oracle: {}', ['0']); - return BigDecimal.zero(); + return BigInt.zero(); } const oracleAddress = Address.fromBytes(comptroller.priceOracle); @@ -19,14 +19,14 @@ export function getTokenPrice(eventAddress: Address, underlyingDecimals: i32): B * Note this returns the value without factoring in token decimals and wei, so we must divide * the number by (bnbDecimals - tokenDecimals) and again by the mantissa. */ - const mantissaDecimalFactor = 18 - underlyingDecimals + 18; - const bdFactor = exponentToBigDecimal(mantissaDecimalFactor); + const mantissaDecimalFactor = 36 - underlyingDecimals; + const bdFactor = exponentToBigInt(mantissaDecimalFactor); const oracle2 = PriceOracle.bind(oracleAddress); - const oracleUnderlyingPrice = oracle2.getUnderlyingPrice(eventAddress).toBigDecimal(); - if (oracleUnderlyingPrice.equals(BigDecimal.zero())) { + const oracleUnderlyingPrice = oracle2.getUnderlyingPrice(eventAddress); + if (oracleUnderlyingPrice.equals(BigInt.zero())) { return oracleUnderlyingPrice; } const underlyingPrice = oracleUnderlyingPrice.div(bdFactor); - return underlyingPrice; + return underlyingPrice.times(BigInt.fromI32(100)); } diff --git a/subgraphs/venus/src/utilities/getUnderlyingPrice.ts b/subgraphs/venus/src/utilities/getUnderlyingPrice.ts index 29489791..0485b75b 100644 --- a/subgraphs/venus/src/utilities/getUnderlyingPrice.ts +++ b/subgraphs/venus/src/utilities/getUnderlyingPrice.ts @@ -1,23 +1,10 @@ -import { Address, BigDecimal } from '@graphprotocol/graph-ts'; +import { Address, BigInt } from '@graphprotocol/graph-ts'; -import { zeroBD } from '../utilities/exponentToBigDecimal'; -import { getTokenPrice } from './getTokenPrice'; - -class GetUnderlyingPriceReturn { - underlyingPrice: BigDecimal; - underlyingPriceUsd: BigDecimal; -} - -export function getUnderlyingPrice( - address: string, - underlyingDecimals: i32, -): GetUnderlyingPriceReturn { - let underlyingPriceUsd = zeroBD; - let underlyingPrice = zeroBD; +import { getTokenPriceCents } from './getTokenPriceCents'; +export function getUnderlyingPrice(address: string, underlyingDecimals: i32): BigInt { const contractAddress = Address.fromString(address); - underlyingPrice = getTokenPrice(contractAddress, underlyingDecimals); - underlyingPriceUsd = underlyingPrice.truncate(underlyingDecimals); + const underlyingPriceCents = getTokenPriceCents(contractAddress, underlyingDecimals); - return { underlyingPrice, underlyingPriceUsd }; + return underlyingPriceCents; } diff --git a/subgraphs/venus/src/utilities/ids.ts b/subgraphs/venus/src/utilities/ids.ts new file mode 100644 index 00000000..ca39c4f7 --- /dev/null +++ b/subgraphs/venus/src/utilities/ids.ts @@ -0,0 +1,25 @@ +import { Address, BigInt, Bytes } from '@graphprotocol/graph-ts'; + +import { Actions } from '../constants'; + +const SEPERATOR = '-'; + +const joinIds = (idArray: Array): string => idArray.join(SEPERATOR); + +export const getMarketId = (vTokenAddress: Address): string => + joinIds([vTokenAddress.toHexString()]); + +export const getAccountVTokenId = (marketAddress: Address, accountAddress: Address): string => + joinIds([marketAddress.toHexString(), accountAddress.toHexString()]); + +export const getAccountVTokenTransactionId = ( + accountAddress: Address, + transactionHash: Bytes, + logIndex: BigInt, +): string => + joinIds([accountAddress.toHexString(), transactionHash.toHexString(), logIndex.toString()]); + +export const getMarketActionId = (vTokenAddress: Address, action: i32): string => { + const actionString = Actions[action]; + return joinIds([vTokenAddress.toHexString(), actionString]); +}; diff --git a/subgraphs/venus/src/utilities/index.ts b/subgraphs/venus/src/utilities/index.ts index 687ddf4f..d409e6a1 100644 --- a/subgraphs/venus/src/utilities/index.ts +++ b/subgraphs/venus/src/utilities/index.ts @@ -1,5 +1,5 @@ export { getBnbPriceInUsd } from './getBnbPriceInUsd'; -export { getTokenPrice } from './getTokenPrice'; +export { getTokenPriceCents } from './getTokenPriceCents'; export { exponentToBigDecimal } from './exponentToBigDecimal'; export { ensureComptrollerSynced } from './ensureComptrollerSynced'; export { getUnderlyingPrice } from './getUnderlyingPrice'; diff --git a/subgraphs/venus/template.yaml b/subgraphs/venus/template.yaml index 5dd06492..001f0d8c 100644 --- a/subgraphs/venus/template.yaml +++ b/subgraphs/venus/template.yaml @@ -57,7 +57,7 @@ templates: kind: ethereum/events apiVersion: 0.0.5 language: wasm/assemblyscript - file: ./src/mappings/vtoken.ts + file: ./src/mappings/vToken.ts entities: - User - Market @@ -65,6 +65,8 @@ templates: abis: - name: VToken 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 - name: PriceOracle file: ../../node_modules/@venusprotocol/oracle/artifacts/contracts/ResilientOracle.sol/ResilientOracle.json - name: BEP20 @@ -72,9 +74,9 @@ templates: - name: Comptroller file: ../../node_modules/@venusprotocol/venus-protocol/artifacts/contracts/Comptroller/Comptroller.sol/Comptroller.json eventHandlers: - - event: Mint(address,uint256,uint256) + - event: Mint(address,uint256,uint256,uint256) handler: handleMint - - event: Redeem(address,uint256,uint256) + - event: Redeem(address,uint256,uint256,uint256) handler: handleRedeem - event: Borrow(address,uint256,uint256,uint256) handler: handleBorrow diff --git a/subgraphs/venus/tests/Comptroller.test.ts b/subgraphs/venus/tests/Comptroller.test.ts index a99eaf36..15d9e624 100644 --- a/subgraphs/venus/tests/Comptroller.test.ts +++ b/subgraphs/venus/tests/Comptroller.test.ts @@ -54,22 +54,21 @@ describe('handleMarketListing', () => { assertMarketDocument('underlyingDecimals', '18'); assertMarketDocument('underlyingName', 'Binance Coin'); assertMarketDocument('underlyingSymbol', 'BNB'); - assertMarketDocument('underlyingPriceUSD', '0'); - assertMarketDocument('underlyingPrice', '1'); - assertMarketDocument('borrowRate', '0'); - assertMarketDocument('cash', '0'); + assertMarketDocument('underlyingPriceCents', '0'); + assertMarketDocument('borrowRateMantissa', '0'); + assertMarketDocument('cashMantissa', '0'); assertMarketDocument('collateralFactor', '0'); - assertMarketDocument('exchangeRate', '0'); + assertMarketDocument('exchangeRateMantissa', '0'); assertMarketDocument('interestRateModelAddress', interestRateModelAddress.toHex()); assertMarketDocument('name', 'Venus BNB'); - assertMarketDocument('reservesWei', '0'); - assertMarketDocument('supplyRate', '0'); + assertMarketDocument('reservesMantissa', '0'); + assertMarketDocument('supplyRateMantissa', '0'); assertMarketDocument('symbol', 'vBNB'); - assertMarketDocument('totalBorrows', '0'); - assertMarketDocument('totalSupply', '0'); + assertMarketDocument('totalBorrowsMantissa', '0'); + assertMarketDocument('totalSupplyMantissa', '0'); assertMarketDocument('accrualBlockNumber', '0'); assertMarketDocument('blockTimestamp', '0'); - assertMarketDocument('borrowIndex', '0'); + assertMarketDocument('borrowIndexMantissa', '0'); assertMarketDocument('reserveFactor', '100'); }); }); diff --git a/subgraphs/venus/tests/VToken/events.ts b/subgraphs/venus/tests/VToken/events.ts new file mode 100644 index 00000000..bf68c952 --- /dev/null +++ b/subgraphs/venus/tests/VToken/events.ts @@ -0,0 +1,381 @@ +import { Address, BigInt, ethereum } from '@graphprotocol/graph-ts'; +import { newMockEvent } from 'matchstick-as'; + +import { MarketListed as MarketListedEvent } from '../../generated/Comptroller/Comptroller'; +import { + AccrueInterest as AccrueInterestEvent, + Approval as ApprovalEvent, + Borrow as BorrowEvent, + LiquidateBorrow as LiquidateBorrowEvent, + Mint as MintEvent, + NewComptroller as NewComptrollerEvent, + NewMarketInterestRateModel as NewMarketInterestRateModelEvent, + NewReserveFactor as NewReserveFactorEvent, + Redeem as RedeemEvent, + RepayBorrow as RepayBorrowEvent, + Transfer as TransferEvent, +} from '../../generated/templates/VToken/VToken'; + +export const createMarketListedEvent = ( + vTokenAddress: Address, +): MarketListedEvent => { + const event = changetype(newMockEvent()); + + event.parameters = []; + const vTokenParam = new ethereum.EventParam('vToken', ethereum.Value.fromAddress(vTokenAddress)); + event.parameters.push(vTokenParam); + + return event; +}; + +export const createMintEvent = ( + vTokenAddress: Address, + minterAddress: Address, + mintAmount: BigInt, + mintTokens: BigInt, + accountBalance: BigInt, +): MintEvent => { + 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); + + const accountBalanceParam = new ethereum.EventParam( + 'accountBalance', + ethereum.Value.fromUnsignedBigInt(accountBalance), + ); + event.parameters.push(accountBalanceParam); + + return event; +}; + +export const createRedeemEvent = ( + vTokenAddress: Address, + redeemerAddress: Address, + redeemAmount: BigInt, + redeemTokens: BigInt, + accountBalance: BigInt, +): RedeemEvent => { + 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); + + const accountBalanceParam = new ethereum.EventParam( + 'accountBalance', + ethereum.Value.fromUnsignedBigInt(accountBalance), + ); + event.parameters.push(accountBalanceParam); + + return event; +}; + +export const createBorrowEvent = ( + vTokenAddress: Address, + borrowerAddress: Address, + borrowAmount: BigInt, + accountBorrows: BigInt, + totalBorrows: BigInt, +): BorrowEvent => { + const event = changetype(newMockEvent()); + event.address = vTokenAddress; + event.parameters = []; + + const borrowerParam = new ethereum.EventParam( + 'borrower', + ethereum.Value.fromAddress(borrowerAddress), + ); + event.parameters.push(borrowerParam); + + const borrowAmountParam = new ethereum.EventParam( + 'borrowAmount', + ethereum.Value.fromUnsignedBigInt(borrowAmount), + ); + event.parameters.push(borrowAmountParam); + + const accountBorrowsParam = new ethereum.EventParam( + 'accountBorrows', + ethereum.Value.fromUnsignedBigInt(accountBorrows), + ); + event.parameters.push(accountBorrowsParam); + + const totalBorrowsParam = new ethereum.EventParam( + 'totalBorrowsMantissa', + ethereum.Value.fromUnsignedBigInt(totalBorrows), + ); + event.parameters.push(totalBorrowsParam); + + return event; +}; + +export const createRepayBorrowEvent = ( + vTokenAddress: Address, + payerAddress: Address, + borrowerAddress: Address, + repayAmount: BigInt, + accountBorrows: BigInt, + totalBorrows: BigInt, +): RepayBorrowEvent => { + const event = changetype(newMockEvent()); + event.address = vTokenAddress; + event.parameters = []; + + const payerParam = new ethereum.EventParam('payer', ethereum.Value.fromAddress(payerAddress)); + event.parameters.push(payerParam); + + const borrowerParam = new ethereum.EventParam( + 'borrower', + ethereum.Value.fromAddress(borrowerAddress), + ); + event.parameters.push(borrowerParam); + + const repayAmountParam = new ethereum.EventParam( + 'repayAmount', + ethereum.Value.fromUnsignedBigInt(repayAmount), + ); + event.parameters.push(repayAmountParam); + + const accountBorrowsParam = new ethereum.EventParam( + 'accountBorrows', + ethereum.Value.fromUnsignedBigInt(accountBorrows), + ); + event.parameters.push(accountBorrowsParam); + + const totalBorrowsParam = new ethereum.EventParam( + 'totalBorrowsMantissa', + ethereum.Value.fromUnsignedBigInt(totalBorrows), + ); + event.parameters.push(totalBorrowsParam); + + return event; +}; + +export const createLiquidateBorrowEvent = ( + vTokenAddress: Address, + liquidatorAddress: Address, + borrowerAddress: Address, + repayAmount: BigInt, + vTokenCollateral: Address, + seizeTokens: BigInt, +): LiquidateBorrowEvent => { + const event = changetype(newMockEvent()); + event.address = vTokenAddress; + event.parameters = []; + + const liquidatorParam = new ethereum.EventParam( + 'liquidator', + ethereum.Value.fromAddress(liquidatorAddress), + ); + event.parameters.push(liquidatorParam); + + const borrowerParam = new ethereum.EventParam( + 'borrower', + ethereum.Value.fromAddress(borrowerAddress), + ); + event.parameters.push(borrowerParam); + + const repayAmountParam = new ethereum.EventParam( + 'repayAmount', + ethereum.Value.fromUnsignedBigInt(repayAmount), + ); + event.parameters.push(repayAmountParam); + + const vTokenCollateralParam = new ethereum.EventParam( + 'vTokenCollateral', + ethereum.Value.fromAddress(vTokenCollateral), + ); + event.parameters.push(vTokenCollateralParam); + + const seizeTokensParam = new ethereum.EventParam( + 'seizeTokens', + ethereum.Value.fromUnsignedBigInt(seizeTokens), + ); + event.parameters.push(seizeTokensParam); + + return event; +}; + +export const createAccrueInterestEvent = ( + vTokenAddress: Address, + cashPrior: BigInt, + interestAccumulated: BigInt, + borrowIndex: BigInt, + totalBorrows: BigInt, +): AccrueInterestEvent => { + const event = changetype(newMockEvent()); + event.address = vTokenAddress; + event.parameters = []; + + const cashPriorParam = new ethereum.EventParam( + 'cashPrior', + ethereum.Value.fromUnsignedBigInt(cashPrior), + ); + event.parameters.push(cashPriorParam); + + const interestAccumulatedParam = new ethereum.EventParam( + 'interestAccumulated', + ethereum.Value.fromUnsignedBigInt(interestAccumulated), + ); + event.parameters.push(interestAccumulatedParam); + + const borrowIndexParam = new ethereum.EventParam( + 'borrowIndexMantissa', + ethereum.Value.fromUnsignedBigInt(borrowIndex), + ); + event.parameters.push(borrowIndexParam); + + const totalBorrowsParam = new ethereum.EventParam( + 'totalBorrowsMantissa', + ethereum.Value.fromUnsignedBigInt(totalBorrows), + ); + event.parameters.push(totalBorrowsParam); + + return event; +}; + +export const createNewReserveFactorEvent = ( + vTokenAddress: Address, + oldReserveFactorMantissa: BigInt, + newReserveFactorMantissa: BigInt, +): NewReserveFactorEvent => { + const event = changetype(newMockEvent()); + event.address = vTokenAddress; + event.parameters = []; + + const oldReserveFactorMantissaParam = new ethereum.EventParam( + 'oldReserveFactorMantissa', + ethereum.Value.fromUnsignedBigInt(oldReserveFactorMantissa), + ); + event.parameters.push(oldReserveFactorMantissaParam); + + const newReserveFactorMantissaParam = new ethereum.EventParam( + 'newReserveFactorMantissa', + ethereum.Value.fromUnsignedBigInt(newReserveFactorMantissa), + ); + event.parameters.push(newReserveFactorMantissaParam); + + return event; +}; + +export const createTransferEvent = ( + vTokenAddress: Address, + from: Address, + to: Address, + amount: BigInt, +): TransferEvent => { + const event = changetype(newMockEvent()); + event.address = vTokenAddress; + event.parameters = []; + + const fromParam = new ethereum.EventParam('from', ethereum.Value.fromAddress(from)); + event.parameters.push(fromParam); + + const toParam = new ethereum.EventParam('to', ethereum.Value.fromAddress(to)); + event.parameters.push(toParam); + + const amountParam = new ethereum.EventParam('amount', ethereum.Value.fromUnsignedBigInt(amount)); + event.parameters.push(amountParam); + + return event; +}; + +export const createNewMarketInterestRateModelEvent = ( + vTokenAddress: Address, + oldInterestRateModel: Address, + newInterestRateModel: Address, +): NewMarketInterestRateModelEvent => { + const event = changetype(newMockEvent()); + event.address = vTokenAddress; + event.parameters = []; + + const oldInterestRateModelParam = new ethereum.EventParam( + 'oldInterestRateModel', + ethereum.Value.fromAddress(oldInterestRateModel), + ); + event.parameters.push(oldInterestRateModelParam); + + const newInterestRateModelParam = new ethereum.EventParam( + 'newInterestRateModel', + ethereum.Value.fromAddress(newInterestRateModel), + ); + event.parameters.push(newInterestRateModelParam); + + return event; +}; + +export const createApprovalEvent = ( + vTokenAddress: Address, + owner: Address, + spender: Address, + amount: BigInt, +): ApprovalEvent => { + const event = changetype(newMockEvent()); + event.address = vTokenAddress; + event.parameters = []; + + const ownerParam = new ethereum.EventParam('owner', ethereum.Value.fromAddress(owner)); + event.parameters.push(ownerParam); + + const spenderParam = new ethereum.EventParam('spender', ethereum.Value.fromAddress(spender)); + event.parameters.push(spenderParam); + + const amountParam = new ethereum.EventParam('amount', ethereum.Value.fromUnsignedBigInt(amount)); + event.parameters.push(amountParam); + + return event; +}; + +export const createNewComptrollerEvent = ( + vTokenAddress: Address, + oldComptroller: Address, + newComptroller: Address, +): NewComptrollerEvent => { + const event = changetype(newMockEvent()); + event.address = vTokenAddress; + event.parameters = []; + + const oldComptrollerParam = new ethereum.EventParam( + 'oldComptroller', + ethereum.Value.fromAddress(oldComptroller), + ); + event.parameters.push(oldComptrollerParam); + + const newComptrollerParam = new ethereum.EventParam( + 'newComptroller', + ethereum.Value.fromAddress(newComptroller), + ); + event.parameters.push(newComptrollerParam); + + return event; +}; diff --git a/subgraphs/venus/tests/VToken/index.test.ts b/subgraphs/venus/tests/VToken/index.test.ts new file mode 100644 index 00000000..8a704b78 --- /dev/null +++ b/subgraphs/venus/tests/VToken/index.test.ts @@ -0,0 +1,564 @@ +import { Address, BigInt, ethereum } from '@graphprotocol/graph-ts'; +import { createMockedFunction } from 'matchstick-as'; +import { + afterEach, + assert, + beforeAll, + beforeEach, + clearStore, + describe, + test, +} from 'matchstick-as/assembly/index'; + +import { oneBigInt, zeroBigInt32 } from '../../src/constants'; +import { comptrollerAddress } from '../../src/constants/addresses'; +import { handleMarketListed } from '../../src/mappings/comptroller'; +import { + handleAccrueInterest, + handleBorrow, + handleLiquidateBorrow, + handleMint, + handleNewMarketInterestRateModel, + handleNewReserveFactor, + handleRedeem, + handleRepayBorrow, + handleTransfer, +} from '../../src/mappings/vToken'; +import { getMarket } from '../../src/operations/get'; +import { getAccountVTokenId } from '../../src/utilities/ids'; +import { + createAccrueInterestEvent, + createBorrowEvent, + createLiquidateBorrowEvent, + createMarketListedEvent, + createMintEvent, + createNewMarketInterestRateModelEvent, + createNewReserveFactorEvent, + createRedeemEvent, + createRepayBorrowEvent, + createTransferEvent, +} from './events'; +import { createAccountVTokenBalanceOfMock } from './mocks'; +import { createMarketMock, createPriceOracleMock, createVBep20AndUnderlyingMock } from './mocks'; + +const tokenAddress = Address.fromString('0x0000000000000000000000000000000000000b0b'); +const user1Address = Address.fromString('0x0000000000000000000000000000000000000101'); +const user2Address = Address.fromString('0x0000000000000000000000000000000000000202'); +const aaaTokenAddress = Address.fromString('0x0000000000000000000000000000000000000aaa'); + +const interestRateModelAddress = Address.fromString('0x594942C0e62eC577889777424CD367545C796A74'); + +const underlyingPrice = BigInt.fromString('15000000000000000'); + +const cleanup = (): void => { + clearStore(); +}; + +beforeAll(() => { + createVBep20AndUnderlyingMock( + aaaTokenAddress, + tokenAddress, + comptrollerAddress, + 'AAA Coin', + 'AAA', + BigInt.fromI32(18), + BigInt.fromI32(100), + interestRateModelAddress, + underlyingPrice, + ); + + createMarketMock(aaaTokenAddress); + + createPriceOracleMock([ + [ethereum.Value.fromAddress(aaaTokenAddress), ethereum.Value.fromI32(99)], + ]); + + createAccountVTokenBalanceOfMock(aaaTokenAddress, user1Address, zeroBigInt32); + createAccountVTokenBalanceOfMock(aaaTokenAddress, user2Address, zeroBigInt32); +}); + +beforeEach(() => { + // Add Market + const marketAddedEvent = createMarketListedEvent(aaaTokenAddress); + + handleMarketListed(marketAddedEvent); +}); + +afterEach(() => { + cleanup(); +}); + +describe('VToken', () => { + test('registers mint event', () => { + const minter = user1Address; + const actualMintAmount = BigInt.fromString('124620530798726345'); + const mintTokens = BigInt.fromString('37035970026454'); + const accountBalance = mintTokens; + const mintEvent = createMintEvent( + aaaTokenAddress, + minter, + actualMintAmount, + mintTokens, + accountBalance, + ); + const market = getMarket(aaaTokenAddress); + assert.assertNotNull(market); + if (!market) { + return; + } + + handleMint(mintEvent); + }); + + test('registers redeem event', () => { + const redeemer = user2Address; + const actualRedeemAmount = BigInt.fromString('124620530798726345'); + const redeemTokens = BigInt.fromString('37035970026454'); + const accountBalance = redeemTokens; + const redeemEvent = createRedeemEvent( + aaaTokenAddress, + redeemer, + actualRedeemAmount, + redeemTokens, + accountBalance, + ); + const market = getMarket(aaaTokenAddress); + assert.assertNotNull(market); + if (!market) { + return; + } + + handleRedeem(redeemEvent); + }); + + test('registers borrow event', () => { + /** Constants */ + const borrower = user1Address; + const borrowAmount = BigInt.fromString('1246205398726345'); + const accountBorrows = BigInt.fromString('35970026454'); + const totalBorrows = BigInt.fromString('37035970026454'); + + /** Setup test */ + const borrowEvent = createBorrowEvent( + aaaTokenAddress, + borrower, + borrowAmount, + accountBorrows, + totalBorrows, + ); + + createMockedFunction( + aaaTokenAddress, + 'getAccountSnapshot', + 'getAccountSnapshot(address):(uint256,uint256,uint256,uint256)', + ) + .withArgs([ethereum.Value.fromAddress(borrower)]) + .returns([ + ethereum.Value.fromSignedBigInt(zeroBigInt32), + ethereum.Value.fromSignedBigInt(zeroBigInt32), + ethereum.Value.fromSignedBigInt(zeroBigInt32), + ethereum.Value.fromSignedBigInt(oneBigInt), + ]); + + /** Fire Event */ + handleBorrow(borrowEvent); + + const accountVTokenId = getAccountVTokenId(aaaTokenAddress, borrower); + const market = getMarket(aaaTokenAddress); + assert.assertNotNull(market); + if (!market) { + return; + } + + assert.fieldEquals( + 'AccountVToken', + accountVTokenId, + 'accrualBlockNumber', + borrowEvent.block.number.toString(), + ); + + assert.fieldEquals( + 'AccountVToken', + accountVTokenId, + 'accountBorrowIndexMantissa', + market.borrowIndexMantissa.toString(), + ); + }); + + test('registers repay borrow event', () => { + /** Constants */ + const borrower = user1Address; + const payer = user1Address; + const repayAmount = BigInt.fromString('1246205398726345'); + const accountBorrows = BigInt.fromString('35970026454'); + const totalBorrows = BigInt.fromString('37035970026454'); + const balanceOf = BigInt.fromString('9937035970026454'); + + /** Setup test */ + const repayBorrowEvent = createRepayBorrowEvent( + aaaTokenAddress, + payer, + borrower, + repayAmount, + accountBorrows, + totalBorrows, + ); + + createMockedFunction( + aaaTokenAddress, + 'getAccountSnapshot', + 'getAccountSnapshot(address):(uint256,uint256,uint256,uint256)', + ) + .withArgs([ethereum.Value.fromAddress(borrower)]) + .returns([ + ethereum.Value.fromSignedBigInt(zeroBigInt32), + ethereum.Value.fromSignedBigInt(balanceOf), + ethereum.Value.fromSignedBigInt(accountBorrows), + ethereum.Value.fromSignedBigInt(oneBigInt), + ]); + + /** Fire Event */ + handleRepayBorrow(repayBorrowEvent); + + const accountVTokenId = getAccountVTokenId(aaaTokenAddress, borrower); + const market = getMarket(aaaTokenAddress); + assert.assertNotNull(market); + if (!market) { + return; + } + + assert.fieldEquals( + 'AccountVToken', + accountVTokenId, + 'accrualBlockNumber', + repayBorrowEvent.block.number.toString(), + ); + + assert.fieldEquals( + 'AccountVToken', + accountVTokenId, + 'accountBorrowIndexMantissa', + market.borrowIndexMantissa.toString(), + ); + }); + + test('registers liquidate borrow event', () => { + /** Constants */ + const borrower = user1Address; + const liquidator = user1Address; + const repayAmount = BigInt.fromString('1246205398726345'); + const seizeTokens = BigInt.fromString('37035970026454'); + const vTokenCollateral = aaaTokenAddress; + + /** Setup test */ + const liquidateBorrowEvent = createLiquidateBorrowEvent( + aaaTokenAddress, + liquidator, + borrower, + repayAmount, + vTokenCollateral, + seizeTokens, + ); + + /** Fire Event */ + handleLiquidateBorrow(liquidateBorrowEvent); + + const market = getMarket(aaaTokenAddress); + assert.assertNotNull(market); + 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); + 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); + + const supplier02 = user2Address; + mintEvent = createMintEvent( + aaaTokenAddress, + supplier02, + actualMintAmount, + mintTokens, + accountBalance, + ); + createAccountVTokenBalanceOfMock(aaaTokenAddress, supplier02, mintTokens); + + handleMint(mintEvent); + + let redeemEvent = createRedeemEvent( + aaaTokenAddress, + supplier02, + actualMintAmount, + mintTokens, + zeroBigInt32, + ); + createAccountVTokenBalanceOfMock(aaaTokenAddress, supplier02, zeroBigInt32); + + handleRedeem(redeemEvent); + + redeemEvent = createRedeemEvent( + aaaTokenAddress, + supplier01, + halfActualMintAmount, + halfMintTokens, + halfMintTokens, + ); + createAccountVTokenBalanceOfMock(aaaTokenAddress, supplier01, halfMintTokens); + + handleRedeem(redeemEvent); + + redeemEvent = createRedeemEvent( + aaaTokenAddress, + supplier01, + halfActualMintAmount, + halfMintTokens, + zeroBigInt32, + ); + createAccountVTokenBalanceOfMock(aaaTokenAddress, supplier01, zeroBigInt32); + + handleRedeem(redeemEvent); + }); + + 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); + + const borrower02 = user2Address; + borrowEvent = createBorrowEvent( + aaaTokenAddress, + borrower02, + borrowAmount, + borrowAmount, + borrowAmount, + ); + + handleBorrow(borrowEvent); + + let repayEvent = createRepayBorrowEvent( + aaaTokenAddress, + borrower02, + borrower02, + borrowAmount, + zeroBigInt32, + zeroBigInt32, + ); + + handleRepayBorrow(repayEvent); + + repayEvent = createRepayBorrowEvent( + aaaTokenAddress, + borrower01, + borrower01, + halfBorrowAmountTokens, + halfBorrowAmountTokens, + halfBorrowAmountTokens, + ); + + handleRepayBorrow(repayEvent); + + repayEvent = createRepayBorrowEvent( + aaaTokenAddress, + borrower01, + borrower01, + halfBorrowAmountTokens, + zeroBigInt32, + zeroBigInt32, + ); + + handleRepayBorrow(repayEvent); + }); + }); +}); diff --git a/subgraphs/venus/tests/VToken/mocks.ts b/subgraphs/venus/tests/VToken/mocks.ts new file mode 100644 index 00000000..47a61834 --- /dev/null +++ b/subgraphs/venus/tests/VToken/mocks.ts @@ -0,0 +1,221 @@ +import { Address, BigInt, ethereum } from '@graphprotocol/graph-ts'; +import { createMockedFunction } from 'matchstick-as'; + +export const mockPriceOracleAddress = Address.fromString( + '0xb0b0000000000000000000000000000000000000', +); + +export const createVBep20AndUnderlyingMock = ( + contractAddress: Address, + underlyingAddress: Address, + comptrollerAddress: Address, + name: string, + symbol: string, + decimals: BigInt, + reserveFactorMantissa: BigInt, + interestRateModelAddress: Address, + underlyingPrice: BigInt, +): void => { + // vBep20 + createMockedFunction(contractAddress, 'underlying', 'underlying():(address)').returns([ + ethereum.Value.fromAddress(underlyingAddress), + ]); + + createMockedFunction(contractAddress, 'name', 'name():(string)').returns([ + ethereum.Value.fromString(`Venus ${name}`), + ]); + + createMockedFunction(contractAddress, 'symbol', 'symbol():(string)').returns([ + ethereum.Value.fromString(`v${symbol}`), + ]); + + createMockedFunction( + contractAddress, + 'interestRateModel', + 'interestRateModel():(address)', + ).returns([ethereum.Value.fromAddress(interestRateModelAddress)]); + + createMockedFunction( + contractAddress, + 'reserveFactorMantissa', + 'reserveFactorMantissa():(uint256)', + ).returns([ethereum.Value.fromUnsignedBigInt(reserveFactorMantissa)]); + + createMockedFunction(contractAddress, 'comptroller', 'comptroller():(address)').returns([ + ethereum.Value.fromAddress(comptrollerAddress), + ]); + + // Underlying + createMockedFunction(underlyingAddress, 'decimals', 'decimals():(uint8)').returns([ + ethereum.Value.fromUnsignedBigInt(decimals), + ]); + + createMockedFunction(underlyingAddress, 'name', 'name():(string)').returns([ + ethereum.Value.fromString(name), + ]); + + createMockedFunction(underlyingAddress, 'symbol', 'symbol():(string)').returns([ + ethereum.Value.fromString(symbol), + ]); + + createMockedFunction( + mockPriceOracleAddress, + 'getUnderlyingPrice', + 'getUnderlyingPrice(address):(uint256)', + ) + .withArgs([ethereum.Value.fromAddress(contractAddress)]) + .returns([ethereum.Value.fromUnsignedBigInt(underlyingPrice)]); + + createMockedFunction( + contractAddress, + 'borrowRatePerBlock', + 'borrowRatePerBlock():(uint256)', + ).returns([ethereum.Value.fromUnsignedBigInt(BigInt.fromString('0'))]); + + createMockedFunction(contractAddress, 'getCash', 'getCash():(uint256)').returns([ + ethereum.Value.fromUnsignedBigInt(BigInt.fromString('0')), + ]); + + createMockedFunction( + contractAddress, + 'exchangeRateStored', + 'exchangeRateStored():(uint256)', + ).returns([ethereum.Value.fromUnsignedBigInt(BigInt.fromString('0'))]); + + createMockedFunction(contractAddress, 'badDebt', 'badDebt():(uint256)').returns([ + ethereum.Value.fromUnsignedBigInt(BigInt.fromString('0')), + ]); + + createMockedFunction(contractAddress, 'totalSupply', 'totalSupply():(uint256)').returns([ + ethereum.Value.fromUnsignedBigInt(BigInt.fromString('36504567163409')), + ]); + + createMockedFunction(contractAddress, 'totalBorrows', 'totalBorrows():(uint256)').returns([ + ethereum.Value.fromUnsignedBigInt(BigInt.fromString('36504567163409')), + ]); + + createMockedFunction(contractAddress, 'totalReserves', 'totalReserves():(uint256)').returns([ + ethereum.Value.fromUnsignedBigInt(BigInt.fromString('0')), + ]); + + createMockedFunction( + contractAddress, + 'accrualBlockNumber', + 'accrualBlockNumber():(uint256)', + ).returns([ethereum.Value.fromUnsignedBigInt(BigInt.fromString('0'))]); + + createMockedFunction(contractAddress, 'borrowIndex', 'borrowIndex():(uint256)').returns([ + ethereum.Value.fromUnsignedBigInt(BigInt.fromString('0')), + ]); + + createMockedFunction( + contractAddress, + 'supplyRatePerBlock', + 'supplyRatePerBlock():(uint256)', + ).returns([ethereum.Value.fromUnsignedBigInt(BigInt.fromString('0'))]); + + createMockedFunction(contractAddress, 'decimals', 'decimals():(uint8)').returns([ + ethereum.Value.fromUnsignedBigInt(BigInt.fromString('18')), + ]); + + createMockedFunction(comptrollerAddress, 'supplyCaps', 'supplyCaps(address):(uint256)') + .withArgs([ethereum.Value.fromAddress(contractAddress)]) + .returns([ethereum.Value.fromUnsignedBigInt(BigInt.fromString('0'))]); + + createMockedFunction(comptrollerAddress, 'borrowCaps', 'borrowCaps(address):(uint256)') + .withArgs([ethereum.Value.fromAddress(contractAddress)]) + .returns([ethereum.Value.fromUnsignedBigInt(BigInt.fromString('0'))]); + + createMockedFunction(comptrollerAddress, 'markets', 'markets(address):(bool,uint256,uint256)') + .withArgs([ethereum.Value.fromAddress(contractAddress)]) + .returns([ + ethereum.Value.fromBoolean(true), + ethereum.Value.fromUnsignedBigInt(BigInt.fromString('0')), + ethereum.Value.fromUnsignedBigInt(BigInt.fromString('0')), + ]); + createMockedFunction(comptrollerAddress, 'oracle', 'oracle():(address)').returns([ + ethereum.Value.fromAddress(mockPriceOracleAddress), + ]); + + createMockedFunction(comptrollerAddress, 'closeFactorMantissa', 'closeFactorMantissa():(uint256)').returns([ + ethereum.Value.fromUnsignedBigInt(BigInt.fromString('10000000000000000')), + ]); + + createMockedFunction(comptrollerAddress, 'liquidationIncentiveMantissa', 'liquidationIncentiveMantissa():(uint256)').returns([ + ethereum.Value.fromUnsignedBigInt(BigInt.fromString('1000000000000000')), + ]); + + createMockedFunction(comptrollerAddress, 'maxAssets', 'maxAssets():(uint256)').returns([ + ethereum.Value.fromUnsignedBigInt(BigInt.fromString('999')), + ]); + +}; + +export const createMarketMock = (marketAddress: Address): void => { + createMockedFunction( + marketAddress, + 'accrualBlockNumber', + 'accrualBlockNumber():(uint256)', + ).returns([ethereum.Value.fromI32(999)]); + + createMockedFunction(marketAddress, 'totalSupply', 'totalSupply():(uint256)').returns([ + ethereum.Value.fromUnsignedBigInt(BigInt.fromString('36504567163409')), + ]); + + createMockedFunction( + marketAddress, + 'exchangeRateStored', + 'exchangeRateStored():(uint256)', + ).returns([ethereum.Value.fromUnsignedBigInt(BigInt.fromString('365045823500000000000000'))]); + + createMockedFunction(marketAddress, 'borrowIndex', 'borrowIndex():(uint256)').returns([ + ethereum.Value.fromUnsignedBigInt(BigInt.fromString('300000000000000000000')), + ]); + + createMockedFunction(marketAddress, 'totalReserves', 'totalReserves():(uint256)').returns([ + ethereum.Value.fromUnsignedBigInt(BigInt.fromString('5128924555022289393')), + ]); + + createMockedFunction(marketAddress, 'totalBorrows', 'totalBorrows():(uint256)').returns([ + ethereum.Value.fromUnsignedBigInt(BigInt.fromString('2641234234636158123')), + ]); + + createMockedFunction(marketAddress, 'getCash', 'getCash():(uint256)').returns([ + ethereum.Value.fromUnsignedBigInt(BigInt.fromString('1418171344423412457')), + ]); + + createMockedFunction( + marketAddress, + 'borrowRatePerBlock', + 'borrowRatePerBlock():(uint256)', + ).returns([ethereum.Value.fromI32(12678493)]); + + createMockedFunction( + marketAddress, + 'supplyRatePerBlock', + 'supplyRatePerBlock():(uint256)', + ).returns([ethereum.Value.fromI32(12678493)]); +}; + +// type Tokens = [address, price][] +export const createPriceOracleMock = (tokens: Array>): void => { + tokens.forEach((token): void => { + createMockedFunction( + mockPriceOracleAddress, + 'getUnderlyingPrice', + 'getUnderlyingPrice(address):(uint256)', + ) + .withArgs([token[0]]) + .returns([token[1]]); + }); +}; + +export const createAccountVTokenBalanceOfMock = ( + vTokenAddress: Address, + accountAddress: Address, + balance: BigInt, +): void => { + createMockedFunction(vTokenAddress, 'balanceOf', 'balanceOf(address):(uint256)') + .withArgs([ethereum.Value.fromAddress(accountAddress)]) + .returns([ethereum.Value.fromSignedBigInt(balance)]); +}; diff --git a/subgraphs/venus/tests/mocks.ts b/subgraphs/venus/tests/mocks.ts index 3da2cc4f..2d4d5e76 100644 --- a/subgraphs/venus/tests/mocks.ts +++ b/subgraphs/venus/tests/mocks.ts @@ -37,6 +37,10 @@ export const createVBep20AndUnderlyingMock = ( 'reserveFactorMantissa():(uint256)', ).returns([ethereum.Value.fromUnsignedBigInt(reserveFactorMantissa)]); + createMockedFunction(contractAddress, 'decimals', 'decimals():(uint8)').returns([ + ethereum.Value.fromUnsignedBigInt(decimals), + ]); + // Underlying createMockedFunction(underlyingAddress, 'decimals', 'decimals():(uint8)').returns([ ethereum.Value.fromUnsignedBigInt(decimals), diff --git a/yarn.lock b/yarn.lock index 2aed9ec9..8e438796 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3697,9 +3697,9 @@ __metadata: linkType: hard "@openzeppelin/contracts@npm:^4.8.3": - version: 4.9.0 - resolution: "@openzeppelin/contracts@npm:4.9.0" - checksum: aa1499897f85821f9184497f5201152a4a272b05749c85c209e9e553d734467a78557bcd2efe35f07994d6e14f30c545b239a4f63622f27f808182efb3103658 + version: 4.9.1 + resolution: "@openzeppelin/contracts@npm:4.9.1" + checksum: 9bb3cc6aecd6c56d5ece10d9820d43e6e9c460395b75cc4af9b4e776f1a9e56c9906bc03538b0707e617dc31f1abdfffcbeedfecc8884358d864198ed73f59c1 languageName: node linkType: hard @@ -4605,15 +4605,17 @@ __metadata: languageName: node linkType: hard -"@venusprotocol/venus-protocol@npm:^0.6.0": - version: 0.6.0 - resolution: "@venusprotocol/venus-protocol@npm:0.6.0" +"@venusprotocol/venus-protocol@npm:^3.0.0-dev.7": + version: 3.0.0-dev.7 + resolution: "@venusprotocol/venus-protocol@npm:3.0.0-dev.7" 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: e05bf173d0320560b64bda12dfc32c1d2c205dd997ea1ca327d91e3b78dc03094f4a81bab4a84499c780d599e1574f70370becad0b3abb5b563d4154ba91668a + checksum: 6ce13172facd3d5caa149581271bda91c36505652902bf453b2c523ca71fcc2239b326dd25078239464a30d334706303f2f95eabd7084a92338e0dfe7ba498b4 languageName: node linkType: hard @@ -4747,13 +4749,6 @@ __metadata: languageName: node linkType: hard -"@yarnpkg/lockfile@npm:^1.1.0": - version: 1.1.0 - resolution: "@yarnpkg/lockfile@npm:1.1.0" - checksum: 05b881b4866a3546861fee756e6d3812776ea47fa6eb7098f983d6d0eefa02e12b66c3fff931574120f196286a7ad4879ce02743c8bb2be36c6a576c7852083a - languageName: node - linkType: hard - "JSONStream@npm:1.3.2": version: 1.3.2 resolution: "JSONStream@npm:1.3.2" @@ -6875,7 +6870,7 @@ __metadata: languageName: node linkType: hard -"cross-spawn@npm:^6.0.0, cross-spawn@npm:^6.0.5": +"cross-spawn@npm:^6.0.0": version: 6.0.5 resolution: "cross-spawn@npm:6.0.5" dependencies: @@ -8459,15 +8454,6 @@ __metadata: languageName: node linkType: hard -"find-yarn-workspace-root@npm:^2.0.0": - version: 2.0.0 - resolution: "find-yarn-workspace-root@npm:2.0.0" - dependencies: - micromatch: ^4.0.2 - checksum: fa5ca8f9d08fe7a54ce7c0a5931ff9b7e36f9ee7b9475fb13752bcea80ec6b5f180fa5102d60b376d5526ce924ea3fc6b19301262efa0a5d248dd710f3644242 - languageName: node - linkType: hard - "flat-cache@npm:^3.0.4": version: 3.0.4 resolution: "flat-cache@npm:3.0.4" @@ -8625,7 +8611,7 @@ __metadata: languageName: node linkType: hard -"fs-extra@npm:9.1.0, fs-extra@npm:^9.0.0, fs-extra@npm:^9.1.0": +"fs-extra@npm:9.1.0, fs-extra@npm:^9.1.0": version: 9.1.0 resolution: "fs-extra@npm:9.1.0" dependencies: @@ -9236,7 +9222,7 @@ __metadata: languageName: node linkType: hard -"graceful-fs@npm:^4.1.11, graceful-fs@npm:^4.1.2, graceful-fs@npm:^4.1.6, graceful-fs@npm:^4.1.9, graceful-fs@npm:^4.2.0, graceful-fs@npm:^4.2.4, graceful-fs@npm:^4.2.6": +"graceful-fs@npm:^4.1.2, graceful-fs@npm:^4.1.6, graceful-fs@npm:^4.1.9, graceful-fs@npm:^4.2.0, graceful-fs@npm:^4.2.4, graceful-fs@npm:^4.2.6": version: 4.2.10 resolution: "graceful-fs@npm:4.2.10" checksum: 3f109d70ae123951905d85032ebeae3c2a5a7a997430df00ea30df0e3a6c60cf6689b109654d6fdacd28810a053348c4d14642da1d075049e6be1ba5216218da @@ -10163,17 +10149,6 @@ __metadata: languageName: node linkType: hard -"is-ci@npm:^2.0.0": - version: 2.0.0 - resolution: "is-ci@npm:2.0.0" - dependencies: - ci-info: ^2.0.0 - bin: - is-ci: bin.js - checksum: 77b869057510f3efa439bbb36e9be429d53b3f51abd4776eeea79ab3b221337fe1753d1e50058a9e2c650d38246108beffb15ccfd443929d77748d8c0cc90144 - languageName: node - linkType: hard - "is-core-module@npm:^2.8.1, is-core-module@npm:^2.9.0": version: 2.11.0 resolution: "is-core-module@npm:2.11.0" @@ -10971,15 +10946,6 @@ __metadata: languageName: node linkType: hard -"klaw-sync@npm:^6.0.0": - version: 6.0.0 - resolution: "klaw-sync@npm:6.0.0" - dependencies: - graceful-fs: ^4.1.11 - checksum: 0da397f8961313c3ef8f79fb63af9002cde5a8fb2aeb1a37351feff0dd6006129c790400c3f5c3b4e757bedcabb13d21ec0a5eaef5a593d59515d4f2c291e475 - languageName: node - linkType: hard - "klaw@npm:^1.0.0": version: 1.3.1 resolution: "klaw@npm:1.3.1" @@ -11615,7 +11581,7 @@ __metadata: languageName: node linkType: hard -"micromatch@npm:^4.0.2, micromatch@npm:^4.0.4, micromatch@npm:^4.0.5": +"micromatch@npm:^4.0.4, micromatch@npm:^4.0.5": version: 4.0.5 resolution: "micromatch@npm:4.0.5" dependencies: @@ -12952,30 +12918,6 @@ __metadata: languageName: node linkType: hard -"patch-package@npm:6.5.1": - version: 6.5.1 - resolution: "patch-package@npm:6.5.1" - dependencies: - "@yarnpkg/lockfile": ^1.1.0 - chalk: ^4.1.2 - cross-spawn: ^6.0.5 - find-yarn-workspace-root: ^2.0.0 - fs-extra: ^9.0.0 - is-ci: ^2.0.0 - klaw-sync: ^6.0.0 - minimist: ^1.2.6 - open: ^7.4.2 - rimraf: ^2.6.3 - semver: ^5.6.0 - slash: ^2.0.0 - tmp: ^0.0.33 - yaml: ^1.10.2 - bin: - patch-package: index.js - checksum: 8530ffa30f11136b527c6eddf6da48fa12856ee510a47edb1f9cdf8a025636adb82968f5fae778b5e04ce8c87915ebdf5911422b54add59a5a42e372a8f30eb2 - languageName: node - linkType: hard - "path-browserify@npm:1.0.1": version: 1.0.1 resolution: "path-browserify@npm:1.0.1" @@ -14136,7 +14078,7 @@ __metadata: languageName: node linkType: hard -"semver@npm:2 || 3 || 4 || 5, semver@npm:^5.5.0, semver@npm:^5.6.0, semver@npm:^5.7.0": +"semver@npm:2 || 3 || 4 || 5, semver@npm:^5.5.0, semver@npm:^5.7.0": version: 5.7.1 resolution: "semver@npm:5.7.1" bin: @@ -14398,13 +14340,6 @@ __metadata: languageName: node linkType: hard -"slash@npm:^2.0.0": - version: 2.0.0 - resolution: "slash@npm:2.0.0" - checksum: 512d4350735375bd11647233cb0e2f93beca6f53441015eea241fe784d8068281c3987fbaa93e7ef1c38df68d9c60013045c92837423c69115297d6169aa85e6 - languageName: node - linkType: hard - "slash@npm:^3.0.0": version: 3.0.0 resolution: "slash@npm:3.0.0" @@ -14922,7 +14857,7 @@ __metadata: "@venusprotocol/governance-contracts": ^1.0.0 "@venusprotocol/isolated-pools": 1.0.0 "@venusprotocol/oracle": ^1.4.1 - "@venusprotocol/venus-protocol": ^0.6.0 + "@venusprotocol/venus-protocol": ^3.0.0-dev.7 assemblyscript: 0.19.23 chai: ^4.3.6 eslint: ^8.25.0 @@ -14940,7 +14875,6 @@ __metadata: matchstick-as: ^0.5.0 module-alias: ^2.2.2 mustache: ^4.2.0 - patch-package: 6.5.1 prettier: ^2.5.1 prettier-airbnb-config: ^1.0.0 solidity-coverage: ^0.7.21 @@ -15224,7 +15158,7 @@ __metadata: languageName: node linkType: hard -"tmp@npm:0.0.33, tmp@npm:^0.0.33": +"tmp@npm:0.0.33": version: 0.0.33 resolution: "tmp@npm:0.0.33" dependencies: