From e81774efe2fb43c8dac4a02bd1c9651d6e17068f Mon Sep 17 00:00:00 2001 From: gmbronco <83549293+gmbronco@users.noreply.github.com> Date: Wed, 11 Dec 2024 18:49:41 +0100 Subject: [PATCH 1/3] fix: syncing changed pools on V3 (#1267) --- .changeset/giant-trains-march.md | 5 +++++ modules/controllers/pool-controller.ts | 16 ++++++++++++---- 2 files changed, 17 insertions(+), 4 deletions(-) create mode 100644 .changeset/giant-trains-march.md diff --git a/.changeset/giant-trains-march.md b/.changeset/giant-trains-march.md new file mode 100644 index 00000000..4ca3590b --- /dev/null +++ b/.changeset/giant-trains-march.md @@ -0,0 +1,5 @@ +--- +'backend': patch +--- + +fix syncing v3 changed pools diff --git a/modules/controllers/pool-controller.ts b/modules/controllers/pool-controller.ts index 611b200c..fe207615 100644 --- a/modules/controllers/pool-controller.ts +++ b/modules/controllers/pool-controller.ts @@ -231,17 +231,19 @@ export function PoolController(tracer?: any) { */ async syncChangedPoolsV3(chain: Chain) { const { + subgraphs: { balancerV3 }, balancer: { v3: { vaultAddress, routerAddress }, }, } = config[chain]; // Guard against unconfigured chains - if (!vaultAddress) { + if (!vaultAddress || !balancerV3) { throw new Error(`Chain not configured: ${chain}`); } const viemClient = getViemClient(chain); + const subgraphClient = getVaultSubgraphClient(balancerV3); const lastSyncBlock = await getLastSyncedBlock(chain, PrismaLastBlockSyncedCategory.POOLS_V3); const fromBlock = lastSyncBlock + 1; @@ -259,9 +261,14 @@ export function PoolController(tracer?: any) { where: { chain, protocolVersion: 3 }, }); - const changedPools = await getChangedPoolsV3(vaultAddress, viemClient, BigInt(fromBlock), BigInt(toBlock)); + // RPC for some reason isn't working, maybe event signatures are wrong? + // const changedPools = await getChangedPoolsV3(vaultAddress, viemClient, BigInt(fromBlock), BigInt(toBlock)); + const changedPoolsIds = await subgraphClient + .getAllInitializedPools({ + _change_block: { number_gte: fromBlock }, + }) + .then((pools) => pools.map((pool) => pool.id.toLowerCase())); - const changedPoolsIds = changedPools.map((id) => id.toLowerCase()); const poolsToSync = pools.filter((pool) => changedPoolsIds.includes(pool.id.toLowerCase())); // only sync pools that are in the database if (poolsToSync.length === 0) { return []; @@ -274,7 +281,8 @@ export function PoolController(tracer?: any) { chain, ); - await upsertLastSyncedBlock(chain, PrismaLastBlockSyncedCategory.POOLS_V3, toBlock); + // Leaving safety margin for reorgs + await upsertLastSyncedBlock(chain, PrismaLastBlockSyncedCategory.POOLS_V3, toBlock - 10n); return poolsToSync.map(({ id }) => id); }, From 8768e95fcb074153ad96de4243295b3b0294daf8 Mon Sep 17 00:00:00 2001 From: gmbronco <83549293+gmbronco@users.noreply.github.com> Date: Wed, 11 Dec 2024 20:57:52 +0100 Subject: [PATCH 2/3] handle custom morpho rewards (#1270) --- .changeset/large-mice-trade.md | 5 ++ .../sources/morpho-apr-handler.ts | 6 +- .../apr-data-sources/yb-tokens-apr.service.ts | 65 ++++++++++++++----- 3 files changed, 59 insertions(+), 17 deletions(-) create mode 100644 .changeset/large-mice-trade.md diff --git a/.changeset/large-mice-trade.md b/.changeset/large-mice-trade.md new file mode 100644 index 00000000..93edd1bc --- /dev/null +++ b/.changeset/large-mice-trade.md @@ -0,0 +1,5 @@ +--- +'backend': patch +--- + +handle custom morpho rewards diff --git a/modules/pool/lib/apr-data-sources/yb-apr-handlers/sources/morpho-apr-handler.ts b/modules/pool/lib/apr-data-sources/yb-apr-handlers/sources/morpho-apr-handler.ts index fe627ee9..dabcd758 100644 --- a/modules/pool/lib/apr-data-sources/yb-apr-handlers/sources/morpho-apr-handler.ts +++ b/modules/pool/lib/apr-data-sources/yb-apr-handlers/sources/morpho-apr-handler.ts @@ -12,6 +12,8 @@ const query = gql` network } state { + apy + fee netApy } } @@ -25,6 +27,8 @@ type Vault = { network: 'ethereum' | 'base'; }; state: { + apy: number; + fee: number; netApy: number; }; }; @@ -56,7 +60,7 @@ export class MorphoAprHandler implements AprHandler { .map((vault) => [ vault.address.toLowerCase(), { - apr: vault.state.netApy, + apr: vault.state.apy * (1 - vault.state.fee), group: this.group, isIbYield: true, }, diff --git a/modules/pool/lib/apr-data-sources/yb-tokens-apr.service.ts b/modules/pool/lib/apr-data-sources/yb-tokens-apr.service.ts index 80b510b4..0a41a461 100644 --- a/modules/pool/lib/apr-data-sources/yb-tokens-apr.service.ts +++ b/modules/pool/lib/apr-data-sources/yb-tokens-apr.service.ts @@ -8,6 +8,9 @@ import { tokenService } from '../../../token/token.service'; import { collectsYieldFee, tokenCollectsYieldFee } from '../pool-utils'; import { YbAprConfig } from '../../../network/apr-config-types'; +const MORPHO_TOKEN = '0x58d97b57bb95320f9a05dc918aef65434969c2b2'; +const MORPHO_RATE = 0.07605350252138458; + export class YbTokensAprService implements PoolAprService { private ybTokensAprHandlers: YbAprHandlers; private underlyingMap: { [wrapper: string]: string } = {}; @@ -31,7 +34,14 @@ export class YbTokensAprService implements PoolAprService { public async updateAprForPools(pools: PrismaPoolWithTokens[]): Promise { const operations: any[] = []; - const tokenPrices = await tokenService.getTokenPrices(pools[0].chain); + const chains = Array.from(new Set(pools.map((pool) => pool.chain))); + const tokenPrices = await tokenService.getCurrentTokenPrices(chains).then((prices) => + Object.fromEntries( + prices.map((price) => { + return [price.tokenAddress, price.price]; + }), + ), + ); const aprs = await this.fetchYieldTokensApr(); const poolsWithYbTokens = pools.filter((pool) => { return pool.tokens.find((token) => { @@ -63,35 +73,34 @@ export class YbTokensAprService implements PoolAprService { continue; } - for (const token of pool.tokens) { + const tokenAprs = pool.tokens.map((token) => { const tokenApr = aprs.get(token.address); - if (!tokenApr) { - continue; - } - - const tokenPrice = tokenService.getPriceForToken(tokenPrices, token.address, pool.chain); - const tokenBalance = token.balance; - - const tokenLiquidity = tokenPrice * parseFloat(tokenBalance || '0'); - const tokenPercentageInPool = tokenLiquidity / totalLiquidity; + return { + ...token, + ...tokenApr, + share: (parseFloat(token.balance) * tokenPrices[token.address]) / totalLiquidity, + }; + }); - if (!tokenApr || !tokenPercentageInPool) { + for (const token of tokenAprs) { + if (!token.apr || !token.share) { continue; } - let userApr = tokenApr.apr * tokenPercentageInPool; + let userApr = token.apr * token.share; // AAVE + LST case, we need to apply the underlying token APR on top of the AAVE market APR const aaveUnderlying = this.underlyingMap[token.address]; if (aaveUnderlying) { const underlyingTokenApr = aprs.get(aaveUnderlying); if (underlyingTokenApr) { - userApr = ((1 + tokenApr.apr) * (1 + underlyingTokenApr.apr) - 1) * tokenPercentageInPool; + userApr = ((1 + token.apr) * (1 + underlyingTokenApr.apr) - 1) * token.share; } } + let fee = 0; if (collectsYieldFee(pool) && tokenCollectsYieldFee(token) && pool.dynamicData) { - const fee = + fee = pool.type === 'META_STABLE' ? parseFloat(pool.dynamicData.protocolSwapFee || '0') : pool.protocolVersion === 3 @@ -111,12 +120,36 @@ export class YbTokensAprService implements PoolAprService { poolId: pool.id, title: `${token.token.symbol} APR`, apr: userApr, - group: tokenApr.group as PrismaPoolAprItemGroup, + group: token.group as PrismaPoolAprItemGroup, type: yieldType, rewardTokenAddress: token.address, rewardTokenSymbol: token.token.symbol, }; + // Custom APR for MORPHO vaults, hardcoding, because it's complicated to get it from the API + if (data.group === PrismaPoolAprItemGroup.MORPHO) { + const morphoTokensShare = tokenAprs + .filter((t) => t.group === 'MORPHO') + .reduce((acc, t) => acc + t.share, 0); + const morphoId = `${pool.id}-morpho`; + const morphoData = { + ...data, + id: morphoId, + apr: MORPHO_RATE * morphoTokensShare, + type: PrismaPoolAprType.IB_YIELD, + title: 'MORPHO APR', + rewardTokenAddress: MORPHO_TOKEN, + rewardTokenSymbol: 'MORPHO', + }; + operations.push( + prisma.prismaPoolAprItem.upsert({ + where: { id_chain: { id: morphoId, chain: pool.chain } }, + create: morphoData, + update: morphoData, + }), + ); + } + operations.push( prisma.prismaPoolAprItem.upsert({ where: { id_chain: { id: itemId, chain: pool.chain } }, From 4de64f4200358e7847fe4dfe5c7b8834fef0c9d9 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 11 Dec 2024 21:05:37 +0100 Subject: [PATCH 3/3] Version Packages (#1268) Co-authored-by: github-actions[bot] --- .changeset/giant-trains-march.md | 5 ----- .changeset/large-mice-trade.md | 5 ----- CHANGELOG.md | 7 +++++++ package.json | 2 +- 4 files changed, 8 insertions(+), 11 deletions(-) delete mode 100644 .changeset/giant-trains-march.md delete mode 100644 .changeset/large-mice-trade.md diff --git a/.changeset/giant-trains-march.md b/.changeset/giant-trains-march.md deleted file mode 100644 index 4ca3590b..00000000 --- a/.changeset/giant-trains-march.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'backend': patch ---- - -fix syncing v3 changed pools diff --git a/.changeset/large-mice-trade.md b/.changeset/large-mice-trade.md deleted file mode 100644 index 93edd1bc..00000000 --- a/.changeset/large-mice-trade.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'backend': patch ---- - -handle custom morpho rewards diff --git a/CHANGELOG.md b/CHANGELOG.md index 3a2afece..db0a007a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # backend +## 1.26.5 + +### Patch Changes + +- e81774e: fix syncing v3 changed pools +- 8768e95: handle custom morpho rewards + ## 1.26.4 ### Patch Changes diff --git a/package.json b/package.json index f7c41560..6918c2f7 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "backend", - "version": "1.26.4", + "version": "1.26.5", "description": "Backend service for Beethoven X and Balancer", "repository": "https://github.com/balancer/backend", "author": "Beethoven X",