diff --git a/packages/client/src/getPools.ts b/packages/client/src/getPools.ts index 0a76cb91..3d03e2e8 100644 --- a/packages/client/src/getPools.ts +++ b/packages/client/src/getPools.ts @@ -1,6 +1,7 @@ import UniswapV2Factory from "@uniswap/v2-core/build/UniswapV2Factory.json"; import UniswapV2Pair from "@uniswap/v2-core/build/UniswapV2Pair.json"; import { getAddress, ParamChainName } from "@zetachain/protocol-contracts"; +import SystemContract from "@zetachain/protocol-contracts/abi/zevm/SystemContract.sol/SystemContract.json"; import { ethers } from "ethers"; import { ZetaChainClient } from "./client"; @@ -8,46 +9,107 @@ import { ZetaChainClient } from "./client"; export const getPools = async function (this: ZetaChainClient) { const rpc = this.getEndpoint("evm", `zeta_${this.network}`); const provider = new ethers.providers.StaticJsonRpcProvider(rpc); - - const uniswapV2FactoryAddress = getAddress( - "uniswapV2Factory", - `zeta_${this.network}` as ParamChainName - ); + const zetaNetwork = `zeta_${this.network}` as ParamChainName; + const uniswapV2FactoryAddress = getAddress("uniswapV2Factory", zetaNetwork); if (!uniswapV2FactoryAddress) { throw new Error("uniswapV2Factory is not defined"); } - const UniswapV2FactoryContract = new ethers.Contract( - uniswapV2FactoryAddress, - UniswapV2Factory.abi, + const systemContractAddress = getAddress("systemContract", zetaNetwork); + if (!systemContractAddress) { + throw new Error("System contract is not defined"); + } + + const systemContract = new ethers.Contract( + systemContractAddress, + SystemContract.abi, provider ); - const totalPairs = await UniswapV2FactoryContract.allPairsLength(); - let pairs = []; - for (let i = 0; i < totalPairs; i++) { - pairs.push(await UniswapV2FactoryContract.allPairs(i)); + const zetaTokenAddress = getAddress("zetaToken", zetaNetwork); + if (!zetaTokenAddress) { + throw new Error("ZETA token address is not defined"); } - const poolPromises = pairs.map(async (pair: any) => { - let pool = { - pair, - t0: {}, - t1: {}, - } as any; - const pairContract = new ethers.Contract(pair, UniswapV2Pair.abi, provider); + const foreignCoins = await this.getForeignCoins(); + const tokenAddresses = foreignCoins.map( + (coin: any) => coin.zrc20_contract_address + ); + tokenAddresses.push(zetaTokenAddress); + + const uniquePairs = tokenAddresses.reduce( + (pairs: any, tokenA: string, i: any) => { + tokenAddresses.slice(i + 1).forEach((tokenB: any) => { + const pairKey = [tokenA, tokenB].sort().join("-"); + if (!pairs.some((p: any) => p.key === pairKey)) { + pairs.push({ key: pairKey, tokenA, tokenB }); + } + }); + return pairs; + }, + [] + ); + + const poolPromises = uniquePairs.map(async ({ tokenA, tokenB }: any) => { + const pair = await systemContract.uniswapv2PairFor( + uniswapV2FactoryAddress, + tokenA, + tokenB + ); + + if (pair === ethers.constants.AddressZero) return null; + + try { + const pairContract = new ethers.Contract( + pair, + UniswapV2Pair.abi, + provider + ); + const [token0, token1] = await Promise.all([ + pairContract.token0(), + pairContract.token1(), + ]); + const reserves = await pairContract.getReserves(); + + return { + pair, + t0: { address: token0, reserve: reserves[0] }, + t1: { address: token1, reserve: reserves[1] }, + }; + } catch (error) { + return null; + } + }); - pool.t0.address = await pairContract.token0(); - pool.t1.address = await pairContract.token1(); + let pools = (await Promise.all(poolPromises)).filter((pool) => pool !== null); - const reserves = await pairContract.getReserves(); - pool.t0.reserve = reserves[0]; - pool.t1.reserve = reserves[1]; + const zrc20Details = foreignCoins.reduce((acc: any, coin: any) => { + acc[coin.zrc20_contract_address.toLowerCase()] = { + decimals: coin.decimals, + symbol: coin.symbol, + }; + return acc; + }, {}); - return pool; + pools = pools.map((t: any) => { + const zeta = { decimals: 18, symbol: "WZETA" }; + const t0 = t.t0.address.toLowerCase(); + const t1 = t.t1.address.toLowerCase(); + const t0ZETA = t0 === zetaTokenAddress.toLowerCase() && zeta; + const t1ZETA = t1 === zetaTokenAddress.toLowerCase() && zeta; + return { + ...t, + t0: { + ...t.t0, + ...(zrc20Details[t0] || t0ZETA), + }, + t1: { + ...t.t1, + ...(zrc20Details[t1] || t1ZETA), + }, + }; }); - const pools = await Promise.all(poolPromises); return pools; }; diff --git a/packages/tasks/src/pools.ts b/packages/tasks/src/pools.ts index dc52c44c..0822e5bf 100644 --- a/packages/tasks/src/pools.ts +++ b/packages/tasks/src/pools.ts @@ -10,45 +10,26 @@ const main = async (args: any, hre: HardhatRuntimeEnvironment) => { network: args.mainnet ? "mainnet" : "testnet", }); - const foreignCoins = await client.getForeignCoins(); const pools = await client.getPools(); - const addressToInfo = foreignCoins.reduce((acc: any, coin: any) => { - acc[coin.zrc20_contract_address.toLowerCase()] = { - decimals: coin.decimals, - symbol: coin.symbol, - }; - return acc; - }, {}); - - const wzeta = getAddress( - "zetaToken", - `zeta_${client.network}` as ParamChainName - ); - if (!wzeta) { - throw new Error("Could not find the WZETA address"); - } - const WZETA_ADDRESS = wzeta.toLowerCase(); - addressToInfo[WZETA_ADDRESS] = { decimals: 18, symbol: "WZETA" }; - - const poolsWithSymbolsAndDecimals = pools.map((pool: any) => { - const placeholder = { decimals: 18, symbol: "Unknown" }; - const t0Info = addressToInfo[pool.t0.address.toLowerCase()] || placeholder; - const t1Info = addressToInfo[pool.t1.address.toLowerCase()] || placeholder; - pool.t0.reserve = formatUnits(pool.t0.reserve, t0Info.decimals); - pool.t1.reserve = formatUnits(pool.t1.reserve, t1Info.decimals); - + const poolsDisplay = pools.map((pool: any) => { return { ...pool, - t0: { ...pool.t0, ...t0Info }, - t1: { ...pool.t1, ...t1Info }, + t0: { + ...pool.t0, + reserve: formatUnits(pool.t0.reserve, pool.t0.decimals), + }, + t1: { + ...pool.t1, + reserve: formatUnits(pool.t1.reserve, pool.t1.decimals), + }, }; }); const tableData = {} as any; - poolsWithSymbolsAndDecimals.forEach((pool: any) => { - const r0 = parseFloat(pool.t0.reserve).toFixed(2); - const r1 = parseFloat(pool.t1.reserve).toFixed(2); + poolsDisplay.forEach((pool: any) => { + const r0 = parseFloat(pool.t0.reserve); + const r1 = parseFloat(pool.t1.reserve); tableData[pool.pair] = { Pool: `${pool.t0.symbol} / ${pool.t1.symbol}`, @@ -57,7 +38,7 @@ const main = async (args: any, hre: HardhatRuntimeEnvironment) => { }); if (args.json) { - console.log(poolsWithSymbolsAndDecimals); + console.log(pools); } else { console.table(tableData); }