diff --git a/contracts/UniswapV2Factory.sol b/contracts/UniswapV2Factory.sol index 70ff8c5..d655a33 100644 --- a/contracts/UniswapV2Factory.sol +++ b/contracts/UniswapV2Factory.sol @@ -1,4 +1,5 @@ -// pragma solidity =0.5.16; +// SPDX-License-Identifier: UNLICENSED + pragma solidity ^0.8.0; import "./interfaces/IUniswapV2Factory.sol"; @@ -44,7 +45,6 @@ contract UniswapV2Factory is IUniswapV2Factory { ); pair = address(deployPair(shard, salt)); - getPair[token0][token1] = pair; getPair[token1][token0] = pair; allPairs.push(pair); @@ -74,9 +74,21 @@ contract UniswapV2Factory is IUniswapV2Factory { feeToSetter = _feeToSetter; } - function deployPair(uint256 shard, uint256 salt) private returns (address deployedAddress) { - bytes memory code = abi.encodePacked(type(UniswapV2Pair).creationCode, abi.encode(msg.sender)); - address contractAddress = Nil.asyncDeploy(shard, msg.sender, 0, code, salt); + function deployPair( + uint256 shard, + uint256 salt + ) private returns (address deployedAddress) { + bytes memory code = abi.encodePacked( + type(UniswapV2Pair).creationCode, + abi.encode(msg.sender) + ); + address contractAddress = Nil.asyncDeploy( + shard, + msg.sender, + 0, + code, + salt + ); return contractAddress; } } diff --git a/contracts/UniswapV2Pair.sol b/contracts/UniswapV2Pair.sol index dc1e84c..1be5c43 100644 --- a/contracts/UniswapV2Pair.sol +++ b/contracts/UniswapV2Pair.sol @@ -13,7 +13,7 @@ contract UniswapV2Pair is NilCurrencyBase, IUniswapV2Pair { using SafeMath for uint; uint public constant MINIMUM_LIQUIDITY = 10 ** 3; bytes4 private constant SELECTOR = - bytes4(keccak256(bytes("transfer(address,uint256)"))); + bytes4(keccak256(bytes("transfer(address,uint256)"))); address public factory; address public token0; @@ -37,13 +37,20 @@ contract UniswapV2Pair is NilCurrencyBase, IUniswapV2Pair { unlocked = 1; } - function getReserves() public view returns (uint256 _reserve0, uint256 _reserve1) + function getReserves() + public + view + returns (uint256 _reserve0, uint256 _reserve1) { _reserve0 = reserve0; _reserve1 = reserve1; } - function _safeTransfer(CurrencyId _tokenId, address _to, uint _value) private { + function _safeTransfer( + CurrencyId _tokenId, + address _to, + uint _value + ) private { sendCurrencyInternal(_to, _tokenId, _value); } @@ -52,7 +59,12 @@ contract UniswapV2Pair is NilCurrencyBase, IUniswapV2Pair { } // called once by the factory at time of deployment - function initialize(address _token0, address _token1, CurrencyId _tokenId0, CurrencyId _tokenId1) public { + function initialize( + address _token0, + address _token1, + CurrencyId _tokenId0, + CurrencyId _tokenId1 + ) public { token0 = _token0; token1 = _token1; tokenId0 = _tokenId0; @@ -63,8 +75,8 @@ contract UniswapV2Pair is NilCurrencyBase, IUniswapV2Pair { function _update( uint balance0, uint balance1, - uint256 _reserve0, - uint256 _reserve1 + uint256 /*_reserve0*/, + uint256 /*_reserve1*/ ) internal { uint32 blockTimestamp = uint32(block.timestamp % 2 ** 32); @@ -89,8 +101,7 @@ contract UniswapV2Pair is NilCurrencyBase, IUniswapV2Pair { uint numerator = totalSupply.mul(rootK.sub(rootKLast)); uint denominator = rootK.mul(5).add(rootKLast); uint liquidity = numerator / denominator; - if (liquidity > 0) - mintCurrencyInternal(liquidity); + if (liquidity > 0) mintCurrencyInternal(liquidity); sendCurrencyInternal(feeTo, getCurrencyId(), liquidity); } } @@ -128,15 +139,13 @@ contract UniswapV2Pair is NilCurrencyBase, IUniswapV2Pair { } // this low-level function should be called from a contract which performs important safety checks - function burn( - address to - ) public lock returns (uint amount0, uint amount1) { + function burn(address to) public lock returns (uint amount0, uint amount1) { (uint256 _reserve0, uint256 _reserve1) = getReserves(); // gas savings - address _token0 = token0; // gas savings - address _token1 = token1; // gas savings + CurrencyId _tokenId0 = tokenId0; // gas savings + CurrencyId _tokenId1 = tokenId1; // gas savings - uint balance0 = Nil.currencyBalance(address(this), tokenId0); - uint balance1 = Nil.currencyBalance(address(this), tokenId1); + uint balance0 = Nil.currencyBalance(address(this), _tokenId0); + uint balance1 = Nil.currencyBalance(address(this), _tokenId1); uint liquidity = Nil.currencyBalance(address(this), getCurrencyId()); bool feeOn = _mintFee(_reserve0, _reserve1); @@ -147,22 +156,18 @@ contract UniswapV2Pair is NilCurrencyBase, IUniswapV2Pair { "UniswapV2: INSUFFICIENT_LIQUIDITY_BURNED" ); burnCurrencyInternal(liquidity); - _safeTransfer(tokenId0, to, amount0); - _safeTransfer(tokenId1, to, amount1); + _safeTransfer(_tokenId0, to, amount0); + _safeTransfer(_tokenId1, to, amount1); - balance0 = Nil.currencyBalance(address(this), tokenId0); - balance1 = Nil.currencyBalance(address(this), tokenId1); + balance0 = Nil.currencyBalance(address(this), _tokenId0); + balance1 = Nil.currencyBalance(address(this), _tokenId1); _update(balance0, balance1, _reserve0, _reserve1); if (feeOn) kLast = uint(reserve0).mul(reserve1); // reserve0 and reserve1 are up-to-date emit Burn(msg.sender, amount0, amount1, to); } // this low-level function should be called from a contract which performs important safety checks - function swap( - uint amount0Out, - uint amount1Out, - address to - ) public lock { + function swap(uint amount0Out, uint amount1Out, address to) public lock { require( amount0Out > 0 || amount1Out > 0, "UniswapV2: INSUFFICIENT_OUTPUT_AMOUNT" @@ -202,7 +207,7 @@ contract UniswapV2Pair is NilCurrencyBase, IUniswapV2Pair { uint balance1Adjusted = balance1.mul(1000).sub(amount1In.mul(3)); require( balance0Adjusted.mul(balance1Adjusted) >= - uint(_reserve0).mul(_reserve1).mul(1000 ** 2), + uint(_reserve0).mul(_reserve1).mul(1000 ** 2), "UniswapV2: K" ); } @@ -212,17 +217,17 @@ contract UniswapV2Pair is NilCurrencyBase, IUniswapV2Pair { // force balances to match reserves function skim(address to) public lock { - address _token0 = token0; // gas savings - address _token1 = token1; // gas savings + CurrencyId _tokenId0 = tokenId0; // gas savings + CurrencyId _tokenId1 = tokenId1; // gas savings _safeTransfer( - tokenId0, + _tokenId0, to, - Nil.currencyBalance(address(this), tokenId0).sub(reserve0) + Nil.currencyBalance(address(this), _tokenId0).sub(reserve0) ); _safeTransfer( - tokenId1, + _tokenId1, to, - Nil.currencyBalance(address(this), tokenId1).sub(reserve1) + Nil.currencyBalance(address(this), _tokenId1).sub(reserve1) ); } @@ -239,6 +244,7 @@ contract UniswapV2Pair is NilCurrencyBase, IUniswapV2Pair { function token0Id() external view returns (CurrencyId) { return tokenId0; } + function token1Id() external view returns (CurrencyId) { return tokenId1; } diff --git a/contracts/UniswapV2Router01.sol b/contracts/UniswapV2Router01.sol index 2fded09..35d4209 100644 --- a/contracts/UniswapV2Router01.sol +++ b/contracts/UniswapV2Router01.sol @@ -1,24 +1,23 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; -import './interfaces/IUniswapV2Router01.sol'; -import './interfaces/IUniswapV2Factory.sol'; -import './libraries/UniswapV2Library.sol'; +import "./interfaces/IUniswapV2Router01.sol"; +import "./interfaces/IUniswapV2Factory.sol"; +import "./libraries/UniswapV2Library.sol"; import "@nilfoundation/smart-contracts/contracts/NilCurrencyBase.sol"; import "@nilfoundation/smart-contracts/contracts/Nil.sol"; import "./interfaces/IUniswapV2Pair.sol"; contract UniswapV2Router01 is IUniswapV2Router01, NilCurrencyBase { - modifier sameShard(address _addr) { - require(Nil.getShardId(_addr) == Nil.getShardId(address(this)), "Sync calls require same shard for all contracts"); + require( + Nil.getShardId(_addr) == Nil.getShardId(address(this)), + "Sync calls require same shard for all contracts" + ); _; } - function addLiquidity( - address pair, - address to - ) public override { + function addLiquidity(address pair, address to) public override { Nil.Token[] memory tokens = Nil.msgTokens(); if (tokens.length != 2) { revert("Send only 2 tokens to add liquidity"); @@ -38,7 +37,15 @@ contract UniswapV2Router01 is IUniswapV2Router01, NilCurrencyBase { if (tokens.length != 2) { revert("Send only 2 tokens to add liquidity"); } - (amountA, amountB) = _addLiquiditySync(pair, tokens[0].id, tokens[1].id, amountADesired, amountBDesired, amountAMin, amountBMin); + (amountA, amountB) = _addLiquiditySync( + pair, + tokens[0].id, + tokens[1].id, + amountADesired, + amountBDesired, + amountAMin, + amountBMin + ); if (amountA < tokens[0].amount) { Nil.Token[] memory tokenAReturns = new Nil.Token[](1); @@ -58,7 +65,11 @@ contract UniswapV2Router01 is IUniswapV2Router01, NilCurrencyBase { tokensToSend[0].amount = amountA; tokensToSend[1].id = tokens[1].id; tokensToSend[1].amount = amountA; - smartCall(pair, tokensToSend, abi.encodeWithSignature("mint(address)", to)); + smartCall( + pair, + tokensToSend, + abi.encodeWithSignature("mint(address)", to) + ); } function _addLiquiditySync( @@ -69,29 +80,44 @@ contract UniswapV2Router01 is IUniswapV2Router01, NilCurrencyBase { uint amountBDesired, uint amountAMin, uint amountBMin - ) private returns (uint amountA, uint amountB) { - (uint reserveA, uint reserveB) = UniswapV2Library.getReserves(pair, tokenA, tokenB); + ) private view returns (uint amountA, uint amountB) { + (uint reserveA, uint reserveB) = UniswapV2Library.getReserves( + pair, + tokenA, + tokenB + ); if (reserveA == 0 && reserveB == 0) { (amountA, amountB) = (amountADesired, amountBDesired); } else { - uint amountBOptimal = UniswapV2Library.quote(amountADesired, reserveA, reserveB); + uint amountBOptimal = UniswapV2Library.quote( + amountADesired, + reserveA, + reserveB + ); if (amountBOptimal <= amountBDesired) { - require(amountBOptimal >= amountBMin, 'UniswapV2Router: INSUFFICIENT_B_AMOUNT'); + require( + amountBOptimal >= amountBMin, + "UniswapV2Router: INSUFFICIENT_B_AMOUNT" + ); (amountA, amountB) = (amountADesired, amountBOptimal); } else { - uint amountAOptimal = UniswapV2Library.quote(amountBDesired, reserveB, reserveA); + uint amountAOptimal = UniswapV2Library.quote( + amountBDesired, + reserveB, + reserveA + ); assert(amountAOptimal <= amountADesired); - require(amountAOptimal >= amountAMin, 'UniswapV2Router: INSUFFICIENT_A_AMOUNT'); + require( + amountAOptimal >= amountAMin, + "UniswapV2Router: INSUFFICIENT_A_AMOUNT" + ); (amountA, amountB) = (amountAOptimal, amountBDesired); } } } // **** REMOVE LIQUIDITY **** - function removeLiquidity( - address pair, - address to - ) public override { + function removeLiquidity(address pair, address to) public override { Nil.Token[] memory tokens = Nil.msgTokens(); if (tokens.length != 1) { revert("UniswapV2Router: should contains only pair token"); @@ -102,14 +128,18 @@ contract UniswapV2Router01 is IUniswapV2Router01, NilCurrencyBase { function removeLiquiditySync( address pair, address to, - uint amountAMin, - uint amountBMin + uint /*amountAMin*/, + uint /*amountBMin*/ ) public override sameShard(pair) returns (uint amountA, uint amountB) { Nil.Token[] memory tokens = Nil.msgTokens(); if (tokens.length != 1) { revert("UniswapV2Router: should contains only pair token"); } - (bool success, bytes memory result) = smartCall(pair, tokens, abi.encodeWithSignature("burn(address)", to)); + (bool success, bytes memory result) = smartCall( + pair, + tokens, + abi.encodeWithSignature("burn(address)", to) + ); if (success) { (amountA, amountB) = abi.decode(result, (uint256, uint256)); } else { @@ -117,12 +147,26 @@ contract UniswapV2Router01 is IUniswapV2Router01, NilCurrencyBase { } } - function swap(address to, address pair, uint amount0Out, uint amount1Out) public override { + function swap( + address to, + address pair, + uint amount0Out, + uint amount1Out + ) public override { Nil.Token[] memory tokens = Nil.msgTokens(); if (tokens.length != 1) { revert("UniswapV2Router: should contains only pair token"); } - smartCall(pair, tokens, abi.encodeWithSignature("swap(uint256,uint256,address)", amount0Out, amount1Out, to)); + smartCall( + pair, + tokens, + abi.encodeWithSignature( + "swap(uint256,uint256,address)", + amount0Out, + amount1Out, + to + ) + ); } function swapExactTokenForTokenSync( @@ -137,38 +181,88 @@ contract UniswapV2Router01 is IUniswapV2Router01, NilCurrencyBase { CurrencyId token0Id = IUniswapV2Pair(pair).token0Id(); CurrencyId token1Id = IUniswapV2Pair(pair).token1Id(); CurrencyId tokenBId = tokens[0].id != token0Id ? token0Id : token1Id; - (uint reserveA, uint reserveB) = UniswapV2Library.getReserves(pair, tokens[0].id, tokenBId); - amount = UniswapV2Library.getAmountOut(tokens[0].amount, reserveA, reserveB); - require(amount >= amountOutMin, 'UniswapV2Router: INSUFFICIENT_OUTPUT_AMOUNT'); + (uint reserveA, uint reserveB) = UniswapV2Library.getReserves( + pair, + tokens[0].id, + tokenBId + ); + amount = UniswapV2Library.getAmountOut( + tokens[0].amount, + reserveA, + reserveB + ); + require( + amount >= amountOutMin, + "UniswapV2Router: INSUFFICIENT_OUTPUT_AMOUNT" + ); uint amount0Out = tokens[0].id == token0Id ? 0 : amount; uint amount1Out = tokens[0].id != token0Id ? 0 : amount; - (bool success, bytes memory result) = smartCall(pair, tokens, abi.encodeWithSignature("swap(uint256,uint256,address)", amount0Out, amount1Out, to)); + (bool success, bytes memory result) = smartCall( + pair, + tokens, + abi.encodeWithSignature( + "swap(uint256,uint256,address)", + amount0Out, + amount1Out, + to + ) + ); if (!success) { revert("UniswapV2Router: should get success swap result"); } } - function quote(uint amountA, uint reserveA, uint reserveB) public pure override returns (uint amountB) { + function quote( + uint amountA, + uint reserveA, + uint reserveB + ) public pure override returns (uint amountB) { return UniswapV2Library.quote(amountA, reserveA, reserveB); } - function getAmountOut(uint amountIn, uint reserveIn, uint reserveOut) public pure override returns (uint amountOut) { + function getAmountOut( + uint amountIn, + uint reserveIn, + uint reserveOut + ) public pure override returns (uint amountOut) { return UniswapV2Library.getAmountOut(amountIn, reserveIn, reserveOut); } - function getAmountIn(uint amountOut, uint reserveIn, uint reserveOut) public pure override returns (uint amountIn) { + function getAmountIn( + uint amountOut, + uint reserveIn, + uint reserveOut + ) public pure override returns (uint amountIn) { return UniswapV2Library.getAmountOut(amountOut, reserveIn, reserveOut); } - receive() external payable { - } + receive() external payable {} - function smartCall(address dst, Nil.Token[] memory tokens, bytes memory callData) private returns (bool, bytes memory) { + function smartCall( + address dst, + Nil.Token[] memory tokens, + bytes memory callData + ) private returns (bool, bytes memory) { if (Nil.getShardId(dst) == Nil.getShardId(address(this))) { - (bool success, bytes memory result) = Nil.syncCall(dst, gasleft(), 0, tokens, callData); + (bool success, bytes memory result) = Nil.syncCall( + dst, + gasleft(), + 0, + tokens, + callData + ); return (success, result); } else { - Nil.asyncCallWithTokens(dst, address(0), address(0), 0, Nil.FORWARD_REMAINING, 0, tokens, callData); + Nil.asyncCallWithTokens( + dst, + address(0), + address(0), + 0, + Nil.FORWARD_REMAINING, + 0, + tokens, + callData + ); return (true, ""); } } diff --git a/contracts/interfaces/IUniswapV2Factory.sol b/contracts/interfaces/IUniswapV2Factory.sol index 18321b7..17af977 100644 --- a/contracts/interfaces/IUniswapV2Factory.sol +++ b/contracts/interfaces/IUniswapV2Factory.sol @@ -1,14 +1,19 @@ +// SPDX-License-Identifier: UNLICENSED + pragma solidity >=0.5.0; interface IUniswapV2Factory { function feeTo() external view returns (address); + function feeToSetter() external view returns (address); function getTokenPair( address tokenA, address tokenB ) external view returns (address pair); + function allPairs(uint) external view returns (address pair); + function allPairsLength() external view returns (uint); function createPair( @@ -19,5 +24,6 @@ interface IUniswapV2Factory { ) external returns (address pair); function setFeeTo(address) external; + function setFeeToSetter(address) external; } diff --git a/contracts/interfaces/IUniswapV2Pair.sol b/contracts/interfaces/IUniswapV2Pair.sol index 0c0c657..22dd658 100644 --- a/contracts/interfaces/IUniswapV2Pair.sol +++ b/contracts/interfaces/IUniswapV2Pair.sol @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: UNLICENSED + pragma solidity >=0.5.0; import {Currency} from "../Currency.sol"; @@ -20,29 +22,39 @@ interface IUniswapV2Pair { address indexed to ); event Sync(uint256 reserve0, uint256 reserve1); + function MINIMUM_LIQUIDITY() external pure returns (uint); + function factory() external view returns (address); + function token0() external view returns (address); + function token1() external view returns (address); + function token0Id() external view returns (CurrencyId); + function token1Id() external view returns (CurrencyId); + function getReserves() external view returns (uint256 reserve0, uint256 reserve1); + function price0CumulativeLast() external view returns (uint); + function price1CumulativeLast() external view returns (uint); + function kLast() external view returns (uint); + function mint(address to) external returns (uint liquidity); - function burn( - address to - ) external returns (uint amount0, uint amount1); - function swap( - uint amount0Out, - uint amount1Out, - address to - ) external; + + function burn(address to) external returns (uint amount0, uint amount1); + + function swap(uint amount0Out, uint amount1Out, address to) external; + function skim(address to) external; + function sync() external; + function initialize(address, address, CurrencyId, CurrencyId) external; }