Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
143 changes: 94 additions & 49 deletions contracts/UniswapERC20.sol
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,11 @@ contract UniswapERC20 is ERC20 {
event AddLiquidity(address indexed provider, uint256 amountTokenA, uint256 amountTokenB);
event RemoveLiquidity(address indexed provider, uint256 amountTokenA, uint256 amountTokenB);

struct TokenData {
uint128 reserve; // cached reserve for this token
uint128 accumulator; // accumulated TWAP value (TODO)
}

// ERC20 Data
string public constant name = 'Uniswap V2';
string public constant symbol = 'UNI-V2';
Expand All @@ -19,6 +24,8 @@ contract UniswapERC20 is ERC20 {
address public tokenB; // ERC20 token traded on this contract
address public factory; // factory that created this contract

mapping (address => TokenData) public dataForToken;

bool private rentrancyLock = false;

modifier nonReentrant() {
Expand Down Expand Up @@ -48,81 +55,108 @@ contract UniswapERC20 is ERC20 {
return numerator / denominator;
}

function swap(address inputToken, address outputToken, uint256 amountSold, address recipient) private returns (uint256) {
uint256 inputReserve = IERC20(inputToken).balanceOf(address(this));
uint256 outputReserve = IERC20(outputToken).balanceOf(address(this));
uint256 amountBought = getInputPrice(amountSold, inputReserve, outputReserve);
require(IERC20(inputToken).transferFrom(msg.sender, address(this), amountSold));
require(IERC20(outputToken).transfer(recipient, amountBought));
return amountBought;
function swap(address inputToken, address outputToken, address recipient) internal returns (uint256, uint256) {
TokenData memory inputTokenData = dataForToken[inputToken];
TokenData memory outputTokenData = dataForToken[outputToken];

uint256 newInputReserve = IERC20(inputToken).balanceOf(address(this));
uint256 oldInputReserve = uint256(inputTokenData.reserve);
uint256 currentOutputReserve = IERC20(outputToken).balanceOf(address(this));
uint256 amountSold = newInputReserve - oldInputReserve;
uint256 amountBought = getInputPrice(amountSold, oldInputReserve, currentOutputReserve);
require(IERC20(outputToken).transfer(recipient, amountBought), "TRANSFER_FAILED");
uint256 newOutputReserve = currentOutputReserve - amountBought;

dataForToken[inputToken] = TokenData({
reserve: uint128(newInputReserve),
accumulator: inputTokenData.accumulator // TODO: update accumulator value
});
dataForToken[outputToken] = TokenData({
reserve: uint128(newOutputReserve),
accumulator: outputTokenData.accumulator // TODO: update accumulator value
});

return (amountSold, amountBought);
}

//TO: DO msg.sender is wrapper
function swapAForB(uint256 amountSold, address recipient) public nonReentrant returns (uint256) {
uint256 amountBought = swap(tokenA, tokenB, amountSold, recipient);
function swapAForB(address recipient) public nonReentrant returns (uint256) {
(uint256 amountSold, uint256 amountBought) = swap(tokenA, tokenB, recipient);
emit SwapAForB(msg.sender, amountSold, amountBought);
return amountBought;
}

//TO: DO msg.sender is wrapper
function swapBForA(uint256 amountSold, address recipient) public nonReentrant returns (uint256) {
uint256 amountBought = swap(tokenB, tokenA, amountSold, recipient);
function swapBForA(address recipient) public nonReentrant returns (uint256) {
(uint256 amountSold, uint256 amountBought) = swap(tokenB, tokenA, recipient);
emit SwapBForA(msg.sender, amountSold, amountBought);
return amountBought;
}

function getInputPrice(address inputToken, uint256 amountSold) public view returns (uint256) {
require(amountSold > 0);
address _tokenA = address(tokenA);
address _tokenB = address(tokenB);
require(inputToken == _tokenA || inputToken == _tokenB);
address outputToken = _tokenA;
if(inputToken == _tokenA) {
outputToken = _tokenB;
}
uint256 inputReserve = IERC20(inputToken).balanceOf(address(this));
uint256 outputReserve = IERC20(outputToken).balanceOf(address(this));
return getInputPrice(amountSold, inputReserve, outputReserve);
function min(uint256 a, uint256 b) internal pure returns (uint256) {
return a < b ? a : b;
}


function addLiquidity(uint256 amountA, uint256 maxTokenB) public nonReentrant returns (uint256) {
require(amountA > 0);
function addLiquidity() public nonReentrant returns (uint256) {
uint256 _totalSupply = totalSupply;

if (_totalSupply > 0) {
address _tokenA = tokenA;
address _tokenB = tokenB;
uint256 reserveA = IERC20(_tokenA).balanceOf(address(this));
uint256 reserveB = IERC20(_tokenB).balanceOf(address(this));
uint256 amountB = (amountA.mul(reserveB) / reserveA).add(1);
uint256 liquidityMinted = amountA.mul(_totalSupply) / reserveA;
balanceOf[msg.sender] = balanceOf[msg.sender].add(liquidityMinted);
totalSupply = _totalSupply.add(liquidityMinted);
require(IERC20(_tokenA).transferFrom(msg.sender, address(this), amountA));
require(IERC20(_tokenB).transferFrom(msg.sender, address(this), amountB));
emit AddLiquidity(msg.sender, amountA, amountB);
emit Transfer(address(0), msg.sender, liquidityMinted);
return liquidityMinted;
address _tokenA = tokenA;
address _tokenB = tokenB;

TokenData memory tokenAData = dataForToken[_tokenA];
TokenData memory tokenBData = dataForToken[_tokenB];

uint256 oldReserveA = uint256(tokenAData.reserve);
uint256 oldReserveB = uint256(tokenBData.reserve);

uint256 newReserveA = IERC20(_tokenA).balanceOf(address(this));
uint256 newReserveB = IERC20(_tokenB).balanceOf(address(this));

uint256 amountA = newReserveA - oldReserveA;
uint256 amountB = newReserveB - oldReserveB;

require(amountA > 0, "INVALID_AMOUNT_A");
require(amountB > 0, "INVALID_AMOUNT_B");

uint256 liquidityMinted;

if (_totalSupply > 0) {
require(oldReserveA > 0, "INVALID_TOKEN_A_RESERVE");
require(oldReserveB > 0, "INVALID_TOKEN_B_RESERVE");
// TODO: take the geometric mean instead of the min?? equivalently sqrt(newK / oldK) * _totalSupply
liquidityMinted = min((amountA.mul(_totalSupply) / oldReserveA), (amountB.mul(_totalSupply) / oldReserveB));
} else {
// TODO: figure out how to set this safely (arithemtic or geometric mean?)
uint256 initialLiquidity = amountA;
totalSupply = initialLiquidity;
balanceOf[msg.sender] = initialLiquidity;
require(IERC20(tokenA).transferFrom(msg.sender, address(this), amountA));
require(IERC20(tokenB).transferFrom(msg.sender, address(this), maxTokenB));
emit AddLiquidity(msg.sender, amountA, maxTokenB);
emit Transfer(address(0), msg.sender, initialLiquidity);
return initialLiquidity;
// TODO: figure out how to set this safely (arithmetic or geometric mean?)
liquidityMinted = amountA;
}
balanceOf[msg.sender] = balanceOf[msg.sender].add(liquidityMinted);
totalSupply = _totalSupply.add(liquidityMinted);

dataForToken[_tokenA] = TokenData({
reserve: uint128(newReserveA),
accumulator: tokenAData.accumulator // TODO: accumulate
});

dataForToken[_tokenB] = TokenData({
reserve: uint128(newReserveB),
accumulator: tokenBData.accumulator // TODO: accumulate
});

emit AddLiquidity(msg.sender, amountA, amountB);
emit Transfer(address(0), msg.sender, liquidityMinted);

return liquidityMinted;
}


function removeLiquidity(uint256 amount) public nonReentrant returns (uint256, uint256) {
require(amount > 0);
address _tokenA = tokenA;
address _tokenB = tokenB;

TokenData memory tokenAData = dataForToken[_tokenA];
TokenData memory tokenBData = dataForToken[_tokenB];

uint256 reserveA = IERC20(_tokenA).balanceOf(address(this));
uint256 reserveB = IERC20(_tokenB).balanceOf(address(this));
uint256 _totalSupply = totalSupply;
Expand All @@ -132,6 +166,17 @@ contract UniswapERC20 is ERC20 {
totalSupply = _totalSupply.sub(amount);
require(IERC20(_tokenA).transfer(msg.sender, tokenAAmount));
require(IERC20(_tokenB).transfer(msg.sender, tokenBAmount));

dataForToken[_tokenA] = TokenData({
reserve: uint128(reserveA - tokenAAmount),
accumulator: tokenAData.accumulator // TODO: accumulate
});

dataForToken[_tokenB] = TokenData({
reserve: uint128(reserveB - tokenBAmount),
accumulator: tokenBData.accumulator // TODO: accumulate
});

emit RemoveLiquidity(msg.sender, tokenAAmount, tokenBAmount);
emit Transfer(msg.sender, address(0), amount);
return (tokenAAmount, tokenBAmount);
Expand Down