From 234ebf6741a71fe08b63e0c84838a74eaf1fcacb Mon Sep 17 00:00:00 2001 From: johngrantuk <johngrantuk@googlemail.com> Date: Thu, 8 Dec 2022 11:36:04 +0000 Subject: [PATCH 01/25] Added debug. --- balancer-js/src/modules/data/pool/subgraph.ts | 3 +- .../joins/debug.module.integration.spec.ts | 242 ++++++++++++++++++ balancer-js/src/modules/joins/joins.module.ts | 9 + 3 files changed, 253 insertions(+), 1 deletion(-) create mode 100644 balancer-js/src/modules/joins/debug.module.integration.spec.ts diff --git a/balancer-js/src/modules/data/pool/subgraph.ts b/balancer-js/src/modules/data/pool/subgraph.ts index d54613f00..5fb495660 100644 --- a/balancer-js/src/modules/data/pool/subgraph.ts +++ b/balancer-js/src/modules/data/pool/subgraph.ts @@ -107,7 +107,8 @@ export class PoolsSubgraphRepository where: { swapEnabled: true, totalShares_gt: '0.000000000001' }, orderBy: Pool_OrderBy.TotalLiquidity, orderDirection: OrderDirection.Desc, - block: await this.block(), + // block: await this.block(), + block: { number: 16067738 }, }); console.timeEnd('fetching pools'); diff --git a/balancer-js/src/modules/joins/debug.module.integration.spec.ts b/balancer-js/src/modules/joins/debug.module.integration.spec.ts new file mode 100644 index 000000000..c54471efc --- /dev/null +++ b/balancer-js/src/modules/joins/debug.module.integration.spec.ts @@ -0,0 +1,242 @@ +// yarn test:only ./src/modules/joins/debug.module.integration.spec.ts +import dotenv from 'dotenv'; +import { expect } from 'chai'; +import hardhat from 'hardhat'; + +import { BalancerSDK, BalancerTenderlyConfig, Network } from '@/.'; +import { BigNumber, formatFixed, parseFixed } from '@ethersproject/bignumber'; +import { Contracts } from '@/modules/contracts/contracts.module'; +import { forkSetup, getBalances } from '@/test/lib/utils'; +import { ADDRESSES } from '@/test/lib/constants'; +import { Relayer } from '@/modules/relayer/relayer.module'; +import { JsonRpcSigner } from '@ethersproject/providers'; +import { SolidityMaths } from '@/lib/utils/solidityMaths'; + +dotenv.config(); + +const TEST_BOOSTED = true; + +/* + * Testing on MAINNET + * - Update hardhat.config.js with chainId = 1 + * - Update ALCHEMY_URL on .env with a mainnet api key + * - Run node on terminal: yarn run node + * - Uncomment section below: + */ +const network = Network.MAINNET; +const blockNumber = 16067738; +const customSubgraphUrl = + 'https://api.thegraph.com/subgraphs/name/balancer-labs/balancer-v2'; +const { ALCHEMY_URL: jsonRpcUrl } = process.env; +const rpcUrl = 'http://127.0.0.1:8545'; + +const { TENDERLY_ACCESS_KEY, TENDERLY_USER, TENDERLY_PROJECT } = process.env; +const { ethers } = hardhat; +const MAX_GAS_LIMIT = 8e6; + +const tenderlyConfig: BalancerTenderlyConfig = { + accessKey: TENDERLY_ACCESS_KEY as string, + user: TENDERLY_USER as string, + project: TENDERLY_PROJECT as string, + blockNumber, +}; + +const sdk = new BalancerSDK({ + network, + rpcUrl, + customSubgraphUrl, + tenderly: tenderlyConfig, +}); +const { pools } = sdk; +const provider = new ethers.providers.JsonRpcProvider(rpcUrl, network); +const signer = provider.getSigner(); +const { contracts, contractAddresses } = new Contracts( + network as number, + provider +); +const relayer = contractAddresses.relayerV4 as string; +const addresses = ADDRESSES[network]; + +interface Test { + signer: JsonRpcSigner; + description: string; + pool: { + id: string; + address: string; + }; + tokensIn: string[]; + amountsIn: string[]; + authorisation: string | undefined; + wrapMainTokens: boolean; +} + +const runTests = async (tests: Test[]) => { + for (let i = 0; i < tests.length; i++) { + const test = tests[i]; + it(test.description, async () => { + const userAddress = await test.signer.getAddress(); + const authorisation = await Relayer.signRelayerApproval( + relayer, + userAddress, + signer, + contracts.vault + ); + await testFlow( + userAddress, + test.pool, + test.tokensIn, + test.amountsIn, + test.wrapMainTokens, + authorisation + ); + }).timeout(120000); + } +}; + +const testFlow = async ( + userAddress: string, + pool: { id: string; address: string }, + tokensIn: string[], + amountsIn: string[], + wrapMainTokens: boolean, + authorisation: string | undefined +) => { + const [bptBalanceBefore, ...tokensInBalanceBefore] = await getBalances( + [pool.address, ...tokensIn], + signer, + userAddress + ); + + const gasLimit = MAX_GAS_LIMIT; + const slippage = '10'; // 10 bps = 0.1% + + const query = await pools.generalisedJoin( + pool.id, + tokensIn, + amountsIn, + userAddress, + wrapMainTokens, + slippage, + authorisation + ); + + console.log(query.priceImpact, 'priceImpact Raw'); + const piPercent = SolidityMaths.mulDownFixed( + BigInt(query.priceImpact), + BigInt('100000000000000000000') + ); + console.log(formatFixed(piPercent, 18), 'priceImpact %'); + + const response = await signer.sendTransaction({ + to: query.to, + data: query.callData, + gasLimit, + }); + + const receipt = await response.wait(); + console.log('Gas used', receipt.gasUsed.toString()); + + const [bptBalanceAfter, ...tokensInBalanceAfter] = await getBalances( + [pool.address, ...tokensIn], + signer, + userAddress + ); + expect(receipt.status).to.eql(1); + expect(BigNumber.from(query.minOut).gte('0')).to.be.true; + expect(BigNumber.from(query.expectedOut).gt(query.minOut)).to.be.true; + tokensInBalanceAfter.forEach((balanceAfter, i) => { + expect(balanceAfter.toString()).to.eq( + tokensInBalanceBefore[i].sub(amountsIn[i]).toString() + ); + }); + expect(bptBalanceBefore.eq(0)).to.be.true; + expect(bptBalanceAfter.gte(query.minOut)).to.be.true; + console.log(bptBalanceAfter.toString(), 'bpt after'); + console.log(query.minOut, 'minOut'); + console.log(query.expectedOut, 'expectedOut'); +}; + +describe('generalised join execution', async () => { + context('bbausd', async () => { + if (!TEST_BOOSTED) return true; + let authorisation: string | undefined; + beforeEach(async () => { + const tokens = [ + addresses.USDC.address, + addresses.DAI.address, + addresses.USDT.address, + ]; + const slots = [ + addresses.USDC.slot, + addresses.DAI.slot, + addresses.USDT.slot, + ]; + const balances = [ + parseFixed('1000000', 6).toString(), + parseFixed('1000000', 18).toString(), + parseFixed('1000000', 6).toString(), + ]; + await forkSetup( + signer, + tokens, + slots, + balances, + jsonRpcUrl as string, + blockNumber + ); + }); + + await runTests([ + { + signer, + description: 'join with leaf tokens', + pool: { + id: addresses.bbausd2.id, + address: addresses.bbausd2.address, + }, + tokensIn: [ + addresses.USDC.address, + addresses.DAI.address, + addresses.USDT.address, + ], + amountsIn: [ + parseFixed('10', 6).toString(), + parseFixed('10', 18).toString(), + parseFixed('10', 6).toString(), + ], + authorisation: authorisation, + wrapMainTokens: false, + }, + { + signer, + description: 'join with leaf tokens', + pool: { + id: addresses.bbausd2.id, + address: addresses.bbausd2.address, + }, + tokensIn: [addresses.USDC.address, addresses.DAI.address], + amountsIn: [ + parseFixed('0.1', 6).toString(), + parseFixed('0.001', 18).toString(), + ], + authorisation: authorisation, + wrapMainTokens: false, + }, + { + signer, + description: 'join with leaf tokens', + pool: { + id: addresses.bbausd2.id, + address: addresses.bbausd2.address, + }, + tokensIn: [addresses.USDC.address, addresses.DAI.address], + amountsIn: [ + parseFixed('900000', 6).toString(), + parseFixed('0.001', 18).toString(), + ], + authorisation: authorisation, + wrapMainTokens: false, + }, + ]); + }); +}); diff --git a/balancer-js/src/modules/joins/joins.module.ts b/balancer-js/src/modules/joins/joins.module.ts index da74ed427..9b4f252de 100644 --- a/balancer-js/src/modules/joins/joins.module.ts +++ b/balancer-js/src/modules/joins/joins.module.ts @@ -122,6 +122,9 @@ export class Join { true ).toString(); + console.log(totalMinAmountOut, `totalMinOut`); + console.log(totalBptZeroPi.toString(), `totalBptZeroPi`); + // Create calls with minAmountsOut const { callData, deltas } = await this.createCalls( joinPaths, @@ -408,6 +411,10 @@ export class Join { parentNode.joinAction === 'joinPool' ) { const sp = parentNode.spotPrices[childAddress.toLowerCase()]; + console.log( + `Spot price ${childAddress.toLowerCase()}: `, + sp.toString() + ); spProduct = spProduct * parseFloat(sp); childAddress = parentNode.address; } @@ -420,6 +427,8 @@ export class Join { inputAmountScaled, spPriceScaled.toBigInt() ); + console.log('sp product: ', spProduct.toString()); + console.log('zeroPriceImpact amount for path: ', bptOut.toString(), '\n'); return bptOut; }; From 6019b26c3a9c03795167a7b812e7bf04c1242276 Mon Sep 17 00:00:00 2001 From: johngrantuk <johngrantuk@googlemail.com> Date: Wed, 14 Dec 2022 12:06:39 +0000 Subject: [PATCH 02/25] Update to use onchain pools/priceRates. Linear uses SP calc instead of default. --- balancer-js/package.json | 2 +- balancer-js/src/modules/data/index.ts | 35 +++ balancer-js/src/modules/data/pool/index.ts | 1 + .../src/modules/data/pool/onChainData.ts | 277 ++++++++++++++++ balancer-js/src/modules/data/pool/subgraph.ts | 7 +- .../src/modules/data/pool/subgraphOnChain.ts | 295 ++++++++++++++++++ balancer-js/src/modules/graph/graph.ts | 2 +- .../joins/debug.module.integration.spec.ts | 2 +- balancer-js/src/modules/joins/joins.module.ts | 3 +- balancer-js/src/modules/pools/index.ts | 4 +- balancer-js/src/types.ts | 1 + balancer-js/yarn.lock | 7 +- 12 files changed, 621 insertions(+), 15 deletions(-) create mode 100644 balancer-js/src/modules/data/pool/onChainData.ts create mode 100644 balancer-js/src/modules/data/pool/subgraphOnChain.ts diff --git a/balancer-js/package.json b/balancer-js/package.json index ff8dd76b5..e7034d9c9 100644 --- a/balancer-js/package.json +++ b/balancer-js/package.json @@ -89,7 +89,7 @@ "typescript": "^4.0.2" }, "dependencies": { - "@balancer-labs/sor": "^4.0.1-beta.15", + "@balancer-labs/sor": "/Users/jg/Documents/balancer-sor/balancer-labs-sor-v4.0.1-beta.22.tgz", "@balancer-labs/typechain": "^1.0.0", "axios": "^0.24.0", "graphql": "^15.6.1", diff --git a/balancer-js/src/modules/data/index.ts b/balancer-js/src/modules/data/index.ts index 57cc72428..d44911131 100644 --- a/balancer-js/src/modules/data/index.ts +++ b/balancer-js/src/modules/data/index.ts @@ -14,10 +14,12 @@ export * from './protocol-fees/provider'; export * from './token-yields/repository'; export * from './block-number'; +import { GraphQLArgs } from '@/.'; import { BalancerNetworkConfig, BalancerDataRepositories } from '@/types'; import { PoolsSubgraphRepository } from './pool/subgraph'; import { PoolSharesRepository } from './pool-shares/repository'; import { PoolJoinExitRepository } from './pool-joinExit/repository'; +import { PoolsSubgraphOnChainRepository } from './pool/subgraphOnChain'; import { PoolGaugesRepository } from './pool-gauges/repository'; import { GaugeSharesRepository } from './gauge-shares/repository'; import { BlockNumberRepository } from './block-number'; @@ -43,6 +45,7 @@ import { SubgraphPriceRepository } from './token-prices/subgraph'; export class Data implements BalancerDataRepositories { pools; + poolsOnChain; yesterdaysPools; poolShares; poolGauges; @@ -64,6 +67,38 @@ export class Data implements BalancerDataRepositories { chainId: networkConfig.chainId, }); + const subgraphArgs: GraphQLArgs = { + where: { + swapEnabled: { + eq: true, + }, + totalShares: { + gt: 0.000000000001, + }, + id: { + in: [ + '0xa13a9247ea42d743238089903570127dda72fe4400000000000000000000035d', + '0x2F4EB100552EF93840D5ADC30560E5513DFFFACB000000000000000000000334'.toLowerCase(), + '0xAE37D54AE477268B9997D4161B96B8200755935C000000000000000000000337'.toLowerCase(), + '0x82698AECC9E28E9BB27608BD52CF57F704BD1B83000000000000000000000336'.toLowerCase(), + ], + }, + }, + orderBy: 'totalLiquidity', + orderDirection: 'desc', + block: { number: 16176441 }, + }; + const subgraphQuery = { args: subgraphArgs, attrs: {} }; + + this.poolsOnChain = new PoolsSubgraphOnChainRepository({ + url: networkConfig.urls.subgraph, + chainId: networkConfig.chainId, + provider: provider, + multicall: networkConfig.addresses.contracts.multicall, + vault: networkConfig.addresses.contracts.vault, + query: subgraphQuery, + }); + this.poolShares = new PoolSharesRepository( networkConfig.urls.subgraph, networkConfig.chainId diff --git a/balancer-js/src/modules/data/pool/index.ts b/balancer-js/src/modules/data/pool/index.ts index 5c246217e..89d24baf0 100644 --- a/balancer-js/src/modules/data/pool/index.ts +++ b/balancer-js/src/modules/data/pool/index.ts @@ -3,3 +3,4 @@ export * from './balancer-api'; export * from './fallback'; export * from './static'; export * from './subgraph'; +export * from './subgraphOnChain'; diff --git a/balancer-js/src/modules/data/pool/onChainData.ts b/balancer-js/src/modules/data/pool/onChainData.ts new file mode 100644 index 000000000..484515d3c --- /dev/null +++ b/balancer-js/src/modules/data/pool/onChainData.ts @@ -0,0 +1,277 @@ +import { formatFixed } from '@ethersproject/bignumber'; +import { Provider } from '@ethersproject/providers'; +import { PoolFilter } from '@balancer-labs/sor'; +import { Multicaller } from '@/lib/utils/multiCaller'; +import { isSameAddress } from '@/lib/utils'; +import { Vault__factory } from '@balancer-labs/typechain'; +import { Pool } from '@/types'; + +// TODO: decide whether we want to trim these ABIs down to the relevant functions +import aTokenRateProvider from '@/lib/abi/StaticATokenRateProvider.json'; +import weightedPoolAbi from '@/lib/abi/WeightedPool.json'; +import stablePoolAbi from '@/lib/abi/StablePool.json'; +import elementPoolAbi from '@/lib/abi/ConvergentCurvePool.json'; +import linearPoolAbi from '@/lib/abi/LinearPool.json'; +import composableStableAbi from '@/lib/abi/ComposableStable.json'; + +export async function getOnChainBalances( + subgraphPoolsOriginal: Pool[], + multiAddress: string, + vaultAddress: string, + provider: Provider +): Promise<Pool[]> { + if (subgraphPoolsOriginal.length === 0) return subgraphPoolsOriginal; + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const abis: any = Object.values( + // Remove duplicate entries using their names + Object.fromEntries( + [ + ...Vault__factory.abi, + ...aTokenRateProvider, + ...weightedPoolAbi, + ...stablePoolAbi, + ...elementPoolAbi, + ...linearPoolAbi, + ...composableStableAbi, + ].map((row) => [row.name, row]) + ) + ); + + const multiPool = new Multicaller(multiAddress, provider, abis); + + const supportedPoolTypes: string[] = Object.values(PoolFilter); + const subgraphPools: Pool[] = []; + subgraphPoolsOriginal.forEach((pool) => { + if (!supportedPoolTypes.includes(pool.poolType)) { + console.error(`Unknown pool type: ${pool.poolType} ${pool.id}`); + return; + } + + subgraphPools.push(pool); + + multiPool.call(`${pool.id}.poolTokens`, vaultAddress, 'getPoolTokens', [ + pool.id, + ]); + multiPool.call(`${pool.id}.totalSupply`, pool.address, 'totalSupply'); + + // Pools with pre minted BPT + if (pool.poolType.includes('Linear') || pool.poolType === 'StablePhantom') { + multiPool.call( + `${pool.id}.virtualSupply`, + pool.address, + 'getVirtualSupply' + ); + } + + /** + * Returns the effective BPT supply. + * In other pools, this would be the same as `totalSupply`, but there are two key differences here: + * - this pool pre-mints BPT and holds it in the Vault as a token, and as such we need to subtract the Vault's + * balance to get the total "circulating supply". This is called the 'virtualSupply'. + * - the Pool owes debt to the Protocol in the form of unminted BPT, which will be minted immediately before the + * next join or exit. We need to take these into account since, even if they don't yet exist, they will + * effectively be included in any Pool operation that involves BPT. + * In the vast majority of cases, this function should be used instead of `totalSupply()`. + */ + if (pool.poolType === 'ComposableStable') + multiPool.call( + `${pool.id}.actualSupply`, + pool.address, + 'getActualSupply' + ); + + // TO DO - Make this part of class to make more flexible? + if ( + pool.poolType === 'Weighted' || + pool.poolType === 'LiquidityBootstrapping' || + pool.poolType === 'Investment' + ) { + multiPool.call( + `${pool.id}.weights`, + pool.address, + 'getNormalizedWeights' + ); + multiPool.call( + `${pool.id}.swapFee`, + pool.address, + 'getSwapFeePercentage' + ); + } else if ( + pool.poolType === 'Stable' || + pool.poolType === 'MetaStable' || + pool.poolType === 'StablePhantom' || + pool.poolType === 'ComposableStable' + ) { + // MetaStable & StablePhantom is the same as Stable for multicall purposes + multiPool.call( + `${pool.id}.amp`, + pool.address, + 'getAmplificationParameter' + ); + multiPool.call( + `${pool.id}.swapFee`, + pool.address, + 'getSwapFeePercentage' + ); + } else if (pool.poolType === 'Element') { + multiPool.call(`${pool.id}.swapFee`, pool.address, 'percentFee'); + } else if (pool.poolType.toString().includes('Linear')) { + multiPool.call( + `${pool.id}.swapFee`, + pool.address, + 'getSwapFeePercentage' + ); + + multiPool.call(`${pool.id}.targets`, pool.address, 'getTargets'); + multiPool.call(`${pool.id}.rate`, pool.address, 'getWrappedTokenRate'); + } else if (pool.poolType.toString().includes('Gyro')) { + multiPool.call( + `${pool.id}.swapFee`, + pool.address, + 'getSwapFeePercentage' + ); + } + }); + + let pools = {} as Record< + string, + { + amp?: string[]; + swapFee: string; + weights?: string[]; + targets?: string[]; + poolTokens: { + tokens: string[]; + balances: string[]; + }; + totalSupply: string; + virtualSupply?: string; + rate?: string; + actualSupply?: string; + } + >; + + try { + pools = (await multiPool.execute()) as Record< + string, + { + amp?: string[]; + swapFee: string; + weights?: string[]; + poolTokens: { + tokens: string[]; + balances: string[]; + }; + totalSupply: string; + virtualSupply?: string; + rate?: string; + actualSupply?: string; + } + >; + } catch (err) { + throw `Issue with multicall execution.`; + } + + const onChainPools: Pool[] = []; + + Object.entries(pools).forEach(([poolId, onchainData], index) => { + try { + const { + poolTokens, + swapFee, + weights, + totalSupply, + virtualSupply, + actualSupply, + } = onchainData; + + if ( + subgraphPools[index].poolType === 'Stable' || + subgraphPools[index].poolType === 'MetaStable' || + subgraphPools[index].poolType === 'StablePhantom' || + subgraphPools[index].poolType === 'ComposableStable' + ) { + if (!onchainData.amp) { + console.error(`Stable Pool Missing Amp: ${poolId}`); + return; + } else { + // Need to scale amp by precision to match expected Subgraph scale + // amp is stored with 3 decimals of precision + subgraphPools[index].amp = formatFixed(onchainData.amp[0], 3); + } + } + + if (subgraphPools[index].poolType.includes('Linear')) { + if (!onchainData.targets) { + console.error(`Linear Pool Missing Targets: ${poolId}`); + return; + } else { + subgraphPools[index].lowerTarget = formatFixed( + onchainData.targets[0], + 18 + ); + subgraphPools[index].upperTarget = formatFixed( + onchainData.targets[1], + 18 + ); + } + + const wrappedIndex = subgraphPools[index].wrappedIndex; + if (wrappedIndex === undefined || onchainData.rate === undefined) { + console.error( + `Linear Pool Missing WrappedIndex or PriceRate: ${poolId}` + ); + return; + } + // Update priceRate of wrappedToken + subgraphPools[index].tokens[wrappedIndex].priceRate = formatFixed( + onchainData.rate, + 18 + ); + } + + subgraphPools[index].swapFee = formatFixed(swapFee, 18); + + poolTokens.tokens.forEach((token, i) => { + const T = subgraphPools[index].tokens.find((t) => + isSameAddress(t.address, token) + ); + if (!T) throw `Pool Missing Expected Token: ${poolId} ${token}`; + T.balance = formatFixed(poolTokens.balances[i], T.decimals); + if (weights) { + // Only expected for WeightedPools + T.weight = formatFixed(weights[i], 18); + } + }); + + // Pools with pre minted BPT + if ( + subgraphPools[index].poolType.includes('Linear') || + subgraphPools[index].poolType === 'StablePhantom' + ) { + if (virtualSupply === undefined) { + console.error( + `Pool with pre-minted BPT missing Virtual Supply: ${poolId}` + ); + return; + } + subgraphPools[index].totalShares = formatFixed(virtualSupply, 18); + } else if (subgraphPools[index].poolType === 'ComposableStable') { + if (actualSupply === undefined) { + console.error(`ComposableStable missing Actual Supply: ${poolId}`); + return; + } + subgraphPools[index].totalShares = formatFixed(actualSupply, 18); + } else { + subgraphPools[index].totalShares = formatFixed(totalSupply, 18); + } + + onChainPools.push(subgraphPools[index]); + } catch (err) { + throw `Issue with pool onchain data: ${err}`; + } + }); + + return onChainPools; +} diff --git a/balancer-js/src/modules/data/pool/subgraph.ts b/balancer-js/src/modules/data/pool/subgraph.ts index 5fb495660..18ad56425 100644 --- a/balancer-js/src/modules/data/pool/subgraph.ts +++ b/balancer-js/src/modules/data/pool/subgraph.ts @@ -33,11 +33,11 @@ interface PoolsSubgraphRepositoryOptions { query?: GraphQLQuery; } -interface SubgraphSubPoolToken extends SubgraphSubPoolTokenFragment { +export interface SubgraphSubPoolToken extends SubgraphSubPoolTokenFragment { token?: SubgraphSubPoolMeta | null; } -interface SubgraphSubPoolMeta { +export interface SubgraphSubPoolMeta { latestUSDPrice?: string | null; pool?: SubgraphSubPool | null; } @@ -107,8 +107,7 @@ export class PoolsSubgraphRepository where: { swapEnabled: true, totalShares_gt: '0.000000000001' }, orderBy: Pool_OrderBy.TotalLiquidity, orderDirection: OrderDirection.Desc, - // block: await this.block(), - block: { number: 16067738 }, + block: await this.block(), }); console.timeEnd('fetching pools'); diff --git a/balancer-js/src/modules/data/pool/subgraphOnChain.ts b/balancer-js/src/modules/data/pool/subgraphOnChain.ts new file mode 100644 index 000000000..dea17d097 --- /dev/null +++ b/balancer-js/src/modules/data/pool/subgraphOnChain.ts @@ -0,0 +1,295 @@ +import { Findable, Searchable } from '../types'; +import { + createSubgraphClient, + SubgraphClient, + SubgraphPool, + Pool_OrderBy, + OrderDirection, + SubgraphPoolTokenFragment, +} from '@/modules/subgraph/subgraph'; +import { + GraphQLArgsBuilder, + SubgraphArgsFormatter, +} from '@/lib/graphql/args-builder'; +import { GraphQLArgs } from '@/lib/graphql/types'; +import { Provider } from '@ethersproject/providers'; +import { PoolAttribute, PoolsRepositoryFetchOptions } from './types'; +import { + GraphQLQuery, + Pool, + PoolType, + PoolToken, + SubPool, + SubPoolMeta, +} from '@/types'; +import { Network } from '@/lib/constants/network'; +import { PoolsQueryVariables } from '../../subgraph/subgraph'; +import { getOnChainBalances } from './onChainData'; +import { SubgraphSubPoolMeta, SubgraphSubPoolToken } from './subgraph'; + +interface PoolsSubgraphOnChainRepositoryOptions { + url: string; + chainId: Network; + provider: Provider; + multicall: string; + vault: string; + blockHeight?: () => Promise<number | undefined>; + query?: GraphQLQuery; +} + +/** + * Access pools using generated subgraph client. + * + * Balancer's subgraph URL: https://thegraph.com/hosted-service/subgraph/balancer-labs/balancer-v2 + */ +export class PoolsSubgraphOnChainRepository + implements Findable<Pool, PoolAttribute>, Searchable<Pool> +{ + private client: SubgraphClient; + private chainId: Network; + private provider: Provider; + private pools?: Promise<Pool[]>; + private multicall: string; + private vault: string; + public skip = 0; + private blockHeight: undefined | (() => Promise<number | undefined>); + private query: GraphQLQuery; + + /** + * Repository with optional lazy loaded blockHeight + * + * @param url subgraph URL + * @param chainId current network, needed for L2s logic + * @param blockHeight lazy loading blockHeigh resolver + * @param multicall multicall address + * @param valt vault address + */ + constructor(options: PoolsSubgraphOnChainRepositoryOptions) { + this.client = createSubgraphClient(options.url); + this.blockHeight = options.blockHeight; + this.chainId = options.chainId; + this.provider = options.provider; + this.multicall = options.multicall; + this.vault = options.vault; + + const defaultArgs: GraphQLArgs = { + orderBy: Pool_OrderBy.TotalLiquidity, + orderDirection: OrderDirection.Desc, + where: { + swapEnabled: { + eq: true, + }, + totalShares: { + gt: 0.000000000001, + }, + }, + }; + + const args = options.query?.args || defaultArgs; + const attrs = options.query?.attrs || {}; + + this.query = { + args, + attrs, + }; + } + + /** + * We need a list of all the pools, for calculating APRs (nested pools), and for SOR (path finding). + * All the pools are fetched on page load and cachced for speedy lookups. + * + * @returns Promise resolving to pools list + */ + private async fetchDefault(): Promise<Pool[]> { + console.time('fetching pools'); + const { pool0, pool1000, pool2000 } = await this.client.AllPools( + await this.getDefaultFilter(this.query.args) + ); + const pools = [...pool0, ...pool1000, ...pool2000].map( + this.mapType.bind(this) + ); + console.timeEnd('fetching pools'); + console.log(pools.length, 'Example filter should limit the pools length'); + console.log('Fetching onchain!'); + const onchainPools = await getOnChainBalances( + pools, + this.multicall, + this.vault, + this.provider + ); + + return onchainPools; + } + + async fetch(options?: PoolsRepositoryFetchOptions): Promise<Pool[]> { + if (options?.skip) { + this.query.args.skip = options.skip; + } + if (!this.query.args.block) { + this.query.args.block = await this.block(); + } + + this.query.args.first = options?.first || 1000; + + const formattedQuery = new GraphQLArgsBuilder(this.query.args).format( + new SubgraphArgsFormatter() + ) as PoolsQueryVariables; + + const { pools } = await this.client.Pools(formattedQuery); + + this.skip = (options?.skip || 0) + pools.length; + + return pools.map(this.mapType.bind(this)); + } + + async find(id: string): Promise<Pool | undefined> { + return await this.findBy('id', id); + } + + async findBy(param: PoolAttribute, value: string): Promise<Pool | undefined> { + if (!this.pools) { + this.pools = this.fetchDefault(); + } + + return (await this.pools).find((pool) => pool[param] == value); + + // TODO: @Nma - Fetching pools outside of default query is causing a lot of requests + // on a frontend, because results aren't cached anywhere. + // For fetching pools directly from subgraph with custom queries please use the client not this repository. + // Code below kept for reference, to be removed later. + // + // if (this.pools) { + // return (await this.pools).find((p) => p[param] === value); + // } + // const { pools } = await this.client.Pools({ + // where: { + // [param]: value, + // swapEnabled: true, + // totalShares_gt: '0.000000000001', + // }, + // block: await this.block(), + // }); + // const poolsTab: Pool[] = pools.map(this.mapType.bind(this)); + // return poolsTab.length > 0 ? poolsTab[0] : undefined; + } + + async all(): Promise<Pool[]> { + if (!this.pools) { + this.pools = this.fetchDefault(); + } + return this.pools; + } + + async block(): Promise<{ number: number | undefined } | undefined> { + return this.blockHeight ? { number: await this.blockHeight() } : undefined; + } + + async getDefaultFilter(args: GraphQLArgs): Promise<PoolsQueryVariables> { + const formattedQuery = new GraphQLArgsBuilder(args).format( + new SubgraphArgsFormatter() + ) as PoolsQueryVariables; + if (!formattedQuery.block) { + formattedQuery.block = await this.block(); + } + return formattedQuery; + } + + async where(filter: (pool: Pool) => boolean): Promise<Pool[]> { + if (!this.pools) { + this.pools = this.fetchDefault(); + } + + return (await this.pools).filter(filter); + } + + private mapType(subgraphPool: SubgraphPool): Pool { + return { + id: subgraphPool.id, + name: subgraphPool.name || '', + address: subgraphPool.address, + chainId: this.chainId, + poolType: subgraphPool.poolType as PoolType, + poolTypeVersion: subgraphPool.poolTypeVersion || 1, + swapFee: subgraphPool.swapFee, + swapEnabled: subgraphPool.swapEnabled, + protocolYieldFeeCache: subgraphPool.protocolYieldFeeCache || '0', + amp: subgraphPool.amp ?? undefined, + owner: subgraphPool.owner ?? undefined, + factory: subgraphPool.factory ?? undefined, + symbol: subgraphPool.symbol ?? undefined, + tokens: (subgraphPool.tokens || []).map(this.mapToken.bind(this)), + tokensList: subgraphPool.tokensList, + tokenAddresses: (subgraphPool.tokens || []).map((t) => t.address), + totalLiquidity: subgraphPool.totalLiquidity, + totalShares: subgraphPool.totalShares, + totalSwapFee: subgraphPool.totalSwapFee, + totalSwapVolume: subgraphPool.totalSwapVolume, + priceRateProviders: subgraphPool.priceRateProviders ?? undefined, + // onchain: subgraphPool.onchain, + createTime: subgraphPool.createTime, + mainIndex: subgraphPool.mainIndex ?? undefined, + wrappedIndex: subgraphPool.wrappedIndex ?? undefined, + // mainTokens: subgraphPool.mainTokens, + // wrappedTokens: subgraphPool.wrappedTokens, + // unwrappedTokens: subgraphPool.unwrappedTokens, + // isNew: subgraphPool.isNew, + // volumeSnapshot: subgraphPool.volumeSnapshot, + // feesSnapshot: subgraphPool.???, // Approximated last 24h fees + // boost: subgraphPool.boost, + totalWeight: subgraphPool.totalWeight || '1', + lowerTarget: subgraphPool.lowerTarget ?? '0', + upperTarget: subgraphPool.upperTarget ?? '0', + }; + } + + private mapToken(subgraphToken: SubgraphPoolTokenFragment): PoolToken { + const subPoolInfo = this.mapSubPools( + // need to typecast as the fragment is 3 layers deep while the type is infinite levels deep + subgraphToken.token as SubgraphSubPoolMeta + ); + return { + ...subgraphToken, + isExemptFromYieldProtocolFee: + subgraphToken.isExemptFromYieldProtocolFee || false, + token: subPoolInfo, + }; + } + + private mapSubPools(metadata: SubgraphSubPoolMeta): SubPoolMeta { + let subPool: SubPool | null = null; + if (metadata.pool) { + subPool = { + id: metadata.pool.id, + address: metadata.pool.address, + totalShares: metadata.pool.totalShares, + poolType: metadata.pool.poolType as PoolType, + mainIndex: metadata.pool.mainIndex || 0, + }; + + if (metadata?.pool.tokens) { + subPool.tokens = metadata.pool.tokens.map( + this.mapSubPoolToken.bind(this) + ); + } + } + + return { + pool: subPool, + latestUSDPrice: metadata.latestUSDPrice || undefined, + }; + } + + private mapSubPoolToken(token: SubgraphSubPoolToken) { + return { + address: token.address, + decimals: token.decimals, + symbol: token.symbol, + balance: token.balance, + priceRate: token.priceRate, + weight: token.weight, + isExemptFromYieldProtocolFee: + token.isExemptFromYieldProtocolFee || undefined, + token: token.token ? this.mapSubPools(token.token) : undefined, + }; + } +} diff --git a/balancer-js/src/modules/graph/graph.ts b/balancer-js/src/modules/graph/graph.ts index c2bc03cbc..ac24ade16 100644 --- a/balancer-js/src/modules/graph/graph.ts +++ b/balancer-js/src/modules/graph/graph.ts @@ -150,7 +150,7 @@ export class PoolGraph { decimals = token.decimals ? token.decimals : 18; return; } - const sp = controller.calcSpotPrice(token.address, pool.address, true); + const sp = controller.calcSpotPrice(token.address, pool.address, false); spotPrices[token.address] = sp; }); diff --git a/balancer-js/src/modules/joins/debug.module.integration.spec.ts b/balancer-js/src/modules/joins/debug.module.integration.spec.ts index c54471efc..ad3e52f22 100644 --- a/balancer-js/src/modules/joins/debug.module.integration.spec.ts +++ b/balancer-js/src/modules/joins/debug.module.integration.spec.ts @@ -24,7 +24,7 @@ const TEST_BOOSTED = true; * - Uncomment section below: */ const network = Network.MAINNET; -const blockNumber = 16067738; +const blockNumber = 16176441; const customSubgraphUrl = 'https://api.thegraph.com/subgraphs/name/balancer-labs/balancer-v2'; const { ALCHEMY_URL: jsonRpcUrl } = process.env; diff --git a/balancer-js/src/modules/joins/joins.module.ts b/balancer-js/src/modules/joins/joins.module.ts index 9b4f252de..d3e357775 100644 --- a/balancer-js/src/modules/joins/joins.module.ts +++ b/balancer-js/src/modules/joins/joins.module.ts @@ -122,9 +122,8 @@ export class Join { true ).toString(); - console.log(totalMinAmountOut, `totalMinOut`); + console.log(totalAmountOut, `totalAmountOut`); console.log(totalBptZeroPi.toString(), `totalBptZeroPi`); - // Create calls with minAmountsOut const { callData, deltas } = await this.createCalls( joinPaths, diff --git a/balancer-js/src/modules/pools/index.ts b/balancer-js/src/modules/pools/index.ts index 24a87cf07..c7bede9eb 100644 --- a/balancer-js/src/modules/pools/index.ts +++ b/balancer-js/src/modules/pools/index.ts @@ -48,8 +48,8 @@ export class Pools implements Findable<PoolWithMethods> { repositories.pools, repositories.tokenPrices ); - this.joinService = new Join(this.repositories.pools, networkConfig); - this.exitService = new Exit(this.repositories.pools, networkConfig); + this.joinService = new Join(this.repositories.poolsOnChain, networkConfig); + this.exitService = new Exit(this.repositories.poolsOnChain, networkConfig); this.feesService = new PoolFees(repositories.yesterdaysPools); this.volumeService = new PoolVolume(repositories.yesterdaysPools); this.impermanentLossService = new ImpermanentLossService( diff --git a/balancer-js/src/types.ts b/balancer-js/src/types.ts index 4b4eaf6ef..ef36bee28 100644 --- a/balancer-js/src/types.ts +++ b/balancer-js/src/types.ts @@ -98,6 +98,7 @@ export interface BalancerNetworkConfig { export interface BalancerDataRepositories { pools: Findable<Pool, PoolAttribute> & Searchable<Pool>; + poolsOnChain: Findable<Pool, PoolAttribute> & Searchable<Pool>; yesterdaysPools?: Findable<Pool, PoolAttribute> & Searchable<Pool>; tokenPrices: Findable<Price>; tokenHistoricalPrices: Findable<Price>; diff --git a/balancer-js/yarn.lock b/balancer-js/yarn.lock index b5760cda8..4b34752df 100644 --- a/balancer-js/yarn.lock +++ b/balancer-js/yarn.lock @@ -503,10 +503,9 @@ "@babel/helper-validator-identifier" "^7.19.1" to-fast-properties "^2.0.0" -"@balancer-labs/sor@^4.0.1-beta.15": - version "4.0.1-beta.15" - resolved "https://registry.yarnpkg.com/@balancer-labs/sor/-/sor-4.0.1-beta.15.tgz#4fc6ae8df195a20abf597babb5453a5d303bdcda" - integrity sha512-8BlaQqmV56gs4b2YFs6xjBFGIxthg7SEjuxDcb7Q45AUUdFutGKsX8glC2hI4ZDM5+7W0FmNwGnKF2OrzFsuiQ== +"@balancer-labs/sor@/Users/jg/Documents/balancer-sor/balancer-labs-sor-v4.0.1-beta.22.tgz": + version "4.0.1-beta.22" + resolved "/Users/jg/Documents/balancer-sor/balancer-labs-sor-v4.0.1-beta.22.tgz#d159ee89feb9cbf01b4807fe402334f8a3b6dc89" dependencies: isomorphic-fetch "^2.2.1" From 14baecda9b9188f5a1d7a83bc8ca6e48ab7855b0 Mon Sep 17 00:00:00 2001 From: Bruno Eidam Guerios <bruno@balancer.finance> Date: Tue, 20 Dec 2022 16:00:02 -0300 Subject: [PATCH 03/25] Fix SubgraphPriceRepository overriding customSubgraphUrl provided on sdk config --- balancer-js/src/modules/data/index.ts | 1 + balancer-js/src/modules/data/token-prices/subgraph.ts | 4 +--- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/balancer-js/src/modules/data/index.ts b/balancer-js/src/modules/data/index.ts index 57cc72428..fb4666a56 100644 --- a/balancer-js/src/modules/data/index.ts +++ b/balancer-js/src/modules/data/index.ts @@ -116,6 +116,7 @@ export class Data implements BalancerDataRepositories { ); const subgraphPriceRepository = new SubgraphPriceRepository( + networkConfig.urls.subgraph, networkConfig.chainId ); diff --git a/balancer-js/src/modules/data/token-prices/subgraph.ts b/balancer-js/src/modules/data/token-prices/subgraph.ts index af46361ba..c9ff89eb7 100644 --- a/balancer-js/src/modules/data/token-prices/subgraph.ts +++ b/balancer-js/src/modules/data/token-prices/subgraph.ts @@ -16,12 +16,10 @@ interface SubgraphPricesResponse { } export class SubgraphPriceRepository implements Findable<Price> { - private subgraphUrl: string; prices: { [key: string]: Promise<Price> } = {}; debouncer: Debouncer<TokenPrices, string>; - constructor(private chainId: Network = 1) { - this.subgraphUrl = BALANCER_NETWORK_CONFIG[chainId].urls.subgraph; + constructor(private subgraphUrl: string, private chainId: Network = 1) { this.debouncer = new Debouncer<TokenPrices, string>( this.fetch.bind(this), 200 From 47858c2897e94e4be27949112193f33607058ee8 Mon Sep 17 00:00:00 2001 From: Bruno Eidam Guerios <bruno@balancer.finance> Date: Tue, 20 Dec 2022 16:08:32 -0300 Subject: [PATCH 04/25] Fix lint issue --- balancer-js/src/modules/data/token-prices/subgraph.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/balancer-js/src/modules/data/token-prices/subgraph.ts b/balancer-js/src/modules/data/token-prices/subgraph.ts index c9ff89eb7..b02590876 100644 --- a/balancer-js/src/modules/data/token-prices/subgraph.ts +++ b/balancer-js/src/modules/data/token-prices/subgraph.ts @@ -1,7 +1,6 @@ /* eslint-disable @typescript-eslint/no-empty-function */ import { Price, Findable, TokenPrices, Network } from '@/types'; import axios from 'axios'; -import { BALANCER_NETWORK_CONFIG } from '@/lib/constants/config'; import { Debouncer, tokenAddressForPricing } from '@/lib/utils'; interface SubgraphPricesResponse { From 1ae583431c61efbe8fa0a964faf47c9851436422 Mon Sep 17 00:00:00 2001 From: johngrantuk <johngrantuk@googlemail.com> Date: Wed, 4 Jan 2023 11:30:36 +0000 Subject: [PATCH 05/25] Fix factory. --- balancer-js/src/test/factories/data.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/balancer-js/src/test/factories/data.ts b/balancer-js/src/test/factories/data.ts index 81267b072..5a489558f 100644 --- a/balancer-js/src/test/factories/data.ts +++ b/balancer-js/src/test/factories/data.ts @@ -78,6 +78,7 @@ export const repositores = ({ ), }): BalancerDataRepositories => ({ pools, + poolsOnChain: pools, yesterdaysPools, tokenPrices, tokenHistoricalPrices, From a980b95ea0090ec56a0564ccc85f8dad50f4fbe2 Mon Sep 17 00:00:00 2001 From: bronco <rg3mvdb0@protonmail.com> Date: Wed, 4 Jan 2023 13:29:52 +0100 Subject: [PATCH 06/25] new: emissions service --- balancer-js/examples/pools/emissions.ts | 21 +++++++++++++ balancer-js/src/modules/data/types.ts | 1 + .../src/modules/pools/emissions/index.spec.ts | 30 +++++++++++++++++++ .../src/modules/pools/emissions/index.ts | 26 ++++++++++++++++ balancer-js/src/modules/pools/index.ts | 7 +++++ balancer-js/src/test/factories/index.ts | 3 +- .../src/test/factories/liquidity-gauges.ts | 17 +++++++++++ 7 files changed, 104 insertions(+), 1 deletion(-) create mode 100644 balancer-js/examples/pools/emissions.ts create mode 100644 balancer-js/src/modules/pools/emissions/index.spec.ts create mode 100644 balancer-js/src/modules/pools/emissions/index.ts create mode 100644 balancer-js/src/test/factories/liquidity-gauges.ts diff --git a/balancer-js/examples/pools/emissions.ts b/balancer-js/examples/pools/emissions.ts new file mode 100644 index 000000000..4c405ee2d --- /dev/null +++ b/balancer-js/examples/pools/emissions.ts @@ -0,0 +1,21 @@ +/** + * Display weekly BAL emissiosn for a pool + * Run command: yarn examples:run ./examples/pools/emissions.ts + */ +import { BalancerSDK } from '@/.' + +const sdk = new BalancerSDK({ + network: 1, + rpcUrl: 'https://rpc.ankr.com/eth', +}) + +const { pools } = sdk + +const main = async () => { + if (pools.emissionsService) { + const emissions = await pools.emissionsService.weekly('0x334c96d792e4b26b841d28f53235281cec1be1f200020000000000000000038a') + console.log(emissions) + } +} + +main() diff --git a/balancer-js/src/modules/data/types.ts b/balancer-js/src/modules/data/types.ts index 939d5a9f2..5a0150f14 100644 --- a/balancer-js/src/modules/data/types.ts +++ b/balancer-js/src/modules/data/types.ts @@ -1,6 +1,7 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ export { LiquidityGauge } from './liquidity-gauges/provider'; +export { RewardData } from './liquidity-gauges/multicall'; export { PoolAttribute } from './pool/types'; export { TokenAttribute } from './token/types'; export { ProtocolFees } from './protocol-fees/provider'; diff --git a/balancer-js/src/modules/pools/emissions/index.spec.ts b/balancer-js/src/modules/pools/emissions/index.spec.ts new file mode 100644 index 000000000..1b336c468 --- /dev/null +++ b/balancer-js/src/modules/pools/emissions/index.spec.ts @@ -0,0 +1,30 @@ +import * as emissions from '@/modules/data/bal/emissions'; +import { factories } from '@/test/factories'; +import { LiquidityGauge } from '@/types'; +import { expect } from 'chai'; +import { EmissionsService } from './'; + +const poolId = '1'; +const relativeWeight = 0.1; +const gauge = factories.gaugesFactory.build({ poolId, relativeWeight }); + +const gaugesMap = new Map([['1', gauge]]); +const gauges = factories.data.findable<LiquidityGauge>(gaugesMap); +const service = new EmissionsService(gauges); +const total = emissions.weekly(); + +describe('EmissionsService', () => { + context('with liquidity gauge', () => { + it('.weekly returns a value', async () => { + const bal = await service.weekly(poolId); + expect(bal).to.eq(total * relativeWeight); + }); + }); + + context('without liquidity gauge', () => { + it('.weekly returns 0', async () => { + const bal = await service.weekly('abc'); + expect(bal).to.eq(0); + }); + }); +}); diff --git a/balancer-js/src/modules/pools/emissions/index.ts b/balancer-js/src/modules/pools/emissions/index.ts new file mode 100644 index 000000000..3fbe06195 --- /dev/null +++ b/balancer-js/src/modules/pools/emissions/index.ts @@ -0,0 +1,26 @@ +import * as emissions from '@/modules/data/bal/emissions'; +import { Findable, LiquidityGauge } from '@/types'; + +/** + * Returns BAL emissions per pool + */ +export class EmissionsService { + constructor(private liquidityGaugesRepository: Findable<LiquidityGauge>) {} + + async relativeWeight(poolId: string): Promise<number> { + const gauge = await this.liquidityGaugesRepository.findBy('poolId', poolId); + + if (gauge) { + return gauge.relativeWeight; + } + + return 0; + } + + async weekly(poolId: string): Promise<number> { + const perWeek = emissions.weekly(); + const relativeWeight = await this.relativeWeight(poolId); + + return perWeek * relativeWeight; + } +} diff --git a/balancer-js/src/modules/pools/index.ts b/balancer-js/src/modules/pools/index.ts index 93e100561..086a986a2 100644 --- a/balancer-js/src/modules/pools/index.ts +++ b/balancer-js/src/modules/pools/index.ts @@ -19,6 +19,7 @@ import { PoolVolume } from './volume/volume'; import { PoolFees } from './fees/fees'; import * as Queries from './queries'; import { BalancerError } from '@/balancerErrors'; +import { EmissionsService } from './emissions'; const notImplemented = (poolType: string, name: string) => () => { throw `${name} for poolType ${poolType} not implemented`; @@ -35,6 +36,7 @@ export class Pools implements Findable<PoolWithMethods> { feesService; volumeService; impermanentLossService; + emissionsService; constructor( private networkConfig: BalancerNetworkConfig, @@ -62,6 +64,11 @@ export class Pools implements Findable<PoolWithMethods> { repositories.tokenPrices, repositories.tokenHistoricalPrices ); + if (repositories.liquidityGauges) { + this.emissionsService = new EmissionsService( + repositories.liquidityGauges + ); + } } dataSource(): Findable<Pool, PoolAttribute> & Searchable<Pool> { diff --git a/balancer-js/src/test/factories/index.ts b/balancer-js/src/test/factories/index.ts index 11f0d81aa..7d3846f95 100644 --- a/balancer-js/src/test/factories/index.ts +++ b/balancer-js/src/test/factories/index.ts @@ -1,8 +1,9 @@ import * as sor from './sor'; import * as pools from './pools'; +import * as liquidityGauges from './liquidity-gauges'; import * as sdk from './sdk'; import * as data from './data'; -const factories = { ...sor, ...pools, ...sdk, data }; +const factories = { ...sor, ...pools, ...sdk, ...liquidityGauges, data }; export { factories }; diff --git a/balancer-js/src/test/factories/liquidity-gauges.ts b/balancer-js/src/test/factories/liquidity-gauges.ts new file mode 100644 index 000000000..fa6ae966e --- /dev/null +++ b/balancer-js/src/test/factories/liquidity-gauges.ts @@ -0,0 +1,17 @@ +import { Factory } from 'fishery'; +import { LiquidityGauge, RewardData } from '@/types'; + +export const gaugesFactory = Factory.define<LiquidityGauge>(({ params }) => { + return { + id: params.id || '1', + address: params.address || '1', + name: params.name || 'gauge', + poolId: params.poolId || '1', + poolAddress: params.poolAddress || '1', + totalSupply: params.totalSupply || 1, + workingSupply: params.workingSupply || 1, + relativeWeight: params.relativeWeight || 1, + rewardTokens: + (params.rewardTokens as { [tokenAddress: string]: RewardData }) || [], + }; +}); From dd018a40f92b79beb4090211a22c6b0e96e41b03 Mon Sep 17 00:00:00 2001 From: johngrantuk <johngrantuk@googlemail.com> Date: Wed, 4 Jan 2023 13:13:27 +0000 Subject: [PATCH 07/25] Add custom sg query to Data module. --- balancer-js/src/modules/data/index.ts | 36 ++++++------------- .../exits/exits.module.integration.spec.ts | 33 +++++++++++++++-- .../joins/debug.module.integration.spec.ts | 26 +++++++++++++- .../joins/joins.module.integration.spec.ts | 33 +++++++++++++++-- .../concerns/metaStable/spotPrice.spec.ts | 2 +- .../concerns/stable/spotPrice.spec.ts | 2 +- .../concerns/stablePhantom/spotPrice.spec.ts | 2 +- balancer-js/src/modules/sdk.module.ts | 6 +++- balancer-js/src/types.ts | 1 + 9 files changed, 106 insertions(+), 35 deletions(-) diff --git a/balancer-js/src/modules/data/index.ts b/balancer-js/src/modules/data/index.ts index d44911131..a1261df12 100644 --- a/balancer-js/src/modules/data/index.ts +++ b/balancer-js/src/modules/data/index.ts @@ -14,8 +14,11 @@ export * from './protocol-fees/provider'; export * from './token-yields/repository'; export * from './block-number'; -import { GraphQLArgs } from '@/.'; -import { BalancerNetworkConfig, BalancerDataRepositories } from '@/types'; +import { + BalancerNetworkConfig, + BalancerDataRepositories, + GraphQLQuery, +} from '@/types'; import { PoolsSubgraphRepository } from './pool/subgraph'; import { PoolSharesRepository } from './pool-shares/repository'; import { PoolJoinExitRepository } from './pool-joinExit/repository'; @@ -61,35 +64,16 @@ export class Data implements BalancerDataRepositories { blockNumbers; poolJoinExits; - constructor(networkConfig: BalancerNetworkConfig, provider: Provider) { + constructor( + networkConfig: BalancerNetworkConfig, + provider: Provider, + subgraphQuery?: GraphQLQuery + ) { this.pools = new PoolsSubgraphRepository({ url: networkConfig.urls.subgraph, chainId: networkConfig.chainId, }); - const subgraphArgs: GraphQLArgs = { - where: { - swapEnabled: { - eq: true, - }, - totalShares: { - gt: 0.000000000001, - }, - id: { - in: [ - '0xa13a9247ea42d743238089903570127dda72fe4400000000000000000000035d', - '0x2F4EB100552EF93840D5ADC30560E5513DFFFACB000000000000000000000334'.toLowerCase(), - '0xAE37D54AE477268B9997D4161B96B8200755935C000000000000000000000337'.toLowerCase(), - '0x82698AECC9E28E9BB27608BD52CF57F704BD1B83000000000000000000000336'.toLowerCase(), - ], - }, - }, - orderBy: 'totalLiquidity', - orderDirection: 'desc', - block: { number: 16176441 }, - }; - const subgraphQuery = { args: subgraphArgs, attrs: {} }; - this.poolsOnChain = new PoolsSubgraphOnChainRepository({ url: networkConfig.urls.subgraph, chainId: networkConfig.chainId, diff --git a/balancer-js/src/modules/exits/exits.module.integration.spec.ts b/balancer-js/src/modules/exits/exits.module.integration.spec.ts index 9cdb66a29..4a5564b97 100644 --- a/balancer-js/src/modules/exits/exits.module.integration.spec.ts +++ b/balancer-js/src/modules/exits/exits.module.integration.spec.ts @@ -3,7 +3,13 @@ import dotenv from 'dotenv'; import { expect } from 'chai'; import hardhat from 'hardhat'; -import { BalancerSDK, BalancerTenderlyConfig, Network } from '@/.'; +import { + BalancerSDK, + BalancerTenderlyConfig, + Network, + GraphQLQuery, + GraphQLArgs, +} from '@/.'; import { BigNumber, parseFixed } from '@ethersproject/bignumber'; import { Contracts } from '@/modules/contracts/contracts.module'; import { forkSetup, getBalances } from '@/test/lib/utils'; @@ -50,6 +56,7 @@ const rpcUrl = 'http://127.0.0.1:8000'; const { TENDERLY_ACCESS_KEY, TENDERLY_USER, TENDERLY_PROJECT } = process.env; const { ethers } = hardhat; const MAX_GAS_LIMIT = 8e6; +const addresses = ADDRESSES[network]; // Custom Tenderly configuration parameters - remove in order to use default values const tenderlyConfig: BalancerTenderlyConfig = { @@ -59,11 +66,34 @@ const tenderlyConfig: BalancerTenderlyConfig = { blockNumber, }; +const poolAddresses = Object.values(addresses).map( + (address) => address.address +); + +const subgraphArgs: GraphQLArgs = { + where: { + swapEnabled: { + eq: true, + }, + totalShares: { + gt: 0.000000000001, + }, + address: { + in: poolAddresses, + }, + }, + orderBy: 'totalLiquidity', + orderDirection: 'desc', + block: { number: blockNumber }, +}; +const subgraphQuery: GraphQLQuery = { args: subgraphArgs, attrs: {} }; + const sdk = new BalancerSDK({ network, rpcUrl, customSubgraphUrl, tenderly: tenderlyConfig, + subgraphQuery, }); const { pools } = sdk; const provider = new ethers.providers.JsonRpcProvider(rpcUrl, network); @@ -73,7 +103,6 @@ const { contracts, contractAddresses } = new Contracts( provider ); const relayer = contractAddresses.relayerV4 as string; -const addresses = ADDRESSES[network]; interface Test { signer: JsonRpcSigner; diff --git a/balancer-js/src/modules/joins/debug.module.integration.spec.ts b/balancer-js/src/modules/joins/debug.module.integration.spec.ts index ad3e52f22..c2a43a242 100644 --- a/balancer-js/src/modules/joins/debug.module.integration.spec.ts +++ b/balancer-js/src/modules/joins/debug.module.integration.spec.ts @@ -3,7 +3,7 @@ import dotenv from 'dotenv'; import { expect } from 'chai'; import hardhat from 'hardhat'; -import { BalancerSDK, BalancerTenderlyConfig, Network } from '@/.'; +import { BalancerSDK, BalancerTenderlyConfig, Network, GraphQLArgs } from '@/.'; import { BigNumber, formatFixed, parseFixed } from '@ethersproject/bignumber'; import { Contracts } from '@/modules/contracts/contracts.module'; import { forkSetup, getBalances } from '@/test/lib/utils'; @@ -41,11 +41,35 @@ const tenderlyConfig: BalancerTenderlyConfig = { blockNumber, }; +const subgraphArgs: GraphQLArgs = { + where: { + swapEnabled: { + eq: true, + }, + totalShares: { + gt: 0.000000000001, + }, + id: { + in: [ + '0xa13a9247ea42d743238089903570127dda72fe4400000000000000000000035d', + '0x2F4EB100552EF93840D5ADC30560E5513DFFFACB000000000000000000000334'.toLowerCase(), + '0xAE37D54AE477268B9997D4161B96B8200755935C000000000000000000000337'.toLowerCase(), + '0x82698AECC9E28E9BB27608BD52CF57F704BD1B83000000000000000000000336'.toLowerCase(), + ], + }, + }, + orderBy: 'totalLiquidity', + orderDirection: 'desc', + block: { number: 16176441 }, +}; +const subgraphQuery = { args: subgraphArgs, attrs: {} }; + const sdk = new BalancerSDK({ network, rpcUrl, customSubgraphUrl, tenderly: tenderlyConfig, + subgraphQuery, }); const { pools } = sdk; const provider = new ethers.providers.JsonRpcProvider(rpcUrl, network); diff --git a/balancer-js/src/modules/joins/joins.module.integration.spec.ts b/balancer-js/src/modules/joins/joins.module.integration.spec.ts index cfca3b54c..3c20f706e 100644 --- a/balancer-js/src/modules/joins/joins.module.integration.spec.ts +++ b/balancer-js/src/modules/joins/joins.module.integration.spec.ts @@ -3,7 +3,13 @@ import dotenv from 'dotenv'; import { expect } from 'chai'; import hardhat from 'hardhat'; -import { BalancerSDK, BalancerTenderlyConfig, Network } from '@/.'; +import { + BalancerSDK, + BalancerTenderlyConfig, + Network, + GraphQLQuery, + GraphQLArgs, +} from '@/.'; import { BigNumber, parseFixed } from '@ethersproject/bignumber'; import { Contracts } from '@/modules/contracts/contracts.module'; import { forkSetup, getBalances } from '@/test/lib/utils'; @@ -54,6 +60,7 @@ const rpcUrl = 'http://127.0.0.1:8000'; const { TENDERLY_ACCESS_KEY, TENDERLY_USER, TENDERLY_PROJECT } = process.env; const { ethers } = hardhat; const MAX_GAS_LIMIT = 8e6; +const addresses = ADDRESSES[network]; // Custom Tenderly configuration parameters - remove in order to use default values const tenderlyConfig: BalancerTenderlyConfig = { @@ -63,11 +70,34 @@ const tenderlyConfig: BalancerTenderlyConfig = { blockNumber, }; +const poolAddresses = Object.values(addresses).map( + (address) => address.address +); + +const subgraphArgs: GraphQLArgs = { + where: { + swapEnabled: { + eq: true, + }, + totalShares: { + gt: 0.000000000001, + }, + address: { + in: poolAddresses, + }, + }, + orderBy: 'totalLiquidity', + orderDirection: 'desc', + block: { number: blockNumber }, +}; +const subgraphQuery: GraphQLQuery = { args: subgraphArgs, attrs: {} }; + const sdk = new BalancerSDK({ network, rpcUrl, customSubgraphUrl, tenderly: tenderlyConfig, + subgraphQuery, }); const { pools } = sdk; const provider = new ethers.providers.JsonRpcProvider(rpcUrl, network); @@ -77,7 +107,6 @@ const { contracts, contractAddresses } = new Contracts( provider ); const relayer = contractAddresses.relayerV4 as string; -const addresses = ADDRESSES[network]; interface Test { signer: JsonRpcSigner; diff --git a/balancer-js/src/modules/pools/pool-types/concerns/metaStable/spotPrice.spec.ts b/balancer-js/src/modules/pools/pool-types/concerns/metaStable/spotPrice.spec.ts index 3595663bb..54a34146a 100644 --- a/balancer-js/src/modules/pools/pool-types/concerns/metaStable/spotPrice.spec.ts +++ b/balancer-js/src/modules/pools/pool-types/concerns/metaStable/spotPrice.spec.ts @@ -22,7 +22,7 @@ describe('metaStable pool spot price', () => { ADDRESSES[network].wSTETH.address, pool ); - expect(spotPrice).to.eq('1.070497605163895290828158545877174735'); + expect(spotPrice).to.eq('1.070296441642066094033346842555222521'); }); }); }); diff --git a/balancer-js/src/modules/pools/pool-types/concerns/stable/spotPrice.spec.ts b/balancer-js/src/modules/pools/pool-types/concerns/stable/spotPrice.spec.ts index aaaa65e16..fcccdad0c 100644 --- a/balancer-js/src/modules/pools/pool-types/concerns/stable/spotPrice.spec.ts +++ b/balancer-js/src/modules/pools/pool-types/concerns/stable/spotPrice.spec.ts @@ -22,7 +22,7 @@ describe('stable pool spot price', () => { ADDRESSES[network].USDC.address, pool ); - expect(spotPrice).to.eq('1.000051911328148725'); + expect(spotPrice).to.eq('1.000067171032243145'); }); }); }); diff --git a/balancer-js/src/modules/pools/pool-types/concerns/stablePhantom/spotPrice.spec.ts b/balancer-js/src/modules/pools/pool-types/concerns/stablePhantom/spotPrice.spec.ts index 6b7638ee7..545c5546d 100644 --- a/balancer-js/src/modules/pools/pool-types/concerns/stablePhantom/spotPrice.spec.ts +++ b/balancer-js/src/modules/pools/pool-types/concerns/stablePhantom/spotPrice.spec.ts @@ -22,7 +22,7 @@ describe('phantomStable pool spot price', () => { ADDRESSES[network].bbausdc.address, pool ); - expect(spotPrice).to.eq('0.997873677414938406552928560423740375'); + expect(spotPrice).to.eq('0.997841372993511964077587098034000689'); }); }); }); diff --git a/balancer-js/src/modules/sdk.module.ts b/balancer-js/src/modules/sdk.module.ts index 7b01f0a12..74c41b806 100644 --- a/balancer-js/src/modules/sdk.module.ts +++ b/balancer-js/src/modules/sdk.module.ts @@ -42,7 +42,11 @@ export class BalancerSDK implements BalancerSDKRoot { this.networkConfig = getNetworkConfig(config); this.provider = sor.provider; - this.data = new Data(this.networkConfig, sor.provider); + this.data = new Data( + this.networkConfig, + sor.provider, + config.subgraphQuery + ); this.swaps = new Swaps(this.config); this.relayer = new Relayer(this.swaps); this.pricing = new Pricing(config, this.swaps); diff --git a/balancer-js/src/types.ts b/balancer-js/src/types.ts index 9b5131ae6..6cd132a68 100644 --- a/balancer-js/src/types.ts +++ b/balancer-js/src/types.ts @@ -35,6 +35,7 @@ export interface BalancerSdkConfig { rpcUrl: string; //overwrite the subgraph url if you don't want to use the balancer labs maintained version customSubgraphUrl?: string; + subgraphQuery?: GraphQLQuery; //optionally overwrite parts of the standard SOR config sor?: Partial<BalancerSdkSorConfig>; tenderly?: BalancerTenderlyConfig; From f01052debfd7fb4f690689aa5ef3a4951a71cb84 Mon Sep 17 00:00:00 2001 From: Bruno Eidam Guerios <bruno@balancer.finance> Date: Wed, 4 Jan 2023 14:24:16 -0300 Subject: [PATCH 08/25] Fix subgraph price repository test not passing --- balancer-js/src/modules/data/token-prices/subgraph.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/balancer-js/src/modules/data/token-prices/subgraph.spec.ts b/balancer-js/src/modules/data/token-prices/subgraph.spec.ts index b80053bb3..462d2980a 100644 --- a/balancer-js/src/modules/data/token-prices/subgraph.spec.ts +++ b/balancer-js/src/modules/data/token-prices/subgraph.spec.ts @@ -31,7 +31,7 @@ const mockedResponse = { const addresses = mockedResponse.data.tokens.map((t) => t.address); -const repository = new SubgraphPriceRepository(1); +const repository = new SubgraphPriceRepository(url, 1); describe('subgraph price repository', () => { let mock: MockAdapter; From 71f5c10889820ec5eab2459bd9cb877c2bdeae50 Mon Sep 17 00:00:00 2001 From: johngrantuk <johngrantuk@googlemail.com> Date: Thu, 5 Jan 2023 11:58:46 +0000 Subject: [PATCH 09/25] Reuse existing onchain methods. --- .../src/modules/data/pool/onChainData.ts | 277 ------------------ .../src/modules/data/pool/subgraphOnChain.ts | 2 +- .../src/modules/sor/pool-data/onChainData.ts | 18 +- 3 files changed, 11 insertions(+), 286 deletions(-) delete mode 100644 balancer-js/src/modules/data/pool/onChainData.ts diff --git a/balancer-js/src/modules/data/pool/onChainData.ts b/balancer-js/src/modules/data/pool/onChainData.ts deleted file mode 100644 index 484515d3c..000000000 --- a/balancer-js/src/modules/data/pool/onChainData.ts +++ /dev/null @@ -1,277 +0,0 @@ -import { formatFixed } from '@ethersproject/bignumber'; -import { Provider } from '@ethersproject/providers'; -import { PoolFilter } from '@balancer-labs/sor'; -import { Multicaller } from '@/lib/utils/multiCaller'; -import { isSameAddress } from '@/lib/utils'; -import { Vault__factory } from '@balancer-labs/typechain'; -import { Pool } from '@/types'; - -// TODO: decide whether we want to trim these ABIs down to the relevant functions -import aTokenRateProvider from '@/lib/abi/StaticATokenRateProvider.json'; -import weightedPoolAbi from '@/lib/abi/WeightedPool.json'; -import stablePoolAbi from '@/lib/abi/StablePool.json'; -import elementPoolAbi from '@/lib/abi/ConvergentCurvePool.json'; -import linearPoolAbi from '@/lib/abi/LinearPool.json'; -import composableStableAbi from '@/lib/abi/ComposableStable.json'; - -export async function getOnChainBalances( - subgraphPoolsOriginal: Pool[], - multiAddress: string, - vaultAddress: string, - provider: Provider -): Promise<Pool[]> { - if (subgraphPoolsOriginal.length === 0) return subgraphPoolsOriginal; - - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const abis: any = Object.values( - // Remove duplicate entries using their names - Object.fromEntries( - [ - ...Vault__factory.abi, - ...aTokenRateProvider, - ...weightedPoolAbi, - ...stablePoolAbi, - ...elementPoolAbi, - ...linearPoolAbi, - ...composableStableAbi, - ].map((row) => [row.name, row]) - ) - ); - - const multiPool = new Multicaller(multiAddress, provider, abis); - - const supportedPoolTypes: string[] = Object.values(PoolFilter); - const subgraphPools: Pool[] = []; - subgraphPoolsOriginal.forEach((pool) => { - if (!supportedPoolTypes.includes(pool.poolType)) { - console.error(`Unknown pool type: ${pool.poolType} ${pool.id}`); - return; - } - - subgraphPools.push(pool); - - multiPool.call(`${pool.id}.poolTokens`, vaultAddress, 'getPoolTokens', [ - pool.id, - ]); - multiPool.call(`${pool.id}.totalSupply`, pool.address, 'totalSupply'); - - // Pools with pre minted BPT - if (pool.poolType.includes('Linear') || pool.poolType === 'StablePhantom') { - multiPool.call( - `${pool.id}.virtualSupply`, - pool.address, - 'getVirtualSupply' - ); - } - - /** - * Returns the effective BPT supply. - * In other pools, this would be the same as `totalSupply`, but there are two key differences here: - * - this pool pre-mints BPT and holds it in the Vault as a token, and as such we need to subtract the Vault's - * balance to get the total "circulating supply". This is called the 'virtualSupply'. - * - the Pool owes debt to the Protocol in the form of unminted BPT, which will be minted immediately before the - * next join or exit. We need to take these into account since, even if they don't yet exist, they will - * effectively be included in any Pool operation that involves BPT. - * In the vast majority of cases, this function should be used instead of `totalSupply()`. - */ - if (pool.poolType === 'ComposableStable') - multiPool.call( - `${pool.id}.actualSupply`, - pool.address, - 'getActualSupply' - ); - - // TO DO - Make this part of class to make more flexible? - if ( - pool.poolType === 'Weighted' || - pool.poolType === 'LiquidityBootstrapping' || - pool.poolType === 'Investment' - ) { - multiPool.call( - `${pool.id}.weights`, - pool.address, - 'getNormalizedWeights' - ); - multiPool.call( - `${pool.id}.swapFee`, - pool.address, - 'getSwapFeePercentage' - ); - } else if ( - pool.poolType === 'Stable' || - pool.poolType === 'MetaStable' || - pool.poolType === 'StablePhantom' || - pool.poolType === 'ComposableStable' - ) { - // MetaStable & StablePhantom is the same as Stable for multicall purposes - multiPool.call( - `${pool.id}.amp`, - pool.address, - 'getAmplificationParameter' - ); - multiPool.call( - `${pool.id}.swapFee`, - pool.address, - 'getSwapFeePercentage' - ); - } else if (pool.poolType === 'Element') { - multiPool.call(`${pool.id}.swapFee`, pool.address, 'percentFee'); - } else if (pool.poolType.toString().includes('Linear')) { - multiPool.call( - `${pool.id}.swapFee`, - pool.address, - 'getSwapFeePercentage' - ); - - multiPool.call(`${pool.id}.targets`, pool.address, 'getTargets'); - multiPool.call(`${pool.id}.rate`, pool.address, 'getWrappedTokenRate'); - } else if (pool.poolType.toString().includes('Gyro')) { - multiPool.call( - `${pool.id}.swapFee`, - pool.address, - 'getSwapFeePercentage' - ); - } - }); - - let pools = {} as Record< - string, - { - amp?: string[]; - swapFee: string; - weights?: string[]; - targets?: string[]; - poolTokens: { - tokens: string[]; - balances: string[]; - }; - totalSupply: string; - virtualSupply?: string; - rate?: string; - actualSupply?: string; - } - >; - - try { - pools = (await multiPool.execute()) as Record< - string, - { - amp?: string[]; - swapFee: string; - weights?: string[]; - poolTokens: { - tokens: string[]; - balances: string[]; - }; - totalSupply: string; - virtualSupply?: string; - rate?: string; - actualSupply?: string; - } - >; - } catch (err) { - throw `Issue with multicall execution.`; - } - - const onChainPools: Pool[] = []; - - Object.entries(pools).forEach(([poolId, onchainData], index) => { - try { - const { - poolTokens, - swapFee, - weights, - totalSupply, - virtualSupply, - actualSupply, - } = onchainData; - - if ( - subgraphPools[index].poolType === 'Stable' || - subgraphPools[index].poolType === 'MetaStable' || - subgraphPools[index].poolType === 'StablePhantom' || - subgraphPools[index].poolType === 'ComposableStable' - ) { - if (!onchainData.amp) { - console.error(`Stable Pool Missing Amp: ${poolId}`); - return; - } else { - // Need to scale amp by precision to match expected Subgraph scale - // amp is stored with 3 decimals of precision - subgraphPools[index].amp = formatFixed(onchainData.amp[0], 3); - } - } - - if (subgraphPools[index].poolType.includes('Linear')) { - if (!onchainData.targets) { - console.error(`Linear Pool Missing Targets: ${poolId}`); - return; - } else { - subgraphPools[index].lowerTarget = formatFixed( - onchainData.targets[0], - 18 - ); - subgraphPools[index].upperTarget = formatFixed( - onchainData.targets[1], - 18 - ); - } - - const wrappedIndex = subgraphPools[index].wrappedIndex; - if (wrappedIndex === undefined || onchainData.rate === undefined) { - console.error( - `Linear Pool Missing WrappedIndex or PriceRate: ${poolId}` - ); - return; - } - // Update priceRate of wrappedToken - subgraphPools[index].tokens[wrappedIndex].priceRate = formatFixed( - onchainData.rate, - 18 - ); - } - - subgraphPools[index].swapFee = formatFixed(swapFee, 18); - - poolTokens.tokens.forEach((token, i) => { - const T = subgraphPools[index].tokens.find((t) => - isSameAddress(t.address, token) - ); - if (!T) throw `Pool Missing Expected Token: ${poolId} ${token}`; - T.balance = formatFixed(poolTokens.balances[i], T.decimals); - if (weights) { - // Only expected for WeightedPools - T.weight = formatFixed(weights[i], 18); - } - }); - - // Pools with pre minted BPT - if ( - subgraphPools[index].poolType.includes('Linear') || - subgraphPools[index].poolType === 'StablePhantom' - ) { - if (virtualSupply === undefined) { - console.error( - `Pool with pre-minted BPT missing Virtual Supply: ${poolId}` - ); - return; - } - subgraphPools[index].totalShares = formatFixed(virtualSupply, 18); - } else if (subgraphPools[index].poolType === 'ComposableStable') { - if (actualSupply === undefined) { - console.error(`ComposableStable missing Actual Supply: ${poolId}`); - return; - } - subgraphPools[index].totalShares = formatFixed(actualSupply, 18); - } else { - subgraphPools[index].totalShares = formatFixed(totalSupply, 18); - } - - onChainPools.push(subgraphPools[index]); - } catch (err) { - throw `Issue with pool onchain data: ${err}`; - } - }); - - return onChainPools; -} diff --git a/balancer-js/src/modules/data/pool/subgraphOnChain.ts b/balancer-js/src/modules/data/pool/subgraphOnChain.ts index dea17d097..bce6e0194 100644 --- a/balancer-js/src/modules/data/pool/subgraphOnChain.ts +++ b/balancer-js/src/modules/data/pool/subgraphOnChain.ts @@ -24,7 +24,7 @@ import { } from '@/types'; import { Network } from '@/lib/constants/network'; import { PoolsQueryVariables } from '../../subgraph/subgraph'; -import { getOnChainBalances } from './onChainData'; +import { getOnChainBalances } from '../../../modules/sor/pool-data/onChainData'; import { SubgraphSubPoolMeta, SubgraphSubPoolToken } from './subgraph'; interface PoolsSubgraphOnChainRepositoryOptions { diff --git a/balancer-js/src/modules/sor/pool-data/onChainData.ts b/balancer-js/src/modules/sor/pool-data/onChainData.ts index 7ad54f1f5..6bf623b9e 100644 --- a/balancer-js/src/modules/sor/pool-data/onChainData.ts +++ b/balancer-js/src/modules/sor/pool-data/onChainData.ts @@ -4,6 +4,7 @@ import { PoolFilter, SubgraphPoolBase } from '@balancer-labs/sor'; import { Multicaller } from '@/lib/utils/multiCaller'; import { isSameAddress } from '@/lib/utils'; import { Vault__factory } from '@balancer-labs/typechain'; +import { Pool } from '@/types'; // TODO: decide whether we want to trim these ABIs down to the relevant functions import aTokenRateProvider from '@/lib/abi/StaticATokenRateProvider.json'; @@ -13,12 +14,12 @@ import elementPoolAbi from '@/lib/abi/ConvergentCurvePool.json'; import linearPoolAbi from '@/lib/abi/LinearPool.json'; import composableStableAbi from '@/lib/abi/ComposableStable.json'; -export async function getOnChainBalances( - subgraphPoolsOriginal: SubgraphPoolBase[], +export async function getOnChainBalances<Type extends SubgraphPoolBase | Pool>( + subgraphPoolsOriginal: Type[], multiAddress: string, vaultAddress: string, provider: Provider -): Promise<SubgraphPoolBase[]> { +): Promise<Type[]> { if (subgraphPoolsOriginal.length === 0) return subgraphPoolsOriginal; // eslint-disable-next-line @typescript-eslint/no-explicit-any @@ -40,7 +41,7 @@ export async function getOnChainBalances( const multiPool = new Multicaller(multiAddress, provider, abis); const supportedPoolTypes: string[] = Object.values(PoolFilter); - const subgraphPools: SubgraphPoolBase[] = []; + const subgraphPools: Type[] = []; subgraphPoolsOriginal.forEach((pool) => { if (!supportedPoolTypes.includes(pool.poolType)) { console.error(`Unknown pool type: ${pool.poolType} ${pool.id}`); @@ -172,7 +173,7 @@ export async function getOnChainBalances( throw `Issue with multicall execution.`; } - const onChainPools: SubgraphPoolBase[] = []; + const onChainPools: Type[] = []; Object.entries(pools).forEach(([poolId, onchainData], index) => { try { @@ -233,9 +234,10 @@ export async function getOnChainBalances( subgraphPools[index].swapFee = formatFixed(swapFee, 18); poolTokens.tokens.forEach((token, i) => { - const T = subgraphPools[index].tokens.find((t) => - isSameAddress(t.address, token) - ); + // Looks like there's a TS issue that means find doesn't work on unions: https://stackoverflow.com/questions/58772314/typescript-array-prototype-map-has-error-expression-is-not-callable-when-th + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const tokens = subgraphPools[index].tokens as any[]; + const T = tokens.find((t) => isSameAddress(t.address, token)); if (!T) throw `Pool Missing Expected Token: ${poolId} ${token}`; T.balance = formatFixed(poolTokens.balances[i], T.decimals); if (weights) { From c30d5e747ef809b0dc36876db7126b152792a3ac Mon Sep 17 00:00:00 2001 From: johngrantuk <johngrantuk@googlemail.com> Date: Thu, 5 Jan 2023 13:42:23 +0000 Subject: [PATCH 10/25] Onchain SG repo reuses non-onchain functions. --- .../src/modules/data/pool/subgraphOnChain.ts | 223 ++---------------- 1 file changed, 20 insertions(+), 203 deletions(-) diff --git a/balancer-js/src/modules/data/pool/subgraphOnChain.ts b/balancer-js/src/modules/data/pool/subgraphOnChain.ts index bce6e0194..78bba7aa9 100644 --- a/balancer-js/src/modules/data/pool/subgraphOnChain.ts +++ b/balancer-js/src/modules/data/pool/subgraphOnChain.ts @@ -1,31 +1,10 @@ import { Findable, Searchable } from '../types'; -import { - createSubgraphClient, - SubgraphClient, - SubgraphPool, - Pool_OrderBy, - OrderDirection, - SubgraphPoolTokenFragment, -} from '@/modules/subgraph/subgraph'; -import { - GraphQLArgsBuilder, - SubgraphArgsFormatter, -} from '@/lib/graphql/args-builder'; -import { GraphQLArgs } from '@/lib/graphql/types'; import { Provider } from '@ethersproject/providers'; import { PoolAttribute, PoolsRepositoryFetchOptions } from './types'; -import { - GraphQLQuery, - Pool, - PoolType, - PoolToken, - SubPool, - SubPoolMeta, -} from '@/types'; +import { GraphQLQuery, Pool } from '@/types'; import { Network } from '@/lib/constants/network'; -import { PoolsQueryVariables } from '../../subgraph/subgraph'; import { getOnChainBalances } from '../../../modules/sor/pool-data/onChainData'; -import { SubgraphSubPoolMeta, SubgraphSubPoolToken } from './subgraph'; +import { PoolsSubgraphRepository } from './subgraph'; interface PoolsSubgraphOnChainRepositoryOptions { url: string; @@ -38,25 +17,20 @@ interface PoolsSubgraphOnChainRepositoryOptions { } /** - * Access pools using generated subgraph client. - * - * Balancer's subgraph URL: https://thegraph.com/hosted-service/subgraph/balancer-labs/balancer-v2 + * Access pools using generated subgraph client and multicall. */ export class PoolsSubgraphOnChainRepository implements Findable<Pool, PoolAttribute>, Searchable<Pool> { - private client: SubgraphClient; - private chainId: Network; + private poolsSubgraph: PoolsSubgraphRepository; private provider: Provider; private pools?: Promise<Pool[]>; private multicall: string; private vault: string; public skip = 0; - private blockHeight: undefined | (() => Promise<number | undefined>); - private query: GraphQLQuery; /** - * Repository with optional lazy loaded blockHeight + * Repository using multicall to get onchain data. * * @param url subgraph URL * @param chainId current network, needed for L2s logic @@ -65,33 +39,15 @@ export class PoolsSubgraphOnChainRepository * @param valt vault address */ constructor(options: PoolsSubgraphOnChainRepositoryOptions) { - this.client = createSubgraphClient(options.url); - this.blockHeight = options.blockHeight; - this.chainId = options.chainId; + this.poolsSubgraph = new PoolsSubgraphRepository({ + url: options.url, + chainId: options.chainId, + blockHeight: options.blockHeight, + query: options.query, + }); this.provider = options.provider; this.multicall = options.multicall; this.vault = options.vault; - - const defaultArgs: GraphQLArgs = { - orderBy: Pool_OrderBy.TotalLiquidity, - orderDirection: OrderDirection.Desc, - where: { - swapEnabled: { - eq: true, - }, - totalShares: { - gt: 0.000000000001, - }, - }, - }; - - const args = options.query?.args || defaultArgs; - const attrs = options.query?.attrs || {}; - - this.query = { - args, - attrs, - }; } /** @@ -102,12 +58,7 @@ export class PoolsSubgraphOnChainRepository */ private async fetchDefault(): Promise<Pool[]> { console.time('fetching pools'); - const { pool0, pool1000, pool2000 } = await this.client.AllPools( - await this.getDefaultFilter(this.query.args) - ); - const pools = [...pool0, ...pool1000, ...pool2000].map( - this.mapType.bind(this) - ); + const pools = await this.poolsSubgraph.fetch(); console.timeEnd('fetching pools'); console.log(pools.length, 'Example filter should limit the pools length'); console.log('Fetching onchain!'); @@ -122,24 +73,14 @@ export class PoolsSubgraphOnChainRepository } async fetch(options?: PoolsRepositoryFetchOptions): Promise<Pool[]> { - if (options?.skip) { - this.query.args.skip = options.skip; - } - if (!this.query.args.block) { - this.query.args.block = await this.block(); - } - - this.query.args.first = options?.first || 1000; - - const formattedQuery = new GraphQLArgsBuilder(this.query.args).format( - new SubgraphArgsFormatter() - ) as PoolsQueryVariables; - - const { pools } = await this.client.Pools(formattedQuery); - - this.skip = (options?.skip || 0) + pools.length; - - return pools.map(this.mapType.bind(this)); + const pools = await this.poolsSubgraph.fetch(options); + const onchainPools = await getOnChainBalances( + pools, + this.multicall, + this.vault, + this.provider + ); + return onchainPools; } async find(id: string): Promise<Pool | undefined> { @@ -152,25 +93,6 @@ export class PoolsSubgraphOnChainRepository } return (await this.pools).find((pool) => pool[param] == value); - - // TODO: @Nma - Fetching pools outside of default query is causing a lot of requests - // on a frontend, because results aren't cached anywhere. - // For fetching pools directly from subgraph with custom queries please use the client not this repository. - // Code below kept for reference, to be removed later. - // - // if (this.pools) { - // return (await this.pools).find((p) => p[param] === value); - // } - // const { pools } = await this.client.Pools({ - // where: { - // [param]: value, - // swapEnabled: true, - // totalShares_gt: '0.000000000001', - // }, - // block: await this.block(), - // }); - // const poolsTab: Pool[] = pools.map(this.mapType.bind(this)); - // return poolsTab.length > 0 ? poolsTab[0] : undefined; } async all(): Promise<Pool[]> { @@ -180,20 +102,6 @@ export class PoolsSubgraphOnChainRepository return this.pools; } - async block(): Promise<{ number: number | undefined } | undefined> { - return this.blockHeight ? { number: await this.blockHeight() } : undefined; - } - - async getDefaultFilter(args: GraphQLArgs): Promise<PoolsQueryVariables> { - const formattedQuery = new GraphQLArgsBuilder(args).format( - new SubgraphArgsFormatter() - ) as PoolsQueryVariables; - if (!formattedQuery.block) { - formattedQuery.block = await this.block(); - } - return formattedQuery; - } - async where(filter: (pool: Pool) => boolean): Promise<Pool[]> { if (!this.pools) { this.pools = this.fetchDefault(); @@ -201,95 +109,4 @@ export class PoolsSubgraphOnChainRepository return (await this.pools).filter(filter); } - - private mapType(subgraphPool: SubgraphPool): Pool { - return { - id: subgraphPool.id, - name: subgraphPool.name || '', - address: subgraphPool.address, - chainId: this.chainId, - poolType: subgraphPool.poolType as PoolType, - poolTypeVersion: subgraphPool.poolTypeVersion || 1, - swapFee: subgraphPool.swapFee, - swapEnabled: subgraphPool.swapEnabled, - protocolYieldFeeCache: subgraphPool.protocolYieldFeeCache || '0', - amp: subgraphPool.amp ?? undefined, - owner: subgraphPool.owner ?? undefined, - factory: subgraphPool.factory ?? undefined, - symbol: subgraphPool.symbol ?? undefined, - tokens: (subgraphPool.tokens || []).map(this.mapToken.bind(this)), - tokensList: subgraphPool.tokensList, - tokenAddresses: (subgraphPool.tokens || []).map((t) => t.address), - totalLiquidity: subgraphPool.totalLiquidity, - totalShares: subgraphPool.totalShares, - totalSwapFee: subgraphPool.totalSwapFee, - totalSwapVolume: subgraphPool.totalSwapVolume, - priceRateProviders: subgraphPool.priceRateProviders ?? undefined, - // onchain: subgraphPool.onchain, - createTime: subgraphPool.createTime, - mainIndex: subgraphPool.mainIndex ?? undefined, - wrappedIndex: subgraphPool.wrappedIndex ?? undefined, - // mainTokens: subgraphPool.mainTokens, - // wrappedTokens: subgraphPool.wrappedTokens, - // unwrappedTokens: subgraphPool.unwrappedTokens, - // isNew: subgraphPool.isNew, - // volumeSnapshot: subgraphPool.volumeSnapshot, - // feesSnapshot: subgraphPool.???, // Approximated last 24h fees - // boost: subgraphPool.boost, - totalWeight: subgraphPool.totalWeight || '1', - lowerTarget: subgraphPool.lowerTarget ?? '0', - upperTarget: subgraphPool.upperTarget ?? '0', - }; - } - - private mapToken(subgraphToken: SubgraphPoolTokenFragment): PoolToken { - const subPoolInfo = this.mapSubPools( - // need to typecast as the fragment is 3 layers deep while the type is infinite levels deep - subgraphToken.token as SubgraphSubPoolMeta - ); - return { - ...subgraphToken, - isExemptFromYieldProtocolFee: - subgraphToken.isExemptFromYieldProtocolFee || false, - token: subPoolInfo, - }; - } - - private mapSubPools(metadata: SubgraphSubPoolMeta): SubPoolMeta { - let subPool: SubPool | null = null; - if (metadata.pool) { - subPool = { - id: metadata.pool.id, - address: metadata.pool.address, - totalShares: metadata.pool.totalShares, - poolType: metadata.pool.poolType as PoolType, - mainIndex: metadata.pool.mainIndex || 0, - }; - - if (metadata?.pool.tokens) { - subPool.tokens = metadata.pool.tokens.map( - this.mapSubPoolToken.bind(this) - ); - } - } - - return { - pool: subPool, - latestUSDPrice: metadata.latestUSDPrice || undefined, - }; - } - - private mapSubPoolToken(token: SubgraphSubPoolToken) { - return { - address: token.address, - decimals: token.decimals, - symbol: token.symbol, - balance: token.balance, - priceRate: token.priceRate, - weight: token.weight, - isExemptFromYieldProtocolFee: - token.isExemptFromYieldProtocolFee || undefined, - token: token.token ? this.mapSubPools(token.token) : undefined, - }; - } } From aeb7d691becaffad0d6df271604190e27f99e61d Mon Sep 17 00:00:00 2001 From: johngrantuk <johngrantuk@googlemail.com> Date: Thu, 5 Jan 2023 14:34:27 +0000 Subject: [PATCH 11/25] Remove SP default flag. --- .../src/modules/data/pool/subgraphOnChain.ts | 7 ++++--- balancer-js/src/modules/graph/graph.ts | 3 +-- balancer-js/src/modules/joins/joins.module.ts | 6 ------ balancer-js/src/modules/pools/index.ts | 9 ++------- .../concerns/linear/spotPrice.concern.ts | 18 +++++------------- .../modules/pools/pool-types/concerns/types.ts | 7 +------ balancer-js/src/types.ts | 6 +----- 7 files changed, 14 insertions(+), 42 deletions(-) diff --git a/balancer-js/src/modules/data/pool/subgraphOnChain.ts b/balancer-js/src/modules/data/pool/subgraphOnChain.ts index 78bba7aa9..a331c79a3 100644 --- a/balancer-js/src/modules/data/pool/subgraphOnChain.ts +++ b/balancer-js/src/modules/data/pool/subgraphOnChain.ts @@ -57,17 +57,18 @@ export class PoolsSubgraphOnChainRepository * @returns Promise resolving to pools list */ private async fetchDefault(): Promise<Pool[]> { - console.time('fetching pools'); + console.time('fetching pools SG'); const pools = await this.poolsSubgraph.fetch(); - console.timeEnd('fetching pools'); + console.timeEnd('fetching pools SG'); console.log(pools.length, 'Example filter should limit the pools length'); - console.log('Fetching onchain!'); + console.time('fetching pools onchain'); const onchainPools = await getOnChainBalances( pools, this.multicall, this.vault, this.provider ); + console.timeEnd('fetching pools onchain'); return onchainPools; } diff --git a/balancer-js/src/modules/graph/graph.ts b/balancer-js/src/modules/graph/graph.ts index b19a8c629..c6a33aded 100644 --- a/balancer-js/src/modules/graph/graph.ts +++ b/balancer-js/src/modules/graph/graph.ts @@ -149,8 +149,7 @@ export class PoolGraph { const sp = spotPriceCalculator.calcPoolSpotPrice( token.address, pool.address, - pool, - false + pool ); spotPrices[token.address] = sp; }); diff --git a/balancer-js/src/modules/joins/joins.module.ts b/balancer-js/src/modules/joins/joins.module.ts index 05d7bd9b2..2b1abc58c 100644 --- a/balancer-js/src/modules/joins/joins.module.ts +++ b/balancer-js/src/modules/joins/joins.module.ts @@ -409,10 +409,6 @@ export class Join { parentNode.joinAction === 'joinPool' ) { const sp = parentNode.spotPrices[childAddress.toLowerCase()]; - console.log( - `Spot price ${childAddress.toLowerCase()}: `, - sp.toString() - ); spProduct = spProduct * parseFloat(sp); childAddress = parentNode.address; } @@ -425,8 +421,6 @@ export class Join { inputAmountScaled, spPriceScaled.toBigInt() ); - console.log('sp product: ', spProduct.toString()); - console.log('zeroPriceImpact amount for path: ', bptOut.toString(), '\n'); return bptOut; }; diff --git a/balancer-js/src/modules/pools/index.ts b/balancer-js/src/modules/pools/index.ts index de5f55498..c84ceadad 100644 --- a/balancer-js/src/modules/pools/index.ts +++ b/balancer-js/src/modules/pools/index.ts @@ -266,16 +266,11 @@ export class Pools implements Findable<PoolWithMethods> { // either we refetch or it needs a type transformation from SDK internal to SOR (subgraph) // spotPrice: async (tokenIn: string, tokenOut: string) => // methods.spotPriceCalculator.calcPoolSpotPrice(tokenIn, tokenOut, data), - calcSpotPrice: ( - tokenIn: string, - tokenOut: string, - isDefault?: boolean - ) => + calcSpotPrice: (tokenIn: string, tokenOut: string) => concerns.spotPriceCalculator.calcPoolSpotPrice( tokenIn, tokenOut, - pool, - isDefault + pool ), }; } catch (error) { diff --git a/balancer-js/src/modules/pools/pool-types/concerns/linear/spotPrice.concern.ts b/balancer-js/src/modules/pools/pool-types/concerns/linear/spotPrice.concern.ts index 8ff606a9e..0848123db 100644 --- a/balancer-js/src/modules/pools/pool-types/concerns/linear/spotPrice.concern.ts +++ b/balancer-js/src/modules/pools/pool-types/concerns/linear/spotPrice.concern.ts @@ -3,19 +3,11 @@ import { SubgraphPoolBase, LinearPool, ZERO } from '@balancer-labs/sor'; import { Pool } from '@/types'; export class LinearPoolSpotPrice implements SpotPriceConcern { - calcPoolSpotPrice( - tokenIn: string, - tokenOut: string, - pool: Pool, - isDefault = false - ): string { + calcPoolSpotPrice(tokenIn: string, tokenOut: string, pool: Pool): string { const linearPool = LinearPool.fromPool(pool as SubgraphPoolBase); - if (isDefault) return '1'; - else { - const poolPairData = linearPool.parsePoolPairData(tokenIn, tokenOut); - return linearPool - ._spotPriceAfterSwapExactTokenInForTokenOut(poolPairData, ZERO) - .toString(); - } + const poolPairData = linearPool.parsePoolPairData(tokenIn, tokenOut); + return linearPool + ._spotPriceAfterSwapExactTokenInForTokenOut(poolPairData, ZERO) + .toString(); } } diff --git a/balancer-js/src/modules/pools/pool-types/concerns/types.ts b/balancer-js/src/modules/pools/pool-types/concerns/types.ts index abcc52bab..016ce5530 100644 --- a/balancer-js/src/modules/pools/pool-types/concerns/types.ts +++ b/balancer-js/src/modules/pools/pool-types/concerns/types.ts @@ -7,12 +7,7 @@ export interface LiquidityConcern { } export interface SpotPriceConcern { - calcPoolSpotPrice: ( - tokenIn: string, - tokenOut: string, - pool: Pool, - isDefault?: boolean - ) => string; + calcPoolSpotPrice: (tokenIn: string, tokenOut: string, pool: Pool) => string; } export interface PriceImpactConcern { diff --git a/balancer-js/src/types.ts b/balancer-js/src/types.ts index 6cd132a68..3902cbcac 100644 --- a/balancer-js/src/types.ts +++ b/balancer-js/src/types.ts @@ -337,11 +337,7 @@ export interface PoolWithMethods extends Pool, Queries.ParamsBuilder { amountsOut: string[], slippage: string ) => ExitPoolAttributes; - calcSpotPrice: ( - tokenIn: string, - tokenOut: string, - isDefault?: boolean - ) => string; + calcSpotPrice: (tokenIn: string, tokenOut: string) => string; } export interface GraphQLQuery { From c46f4b7c16212ef83d193690911808e860ac34a7 Mon Sep 17 00:00:00 2001 From: johngrantuk <johngrantuk@googlemail.com> Date: Thu, 5 Jan 2023 15:21:29 +0000 Subject: [PATCH 12/25] Handle PI<0. Remove debug logs and tests. --- balancer-js/package.json | 2 +- .../src/modules/data/pool/subgraphOnChain.ts | 1 - .../joins/debug.module.integration.spec.ts | 266 ------------------ balancer-js/src/modules/joins/joins.module.ts | 2 - .../src/modules/pricing/priceImpact.ts | 6 +- balancer-js/yarn.lock | 6 +- 6 files changed, 8 insertions(+), 275 deletions(-) delete mode 100644 balancer-js/src/modules/joins/debug.module.integration.spec.ts diff --git a/balancer-js/package.json b/balancer-js/package.json index 81ca855b7..0fc284998 100644 --- a/balancer-js/package.json +++ b/balancer-js/package.json @@ -89,7 +89,7 @@ "typescript": "^4.0.2" }, "dependencies": { - "@balancer-labs/sor": "/Users/jg/Documents/balancer-sor/balancer-labs-sor-v4.0.1-beta.22.tgz", + "@balancer-labs/sor": "/Users/jg/Documents/balancer-sor/balancer-labs-sor-v4.0.1-beta.16010.tgz", "@balancer-labs/typechain": "^1.0.0", "axios": "^0.24.0", "graphql": "^15.6.1", diff --git a/balancer-js/src/modules/data/pool/subgraphOnChain.ts b/balancer-js/src/modules/data/pool/subgraphOnChain.ts index a331c79a3..be7037313 100644 --- a/balancer-js/src/modules/data/pool/subgraphOnChain.ts +++ b/balancer-js/src/modules/data/pool/subgraphOnChain.ts @@ -60,7 +60,6 @@ export class PoolsSubgraphOnChainRepository console.time('fetching pools SG'); const pools = await this.poolsSubgraph.fetch(); console.timeEnd('fetching pools SG'); - console.log(pools.length, 'Example filter should limit the pools length'); console.time('fetching pools onchain'); const onchainPools = await getOnChainBalances( pools, diff --git a/balancer-js/src/modules/joins/debug.module.integration.spec.ts b/balancer-js/src/modules/joins/debug.module.integration.spec.ts deleted file mode 100644 index c2a43a242..000000000 --- a/balancer-js/src/modules/joins/debug.module.integration.spec.ts +++ /dev/null @@ -1,266 +0,0 @@ -// yarn test:only ./src/modules/joins/debug.module.integration.spec.ts -import dotenv from 'dotenv'; -import { expect } from 'chai'; -import hardhat from 'hardhat'; - -import { BalancerSDK, BalancerTenderlyConfig, Network, GraphQLArgs } from '@/.'; -import { BigNumber, formatFixed, parseFixed } from '@ethersproject/bignumber'; -import { Contracts } from '@/modules/contracts/contracts.module'; -import { forkSetup, getBalances } from '@/test/lib/utils'; -import { ADDRESSES } from '@/test/lib/constants'; -import { Relayer } from '@/modules/relayer/relayer.module'; -import { JsonRpcSigner } from '@ethersproject/providers'; -import { SolidityMaths } from '@/lib/utils/solidityMaths'; - -dotenv.config(); - -const TEST_BOOSTED = true; - -/* - * Testing on MAINNET - * - Update hardhat.config.js with chainId = 1 - * - Update ALCHEMY_URL on .env with a mainnet api key - * - Run node on terminal: yarn run node - * - Uncomment section below: - */ -const network = Network.MAINNET; -const blockNumber = 16176441; -const customSubgraphUrl = - 'https://api.thegraph.com/subgraphs/name/balancer-labs/balancer-v2'; -const { ALCHEMY_URL: jsonRpcUrl } = process.env; -const rpcUrl = 'http://127.0.0.1:8545'; - -const { TENDERLY_ACCESS_KEY, TENDERLY_USER, TENDERLY_PROJECT } = process.env; -const { ethers } = hardhat; -const MAX_GAS_LIMIT = 8e6; - -const tenderlyConfig: BalancerTenderlyConfig = { - accessKey: TENDERLY_ACCESS_KEY as string, - user: TENDERLY_USER as string, - project: TENDERLY_PROJECT as string, - blockNumber, -}; - -const subgraphArgs: GraphQLArgs = { - where: { - swapEnabled: { - eq: true, - }, - totalShares: { - gt: 0.000000000001, - }, - id: { - in: [ - '0xa13a9247ea42d743238089903570127dda72fe4400000000000000000000035d', - '0x2F4EB100552EF93840D5ADC30560E5513DFFFACB000000000000000000000334'.toLowerCase(), - '0xAE37D54AE477268B9997D4161B96B8200755935C000000000000000000000337'.toLowerCase(), - '0x82698AECC9E28E9BB27608BD52CF57F704BD1B83000000000000000000000336'.toLowerCase(), - ], - }, - }, - orderBy: 'totalLiquidity', - orderDirection: 'desc', - block: { number: 16176441 }, -}; -const subgraphQuery = { args: subgraphArgs, attrs: {} }; - -const sdk = new BalancerSDK({ - network, - rpcUrl, - customSubgraphUrl, - tenderly: tenderlyConfig, - subgraphQuery, -}); -const { pools } = sdk; -const provider = new ethers.providers.JsonRpcProvider(rpcUrl, network); -const signer = provider.getSigner(); -const { contracts, contractAddresses } = new Contracts( - network as number, - provider -); -const relayer = contractAddresses.relayerV4 as string; -const addresses = ADDRESSES[network]; - -interface Test { - signer: JsonRpcSigner; - description: string; - pool: { - id: string; - address: string; - }; - tokensIn: string[]; - amountsIn: string[]; - authorisation: string | undefined; - wrapMainTokens: boolean; -} - -const runTests = async (tests: Test[]) => { - for (let i = 0; i < tests.length; i++) { - const test = tests[i]; - it(test.description, async () => { - const userAddress = await test.signer.getAddress(); - const authorisation = await Relayer.signRelayerApproval( - relayer, - userAddress, - signer, - contracts.vault - ); - await testFlow( - userAddress, - test.pool, - test.tokensIn, - test.amountsIn, - test.wrapMainTokens, - authorisation - ); - }).timeout(120000); - } -}; - -const testFlow = async ( - userAddress: string, - pool: { id: string; address: string }, - tokensIn: string[], - amountsIn: string[], - wrapMainTokens: boolean, - authorisation: string | undefined -) => { - const [bptBalanceBefore, ...tokensInBalanceBefore] = await getBalances( - [pool.address, ...tokensIn], - signer, - userAddress - ); - - const gasLimit = MAX_GAS_LIMIT; - const slippage = '10'; // 10 bps = 0.1% - - const query = await pools.generalisedJoin( - pool.id, - tokensIn, - amountsIn, - userAddress, - wrapMainTokens, - slippage, - authorisation - ); - - console.log(query.priceImpact, 'priceImpact Raw'); - const piPercent = SolidityMaths.mulDownFixed( - BigInt(query.priceImpact), - BigInt('100000000000000000000') - ); - console.log(formatFixed(piPercent, 18), 'priceImpact %'); - - const response = await signer.sendTransaction({ - to: query.to, - data: query.callData, - gasLimit, - }); - - const receipt = await response.wait(); - console.log('Gas used', receipt.gasUsed.toString()); - - const [bptBalanceAfter, ...tokensInBalanceAfter] = await getBalances( - [pool.address, ...tokensIn], - signer, - userAddress - ); - expect(receipt.status).to.eql(1); - expect(BigNumber.from(query.minOut).gte('0')).to.be.true; - expect(BigNumber.from(query.expectedOut).gt(query.minOut)).to.be.true; - tokensInBalanceAfter.forEach((balanceAfter, i) => { - expect(balanceAfter.toString()).to.eq( - tokensInBalanceBefore[i].sub(amountsIn[i]).toString() - ); - }); - expect(bptBalanceBefore.eq(0)).to.be.true; - expect(bptBalanceAfter.gte(query.minOut)).to.be.true; - console.log(bptBalanceAfter.toString(), 'bpt after'); - console.log(query.minOut, 'minOut'); - console.log(query.expectedOut, 'expectedOut'); -}; - -describe('generalised join execution', async () => { - context('bbausd', async () => { - if (!TEST_BOOSTED) return true; - let authorisation: string | undefined; - beforeEach(async () => { - const tokens = [ - addresses.USDC.address, - addresses.DAI.address, - addresses.USDT.address, - ]; - const slots = [ - addresses.USDC.slot, - addresses.DAI.slot, - addresses.USDT.slot, - ]; - const balances = [ - parseFixed('1000000', 6).toString(), - parseFixed('1000000', 18).toString(), - parseFixed('1000000', 6).toString(), - ]; - await forkSetup( - signer, - tokens, - slots, - balances, - jsonRpcUrl as string, - blockNumber - ); - }); - - await runTests([ - { - signer, - description: 'join with leaf tokens', - pool: { - id: addresses.bbausd2.id, - address: addresses.bbausd2.address, - }, - tokensIn: [ - addresses.USDC.address, - addresses.DAI.address, - addresses.USDT.address, - ], - amountsIn: [ - parseFixed('10', 6).toString(), - parseFixed('10', 18).toString(), - parseFixed('10', 6).toString(), - ], - authorisation: authorisation, - wrapMainTokens: false, - }, - { - signer, - description: 'join with leaf tokens', - pool: { - id: addresses.bbausd2.id, - address: addresses.bbausd2.address, - }, - tokensIn: [addresses.USDC.address, addresses.DAI.address], - amountsIn: [ - parseFixed('0.1', 6).toString(), - parseFixed('0.001', 18).toString(), - ], - authorisation: authorisation, - wrapMainTokens: false, - }, - { - signer, - description: 'join with leaf tokens', - pool: { - id: addresses.bbausd2.id, - address: addresses.bbausd2.address, - }, - tokensIn: [addresses.USDC.address, addresses.DAI.address], - amountsIn: [ - parseFixed('900000', 6).toString(), - parseFixed('0.001', 18).toString(), - ], - authorisation: authorisation, - wrapMainTokens: false, - }, - ]); - }); -}); diff --git a/balancer-js/src/modules/joins/joins.module.ts b/balancer-js/src/modules/joins/joins.module.ts index 2b1abc58c..7feba4fa0 100644 --- a/balancer-js/src/modules/joins/joins.module.ts +++ b/balancer-js/src/modules/joins/joins.module.ts @@ -121,8 +121,6 @@ export class Join { true ).toString(); - console.log(totalAmountOut, `totalAmountOut`); - console.log(totalBptZeroPi.toString(), `totalBptZeroPi`); // Create calls with minAmountsOut const { callData, deltas } = await this.createCalls( joinPaths, diff --git a/balancer-js/src/modules/pricing/priceImpact.ts b/balancer-js/src/modules/pricing/priceImpact.ts index d28c125a1..eb55c09ff 100644 --- a/balancer-js/src/modules/pricing/priceImpact.ts +++ b/balancer-js/src/modules/pricing/priceImpact.ts @@ -14,12 +14,14 @@ function calcPriceImpactJoin( bptZeroPriceImpact: bigint ): bigint { // 1 - (bptAmount/bptZeroPI) - return ONE - SolidityMaths.divDownFixed(bptAmount, bptZeroPriceImpact); + const pi = ONE - SolidityMaths.divDownFixed(bptAmount, bptZeroPriceImpact); + return pi < 0 ? BigInt(0) : pi; } function calcPriceImpactExit( bptAmount: bigint, bptZeroPriceImpact: bigint ): bigint { // (bptAmount/bptZeroPI) - 1 - return SolidityMaths.divDownFixed(bptAmount, bptZeroPriceImpact) - ONE; + const pi = SolidityMaths.divDownFixed(bptAmount, bptZeroPriceImpact) - ONE; + return pi < 0 ? BigInt(0) : pi; } diff --git a/balancer-js/yarn.lock b/balancer-js/yarn.lock index 4b34752df..e494689e2 100644 --- a/balancer-js/yarn.lock +++ b/balancer-js/yarn.lock @@ -503,9 +503,9 @@ "@babel/helper-validator-identifier" "^7.19.1" to-fast-properties "^2.0.0" -"@balancer-labs/sor@/Users/jg/Documents/balancer-sor/balancer-labs-sor-v4.0.1-beta.22.tgz": - version "4.0.1-beta.22" - resolved "/Users/jg/Documents/balancer-sor/balancer-labs-sor-v4.0.1-beta.22.tgz#d159ee89feb9cbf01b4807fe402334f8a3b6dc89" +"@balancer-labs/sor@/Users/jg/Documents/balancer-sor/balancer-labs-sor-v4.0.1-beta.16010.tgz": + version "4.0.1-beta.16010" + resolved "/Users/jg/Documents/balancer-sor/balancer-labs-sor-v4.0.1-beta.16010.tgz#bbcef0e873f7662f76e9e0aaf3d17ef1202c4795" dependencies: isomorphic-fetch "^2.2.1" From 79181828f33212e363e95c1412344694f8c0f4e2 Mon Sep 17 00:00:00 2001 From: johngrantuk <johngrantuk@googlemail.com> Date: Thu, 5 Jan 2023 15:22:48 +0000 Subject: [PATCH 13/25] Update SOR to 4.0.1-beta.17. --- balancer-js/package.json | 2 +- balancer-js/yarn.lock | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/balancer-js/package.json b/balancer-js/package.json index 0fc284998..02230b322 100644 --- a/balancer-js/package.json +++ b/balancer-js/package.json @@ -89,7 +89,7 @@ "typescript": "^4.0.2" }, "dependencies": { - "@balancer-labs/sor": "/Users/jg/Documents/balancer-sor/balancer-labs-sor-v4.0.1-beta.16010.tgz", + "@balancer-labs/sor": "^4.0.1-beta.17", "@balancer-labs/typechain": "^1.0.0", "axios": "^0.24.0", "graphql": "^15.6.1", diff --git a/balancer-js/yarn.lock b/balancer-js/yarn.lock index e494689e2..fdfd4f10e 100644 --- a/balancer-js/yarn.lock +++ b/balancer-js/yarn.lock @@ -503,9 +503,10 @@ "@babel/helper-validator-identifier" "^7.19.1" to-fast-properties "^2.0.0" -"@balancer-labs/sor@/Users/jg/Documents/balancer-sor/balancer-labs-sor-v4.0.1-beta.16010.tgz": - version "4.0.1-beta.16010" - resolved "/Users/jg/Documents/balancer-sor/balancer-labs-sor-v4.0.1-beta.16010.tgz#bbcef0e873f7662f76e9e0aaf3d17ef1202c4795" +"@balancer-labs/sor@^4.0.1-beta.17": + version "4.0.1-beta.17" + resolved "https://registry.yarnpkg.com/@balancer-labs/sor/-/sor-4.0.1-beta.17.tgz#b78d67ba4107d4ec366015532d34b5c7af3b0169" + integrity sha512-KSkPs8Rxn/MPLNeO51uQWffvCJCjLjZCSFpBrWrSpC0lY536sfmmtmt89GF2Ocin8cnZg01UEg/0JYGOnMj1Og== dependencies: isomorphic-fetch "^2.2.1" From 8f1ba8df830ec452eea97ac627d931ea126a799c Mon Sep 17 00:00:00 2001 From: johngrantuk <johngrantuk@googlemail.com> Date: Thu, 5 Jan 2023 15:25:52 +0000 Subject: [PATCH 14/25] Remove unnecessary exports. --- balancer-js/src/modules/data/pool/subgraph.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/balancer-js/src/modules/data/pool/subgraph.ts b/balancer-js/src/modules/data/pool/subgraph.ts index 18ad56425..d54613f00 100644 --- a/balancer-js/src/modules/data/pool/subgraph.ts +++ b/balancer-js/src/modules/data/pool/subgraph.ts @@ -33,11 +33,11 @@ interface PoolsSubgraphRepositoryOptions { query?: GraphQLQuery; } -export interface SubgraphSubPoolToken extends SubgraphSubPoolTokenFragment { +interface SubgraphSubPoolToken extends SubgraphSubPoolTokenFragment { token?: SubgraphSubPoolMeta | null; } -export interface SubgraphSubPoolMeta { +interface SubgraphSubPoolMeta { latestUSDPrice?: string | null; pool?: SubgraphSubPool | null; } From b72f6b5ec0b854fdc851b602a8c2090e838a4986 Mon Sep 17 00:00:00 2001 From: johngrantuk <johngrantuk@googlemail.com> Date: Thu, 5 Jan 2023 15:29:10 +0000 Subject: [PATCH 15/25] Update default args totalShares to filter more. --- balancer-js/src/modules/data/pool/subgraph.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/balancer-js/src/modules/data/pool/subgraph.ts b/balancer-js/src/modules/data/pool/subgraph.ts index d54613f00..a03e0893d 100644 --- a/balancer-js/src/modules/data/pool/subgraph.ts +++ b/balancer-js/src/modules/data/pool/subgraph.ts @@ -81,7 +81,7 @@ export class PoolsSubgraphRepository eq: true, }, totalShares: { - gt: 0, + gt: 0.000000000001, }, }, }; From 272a9949b22978e70be3dabf1e0603ea77a73009 Mon Sep 17 00:00:00 2001 From: johngrantuk <johngrantuk@googlemail.com> Date: Thu, 5 Jan 2023 15:30:53 +0000 Subject: [PATCH 16/25] Add comments for pool filtering. --- balancer-js/src/modules/exits/exits.module.integration.spec.ts | 1 + balancer-js/src/modules/joins/joins.module.integration.spec.ts | 1 + 2 files changed, 2 insertions(+) diff --git a/balancer-js/src/modules/exits/exits.module.integration.spec.ts b/balancer-js/src/modules/exits/exits.module.integration.spec.ts index 4a5564b97..ca58becb3 100644 --- a/balancer-js/src/modules/exits/exits.module.integration.spec.ts +++ b/balancer-js/src/modules/exits/exits.module.integration.spec.ts @@ -66,6 +66,7 @@ const tenderlyConfig: BalancerTenderlyConfig = { blockNumber, }; +// This filters to pool addresses of interest to avoid too many onchain calls during tests const poolAddresses = Object.values(addresses).map( (address) => address.address ); diff --git a/balancer-js/src/modules/joins/joins.module.integration.spec.ts b/balancer-js/src/modules/joins/joins.module.integration.spec.ts index 3c20f706e..98a8b97a5 100644 --- a/balancer-js/src/modules/joins/joins.module.integration.spec.ts +++ b/balancer-js/src/modules/joins/joins.module.integration.spec.ts @@ -70,6 +70,7 @@ const tenderlyConfig: BalancerTenderlyConfig = { blockNumber, }; +// This filters to pool addresses of interest to avoid too many onchain calls during tests const poolAddresses = Object.values(addresses).map( (address) => address.address ); From b5a67ac187447b560b28ad7437556296cf4ae88e Mon Sep 17 00:00:00 2001 From: johngrantuk <johngrantuk@googlemail.com> Date: Mon, 9 Jan 2023 10:56:21 +0000 Subject: [PATCH 17/25] Move peer dependencies into dependencies to improve dev UX. --- balancer-js/package.json | 26 ++++++++------------------ balancer-js/rollup.config.ts | 3 +-- 2 files changed, 9 insertions(+), 20 deletions(-) diff --git a/balancer-js/package.json b/balancer-js/package.json index c7f62cb6f..694d7a367 100644 --- a/balancer-js/package.json +++ b/balancer-js/package.json @@ -32,14 +32,6 @@ "node:goerli": "npx hardhat --tsconfig tsconfig.testing.json --config hardhat.config.goerli.ts node --fork $(grep ALCHEMY_URL_GOERLI .env | cut -d '=' -f2 | tail -1) --port 8000" }, "devDependencies": { - "@ethersproject/abi": "^5.4.0", - "@ethersproject/abstract-signer": "^5.4.0", - "@ethersproject/address": "^5.4.0", - "@ethersproject/bignumber": "^5.4.0", - "@ethersproject/bytes": "^5.4.0", - "@ethersproject/constants": "^5.4.0", - "@ethersproject/contracts": "^5.4.0", - "@ethersproject/providers": "^5.4.5", "@ethersproject/solidity": "^5.6.1", "@ethersproject/units": "^5.7.0", "@ethersproject/wallet": "^5.5.0", @@ -89,15 +81,6 @@ "typescript": "^4.0.2" }, "dependencies": { - "@balancer-labs/sor": "^4.0.1-beta.16", - "@balancer-labs/typechain": "^1.0.0", - "axios": "^0.24.0", - "graphql": "^15.6.1", - "graphql-request": "^3.5.0", - "json-to-graphql-query": "^2.2.4", - "lodash": "^4.17.21" - }, - "peerDependencies": { "@ethersproject/abi": "^5.4.0", "@ethersproject/abstract-signer": "^5.4.0", "@ethersproject/address": "^5.4.0", @@ -105,6 +88,13 @@ "@ethersproject/bytes": "^5.4.0", "@ethersproject/constants": "^5.4.0", "@ethersproject/contracts": "^5.4.0", - "@ethersproject/providers": "^5.4.5" + "@ethersproject/providers": "^5.4.5", + "@balancer-labs/sor": "^4.0.1-beta.16", + "@balancer-labs/typechain": "^1.0.0", + "axios": "^0.24.0", + "graphql": "^15.6.1", + "graphql-request": "^3.5.0", + "json-to-graphql-query": "^2.2.4", + "lodash": "^4.17.21" } } diff --git a/balancer-js/rollup.config.ts b/balancer-js/rollup.config.ts index c51c421e0..7402fbb83 100644 --- a/balancer-js/rollup.config.ts +++ b/balancer-js/rollup.config.ts @@ -7,8 +7,7 @@ import dts from 'rollup-plugin-dts'; import pkg from './package.json'; const external = [ - ...Object.keys(pkg.dependencies), - ...Object.keys(pkg.peerDependencies), + ...Object.keys(pkg.dependencies) ]; export default [ From 4afd56878922ac8d3d0c717943324574a3aa3cf1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A1bio=20Mendes?= <ms.fabiomendes@gmail.com> Date: Mon, 9 Jan 2023 09:46:07 -0300 Subject: [PATCH 18/25] add gnosis to networks --- balancer-js/src/lib/constants/config.ts | 20 ++++++++++++++++++++ balancer-js/src/lib/constants/network.ts | 1 + 2 files changed, 21 insertions(+) diff --git a/balancer-js/src/lib/constants/config.ts b/balancer-js/src/lib/constants/config.ts index d875025f2..1a1cb0420 100644 --- a/balancer-js/src/lib/constants/config.ts +++ b/balancer-js/src/lib/constants/config.ts @@ -209,6 +209,26 @@ export const BALANCER_NETWORK_CONFIG: Record<Network, BalancerNetworkConfig> = { }, pools: {}, }, + [Network.GNOSIS]: { + chainId: Network.GNOSIS, //100 + addresses: { + contracts: { + vault: '0xBA12222222228d8Ba445958a75a0704d566BF2C8', + multicall: '0xb5b692a88bdfc81ca69dcb1d924f59f0413a602a', + relayerV4: '0xeF606F58A4FD0fCcb066c6203d0994694d3eB2D3', + balancerHelpers: '0x8E9aa87E45e92bad84D5F8DD1bff34Fb92637dE9', + }, + tokens: { + wrappedNativeAsset: '0xe91D153E0b41518A2Ce8Dd3D7944Fa863463a97d', + }, + }, + urls: { + subgraph: + 'https://api.thegraph.com/subgraphs/name/balancer-labs/balancer-gnosis-chain-v2', + gaugesSubgraph: '', + }, + pools: {}, + }, }; export const networkAddresses = ( diff --git a/balancer-js/src/lib/constants/network.ts b/balancer-js/src/lib/constants/network.ts index fd009b33e..0e78f617d 100644 --- a/balancer-js/src/lib/constants/network.ts +++ b/balancer-js/src/lib/constants/network.ts @@ -6,6 +6,7 @@ export enum Network { GĂ–RLI = 5, OPTIMISM = 10, KOVAN = 42, + GNOSIS = 100, POLYGON = 137, ARBITRUM = 42161, } From 55956f8e29e7ce9a3da30544e114e8d2b15020bd Mon Sep 17 00:00:00 2001 From: johngrantuk <johngrantuk+bot@users.noreply.github.com> Date: Mon, 9 Jan 2023 13:27:53 +0000 Subject: [PATCH 19/25] chore: version bump v0.1.44-beta.0 --- balancer-js/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/balancer-js/package.json b/balancer-js/package.json index c7f62cb6f..90be87ed6 100644 --- a/balancer-js/package.json +++ b/balancer-js/package.json @@ -1,6 +1,6 @@ { "name": "@balancer-labs/sdk", - "version": "0.1.43", + "version": "0.1.44-beta.0", "description": "JavaScript SDK for interacting with the Balancer Protocol V2", "license": "GPL-3.0-only", "homepage": "https://github.com/balancer-labs/balancer-sdk#readme", From e1ca821edfca8e261b707920f81f9cb15a01bbaa Mon Sep 17 00:00:00 2001 From: johngrantuk <johngrantuk@googlemail.com> Date: Mon, 9 Jan 2023 14:20:13 +0000 Subject: [PATCH 20/25] Improve Generic var name. --- balancer-js/src/modules/sor/pool-data/onChainData.ts | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/balancer-js/src/modules/sor/pool-data/onChainData.ts b/balancer-js/src/modules/sor/pool-data/onChainData.ts index 6bf623b9e..02888139d 100644 --- a/balancer-js/src/modules/sor/pool-data/onChainData.ts +++ b/balancer-js/src/modules/sor/pool-data/onChainData.ts @@ -14,12 +14,14 @@ import elementPoolAbi from '@/lib/abi/ConvergentCurvePool.json'; import linearPoolAbi from '@/lib/abi/LinearPool.json'; import composableStableAbi from '@/lib/abi/ComposableStable.json'; -export async function getOnChainBalances<Type extends SubgraphPoolBase | Pool>( - subgraphPoolsOriginal: Type[], +export async function getOnChainBalances< + GenericPool extends SubgraphPoolBase | Pool +>( + subgraphPoolsOriginal: GenericPool[], multiAddress: string, vaultAddress: string, provider: Provider -): Promise<Type[]> { +): Promise<GenericPool[]> { if (subgraphPoolsOriginal.length === 0) return subgraphPoolsOriginal; // eslint-disable-next-line @typescript-eslint/no-explicit-any @@ -41,7 +43,7 @@ export async function getOnChainBalances<Type extends SubgraphPoolBase | Pool>( const multiPool = new Multicaller(multiAddress, provider, abis); const supportedPoolTypes: string[] = Object.values(PoolFilter); - const subgraphPools: Type[] = []; + const subgraphPools: GenericPool[] = []; subgraphPoolsOriginal.forEach((pool) => { if (!supportedPoolTypes.includes(pool.poolType)) { console.error(`Unknown pool type: ${pool.poolType} ${pool.id}`); @@ -173,7 +175,7 @@ export async function getOnChainBalances<Type extends SubgraphPoolBase | Pool>( throw `Issue with multicall execution.`; } - const onChainPools: Type[] = []; + const onChainPools: GenericPool[] = []; Object.entries(pools).forEach(([poolId, onchainData], index) => { try { From 9b841ea4f73689af91055edc031da76b60acbff0 Mon Sep 17 00:00:00 2001 From: sssmi <simeon.kerkola@gmail.com> Date: Mon, 9 Jan 2023 22:05:55 +0200 Subject: [PATCH 21/25] Fix: Ignore casing when comparing pool token addresses in exit concerns --- .../pools/pool-types/concerns/metaStable/exit.concern.ts | 6 ++++-- .../pools/pool-types/concerns/stable/exit.concern.ts | 6 ++++-- .../pools/pool-types/concerns/weighted/exit.concern.ts | 6 ++++-- 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/balancer-js/src/modules/pools/pool-types/concerns/metaStable/exit.concern.ts b/balancer-js/src/modules/pools/pool-types/concerns/metaStable/exit.concern.ts index c10860dfd..b3693d42f 100644 --- a/balancer-js/src/modules/pools/pool-types/concerns/metaStable/exit.concern.ts +++ b/balancer-js/src/modules/pools/pool-types/concerns/metaStable/exit.concern.ts @@ -7,7 +7,7 @@ import { ExitPool, ExitPoolAttributes, } from '../types'; -import { AssetHelpers, parsePoolInfo } from '@/lib/utils'; +import { AssetHelpers, isSameAddress, parsePoolInfo } from '@/lib/utils'; import { Vault__factory } from '@balancer-labs/typechain'; import { addSlippage, subSlippage } from '@/lib/utils/slippageHelper'; import { balancerVault } from '@/lib/constants/config'; @@ -31,7 +31,9 @@ export class MetaStablePoolExit implements ExitConcern { if ( singleTokenMaxOut && singleTokenMaxOut !== AddressZero && - !pool.tokens.map((t) => t.address).some((a) => a === singleTokenMaxOut) + !pool.tokens + .map((t) => t.address) + .some((a) => isSameAddress(a, singleTokenMaxOut)) ) { throw new BalancerError(BalancerErrorCode.TOKEN_MISMATCH); } diff --git a/balancer-js/src/modules/pools/pool-types/concerns/stable/exit.concern.ts b/balancer-js/src/modules/pools/pool-types/concerns/stable/exit.concern.ts index f193e36ca..eaa04b477 100644 --- a/balancer-js/src/modules/pools/pool-types/concerns/stable/exit.concern.ts +++ b/balancer-js/src/modules/pools/pool-types/concerns/stable/exit.concern.ts @@ -8,7 +8,7 @@ import { ExitPool, ExitPoolAttributes, } from '../types'; -import { AssetHelpers, parsePoolInfo } from '@/lib/utils'; +import { AssetHelpers, isSameAddress, parsePoolInfo } from '@/lib/utils'; import { Vault__factory } from '@balancer-labs/typechain'; import { addSlippage, subSlippage } from '@/lib/utils/slippageHelper'; import { balancerVault } from '@/lib/constants/config'; @@ -32,7 +32,9 @@ export class StablePoolExit implements ExitConcern { if ( singleTokenMaxOut && singleTokenMaxOut !== AddressZero && - !pool.tokens.map((t) => t.address).some((a) => a === singleTokenMaxOut) + !pool.tokens + .map((t) => t.address) + .some((a) => isSameAddress(a, singleTokenMaxOut)) ) { throw new BalancerError(BalancerErrorCode.TOKEN_MISMATCH); } diff --git a/balancer-js/src/modules/pools/pool-types/concerns/weighted/exit.concern.ts b/balancer-js/src/modules/pools/pool-types/concerns/weighted/exit.concern.ts index abf7b5d13..e8d602719 100644 --- a/balancer-js/src/modules/pools/pool-types/concerns/weighted/exit.concern.ts +++ b/balancer-js/src/modules/pools/pool-types/concerns/weighted/exit.concern.ts @@ -7,7 +7,7 @@ import { ExitPool, ExitPoolAttributes, } from '../types'; -import { AssetHelpers, parsePoolInfo } from '@/lib/utils'; +import { AssetHelpers, isSameAddress, parsePoolInfo } from '@/lib/utils'; import { Vault__factory } from '@balancer-labs/typechain'; import { WeightedPoolEncoder } from '@/pool-weighted'; import { addSlippage, subSlippage } from '@/lib/utils/slippageHelper'; @@ -31,7 +31,9 @@ export class WeightedPoolExit implements ExitConcern { if ( singleTokenMaxOut && singleTokenMaxOut !== AddressZero && - !pool.tokens.map((t) => t.address).some((a) => a === singleTokenMaxOut) + !pool.tokens + .map((t) => t.address) + .some((a) => isSameAddress(a, singleTokenMaxOut)) ) { throw new BalancerError(BalancerErrorCode.TOKEN_MISMATCH); } From e8765ec9aa78bc0e1fbb8688a9ad96ddf4ec6333 Mon Sep 17 00:00:00 2001 From: johngrantuk <johngrantuk+bot@users.noreply.github.com> Date: Tue, 10 Jan 2023 11:32:35 +0000 Subject: [PATCH 22/25] chore: version bump v0.1.44-beta.1 --- balancer-js/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/balancer-js/package.json b/balancer-js/package.json index 6388312a3..cb55f7810 100644 --- a/balancer-js/package.json +++ b/balancer-js/package.json @@ -1,6 +1,6 @@ { "name": "@balancer-labs/sdk", - "version": "0.1.44-beta.0", + "version": "0.1.44-beta.1", "description": "JavaScript SDK for interacting with the Balancer Protocol V2", "license": "GPL-3.0-only", "homepage": "https://github.com/balancer-labs/balancer-sdk#readme", From eac4575ac051d0b6c1bf4528626162c75923c2fc Mon Sep 17 00:00:00 2001 From: johngrantuk <johngrantuk@googlemail.com> Date: Tue, 10 Jan 2023 11:35:14 +0000 Subject: [PATCH 23/25] Update SOR. --- balancer-js/package.json | 4 ++-- balancer-js/yarn.lock | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/balancer-js/package.json b/balancer-js/package.json index 237662ac1..4cf11e23f 100644 --- a/balancer-js/package.json +++ b/balancer-js/package.json @@ -81,6 +81,8 @@ "typescript": "^4.0.2" }, "dependencies": { + "@balancer-labs/sor": "^4.0.1-beta.18", + "@balancer-labs/typechain": "^1.0.0", "@ethersproject/abi": "^5.4.0", "@ethersproject/abstract-signer": "^5.4.0", "@ethersproject/address": "^5.4.0", @@ -89,8 +91,6 @@ "@ethersproject/constants": "^5.4.0", "@ethersproject/contracts": "^5.4.0", "@ethersproject/providers": "^5.4.5", - "@balancer-labs/sor": "^4.0.1-beta.16", - "@balancer-labs/typechain": "^1.0.0", "axios": "^0.24.0", "graphql": "^15.6.1", "graphql-request": "^3.5.0", diff --git a/balancer-js/yarn.lock b/balancer-js/yarn.lock index fdfd4f10e..00ffc3ce4 100644 --- a/balancer-js/yarn.lock +++ b/balancer-js/yarn.lock @@ -503,10 +503,10 @@ "@babel/helper-validator-identifier" "^7.19.1" to-fast-properties "^2.0.0" -"@balancer-labs/sor@^4.0.1-beta.17": - version "4.0.1-beta.17" - resolved "https://registry.yarnpkg.com/@balancer-labs/sor/-/sor-4.0.1-beta.17.tgz#b78d67ba4107d4ec366015532d34b5c7af3b0169" - integrity sha512-KSkPs8Rxn/MPLNeO51uQWffvCJCjLjZCSFpBrWrSpC0lY536sfmmtmt89GF2Ocin8cnZg01UEg/0JYGOnMj1Og== +"@balancer-labs/sor@^4.0.1-beta.18": + version "4.0.1-beta.18" + resolved "https://registry.yarnpkg.com/@balancer-labs/sor/-/sor-4.0.1-beta.18.tgz#811af4b41f8e9f670c1a38e5f5f2f131d28178ed" + integrity sha512-tNY9OIptCVCysSYY6QKBIpJleM66DMKRK78t1WOG1boJbay3kCm+x9R4+6LN5n05gDYM0PE2QwCPytOxzkrCfA== dependencies: isomorphic-fetch "^2.2.1" From 883a3822270c232f2f22e599f08e7ce5288d8d0f Mon Sep 17 00:00:00 2001 From: johngrantuk <johngrantuk+bot@users.noreply.github.com> Date: Tue, 10 Jan 2023 11:44:00 +0000 Subject: [PATCH 24/25] chore: version bump v0.1.44-beta.2 --- balancer-js/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/balancer-js/package.json b/balancer-js/package.json index 4cf11e23f..f6cfe5c7b 100644 --- a/balancer-js/package.json +++ b/balancer-js/package.json @@ -1,6 +1,6 @@ { "name": "@balancer-labs/sdk", - "version": "0.1.44-beta.1", + "version": "0.1.44-beta.2", "description": "JavaScript SDK for interacting with the Balancer Protocol V2", "license": "GPL-3.0-only", "homepage": "https://github.com/balancer-labs/balancer-sdk#readme", From b1964352326c8b534a2d3c3a019df98bba876144 Mon Sep 17 00:00:00 2001 From: johngrantuk <johngrantuk@googlemail.com> Date: Tue, 10 Jan 2023 11:46:17 +0000 Subject: [PATCH 25/25] Update to version 0.1.44. --- balancer-js/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/balancer-js/package.json b/balancer-js/package.json index f6cfe5c7b..16aa1300a 100644 --- a/balancer-js/package.json +++ b/balancer-js/package.json @@ -1,6 +1,6 @@ { "name": "@balancer-labs/sdk", - "version": "0.1.44-beta.2", + "version": "0.1.44", "description": "JavaScript SDK for interacting with the Balancer Protocol V2", "license": "GPL-3.0-only", "homepage": "https://github.com/balancer-labs/balancer-sdk#readme",