diff --git a/lib/v2-periphery b/lib/v2-periphery index cc85976..0335e8f 160000 --- a/lib/v2-periphery +++ b/lib/v2-periphery @@ -1 +1 @@ -Subproject commit cc85976620d4a00453256ca687acc157fffc9e3e +Subproject commit 0335e8f7e1bd1e8d8329fd300aea2ef2f36dd19f diff --git a/src/IUniswapV2Router01.sol b/src/IUniswapV2Router01.sol new file mode 100644 index 0000000..5ee973e --- /dev/null +++ b/src/IUniswapV2Router01.sol @@ -0,0 +1,95 @@ +pragma solidity >=0.6.2; + +interface IUniswapV2Router01 { + function factory() external pure returns (address); + function WETH() external pure returns (address); + + function addLiquidity( + address tokenA, + address tokenB, + uint amountADesired, + uint amountBDesired, + uint amountAMin, + uint amountBMin, + address to, + uint deadline + ) external returns (uint amountA, uint amountB, uint liquidity); + function addLiquidityETH( + address token, + uint amountTokenDesired, + uint amountTokenMin, + uint amountETHMin, + address to, + uint deadline + ) external payable returns (uint amountToken, uint amountETH, uint liquidity); + function removeLiquidity( + address tokenA, + address tokenB, + uint liquidity, + uint amountAMin, + uint amountBMin, + address to, + uint deadline + ) external returns (uint amountA, uint amountB); + function removeLiquidityETH( + address token, + uint liquidity, + uint amountTokenMin, + uint amountETHMin, + address to, + uint deadline + ) external returns (uint amountToken, uint amountETH); + function removeLiquidityWithPermit( + address tokenA, + address tokenB, + uint liquidity, + uint amountAMin, + uint amountBMin, + address to, + uint deadline, + bool approveMax, uint8 v, bytes32 r, bytes32 s + ) external returns (uint amountA, uint amountB); + function removeLiquidityETHWithPermit( + address token, + uint liquidity, + uint amountTokenMin, + uint amountETHMin, + address to, + uint deadline, + bool approveMax, uint8 v, bytes32 r, bytes32 s + ) external returns (uint amountToken, uint amountETH); + function swapExactTokensForTokens( + uint amountIn, + uint amountOutMin, + address[] calldata path, + address to, + uint deadline + ) external returns (uint[] memory amounts); + function swapTokensForExactTokens( + uint amountOut, + uint amountInMax, + address[] calldata path, + address to, + uint deadline + ) external returns (uint[] memory amounts); + function swapExactETHForTokens(uint amountOutMin, address[] calldata path, address to, uint deadline) + external + payable + returns (uint[] memory amounts); + function swapTokensForExactETH(uint amountOut, uint amountInMax, address[] calldata path, address to, uint deadline) + external + returns (uint[] memory amounts); + function swapExactTokensForETH(uint amountIn, uint amountOutMin, address[] calldata path, address to, uint deadline) + external + returns (uint[] memory amounts); + function swapETHForExactTokens(uint amountOut, address[] calldata path, address to, uint deadline) + external + payable + returns (uint[] memory amounts); + + function quote(uint amountA, uint reserveA, uint reserveB) external pure returns (uint amountB); + function getAmountOut(uint amountIn, uint reserveIn, uint reserveOut) external pure returns (uint amountOut); + function getAmountIn(uint amountOut, uint reserveIn, uint reserveOut) external pure returns (uint amountIn); + function getAmountsOut(uint amountIn, address[] calldata path) external view returns (uint[] memory amounts); + function getAmountsIn(uint amountOut, address[] calldata path) external view returns (uint[] memory amounts); +} \ No newline at end of file diff --git a/src/UniswapPortal.sol b/src/UniswapPortal.sol new file mode 100644 index 0000000..ab8bd2d --- /dev/null +++ b/src/UniswapPortal.sol @@ -0,0 +1,45 @@ +pragma solidity ^0.8.20; + +import "./IUniswapV2Router01.sol"; +import "./erc20/xERC20.sol"; + +contract UniswapPortal { + IUniswapV2Router01 public uniswapRouter; + uint64 public parentChainId; + address public pool; + + constructor( + IUniswapV2Router01 _uniswapRouter, + uint64 _parentChainId, + address _pool + ) { + uniswapRouter = _uniswapRouter; + parentChainId = _parentChainId; + pool = _pool; + } + + function swapExactTokensForTokens( + uint amountIn, + uint amountOutMin, + address[] calldata path, + address to, + uint deadline + ) external returns (uint[] memory amounts) { + // Transfer tokens to L1 + // The user needs to approve this contract for spending (on L2) + xERC20(path[0]).xTransferFrom(msg.sender, parentChainId, address(this), amountIn); + + // Do the swap on L1 like normal + EVM.xCallOptions(parentChainId); + amounts = uniswapRouter.swapExactTokensForTokens( + amountIn, + amountOutMin, + path, + to, + deadline + ); + + // Get the tokens from L1 back to the user's L2 account + xERC20(path[1]).xTransfer(parentChainId, block.chainid, msg.sender, amounts[1]); + } +} \ No newline at end of file diff --git a/src/erc20/xERC20.sol b/src/erc20/xERC20.sol index fa77c17..6128476 100644 --- a/src/erc20/xERC20.sol +++ b/src/erc20/xERC20.sol @@ -151,4 +151,19 @@ contract xERC20 is IERC20, GwynethContract { emit Transfer(from, to, value); return true; } + + function xTransferFrom(address from, uint256 chain, address to, uint256 value) public returns (bool) { + require(balanceOf[from] >= value, "Insufficient balance"); + if (from != msg.sender) { + require(_allowances[from][msg.sender] >= value, "Allowance exceeded"); + _allowances[from][msg.sender] -= value; + } + balanceOf[from] -= value; + + EVM.xCallOptions(chain); + this._mint(to, value); + + emit Transfer(from, to, value); + return true; + } } \ No newline at end of file