Skip to content
Permalink
Browse files
remove forfeit{,from}
make name + symbol + decimals constant

add GenericERC20

rename some contracts

update tests to reflect all of this
  • Loading branch information
NoahZinsmeister committed Jan 13, 2020
1 parent 5a84023 commit cbe801b54f2a05198b9e7a8e2c437d0f745d9b78
Show file tree
Hide file tree
Showing 11 changed files with 73 additions and 93 deletions.
@@ -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
)
@@ -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;

@@ -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);
@@ -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;
}
@@ -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;
}
@@ -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);
@@ -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);
}
}
@@ -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
})

@@ -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])
@@ -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)
@@ -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<FactoryFixture> {
export async function factoryFixture(_: providers.Web3Provider, [wallet]: Wallet[]): Promise<FactoryFixture> {
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<ExchangeFixture> {
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

0 comments on commit cbe801b

Please sign in to comment.