exchange -> pair
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@ -1,2 +1 @@
|
||||
build/
|
||||
node_modules/
|
||||
|
||||
44913
build/Combined-Json.json
44913
build/Combined-Json.json
File diff suppressed because one or more lines are too long
@ -18,7 +18,7 @@
|
||||
{
|
||||
"indexed": false,
|
||||
"internalType": "address",
|
||||
"name": "exchange",
|
||||
"name": "pair",
|
||||
"type": "address"
|
||||
},
|
||||
{
|
||||
@ -28,7 +28,7 @@
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"name": "ExchangeCreated",
|
||||
"name": "PairCreated",
|
||||
"type": "event"
|
||||
},
|
||||
{
|
||||
@ -40,11 +40,11 @@
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"name": "allExchanges",
|
||||
"name": "allPairs",
|
||||
"outputs": [
|
||||
{
|
||||
"internalType": "address",
|
||||
"name": "exchange",
|
||||
"name": "pair",
|
||||
"type": "address"
|
||||
}
|
||||
],
|
||||
@ -55,7 +55,7 @@
|
||||
{
|
||||
"constant": true,
|
||||
"inputs": [],
|
||||
"name": "allExchangesLength",
|
||||
"name": "allPairsLength",
|
||||
"outputs": [
|
||||
{
|
||||
"internalType": "uint256",
|
||||
@ -81,11 +81,11 @@
|
||||
"type": "address"
|
||||
}
|
||||
],
|
||||
"name": "createExchange",
|
||||
"name": "createPair",
|
||||
"outputs": [
|
||||
{
|
||||
"internalType": "address",
|
||||
"name": "exchange",
|
||||
"name": "pair",
|
||||
"type": "address"
|
||||
}
|
||||
],
|
||||
@ -137,11 +137,11 @@
|
||||
"type": "address"
|
||||
}
|
||||
],
|
||||
"name": "getExchange",
|
||||
"name": "getPair",
|
||||
"outputs": [
|
||||
{
|
||||
"internalType": "address",
|
||||
"name": "exchange",
|
||||
"name": "pair",
|
||||
"type": "address"
|
||||
}
|
||||
],
|
||||
@ -213,7 +213,7 @@
|
||||
{
|
||||
"indexed": false,
|
||||
"internalType": "address",
|
||||
"name": "exchange",
|
||||
"name": "pair",
|
||||
"type": "address"
|
||||
},
|
||||
{
|
||||
@ -223,7 +223,7 @@
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"name": "ExchangeCreated",
|
||||
"name": "PairCreated",
|
||||
"type": "event"
|
||||
},
|
||||
{
|
||||
@ -235,11 +235,11 @@
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"name": "allExchanges",
|
||||
"name": "allPairs",
|
||||
"outputs": [
|
||||
{
|
||||
"internalType": "address",
|
||||
"name": "exchange",
|
||||
"name": "pair",
|
||||
"type": "address"
|
||||
}
|
||||
],
|
||||
@ -250,7 +250,7 @@
|
||||
{
|
||||
"constant": true,
|
||||
"inputs": [],
|
||||
"name": "allExchangesLength",
|
||||
"name": "allPairsLength",
|
||||
"outputs": [
|
||||
{
|
||||
"internalType": "uint256",
|
||||
@ -276,11 +276,11 @@
|
||||
"type": "address"
|
||||
}
|
||||
],
|
||||
"name": "createExchange",
|
||||
"name": "createPair",
|
||||
"outputs": [
|
||||
{
|
||||
"internalType": "address",
|
||||
"name": "exchange",
|
||||
"name": "pair",
|
||||
"type": "address"
|
||||
}
|
||||
],
|
||||
@ -332,11 +332,11 @@
|
||||
"type": "address"
|
||||
}
|
||||
],
|
||||
"name": "getExchange",
|
||||
"name": "getPair",
|
||||
"outputs": [
|
||||
{
|
||||
"internalType": "address",
|
||||
"name": "exchange",
|
||||
"name": "pair",
|
||||
"type": "address"
|
||||
}
|
||||
],
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -1,41 +1,40 @@
|
||||
pragma solidity =0.5.16;
|
||||
|
||||
import './interfaces/IUniswapV2Factory.sol';
|
||||
import './UniswapV2Exchange.sol';
|
||||
import './interfaces/IUniswapV2Exchange.sol';
|
||||
import './UniswapV2Pair.sol';
|
||||
|
||||
contract UniswapV2Factory is IUniswapV2Factory {
|
||||
address public feeTo;
|
||||
address public feeToSetter;
|
||||
|
||||
mapping(address => mapping(address => address)) public getExchange;
|
||||
address[] public allExchanges;
|
||||
mapping(address => mapping(address => address)) public getPair;
|
||||
address[] public allPairs;
|
||||
|
||||
event ExchangeCreated(address indexed token0, address indexed token1, address exchange, uint);
|
||||
event PairCreated(address indexed token0, address indexed token1, address pair, uint);
|
||||
|
||||
constructor(address _feeToSetter) public {
|
||||
feeToSetter = _feeToSetter;
|
||||
}
|
||||
|
||||
function allExchangesLength() external view returns (uint) {
|
||||
return allExchanges.length;
|
||||
function allPairsLength() external view returns (uint) {
|
||||
return allPairs.length;
|
||||
}
|
||||
|
||||
function createExchange(address tokenA, address tokenB) external returns (address exchange) {
|
||||
function createPair(address tokenA, address tokenB) external returns (address pair) {
|
||||
require(tokenA != tokenB, 'UniswapV2: IDENTICAL_ADDRESSES');
|
||||
(address token0, address token1) = tokenA < tokenB ? (tokenA, tokenB) : (tokenB, tokenA);
|
||||
require(token0 != address(0), 'UniswapV2: ZERO_ADDRESS');
|
||||
require(getExchange[token0][token1] == address(0), 'UniswapV2: EXCHANGE_EXISTS'); // single check is sufficient
|
||||
bytes memory exchangeBytecode = type(UniswapV2Exchange).creationCode;
|
||||
require(getPair[token0][token1] == address(0), 'UniswapV2: PAIR_EXISTS'); // single check is sufficient
|
||||
bytes memory bytecode = type(UniswapV2Pair).creationCode;
|
||||
bytes32 salt = keccak256(abi.encodePacked(token0, token1));
|
||||
assembly {
|
||||
exchange := create2(0, add(exchangeBytecode, 32), mload(exchangeBytecode), salt)
|
||||
pair := create2(0, add(bytecode, 32), mload(bytecode), salt)
|
||||
}
|
||||
IUniswapV2Exchange(exchange).initialize(token0, token1);
|
||||
getExchange[token0][token1] = exchange;
|
||||
getExchange[token1][token0] = exchange; // populate mapping in the reverse direction
|
||||
allExchanges.push(exchange);
|
||||
emit ExchangeCreated(token0, token1, exchange, allExchanges.length);
|
||||
IUniswapV2Pair(pair).initialize(token0, token1);
|
||||
getPair[token0][token1] = pair;
|
||||
getPair[token1][token0] = pair; // populate mapping in the reverse direction
|
||||
allPairs.push(pair);
|
||||
emit PairCreated(token0, token1, pair, allPairs.length);
|
||||
}
|
||||
|
||||
function setFeeTo(address _feeTo) external {
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
pragma solidity =0.5.16;
|
||||
|
||||
import './interfaces/IUniswapV2Exchange.sol';
|
||||
import './interfaces/IUniswapV2Pair.sol';
|
||||
import './UniswapV2ERC20.sol';
|
||||
import './libraries/Math.sol';
|
||||
import './libraries/UQ112x112.sol';
|
||||
@ -8,7 +8,7 @@ import './interfaces/IERC20.sol';
|
||||
import './interfaces/IUniswapV2Factory.sol';
|
||||
import './interfaces/IUniswapV2Callee.sol';
|
||||
|
||||
contract UniswapV2Exchange is IUniswapV2Exchange, UniswapV2ERC20 {
|
||||
contract UniswapV2Pair is IUniswapV2Pair, UniswapV2ERC20 {
|
||||
using SafeMath for uint;
|
||||
using UQ112x112 for uint224;
|
||||
|
||||
@ -1,16 +1,16 @@
|
||||
pragma solidity =0.5.16;
|
||||
|
||||
interface IUniswapV2Factory {
|
||||
event ExchangeCreated(address indexed token0, address indexed token1, address exchange, uint);
|
||||
event PairCreated(address indexed token0, address indexed token1, address pair, uint);
|
||||
|
||||
function feeTo() external view returns (address);
|
||||
function feeToSetter() external view returns (address);
|
||||
|
||||
function getExchange(address tokenA, address tokenB) external view returns (address exchange);
|
||||
function allExchanges(uint) external view returns (address exchange);
|
||||
function allExchangesLength() external view returns (uint);
|
||||
function getPair(address tokenA, address tokenB) external view returns (address pair);
|
||||
function allPairs(uint) external view returns (address pair);
|
||||
function allPairsLength() external view returns (uint);
|
||||
|
||||
function createExchange(address tokenA, address tokenB) external returns (address exchange);
|
||||
function createPair(address tokenA, address tokenB) external returns (address pair);
|
||||
|
||||
function setFeeTo(address) external;
|
||||
function setFeeToSetter(address) external;
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
pragma solidity =0.5.16;
|
||||
|
||||
interface IUniswapV2Exchange {
|
||||
interface IUniswapV2Pair {
|
||||
event Approval(address indexed owner, address indexed spender, uint value);
|
||||
event Transfer(address indexed from, address indexed to, uint value);
|
||||
|
||||
@ -7,7 +7,7 @@ import { solidity, MockProvider, createFixtureLoader } from 'ethereum-waffle'
|
||||
import { getCreate2Address } from './shared/utilities'
|
||||
import { factoryFixture } from './shared/fixtures'
|
||||
|
||||
import UniswapV2Exchange from '../build/UniswapV2Exchange.json'
|
||||
import UniswapV2Pair from '../build/UniswapV2Pair.json'
|
||||
|
||||
chai.use(solidity)
|
||||
|
||||
@ -31,43 +31,43 @@ describe('UniswapV2Factory', () => {
|
||||
factory = fixture.factory
|
||||
})
|
||||
|
||||
it('feeTo, feeToSetter, allExchanges, allExchangesLength', async () => {
|
||||
it('feeTo, feeToSetter, allPairsLength', async () => {
|
||||
expect(await factory.feeTo()).to.eq(AddressZero)
|
||||
expect(await factory.feeToSetter()).to.eq(wallet.address)
|
||||
expect(await factory.allExchangesLength()).to.eq(0)
|
||||
expect(await factory.allPairsLength()).to.eq(0)
|
||||
})
|
||||
|
||||
async function createExchange(tokens: [string, string]) {
|
||||
const bytecode = `0x${UniswapV2Exchange.evm.bytecode.object}`
|
||||
async function createPair(tokens: [string, string]) {
|
||||
const bytecode = `0x${UniswapV2Pair.evm.bytecode.object}`
|
||||
const create2Address = getCreate2Address(factory.address, tokens, bytecode)
|
||||
await expect(factory.createExchange(...tokens))
|
||||
.to.emit(factory, 'ExchangeCreated')
|
||||
await expect(factory.createPair(...tokens))
|
||||
.to.emit(factory, 'PairCreated')
|
||||
.withArgs(TEST_ADDRESSES[0], TEST_ADDRESSES[1], create2Address, bigNumberify(1))
|
||||
|
||||
await expect(factory.createExchange(...tokens)).to.be.reverted // UniswapV2: EXCHANGE_EXISTS
|
||||
await expect(factory.createExchange(...tokens.slice().reverse())).to.be.reverted // UniswapV2: EXCHANGE_EXISTS
|
||||
expect(await factory.getExchange(...tokens)).to.eq(create2Address)
|
||||
expect(await factory.getExchange(...tokens.slice().reverse())).to.eq(create2Address)
|
||||
expect(await factory.allExchanges(0)).to.eq(create2Address)
|
||||
expect(await factory.allExchangesLength()).to.eq(1)
|
||||
await expect(factory.createPair(...tokens)).to.be.reverted // UniswapV2: PAIR_EXISTS
|
||||
await expect(factory.createPair(...tokens.slice().reverse())).to.be.reverted // UniswapV2: PAIR_EXISTS
|
||||
expect(await factory.getPair(...tokens)).to.eq(create2Address)
|
||||
expect(await factory.getPair(...tokens.slice().reverse())).to.eq(create2Address)
|
||||
expect(await factory.allPairs(0)).to.eq(create2Address)
|
||||
expect(await factory.allPairsLength()).to.eq(1)
|
||||
|
||||
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[0])
|
||||
expect(await exchange.token1()).to.eq(TEST_ADDRESSES[1])
|
||||
const pair = new Contract(create2Address, JSON.stringify(UniswapV2Pair.abi), provider)
|
||||
expect(await pair.factory()).to.eq(factory.address)
|
||||
expect(await pair.token0()).to.eq(TEST_ADDRESSES[0])
|
||||
expect(await pair.token1()).to.eq(TEST_ADDRESSES[1])
|
||||
}
|
||||
|
||||
it('createExchange', async () => {
|
||||
await createExchange(TEST_ADDRESSES)
|
||||
it('createPair', async () => {
|
||||
await createPair(TEST_ADDRESSES)
|
||||
})
|
||||
|
||||
it('createExchange:reverse', async () => {
|
||||
await createExchange(TEST_ADDRESSES.slice().reverse() as [string, string])
|
||||
it('createPair:reverse', async () => {
|
||||
await createPair(TEST_ADDRESSES.slice().reverse() as [string, string])
|
||||
})
|
||||
|
||||
it('createExchange:gas', async () => {
|
||||
const gasCost = await factory.estimate.createExchange(...TEST_ADDRESSES)
|
||||
console.log(`Gas required for createExchange: ${gasCost}`)
|
||||
it('createPair:gas', async () => {
|
||||
const gasCost = await factory.estimate.createPair(...TEST_ADDRESSES)
|
||||
console.log(`Gas required for createPair: ${gasCost}`)
|
||||
})
|
||||
|
||||
it('setFeeTo', async () => {
|
||||
|
||||
@ -4,7 +4,7 @@ import { solidity, MockProvider, createFixtureLoader } from 'ethereum-waffle'
|
||||
import { BigNumber, bigNumberify } from 'ethers/utils'
|
||||
|
||||
import { expandTo18Decimals, mineBlock, encodePrice } from './shared/utilities'
|
||||
import { exchangeFixture } from './shared/fixtures'
|
||||
import { pairFixture } from './shared/fixtures'
|
||||
import { AddressZero } from 'ethers/constants'
|
||||
|
||||
const MINIMUM_LIQUIDITY = bigNumberify(10).pow(3)
|
||||
@ -15,7 +15,7 @@ const overrides = {
|
||||
gasLimit: 9999999
|
||||
}
|
||||
|
||||
describe('UniswapV2Exchange', () => {
|
||||
describe('UniswapV2Pair', () => {
|
||||
const provider = new MockProvider({
|
||||
hardfork: 'istanbul',
|
||||
mnemonic: 'horn horn horn horn horn horn horn horn horn horn horn horn',
|
||||
@ -27,46 +27,46 @@ describe('UniswapV2Exchange', () => {
|
||||
let factory: Contract
|
||||
let token0: Contract
|
||||
let token1: Contract
|
||||
let exchange: Contract
|
||||
let pair: Contract
|
||||
beforeEach(async () => {
|
||||
const fixture = await loadFixture(exchangeFixture)
|
||||
const fixture = await loadFixture(pairFixture)
|
||||
factory = fixture.factory
|
||||
token0 = fixture.token0
|
||||
token1 = fixture.token1
|
||||
exchange = fixture.exchange
|
||||
pair = fixture.pair
|
||||
})
|
||||
|
||||
it('mint', async () => {
|
||||
const token0Amount = expandTo18Decimals(1)
|
||||
const token1Amount = expandTo18Decimals(4)
|
||||
await token0.transfer(exchange.address, token0Amount)
|
||||
await token1.transfer(exchange.address, token1Amount)
|
||||
await token0.transfer(pair.address, token0Amount)
|
||||
await token1.transfer(pair.address, token1Amount)
|
||||
|
||||
const expectedLiquidity = expandTo18Decimals(2)
|
||||
await expect(exchange.mint(wallet.address, overrides))
|
||||
.to.emit(exchange, 'Transfer')
|
||||
await expect(pair.mint(wallet.address, overrides))
|
||||
.to.emit(pair, 'Transfer')
|
||||
.withArgs(AddressZero, AddressZero, MINIMUM_LIQUIDITY)
|
||||
// commented out because of this bug: https://github.com/EthWorks/Waffle/issues/100
|
||||
// .to.emit(exchange, 'Transfer')
|
||||
// .to.emit(pair, 'Transfer')
|
||||
// .withArgs(AddressZero, wallet.address, expectedLiquidity.sub(MINIMUM_LIQUIDITY))
|
||||
.to.emit(exchange, 'Sync')
|
||||
.to.emit(pair, 'Sync')
|
||||
.withArgs(token0Amount, token1Amount)
|
||||
.to.emit(exchange, 'Mint')
|
||||
.to.emit(pair, 'Mint')
|
||||
.withArgs(wallet.address, token0Amount, token1Amount)
|
||||
|
||||
expect(await exchange.totalSupply()).to.eq(expectedLiquidity)
|
||||
expect(await exchange.balanceOf(wallet.address)).to.eq(expectedLiquidity.sub(MINIMUM_LIQUIDITY))
|
||||
expect(await token0.balanceOf(exchange.address)).to.eq(token0Amount)
|
||||
expect(await token1.balanceOf(exchange.address)).to.eq(token1Amount)
|
||||
const reserves = await exchange.getReserves()
|
||||
expect(await pair.totalSupply()).to.eq(expectedLiquidity)
|
||||
expect(await pair.balanceOf(wallet.address)).to.eq(expectedLiquidity.sub(MINIMUM_LIQUIDITY))
|
||||
expect(await token0.balanceOf(pair.address)).to.eq(token0Amount)
|
||||
expect(await token1.balanceOf(pair.address)).to.eq(token1Amount)
|
||||
const reserves = await pair.getReserves()
|
||||
expect(reserves[0]).to.eq(token0Amount)
|
||||
expect(reserves[1]).to.eq(token1Amount)
|
||||
})
|
||||
|
||||
async function addLiquidity(token0Amount: BigNumber, token1Amount: BigNumber) {
|
||||
await token0.transfer(exchange.address, token0Amount)
|
||||
await token1.transfer(exchange.address, token1Amount)
|
||||
await exchange.mint(wallet.address, overrides)
|
||||
await token0.transfer(pair.address, token0Amount)
|
||||
await token1.transfer(pair.address, token1Amount)
|
||||
await pair.mint(wallet.address, overrides)
|
||||
}
|
||||
const swapTestCases: BigNumber[][] = [
|
||||
[1, 5, 10, '1662497915624478906'],
|
||||
@ -83,11 +83,11 @@ describe('UniswapV2Exchange', () => {
|
||||
it(`getInputPrice:${i}`, async () => {
|
||||
const [swapAmount, token0Amount, token1Amount, expectedOutputAmount] = swapTestCase
|
||||
await addLiquidity(token0Amount, token1Amount)
|
||||
await token0.transfer(exchange.address, swapAmount)
|
||||
await expect(exchange.swap(0, expectedOutputAmount.add(1), wallet.address, '0x', overrides)).to.be.revertedWith(
|
||||
await token0.transfer(pair.address, swapAmount)
|
||||
await expect(pair.swap(0, expectedOutputAmount.add(1), wallet.address, '0x', overrides)).to.be.revertedWith(
|
||||
'UniswapV2: K'
|
||||
)
|
||||
await exchange.swap(0, expectedOutputAmount, wallet.address, '0x', overrides)
|
||||
await pair.swap(0, expectedOutputAmount, wallet.address, '0x', overrides)
|
||||
})
|
||||
})
|
||||
|
||||
@ -101,11 +101,11 @@ describe('UniswapV2Exchange', () => {
|
||||
it(`optimistic:${i}`, async () => {
|
||||
const [outputAmount, token0Amount, token1Amount, inputAmount] = optimisticTestCase
|
||||
await addLiquidity(token0Amount, token1Amount)
|
||||
await token0.transfer(exchange.address, inputAmount)
|
||||
await expect(exchange.swap(outputAmount.add(1), 0, wallet.address, '0x', overrides)).to.be.revertedWith(
|
||||
await token0.transfer(pair.address, inputAmount)
|
||||
await expect(pair.swap(outputAmount.add(1), 0, wallet.address, '0x', overrides)).to.be.revertedWith(
|
||||
'UniswapV2: K'
|
||||
)
|
||||
await exchange.swap(outputAmount, 0, wallet.address, '0x', overrides)
|
||||
await pair.swap(outputAmount, 0, wallet.address, '0x', overrides)
|
||||
})
|
||||
})
|
||||
|
||||
@ -116,20 +116,20 @@ describe('UniswapV2Exchange', () => {
|
||||
|
||||
const swapAmount = expandTo18Decimals(1)
|
||||
const expectedOutputAmount = bigNumberify('1662497915624478906')
|
||||
await token0.transfer(exchange.address, swapAmount)
|
||||
await expect(exchange.swap(0, expectedOutputAmount, wallet.address, '0x', overrides))
|
||||
await token0.transfer(pair.address, swapAmount)
|
||||
await expect(pair.swap(0, expectedOutputAmount, wallet.address, '0x', overrides))
|
||||
.to.emit(token1, 'Transfer')
|
||||
.withArgs(exchange.address, wallet.address, expectedOutputAmount)
|
||||
.to.emit(exchange, 'Sync')
|
||||
.withArgs(pair.address, wallet.address, expectedOutputAmount)
|
||||
.to.emit(pair, 'Sync')
|
||||
.withArgs(token0Amount.add(swapAmount), token1Amount.sub(expectedOutputAmount))
|
||||
.to.emit(exchange, 'Swap')
|
||||
.to.emit(pair, 'Swap')
|
||||
.withArgs(wallet.address, swapAmount, 0, 0, expectedOutputAmount, wallet.address)
|
||||
|
||||
const reserves = await exchange.getReserves()
|
||||
const reserves = await pair.getReserves()
|
||||
expect(reserves[0]).to.eq(token0Amount.add(swapAmount))
|
||||
expect(reserves[1]).to.eq(token1Amount.sub(expectedOutputAmount))
|
||||
expect(await token0.balanceOf(exchange.address)).to.eq(token0Amount.add(swapAmount))
|
||||
expect(await token1.balanceOf(exchange.address)).to.eq(token1Amount.sub(expectedOutputAmount))
|
||||
expect(await token0.balanceOf(pair.address)).to.eq(token0Amount.add(swapAmount))
|
||||
expect(await token1.balanceOf(pair.address)).to.eq(token1Amount.sub(expectedOutputAmount))
|
||||
const totalSupplyToken0 = await token0.totalSupply()
|
||||
const totalSupplyToken1 = await token1.totalSupply()
|
||||
expect(await token0.balanceOf(wallet.address)).to.eq(totalSupplyToken0.sub(token0Amount).sub(swapAmount))
|
||||
@ -143,20 +143,20 @@ describe('UniswapV2Exchange', () => {
|
||||
|
||||
const swapAmount = expandTo18Decimals(1)
|
||||
const expectedOutputAmount = bigNumberify('453305446940074565')
|
||||
await token1.transfer(exchange.address, swapAmount)
|
||||
await expect(exchange.swap(expectedOutputAmount, 0, wallet.address, '0x', overrides))
|
||||
await token1.transfer(pair.address, swapAmount)
|
||||
await expect(pair.swap(expectedOutputAmount, 0, wallet.address, '0x', overrides))
|
||||
.to.emit(token0, 'Transfer')
|
||||
.withArgs(exchange.address, wallet.address, expectedOutputAmount)
|
||||
.to.emit(exchange, 'Sync')
|
||||
.withArgs(pair.address, wallet.address, expectedOutputAmount)
|
||||
.to.emit(pair, 'Sync')
|
||||
.withArgs(token0Amount.sub(expectedOutputAmount), token1Amount.add(swapAmount))
|
||||
.to.emit(exchange, 'Swap')
|
||||
.to.emit(pair, 'Swap')
|
||||
.withArgs(wallet.address, 0, swapAmount, expectedOutputAmount, 0, wallet.address)
|
||||
|
||||
const reserves = await exchange.getReserves()
|
||||
const reserves = await pair.getReserves()
|
||||
expect(reserves[0]).to.eq(token0Amount.sub(expectedOutputAmount))
|
||||
expect(reserves[1]).to.eq(token1Amount.add(swapAmount))
|
||||
expect(await token0.balanceOf(exchange.address)).to.eq(token0Amount.sub(expectedOutputAmount))
|
||||
expect(await token1.balanceOf(exchange.address)).to.eq(token1Amount.add(swapAmount))
|
||||
expect(await token0.balanceOf(pair.address)).to.eq(token0Amount.sub(expectedOutputAmount))
|
||||
expect(await token1.balanceOf(pair.address)).to.eq(token1Amount.add(swapAmount))
|
||||
const totalSupplyToken0 = await token0.totalSupply()
|
||||
const totalSupplyToken1 = await token1.totalSupply()
|
||||
expect(await token0.balanceOf(wallet.address)).to.eq(totalSupplyToken0.sub(token0Amount).add(expectedOutputAmount))
|
||||
@ -170,13 +170,13 @@ describe('UniswapV2Exchange', () => {
|
||||
|
||||
// ensure that setting price{0,1}CumulativeLast for the first time doesn't affect our gas math
|
||||
await mineBlock(provider, (await provider.getBlock('latest')).timestamp + 1)
|
||||
await exchange.sync(overrides)
|
||||
await pair.sync(overrides)
|
||||
|
||||
const swapAmount = expandTo18Decimals(1)
|
||||
const expectedOutputAmount = bigNumberify('453305446940074565')
|
||||
await token1.transfer(exchange.address, swapAmount)
|
||||
await token1.transfer(pair.address, swapAmount)
|
||||
await mineBlock(provider, (await provider.getBlock('latest')).timestamp + 1)
|
||||
const gasCost = await exchange.estimate.swap(expectedOutputAmount, 0, wallet.address, '0x', overrides)
|
||||
const gasCost = await pair.estimate.swap(expectedOutputAmount, 0, wallet.address, '0x', overrides)
|
||||
console.log(`Gas required for swap: ${gasCost}`)
|
||||
})
|
||||
|
||||
@ -186,24 +186,24 @@ describe('UniswapV2Exchange', () => {
|
||||
await addLiquidity(token0Amount, token1Amount)
|
||||
|
||||
const expectedLiquidity = expandTo18Decimals(3)
|
||||
await exchange.transfer(exchange.address, expectedLiquidity.sub(MINIMUM_LIQUIDITY))
|
||||
await expect(exchange.burn(wallet.address, overrides))
|
||||
.to.emit(exchange, 'Transfer')
|
||||
.withArgs(exchange.address, AddressZero, expectedLiquidity.sub(MINIMUM_LIQUIDITY))
|
||||
await pair.transfer(pair.address, expectedLiquidity.sub(MINIMUM_LIQUIDITY))
|
||||
await expect(pair.burn(wallet.address, overrides))
|
||||
.to.emit(pair, 'Transfer')
|
||||
.withArgs(pair.address, AddressZero, expectedLiquidity.sub(MINIMUM_LIQUIDITY))
|
||||
// commented out because of this bug: https://github.com/EthWorks/Waffle/issues/100
|
||||
// .to.emit(token0, 'Transfer')
|
||||
// .withArgs(exchange.address, wallet.address, token0Amount.sub(1000))
|
||||
// .withArgs(pair.address, wallet.address, token0Amount.sub(1000))
|
||||
// .to.emit(token1, 'Transfer')
|
||||
// .withArgs(exchange.address, wallet.address, token1Amount.sub(1000))
|
||||
.to.emit(exchange, 'Sync')
|
||||
// .withArgs(pair.address, wallet.address, token1Amount.sub(1000))
|
||||
.to.emit(pair, 'Sync')
|
||||
.withArgs(1000, 1000)
|
||||
.to.emit(exchange, 'Burn')
|
||||
.to.emit(pair, 'Burn')
|
||||
.withArgs(wallet.address, token0Amount.sub(1000), token1Amount.sub(1000), wallet.address)
|
||||
|
||||
expect(await exchange.balanceOf(wallet.address)).to.eq(0)
|
||||
expect(await exchange.totalSupply()).to.eq(MINIMUM_LIQUIDITY)
|
||||
expect(await token0.balanceOf(exchange.address)).to.eq(1000)
|
||||
expect(await token1.balanceOf(exchange.address)).to.eq(1000)
|
||||
expect(await pair.balanceOf(wallet.address)).to.eq(0)
|
||||
expect(await pair.totalSupply()).to.eq(MINIMUM_LIQUIDITY)
|
||||
expect(await token0.balanceOf(pair.address)).to.eq(1000)
|
||||
expect(await token1.balanceOf(pair.address)).to.eq(1000)
|
||||
const totalSupplyToken0 = await token0.totalSupply()
|
||||
const totalSupplyToken1 = await token1.totalSupply()
|
||||
expect(await token0.balanceOf(wallet.address)).to.eq(totalSupplyToken0.sub(1000))
|
||||
@ -215,32 +215,32 @@ describe('UniswapV2Exchange', () => {
|
||||
const token1Amount = expandTo18Decimals(3)
|
||||
await addLiquidity(token0Amount, token1Amount)
|
||||
|
||||
const blockTimestamp = (await exchange.getReserves())[2]
|
||||
const blockTimestamp = (await pair.getReserves())[2]
|
||||
await mineBlock(provider, blockTimestamp + 1)
|
||||
await exchange.sync(overrides)
|
||||
await pair.sync(overrides)
|
||||
|
||||
const initialPrice = encodePrice(token0Amount, token1Amount)
|
||||
expect(await exchange.price0CumulativeLast()).to.eq(initialPrice[0])
|
||||
expect(await exchange.price1CumulativeLast()).to.eq(initialPrice[1])
|
||||
expect((await exchange.getReserves())[2]).to.eq(blockTimestamp + 1)
|
||||
expect(await pair.price0CumulativeLast()).to.eq(initialPrice[0])
|
||||
expect(await pair.price1CumulativeLast()).to.eq(initialPrice[1])
|
||||
expect((await pair.getReserves())[2]).to.eq(blockTimestamp + 1)
|
||||
|
||||
const swapAmount = expandTo18Decimals(3)
|
||||
await token0.transfer(exchange.address, swapAmount)
|
||||
await token0.transfer(pair.address, swapAmount)
|
||||
await mineBlock(provider, blockTimestamp + 10)
|
||||
// swap to a new price eagerly instead of syncing
|
||||
await exchange.swap(0, expandTo18Decimals(1), wallet.address, '0x', overrides) // make the price nice
|
||||
await pair.swap(0, expandTo18Decimals(1), wallet.address, '0x', overrides) // make the price nice
|
||||
|
||||
expect(await exchange.price0CumulativeLast()).to.eq(initialPrice[0].mul(10))
|
||||
expect(await exchange.price1CumulativeLast()).to.eq(initialPrice[1].mul(10))
|
||||
expect((await exchange.getReserves())[2]).to.eq(blockTimestamp + 10)
|
||||
expect(await pair.price0CumulativeLast()).to.eq(initialPrice[0].mul(10))
|
||||
expect(await pair.price1CumulativeLast()).to.eq(initialPrice[1].mul(10))
|
||||
expect((await pair.getReserves())[2]).to.eq(blockTimestamp + 10)
|
||||
|
||||
await mineBlock(provider, blockTimestamp + 20)
|
||||
await exchange.sync(overrides)
|
||||
await pair.sync(overrides)
|
||||
|
||||
const newPrice = encodePrice(expandTo18Decimals(6), expandTo18Decimals(2))
|
||||
expect(await exchange.price0CumulativeLast()).to.eq(initialPrice[0].mul(10).add(newPrice[0].mul(10)))
|
||||
expect(await exchange.price1CumulativeLast()).to.eq(initialPrice[1].mul(10).add(newPrice[1].mul(10)))
|
||||
expect((await exchange.getReserves())[2]).to.eq(blockTimestamp + 20)
|
||||
expect(await pair.price0CumulativeLast()).to.eq(initialPrice[0].mul(10).add(newPrice[0].mul(10)))
|
||||
expect(await pair.price1CumulativeLast()).to.eq(initialPrice[1].mul(10).add(newPrice[1].mul(10)))
|
||||
expect((await pair.getReserves())[2]).to.eq(blockTimestamp + 20)
|
||||
})
|
||||
|
||||
it('feeTo:off', async () => {
|
||||
@ -250,13 +250,13 @@ describe('UniswapV2Exchange', () => {
|
||||
|
||||
const swapAmount = expandTo18Decimals(1)
|
||||
const expectedOutputAmount = bigNumberify('996006981039903216')
|
||||
await token1.transfer(exchange.address, swapAmount)
|
||||
await exchange.swap(expectedOutputAmount, 0, wallet.address, '0x', overrides)
|
||||
await token1.transfer(pair.address, swapAmount)
|
||||
await pair.swap(expectedOutputAmount, 0, wallet.address, '0x', overrides)
|
||||
|
||||
const expectedLiquidity = expandTo18Decimals(1000)
|
||||
await exchange.transfer(exchange.address, expectedLiquidity.sub(MINIMUM_LIQUIDITY))
|
||||
await exchange.burn(wallet.address, overrides)
|
||||
expect(await exchange.totalSupply()).to.eq(MINIMUM_LIQUIDITY)
|
||||
await pair.transfer(pair.address, expectedLiquidity.sub(MINIMUM_LIQUIDITY))
|
||||
await pair.burn(wallet.address, overrides)
|
||||
expect(await pair.totalSupply()).to.eq(MINIMUM_LIQUIDITY)
|
||||
})
|
||||
|
||||
it('feeTo:on', async () => {
|
||||
@ -268,18 +268,18 @@ describe('UniswapV2Exchange', () => {
|
||||
|
||||
const swapAmount = expandTo18Decimals(1)
|
||||
const expectedOutputAmount = bigNumberify('996006981039903216')
|
||||
await token1.transfer(exchange.address, swapAmount)
|
||||
await exchange.swap(expectedOutputAmount, 0, wallet.address, '0x', overrides)
|
||||
await token1.transfer(pair.address, swapAmount)
|
||||
await pair.swap(expectedOutputAmount, 0, wallet.address, '0x', overrides)
|
||||
|
||||
const expectedLiquidity = expandTo18Decimals(1000)
|
||||
await exchange.transfer(exchange.address, expectedLiquidity.sub(MINIMUM_LIQUIDITY))
|
||||
await exchange.burn(wallet.address, overrides)
|
||||
expect(await exchange.totalSupply()).to.eq(MINIMUM_LIQUIDITY.add('249750499251388'))
|
||||
expect(await exchange.balanceOf(other.address)).to.eq('249750499251388')
|
||||
await pair.transfer(pair.address, expectedLiquidity.sub(MINIMUM_LIQUIDITY))
|
||||
await pair.burn(wallet.address, overrides)
|
||||
expect(await pair.totalSupply()).to.eq(MINIMUM_LIQUIDITY.add('249750499251388'))
|
||||
expect(await pair.balanceOf(other.address)).to.eq('249750499251388')
|
||||
|
||||
// using 1000 here instead of the symbolic MINIMUM_LIQUIDITY because the amounts only happen to be equal...
|
||||
// ...because the initial liquidity amounts were equal
|
||||
expect(await token0.balanceOf(exchange.address)).to.eq(bigNumberify(1000).add('249501683697445'))
|
||||
expect(await token1.balanceOf(exchange.address)).to.eq(bigNumberify(1000).add('250000187312969'))
|
||||
expect(await token0.balanceOf(pair.address)).to.eq(bigNumberify(1000).add('249501683697445'))
|
||||
expect(await token1.balanceOf(pair.address)).to.eq(bigNumberify(1000).add('250000187312969'))
|
||||
})
|
||||
})
|
||||
@ -6,7 +6,7 @@ import { expandTo18Decimals } from './utilities'
|
||||
|
||||
import ERC20 from '../../build/ERC20.json'
|
||||
import UniswapV2Factory from '../../build/UniswapV2Factory.json'
|
||||
import UniswapV2Exchange from '../../build/UniswapV2Exchange.json'
|
||||
import UniswapV2Pair from '../../build/UniswapV2Pair.json'
|
||||
|
||||
interface FactoryFixture {
|
||||
factory: Contract
|
||||
@ -21,25 +21,25 @@ export async function factoryFixture(_: Web3Provider, [wallet]: Wallet[]): Promi
|
||||
return { factory }
|
||||
}
|
||||
|
||||
interface ExchangeFixture extends FactoryFixture {
|
||||
interface PairFixture extends FactoryFixture {
|
||||
token0: Contract
|
||||
token1: Contract
|
||||
exchange: Contract
|
||||
pair: Contract
|
||||
}
|
||||
|
||||
export async function exchangeFixture(provider: Web3Provider, [wallet]: Wallet[]): Promise<ExchangeFixture> {
|
||||
export async function pairFixture(provider: Web3Provider, [wallet]: Wallet[]): Promise<PairFixture> {
|
||||
const { factory } = await factoryFixture(provider, [wallet])
|
||||
|
||||
const tokenA = await deployContract(wallet, ERC20, [expandTo18Decimals(10000)], overrides)
|
||||
const tokenB = await deployContract(wallet, ERC20, [expandTo18Decimals(10000)], overrides)
|
||||
|
||||
await factory.createExchange(tokenA.address, tokenB.address, overrides)
|
||||
const exchangeAddress = await factory.getExchange(tokenA.address, tokenB.address)
|
||||
const exchange = new Contract(exchangeAddress, JSON.stringify(UniswapV2Exchange.abi), provider).connect(wallet)
|
||||
await factory.createPair(tokenA.address, tokenB.address, overrides)
|
||||
const pairAddress = await factory.getPair(tokenA.address, tokenB.address)
|
||||
const pair = new Contract(pairAddress, JSON.stringify(UniswapV2Pair.abi), provider).connect(wallet)
|
||||
|
||||
const token0Address = (await exchange.token0()).address
|
||||
const token0Address = (await pair.token0()).address
|
||||
const token0 = tokenA.address === token0Address ? tokenA : tokenB
|
||||
const token1 = tokenA.address === token0Address ? tokenB : tokenA
|
||||
|
||||
return { factory, token0, token1, exchange }
|
||||
return { factory, token0, token1, pair }
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user