@msafe/sui3-sdk 1.0.15 → 1.0.18

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,382 @@ 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
+ function cloneWithClearedGasConfig(tx) {
532
+ const data = structuredClone(tx.getData());
533
+ data.gasData.budget = null;
534
+ data.gasData.price = null;
535
+ data.gasData.payment = null;
536
+ data.expiration = null;
537
+ return Transaction.from(JSON.stringify(data));
538
+ }
539
+ function isPositiveBudget(budget) {
540
+ if (budget == null) {
541
+ return false;
542
+ }
543
+ try {
544
+ return BigInt(budget) > 0n;
545
+ } catch {
546
+ return false;
547
+ }
548
+ }
549
+ async function estimateGasBudget(input) {
550
+ const { tx, suiClient, owner, gasPrice } = input;
551
+ const funding = await loadSuiGasFunding(suiClient, owner, tx);
552
+ if (funding.total <= 0n) {
553
+ throw new InsufficientGasFundsError(0n, funding.coinBalance, funding.addressBalance);
554
+ }
555
+ const { budget, payment } = tx.getData().gasData;
556
+ const mustResetGas = payment != null && payment.length === 0 || budget != null && !isPositiveBudget(budget);
557
+ const probe = mustResetGas ? cloneWithClearedGasConfig(tx) : Transaction.from(tx);
558
+ probe.setSender(owner);
559
+ probe.setGasPrice(gasPrice);
560
+ if (funding.paymentCoins.length > 0) {
561
+ probe.setGasPayment(toObjectRefs(funding.paymentCoins));
562
+ }
563
+ let built;
564
+ try {
565
+ built = await probe.build({ client: suiClient });
566
+ } catch (e) {
567
+ const message = e instanceof Error ? e.message : String(e);
568
+ throw new Error(`Failed to estimate gas budget: ${message}`);
569
+ }
570
+ const inspect = await suiClient.simulateTransaction({
571
+ transaction: built,
572
+ include: { effects: true }
573
+ });
574
+ const effects = (inspect.Transaction ?? inspect.FailedTransaction)?.effects;
575
+ if (!effects?.gasUsed) {
576
+ throw new Error("Failed to estimate gas budget: simulateTransaction returned no gasUsed");
577
+ }
578
+ if (effects.status.success !== true) {
579
+ const err = effects.status.success === false ? effects.status.error?.message : void 0;
580
+ throw new Error(`Failed to estimate gas budget: ${err ?? "simulation failed"}`);
581
+ }
582
+ return computeGasBudget(effects.gasUsed, gasPrice);
583
+ }
584
+ async function prepareGasFunding(input) {
585
+ const { tx, suiClient, owner, gasPrice } = input;
586
+ const { payment, budget: existingBudget } = tx.getData().gasData;
587
+ const bakedAddressBalanceGas = payment != null && payment.length === 0;
588
+ let { gasBudget } = input;
589
+ if (gasBudget != null && gasBudget <= 0n) {
590
+ gasBudget = void 0;
591
+ }
592
+ const existingPositive = isPositiveBudget(existingBudget) ? BigInt(existingBudget) : null;
593
+ if (gasBudget == null) {
594
+ if (bakedAddressBalanceGas || existingPositive == null) {
595
+ gasBudget = await estimateGasBudget({ tx, suiClient, owner, gasPrice });
596
+ } else {
597
+ gasBudget = existingPositive;
598
+ }
599
+ }
600
+ tx.setGasBudget(gasBudget);
601
+ if (bakedAddressBalanceGas) {
602
+ tx.setExpiration(null);
603
+ }
604
+ return selectGasFunding({ tx, suiClient, owner, gasBudget });
605
+ }
606
+ async function preferClassicGasPayment(input) {
607
+ const { tx, suiClient, owner } = input;
608
+ const { payment } = tx.getData().gasData;
609
+ if (payment != null) {
610
+ return payment.length > 0;
611
+ }
612
+ const funding = await loadSuiGasFunding(suiClient, owner, tx);
613
+ if (funding.paymentCoins.length === 0) {
614
+ return false;
615
+ }
616
+ tx.setGasPayment(toObjectRefs(funding.paymentCoins));
617
+ return true;
618
+ }
619
+
620
+ // src/simulate/simulator.ts
621
+ function formatExecutionError(error) {
622
+ return error.message;
623
+ }
624
+ function gasObjectToReference(gas) {
625
+ if (!gas?.objectId) {
626
+ throw new Error("Gas object missing from simulation effects");
627
+ }
628
+ const version = gas.outputVersion ?? gas.inputVersion ?? "0";
629
+ const digest = gas.outputDigest ?? gas.inputDigest ?? "";
630
+ return { objectId: gas.objectId, version, digest };
631
+ }
632
+ var Simulator = class {
633
+ constructor(globals) {
634
+ this.globals = globals;
635
+ }
636
+ async simulate(input) {
637
+ const tx = this.copyTransaction(input.txb);
638
+ const { suiClient } = this.globals;
639
+ const gasPrice = await this.getGasPrice();
640
+ tx.setGasPrice(gasPrice);
641
+ tx.setSender(input.sender);
642
+ try {
643
+ await prepareGasFunding({
644
+ tx,
645
+ suiClient,
646
+ owner: input.sender,
647
+ gasPrice
648
+ });
649
+ } catch (e) {
650
+ const message = e instanceof Error ? e.message : String(e);
651
+ return {
652
+ success: false,
653
+ gasPrice,
654
+ simulationError: message
655
+ };
656
+ }
657
+ let built;
658
+ try {
659
+ built = await tx.build({ client: suiClient });
660
+ } catch (e) {
661
+ const message = e instanceof Error ? e.message : String(e);
662
+ return {
663
+ success: false,
664
+ gasPrice,
665
+ simulationError: message
666
+ };
667
+ }
668
+ let inspectResult;
669
+ try {
670
+ inspectResult = await suiClient.simulateTransaction({
671
+ transaction: built,
672
+ include: {
673
+ effects: true,
674
+ balanceChanges: true,
675
+ events: true,
676
+ objectTypes: true,
677
+ transaction: true,
678
+ bcs: true,
679
+ commandResults: true
680
+ }
681
+ });
682
+ } catch (e) {
683
+ const message = e instanceof Error ? e.message : String(e);
684
+ return {
685
+ success: false,
686
+ gasPrice,
687
+ simulationError: message
688
+ };
689
+ }
690
+ const txRow = inspectResult.Transaction ?? inspectResult.FailedTransaction;
691
+ const { effects } = txRow;
692
+ if (!effects) {
693
+ throw new Error("simulateTransaction did not return effects");
694
+ }
695
+ const success = effects.status.success === true;
696
+ if (!success) {
697
+ const err = effects.status.success === false ? effects.status.error : null;
698
+ return {
699
+ success,
700
+ gasPrice,
701
+ response: inspectResult,
702
+ simulationError: err ? formatExecutionError(err) : "Simulation failed"
703
+ };
704
+ }
705
+ const gasBudget = this.toGasBudget(effects.gasUsed, gasPrice);
706
+ return {
707
+ success,
708
+ gasPrice,
709
+ gasObject: gasObjectToReference(effects.gasObject),
710
+ gasUsed: effects.gasUsed,
711
+ gasBudget,
712
+ response: inspectResult
713
+ };
714
+ }
715
+ async getGasPrice() {
716
+ const { referenceGasPrice } = await this.globals.suiClient.getReferenceGasPrice();
717
+ return BigInt(referenceGasPrice);
718
+ }
719
+ toGasBudget(gasUsed, gasPrice) {
720
+ return computeGasBudget(gasUsed, gasPrice);
721
+ }
722
+ copyTransaction(tx) {
723
+ return Transaction2.from(tx);
724
+ }
725
+ };
726
+
727
+ // src/utils/buffer.ts
728
+ import { Buffer } from "buffer";
729
+ function stringToBuffer(s) {
730
+ return Buffer.from(s, "utf-8");
731
+ }
732
+ function Uint8ArrayToHex(b) {
733
+ return `0x${Array.prototype.map.call(b, (x) => `0${x.toString(16)}`.slice(-2)).join("")}`;
734
+ }
735
+ function HexToUint8Array(hex) {
736
+ return Uint8Array.from(Buffer.from(hex.startsWith("0x") ? hex.slice(2) : hex, "hex"));
737
+ }
738
+
739
+ // src/utils/crypto.ts
740
+ import { verifyPersonalMessageSignature, verifyTransactionSignature } from "@mysten/sui/verify";
741
+ var SignatureVerifier = class _SignatureVerifier {
742
+ static async getPublicKeyFromSignature(input) {
743
+ if (input.messageType === "TransactionBlock") {
744
+ return verifyTransactionSignature(input.message, input.signature);
745
+ }
746
+ return verifyPersonalMessageSignature(input.message, input.signature);
747
+ }
748
+ static async getPublicKeyFromPersonalSignature(input) {
749
+ const message = stringToBuffer(input.messageStr);
750
+ return this.getPublicKeyFromSignature({
751
+ message,
752
+ messageType: "Personal",
753
+ signature: input.signature
754
+ });
755
+ }
756
+ static async verifySignature(input) {
757
+ const publicKey = await _SignatureVerifier.getPublicKeyFromSignature(input);
758
+ return Formatter.isSuiAddressEqual(publicKey.toSuiAddress(), input.targetAddress);
759
+ }
760
+ static async verifyPersonalSignature(input) {
761
+ const message = stringToBuffer(input.messageStr);
762
+ return this.verifySignature({
763
+ message,
764
+ messageType: "Personal",
765
+ signature: input.signature,
766
+ targetAddress: input.targetAddress
767
+ });
768
+ }
769
+ static async verifyTransactionSignature(input) {
770
+ return this.verifySignature({
771
+ messageType: "TransactionBlock",
772
+ message: input.payload,
773
+ signature: input.signature,
774
+ targetAddress: input.targetAddress
775
+ });
776
+ }
777
+ };
778
+
495
779
  // src/utils/transaction.ts
496
- import { Transaction as Transaction2, isTransaction } from "@mysten/sui/transactions";
780
+ import { Transaction as Transaction3, isTransaction } from "@mysten/sui/transactions";
497
781
  function toSuiTransaction(txb) {
498
782
  if (isTransaction(txb)) {
499
783
  return txb;
500
784
  }
785
+ if (typeof txb === "string" || txb instanceof Uint8Array) {
786
+ return Transaction3.from(txb);
787
+ }
501
788
  const legacy = txb;
502
- return Transaction2.from(legacy.serialize());
789
+ if (typeof legacy?.serialize === "function") {
790
+ return Transaction3.from(legacy.serialize());
791
+ }
792
+ throw new Error("Unsupported transaction value for toSuiTransaction");
503
793
  }
504
794
 
505
795
  // src/utils/iter/iterator.ts
@@ -740,7 +1030,7 @@ var MSafeAccount = class _MSafeAccount {
740
1030
  clientUrl: this.globals.config.suiClient.url,
741
1031
  account: {
742
1032
  address: this.address,
743
- publicKey: fromHex(this.address),
1033
+ publicKey: fromHex2(this.address),
744
1034
  chains: [SUI_MAINNET_CHAIN, SUI_TESTNET_CHAIN],
745
1035
  features: []
746
1036
  }
@@ -783,7 +1073,7 @@ var MSafeAccount = class _MSafeAccount {
783
1073
  clientUrl: this.globals.config.suiClient.url,
784
1074
  account: {
785
1075
  address: this.address,
786
- publicKey: fromHex(this.address),
1076
+ publicKey: fromHex2(this.address),
787
1077
  chains: [SUI_MAINNET_CHAIN, SUI_TESTNET_CHAIN],
788
1078
  features: []
789
1079
  }
@@ -791,14 +1081,20 @@ var MSafeAccount = class _MSafeAccount {
791
1081
  );
792
1082
  }
793
1083
  txb.setGasPrice(input.gasPrice);
794
- txb.setGasBudget(input.gasBudget);
795
1084
  txb.setSender(this.address);
1085
+ await prepareGasFunding({
1086
+ tx: txb,
1087
+ suiClient: this.globals.suiClient,
1088
+ owner: this.address,
1089
+ gasPrice: input.gasPrice,
1090
+ gasBudget: input.gasBudget
1091
+ });
796
1092
  const payload = await txb.build({ client: this.globals.suiClient });
797
1093
  const digest = await txb.getDigest({ client: this.globals.suiClient });
798
1094
  const signature = await this.wallet.signTransactionBlock({ transactionBlock: payload });
799
1095
  return this.backend.proposeIntentionAndBuildVote({
800
1096
  ...input,
801
- payload: toHex(payload),
1097
+ payload: toHex2(payload),
802
1098
  digest,
803
1099
  msafeAddress: this.address,
804
1100
  signature: signature.signature
@@ -856,7 +1152,7 @@ var MSafeAccount = class _MSafeAccount {
856
1152
  }
857
1153
  async proposePlainPayloadIntention(intention) {
858
1154
  const sn = await this.nextSequenceNumber();
859
- const tb = Transaction3.from(intention.payload);
1155
+ const tb = Transaction4.from(intention.payload);
860
1156
  const data = tb.getData();
861
1157
  if (!data.sender || !isSameAddress2(data.sender, this.address)) {
862
1158
  throw new Error("Transaction sender is not same as the multisig address");
@@ -893,6 +1189,14 @@ var MSafeAccount = class _MSafeAccount {
893
1189
  throw new Error("Already rejected");
894
1190
  }
895
1191
  const rejectTxb = toSuiTransaction(buildRejectTxb(this.address));
1192
+ rejectTxb.setGasPrice(input.gasPrice);
1193
+ await prepareGasFunding({
1194
+ tx: rejectTxb,
1195
+ suiClient: this.suiClient,
1196
+ owner: this.address,
1197
+ gasPrice: input.gasPrice,
1198
+ gasBudget: input.gasBudget
1199
+ });
896
1200
  const digest = await rejectTxb.getDigest({ client: this.suiClient });
897
1201
  const payload = await rejectTxb.build({ client: this.suiClient });
898
1202
  const signature = await this.wallet.signTransactionBlock({ transactionBlock: payload });
@@ -918,7 +1222,7 @@ var MSafeAccount = class _MSafeAccount {
918
1222
  clientUrl: this.globals.config.suiClient.url,
919
1223
  account: {
920
1224
  address: this.address,
921
- publicKey: fromHex(this.address),
1225
+ publicKey: fromHex2(this.address),
922
1226
  chains: [SUI_MAINNET_CHAIN, SUI_TESTNET_CHAIN],
923
1227
  features: []
924
1228
  }
@@ -1000,7 +1304,7 @@ var MSafeAccount = class _MSafeAccount {
1000
1304
 
1001
1305
  // src/core/PublicKeyHelper.ts
1002
1306
  import { isSameAddress as isSameAddress3 } from "@msafe/sui3-utils";
1003
- import { normalizeSuiAddress as normalizeSuiAddress2 } from "@mysten/sui/utils";
1307
+ import { normalizeSuiAddress as normalizeSuiAddress3 } from "@mysten/sui/utils";
1004
1308
  var PublicKeyHelper = class {
1005
1309
  constructor(globals) {
1006
1310
  this.globals = globals;
@@ -1049,7 +1353,7 @@ var PublicKeyHelper = class {
1049
1353
  if (!isSameAddress3(address, publicKey.toSuiAddress())) {
1050
1354
  throw new Error("Invalid public key");
1051
1355
  }
1052
- this.knownPublicKeys.set(normalizeSuiAddress2(address), publicKey);
1356
+ this.knownPublicKeys.set(normalizeSuiAddress3(address), publicKey);
1053
1357
  }
1054
1358
  async _getPublicKey(address) {
1055
1359
  const pkBackend = await this.getPublicKeyFromBackend(address);
@@ -1668,6 +1972,7 @@ var MSafeEnabledDto = class {
1668
1972
  };
1669
1973
  export {
1670
1974
  AddressBookSDK,
1975
+ COIN_RESERVATION_MAGIC,
1671
1976
  COIN_TYPE_ARG_REGEX,
1672
1977
  Coin,
1673
1978
  CoinHelper,
@@ -1679,6 +1984,7 @@ export {
1679
1984
  HexToUint8Array,
1680
1985
  InfoTypeEnabledDto,
1681
1986
  InfoTypeKey,
1987
+ InsufficientGasFundsError,
1682
1988
  LOCAL_API_URL,
1683
1989
  LOCAL_SYNCING_URL,
1684
1990
  MAINNET_RPC_URL,
@@ -1696,11 +2002,18 @@ export {
1696
2002
  TESTNET_RPC_URL,
1697
2003
  Uint8ArrayToHex,
1698
2004
  addPrefix,
2005
+ computeGasBudget,
2006
+ createCoinReservationRef,
2007
+ estimateGasBudget,
1699
2008
  getAllCoins,
1700
2009
  getMSafeConfig,
1701
2010
  getPublicKeyFromChain,
1702
2011
  httpUrlToGrpcBaseUrl,
2012
+ isCoinReservationDigest,
1703
2013
  msafeChainToSuiNetwork,
2014
+ preferClassicGasPayment,
2015
+ prepareGasFunding,
2016
+ selectGasFunding,
1704
2017
  stringToBuffer,
1705
2018
  toSuiTransaction
1706
2019
  };