diff --git a/schema.graphql b/schema.graphql index 8e16b25..25dc5ad 100644 --- a/schema.graphql +++ b/schema.graphql @@ -75,10 +75,10 @@ type User { # By nature this entity saves the latest state of the token, and its state at different times should be attained from the snapshot entities type Token { id: ID! # token address - address: String! # token address + address: String! @index # token address symbol: String! # token symbol name: String! # token name - chainId: Int! + chainId: Int! @index decimals: BigInt! @config(precision: 76) # number of decimals pricePerUSDNew: BigInt! @config(precision: 76) # price of token per USD lastUpdatedTimestamp: Timestamp! # timestamp of last update diff --git a/src/EventHandlers/CLPool.ts b/src/EventHandlers/CLPool.ts index 0ec527c..2721443 100644 --- a/src/EventHandlers/CLPool.ts +++ b/src/EventHandlers/CLPool.ts @@ -464,12 +464,15 @@ CLPool.Swap.handlerWithLoader({ return null; } - const [token0Instance, token1Instance] = await Promise.all([ + const [token0Instance, token1Instance, currentTokens] = await Promise.all([ context.Token.get(liquidityPoolAggregator.token0_id), context.Token.get(liquidityPoolAggregator.token1_id), + context.Token.getWhere.chainId.eq(event.chainId) ]); - return { liquidityPoolAggregator, token0Instance, token1Instance }; + const currentTokenAddresses = currentTokens.map((token: Token) => token.address); + + return { liquidityPoolAggregator, token0Instance, token1Instance, currentTokenAddresses }; }, handler: async ({ event, context, loaderReturn }) => { const blockDatetime = new Date(event.block.timestamp * 1000); @@ -490,7 +493,7 @@ CLPool.Swap.handlerWithLoader({ context.CLPool_Swap.set(entity); if (loaderReturn && loaderReturn.liquidityPoolAggregator) { - const { liquidityPoolAggregator, token0Instance, token1Instance } = loaderReturn; + const { liquidityPoolAggregator, token0Instance, token1Instance, currentTokenAddresses } = loaderReturn; let token0 = token0Instance; let token1 = token1Instance; @@ -508,7 +511,7 @@ CLPool.Swap.handlerWithLoader({ if (token0) { try { - token0 = await refreshTokenPrice(token0, event.block.number, event.block.timestamp, event.chainId, context); + token0 = await refreshTokenPrice(token0, currentTokenAddresses, event.block.number, event.block.timestamp, event.chainId, context); } catch (error) { context.log.error(`Error refreshing token price for ${token0?.address} on chain ${event.chainId}: ${error}`); } @@ -524,7 +527,7 @@ CLPool.Swap.handlerWithLoader({ if (token1) { try { - token1 = await refreshTokenPrice(token1, event.block.number, event.block.timestamp, event.chainId, context); + token1 = await refreshTokenPrice(token1, currentTokenAddresses, event.block.number, event.block.timestamp, event.chainId, context); } catch (error) { context.log.error(`Error refreshing token price for ${token1?.address} on chain ${event.chainId}: ${error}`); } diff --git a/src/EventHandlers/Pool.ts b/src/EventHandlers/Pool.ts index 1100fbf..fc5acff 100644 --- a/src/EventHandlers/Pool.ts +++ b/src/EventHandlers/Pool.ts @@ -1,6 +1,6 @@ import { Pool, Pool_Swap, Pool_Sync, Pool_Mint, Pool_Burn } from "generated"; -import { LiquidityPoolAggregator, User } from "./../src/Types.gen"; +import { LiquidityPoolAggregator, Token, User } from "./../src/Types.gen"; import { normalizeTokenAmountTo1e18 } from "./../Helpers"; import { abs, multiplyBase1e18 } from "./../Maths"; import { refreshTokenPrice } from "../PriceOracle"; @@ -125,21 +125,24 @@ Pool.Swap.handlerWithLoader({ return null; } - const [token0Instance, token1Instance, user, isLiquidityPool] = + const [token0Instance, token1Instance, user, isLiquidityPool, currentTokens] = await Promise.all([ context.Token.get(liquidityPoolAggregator.token0_id), context.Token.get(liquidityPoolAggregator.token1_id), context.User.get(event.params.to), context.LiquidityPoolAggregator.get(event.params.to), + context.Token.getWhere.chainId.eq(event.chainId) ]); + const currentTokenAddresses = currentTokens.map((token: Token) => token.address); + return { liquidityPoolAggregator, token0Instance, token1Instance, to_address: event.params.to, user, - isLiquidityPool: isLiquidityPool != undefined, + currentTokenAddresses, }; }, handler: async ({ event, context, loaderReturn }) => { @@ -160,7 +163,7 @@ Pool.Swap.handlerWithLoader({ context.Pool_Swap.set(entity); if (loaderReturn) { - const { liquidityPoolAggregator, token0Instance, token1Instance, to_address, user } = + const { liquidityPoolAggregator, token0Instance, token1Instance, to_address, user, currentTokenAddresses } = loaderReturn; let token0 = token0Instance; @@ -177,7 +180,7 @@ Pool.Swap.handlerWithLoader({ tokenUpdateData.netAmount0 = event.params.amount0In + event.params.amount0Out; if (token0) { try { - token0 = await refreshTokenPrice(token0, event.block.number, event.block.timestamp, event.chainId, context); + token0 = await refreshTokenPrice(token0, currentTokenAddresses, event.block.number, event.block.timestamp, event.chainId, context); } catch (error) { context.log.error(`Error refreshing token price for ${token0?.address} on chain ${event.chainId}: ${error}`); } @@ -194,7 +197,7 @@ Pool.Swap.handlerWithLoader({ tokenUpdateData.netAmount1 = event.params.amount1In + event.params.amount1Out; if (token1) { try { - token1 = await refreshTokenPrice(token1, event.block.number, event.block.timestamp, event.chainId, context); + token1 = await refreshTokenPrice(token1, currentTokenAddresses, event.block.number, event.block.timestamp, event.chainId, context); } catch (error) { context.log.error(`Error refreshing token price for ${token1?.address} on chain ${event.chainId}: ${error}`); } diff --git a/src/EventHandlers/VotingReward.ts b/src/EventHandlers/VotingReward.ts index e796476..2a27008 100644 --- a/src/EventHandlers/VotingReward.ts +++ b/src/EventHandlers/VotingReward.ts @@ -5,7 +5,7 @@ import { VotingReward_NotifyReward, } from "generated"; -import { LiquidityPoolAggregator } from "./../src/Types.gen"; +import { LiquidityPoolAggregator, Token } from "./../src/Types.gen"; import { normalizeTokenAmountTo1e18 } from "./../Helpers"; import { multiplyBase1e18 } from "./../Maths"; import { poolLookupStoreManager } from "./../Store"; @@ -33,29 +33,15 @@ VotingReward.NotifyReward.handlerWithLoader({ ); } - let rewardToken: TokenPriceData | null = null; - - const [currentLiquidityPool, storedToken] = await Promise.all([ + const [currentLiquidityPool, storedToken, currentTokens] = await Promise.all([ promisePool, context.Token.get(TokenIdByChain(event.params.reward, event.chainId)), + context.Token.getWhere.chainId.eq(event.chainId) ]); - if (!storedToken) { - try { - const rewardTokenDetails = await getTokenPriceData(event.params.reward, event.block.number, event.chainId); - rewardToken = rewardTokenDetails; - } catch (error) { - context.log.error(`Error in voting reward notify reward event fetching token details` + - ` for ${event.params.reward} on chain ${event.chainId}: ${error}`); - } - } else { - rewardToken = { - pricePerUSDNew: storedToken.pricePerUSDNew, - decimals: storedToken.decimals - } - } + const currentTokenAddresses = currentTokens.map((token: Token) => token.address); - return { currentLiquidityPool, rewardToken }; + return { currentLiquidityPool, storedToken, currentTokenAddresses }; }, handler: async ({ event, context, loaderReturn }) => { const entity: VotingReward_NotifyReward = { @@ -73,7 +59,23 @@ VotingReward.NotifyReward.handlerWithLoader({ context.VotingReward_NotifyReward.set(entity); if (loaderReturn) { - const { currentLiquidityPool, rewardToken } = loaderReturn; + const { currentLiquidityPool, storedToken, currentTokenAddresses } = loaderReturn; + + let rewardToken: TokenPriceData | null = null; + + if (!storedToken) { + try { + rewardToken = await getTokenPriceData(event.params.reward, currentTokenAddresses, event.block.number, event.chainId); + } catch (error) { + context.log.error(`Error in voting reward notify reward event fetching token details` + + ` for ${event.params.reward} on chain ${event.chainId}: ${error}`); + } + } else { + rewardToken = { + pricePerUSDNew: storedToken.pricePerUSDNew, + decimals: storedToken.decimals + } + } if (currentLiquidityPool && rewardToken) { let normalizedBribesAmount = normalizeTokenAmountTo1e18( diff --git a/src/PriceOracle.ts b/src/PriceOracle.ts index 7e4c428..7caea94 100644 --- a/src/PriceOracle.ts +++ b/src/PriceOracle.ts @@ -53,6 +53,7 @@ const ONE_HOUR_MS = 60 * 60 * 1000; // 1 hour in milliseconds */ export async function refreshTokenPrice( token: Token, + connectors: string[], blockNumber: number, blockTimestamp: number, chainId: number, @@ -62,7 +63,7 @@ export async function refreshTokenPrice( return token; } - const tokenPriceData = await getTokenPriceData(token.address, blockNumber, chainId); + const tokenPriceData = await getTokenPriceData(token.address, connectors, blockNumber, chainId); const updatedToken: Token = { ...token, pricePerUSDNew: tokenPriceData.pricePerUSDNew, @@ -89,6 +90,7 @@ export async function refreshTokenPrice( */ export async function getTokenPriceData( tokenAddress: string, + connectors: string[], blockNumber: number, chainId: number ): Promise { @@ -104,7 +106,10 @@ export async function getTokenPriceData( let tokenDecimals: bigint = 0n; try { - const prices = await read_prices([tokenAddress, WETH_ADDRESS, USDC_ADDRESS], chainId, blockNumber); + const connectorList = connectors.filter((connector) => connector !== tokenAddress) + .filter((connector) => connector !== WETH_ADDRESS) + .filter((connector) => connector !== USDC_ADDRESS); + const prices = await read_prices([tokenAddress, ...connectorList, WETH_ADDRESS, USDC_ADDRESS], chainId, blockNumber); tokenPrice = BigInt(prices[0]); tokenDecimals = BigInt(tokenDetails.decimals); } catch (error) { diff --git a/test/PriceOracle.test.ts b/test/PriceOracle.test.ts index f8792e5..0cbd300 100644 --- a/test/PriceOracle.test.ts +++ b/test/PriceOracle.test.ts @@ -69,7 +69,7 @@ describe("PriceOracle", () => { ...mockToken0Data, lastUpdatedTimestamp: testLastUpdated }; - await PriceOracle.refreshTokenPrice(fetchedToken, blockNumber, blockDatetime.getTime(), chainId, mockContext); + await PriceOracle.refreshTokenPrice(fetchedToken, [], blockNumber, blockDatetime.getTime(), chainId, mockContext); }); it("should not update prices if the update interval hasn't passed", async () => { expect(mockContract.called).to.be.false; @@ -84,7 +84,7 @@ describe("PriceOracle", () => { ...mockToken0Data, lastUpdatedTimestamp: testLastUpdated }; - await PriceOracle.refreshTokenPrice(fetchedToken, blockNumber, blockDatetime.getTime(), chainId, mockContext); + await PriceOracle.refreshTokenPrice(fetchedToken, [], blockNumber, blockDatetime.getTime(), chainId, mockContext); updatedToken = mockContext.Token.set.lastCall.args[0]; }); it("should update prices if the update interval has passed", async () => {