Skip to content

Commit

Permalink
bump solc to 0.5.14
Browse files Browse the repository at this point in the history
chainId implementation (commented out for now)

improve events

move safeTransfer in UniswapV2

tweak error messages
  • Loading branch information
NoahZinsmeister committed Dec 12, 2019
1 parent f128ef3 commit 4e4546d
Show file tree
Hide file tree
Showing 14 changed files with 143 additions and 188 deletions.
67 changes: 31 additions & 36 deletions contracts/token/ERC20.sol → contracts/ERC20.sol
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
pragma solidity 0.5.13;
pragma solidity 0.5.14;

import "../interfaces/IERC20.sol";
import "../libraries/SafeMath.sol";
import "./interfaces/IERC20.sol";
import "./libraries/SafeMath.sol";

contract ERC20 is IERC20 {
using SafeMath for uint;
Expand All @@ -15,28 +15,26 @@ contract ERC20 is IERC20 {

bytes32 public DOMAIN_SEPARATOR;
// keccak256("Approve(address owner,address spender,uint256 value,uint256 nonce,uint256 expiration)");
bytes32 public constant APPROVE_TYPEHASH = hex"25a0822e8c2ed7ff64a57c55df37ff176282195b9e0c9bb770ed24a300c89762";
bytes32 public constant APPROVE_TYPEHASH = 0x25a0822e8c2ed7ff64a57c55df37ff176282195b9e0c9bb770ed24a300c89762;
mapping (address => uint) public nonces;

event Transfer(address indexed from, address indexed to, uint value);
event Approval(address indexed owner, address indexed spender, uint value);

function MOCK_getChainId() private pure returns (uint) {
return 1;
}

constructor(string memory _name, string memory _symbol, uint8 _decimals, uint _totalSupply) public {
name = _name;
symbol = _symbol;
decimals = _decimals;
if (_totalSupply > 0) {
_mint(msg.sender, _totalSupply);
}
uint chainId = 1; // hardcode as 1 until ethereum-waffle support istanbul-specific EVM opcodes
// assembly { chainId := chainid() } // solium-disable-line security/no-inline-assembly
DOMAIN_SEPARATOR = keccak256(abi.encode(
keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"),
keccak256(bytes(name)),
keccak256(bytes("1")),
MOCK_getChainId(),
chainId,
address(this)
));
}
Expand All @@ -47,18 +45,18 @@ contract ERC20 is IERC20 {
emit Transfer(address(0), to, value);
}

function _burn(address from, uint value) internal {
balanceOf[from] = balanceOf[from].sub(value);
totalSupply = totalSupply.sub(value);
emit Transfer(from, address(0), value);
}

function _transfer(address from, address to, uint value) private {
balanceOf[from] = balanceOf[from].sub(value);
balanceOf[to] = balanceOf[to].add(value);
emit Transfer(from, to, value);
}

function _burn(address from, uint value) internal {
balanceOf[from] = balanceOf[from].sub(value);
totalSupply = totalSupply.sub(value);
emit Transfer(from, address(0), value);
}

function _approve(address owner, address spender, uint value) private {
allowance[owner][spender] = value;
emit Approval(owner, spender, value);
Expand All @@ -78,40 +76,37 @@ contract ERC20 is IERC20 {
return true;
}

function transferFrom(address from, address to, uint value) external returns (bool) {
if (allowance[from][msg.sender] != uint(-1)) {
allowance[from][msg.sender] = allowance[from][msg.sender].sub(value);
}
_transfer(from, to, value);
return true;
}

function burnFrom(address from, uint value) external {
if (allowance[from][msg.sender] != uint(-1)) {
allowance[from][msg.sender] = allowance[from][msg.sender].sub(value);
}
_burn(from, value);
}

function approveMeta(
address owner, address spender, uint value, uint nonce, uint expiration, uint8 v, bytes32 r, bytes32 s
)
external
{
require(nonce == nonces[owner]++, "ERC20: INVALID_NONCE");
// solium-disable-next-line security/no-block-members
require(expiration > block.timestamp, "ERC20: EXPIRED");
require(expiration > block.timestamp, "ERC20: EXPIRED"); // solium-disable-line security/no-block-members
require(v == 27 || v == 28, "ERC20: INVALID_V");
require(uint(s) <= 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0, "ERC20: INVALID_S");
bytes32 digest = keccak256(abi.encodePacked(
hex"19",
hex"01",
"\x19\x01",
DOMAIN_SEPARATOR,
keccak256(abi.encode(APPROVE_TYPEHASH, owner, spender, value, nonce, expiration))
));
address recoveredAddress = ecrecover(digest, v, r, s);
require(recoveredAddress != address(0), "ERC20: INVALID_SIGNATURE");
require(recoveredAddress == owner, "ERC20: INVALID_ADDRESS");
require(recoveredAddress != address(0) && recoveredAddress == owner, "ERC20: INVALID_SIGNATURE");
_approve(owner, spender, value);
}

function transferFrom(address from, address to, uint value) external returns (bool) {
if (allowance[from][msg.sender] != uint(-1)) {
allowance[from][msg.sender] = allowance[from][msg.sender].sub(value);
}
_transfer(from, to, value);
return true;
}

function burnFrom(address from, uint value) external {
if (allowance[from][msg.sender] != uint(-1)) {
allowance[from][msg.sender] = allowance[from][msg.sender].sub(value);
}
_burn(from, value);
}
}
101 changes: 44 additions & 57 deletions contracts/UniswapV2.sol
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
pragma solidity 0.5.13;
pragma solidity 0.5.14;

import "./interfaces/IUniswapV2.sol";
import "./libraries/Math.sol";
import "./ERC20.sol";
import "./libraries/UQ112x112.sol";
import "./token/ERC20.sol";
import "./token/SafeTransfer.sol";
import "./libraries/Math.sol";

contract UniswapV2 is IUniswapV2, ERC20("Uniswap V2", "UNI-V2", 18, 0), SafeTransfer {
contract UniswapV2 is IUniswapV2, ERC20("Uniswap V2", "UNI-V2", 18, 0) {
using SafeMath for uint;
using UQ112x112 for uint224;

Expand All @@ -17,78 +16,65 @@ contract UniswapV2 is IUniswapV2, ERC20("Uniswap V2", "UNI-V2", 18, 0), SafeTran
uint112 public reserve0;
uint112 public reserve1;
uint32 public blockNumberLast;
uint public priceCumulative0Last;
uint public priceCumulative1Last;
uint public price0CumulativeLast;
uint public price1CumulativeLast;

uint private invariantLast;

bool private notEntered = true;

event ReservesUpdated(uint112 reserve0, uint112 reserve1);
event LiquidityMinted(address indexed sender, uint amount0, uint amount1);
event LiquidityBurned(address indexed sender, address indexed recipient, uint amount0, uint amount1);
event Swap(address indexed sender, address indexed recipient, address indexed input, uint amount0, uint amount1);

modifier lock() {
require(notEntered, "UniswapV2: LOCKED");
notEntered = false;
_;
notEntered = true;
}

event LiquidityMinted(
address indexed sender,
address indexed recipient,
uint amount0,
uint amount1,
uint128 reserve0,
uint128 reserve1,
uint liquidity
);
event LiquidityBurned(
address indexed sender,
address indexed recipient,
uint amount0,
uint amount1,
uint128 reserve0,
uint128 reserve1,
uint liquidity
);
event Swap(
address indexed sender,
address indexed recipient,
uint amount0,
uint amount1,
uint128 reserve0,
uint128 reserve1,
address input
);
event FeeLiquidityMinted(uint liquidity);

constructor() public {
factory = msg.sender;
blockNumberLast = uint32(block.number % 2**32);
}

function initialize(address _token0, address _token1) external {
require(msg.sender == factory && token0 == address(0) && token1 == address(0), 'UniswapV2: FORBIDDEN');
require(msg.sender == factory && token0 == address(0) && token1 == address(0), "UniswapV2: FORBIDDEN");
token0 = _token0;
token1 = _token1;
}

function safeTransfer(address token, address to, uint value) private {
// solium-disable-next-line security/no-low-level-calls
(bool success, bytes memory data) = token.call(abi.encodeWithSignature("transfer(address,uint256)", to, value));
require(success, "UniswapV2: TRANSFER_UNSUCCESSFUL");
if (data.length > 0) {
require(abi.decode(data, (bool)), "SafeTransfer: TRANSFER_FAILED");
}
}

function getInputPrice(uint inputAmount, uint inputReserve, uint outputReserve) public pure returns (uint) {
require(inputReserve > 0 && outputReserve > 0, "UniswapV2: INVALID_VALUE");
require(inputReserve > 0 && outputReserve > 0, "UniswapV2: INSUFFICIENT_RESERVES");
uint amountInputWithFee = inputAmount.mul(997);
uint numerator = amountInputWithFee.mul(outputReserve);
uint denominator = inputReserve.mul(1000).add(amountInputWithFee);
return numerator / denominator;
}

// increment price accumulators if necessary, and update reserves
function update(uint balance0, uint balance1) private {
uint32 blockNumber = uint32(block.number % 2**32);
uint32 blocksElapsed = blockNumber - blockNumberLast; // overflow is desired
if (blocksElapsed > 0 && reserve0 != 0 && reserve1 != 0) {
// in the following 2 lines, * never overflows, + overflow is desired
priceCumulative0Last += uint256(UQ112x112.encode(reserve0).qdiv(reserve1)) * blocksElapsed;
priceCumulative1Last += uint256(UQ112x112.encode(reserve1).qdiv(reserve0)) * blocksElapsed;
// * never overflows, and + overflow is desired
price0CumulativeLast += uint256(UQ112x112.encode(reserve0).qdiv(reserve1)) * blocksElapsed;
price1CumulativeLast += uint256(UQ112x112.encode(reserve1).qdiv(reserve0)) * blocksElapsed;
}
reserve0 = balance0.clamp112();
reserve1 = balance1.clamp112();
reserve0 = Math.clamp112(balance0);
reserve1 = Math.clamp112(balance1);
blockNumberLast = blockNumber;
emit ReservesUpdated(reserve0, reserve1);
}

// mint liquidity equivalent to 20% of accumulated fees
Expand All @@ -98,47 +84,48 @@ contract UniswapV2 is IUniswapV2, ERC20("Uniswap V2", "UNI-V2", 18, 0), SafeTran
uint numerator = totalSupply.mul(invariant.sub(invariantLast));
uint denominator = uint256(4).mul(invariant).add(invariantLast);
uint liquidity = numerator / denominator;
_mint(factory, liquidity); // factory is just a placeholder
emit FeeLiquidityMinted(liquidity);
if (liquidity > 0) {
_mint(factory, liquidity); // factory is a placeholder
}
}
}

function mintLiquidity(address recipient) external lock returns (uint liquidity) {
uint balance0 = IERC20(token0).balanceOf(address(this));
uint balance1 = IERC20(token1).balanceOf(address(this));
require(balance0 <= uint112(-1) && balance1 <= uint112(-1), "UniswapV2: EXCESS_LIQUIDITY");
require(balance0 <= uint112(-1) && balance1 <= uint112(-1), "UniswapV2: EXCESS_BALANCES");
uint amount0 = balance0.sub(reserve0);
uint amount1 = balance1.sub(reserve1);

mintFeeLiquidity();
liquidity = totalSupply == 0 ?
Math.sqrt(amount0.mul(amount1)) :
Math.min(amount0.mul(totalSupply) / reserve0, amount1.mul(totalSupply) / reserve1);
require(liquidity > 0, "UniswapV2: INSUFFICIENT_VALUE");
require(liquidity > 0, "UniswapV2: INSUFFICIENT_LIQUIDITY");
_mint(recipient, liquidity);

update(balance0, balance1);
invariantLast = Math.sqrt(uint(reserve0).mul(reserve1));
emit LiquidityMinted(msg.sender, recipient, amount0, amount1, reserve0, reserve1, liquidity);
emit LiquidityMinted(msg.sender, amount0, amount1);
}

function burnLiquidity(address recipient) external lock returns (uint amount0, uint amount1) {
uint liquidity = balanceOf[address(this)];
uint balance0 = IERC20(token0).balanceOf(address(this));
uint balance1 = IERC20(token1).balanceOf(address(this));
require(balance0 >= reserve0 && balance0 >= reserve1, "UniswapV2: INSUFFICIENT_BALANCE");
require(balance0 >= reserve0 && balance0 >= reserve1, "UniswapV2: INSUFFICIENT_BALANCES");

mintFeeLiquidity();
amount0 = liquidity.mul(balance0) / totalSupply; // intentionally using balances not reserves
amount1 = liquidity.mul(balance1) / totalSupply; // intentionally using balances not reserves
require(amount0 > 0 && amount1 > 0, "UniswapV2: INSUFFICIENT_VALUE");
require(amount0 > 0 && amount1 > 0, "UniswapV2: INSUFFICIENT_AMOUNTS");
safeTransfer(token0, recipient, amount0);
safeTransfer(token1, recipient, amount1);
_burn(address(this), liquidity);

update(IERC20(token0).balanceOf(address(this)), IERC20(token1).balanceOf(address(this)));
invariantLast = Math.sqrt(uint(reserve0).mul(reserve1));
emit LiquidityBurned(msg.sender, recipient, amount0, amount1, reserve0, reserve1, liquidity);
emit LiquidityBurned(msg.sender, recipient, amount0, amount1);
}

function swap0(address recipient) external lock returns (uint amount1) {
Expand All @@ -147,11 +134,11 @@ contract UniswapV2 is IUniswapV2, ERC20("Uniswap V2", "UNI-V2", 18, 0), SafeTran
uint amount0 = balance0.sub(reserve0);

amount1 = getInputPrice(amount0, reserve0, reserve1);
require(amount1 > 0, "UniswapV2: INSUFFICIENT_VALUE");
require(amount1 > 0, "UniswapV2: INSUFFICIENT_AMOUNT");
safeTransfer(token1, recipient, amount1);

update(balance0, IERC20(token1).balanceOf(address(this)));
emit Swap(msg.sender, recipient, amount0, amount1, reserve0, reserve1, token0);
emit Swap(msg.sender, recipient, token0, amount0, amount1);
}

function swap1(address recipient) external lock returns (uint amount0) {
Expand All @@ -160,14 +147,14 @@ contract UniswapV2 is IUniswapV2, ERC20("Uniswap V2", "UNI-V2", 18, 0), SafeTran
uint amount1 = balance1.sub(reserve1);

amount0 = getInputPrice(amount1, reserve1, reserve0);
require(amount0 > 0, "UniswapV2: INSUFFICIENT_VALUE");
require(amount0 > 0, "UniswapV2: INSUFFICIENT_AMOUNT");
safeTransfer(token0, recipient, amount0);

update(IERC20(token0).balanceOf(address(this)), balance1);
emit Swap(msg.sender, recipient, amount0, amount1, reserve0, reserve1, token1);
emit Swap(msg.sender, recipient, token1, amount0, amount1);
}

// almost never _needs_ to be called, it's for weird tokens and can also be helpful for oracles
// almost certainly never needs to be called, but can be helpful for oracles and possibly some weird tokens
function sync() external lock {
update(IERC20(token0).balanceOf(address(this)), IERC20(token1).balanceOf(address(this)));
}
Expand Down
2 changes: 1 addition & 1 deletion contracts/UniswapV2Factory.sol
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
pragma solidity 0.5.13;
pragma solidity 0.5.14;

import "./interfaces/IUniswapV2Factory.sol";
import "./UniswapV2.sol";
Expand Down
3 changes: 1 addition & 2 deletions contracts/interfaces/IERC20.sol
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
pragma solidity 0.5.13;
pragma solidity 0.5.14;

interface IERC20 {
event Transfer(address indexed from, address indexed to, uint value);
Expand All @@ -20,7 +20,6 @@ interface IERC20 {
function approve(address spender, uint value) external returns (bool);
function transferFrom(address from, address to, uint value) external returns (bool);
function burnFrom(address from, uint value) external;

function approveMeta(
address owner, address spender, uint value, uint nonce, uint expiration, uint8 v, bytes32 r, bytes32 s
)
Expand Down
Loading

0 comments on commit 4e4546d

Please sign in to comment.