import { expect } from 'chai'
import { ethers, waffle } from 'hardhat'
import { BigNumber, BigNumberish, constants, ContractFactory, Contract } from 'ethers'
import { OracleTest, TestERC20 } from '../typechain'
import { expandTo18Decimals } from './shared/expandTo18Decimals'
import snapshotGasCost from './shared/snapshotGasCost'

describe('OracleLibrary', () => {
  let loadFixture: ReturnType<typeof waffle.createFixtureLoader>
  let tokens: TestERC20[]
  let oracle: OracleTest

  const BN0 = BigNumber.from(0)

  const oracleTestFixture = async () => {
    const tokenFactory = await ethers.getContractFactory('TestERC20')
    const tokens: [TestERC20, TestERC20, TestERC20] = [
      (await tokenFactory.deploy(constants.MaxUint256.div(2))) as TestERC20, // do not use maxu256 to avoid overflowing
      (await tokenFactory.deploy(constants.MaxUint256.div(2))) as TestERC20,
      (await tokenFactory.deploy(constants.MaxUint256.div(2))) as TestERC20,
    ]

    tokens.sort((a, b) => (a.address.toLowerCase() < b.address.toLowerCase() ? -1 : 1))

    const oracleFactory = await ethers.getContractFactory('OracleTest')
    const oracle = await oracleFactory.deploy()

    return {
      tokens: tokens as TestERC20[],
      oracle: oracle as OracleTest,
    }
  }

  before('create fixture loader', async () => {
    loadFixture = waffle.createFixtureLoader(await (ethers as any).getSigners())
  })

  beforeEach('deploy fixture', async () => {
    const fixtures = await loadFixture(oracleTestFixture)
    tokens = fixtures['tokens']
    oracle = fixtures['oracle']
  })

  describe('#consult', () => {
    let mockObservableFactory: ContractFactory

    before('create mockObservableFactory', async () => {
      mockObservableFactory = await ethers.getContractFactory('MockObservable')
    })

    it('reverts when period is 0', async () => {
      await expect(oracle.consult(oracle.address, 0)).to.be.revertedWith('BP')
    })

    it('correct output when tick is 0', async () => {
      const period = 3
      const secondsPerLiqCumulatives: [BigNumberish, BigNumberish] = [10, 20]
      const mockObservable = await observableWith({
        period,
        tickCumulatives: [12, 12],
        secondsPerLiqCumulatives,
      })
      const { arithmeticMeanTick, harmonicMeanLiquidity } = await oracle.consult(mockObservable.address, period)

      expect(arithmeticMeanTick).to.equal(0)
      expect(harmonicMeanLiquidity).to.equal(calculateHarmonicAvgLiq(period, secondsPerLiqCumulatives))
    })

    it('correct rounding for .5 negative tick', async () => {
      const period = 4

      const secondsPerLiqCumulatives: [BigNumberish, BigNumberish] = [10, 15]
      const mockObservable = await observableWith({
        period,
        tickCumulatives: [-10, -12],
        secondsPerLiqCumulatives,
      })

      const { arithmeticMeanTick, harmonicMeanLiquidity } = await oracle.consult(mockObservable.address, period)

      // Always round to negative infinity
      // In this case, we need to subtract one because integer division rounds to 0
      expect(arithmeticMeanTick).to.equal(-1)
      expect(harmonicMeanLiquidity).to.equal(calculateHarmonicAvgLiq(period, secondsPerLiqCumulatives))
    })

    it('correct output for liquidity overflow', async () => {
      const period = 1

      const secondsPerLiqCumulatives: [BigNumberish, BigNumberish] = [10, 11]
      const mockObservable = await observableWith({
        period,
        tickCumulatives: [12, 12],
        secondsPerLiqCumulatives,
      })

      const { arithmeticMeanTick, harmonicMeanLiquidity } = await oracle.consult(mockObservable.address, period)

      expect(arithmeticMeanTick).to.equal(0)
      // Make sure liquidity doesn't overflow uint128
      expect(harmonicMeanLiquidity).to.equal(BigNumber.from(2).pow(128).sub(1))
    })

    function calculateHarmonicAvgLiq(period: number, secondsPerLiqCumulatives: [BigNumberish, BigNumberish]) {
      const [secondsPerLiq0, secondsPerLiq1] = secondsPerLiqCumulatives.map(BigNumber.from)
      const delta = secondsPerLiq1.sub(secondsPerLiq0)

      const maxUint160 = BigNumber.from(2).pow(160).sub(1)
      return maxUint160.mul(period).div(delta.shl(32))
    }

    function observableWith({
      period,
      tickCumulatives,
      secondsPerLiqCumulatives,
    }: {
      period: number
      tickCumulatives: [BigNumberish, BigNumberish]
      secondsPerLiqCumulatives: [BigNumberish, BigNumberish]
    }) {
      return mockObservableFactory.deploy(
        [period, 0],
        tickCumulatives.map(BigNumber.from),
        secondsPerLiqCumulatives.map(BigNumber.from)
      )
    }
  })

  describe('#getQuoteAtTick', () => {
    // sanity check
    it('token0: returns correct value when tick = 0', async () => {
      const quoteAmount = await oracle.getQuoteAtTick(BN0, expandTo18Decimals(1), tokens[0].address, tokens[1].address)

      expect(quoteAmount).to.equal(expandTo18Decimals(1))
    })

    // sanity check
    it('token1: returns correct value when tick = 0', async () => {
      const quoteAmount = await oracle.getQuoteAtTick(BN0, expandTo18Decimals(1), tokens[1].address, tokens[0].address)

      expect(quoteAmount).to.equal(expandTo18Decimals(1))
    })

    it('token0: returns correct value when at min tick | 0 < sqrtRatioX96 <= type(uint128).max', async () => {
      const quoteAmount = await oracle.getQuoteAtTick(
        BigNumber.from(-887272),
        BigNumber.from(2).pow(128).sub(1),
        tokens[0].address,
        tokens[1].address
      )
      expect(quoteAmount).to.equal(BigNumber.from('1'))
    })

    it('token1: returns correct value when at min tick | 0 < sqrtRatioX96 <= type(uint128).max', async () => {
      const quoteAmount = await oracle.getQuoteAtTick(
        BigNumber.from(-887272),
        BigNumber.from(2).pow(128).sub(1),
        tokens[1].address,
        tokens[0].address
      )
      expect(quoteAmount).to.equal(
        BigNumber.from('115783384738768196242144082653949453838306988932806144552194799290216044976282')
      )
    })

    it('token0: returns correct value when at max tick | sqrtRatioX96 > type(uint128).max', async () => {
      const quoteAmount = await oracle.getQuoteAtTick(
        BigNumber.from(887272),
        BigNumber.from(2).pow(128).sub(1),
        tokens[0].address,
        tokens[1].address
      )
      expect(quoteAmount).to.equal(
        BigNumber.from('115783384785599357996676985412062652720342362943929506828539444553934033845703')
      )
    })

    it('token1: returns correct value when at max tick | sqrtRatioX96 > type(uint128).max', async () => {
      const quoteAmount = await oracle.getQuoteAtTick(
        BigNumber.from(887272),
        BigNumber.from(2).pow(128).sub(1),
        tokens[1].address,
        tokens[0].address
      )
      expect(quoteAmount).to.equal(BigNumber.from('1'))
    })

    it('gas test', async () => {
      await snapshotGasCost(
        oracle.getGasCostOfGetQuoteAtTick(
          BigNumber.from(10),
          expandTo18Decimals(1),
          tokens[0].address,
          tokens[1].address
        )
      )
    })
  })

  describe('#getOldestObservationSecondsAgo', () => {
    let mockObservationsFactory: ContractFactory

    // some empty tick values as this function does not use them
    const emptySPL = [0, 0, 0, 0]
    const emptyTickCumulatives = [0, 0, 0, 0]
    const emptyTick = 0
    const emptyLiquidity = 0

    // helper function to run each test case identically
    const runOldestObservationsTest = async (
      blockTimestamps: number[],
      initializeds: boolean[],
      observationCardinality: number,
      observationIndex: number
    ) => {
      const mockObservations = await mockObservationsFactory.deploy(
        blockTimestamps,
        emptyTickCumulatives,
        emptySPL,
        initializeds,
        emptyTick,
        observationCardinality,
        observationIndex,
        false,
        emptyLiquidity
      )

      var result = await oracle.getOldestObservationSecondsAgo(mockObservations.address)

      //calculate seconds ago
      var secondsAgo
      if (initializeds[(observationIndex + 1) % observationCardinality]) {
        secondsAgo = result['currentTimestamp'] - blockTimestamps[(observationIndex + 1) % observationCardinality]
      } else {
        secondsAgo = result['currentTimestamp'] - blockTimestamps[0]
      }

      if (secondsAgo < 0) {
        secondsAgo += 2 ** 32
      }

      expect(result['secondsAgo']).to.equal(secondsAgo)
    }

    before('create mockObservationsFactory', async () => {
      mockObservationsFactory = await ethers.getContractFactory('MockObservations')
    })

    it('fetches the oldest timestamp from the slot after observationIndex', async () => {
      // set up test case
      const blockTimestamps = [2, 3, 1, 0]
      const initializeds = [true, true, true, false]
      const observationCardinality = 3
      const observationIndex = 1

      // run test
      await runOldestObservationsTest(blockTimestamps, initializeds, observationCardinality, observationIndex)
    })

    it('loops to fetches the oldest timestamp from index 0', async () => {
      // set up test case
      const blockTimestamps = [1, 2, 3, 0]
      const initializeds = [true, true, true, false]
      const observationCardinality = 3
      const observationIndex = 2

      // run test
      await runOldestObservationsTest(blockTimestamps, initializeds, observationCardinality, observationIndex)
    })

    it('fetches from index 0 if the next index is uninitialized', async () => {
      // set up test case
      const blockTimestamps = [1, 2, 0, 0]
      const initializeds = [true, true, false, false]
      const observationCardinality = 4
      const observationIndex = 1

      // run test
      await runOldestObservationsTest(blockTimestamps, initializeds, observationCardinality, observationIndex)
    })

    it('reverts if the pool is not initialized', async () => {
      const blockTimestamps = [0, 0, 0, 0]
      const initializeds = [false, false, false, false]
      const observationCardinality = 0
      const observationIndex = 0
      const mockObservations = await mockObservationsFactory.deploy(
        blockTimestamps,
        emptyTickCumulatives,
        emptySPL,
        initializeds,
        emptyTick,
        observationCardinality,
        observationIndex,
        false,
        emptyLiquidity
      )

      await expect(oracle.getOldestObservationSecondsAgo(mockObservations.address)).to.be.revertedWith('NI')
    })

    it('fetches the correct timestamp when the timestamps overflow', async () => {
      // set up test case
      const maxUint32 = 2 ** 32 - 1
      const blockTimestamps = [maxUint32, 3, maxUint32 - 2, 0]
      const initializeds = [true, true, true, false]
      const observationCardinality = 3
      const observationIndex = 1

      // run test
      await runOldestObservationsTest(blockTimestamps, initializeds, observationCardinality, observationIndex)
    })
  })

  describe('#getBlockStartingTickAndLiquidity', () => {
    let mockObservationsFactory: ContractFactory
    let mockObservations: Contract
    let blockTimestamps: number[]
    let tickCumulatives: number[]
    let liquidityValues: BigNumber[]
    let initializeds: boolean[]
    let slot0Tick: number
    let observationCardinality: number
    let observationIndex: number
    let lastObservationCurrentTimestamp: boolean
    let liquidity: number

    before('create mockObservationsFactory', async () => {
      mockObservationsFactory = await ethers.getContractFactory('MockObservations')
    })

    const deployMockObservationsContract = async () => {
      mockObservations = await mockObservationsFactory.deploy(
        blockTimestamps,
        tickCumulatives,
        liquidityValues,
        initializeds,
        slot0Tick,
        observationCardinality,
        observationIndex,
        lastObservationCurrentTimestamp,
        liquidity
      )
    }

    it('reverts if the pool is not initialized', async () => {
      blockTimestamps = [0, 0, 0, 0]
      tickCumulatives = [0, 0, 0, 0]
      liquidityValues = [BN0, BN0, BN0, BN0]
      initializeds = [false, false, false, false]
      slot0Tick = 0
      observationCardinality = 0
      observationIndex = 0
      lastObservationCurrentTimestamp = false
      liquidity = 0

      await deployMockObservationsContract()

      await expect(oracle.getBlockStartingTickAndLiquidity(mockObservations.address)).to.be.revertedWith('NEO')
    })

    it('returns the tick and liquidity in storage if the latest observation was in a previous block', async () => {
      blockTimestamps = [1, 3, 4, 0]
      // 0
      // 8: 0 + (4*(3-1))
      // 13: 8 + (5*(4-3))
      tickCumulatives = [0, 8, 13, 0]
      // 0
      // (1): 0 + ((3-1)*2**128)/5000
      // (1) + ((4-3)*2**128)/7000
      liquidityValues = [
        BN0,
        BigNumber.from('136112946768375385385349842972707284'),
        BigNumber.from('184724713471366594451546215462959885'),
        BN0,
      ]
      initializeds = [true, true, true, false]
      observationCardinality = 3
      observationIndex = 2
      slot0Tick = 6
      lastObservationCurrentTimestamp = false
      liquidity = 10000

      await deployMockObservationsContract()

      var result = await oracle.getBlockStartingTickAndLiquidity(mockObservations.address)
      expect(result[0]).to.equal(slot0Tick)
      expect(result[1]).to.equal(liquidity)
    })

    it('reverts if it needs 2 observations and doesnt have them', async () => {
      blockTimestamps = [1, 0, 0, 0]
      tickCumulatives = [8, 0, 0, 0]
      liquidityValues = [BigNumber.from('136112946768375385385349842972707284'), BN0, BN0, BN0]
      initializeds = [true, false, false, false]
      observationCardinality = 1
      observationIndex = 0
      slot0Tick = 4
      lastObservationCurrentTimestamp = true
      liquidity = 10000

      await deployMockObservationsContract()

      await expect(oracle.getBlockStartingTickAndLiquidity(mockObservations.address)).to.be.revertedWith('NEO')
    })

    it('reverts if the prior observation needed is not initialized', async () => {
      blockTimestamps = [1, 0, 0, 0]
      observationCardinality = 2
      observationIndex = 0
      liquidityValues = [BigNumber.from('136112946768375385385349842972707284'), BN0, BN0, BN0]
      initializeds = [true, false, false, false]
      tickCumulatives = [8, 0, 0, 0]
      slot0Tick = 4
      lastObservationCurrentTimestamp = true

      await deployMockObservationsContract()

      await expect(oracle.getBlockStartingTickAndLiquidity(mockObservations.address)).to.be.revertedWith('ONI')
    })

    it('calculates the prior tick and liquidity from the prior observations', async () => {
      blockTimestamps = [9, 5, 8, 0]
      observationCardinality = 3
      observationIndex = 0
      initializeds = [true, true, true, false]
      // 99: 95 + (4*1)
      // 80: 72 + (4*2)
      // 95: 80 + (5*3)
      tickCumulatives = [99, 80, 95, 0]
      // prev: 784724713471366594451546215462959885
      // (3): (2) + (1*2**128)/13212
      // (1): prev + (2*2**128)/12345
      // (2): (1) + (3*2**128)/10238
      liquidityValues = [
        BigNumber.from('965320616647837491242414421221086683'),
        BigNumber.from('839853488995212437053956034406948254'),
        BigNumber.from('939565063595995342933046073701273770'),
        BN0,
      ]
      slot0Tick = 3
      lastObservationCurrentTimestamp = true

      await deployMockObservationsContract()

      var result = await oracle.getBlockStartingTickAndLiquidity(mockObservations.address)

      var actualStartingTick = (tickCumulatives[0] - tickCumulatives[2]) / (blockTimestamps[0] - blockTimestamps[2])
      expect(result[0]).to.equal(actualStartingTick)

      var actualStartingLiquidity = 13212 // see comments above
      expect(result[1]).to.equal(actualStartingLiquidity)
    })
  })

  describe('#getWeightedArithmeticMeanTick', () => {
    it('single observation returns average tick', async () => {
      const observation = { tick: 10, weight: 10 }

      const oracleTick = await oracle.getWeightedArithmeticMeanTick([observation])

      expect(oracleTick).to.equal(10)
    })

    it('multiple observations with same weight result in average across tiers', async () => {
      const observation1 = { tick: 10, weight: 10 }
      const observation2 = { tick: 20, weight: 10 }

      const oracleTick = await oracle.getWeightedArithmeticMeanTick([observation1, observation2])

      expect(oracleTick).to.equal(15)
    })

    it('multiple observations with different weights are weighted correctly', async () => {
      const observation2 = { tick: 20, weight: 15 }
      const observation1 = { tick: 10, weight: 10 }

      const oracleTick = await oracle.getWeightedArithmeticMeanTick([observation1, observation2])

      expect(oracleTick).to.equal(16)
    })

    it('correct rounding for .5 negative tick', async () => {
      const observation1 = { tick: -10, weight: 10 }
      const observation2 = { tick: -11, weight: 10 }

      const oracleTick = await oracle.getWeightedArithmeticMeanTick([observation1, observation2])

      expect(oracleTick).to.equal(-11)
    })
  })
  describe('#getChainedPrice', () => {
    let ticks: number[]

    it('fails with discrepant length', async () => {
      const tokenAddresses = [tokens[0].address, tokens[2].address]
      ticks = [5, 5]

      expect(oracle.getChainedPrice(tokenAddresses, ticks)).to.be.revertedWith('DL')
    })
    it('add two positive ticks, sorted order', async () => {
      const tokenAddresses = [tokens[0].address, tokens[1].address, tokens[2].address]
      ticks = [5, 5]
      const oracleTick = await oracle.getChainedPrice(tokenAddresses, ticks)

      expect(oracleTick).to.equal(10)
    })
    it('add one positive and one negative tick, sorted order', async () => {
      const tokenAddresses = [tokens[0].address, tokens[1].address, tokens[2].address]
      ticks = [5, -5]
      const oracleTick = await oracle.getChainedPrice(tokenAddresses, ticks)

      expect(oracleTick).to.equal(0)
    })
    it('add one negative and one positive tick, sorted order', async () => {
      const tokenAddresses = [tokens[0].address, tokens[1].address, tokens[2].address]
      ticks = [-5, 5]
      const oracleTick = await oracle.getChainedPrice(tokenAddresses, ticks)

      expect(oracleTick).to.equal(0)
    })
    it('add two negative ticks, sorted order', async () => {
      const tokenAddresses = [tokens[0].address, tokens[1].address, tokens[2].address]
      ticks = [-5, -5]
      const oracleTick = await oracle.getChainedPrice(tokenAddresses, ticks)

      expect(oracleTick).to.equal(-10)
    })

    it('add two positive ticks, token0/token1 + token1/token0', async () => {
      const tokenAddresses = [tokens[0].address, tokens[2].address, tokens[1].address]
      ticks = [5, 5]
      const oracleTick = await oracle.getChainedPrice(tokenAddresses, ticks)

      expect(oracleTick).to.equal(0)
    })
    it('add one positive tick and one negative tick, token0/token1 + token1/token0', async () => {
      const tokenAddresses = [tokens[0].address, tokens[2].address, tokens[1].address]
      ticks = [5, -5]
      const oracleTick = await oracle.getChainedPrice(tokenAddresses, ticks)

      expect(oracleTick).to.equal(10)
    })
    it('add one negative tick and one positive tick, token0/token1 + token1/token0', async () => {
      const tokenAddresses = [tokens[0].address, tokens[2].address, tokens[1].address]
      ticks = [-5, 5]
      const oracleTick = await oracle.getChainedPrice(tokenAddresses, ticks)

      expect(oracleTick).to.equal(-10)
    })
    it('add two negative ticks, token0/token1 + token1/token0', async () => {
      const tokenAddresses = [tokens[0].address, tokens[2].address, tokens[1].address]
      ticks = [-5, -5]
      const oracleTick = await oracle.getChainedPrice(tokenAddresses, ticks)

      expect(oracleTick).to.equal(0)
    })

    it('add two positive ticks, token1/token0 + token0/token1', async () => {
      const tokenAddresses = [tokens[1].address, tokens[0].address, tokens[2].address]
      ticks = [5, 5]
      const oracleTick = await oracle.getChainedPrice(tokenAddresses, ticks)

      expect(oracleTick).to.equal(0)
    })
    it('add one positive tick and one negative tick, token1/token0 + token0/token1', async () => {
      const tokenAddresses = [tokens[1].address, tokens[0].address, tokens[2].address]
      ticks = [5, -5]
      const oracleTick = await oracle.getChainedPrice(tokenAddresses, ticks)

      expect(oracleTick).to.equal(-10)
    })
    it('add one negative tick and one positive tick, token1/token0 + token0/token1', async () => {
      const tokenAddresses = [tokens[1].address, tokens[0].address, tokens[2].address]
      ticks = [-5, 5]
      const oracleTick = await oracle.getChainedPrice(tokenAddresses, ticks)

      expect(oracleTick).to.equal(10)
    })
    it('add two negative ticks, token1/token0 + token0/token1', async () => {
      const tokenAddresses = [tokens[1].address, tokens[0].address, tokens[2].address]
      ticks = [-5, -5]
      const oracleTick = await oracle.getChainedPrice(tokenAddresses, ticks)

      expect(oracleTick).to.equal(0)
    })

    it('add two positive ticks, token0/token1 + token1/token0', async () => {
      const tokenAddresses = [tokens[1].address, tokens[2].address, tokens[0].address]
      ticks = [5, 5]
      const oracleTick = await oracle.getChainedPrice(tokenAddresses, ticks)

      expect(oracleTick).to.equal(0)
    })
    it('add one positive tick and one negative tick, token0/token1 + token1/token0', async () => {
      const tokenAddresses = [tokens[1].address, tokens[2].address, tokens[0].address]
      ticks = [5, -5]
      const oracleTick = await oracle.getChainedPrice(tokenAddresses, ticks)

      expect(oracleTick).to.equal(10)
    })
    it('add one negative tick and one positive tick, token0/token1 + token1/token0', async () => {
      const tokenAddresses = [tokens[1].address, tokens[2].address, tokens[0].address]
      ticks = [-5, 5]
      const oracleTick = await oracle.getChainedPrice(tokenAddresses, ticks)

      expect(oracleTick).to.equal(-10)
    })
    it('add two negative ticks, token0/token1 + token1/token0', async () => {
      const tokenAddresses = [tokens[1].address, tokens[2].address, tokens[0].address]
      ticks = [-5, -5]
      const oracleTick = await oracle.getChainedPrice(tokenAddresses, ticks)

      expect(oracleTick).to.equal(0)
    })

    it('add two positive ticks, token1/token0 + token0/token1', async () => {
      const tokenAddresses = [tokens[2].address, tokens[0].address, tokens[1].address]
      ticks = [5, 5]
      const oracleTick = await oracle.getChainedPrice(tokenAddresses, ticks)

      expect(oracleTick).to.equal(0)
    })
    it('add one positive tick and one negative tick, token1/token0 + token0/token1', async () => {
      const tokenAddresses = [tokens[2].address, tokens[0].address, tokens[1].address]
      ticks = [5, -5]
      const oracleTick = await oracle.getChainedPrice(tokenAddresses, ticks)

      expect(oracleTick).to.equal(-10)
    })
    it('add one negative tick and one positive tick, token1/token0 + token0/token1', async () => {
      const tokenAddresses = [tokens[2].address, tokens[0].address, tokens[1].address]
      ticks = [-5, 5]
      const oracleTick = await oracle.getChainedPrice(tokenAddresses, ticks)

      expect(oracleTick).to.equal(10)
    })
    it('add two negative ticks, token1/token0 + token0/token1', async () => {
      const tokenAddresses = [tokens[2].address, tokens[0].address, tokens[1].address]
      ticks = [-5, -5]
      const oracleTick = await oracle.getChainedPrice(tokenAddresses, ticks)

      expect(oracleTick).to.equal(0)
    })

    it('add two positive ticks, token1/token0 + token1/token0', async () => {
      const tokenAddresses = [tokens[2].address, tokens[1].address, tokens[0].address]
      ticks = [5, 5]
      const oracleTick = await oracle.getChainedPrice(tokenAddresses, ticks)

      expect(oracleTick).to.equal(-10)
    })
    it('add one positive tick and one negative tick, token1/token0 + token1/token0', async () => {
      const tokenAddresses = [tokens[2].address, tokens[1].address, tokens[0].address]
      ticks = [5, -5]
      const oracleTick = await oracle.getChainedPrice(tokenAddresses, ticks)

      expect(oracleTick).to.equal(0)
    })
    it('add one negative tick and one positive tick, token1/token0 + token1/token0', async () => {
      const tokenAddresses = [tokens[2].address, tokens[1].address, tokens[0].address]
      ticks = [-5, 5]
      const oracleTick = await oracle.getChainedPrice(tokenAddresses, ticks)

      expect(oracleTick).to.equal(0)
    })
    it('add two negative ticks, token1/token0 + token1/token0', async () => {
      const tokenAddresses = [tokens[2].address, tokens[1].address, tokens[0].address]
      ticks = [-5, -5]
      const oracleTick = await oracle.getChainedPrice(tokenAddresses, ticks)

      expect(oracleTick).to.equal(10)
    })
  })
})
