import BN from "bn.js"; import { AmmV3PoolInfo, AmmV3PoolPersonalPosition, AmmV3PoolRewardInfo } from "../ammV3"; import { Q64 } from "./constants"; import { MathUtil } from "./math"; import { Tick } from "./tick"; export class PositionUtils { static getfeeGrowthInside( poolState: AmmV3PoolInfo, tickLowerState: Tick, tickUpperState: Tick ) { let feeGrowthBelowX64A = new BN(0); let feeGrowthBelowX64B = new BN(0); if (poolState.tickCurrent >= tickLowerState.tick) { feeGrowthBelowX64A = tickLowerState.feeGrowthOutsideX64A; feeGrowthBelowX64B = tickLowerState.feeGrowthOutsideX64B; } else { feeGrowthBelowX64A = poolState.feeGrowthGlobalX64A.sub( tickLowerState.feeGrowthOutsideX64A ); feeGrowthBelowX64B = poolState.feeGrowthGlobalX64B.sub( tickLowerState.feeGrowthOutsideX64B ); } let feeGrowthAboveX64A = new BN(0); let feeGrowthAboveX64B = new BN(0); if (poolState.tickCurrent < tickUpperState.tick) { feeGrowthAboveX64A = tickUpperState.feeGrowthOutsideX64A; feeGrowthAboveX64B = tickUpperState.feeGrowthOutsideX64B; } else { feeGrowthAboveX64A = poolState.feeGrowthGlobalX64A.sub( tickUpperState.feeGrowthOutsideX64A ); feeGrowthAboveX64B = poolState.feeGrowthGlobalX64B.sub( tickUpperState.feeGrowthOutsideX64B ); } const feeGrowthInsideX64A = MathUtil.wrappingSubU128( MathUtil.wrappingSubU128(poolState.feeGrowthGlobalX64A, feeGrowthBelowX64A), feeGrowthAboveX64A ); const feeGrowthInsideBX64 = MathUtil.wrappingSubU128( MathUtil.wrappingSubU128(poolState.feeGrowthGlobalX64B, feeGrowthBelowX64B), feeGrowthAboveX64B ); return { feeGrowthInsideX64A, feeGrowthInsideBX64 }; } static GetPositionFees( ammPool: AmmV3PoolInfo, positionState: AmmV3PoolPersonalPosition, tickLowerState: Tick, tickUpperState: Tick ) { const { feeGrowthInsideX64A, feeGrowthInsideBX64 } = this.getfeeGrowthInside( ammPool, tickLowerState, tickUpperState ); const feeGrowthdeltaA = MathUtil.mulDivFloor( MathUtil.wrappingSubU128( feeGrowthInsideX64A, positionState.feeGrowthInsideLastX64A ), positionState.liquidity, Q64 ); const tokenFeeAmountA = positionState.tokenFeesOwedA.add(feeGrowthdeltaA); const feeGrowthdelta1 = MathUtil.mulDivFloor( MathUtil.wrappingSubU128( feeGrowthInsideBX64, positionState.feeGrowthInsideLastX64B ), positionState.liquidity, Q64 ); const tokenFeeAmountB = positionState.tokenFeesOwedB.add(feeGrowthdelta1); return { tokenFeeAmountA, tokenFeeAmountB }; } static GetPositionRewards( ammPool: AmmV3PoolInfo, positionState: AmmV3PoolPersonalPosition, tickLowerState: Tick, tickUpperState: Tick ): BN[] { const rewards: BN[] = []; const rewardGrowthsInside = this.getRewardGrowthInside( ammPool.tickCurrent, tickLowerState, tickUpperState, ammPool.rewardInfos ); for (let i = 0; i < rewardGrowthsInside.length; i++) { const rewardGrowthInside = rewardGrowthsInside[i]; const currRewardInfo = positionState.rewardInfos[i]; const rewardGrowthDelta = MathUtil.wrappingSubU128( rewardGrowthInside, currRewardInfo.growthInsideLastX64 ); const amountOwedDelta = MathUtil.mulDivFloor( rewardGrowthDelta, positionState.liquidity, Q64 ); const rewardAmountOwed = currRewardInfo.rewardAmountOwed.add(amountOwedDelta); rewards.push(rewardAmountOwed); } return rewards; } static getRewardGrowthInside( tickCurrentIndex: number, tickLowerState: Tick, tickUpperState: Tick, rewardInfos: AmmV3PoolRewardInfo[] ): BN[] { const rewardGrowthsInside: BN[] = []; for (let i = 0; i < rewardInfos.length; i++) { let rewardGrowthsBelow = new BN(0); if (tickLowerState.liquidityGross.eqn(0)) { rewardGrowthsBelow = rewardInfos[i].rewardGrowthGlobalX64; } else if (tickCurrentIndex < tickLowerState.tick) { rewardGrowthsBelow = rewardInfos[i].rewardGrowthGlobalX64.sub( tickLowerState.rewardGrowthsOutsideX64[i] ); } else { rewardGrowthsBelow = tickLowerState.rewardGrowthsOutsideX64[i]; } let rewardGrowthsAbove = new BN(0); if (tickUpperState.liquidityGross.eqn(0)) { // } else if (tickCurrentIndex < tickUpperState.tick) { rewardGrowthsAbove = tickUpperState.rewardGrowthsOutsideX64[i]; } else { rewardGrowthsAbove = rewardInfos[i].rewardGrowthGlobalX64.sub( tickUpperState.rewardGrowthsOutsideX64[i] ); } rewardGrowthsInside.push( MathUtil.wrappingSubU128( MathUtil.wrappingSubU128( rewardInfos[i].rewardGrowthGlobalX64, rewardGrowthsBelow ), rewardGrowthsAbove ) ); } return rewardGrowthsInside; } }