@t2000/sdk 4.2.0 → 4.2.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
@@ -1,12 +1,12 @@
1
1
  import { EventEmitter } from 'eventemitter3';
2
2
  import { SuiJsonRpcClient } from '@mysten/sui/jsonRpc';
3
3
  import { Ed25519Keypair } from '@mysten/sui/keypairs/ed25519';
4
- import { Q as TxMetadata, o as SafeguardConfig, T as T2000Error, H as TransactionSigner, v as SupportedAsset, _ as ZkLoginProof, ax as SendableAsset } from './types-CIclvR39.cjs';
5
- export { A as ALL_NAVI_ASSETS, B as BORROW_FEE_BPS, a as BPS_DENOMINATOR, ay as CETUS_USDC_SUI_POOL, C as CLOCK_ID, b as COIN_REGISTRY, c as ClassifyBalanceChange, d as ClassifyResult, e as CoinMeta, az as DEFAULT_GRPC_URL, D as DEFAULT_NETWORK, f as DEFAULT_SAFEGUARD_CONFIG, E as ETH_TYPE, g as ExtractedTransfer, F as FeeOperation, aA as GASLESS_MIN_STABLE_AMOUNT, aB as GASLESS_STABLE_TYPES, G as GAS_RESERVE_MIN, I as IKA_TYPE, K as KNOWN_TARGETS, h as KeypairSigner, L as LABEL_PATTERNS, i as LOFI_TYPE, M as MANIFEST_TYPE, j as MIST_PER_SUI, N as NAVX_TYPE, aC as OPERATION_ASSETS, O as OUTBOUND_OPS, aD as Operation, P as ProtocolFeeInfo, aE as SAVEABLE_ASSETS, S as SAVE_FEE_BPS, aF as SENDABLE_ASSETS, k as STABLE_ASSETS, l as SUI_DECIMALS, m as SUI_TYPE, n as SUPPORTED_ASSETS, p as SafeguardError, q as SafeguardErrorDetails, r as SafeguardRule, aG as SaveableAsset, s as SimulationResult, t as StableAsset, u as SuiRpcTxBlock, w as T2000ErrorCode, x as T2000ErrorData, y as T2000_OVERLAY_FEE_WALLET, z as TOKEN_MAP, J as TxDirection, U as USDC_DECIMALS, R as USDC_TYPE, V as USDE_TYPE, W as USDSUI_TYPE, X as USDT_TYPE, Y as WAL_TYPE, Z as WBTC_TYPE, $ as ZkLoginSigner, a0 as addFeeTransfer, aH as assertAllowedAsset, a1 as calculateFee, a2 as classifyAction, a3 as classifyLabel, a4 as classifyTransaction, aI as createSuiClient, a5 as extractAllUserLegs, a6 as extractTransferDetails, a7 as extractTxCommands, a8 as extractTxSender, a9 as fallbackLabel, aa as formatAssetAmount, ab as formatSui, ac as formatUsd, aJ as getCoinMeta, ad as getDecimals, ae as getDecimalsForCoinType, aK as getSuiClient, aL as getSuiGrpcClient, af as getTier, aM as isAllowedAsset, aN as isInRegistry, ag as isSupported, ah as isTier1, ai as isTier2, aj as mapMoveAbortCode, ak as mapWalletError, al as mistToSui, aO as normalizeAsset, aP as normalizeCoinType, am as parseSuiRpcTx, aQ as queryHistory, aR as queryTransaction, an as rawToStable, ao as rawToUsdc, ap as refineLendingLabel, aq as resolveSymbol, ar as resolveTokenType, aS as simulateTransaction, as as stableToRaw, at as suiToMist, aT as throwIfSimulationFailed, au as truncateAddress, av as usdcToRaw, aw as validateAddress } from './types-CIclvR39.cjs';
4
+ import { Q as TxMetadata, o as SafeguardConfig, T as T2000Error, H as TransactionSigner, v as SupportedAsset, _ as ZkLoginProof, ax as SendableAsset } from './types-8CNP74n6.cjs';
5
+ export { A as ALL_NAVI_ASSETS, B as BORROW_FEE_BPS, a as BPS_DENOMINATOR, ay as CETUS_USDC_SUI_POOL, C as CLOCK_ID, b as COIN_REGISTRY, c as ClassifyBalanceChange, d as ClassifyResult, e as CoinMeta, az as DEFAULT_GRPC_URL, D as DEFAULT_NETWORK, f as DEFAULT_SAFEGUARD_CONFIG, E as ETH_TYPE, g as ExtractedTransfer, F as FeeOperation, aA as GASLESS_MIN_STABLE_AMOUNT, aB as GASLESS_STABLE_TYPES, G as GAS_RESERVE_MIN, I as IKA_TYPE, K as KNOWN_TARGETS, h as KeypairSigner, L as LABEL_PATTERNS, i as LOFI_TYPE, M as MANIFEST_TYPE, j as MIST_PER_SUI, N as NAVX_TYPE, aC as OPERATION_ASSETS, O as OUTBOUND_OPS, aD as Operation, P as ProtocolFeeInfo, aE as SAVEABLE_ASSETS, S as SAVE_FEE_BPS, aF as SENDABLE_ASSETS, k as STABLE_ASSETS, l as SUI_DECIMALS, m as SUI_TYPE, n as SUPPORTED_ASSETS, p as SafeguardError, q as SafeguardErrorDetails, r as SafeguardRule, aG as SaveableAsset, s as SimulationResult, t as StableAsset, u as SuiRpcTxBlock, w as T2000ErrorCode, x as T2000ErrorData, y as T2000_OVERLAY_FEE_WALLET, z as TOKEN_MAP, J as TxDirection, U as USDC_DECIMALS, R as USDC_TYPE, V as USDE_TYPE, W as USDSUI_TYPE, X as USDT_TYPE, Y as WAL_TYPE, Z as WBTC_TYPE, $ as ZkLoginSigner, a0 as addFeeTransfer, aH as assertAllowedAsset, a1 as calculateFee, a2 as classifyAction, a3 as classifyLabel, a4 as classifyTransaction, aI as createSuiClient, a5 as extractAllUserLegs, a6 as extractTransferDetails, a7 as extractTxCommands, a8 as extractTxSender, a9 as fallbackLabel, aa as formatAssetAmount, ab as formatSui, ac as formatUsd, aJ as getCoinMeta, ad as getDecimals, ae as getDecimalsForCoinType, aK as getSuiClient, aL as getSuiGrpcClient, af as getTier, aM as isAllowedAsset, aN as isInRegistry, ag as isSupported, ah as isTier1, ai as isTier2, aj as mapMoveAbortCode, ak as mapWalletError, al as mistToSui, aO as normalizeAsset, aP as normalizeCoinType, am as parseSuiRpcTx, aQ as queryHistory, aR as queryTransaction, an as rawToStable, ao as rawToUsdc, ap as refineLendingLabel, aq as resolveSymbol, ar as resolveTokenType, aS as simulateTransaction, as as stableToRaw, at as suiToMist, aT as throwIfSimulationFailed, au as truncateAddress, av as usdcToRaw, aw as validateAddress } from './types-8CNP74n6.cjs';
6
6
  import { L as LendingAdapter, a as LendingRates, P as PendingReward$1 } from './descriptors-DqIb4AnV.cjs';
7
7
  export { A as AdapterCapability, b as AdapterPositions, c as AdapterTxResult, H as HealthInfo, d as ProtocolDescriptor, e as allDescriptors, n as naviDescriptor } from './descriptors-DqIb4AnV.cjs';
8
- import { n as T2000Options, P as PayOptions, d as PayResult, o as SwapResult, p as SwapQuoteResult, i as SendResult, B as BalanceResponse, k as TransactionRecord, D as DepositInfo, q as PaymentRequest, S as SaveResult, W as WithdrawResult, b as MaxWithdrawResult, a as BorrowResult, h as RepayResult, M as MaxBorrowResult, H as HealthFactorResult, e as PendingReward, C as ClaimRewardsResult, r as CompoundRewardsResult, g as PositionsResult, R as RatesResult, E as EarningsResult, F as FundStatusResult, c as OverlayFeeConfig, j as SwapRouteResult, s as FinancialSummary } from './types-HJGbXJ37.cjs';
9
- export { A as AssetRates, G as GasReserve, t as HFAlertLevel, O as OVERLAY_FEE_RATE, f as PositionEntry, u as SerializedCetusRoute, v as SerializedCetusRoutePath, w as SerializedRouterDataV3, T as TransactionLeg, x as addSwapToTx, l as buildSwapTx, y as deserializeCetusRoute, m as findSwapRoute, z as isCetusRouteFresh, I as serializeCetusRoute, J as verifyCetusRouteCoinMatch } from './types-HJGbXJ37.cjs';
8
+ import { n as T2000Options, P as PayOptions, d as PayResult, o as SwapResult, p as SwapQuoteResult, i as SendResult, B as BalanceResponse, k as TransactionRecord, D as DepositInfo, q as PaymentRequest, S as SaveResult, W as WithdrawResult, b as MaxWithdrawResult, a as BorrowResult, h as RepayResult, M as MaxBorrowResult, H as HealthFactorResult, e as PendingReward, C as ClaimRewardsResult, r as CompoundRewardsResult, g as PositionsResult, R as RatesResult, E as EarningsResult, F as FundStatusResult, c as OverlayFeeConfig, s as SponsoredCoinMergeCache, j as SwapRouteResult, t as FinancialSummary } from './types-DUmk5ivZ.cjs';
9
+ export { A as AssetRates, u as CoinPage, G as GasReserve, v as HFAlertLevel, O as OVERLAY_FEE_RATE, f as PositionEntry, w as SelectAndSplitResult, x as SerializedCetusRoute, y as SerializedCetusRoutePath, z as SerializedRouterDataV3, T as TransactionLeg, I as addSwapToTx, l as buildSwapTx, J as deserializeCetusRoute, K as fetchAllCoins, m as findSwapRoute, L as isCetusRouteFresh, N as selectAndSplitCoin, Q as selectSuiCoin, U as serializeCetusRoute, V as verifyCetusRouteCoinMatch } from './types-DUmk5ivZ.cjs';
10
10
  import { Transaction, TransactionObjectArgument } from '@mysten/sui/transactions';
11
11
  import { SuinsClient } from '@mysten/suins';
12
12
  export { NaviAdapter, ProtocolRegistry } from './adapters/index.cjs';
@@ -497,114 +497,6 @@ declare function buildSendTx({ client, address, to, amount, asset, }: {
497
497
  */
498
498
  declare function addSendToTx(tx: Transaction, coin: TransactionObjectArgument, recipient: string): void;
499
499
 
500
- /**
501
- * Wallet-side coin selection helpers — single source of truth for the
502
- * "produce a `Coin<T>` argument holding `amount` raw units of `coinType`,
503
- * owned by `address`" pattern. Used by every wallet-mode appender that
504
- * needs a coin input (save, send, swap, repay, stake, etc.).
505
- *
506
- * **2026-05-22 — address-balance migration.** Sui mainnet's address-balance
507
- * feature ships funds account-style instead of as discrete `Coin<T>` objects.
508
- * After a payment via `0x2::balance::send_funds`, the leftover lands in the
509
- * sender's address balance with a synthetic "coin reservation" representing
510
- * the deposit. `client.getCoins()` correctly filters those reservations out
511
- * (they aren't real owned objects), so the old fetch+merge+split pattern
512
- * threw `INSUFFICIENT_BALANCE` for users whose stables had drifted into
513
- * address balance — even when `getBalance().totalBalance` showed plenty.
514
- *
515
- * The fix is structural: hand the work to `coinWithBalance({ type, balance })`
516
- * from `@mysten/sui/transactions`. Its build-time resolver inspects coins +
517
- * address balance together (`getBalance` + `listCoins`), then emits the
518
- * right shape — direct `redeem_funds` from address balance when AB ≥
519
- * required, or merge-and-split across coins + AB withdrawal when not. Multi
520
- * intents per coin type get batched into a single merge in one PTB, so the
521
- * old per-PTB merge cache is no longer needed.
522
- *
523
- * Pre-flight uses `client.getBalance().totalBalance` (sums coins + AB)
524
- * instead of summing the paginated `getCoins` page. That's the OTHER half
525
- * of the migration — the legacy path could see `0` from `getCoins` and
526
- * mistakenly throw before `coinWithBalance` ever ran.
527
- */
528
-
529
- interface CoinPage {
530
- ids: string[];
531
- totalBalance: bigint;
532
- }
533
- /**
534
- * Sum every coin of `coinType` owned by `owner`, INCLUDING address balance.
535
- * Returns the IDs of any discrete coin objects that exist (callers
536
- * occasionally need this for non-`coinWithBalance` paths, e.g. SUIns name
537
- * registration which expects raw object IDs).
538
- *
539
- * Pre-2026-05-22 this function paginated `client.getCoins` and summed
540
- * the page balances. That misses address-balance funds (the SDK filters
541
- * them out of `getCoins` for back-compat). The new implementation calls
542
- * `getBalance` for the canonical total and `getCoins` for the optional
543
- * ID list — both round-trips, but they happen in parallel.
544
- */
545
- declare function fetchAllCoins(client: SuiJsonRpcClient, owner: string, coinType: string): Promise<CoinPage>;
546
- interface SelectAndSplitResult {
547
- /** TransactionObjectArgument for a coin holding `effectiveAmount` raw units. */
548
- coin: TransactionObjectArgument;
549
- /** Actual raw amount the returned coin holds. May be < requested if `swapAll` is true. */
550
- effectiveAmount: bigint;
551
- /** True iff the request consumed the entire wallet balance (no split needed). */
552
- swapAll: boolean;
553
- }
554
- /**
555
- * Wallet-mode coin selection prelude. Pre-flights against
556
- * `getBalance().totalBalance` (coins + address balance combined), then
557
- * returns a `coinWithBalance({ type, balance })` argument that the
558
- * `@mysten/sui` resolver fulfills at build time.
559
- *
560
- * Throws `T2000Error` (`INSUFFICIENT_BALANCE`) when:
561
- * - `amount` is bigger than the total balance AND the caller did NOT
562
- * opt into `swapAll: true` clipping.
563
- * - `amount === 'all'` AND total balance is zero.
564
- *
565
- * @param tx — PTB to register the `coinWithBalance` intent against.
566
- * @param client — Sui RPC client for the pre-flight `getBalance` lookup.
567
- * @param owner — wallet address whose coins to source from.
568
- * @param coinType — fully-qualified Sui coin type (e.g. `"0x...::usdc::USDC"`).
569
- * @param amount — raw amount to source (in MIST / smallest unit). Pass
570
- * `'all'` to consume the entire balance.
571
- * @param options.allowSwapAll — if true (default), `amount` >= totalBalance
572
- * auto-clips to total. If false, throws when the request would over-consume.
573
- * @param options.sponsoredContext — when true, source ONLY from discrete coin
574
- * objects (never the address balance). See the long note below — this exists
575
- * because Enoki's gas station can't yet deserialize a `TransactionData` that
576
- * contains the address-balance `FundsWithdrawal` reservation that
577
- * `coinWithBalance` emits. Self-funded callers leave this false: the fullnode
578
- * handles `FundsWithdrawal` fine, so the address-balance path is preferred
579
- * (it can reach funds that aren't held as coin objects).
580
- *
581
- * @returns
582
- * - `coin` — `TransactionObjectArgument` ready for downstream consumption.
583
- * - `effectiveAmount` — the raw amount the returned coin holds (handles
584
- * swapAll clipping).
585
- * - `swapAll` — true iff the entire balance was consumed.
586
- */
587
- declare function selectAndSplitCoin(tx: Transaction, client: SuiJsonRpcClient, owner: string, coinType: string, amount: bigint | 'all', options?: {
588
- allowSwapAll?: boolean;
589
- sponsoredContext?: boolean;
590
- }): Promise<SelectAndSplitResult>;
591
- /**
592
- * SUI-specific coin selection. Branches on sponsorship context:
593
- *
594
- * - **Self-funded (`sponsoredContext: false`)** — splits from `tx.gas`
595
- * directly (the user's gas coin IS their SUI). More efficient — no
596
- * `getBalance` RTT.
597
- *
598
- * - **Sponsored (`sponsoredContext: true`)** — sources from the user's
599
- * discrete SUI coin objects (`selectCoinObjectsOnly`). This both (a) avoids
600
- * `tx.gas`, which belongs to the Enoki sponsor — NOT the user — under
601
- * sponsored flows (the original S.260 reason for `useGasCoin: false`), AND
602
- * (b) avoids `coinWithBalance`'s address-balance `FundsWithdrawal`, which
603
- * Enoki's gas station can't deserialize (issue #93). If the user's SUI is
604
- * address-balance-only, it raises `ADDRESS_BALANCE_UNSPONSORABLE`.
605
- */
606
- declare function selectSuiCoin(tx: Transaction, client: SuiJsonRpcClient, owner: string, amountMist: bigint, sponsoredContext: boolean): Promise<SelectAndSplitResult>;
607
-
608
500
  /**
609
501
  * SPEC 7 v0.4 § Layer 0 — Canonical Write Architecture.
610
502
  *
@@ -981,6 +873,15 @@ interface AppenderContext {
981
873
  * the PTB build fails or the on-chain leg reverts.
982
874
  */
983
875
  isOutputConsumed?: boolean;
876
+ /**
877
+ * Per-PTB merge cache for sponsored SUI sourcing, shared across every
878
+ * appender in a single `composeTx` run. Lets multiple SUI-source swap
879
+ * legs in one bundle reuse a single merged primary coin instead of each
880
+ * re-fetching + re-merging the same coin objects (the second merge of
881
+ * which references already-consumed coins → Enoki dry-run
882
+ * `ArgumentWithoutValue`). See `SponsoredCoinMergeCache` JSDoc.
883
+ */
884
+ suiMergeCache?: SponsoredCoinMergeCache;
984
885
  }
985
886
  /**
986
887
  * [SPEC 13 Phase 1] Appender return shape. Producers populate
@@ -1498,4 +1399,4 @@ declare function fullHandle(label: string): string;
1498
1399
  */
1499
1400
  declare function displayHandle(label: string): string;
1500
1401
 
1501
- export { AUDRIC_PARENT_NAME, AUDRIC_PARENT_NFT_ID, type AppenderContext, BalanceResponse, type BorrowInput, BorrowResult, type BuildAddLeafParams, type BuildHarvestRewardsTxOptions, type BuildRevokeLeafParams, type ClaimRewardsInput, ClaimRewardsResult, type CoinPage, type ComposeTxFeeHookContext, type ComposeTxFeeHooks, type ComposeTxOptions, type ComposeTxResult, CompoundRewardsResult, ContactManager, DepositInfo, EarningsResult, FinancialSummary, type FinancialSummaryOptions, FundStatusResult, HF_CRITICAL_THRESHOLD, HF_WARN_THRESHOLD, type HarvestPlan, type HarvestSkippedLeg, type HarvestSwapLeg, HealthFactorResult, InvalidAddressError, type LabelValidationResult, LendingAdapter, LendingRates, MaxBorrowResult, MaxWithdrawResult, type NormalizedAddress, OverlayFeeConfig, PayOptions, PayResult, PaymentRequest, PendingReward, PositionsResult, RatesResult, type RepayDebtInput, RepayResult, SPONSORED_PYTH_DEPENDENT_PROVIDERS, SUINS_NAME_REGEX, SUI_ADDRESS_REGEX, SUI_ADDRESS_STRICT_REGEX, SafeguardConfig, SafeguardEnforcer, type SaveDepositInput, SaveResult, type SelectAndSplitResult, SendResult, type SendTransferInput, SendableAsset, type StepPreview, SuinsNotRegisteredError, SuinsRpcError, SupportedAsset, type SwapExecuteInput, SwapQuoteResult, SwapResult, SwapRouteResult, T2000, T2000Error, T2000Options, TransactionRecord, TransactionSigner, TxMetadata, WRITE_APPENDER_REGISTRY, type WithdrawInput, WithdrawResult, type WriteStep, type WriteToolName, ZkLoginProof, addClaimRewardsToTx, addSendToTx, aggregateClaimableRewards, buildAddLeafTx, buildClaimRewardsTx, buildHarvestRewardsTx, buildRevokeLeafTx, buildSendTx, composeTx, deriveAllowedAddressesFromPtb, displayHandle, exportPrivateKey, fetchAllCoins, fullHandle, generateKeypair, getAddress, getFinancialSummary, getPendingRewards, getPendingRewardsByAddress, getRates, getSponsoredSwapProviders, getSwapQuote, keypairFromPrivateKey, loadKey, looksLikeSuiNs, normalizeAddressInput, resolveAddressToSuinsViaRpc, resolveSuinsViaRpc, saveBech32, saveKey, selectAndSplitCoin, selectSuiCoin, validateLabel, walletExists };
1402
+ export { AUDRIC_PARENT_NAME, AUDRIC_PARENT_NFT_ID, type AppenderContext, BalanceResponse, type BorrowInput, BorrowResult, type BuildAddLeafParams, type BuildHarvestRewardsTxOptions, type BuildRevokeLeafParams, type ClaimRewardsInput, ClaimRewardsResult, type ComposeTxFeeHookContext, type ComposeTxFeeHooks, type ComposeTxOptions, type ComposeTxResult, CompoundRewardsResult, ContactManager, DepositInfo, EarningsResult, FinancialSummary, type FinancialSummaryOptions, FundStatusResult, HF_CRITICAL_THRESHOLD, HF_WARN_THRESHOLD, type HarvestPlan, type HarvestSkippedLeg, type HarvestSwapLeg, HealthFactorResult, InvalidAddressError, type LabelValidationResult, LendingAdapter, LendingRates, MaxBorrowResult, MaxWithdrawResult, type NormalizedAddress, OverlayFeeConfig, PayOptions, PayResult, PaymentRequest, PendingReward, PositionsResult, RatesResult, type RepayDebtInput, RepayResult, SPONSORED_PYTH_DEPENDENT_PROVIDERS, SUINS_NAME_REGEX, SUI_ADDRESS_REGEX, SUI_ADDRESS_STRICT_REGEX, SafeguardConfig, SafeguardEnforcer, type SaveDepositInput, SaveResult, SendResult, type SendTransferInput, SendableAsset, type StepPreview, SuinsNotRegisteredError, SuinsRpcError, SupportedAsset, type SwapExecuteInput, SwapQuoteResult, SwapResult, SwapRouteResult, T2000, T2000Error, T2000Options, TransactionRecord, TransactionSigner, TxMetadata, WRITE_APPENDER_REGISTRY, type WithdrawInput, WithdrawResult, type WriteStep, type WriteToolName, ZkLoginProof, addClaimRewardsToTx, addSendToTx, aggregateClaimableRewards, buildAddLeafTx, buildClaimRewardsTx, buildHarvestRewardsTx, buildRevokeLeafTx, buildSendTx, composeTx, deriveAllowedAddressesFromPtb, displayHandle, exportPrivateKey, fullHandle, generateKeypair, getAddress, getFinancialSummary, getPendingRewards, getPendingRewardsByAddress, getRates, getSponsoredSwapProviders, getSwapQuote, keypairFromPrivateKey, loadKey, looksLikeSuiNs, normalizeAddressInput, resolveAddressToSuinsViaRpc, resolveSuinsViaRpc, saveBech32, saveKey, validateLabel, walletExists };
package/dist/index.d.ts CHANGED
@@ -1,12 +1,12 @@
1
1
  import { EventEmitter } from 'eventemitter3';
2
2
  import { SuiJsonRpcClient } from '@mysten/sui/jsonRpc';
3
3
  import { Ed25519Keypair } from '@mysten/sui/keypairs/ed25519';
4
- import { Q as TxMetadata, o as SafeguardConfig, T as T2000Error, H as TransactionSigner, v as SupportedAsset, _ as ZkLoginProof, ax as SendableAsset } from './types-B63ogqZZ.js';
5
- export { A as ALL_NAVI_ASSETS, B as BORROW_FEE_BPS, a as BPS_DENOMINATOR, ay as CETUS_USDC_SUI_POOL, C as CLOCK_ID, b as COIN_REGISTRY, c as ClassifyBalanceChange, d as ClassifyResult, e as CoinMeta, az as DEFAULT_GRPC_URL, D as DEFAULT_NETWORK, f as DEFAULT_SAFEGUARD_CONFIG, E as ETH_TYPE, g as ExtractedTransfer, F as FeeOperation, aA as GASLESS_MIN_STABLE_AMOUNT, aB as GASLESS_STABLE_TYPES, G as GAS_RESERVE_MIN, I as IKA_TYPE, K as KNOWN_TARGETS, h as KeypairSigner, L as LABEL_PATTERNS, i as LOFI_TYPE, M as MANIFEST_TYPE, j as MIST_PER_SUI, N as NAVX_TYPE, aC as OPERATION_ASSETS, O as OUTBOUND_OPS, aD as Operation, P as ProtocolFeeInfo, aE as SAVEABLE_ASSETS, S as SAVE_FEE_BPS, aF as SENDABLE_ASSETS, k as STABLE_ASSETS, l as SUI_DECIMALS, m as SUI_TYPE, n as SUPPORTED_ASSETS, p as SafeguardError, q as SafeguardErrorDetails, r as SafeguardRule, aG as SaveableAsset, s as SimulationResult, t as StableAsset, u as SuiRpcTxBlock, w as T2000ErrorCode, x as T2000ErrorData, y as T2000_OVERLAY_FEE_WALLET, z as TOKEN_MAP, J as TxDirection, U as USDC_DECIMALS, R as USDC_TYPE, V as USDE_TYPE, W as USDSUI_TYPE, X as USDT_TYPE, Y as WAL_TYPE, Z as WBTC_TYPE, $ as ZkLoginSigner, a0 as addFeeTransfer, aH as assertAllowedAsset, a1 as calculateFee, a2 as classifyAction, a3 as classifyLabel, a4 as classifyTransaction, aI as createSuiClient, a5 as extractAllUserLegs, a6 as extractTransferDetails, a7 as extractTxCommands, a8 as extractTxSender, a9 as fallbackLabel, aa as formatAssetAmount, ab as formatSui, ac as formatUsd, aJ as getCoinMeta, ad as getDecimals, ae as getDecimalsForCoinType, aK as getSuiClient, aL as getSuiGrpcClient, af as getTier, aM as isAllowedAsset, aN as isInRegistry, ag as isSupported, ah as isTier1, ai as isTier2, aj as mapMoveAbortCode, ak as mapWalletError, al as mistToSui, aO as normalizeAsset, aP as normalizeCoinType, am as parseSuiRpcTx, aQ as queryHistory, aR as queryTransaction, an as rawToStable, ao as rawToUsdc, ap as refineLendingLabel, aq as resolveSymbol, ar as resolveTokenType, aS as simulateTransaction, as as stableToRaw, at as suiToMist, aT as throwIfSimulationFailed, au as truncateAddress, av as usdcToRaw, aw as validateAddress } from './types-B63ogqZZ.js';
4
+ import { Q as TxMetadata, o as SafeguardConfig, T as T2000Error, H as TransactionSigner, v as SupportedAsset, _ as ZkLoginProof, ax as SendableAsset } from './types-BTbqE8AE.js';
5
+ export { A as ALL_NAVI_ASSETS, B as BORROW_FEE_BPS, a as BPS_DENOMINATOR, ay as CETUS_USDC_SUI_POOL, C as CLOCK_ID, b as COIN_REGISTRY, c as ClassifyBalanceChange, d as ClassifyResult, e as CoinMeta, az as DEFAULT_GRPC_URL, D as DEFAULT_NETWORK, f as DEFAULT_SAFEGUARD_CONFIG, E as ETH_TYPE, g as ExtractedTransfer, F as FeeOperation, aA as GASLESS_MIN_STABLE_AMOUNT, aB as GASLESS_STABLE_TYPES, G as GAS_RESERVE_MIN, I as IKA_TYPE, K as KNOWN_TARGETS, h as KeypairSigner, L as LABEL_PATTERNS, i as LOFI_TYPE, M as MANIFEST_TYPE, j as MIST_PER_SUI, N as NAVX_TYPE, aC as OPERATION_ASSETS, O as OUTBOUND_OPS, aD as Operation, P as ProtocolFeeInfo, aE as SAVEABLE_ASSETS, S as SAVE_FEE_BPS, aF as SENDABLE_ASSETS, k as STABLE_ASSETS, l as SUI_DECIMALS, m as SUI_TYPE, n as SUPPORTED_ASSETS, p as SafeguardError, q as SafeguardErrorDetails, r as SafeguardRule, aG as SaveableAsset, s as SimulationResult, t as StableAsset, u as SuiRpcTxBlock, w as T2000ErrorCode, x as T2000ErrorData, y as T2000_OVERLAY_FEE_WALLET, z as TOKEN_MAP, J as TxDirection, U as USDC_DECIMALS, R as USDC_TYPE, V as USDE_TYPE, W as USDSUI_TYPE, X as USDT_TYPE, Y as WAL_TYPE, Z as WBTC_TYPE, $ as ZkLoginSigner, a0 as addFeeTransfer, aH as assertAllowedAsset, a1 as calculateFee, a2 as classifyAction, a3 as classifyLabel, a4 as classifyTransaction, aI as createSuiClient, a5 as extractAllUserLegs, a6 as extractTransferDetails, a7 as extractTxCommands, a8 as extractTxSender, a9 as fallbackLabel, aa as formatAssetAmount, ab as formatSui, ac as formatUsd, aJ as getCoinMeta, ad as getDecimals, ae as getDecimalsForCoinType, aK as getSuiClient, aL as getSuiGrpcClient, af as getTier, aM as isAllowedAsset, aN as isInRegistry, ag as isSupported, ah as isTier1, ai as isTier2, aj as mapMoveAbortCode, ak as mapWalletError, al as mistToSui, aO as normalizeAsset, aP as normalizeCoinType, am as parseSuiRpcTx, aQ as queryHistory, aR as queryTransaction, an as rawToStable, ao as rawToUsdc, ap as refineLendingLabel, aq as resolveSymbol, ar as resolveTokenType, aS as simulateTransaction, as as stableToRaw, at as suiToMist, aT as throwIfSimulationFailed, au as truncateAddress, av as usdcToRaw, aw as validateAddress } from './types-BTbqE8AE.js';
6
6
  import { L as LendingAdapter, a as LendingRates, P as PendingReward$1 } from './descriptors-DqIb4AnV.js';
7
7
  export { A as AdapterCapability, b as AdapterPositions, c as AdapterTxResult, H as HealthInfo, d as ProtocolDescriptor, e as allDescriptors, n as naviDescriptor } from './descriptors-DqIb4AnV.js';
8
- import { n as T2000Options, P as PayOptions, d as PayResult, o as SwapResult, p as SwapQuoteResult, i as SendResult, B as BalanceResponse, k as TransactionRecord, D as DepositInfo, q as PaymentRequest, S as SaveResult, W as WithdrawResult, b as MaxWithdrawResult, a as BorrowResult, h as RepayResult, M as MaxBorrowResult, H as HealthFactorResult, e as PendingReward, C as ClaimRewardsResult, r as CompoundRewardsResult, g as PositionsResult, R as RatesResult, E as EarningsResult, F as FundStatusResult, c as OverlayFeeConfig, j as SwapRouteResult, s as FinancialSummary } from './types-HJGbXJ37.js';
9
- export { A as AssetRates, G as GasReserve, t as HFAlertLevel, O as OVERLAY_FEE_RATE, f as PositionEntry, u as SerializedCetusRoute, v as SerializedCetusRoutePath, w as SerializedRouterDataV3, T as TransactionLeg, x as addSwapToTx, l as buildSwapTx, y as deserializeCetusRoute, m as findSwapRoute, z as isCetusRouteFresh, I as serializeCetusRoute, J as verifyCetusRouteCoinMatch } from './types-HJGbXJ37.js';
8
+ import { n as T2000Options, P as PayOptions, d as PayResult, o as SwapResult, p as SwapQuoteResult, i as SendResult, B as BalanceResponse, k as TransactionRecord, D as DepositInfo, q as PaymentRequest, S as SaveResult, W as WithdrawResult, b as MaxWithdrawResult, a as BorrowResult, h as RepayResult, M as MaxBorrowResult, H as HealthFactorResult, e as PendingReward, C as ClaimRewardsResult, r as CompoundRewardsResult, g as PositionsResult, R as RatesResult, E as EarningsResult, F as FundStatusResult, c as OverlayFeeConfig, s as SponsoredCoinMergeCache, j as SwapRouteResult, t as FinancialSummary } from './types-DUmk5ivZ.js';
9
+ export { A as AssetRates, u as CoinPage, G as GasReserve, v as HFAlertLevel, O as OVERLAY_FEE_RATE, f as PositionEntry, w as SelectAndSplitResult, x as SerializedCetusRoute, y as SerializedCetusRoutePath, z as SerializedRouterDataV3, T as TransactionLeg, I as addSwapToTx, l as buildSwapTx, J as deserializeCetusRoute, K as fetchAllCoins, m as findSwapRoute, L as isCetusRouteFresh, N as selectAndSplitCoin, Q as selectSuiCoin, U as serializeCetusRoute, V as verifyCetusRouteCoinMatch } from './types-DUmk5ivZ.js';
10
10
  import { Transaction, TransactionObjectArgument } from '@mysten/sui/transactions';
11
11
  import { SuinsClient } from '@mysten/suins';
12
12
  export { NaviAdapter, ProtocolRegistry } from './adapters/index.js';
@@ -497,114 +497,6 @@ declare function buildSendTx({ client, address, to, amount, asset, }: {
497
497
  */
498
498
  declare function addSendToTx(tx: Transaction, coin: TransactionObjectArgument, recipient: string): void;
499
499
 
500
- /**
501
- * Wallet-side coin selection helpers — single source of truth for the
502
- * "produce a `Coin<T>` argument holding `amount` raw units of `coinType`,
503
- * owned by `address`" pattern. Used by every wallet-mode appender that
504
- * needs a coin input (save, send, swap, repay, stake, etc.).
505
- *
506
- * **2026-05-22 — address-balance migration.** Sui mainnet's address-balance
507
- * feature ships funds account-style instead of as discrete `Coin<T>` objects.
508
- * After a payment via `0x2::balance::send_funds`, the leftover lands in the
509
- * sender's address balance with a synthetic "coin reservation" representing
510
- * the deposit. `client.getCoins()` correctly filters those reservations out
511
- * (they aren't real owned objects), so the old fetch+merge+split pattern
512
- * threw `INSUFFICIENT_BALANCE` for users whose stables had drifted into
513
- * address balance — even when `getBalance().totalBalance` showed plenty.
514
- *
515
- * The fix is structural: hand the work to `coinWithBalance({ type, balance })`
516
- * from `@mysten/sui/transactions`. Its build-time resolver inspects coins +
517
- * address balance together (`getBalance` + `listCoins`), then emits the
518
- * right shape — direct `redeem_funds` from address balance when AB ≥
519
- * required, or merge-and-split across coins + AB withdrawal when not. Multi
520
- * intents per coin type get batched into a single merge in one PTB, so the
521
- * old per-PTB merge cache is no longer needed.
522
- *
523
- * Pre-flight uses `client.getBalance().totalBalance` (sums coins + AB)
524
- * instead of summing the paginated `getCoins` page. That's the OTHER half
525
- * of the migration — the legacy path could see `0` from `getCoins` and
526
- * mistakenly throw before `coinWithBalance` ever ran.
527
- */
528
-
529
- interface CoinPage {
530
- ids: string[];
531
- totalBalance: bigint;
532
- }
533
- /**
534
- * Sum every coin of `coinType` owned by `owner`, INCLUDING address balance.
535
- * Returns the IDs of any discrete coin objects that exist (callers
536
- * occasionally need this for non-`coinWithBalance` paths, e.g. SUIns name
537
- * registration which expects raw object IDs).
538
- *
539
- * Pre-2026-05-22 this function paginated `client.getCoins` and summed
540
- * the page balances. That misses address-balance funds (the SDK filters
541
- * them out of `getCoins` for back-compat). The new implementation calls
542
- * `getBalance` for the canonical total and `getCoins` for the optional
543
- * ID list — both round-trips, but they happen in parallel.
544
- */
545
- declare function fetchAllCoins(client: SuiJsonRpcClient, owner: string, coinType: string): Promise<CoinPage>;
546
- interface SelectAndSplitResult {
547
- /** TransactionObjectArgument for a coin holding `effectiveAmount` raw units. */
548
- coin: TransactionObjectArgument;
549
- /** Actual raw amount the returned coin holds. May be < requested if `swapAll` is true. */
550
- effectiveAmount: bigint;
551
- /** True iff the request consumed the entire wallet balance (no split needed). */
552
- swapAll: boolean;
553
- }
554
- /**
555
- * Wallet-mode coin selection prelude. Pre-flights against
556
- * `getBalance().totalBalance` (coins + address balance combined), then
557
- * returns a `coinWithBalance({ type, balance })` argument that the
558
- * `@mysten/sui` resolver fulfills at build time.
559
- *
560
- * Throws `T2000Error` (`INSUFFICIENT_BALANCE`) when:
561
- * - `amount` is bigger than the total balance AND the caller did NOT
562
- * opt into `swapAll: true` clipping.
563
- * - `amount === 'all'` AND total balance is zero.
564
- *
565
- * @param tx — PTB to register the `coinWithBalance` intent against.
566
- * @param client — Sui RPC client for the pre-flight `getBalance` lookup.
567
- * @param owner — wallet address whose coins to source from.
568
- * @param coinType — fully-qualified Sui coin type (e.g. `"0x...::usdc::USDC"`).
569
- * @param amount — raw amount to source (in MIST / smallest unit). Pass
570
- * `'all'` to consume the entire balance.
571
- * @param options.allowSwapAll — if true (default), `amount` >= totalBalance
572
- * auto-clips to total. If false, throws when the request would over-consume.
573
- * @param options.sponsoredContext — when true, source ONLY from discrete coin
574
- * objects (never the address balance). See the long note below — this exists
575
- * because Enoki's gas station can't yet deserialize a `TransactionData` that
576
- * contains the address-balance `FundsWithdrawal` reservation that
577
- * `coinWithBalance` emits. Self-funded callers leave this false: the fullnode
578
- * handles `FundsWithdrawal` fine, so the address-balance path is preferred
579
- * (it can reach funds that aren't held as coin objects).
580
- *
581
- * @returns
582
- * - `coin` — `TransactionObjectArgument` ready for downstream consumption.
583
- * - `effectiveAmount` — the raw amount the returned coin holds (handles
584
- * swapAll clipping).
585
- * - `swapAll` — true iff the entire balance was consumed.
586
- */
587
- declare function selectAndSplitCoin(tx: Transaction, client: SuiJsonRpcClient, owner: string, coinType: string, amount: bigint | 'all', options?: {
588
- allowSwapAll?: boolean;
589
- sponsoredContext?: boolean;
590
- }): Promise<SelectAndSplitResult>;
591
- /**
592
- * SUI-specific coin selection. Branches on sponsorship context:
593
- *
594
- * - **Self-funded (`sponsoredContext: false`)** — splits from `tx.gas`
595
- * directly (the user's gas coin IS their SUI). More efficient — no
596
- * `getBalance` RTT.
597
- *
598
- * - **Sponsored (`sponsoredContext: true`)** — sources from the user's
599
- * discrete SUI coin objects (`selectCoinObjectsOnly`). This both (a) avoids
600
- * `tx.gas`, which belongs to the Enoki sponsor — NOT the user — under
601
- * sponsored flows (the original S.260 reason for `useGasCoin: false`), AND
602
- * (b) avoids `coinWithBalance`'s address-balance `FundsWithdrawal`, which
603
- * Enoki's gas station can't deserialize (issue #93). If the user's SUI is
604
- * address-balance-only, it raises `ADDRESS_BALANCE_UNSPONSORABLE`.
605
- */
606
- declare function selectSuiCoin(tx: Transaction, client: SuiJsonRpcClient, owner: string, amountMist: bigint, sponsoredContext: boolean): Promise<SelectAndSplitResult>;
607
-
608
500
  /**
609
501
  * SPEC 7 v0.4 § Layer 0 — Canonical Write Architecture.
610
502
  *
@@ -981,6 +873,15 @@ interface AppenderContext {
981
873
  * the PTB build fails or the on-chain leg reverts.
982
874
  */
983
875
  isOutputConsumed?: boolean;
876
+ /**
877
+ * Per-PTB merge cache for sponsored SUI sourcing, shared across every
878
+ * appender in a single `composeTx` run. Lets multiple SUI-source swap
879
+ * legs in one bundle reuse a single merged primary coin instead of each
880
+ * re-fetching + re-merging the same coin objects (the second merge of
881
+ * which references already-consumed coins → Enoki dry-run
882
+ * `ArgumentWithoutValue`). See `SponsoredCoinMergeCache` JSDoc.
883
+ */
884
+ suiMergeCache?: SponsoredCoinMergeCache;
984
885
  }
985
886
  /**
986
887
  * [SPEC 13 Phase 1] Appender return shape. Producers populate
@@ -1498,4 +1399,4 @@ declare function fullHandle(label: string): string;
1498
1399
  */
1499
1400
  declare function displayHandle(label: string): string;
1500
1401
 
1501
- export { AUDRIC_PARENT_NAME, AUDRIC_PARENT_NFT_ID, type AppenderContext, BalanceResponse, type BorrowInput, BorrowResult, type BuildAddLeafParams, type BuildHarvestRewardsTxOptions, type BuildRevokeLeafParams, type ClaimRewardsInput, ClaimRewardsResult, type CoinPage, type ComposeTxFeeHookContext, type ComposeTxFeeHooks, type ComposeTxOptions, type ComposeTxResult, CompoundRewardsResult, ContactManager, DepositInfo, EarningsResult, FinancialSummary, type FinancialSummaryOptions, FundStatusResult, HF_CRITICAL_THRESHOLD, HF_WARN_THRESHOLD, type HarvestPlan, type HarvestSkippedLeg, type HarvestSwapLeg, HealthFactorResult, InvalidAddressError, type LabelValidationResult, LendingAdapter, LendingRates, MaxBorrowResult, MaxWithdrawResult, type NormalizedAddress, OverlayFeeConfig, PayOptions, PayResult, PaymentRequest, PendingReward, PositionsResult, RatesResult, type RepayDebtInput, RepayResult, SPONSORED_PYTH_DEPENDENT_PROVIDERS, SUINS_NAME_REGEX, SUI_ADDRESS_REGEX, SUI_ADDRESS_STRICT_REGEX, SafeguardConfig, SafeguardEnforcer, type SaveDepositInput, SaveResult, type SelectAndSplitResult, SendResult, type SendTransferInput, SendableAsset, type StepPreview, SuinsNotRegisteredError, SuinsRpcError, SupportedAsset, type SwapExecuteInput, SwapQuoteResult, SwapResult, SwapRouteResult, T2000, T2000Error, T2000Options, TransactionRecord, TransactionSigner, TxMetadata, WRITE_APPENDER_REGISTRY, type WithdrawInput, WithdrawResult, type WriteStep, type WriteToolName, ZkLoginProof, addClaimRewardsToTx, addSendToTx, aggregateClaimableRewards, buildAddLeafTx, buildClaimRewardsTx, buildHarvestRewardsTx, buildRevokeLeafTx, buildSendTx, composeTx, deriveAllowedAddressesFromPtb, displayHandle, exportPrivateKey, fetchAllCoins, fullHandle, generateKeypair, getAddress, getFinancialSummary, getPendingRewards, getPendingRewardsByAddress, getRates, getSponsoredSwapProviders, getSwapQuote, keypairFromPrivateKey, loadKey, looksLikeSuiNs, normalizeAddressInput, resolveAddressToSuinsViaRpc, resolveSuinsViaRpc, saveBech32, saveKey, selectAndSplitCoin, selectSuiCoin, validateLabel, walletExists };
1402
+ export { AUDRIC_PARENT_NAME, AUDRIC_PARENT_NFT_ID, type AppenderContext, BalanceResponse, type BorrowInput, BorrowResult, type BuildAddLeafParams, type BuildHarvestRewardsTxOptions, type BuildRevokeLeafParams, type ClaimRewardsInput, ClaimRewardsResult, type ComposeTxFeeHookContext, type ComposeTxFeeHooks, type ComposeTxOptions, type ComposeTxResult, CompoundRewardsResult, ContactManager, DepositInfo, EarningsResult, FinancialSummary, type FinancialSummaryOptions, FundStatusResult, HF_CRITICAL_THRESHOLD, HF_WARN_THRESHOLD, type HarvestPlan, type HarvestSkippedLeg, type HarvestSwapLeg, HealthFactorResult, InvalidAddressError, type LabelValidationResult, LendingAdapter, LendingRates, MaxBorrowResult, MaxWithdrawResult, type NormalizedAddress, OverlayFeeConfig, PayOptions, PayResult, PaymentRequest, PendingReward, PositionsResult, RatesResult, type RepayDebtInput, RepayResult, SPONSORED_PYTH_DEPENDENT_PROVIDERS, SUINS_NAME_REGEX, SUI_ADDRESS_REGEX, SUI_ADDRESS_STRICT_REGEX, SafeguardConfig, SafeguardEnforcer, type SaveDepositInput, SaveResult, SendResult, type SendTransferInput, SendableAsset, type StepPreview, SuinsNotRegisteredError, SuinsRpcError, SupportedAsset, type SwapExecuteInput, SwapQuoteResult, SwapResult, SwapRouteResult, T2000, T2000Error, T2000Options, TransactionRecord, TransactionSigner, TxMetadata, WRITE_APPENDER_REGISTRY, type WithdrawInput, WithdrawResult, type WriteStep, type WriteToolName, ZkLoginProof, addClaimRewardsToTx, addSendToTx, aggregateClaimableRewards, buildAddLeafTx, buildClaimRewardsTx, buildHarvestRewardsTx, buildRevokeLeafTx, buildSendTx, composeTx, deriveAllowedAddressesFromPtb, displayHandle, exportPrivateKey, fullHandle, generateKeypair, getAddress, getFinancialSummary, getPendingRewards, getPendingRewardsByAddress, getRates, getSponsoredSwapProviders, getSwapQuote, keypairFromPrivateKey, loadKey, looksLikeSuiNs, normalizeAddressInput, resolveAddressToSuinsViaRpc, resolveSuinsViaRpc, saveBech32, saveKey, validateLabel, walletExists };
package/dist/index.js CHANGED
@@ -692,7 +692,23 @@ async function selectAndSplitCoin(tx, client, owner, coinType, amount, options =
692
692
  const coin = coinWithBalance({ type: coinType, balance: effectiveAmount })(tx);
693
693
  return { coin, effectiveAmount, swapAll };
694
694
  }
695
- async function selectCoinObjectsOnly(tx, client, owner, coinType, amount, allowSwapAll) {
695
+ async function selectCoinObjectsOnly(tx, client, owner, coinType, amount, allowSwapAll, mergeCache) {
696
+ const cached = mergeCache?.get(coinType);
697
+ if (cached) {
698
+ const requested2 = amount === "all" ? cached.remaining : amount;
699
+ if (cached.remaining === 0n || requested2 > cached.remaining) {
700
+ throw new T2000Error(
701
+ "ADDRESS_BALANCE_UNSPONSORABLE",
702
+ `Not enough ${coinType} in coin objects to cover all legs of this sponsored bundle. The remaining funds are in your address balance, which sponsored transactions can't access yet.`,
703
+ { remaining: cached.remaining.toString(), requested: requested2.toString(), coinType }
704
+ );
705
+ }
706
+ const swapAll2 = amount === "all" || requested2 >= cached.remaining;
707
+ const effectiveAmount2 = swapAll2 ? cached.remaining : requested2;
708
+ const coin2 = swapAll2 ? cached.primary : tx.splitCoins(cached.primary, [effectiveAmount2])[0];
709
+ cached.remaining -= effectiveAmount2;
710
+ return { coin: coin2, effectiveAmount: effectiveAmount2, swapAll: swapAll2 };
711
+ }
696
712
  const objects = [];
697
713
  let coinObjectTotal = 0n;
698
714
  let cursor;
@@ -731,12 +747,16 @@ async function selectCoinObjectsOnly(tx, client, owner, coinType, amount, allowS
731
747
  );
732
748
  }
733
749
  const coin = swapAll ? primary : tx.splitCoins(primary, [effectiveAmount])[0];
750
+ mergeCache?.set(coinType, {
751
+ primary,
752
+ remaining: coinObjectTotal - effectiveAmount
753
+ });
734
754
  return { coin, effectiveAmount, swapAll };
735
755
  }
736
- async function selectSuiCoin(tx, client, owner, amountMist, sponsoredContext) {
756
+ async function selectSuiCoin(tx, client, owner, amountMist, sponsoredContext, mergeCache) {
737
757
  if (sponsoredContext) {
738
758
  const { SUI_TYPE: SUI_TYPE2 } = await Promise.resolve().then(() => (init_token_registry(), token_registry_exports));
739
- return selectCoinObjectsOnly(tx, client, owner, SUI_TYPE2, amountMist, false);
759
+ return selectCoinObjectsOnly(tx, client, owner, SUI_TYPE2, amountMist, false, mergeCache);
740
760
  }
741
761
  const [coin] = tx.splitCoins(tx.gas, [amountMist]);
742
762
  return { coin, effectiveAmount: amountMist, swapAll: false };
@@ -958,7 +978,8 @@ async function addSwapToTx(tx, client, address, input) {
958
978
  client,
959
979
  address,
960
980
  requestedRaw,
961
- input.sponsoredContext ?? false
981
+ input.sponsoredContext ?? false,
982
+ input.suiMergeCache
962
983
  );
963
984
  inputCoin = result.coin;
964
985
  effectiveRaw = result.effectiveAmount;
@@ -8126,7 +8147,8 @@ var WRITE_APPENDER_REGISTRY = {
8126
8147
  providers,
8127
8148
  inputCoin: ctx.chainedCoin,
8128
8149
  precomputedRoute: input.precomputedRoute,
8129
- sponsoredContext: ctx.sponsoredContext
8150
+ sponsoredContext: ctx.sponsoredContext,
8151
+ suiMergeCache: ctx.suiMergeCache
8130
8152
  });
8131
8153
  if (!ctx.isOutputConsumed) {
8132
8154
  tx.transferObjects([result.coin], ctx.sender);
@@ -8229,7 +8251,11 @@ async function composeTx(opts) {
8229
8251
  sender: opts.sender,
8230
8252
  sponsoredContext: opts.sponsoredContext ?? false,
8231
8253
  overlayFee: opts.overlayFee,
8232
- feeHooks: opts.feeHooks
8254
+ feeHooks: opts.feeHooks,
8255
+ // One cache per compose run — shared across all legs so multi-leg
8256
+ // sponsored bundles that source the same coin (e.g. two SUI swaps)
8257
+ // merge that coin's objects exactly once.
8258
+ suiMergeCache: /* @__PURE__ */ new Map()
8233
8259
  };
8234
8260
  const consumedSteps = /* @__PURE__ */ new Set();
8235
8261
  for (let i = 0; i < opts.steps.length; i++) {