@msafe/sui3-sdk 1.0.24 → 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 +100 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +20 -1
- package/dist/index.d.ts +20 -1
- package/dist/index.js +100 -3
- package/dist/index.js.map +1 -1
- package/package.json +4 -2
- package/src/utils/gasFunding.ts +133 -5
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,
|
|
@@ -67,6 +68,8 @@ __export(src_exports, {
|
|
|
67
68
|
computeGasBudget: () => computeGasBudget,
|
|
68
69
|
createCoinReservationRef: () => createCoinReservationRef,
|
|
69
70
|
estimateGasBudget: () => estimateGasBudget,
|
|
71
|
+
gasCoinWithdrawal: () => gasCoinWithdrawal,
|
|
72
|
+
gasPaymentCover: () => gasPaymentCover,
|
|
70
73
|
getAllCoins: () => getAllCoins,
|
|
71
74
|
getIntentionContent: () => getIntentionContent,
|
|
72
75
|
getMSafeConfig: () => getMSafeConfig,
|
|
@@ -489,6 +492,8 @@ async function getAllCoins(input) {
|
|
|
489
492
|
var GAS_SAFE_OVERHEAD = 1000n;
|
|
490
493
|
var MAX_GAS_PAYMENT_OBJECTS = 256;
|
|
491
494
|
var ESTIMATE_GAS_COVER = 1000000000n;
|
|
495
|
+
var MAX_PROBE_GAS_BUDGET = 50000000000n;
|
|
496
|
+
var COIN_WITH_BALANCE_INTENT = "CoinWithBalance";
|
|
492
497
|
var InsufficientGasFundsError = class extends Error {
|
|
493
498
|
gasBudget;
|
|
494
499
|
coinBalance;
|
|
@@ -504,22 +509,57 @@ var InsufficientGasFundsError = class extends Error {
|
|
|
504
509
|
this.addressBalance = addressBalance;
|
|
505
510
|
}
|
|
506
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
|
+
}
|
|
507
540
|
function computeGasBudget(gasUsed, gasPrice) {
|
|
508
541
|
const { computationCost, storageCost } = gasUsed;
|
|
509
542
|
const safeOverhead = GAS_SAFE_OVERHEAD * gasPrice;
|
|
510
543
|
return BigInt(computationCost) + BigInt(storageCost) + safeOverhead;
|
|
511
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
|
+
}
|
|
512
551
|
function pickGasPaymentCoins(coins, cover) {
|
|
513
552
|
if (cover <= 0n) {
|
|
514
553
|
return [];
|
|
515
554
|
}
|
|
516
|
-
const sorted = [...coins].filter((coin) => coin.balance > 0n).sort(
|
|
555
|
+
const sorted = [...coins].filter((coin) => coin.balance > 0n).sort(compareBalanceDescending);
|
|
517
556
|
const selected = [];
|
|
518
557
|
let sum = 0n;
|
|
519
|
-
for (
|
|
558
|
+
for (let index = 0; index < sorted.length; index += 1) {
|
|
520
559
|
if (selected.length >= MAX_GAS_PAYMENT_OBJECTS) {
|
|
521
560
|
break;
|
|
522
561
|
}
|
|
562
|
+
const coin = sorted[index];
|
|
523
563
|
selected.push(coin);
|
|
524
564
|
sum += coin.balance;
|
|
525
565
|
if (sum >= cover) {
|
|
@@ -528,6 +568,46 @@ function pickGasPaymentCoins(coins, cover) {
|
|
|
528
568
|
}
|
|
529
569
|
return selected;
|
|
530
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
|
+
}
|
|
531
611
|
function collectUsedObjectIds(tx) {
|
|
532
612
|
const used = tx.getData().inputs.reduce((acc, input) => {
|
|
533
613
|
const immOrOwned = input.Object?.ImmOrOwnedObject?.objectId;
|
|
@@ -671,6 +751,12 @@ async function selectGasFunding(input) {
|
|
|
671
751
|
if (total < gasBudget) {
|
|
672
752
|
throw new InsufficientGasFundsError(gasBudget, coinBalance, addressBalance);
|
|
673
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
|
+
}
|
|
674
760
|
if (paymentCoins.length > 0 && coinBalance >= gasBudget) {
|
|
675
761
|
tx.setGasPayment(toObjectRefs(pickGasPaymentCoins(paymentCoins, gasBudget)));
|
|
676
762
|
return { mode: "classic", gasBudget, coinBalance, addressBalance };
|
|
@@ -726,7 +812,18 @@ async function estimateGasBudget(input) {
|
|
|
726
812
|
const probe = mustResetGas ? cloneWithClearedGasConfig(tx) : import_transactions.Transaction.from(tx);
|
|
727
813
|
probe.setSender(owner);
|
|
728
814
|
probe.setGasPrice(gasPrice);
|
|
729
|
-
|
|
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) {
|
|
730
827
|
probe.setGasPayment(toObjectRefs(pickGasPaymentCoins(funding.paymentCoins, ESTIMATE_GAS_COVER)));
|
|
731
828
|
}
|
|
732
829
|
let built;
|