@msafe/sui3-sdk 1.0.23 → 1.0.25
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 +125 -5
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +35 -1
- package/dist/index.d.ts +35 -1
- package/dist/index.js +125 -5
- package/dist/index.js.map +1 -1
- package/package.json +4 -2
- package/src/utils/gasFunding.ts +172 -7
package/dist/index.cjs
CHANGED
|
@@ -40,6 +40,7 @@ __export(src_exports, {
|
|
|
40
40
|
DEV_SYNCING_URL: () => DEV_SYNCING_URL,
|
|
41
41
|
ENV_CONFIGS: () => ENV_CONFIGS,
|
|
42
42
|
Formatter: () => Formatter,
|
|
43
|
+
GasPaymentLimitError: () => GasPaymentLimitError,
|
|
43
44
|
HexToUint8Array: () => HexToUint8Array,
|
|
44
45
|
InfoTypeEnabledDto: () => InfoTypeEnabledDto,
|
|
45
46
|
InfoTypeKey: () => InfoTypeKey,
|
|
@@ -47,6 +48,7 @@ __export(src_exports, {
|
|
|
47
48
|
LOCAL_API_URL: () => LOCAL_API_URL,
|
|
48
49
|
LOCAL_SYNCING_URL: () => LOCAL_SYNCING_URL,
|
|
49
50
|
MAINNET_RPC_URL: () => MAINNET_RPC_URL,
|
|
51
|
+
MAX_GAS_PAYMENT_OBJECTS: () => MAX_GAS_PAYMENT_OBJECTS,
|
|
50
52
|
MSafeAccount: () => MSafeAccount,
|
|
51
53
|
MSafeClient: () => MSafeClient,
|
|
52
54
|
MSafeEnabledDto: () => MSafeEnabledDto,
|
|
@@ -66,6 +68,8 @@ __export(src_exports, {
|
|
|
66
68
|
computeGasBudget: () => computeGasBudget,
|
|
67
69
|
createCoinReservationRef: () => createCoinReservationRef,
|
|
68
70
|
estimateGasBudget: () => estimateGasBudget,
|
|
71
|
+
gasCoinWithdrawal: () => gasCoinWithdrawal,
|
|
72
|
+
gasPaymentCover: () => gasPaymentCover,
|
|
69
73
|
getAllCoins: () => getAllCoins,
|
|
70
74
|
getIntentionContent: () => getIntentionContent,
|
|
71
75
|
getMSafeConfig: () => getMSafeConfig,
|
|
@@ -74,6 +78,7 @@ __export(src_exports, {
|
|
|
74
78
|
isCoinReservationDigest: () => isCoinReservationDigest,
|
|
75
79
|
msafeChainToSuiNetwork: () => msafeChainToSuiNetwork,
|
|
76
80
|
parkedPayloadHex: () => parkedPayloadHex,
|
|
81
|
+
pickGasPaymentCoins: () => pickGasPaymentCoins,
|
|
77
82
|
preferClassicGasPayment: () => preferClassicGasPayment,
|
|
78
83
|
prepareGasFunding: () => prepareGasFunding,
|
|
79
84
|
selectGasFunding: () => selectGasFunding,
|
|
@@ -485,6 +490,10 @@ async function getAllCoins(input) {
|
|
|
485
490
|
|
|
486
491
|
// src/utils/gasFunding.ts
|
|
487
492
|
var GAS_SAFE_OVERHEAD = 1000n;
|
|
493
|
+
var MAX_GAS_PAYMENT_OBJECTS = 256;
|
|
494
|
+
var ESTIMATE_GAS_COVER = 1000000000n;
|
|
495
|
+
var MAX_PROBE_GAS_BUDGET = 50000000000n;
|
|
496
|
+
var COIN_WITH_BALANCE_INTENT = "CoinWithBalance";
|
|
488
497
|
var InsufficientGasFundsError = class extends Error {
|
|
489
498
|
gasBudget;
|
|
490
499
|
coinBalance;
|
|
@@ -500,11 +509,105 @@ var InsufficientGasFundsError = class extends Error {
|
|
|
500
509
|
this.addressBalance = addressBalance;
|
|
501
510
|
}
|
|
502
511
|
};
|
|
512
|
+
var GasPaymentLimitError = class extends Error {
|
|
513
|
+
required;
|
|
514
|
+
covered;
|
|
515
|
+
coinCount;
|
|
516
|
+
constructor(required, covered, coinCount) {
|
|
517
|
+
super(gasPaymentLimitMessage(required, covered, coinCount));
|
|
518
|
+
this.name = "GasPaymentLimitError";
|
|
519
|
+
this.required = required;
|
|
520
|
+
this.covered = covered;
|
|
521
|
+
this.coinCount = coinCount;
|
|
522
|
+
}
|
|
523
|
+
};
|
|
524
|
+
function formatSui(mist) {
|
|
525
|
+
const negative = mist < 0n;
|
|
526
|
+
const value = negative ? -mist : mist;
|
|
527
|
+
const whole = value / 1000000000n;
|
|
528
|
+
const fraction = (value % 1000000000n).toString().padStart(9, "0").replace(/0+$/, "");
|
|
529
|
+
const text = fraction.length > 0 ? `${whole}.${fraction}` : `${whole}`;
|
|
530
|
+
return negative ? `-${text}` : text;
|
|
531
|
+
}
|
|
532
|
+
function gasPaymentLimitMessage(required, covered, coinCount) {
|
|
533
|
+
const requiredSui = formatSui(required);
|
|
534
|
+
const coveredSui = formatSui(covered);
|
|
535
|
+
if (coinCount >= MAX_GAS_PAYMENT_OBJECTS) {
|
|
536
|
+
return `This transfer needs ${requiredSui} SUI from gas coins, but the largest ${MAX_GAS_PAYMENT_OBJECTS} coins only cover ${coveredSui} SUI. Send a smaller amount, or consolidate coins into fewer objects first.`;
|
|
537
|
+
}
|
|
538
|
+
return `This transfer needs ${requiredSui} SUI from gas coins, but the available SUI coin objects only cover ${coveredSui} SUI.`;
|
|
539
|
+
}
|
|
503
540
|
function computeGasBudget(gasUsed, gasPrice) {
|
|
504
541
|
const { computationCost, storageCost } = gasUsed;
|
|
505
542
|
const safeOverhead = GAS_SAFE_OVERHEAD * gasPrice;
|
|
506
543
|
return BigInt(computationCost) + BigInt(storageCost) + safeOverhead;
|
|
507
544
|
}
|
|
545
|
+
function compareBalanceDescending(a, b) {
|
|
546
|
+
if (a.balance === b.balance) {
|
|
547
|
+
return 0;
|
|
548
|
+
}
|
|
549
|
+
return a.balance < b.balance ? 1 : -1;
|
|
550
|
+
}
|
|
551
|
+
function pickGasPaymentCoins(coins, cover) {
|
|
552
|
+
if (cover <= 0n) {
|
|
553
|
+
return [];
|
|
554
|
+
}
|
|
555
|
+
const sorted = [...coins].filter((coin) => coin.balance > 0n).sort(compareBalanceDescending);
|
|
556
|
+
const selected = [];
|
|
557
|
+
let sum = 0n;
|
|
558
|
+
for (let index = 0; index < sorted.length; index += 1) {
|
|
559
|
+
if (selected.length >= MAX_GAS_PAYMENT_OBJECTS) {
|
|
560
|
+
break;
|
|
561
|
+
}
|
|
562
|
+
const coin = sorted[index];
|
|
563
|
+
selected.push(coin);
|
|
564
|
+
sum += coin.balance;
|
|
565
|
+
if (sum >= cover) {
|
|
566
|
+
break;
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
return selected;
|
|
570
|
+
}
|
|
571
|
+
function readBigInt(value) {
|
|
572
|
+
if (typeof value === "bigint") {
|
|
573
|
+
return value;
|
|
574
|
+
}
|
|
575
|
+
if (typeof value === "number" && Number.isFinite(value)) {
|
|
576
|
+
return BigInt(Math.trunc(value));
|
|
577
|
+
}
|
|
578
|
+
if (typeof value === "string" && /^-?\d+$/.test(value)) {
|
|
579
|
+
return BigInt(value);
|
|
580
|
+
}
|
|
581
|
+
return 0n;
|
|
582
|
+
}
|
|
583
|
+
function gasCoinWithdrawal(tx) {
|
|
584
|
+
return tx.getData().commands.reduce((sum, command) => {
|
|
585
|
+
if (command.$kind !== "$Intent" || command.$Intent.name !== COIN_WITH_BALANCE_INTENT) {
|
|
586
|
+
return sum;
|
|
587
|
+
}
|
|
588
|
+
const data = command.$Intent.data;
|
|
589
|
+
if (data?.type !== "gas") {
|
|
590
|
+
return sum;
|
|
591
|
+
}
|
|
592
|
+
return sum + readBigInt(data.balance);
|
|
593
|
+
}, 0n);
|
|
594
|
+
}
|
|
595
|
+
function gasPaymentCover(tx, addressBalance, gasBudget) {
|
|
596
|
+
const withdrawal = gasCoinWithdrawal(tx);
|
|
597
|
+
const fromGasCoin = withdrawal > addressBalance ? withdrawal : 0n;
|
|
598
|
+
return gasBudget + fromGasCoin;
|
|
599
|
+
}
|
|
600
|
+
function sumBalances(coins) {
|
|
601
|
+
return coins.reduce((sum, coin) => sum + coin.balance, 0n);
|
|
602
|
+
}
|
|
603
|
+
function pickGasPaymentCoinsForCover(coins, cover) {
|
|
604
|
+
const picked = pickGasPaymentCoins(coins, cover);
|
|
605
|
+
const covered = sumBalances(picked);
|
|
606
|
+
if (cover > 0n && covered < cover) {
|
|
607
|
+
throw new GasPaymentLimitError(cover, covered, picked.length);
|
|
608
|
+
}
|
|
609
|
+
return picked;
|
|
610
|
+
}
|
|
508
611
|
function collectUsedObjectIds(tx) {
|
|
509
612
|
const used = tx.getData().inputs.reduce((acc, input) => {
|
|
510
613
|
const immOrOwned = input.Object?.ImmOrOwnedObject?.objectId;
|
|
@@ -648,8 +751,14 @@ async function selectGasFunding(input) {
|
|
|
648
751
|
if (total < gasBudget) {
|
|
649
752
|
throw new InsufficientGasFundsError(gasBudget, coinBalance, addressBalance);
|
|
650
753
|
}
|
|
754
|
+
const cover = gasPaymentCover(tx, addressBalance, gasBudget);
|
|
755
|
+
if (cover > gasBudget) {
|
|
756
|
+
const picked = pickGasPaymentCoinsForCover(paymentCoins, cover);
|
|
757
|
+
tx.setGasPayment(toObjectRefs(picked));
|
|
758
|
+
return { mode: "classic", gasBudget, coinBalance, addressBalance };
|
|
759
|
+
}
|
|
651
760
|
if (paymentCoins.length > 0 && coinBalance >= gasBudget) {
|
|
652
|
-
tx.setGasPayment(toObjectRefs(paymentCoins));
|
|
761
|
+
tx.setGasPayment(toObjectRefs(pickGasPaymentCoins(paymentCoins, gasBudget)));
|
|
653
762
|
return { mode: "classic", gasBudget, coinBalance, addressBalance };
|
|
654
763
|
}
|
|
655
764
|
if (paymentCoins.length > 0 && total >= gasBudget) {
|
|
@@ -658,7 +767,7 @@ async function selectGasFunding(input) {
|
|
|
658
767
|
tx,
|
|
659
768
|
suiClient,
|
|
660
769
|
owner,
|
|
661
|
-
paymentCoins,
|
|
770
|
+
paymentCoins: pickGasPaymentCoins(paymentCoins, 1n),
|
|
662
771
|
topUpAmount
|
|
663
772
|
});
|
|
664
773
|
return { mode: "mixedTopUp", gasBudget, coinBalance, addressBalance };
|
|
@@ -703,8 +812,19 @@ async function estimateGasBudget(input) {
|
|
|
703
812
|
const probe = mustResetGas ? cloneWithClearedGasConfig(tx) : import_transactions.Transaction.from(tx);
|
|
704
813
|
probe.setSender(owner);
|
|
705
814
|
probe.setGasPrice(gasPrice);
|
|
706
|
-
|
|
707
|
-
|
|
815
|
+
const fromGasCoin = gasPaymentCover(tx, funding.addressBalance, 0n);
|
|
816
|
+
if (fromGasCoin > 0n) {
|
|
817
|
+
const picked = pickGasPaymentCoins(funding.paymentCoins, fromGasCoin + ESTIMATE_GAS_COVER);
|
|
818
|
+
const covered = sumBalances(picked);
|
|
819
|
+
if (covered <= fromGasCoin) {
|
|
820
|
+
throw new GasPaymentLimitError(fromGasCoin, covered, picked.length);
|
|
821
|
+
}
|
|
822
|
+
probe.setGasPayment(toObjectRefs(picked));
|
|
823
|
+
const remainder = covered - fromGasCoin;
|
|
824
|
+
const probeBudget = remainder > MAX_PROBE_GAS_BUDGET ? MAX_PROBE_GAS_BUDGET : remainder;
|
|
825
|
+
probe.setGasBudget(probeBudget);
|
|
826
|
+
} else if (funding.paymentCoins.length > 0) {
|
|
827
|
+
probe.setGasPayment(toObjectRefs(pickGasPaymentCoins(funding.paymentCoins, ESTIMATE_GAS_COVER)));
|
|
708
828
|
}
|
|
709
829
|
let built;
|
|
710
830
|
try {
|
|
@@ -779,7 +899,7 @@ async function preferClassicGasPayment(input) {
|
|
|
779
899
|
if (funding.paymentCoins.length === 0) {
|
|
780
900
|
return false;
|
|
781
901
|
}
|
|
782
|
-
tx.setGasPayment(toObjectRefs(funding.paymentCoins));
|
|
902
|
+
tx.setGasPayment(toObjectRefs(pickGasPaymentCoins(funding.paymentCoins, ESTIMATE_GAS_COVER)));
|
|
783
903
|
return true;
|
|
784
904
|
}
|
|
785
905
|
|