@uniswap/v4-core
Version:
🦄 Core smart contracts of Uniswap v4
567 lines (566 loc) • 24.7 kB
text/typescript
// import { ethers, waffle } from 'hardhat'
// import { Wallet } from 'ethers'
// import { PoolManager, PoolSwapTest, PoolDonateTest, PoolModifyPositionTest } from '../typechain'
// import { expect } from './shared/expect'
// import { ADDRESS_ZERO } from './shared/constants'
//
// import { tokensFixture } from './shared/fixtures'
// import snapshotGasCost from '@uniswap/snapshot-gas-cost'
//
// import {
// expandTo18Decimals,
// FeeAmount,
// getMinTick,
// encodeSqrtPriceX96,
// TICK_SPACINGS,
// SwapFunction,
// ModifyPositionFunction,
// DonateFunction,
// getMaxTick,
// MaxUint128,
// SwapToPriceFunction,
// MIN_SQRT_PRICE,
// getPoolId,
// } from './shared/utilities'
//
// const { constants } = ethers
//
// const createFixtureLoader = waffle.createFixtureLoader
//
// type AsyncReturnType<T extends (...args: any) => any> = T extends (...args: any) => Promise<infer U>
// ? U
// : T extends (...args: any) => infer U
// ? U
// : any
//
// describe('PoolManager gas tests', () => {
// let wallet: Wallet, other: Wallet
//
// let loadFixture: ReturnType<typeof createFixtureLoader>
//
// before('create fixture loader', async () => {
// ;[wallet, other] = await (ethers as any).getSigners()
// loadFixture = createFixtureLoader([wallet, other])
// })
//
// const startingPrice = encodeSqrtPriceX96(100001, 100000)
// const startingTick = 0
// const feeAmount = FeeAmount.MEDIUM
// const tickSpacing = TICK_SPACINGS[feeAmount]
// const minTick = getMinTick(tickSpacing)
// const maxTick = getMaxTick(tickSpacing)
//
// describe('ERC20 tokens', () => {
// const gasTestFixture = async ([wallet]: Wallet[]) => {
// const { currency0, currency1 } = await tokensFixture()
//
// const singletonPoolFactory = await ethers.getContractFactory('PoolManager')
// const swapTestFactory = await ethers.getContractFactory('PoolSwapTest')
// const donateTestFactory = await ethers.getContractFactory('PoolDonateTest')
// const mintTestFactory = await ethers.getContractFactory('PoolModifyPositionTest')
// const CONTROLLER_GAS_LIMIT = 50000
// const manager = (await singletonPoolFactory.deploy(CONTROLLER_GAS_LIMIT)) as PoolManager
//
// const swapTest = (await swapTestFactory.deploy(manager.address)) as PoolSwapTest
// const donateTest = (await donateTestFactory.deploy(manager.address)) as PoolDonateTest
// const modifyPositionTest = (await mintTestFactory.deploy(manager.address)) as PoolModifyPositionTest
//
// for (const token of [currency0, currency1]) {
// for (const spender of [swapTest, donateTest, modifyPositionTest]) {
// await token.connect(wallet).approve(spender.address, constants.MaxUint256)
// }
// }
//
// const poolKey = {
// currency0: currency0.address,
// currency1: currency1.address,
// fee: FeeAmount.MEDIUM,
// tickSpacing: 60,
// hooks: ADDRESS_ZERO,
// }
//
// const swapExact0For1: SwapFunction = (amount, to, sqrtPriceLimitX96) => {
// return swapTest.swap(
// poolKey,
// {
// zeroForOne: true,
// amountSpecified: amount,
// sqrtPriceLimitX96: sqrtPriceLimitX96 ?? MIN_SQRT_PRICE.add(1),
// },
// {
// withdrawTokens: true,
// settleUsingTransfer: true,
// },
// '0x00'
// )
// }
// const swapToHigherPrice: SwapToPriceFunction = (sqrtPriceX96, to) => {
// return swapTest.swap(
// poolKey,
// {
// zeroForOne: false,
// amountSpecified: MaxUint128,
// sqrtPriceLimitX96: sqrtPriceX96,
// },
// {
// withdrawTokens: true,
// settleUsingTransfer: true,
// },
// '0x00'
// )
// }
// const swapToLowerPrice: SwapToPriceFunction = (sqrtPriceX96, to) => {
// return swapTest.swap(
// poolKey,
// {
// zeroForOne: true,
// amountSpecified: MaxUint128,
// sqrtPriceLimitX96: sqrtPriceX96,
// },
// {
// withdrawTokens: true,
// settleUsingTransfer: true,
// },
// '0x00'
// )
// }
// const modifyPosition: ModifyPositionFunction = (tickLower, tickUpper, liquidityDelta) => {
// return modifyPositionTest.modifyPosition(
// poolKey,
// {
// tickLower,
// tickUpper,
// liquidityDelta,
// },
// '0x00'
// )
// }
// const donate: DonateFunction = (amount0, amount1) => {
// return donateTest.donate(poolKey, amount0, amount1, '0x00')
// }
// const getSlot0 = async () => {
// return await manager.getSlot0(getPoolId(poolKey))
// }
//
// await manager.initialize(poolKey, encodeSqrtPriceX96(1, 1), '0x00')
//
// await modifyPosition(minTick, maxTick, expandTo18Decimals(2))
//
// await swapExact0For1(expandTo18Decimals(1), wallet.address)
// await swapToHigherPrice(startingPrice, wallet.address)
//
// const { tick, sqrtPriceX96 } = await getSlot0()
//
// expect(tick).to.eq(startingTick)
// expect(sqrtPriceX96).to.eq(startingPrice)
//
// return { manager, getSlot0, poolKey, swapExact0For1, modifyPosition, donate, swapToHigherPrice, swapToLowerPrice }
// }
//
// let swapExact0For1: SwapFunction
// let swapToHigherPrice: SwapToPriceFunction
// let swapToLowerPrice: SwapToPriceFunction
// let manager: PoolManager
// let modifyPosition: ModifyPositionFunction
// let donate: DonateFunction
// let getSlot0: AsyncReturnType<typeof gasTestFixture>['getSlot0']
// let poolKey: AsyncReturnType<typeof gasTestFixture>['poolKey']
//
// beforeEach('load the fixture', async () => {
// ;({ swapExact0For1, manager, modifyPosition, donate, swapToHigherPrice, swapToLowerPrice, getSlot0, poolKey } =
// await loadFixture(gasTestFixture))
// })
//
// describe('#initialize', () => {
// it('initialize pool with no hooks and no protocol fee', async () => {
// let currency0 = wallet.address
// let currency1 = other.address
//
// ;[currency0, currency1] = currency0 < currency1 ? [currency0, currency1] : [currency1, currency0]
//
// const altPoolKey = {
// currency0,
// currency1,
// fee: FeeAmount.MEDIUM,
// tickSpacing: 60,
// hooks: '0x0000000000000000000000000000000000000000',
// }
// await snapshotGasCost(manager.initialize(altPoolKey, encodeSqrtPriceX96(1, 1), '0x00'))
// })
// })
//
// describe('#swapExact0For1', () => {
// it('first swap in block with no tick movement', async () => {
// await snapshotGasCost(swapExact0For1(2000, wallet.address))
// expect((await getSlot0()).sqrtPriceX96).to.not.eq(startingPrice)
// expect((await getSlot0()).tick).to.eq(startingTick)
// })
//
// it('first swap in block moves tick, no initialized crossings', async () => {
// await snapshotGasCost(swapExact0For1(expandTo18Decimals(1).div(10000), wallet.address))
// expect((await getSlot0()).tick).to.eq(startingTick - 1)
// })
//
// it('second swap in block with no tick movement', async () => {
// await swapExact0For1(expandTo18Decimals(1).div(10000), wallet.address)
// expect((await getSlot0()).tick).to.eq(startingTick - 1)
// await snapshotGasCost(swapExact0For1(2000, wallet.address))
// expect((await getSlot0()).tick).to.eq(startingTick - 1)
// })
//
// it('second swap in block moves tick, no initialized crossings', async () => {
// await swapExact0For1(1000, wallet.address)
// expect((await getSlot0()).tick).to.eq(startingTick)
// await snapshotGasCost(swapExact0For1(expandTo18Decimals(1).div(10000), wallet.address))
// expect((await getSlot0()).tick).to.eq(startingTick - 1)
// })
//
// it('first swap in block, large swap, no initialized crossings', async () => {
// await snapshotGasCost(swapExact0For1(expandTo18Decimals(10), wallet.address))
// expect((await getSlot0()).tick).to.eq(-35787)
// })
//
// it('first swap in block, large swap crossing several initialized ticks', async () => {
// await modifyPosition(startingTick - 3 * tickSpacing, startingTick - tickSpacing, expandTo18Decimals(1))
// await modifyPosition(startingTick - 4 * tickSpacing, startingTick - 2 * tickSpacing, expandTo18Decimals(1))
// expect((await getSlot0()).tick).to.eq(startingTick)
// await snapshotGasCost(swapExact0For1(expandTo18Decimals(1), wallet.address))
// expect((await getSlot0()).tick).to.be.lt(startingTick - 4 * tickSpacing) // we crossed the last tick
// })
//
// it('first swap in block, large swap crossing a single initialized tick', async () => {
// await modifyPosition(minTick, startingTick - 2 * tickSpacing, expandTo18Decimals(1))
// await snapshotGasCost(swapExact0For1(expandTo18Decimals(1), wallet.address))
// expect((await getSlot0()).tick).to.be.lt(startingTick - 2 * tickSpacing) // we crossed the last tick
// })
//
// it('second swap in block, large swap crossing several initialized ticks', async () => {
// await modifyPosition(startingTick - 3 * tickSpacing, startingTick - tickSpacing, expandTo18Decimals(1))
// await modifyPosition(startingTick - 4 * tickSpacing, startingTick - 2 * tickSpacing, expandTo18Decimals(1))
// await swapExact0For1(expandTo18Decimals(1).div(10000), wallet.address)
// await snapshotGasCost(swapExact0For1(expandTo18Decimals(1), wallet.address))
// expect((await getSlot0()).tick).to.be.lt(startingTick - 4 * tickSpacing)
// })
//
// it('second swap in block, large swap crossing a single initialized tick', async () => {
// await modifyPosition(minTick, startingTick - 2 * tickSpacing, expandTo18Decimals(1))
// await swapExact0For1(expandTo18Decimals(1).div(10000), wallet.address)
// expect((await getSlot0()).tick).to.be.gt(startingTick - 2 * tickSpacing) // we didn't cross the initialized tick
// await snapshotGasCost(swapExact0For1(expandTo18Decimals(1), wallet.address))
// expect((await getSlot0()).tick).to.be.lt(startingTick - 2 * tickSpacing) // we crossed the last tick
// })
// })
//
// describe('#mint', () => {
// for (const { description, tickLower, tickUpper } of [
// {
// description: 'around current price',
// tickLower: startingTick - tickSpacing,
// tickUpper: startingTick + tickSpacing,
// },
// {
// description: 'below current price',
// tickLower: startingTick - 2 * tickSpacing,
// tickUpper: startingTick - tickSpacing,
// },
// {
// description: 'above current price',
// tickLower: startingTick + tickSpacing,
// tickUpper: startingTick + 2 * tickSpacing,
// },
// ]) {
// describe(description, () => {
// it('new position mint first in range', async () => {
// await snapshotGasCost(modifyPosition(tickLower, tickUpper, expandTo18Decimals(1)))
// })
// it('add to position existing', async () => {
// await modifyPosition(tickLower, tickUpper, expandTo18Decimals(1))
// await snapshotGasCost(modifyPosition(tickLower, tickUpper, expandTo18Decimals(1)))
// })
// it('second position in same range', async () => {
// await modifyPosition(tickLower, tickUpper, expandTo18Decimals(1))
// await snapshotGasCost(modifyPosition(tickLower, tickUpper, expandTo18Decimals(1)))
// })
// })
// }
// })
//
// // describe('#burn', () => {
// // for (const { description, tickLower, tickUpper } of [
// // {
// // description: 'around current price',
// // tickLower: startingTick - tickSpacing,
// // tickUpper: startingTick + tickSpacing,
// // },
// // {
// // description: 'below current price',
// // tickLower: startingTick - 2 * tickSpacing,
// // tickUpper: startingTick - tickSpacing,
// // },
// // {
// // description: 'above current price',
// // tickLower: startingTick + tickSpacing,
// // tickUpper: startingTick + 2 * tickSpacing,
// // },
// // ]) {
// // describe(description, () => {
// // const liquidityAmount = expandTo18Decimals(1)
// // beforeEach('mint a position', async () => {
// // await modifyPosition( tickLower, tickUpper, liquidityAmount)
// // })
// //
// // it('burn when only position using ticks', async () => {
// // await snapshotGasCost(pool.burn(tickLower, tickUpper, expandTo18Decimals(1)))
// // })
// // it('partial position burn', async () => {
// // await snapshotGasCost(pool.burn(tickLower, tickUpper, expandTo18Decimals(1).div(2)))
// // })
// // it('entire position burn but other positions are using the ticks', async () => {
// // await mint(other.address, tickLower, tickUpper, expandTo18Decimals(1))
// // await snapshotGasCost(pool.burn(tickLower, tickUpper, expandTo18Decimals(1)))
// // })
// // it('burn entire position after some time passes', async () => {
// // await manager.advanceTime(1)
// // await snapshotGasCost(pool.burn(tickLower, tickUpper, expandTo18Decimals(1)))
// // })
// // })
// // }
// // })
//
// // describe('#poke', () => {
// // const tickLower = startingTick - tickSpacing
// // const tickUpper = startingTick + tickSpacing
// //
// // it('best case', async () => {
// // await modifyPosition( tickLower, tickUpper, expandTo18Decimals(1))
// // await swapExact0For1(expandTo18Decimals(1).div(100), wallet.address)
// // await pool.burn(tickLower, tickUpper, 0)
// // await swapExact0For1(expandTo18Decimals(1).div(100), wallet.address)
// // await snapshotGasCost(pool.burn(tickLower, tickUpper, 0))
// // })
// // })
// })
//
// describe('Native Tokens', () => {
// const gasTestFixture = async ([wallet]: Wallet[]) => {
// const { currency1 } = await tokensFixture()
//
// const singletonPoolFactory = await ethers.getContractFactory('PoolManager')
// const swapTestFactory = await ethers.getContractFactory('PoolSwapTest')
// const donateTestFactory = await ethers.getContractFactory('PoolDonateTest')
// const mintTestFactory = await ethers.getContractFactory('PoolModifyPositionTest')
// const CONTROLLER_GAS_LIMIT = 50000
// const manager = (await singletonPoolFactory.deploy(CONTROLLER_GAS_LIMIT)) as PoolManager
//
// const swapTest = (await swapTestFactory.deploy(manager.address)) as PoolSwapTest
// const donateTest = (await donateTestFactory.deploy(manager.address)) as PoolDonateTest
// const modifyPositionTest = (await mintTestFactory.deploy(manager.address)) as PoolModifyPositionTest
//
// for (const spender of [swapTest, donateTest, modifyPositionTest]) {
// await currency1.connect(wallet).approve(spender.address, constants.MaxUint256)
// }
//
// const poolKey = {
// currency0: ADDRESS_ZERO,
// currency1: currency1.address,
// fee: FeeAmount.MEDIUM,
// tickSpacing: 60,
// hooks: ADDRESS_ZERO,
// }
//
// const swapExact0For1: SwapFunction = (amount, to, sqrtPriceLimitX96) => {
// return swapTest.swap(
// poolKey,
// {
// zeroForOne: true,
// amountSpecified: amount,
// sqrtPriceLimitX96: sqrtPriceLimitX96 ?? MIN_SQRT_PRICE.add(1),
// },
// {
// withdrawTokens: true,
// settleUsingTransfer: true,
// },
// '0x00',
// {
// value: amount,
// }
// )
// }
// const swapToHigherPrice: SwapToPriceFunction = (sqrtPriceX96, to) => {
// return swapTest.swap(
// poolKey,
// {
// zeroForOne: false,
// amountSpecified: MaxUint128,
// sqrtPriceLimitX96: sqrtPriceX96,
// },
// {
// withdrawTokens: true,
// settleUsingTransfer: true,
// },
// '0x00'
// )
// }
// const swapToLowerPrice: SwapToPriceFunction = (sqrtPriceX96, to) => {
// return swapTest.swap(
// poolKey,
// {
// zeroForOne: true,
// amountSpecified: MaxUint128,
// sqrtPriceLimitX96: sqrtPriceX96,
// },
// {
// withdrawTokens: true,
// settleUsingTransfer: true,
// },
// '0x00',
// {
// value: MaxUint128,
// }
// )
// }
// const modifyPosition: ModifyPositionFunction = (tickLower, tickUpper, liquidityDelta) => {
// return modifyPositionTest.modifyPosition(
// poolKey,
// {
// tickLower,
// tickUpper,
// liquidityDelta,
// },
// '0x00',
// { value: liquidityDelta }
// )
// }
// const donate: DonateFunction = (amount0, amount1) => {
// return donateTest.donate(poolKey, amount0, amount1, '0x00', { value: amount0 })
// }
// const getSlot0 = async () => {
// return await manager.getSlot0(getPoolId(poolKey))
// }
//
// await manager.initialize(poolKey, encodeSqrtPriceX96(1, 1), '0x00')
//
// await modifyPosition(minTick, maxTick, expandTo18Decimals(2))
//
// await swapExact0For1(expandTo18Decimals(1), wallet.address)
// await swapToHigherPrice(startingPrice, wallet.address)
//
// const { tick, sqrtPriceX96 } = await getSlot0()
//
// expect(tick).to.eq(startingTick)
// expect(sqrtPriceX96).to.eq(startingPrice)
//
// return { manager, getSlot0, poolKey, swapExact0For1, modifyPosition, donate, swapToHigherPrice, swapToLowerPrice }
// }
//
// let swapExact0For1: SwapFunction
// let swapToHigherPrice: SwapToPriceFunction
// let swapToLowerPrice: SwapToPriceFunction
// let manager: PoolManager
// let modifyPosition: ModifyPositionFunction
// let donate: DonateFunction
// let getSlot0: AsyncReturnType<typeof gasTestFixture>['getSlot0']
// let poolKey: AsyncReturnType<typeof gasTestFixture>['poolKey']
//
// beforeEach('load the fixture', async () => {
// ;({ swapExact0For1, manager, modifyPosition, donate, swapToHigherPrice, swapToLowerPrice, getSlot0, poolKey } =
// await loadFixture(gasTestFixture))
// })
//
// describe('#swapExact0For1', () => {
// it('first swap in block with no tick movement', async () => {
// await snapshotGasCost(swapExact0For1(2000, wallet.address))
// expect((await getSlot0()).sqrtPriceX96).to.not.eq(startingPrice)
// expect((await getSlot0()).tick).to.eq(startingTick)
// })
//
// it('first swap in block moves tick, no initialized crossings', async () => {
// await snapshotGasCost(swapExact0For1(expandTo18Decimals(1).div(10000), wallet.address))
// expect((await getSlot0()).tick).to.eq(startingTick - 1)
// })
//
// it('second swap in block with no tick movement', async () => {
// await swapExact0For1(expandTo18Decimals(1).div(10000), wallet.address)
// expect((await getSlot0()).tick).to.eq(startingTick - 1)
// await snapshotGasCost(swapExact0For1(2000, wallet.address))
// expect((await getSlot0()).tick).to.eq(startingTick - 1)
// })
//
// it('second swap in block moves tick, no initialized crossings', async () => {
// await swapExact0For1(1000, wallet.address)
// expect((await getSlot0()).tick).to.eq(startingTick)
// await snapshotGasCost(swapExact0For1(expandTo18Decimals(1).div(10000), wallet.address))
// expect((await getSlot0()).tick).to.eq(startingTick - 1)
// })
//
// it('first swap in block, large swap, no initialized crossings', async () => {
// await snapshotGasCost(swapExact0For1(expandTo18Decimals(10), wallet.address))
// expect((await getSlot0()).tick).to.eq(-35787)
// })
//
// it('first swap in block, large swap crossing several initialized ticks', async () => {
// await modifyPosition(startingTick - 3 * tickSpacing, startingTick - tickSpacing, expandTo18Decimals(1))
// await modifyPosition(startingTick - 4 * tickSpacing, startingTick - 2 * tickSpacing, expandTo18Decimals(1))
// expect((await getSlot0()).tick).to.eq(startingTick)
// await snapshotGasCost(swapExact0For1(expandTo18Decimals(1), wallet.address))
// expect((await getSlot0()).tick).to.be.lt(startingTick - 4 * tickSpacing) // we crossed the last tick
// })
//
// it('first swap in block, large swap crossing a single initialized tick', async () => {
// await modifyPosition(minTick, startingTick - 2 * tickSpacing, expandTo18Decimals(1))
// await snapshotGasCost(swapExact0For1(expandTo18Decimals(1), wallet.address))
// expect((await getSlot0()).tick).to.be.lt(startingTick - 2 * tickSpacing) // we crossed the last tick
// })
//
// it('second swap in block, large swap crossing several initialized ticks', async () => {
// await modifyPosition(startingTick - 3 * tickSpacing, startingTick - tickSpacing, expandTo18Decimals(1))
// await modifyPosition(startingTick - 4 * tickSpacing, startingTick - 2 * tickSpacing, expandTo18Decimals(1))
// await swapExact0For1(expandTo18Decimals(1).div(10000), wallet.address)
// await snapshotGasCost(swapExact0For1(expandTo18Decimals(1), wallet.address))
// expect((await getSlot0()).tick).to.be.lt(startingTick - 4 * tickSpacing)
// })
//
// it('second swap in block, large swap crossing a single initialized tick', async () => {
// await modifyPosition(minTick, startingTick - 2 * tickSpacing, expandTo18Decimals(1))
// await swapExact0For1(expandTo18Decimals(1).div(10000), wallet.address)
// expect((await getSlot0()).tick).to.be.gt(startingTick - 2 * tickSpacing) // we didn't cross the initialized tick
// await snapshotGasCost(swapExact0For1(expandTo18Decimals(1), wallet.address))
// expect((await getSlot0()).tick).to.be.lt(startingTick - 2 * tickSpacing) // we crossed the last tick
// })
// })
//
// describe('#mint', () => {
// for (const { description, tickLower, tickUpper } of [
// {
// description: 'around current price',
// tickLower: startingTick - tickSpacing,
// tickUpper: startingTick + tickSpacing,
// },
// {
// description: 'below current price',
// tickLower: startingTick - 2 * tickSpacing,
// tickUpper: startingTick - tickSpacing,
// },
// {
// description: 'above current price',
// tickLower: startingTick + tickSpacing,
// tickUpper: startingTick + 2 * tickSpacing,
// },
// ]) {
// describe(description, () => {
// it('new position mint first in range', async () => {
// await snapshotGasCost(modifyPosition(tickLower, tickUpper, expandTo18Decimals(1)))
// })
// it('add to position existing', async () => {
// await modifyPosition(tickLower, tickUpper, expandTo18Decimals(1))
// await snapshotGasCost(modifyPosition(tickLower, tickUpper, expandTo18Decimals(1)))
// })
// it('second position in same range', async () => {
// await modifyPosition(tickLower, tickUpper, expandTo18Decimals(1))
// await snapshotGasCost(modifyPosition(tickLower, tickUpper, expandTo18Decimals(1)))
// })
// })
// }
// })
// })
// })