@msafe/sui3-sdk 1.0.20 → 1.0.22

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
@@ -352,6 +352,18 @@ declare class InsufficientGasFundsError extends Error {
352
352
  * storageRebate is refunded after execution and must not reduce the budget.
353
353
  */
354
354
  declare function computeGasBudget(gasUsed: Pick<SuiClientTypes.GasCostSummary, 'computationCost' | 'storageCost'>, gasPrice: bigint): bigint;
355
+ /** Object IDs a built payload already pins (inputs + gas payment). */
356
+ declare function collectPayloadObjectIds(payload: string | Uint8Array): string[];
357
+ /**
358
+ * Parked queue item: intention was saved with a built hex payload + digest
359
+ * so build-next can promote it without rebuilding.
360
+ */
361
+ declare function parkedPayloadHex(intention: unknown): string | undefined;
362
+ /** Coins already pinned by the current pending tx and any parked future payloads. */
363
+ declare function collectQueueExcludeObjectIds(input: {
364
+ pendingPayload?: string;
365
+ parkedPayloads?: Array<string | undefined>;
366
+ }): string[];
355
367
  /**
356
368
  * Select gas funding before tx.build() according to mixed coin/addressBalance policy:
357
369
  * 1) budget without storageRebate (caller supplies computeGasBudget result)
@@ -365,12 +377,15 @@ declare function selectGasFunding(input: {
365
377
  suiClient: SuiGrpcClient;
366
378
  owner: string;
367
379
  gasBudget: bigint;
380
+ excludeObjectIds?: Iterable<string>;
368
381
  }): Promise<GasFundingResult>;
369
382
  declare function estimateGasBudget(input: {
370
383
  tx: Transaction;
371
384
  suiClient: SuiGrpcClient;
372
385
  owner: string;
373
386
  gasPrice: bigint;
387
+ excludeObjectIds?: Iterable<string>;
388
+ preferAddressBalance?: boolean;
374
389
  }): Promise<bigint>;
375
390
  /**
376
391
  * Resolve gasBudget (no rebate) and apply gas funding selection before build.
@@ -381,6 +396,16 @@ declare function prepareGasFunding(input: {
381
396
  owner: string;
382
397
  gasPrice: bigint;
383
398
  gasBudget?: bigint;
399
+ /**
400
+ * Force Address Balance gas (`payment: []`). Prefer {@link excludeObjectIds}
401
+ * for queue-behind-pending so unused SUI coins can still pay.
402
+ */
403
+ preferAddressBalance?: boolean;
404
+ /**
405
+ * Queue-behind-pending: do not pick coins the current pending / parked
406
+ * payloads already pin. Those versions will change when the earlier tx executes.
407
+ */
408
+ excludeObjectIds?: Iterable<string>;
384
409
  }): Promise<GasFundingResult>;
385
410
  /**
386
411
  * @deprecated Use {@link prepareGasFunding} / {@link selectGasFunding}.
@@ -398,7 +423,12 @@ declare class Simulator {
398
423
  simulate(input: {
399
424
  txb: Transaction;
400
425
  sender: string;
426
+ preferAddressBalance?: boolean;
427
+ excludeObjectIds?: Iterable<string>;
401
428
  }): Promise<SimulationResult>;
429
+ /** Dry-run a frozen payload without re-selecting gas. */
430
+ simulateBuilt(built: Uint8Array): Promise<SimulationResult>;
431
+ private inspectBuilt;
402
432
  private getGasPrice;
403
433
  private toGasBudget;
404
434
  private copyTransaction;
@@ -440,10 +470,14 @@ declare class MSafeAccount {
440
470
  nextSequenceNumber(): Promise<number>;
441
471
  simulateIntention(request: Omit<IProposeIntentionRequest, 'msafeAddress' | 'signature'> & {
442
472
  txb?: Transaction;
473
+ preferAddressBalance?: boolean;
474
+ excludeObjectIds?: Iterable<string>;
443
475
  }): Promise<SimulationResult>;
444
476
  proposeIntention(input: Omit<IProposeIntentionRequest, 'signature' | 'msafeAddress'>): Promise<void>;
445
477
  proposeIntentionAndBuildVote(input: Omit<IProposeIntentionAndBuildAndVoteRequest, 'signature' | 'msafeAddress' | 'payload' | 'digest'> & {
446
478
  txb?: Transaction;
479
+ preferAddressBalance?: boolean;
480
+ excludeObjectIds?: Iterable<string>;
447
481
  }): Promise<void>;
448
482
  simulateCoinTransferIntention(intention: CoinTransferIntention): Promise<SimulationResult>;
449
483
  proposeCoinTransferIntention(intention: CoinTransferIntention): Promise<void>;
@@ -467,6 +501,13 @@ declare class MSafeAccount {
467
501
  events: true;
468
502
  }>>;
469
503
  dashboard(): Promise<_msafe_sui3_utils.IGetDashboardResp>;
504
+ /**
505
+ * Resolve a Transaction from propose/simulate input.
506
+ * 1. Caller-supplied `txb` (generic store path — do not rebuild).
507
+ * 2. Registered app helper `build`.
508
+ * 3. Unregistered app: restore from `intention.content` (JSON or hex).
509
+ */
510
+ private buildTxFromIntention;
470
511
  private calculateWeightFromVotes;
471
512
  private calculateWeight;
472
513
  get address(): string;
@@ -595,5 +636,11 @@ declare function createCoinReservationRef(reservedBalance: bigint, owner: string
595
636
  * (V1 drops CallArg::FundsWithdrawal).
596
637
  */
597
638
  declare function toSuiTransaction(txb: Transaction | unknown): Transaction;
639
+ declare function getIntentionContent(intention: unknown): unknown;
640
+ /**
641
+ * Restore a Transaction from plain-tx `intention.content`.
642
+ * Accepts V2 JSON (`tx.toJSON()`), hex BCS bytes, or a JSON object.
643
+ */
644
+ declare function transactionFromContent(content: unknown): Transaction;
598
645
 
599
- export { AddressBookSDK, COIN_RESERVATION_MAGIC, COIN_TYPE_ARG_REGEX, Coin, CoinHelper, CreateHelper, type CreateMSafeInfo, DEV_API_URL, DEV_SYNCING_URL, ENV_CONFIGS, Formatter, type FutureIntention, type GasFundingMode, type GasFundingResult, type GasObjectReference, HexToUint8Array, type HistorySendTx, type HistoryVote, type IWallet, InfoTypeEnabledDto, InfoTypeKey, InsufficientGasFundsError, LOCAL_API_URL, LOCAL_SYNCING_URL, MAINNET_RPC_URL, MSafeAccount, MSafeClient, type MSafeConfig, type MSafeConfigOptions, MSafeEnabledDto, MSafeEnv, MSafeGlobals, type NotificationData, type NotificationInfo, NotificationSubscribeDto, NotificationUpdateDto, PREV_API_URL, PROD_API_URL, type Pagination, type PendingTx, type PendingVote, type ProposeIntention, type ReceiveTransaction, SUI_COIN, SignatureVerifier, type SimulateDryRunResponse, type SimulationFailedResult, type SimulationOption, type SimulationResult, type SimulationSuccessResult, TESTNET_RPC_URL, Uint8ArrayToHex, addPrefix, computeGasBudget, createCoinReservationRef, estimateGasBudget, getAllCoins, getMSafeConfig, getPublicKeyFromChain, httpUrlToGrpcBaseUrl, isCoinReservationDigest, msafeChainToSuiNetwork, preferClassicGasPayment, prepareGasFunding, selectGasFunding, stringToBuffer, toSuiTransaction };
646
+ export { AddressBookSDK, COIN_RESERVATION_MAGIC, COIN_TYPE_ARG_REGEX, Coin, CoinHelper, CreateHelper, type CreateMSafeInfo, DEV_API_URL, DEV_SYNCING_URL, ENV_CONFIGS, Formatter, type FutureIntention, type GasFundingMode, type GasFundingResult, type GasObjectReference, HexToUint8Array, type HistorySendTx, type HistoryVote, type IWallet, InfoTypeEnabledDto, InfoTypeKey, InsufficientGasFundsError, LOCAL_API_URL, LOCAL_SYNCING_URL, MAINNET_RPC_URL, MSafeAccount, MSafeClient, type MSafeConfig, type MSafeConfigOptions, MSafeEnabledDto, MSafeEnv, MSafeGlobals, type NotificationData, type NotificationInfo, NotificationSubscribeDto, NotificationUpdateDto, PREV_API_URL, PROD_API_URL, type Pagination, type PendingTx, type PendingVote, type ProposeIntention, type ReceiveTransaction, SUI_COIN, SignatureVerifier, type SimulateDryRunResponse, type SimulationFailedResult, type SimulationOption, type SimulationResult, type SimulationSuccessResult, TESTNET_RPC_URL, Uint8ArrayToHex, addPrefix, collectPayloadObjectIds, collectQueueExcludeObjectIds, computeGasBudget, createCoinReservationRef, estimateGasBudget, getAllCoins, getIntentionContent, getMSafeConfig, getPublicKeyFromChain, httpUrlToGrpcBaseUrl, isCoinReservationDigest, msafeChainToSuiNetwork, parkedPayloadHex, preferClassicGasPayment, prepareGasFunding, selectGasFunding, stringToBuffer, toSuiTransaction, transactionFromContent };
package/dist/index.d.ts CHANGED
@@ -352,6 +352,18 @@ declare class InsufficientGasFundsError extends Error {
352
352
  * storageRebate is refunded after execution and must not reduce the budget.
353
353
  */
354
354
  declare function computeGasBudget(gasUsed: Pick<SuiClientTypes.GasCostSummary, 'computationCost' | 'storageCost'>, gasPrice: bigint): bigint;
355
+ /** Object IDs a built payload already pins (inputs + gas payment). */
356
+ declare function collectPayloadObjectIds(payload: string | Uint8Array): string[];
357
+ /**
358
+ * Parked queue item: intention was saved with a built hex payload + digest
359
+ * so build-next can promote it without rebuilding.
360
+ */
361
+ declare function parkedPayloadHex(intention: unknown): string | undefined;
362
+ /** Coins already pinned by the current pending tx and any parked future payloads. */
363
+ declare function collectQueueExcludeObjectIds(input: {
364
+ pendingPayload?: string;
365
+ parkedPayloads?: Array<string | undefined>;
366
+ }): string[];
355
367
  /**
356
368
  * Select gas funding before tx.build() according to mixed coin/addressBalance policy:
357
369
  * 1) budget without storageRebate (caller supplies computeGasBudget result)
@@ -365,12 +377,15 @@ declare function selectGasFunding(input: {
365
377
  suiClient: SuiGrpcClient;
366
378
  owner: string;
367
379
  gasBudget: bigint;
380
+ excludeObjectIds?: Iterable<string>;
368
381
  }): Promise<GasFundingResult>;
369
382
  declare function estimateGasBudget(input: {
370
383
  tx: Transaction;
371
384
  suiClient: SuiGrpcClient;
372
385
  owner: string;
373
386
  gasPrice: bigint;
387
+ excludeObjectIds?: Iterable<string>;
388
+ preferAddressBalance?: boolean;
374
389
  }): Promise<bigint>;
375
390
  /**
376
391
  * Resolve gasBudget (no rebate) and apply gas funding selection before build.
@@ -381,6 +396,16 @@ declare function prepareGasFunding(input: {
381
396
  owner: string;
382
397
  gasPrice: bigint;
383
398
  gasBudget?: bigint;
399
+ /**
400
+ * Force Address Balance gas (`payment: []`). Prefer {@link excludeObjectIds}
401
+ * for queue-behind-pending so unused SUI coins can still pay.
402
+ */
403
+ preferAddressBalance?: boolean;
404
+ /**
405
+ * Queue-behind-pending: do not pick coins the current pending / parked
406
+ * payloads already pin. Those versions will change when the earlier tx executes.
407
+ */
408
+ excludeObjectIds?: Iterable<string>;
384
409
  }): Promise<GasFundingResult>;
385
410
  /**
386
411
  * @deprecated Use {@link prepareGasFunding} / {@link selectGasFunding}.
@@ -398,7 +423,12 @@ declare class Simulator {
398
423
  simulate(input: {
399
424
  txb: Transaction;
400
425
  sender: string;
426
+ preferAddressBalance?: boolean;
427
+ excludeObjectIds?: Iterable<string>;
401
428
  }): Promise<SimulationResult>;
429
+ /** Dry-run a frozen payload without re-selecting gas. */
430
+ simulateBuilt(built: Uint8Array): Promise<SimulationResult>;
431
+ private inspectBuilt;
402
432
  private getGasPrice;
403
433
  private toGasBudget;
404
434
  private copyTransaction;
@@ -440,10 +470,14 @@ declare class MSafeAccount {
440
470
  nextSequenceNumber(): Promise<number>;
441
471
  simulateIntention(request: Omit<IProposeIntentionRequest, 'msafeAddress' | 'signature'> & {
442
472
  txb?: Transaction;
473
+ preferAddressBalance?: boolean;
474
+ excludeObjectIds?: Iterable<string>;
443
475
  }): Promise<SimulationResult>;
444
476
  proposeIntention(input: Omit<IProposeIntentionRequest, 'signature' | 'msafeAddress'>): Promise<void>;
445
477
  proposeIntentionAndBuildVote(input: Omit<IProposeIntentionAndBuildAndVoteRequest, 'signature' | 'msafeAddress' | 'payload' | 'digest'> & {
446
478
  txb?: Transaction;
479
+ preferAddressBalance?: boolean;
480
+ excludeObjectIds?: Iterable<string>;
447
481
  }): Promise<void>;
448
482
  simulateCoinTransferIntention(intention: CoinTransferIntention): Promise<SimulationResult>;
449
483
  proposeCoinTransferIntention(intention: CoinTransferIntention): Promise<void>;
@@ -467,6 +501,13 @@ declare class MSafeAccount {
467
501
  events: true;
468
502
  }>>;
469
503
  dashboard(): Promise<_msafe_sui3_utils.IGetDashboardResp>;
504
+ /**
505
+ * Resolve a Transaction from propose/simulate input.
506
+ * 1. Caller-supplied `txb` (generic store path — do not rebuild).
507
+ * 2. Registered app helper `build`.
508
+ * 3. Unregistered app: restore from `intention.content` (JSON or hex).
509
+ */
510
+ private buildTxFromIntention;
470
511
  private calculateWeightFromVotes;
471
512
  private calculateWeight;
472
513
  get address(): string;
@@ -595,5 +636,11 @@ declare function createCoinReservationRef(reservedBalance: bigint, owner: string
595
636
  * (V1 drops CallArg::FundsWithdrawal).
596
637
  */
597
638
  declare function toSuiTransaction(txb: Transaction | unknown): Transaction;
639
+ declare function getIntentionContent(intention: unknown): unknown;
640
+ /**
641
+ * Restore a Transaction from plain-tx `intention.content`.
642
+ * Accepts V2 JSON (`tx.toJSON()`), hex BCS bytes, or a JSON object.
643
+ */
644
+ declare function transactionFromContent(content: unknown): Transaction;
598
645
 
599
- export { AddressBookSDK, COIN_RESERVATION_MAGIC, COIN_TYPE_ARG_REGEX, Coin, CoinHelper, CreateHelper, type CreateMSafeInfo, DEV_API_URL, DEV_SYNCING_URL, ENV_CONFIGS, Formatter, type FutureIntention, type GasFundingMode, type GasFundingResult, type GasObjectReference, HexToUint8Array, type HistorySendTx, type HistoryVote, type IWallet, InfoTypeEnabledDto, InfoTypeKey, InsufficientGasFundsError, LOCAL_API_URL, LOCAL_SYNCING_URL, MAINNET_RPC_URL, MSafeAccount, MSafeClient, type MSafeConfig, type MSafeConfigOptions, MSafeEnabledDto, MSafeEnv, MSafeGlobals, type NotificationData, type NotificationInfo, NotificationSubscribeDto, NotificationUpdateDto, PREV_API_URL, PROD_API_URL, type Pagination, type PendingTx, type PendingVote, type ProposeIntention, type ReceiveTransaction, SUI_COIN, SignatureVerifier, type SimulateDryRunResponse, type SimulationFailedResult, type SimulationOption, type SimulationResult, type SimulationSuccessResult, TESTNET_RPC_URL, Uint8ArrayToHex, addPrefix, computeGasBudget, createCoinReservationRef, estimateGasBudget, getAllCoins, getMSafeConfig, getPublicKeyFromChain, httpUrlToGrpcBaseUrl, isCoinReservationDigest, msafeChainToSuiNetwork, preferClassicGasPayment, prepareGasFunding, selectGasFunding, stringToBuffer, toSuiTransaction };
646
+ export { AddressBookSDK, COIN_RESERVATION_MAGIC, COIN_TYPE_ARG_REGEX, Coin, CoinHelper, CreateHelper, type CreateMSafeInfo, DEV_API_URL, DEV_SYNCING_URL, ENV_CONFIGS, Formatter, type FutureIntention, type GasFundingMode, type GasFundingResult, type GasObjectReference, HexToUint8Array, type HistorySendTx, type HistoryVote, type IWallet, InfoTypeEnabledDto, InfoTypeKey, InsufficientGasFundsError, LOCAL_API_URL, LOCAL_SYNCING_URL, MAINNET_RPC_URL, MSafeAccount, MSafeClient, type MSafeConfig, type MSafeConfigOptions, MSafeEnabledDto, MSafeEnv, MSafeGlobals, type NotificationData, type NotificationInfo, NotificationSubscribeDto, NotificationUpdateDto, PREV_API_URL, PROD_API_URL, type Pagination, type PendingTx, type PendingVote, type ProposeIntention, type ReceiveTransaction, SUI_COIN, SignatureVerifier, type SimulateDryRunResponse, type SimulationFailedResult, type SimulationOption, type SimulationResult, type SimulationSuccessResult, TESTNET_RPC_URL, Uint8ArrayToHex, addPrefix, collectPayloadObjectIds, collectQueueExcludeObjectIds, computeGasBudget, createCoinReservationRef, estimateGasBudget, getAllCoins, getIntentionContent, getMSafeConfig, getPublicKeyFromChain, httpUrlToGrpcBaseUrl, isCoinReservationDigest, msafeChainToSuiNetwork, parkedPayloadHex, preferClassicGasPayment, prepareGasFunding, selectGasFunding, stringToBuffer, toSuiTransaction, transactionFromContent };
package/dist/index.js CHANGED
@@ -134,7 +134,7 @@ import {
134
134
  isSameAddress as isSameAddress2
135
135
  } from "@msafe/sui3-utils";
136
136
  import { Transaction as Transaction4 } from "@mysten/sui/transactions";
137
- import { fromHex as fromHex2, normalizeStructTag as normalizeStructTag4, toHex as toHex2 } from "@mysten/sui/utils";
137
+ import { fromHex as fromHex4, normalizeStructTag as normalizeStructTag4, toHex as toHex2 } from "@mysten/sui/utils";
138
138
  import { SUI_MAINNET_CHAIN, SUI_TESTNET_CHAIN } from "@mysten/wallet-standard";
139
139
 
140
140
  // src/simulate/simulator.ts
@@ -142,7 +142,7 @@ import { Transaction as Transaction2 } from "@mysten/sui/transactions";
142
142
 
143
143
  // src/utils/gasFunding.ts
144
144
  import { Transaction } from "@mysten/sui/transactions";
145
- import { normalizeStructTag as normalizeStructTag3, normalizeSuiObjectId } from "@mysten/sui/utils";
145
+ import { fromHex as fromHex2, normalizeStructTag as normalizeStructTag3, normalizeSuiObjectId } from "@mysten/sui/utils";
146
146
 
147
147
  // src/utils/coinReservation.ts
148
148
  import { bcs, TypeTagSerializer } from "@mysten/sui/bcs";
@@ -437,18 +437,66 @@ function computeGasBudget(gasUsed, gasPrice) {
437
437
  return BigInt(computationCost) + BigInt(storageCost) + safeOverhead;
438
438
  }
439
439
  function collectUsedObjectIds(tx) {
440
- return tx.getData().inputs.reduce((used, input) => {
440
+ const used = tx.getData().inputs.reduce((acc, input) => {
441
441
  const immOrOwned = input.Object?.ImmOrOwnedObject?.objectId;
442
442
  if (immOrOwned) {
443
- used.add(normalizeSuiObjectId(immOrOwned));
444
- return used;
443
+ acc.add(normalizeSuiObjectId(immOrOwned));
444
+ return acc;
445
445
  }
446
446
  const unresolved = input.UnresolvedObject?.objectId;
447
447
  if (unresolved) {
448
- used.add(normalizeSuiObjectId(unresolved));
448
+ acc.add(normalizeSuiObjectId(unresolved));
449
449
  }
450
- return used;
450
+ return acc;
451
451
  }, /* @__PURE__ */ new Set());
452
+ (tx.getData().gasData.payment ?? []).forEach((payment) => {
453
+ if (payment.objectId) {
454
+ used.add(normalizeSuiObjectId(payment.objectId));
455
+ }
456
+ });
457
+ return used;
458
+ }
459
+ function addExcludeObjectIds(used, excludeObjectIds) {
460
+ Array.from(excludeObjectIds ?? [], (id) => used.add(normalizeSuiObjectId(id)));
461
+ return used;
462
+ }
463
+ function payloadToTransaction(payload) {
464
+ if (typeof payload !== "string") {
465
+ return Transaction.from(payload);
466
+ }
467
+ if (/^[0-9a-fA-F]+$/.test(payload) && payload.length % 2 === 0) {
468
+ return Transaction.from(fromHex2(payload));
469
+ }
470
+ return Transaction.from(payload);
471
+ }
472
+ function collectPayloadObjectIds(payload) {
473
+ return [...collectUsedObjectIds(payloadToTransaction(payload))];
474
+ }
475
+ function parkedPayloadHex(intention) {
476
+ if (!intention || typeof intention !== "object") {
477
+ return void 0;
478
+ }
479
+ const { content, digest } = intention;
480
+ if (typeof content !== "string" || typeof digest !== "string" || !digest) {
481
+ return void 0;
482
+ }
483
+ if (!/^[0-9a-fA-F]+$/.test(content) || content.length < 64) {
484
+ return void 0;
485
+ }
486
+ return content;
487
+ }
488
+ function collectQueueExcludeObjectIds(input) {
489
+ const ids = /* @__PURE__ */ new Set();
490
+ if (input.pendingPayload) {
491
+ collectPayloadObjectIds(input.pendingPayload).forEach((id) => ids.add(id));
492
+ }
493
+ (input.parkedPayloads ?? []).forEach((payload) => {
494
+ if (!payload) {
495
+ return;
496
+ }
497
+ collectPayloadObjectIds(payload).forEach((id) => ids.add(id));
498
+ });
499
+ return [...ids];
452
500
  }
453
501
  function txConsumesOwnedSuiCoins(tx) {
454
502
  return tx.getData().commands.some((cmd) => {
@@ -466,8 +514,8 @@ function txConsumesOwnedSuiCoins(tx) {
466
514
  }
467
515
  });
468
516
  }
469
- async function loadSuiGasFunding(suiClient, owner, tx) {
470
- const usedObjectIds = collectUsedObjectIds(tx);
517
+ async function loadSuiGasFunding(suiClient, owner, tx, excludeObjectIds) {
518
+ const usedObjectIds = addExcludeObjectIds(collectUsedObjectIds(tx), excludeObjectIds);
471
519
  const [coins, balanceRes] = await Promise.all([
472
520
  getAllCoins({ suiClient, owner, coinType: SUI_COIN }),
473
521
  suiClient.getBalance({ owner, coinType: SUI_COIN })
@@ -518,7 +566,7 @@ async function selectGasFunding(input) {
518
566
  const { tx, suiClient, owner, gasBudget } = input;
519
567
  const { payment } = tx.getData().gasData;
520
568
  if (payment != null && payment.length > 0) {
521
- const funding2 = await loadSuiGasFunding(suiClient, owner, tx);
569
+ const funding2 = await loadSuiGasFunding(suiClient, owner, tx, input.excludeObjectIds);
522
570
  return {
523
571
  mode: "classic",
524
572
  gasBudget,
@@ -526,7 +574,7 @@ async function selectGasFunding(input) {
526
574
  addressBalance: funding2.addressBalance
527
575
  };
528
576
  }
529
- const funding = await loadSuiGasFunding(suiClient, owner, tx);
577
+ const funding = await loadSuiGasFunding(suiClient, owner, tx, input.excludeObjectIds);
530
578
  const { paymentCoins, coinBalance, addressBalance, total } = funding;
531
579
  if (total < gasBudget) {
532
580
  throw new InsufficientGasFundsError(gasBudget, coinBalance, addressBalance);
@@ -572,8 +620,13 @@ function isPositiveBudget(budget) {
572
620
  }
573
621
  async function estimateGasBudget(input) {
574
622
  const { tx, suiClient, owner, gasPrice } = input;
575
- const funding = await loadSuiGasFunding(suiClient, owner, tx);
576
- if (funding.total <= 0n) {
623
+ const funding = input.preferAddressBalance ? {
624
+ ...await loadSuiGasFunding(suiClient, owner, tx, input.excludeObjectIds),
625
+ paymentCoins: [],
626
+ coinBalance: 0n
627
+ } : await loadSuiGasFunding(suiClient, owner, tx, input.excludeObjectIds);
628
+ const fundingTotal = input.preferAddressBalance ? funding.addressBalance : funding.total;
629
+ if (fundingTotal <= 0n) {
577
630
  throw new InsufficientGasFundsError(0n, funding.coinBalance, funding.addressBalance);
578
631
  }
579
632
  const { budget, payment } = tx.getData().gasData;
@@ -606,7 +659,7 @@ async function estimateGasBudget(input) {
606
659
  return computeGasBudget(effects.gasUsed, gasPrice);
607
660
  }
608
661
  async function prepareGasFunding(input) {
609
- const { tx, suiClient, owner, gasPrice } = input;
662
+ const { tx, suiClient, owner, gasPrice, excludeObjectIds } = input;
610
663
  const { payment, budget: existingBudget } = tx.getData().gasData;
611
664
  const bakedAddressBalanceGas = payment != null && payment.length === 0;
612
665
  let { gasBudget } = input;
@@ -616,7 +669,14 @@ async function prepareGasFunding(input) {
616
669
  const existingPositive = isPositiveBudget(existingBudget) ? BigInt(existingBudget) : null;
617
670
  if (gasBudget == null) {
618
671
  if (bakedAddressBalanceGas || existingPositive == null) {
619
- gasBudget = await estimateGasBudget({ tx, suiClient, owner, gasPrice });
672
+ gasBudget = await estimateGasBudget({
673
+ tx,
674
+ suiClient,
675
+ owner,
676
+ gasPrice,
677
+ excludeObjectIds,
678
+ preferAddressBalance: input.preferAddressBalance
679
+ });
620
680
  } else {
621
681
  gasBudget = existingPositive;
622
682
  }
@@ -625,7 +685,20 @@ async function prepareGasFunding(input) {
625
685
  if (bakedAddressBalanceGas) {
626
686
  tx.setExpiration(null);
627
687
  }
628
- return selectGasFunding({ tx, suiClient, owner, gasBudget });
688
+ if (input.preferAddressBalance) {
689
+ const funding = await loadSuiGasFunding(suiClient, owner, tx, excludeObjectIds);
690
+ if (funding.addressBalance < gasBudget) {
691
+ throw new InsufficientGasFundsError(gasBudget, funding.coinBalance, funding.addressBalance);
692
+ }
693
+ tx.setGasPayment([]);
694
+ return {
695
+ mode: "addressBalance",
696
+ gasBudget,
697
+ coinBalance: funding.coinBalance,
698
+ addressBalance: funding.addressBalance
699
+ };
700
+ }
701
+ return selectGasFunding({ tx, suiClient, owner, gasBudget, excludeObjectIds });
629
702
  }
630
703
  async function preferClassicGasPayment(input) {
631
704
  const { tx, suiClient, owner } = input;
@@ -668,7 +741,9 @@ var Simulator = class {
668
741
  tx,
669
742
  suiClient,
670
743
  owner: input.sender,
671
- gasPrice
744
+ gasPrice,
745
+ preferAddressBalance: input.preferAddressBalance,
746
+ excludeObjectIds: input.excludeObjectIds
672
747
  });
673
748
  } catch (e) {
674
749
  const message = e instanceof Error ? e.message : String(e);
@@ -689,9 +764,17 @@ var Simulator = class {
689
764
  simulationError: message
690
765
  };
691
766
  }
767
+ return this.inspectBuilt(built, gasPrice);
768
+ }
769
+ /** Dry-run a frozen payload without re-selecting gas. */
770
+ async simulateBuilt(built) {
771
+ const gasPrice = await this.getGasPrice();
772
+ return this.inspectBuilt(built, gasPrice);
773
+ }
774
+ async inspectBuilt(built, gasPrice) {
692
775
  let inspectResult;
693
776
  try {
694
- inspectResult = await suiClient.simulateTransaction({
777
+ inspectResult = await this.globals.suiClient.simulateTransaction({
695
778
  transaction: built,
696
779
  include: {
697
780
  effects: true,
@@ -802,6 +885,7 @@ var SignatureVerifier = class _SignatureVerifier {
802
885
 
803
886
  // src/utils/transaction.ts
804
887
  import { Transaction as Transaction3, isTransaction } from "@mysten/sui/transactions";
888
+ import { fromHex as fromHex3 } from "@mysten/sui/utils";
805
889
  function toSuiTransaction(txb) {
806
890
  if (isTransaction(txb)) {
807
891
  return txb;
@@ -815,6 +899,30 @@ function toSuiTransaction(txb) {
815
899
  }
816
900
  throw new Error("Unsupported transaction value for toSuiTransaction");
817
901
  }
902
+ function isHex(str) {
903
+ return /^[0-9a-fA-F]+$/.test(str);
904
+ }
905
+ function getIntentionContent(intention) {
906
+ if (intention && typeof intention === "object" && "content" in intention) {
907
+ return intention.content;
908
+ }
909
+ return void 0;
910
+ }
911
+ function transactionFromContent(content) {
912
+ if (content instanceof Uint8Array) {
913
+ return Transaction3.from(content);
914
+ }
915
+ if (typeof content === "string") {
916
+ if (isHex(content)) {
917
+ return Transaction3.from(fromHex3(content));
918
+ }
919
+ return Transaction3.from(content);
920
+ }
921
+ if (content && typeof content === "object") {
922
+ return Transaction3.from(JSON.stringify(content));
923
+ }
924
+ throw new Error("Invalid transaction content");
925
+ }
818
926
 
819
927
  // src/utils/iter/iterator.ts
820
928
  var REQUEST_PAGE_SIZE = 25;
@@ -1037,32 +1145,14 @@ var MSafeAccount = class _MSafeAccount {
1037
1145
  return this.backend.getNextSequenceNumber(this.address);
1038
1146
  }
1039
1147
  async simulateIntention(request) {
1040
- let txb;
1041
- if (request.txb) {
1042
- txb = request.txb;
1043
- } else {
1044
- const appHelper = appHelpers.getAppHelper(request.application);
1045
- if (!appHelper) {
1046
- throw new Error(`Can't find app helper for application ${request.application}`);
1047
- }
1048
- txb = toSuiTransaction(
1049
- await appHelper.build({
1050
- network: this.globals.config.network,
1051
- intentionData: request.intention,
1052
- txType: request.txType,
1053
- txSubType: request.txSubType,
1054
- clientUrl: this.globals.config.suiClient.url,
1055
- account: {
1056
- address: this.address,
1057
- publicKey: fromHex2(this.address),
1058
- chains: [SUI_MAINNET_CHAIN, SUI_TESTNET_CHAIN],
1059
- features: []
1060
- }
1061
- })
1062
- );
1063
- }
1148
+ const txb = await this.buildTxFromIntention(request);
1064
1149
  txb.setSender(this.address);
1065
- return this.simulator.simulate({ txb, sender: this.address });
1150
+ return this.simulator.simulate({
1151
+ txb,
1152
+ sender: this.address,
1153
+ preferAddressBalance: request.preferAddressBalance,
1154
+ excludeObjectIds: request.excludeObjectIds
1155
+ });
1066
1156
  }
1067
1157
  async proposeIntention(input) {
1068
1158
  const message = SigningMessageHelper3.proposeIntentionMessage({
@@ -1080,30 +1170,7 @@ var MSafeAccount = class _MSafeAccount {
1080
1170
  });
1081
1171
  }
1082
1172
  async proposeIntentionAndBuildVote(input) {
1083
- let txb;
1084
- if (input.txb) {
1085
- txb = input.txb;
1086
- } else {
1087
- const appHelper = appHelpers.getAppHelper(input.application);
1088
- if (!appHelper) {
1089
- throw new Error(`Can't find app helper for application ${input.application}`);
1090
- }
1091
- txb = toSuiTransaction(
1092
- await appHelper.build({
1093
- network: this.globals.config.network,
1094
- intentionData: input.intention,
1095
- txType: input.txType,
1096
- txSubType: input.txSubType,
1097
- clientUrl: this.globals.config.suiClient.url,
1098
- account: {
1099
- address: this.address,
1100
- publicKey: fromHex2(this.address),
1101
- chains: [SUI_MAINNET_CHAIN, SUI_TESTNET_CHAIN],
1102
- features: []
1103
- }
1104
- })
1105
- );
1106
- }
1173
+ const txb = await this.buildTxFromIntention(input);
1107
1174
  txb.setGasPrice(input.gasPrice);
1108
1175
  txb.setSender(this.address);
1109
1176
  await prepareGasFunding({
@@ -1111,13 +1178,19 @@ var MSafeAccount = class _MSafeAccount {
1111
1178
  suiClient: this.globals.suiClient,
1112
1179
  owner: this.address,
1113
1180
  gasPrice: input.gasPrice,
1114
- gasBudget: input.gasBudget
1181
+ gasBudget: input.gasBudget,
1182
+ preferAddressBalance: input.preferAddressBalance,
1183
+ excludeObjectIds: input.excludeObjectIds
1115
1184
  });
1116
1185
  const payload = await txb.build({ client: this.globals.suiClient });
1117
1186
  const digest = await txb.getDigest({ client: this.globals.suiClient });
1118
1187
  const signature = await this.wallet.signTransactionBlock({ transactionBlock: payload });
1188
+ const apiInput = { ...input };
1189
+ delete apiInput.txb;
1190
+ delete apiInput.preferAddressBalance;
1191
+ delete apiInput.excludeObjectIds;
1119
1192
  return this.backend.proposeIntentionAndBuildVote({
1120
- ...input,
1193
+ ...apiInput,
1121
1194
  payload: toHex2(payload),
1122
1195
  digest,
1123
1196
  msafeAddress: this.address,
@@ -1225,25 +1298,16 @@ var MSafeAccount = class _MSafeAccount {
1225
1298
  }
1226
1299
  async simulateBuildTransaction() {
1227
1300
  const intention = await this.backend.getNextIntention({ msafeAddress: this.address });
1228
- const appHelper = appHelpers.getAppHelper(intention.application);
1229
- if (!appHelper) {
1230
- throw new Error(`Can't find app helper for application ${intention.application}`);
1231
- }
1232
- const txb = toSuiTransaction(
1233
- await appHelper.build({
1234
- network: this.globals.config.network,
1235
- intentionData: intention.intention,
1236
- txType: intention.txType,
1237
- txSubType: intention.txSubType,
1238
- clientUrl: this.globals.config.suiClient.url,
1239
- account: {
1240
- address: this.address,
1241
- publicKey: fromHex2(this.address),
1242
- chains: [SUI_MAINNET_CHAIN, SUI_TESTNET_CHAIN],
1243
- features: []
1244
- }
1245
- })
1246
- );
1301
+ const parked = parkedPayloadHex(intention.intention);
1302
+ if (parked) {
1303
+ return this.simulator.simulateBuilt(fromHex4(parked));
1304
+ }
1305
+ const txb = await this.buildTxFromIntention({
1306
+ application: intention.application,
1307
+ intention: intention.intention,
1308
+ txType: intention.txType,
1309
+ txSubType: intention.txSubType
1310
+ });
1247
1311
  txb.setSender(this.address);
1248
1312
  return this.simulator.simulate({ txb, sender: this.address });
1249
1313
  }
@@ -1288,6 +1352,45 @@ var MSafeAccount = class _MSafeAccount {
1288
1352
  msafeAddress: this.address
1289
1353
  });
1290
1354
  }
1355
+ /**
1356
+ * Resolve a Transaction from propose/simulate input.
1357
+ * 1. Caller-supplied `txb` (generic store path — do not rebuild).
1358
+ * 2. Registered app helper `build`.
1359
+ * 3. Unregistered app: restore from `intention.content` (JSON or hex).
1360
+ */
1361
+ async buildTxFromIntention(input) {
1362
+ if (input.txb) {
1363
+ return input.txb;
1364
+ }
1365
+ let appHelper;
1366
+ try {
1367
+ appHelper = appHelpers.getAppHelper(input.application);
1368
+ } catch {
1369
+ appHelper = void 0;
1370
+ }
1371
+ if (appHelper) {
1372
+ return toSuiTransaction(
1373
+ await appHelper.build({
1374
+ network: this.globals.config.network,
1375
+ intentionData: input.intention,
1376
+ txType: input.txType,
1377
+ txSubType: input.txSubType,
1378
+ clientUrl: this.globals.config.suiClient.url,
1379
+ account: {
1380
+ address: this.address,
1381
+ publicKey: fromHex4(this.address),
1382
+ chains: [SUI_MAINNET_CHAIN, SUI_TESTNET_CHAIN],
1383
+ features: []
1384
+ }
1385
+ })
1386
+ );
1387
+ }
1388
+ const content = getIntentionContent(input.intention);
1389
+ if (content != null) {
1390
+ return transactionFromContent(content);
1391
+ }
1392
+ throw new Error(`Can't find app helper for application ${input.application}`);
1393
+ }
1291
1394
  calculateWeightFromVotes(votes) {
1292
1395
  return this.calculateWeight(votes.map((vote) => vote.userAddress));
1293
1396
  }
@@ -2018,19 +2121,24 @@ export {
2018
2121
  TESTNET_RPC_URL,
2019
2122
  Uint8ArrayToHex,
2020
2123
  addPrefix,
2124
+ collectPayloadObjectIds,
2125
+ collectQueueExcludeObjectIds,
2021
2126
  computeGasBudget,
2022
2127
  createCoinReservationRef,
2023
2128
  estimateGasBudget,
2024
2129
  getAllCoins,
2130
+ getIntentionContent,
2025
2131
  getMSafeConfig,
2026
2132
  getPublicKeyFromChain,
2027
2133
  httpUrlToGrpcBaseUrl,
2028
2134
  isCoinReservationDigest,
2029
2135
  msafeChainToSuiNetwork,
2136
+ parkedPayloadHex,
2030
2137
  preferClassicGasPayment,
2031
2138
  prepareGasFunding,
2032
2139
  selectGasFunding,
2033
2140
  stringToBuffer,
2034
- toSuiTransaction
2141
+ toSuiTransaction,
2142
+ transactionFromContent
2035
2143
  };
2036
2144
  //# sourceMappingURL=index.js.map