From 207bed40ec7628ff5c7c7b9e3f88e98c221ee254 Mon Sep 17 00:00:00 2001 From: Jean Chambras Date: Thu, 23 May 2024 13:51:21 +0200 Subject: [PATCH 1/2] Create an IBT entity to get more IBT Rate precision --- schema.graphql | 18 +++++++++++++ src/entities/ERC20.ts | 16 +++++++++++ src/entities/ERC4626.ts | 38 ++++++++++++++++++++++++-- src/entities/IBT.ts | 48 +++++++++++++++++++++++++++++++++ src/mappings/futures.ts | 7 ++++- src/mappings/transfers.ts | 10 +++++++ src/tests/amm.test.ts | 31 ++++++++++++++++++--- src/tests/futureDayData.test.ts | 18 ++++++++++--- src/tests/futures.test.ts | 36 ++++++++++++++++++++++--- src/tests/mocks/ERC4626.ts | 33 +++++++++++++---------- src/tests/transfers.test.ts | 14 +++++++--- src/utils/idGenerators.ts | 2 ++ subgraph.template.yaml | 35 +++++++++++++++++++----- 13 files changed, 269 insertions(+), 37 deletions(-) create mode 100644 src/entities/IBT.ts diff --git a/schema.graphql b/schema.graphql index 6a6d720..22b4c92 100644 --- a/schema.graphql +++ b/schema.graphql @@ -115,6 +115,23 @@ type Factory @entity { deployedLPVaults: [LPVault!]! @derivedFrom(field: "factory") } +#################### +# IBT and Rates +#################### + +type IBT @entity { + id: ID! + chainId: Int! + address: Bytes! + createdAtTimestamp: BigInt! + ibtDetails: Asset! + "IBT to underlying asset as a regular number with no decimals" + lastIBTRate: BigDecimal! + "IBT to underlying asset as returned by convertToAssets(UNIT). The number is in underlying asset decimals" + convertToAssetsUnit: BigInt! + lastUpdateTimestamp: BigInt! +} + #################### # Assets and prices #################### @@ -229,6 +246,7 @@ type Future @entity { state: FutureState! underlyingAsset: Asset! ibtAsset: Asset! + ibt: IBT! pools: [Pool!]! @derivedFrom(field: "futureVault") diff --git a/src/entities/ERC20.ts b/src/entities/ERC20.ts index 057f40e..ed7d2bd 100644 --- a/src/entities/ERC20.ts +++ b/src/entities/ERC20.ts @@ -1,11 +1,17 @@ import { Address, BigInt, log } from "@graphprotocol/graph-ts" +import { Asset } from "../../generated/schema" import { ERC20 } from "../../generated/templates/ERC20/ERC20" import { ZERO_BI } from "../constants" const UNKNOWN = "Unknown" export function getERC20Name(address: Address): string { + let asset = Asset.load(address.toHex()) + if (asset) { + return asset.name + } + let erc20Contract = ERC20.bind(address) let nameCall = erc20Contract.try_name() @@ -21,6 +27,11 @@ export function getERC20Name(address: Address): string { } export function getERC20Symbol(address: Address): string { + let asset = Asset.load(address.toHex()) + if (asset) { + return asset.symbol + } + let erc20Contract = ERC20.bind(address) let symbolCall = erc20Contract.try_symbol() @@ -36,6 +47,11 @@ export function getERC20Symbol(address: Address): string { } export function getERC20Decimals(address: Address): i32 { + let asset = Asset.load(address.toHex()) + if (asset) { + return asset.decimals + } + let erc20Contract = ERC20.bind(address) let decimalsCall = erc20Contract.try_decimals() diff --git a/src/entities/ERC4626.ts b/src/entities/ERC4626.ts index bb708fa..dbb7f11 100644 --- a/src/entities/ERC4626.ts +++ b/src/entities/ERC4626.ts @@ -1,11 +1,15 @@ import { Address, BigInt, log } from "@graphprotocol/graph-ts" +import { Asset } from "../../generated/schema" import { ERC4626 } from "../../generated/templates/PrincipalToken/ERC4626" -import { UNIT_BI, ZERO_BI } from "../constants" +import { UNIT_BI, ZERO_ADDRESS, ZERO_BI } from "../constants" +import { getERC20Decimals } from "./ERC20" export function getIBTRate(address: Address): BigInt { let erc4626Contract = ERC4626.bind(address) - let rate = erc4626Contract.try_convertToAssets(UNIT_BI) + const ibtDecimals = getERC20Decimals(address) + const IBT_UNIT = BigInt.fromI32(10).pow(ibtDecimals as u8) + let rate = erc4626Contract.try_convertToAssets(IBT_UNIT) if (!rate.reverted) { return rate.value @@ -15,6 +19,36 @@ export function getIBTRate(address: Address): BigInt { return UNIT_BI } +export function getERC4626Asset(address: Address): Address { + let erc4626Contract = ERC4626.bind(address) + let asset = erc4626Contract.try_asset() + + if (!asset.reverted) { + return asset.value + } + + log.warning("asset() call reverted for {}", [address.toHex()]) + + return ZERO_ADDRESS +} + +export function getERC4626UnderlyingDecimals(address: Address): i32 { + let ibtAsset = Asset.load(address.toHex()) + if (ibtAsset) { + let underlyingAddress = ibtAsset.underlying + if (underlyingAddress) { + return getERC20Decimals(Address.fromString(underlyingAddress)) + } + } + let underlying = getERC4626Asset(address) + return getERC20Decimals(underlying) +} + +export function getUnderlyingUnit(address: Address): BigInt { + let underlyingDecimals = getERC4626UnderlyingDecimals(address) + return BigInt.fromI32(10).pow(underlyingDecimals as u8) +} + export function getERC4626Balance( tokenAddress: Address, account: Address diff --git a/src/entities/IBT.ts b/src/entities/IBT.ts new file mode 100644 index 0000000..596de03 --- /dev/null +++ b/src/entities/IBT.ts @@ -0,0 +1,48 @@ +import { Address, BigInt, Bytes } from "@graphprotocol/graph-ts" + +import { IBT } from "../../generated/schema" +import { AssetType } from "../utils" +import { generateIBTId } from "../utils/idGenerators" +import { getAsset } from "./Asset" +import { getERC20Decimals } from "./ERC20" +import { getERC4626Asset, getIBTRate, getUnderlyingUnit } from "./ERC4626" +import { getNetwork } from "./Network" + +export function getIBT(ibtAddress: Bytes, timestamp: BigInt): IBT { + const ibtID = generateIBTId(ibtAddress.toHex()) + let ibt = IBT.load(ibtID) + if (ibt) { + return ibt + } + ibt = createIBT(ibtAddress, timestamp) + return ibt +} + +export function updateIBTRates(ibtAddress: Bytes, timestamp: BigInt): void { + let ibt = getIBT(ibtAddress, timestamp) + let convertToAssets = getIBTRate(Address.fromBytes(ibtAddress)) + ibt.convertToAssetsUnit = convertToAssets + const UNDERLYING_UNIT = getUnderlyingUnit(Address.fromBytes(ibtAddress)) + ibt.lastIBTRate = convertToAssets.divDecimal(UNDERLYING_UNIT.toBigDecimal()) + ibt.lastUpdateTimestamp = timestamp + ibt.save() +} + +export function createIBT(ibtAddress: Bytes, timestamp: BigInt): IBT { + const ibtID = generateIBTId(ibtAddress.toHex()) + let ibt = new IBT(ibtID) + ibt.chainId = getNetwork().chainId + ibt.address = ibtAddress + ibt.createdAtTimestamp = timestamp + let ibtDetails = getAsset(ibtAddress.toHex(), timestamp, AssetType.IBT) + let convertToAssets = getIBTRate(Address.fromBytes(ibtAddress)) + ibt.convertToAssetsUnit = convertToAssets + const underlying = getERC4626Asset(Address.fromBytes(ibtAddress)) + const underlying_decimals = getERC20Decimals(underlying) + const UNDERLYING_UNIT = BigInt.fromI32(10).pow(underlying_decimals as u8) + ibt.ibtDetails = ibtDetails.id + ibt.lastIBTRate = convertToAssets.divDecimal(UNDERLYING_UNIT.toBigDecimal()) + ibt.lastUpdateTimestamp = timestamp + ibt.save() + return ibt as IBT +} diff --git a/src/mappings/futures.ts b/src/mappings/futures.ts index 2ff5173..b288c51 100644 --- a/src/mappings/futures.ts +++ b/src/mappings/futures.ts @@ -15,6 +15,7 @@ import { import { ERC20, // LPVault as LPVaultTemplate, PrincipalToken as PrincipalTokenTemplate, + IBT, } from "../../generated/templates" import { FeeClaimed, @@ -47,6 +48,7 @@ import { getTotalAssets, getYT, } from "../entities/FutureVault" +import { getIBT as getIBTEntity } from "../entities/IBT" import { getNetwork } from "../entities/Network" import { createPool } from "../entities/Pool" import { createTransaction } from "../entities/Transaction" @@ -114,7 +116,7 @@ export function handlePTDeployed(event: PTDeployed): void { newFuture.underlyingAsset = underlyingAddress.toHex() newFuture.ibtAsset = ibtAddress.toHex() - + newFuture.ibt = getIBTEntity(ibtAddress, event.block.timestamp).id newFuture.yieldGenerators = [] newFuture.save() @@ -140,6 +142,9 @@ export function handlePTDeployed(event: PTDeployed): void { // Create dynamic data source for PT token events ERC20.create(event.params.pt) + // Create dynamic data source for IBT token events + IBT.create(ibtAddress) + // Create dynamic data source for YT token events ERC20.create(Address.fromBytes(ytToken.address)) diff --git a/src/mappings/transfers.ts b/src/mappings/transfers.ts index 5c3c369..0d37d2c 100644 --- a/src/mappings/transfers.ts +++ b/src/mappings/transfers.ts @@ -7,6 +7,7 @@ import { updateAccountAssetYTBalance, } from "../entities/AccountAsset" import { getAssetAmount } from "../entities/AssetAmount" +import { updateIBTRates } from "../entities/IBT" import { updateYieldForAll } from "../entities/Yield" import { AssetType, logWarning } from "../utils" import { generateTransferId } from "../utils/idGenerators" @@ -89,3 +90,12 @@ export function handleTransfer(event: TransferEvent): void { ]) } } + +/** @dev Handles the Transfer event for IBT tokens. + * @param event The Transfer event. + * @notice We use a separate function for IBT tokens because to limit the number of entities stored we won't store all IBT transfer entities. We will simply update the IBT entity to update its IBTRate. + * @returns void + */ +export function handleIBTTransfer(event: TransferEvent): void { + updateIBTRates(event.address, event.block.timestamp) +} diff --git a/src/tests/amm.test.ts b/src/tests/amm.test.ts index c5f3ef9..97d8305 100644 --- a/src/tests/amm.test.ts +++ b/src/tests/amm.test.ts @@ -1,4 +1,4 @@ -import { BigInt, ethereum } from "@graphprotocol/graph-ts" +import { Address, BigInt, ethereum } from "@graphprotocol/graph-ts" import { describe, test, @@ -63,8 +63,10 @@ import { POOL_PT_BALANCE_MOCK, POOL_LP_BALANCE_MOCK, LP_TOTAL_SUPPLY, + ETH_ADDRESS_MOCK, } from "./mocks/ERC20" import { + createAssetCallMock, createConvertToAssetsCallMock, createConvertToSharesCallMock, } from "./mocks/ERC4626" @@ -155,7 +157,11 @@ describe("handleAddLiquidity()", () => { IBT_ADDRESS_MOCK, toPrecision(BigInt.fromI32(10), 1, 18) ) - + createConvertToAssetsCallMock(IBT_ADDRESS_MOCK, 1) + createAssetCallMock( + IBT_ADDRESS_MOCK, + Address.fromString(ETH_ADDRESS_MOCK) + ) emitFactoryUpdated() emitFutureVaultDeployed(FIRST_FUTURE_VAULT_ADDRESS_MOCK) emiCurveFactoryChange() @@ -448,6 +454,10 @@ describe("handleRemoveLiquidity()", () => { tokenSupplyParam, ] createConvertToAssetsCallMock(IBT_ADDRESS_MOCK, 1) + createAssetCallMock( + IBT_ADDRESS_MOCK, + Address.fromString(ETH_ADDRESS_MOCK) + ) handleRemoveLiquidity(removeLiquidityEvent) }) @@ -710,7 +720,11 @@ describe("handleTokenExchange()", () => { boughtIdParam, tokensBoughtParam, ] - + createConvertToAssetsCallMock(IBT_ADDRESS_MOCK, 1) + createAssetCallMock( + IBT_ADDRESS_MOCK, + Address.fromString(ETH_ADDRESS_MOCK) + ) handleTokenExchange(tokenExchangeEvent) }) @@ -1039,7 +1053,11 @@ describe("handleRemoveLiquidityOne()", () => { coinIndexParam, coinAmountParam, ] - + createConvertToAssetsCallMock(IBT_ADDRESS_MOCK, 1) + createAssetCallMock( + IBT_ADDRESS_MOCK, + Address.fromString(ETH_ADDRESS_MOCK) + ) handleRemoveLiquidityOne(removeLiquidityOneEvent) }) @@ -1319,6 +1337,11 @@ describe("handleClaimAdminFee", () => { claimAdminFeeEvent.parameters = [adminParam, tokensParam] + createConvertToAssetsCallMock(IBT_ADDRESS_MOCK, 1) + createAssetCallMock( + IBT_ADDRESS_MOCK, + Address.fromString(ETH_ADDRESS_MOCK) + ) handleClaimAdminFee(claimAdminFeeEvent) }) diff --git a/src/tests/futureDayData.test.ts b/src/tests/futureDayData.test.ts index 4387de9..b435a24 100644 --- a/src/tests/futureDayData.test.ts +++ b/src/tests/futureDayData.test.ts @@ -1,4 +1,4 @@ -import { BigDecimal, BigInt } from "@graphprotocol/graph-ts" +import { Address, BigDecimal, BigInt } from "@graphprotocol/graph-ts" import { assert, beforeAll, clearStore, describe, test } from "matchstick-as" import { FutureDailyStats } from "../../generated/schema" @@ -15,8 +15,15 @@ import { FIRST_POOL_ADDRESS_MOCK, mockCurvePoolFunctions, } from "./mocks/CurvePool" -import { mockERC20Balances, mockERC20Functions } from "./mocks/ERC20" -import { createConvertToAssetsCallMockFromString } from "./mocks/ERC4626" +import { + ETH_ADDRESS_MOCK, + mockERC20Balances, + mockERC20Functions, +} from "./mocks/ERC20" +import { + createAssetCallMock, + createConvertToAssetsCallMock, +} from "./mocks/ERC4626" import { mockFactoryFunctions } from "./mocks/Factory" import { mockFeedRegistryInterfaceFunctions } from "./mocks/FeedRegistryInterface" import { @@ -39,6 +46,11 @@ describe("APY Computations on futureDailyStats", () => { mockFeedRegistryInterfaceFunctions() mockFactoryFunctions() mockCurvePoolFunctions() + createConvertToAssetsCallMock(IBT_ADDRESS_MOCK, 1) + createAssetCallMock( + IBT_ADDRESS_MOCK, + Address.fromString(ETH_ADDRESS_MOCK) + ) emitFactoryUpdated() emitFutureVaultDeployed(FIRST_FUTURE_VAULT_ADDRESS_MOCK) diff --git a/src/tests/futures.test.ts b/src/tests/futures.test.ts index f5542b2..0770b59 100644 --- a/src/tests/futures.test.ts +++ b/src/tests/futures.test.ts @@ -1,4 +1,4 @@ -import { BigInt, ethereum } from "@graphprotocol/graph-ts" +import { Address, BigInt, ethereum } from "@graphprotocol/graph-ts" import { assert, beforeAll, @@ -57,7 +57,10 @@ import { POOL_PT_BALANCE_MOCK, YT_BALANCE_MOCK, } from "./mocks/ERC20" -import { createConvertToAssetsCallMock } from "./mocks/ERC4626" +import { + createAssetCallMock, + createConvertToAssetsCallMock, +} from "./mocks/ERC4626" import { mockFactoryFunctions, FACTORY_ADDRESS_MOCK, @@ -121,6 +124,12 @@ describe("handleFutureVaultDeployed()", () => { mockFutureVaultFunctions() mockFeedRegistryInterfaceFunctions() + createConvertToAssetsCallMock(IBT_ADDRESS_MOCK, 1) + createAssetCallMock( + IBT_ADDRESS_MOCK, + Address.fromString(ETH_ADDRESS_MOCK) + ) + emitFactoryUpdated() emitFutureVaultDeployed(FIRST_FUTURE_VAULT_ADDRESS_MOCK) emitFutureVaultDeployed(SECOND_FUTURE_VAULT_ADDRESS_MOCK) @@ -269,6 +278,10 @@ describe("handleUnpaused()", () => { describe("handleYieldUpdated()", () => { beforeAll(() => { createConvertToAssetsCallMock(IBT_ADDRESS_MOCK, 1) + createAssetCallMock( + IBT_ADDRESS_MOCK, + Address.fromString(ETH_ADDRESS_MOCK) + ) emitMint(0, FEE_COLLECTOR_ADDRESS_MOCK) @@ -356,6 +369,11 @@ describe("handleYieldUpdated()", () => { describe("handleFeeClaimed()", () => { test("Should create a new FeeClaim entity with properly assign future as well as fee collector entity", () => { + createConvertToAssetsCallMock(IBT_ADDRESS_MOCK, 1) + createAssetCallMock( + IBT_ADDRESS_MOCK, + Address.fromString(ETH_ADDRESS_MOCK) + ) let feeClaimedEvent = changetype(newMockEvent()) feeClaimedEvent.address = FIRST_FUTURE_VAULT_ADDRESS_MOCK @@ -429,6 +447,10 @@ describe("handleFeeClaimed()", () => { describe("handleMint()", () => { beforeAll(() => { createConvertToAssetsCallMock(IBT_ADDRESS_MOCK, 1) + createAssetCallMock( + IBT_ADDRESS_MOCK, + Address.fromString(ETH_ADDRESS_MOCK) + ) emitMint() }) @@ -612,6 +634,10 @@ describe("handleRedeem()", () => { redeemEvent.parameters = [senderParam, receiverParam, sharesParam] createConvertToAssetsCallMock(IBT_ADDRESS_MOCK, 1) + createAssetCallMock( + IBT_ADDRESS_MOCK, + Address.fromString(ETH_ADDRESS_MOCK) + ) handleRedeem(redeemEvent) }) @@ -931,7 +957,11 @@ describe("handleYieldClaimed()", () => { receiverParam, yieldInIBTParam, ] - + createConvertToAssetsCallMock(IBT_ADDRESS_MOCK, 1) + createAssetCallMock( + IBT_ADDRESS_MOCK, + Address.fromString(ETH_ADDRESS_MOCK) + ) handleYieldClaimed(yieldClaimedEvent) }) diff --git a/src/tests/mocks/ERC4626.ts b/src/tests/mocks/ERC4626.ts index 8c70923..7b290cf 100644 --- a/src/tests/mocks/ERC4626.ts +++ b/src/tests/mocks/ERC4626.ts @@ -18,26 +18,22 @@ export const createConvertToAssetsCallMock = ( ) .withArgs([ethereum.Value.fromUnsignedBigInt(BigInt.fromI32(1))]) .returns([ethereum.Value.fromI32(rate as u32)]) -} -/** - * Mock the convertToAsset function from the ERC4626 contract. Specify the mocked rate as a String - * The rate is computed for a unit of IBT - * @param addressMock The address of the ERC4626 asset - * @param rate The rate to return but the convertToAsset function - */ -export const createConvertToAssetsCallMockFromString = ( - addressMock: Address, - rate: string -): void => { - let rateBI = BigInt.fromString(rate) createMockedFunction( addressMock, "convertToAssets", "convertToAssets(uint256):(uint256)" ) - .withArgs([ethereum.Value.fromUnsignedBigInt(BigInt.fromI32(1))]) - .returns([ethereum.Value.fromSignedBigInt(rateBI)]) + .withArgs([ + ethereum.Value.fromUnsignedBigInt( + BigInt.fromI64(1000000000000000000) + ), + ]) + .returns([ + ethereum.Value.fromUnsignedBigInt( + BigInt.fromI64(1000000000000000000) + ), + ]) } export const createConvertToSharesCallMock = ( @@ -52,3 +48,12 @@ export const createConvertToSharesCallMock = ( .withArgs([ethereum.Value.fromUnsignedBigInt(rate)]) .returns([ethereum.Value.fromUnsignedBigInt(rate)]) } + +export const createAssetCallMock = ( + addressMock: Address, + asset: Address +): void => { + createMockedFunction(addressMock, "asset", "asset():(address)").returns([ + ethereum.Value.fromAddress(asset), + ]) +} diff --git a/src/tests/transfers.test.ts b/src/tests/transfers.test.ts index 32bd638..2bb4211 100644 --- a/src/tests/transfers.test.ts +++ b/src/tests/transfers.test.ts @@ -31,13 +31,17 @@ import { POOL_PT_ADDRESS_MOCK, } from "./mocks/CurvePool" import { + ETH_ADDRESS_MOCK, mockERC20Balances, mockERC20Functions, POOL_LP_BALANCE_MOCK, POOL_PT_BALANCE_MOCK, STANDARD_DECIMALS_MOCK, } from "./mocks/ERC20" -import { createConvertToAssetsCallMock } from "./mocks/ERC4626" +import { + createAssetCallMock, + createConvertToAssetsCallMock, +} from "./mocks/ERC4626" import { mockFactoryFunctions } from "./mocks/Factory" import { mockFeedRegistryInterfaceFunctions } from "./mocks/FeedRegistryInterface" import { @@ -87,6 +91,12 @@ describe("handleTransfer()", () => { mockFactoryFunctions() + createConvertToAssetsCallMock(IBT_ADDRESS_MOCK, 1) + createAssetCallMock( + IBT_ADDRESS_MOCK, + Address.fromString(ETH_ADDRESS_MOCK) + ) + mockFutureVaultFunctions() mockFeedRegistryInterfaceFunctions() mockCurvePoolFunctions() @@ -95,8 +105,6 @@ describe("handleTransfer()", () => { emitFutureVaultDeployed(FIRST_FUTURE_VAULT_ADDRESS_MOCK) emiCurveFactoryChange() emitCurvePoolDeployed(FIRST_POOL_ADDRESS_MOCK) - - createConvertToAssetsCallMock(IBT_ADDRESS_MOCK, 1) // Necessary to have YT entity to follow yield emitMint(0, SENDER_USER_MOCK) emitMint(0, RECEIVER_USER_MOCK) diff --git a/src/utils/idGenerators.ts b/src/utils/idGenerators.ts index 3b221da..befda4f 100644 --- a/src/utils/idGenerators.ts +++ b/src/utils/idGenerators.ts @@ -57,3 +57,5 @@ export const generateYieldAssetId = (principalToken: string): string => export const generateClaimedYieldAssetId = (principalToken: string): string => `${principalToken}-claimed-yield` + +export const generateIBTId = (ibtAddress: string): string => `${ibtAddress}-IBT` diff --git a/subgraph.template.yaml b/subgraph.template.yaml index 337d310..82f4609 100644 --- a/subgraph.template.yaml +++ b/subgraph.template.yaml @@ -1,4 +1,4 @@ -specVersion: 0.0.2 +specVersion: 0.0.4 description: Spectra Subgraph repository: https://github.com/perspectivefi/spectra-subgraph schema: @@ -16,7 +16,7 @@ dataSources: mapping: kind: ethereum/events language: wasm/assemblyscript - apiVersion: 0.0.5 + apiVersion: 0.0.6 entities: [ ] file: ./src/mappings/registry.ts abis: @@ -37,7 +37,7 @@ dataSources: mapping: kind: ethereum/events language: wasm/assemblyscript - apiVersion: 0.0.5 + apiVersion: 0.0.6 entities: [ ] file: ./src/mappings/futures.ts abis: @@ -74,7 +74,7 @@ dataSources: mapping: kind: ethereum/events language: wasm/assemblyscript - apiVersion: 0.0.5 + apiVersion: 0.0.6 entities: [ ] file: ./src/mappings/amm.ts abis: @@ -111,7 +111,7 @@ templates: abi: ERC20 mapping: kind: ethereum/events - apiVersion: 0.0.5 + apiVersion: 0.0.6 language: wasm/assemblyscript entities: [ ] file: ./src/mappings/transfers.ts @@ -125,6 +125,27 @@ templates: eventHandlers: - event: Transfer(indexed address,indexed address,uint256) handler: handleTransfer + - name: IBT + kind: ethereum/contract + network: {{ network }} + source: + abi: ERC4626 + mapping: + kind: ethereum/events + apiVersion: 0.0.6 + language: wasm/assemblyscript + entities: [ ] + file: ./src/mappings/transfers.ts + abis: + - name: ERC20 + file: ./node_modules/@openzeppelin/contracts/build/contracts/ERC20.json + - name: ERC4626 + file: ./node_modules/@openzeppelin/contracts/build/contracts/ERC4626.json + - name: PrincipalToken + file: ./abis/PrincipalToken.json + eventHandlers: + - event: Transfer(indexed address,indexed address,uint256) + handler: handleIBTTransfer - name: PrincipalToken kind: ethereum/contract network: {{ network }} @@ -133,7 +154,7 @@ templates: mapping: kind: ethereum/events language: wasm/assemblyscript - apiVersion: 0.0.5 + apiVersion: 0.0.6 entities: [ ] file: ./src/mappings/futures.ts abis: @@ -170,7 +191,7 @@ templates: # mapping: # kind: ethereum/events # language: wasm/assemblyscript -# apiVersion: 0.0.5 +# apiVersion: 0.0.6 # entities: [ ] # file: ./src/mappings/lpVaults.ts # abis: From 7a1ede6b47d8bb0c2ceff7d07e89691036d6af26 Mon Sep 17 00:00:00 2001 From: Jean Chambras Date: Fri, 24 May 2024 16:49:54 +0200 Subject: [PATCH 2/2] Reuse Asset entity for IBT --- schema.graphql | 24 ++++++------------------ src/entities/{IBT.ts => IBTAsset.ts} | 21 ++++++++------------- src/mappings/futures.ts | 12 +++++------- src/mappings/transfers.ts | 2 +- src/utils/idGenerators.ts | 2 -- 5 files changed, 20 insertions(+), 41 deletions(-) rename src/entities/{IBT.ts => IBTAsset.ts} (68%) diff --git a/schema.graphql b/schema.graphql index 22b4c92..e0c0336 100644 --- a/schema.graphql +++ b/schema.graphql @@ -115,23 +115,6 @@ type Factory @entity { deployedLPVaults: [LPVault!]! @derivedFrom(field: "factory") } -#################### -# IBT and Rates -#################### - -type IBT @entity { - id: ID! - chainId: Int! - address: Bytes! - createdAtTimestamp: BigInt! - ibtDetails: Asset! - "IBT to underlying asset as a regular number with no decimals" - lastIBTRate: BigDecimal! - "IBT to underlying asset as returned by convertToAssets(UNIT). The number is in underlying asset decimals" - convertToAssetsUnit: BigInt! - lastUpdateTimestamp: BigInt! -} - #################### # Assets and prices #################### @@ -160,6 +143,12 @@ type Asset @entity { ibt: Asset fytTokenDetails: FYTTokenDetails lpTokenDetails: LPTokenDetails + "in case the AssetType is IBT we give the IBT Rates" + lastIBTRate: BigDecimal + "IBT to underlying asset as returned by convertToAssets(UNIT). The number is in underlying asset decimals" + convertToAssetsUnit: BigInt + lastUpdateTimestamp: BigInt + } "AssetPrice entity to assign a price of a token to an Asset entity and its price source" @@ -246,7 +235,6 @@ type Future @entity { state: FutureState! underlyingAsset: Asset! ibtAsset: Asset! - ibt: IBT! pools: [Pool!]! @derivedFrom(field: "futureVault") diff --git a/src/entities/IBT.ts b/src/entities/IBTAsset.ts similarity index 68% rename from src/entities/IBT.ts rename to src/entities/IBTAsset.ts index 596de03..2fd4aef 100644 --- a/src/entities/IBT.ts +++ b/src/entities/IBTAsset.ts @@ -1,25 +1,23 @@ import { Address, BigInt, Bytes } from "@graphprotocol/graph-ts" -import { IBT } from "../../generated/schema" +import { Asset } from "../../generated/schema" import { AssetType } from "../utils" -import { generateIBTId } from "../utils/idGenerators" import { getAsset } from "./Asset" import { getERC20Decimals } from "./ERC20" import { getERC4626Asset, getIBTRate, getUnderlyingUnit } from "./ERC4626" import { getNetwork } from "./Network" -export function getIBT(ibtAddress: Bytes, timestamp: BigInt): IBT { - const ibtID = generateIBTId(ibtAddress.toHex()) - let ibt = IBT.load(ibtID) +export function getIBTAsset(ibtAddress: Bytes, timestamp: BigInt): Asset { + let ibt = Asset.load(ibtAddress.toString()) if (ibt) { return ibt } - ibt = createIBT(ibtAddress, timestamp) + ibt = createIBTAsset(ibtAddress, timestamp) return ibt } export function updateIBTRates(ibtAddress: Bytes, timestamp: BigInt): void { - let ibt = getIBT(ibtAddress, timestamp) + let ibt = getIBTAsset(ibtAddress, timestamp) let convertToAssets = getIBTRate(Address.fromBytes(ibtAddress)) ibt.convertToAssetsUnit = convertToAssets const UNDERLYING_UNIT = getUnderlyingUnit(Address.fromBytes(ibtAddress)) @@ -28,21 +26,18 @@ export function updateIBTRates(ibtAddress: Bytes, timestamp: BigInt): void { ibt.save() } -export function createIBT(ibtAddress: Bytes, timestamp: BigInt): IBT { - const ibtID = generateIBTId(ibtAddress.toHex()) - let ibt = new IBT(ibtID) +export function createIBTAsset(ibtAddress: Bytes, timestamp: BigInt): Asset { + let ibt = getAsset(ibtAddress.toHex(), timestamp, AssetType.IBT) ibt.chainId = getNetwork().chainId ibt.address = ibtAddress ibt.createdAtTimestamp = timestamp - let ibtDetails = getAsset(ibtAddress.toHex(), timestamp, AssetType.IBT) let convertToAssets = getIBTRate(Address.fromBytes(ibtAddress)) ibt.convertToAssetsUnit = convertToAssets const underlying = getERC4626Asset(Address.fromBytes(ibtAddress)) const underlying_decimals = getERC20Decimals(underlying) const UNDERLYING_UNIT = BigInt.fromI32(10).pow(underlying_decimals as u8) - ibt.ibtDetails = ibtDetails.id ibt.lastIBTRate = convertToAssets.divDecimal(UNDERLYING_UNIT.toBigDecimal()) ibt.lastUpdateTimestamp = timestamp ibt.save() - return ibt as IBT + return ibt as Asset } diff --git a/src/mappings/futures.ts b/src/mappings/futures.ts index b288c51..642a710 100644 --- a/src/mappings/futures.ts +++ b/src/mappings/futures.ts @@ -1,4 +1,4 @@ -import { Address, ethereum, log } from "@graphprotocol/graph-ts" +import { Address, Bytes, ethereum, log } from "@graphprotocol/graph-ts" import { CurveFactoryChange, // CurveFactoryChange, @@ -48,7 +48,7 @@ import { getTotalAssets, getYT, } from "../entities/FutureVault" -import { getIBT as getIBTEntity } from "../entities/IBT" +import { getIBTAsset } from "../entities/IBTAsset" import { getNetwork } from "../entities/Network" import { createPool } from "../entities/Pool" import { createTransaction } from "../entities/Transaction" @@ -106,17 +106,15 @@ export function handlePTDeployed(event: PTDeployed): void { underlyingAsset.save() let ibtAddress = getIBT(ptAddress) - let ibtAsset = getAsset( - ibtAddress.toHex(), - event.block.timestamp, - AssetType.IBT + let ibtAsset = getIBTAsset( + Bytes.fromHexString(ibtAddress.toHex()), + event.block.timestamp ) ibtAsset.underlying = underlyingAsset.id ibtAsset.save() newFuture.underlyingAsset = underlyingAddress.toHex() newFuture.ibtAsset = ibtAddress.toHex() - newFuture.ibt = getIBTEntity(ibtAddress, event.block.timestamp).id newFuture.yieldGenerators = [] newFuture.save() diff --git a/src/mappings/transfers.ts b/src/mappings/transfers.ts index 0d37d2c..4c8c16c 100644 --- a/src/mappings/transfers.ts +++ b/src/mappings/transfers.ts @@ -7,7 +7,7 @@ import { updateAccountAssetYTBalance, } from "../entities/AccountAsset" import { getAssetAmount } from "../entities/AssetAmount" -import { updateIBTRates } from "../entities/IBT" +import { updateIBTRates } from "../entities/IBTAsset" import { updateYieldForAll } from "../entities/Yield" import { AssetType, logWarning } from "../utils" import { generateTransferId } from "../utils/idGenerators" diff --git a/src/utils/idGenerators.ts b/src/utils/idGenerators.ts index befda4f..3b221da 100644 --- a/src/utils/idGenerators.ts +++ b/src/utils/idGenerators.ts @@ -57,5 +57,3 @@ export const generateYieldAssetId = (principalToken: string): string => export const generateClaimedYieldAssetId = (principalToken: string): string => `${principalToken}-claimed-yield` - -export const generateIBTId = (ibtAddress: string): string => `${ibtAddress}-IBT`