UNPKG

@orca-so/whirlpools-sdk

Version:

Typescript SDK to interact with Orca's Whirlpool program.

295 lines 14.1 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.AdaptiveFeeVariables = exports.FeeRateManager = void 0; const anchor_1 = require("@coral-xyz/anchor"); const tiny_invariant_1 = __importDefault(require("tiny-invariant")); const public_1 = require("../../types/public"); const public_2 = require("../../utils/public"); class FeeRateManager { static new(aToB, currentTickIndex, timestamp, staticFeeRate, adaptiveFeeInfo) { if (!adaptiveFeeInfo) { return new StaticFeeRateManager(staticFeeRate); } return new AdaptiveFeeRateManager(aToB, currentTickIndex, timestamp, staticFeeRate, adaptiveFeeInfo); } } exports.FeeRateManager = FeeRateManager; class StaticFeeRateManager extends FeeRateManager { staticFeeRate; constructor(staticFeeRate) { super(); this.staticFeeRate = staticFeeRate; } updateVolatilityAccumulator() { } getTotalFeeRate() { return this.staticFeeRate; } getBoundedSqrtPriceTarget(sqrtPrice, _currLiquidity) { return { boundedSqrtPriceTarget: sqrtPrice, adaptiveFeeUpdateSkipped: false, }; } advanceTickGroup() { } advanceTickGroupAfterSkip(_sqrtPrice, _nextTickSqrtPrice, _nextTickIndex) { throw new Error("StaticFeeRateManager does not support advanceTickGroupAfterSkip"); } updateMajorSwapTimestamp(_preSqrtPrice, _postSqrtPrice) { } getNextAdaptiveFeeInfo() { return null; } } class AdaptiveFeeRateManager extends FeeRateManager { aToB; currentTickIndex; timestamp; staticFeeRate; tickGroupIndex; adaptiveFeeConstants; adaptiveFeeVariables; coreTickGroupRangeLowerBound; coreTickGroupRangeUpperBound; constructor(aToB, currentTickIndex, timestamp, staticFeeRate, adaptiveFeeInfo) { super(); this.aToB = aToB; this.currentTickIndex = currentTickIndex; this.timestamp = timestamp; this.staticFeeRate = staticFeeRate; this.adaptiveFeeConstants = adaptiveFeeInfo.adaptiveFeeConstants; this.adaptiveFeeVariables = new AdaptiveFeeVariables(adaptiveFeeInfo.adaptiveFeeVariables.lastReferenceUpdateTimestamp, adaptiveFeeInfo.adaptiveFeeVariables.lastMajorSwapTimestamp, adaptiveFeeInfo.adaptiveFeeVariables.tickGroupIndexReference, adaptiveFeeInfo.adaptiveFeeVariables.volatilityReference, adaptiveFeeInfo.adaptiveFeeVariables.volatilityAccumulator); this.tickGroupIndex = Math.floor(this.currentTickIndex / this.adaptiveFeeConstants.tickGroupSize); this.adaptiveFeeVariables.updateReference(this.tickGroupIndex, this.timestamp, this.adaptiveFeeConstants); const { coreTickGroupRangeLowerBound, coreTickGroupRangeUpperBound } = this.adaptiveFeeVariables.getCoreTickGroupRange(this.adaptiveFeeConstants); this.coreTickGroupRangeLowerBound = coreTickGroupRangeLowerBound; this.coreTickGroupRangeUpperBound = coreTickGroupRangeUpperBound; } updateVolatilityAccumulator() { this.adaptiveFeeVariables.updateVolatilityAccumulator(this.tickGroupIndex, this.adaptiveFeeConstants); } getTotalFeeRate() { const adaptiveFeeRate = this.adaptiveFeeVariables.computeAdaptiveFeeRate(this.adaptiveFeeConstants); const totalFeeRate = this.staticFeeRate + adaptiveFeeRate; return Math.min(totalFeeRate, public_1.FEE_RATE_HARD_LIMIT); } getBoundedSqrtPriceTarget(sqrtPrice, currLiquidity) { if (this.adaptiveFeeConstants.adaptiveFeeControlFactor === 0) { return { boundedSqrtPriceTarget: sqrtPrice, adaptiveFeeUpdateSkipped: true, }; } if (currLiquidity.isZero()) { return { boundedSqrtPriceTarget: sqrtPrice, adaptiveFeeUpdateSkipped: true, }; } if (this.coreTickGroupRangeLowerBound && this.tickGroupIndex < this.coreTickGroupRangeLowerBound.tickGroupIndex) { if (this.aToB) { return { boundedSqrtPriceTarget: sqrtPrice, adaptiveFeeUpdateSkipped: true, }; } else { return { boundedSqrtPriceTarget: anchor_1.BN.min(sqrtPrice, this.coreTickGroupRangeLowerBound.sqrtPrice), adaptiveFeeUpdateSkipped: true, }; } } if (this.coreTickGroupRangeUpperBound && this.tickGroupIndex > this.coreTickGroupRangeUpperBound.tickGroupIndex) { if (this.aToB) { return { boundedSqrtPriceTarget: anchor_1.BN.max(sqrtPrice, this.coreTickGroupRangeUpperBound.sqrtPrice), adaptiveFeeUpdateSkipped: true, }; } else { return { boundedSqrtPriceTarget: sqrtPrice, adaptiveFeeUpdateSkipped: true, }; } } const boundaryTickIndex = this.aToB ? this.tickGroupIndex * this.adaptiveFeeConstants.tickGroupSize : this.tickGroupIndex * this.adaptiveFeeConstants.tickGroupSize + this.adaptiveFeeConstants.tickGroupSize; const boundarySqrtPrice = public_2.PriceMath.tickIndexToSqrtPriceX64(Math.max(public_1.MIN_TICK_INDEX, Math.min(boundaryTickIndex, public_1.MAX_TICK_INDEX))); if (this.aToB) { return { boundedSqrtPriceTarget: anchor_1.BN.max(sqrtPrice, boundarySqrtPrice), adaptiveFeeUpdateSkipped: false, }; } else { return { boundedSqrtPriceTarget: anchor_1.BN.min(sqrtPrice, boundarySqrtPrice), adaptiveFeeUpdateSkipped: false, }; } } advanceTickGroup() { if (this.aToB) { this.tickGroupIndex--; } else { this.tickGroupIndex++; } } advanceTickGroupAfterSkip(sqrtPrice, nextTickSqrtPrice, nextTickIndex) { const [tickIndex, isOnTickGroupBoundary] = (() => { if (sqrtPrice.eq(nextTickSqrtPrice)) { const isOnTickGroupBoundary = nextTickIndex % this.adaptiveFeeConstants.tickGroupSize === 0; return [nextTickIndex, isOnTickGroupBoundary]; } else { const tickIndex = public_2.PriceMath.sqrtPriceX64ToTickIndex(sqrtPrice); const isOnTickGroupBoundary = tickIndex % this.adaptiveFeeConstants.tickGroupSize === 0 && sqrtPrice.eq(public_2.PriceMath.tickIndexToSqrtPriceX64(tickIndex)); return [tickIndex, isOnTickGroupBoundary]; } })(); const lastTraversedTickGroupIndex = isOnTickGroupBoundary && !this.aToB ? tickIndex / this.adaptiveFeeConstants.tickGroupSize - 1 : Math.floor(tickIndex / this.adaptiveFeeConstants.tickGroupSize); if ((this.aToB && lastTraversedTickGroupIndex < this.tickGroupIndex) || (!this.aToB && lastTraversedTickGroupIndex > this.tickGroupIndex)) { this.tickGroupIndex = lastTraversedTickGroupIndex; this.adaptiveFeeVariables.updateVolatilityAccumulator(this.tickGroupIndex, this.adaptiveFeeConstants); } if (this.aToB) { this.tickGroupIndex--; } else { this.tickGroupIndex++; } } updateMajorSwapTimestamp(preSqrtPrice, postSqrtPrice) { this.adaptiveFeeVariables.updateMajorSwapTimestamp(preSqrtPrice, postSqrtPrice, this.timestamp, this.adaptiveFeeConstants); } getNextAdaptiveFeeInfo() { return { adaptiveFeeConstants: this.adaptiveFeeConstants, adaptiveFeeVariables: this.adaptiveFeeVariables.toData(), }; } } class AdaptiveFeeVariables { lastReferenceUpdateTimestamp; lastMajorSwapTimestamp; tickGroupIndexReference; volatilityReference; volatilityAccumulator; constructor(lastReferenceUpdateTimestamp, lastMajorSwapTimestamp, tickGroupIndexReference, volatilityReference, volatilityAccumulator) { this.lastReferenceUpdateTimestamp = lastReferenceUpdateTimestamp; this.lastMajorSwapTimestamp = lastMajorSwapTimestamp; this.tickGroupIndexReference = tickGroupIndexReference; this.volatilityReference = volatilityReference; this.volatilityAccumulator = volatilityAccumulator; } getCoreTickGroupRange(adaptiveFeeConstants) { const maxVolatilityAccumulatorTickGroupIndexDelta = Math.ceil((adaptiveFeeConstants.maxVolatilityAccumulator - this.volatilityReference) / public_1.VOLATILITY_ACCUMULATOR_SCALE_FACTOR); const coreTickGroupRangeLowerIndex = this.tickGroupIndexReference - maxVolatilityAccumulatorTickGroupIndexDelta; const coreTickGroupRangeUpperIndex = this.tickGroupIndexReference + maxVolatilityAccumulatorTickGroupIndexDelta; const coreTickGroupRangeLowerBoundTickIndex = coreTickGroupRangeLowerIndex * adaptiveFeeConstants.tickGroupSize; const coreTickGroupRangeUpperBoundTickIndex = coreTickGroupRangeUpperIndex * adaptiveFeeConstants.tickGroupSize + adaptiveFeeConstants.tickGroupSize; const coreTickGroupRangeLowerBound = coreTickGroupRangeLowerBoundTickIndex > public_1.MIN_TICK_INDEX ? { tickGroupIndex: coreTickGroupRangeLowerIndex, sqrtPrice: public_2.PriceMath.tickIndexToSqrtPriceX64(coreTickGroupRangeLowerBoundTickIndex), } : null; const coreTickGroupRangeUpperBound = coreTickGroupRangeUpperBoundTickIndex < public_1.MAX_TICK_INDEX ? { tickGroupIndex: coreTickGroupRangeUpperIndex, sqrtPrice: public_2.PriceMath.tickIndexToSqrtPriceX64(coreTickGroupRangeUpperBoundTickIndex), } : null; return { coreTickGroupRangeLowerBound, coreTickGroupRangeUpperBound }; } updateReference(tickGroupIndex, timestamp, adaptiveFeeConstants) { const maxTimestamp = anchor_1.BN.max(this.lastReferenceUpdateTimestamp, this.lastMajorSwapTimestamp); (0, tiny_invariant_1.default)(timestamp.gte(maxTimestamp), "Invalid timestamp"); const referenceAge = timestamp.sub(this.lastReferenceUpdateTimestamp); if (referenceAge.gtn(public_1.MAX_REFERENCE_AGE)) { this.tickGroupIndexReference = tickGroupIndex; this.volatilityReference = 0; this.lastReferenceUpdateTimestamp = timestamp; return; } const elapsed = timestamp.sub(maxTimestamp); if (elapsed.ltn(adaptiveFeeConstants.filterPeriod)) { } else if (elapsed.ltn(adaptiveFeeConstants.decayPeriod)) { this.tickGroupIndexReference = tickGroupIndex; this.volatilityReference = Math.floor((this.volatilityAccumulator * adaptiveFeeConstants.reductionFactor) / public_1.REDUCTION_FACTOR_DENOMINATOR); this.lastReferenceUpdateTimestamp = timestamp; } else { this.tickGroupIndexReference = tickGroupIndex; this.volatilityReference = 0; this.lastReferenceUpdateTimestamp = timestamp; } } updateVolatilityAccumulator(tickGroupIndex, adaptiveFeeConstants) { const indexDelta = Math.abs(this.tickGroupIndexReference - tickGroupIndex); const volatilityAccumulator = this.volatilityReference + indexDelta * public_1.VOLATILITY_ACCUMULATOR_SCALE_FACTOR; this.volatilityAccumulator = Math.min(volatilityAccumulator, adaptiveFeeConstants.maxVolatilityAccumulator); } updateMajorSwapTimestamp(preSqrtPrice, postSqrtPrice, timestamp, adaptiveFeeConstants) { if (AdaptiveFeeVariables.isMajorSwap(preSqrtPrice, postSqrtPrice, adaptiveFeeConstants.majorSwapThresholdTicks)) { this.lastMajorSwapTimestamp = timestamp; } } computeAdaptiveFeeRate(adaptiveFeeConstants) { const crossed = this.volatilityAccumulator * adaptiveFeeConstants.tickGroupSize; const squared = new anchor_1.BN(crossed).mul(new anchor_1.BN(crossed)); const dividend = new anchor_1.BN(adaptiveFeeConstants.adaptiveFeeControlFactor).mul(squared); const divisor = new anchor_1.BN(public_1.ADAPTIVE_FEE_CONTROL_FACTOR_DENOMINATOR) .mul(new anchor_1.BN(public_1.VOLATILITY_ACCUMULATOR_SCALE_FACTOR)) .mul(new anchor_1.BN(public_1.VOLATILITY_ACCUMULATOR_SCALE_FACTOR)); const feeRate = dividend.add(divisor.subn(1)).div(divisor); if (feeRate.gtn(public_1.FEE_RATE_HARD_LIMIT)) { return public_1.FEE_RATE_HARD_LIMIT; } return feeRate.toNumber(); } toData() { return { lastReferenceUpdateTimestamp: this.lastReferenceUpdateTimestamp, lastMajorSwapTimestamp: this.lastMajorSwapTimestamp, tickGroupIndexReference: this.tickGroupIndexReference, volatilityReference: this.volatilityReference, volatilityAccumulator: this.volatilityAccumulator, }; } static isMajorSwap(preSqrtPrice, postSqrtPrice, majorSwapThresholdTicks) { const [smallerSqrtPrice, largerSqrtPrice] = preSqrtPrice.lt(postSqrtPrice) ? [preSqrtPrice, postSqrtPrice] : [postSqrtPrice, preSqrtPrice]; const majorSwapSqrtPriceFactor = public_2.PriceMath.tickIndexToSqrtPriceX64(majorSwapThresholdTicks); const majorSwapSqrtPriceTarget = smallerSqrtPrice .mul(majorSwapSqrtPriceFactor) .shrn(64); return largerSqrtPrice.gte(majorSwapSqrtPriceTarget); } } exports.AdaptiveFeeVariables = AdaptiveFeeVariables; //# sourceMappingURL=fee-rate-manager.js.map