remove forfeit{,from}

make name + symbol + decimals constant

add GenericERC20

rename some contracts

update tests to reflect all of this
This commit is contained in:
Noah Zinsmeister
2020-01-13 15:59:07 -05:00
parent 5a84023836
commit cbe801b54f
11 changed files with 73 additions and 93 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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])

View File

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

View File

@ -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