Skip to content

Commit

Permalink
add "to" parameter to relevant functions
Browse files Browse the repository at this point in the history
  • Loading branch information
NoahZinsmeister committed Dec 30, 2019
1 parent 623353e commit b8655fe
Show file tree
Hide file tree
Showing 6 changed files with 59 additions and 52 deletions.
48 changes: 24 additions & 24 deletions contracts/UniswapV2.sol
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ contract UniswapV2 is IUniswapV2, ERC20("Uniswap V2", "UNI-V2", 18, 0) {
uint private invariantLast;

event Mint(address indexed sender, uint amount0, uint amount1);
event Burn(address indexed sender, uint amount0, uint amount1);
event Swap(address indexed sender, address indexed tokenIn, uint amountIn, uint amountOut);
event Burn(address indexed sender, uint amount0, uint amount1, address indexed to);
event Swap(address indexed sender, address indexed tokenIn, uint amountIn, uint amountOut, address indexed to);
event Sync(uint112 reserve0, uint112 reserve1);

bool private unlocked = true;
Expand Down Expand Up @@ -68,56 +68,56 @@ contract UniswapV2 is IUniswapV2, ERC20("Uniswap V2", "UNI-V2", 18, 0) {
}

// mint liquidity equivalent to 20% of accumulated fees
function mintFeeLiquidity(address feeRecipient) private {
if (feeRecipient != address(0) && invariantLast != 0) {
function _mintFeeLiquidity(address feeTo) private {
if (feeTo != address(0) && invariantLast != 0) {
uint invariant = Math.sqrt(uint(reserve0).mul(reserve1));
if (invariant > invariantLast) {
uint numerator = totalSupply.mul(invariant.sub(invariantLast));
uint denominator = uint(4).mul(invariant).add(invariantLast);
uint liquidity = numerator / denominator;
if (liquidity > 0) _mint(feeRecipient, liquidity);
if (liquidity > 0) _mint(feeTo, liquidity);
}
}
}

function mint() external lock returns (uint liquidity) {
function mint(address to) external lock returns (uint liquidity) {
uint balance0 = IERC20(token0).balanceOf(address(this));
uint balance1 = IERC20(token1).balanceOf(address(this));
uint amount0 = balance0.sub(reserve0);
uint amount1 = balance1.sub(reserve1);

address feeRecipient = IUniswapV2Factory(factory).feeRecipient();
mintFeeLiquidity(feeRecipient);
address feeTo = IUniswapV2Factory(factory).feeTo();
_mintFeeLiquidity(feeTo);
liquidity = totalSupply == 0 ?
Math.sqrt(amount0.mul(amount1)) :
Math.min(amount0.mul(totalSupply) / reserve0, amount1.mul(totalSupply) / reserve1);
require(liquidity > 0, "UniswapV2: INSUFFICIENT_LIQUIDITY_MINTED");
_mint(msg.sender, liquidity);
_mint(to, liquidity);

_update(balance0, balance1);
if (feeRecipient != address(0)) invariantLast = Math.sqrt(uint(reserve0).mul(reserve1));
if (feeTo != address(0)) invariantLast = Math.sqrt(uint(reserve0).mul(reserve1));
emit Mint(msg.sender, amount0, amount1);
}

function burn() external lock returns (uint amount0, uint amount1) {
function burn(address to) external lock returns (uint amount0, uint amount1) {
uint liquidity = balanceOf[address(this)];

address feeRecipient = IUniswapV2Factory(factory).feeRecipient();
mintFeeLiquidity(feeRecipient);
address feeTo = IUniswapV2Factory(factory).feeTo();
_mintFeeLiquidity(feeTo);
// there's a funny case here where if a token deflates uniswap's balance, we give too many tokens...
amount0 = liquidity.mul(reserve0) / totalSupply;
amount1 = liquidity.mul(reserve1) / totalSupply;
require(amount0 > 0 && amount1 > 0, "UniswapV2: INSUFFICIENT_LIQUIDITY_BURNED");
_safeTransfer(token0, msg.sender, amount0);
_safeTransfer(token1, msg.sender, amount1);
_safeTransfer(token0, to, amount0);
_safeTransfer(token1, to, amount1);
_burn(address(this), liquidity);

_update(IERC20(token0).balanceOf(address(this)), IERC20(token1).balanceOf(address(this)));
if (feeRecipient != address(0)) invariantLast = Math.sqrt(uint(reserve0).mul(reserve1));
emit Burn(msg.sender, amount0, amount1);
if (feeTo != address(0)) invariantLast = Math.sqrt(uint(reserve0).mul(reserve1));
emit Burn(msg.sender, amount0, amount1, to);
}

function swap(address tokenIn, uint amountOut) external lock {
function swap(address tokenIn, uint amountOut, address to) external lock {
uint balance0; uint balance1; uint amountIn;

if (tokenIn == token0) {
Expand All @@ -126,7 +126,7 @@ contract UniswapV2 is IUniswapV2, ERC20("Uniswap V2", "UNI-V2", 18, 0) {
amountIn = balance0.sub(reserve0);
require(amountIn > 0, "UniswapV2: INSUFFICIENT_INPUT_AMOUNT");
require(amountIn.mul(reserve1 - amountOut).mul(997) >= amountOut.mul(reserve0).mul(1000), "UniswapV2: K");
_safeTransfer(token1, msg.sender, amountOut);
_safeTransfer(token1, to, amountOut);
balance1 = IERC20(token1).balanceOf(address(this));
} else {
require(tokenIn == token1, "UniswapV2: INVALID_INPUT_TOKEN");
Expand All @@ -135,18 +135,18 @@ contract UniswapV2 is IUniswapV2, ERC20("Uniswap V2", "UNI-V2", 18, 0) {
amountIn = balance1.sub(reserve1);
require(amountIn > 0, "UniswapV2: INSUFFICIENT_INPUT_AMOUNT");
require(amountIn.mul(reserve0 - amountOut).mul(997) >= amountOut.mul(reserve1).mul(1000), "UniswapV2: K");
_safeTransfer(token0, msg.sender, amountOut);
_safeTransfer(token0, to, amountOut);
balance0 = IERC20(token0).balanceOf(address(this));
}

_update(balance0, balance1);
emit Swap(msg.sender, tokenIn, amountIn, amountOut);
emit Swap(msg.sender, tokenIn, amountIn, amountOut, to);
}

// force balances to match reserves
function skim() external lock {
_safeTransfer(token0, msg.sender, IERC20(token0).balanceOf(address(this)).sub(reserve0));
_safeTransfer(token1, msg.sender, IERC20(token1).balanceOf(address(this)).sub(reserve1));
function skim(address to) external lock {
_safeTransfer(token0, to, IERC20(token0).balanceOf(address(this)).sub(reserve0));
_safeTransfer(token1, to, IERC20(token1).balanceOf(address(this)).sub(reserve1));
}

// force reserves to match balances
Expand Down
6 changes: 3 additions & 3 deletions contracts/UniswapV2Factory.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import "./interfaces/IUniswapV2.sol";
contract UniswapV2Factory is IUniswapV2Factory {
bytes public exchangeBytecode;
address public factoryOwner;
address public feeRecipient;
address public feeTo;

mapping (address => mapping(address => address)) private _getExchange;
address[] public exchanges;
Expand Down Expand Up @@ -53,8 +53,8 @@ contract UniswapV2Factory is IUniswapV2Factory {
factoryOwner = _factoryOwner;
}

function setFeeRecipient(address _feeRecipient) external {
function setFeeTo(address _feeTo) external {
require(msg.sender == factoryOwner, "UniswapV2Factory: FORBIDDEN");
feeRecipient = _feeRecipient;
feeTo = _feeTo;
}
}
12 changes: 6 additions & 6 deletions contracts/interfaces/IUniswapV2.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ pragma solidity 0.5.15;

interface IUniswapV2 {
event Mint(address indexed sender, uint amount0, uint amount1);
event Burn(address indexed sender, uint amount0, uint amount1);
event Swap(address indexed sender, address indexed tokenIn, uint amountIn, uint amountOut);
event Burn(address indexed sender, uint amount0, uint amount1, address indexed to);
event Swap(address indexed sender, address indexed tokenIn, uint amountIn, uint amountOut, address indexed to);
event Sync(uint112 reserve0, uint112 reserve1);

function factory() external view returns (address);
Expand All @@ -15,10 +15,10 @@ interface IUniswapV2 {
function price0CumulativeLast() external view returns (uint);
function price1CumulativeLast() external view returns (uint);

function mint() external returns (uint liquidity);
function burn() external returns (uint amount0, uint amount1);
function swap(address tokenIn, uint amountOut) external;
function skim() external;
function mint(address to) external returns (uint liquidity);
function burn(address to) external returns (uint amount0, uint amount1);
function swap(address tokenIn, uint amountOut, address to) external;
function skim(address to) external;
function sync() external;

function initialize(address, address) external; // only called once by the factory on deploy
Expand Down
2 changes: 1 addition & 1 deletion contracts/interfaces/IUniswapV2Factory.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ interface IUniswapV2Factory {

function exchangeBytecode() external view returns (bytes memory);
function factoryOwner() external view returns (address);
function feeRecipient() external view returns (address);
function feeTo() external view returns (address);

function sortTokens(address tokenA, address tokenB) external pure returns (address token0, address token1);
function getExchange(address tokenA, address tokenB) external view returns (address exchange);
Expand Down
25 changes: 13 additions & 12 deletions test/UniswapV2.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ describe('UniswapV2', () => {
await token1.transfer(exchange.address, token1Amount)

const expectedLiquidity = expandTo18Decimals(2)
await expect(exchange.connect(wallet).mint(overrides))
await expect(exchange.connect(wallet).mint(wallet.address, overrides))
.to.emit(exchange, 'Transfer')
.withArgs(AddressZero, wallet.address, expectedLiquidity)
.to.emit(exchange, 'Sync')
Expand All @@ -58,7 +58,7 @@ describe('UniswapV2', () => {
async function addLiquidity(token0Amount: BigNumber, token1Amount: BigNumber) {
await token0.transfer(exchange.address, token0Amount)
await token1.transfer(exchange.address, token1Amount)
await exchange.connect(wallet).mint(overrides)
await exchange.connect(wallet).mint(wallet.address, overrides)
}

it('getInputPrice', async () => {
Expand All @@ -77,11 +77,12 @@ describe('UniswapV2', () => {
for (let testCase of testCases) {
await addLiquidity(testCase[1], testCase[2])
await token0.transfer(exchange.address, testCase[0])
await expect(exchange.connect(wallet).swap(token0.address, testCase[3].add(1), overrides)).to.be.reverted // UniswapV2: K_VIOLATED
await exchange.connect(wallet).swap(token0.address, testCase[3], overrides)
await expect(exchange.connect(wallet).swap(token0.address, testCase[3].add(1), wallet.address, overrides)).to.be
.reverted // UniswapV2: K_VIOLATED
await exchange.connect(wallet).swap(token0.address, testCase[3], wallet.address, overrides)
const totalSupply = await exchange.totalSupply()
await exchange.connect(wallet).transfer(exchange.address, totalSupply)
await exchange.connect(wallet).burn(overrides)
await exchange.connect(wallet).burn(wallet.address, overrides)
}
})

Expand All @@ -93,11 +94,11 @@ describe('UniswapV2', () => {
const swapAmount = expandTo18Decimals(1)
const expectedOutputAmount = bigNumberify('1662497915624478906')
await token0.transfer(exchange.address, swapAmount)
await expect(exchange.connect(wallet).swap(token0.address, expectedOutputAmount, overrides))
await expect(exchange.connect(wallet).swap(token0.address, expectedOutputAmount, wallet.address, overrides))
.to.emit(exchange, 'Sync')
.withArgs(token0Amount.add(swapAmount), token1Amount.sub(expectedOutputAmount))
.to.emit(exchange, 'Swap')
.withArgs(wallet.address, token0.address, swapAmount, expectedOutputAmount)
.withArgs(wallet.address, token0.address, swapAmount, expectedOutputAmount, wallet.address)

expect(await exchange.reserve0()).to.eq(token0Amount.add(swapAmount))
expect(await exchange.reserve1()).to.eq(token1Amount.sub(expectedOutputAmount))
Expand All @@ -117,11 +118,11 @@ describe('UniswapV2', () => {
const swapAmount = expandTo18Decimals(1)
const expectedOutputAmount = bigNumberify('453305446940074565')
await token1.transfer(exchange.address, swapAmount)
await expect(exchange.connect(wallet).swap(token1.address, expectedOutputAmount, overrides))
await expect(exchange.connect(wallet).swap(token1.address, expectedOutputAmount, wallet.address, overrides))
.to.emit(exchange, 'Sync')
.withArgs(token0Amount.sub(expectedOutputAmount), token1Amount.add(swapAmount))
.to.emit(exchange, 'Swap')
.withArgs(wallet.address, token1.address, swapAmount, expectedOutputAmount)
.withArgs(wallet.address, token1.address, swapAmount, expectedOutputAmount, wallet.address)

expect(await exchange.reserve0()).to.eq(token0Amount.sub(expectedOutputAmount))
expect(await exchange.reserve1()).to.eq(token1Amount.add(swapAmount))
Expand All @@ -144,7 +145,7 @@ describe('UniswapV2', () => {
const swapAmount = expandTo18Decimals(1)
const expectedOutputAmount = bigNumberify('453305446940074565')
await token0.transfer(exchange.address, swapAmount)
const gasCost = await exchange.estimate.swap(token0.address, expectedOutputAmount, overrides)
const gasCost = await exchange.estimate.swap(token0.address, expectedOutputAmount, wallet.address, overrides)
console.log(`Gas required for swap: ${gasCost}`)
})

Expand All @@ -156,11 +157,11 @@ describe('UniswapV2', () => {
const expectedLiquidity = expandTo18Decimals(3)
await exchange.connect(wallet).transfer(exchange.address, expectedLiquidity)
// this test is bugged, it catches the token{0,1} transfers before the lp transfers
await expect(exchange.connect(wallet).burn(overrides))
await expect(exchange.connect(wallet).burn(wallet.address, overrides))
// .to.emit(exchange, 'Transfer')
// .withArgs(exchange.address, AddressZero, expectedLiquidity)
.to.emit(exchange, 'Burn')
.withArgs(wallet.address, token0Amount, token1Amount)
.withArgs(wallet.address, token0Amount, token1Amount, wallet.address)
.to.emit(exchange, 'Sync')
.withArgs(0, 0)

Expand Down
18 changes: 12 additions & 6 deletions test/UniswapV2Factory.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,10 @@ describe('UniswapV2Factory', () => {
factory = _factory
})

it('exchangeBytecode, factoryOwner, feeRecipient, exchangesCount', async () => {
it('exchangeBytecode, factoryOwner, feeTo, exchangesCount', async () => {
expect(await factory.exchangeBytecode()).to.eq(bytecode)
expect(await factory.factoryOwner()).to.eq(wallet.address)
expect(await factory.feeRecipient()).to.eq(AddressZero)
expect(await factory.feeTo()).to.eq(AddressZero)
expect(await factory.exchangesCount()).to.eq(0)
})

Expand All @@ -54,6 +54,7 @@ describe('UniswapV2Factory', () => {
await expect(factory.createExchange(...tokens))
.to.emit(factory, 'ExchangeCreated')
.withArgs(TEST_ADDRESSES.token0, TEST_ADDRESSES.token1, create2Address, bigNumberify(1))

await expect(factory.createExchange(...tokens)).to.be.reverted // UniswapV2Factory: EXCHANGE_EXISTS
await expect(factory.createExchange(...tokens.slice().reverse())).to.be.reverted // UniswapV2Factory: EXCHANGE_EXISTS
expect(await factory.getExchange(...tokens)).to.eq(create2Address)
Expand All @@ -75,16 +76,21 @@ describe('UniswapV2Factory', () => {
await createExchange([TEST_ADDRESSES.token1, TEST_ADDRESSES.token0])
})

it('createExchange:gas', async () => {
const gasCost = await factory.estimate.createExchange(TEST_ADDRESSES.token0, TEST_ADDRESSES.token1)
console.log(`Gas required for createExchange: ${gasCost}`)
})

it('setFactoryOwner', async () => {
await expect(factory.connect(other).setFactoryOwner(other.address)).to.be.reverted // UniswapV2Factory: FORBIDDEN
await factory.setFactoryOwner(other.address)
expect(await factory.factoryOwner()).to.eq(other.address)
await expect(factory.setFactoryOwner(wallet.address)).to.be.reverted // UniswapV2Factory: FORBIDDEN
})

it('setFeeRecipient', async () => {
await expect(factory.connect(other).setFeeRecipient(other.address)).to.be.reverted // UniswapV2Factory: FORBIDDEN
await factory.setFeeRecipient(wallet.address)
expect(await factory.feeRecipient()).to.eq(wallet.address)
it('setFeeTo', async () => {
await expect(factory.connect(other).setFeeTo(other.address)).to.be.reverted // UniswapV2Factory: FORBIDDEN
await factory.setFeeTo(wallet.address)
expect(await factory.feeTo()).to.eq(wallet.address)
})
})

0 comments on commit b8655fe

Please sign in to comment.