From c3bcaf911fb239ea6e5d929754fec6dc689970cc Mon Sep 17 00:00:00 2001 From: franz Date: Thu, 11 Jan 2024 17:13:51 +0100 Subject: [PATCH 01/15] fix remaining phantomstable pool type checks --- modules/content/github-content.service.ts | 5 ++++- modules/content/sanity-content.service.ts | 5 ++++- .../lib/apr-data-sources/boosted-pool-apr.service.ts | 10 +++++----- .../lib/apr-data-sources/phantom-stable-apr.service.ts | 2 +- modules/pool/lib/pool-snapshot.service.ts | 2 +- modules/pool/lib/pool-utils.ts | 2 +- modules/vebal/vebal-voting-list.service.ts | 2 +- 7 files changed, 17 insertions(+), 11 deletions(-) diff --git a/modules/content/github-content.service.ts b/modules/content/github-content.service.ts index 546556782..578db0eea 100644 --- a/modules/content/github-content.service.ts +++ b/modules/content/github-content.service.ts @@ -139,7 +139,10 @@ export class GithubContentService implements ContentService { }); } - if ((pool?.type === 'PHANTOM_STABLE' || pool?.type === 'LINEAR') && !tokenTypes.includes('PHANTOM_BPT')) { + if ( + (pool?.type === 'COMPOSABLE_STABLE' || pool?.type === 'LINEAR') && + !tokenTypes.includes('PHANTOM_BPT') + ) { types.push({ id: `${token.address}-phantom-bpt`, chain: networkContext.chain, diff --git a/modules/content/sanity-content.service.ts b/modules/content/sanity-content.service.ts index 1ed63c6c7..d48fef8aa 100644 --- a/modules/content/sanity-content.service.ts +++ b/modules/content/sanity-content.service.ts @@ -166,7 +166,10 @@ export class SanityContentService implements ContentService { }); } - if ((pool?.type === 'PHANTOM_STABLE' || pool?.type === 'LINEAR') && !tokenTypes.includes('PHANTOM_BPT')) { + if ( + (pool?.type === 'COMPOSABLE_STABLE' || pool?.type === 'LINEAR') && + !tokenTypes.includes('PHANTOM_BPT') + ) { types.push({ id: `${token.address}-phantom-bpt`, chain: this.chain, diff --git a/modules/pool/lib/apr-data-sources/boosted-pool-apr.service.ts b/modules/pool/lib/apr-data-sources/boosted-pool-apr.service.ts index a6c64e39c..c0e93d27f 100644 --- a/modules/pool/lib/apr-data-sources/boosted-pool-apr.service.ts +++ b/modules/pool/lib/apr-data-sources/boosted-pool-apr.service.ts @@ -11,7 +11,7 @@ export class BoostedPoolAprService implements PoolAprService { public async updateAprForPools(pools: PrismaPoolWithTokens[]): Promise { // need to do multiple queries otherwise the nesting is too deep for many pools. Error: stack depth limit exceeded - const boostedPools = pools.filter((pool) => pool.type === 'PHANTOM_STABLE' || pool.type === 'WEIGHTED'); + const boostedPools = pools.filter((pool) => pool.type === 'COMPOSABLE_STABLE' || pool.type === 'WEIGHTED'); const boostedPoolsWithNestedPool = await prisma.prismaPool.findMany({ where: { chain: networkContext.chain, id: { in: boostedPools.map((pool) => pool.id) } }, @@ -56,11 +56,11 @@ export class BoostedPoolAprService implements PoolAprService { //for phantom stable pools, the linear apr items have already been set in PhantomStableAprService, //so we're only concerned with finding the apr for phantom stable BPTs nested inside of //this phantom stable - if (pool.type === 'PHANTOM_STABLE') { - return token.nestedPool?.type === 'PHANTOM_STABLE'; + if (pool.type === 'COMPOSABLE_STABLE') { + return token.nestedPool?.type === 'COMPOSABLE_STABLE'; } - return token.nestedPool?.type === 'LINEAR' || token.nestedPool?.type === 'PHANTOM_STABLE'; + return token.nestedPool?.type === 'LINEAR' || token.nestedPool?.type === 'COMPOSABLE_STABLE'; }); const poolIds = tokens.map((token) => token.nestedPool?.id || ''); @@ -96,7 +96,7 @@ export class BoostedPoolAprService implements PoolAprService { if ( collectsYieldFee(pool) && //nested phantom stables already have the yield fee removed - token.nestedPool.type !== 'PHANTOM_STABLE' && + token.nestedPool.type !== 'COMPOSABLE_STABLE' && // nested tokens/bpts that dont have a rate provider, we don't take any fees token.dynamicData.priceRate !== '1.0' ) { diff --git a/modules/pool/lib/apr-data-sources/phantom-stable-apr.service.ts b/modules/pool/lib/apr-data-sources/phantom-stable-apr.service.ts index fff2dc917..de75b1b46 100644 --- a/modules/pool/lib/apr-data-sources/phantom-stable-apr.service.ts +++ b/modules/pool/lib/apr-data-sources/phantom-stable-apr.service.ts @@ -12,7 +12,7 @@ export class PhantomStableAprService implements PoolAprService { } public async updateAprForPools(pools: PrismaPoolWithTokens[]): Promise { - const phantomStablePools = pools.filter((pool) => pool.type === 'PHANTOM_STABLE'); + const phantomStablePools = pools.filter((pool) => pool.type === 'COMPOSABLE_STABLE'); const phantomStablePoolsExpanded = await prisma.prismaPool.findMany({ ...prismaPoolWithExpandedNesting, diff --git a/modules/pool/lib/pool-snapshot.service.ts b/modules/pool/lib/pool-snapshot.service.ts index f46e8c757..8e61477ca 100644 --- a/modules/pool/lib/pool-snapshot.service.ts +++ b/modules/pool/lib/pool-snapshot.service.ts @@ -112,7 +112,7 @@ export class PoolSnapshotService { const poolsWithoutSnapshots = await prisma.prismaPool.findMany({ where: { OR: [ - { type: 'PHANTOM_STABLE', chain: this.chain }, + { type: 'COMPOSABLE_STABLE', chain: this.chain }, { tokens: { some: { nestedPoolId: { not: null } } }, chain: this.chain }, ], }, diff --git a/modules/pool/lib/pool-utils.ts b/modules/pool/lib/pool-utils.ts index 321575fd9..b3fbc5d1c 100644 --- a/modules/pool/lib/pool-utils.ts +++ b/modules/pool/lib/pool-utils.ts @@ -9,7 +9,7 @@ type PoolWithTypeAndFactory = { }; export function isStablePool(poolType: PrismaPoolType) { - return poolType === 'STABLE' || poolType === 'META_STABLE' || poolType === 'PHANTOM_STABLE'; + return poolType === 'STABLE' || poolType === 'META_STABLE' || poolType === 'COMPOSABLE_STABLE'; } export function isWeightedPoolV2(pool: PoolWithTypeAndFactory) { diff --git a/modules/vebal/vebal-voting-list.service.ts b/modules/vebal/vebal-voting-list.service.ts index 02338e74d..db30f16b9 100644 --- a/modules/vebal/vebal-voting-list.service.ts +++ b/modules/vebal/vebal-voting-list.service.ts @@ -42,7 +42,7 @@ export class VeBalVotingListService { chain: pool.chain, symbol: pool.symbol, address: pool.address, - type: pool.type === 'COMPOSABLE_STABLE' ? 'PHANTOM_STABLE' : pool.type, + type: pool.type, tokens: pool.tokens.map((token) => ({ address: token.address, weight: token.dynamicData?.weight, From 299b00841f4c7266d51d7d0e79d7ea5a55168278 Mon Sep 17 00:00:00 2001 From: franzns <93920061+franzns@users.noreply.github.com> Date: Thu, 11 Jan 2024 17:24:32 +0100 Subject: [PATCH 02/15] Update on change only (#30) * only update gauges if necessary * only update masterchef farms if necessary --- .../pool/lib/staking/gauge-staking.service.ts | 153 ++++++++++-------- .../staking/master-chef-staking.service.ts | 79 +++++---- 2 files changed, 134 insertions(+), 98 deletions(-) diff --git a/modules/pool/lib/staking/gauge-staking.service.ts b/modules/pool/lib/staking/gauge-staking.service.ts index 5b56899df..9919c4a56 100644 --- a/modules/pool/lib/staking/gauge-staking.service.ts +++ b/modules/pool/lib/staking/gauge-staking.service.ts @@ -24,6 +24,8 @@ import type { JsonFragment } from '@ethersproject/abi'; import { Multicaller3 } from '../../../web3/multicaller3'; import { getInflationRate } from '../../../vebal/balancer-token-admin.service'; import _ from 'lodash'; +import { FtmStaking_OrderBy } from '../../../subgraphs/sftmx-subgraph/generated/sftmx-subgraph-types'; +import { total } from './bal-emissions'; interface GaugeRate { /** 1 for old gauges, 2 for gauges receiving cross chain BAL rewards */ @@ -81,15 +83,14 @@ export class GaugeStakingService implements PoolStakingService { ]); } - async syncStakingForPools(pools?: { id: string }[]): Promise { + async syncStakingForPools(): Promise { // Getting data from the DB and subgraph - const poolIds = ( - pools ?? - (await prisma.prismaPool.findMany({ - select: { id: true }, - where: { chain: networkContext.chain }, - })) - ).map((pool) => pool.id); + + const dbPools = await prisma.prismaPool.findMany({ + where: { chain: networkContext.chain }, + include: { staking: { include: { gauge: { include: { rewards: true } } } } }, + }); + const poolIds = dbPools.map((pool) => pool.id); const { pools: subgraphPoolsWithGauges } = await this.gaugeSubgraphService.getPoolsWithGauges(poolIds); const subgraphGauges = subgraphPoolsWithGauges @@ -101,7 +102,7 @@ export class GaugeStakingService implements PoolStakingService { .filter((pool) => pool.preferentialGauge) .map((pool) => pool.preferentialGauge!.id); - const dbGauges = subgraphGauges.map((gauge) => ({ + const gaugesForDb = subgraphGauges.map((gauge) => ({ id: gauge.id, poolId: gauge.poolId!, // we need to set the status based on the preferentialGauge entity on the gaugePool. If it's set there, it's preferential, otherwise it's active (or killed) @@ -130,52 +131,72 @@ export class GaugeStakingService implements PoolStakingService { }, }); - const onchainRates = await this.getOnchainRewardTokensData(dbGauges); + const onchainRates = await this.getOnchainRewardTokensData(gaugesForDb); // Prepare DB operations const operations: any[] = []; + const allDbStakings = dbPools.map((pool) => pool.staking).flat(); + const allDbStakingGauges = dbPools + .map((pool) => pool.staking) + .flat() + .map((gauge) => gauge.gauge); + // DB operations for gauges - for (const gauge of dbGauges) { - operations.push( - prisma.prismaPoolStaking.upsert({ - where: { id_chain: { id: gauge.id, chain: networkContext.chain } }, - create: { - id: gauge.id, - chain: networkContext.chain, - poolId: gauge.poolId, - type: 'GAUGE', - address: gauge.id, - }, - update: {}, - }), - ); - - operations.push( - prisma.prismaPoolStakingGauge.upsert({ - where: { id_chain: { id: gauge.id, chain: networkContext.chain } }, - create: { - id: gauge.id, - stakingId: gauge.id, - gaugeAddress: gauge.id, - chain: networkContext.chain, - status: gauge.status, - version: gauge.version, - workingSupply: onchainRates.find(({ id }) => `${gauge.id}-${this.balAddress}` === id) - ?.workingSupply, - totalSupply: onchainRates.find(({ id }) => id.includes(gauge.id))?.totalSupply, - }, - update: { - status: gauge.status, - version: gauge.version, - workingSupply: onchainRates.find(({ id }) => `${gauge.id}-${this.balAddress}` === id) - ?.workingSupply, - totalSupply: onchainRates.find(({ id }) => id.includes(gauge.id))?.totalSupply, - }, - }), - ); + for (const gauge of gaugesForDb) { + const dbStaking = allDbStakings.find((staking) => staking.id === gauge.id); + if (!dbStaking) { + operations.push( + prisma.prismaPoolStaking.upsert({ + where: { id_chain: { id: gauge.id, chain: networkContext.chain } }, + create: { + id: gauge.id, + chain: networkContext.chain, + poolId: gauge.poolId, + type: 'GAUGE', + address: gauge.id, + }, + update: {}, + }), + ); + } + + const dbStakingGauge = allDbStakingGauges.find((stakingGauge) => stakingGauge?.id === gauge.id); + const workingSupply = onchainRates.find(({ id }) => `${gauge.id}-${this.balAddress}` === id)?.workingSupply; + const totalSupply = onchainRates.find(({ id }) => id.includes(gauge.id))?.totalSupply; + if ( + !dbStakingGauge || + dbStakingGauge.status !== gauge.status || + dbStakingGauge.version !== gauge.version || + dbStakingGauge.workingSupply !== workingSupply || + dbStakingGauge.totalSupply !== totalSupply + ) { + operations.push( + prisma.prismaPoolStakingGauge.upsert({ + where: { id_chain: { id: gauge.id, chain: networkContext.chain } }, + create: { + id: gauge.id, + stakingId: gauge.id, + gaugeAddress: gauge.id, + chain: networkContext.chain, + status: gauge.status, + version: gauge.version, + workingSupply: workingSupply, + totalSupply: totalSupply, + }, + update: { + status: gauge.status, + version: gauge.version, + workingSupply: workingSupply, + totalSupply: totalSupply, + }, + }), + ); + } } + const allStakingGaugeRewards = allDbStakingGauges.map((gauge) => gauge?.rewards).flat(); + // DB operations for gauge reward tokens for (const { id, rewardPerSecond } of onchainRates) { const [gaugeId, tokenAddress] = id.toLowerCase().split('-'); @@ -188,24 +209,28 @@ export class GaugeStakingService implements PoolStakingService { continue; } - operations.push( - prisma.prismaPoolStakingGaugeReward.upsert({ - create: { - id, - chain: networkContext.chain, - gaugeId, - tokenAddress, - rewardPerSecond, - }, - update: { - rewardPerSecond, - }, - where: { id_chain: { id, chain: networkContext.chain } }, - }), - ); + const dbStakingGaugeRewards = allStakingGaugeRewards.find((rewards) => rewards?.id === id); + + if (!dbStakingGaugeRewards || dbStakingGaugeRewards.rewardPerSecond !== rewardPerSecond) { + operations.push( + prisma.prismaPoolStakingGaugeReward.upsert({ + create: { + id, + chain: networkContext.chain, + gaugeId, + tokenAddress, + rewardPerSecond, + }, + update: { + rewardPerSecond, + }, + where: { id_chain: { id, chain: networkContext.chain } }, + }), + ); + } } - await prismaBulkExecuteOperations(operations, true, undefined); + await prismaBulkExecuteOperations(operations, true); } private async getOnchainRewardTokensData( diff --git a/modules/pool/lib/staking/master-chef-staking.service.ts b/modules/pool/lib/staking/master-chef-staking.service.ts index 5dc744230..a10b2de79 100644 --- a/modules/pool/lib/staking/master-chef-staking.service.ts +++ b/modules/pool/lib/staking/master-chef-staking.service.ts @@ -47,27 +47,32 @@ export class MasterChefStakingService implements PoolStakingService { 18, ); - operations.push( - prisma.prismaPoolStaking.upsert({ - where: { id_chain: { id: farmId, chain: networkContext.chain } }, - create: { - id: farmId, - chain: networkContext.chain, - poolId: pool.id, - type: isFbeetsFarm ? 'FRESH_BEETS' : 'MASTER_CHEF', - address: isFbeetsFarm ? networkContext.data.fbeets!.address : farm.masterChef.id, - }, - update: {}, - }), - ); + const dbStaking = pool.staking.find((farm) => farm.id === farmId); - operations.push( - prisma.prismaPoolStakingMasterChefFarm.upsert({ - where: { id_chain: { id: farmId, chain: networkContext.chain } }, - create: { id: farmId, chain: networkContext.chain, stakingId: farmId, beetsPerBlock }, - update: { beetsPerBlock }, - }), - ); + if (!dbStaking) { + operations.push( + prisma.prismaPoolStaking.upsert({ + where: { id_chain: { id: farmId, chain: networkContext.chain } }, + create: { + id: farmId, + chain: networkContext.chain, + poolId: pool.id, + type: isFbeetsFarm ? 'FRESH_BEETS' : 'MASTER_CHEF', + address: isFbeetsFarm ? networkContext.data.fbeets!.address : farm.masterChef.id, + }, + update: {}, + }), + ); + } + + if (!dbStaking || !dbStaking.farm || dbStaking.farm.beetsPerBlock !== beetsPerBlock) + operations.push( + prisma.prismaPoolStakingMasterChefFarm.upsert({ + where: { id_chain: { id: farmId, chain: networkContext.chain } }, + create: { id: farmId, chain: networkContext.chain, stakingId: farmId, beetsPerBlock }, + update: { beetsPerBlock }, + }), + ); if (farm.rewarder) { for (const rewardToken of farm.rewarder.rewardTokens || []) { @@ -78,20 +83,26 @@ export class MasterChefStakingService implements PoolStakingService { ? formatFixed(rewardToken.rewardPerSecond, rewardToken.decimals) : '0.0'; - operations.push( - prisma.prismaPoolStakingMasterChefFarmRewarder.upsert({ - where: { id_chain: { id, chain: networkContext.chain } }, - create: { - id, - chain: networkContext.chain, - farmId, - tokenAddress: rewardToken.token, - address: farm.rewarder.id, - rewardPerSecond, - }, - update: { rewardPerSecond }, - }), - ); + if ( + !dbStaking || + !dbStaking.farm || + dbStaking.farm.rewarders.find((rewarder) => rewarder.id === id)?.rewardPerSecond !== + rewardPerSecond + ) + operations.push( + prisma.prismaPoolStakingMasterChefFarmRewarder.upsert({ + where: { id_chain: { id, chain: networkContext.chain } }, + create: { + id, + chain: networkContext.chain, + farmId, + tokenAddress: rewardToken.token, + address: farm.rewarder.id, + rewardPerSecond, + }, + update: { rewardPerSecond }, + }), + ); } } } From 3868fa5d6a6b5fe0c26562338e5742e5222654ec Mon Sep 17 00:00:00 2001 From: gmbronco <83549293+gmbronco@users.noreply.github.com> Date: Fri, 12 Jan 2024 13:44:14 +0100 Subject: [PATCH 03/15] Token yields update (#28) * renaming ib to yb i suppose ib stands for yield bearing * new aprs * removing obsolete ankr apr handler * fix plutusdao APR url --- modules/network/apr-config-types.ts | 16 ++---- modules/network/arbitrum.ts | 29 +++++++++-- modules/network/avalanche.ts | 24 ++++----- modules/network/base.ts | 8 +-- modules/network/fantom.ts | 41 +++++++-------- modules/network/gnosis.ts | 8 +-- modules/network/mainnet.ts | 26 +++++----- modules/network/network-config-types.ts | 8 +-- modules/network/optimism.ts | 37 ++++++++------ modules/network/polygon.ts | 8 +-- modules/network/zkevm.ts | 14 ++++-- .../sources/ankr-apr-handler.ts | 44 ---------------- .../sources/maker-gnosis-apr-handler.ts | 36 ------------- .../index.ts} | 11 ++-- .../sources/aave-apr-handler.ts | 2 +- .../sources/abis/ReaperCrypt.json | 0 .../sources/abis/ReaperCryptStrategy.json | 0 .../sources/abis/bloom-bps-feed.ts | 0 .../sources/abis/maker-pot.ts | 0 .../sources/abis/oErc20.ts | 0 .../sources/abis/reaperStrategy.ts | 0 .../sources/abis/tesseraPool.ts | 0 .../sources/beefy-apr-handler.ts | 6 +-- .../sources/bloom-apr-handler.ts | 8 +-- .../sources/default-apr-handler.ts | 23 +++++---- .../sources/etherfi-apr-handler.ts | 50 +++++++++++++++++++ .../sources/euler-apr-handler.ts | 2 +- .../sources/gearbox-apr-handler.ts | 2 +- .../sources/idle-apr-handler.ts | 15 +++--- .../sources/index.ts | 3 +- .../sources/maker-apr-handler.ts | 6 +-- .../sources/maker-gnosis-apr-handler.ts | 36 +++++++++++++ .../sources/ovix-apr-handler.ts | 2 +- .../sources/reaper-crypt-apr-handler.ts | 2 +- .../sources/sftmx-apr-handler.ts | 2 +- .../sources/stakewise-apr-handler.ts | 48 ++++++++++++++++++ .../sources/tessera-apr-handler.ts | 15 +++--- .../sources/tetu-apr-handler.ts | 2 +- .../sources/tranchess-apr-handler.ts | 4 +- .../sources/yearn-apr-handler.ts | 15 +++--- ...pr.service.ts => yb-tokens-apr.service.ts} | 26 +++++----- 41 files changed, 329 insertions(+), 250 deletions(-) delete mode 100644 modules/pool/lib/apr-data-sources/ib-linear-apr-handlers/sources/ankr-apr-handler.ts delete mode 100644 modules/pool/lib/apr-data-sources/ib-linear-apr-handlers/sources/maker-gnosis-apr-handler.ts rename modules/pool/lib/apr-data-sources/{ib-linear-apr-handlers/ib-linear-apr-handlers.ts => yb-apr-handlers/index.ts} (92%) rename modules/pool/lib/apr-data-sources/{ib-linear-apr-handlers => yb-apr-handlers}/sources/aave-apr-handler.ts (98%) rename modules/pool/lib/apr-data-sources/{ib-linear-apr-handlers => yb-apr-handlers}/sources/abis/ReaperCrypt.json (100%) rename modules/pool/lib/apr-data-sources/{ib-linear-apr-handlers => yb-apr-handlers}/sources/abis/ReaperCryptStrategy.json (100%) rename modules/pool/lib/apr-data-sources/{ib-linear-apr-handlers => yb-apr-handlers}/sources/abis/bloom-bps-feed.ts (100%) rename modules/pool/lib/apr-data-sources/{ib-linear-apr-handlers => yb-apr-handlers}/sources/abis/maker-pot.ts (100%) rename modules/pool/lib/apr-data-sources/{ib-linear-apr-handlers => yb-apr-handlers}/sources/abis/oErc20.ts (100%) rename modules/pool/lib/apr-data-sources/{ib-linear-apr-handlers => yb-apr-handlers}/sources/abis/reaperStrategy.ts (100%) rename modules/pool/lib/apr-data-sources/{ib-linear-apr-handlers => yb-apr-handlers}/sources/abis/tesseraPool.ts (100%) rename modules/pool/lib/apr-data-sources/{ib-linear-apr-handlers => yb-apr-handlers}/sources/beefy-apr-handler.ts (92%) rename modules/pool/lib/apr-data-sources/{ib-linear-apr-handlers => yb-apr-handlers}/sources/bloom-apr-handler.ts (89%) rename modules/pool/lib/apr-data-sources/{ib-linear-apr-handlers => yb-apr-handlers}/sources/default-apr-handler.ts (71%) create mode 100644 modules/pool/lib/apr-data-sources/yb-apr-handlers/sources/etherfi-apr-handler.ts rename modules/pool/lib/apr-data-sources/{ib-linear-apr-handlers => yb-apr-handlers}/sources/euler-apr-handler.ts (97%) rename modules/pool/lib/apr-data-sources/{ib-linear-apr-handlers => yb-apr-handlers}/sources/gearbox-apr-handler.ts (96%) rename modules/pool/lib/apr-data-sources/{ib-linear-apr-handlers => yb-apr-handlers}/sources/idle-apr-handler.ts (84%) rename modules/pool/lib/apr-data-sources/{ib-linear-apr-handlers => yb-apr-handlers}/sources/index.ts (88%) rename modules/pool/lib/apr-data-sources/{ib-linear-apr-handlers => yb-apr-handlers}/sources/maker-apr-handler.ts (92%) create mode 100644 modules/pool/lib/apr-data-sources/yb-apr-handlers/sources/maker-gnosis-apr-handler.ts rename modules/pool/lib/apr-data-sources/{ib-linear-apr-handlers => yb-apr-handlers}/sources/ovix-apr-handler.ts (96%) rename modules/pool/lib/apr-data-sources/{ib-linear-apr-handlers => yb-apr-handlers}/sources/reaper-crypt-apr-handler.ts (99%) rename modules/pool/lib/apr-data-sources/{ib-linear-apr-handlers => yb-apr-handlers}/sources/sftmx-apr-handler.ts (98%) create mode 100644 modules/pool/lib/apr-data-sources/yb-apr-handlers/sources/stakewise-apr-handler.ts rename modules/pool/lib/apr-data-sources/{ib-linear-apr-handlers => yb-apr-handlers}/sources/tessera-apr-handler.ts (84%) rename modules/pool/lib/apr-data-sources/{ib-linear-apr-handlers => yb-apr-handlers}/sources/tetu-apr-handler.ts (96%) rename modules/pool/lib/apr-data-sources/{ib-linear-apr-handlers => yb-apr-handlers}/sources/tranchess-apr-handler.ts (94%) rename modules/pool/lib/apr-data-sources/{ib-linear-apr-handlers => yb-apr-handlers}/sources/yearn-apr-handler.ts (76%) rename modules/pool/lib/apr-data-sources/{ib-tokens-apr.service.ts => yb-tokens-apr.service.ts} (84%) diff --git a/modules/network/apr-config-types.ts b/modules/network/apr-config-types.ts index 93b9c8ac0..76a24aa87 100644 --- a/modules/network/apr-config-types.ts +++ b/modules/network/apr-config-types.ts @@ -1,6 +1,5 @@ -export interface IbAprConfig { +export interface YbAprConfig { aave?: AaveAprConfig; - ankr?: AnkrAprConfig; bloom?: BloomAprConfig; beefy?: BeefyAprConfig; sftmx?: SftmxAprConfig; @@ -14,6 +13,8 @@ export interface IbAprConfig { tetu?: TetuAprConfig; tranchess?: TranchessAprConfig; yearn?: YearnAprConfig; + stakewise?: string; + etherfi?: string; defaultHandlers?: DefaultHandlerAprConfig; fixedAprHandler?: FixedAprConfig; } @@ -34,17 +35,6 @@ export interface AaveAprConfig { }; } -export interface AnkrAprConfig { - sourceUrl: string; - tokens: { - [underlyingAssetName: string]: { - address: string; - serviceName: string; - isIbYield?: boolean; - }; - }; -} - export interface BeefyAprConfig { sourceUrl: string; tokens: { diff --git a/modules/network/arbitrum.ts b/modules/network/arbitrum.ts index 55676c40b..5e5b8f475 100644 --- a/modules/network/arbitrum.ts +++ b/modules/network/arbitrum.ts @@ -15,11 +15,11 @@ import { GithubContentService } from '../content/github-content.service'; import { gaugeSubgraphService } from '../subgraphs/gauge-subgraph/gauge-subgraph.service'; import { CoingeckoPriceHandlerService } from '../token/lib/token-price-handlers/coingecko-price-handler.service'; import { coingeckoService } from '../coingecko/coingecko.service'; -import { IbTokensAprService } from '../pool/lib/apr-data-sources/ib-tokens-apr.service'; +import { YbTokensAprService } from '../pool/lib/apr-data-sources/yb-tokens-apr.service'; import { env } from '../../app/env'; import { BalancerSubgraphService } from '../subgraphs/balancer-subgraph/balancer-subgraph.service'; -const arbitrumNetworkData: NetworkData = { +export const arbitrumNetworkData: NetworkData = { chain: { slug: 'arbitrum', id: 42161, @@ -93,7 +93,7 @@ const arbitrumNetworkData: NetworkData = { poolIdsToExclude: [], }, }, - ibAprConfig: { + ybAprConfig: { aave: { v3: { subgraphUrl: 'https://api.thegraph.com/subgraphs/name/aave/protocol-v3-arbitrum', @@ -175,6 +175,25 @@ const arbitrumNetworkData: NetworkData = { path: 'sfrxethApr', isIbYield: true, }, + sFRAX: { + tokenAddress: '0xe3b3fe7bca19ca77ad877a5bebab186becfad906', + sourceUrl: 'https://api.frax.finance/v2/frax/sfrax/summary/history?range=1d', + path: 'items.0.sfraxApr', + isIbYield: true, + }, + ankrETH: { + tokenAddress: '0xe05a08226c49b636acf99c40da8dc6af83ce5bb3', + sourceUrl: 'https://api.staking.ankr.com/v1alpha/metrics', + path: 'services.{serviceName == "eth"}.apy', + isIbYield: true, + }, + plsRDNT: { + tokenAddress: '0x6dbf2155b0636cb3fd5359fccefb8a2c02b6cb51', + sourceUrl: 'https://www.plutusdao.io/api/getPlsRdntInfo', + path: 'apr', + scale: 10000, + isIbYield: true, + }, }, }, beefy: { @@ -215,8 +234,8 @@ export const arbitrumNetworkConfig: NetworkConfig = { contentService: new GithubContentService(), provider: new ethers.providers.JsonRpcProvider({ url: arbitrumNetworkData.rpcUrl, timeout: 60000 }), poolAprServices: [ - new IbTokensAprService( - arbitrumNetworkData.ibAprConfig, + new YbTokensAprService( + arbitrumNetworkData.ybAprConfig, arbitrumNetworkData.chain.prismaId, arbitrumNetworkData.balancer.yieldProtocolFeePercentage, arbitrumNetworkData.balancer.swapProtocolFeePercentage, diff --git a/modules/network/avalanche.ts b/modules/network/avalanche.ts index 8cf724a68..d00e1b3e8 100644 --- a/modules/network/avalanche.ts +++ b/modules/network/avalanche.ts @@ -16,7 +16,7 @@ import { gaugeSubgraphService } from '../subgraphs/gauge-subgraph/gauge-subgraph import { coingeckoService } from '../coingecko/coingecko.service'; import { CoingeckoPriceHandlerService } from '../token/lib/token-price-handlers/coingecko-price-handler.service'; import { env } from '../../app/env'; -import { IbTokensAprService } from '../pool/lib/apr-data-sources/ib-tokens-apr.service'; +import { YbTokensAprService } from '../pool/lib/apr-data-sources/yb-tokens-apr.service'; import { BalancerSubgraphService } from '../subgraphs/balancer-subgraph/balancer-subgraph.service'; const avalancheNetworkData: NetworkData = { @@ -91,7 +91,7 @@ const avalancheNetworkData: NetworkData = { poolIdsToExclude: [], }, }, - ibAprConfig: { + ybAprConfig: { aave: { v3: { subgraphUrl: 'https://api.thegraph.com/subgraphs/name/aave/protocol-v3-avalanche', @@ -141,16 +141,6 @@ const avalancheNetworkData: NetworkData = { }, }, }, - ankr: { - sourceUrl: 'https://api.staking.ankr.com/v1alpha/metrics', - tokens: { - ankrAVAX: { - address: '0xc3344870d52688874b06d844e0c36cc39fc727f6', - serviceName: 'avax', - isIbYield: true, - }, - }, - }, defaultHandlers: { sAVAX: { tokenAddress: '0x2b2c81e08f1af8835a78bb2a90ae924ace0ea4be', @@ -173,6 +163,12 @@ const avalancheNetworkData: NetworkData = { // M * -12 = A (M is monthly rate and A is APR) => (M/x) = (A/100) => (A / -12x) = (A / 100) [replacing M by A/-12] => x = 100/-12 = -8,33333 scale: -8.3333, }, + ankrAVAX: { + tokenAddress: '0xc3344870d52688874b06d844e0c36cc39fc727f6', + sourceUrl: 'https://api.staking.ankr.com/v1alpha/metrics', + path: 'services.{serviceName == "avax"}.apy', + isIbYield: true, + }, }, }, beefy: { @@ -209,8 +205,8 @@ export const avalancheNetworkConfig: NetworkConfig = { contentService: new GithubContentService(), provider: new ethers.providers.JsonRpcProvider({ url: avalancheNetworkData.rpcUrl, timeout: 60000 }), poolAprServices: [ - new IbTokensAprService( - avalancheNetworkData.ibAprConfig, + new YbTokensAprService( + avalancheNetworkData.ybAprConfig, avalancheNetworkData.chain.prismaId, avalancheNetworkData.balancer.yieldProtocolFeePercentage, avalancheNetworkData.balancer.swapProtocolFeePercentage, diff --git a/modules/network/base.ts b/modules/network/base.ts index 43344f5b5..28c6a7f64 100644 --- a/modules/network/base.ts +++ b/modules/network/base.ts @@ -15,7 +15,7 @@ import { gaugeSubgraphService } from '../subgraphs/gauge-subgraph/gauge-subgraph import { CoingeckoPriceHandlerService } from '../token/lib/token-price-handlers/coingecko-price-handler.service'; import { coingeckoService } from '../coingecko/coingecko.service'; import { env } from '../../app/env'; -import { IbTokensAprService } from '../pool/lib/apr-data-sources/ib-tokens-apr.service'; +import { YbTokensAprService } from '../pool/lib/apr-data-sources/yb-tokens-apr.service'; import { BalancerSubgraphService } from '../subgraphs/balancer-subgraph/balancer-subgraph.service'; const baseNetworkData: NetworkData = { @@ -66,7 +66,7 @@ const baseNetworkData: NetworkData = { swapProtocolFeePercentage: 0.5, yieldProtocolFeePercentage: 0.5, }, - ibAprConfig: { + ybAprConfig: { defaultHandlers: { cbETH: { tokenAddress: '0x2ae3f1ec7f1f5012cfeab0185bfc7aa3cf0dec22', @@ -129,8 +129,8 @@ export const baseNetworkConfig: NetworkConfig = { contentService: new GithubContentService(), provider: new ethers.providers.JsonRpcProvider({ url: baseNetworkData.rpcUrl, timeout: 60000 }), poolAprServices: [ - new IbTokensAprService( - baseNetworkData.ibAprConfig, + new YbTokensAprService( + baseNetworkData.ybAprConfig, baseNetworkData.chain.prismaId, baseNetworkData.balancer.yieldProtocolFeePercentage, baseNetworkData.balancer.swapProtocolFeePercentage, diff --git a/modules/network/fantom.ts b/modules/network/fantom.ts index ff3a5510c..36549c936 100644 --- a/modules/network/fantom.ts +++ b/modules/network/fantom.ts @@ -24,7 +24,7 @@ import { SanityContentService } from '../content/sanity-content.service'; import { CoingeckoPriceHandlerService } from '../token/lib/token-price-handlers/coingecko-price-handler.service'; import { coingeckoService } from '../coingecko/coingecko.service'; import { env } from '../../app/env'; -import { IbTokensAprService } from '../pool/lib/apr-data-sources/ib-tokens-apr.service'; +import { YbTokensAprService } from '../pool/lib/apr-data-sources/yb-tokens-apr.service'; import { BeetswarsGaugeVotingAprService } from '../pool/lib/apr-data-sources/fantom/beetswars-gauge-voting-apr'; import { BalancerSubgraphService } from '../subgraphs/balancer-subgraph/balancer-subgraph.service'; import { SftmxSubgraphService } from '../subgraphs/sftmx-subgraph/sftmx.service'; @@ -155,22 +155,7 @@ const fantomNetworkData: NetworkData = { poolIdsToExclude: [], }, }, - ibAprConfig: { - ankr: { - sourceUrl: 'https://api.staking.ankr.com/v1alpha/metrics', - tokens: { - ankrETH: { - address: '0x12d8ce035c5de3ce39b1fdd4c1d5a745eaba3b8c', - serviceName: 'eth', - isIbYield: true, - }, - ankrFTM: { - address: '0xcfc785741dc0e98ad4c9f6394bb9d43cd1ef5179', - serviceName: 'ftm', - isIbYield: true, - }, - }, - }, + ybAprConfig: { // sftmx: { // tokens: { // sftmx: { @@ -246,6 +231,20 @@ const fantomNetworkData: NetworkData = { isIbYield: true, }, }, + defaultHandlers: { + ankrETH: { + tokenAddress: '0x12d8ce035c5de3ce39b1fdd4c1d5a745eaba3b8c', + sourceUrl: 'https://api.staking.ankr.com/v1alpha/metrics', + path: 'services.{serviceName == "eth"}.apy', + isIbYield: true, + }, + ankrFTM: { + tokenAddress: '0xcfc785741dc0e98ad4c9f6394bb9d43cd1ef5179', + sourceUrl: 'https://api.staking.ankr.com/v1alpha/metrics', + path: 'services.{serviceName == "ftm"}.apy', + isIbYield: true, + }, + }, }, copper: { proxyAddress: '0xbc8a71c75ffbd2807c021f4f81a8832392def93c', @@ -259,10 +258,6 @@ const fantomNetworkData: NetworkData = { stader: { sFtmxContract: '0xd7028092c830b5c8fce061af2e593413ebbc1fc1', }, - ankr: { - ankrFtmContract: '0xcfc785741dc0e98ad4c9f6394bb9d43cd1ef5179', - ankrEthContract: '0x12d8ce035c5de3ce39b1fdd4c1d5a745eaba3b8c', - }, datastudio: { main: { user: 'datafeed-service@datastudio-366113.iam.gserviceaccount.com', @@ -294,8 +289,8 @@ export const fantomNetworkConfig: NetworkConfig = { contentService: new SanityContentService(fantomNetworkData.chain.prismaId), provider: new ethers.providers.JsonRpcProvider({ url: fantomNetworkData.rpcUrl, timeout: 60000 }), poolAprServices: [ - new IbTokensAprService( - fantomNetworkData.ibAprConfig, + new YbTokensAprService( + fantomNetworkData.ybAprConfig, fantomNetworkData.chain.prismaId, fantomNetworkData.balancer.yieldProtocolFeePercentage, fantomNetworkData.balancer.swapProtocolFeePercentage, diff --git a/modules/network/gnosis.ts b/modules/network/gnosis.ts index 8323dd363..fba9f6584 100644 --- a/modules/network/gnosis.ts +++ b/modules/network/gnosis.ts @@ -16,7 +16,7 @@ import { gaugeSubgraphService } from '../subgraphs/gauge-subgraph/gauge-subgraph import { coingeckoService } from '../coingecko/coingecko.service'; import { CoingeckoPriceHandlerService } from '../token/lib/token-price-handlers/coingecko-price-handler.service'; import { env } from '../../app/env'; -import { IbTokensAprService } from '../pool/lib/apr-data-sources/ib-tokens-apr.service'; +import { YbTokensAprService } from '../pool/lib/apr-data-sources/yb-tokens-apr.service'; import { BalancerSubgraphService } from '../subgraphs/balancer-subgraph/balancer-subgraph.service'; const gnosisNetworkData: NetworkData = { @@ -89,7 +89,7 @@ const gnosisNetworkData: NetworkData = { poolIdsToExclude: [], }, }, - ibAprConfig: { + ybAprConfig: { defaultHandlers: { wstETH: { tokenAddress: '0x6c76971f98945ae98dd7d4dfca8711ebea946ea6', @@ -133,8 +133,8 @@ export const gnosisNetworkConfig: NetworkConfig = { contentService: new GithubContentService(), provider: new ethers.providers.JsonRpcProvider({ url: gnosisNetworkData.rpcUrl, timeout: 60000 }), poolAprServices: [ - new IbTokensAprService( - gnosisNetworkData.ibAprConfig, + new YbTokensAprService( + gnosisNetworkData.ybAprConfig, gnosisNetworkData.chain.prismaId, gnosisNetworkData.balancer.yieldProtocolFeePercentage, gnosisNetworkData.balancer.swapProtocolFeePercentage, diff --git a/modules/network/mainnet.ts b/modules/network/mainnet.ts index 0fa6025da..b862ed02f 100644 --- a/modules/network/mainnet.ts +++ b/modules/network/mainnet.ts @@ -15,7 +15,7 @@ import { GithubContentService } from '../content/github-content.service'; import { gaugeSubgraphService } from '../subgraphs/gauge-subgraph/gauge-subgraph.service'; import { coingeckoService } from '../coingecko/coingecko.service'; import { CoingeckoPriceHandlerService } from '../token/lib/token-price-handlers/coingecko-price-handler.service'; -import { IbTokensAprService } from '../pool/lib/apr-data-sources/ib-tokens-apr.service'; +import { YbTokensAprService } from '../pool/lib/apr-data-sources/yb-tokens-apr.service'; import { env } from '../../app/env'; import { BalancerSubgraphService } from '../subgraphs/balancer-subgraph/balancer-subgraph.service'; @@ -114,7 +114,7 @@ const data: NetworkData = { ], }, }, - ibAprConfig: { + ybAprConfig: { aave: { v2: { subgraphUrl: 'https://api.thegraph.com/subgraphs/name/aave/protocol-v2', @@ -180,16 +180,6 @@ const data: NetworkData = { }, }, }, - ankr: { - sourceUrl: 'https://api.staking.ankr.com/v1alpha/metrics', - tokens: { - ankrETH: { - address: '0xe95a203b1a91a908f9b9ce46459d101078c2c3cb', - serviceName: 'eth', - isIbYield: true, - }, - }, - }, bloom: { tokens: { tbyFeb1924: { @@ -258,6 +248,8 @@ const data: NetworkData = { }, }, }, + stakewise: '0xf1c9acdc66974dfb6decb12aa385b9cd01190e38', + etherfi: '0xcd5fe23c85820f7b72d0926fc9b05b43e359b7ee', defaultHandlers: { vETH: { tokenAddress: '0x4bc3263eb5bb2ef7ad9ab6fb68be80e43b43801f', @@ -331,6 +323,12 @@ const data: NetworkData = { isIbYield: true, scale: 1, }, + ankrETH: { + tokenAddress: '0xe95a203b1a91a908f9b9ce46459d101078c2c3cb', + sourceUrl: 'https://api.staking.ankr.com/v1alpha/metrics', + path: 'services.{serviceName == "eth"}.apy', + isIbYield: true, + }, }, }, beefy: { @@ -367,8 +365,8 @@ export const mainnetNetworkConfig: NetworkConfig = { contentService: new GithubContentService(), provider: new ethers.providers.JsonRpcProvider({ url: data.rpcUrl, timeout: 60000 }), poolAprServices: [ - new IbTokensAprService( - data.ibAprConfig, + new YbTokensAprService( + data.ybAprConfig, data.chain.prismaId, data.balancer.yieldProtocolFeePercentage, data.balancer.swapProtocolFeePercentage, diff --git a/modules/network/network-config-types.ts b/modules/network/network-config-types.ts index 9127ac3a4..efeaabc08 100644 --- a/modules/network/network-config-types.ts +++ b/modules/network/network-config-types.ts @@ -6,7 +6,7 @@ import { TokenPriceHandler } from '../token/token-types'; import { BaseProvider } from '@ethersproject/providers'; import { GqlChain } from '../../schema'; import { ContentService } from '../content/content-types'; -import { IbAprConfig } from './apr-config-types'; +import { YbAprConfig } from './apr-config-types'; import { BalancerSubgraphService } from '../subgraphs/balancer-subgraph/balancer-subgraph.service'; import { SftmxSubgraphService } from '../subgraphs/sftmx-subgraph/sftmx.service'; @@ -113,7 +113,7 @@ export interface NetworkData { address: string; excludedFarmIds: string[]; }; - ibAprConfig: IbAprConfig; + ybAprConfig: YbAprConfig; reliquary?: { address: string; excludedFarmIds: string[]; @@ -137,10 +137,6 @@ export interface NetworkData { spooky?: { xBooContract: string; }; - ankr?: { - ankrFtmContract: string; - ankrEthContract: string; - }; overnight?: { aprEndpoint: string; }; diff --git a/modules/network/optimism.ts b/modules/network/optimism.ts index 34740a0b7..2faa94d8f 100644 --- a/modules/network/optimism.ts +++ b/modules/network/optimism.ts @@ -16,7 +16,7 @@ import { SanityContentService } from '../content/sanity-content.service'; import { gaugeSubgraphService } from '../subgraphs/gauge-subgraph/gauge-subgraph.service'; import { coingeckoService } from '../coingecko/coingecko.service'; import { CoingeckoPriceHandlerService } from '../token/lib/token-price-handlers/coingecko-price-handler.service'; -import { IbTokensAprService } from '../pool/lib/apr-data-sources/ib-tokens-apr.service'; +import { YbTokensAprService } from '../pool/lib/apr-data-sources/yb-tokens-apr.service'; import { env } from '../../app/env'; import { BalancerSubgraphService } from '../subgraphs/balancer-subgraph/balancer-subgraph.service'; @@ -102,17 +102,7 @@ const optimismNetworkData: NetworkData = { poolIdsToExclude: [], }, }, - ibAprConfig: { - ankr: { - sourceUrl: 'https://api.staking.ankr.com/v1alpha/metrics', - tokens: { - ankrETH: { - address: '0xe05a08226c49b636acf99c40da8dc6af83ce5bb3', - serviceName: 'eth', - isIbYield: true, - }, - }, - }, + ybAprConfig: { beefy: { sourceUrl: 'https://api.beefy.finance/apy/breakdown?_=', tokens: { @@ -208,6 +198,25 @@ const optimismNetworkData: NetworkData = { path: 'sfrxethApr', isIbYield: true, }, + sFRAX: { + tokenAddress: '0x2dd1b4d4548accea497050619965f91f78b3b532', + sourceUrl: 'https://api.frax.finance/v2/frax/sfrax/summary/history?range=1d', + path: 'items.0.sfraxApr', + isIbYield: true, + }, + stERN: { + tokenAddress: '0x3ee6107d9c93955acbb3f39871d32b02f82b78ab', + sourceUrl: + 'https://2ch9hbg8hh.execute-api.us-east-1.amazonaws.com/dev/api/vault/0x3eE6107d9C93955acBb3f39871D32B02F82B78AB:0xa', + path: 'data.yields.apy', + isIbYield: true, + }, + ankrETH: { + tokenAddress: '0xe05a08226c49b636acf99c40da8dc6af83ce5bb3', + sourceUrl: 'https://api.staking.ankr.com/v1alpha/metrics', + path: 'services.{serviceName == "eth"}.apy', + isIbYield: true, + }, }, }, beefy: { @@ -253,8 +262,8 @@ export const optimismNetworkConfig: NetworkConfig = { contentService: new SanityContentService(optimismNetworkData.chain.prismaId), provider: new ethers.providers.JsonRpcProvider({ url: optimismNetworkData.rpcUrl, timeout: 60000 }), poolAprServices: [ - new IbTokensAprService( - optimismNetworkData.ibAprConfig, + new YbTokensAprService( + optimismNetworkData.ybAprConfig, optimismNetworkData.chain.prismaId, optimismNetworkData.balancer.yieldProtocolFeePercentage, optimismNetworkData.balancer.swapProtocolFeePercentage, diff --git a/modules/network/polygon.ts b/modules/network/polygon.ts index 8598d6930..ae8f88d55 100644 --- a/modules/network/polygon.ts +++ b/modules/network/polygon.ts @@ -15,7 +15,7 @@ import { GithubContentService } from '../content/github-content.service'; import { gaugeSubgraphService } from '../subgraphs/gauge-subgraph/gauge-subgraph.service'; import { coingeckoService } from '../coingecko/coingecko.service'; import { CoingeckoPriceHandlerService } from '../token/lib/token-price-handlers/coingecko-price-handler.service'; -import { IbTokensAprService } from '../pool/lib/apr-data-sources/ib-tokens-apr.service'; +import { YbTokensAprService } from '../pool/lib/apr-data-sources/yb-tokens-apr.service'; import { env } from '../../app/env'; import { BalancerSubgraphService } from '../subgraphs/balancer-subgraph/balancer-subgraph.service'; @@ -93,7 +93,7 @@ const polygonNetworkData: NetworkData = { poolIdsToExclude: [], }, }, - ibAprConfig: { + ybAprConfig: { aave: { v2: { subgraphUrl: 'https://api.thegraph.com/subgraphs/name/aave/aave-v2-matic', @@ -250,8 +250,8 @@ export const polygonNetworkConfig: NetworkConfig = { contentService: new GithubContentService(), provider: new ethers.providers.JsonRpcProvider({ url: polygonNetworkData.rpcUrl, timeout: 60000 }), poolAprServices: [ - new IbTokensAprService( - polygonNetworkData.ibAprConfig, + new YbTokensAprService( + polygonNetworkData.ybAprConfig, polygonNetworkData.chain.prismaId, polygonNetworkData.balancer.yieldProtocolFeePercentage, polygonNetworkData.balancer.swapProtocolFeePercentage, diff --git a/modules/network/zkevm.ts b/modules/network/zkevm.ts index b540565dd..d5e229b87 100644 --- a/modules/network/zkevm.ts +++ b/modules/network/zkevm.ts @@ -16,7 +16,7 @@ import { gaugeSubgraphService } from '../subgraphs/gauge-subgraph/gauge-subgraph import { CoingeckoPriceHandlerService } from '../token/lib/token-price-handlers/coingecko-price-handler.service'; import { coingeckoService } from '../coingecko/coingecko.service'; import { env } from '../../app/env'; -import { IbTokensAprService } from '../pool/lib/apr-data-sources/ib-tokens-apr.service'; +import { YbTokensAprService } from '../pool/lib/apr-data-sources/yb-tokens-apr.service'; import { BalancerSubgraphService } from '../subgraphs/balancer-subgraph/balancer-subgraph.service'; const zkevmNetworkData: NetworkData = { @@ -95,7 +95,7 @@ const zkevmNetworkData: NetworkData = { poolIdsToExclude: [], }, }, - ibAprConfig: { + ybAprConfig: { ovix: { tokens: { USDT: { @@ -121,6 +121,12 @@ const zkevmNetworkData: NetworkData = { path: 'rethAPR', isIbYield: true, }, + ankrETH: { + tokenAddress: '0x12d8ce035c5de3ce39b1fdd4c1d5a745eaba3b8c', + sourceUrl: 'https://api.staking.ankr.com/v1alpha/metrics', + path: 'services.{serviceName == "eth"}.apy', + isIbYield: true, + }, }, }, beefy: { @@ -157,8 +163,8 @@ export const zkevmNetworkConfig: NetworkConfig = { contentService: new GithubContentService(), provider: new ethers.providers.JsonRpcProvider({ url: zkevmNetworkData.rpcUrl, timeout: 60000 }), poolAprServices: [ - new IbTokensAprService( - zkevmNetworkData.ibAprConfig, + new YbTokensAprService( + zkevmNetworkData.ybAprConfig, zkevmNetworkData.chain.prismaId, zkevmNetworkData.balancer.yieldProtocolFeePercentage, zkevmNetworkData.balancer.swapProtocolFeePercentage, diff --git a/modules/pool/lib/apr-data-sources/ib-linear-apr-handlers/sources/ankr-apr-handler.ts b/modules/pool/lib/apr-data-sources/ib-linear-apr-handlers/sources/ankr-apr-handler.ts deleted file mode 100644 index 86c4e446d..000000000 --- a/modules/pool/lib/apr-data-sources/ib-linear-apr-handlers/sources/ankr-apr-handler.ts +++ /dev/null @@ -1,44 +0,0 @@ -import axios from 'axios'; -import * as Sentry from '@sentry/node'; -import { AprHandler } from '../ib-linear-apr-handlers'; -import { AnkrAprConfig } from '../../../../../network/apr-config-types'; - -export class AnkrAprHandler implements AprHandler { - tokens: { - [underlyingAssetName: string]: { - address: string; - serviceName: string; - isIbYield?: boolean; - }; - }; - url: string; - - constructor(config: AnkrAprConfig) { - this.tokens = config.tokens; - this.url = config.sourceUrl; - } - - async getAprs() { - try { - const { data } = await axios.get(this.url); - const services = (data as { services: { serviceName: string; apy: string }[] }).services; - const aprs = Object.fromEntries( - Object.values(this.tokens).map(({ address, serviceName, isIbYield }) => { - const service = services.find((service) => service.serviceName === serviceName); - if (!service) { - return [address, 0]; - } - return [address, { - apr: parseFloat(service.apy) / 1e2, - isIbYield: isIbYield ?? false, - }]; - }), - ); - return aprs; - } catch (error) { - console.error('Failed to fetch Ankr APR:', error); - Sentry.captureException(`Ankr IB APR handler failed: ${error}`); - return {}; - } - } -} diff --git a/modules/pool/lib/apr-data-sources/ib-linear-apr-handlers/sources/maker-gnosis-apr-handler.ts b/modules/pool/lib/apr-data-sources/ib-linear-apr-handlers/sources/maker-gnosis-apr-handler.ts deleted file mode 100644 index a89220193..000000000 --- a/modules/pool/lib/apr-data-sources/ib-linear-apr-handlers/sources/maker-gnosis-apr-handler.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { AprHandler } from '../ib-linear-apr-handlers'; -import { getContractAt } from '../../../../../web3/contract'; -import { Chain } from '@prisma/client'; - -const helperAbi = ['function vaultAPY() view returns (uint256)']; - -/** Sets the config data used internally */ -const config = { - "GNOSIS": { - sdaiAddress: '0xaf204776c7245bf4147c2612bf6e5972ee483701', - helperAddress: '0xd499b51fcfc66bd31248ef4b28d656d67e591a94', - } -} - -/** Makes handler callable by chain */ -export const chains = Object.keys(config) as Chain[]; - -export class Handler implements AprHandler { - async getAprs(chain: Chain) { - if (chain !== 'GNOSIS') { - throw `Handler supports GNOSIS only, but called for ${chain}` - } - - const helper = getContractAt(config[chain].helperAddress, helperAbi); - const vaultAPY = await helper.vaultAPY(); - const apr = Number(vaultAPY) * (10 ** -18); - - return { - [config[chain].sdaiAddress]: { - apr, - isIbYield: true, - group: 'MAKER' - } - } - } -} diff --git a/modules/pool/lib/apr-data-sources/ib-linear-apr-handlers/ib-linear-apr-handlers.ts b/modules/pool/lib/apr-data-sources/yb-apr-handlers/index.ts similarity index 92% rename from modules/pool/lib/apr-data-sources/ib-linear-apr-handlers/ib-linear-apr-handlers.ts rename to modules/pool/lib/apr-data-sources/yb-apr-handlers/index.ts index adea7ee34..167dd23d6 100644 --- a/modules/pool/lib/apr-data-sources/ib-linear-apr-handlers/ib-linear-apr-handlers.ts +++ b/modules/pool/lib/apr-data-sources/yb-apr-handlers/index.ts @@ -1,10 +1,9 @@ import * as sources from './sources'; -import { IbAprConfig } from '../../../../network/apr-config-types'; +import { YbAprConfig } from '../../../../network/apr-config-types'; import { Chain } from '@prisma/client'; const sourceToHandler = { aave: sources.AaveAprHandler, - ankr: sources.AnkrAprHandler, beefy: sources.BeefyAprHandler, bloom: sources.BloomAprHandler, sftmx: sources.SftmxAprHandler, @@ -19,19 +18,21 @@ const sourceToHandler = { tranchess: sources.TranchessAprHandler, yearn: sources.YearnAprHandler, defaultHandlers: sources.DefaultAprHandler, + stakewise: sources.Stakewise, + etherfi: sources.Etherfi, }; -export class IbLinearAprHandlers { +export class YbAprHandlers { private handlers: AprHandler[] = []; fixedAprTokens?: { [tokenName: string]: { address: string; apr: number; group?: string; isIbYield?: boolean } }; - constructor(aprConfig: IbAprConfig, private chain?: Chain) { + constructor(aprConfig: YbAprConfig, private chain?: Chain) { const { fixedAprHandler, ...config } = aprConfig; this.handlers = this.buildAprHandlers(config); this.fixedAprTokens = fixedAprHandler; } - private buildAprHandlers(aprConfig: IbAprConfig) { + private buildAprHandlers(aprConfig: YbAprConfig) { const handlers: AprHandler[] = []; // Add handlers from global configuration diff --git a/modules/pool/lib/apr-data-sources/ib-linear-apr-handlers/sources/aave-apr-handler.ts b/modules/pool/lib/apr-data-sources/yb-apr-handlers/sources/aave-apr-handler.ts similarity index 98% rename from modules/pool/lib/apr-data-sources/ib-linear-apr-handlers/sources/aave-apr-handler.ts rename to modules/pool/lib/apr-data-sources/yb-apr-handlers/sources/aave-apr-handler.ts index 5312b1985..3286759c4 100644 --- a/modules/pool/lib/apr-data-sources/ib-linear-apr-handlers/sources/aave-apr-handler.ts +++ b/modules/pool/lib/apr-data-sources/yb-apr-handlers/sources/aave-apr-handler.ts @@ -1,6 +1,6 @@ import axios from 'axios'; import * as Sentry from '@sentry/node'; -import { AprHandler } from '../ib-linear-apr-handlers'; +import { AprHandler } from '..'; export class AaveAprHandler implements AprHandler { tokens: { diff --git a/modules/pool/lib/apr-data-sources/ib-linear-apr-handlers/sources/abis/ReaperCrypt.json b/modules/pool/lib/apr-data-sources/yb-apr-handlers/sources/abis/ReaperCrypt.json similarity index 100% rename from modules/pool/lib/apr-data-sources/ib-linear-apr-handlers/sources/abis/ReaperCrypt.json rename to modules/pool/lib/apr-data-sources/yb-apr-handlers/sources/abis/ReaperCrypt.json diff --git a/modules/pool/lib/apr-data-sources/ib-linear-apr-handlers/sources/abis/ReaperCryptStrategy.json b/modules/pool/lib/apr-data-sources/yb-apr-handlers/sources/abis/ReaperCryptStrategy.json similarity index 100% rename from modules/pool/lib/apr-data-sources/ib-linear-apr-handlers/sources/abis/ReaperCryptStrategy.json rename to modules/pool/lib/apr-data-sources/yb-apr-handlers/sources/abis/ReaperCryptStrategy.json diff --git a/modules/pool/lib/apr-data-sources/ib-linear-apr-handlers/sources/abis/bloom-bps-feed.ts b/modules/pool/lib/apr-data-sources/yb-apr-handlers/sources/abis/bloom-bps-feed.ts similarity index 100% rename from modules/pool/lib/apr-data-sources/ib-linear-apr-handlers/sources/abis/bloom-bps-feed.ts rename to modules/pool/lib/apr-data-sources/yb-apr-handlers/sources/abis/bloom-bps-feed.ts diff --git a/modules/pool/lib/apr-data-sources/ib-linear-apr-handlers/sources/abis/maker-pot.ts b/modules/pool/lib/apr-data-sources/yb-apr-handlers/sources/abis/maker-pot.ts similarity index 100% rename from modules/pool/lib/apr-data-sources/ib-linear-apr-handlers/sources/abis/maker-pot.ts rename to modules/pool/lib/apr-data-sources/yb-apr-handlers/sources/abis/maker-pot.ts diff --git a/modules/pool/lib/apr-data-sources/ib-linear-apr-handlers/sources/abis/oErc20.ts b/modules/pool/lib/apr-data-sources/yb-apr-handlers/sources/abis/oErc20.ts similarity index 100% rename from modules/pool/lib/apr-data-sources/ib-linear-apr-handlers/sources/abis/oErc20.ts rename to modules/pool/lib/apr-data-sources/yb-apr-handlers/sources/abis/oErc20.ts diff --git a/modules/pool/lib/apr-data-sources/ib-linear-apr-handlers/sources/abis/reaperStrategy.ts b/modules/pool/lib/apr-data-sources/yb-apr-handlers/sources/abis/reaperStrategy.ts similarity index 100% rename from modules/pool/lib/apr-data-sources/ib-linear-apr-handlers/sources/abis/reaperStrategy.ts rename to modules/pool/lib/apr-data-sources/yb-apr-handlers/sources/abis/reaperStrategy.ts diff --git a/modules/pool/lib/apr-data-sources/ib-linear-apr-handlers/sources/abis/tesseraPool.ts b/modules/pool/lib/apr-data-sources/yb-apr-handlers/sources/abis/tesseraPool.ts similarity index 100% rename from modules/pool/lib/apr-data-sources/ib-linear-apr-handlers/sources/abis/tesseraPool.ts rename to modules/pool/lib/apr-data-sources/yb-apr-handlers/sources/abis/tesseraPool.ts diff --git a/modules/pool/lib/apr-data-sources/ib-linear-apr-handlers/sources/beefy-apr-handler.ts b/modules/pool/lib/apr-data-sources/yb-apr-handlers/sources/beefy-apr-handler.ts similarity index 92% rename from modules/pool/lib/apr-data-sources/ib-linear-apr-handlers/sources/beefy-apr-handler.ts rename to modules/pool/lib/apr-data-sources/yb-apr-handlers/sources/beefy-apr-handler.ts index 98e44bea3..147f55ea2 100644 --- a/modules/pool/lib/apr-data-sources/ib-linear-apr-handlers/sources/beefy-apr-handler.ts +++ b/modules/pool/lib/apr-data-sources/yb-apr-handlers/sources/beefy-apr-handler.ts @@ -1,5 +1,5 @@ import { BeefyAprConfig } from '../../../../../network/apr-config-types'; -import { AprHandler } from '../ib-linear-apr-handlers'; +import { AprHandler } from '..'; import axios from 'axios'; import * as Sentry from '@sentry/node'; @@ -28,8 +28,8 @@ export class BeefyAprHandler implements AprHandler { [address]: { apr, isIbYield: isIbYield ?? false, - group: this.group - } + group: this.group, + }, }; }); diff --git a/modules/pool/lib/apr-data-sources/ib-linear-apr-handlers/sources/bloom-apr-handler.ts b/modules/pool/lib/apr-data-sources/yb-apr-handlers/sources/bloom-apr-handler.ts similarity index 89% rename from modules/pool/lib/apr-data-sources/ib-linear-apr-handlers/sources/bloom-apr-handler.ts rename to modules/pool/lib/apr-data-sources/yb-apr-handlers/sources/bloom-apr-handler.ts index 797cb390b..3dc7c7443 100644 --- a/modules/pool/lib/apr-data-sources/ib-linear-apr-handlers/sources/bloom-apr-handler.ts +++ b/modules/pool/lib/apr-data-sources/yb-apr-handlers/sources/bloom-apr-handler.ts @@ -1,11 +1,11 @@ -import { AprHandler } from '../ib-linear-apr-handlers'; +import { AprHandler } from '..'; import { BloomAprConfig } from '../../../../../network/apr-config-types'; import { getContractAt } from '../../../../../web3/contract'; import { abi as bloomBpsFeed } from './abis/bloom-bps-feed'; import * as Sentry from '@sentry/node'; export class BloomAprHandler implements AprHandler { - group = "DEFAULT"; + group = 'DEFAULT'; tokens: BloomAprConfig['tokens']; @@ -14,7 +14,7 @@ export class BloomAprHandler implements AprHandler { } async getAprs() { - const aprs: { [p: string]: { apr: number; isIbYield: boolean, group?: string } } = {}; + const aprs: { [p: string]: { apr: number; isIbYield: boolean; group?: string } } = {}; for (const { address, feedAddress, isIbYield } of Object.values(this.tokens)) { try { const feedContract = getContractAt(feedAddress, bloomBpsFeed); @@ -26,7 +26,7 @@ export class BloomAprHandler implements AprHandler { aprs[address] = { apr: tokenApr, isIbYield: isIbYield ?? false, - group: this.group + group: this.group, }; } catch (error) { console.error(`Bloom APR Failed for token ${address}: `, error); diff --git a/modules/pool/lib/apr-data-sources/ib-linear-apr-handlers/sources/default-apr-handler.ts b/modules/pool/lib/apr-data-sources/yb-apr-handlers/sources/default-apr-handler.ts similarity index 71% rename from modules/pool/lib/apr-data-sources/ib-linear-apr-handlers/sources/default-apr-handler.ts rename to modules/pool/lib/apr-data-sources/yb-apr-handlers/sources/default-apr-handler.ts index 3e393f780..839e8c518 100644 --- a/modules/pool/lib/apr-data-sources/ib-linear-apr-handlers/sources/default-apr-handler.ts +++ b/modules/pool/lib/apr-data-sources/yb-apr-handlers/sources/default-apr-handler.ts @@ -1,6 +1,6 @@ import axios from 'axios'; -import { AprHandler } from '../ib-linear-apr-handlers'; +import { AprHandler } from '..'; import * as Sentry from '@sentry/node'; export class DefaultAprHandler implements AprHandler { @@ -37,8 +37,8 @@ export class DefaultAprHandler implements AprHandler { [this.tokenAddress]: { apr: scaledValue, isIbYield: this.isIbYield ?? false, - group: this.group - } + group: this.group, + }, }; } catch (error) { console.error(`Failed to fetch APRs in url ${this.url}:`, error); @@ -47,15 +47,20 @@ export class DefaultAprHandler implements AprHandler { } } - getValueFromPath(obj: any, path: string) { - if (path === '') { - return obj; - } + // Get a specific value from a JSON object based on a path + getValueFromPath = (obj: any, path: string) => { const parts = path.split('.'); let value = obj; for (const part of parts) { - value = value[part]; + if (part[0] === '{' && part[part.length - 1] === '}') { + const selector = part.slice(1, -1); + const variableName = selector.split('==')[0].trim(); + const variableValue = selector.split('==')[1].trim().replace(/"/g, ''); + value = value.find((v: any) => v[variableName] === variableValue); + } else { + value = value[part]; + } } return value; - } + }; } diff --git a/modules/pool/lib/apr-data-sources/yb-apr-handlers/sources/etherfi-apr-handler.ts b/modules/pool/lib/apr-data-sources/yb-apr-handlers/sources/etherfi-apr-handler.ts new file mode 100644 index 000000000..5d646cc01 --- /dev/null +++ b/modules/pool/lib/apr-data-sources/yb-apr-handlers/sources/etherfi-apr-handler.ts @@ -0,0 +1,50 @@ +const url = 'https://api.studio.thegraph.com/proxy/41778/syko-dev-mainnet/v0.0.3'; + +const query = ` + { + rebaseEventLinkedLists { + latest_aprs + } + } +`; + +const requestQuery = { + query, +}; + +interface Response { + data: { + rebaseEventLinkedLists: { + latest_aprs: string[]; + }[]; + }; +} + +export class Etherfi { + constructor(private tokenAddress: string) {} + + async getAprs() { + const response = await fetch(url, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify(requestQuery), + }); + + const { + data: { + rebaseEventLinkedLists: [{ latest_aprs }], + }, + } = (await response.json()) as Response; + + const avgApr = latest_aprs.map((apr) => Number(apr)).reduce((acc, apr) => acc + apr, 0) / latest_aprs.length; + + return { + [this.tokenAddress]: { + apr: avgApr / 10000, + isIbYield: true, + }, + }; + } +} diff --git a/modules/pool/lib/apr-data-sources/ib-linear-apr-handlers/sources/euler-apr-handler.ts b/modules/pool/lib/apr-data-sources/yb-apr-handlers/sources/euler-apr-handler.ts similarity index 97% rename from modules/pool/lib/apr-data-sources/ib-linear-apr-handlers/sources/euler-apr-handler.ts rename to modules/pool/lib/apr-data-sources/yb-apr-handlers/sources/euler-apr-handler.ts index 960954f78..25310f5f6 100644 --- a/modules/pool/lib/apr-data-sources/ib-linear-apr-handlers/sources/euler-apr-handler.ts +++ b/modules/pool/lib/apr-data-sources/yb-apr-handlers/sources/euler-apr-handler.ts @@ -1,5 +1,5 @@ import axios from 'axios'; -import { AprHandler } from '../ib-linear-apr-handlers'; +import { AprHandler } from '..'; import { EulerAprConfig } from '../../../../../network/apr-config-types'; import * as Sentry from '@sentry/node'; diff --git a/modules/pool/lib/apr-data-sources/ib-linear-apr-handlers/sources/gearbox-apr-handler.ts b/modules/pool/lib/apr-data-sources/yb-apr-handlers/sources/gearbox-apr-handler.ts similarity index 96% rename from modules/pool/lib/apr-data-sources/ib-linear-apr-handlers/sources/gearbox-apr-handler.ts rename to modules/pool/lib/apr-data-sources/yb-apr-handlers/sources/gearbox-apr-handler.ts index 490d6f441..505dd69b8 100644 --- a/modules/pool/lib/apr-data-sources/ib-linear-apr-handlers/sources/gearbox-apr-handler.ts +++ b/modules/pool/lib/apr-data-sources/yb-apr-handlers/sources/gearbox-apr-handler.ts @@ -1,7 +1,7 @@ import axios from 'axios'; import * as Sentry from '@sentry/node'; -import { AprHandler } from '../ib-linear-apr-handlers'; +import { AprHandler } from '..'; import { GearBoxAprConfig } from '../../../../../network/apr-config-types'; export class GearboxAprHandler implements AprHandler { diff --git a/modules/pool/lib/apr-data-sources/ib-linear-apr-handlers/sources/idle-apr-handler.ts b/modules/pool/lib/apr-data-sources/yb-apr-handlers/sources/idle-apr-handler.ts similarity index 84% rename from modules/pool/lib/apr-data-sources/ib-linear-apr-handlers/sources/idle-apr-handler.ts rename to modules/pool/lib/apr-data-sources/yb-apr-handlers/sources/idle-apr-handler.ts index 24857b410..95128ee15 100644 --- a/modules/pool/lib/apr-data-sources/ib-linear-apr-handlers/sources/idle-apr-handler.ts +++ b/modules/pool/lib/apr-data-sources/yb-apr-handlers/sources/idle-apr-handler.ts @@ -1,6 +1,6 @@ import axios from 'axios'; -import { AprHandler } from '../ib-linear-apr-handlers'; +import { AprHandler } from '..'; import { IdleAprConfig } from '../../../../../network/apr-config-types'; import * as Sentry from '@sentry/node'; @@ -32,11 +32,14 @@ export class IdleAprHandler implements AprHandler { }); const [json] = data as { idleRate: string }[]; const value = Number(json.idleRate) / 1e20; - return [wrapped4626Address, { - apr: value, - isIbYield: isIbYield ?? false, - group: this.group, - }]; + return [ + wrapped4626Address, + { + apr: value, + isIbYield: isIbYield ?? false, + group: this.group, + }, + ]; }); const res = Array(Object.keys(this.tokens).length); for (const [index, aprPromise] of aprPromises.entries()) { diff --git a/modules/pool/lib/apr-data-sources/ib-linear-apr-handlers/sources/index.ts b/modules/pool/lib/apr-data-sources/yb-apr-handlers/sources/index.ts similarity index 88% rename from modules/pool/lib/apr-data-sources/ib-linear-apr-handlers/sources/index.ts rename to modules/pool/lib/apr-data-sources/yb-apr-handlers/sources/index.ts index 19f3c7453..74bb6844d 100644 --- a/modules/pool/lib/apr-data-sources/ib-linear-apr-handlers/sources/index.ts +++ b/modules/pool/lib/apr-data-sources/yb-apr-handlers/sources/index.ts @@ -1,5 +1,4 @@ export * from './aave-apr-handler'; -export * from './ankr-apr-handler'; export * from './sftmx-apr-handler'; export * from './default-apr-handler'; export * from './euler-apr-handler'; @@ -15,3 +14,5 @@ export * from './beefy-apr-handler'; export * from './maker-apr-handler'; export * as MakerGnosis from './maker-gnosis-apr-handler'; export * from './bloom-apr-handler'; +export * from './stakewise-apr-handler'; +export * from './etherfi-apr-handler'; diff --git a/modules/pool/lib/apr-data-sources/ib-linear-apr-handlers/sources/maker-apr-handler.ts b/modules/pool/lib/apr-data-sources/yb-apr-handlers/sources/maker-apr-handler.ts similarity index 92% rename from modules/pool/lib/apr-data-sources/ib-linear-apr-handlers/sources/maker-apr-handler.ts rename to modules/pool/lib/apr-data-sources/yb-apr-handlers/sources/maker-apr-handler.ts index 0b47bf9be..6a8885e3d 100644 --- a/modules/pool/lib/apr-data-sources/ib-linear-apr-handlers/sources/maker-apr-handler.ts +++ b/modules/pool/lib/apr-data-sources/yb-apr-handlers/sources/maker-apr-handler.ts @@ -1,4 +1,4 @@ -import { AprHandler } from '../ib-linear-apr-handlers'; +import { AprHandler } from '..'; import { MakerAprConfig } from '../../../../../network/apr-config-types'; import { getContractAt } from '../../../../../web3/contract'; import { abi as makerPotAbi } from './abis/maker-pot'; @@ -19,7 +19,7 @@ export class MakerAprHandler implements AprHandler { } async getAprs() { - const aprs: { [p: string]: { apr: number; isIbYield: boolean, group: string } } = {}; + const aprs: { [p: string]: { apr: number; isIbYield: boolean; group: string } } = {}; for (const { address, potAddress, isIbYield } of Object.values(this.tokens)) { try { const potContract = getContractAt(potAddress, makerPotAbi); @@ -31,7 +31,7 @@ export class MakerAprHandler implements AprHandler { aprs[address] = { apr: tokenApr, isIbYield: isIbYield ?? false, - group: this.group + group: this.group, }; } catch (error) { console.error(`Maker APR Failed for token ${address}: `, error); diff --git a/modules/pool/lib/apr-data-sources/yb-apr-handlers/sources/maker-gnosis-apr-handler.ts b/modules/pool/lib/apr-data-sources/yb-apr-handlers/sources/maker-gnosis-apr-handler.ts new file mode 100644 index 000000000..b52955fd7 --- /dev/null +++ b/modules/pool/lib/apr-data-sources/yb-apr-handlers/sources/maker-gnosis-apr-handler.ts @@ -0,0 +1,36 @@ +import { AprHandler } from '..'; +import { getContractAt } from '../../../../../web3/contract'; +import { Chain } from '@prisma/client'; + +const helperAbi = ['function vaultAPY() view returns (uint256)']; + +/** Sets the config data used internally */ +const config = { + GNOSIS: { + sdaiAddress: '0xaf204776c7245bf4147c2612bf6e5972ee483701', + helperAddress: '0xd499b51fcfc66bd31248ef4b28d656d67e591a94', + }, +}; + +/** Makes handler callable by chain */ +export const chains = Object.keys(config) as Chain[]; + +export class Handler implements AprHandler { + async getAprs(chain: Chain) { + if (chain !== 'GNOSIS') { + throw `Handler supports GNOSIS only, but called for ${chain}`; + } + + const helper = getContractAt(config[chain].helperAddress, helperAbi); + const vaultAPY = await helper.vaultAPY(); + const apr = Number(vaultAPY) * 10 ** -18; + + return { + [config[chain].sdaiAddress]: { + apr, + isIbYield: true, + group: 'MAKER', + }, + }; + } +} diff --git a/modules/pool/lib/apr-data-sources/ib-linear-apr-handlers/sources/ovix-apr-handler.ts b/modules/pool/lib/apr-data-sources/yb-apr-handlers/sources/ovix-apr-handler.ts similarity index 96% rename from modules/pool/lib/apr-data-sources/ib-linear-apr-handlers/sources/ovix-apr-handler.ts rename to modules/pool/lib/apr-data-sources/yb-apr-handlers/sources/ovix-apr-handler.ts index a8206b52d..a96288f72 100644 --- a/modules/pool/lib/apr-data-sources/ib-linear-apr-handlers/sources/ovix-apr-handler.ts +++ b/modules/pool/lib/apr-data-sources/yb-apr-handlers/sources/ovix-apr-handler.ts @@ -2,7 +2,7 @@ import { BigNumber, Contract } from 'ethers'; import { abi } from './abis/oErc20'; import * as Sentry from '@sentry/node'; -import { AprHandler } from '../ib-linear-apr-handlers'; +import { AprHandler } from '..'; import { networkContext } from '../../../../../network/network-context.service'; import { OvixAprConfig } from '../../../../../network/apr-config-types'; diff --git a/modules/pool/lib/apr-data-sources/ib-linear-apr-handlers/sources/reaper-crypt-apr-handler.ts b/modules/pool/lib/apr-data-sources/yb-apr-handlers/sources/reaper-crypt-apr-handler.ts similarity index 99% rename from modules/pool/lib/apr-data-sources/ib-linear-apr-handlers/sources/reaper-crypt-apr-handler.ts rename to modules/pool/lib/apr-data-sources/yb-apr-handlers/sources/reaper-crypt-apr-handler.ts index 6acf75005..d454deb16 100644 --- a/modules/pool/lib/apr-data-sources/ib-linear-apr-handlers/sources/reaper-crypt-apr-handler.ts +++ b/modules/pool/lib/apr-data-sources/yb-apr-handlers/sources/reaper-crypt-apr-handler.ts @@ -1,4 +1,4 @@ -import { AprHandler } from '../ib-linear-apr-handlers'; +import { AprHandler } from '..'; import { getContractAt } from '../../../../../web3/contract'; import ReaperCryptStrategyAbi from './abis/ReaperCryptStrategy.json'; import axios from 'axios'; diff --git a/modules/pool/lib/apr-data-sources/ib-linear-apr-handlers/sources/sftmx-apr-handler.ts b/modules/pool/lib/apr-data-sources/yb-apr-handlers/sources/sftmx-apr-handler.ts similarity index 98% rename from modules/pool/lib/apr-data-sources/ib-linear-apr-handlers/sources/sftmx-apr-handler.ts rename to modules/pool/lib/apr-data-sources/yb-apr-handlers/sources/sftmx-apr-handler.ts index 6c805269e..49c590d32 100644 --- a/modules/pool/lib/apr-data-sources/ib-linear-apr-handlers/sources/sftmx-apr-handler.ts +++ b/modules/pool/lib/apr-data-sources/yb-apr-handlers/sources/sftmx-apr-handler.ts @@ -1,5 +1,5 @@ import * as Sentry from '@sentry/node'; -import { AprHandler } from '../ib-linear-apr-handlers'; +import { AprHandler } from '..'; import { SftmxAprConfig } from '../../../../../network/apr-config-types'; import { BigNumber, ethers } from 'ethers'; import { getContractAt } from '../../../../../web3/contract'; diff --git a/modules/pool/lib/apr-data-sources/yb-apr-handlers/sources/stakewise-apr-handler.ts b/modules/pool/lib/apr-data-sources/yb-apr-handlers/sources/stakewise-apr-handler.ts new file mode 100644 index 000000000..ed87a7266 --- /dev/null +++ b/modules/pool/lib/apr-data-sources/yb-apr-handlers/sources/stakewise-apr-handler.ts @@ -0,0 +1,48 @@ +const url = 'https://mainnet-graph.stakewise.io/subgraphs/name/stakewise/stakewise'; + +const query = ` + { + osTokens { + apy + } + } +`; + +const requestQuery = { + query, +}; + +interface Response { + data: { + osTokens: { + apy: string[]; + }[]; + }; +} + +export class Stakewise { + constructor(private tokenAddress: string) {} + + async getAprs() { + const response = await fetch(url, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify(requestQuery), + }); + + const { + data: { + osTokens: [{ apy }], + }, + } = (await response.json()) as Response; + + return { + [this.tokenAddress]: { + apr: Number(apy) / 100, + isIbYield: true, + }, + }; + } +} diff --git a/modules/pool/lib/apr-data-sources/ib-linear-apr-handlers/sources/tessera-apr-handler.ts b/modules/pool/lib/apr-data-sources/yb-apr-handlers/sources/tessera-apr-handler.ts similarity index 84% rename from modules/pool/lib/apr-data-sources/ib-linear-apr-handlers/sources/tessera-apr-handler.ts rename to modules/pool/lib/apr-data-sources/yb-apr-handlers/sources/tessera-apr-handler.ts index f1d67f18f..ff3e94721 100644 --- a/modules/pool/lib/apr-data-sources/ib-linear-apr-handlers/sources/tessera-apr-handler.ts +++ b/modules/pool/lib/apr-data-sources/yb-apr-handlers/sources/tessera-apr-handler.ts @@ -1,7 +1,7 @@ import { Contract } from 'ethers'; import { abi } from './abis/tesseraPool'; -import { AprHandler } from '../ib-linear-apr-handlers'; +import { AprHandler } from '..'; import { networkContext } from '../../../../../network/network-context.service'; import { TesseraAprConfig } from '../../../../../network/apr-config-types'; import * as Sentry from '@sentry/node'; @@ -31,11 +31,14 @@ export class TesseraAprHandler implements AprHandler { const staked = BigInt(pool.stakedAmount); const reward = BigInt(pool.currentTimeRange.rewardsPerHour) * BigInt(24 * 365); const apr = Number(reward.toString()) / Number(staked.toString()); - aprEntries.push([tokenAddress, { - apr, - isIbYield: isIbYield ?? false, - group: this.group - }]); + aprEntries.push([ + tokenAddress, + { + apr, + isIbYield: isIbYield ?? false, + group: this.group, + }, + ]); } catch (error) { console.error('Failed to fetch Tessera Ape Coin APR:', error); Sentry.captureException(`Tessera IB APR handler failed: ${error}`); diff --git a/modules/pool/lib/apr-data-sources/ib-linear-apr-handlers/sources/tetu-apr-handler.ts b/modules/pool/lib/apr-data-sources/yb-apr-handlers/sources/tetu-apr-handler.ts similarity index 96% rename from modules/pool/lib/apr-data-sources/ib-linear-apr-handlers/sources/tetu-apr-handler.ts rename to modules/pool/lib/apr-data-sources/yb-apr-handlers/sources/tetu-apr-handler.ts index d42480aac..bb281341f 100644 --- a/modules/pool/lib/apr-data-sources/ib-linear-apr-handlers/sources/tetu-apr-handler.ts +++ b/modules/pool/lib/apr-data-sources/yb-apr-handlers/sources/tetu-apr-handler.ts @@ -1,6 +1,6 @@ import axios from 'axios'; -import { AprHandler } from '../ib-linear-apr-handlers'; +import { AprHandler } from '..'; import { TetuAprConfig } from '../../../../../network/apr-config-types'; import * as Sentry from '@sentry/node'; diff --git a/modules/pool/lib/apr-data-sources/ib-linear-apr-handlers/sources/tranchess-apr-handler.ts b/modules/pool/lib/apr-data-sources/yb-apr-handlers/sources/tranchess-apr-handler.ts similarity index 94% rename from modules/pool/lib/apr-data-sources/ib-linear-apr-handlers/sources/tranchess-apr-handler.ts rename to modules/pool/lib/apr-data-sources/yb-apr-handlers/sources/tranchess-apr-handler.ts index aec8047ee..e60c21bc3 100644 --- a/modules/pool/lib/apr-data-sources/ib-linear-apr-handlers/sources/tranchess-apr-handler.ts +++ b/modules/pool/lib/apr-data-sources/yb-apr-handlers/sources/tranchess-apr-handler.ts @@ -1,6 +1,6 @@ import axios from 'axios'; -import { AprHandler } from '../ib-linear-apr-handlers'; +import { AprHandler } from '..'; import { TranchessAprConfig } from '../../../../../network/apr-config-types'; import * as Sentry from '@sentry/node'; @@ -33,7 +33,7 @@ export class TranchessAprHandler implements AprHandler { { apr: (365 * Number(weeklyAveragePnlPercentage)) / 1e18, isIbYield: isIbYield ?? false, - group: this.group + group: this.group, }, ]; }); diff --git a/modules/pool/lib/apr-data-sources/ib-linear-apr-handlers/sources/yearn-apr-handler.ts b/modules/pool/lib/apr-data-sources/yb-apr-handlers/sources/yearn-apr-handler.ts similarity index 76% rename from modules/pool/lib/apr-data-sources/ib-linear-apr-handlers/sources/yearn-apr-handler.ts rename to modules/pool/lib/apr-data-sources/yb-apr-handlers/sources/yearn-apr-handler.ts index ac519fad0..756bbcaba 100644 --- a/modules/pool/lib/apr-data-sources/ib-linear-apr-handlers/sources/yearn-apr-handler.ts +++ b/modules/pool/lib/apr-data-sources/yb-apr-handlers/sources/yearn-apr-handler.ts @@ -1,4 +1,4 @@ -import { AprHandler } from '../ib-linear-apr-handlers'; +import { AprHandler } from '..'; import axios from 'axios'; import { YearnAprConfig } from '../../../../../network/apr-config-types'; import * as Sentry from '@sentry/node'; @@ -17,11 +17,14 @@ export class YearnAprHandler implements AprHandler { const { data } = await axios.get(this.sourceUrl); const aprs = Object.fromEntries( data.map(({ address, apy: { net_apy } }) => { - return [address.toLowerCase(), { - apr: net_apy, - isIbYield: this.isIbYield ?? false, - group: this.group - }]; + return [ + address.toLowerCase(), + { + apr: net_apy, + isIbYield: this.isIbYield ?? false, + group: this.group, + }, + ]; }), ); return aprs; diff --git a/modules/pool/lib/apr-data-sources/ib-tokens-apr.service.ts b/modules/pool/lib/apr-data-sources/yb-tokens-apr.service.ts similarity index 84% rename from modules/pool/lib/apr-data-sources/ib-tokens-apr.service.ts rename to modules/pool/lib/apr-data-sources/yb-tokens-apr.service.ts index d689eecd7..d7ad92276 100644 --- a/modules/pool/lib/apr-data-sources/ib-tokens-apr.service.ts +++ b/modules/pool/lib/apr-data-sources/yb-tokens-apr.service.ts @@ -3,32 +3,32 @@ import { PrismaPoolWithTokens } from '../../../../prisma/prisma-types'; import { prisma } from '../../../../prisma/prisma-client'; import { prismaBulkExecuteOperations } from '../../../../prisma/prisma-util'; import { Chain, PrismaPoolAprItemGroup, PrismaPoolAprType, PrismaPoolLinearData } from '@prisma/client'; -import { IbLinearAprHandlers as IbTokensAprHandlers, TokenApr } from './ib-linear-apr-handlers/ib-linear-apr-handlers'; +import { YbAprHandlers, TokenApr } from './yb-apr-handlers'; import { tokenService } from '../../../token/token.service'; import { collectsYieldFee } from '../pool-utils'; -import { IbAprConfig } from '../../../network/apr-config-types'; +import { YbAprConfig } from '../../../network/apr-config-types'; -export class IbTokensAprService implements PoolAprService { - private ibTokensAprHandlers: IbTokensAprHandlers; +export class YbTokensAprService implements PoolAprService { + private ybTokensAprHandlers: YbAprHandlers; constructor( - aprConfig: IbAprConfig, + aprConfig: YbAprConfig, private chain: Chain, private defaultYieldFee: number, - private defaultSwapFee: number + private defaultSwapFee: number, ) { - this.ibTokensAprHandlers = new IbTokensAprHandlers(aprConfig, chain); + this.ybTokensAprHandlers = new YbAprHandlers(aprConfig, chain); } getAprServiceName(): string { - return 'IbTokensAprService'; + return 'YbTokensAprService'; } public async updateAprForPools(pools: PrismaPoolWithTokens[]): Promise { const operations: any[] = []; const tokenPrices = await tokenService.getTokenPrices(); const aprs = await this.fetchYieldTokensApr(); - const poolsWithIbTokens = pools.filter((pool) => { + const poolsWithYbTokens = pools.filter((pool) => { return pool.tokens.find((token) => { return Array.from(aprs.keys()) .map((key) => key.toLowerCase()) @@ -36,8 +36,8 @@ export class IbTokensAprService implements PoolAprService { }); }); - const poolsWithIbTokensExpanded = await prisma.prismaPool.findMany({ - where: { chain: this.chain, id: { in: poolsWithIbTokens.map((pool) => pool.id) } }, + const poolsWithYbTokensExpanded = await prisma.prismaPool.findMany({ + where: { chain: this.chain, id: { in: poolsWithYbTokens.map((pool) => pool.id) } }, include: { dynamicData: true, tokens: { @@ -50,7 +50,7 @@ export class IbTokensAprService implements PoolAprService { }, }); - for (const pool of poolsWithIbTokensExpanded) { + for (const pool of poolsWithYbTokensExpanded) { if (!pool.dynamicData) { continue; } @@ -115,7 +115,7 @@ export class IbTokensAprService implements PoolAprService { } private async fetchYieldTokensApr(): Promise> { - const data = await this.ibTokensAprHandlers.fetchAprsFromAllHandlers(); + const data = await this.ybTokensAprHandlers.fetchAprsFromAllHandlers(); return new Map( data .filter((tokenApr) => { From eb9c8aeeda514752e2c983f753364ce2c0409428 Mon Sep 17 00:00:00 2001 From: franzns <93920061+franzns@users.noreply.github.com> Date: Fri, 12 Jan 2024 15:44:31 +0100 Subject: [PATCH 04/15] removing sentry integrations (#36) --- app.ts | 6 +++--- worker/worker.ts | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/app.ts b/app.ts index 6151bbaff..2a4071599 100644 --- a/app.ts +++ b/app.ts @@ -31,9 +31,9 @@ async function startServer() { enabled: env.NODE_ENV === 'production', ignoreErrors: [/.*error: Provide.*chain.*param/], integrations: [ - new Sentry.Integrations.Apollo(), - new Sentry.Integrations.GraphQL(), - new Sentry.Integrations.Prisma({ client: prisma }), + // new Sentry.Integrations.Apollo(), + // new Sentry.Integrations.GraphQL(), + // new Sentry.Integrations.Prisma({ client: prisma }), new Sentry.Integrations.Express({ app }), new Sentry.Integrations.Http({ tracing: true }), new ProfilingIntegration(), diff --git a/worker/worker.ts b/worker/worker.ts index 375a8095a..9ef3cf5bb 100644 --- a/worker/worker.ts +++ b/worker/worker.ts @@ -13,10 +13,10 @@ export async function startWorker() { environment: `multichain-worker-${env.DEPLOYMENT_ENV}`, enabled: env.NODE_ENV === 'production', integrations: [ - new Sentry.Integrations.Apollo(), - new Sentry.Integrations.GraphQL(), - new Sentry.Integrations.Prisma({ client: prisma }), - new Sentry.Integrations.Express({ app }), + // new Sentry.Integrations.Apollo(), + // new Sentry.Integrations.GraphQL(), + // new Sentry.Integrations.Prisma({ client: prisma }), + // new Sentry.Integrations.Express({ app }), new Sentry.Integrations.Http({ tracing: true }), new ProfilingIntegration(), ], From ce5249047fbf06f1bb8d47877a446445ab87ee0b Mon Sep 17 00:00:00 2001 From: franzns <93920061+franzns@users.noreply.github.com> Date: Fri, 12 Jan 2024 15:44:44 +0100 Subject: [PATCH 05/15] create separate featuredPools endpoint for v3 (#34) * create separate featuredPools endpoint for v3 * cleanup * require chains param --- modules/content/content-types.ts | 9 +++- modules/content/github-content.service.ts | 24 ++++----- modules/content/sanity-content.service.ts | 57 ++++++++++++++++++--- modules/pool/lib/pool-gql-loader.service.ts | 34 ++++++++++-- modules/pool/pool.gql | 9 +++- modules/pool/pool.resolvers.ts | 3 ++ modules/pool/pool.service.ts | 5 ++ 7 files changed, 110 insertions(+), 31 deletions(-) diff --git a/modules/content/content-types.ts b/modules/content/content-types.ts index 4e4e65df7..8d0051851 100644 --- a/modules/content/content-types.ts +++ b/modules/content/content-types.ts @@ -13,8 +13,6 @@ export interface HomeScreenFeaturedPoolGroup { id: string; items: (HomeScreenFeaturedPoolGroupItemPoolId | HomeScreenFeaturedPoolGroupItemExternalLink)[]; title: string; - primary: boolean; - chain: GqlChain; } interface HomeScreenFeaturedPoolGroupItemPoolId { @@ -32,6 +30,12 @@ interface HomeScreenFeaturedPoolGroupItemExternalLink { image: string; } +export interface FeaturedPool { + poolId: string; + primary: boolean; + chain: GqlChain; +} + export interface HomeScreenNewsItem { id: string; timestamp: string; @@ -45,5 +49,6 @@ export interface ContentService { syncTokenContentData(): Promise; syncPoolContentData(): Promise; getFeaturedPoolGroups(chains: Chain[]): Promise; + getFeaturedPools(chains: Chain[]): Promise; getNewsItems(): Promise; } diff --git a/modules/content/github-content.service.ts b/modules/content/github-content.service.ts index 578db0eea..ac798a5d5 100644 --- a/modules/content/github-content.service.ts +++ b/modules/content/github-content.service.ts @@ -3,7 +3,7 @@ import { Chain, Prisma } from '@prisma/client'; import axios from 'axios'; import { prisma } from '../../prisma/prisma-client'; import { networkContext } from '../network/network-context.service'; -import { ContentService, HomeScreenFeaturedPoolGroup, HomeScreenNewsItem } from './content-types'; +import { ContentService, FeaturedPool, HomeScreenFeaturedPoolGroup, HomeScreenNewsItem } from './content-types'; import { chainIdToChain } from '../network/network-config'; const POOLS_METADATA_URL = 'https://raw.githubusercontent.com/balancer/metadata/main/pools/featured.json'; @@ -168,25 +168,21 @@ export class GithubContentService implements ContentService { await prisma.prismaTokenType.createMany({ skipDuplicates: true, data: types }); } async syncPoolContentData(): Promise {} + async getFeaturedPoolGroups(chains: Chain[]): Promise { + return []; + } + + async getFeaturedPools(chains: Chain[]): Promise { const { data } = await axios.get(POOLS_METADATA_URL); const pools = data.filter((pool) => chains.includes(chainIdToChain[pool.chainId])); - return pools.map(({ id, imageUrl, primary, chainId }) => ({ - id, - _type: 'homeScreenFeaturedPoolGroupPoolId', - title: 'Popular pools', - items: [ - { - _key: '', - _type: 'homeScreenFeaturedPoolGroupPoolId', - poolId: id, - }, - ], - icon: imageUrl, + return pools.map(({ id, primary, chainId }) => ({ + poolId: id, chain: chainIdToChain[chainId], primary: Boolean(primary), - })) as HomeScreenFeaturedPoolGroup[]; + })) as FeaturedPool[]; } + async getNewsItems(): Promise { return []; } diff --git a/modules/content/sanity-content.service.ts b/modules/content/sanity-content.service.ts index d48fef8aa..e512718e9 100644 --- a/modules/content/sanity-content.service.ts +++ b/modules/content/sanity-content.service.ts @@ -1,7 +1,13 @@ import { isSameAddress } from '@balancer-labs/sdk'; import { Chain, Prisma, PrismaPoolCategoryType } from '@prisma/client'; import { prisma } from '../../prisma/prisma-client'; -import { ConfigHomeScreen, ContentService, HomeScreenFeaturedPoolGroup, HomeScreenNewsItem } from './content-types'; +import { + ConfigHomeScreen, + ContentService, + FeaturedPool, + HomeScreenFeaturedPoolGroup, + HomeScreenNewsItem, +} from './content-types'; import SanityClient from '@sanity/client'; import { env } from '../../app/env'; import { chainToIdMap } from '../network/network-config'; @@ -279,18 +285,53 @@ export class SanityContentService implements ContentService { } `); if (data) { - featuredPoolGroups.push( - ...data.featuredPoolGroups.map((pool, i) => ({ - ...pool, - chain: chain, - primary: i === 0 ? true : false, - })), - ); + featuredPoolGroups.push(...data.featuredPoolGroups); } } return featuredPoolGroups; } + public async getFeaturedPools(chains: Chain[]): Promise { + const featuredPools: FeaturedPool[] = []; + for (const chain of chains) { + const data = await this.getSanityClient().fetch(` + *[_type == "homeScreen" && chainId == ${chainToIdMap[chain]}][0]{ + ..., + "featuredPoolGroups": featuredPoolGroups[]{ + ..., + "icon": icon.asset->url + "?w=64", + "items": items[]{ + ..., + "image": image.asset->url + "?w=600" + } + }, + "newsItems": newsItems[]{ + ..., + "image": image.asset->url + "?w=800" + } + } + `); + if (data) { + const featuredPoolGroupItems = data.featuredPoolGroups.find( + (group) => group.id === 'popular-pools', + )?.items; + if (featuredPoolGroupItems) { + for (let i = 0; i < featuredPoolGroupItems.length; i++) { + const group = featuredPoolGroupItems[i]; + if (group._type === 'homeScreenFeaturedPoolGroupPoolId') { + featuredPools.push({ + poolId: group.poolId, + primary: i === 0 ? true : false, + chain: chain, + }); + } + } + } + } + } + return featuredPools; + } + public async getNewsItems(): Promise { const data = await this.getSanityClient().fetch(` *[_type == "homeScreen" && chainId == ${chainToIdMap[this.chain]}][0]{ diff --git a/modules/pool/lib/pool-gql-loader.service.ts b/modules/pool/lib/pool-gql-loader.service.ts index 6b98fae01..fac3005a0 100644 --- a/modules/pool/lib/pool-gql-loader.service.ts +++ b/modules/pool/lib/pool-gql-loader.service.ts @@ -13,6 +13,7 @@ import { GqlBalancePoolAprSubItem, GqlPoolDynamicData, GqlPoolFeaturedPoolGroup, + GqlPoolFeaturedPool, GqlPoolGyro, GqlPoolInvestConfig, GqlPoolInvestOption, @@ -44,8 +45,8 @@ import { parseUnits } from 'ethers/lib/utils'; import { formatFixed } from '@ethersproject/bignumber'; import { BalancerChainIds, BeethovenChainIds, chainIdToChain, chainToIdMap } from '../../network/network-config'; import { GithubContentService } from '../../content/github-content.service'; -import SanityClientConstructor from '@sanity/client'; import { SanityContentService } from '../../content/sanity-content.service'; +import { FeaturedPool } from '../../content/content-types'; export class PoolGqlLoaderService { public async getPool(id: string, chain: Chain, userAddress?: string): Promise { @@ -160,10 +161,7 @@ export class PoolGqlLoaderService { public async getFeaturedPoolGroups(chains: Chain[]): Promise { const featuredPoolGroups = []; - if (chains.some((chain) => BalancerChainIds.includes(chainToIdMap[chain]))) { - const githubContentService = new GithubContentService(); - featuredPoolGroups.push(...(await githubContentService.getFeaturedPoolGroups(chains))); - } else if (chains.some((chain) => BeethovenChainIds.includes(chainToIdMap[chain]))) { + if (chains.some((chain) => BeethovenChainIds.includes(chainToIdMap[chain]))) { const sanityContentService = new SanityContentService('FANTOM'); featuredPoolGroups.push(...(await sanityContentService.getFeaturedPoolGroups(chains))); } @@ -202,6 +200,32 @@ export class PoolGqlLoaderService { }); } + public async getFeaturedPools(chains: Chain[]): Promise { + const featuredPoolsFromService: FeaturedPool[] = []; + if (chains.some((chain) => BalancerChainIds.includes(chainToIdMap[chain]))) { + const githubContentService = new GithubContentService(); + featuredPoolsFromService.push(...(await githubContentService.getFeaturedPools(chains))); + } + if (chains.some((chain) => BeethovenChainIds.includes(chainToIdMap[chain]))) { + // chain in constructor doesnt matter for this query as we pass the chain in the param + const sanityContentService = new SanityContentService('FANTOM'); + featuredPoolsFromService.push(...(await sanityContentService.getFeaturedPools(chains))); + } + + const featuredPools: GqlPoolFeaturedPool[] = []; + + for (const contentPool of featuredPoolsFromService) { + const pool = await this.getPool(contentPool.poolId.toLowerCase(), contentPool.chain); + featuredPools.push({ + poolId: contentPool.poolId, + primary: contentPool.primary, + pool: pool, + }); + } + + return featuredPools; + } + private mapQueryArgsToPoolQuery(args: QueryPoolGetPoolsArgs): Prisma.PrismaPoolFindManyArgs { let orderBy: Prisma.PrismaPoolOrderByWithRelationInput = {}; const orderDirection = args.orderDirection || undefined; diff --git a/modules/pool/pool.gql b/modules/pool/pool.gql index f37cbd14a..f1fa5ed92 100644 --- a/modules/pool/pool.gql +++ b/modules/pool/pool.gql @@ -20,6 +20,7 @@ extend type Query { poolGetBatchSwaps(first: Int, skip: Int, where: GqlPoolSwapFilter): [GqlPoolBatchSwap!]! poolGetJoinExits(first: Int, skip: Int, where: GqlPoolJoinExitFilter): [GqlPoolJoinExit!]! poolGetFeaturedPoolGroups(chains: [GqlChain!]): [GqlPoolFeaturedPoolGroup!]! + poolGetFeaturedPools(chains: [GqlChain!]!): [GqlPoolFeaturedPool!]! poolGetSnapshots(id: String!, chain: GqlChain, range: GqlPoolSnapshotDataRange!): [GqlPoolSnapshot!]! poolGetLinearPools(chains: [GqlChain!]): [GqlPoolLinear!]! poolGetGyroPools(chains: [GqlChain!]): [GqlPoolGyro!]! @@ -815,12 +816,16 @@ type GqlPoolUserSwapVolume { swapVolumeUSD: BigDecimal! } +type GqlPoolFeaturedPool { + poolId: ID! + primary: Boolean! + pool: GqlPoolBase! +} + type GqlPoolFeaturedPoolGroup { id: ID! title: String! icon: String! - primary: Boolean - chain: GqlChain! items: [GqlPoolFeaturedPoolGroupItem!]! } diff --git a/modules/pool/pool.resolvers.ts b/modules/pool/pool.resolvers.ts index 18a13603a..9f2ff0d7e 100644 --- a/modules/pool/pool.resolvers.ts +++ b/modules/pool/pool.resolvers.ts @@ -58,6 +58,9 @@ const balancerResolvers: Resolvers = { } return poolService.getFeaturedPoolGroups(chains); }, + poolGetFeaturedPools: async (parent, { chains }, context) => { + return poolService.getFeaturedPools(chains); + }, poolGetSnapshots: async (parent, { id, chain, range }, context) => { const currentChain = headerChain(); if (!chain && currentChain) { diff --git a/modules/pool/pool.service.ts b/modules/pool/pool.service.ts index 70ea01962..9461b541f 100644 --- a/modules/pool/pool.service.ts +++ b/modules/pool/pool.service.ts @@ -6,6 +6,7 @@ import { prisma } from '../../prisma/prisma-client'; import { GqlChain, GqlPoolBatchSwap, + GqlPoolFeaturedPool, GqlPoolFeaturedPoolGroup, GqlPoolGyro, GqlPoolJoinExit, @@ -120,6 +121,10 @@ export class PoolService { return this.poolGqlLoaderService.getFeaturedPoolGroups(chains); } + public async getFeaturedPools(chains: Chain[]): Promise { + return this.poolGqlLoaderService.getFeaturedPools(chains); + } + public async getSnapshotsForPool(poolId: string, chain: Chain, range: GqlPoolSnapshotDataRange) { return this.poolSnapshotService.getSnapshotsForPool(poolId, chain, range); } From d95310395aaefd86185e21c9bf7aa80b9de27098 Mon Sep 17 00:00:00 2001 From: franzns <93920061+franzns@users.noreply.github.com> Date: Mon, 15 Jan 2024 14:14:33 +0100 Subject: [PATCH 06/15] stop token price purge for now (#41) --- modules/network/mainnet.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/modules/network/mainnet.ts b/modules/network/mainnet.ts index b862ed02f..3084758d9 100644 --- a/modules/network/mainnet.ts +++ b/modules/network/mainnet.ts @@ -492,11 +492,11 @@ export const mainnetNetworkConfig: NetworkConfig = { name: 'sync-global-coingecko-prices', interval: (env.DEPLOYMENT_ENV as DeploymentEnv) === 'canary' ? every(10, 'minutes') : every(2, 'minutes'), }, - { - name: 'global-purge-old-tokenprices', - interval: every(1, 'days'), - alarmEvaluationPeriod: 1, - alarmDatapointsToAlarm: 1, - }, + // { + // name: 'global-purge-old-tokenprices', + // interval: every(1, 'days'), + // alarmEvaluationPeriod: 1, + // alarmDatapointsToAlarm: 1, + // }, ], }; From 568181a9547cedeac39a2436ec9e20eb7d2d0860 Mon Sep 17 00:00:00 2001 From: franzns <93920061+franzns@users.noreply.github.com> Date: Tue, 16 Jan 2024 15:45:55 +0100 Subject: [PATCH 07/15] remove wrapped linear token propery (#44) --- modules/content/github-content.service.ts | 10 ++++++++-- modules/content/sanity-content.service.ts | 11 +++++++++-- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/modules/content/github-content.service.ts b/modules/content/github-content.service.ts index ac798a5d5..053052a89 100644 --- a/modules/content/github-content.service.ts +++ b/modules/content/github-content.service.ts @@ -151,11 +151,11 @@ export class GithubContentService implements ContentService { }); } - const linearPool = pools.find( + const wrappedLinearPoolToken = pools.find( (pool) => pool.linearData && pool.tokens[pool.linearData.wrappedIndex]?.address === token.address, ); - if (linearPool && !tokenTypes.includes('LINEAR_WRAPPED_TOKEN')) { + if (wrappedLinearPoolToken && !tokenTypes.includes('LINEAR_WRAPPED_TOKEN')) { types.push({ id: `${token.address}-linear-wrapped`, chain: networkContext.chain, @@ -163,6 +163,12 @@ export class GithubContentService implements ContentService { tokenAddress: token.address, }); } + + if (!wrappedLinearPoolToken && tokenTypes.includes('LINEAR_WRAPPED_TOKEN')) { + prisma.prismaTokenType.delete({ + where: { id_chain: { id: `${token.address}-linear-wrapped`, chain: networkContext.chain } }, + }); + } } await prisma.prismaTokenType.createMany({ skipDuplicates: true, data: types }); diff --git a/modules/content/sanity-content.service.ts b/modules/content/sanity-content.service.ts index e512718e9..bbbe66707 100644 --- a/modules/content/sanity-content.service.ts +++ b/modules/content/sanity-content.service.ts @@ -11,6 +11,7 @@ import { import SanityClient from '@sanity/client'; import { env } from '../../app/env'; import { chainToIdMap } from '../network/network-config'; +import { wrap } from 'module'; interface SanityToken { name: string; @@ -184,11 +185,11 @@ export class SanityContentService implements ContentService { }); } - const linearPool = pools.find( + const wrappedLinearPoolToken = pools.find( (pool) => pool.linearData && pool.tokens[pool.linearData.wrappedIndex].address === token.address, ); - if (linearPool && !tokenTypes.includes('LINEAR_WRAPPED_TOKEN')) { + if (wrappedLinearPoolToken && !tokenTypes.includes('LINEAR_WRAPPED_TOKEN')) { types.push({ id: `${token.address}-linear-wrapped`, chain: this.chain, @@ -196,6 +197,12 @@ export class SanityContentService implements ContentService { tokenAddress: token.address, }); } + + if (!wrappedLinearPoolToken && tokenTypes.includes('LINEAR_WRAPPED_TOKEN')) { + prisma.prismaTokenType.delete({ + where: { id_chain: { id: `${token.address}-linear-wrapped`, chain: this.chain } }, + }); + } } await prisma.prismaTokenType.createMany({ skipDuplicates: true, data: types }); From bf17d891c00438c22f8bbc558cf19028f01ac4f7 Mon Sep 17 00:00:00 2001 From: gmbronco <83549293+gmbronco@users.noreply.github.com> Date: Thu, 18 Jan 2024 15:43:20 +0100 Subject: [PATCH 08/15] Add support for FX pools (#29) * docker-compose setup * more tests * allow deleting pools with associations * pool defaults for 24h volume and fees * pools data json column * add fx data to subgraph * subgraph to DB mappers * FX pools parsing in SOR * FX pools in the graph schema * subgraph pool to DB update * obsolete poolSyncAllPoolTypesVersions * include pool tokens in subgraph sync * obsolete poolReloadPoolTokenIndexes covered by syncAllPoolsFromSubgraph * obsolete poolReloadPoolNestedTokens covered by syncAllPoolsFromSubgraph * obsolete poolSync mutations covered by syncAllPools * fix pool type mapping for StablePhantom * add nestedPool list when creating new pools * renaming pool.data to pool.poolTypeSpecificData * import all of the functions directly to have references in the header * delete cleanup based on constraints * renaming * rename column * prisma schema --------- Co-authored-by: franz --- README.md | 9 +- docker-compose.yml | 9 +- modules/pool/lib/pool-creator.service.ts | 334 ++++-------------- modules/pool/lib/pool-gql-loader.service.ts | 24 ++ modules/pool/pool-data/element.ts | 9 + modules/pool/pool-data/fx.ts | 11 + modules/pool/pool-data/gyro.ts | 23 ++ modules/pool/pool-data/index.ts | 5 + modules/pool/pool-data/linear.ts | 16 + modules/pool/pool-data/stable.ts | 8 + modules/pool/pool.gql | 35 +- modules/pool/pool.prisma | 41 +-- modules/pool/pool.resolvers.ts | 45 +-- modules/pool/pool.service.ts | 114 +----- modules/pool/subgraph-mapper.test.ts | 103 ++++++ modules/pool/subgraph-mapper.ts | 291 +++++++++++++++ modules/sor/sorV2/sorV2.service.ts | 19 +- .../balancer-subgraph-queries.graphql | 3 + modules/user/user.prisma | 6 +- modules/vebal/vebal.prisma | 2 +- package.json | 1 + .../migration.sql | 131 +++++++ .../migration.sql | 3 + .../migration.sql | 2 + prisma/schema.prisma | 49 +-- test/factories/index.ts | 2 + test/factories/pool.factory.ts | 50 +++ test/factories/pool_token.factory.ts | 17 + yarn.lock | 12 + 29 files changed, 901 insertions(+), 473 deletions(-) create mode 100644 modules/pool/pool-data/element.ts create mode 100644 modules/pool/pool-data/fx.ts create mode 100644 modules/pool/pool-data/gyro.ts create mode 100644 modules/pool/pool-data/index.ts create mode 100644 modules/pool/pool-data/linear.ts create mode 100644 modules/pool/pool-data/stable.ts create mode 100644 modules/pool/subgraph-mapper.test.ts create mode 100644 modules/pool/subgraph-mapper.ts create mode 100644 prisma/migrations/20231221153955_allow_deleting_pools_with_associations/migration.sql create mode 100644 prisma/migrations/20231221154038_more_pool_defaults/migration.sql create mode 100644 prisma/migrations/20231221154118_pools_data_json_column/migration.sql create mode 100644 test/factories/index.ts create mode 100644 test/factories/pool.factory.ts create mode 100644 test/factories/pool_token.factory.ts diff --git a/README.md b/README.md index 828d6e766..f638248cd 100644 --- a/README.md +++ b/README.md @@ -47,14 +47,11 @@ userInitStakedBalances ### Setup database & Prisma from backup Retrieve the current pg_dump file under `https://api-db-dump.s3.eu-central-1.amazonaws.com/canary/api-dump.YYYYMMDD`. -Database dumps are kept for the previous 7 days, replace YYYYMMDD in the URL above (ie: 20230317) to download a db dump. +Database dumps are kept for the previous 7 days, replace YYYYMMDD in the URL above (ie: 20230317) to download a db dump. Run `docker-compose up -d` to start the database via docker compose. -Retrieve the docker container ID through `docker ps`. - -Run `docker exec -i /bin/bash -c "PGPASSWORD=let-me-in psql --username backend database" < /path/on/your/machine/dump` -with the container-ID from the step before. +Run `docker exec -i $(docker ps -qf "name=balancer-backend") /bin/bash -c "PGPASSWORD=let-me-in psql --username backend database" < /path/on/your/machine/dump` The output at the very end saying `ERROR: role "rdsadmin" does not exist` is normal and can be ignored. @@ -90,4 +87,4 @@ To contribute, branch from `v2-canary` (which is our development branch) and ope ### Database Updates -If you make any changes to the database schema be sure to run `yarn prisma migrate dev --name ` which will create a new file in `prisma/migrations` that contains all the database changes you've made as an SQL update script. \ No newline at end of file +If you make any changes to the database schema be sure to run `yarn prisma migrate dev --name ` which will create a new file in `prisma/migrations` that contains all the database changes you've made as an SQL update script. diff --git a/docker-compose.yml b/docker-compose.yml index 91766dccb..05ae87ada 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -2,10 +2,17 @@ version: '3' services: postgres: - image: postgres + container_name: balancer-backend + image: postgres:14-alpine ports: - '5431:5432' environment: POSTGRES_USER: backend POSTGRES_PASSWORD: let-me-in POSTGRES_DB: database + networks: + - balancer + +networks: + balancer: + name: balancer diff --git a/modules/pool/lib/pool-creator.service.ts b/modules/pool/lib/pool-creator.service.ts index b138ea03b..26ef74e99 100644 --- a/modules/pool/lib/pool-creator.service.ts +++ b/modules/pool/lib/pool-creator.service.ts @@ -1,12 +1,12 @@ import { BalancerPoolFragment } from '../../subgraphs/balancer-subgraph/generated/balancer-subgraph-types'; import { prisma } from '../../../prisma/prisma-client'; -import { ZERO_ADDRESS } from '@gnosis.pm/safe-core-sdk/dist/src/utils/constants'; import { PrismaPoolType } from '@prisma/client'; import _ from 'lodash'; import { prismaPoolWithExpandedNesting } from '../../../prisma/prisma-types'; import { UserService } from '../../user/user.service'; import { prismaBulkExecuteOperations } from '../../../prisma/prisma-util'; import { networkContext } from '../../network/network-context.service'; +import { subgraphToPrismaCreate, subgraphToPrismaUpdate } from '../subgraph-mapper'; export class PoolCreatorService { constructor(private readonly userService: UserService) {} @@ -23,6 +23,13 @@ export class PoolCreatorService { const existingPools = await prisma.prismaPool.findMany({ where: { chain: this.chain } }); const subgraphPools = await this.balancerSubgraphService.getAllPools({}, false); const sortedSubgraphPools = this.sortSubgraphPools(subgraphPools); + const allNestedTypePools = await prisma.prismaPool.findMany({ + where: { + chain: this.chain, + type: { in: [PrismaPoolType.LINEAR, PrismaPoolType.COMPOSABLE_STABLE] }, + }, + select: { id: true, address: true }, + }); const poolIds: string[] = []; @@ -33,42 +40,11 @@ export class PoolCreatorService { const existsInDb = !!existingPools.find((pool) => pool.id === subgraphPool.id); if (!existsInDb) { - await this.createPoolRecord(subgraphPool, blockNumber); + await this.createPoolRecord(subgraphPool, blockNumber, allNestedTypePools); poolIds.push(subgraphPool.id); - } else if (subgraphPool.poolType?.includes('Gyro')) { - await prisma.prismaPool.update({ - data: { - gyroData: { - update: { - id: subgraphPool.id, - alpha: subgraphPool.alpha || '', - beta: subgraphPool.beta || '', - sqrtAlpha: subgraphPool.sqrtAlpha || '', - sqrtBeta: subgraphPool.sqrtBeta || '', - root3Alpha: subgraphPool.root3Alpha || '', - c: subgraphPool.c || '', - s: subgraphPool.s || '', - lambda: subgraphPool.lambda || '', - tauAlphaX: subgraphPool.tauAlphaX || '', - tauAlphaY: subgraphPool.tauAlphaY || '', - tauBetaX: subgraphPool.tauBetaX || '', - tauBetaY: subgraphPool.tauBetaY || '', - u: subgraphPool.u || '', - v: subgraphPool.v || '', - w: subgraphPool.w || '', - z: subgraphPool.z || '', - dSq: subgraphPool.dSq || '', - }, - }, - }, - where: { - id_chain: { - id: subgraphPool.id, - chain: this.chain, - }, - }, - }); + } else { + await this.updatePoolRecord(subgraphPool, blockNumber, allNestedTypePools); } } @@ -92,11 +68,22 @@ export class PoolCreatorService { const sortedSubgraphPools = this.sortSubgraphPools(subgraphPools); const poolIds = new Set(); + const allNestedTypePools = [ + ...(await prisma.prismaPool.findMany({ + where: { + chain: this.chain, + type: { in: [PrismaPoolType.LINEAR, PrismaPoolType.COMPOSABLE_STABLE] }, + }, + select: { id: true, address: true }, + })), + ...sortedSubgraphPools.map((pool) => ({ id: pool.id, address: pool.address })), + ]; + for (const subgraphPool of sortedSubgraphPools) { const existsInDb = !!existingPools.find((pool) => pool.id === subgraphPool.id); if (!existsInDb) { - await this.createPoolRecord(subgraphPool, blockNumber); + await this.createPoolRecord(subgraphPool, blockNumber, allNestedTypePools); poolIds.add(subgraphPool.id); } @@ -105,42 +92,6 @@ export class PoolCreatorService { return Array.from(poolIds); } - public async reloadPoolNestedTokens(poolId: string): Promise { - const subgraphPools = await this.balancerSubgraphService.getAllPools({}, false); - const poolToLoad = subgraphPools.find((pool) => pool.id === poolId); - - if (!poolToLoad) { - throw new Error('Pool with id does not exist'); - } - - const poolTokens = poolToLoad.tokens || []; - - for (let i = 0; i < poolTokens.length; i++) { - const token = poolTokens[i]; - - if (token.address === poolToLoad.address) { - continue; - } - - const nestedPool = subgraphPools.find((nestedPool) => { - const poolType = this.mapSubgraphPoolTypeToPoolType(nestedPool.poolType || ''); - - return ( - nestedPool.address === token.address && (poolType === 'LINEAR' || poolType === 'COMPOSABLE_STABLE') - ); - }); - - if (nestedPool) { - await prisma.prismaPoolToken.update({ - where: { id_chain: { id: token.id, chain: this.chain } }, - data: { nestedPoolId: nestedPool.id }, - }); - } - } - - await this.createAllTokensRelationshipForPool(poolId); - } - public async reloadAllTokenNestedPoolIds(): Promise { let operations: any[] = []; const pools = await prisma.prismaPool.findMany({ @@ -192,18 +143,13 @@ export class PoolCreatorService { await prismaBulkExecuteOperations(operations); } - private async createPoolRecord(pool: BalancerPoolFragment, blockNumber: number) { - const poolType = this.mapSubgraphPoolTypeToPoolType(pool.poolType || ''); + private async createPoolRecord( + pool: BalancerPoolFragment, + blockNumber: number, + nestedPools: { id: string; address: string }[], + ) { const poolTokens = pool.tokens || []; - const allNestedTypePools = await prisma.prismaPool.findMany({ - where: { - chain: this.chain, - type: { in: [PrismaPoolType.LINEAR, PrismaPoolType.COMPOSABLE_STABLE] }, - }, - select: { id: true, address: true }, - }); - await prisma.prismaToken.createMany({ skipDuplicates: true, data: [ @@ -224,136 +170,9 @@ export class PoolCreatorService { ], }); - // for the old phantom stable pool, we add it to the DB as type COMPOSABLE_STABLE with version 0 - let poolTypeVersion = pool.poolTypeVersion ? pool.poolTypeVersion : 1; - if (pool.poolType === 'StablePhantom') { - poolTypeVersion = 0; - } + const prismaPoolRecordWithAssociations = subgraphToPrismaCreate(pool, this.chain, blockNumber, nestedPools); - await prisma.prismaPool.create({ - data: { - id: pool.id, - chain: this.chain, - createTime: pool.createTime, - address: pool.address, - symbol: pool.symbol || '', - name: pool.name || '', - decimals: 18, - type: poolType, - version: poolTypeVersion, - owner: pool.owner || ZERO_ADDRESS, - factory: pool.factory, - tokens: { - createMany: { - data: poolTokens.map((token) => { - const nestedPool = allNestedTypePools.find((nestedPool) => { - return nestedPool.address === token.address; - }); - - let priceRateProvider; - if (pool.priceRateProviders) { - const data = pool.priceRateProviders.find( - (provider) => provider.token.address === token.address, - ); - priceRateProvider = data?.address; - } - - return { - id: token.id, - address: token.address, - priceRateProvider, - exemptFromProtocolYieldFee: token.isExemptFromYieldProtocolFee - ? token.isExemptFromYieldProtocolFee - : false, - nestedPoolId: nestedPool?.id, - index: token.index || pool.tokensList.findIndex((address) => address === token.address), - }; - }), - }, - }, - linearData: - poolType === 'LINEAR' - ? { - create: { - id: pool.id, - mainIndex: pool.mainIndex || 0, - wrappedIndex: pool.wrappedIndex || 0, - }, - } - : undefined, - linearDynamicData: - poolType === 'LINEAR' - ? { - create: { - id: pool.id, - upperTarget: pool.upperTarget || '', - lowerTarget: pool.lowerTarget || '', - blockNumber, - }, - } - : undefined, - elementData: - poolType === 'ELEMENT' - ? { - create: { - id: pool.id, - unitSeconds: pool.unitSeconds || '', - principalToken: pool.principalToken || '', - baseToken: pool.baseToken || '', - }, - } - : undefined, - gyroData: ['GYRO', 'GYRO3', 'GYROE'].includes(poolType) - ? { - create: { - id: pool.id, - alpha: pool.alpha || '', - beta: pool.beta || '', - sqrtAlpha: pool.sqrtAlpha || '', - sqrtBeta: pool.sqrtBeta || '', - root3Alpha: pool.root3Alpha || '', - c: pool.c || '', - s: pool.s || '', - lambda: pool.lambda || '', - tauAlphaX: pool.tauAlphaX || '', - tauAlphaY: pool.tauAlphaY || '', - tauBetaX: pool.tauBetaX || '', - tauBetaY: pool.tauBetaY || '', - u: pool.u || '', - v: pool.v || '', - w: pool.w || '', - z: pool.z || '', - dSq: pool.dSq || '', - }, - } - : undefined, - stableDynamicData: - poolType === 'STABLE' || poolType === 'COMPOSABLE_STABLE' || poolType === 'META_STABLE' - ? { - create: { - id: pool.id, - amp: pool.amp || '', - blockNumber, - }, - } - : undefined, - dynamicData: { - create: { - id: pool.id, - blockNumber, - swapFee: pool.swapFee, - swapEnabled: pool.swapEnabled, - totalShares: pool.totalShares, - totalSharesNum: parseFloat(pool.totalShares), - totalLiquidity: Math.max(parseFloat(pool.totalLiquidity), 0), - volume24h: 0, - fees24h: 0, - volume48h: 0, - fees48h: 0, - }, - }, - }, - }); + await prisma.prismaPool.create(prismaPoolRecordWithAssociations); await prisma.prismaPoolTokenDynamicData.createMany({ data: poolTokens.map((token) => ({ @@ -372,6 +191,42 @@ export class PoolCreatorService { await this.userService.initWalletBalancesForPool(pool.id); } + private async updatePoolRecord( + pool: BalancerPoolFragment, + blockNumber: number, + nestedPools: { id: string; address: string }[] = [], + ) { + const prismaPoolRecordWithAssociations = subgraphToPrismaUpdate(pool, this.chain, blockNumber, nestedPools); + const { tokens, ...poolWithoutTokens } = prismaPoolRecordWithAssociations; + + // Make sure all tokens are there, for managed pools tokenlist can change + for (const token of tokens.update) { + await prisma.prismaPoolToken.upsert({ + where: token.where, + create: { + ...token.data, + poolId: pool.id, + chain: this.chain, + }, + update: { + ...token.data, + }, + }); + } + + await prisma.prismaPool.update({ + data: poolWithoutTokens, + where: { + id_chain: { + id: pool.id, + chain: this.chain, + }, + }, + }); + + await this.createAllTokensRelationshipForPool(pool.id); + } + public async createAllTokensRelationshipForPool(poolId: string): Promise { const pool = await prisma.prismaPool.findUnique({ ...prismaPoolWithExpandedNesting, @@ -409,63 +264,6 @@ export class PoolCreatorService { }); } - public async reloadPoolTokenIndexes(poolId: string): Promise { - const { pool: subgraphPool } = await this.balancerSubgraphService.getPool({ id: poolId }); - - if (!subgraphPool) { - throw new Error('Pool with id does not exist'); - } - - const poolTokens = subgraphPool.tokens || []; - - for (let i = 0; i < poolTokens.length; i++) { - const token = poolTokens[i]; - - await prisma.prismaPoolToken.update({ - where: { id_chain: { id: token.id, chain: this.chain } }, - data: { - index: token.index || subgraphPool.tokensList.findIndex((address) => address === token.address), - }, - }); - } - } - - public async updatePoolTypesAndVersionForAllPools() { - const subgraphPools = await this.balancerSubgraphService.getAllPools({}, false); - - for (const subgraphPool of subgraphPools) { - // for the old phantom stable pool, we add it to the DB as type COMPOSABLE_STABLE with version 0 - let poolTypeVersion = subgraphPool.poolTypeVersion ? subgraphPool.poolTypeVersion : 1; - if (subgraphPool.poolType === 'StablePhantom') { - poolTypeVersion = 0; - } - - const poolType = this.mapSubgraphPoolTypeToPoolType(subgraphPool.poolType || ''); - - try { - await prisma.prismaPool.update({ - where: { id_chain: { chain: networkContext.chain, id: subgraphPool.id } }, - data: { - version: poolTypeVersion, - type: poolType, - }, - }); - } catch (e: any) { - // Some pools are filtered from the DB, like test pools, - // so we just ignore them without breaking the loop - const error = e.meta ? e.meta.cause : e; - console.error( - 'Error in updating pool versions: ', - error, - 'Network', - networkContext.chain, - 'Pool ID: ', - subgraphPool.id, - ); - } - } - } - private sortSubgraphPools(subgraphPools: BalancerPoolFragment[]) { return _.sortBy(subgraphPools, (pool) => { const poolType = this.mapSubgraphPoolTypeToPoolType(pool.poolType || ''); diff --git a/modules/pool/lib/pool-gql-loader.service.ts b/modules/pool/lib/pool-gql-loader.service.ts index fac3005a0..de731f980 100644 --- a/modules/pool/lib/pool-gql-loader.service.ts +++ b/modules/pool/lib/pool-gql-loader.service.ts @@ -32,6 +32,7 @@ import { GqlPoolWithdrawConfig, GqlPoolWithdrawOption, QueryPoolGetPoolsArgs, + GqlPoolFx, } from '../../../schema'; import { isSameAddress } from '@balancer-labs/sdk'; import _ from 'lodash'; @@ -47,6 +48,7 @@ import { BalancerChainIds, BeethovenChainIds, chainIdToChain, chainToIdMap } fro import { GithubContentService } from '../../content/github-content.service'; import { SanityContentService } from '../../content/sanity-content.service'; import { FeaturedPool } from '../../content/content-types'; +import { FxData } from '../subgraph-mapper'; export class PoolGqlLoaderService { public async getPool(id: string, chain: Chain, userAddress?: string): Promise { @@ -139,6 +141,16 @@ export class PoolGqlLoaderService { return pools.map((pool) => this.mapPoolToGqlPool(pool)) as GqlPoolGyro[]; } + public async getFxPools(chains: Chain[]): Promise { + const pools = await prisma.prismaPool.findMany({ + where: { type: { in: ['FX'] }, chain: { in: chains } }, + orderBy: { dynamicData: { totalLiquidity: 'desc' } }, + include: prismaPoolWithExpandedNesting.include, + }); + + return pools.map((pool) => this.mapPoolToGqlPool(pool)) as GqlPoolFx[]; + } + public mapToMinimalGqlPool( pool: PrismaPoolMinimal, userWalletbalances: PrismaUserWalletBalance[] = [], @@ -581,6 +593,18 @@ export class PoolGqlLoaderService { z: pool.gyroData?.z || '', dSq: pool.gyroData?.dSq || '', }; + case 'FX': + const data = pool.staticTypeData as FxData; + return { + __typename: 'GqlPoolFx', + ...mappedData, + type: mappedData.type, + alpha: data.alpha || '', + beta: data.beta || '', + delta: data.delta || '', + epsilon: data.epsilon || '', + lambda: data.lambda || '', + }; } return { diff --git a/modules/pool/pool-data/element.ts b/modules/pool/pool-data/element.ts new file mode 100644 index 000000000..89c6af0ec --- /dev/null +++ b/modules/pool/pool-data/element.ts @@ -0,0 +1,9 @@ +import { BalancerPoolFragment } from '../../subgraphs/balancer-subgraph/generated/balancer-subgraph-types'; + +export const element = (pool: BalancerPoolFragment) => { + return { + unitSeconds: pool.unitSeconds || '', + principalToken: pool.principalToken || '', + baseToken: pool.baseToken || '', + }; +}; diff --git a/modules/pool/pool-data/fx.ts b/modules/pool/pool-data/fx.ts new file mode 100644 index 000000000..7b1edf745 --- /dev/null +++ b/modules/pool/pool-data/fx.ts @@ -0,0 +1,11 @@ +import { BalancerPoolFragment } from '../../subgraphs/balancer-subgraph/generated/balancer-subgraph-types'; + +export const fx = (pool: BalancerPoolFragment) => { + return { + alpha: pool.alpha, + beta: pool.beta, + delta: pool.delta, + epsilon: pool.epsilon, + lambda: pool.lambda, + }; +}; diff --git a/modules/pool/pool-data/gyro.ts b/modules/pool/pool-data/gyro.ts new file mode 100644 index 000000000..326a3c044 --- /dev/null +++ b/modules/pool/pool-data/gyro.ts @@ -0,0 +1,23 @@ +import { BalancerPoolFragment } from '../../subgraphs/balancer-subgraph/generated/balancer-subgraph-types'; + +export const gyro = (pool: BalancerPoolFragment) => { + return { + alpha: pool.alpha || '', + beta: pool.beta || '', + sqrtAlpha: pool.sqrtAlpha || '', + sqrtBeta: pool.sqrtBeta || '', + root3Alpha: pool.root3Alpha || '', + c: pool.c || '', + s: pool.s || '', + lambda: pool.lambda || '', + tauAlphaX: pool.tauAlphaX || '', + tauAlphaY: pool.tauAlphaY || '', + tauBetaX: pool.tauBetaX || '', + tauBetaY: pool.tauBetaY || '', + u: pool.u || '', + v: pool.v || '', + w: pool.w || '', + z: pool.z || '', + dSq: pool.dSq || '', + }; +}; diff --git a/modules/pool/pool-data/index.ts b/modules/pool/pool-data/index.ts new file mode 100644 index 000000000..52c0b91de --- /dev/null +++ b/modules/pool/pool-data/index.ts @@ -0,0 +1,5 @@ +export * from './element'; +export * from './fx'; +export * from './gyro'; +export * from './linear'; +export * from './stable'; diff --git a/modules/pool/pool-data/linear.ts b/modules/pool/pool-data/linear.ts new file mode 100644 index 000000000..becb29a4c --- /dev/null +++ b/modules/pool/pool-data/linear.ts @@ -0,0 +1,16 @@ +import { BalancerPoolFragment } from '../../subgraphs/balancer-subgraph/generated/balancer-subgraph-types'; + +export const linear = (pool: BalancerPoolFragment) => { + return { + mainIndex: pool.mainIndex || 0, + wrappedIndex: pool.wrappedIndex || 0, + }; +}; + +export const linearDynamic = (pool: BalancerPoolFragment, blockNumber: number) => { + return { + upperTarget: pool.upperTarget || '', + lowerTarget: pool.lowerTarget || '', + blockNumber, + }; +}; diff --git a/modules/pool/pool-data/stable.ts b/modules/pool/pool-data/stable.ts new file mode 100644 index 000000000..7ac8218d8 --- /dev/null +++ b/modules/pool/pool-data/stable.ts @@ -0,0 +1,8 @@ +import { BalancerPoolFragment } from '../../subgraphs/balancer-subgraph/generated/balancer-subgraph-types'; + +export const stableDynamic = (pool: BalancerPoolFragment, blockNumber: number) => { + return { + amp: pool.amp || '', + blockNumber, + }; +}; diff --git a/modules/pool/pool.gql b/modules/pool/pool.gql index f1fa5ed92..db7656ce2 100644 --- a/modules/pool/pool.gql +++ b/modules/pool/pool.gql @@ -24,6 +24,7 @@ extend type Query { poolGetSnapshots(id: String!, chain: GqlChain, range: GqlPoolSnapshotDataRange!): [GqlPoolSnapshot!]! poolGetLinearPools(chains: [GqlChain!]): [GqlPoolLinear!]! poolGetGyroPools(chains: [GqlChain!]): [GqlPoolGyro!]! + poolGetFxPools(chains: [GqlChain!]): [GqlPoolFx!]! } extend type Mutation { @@ -48,16 +49,11 @@ extend type Mutation { poolUpdateLifetimeValuesForAllPools: String! poolInitializeSnapshotsForPool(poolId: String!): String! poolSyncPool(poolId: String!): String! - poolReloadPoolNestedTokens(poolId: String!): String! poolReloadAllTokenNestedPoolIds: String! - poolReloadPoolTokenIndexes(poolId: String!): String! poolSetPoolsWithPreferredGaugesAsIncentivized: String! poolBlackListAddPool(poolId: String!): String! poolBlackListRemovePool(poolId: String!): String! poolDeletePool(poolId: String!): String! - poolSyncAllPoolTypesVersions: String! - poolSyncPriceRateProviders: String! - poolSyncProtocolYieldFeeExemptions: String! poolInitOnChainDataForAllPools: String! } @@ -282,6 +278,34 @@ type GqlPoolGyro implements GqlPoolBase { userBalance: GqlPoolUserBalance } +type GqlPoolFx implements GqlPoolBase { + alpha: String! + beta: String! + delta: String! + epsilon: String! + lambda: String! + + # Base pool + id: ID! + chain: GqlChain! + type: GqlPoolType! + version: Int! + name: String! + symbol: String! + address: Bytes! + decimals: Int! + owner: Bytes + factory: Bytes + createTime: Int! + investConfig: GqlPoolInvestConfig! + withdrawConfig: GqlPoolWithdrawConfig! + displayTokens: [GqlPoolTokenDisplay!]! + allTokens: [GqlPoolTokenExpanded!]! + dynamicData: GqlPoolDynamicData! + staking: GqlPoolStaking + userBalance: GqlPoolUserBalance +} + type GqlPoolLiquidityBootstrapping implements GqlPoolBase { id: ID! chain: GqlChain! @@ -495,6 +519,7 @@ union GqlPoolUnion = | GqlPoolElement | GqlPoolLiquidityBootstrapping | GqlPoolGyro + | GqlPoolFx union GqlPoolNestedUnion = GqlPoolLinearNested | GqlPoolComposableStableNested union GqlPoolTokenUnion = GqlPoolToken | GqlPoolTokenComposableStable | GqlPoolTokenLinear diff --git a/modules/pool/pool.prisma b/modules/pool/pool.prisma index 9b16b15c2..e9189664e 100644 --- a/modules/pool/pool.prisma +++ b/modules/pool/pool.prisma @@ -18,6 +18,7 @@ model PrismaPool { linearData PrismaPoolLinearData? elementData PrismaPoolElementData? gyroData PrismaPoolGyroData? + staticTypeData Json @default("{}") tokens PrismaPoolToken[] @@ -65,7 +66,7 @@ model PrismaPoolLinearData { id String poolId String - pool PrismaPool @relation(fields:[poolId, chain], references: [id, chain]) + pool PrismaPool @relation(fields:[poolId, chain], references: [id, chain], onDelete: Cascade) chain Chain mainIndex Int @@ -78,7 +79,7 @@ model PrismaPoolElementData { id String poolId String - pool PrismaPool @relation(fields:[poolId, chain], references: [id, chain]) + pool PrismaPool @relation(fields:[poolId, chain], references: [id, chain], onDelete: Cascade) chain Chain unitSeconds String @@ -92,7 +93,7 @@ model PrismaPoolGyroData{ id String poolId String - pool PrismaPool @relation(fields:[poolId, chain], references: [id, chain]) + pool PrismaPool @relation(fields:[poolId, chain], references: [id, chain], onDelete: Cascade) chain Chain alpha String @@ -120,7 +121,7 @@ model PrismaPoolDynamicData { id String poolId String - pool PrismaPool @relation(fields:[poolId, chain], references: [id, chain]) + pool PrismaPool @relation(fields:[poolId, chain], references: [id, chain], onDelete: Cascade) chain Chain blockNumber Int updatedAt DateTime @updatedAt @@ -133,8 +134,8 @@ model PrismaPoolDynamicData { totalShares String totalSharesNum Float @default(0) totalLiquidity Float - volume24h Float - fees24h Float + volume24h Float @default(0) + fees24h Float @default(0) yieldCapture24h Float @default(0) apr Float @default(0) volume48h Float @default(0) @@ -174,7 +175,7 @@ model PrismaPoolStableDynamicData { id String poolId String - pool PrismaPool @relation(fields:[poolId, chain], references: [id, chain]) + pool PrismaPool @relation(fields:[poolId, chain], references: [id, chain], onDelete: Cascade) chain Chain blockNumber Int updatedAt DateTime @updatedAt @@ -188,7 +189,7 @@ model PrismaPoolLinearDynamicData { id String poolId String - pool PrismaPool @relation(fields:[poolId, chain], references: [id, chain]) + pool PrismaPool @relation(fields:[poolId, chain], references: [id, chain], onDelete: Cascade) chain Chain blockNumber Int updatedAt DateTime @updatedAt @@ -202,7 +203,7 @@ model PrismaPoolToken { id String poolId String - pool PrismaPool @relation(fields:[poolId, chain], references: [id, chain]) + pool PrismaPool @relation(fields:[poolId, chain], references: [id, chain], onDelete: Cascade) chain Chain address String @@ -224,7 +225,7 @@ model PrismaPoolTokenDynamicData { id String poolTokenId String - poolToken PrismaPoolToken @relation(fields:[poolTokenId, chain], references: [id, chain]) + poolToken PrismaPoolToken @relation(fields:[poolTokenId, chain], references: [id, chain], onDelete: Cascade) chain Chain blockNumber Int updatedAt DateTime @updatedAt @@ -241,7 +242,7 @@ model PrismaPoolSwap { id String poolId String - pool PrismaPool @relation(fields:[poolId, chain], references: [id, chain]) + pool PrismaPool @relation(fields:[poolId, chain], references: [id, chain], onDelete: Cascade) chain Chain userAddress String @@ -284,7 +285,7 @@ model PrismaPoolAprItem { id String poolId String - pool PrismaPool @relation(fields:[poolId, chain], references: [id, chain]) + pool PrismaPool @relation(fields:[poolId, chain], references: [id, chain], onDelete: Cascade) chain Chain title String apr Float @@ -301,7 +302,7 @@ model PrismaPoolAprRange { id String chain Chain aprItemId String - aprItem PrismaPoolAprItem @relation(fields:[aprItemId, chain], references: [id, chain]) + aprItem PrismaPoolAprItem @relation(fields:[aprItemId, chain], references: [id, chain], onDelete: Cascade) min Float max Float } @@ -355,11 +356,11 @@ model PrismaPoolExpandedTokens { tokenAddress String token PrismaToken @relation(fields:[tokenAddress, chain], references: [address, chain]) poolId String - pool PrismaPool @relation(fields:[poolId, chain], references: [id, chain]) + pool PrismaPool @relation(fields:[poolId, chain], references: [id, chain], onDelete: Cascade) chain Chain nestedPoolId String? - nestedPool PrismaPool? @relation(name: "NestedPoolForAllToken", fields:[nestedPoolId, chain], references: [id, chain]) + nestedPool PrismaPool? @relation(name: "NestedPoolForAllToken", fields:[nestedPoolId, chain], references: [id, chain], onDelete: Cascade) } @@ -381,7 +382,7 @@ model PrismaPoolFilterMap { filterId String filter PrismaPoolFilter @relation(fields:[filterId, chain], references: [id, chain]) poolId String - pool PrismaPool @relation(fields:[poolId, chain], references: [id, chain]) + pool PrismaPool @relation(fields:[poolId, chain], references: [id, chain], onDelete: Cascade) } model PrismaPoolStaking { @@ -389,7 +390,7 @@ model PrismaPoolStaking { id String poolId String - pool PrismaPool @relation(fields:[poolId, chain], references: [id, chain]) + pool PrismaPool @relation(fields:[poolId, chain], references: [id, chain], onDelete: Cascade) chain Chain type PrismaPoolStakingType address String @@ -439,7 +440,7 @@ model PrismaPoolStakingGauge { id String stakingId String - staking PrismaPoolStaking @relation(fields:[stakingId, chain], references: [id, chain]) + staking PrismaPoolStaking @relation(fields:[stakingId, chain], references: [id, chain], onDelete: Cascade) chain Chain gaugeAddress String @@ -463,7 +464,7 @@ model PrismaPoolStakingGaugeReward{ id String gaugeId String - gauge PrismaPoolStakingGauge @relation(fields:[gaugeId, chain], references: [id, chain]) + gauge PrismaPoolStakingGauge @relation(fields:[gaugeId, chain], references: [id, chain], onDelete: Cascade) chain Chain tokenAddress String @@ -510,7 +511,7 @@ model PrismaPoolSnapshot { id String poolId String - pool PrismaPool @relation(fields:[poolId, chain], references: [id, chain]) + pool PrismaPool @relation(fields:[poolId, chain], references: [id, chain], onDelete: Cascade) chain Chain timestamp Int diff --git a/modules/pool/pool.resolvers.ts b/modules/pool/pool.resolvers.ts index 9f2ff0d7e..bb8275e65 100644 --- a/modules/pool/pool.resolvers.ts +++ b/modules/pool/pool.resolvers.ts @@ -94,6 +94,16 @@ const balancerResolvers: Resolvers = { poolGetGyroPools: async () => { return poolService.getGqlGyroPools(); }, + + poolGetFxPools: async (parent, { chains }) => { + const currentChain = headerChain(); + if (!chains && currentChain) { + chains = [currentChain]; + } else if (!chains) { + throw new Error('poolGetLinearPools error: Provide "chains" param'); + } + return poolService.getGqlFxPools(chains); + }, }, Mutation: { poolSyncAllPoolsFromSubgraph: async (parent, {}, context) => { @@ -255,13 +265,6 @@ const balancerResolvers: Resolvers = { return 'success'; }, - poolReloadPoolNestedTokens: async (parent, { poolId }, context) => { - isAdminRoute(context); - - await poolService.reloadPoolNestedTokens(poolId); - - return 'success'; - }, poolReloadAllTokenNestedPoolIds: async (parent, {}, context) => { isAdminRoute(context); @@ -269,13 +272,6 @@ const balancerResolvers: Resolvers = { return 'success'; }, - poolReloadPoolTokenIndexes: async (parent, { poolId }, context) => { - isAdminRoute(context); - - await poolService.reloadPoolTokenIndexes(poolId); - - return 'success'; - }, poolSetPoolsWithPreferredGaugesAsIncentivized: async (parent, {}, context) => { isAdminRoute(context); @@ -302,27 +298,6 @@ const balancerResolvers: Resolvers = { await poolService.deletePool(poolId); - return 'success'; - }, - poolSyncAllPoolTypesVersions: async (parent, {}, context) => { - isAdminRoute(context); - - await poolService.syncPoolTypeAndVersionForAllPools(); - - return 'success'; - }, - poolSyncPriceRateProviders: async (parent, {}, context) => { - isAdminRoute(context); - - await poolService.syncPriceRateProvidersForAllPools(); - - return 'success'; - }, - poolSyncProtocolYieldFeeExemptions: async (parent, {}, context) => { - isAdminRoute(context); - - await poolService.syncProtocolYieldFeeExemptionsForAllPools(); - return 'success'; }, }, diff --git a/modules/pool/pool.service.ts b/modules/pool/pool.service.ts index 9461b541f..7cd8eb9ac 100644 --- a/modules/pool/pool.service.ts +++ b/modules/pool/pool.service.ts @@ -8,6 +8,7 @@ import { GqlPoolBatchSwap, GqlPoolFeaturedPool, GqlPoolFeaturedPoolGroup, + GqlPoolFx, GqlPoolGyro, GqlPoolJoinExit, GqlPoolLinear, @@ -87,6 +88,10 @@ export class PoolService { return this.poolGqlLoaderService.getGyroPools(); } + public async getGqlFxPools(chains: Chain[]): Promise { + return this.poolGqlLoaderService.getFxPools(chains); + } + public async getPoolsCount(args: QueryPoolGetPoolsArgs): Promise { return this.poolGqlLoaderService.getPoolsCount(args); } @@ -328,78 +333,14 @@ export class PoolService { await this.poolSnapshotService.createPoolSnapshotsForPoolsMissingSubgraphData(poolId); } - public async reloadPoolNestedTokens(poolId: string) { - await this.poolCreatorService.reloadPoolNestedTokens(poolId); - } - public async reloadAllTokenNestedPoolIds() { await this.poolCreatorService.reloadAllTokenNestedPoolIds(); } - public async reloadPoolTokenIndexes(poolId: string) { - await this.poolCreatorService.reloadPoolTokenIndexes(poolId); - } - public async setPoolsWithPreferredGaugesAsIncentivized() { await this.poolSyncService.setPoolsWithPreferredGaugesAsIncentivized(); } - public async syncPoolTypeAndVersionForAllPools() { - await this.poolCreatorService.updatePoolTypesAndVersionForAllPools(); - } - - public async syncProtocolYieldFeeExemptionsForAllPools() { - const subgraphPools = await this.balancerSubgraphService.getAllPools({}, false); - for (const subgraphPool of subgraphPools) { - const poolTokens = subgraphPool.tokens || []; - for (let i = 0; i < poolTokens.length; i++) { - const token = poolTokens[i]; - try { - await prisma.prismaPoolToken.update({ - where: { id_chain: { id: token.id, chain: networkContext.chain } }, - data: { - exemptFromProtocolYieldFee: token.isExemptFromYieldProtocolFee - ? token.isExemptFromYieldProtocolFee - : false, - }, - }); - } catch (e) { - console.error('Failed to update token ', token.id, ' error is: ', e); - } - } - } - } - - public async syncPriceRateProvidersForAllPools() { - const subgraphPools = await this.balancerSubgraphService.getAllPools({}, false); - for (const subgraphPool of subgraphPools) { - if (!subgraphPool.priceRateProviders || !subgraphPool.priceRateProviders.length) continue; - - const poolTokens = subgraphPool.tokens || []; - for (let i = 0; i < poolTokens.length; i++) { - const token = poolTokens[i]; - - let priceRateProvider; - const data = subgraphPool.priceRateProviders.find( - (provider) => provider.token.address === token.address, - ); - priceRateProvider = data?.address; - if (!priceRateProvider) continue; - - try { - await prisma.prismaPoolToken.update({ - where: { id_chain: { id: token.id, chain: networkContext.chain } }, - data: { - priceRateProvider, - }, - }); - } catch (e) { - console.error('Failed to update token ', token.id, ' error is: ', e); - } - } - } - } - public async addToBlackList(poolId: string) { const category = await prisma.prismaPoolCategory.findFirst({ where: { poolId, chain: this.chain, category: 'BLACK_LISTED' }, @@ -438,13 +379,8 @@ export class PoolService { where: { chain: this.chain, poolId: poolId }, }); - const poolTokenIds = poolTokens.map((poolToken) => poolToken.id); const poolTokenAddresses = poolTokens.map((poolToken) => poolToken.address); - await prisma.prismaPoolSnapshot.deleteMany({ - where: { chain: this.chain, poolId: poolId }, - }); - await prisma.prismaTokenType.deleteMany({ where: { chain: this.chain, tokenAddress: pool.address }, }); @@ -453,50 +389,10 @@ export class PoolService { where: { chain: this.chain, poolId: poolId }, }); - await prisma.prismaPoolTokenDynamicData.deleteMany({ - where: { chain: this.chain, poolTokenId: { in: poolTokenIds } }, - }); - await prisma.prismaTokenDynamicData.deleteMany({ where: { chain: this.chain, tokenAddress: { in: poolTokenAddresses } }, }); - await prisma.prismaPoolToken.deleteMany({ - where: { chain: this.chain, poolId: poolId }, - }); - - await prisma.prismaPoolDynamicData.deleteMany({ - where: { chain: this.chain, poolId: poolId }, - }); - - await prisma.prismaPoolToken.deleteMany({ - where: { chain: this.chain, poolId: poolId }, - }); - - await prisma.prismaPoolLinearData.deleteMany({ - where: { chain: this.chain, poolId: poolId }, - }); - - await prisma.prismaPoolGyroData.deleteMany({ - where: { chain: this.chain, poolId: poolId }, - }); - - await prisma.prismaPoolExpandedTokens.deleteMany({ - where: { chain: this.chain, poolId: poolId }, - }); - - await prisma.prismaPoolLinearDynamicData.deleteMany({ - where: { chain: this.chain, poolId: poolId }, - }); - - await prisma.prismaPoolAprItem.deleteMany({ - where: { chain: this.chain, poolId: poolId }, - }); - - await prisma.prismaPoolSwap.deleteMany({ - where: { chain: this.chain, poolId: poolId }, - }); - const poolStaking = await prisma.prismaPoolStaking.findMany({ where: { chain: this.chain, poolId: poolId }, }); diff --git a/modules/pool/subgraph-mapper.test.ts b/modules/pool/subgraph-mapper.test.ts new file mode 100644 index 000000000..0aa8889b0 --- /dev/null +++ b/modules/pool/subgraph-mapper.test.ts @@ -0,0 +1,103 @@ +import { subgraphToPrismaCreate } from './subgraph-mapper'; +import { poolFactory, poolTokenFactory } from '../../test/factories'; + +describe('subgraphToPrismaCreate', () => { + const weightedPool = poolFactory.build({ + poolType: 'Weighted', + }); + + const stablePool = poolFactory.build({ + poolType: 'ComposableStable', + amp: '0.1', + }); + + const oldStablePool = poolFactory.build({ + poolType: 'StablePhantom', + amp: '0.1', + }); + + const linearPool = poolFactory.build({ + poolType: 'Linear', + wrappedIndex: 1, + upperTarget: '1', + }); + + const elementPool = poolFactory.build({ + poolType: 'Element', + principalToken: '0x123', + }); + + const gyroPool = poolFactory.build({ + poolType: 'GyroE', + alpha: '0.5', + tauAlphaX: '0.5', + }); + + const fxPool = poolFactory.build({ + poolType: 'FX', + alpha: '0.5', + }); + + it('should return correct object for weighted pool', () => { + const result = subgraphToPrismaCreate(weightedPool, 'MAINNET', 1, []); + expect(result.data.type).toBe('WEIGHTED'); + }); + + it('should return correct object for stable pool', () => { + const result = subgraphToPrismaCreate(stablePool, 'MAINNET', 1, []); + expect(result.data.type).toBe('COMPOSABLE_STABLE'); + expect(result.data.stableDynamicData?.create.amp).toBe(stablePool.amp); + }); + + it('should return correct object for old stable pool', () => { + const result = subgraphToPrismaCreate(oldStablePool, 'MAINNET', 1, []); + expect(result.data.type).toBe('COMPOSABLE_STABLE'); + expect(result.data.version).toBe(0); + expect(result.data.stableDynamicData?.create.amp).toBe(oldStablePool.amp); + }); + + it('should return correct object for linear pool', () => { + const result = subgraphToPrismaCreate(linearPool, 'MAINNET', 1, []); + expect(result.data.type).toBe('LINEAR'); + expect(result.data.linearDynamicData?.create.upperTarget).toBe(linearPool.upperTarget); + expect(result.data.linearData?.create.wrappedIndex).toBe(linearPool.wrappedIndex); + }); + + it('should return correct object for element pool', () => { + const result = subgraphToPrismaCreate(elementPool, 'MAINNET', 1, []); + expect(result.data.type).toBe('ELEMENT'); + expect(result.data.elementData?.create.principalToken).toBe(elementPool.principalToken); + }); + + it('should return correct object for gyro pool', () => { + const result = subgraphToPrismaCreate(gyroPool, 'MAINNET', 1, []); + expect(result.data.type).toBe('GYROE'); + expect(result.data.gyroData?.create.alpha).toBe(gyroPool.alpha); + expect(result.data.gyroData?.create.tauAlphaX).toBe(gyroPool.tauAlphaX); + }); + + it('should return correct object for fx pool', () => { + const result = subgraphToPrismaCreate(fxPool, 'MAINNET', 1, []); + expect(result.data.type).toBe('FX'); + expect(result.data.poolTypeSpecificData['alpha']).toBe(gyroPool.alpha); + }); + + describe('nested pools', () => { + const nestedPools = [linearPool]; + const poolWithNestedPools = poolFactory.build({ + poolType: 'ComposableStable', + tokens: [ + poolTokenFactory.build({ + address: linearPool.address, + }), + poolTokenFactory.build({}), + ], + }); + + it('should recognise nested pools', () => { + const result = subgraphToPrismaCreate(poolWithNestedPools, 'MAINNET', 1, nestedPools); + expect(result.data.type).toBe('COMPOSABLE_STABLE'); + expect(result.data.tokens.createMany.data[0].nestedPoolId).toBe(linearPool.id); + }); + }); +}); diff --git a/modules/pool/subgraph-mapper.ts b/modules/pool/subgraph-mapper.ts new file mode 100644 index 000000000..9d09f0692 --- /dev/null +++ b/modules/pool/subgraph-mapper.ts @@ -0,0 +1,291 @@ +import { Chain, PrismaPoolType } from '@prisma/client'; +import { BalancerPoolFragment } from '../subgraphs/balancer-subgraph/generated/balancer-subgraph-types'; +import { AddressZero } from '@ethersproject/constants'; +import { fx, gyro, linear, element, stableDynamic, linearDynamic } from './pool-data'; + +export const subgraphToPrismaCreate = ( + pool: BalancerPoolFragment, + chain: Chain, + blockNumber: number, + nestedPools: { id: string; address: string }[], +) => { + const dbData = subgraphMapper(pool, chain, blockNumber, nestedPools); + + const prismaPoolRecordWithAssociations = { + data: { + ...dbData.base, + staticTypeData: dbData.staticTypeData, + tokens: { + createMany: { + data: dbData.tokens, + }, + }, + dynamicData: { + create: { + id: dbData.base.id, + ...dbData.dynamicData, + }, + }, + linearData: + dbData.base.type === 'LINEAR' + ? { + create: { + id: dbData.base.id, + ...(dbData.staticTypeData as ReturnType), + }, + } + : undefined, + elementData: + dbData.base.type === 'ELEMENT' + ? { + create: { + id: dbData.base.id, + ...(dbData.staticTypeData as ReturnType), + }, + } + : undefined, + gyroData: ['GYRO', 'GYRO3', 'GYROE'].includes(dbData.base.type) + ? { + create: { + id: dbData.base.id, + ...(dbData.staticTypeData as ReturnType), + }, + } + : undefined, + linearDynamicData: + dbData.base.type === 'LINEAR' + ? { + create: { + id: dbData.base.id, + ...(dbData.dynamicTypeData as ReturnType), + }, + } + : undefined, + stableDynamicData: ['STABLE', 'COMPOSABLE_STABLE', 'META_STABLE'].includes(dbData.base.type) + ? { + create: { + id: dbData.base.id, + ...(dbData.dynamicTypeData as ReturnType), + }, + } + : undefined, + }, + }; + + return prismaPoolRecordWithAssociations; +}; + +export const subgraphToPrismaUpdate = ( + pool: BalancerPoolFragment, + chain: Chain, + blockNumber: number, + nestedPools: { id: string; address: string }[], +) => { + const dbData = subgraphMapper(pool, chain, blockNumber, nestedPools); + const { id, ...baseWithoutId } = dbData.base; + + const prismaPoolRecordWithDataAssociations = { + ...baseWithoutId, + staticTypeData: dbData.staticTypeData, + tokens: { + update: dbData.tokens.map((token) => ({ + where: { + id_chain: { + id: token.id, + chain: chain, + }, + }, + data: { + ...token, + }, + })), + }, + linearData: + dbData.base.type === 'LINEAR' + ? { + update: { + ...(dbData.staticTypeData as ReturnType), + }, + } + : undefined, + elementData: + dbData.base.type === 'ELEMENT' + ? { + update: { + ...(dbData.staticTypeData as ReturnType), + }, + } + : undefined, + gyroData: ['GYRO', 'GYRO3', 'GYROE'].includes(dbData.base.type) + ? { + update: { + ...(dbData.staticTypeData as ReturnType), + }, + } + : undefined, + linearDynamicData: + dbData.base.type === 'LINEAR' + ? { + update: { + ...(dbData.dynamicTypeData as ReturnType), + }, + } + : undefined, + stableDynamicData: ['STABLE', 'COMPOSABLE_STABLE', 'META_STABLE'].includes(dbData.base.type) + ? { + update: { + ...(dbData.dynamicTypeData as ReturnType), + }, + } + : undefined, + }; + + return prismaPoolRecordWithDataAssociations; +}; + +const subgraphMapper = ( + pool: BalancerPoolFragment, + chain: Chain, + blockNumber: number, + nestedPools: { id: string; address: string }[], +) => { + const type = mapSubgraphPoolTypeToPoolType(pool.poolType!); + const version = mapPoolTypeVersion(pool.poolType!, pool.poolTypeVersion!); + + const base = { + id: pool.id, + chain: chain, + createTime: pool.createTime, + address: pool.address, + symbol: pool.symbol || '', + name: pool.name || '', + decimals: 18, + type: type, + version: version, + owner: pool.owner || AddressZero, + factory: pool.factory, + }; + + const dynamicData = { + blockNumber, + swapFee: pool.swapFee, + swapEnabled: pool.swapEnabled, + totalShares: pool.totalShares, + totalSharesNum: parseFloat(pool.totalShares), + totalLiquidity: Math.max(parseFloat(pool.totalLiquidity), 0), + }; + + const staticTypeData: ReturnType | {} = Object.keys( + staticTypeDataMapper, + ).includes(type) + ? staticTypeDataMapper[type as keyof typeof staticTypeDataMapper](pool) + : {}; + + const dynamicTypeData = Object.keys(dynamicTypeDataMapper).includes(type) + ? dynamicTypeDataMapper[type as keyof typeof dynamicTypeDataMapper](pool, blockNumber) + : {}; + + const tokens = + pool.tokens?.map((token) => { + const nestedPool = nestedPools.find((nestedPool) => { + return nestedPool.address === token.address; + }); + + let priceRateProvider; + if (pool.priceRateProviders) { + const data = pool.priceRateProviders.find((provider) => provider.token.address === token.address); + priceRateProvider = data?.address; + } + + return { + id: token.id, + address: token.address, + priceRateProvider, + exemptFromProtocolYieldFee: token.isExemptFromYieldProtocolFee + ? token.isExemptFromYieldProtocolFee + : false, + nestedPoolId: nestedPool?.id, + index: token.index || pool.tokensList.findIndex((address) => address === token.address), + }; + }) ?? []; + + return { + base, + dynamicData, + tokens, + staticTypeData, + dynamicTypeData, + }; +}; + +const mapSubgraphPoolTypeToPoolType = (poolType: string): PrismaPoolType => { + switch (poolType) { + case 'Weighted': + return 'WEIGHTED'; + case 'LiquidityBootstrapping': + return 'LIQUIDITY_BOOTSTRAPPING'; + case 'Stable': + return 'STABLE'; + case 'MetaStable': + return 'META_STABLE'; + // for the old phantom stable pool, we add it to the DB as type COMPOSABLE_STABLE with version 0 + case 'StablePhantom': + return 'COMPOSABLE_STABLE'; + case 'ComposableStable': + return 'COMPOSABLE_STABLE'; + case 'Linear': + return 'LINEAR'; + case 'Element': + return 'ELEMENT'; + case 'Investment': + return 'INVESTMENT'; + case 'Gyro2': + return 'GYRO'; + case 'Gyro3': + return 'GYRO3'; + case 'GyroE': + return 'GYROE'; + case 'FX': + return 'FX'; + } + + // balancer still uses AaveLinear, etc, so we account for that here + if (poolType.includes('Linear')) { + return 'LINEAR'; + } + + return 'UNKNOWN'; +}; + +const mapPoolTypeVersion = (poolType: string, poolTypeVersion: number): number => { + // for the old phantom stable pool, we add it to the DB as type COMPOSABLE_STABLE with version 0 + let version = poolTypeVersion ? poolTypeVersion : 1; + if (poolType === 'StablePhantom') { + version = 0; + } + + return version; +}; + +const staticTypeDataMapper = { + ELEMENT: element, + FX: fx, + GYRO: gyro, + GYRO3: gyro, + GYROE: gyro, + LINEAR: linear, +}; + +const dynamicTypeDataMapper = { + STABLE: stableDynamic, + COMPOSABLE_STABLE: stableDynamic, + META_STABLE: stableDynamic, + LINEAR: linearDynamic, +}; + +export type FxData = ReturnType; +export type GyroData = ReturnType; +export type LinearData = ReturnType; +export type ElementData = ReturnType; +export type StableDynamicData = ReturnType; +export type LinearDynamicData = ReturnType; diff --git a/modules/sor/sorV2/sorV2.service.ts b/modules/sor/sorV2/sorV2.service.ts index 279c65459..55672579b 100644 --- a/modules/sor/sorV2/sorV2.service.ts +++ b/modules/sor/sorV2/sorV2.service.ts @@ -13,6 +13,7 @@ import { RawGyro2Pool, RawGyro3Pool, RawGyroEPool, + RawFxPool, HumanAmount, SupportedRawPoolTypes, SingleSwap, @@ -44,6 +45,7 @@ import { poolsToIgnore } from '../constants'; import { AllNetworkConfigsKeyedOnChain, chainToIdMap } from '../../network/network-config'; import * as Sentry from '@sentry/node'; import { getToken } from '../utils'; +import { FxData } from '../../pool/subgraph-mapper'; const ALL_BASEPOOLS_CACHE_KEY = `basePools:all`; @@ -377,7 +379,6 @@ export class SorV2Service implements SwapService { 'ELEMENT', // not supported by b-sdk 'UNKNOWN', // not supported by b-sdk 'INVESTMENT', // not supported by b-sdk - 'FX', // needs more data ], }, AND: { @@ -428,6 +429,22 @@ export class SorV2Service implements SwapService { inRecoveryMode: !!prismaPool.dynamicData?.isInRecoveryMode, name: 'n/a', }; + if (['FX'].includes(rawPool.poolType)) { + const data = prismaPool.staticTypeData as FxData; + rawPool = { + ...rawPool, + ...data, + tokens: rawPool.tokens.map((t, i) => { + return { + ...t, + token: { + latestFXPrice: prismaPool.tokens[i].dynamicData?.latestFxPrice?.toString(), + fxOracleDecimals: 18, + }, + }; + }), + } as RawFxPool; + } if (['Weighted', 'Investment', 'LiquidityBootstrapping'].includes(rawPool.poolType)) { rawPool = { ...rawPool, diff --git a/modules/subgraphs/balancer-subgraph/balancer-subgraph-queries.graphql b/modules/subgraphs/balancer-subgraph/balancer-subgraph-queries.graphql index 5469dc094..faa6bd405 100644 --- a/modules/subgraphs/balancer-subgraph/balancer-subgraph-queries.graphql +++ b/modules/subgraphs/balancer-subgraph/balancer-subgraph-queries.graphql @@ -198,6 +198,9 @@ fragment BalancerPool on Pool { w z dSq + delta + epsilon + priceRateProviders { address token { diff --git a/modules/user/user.prisma b/modules/user/user.prisma index 69746e651..11cdd6c6e 100644 --- a/modules/user/user.prisma +++ b/modules/user/user.prisma @@ -26,7 +26,7 @@ model PrismaUserWalletBalance { user PrismaUser @relation(fields:[userAddress], references: [address]) poolId String? - pool PrismaPool? @relation(fields:[poolId, chain], references: [id, chain]) + pool PrismaPool? @relation(fields:[poolId, chain], references: [id, chain], onDelete: Cascade) tokenAddress String token PrismaToken @relation(fields:[tokenAddress, chain], references: [address, chain]) @@ -46,13 +46,13 @@ model PrismaUserStakedBalance { user PrismaUser @relation(fields:[userAddress], references: [address]) poolId String? - pool PrismaPool? @relation(fields:[poolId, chain], references: [id, chain]) + pool PrismaPool? @relation(fields:[poolId, chain], references: [id, chain], onDelete: Cascade) tokenAddress String token PrismaToken @relation(fields:[tokenAddress, chain], references: [address, chain]) stakingId String - staking PrismaPoolStaking @relation(fields:[stakingId, chain], references: [id, chain]) + staking PrismaPoolStaking @relation(fields:[stakingId, chain], references: [id, chain], onDelete: Cascade) } model PrismaUserBalanceSyncStatus { diff --git a/modules/vebal/vebal.prisma b/modules/vebal/vebal.prisma index df28431b5..3ea981dc9 100644 --- a/modules/vebal/vebal.prisma +++ b/modules/vebal/vebal.prisma @@ -31,7 +31,7 @@ model PrismaVotingGauge { id String chain Chain - stakingGauge PrismaPoolStakingGauge? @relation(fields: [stakingGaugeId, chain], references: [id, chain]) + stakingGauge PrismaPoolStakingGauge? @relation(fields: [stakingGaugeId, chain], references: [id, chain], onDelete: Cascade) status PrismaVotingGaugeStatus @default(ACTIVE) gaugeAddress String stakingGaugeId String? diff --git a/package.json b/package.json index ce3785b5f..8165f0df3 100644 --- a/package.json +++ b/package.json @@ -82,6 +82,7 @@ "@types/uuid": "^8.0.0", "concurrently": "^6.4.0", "execa": "^4.1.0", + "fishery": "^2.2.2", "jest": "^28.1.3", "jest-watch-typeahead": "^1.0.0", "msw": "^0.47.3", diff --git a/prisma/migrations/20231221153955_allow_deleting_pools_with_associations/migration.sql b/prisma/migrations/20231221153955_allow_deleting_pools_with_associations/migration.sql new file mode 100644 index 000000000..7160564a4 --- /dev/null +++ b/prisma/migrations/20231221153955_allow_deleting_pools_with_associations/migration.sql @@ -0,0 +1,131 @@ +-- DropForeignKey +ALTER TABLE "PrismaPoolAprItem" DROP CONSTRAINT "PrismaPoolAprItem_poolId_chain_fkey"; + +-- DropForeignKey +ALTER TABLE "PrismaPoolAprRange" DROP CONSTRAINT "PrismaPoolAprRange_aprItemId_chain_fkey"; + +-- DropForeignKey +ALTER TABLE "PrismaPoolDynamicData" DROP CONSTRAINT "PrismaPoolDynamicData_poolId_chain_fkey"; + +-- DropForeignKey +ALTER TABLE "PrismaPoolElementData" DROP CONSTRAINT "PrismaPoolElementData_poolId_chain_fkey"; + +-- DropForeignKey +ALTER TABLE "PrismaPoolExpandedTokens" DROP CONSTRAINT "PrismaPoolExpandedTokens_nestedPoolId_chain_fkey"; + +-- DropForeignKey +ALTER TABLE "PrismaPoolExpandedTokens" DROP CONSTRAINT "PrismaPoolExpandedTokens_poolId_chain_fkey"; + +-- DropForeignKey +ALTER TABLE "PrismaPoolFilterMap" DROP CONSTRAINT "PrismaPoolFilterMap_poolId_chain_fkey"; + +-- DropForeignKey +ALTER TABLE "PrismaPoolGyroData" DROP CONSTRAINT "PrismaPoolGyroData_poolId_chain_fkey"; + +-- DropForeignKey +ALTER TABLE "PrismaPoolLinearData" DROP CONSTRAINT "PrismaPoolLinearData_poolId_chain_fkey"; + +-- DropForeignKey +ALTER TABLE "PrismaPoolLinearDynamicData" DROP CONSTRAINT "PrismaPoolLinearDynamicData_poolId_chain_fkey"; + +-- DropForeignKey +ALTER TABLE "PrismaPoolSnapshot" DROP CONSTRAINT "PrismaPoolSnapshot_poolId_chain_fkey"; + +-- DropForeignKey +ALTER TABLE "PrismaPoolStableDynamicData" DROP CONSTRAINT "PrismaPoolStableDynamicData_poolId_chain_fkey"; + +-- DropForeignKey +ALTER TABLE "PrismaPoolStaking" DROP CONSTRAINT "PrismaPoolStaking_poolId_chain_fkey"; + +-- DropForeignKey +ALTER TABLE "PrismaPoolStakingGauge" DROP CONSTRAINT "PrismaPoolStakingGauge_stakingId_chain_fkey"; + +-- DropForeignKey +ALTER TABLE "PrismaPoolStakingGaugeReward" DROP CONSTRAINT "PrismaPoolStakingGaugeReward_gaugeId_chain_fkey"; + +-- DropForeignKey +ALTER TABLE "PrismaPoolSwap" DROP CONSTRAINT "PrismaPoolSwap_poolId_chain_fkey"; + +-- DropForeignKey +ALTER TABLE "PrismaPoolToken" DROP CONSTRAINT "PrismaPoolToken_poolId_chain_fkey"; + +-- DropForeignKey +ALTER TABLE "PrismaPoolTokenDynamicData" DROP CONSTRAINT "PrismaPoolTokenDynamicData_poolTokenId_chain_fkey"; + +-- DropForeignKey +ALTER TABLE "PrismaUserStakedBalance" DROP CONSTRAINT "PrismaUserStakedBalance_poolId_chain_fkey"; + +-- DropForeignKey +ALTER TABLE "PrismaUserStakedBalance" DROP CONSTRAINT "PrismaUserStakedBalance_stakingId_chain_fkey"; + +-- DropForeignKey +ALTER TABLE "PrismaUserWalletBalance" DROP CONSTRAINT "PrismaUserWalletBalance_poolId_chain_fkey"; + +-- DropForeignKey +ALTER TABLE "PrismaVotingGauge" DROP CONSTRAINT "PrismaVotingGauge_stakingGaugeId_chain_fkey"; + +-- AddForeignKey +ALTER TABLE "PrismaPoolLinearData" ADD CONSTRAINT "PrismaPoolLinearData_poolId_chain_fkey" FOREIGN KEY ("poolId", "chain") REFERENCES "PrismaPool"("id", "chain") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "PrismaPoolElementData" ADD CONSTRAINT "PrismaPoolElementData_poolId_chain_fkey" FOREIGN KEY ("poolId", "chain") REFERENCES "PrismaPool"("id", "chain") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "PrismaPoolGyroData" ADD CONSTRAINT "PrismaPoolGyroData_poolId_chain_fkey" FOREIGN KEY ("poolId", "chain") REFERENCES "PrismaPool"("id", "chain") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "PrismaPoolDynamicData" ADD CONSTRAINT "PrismaPoolDynamicData_poolId_chain_fkey" FOREIGN KEY ("poolId", "chain") REFERENCES "PrismaPool"("id", "chain") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "PrismaPoolStableDynamicData" ADD CONSTRAINT "PrismaPoolStableDynamicData_poolId_chain_fkey" FOREIGN KEY ("poolId", "chain") REFERENCES "PrismaPool"("id", "chain") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "PrismaPoolLinearDynamicData" ADD CONSTRAINT "PrismaPoolLinearDynamicData_poolId_chain_fkey" FOREIGN KEY ("poolId", "chain") REFERENCES "PrismaPool"("id", "chain") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "PrismaPoolToken" ADD CONSTRAINT "PrismaPoolToken_poolId_chain_fkey" FOREIGN KEY ("poolId", "chain") REFERENCES "PrismaPool"("id", "chain") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "PrismaPoolTokenDynamicData" ADD CONSTRAINT "PrismaPoolTokenDynamicData_poolTokenId_chain_fkey" FOREIGN KEY ("poolTokenId", "chain") REFERENCES "PrismaPoolToken"("id", "chain") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "PrismaPoolSwap" ADD CONSTRAINT "PrismaPoolSwap_poolId_chain_fkey" FOREIGN KEY ("poolId", "chain") REFERENCES "PrismaPool"("id", "chain") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "PrismaPoolAprItem" ADD CONSTRAINT "PrismaPoolAprItem_poolId_chain_fkey" FOREIGN KEY ("poolId", "chain") REFERENCES "PrismaPool"("id", "chain") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "PrismaPoolAprRange" ADD CONSTRAINT "PrismaPoolAprRange_aprItemId_chain_fkey" FOREIGN KEY ("aprItemId", "chain") REFERENCES "PrismaPoolAprItem"("id", "chain") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "PrismaPoolExpandedTokens" ADD CONSTRAINT "PrismaPoolExpandedTokens_poolId_chain_fkey" FOREIGN KEY ("poolId", "chain") REFERENCES "PrismaPool"("id", "chain") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "PrismaPoolExpandedTokens" ADD CONSTRAINT "PrismaPoolExpandedTokens_nestedPoolId_chain_fkey" FOREIGN KEY ("nestedPoolId", "chain") REFERENCES "PrismaPool"("id", "chain") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "PrismaPoolFilterMap" ADD CONSTRAINT "PrismaPoolFilterMap_poolId_chain_fkey" FOREIGN KEY ("poolId", "chain") REFERENCES "PrismaPool"("id", "chain") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "PrismaPoolStaking" ADD CONSTRAINT "PrismaPoolStaking_poolId_chain_fkey" FOREIGN KEY ("poolId", "chain") REFERENCES "PrismaPool"("id", "chain") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "PrismaPoolStakingGauge" ADD CONSTRAINT "PrismaPoolStakingGauge_stakingId_chain_fkey" FOREIGN KEY ("stakingId", "chain") REFERENCES "PrismaPoolStaking"("id", "chain") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "PrismaPoolStakingGaugeReward" ADD CONSTRAINT "PrismaPoolStakingGaugeReward_gaugeId_chain_fkey" FOREIGN KEY ("gaugeId", "chain") REFERENCES "PrismaPoolStakingGauge"("id", "chain") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "PrismaPoolSnapshot" ADD CONSTRAINT "PrismaPoolSnapshot_poolId_chain_fkey" FOREIGN KEY ("poolId", "chain") REFERENCES "PrismaPool"("id", "chain") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "PrismaUserWalletBalance" ADD CONSTRAINT "PrismaUserWalletBalance_poolId_chain_fkey" FOREIGN KEY ("poolId", "chain") REFERENCES "PrismaPool"("id", "chain") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "PrismaUserStakedBalance" ADD CONSTRAINT "PrismaUserStakedBalance_poolId_chain_fkey" FOREIGN KEY ("poolId", "chain") REFERENCES "PrismaPool"("id", "chain") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "PrismaUserStakedBalance" ADD CONSTRAINT "PrismaUserStakedBalance_stakingId_chain_fkey" FOREIGN KEY ("stakingId", "chain") REFERENCES "PrismaPoolStaking"("id", "chain") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "PrismaVotingGauge" ADD CONSTRAINT "PrismaVotingGauge_stakingGaugeId_chain_fkey" FOREIGN KEY ("stakingGaugeId", "chain") REFERENCES "PrismaPoolStakingGauge"("id", "chain") ON DELETE CASCADE ON UPDATE CASCADE; diff --git a/prisma/migrations/20231221154038_more_pool_defaults/migration.sql b/prisma/migrations/20231221154038_more_pool_defaults/migration.sql new file mode 100644 index 000000000..56c17b3ad --- /dev/null +++ b/prisma/migrations/20231221154038_more_pool_defaults/migration.sql @@ -0,0 +1,3 @@ +-- AlterTable +ALTER TABLE "PrismaPoolDynamicData" ALTER COLUMN "volume24h" SET DEFAULT 0, +ALTER COLUMN "fees24h" SET DEFAULT 0; diff --git a/prisma/migrations/20231221154118_pools_data_json_column/migration.sql b/prisma/migrations/20231221154118_pools_data_json_column/migration.sql new file mode 100644 index 000000000..873e5ef60 --- /dev/null +++ b/prisma/migrations/20231221154118_pools_data_json_column/migration.sql @@ -0,0 +1,2 @@ +-- AlterTable +ALTER TABLE "PrismaPool" ADD COLUMN "staticTypeData" JSONB NOT NULL DEFAULT '{}'; diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 186f5c992..f3320019f 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -64,6 +64,7 @@ model PrismaPool { linearData PrismaPoolLinearData? elementData PrismaPoolElementData? gyroData PrismaPoolGyroData? + staticTypeData Json @default("{}") tokens PrismaPoolToken[] @@ -111,7 +112,7 @@ model PrismaPoolLinearData { id String poolId String - pool PrismaPool @relation(fields:[poolId, chain], references: [id, chain]) + pool PrismaPool @relation(fields:[poolId, chain], references: [id, chain], onDelete: Cascade) chain Chain mainIndex Int @@ -124,7 +125,7 @@ model PrismaPoolElementData { id String poolId String - pool PrismaPool @relation(fields:[poolId, chain], references: [id, chain]) + pool PrismaPool @relation(fields:[poolId, chain], references: [id, chain], onDelete: Cascade) chain Chain unitSeconds String @@ -138,7 +139,7 @@ model PrismaPoolGyroData{ id String poolId String - pool PrismaPool @relation(fields:[poolId, chain], references: [id, chain]) + pool PrismaPool @relation(fields:[poolId, chain], references: [id, chain], onDelete: Cascade) chain Chain alpha String @@ -166,7 +167,7 @@ model PrismaPoolDynamicData { id String poolId String - pool PrismaPool @relation(fields:[poolId, chain], references: [id, chain]) + pool PrismaPool @relation(fields:[poolId, chain], references: [id, chain], onDelete: Cascade) chain Chain blockNumber Int updatedAt DateTime @updatedAt @@ -179,8 +180,8 @@ model PrismaPoolDynamicData { totalShares String totalSharesNum Float @default(0) totalLiquidity Float - volume24h Float - fees24h Float + volume24h Float @default(0) + fees24h Float @default(0) yieldCapture24h Float @default(0) apr Float @default(0) volume48h Float @default(0) @@ -220,7 +221,7 @@ model PrismaPoolStableDynamicData { id String poolId String - pool PrismaPool @relation(fields:[poolId, chain], references: [id, chain]) + pool PrismaPool @relation(fields:[poolId, chain], references: [id, chain], onDelete: Cascade) chain Chain blockNumber Int updatedAt DateTime @updatedAt @@ -234,7 +235,7 @@ model PrismaPoolLinearDynamicData { id String poolId String - pool PrismaPool @relation(fields:[poolId, chain], references: [id, chain]) + pool PrismaPool @relation(fields:[poolId, chain], references: [id, chain], onDelete: Cascade) chain Chain blockNumber Int updatedAt DateTime @updatedAt @@ -248,7 +249,7 @@ model PrismaPoolToken { id String poolId String - pool PrismaPool @relation(fields:[poolId, chain], references: [id, chain]) + pool PrismaPool @relation(fields:[poolId, chain], references: [id, chain], onDelete: Cascade) chain Chain address String @@ -270,7 +271,7 @@ model PrismaPoolTokenDynamicData { id String poolTokenId String - poolToken PrismaPoolToken @relation(fields:[poolTokenId, chain], references: [id, chain]) + poolToken PrismaPoolToken @relation(fields:[poolTokenId, chain], references: [id, chain], onDelete: Cascade) chain Chain blockNumber Int updatedAt DateTime @updatedAt @@ -287,7 +288,7 @@ model PrismaPoolSwap { id String poolId String - pool PrismaPool @relation(fields:[poolId, chain], references: [id, chain]) + pool PrismaPool @relation(fields:[poolId, chain], references: [id, chain], onDelete: Cascade) chain Chain userAddress String @@ -330,7 +331,7 @@ model PrismaPoolAprItem { id String poolId String - pool PrismaPool @relation(fields:[poolId, chain], references: [id, chain]) + pool PrismaPool @relation(fields:[poolId, chain], references: [id, chain], onDelete: Cascade) chain Chain title String apr Float @@ -347,7 +348,7 @@ model PrismaPoolAprRange { id String chain Chain aprItemId String - aprItem PrismaPoolAprItem @relation(fields:[aprItemId, chain], references: [id, chain]) + aprItem PrismaPoolAprItem @relation(fields:[aprItemId, chain], references: [id, chain], onDelete: Cascade) min Float max Float } @@ -401,11 +402,11 @@ model PrismaPoolExpandedTokens { tokenAddress String token PrismaToken @relation(fields:[tokenAddress, chain], references: [address, chain]) poolId String - pool PrismaPool @relation(fields:[poolId, chain], references: [id, chain]) + pool PrismaPool @relation(fields:[poolId, chain], references: [id, chain], onDelete: Cascade) chain Chain nestedPoolId String? - nestedPool PrismaPool? @relation(name: "NestedPoolForAllToken", fields:[nestedPoolId, chain], references: [id, chain]) + nestedPool PrismaPool? @relation(name: "NestedPoolForAllToken", fields:[nestedPoolId, chain], references: [id, chain], onDelete: Cascade) } @@ -427,7 +428,7 @@ model PrismaPoolFilterMap { filterId String filter PrismaPoolFilter @relation(fields:[filterId, chain], references: [id, chain]) poolId String - pool PrismaPool @relation(fields:[poolId, chain], references: [id, chain]) + pool PrismaPool @relation(fields:[poolId, chain], references: [id, chain], onDelete: Cascade) } model PrismaPoolStaking { @@ -435,7 +436,7 @@ model PrismaPoolStaking { id String poolId String - pool PrismaPool @relation(fields:[poolId, chain], references: [id, chain]) + pool PrismaPool @relation(fields:[poolId, chain], references: [id, chain], onDelete: Cascade) chain Chain type PrismaPoolStakingType address String @@ -485,7 +486,7 @@ model PrismaPoolStakingGauge { id String stakingId String - staking PrismaPoolStaking @relation(fields:[stakingId, chain], references: [id, chain]) + staking PrismaPoolStaking @relation(fields:[stakingId, chain], references: [id, chain], onDelete: Cascade) chain Chain gaugeAddress String @@ -509,7 +510,7 @@ model PrismaPoolStakingGaugeReward{ id String gaugeId String - gauge PrismaPoolStakingGauge @relation(fields:[gaugeId, chain], references: [id, chain]) + gauge PrismaPoolStakingGauge @relation(fields:[gaugeId, chain], references: [id, chain], onDelete: Cascade) chain Chain tokenAddress String @@ -556,7 +557,7 @@ model PrismaPoolSnapshot { id String poolId String - pool PrismaPool @relation(fields:[poolId, chain], references: [id, chain]) + pool PrismaPool @relation(fields:[poolId, chain], references: [id, chain], onDelete: Cascade) chain Chain timestamp Int @@ -787,7 +788,7 @@ model PrismaUserWalletBalance { user PrismaUser @relation(fields:[userAddress], references: [address]) poolId String? - pool PrismaPool? @relation(fields:[poolId, chain], references: [id, chain]) + pool PrismaPool? @relation(fields:[poolId, chain], references: [id, chain], onDelete: Cascade) tokenAddress String token PrismaToken @relation(fields:[tokenAddress, chain], references: [address, chain]) @@ -807,13 +808,13 @@ model PrismaUserStakedBalance { user PrismaUser @relation(fields:[userAddress], references: [address]) poolId String? - pool PrismaPool? @relation(fields:[poolId, chain], references: [id, chain]) + pool PrismaPool? @relation(fields:[poolId, chain], references: [id, chain], onDelete: Cascade) tokenAddress String token PrismaToken @relation(fields:[tokenAddress, chain], references: [address, chain]) stakingId String - staking PrismaPoolStaking @relation(fields:[stakingId, chain], references: [id, chain]) + staking PrismaPoolStaking @relation(fields:[stakingId, chain], references: [id, chain], onDelete: Cascade) } model PrismaUserBalanceSyncStatus { @@ -903,7 +904,7 @@ model PrismaVotingGauge { id String chain Chain - stakingGauge PrismaPoolStakingGauge? @relation(fields: [stakingGaugeId, chain], references: [id, chain]) + stakingGauge PrismaPoolStakingGauge? @relation(fields: [stakingGaugeId, chain], references: [id, chain], onDelete: Cascade) status PrismaVotingGaugeStatus @default(ACTIVE) gaugeAddress String stakingGaugeId String? diff --git a/test/factories/index.ts b/test/factories/index.ts new file mode 100644 index 000000000..6706ad5bc --- /dev/null +++ b/test/factories/index.ts @@ -0,0 +1,2 @@ +export * from './pool.factory'; +export * from './pool_token.factory'; diff --git a/test/factories/pool.factory.ts b/test/factories/pool.factory.ts new file mode 100644 index 000000000..a64270533 --- /dev/null +++ b/test/factories/pool.factory.ts @@ -0,0 +1,50 @@ +import { Factory } from 'fishery'; +import { BalancerPoolFragment } from '../../modules/subgraphs/balancer-subgraph/generated/balancer-subgraph-types'; + +export const poolFactory = Factory.define(() => ({ + id: '123', + poolType: 'A', + address: '0x123456789', + swapFee: '0.01', + totalSwapVolume: '1000', + totalSwapFee: '10', + totalLiquidity: '10000', + totalShares: '100', + swapsCount: '5', + holdersCount: '10', + createTime: 1234567890, // Add createTime property as a number + swapEnabled: true, // Add swapEnabled property + tokensList: ['token1', 'token2'], // Add tokensList property + tokens: [ + { + id: 'token1', + symbol: 'T1', + name: 'Token 1', + decimals: 18, + address: 'token1', + balance: '100', + weight: '0.5', + priceRate: '1', + index: 0, + token: { + // token properties... + }, + }, + { + id: 'token2', + symbol: 'T2', + name: 'Token 2', + decimals: 18, + address: 'token2', + balance: '200', + weight: '0.5', + priceRate: '1', + index: 1, + token: { + // token properties... + }, + }, + ], + + // Add other required properties... +})); diff --git a/test/factories/pool_token.factory.ts b/test/factories/pool_token.factory.ts new file mode 100644 index 000000000..50c03cc75 --- /dev/null +++ b/test/factories/pool_token.factory.ts @@ -0,0 +1,17 @@ +import { Factory } from 'fishery'; +import { BalancerPoolTokenFragment } from '../../modules/subgraphs/balancer-subgraph/generated/balancer-subgraph-types'; + +export const poolTokenFactory = Factory.define(() => ({ + id: 'token1', + symbol: 'T1', + name: 'Token 1', + decimals: 18, + address: 'token1', + balance: '100', + weight: '0.5', + priceRate: '1', + index: 0, + token: { + // token properties... + }, +})); diff --git a/yarn.lock b/yarn.lock index 1f593b651..830c56f46 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7280,6 +7280,13 @@ find-up@^4.0.0, find-up@^4.1.0: locate-path "^5.0.0" path-exists "^4.0.0" +fishery@^2.2.2: + version "2.2.2" + resolved "https://registry.yarnpkg.com/fishery/-/fishery-2.2.2.tgz#94d3d9380295dd3ce555021e9353c5348b8beb77" + integrity sha512-jeU0nDhPHJkupmjX+r9niKgVMTBDB8X+U/pktoGHAiWOSyNlMd0HhmqnjrpjUOCDPJYaSSu4Ze16h6dZOKSp2w== + dependencies: + lodash.mergewith "^4.6.2" + follow-redirects@^1.0.0: version "1.15.3" resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.3.tgz#fe2f3ef2690afce7e82ed0b44db08165b207123a" @@ -9263,6 +9270,11 @@ lodash.memoize@4.x: resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe" integrity sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4= +lodash.mergewith@^4.6.2: + version "4.6.2" + resolved "https://registry.yarnpkg.com/lodash.mergewith/-/lodash.mergewith-4.6.2.tgz#617121f89ac55f59047c7aec1ccd6654c6590f55" + integrity sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ== + lodash.once@^4.0.0: version "4.1.1" resolved "https://registry.yarnpkg.com/lodash.once/-/lodash.once-4.1.1.tgz#0dd3971213c7c56df880977d504c88fb471a97ac" From 27637b3f6c032108f1a28c7f8761147873e35ddf Mon Sep 17 00:00:00 2001 From: franzns <93920061+franzns@users.noreply.github.com> Date: Thu, 18 Jan 2024 16:29:48 +0100 Subject: [PATCH 09/15] Raw query purge (#42) * stop token price purge for now * purge tokens using raw query --- modules/token/lib/token-price.service.ts | 25 ++++-------------------- 1 file changed, 4 insertions(+), 21 deletions(-) diff --git a/modules/token/lib/token-price.service.ts b/modules/token/lib/token-price.service.ts index 9b51277fb..572b450bb 100644 --- a/modules/token/lib/token-price.service.ts +++ b/modules/token/lib/token-price.service.ts @@ -256,28 +256,11 @@ export class TokenPriceService { } public async purgeOldTokenPricesForAllChains(): Promise { - const purgeBeforeTimestamp = moment().startOf('day').subtract(100, 'days').utc().unix(); - const oldPrices = await prisma.prismaTokenPrice.findMany({ - where: { - timestamp: { lt: purgeBeforeTimestamp }, - }, - }); - - // returns all non midnight prices - const tobeDeleted = _.uniq(oldPrices.filter((tokenPrice) => tokenPrice.timestamp % secondsPerDay !== 0)); - - //apparently prisma has a limitation on delete - const chunks = _.chunk(tobeDeleted, 1000); - - for (const chunk of chunks) { - await prisma.prismaTokenPrice.deleteMany({ - where: { - timestamp: { in: chunk.map((tokenPrice) => tokenPrice.timestamp) }, - }, - }); - } + // DATE(to_timestamp(timestamp)) will return the midnight timestamp. We'll delete all prices that are not midnight timestamps AND are older than 100 days. + const deleted = + await prisma.$executeRaw`DELETE FROM "PrismaTokenPrice" WHERE DATE(to_timestamp(timestamp)) != to_timestamp(timestamp) AND to_timestamp(timestamp) < CURRENT_DATE - INTERVAL '100 days'`; - return tobeDeleted.length; + return deleted; } private async updateCandleStickData() { From ccdd3d8045e848d346da3823d8a4e353ba11be18 Mon Sep 17 00:00:00 2001 From: franzns <93920061+franzns@users.noreply.github.com> Date: Thu, 18 Jan 2024 16:31:25 +0100 Subject: [PATCH 10/15] enable price purge cron (#49) --- modules/network/mainnet.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/modules/network/mainnet.ts b/modules/network/mainnet.ts index 3084758d9..b862ed02f 100644 --- a/modules/network/mainnet.ts +++ b/modules/network/mainnet.ts @@ -492,11 +492,11 @@ export const mainnetNetworkConfig: NetworkConfig = { name: 'sync-global-coingecko-prices', interval: (env.DEPLOYMENT_ENV as DeploymentEnv) === 'canary' ? every(10, 'minutes') : every(2, 'minutes'), }, - // { - // name: 'global-purge-old-tokenprices', - // interval: every(1, 'days'), - // alarmEvaluationPeriod: 1, - // alarmDatapointsToAlarm: 1, - // }, + { + name: 'global-purge-old-tokenprices', + interval: every(1, 'days'), + alarmEvaluationPeriod: 1, + alarmDatapointsToAlarm: 1, + }, ], }; From 5cf75dab828f9c9a29a8aa209a97239d705c8da7 Mon Sep 17 00:00:00 2001 From: franzns <93920061+franzns@users.noreply.github.com> Date: Thu, 18 Jan 2024 16:31:38 +0100 Subject: [PATCH 11/15] increase cron for gnosis and zkevm (#48) --- modules/network/gnosis.ts | 32 ++++++++++++++++++-------------- modules/network/zkevm.ts | 32 ++++++++++++++++++-------------- 2 files changed, 36 insertions(+), 28 deletions(-) diff --git a/modules/network/gnosis.ts b/modules/network/gnosis.ts index fba9f6584..85462a672 100644 --- a/modules/network/gnosis.ts +++ b/modules/network/gnosis.ts @@ -181,27 +181,27 @@ export const gnosisNetworkConfig: NetworkConfig = { }, { name: 'update-liquidity-for-active-pools', - interval: (env.DEPLOYMENT_ENV as DeploymentEnv) === 'canary' ? every(8, 'minutes') : every(4, 'minutes'), + interval: (env.DEPLOYMENT_ENV as DeploymentEnv) === 'canary' ? every(6, 'minutes') : every(2, 'minutes'), }, { name: 'update-pool-apr', - interval: (env.DEPLOYMENT_ENV as DeploymentEnv) === 'canary' ? every(7, 'minutes') : every(5, 'minutes'), + interval: (env.DEPLOYMENT_ENV as DeploymentEnv) === 'canary' ? every(6, 'minutes') : every(2, 'minutes'), }, { name: 'load-on-chain-data-for-pools-with-active-updates', - interval: (env.DEPLOYMENT_ENV as DeploymentEnv) === 'canary' ? every(9, 'minutes') : every(5, 'minutes'), + interval: (env.DEPLOYMENT_ENV as DeploymentEnv) === 'canary' ? every(4, 'minutes') : every(1, 'minutes'), }, { name: 'sync-new-pools-from-subgraph', - interval: (env.DEPLOYMENT_ENV as DeploymentEnv) === 'canary' ? every(12, 'minutes') : every(8, 'minutes'), + interval: (env.DEPLOYMENT_ENV as DeploymentEnv) === 'canary' ? every(6, 'minutes') : every(2, 'minutes'), }, { name: 'sync-tokens-from-pool-tokens', - interval: (env.DEPLOYMENT_ENV as DeploymentEnv) === 'canary' ? every(10, 'minutes') : every(7, 'minutes'), + interval: (env.DEPLOYMENT_ENV as DeploymentEnv) === 'canary' ? every(10, 'minutes') : every(5, 'minutes'), }, { name: 'update-liquidity-24h-ago-for-all-pools', - interval: (env.DEPLOYMENT_ENV as DeploymentEnv) === 'canary' ? every(15, 'minutes') : every(8, 'minutes'), + interval: (env.DEPLOYMENT_ENV as DeploymentEnv) === 'canary' ? every(10, 'minutes') : every(5, 'minutes'), }, { name: 'cache-average-block-time', @@ -209,7 +209,7 @@ export const gnosisNetworkConfig: NetworkConfig = { }, { name: 'sync-staking-for-pools', - interval: (env.DEPLOYMENT_ENV as DeploymentEnv) === 'canary' ? every(15, 'minutes') : every(10, 'minutes'), + interval: (env.DEPLOYMENT_ENV as DeploymentEnv) === 'canary' ? every(10, 'minutes') : every(5, 'minutes'), }, { name: 'sync-latest-snapshots-for-all-pools', @@ -217,21 +217,25 @@ export const gnosisNetworkConfig: NetworkConfig = { }, { name: 'update-lifetime-values-for-all-pools', - interval: every(45, 'minutes'), + interval: every(50, 'minutes'), }, { name: 'sync-changed-pools', - interval: (env.DEPLOYMENT_ENV as DeploymentEnv) === 'canary' ? every(2, 'minutes') : every(1, 'minutes'), + interval: (env.DEPLOYMENT_ENV as DeploymentEnv) === 'canary' ? every(2, 'minutes') : every(30, 'seconds'), alarmEvaluationPeriod: (env.DEPLOYMENT_ENV as DeploymentEnv) === 'canary' ? 3 : 1, alarmDatapointsToAlarm: (env.DEPLOYMENT_ENV as DeploymentEnv) === 'canary' ? 3 : 1, }, { name: 'user-sync-wallet-balances-for-all-pools', - interval: (env.DEPLOYMENT_ENV as DeploymentEnv) === 'canary' ? every(29, 'minutes') : every(9, 'minutes'), + interval: (env.DEPLOYMENT_ENV as DeploymentEnv) === 'canary' ? every(5, 'minutes') : every(20, 'seconds'), + alarmEvaluationPeriod: (env.DEPLOYMENT_ENV as DeploymentEnv) === 'canary' ? 3 : 1, + alarmDatapointsToAlarm: (env.DEPLOYMENT_ENV as DeploymentEnv) === 'canary' ? 3 : 1, }, { name: 'user-sync-staked-balances', - interval: (env.DEPLOYMENT_ENV as DeploymentEnv) === 'canary' ? every(31, 'minutes') : every(11, 'minutes'), + interval: (env.DEPLOYMENT_ENV as DeploymentEnv) === 'canary' ? every(5, 'minutes') : every(20, 'seconds'), + alarmEvaluationPeriod: (env.DEPLOYMENT_ENV as DeploymentEnv) === 'canary' ? 3 : 1, + alarmDatapointsToAlarm: (env.DEPLOYMENT_ENV as DeploymentEnv) === 'canary' ? 3 : 1, }, { name: 'sync-coingecko-coinids', @@ -239,15 +243,15 @@ export const gnosisNetworkConfig: NetworkConfig = { }, { name: 'update-fee-volume-yield-all-pools', - interval: every(75, 'minutes'), + interval: every(1, 'hours'), }, { name: 'sync-vebal-balances', - interval: (env.DEPLOYMENT_ENV as DeploymentEnv) === 'canary' ? every(20, 'minutes') : every(14, 'minutes'), + interval: (env.DEPLOYMENT_ENV as DeploymentEnv) === 'canary' ? every(9, 'minutes') : every(3, 'minutes'), }, { name: 'sync-vebal-totalSupply', - interval: (env.DEPLOYMENT_ENV as DeploymentEnv) === 'canary' ? every(20, 'minutes') : every(16, 'minutes'), + interval: (env.DEPLOYMENT_ENV as DeploymentEnv) === 'canary' ? every(10, 'minutes') : every(5, 'minutes'), }, { name: 'feed-data-to-datastudio', diff --git a/modules/network/zkevm.ts b/modules/network/zkevm.ts index d5e229b87..2e118fc60 100644 --- a/modules/network/zkevm.ts +++ b/modules/network/zkevm.ts @@ -211,27 +211,27 @@ export const zkevmNetworkConfig: NetworkConfig = { }, { name: 'update-liquidity-for-active-pools', - interval: (env.DEPLOYMENT_ENV as DeploymentEnv) === 'canary' ? every(8, 'minutes') : every(4, 'minutes'), + interval: (env.DEPLOYMENT_ENV as DeploymentEnv) === 'canary' ? every(6, 'minutes') : every(2, 'minutes'), }, { name: 'update-pool-apr', - interval: (env.DEPLOYMENT_ENV as DeploymentEnv) === 'canary' ? every(7, 'minutes') : every(5, 'minutes'), + interval: (env.DEPLOYMENT_ENV as DeploymentEnv) === 'canary' ? every(6, 'minutes') : every(2, 'minutes'), }, { name: 'load-on-chain-data-for-pools-with-active-updates', - interval: (env.DEPLOYMENT_ENV as DeploymentEnv) === 'canary' ? every(9, 'minutes') : every(5, 'minutes'), + interval: (env.DEPLOYMENT_ENV as DeploymentEnv) === 'canary' ? every(4, 'minutes') : every(1, 'minutes'), }, { name: 'sync-new-pools-from-subgraph', - interval: (env.DEPLOYMENT_ENV as DeploymentEnv) === 'canary' ? every(12, 'minutes') : every(8, 'minutes'), + interval: (env.DEPLOYMENT_ENV as DeploymentEnv) === 'canary' ? every(6, 'minutes') : every(2, 'minutes'), }, { name: 'sync-tokens-from-pool-tokens', - interval: (env.DEPLOYMENT_ENV as DeploymentEnv) === 'canary' ? every(10, 'minutes') : every(7, 'minutes'), + interval: (env.DEPLOYMENT_ENV as DeploymentEnv) === 'canary' ? every(10, 'minutes') : every(5, 'minutes'), }, { name: 'update-liquidity-24h-ago-for-all-pools', - interval: (env.DEPLOYMENT_ENV as DeploymentEnv) === 'canary' ? every(15, 'minutes') : every(8, 'minutes'), + interval: (env.DEPLOYMENT_ENV as DeploymentEnv) === 'canary' ? every(10, 'minutes') : every(5, 'minutes'), }, { name: 'cache-average-block-time', @@ -239,7 +239,7 @@ export const zkevmNetworkConfig: NetworkConfig = { }, { name: 'sync-staking-for-pools', - interval: (env.DEPLOYMENT_ENV as DeploymentEnv) === 'canary' ? every(15, 'minutes') : every(10, 'minutes'), + interval: (env.DEPLOYMENT_ENV as DeploymentEnv) === 'canary' ? every(10, 'minutes') : every(5, 'minutes'), }, { name: 'sync-latest-snapshots-for-all-pools', @@ -247,21 +247,25 @@ export const zkevmNetworkConfig: NetworkConfig = { }, { name: 'update-lifetime-values-for-all-pools', - interval: every(45, 'minutes'), + interval: every(50, 'minutes'), }, { name: 'sync-changed-pools', - interval: (env.DEPLOYMENT_ENV as DeploymentEnv) === 'canary' ? every(2, 'minutes') : every(1, 'minutes'), + interval: (env.DEPLOYMENT_ENV as DeploymentEnv) === 'canary' ? every(2, 'minutes') : every(30, 'seconds'), alarmEvaluationPeriod: (env.DEPLOYMENT_ENV as DeploymentEnv) === 'canary' ? 3 : 1, alarmDatapointsToAlarm: (env.DEPLOYMENT_ENV as DeploymentEnv) === 'canary' ? 3 : 1, }, { name: 'user-sync-wallet-balances-for-all-pools', - interval: (env.DEPLOYMENT_ENV as DeploymentEnv) === 'canary' ? every(29, 'minutes') : every(9, 'minutes'), + interval: (env.DEPLOYMENT_ENV as DeploymentEnv) === 'canary' ? every(5, 'minutes') : every(20, 'seconds'), + alarmEvaluationPeriod: (env.DEPLOYMENT_ENV as DeploymentEnv) === 'canary' ? 3 : 1, + alarmDatapointsToAlarm: (env.DEPLOYMENT_ENV as DeploymentEnv) === 'canary' ? 3 : 1, }, { name: 'user-sync-staked-balances', - interval: (env.DEPLOYMENT_ENV as DeploymentEnv) === 'canary' ? every(31, 'minutes') : every(11, 'minutes'), + interval: (env.DEPLOYMENT_ENV as DeploymentEnv) === 'canary' ? every(5, 'minutes') : every(20, 'seconds'), + alarmEvaluationPeriod: (env.DEPLOYMENT_ENV as DeploymentEnv) === 'canary' ? 3 : 1, + alarmDatapointsToAlarm: (env.DEPLOYMENT_ENV as DeploymentEnv) === 'canary' ? 3 : 1, }, { name: 'sync-coingecko-coinids', @@ -269,15 +273,15 @@ export const zkevmNetworkConfig: NetworkConfig = { }, { name: 'update-fee-volume-yield-all-pools', - interval: every(75, 'minutes'), + interval: every(1, 'hours'), }, { name: 'sync-vebal-balances', - interval: (env.DEPLOYMENT_ENV as DeploymentEnv) === 'canary' ? every(20, 'minutes') : every(14, 'minutes'), + interval: (env.DEPLOYMENT_ENV as DeploymentEnv) === 'canary' ? every(9, 'minutes') : every(3, 'minutes'), }, { name: 'sync-vebal-totalSupply', - interval: (env.DEPLOYMENT_ENV as DeploymentEnv) === 'canary' ? every(20, 'minutes') : every(16, 'minutes'), + interval: (env.DEPLOYMENT_ENV as DeploymentEnv) === 'canary' ? every(10, 'minutes') : every(5, 'minutes'), }, { name: 'feed-data-to-datastudio', From f035ea4e26b37142258aad47ae68cfdecabed33e Mon Sep 17 00:00:00 2001 From: franzns <93920061+franzns@users.noreply.github.com> Date: Thu, 18 Jan 2024 16:38:43 +0100 Subject: [PATCH 12/15] SOR: compare local results, not query results (#45) * remove log * add option to queryBatchSwap for v2, never queryBatchSwap v1 --- modules/sor/sor.gql | 1 + modules/sor/sor.service.ts | 2 +- .../sor/sorV1Beets/balancer-sor.service.ts | 55 +------------------ modules/sor/sorV1Beets/sorV1Beets.service.ts | 2 +- modules/sor/sorV2/sorV2.service.ts | 5 -- 5 files changed, 5 insertions(+), 60 deletions(-) diff --git a/modules/sor/sor.gql b/modules/sor/sor.gql index 9bee51655..f6b449aa7 100644 --- a/modules/sor/sor.gql +++ b/modules/sor/sor.gql @@ -47,6 +47,7 @@ input GqlSorSwapOptionsInput { timestamp: Int #used for caching purposes maxPools: Int forceRefresh: Boolean #don't use any cached responses + queryBatchSwap: Boolean #run queryBatchSwap to update with onchain values } """ diff --git a/modules/sor/sor.service.ts b/modules/sor/sor.service.ts index f959b58d1..ede53d152 100644 --- a/modules/sor/sor.service.ts +++ b/modules/sor/sor.service.ts @@ -76,7 +76,7 @@ export class SorService { try { // Updates with latest onchain data before returning - return swap.getSorSwapResponse(true); + return swap.getSorSwapResponse(args.swapOptions.queryBatchSwap ? args.swapOptions.queryBatchSwap : false); } catch (err) { console.log(`Error Retrieving QuerySwap`, err); return emptyResponse; diff --git a/modules/sor/sorV1Beets/balancer-sor.service.ts b/modules/sor/sorV1Beets/balancer-sor.service.ts index 19d5c2b3a..7825abfbb 100644 --- a/modules/sor/sorV1Beets/balancer-sor.service.ts +++ b/modules/sor/sorV1Beets/balancer-sor.service.ts @@ -14,9 +14,7 @@ import VaultAbi from '../../pool/abi/Vault.json'; import { env } from '../../../app/env'; import { networkContext } from '../../network/network-context.service'; import { DeploymentEnv } from '../../network/network-config-types'; -import * as Sentry from '@sentry/node'; import _ from 'lodash'; -import { Logger } from '@ethersproject/logger'; import { SwapInfoRoute } from '@balancer-labs/sor'; import { NATIVE_ADDRESS, ZERO_ADDRESS } from '@balancer/sdk'; @@ -60,61 +58,12 @@ export class BalancerSorService { return this.zeroResponse(swapType, tokenIn, tokenOut, swapAmount); } - let deltas: string[] = []; - - try { - deltas = await this.queryBatchSwap( - swapType === 'EXACT_IN' ? SwapTypes.SwapExactIn : SwapTypes.SwapExactOut, - swapInfo.swaps, - swapInfo.tokenAddresses, - ); - } catch (error: any) { - const poolIds = _.uniq(swapInfo.swaps.map((swap) => swap.poolId)); - if (error.code === Logger.errors.CALL_EXCEPTION) { - // Chances are a 304 means that we missed a pool draining event, and the pool data is stale. - // We force an update on any pools inside of the swapInfo - if (error.error?.error?.message?.includes('BAL#304')) { - Sentry.captureException( - `Received a BAL#304 during getSwaps, forcing an on-chain refresh for: ${poolIds.join(',')}`, - { - tags: { - tokenIn, - tokenOut, - swapType, - swapAmount, - swapPools: `${poolIds.join(',')}`, - }, - }, - ); - - const blockNumber = await networkContext.provider.getBlockNumber(); - - poolService.updateOnChainDataForPools(poolIds, blockNumber).catch(); - } else if (error.error?.error?.message?.includes('BAL#')) { - Sentry.captureException( - `Received an unhandled BAL error during getSwaps: ${error.error?.error?.message}`, - { - tags: { - tokenIn, - tokenOut, - swapType, - swapAmount, - swapPools: `${poolIds.join(',')}`, - }, - }, - ); - } - } - - throw new Error(error); - } - const pools = await poolService.getGqlPools({ where: { idIn: swapInfo.routes.map((route) => route.hops.map((hop) => hop.poolId)).flat() }, }); - const tokenInAmount = BigNumber.from(deltas[swapInfo.tokenAddresses.indexOf(tokenIn)]); - const tokenOutAmount = BigNumber.from(deltas[swapInfo.tokenAddresses.indexOf(tokenOut)]).abs(); + const tokenInAmount = swapType === 'EXACT_IN' ? swapAmountScaled : BigNumber.from(swapInfo.returnAmount); + const tokenOutAmount = swapType === 'EXACT_IN' ? BigNumber.from(swapInfo.returnAmount) : swapAmountScaled; return this.formatResponse({ tokenIn: swapInfo.tokenIn, diff --git a/modules/sor/sorV1Beets/sorV1Beets.service.ts b/modules/sor/sorV1Beets/sorV1Beets.service.ts index 3b299996f..30f416fd3 100644 --- a/modules/sor/sorV1Beets/sorV1Beets.service.ts +++ b/modules/sor/sorV1Beets/sorV1Beets.service.ts @@ -29,7 +29,7 @@ class SwapResultV1 implements SwapResult { async getSorSwapResponse(queryFirst: boolean): Promise { if (!this.isValid || this.swap === null) throw new Error('No Response - Invalid Swap'); - // Beets service is already querying onchain + // never query onchain for old v1 service return this.swap; } } diff --git a/modules/sor/sorV2/sorV2.service.ts b/modules/sor/sorV2/sorV2.service.ts index 55672579b..8d3563d49 100644 --- a/modules/sor/sorV2/sorV2.service.ts +++ b/modules/sor/sorV2/sorV2.service.ts @@ -307,11 +307,6 @@ export class SorV2Service implements SwapService { maxNonBoostedPathDepth, }, }; - - console.info( - `SOR: Fetching SORv2 on ${chain} for ${tokenIn} -> ${tokenOut} with maxNonBoostedPathDepth`, - maxNonBoostedPathDepth, - ); const swap = await sorGetSwapsWithPools(tIn, tOut, swapKind, swapAmount, poolsFromDb, config); if (!swap && maxNonBoostedPathDepth < 5) { return this.getSwapResult(arguments[0], maxNonBoostedPathDepth + 1); From 8ab1ebb5c4542e53c791ffcd93e74f1a737603ea Mon Sep 17 00:00:00 2001 From: franz Date: Thu, 18 Jan 2024 17:02:49 +0100 Subject: [PATCH 13/15] add tokens to gqlfxpool --- modules/pool/pool.gql | 1 + 1 file changed, 1 insertion(+) diff --git a/modules/pool/pool.gql b/modules/pool/pool.gql index db7656ce2..de96f2c52 100644 --- a/modules/pool/pool.gql +++ b/modules/pool/pool.gql @@ -304,6 +304,7 @@ type GqlPoolFx implements GqlPoolBase { dynamicData: GqlPoolDynamicData! staking: GqlPoolStaking userBalance: GqlPoolUserBalance + tokens: [GqlPoolTokenUnion!]! } type GqlPoolLiquidityBootstrapping implements GqlPoolBase { From cc6eef8877044e94e783b865554df1db9e33e9b1 Mon Sep 17 00:00:00 2001 From: franzns <93920061+franzns@users.noreply.github.com> Date: Thu, 18 Jan 2024 17:48:10 +0100 Subject: [PATCH 14/15] adding chain param to various token queries (#54) --- modules/token/lib/token-price.service.ts | 11 ++++-- modules/token/token.gql | 17 ++++++-- modules/token/token.resolvers.ts | 50 +++++++++++++++++++----- modules/token/token.service.ts | 15 ++++--- 4 files changed, 71 insertions(+), 22 deletions(-) diff --git a/modules/token/lib/token-price.service.ts b/modules/token/lib/token-price.service.ts index 572b450bb..f32cf7c06 100644 --- a/modules/token/lib/token-price.service.ts +++ b/modules/token/lib/token-price.service.ts @@ -184,11 +184,15 @@ export class TokenPriceService { //await prisma.prismaTokenPrice.deleteMany({ where: { timestamp: { lt: yesterday } } }); } - public async getDataForRange(tokenAddress: string, range: GqlTokenChartDataRange): Promise { + public async getDataForRange( + tokenAddress: string, + range: GqlTokenChartDataRange, + chain: Chain, + ): Promise { const startTimestamp = this.getStartTimestampFromRange(range); return prisma.prismaTokenPrice.findMany({ - where: { tokenAddress, timestamp: { gt: startTimestamp }, chain: networkContext.chain }, + where: { tokenAddress, timestamp: { gt: startTimestamp }, chain: chain }, orderBy: { timestamp: 'asc' }, }); } @@ -197,12 +201,13 @@ export class TokenPriceService { tokenIn: string, tokenOut: string, range: GqlTokenChartDataRange, + chain: Chain, ): Promise { const startTimestamp = this.getStartTimestampFromRange(range); const data = await prisma.prismaTokenPrice.findMany({ where: { - chain: networkContext.chain, + chain: chain, tokenAddress: { in: [tokenIn, tokenOut] }, timestamp: { gt: startTimestamp }, }, diff --git a/modules/token/token.gql b/modules/token/token.gql index 3adbb8db1..4436512be 100644 --- a/modules/token/token.gql +++ b/modules/token/token.gql @@ -3,15 +3,24 @@ extend type Query { tokenGetCurrentPrices(chains: [GqlChain!]): [GqlTokenPrice!]! tokenGetHistoricalPrices(addresses: [String!]!): [GqlHistoricalTokenPrice!]! tokenGetTokensDynamicData(addresses: [String!]!): [GqlTokenDynamicData!]! - tokenGetTokenDynamicData(address: String!): GqlTokenDynamicData + tokenGetTokenDynamicData(address: String!, chain: GqlChain): GqlTokenDynamicData tokenGetRelativePriceChartData( tokenIn: String! tokenOut: String! range: GqlTokenChartDataRange! + chain: GqlChain ): [GqlTokenPriceChartDataItem!]! - tokenGetPriceChartData(address: String!, range: GqlTokenChartDataRange!): [GqlTokenPriceChartDataItem!]! - tokenGetCandlestickChartData(address: String!, range: GqlTokenChartDataRange!): [GqlTokenCandlestickChartDataItem!]! - tokenGetTokenData(address: String!): GqlTokenData + tokenGetPriceChartData( + address: String! + range: GqlTokenChartDataRange! + chain: GqlChain + ): [GqlTokenPriceChartDataItem!]! + tokenGetCandlestickChartData( + address: String! + range: GqlTokenChartDataRange! + chain: GqlChain + ): [GqlTokenCandlestickChartDataItem!]! + tokenGetTokenData(address: String!, chain: GqlChain): GqlTokenData tokenGetTokensData(addresses: [String!]!): [GqlTokenData!]! tokenGetProtocolTokenPrice: AmountHumanReadable! } diff --git a/modules/token/token.resolvers.ts b/modules/token/token.resolvers.ts index 7554d5c9c..475b14bfb 100644 --- a/modules/token/token.resolvers.ts +++ b/modules/token/token.resolvers.ts @@ -44,8 +44,14 @@ const resolvers: Resolvers = { })), })); }, - tokenGetTokenDynamicData: async (parent, { address }, context) => { - const data = await tokenService.getTokenDynamicData(address); + tokenGetTokenDynamicData: async (parent, { address, chain }, context) => { + const currentChain = headerChain(); + if (!chain && currentChain) { + chain = currentChain; + } else if (!chain) { + throw new Error('tokenGetRelativePriceChartData error: Provide "chain" param'); + } + const data = await tokenService.getTokenDynamicData(address, chain); return data ? { @@ -68,8 +74,14 @@ const resolvers: Resolvers = { updatedAt: item.updatedAt.toUTCString(), })); }, - tokenGetPriceChartData: async (parent, { address, range }, context) => { - const data = await tokenService.getDataForRange(address, range); + tokenGetPriceChartData: async (parent, { address, range, chain }, context) => { + const currentChain = headerChain(); + if (!chain && currentChain) { + chain = currentChain; + } else if (!chain) { + throw new Error('tokenGetRelativePriceChartData error: Provide "chain" param'); + } + const data = await tokenService.getDataForRange(address, range, chain); return data.map((item) => ({ id: `${address}-${item.timestamp}`, @@ -77,8 +89,14 @@ const resolvers: Resolvers = { price: `${item.price}`, })); }, - tokenGetRelativePriceChartData: async (parent, { tokenIn, tokenOut, range }, context) => { - const data = await tokenService.getRelativeDataForRange(tokenIn, tokenOut, range); + tokenGetRelativePriceChartData: async (parent, { tokenIn, tokenOut, range, chain }, context) => { + const currentChain = headerChain(); + if (!chain && currentChain) { + chain = currentChain; + } else if (!chain) { + throw new Error('tokenGetRelativePriceChartData error: Provide "chain" param'); + } + const data = await tokenService.getRelativeDataForRange(tokenIn, tokenOut, range, chain); return data.map((item) => ({ id: `${tokenIn}-${tokenOut}-${item.timestamp}`, @@ -86,8 +104,14 @@ const resolvers: Resolvers = { price: `${item.price}`, })); }, - tokenGetCandlestickChartData: async (parent, { address, range }, context) => { - const data = await tokenService.getDataForRange(address, range); + tokenGetCandlestickChartData: async (parent, { address, range, chain }, context) => { + const currentChain = headerChain(); + if (!chain && currentChain) { + chain = currentChain; + } else if (!chain) { + throw new Error('tokenGetCandlestickChartData error: Provide "chain" param'); + } + const data = await tokenService.getDataForRange(address, range, chain); return data.map((item) => ({ id: `${address}-${item.timestamp}`, @@ -98,8 +122,14 @@ const resolvers: Resolvers = { close: `${item.close}`, })); }, - tokenGetTokenData: async (parent, { address }, context) => { - const token = await tokenService.getToken(address); + tokenGetTokenData: async (parent, { address, chain }, context) => { + const currentChain = headerChain(); + if (!chain && currentChain) { + chain = currentChain; + } else if (!chain) { + throw new Error('tokenGetRelativePriceChartData error: Provide "chain" param'); + } + const token = await tokenService.getToken(address, chain); if (token) { return { ...token, diff --git a/modules/token/token.service.ts b/modules/token/token.service.ts index 8b2258e30..5c0d5af3b 100644 --- a/modules/token/token.service.ts +++ b/modules/token/token.service.ts @@ -141,12 +141,12 @@ export class TokenService { await this.coingeckoDataService.syncCoingeckoIds(); } - public async getTokenDynamicData(tokenAddress: string): Promise { + public async getTokenDynamicData(tokenAddress: string, chain: Chain): Promise { const token = await prisma.prismaToken.findUnique({ where: { address_chain: { address: tokenAddress.toLowerCase(), - chain: networkContext.chain, + chain: chain, }, }, include: { @@ -183,16 +183,21 @@ export class TokenService { return dynamicData; } - public async getDataForRange(tokenAddress: string, range: GqlTokenChartDataRange): Promise { - return this.tokenPriceService.getDataForRange(tokenAddress, range); + public async getDataForRange( + tokenAddress: string, + range: GqlTokenChartDataRange, + chain: Chain, + ): Promise { + return this.tokenPriceService.getDataForRange(tokenAddress, range, chain); } public async getRelativeDataForRange( tokenIn: string, tokenOut: string, range: GqlTokenChartDataRange, + chain: Chain, ): Promise { - return this.tokenPriceService.getRelativeDataForRange(tokenIn, tokenOut, range); + return this.tokenPriceService.getRelativeDataForRange(tokenIn, tokenOut, range, chain); } public async initChartData(tokenAddress: string) { From 38646bc5dd93a9053e1223e78d7c26e8ce0769ae Mon Sep 17 00:00:00 2001 From: franzns <93920061+franzns@users.noreply.github.com> Date: Mon, 22 Jan 2024 21:20:24 +0100 Subject: [PATCH 15/15] redo wallet/staked balance query (#53) * replace queries, no join but two queries * adding indexes on user addresses in balance tables * filter non-zero balances manually --------- Co-authored-by: gmbronco <83549293+gmbronco@users.noreply.github.com> --- modules/user/lib/user-balance.service.ts | 71 +++++++++++++------ modules/user/user.prisma | 2 + .../migration.sql | 5 ++ prisma/schema.prisma | 2 + 4 files changed, 58 insertions(+), 22 deletions(-) create mode 100644 prisma/migrations/20240119115442_user_address_indexes/migration.sql diff --git a/modules/user/lib/user-balance.service.ts b/modules/user/lib/user-balance.service.ts index fb98fb1f2..5167d30a0 100644 --- a/modules/user/lib/user-balance.service.ts +++ b/modules/user/lib/user-balance.service.ts @@ -10,30 +10,48 @@ export class UserBalanceService { constructor() {} public async getUserPoolBalances(address: string, chains: Chain[]): Promise { - const user = await prisma.prismaUser.findUnique({ - where: { address: address.toLowerCase() }, - include: { - walletBalances: { - where: { chain: { in: chains }, poolId: { not: null }, balanceNum: { gt: 0 } }, - }, - stakedBalances: { - where: { chain: { in: chains }, poolId: { not: null }, balanceNum: { gt: 0 } }, - }, + // this seems to cause heavy load + // const user = await prisma.prismaUser.findUnique({ + // where: { address: address.toLowerCase() }, + // include: { + // walletBalances: { + // where: { chain: { in: chains }, poolId: { not: null }, balanceNum: { gt: 0 } }, + // }, + // stakedBalances: { + // where: { chain: { in: chains }, poolId: { not: null }, balanceNum: { gt: 0 } }, + // }, + // }, + // }); + + const userWalletBalances = await prisma.prismaUserWalletBalance.findMany({ + where: { + userAddress: address.toLowerCase(), + chain: { in: chains }, + }, + }); + + const userStakedBalances = await prisma.prismaUserStakedBalance.findMany({ + where: { + userAddress: address.toLowerCase(), + chain: { in: chains }, }, }); - if (!user) { + const nonZeroUserWalletBalances = userWalletBalances.filter((balance) => balance.balanceNum > 0); + const nonZeroUserStakedBalances = userStakedBalances.filter((balance) => balance.balanceNum > 0); + + if (nonZeroUserWalletBalances.length === 0 && nonZeroUserStakedBalances.length === 0) { return []; } const poolIds = _.uniq([ - ...user.stakedBalances.map((balance) => balance.poolId), - ...user.walletBalances.map((balance) => balance.poolId), + ...nonZeroUserWalletBalances.map((balance) => balance.poolId), + ...nonZeroUserStakedBalances.map((balance) => balance.poolId), ]) as string[]; return poolIds.map((poolId) => { - const stakedBalance = user.stakedBalances.find((balance) => balance.poolId === poolId); - const walletBalance = user.walletBalances.find((balance) => balance.poolId === poolId); + const walletBalance = nonZeroUserWalletBalances.find((balance) => balance.poolId === poolId); + const stakedBalance = nonZeroUserStakedBalances.find((balance) => balance.poolId === poolId); const stakedNum = parseUnits(stakedBalance?.balance || '0', 18); const walletNum = parseUnits(walletBalance?.balance || '0', 18); @@ -52,16 +70,25 @@ export class UserBalanceService { public async getUserFbeetsBalance(address: string): Promise> { const fbeetsAddress = networkContext.data.fbeets?.address || ''; - const user = await prisma.prismaUser.findUnique({ - where: { address: address.toLowerCase() }, - include: { - walletBalances: { where: { chain: networkContext.chain, tokenAddress: fbeetsAddress } }, - stakedBalances: { where: { chain: networkContext.chain, tokenAddress: fbeetsAddress } }, - }, + const userWalletBalances = await prisma.prismaUserWalletBalance.findMany({ + where: { userAddress: address.toLowerCase(), chain: networkContext.chain, tokenAddress: fbeetsAddress }, + }); + + const userStakedBalances = await prisma.prismaUserStakedBalance.findMany({ + where: { userAddress: address.toLowerCase(), chain: networkContext.chain, tokenAddress: fbeetsAddress }, }); - const stakedBalance = user?.stakedBalances[0]; - const walletBalance = user?.walletBalances[0]; + // this seems to cause heavy load + // const user = await prisma.prismaUser.findUnique({ + // where: { address: address.toLowerCase() }, + // include: { + // walletBalances: { where: { chain: networkContext.chain, tokenAddress: fbeetsAddress } }, + // stakedBalances: { where: { chain: networkContext.chain, tokenAddress: fbeetsAddress } }, + // }, + // }); + + const stakedBalance = userWalletBalances[0]; + const walletBalance = userStakedBalances[0]; const stakedNum = parseUnits(stakedBalance?.balance || '0', 18); const walletNum = parseUnits(walletBalance?.balance || '0', 18); diff --git a/modules/user/user.prisma b/modules/user/user.prisma index 11cdd6c6e..191cafe18 100644 --- a/modules/user/user.prisma +++ b/modules/user/user.prisma @@ -14,6 +14,7 @@ model PrismaUser { model PrismaUserWalletBalance { @@id([id, chain]) + @@index(userAddress) id String chain Chain @@ -35,6 +36,7 @@ model PrismaUserWalletBalance { model PrismaUserStakedBalance { @@id([id, chain]) + @@index(userAddress) id String chain Chain diff --git a/prisma/migrations/20240119115442_user_address_indexes/migration.sql b/prisma/migrations/20240119115442_user_address_indexes/migration.sql new file mode 100644 index 000000000..df19df672 --- /dev/null +++ b/prisma/migrations/20240119115442_user_address_indexes/migration.sql @@ -0,0 +1,5 @@ +-- CreateIndex +CREATE INDEX "PrismaUserStakedBalance_userAddress_idx" ON "PrismaUserStakedBalance"("userAddress"); + +-- CreateIndex +CREATE INDEX "PrismaUserWalletBalance_userAddress_idx" ON "PrismaUserWalletBalance"("userAddress"); diff --git a/prisma/schema.prisma b/prisma/schema.prisma index c3e363422..02648160e 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -776,6 +776,7 @@ model PrismaUser { model PrismaUserWalletBalance { @@id([id, chain]) + @@index(userAddress) id String chain Chain @@ -797,6 +798,7 @@ model PrismaUserWalletBalance { model PrismaUserStakedBalance { @@id([id, chain]) + @@index(userAddress) id String chain Chain