@t2000/sdk 1.10.1 → 1.11.1

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/dist/index.d.cts CHANGED
@@ -271,8 +271,9 @@ declare function addSendToTx(tx: Transaction, coin: TransactionObjectArgument, r
271
271
  * given coin type.
272
272
  *
273
273
  * Replaces three earlier inline implementations:
274
- * - `cetus-swap.ts:fetchAllCoinsForSwap` (kept for backwards-compat,
275
- * delegates here)
274
+ * - `cetus-swap.ts:fetchAllCoinsForSwap` (deleted 2026-05-02 — addSwapToTx
275
+ * now calls `selectAndSplitCoin` directly so the merge cache is shared
276
+ * across every appender in a multi-write bundle)
276
277
  * - `volo.ts:fetchCoinsByType` (kept for backwards-compat, delegates
277
278
  * here)
278
279
  * - audric host's `transactions/prepare/route.ts:fetchCoinsForSwap`
@@ -281,7 +282,10 @@ declare function addSendToTx(tx: Transaction, coin: TransactionObjectArgument, r
281
282
  * Single source of truth for the "fetch coins of type X owned by
282
283
  * address Y, paginated" pattern. P2.2b extracts this so `composeTx`'s
283
284
  * registry adapters can build a wallet-mode `TransactionObjectArgument`
284
- * uniformly across save / send / repay / etc.
285
+ * uniformly across save / send / repay / etc. The 2026-05-02 P2.7 fix
286
+ * adds a per-PTB merge cache so multi-write bundles that touch the same
287
+ * coin type twice (swap+save, save+send, etc.) only emit `mergeCoins`
288
+ * once — see `ptbMergeCache` JSDoc below.
285
289
  */
286
290
 
287
291
  interface CoinPage {
package/dist/index.d.ts CHANGED
@@ -271,8 +271,9 @@ declare function addSendToTx(tx: Transaction, coin: TransactionObjectArgument, r
271
271
  * given coin type.
272
272
  *
273
273
  * Replaces three earlier inline implementations:
274
- * - `cetus-swap.ts:fetchAllCoinsForSwap` (kept for backwards-compat,
275
- * delegates here)
274
+ * - `cetus-swap.ts:fetchAllCoinsForSwap` (deleted 2026-05-02 — addSwapToTx
275
+ * now calls `selectAndSplitCoin` directly so the merge cache is shared
276
+ * across every appender in a multi-write bundle)
276
277
  * - `volo.ts:fetchCoinsByType` (kept for backwards-compat, delegates
277
278
  * here)
278
279
  * - audric host's `transactions/prepare/route.ts:fetchCoinsForSwap`
@@ -281,7 +282,10 @@ declare function addSendToTx(tx: Transaction, coin: TransactionObjectArgument, r
281
282
  * Single source of truth for the "fetch coins of type X owned by
282
283
  * address Y, paginated" pattern. P2.2b extracts this so `composeTx`'s
283
284
  * registry adapters can build a wallet-mode `TransactionObjectArgument`
284
- * uniformly across save / send / repay / etc.
285
+ * uniformly across save / send / repay / etc. The 2026-05-02 P2.7 fix
286
+ * adds a per-PTB merge cache so multi-write bundles that touch the same
287
+ * coin type twice (swap+save, save+send, etc.) only emit `mergeCoins`
288
+ * once — see `ptbMergeCache` JSDoc below.
285
289
  */
286
290
 
287
291
  interface CoinPage {
package/dist/index.js CHANGED
@@ -818,6 +818,97 @@ var init_volo = __esm({
818
818
  }
819
819
  });
820
820
 
821
+ // src/wallet/coinSelection.ts
822
+ var coinSelection_exports = {};
823
+ __export(coinSelection_exports, {
824
+ fetchAllCoins: () => fetchAllCoins,
825
+ selectAndSplitCoin: () => selectAndSplitCoin,
826
+ selectSuiCoin: () => selectSuiCoin
827
+ });
828
+ function getMergeCache(tx) {
829
+ let cache = ptbMergeCache.get(tx);
830
+ if (!cache) {
831
+ cache = /* @__PURE__ */ new Map();
832
+ ptbMergeCache.set(tx, cache);
833
+ }
834
+ return cache;
835
+ }
836
+ async function fetchAllCoins(client, owner, coinType) {
837
+ const ids = [];
838
+ let totalBalance = 0n;
839
+ let cursor;
840
+ let hasNext = true;
841
+ while (hasNext) {
842
+ const page = await client.getCoins({ owner, coinType, cursor: cursor ?? void 0 });
843
+ for (const c of page.data) {
844
+ ids.push(c.coinObjectId);
845
+ totalBalance += BigInt(c.balance);
846
+ }
847
+ cursor = page.nextCursor;
848
+ hasNext = page.hasNextPage;
849
+ }
850
+ return { ids, totalBalance };
851
+ }
852
+ async function selectAndSplitCoin(tx, client, owner, coinType, amount, options = {}) {
853
+ const cache = getMergeCache(tx);
854
+ const cacheKey = `${owner}|${coinType}`;
855
+ const cached = cache.get(cacheKey);
856
+ if (cached) {
857
+ const allowSwapAll2 = options.allowSwapAll ?? true;
858
+ if (amount !== "all" && amount > cached.remaining && !allowSwapAll2) {
859
+ throw new T2000Error("INSUFFICIENT_BALANCE", `Insufficient balance for ${coinType}`, {
860
+ available: cached.remaining.toString(),
861
+ required: amount.toString()
862
+ });
863
+ }
864
+ const requested2 = amount === "all" ? cached.remaining : amount;
865
+ const swapAll2 = amount === "all" || requested2 >= cached.remaining;
866
+ const effectiveAmount2 = swapAll2 ? cached.remaining : requested2;
867
+ const coin2 = swapAll2 ? cached.primary : tx.splitCoins(cached.primary, [effectiveAmount2])[0];
868
+ cached.remaining = swapAll2 ? 0n : cached.remaining - effectiveAmount2;
869
+ return { coin: coin2, effectiveAmount: effectiveAmount2, swapAll: swapAll2 };
870
+ }
871
+ const { ids, totalBalance } = await fetchAllCoins(client, owner, coinType);
872
+ if (ids.length === 0) {
873
+ throw new T2000Error("INSUFFICIENT_BALANCE", `No coins found for ${coinType}`);
874
+ }
875
+ const allowSwapAll = options.allowSwapAll ?? true;
876
+ if (amount !== "all" && amount > totalBalance && !allowSwapAll) {
877
+ throw new T2000Error("INSUFFICIENT_BALANCE", `Insufficient balance for ${coinType}`, {
878
+ available: totalBalance.toString(),
879
+ required: amount.toString()
880
+ });
881
+ }
882
+ const requested = amount === "all" ? totalBalance : amount;
883
+ const swapAll = amount === "all" || requested >= totalBalance;
884
+ const effectiveAmount = swapAll ? totalBalance : requested;
885
+ const primary = tx.object(ids[0]);
886
+ if (ids.length > 1) {
887
+ tx.mergeCoins(primary, ids.slice(1).map((id) => tx.object(id)));
888
+ }
889
+ const coin = swapAll ? primary : tx.splitCoins(primary, [effectiveAmount])[0];
890
+ cache.set(cacheKey, {
891
+ primary,
892
+ remaining: swapAll ? 0n : totalBalance - effectiveAmount
893
+ });
894
+ return { coin, effectiveAmount, swapAll };
895
+ }
896
+ async function selectSuiCoin(tx, client, owner, amountMist, sponsoredContext) {
897
+ if (sponsoredContext) {
898
+ const { SUI_TYPE: SUI_TYPE2 } = await Promise.resolve().then(() => (init_token_registry(), token_registry_exports));
899
+ return selectAndSplitCoin(tx, client, owner, SUI_TYPE2, amountMist);
900
+ }
901
+ const [coin] = tx.splitCoins(tx.gas, [amountMist]);
902
+ return { coin, effectiveAmount: amountMist, swapAll: false };
903
+ }
904
+ var ptbMergeCache;
905
+ var init_coinSelection = __esm({
906
+ "src/wallet/coinSelection.ts"() {
907
+ init_errors();
908
+ ptbMergeCache = /* @__PURE__ */ new WeakMap();
909
+ }
910
+ });
911
+
821
912
  // src/protocols/cetus-swap.ts
822
913
  var cetus_swap_exports = {};
823
914
  __export(cetus_swap_exports, {
@@ -913,17 +1004,10 @@ async function addSwapToTx(tx, client, address, input) {
913
1004
  inputCoin = input.inputCoin;
914
1005
  effectiveRaw = requestedRaw;
915
1006
  } else {
916
- const { ids, totalBalance } = await fetchAllCoinsForSwap(client, address, fromType);
917
- if (ids.length === 0) {
918
- throw new T2000Error2("INSUFFICIENT_BALANCE", `No ${input.from} coins found in wallet`);
919
- }
920
- const swapAll = requestedRaw >= totalBalance;
921
- effectiveRaw = swapAll ? totalBalance : requestedRaw;
922
- const primary = tx.object(ids[0]);
923
- if (ids.length > 1) {
924
- tx.mergeCoins(primary, ids.slice(1).map((id) => tx.object(id)));
925
- }
926
- inputCoin = swapAll ? primary : tx.splitCoins(primary, [effectiveRaw])[0];
1007
+ const { selectAndSplitCoin: selectAndSplitCoin2 } = await Promise.resolve().then(() => (init_coinSelection(), coinSelection_exports));
1008
+ const result = await selectAndSplitCoin2(tx, client, address, fromType, requestedRaw);
1009
+ inputCoin = result.coin;
1010
+ effectiveRaw = result.effectiveAmount;
927
1011
  }
928
1012
  const route = await findSwapRoute({
929
1013
  walletAddress: address,
@@ -955,22 +1039,6 @@ async function addSwapToTx(tx, client, address, input) {
955
1039
  route
956
1040
  };
957
1041
  }
958
- async function fetchAllCoinsForSwap(client, owner, coinType) {
959
- const ids = [];
960
- let totalBalance = 0n;
961
- let cursor;
962
- let hasNext = true;
963
- while (hasNext) {
964
- const page = await client.getCoins({ owner, coinType, cursor: cursor ?? void 0 });
965
- for (const c of page.data) {
966
- ids.push(c.coinObjectId);
967
- totalBalance += BigInt(c.balance);
968
- }
969
- cursor = page.nextCursor;
970
- hasNext = page.hasNextPage;
971
- }
972
- return { ids, totalBalance };
973
- }
974
1042
  async function simulateSwap(params) {
975
1043
  const client = getClient(params.walletAddress, params.overlayFee);
976
1044
  try {
@@ -7378,57 +7446,10 @@ var T2000 = class _T2000 extends EventEmitter {
7378
7446
 
7379
7447
  // src/index.ts
7380
7448
  init_errors();
7381
-
7382
- // src/wallet/coinSelection.ts
7383
- init_errors();
7384
- async function fetchAllCoins(client, owner, coinType) {
7385
- const ids = [];
7386
- let totalBalance = 0n;
7387
- let cursor;
7388
- let hasNext = true;
7389
- while (hasNext) {
7390
- const page = await client.getCoins({ owner, coinType, cursor: cursor ?? void 0 });
7391
- for (const c of page.data) {
7392
- ids.push(c.coinObjectId);
7393
- totalBalance += BigInt(c.balance);
7394
- }
7395
- cursor = page.nextCursor;
7396
- hasNext = page.hasNextPage;
7397
- }
7398
- return { ids, totalBalance };
7399
- }
7400
- async function selectAndSplitCoin(tx, client, owner, coinType, amount, options = {}) {
7401
- const { ids, totalBalance } = await fetchAllCoins(client, owner, coinType);
7402
- if (ids.length === 0) {
7403
- throw new T2000Error("INSUFFICIENT_BALANCE", `No coins found for ${coinType}`);
7404
- }
7405
- const allowSwapAll = options.allowSwapAll ?? true;
7406
- if (amount !== "all" && amount > totalBalance && !allowSwapAll) {
7407
- throw new T2000Error("INSUFFICIENT_BALANCE", `Insufficient balance for ${coinType}`, {
7408
- available: totalBalance.toString(),
7409
- required: amount.toString()
7410
- });
7411
- }
7412
- const requested = amount === "all" ? totalBalance : amount;
7413
- const swapAll = amount === "all" || requested >= totalBalance;
7414
- const effectiveAmount = swapAll ? totalBalance : requested;
7415
- const primary = tx.object(ids[0]);
7416
- if (ids.length > 1) {
7417
- tx.mergeCoins(primary, ids.slice(1).map((id) => tx.object(id)));
7418
- }
7419
- const coin = swapAll ? primary : tx.splitCoins(primary, [effectiveAmount])[0];
7420
- return { coin, effectiveAmount, swapAll };
7421
- }
7422
- async function selectSuiCoin(tx, client, owner, amountMist, sponsoredContext) {
7423
- if (sponsoredContext) {
7424
- const { SUI_TYPE: SUI_TYPE2 } = await Promise.resolve().then(() => (init_token_registry(), token_registry_exports));
7425
- return selectAndSplitCoin(tx, client, owner, SUI_TYPE2, amountMist);
7426
- }
7427
- const [coin] = tx.splitCoins(tx.gas, [amountMist]);
7428
- return { coin, effectiveAmount: amountMist, swapAll: false };
7429
- }
7449
+ init_coinSelection();
7430
7450
  init_cetus_swap();
7431
7451
  init_volo();
7452
+ init_coinSelection();
7432
7453
  init_token_registry();
7433
7454
  init_errors();
7434
7455
  var SPONSORED_PYTH_DEPENDENT_PROVIDERS = [