-
Notifications
You must be signed in to change notification settings - Fork 49
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
12 changed files
with
853 additions
and
1 deletion.
There are no files selected for viewing
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity 0.8.7; | ||
|
||
import "@zetachain/protocol-contracts/contracts/zevm/interfaces/zContract.sol"; | ||
import "@zetachain/protocol-contracts/contracts/zevm/interfaces/IZRC20.sol"; | ||
|
||
interface SystemContractErrors { | ||
error CallerIsNotFungibleModule(); | ||
|
||
error InvalidTarget(); | ||
|
||
error CantBeIdenticalAddresses(); | ||
|
||
error CantBeZeroAddress(); | ||
} | ||
|
||
contract MockSystemContract is SystemContractErrors { | ||
error TransferFailed(); | ||
|
||
mapping(uint256 => uint256) public gasPriceByChainId; | ||
mapping(uint256 => address) public gasCoinZRC20ByChainId; | ||
mapping(uint256 => address) public gasZetaPoolByChainId; | ||
|
||
address public wZetaContractAddress; | ||
address public immutable uniswapv2FactoryAddress; | ||
address public immutable uniswapv2Router02Address; | ||
|
||
event SystemContractDeployed(); | ||
event SetGasPrice(uint256, uint256); | ||
event SetGasCoin(uint256, address); | ||
event SetGasZetaPool(uint256, address); | ||
event SetWZeta(address); | ||
|
||
constructor(address wzeta_, address uniswapv2Factory_, address uniswapv2Router02_) { | ||
wZetaContractAddress = wzeta_; | ||
uniswapv2FactoryAddress = uniswapv2Factory_; | ||
uniswapv2Router02Address = uniswapv2Router02_; | ||
emit SystemContractDeployed(); | ||
} | ||
|
||
// fungible module updates the gas price oracle periodically | ||
function setGasPrice(uint256 chainID, uint256 price) external { | ||
gasPriceByChainId[chainID] = price; | ||
emit SetGasPrice(chainID, price); | ||
} | ||
|
||
function setGasCoinZRC20(uint256 chainID, address zrc20) external { | ||
gasCoinZRC20ByChainId[chainID] = zrc20; | ||
emit SetGasCoin(chainID, zrc20); | ||
} | ||
|
||
function setWZETAContractAddress(address addr) external { | ||
wZetaContractAddress = addr; | ||
emit SetWZeta(wZetaContractAddress); | ||
} | ||
|
||
// returns sorted token addresses, used to handle return values from pairs sorted in this order | ||
function sortTokens(address tokenA, address tokenB) internal pure returns (address token0, address token1) { | ||
if (tokenA == tokenB) revert CantBeIdenticalAddresses(); | ||
(token0, token1) = tokenA < tokenB ? (tokenA, tokenB) : (tokenB, tokenA); | ||
if (token0 == address(0)) revert CantBeZeroAddress(); | ||
} | ||
|
||
function uniswapv2PairFor(address factory, address tokenA, address tokenB) public pure returns (address pair) { | ||
(address token0, address token1) = sortTokens(tokenA, tokenB); | ||
pair = address( | ||
uint160( | ||
uint256( | ||
keccak256( | ||
abi.encodePacked( | ||
hex"ff", | ||
factory, | ||
keccak256(abi.encodePacked(token0, token1)), | ||
hex"96e8ac4277198ff8b6f785478aa9a39f403cb768dd02cbee326c3e7da348845f" // init code hash | ||
) | ||
) | ||
) | ||
) | ||
); | ||
} | ||
|
||
function onCrossChainCall(uint256 chainID, address target, address zrc20, uint256 amount, bytes calldata message) external { | ||
zContext memory context = zContext({sender: msg.sender, origin: "", chainID: chainID}); | ||
bool transfer = IZRC20(zrc20).transfer(target, amount); | ||
if (!transfer) revert TransferFailed(); | ||
zContract(target).onCrossChainCall(context, zrc20, amount, message); | ||
} | ||
} |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity =0.8.7; | ||
|
||
import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; | ||
import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; | ||
import "@zetachain/toolkit/contracts/BytesHelperLib.sol"; | ||
|
||
contract MockZRC20 is ERC20 { | ||
address public gasFeeAddress; | ||
uint256 public gasFee; | ||
|
||
event Withdrawal(address indexed from, bytes to, uint256 value, uint256 gasfee, uint256 protocolFlatFee); | ||
|
||
constructor(uint256 initialSupply, string memory name, string memory symbol) ERC20(name, symbol) { | ||
_mint(msg.sender, initialSupply * (10 ** uint256(decimals()))); | ||
gasFeeAddress = address(this); | ||
} | ||
|
||
function setGasFeeAddress(address gasFeeAddress_) external { | ||
gasFeeAddress = gasFeeAddress_; | ||
} | ||
|
||
function setGasFee(uint256 gasFee_) external { | ||
gasFee = gasFee_; | ||
} | ||
|
||
function deposit(address to, uint256 amount) external returns (bool) { | ||
return true; | ||
} | ||
|
||
function bytesToAddress(bytes calldata data, uint256 offset, uint256 size) public pure returns (address output) { | ||
bytes memory b = data[offset:offset + size]; | ||
assembly { | ||
output := mload(add(b, size)) | ||
} | ||
} | ||
|
||
function withdraw(bytes calldata to, uint256 amount) external returns (bool) { | ||
address toAddress; | ||
if (to.length < 32) { | ||
toAddress = BytesHelperLib.bytesToAddress(to, 0); | ||
} else { | ||
toAddress = BytesHelperLib.bytesToAddress(to, 12); | ||
} | ||
|
||
emit Withdrawal(msg.sender, to, amount, gasFee, 0); | ||
return transfer(toAddress, amount); | ||
} | ||
|
||
function withdrawGasFee() external view returns (address, uint256) { | ||
return (gasFeeAddress, gasFee); | ||
} | ||
} |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
|
||
// SPDX-License-Identifier: MIT | ||
pragma solidity 0.5.16; | ||
|
||
/** | ||
* @dev Contracts that need to be compiled for testing purposes | ||
*/ | ||
|
||
import "@uniswap/v2-core/contracts/UniswapV2Factory.sol"; | ||
import "@uniswap/v2-core/contracts/UniswapV2Pair.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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,222 @@ | ||
pragma solidity =0.6.6; | ||
|
||
import '@uniswap/v2-core/contracts/interfaces/IUniswapV2Factory.sol'; | ||
import '@uniswap/lib/contracts/libraries/TransferHelper.sol'; | ||
|
||
import './libraries/UniswapV2Library.sol'; | ||
import './libraries/SafeMath.sol'; | ||
import './interfaces/IERC20.sol'; | ||
import './interfaces/IWETH.sol'; | ||
|
||
contract TestUniswapRouter { | ||
using SafeMath for uint; | ||
|
||
address public immutable factory; | ||
address public immutable WETH; | ||
|
||
modifier ensure(uint deadline) { | ||
require(deadline >= block.timestamp, 'UniswapV2Router: EXPIRED'); | ||
_; | ||
} | ||
|
||
constructor(address _factory, address _WETH) public { | ||
factory = _factory; | ||
WETH = _WETH; | ||
} | ||
|
||
receive() external payable { | ||
assert(msg.sender == WETH); // only accept ETH via fallback from the WETH contract | ||
} | ||
|
||
// **** ADD LIQUIDITY **** | ||
function _addLiquidity( | ||
address tokenA, | ||
address tokenB, | ||
uint amountADesired, | ||
uint amountBDesired, | ||
uint amountAMin, | ||
uint amountBMin | ||
) internal returns (uint amountA, uint amountB) { | ||
// create the pair if it doesn't exist yet | ||
if (IUniswapV2Factory(factory).getPair(tokenA, tokenB) == address(0)) { | ||
address pair = IUniswapV2Factory(factory).createPair(tokenA, tokenB); | ||
} | ||
|
||
(uint reserveA, uint reserveB) = UniswapV2Library.getReserves(factory, tokenA, tokenB); | ||
if (reserveA == 0 && reserveB == 0) { | ||
(amountA, amountB) = (amountADesired, amountBDesired); | ||
} else { | ||
uint amountBOptimal = UniswapV2Library.quote(amountADesired, reserveA, reserveB); | ||
if (amountBOptimal <= amountBDesired) { | ||
require(amountBOptimal >= amountBMin, 'UniswapV2Router: INSUFFICIENT_B_AMOUNT'); | ||
(amountA, amountB) = (amountADesired, amountBOptimal); | ||
} else { | ||
uint amountAOptimal = UniswapV2Library.quote(amountBDesired, reserveB, reserveA); | ||
assert(amountAOptimal <= amountADesired); | ||
require(amountAOptimal >= amountAMin, 'UniswapV2Router: INSUFFICIENT_A_AMOUNT'); | ||
(amountA, amountB) = (amountAOptimal, amountBDesired); | ||
} | ||
} | ||
} | ||
function addLiquidity( | ||
address tokenA, | ||
address tokenB, | ||
uint amountADesired, | ||
uint amountBDesired, | ||
uint amountAMin, | ||
uint amountBMin, | ||
address to, | ||
uint deadline | ||
) external ensure(deadline) returns (uint amountA, uint amountB, uint liquidity) { | ||
(amountA, amountB) = _addLiquidity(tokenA, tokenB, amountADesired, amountBDesired, amountAMin, amountBMin); | ||
address pair = UniswapV2Library.pairFor(factory, tokenA, tokenB); | ||
TransferHelper.safeTransferFrom(tokenA, msg.sender, pair, amountA); | ||
TransferHelper.safeTransferFrom(tokenB, msg.sender, pair, amountB); | ||
liquidity = IUniswapV2Pair(pair).mint(to); | ||
} | ||
function addLiquidityETH( | ||
address token, | ||
uint amountTokenDesired, | ||
uint amountTokenMin, | ||
uint amountETHMin, | ||
address to, | ||
uint deadline | ||
) external payable ensure(deadline) returns (uint amountToken, uint amountETH, uint liquidity) { | ||
(amountToken, amountETH) = _addLiquidity( | ||
token, | ||
WETH, | ||
amountTokenDesired, | ||
msg.value, | ||
amountTokenMin, | ||
amountETHMin | ||
); | ||
address pair = UniswapV2Library.pairFor(factory, token, WETH); | ||
TransferHelper.safeTransferFrom(token, msg.sender, pair, amountToken); | ||
IWETH(WETH).deposit{value: amountETH}(); | ||
assert(IWETH(WETH).transfer(pair, amountETH)); | ||
liquidity = IUniswapV2Pair(pair).mint(to); | ||
// refund dust eth, if any | ||
if (msg.value > amountETH) TransferHelper.safeTransferETH(msg.sender, msg.value - amountETH); | ||
|
||
} | ||
|
||
// **** REMOVE LIQUIDITY **** | ||
function removeLiquidity( | ||
address tokenA, | ||
address tokenB, | ||
uint liquidity, | ||
uint amountAMin, | ||
uint amountBMin, | ||
address to, | ||
uint deadline | ||
) public ensure(deadline) returns (uint amountA, uint amountB) { | ||
address pair = UniswapV2Library.pairFor(factory, tokenA, tokenB); | ||
IUniswapV2Pair(pair).transferFrom(msg.sender, pair, liquidity); // send liquidity to pair | ||
(uint amount0, uint amount1) = IUniswapV2Pair(pair).burn(to); | ||
(address token0,) = UniswapV2Library.sortTokens(tokenA, tokenB); | ||
(amountA, amountB) = tokenA == token0 ? (amount0, amount1) : (amount1, amount0); | ||
require(amountA >= amountAMin, 'UniswapV2Router: INSUFFICIENT_A_AMOUNT'); | ||
require(amountB >= amountBMin, 'UniswapV2Router: INSUFFICIENT_B_AMOUNT'); | ||
} | ||
function removeLiquidityETH( | ||
address token, | ||
uint liquidity, | ||
uint amountTokenMin, | ||
uint amountETHMin, | ||
address to, | ||
uint deadline | ||
) public ensure(deadline) returns (uint amountToken, uint amountETH) { | ||
(amountToken, amountETH) = removeLiquidity( | ||
token, | ||
WETH, | ||
liquidity, | ||
amountTokenMin, | ||
amountETHMin, | ||
address(this), | ||
deadline | ||
); | ||
TransferHelper.safeTransfer(token, to, amountToken); | ||
IWETH(WETH).withdraw(amountETH); | ||
TransferHelper.safeTransferETH(to, amountETH); | ||
} | ||
|
||
// **** SWAP **** | ||
// requires the initial amount to have already been sent to the first pair | ||
function _swap(uint[] memory amounts, address[] memory path, address _to) internal virtual { | ||
for (uint i; i < path.length - 1; i++) { | ||
(address input, address output) = (path[i], path[i + 1]); | ||
(address token0,) = UniswapV2Library.sortTokens(input, output); | ||
uint amountOut = amounts[i + 1]; | ||
(uint amount0Out, uint amount1Out) = input == token0 ? (uint(0), amountOut) : (amountOut, uint(0)); | ||
address to = i < path.length - 2 ? UniswapV2Library.pairFor(factory, output, path[i + 2]) : _to; | ||
IUniswapV2Pair(UniswapV2Library.pairFor(factory, input, output)).swap( | ||
amount0Out, amount1Out, to, new bytes(0) | ||
); | ||
} | ||
} | ||
function swapExactTokensForTokens( | ||
uint amountIn, | ||
uint amountOutMin, | ||
address[] calldata path, | ||
address to, | ||
uint deadline | ||
) external ensure(deadline) returns (uint[] memory amounts) { | ||
amounts = UniswapV2Library.getAmountsOut(factory, amountIn, path); | ||
require(amounts[amounts.length - 1] >= amountOutMin, 'UniswapV2Router: INSUFFICIENT_OUTPUT_AMOUNT'); | ||
TransferHelper.safeTransferFrom( | ||
path[0], msg.sender, UniswapV2Library.pairFor(factory, path[0], path[1]), amounts[0] | ||
); | ||
_swap(amounts, path, to); | ||
} | ||
function swapTokensForExactTokens( | ||
uint amountOut, | ||
uint amountInMax, | ||
address[] calldata path, | ||
address to, | ||
uint deadline | ||
) external ensure(deadline) returns (uint[] memory amounts) { | ||
amounts = UniswapV2Library.getAmountsIn(factory, amountOut, path); | ||
require(amounts[0] <= amountInMax, 'UniswapV2Router: EXCESSIVE_INPUT_AMOUNT'); | ||
TransferHelper.safeTransferFrom( | ||
path[0], msg.sender, UniswapV2Library.pairFor(factory, path[0], path[1]), amounts[0] | ||
); | ||
_swap(amounts, path, to); | ||
} | ||
|
||
// **** LIBRARY FUNCTIONS **** | ||
function quote(uint amountA, uint reserveA, uint reserveB) public pure returns (uint amountB) { | ||
return UniswapV2Library.quote(amountA, reserveA, reserveB); | ||
} | ||
|
||
function getAmountOut(uint amountIn, uint reserveIn, uint reserveOut) | ||
public | ||
pure | ||
returns (uint amountOut) | ||
{ | ||
return UniswapV2Library.getAmountOut(amountIn, reserveIn, reserveOut); | ||
} | ||
|
||
function getAmountIn(uint amountOut, uint reserveIn, uint reserveOut) | ||
public | ||
pure | ||
returns (uint amountIn) | ||
{ | ||
return UniswapV2Library.getAmountIn(amountOut, reserveIn, reserveOut); | ||
} | ||
|
||
function getAmountsOut(uint amountIn, address[] memory path) | ||
public | ||
view | ||
returns (uint[] memory amounts) | ||
{ | ||
return UniswapV2Library.getAmountsOut(factory, amountIn, path); | ||
} | ||
|
||
function getAmountsIn(uint amountOut, address[] memory path) | ||
public | ||
view | ||
returns (uint[] memory amounts) | ||
{ | ||
return UniswapV2Library.getAmountsIn(factory, amountOut, path); | ||
} | ||
} |
Oops, something went wrong.