@stabbleorg/mclmm-sdk 0.3.2 → 0.4.0

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.mjs CHANGED
@@ -5733,6 +5733,7 @@ import Decimal2 from "decimal.js";
5733
5733
  // src/constants.ts
5734
5734
  import BN from "bn.js";
5735
5735
  var STABBLE_CLMM_PROGRAM_ID = "6dMXqGZ3ga2dikrYS9ovDXgHGh5RUsb2RTUj6hrQXhk6";
5736
+ var STABBLE_CLMM_QAS_PROGRAM_ID = "8896VTm3Z3g8PuktiDdW9JLxZP1ww2r5c9Tz5AbaBjAJ";
5736
5737
  var METADATA_PROGRAM_ID = "metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s";
5737
5738
  var SYSTEM_PROGRAM_ID = "11111111111111111111111111111111";
5738
5739
  var SYSVAR_RENT_PROGRAM_ID = "SysvarRent111111111111111111111111111111111";
@@ -5898,6 +5899,22 @@ var TickUtils = class _TickUtils {
5898
5899
  );
5899
5900
  }
5900
5901
  }
5902
+ static getTickOffsetInArray(tickIndex, tickSpacing) {
5903
+ if (tickIndex % tickSpacing != 0) {
5904
+ throw new Error("tickIndex % tickSpacing not equal 0");
5905
+ }
5906
+ const startTickIndex = _TickUtils.getTickArrayStartIndexByTick(
5907
+ tickIndex,
5908
+ tickSpacing
5909
+ );
5910
+ const offsetInArray = Math.floor(
5911
+ (tickIndex - startTickIndex) / tickSpacing
5912
+ );
5913
+ if (offsetInArray < 0 || offsetInArray >= TICK_ARRAY_SIZE) {
5914
+ throw new Error("tick offset in array overflow");
5915
+ }
5916
+ return offsetInArray;
5917
+ }
5901
5918
  /**
5902
5919
  * Get the start index of the tick array containing a specific tick
5903
5920
  * @param tick - Target tick
@@ -6186,11 +6203,12 @@ var PdaUtils = class {
6186
6203
  * @param ammConfig - AMM config address
6187
6204
  * @param tokenMintA - Token A mint address
6188
6205
  * @param tokenMintB - Token B mint address
6206
+ * @param programId - Program address (defaults to production)
6189
6207
  * @returns Pool state PDA
6190
6208
  */
6191
- static async getPoolStatePda(ammConfig, tokenMintA, tokenMintB) {
6209
+ static async getPoolStatePda(ammConfig, tokenMintA, tokenMintB, programId = STABBLE_CLMM_PROGRAM_ID) {
6192
6210
  return await getProgramDerivedAddress10({
6193
- programAddress: STABBLE_CLMM_PROGRAM_ID,
6211
+ programAddress: programId,
6194
6212
  seeds: [
6195
6213
  PDA_SEEDS.POOL_STATE,
6196
6214
  addressEncoder.encode(ammConfig),
@@ -6202,22 +6220,24 @@ var PdaUtils = class {
6202
6220
  /**
6203
6221
  * Derive AMM config PDA
6204
6222
  * @param index - Config index
6223
+ * @param programId - Program address (defaults to production)
6205
6224
  * @returns AMM config PDA
6206
6225
  */
6207
- static async getAmmConfigPda(index) {
6226
+ static async getAmmConfigPda(index, programId = STABBLE_CLMM_PROGRAM_ID) {
6208
6227
  return await getProgramDerivedAddress10({
6209
- programAddress: STABBLE_CLMM_PROGRAM_ID,
6228
+ programAddress: programId,
6210
6229
  seeds: [PDA_SEEDS.AMM_CONFIG, getU16Encoder7().encode(index)]
6211
6230
  });
6212
6231
  }
6213
6232
  /**
6214
6233
  * Derive position state PDA
6215
6234
  * @param nftMint - Position NFT mint address
6235
+ * @param programId - Program address (defaults to production)
6216
6236
  * @returns Position state PDA
6217
6237
  */
6218
- static async getPositionStatePda(nftMint) {
6238
+ static async getPositionStatePda(nftMint, programId = STABBLE_CLMM_PROGRAM_ID) {
6219
6239
  return await getProgramDerivedAddress10({
6220
- programAddress: STABBLE_CLMM_PROGRAM_ID,
6240
+ programAddress: programId,
6221
6241
  seeds: [PDA_SEEDS.POSITION_STATE, addressEncoder.encode(nftMint)]
6222
6242
  });
6223
6243
  }
@@ -6225,11 +6245,12 @@ var PdaUtils = class {
6225
6245
  * Derive tick array state PDA
6226
6246
  * @param poolState - Pool state address
6227
6247
  * @param startTickIndex - Starting tick index of the array
6248
+ * @param programId - Program address (defaults to production)
6228
6249
  * @returns Tick array state PDA
6229
6250
  */
6230
- static async getTickArrayStatePda(poolState, startTickIndex) {
6251
+ static async getTickArrayStatePda(poolState, startTickIndex, programId = STABBLE_CLMM_PROGRAM_ID) {
6231
6252
  return await getProgramDerivedAddress10({
6232
- programAddress: STABBLE_CLMM_PROGRAM_ID,
6253
+ programAddress: programId,
6233
6254
  seeds: [
6234
6255
  PDA_SEEDS.TICK_ARRAY_STATE,
6235
6256
  addressEncoder.encode(poolState),
@@ -6240,32 +6261,34 @@ var PdaUtils = class {
6240
6261
  /**
6241
6262
  * Derive observation state PDA
6242
6263
  * @param poolState - Pool state address
6264
+ * @param programId - Program address (defaults to production)
6243
6265
  * @returns Observation state PDA
6244
6266
  */
6245
- static async getObservationStatePda(poolState) {
6267
+ static async getObservationStatePda(poolState, programId = STABBLE_CLMM_PROGRAM_ID) {
6246
6268
  return await getProgramDerivedAddress10({
6247
- programAddress: STABBLE_CLMM_PROGRAM_ID,
6269
+ programAddress: programId,
6248
6270
  seeds: [PDA_SEEDS.OBSERVATION_STATE, addressEncoder.encode(poolState)]
6249
6271
  });
6250
6272
  }
6251
- static async getPoolVaultIdPda(poolAddress, vaultAddress) {
6273
+ static async getPoolVaultIdPda(poolAddress, vaultAddress, programId = STABBLE_CLMM_PROGRAM_ID) {
6252
6274
  return await getProgramDerivedAddress10({
6253
6275
  seeds: [
6254
6276
  PDA_SEEDS.POOL_VAULT,
6255
6277
  addressEncoder.encode(poolAddress),
6256
6278
  addressEncoder.encode(vaultAddress)
6257
6279
  ],
6258
- programAddress: STABBLE_CLMM_PROGRAM_ID
6280
+ programAddress: programId
6259
6281
  });
6260
6282
  }
6261
6283
  /**
6262
6284
  * Derive tick array bitmap extension PDA
6263
6285
  * @param poolState - Pool state address
6286
+ * @param programId - Program address (defaults to production)
6264
6287
  * @returns Tick array bitmap extension PDA
6265
6288
  */
6266
- static async getTickArrayBitmapExtensionPda(poolState) {
6289
+ static async getTickArrayBitmapExtensionPda(poolState, programId = STABBLE_CLMM_PROGRAM_ID) {
6267
6290
  return await getProgramDerivedAddress10({
6268
- programAddress: STABBLE_CLMM_PROGRAM_ID,
6291
+ programAddress: programId,
6269
6292
  seeds: [PDA_SEEDS.BITMAP_EXTENSION, addressEncoder.encode(poolState)]
6270
6293
  });
6271
6294
  }
@@ -6288,9 +6311,10 @@ var PdaUtils = class {
6288
6311
  * @param tickUpper - Upper tick of range
6289
6312
  * @param tickSpacing - Tick spacing of the pool
6290
6313
  * @param tickCurrent - Current pool tick
6314
+ * @param programId - Program address (defaults to production)
6291
6315
  * @returns Array of tick array PDAs
6292
6316
  */
6293
- static async getTickArrayPdasForRange(poolState, tickLower, tickUpper, tickSpacing, tickCurrent) {
6317
+ static async getTickArrayPdasForRange(poolState, tickLower, tickUpper, tickSpacing, tickCurrent, programId = STABBLE_CLMM_PROGRAM_ID) {
6294
6318
  const startIndexLower = this.getTickArrayStartIndex(tickLower, tickSpacing);
6295
6319
  const startIndexUpper = this.getTickArrayStartIndex(tickUpper, tickSpacing);
6296
6320
  const startIndexCurrent = this.getTickArrayStartIndex(
@@ -6304,7 +6328,7 @@ var PdaUtils = class {
6304
6328
  ]);
6305
6329
  return await Promise.all(
6306
6330
  Array.from(indices).map(
6307
- (index) => this.getTickArrayStatePda(poolState, index)
6331
+ (index) => this.getTickArrayStatePda(poolState, index, programId)
6308
6332
  )
6309
6333
  );
6310
6334
  }
@@ -6313,11 +6337,12 @@ var PdaUtils = class {
6313
6337
  * @param poolState - Pool state address
6314
6338
  * @param tickLowerIndex - Lower tick index
6315
6339
  * @param tickUpperIndex - Upper tick index
6340
+ * @param programId - Program address (defaults to production)
6316
6341
  * @returns Protocol position state PDA
6317
6342
  */
6318
- static async getProtocolPositionStatePda(poolState, tickLowerIndex, tickUpperIndex) {
6343
+ static async getProtocolPositionStatePda(poolState, tickLowerIndex, tickUpperIndex, programId = STABBLE_CLMM_PROGRAM_ID) {
6319
6344
  return await getProgramDerivedAddress10({
6320
- programAddress: STABBLE_CLMM_PROGRAM_ID,
6345
+ programAddress: programId,
6321
6346
  seeds: [
6322
6347
  PDA_SEEDS.POSITION_STATE,
6323
6348
  addressEncoder.encode(poolState),
@@ -6329,11 +6354,12 @@ var PdaUtils = class {
6329
6354
  /**
6330
6355
  * Derive operation state PDA
6331
6356
  * @param poolState - Pool state address
6357
+ * @param programId - Program address (defaults to production)
6332
6358
  * @returns Operation state PDA
6333
6359
  */
6334
- static async getOperationStatePda(poolState) {
6360
+ static async getOperationStatePda(poolState, programId = STABBLE_CLMM_PROGRAM_ID) {
6335
6361
  return await getProgramDerivedAddress10({
6336
- programAddress: STABBLE_CLMM_PROGRAM_ID,
6362
+ programAddress: programId,
6337
6363
  seeds: [PDA_SEEDS.OPERATION, addressEncoder.encode(poolState)]
6338
6364
  });
6339
6365
  }
@@ -6352,7 +6378,7 @@ async function getMetadataPda(mint) {
6352
6378
  // src/utils/tickQuery.ts
6353
6379
  var FETCH_TICKARRAY_COUNT = 15;
6354
6380
  var TickQuery = class _TickQuery {
6355
- static async getTickArrays(rpc, poolId, tickCurrent, tickSpacing, tickArrayBitmapArray, exTickArrayBitmap) {
6381
+ static async getTickArrays(rpc, poolId, tickCurrent, tickSpacing, tickArrayBitmapArray, exTickArrayBitmap, programId = STABBLE_CLMM_PROGRAM_ID) {
6356
6382
  const tickArraysToFetch = [];
6357
6383
  const currentTickArrayStartIndex = TickUtils.getTickArrayStartIndexByTick(
6358
6384
  tickCurrent,
@@ -6368,7 +6394,8 @@ var TickQuery = class _TickQuery {
6368
6394
  for (let i = 0; i < startIndexArray.length; i++) {
6369
6395
  const [tickArrayAddress] = await PdaUtils.getTickArrayStatePda(
6370
6396
  poolId,
6371
- startIndexArray[i]
6397
+ startIndexArray[i],
6398
+ programId
6372
6399
  );
6373
6400
  tickArraysToFetch.push(tickArrayAddress);
6374
6401
  }
@@ -6423,7 +6450,7 @@ var TickQuery = class _TickQuery {
6423
6450
  * @param zeroForOne - Search direction
6424
6451
  * @returns First initialized tick, tick array address, and start index
6425
6452
  */
6426
- static async firstInitializedTickInOneArray(poolId, tickArray, zeroForOne) {
6453
+ static async firstInitializedTickInOneArray(poolId, tickArray, zeroForOne, programId = STABBLE_CLMM_PROGRAM_ID) {
6427
6454
  let nextInitializedTick = void 0;
6428
6455
  if (zeroForOne) {
6429
6456
  for (let i = TICK_ARRAY_SIZE - 1; i >= 0; i--) {
@@ -6444,7 +6471,8 @@ var TickQuery = class _TickQuery {
6444
6471
  }
6445
6472
  const [tickArrayAddress] = await PdaUtils.getTickArrayStatePda(
6446
6473
  poolId,
6447
- tickArray.data.startTickIndex
6474
+ tickArray.data.startTickIndex,
6475
+ programId
6448
6476
  );
6449
6477
  return {
6450
6478
  nextTick: nextInitializedTick,
@@ -6462,7 +6490,7 @@ var TickQuery = class _TickQuery {
6462
6490
  * @param zeroForOne - Search direction
6463
6491
  * @returns Next initialized tick info
6464
6492
  */
6465
- static async nextInitializedTickInOneArray(poolId, tickArrayCache, tickIndex, tickSpacing, zeroForOne) {
6493
+ static async nextInitializedTickInOneArray(poolId, tickArrayCache, tickIndex, tickSpacing, zeroForOne, programId = STABBLE_CLMM_PROGRAM_ID) {
6466
6494
  const startIndex = TickUtils.getTickArrayStartIndexByTick(
6467
6495
  tickIndex,
6468
6496
  tickSpacing
@@ -6501,7 +6529,8 @@ var TickQuery = class _TickQuery {
6501
6529
  }
6502
6530
  const [tickArrayAddress] = await PdaUtils.getTickArrayStatePda(
6503
6531
  poolId,
6504
- startIndex
6532
+ startIndex,
6533
+ programId
6505
6534
  );
6506
6535
  return {
6507
6536
  initializedTick: nextInitializedTick,
@@ -6519,7 +6548,7 @@ var TickQuery = class _TickQuery {
6519
6548
  * @param zeroForOne - Search direction
6520
6549
  * @returns Next initialized tick info
6521
6550
  */
6522
- static async nextInitializedTick(poolId, tickArrayCache, tickIndex, tickSpacing, zeroForOne) {
6551
+ static async nextInitializedTick(poolId, tickArrayCache, tickIndex, tickSpacing, zeroForOne, programId = STABBLE_CLMM_PROGRAM_ID) {
6523
6552
  let {
6524
6553
  initializedTick: nextTick,
6525
6554
  tickArrayAddress,
@@ -6529,7 +6558,8 @@ var TickQuery = class _TickQuery {
6529
6558
  tickArrayCache,
6530
6559
  tickIndex,
6531
6560
  tickSpacing,
6532
- zeroForOne
6561
+ zeroForOne,
6562
+ programId
6533
6563
  );
6534
6564
  while (nextTick === void 0 || nextTick.liquidityGross <= 0n) {
6535
6565
  const nextArrayStartIndex = zeroForOne ? tickArrayStartTickIndex - this.tickCount(tickSpacing) : tickArrayStartTickIndex + this.tickCount(tickSpacing);
@@ -6546,7 +6576,8 @@ var TickQuery = class _TickQuery {
6546
6576
  const result = await this.firstInitializedTickInOneArray(
6547
6577
  poolId,
6548
6578
  cachedTickArray,
6549
- zeroForOne
6579
+ zeroForOne,
6580
+ programId
6550
6581
  );
6551
6582
  nextTick = result.nextTick;
6552
6583
  tickArrayAddress = result.tickArrayAddress;
@@ -8028,6 +8059,268 @@ var PoolUtils = class {
8028
8059
  }
8029
8060
  };
8030
8061
 
8062
+ // src/utils/position.ts
8063
+ import BN5 from "bn.js";
8064
+ var PositionUtils = class _PositionUtils {
8065
+ /**
8066
+ * Calculate fee growth inside a position's tick range.
8067
+ *
8068
+ * formula:
8069
+ * ```
8070
+ * feeGrowthInside = feeGrowthGlobal - feeGrowthBelow - feeGrowthAbove
8071
+ * ```
8072
+ *
8073
+ * Where feeGrowthBelow and feeGrowthAbove depend on the current tick
8074
+ * relative to the position's tick boundaries.
8075
+ *
8076
+ * @param params - Parameters for fee growth calculation
8077
+ * @returns Fee growth inside for both tokens (X64 fixed-point)
8078
+ */
8079
+ static getFeeGrowthInside(params) {
8080
+ const {
8081
+ tickCurrent,
8082
+ tickLower,
8083
+ tickUpper,
8084
+ tickLowerState,
8085
+ tickUpperState,
8086
+ feeGrowthGlobal0X64,
8087
+ feeGrowthGlobal1X64
8088
+ } = params;
8089
+ const feeGrowthGlobal0 = new BN5(feeGrowthGlobal0X64.toString());
8090
+ const feeGrowthGlobal1 = new BN5(feeGrowthGlobal1X64.toString());
8091
+ const tickLowerFeeGrowthOutside0 = new BN5(
8092
+ tickLowerState.feeGrowthOutside0X64.toString()
8093
+ );
8094
+ const tickLowerFeeGrowthOutside1 = new BN5(
8095
+ tickLowerState.feeGrowthOutside1X64.toString()
8096
+ );
8097
+ const tickUpperFeeGrowthOutside0 = new BN5(
8098
+ tickUpperState.feeGrowthOutside0X64.toString()
8099
+ );
8100
+ const tickUpperFeeGrowthOutside1 = new BN5(
8101
+ tickUpperState.feeGrowthOutside1X64.toString()
8102
+ );
8103
+ let feeGrowthBelow0X64;
8104
+ let feeGrowthBelow1X64;
8105
+ if (tickCurrent >= tickLower) {
8106
+ feeGrowthBelow0X64 = tickLowerFeeGrowthOutside0;
8107
+ feeGrowthBelow1X64 = tickLowerFeeGrowthOutside1;
8108
+ } else {
8109
+ feeGrowthBelow0X64 = MathUtils.wrappingSubU128(
8110
+ feeGrowthGlobal0,
8111
+ tickLowerFeeGrowthOutside0
8112
+ );
8113
+ feeGrowthBelow1X64 = MathUtils.wrappingSubU128(
8114
+ feeGrowthGlobal1,
8115
+ tickLowerFeeGrowthOutside1
8116
+ );
8117
+ }
8118
+ let feeGrowthAbove0X64;
8119
+ let feeGrowthAbove1X64;
8120
+ if (tickCurrent < tickUpper) {
8121
+ feeGrowthAbove0X64 = tickUpperFeeGrowthOutside0;
8122
+ feeGrowthAbove1X64 = tickUpperFeeGrowthOutside1;
8123
+ } else {
8124
+ feeGrowthAbove0X64 = MathUtils.wrappingSubU128(
8125
+ feeGrowthGlobal0,
8126
+ tickUpperFeeGrowthOutside0
8127
+ );
8128
+ feeGrowthAbove1X64 = MathUtils.wrappingSubU128(
8129
+ feeGrowthGlobal1,
8130
+ tickUpperFeeGrowthOutside1
8131
+ );
8132
+ }
8133
+ const feeGrowthInside0X64 = MathUtils.wrappingSubU128(
8134
+ MathUtils.wrappingSubU128(feeGrowthGlobal0, feeGrowthBelow0X64),
8135
+ feeGrowthAbove0X64
8136
+ );
8137
+ const feeGrowthInside1X64 = MathUtils.wrappingSubU128(
8138
+ MathUtils.wrappingSubU128(feeGrowthGlobal1, feeGrowthBelow1X64),
8139
+ feeGrowthAbove1X64
8140
+ );
8141
+ return {
8142
+ feeGrowthInside0X64,
8143
+ feeGrowthInside1X64
8144
+ };
8145
+ }
8146
+ /**
8147
+ * Calculate pending fees for a position.
8148
+ *
8149
+ * Formula:
8150
+ * ```
8151
+ * feeDelta = (feeGrowthInside - feeGrowthInsideLast) × liquidity / 2^64
8152
+ * totalFees = tokenFeesOwed + feeDelta
8153
+ * ```
8154
+ *
8155
+ * @param params - Parameters for fee calculation
8156
+ * @returns Pending fees for both tokens in native units
8157
+ */
8158
+ static getPositionFees(params) {
8159
+ const {
8160
+ liquidity,
8161
+ tickLower,
8162
+ tickUpper,
8163
+ feeGrowthInside0LastX64,
8164
+ feeGrowthInside1LastX64,
8165
+ tokenFeesOwed0,
8166
+ tokenFeesOwed1,
8167
+ tickCurrent,
8168
+ feeGrowthGlobal0X64,
8169
+ feeGrowthGlobal1X64,
8170
+ tickLowerState,
8171
+ tickUpperState
8172
+ } = params;
8173
+ const { feeGrowthInside0X64, feeGrowthInside1X64 } = _PositionUtils.getFeeGrowthInside({
8174
+ tickCurrent,
8175
+ tickLower,
8176
+ tickUpper,
8177
+ tickLowerState,
8178
+ tickUpperState,
8179
+ feeGrowthGlobal0X64,
8180
+ feeGrowthGlobal1X64
8181
+ });
8182
+ const liquidityBN = new BN5(liquidity.toString());
8183
+ const feeGrowthInside0Last = new BN5(feeGrowthInside0LastX64.toString());
8184
+ const feeGrowthInside1Last = new BN5(feeGrowthInside1LastX64.toString());
8185
+ const feesOwed0 = new BN5(tokenFeesOwed0.toString());
8186
+ const feesOwed1 = new BN5(tokenFeesOwed1.toString());
8187
+ const feeGrowthDelta0 = MathUtils.wrappingSubU128(
8188
+ feeGrowthInside0X64,
8189
+ feeGrowthInside0Last
8190
+ );
8191
+ const feeGrowthDelta1 = MathUtils.wrappingSubU128(
8192
+ feeGrowthInside1X64,
8193
+ feeGrowthInside1Last
8194
+ );
8195
+ const feeAmount0 = MathUtils.mulDivFloor(feeGrowthDelta0, liquidityBN, Q64);
8196
+ const feeAmount1 = MathUtils.mulDivFloor(feeGrowthDelta1, liquidityBN, Q64);
8197
+ return {
8198
+ tokenFees0: feesOwed0.add(feeAmount0),
8199
+ tokenFees1: feesOwed1.add(feeAmount1)
8200
+ };
8201
+ }
8202
+ /**
8203
+ * Calculate reward growth inside a position's tick range for all reward tokens.
8204
+ *
8205
+ * Formula:
8206
+ * ```
8207
+ * rewardGrowthInside = rewardGrowthGlobal - rewardGrowthBelow - rewardGrowthAbove
8208
+ * ```
8209
+ *
8210
+ * Special cases:
8211
+ * - If tickLower has no liquidity (liquidityGross = 0), rewardGrowthBelow = rewardGrowthGlobal
8212
+ * - If tickUpper has no liquidity (liquidityGross = 0), rewardGrowthAbove = 0
8213
+ *
8214
+ * @param params - Parameters for reward growth calculation
8215
+ * @returns Reward growth inside for each reward token (X64 fixed-point)
8216
+ */
8217
+ static getRewardGrowthInside(params) {
8218
+ const {
8219
+ tickCurrent,
8220
+ tickLower,
8221
+ tickUpper,
8222
+ tickLowerState,
8223
+ tickUpperState,
8224
+ rewardInfos
8225
+ } = params;
8226
+ const rewardGrowthsInside = [];
8227
+ for (let i = 0; i < rewardInfos.length; i++) {
8228
+ const rewardGrowthGlobal = new BN5(
8229
+ rewardInfos[i].rewardGrowthGlobalX64.toString()
8230
+ );
8231
+ let rewardGrowthBelow;
8232
+ if (tickLowerState.liquidityGross === 0n) {
8233
+ rewardGrowthBelow = rewardGrowthGlobal;
8234
+ } else if (tickCurrent < tickLower) {
8235
+ rewardGrowthBelow = rewardGrowthGlobal.sub(
8236
+ new BN5(tickLowerState.rewardGrowthsOutsideX64[i].toString())
8237
+ );
8238
+ } else {
8239
+ rewardGrowthBelow = new BN5(
8240
+ tickLowerState.rewardGrowthsOutsideX64[i].toString()
8241
+ );
8242
+ }
8243
+ let rewardGrowthAbove;
8244
+ if (tickUpperState.liquidityGross === 0n) {
8245
+ rewardGrowthAbove = new BN5(0);
8246
+ } else if (tickCurrent < tickUpper) {
8247
+ rewardGrowthAbove = new BN5(
8248
+ tickUpperState.rewardGrowthsOutsideX64[i].toString()
8249
+ );
8250
+ } else {
8251
+ rewardGrowthAbove = rewardGrowthGlobal.sub(
8252
+ new BN5(tickUpperState.rewardGrowthsOutsideX64[i].toString())
8253
+ );
8254
+ }
8255
+ const rewardGrowthInside = MathUtils.wrappingSubU128(
8256
+ MathUtils.wrappingSubU128(rewardGrowthGlobal, rewardGrowthBelow),
8257
+ rewardGrowthAbove
8258
+ );
8259
+ rewardGrowthsInside.push(rewardGrowthInside);
8260
+ }
8261
+ return rewardGrowthsInside;
8262
+ }
8263
+ /**
8264
+ * Calculate pending rewards for a position.
8265
+ *
8266
+ * Formula:
8267
+ * ```
8268
+ * rewardDelta = (rewardGrowthInside - rewardGrowthInsideLast) × liquidity / 2^64
8269
+ * totalReward = rewardAmountOwed + rewardDelta
8270
+ * ```
8271
+ *
8272
+ * @param params - Parameters for reward calculation
8273
+ * @returns Pending rewards for each reward token in native units
8274
+ */
8275
+ static getPositionRewards(params) {
8276
+ const {
8277
+ liquidity,
8278
+ tickLower,
8279
+ tickUpper,
8280
+ positionRewardInfos,
8281
+ tickCurrent,
8282
+ rewardInfos,
8283
+ tickLowerState,
8284
+ tickUpperState
8285
+ } = params;
8286
+ const rewardGrowthsInside = _PositionUtils.getRewardGrowthInside({
8287
+ tickCurrent,
8288
+ tickLower,
8289
+ tickUpper,
8290
+ tickLowerState,
8291
+ tickUpperState,
8292
+ rewardInfos
8293
+ });
8294
+ const liquidityBN = new BN5(liquidity.toString());
8295
+ const rewards = [];
8296
+ for (let i = 0; i < rewardGrowthsInside.length; i++) {
8297
+ const rewardGrowthInside = rewardGrowthsInside[i];
8298
+ const positionRewardInfo = positionRewardInfos[i];
8299
+ if (!positionRewardInfo) {
8300
+ rewards.push(new BN5(0));
8301
+ continue;
8302
+ }
8303
+ const growthInsideLast = new BN5(
8304
+ positionRewardInfo.growthInsideLastX64.toString()
8305
+ );
8306
+ const rewardAmountOwed = new BN5(
8307
+ positionRewardInfo.rewardAmountOwed.toString()
8308
+ );
8309
+ const rewardGrowthDelta = MathUtils.wrappingSubU128(
8310
+ rewardGrowthInside,
8311
+ growthInsideLast
8312
+ );
8313
+ const rewardAmountDelta = MathUtils.mulDivFloor(
8314
+ rewardGrowthDelta,
8315
+ liquidityBN,
8316
+ Q64
8317
+ );
8318
+ rewards.push(rewardAmountOwed.add(rewardAmountDelta));
8319
+ }
8320
+ return { rewards };
8321
+ }
8322
+ };
8323
+
8031
8324
  // src/utils/index.ts
8032
8325
  import {
8033
8326
  getAddressEncoder as getAddressEncoder29
@@ -8127,7 +8420,9 @@ function getApisFromEndpoint(rpc) {
8127
8420
  var Clmm = class {
8128
8421
  constructor(config) {
8129
8422
  this.config = config;
8423
+ this.programId = config.programAddress ?? STABBLE_CLMM_PROGRAM_ID;
8130
8424
  }
8425
+ programId;
8131
8426
  /**
8132
8427
  * Create a new AMM configuration
8133
8428
  * @param params - Configuration parameters
@@ -8142,7 +8437,7 @@ var Clmm = class {
8142
8437
  protocolFeeRate,
8143
8438
  fundFeeRate
8144
8439
  } = params;
8145
- const ammConfigPda = await PdaUtils.getAmmConfigPda(index);
8440
+ const ammConfigPda = await PdaUtils.getAmmConfigPda(index, this.programId);
8146
8441
  const instruction = await getCreateAmmConfigInstructionAsync({
8147
8442
  owner,
8148
8443
  ammConfig: ammConfigPda[0],
@@ -8197,7 +8492,7 @@ import {
8197
8492
  address as address3
8198
8493
  } from "@solana/kit";
8199
8494
  import { TOKEN_PROGRAM_ADDRESS as TOKEN_PROGRAM_ADDRESS2 } from "@solana-program/token";
8200
- import BN5 from "bn.js";
8495
+ import BN6 from "bn.js";
8201
8496
  import Decimal3 from "decimal.js";
8202
8497
 
8203
8498
  // src/utils/token.ts
@@ -8221,7 +8516,9 @@ async function getToken(rpc, tokenAccount) {
8221
8516
  var PoolManager = class {
8222
8517
  constructor(config) {
8223
8518
  this.config = config;
8519
+ this.programId = config.programAddress ?? STABBLE_CLMM_PROGRAM_ID;
8224
8520
  }
8521
+ programId;
8225
8522
  /**
8226
8523
  * Make create pool instructions
8227
8524
  * @param params - Pool creation parameters
@@ -8239,8 +8536,8 @@ var PoolManager = class {
8239
8536
  } = params;
8240
8537
  const addressA = address3(tokenMintA);
8241
8538
  const addressB = address3(tokenMintB);
8242
- const isAFirst = new BN5(Buffer.from(addressB)).gt(
8243
- new BN5(Buffer.from(addressA))
8539
+ const isAFirst = new BN6(Buffer.from(addressB)).gt(
8540
+ new BN6(Buffer.from(addressA))
8244
8541
  );
8245
8542
  const [token0, token1, decimals0, decimals1, priceAdjusted] = isAFirst ? [tokenMintA, tokenMintB, mintADecimals, mintBDecimals, initialPrice] : [
8246
8543
  tokenMintB,
@@ -8257,12 +8554,13 @@ var PoolManager = class {
8257
8554
  const [poolPda] = await PdaUtils.getPoolStatePda(
8258
8555
  ammConfigId,
8259
8556
  token0,
8260
- token1
8557
+ token1,
8558
+ this.programId
8261
8559
  );
8262
- const [observationPda] = await PdaUtils.getObservationStatePda(poolPda);
8263
- const [tickArrayBitmapPda] = await PdaUtils.getTickArrayBitmapExtensionPda(poolPda);
8264
- const [tokenVault0] = await PdaUtils.getPoolVaultIdPda(poolPda, token0);
8265
- const [tokenVault1] = await PdaUtils.getPoolVaultIdPda(poolPda, token1);
8560
+ const [observationPda] = await PdaUtils.getObservationStatePda(poolPda, this.programId);
8561
+ const [tickArrayBitmapPda] = await PdaUtils.getTickArrayBitmapExtensionPda(poolPda, this.programId);
8562
+ const [tokenVault0] = await PdaUtils.getPoolVaultIdPda(poolPda, token0, this.programId);
8563
+ const [tokenVault1] = await PdaUtils.getPoolVaultIdPda(poolPda, token1, this.programId);
8266
8564
  const instruction = await getCreatePoolInstructionAsync({
8267
8565
  poolCreator: owner,
8268
8566
  ammConfig: ammConfigId,
@@ -8307,7 +8605,7 @@ var PoolManager = class {
8307
8605
  protocolFeeRate,
8308
8606
  fundFeeRate
8309
8607
  } = params;
8310
- const ammConfigPda = await PdaUtils.getAmmConfigPda(index);
8608
+ const ammConfigPda = await PdaUtils.getAmmConfigPda(index, this.programId);
8311
8609
  const instruction = getCreateAmmConfigInstruction({
8312
8610
  owner,
8313
8611
  ammConfig: ammConfigPda[0],
@@ -8359,17 +8657,18 @@ var PoolManager = class {
8359
8657
  * @returns Pool information if found
8360
8658
  */
8361
8659
  async getPoolByTokenPairAndConfig(tokenA, tokenB, ammConfigIndex = 0) {
8362
- const ammConfigPda = await PdaUtils.getAmmConfigPda(ammConfigIndex);
8660
+ const ammConfigPda = await PdaUtils.getAmmConfigPda(ammConfigIndex, this.programId);
8363
8661
  const poolPda = await PdaUtils.getPoolStatePda(
8364
8662
  ammConfigPda[0],
8365
8663
  tokenA,
8366
- tokenB
8664
+ tokenB,
8665
+ this.programId
8367
8666
  );
8368
8667
  return this.getPool(poolPda[0]);
8369
8668
  }
8370
8669
  async getAllPools(rpc) {
8371
8670
  try {
8372
- let accounts = await rpc.getProgramAccounts(STABBLE_CLMM_PROGRAM_ID, {
8671
+ let accounts = await rpc.getProgramAccounts(this.programId, {
8373
8672
  commitment: "finalized",
8374
8673
  encoding: "base64",
8375
8674
  filters: [
@@ -8422,7 +8721,7 @@ var PoolManager = class {
8422
8721
  getToken(this.config.rpc, poolState.tokenVault1)
8423
8722
  ]);
8424
8723
  const currentPrice = this.calculatePoolPrice(
8425
- new BN5(poolState.sqrtPriceX64.toString()),
8724
+ new BN6(poolState.sqrtPriceX64.toString()),
8426
8725
  tokenA.decimals,
8427
8726
  tokenB.decimals
8428
8727
  );
@@ -8479,7 +8778,7 @@ import {
8479
8778
  getTransferCheckedInstruction
8480
8779
  } from "@solana-program/token";
8481
8780
  import { TOKEN_2022_PROGRAM_ADDRESS as TOKEN_2022_PROGRAM_ADDRESS2 } from "@solana-program/token-2022";
8482
- import BN6 from "bn.js";
8781
+ import BN7 from "bn.js";
8483
8782
 
8484
8783
  // ../node_modules/@solana/spl-token/lib/esm/constants.js
8485
8784
  import { PublicKey } from "@solana/web3.js";
@@ -8499,7 +8798,9 @@ var TOKEN_ACCOUNT_SIZE = 165n;
8499
8798
  var PositionManager = class {
8500
8799
  constructor(config) {
8501
8800
  this.config = config;
8801
+ this.programId = config.programAddress ?? STABBLE_CLMM_PROGRAM_ID;
8502
8802
  }
8803
+ programId;
8503
8804
  buildWrapSolInstructions(params) {
8504
8805
  const { payer, ata, owner, amount } = params;
8505
8806
  return [
@@ -8601,7 +8902,8 @@ var PositionManager = class {
8601
8902
  poolAccount.data.tickSpacing
8602
8903
  );
8603
8904
  const [positionStatePda] = await PdaUtils.getPositionStatePda(
8604
- nftMintAccount.address
8905
+ nftMintAccount.address,
8906
+ this.programId
8605
8907
  );
8606
8908
  const [metadataPda] = await getMetadataPda(nftMintAccount.address);
8607
8909
  const [positionNftAccountPda] = await findAssociatedTokenPda({
@@ -8612,7 +8914,8 @@ var PositionManager = class {
8612
8914
  const [protocolPositionPda] = await PdaUtils.getProtocolPositionStatePda(
8613
8915
  poolAccount.address,
8614
8916
  tickLower,
8615
- tickUpper
8917
+ tickUpper,
8918
+ this.programId
8616
8919
  );
8617
8920
  const instruction = await getOpenPositionWithToken22NftInstructionAsync({
8618
8921
  payer: ownerInfo.feePayer,
@@ -8687,14 +8990,17 @@ var PositionManager = class {
8687
8990
  );
8688
8991
  const [tickArrayLower] = await PdaUtils.getTickArrayStatePda(
8689
8992
  poolAccount.address,
8690
- tickArrayLowerStartIndex
8993
+ tickArrayLowerStartIndex,
8994
+ this.programId
8691
8995
  );
8692
8996
  const [tickArrayUpper] = await PdaUtils.getTickArrayStatePda(
8693
8997
  poolAccount.address,
8694
- tickArrayUpperStartIndex
8998
+ tickArrayUpperStartIndex,
8999
+ this.programId
8695
9000
  );
8696
9001
  const [positionStatePda] = await PdaUtils.getPositionStatePda(
8697
- nftMintAccount.address
9002
+ nftMintAccount.address,
9003
+ this.programId
8698
9004
  );
8699
9005
  const [metadataPda] = await getMetadataPda(nftMintAccount.address);
8700
9006
  const [positionNftAccountPda] = await findAssociatedTokenPda({
@@ -8705,7 +9011,8 @@ var PositionManager = class {
8705
9011
  const [protocolPositionPda] = await PdaUtils.getProtocolPositionStatePda(
8706
9012
  poolAccount.address,
8707
9013
  tickLower,
8708
- tickUpper
9014
+ tickUpper,
9015
+ this.programId
8709
9016
  );
8710
9017
  const amount0Max = base === "MintA" ? baseAmount : otherAmountMax;
8711
9018
  const amount1Max = base === "MintA" ? otherAmountMax : baseAmount;
@@ -8713,7 +9020,7 @@ var PositionManager = class {
8713
9020
  poolAccount.data.tickSpacing,
8714
9021
  [tickArrayLowerStartIndex, tickArrayUpperStartIndex]
8715
9022
  );
8716
- const extBitmapAccount = isOverflow ? await PdaUtils.getTickArrayBitmapExtensionPda(poolAccount.address) : void 0;
9023
+ const extBitmapAccount = isOverflow ? await PdaUtils.getTickArrayBitmapExtensionPda(poolAccount.address, this.programId) : void 0;
8717
9024
  const remAccounts = extBitmapAccount ? [{ address: extBitmapAccount[0], role: AccountRole2.WRITABLE }] : [];
8718
9025
  let wrapSolInstructions = [];
8719
9026
  if (poolAccount.data.tokenMint0.toString() === NATIVE_MINT.toString()) {
@@ -8789,7 +9096,8 @@ var PositionManager = class {
8789
9096
  amountMaxB
8790
9097
  } = params;
8791
9098
  const [personalPosition] = await PdaUtils.getPositionStatePda(
8792
- ownerPosition.nftMint
9099
+ ownerPosition.nftMint,
9100
+ this.programId
8793
9101
  );
8794
9102
  const [positionNftAccount] = await findAssociatedTokenPda({
8795
9103
  mint: ownerPosition.nftMint,
@@ -8799,27 +9107,30 @@ var PositionManager = class {
8799
9107
  const [protocolPositionPda] = await PdaUtils.getProtocolPositionStatePda(
8800
9108
  poolState.address,
8801
9109
  ownerPosition.tickLowerIndex,
8802
- ownerPosition.tickUpperIndex
9110
+ ownerPosition.tickUpperIndex,
9111
+ this.programId
8803
9112
  );
8804
9113
  const [tickArrayLower] = await PdaUtils.getTickArrayStatePda(
8805
9114
  poolState.address,
8806
9115
  PdaUtils.getTickArrayStartIndex(
8807
9116
  ownerPosition.tickLowerIndex,
8808
9117
  poolState.data.tickSpacing
8809
- )
9118
+ ),
9119
+ this.programId
8810
9120
  );
8811
9121
  const [tickArrayUpper] = await PdaUtils.getTickArrayStatePda(
8812
9122
  poolState.address,
8813
9123
  PdaUtils.getTickArrayStartIndex(
8814
9124
  ownerPosition.tickUpperIndex,
8815
9125
  poolState.data.tickSpacing
8816
- )
9126
+ ),
9127
+ this.programId
8817
9128
  );
8818
9129
  const isOverflow = PoolUtils.isOverflowDefaultTickArrayBitmap(
8819
9130
  poolState.data.tickSpacing,
8820
9131
  [ownerPosition.tickLowerIndex, ownerPosition.tickUpperIndex]
8821
9132
  );
8822
- const extBitmapAccount = isOverflow ? await PdaUtils.getTickArrayBitmapExtensionPda(poolState.address) : void 0;
9133
+ const extBitmapAccount = isOverflow ? await PdaUtils.getTickArrayBitmapExtensionPda(poolState.address, this.programId) : void 0;
8823
9134
  const remAccounts = extBitmapAccount ? [{ address: extBitmapAccount[0], role: AccountRole2.WRITABLE }] : [];
8824
9135
  const instruction = getIncreaseLiquidityV2Instruction({
8825
9136
  nftOwner: ownerInfo.wallet,
@@ -8877,7 +9188,8 @@ var PositionManager = class {
8877
9188
  } = params;
8878
9189
  const signers = [];
8879
9190
  const [personalPosition] = await PdaUtils.getPositionStatePda(
8880
- ownerPosition.nftMint
9191
+ ownerPosition.nftMint,
9192
+ this.programId
8881
9193
  );
8882
9194
  const [positionNftAccount] = await findAssociatedTokenPda({
8883
9195
  mint: ownerPosition.nftMint,
@@ -8887,27 +9199,30 @@ var PositionManager = class {
8887
9199
  const [protocolPositionPda] = await PdaUtils.getProtocolPositionStatePda(
8888
9200
  poolState.address,
8889
9201
  ownerPosition.tickLowerIndex,
8890
- ownerPosition.tickUpperIndex
9202
+ ownerPosition.tickUpperIndex,
9203
+ this.programId
8891
9204
  );
8892
9205
  const [tickArrayLower] = await PdaUtils.getTickArrayStatePda(
8893
9206
  poolState.address,
8894
9207
  PdaUtils.getTickArrayStartIndex(
8895
9208
  ownerPosition.tickLowerIndex,
8896
9209
  poolState.data.tickSpacing
8897
- )
9210
+ ),
9211
+ this.programId
8898
9212
  );
8899
9213
  const [tickArrayUpper] = await PdaUtils.getTickArrayStatePda(
8900
9214
  poolState.address,
8901
9215
  PdaUtils.getTickArrayStartIndex(
8902
9216
  ownerPosition.tickUpperIndex,
8903
9217
  poolState.data.tickSpacing
8904
- )
9218
+ ),
9219
+ this.programId
8905
9220
  );
8906
9221
  const isOverflow = PoolUtils.isOverflowDefaultTickArrayBitmap(
8907
9222
  poolState.data.tickSpacing,
8908
9223
  [ownerPosition.tickLowerIndex, ownerPosition.tickUpperIndex]
8909
9224
  );
8910
- const extBitmapAccount = isOverflow ? await PdaUtils.getTickArrayBitmapExtensionPda(poolState.address) : void 0;
9225
+ const extBitmapAccount = isOverflow ? await PdaUtils.getTickArrayBitmapExtensionPda(poolState.address, this.programId) : void 0;
8911
9226
  const remAccounts = extBitmapAccount ? [{ address: extBitmapAccount[0], role: AccountRole2.WRITABLE }] : [];
8912
9227
  const createRecipientAta0Ix = getCreateAssociatedTokenIdempotentInstruction(
8913
9228
  {
@@ -8990,7 +9305,8 @@ var PositionManager = class {
8990
9305
  async makeClosePositionInstructions(params) {
8991
9306
  const { ownerPosition, ownerInfo } = params;
8992
9307
  const [personalPosition] = await PdaUtils.getPositionStatePda(
8993
- ownerPosition.nftMint
9308
+ ownerPosition.nftMint,
9309
+ this.programId
8994
9310
  );
8995
9311
  const [positionNftAccount] = await findAssociatedTokenPda({
8996
9312
  mint: ownerPosition.nftMint,
@@ -9019,7 +9335,7 @@ var PositionManager = class {
9019
9335
  */
9020
9336
  async getPosition(positionMint) {
9021
9337
  try {
9022
- const positionStatePda = await PdaUtils.getPositionStatePda(positionMint);
9338
+ const positionStatePda = await PdaUtils.getPositionStatePda(positionMint, this.programId);
9023
9339
  const positionState = await fetchMaybePersonalPositionState(
9024
9340
  this.config.rpc,
9025
9341
  positionStatePda[0],
@@ -9040,21 +9356,22 @@ var PositionManager = class {
9040
9356
  * Enrich position state with computed fields from pool data
9041
9357
  * @param position - Raw position state from blockchain
9042
9358
  * @param pool - Pool state from blockchain
9359
+ * @param fees - Position fees
9043
9360
  * @returns Enriched position info with calculated amounts and prices
9044
9361
  */
9045
- enrichPositionInfo(position, pool) {
9362
+ enrichPositionInfo(position, pool, fees, rewards) {
9046
9363
  const sqrtPriceLowerX64 = SqrtPriceMath.getSqrtPriceX64FromTick(
9047
9364
  position.tickLowerIndex
9048
9365
  );
9049
9366
  const sqrtPriceUpperX64 = SqrtPriceMath.getSqrtPriceX64FromTick(
9050
9367
  position.tickUpperIndex
9051
9368
  );
9052
- const sqrtPriceCurrentX64 = new BN6(pool.sqrtPriceX64.toString());
9369
+ const sqrtPriceCurrentX64 = new BN7(pool.sqrtPriceX64.toString());
9053
9370
  const { amountA, amountB } = LiquidityMath.getAmountsFromLiquidity(
9054
9371
  sqrtPriceCurrentX64,
9055
9372
  sqrtPriceLowerX64,
9056
9373
  sqrtPriceUpperX64,
9057
- new BN6(position.liquidity.toString()),
9374
+ new BN7(position.liquidity.toString()),
9058
9375
  true
9059
9376
  );
9060
9377
  const [_amountA, _amountB] = [
@@ -9075,9 +9392,13 @@ var PositionManager = class {
9075
9392
  });
9076
9393
  const inRange = pool.tickCurrent >= position.tickLowerIndex && pool.tickCurrent < position.tickUpperIndex;
9077
9394
  const unclaimedFees = {
9078
- token0: new BN6(position.tokenFeesOwed0.toString()),
9079
- token1: new BN6(position.tokenFeesOwed1.toString())
9395
+ token0: fees?.tokenFees0 ? fees?.tokenFees0 : new BN7(0),
9396
+ token1: fees?.tokenFees1 ? fees?.tokenFees1 : new BN7(0)
9080
9397
  };
9398
+ const unclaimedRewards = rewards?.rewards ? rewards.rewards.map((amount, i) => ({
9399
+ mint: pool.rewardInfos[i]?.tokenMint,
9400
+ amount
9401
+ })).filter((r) => r.mint !== void 0) : void 0;
9081
9402
  const ageSeconds = 0;
9082
9403
  return {
9083
9404
  ...position,
@@ -9090,18 +9411,16 @@ var PositionManager = class {
9090
9411
  inRange,
9091
9412
  ageSeconds,
9092
9413
  unclaimedFees,
9093
- // valueUsd is optional and requires external price feeds
9094
- valueUsd: void 0,
9095
- // unclaimedRewards is optional
9096
- unclaimedRewards: void 0
9414
+ unclaimedRewards,
9415
+ valueUsd: void 0
9097
9416
  };
9098
9417
  }
9099
9418
  /**
9100
- * Get all positions for a wallet
9419
+ * Get raw positions for a wallet (without pool-state enrichment)
9101
9420
  * @param wallet - Wallet address
9102
- * @returns Array of positions owned by the wallet
9421
+ * @returns Array of raw position states owned by the wallet
9103
9422
  */
9104
- async getPositionsForWallet(wallet) {
9423
+ async getRawPositionsForWallet(wallet) {
9105
9424
  try {
9106
9425
  const response22 = await this.config.rpc.getTokenAccountsByOwner(
9107
9426
  wallet,
@@ -9118,29 +9437,7 @@ var PositionManager = class {
9118
9437
  (ta) => this.getPosition(ta.account.data.parsed.info.mint)
9119
9438
  )
9120
9439
  );
9121
- const validPositions = positions.filter(
9122
- (p) => !!p
9123
- );
9124
- const enrichedPositions = await Promise.all(
9125
- validPositions.map(async (position) => {
9126
- try {
9127
- const poolAccount = await fetchMaybePoolState(
9128
- this.config.rpc,
9129
- position.poolId,
9130
- { commitment: this.config.commitment }
9131
- );
9132
- if (!poolAccount.exists) {
9133
- console.warn(`Pool ${position.poolId} not found for position`);
9134
- return null;
9135
- }
9136
- return this.enrichPositionInfo(position, poolAccount.data);
9137
- } catch (error) {
9138
- console.error(`Failed to enrich position: ${error}`);
9139
- return null;
9140
- }
9141
- })
9142
- );
9143
- return enrichedPositions.filter((p) => !!p);
9440
+ return positions.filter((p) => !!p);
9144
9441
  } catch (error) {
9145
9442
  throw new ClmmError(
9146
9443
  "POSITION_NOT_FOUND" /* POSITION_NOT_FOUND */,
@@ -9148,6 +9445,146 @@ var PositionManager = class {
9148
9445
  );
9149
9446
  }
9150
9447
  }
9448
+ /**
9449
+ * Get all positions for a wallet, enriched with pool data
9450
+ * @param wallet - Wallet address
9451
+ * @returns Structured result with enriched positions and any failures
9452
+ */
9453
+ async getPositionsForWallet(wallet) {
9454
+ const rawPositions = await this.getRawPositionsForWallet(wallet);
9455
+ const results = await Promise.allSettled(
9456
+ rawPositions.map(async (position) => {
9457
+ const poolAccount = await fetchMaybePoolState(
9458
+ this.config.rpc,
9459
+ position.poolId,
9460
+ { commitment: this.config.commitment }
9461
+ );
9462
+ if (!poolAccount.exists) {
9463
+ throw new Error(`Pool ${position.poolId} not found for position`);
9464
+ }
9465
+ const { fees, rewards } = await this.getPositionFeeAndRewards(
9466
+ position,
9467
+ poolAccount.data
9468
+ );
9469
+ return this.enrichPositionInfo(
9470
+ position,
9471
+ poolAccount.data,
9472
+ fees,
9473
+ rewards
9474
+ );
9475
+ })
9476
+ );
9477
+ const positions = [];
9478
+ const failed = [];
9479
+ results.forEach((result, index) => {
9480
+ if (result.status === "fulfilled") {
9481
+ positions.push(result.value);
9482
+ } else {
9483
+ failed.push({
9484
+ position: rawPositions[index],
9485
+ error: result.reason instanceof Error ? result.reason : new Error(String(result.reason))
9486
+ });
9487
+ }
9488
+ });
9489
+ return { positions, failed };
9490
+ }
9491
+ /**
9492
+ * Calculate pending fees and rewards for a position.
9493
+ *
9494
+ * @param position - Personal position state
9495
+ * @param pool - Pool state
9496
+ * @returns Pending fees for both tokens and rewards for each reward token (up to 3)
9497
+ */
9498
+ async getPositionFeeAndRewards(position, pool) {
9499
+ const [tickArrayLower] = await PdaUtils.getTickArrayStatePda(
9500
+ position.poolId,
9501
+ PdaUtils.getTickArrayStartIndex(
9502
+ position.tickLowerIndex,
9503
+ pool.tickSpacing
9504
+ ),
9505
+ this.programId
9506
+ );
9507
+ const [tickArrayUpper] = await PdaUtils.getTickArrayStatePda(
9508
+ position.poolId,
9509
+ PdaUtils.getTickArrayStartIndex(
9510
+ position.tickUpperIndex,
9511
+ pool.tickSpacing
9512
+ ),
9513
+ this.programId
9514
+ );
9515
+ const tickArrayLowerAccount = await fetchMaybeTickArrayState(
9516
+ this.config.rpc,
9517
+ tickArrayLower
9518
+ );
9519
+ const tickArrayUpperAccount = await fetchMaybeTickArrayState(
9520
+ this.config.rpc,
9521
+ tickArrayUpper
9522
+ );
9523
+ if (!tickArrayLowerAccount.exists || !tickArrayUpperAccount.exists) {
9524
+ console.log(
9525
+ "[getPositionFeeAndRewards] tick array state accounts do not exist."
9526
+ );
9527
+ return {
9528
+ fees: { tokenFees0: new BN7(0), tokenFees1: new BN7(0) },
9529
+ rewards: { rewards: [new BN7(0), new BN7(0), new BN7(0)] }
9530
+ };
9531
+ }
9532
+ const tickSpacing = pool.tickSpacing;
9533
+ const tickArrayLowerIndex = TickUtils.getTickOffsetInArray(
9534
+ position.tickLowerIndex,
9535
+ tickSpacing
9536
+ );
9537
+ const tickArrayUpperIndex = TickUtils.getTickOffsetInArray(
9538
+ position.tickUpperIndex,
9539
+ tickSpacing
9540
+ );
9541
+ if (tickArrayLowerIndex < 0 || tickArrayUpperIndex < 0) {
9542
+ console.log("[getPositionFeeAndRewards] tick array indexes < 0.");
9543
+ return {
9544
+ fees: { tokenFees0: new BN7(0), tokenFees1: new BN7(0) },
9545
+ rewards: { rewards: [new BN7(0), new BN7(0), new BN7(0)] }
9546
+ };
9547
+ }
9548
+ const lowerTickState = tickArrayLowerAccount.data.ticks[tickArrayLowerIndex];
9549
+ const upperTickState = tickArrayUpperAccount.data.ticks[tickArrayUpperIndex];
9550
+ const fees = PositionUtils.getPositionFees({
9551
+ liquidity: position.liquidity,
9552
+ tickLower: position.tickLowerIndex,
9553
+ tickUpper: position.tickUpperIndex,
9554
+ feeGrowthInside0LastX64: position.feeGrowthInside0LastX64,
9555
+ feeGrowthInside1LastX64: position.feeGrowthInside1LastX64,
9556
+ tokenFeesOwed0: position.tokenFeesOwed0,
9557
+ tokenFeesOwed1: position.tokenFeesOwed1,
9558
+ tickCurrent: pool.tickCurrent,
9559
+ feeGrowthGlobal0X64: pool.feeGrowthGlobal0X64,
9560
+ feeGrowthGlobal1X64: pool.feeGrowthGlobal1X64,
9561
+ tickLowerState: {
9562
+ feeGrowthOutside0X64: lowerTickState.feeGrowthOutside0X64,
9563
+ feeGrowthOutside1X64: lowerTickState.feeGrowthOutside1X64
9564
+ },
9565
+ tickUpperState: {
9566
+ feeGrowthOutside0X64: upperTickState.feeGrowthOutside0X64,
9567
+ feeGrowthOutside1X64: upperTickState.feeGrowthOutside1X64
9568
+ }
9569
+ });
9570
+ const rewards = PositionUtils.getPositionRewards({
9571
+ liquidity: position.liquidity,
9572
+ tickLower: position.tickLowerIndex,
9573
+ tickUpper: position.tickUpperIndex,
9574
+ positionRewardInfos: position.rewardInfos,
9575
+ tickCurrent: pool.tickCurrent,
9576
+ rewardInfos: pool.rewardInfos,
9577
+ tickLowerState: {
9578
+ liquidityGross: lowerTickState.liquidityGross,
9579
+ rewardGrowthsOutsideX64: lowerTickState.rewardGrowthsOutsideX64
9580
+ },
9581
+ tickUpperState: {
9582
+ liquidityGross: upperTickState.liquidityGross,
9583
+ rewardGrowthsOutsideX64: upperTickState.rewardGrowthsOutsideX64
9584
+ }
9585
+ });
9586
+ return { fees, rewards };
9587
+ }
9151
9588
  };
9152
9589
 
9153
9590
  // src/api/config.ts
@@ -10122,7 +10559,7 @@ var PriceApiClient = class _PriceApiClient {
10122
10559
  };
10123
10560
 
10124
10561
  // src/swap.ts
10125
- import BN7 from "bn.js";
10562
+ import BN8 from "bn.js";
10126
10563
  import Decimal6 from "decimal.js";
10127
10564
  var DEFAULT_RETRY_CONFIG = {
10128
10565
  maxRetries: 3,
@@ -10182,7 +10619,7 @@ var SwapMathEngine = class {
10182
10619
  slippageTolerance,
10183
10620
  poolAddress
10184
10621
  } = params;
10185
- if (amountIn.lte(new BN7(0))) {
10622
+ if (amountIn.lte(new BN8(0))) {
10186
10623
  throw new ClmmError(
10187
10624
  "SWAP_AMOUNT_CANNOT_BE_ZERO" /* SWAP_AMOUNT_CANNOT_BE_ZERO */,
10188
10625
  "Swap amount must be greater than zero"
@@ -10194,8 +10631,8 @@ var SwapMathEngine = class {
10194
10631
  `Slippage tolerance must be between 0 and 1, got ${slippageTolerance}`
10195
10632
  );
10196
10633
  }
10197
- const liquidity = new BN7(pool.liquidity.toString());
10198
- const sqrtPriceCurrentX64 = new BN7(pool.sqrtPriceX64.toString());
10634
+ const liquidity = new BN8(pool.liquidity.toString());
10635
+ const sqrtPriceCurrentX64 = new BN8(pool.sqrtPriceX64.toString());
10199
10636
  const feeRate = ammConfig.tradeFeeRate;
10200
10637
  const sqrtPriceTargetX64 = this.calculateSqrtPriceLimit(
10201
10638
  sqrtPriceCurrentX64,
@@ -10223,9 +10660,9 @@ var SwapMathEngine = class {
10223
10660
  pool.mintDecimals1
10224
10661
  );
10225
10662
  const priceImpact = priceAfter.minus(priceBefore).div(priceBefore).abs().toNumber();
10226
- const slippageBN = new BN7(Math.floor(slippageTolerance * 1e4));
10663
+ const slippageBN = new BN8(Math.floor(slippageTolerance * 1e4));
10227
10664
  const minAmountOut = step.amountOut.sub(
10228
- step.amountOut.mul(slippageBN).div(new BN7(1e4))
10665
+ step.amountOut.mul(slippageBN).div(new BN8(1e4))
10229
10666
  );
10230
10667
  return {
10231
10668
  amountIn,
@@ -10272,17 +10709,17 @@ var SwapMathEngine = class {
10272
10709
  tickArrayCache
10273
10710
  } = params;
10274
10711
  const sqrtPriceLimitX64 = this.calculateSqrtPriceLimit(
10275
- new BN7(pool.sqrtPriceX64.toString()),
10712
+ new BN8(pool.sqrtPriceX64.toString()),
10276
10713
  zeroForOne,
10277
10714
  slippageTolerance
10278
10715
  );
10279
10716
  const result = await SwapMath.swapCompute({
10280
10717
  poolState: {
10281
- sqrtPriceX64: new BN7(pool.sqrtPriceX64.toString()),
10718
+ sqrtPriceX64: new BN8(pool.sqrtPriceX64.toString()),
10282
10719
  tickCurrent: pool.tickCurrent,
10283
- liquidity: new BN7(pool.liquidity.toString()),
10284
- feeGrowthGlobal0X64: new BN7(pool.feeGrowthGlobal0X64.toString()),
10285
- feeGrowthGlobal1X64: new BN7(pool.feeGrowthGlobal1X64.toString())
10720
+ liquidity: new BN8(pool.liquidity.toString()),
10721
+ feeGrowthGlobal0X64: new BN8(pool.feeGrowthGlobal0X64.toString()),
10722
+ feeGrowthGlobal1X64: new BN8(pool.feeGrowthGlobal1X64.toString())
10286
10723
  },
10287
10724
  tickArrayCache,
10288
10725
  tickSpacing: pool.tickSpacing,
@@ -10299,7 +10736,7 @@ var SwapMathEngine = class {
10299
10736
  poolId: poolAddress
10300
10737
  });
10301
10738
  const priceBefore = SqrtPriceMath.sqrtPriceX64ToPrice(
10302
- new BN7(pool.sqrtPriceX64.toString()),
10739
+ new BN8(pool.sqrtPriceX64.toString()),
10303
10740
  pool.mintDecimals0,
10304
10741
  pool.mintDecimals1
10305
10742
  );
@@ -10309,9 +10746,9 @@ var SwapMathEngine = class {
10309
10746
  pool.mintDecimals1
10310
10747
  );
10311
10748
  const priceImpact = priceAfter.minus(priceBefore).div(priceBefore).abs().toNumber();
10312
- const slippageBN = new BN7(Math.floor(slippageTolerance * 1e4));
10749
+ const slippageBN = new BN8(Math.floor(slippageTolerance * 1e4));
10313
10750
  const minAmountOut = result.amountOut.sub(
10314
- result.amountOut.mul(slippageBN).div(new BN7(1e4))
10751
+ result.amountOut.mul(slippageBN).div(new BN8(1e4))
10315
10752
  );
10316
10753
  const feeImpact = ammConfig.tradeFeeRate / FEE_RATE_DENOMINATOR_NUMBER;
10317
10754
  return {
@@ -10355,10 +10792,10 @@ var SwapMathEngine = class {
10355
10792
  */
10356
10793
  calculateSqrtPriceLimit(sqrtPriceX64, zeroForOne, slippageTolerance) {
10357
10794
  const bps = Math.floor(slippageTolerance * 1e4);
10358
- const base = new BN7(1e4);
10359
- const scaled = zeroForOne ? sqrtPriceX64.mul(base.sub(new BN7(bps))).div(base) : sqrtPriceX64.mul(base.add(new BN7(bps))).div(base);
10360
- const min2 = new BN7(MIN_SQRT_PRICE_X64);
10361
- const max = new BN7(MAX_SQRT_PRICE_X64);
10795
+ const base = new BN8(1e4);
10796
+ const scaled = zeroForOne ? sqrtPriceX64.mul(base.sub(new BN8(bps))).div(base) : sqrtPriceX64.mul(base.add(new BN8(bps))).div(base);
10797
+ const min2 = new BN8(MIN_SQRT_PRICE_X64);
10798
+ const max = new BN8(MAX_SQRT_PRICE_X64);
10362
10799
  if (scaled.lt(min2)) return min2;
10363
10800
  if (scaled.gt(max)) return max;
10364
10801
  return scaled;
@@ -10451,6 +10888,7 @@ var SwapManager = class {
10451
10888
  constructor(config, managerConfig) {
10452
10889
  this.config = config;
10453
10890
  this.managerConfig = managerConfig;
10891
+ this.programId = config.programAddress ?? STABBLE_CLMM_PROGRAM_ID;
10454
10892
  this.poolDataManager = new PoolDataManager(config, {
10455
10893
  cacheTTL: 2e3,
10456
10894
  immutability: "freeze",
@@ -10471,6 +10909,7 @@ var SwapManager = class {
10471
10909
  );
10472
10910
  }
10473
10911
  }
10912
+ programId;
10474
10913
  poolDataManager;
10475
10914
  mathEngine;
10476
10915
  priceApiClient;
@@ -10782,7 +11221,7 @@ var SwapManager = class {
10782
11221
  `Invalid slippage tolerance: ${slippageTolerance}. Must be between 0 and 1.`
10783
11222
  );
10784
11223
  }
10785
- if (amountIn.lte(new BN7(0))) {
11224
+ if (amountIn.lte(new BN8(0))) {
10786
11225
  throw new ClmmError(
10787
11226
  "SWAP_AMOUNT_CANNOT_BE_ZERO" /* SWAP_AMOUNT_CANNOT_BE_ZERO */,
10788
11227
  "Swap amount must be greater than zero."
@@ -10884,7 +11323,7 @@ var SwapManager = class {
10884
11323
  `Invalid slippage tolerance: ${slippageTolerance}. Must be between 0 and 1.`
10885
11324
  );
10886
11325
  }
10887
- if (amountIn.lte(new BN7(0))) {
11326
+ if (amountIn.lte(new BN8(0))) {
10888
11327
  throw new ClmmError(
10889
11328
  "SWAP_AMOUNT_CANNOT_BE_ZERO" /* SWAP_AMOUNT_CANNOT_BE_ZERO */,
10890
11329
  "Swap amount must be greater than zero."
@@ -10938,7 +11377,7 @@ var SwapManager = class {
10938
11377
  poolAddress
10939
11378
  });
10940
11379
  const priceBefore = SqrtPriceMath.sqrtPriceX64ToPrice(
10941
- new BN7(pool.sqrtPriceX64.toString()),
11380
+ new BN8(pool.sqrtPriceX64.toString()),
10942
11381
  pool.mintDecimals0,
10943
11382
  pool.mintDecimals1
10944
11383
  );
@@ -11048,21 +11487,21 @@ var SwapManager = class {
11048
11487
  * @returns Number of tick arrays to fetch (includes safety buffer)
11049
11488
  */
11050
11489
  estimateTickArrayCount(pool, amountIn) {
11051
- const liquidity = new BN7(pool.liquidity.toString());
11052
- if (liquidity.eq(new BN7(0)) || liquidity.lt(new BN7(1e3))) {
11490
+ const liquidity = new BN8(pool.liquidity.toString());
11491
+ if (liquidity.eq(new BN8(0)) || liquidity.lt(new BN8(1e3))) {
11053
11492
  return 10;
11054
11493
  }
11055
- const roughImpact = amountIn.mul(new BN7(1e3)).div(liquidity);
11494
+ const roughImpact = amountIn.mul(new BN8(1e3)).div(liquidity);
11056
11495
  let baseArrays;
11057
- if (roughImpact.lte(new BN7(1))) {
11496
+ if (roughImpact.lte(new BN8(1))) {
11058
11497
  baseArrays = 2;
11059
- } else if (roughImpact.lte(new BN7(10))) {
11498
+ } else if (roughImpact.lte(new BN8(10))) {
11060
11499
  baseArrays = 4;
11061
- } else if (roughImpact.lte(new BN7(50))) {
11500
+ } else if (roughImpact.lte(new BN8(50))) {
11062
11501
  baseArrays = 6;
11063
- } else if (roughImpact.lte(new BN7(100))) {
11502
+ } else if (roughImpact.lte(new BN8(100))) {
11064
11503
  baseArrays = 8;
11065
- } else if (roughImpact.lte(new BN7(200))) {
11504
+ } else if (roughImpact.lte(new BN8(200))) {
11066
11505
  baseArrays = 12;
11067
11506
  } else {
11068
11507
  baseArrays = 15;
@@ -11081,7 +11520,8 @@ var SwapManager = class {
11081
11520
  const startIndex = currentStartIndex + offset * TICKS_PER_ARRAY * tickSpacing;
11082
11521
  const [tickArrayPda] = await PdaUtils.getTickArrayStatePda(
11083
11522
  poolAddress,
11084
- startIndex
11523
+ startIndex,
11524
+ this.programId
11085
11525
  );
11086
11526
  tickArrayAddresses.push(tickArrayPda);
11087
11527
  }
@@ -11107,7 +11547,7 @@ var SwapManager = class {
11107
11547
  const decOut = zeroForOne ? pool.mintDecimals1 : pool.mintDecimals0;
11108
11548
  const promises = params.amounts.map(async (amountIn) => {
11109
11549
  try {
11110
- if (amountIn.lte(new BN7(0))) return;
11550
+ if (amountIn.lte(new BN8(0))) return;
11111
11551
  const quote = await this.mathEngine.calculateSimpleSwap({
11112
11552
  pool,
11113
11553
  ammConfig,
@@ -11149,7 +11589,7 @@ var SwapManager = class {
11149
11589
  options
11150
11590
  );
11151
11591
  const priceBefore = SqrtPriceMath.sqrtPriceX64ToPrice(
11152
- new BN7(pool.sqrtPriceX64.toString()),
11592
+ new BN8(pool.sqrtPriceX64.toString()),
11153
11593
  pool.mintDecimals0,
11154
11594
  pool.mintDecimals1
11155
11595
  );
@@ -11272,13 +11712,13 @@ var SwapManager = class {
11272
11712
  `Extremely high price impact: ${(quote.priceImpact * 100).toFixed(2)}%. Swap likely to fail or result in significant slippage.`
11273
11713
  );
11274
11714
  }
11275
- const oneUnit = new BN7(10).pow(new BN7(outputDecimals));
11715
+ const oneUnit = new BN8(10).pow(new BN8(outputDecimals));
11276
11716
  if (quote.amountOut.lt(oneUnit)) {
11277
11717
  warnings.push(
11278
11718
  `Output amount is less than 1 unit of the output token. Consider increasing input amount or slippage tolerance.`
11279
11719
  );
11280
11720
  }
11281
- if (quote.amountOut.lte(new BN7(0))) {
11721
+ if (quote.amountOut.lte(new BN8(0))) {
11282
11722
  errors.push(
11283
11723
  "Swap would result in zero output. Amount may be too small."
11284
11724
  );
@@ -11299,11 +11739,11 @@ var SwapManager = class {
11299
11739
  return {
11300
11740
  quote: {
11301
11741
  amountIn: params.amountIn,
11302
- amountOut: new BN7(0),
11303
- minAmountOut: new BN7(0),
11742
+ amountOut: new BN8(0),
11743
+ minAmountOut: new BN8(0),
11304
11744
  priceImpact: 0,
11305
11745
  route: [],
11306
- fee: new BN7(0)
11746
+ fee: new BN8(0)
11307
11747
  },
11308
11748
  willSucceed: false,
11309
11749
  errors,
@@ -11391,7 +11831,7 @@ var SwapManager = class {
11391
11831
  }
11392
11832
  const pool = await this.poolDataManager.getPoolState(poolAddress, options);
11393
11833
  const zeroForOne = params.tokenIn === pool.tokenMint0;
11394
- const [observationState] = await PdaUtils.getObservationStatePda(poolAddress);
11834
+ const [observationState] = await PdaUtils.getObservationStatePda(poolAddress, this.programId);
11395
11835
  const [inputTokenAccount] = await findAssociatedTokenPda2({
11396
11836
  mint: params.tokenIn,
11397
11837
  owner: payer.address,
@@ -11403,7 +11843,7 @@ var SwapManager = class {
11403
11843
  tokenProgram: TOKEN_PROGRAM_ADDRESS4
11404
11844
  });
11405
11845
  const sqrtPriceLimitX64 = params.sqrtPriceLimitX64 || this.mathEngine.calculateSqrtPriceLimit(
11406
- new BN7(pool.sqrtPriceX64.toString()),
11846
+ new BN8(pool.sqrtPriceX64.toString()),
11407
11847
  zeroForOne,
11408
11848
  params.slippageTolerance || DEFAULT_SLIPPAGE_TOLERANCE
11409
11849
  );
@@ -11428,7 +11868,7 @@ var SwapManager = class {
11428
11868
  async getCurrentPrice(poolAddress, options) {
11429
11869
  const pool = await this.poolDataManager.getPoolState(poolAddress, options);
11430
11870
  return SqrtPriceMath.sqrtPriceX64ToPrice(
11431
- new BN7(pool.sqrtPriceX64.toString()),
11871
+ new BN8(pool.sqrtPriceX64.toString()),
11432
11872
  pool.mintDecimals0,
11433
11873
  pool.mintDecimals1
11434
11874
  );
@@ -11640,11 +12080,13 @@ export {
11640
12080
  PoolManager,
11641
12081
  PoolUtils,
11642
12082
  PositionManager,
12083
+ PositionUtils,
11643
12084
  PriceApiClient,
11644
12085
  Q128,
11645
12086
  Q64,
11646
12087
  SLIPPAGE_CALC,
11647
12088
  STABBLE_CLMM_PROGRAM_ID,
12089
+ STABBLE_CLMM_QAS_PROGRAM_ID,
11648
12090
  SYSTEM_PROGRAM_ID,
11649
12091
  SYSVAR_RENT_PROGRAM_ID,
11650
12092
  SqrtPriceMath,