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:
@ -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;
|
||||
}
|
||||
|
||||
24
contracts/interfaces/IUniswapV2ERC20.sol
Normal file
24
contracts/interfaces/IUniswapV2ERC20.sol
Normal 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;
|
||||
}
|
||||
@ -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);
|
||||
9
contracts/test/GenericERC20.sol
Normal file
9
contracts/test/GenericERC20.sol
Normal 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);
|
||||
}
|
||||
}
|
||||
@ -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
|
||||
|
||||
Reference in New Issue
Block a user