From ff61e998a4749ee0dc8e4927e33ece05dee607be Mon Sep 17 00:00:00 2001 From: gmbronco <83549293+gmbronco@users.noreply.github.com> Date: Thu, 28 Sep 2023 17:40:59 +0200 Subject: [PATCH 1/6] fix: BAL token address on base --- balancer-js/src/lib/constants/addresses.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/balancer-js/src/lib/constants/addresses.json b/balancer-js/src/lib/constants/addresses.json index 2e70ee6fc..e92467009 100644 --- a/balancer-js/src/lib/constants/addresses.json +++ b/balancer-js/src/lib/constants/addresses.json @@ -480,7 +480,7 @@ "authorizerAdaptor": "0x6cad2ea22bfa7f4c14aae92e47f510cd5c509bc7", "authorizerAdaptorEntrypoint": "0x9129e834e15ea19b6069e8f08a8ecfc13686b8dc", "authorizerWithAdaptorValidation": "0xa69e0ccf150a29369d8bbc0b3f510849db7e8eee", - "bal": "0x7c6b91d9be155a6db01f749217d76ff02a7227f2", + "bal": "0x4158734d47fc9692176b5085e0f52ee0da5d47f1", "balancerHelpers": "0x8e9aa87e45e92bad84d5f8dd1bff34fb92637de9", "balancerQueries": "0x300ab2038eac391f26d9f895dc61f8f66a548833", "balancerRelayer": "0x76f7204b62f554b79d444588edac9dfa7032c71a", From a07aabef66db237858b5de267d6a41817da16472 Mon Sep 17 00:00:00 2001 From: gmbronco <83549293+gmbronco@users.noreply.github.com> Date: Thu, 28 Sep 2023 17:45:16 +0200 Subject: [PATCH 2/6] fix: base in coingecko --- balancer-js/src/modules/data/token-prices/coingecko.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/balancer-js/src/modules/data/token-prices/coingecko.ts b/balancer-js/src/modules/data/token-prices/coingecko.ts index e3bf971e7..1f4792698 100644 --- a/balancer-js/src/modules/data/token-prices/coingecko.ts +++ b/balancer-js/src/modules/data/token-prices/coingecko.ts @@ -141,6 +141,8 @@ export class CoingeckoPriceRepository implements Findable { return 'fantom'; case 1101: return 'polygon-zkevm'; + case 8453: + return 'base'; case 42161: return 'arbitrum-one'; case 43114: From a727d662e133ced428b757ba7ddb25c6f0ac8006 Mon Sep 17 00:00:00 2001 From: gmbronco <83549293+gmbronco@users.noreply.github.com> Date: Thu, 28 Sep 2023 17:41:10 +0200 Subject: [PATCH 3/6] new: base APRs example --- balancer-js/examples/pools/aprs/aprs.base.ts | 44 ++++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 balancer-js/examples/pools/aprs/aprs.base.ts diff --git a/balancer-js/examples/pools/aprs/aprs.base.ts b/balancer-js/examples/pools/aprs/aprs.base.ts new file mode 100644 index 000000000..ef8b46e8b --- /dev/null +++ b/balancer-js/examples/pools/aprs/aprs.base.ts @@ -0,0 +1,44 @@ +/** + * Display APRs for pool ids hardcoded under `const ids` + * + * Run command + * yarn example ./examples/pools/aprs/aprs.arbitrum.ts + */ +import { BalancerSDK, Pool } from '@balancer-labs/sdk'; + +const sdk = new BalancerSDK({ + network: 8453, + rpcUrl: 'https://rpc.ankr.com/base', +}); + +const { pools } = sdk; + +const main = async () => { + const id = + '0xfb4c2e6e6e27b5b4a07a36360c89ede29bb3c9b6000000000000000000000026'; + const pool = (await pools.find(id)) as Pool; + const apr = await pools.apr(pool); + console.log(pool.id, apr); + + // const list = ( + // await pools.where( + // (pool) => + // pool.poolType != 'Element' && + // pool.poolType != 'AaveLinear' && + // pool.poolType != 'LiquidityBootstrapping' + // ) + // ) + // .sort((a, b) => parseFloat(b.totalLiquidity) - parseFloat(a.totalLiquidity)) + // .slice(0, 30) + + // list.forEach(async (pool) => { + // try { + // const apr = await pools.apr(pool) + // console.log(pool.id, apr) + // } catch (e) { + // console.log(e) + // } + // }); +}; + +main(); From f0356e3b224fa04d52c1711486b72640fdb6381d Mon Sep 17 00:00:00 2001 From: gmbronco <83549293+gmbronco@users.noreply.github.com> Date: Thu, 28 Sep 2023 17:45:02 +0200 Subject: [PATCH 4/6] fix: onchain data defaults --- .../src/modules/data/pool/onchain-data.ts | 44 ++++++++++--------- 1 file changed, 23 insertions(+), 21 deletions(-) diff --git a/balancer-js/src/modules/data/pool/onchain-data.ts b/balancer-js/src/modules/data/pool/onchain-data.ts index cd168be7c..07b27f8b8 100644 --- a/balancer-js/src/modules/data/pool/onchain-data.ts +++ b/balancer-js/src/modules/data/pool/onchain-data.ts @@ -135,30 +135,32 @@ const poolTypeCalls = (poolType: string, poolTypeVersion = 1) => { const merge = (pool: SubgraphPoolBase, result: OnchainData) => ({ ...pool, - tokens: pool.tokens.map((token) => { - const idx = result.poolTokens[0] - .map((t) => t.toLowerCase()) - .indexOf(token.address); - const wrappedToken = - pool.wrappedIndex && pool.tokensList[pool.wrappedIndex]; - return { - ...token, - balance: formatFixed(result.poolTokens[1][idx], token.decimals || 18), - weight: - (result.weights && formatFixed(result.weights[idx], 18)) || - token.weight, - priceRate: - (result.wrappedTokenRate && - wrappedToken && - wrappedToken.toLowerCase() === token.address.toLowerCase() && - formatFixed(result.wrappedTokenRate, 18)) || - token.priceRate, - } as SubgraphToken; - }), + tokens: result.poolTokens + ? pool.tokens.map((token) => { + const idx = result.poolTokens[0] + .map((t) => t.toLowerCase()) + .indexOf(token.address); + const wrappedToken = + pool.wrappedIndex && pool.tokensList[pool.wrappedIndex]; + return { + ...token, + balance: formatFixed(result.poolTokens[1][idx], token.decimals || 18), + weight: + (result.weights && formatFixed(result.weights[idx], 18)) || + token.weight, + priceRate: + (result.wrappedTokenRate && + wrappedToken && + wrappedToken.toLowerCase() === token.address.toLowerCase() && + formatFixed(result.wrappedTokenRate, 18)) || + token.priceRate, + } as SubgraphToken; + }) + : pool.tokens, totalShares: result.totalShares ? formatFixed(result.totalShares, 18) : pool.totalShares, - swapFee: formatFixed(result.swapFee, 18), + swapFee: result.swapFee ? formatFixed(result.swapFee, 18) : pool.swapFee, amp: (result.amp && result.amp[0] && From 5c3a1176aff7751a91c274543578ec3ffc886471 Mon Sep 17 00:00:00 2001 From: gmbronco <83549293+gmbronco@users.noreply.github.com> Date: Fri, 29 Sep 2023 18:11:05 +0200 Subject: [PATCH 5/6] fix: recovery spec --- .../recovery.integration.spec.ts | 398 +++--------------- 1 file changed, 52 insertions(+), 346 deletions(-) diff --git a/balancer-js/src/modules/pools/pool-types/concerns/composableStable/recovery.integration.spec.ts b/balancer-js/src/modules/pools/pool-types/concerns/composableStable/recovery.integration.spec.ts index 6f41625a7..ae53b7f7f 100644 --- a/balancer-js/src/modules/pools/pool-types/concerns/composableStable/recovery.integration.spec.ts +++ b/balancer-js/src/modules/pools/pool-types/concerns/composableStable/recovery.integration.spec.ts @@ -1,370 +1,76 @@ // yarn test:only ./src/modules/pools/pool-types/concerns/composableStable/recovery.integration.spec.ts -import dotenv from 'dotenv'; import { ethers } from 'hardhat'; import { parseFixed } from '@ethersproject/bignumber'; -import { - BalancerSDK, - getPoolAddress, - Network, - GraphQLArgs, - GraphQLQuery, -} from '@/.'; +import { BalancerSDK, PoolWithMethods, getPoolAddress } from '@/.'; import { forkSetup } from '@/test/lib/utils'; import { assertRecoveryExit } from '@/test/lib/exitHelper'; -dotenv.config(); - -const network = Network.POLYGON; -const { ALCHEMY_URL_POLYGON: jsonRpcUrl } = process.env; +const network = 137; // POLYGON; +const jsonRpcUrl = 'https://rpc.ankr.com/polygon'; const rpcUrl = 'http://127.0.0.1:8137'; const provider = new ethers.providers.JsonRpcProvider(rpcUrl, network); const signer = provider.getSigner(); -const blockNumber = 47427007; + let balancer: BalancerSDK; -describe('ComposableStable - recovery', () => { - context('V1', async () => { - const poolId = - '0x02d2e2d7a89d6c5cb3681cfcb6f7dac02a55eda400000000000000000000088f'; - // We have to reset the fork between each test as pool value changes after tx is submitted - beforeEach(async () => { - // Setup forked network, set initial token balances and allowances - await forkSetup( - signer, - [getPoolAddress(poolId)], - [0], - [parseFixed('10000', 18).toString()], - jsonRpcUrl as string, - blockNumber - ); - const subgraphArgs: GraphQLArgs = { - where: { - id: { - in: [poolId], - }, - }, - block: { number: blockNumber }, - }; +const poolIds = [ + '0x02d2e2d7a89d6c5cb3681cfcb6f7dac02a55eda400000000000000000000088f', // V1 + '0xe2dc0e0f2c358d6e31836dee69a558ab8d1390e70000000000000000000009fa', // V2 + '0x10b040038f87219d9b42e025e3bd9b8095c87dd9000000000000000000000b11', // V3 + '0xa2ccad543fbe9332b87910beabd941b86dd5f762000000000000000000000b5c', // V4 +]; - const subgraphQuery: GraphQLQuery = { args: subgraphArgs, attrs: {} }; - balancer = new BalancerSDK({ - network, - rpcUrl, - subgraphQuery, - }); - }); +describe('ComposableStable - recovery', () => { + before(async () => { + await forkSetup( + signer, + poolIds.map((id) => getPoolAddress(id)), + Array(poolIds.length).fill(0), + Array(poolIds.length).fill(parseFixed('10000', 18).toString()), + jsonRpcUrl as string + ); - context('buildRecoveryExit', async () => { - context('PoolWithMethods', async () => { - it('should recovery exit', async () => { - const bptAmount = parseFixed('1.34', 18).toString(); - const slippage = '10'; // 10 bps = 0.1% - const pool = await balancer.pools.find(poolId); - if (!pool) throw Error('Pool not found'); - const signerAddr = await signer.getAddress(); - const { to, data, minAmountsOut, expectedAmountsOut, priceImpact } = - pool.buildRecoveryExit(signerAddr, bptAmount, slippage); - await assertRecoveryExit( - signerAddr, - slippage, - to, - data, - minAmountsOut, - expectedAmountsOut, - priceImpact, - pool, - signer, - bptAmount - ); - }); - }); - context('Pool & refresh', async () => { - it('should recovery exit', async () => { - const bptAmount = parseFixed('1.34', 18).toString(); - const slippage = '10'; // 10 bps = 0.1% - let pool = await balancer.data.pools.find(poolId); - if (!pool) throw Error('Pool not found'); - const signerAddr = await signer.getAddress(); - pool = await balancer.data.poolsOnChain.refresh(pool); - const { to, data, expectedAmountsOut, minAmountsOut, priceImpact } = - balancer.pools.buildRecoveryExit({ - pool, - bptAmount, - userAddress: signerAddr, - slippage, - }); - await assertRecoveryExit( - signerAddr, - slippage, - to, - data, - minAmountsOut, - expectedAmountsOut, - priceImpact, - pool, - signer, - bptAmount - ); - }); - }); - }); - }); - context('V2', async () => { - const poolId = - '0xe2dc0e0f2c358d6e31836dee69a558ab8d1390e70000000000000000000009fa'; - // We have to reset the fork between each test as pool value changes after tx is submitted - beforeEach(async () => { - // Setup forked network, set initial token balances and allowances - await forkSetup( - signer, - [getPoolAddress(poolId)], - [0], - [parseFixed('10000', 18).toString()], - jsonRpcUrl as string, - blockNumber - ); - const subgraphArgs: GraphQLArgs = { - where: { - id: { - in: [poolId], - }, + const subgraphArgs = { + where: { + id: { + in: poolIds, }, - block: { number: blockNumber }, - }; + }, + }; - const subgraphQuery: GraphQLQuery = { args: subgraphArgs, attrs: {} }; - balancer = new BalancerSDK({ - network, - rpcUrl, - subgraphQuery, - }); - }); + const subgraphQuery = { args: subgraphArgs, attrs: {} }; - context('buildRecoveryExit', async () => { - context('PoolWithMethods', async () => { - it('should recovery exit', async () => { - const bptAmount = parseFixed('1.34', 18).toString(); - const slippage = '10'; // 10 bps = 0.1% - const pool = await balancer.pools.find(poolId); - if (!pool) throw Error('Pool not found'); - const signerAddr = await signer.getAddress(); - const { to, data, minAmountsOut, expectedAmountsOut, priceImpact } = - pool.buildRecoveryExit(signerAddr, bptAmount, slippage); - await assertRecoveryExit( - signerAddr, - slippage, - to, - data, - minAmountsOut, - expectedAmountsOut, - priceImpact, - pool, - signer, - bptAmount - ); - }); - }); - context('Pool & refresh', async () => { - it('should recovery exit', async () => { - const bptAmount = parseFixed('1.34', 18).toString(); - const slippage = '10'; // 10 bps = 0.1% - let pool = await balancer.data.pools.find(poolId); - if (!pool) throw Error('Pool not found'); - const signerAddr = await signer.getAddress(); - pool = await balancer.data.poolsOnChain.refresh(pool); - const { to, data, expectedAmountsOut, minAmountsOut, priceImpact } = - balancer.pools.buildRecoveryExit({ - pool, - bptAmount, - userAddress: signerAddr, - slippage, - }); - await assertRecoveryExit( - signerAddr, - slippage, - to, - data, - minAmountsOut, - expectedAmountsOut, - priceImpact, - pool, - signer, - bptAmount - ); - }); - }); + balancer = new BalancerSDK({ + network, + rpcUrl, + subgraphQuery, }); }); - context('V3', async () => { - const poolId = - '0x10b040038f87219d9b42e025e3bd9b8095c87dd9000000000000000000000b11'; - // We have to reset the fork between each test as pool value changes after tx is submitted - beforeEach(async () => { - // Setup forked network, set initial token balances and allowances - await forkSetup( - signer, - [getPoolAddress(poolId)], - [0], - [parseFixed('10000', 18).toString()], - jsonRpcUrl as string, - blockNumber - ); - const subgraphArgs: GraphQLArgs = { - where: { - id: { - in: [poolId], - }, - }, - block: { number: blockNumber }, - }; - const subgraphQuery: GraphQLQuery = { args: subgraphArgs, attrs: {} }; - balancer = new BalancerSDK({ - network, - rpcUrl, - subgraphQuery, - }); - }); - - context('buildRecoveryExit', async () => { - context('PoolWithMethods', async () => { - it('should recovery exit', async () => { - const bptAmount = parseFixed('0.001', 18).toString(); - const slippage = '10'; // 10 bps = 0.1% - const pool = await balancer.pools.find(poolId); - if (!pool) throw Error('Pool not found'); - const signerAddr = await signer.getAddress(); - const { to, data, minAmountsOut, expectedAmountsOut, priceImpact } = - pool.buildRecoveryExit(signerAddr, bptAmount, slippage); - await assertRecoveryExit( - signerAddr, - slippage, - to, - data, - minAmountsOut, - expectedAmountsOut, - priceImpact, - pool, - signer, - bptAmount - ); - }); - }); - context('Pool & refresh', async () => { - it('should recovery exit', async () => { - const bptAmount = parseFixed('0.00001', 18).toString(); - const slippage = '10'; // 10 bps = 0.1% - let pool = await balancer.data.pools.find(poolId); - if (!pool) throw Error('Pool not found'); - const signerAddr = await signer.getAddress(); - pool = await balancer.data.poolsOnChain.refresh(pool); - const { to, data, expectedAmountsOut, minAmountsOut, priceImpact } = - balancer.pools.buildRecoveryExit({ - pool, - bptAmount, - userAddress: signerAddr, - slippage, - }); - await assertRecoveryExit( - signerAddr, - slippage, - to, - data, - minAmountsOut, - expectedAmountsOut, - priceImpact, - pool, - signer, - bptAmount - ); - }); - }); - }); - }); - context('V4', async () => { - const poolId = - '0xa2ccad543fbe9332b87910beabd941b86dd5f762000000000000000000000b5c'; - // We have to reset the fork between each test as pool value changes after tx is submitted - beforeEach(async () => { - // Setup forked network, set initial token balances and allowances - await forkSetup( - signer, - [getPoolAddress(poolId)], - [0], - [parseFixed('10000', 18).toString()], - jsonRpcUrl as string, - blockNumber - ); - const subgraphArgs: GraphQLArgs = { - where: { - id: { - in: [poolId], - }, - }, - block: { number: blockNumber }, - }; + context('Recovery exit', async () => { + for (const [i, poolId] of poolIds.entries()) { + it(`works for V${i + 1}`, async () => { + const pool = (await balancer.pools.find(poolId)) as PoolWithMethods; + const bptAmount = parseFixed('0.00001', 18).toString(); + const slippage = '10'; // 10 bps = 0.1% + const signerAddr = await signer.getAddress(); - const subgraphQuery: GraphQLQuery = { args: subgraphArgs, attrs: {} }; - balancer = new BalancerSDK({ - network, - rpcUrl, - subgraphQuery, - }); - }); + const { to, data, minAmountsOut, expectedAmountsOut, priceImpact } = + pool.buildRecoveryExit(signerAddr, bptAmount, slippage); - context('buildRecoveryExit', async () => { - context('PoolWithMethods', async () => { - it('should recovery exit', async () => { - const bptAmount = parseFixed('0.34', 18).toString(); - const slippage = '10'; // 10 bps = 0.1% - const pool = await balancer.pools.find(poolId); - if (!pool) throw Error('Pool not found'); - const signerAddr = await signer.getAddress(); - const { to, data, minAmountsOut, expectedAmountsOut, priceImpact } = - pool.buildRecoveryExit(signerAddr, bptAmount, slippage); - await assertRecoveryExit( - signerAddr, - slippage, - to, - data, - minAmountsOut, - expectedAmountsOut, - priceImpact, - pool, - signer, - bptAmount - ); - }); - }); - context('Pool & refresh', async () => { - it('should recovery exit', async () => { - const bptAmount = parseFixed('0.34', 18).toString(); - const slippage = '10'; // 10 bps = 0.1% - let pool = await balancer.data.pools.find(poolId); - if (!pool) throw Error('Pool not found'); - const signerAddr = await signer.getAddress(); - pool = await balancer.data.poolsOnChain.refresh(pool); - const { to, data, expectedAmountsOut, minAmountsOut, priceImpact } = - balancer.pools.buildRecoveryExit({ - pool, - bptAmount, - userAddress: signerAddr, - slippage, - }); - await assertRecoveryExit( - signerAddr, - slippage, - to, - data, - minAmountsOut, - expectedAmountsOut, - priceImpact, - pool, - signer, - bptAmount - ); - }); + await assertRecoveryExit( + signerAddr, + slippage, + to, + data, + minAmountsOut, + expectedAmountsOut, + priceImpact, + pool, + signer, + bptAmount + ); }); - }); + } }); }); From af29efc3065cdde50edeffa60077903541126b05 Mon Sep 17 00:00:00 2001 From: gmbronco <83549293+gmbronco@users.noreply.github.com> Date: Thu, 5 Oct 2023 07:50:12 +0200 Subject: [PATCH 6/6] fix: staking boost on L2s --- .../src/modules/data/liquidity-gauges/provider.ts | 5 +++++ balancer-js/src/modules/pools/apr/apr.ts | 9 ++++----- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/balancer-js/src/modules/data/liquidity-gauges/provider.ts b/balancer-js/src/modules/data/liquidity-gauges/provider.ts index 3ef53e75b..2c589506a 100644 --- a/balancer-js/src/modules/data/liquidity-gauges/provider.ts +++ b/balancer-js/src/modules/data/liquidity-gauges/provider.ts @@ -86,6 +86,11 @@ export class LiquidityGaugeSubgraphRPCProvider childGaugeAddresses ); console.timeEnd('Fetching multicall.inflationRates'); + console.time('Fetching multicall.getWorkingSupplies'); + this.workingSupplies = await this.multicall.getWorkingSupplies( + childGaugeAddresses + ); + console.timeEnd('Fetching multicall.getWorkingSupplies'); } } if (this.gaugeController) { diff --git a/balancer-js/src/modules/pools/apr/apr.ts b/balancer-js/src/modules/pools/apr/apr.ts index 679dac80d..54d76c8ef 100644 --- a/balancer-js/src/modules/pools/apr/apr.ts +++ b/balancer-js/src/modules/pools/apr/apr.ts @@ -276,14 +276,15 @@ export class PoolApr { throw 'Missing BAL price'; } + const gaugeSupply = (gauge.workingSupply + 0.4) / 0.4; // Only 40% of LP token staked accrue emissions, totalSupply = workingSupply * 2.5 + const gaugeSupplyUsd = gaugeSupply * bptPriceUsd; + // Handle child chain gauges with inflation_rate // balInflationRate - amount of BAL tokens per second as a float if (gauge.balInflationRate) { const reward = gauge.balInflationRate * 86400 * 365 * parseFloat(balPrice.usd); - const totalSupplyUsd = gauge.totalSupply * bptPriceUsd; - const rewardValue = reward / totalSupplyUsd; - return Math.round(boost * 10000 * rewardValue); + return Math.round((boost * 10000 * reward) / gaugeSupplyUsd); } else if (pool.chainId > 1) { // TODO: remove after all gauges are migrated (around 01-07-2023), Subgraph is returning BAL staking rewards as reward tokens for L2 gauges. if (!gauge.rewardTokens) { @@ -307,8 +308,6 @@ export class PoolApr { const totalBalEmissions = (emissions.weekly(now) / 7) * 365; const gaugeBalEmissions = totalBalEmissions * gauge.relativeWeight; const gaugeBalEmissionsUsd = gaugeBalEmissions * balPriceUsd; - const gaugeSupply = (gauge.workingSupply + 0.4) / 0.4; // Only 40% of LP token staked accrue emissions, totalSupply = workingSupply * 2.5 - const gaugeSupplyUsd = gaugeSupply * bptPriceUsd; const gaugeBalAprBps = Math.round( (boost * 10000 * gaugeBalEmissionsUsd) / gaugeSupplyUsd );