From cbe801b54f2a05198b9e7a8e2c437d0f745d9b78 Mon Sep 17 00:00:00 2001 From: Noah Zinsmeister Date: Mon, 13 Jan 2020 15:59:07 -0500 Subject: [PATCH] remove forfeit{,from} make name + symbol + decimals constant add GenericERC20 rename some contracts update tests to reflect all of this --- contracts/{ERC20.sol => UniswapV2ERC20.sol} | 27 ++------- .../{UniswapV2.sol => UniswapV2Exchange.sol} | 9 +-- contracts/UniswapV2Factory.sol | 8 +-- contracts/interfaces/IERC20.sol | 9 --- contracts/interfaces/IUniswapV2ERC20.sol | 24 ++++++++ ...{IUniswapV2.sol => IUniswapV2Exchange.sol} | 2 +- contracts/test/GenericERC20.sol | 9 +++ .../{ERC20.spec.ts => UniswapV2ERC20.spec.ts} | 57 +++++-------------- ...apV2.spec.ts => UniswapV2Exchange.spec.ts} | 2 +- test/UniswapV2Factory.spec.ts | 6 +- test/shared/fixtures.ts | 13 ++--- 11 files changed, 73 insertions(+), 93 deletions(-) rename contracts/{ERC20.sol => UniswapV2ERC20.sol} (82%) rename contracts/{UniswapV2.sol => UniswapV2Exchange.sol} (97%) create mode 100644 contracts/interfaces/IUniswapV2ERC20.sol rename contracts/interfaces/{IUniswapV2.sol => IUniswapV2Exchange.sol} (97%) create mode 100644 contracts/test/GenericERC20.sol rename test/{ERC20.spec.ts => UniswapV2ERC20.spec.ts} (64%) rename test/{UniswapV2.spec.ts => UniswapV2Exchange.spec.ts} (99%) diff --git a/contracts/ERC20.sol b/contracts/UniswapV2ERC20.sol similarity index 82% rename from contracts/ERC20.sol rename to contracts/UniswapV2ERC20.sol index 82d34e5..ce1f7d0 100644 --- a/contracts/ERC20.sol +++ b/contracts/UniswapV2ERC20.sol @@ -1,14 +1,14 @@ pragma solidity 0.5.15; -import "./interfaces/IERC20.sol"; +import "./interfaces/IUniswapV2ERC20.sol"; import "./libraries/SafeMath.sol"; -contract ERC20 is IERC20 { +contract UniswapV2ERC20 is IUniswapV2ERC20 { using SafeMath for uint; - string public name; - string public symbol; - uint8 public decimals; + string constant public name = "Uniswap V2"; + string constant public symbol = "UNI-V2"; + uint8 constant public decimals = 18; uint public totalSupply; mapping (address => uint) public balanceOf; mapping (address => mapping (address => uint)) public allowance; @@ -21,11 +21,7 @@ contract ERC20 is IERC20 { event Transfer(address indexed from, address indexed to, uint value); event Approval(address indexed owner, address indexed spender, uint value); - 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); + constructor() public { uint chainId; assembly { // solium-disable-line security/no-inline-assembly chainId := chainid() @@ -67,10 +63,6 @@ contract ERC20 is IERC20 { return true; } - function forfeit(uint value) external { - _burn(msg.sender, value); - } - function approve(address spender, uint value) external returns (bool) { _approve(msg.sender, spender, value); return true; @@ -84,13 +76,6 @@ contract ERC20 is IERC20 { return true; } - function forfeitFrom(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 permit( address owner, address spender, uint value, uint nonce, uint deadline, uint8 v, bytes32 r, bytes32 s ) diff --git a/contracts/UniswapV2.sol b/contracts/UniswapV2Exchange.sol similarity index 97% rename from contracts/UniswapV2.sol rename to contracts/UniswapV2Exchange.sol index 49577a5..cd70ad3 100644 --- a/contracts/UniswapV2.sol +++ b/contracts/UniswapV2Exchange.sol @@ -1,12 +1,13 @@ pragma solidity 0.5.15; -import "./interfaces/IUniswapV2.sol"; -import "./ERC20.sol"; -import "./libraries/UQ112x112.sol"; +import "./interfaces/IUniswapV2Exchange.sol"; +import "./UniswapV2ERC20.sol"; import "./libraries/Math.sol"; +import "./libraries/UQ112x112.sol"; +import "./interfaces/IERC20.sol"; import "./interfaces/IUniswapV2Factory.sol"; -contract UniswapV2 is IUniswapV2, ERC20("Uniswap V2", "UNI-V2", 18, 0) { +contract UniswapV2Exchange is IUniswapV2Exchange, UniswapV2ERC20 { using SafeMath for uint; using UQ112x112 for uint224; diff --git a/contracts/UniswapV2Factory.sol b/contracts/UniswapV2Factory.sol index 6b141cc..687c674 100644 --- a/contracts/UniswapV2Factory.sol +++ b/contracts/UniswapV2Factory.sol @@ -1,8 +1,8 @@ pragma solidity 0.5.15; import "./interfaces/IUniswapV2Factory.sol"; -import "./interfaces/IUniswapV2.sol"; -import "./UniswapV2.sol"; +import "./UniswapV2Exchange.sol"; +import "./interfaces/IUniswapV2Exchange.sol"; contract UniswapV2Factory is IUniswapV2Factory { address public feeToSetter; @@ -35,12 +35,12 @@ contract UniswapV2Factory is IUniswapV2Factory { require(tokenA != address(0) && tokenB != address(0), "UniswapV2Factory: ZERO_ADDRESS"); (address token0, address token1) = sortTokens(tokenA, tokenB); require(getExchange_[token0][token1] == address(0), "UniswapV2Factory: EXCHANGE_EXISTS"); - bytes memory exchangeBytecode = type(UniswapV2).creationCode; + bytes memory exchangeBytecode = type(UniswapV2Exchange).creationCode; bytes32 salt = keccak256(abi.encodePacked(token0, token1)); assembly { // solium-disable-line security/no-inline-assembly exchange := create2(0, add(exchangeBytecode, 32), mload(exchangeBytecode), salt) } - IUniswapV2(exchange).initialize(token0, token1); + IUniswapV2Exchange(exchange).initialize(token0, token1); getExchange_[token0][token1] = exchange; exchanges.push(exchange); emit ExchangeCreated(token0, token1, exchange, exchanges.length); diff --git a/contracts/interfaces/IERC20.sol b/contracts/interfaces/IERC20.sol index f4f1c36..a531cec 100644 --- a/contracts/interfaces/IERC20.sol +++ b/contracts/interfaces/IERC20.sol @@ -10,17 +10,8 @@ interface IERC20 { function totalSupply() external view returns (uint); function balanceOf(address owner) external view returns (uint); function allowance(address owner, address spender) external view returns (uint); - function DOMAIN_SEPARATOR() external view returns (bytes32); - function PERMIT_TYPEHASH() external pure returns (bytes32); - function nonces(address owner) external view returns (uint); function transfer(address to, uint value) external returns (bool); - function forfeit(uint value) external; function approve(address spender, uint value) external returns (bool); function transferFrom(address from, address to, uint value) external returns (bool); - function forfeitFrom(address from, uint value) external; - function permit( - address owner, address spender, uint value, uint nonce, uint expiration, uint8 v, bytes32 r, bytes32 s - ) - external; } diff --git a/contracts/interfaces/IUniswapV2ERC20.sol b/contracts/interfaces/IUniswapV2ERC20.sol new file mode 100644 index 0000000..baa9d8f --- /dev/null +++ b/contracts/interfaces/IUniswapV2ERC20.sol @@ -0,0 +1,24 @@ +pragma solidity 0.5.15; + +interface IUniswapV2ERC20 { + event Transfer(address indexed from, address indexed to, uint value); + event Approval(address indexed owner, address indexed spender, uint value); + + function name() external view returns (string memory); + function symbol() external view returns (string memory); + function decimals() external view returns (uint8); + function totalSupply() external view returns (uint); + function balanceOf(address owner) external view returns (uint); + function allowance(address owner, address spender) external view returns (uint); + function DOMAIN_SEPARATOR() external view returns (bytes32); + function PERMIT_TYPEHASH() external pure returns (bytes32); + function nonces(address owner) external view returns (uint); + + function transfer(address to, uint value) external returns (bool); + function approve(address spender, uint value) external returns (bool); + function transferFrom(address from, address to, uint value) external returns (bool); + function permit( + address owner, address spender, uint value, uint nonce, uint expiration, uint8 v, bytes32 r, bytes32 s + ) + external; +} diff --git a/contracts/interfaces/IUniswapV2.sol b/contracts/interfaces/IUniswapV2Exchange.sol similarity index 97% rename from contracts/interfaces/IUniswapV2.sol rename to contracts/interfaces/IUniswapV2Exchange.sol index c0d8278..d7b4f66 100644 --- a/contracts/interfaces/IUniswapV2.sol +++ b/contracts/interfaces/IUniswapV2Exchange.sol @@ -1,6 +1,6 @@ pragma solidity 0.5.15; -interface IUniswapV2 { +interface IUniswapV2Exchange { event Mint(address indexed sender, uint amount0, uint amount1); 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); diff --git a/contracts/test/GenericERC20.sol b/contracts/test/GenericERC20.sol new file mode 100644 index 0000000..53f4791 --- /dev/null +++ b/contracts/test/GenericERC20.sol @@ -0,0 +1,9 @@ +pragma solidity 0.5.15; + +import "../UniswapV2Exchange.sol"; + +contract GenericERC20 is UniswapV2Exchange { + constructor(uint _totalSupply) public { + if (_totalSupply > 0) _mint(msg.sender, _totalSupply); + } +} diff --git a/test/ERC20.spec.ts b/test/UniswapV2ERC20.spec.ts similarity index 64% rename from test/ERC20.spec.ts rename to test/UniswapV2ERC20.spec.ts index 71fc2da..0dda5b2 100644 --- a/test/ERC20.spec.ts +++ b/test/UniswapV2ERC20.spec.ts @@ -2,46 +2,36 @@ import path from 'path' import chai from 'chai' import { solidity, createMockProvider, getWallets, deployContract } from 'ethereum-waffle' import { Contract } from 'ethers' -import { AddressZero, MaxUint256 } from 'ethers/constants' +import { MaxUint256 } from 'ethers/constants' import { bigNumberify, hexlify, keccak256, defaultAbiCoder, toUtf8Bytes } from 'ethers/utils' import { ecsign } from 'ethereumjs-util' import { expandTo18Decimals, getApprovalDigest } from './shared/utilities' -import ERC20 from '../build/ERC20.json' +import GenericERC20 from '../build/GenericERC20.json' chai.use(solidity) const { expect } = chai -const TOKEN_DETAILS = { - name: 'Test Token', - symbol: 'TEST', - decimals: 18, - totalSupply: expandTo18Decimals(1000) -} +const TOTAL_SUPPLY = expandTo18Decimals(10000) const TEST_AMOUNT = expandTo18Decimals(10) -describe('ERC20', () => { +describe('UniswapV2ERC20 via GenericERC20', () => { const provider = createMockProvider(path.join(__dirname, '..', 'waffle.json')) const [wallet, other] = getWallets(provider) let token: Contract beforeEach(async () => { - token = await deployContract(wallet, ERC20, [ - TOKEN_DETAILS.name, - TOKEN_DETAILS.symbol, - TOKEN_DETAILS.decimals, - TOKEN_DETAILS.totalSupply - ]) + token = await deployContract(wallet, GenericERC20, [TOTAL_SUPPLY]) }) it('name, symbol, decimals, totalSupply, balanceOf, DOMAIN_SEPARATOR, PERMIT_TYPEHASH', async () => { const name = await token.name() - expect(name).to.eq(TOKEN_DETAILS.name) - expect(await token.symbol()).to.eq(TOKEN_DETAILS.symbol) - expect(await token.decimals()).to.eq(TOKEN_DETAILS.decimals) - expect(await token.totalSupply()).to.eq(TOKEN_DETAILS.totalSupply) - expect(await token.balanceOf(wallet.address)).to.eq(TOKEN_DETAILS.totalSupply) + expect(name).to.eq('Uniswap V2') + expect(await token.symbol()).to.eq('UNI-V2') + expect(await token.decimals()).to.eq(18) + expect(await token.totalSupply()).to.eq(TOTAL_SUPPLY) + expect(await token.balanceOf(wallet.address)).to.eq(TOTAL_SUPPLY) expect(await token.DOMAIN_SEPARATOR()).to.eq( keccak256( defaultAbiCoder.encode( @@ -67,18 +57,10 @@ describe('ERC20', () => { await expect(token.transfer(other.address, TEST_AMOUNT)) .to.emit(token, 'Transfer') .withArgs(wallet.address, other.address, TEST_AMOUNT) - expect(await token.balanceOf(wallet.address)).to.eq(TOKEN_DETAILS.totalSupply.sub(TEST_AMOUNT)) + expect(await token.balanceOf(wallet.address)).to.eq(TOTAL_SUPPLY.sub(TEST_AMOUNT)) expect(await token.balanceOf(other.address)).to.eq(TEST_AMOUNT) }) - it('forfeit', async () => { - await expect(token.forfeit(TEST_AMOUNT)) - .to.emit(token, 'Transfer') - .withArgs(wallet.address, AddressZero, TEST_AMOUNT) - expect(await token.balanceOf(wallet.address)).to.eq(TOKEN_DETAILS.totalSupply.sub(TEST_AMOUNT)) - expect(await token.totalSupply()).to.eq(TOKEN_DETAILS.totalSupply.sub(TEST_AMOUNT)) - }) - it('approve', async () => { await expect(token.approve(other.address, TEST_AMOUNT)) .to.emit(token, 'Approval') @@ -92,7 +74,7 @@ describe('ERC20', () => { .to.emit(token, 'Transfer') .withArgs(wallet.address, other.address, TEST_AMOUNT) expect(await token.allowance(wallet.address, other.address)).to.eq(0) - expect(await token.balanceOf(wallet.address)).to.eq(TOKEN_DETAILS.totalSupply.sub(TEST_AMOUNT)) + expect(await token.balanceOf(wallet.address)).to.eq(TOTAL_SUPPLY.sub(TEST_AMOUNT)) expect(await token.balanceOf(other.address)).to.eq(TEST_AMOUNT) }) @@ -102,23 +84,12 @@ describe('ERC20', () => { .to.emit(token, 'Transfer') .withArgs(wallet.address, other.address, TEST_AMOUNT) expect(await token.allowance(wallet.address, other.address)).to.eq(MaxUint256) - expect(await token.balanceOf(wallet.address)).to.eq(TOKEN_DETAILS.totalSupply.sub(TEST_AMOUNT)) + expect(await token.balanceOf(wallet.address)).to.eq(TOTAL_SUPPLY.sub(TEST_AMOUNT)) expect(await token.balanceOf(other.address)).to.eq(TEST_AMOUNT) }) - it('forfeitFrom', async () => { - await token.approve(other.address, TEST_AMOUNT) - await expect(token.connect(other).forfeitFrom(wallet.address, TEST_AMOUNT)) - .to.emit(token, 'Transfer') - .withArgs(wallet.address, AddressZero, TEST_AMOUNT) - expect(await token.allowance(wallet.address, other.address)).to.eq(0) - expect(await token.balanceOf(wallet.address)).to.eq(TOKEN_DETAILS.totalSupply.sub(TEST_AMOUNT)) - expect(await token.totalSupply()).to.eq(TOKEN_DETAILS.totalSupply.sub(TEST_AMOUNT)) - expect(await token.balanceOf(other.address)).to.eq(0) - }) - it('transfer:fail', async () => { - await expect(token.transfer(other.address, TOKEN_DETAILS.totalSupply.add(1))).to.be.reverted // ds-math-sub-underflow + await expect(token.transfer(other.address, TOTAL_SUPPLY.add(1))).to.be.reverted // ds-math-sub-underflow await expect(token.connect(other).transfer(wallet.address, 1)).to.be.reverted // ds-math-sub-underflow }) diff --git a/test/UniswapV2.spec.ts b/test/UniswapV2Exchange.spec.ts similarity index 99% rename from test/UniswapV2.spec.ts rename to test/UniswapV2Exchange.spec.ts index 2813f5a..4ad63cc 100644 --- a/test/UniswapV2.spec.ts +++ b/test/UniswapV2Exchange.spec.ts @@ -15,7 +15,7 @@ const overrides = { gasLimit: 1000000 } -describe('UniswapV2', () => { +describe('UniswapV2Exchange', () => { const provider = createMockProvider(path.join(__dirname, '..', 'waffle.json')) const [wallet] = getWallets(provider) const loadFixture = createFixtureLoader(provider, [wallet]) diff --git a/test/UniswapV2Factory.spec.ts b/test/UniswapV2Factory.spec.ts index 5d4329d..d43b887 100644 --- a/test/UniswapV2Factory.spec.ts +++ b/test/UniswapV2Factory.spec.ts @@ -7,7 +7,7 @@ import { bigNumberify } from 'ethers/utils' import { getCreate2Address } from './shared/utilities' import { factoryFixture, FactoryFixture } from './shared/fixtures' -import UniswapV2 from '../build/UniswapV2.json' +import UniswapV2Exchange from '../build/UniswapV2Exchange.json' import { AddressZero } from 'ethers/constants' chai.use(solidity) @@ -47,7 +47,7 @@ describe('UniswapV2Factory', () => { }) async function createExchange(tokens: string[]) { - const bytecode = `0x${UniswapV2.evm.bytecode.object}` + const bytecode = `0x${UniswapV2Exchange.evm.bytecode.object}` const create2Address = getCreate2Address(factory.address, TEST_ADDRESSES.token0, TEST_ADDRESSES.token1, bytecode) await expect(factory.createExchange(...tokens)) .to.emit(factory, 'ExchangeCreated') @@ -60,7 +60,7 @@ describe('UniswapV2Factory', () => { expect(await factory.exchanges(0)).to.eq(create2Address) expect(await factory.exchangesCount()).to.eq(1) - const exchange = new Contract(create2Address, JSON.stringify(UniswapV2.abi), provider) + const exchange = new Contract(create2Address, JSON.stringify(UniswapV2Exchange.abi), provider) expect(await exchange.factory()).to.eq(factory.address) expect(await exchange.token0()).to.eq(TEST_ADDRESSES.token0) expect(await exchange.token1()).to.eq(TEST_ADDRESSES.token1) diff --git a/test/shared/fixtures.ts b/test/shared/fixtures.ts index 5fb45da..3af3710 100644 --- a/test/shared/fixtures.ts +++ b/test/shared/fixtures.ts @@ -3,17 +3,16 @@ import { deployContract } from 'ethereum-waffle' import { expandTo18Decimals } from './utilities' -import ERC20 from '../../build/ERC20.json' -import UniswapV2 from '../../build/UniswapV2.json' +import GenericERC20 from '../../build/GenericERC20.json' import UniswapV2Factory from '../../build/UniswapV2Factory.json' +import UniswapV2Exchange from '../../build/UniswapV2Exchange.json' export interface FactoryFixture { factory: Contract } -export async function factoryFixture(provider: providers.Web3Provider, [wallet]: Wallet[]): Promise { +export async function factoryFixture(_: providers.Web3Provider, [wallet]: Wallet[]): Promise { const factory = await deployContract(wallet, UniswapV2Factory, [wallet.address]) - return { factory } } @@ -26,12 +25,12 @@ export interface ExchangeFixture extends FactoryFixture { export async function exchangeFixture(provider: providers.Web3Provider, [wallet]: Wallet[]): Promise { const { factory } = await factoryFixture(provider, [wallet]) - const tokenA = await deployContract(wallet, ERC20, ['Test Token A', 'TESTA', 18, expandTo18Decimals(10000)]) - const tokenB = await deployContract(wallet, ERC20, ['Test Token B', 'TESTB', 18, expandTo18Decimals(10000)]) + const tokenA = await deployContract(wallet, GenericERC20, [expandTo18Decimals(10000)]) + const tokenB = await deployContract(wallet, GenericERC20, [expandTo18Decimals(10000)]) await factory.createExchange(tokenA.address, tokenB.address) const exchangeAddress = await factory.getExchange(tokenA.address, tokenB.address) - const exchange = new Contract(exchangeAddress, JSON.stringify(UniswapV2.abi), provider) + const exchange = new Contract(exchangeAddress, JSON.stringify(UniswapV2Exchange.abi), provider) const token0Address = (await exchange.token0()).address const token0 = tokenA.address === token0Address ? tokenA : tokenB