@stabbleorg/mclmm-sdk 0.3.2 → 0.3.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/index.js +440 -53
- package/lib/index.mjs +439 -53
- package/lib/position-manager.d.ts +14 -1
- package/lib/position-manager.d.ts.map +1 -1
- package/lib/utils/index.d.ts +1 -0
- package/lib/utils/index.d.ts.map +1 -1
- package/lib/utils/position.d.ts +191 -0
- package/lib/utils/position.d.ts.map +1 -0
- package/lib/utils/tick.d.ts +1 -0
- package/lib/utils/tick.d.ts.map +1 -1
- package/package.json +1 -1
package/lib/index.mjs
CHANGED
|
@@ -5898,6 +5898,22 @@ var TickUtils = class _TickUtils {
|
|
|
5898
5898
|
);
|
|
5899
5899
|
}
|
|
5900
5900
|
}
|
|
5901
|
+
static getTickOffsetInArray(tickIndex, tickSpacing) {
|
|
5902
|
+
if (tickIndex % tickSpacing != 0) {
|
|
5903
|
+
throw new Error("tickIndex % tickSpacing not equal 0");
|
|
5904
|
+
}
|
|
5905
|
+
const startTickIndex = _TickUtils.getTickArrayStartIndexByTick(
|
|
5906
|
+
tickIndex,
|
|
5907
|
+
tickSpacing
|
|
5908
|
+
);
|
|
5909
|
+
const offsetInArray = Math.floor(
|
|
5910
|
+
(tickIndex - startTickIndex) / tickSpacing
|
|
5911
|
+
);
|
|
5912
|
+
if (offsetInArray < 0 || offsetInArray >= TICK_ARRAY_SIZE) {
|
|
5913
|
+
throw new Error("tick offset in array overflow");
|
|
5914
|
+
}
|
|
5915
|
+
return offsetInArray;
|
|
5916
|
+
}
|
|
5901
5917
|
/**
|
|
5902
5918
|
* Get the start index of the tick array containing a specific tick
|
|
5903
5919
|
* @param tick - Target tick
|
|
@@ -8028,6 +8044,268 @@ var PoolUtils = class {
|
|
|
8028
8044
|
}
|
|
8029
8045
|
};
|
|
8030
8046
|
|
|
8047
|
+
// src/utils/position.ts
|
|
8048
|
+
import BN5 from "bn.js";
|
|
8049
|
+
var PositionUtils = class _PositionUtils {
|
|
8050
|
+
/**
|
|
8051
|
+
* Calculate fee growth inside a position's tick range.
|
|
8052
|
+
*
|
|
8053
|
+
* formula:
|
|
8054
|
+
* ```
|
|
8055
|
+
* feeGrowthInside = feeGrowthGlobal - feeGrowthBelow - feeGrowthAbove
|
|
8056
|
+
* ```
|
|
8057
|
+
*
|
|
8058
|
+
* Where feeGrowthBelow and feeGrowthAbove depend on the current tick
|
|
8059
|
+
* relative to the position's tick boundaries.
|
|
8060
|
+
*
|
|
8061
|
+
* @param params - Parameters for fee growth calculation
|
|
8062
|
+
* @returns Fee growth inside for both tokens (X64 fixed-point)
|
|
8063
|
+
*/
|
|
8064
|
+
static getFeeGrowthInside(params) {
|
|
8065
|
+
const {
|
|
8066
|
+
tickCurrent,
|
|
8067
|
+
tickLower,
|
|
8068
|
+
tickUpper,
|
|
8069
|
+
tickLowerState,
|
|
8070
|
+
tickUpperState,
|
|
8071
|
+
feeGrowthGlobal0X64,
|
|
8072
|
+
feeGrowthGlobal1X64
|
|
8073
|
+
} = params;
|
|
8074
|
+
const feeGrowthGlobal0 = new BN5(feeGrowthGlobal0X64.toString());
|
|
8075
|
+
const feeGrowthGlobal1 = new BN5(feeGrowthGlobal1X64.toString());
|
|
8076
|
+
const tickLowerFeeGrowthOutside0 = new BN5(
|
|
8077
|
+
tickLowerState.feeGrowthOutside0X64.toString()
|
|
8078
|
+
);
|
|
8079
|
+
const tickLowerFeeGrowthOutside1 = new BN5(
|
|
8080
|
+
tickLowerState.feeGrowthOutside1X64.toString()
|
|
8081
|
+
);
|
|
8082
|
+
const tickUpperFeeGrowthOutside0 = new BN5(
|
|
8083
|
+
tickUpperState.feeGrowthOutside0X64.toString()
|
|
8084
|
+
);
|
|
8085
|
+
const tickUpperFeeGrowthOutside1 = new BN5(
|
|
8086
|
+
tickUpperState.feeGrowthOutside1X64.toString()
|
|
8087
|
+
);
|
|
8088
|
+
let feeGrowthBelow0X64;
|
|
8089
|
+
let feeGrowthBelow1X64;
|
|
8090
|
+
if (tickCurrent >= tickLower) {
|
|
8091
|
+
feeGrowthBelow0X64 = tickLowerFeeGrowthOutside0;
|
|
8092
|
+
feeGrowthBelow1X64 = tickLowerFeeGrowthOutside1;
|
|
8093
|
+
} else {
|
|
8094
|
+
feeGrowthBelow0X64 = MathUtils.wrappingSubU128(
|
|
8095
|
+
feeGrowthGlobal0,
|
|
8096
|
+
tickLowerFeeGrowthOutside0
|
|
8097
|
+
);
|
|
8098
|
+
feeGrowthBelow1X64 = MathUtils.wrappingSubU128(
|
|
8099
|
+
feeGrowthGlobal1,
|
|
8100
|
+
tickLowerFeeGrowthOutside1
|
|
8101
|
+
);
|
|
8102
|
+
}
|
|
8103
|
+
let feeGrowthAbove0X64;
|
|
8104
|
+
let feeGrowthAbove1X64;
|
|
8105
|
+
if (tickCurrent < tickUpper) {
|
|
8106
|
+
feeGrowthAbove0X64 = tickUpperFeeGrowthOutside0;
|
|
8107
|
+
feeGrowthAbove1X64 = tickUpperFeeGrowthOutside1;
|
|
8108
|
+
} else {
|
|
8109
|
+
feeGrowthAbove0X64 = MathUtils.wrappingSubU128(
|
|
8110
|
+
feeGrowthGlobal0,
|
|
8111
|
+
tickUpperFeeGrowthOutside0
|
|
8112
|
+
);
|
|
8113
|
+
feeGrowthAbove1X64 = MathUtils.wrappingSubU128(
|
|
8114
|
+
feeGrowthGlobal1,
|
|
8115
|
+
tickUpperFeeGrowthOutside1
|
|
8116
|
+
);
|
|
8117
|
+
}
|
|
8118
|
+
const feeGrowthInside0X64 = MathUtils.wrappingSubU128(
|
|
8119
|
+
MathUtils.wrappingSubU128(feeGrowthGlobal0, feeGrowthBelow0X64),
|
|
8120
|
+
feeGrowthAbove0X64
|
|
8121
|
+
);
|
|
8122
|
+
const feeGrowthInside1X64 = MathUtils.wrappingSubU128(
|
|
8123
|
+
MathUtils.wrappingSubU128(feeGrowthGlobal1, feeGrowthBelow1X64),
|
|
8124
|
+
feeGrowthAbove1X64
|
|
8125
|
+
);
|
|
8126
|
+
return {
|
|
8127
|
+
feeGrowthInside0X64,
|
|
8128
|
+
feeGrowthInside1X64
|
|
8129
|
+
};
|
|
8130
|
+
}
|
|
8131
|
+
/**
|
|
8132
|
+
* Calculate pending fees for a position.
|
|
8133
|
+
*
|
|
8134
|
+
* Formula:
|
|
8135
|
+
* ```
|
|
8136
|
+
* feeDelta = (feeGrowthInside - feeGrowthInsideLast) × liquidity / 2^64
|
|
8137
|
+
* totalFees = tokenFeesOwed + feeDelta
|
|
8138
|
+
* ```
|
|
8139
|
+
*
|
|
8140
|
+
* @param params - Parameters for fee calculation
|
|
8141
|
+
* @returns Pending fees for both tokens in native units
|
|
8142
|
+
*/
|
|
8143
|
+
static getPositionFees(params) {
|
|
8144
|
+
const {
|
|
8145
|
+
liquidity,
|
|
8146
|
+
tickLower,
|
|
8147
|
+
tickUpper,
|
|
8148
|
+
feeGrowthInside0LastX64,
|
|
8149
|
+
feeGrowthInside1LastX64,
|
|
8150
|
+
tokenFeesOwed0,
|
|
8151
|
+
tokenFeesOwed1,
|
|
8152
|
+
tickCurrent,
|
|
8153
|
+
feeGrowthGlobal0X64,
|
|
8154
|
+
feeGrowthGlobal1X64,
|
|
8155
|
+
tickLowerState,
|
|
8156
|
+
tickUpperState
|
|
8157
|
+
} = params;
|
|
8158
|
+
const { feeGrowthInside0X64, feeGrowthInside1X64 } = _PositionUtils.getFeeGrowthInside({
|
|
8159
|
+
tickCurrent,
|
|
8160
|
+
tickLower,
|
|
8161
|
+
tickUpper,
|
|
8162
|
+
tickLowerState,
|
|
8163
|
+
tickUpperState,
|
|
8164
|
+
feeGrowthGlobal0X64,
|
|
8165
|
+
feeGrowthGlobal1X64
|
|
8166
|
+
});
|
|
8167
|
+
const liquidityBN = new BN5(liquidity.toString());
|
|
8168
|
+
const feeGrowthInside0Last = new BN5(feeGrowthInside0LastX64.toString());
|
|
8169
|
+
const feeGrowthInside1Last = new BN5(feeGrowthInside1LastX64.toString());
|
|
8170
|
+
const feesOwed0 = new BN5(tokenFeesOwed0.toString());
|
|
8171
|
+
const feesOwed1 = new BN5(tokenFeesOwed1.toString());
|
|
8172
|
+
const feeGrowthDelta0 = MathUtils.wrappingSubU128(
|
|
8173
|
+
feeGrowthInside0X64,
|
|
8174
|
+
feeGrowthInside0Last
|
|
8175
|
+
);
|
|
8176
|
+
const feeGrowthDelta1 = MathUtils.wrappingSubU128(
|
|
8177
|
+
feeGrowthInside1X64,
|
|
8178
|
+
feeGrowthInside1Last
|
|
8179
|
+
);
|
|
8180
|
+
const feeAmount0 = MathUtils.mulDivFloor(feeGrowthDelta0, liquidityBN, Q64);
|
|
8181
|
+
const feeAmount1 = MathUtils.mulDivFloor(feeGrowthDelta1, liquidityBN, Q64);
|
|
8182
|
+
return {
|
|
8183
|
+
tokenFees0: feesOwed0.add(feeAmount0),
|
|
8184
|
+
tokenFees1: feesOwed1.add(feeAmount1)
|
|
8185
|
+
};
|
|
8186
|
+
}
|
|
8187
|
+
/**
|
|
8188
|
+
* Calculate reward growth inside a position's tick range for all reward tokens.
|
|
8189
|
+
*
|
|
8190
|
+
* Formula:
|
|
8191
|
+
* ```
|
|
8192
|
+
* rewardGrowthInside = rewardGrowthGlobal - rewardGrowthBelow - rewardGrowthAbove
|
|
8193
|
+
* ```
|
|
8194
|
+
*
|
|
8195
|
+
* Special cases:
|
|
8196
|
+
* - If tickLower has no liquidity (liquidityGross = 0), rewardGrowthBelow = rewardGrowthGlobal
|
|
8197
|
+
* - If tickUpper has no liquidity (liquidityGross = 0), rewardGrowthAbove = 0
|
|
8198
|
+
*
|
|
8199
|
+
* @param params - Parameters for reward growth calculation
|
|
8200
|
+
* @returns Reward growth inside for each reward token (X64 fixed-point)
|
|
8201
|
+
*/
|
|
8202
|
+
static getRewardGrowthInside(params) {
|
|
8203
|
+
const {
|
|
8204
|
+
tickCurrent,
|
|
8205
|
+
tickLower,
|
|
8206
|
+
tickUpper,
|
|
8207
|
+
tickLowerState,
|
|
8208
|
+
tickUpperState,
|
|
8209
|
+
rewardInfos
|
|
8210
|
+
} = params;
|
|
8211
|
+
const rewardGrowthsInside = [];
|
|
8212
|
+
for (let i = 0; i < rewardInfos.length; i++) {
|
|
8213
|
+
const rewardGrowthGlobal = new BN5(
|
|
8214
|
+
rewardInfos[i].rewardGrowthGlobalX64.toString()
|
|
8215
|
+
);
|
|
8216
|
+
let rewardGrowthBelow;
|
|
8217
|
+
if (tickLowerState.liquidityGross === 0n) {
|
|
8218
|
+
rewardGrowthBelow = rewardGrowthGlobal;
|
|
8219
|
+
} else if (tickCurrent < tickLower) {
|
|
8220
|
+
rewardGrowthBelow = rewardGrowthGlobal.sub(
|
|
8221
|
+
new BN5(tickLowerState.rewardGrowthsOutsideX64[i].toString())
|
|
8222
|
+
);
|
|
8223
|
+
} else {
|
|
8224
|
+
rewardGrowthBelow = new BN5(
|
|
8225
|
+
tickLowerState.rewardGrowthsOutsideX64[i].toString()
|
|
8226
|
+
);
|
|
8227
|
+
}
|
|
8228
|
+
let rewardGrowthAbove;
|
|
8229
|
+
if (tickUpperState.liquidityGross === 0n) {
|
|
8230
|
+
rewardGrowthAbove = new BN5(0);
|
|
8231
|
+
} else if (tickCurrent < tickUpper) {
|
|
8232
|
+
rewardGrowthAbove = new BN5(
|
|
8233
|
+
tickUpperState.rewardGrowthsOutsideX64[i].toString()
|
|
8234
|
+
);
|
|
8235
|
+
} else {
|
|
8236
|
+
rewardGrowthAbove = rewardGrowthGlobal.sub(
|
|
8237
|
+
new BN5(tickUpperState.rewardGrowthsOutsideX64[i].toString())
|
|
8238
|
+
);
|
|
8239
|
+
}
|
|
8240
|
+
const rewardGrowthInside = MathUtils.wrappingSubU128(
|
|
8241
|
+
MathUtils.wrappingSubU128(rewardGrowthGlobal, rewardGrowthBelow),
|
|
8242
|
+
rewardGrowthAbove
|
|
8243
|
+
);
|
|
8244
|
+
rewardGrowthsInside.push(rewardGrowthInside);
|
|
8245
|
+
}
|
|
8246
|
+
return rewardGrowthsInside;
|
|
8247
|
+
}
|
|
8248
|
+
/**
|
|
8249
|
+
* Calculate pending rewards for a position.
|
|
8250
|
+
*
|
|
8251
|
+
* Formula:
|
|
8252
|
+
* ```
|
|
8253
|
+
* rewardDelta = (rewardGrowthInside - rewardGrowthInsideLast) × liquidity / 2^64
|
|
8254
|
+
* totalReward = rewardAmountOwed + rewardDelta
|
|
8255
|
+
* ```
|
|
8256
|
+
*
|
|
8257
|
+
* @param params - Parameters for reward calculation
|
|
8258
|
+
* @returns Pending rewards for each reward token in native units
|
|
8259
|
+
*/
|
|
8260
|
+
static getPositionRewards(params) {
|
|
8261
|
+
const {
|
|
8262
|
+
liquidity,
|
|
8263
|
+
tickLower,
|
|
8264
|
+
tickUpper,
|
|
8265
|
+
positionRewardInfos,
|
|
8266
|
+
tickCurrent,
|
|
8267
|
+
rewardInfos,
|
|
8268
|
+
tickLowerState,
|
|
8269
|
+
tickUpperState
|
|
8270
|
+
} = params;
|
|
8271
|
+
const rewardGrowthsInside = _PositionUtils.getRewardGrowthInside({
|
|
8272
|
+
tickCurrent,
|
|
8273
|
+
tickLower,
|
|
8274
|
+
tickUpper,
|
|
8275
|
+
tickLowerState,
|
|
8276
|
+
tickUpperState,
|
|
8277
|
+
rewardInfos
|
|
8278
|
+
});
|
|
8279
|
+
const liquidityBN = new BN5(liquidity.toString());
|
|
8280
|
+
const rewards = [];
|
|
8281
|
+
for (let i = 0; i < rewardGrowthsInside.length; i++) {
|
|
8282
|
+
const rewardGrowthInside = rewardGrowthsInside[i];
|
|
8283
|
+
const positionRewardInfo = positionRewardInfos[i];
|
|
8284
|
+
if (!positionRewardInfo) {
|
|
8285
|
+
rewards.push(new BN5(0));
|
|
8286
|
+
continue;
|
|
8287
|
+
}
|
|
8288
|
+
const growthInsideLast = new BN5(
|
|
8289
|
+
positionRewardInfo.growthInsideLastX64.toString()
|
|
8290
|
+
);
|
|
8291
|
+
const rewardAmountOwed = new BN5(
|
|
8292
|
+
positionRewardInfo.rewardAmountOwed.toString()
|
|
8293
|
+
);
|
|
8294
|
+
const rewardGrowthDelta = MathUtils.wrappingSubU128(
|
|
8295
|
+
rewardGrowthInside,
|
|
8296
|
+
growthInsideLast
|
|
8297
|
+
);
|
|
8298
|
+
const rewardAmountDelta = MathUtils.mulDivFloor(
|
|
8299
|
+
rewardGrowthDelta,
|
|
8300
|
+
liquidityBN,
|
|
8301
|
+
Q64
|
|
8302
|
+
);
|
|
8303
|
+
rewards.push(rewardAmountOwed.add(rewardAmountDelta));
|
|
8304
|
+
}
|
|
8305
|
+
return { rewards };
|
|
8306
|
+
}
|
|
8307
|
+
};
|
|
8308
|
+
|
|
8031
8309
|
// src/utils/index.ts
|
|
8032
8310
|
import {
|
|
8033
8311
|
getAddressEncoder as getAddressEncoder29
|
|
@@ -8197,7 +8475,7 @@ import {
|
|
|
8197
8475
|
address as address3
|
|
8198
8476
|
} from "@solana/kit";
|
|
8199
8477
|
import { TOKEN_PROGRAM_ADDRESS as TOKEN_PROGRAM_ADDRESS2 } from "@solana-program/token";
|
|
8200
|
-
import
|
|
8478
|
+
import BN6 from "bn.js";
|
|
8201
8479
|
import Decimal3 from "decimal.js";
|
|
8202
8480
|
|
|
8203
8481
|
// src/utils/token.ts
|
|
@@ -8239,8 +8517,8 @@ var PoolManager = class {
|
|
|
8239
8517
|
} = params;
|
|
8240
8518
|
const addressA = address3(tokenMintA);
|
|
8241
8519
|
const addressB = address3(tokenMintB);
|
|
8242
|
-
const isAFirst = new
|
|
8243
|
-
new
|
|
8520
|
+
const isAFirst = new BN6(Buffer.from(addressB)).gt(
|
|
8521
|
+
new BN6(Buffer.from(addressA))
|
|
8244
8522
|
);
|
|
8245
8523
|
const [token0, token1, decimals0, decimals1, priceAdjusted] = isAFirst ? [tokenMintA, tokenMintB, mintADecimals, mintBDecimals, initialPrice] : [
|
|
8246
8524
|
tokenMintB,
|
|
@@ -8422,7 +8700,7 @@ var PoolManager = class {
|
|
|
8422
8700
|
getToken(this.config.rpc, poolState.tokenVault1)
|
|
8423
8701
|
]);
|
|
8424
8702
|
const currentPrice = this.calculatePoolPrice(
|
|
8425
|
-
new
|
|
8703
|
+
new BN6(poolState.sqrtPriceX64.toString()),
|
|
8426
8704
|
tokenA.decimals,
|
|
8427
8705
|
tokenB.decimals
|
|
8428
8706
|
);
|
|
@@ -8479,7 +8757,7 @@ import {
|
|
|
8479
8757
|
getTransferCheckedInstruction
|
|
8480
8758
|
} from "@solana-program/token";
|
|
8481
8759
|
import { TOKEN_2022_PROGRAM_ADDRESS as TOKEN_2022_PROGRAM_ADDRESS2 } from "@solana-program/token-2022";
|
|
8482
|
-
import
|
|
8760
|
+
import BN7 from "bn.js";
|
|
8483
8761
|
|
|
8484
8762
|
// ../node_modules/@solana/spl-token/lib/esm/constants.js
|
|
8485
8763
|
import { PublicKey } from "@solana/web3.js";
|
|
@@ -9040,21 +9318,22 @@ var PositionManager = class {
|
|
|
9040
9318
|
* Enrich position state with computed fields from pool data
|
|
9041
9319
|
* @param position - Raw position state from blockchain
|
|
9042
9320
|
* @param pool - Pool state from blockchain
|
|
9321
|
+
* @param fees - Position fees
|
|
9043
9322
|
* @returns Enriched position info with calculated amounts and prices
|
|
9044
9323
|
*/
|
|
9045
|
-
enrichPositionInfo(position, pool) {
|
|
9324
|
+
enrichPositionInfo(position, pool, fees, rewards) {
|
|
9046
9325
|
const sqrtPriceLowerX64 = SqrtPriceMath.getSqrtPriceX64FromTick(
|
|
9047
9326
|
position.tickLowerIndex
|
|
9048
9327
|
);
|
|
9049
9328
|
const sqrtPriceUpperX64 = SqrtPriceMath.getSqrtPriceX64FromTick(
|
|
9050
9329
|
position.tickUpperIndex
|
|
9051
9330
|
);
|
|
9052
|
-
const sqrtPriceCurrentX64 = new
|
|
9331
|
+
const sqrtPriceCurrentX64 = new BN7(pool.sqrtPriceX64.toString());
|
|
9053
9332
|
const { amountA, amountB } = LiquidityMath.getAmountsFromLiquidity(
|
|
9054
9333
|
sqrtPriceCurrentX64,
|
|
9055
9334
|
sqrtPriceLowerX64,
|
|
9056
9335
|
sqrtPriceUpperX64,
|
|
9057
|
-
new
|
|
9336
|
+
new BN7(position.liquidity.toString()),
|
|
9058
9337
|
true
|
|
9059
9338
|
);
|
|
9060
9339
|
const [_amountA, _amountB] = [
|
|
@@ -9075,9 +9354,13 @@ var PositionManager = class {
|
|
|
9075
9354
|
});
|
|
9076
9355
|
const inRange = pool.tickCurrent >= position.tickLowerIndex && pool.tickCurrent < position.tickUpperIndex;
|
|
9077
9356
|
const unclaimedFees = {
|
|
9078
|
-
token0: new
|
|
9079
|
-
token1: new
|
|
9357
|
+
token0: fees?.tokenFees0 ? fees?.tokenFees0 : new BN7(0),
|
|
9358
|
+
token1: fees?.tokenFees1 ? fees?.tokenFees1 : new BN7(0)
|
|
9080
9359
|
};
|
|
9360
|
+
const unclaimedRewards = rewards?.rewards ? rewards.rewards.map((amount, i) => ({
|
|
9361
|
+
mint: pool.rewardInfos[i]?.tokenMint,
|
|
9362
|
+
amount
|
|
9363
|
+
})).filter((r) => r.mint !== void 0) : void 0;
|
|
9081
9364
|
const ageSeconds = 0;
|
|
9082
9365
|
return {
|
|
9083
9366
|
...position,
|
|
@@ -9090,10 +9373,8 @@ var PositionManager = class {
|
|
|
9090
9373
|
inRange,
|
|
9091
9374
|
ageSeconds,
|
|
9092
9375
|
unclaimedFees,
|
|
9093
|
-
|
|
9094
|
-
valueUsd: void 0
|
|
9095
|
-
// unclaimedRewards is optional
|
|
9096
|
-
unclaimedRewards: void 0
|
|
9376
|
+
unclaimedRewards,
|
|
9377
|
+
valueUsd: void 0
|
|
9097
9378
|
};
|
|
9098
9379
|
}
|
|
9099
9380
|
/**
|
|
@@ -9133,7 +9414,16 @@ var PositionManager = class {
|
|
|
9133
9414
|
console.warn(`Pool ${position.poolId} not found for position`);
|
|
9134
9415
|
return null;
|
|
9135
9416
|
}
|
|
9136
|
-
|
|
9417
|
+
const { fees, rewards } = await this.getPositionFeeAndRewards(
|
|
9418
|
+
position,
|
|
9419
|
+
poolAccount.data
|
|
9420
|
+
);
|
|
9421
|
+
return this.enrichPositionInfo(
|
|
9422
|
+
position,
|
|
9423
|
+
poolAccount.data,
|
|
9424
|
+
fees,
|
|
9425
|
+
rewards
|
|
9426
|
+
);
|
|
9137
9427
|
} catch (error) {
|
|
9138
9428
|
console.error(`Failed to enrich position: ${error}`);
|
|
9139
9429
|
return null;
|
|
@@ -9148,6 +9438,101 @@ var PositionManager = class {
|
|
|
9148
9438
|
);
|
|
9149
9439
|
}
|
|
9150
9440
|
}
|
|
9441
|
+
/**
|
|
9442
|
+
* Calculate pending fees and rewards for a position.
|
|
9443
|
+
*
|
|
9444
|
+
* @param position - Personal position state
|
|
9445
|
+
* @param pool - Pool state
|
|
9446
|
+
* @returns Pending fees for both tokens and rewards for each reward token (up to 3)
|
|
9447
|
+
*/
|
|
9448
|
+
async getPositionFeeAndRewards(position, pool) {
|
|
9449
|
+
const [tickArrayLower] = await PdaUtils.getTickArrayStatePda(
|
|
9450
|
+
position.poolId,
|
|
9451
|
+
PdaUtils.getTickArrayStartIndex(
|
|
9452
|
+
position.tickLowerIndex,
|
|
9453
|
+
pool.tickSpacing
|
|
9454
|
+
)
|
|
9455
|
+
);
|
|
9456
|
+
const [tickArrayUpper] = await PdaUtils.getTickArrayStatePda(
|
|
9457
|
+
position.poolId,
|
|
9458
|
+
PdaUtils.getTickArrayStartIndex(
|
|
9459
|
+
position.tickUpperIndex,
|
|
9460
|
+
pool.tickSpacing
|
|
9461
|
+
)
|
|
9462
|
+
);
|
|
9463
|
+
const tickArrayLowerAccount = await fetchMaybeTickArrayState(
|
|
9464
|
+
this.config.rpc,
|
|
9465
|
+
tickArrayLower
|
|
9466
|
+
);
|
|
9467
|
+
const tickArrayUpperAccount = await fetchMaybeTickArrayState(
|
|
9468
|
+
this.config.rpc,
|
|
9469
|
+
tickArrayUpper
|
|
9470
|
+
);
|
|
9471
|
+
if (!tickArrayLowerAccount.exists || !tickArrayUpperAccount.exists) {
|
|
9472
|
+
console.log(
|
|
9473
|
+
"[getPositionFeeAndRewards] tick array state accounts do not exist."
|
|
9474
|
+
);
|
|
9475
|
+
return {
|
|
9476
|
+
fees: { tokenFees0: new BN7(0), tokenFees1: new BN7(0) },
|
|
9477
|
+
rewards: { rewards: [new BN7(0), new BN7(0), new BN7(0)] }
|
|
9478
|
+
};
|
|
9479
|
+
}
|
|
9480
|
+
const tickSpacing = pool.tickSpacing;
|
|
9481
|
+
const tickArrayLowerIndex = TickUtils.getTickOffsetInArray(
|
|
9482
|
+
position.tickLowerIndex,
|
|
9483
|
+
tickSpacing
|
|
9484
|
+
);
|
|
9485
|
+
const tickArrayUpperIndex = TickUtils.getTickOffsetInArray(
|
|
9486
|
+
position.tickUpperIndex,
|
|
9487
|
+
tickSpacing
|
|
9488
|
+
);
|
|
9489
|
+
if (tickArrayLowerIndex < 0 || tickArrayUpperIndex < 0) {
|
|
9490
|
+
console.log("[getPositionFeeAndRewards] tick array indexes < 0.");
|
|
9491
|
+
return {
|
|
9492
|
+
fees: { tokenFees0: new BN7(0), tokenFees1: new BN7(0) },
|
|
9493
|
+
rewards: { rewards: [new BN7(0), new BN7(0), new BN7(0)] }
|
|
9494
|
+
};
|
|
9495
|
+
}
|
|
9496
|
+
const lowerTickState = tickArrayLowerAccount.data.ticks[tickArrayLowerIndex];
|
|
9497
|
+
const upperTickState = tickArrayUpperAccount.data.ticks[tickArrayUpperIndex];
|
|
9498
|
+
const fees = PositionUtils.getPositionFees({
|
|
9499
|
+
liquidity: position.liquidity,
|
|
9500
|
+
tickLower: position.tickLowerIndex,
|
|
9501
|
+
tickUpper: position.tickUpperIndex,
|
|
9502
|
+
feeGrowthInside0LastX64: position.feeGrowthInside0LastX64,
|
|
9503
|
+
feeGrowthInside1LastX64: position.feeGrowthInside1LastX64,
|
|
9504
|
+
tokenFeesOwed0: position.tokenFeesOwed0,
|
|
9505
|
+
tokenFeesOwed1: position.tokenFeesOwed1,
|
|
9506
|
+
tickCurrent: pool.tickCurrent,
|
|
9507
|
+
feeGrowthGlobal0X64: pool.feeGrowthGlobal0X64,
|
|
9508
|
+
feeGrowthGlobal1X64: pool.feeGrowthGlobal1X64,
|
|
9509
|
+
tickLowerState: {
|
|
9510
|
+
feeGrowthOutside0X64: lowerTickState.feeGrowthOutside0X64,
|
|
9511
|
+
feeGrowthOutside1X64: lowerTickState.feeGrowthOutside1X64
|
|
9512
|
+
},
|
|
9513
|
+
tickUpperState: {
|
|
9514
|
+
feeGrowthOutside0X64: upperTickState.feeGrowthOutside0X64,
|
|
9515
|
+
feeGrowthOutside1X64: upperTickState.feeGrowthOutside1X64
|
|
9516
|
+
}
|
|
9517
|
+
});
|
|
9518
|
+
const rewards = PositionUtils.getPositionRewards({
|
|
9519
|
+
liquidity: position.liquidity,
|
|
9520
|
+
tickLower: position.tickLowerIndex,
|
|
9521
|
+
tickUpper: position.tickUpperIndex,
|
|
9522
|
+
positionRewardInfos: position.rewardInfos,
|
|
9523
|
+
tickCurrent: pool.tickCurrent,
|
|
9524
|
+
rewardInfos: pool.rewardInfos,
|
|
9525
|
+
tickLowerState: {
|
|
9526
|
+
liquidityGross: lowerTickState.liquidityGross,
|
|
9527
|
+
rewardGrowthsOutsideX64: lowerTickState.rewardGrowthsOutsideX64
|
|
9528
|
+
},
|
|
9529
|
+
tickUpperState: {
|
|
9530
|
+
liquidityGross: upperTickState.liquidityGross,
|
|
9531
|
+
rewardGrowthsOutsideX64: upperTickState.rewardGrowthsOutsideX64
|
|
9532
|
+
}
|
|
9533
|
+
});
|
|
9534
|
+
return { fees, rewards };
|
|
9535
|
+
}
|
|
9151
9536
|
};
|
|
9152
9537
|
|
|
9153
9538
|
// src/api/config.ts
|
|
@@ -10122,7 +10507,7 @@ var PriceApiClient = class _PriceApiClient {
|
|
|
10122
10507
|
};
|
|
10123
10508
|
|
|
10124
10509
|
// src/swap.ts
|
|
10125
|
-
import
|
|
10510
|
+
import BN8 from "bn.js";
|
|
10126
10511
|
import Decimal6 from "decimal.js";
|
|
10127
10512
|
var DEFAULT_RETRY_CONFIG = {
|
|
10128
10513
|
maxRetries: 3,
|
|
@@ -10182,7 +10567,7 @@ var SwapMathEngine = class {
|
|
|
10182
10567
|
slippageTolerance,
|
|
10183
10568
|
poolAddress
|
|
10184
10569
|
} = params;
|
|
10185
|
-
if (amountIn.lte(new
|
|
10570
|
+
if (amountIn.lte(new BN8(0))) {
|
|
10186
10571
|
throw new ClmmError(
|
|
10187
10572
|
"SWAP_AMOUNT_CANNOT_BE_ZERO" /* SWAP_AMOUNT_CANNOT_BE_ZERO */,
|
|
10188
10573
|
"Swap amount must be greater than zero"
|
|
@@ -10194,8 +10579,8 @@ var SwapMathEngine = class {
|
|
|
10194
10579
|
`Slippage tolerance must be between 0 and 1, got ${slippageTolerance}`
|
|
10195
10580
|
);
|
|
10196
10581
|
}
|
|
10197
|
-
const liquidity = new
|
|
10198
|
-
const sqrtPriceCurrentX64 = new
|
|
10582
|
+
const liquidity = new BN8(pool.liquidity.toString());
|
|
10583
|
+
const sqrtPriceCurrentX64 = new BN8(pool.sqrtPriceX64.toString());
|
|
10199
10584
|
const feeRate = ammConfig.tradeFeeRate;
|
|
10200
10585
|
const sqrtPriceTargetX64 = this.calculateSqrtPriceLimit(
|
|
10201
10586
|
sqrtPriceCurrentX64,
|
|
@@ -10223,9 +10608,9 @@ var SwapMathEngine = class {
|
|
|
10223
10608
|
pool.mintDecimals1
|
|
10224
10609
|
);
|
|
10225
10610
|
const priceImpact = priceAfter.minus(priceBefore).div(priceBefore).abs().toNumber();
|
|
10226
|
-
const slippageBN = new
|
|
10611
|
+
const slippageBN = new BN8(Math.floor(slippageTolerance * 1e4));
|
|
10227
10612
|
const minAmountOut = step.amountOut.sub(
|
|
10228
|
-
step.amountOut.mul(slippageBN).div(new
|
|
10613
|
+
step.amountOut.mul(slippageBN).div(new BN8(1e4))
|
|
10229
10614
|
);
|
|
10230
10615
|
return {
|
|
10231
10616
|
amountIn,
|
|
@@ -10272,17 +10657,17 @@ var SwapMathEngine = class {
|
|
|
10272
10657
|
tickArrayCache
|
|
10273
10658
|
} = params;
|
|
10274
10659
|
const sqrtPriceLimitX64 = this.calculateSqrtPriceLimit(
|
|
10275
|
-
new
|
|
10660
|
+
new BN8(pool.sqrtPriceX64.toString()),
|
|
10276
10661
|
zeroForOne,
|
|
10277
10662
|
slippageTolerance
|
|
10278
10663
|
);
|
|
10279
10664
|
const result = await SwapMath.swapCompute({
|
|
10280
10665
|
poolState: {
|
|
10281
|
-
sqrtPriceX64: new
|
|
10666
|
+
sqrtPriceX64: new BN8(pool.sqrtPriceX64.toString()),
|
|
10282
10667
|
tickCurrent: pool.tickCurrent,
|
|
10283
|
-
liquidity: new
|
|
10284
|
-
feeGrowthGlobal0X64: new
|
|
10285
|
-
feeGrowthGlobal1X64: new
|
|
10668
|
+
liquidity: new BN8(pool.liquidity.toString()),
|
|
10669
|
+
feeGrowthGlobal0X64: new BN8(pool.feeGrowthGlobal0X64.toString()),
|
|
10670
|
+
feeGrowthGlobal1X64: new BN8(pool.feeGrowthGlobal1X64.toString())
|
|
10286
10671
|
},
|
|
10287
10672
|
tickArrayCache,
|
|
10288
10673
|
tickSpacing: pool.tickSpacing,
|
|
@@ -10299,7 +10684,7 @@ var SwapMathEngine = class {
|
|
|
10299
10684
|
poolId: poolAddress
|
|
10300
10685
|
});
|
|
10301
10686
|
const priceBefore = SqrtPriceMath.sqrtPriceX64ToPrice(
|
|
10302
|
-
new
|
|
10687
|
+
new BN8(pool.sqrtPriceX64.toString()),
|
|
10303
10688
|
pool.mintDecimals0,
|
|
10304
10689
|
pool.mintDecimals1
|
|
10305
10690
|
);
|
|
@@ -10309,9 +10694,9 @@ var SwapMathEngine = class {
|
|
|
10309
10694
|
pool.mintDecimals1
|
|
10310
10695
|
);
|
|
10311
10696
|
const priceImpact = priceAfter.minus(priceBefore).div(priceBefore).abs().toNumber();
|
|
10312
|
-
const slippageBN = new
|
|
10697
|
+
const slippageBN = new BN8(Math.floor(slippageTolerance * 1e4));
|
|
10313
10698
|
const minAmountOut = result.amountOut.sub(
|
|
10314
|
-
result.amountOut.mul(slippageBN).div(new
|
|
10699
|
+
result.amountOut.mul(slippageBN).div(new BN8(1e4))
|
|
10315
10700
|
);
|
|
10316
10701
|
const feeImpact = ammConfig.tradeFeeRate / FEE_RATE_DENOMINATOR_NUMBER;
|
|
10317
10702
|
return {
|
|
@@ -10355,10 +10740,10 @@ var SwapMathEngine = class {
|
|
|
10355
10740
|
*/
|
|
10356
10741
|
calculateSqrtPriceLimit(sqrtPriceX64, zeroForOne, slippageTolerance) {
|
|
10357
10742
|
const bps = Math.floor(slippageTolerance * 1e4);
|
|
10358
|
-
const base = new
|
|
10359
|
-
const scaled = zeroForOne ? sqrtPriceX64.mul(base.sub(new
|
|
10360
|
-
const min2 = new
|
|
10361
|
-
const max = new
|
|
10743
|
+
const base = new BN8(1e4);
|
|
10744
|
+
const scaled = zeroForOne ? sqrtPriceX64.mul(base.sub(new BN8(bps))).div(base) : sqrtPriceX64.mul(base.add(new BN8(bps))).div(base);
|
|
10745
|
+
const min2 = new BN8(MIN_SQRT_PRICE_X64);
|
|
10746
|
+
const max = new BN8(MAX_SQRT_PRICE_X64);
|
|
10362
10747
|
if (scaled.lt(min2)) return min2;
|
|
10363
10748
|
if (scaled.gt(max)) return max;
|
|
10364
10749
|
return scaled;
|
|
@@ -10782,7 +11167,7 @@ var SwapManager = class {
|
|
|
10782
11167
|
`Invalid slippage tolerance: ${slippageTolerance}. Must be between 0 and 1.`
|
|
10783
11168
|
);
|
|
10784
11169
|
}
|
|
10785
|
-
if (amountIn.lte(new
|
|
11170
|
+
if (amountIn.lte(new BN8(0))) {
|
|
10786
11171
|
throw new ClmmError(
|
|
10787
11172
|
"SWAP_AMOUNT_CANNOT_BE_ZERO" /* SWAP_AMOUNT_CANNOT_BE_ZERO */,
|
|
10788
11173
|
"Swap amount must be greater than zero."
|
|
@@ -10884,7 +11269,7 @@ var SwapManager = class {
|
|
|
10884
11269
|
`Invalid slippage tolerance: ${slippageTolerance}. Must be between 0 and 1.`
|
|
10885
11270
|
);
|
|
10886
11271
|
}
|
|
10887
|
-
if (amountIn.lte(new
|
|
11272
|
+
if (amountIn.lte(new BN8(0))) {
|
|
10888
11273
|
throw new ClmmError(
|
|
10889
11274
|
"SWAP_AMOUNT_CANNOT_BE_ZERO" /* SWAP_AMOUNT_CANNOT_BE_ZERO */,
|
|
10890
11275
|
"Swap amount must be greater than zero."
|
|
@@ -10938,7 +11323,7 @@ var SwapManager = class {
|
|
|
10938
11323
|
poolAddress
|
|
10939
11324
|
});
|
|
10940
11325
|
const priceBefore = SqrtPriceMath.sqrtPriceX64ToPrice(
|
|
10941
|
-
new
|
|
11326
|
+
new BN8(pool.sqrtPriceX64.toString()),
|
|
10942
11327
|
pool.mintDecimals0,
|
|
10943
11328
|
pool.mintDecimals1
|
|
10944
11329
|
);
|
|
@@ -11048,21 +11433,21 @@ var SwapManager = class {
|
|
|
11048
11433
|
* @returns Number of tick arrays to fetch (includes safety buffer)
|
|
11049
11434
|
*/
|
|
11050
11435
|
estimateTickArrayCount(pool, amountIn) {
|
|
11051
|
-
const liquidity = new
|
|
11052
|
-
if (liquidity.eq(new
|
|
11436
|
+
const liquidity = new BN8(pool.liquidity.toString());
|
|
11437
|
+
if (liquidity.eq(new BN8(0)) || liquidity.lt(new BN8(1e3))) {
|
|
11053
11438
|
return 10;
|
|
11054
11439
|
}
|
|
11055
|
-
const roughImpact = amountIn.mul(new
|
|
11440
|
+
const roughImpact = amountIn.mul(new BN8(1e3)).div(liquidity);
|
|
11056
11441
|
let baseArrays;
|
|
11057
|
-
if (roughImpact.lte(new
|
|
11442
|
+
if (roughImpact.lte(new BN8(1))) {
|
|
11058
11443
|
baseArrays = 2;
|
|
11059
|
-
} else if (roughImpact.lte(new
|
|
11444
|
+
} else if (roughImpact.lte(new BN8(10))) {
|
|
11060
11445
|
baseArrays = 4;
|
|
11061
|
-
} else if (roughImpact.lte(new
|
|
11446
|
+
} else if (roughImpact.lte(new BN8(50))) {
|
|
11062
11447
|
baseArrays = 6;
|
|
11063
|
-
} else if (roughImpact.lte(new
|
|
11448
|
+
} else if (roughImpact.lte(new BN8(100))) {
|
|
11064
11449
|
baseArrays = 8;
|
|
11065
|
-
} else if (roughImpact.lte(new
|
|
11450
|
+
} else if (roughImpact.lte(new BN8(200))) {
|
|
11066
11451
|
baseArrays = 12;
|
|
11067
11452
|
} else {
|
|
11068
11453
|
baseArrays = 15;
|
|
@@ -11107,7 +11492,7 @@ var SwapManager = class {
|
|
|
11107
11492
|
const decOut = zeroForOne ? pool.mintDecimals1 : pool.mintDecimals0;
|
|
11108
11493
|
const promises = params.amounts.map(async (amountIn) => {
|
|
11109
11494
|
try {
|
|
11110
|
-
if (amountIn.lte(new
|
|
11495
|
+
if (amountIn.lte(new BN8(0))) return;
|
|
11111
11496
|
const quote = await this.mathEngine.calculateSimpleSwap({
|
|
11112
11497
|
pool,
|
|
11113
11498
|
ammConfig,
|
|
@@ -11149,7 +11534,7 @@ var SwapManager = class {
|
|
|
11149
11534
|
options
|
|
11150
11535
|
);
|
|
11151
11536
|
const priceBefore = SqrtPriceMath.sqrtPriceX64ToPrice(
|
|
11152
|
-
new
|
|
11537
|
+
new BN8(pool.sqrtPriceX64.toString()),
|
|
11153
11538
|
pool.mintDecimals0,
|
|
11154
11539
|
pool.mintDecimals1
|
|
11155
11540
|
);
|
|
@@ -11272,13 +11657,13 @@ var SwapManager = class {
|
|
|
11272
11657
|
`Extremely high price impact: ${(quote.priceImpact * 100).toFixed(2)}%. Swap likely to fail or result in significant slippage.`
|
|
11273
11658
|
);
|
|
11274
11659
|
}
|
|
11275
|
-
const oneUnit = new
|
|
11660
|
+
const oneUnit = new BN8(10).pow(new BN8(outputDecimals));
|
|
11276
11661
|
if (quote.amountOut.lt(oneUnit)) {
|
|
11277
11662
|
warnings.push(
|
|
11278
11663
|
`Output amount is less than 1 unit of the output token. Consider increasing input amount or slippage tolerance.`
|
|
11279
11664
|
);
|
|
11280
11665
|
}
|
|
11281
|
-
if (quote.amountOut.lte(new
|
|
11666
|
+
if (quote.amountOut.lte(new BN8(0))) {
|
|
11282
11667
|
errors.push(
|
|
11283
11668
|
"Swap would result in zero output. Amount may be too small."
|
|
11284
11669
|
);
|
|
@@ -11299,11 +11684,11 @@ var SwapManager = class {
|
|
|
11299
11684
|
return {
|
|
11300
11685
|
quote: {
|
|
11301
11686
|
amountIn: params.amountIn,
|
|
11302
|
-
amountOut: new
|
|
11303
|
-
minAmountOut: new
|
|
11687
|
+
amountOut: new BN8(0),
|
|
11688
|
+
minAmountOut: new BN8(0),
|
|
11304
11689
|
priceImpact: 0,
|
|
11305
11690
|
route: [],
|
|
11306
|
-
fee: new
|
|
11691
|
+
fee: new BN8(0)
|
|
11307
11692
|
},
|
|
11308
11693
|
willSucceed: false,
|
|
11309
11694
|
errors,
|
|
@@ -11403,7 +11788,7 @@ var SwapManager = class {
|
|
|
11403
11788
|
tokenProgram: TOKEN_PROGRAM_ADDRESS4
|
|
11404
11789
|
});
|
|
11405
11790
|
const sqrtPriceLimitX64 = params.sqrtPriceLimitX64 || this.mathEngine.calculateSqrtPriceLimit(
|
|
11406
|
-
new
|
|
11791
|
+
new BN8(pool.sqrtPriceX64.toString()),
|
|
11407
11792
|
zeroForOne,
|
|
11408
11793
|
params.slippageTolerance || DEFAULT_SLIPPAGE_TOLERANCE
|
|
11409
11794
|
);
|
|
@@ -11428,7 +11813,7 @@ var SwapManager = class {
|
|
|
11428
11813
|
async getCurrentPrice(poolAddress, options) {
|
|
11429
11814
|
const pool = await this.poolDataManager.getPoolState(poolAddress, options);
|
|
11430
11815
|
return SqrtPriceMath.sqrtPriceX64ToPrice(
|
|
11431
|
-
new
|
|
11816
|
+
new BN8(pool.sqrtPriceX64.toString()),
|
|
11432
11817
|
pool.mintDecimals0,
|
|
11433
11818
|
pool.mintDecimals1
|
|
11434
11819
|
);
|
|
@@ -11640,6 +12025,7 @@ export {
|
|
|
11640
12025
|
PoolManager,
|
|
11641
12026
|
PoolUtils,
|
|
11642
12027
|
PositionManager,
|
|
12028
|
+
PositionUtils,
|
|
11643
12029
|
PriceApiClient,
|
|
11644
12030
|
Q128,
|
|
11645
12031
|
Q64,
|