diff --git a/balancer-js/examples/data/onchain-multicall3.ts b/balancer-js/examples/data/onchain-multicall3.ts new file mode 100644 index 000000000..59aff46cd --- /dev/null +++ b/balancer-js/examples/data/onchain-multicall3.ts @@ -0,0 +1,102 @@ +import { + PoolsSubgraphRepository, + SubgraphPoolBase, + Network, + BALANCER_NETWORK_CONFIG, + getOnChainBalances as getOnChainBalances3, +} from '@balancer-labs/sdk'; +import { JsonRpcProvider } from '@ethersproject/providers'; +// import _ from 'lodash'; + +// Importing legacy multicall fetcher for comparison +// import { getOnChainBalances } from '@/modules/sor/pool-data/onChainData'; + +const network = Network.POLYGON; + +const pools = new PoolsSubgraphRepository({ + url: BALANCER_NETWORK_CONFIG[network].urls.subgraph, + chainId: network, + query: { + args: { + first: 10, + skip: 0, + orderBy: 'totalLiquidity', + orderDirection: 'desc', + where: { + poolType: { + eq: 'GyroE', + }, + }, + }, + attrs: {}, + }, +}); + +const providers: Record = { + [Network.MAINNET]: new JsonRpcProvider('https://rpc.ankr.com/eth'), + [Network.POLYGON]: new JsonRpcProvider('https://rpc.ankr.com/polygon'), + [Network.ARBITRUM]: new JsonRpcProvider('https://rpc.ankr.com/arbitrum'), + [Network.OPTIMISM]: new JsonRpcProvider('https://rpc.ankr.com/optimism'), + [Network.BASE]: new JsonRpcProvider('https://rpc.ankr.com/base'), + [Network.FANTOM]: new JsonRpcProvider('https://rpc.ankr.com/fantom'), + [Network.ZKEVM]: new JsonRpcProvider('https://rpc.ankr.com/polygon_zkevm'), +}; + +const provider = providers[network]; + +/** + * Recursively finds differences between two objects. Use to compare onchain data from 2 different fetchers. + */ +// function diffObjects( +// object1: any, +// object2: any, +// path = '' +// ): any { +// const allKeys = _.union(Object.keys(object1), Object.keys(object2)); + +// const differences = []; + +// for (const key of allKeys) { +// const newPath = path ? `${path}.${key}` : key; + +// if (_.isObject(object1[key]) && _.isObject(object2[key])) { +// differences.push(...diffObjects(object1[key], object2[key], newPath)); +// } else if (!_.isEqual(object1[key], object2[key])) { +// differences.push({ +// path: newPath, +// value1: object1[key], +// value2: object2[key], +// }); +// } +// } + +// return differences; +// } + +async function main() { + const subgraph = (await pools.fetch()) as SubgraphPoolBase[]; + const onchain3 = await getOnChainBalances3( + subgraph, + '', + '0xBA12222222228d8Ba445958a75a0704d566BF2C8', + provider + ); + console.log(onchain3.length); + console.log(JSON.stringify(onchain3, null, 2)); + // const onchain = await getOnChainBalances(subgraph, '0xeefba1e63905ef1d7acba5a8513c70307c1ce441', '0xBA12222222228d8Ba445958a75a0704d566BF2C8', provider); + // console.log(onchain.length) + // for(const i in subgraph) { + // const one = onchain3.find((x) => x.id === subgraph[i].id) + // const two = onchain.find((x) => x.id === subgraph[i].id) + // console.log('Pool', subgraph[i].id) + // if (!two) { + // console.log('two missing') + // continue + // } + // console.log(JSON.stringify(findNestedValueDifferences(one, two), null, 2)); + // } +} + +main(); + +// yarn example ./debug/onchain-multicall3.ts diff --git a/balancer-js/src/lib/abi/Multicall3.json b/balancer-js/src/lib/abi/Multicall3.json new file mode 100644 index 000000000..d9c5855e7 --- /dev/null +++ b/balancer-js/src/lib/abi/Multicall3.json @@ -0,0 +1,440 @@ +[ + { + "inputs": [ + { + "components": [ + { + "internalType": "address", + "name": "target", + "type": "address" + }, + { + "internalType": "bytes", + "name": "callData", + "type": "bytes" + } + ], + "internalType": "struct Multicall3.Call[]", + "name": "calls", + "type": "tuple[]" + } + ], + "name": "aggregate", + "outputs": [ + { + "internalType": "uint256", + "name": "blockNumber", + "type": "uint256" + }, + { + "internalType": "bytes[]", + "name": "returnData", + "type": "bytes[]" + } + ], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "address", + "name": "target", + "type": "address" + }, + { + "internalType": "bool", + "name": "allowFailure", + "type": "bool" + }, + { + "internalType": "bytes", + "name": "callData", + "type": "bytes" + } + ], + "internalType": "struct Multicall3.Call3[]", + "name": "calls", + "type": "tuple[]" + } + ], + "name": "aggregate3", + "outputs": [ + { + "components": [ + { + "internalType": "bool", + "name": "success", + "type": "bool" + }, + { + "internalType": "bytes", + "name": "returnData", + "type": "bytes" + } + ], + "internalType": "struct Multicall3.Result[]", + "name": "returnData", + "type": "tuple[]" + } + ], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "address", + "name": "target", + "type": "address" + }, + { + "internalType": "bool", + "name": "allowFailure", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "callData", + "type": "bytes" + } + ], + "internalType": "struct Multicall3.Call3Value[]", + "name": "calls", + "type": "tuple[]" + } + ], + "name": "aggregate3Value", + "outputs": [ + { + "components": [ + { + "internalType": "bool", + "name": "success", + "type": "bool" + }, + { + "internalType": "bytes", + "name": "returnData", + "type": "bytes" + } + ], + "internalType": "struct Multicall3.Result[]", + "name": "returnData", + "type": "tuple[]" + } + ], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "address", + "name": "target", + "type": "address" + }, + { + "internalType": "bytes", + "name": "callData", + "type": "bytes" + } + ], + "internalType": "struct Multicall3.Call[]", + "name": "calls", + "type": "tuple[]" + } + ], + "name": "blockAndAggregate", + "outputs": [ + { + "internalType": "uint256", + "name": "blockNumber", + "type": "uint256" + }, + { + "internalType": "bytes32", + "name": "blockHash", + "type": "bytes32" + }, + { + "components": [ + { + "internalType": "bool", + "name": "success", + "type": "bool" + }, + { + "internalType": "bytes", + "name": "returnData", + "type": "bytes" + } + ], + "internalType": "struct Multicall3.Result[]", + "name": "returnData", + "type": "tuple[]" + } + ], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [], + "name": "getBasefee", + "outputs": [ + { + "internalType": "uint256", + "name": "basefee", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "blockNumber", + "type": "uint256" + } + ], + "name": "getBlockHash", + "outputs": [ + { + "internalType": "bytes32", + "name": "blockHash", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getBlockNumber", + "outputs": [ + { + "internalType": "uint256", + "name": "blockNumber", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getChainId", + "outputs": [ + { + "internalType": "uint256", + "name": "chainid", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getCurrentBlockCoinbase", + "outputs": [ + { + "internalType": "address", + "name": "coinbase", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getCurrentBlockDifficulty", + "outputs": [ + { + "internalType": "uint256", + "name": "difficulty", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getCurrentBlockGasLimit", + "outputs": [ + { + "internalType": "uint256", + "name": "gaslimit", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getCurrentBlockTimestamp", + "outputs": [ + { + "internalType": "uint256", + "name": "timestamp", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "addr", + "type": "address" + } + ], + "name": "getEthBalance", + "outputs": [ + { + "internalType": "uint256", + "name": "balance", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getLastBlockHash", + "outputs": [ + { + "internalType": "bytes32", + "name": "blockHash", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bool", + "name": "requireSuccess", + "type": "bool" + }, + { + "components": [ + { + "internalType": "address", + "name": "target", + "type": "address" + }, + { + "internalType": "bytes", + "name": "callData", + "type": "bytes" + } + ], + "internalType": "struct Multicall3.Call[]", + "name": "calls", + "type": "tuple[]" + } + ], + "name": "tryAggregate", + "outputs": [ + { + "components": [ + { + "internalType": "bool", + "name": "success", + "type": "bool" + }, + { + "internalType": "bytes", + "name": "returnData", + "type": "bytes" + } + ], + "internalType": "struct Multicall3.Result[]", + "name": "returnData", + "type": "tuple[]" + } + ], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bool", + "name": "requireSuccess", + "type": "bool" + }, + { + "components": [ + { + "internalType": "address", + "name": "target", + "type": "address" + }, + { + "internalType": "bytes", + "name": "callData", + "type": "bytes" + } + ], + "internalType": "struct Multicall3.Call[]", + "name": "calls", + "type": "tuple[]" + } + ], + "name": "tryBlockAndAggregate", + "outputs": [ + { + "internalType": "uint256", + "name": "blockNumber", + "type": "uint256" + }, + { + "internalType": "bytes32", + "name": "blockHash", + "type": "bytes32" + }, + { + "components": [ + { + "internalType": "bool", + "name": "success", + "type": "bool" + }, + { + "internalType": "bytes", + "name": "returnData", + "type": "bytes" + } + ], + "internalType": "struct Multicall3.Result[]", + "name": "returnData", + "type": "tuple[]" + } + ], + "stateMutability": "payable", + "type": "function" + } +] \ No newline at end of file diff --git a/balancer-js/src/lib/utils/multiCaller3.ts b/balancer-js/src/lib/utils/multiCaller3.ts new file mode 100644 index 000000000..db4c87f68 --- /dev/null +++ b/balancer-js/src/lib/utils/multiCaller3.ts @@ -0,0 +1,104 @@ +import { set } from 'lodash'; +import { Fragment, JsonFragment, Interface, Result } from '@ethersproject/abi'; +import { CallOverrides } from '@ethersproject/contracts'; +import { Multicall3, Multicall3__factory } from '@/contracts'; +import { Provider } from '@ethersproject/providers'; + +export class Multicaller3 { + private interface: Interface; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + private calls: [string, string, any][] = []; + private paths: string[] = []; + address = '0xcA11bde05977b3631167028862bE2a173976CA11'; + multicall: Multicall3; + + constructor( + abi: string | Array, + provider: Provider, + private options: CallOverrides = {} + ) { + this.interface = new Interface(abi); + this.multicall = Multicall3__factory.connect(this.address, provider); + } + + call( + path: string, + address: string, + functionName: string, + // eslint-disable-next-line @typescript-eslint/no-explicit-any + params?: any[] + ): Multicaller3 { + this.calls.push([address, functionName, params]); + this.paths.push(path); + return this; + } + + async execute( + from: Record = {}, + batchSize = 1024 // Define the number of function calls in each batch + ): Promise> { + const obj = from; + const results = await this.executeMulticall(batchSize); + results.forEach((result, i) => + set(obj, this.paths[i], result.length > 1 ? result : result[0]) + ); + this.calls = []; + this.paths = []; + return obj; + } + + private async executeMulticall(batchSize: number): Promise { + const numBatches = Math.ceil(this.calls.length / batchSize); + const results: Result[] = []; + + const batchPromises = []; + + for (let batchIndex = 0; batchIndex < numBatches; batchIndex++) { + const batchCalls = this.calls.slice( + batchIndex * batchSize, + (batchIndex + 1) * batchSize + ); + + const batchRequests = batchCalls.map( + ([address, functionName, params]) => ({ + target: address, + allowFailure: true, + callData: this.interface.encodeFunctionData(functionName, params), + }) + ); + + batchPromises.push( + this.multicall.callStatic.aggregate3(batchRequests, this.options) + ); + } + + const batchResults = await Promise.all(batchPromises); + + batchResults.forEach((res, batchIndex) => { + const offset = batchIndex * batchSize; + + for (let i = 0; i < res.length; i++) { + const callIndex = offset + i; + const { success, returnData } = res[i]; + + if (success) { + try { + const result = this.interface.decodeFunctionResult( + this.calls[callIndex][1], + returnData + ); + results[callIndex] = result; + } catch (e) { + console.error('Multicall error', this.paths[callIndex]); + results[callIndex] = []; + } + } else { + console.error('Failed request in multicall', this.paths[callIndex]); + results[callIndex] = []; + } + } + }); + + return results; + } +} diff --git a/balancer-js/src/modules/data/pool/index.ts b/balancer-js/src/modules/data/pool/index.ts index 89d24baf0..9fa9b327b 100644 --- a/balancer-js/src/modules/data/pool/index.ts +++ b/balancer-js/src/modules/data/pool/index.ts @@ -4,3 +4,4 @@ export * from './fallback'; export * from './static'; export * from './subgraph'; export * from './subgraphOnChain'; +export * from './onchain-data'; diff --git a/balancer-js/src/modules/data/pool/onchain-data.ts b/balancer-js/src/modules/data/pool/onchain-data.ts new file mode 100644 index 000000000..cd168be7c --- /dev/null +++ b/balancer-js/src/modules/data/pool/onchain-data.ts @@ -0,0 +1,232 @@ +import { Multicaller3 } from '@/lib/utils/multiCaller3'; +import { SubgraphPoolBase } from '@/.'; +import { Provider } from '@ethersproject/providers'; +import { formatFixed } from '@ethersproject/bignumber'; +import { SubgraphToken } from '@balancer-labs/sor'; + +const abi = [ + 'function getSwapFeePercentage() view returns (uint256)', + 'function percentFee() view returns (uint256)', + 'function protocolPercentFee() view returns (uint256)', + 'function getNormalizedWeights() view returns (uint256[])', + 'function totalSupply() view returns (uint256)', + 'function getVirtualSupply() view returns (uint256)', + 'function getActualSupply() view returns (uint256)', + 'function getTargets() view returns (uint256 lowerTarget, uint256 upperTarget)', + 'function getTokenRates() view returns (uint256, uint256)', + 'function getWrappedTokenRate() view returns (uint256)', + 'function getAmplificationParameter() view returns (uint256 value, bool isUpdating, uint256 precision)', + 'function getPausedState() view returns (bool)', + 'function inRecoveryMode() view returns (bool)', + 'function getRate() view returns (uint256)', + 'function getScalingFactors() view returns (uint256[] memory)', // do we need this here? + 'function getPoolTokens(bytes32) view returns (address[], uint256[])', +]; + +const getTotalSupplyFn = (poolType: string) => { + if (poolType.includes('Linear') || ['StablePhantom'].includes(poolType)) { + return 'getVirtualSupply'; + } else if (poolType === 'ComposableStable') { + return 'getActualSupply'; + } else { + return 'totalSupply'; + } +}; + +const getSwapFeeFn = (poolType: string) => { + if (poolType === 'Element') { + return 'percentFee'; + } else if (poolType === 'FX') { + return 'protocolPercentFee'; + } else { + return 'getSwapFeePercentage'; + } +}; + +interface OnchainData { + poolTokens: [string[], string[]]; + totalShares: string; + swapFee: string; + isPaused?: boolean; + inRecoveryMode?: boolean; + rate?: string; + scalingFactors?: string[]; + weights?: string[]; + targets?: [string, string]; + wrappedTokenRate?: string; + amp?: [string, boolean, string]; + tokenRates?: [string, string]; +} + +const defaultCalls = ( + id: string, + address: string, + vaultAddress: string, + poolType: string, + multicaller: Multicaller3 +) => { + multicaller.call(`${id}.poolTokens`, vaultAddress, 'getPoolTokens', [id]); + multicaller.call(`${id}.totalShares`, address, getTotalSupplyFn(poolType)); + multicaller.call(`${id}.swapFee`, address, getSwapFeeFn(poolType)); + // Following where added to the pools query contract, however legacy onchain data didn't have them. + // multicaller.call(`${id}.isPaused`, address, 'getPausedState'); + // multicaller.call(`${id}.inRecoveryMode`, address, 'inRecoveryMode'); + // multicaller.call(`${id}.rate`, address, 'getRate'); + // multicaller.call(`${id}.scalingFactors`, address, 'getScalingFactors'); +}; + +const weightedCalls = ( + id: string, + address: string, + multicaller: Multicaller3 +) => { + multicaller.call(`${id}.weights`, address, 'getNormalizedWeights'); +}; + +const linearCalls = ( + id: string, + address: string, + multicaller: Multicaller3 +) => { + multicaller.call(`${id}.targets`, address, 'getTargets'); + multicaller.call(`${id}.wrappedTokenRate`, address, 'getWrappedTokenRate'); +}; + +const stableCalls = ( + id: string, + address: string, + multicaller: Multicaller3 +) => { + multicaller.call(`${id}.amp`, address, 'getAmplificationParameter'); +}; + +const gyroECalls = (id: string, address: string, multicaller: Multicaller3) => { + multicaller.call(`${id}.tokenRates`, address, 'getTokenRates'); +}; + +const poolTypeCalls = (poolType: string, poolTypeVersion = 1) => { + const do_nothing = () => ({}); + switch (poolType) { + case 'Weighted': + case 'LiquidityBootstrapping': + case 'Investment': + return weightedCalls; + case 'Stable': + case 'StablePhantom': + case 'MetaStable': + case 'ComposableStable': + return stableCalls; + case 'GyroE': + if (poolTypeVersion === 2) { + return gyroECalls; + } else { + return do_nothing; + } + case 'AaveLinear': + if (poolTypeVersion === 1) { + return linearCalls; + } else { + return do_nothing; + } + default: + return do_nothing; + } +}; + +const merge = (pool: SubgraphPoolBase, result: OnchainData) => ({ + ...pool, + tokens: pool.tokens.map((token) => { + const idx = result.poolTokens[0] + .map((t) => t.toLowerCase()) + .indexOf(token.address); + const wrappedToken = + pool.wrappedIndex && pool.tokensList[pool.wrappedIndex]; + return { + ...token, + balance: formatFixed(result.poolTokens[1][idx], token.decimals || 18), + weight: + (result.weights && formatFixed(result.weights[idx], 18)) || + token.weight, + priceRate: + (result.wrappedTokenRate && + wrappedToken && + wrappedToken.toLowerCase() === token.address.toLowerCase() && + formatFixed(result.wrappedTokenRate, 18)) || + token.priceRate, + } as SubgraphToken; + }), + totalShares: result.totalShares + ? formatFixed(result.totalShares, 18) + : pool.totalShares, + swapFee: formatFixed(result.swapFee, 18), + amp: + (result.amp && + result.amp[0] && + formatFixed(result.amp[0], String(result.amp[2]).length - 1)) || + pool.amp, + lowerTarget: + (result.targets && formatFixed(result.targets[0], 18)) || pool.lowerTarget, + upperTarget: + (result.targets && formatFixed(result.targets[1], 18)) || pool.upperTarget, + tokenRates: + (result.tokenRates && + result.tokenRates.map((rate) => formatFixed(rate, 18))) || + pool.tokenRates, + // rate: result.rate, + // isPaused: result.isPaused, + // inRecoveryMode: result.inRecoveryMode, + // scalingFactors: result.scalingFactors, +}); + +export const fetchOnChainPoolData = async ( + pools: { + id: string; + address: string; + poolType: string; + poolTypeVersion?: number; + }[], + vaultAddress: string, + provider: Provider +): Promise<{ [id: string]: OnchainData }> => { + if (pools.length === 0) { + return {}; + } + + const multicaller = new Multicaller3(abi, provider); + + pools.forEach(({ id, address, poolType, poolTypeVersion }) => { + defaultCalls(id, address, vaultAddress, poolType, multicaller); + poolTypeCalls(poolType, poolTypeVersion)(id, address, multicaller); + }); + + // ZkEVM needs a smaller batch size + const results = (await multicaller.execute({}, 128)) as { + [id: string]: OnchainData; + }; + + return results; +}; + +export async function getOnChainBalances( + subgraphPoolsOriginal: SubgraphPoolBase[], + _multiAddress: string, + vaultAddress: string, + provider: Provider +): Promise { + if (subgraphPoolsOriginal.length === 0) return subgraphPoolsOriginal; + + const poolsWithOnchainData: SubgraphPoolBase[] = []; + + const onchainData = (await fetchOnChainPoolData( + subgraphPoolsOriginal, + vaultAddress, + provider + )) as { [id: string]: OnchainData }; + + subgraphPoolsOriginal.forEach((pool) => { + const data = onchainData[pool.id]; + poolsWithOnchainData.push(merge(pool, data)); + }); + + return poolsWithOnchainData; +} diff --git a/balancer-js/src/modules/data/pool/subgraph.ts b/balancer-js/src/modules/data/pool/subgraph.ts index cfc963b7a..f0492ea8a 100644 --- a/balancer-js/src/modules/data/pool/subgraph.ts +++ b/balancer-js/src/modules/data/pool/subgraph.ts @@ -107,7 +107,7 @@ export class PoolsSubgraphRepository this.query.args.block = { number: await this.blockHeight() }; } - this.query.args.first = options?.first || 1000; + this.query.args.first = options?.first || this.query.args.first || 1000; const formattedQuery = new GraphQLArgsBuilder(this.query.args).format( new SubgraphArgsFormatter() diff --git a/balancer-js/src/modules/pools/queries/queries.integration.spec.ts b/balancer-js/src/modules/pools/queries/queries.integration.spec.ts index 125a139ea..efaf3be34 100644 --- a/balancer-js/src/modules/pools/queries/queries.integration.spec.ts +++ b/balancer-js/src/modules/pools/queries/queries.integration.spec.ts @@ -40,22 +40,21 @@ const composableStablePool = { ], }; -// eslint-disable-next-line @typescript-eslint/no-unused-vars -const composableStablePoolWithTokenAtZero = { - id: '0x02d928e68d8f10c0358566152677db51e1e2dc8c00000000000000000000051e', - poolType: PoolType.ComposableStable, - tokensList: [ - '0x02d928e68d8f10c0358566152677db51e1e2dc8c', - '0x60d604890feaa0b5460b28a424407c24fe89374a', - '0xf951e335afb289353dc249e82926178eac7ded78', - ], -}; +// const composableStablePoolWithTokenAtZero = { +// id: '0x02d928e68d8f10c0358566152677db51e1e2dc8c00000000000000000000051e', +// poolType: PoolType.ComposableStable, +// tokensList: [ +// '0x02d928e68d8f10c0358566152677db51e1e2dc8c', +// '0x60d604890feaa0b5460b28a424407c24fe89374a', +// '0xf951e335afb289353dc249e82926178eac7ded78', +// ], +// }; const pools = [ stETHPool, balPool, composableStablePool, - // composableStablePoolWithTokenAtZero, // This appears to be broken after recovery? + // composableStablePoolWithTokenAtZero, ]; let queryParams: ParamsBuilder; diff --git a/balancer-js/src/modules/sor/pool-data/onChainData3.ts b/balancer-js/src/modules/sor/pool-data/onChainData3.ts new file mode 100644 index 000000000..15acacd2f --- /dev/null +++ b/balancer-js/src/modules/sor/pool-data/onChainData3.ts @@ -0,0 +1 @@ +export * from '@/modules/data/pool/onchain-data'; diff --git a/balancer-js/src/modules/sor/pool-data/subgraphPoolDataService.ts b/balancer-js/src/modules/sor/pool-data/subgraphPoolDataService.ts index 86dbcef33..94c0b1af7 100644 --- a/balancer-js/src/modules/sor/pool-data/subgraphPoolDataService.ts +++ b/balancer-js/src/modules/sor/pool-data/subgraphPoolDataService.ts @@ -6,7 +6,7 @@ import { SubgraphClient, } from '@/modules/subgraph/subgraph'; import { parseInt } from 'lodash'; -import { getOnChainBalances } from './onChainData'; +import { getOnChainBalances } from './onChainData3'; import { Provider } from '@ethersproject/providers'; import { BalancerNetworkConfig,