@msafe/sui3-sdk 1.0.18 → 1.0.19

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@msafe/sui3-sdk",
3
- "version": "1.0.18",
3
+ "version": "1.0.19",
4
4
  "description": "SDK for MSafe SUI V3",
5
5
  "type": "module",
6
6
  "module": "./dist/index.js",
@@ -1,7 +1,7 @@
1
1
  import type { SuiClientTypes } from '@mysten/sui/client';
2
2
  import type { SuiGrpcClient } from '@mysten/sui/grpc';
3
3
  import { Transaction } from '@mysten/sui/transactions';
4
- import { normalizeSuiObjectId } from '@mysten/sui/utils';
4
+ import { normalizeStructTag, normalizeSuiObjectId } from '@mysten/sui/utils';
5
5
 
6
6
  import { createCoinReservationRef } from '@/utils/coinReservation';
7
7
  import { getAllCoins, SUI_COIN } from '@/utils/sui';
@@ -73,6 +73,29 @@ function collectUsedObjectIds(tx: Transaction): Set<string> {
73
73
  }, new Set<string>());
74
74
  }
75
75
 
76
+ /**
77
+ * Mysten CoinWithBalance with useGasCoin=false (type is 0x2::sui::SUI, not "gas")
78
+ * loads every unused SUI coin as a mutable input at resolve time. Those coins
79
+ * cannot also be gas payment or the node rejects:
80
+ * "Mutable object ... cannot appear more than one in one transaction".
81
+ */
82
+ function txConsumesOwnedSuiCoins(tx: Transaction): boolean {
83
+ return tx.getData().commands.some((cmd) => {
84
+ if (cmd.$kind !== '$Intent' || cmd.$Intent.name !== 'CoinWithBalance') {
85
+ return false;
86
+ }
87
+ const type = (cmd.$Intent.data as { type?: string } | undefined)?.type;
88
+ if (!type || type === 'gas') {
89
+ return false;
90
+ }
91
+ try {
92
+ return normalizeStructTag(type) === normalizeStructTag(SUI_COIN);
93
+ } catch {
94
+ return false;
95
+ }
96
+ });
97
+ }
98
+
76
99
  async function loadSuiGasFunding(suiClient: SuiGrpcClient, owner: string, tx: Transaction): Promise<SuiGasFunding> {
77
100
  const usedObjectIds = collectUsedObjectIds(tx);
78
101
  const [coins, balanceRes] = await Promise.all([
@@ -80,6 +103,18 @@ async function loadSuiGasFunding(suiClient: SuiGrpcClient, owner: string, tx: Tr
80
103
  suiClient.getBalance({ owner, coinType: SUI_COIN }),
81
104
  ]);
82
105
 
106
+ const addressBalance = BigInt(balanceRes.balance.addressBalance);
107
+
108
+ // Intent will take every owned SUI coin at resolve time — leave them off gas payment.
109
+ if (txConsumesOwnedSuiCoins(tx)) {
110
+ return {
111
+ paymentCoins: [],
112
+ coinBalance: 0n,
113
+ addressBalance,
114
+ total: addressBalance,
115
+ };
116
+ }
117
+
83
118
  const paymentCoins = coins
84
119
  .filter((coin) => !usedObjectIds.has(normalizeSuiObjectId(coin.objectId)) && BigInt(coin.balance) > 0n)
85
120
  .map((coin) => ({
@@ -90,7 +125,6 @@ async function loadSuiGasFunding(suiClient: SuiGrpcClient, owner: string, tx: Tr
90
125
  }));
91
126
 
92
127
  const coinBalance = paymentCoins.reduce((sum, coin) => sum + coin.balance, 0n);
93
- const addressBalance = BigInt(balanceRes.balance.addressBalance);
94
128
  return {
95
129
  paymentCoins,
96
130
  coinBalance,