@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.cjs CHANGED
@@ -61,20 +61,25 @@ __export(src_exports, {
61
61
  TESTNET_RPC_URL: () => TESTNET_RPC_URL,
62
62
  Uint8ArrayToHex: () => Uint8ArrayToHex,
63
63
  addPrefix: () => addPrefix,
64
+ collectPayloadObjectIds: () => collectPayloadObjectIds,
65
+ collectQueueExcludeObjectIds: () => collectQueueExcludeObjectIds,
64
66
  computeGasBudget: () => computeGasBudget,
65
67
  createCoinReservationRef: () => createCoinReservationRef,
66
68
  estimateGasBudget: () => estimateGasBudget,
67
69
  getAllCoins: () => getAllCoins,
70
+ getIntentionContent: () => getIntentionContent,
68
71
  getMSafeConfig: () => getMSafeConfig,
69
72
  getPublicKeyFromChain: () => getPublicKeyFromChain,
70
73
  httpUrlToGrpcBaseUrl: () => httpUrlToGrpcBaseUrl,
71
74
  isCoinReservationDigest: () => isCoinReservationDigest,
72
75
  msafeChainToSuiNetwork: () => msafeChainToSuiNetwork,
76
+ parkedPayloadHex: () => parkedPayloadHex,
73
77
  preferClassicGasPayment: () => preferClassicGasPayment,
74
78
  prepareGasFunding: () => prepareGasFunding,
75
79
  selectGasFunding: () => selectGasFunding,
76
80
  stringToBuffer: () => stringToBuffer,
77
- toSuiTransaction: () => toSuiTransaction
81
+ toSuiTransaction: () => toSuiTransaction,
82
+ transactionFromContent: () => transactionFromContent
78
83
  });
79
84
  module.exports = __toCommonJS(src_exports);
80
85
 
@@ -198,7 +203,7 @@ var InvitationSDK = class {
198
203
  var import_sui_app_store = require("@msafe/sui-app-store");
199
204
  var import_sui3_utils4 = require("@msafe/sui3-utils");
200
205
  var import_transactions4 = require("@mysten/sui/transactions");
201
- var import_utils5 = require("@mysten/sui/utils");
206
+ var import_utils6 = require("@mysten/sui/utils");
202
207
  var import_wallet_standard = require("@mysten/wallet-standard");
203
208
 
204
209
  // src/simulate/simulator.ts
@@ -501,18 +506,66 @@ function computeGasBudget(gasUsed, gasPrice) {
501
506
  return BigInt(computationCost) + BigInt(storageCost) + safeOverhead;
502
507
  }
503
508
  function collectUsedObjectIds(tx) {
504
- return tx.getData().inputs.reduce((used, input) => {
509
+ const used = tx.getData().inputs.reduce((acc, input) => {
505
510
  const immOrOwned = input.Object?.ImmOrOwnedObject?.objectId;
506
511
  if (immOrOwned) {
507
- used.add((0, import_utils4.normalizeSuiObjectId)(immOrOwned));
508
- return used;
512
+ acc.add((0, import_utils4.normalizeSuiObjectId)(immOrOwned));
513
+ return acc;
509
514
  }
510
515
  const unresolved = input.UnresolvedObject?.objectId;
511
516
  if (unresolved) {
512
- used.add((0, import_utils4.normalizeSuiObjectId)(unresolved));
517
+ acc.add((0, import_utils4.normalizeSuiObjectId)(unresolved));
513
518
  }
514
- return used;
519
+ return acc;
515
520
  }, /* @__PURE__ */ new Set());
521
+ (tx.getData().gasData.payment ?? []).forEach((payment) => {
522
+ if (payment.objectId) {
523
+ used.add((0, import_utils4.normalizeSuiObjectId)(payment.objectId));
524
+ }
525
+ });
526
+ return used;
527
+ }
528
+ function addExcludeObjectIds(used, excludeObjectIds) {
529
+ Array.from(excludeObjectIds ?? [], (id) => used.add((0, import_utils4.normalizeSuiObjectId)(id)));
530
+ return used;
531
+ }
532
+ function payloadToTransaction(payload) {
533
+ if (typeof payload !== "string") {
534
+ return import_transactions.Transaction.from(payload);
535
+ }
536
+ if (/^[0-9a-fA-F]+$/.test(payload) && payload.length % 2 === 0) {
537
+ return import_transactions.Transaction.from((0, import_utils4.fromHex)(payload));
538
+ }
539
+ return import_transactions.Transaction.from(payload);
540
+ }
541
+ function collectPayloadObjectIds(payload) {
542
+ return [...collectUsedObjectIds(payloadToTransaction(payload))];
543
+ }
544
+ function parkedPayloadHex(intention) {
545
+ if (!intention || typeof intention !== "object") {
546
+ return void 0;
547
+ }
548
+ const { content, digest } = intention;
549
+ if (typeof content !== "string" || typeof digest !== "string" || !digest) {
550
+ return void 0;
551
+ }
552
+ if (!/^[0-9a-fA-F]+$/.test(content) || content.length < 64) {
553
+ return void 0;
554
+ }
555
+ return content;
556
+ }
557
+ function collectQueueExcludeObjectIds(input) {
558
+ const ids = /* @__PURE__ */ new Set();
559
+ if (input.pendingPayload) {
560
+ collectPayloadObjectIds(input.pendingPayload).forEach((id) => ids.add(id));
561
+ }
562
+ (input.parkedPayloads ?? []).forEach((payload) => {
563
+ if (!payload) {
564
+ return;
565
+ }
566
+ collectPayloadObjectIds(payload).forEach((id) => ids.add(id));
567
+ });
568
+ return [...ids];
516
569
  }
517
570
  function txConsumesOwnedSuiCoins(tx) {
518
571
  return tx.getData().commands.some((cmd) => {
@@ -530,8 +583,8 @@ function txConsumesOwnedSuiCoins(tx) {
530
583
  }
531
584
  });
532
585
  }
533
- async function loadSuiGasFunding(suiClient, owner, tx) {
534
- const usedObjectIds = collectUsedObjectIds(tx);
586
+ async function loadSuiGasFunding(suiClient, owner, tx, excludeObjectIds) {
587
+ const usedObjectIds = addExcludeObjectIds(collectUsedObjectIds(tx), excludeObjectIds);
535
588
  const [coins, balanceRes] = await Promise.all([
536
589
  getAllCoins({ suiClient, owner, coinType: SUI_COIN }),
537
590
  suiClient.getBalance({ owner, coinType: SUI_COIN })
@@ -582,7 +635,7 @@ async function selectGasFunding(input) {
582
635
  const { tx, suiClient, owner, gasBudget } = input;
583
636
  const { payment } = tx.getData().gasData;
584
637
  if (payment != null && payment.length > 0) {
585
- const funding2 = await loadSuiGasFunding(suiClient, owner, tx);
638
+ const funding2 = await loadSuiGasFunding(suiClient, owner, tx, input.excludeObjectIds);
586
639
  return {
587
640
  mode: "classic",
588
641
  gasBudget,
@@ -590,7 +643,7 @@ async function selectGasFunding(input) {
590
643
  addressBalance: funding2.addressBalance
591
644
  };
592
645
  }
593
- const funding = await loadSuiGasFunding(suiClient, owner, tx);
646
+ const funding = await loadSuiGasFunding(suiClient, owner, tx, input.excludeObjectIds);
594
647
  const { paymentCoins, coinBalance, addressBalance, total } = funding;
595
648
  if (total < gasBudget) {
596
649
  throw new InsufficientGasFundsError(gasBudget, coinBalance, addressBalance);
@@ -636,8 +689,13 @@ function isPositiveBudget(budget) {
636
689
  }
637
690
  async function estimateGasBudget(input) {
638
691
  const { tx, suiClient, owner, gasPrice } = input;
639
- const funding = await loadSuiGasFunding(suiClient, owner, tx);
640
- if (funding.total <= 0n) {
692
+ const funding = input.preferAddressBalance ? {
693
+ ...await loadSuiGasFunding(suiClient, owner, tx, input.excludeObjectIds),
694
+ paymentCoins: [],
695
+ coinBalance: 0n
696
+ } : await loadSuiGasFunding(suiClient, owner, tx, input.excludeObjectIds);
697
+ const fundingTotal = input.preferAddressBalance ? funding.addressBalance : funding.total;
698
+ if (fundingTotal <= 0n) {
641
699
  throw new InsufficientGasFundsError(0n, funding.coinBalance, funding.addressBalance);
642
700
  }
643
701
  const { budget, payment } = tx.getData().gasData;
@@ -670,7 +728,7 @@ async function estimateGasBudget(input) {
670
728
  return computeGasBudget(effects.gasUsed, gasPrice);
671
729
  }
672
730
  async function prepareGasFunding(input) {
673
- const { tx, suiClient, owner, gasPrice } = input;
731
+ const { tx, suiClient, owner, gasPrice, excludeObjectIds } = input;
674
732
  const { payment, budget: existingBudget } = tx.getData().gasData;
675
733
  const bakedAddressBalanceGas = payment != null && payment.length === 0;
676
734
  let { gasBudget } = input;
@@ -680,7 +738,14 @@ async function prepareGasFunding(input) {
680
738
  const existingPositive = isPositiveBudget(existingBudget) ? BigInt(existingBudget) : null;
681
739
  if (gasBudget == null) {
682
740
  if (bakedAddressBalanceGas || existingPositive == null) {
683
- gasBudget = await estimateGasBudget({ tx, suiClient, owner, gasPrice });
741
+ gasBudget = await estimateGasBudget({
742
+ tx,
743
+ suiClient,
744
+ owner,
745
+ gasPrice,
746
+ excludeObjectIds,
747
+ preferAddressBalance: input.preferAddressBalance
748
+ });
684
749
  } else {
685
750
  gasBudget = existingPositive;
686
751
  }
@@ -689,7 +754,20 @@ async function prepareGasFunding(input) {
689
754
  if (bakedAddressBalanceGas) {
690
755
  tx.setExpiration(null);
691
756
  }
692
- return selectGasFunding({ tx, suiClient, owner, gasBudget });
757
+ if (input.preferAddressBalance) {
758
+ const funding = await loadSuiGasFunding(suiClient, owner, tx, excludeObjectIds);
759
+ if (funding.addressBalance < gasBudget) {
760
+ throw new InsufficientGasFundsError(gasBudget, funding.coinBalance, funding.addressBalance);
761
+ }
762
+ tx.setGasPayment([]);
763
+ return {
764
+ mode: "addressBalance",
765
+ gasBudget,
766
+ coinBalance: funding.coinBalance,
767
+ addressBalance: funding.addressBalance
768
+ };
769
+ }
770
+ return selectGasFunding({ tx, suiClient, owner, gasBudget, excludeObjectIds });
693
771
  }
694
772
  async function preferClassicGasPayment(input) {
695
773
  const { tx, suiClient, owner } = input;
@@ -732,7 +810,9 @@ var Simulator = class {
732
810
  tx,
733
811
  suiClient,
734
812
  owner: input.sender,
735
- gasPrice
813
+ gasPrice,
814
+ preferAddressBalance: input.preferAddressBalance,
815
+ excludeObjectIds: input.excludeObjectIds
736
816
  });
737
817
  } catch (e) {
738
818
  const message = e instanceof Error ? e.message : String(e);
@@ -753,9 +833,17 @@ var Simulator = class {
753
833
  simulationError: message
754
834
  };
755
835
  }
836
+ return this.inspectBuilt(built, gasPrice);
837
+ }
838
+ /** Dry-run a frozen payload without re-selecting gas. */
839
+ async simulateBuilt(built) {
840
+ const gasPrice = await this.getGasPrice();
841
+ return this.inspectBuilt(built, gasPrice);
842
+ }
843
+ async inspectBuilt(built, gasPrice) {
756
844
  let inspectResult;
757
845
  try {
758
- inspectResult = await suiClient.simulateTransaction({
846
+ inspectResult = await this.globals.suiClient.simulateTransaction({
759
847
  transaction: built,
760
848
  include: {
761
849
  effects: true,
@@ -866,6 +954,7 @@ var SignatureVerifier = class _SignatureVerifier {
866
954
 
867
955
  // src/utils/transaction.ts
868
956
  var import_transactions3 = require("@mysten/sui/transactions");
957
+ var import_utils5 = require("@mysten/sui/utils");
869
958
  function toSuiTransaction(txb) {
870
959
  if ((0, import_transactions3.isTransaction)(txb)) {
871
960
  return txb;
@@ -879,6 +968,30 @@ function toSuiTransaction(txb) {
879
968
  }
880
969
  throw new Error("Unsupported transaction value for toSuiTransaction");
881
970
  }
971
+ function isHex(str) {
972
+ return /^[0-9a-fA-F]+$/.test(str);
973
+ }
974
+ function getIntentionContent(intention) {
975
+ if (intention && typeof intention === "object" && "content" in intention) {
976
+ return intention.content;
977
+ }
978
+ return void 0;
979
+ }
980
+ function transactionFromContent(content) {
981
+ if (content instanceof Uint8Array) {
982
+ return import_transactions3.Transaction.from(content);
983
+ }
984
+ if (typeof content === "string") {
985
+ if (isHex(content)) {
986
+ return import_transactions3.Transaction.from((0, import_utils5.fromHex)(content));
987
+ }
988
+ return import_transactions3.Transaction.from(content);
989
+ }
990
+ if (content && typeof content === "object") {
991
+ return import_transactions3.Transaction.from(JSON.stringify(content));
992
+ }
993
+ throw new Error("Invalid transaction content");
994
+ }
882
995
 
883
996
  // src/utils/iter/iterator.ts
884
997
  var REQUEST_PAGE_SIZE = 25;
@@ -1045,7 +1158,7 @@ var MSafeAccount = class _MSafeAccount {
1045
1158
  balances.map(async (balance) => {
1046
1159
  const meta = await this.coinHelper.getCoinMeta(balance.coinType);
1047
1160
  return {
1048
- type: (0, import_utils5.normalizeStructTag)(balance.coinType),
1161
+ type: (0, import_utils6.normalizeStructTag)(balance.coinType),
1049
1162
  balance: BigInt(balance.addressBalance),
1050
1163
  metadata: meta
1051
1164
  };
@@ -1101,32 +1214,14 @@ var MSafeAccount = class _MSafeAccount {
1101
1214
  return this.backend.getNextSequenceNumber(this.address);
1102
1215
  }
1103
1216
  async simulateIntention(request) {
1104
- let txb;
1105
- if (request.txb) {
1106
- txb = request.txb;
1107
- } else {
1108
- const appHelper = import_sui_app_store.appHelpers.getAppHelper(request.application);
1109
- if (!appHelper) {
1110
- throw new Error(`Can't find app helper for application ${request.application}`);
1111
- }
1112
- txb = toSuiTransaction(
1113
- await appHelper.build({
1114
- network: this.globals.config.network,
1115
- intentionData: request.intention,
1116
- txType: request.txType,
1117
- txSubType: request.txSubType,
1118
- clientUrl: this.globals.config.suiClient.url,
1119
- account: {
1120
- address: this.address,
1121
- publicKey: (0, import_utils5.fromHex)(this.address),
1122
- chains: [import_wallet_standard.SUI_MAINNET_CHAIN, import_wallet_standard.SUI_TESTNET_CHAIN],
1123
- features: []
1124
- }
1125
- })
1126
- );
1127
- }
1217
+ const txb = await this.buildTxFromIntention(request);
1128
1218
  txb.setSender(this.address);
1129
- return this.simulator.simulate({ txb, sender: this.address });
1219
+ return this.simulator.simulate({
1220
+ txb,
1221
+ sender: this.address,
1222
+ preferAddressBalance: request.preferAddressBalance,
1223
+ excludeObjectIds: request.excludeObjectIds
1224
+ });
1130
1225
  }
1131
1226
  async proposeIntention(input) {
1132
1227
  const message = import_sui3_utils4.SigningMessageHelper.proposeIntentionMessage({
@@ -1144,30 +1239,7 @@ var MSafeAccount = class _MSafeAccount {
1144
1239
  });
1145
1240
  }
1146
1241
  async proposeIntentionAndBuildVote(input) {
1147
- let txb;
1148
- if (input.txb) {
1149
- txb = input.txb;
1150
- } else {
1151
- const appHelper = import_sui_app_store.appHelpers.getAppHelper(input.application);
1152
- if (!appHelper) {
1153
- throw new Error(`Can't find app helper for application ${input.application}`);
1154
- }
1155
- txb = toSuiTransaction(
1156
- await appHelper.build({
1157
- network: this.globals.config.network,
1158
- intentionData: input.intention,
1159
- txType: input.txType,
1160
- txSubType: input.txSubType,
1161
- clientUrl: this.globals.config.suiClient.url,
1162
- account: {
1163
- address: this.address,
1164
- publicKey: (0, import_utils5.fromHex)(this.address),
1165
- chains: [import_wallet_standard.SUI_MAINNET_CHAIN, import_wallet_standard.SUI_TESTNET_CHAIN],
1166
- features: []
1167
- }
1168
- })
1169
- );
1170
- }
1242
+ const txb = await this.buildTxFromIntention(input);
1171
1243
  txb.setGasPrice(input.gasPrice);
1172
1244
  txb.setSender(this.address);
1173
1245
  await prepareGasFunding({
@@ -1175,14 +1247,20 @@ var MSafeAccount = class _MSafeAccount {
1175
1247
  suiClient: this.globals.suiClient,
1176
1248
  owner: this.address,
1177
1249
  gasPrice: input.gasPrice,
1178
- gasBudget: input.gasBudget
1250
+ gasBudget: input.gasBudget,
1251
+ preferAddressBalance: input.preferAddressBalance,
1252
+ excludeObjectIds: input.excludeObjectIds
1179
1253
  });
1180
1254
  const payload = await txb.build({ client: this.globals.suiClient });
1181
1255
  const digest = await txb.getDigest({ client: this.globals.suiClient });
1182
1256
  const signature = await this.wallet.signTransactionBlock({ transactionBlock: payload });
1257
+ const apiInput = { ...input };
1258
+ delete apiInput.txb;
1259
+ delete apiInput.preferAddressBalance;
1260
+ delete apiInput.excludeObjectIds;
1183
1261
  return this.backend.proposeIntentionAndBuildVote({
1184
- ...input,
1185
- payload: (0, import_utils5.toHex)(payload),
1262
+ ...apiInput,
1263
+ payload: (0, import_utils6.toHex)(payload),
1186
1264
  digest,
1187
1265
  msafeAddress: this.address,
1188
1266
  signature: signature.signature
@@ -1289,25 +1367,16 @@ var MSafeAccount = class _MSafeAccount {
1289
1367
  }
1290
1368
  async simulateBuildTransaction() {
1291
1369
  const intention = await this.backend.getNextIntention({ msafeAddress: this.address });
1292
- const appHelper = import_sui_app_store.appHelpers.getAppHelper(intention.application);
1293
- if (!appHelper) {
1294
- throw new Error(`Can't find app helper for application ${intention.application}`);
1295
- }
1296
- const txb = toSuiTransaction(
1297
- await appHelper.build({
1298
- network: this.globals.config.network,
1299
- intentionData: intention.intention,
1300
- txType: intention.txType,
1301
- txSubType: intention.txSubType,
1302
- clientUrl: this.globals.config.suiClient.url,
1303
- account: {
1304
- address: this.address,
1305
- publicKey: (0, import_utils5.fromHex)(this.address),
1306
- chains: [import_wallet_standard.SUI_MAINNET_CHAIN, import_wallet_standard.SUI_TESTNET_CHAIN],
1307
- features: []
1308
- }
1309
- })
1310
- );
1370
+ const parked = parkedPayloadHex(intention.intention);
1371
+ if (parked) {
1372
+ return this.simulator.simulateBuilt((0, import_utils6.fromHex)(parked));
1373
+ }
1374
+ const txb = await this.buildTxFromIntention({
1375
+ application: intention.application,
1376
+ intention: intention.intention,
1377
+ txType: intention.txType,
1378
+ txSubType: intention.txSubType
1379
+ });
1311
1380
  txb.setSender(this.address);
1312
1381
  return this.simulator.simulate({ txb, sender: this.address });
1313
1382
  }
@@ -1352,6 +1421,45 @@ var MSafeAccount = class _MSafeAccount {
1352
1421
  msafeAddress: this.address
1353
1422
  });
1354
1423
  }
1424
+ /**
1425
+ * Resolve a Transaction from propose/simulate input.
1426
+ * 1. Caller-supplied `txb` (generic store path — do not rebuild).
1427
+ * 2. Registered app helper `build`.
1428
+ * 3. Unregistered app: restore from `intention.content` (JSON or hex).
1429
+ */
1430
+ async buildTxFromIntention(input) {
1431
+ if (input.txb) {
1432
+ return input.txb;
1433
+ }
1434
+ let appHelper;
1435
+ try {
1436
+ appHelper = import_sui_app_store.appHelpers.getAppHelper(input.application);
1437
+ } catch {
1438
+ appHelper = void 0;
1439
+ }
1440
+ if (appHelper) {
1441
+ return toSuiTransaction(
1442
+ await appHelper.build({
1443
+ network: this.globals.config.network,
1444
+ intentionData: input.intention,
1445
+ txType: input.txType,
1446
+ txSubType: input.txSubType,
1447
+ clientUrl: this.globals.config.suiClient.url,
1448
+ account: {
1449
+ address: this.address,
1450
+ publicKey: (0, import_utils6.fromHex)(this.address),
1451
+ chains: [import_wallet_standard.SUI_MAINNET_CHAIN, import_wallet_standard.SUI_TESTNET_CHAIN],
1452
+ features: []
1453
+ }
1454
+ })
1455
+ );
1456
+ }
1457
+ const content = getIntentionContent(input.intention);
1458
+ if (content != null) {
1459
+ return transactionFromContent(content);
1460
+ }
1461
+ throw new Error(`Can't find app helper for application ${input.application}`);
1462
+ }
1355
1463
  calculateWeightFromVotes(votes) {
1356
1464
  return this.calculateWeight(votes.map((vote) => vote.userAddress));
1357
1465
  }
@@ -1384,7 +1492,7 @@ var MSafeAccount = class _MSafeAccount {
1384
1492
 
1385
1493
  // src/core/PublicKeyHelper.ts
1386
1494
  var import_sui3_utils5 = require("@msafe/sui3-utils");
1387
- var import_utils7 = require("@mysten/sui/utils");
1495
+ var import_utils8 = require("@mysten/sui/utils");
1388
1496
  var PublicKeyHelper = class {
1389
1497
  constructor(globals) {
1390
1498
  this.globals = globals;
@@ -1433,7 +1541,7 @@ var PublicKeyHelper = class {
1433
1541
  if (!(0, import_sui3_utils5.isSameAddress)(address, publicKey.toSuiAddress())) {
1434
1542
  throw new Error("Invalid public key");
1435
1543
  }
1436
- this.knownPublicKeys.set((0, import_utils7.normalizeSuiAddress)(address), publicKey);
1544
+ this.knownPublicKeys.set((0, import_utils8.normalizeSuiAddress)(address), publicKey);
1437
1545
  }
1438
1546
  async _getPublicKey(address) {
1439
1547
  const pkBackend = await this.getPublicKeyFromBackend(address);