@picon-finance/dlmm-sdk 1.16.3 → 1.17.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/README.md CHANGED
@@ -98,7 +98,7 @@ const depositIxs = await position.depositByWeight(ownerSigner, {
98
98
 
99
99
  // Later: claim fees, withdraw (partially or fully, by bps), close.
100
100
  const claimFeeIxs = await position.claimFee(ownerSigner);
101
- const withdrawIxs = await position.withdraw(ownerSigner, 5_000); // bps, out of 10_000 = 100%
101
+ const withdrawIxs = await position.withdraw(ownerSigner, { bpsToRemove: 5_000 }); // bps, out of 10_000 = 100%
102
102
  const closeIx = await position.close(ownerSigner);
103
103
  ```
104
104
 
@@ -10,6 +10,7 @@ export declare class BinArray {
10
10
  refresh(data: Account<generated.BinArray>): Promise<void>;
11
11
  refresh(config?: FetchAccountConfig): Promise<void>;
12
12
  static loadAll(rpc: ReturnType<typeof createSolanaRpc>, pool: Address, indices: number[], programAddress: Address, config?: FetchAccountsConfig): Promise<BinArray[]>;
13
+ static loadAllMaybe(rpc: ReturnType<typeof createSolanaRpc>, pool: Address, indices: number[], programAddress: Address, config?: FetchAccountsConfig): Promise<Array<BinArray | undefined>>;
13
14
  delete(authority: TransactionSigner): Promise<Instruction>;
14
15
  }
15
16
  export declare function createBinArray({ payer, pool, index, idempotent, }: {
@@ -33,5 +34,6 @@ export declare function findAllBinArrayAddresses({ pool, indices }: {
33
34
  indices: number[];
34
35
  }, config: ProgramConfig): Promise<Address[]>;
35
36
  export declare function fetchAllBinArraysChunked(rpc: ReturnType<typeof createSolanaRpc>, addresses: Address[], config?: FetchAccountsConfig): Promise<Account<generated.BinArray>[]>;
37
+ export declare function fetchAllMaybeBinArraysChunked(rpc: ReturnType<typeof createSolanaRpc>, addresses: Address[], config?: FetchAccountsConfig): Promise<import("@solana/accounts").MaybeAccount<generated.BinArray>[]>;
36
38
  export declare function decodeAllBinArrays(maybeAccounts: MaybeEncodedAccount<string>[]): Account<generated.BinArray>[];
37
39
  //# sourceMappingURL=bin_array.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"bin_array.d.ts","sourceRoot":"","sources":["../../src/entities/bin_array.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,eAAe,EAEf,KAAK,OAAO,EACZ,KAAK,OAAO,EACZ,KAAK,WAAW,EAChB,KAAK,iBAAiB,EACtB,KAAK,kBAAkB,EACvB,KAAK,mBAAmB,EACxB,KAAK,mBAAmB,EACzB,MAAM,aAAa,CAAC;AAErB,OAAO,EAAE,SAAS,EAAE,MAAM,IAAI,CAAC;AAK/B,OAAO,EAAE,KAAK,aAAa,EAAE,MAAM,SAAS,CAAC;AAE7C,qBAAa,QAAQ;;IAKnB,OAAO,eAQN;IAED,IAAW,OAAO,oBAAoC;IACtD,IAAW,IAAI,uBAAiC;IAChD,IAAW,IAAI,oBAA6B;IAE/B,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,SAAS,CAAC,QAAQ,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1D,OAAO,CAAC,MAAM,CAAC,EAAE,kBAAkB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAQjE,OAAoB,OAAO,CACzB,GAAG,EAAE,UAAU,CAAC,OAAO,eAAe,CAAC,EACvC,IAAI,EAAE,OAAO,EACb,OAAO,EAAE,MAAM,EAAE,EACjB,cAAc,EAAE,OAAO,EACvB,MAAM,CAAC,EAAE,mBAAmB,GAC3B,OAAO,CAAC,QAAQ,EAAE,CAAC,CAOrB;IAEY,MAAM,CAAC,SAAS,EAAE,iBAAiB,GAAG,OAAO,CAAC,WAAW,CAAC,CAUtE;CACF;AAED,wBAAsB,cAAc,CAClC,EACE,KAAK,EACL,IAAI,EACJ,KAAK,EACL,UAAiB,GAClB,EAAE;IACD,KAAK,EAAE,iBAAiB,CAAC;IACzB,IAAI,EAAE,OAAO,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB,EACD,MAAM,EAAE,aAAa,GACpB,OAAO,CAAC,WAAW,CAAC,CAStB;AAED,wBAAsB,kBAAkB,CACtC,EACE,KAAK,EACL,IAAI,EACJ,OAAO,EACP,UAAiB,GAClB,EAAE;IACD,KAAK,EAAE,iBAAiB,CAAC;IACzB,IAAI,EAAE,OAAO,CAAC;IACd,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB,EACD,MAAM,EAAE,aAAa,GACpB,OAAO,CAAC,WAAW,EAAE,CAAC,CAKxB;AAED,wBAAsB,mBAAmB,CACvC,KAAK,EAAE;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,EACvC,MAAM,EAAE,aAAa,GACpB,OAAO,CAAC,OAAO,CAAC,CAGlB;AAED,wBAAsB,wBAAwB,CAC5C,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,MAAM,EAAE,CAAA;CAAE,EACvD,MAAM,EAAE,aAAa,GACpB,OAAO,CAAC,OAAO,EAAE,CAAC,CAIpB;AAED,wBAAsB,wBAAwB,CAC5C,GAAG,EAAE,UAAU,CAAC,OAAO,eAAe,CAAC,EACvC,SAAS,EAAE,OAAO,EAAE,EACpB,MAAM,CAAC,EAAE,mBAAmB,0CAO7B;AAID,wBAAgB,kBAAkB,CAChC,aAAa,EAAE,mBAAmB,CAAC,MAAM,CAAC,EAAE,GAC3C,OAAO,CAAC,SAAS,CAAC,QAAQ,CAAC,EAAE,CAG/B"}
1
+ {"version":3,"file":"bin_array.d.ts","sourceRoot":"","sources":["../../src/entities/bin_array.ts"],"names":[],"mappings":"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"}
@@ -27,6 +27,13 @@ export class BinArray {
27
27
  const accounts = await fetchAllBinArraysChunked(rpc, addresses, config);
28
28
  return accounts.map(account => new BinArray(rpc, account, programAddress));
29
29
  }
30
+ // Same as loadAll, but for a caller-chosen index range that may include bin arrays nobody has
31
+ // created on-chain yet (e.g. a UI-selected price range) — undefined in place of throwing.
32
+ static async loadAllMaybe(rpc, pool, indices, programAddress, config) {
33
+ const addresses = await findAllBinArrayAddresses({ indices, pool }, { programAddress });
34
+ const maybeAccounts = await fetchAllMaybeBinArraysChunked(rpc, addresses, config);
35
+ return maybeAccounts.map(maybeAccount => maybeAccount.exists ? new BinArray(rpc, maybeAccount, programAddress) : undefined);
36
+ }
30
37
  async delete(authority) {
31
38
  const adminConfig = await resolveAdminConfigAddress(this.#programAddress);
32
39
  return await generated.getDeleteBinArrayInstructionAsync({
@@ -56,6 +63,9 @@ export async function findAllBinArrayAddresses({ pool, indices }, config) {
56
63
  export async function fetchAllBinArraysChunked(rpc, addresses, config) {
57
64
  return await fetchInChunks(addresses, MAX_ACCOUNTS_PER_RPC_BATCH, chunkAddresses => generated.fetchAllBinArray(rpc, chunkAddresses, config));
58
65
  }
66
+ export async function fetchAllMaybeBinArraysChunked(rpc, addresses, config) {
67
+ return await fetchInChunks(addresses, MAX_ACCOUNTS_PER_RPC_BATCH, chunkAddresses => generated.fetchAllMaybeBinArray(rpc, chunkAddresses, config));
68
+ }
59
69
  const binArrayDecoder = generated.getBinArrayDecoder();
60
70
  export function decodeAllBinArrays(maybeAccounts) {
61
71
  assertAccountsExist(maybeAccounts);
@@ -3,6 +3,7 @@ import { generated } from "..";
3
3
  import { type FeeRates, type QuoteExactInOutput, type QuoteExactOutOutput, type TransferFee } from "../quote";
4
4
  import { type ExtraAccount, type TransferAccounts as TransferHookAccounts } from "../utils/transfer_hook";
5
5
  import { Position } from "./position";
6
+ import { BinArray } from "./bin_array";
6
7
  import { type ProgramConfig } from "./types";
7
8
  export declare class Pool {
8
9
  #private;
@@ -30,6 +31,7 @@ export declare class Pool {
30
31
  getPosition(address: Address, config?: FetchAccountsConfig): Promise<Position>;
31
32
  getPositionByPositionMint(positionMint: Address, config?: FetchAccountsConfig): Promise<Position>;
32
33
  getAllPositionsByPositionMints(positionMints: Address[], config?: FetchAccountsConfig): Promise<Position[]>;
34
+ getBinArraysForRange(lowerBinId: number, upperBinId: number, config?: FetchAccountsConfig): Promise<Array<BinArray | undefined>>;
33
35
  openPosition(owner: TransactionSigner, positionMint: TransactionSigner, { lowerBinId, upperBinId }: {
34
36
  lowerBinId: number;
35
37
  upperBinId: number;
@@ -1 +1 @@
1
- {"version":3,"file":"pool.d.ts","sourceRoot":"","sources":["../../src/entities/pool.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"pool.d.ts","sourceRoot":"","sources":["../../src/entities/pool.ts"],"names":[],"mappings":"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"}
@@ -5,12 +5,12 @@ import { quoteExactIn, quoteExactOut, getPoolFeeRates, } from "../quote";
5
5
  import { isAccount } from "../utils/type_guards";
6
6
  import { resolveAdminConfigAddress } from "./admin_config";
7
7
  import { processAssociatedTokenAccountPair } from "../utils/ata";
8
- import { getBinArrayIndex, generateBinArrayIndicesForSwap, generateBinArrayIndicesForSync, } from "../utils/bin_location";
8
+ import { getBinArrayIndex, getBinArrayIndexSequence, generateBinArrayIndicesForSwap, generateBinArrayIndicesForSync, } from "../utils/bin_location";
9
9
  import { resolveTransferFee } from "../utils/transfer_fee";
10
- import { resolveTransferHookProgram, resolveTransferHookExtraAccounts, toRemainingAccountMeta, } from "../utils/transfer_hook";
10
+ import { appendRemainingAccounts, buildTransferHookScheme, resolveTransferHookProgram, resolveTransferHookExtraAccounts, } from "../utils/transfer_hook";
11
11
  import { MAX_BIN_ARRAYS_PER_TRAVERSAL } from "../utils/constants";
12
12
  import { Position, openPosition, closePosition, findPositionAddress, findAllPositionAddresses, } from "./position";
13
- import { findAllBinArrayAddresses } from "./bin_array";
13
+ import { BinArray, findAllBinArrayAddresses } from "./bin_array";
14
14
  export class Pool {
15
15
  #programAddress;
16
16
  #rpc;
@@ -64,6 +64,14 @@ export class Pool {
64
64
  const addresses = await findAllPositionAddresses({ positionMints }, { programAddress: this.#programAddress });
65
65
  return await Position.loadAll(this, addresses, config);
66
66
  }
67
+ // Unlike #resolveSwapBinArrays (bounded to a fixed traversal window around the active bin) or
68
+ // a position's own bin arrays (bounded to that position's range), this covers a caller-chosen
69
+ // [lowerBinId, upperBinId] — e.g. a UI-selected price range that may extend past what's
70
+ // already loaded. undefined entries mean no bin array has been created there yet.
71
+ async getBinArraysForRange(lowerBinId, upperBinId, config) {
72
+ const indices = getBinArrayIndexSequence(getBinArrayIndex(lowerBinId), getBinArrayIndex(upperBinId));
73
+ return await BinArray.loadAllMaybe(this.#rpc, this.address, indices, this.#programAddress, config);
74
+ }
67
75
  async openPosition(owner, positionMint, { lowerBinId, upperBinId }) {
68
76
  return await openPosition(this.#rpc, { owner, positionMint, pool: this.address, lowerBinId, upperBinId }, { programAddress: this.#programAddress });
69
77
  }
@@ -139,25 +147,21 @@ export class Pool {
139
147
  this.resolveTransferHookAccountsX(xTransferAccounts),
140
148
  this.resolveTransferHookAccountsY(yTransferAccounts),
141
149
  ]);
150
+ const hookScheme = buildTransferHookScheme(transferHookXAccounts, transferHookYAccounts);
142
151
  const groups = [
143
152
  { kind: generated.AccountKind.BinArray, count: binArrayAddresses.length },
153
+ ...hookScheme.groups,
154
+ ];
155
+ const remainingAccounts = [
156
+ ...binArrayAddresses.map(address => ({ address, role: AccountRole.WRITABLE })),
157
+ ...hookScheme.remainingAccounts,
144
158
  ];
145
- const remainingAccounts = binArrayAddresses.map(address => ({ address, role: AccountRole.WRITABLE }));
146
- if (transferHookXAccounts.length > 0) {
147
- groups.push({ kind: generated.AccountKind.TransferHookX, count: transferHookXAccounts.length });
148
- remainingAccounts.push(...transferHookXAccounts.map(toRemainingAccountMeta));
149
- }
150
- if (transferHookYAccounts.length > 0) {
151
- groups.push({ kind: generated.AccountKind.TransferHookY, count: transferHookYAccounts.length });
152
- remainingAccounts.push(...transferHookYAccounts.map(toRemainingAccountMeta));
153
- }
154
159
  const swapIx = buildSwapIx({
155
160
  tokenUserAccountX,
156
161
  tokenUserAccountY,
157
162
  remainingAccountsScheme: { groups },
158
163
  });
159
- const swapIxWithBinArrays = { ...swapIx, accounts: [...(swapIx.accounts ?? []), ...remainingAccounts] };
160
- return [...preInstructions, swapIxWithBinArrays, ...postInstructions];
164
+ return [...preInstructions, appendRemainingAccounts(swapIx, remainingAccounts), ...postInstructions];
161
165
  }
162
166
  // Public so Position (which only holds a reference to this Pool, not its private mint data)
163
167
  // can resolve hook accounts for its own deposit/withdraw/claimFee instructions without ever
@@ -275,16 +279,7 @@ export class Pool {
275
279
  source: this.tokenVaultY, mint: this.tokenMintY, destination: tokenAdminAccountY, owner: this.address,
276
280
  }),
277
281
  ]);
278
- const groups = [];
279
- const remainingAccounts = [];
280
- if (transferHookXAccounts.length > 0) {
281
- groups.push({ kind: generated.AccountKind.TransferHookX, count: transferHookXAccounts.length });
282
- remainingAccounts.push(...transferHookXAccounts.map(toRemainingAccountMeta));
283
- }
284
- if (transferHookYAccounts.length > 0) {
285
- groups.push({ kind: generated.AccountKind.TransferHookY, count: transferHookYAccounts.length });
286
- remainingAccounts.push(...transferHookYAccounts.map(toRemainingAccountMeta));
287
- }
282
+ const { groups, remainingAccounts } = buildTransferHookScheme(transferHookXAccounts, transferHookYAccounts);
288
283
  const claimProtocolFeeIx = await generated.getClaimProtocolFeeInstructionAsync({
289
284
  authority,
290
285
  pool: this.address,
@@ -299,11 +294,7 @@ export class Pool {
299
294
  tokenProgramX: this.tokenProgramX,
300
295
  tokenProgramY: this.tokenProgramY,
301
296
  }, { programAddress: this.#programAddress });
302
- const claimProtocolFeeIxWithHookAccounts = {
303
- ...claimProtocolFeeIx,
304
- accounts: [...claimProtocolFeeIx.accounts, ...remainingAccounts],
305
- };
306
- return [...preInstructions, claimProtocolFeeIxWithHookAccounts, ...postInstructions];
297
+ return [...preInstructions, appendRemainingAccounts(claimProtocolFeeIx, remainingAccounts), ...postInstructions];
307
298
  }
308
299
  static async load(rpc, address, programAddress, config) {
309
300
  const pool = await generated.fetchPool(rpc, address, config);
@@ -27,8 +27,13 @@ export declare class Position {
27
27
  maxActiveBinSlippage: number;
28
28
  distributionMode: generated.DistributionMode;
29
29
  }): Promise<Instruction[]>;
30
- claimFee(owner: TransactionSigner, range?: BinIdRange): Promise<Instruction[]>;
31
- withdraw(owner: TransactionSigner, bpsToRemove: number, range?: BinIdRange): Promise<Instruction[]>;
30
+ claimFee(owner: TransactionSigner, { range }?: {
31
+ range?: BinIdRange;
32
+ }): Promise<Instruction[]>;
33
+ withdraw(owner: TransactionSigner, { bpsToRemove, range }: {
34
+ bpsToRemove: number;
35
+ range?: BinIdRange;
36
+ }): Promise<Instruction[]>;
32
37
  close(owner: TransactionSigner): Promise<Instruction>;
33
38
  static load(pool: Pool, address: Address, config?: FetchAccountsConfig): Promise<Position>;
34
39
  static loadAll(pool: Pool, addresses: Address[], config?: FetchAccountsConfig): Promise<Position[]>;
@@ -1 +1 @@
1
- {"version":3,"file":"position.d.ts","sourceRoot":"","sources":["../../src/entities/position.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"position.d.ts","sourceRoot":"","sources":["../../src/entities/position.ts"],"names":[],"mappings":"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"}
@@ -2,7 +2,7 @@ import { decodeAccount, assertAccountExists, fetchEncodedAccounts, } from "@sola
2
2
  import { generated } from "..";
3
3
  import { getAmountAfterTransferFee } from "../quote";
4
4
  import { findPositionTokenAccount, processAssociatedTokenAccountPair, } from "../utils/ata";
5
- import { toRemainingAccountMeta } from "../utils/transfer_hook";
5
+ import { appendRemainingAccounts, buildTransferHookScheme, } from "../utils/transfer_hook";
6
6
  import { getBinIndex, getBinArrayIndex, getPositionBinArrayIndices, } from "../utils/bin_location";
7
7
  import { assertRangeWithinRange, assertPositionBelongsToPool, } from "../utils/assert";
8
8
  import { isUndefined } from "../utils/type_guards";
@@ -97,12 +97,14 @@ export class Position {
97
97
  }
98
98
  const [position, binArrays] = arg;
99
99
  this.#account = position;
100
- this.#binArrays.forEach((b, i) => b.refresh(binArrays[i]));
100
+ await Promise.all(this.#binArrays.map((b, i) => b.refresh(binArrays[i])));
101
101
  }
102
- async depositByWeight(owner, { amountX, amountY, lowerBinId, binWeights, maxActiveBinSlippage, distributionMode, }) {
103
- const upperBinId = lowerBinId + binWeights.length - 1;
104
- assertRangeWithinRange({ lowerBinId, upperBinId }, this.data);
105
- const [positionTokenAccount, { ataX: tokenOwnerAccountX, ataY: tokenOwnerAccountY, preInstructions, postInstructions }] = await Promise.all([
102
+ // Shared by depositByWeight/claimFee/withdraw — each needs the owner's position-NFT token
103
+ // account plus its X/Y ATAs (creating either that don't exist yet, funding wSOL if native).
104
+ // amounts is only relevant for a deposit (it decides how much wSOL to wrap); omitted for
105
+ // claim/withdraw, which only ever receive tokens.
106
+ async #prepareOwnerAccounts(owner, amounts) {
107
+ return await Promise.all([
106
108
  findPositionTokenAccount({ owner: owner.address, positionMint: this.data.positionMint }),
107
109
  processAssociatedTokenAccountPair({
108
110
  owner,
@@ -110,10 +112,15 @@ export class Position {
110
112
  tokenMintY: this.#pool.tokenMintY,
111
113
  tokenProgramX: this.#pool.tokenProgramX,
112
114
  tokenProgramY: this.#pool.tokenProgramY,
113
- amountX,
114
- amountY,
115
+ amountX: amounts?.amountX,
116
+ amountY: amounts?.amountY,
115
117
  }),
116
118
  ]);
119
+ }
120
+ async depositByWeight(owner, { amountX, amountY, lowerBinId, binWeights, maxActiveBinSlippage, distributionMode, }) {
121
+ const upperBinId = lowerBinId + binWeights.length - 1;
122
+ assertRangeWithinRange({ lowerBinId, upperBinId }, this.data);
123
+ const [positionTokenAccount, { ataX: tokenOwnerAccountX, ataY: tokenOwnerAccountY, preInstructions, postInstructions }] = await this.#prepareOwnerAccounts(owner, { amountX, amountY });
117
124
  // Both legs move owner -> vault (deposit), signed by owner itself.
118
125
  const [transferHookXAccounts, transferHookYAccounts] = await Promise.all([
119
126
  this.#pool.resolveTransferHookAccountsX({
@@ -123,16 +130,7 @@ export class Position {
123
130
  source: tokenOwnerAccountY, mint: this.#pool.tokenMintY, destination: this.#pool.tokenVaultY, owner: owner.address,
124
131
  }),
125
132
  ]);
126
- const groups = [];
127
- const remainingAccounts = [];
128
- if (transferHookXAccounts.length > 0) {
129
- groups.push({ kind: generated.AccountKind.TransferHookX, count: transferHookXAccounts.length });
130
- remainingAccounts.push(...transferHookXAccounts.map(toRemainingAccountMeta));
131
- }
132
- if (transferHookYAccounts.length > 0) {
133
- groups.push({ kind: generated.AccountKind.TransferHookY, count: transferHookYAccounts.length });
134
- remainingAccounts.push(...transferHookYAccounts.map(toRemainingAccountMeta));
135
- }
133
+ const { groups, remainingAccounts } = buildTransferHookScheme(transferHookXAccounts, transferHookYAccounts);
136
134
  const { lowerBinArray, upperBinArray } = this.binArrayAddresses;
137
135
  const depositByWeightIx = generated.getDepositByWeightInstruction({
138
136
  owner,
@@ -160,11 +158,7 @@ export class Position {
160
158
  }, {
161
159
  programAddress: this.#pool.programAddress
162
160
  });
163
- const depositByWeightIxWithHookAccounts = {
164
- ...depositByWeightIx,
165
- accounts: [...depositByWeightIx.accounts, ...remainingAccounts],
166
- };
167
- return [...preInstructions, depositByWeightIxWithHookAccounts, ...postInstructions];
161
+ return [...preInstructions, appendRemainingAccounts(depositByWeightIx, remainingAccounts), ...postInstructions];
168
162
  }
169
163
  async #buildClaimFeeInstruction(owner, positionTokenAccount, tokenOwnerAccountX, tokenOwnerAccountY, range) {
170
164
  const { lowerBinId, upperBinId } = range ?? this.data;
@@ -178,16 +172,7 @@ export class Position {
178
172
  source: this.#pool.tokenVaultY, mint: this.#pool.tokenMintY, destination: tokenOwnerAccountY, owner: this.#pool.address,
179
173
  }),
180
174
  ]);
181
- const groups = [];
182
- const remainingAccounts = [];
183
- if (transferHookXAccounts.length > 0) {
184
- groups.push({ kind: generated.AccountKind.TransferHookX, count: transferHookXAccounts.length });
185
- remainingAccounts.push(...transferHookXAccounts.map(toRemainingAccountMeta));
186
- }
187
- if (transferHookYAccounts.length > 0) {
188
- groups.push({ kind: generated.AccountKind.TransferHookY, count: transferHookYAccounts.length });
189
- remainingAccounts.push(...transferHookYAccounts.map(toRemainingAccountMeta));
190
- }
175
+ const { groups, remainingAccounts } = buildTransferHookScheme(transferHookXAccounts, transferHookYAccounts);
191
176
  const { lowerBinArray, upperBinArray } = this.binArrayAddresses;
192
177
  const claimFeeIx = generated.getClaimFeeInstruction({
193
178
  owner,
@@ -210,35 +195,17 @@ export class Position {
210
195
  }, {
211
196
  programAddress: this.#pool.programAddress
212
197
  });
213
- return { ...claimFeeIx, accounts: [...claimFeeIx.accounts, ...remainingAccounts] };
198
+ return appendRemainingAccounts(claimFeeIx, remainingAccounts);
214
199
  }
215
- async claimFee(owner, range) {
216
- const [positionTokenAccount, { ataX: tokenOwnerAccountX, ataY: tokenOwnerAccountY, preInstructions, postInstructions }] = await Promise.all([
217
- findPositionTokenAccount({ owner: owner.address, positionMint: this.data.positionMint }),
218
- processAssociatedTokenAccountPair({
219
- owner,
220
- tokenMintX: this.#pool.tokenMintX,
221
- tokenMintY: this.#pool.tokenMintY,
222
- tokenProgramX: this.#pool.tokenProgramX,
223
- tokenProgramY: this.#pool.tokenProgramY,
224
- }),
225
- ]);
200
+ async claimFee(owner, { range } = {}) {
201
+ const [positionTokenAccount, { ataX: tokenOwnerAccountX, ataY: tokenOwnerAccountY, preInstructions, postInstructions }] = await this.#prepareOwnerAccounts(owner);
226
202
  const claimFeeIx = await this.#buildClaimFeeInstruction(owner, positionTokenAccount, tokenOwnerAccountX, tokenOwnerAccountY, range);
227
203
  return [...preInstructions, claimFeeIx, ...postInstructions];
228
204
  }
229
- async withdraw(owner, bpsToRemove, range) {
205
+ async withdraw(owner, { bpsToRemove, range }) {
230
206
  const { lowerBinId, upperBinId } = range ?? this.data;
231
207
  assertRangeWithinRange({ lowerBinId, upperBinId }, this.data);
232
- const [positionTokenAccount, { ataX: tokenOwnerAccountX, ataY: tokenOwnerAccountY, preInstructions, postInstructions }] = await Promise.all([
233
- findPositionTokenAccount({ owner: owner.address, positionMint: this.data.positionMint }),
234
- processAssociatedTokenAccountPair({
235
- owner,
236
- tokenMintX: this.#pool.tokenMintX,
237
- tokenMintY: this.#pool.tokenMintY,
238
- tokenProgramX: this.#pool.tokenProgramX,
239
- tokenProgramY: this.#pool.tokenProgramY,
240
- }),
241
- ]);
208
+ const [positionTokenAccount, { ataX: tokenOwnerAccountX, ataY: tokenOwnerAccountY, preInstructions, postInstructions }] = await this.#prepareOwnerAccounts(owner);
242
209
  // Both legs move vault -> owner (withdraw), signed by the pool PDA.
243
210
  const [transferHookXAccounts, transferHookYAccounts] = await Promise.all([
244
211
  this.#pool.resolveTransferHookAccountsX({
@@ -248,16 +215,7 @@ export class Position {
248
215
  source: this.#pool.tokenVaultY, mint: this.#pool.tokenMintY, destination: tokenOwnerAccountY, owner: this.#pool.address,
249
216
  }),
250
217
  ]);
251
- const groups = [];
252
- const remainingAccounts = [];
253
- if (transferHookXAccounts.length > 0) {
254
- groups.push({ kind: generated.AccountKind.TransferHookX, count: transferHookXAccounts.length });
255
- remainingAccounts.push(...transferHookXAccounts.map(toRemainingAccountMeta));
256
- }
257
- if (transferHookYAccounts.length > 0) {
258
- groups.push({ kind: generated.AccountKind.TransferHookY, count: transferHookYAccounts.length });
259
- remainingAccounts.push(...transferHookYAccounts.map(toRemainingAccountMeta));
260
- }
218
+ const { groups, remainingAccounts } = buildTransferHookScheme(transferHookXAccounts, transferHookYAccounts);
261
219
  const { lowerBinArray, upperBinArray } = this.binArrayAddresses;
262
220
  const withdrawIx = generated.getWithdrawInstruction({
263
221
  owner,
@@ -281,11 +239,7 @@ export class Position {
281
239
  }, {
282
240
  programAddress: this.#pool.programAddress
283
241
  });
284
- const withdrawIxWithHookAccounts = {
285
- ...withdrawIx,
286
- accounts: [...withdrawIx.accounts, ...remainingAccounts],
287
- };
288
- return [...preInstructions, withdrawIxWithHookAccounts, ...postInstructions];
242
+ return [...preInstructions, appendRemainingAccounts(withdrawIx, remainingAccounts), ...postInstructions];
289
243
  }
290
244
  async close(owner) {
291
245
  return await closePosition({ owner, positionMint: this.data.positionMint, position: this.address }, { programAddress: this.#pool.programAddress });
@@ -9,16 +9,6 @@ export declare function findPositionTokenAccount({ owner, positionMint, }: {
9
9
  owner: Address;
10
10
  positionMint: Address;
11
11
  }): Promise<Address>;
12
- export declare function findPoolTokenVaults({ pool, tokenMintX, tokenMintY, tokenProgramX, tokenProgramY, }: {
13
- pool: Address;
14
- tokenMintX: Address;
15
- tokenMintY: Address;
16
- tokenProgramX: Address;
17
- tokenProgramY: Address;
18
- }): Promise<{
19
- tokenVaultX: Address;
20
- tokenVaultY: Address;
21
- }>;
22
12
  interface ProcessAssociatedTokenAccountParams {
23
13
  owner: TransactionSigner;
24
14
  mint: Address;
@@ -1 +1 @@
1
- {"version":3,"file":"ata.d.ts","sourceRoot":"","sources":["../../src/utils/ata.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,OAAO,EACZ,KAAK,WAAW,EAChB,KAAK,iBAAiB,EACvB,MAAM,aAAa,CAAC;AAerB,eAAO,MAAM,mBAAmB,EAAoD,OAAO,CAAC;AAE5F,wBAAsB,oCAAoC,CAAC,MAAM,EAAE;IACjE,KAAK,EAAE,OAAO,CAAC;IACf,IAAI,EAAE,OAAO,CAAC;IACd,YAAY,EAAE,OAAO,CAAC;CACvB,4BAIA;AAID,wBAAgB,wBAAwB,CAAC,EACvC,KAAK,EACL,YAAY,GACb,EAAE;IACD,KAAK,EAAE,OAAO,CAAC;IACf,YAAY,EAAE,OAAO,CAAC;CACvB,GAAG,OAAO,CAAC,OAAO,CAAC,CAEnB;AAID,wBAAsB,mBAAmB,CAAC,EACxC,IAAI,EACJ,UAAU,EACV,UAAU,EACV,aAAa,EACb,aAAa,GACd,EAAE;IACD,IAAI,EAAE,OAAO,CAAC;IACd,UAAU,EAAE,OAAO,CAAC;IACpB,UAAU,EAAE,OAAO,CAAC;IACpB,aAAa,EAAE,OAAO,CAAC;IACvB,aAAa,EAAE,OAAO,CAAC;CACxB,GAAG,OAAO,CAAC;IAAE,WAAW,EAAE,OAAO,CAAC;IAAC,WAAW,EAAE,OAAO,CAAA;CAAE,CAAC,CAM1D;AAED,UAAU,mCAAmC;IAC3C,KAAK,EAAE,iBAAiB,CAAC;IACzB,IAAI,EAAE,OAAO,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,OAAO,CAAC;CACvB;AAED,UAAU,mCAAmC;IAC3C,GAAG,EAAE,OAAO,CAAC;IACb,eAAe,EAAE,WAAW,EAAE,CAAC;IAC/B,gBAAgB,EAAE,WAAW,EAAE,CAAC;CACjC;AAED,wBAAgB,6BAA6B,CAAC,MAAM,EAAE,mCAAmC,GAAG,OAAO,CAAC,mCAAmC,CAAC,CAMvI;AAKD,wBAAsB,iCAAiC,CAAC,EACtD,KAAK,EACL,UAAU,EACV,UAAU,EACV,aAAa,EACb,aAAa,EACb,OAAO,EACP,OAAO,GACR,EAAE;IACD,KAAK,EAAE,iBAAiB,CAAC;IACzB,UAAU,EAAE,OAAO,CAAC;IACpB,UAAU,EAAE,OAAO,CAAC;IACpB,aAAa,EAAE,OAAO,CAAC;IACvB,aAAa,EAAE,OAAO,CAAC;IACvB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB,GAAG,OAAO,CAAC;IACV,IAAI,EAAE,OAAO,CAAC;IACd,IAAI,EAAE,OAAO,CAAC;IACd,eAAe,EAAE,WAAW,EAAE,CAAC;IAC/B,gBAAgB,EAAE,WAAW,EAAE,CAAC;CACjC,CAAC,CAWD"}
1
+ {"version":3,"file":"ata.d.ts","sourceRoot":"","sources":["../../src/utils/ata.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,OAAO,EACZ,KAAK,WAAW,EAChB,KAAK,iBAAiB,EACvB,MAAM,aAAa,CAAC;AAerB,eAAO,MAAM,mBAAmB,EAAoD,OAAO,CAAC;AAE5F,wBAAsB,oCAAoC,CAAC,MAAM,EAAE;IACjE,KAAK,EAAE,OAAO,CAAC;IACf,IAAI,EAAE,OAAO,CAAC;IACd,YAAY,EAAE,OAAO,CAAC;CACvB,4BAIA;AAID,wBAAgB,wBAAwB,CAAC,EACvC,KAAK,EACL,YAAY,GACb,EAAE;IACD,KAAK,EAAE,OAAO,CAAC;IACf,YAAY,EAAE,OAAO,CAAC;CACvB,GAAG,OAAO,CAAC,OAAO,CAAC,CAEnB;AAED,UAAU,mCAAmC;IAC3C,KAAK,EAAE,iBAAiB,CAAC;IACzB,IAAI,EAAE,OAAO,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,OAAO,CAAC;CACvB;AAED,UAAU,mCAAmC;IAC3C,GAAG,EAAE,OAAO,CAAC;IACb,eAAe,EAAE,WAAW,EAAE,CAAC;IAC/B,gBAAgB,EAAE,WAAW,EAAE,CAAC;CACjC;AAED,wBAAgB,6BAA6B,CAAC,MAAM,EAAE,mCAAmC,GAAG,OAAO,CAAC,mCAAmC,CAAC,CAMvI;AAKD,wBAAsB,iCAAiC,CAAC,EACtD,KAAK,EACL,UAAU,EACV,UAAU,EACV,aAAa,EACb,aAAa,EACb,OAAO,EACP,OAAO,GACR,EAAE;IACD,KAAK,EAAE,iBAAiB,CAAC;IACzB,UAAU,EAAE,OAAO,CAAC;IACpB,UAAU,EAAE,OAAO,CAAC;IACpB,aAAa,EAAE,OAAO,CAAC;IACvB,aAAa,EAAE,OAAO,CAAC;IACvB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB,GAAG,OAAO,CAAC;IACV,IAAI,EAAE,OAAO,CAAC;IACd,IAAI,EAAE,OAAO,CAAC;IACd,eAAe,EAAE,WAAW,EAAE,CAAC;IAC/B,gBAAgB,EAAE,WAAW,EAAE,CAAC;CACjC,CAAC,CAWD"}
package/dist/utils/ata.js CHANGED
@@ -12,15 +12,6 @@ export async function findAssociatedTokenPdaByTokenProgram(params) {
12
12
  export function findPositionTokenAccount({ owner, positionMint, }) {
13
13
  return findAssociatedTokenPdaByTokenProgram({ owner, mint: positionMint, tokenProgram: TOKEN_2022_PROGRAM_ADDRESS });
14
14
  }
15
- // A pool's X/Y vaults — shared by every instruction that moves tokens through a pool
16
- // (withdraw, claim_fee, claim_multiple_fees, claim_protocol_fee, deposit_by_weight, swap).
17
- export async function findPoolTokenVaults({ pool, tokenMintX, tokenMintY, tokenProgramX, tokenProgramY, }) {
18
- const [tokenVaultX, tokenVaultY] = await Promise.all([
19
- findAssociatedTokenPdaByTokenProgram({ owner: pool, mint: tokenMintX, tokenProgram: tokenProgramX }),
20
- findAssociatedTokenPdaByTokenProgram({ owner: pool, mint: tokenMintY, tokenProgram: tokenProgramY }),
21
- ]);
22
- return { tokenVaultX, tokenVaultY };
23
- }
24
15
  export function processAssociatedTokenAccount(params) {
25
16
  if (params.mint === NATIVE_MINT_ADDRESS) {
26
17
  return processWsolAssociatedTokenAccount(params);
@@ -1,5 +1,6 @@
1
1
  import { AccountRole, createSolanaRpc, type Address } from "@solana/kit";
2
2
  import type { Mint } from "@solana-program/token-2022";
3
+ import * as generated from "../generated";
3
4
  export declare function resolveTransferHookProgram(mint: Mint): Address | undefined;
4
5
  export declare function getExtraAccountMetaListAddress(mint: Address, hookProgram: Address): Promise<Address>;
5
6
  export type ExtraAccount = {
@@ -18,4 +19,15 @@ export declare function toRemainingAccountMeta({ address, isSigner, isWritable }
18
19
  address: Address;
19
20
  role: AccountRole;
20
21
  };
22
+ export type RemainingAccountMeta = {
23
+ address: Address;
24
+ role: AccountRole;
25
+ };
26
+ export declare function buildTransferHookScheme(transferHookXAccounts: ExtraAccount[], transferHookYAccounts: ExtraAccount[]): {
27
+ groups: generated.AccountGroup[];
28
+ remainingAccounts: RemainingAccountMeta[];
29
+ };
30
+ export declare function appendRemainingAccounts<TIx extends {
31
+ accounts?: readonly unknown[];
32
+ }>(instruction: TIx, remainingAccounts: RemainingAccountMeta[]): TIx;
21
33
  //# sourceMappingURL=transfer_hook.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"transfer_hook.d.ts","sourceRoot":"","sources":["../../src/utils/transfer_hook.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,WAAW,EAIX,eAAe,EAEf,KAAK,OAAO,EACb,MAAM,aAAa,CAAC;AAErB,OAAO,KAAK,EAAE,IAAI,EAAa,MAAM,4BAA4B,CAAC;AA8BlE,wBAAgB,0BAA0B,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,GAAG,SAAS,CAQ1E;AAKD,wBAAsB,8BAA8B,CAClD,IAAI,EAAE,OAAO,EACb,WAAW,EAAE,OAAO,GACnB,OAAO,CAAC,OAAO,CAAC,CAQlB;AA4ED,MAAM,MAAM,YAAY,GAAG;IACzB,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,EAAE,OAAO,CAAC;IAClB,UAAU,EAAE,OAAO,CAAC;CACrB,CAAC;AA+CF,MAAM,MAAM,gBAAgB,GAAG;IAC7B,MAAM,EAAE,OAAO,CAAC;IAChB,IAAI,EAAE,OAAO,CAAC;IACd,WAAW,EAAE,OAAO,CAAC;IACrB,KAAK,EAAE,OAAO,CAAC;CAChB,CAAC;AAWF,wBAAsB,gCAAgC,CACpD,GAAG,EAAE,UAAU,CAAC,OAAO,eAAe,CAAC,EACvC,WAAW,EAAE,OAAO,EACpB,gBAAgB,EAAE,gBAAgB,EAClC,eAAe,GAAE,UAA6B,GAC7C,OAAO,CAAC,YAAY,EAAE,CAAC,CAqEzB;AAGD,wBAAgB,sBAAsB,CAAC,EAAE,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE,EAAE,YAAY;;;EAKrF"}
1
+ {"version":3,"file":"transfer_hook.d.ts","sourceRoot":"","sources":["../../src/utils/transfer_hook.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,WAAW,EAIX,eAAe,EAEf,KAAK,OAAO,EACb,MAAM,aAAa,CAAC;AAErB,OAAO,KAAK,EAAE,IAAI,EAAa,MAAM,4BAA4B,CAAC;AAClE,OAAO,KAAK,SAAS,MAAM,cAAc,CAAC;AA8B1C,wBAAgB,0BAA0B,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,GAAG,SAAS,CAQ1E;AAKD,wBAAsB,8BAA8B,CAClD,IAAI,EAAE,OAAO,EACb,WAAW,EAAE,OAAO,GACnB,OAAO,CAAC,OAAO,CAAC,CAQlB;AA4ED,MAAM,MAAM,YAAY,GAAG;IACzB,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,EAAE,OAAO,CAAC;IAClB,UAAU,EAAE,OAAO,CAAC;CACrB,CAAC;AA+CF,MAAM,MAAM,gBAAgB,GAAG;IAC7B,MAAM,EAAE,OAAO,CAAC;IAChB,IAAI,EAAE,OAAO,CAAC;IACd,WAAW,EAAE,OAAO,CAAC;IACrB,KAAK,EAAE,OAAO,CAAC;CAChB,CAAC;AAWF,wBAAsB,gCAAgC,CACpD,GAAG,EAAE,UAAU,CAAC,OAAO,eAAe,CAAC,EACvC,WAAW,EAAE,OAAO,EACpB,gBAAgB,EAAE,gBAAgB,EAClC,eAAe,GAAE,UAA6B,GAC7C,OAAO,CAAC,YAAY,EAAE,CAAC,CAqEzB;AAGD,wBAAgB,sBAAsB,CAAC,EAAE,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE,EAAE,YAAY;;;EAKrF;AAED,MAAM,MAAM,oBAAoB,GAAG;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,IAAI,EAAE,WAAW,CAAA;CAAE,CAAC;AAO3E,wBAAgB,uBAAuB,CACrC,qBAAqB,EAAE,YAAY,EAAE,EACrC,qBAAqB,EAAE,YAAY,EAAE,GACpC;IAAE,MAAM,EAAE,SAAS,CAAC,YAAY,EAAE,CAAC;IAAC,iBAAiB,EAAE,oBAAoB,EAAE,CAAA;CAAE,CAcjF;AAKD,wBAAgB,uBAAuB,CAAC,GAAG,SAAS;IAAE,QAAQ,CAAC,EAAE,SAAS,OAAO,EAAE,CAAA;CAAE,EACnF,WAAW,EAAE,GAAG,EAChB,iBAAiB,EAAE,oBAAoB,EAAE,GACxC,GAAG,CAEL"}
@@ -1,5 +1,6 @@
1
1
  import { AccountRole, getAddressEncoder, getAddressDecoder, getProgramDerivedAddress, unwrapOption, } from "@solana/kit";
2
2
  import { SYSTEM_PROGRAM_ADDRESS } from "@solana-program/system";
3
+ import * as generated from "../generated";
3
4
  const EXTRA_ACCOUNT_METAS_SEED = "extra-account-metas";
4
5
  // spl-tlv-account-resolution's on-chain byte layout (verified against the actual Rust crate
5
6
  // source, spl-tlv-account-resolution 0.4.0's account.rs/state.rs/seeds.rs — not guessed):
@@ -198,3 +199,27 @@ export function toRemainingAccountMeta({ address, isSigner, isWritable }) {
198
199
  : (isWritable ? AccountRole.WRITABLE : AccountRole.READONLY);
199
200
  return { address, role };
200
201
  }
202
+ // Builds the TransferHookX/TransferHookY groups + their remaining-account metas from a resolved
203
+ // pair of hook accounts — one group per side, omitted entirely when that side has no active hook
204
+ // (mirrors the on-chain program's own RemainingAccountsScheme, which only expects a group to be
205
+ // present when the corresponding mint actually carries a hook). Callers that also need a group
206
+ // ahead of these (e.g. swap's BinArray group) prepend their own groups/accounts to the result.
207
+ export function buildTransferHookScheme(transferHookXAccounts, transferHookYAccounts) {
208
+ const groups = [];
209
+ const remainingAccounts = [];
210
+ if (transferHookXAccounts.length > 0) {
211
+ groups.push({ kind: generated.AccountKind.TransferHookX, count: transferHookXAccounts.length });
212
+ remainingAccounts.push(...transferHookXAccounts.map(toRemainingAccountMeta));
213
+ }
214
+ if (transferHookYAccounts.length > 0) {
215
+ groups.push({ kind: generated.AccountKind.TransferHookY, count: transferHookYAccounts.length });
216
+ remainingAccounts.push(...transferHookYAccounts.map(toRemainingAccountMeta));
217
+ }
218
+ return { groups, remainingAccounts };
219
+ }
220
+ // Appends resolved remaining-account metas (transfer hook accounts, bin arrays, ...) onto an
221
+ // already-built instruction — the one-line splice every instruction builder in entities/ needs
222
+ // after computing its own RemainingAccountsScheme.
223
+ export function appendRemainingAccounts(instruction, remainingAccounts) {
224
+ return { ...instruction, accounts: [...(instruction.accounts ?? []), ...remainingAccounts] };
225
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@picon-finance/dlmm-sdk",
3
- "version": "1.16.3",
3
+ "version": "1.17.0",
4
4
  "description": "Typescript SDK for interacting with the Picon DLMM protocol",
5
5
  "author": "Picon Finance",
6
6
  "license": "MIT",
@@ -52,8 +52,8 @@
52
52
  },
53
53
  "devDependencies": {
54
54
  "@types/bun": "latest",
55
- "@codama/nodes-from-anchor": "^1.5.2",
56
- "@codama/renderers-js": "^2.3.0",
57
- "codama": "^1.9.0"
55
+ "@codama/nodes-from-anchor": "^1.5.3",
56
+ "@codama/renderers-js": "^2.3.1",
57
+ "codama": "^1.10.0"
58
58
  }
59
59
  }
@@ -61,6 +61,24 @@ export class BinArray {
61
61
  return accounts.map(account => new BinArray(rpc, account, programAddress));
62
62
  }
63
63
 
64
+ // Same as loadAll, but for a caller-chosen index range that may include bin arrays nobody has
65
+ // created on-chain yet (e.g. a UI-selected price range) — undefined in place of throwing.
66
+ public static async loadAllMaybe(
67
+ rpc: ReturnType<typeof createSolanaRpc>,
68
+ pool: Address,
69
+ indices: number[],
70
+ programAddress: Address,
71
+ config?: FetchAccountsConfig,
72
+ ): Promise<Array<BinArray | undefined>> {
73
+ const addresses = await findAllBinArrayAddresses(
74
+ { indices, pool },
75
+ { programAddress },
76
+ );
77
+ const maybeAccounts = await fetchAllMaybeBinArraysChunked(rpc, addresses, config);
78
+ return maybeAccounts.map(maybeAccount =>
79
+ maybeAccount.exists ? new BinArray(rpc, maybeAccount, programAddress) : undefined);
80
+ }
81
+
64
82
  public async delete(authority: TransactionSigner): Promise<Instruction> {
65
83
  const adminConfig = await resolveAdminConfigAddress(this.#programAddress);
66
84
  return await generated.getDeleteBinArrayInstructionAsync({
@@ -147,6 +165,18 @@ export async function fetchAllBinArraysChunked(
147
165
  );
148
166
  }
149
167
 
168
+ export async function fetchAllMaybeBinArraysChunked(
169
+ rpc: ReturnType<typeof createSolanaRpc>,
170
+ addresses: Address[],
171
+ config?: FetchAccountsConfig,
172
+ ) {
173
+ return await fetchInChunks(
174
+ addresses,
175
+ MAX_ACCOUNTS_PER_RPC_BATCH,
176
+ chunkAddresses => generated.fetchAllMaybeBinArray(rpc, chunkAddresses, config),
177
+ );
178
+ }
179
+
150
180
  const binArrayDecoder = generated.getBinArrayDecoder();
151
181
 
152
182
  export function decodeAllBinArrays(
@@ -28,14 +28,16 @@ import { resolveAdminConfigAddress } from "./admin_config";
28
28
  import { processAssociatedTokenAccountPair } from "../utils/ata";
29
29
  import {
30
30
  getBinArrayIndex,
31
+ getBinArrayIndexSequence,
31
32
  generateBinArrayIndicesForSwap,
32
33
  generateBinArrayIndicesForSync,
33
34
  } from "../utils/bin_location";
34
35
  import { resolveTransferFee } from "../utils/transfer_fee";
35
36
  import {
37
+ appendRemainingAccounts,
38
+ buildTransferHookScheme,
36
39
  resolveTransferHookProgram,
37
40
  resolveTransferHookExtraAccounts,
38
- toRemainingAccountMeta,
39
41
  type ExtraAccount,
40
42
  type TransferAccounts as TransferHookAccounts,
41
43
  } from "../utils/transfer_hook";
@@ -47,7 +49,7 @@ import {
47
49
  findPositionAddress,
48
50
  findAllPositionAddresses,
49
51
  } from "./position";
50
- import { findAllBinArrayAddresses } from "./bin_array";
52
+ import { BinArray, findAllBinArrayAddresses } from "./bin_array";
51
53
  import { type ProgramConfig } from "./types";
52
54
 
53
55
  export class Pool {
@@ -141,6 +143,22 @@ export class Pool {
141
143
  return await Position.loadAll(this, addresses, config);
142
144
  }
143
145
 
146
+ // Unlike #resolveSwapBinArrays (bounded to a fixed traversal window around the active bin) or
147
+ // a position's own bin arrays (bounded to that position's range), this covers a caller-chosen
148
+ // [lowerBinId, upperBinId] — e.g. a UI-selected price range that may extend past what's
149
+ // already loaded. undefined entries mean no bin array has been created there yet.
150
+ public async getBinArraysForRange(
151
+ lowerBinId: number,
152
+ upperBinId: number,
153
+ config?: FetchAccountsConfig,
154
+ ): Promise<Array<BinArray | undefined>> {
155
+ const indices = getBinArrayIndexSequence(
156
+ getBinArrayIndex(lowerBinId),
157
+ getBinArrayIndex(upperBinId),
158
+ );
159
+ return await BinArray.loadAllMaybe(this.#rpc, this.address, indices, this.#programAddress, config);
160
+ }
161
+
144
162
  public async openPosition(
145
163
  owner: TransactionSigner,
146
164
  positionMint: TransactionSigner,
@@ -294,28 +312,23 @@ export class Pool {
294
312
  this.resolveTransferHookAccountsY(yTransferAccounts),
295
313
  ]);
296
314
 
315
+ const hookScheme = buildTransferHookScheme(transferHookXAccounts, transferHookYAccounts);
297
316
  const groups: generated.AccountGroup[] = [
298
317
  { kind: generated.AccountKind.BinArray, count: binArrayAddresses.length },
318
+ ...hookScheme.groups,
319
+ ];
320
+ const remainingAccounts = [
321
+ ...binArrayAddresses.map(address => ({ address, role: AccountRole.WRITABLE })),
322
+ ...hookScheme.remainingAccounts,
299
323
  ];
300
- const remainingAccounts = binArrayAddresses.map(address => ({ address, role: AccountRole.WRITABLE }));
301
-
302
- if (transferHookXAccounts.length > 0) {
303
- groups.push({ kind: generated.AccountKind.TransferHookX, count: transferHookXAccounts.length });
304
- remainingAccounts.push(...transferHookXAccounts.map(toRemainingAccountMeta));
305
- }
306
- if (transferHookYAccounts.length > 0) {
307
- groups.push({ kind: generated.AccountKind.TransferHookY, count: transferHookYAccounts.length });
308
- remainingAccounts.push(...transferHookYAccounts.map(toRemainingAccountMeta));
309
- }
310
324
 
311
325
  const swapIx = buildSwapIx({
312
326
  tokenUserAccountX,
313
327
  tokenUserAccountY,
314
328
  remainingAccountsScheme: { groups },
315
329
  });
316
- const swapIxWithBinArrays = { ...swapIx, accounts: [...(swapIx.accounts ?? []), ...remainingAccounts] };
317
330
 
318
- return [...preInstructions, swapIxWithBinArrays, ...postInstructions];
331
+ return [...preInstructions, appendRemainingAccounts(swapIx, remainingAccounts), ...postInstructions];
319
332
  }
320
333
 
321
334
  // Public so Position (which only holds a reference to this Pool, not its private mint data)
@@ -531,17 +544,7 @@ export class Pool {
531
544
  }),
532
545
  ]);
533
546
 
534
- const groups: generated.AccountGroup[] = [];
535
- const remainingAccounts: { address: Address, role: AccountRole }[] = [];
536
-
537
- if (transferHookXAccounts.length > 0) {
538
- groups.push({ kind: generated.AccountKind.TransferHookX, count: transferHookXAccounts.length });
539
- remainingAccounts.push(...transferHookXAccounts.map(toRemainingAccountMeta));
540
- }
541
- if (transferHookYAccounts.length > 0) {
542
- groups.push({ kind: generated.AccountKind.TransferHookY, count: transferHookYAccounts.length });
543
- remainingAccounts.push(...transferHookYAccounts.map(toRemainingAccountMeta));
544
- }
547
+ const { groups, remainingAccounts } = buildTransferHookScheme(transferHookXAccounts, transferHookYAccounts);
545
548
 
546
549
  const claimProtocolFeeIx = await generated.getClaimProtocolFeeInstructionAsync({
547
550
  authority,
@@ -558,12 +561,7 @@ export class Pool {
558
561
  tokenProgramY: this.tokenProgramY,
559
562
  }, { programAddress: this.#programAddress });
560
563
 
561
- const claimProtocolFeeIxWithHookAccounts = {
562
- ...claimProtocolFeeIx,
563
- accounts: [...claimProtocolFeeIx.accounts, ...remainingAccounts],
564
- };
565
-
566
- return [...preInstructions, claimProtocolFeeIxWithHookAccounts, ...postInstructions];
564
+ return [...preInstructions, appendRemainingAccounts(claimProtocolFeeIx, remainingAccounts), ...postInstructions];
567
565
  }
568
566
 
569
567
  public static async load(
@@ -3,7 +3,6 @@ import {
3
3
  createSolanaRpc,
4
4
  assertAccountExists,
5
5
  fetchEncodedAccounts,
6
- type AccountRole,
7
6
  type Address,
8
7
  type Account,
9
8
  type Instruction,
@@ -18,7 +17,10 @@ import {
18
17
  findPositionTokenAccount,
19
18
  processAssociatedTokenAccountPair,
20
19
  } from "../utils/ata";
21
- import { toRemainingAccountMeta } from "../utils/transfer_hook";
20
+ import {
21
+ appendRemainingAccounts,
22
+ buildTransferHookScheme,
23
+ } from "../utils/transfer_hook";
22
24
  import {
23
25
  getBinIndex,
24
26
  getBinArrayIndex,
@@ -163,7 +165,29 @@ export class Position {
163
165
  }
164
166
  const [position, binArrays] = arg;
165
167
  this.#account = position;
166
- this.#binArrays.forEach((b, i) => b.refresh(binArrays[i]));
168
+ await Promise.all(this.#binArrays.map((b, i) => b.refresh(binArrays[i])));
169
+ }
170
+
171
+ // Shared by depositByWeight/claimFee/withdraw — each needs the owner's position-NFT token
172
+ // account plus its X/Y ATAs (creating either that don't exist yet, funding wSOL if native).
173
+ // amounts is only relevant for a deposit (it decides how much wSOL to wrap); omitted for
174
+ // claim/withdraw, which only ever receive tokens.
175
+ async #prepareOwnerAccounts(
176
+ owner: TransactionSigner,
177
+ amounts?: { amountX: bigint, amountY: bigint },
178
+ ) {
179
+ return await Promise.all([
180
+ findPositionTokenAccount({ owner: owner.address, positionMint: this.data.positionMint }),
181
+ processAssociatedTokenAccountPair({
182
+ owner,
183
+ tokenMintX: this.#pool.tokenMintX,
184
+ tokenMintY: this.#pool.tokenMintY,
185
+ tokenProgramX: this.#pool.tokenProgramX,
186
+ tokenProgramY: this.#pool.tokenProgramY,
187
+ amountX: amounts?.amountX,
188
+ amountY: amounts?.amountY,
189
+ }),
190
+ ]);
167
191
  }
168
192
 
169
193
  public async depositByWeight(owner: TransactionSigner, {
@@ -189,18 +213,7 @@ export class Position {
189
213
  ataY: tokenOwnerAccountY,
190
214
  preInstructions,
191
215
  postInstructions
192
- }] = await Promise.all([
193
- findPositionTokenAccount({ owner: owner.address, positionMint: this.data.positionMint }),
194
- processAssociatedTokenAccountPair({
195
- owner,
196
- tokenMintX: this.#pool.tokenMintX,
197
- tokenMintY: this.#pool.tokenMintY,
198
- tokenProgramX: this.#pool.tokenProgramX,
199
- tokenProgramY: this.#pool.tokenProgramY,
200
- amountX,
201
- amountY,
202
- }),
203
- ]);
216
+ }] = await this.#prepareOwnerAccounts(owner, { amountX, amountY });
204
217
 
205
218
  // Both legs move owner -> vault (deposit), signed by owner itself.
206
219
  const [transferHookXAccounts, transferHookYAccounts] = await Promise.all([
@@ -212,17 +225,7 @@ export class Position {
212
225
  }),
213
226
  ]);
214
227
 
215
- const groups: generated.AccountGroup[] = [];
216
- const remainingAccounts: { address: Address, role: AccountRole }[] = [];
217
-
218
- if (transferHookXAccounts.length > 0) {
219
- groups.push({ kind: generated.AccountKind.TransferHookX, count: transferHookXAccounts.length });
220
- remainingAccounts.push(...transferHookXAccounts.map(toRemainingAccountMeta));
221
- }
222
- if (transferHookYAccounts.length > 0) {
223
- groups.push({ kind: generated.AccountKind.TransferHookY, count: transferHookYAccounts.length });
224
- remainingAccounts.push(...transferHookYAccounts.map(toRemainingAccountMeta));
225
- }
228
+ const { groups, remainingAccounts } = buildTransferHookScheme(transferHookXAccounts, transferHookYAccounts);
226
229
 
227
230
  const { lowerBinArray, upperBinArray } = this.binArrayAddresses;
228
231
  const depositByWeightIx = generated.getDepositByWeightInstruction({
@@ -252,12 +255,7 @@ export class Position {
252
255
  programAddress: this.#pool.programAddress
253
256
  });
254
257
 
255
- const depositByWeightIxWithHookAccounts = {
256
- ...depositByWeightIx,
257
- accounts: [...depositByWeightIx.accounts, ...remainingAccounts],
258
- };
259
-
260
- return [...preInstructions, depositByWeightIxWithHookAccounts, ...postInstructions];
258
+ return [...preInstructions, appendRemainingAccounts(depositByWeightIx, remainingAccounts), ...postInstructions];
261
259
  }
262
260
 
263
261
  async #buildClaimFeeInstruction(
@@ -280,17 +278,7 @@ export class Position {
280
278
  }),
281
279
  ]);
282
280
 
283
- const groups: generated.AccountGroup[] = [];
284
- const remainingAccounts: { address: Address, role: AccountRole }[] = [];
285
-
286
- if (transferHookXAccounts.length > 0) {
287
- groups.push({ kind: generated.AccountKind.TransferHookX, count: transferHookXAccounts.length });
288
- remainingAccounts.push(...transferHookXAccounts.map(toRemainingAccountMeta));
289
- }
290
- if (transferHookYAccounts.length > 0) {
291
- groups.push({ kind: generated.AccountKind.TransferHookY, count: transferHookYAccounts.length });
292
- remainingAccounts.push(...transferHookYAccounts.map(toRemainingAccountMeta));
293
- }
281
+ const { groups, remainingAccounts } = buildTransferHookScheme(transferHookXAccounts, transferHookYAccounts);
294
282
 
295
283
  const { lowerBinArray, upperBinArray } = this.binArrayAddresses;
296
284
  const claimFeeIx = generated.getClaimFeeInstruction({
@@ -315,28 +303,19 @@ export class Position {
315
303
  programAddress: this.#pool.programAddress
316
304
  });
317
305
 
318
- return { ...claimFeeIx, accounts: [...claimFeeIx.accounts, ...remainingAccounts] };
306
+ return appendRemainingAccounts(claimFeeIx, remainingAccounts);
319
307
  }
320
308
 
321
309
  public async claimFee(
322
310
  owner: TransactionSigner,
323
- range?: BinIdRange,
311
+ { range }: { range?: BinIdRange } = {},
324
312
  ): Promise<Instruction[]> {
325
313
  const [positionTokenAccount, {
326
314
  ataX: tokenOwnerAccountX,
327
315
  ataY: tokenOwnerAccountY,
328
316
  preInstructions,
329
317
  postInstructions
330
- }] = await Promise.all([
331
- findPositionTokenAccount({ owner: owner.address, positionMint: this.data.positionMint }),
332
- processAssociatedTokenAccountPair({
333
- owner,
334
- tokenMintX: this.#pool.tokenMintX,
335
- tokenMintY: this.#pool.tokenMintY,
336
- tokenProgramX: this.#pool.tokenProgramX,
337
- tokenProgramY: this.#pool.tokenProgramY,
338
- }),
339
- ]);
318
+ }] = await this.#prepareOwnerAccounts(owner);
340
319
 
341
320
  const claimFeeIx = await this.#buildClaimFeeInstruction(
342
321
  owner, positionTokenAccount, tokenOwnerAccountX, tokenOwnerAccountY, range,
@@ -347,8 +326,7 @@ export class Position {
347
326
 
348
327
  public async withdraw(
349
328
  owner: TransactionSigner,
350
- bpsToRemove: number,
351
- range?: BinIdRange,
329
+ { bpsToRemove, range }: { bpsToRemove: number, range?: BinIdRange },
352
330
  ): Promise<Instruction[]> {
353
331
  const { lowerBinId, upperBinId } = range ?? this.data;
354
332
  assertRangeWithinRange({ lowerBinId, upperBinId }, this.data);
@@ -358,16 +336,7 @@ export class Position {
358
336
  ataY: tokenOwnerAccountY,
359
337
  preInstructions,
360
338
  postInstructions
361
- }] = await Promise.all([
362
- findPositionTokenAccount({ owner: owner.address, positionMint: this.data.positionMint }),
363
- processAssociatedTokenAccountPair({
364
- owner,
365
- tokenMintX: this.#pool.tokenMintX,
366
- tokenMintY: this.#pool.tokenMintY,
367
- tokenProgramX: this.#pool.tokenProgramX,
368
- tokenProgramY: this.#pool.tokenProgramY,
369
- }),
370
- ]);
339
+ }] = await this.#prepareOwnerAccounts(owner);
371
340
 
372
341
  // Both legs move vault -> owner (withdraw), signed by the pool PDA.
373
342
  const [transferHookXAccounts, transferHookYAccounts] = await Promise.all([
@@ -379,17 +348,7 @@ export class Position {
379
348
  }),
380
349
  ]);
381
350
 
382
- const groups: generated.AccountGroup[] = [];
383
- const remainingAccounts: { address: Address, role: AccountRole }[] = [];
384
-
385
- if (transferHookXAccounts.length > 0) {
386
- groups.push({ kind: generated.AccountKind.TransferHookX, count: transferHookXAccounts.length });
387
- remainingAccounts.push(...transferHookXAccounts.map(toRemainingAccountMeta));
388
- }
389
- if (transferHookYAccounts.length > 0) {
390
- groups.push({ kind: generated.AccountKind.TransferHookY, count: transferHookYAccounts.length });
391
- remainingAccounts.push(...transferHookYAccounts.map(toRemainingAccountMeta));
392
- }
351
+ const { groups, remainingAccounts } = buildTransferHookScheme(transferHookXAccounts, transferHookYAccounts);
393
352
 
394
353
  const { lowerBinArray, upperBinArray } = this.binArrayAddresses;
395
354
  const withdrawIx = generated.getWithdrawInstruction({
@@ -415,12 +374,7 @@ export class Position {
415
374
  programAddress: this.#pool.programAddress
416
375
  });
417
376
 
418
- const withdrawIxWithHookAccounts = {
419
- ...withdrawIx,
420
- accounts: [...withdrawIx.accounts, ...remainingAccounts],
421
- };
422
-
423
- return [...preInstructions, withdrawIxWithHookAccounts, ...postInstructions];
377
+ return [...preInstructions, appendRemainingAccounts(withdrawIx, remainingAccounts), ...postInstructions];
424
378
  }
425
379
 
426
380
  public async close(owner: TransactionSigner): Promise<Instruction> {
package/src/utils/ata.ts CHANGED
@@ -41,28 +41,6 @@ export function findPositionTokenAccount({
41
41
  return findAssociatedTokenPdaByTokenProgram({ owner, mint: positionMint, tokenProgram: TOKEN_2022_PROGRAM_ADDRESS });
42
42
  }
43
43
 
44
- // A pool's X/Y vaults — shared by every instruction that moves tokens through a pool
45
- // (withdraw, claim_fee, claim_multiple_fees, claim_protocol_fee, deposit_by_weight, swap).
46
- export async function findPoolTokenVaults({
47
- pool,
48
- tokenMintX,
49
- tokenMintY,
50
- tokenProgramX,
51
- tokenProgramY,
52
- }: {
53
- pool: Address,
54
- tokenMintX: Address,
55
- tokenMintY: Address,
56
- tokenProgramX: Address,
57
- tokenProgramY: Address,
58
- }): Promise<{ tokenVaultX: Address; tokenVaultY: Address }> {
59
- const [tokenVaultX, tokenVaultY] = await Promise.all([
60
- findAssociatedTokenPdaByTokenProgram({ owner: pool, mint: tokenMintX, tokenProgram: tokenProgramX }),
61
- findAssociatedTokenPdaByTokenProgram({ owner: pool, mint: tokenMintY, tokenProgram: tokenProgramY }),
62
- ]);
63
- return { tokenVaultX, tokenVaultY };
64
- }
65
-
66
44
  interface ProcessAssociatedTokenAccountParams {
67
45
  owner: TransactionSigner,
68
46
  mint: Address,
@@ -9,6 +9,7 @@ import {
9
9
  } from "@solana/kit";
10
10
  import { SYSTEM_PROGRAM_ADDRESS } from "@solana-program/system";
11
11
  import type { Mint, Extension } from "@solana-program/token-2022";
12
+ import * as generated from "../generated";
12
13
 
13
14
  const EXTRA_ACCOUNT_METAS_SEED = "extra-account-metas";
14
15
 
@@ -288,3 +289,39 @@ export function toRemainingAccountMeta({ address, isSigner, isWritable }: ExtraA
288
289
  : (isWritable ? AccountRole.WRITABLE : AccountRole.READONLY);
289
290
  return { address, role };
290
291
  }
292
+
293
+ export type RemainingAccountMeta = { address: Address, role: AccountRole };
294
+
295
+ // Builds the TransferHookX/TransferHookY groups + their remaining-account metas from a resolved
296
+ // pair of hook accounts — one group per side, omitted entirely when that side has no active hook
297
+ // (mirrors the on-chain program's own RemainingAccountsScheme, which only expects a group to be
298
+ // present when the corresponding mint actually carries a hook). Callers that also need a group
299
+ // ahead of these (e.g. swap's BinArray group) prepend their own groups/accounts to the result.
300
+ export function buildTransferHookScheme(
301
+ transferHookXAccounts: ExtraAccount[],
302
+ transferHookYAccounts: ExtraAccount[],
303
+ ): { groups: generated.AccountGroup[], remainingAccounts: RemainingAccountMeta[] } {
304
+ const groups: generated.AccountGroup[] = [];
305
+ const remainingAccounts: RemainingAccountMeta[] = [];
306
+
307
+ if (transferHookXAccounts.length > 0) {
308
+ groups.push({ kind: generated.AccountKind.TransferHookX, count: transferHookXAccounts.length });
309
+ remainingAccounts.push(...transferHookXAccounts.map(toRemainingAccountMeta));
310
+ }
311
+ if (transferHookYAccounts.length > 0) {
312
+ groups.push({ kind: generated.AccountKind.TransferHookY, count: transferHookYAccounts.length });
313
+ remainingAccounts.push(...transferHookYAccounts.map(toRemainingAccountMeta));
314
+ }
315
+
316
+ return { groups, remainingAccounts };
317
+ }
318
+
319
+ // Appends resolved remaining-account metas (transfer hook accounts, bin arrays, ...) onto an
320
+ // already-built instruction — the one-line splice every instruction builder in entities/ needs
321
+ // after computing its own RemainingAccountsScheme.
322
+ export function appendRemainingAccounts<TIx extends { accounts?: readonly unknown[] }>(
323
+ instruction: TIx,
324
+ remainingAccounts: RemainingAccountMeta[],
325
+ ): TIx {
326
+ return { ...instruction, accounts: [...(instruction.accounts ?? []), ...remainingAccounts] };
327
+ }