optimistic swaps (#53)
* alternative flash lending (renting) design
* add rent interface
* fix stack too deep error
rearrange order of k condition math
ignore erroneous out of gas errors in tests
* try removing rent in favor of monolithic swap
IUniswapV2Borrower -> IUniswapV2Callee
update tests
* fix implementation
* clean up math a bit
* amount{0,1}In -> amount{0,1}InNet
* charge on all inputs, not just net
* removed unnecessary safemath
* add to != token check
don't indent in scope
rename reserve{0,1}Next -> reserve{0,1}Adjusted
* > instead of >=
simplify algebra
reserve{0,1}Adjusted -> balance{0,1}Adjusted
add comments
* add some optimistic swap test cases
This commit is contained in:
@ -6,6 +6,7 @@ import './libraries/Math.sol';
|
|||||||
import './libraries/UQ112x112.sol';
|
import './libraries/UQ112x112.sol';
|
||||||
import './interfaces/IERC20.sol';
|
import './interfaces/IERC20.sol';
|
||||||
import './interfaces/IUniswapV2Factory.sol';
|
import './interfaces/IUniswapV2Factory.sol';
|
||||||
|
import './interfaces/IUniswapV2Callee.sol';
|
||||||
|
|
||||||
contract UniswapV2Exchange is IUniswapV2Exchange, UniswapV2ERC20 {
|
contract UniswapV2Exchange is IUniswapV2Exchange, UniswapV2ERC20 {
|
||||||
using SafeMath for uint;
|
using SafeMath for uint;
|
||||||
@ -49,13 +50,21 @@ contract UniswapV2Exchange is IUniswapV2Exchange, UniswapV2ERC20 {
|
|||||||
|
|
||||||
event Mint(address indexed sender, uint amount0, uint amount1);
|
event Mint(address indexed sender, uint amount0, uint amount1);
|
||||||
event Burn(address indexed sender, uint amount0, uint amount1, address indexed to);
|
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);
|
event Swap(
|
||||||
|
address indexed sender,
|
||||||
|
uint amount0In,
|
||||||
|
uint amount1In,
|
||||||
|
uint amount0Out,
|
||||||
|
uint amount1Out,
|
||||||
|
address indexed to
|
||||||
|
);
|
||||||
event Sync(uint112 reserve0, uint112 reserve1);
|
event Sync(uint112 reserve0, uint112 reserve1);
|
||||||
|
|
||||||
constructor() public {
|
constructor() public {
|
||||||
factory = msg.sender;
|
factory = msg.sender;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// called once by the factory at time of deployment
|
||||||
function initialize(address _token0, address _token1) external {
|
function initialize(address _token0, address _token1) external {
|
||||||
require(msg.sender == factory, 'UniswapV2: FORBIDDEN'); // sufficient check
|
require(msg.sender == factory, 'UniswapV2: FORBIDDEN'); // sufficient check
|
||||||
token0 = _token0;
|
token0 = _token0;
|
||||||
@ -99,6 +108,7 @@ contract UniswapV2Exchange is IUniswapV2Exchange, UniswapV2ERC20 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// this low-level function should be called from a contract which performs important safety checks
|
||||||
function mint(address to) external lock returns (uint liquidity) {
|
function mint(address to) external lock returns (uint liquidity) {
|
||||||
(uint112 _reserve0, uint112 _reserve1,) = getReserves(); // gas savings
|
(uint112 _reserve0, uint112 _reserve1,) = getReserves(); // gas savings
|
||||||
uint balance0 = IERC20(token0).balanceOf(address(this));
|
uint balance0 = IERC20(token0).balanceOf(address(this));
|
||||||
@ -118,10 +128,11 @@ contract UniswapV2Exchange is IUniswapV2Exchange, UniswapV2ERC20 {
|
|||||||
_mint(to, liquidity);
|
_mint(to, liquidity);
|
||||||
|
|
||||||
_update(balance0, balance1, _reserve0, _reserve1);
|
_update(balance0, balance1, _reserve0, _reserve1);
|
||||||
if (feeOn) kLast = uint(reserve0).mul(reserve1);
|
if (feeOn) kLast = uint(reserve0).mul(reserve1); // reserve0 and reserve1 are up-to-date
|
||||||
emit Mint(msg.sender, amount0, amount1);
|
emit Mint(msg.sender, amount0, amount1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// this low-level function should be called from a contract which performs important safety checks
|
||||||
function burn(address to) external lock returns (uint amount0, uint amount1) {
|
function burn(address to) external lock returns (uint amount0, uint amount1) {
|
||||||
(uint112 _reserve0, uint112 _reserve1,) = getReserves(); // gas savings
|
(uint112 _reserve0, uint112 _reserve1,) = getReserves(); // gas savings
|
||||||
address _token0 = token0; // gas savings
|
address _token0 = token0; // gas savings
|
||||||
@ -142,40 +153,39 @@ contract UniswapV2Exchange is IUniswapV2Exchange, UniswapV2ERC20 {
|
|||||||
balance1 = IERC20(_token1).balanceOf(address(this));
|
balance1 = IERC20(_token1).balanceOf(address(this));
|
||||||
|
|
||||||
_update(balance0, balance1, _reserve0, _reserve1);
|
_update(balance0, balance1, _reserve0, _reserve1);
|
||||||
if (feeOn) kLast = uint(reserve0).mul(reserve1);
|
if (feeOn) kLast = uint(reserve0).mul(reserve1); // reserve0 and reserve1 are up-to-date
|
||||||
emit Burn(msg.sender, amount0, amount1, to);
|
emit Burn(msg.sender, amount0, amount1, to);
|
||||||
}
|
}
|
||||||
|
|
||||||
function swap(address tokenIn, uint amountOut, address to) external lock {
|
// this low-level function should be called from a contract which performs important safety checks
|
||||||
require(amountOut > 0, 'UniswapV2: INSUFFICIENT_OUTPUT_AMOUNT');
|
function swap(uint amount0Out, uint amount1Out, address to, bytes calldata data) external lock {
|
||||||
|
require(amount0Out > 0 || amount1Out > 0, 'UniswapV2: INSUFFICIENT_OUTPUT_AMOUNT');
|
||||||
(uint112 _reserve0, uint112 _reserve1,) = getReserves(); // gas savings
|
(uint112 _reserve0, uint112 _reserve1,) = getReserves(); // gas savings
|
||||||
address _token0 = token0; // gas savings
|
require(amount0Out < _reserve0 && amount1Out < _reserve1, 'UniswapV2: INSUFFICIENT_LIQUIDITY');
|
||||||
address _token1 = token1; // gas savings
|
|
||||||
uint balance0;
|
uint balance0;
|
||||||
uint balance1;
|
uint balance1;
|
||||||
uint amountIn;
|
{ // scope for _token{0,1}, avoids stack too deep errors
|
||||||
|
address _token0 = token0;
|
||||||
if (tokenIn == _token0) {
|
address _token1 = token1;
|
||||||
require(amountOut < _reserve1, 'UniswapV2: INSUFFICIENT_LIQUIDITY');
|
require(to != _token0 && to != _token1, 'UniswapV2: INVALID_TO');
|
||||||
balance0 = IERC20(_token0).balanceOf(address(this));
|
if (amount0Out > 0) _safeTransfer(_token0, to, amount0Out); // optimistically transfer tokens
|
||||||
amountIn = balance0.sub(_reserve0);
|
if (amount1Out > 0) _safeTransfer(_token1, to, amount1Out); // optimistically transfer tokens
|
||||||
require(amountIn > 0, 'UniswapV2: INSUFFICIENT_INPUT_AMOUNT');
|
if (data.length > 0) IUniswapV2Callee(to).uniswapV2Call(msg.sender, amount0Out, amount1Out, data);
|
||||||
require(amountIn.mul(_reserve1 - amountOut).mul(997) >= amountOut.mul(_reserve0).mul(1000), 'UniswapV2: K');
|
balance0 = IERC20(_token0).balanceOf(address(this));
|
||||||
_safeTransfer(_token1, to, amountOut);
|
balance1 = IERC20(_token1).balanceOf(address(this));
|
||||||
balance1 = IERC20(_token1).balanceOf(address(this));
|
}
|
||||||
} else {
|
uint amount0In = balance0 > _reserve0 - amount0Out ? balance0 - (_reserve0 - amount0Out) : 0;
|
||||||
require(tokenIn == _token1, 'UniswapV2: INVALID_INPUT_TOKEN');
|
uint amount1In = balance1 > _reserve1 - amount1Out ? balance1 - (_reserve1 - amount1Out) : 0;
|
||||||
require(amountOut < _reserve0, 'UniswapV2: INSUFFICIENT_LIQUIDITY');
|
require(amount0In > 0 || amount1In > 0, 'UniswapV2: INSUFFICIENT_INPUT_AMOUNT');
|
||||||
balance1 = IERC20(_token1).balanceOf(address(this));
|
{ // scope for reserve{0,1}Adjusted, avoids stack too deep errors
|
||||||
amountIn = balance1.sub(_reserve1);
|
uint balance0Adjusted = balance0.mul(1000).sub(amount0In.mul(3));
|
||||||
require(amountIn > 0, 'UniswapV2: INSUFFICIENT_INPUT_AMOUNT');
|
uint balance1Adjusted = balance1.mul(1000).sub(amount1In.mul(3));
|
||||||
require(amountIn.mul(_reserve0 - amountOut).mul(997) >= amountOut.mul(_reserve1).mul(1000), 'UniswapV2: K');
|
require(balance0Adjusted.mul(balance1Adjusted) >= uint(_reserve0).mul(_reserve1).mul(1000**2), 'UniswapV2: K');
|
||||||
_safeTransfer(_token0, to, amountOut);
|
|
||||||
balance0 = IERC20(_token0).balanceOf(address(this));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_update(balance0, balance1, _reserve0, _reserve1);
|
_update(balance0, balance1, _reserve0, _reserve1);
|
||||||
emit Swap(msg.sender, tokenIn, amountIn, amountOut, to);
|
emit Swap(msg.sender, amount0In, amount1In, amount0Out, amount1Out, to);
|
||||||
}
|
}
|
||||||
|
|
||||||
// force balances to match reserves
|
// force balances to match reserves
|
||||||
|
|||||||
5
contracts/interfaces/IUniswapV2Callee.sol
Normal file
5
contracts/interfaces/IUniswapV2Callee.sol
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
pragma solidity =0.5.16;
|
||||||
|
|
||||||
|
interface IUniswapV2Callee {
|
||||||
|
function uniswapV2Call(address sender, uint amount0, uint amount1, bytes calldata data) external;
|
||||||
|
}
|
||||||
@ -23,7 +23,14 @@ interface IUniswapV2Exchange {
|
|||||||
|
|
||||||
event Mint(address indexed sender, uint amount0, uint amount1);
|
event Mint(address indexed sender, uint amount0, uint amount1);
|
||||||
event Burn(address indexed sender, uint amount0, uint amount1, address indexed to);
|
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);
|
event Swap(
|
||||||
|
address indexed sender,
|
||||||
|
uint amount0In,
|
||||||
|
uint amount1In,
|
||||||
|
uint amount0Out,
|
||||||
|
uint amount1Out,
|
||||||
|
address indexed to
|
||||||
|
);
|
||||||
event Sync(uint112 reserve0, uint112 reserve1);
|
event Sync(uint112 reserve0, uint112 reserve1);
|
||||||
|
|
||||||
function MINIMUM_LIQUIDITY() external pure returns (uint);
|
function MINIMUM_LIQUIDITY() external pure returns (uint);
|
||||||
@ -37,7 +44,7 @@ interface IUniswapV2Exchange {
|
|||||||
|
|
||||||
function mint(address to) external returns (uint liquidity);
|
function mint(address to) external returns (uint liquidity);
|
||||||
function burn(address to) external returns (uint amount0, uint amount1);
|
function burn(address to) external returns (uint amount0, uint amount1);
|
||||||
function swap(address tokenIn, uint amountOut, address to) external;
|
function swap(uint amount0Out, uint amount1Out, address to, bytes calldata data) external;
|
||||||
function skim(address to) external;
|
function skim(address to) external;
|
||||||
function sync() external;
|
function sync() external;
|
||||||
|
|
||||||
|
|||||||
@ -12,7 +12,7 @@ const MINIMUM_LIQUIDITY = bigNumberify(10).pow(3)
|
|||||||
chai.use(solidity)
|
chai.use(solidity)
|
||||||
|
|
||||||
const overrides = {
|
const overrides = {
|
||||||
gasLimit: 1000000
|
gasLimit: 9999999
|
||||||
}
|
}
|
||||||
|
|
||||||
describe('UniswapV2Exchange', () => {
|
describe('UniswapV2Exchange', () => {
|
||||||
@ -68,7 +68,7 @@ describe('UniswapV2Exchange', () => {
|
|||||||
await token1.transfer(exchange.address, token1Amount)
|
await token1.transfer(exchange.address, token1Amount)
|
||||||
await exchange.mint(wallet.address, overrides)
|
await exchange.mint(wallet.address, overrides)
|
||||||
}
|
}
|
||||||
const testCases: BigNumber[][] = [
|
const swapTestCases: BigNumber[][] = [
|
||||||
[1, 5, 10, '1662497915624478906'],
|
[1, 5, 10, '1662497915624478906'],
|
||||||
[1, 10, 5, '453305446940074565'],
|
[1, 10, 5, '453305446940074565'],
|
||||||
|
|
||||||
@ -78,15 +78,34 @@ describe('UniswapV2Exchange', () => {
|
|||||||
[1, 10, 10, '906610893880149131'],
|
[1, 10, 10, '906610893880149131'],
|
||||||
[1, 100, 100, '987158034397061298'],
|
[1, 100, 100, '987158034397061298'],
|
||||||
[1, 1000, 1000, '996006981039903216']
|
[1, 1000, 1000, '996006981039903216']
|
||||||
].map(a => a.map((n, i) => (i === 3 ? bigNumberify(n) : expandTo18Decimals(n as number))))
|
].map(a => a.map(n => (typeof n === 'string' ? bigNumberify(n) : expandTo18Decimals(n))))
|
||||||
testCases.forEach((testCase, i) => {
|
swapTestCases.forEach((swapTestCase, i) => {
|
||||||
it(`getInputPrice:${i}`, async () => {
|
it(`getInputPrice:${i}`, async () => {
|
||||||
await addLiquidity(testCase[1], testCase[2])
|
const [swapAmount, token0Amount, token1Amount, expectedOutputAmount] = swapTestCase
|
||||||
await token0.transfer(exchange.address, testCase[0])
|
await addLiquidity(token0Amount, token1Amount)
|
||||||
await expect(exchange.swap(token0.address, testCase[3].add(1), wallet.address, overrides)).to.be.revertedWith(
|
await token0.transfer(exchange.address, swapAmount)
|
||||||
|
await expect(exchange.swap(0, expectedOutputAmount.add(1), wallet.address, '0x', overrides)).to.be.revertedWith(
|
||||||
'UniswapV2: K'
|
'UniswapV2: K'
|
||||||
)
|
)
|
||||||
await exchange.swap(token0.address, testCase[3], wallet.address, overrides)
|
await exchange.swap(0, expectedOutputAmount, wallet.address, '0x', overrides)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
const optimisticTestCases: BigNumber[][] = [
|
||||||
|
['997000000000000000', 5, 10, 1], // given amountIn, amountOut = floor(amountIn * .997)
|
||||||
|
['997000000000000000', 10, 5, 1],
|
||||||
|
['997000000000000000', 5, 5, 1],
|
||||||
|
[1, 5, 5, '1003009027081243732'] // given amountOut, amountIn = ceiling(amountOut / .997)
|
||||||
|
].map(a => a.map(n => (typeof n === 'string' ? bigNumberify(n) : expandTo18Decimals(n))))
|
||||||
|
optimisticTestCases.forEach((optimisticTestCase, i) => {
|
||||||
|
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(
|
||||||
|
'UniswapV2: K'
|
||||||
|
)
|
||||||
|
await exchange.swap(outputAmount, 0, wallet.address, '0x', overrides)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -98,13 +117,13 @@ describe('UniswapV2Exchange', () => {
|
|||||||
const swapAmount = expandTo18Decimals(1)
|
const swapAmount = expandTo18Decimals(1)
|
||||||
const expectedOutputAmount = bigNumberify('1662497915624478906')
|
const expectedOutputAmount = bigNumberify('1662497915624478906')
|
||||||
await token0.transfer(exchange.address, swapAmount)
|
await token0.transfer(exchange.address, swapAmount)
|
||||||
await expect(exchange.swap(token0.address, expectedOutputAmount, wallet.address, overrides))
|
await expect(exchange.swap(0, expectedOutputAmount, wallet.address, '0x', overrides))
|
||||||
.to.emit(token1, 'Transfer')
|
.to.emit(token1, 'Transfer')
|
||||||
.withArgs(exchange.address, wallet.address, expectedOutputAmount)
|
.withArgs(exchange.address, wallet.address, expectedOutputAmount)
|
||||||
.to.emit(exchange, 'Sync')
|
.to.emit(exchange, 'Sync')
|
||||||
.withArgs(token0Amount.add(swapAmount), token1Amount.sub(expectedOutputAmount))
|
.withArgs(token0Amount.add(swapAmount), token1Amount.sub(expectedOutputAmount))
|
||||||
.to.emit(exchange, 'Swap')
|
.to.emit(exchange, 'Swap')
|
||||||
.withArgs(wallet.address, token0.address, swapAmount, expectedOutputAmount, wallet.address)
|
.withArgs(wallet.address, swapAmount, 0, 0, expectedOutputAmount, wallet.address)
|
||||||
|
|
||||||
const reserves = await exchange.getReserves()
|
const reserves = await exchange.getReserves()
|
||||||
expect(reserves[0]).to.eq(token0Amount.add(swapAmount))
|
expect(reserves[0]).to.eq(token0Amount.add(swapAmount))
|
||||||
@ -125,13 +144,13 @@ describe('UniswapV2Exchange', () => {
|
|||||||
const swapAmount = expandTo18Decimals(1)
|
const swapAmount = expandTo18Decimals(1)
|
||||||
const expectedOutputAmount = bigNumberify('453305446940074565')
|
const expectedOutputAmount = bigNumberify('453305446940074565')
|
||||||
await token1.transfer(exchange.address, swapAmount)
|
await token1.transfer(exchange.address, swapAmount)
|
||||||
await expect(exchange.swap(token1.address, expectedOutputAmount, wallet.address, overrides))
|
await expect(exchange.swap(expectedOutputAmount, 0, wallet.address, '0x', overrides))
|
||||||
.to.emit(token0, 'Transfer')
|
.to.emit(token0, 'Transfer')
|
||||||
.withArgs(exchange.address, wallet.address, expectedOutputAmount)
|
.withArgs(exchange.address, wallet.address, expectedOutputAmount)
|
||||||
.to.emit(exchange, 'Sync')
|
.to.emit(exchange, 'Sync')
|
||||||
.withArgs(token0Amount.sub(expectedOutputAmount), token1Amount.add(swapAmount))
|
.withArgs(token0Amount.sub(expectedOutputAmount), token1Amount.add(swapAmount))
|
||||||
.to.emit(exchange, 'Swap')
|
.to.emit(exchange, 'Swap')
|
||||||
.withArgs(wallet.address, token1.address, swapAmount, expectedOutputAmount, wallet.address)
|
.withArgs(wallet.address, 0, swapAmount, expectedOutputAmount, 0, wallet.address)
|
||||||
|
|
||||||
const reserves = await exchange.getReserves()
|
const reserves = await exchange.getReserves()
|
||||||
expect(reserves[0]).to.eq(token0Amount.sub(expectedOutputAmount))
|
expect(reserves[0]).to.eq(token0Amount.sub(expectedOutputAmount))
|
||||||
@ -157,7 +176,7 @@ describe('UniswapV2Exchange', () => {
|
|||||||
const expectedOutputAmount = bigNumberify('453305446940074565')
|
const expectedOutputAmount = bigNumberify('453305446940074565')
|
||||||
await token0.transfer(exchange.address, swapAmount)
|
await token0.transfer(exchange.address, swapAmount)
|
||||||
await mineBlock(provider, (await provider.getBlock('latest')).timestamp + 1)
|
await mineBlock(provider, (await provider.getBlock('latest')).timestamp + 1)
|
||||||
const gasCost = await exchange.estimate.swap(token0.address, expectedOutputAmount, wallet.address, overrides)
|
const gasCost = await exchange.estimate.swap(0, expectedOutputAmount, wallet.address, '0x', overrides)
|
||||||
console.log(`Gas required for swap: ${gasCost}`)
|
console.log(`Gas required for swap: ${gasCost}`)
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -209,7 +228,7 @@ describe('UniswapV2Exchange', () => {
|
|||||||
await token0.transfer(exchange.address, swapAmount)
|
await token0.transfer(exchange.address, swapAmount)
|
||||||
await mineBlock(provider, blockTimestamp + 10)
|
await mineBlock(provider, blockTimestamp + 10)
|
||||||
// swap to a new price eagerly instead of syncing
|
// swap to a new price eagerly instead of syncing
|
||||||
await exchange.swap(token0.address, expandTo18Decimals(1), wallet.address, overrides) // make the price nice
|
await exchange.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.price0CumulativeLast()).to.eq(initialPrice[0].mul(10))
|
||||||
expect(await exchange.price1CumulativeLast()).to.eq(initialPrice[1].mul(10))
|
expect(await exchange.price1CumulativeLast()).to.eq(initialPrice[1].mul(10))
|
||||||
@ -232,7 +251,7 @@ describe('UniswapV2Exchange', () => {
|
|||||||
const swapAmount = expandTo18Decimals(1)
|
const swapAmount = expandTo18Decimals(1)
|
||||||
const expectedOutputAmount = bigNumberify('996006981039903216')
|
const expectedOutputAmount = bigNumberify('996006981039903216')
|
||||||
await token1.transfer(exchange.address, swapAmount)
|
await token1.transfer(exchange.address, swapAmount)
|
||||||
await exchange.swap(token1.address, expectedOutputAmount, wallet.address, overrides)
|
await exchange.swap(expectedOutputAmount, 0, wallet.address, '0x', overrides)
|
||||||
|
|
||||||
const expectedLiquidity = expandTo18Decimals(1000)
|
const expectedLiquidity = expandTo18Decimals(1000)
|
||||||
await exchange.transfer(exchange.address, expectedLiquidity.sub(MINIMUM_LIQUIDITY))
|
await exchange.transfer(exchange.address, expectedLiquidity.sub(MINIMUM_LIQUIDITY))
|
||||||
@ -250,7 +269,7 @@ describe('UniswapV2Exchange', () => {
|
|||||||
const swapAmount = expandTo18Decimals(1)
|
const swapAmount = expandTo18Decimals(1)
|
||||||
const expectedOutputAmount = bigNumberify('996006981039903216')
|
const expectedOutputAmount = bigNumberify('996006981039903216')
|
||||||
await token1.transfer(exchange.address, swapAmount)
|
await token1.transfer(exchange.address, swapAmount)
|
||||||
await exchange.swap(token1.address, expectedOutputAmount, wallet.address, overrides)
|
await exchange.swap(expectedOutputAmount, 0, wallet.address, '0x', overrides)
|
||||||
|
|
||||||
const expectedLiquidity = expandTo18Decimals(1000)
|
const expectedLiquidity = expandTo18Decimals(1000)
|
||||||
await exchange.transfer(exchange.address, expectedLiquidity.sub(MINIMUM_LIQUIDITY))
|
await exchange.transfer(exchange.address, expectedLiquidity.sub(MINIMUM_LIQUIDITY))
|
||||||
|
|||||||
@ -12,8 +12,12 @@ interface FactoryFixture {
|
|||||||
factory: Contract
|
factory: Contract
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const overrides = {
|
||||||
|
gasLimit: 9999999
|
||||||
|
}
|
||||||
|
|
||||||
export async function factoryFixture(_: Web3Provider, [wallet]: Wallet[]): Promise<FactoryFixture> {
|
export async function factoryFixture(_: Web3Provider, [wallet]: Wallet[]): Promise<FactoryFixture> {
|
||||||
const factory = await deployContract(wallet, UniswapV2Factory, [wallet.address])
|
const factory = await deployContract(wallet, UniswapV2Factory, [wallet.address], overrides)
|
||||||
return { factory }
|
return { factory }
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -26,10 +30,10 @@ interface ExchangeFixture extends FactoryFixture {
|
|||||||
export async function exchangeFixture(provider: Web3Provider, [wallet]: Wallet[]): Promise<ExchangeFixture> {
|
export async function exchangeFixture(provider: Web3Provider, [wallet]: Wallet[]): Promise<ExchangeFixture> {
|
||||||
const { factory } = await factoryFixture(provider, [wallet])
|
const { factory } = await factoryFixture(provider, [wallet])
|
||||||
|
|
||||||
const tokenA = await deployContract(wallet, ERC20, [expandTo18Decimals(10000)])
|
const tokenA = await deployContract(wallet, ERC20, [expandTo18Decimals(10000)], overrides)
|
||||||
const tokenB = await deployContract(wallet, ERC20, [expandTo18Decimals(10000)])
|
const tokenB = await deployContract(wallet, ERC20, [expandTo18Decimals(10000)], overrides)
|
||||||
|
|
||||||
await factory.createExchange(tokenA.address, tokenB.address)
|
await factory.createExchange(tokenA.address, tokenB.address, overrides)
|
||||||
const exchangeAddress = await factory.getExchange(tokenA.address, tokenB.address)
|
const exchangeAddress = await factory.getExchange(tokenA.address, tokenB.address)
|
||||||
const exchange = new Contract(exchangeAddress, JSON.stringify(UniswapV2Exchange.abi), provider).connect(wallet)
|
const exchange = new Contract(exchangeAddress, JSON.stringify(UniswapV2Exchange.abi), provider).connect(wallet)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user