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')
+ })
+ })
+ })
+})