diff --git a/contracts/SwapHelperLib.sol b/contracts/SwapHelperLib.sol index 8d7fd531..7c5d2b2e 100644 --- a/contracts/SwapHelperLib.sol +++ b/contracts/SwapHelperLib.sol @@ -5,6 +5,7 @@ import "@uniswap/v2-periphery/contracts/interfaces/IUniswapV2Router02.sol"; import "@uniswap/v2-periphery/contracts/interfaces/IUniswapV2Router01.sol"; import "@zetachain/protocol-contracts/contracts/zevm/interfaces/IZRC20.sol"; import "@zetachain/protocol-contracts/contracts/zevm/SystemContract.sol"; +import "./shared/libraries/UniswapV2Library.sol"; library SwapHelperLib { uint16 internal constant MAX_DEADLINE = 200; @@ -17,6 +18,8 @@ library SwapHelperLib { error CantBeZeroAddress(); + error InvalidPathLength(); + // returns sorted token addresses, used to handle return values from pairs sorted in this order function sortTokens( address tokenA, @@ -85,6 +88,25 @@ library SwapHelperLib { IZRC20(zrc20B).balanceOf(uniswapPool) > 0; } + function _isSufficientLiquidity( + address uniswapV2Factory, + uint256 amountIn, + uint256 minAmountOut, + address[] memory path + ) internal view returns (bool) { + if (path.length != 2) revert InvalidPathLength(); + bool existsPairPool = _existsPairPool( + uniswapV2Factory, + path[0], + path[1] + ); + if (!existsPairPool) { + return false; + } + uint256[] memory amounts = UniswapV2Library.getAmountsOut(uniswapV2Factory, amountIn, path); + return amounts[amounts.length - 1] >= minAmountOut; + } + function swapExactTokensForTokens( SystemContract systemContract, address zrc20, @@ -92,6 +114,51 @@ library SwapHelperLib { address targetZRC20, uint256 minAmountOut ) internal returns (uint256) { + + address[] memory path; + path = new address[](2); + path[0] = zrc20; + path[1] = targetZRC20; + + bool isSufficientLiquidity = _isSufficientLiquidity( + systemContract.uniswapv2FactoryAddress(), + amount, + minAmountOut, + path + ); + + bool isZETA = targetZRC20 == systemContract.wZetaContractAddress() || zrc20 == systemContract.wZetaContractAddress(); + + if (!isSufficientLiquidity && !isZETA) { + path = new address[](3); + path[0] = zrc20; + path[1] = systemContract.wZetaContractAddress(); + path[2] = targetZRC20; + } + + IZRC20(zrc20).approve( + address(systemContract.uniswapv2Router02Address()), + amount + ); + uint256[] memory amounts = IUniswapV2Router01( + systemContract.uniswapv2Router02Address() + ).swapExactTokensForTokens( + amount, + minAmountOut, + path, + address(this), + block.timestamp + MAX_DEADLINE + ); + return amounts[path.length - 1]; + } + + function swapExactTokensForTokensDirectly( + SystemContract systemContract, + address zrc20, + uint256 amount, + address targetZRC20, + uint256 minAmountOut + ) internal returns (uint256) { bool existsPairPool = _existsPairPool( systemContract.uniswapv2FactoryAddress(), zrc20, @@ -166,4 +233,21 @@ library SwapHelperLib { ); return amounts[0]; } + + function getMinOutAmount(SystemContract systemContract, address zrc20, address target, uint256 amountIn) public view returns (uint256 minOutAmount) { + address[] memory path; + + path = new address[](2); + path[0] = zrc20; + path[1] = target; + uint[] memory amounts1 = UniswapV2Library.getAmountsOut(systemContract.uniswapv2FactoryAddress(), amountIn, path); + + path = new address[](3); + path[0] = zrc20; + path[1] = systemContract.wZetaContractAddress(); + path[2] = target; + uint[] memory amounts2 = UniswapV2Library.getAmountsOut(systemContract.uniswapv2FactoryAddress(), amountIn, path); + + minOutAmount = amounts1[amounts1.length - 1] > amounts2[amounts2.length - 1] ? amounts1[amounts1.length - 1] : amounts2[amounts2.length - 1]; + } } diff --git a/packages/client/src/getBalances.ts b/packages/client/src/getBalances.ts index 72d88295..eed04496 100644 --- a/packages/client/src/getBalances.ts +++ b/packages/client/src/getBalances.ts @@ -92,15 +92,21 @@ export const getBalances = async function ( } }); supportedChains.forEach((chain: any) => { - const contract = getAddress("zetaToken", chain.chain_name as any); - if (contract) { - tokens.push({ - chain_id: chain.chain_id, - coin_type: "ERC20", - contract, - decimals: 18, - symbol: "WZETA", - }); + const chainLabel = Object.keys(this.getChains()).find( + (key) => this.getChains()[key].chain_id === parseInt(chain.chain_id) + ); + + if (chainLabel) { + const contract = getAddress("zetaToken", chainLabel as any); + if (contract) { + tokens.push({ + chain_id: chain.chain_id, + coin_type: "ERC20", + contract, + decimals: 18, + symbol: "WZETA", + }); + } } }); tokens.push({ @@ -217,11 +223,16 @@ export const getBalances = async function ( !["btc_testnet", "btc_mainnet"].includes(token.chain_name) ) .map(async (token) => { - const rpc = await this.getEndpoint("evm", token.chain_name); - const provider = new ethers.providers.StaticJsonRpcProvider(rpc); - const balance = await provider.getBalance(evmAddress); - const formattedBalance = formatUnits(balance, token.decimals); - balances.push({ ...token, balance: formattedBalance }); + const chainLabel = Object.keys(this.getChains()).find( + (key) => this.getChains()[key].chain_id === parseInt(token.chain_id) + ); + if (chainLabel) { + const rpc = await this.getEndpoint("evm", chainLabel); + const provider = new ethers.providers.StaticJsonRpcProvider(rpc); + const balance = await provider.getBalance(evmAddress); + const formattedBalance = formatUnits(balance, token.decimals); + balances.push({ ...token, balance: formattedBalance }); + } }) ); diff --git a/packages/client/src/getPools.ts b/packages/client/src/getPools.ts index 3d03e2e8..5b408884 100644 --- a/packages/client/src/getPools.ts +++ b/packages/client/src/getPools.ts @@ -5,6 +5,13 @@ import SystemContract from "@zetachain/protocol-contracts/abi/zevm/SystemContrac import { ethers } from "ethers"; import { ZetaChainClient } from "./client"; +import MULTICALL3_ABI from "./multicall3.json"; + +type Pair = { + key: string; + tokenA: string; + tokenB: string; +}; export const getPools = async function (this: ZetaChainClient) { const rpc = this.getEndpoint("evm", `zeta_${this.network}`); @@ -38,11 +45,11 @@ export const getPools = async function (this: ZetaChainClient) { ); tokenAddresses.push(zetaTokenAddress); - const uniquePairs = tokenAddresses.reduce( - (pairs: any, tokenA: string, i: any) => { - tokenAddresses.slice(i + 1).forEach((tokenB: any) => { + const uniquePairs: Pair[] = tokenAddresses.reduce( + (pairs: Pair[], tokenA: string, i: number) => { + tokenAddresses.slice(i + 1).forEach((tokenB: string) => { const pairKey = [tokenA, tokenB].sort().join("-"); - if (!pairs.some((p: any) => p.key === pairKey)) { + if (!pairs.some((p: Pair) => p.key === pairKey)) { pairs.push({ key: pairKey, tokenA, tokenB }); } }); @@ -51,38 +58,103 @@ export const getPools = async function (this: ZetaChainClient) { [] ); - const poolPromises = uniquePairs.map(async ({ tokenA, tokenB }: any) => { - const pair = await systemContract.uniswapv2PairFor( + const multicallAddress = "0xca11bde05977b3631167028862be2a173976ca11"; + const multicallContract = new ethers.Contract( + multicallAddress, + MULTICALL3_ABI, + provider + ); + + const calls = uniquePairs.map(({ tokenA, tokenB }) => ({ + callData: systemContract.interface.encodeFunctionData("uniswapv2PairFor", [ uniswapV2FactoryAddress, tokenA, - tokenB - ); + tokenB, + ]), + target: systemContractAddress, + })); + + const { returnData } = await multicallContract.callStatic.aggregate(calls); + + const validPairs = returnData + .map((data: any, index: number) => { + try { + const pair = systemContract.interface.decodeFunctionResult( + "uniswapv2PairFor", + data + )[0]; + return pair !== ethers.constants.AddressZero ? pair : null; + } catch { + return null; + } + }) + .filter((pair: string | null) => pair !== null); + + const pairCalls = validPairs + .map((pair: string) => [ + { + callData: new ethers.utils.Interface( + UniswapV2Pair.abi + ).encodeFunctionData("token0"), + target: pair, + }, + { + callData: new ethers.utils.Interface( + UniswapV2Pair.abi + ).encodeFunctionData("token1"), + target: pair, + }, + { + callData: new ethers.utils.Interface( + UniswapV2Pair.abi + ).encodeFunctionData("getReserves"), + target: pair, + }, + ]) + .flat(); - if (pair === ethers.constants.AddressZero) return null; + const pairReturnData = await multicallContract.callStatic.aggregate( + pairCalls + ); + const pools = []; + const uniswapInterface = new ethers.utils.Interface(UniswapV2Pair.abi); + + for (let i = 0; i < pairReturnData.returnData.length; i += 3) { + const pairIndex = Math.floor(i / 3); + const pair = validPairs[pairIndex]; + + if ( + !pairReturnData.returnData[i] || + !pairReturnData.returnData[i + 1] || + !pairReturnData.returnData[i + 2] + ) { + console.warn(`Missing data for pair ${pair} at index ${i}`); + continue; + } + + const token0Data = pairReturnData.returnData[i]; + const token1Data = pairReturnData.returnData[i + 1]; + const reservesData = pairReturnData.returnData[i + 2]; + + // Check if data can be decoded + let token0, token1, reserves; try { - const pairContract = new ethers.Contract( - pair, - UniswapV2Pair.abi, - provider + token0 = uniswapInterface.decodeFunctionResult("token0", token0Data)[0]; + token1 = uniswapInterface.decodeFunctionResult("token1", token1Data)[0]; + reserves = uniswapInterface.decodeFunctionResult( + "getReserves", + reservesData ); - 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; + } catch { + continue; } - }); - - let pools = (await Promise.all(poolPromises)).filter((pool) => pool !== null); + pools.push({ + pair, + t0: { address: token0, reserve: reserves._reserve0 }, + t1: { address: token1, reserve: reserves._reserve1 }, + }); + } const zrc20Details = foreignCoins.reduce((acc: any, coin: any) => { acc[coin.zrc20_contract_address.toLowerCase()] = { @@ -92,7 +164,7 @@ export const getPools = async function (this: ZetaChainClient) { return acc; }, {}); - pools = pools.map((t: any) => { + const formattedPools = pools.map((t: any) => { const zeta = { decimals: 18, symbol: "WZETA" }; const t0 = t.t0.address.toLowerCase(); const t1 = t.t1.address.toLowerCase(); @@ -111,5 +183,5 @@ export const getPools = async function (this: ZetaChainClient) { }; }); - return pools; + return formattedPools; }; diff --git a/packages/client/src/multicall3.json b/packages/client/src/multicall3.json new file mode 100644 index 00000000..bcd99362 --- /dev/null +++ b/packages/client/src/multicall3.json @@ -0,0 +1,38 @@ +[ + { + "inputs": [ + { + "components": [ + { + "internalType": "address", + "name": "target", + "type": "address" + }, + { + "internalType": "bytes", + "name": "callData", + "type": "bytes" + } + ], + "internalType": "struct IMulticall3.Call[]", + "name": "calls", + "type": "tuple[]" + } + ], + "name": "aggregate", + "outputs": [ + { + "internalType": "uint256", + "name": "blockNumber", + "type": "uint256" + }, + { + "internalType": "bytes[]", + "name": "returnData", + "type": "bytes[]" + } + ], + "stateMutability": "view", + "type": "function" + } +] \ No newline at end of file diff --git a/packages/tasks/src/cctx.ts b/packages/tasks/src/cctx.ts index f407df7b..522d3b0b 100644 --- a/packages/tasks/src/cctx.ts +++ b/packages/tasks/src/cctx.ts @@ -35,4 +35,4 @@ export const cctxTask = task( ) .addPositionalParam("tx", "Hash of an inbound or a cross-chain transaction") .addFlag("json", "Output as JSON") - .addOptionalParam("type", "Testnet or mainnet", "testnet"); + .addFlag("mainnet", "Run the task on mainnet"); diff --git a/typechain-types/contracts/SwapHelperLib.ts b/typechain-types/contracts/SwapHelperLib.ts index e35f4386..4cc07069 100644 --- a/typechain-types/contracts/SwapHelperLib.ts +++ b/typechain-types/contracts/SwapHelperLib.ts @@ -4,6 +4,7 @@ import type { BaseContract, BigNumber, + BigNumberish, BytesLike, CallOverrides, PopulatedTransaction, @@ -22,11 +23,23 @@ import type { export interface SwapHelperLibInterface extends utils.Interface { functions: { + "getMinOutAmount(SystemContract,address,address,uint256)": FunctionFragment; "uniswapv2PairFor(address,address,address)": FunctionFragment; }; - getFunction(nameOrSignatureOrTopic: "uniswapv2PairFor"): FunctionFragment; + getFunction( + nameOrSignatureOrTopic: "getMinOutAmount" | "uniswapv2PairFor" + ): FunctionFragment; + encodeFunctionData( + functionFragment: "getMinOutAmount", + values: [ + PromiseOrValue, + PromiseOrValue, + PromiseOrValue, + PromiseOrValue + ] + ): string; encodeFunctionData( functionFragment: "uniswapv2PairFor", values: [ @@ -36,6 +49,10 @@ export interface SwapHelperLibInterface extends utils.Interface { ] ): string; + decodeFunctionResult( + functionFragment: "getMinOutAmount", + data: BytesLike + ): Result; decodeFunctionResult( functionFragment: "uniswapv2PairFor", data: BytesLike @@ -71,6 +88,14 @@ export interface SwapHelperLib extends BaseContract { removeListener: OnEvent; functions: { + getMinOutAmount( + systemContract: PromiseOrValue, + zrc20: PromiseOrValue, + target: PromiseOrValue, + amountIn: PromiseOrValue, + overrides?: CallOverrides + ): Promise<[BigNumber] & { minOutAmount: BigNumber }>; + uniswapv2PairFor( factory: PromiseOrValue, tokenA: PromiseOrValue, @@ -79,6 +104,14 @@ export interface SwapHelperLib extends BaseContract { ): Promise<[string] & { pair: string }>; }; + getMinOutAmount( + systemContract: PromiseOrValue, + zrc20: PromiseOrValue, + target: PromiseOrValue, + amountIn: PromiseOrValue, + overrides?: CallOverrides + ): Promise; + uniswapv2PairFor( factory: PromiseOrValue, tokenA: PromiseOrValue, @@ -87,6 +120,14 @@ export interface SwapHelperLib extends BaseContract { ): Promise; callStatic: { + getMinOutAmount( + systemContract: PromiseOrValue, + zrc20: PromiseOrValue, + target: PromiseOrValue, + amountIn: PromiseOrValue, + overrides?: CallOverrides + ): Promise; + uniswapv2PairFor( factory: PromiseOrValue, tokenA: PromiseOrValue, @@ -98,6 +139,14 @@ export interface SwapHelperLib extends BaseContract { filters: {}; estimateGas: { + getMinOutAmount( + systemContract: PromiseOrValue, + zrc20: PromiseOrValue, + target: PromiseOrValue, + amountIn: PromiseOrValue, + overrides?: CallOverrides + ): Promise; + uniswapv2PairFor( factory: PromiseOrValue, tokenA: PromiseOrValue, @@ -107,6 +156,14 @@ export interface SwapHelperLib extends BaseContract { }; populateTransaction: { + getMinOutAmount( + systemContract: PromiseOrValue, + zrc20: PromiseOrValue, + target: PromiseOrValue, + amountIn: PromiseOrValue, + overrides?: CallOverrides + ): Promise; + uniswapv2PairFor( factory: PromiseOrValue, tokenA: PromiseOrValue, diff --git a/typechain-types/factories/contracts/SwapHelperLib__factory.ts b/typechain-types/factories/contracts/SwapHelperLib__factory.ts index 2f0f5fb1..006b1bcd 100644 --- a/typechain-types/factories/contracts/SwapHelperLib__factory.ts +++ b/typechain-types/factories/contracts/SwapHelperLib__factory.ts @@ -10,6 +10,11 @@ import type { } from "../../contracts/SwapHelperLib"; const _abi = [ + { + inputs: [], + name: "AdditionsOverflow", + type: "error", + }, { inputs: [], name: "CantBeIdenticalAddresses", @@ -20,6 +25,36 @@ const _abi = [ name: "CantBeZeroAddress", type: "error", }, + { + inputs: [], + name: "IdenticalAddresses", + type: "error", + }, + { + inputs: [], + name: "InsufficientInputAmount", + type: "error", + }, + { + inputs: [], + name: "InsufficientLiquidity", + type: "error", + }, + { + inputs: [], + name: "InvalidPath", + type: "error", + }, + { + inputs: [], + name: "InvalidPathLength", + type: "error", + }, + { + inputs: [], + name: "MultiplicationsOverflow", + type: "error", + }, { inputs: [], name: "NotEnoughToPayGasFee", @@ -30,6 +65,45 @@ const _abi = [ name: "WrongGasContract", type: "error", }, + { + inputs: [], + name: "ZeroAddress", + type: "error", + }, + { + inputs: [ + { + internalType: "contract SystemContract", + name: "systemContract", + type: "SystemContract", + }, + { + internalType: "address", + name: "zrc20", + type: "address", + }, + { + internalType: "address", + name: "target", + type: "address", + }, + { + internalType: "uint256", + name: "amountIn", + type: "uint256", + }, + ], + name: "getMinOutAmount", + outputs: [ + { + internalType: "uint256", + name: "minOutAmount", + type: "uint256", + }, + ], + stateMutability: "view", + type: "function", + }, { inputs: [ { @@ -62,7 +136,7 @@ const _abi = [ ] as const; const _bytecode = - "0x610492610053600b82828239805160001a607314610046577f4e487b7100000000000000000000000000000000000000000000000000000000600052600060045260246000fd5b30600052607381538281f3fe73000000000000000000000000000000000000000030146080604052600436106100355760003560e01c8063c63585cc1461003a575b600080fd5b610054600480360381019061004f919061020d565b61006a565b6040516100619190610351565b60405180910390f35b600080600061007985856100dc565b915091508582826040516020016100919291906102e3565b604051602081830303815290604052805190602001206040516020016100b892919061030f565b6040516020818303038152906040528051906020012060001c925050509392505050565b6000808273ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff161415610145576040517fcb1e7cfe00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8273ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff161061017f578284610182565b83835b8092508193505050600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1614156101f1576040517f78b507da00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b9250929050565b60008135905061020781610445565b92915050565b600080600060608486031215610226576102256103e1565b5b6000610234868287016101f8565b9350506020610245868287016101f8565b9250506040610256868287016101f8565b9150509250925092565b61026981610377565b82525050565b61028061027b82610377565b6103b3565b82525050565b61029761029282610389565b6103c5565b82525050565b60006102aa60208361036c565b91506102b5826103f3565b602082019050919050565b60006102cd60018361036c565b91506102d88261041c565b600182019050919050565b60006102ef828561026f565b6014820191506102ff828461026f565b6014820191508190509392505050565b600061031a826102c0565b9150610326828561026f565b6014820191506103368284610286565b6020820191506103458261029d565b91508190509392505050565b60006020820190506103666000830184610260565b92915050565b600081905092915050565b600061038282610393565b9050919050565b6000819050919050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b60006103be826103cf565b9050919050565b6000819050919050565b60006103da826103e6565b9050919050565b600080fd5b60008160601b9050919050565b7f96e8ac4277198ff8b6f785478aa9a39f403cb768dd02cbee326c3e7da348845f600082015250565b7fff00000000000000000000000000000000000000000000000000000000000000600082015250565b61044e81610377565b811461045957600080fd5b5056fea2646970667358221220ecd86d07164105c72752ddee1f025ed5b5a1f55fabb9a16df5693cec1df5179a64736f6c63430008070033"; + "0x6113cb610053600b82828239805160001a607314610046577f4e487b7100000000000000000000000000000000000000000000000000000000600052600060045260246000fd5b30600052607381538281f3fe73000000000000000000000000000000000000000030146080604052600436106100405760003560e01c8063b864987914610045578063c63585cc14610075575b600080fd5b61005f600480360381019061005a9190610e1b565b6100a5565b60405161006c9190611028565b60405180910390f35b61008f600480360381019061008a9190610dc8565b610519565b60405161009c9190610fe4565b60405180910390f35b60006060600267ffffffffffffffff8111156100c4576100c361128f565b5b6040519080825280602002602001820160405280156100f25781602001602082028036833780820191505090505b509050848160008151811061010a57610109611260565b5b602002602001019073ffffffffffffffffffffffffffffffffffffffff16908173ffffffffffffffffffffffffffffffffffffffff1681525050838160018151811061015957610158611260565b5b602002602001019073ffffffffffffffffffffffffffffffffffffffff16908173ffffffffffffffffffffffffffffffffffffffff1681525050600061021d8773ffffffffffffffffffffffffffffffffffffffff1663d936a0126040518163ffffffff1660e01b815260040160206040518083038186803b1580156101de57600080fd5b505afa1580156101f2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906102169190610d9b565b858461058b565b9050600367ffffffffffffffff81111561023a5761023961128f565b5b6040519080825280602002602001820160405280156102685781602001602082028036833780820191505090505b50915085826000815181106102805761027f611260565b5b602002602001019073ffffffffffffffffffffffffffffffffffffffff16908173ffffffffffffffffffffffffffffffffffffffff16815250508673ffffffffffffffffffffffffffffffffffffffff1663569541b96040518163ffffffff1660e01b815260040160206040518083038186803b15801561030057600080fd5b505afa158015610314573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906103389190610d9b565b8260018151811061034c5761034b611260565b5b602002602001019073ffffffffffffffffffffffffffffffffffffffff16908173ffffffffffffffffffffffffffffffffffffffff1681525050848260028151811061039b5761039a611260565b5b602002602001019073ffffffffffffffffffffffffffffffffffffffff16908173ffffffffffffffffffffffffffffffffffffffff1681525050600061045f8873ffffffffffffffffffffffffffffffffffffffff1663d936a0126040518163ffffffff1660e01b815260040160206040518083038186803b15801561042057600080fd5b505afa158015610434573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906104589190610d9b565b868561058b565b9050806001825161047091906110d5565b8151811061048157610480611260565b5b6020026020010151826001845161049891906110d5565b815181106104a9576104a8611260565b5b6020026020010151116104e35780600182516104c591906110d5565b815181106104d6576104d5611260565b5b602002602001015161050c565b81600183516104f291906110d5565b8151811061050357610502611260565b5b60200260200101515b9350505050949350505050565b6000806000610528858561070d565b91509150858282604051602001610540929190610f76565b60405160208183030381529060405280519060200120604051602001610567929190610fa2565b6040516020818303038152906040528051906020012060001c925050509392505050565b60606002825110156105c9576040517f20db826700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b815167ffffffffffffffff8111156105e4576105e361128f565b5b6040519080825280602002602001820160405280156106125781602001602082028036833780820191505090505b509050828160008151811061062a57610629611260565b5b60200260200101818152505060005b6001835161064791906110d5565b8110156107055760008061069c8786858151811061066857610667611260565b5b60200260200101518760018761067e919061104e565b8151811061068f5761068e611260565b5b6020026020010151610829565b915091506106c58484815181106106b6576106b5611260565b5b6020026020010151838361093b565b846001856106d3919061104e565b815181106106e4576106e3611260565b5b602002602001018181525050505080806106fd9061118b565b915050610639565b509392505050565b6000808273ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff161415610776576040517fcb1e7cfe00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8273ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff16106107b05782846107b3565b83835b8092508193505050600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff161415610822576040517f78b507da00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b9250929050565b60008060006108388585610a30565b509050600080610849888888610b4c565b73ffffffffffffffffffffffffffffffffffffffff16630902f1ac6040518163ffffffff1660e01b815260040160606040518083038186803b15801561088e57600080fd5b505afa1580156108a2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108c69190610e82565b506dffffffffffffffffffffffffffff1691506dffffffffffffffffffffffffffff1691508273ffffffffffffffffffffffffffffffffffffffff168773ffffffffffffffffffffffffffffffffffffffff1614610925578082610928565b81815b8095508196505050505050935093915050565b600080841415610977576040517f098fb56100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008314806109865750600082145b156109bd576040517fbb55fd2700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60006109d46103e586610be290919063ffffffff16565b905060006109eb8483610be290919063ffffffff16565b90506000610a1683610a086103e889610be290919063ffffffff16565b610c3e90919063ffffffff16565b90508082610a2491906110a4565b93505050509392505050565b6000808273ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff161415610a99576040517fbd969eb000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8273ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff1610610ad3578284610ad6565b83835b8092508193505050600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff161415610b45576040517fd92e233d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b9250929050565b60008373ffffffffffffffffffffffffffffffffffffffff1663e6a4390584846040518363ffffffff1660e01b8152600401610b89929190610fff565b60206040518083038186803b158015610ba157600080fd5b505afa158015610bb5573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610bd99190610d9b565b90509392505050565b600080610bf88385610c9a90919063ffffffff16565b809350819250505080610c37576040517f5797276a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5092915050565b600080610c548385610cee90919063ffffffff16565b809350819250505080610c93576040517fa259879500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5092915050565b6000806000841415610cb3576001600091509150610ce7565b6000838502905083858281610ccb57610cca611231565b5b0414610cde576000809250925050610ce7565b60018192509250505b9250929050565b6000806000838501905084811015610d0d576000809250925050610d16565b60018192509250505b9250929050565b600081359050610d2c81611322565b92915050565b600081519050610d4181611322565b92915050565b600081359050610d5681611339565b92915050565b600081519050610d6b81611350565b92915050565b600081359050610d8081611367565b92915050565b600081519050610d958161137e565b92915050565b600060208284031215610db157610db06112be565b5b6000610dbf84828501610d32565b91505092915050565b600080600060608486031215610de157610de06112be565b5b6000610def86828701610d1d565b9350506020610e0086828701610d1d565b9250506040610e1186828701610d1d565b9150509250925092565b60008060008060808587031215610e3557610e346112be565b5b6000610e4387828801610d47565b9450506020610e5487828801610d1d565b9350506040610e6587828801610d1d565b9250506060610e7687828801610d71565b91505092959194509250565b600080600060608486031215610e9b57610e9a6112be565b5b6000610ea986828701610d5c565b9350506020610eba86828701610d5c565b9250506040610ecb86828701610d86565b9150509250925092565b610ede81611109565b82525050565b610eed81611109565b82525050565b610f04610eff82611109565b6111d4565b82525050565b610f1b610f168261111b565b6111e6565b82525050565b6000610f2e602083611043565b9150610f39826112d0565b602082019050919050565b6000610f51600183611043565b9150610f5c826112f9565b600182019050919050565b610f7081611171565b82525050565b6000610f828285610ef3565b601482019150610f928284610ef3565b6014820191508190509392505050565b6000610fad82610f44565b9150610fb98285610ef3565b601482019150610fc98284610f0a565b602082019150610fd882610f21565b91508190509392505050565b6000602082019050610ff96000830184610ee4565b92915050565b60006040820190506110146000830185610ed5565b6110216020830184610ed5565b9392505050565b600060208201905061103d6000830184610f67565b92915050565b600081905092915050565b600061105982611171565b915061106483611171565b9250827fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0382111561109957611098611202565b5b828201905092915050565b60006110af82611171565b91506110ba83611171565b9250826110ca576110c9611231565b5b828204905092915050565b60006110e082611171565b91506110eb83611171565b9250828210156110fe576110fd611202565b5b828203905092915050565b600061111482611151565b9050919050565b6000819050919050565b600061113082611109565b9050919050565b60006dffffffffffffffffffffffffffff82169050919050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000819050919050565b600063ffffffff82169050919050565b600061119682611171565b91507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8214156111c9576111c8611202565b5b600182019050919050565b60006111df826111f0565b9050919050565b6000819050919050565b60006111fb826112c3565b9050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b600080fd5b60008160601b9050919050565b7f96e8ac4277198ff8b6f785478aa9a39f403cb768dd02cbee326c3e7da348845f600082015250565b7fff00000000000000000000000000000000000000000000000000000000000000600082015250565b61132b81611109565b811461133657600080fd5b50565b61134281611125565b811461134d57600080fd5b50565b61135981611137565b811461136457600080fd5b50565b61137081611171565b811461137b57600080fd5b50565b6113878161117b565b811461139257600080fd5b5056fea26469706673582212200cfb8bfcd1e1ce90ae3341161db50ec139df6b1f755b577055e02710b74062a864736f6c63430008070033"; type SwapHelperLibConstructorParams = | [signer?: Signer]