run everything on constantinople
turn on compiler optimization don't hardcode 18 decimals add create2 name return values in factory
This commit is contained in:
@ -42,11 +42,10 @@ contract UniswapV2 is ERC20("Uniswap V2", "UNI-V2", 18, 0) {
|
||||
reentrancyLock = false;
|
||||
}
|
||||
|
||||
constructor(address _tokenA, address _tokenB) public {
|
||||
require(address(_tokenA) != address(0) && _tokenB != address(0), "Uniswap: INVALID_ADDRESS");
|
||||
constructor() public {
|
||||
factory = msg.sender;
|
||||
tokenA = _tokenA;
|
||||
tokenB = _tokenB;
|
||||
// tokenA = _tokenA;
|
||||
// tokenB = _tokenB;
|
||||
}
|
||||
|
||||
function () external {}
|
||||
|
||||
@ -1,45 +1,62 @@
|
||||
// TODO review, create2
|
||||
// TODO create2 exchanges
|
||||
pragma solidity 0.5.12;
|
||||
|
||||
import "./UniswapV2.sol";
|
||||
import "./interfaces/IUniswapV2Factory.sol";
|
||||
|
||||
contract UniswapV2Factory {
|
||||
event ExchangeCreated(address indexed token0, address indexed token1, address exchange);
|
||||
contract UniswapV2Factory is IUniswapV2Factory {
|
||||
event ExchangeCreated(address indexed token0, address indexed token1, address exchange, uint256 exchangeCount);
|
||||
|
||||
mapping (address => address) internal exchangeToToken0;
|
||||
mapping (address => address) internal exchangeToToken1;
|
||||
mapping (address => mapping(address => address)) internal token0ToToken1ToExchange;
|
||||
|
||||
function orderTokens(address tokenA, address tokenB) internal pure returns (address, address) {
|
||||
address token0 = tokenA < tokenB ? tokenA : tokenB;
|
||||
address token1 = tokenA < tokenB ? tokenB : tokenA;
|
||||
return (token0, token1);
|
||||
struct Pair {
|
||||
address token0;
|
||||
address token1;
|
||||
}
|
||||
|
||||
function createExchange(address tokenA, address tokenB) public returns (address) {
|
||||
require(tokenA != tokenB, "UniswapV2Factory: INVALID_PAIR");
|
||||
require(tokenA != address(0) && tokenB != address(0), "UniswapV2Factory: NO_ZERO_ADDRESS_TOKENS");
|
||||
bytes private exchangeBytecode;
|
||||
uint256 public exchangeCount;
|
||||
mapping (address => Pair) private exchangeToPair;
|
||||
mapping (address => mapping(address => address)) private token0ToToken1ToExchange;
|
||||
|
||||
(address token0, address token1) = orderTokens(tokenA, tokenB);
|
||||
|
||||
require(token0ToToken1ToExchange[token0][token1] == address(0), "UniswapV2Factory: EXCHANGE_EXISTS");
|
||||
|
||||
UniswapV2 exchange = new UniswapV2(token0, token1);
|
||||
exchangeToToken0[address(exchange)] = token0;
|
||||
exchangeToToken1[address(exchange)] = token1;
|
||||
token0ToToken1ToExchange[token0][token1] = address(exchange);
|
||||
|
||||
emit ExchangeCreated(token0, token1, address(exchange));
|
||||
|
||||
return address(exchange);
|
||||
constructor(bytes memory _exchangeBytecode) public {
|
||||
require(_exchangeBytecode.length >= 0x20, "UniswapV2Factory: SHORT_BYTECODE");
|
||||
exchangeBytecode = _exchangeBytecode;
|
||||
}
|
||||
|
||||
function getExchange(address tokenA, address tokenB) public view returns (address) {
|
||||
(address token0, address token1) = orderTokens(tokenA, tokenB);
|
||||
return token0ToToken1ToExchange[token0][token1];
|
||||
function orderTokens(address tokenA, address tokenB) private pure returns (Pair memory pair) {
|
||||
pair = tokenA < tokenB ? Pair(tokenA, tokenB) : Pair(tokenB, tokenA);
|
||||
}
|
||||
|
||||
function getTokens(address exchange) public view returns (address, address) {
|
||||
return (exchangeToToken0[exchange], exchangeToToken1[exchange]);
|
||||
function getTokens(address exchange) public view returns (address token0, address token1) {
|
||||
Pair storage pair = exchangeToPair[exchange];
|
||||
(token0, token1) = (pair.token0, pair.token1);
|
||||
}
|
||||
|
||||
function getExchange(address tokenA, address tokenB) public view returns (address exchange) {
|
||||
Pair memory pair = orderTokens(tokenA, tokenB);
|
||||
exchange = token0ToToken1ToExchange[pair.token0][pair.token1];
|
||||
}
|
||||
|
||||
function createExchange(address tokenA, address tokenB) public returns (address exchange) {
|
||||
require(tokenA != tokenB, "UniswapV2Factory: SAME_TOKEN");
|
||||
require(tokenA != address(0) && tokenB != address(0), "UniswapV2Factory: ZERO_ADDRESS_TOKEN");
|
||||
|
||||
Pair memory pair = orderTokens(tokenA, tokenB);
|
||||
|
||||
require(token0ToToken1ToExchange[pair.token0][pair.token1] == address(0), "UniswapV2Factory: EXCHANGE_EXISTS");
|
||||
|
||||
bytes memory exchangeBytecodeMemory = exchangeBytecode;
|
||||
uint256 exchangeBytecodeLength = exchangeBytecode.length;
|
||||
bytes32 salt = keccak256(abi.encodePacked(pair.token0, pair.token1));
|
||||
assembly {
|
||||
exchange := create2(
|
||||
0,
|
||||
add(exchangeBytecodeMemory, 0x20),
|
||||
exchangeBytecodeLength,
|
||||
salt
|
||||
)
|
||||
}
|
||||
exchangeToPair[exchange] = pair;
|
||||
token0ToToken1ToExchange[pair.token0][pair.token1] = exchange;
|
||||
|
||||
emit ExchangeCreated(pair.token0, pair.token1, exchange, exchangeCount++);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,6 +1,9 @@
|
||||
pragma solidity 0.5.12;
|
||||
|
||||
interface IERC20 {
|
||||
event Transfer(address indexed from, address indexed to, uint256 value);
|
||||
event Approval(address indexed owner, address indexed spender, uint256 value);
|
||||
|
||||
function name() external view returns (string memory);
|
||||
function symbol() external view returns (string memory);
|
||||
function decimals() external view returns (uint8);
|
||||
@ -8,9 +11,6 @@ interface IERC20 {
|
||||
function balanceOf(address owner) external view returns (uint256);
|
||||
function allowance(address owner, address spender) external view returns (uint256);
|
||||
|
||||
event Transfer(address indexed from, address indexed to, uint256 value);
|
||||
event Approval(address indexed owner, address indexed spender, uint256 value);
|
||||
|
||||
function transfer(address to, uint256 value) external returns (bool);
|
||||
function transferFrom(address from, address to, uint256 value) external returns (bool);
|
||||
function burn(uint256 value) external;
|
||||
|
||||
11
contracts/interfaces/IUniswapV2Factory.sol
Normal file
11
contracts/interfaces/IUniswapV2Factory.sol
Normal file
@ -0,0 +1,11 @@
|
||||
pragma solidity 0.5.12;
|
||||
|
||||
interface IUniswapV2Factory {
|
||||
event ExchangeCreated(address indexed token0, address indexed token1, address exchange, uint256 exchangeNumber);
|
||||
|
||||
function exchangeCount() external view returns (uint256);
|
||||
function getTokens(address exchange) external view returns (address token0, address token1);
|
||||
function getExchange(address tokenA, address tokenB) external view returns (address exchange);
|
||||
|
||||
function createExchange(address tokenA, address tokenB) external returns (address exchange);
|
||||
}
|
||||
@ -4,7 +4,6 @@ import { Contract } from 'ethers'
|
||||
import { BigNumber, bigNumberify } from 'ethers/utils'
|
||||
|
||||
import ERC20 from '../build/ERC20.json'
|
||||
import {} from 'ethers/utils'
|
||||
|
||||
chai.use(solidity)
|
||||
const { expect } = chai
|
||||
@ -13,6 +12,7 @@ const decimalize = (n: number): BigNumber => bigNumberify(n).mul(bigNumberify(10
|
||||
|
||||
const name = 'Mock ERC20'
|
||||
const symbol = 'MOCK'
|
||||
const decimals = 18
|
||||
const totalSupply = decimalize(1000)
|
||||
|
||||
describe('ERC20', () => {
|
||||
@ -21,7 +21,7 @@ describe('ERC20', () => {
|
||||
let token: Contract
|
||||
|
||||
beforeEach(async () => {
|
||||
token = await deployContract(wallet, ERC20, [name, symbol, totalSupply])
|
||||
token = await deployContract(wallet, ERC20, [name, symbol, decimals, totalSupply])
|
||||
})
|
||||
|
||||
it('name, symbol, decimals, totalSupply, balanceOf', async () => {
|
||||
|
||||
54
test/UniswapV2Factory.ts
Normal file
54
test/UniswapV2Factory.ts
Normal file
@ -0,0 +1,54 @@
|
||||
import path from 'path'
|
||||
import chai from 'chai'
|
||||
import { solidity, createMockProvider, getWallets, deployContract } from 'ethereum-waffle'
|
||||
import { Contract } from 'ethers'
|
||||
import { keccak256, solidityPack, getAddress } from 'ethers/utils'
|
||||
|
||||
import UniswapV2 from '../build/UniswapV2.json'
|
||||
import UniswapV2Factory from '../build/UniswapV2Factory.json'
|
||||
|
||||
chai.use(solidity)
|
||||
const { expect } = chai
|
||||
|
||||
const dummyTokens = ['0x1000000000000000000000000000000000000000', '0x2000000000000000000000000000000000000000']
|
||||
|
||||
describe('UniswapV2Factory', () => {
|
||||
const provider = createMockProvider(path.join(__dirname, '..', 'waffle.json'))
|
||||
const [wallet] = getWallets(provider)
|
||||
let bytecode: string
|
||||
let factory: Contract
|
||||
|
||||
it('can deploy factory', async () => {
|
||||
bytecode = `0x${UniswapV2.evm.bytecode.object}`
|
||||
|
||||
factory = await deployContract(wallet, UniswapV2Factory, [bytecode], {
|
||||
gasLimit: (provider._web3Provider as any).options.gasLimit
|
||||
})
|
||||
|
||||
expect(await factory.exchangeCount()).to.eq(0)
|
||||
})
|
||||
|
||||
it('can create exchange', async () => {
|
||||
const expectedAddress = getAddress(
|
||||
`0x${keccak256(
|
||||
[
|
||||
'0xff',
|
||||
factory.address.slice(2),
|
||||
keccak256(solidityPack(['address', 'address'], dummyTokens)).slice(2),
|
||||
keccak256(bytecode).slice(2)
|
||||
].join('')
|
||||
).slice(-40)}`
|
||||
)
|
||||
|
||||
await expect(factory.createExchange(...dummyTokens))
|
||||
.to.emit(factory, 'ExchangeCreated')
|
||||
.withArgs(...[...dummyTokens, expectedAddress, 0])
|
||||
|
||||
expect(await factory.exchangeCount()).to.eq(1)
|
||||
expect(await factory.getTokens(expectedAddress)).to.deep.eq(dummyTokens)
|
||||
expect(await factory.getExchange(...dummyTokens)).to.eq(expectedAddress)
|
||||
|
||||
const exchange = new Contract(expectedAddress, UniswapV2.abi, provider)
|
||||
expect(await exchange.factory()).to.eq(factory.address)
|
||||
})
|
||||
})
|
||||
14
waffle.json
14
waffle.json
@ -1,3 +1,15 @@
|
||||
{
|
||||
"solcVersion": "./node_modules/solc"
|
||||
"solcVersion": "./node_modules/solc",
|
||||
"compilerOptions": {
|
||||
"evmVersion": "constantinople",
|
||||
"optimizer": {
|
||||
"enabled": true,
|
||||
"runs": 1000
|
||||
}
|
||||
},
|
||||
"ganacheOptions": {
|
||||
"hardfork": "constantinople",
|
||||
"mnemonic": "horn horn horn horn horn horn horn horn horn horn horn horn",
|
||||
"gasLimit": "0x7A1200"
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user