Skip to content

Commit

Permalink
Merge branch 'master' into connectors/implement_wormhole_bridge_conne…
Browse files Browse the repository at this point in the history
…ctor
  • Loading branch information
facuspagnuolo authored Jun 26, 2023
2 parents 0d510d2 + 440c32c commit 4d48999
Show file tree
Hide file tree
Showing 24 changed files with 924 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
// 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 <http://www.gnu.org/licenses/>.

pragma solidity ^0.8.0;

import '@openzeppelin/contracts/token/ERC20/IERC20.sol';

interface IAggregationExecutor {
/// @notice propagates information about original msg.sender and executes arbitrary data
function execute(address msgSender) external payable;
}

interface IOneInchV5AggregationRouter {
struct SwapDescription {
IERC20 srcToken;
IERC20 dstToken;
address payable srcReceiver;
address payable dstReceiver;
uint256 amount;
uint256 minReturnAmount;
uint256 flags;
}

/// @notice Performs a swap, delegating all calls encoded in `data` to `executor`. See tests for usage examples
/// @dev router keeps 1 wei of every token on the contract balance for gas optimisations reasons. This affects first swap of every token by leaving 1 wei on the contract.
/// @param executor Aggregation executor that executes calls described in `data`
/// @param desc Swap description
/// @param permit Should contain valid permit that can be used in `IERC20Permit.permit` calls.
/// @param data Encoded calls that `caller` should execute in between of swaps
/// @return returnAmount Resulting token amount
/// @return spentAmount Source token amount
function swap(
IAggregationExecutor executor,
SwapDescription calldata desc,
bytes calldata permit,
bytes calldata data
) external payable returns (uint256 returnAmount, uint256 spentAmount);
}
68 changes: 68 additions & 0 deletions packages/connectors/contracts/swap/1inch-v5/OneInchV5Connector.sol
Original file line number Diff line number Diff line change
@@ -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 <http://www.gnu.org/licenses/>.

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 './IOneInchV5AggregationRouter.sol';

/**
* @title OneInchV5Connector
* @dev Interfaces with 1inch V5 to swap tokens
*/
contract OneInchV5Connector {
// Reference to 1inch aggregation router v5
IOneInchV5AggregationRouter public immutable oneInchV5Router;

/**
* @dev Creates a new OneInchV5Connector contract
* @param _oneInchV5Router 1inch aggregation router v5 reference
*/
constructor(address _oneInchV5Router) {
oneInchV5Router = IOneInchV5AggregationRouter(_oneInchV5Router);
}

/**
* @dev Executes a token swap in 1Inch V5
* @param tokenIn Token to be sent
* @param tokenOut Token to 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 1inch aggregation router
*/
function execute(address tokenIn, address tokenOut, uint256 amountIn, uint256 minAmountOut, bytes memory data)
external
returns (uint256 amountOut)
{
require(tokenIn != tokenOut, '1INCH_V5_SWAP_SAME_TOKEN');

uint256 preBalanceIn = IERC20(tokenIn).balanceOf(address(this));
uint256 preBalanceOut = IERC20(tokenOut).balanceOf(address(this));

ERC20Helpers.approve(tokenIn, address(oneInchV5Router), amountIn);
Address.functionCall(address(oneInchV5Router), data, '1INCH_V5_SWAP_FAILED');

uint256 postBalanceIn = IERC20(tokenIn).balanceOf(address(this));
require(postBalanceIn >= preBalanceIn - amountIn, '1INCH_V5_BAD_TOKEN_IN_BALANCE');

uint256 postBalanceOut = IERC20(tokenOut).balanceOf(address(this));
amountOut = postBalanceOut - preBalanceOut;
require(amountOut >= minAmountOut, '1INCH_V5_MIN_AMOUNT_OUT');
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// 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 <http://www.gnu.org/licenses/>.

pragma solidity >=0.5.0;

interface IUniswapV2Factory {
function getPair(address tokenA, address tokenB) external view returns (address);
}
Original file line number Diff line number Diff line change
@@ -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 <http://www.gnu.org/licenses/>.

pragma solidity >=0.6.2;

interface IUniswapV2Router02 {
function factory() external pure returns (address);

function swapExactTokensForTokens(
uint256 amountIn,
uint256 amountOutMin,
address[] calldata path,
address to,
uint256 deadline
) external returns (uint256[] memory amounts);
}
129 changes: 129 additions & 0 deletions packages/connectors/contracts/swap/uniswap-v2/UniswapV2Connector.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
// 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 <http://www.gnu.org/licenses/>.

pragma solidity ^0.8.0;

import '@openzeppelin/contracts/token/ERC20/IERC20.sol';
import '@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol';

import '@mimic-fi/v3-helpers/contracts/math/UncheckedMath.sol';
import '@mimic-fi/v3-helpers/contracts/utils/Arrays.sol';
import '@mimic-fi/v3-helpers/contracts/utils/ERC20Helpers.sol';

import './IUniswapV2Factory.sol';
import './IUniswapV2Router02.sol';

/**
* @title UniswapV2Connector
* @dev Interfaces with Uniswap V2 to swap tokens
*/
contract UniswapV2Connector {
using UncheckedMath for uint256;

// Reference to UniswapV2 router
IUniswapV2Router02 public immutable uniswapV2Router;

/**
* @dev Initializes the UniswapV2Connector contract
* @param _uniswapV2Router Uniswap V2 router reference
*/
constructor(address _uniswapV2Router) {
uniswapV2Router = IUniswapV2Router02(_uniswapV2Router);
}

/**
* @dev Executes a token swap in Uniswap V2
* @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 hopTokens Optional list of hop-tokens between tokenIn and tokenOut, only used for multi-hops
*/
function execute(
address tokenIn,
address tokenOut,
uint256 amountIn,
uint256 minAmountOut,
address[] memory hopTokens
) external returns (uint256 amountOut) {
require(tokenIn != tokenOut, 'UNI_V2_SWAP_SAME_TOKEN');

uint256 preBalanceIn = IERC20(tokenIn).balanceOf(address(this));
uint256 preBalanceOut = IERC20(tokenOut).balanceOf(address(this));

ERC20Helpers.approve(tokenIn, address(uniswapV2Router), amountIn);
hopTokens.length == 0
? _singleSwap(tokenIn, tokenOut, amountIn, minAmountOut)
: _batchSwap(tokenIn, tokenOut, amountIn, minAmountOut, hopTokens);

uint256 postBalanceIn = IERC20(tokenIn).balanceOf(address(this));
require(postBalanceIn >= preBalanceIn - amountIn, 'UNI_V2_BAD_TOKEN_IN_BALANCE');

uint256 postBalanceOut = IERC20(tokenOut).balanceOf(address(this));
amountOut = postBalanceOut - preBalanceOut;
require(amountOut >= minAmountOut, 'UNI_V2_MIN_AMOUNT_OUT');
}

/**
* @dev Swap two tokens through UniswapV2 using a single hop
* @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
*/
function _singleSwap(address tokenIn, address tokenOut, uint256 amountIn, uint256 minAmountOut)
internal
returns (uint256[] memory)
{
address factory = uniswapV2Router.factory();
address[] memory tokens = Arrays.from(tokenIn, tokenOut);
_validatePool(factory, tokenIn, tokenOut);
return uniswapV2Router.swapExactTokensForTokens(amountIn, minAmountOut, tokens, address(this), block.timestamp);
}

/**
* @dev Swap two tokens through UniswapV2 using a multi hop
* @param tokenIn Token being sent
* @param tokenOut Token being received
* @param amountIn Amount of the first token in the path to be swapped
* @param minAmountOut Minimum amount of the last token in the path willing to receive
* @param hopTokens List of hop-tokens between tokenIn and tokenOut
*/
function _batchSwap(
address tokenIn,
address tokenOut,
uint256 amountIn,
uint256 minAmountOut,
address[] memory hopTokens
) internal returns (uint256[] memory) {
address factory = uniswapV2Router.factory();
address[] memory tokens = Arrays.from(tokenIn, hopTokens, tokenOut);
// No need for checked math since we are using it to compute indexes manually, always within boundaries
for (uint256 i = 0; i < tokens.length.uncheckedSub(1); i = i.uncheckedAdd(1)) {
_validatePool(factory, tokens[i], tokens[i.uncheckedAdd(1)]);
}
return uniswapV2Router.swapExactTokensForTokens(amountIn, minAmountOut, tokens, address(this), block.timestamp);
}

/**
* @dev Validates that there is a pool created for tokenA and tokenB
* @param factory UniswapV2 factory to check against
* @param tokenA First token of the pair
* @param tokenB Second token of the pair
*/
function _validatePool(address factory, address tokenA, address tokenB) private view {
address pool = IUniswapV2Factory(factory).getPair(tokenA, tokenB);
require(pool != address(0), 'INVALID_UNISWAP_POOL');
}
}
47 changes: 47 additions & 0 deletions packages/connectors/src/1inch-v5.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import axios, { AxiosError } from 'axios'
import { BigNumber, Contract } from 'ethers'

const ONE_INCH_URL = 'https://api.1inch.io/v5.0'

export type SwapResponse = { data: { tx: { data: string } } }

export async function get1inchSwapData(
chainId: number,
sender: Contract,
tokenIn: Contract,
tokenOut: Contract,
amountIn: BigNumber,
slippage: number
): Promise<string> {
try {
const response = await getSwap(chainId, sender, tokenIn, tokenOut, amountIn, slippage)
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,
slippage: number
): Promise<SwapResponse> {
return axios.get(`${ONE_INCH_URL}/${chainId}/swap`, {
headers: {
'Content-Type': 'application/json',
Accept: 'application/json',
},
params: {
disableEstimate: true,
fromAddress: sender.address,
fromTokenAddress: tokenIn.address,
toTokenAddress: tokenOut.address,
amount: amountIn.toString(),
slippage: slippage < 1 ? slippage * 100 : slippage,
},
})
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"tokenIn": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
"tokenOut": "0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599",
"amountIn": "10000000000",
"slippage": 0.015,
"data": "0xe449022e00000000000000000000000000000000000000000000000000000002540be40000000000000000000000000000000000000000000000000000000000020a42930000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000200000000000000000000000088e6a0c2ddd26feeb64f039a2c41296fcb3f56408000000000000000000000004585fe77225b41b697c938b018e2ac67ac5a20c0cfee7c08"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"tokenIn": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
"tokenOut": "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2",
"amountIn": "10000000000",
"slippage": 0.015,
"data": "0xe449022e00000000000000000000000000000000000000000000000000000002540be4000000000000000000000000000000000000000000000000004b88924a476a67cd0000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000100000000000000000000000088e6a0c2ddd26feeb64f039a2c41296fcb3f5640cfee7c08"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"tokenIn": "0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599",
"tokenOut": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
"amountIn": "100000000",
"slippage": 0.015,
"data": "0xe449022e0000000000000000000000000000000000000000000000000000000005f5e1000000000000000000000000000000000000000000000000000000000695097f210000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000100000000000000000000000099ac8ca7087fa4a2a1fb6357269965a2014abc35cfee7c08"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"tokenIn": "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2",
"tokenOut": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
"amountIn": "1000000000000000000",
"slippage": 0.015,
"data": "0xe449022e0000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000000000000000000000000000000000006a1d73080000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000180000000000000000000000088e6a0c2ddd26feeb64f039a2c41296fcb3f5640cfee7c08"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"tokenIn": "0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174",
"tokenOut": "0x1bfd67037b42cf73acf2047067bd4f2c47d9bfd6",
"amountIn": "10000000000",
"slippage": 0.01,
"data": "0x12aa3caf000000000000000000000000cfd674f8731e801a4a15c1ae31770960e1afded10000000000000000000000002791bca1f2de4661ed88a30c99a7a9449aa841740000000000000000000000001bfd67037b42cf73acf2047067bd4f2c47d9bfd6000000000000000000000000cfd674f8731e801a4a15c1ae31770960e1afded1000000000000000000000000502dcaf7b2a3ce981f1a6c86cc4634b16c7a836f00000000000000000000000000000000000000000000000000000002540be40000000000000000000000000000000000000000000000000000000000020dd1670000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000001600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000034900000000000000000000000000000000000000000000000000032b0002fd00a0c9e75c48000000000000000006040000000000000000000000000000000000000000000000000002cf00016200a007e5c0d200000000000000000000000000000000000000000000000000013e00004e4820cdc878c037625afe3a98e14fcc56e169f0b5b4112791bca1f2de4661ed88a30c99a7a9449aa84174dd93f59a000000000000000000000000cfd674f8731e801a4a15c1ae31770960e1afded15120817eb46d60762442da3d931ff51a30334ca39b74c2132d05d31c914a87c6611c10748aeb04b58e8f00447dc20382000000000000000000000000c2132d05d31c914a87c6611c10748aeb04b58e8f0000000000000000000000001bfd67037b42cf73acf2047067bd4f2c47d9bfd600000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000d2531e000000000000000000000000cfd674f8731e801a4a15c1ae31770960e1afded1000000000000000000000000910bf2d50fa5e014fd06666f456182d4ab7c8bd200a0c9e75c4800000000000000002e0400000000000000000000000000000000000000000000000000013f00004f02a00000000000000000000000000000000000000000000000000000000000193d96ee63c1e500eef1a9507b3d505f0062f2be9453981255b503c82791bca1f2de4661ed88a30c99a7a9449aa841745120817eb46d60762442da3d931ff51a30334ca39b742791bca1f2de4661ed88a30c99a7a9449aa8417400447dc203820000000000000000000000002791bca1f2de4661ed88a30c99a7a9449aa841740000000000000000000000001bfd67037b42cf73acf2047067bd4f2c47d9bfd6000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000012240b2000000000000000000000000cfd674f8731e801a4a15c1ae31770960e1afded1000000000000000000000000910bf2d50fa5e014fd06666f456182d4ab7c8bd280a06c4eca271bfd67037b42cf73acf2047067bd4f2c47d9bfd61111111254eeb25477b68fb85ed929f73a9605820000000000000000000000000000000000000000000000cfee7c08"
}
Loading

0 comments on commit 4d48999

Please sign in to comment.