Created
October 22, 2024 06:21
-
-
Save korrio/8bd3792f3dad661d25ba2247d1917500 to your computer and use it in GitHub Desktop.
V3.sol
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// SPDX-License-Identifier: MIT | |
pragma solidity ^0.8.20; | |
import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; | |
import "@openzeppelin/contracts/access/Ownable.sol"; | |
import "@uniswap/v3-periphery/contracts/interfaces/ISwapRouter.sol"; | |
import "@uniswap/v3-periphery/contracts/interfaces/IQuoter.sol"; | |
import "@uniswap/v3-periphery/contracts/libraries/TransferHelper.sol"; | |
contract CrossDexAtomicSwap is Ownable { | |
// Router addresses for Base chain | |
address public constant UNISWAP_V3_ROUTER = 0x2626664c2603336E57B271c5C0b26F421741e481; | |
address public constant SUSHISWAP_V3_ROUTER = 0x0BE808376Ecb75a5CF9bB6D237d16cd37893d904; | |
// Quoter addresses | |
address public constant UNISWAP_QUOTER = 0x3d4e44Eb1374240CE5F1B871ab261CD16335B76a; | |
address public constant SUSHISWAP_QUOTER = 0xb1E835Dc2785b52265711e17fCCb0fd018226a6e; | |
ISwapRouter public immutable uniswapRouter; | |
ISwapRouter public immutable sushiswapRouter; | |
IQuoter public immutable uniswapQuoter; | |
IQuoter public immutable sushiswapQuoter; | |
uint24 public constant poolFee = 3000; // 0.3% | |
constructor() Ownable(msg.sender) { | |
uniswapRouter = ISwapRouter(UNISWAP_V3_ROUTER); | |
sushiswapRouter = ISwapRouter(SUSHISWAP_V3_ROUTER); | |
uniswapQuoter = IQuoter(UNISWAP_QUOTER); | |
sushiswapQuoter = IQuoter(SUSHISWAP_QUOTER); | |
} | |
function atomicSwapWithBestRate( | |
address tokenIn, | |
address tokenOut, | |
uint256 amountIn, | |
uint256 minAmountOut, | |
bool checkUniswapFirst | |
) external returns (uint256 amountOut) { | |
// Transfer tokens from user to contract | |
require( | |
IERC20(tokenIn).transferFrom(msg.sender, address(this), amountIn), | |
"TransferFrom failed" | |
); | |
// Approve routers | |
TransferHelper.safeApprove(tokenIn, UNISWAP_V3_ROUTER, amountIn); | |
TransferHelper.safeApprove(tokenIn, SUSHISWAP_V3_ROUTER, amountIn); | |
// Prepare swap parameters | |
ISwapRouter.ExactInputSingleParams memory params = ISwapRouter | |
.ExactInputSingleParams({ | |
tokenIn: tokenIn, | |
tokenOut: tokenOut, | |
fee: poolFee, | |
recipient: address(this), | |
deadline: block.timestamp + 300, | |
amountIn: amountIn, | |
amountOutMinimum: 0, // We'll check total amount at the end | |
sqrtPriceLimitX96: 0 | |
}); | |
// Try first DEX | |
uint256 firstAmount; | |
try | |
(checkUniswapFirst ? uniswapRouter : sushiswapRouter).exactInputSingle( | |
params | |
) | |
returns (uint256 amount) { | |
firstAmount = amount; | |
} catch { | |
firstAmount = 0; | |
} | |
// Try second DEX | |
uint256 secondAmount; | |
if (firstAmount == 0) { | |
try | |
(checkUniswapFirst ? sushiswapRouter : uniswapRouter) | |
.exactInputSingle(params) | |
returns (uint256 amount) { | |
secondAmount = amount; | |
} catch { | |
secondAmount = 0; | |
} | |
} | |
// Get best amount | |
amountOut = firstAmount > secondAmount ? firstAmount : secondAmount; | |
require(amountOut >= minAmountOut, "Insufficient output amount"); | |
// Transfer tokens back to user | |
require( | |
IERC20(tokenOut).transfer(msg.sender, amountOut), | |
"Transfer failed" | |
); | |
// Refund unused tokenIn if any | |
uint256 remainingBalance = IERC20(tokenIn).balanceOf(address(this)); | |
if (remainingBalance > 0) { | |
require( | |
IERC20(tokenIn).transfer(msg.sender, remainingBalance), | |
"Refund failed" | |
); | |
} | |
} | |
// Function to get quote from both DEXes using Quoter contracts | |
function getQuotes( | |
address tokenIn, | |
address tokenOut, | |
uint256 amountIn | |
) external returns (uint256 uniswapQuote, uint256 sushiswapQuote) { | |
try uniswapQuoter.quoteExactInputSingle( | |
tokenIn, | |
tokenOut, | |
poolFee, | |
amountIn, | |
0 | |
) returns (uint256 amount) { | |
uniswapQuote = amount; | |
} catch { | |
uniswapQuote = 0; | |
} | |
try sushiswapQuoter.quoteExactInputSingle( | |
tokenIn, | |
tokenOut, | |
poolFee, | |
amountIn, | |
0 | |
) returns (uint256 amount) { | |
sushiswapQuote = amount; | |
} catch { | |
sushiswapQuote = 0; | |
} | |
} | |
// Emergency withdraw function | |
function emergencyWithdraw( | |
address token, | |
address to | |
) external onlyOwner { | |
uint256 balance = IERC20(token).balanceOf(address(this)); | |
require(balance > 0, "No balance to withdraw"); | |
require(IERC20(token).transfer(to, balance), "Withdraw failed"); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment