735546619e
Signed-off-by: T-Hax <>
178 lines
6.9 KiB
TypeScript
178 lines
6.9 KiB
TypeScript
import { Wallet } from 'ethers'
|
|
import { ethers, waffle } from 'hardhat'
|
|
import { UniswapV3Factory } from '../typechain/UniswapV3Factory'
|
|
import { expect } from './shared/expect'
|
|
import snapshotGasCost from './shared/snapshotGasCost'
|
|
|
|
import { FeeAmount, getCreate2Address, TICK_SPACINGS } from './shared/utilities'
|
|
|
|
const { constants } = ethers
|
|
|
|
const TEST_ADDRESSES: [string, string] = [
|
|
'0x1000000000000000000000000000000000000000',
|
|
'0x2000000000000000000000000000000000000000',
|
|
]
|
|
|
|
const createFixtureLoader = waffle.createFixtureLoader
|
|
|
|
describe('UniswapV3Factory', () => {
|
|
let wallet: Wallet, other: Wallet
|
|
|
|
let factory: UniswapV3Factory
|
|
let poolBytecode: string
|
|
const fixture = async () => {
|
|
const factoryFactory = await ethers.getContractFactory('UniswapV3Factory')
|
|
return (await factoryFactory.deploy()) as UniswapV3Factory
|
|
}
|
|
|
|
let loadFixture: ReturnType<typeof createFixtureLoader>
|
|
before('create fixture loader', async () => {
|
|
;[wallet, other] = await (ethers as any).getSigners()
|
|
|
|
loadFixture = createFixtureLoader([wallet, other])
|
|
})
|
|
|
|
before('load pool bytecode', async () => {
|
|
poolBytecode = (await ethers.getContractFactory('UniswapV3Pool')).bytecode
|
|
})
|
|
|
|
beforeEach('deploy factory', async () => {
|
|
factory = await loadFixture(fixture)
|
|
})
|
|
|
|
it('owner is deployer', async () => {
|
|
expect(await factory.owner()).to.eq(wallet.address)
|
|
})
|
|
|
|
it('factory bytecode size', async () => {
|
|
expect(((await waffle.provider.getCode(factory.address)).length - 2) / 2).to.matchSnapshot()
|
|
})
|
|
|
|
it('pool bytecode size', async () => {
|
|
await factory.createPool(TEST_ADDRESSES[0], TEST_ADDRESSES[1], FeeAmount.MEDIUM)
|
|
const poolAddress = getCreate2Address(factory.address, TEST_ADDRESSES, FeeAmount.MEDIUM, poolBytecode)
|
|
expect(((await waffle.provider.getCode(poolAddress)).length - 2) / 2).to.matchSnapshot()
|
|
})
|
|
|
|
it('initial enabled fee amounts', async () => {
|
|
expect(await factory.feeAmountTickSpacing(FeeAmount.LOW)).to.eq(TICK_SPACINGS[FeeAmount.LOW])
|
|
expect(await factory.feeAmountTickSpacing(FeeAmount.MEDIUM)).to.eq(TICK_SPACINGS[FeeAmount.MEDIUM])
|
|
expect(await factory.feeAmountTickSpacing(FeeAmount.HIGH)).to.eq(TICK_SPACINGS[FeeAmount.HIGH])
|
|
})
|
|
|
|
async function createAndCheckPool(
|
|
tokens: [string, string],
|
|
feeAmount: FeeAmount,
|
|
tickSpacing: number = TICK_SPACINGS[feeAmount]
|
|
) {
|
|
const create2Address = getCreate2Address(factory.address, tokens, feeAmount, poolBytecode)
|
|
const create = factory.createPool(tokens[0], tokens[1], feeAmount)
|
|
|
|
await expect(create)
|
|
.to.emit(factory, 'PoolCreated')
|
|
.withArgs(TEST_ADDRESSES[0], TEST_ADDRESSES[1], feeAmount, tickSpacing, create2Address)
|
|
|
|
await expect(factory.createPool(tokens[0], tokens[1], feeAmount)).to.be.reverted
|
|
await expect(factory.createPool(tokens[1], tokens[0], feeAmount)).to.be.reverted
|
|
expect(await factory.getPool(tokens[0], tokens[1], feeAmount), 'getPool in order').to.eq(create2Address)
|
|
expect(await factory.getPool(tokens[1], tokens[0], feeAmount), 'getPool in reverse').to.eq(create2Address)
|
|
|
|
const poolContractFactory = await ethers.getContractFactory('UniswapV3Pool')
|
|
const pool = poolContractFactory.attach(create2Address)
|
|
expect(await pool.factory(), 'pool factory address').to.eq(factory.address)
|
|
expect(await pool.token0(), 'pool token0').to.eq(TEST_ADDRESSES[0])
|
|
expect(await pool.token1(), 'pool token1').to.eq(TEST_ADDRESSES[1])
|
|
expect(await pool.fee(), 'pool fee').to.eq(feeAmount)
|
|
expect(await pool.tickSpacing(), 'pool tick spacing').to.eq(tickSpacing)
|
|
}
|
|
|
|
describe('#createPool', () => {
|
|
it('succeeds for low fee pool', async () => {
|
|
await createAndCheckPool(TEST_ADDRESSES, FeeAmount.LOW)
|
|
})
|
|
|
|
it('succeeds for medium fee pool', async () => {
|
|
await createAndCheckPool(TEST_ADDRESSES, FeeAmount.MEDIUM)
|
|
})
|
|
it('succeeds for high fee pool', async () => {
|
|
await createAndCheckPool(TEST_ADDRESSES, FeeAmount.HIGH)
|
|
})
|
|
|
|
it('succeeds if tokens are passed in reverse', async () => {
|
|
await createAndCheckPool([TEST_ADDRESSES[1], TEST_ADDRESSES[0]], FeeAmount.MEDIUM)
|
|
})
|
|
|
|
it('fails if token a == token b', async () => {
|
|
await expect(factory.createPool(TEST_ADDRESSES[0], TEST_ADDRESSES[0], FeeAmount.LOW)).to.be.reverted
|
|
})
|
|
|
|
it('fails if token a is 0 or token b is 0', async () => {
|
|
await expect(factory.createPool(TEST_ADDRESSES[0], constants.AddressZero, FeeAmount.LOW)).to.be.reverted
|
|
await expect(factory.createPool(constants.AddressZero, TEST_ADDRESSES[0], FeeAmount.LOW)).to.be.reverted
|
|
await expect(factory.createPool(constants.AddressZero, constants.AddressZero, FeeAmount.LOW)).to.be.revertedWith(
|
|
''
|
|
)
|
|
})
|
|
|
|
it('fails if fee amount is not enabled', async () => {
|
|
await expect(factory.createPool(TEST_ADDRESSES[0], TEST_ADDRESSES[1], 250)).to.be.reverted
|
|
})
|
|
|
|
it('gas', async () => {
|
|
await snapshotGasCost(factory.createPool(TEST_ADDRESSES[0], TEST_ADDRESSES[1], FeeAmount.MEDIUM))
|
|
})
|
|
})
|
|
|
|
describe('#setOwner', () => {
|
|
it('fails if caller is not owner', async () => {
|
|
await expect(factory.connect(other).setOwner(wallet.address)).to.be.reverted
|
|
})
|
|
|
|
it('updates owner', async () => {
|
|
await factory.setOwner(other.address)
|
|
expect(await factory.owner()).to.eq(other.address)
|
|
})
|
|
|
|
it('emits event', async () => {
|
|
await expect(factory.setOwner(other.address))
|
|
.to.emit(factory, 'OwnerChanged')
|
|
.withArgs(wallet.address, other.address)
|
|
})
|
|
|
|
it('cannot be called by original owner', async () => {
|
|
await factory.setOwner(other.address)
|
|
await expect(factory.setOwner(wallet.address)).to.be.reverted
|
|
})
|
|
})
|
|
|
|
describe('#enableFeeAmount', () => {
|
|
it('fails if caller is not owner', async () => {
|
|
await expect(factory.connect(other).enableFeeAmount(100, 2)).to.be.reverted
|
|
})
|
|
it('fails if fee is too great', async () => {
|
|
await expect(factory.enableFeeAmount(1000000, 10)).to.be.reverted
|
|
})
|
|
it('fails if tick spacing is too small', async () => {
|
|
await expect(factory.enableFeeAmount(500, 0)).to.be.reverted
|
|
})
|
|
it('fails if tick spacing is too large', async () => {
|
|
await expect(factory.enableFeeAmount(500, 16834)).to.be.reverted
|
|
})
|
|
it('fails if already initialized', async () => {
|
|
await factory.enableFeeAmount(100, 5)
|
|
await expect(factory.enableFeeAmount(100, 10)).to.be.reverted
|
|
})
|
|
it('sets the fee amount in the mapping', async () => {
|
|
await factory.enableFeeAmount(100, 5)
|
|
expect(await factory.feeAmountTickSpacing(100)).to.eq(5)
|
|
})
|
|
it('emits an event', async () => {
|
|
await expect(factory.enableFeeAmount(100, 5)).to.emit(factory, 'FeeAmountEnabled').withArgs(100, 5)
|
|
})
|
|
it('enables pool creation', async () => {
|
|
await factory.enableFeeAmount(250, 15)
|
|
await createAndCheckPool([TEST_ADDRESSES[0], TEST_ADDRESSES[1]], 250, 15)
|
|
})
|
|
})
|
|
})
|