Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Instant #2

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
80 changes: 41 additions & 39 deletions AvaxInstantSwap.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@
pragma solidity =0.8.20;

import "@openzeppelin/contracts/access/Ownable.sol";
import "@uniswap/v3-core/contracts/interfaces/IUniswapV3Factory.sol";
import "@uniswap/v3-core/contracts/interfaces/IUniswapV3Pool.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
Expand Down Expand Up @@ -62,8 +60,8 @@ interface IQuoter {
contract AvaxInstantSwap is Ownable, ReentrancyGuard {
using SafeERC20 for IERC20;

ILBRouter public lbRouter;
IQuoter public lbQuoter;
ILBRouter public immutable lbRouter;
IQuoter public immutable lbQuoter;
address public immutable wethToken;
address public immutable usdc;
address public executor;
Expand All @@ -73,19 +71,22 @@ contract AvaxInstantSwap is Ownable, ReentrancyGuard {
address indexed receiver,
address indexed token,
uint256 amountIn,
uint256 amountOut,
uint256 time
);

event ExecutorUpdated(address indexed oldExecutor, address indexed newExecutor);

modifier onlyExecutor() {
require(msg.sender == executor, "not executor");
_;
}

constructor(
address _lbRouter,
address _lbQuoter,
address _usdc,
address _executor,
address _owner
) Ownable(_owner) {
address _executor
) Ownable(msg.sender) {
lbRouter = ILBRouter(_lbRouter);
lbQuoter = IQuoter(_lbQuoter);
wethToken = address(lbRouter.getWNATIVE());
Expand All @@ -95,7 +96,7 @@ contract AvaxInstantSwap is Ownable, ReentrancyGuard {

// To get the estimated path for making a swap
function getQuote(
address[] calldata _path,
address[] memory _path,
uint256 _amountIn
) public view returns (ILBRouter.Path memory) {
// Use the quoter to find the best route for the swap
Expand All @@ -121,36 +122,38 @@ contract AvaxInstantSwap is Ownable, ReentrancyGuard {
address _tokenB,
uint256 _amountIn,
uint256 _minAmountOut,
address[] calldata _path,
address[] memory _path,
address _to,
bool _unwrappETH
) public nonReentrant {
require(_to != address(0), "can not send address(0)");
require(msg.sender == executor, "not executor");

) public nonReentrant onlyExecutor {
if(_tokenB == usdc) {
emit SwapFromUSDC(_to, _tokenB, _amountIn, block.timestamp);
IERC20(usdc).transfer(_to, _amountIn);
emit SwapFromUSDC(_to, _tokenB, _amountIn, _amountIn, block.timestamp);
return;
}
if (IERC20(usdc).allowance(address(this), address(lbRouter)) < _amountIn) {
IERC20(usdc).approve(address(lbRouter), _amountIn);
}
ILBRouter.Path memory pathQuote = getQuote(_path, _amountIn);
// Make LBRouter swap
if (_unwrappETH) {
lbRouter.swapExactTokensForNATIVESupportingFeeOnTransferTokens(
_amountIn,
_minAmountOut, // Amount out min
pathQuote,
payable(_to),
block.timestamp + 1 hours
);
} else {
checkAndApproveAll(usdc, address(lbRouter), _amountIn);

ILBRouter.Path memory pathQuote = getQuote(_path, _amountIn);
uint256 output = lbRouter
.swapExactTokensForTokensSupportingFeeOnTransferTokens(
_amountIn,
_minAmountOut, // Amount out min
pathQuote,
_unwrappETH ? address(this) : _to,
block.timestamp + 10 minutes
);

if (_unwrappETH) {
IWNATIVE(wethToken).withdraw(output);
payable(_to).transfer(output);
}

emit SwapFromUSDC(_to, _tokenB, _amountIn, output, block.timestamp);
lbRouter.swapExactTokensForTokensSupportingFeeOnTransferTokens(
_amountIn,
_minAmountOut, // Amount out min
pathQuote,
_to,
block.timestamp + 1 hours
);
}
emit SwapFromUSDC(_to, _tokenB, _amountIn, block.timestamp);
}

function setExecutor(address _newExecutor) external onlyOwner {
Expand All @@ -165,21 +168,20 @@ contract AvaxInstantSwap is Ownable, ReentrancyGuard {
) internal {
if (IERC20(_token).allowance(address(this), _target) < _amountToCheck) {
IERC20(_token).forceApprove(_target, 0);
IERC20(_token).forceApprove(_target, _amountToCheck);
IERC20(_token).forceApprove(_target, ~uint256(0));
}
}


function recoverStuckETH(address payable _beneficiary) public onlyOwner {
_beneficiary.transfer(address(this).balance);
function recoverStuckETH() public onlyOwner {
payable(owner()).transfer(address(this).balance);
}

function recoverStuckTokens(address _token) external onlyOwner {
uint256 amount = IERC20(_token).balanceOf(address(this));
IERC20(_token).safeTransfer(owner(), amount);
IERC20(_token).transfer(owner(), amount);
}

receive() external payable {}

fallback() external payable {}
}
}
115 changes: 38 additions & 77 deletions OfficialInstantSwap.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@
pragma solidity =0.8.20;

import "@openzeppelin/contracts/access/Ownable.sol";
import "@uniswap/v3-core/contracts/interfaces/IUniswapV3Factory.sol";
import "@uniswap/v3-core/contracts/interfaces/IUniswapV3Pool.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
Expand Down Expand Up @@ -118,10 +116,10 @@ contract OfficialInstantSwap is Ownable, ReentrancyGuard {
IUniswapV2Router02 public v2Router;
IV3SwapRouter public v3Router;

address public immutable USDC;
address public immutable weth;
address public USDC;
address public weth;
address public executor;

error FailedCall();
modifier onlyExecutor() {
require(msg.sender == executor, "not executor");
_;
Expand All @@ -133,7 +131,6 @@ contract OfficialInstantSwap is Ownable, ReentrancyGuard {
address indexed receiver,
address indexed token,
uint256 amountIn,
uint256 amountOut,
uint256 time
);

Expand All @@ -143,9 +140,8 @@ contract OfficialInstantSwap is Ownable, ReentrancyGuard {
address _v2Router,
address _executor,
address _usdc,
address _weth,
address _owner
) Ownable(_owner) {
address _weth
) Ownable(msg.sender) {
v3Router = IV3SwapRouter(_v3Router);
v2Router = IUniswapV2Router02(_v2Router);
executor= _executor;
Expand All @@ -157,98 +153,63 @@ contract OfficialInstantSwap is Ownable, ReentrancyGuard {
address _outputToken,
uint256 _amountIn,
uint256 _minAmountOut,
uint256 _minAmountOutV2Swap,
bool _useV2,
address[] calldata _pathV2,
bytes calldata _pathV3,
address[] memory _pathV2,
bytes memory _pathV3,
address to,
bool unwrapETH
) public nonReentrant onlyExecutor {
require(to != address(0), "can not send address(0)");
// USDC -> Token
uint256 outputAmount;
if(_useV2) {
outputAmount = v2Swap(_pathV2, _amountIn, _minAmountOut, to, unwrapETH);
} else {
outputAmount = v3Swap(USDC, _pathV3, _amountIn, _minAmountOut, to, unwrapETH);
if(_outputToken == USDC) {
IERC20(USDC).transfer(to, _amountIn);
emit SwapFromUSDC(to, USDC, _amountIn, block.timestamp);
return;
}
emit SwapFromUSDC(to, _outputToken, _amountIn, outputAmount, block.timestamp);
}

function directSendUSDC(address to, uint256 amount) external onlyExecutor {
require(to != address(0), "can not send address(0)");
IERC20(USDC).transfer(to, amount);
emit SwapFromUSDC(to, USDC, amount,amount, block.timestamp);
}
// 1. Swap USDC to ETH (and/or final token) on v3
IERC20(USDC).approve(address(v3Router), _amountIn);
IV3SwapRouter.ExactInputParams memory params = IV3SwapRouter.ExactInputParams(
_pathV3, _useV2 || unwrapETH? address(this) : to, _amountIn, _minAmountOut
);

function checkAndApproveAll(address _token, address _target, uint256 _amountToCheck) internal {
if (IERC20(_token).allowance(address(this), _target) < _amountToCheck) {
IERC20(_token).forceApprove(_target, 0);
IERC20(_token).forceApprove(_target, _amountToCheck);
}
}
uint256 wethOrFinalTokenOut = v3Router.exactInput(params);

function v2Swap(
address[] calldata _path,
uint256 _amountIn,
uint256 _minAmountOut, // Slippage in base of 1000 meaning 10 is 1% and 1 is 0.1% where 1000 is 1
address to,
bool unwrapETH
) internal returns (uint256) {
address tokenOut = _path[_path.length - 1];
checkAndApproveAll(_path[0], address(v2Router), _amountIn);
uint256 initial = IERC20(tokenOut).balanceOf(to);
v2Router.swapExactTokensForTokensSupportingFeeOnTransferTokens(
_amountIn,
_minAmountOut,
_path,
unwrapETH ? address(this) : to,
block.timestamp + 10 minutes
);
uint256 finalAmount = IERC20(tokenOut).balanceOf(to);
if (unwrapETH) { // Get ETH at the end
uint256 wethBalance = IERC20(weth).balanceOf(address(this));
IWETH(weth).withdraw(wethBalance);
payable(to).transfer(address(this).balance);
if(_useV2) {
IERC20(weth).approve(address(v2Router), wethOrFinalTokenOut);
v2Router.swapExactTokensForTokensSupportingFeeOnTransferTokens(
wethOrFinalTokenOut,
_minAmountOutV2Swap,
_pathV2,
unwrapETH? address(this) : to,
block.timestamp + 1 hours
);
}
return finalAmount - initial;
}

function v3Swap(
address _tokenIn,
bytes calldata _path,
uint256 _amountIn,
uint256 _minAmountOut,
address to,
bool unwrapETH
) internal returns (uint256 amountOutput) {
checkAndApproveAll(_tokenIn, address(v3Router), _amountIn);
IV3SwapRouter.ExactInputParams memory params = IV3SwapRouter.ExactInputParams(
_path, unwrapETH ? address(this) : to, _amountIn, _minAmountOut
);
amountOutput = v3Router.exactInput( params );
if (unwrapETH) { // Get ETH at the end
uint256 wethBalance = IERC20(weth).balanceOf(address(this));
IWETH(weth).withdraw(wethBalance);
payable(to).transfer(address(this).balance);
if(unwrapETH) {
uint256 wethBalance = IERC20(weth).balanceOf(address(this));
IWETH(weth).withdraw(wethBalance);
// payable(receiverData.userReceiver).transfer(address(this).balance);
(bool success, ) = to.call{value: address(this).balance}("");
if(!success) {
revert FailedCall();
}
}
emit SwapFromUSDC(to, _outputToken, _amountIn, block.timestamp);
}

function setExecutor(address _newExecutor) external onlyOwner {
emit ExecutorUpdated(executor, _newExecutor);
executor = _newExecutor;
}

function recoverStuckETH(address payable _beneficiary) public onlyOwner {
_beneficiary.transfer(address(this).balance);
}

function recoverStuckTokens(address _token) external onlyOwner {
uint256 amount = IERC20(_token).balanceOf(address(this));
IERC20(_token).safeTransfer(owner(), amount);
IERC20(_token).transfer(owner(), amount);
}


receive() external payable {}

fallback() external payable {}
}
}
Loading