@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.js CHANGED
@@ -133,125 +133,90 @@ import {
133
133
  buildRejectTxb,
134
134
  isSameAddress as isSameAddress2
135
135
  } from "@msafe/sui3-utils";
136
- import { Transaction as Transaction3 } from "@mysten/sui/transactions";
137
- import { fromHex, normalizeStructTag as normalizeStructTag3, toHex } from "@mysten/sui/utils";
136
+ import { Transaction as Transaction4 } from "@mysten/sui/transactions";
137
+ import { fromHex as fromHex2, normalizeStructTag as normalizeStructTag3, 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
141
+ import { Transaction as Transaction2 } from "@mysten/sui/transactions";
142
+
143
+ // src/utils/gasFunding.ts
141
144
  import { Transaction } from "@mysten/sui/transactions";
142
- var GAS_SAFE_OVERHEAD = 1000n;
143
- function formatExecutionError(error) {
144
- return error.message;
145
- }
146
- function gasObjectToReference(gas) {
147
- if (!gas?.objectId) {
148
- throw new Error("Gas object missing from simulation effects");
149
- }
150
- const version = gas.outputVersion ?? gas.inputVersion ?? "0";
151
- const digest = gas.outputDigest ?? gas.inputDigest ?? "";
152
- return { objectId: gas.objectId, version, digest };
153
- }
154
- var Simulator = class {
155
- constructor(globals) {
156
- this.globals = globals;
157
- }
158
- async simulate(input) {
159
- const tx = this.copyTransaction(input.txb);
160
- const { suiClient } = this.globals;
161
- const gasPrice = await this.getGasPrice();
162
- tx.setGasPrice(gasPrice);
163
- tx.setSender(input.sender);
164
- let built;
165
- try {
166
- built = await tx.build({ client: suiClient });
167
- } catch (e) {
168
- const message = e instanceof Error ? e.message : String(e);
169
- return {
170
- success: false,
171
- gasPrice,
172
- simulationError: message
173
- };
174
- }
175
- let inspectResult;
176
- try {
177
- inspectResult = await suiClient.simulateTransaction({
178
- transaction: built,
179
- include: {
180
- effects: true,
181
- balanceChanges: true,
182
- events: true,
183
- objectTypes: true,
184
- transaction: true,
185
- bcs: true,
186
- commandResults: true
187
- }
188
- });
189
- } catch (e) {
190
- const message = e instanceof Error ? e.message : String(e);
191
- return {
192
- success: false,
193
- gasPrice,
194
- simulationError: message
195
- };
196
- }
197
- const txRow = inspectResult.Transaction ?? inspectResult.FailedTransaction;
198
- const { effects } = txRow;
199
- if (!effects) {
200
- throw new Error("simulateTransaction did not return effects");
201
- }
202
- const success = effects.status.success === true;
203
- if (!success) {
204
- const err = effects.status.success === false ? effects.status.error : null;
205
- return {
206
- success,
207
- gasPrice,
208
- response: inspectResult,
209
- simulationError: err ? formatExecutionError(err) : "Simulation failed"
210
- };
211
- }
212
- const gasBudget = this.toGasBudget(effects.gasUsed, gasPrice);
213
- return {
214
- success,
215
- gasPrice,
216
- gasObject: gasObjectToReference(effects.gasObject),
217
- gasUsed: effects.gasUsed,
218
- gasBudget,
219
- response: inspectResult
220
- };
221
- }
222
- async getGasPrice() {
223
- const { referenceGasPrice } = await this.globals.suiClient.getReferenceGasPrice();
224
- return BigInt(referenceGasPrice);
225
- }
226
- toGasBudget(gasUsed, gasPrice) {
227
- const { computationCost, storageCost, storageRebate } = gasUsed;
228
- const safeOverhead = GAS_SAFE_OVERHEAD * gasPrice;
229
- const baseComputationCostWithOverhead = BigInt(computationCost) + safeOverhead;
230
- const gasBudget = baseComputationCostWithOverhead + BigInt(storageCost) - BigInt(storageRebate);
231
- return gasBudget > baseComputationCostWithOverhead ? gasBudget : baseComputationCostWithOverhead;
232
- }
233
- copyTransaction(tx) {
234
- return Transaction.from(tx);
235
- }
236
- };
145
+ import { normalizeSuiObjectId } from "@mysten/sui/utils";
237
146
 
238
- // src/utils/buffer.ts
239
- import { Buffer } from "buffer";
240
- function stringToBuffer(s) {
241
- return Buffer.from(s, "utf-8");
147
+ // src/utils/coinReservation.ts
148
+ import { bcs, TypeTagSerializer } from "@mysten/sui/bcs";
149
+ import { deriveDynamicFieldID, fromBase58, fromHex, normalizeSuiAddress, toBase58, toHex } from "@mysten/sui/utils";
150
+ var SUI_ACCUMULATOR_ROOT_OBJECT_ID = normalizeSuiAddress("0xacc");
151
+ var ACCUMULATOR_KEY_TYPE_TAG = TypeTagSerializer.parseFromStr(
152
+ "0x2::accumulator::Key<0x2::balance::Balance<0x2::sui::SUI>>"
153
+ );
154
+ var COIN_RESERVATION_MAGIC = new Uint8Array([
155
+ 172,
156
+ 172,
157
+ 172,
158
+ 172,
159
+ 172,
160
+ 172,
161
+ 172,
162
+ 172,
163
+ 172,
164
+ 172,
165
+ 172,
166
+ 172,
167
+ 172,
168
+ 172,
169
+ 172,
170
+ 172,
171
+ 172,
172
+ 172,
173
+ 172,
174
+ 172
175
+ ]);
176
+ function isCoinReservationDigest(digestBase58) {
177
+ const digestBytes = fromBase58(digestBase58);
178
+ const last20Bytes = digestBytes.slice(12, 32);
179
+ return last20Bytes.every((byte, i) => byte === COIN_RESERVATION_MAGIC[i]);
242
180
  }
243
- function Uint8ArrayToHex(b) {
244
- return `0x${Array.prototype.map.call(b, (x) => `0${x.toString(16)}`.slice(-2)).join("")}`;
181
+ function deriveReservationObjectId(owner, chainIdentifier) {
182
+ const keyBcs = bcs.Address.serialize(owner).toBytes();
183
+ const accumulatorId = deriveDynamicFieldID(SUI_ACCUMULATOR_ROOT_OBJECT_ID, ACCUMULATOR_KEY_TYPE_TAG, keyBcs);
184
+ const accBytes = fromHex(accumulatorId.slice(2));
185
+ const chainBytes = fromBase58(chainIdentifier);
186
+ if (chainBytes.length !== 32) {
187
+ throw new Error(`Invalid chain identifier length: expected 32 bytes, got ${chainBytes.length}`);
188
+ }
189
+ const xored = new Uint8Array(32);
190
+ for (let i = 0; i < 32; i++) {
191
+ xored[i] = accBytes[i] ^ chainBytes[i];
192
+ }
193
+ return `0x${toHex(xored)}`;
245
194
  }
246
- function HexToUint8Array(hex) {
247
- return Uint8Array.from(Buffer.from(hex.startsWith("0x") ? hex.slice(2) : hex, "hex"));
195
+ function createCoinReservationRef(reservedBalance, owner, chainIdentifier, epoch) {
196
+ const digestBytes = new Uint8Array(32);
197
+ const view = new DataView(digestBytes.buffer);
198
+ view.setBigUint64(0, reservedBalance, true);
199
+ const epochNum = Number(epoch);
200
+ if (!Number.isSafeInteger(epochNum) || epochNum < 0 || epochNum > 4294967295) {
201
+ throw new Error(`Epoch ${epoch} out of u32 range for coin reservation digest`);
202
+ }
203
+ view.setUint32(8, epochNum, true);
204
+ digestBytes.set(COIN_RESERVATION_MAGIC, 12);
205
+ return {
206
+ objectId: deriveReservationObjectId(owner, chainIdentifier),
207
+ version: "0",
208
+ digest: toBase58(digestBytes)
209
+ };
248
210
  }
249
211
 
250
- // src/utils/crypto.ts
251
- import { verifyPersonalMessageSignature, verifyTransactionSignature } from "@mysten/sui/verify";
212
+ // src/utils/sui.ts
213
+ import { PublicKeySerde as PublicKeySerde2 } from "@msafe/sui3-utils";
214
+ import { parseSerializedSignature } from "@mysten/sui/cryptography";
215
+ import { SuiGraphQLClient } from "@mysten/sui/graphql";
216
+ import { MultiSigPublicKey } from "@mysten/sui/multisig";
252
217
 
253
218
  // src/utils/format.ts
254
- import { normalizeSuiAddress, normalizeStructTag as normalizeStructTag2 } from "@mysten/sui/utils";
219
+ import { normalizeSuiAddress as normalizeSuiAddress2, normalizeStructTag as normalizeStructTag2 } from "@mysten/sui/utils";
255
220
 
256
221
  // src/utils/coin.ts
257
222
  import { normalizeStructTag } from "@mysten/sui/utils";
@@ -315,13 +280,13 @@ var Coin = class _Coin {
315
280
  // src/utils/format.ts
316
281
  var Formatter = class {
317
282
  static normalizeSuiAddress(addr) {
318
- return normalizeSuiAddress(addr);
283
+ return normalizeSuiAddress2(addr);
319
284
  }
320
285
  static normalizeStructTag(struct) {
321
286
  return normalizeStructTag2(struct);
322
287
  }
323
288
  static isSuiAddressEqual(addr1, addr2) {
324
- return normalizeSuiAddress(addr1) === normalizeSuiAddress(addr2);
289
+ return normalizeSuiAddress2(addr1) === normalizeSuiAddress2(addr2);
325
290
  }
326
291
  static isSuiStructEqual(struct1, struct2) {
327
292
  return normalizeStructTag2(struct1) === normalizeStructTag2(struct2);
@@ -337,50 +302,7 @@ function addPrefix(s, prefix) {
337
302
  return prefix + s;
338
303
  }
339
304
 
340
- // src/utils/crypto.ts
341
- var SignatureVerifier = class _SignatureVerifier {
342
- static async getPublicKeyFromSignature(input) {
343
- if (input.messageType === "TransactionBlock") {
344
- return verifyTransactionSignature(input.message, input.signature);
345
- }
346
- return verifyPersonalMessageSignature(input.message, input.signature);
347
- }
348
- static async getPublicKeyFromPersonalSignature(input) {
349
- const message = stringToBuffer(input.messageStr);
350
- return this.getPublicKeyFromSignature({
351
- message,
352
- messageType: "Personal",
353
- signature: input.signature
354
- });
355
- }
356
- static async verifySignature(input) {
357
- const publicKey = await _SignatureVerifier.getPublicKeyFromSignature(input);
358
- return Formatter.isSuiAddressEqual(publicKey.toSuiAddress(), input.targetAddress);
359
- }
360
- static async verifyPersonalSignature(input) {
361
- const message = stringToBuffer(input.messageStr);
362
- return this.verifySignature({
363
- message,
364
- messageType: "Personal",
365
- signature: input.signature,
366
- targetAddress: input.targetAddress
367
- });
368
- }
369
- static async verifyTransactionSignature(input) {
370
- return this.verifySignature({
371
- messageType: "TransactionBlock",
372
- message: input.payload,
373
- signature: input.signature,
374
- targetAddress: input.targetAddress
375
- });
376
- }
377
- };
378
-
379
305
  // src/utils/sui.ts
380
- import { PublicKeySerde as PublicKeySerde2 } from "@msafe/sui3-utils";
381
- import { parseSerializedSignature } from "@mysten/sui/cryptography";
382
- import { SuiGraphQLClient } from "@mysten/sui/graphql";
383
- import { MultiSigPublicKey } from "@mysten/sui/multisig";
384
306
  var SUI_COIN = "0x2::sui::SUI";
385
307
  var GRAPHQL_URL_BY_NETWORK = {
386
308
  mainnet: "https://sui-mainnet.mystenlabs.com/graphql",
@@ -492,14 +414,355 @@ async function getAllCoins(input) {
492
414
  return res;
493
415
  }
494
416
 
417
+ // src/utils/gasFunding.ts
418
+ var GAS_SAFE_OVERHEAD = 1000n;
419
+ var InsufficientGasFundsError = class extends Error {
420
+ gasBudget;
421
+ coinBalance;
422
+ addressBalance;
423
+ constructor(gasBudget, coinBalance, addressBalance) {
424
+ const total = coinBalance + addressBalance;
425
+ super(
426
+ `Insufficient gas funds: need ${gasBudget}, have coinBalance=${coinBalance} + addressBalance=${addressBalance} = ${total}`
427
+ );
428
+ this.name = "InsufficientGasFundsError";
429
+ this.gasBudget = gasBudget;
430
+ this.coinBalance = coinBalance;
431
+ this.addressBalance = addressBalance;
432
+ }
433
+ };
434
+ function computeGasBudget(gasUsed, gasPrice) {
435
+ const { computationCost, storageCost } = gasUsed;
436
+ const safeOverhead = GAS_SAFE_OVERHEAD * gasPrice;
437
+ return BigInt(computationCost) + BigInt(storageCost) + safeOverhead;
438
+ }
439
+ function collectUsedObjectIds(tx) {
440
+ return tx.getData().inputs.reduce((used, input) => {
441
+ const immOrOwned = input.Object?.ImmOrOwnedObject?.objectId;
442
+ if (immOrOwned) {
443
+ used.add(normalizeSuiObjectId(immOrOwned));
444
+ return used;
445
+ }
446
+ const unresolved = input.UnresolvedObject?.objectId;
447
+ if (unresolved) {
448
+ used.add(normalizeSuiObjectId(unresolved));
449
+ }
450
+ return used;
451
+ }, /* @__PURE__ */ new Set());
452
+ }
453
+ async function loadSuiGasFunding(suiClient, owner, tx) {
454
+ const usedObjectIds = collectUsedObjectIds(tx);
455
+ const [coins, balanceRes] = await Promise.all([
456
+ getAllCoins({ suiClient, owner, coinType: SUI_COIN }),
457
+ suiClient.getBalance({ owner, coinType: SUI_COIN })
458
+ ]);
459
+ const paymentCoins = coins.filter((coin) => !usedObjectIds.has(normalizeSuiObjectId(coin.objectId)) && BigInt(coin.balance) > 0n).map((coin) => ({
460
+ objectId: coin.objectId,
461
+ version: coin.version,
462
+ digest: coin.digest,
463
+ balance: BigInt(coin.balance)
464
+ }));
465
+ const coinBalance = paymentCoins.reduce((sum, coin) => sum + coin.balance, 0n);
466
+ const addressBalance = BigInt(balanceRes.balance.addressBalance);
467
+ return {
468
+ paymentCoins,
469
+ coinBalance,
470
+ addressBalance,
471
+ total: coinBalance + addressBalance
472
+ };
473
+ }
474
+ function toObjectRefs(coins) {
475
+ return coins.map(({ objectId, version, digest }) => ({ objectId, version, digest }));
476
+ }
477
+ async function applyMixedTopUp(input) {
478
+ const { tx, suiClient, owner, paymentCoins, topUpAmount } = input;
479
+ if (paymentCoins.length === 0) {
480
+ throw new Error("mixed gas top-up requires at least one SUI coin object");
481
+ }
482
+ if (topUpAmount <= 0n) {
483
+ tx.setGasPayment(toObjectRefs(paymentCoins));
484
+ return;
485
+ }
486
+ const [{ chainIdentifier }, { systemState }] = await Promise.all([
487
+ suiClient.core.getChainIdentifier(),
488
+ suiClient.core.getCurrentSystemState()
489
+ ]);
490
+ const reservation = createCoinReservationRef(topUpAmount, owner, chainIdentifier, systemState.epoch);
491
+ tx.setGasPayment([...toObjectRefs(paymentCoins), reservation]);
492
+ }
493
+ async function selectGasFunding(input) {
494
+ const { tx, suiClient, owner, gasBudget } = input;
495
+ const { payment } = tx.getData().gasData;
496
+ if (payment != null && payment.length > 0) {
497
+ const funding2 = await loadSuiGasFunding(suiClient, owner, tx);
498
+ return {
499
+ mode: "classic",
500
+ gasBudget,
501
+ coinBalance: funding2.coinBalance,
502
+ addressBalance: funding2.addressBalance
503
+ };
504
+ }
505
+ const funding = await loadSuiGasFunding(suiClient, owner, tx);
506
+ const { paymentCoins, coinBalance, addressBalance, total } = funding;
507
+ if (total < gasBudget) {
508
+ throw new InsufficientGasFundsError(gasBudget, coinBalance, addressBalance);
509
+ }
510
+ if (paymentCoins.length > 0 && coinBalance >= gasBudget) {
511
+ tx.setGasPayment(toObjectRefs(paymentCoins));
512
+ return { mode: "classic", gasBudget, coinBalance, addressBalance };
513
+ }
514
+ if (paymentCoins.length > 0 && total >= gasBudget) {
515
+ const topUpAmount = gasBudget - coinBalance;
516
+ await applyMixedTopUp({
517
+ tx,
518
+ suiClient,
519
+ owner,
520
+ paymentCoins,
521
+ topUpAmount
522
+ });
523
+ return { mode: "mixedTopUp", gasBudget, coinBalance, addressBalance };
524
+ }
525
+ if (addressBalance >= gasBudget) {
526
+ tx.setGasPayment([]);
527
+ return { mode: "addressBalance", gasBudget, coinBalance, addressBalance };
528
+ }
529
+ throw new InsufficientGasFundsError(gasBudget, coinBalance, addressBalance);
530
+ }
531
+ async function estimateGasBudget(input) {
532
+ const { tx, suiClient, owner, gasPrice } = input;
533
+ const funding = await loadSuiGasFunding(suiClient, owner, tx);
534
+ if (funding.total <= 0n) {
535
+ throw new InsufficientGasFundsError(0n, funding.coinBalance, funding.addressBalance);
536
+ }
537
+ const probe = Transaction.from(tx);
538
+ probe.setSender(owner);
539
+ probe.setGasPrice(gasPrice);
540
+ if (funding.paymentCoins.length > 0) {
541
+ probe.setGasPayment(toObjectRefs(funding.paymentCoins));
542
+ }
543
+ let built;
544
+ try {
545
+ built = await probe.build({ client: suiClient });
546
+ } catch (e) {
547
+ const message = e instanceof Error ? e.message : String(e);
548
+ throw new Error(`Failed to estimate gas budget: ${message}`);
549
+ }
550
+ const inspect = await suiClient.simulateTransaction({
551
+ transaction: built,
552
+ include: { effects: true }
553
+ });
554
+ const effects = (inspect.Transaction ?? inspect.FailedTransaction)?.effects;
555
+ if (!effects?.gasUsed) {
556
+ throw new Error("Failed to estimate gas budget: simulateTransaction returned no gasUsed");
557
+ }
558
+ if (effects.status.success !== true) {
559
+ const err = effects.status.success === false ? effects.status.error?.message : void 0;
560
+ throw new Error(`Failed to estimate gas budget: ${err ?? "simulation failed"}`);
561
+ }
562
+ return computeGasBudget(effects.gasUsed, gasPrice);
563
+ }
564
+ async function prepareGasFunding(input) {
565
+ const { tx, suiClient, owner, gasPrice } = input;
566
+ const { payment, budget: existingBudget } = tx.getData().gasData;
567
+ const bakedAddressBalanceGas = payment != null && payment.length === 0;
568
+ let { gasBudget } = input;
569
+ if (gasBudget == null) {
570
+ if (bakedAddressBalanceGas || existingBudget == null) {
571
+ gasBudget = await estimateGasBudget({ tx, suiClient, owner, gasPrice });
572
+ } else {
573
+ gasBudget = BigInt(existingBudget);
574
+ }
575
+ }
576
+ tx.setGasBudget(gasBudget);
577
+ return selectGasFunding({ tx, suiClient, owner, gasBudget });
578
+ }
579
+ async function preferClassicGasPayment(input) {
580
+ const { tx, suiClient, owner } = input;
581
+ const { payment } = tx.getData().gasData;
582
+ if (payment != null) {
583
+ return payment.length > 0;
584
+ }
585
+ const funding = await loadSuiGasFunding(suiClient, owner, tx);
586
+ if (funding.paymentCoins.length === 0) {
587
+ return false;
588
+ }
589
+ tx.setGasPayment(toObjectRefs(funding.paymentCoins));
590
+ return true;
591
+ }
592
+
593
+ // src/simulate/simulator.ts
594
+ function formatExecutionError(error) {
595
+ return error.message;
596
+ }
597
+ function gasObjectToReference(gas) {
598
+ if (!gas?.objectId) {
599
+ throw new Error("Gas object missing from simulation effects");
600
+ }
601
+ const version = gas.outputVersion ?? gas.inputVersion ?? "0";
602
+ const digest = gas.outputDigest ?? gas.inputDigest ?? "";
603
+ return { objectId: gas.objectId, version, digest };
604
+ }
605
+ var Simulator = class {
606
+ constructor(globals) {
607
+ this.globals = globals;
608
+ }
609
+ async simulate(input) {
610
+ const tx = this.copyTransaction(input.txb);
611
+ const { suiClient } = this.globals;
612
+ const gasPrice = await this.getGasPrice();
613
+ tx.setGasPrice(gasPrice);
614
+ tx.setSender(input.sender);
615
+ try {
616
+ await prepareGasFunding({
617
+ tx,
618
+ suiClient,
619
+ owner: input.sender,
620
+ gasPrice
621
+ });
622
+ } catch (e) {
623
+ const message = e instanceof Error ? e.message : String(e);
624
+ return {
625
+ success: false,
626
+ gasPrice,
627
+ simulationError: message
628
+ };
629
+ }
630
+ let built;
631
+ try {
632
+ built = await tx.build({ client: suiClient });
633
+ } catch (e) {
634
+ const message = e instanceof Error ? e.message : String(e);
635
+ return {
636
+ success: false,
637
+ gasPrice,
638
+ simulationError: message
639
+ };
640
+ }
641
+ let inspectResult;
642
+ try {
643
+ inspectResult = await suiClient.simulateTransaction({
644
+ transaction: built,
645
+ include: {
646
+ effects: true,
647
+ balanceChanges: true,
648
+ events: true,
649
+ objectTypes: true,
650
+ transaction: true,
651
+ bcs: true,
652
+ commandResults: true
653
+ }
654
+ });
655
+ } catch (e) {
656
+ const message = e instanceof Error ? e.message : String(e);
657
+ return {
658
+ success: false,
659
+ gasPrice,
660
+ simulationError: message
661
+ };
662
+ }
663
+ const txRow = inspectResult.Transaction ?? inspectResult.FailedTransaction;
664
+ const { effects } = txRow;
665
+ if (!effects) {
666
+ throw new Error("simulateTransaction did not return effects");
667
+ }
668
+ const success = effects.status.success === true;
669
+ if (!success) {
670
+ const err = effects.status.success === false ? effects.status.error : null;
671
+ return {
672
+ success,
673
+ gasPrice,
674
+ response: inspectResult,
675
+ simulationError: err ? formatExecutionError(err) : "Simulation failed"
676
+ };
677
+ }
678
+ const gasBudget = this.toGasBudget(effects.gasUsed, gasPrice);
679
+ return {
680
+ success,
681
+ gasPrice,
682
+ gasObject: gasObjectToReference(effects.gasObject),
683
+ gasUsed: effects.gasUsed,
684
+ gasBudget,
685
+ response: inspectResult
686
+ };
687
+ }
688
+ async getGasPrice() {
689
+ const { referenceGasPrice } = await this.globals.suiClient.getReferenceGasPrice();
690
+ return BigInt(referenceGasPrice);
691
+ }
692
+ toGasBudget(gasUsed, gasPrice) {
693
+ return computeGasBudget(gasUsed, gasPrice);
694
+ }
695
+ copyTransaction(tx) {
696
+ return Transaction2.from(tx);
697
+ }
698
+ };
699
+
700
+ // src/utils/buffer.ts
701
+ import { Buffer } from "buffer";
702
+ function stringToBuffer(s) {
703
+ return Buffer.from(s, "utf-8");
704
+ }
705
+ function Uint8ArrayToHex(b) {
706
+ return `0x${Array.prototype.map.call(b, (x) => `0${x.toString(16)}`.slice(-2)).join("")}`;
707
+ }
708
+ function HexToUint8Array(hex) {
709
+ return Uint8Array.from(Buffer.from(hex.startsWith("0x") ? hex.slice(2) : hex, "hex"));
710
+ }
711
+
712
+ // src/utils/crypto.ts
713
+ import { verifyPersonalMessageSignature, verifyTransactionSignature } from "@mysten/sui/verify";
714
+ var SignatureVerifier = class _SignatureVerifier {
715
+ static async getPublicKeyFromSignature(input) {
716
+ if (input.messageType === "TransactionBlock") {
717
+ return verifyTransactionSignature(input.message, input.signature);
718
+ }
719
+ return verifyPersonalMessageSignature(input.message, input.signature);
720
+ }
721
+ static async getPublicKeyFromPersonalSignature(input) {
722
+ const message = stringToBuffer(input.messageStr);
723
+ return this.getPublicKeyFromSignature({
724
+ message,
725
+ messageType: "Personal",
726
+ signature: input.signature
727
+ });
728
+ }
729
+ static async verifySignature(input) {
730
+ const publicKey = await _SignatureVerifier.getPublicKeyFromSignature(input);
731
+ return Formatter.isSuiAddressEqual(publicKey.toSuiAddress(), input.targetAddress);
732
+ }
733
+ static async verifyPersonalSignature(input) {
734
+ const message = stringToBuffer(input.messageStr);
735
+ return this.verifySignature({
736
+ message,
737
+ messageType: "Personal",
738
+ signature: input.signature,
739
+ targetAddress: input.targetAddress
740
+ });
741
+ }
742
+ static async verifyTransactionSignature(input) {
743
+ return this.verifySignature({
744
+ messageType: "TransactionBlock",
745
+ message: input.payload,
746
+ signature: input.signature,
747
+ targetAddress: input.targetAddress
748
+ });
749
+ }
750
+ };
751
+
495
752
  // src/utils/transaction.ts
496
- import { Transaction as Transaction2, isTransaction } from "@mysten/sui/transactions";
753
+ import { Transaction as Transaction3, isTransaction } from "@mysten/sui/transactions";
497
754
  function toSuiTransaction(txb) {
498
755
  if (isTransaction(txb)) {
499
756
  return txb;
500
757
  }
758
+ if (typeof txb === "string" || txb instanceof Uint8Array) {
759
+ return Transaction3.from(txb);
760
+ }
501
761
  const legacy = txb;
502
- return Transaction2.from(legacy.serialize());
762
+ if (typeof legacy?.serialize === "function") {
763
+ return Transaction3.from(legacy.serialize());
764
+ }
765
+ throw new Error("Unsupported transaction value for toSuiTransaction");
503
766
  }
504
767
 
505
768
  // src/utils/iter/iterator.ts
@@ -740,7 +1003,7 @@ var MSafeAccount = class _MSafeAccount {
740
1003
  clientUrl: this.globals.config.suiClient.url,
741
1004
  account: {
742
1005
  address: this.address,
743
- publicKey: fromHex(this.address),
1006
+ publicKey: fromHex2(this.address),
744
1007
  chains: [SUI_MAINNET_CHAIN, SUI_TESTNET_CHAIN],
745
1008
  features: []
746
1009
  }
@@ -783,7 +1046,7 @@ var MSafeAccount = class _MSafeAccount {
783
1046
  clientUrl: this.globals.config.suiClient.url,
784
1047
  account: {
785
1048
  address: this.address,
786
- publicKey: fromHex(this.address),
1049
+ publicKey: fromHex2(this.address),
787
1050
  chains: [SUI_MAINNET_CHAIN, SUI_TESTNET_CHAIN],
788
1051
  features: []
789
1052
  }
@@ -791,14 +1054,20 @@ var MSafeAccount = class _MSafeAccount {
791
1054
  );
792
1055
  }
793
1056
  txb.setGasPrice(input.gasPrice);
794
- txb.setGasBudget(input.gasBudget);
795
1057
  txb.setSender(this.address);
1058
+ await prepareGasFunding({
1059
+ tx: txb,
1060
+ suiClient: this.globals.suiClient,
1061
+ owner: this.address,
1062
+ gasPrice: input.gasPrice,
1063
+ gasBudget: input.gasBudget
1064
+ });
796
1065
  const payload = await txb.build({ client: this.globals.suiClient });
797
1066
  const digest = await txb.getDigest({ client: this.globals.suiClient });
798
1067
  const signature = await this.wallet.signTransactionBlock({ transactionBlock: payload });
799
1068
  return this.backend.proposeIntentionAndBuildVote({
800
1069
  ...input,
801
- payload: toHex(payload),
1070
+ payload: toHex2(payload),
802
1071
  digest,
803
1072
  msafeAddress: this.address,
804
1073
  signature: signature.signature
@@ -856,7 +1125,7 @@ var MSafeAccount = class _MSafeAccount {
856
1125
  }
857
1126
  async proposePlainPayloadIntention(intention) {
858
1127
  const sn = await this.nextSequenceNumber();
859
- const tb = Transaction3.from(intention.payload);
1128
+ const tb = Transaction4.from(intention.payload);
860
1129
  const data = tb.getData();
861
1130
  if (!data.sender || !isSameAddress2(data.sender, this.address)) {
862
1131
  throw new Error("Transaction sender is not same as the multisig address");
@@ -893,6 +1162,14 @@ var MSafeAccount = class _MSafeAccount {
893
1162
  throw new Error("Already rejected");
894
1163
  }
895
1164
  const rejectTxb = toSuiTransaction(buildRejectTxb(this.address));
1165
+ rejectTxb.setGasPrice(input.gasPrice);
1166
+ await prepareGasFunding({
1167
+ tx: rejectTxb,
1168
+ suiClient: this.suiClient,
1169
+ owner: this.address,
1170
+ gasPrice: input.gasPrice,
1171
+ gasBudget: input.gasBudget
1172
+ });
896
1173
  const digest = await rejectTxb.getDigest({ client: this.suiClient });
897
1174
  const payload = await rejectTxb.build({ client: this.suiClient });
898
1175
  const signature = await this.wallet.signTransactionBlock({ transactionBlock: payload });
@@ -918,7 +1195,7 @@ var MSafeAccount = class _MSafeAccount {
918
1195
  clientUrl: this.globals.config.suiClient.url,
919
1196
  account: {
920
1197
  address: this.address,
921
- publicKey: fromHex(this.address),
1198
+ publicKey: fromHex2(this.address),
922
1199
  chains: [SUI_MAINNET_CHAIN, SUI_TESTNET_CHAIN],
923
1200
  features: []
924
1201
  }
@@ -1000,7 +1277,7 @@ var MSafeAccount = class _MSafeAccount {
1000
1277
 
1001
1278
  // src/core/PublicKeyHelper.ts
1002
1279
  import { isSameAddress as isSameAddress3 } from "@msafe/sui3-utils";
1003
- import { normalizeSuiAddress as normalizeSuiAddress2 } from "@mysten/sui/utils";
1280
+ import { normalizeSuiAddress as normalizeSuiAddress3 } from "@mysten/sui/utils";
1004
1281
  var PublicKeyHelper = class {
1005
1282
  constructor(globals) {
1006
1283
  this.globals = globals;
@@ -1049,7 +1326,7 @@ var PublicKeyHelper = class {
1049
1326
  if (!isSameAddress3(address, publicKey.toSuiAddress())) {
1050
1327
  throw new Error("Invalid public key");
1051
1328
  }
1052
- this.knownPublicKeys.set(normalizeSuiAddress2(address), publicKey);
1329
+ this.knownPublicKeys.set(normalizeSuiAddress3(address), publicKey);
1053
1330
  }
1054
1331
  async _getPublicKey(address) {
1055
1332
  const pkBackend = await this.getPublicKeyFromBackend(address);
@@ -1668,6 +1945,7 @@ var MSafeEnabledDto = class {
1668
1945
  };
1669
1946
  export {
1670
1947
  AddressBookSDK,
1948
+ COIN_RESERVATION_MAGIC,
1671
1949
  COIN_TYPE_ARG_REGEX,
1672
1950
  Coin,
1673
1951
  CoinHelper,
@@ -1679,6 +1957,7 @@ export {
1679
1957
  HexToUint8Array,
1680
1958
  InfoTypeEnabledDto,
1681
1959
  InfoTypeKey,
1960
+ InsufficientGasFundsError,
1682
1961
  LOCAL_API_URL,
1683
1962
  LOCAL_SYNCING_URL,
1684
1963
  MAINNET_RPC_URL,
@@ -1696,11 +1975,18 @@ export {
1696
1975
  TESTNET_RPC_URL,
1697
1976
  Uint8ArrayToHex,
1698
1977
  addPrefix,
1978
+ computeGasBudget,
1979
+ createCoinReservationRef,
1980
+ estimateGasBudget,
1699
1981
  getAllCoins,
1700
1982
  getMSafeConfig,
1701
1983
  getPublicKeyFromChain,
1702
1984
  httpUrlToGrpcBaseUrl,
1985
+ isCoinReservationDigest,
1703
1986
  msafeChainToSuiNetwork,
1987
+ preferClassicGasPayment,
1988
+ prepareGasFunding,
1989
+ selectGasFunding,
1704
1990
  stringToBuffer,
1705
1991
  toSuiTransaction
1706
1992
  };