diff --git a/CHANGELOG.md b/CHANGELOG.md index c3b3165b6..e9cf291b6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,14 @@ # backend +## 1.26.27 + +### Patch Changes + +- 7a0ae59: adding dynamic swap fee to fx pools +- a16406b: balance tables indexes +- 5011ef2: optimise multichain events query +- 580c5e7: db indexes + ## 1.26.26 ### Patch Changes diff --git a/apps/api/gql/resolvers/pool.resolvers.ts b/apps/api/gql/resolvers/pool.resolvers.ts index 249eaa7e2..4c881dbea 100644 --- a/apps/api/gql/resolvers/pool.resolvers.ts +++ b/apps/api/gql/resolvers/pool.resolvers.ts @@ -8,6 +8,7 @@ import { EventsQueryController, SnapshotsController, PoolController, + FXPoolsController, } from '../../../../modules/controllers'; import { chainIdToChain } from '../../../../modules/network/chain-id-to-chain'; @@ -207,6 +208,23 @@ const balancerResolvers: Resolvers = { } } + return result; + }, + poolSyncFxQuoteTokens: async (parent, { chains }, context) => { + isAdminRoute(context); + + const result: { type: string; chain: GqlChain; success: boolean; error: string | undefined }[] = []; + + for (const chain of chains) { + try { + await FXPoolsController().syncQuoteTokens(chain); + result.push({ type: 'fx', chain, success: true, error: undefined }); + } catch (e) { + result.push({ type: 'fx', chain, success: false, error: `${e}` }); + console.log(`Could not sync fx quote tokens for chain ${chain}: ${e}`); + } + } + return result; }, }, diff --git a/apps/api/gql/schema/pool.gql b/apps/api/gql/schema/pool.gql index a530cf0db..7e638f454 100644 --- a/apps/api/gql/schema/pool.gql +++ b/apps/api/gql/schema/pool.gql @@ -90,6 +90,7 @@ extend type Mutation { poolLoadOnChainDataForAllPools(chains: [GqlChain!]!): [GqlPoolMutationResult!]! poolReloadPools(chains: [GqlChain!]!): [GqlPoolMutationResult!]! poolSyncAllCowSnapshots(chains: [GqlChain!]!): [GqlPoolMutationResult!]! + poolSyncFxQuoteTokens(chains: [GqlChain!]!): [GqlPoolMutationResult!]! } """ diff --git a/graphql_schema_generated.ts b/graphql_schema_generated.ts index 4fe5c7214..1f8038f35 100644 --- a/graphql_schema_generated.ts +++ b/graphql_schema_generated.ts @@ -3684,6 +3684,7 @@ export const schema = gql` poolReloadStakingForAllPools(stakingTypes: [GqlPoolStakingType!]!): String! poolSyncAllCowSnapshots(chains: [GqlChain!]!): [GqlPoolMutationResult!]! poolSyncAllPoolsFromSubgraph: [String!]! + poolSyncFxQuoteTokens(chains: [GqlChain!]!): [GqlPoolMutationResult!]! poolUpdateLifetimeValuesForAllPools: String! poolUpdateLiquidityValuesForAllPools: String! protocolCacheMetrics: String! diff --git a/modules/actions/pool/v2/add-pools.ts b/modules/actions/pool/v2/add-pools.ts index fb9c9da50..763af6286 100644 --- a/modules/actions/pool/v2/add-pools.ts +++ b/modules/actions/pool/v2/add-pools.ts @@ -1,4 +1,4 @@ -import { Chain } from '@prisma/client'; +import { Chain, PrismaPool } from '@prisma/client'; import { prisma } from '../../../../prisma/prisma-client'; import { nestedPoolWithSingleLayerNesting } from '../../../../prisma/prisma-types'; import { V2SubgraphClient } from '../../../subgraphs/balancer-subgraph'; @@ -6,6 +6,7 @@ import { BalancerPoolFragment } from '../../../subgraphs/balancer-subgraph/gener import { subgraphToPrismaCreate } from '../../../pool/subgraph-mapper'; import { upsertBptBalancesV2 } from '../../user/upsert-bpt-balances-v2'; import _ from 'lodash'; +import { syncPoolTypeOnchainData } from './sync-pool-type-onchain-data'; export const addPools = async (subgraphService: V2SubgraphClient, chain: Chain): Promise => { const { block } = await subgraphService.legacyService.getMetadata(); @@ -25,9 +26,13 @@ export const addPools = async (subgraphService: V2SubgraphClient, chain: Chain): const createdPools: string[] = []; for (const subgraphPool of newPools) { - const created = await createPoolRecord(subgraphPool, chain, block.number, allNestedTypePools); - if (created) { + const dbPool = await createPoolRecord(subgraphPool, chain, block.number, allNestedTypePools); + if (dbPool) { createdPools.push(subgraphPool.id); + // When new FX pool is added, we need to get the quote token + if (subgraphPool.poolType === 'FX') { + await syncPoolTypeOnchainData([dbPool], chain); + } } } @@ -48,7 +53,7 @@ const createPoolRecord = async ( chain: Chain, blockNumber: number, nestedPools: { id: string; address: string }[], -): Promise => { +): Promise => { const poolTokens = pool.tokens || []; await prisma.prismaToken.createMany({ @@ -74,14 +79,14 @@ const createPoolRecord = async ( const prismaPoolRecordWithAssociations = subgraphToPrismaCreate(pool, chain, blockNumber, nestedPools); try { - await prisma.prismaPool.create(prismaPoolRecordWithAssociations); + const pool = await prisma.prismaPool.create(prismaPoolRecordWithAssociations); await createAllTokensRelationshipForPool(pool.id, chain); + + return pool; } catch (e) { console.error(`Could not create pool ${pool.id} on chain ${chain}. Skipping.`, e); - return false; } - return true; }; const createAllTokensRelationshipForPool = async (poolId: string, chain: Chain): Promise => { diff --git a/modules/actions/pool/v2/sync-pool-type-onchain-data.ts b/modules/actions/pool/v2/sync-pool-type-onchain-data.ts new file mode 100644 index 000000000..84bb2e6b8 --- /dev/null +++ b/modules/actions/pool/v2/sync-pool-type-onchain-data.ts @@ -0,0 +1,64 @@ +import { Abi } from 'abitype'; +import FX from '../../../pool/abi/FxPool.json'; +import { getViemClient, ViemClient } from '../../../sources/viem-client'; +import { Chain, PrismaPoolType } from '@prisma/client'; +import { prisma } from '../../../../prisma/prisma-client'; +import { prismaBulkExecuteOperations } from '../../../../prisma/prisma-util'; + +const update = async (data: { id: string; chain: Chain; typeData: any }[]) => { + // Update the pool type data + const updates = data.map(({ id, chain, typeData }) => + prisma.prismaPool.update({ + where: { id_chain: { id, chain } }, + data: { typeData }, + }), + ); + + await prismaBulkExecuteOperations(updates, false); +}; + +export const syncPoolTypeOnchainData = async ( + pools: { id: string; chain: Chain; address: string; type: PrismaPoolType; typeData: any }[], + chain: Chain, +) => { + const viemClient = getViemClient(chain); + + // Get FX pools + const fxPools = pools.filter((pool) => pool.type === 'FX'); + const quoteTokens = await fetchFxQuoteTokens(fxPools, viemClient); + await update(quoteTokens); + + return true; +}; + +export const fetchFxQuoteTokens = async ( + pools: { id: string; chain: Chain; address: string; typeData: any }[], + viemClient: ViemClient, +) => { + // Fetch the tokens from the subgraph + const contracts = pools.map(({ address }) => { + return { + address: address as `0x${string}`, + abi: FX as Abi, + functionName: 'derivatives', + args: [1], + }; + }); + + const results = await viemClient.multicall({ contracts, allowFailure: true }); + + return results + .map((call, index) => { + // If the call failed, return null + if (call.status === 'failure') return null; + + const typeData = { ...pools[index].typeData, quoteToken: (call.result as string).toLowerCase() }; + + return { + id: pools[index].id, + chain: pools[index].chain, + typeData, + }; + }) + .filter((quoteToken): quoteToken is { id: string; chain: Chain; typeData: any } => quoteToken !== null); +}; diff --git a/modules/actions/pool/v2/sync-swaps.ts b/modules/actions/pool/v2/sync-swaps.ts index c0ea33e06..d8e84ae3c 100644 --- a/modules/actions/pool/v2/sync-swaps.ts +++ b/modules/actions/pool/v2/sync-swaps.ts @@ -36,6 +36,18 @@ export async function syncSwaps(subgraphClient: V2SubgraphClient, chain: Chain): }, }); + // Get list of FX pool addresses for the fee calculation + const fxPools = (await prisma.prismaPool.findMany({ + where: { + chain: chain, + type: 'FX', + }, + select: { + id: true, + typeData: true, // contains the quote token address + }, + })) as { id: string; typeData: { quoteToken: string } }[]; + // Querying by timestamp of Fantom, because it has events without a block number in the DB const where = latestEvent ? chain === Chain.FANTOM @@ -47,14 +59,14 @@ export async function syncSwaps(subgraphClient: V2SubgraphClient, chain: Chain): console.time('BalancerSwaps'); const { swaps } = await subgraphClient.BalancerSwaps({ first: 1000, - where, + where: where, orderBy: chain === Chain.FANTOM ? Swap_OrderBy.Timestamp : Swap_OrderBy.Block, orderDirection: OrderDirection.Asc, }); console.timeEnd('BalancerSwaps'); console.time('swapV2Transformer'); - const dbSwaps = swaps.map((swap) => swapV2Transformer(swap, chain)); + const dbSwaps = swaps.map((swap) => swapV2Transformer(swap, chain, fxPools)); console.timeEnd('swapV2Transformer'); // TODO: parse batchSwaps, if needed diff --git a/modules/controllers/event-query-controller.ts b/modules/controllers/event-query-controller.ts index b9ea8c2b4..4e7096f94 100644 --- a/modules/controllers/event-query-controller.ts +++ b/modules/controllers/event-query-controller.ts @@ -71,6 +71,48 @@ const rangeToTimestamp = (range: GqlPoolEventsDataRange): number => { } }; +const getMultichainEvents = async (chainIn: Chain[]) => { + const results = await Promise.all( + chainIn.map(async (chain) => { + return ( + await prisma.prismaPoolEvent.findMany({ + where: { + chain, + }, + take: 100, + orderBy: [ + { + blockTimestamp: 'desc', + }, + { + blockNumber: 'desc', + }, + { + logIndex: 'desc', + }, + ], + }) + ).map((event) => + event.type === 'SWAP' && (event as SwapEvent).payload?.surplus + ? parseCowAmmSwap(event as SwapEvent) + : event.type === 'SWAP' + ? parseSwap(event as SwapEvent) + : parseJoinExit(event as JoinExitEvent), + ); + }), + ); + + return results.flat().sort((a, b) => { + if (a.blockTimestamp === b.blockTimestamp) { + if (a.blockNumber === b.blockNumber) { + return a.logIndex - b.logIndex; + } + return a.blockNumber - b.blockNumber; + } + return a.blockTimestamp - b.blockTimestamp; + }); +}; + export function EventsQueryController(tracer?: any) { return { /** @@ -93,6 +135,11 @@ export function EventsQueryController(tracer?: any) { const conditions: Prisma.PrismaPoolEventWhereInput = {}; + // Table is partitioned by chain, so querying by many chains is extermenly inefficient. + if (chainIn && chainIn.length > 1) { + return getMultichainEvents(chainIn as Chain[]); + } + if (chainIn && chainIn.length) { conditions.chain = { in: chainIn as Chain[], diff --git a/modules/controllers/fx-pools-controller.ts b/modules/controllers/fx-pools-controller.ts index 64a95346c..2d15a46e5 100644 --- a/modules/controllers/fx-pools-controller.ts +++ b/modules/controllers/fx-pools-controller.ts @@ -1,4 +1,6 @@ import config from '../../config'; +import { prisma } from '../../prisma/prisma-client'; +import { syncPoolTypeOnchainData } from '../actions/pool/v2/sync-pool-type-onchain-data'; import { syncLatestFXPrices } from '../token/latest-fx-price'; import { Chain } from '@prisma/client'; @@ -11,5 +13,12 @@ export function FXPoolsController() { return syncLatestFXPrices(balancer, chain); }, + async syncQuoteTokens(chain: Chain) { + const pools = await prisma.prismaPool.findMany({ + where: { chain, type: 'FX' }, + }); + + return syncPoolTypeOnchainData(pools, chain); + }, }; } diff --git a/modules/sources/enrichers/swaps-usd.ts b/modules/sources/enrichers/swaps-usd.ts index 095e72fc6..d5ebc2d89 100644 --- a/modules/sources/enrichers/swaps-usd.ts +++ b/modules/sources/enrichers/swaps-usd.ts @@ -40,11 +40,12 @@ export async function swapsUsd(swaps: SwapEvent[], chain: Chain): Promise price.tokenAddress === swap.payload.tokenOut.address); const feeToken = tokenPrices.find((price) => price.tokenAddress === swap.payload.fee.address); const surplusToken = tokenPrices.find((price) => price.tokenAddress === swap.payload.surplus?.address); + const feeValueUSD = parseFloat(swap.payload.fee.amount) * (feeToken?.price || 0); const payload = { fee: { ...swap.payload.fee, - valueUSD: String((feeToken?.price || 0) * parseFloat(swap.payload.fee.amount)), + valueUSD: String(feeValueUSD > 0 ? feeValueUSD : swap.payload.fee.valueUSD), }, tokenIn: { ...swap.payload.tokenIn, diff --git a/modules/sources/transformers/swap-v2-transformer.ts b/modules/sources/transformers/swap-v2-transformer.ts index 8d680fdd9..3974895e6 100644 --- a/modules/sources/transformers/swap-v2-transformer.ts +++ b/modules/sources/transformers/swap-v2-transformer.ts @@ -10,14 +10,53 @@ import { SwapEvent } from '../../../prisma/prisma-types'; * @param chain * @returns */ -export function swapV2Transformer(swap: BalancerSwapFragment, chain: Chain): SwapEvent { +export function swapV2Transformer( + swap: BalancerSwapFragment, + chain: Chain, + fxPools: { id: string; typeData: { quoteToken: string } }[] = [], +): SwapEvent { // Avoiding scientific notation const feeFloat = parseFloat(swap.tokenAmountIn) * parseFloat(swap.poolId.swapFee ?? 0); - const fee = feeFloat < 1e6 ? feeFloat.toFixed(18).replace(/0+$/, '').replace(/\.$/, '') : String(feeFloat); - const feeFloatUSD = parseFloat(swap.valueUSD) * parseFloat(swap.poolId.swapFee ?? 0); - const feeUSD = + let fee = feeFloat < 1e6 ? feeFloat.toFixed(18).replace(/0+$/, '').replace(/\.$/, '') : String(feeFloat); + let feeFloatUSD = parseFloat(swap.valueUSD) * parseFloat(swap.poolId.swapFee ?? 0); + let feeUSD = feeFloatUSD < 1e6 ? feeFloatUSD.toFixed(18).replace(/0+$/, '').replace(/\.$/, '') : String(feeFloatUSD); + // FX pools have a different fee calculation + // Replica of the subgraph logic: + // https://github.com/balancer/balancer-subgraph-v2/blob/60453224453bd07a0a3a22a8ad6cc26e65fd809f/src/mappings/vault.ts#L551-L564 + if (swap.poolId.poolType === 'FX') { + // Find the pool that has the quote token + const fxPool = fxPools.find((pool) => pool.id === swap.poolId.id); + if (fxPool && [swap.tokenOut, swap.tokenIn].includes(fxPool.typeData.quoteToken)) { + const quoteTokenAddress = fxPool.typeData.quoteToken; + const baseTokenAddress = swap.tokenIn === quoteTokenAddress ? swap.tokenOut : swap.tokenIn; + let isTokenInBase = swap.tokenOut === quoteTokenAddress; + let baseToken = swap.poolId.tokens?.find(({ token }) => token.address == baseTokenAddress); + let quoteToken = swap.poolId.tokens?.find(({ token }) => token.address == quoteTokenAddress); + let baseRate = baseToken != null ? baseToken.token.latestFXPrice : null; + let quoteRate = quoteToken != null ? quoteToken.token.latestFXPrice : null; + + if (baseRate && quoteRate) { + if (isTokenInBase) { + feeFloatUSD += + parseFloat(swap.tokenAmountIn) * parseFloat(baseRate) - + parseFloat(swap.tokenAmountOut) * parseFloat(quoteRate); + // Need to set the fee in the tokenIn price, because it's later recalculated based on the DB prices + fee = String(feeFloatUSD / parseFloat(baseRate)); // fee / tokenIn price + } else { + feeFloatUSD += + parseFloat(swap.tokenAmountIn) * parseFloat(quoteRate) - + parseFloat(swap.tokenAmountOut) * parseFloat(baseRate); + // Need to set the fee in the tokenIn price, because it's later recalculated based on the DB prices + fee = String(feeFloatUSD / parseFloat(quoteRate)); // fee / tokenIn price + } + } + + feeUSD = String(feeFloatUSD); + } + } + return { id: swap.id, // tx + logIndex tx: swap.tx, diff --git a/modules/subgraphs/balancer-subgraph/balancer-subgraph-queries.graphql b/modules/subgraphs/balancer-subgraph/balancer-subgraph-queries.graphql index 158a15ee9..6b051ada9 100644 --- a/modules/subgraphs/balancer-subgraph/balancer-subgraph-queries.graphql +++ b/modules/subgraphs/balancer-subgraph/balancer-subgraph-queries.graphql @@ -481,6 +481,13 @@ fragment BalancerSwap on Swap { poolId { id swapFee + poolType + tokens { + token { + address + latestFXPrice + } + } } userAddress { id diff --git a/modules/subgraphs/balancer-subgraph/generated/balancer-subgraph-types.ts b/modules/subgraphs/balancer-subgraph/generated/balancer-subgraph-types.ts index c54a116ce..17182c371 100644 --- a/modules/subgraphs/balancer-subgraph/generated/balancer-subgraph-types.ts +++ b/modules/subgraphs/balancer-subgraph/generated/balancer-subgraph-types.ts @@ -6744,7 +6744,19 @@ export type BalancerSwapsQuery = { tx: string; valueUSD: string; block?: string | null | undefined; - poolId: { __typename?: 'Pool'; id: string; swapFee: string }; + poolId: { + __typename?: 'Pool'; + id: string; + swapFee: string; + poolType?: string | null | undefined; + tokens?: + | Array<{ + __typename?: 'PoolToken'; + token: { __typename?: 'Token'; address: string; latestFXPrice?: string | null | undefined }; + }> + | null + | undefined; + }; userAddress: { __typename?: 'User'; id: string }; }>; }; @@ -6763,7 +6775,19 @@ export type BalancerSwapFragment = { tx: string; valueUSD: string; block?: string | null | undefined; - poolId: { __typename?: 'Pool'; id: string; swapFee: string }; + poolId: { + __typename?: 'Pool'; + id: string; + swapFee: string; + poolType?: string | null | undefined; + tokens?: + | Array<{ + __typename?: 'PoolToken'; + token: { __typename?: 'Token'; address: string; latestFXPrice?: string | null | undefined }; + }> + | null + | undefined; + }; userAddress: { __typename?: 'User'; id: string }; }; @@ -7092,6 +7116,13 @@ export const BalancerSwapFragmentDoc = gql` poolId { id swapFee + poolType + tokens { + token { + address + latestFXPrice + } + } } userAddress { id diff --git a/package.json b/package.json index dcaeec39a..a839c1bf6 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "backend", - "version": "1.26.26", + "version": "1.26.27", "description": "Backend service for Beethoven X and Balancer", "repository": "https://github.com/balancer/backend", "author": "Beethoven X", diff --git a/prisma/migrations/20241221104434_indexes/migration.sql b/prisma/migrations/20241221104434_indexes/migration.sql new file mode 100644 index 000000000..e28688a1c --- /dev/null +++ b/prisma/migrations/20241221104434_indexes/migration.sql @@ -0,0 +1,47 @@ +-- DropIndex +DROP INDEX "PrismaTokenPrice_chain_idx"; + +-- DropIndex +DROP INDEX "PrismaTokenPrice_timestamp_idx"; + +-- DropIndex +DROP INDEX "PrismaTokenPrice_tokenAddress_idx"; + +-- CreateIndex +CREATE INDEX "PrismaPool_id_chain_idx" ON "PrismaPool"("id", "chain"); + +-- CreateIndex +CREATE INDEX "PrismaPoolAprItem_poolId_chain_idx" ON "PrismaPoolAprItem"("poolId", "chain"); + +-- CreateIndex +CREATE INDEX "PrismaPoolExpandedTokens_tokenAddress_chain_idx" ON "PrismaPoolExpandedTokens"("tokenAddress", "chain"); + +-- CreateIndex +CREATE INDEX "PrismaPoolStaking_poolId_chain_idx" ON "PrismaPoolStaking"("poolId", "chain"); + +-- CreateIndex +CREATE INDEX "PrismaPoolStakingGaugeReward_gaugeId_chain_idx" ON "PrismaPoolStakingGaugeReward"("gaugeId", "chain"); + +-- CreateIndex +CREATE INDEX "PrismaPoolStakingMasterChefFarmRewarder_farmId_chain_idx" ON "PrismaPoolStakingMasterChefFarmRewarder"("farmId", "chain"); + +-- CreateIndex +CREATE INDEX "PrismaPoolStakingReliquaryFarmLevel_farmId_chain_idx" ON "PrismaPoolStakingReliquaryFarmLevel"("farmId", "chain"); + +-- CreateIndex +CREATE INDEX "PrismaPoolSwap_poolId_chain_idx" ON "PrismaPoolSwap"("poolId", "chain"); + +-- CreateIndex +CREATE INDEX "PrismaReliquaryFarmSnapshot_farmId_chain_idx" ON "PrismaReliquaryFarmSnapshot"("farmId", "chain"); + +-- CreateIndex +CREATE INDEX "PrismaReliquaryLevelSnapshot_farmSnapshotId_chain_idx" ON "PrismaReliquaryLevelSnapshot"("farmSnapshotId", "chain"); + +-- CreateIndex +CREATE INDEX "PrismaReliquaryTokenBalanceSnapshot_farmSnapshotId_chain_idx" ON "PrismaReliquaryTokenBalanceSnapshot"("farmSnapshotId", "chain"); + +-- CreateIndex +CREATE INDEX "PrismaTokenPrice_timestamp_chain_idx" ON "PrismaTokenPrice"("timestamp", "chain"); + +-- CreateIndex +CREATE INDEX "PrismaTokenPrice_tokenAddress_chain_idx" ON "PrismaTokenPrice"("tokenAddress", "chain"); diff --git a/prisma/migrations/20241221134015_balance_indexes/migration.sql b/prisma/migrations/20241221134015_balance_indexes/migration.sql new file mode 100644 index 000000000..4bd044d8c --- /dev/null +++ b/prisma/migrations/20241221134015_balance_indexes/migration.sql @@ -0,0 +1,8 @@ +-- CreateIndex +CREATE INDEX "PrismaUserStakedBalance_poolId_chain_idx" ON "PrismaUserStakedBalance"("poolId", "chain"); + +-- CreateIndex +CREATE INDEX "PrismaUserStakedBalance_stakingId_chain_idx" ON "PrismaUserStakedBalance"("stakingId", "chain"); + +-- CreateIndex +CREATE INDEX "PrismaUserWalletBalance_poolId_chain_idx" ON "PrismaUserWalletBalance"("poolId", "chain"); diff --git a/prisma/schema.prisma b/prisma/schema.prisma index cc401504c..3ac87f9b7 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -105,6 +105,7 @@ model PrismaPool { hook Json? + @@index([id, chain]) @@index([categories(ops: ArrayOps)], type: Gin) } @@ -244,6 +245,8 @@ model PrismaPoolSwap { batchSwapId String? batchSwap PrismaPoolBatchSwap? @relation(fields:[batchSwapId, chain], references: [id, chain]) batchSwapIdx Int? + + @@index([poolId, chain]) } model PrismaPoolBatchSwap { @@ -280,6 +283,8 @@ model PrismaPoolAprItem { type PrismaPoolAprType? group PrismaPoolAprItemGroup? + + @@index([poolId, chain]) } model PrismaPoolAprRange { @@ -342,6 +347,8 @@ model PrismaPoolExpandedTokens { nestedPoolId String? nestedPool PrismaPool? @relation(name: "NestedPoolForAllToken", fields:[nestedPoolId, chain], references: [id, chain], onDelete: Cascade) + + @@index([tokenAddress, chain]) } @@ -382,6 +389,8 @@ model PrismaPoolStaking { vebal PrismaPoolStakingVebal? userStakedBalances PrismaUserStakedBalance[] + + @@index([poolId, chain]) } @@ -417,6 +426,8 @@ model PrismaPoolStakingMasterChefFarmRewarder { address String tokenAddress String rewardPerSecond String + + @@index([farmId, chain]) } model PrismaPoolStakingGauge { @@ -482,6 +493,8 @@ model PrismaPoolStakingGaugeReward{ tokenAddress String rewardPerSecond String isVeBalemissions Boolean @default(false) + + @@index([gaugeId, chain]) } @@ -516,6 +529,8 @@ model PrismaPoolStakingReliquaryFarmLevel { requiredMaturity Int allocationPoints Int apr Float + + @@index([farmId, chain]) } @@ -575,6 +590,8 @@ model PrismaReliquaryFarmSnapshot { totalLiquidity String @default("0") levelBalances PrismaReliquaryLevelSnapshot[] tokenBalances PrismaReliquaryTokenBalanceSnapshot[] + + @@index([farmId, chain]) } model PrismaReliquaryLevelSnapshot { @@ -587,6 +604,8 @@ model PrismaReliquaryLevelSnapshot { level String balance String + + @@index([farmSnapshotId, chain]) } model PrismaReliquaryTokenBalanceSnapshot { @@ -602,6 +621,8 @@ model PrismaReliquaryTokenBalanceSnapshot { name String decimals Int balance String + + @@index([farmSnapshotId, chain]) } enum PoolEventType { @@ -803,9 +824,8 @@ model PrismaTokenPrice { open Float close Float - @@index(timestamp) - @@index(chain) - @@index(tokenAddress) + @@index([timestamp, chain]) + @@index([tokenAddress, chain]) } model PrismaTokenDynamicData { @@ -922,6 +942,8 @@ model PrismaUserWalletBalance { tokenAddress String token PrismaToken @relation(fields:[tokenAddress, chain], references: [address, chain], onDelete: Cascade) + + @@index([poolId, chain]) } @@ -946,6 +968,9 @@ model PrismaUserStakedBalance { stakingId String staking PrismaPoolStaking @relation(fields:[stakingId, chain], references: [id, chain], onDelete: Cascade) + + @@index([poolId, chain]) + @@index([stakingId, chain]) } model PrismaUserBalanceSyncStatus { diff --git a/prisma/schema/pool.prisma b/prisma/schema/pool.prisma index 0158e7c1d..26081f4d6 100644 --- a/prisma/schema/pool.prisma +++ b/prisma/schema/pool.prisma @@ -45,6 +45,7 @@ model PrismaPool { hook Json? + @@index([id, chain]) @@index([categories(ops: ArrayOps)], type: Gin) } @@ -184,6 +185,8 @@ model PrismaPoolSwap { batchSwapId String? batchSwap PrismaPoolBatchSwap? @relation(fields:[batchSwapId, chain], references: [id, chain]) batchSwapIdx Int? + + @@index([poolId, chain]) } model PrismaPoolBatchSwap { @@ -220,6 +223,8 @@ model PrismaPoolAprItem { type PrismaPoolAprType? group PrismaPoolAprItemGroup? + + @@index([poolId, chain]) } model PrismaPoolAprRange { @@ -282,6 +287,8 @@ model PrismaPoolExpandedTokens { nestedPoolId String? nestedPool PrismaPool? @relation(name: "NestedPoolForAllToken", fields:[nestedPoolId, chain], references: [id, chain], onDelete: Cascade) + + @@index([tokenAddress, chain]) } @@ -322,6 +329,8 @@ model PrismaPoolStaking { vebal PrismaPoolStakingVebal? userStakedBalances PrismaUserStakedBalance[] + + @@index([poolId, chain]) } @@ -357,6 +366,8 @@ model PrismaPoolStakingMasterChefFarmRewarder { address String tokenAddress String rewardPerSecond String + + @@index([farmId, chain]) } model PrismaPoolStakingGauge { @@ -422,6 +433,8 @@ model PrismaPoolStakingGaugeReward{ tokenAddress String rewardPerSecond String isVeBalemissions Boolean @default(false) + + @@index([gaugeId, chain]) } @@ -456,6 +469,8 @@ model PrismaPoolStakingReliquaryFarmLevel { requiredMaturity Int allocationPoints Int apr Float + + @@index([farmId, chain]) } @@ -493,9 +508,8 @@ model PrismaPoolSnapshot { totalProtocolYieldFees String[] @default([]) // Used for V3 snapshots dailyProtocolYieldFees String[] @default([]) // Used for V3 snapshots - @@index([protocolVersion]) - @@index([timestamp(sort: Desc)]) @@index([poolId, chain]) + @@index([chain, timestamp(sort: Desc)]) } model PrismaReliquaryFarmSnapshot { @@ -515,6 +529,8 @@ model PrismaReliquaryFarmSnapshot { totalLiquidity String @default("0") levelBalances PrismaReliquaryLevelSnapshot[] tokenBalances PrismaReliquaryTokenBalanceSnapshot[] + + @@index([farmId, chain]) } model PrismaReliquaryLevelSnapshot { @@ -527,6 +543,8 @@ model PrismaReliquaryLevelSnapshot { level String balance String + + @@index([farmSnapshotId, chain]) } model PrismaReliquaryTokenBalanceSnapshot { @@ -542,6 +560,8 @@ model PrismaReliquaryTokenBalanceSnapshot { name String decimals Int balance String + + @@index([farmSnapshotId, chain]) } enum PoolEventType { diff --git a/prisma/schema/token.prisma b/prisma/schema/token.prisma index c9b855e60..8466fc6e3 100644 --- a/prisma/schema/token.prisma +++ b/prisma/schema/token.prisma @@ -71,9 +71,8 @@ model PrismaTokenPrice { open Float close Float - @@index(timestamp) - @@index(chain) - @@index(tokenAddress) + @@index([timestamp, chain]) + @@index([tokenAddress, chain]) } model PrismaTokenDynamicData { diff --git a/prisma/schema/user.prisma b/prisma/schema/user.prisma index 2b25b7129..ba6bdf3c4 100644 --- a/prisma/schema/user.prisma +++ b/prisma/schema/user.prisma @@ -33,6 +33,8 @@ model PrismaUserWalletBalance { tokenAddress String token PrismaToken @relation(fields:[tokenAddress, chain], references: [address, chain], onDelete: Cascade) + + @@index([poolId, chain]) } @@ -57,6 +59,9 @@ model PrismaUserStakedBalance { stakingId String staking PrismaPoolStaking @relation(fields:[stakingId, chain], references: [id, chain], onDelete: Cascade) + + @@index([poolId, chain]) + @@index([stakingId, chain]) } model PrismaUserBalanceSyncStatus { diff --git a/schema.ts b/schema.ts index aa048d5b7..4972b7ce7 100644 --- a/schema.ts +++ b/schema.ts @@ -2448,6 +2448,7 @@ export interface Mutation { poolReloadStakingForAllPools: Scalars['String']; poolSyncAllCowSnapshots: Array; poolSyncAllPoolsFromSubgraph: Array; + poolSyncFxQuoteTokens: Array; poolUpdateLifetimeValuesForAllPools: Scalars['String']; poolUpdateLiquidityValuesForAllPools: Scalars['String']; protocolCacheMetrics: Scalars['String']; @@ -2495,6 +2496,10 @@ export interface MutationPoolSyncAllCowSnapshotsArgs { chains: Array; } +export interface MutationPoolSyncFxQuoteTokensArgs { + chains: Array; +} + export interface MutationTokenDeleteTokenTypeArgs { tokenAddress: Scalars['String']; type: GqlTokenType; @@ -5333,6 +5338,12 @@ export type MutationResolvers< RequireFields >; poolSyncAllPoolsFromSubgraph?: Resolver, ParentType, ContextType>; + poolSyncFxQuoteTokens?: Resolver< + Array, + ParentType, + ContextType, + RequireFields + >; poolUpdateLifetimeValuesForAllPools?: Resolver; poolUpdateLiquidityValuesForAllPools?: Resolver; protocolCacheMetrics?: Resolver; diff --git a/tasks/index.ts b/tasks/index.ts index fa4751629..731b1fb19 100644 --- a/tasks/index.ts +++ b/tasks/index.ts @@ -133,6 +133,10 @@ async function run(job: string = process.argv[2], chainId: string = process.argv } else if (job === 'sync-sts-data') { return StakedSonicController().syncSonicStakingData(); } + // Maintenance + else if (job === 'sync-fx-quote-tokens') { + return FXPoolsController().syncQuoteTokens(chain); + } return Promise.reject(new Error(`Unknown job: ${job}`)); }