@msafe/sui3-sdk 1.0.15 → 1.0.17

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
@@ -31,6 +31,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
31
31
  var src_exports = {};
32
32
  __export(src_exports, {
33
33
  AddressBookSDK: () => AddressBookSDK,
34
+ COIN_RESERVATION_MAGIC: () => COIN_RESERVATION_MAGIC,
34
35
  COIN_TYPE_ARG_REGEX: () => COIN_TYPE_ARG_REGEX,
35
36
  Coin: () => Coin,
36
37
  CoinHelper: () => CoinHelper,
@@ -42,6 +43,7 @@ __export(src_exports, {
42
43
  HexToUint8Array: () => HexToUint8Array,
43
44
  InfoTypeEnabledDto: () => InfoTypeEnabledDto,
44
45
  InfoTypeKey: () => InfoTypeKey,
46
+ InsufficientGasFundsError: () => InsufficientGasFundsError,
45
47
  LOCAL_API_URL: () => LOCAL_API_URL,
46
48
  LOCAL_SYNCING_URL: () => LOCAL_SYNCING_URL,
47
49
  MAINNET_RPC_URL: () => MAINNET_RPC_URL,
@@ -59,11 +61,18 @@ __export(src_exports, {
59
61
  TESTNET_RPC_URL: () => TESTNET_RPC_URL,
60
62
  Uint8ArrayToHex: () => Uint8ArrayToHex,
61
63
  addPrefix: () => addPrefix,
64
+ computeGasBudget: () => computeGasBudget,
65
+ createCoinReservationRef: () => createCoinReservationRef,
66
+ estimateGasBudget: () => estimateGasBudget,
62
67
  getAllCoins: () => getAllCoins,
63
68
  getMSafeConfig: () => getMSafeConfig,
64
69
  getPublicKeyFromChain: () => getPublicKeyFromChain,
65
70
  httpUrlToGrpcBaseUrl: () => httpUrlToGrpcBaseUrl,
71
+ isCoinReservationDigest: () => isCoinReservationDigest,
66
72
  msafeChainToSuiNetwork: () => msafeChainToSuiNetwork,
73
+ preferClassicGasPayment: () => preferClassicGasPayment,
74
+ prepareGasFunding: () => prepareGasFunding,
75
+ selectGasFunding: () => selectGasFunding,
67
76
  stringToBuffer: () => stringToBuffer,
68
77
  toSuiTransaction: () => toSuiTransaction
69
78
  });
@@ -188,128 +197,93 @@ var InvitationSDK = class {
188
197
  // src/core/MSafeAccount.ts
189
198
  var import_sui_app_store = require("@msafe/sui-app-store");
190
199
  var import_sui3_utils4 = require("@msafe/sui3-utils");
191
- var import_transactions3 = require("@mysten/sui/transactions");
192
- var import_utils3 = require("@mysten/sui/utils");
200
+ var import_transactions4 = require("@mysten/sui/transactions");
201
+ var import_utils5 = require("@mysten/sui/utils");
193
202
  var import_wallet_standard = require("@mysten/wallet-standard");
194
203
 
195
204
  // src/simulate/simulator.ts
205
+ var import_transactions2 = require("@mysten/sui/transactions");
206
+
207
+ // src/utils/gasFunding.ts
196
208
  var import_transactions = require("@mysten/sui/transactions");
197
- var GAS_SAFE_OVERHEAD = 1000n;
198
- function formatExecutionError(error) {
199
- return error.message;
200
- }
201
- function gasObjectToReference(gas) {
202
- if (!gas?.objectId) {
203
- throw new Error("Gas object missing from simulation effects");
204
- }
205
- const version = gas.outputVersion ?? gas.inputVersion ?? "0";
206
- const digest = gas.outputDigest ?? gas.inputDigest ?? "";
207
- return { objectId: gas.objectId, version, digest };
208
- }
209
- var Simulator = class {
210
- constructor(globals) {
211
- this.globals = globals;
212
- }
213
- async simulate(input) {
214
- const tx = this.copyTransaction(input.txb);
215
- const { suiClient } = this.globals;
216
- const gasPrice = await this.getGasPrice();
217
- tx.setGasPrice(gasPrice);
218
- tx.setSender(input.sender);
219
- let built;
220
- try {
221
- built = await tx.build({ client: suiClient });
222
- } catch (e) {
223
- const message = e instanceof Error ? e.message : String(e);
224
- return {
225
- success: false,
226
- gasPrice,
227
- simulationError: message
228
- };
229
- }
230
- let inspectResult;
231
- try {
232
- inspectResult = await suiClient.simulateTransaction({
233
- transaction: built,
234
- include: {
235
- effects: true,
236
- balanceChanges: true,
237
- events: true,
238
- objectTypes: true,
239
- transaction: true,
240
- bcs: true,
241
- commandResults: true
242
- }
243
- });
244
- } catch (e) {
245
- const message = e instanceof Error ? e.message : String(e);
246
- return {
247
- success: false,
248
- gasPrice,
249
- simulationError: message
250
- };
251
- }
252
- const txRow = inspectResult.Transaction ?? inspectResult.FailedTransaction;
253
- const { effects } = txRow;
254
- if (!effects) {
255
- throw new Error("simulateTransaction did not return effects");
256
- }
257
- const success = effects.status.success === true;
258
- if (!success) {
259
- const err = effects.status.success === false ? effects.status.error : null;
260
- return {
261
- success,
262
- gasPrice,
263
- response: inspectResult,
264
- simulationError: err ? formatExecutionError(err) : "Simulation failed"
265
- };
266
- }
267
- const gasBudget = this.toGasBudget(effects.gasUsed, gasPrice);
268
- return {
269
- success,
270
- gasPrice,
271
- gasObject: gasObjectToReference(effects.gasObject),
272
- gasUsed: effects.gasUsed,
273
- gasBudget,
274
- response: inspectResult
275
- };
276
- }
277
- async getGasPrice() {
278
- const { referenceGasPrice } = await this.globals.suiClient.getReferenceGasPrice();
279
- return BigInt(referenceGasPrice);
280
- }
281
- toGasBudget(gasUsed, gasPrice) {
282
- const { computationCost, storageCost, storageRebate } = gasUsed;
283
- const safeOverhead = GAS_SAFE_OVERHEAD * gasPrice;
284
- const baseComputationCostWithOverhead = BigInt(computationCost) + safeOverhead;
285
- const gasBudget = baseComputationCostWithOverhead + BigInt(storageCost) - BigInt(storageRebate);
286
- return gasBudget > baseComputationCostWithOverhead ? gasBudget : baseComputationCostWithOverhead;
287
- }
288
- copyTransaction(tx) {
289
- return import_transactions.Transaction.from(tx);
290
- }
291
- };
209
+ var import_utils4 = require("@mysten/sui/utils");
292
210
 
293
- // src/utils/buffer.ts
294
- var import_buffer = require("buffer");
295
- function stringToBuffer(s) {
296
- return import_buffer.Buffer.from(s, "utf-8");
211
+ // src/utils/coinReservation.ts
212
+ var import_bcs = require("@mysten/sui/bcs");
213
+ var import_utils = require("@mysten/sui/utils");
214
+ var SUI_ACCUMULATOR_ROOT_OBJECT_ID = (0, import_utils.normalizeSuiAddress)("0xacc");
215
+ var ACCUMULATOR_KEY_TYPE_TAG = import_bcs.TypeTagSerializer.parseFromStr(
216
+ "0x2::accumulator::Key<0x2::balance::Balance<0x2::sui::SUI>>"
217
+ );
218
+ var COIN_RESERVATION_MAGIC = new Uint8Array([
219
+ 172,
220
+ 172,
221
+ 172,
222
+ 172,
223
+ 172,
224
+ 172,
225
+ 172,
226
+ 172,
227
+ 172,
228
+ 172,
229
+ 172,
230
+ 172,
231
+ 172,
232
+ 172,
233
+ 172,
234
+ 172,
235
+ 172,
236
+ 172,
237
+ 172,
238
+ 172
239
+ ]);
240
+ function isCoinReservationDigest(digestBase58) {
241
+ const digestBytes = (0, import_utils.fromBase58)(digestBase58);
242
+ const last20Bytes = digestBytes.slice(12, 32);
243
+ return last20Bytes.every((byte, i) => byte === COIN_RESERVATION_MAGIC[i]);
297
244
  }
298
- function Uint8ArrayToHex(b) {
299
- return `0x${Array.prototype.map.call(b, (x) => `0${x.toString(16)}`.slice(-2)).join("")}`;
245
+ function deriveReservationObjectId(owner, chainIdentifier) {
246
+ const keyBcs = import_bcs.bcs.Address.serialize(owner).toBytes();
247
+ const accumulatorId = (0, import_utils.deriveDynamicFieldID)(SUI_ACCUMULATOR_ROOT_OBJECT_ID, ACCUMULATOR_KEY_TYPE_TAG, keyBcs);
248
+ const accBytes = (0, import_utils.fromHex)(accumulatorId.slice(2));
249
+ const chainBytes = (0, import_utils.fromBase58)(chainIdentifier);
250
+ if (chainBytes.length !== 32) {
251
+ throw new Error(`Invalid chain identifier length: expected 32 bytes, got ${chainBytes.length}`);
252
+ }
253
+ const xored = new Uint8Array(32);
254
+ for (let i = 0; i < 32; i++) {
255
+ xored[i] = accBytes[i] ^ chainBytes[i];
256
+ }
257
+ return `0x${(0, import_utils.toHex)(xored)}`;
300
258
  }
301
- function HexToUint8Array(hex) {
302
- return Uint8Array.from(import_buffer.Buffer.from(hex.startsWith("0x") ? hex.slice(2) : hex, "hex"));
259
+ function createCoinReservationRef(reservedBalance, owner, chainIdentifier, epoch) {
260
+ const digestBytes = new Uint8Array(32);
261
+ const view = new DataView(digestBytes.buffer);
262
+ view.setBigUint64(0, reservedBalance, true);
263
+ const epochNum = Number(epoch);
264
+ if (!Number.isSafeInteger(epochNum) || epochNum < 0 || epochNum > 4294967295) {
265
+ throw new Error(`Epoch ${epoch} out of u32 range for coin reservation digest`);
266
+ }
267
+ view.setUint32(8, epochNum, true);
268
+ digestBytes.set(COIN_RESERVATION_MAGIC, 12);
269
+ return {
270
+ objectId: deriveReservationObjectId(owner, chainIdentifier),
271
+ version: "0",
272
+ digest: (0, import_utils.toBase58)(digestBytes)
273
+ };
303
274
  }
304
275
 
305
- // src/utils/crypto.ts
306
- var import_verify = require("@mysten/sui/verify");
276
+ // src/utils/sui.ts
277
+ var import_sui3_utils3 = require("@msafe/sui3-utils");
278
+ var import_cryptography = require("@mysten/sui/cryptography");
279
+ var import_graphql = require("@mysten/sui/graphql");
280
+ var import_multisig = require("@mysten/sui/multisig");
307
281
 
308
282
  // src/utils/format.ts
309
- var import_utils2 = require("@mysten/sui/utils");
283
+ var import_utils3 = require("@mysten/sui/utils");
310
284
 
311
285
  // src/utils/coin.ts
312
- var import_utils = require("@mysten/sui/utils");
286
+ var import_utils2 = require("@mysten/sui/utils");
313
287
  var CoinHelper = class {
314
288
  _client;
315
289
  _coinMetaReg;
@@ -318,7 +292,7 @@ var CoinHelper = class {
318
292
  this._coinMetaReg = /* @__PURE__ */ new Map();
319
293
  }
320
294
  async getCoinMeta(coinType) {
321
- const normalized = (0, import_utils.normalizeStructTag)(coinType);
295
+ const normalized = (0, import_utils2.normalizeStructTag)(coinType);
322
296
  if (this._coinMetaReg.has(normalized)) {
323
297
  return this._coinMetaReg.get(normalized);
324
298
  }
@@ -339,10 +313,10 @@ var Coin = class _Coin {
339
313
  if (!type) {
340
314
  return false;
341
315
  }
342
- return (0, import_utils.normalizeStructTag)(type).match(COIN_TYPE_ARG_REGEX) != null;
316
+ return (0, import_utils2.normalizeStructTag)(type).match(COIN_TYPE_ARG_REGEX) != null;
343
317
  }
344
318
  static getCoinType(type) {
345
- const [, res] = (0, import_utils.normalizeStructTag)(type).match(COIN_TYPE_ARG_REGEX) ?? [];
319
+ const [, res] = (0, import_utils2.normalizeStructTag)(type).match(COIN_TYPE_ARG_REGEX) ?? [];
346
320
  return res || null;
347
321
  }
348
322
  static getBalance(data) {
@@ -370,16 +344,16 @@ var Coin = class _Coin {
370
344
  // src/utils/format.ts
371
345
  var Formatter = class {
372
346
  static normalizeSuiAddress(addr) {
373
- return (0, import_utils2.normalizeSuiAddress)(addr);
347
+ return (0, import_utils3.normalizeSuiAddress)(addr);
374
348
  }
375
349
  static normalizeStructTag(struct) {
376
- return (0, import_utils2.normalizeStructTag)(struct);
350
+ return (0, import_utils3.normalizeStructTag)(struct);
377
351
  }
378
352
  static isSuiAddressEqual(addr1, addr2) {
379
- return (0, import_utils2.normalizeSuiAddress)(addr1) === (0, import_utils2.normalizeSuiAddress)(addr2);
353
+ return (0, import_utils3.normalizeSuiAddress)(addr1) === (0, import_utils3.normalizeSuiAddress)(addr2);
380
354
  }
381
355
  static isSuiStructEqual(struct1, struct2) {
382
- return (0, import_utils2.normalizeStructTag)(struct1) === (0, import_utils2.normalizeStructTag)(struct2);
356
+ return (0, import_utils3.normalizeStructTag)(struct1) === (0, import_utils3.normalizeStructTag)(struct2);
383
357
  }
384
358
  static isCoinObjectType(struct) {
385
359
  return Coin.isCoin(struct);
@@ -392,50 +366,7 @@ function addPrefix(s, prefix) {
392
366
  return prefix + s;
393
367
  }
394
368
 
395
- // src/utils/crypto.ts
396
- var SignatureVerifier = class _SignatureVerifier {
397
- static async getPublicKeyFromSignature(input) {
398
- if (input.messageType === "TransactionBlock") {
399
- return (0, import_verify.verifyTransactionSignature)(input.message, input.signature);
400
- }
401
- return (0, import_verify.verifyPersonalMessageSignature)(input.message, input.signature);
402
- }
403
- static async getPublicKeyFromPersonalSignature(input) {
404
- const message = stringToBuffer(input.messageStr);
405
- return this.getPublicKeyFromSignature({
406
- message,
407
- messageType: "Personal",
408
- signature: input.signature
409
- });
410
- }
411
- static async verifySignature(input) {
412
- const publicKey = await _SignatureVerifier.getPublicKeyFromSignature(input);
413
- return Formatter.isSuiAddressEqual(publicKey.toSuiAddress(), input.targetAddress);
414
- }
415
- static async verifyPersonalSignature(input) {
416
- const message = stringToBuffer(input.messageStr);
417
- return this.verifySignature({
418
- message,
419
- messageType: "Personal",
420
- signature: input.signature,
421
- targetAddress: input.targetAddress
422
- });
423
- }
424
- static async verifyTransactionSignature(input) {
425
- return this.verifySignature({
426
- messageType: "TransactionBlock",
427
- message: input.payload,
428
- signature: input.signature,
429
- targetAddress: input.targetAddress
430
- });
431
- }
432
- };
433
-
434
369
  // src/utils/sui.ts
435
- var import_sui3_utils3 = require("@msafe/sui3-utils");
436
- var import_cryptography = require("@mysten/sui/cryptography");
437
- var import_graphql = require("@mysten/sui/graphql");
438
- var import_multisig = require("@mysten/sui/multisig");
439
370
  var SUI_COIN = "0x2::sui::SUI";
440
371
  var GRAPHQL_URL_BY_NETWORK = {
441
372
  mainnet: "https://sui-mainnet.mystenlabs.com/graphql",
@@ -547,14 +478,355 @@ async function getAllCoins(input) {
547
478
  return res;
548
479
  }
549
480
 
481
+ // src/utils/gasFunding.ts
482
+ var GAS_SAFE_OVERHEAD = 1000n;
483
+ var InsufficientGasFundsError = class extends Error {
484
+ gasBudget;
485
+ coinBalance;
486
+ addressBalance;
487
+ constructor(gasBudget, coinBalance, addressBalance) {
488
+ const total = coinBalance + addressBalance;
489
+ super(
490
+ `Insufficient gas funds: need ${gasBudget}, have coinBalance=${coinBalance} + addressBalance=${addressBalance} = ${total}`
491
+ );
492
+ this.name = "InsufficientGasFundsError";
493
+ this.gasBudget = gasBudget;
494
+ this.coinBalance = coinBalance;
495
+ this.addressBalance = addressBalance;
496
+ }
497
+ };
498
+ function computeGasBudget(gasUsed, gasPrice) {
499
+ const { computationCost, storageCost } = gasUsed;
500
+ const safeOverhead = GAS_SAFE_OVERHEAD * gasPrice;
501
+ return BigInt(computationCost) + BigInt(storageCost) + safeOverhead;
502
+ }
503
+ function collectUsedObjectIds(tx) {
504
+ return tx.getData().inputs.reduce((used, input) => {
505
+ const immOrOwned = input.Object?.ImmOrOwnedObject?.objectId;
506
+ if (immOrOwned) {
507
+ used.add((0, import_utils4.normalizeSuiObjectId)(immOrOwned));
508
+ return used;
509
+ }
510
+ const unresolved = input.UnresolvedObject?.objectId;
511
+ if (unresolved) {
512
+ used.add((0, import_utils4.normalizeSuiObjectId)(unresolved));
513
+ }
514
+ return used;
515
+ }, /* @__PURE__ */ new Set());
516
+ }
517
+ async function loadSuiGasFunding(suiClient, owner, tx) {
518
+ const usedObjectIds = collectUsedObjectIds(tx);
519
+ const [coins, balanceRes] = await Promise.all([
520
+ getAllCoins({ suiClient, owner, coinType: SUI_COIN }),
521
+ suiClient.getBalance({ owner, coinType: SUI_COIN })
522
+ ]);
523
+ const paymentCoins = coins.filter((coin) => !usedObjectIds.has((0, import_utils4.normalizeSuiObjectId)(coin.objectId)) && BigInt(coin.balance) > 0n).map((coin) => ({
524
+ objectId: coin.objectId,
525
+ version: coin.version,
526
+ digest: coin.digest,
527
+ balance: BigInt(coin.balance)
528
+ }));
529
+ const coinBalance = paymentCoins.reduce((sum, coin) => sum + coin.balance, 0n);
530
+ const addressBalance = BigInt(balanceRes.balance.addressBalance);
531
+ return {
532
+ paymentCoins,
533
+ coinBalance,
534
+ addressBalance,
535
+ total: coinBalance + addressBalance
536
+ };
537
+ }
538
+ function toObjectRefs(coins) {
539
+ return coins.map(({ objectId, version, digest }) => ({ objectId, version, digest }));
540
+ }
541
+ async function applyMixedTopUp(input) {
542
+ const { tx, suiClient, owner, paymentCoins, topUpAmount } = input;
543
+ if (paymentCoins.length === 0) {
544
+ throw new Error("mixed gas top-up requires at least one SUI coin object");
545
+ }
546
+ if (topUpAmount <= 0n) {
547
+ tx.setGasPayment(toObjectRefs(paymentCoins));
548
+ return;
549
+ }
550
+ const [{ chainIdentifier }, { systemState }] = await Promise.all([
551
+ suiClient.core.getChainIdentifier(),
552
+ suiClient.core.getCurrentSystemState()
553
+ ]);
554
+ const reservation = createCoinReservationRef(topUpAmount, owner, chainIdentifier, systemState.epoch);
555
+ tx.setGasPayment([...toObjectRefs(paymentCoins), reservation]);
556
+ }
557
+ async function selectGasFunding(input) {
558
+ const { tx, suiClient, owner, gasBudget } = input;
559
+ const { payment } = tx.getData().gasData;
560
+ if (payment != null && payment.length > 0) {
561
+ const funding2 = await loadSuiGasFunding(suiClient, owner, tx);
562
+ return {
563
+ mode: "classic",
564
+ gasBudget,
565
+ coinBalance: funding2.coinBalance,
566
+ addressBalance: funding2.addressBalance
567
+ };
568
+ }
569
+ const funding = await loadSuiGasFunding(suiClient, owner, tx);
570
+ const { paymentCoins, coinBalance, addressBalance, total } = funding;
571
+ if (total < gasBudget) {
572
+ throw new InsufficientGasFundsError(gasBudget, coinBalance, addressBalance);
573
+ }
574
+ if (paymentCoins.length > 0 && coinBalance >= gasBudget) {
575
+ tx.setGasPayment(toObjectRefs(paymentCoins));
576
+ return { mode: "classic", gasBudget, coinBalance, addressBalance };
577
+ }
578
+ if (paymentCoins.length > 0 && total >= gasBudget) {
579
+ const topUpAmount = gasBudget - coinBalance;
580
+ await applyMixedTopUp({
581
+ tx,
582
+ suiClient,
583
+ owner,
584
+ paymentCoins,
585
+ topUpAmount
586
+ });
587
+ return { mode: "mixedTopUp", gasBudget, coinBalance, addressBalance };
588
+ }
589
+ if (addressBalance >= gasBudget) {
590
+ tx.setGasPayment([]);
591
+ return { mode: "addressBalance", gasBudget, coinBalance, addressBalance };
592
+ }
593
+ throw new InsufficientGasFundsError(gasBudget, coinBalance, addressBalance);
594
+ }
595
+ async function estimateGasBudget(input) {
596
+ const { tx, suiClient, owner, gasPrice } = input;
597
+ const funding = await loadSuiGasFunding(suiClient, owner, tx);
598
+ if (funding.total <= 0n) {
599
+ throw new InsufficientGasFundsError(0n, funding.coinBalance, funding.addressBalance);
600
+ }
601
+ const probe = import_transactions.Transaction.from(tx);
602
+ probe.setSender(owner);
603
+ probe.setGasPrice(gasPrice);
604
+ if (funding.paymentCoins.length > 0) {
605
+ probe.setGasPayment(toObjectRefs(funding.paymentCoins));
606
+ }
607
+ let built;
608
+ try {
609
+ built = await probe.build({ client: suiClient });
610
+ } catch (e) {
611
+ const message = e instanceof Error ? e.message : String(e);
612
+ throw new Error(`Failed to estimate gas budget: ${message}`);
613
+ }
614
+ const inspect = await suiClient.simulateTransaction({
615
+ transaction: built,
616
+ include: { effects: true }
617
+ });
618
+ const effects = (inspect.Transaction ?? inspect.FailedTransaction)?.effects;
619
+ if (!effects?.gasUsed) {
620
+ throw new Error("Failed to estimate gas budget: simulateTransaction returned no gasUsed");
621
+ }
622
+ if (effects.status.success !== true) {
623
+ const err = effects.status.success === false ? effects.status.error?.message : void 0;
624
+ throw new Error(`Failed to estimate gas budget: ${err ?? "simulation failed"}`);
625
+ }
626
+ return computeGasBudget(effects.gasUsed, gasPrice);
627
+ }
628
+ async function prepareGasFunding(input) {
629
+ const { tx, suiClient, owner, gasPrice } = input;
630
+ const { payment, budget: existingBudget } = tx.getData().gasData;
631
+ const bakedAddressBalanceGas = payment != null && payment.length === 0;
632
+ let { gasBudget } = input;
633
+ if (gasBudget == null) {
634
+ if (bakedAddressBalanceGas || existingBudget == null) {
635
+ gasBudget = await estimateGasBudget({ tx, suiClient, owner, gasPrice });
636
+ } else {
637
+ gasBudget = BigInt(existingBudget);
638
+ }
639
+ }
640
+ tx.setGasBudget(gasBudget);
641
+ return selectGasFunding({ tx, suiClient, owner, gasBudget });
642
+ }
643
+ async function preferClassicGasPayment(input) {
644
+ const { tx, suiClient, owner } = input;
645
+ const { payment } = tx.getData().gasData;
646
+ if (payment != null) {
647
+ return payment.length > 0;
648
+ }
649
+ const funding = await loadSuiGasFunding(suiClient, owner, tx);
650
+ if (funding.paymentCoins.length === 0) {
651
+ return false;
652
+ }
653
+ tx.setGasPayment(toObjectRefs(funding.paymentCoins));
654
+ return true;
655
+ }
656
+
657
+ // src/simulate/simulator.ts
658
+ function formatExecutionError(error) {
659
+ return error.message;
660
+ }
661
+ function gasObjectToReference(gas) {
662
+ if (!gas?.objectId) {
663
+ throw new Error("Gas object missing from simulation effects");
664
+ }
665
+ const version = gas.outputVersion ?? gas.inputVersion ?? "0";
666
+ const digest = gas.outputDigest ?? gas.inputDigest ?? "";
667
+ return { objectId: gas.objectId, version, digest };
668
+ }
669
+ var Simulator = class {
670
+ constructor(globals) {
671
+ this.globals = globals;
672
+ }
673
+ async simulate(input) {
674
+ const tx = this.copyTransaction(input.txb);
675
+ const { suiClient } = this.globals;
676
+ const gasPrice = await this.getGasPrice();
677
+ tx.setGasPrice(gasPrice);
678
+ tx.setSender(input.sender);
679
+ try {
680
+ await prepareGasFunding({
681
+ tx,
682
+ suiClient,
683
+ owner: input.sender,
684
+ gasPrice
685
+ });
686
+ } catch (e) {
687
+ const message = e instanceof Error ? e.message : String(e);
688
+ return {
689
+ success: false,
690
+ gasPrice,
691
+ simulationError: message
692
+ };
693
+ }
694
+ let built;
695
+ try {
696
+ built = await tx.build({ client: suiClient });
697
+ } catch (e) {
698
+ const message = e instanceof Error ? e.message : String(e);
699
+ return {
700
+ success: false,
701
+ gasPrice,
702
+ simulationError: message
703
+ };
704
+ }
705
+ let inspectResult;
706
+ try {
707
+ inspectResult = await suiClient.simulateTransaction({
708
+ transaction: built,
709
+ include: {
710
+ effects: true,
711
+ balanceChanges: true,
712
+ events: true,
713
+ objectTypes: true,
714
+ transaction: true,
715
+ bcs: true,
716
+ commandResults: true
717
+ }
718
+ });
719
+ } catch (e) {
720
+ const message = e instanceof Error ? e.message : String(e);
721
+ return {
722
+ success: false,
723
+ gasPrice,
724
+ simulationError: message
725
+ };
726
+ }
727
+ const txRow = inspectResult.Transaction ?? inspectResult.FailedTransaction;
728
+ const { effects } = txRow;
729
+ if (!effects) {
730
+ throw new Error("simulateTransaction did not return effects");
731
+ }
732
+ const success = effects.status.success === true;
733
+ if (!success) {
734
+ const err = effects.status.success === false ? effects.status.error : null;
735
+ return {
736
+ success,
737
+ gasPrice,
738
+ response: inspectResult,
739
+ simulationError: err ? formatExecutionError(err) : "Simulation failed"
740
+ };
741
+ }
742
+ const gasBudget = this.toGasBudget(effects.gasUsed, gasPrice);
743
+ return {
744
+ success,
745
+ gasPrice,
746
+ gasObject: gasObjectToReference(effects.gasObject),
747
+ gasUsed: effects.gasUsed,
748
+ gasBudget,
749
+ response: inspectResult
750
+ };
751
+ }
752
+ async getGasPrice() {
753
+ const { referenceGasPrice } = await this.globals.suiClient.getReferenceGasPrice();
754
+ return BigInt(referenceGasPrice);
755
+ }
756
+ toGasBudget(gasUsed, gasPrice) {
757
+ return computeGasBudget(gasUsed, gasPrice);
758
+ }
759
+ copyTransaction(tx) {
760
+ return import_transactions2.Transaction.from(tx);
761
+ }
762
+ };
763
+
764
+ // src/utils/buffer.ts
765
+ var import_buffer = require("buffer");
766
+ function stringToBuffer(s) {
767
+ return import_buffer.Buffer.from(s, "utf-8");
768
+ }
769
+ function Uint8ArrayToHex(b) {
770
+ return `0x${Array.prototype.map.call(b, (x) => `0${x.toString(16)}`.slice(-2)).join("")}`;
771
+ }
772
+ function HexToUint8Array(hex) {
773
+ return Uint8Array.from(import_buffer.Buffer.from(hex.startsWith("0x") ? hex.slice(2) : hex, "hex"));
774
+ }
775
+
776
+ // src/utils/crypto.ts
777
+ var import_verify = require("@mysten/sui/verify");
778
+ var SignatureVerifier = class _SignatureVerifier {
779
+ static async getPublicKeyFromSignature(input) {
780
+ if (input.messageType === "TransactionBlock") {
781
+ return (0, import_verify.verifyTransactionSignature)(input.message, input.signature);
782
+ }
783
+ return (0, import_verify.verifyPersonalMessageSignature)(input.message, input.signature);
784
+ }
785
+ static async getPublicKeyFromPersonalSignature(input) {
786
+ const message = stringToBuffer(input.messageStr);
787
+ return this.getPublicKeyFromSignature({
788
+ message,
789
+ messageType: "Personal",
790
+ signature: input.signature
791
+ });
792
+ }
793
+ static async verifySignature(input) {
794
+ const publicKey = await _SignatureVerifier.getPublicKeyFromSignature(input);
795
+ return Formatter.isSuiAddressEqual(publicKey.toSuiAddress(), input.targetAddress);
796
+ }
797
+ static async verifyPersonalSignature(input) {
798
+ const message = stringToBuffer(input.messageStr);
799
+ return this.verifySignature({
800
+ message,
801
+ messageType: "Personal",
802
+ signature: input.signature,
803
+ targetAddress: input.targetAddress
804
+ });
805
+ }
806
+ static async verifyTransactionSignature(input) {
807
+ return this.verifySignature({
808
+ messageType: "TransactionBlock",
809
+ message: input.payload,
810
+ signature: input.signature,
811
+ targetAddress: input.targetAddress
812
+ });
813
+ }
814
+ };
815
+
550
816
  // src/utils/transaction.ts
551
- var import_transactions2 = require("@mysten/sui/transactions");
817
+ var import_transactions3 = require("@mysten/sui/transactions");
552
818
  function toSuiTransaction(txb) {
553
- if ((0, import_transactions2.isTransaction)(txb)) {
819
+ if ((0, import_transactions3.isTransaction)(txb)) {
554
820
  return txb;
555
821
  }
822
+ if (typeof txb === "string" || txb instanceof Uint8Array) {
823
+ return import_transactions3.Transaction.from(txb);
824
+ }
556
825
  const legacy = txb;
557
- return import_transactions2.Transaction.from(legacy.serialize());
826
+ if (typeof legacy?.serialize === "function") {
827
+ return import_transactions3.Transaction.from(legacy.serialize());
828
+ }
829
+ throw new Error("Unsupported transaction value for toSuiTransaction");
558
830
  }
559
831
 
560
832
  // src/utils/iter/iterator.ts
@@ -722,7 +994,7 @@ var MSafeAccount = class _MSafeAccount {
722
994
  balances.map(async (balance) => {
723
995
  const meta = await this.coinHelper.getCoinMeta(balance.coinType);
724
996
  return {
725
- type: (0, import_utils3.normalizeStructTag)(balance.coinType),
997
+ type: (0, import_utils5.normalizeStructTag)(balance.coinType),
726
998
  balance: BigInt(balance.addressBalance),
727
999
  metadata: meta
728
1000
  };
@@ -795,7 +1067,7 @@ var MSafeAccount = class _MSafeAccount {
795
1067
  clientUrl: this.globals.config.suiClient.url,
796
1068
  account: {
797
1069
  address: this.address,
798
- publicKey: (0, import_utils3.fromHex)(this.address),
1070
+ publicKey: (0, import_utils5.fromHex)(this.address),
799
1071
  chains: [import_wallet_standard.SUI_MAINNET_CHAIN, import_wallet_standard.SUI_TESTNET_CHAIN],
800
1072
  features: []
801
1073
  }
@@ -838,7 +1110,7 @@ var MSafeAccount = class _MSafeAccount {
838
1110
  clientUrl: this.globals.config.suiClient.url,
839
1111
  account: {
840
1112
  address: this.address,
841
- publicKey: (0, import_utils3.fromHex)(this.address),
1113
+ publicKey: (0, import_utils5.fromHex)(this.address),
842
1114
  chains: [import_wallet_standard.SUI_MAINNET_CHAIN, import_wallet_standard.SUI_TESTNET_CHAIN],
843
1115
  features: []
844
1116
  }
@@ -846,14 +1118,20 @@ var MSafeAccount = class _MSafeAccount {
846
1118
  );
847
1119
  }
848
1120
  txb.setGasPrice(input.gasPrice);
849
- txb.setGasBudget(input.gasBudget);
850
1121
  txb.setSender(this.address);
1122
+ await prepareGasFunding({
1123
+ tx: txb,
1124
+ suiClient: this.globals.suiClient,
1125
+ owner: this.address,
1126
+ gasPrice: input.gasPrice,
1127
+ gasBudget: input.gasBudget
1128
+ });
851
1129
  const payload = await txb.build({ client: this.globals.suiClient });
852
1130
  const digest = await txb.getDigest({ client: this.globals.suiClient });
853
1131
  const signature = await this.wallet.signTransactionBlock({ transactionBlock: payload });
854
1132
  return this.backend.proposeIntentionAndBuildVote({
855
1133
  ...input,
856
- payload: (0, import_utils3.toHex)(payload),
1134
+ payload: (0, import_utils5.toHex)(payload),
857
1135
  digest,
858
1136
  msafeAddress: this.address,
859
1137
  signature: signature.signature
@@ -911,7 +1189,7 @@ var MSafeAccount = class _MSafeAccount {
911
1189
  }
912
1190
  async proposePlainPayloadIntention(intention) {
913
1191
  const sn = await this.nextSequenceNumber();
914
- const tb = import_transactions3.Transaction.from(intention.payload);
1192
+ const tb = import_transactions4.Transaction.from(intention.payload);
915
1193
  const data = tb.getData();
916
1194
  if (!data.sender || !(0, import_sui3_utils4.isSameAddress)(data.sender, this.address)) {
917
1195
  throw new Error("Transaction sender is not same as the multisig address");
@@ -948,6 +1226,14 @@ var MSafeAccount = class _MSafeAccount {
948
1226
  throw new Error("Already rejected");
949
1227
  }
950
1228
  const rejectTxb = toSuiTransaction((0, import_sui3_utils4.buildRejectTxb)(this.address));
1229
+ rejectTxb.setGasPrice(input.gasPrice);
1230
+ await prepareGasFunding({
1231
+ tx: rejectTxb,
1232
+ suiClient: this.suiClient,
1233
+ owner: this.address,
1234
+ gasPrice: input.gasPrice,
1235
+ gasBudget: input.gasBudget
1236
+ });
951
1237
  const digest = await rejectTxb.getDigest({ client: this.suiClient });
952
1238
  const payload = await rejectTxb.build({ client: this.suiClient });
953
1239
  const signature = await this.wallet.signTransactionBlock({ transactionBlock: payload });
@@ -973,7 +1259,7 @@ var MSafeAccount = class _MSafeAccount {
973
1259
  clientUrl: this.globals.config.suiClient.url,
974
1260
  account: {
975
1261
  address: this.address,
976
- publicKey: (0, import_utils3.fromHex)(this.address),
1262
+ publicKey: (0, import_utils5.fromHex)(this.address),
977
1263
  chains: [import_wallet_standard.SUI_MAINNET_CHAIN, import_wallet_standard.SUI_TESTNET_CHAIN],
978
1264
  features: []
979
1265
  }
@@ -1055,7 +1341,7 @@ var MSafeAccount = class _MSafeAccount {
1055
1341
 
1056
1342
  // src/core/PublicKeyHelper.ts
1057
1343
  var import_sui3_utils5 = require("@msafe/sui3-utils");
1058
- var import_utils5 = require("@mysten/sui/utils");
1344
+ var import_utils7 = require("@mysten/sui/utils");
1059
1345
  var PublicKeyHelper = class {
1060
1346
  constructor(globals) {
1061
1347
  this.globals = globals;
@@ -1104,7 +1390,7 @@ var PublicKeyHelper = class {
1104
1390
  if (!(0, import_sui3_utils5.isSameAddress)(address, publicKey.toSuiAddress())) {
1105
1391
  throw new Error("Invalid public key");
1106
1392
  }
1107
- this.knownPublicKeys.set((0, import_utils5.normalizeSuiAddress)(address), publicKey);
1393
+ this.knownPublicKeys.set((0, import_utils7.normalizeSuiAddress)(address), publicKey);
1108
1394
  }
1109
1395
  async _getPublicKey(address) {
1110
1396
  const pkBackend = await this.getPublicKeyFromBackend(address);