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:
Noah Zinsmeister
2019-10-24 18:03:15 -04:00
parent d53d6de4ef
commit d48b5f5a44
7 changed files with 135 additions and 42 deletions

View File

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

View File

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

View File

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

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

View File

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

View File

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