Skip to content

Commit

Permalink
connectors: implement odos connector
Browse files Browse the repository at this point in the history
  • Loading branch information
PedroAraoz committed Aug 12, 2024
1 parent acc770e commit 36863d5
Show file tree
Hide file tree
Showing 14 changed files with 393 additions and 22 deletions.
52 changes: 52 additions & 0 deletions packages/connectors/contracts/interfaces/odos/IOdosV2Connector.sol
Original file line number Diff line number Diff line change
@@ -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 <http://www.gnu.org/licenses/>.

pragma solidity >=0.8.0;

/**
* @title Odos V2 connector interface
*/
interface IOdosV2Connector {
/**
* @dev The token in is the same as the token out
*/
error OdosV2SwapSameToken(address token);

/**
* @dev The amount out is lower than the minimum amount out
*/
error OdosV2BadAmountOut(uint256 amountOut, uint256 minAmountOut);

/**
* @dev The post token in balance is lower than the previous token in balance minus the amount in
*/
error OdosV2BadPostTokenInBalance(uint256 postBalanceIn, uint256 preBalanceIn, uint256 amountIn);

/**
* @dev Tells the reference to Odos aggregation router v2
*/
function odosV2Router() external view returns (address);

/**
* @dev Executes a token swap in Odos 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 Odos aggregation router
*/
function execute(address tokenIn, address tokenOut, uint256 amountIn, uint256 minAmountOut, bytes memory data)
external
returns (uint256 amountOut);
}
70 changes: 70 additions & 0 deletions packages/connectors/contracts/odos/OdosV2Connector.sol
Original file line number Diff line number Diff line change
@@ -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 <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 '../interfaces/odos/IOdosV2Connector.sol';

/**
* @title OdosV2Connector
* @dev Interfaces with Odos V2 to swap tokens
*/
contract OdosV2Connector is IOdosV2Connector {
// Reference to Odos aggregation router v2
address public immutable override odosV2Router;

/**
* @dev Creates a new OdosV2Connector contract
* @param _odosV2Router Odos aggregation router v2 reference
*/
constructor(address _odosV2Router) {
odosV2Router = _odosV2Router;
}

/**
* @dev Executes a token swap in Odos 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 Odos aggregation router
*/
function execute(address tokenIn, address tokenOut, uint256 amountIn, uint256 minAmountOut, bytes memory data)
external
override
returns (uint256 amountOut)
{
if (tokenIn == tokenOut) revert OdosV2SwapSameToken(tokenIn);

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

ERC20Helpers.approve(tokenIn, odosV2Router, amountIn);
Address.functionCall(odosV2Router, data, 'ODOS_V2_SWAP_FAILED');

uint256 postBalanceIn = IERC20(tokenIn).balanceOf(address(this));
bool isPostBalanceInUnexpected = postBalanceIn < preBalanceIn - amountIn;
if (isPostBalanceInUnexpected) revert OdosV2BadPostTokenInBalance(postBalanceIn, preBalanceIn, amountIn);

uint256 postBalanceOut = IERC20(tokenOut).balanceOf(address(this));
amountOut = postBalanceOut - preBalanceOut;
if (amountOut < minAmountOut) revert OdosV2BadAmountOut(amountOut, minAmountOut);
}
}
2 changes: 1 addition & 1 deletion packages/connectors/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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 14845449 --chain-id 8453",
"test:base": "yarn test --fork base --block-number 18341138 --chain-id 8453",
"prepare": "yarn build"
},
"dependencies": {
Expand Down
72 changes: 72 additions & 0 deletions packages/connectors/src/odos.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import axios, { AxiosError } from 'axios'
import { BigNumber, Contract } from 'ethers'

const ODOS_URL = 'https://api.odos.xyz'
export type SwapResponse = { data: { transaction: { data: string } } }

export async function getOdosSwapData(
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.transaction.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> {
const response = await axios.post(
`${ODOS_URL}/sor/quote/v2`,
{
chainId,
inputTokens: [
{
tokenAddress: tokenIn.address,
amount: amountIn.toString(),
},
],
outputTokens: [
{
tokenAddress: tokenOut.address,
proportion: 1,
},
],
userAddr: sender.address,
slippageLimitPercent: slippage < 1 ? slippage * 100 : slippage, // The value is 0.5 -> 0.5%
},
{
headers: {
'Content-Type': 'application/json',
Accept: 'application/json',
},
}
)
const pathId = response.data.pathId
return await axios.post(
`${ODOS_URL}/sor/assemble`,
{
userAddr: sender.address,
pathId: pathId,
},
{
headers: {
'Content-Type': 'application/json',
Accept: 'application/json',
},
}
)
}

This file was deleted.

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,6 @@
"tokenIn": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
"tokenOut": "0x4200000000000000000000000000000000000006",
"amountIn": "100000000000",
"slippage": 0.015,
"data": "0xe21fd0e9000000000000000000000000000000000000000000000000000000000000002000000000000000000000000011ddd59c33c73c44733b4123a86ea5ce57f6e854000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000018000000000000000000000000000000000000000000000000000000000000003c000000000000000000000000000000000000000000000000000000000000000c001010000002902000000b2cc224c1c9fee385f8ad6a55b4d94e92359dc590000000000000000000000174876e800010a833589fcd6edb6e08f4c7c32d4f71b54bda0291342000000000000000000000000000000000000065bf5b11053e734690269c6b9d438f8c9d48f528a000000000000000000000000664f6ec80000004000000000000000000000000000000000000000000000000000000000000000000000000000000000000017d44c4c857500000000000000016b9b293a9f666f53000000000000000000000000833589fcd6edb6e08f4c7c32d4f71b54bda029130000000000000000000000004200000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000016000000000000000000000000000000000000000000000000000000000000001a000000000000000000000000000000000000000000000000000000000000001e000000000000000000000000000000000000000000000000000000000000002000000000000000000000000005bf5b11053e734690269c6b9d438f8c9d48f528a000000000000000000000000000000000000000000000000000000174876e8000000000000000000000000000000000000000000000000016626e9e3fbbaa88800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000220000000000000000000000000000000000000000000000000000000000000000100000000000000000000000011ddd59c33c73c44733b4123a86ea5ce57f6e8540000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000174876e80000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001e37b22536f75726365223a22222c22416d6f756e74496e555344223a22313030303030222c22416d6f756e744f7574555344223a2239393934372e31313231323334313739222c22526566657272616c223a22222c22466c616773223a312c22496e74656772697479496e666f223a7b224b65794944223a2231222c225369676e6174757265223a22505038666d616b304377714e7876674370366e6b7371724a7237667674546c2f416e454565553777516c67537532424f734e4f55434a7a454c767264776841694878344b594655496b41744d6c644461353244365636756a5359574b437238425a4f3448526d3452584e74334674366f6d67356a735a4c41723365425a79664573756b416a79344f3766566e55557a41545a2b4d5975424e77692b544a4d434c734f367a4f36442f71365a54446d516d3234342b5136475149756a794f48733975444243594b59525338384732574c49672f615864336872336f5357624b43736133694b42797459776733386b5454757254466a734f42766d34746530555731673053654c434d7a7a6a4f64584969396379774277475a526a4b6d307643517741473877484d414b346e654a4579616f2b79692f484941315a7a546446466361336f776b6658474b76456c7a5450414b2b2f4c3447513d3d227d7d0000000000000000000000000000000000000000000000000000000000"
"slippage": 0.02,
"data": "0xe21fd0e9000000000000000000000000000000000000000000000000000000000000002000000000000000000000000011ddd59c33c73c44733b4123a86ea5ce57f6e854000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000018000000000000000000000000000000000000000000000000000000000000003c000000000000000000000000000000000000000000000000000000000000000c001010000002902000000b2cc224c1c9fee385f8ad6a55b4d94e92359dc590000000000000000000000174876e800010a833589fcd6edb6e08f4c7c32d4f71b54bda02913420000000000000000000000000000000000000601c1def3b91672704716159c9041aeca392ddffb00000000000000000000000066ba306a000000400000000000000000000000000000000000000000000000000000000000000000000000000000000000002228f508406a0000000000000002093d74e528e3fcc8000000000000000000000000833589fcd6edb6e08f4c7c32d4f71b54bda029130000000000000000000000004200000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000016000000000000000000000000000000000000000000000000000000000000001a000000000000000000000000000000000000000000000000000000000000001e0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000001c1def3b91672704716159c9041aeca392ddffb000000000000000000000000000000000000000000000000000000174876e800000000000000000000000000000000000000000000000001fed0b51e043b967200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000220000000000000000000000000000000000000000000000000000000000000000100000000000000000000000011ddd59c33c73c44733b4123a86ea5ce57f6e8540000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000174876e800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000022b7b22536f75726365223a22222c22416d6f756e74496e555344223a223130303133332e3736363535323434383536222c22416d6f756e744f7574555344223a223130303035312e3432383931383833373938222c22526566657272616c223a22222c22466c616773223a302c22416d6f756e744f7574223a223337353539333034393934393232313637343936222c2254696d657374616d70223a313732333437363932322c22496e74656772697479496e666f223a7b224b65794944223a2231222c225369676e6174757265223a2258425531463135387552347462366d39504a46656f2b6732416f7776776c3246676d556e7a634739635134376538587a4c6b39656d6d57796e45565264664a5a44766b5834724a41416450626a44305654476844486b48526a7344696d3173487a55794a39696359794f536975386f4473396331344675474f524d67386c4c61436b30777531766e30596339424278583031534a7569714e6c562b432f354252316a555a6b44674638564337615643507470336c4946347335544e6a42677a637a48563469317436326245384177456f44774b346a4d304761386c795a305044342f665936373536597174302f553551317a625a6b417a71496e5231676b69534e45316f71352b5859536e374c612b4f627a5444794a3757645065384f43742f42526259454d79614941376d705779562b747967547236384c7565596c4636656e4133665831465052314771614a43486a506e6d74673d3d227d7d000000000000000000000000000000000000000000"
}
Loading

0 comments on commit 36863d5

Please sign in to comment.