@orca-so/whirlpools-sdk
Version:
Typescript SDK to interact with Orca's Whirlpool program.
295 lines • 14.1 kB
JavaScript
"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