diff --git a/packages/connectors/contracts/interfaces/kyberswap/IKyberSwapV2Connector.sol b/packages/connectors/contracts/interfaces/kyberswap/IKyberSwapV2Connector.sol new file mode 100644 index 00000000..61c5e3c4 --- /dev/null +++ b/packages/connectors/contracts/interfaces/kyberswap/IKyberSwapV2Connector.sol @@ -0,0 +1,52 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +pragma solidity >=0.8.0; + +/** + * @title KyberSwap V2 connector interface + */ +interface IKyberSwapV2Connector { + /** + * @dev The token in is the same as the token out + */ + error KyberSwapV2SwapSameToken(address token); + + /** + * @dev The amount out is lower than the minimum amount out + */ + error KyberSwapV2BadAmountOut(uint256 amountOut, uint256 minAmountOut); + + /** + * @dev The post token in balance is lower than the previous token in balance minus the amount in + */ + error KyberSwapV2BadPostTokenInBalance(uint256 postBalanceIn, uint256 preBalanceIn, uint256 amountIn); + + /** + * @dev Tells the reference to KyberSwap aggregation router v2 + */ + function kyberSwapV2Router() external view returns (address); + + /** + * @dev Executes a token swap in KyberSwap V2 + * @param tokenIn Token to be sent + * @param tokenOut Token to be received + * @param amountIn Amount of token in to be swapped + * @param minAmountOut Minimum amount of token out willing to receive + * @param data Calldata to be sent to the KyberSwap aggregation router + */ + function execute(address tokenIn, address tokenOut, uint256 amountIn, uint256 minAmountOut, bytes memory data) + external + returns (uint256 amountOut); +} diff --git a/packages/connectors/contracts/kyberswap/KyberSwapV2Connector.sol b/packages/connectors/contracts/kyberswap/KyberSwapV2Connector.sol new file mode 100644 index 00000000..3f46c87d --- /dev/null +++ b/packages/connectors/contracts/kyberswap/KyberSwapV2Connector.sol @@ -0,0 +1,70 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +pragma solidity ^0.8.0; + +import '@openzeppelin/contracts/token/ERC20/IERC20.sol'; +import '@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol'; +import '@openzeppelin/contracts/utils/Address.sol'; + +import '@mimic-fi/v3-helpers/contracts/utils/ERC20Helpers.sol'; + +import '../interfaces/kyberswap/IKyberSwapV2Connector.sol'; + +/** + * @title KyberSwapV2Connector + * @dev Interfaces with KyberSwap V2 to swap tokens + */ +contract KyberSwapV2Connector is IKyberSwapV2Connector { + // Reference to KyberSwap aggregation router v2 + address public immutable override kyberSwapV2Router; + + /** + * @dev Creates a new KyberSwapV2Connector contract + * @param _kyberSwapV2Router KyberSwap aggregation router v2 reference + */ + constructor(address _kyberSwapV2Router) { + kyberSwapV2Router = _kyberSwapV2Router; + } + + /** + * @dev Executes a token swap in KyberSwap V2 + * @param tokenIn Token to be sent + * @param tokenOut Token to be received + * @param amountIn Amount of token in to be swapped + * @param minAmountOut Minimum amount of token out willing to receive + * @param data Calldata to be sent to the KyberSwap aggregation router + */ + function execute(address tokenIn, address tokenOut, uint256 amountIn, uint256 minAmountOut, bytes memory data) + external + override + returns (uint256 amountOut) + { + if (tokenIn == tokenOut) revert KyberSwapV2SwapSameToken(tokenIn); + + uint256 preBalanceIn = IERC20(tokenIn).balanceOf(address(this)); + uint256 preBalanceOut = IERC20(tokenOut).balanceOf(address(this)); + + ERC20Helpers.approve(tokenIn, kyberSwapV2Router, amountIn); + Address.functionCall(kyberSwapV2Router, data, 'KYBER_SWAP_V2_SWAP_FAILED'); + + uint256 postBalanceIn = IERC20(tokenIn).balanceOf(address(this)); + bool isPostBalanceInUnexpected = postBalanceIn < preBalanceIn - amountIn; + if (isPostBalanceInUnexpected) revert KyberSwapV2BadPostTokenInBalance(postBalanceIn, preBalanceIn, amountIn); + + uint256 postBalanceOut = IERC20(tokenOut).balanceOf(address(this)); + amountOut = postBalanceOut - preBalanceOut; + if (amountOut < minAmountOut) revert KyberSwapV2BadAmountOut(amountOut, minAmountOut); + } +} diff --git a/packages/connectors/package.json b/packages/connectors/package.json index cd38cbd8..45c10dcc 100644 --- a/packages/connectors/package.json +++ b/packages/connectors/package.json @@ -15,7 +15,7 @@ "lint:solidity": "solhint 'contracts/**/*.sol' --config ../../node_modules/solhint-config-mimic/index.js", "lint:typescript": "eslint . --ext .ts", "test": "hardhat test", - "test:mainnet": "yarn test --fork mainnet --block-number 17525323 --chain-id 1", + "test:mainnet": "yarn test --fork mainnet --block-number 19932950 --chain-id 1", "test:polygon": "yarn test --fork polygon --block-number 44153231 --chain-id 137", "test:optimism": "yarn test --fork optimism --block-number 105914596 --chain-id 10", "test:arbitrum": "yarn test --fork arbitrum --block-number 212259071 --chain-id 42161", @@ -24,7 +24,7 @@ "test:bsc": "yarn test --fork bsc --block-number 27925272 --chain-id 56", "test:fantom": "yarn test --fork fantom --block-number 61485606 --chain-id 250", "test:zkevm": "yarn test --fork zkevm --block-number 9014946 --chain-id 1101", - "test:base": "yarn test --fork base --block-number 14589312 --chain-id 8453", + "test:base": "yarn test --fork base --block-number 14845449 --chain-id 8453", "prepare": "yarn build" }, "dependencies": { diff --git a/packages/connectors/src/1inch-v5.ts b/packages/connectors/src/1inch-v5.ts index 60ccc537..b6c633ee 100644 --- a/packages/connectors/src/1inch-v5.ts +++ b/packages/connectors/src/1inch-v5.ts @@ -1,7 +1,8 @@ import axios, { AxiosError } from 'axios' import { BigNumber, Contract } from 'ethers' -const ONE_INCH_URL = 'https://api.1inch.io/v5.0' +const ONE_INCH_URL = 'https://api.1inch.dev/swap/v5.2' +const ONE_INCH_API_KEY = process.env.ONE_INCH_API_KEY export type SwapResponse = { data: { tx: { data: string } } } @@ -32,7 +33,7 @@ async function getSwap( ): Promise { return axios.get(`${ONE_INCH_URL}/${chainId}/swap`, { headers: { - 'Content-Type': 'application/json', + Authorization: `Bearer ${ONE_INCH_API_KEY}`, Accept: 'application/json', }, params: { diff --git a/packages/connectors/src/kyberswap.ts b/packages/connectors/src/kyberswap.ts new file mode 100644 index 00000000..5de3aeb4 --- /dev/null +++ b/packages/connectors/src/kyberswap.ts @@ -0,0 +1,68 @@ +import axios, { AxiosError } from 'axios' +import { BigNumber, Contract } from 'ethers' + +const KYBER_SWAP_URL = 'https://aggregator-api.kyberswap.com' +export type SwapResponse = { data: { data: { data: string } } } + +const CHAINS: { [key: number]: string } = { + 1: 'ethereum', + 8453: 'base', +} + +export async function getKyberSwapSwapData( + chainId: number, + sender: Contract, + tokenIn: Contract, + tokenOut: Contract, + amountIn: BigNumber, + slippage: number +): Promise { + try { + const response = await getSwap(chainId, sender, tokenIn, tokenOut, amountIn, slippage) + return response.data.data.data + } catch (error) { + if (error instanceof AxiosError) throw Error(error.toString() + ' - ' + error.response?.data?.description) + else throw error + } +} + +async function getSwap( + chainId: number, + sender: Contract, + tokenIn: Contract, + tokenOut: Contract, + amountIn: BigNumber, + slippage: number +): Promise { + const chain = CHAINS[chainId] + const response = await axios.get(`${KYBER_SWAP_URL}/${chain}/api/v1/routes`, { + headers: { + 'Content-Type': 'application/json', + Accept: 'application/json', + }, + params: { + tokenIn: tokenIn.address, + tokenOut: tokenOut.address, + amountIn: amountIn.toString(), + saveGas: true, + }, + }) + + // The value is in ranges [0, 2000], 10 means 0.1% + const slippageTolerance = Math.floor(slippage < 1 ? slippage * 10000 : slippage) + return await axios.post( + `${KYBER_SWAP_URL}/${chain}/api/v1/route/build`, + { + routeSummary: response.data.data.routeSummary, + slippageTolerance, + sender: sender.address, + recipient: sender.address, + }, + { + headers: { + 'Content-Type': 'application/json', + Accept: 'application/json', + }, + } + ) +} diff --git a/packages/connectors/test/1inch/OneInchV5Connector.behavior.ts b/packages/connectors/test/1inch/OneInchV5Connector.behavior.ts index 0401b683..cb5e600b 100644 --- a/packages/connectors/test/1inch/OneInchV5Connector.behavior.ts +++ b/packages/connectors/test/1inch/OneInchV5Connector.behavior.ts @@ -84,22 +84,6 @@ export function itBehavesLikeOneInchV5Connector( }) if (WBTC !== ZERO_ADDRESS) { - context('USDC-WBTC', () => { - const amountIn = toUSDC(10e3) - - it('swaps correctly USDC-WBTC', async function () { - const previousBalance = await wbtc.balanceOf(this.connector.address) - await usdc.connect(whale).transfer(this.connector.address, amountIn) - - const data = await loadOrGet1inchSwapData(CHAIN, this.connector, usdc, wbtc, amountIn, SLIPPAGE) - await this.connector.connect(whale).execute(USDC, WBTC, amountIn, 0, data) - - const currentBalance = await wbtc.balanceOf(this.connector.address) - const expectedMinAmountOut = await getExpectedMinAmountOut(USDC, WBTC, amountIn, SLIPPAGE) - expect(currentBalance.sub(previousBalance)).to.be.at.least(expectedMinAmountOut) - }) - }) - context('WBTC-USDC', () => { const amountIn = toWBTC(1) diff --git a/packages/connectors/test/balancer/BalancerV2PoolConnector.mainnet.ts b/packages/connectors/test/balancer/BalancerV2PoolConnector.mainnet.ts index 527e01a9..4a62692c 100644 --- a/packages/connectors/test/balancer/BalancerV2PoolConnector.mainnet.ts +++ b/packages/connectors/test/balancer/BalancerV2PoolConnector.mainnet.ts @@ -50,7 +50,7 @@ describe('BalancerV2PoolConnector', function () { }) context('when the min amount out is not enough', () => { - const minAmountOut = fp(1000) + const minAmountOut = fp(2000) it('reverts', async () => { await expect(connector.join(poolId, tokenIn, joinAmount, minAmountOut)).to.be.revertedWith('BAL#208') diff --git a/packages/connectors/test/balancer/BalancerV2SwapConnector.mainnet.ts b/packages/connectors/test/balancer/BalancerV2SwapConnector.mainnet.ts index 1fde3143..f0e4192c 100644 --- a/packages/connectors/test/balancer/BalancerV2SwapConnector.mainnet.ts +++ b/packages/connectors/test/balancer/BalancerV2SwapConnector.mainnet.ts @@ -15,7 +15,7 @@ const CHAINLINK_USDC_ETH = '0x986b5E1e1755e3C2440e960477f25201B0a8bbD4' const CHAINLINK_WBTC_ETH = '0xdeb288F737066589598e9214E782fa5A8eD689e8' describe('BalancerV2SwapConnector', () => { - const SLIPPAGE = 0.06 + const SLIPPAGE = 0.09 const WETH_USDC_POOL_ID = '0x96646936b91d6b9d7d0c47c496afbf3d6ec7b6f8000200000000000000000019' const WETH_WBTC_POOL_ID = '0xa6f548df93de924d73be7d25dc02554c6bd66db500020000000000000000000e' diff --git a/packages/connectors/test/helpers/1inch-v5/fixtures/1/17525323/USDC-WBTC.json b/packages/connectors/test/helpers/1inch-v5/fixtures/1/17525323/USDC-WBTC.json deleted file mode 100644 index 6f5c3027..00000000 --- a/packages/connectors/test/helpers/1inch-v5/fixtures/1/17525323/USDC-WBTC.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "tokenIn": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", - "tokenOut": "0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599", - "amountIn": "10000000000", - "slippage": 0.015, - "data": "0xe449022e00000000000000000000000000000000000000000000000000000002540be40000000000000000000000000000000000000000000000000000000000020a42930000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000200000000000000000000000088e6a0c2ddd26feeb64f039a2c41296fcb3f56408000000000000000000000004585fe77225b41b697c938b018e2ac67ac5a20c0cfee7c08" -} \ No newline at end of file diff --git a/packages/connectors/test/helpers/1inch-v5/fixtures/1/19932950/USDC-WBTC.json b/packages/connectors/test/helpers/1inch-v5/fixtures/1/19932950/USDC-WBTC.json new file mode 100644 index 00000000..5e2116da --- /dev/null +++ b/packages/connectors/test/helpers/1inch-v5/fixtures/1/19932950/USDC-WBTC.json @@ -0,0 +1,7 @@ +{ + "tokenIn": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", + "tokenOut": "0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599", + "amountIn": "10000000000", + "slippage": 0.015, + "data": "0x12aa3caf000000000000000000000000e37e799d5077682fa0a244d46e5649f71457bd09000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000002260fac5e5542a773aa44fbcfedf7c193bc2c599000000000000000000000000e37e799d5077682fa0a244d46e5649f71457bd09000000000000000000000000f42ec71a4440f5e9871c643696dd6dc9a38911f800000000000000000000000000000000000000000000000000000002540be4000000000000000000000000000000000000000000000000000000000000ddb859000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000001400000000000000000000000000000000000000000000000000000000000000160000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002540000000000000000000000000000000000000000000002360002080001be00a007e5c0d200000000000000000000000000000000000000000000019a0000ca0000b051204dece678ceceb27446b35c672dc7d61f30bad69ea0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800443df02124000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000217507ae7cc9d125d3b0020d6bdbf78f939e0a03fb07f59a73314e73794be0e57ac1b4e5122e0438eb3703bf871e31ce639bd351109c88666eaf939e0a03fb07f59a73314e73794be0e57ac1b4e0044a64833a00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e37e799d5077682fa0a244d46e5649f71457bd0900a0f2fa6b662260fac5e5542a773aa44fbcfedf7c193bc2c5990000000000000000000000000000000000000000000000000000000000e118b800000000000000000000000000001cac80a06c4eca272260fac5e5542a773aa44fbcfedf7c193bc2c5991111111254eeb25477b68fb85ed929f73a9605820000000000000000000000008bb21a3d" +} \ No newline at end of file diff --git a/packages/connectors/test/helpers/1inch-v5/fixtures/1/17525323/USDC-WETH.json b/packages/connectors/test/helpers/1inch-v5/fixtures/1/19932950/USDC-WETH.json similarity index 81% rename from packages/connectors/test/helpers/1inch-v5/fixtures/1/17525323/USDC-WETH.json rename to packages/connectors/test/helpers/1inch-v5/fixtures/1/19932950/USDC-WETH.json index e8b8ba41..1007445a 100644 --- a/packages/connectors/test/helpers/1inch-v5/fixtures/1/17525323/USDC-WETH.json +++ b/packages/connectors/test/helpers/1inch-v5/fixtures/1/19932950/USDC-WETH.json @@ -3,5 +3,5 @@ "tokenOut": "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", "amountIn": "10000000000", "slippage": 0.015, - "data": "0xe449022e00000000000000000000000000000000000000000000000000000002540be4000000000000000000000000000000000000000000000000004b88924a476a67cd0000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000100000000000000000000000088e6a0c2ddd26feeb64f039a2c41296fcb3f5640cfee7c08" + "data": "0xe449022e00000000000000000000000000000000000000000000000000000002540be40000000000000000000000000000000000000000000000000024405ef5d36b90c10000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000100000000000000000000000088e6a0c2ddd26feeb64f039a2c41296fcb3f56408bb21a3d" } \ No newline at end of file diff --git a/packages/connectors/test/helpers/1inch-v5/fixtures/1/17525323/WBTC-USDC.json b/packages/connectors/test/helpers/1inch-v5/fixtures/1/19932950/WBTC-USDC.json similarity index 61% rename from packages/connectors/test/helpers/1inch-v5/fixtures/1/17525323/WBTC-USDC.json rename to packages/connectors/test/helpers/1inch-v5/fixtures/1/19932950/WBTC-USDC.json index 6b2c0092..3561f7ff 100644 --- a/packages/connectors/test/helpers/1inch-v5/fixtures/1/17525323/WBTC-USDC.json +++ b/packages/connectors/test/helpers/1inch-v5/fixtures/1/19932950/WBTC-USDC.json @@ -3,5 +3,5 @@ "tokenOut": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", "amountIn": "100000000", "slippage": 0.015, - "data": "0xe449022e0000000000000000000000000000000000000000000000000000000005f5e1000000000000000000000000000000000000000000000000000000000695097f210000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000100000000000000000000000099ac8ca7087fa4a2a1fb6357269965a2014abc35cfee7c08" + "data": "0xe449022e0000000000000000000000000000000000000000000000000000000005f5e1000000000000000000000000000000000000000000000000000000000f99780dd4000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000020000000000000000000000004585fe77225b41b697c938b018e2ac67ac5a20c080000000000000000000000088e6a0c2ddd26feeb64f039a2c41296fcb3f56408bb21a3d" } \ No newline at end of file diff --git a/packages/connectors/test/helpers/1inch-v5/fixtures/1/17525323/WETH-USDC.json b/packages/connectors/test/helpers/1inch-v5/fixtures/1/19932950/WETH-USDC.json similarity index 82% rename from packages/connectors/test/helpers/1inch-v5/fixtures/1/17525323/WETH-USDC.json rename to packages/connectors/test/helpers/1inch-v5/fixtures/1/19932950/WETH-USDC.json index 1f3d0428..b08c984b 100644 --- a/packages/connectors/test/helpers/1inch-v5/fixtures/1/17525323/WETH-USDC.json +++ b/packages/connectors/test/helpers/1inch-v5/fixtures/1/19932950/WETH-USDC.json @@ -3,5 +3,5 @@ "tokenOut": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", "amountIn": "1000000000000000000", "slippage": 0.015, - "data": "0xe449022e0000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000000000000000000000000000000000006a1d73080000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000180000000000000000000000088e6a0c2ddd26feeb64f039a2c41296fcb3f5640cfee7c08" + "data": "0xe449022e0000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000000000000000000000000000000000000dd28edaa0000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000180000000000000000000000088e6a0c2ddd26feeb64f039a2c41296fcb3f56408bb21a3d" } \ No newline at end of file diff --git a/packages/connectors/test/helpers/bebop/fixtures/8453/14589312/USDC-WETH.json b/packages/connectors/test/helpers/bebop/fixtures/8453/14845449/USDC-WETH.json similarity index 58% rename from packages/connectors/test/helpers/bebop/fixtures/8453/14589312/USDC-WETH.json rename to packages/connectors/test/helpers/bebop/fixtures/8453/14845449/USDC-WETH.json index df5ae968..cccdf1f4 100644 --- a/packages/connectors/test/helpers/bebop/fixtures/8453/14589312/USDC-WETH.json +++ b/packages/connectors/test/helpers/bebop/fixtures/8453/14845449/USDC-WETH.json @@ -2,5 +2,5 @@ "tokenIn": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913", "tokenOut": "0x4200000000000000000000000000000000000006", "amountIn": "10000000000", - "data": "0x4dcebcba000000000000000000000000000000000000000000000000000000006647a7a600000000000000000000000034b40ba116d5dec75548a9e9a8f15411461e8c7000000000000000000000000051c72848c68a965f66fa7a88855f9f7784502a7f0000000000000000000000000000000000000000000000000000031ef93f5bf7000000000000000000000000833589fcd6edb6e08f4c7c32d4f71b54bda02913000000000000000000000000420000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000002540be4000000000000000000000000000000000000000000000000002ce1a31db374011a00000000000000000000000034b40ba116d5dec75548a9e9a8f15411461e8c70000000000000000000000000000000000000000000000000000000000000000085543df12d753b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000410503ae0427d1300d5d9092c65def74ef08b144804dd062976cde63a2813ece05729017fa0ccc9ac2b9587a5a347d189aac529cf55b65185a9585e2f4f50b640d1c00000000000000000000000000000000000000000000000000000000000000" + "data": "0x4dcebcba00000000000000000000000000000000000000000000000000000000664f699a000000000000000000000000faaddc93baf78e89dcf37ba67943e1be8f37bb8c00000000000000000000000051c72848c68a965f66fa7a88855f9f7784502a7f0000000000000000000000000000000000000000000000000000031ef93f76ba000000000000000000000000833589fcd6edb6e08f4c7c32d4f71b54bda02913000000000000000000000000420000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000002540be40000000000000000000000000000000000000000000000000024454cd9c93eef31000000000000000000000000faaddc93baf78e89dcf37ba67943e1be8f37bb8c0000000000000000000000000000000000000000000000000000000000000000311627ff2adae9c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001a00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000041342f31efd4fa1a26eb099aaefeab0c13627cfb74f5dc0b1fda580d1cdc3df8a2775e6e0db3ea045debbac8bb307602500d90ca0ed6c0f02f39f31262ebf3db661b00000000000000000000000000000000000000000000000000000000000000" } \ No newline at end of file diff --git a/packages/connectors/test/helpers/bebop/fixtures/8453/14589312/WETH-USDC.json b/packages/connectors/test/helpers/bebop/fixtures/8453/14845449/WETH-USDC.json similarity index 58% rename from packages/connectors/test/helpers/bebop/fixtures/8453/14589312/WETH-USDC.json rename to packages/connectors/test/helpers/bebop/fixtures/8453/14845449/WETH-USDC.json index f458a55f..3a2ccfd8 100644 --- a/packages/connectors/test/helpers/bebop/fixtures/8453/14589312/WETH-USDC.json +++ b/packages/connectors/test/helpers/bebop/fixtures/8453/14845449/WETH-USDC.json @@ -2,5 +2,5 @@ "tokenIn": "0x4200000000000000000000000000000000000006", "tokenOut": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913", "amountIn": "1000000000000000000", - "data": "0x4dcebcba000000000000000000000000000000000000000000000000000000006647a7a700000000000000000000000034b40ba116d5dec75548a9e9a8f15411461e8c7000000000000000000000000051c72848c68a965f66fa7a88855f9f7784502a7f0000000000000000000000000000000000000000000000000000031ef93f5bf90000000000000000000000004200000000000000000000000000000000000006000000000000000000000000833589fcd6edb6e08f4c7c32d4f71b54bda029130000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000000000000000000000000000000000000b7b369c400000000000000000000000034b40ba116d5dec75548a9e9a8f15411461e8c70000000000000000000000000000000000000000000000000000000000000000006a0f9771de8210c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001a00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000041252d2f60f204dfa8c3d4ed6950905708d4c6f98d514adaa64fb8fc45f009e0c473ea6273fc4ab2f42c3a1bdb3236978ddca8d36ec1494878f9067f2f995493291b00000000000000000000000000000000000000000000000000000000000000" + "data": "0x4dcebcba00000000000000000000000000000000000000000000000000000000664f6a09000000000000000000000000faaddc93baf78e89dcf37ba67943e1be8f37bb8c00000000000000000000000051c72848c68a965f66fa7a88855f9f7784502a7f0000000000000000000000000000000000000000000000000000031ef93f76bd0000000000000000000000004200000000000000000000000000000000000006000000000000000000000000833589fcd6edb6e08f4c7c32d4f71b54bda029130000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000000000000000000000000000000000000e338a472000000000000000000000000faaddc93baf78e89dcf37ba67943e1be8f37bb8c00000000000000000000000000000000000000000000000000000000000000001bcba9f46988af1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000004192ff56a724a1d35c56047cf43dd03f101d1adb44d429787bd9d60e5a542f14f678389cb688bf7039da4df38d5df2f1ad7720ec48650a20d4d46f4e5ce02d300b1b00000000000000000000000000000000000000000000000000000000000000" } \ No newline at end of file diff --git a/packages/connectors/test/helpers/kyberswap/fixtures/1/19932950/USDC-WETH.json b/packages/connectors/test/helpers/kyberswap/fixtures/1/19932950/USDC-WETH.json new file mode 100644 index 00000000..842aba27 --- /dev/null +++ b/packages/connectors/test/helpers/kyberswap/fixtures/1/19932950/USDC-WETH.json @@ -0,0 +1,7 @@ +{ + "tokenIn": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", + "tokenOut": "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", + "amountIn": "10000000000", + "slippage": 0.02, + "data": "0xe21fd0e90000000000000000000000000000000000000000000000000000000000000020000000000000000000000000f081470f5c6fbccf48cc4e5b82dd926409dcdd67000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000006c000000000000000000000000000000000000000000000000000000000000009000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc200000000000000000000000000b0517de6b2b09abd3a7b69d66d85efdb2c7d9400000000000000000000000000000000000000000000000000000000664f74e200000000000000000000000000000000000000000000000000000000000005a0000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000001e000000000000000000000000000000000000000000000000000000000000003400000000000000000000000000000000000000000000000000000000000000040a8d2cb110000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000001200000000000000000000000001116898dda4015ed8ddefb84b6e8bc24528af2d8000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec70000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000002540be40000000000000000000000000000000000000000000000000000000000000000030000000000000000000000001116898dda4015ed8ddefb84b6e8bc24528af2d800000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000040d90ce491000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000100000000000000000000000000d51a44d3fae010294c616388b506acda1bfaae46000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec70000000000000000000000002260fac5e5542a773aa44fbcfedf7c193bc2c59900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000025468f1c700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000004063407a490000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000f081470f5c6fbccf48cc4e5b82dd926409dcdd670000000000000000000000004585fe77225b41b697c938b018e2ac67ac5a20c00000000000000000000000002260fac5e5542a773aa44fbcfedf7c193bc2c599000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000000000000000000000000000000000000000e04e6700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002000000000000000000000026566ef8ad60000000000000000248fc5eca046075e000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000000000000000000000000000000000000000016000000000000000000000000000000000000000000000000000000000000001a000000000000000000000000000000000000000000000000000000000000001e0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000b0517de6b2b09abd3a7b69d66d85efdb2c7d9400000000000000000000000000000000000000000000000000000002540be40000000000000000000000000000000000000000000000000023d493e2c607302e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002200000000000000000000000000000000000000000000000000000000000000001000000000000000000000000f081470f5c6fbccf48cc4e5b82dd926409dcdd67000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000002540be40000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001e57b22536f75726365223a22222c22416d6f756e74496e555344223a22393939342e3038222c22416d6f756e744f7574555344223a22393939312e313832303435373938353932222c22526566657272616c223a22222c22466c616773223a312c22496e74656772697479496e666f223a7b224b65794944223a2231222c225369676e6174757265223a22536b362f744b364a47696563457556464839674f4d554d394e667a682f4a4953476165567239664967544948544379486f4d784f6e656a6d484f71364d596e5638384c6577574249564a5270495831596b63513945362f7652372b7747556a7131442b7a71417179474d622b71423335734e382b6a49546e4b676d55396a577934305431556863444647397a7379542f4551755567503939644946623259334a552f4576384b57543179414f4b2f4b505a5a6f5168664458737961434d5a76786e63534231725651647177684c7466786238713336515651356972736d654b616a563137316b64495377616b496d453956643572544d73626d61327a35694c7162367341345a4e663037326f6450544d6e51546350616772795a77366c6976615257695443517878396e6e6a594a334a6d303277757879324f355358706672614d6f4549682f6b44584f456e69756246736d356546673d3d227d7d000000000000000000000000000000000000000000000000000000" +} \ No newline at end of file diff --git a/packages/connectors/test/helpers/kyberswap/fixtures/1/19932950/WETH-USDC.json b/packages/connectors/test/helpers/kyberswap/fixtures/1/19932950/WETH-USDC.json new file mode 100644 index 00000000..39ed1ed5 --- /dev/null +++ b/packages/connectors/test/helpers/kyberswap/fixtures/1/19932950/WETH-USDC.json @@ -0,0 +1,7 @@ +{ + "tokenIn": "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", + "tokenOut": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", + "amountIn": "1000000000000000000", + "slippage": 0.02, + "data": "0xe21fd0e90000000000000000000000000000000000000000000000000000000000000020000000000000000000000000f081470f5c6fbccf48cc4e5b82dd926409dcdd67000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000006a000000000000000000000000000000000000000000000000000000000000008e000000000000000000000000000000000000000000000000000000000000005e0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000b0517de6b2b09abd3a7b69d66d85efdb2c7d9400000000000000000000000000000000000000000000000000000000664f74e50000000000000000000000000000000000000000000000000000000000000580000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000001a00000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000004063407a490000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000f081470f5c6fbccf48cc4e5b82dd926409dcdd67000000000000000000000000c7bbec68d12a0d1830360f8ec58fa599ba1b0e9b000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec70000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000040d90ce491000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000100000000000000000000000000390f3595bca2df7d23783dfd126427cceb997bf4000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec7000000000000000000000000f939e0a03fb07f59a73314e73794be0e57ac1b4e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000e29180ee000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040d90ce4910000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000001000000000000000000000000004dece678ceceb27446b35c672dc7d61f30bad69e000000000000000000000000f939e0a03fb07f59a73314e73794be0e57ac1b4e000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ce621d3277d2f70db100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000ed7000000000000000000000000e27bd859000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000000000000000000000000000000000000000016000000000000000000000000000000000000000000000000000000000000001a000000000000000000000000000000000000000000000000000000000000001e0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000b0517de6b2b09abd3a7b69d66d85efdb2c7d940000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000000000000000000000000000000000000ddf43f8a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002200000000000000000000000000000000000000000000000000000000000000001000000000000000000000000f081470f5c6fbccf48cc4e5b82dd926409dcdd6700000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001ef7b22536f75726365223a22222c22416d6f756e74496e555344223a22333739322e333739323135313230303933222c22416d6f756e744f7574555344223a22333739372e353137363636383539363332222c22526566657272616c223a22222c22466c616773223a322c22496e74656772697479496e666f223a7b224b65794944223a2231222c225369676e6174757265223a224a304e6d364674636b5a446f566348507273554539716f6b326771785a477938344733764b54746c54786a3762776b43775a6a5569776948437a796966446d796934647a354d4b58737143387234445a31563053756c7872576a4276387936706f6f69326175515030674c79476a356159786e3551684b392b2f342f6c43444a6c687a74783750754e7a6f4357374f76492f79326f30746f5536523377364c51665a785044755a6250656b454e793978665750536d2f7a645149574d56694d4a686b312b684249794d3376714934583049395537676a634c634348325a69627a6936494b4b34543553724937414e59676f2f75443878506770465a714c354272397876315546434a6e52426678564a7269417a746a73694c6575674256516d4a42666c536a7244697534617267664134616d6d76354d68662b7635494a48544463506a6169645768614d674f505544656e4e69756d513d3d227d7d0000000000000000000000000000000000" +} \ No newline at end of file diff --git a/packages/connectors/test/helpers/kyberswap/fixtures/8453/14845449/USDC-WETH.json b/packages/connectors/test/helpers/kyberswap/fixtures/8453/14845449/USDC-WETH.json new file mode 100644 index 00000000..ac27fe95 --- /dev/null +++ b/packages/connectors/test/helpers/kyberswap/fixtures/8453/14845449/USDC-WETH.json @@ -0,0 +1,7 @@ +{ + "tokenIn": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913", + "tokenOut": "0x4200000000000000000000000000000000000006", + "amountIn": "100000000000", + "slippage": 0.015, + "data": "0xe21fd0e9000000000000000000000000000000000000000000000000000000000000002000000000000000000000000011ddd59c33c73c44733b4123a86ea5ce57f6e854000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000018000000000000000000000000000000000000000000000000000000000000003c000000000000000000000000000000000000000000000000000000000000000c001010000002902000000b2cc224c1c9fee385f8ad6a55b4d94e92359dc590000000000000000000000174876e800010a833589fcd6edb6e08f4c7c32d4f71b54bda0291342000000000000000000000000000000000000065bf5b11053e734690269c6b9d438f8c9d48f528a000000000000000000000000664f6ec80000004000000000000000000000000000000000000000000000000000000000000000000000000000000000000017d44c4c857500000000000000016b9b293a9f666f53000000000000000000000000833589fcd6edb6e08f4c7c32d4f71b54bda029130000000000000000000000004200000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000016000000000000000000000000000000000000000000000000000000000000001a000000000000000000000000000000000000000000000000000000000000001e000000000000000000000000000000000000000000000000000000000000002000000000000000000000000005bf5b11053e734690269c6b9d438f8c9d48f528a000000000000000000000000000000000000000000000000000000174876e8000000000000000000000000000000000000000000000000016626e9e3fbbaa88800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000220000000000000000000000000000000000000000000000000000000000000000100000000000000000000000011ddd59c33c73c44733b4123a86ea5ce57f6e8540000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000174876e80000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001e37b22536f75726365223a22222c22416d6f756e74496e555344223a22313030303030222c22416d6f756e744f7574555344223a2239393934372e31313231323334313739222c22526566657272616c223a22222c22466c616773223a312c22496e74656772697479496e666f223a7b224b65794944223a2231222c225369676e6174757265223a22505038666d616b304377714e7876674370366e6b7371724a7237667674546c2f416e454565553777516c67537532424f734e4f55434a7a454c767264776841694878344b594655496b41744d6c644461353244365636756a5359574b437238425a4f3448526d3452584e74334674366f6d67356a735a4c41723365425a79664573756b416a79344f3766566e55557a41545a2b4d5975424e77692b544a4d434c734f367a4f36442f71365a54446d516d3234342b5136475149756a794f48733975444243594b59525338384732574c49672f615864336872336f5357624b43736133694b42797459776733386b5454757254466a734f42766d34746530555731673053654c434d7a7a6a4f64584969396379774277475a526a4b6d307643517741473877484d414b346e654a4579616f2b79692f484941315a7a546446466361336f776b6658474b76456c7a5450414b2b2f4c3447513d3d227d7d0000000000000000000000000000000000000000000000000000000000" +} \ No newline at end of file diff --git a/packages/connectors/test/helpers/kyberswap/fixtures/8453/14845449/WETH-USDC.json b/packages/connectors/test/helpers/kyberswap/fixtures/8453/14845449/WETH-USDC.json new file mode 100644 index 00000000..2fb11cdc --- /dev/null +++ b/packages/connectors/test/helpers/kyberswap/fixtures/8453/14845449/WETH-USDC.json @@ -0,0 +1,7 @@ +{ + "tokenIn": "0x4200000000000000000000000000000000000006", + "tokenOut": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913", + "amountIn": "1000000000000000000", + "slippage": 0.015, + "data": "0xe21fd0e9000000000000000000000000000000000000000000000000000000000000002000000000000000000000000011ddd59c33c73c44733b4123a86ea5ce57f6e854000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000018000000000000000000000000000000000000000000000000000000000000003c000000000000000000000000000000000000000000000000000000000000000c001010000002902000000d0b53d9277642d899df5c87a3966a349a798f22400000000000000000de0b6b3a7640000010a4200000000000000000000000000000000000006833589fcd6edb6e08f4c7c32d4f71b54bda029135bf5b11053e734690269c6b9d438f8c9d48f528a000000000000000000000000664f6f2400000040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ee3000000000000000000000000e33363cc0000000000000000000000004200000000000000000000000000000000000006000000000000000000000000833589fcd6edb6e08f4c7c32d4f71b54bda02913000000000000000000000000000000000000000000000000000000000000016000000000000000000000000000000000000000000000000000000000000001a000000000000000000000000000000000000000000000000000000000000001e000000000000000000000000000000000000000000000000000000000000002000000000000000000000000005bf5b11053e734690269c6b9d438f8c9d48f528a0000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000000000000000000000000000000000000dfcaf06100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000220000000000000000000000000000000000000000000000000000000000000000100000000000000000000000011ddd59c33c73c44733b4123a86ea5ce57f6e85400000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001ef7b22536f75726365223a22222c22416d6f756e74496e555344223a22333831342e363930373338373838323731222c22416d6f756e744f7574555344223a22333831312e313133363034353331303336222c22526566657272616c223a22222c22466c616773223a322c22496e74656772697479496e666f223a7b224b65794944223a2231222c225369676e6174757265223a22624d65762f55584b4c34654a466b7277547a37326e77316c39667058327a37444f6b46546e743975724f7a664d4f4e3856324c665248593963715755686e4c65524233684c394a33646e744132655a45734446494844436b2b5733507a333268366150377056724b4830436549362f37436a5736613963316e6e4b7479314a7433416269594238534a61736d66664a322f7761483964424b4f4935444777654c755657613837613863627969412b3934556b55572b6177557253356b33654c575951634d7639373270677a4336394d5771416a705749343677547343533668685a58304570794879654b74565133653271594f78346d306d61466650364d4f4344513541694832523338436e6336356b69365a61686e6953435938713030533452346161327a46743352476c4e43452f704943637352637538304c67324e6672452f6672536e6f766946762b6634504b3878465565673d3d227d7d0000000000000000000000000000000000" +} \ No newline at end of file diff --git a/packages/connectors/test/helpers/kyberswap/index.ts b/packages/connectors/test/helpers/kyberswap/index.ts new file mode 100644 index 00000000..2128f86d --- /dev/null +++ b/packages/connectors/test/helpers/kyberswap/index.ts @@ -0,0 +1,77 @@ +import { currentBlockNumber } from '@mimic-fi/v3-helpers' +import { BigNumber, Contract } from 'ethers' +import fs from 'fs' +import hre from 'hardhat' +import { HardhatNetworkConfig } from 'hardhat/types' +import path from 'path' + +import { getKyberSwapSwapData } from '../../../src/kyberswap' + +type Fixture = { + tokenIn: string + tokenOut: string + amountIn: string + slippage: number + data: string +} + +export async function loadOrGetKyberSwapSwapData( + chainId: number, + sender: Contract, + tokenIn: Contract, + tokenOut: Contract, + amountIn: BigNumber, + slippage: number +): Promise { + const config = hre.network.config as HardhatNetworkConfig + const blockNumber = config?.forking?.blockNumber?.toString() || (await currentBlockNumber()).toString() + + const fixture = await readFixture(chainId, tokenIn, tokenOut, blockNumber) + if (fixture) return fixture.data + + const data = await getKyberSwapSwapData(chainId, sender, tokenIn, tokenOut, amountIn, slippage) + await saveFixture(chainId, tokenIn, tokenOut, amountIn, slippage, data, blockNumber) + return data +} + +async function readFixture( + chainId: number, + tokenIn: Contract, + tokenOut: Contract, + blockNumber: string +): Promise { + const swapPath = `${await tokenIn.symbol()}-${await tokenOut.symbol()}.json` + const fixturePath = path.join(__dirname, 'fixtures', chainId.toString(), blockNumber, swapPath) + if (!fs.existsSync(fixturePath)) return undefined + return JSON.parse(fs.readFileSync(fixturePath).toString()) +} + +async function saveFixture( + chainId: number, + tokenIn: Contract, + tokenOut: Contract, + amountIn: BigNumber, + slippage: number, + data: string, + blockNumber: string +): Promise { + const output = { + tokenIn: tokenIn.address, + tokenOut: tokenOut.address, + amountIn: amountIn.toString(), + slippage, + data, + } + + const fixturesPath = path.join(__dirname, 'fixtures') + if (!fs.existsSync(fixturesPath)) fs.mkdirSync(fixturesPath) + + const networkPath = path.join(fixturesPath, chainId.toString()) + if (!fs.existsSync(networkPath)) fs.mkdirSync(networkPath) + + const blockNumberPath = path.join(networkPath, blockNumber) + if (!fs.existsSync(blockNumberPath)) fs.mkdirSync(blockNumberPath) + + const swapPath = path.join(blockNumberPath, `${await tokenIn.symbol()}-${await tokenOut.symbol()}.json`) + fs.writeFileSync(swapPath, JSON.stringify(output, null, 2)) +} diff --git a/packages/connectors/test/kyberswap/KyberSwapV2Connector.base.ts b/packages/connectors/test/kyberswap/KyberSwapV2Connector.base.ts new file mode 100644 index 00000000..bf6ce1c9 --- /dev/null +++ b/packages/connectors/test/kyberswap/KyberSwapV2Connector.base.ts @@ -0,0 +1,24 @@ +import { deploy } from '@mimic-fi/v3-helpers' + +import { itBehavesLikeKyberSwapV2Connector } from './KyberSwapV2Connector.behavior' + +/* eslint-disable no-secrets/no-secrets */ +const CHAIN = 8453 + +const USDC = '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913' +const WETH = '0x4200000000000000000000000000000000000006' +const WHALE = '0xec8d8D4b215727f3476FF0ab41c406FA99b4272C' + +const KYBER_SWAP_ROUTER = '0x6131B5fae19EA4f9D964eAc0408E4408b66337b5' + +const CHAINLINK_ETH_USD = '0x71041dddad3595F9CEd3DcCFBe3D1F4b0a16Bb70' + +describe('KyberSwapV2Connector', () => { + const SLIPPAGE = 0.02 + + before('create KyberSwap connector', async function () { + this.connector = await deploy('KyberSwapV2Connector', [KYBER_SWAP_ROUTER]) + }) + + itBehavesLikeKyberSwapV2Connector(CHAIN, USDC, WETH, WHALE, SLIPPAGE, CHAINLINK_ETH_USD) +}) diff --git a/packages/connectors/test/kyberswap/KyberSwapV2Connector.behavior.ts b/packages/connectors/test/kyberswap/KyberSwapV2Connector.behavior.ts new file mode 100644 index 00000000..7e056727 --- /dev/null +++ b/packages/connectors/test/kyberswap/KyberSwapV2Connector.behavior.ts @@ -0,0 +1,74 @@ +import { deployProxy, fp, impersonate, instanceAt, pct, toUSDC, ZERO_ADDRESS } from '@mimic-fi/v3-helpers' +import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers' +import { expect } from 'chai' +import { BigNumber, Contract } from 'ethers' + +import { loadOrGetKyberSwapSwapData } from '../helpers/kyberswap' + +export function itBehavesLikeKyberSwapV2Connector( + CHAIN: number, + USDC: string, + WETH: string, + WHALE: string, + SLIPPAGE: number, + CHAINLINK_ETH_USD: string +): void { + let weth: Contract, usdc: Contract, whale: SignerWithAddress, priceOracle: Contract + + before('load tokens and accounts', async function () { + weth = await instanceAt('IERC20Metadata', WETH) + usdc = await instanceAt('IERC20Metadata', USDC) + whale = await impersonate(WHALE, fp(100)) + }) + + before('create price oracle', async function () { + priceOracle = await deployProxy( + '@mimic-fi/v3-price-oracle/artifacts/contracts/PriceOracle.sol/PriceOracle', + [], + [ZERO_ADDRESS, ZERO_ADDRESS, USDC, [{ base: WETH, quote: USDC, feed: CHAINLINK_ETH_USD }]] + ) + }) + + const getExpectedMinAmountOut = async ( + tokenIn: string, + tokenOut: string, + amountIn: BigNumber, + slippage: number + ): Promise => { + const price = await priceOracle['getPrice(address,address)'](tokenIn, tokenOut) + const expectedAmountOut = price.mul(amountIn).div(fp(1)) + return expectedAmountOut.sub(pct(expectedAmountOut, slippage)) + } + + context('USDC-WETH', () => { + const amountIn = toUSDC(10e4) + + it('swaps correctly USDC-WETH', async function () { + const previousBalance = await weth.balanceOf(this.connector.address) + await usdc.connect(whale).transfer(this.connector.address, amountIn) + + const data = await loadOrGetKyberSwapSwapData(CHAIN, this.connector, usdc, weth, amountIn, SLIPPAGE) + await this.connector.connect(whale).execute(USDC, WETH, amountIn, 0, data) + + const currentBalance = await weth.balanceOf(this.connector.address) + const expectedMinAmountOut = await getExpectedMinAmountOut(USDC, WETH, amountIn, SLIPPAGE) + expect(currentBalance.sub(previousBalance)).to.be.at.least(expectedMinAmountOut) + }) + }) + + context('WETH-USDC', () => { + const amountIn = fp(1) + + it('swaps correctly WETH-USDC', async function () { + const previousBalance = await usdc.balanceOf(this.connector.address) + await weth.connect(whale).transfer(this.connector.address, amountIn) + + const data = await loadOrGetKyberSwapSwapData(CHAIN, this.connector, weth, usdc, amountIn, SLIPPAGE) + await this.connector.connect(whale).execute(WETH, USDC, amountIn, 0, data) + + const currentBalance = await usdc.balanceOf(this.connector.address) + const expectedMinAmountOut = await getExpectedMinAmountOut(WETH, USDC, amountIn, SLIPPAGE) + expect(currentBalance.sub(previousBalance)).to.be.at.least(expectedMinAmountOut) + }) + }) +} diff --git a/packages/connectors/test/kyberswap/KyberSwapV2Connector.mainnet.ts b/packages/connectors/test/kyberswap/KyberSwapV2Connector.mainnet.ts new file mode 100644 index 00000000..8ec9db8f --- /dev/null +++ b/packages/connectors/test/kyberswap/KyberSwapV2Connector.mainnet.ts @@ -0,0 +1,25 @@ +import { deploy } from '@mimic-fi/v3-helpers' + +import { itBehavesLikeKyberSwapV2Connector } from './KyberSwapV2Connector.behavior' + +/* eslint-disable no-secrets/no-secrets */ + +const CHAIN = 1 + +const USDC = '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48' +const WETH = '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2' +const WHALE = '0xf584f8728b874a6a5c7a8d4d387c9aae9172d621' + +const KYBER_SWAP_ROUTER = '0x6131B5fae19EA4f9D964eAc0408E4408b66337b5' + +const CHAINLINK_ETH_USD = '0x5f4eC3Df9cbd43714FE2740f5E3616155c5b8419' + +describe.skip('KyberSwapV2Connector', () => { + const SLIPPAGE = 0.02 + + before('create KyberSwap connector', async function () { + this.connector = await deploy('KyberSwapV2Connector', [KYBER_SWAP_ROUTER]) + }) + + itBehavesLikeKyberSwapV2Connector(CHAIN, USDC, WETH, WHALE, SLIPPAGE, CHAINLINK_ETH_USD) +}) diff --git a/packages/connectors/test/wormhole/WormholeConnector.behavior.ts b/packages/connectors/test/wormhole/WormholeConnector.behavior.ts index 1ef954f0..cbce9b37 100644 --- a/packages/connectors/test/wormhole/WormholeConnector.behavior.ts +++ b/packages/connectors/test/wormhole/WormholeConnector.behavior.ts @@ -21,11 +21,11 @@ export function itBehavesLikeWormholeConnector( let amount: BigNumber let minAmountOut: BigNumber - const relayerFee = sourceChainId == 1 ? bn(270000) : bn(35000000) + const relayerFee = sourceChainId == 1 ? bn(1500000) : bn(35000000) beforeEach('set amount and min amount out', async () => { const decimals = await token.decimals() - amount = bn(300).mul(bn(10).pow(decimals)) + amount = bn(3000).mul(bn(10).pow(decimals)) minAmountOut = amount.sub(relayerFee) }) @@ -53,7 +53,7 @@ export function itBehavesLikeWormholeConnector( }) context('when relayerFee is greater than amount', () => { - const amount = relayerFee.sub(1) + const amount = relayerFee.div(2) it('reverts', async function () { await expect( diff --git a/packages/connectors/test/wormhole/WormholeConnector.mainnet.ts b/packages/connectors/test/wormhole/WormholeConnector.mainnet.ts index 7a0a1103..992135bf 100644 --- a/packages/connectors/test/wormhole/WormholeConnector.mainnet.ts +++ b/packages/connectors/test/wormhole/WormholeConnector.mainnet.ts @@ -7,7 +7,7 @@ import { itBehavesLikeWormholeConnector } from './WormholeConnector.behavior' const USDC = '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48' const WHALE = '0xf584f8728b874a6a5c7a8d4d387c9aae9172d621' -const WORMHOLE_CIRCLE_RELAYER = '0x32DeC3F4A0723Ce02232f87e8772024E0C86d834' +const WORMHOLE_CIRCLE_RELAYER = '0x4cb69FaE7e7Af841e44E1A1c30Af640739378bb2' describe('WormholeConnector', () => { const SOURCE_CHAIN_ID = 1 diff --git a/packages/helpers/src/addresses/chainlink/mainnet.ts b/packages/helpers/src/addresses/chainlink/mainnet.ts index 58eee379..4e8bb3ac 100644 --- a/packages/helpers/src/addresses/chainlink/mainnet.ts +++ b/packages/helpers/src/addresses/chainlink/mainnet.ts @@ -5,3 +5,4 @@ export const USDT_ETH = '0xEe9F2375b4bdF6387aa8265dD4FB8F16512A1d46' export const DAI_ETH = '0x773616E4d11A78F511299002da57A0a94577F1f4' export const MANA_ETH = '0x82A44D92D6c329826dc557c5E1Be6ebeC5D5FeB9' export const BAL_ETH = '0xC1438AA3823A6Ba0C159CfA8D98dF5A994bA120b' +export const ETH_USD = '0x5f4eC3Df9cbd43714FE2740f5E3616155c5b8419' diff --git a/packages/tasks/contracts/interfaces/swap/IKyberSwapV2Swapper.sol b/packages/tasks/contracts/interfaces/swap/IKyberSwapV2Swapper.sol new file mode 100644 index 00000000..a57b7872 --- /dev/null +++ b/packages/tasks/contracts/interfaces/swap/IKyberSwapV2Swapper.sol @@ -0,0 +1,27 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +pragma solidity >=0.8.0; + +import './IBaseSwapTask.sol'; + +/** + * @dev KyberSwap swapper task interface + */ +interface IKyberSwapV2Swapper is IBaseSwapTask { + /** + * @dev Execution function + */ + function call(address tokenIn, uint256 amountIn, uint256 slippage, bytes memory data) external; +} diff --git a/packages/tasks/contracts/swap/KyberSwapV2Swapper.sol b/packages/tasks/contracts/swap/KyberSwapV2Swapper.sol new file mode 100644 index 00000000..a99a58aa --- /dev/null +++ b/packages/tasks/contracts/swap/KyberSwapV2Swapper.sol @@ -0,0 +1,113 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +pragma solidity ^0.8.0; + +import '@mimic-fi/v3-helpers/contracts/math/FixedPoint.sol'; +import '@mimic-fi/v3-helpers/contracts/utils/BytesHelpers.sol'; +import '@mimic-fi/v3-connectors/contracts/interfaces/kyberswap/IKyberSwapV2Connector.sol'; + +import './BaseSwapTask.sol'; +import '../interfaces/swap/IKyberSwapV2Swapper.sol'; + +/** + * @title KyberSwap v2 swapper + * @dev Task that extends the base swap task to use KyberSwap v2 + */ +contract KyberSwapV2Swapper is IKyberSwapV2Swapper, BaseSwapTask { + using FixedPoint for uint256; + using BytesHelpers for bytes; + + // Execution type for relayers + bytes32 public constant override EXECUTION_TYPE = keccak256('KYBER_SWAP_V2_SWAPPER'); + + /** + * @dev KyberSwap v2 swap config. Only used in the initializer. + */ + struct KyberSwapV2SwapConfig { + BaseSwapConfig baseSwapConfig; + } + + /** + * @dev Initializes the KyberSwap v2 swapper + * @param config KyberSwap v2 swap config + */ + function initialize(KyberSwapV2SwapConfig memory config) external virtual initializer { + __KyberSwapV2Swapper_init(config); + } + + /** + * @dev Initializes the KyberSwap v2 swapper. It does call upper contracts initializers. + * @param config KyberSwap v2 swap config + */ + function __KyberSwapV2Swapper_init(KyberSwapV2SwapConfig memory config) internal onlyInitializing { + __BaseSwapTask_init(config.baseSwapConfig); + __KyberSwapV2Swapper_init_unchained(config); + } + + /** + * @dev Initializes the KyberSwap v2 swapper. It does not call upper contracts initializers. + * @param config KyberSwap v2 swap config + */ + function __KyberSwapV2Swapper_init_unchained(KyberSwapV2SwapConfig memory config) internal onlyInitializing { + // solhint-disable-previous-line no-empty-blocks + } + + /** + * @dev Executes the KyberSwap V2 swapper task + */ + function call(address tokenIn, uint256 amountIn, uint256 slippage, bytes memory data) + external + override + authP(authParams(tokenIn, amountIn, slippage)) + { + if (amountIn == 0) amountIn = getTaskAmount(tokenIn); + _beforeKyberSwapV2Swapper(tokenIn, amountIn, slippage); + + address tokenOut = getTokenOut(tokenIn); + uint256 price = _getPrice(tokenIn, tokenOut); + uint256 minAmountOut = amountIn.mulUp(price).mulUp(FixedPoint.ONE - slippage); + bytes memory connectorData = abi.encodeWithSelector( + IKyberSwapV2Connector.execute.selector, + tokenIn, + tokenOut, + amountIn, + minAmountOut, + data + ); + + bytes memory result = ISmartVault(smartVault).execute(connector, connectorData); + _afterKyberSwapV2Swapper(tokenIn, amountIn, slippage, tokenOut, result.toUint256()); + } + + /** + * @dev Before KyberSwap v2 swapper hook + */ + function _beforeKyberSwapV2Swapper(address token, uint256 amount, uint256 slippage) internal virtual { + _beforeBaseSwapTask(token, amount, slippage); + } + + /** + * @dev After KyberSwap v2 swapper hook + */ + function _afterKyberSwapV2Swapper( + address tokenIn, + uint256 amountIn, + uint256 slippage, + address tokenOut, + uint256 amountOut + ) internal virtual { + _afterBaseSwapTask(tokenIn, amountIn, slippage, tokenOut, amountOut); + } +} diff --git a/packages/tasks/contracts/test/swap/KyberSwapV2ConnectorMock.sol b/packages/tasks/contracts/test/swap/KyberSwapV2ConnectorMock.sol new file mode 100644 index 00000000..faa154a6 --- /dev/null +++ b/packages/tasks/contracts/test/swap/KyberSwapV2ConnectorMock.sol @@ -0,0 +1,27 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +pragma solidity ^0.8.0; + +contract KyberSwapV2ConnectorMock { + event LogExecute(address tokenIn, address tokenOut, uint256 amountIn, uint256 minAmountOut, bytes data); + + function execute(address tokenIn, address tokenOut, uint256 amountIn, uint256 minAmountOut, bytes memory data) + external + returns (uint256) + { + emit LogExecute(tokenIn, tokenOut, amountIn, minAmountOut, data); + return minAmountOut; + } +} diff --git a/packages/tasks/test/swap/KyberSwapV2Swapper.test.ts b/packages/tasks/test/swap/KyberSwapV2Swapper.test.ts new file mode 100644 index 00000000..eae4bfba --- /dev/null +++ b/packages/tasks/test/swap/KyberSwapV2Swapper.test.ts @@ -0,0 +1,440 @@ +import { OP } from '@mimic-fi/v3-authorizer' +import { + assertIndirectEvent, + assertNoEvent, + BigNumberish, + deploy, + deployFeedMock, + deployProxy, + deployTokenMock, + fp, + getSigners, + MAX_UINT256, + ZERO_ADDRESS, +} from '@mimic-fi/v3-helpers' +import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/dist/src/signer-with-address' +import { expect } from 'chai' +import { Contract, ContractTransaction } from 'ethers' +import { defaultAbiCoder } from 'ethers/lib/utils' +import { ethers } from 'hardhat' + +import { buildEmptyTaskConfig, deployEnvironment } from '../../src/setup' +import { itBehavesLikeBaseSwapTask } from './BaseSwapTask.behavior' + +describe('KyberSwapV2Swapper', () => { + let task: Contract + let smartVault: Contract, authorizer: Contract, priceOracle: Contract, connector: Contract, owner: SignerWithAddress + + before('setup', async () => { + // eslint-disable-next-line prettier/prettier + ([, owner] = await getSigners()) + ;({ authorizer, smartVault, priceOracle } = await deployEnvironment(owner)) + }) + + before('deploy connector', async () => { + //eslint-disable-next-line no-secrets/no-secrets + connector = await deploy('KyberSwapV2ConnectorMock') + const overrideConnectorCheckRole = smartVault.interface.getSighash('overrideConnectorCheck') + await authorizer.connect(owner).authorize(owner.address, smartVault.address, overrideConnectorCheckRole, []) + await smartVault.connect(owner).overrideConnectorCheck(connector.address, true) + }) + + beforeEach('deploy task', async () => { + task = await deployProxy( + 'KyberSwapV2Swapper', + [], + [ + { + baseSwapConfig: { + connector: connector.address, + tokenOut: ZERO_ADDRESS, + maxSlippage: 0, + customTokensOut: [], + customMaxSlippages: [], + taskConfig: buildEmptyTaskConfig(owner, smartVault), + }, + }, + ] + ) + }) + + describe('swapper', () => { + beforeEach('set params', async function () { + this.owner = owner + this.task = task + this.authorizer = authorizer + }) + + itBehavesLikeBaseSwapTask('KYBER_SWAP_V2_SWAPPER') + }) + + describe('call', () => { + beforeEach('authorize task', async () => { + const executeRole = smartVault.interface.getSighash('execute') + const params = [{ op: OP.EQ, value: connector.address }] + await authorizer.connect(owner).authorize(task.address, smartVault.address, executeRole, params) + }) + + context('when the sender is authorized', () => { + beforeEach('set sender', async () => { + const callRole = task.interface.getSighash('call') + await authorizer.connect(owner).authorize(owner.address, task.address, callRole, []) + task = task.connect(owner) + }) + + context('when the token in is not the zero address', () => { + let tokenIn: Contract + + beforeEach('set token in', async () => { + tokenIn = await deployTokenMock('TKN') + }) + + context('when the amount in is not zero', () => { + const tokenRate = 2 // 1 token in = 2 token out + const thresholdAmount = fp(0.1) // in token out + const thresholdAmountInTokenIn = thresholdAmount.div(tokenRate) // threshold expressed in token in + const amountIn = thresholdAmountInTokenIn + + context('when the token in is allowed', () => { + context('when there is a token out set', () => { + let tokenOut: Contract + let extraCallData = '' + + beforeEach('set default token out', async () => { + tokenOut = await deployTokenMock('TKN') + const setDefaultTokenOutRole = task.interface.getSighash('setDefaultTokenOut') + await authorizer.connect(owner).authorize(owner.address, task.address, setDefaultTokenOutRole, []) + await task.connect(owner).setDefaultTokenOut(tokenOut.address) + }) + + context('when an off-chain oracle is given', () => { + beforeEach('sign off-chain oracle', async () => { + const setSignerRole = priceOracle.interface.getSighash('setSigner') + await authorizer.connect(owner).authorize(owner.address, priceOracle.address, setSignerRole, []) + await priceOracle.connect(owner).setSigner(owner.address, true) + + type PriceData = { base: string; quote: string; rate: BigNumberish; deadline: BigNumberish } + const pricesData: PriceData[] = [ + { + base: tokenIn.address, + quote: tokenOut.address, + rate: fp(tokenRate), + deadline: MAX_UINT256, + }, + { + base: tokenOut.address, + quote: tokenIn.address, + rate: fp(1).mul(fp(1)).div(fp(tokenRate)), + deadline: MAX_UINT256, + }, + ] + + const PricesDataType = 'PriceData(address base, address quote, uint256 rate, uint256 deadline)[]' + const encodedPrices = await defaultAbiCoder.encode([PricesDataType], [pricesData]) + const message = ethers.utils.solidityKeccak256(['bytes'], [encodedPrices]) + const signature = await owner.signMessage(ethers.utils.arrayify(message)) + const data = defaultAbiCoder.encode([PricesDataType, 'bytes'], [pricesData, signature]).slice(2) + const dataLength = defaultAbiCoder.encode(['uint256'], [data.length / 2]).slice(2) + extraCallData = `${data}${dataLength}` + }) + + beforeEach('set threshold', async () => { + const setDefaultTokenThresholdRole = task.interface.getSighash('setDefaultTokenThreshold') + await authorizer + .connect(owner) + .authorize(owner.address, task.address, setDefaultTokenThresholdRole, []) + await task.connect(owner).setDefaultTokenThreshold(tokenOut.address, thresholdAmount, 0) + }) + + const executeTask = async (amountIn, slippage, data): Promise => { + const callTx = await task.populateTransaction.call(tokenIn.address, amountIn, slippage, data) + const callData = `${callTx.data}${extraCallData}` + return owner.sendTransaction({ to: task.address, data: callData }) + } + + context('when the smart vault balance passes the threshold', () => { + beforeEach('fund smart vault', async () => { + await tokenIn.mint(smartVault.address, amountIn) + }) + + context('when the slippage is below the limit', () => { + const data = '0xaabb' + const slippage = fp(0.01) + const expectedAmountOut = amountIn.mul(tokenRate) + const minAmountOut = expectedAmountOut.mul(fp(1).sub(slippage)).div(fp(1)) + + beforeEach('set max slippage', async () => { + const setDefaultMaxSlippageRole = task.interface.getSighash('setDefaultMaxSlippage') + await authorizer + .connect(owner) + .authorize(owner.address, task.address, setDefaultMaxSlippageRole, []) + await task.connect(owner).setDefaultMaxSlippage(slippage) + }) + + const itExecutesTheTaskProperly = (requestedAmount: BigNumberish) => { + it('executes the expected connector', async () => { + const tx = await executeTask(requestedAmount, slippage, data) + + const connectorData = connector.interface.encodeFunctionData('execute', [ + tokenIn.address, + tokenOut.address, + amountIn, + minAmountOut, + data, + ]) + + await assertIndirectEvent(tx, smartVault.interface, 'Executed', { + connector, + data: connectorData, + }) + + await assertIndirectEvent(tx, connector.interface, 'LogExecute', { + tokenIn, + tokenOut, + amountIn, + minAmountOut, + data, + }) + }) + + it('emits an Executed event', async () => { + const tx = await executeTask(requestedAmount, slippage, data) + + await assertIndirectEvent(tx, task.interface, 'Executed') + }) + } + + context('without balance connectors', () => { + const requestedAmount = amountIn + + itExecutesTheTaskProperly(requestedAmount) + + it('does not update any balance connectors', async () => { + const tx = await executeTask(requestedAmount, slippage, data) + + await assertNoEvent(tx, 'BalanceConnectorUpdated') + }) + }) + + context('with balance connectors', () => { + const requestedAmount = 0 + const prevConnectorId = '0x0000000000000000000000000000000000000000000000000000000000000001' + const nextConnectorId = '0x0000000000000000000000000000000000000000000000000000000000000002' + + beforeEach('set balance connectors', async () => { + const setBalanceConnectorsRole = task.interface.getSighash('setBalanceConnectors') + await authorizer + .connect(owner) + .authorize(owner.address, task.address, setBalanceConnectorsRole, []) + await task.connect(owner).setBalanceConnectors(prevConnectorId, nextConnectorId) + }) + + beforeEach('authorize task to update balance connectors', async () => { + const updateBalanceConnectorRole = smartVault.interface.getSighash('updateBalanceConnector') + await authorizer + .connect(owner) + .authorize(task.address, smartVault.address, updateBalanceConnectorRole, []) + }) + + beforeEach('assign amount in to previous balance connector', async () => { + const updateBalanceConnectorRole = smartVault.interface.getSighash('updateBalanceConnector') + await authorizer + .connect(owner) + .authorize(owner.address, smartVault.address, updateBalanceConnectorRole, []) + await smartVault + .connect(owner) + .updateBalanceConnector(prevConnectorId, tokenIn.address, amountIn, true) + }) + + itExecutesTheTaskProperly(requestedAmount) + + it('updates the balance connectors properly', async () => { + const tx = await executeTask(requestedAmount, slippage, data) + + await assertIndirectEvent(tx, smartVault.interface, 'BalanceConnectorUpdated', { + id: prevConnectorId, + token: tokenIn.address, + amount: amountIn, + added: false, + }) + + await assertIndirectEvent(tx, smartVault.interface, 'BalanceConnectorUpdated', { + id: nextConnectorId, + token: tokenOut.address, + amount: minAmountOut, + added: true, + }) + }) + }) + }) + + context('when the slippage is above the limit', () => { + const slippage = fp(0.01) + + it('reverts', async () => { + await expect(executeTask(amountIn, slippage, '0x')).to.be.revertedWith('TaskSlippageAboveMax') + }) + }) + }) + + context('when the smart vault balance does not pass the threshold', () => { + const amountIn = thresholdAmountInTokenIn.div(2) + + beforeEach('fund smart vault', async () => { + await tokenIn.mint(smartVault.address, amountIn) + }) + + it('reverts', async () => { + await expect(executeTask(amountIn, 0, '0x')).to.be.revertedWith('TaskTokenThresholdNotMet') + }) + }) + }) + + context('when no off-chain oracle is given', () => { + context('when an on-chain oracle is given', () => { + beforeEach('set price feed', async () => { + const feed = await deployFeedMock(fp(tokenRate), 18) + const setFeedRole = priceOracle.interface.getSighash('setFeed') + await authorizer.connect(owner).authorize(owner.address, priceOracle.address, setFeedRole, []) + await priceOracle.connect(owner).setFeed(tokenIn.address, tokenOut.address, feed.address) + }) + + beforeEach('set threshold', async () => { + const setDefaultTokenThresholdRole = task.interface.getSighash('setDefaultTokenThreshold') + await authorizer + .connect(owner) + .authorize(owner.address, task.address, setDefaultTokenThresholdRole, []) + await task.connect(owner).setDefaultTokenThreshold(tokenOut.address, thresholdAmount, 0) + }) + + context('when the smart vault balance passes the threshold', () => { + beforeEach('fund smart vault', async () => { + await tokenIn.mint(smartVault.address, amountIn) + }) + + context('when the slippage is below the limit', () => { + const data = '0xaabb' + const slippage = fp(0.01) + const expectedAmountOut = amountIn.mul(tokenRate) + const minAmountOut = expectedAmountOut.mul(fp(1).sub(slippage)).div(fp(1)) + + beforeEach('set max slippage', async () => { + const setDefaultMaxSlippageRole = task.interface.getSighash('setDefaultMaxSlippage') + await authorizer + .connect(owner) + .authorize(owner.address, task.address, setDefaultMaxSlippageRole, []) + await task.connect(owner).setDefaultMaxSlippage(slippage) + }) + + it('executes the expected connector', async () => { + const tx = await task.call(tokenIn.address, amountIn, slippage, data) + + const connectorData = connector.interface.encodeFunctionData('execute', [ + tokenIn.address, + tokenOut.address, + amountIn, + minAmountOut, + data, + ]) + + await assertIndirectEvent(tx, smartVault.interface, 'Executed', { + connector, + data: connectorData, + }) + + await assertIndirectEvent(tx, connector.interface, 'LogExecute', { + tokenIn, + tokenOut, + amountIn, + minAmountOut, + data, + }) + }) + + it('emits an Executed event', async () => { + const tx = await task.call(tokenIn.address, amountIn, slippage, data) + + await assertIndirectEvent(tx, task.interface, 'Executed') + }) + }) + + context('when the slippage is above the limit', () => { + const slippage = fp(0.01) + + it('reverts', async () => { + await expect(task.call(tokenIn.address, amountIn, slippage, '0x')).to.be.revertedWith( + 'TaskSlippageAboveMax' + ) + }) + }) + }) + + context('when the smart vault balance does not pass the threshold', () => { + const amountIn = thresholdAmountInTokenIn.div(2) + + beforeEach('fund smart vault', async () => { + await tokenIn.mint(smartVault.address, amountIn) + }) + + it('reverts', async () => { + await expect(task.call(tokenIn.address, amountIn, 0, '0x')).to.be.revertedWith( + 'TaskTokenThresholdNotMet' + ) + }) + }) + }) + + context('when no on-chain oracle is given', () => { + it('reverts', async () => { + // TODO: Hardhat does not decode price oracle error properly + await expect(task.call(tokenIn.address, amountIn, 0, '0x')).to.be.reverted + }) + }) + }) + }) + + context('when the token out is not set', () => { + it('reverts', async () => { + await expect(task.call(tokenIn.address, amountIn, 0, '0x')).to.be.revertedWith('TaskTokenOutNotSet') + }) + }) + }) + + context('when the token in is denied', () => { + beforeEach('deny token in', async () => { + const setTokensAcceptanceListRole = task.interface.getSighash('setTokensAcceptanceList') + await authorizer.connect(owner).authorize(owner.address, task.address, setTokensAcceptanceListRole, []) + await task.connect(owner).setTokensAcceptanceList([tokenIn.address], [true]) + }) + + it('reverts', async () => { + await expect(task.call(tokenIn.address, 0, 0, '0x')).to.be.revertedWith('TaskTokenNotAllowed') + }) + }) + }) + + context('when the amount in is zero', () => { + const amountIn = 0 + + it('reverts', async () => { + await expect(task.call(tokenIn.address, amountIn, 0, '0x')).to.be.revertedWith('TaskAmountZero') + }) + }) + }) + + context('when the token in is the zero address', () => { + const tokenIn = ZERO_ADDRESS + + it('reverts', async () => { + await expect(task.call(tokenIn, 0, 0, '0x')).to.be.revertedWith('TaskTokenZero') + }) + }) + }) + + context('when the sender is not authorized', () => { + it('reverts', async () => { + await expect(task.call(ZERO_ADDRESS, 0, 0, '0x')).to.be.revertedWith('AuthSenderNotAllowed') + }) + }) + }) +})