From f263dab9b5b8555dcb3d3ac0cd5bd0f1bb8b92ff Mon Sep 17 00:00:00 2001 From: Noah Zinsmeister Date: Fri, 13 Dec 2019 14:39:09 -0500 Subject: [PATCH] add more tests --- README.md | 2 +- contracts/UniswapV2.sol | 2 +- test/ERC20.spec.ts | 99 +++++++++++++++++----------- test/UniswapV2.spec.ts | 117 +++++++++++++++++++++++++--------- test/UniswapV2Factory.spec.ts | 35 ++++++++-- test/shared/fixtures.ts | 3 +- test/shared/utilities.ts | 41 ++++++------ 7 files changed, 201 insertions(+), 98 deletions(-) diff --git a/README.md b/README.md index 3b815b6..33638d3 100644 --- a/README.md +++ b/README.md @@ -29,4 +29,4 @@ yarn test - [dapp-bin math](https://github.com/ethereum/dapp-bin/pull/50) - [OpenZeppelin ECDSA](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/81b1e4810761b088922dbd19a0642873ea581176/contracts/cryptography/ECDSA.sol) - [OpenZeppelin SafeERC20](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/81b1e4810761b088922dbd19a0642873ea581176/contracts/token/ERC20/SafeERC20.sol) -- [DAI token](https://github.com/makerdao/dss/blob/17be7db1c663d8069308c6b78fa5c5f9d71134a3/src/dai.sol) +- [DAI](https://github.com/makerdao/dss/blob/17be7db1c663d8069308c6b78fa5c5f9d71134a3/src/dai.sol) diff --git a/contracts/UniswapV2.sol b/contracts/UniswapV2.sol index c28a325..4f03970 100644 --- a/contracts/UniswapV2.sol +++ b/contracts/UniswapV2.sol @@ -1,10 +1,10 @@ pragma solidity 0.5.14; import "./interfaces/IUniswapV2.sol"; -import "./interfaces/IUniswapV2Factory.sol"; import "./ERC20.sol"; import "./libraries/UQ112x112.sol"; import "./libraries/Math.sol"; +import "./interfaces/IUniswapV2Factory.sol"; contract UniswapV2 is IUniswapV2, ERC20("Uniswap V2", "UNI-V2", 18, 0) { using SafeMath for uint; diff --git a/test/ERC20.spec.ts b/test/ERC20.spec.ts index 0b79565..e58bc04 100644 --- a/test/ERC20.spec.ts +++ b/test/ERC20.spec.ts @@ -3,7 +3,7 @@ import chai from 'chai' import { solidity, createMockProvider, getWallets, deployContract } from 'ethereum-waffle' import { Contract } from 'ethers' import { AddressZero, MaxUint256 } from 'ethers/constants' -import { bigNumberify, hexlify } from 'ethers/utils' +import { bigNumberify, hexlify, keccak256, defaultAbiCoder, toUtf8Bytes } from 'ethers/utils' import { ecsign } from 'ethereumjs-util' import { expandTo18Decimals, getApprovalDigest } from './shared/utilities' @@ -35,18 +35,38 @@ describe('ERC20', () => { ]) }) - it('name, symbol, decimals, totalSupply', async () => { - expect(await token.name()).to.eq(TOKEN_DETAILS.name) + it('name, symbol, decimals, totalSupply, balanceOf, DOMAIN_SEPARATOR, APPROVE_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(await token.DOMAIN_SEPARATOR()).to.eq( + keccak256( + defaultAbiCoder.encode( + ['bytes32', 'bytes32', 'bytes32', 'uint256', 'address'], + [ + keccak256( + toUtf8Bytes('EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)') + ), + keccak256(toUtf8Bytes(name)), + keccak256(toUtf8Bytes('1')), + 1, + token.address + ] + ) + ) + ) + expect(await token.APPROVE_TYPEHASH()).to.eq( + keccak256(toUtf8Bytes('Approve(address owner,address spender,uint256 value,uint256 nonce,uint256 expiration)')) + ) }) it('transfer', async () => { 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(other.address)).to.eq(TEST_AMOUNT) }) @@ -55,7 +75,6 @@ describe('ERC20', () => { await expect(token.burn(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)) }) @@ -64,10 +83,47 @@ describe('ERC20', () => { await expect(token.approve(other.address, TEST_AMOUNT)) .to.emit(token, 'Approval') .withArgs(wallet.address, other.address, TEST_AMOUNT) - expect(await token.allowance(wallet.address, other.address)).to.eq(TEST_AMOUNT) }) + it('transferFrom', async () => { + await token.approve(other.address, TEST_AMOUNT) + await expect(token.connect(other).transferFrom(wallet.address, other.address, TEST_AMOUNT)) + .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(other.address)).to.eq(TEST_AMOUNT) + }) + + it('transferFrom:max', async () => { + await token.approve(other.address, MaxUint256) + await expect(token.connect(other).transferFrom(wallet.address, other.address, TEST_AMOUNT)) + .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(other.address)).to.eq(TEST_AMOUNT) + }) + + it('burnFrom', async () => { + await token.approve(other.address, TEST_AMOUNT) + await expect(token.connect(other).burnFrom(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.revertedWith( + 'ds-math-sub-underflow' + ) + await expect(token.connect(other).transfer(wallet.address, 1)).to.be.revertedWith('ds-math-sub-underflow') + }) + it('approveMeta', async () => { const nonce = await token.nonces(wallet.address) const expiration = MaxUint256 @@ -85,38 +141,7 @@ describe('ERC20', () => { ) .to.emit(token, 'Approval') .withArgs(wallet.address, other.address, TEST_AMOUNT) - expect(await token.nonces(wallet.address)).to.eq(bigNumberify(1)) expect(await token.allowance(wallet.address, other.address)).to.eq(TEST_AMOUNT) }) - - it('transferFrom', async () => { - await token.approve(other.address, TEST_AMOUNT) - await expect(token.connect(other).transferFrom(wallet.address, other.address, TEST_AMOUNT)) - .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(other.address)).to.eq(TEST_AMOUNT) - }) - - it('burnFrom', async () => { - await token.approve(other.address, TEST_AMOUNT) - await expect(token.connect(other).burnFrom(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.revertedWith( - 'ds-math-sub-underflow' - ) - await expect(token.connect(other).transfer(wallet.address, 1)).to.be.revertedWith('ds-math-sub-underflow') - }) }) diff --git a/test/UniswapV2.spec.ts b/test/UniswapV2.spec.ts index 9ee4055..1a41e48 100644 --- a/test/UniswapV2.spec.ts +++ b/test/UniswapV2.spec.ts @@ -61,22 +61,22 @@ describe('UniswapV2', () => { it('mintLiquidity', async () => { const token0Amount = expandTo18Decimals(1) const token1Amount = expandTo18Decimals(4) - const expectedLiquidity = expandTo18Decimals(2) - - expect(await exchange.reserve0()).to.eq(bigNumberify(0)) - expect(await exchange.reserve1()).to.eq(bigNumberify(0)) - await token0.transfer(exchange.address, token0Amount) await token1.transfer(exchange.address, token1Amount) + + const expectedLiquidity = expandTo18Decimals(2) await expect(exchange.connect(wallet).mintLiquidity(wallet.address)) - .to.emit(exchange, 'LiquidityMinted') - .withArgs(wallet.address, token0Amount, token1Amount) .to.emit(exchange, 'Transfer') .withArgs(AddressZero, wallet.address, expectedLiquidity) + .to.emit(exchange, 'ReservesUpdated') + .withArgs(token0Amount, token1Amount) + .to.emit(exchange, 'LiquidityMinted') + .withArgs(wallet.address, token0Amount, token1Amount) expect(await exchange.totalSupply()).to.eq(expectedLiquidity) expect(await exchange.balanceOf(wallet.address)).to.eq(expectedLiquidity) - + expect(await token0.balanceOf(exchange.address)).to.eq(token0Amount) + expect(await token1.balanceOf(exchange.address)).to.eq(token1Amount) expect(await exchange.reserve0()).to.eq(token0Amount) expect(await exchange.reserve1()).to.eq(token1Amount) }) @@ -87,21 +87,7 @@ describe('UniswapV2', () => { await exchange.connect(wallet).mintLiquidity(wallet.address) } - it('swap:gas', async () => { - const token0Amount = expandTo18Decimals(5) - const token1Amount = expandTo18Decimals(10) - await addLiquidity(token0Amount, token1Amount) - - const swapAmount = expandTo18Decimals(1) - await token0.transfer(exchange.address, swapAmount) - await exchange.connect(wallet).swap0(wallet.address) - - await token0.transfer(exchange.address, swapAmount) - const gasCost = await exchange.estimate.swap0(wallet.address) - console.log(`Gas cost of swap: ${gasCost}`) - }) - - it('swap', async () => { + it('swap0', async () => { const token0Amount = expandTo18Decimals(5) const token1Amount = expandTo18Decimals(10) await addLiquidity(token0Amount, token1Amount) @@ -110,41 +96,110 @@ describe('UniswapV2', () => { const expectedOutputAmount = bigNumberify('1662497915624478906') await token0.transfer(exchange.address, swapAmount) await expect(exchange.connect(wallet).swap0(wallet.address)) - .to.emit(exchange, 'Swap') - .withArgs(wallet.address, wallet.address, token0.address, swapAmount, expectedOutputAmount) .to.emit(exchange, 'ReservesUpdated') .withArgs(token0Amount.add(swapAmount), token1Amount.sub(expectedOutputAmount)) + .to.emit(exchange, 'Swap') + .withArgs(wallet.address, wallet.address, token0.address, swapAmount, expectedOutputAmount) + expect(await exchange.reserve0()).to.eq(token0Amount.add(swapAmount)) + expect(await exchange.reserve1()).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)) - const totalSupplyToken0 = await token0.totalSupply() const totalSupplyToken1 = await token1.totalSupply() expect(await token0.balanceOf(wallet.address)).to.eq(totalSupplyToken0.sub(token0Amount).sub(swapAmount)) expect(await token1.balanceOf(wallet.address)).to.eq(totalSupplyToken1.sub(token1Amount).add(expectedOutputAmount)) }) + it('swap1', async () => { + const token0Amount = expandTo18Decimals(5) + const token1Amount = expandTo18Decimals(10) + await addLiquidity(token0Amount, token1Amount) + + const swapAmount = expandTo18Decimals(1) + const expectedOutputAmount = bigNumberify('453305446940074565') + await token1.transfer(exchange.address, swapAmount) + await expect(exchange.connect(wallet).swap1(wallet.address)) + .to.emit(exchange, 'ReservesUpdated') + .withArgs(token0Amount.sub(expectedOutputAmount), token1Amount.add(swapAmount)) + .to.emit(exchange, 'Swap') + .withArgs(wallet.address, wallet.address, token1.address, expectedOutputAmount, swapAmount) + + expect(await exchange.reserve0()).to.eq(token0Amount.sub(expectedOutputAmount)) + expect(await exchange.reserve1()).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)) + const totalSupplyToken0 = await token0.totalSupply() + const totalSupplyToken1 = await token1.totalSupply() + expect(await token0.balanceOf(wallet.address)).to.eq(totalSupplyToken0.sub(token0Amount).add(expectedOutputAmount)) + expect(await token1.balanceOf(wallet.address)).to.eq(totalSupplyToken1.sub(token1Amount).sub(swapAmount)) + }) + + it('swap:gas', async () => { + const token0Amount = expandTo18Decimals(5) + const token1Amount = expandTo18Decimals(10) + await addLiquidity(token0Amount, token1Amount) + + // ensure that setting price{0,1}CumulativeLast for the first time doesn't affect our gas math + await exchange.connect(wallet).sync() + + const swapAmount = expandTo18Decimals(1) + await token0.transfer(exchange.address, swapAmount) + const gasCost = await exchange.estimate.swap0(wallet.address) + console.log(`Gas required for swap: ${gasCost}`) + }) + it('burnLiquidity', async () => { const token0Amount = expandTo18Decimals(3) const token1Amount = expandTo18Decimals(3) await addLiquidity(token0Amount, token1Amount) - const liquidity = expandTo18Decimals(3) - await exchange.connect(wallet).transfer(exchange.address, liquidity) + const expectedLiquidity = expandTo18Decimals(3) + await exchange.connect(wallet).transfer(exchange.address, expectedLiquidity) + // this test is bugged, it catches the token{0,1} transfers before the lp transfers await expect(exchange.connect(wallet).burnLiquidity(wallet.address)) + // .to.emit(exchange, 'Transfer') + // .withArgs(exchange.address, AddressZero, expectedLiquidity) .to.emit(exchange, 'LiquidityBurned') .withArgs(wallet.address, wallet.address, token0Amount, token1Amount) .to.emit(exchange, 'ReservesUpdated') - .withArgs(bigNumberify(0), bigNumberify(0)) + .withArgs(0, 0) expect(await exchange.balanceOf(wallet.address)).to.eq(0) + expect(await exchange.totalSupply()).to.eq(0) expect(await token0.balanceOf(exchange.address)).to.eq(0) expect(await token1.balanceOf(exchange.address)).to.eq(0) - const totalSupplyToken0 = await token0.totalSupply() const totalSupplyToken1 = await token1.totalSupply() - expect(await token0.balanceOf(wallet.address)).to.eq(totalSupplyToken0) expect(await token1.balanceOf(wallet.address)).to.eq(totalSupplyToken1) }) + + it('price{0,1}CumulativeLast', async () => { + const token0Amount = expandTo18Decimals(3) + const token1Amount = expandTo18Decimals(3) + await addLiquidity(token0Amount, token1Amount) + + const blockNumber = await exchange.blockNumberLast() + expect(await exchange.price0CumulativeLast()).to.eq(0) + expect(await exchange.price1CumulativeLast()).to.eq(0) + + await exchange.connect(wallet).sync() + expect(await exchange.price0CumulativeLast()).to.eq(bigNumberify(2).pow(112)) + expect(await exchange.price1CumulativeLast()).to.eq(bigNumberify(2).pow(112)) + expect(await exchange.blockNumberLast()).to.eq(blockNumber + 1) + + await exchange.connect(wallet).sync() + expect(await exchange.price0CumulativeLast()).to.eq( + bigNumberify(2) + .pow(112) + .mul(2) + ) + expect(await exchange.price1CumulativeLast()).to.eq( + bigNumberify(2) + .pow(112) + .mul(2) + ) + expect(await exchange.blockNumberLast()).to.eq(blockNumber + 2) + }) }) diff --git a/test/UniswapV2Factory.spec.ts b/test/UniswapV2Factory.spec.ts index 24c9716..2f23e19 100644 --- a/test/UniswapV2Factory.spec.ts +++ b/test/UniswapV2Factory.spec.ts @@ -19,25 +19,37 @@ const TEST_ADDRESSES = { describe('UniswapV2Factory', () => { const provider = createMockProvider(path.join(__dirname, '..', 'waffle.json')) - const [wallet] = getWallets(provider) + const [wallet, other] = getWallets(provider) const loadFixture = createFixtureLoader(provider, [wallet]) let bytecode: string let factory: Contract beforeEach(async () => { - const { bytecode: _bytecode, factory: _factory } = (await loadFixture(factoryFixture as any)) as FactoryFixture + const { bytecode: _bytecode, factory: _factory }: FactoryFixture = await loadFixture(factoryFixture as any) bytecode = _bytecode factory = _factory }) - it('exchangeBytecode', async () => { + it('exchangeBytecode, feeAddress, feeOn, exchangesCount', async () => { expect(await factory.exchangeBytecode()).to.eq(bytecode) + expect(await factory.feeAddress()).to.eq(wallet.address) + expect(await factory.feeOn()).to.eq(false) expect(await factory.exchangesCount()).to.eq(0) }) + it('sortTokens', async () => { + expect(await factory.sortTokens(TEST_ADDRESSES.token0, TEST_ADDRESSES.token1)).to.deep.eq([ + TEST_ADDRESSES.token0, + TEST_ADDRESSES.token1 + ]) + expect(await factory.sortTokens(TEST_ADDRESSES.token1, TEST_ADDRESSES.token0)).to.deep.eq([ + TEST_ADDRESSES.token0, + TEST_ADDRESSES.token1 + ]) + }) + async function createExchange(tokens: string[]) { const create2Address = getCreate2Address(factory.address, TEST_ADDRESSES.token0, TEST_ADDRESSES.token1, bytecode) - await expect(factory.createExchange(...tokens)) .to.emit(factory, 'ExchangeCreated') .withArgs(TEST_ADDRESSES.token0, TEST_ADDRESSES.token1, create2Address, bigNumberify(1)) @@ -45,7 +57,6 @@ describe('UniswapV2Factory', () => { await expect(factory.createExchange(...tokens.slice().reverse())).to.be.revertedWith( 'UniswapV2Factory: EXCHANGE_EXISTS' ) - expect(await factory.getExchange(...tokens)).to.eq(create2Address) expect(await factory.getExchange(...tokens.slice().reverse())).to.eq(create2Address) expect(await factory.getTokens(create2Address)).to.deep.eq([TEST_ADDRESSES.token0, TEST_ADDRESSES.token1]) @@ -56,6 +67,7 @@ describe('UniswapV2Factory', () => { 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) + expect(await exchange.feeAddress()).to.eq(wallet.address) } it('createExchange', async () => { @@ -65,4 +77,17 @@ describe('UniswapV2Factory', () => { it('createExchange:reverse', async () => { await createExchange([TEST_ADDRESSES.token1, TEST_ADDRESSES.token0]) }) + + it('setFeeAddress', async () => { + await expect(factory.connect(other).setFeeAddress(other.address)).to.be.revertedWith('UniswapV2Factory: FORBIDDEN') + await factory.setFeeAddress(other.address) + expect(await factory.feeAddress()).to.eq(other.address) + await expect(factory.setFeeAddress(wallet.address)).to.be.revertedWith('UniswapV2Factory: FORBIDDEN') + }) + + it('turnFeeOn', async () => { + await expect(factory.connect(other).turnFeeOn()).to.be.revertedWith('UniswapV2Factory: FORBIDDEN') + await factory.turnFeeOn() + expect(await factory.feeOn()).to.eq(true) + }) }) diff --git a/test/shared/fixtures.ts b/test/shared/fixtures.ts index 047905c..1232836 100644 --- a/test/shared/fixtures.ts +++ b/test/shared/fixtures.ts @@ -15,10 +15,9 @@ export interface FactoryFixture { export async function factoryFixture(provider: providers.Web3Provider, [wallet]: Wallet[]): Promise { const bytecode = `0x${UniswapV2.evm.bytecode.object}` - const factory = await deployContract(wallet, UniswapV2Factory, [bytecode, AddressZero], { + const factory = await deployContract(wallet, UniswapV2Factory, [bytecode, wallet.address], { gasLimit: (provider._web3Provider as any).options.gasLimit }) - return { bytecode, factory } } diff --git a/test/shared/utilities.ts b/test/shared/utilities.ts index 99d98aa..b9928e1 100644 --- a/test/shared/utilities.ts +++ b/test/shared/utilities.ts @@ -9,6 +9,10 @@ import { solidityPack } from 'ethers/utils' +export function expandTo18Decimals(n: number): BigNumber { + return bigNumberify(n).mul(bigNumberify(10).pow(18)) +} + const APPROVE_TYPEHASH = keccak256( toUtf8Bytes('Approve(address owner,address spender,uint256 value,uint256 nonce,uint256 expiration)') ) @@ -29,8 +33,22 @@ const GET_DOMAIN_SEPARATOR = async (token: Contract) => { ) } -export function expandTo18Decimals(n: number): BigNumber { - return bigNumberify(n).mul(bigNumberify(10).pow(18)) +export function getCreate2Address( + factoryAddress: string, + token0Address: string, + token1Address: string, + bytecode: string +): string { + const create2Inputs = [ + '0xff', + factoryAddress, + keccak256(solidityPack(['address', 'address'], [token0Address, token1Address])), + keccak256(bytecode) + ] + + const sanitizedInputs = `0x${create2Inputs.map(i => i.slice(2)).join('')}` + + return getAddress(`0x${keccak256(sanitizedInputs).slice(-40)}`) } interface Approve { @@ -38,7 +56,6 @@ interface Approve { spender: string value: BigNumber } - export async function getApprovalDigest( token: Contract, approve: Approve, @@ -64,24 +81,6 @@ export async function getApprovalDigest( ) } -export function getCreate2Address( - factoryAddress: string, - token0Address: string, - token1Address: string, - bytecode: string -): string { - const create2Inputs = [ - '0xff', - factoryAddress, - keccak256(solidityPack(['address', 'address'], [token0Address, token1Address])), - keccak256(bytecode) - ] - - const sanitizedInputs = `0x${create2Inputs.map(i => i.slice(2)).join('')}` - - return getAddress(`0x${keccak256(sanitizedInputs).slice(-40)}`) -} - async function mineBlock(provider: providers.Web3Provider, timestamp?: number): Promise { await new Promise((resolve, reject) => { ;(provider._web3Provider.sendAsync as any)(