From 018af8669f5afe47cfbdf1d8b2fe7be3a749a25d Mon Sep 17 00:00:00 2001 From: lgalende <63872655+lgalende@users.noreply.github.com> Date: Wed, 22 May 2024 11:20:21 -0300 Subject: [PATCH] Connectors: Implement Bebop swap connector (#150) --- .github/scripts/setup-hardhat-config.sh | 4 +- .github/workflows/ci-connectors.yml | 6 +- .../contracts/bebop/BebopConnector.sol | 68 +++++++++++++++++ .../interfaces/bebop/IBebopConnector.sol | 52 +++++++++++++ packages/connectors/package.json | 3 +- packages/connectors/src/bebop.ts | 53 +++++++++++++ packages/connectors/src/hop.ts | 2 +- .../BalancerV2SwapConnector.behavior.ts | 4 +- .../test/bebop/BebopConnector.arbitrum.ts | 25 ++++++ .../test/bebop/BebopConnector.base.ts | 25 ++++++ .../test/bebop/BebopConnector.behavior.ts | 76 +++++++++++++++++++ .../fixtures/42161/212259071/USDC-WETH.json | 6 ++ .../fixtures/42161/212259071/WETH-USDC.json | 6 ++ .../fixtures/8453/14589312/USDC-WETH.json | 6 ++ .../fixtures/8453/14589312/WETH-USDC.json | 6 ++ .../connectors/test/helpers/bebop/index.ts | 74 ++++++++++++++++++ .../test/hop/HopBridgeConnector.gnosis.ts | 10 +-- .../test/hop/HopSwapConnector.arbitrum.ts | 2 +- 18 files changed, 415 insertions(+), 13 deletions(-) create mode 100644 packages/connectors/contracts/bebop/BebopConnector.sol create mode 100644 packages/connectors/contracts/interfaces/bebop/IBebopConnector.sol create mode 100644 packages/connectors/src/bebop.ts create mode 100644 packages/connectors/test/bebop/BebopConnector.arbitrum.ts create mode 100644 packages/connectors/test/bebop/BebopConnector.base.ts create mode 100644 packages/connectors/test/bebop/BebopConnector.behavior.ts create mode 100644 packages/connectors/test/helpers/bebop/fixtures/42161/212259071/USDC-WETH.json create mode 100644 packages/connectors/test/helpers/bebop/fixtures/42161/212259071/WETH-USDC.json create mode 100644 packages/connectors/test/helpers/bebop/fixtures/8453/14589312/USDC-WETH.json create mode 100644 packages/connectors/test/helpers/bebop/fixtures/8453/14589312/WETH-USDC.json create mode 100644 packages/connectors/test/helpers/bebop/index.ts diff --git a/.github/scripts/setup-hardhat-config.sh b/.github/scripts/setup-hardhat-config.sh index 393f6d3a..792c0483 100755 --- a/.github/scripts/setup-hardhat-config.sh +++ b/.github/scripts/setup-hardhat-config.sh @@ -8,6 +8,7 @@ AVALANCHE_URL="$6" BSC_URL="$7" FANTOM_URL="$8" ZKEVM_URL="$9" +BASE_URL="${10}" set -o errexit @@ -24,7 +25,8 @@ echo " \"avalanche\": { \"url\": \"${AVALANCHE_URL}\" }, \"bsc\": { \"url\": \"${BSC_URL}\" }, \"fantom\": { \"url\": \"${FANTOM_URL}\" }, - \"zkevm\": { \"url\": \"${ZKEVM_URL}\" } + \"zkevm\": { \"url\": \"${ZKEVM_URL}\" }, + \"base\": { \"url\": \"${BASE_URL}\" } } } " > $HOME/.hardhat/networks.mimic.json diff --git a/.github/workflows/ci-connectors.yml b/.github/workflows/ci-connectors.yml index b49593d7..32735cbd 100644 --- a/.github/workflows/ci-connectors.yml +++ b/.github/workflows/ci-connectors.yml @@ -44,7 +44,7 @@ jobs: - name: Set up environment uses: ./.github/actions/setup - name: Set up hardhat config - run: .github/scripts/setup-hardhat-config.sh ${{secrets.MAINNET_RPC}} ${{secrets.POLYGON_RPC}} ${{secrets.OPTIMISM_RPC}} ${{secrets.ARBITRUM_RPC}} ${{secrets.GNOSIS_RPC}} ${{secrets.AVALANCHE_RPC}} ${{secrets.BSC_RPC}} ${{secrets.FANTOM_RPC}} ${{secrets.ZKEVM_RPC}} + run: .github/scripts/setup-hardhat-config.sh ${{secrets.MAINNET_RPC}} ${{secrets.POLYGON_RPC}} ${{secrets.OPTIMISM_RPC}} ${{secrets.ARBITRUM_RPC}} ${{secrets.GNOSIS_RPC}} ${{secrets.AVALANCHE_RPC}} ${{secrets.BSC_RPC}} ${{secrets.FANTOM_RPC}} ${{secrets.ZKEVM_RPC}} ${{secrets.BASE_RPC}} - name: Build run: yarn build - name: Test mainnet @@ -64,4 +64,6 @@ jobs: - name: Test fantom run: yarn workspace @mimic-fi/v3-connectors test:fantom - name: Test zkevm - run: yarn workspace @mimic-fi/v3-connectors test:zkevm \ No newline at end of file + run: yarn workspace @mimic-fi/v3-connectors test:zkevm + - name: Test base + run: yarn workspace @mimic-fi/v3-connectors test:base \ No newline at end of file diff --git a/packages/connectors/contracts/bebop/BebopConnector.sol b/packages/connectors/contracts/bebop/BebopConnector.sol new file mode 100644 index 00000000..310f74ae --- /dev/null +++ b/packages/connectors/contracts/bebop/BebopConnector.sol @@ -0,0 +1,68 @@ +// 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/utils/Address.sol'; + +import '@mimic-fi/v3-helpers/contracts/utils/ERC20Helpers.sol'; + +import '../interfaces/bebop/IBebopConnector.sol'; + +/** + * @title BebopConnector + * @dev Interfaces with Bebop to swap tokens + */ +contract BebopConnector is IBebopConnector { + // Reference to Bebop Settlement contract + address public immutable override bebopSettlement; + + /** + * @dev Creates a new BebopConnector contract + * @param _bebopSettlement Address of Bebop Settlement contract + */ + constructor(address _bebopSettlement) { + bebopSettlement = _bebopSettlement; + } + + /** + * @dev Executes a token swap using Bebop + * @param tokenIn Token being sent + * @param tokenOut Token being received + * @param amountIn Amount of tokenIn being swapped + * @param minAmountOut Minimum amount of tokenOut willing to receive + * @param data Calldata to be sent to the Bebop Settlement contract + */ + function execute(address tokenIn, address tokenOut, uint256 amountIn, uint256 minAmountOut, bytes memory data) + external + returns (uint256 amountOut) + { + if (tokenIn == tokenOut) revert BebopSwapSameToken(tokenIn); + + uint256 preBalanceIn = IERC20(tokenIn).balanceOf(address(this)); + uint256 preBalanceOut = IERC20(tokenOut).balanceOf(address(this)); + + ERC20Helpers.approve(tokenIn, bebopSettlement, amountIn); + Address.functionCall(bebopSettlement, data, 'BEBOP_SWAP_FAILED'); + + uint256 postBalanceIn = IERC20(tokenIn).balanceOf(address(this)); + bool isPostBalanceInUnexpected = postBalanceIn < preBalanceIn - amountIn; + if (isPostBalanceInUnexpected) revert BebopBadPostTokenInBalance(postBalanceIn, preBalanceIn, amountIn); + + uint256 postBalanceOut = IERC20(tokenOut).balanceOf(address(this)); + amountOut = postBalanceOut - preBalanceOut; + if (amountOut < minAmountOut) revert BebopBadAmountOut(amountOut, minAmountOut); + } +} diff --git a/packages/connectors/contracts/interfaces/bebop/IBebopConnector.sol b/packages/connectors/contracts/interfaces/bebop/IBebopConnector.sol new file mode 100644 index 00000000..fca643f7 --- /dev/null +++ b/packages/connectors/contracts/interfaces/bebop/IBebopConnector.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 Bebop connector interface + */ +interface IBebopConnector { + /** + * @dev The token in is the same as the token out + */ + error BebopSwapSameToken(address token); + + /** + * @dev The amount out is lower than the minimum amount out + */ + error BebopBadAmountOut(uint256 amountOut, uint256 minAmountOut); + + /** + * @dev The post token in balance is lower than the previous token in balance minus the amount in + */ + error BebopBadPostTokenInBalance(uint256 postBalanceIn, uint256 preBalanceIn, uint256 amountIn); + + /** + * @dev Tells the reference to Bebop Settlement contract + */ + function bebopSettlement() external view returns (address); + + /** + * @dev Executes a token swap using Bebop + * @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 Bebop Settlement contract + */ + function execute(address tokenIn, address tokenOut, uint256 amountIn, uint256 minAmountOut, bytes memory data) + external + returns (uint256 amountOut); +} diff --git a/packages/connectors/package.json b/packages/connectors/package.json index fed6f5b5..cd38cbd8 100644 --- a/packages/connectors/package.json +++ b/packages/connectors/package.json @@ -18,12 +18,13 @@ "test:mainnet": "yarn test --fork mainnet --block-number 17525323 --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 105116582 --chain-id 42161", + "test:arbitrum": "yarn test --fork arbitrum --block-number 212259071 --chain-id 42161", "test:gnosis": "yarn test --fork gnosis --block-number 28580764 --chain-id 100", "test:avalanche": "yarn test --fork avalanche --block-number 31333905 --chain-id 43114", "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", "prepare": "yarn build" }, "dependencies": { diff --git a/packages/connectors/src/bebop.ts b/packages/connectors/src/bebop.ts new file mode 100644 index 00000000..0d9100bf --- /dev/null +++ b/packages/connectors/src/bebop.ts @@ -0,0 +1,53 @@ +import axios, { AxiosError } from 'axios' +import { BigNumber, Contract } from 'ethers' + +const BEBOP_URL = 'https://api.bebop.xyz/pmm' + +const CHAIN_NAMES = { + 42161: 'arbitrum', + 8453: 'base', +} + +export type SwapResponse = { data: { tx: { data: string } } } + +export async function getBebopSwapData( + chainId: number, + sender: Contract, + tokenIn: Contract, + tokenOut: Contract, + amountIn: BigNumber +): Promise { + try { + const response = await getSwap(chainId, sender, tokenIn, tokenOut, amountIn) + return response.data.tx.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 +): Promise { + const chainName = CHAIN_NAMES[chainId] + if (!chainName) throw Error('Unsupported chain id') + + return axios.get(`${BEBOP_URL}/${chainName}/v3/quote`, { + headers: { + 'Content-Type': 'application/json', + Accept: 'application/json', + }, + params: { + taker_address: sender.address, + sell_tokens: tokenIn.address, + buy_tokens: tokenOut.address, + sell_amounts: amountIn.toString(), + gasless: false, + skip_validation: true, + }, + }) +} diff --git a/packages/connectors/src/hop.ts b/packages/connectors/src/hop.ts index 01b04567..4441e136 100644 --- a/packages/connectors/src/hop.ts +++ b/packages/connectors/src/hop.ts @@ -7,7 +7,7 @@ const HOP_URL = 'https://api.hop.exchange/v1' export type QuoteResponse = { data: { bonderFee: string; error: string } } const CHAINS: { [key: number]: string } = { - 1: 'mainnet', + 1: 'ethereum', 137: 'polygon', 100: 'gnosis', 10: 'optimism', diff --git a/packages/connectors/test/balancer/BalancerV2SwapConnector.behavior.ts b/packages/connectors/test/balancer/BalancerV2SwapConnector.behavior.ts index 90d76a31..fea3d614 100644 --- a/packages/connectors/test/balancer/BalancerV2SwapConnector.behavior.ts +++ b/packages/connectors/test/balancer/BalancerV2SwapConnector.behavior.ts @@ -38,7 +38,7 @@ export function itBehavesLikeBalancerV2SwapConnector( const hopTokens = [] context('USDC-WETH', () => { - const amountIn = toUSDC(10e3) + const amountIn = toUSDC(100) it('swaps correctly', async function () { const previousBalance = await weth.balanceOf(this.connector.address) @@ -94,7 +94,7 @@ export function itBehavesLikeBalancerV2SwapConnector( }) context('WBTC-USDC', () => { - const amountIn = toWBTC(1) + const amountIn = toWBTC(0.1) const hopPoolIds = [WETH_USDC_POOL_ID] it('swaps correctly', async function () { diff --git a/packages/connectors/test/bebop/BebopConnector.arbitrum.ts b/packages/connectors/test/bebop/BebopConnector.arbitrum.ts new file mode 100644 index 00000000..6d23ff33 --- /dev/null +++ b/packages/connectors/test/bebop/BebopConnector.arbitrum.ts @@ -0,0 +1,25 @@ +import { deploy } from '@mimic-fi/v3-helpers' + +import { itBehavesLikeBebopConnector } from './BebopConnector.behavior' + +/* eslint-disable no-secrets/no-secrets */ + +const CHAIN = 42161 + +const USDC = '0xFF970A61A04b1cA14834A43f5dE4533eBDDB5CC8' +const WETH = '0x82aF49447D8a07e3bd95BD0d56f35241523fBab1' +const WHALE = '0xEeBe760354F5dcBa195EDe0a3B93901441D0968F' + +const BEBOP_SETTLEMENT = '0xbbbbbBB520d69a9775E85b458C58c648259FAD5F' + +const CHAINLINK_ETH_USD = '0x639fe6ab55c921f74e7fac1ee960c0b6293ba612' + +describe('BebopConnector', () => { + const SLIPPAGE = 0.015 + + before('create bebop connector', async function () { + this.connector = await deploy('BebopConnector', [BEBOP_SETTLEMENT]) + }) + + itBehavesLikeBebopConnector(CHAIN, USDC, WETH, WHALE, SLIPPAGE, CHAINLINK_ETH_USD) +}) diff --git a/packages/connectors/test/bebop/BebopConnector.base.ts b/packages/connectors/test/bebop/BebopConnector.base.ts new file mode 100644 index 00000000..31be5a62 --- /dev/null +++ b/packages/connectors/test/bebop/BebopConnector.base.ts @@ -0,0 +1,25 @@ +import { deploy } from '@mimic-fi/v3-helpers' + +import { itBehavesLikeBebopConnector } from './BebopConnector.behavior' + +/* eslint-disable no-secrets/no-secrets */ + +const CHAIN = 8453 + +const USDC = '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913' +const WETH = '0x4200000000000000000000000000000000000006' +const WHALE = '0xec8d8D4b215727f3476FF0ab41c406FA99b4272C' + +const BEBOP_SETTLEMENT = '0xbbbbbBB520d69a9775E85b458C58c648259FAD5F' + +const CHAINLINK_ETH_USD = '0x71041dddad3595F9CEd3DcCFBe3D1F4b0a16Bb70' + +describe('BebopConnector', () => { + const SLIPPAGE = 0.015 + + before('create bebop connector', async function () { + this.connector = await deploy('BebopConnector', [BEBOP_SETTLEMENT]) + }) + + itBehavesLikeBebopConnector(CHAIN, USDC, WETH, WHALE, SLIPPAGE, CHAINLINK_ETH_USD) +}) diff --git a/packages/connectors/test/bebop/BebopConnector.behavior.ts b/packages/connectors/test/bebop/BebopConnector.behavior.ts new file mode 100644 index 00000000..c21d6fcd --- /dev/null +++ b/packages/connectors/test/bebop/BebopConnector.behavior.ts @@ -0,0 +1,76 @@ +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 { loadOrGetBebopSwapData } from '../helpers/bebop' + +export function itBehavesLikeBebopConnector( + 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(10e3) + + 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 minAmountOut = 0 + const data = await loadOrGetBebopSwapData(CHAIN, this.connector, usdc, weth, amountIn) + await this.connector.connect(whale).execute(USDC, WETH, amountIn, minAmountOut, 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 minAmountOut = 0 + const data = await loadOrGetBebopSwapData(CHAIN, this.connector, weth, usdc, amountIn) + await this.connector.connect(whale).execute(WETH, USDC, amountIn, minAmountOut, 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/helpers/bebop/fixtures/42161/212259071/USDC-WETH.json b/packages/connectors/test/helpers/bebop/fixtures/42161/212259071/USDC-WETH.json new file mode 100644 index 00000000..9affdf68 --- /dev/null +++ b/packages/connectors/test/helpers/bebop/fixtures/42161/212259071/USDC-WETH.json @@ -0,0 +1,6 @@ +{ + "tokenIn": "0xFF970A61A04b1cA14834A43f5dE4533eBDDB5CC8", + "tokenOut": "0x82aF49447D8a07e3bd95BD0d56f35241523fBab1", + "amountIn": "10000000000", + "data": "0x4dcebcba000000000000000000000000000000000000000000000000000000006647a4370000000000000000000000004f0a4ce902616f0e16a7c31075aa5601779ad40500000000000000000000000051c72848c68a965f66fa7a88855f9f7784502a7f0000000000000000000000000000000000000000000000000000031ef93f4f3b000000000000000000000000ff970a61a04b1ca14834a43f5de4533ebddb5cc800000000000000000000000082af49447d8a07e3bd95bd0d56f35241523fbab100000000000000000000000000000000000000000000000000000002540be4000000000000000000000000000000000000000000000000002d0f5f4319e062100000000000000000000000004f0a4ce902616f0e16a7c31075aa5601779ad40500000000000000000000000000000000000000000000000000000000000000007409e45e4bca4e8000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001a00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000041791c1ef4b257ba835057f84321c9e9804a94a1352b8a3961ed1d656fdab3d77f0f8ba4428da58c1c91fcd28472f6e8fce0ba10f9068f1d727e62ce1dbb405cc11b00000000000000000000000000000000000000000000000000000000000000" +} \ No newline at end of file diff --git a/packages/connectors/test/helpers/bebop/fixtures/42161/212259071/WETH-USDC.json b/packages/connectors/test/helpers/bebop/fixtures/42161/212259071/WETH-USDC.json new file mode 100644 index 00000000..84ff1896 --- /dev/null +++ b/packages/connectors/test/helpers/bebop/fixtures/42161/212259071/WETH-USDC.json @@ -0,0 +1,6 @@ +{ + "tokenIn": "0x82aF49447D8a07e3bd95BD0d56f35241523fBab1", + "tokenOut": "0xFF970A61A04b1cA14834A43f5dE4533eBDDB5CC8", + "amountIn": "1000000000000000000", + "data": "0x4dcebcba000000000000000000000000000000000000000000000000000000006647a4390000000000000000000000004f0a4ce902616f0e16a7c31075aa5601779ad40500000000000000000000000051c72848c68a965f66fa7a88855f9f7784502a7f0000000000000000000000000000000000000000000000000000031ef93f4f3c00000000000000000000000082af49447d8a07e3bd95bd0d56f35241523fbab1000000000000000000000000ff970a61a04b1ca14834a43f5de4533ebddb5cc80000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000000000000000000000000000000000000b7761b950000000000000000000000004f0a4ce902616f0e16a7c31075aa5601779ad40500000000000000000000000000000000000000000000000000000000000000004087748dff5aa5e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000418ad1282832998747d3a68672463c5855f61e09a2485d7505c42d2fddaa998a39077cdf67c41cc05c97b49602b9c2412f0cfd8d94c3ff2869b9d146ea9843d9691b00000000000000000000000000000000000000000000000000000000000000" +} \ 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/14589312/USDC-WETH.json new file mode 100644 index 00000000..df5ae968 --- /dev/null +++ b/packages/connectors/test/helpers/bebop/fixtures/8453/14589312/USDC-WETH.json @@ -0,0 +1,6 @@ +{ + "tokenIn": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913", + "tokenOut": "0x4200000000000000000000000000000000000006", + "amountIn": "10000000000", + "data": "0x4dcebcba000000000000000000000000000000000000000000000000000000006647a7a600000000000000000000000034b40ba116d5dec75548a9e9a8f15411461e8c7000000000000000000000000051c72848c68a965f66fa7a88855f9f7784502a7f0000000000000000000000000000000000000000000000000000031ef93f5bf7000000000000000000000000833589fcd6edb6e08f4c7c32d4f71b54bda02913000000000000000000000000420000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000002540be4000000000000000000000000000000000000000000000000002ce1a31db374011a00000000000000000000000034b40ba116d5dec75548a9e9a8f15411461e8c70000000000000000000000000000000000000000000000000000000000000000085543df12d753b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000410503ae0427d1300d5d9092c65def74ef08b144804dd062976cde63a2813ece05729017fa0ccc9ac2b9587a5a347d189aac529cf55b65185a9585e2f4f50b640d1c00000000000000000000000000000000000000000000000000000000000000" +} \ 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/14589312/WETH-USDC.json new file mode 100644 index 00000000..f458a55f --- /dev/null +++ b/packages/connectors/test/helpers/bebop/fixtures/8453/14589312/WETH-USDC.json @@ -0,0 +1,6 @@ +{ + "tokenIn": "0x4200000000000000000000000000000000000006", + "tokenOut": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913", + "amountIn": "1000000000000000000", + "data": "0x4dcebcba000000000000000000000000000000000000000000000000000000006647a7a700000000000000000000000034b40ba116d5dec75548a9e9a8f15411461e8c7000000000000000000000000051c72848c68a965f66fa7a88855f9f7784502a7f0000000000000000000000000000000000000000000000000000031ef93f5bf90000000000000000000000004200000000000000000000000000000000000006000000000000000000000000833589fcd6edb6e08f4c7c32d4f71b54bda029130000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000000000000000000000000000000000000b7b369c400000000000000000000000034b40ba116d5dec75548a9e9a8f15411461e8c70000000000000000000000000000000000000000000000000000000000000000006a0f9771de8210c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001a00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000041252d2f60f204dfa8c3d4ed6950905708d4c6f98d514adaa64fb8fc45f009e0c473ea6273fc4ab2f42c3a1bdb3236978ddca8d36ec1494878f9067f2f995493291b00000000000000000000000000000000000000000000000000000000000000" +} \ No newline at end of file diff --git a/packages/connectors/test/helpers/bebop/index.ts b/packages/connectors/test/helpers/bebop/index.ts new file mode 100644 index 00000000..e92321c5 --- /dev/null +++ b/packages/connectors/test/helpers/bebop/index.ts @@ -0,0 +1,74 @@ +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 { getBebopSwapData } from '../../../src/bebop' + +type Fixture = { + tokenIn: string + tokenOut: string + amountIn: string + slippage: number + data: string +} + +export async function loadOrGetBebopSwapData( + chainId: number, + sender: Contract, + tokenIn: Contract, + tokenOut: Contract, + amountIn: BigNumber +): 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 getBebopSwapData(chainId, sender, tokenIn, tokenOut, amountIn) + await saveFixture(chainId, tokenIn, tokenOut, amountIn, 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, + data: string, + blockNumber: string +): Promise { + const output = { + tokenIn: tokenIn.address, + tokenOut: tokenOut.address, + amountIn: amountIn.toString(), + 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/hop/HopBridgeConnector.gnosis.ts b/packages/connectors/test/hop/HopBridgeConnector.gnosis.ts index d77b7f61..b7e6c913 100644 --- a/packages/connectors/test/hop/HopBridgeConnector.gnosis.ts +++ b/packages/connectors/test/hop/HopBridgeConnector.gnosis.ts @@ -5,7 +5,7 @@ import { itBehavesLikeHopNativeConnector } from './HopL2NativeConnector.behavior /* eslint-disable no-secrets/no-secrets */ -const USDC = '0xDDAfbb505ad214D7b80b1f830fcCc89B60fb7A83' +const USDT = '0x4ECaBa5870353805a9F068101A40E0f32ed605C6' const WXDAI = '0xe91D153E0b41518A2Ce8Dd3D7944Fa863463a97d' describe('HopBridgeConnector', () => { @@ -15,11 +15,11 @@ describe('HopBridgeConnector', () => { this.connector = await deploy('HopBridgeConnector', [WXDAI]) }) - context('USDC', () => { - const WHALE = '0xc66825c5c04b3c2ccd536d626934e16248a63f68' - const HOP_USDC_AMM = '0x76b22b8C1079A44F1211D867D68b1eda76a635A7' + context('USDT', () => { + const WHALE = '0xba12222222228d8ba445958a75a0704d566bf2c8' + const HOP_USDT_AMM = '0x49094a1B3463c4e2E82ca41b8e6A023bdd6E222f' - itBehavesLikeHopERC20Connector(SOURCE_CHAIN_ID, USDC, HOP_USDC_AMM, WHALE) + itBehavesLikeHopERC20Connector(SOURCE_CHAIN_ID, USDT, HOP_USDT_AMM, WHALE) }) context('xDAI', () => { diff --git a/packages/connectors/test/hop/HopSwapConnector.arbitrum.ts b/packages/connectors/test/hop/HopSwapConnector.arbitrum.ts index f4545ab2..6d61f473 100644 --- a/packages/connectors/test/hop/HopSwapConnector.arbitrum.ts +++ b/packages/connectors/test/hop/HopSwapConnector.arbitrum.ts @@ -12,7 +12,7 @@ const WHALE = '0x5bdf85216ec1e38d6458c870992a69e38e03f7ef' const HOP_USDC_DEX = '0x10541b07d8ad2647dc6cd67abd4c03575dade261' describe('HopSwapConnector', () => { - const SLIPPAGE = 0.01 + const SLIPPAGE = 0.015 before('create hop swap connector', async function () { this.connector = await deploy('HopSwapConnector')