Skip to content

Commit

Permalink
Create an IBT entity to get more IBT Rate precision (#156)
Browse files Browse the repository at this point in the history
* Create an IBT entity to get more IBT Rate precision

* Reuse Asset entity for IBT
  • Loading branch information
jeanchambras authored May 27, 2024
1 parent 3a7721f commit 7d7b3c4
Show file tree
Hide file tree
Showing 12 changed files with 253 additions and 42 deletions.
6 changes: 6 additions & 0 deletions schema.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -143,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"
Expand Down
16 changes: 16 additions & 0 deletions src/entities/ERC20.ts
Original file line number Diff line number Diff line change
@@ -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()
Expand All @@ -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()
Expand All @@ -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()
Expand Down
38 changes: 36 additions & 2 deletions src/entities/ERC4626.ts
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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
Expand Down
43 changes: 43 additions & 0 deletions src/entities/IBTAsset.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { Address, BigInt, Bytes } from "@graphprotocol/graph-ts"

import { Asset } from "../../generated/schema"
import { AssetType } from "../utils"
import { getAsset } from "./Asset"
import { getERC20Decimals } from "./ERC20"
import { getERC4626Asset, getIBTRate, getUnderlyingUnit } from "./ERC4626"
import { getNetwork } from "./Network"

export function getIBTAsset(ibtAddress: Bytes, timestamp: BigInt): Asset {
let ibt = Asset.load(ibtAddress.toString())
if (ibt) {
return ibt
}
ibt = createIBTAsset(ibtAddress, timestamp)
return ibt
}

export function updateIBTRates(ibtAddress: Bytes, timestamp: BigInt): void {
let ibt = getIBTAsset(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 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 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.lastIBTRate = convertToAssets.divDecimal(UNDERLYING_UNIT.toBigDecimal())
ibt.lastUpdateTimestamp = timestamp
ibt.save()
return ibt as Asset
}
15 changes: 9 additions & 6 deletions src/mappings/futures.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Address, ethereum, log } from "@graphprotocol/graph-ts"
import { Address, Bytes, ethereum, log } from "@graphprotocol/graph-ts"

import {
CurveFactoryChange, // CurveFactoryChange,
Expand All @@ -15,6 +15,7 @@ import {
import {
ERC20, // LPVault as LPVaultTemplate,
PrincipalToken as PrincipalTokenTemplate,
IBT,
} from "../../generated/templates"
import {
FeeClaimed,
Expand Down Expand Up @@ -47,6 +48,7 @@ import {
getTotalAssets,
getYT,
} from "../entities/FutureVault"
import { getIBTAsset } from "../entities/IBTAsset"
import { getNetwork } from "../entities/Network"
import { createPool } from "../entities/Pool"
import { createTransaction } from "../entities/Transaction"
Expand Down Expand Up @@ -104,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.yieldGenerators = []

newFuture.save()
Expand All @@ -140,6 +140,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))

Expand Down
10 changes: 10 additions & 0 deletions src/mappings/transfers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
updateAccountAssetYTBalance,
} from "../entities/AccountAsset"
import { getAssetAmount } from "../entities/AssetAmount"
import { updateIBTRates } from "../entities/IBTAsset"
import { updateYieldForAll } from "../entities/Yield"
import { AssetType, logWarning } from "../utils"
import { generateTransferId } from "../utils/idGenerators"
Expand Down Expand Up @@ -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)
}
31 changes: 27 additions & 4 deletions src/tests/amm.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { BigInt, ethereum } from "@graphprotocol/graph-ts"
import { Address, BigInt, ethereum } from "@graphprotocol/graph-ts"
import {
describe,
test,
Expand Down Expand Up @@ -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"
Expand Down Expand Up @@ -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()
Expand Down Expand Up @@ -448,6 +454,10 @@ describe("handleRemoveLiquidity()", () => {
tokenSupplyParam,
]
createConvertToAssetsCallMock(IBT_ADDRESS_MOCK, 1)
createAssetCallMock(
IBT_ADDRESS_MOCK,
Address.fromString(ETH_ADDRESS_MOCK)
)
handleRemoveLiquidity(removeLiquidityEvent)
})

Expand Down Expand Up @@ -710,7 +720,11 @@ describe("handleTokenExchange()", () => {
boughtIdParam,
tokensBoughtParam,
]

createConvertToAssetsCallMock(IBT_ADDRESS_MOCK, 1)
createAssetCallMock(
IBT_ADDRESS_MOCK,
Address.fromString(ETH_ADDRESS_MOCK)
)
handleTokenExchange(tokenExchangeEvent)
})

Expand Down Expand Up @@ -1039,7 +1053,11 @@ describe("handleRemoveLiquidityOne()", () => {
coinIndexParam,
coinAmountParam,
]

createConvertToAssetsCallMock(IBT_ADDRESS_MOCK, 1)
createAssetCallMock(
IBT_ADDRESS_MOCK,
Address.fromString(ETH_ADDRESS_MOCK)
)
handleRemoveLiquidityOne(removeLiquidityOneEvent)
})

Expand Down Expand Up @@ -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)
})

Expand Down
18 changes: 15 additions & 3 deletions src/tests/futureDayData.test.ts
Original file line number Diff line number Diff line change
@@ -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"
Expand All @@ -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 {
Expand All @@ -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)
Expand Down
Loading

0 comments on commit 7d7b3c4

Please sign in to comment.