@msafe/sui3-sdk 1.0.19 → 1.0.21

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.19",
3
+ "version": "1.0.21",
4
4
  "description": "SDK for MSafe SUI V3",
5
5
  "type": "module",
6
6
  "module": "./dist/index.js",
@@ -27,7 +27,7 @@ import { CoinHelper } from '@/utils';
27
27
  import { HexToUint8Array } from '@/utils/buffer';
28
28
  import { prepareGasFunding } from '@/utils/gasFunding';
29
29
  import { BatchObjectOptions, getAllOwnedObjects, type OwnedObjectData } from '@/utils/iter/object';
30
- import { toSuiTransaction } from '@/utils/transaction';
30
+ import { getIntentionContent, toSuiTransaction, transactionFromContent } from '@/utils/transaction';
31
31
 
32
32
  export class MSafeAccount {
33
33
  public multiSig: MultiSigAccount;
@@ -140,30 +140,7 @@ export class MSafeAccount {
140
140
  txb?: Transaction;
141
141
  },
142
142
  ) {
143
- let txb: Transaction;
144
- if (request.txb) {
145
- txb = request.txb;
146
- } else {
147
- const appHelper = appHelpers.getAppHelper(request.application);
148
- if (!appHelper) {
149
- throw new Error(`Can't find app helper for application ${request.application}`);
150
- }
151
- txb = toSuiTransaction(
152
- await appHelper.build({
153
- network: this.globals.config.network,
154
- intentionData: request.intention,
155
- txType: request.txType,
156
- txSubType: request.txSubType,
157
- clientUrl: this.globals.config.suiClient.url,
158
- account: {
159
- address: this.address,
160
- publicKey: fromHex(this.address),
161
- chains: [SUI_MAINNET_CHAIN, SUI_TESTNET_CHAIN],
162
- features: [],
163
- },
164
- }),
165
- );
166
- }
143
+ const txb = await this.buildTxFromIntention(request);
167
144
  txb.setSender(this.address);
168
145
  return this.simulator.simulate({ txb, sender: this.address });
169
146
  }
@@ -190,30 +167,7 @@ export class MSafeAccount {
190
167
  txb?: Transaction;
191
168
  },
192
169
  ): Promise<void> {
193
- let txb: Transaction;
194
- if (input.txb) {
195
- txb = input.txb;
196
- } else {
197
- const appHelper = appHelpers.getAppHelper(input.application);
198
- if (!appHelper) {
199
- throw new Error(`Can't find app helper for application ${input.application}`);
200
- }
201
- txb = toSuiTransaction(
202
- await appHelper.build({
203
- network: this.globals.config.network,
204
- intentionData: input.intention,
205
- txType: input.txType,
206
- txSubType: input.txSubType,
207
- clientUrl: this.globals.config.suiClient.url,
208
- account: {
209
- address: this.address,
210
- publicKey: fromHex(this.address),
211
- chains: [SUI_MAINNET_CHAIN, SUI_TESTNET_CHAIN],
212
- features: [],
213
- },
214
- }),
215
- );
216
- }
170
+ const txb = await this.buildTxFromIntention(input);
217
171
  txb.setGasPrice(input.gasPrice);
218
172
  txb.setSender(this.address);
219
173
  await prepareGasFunding({
@@ -340,15 +294,9 @@ export class MSafeAccount {
340
294
  throw new Error('Already rejected');
341
295
  }
342
296
 
297
+ // API independently rebuilds via buildRejectTxb() + build() and compares digest.
298
+ // Do not set gas / prepareGasFunding here — that changes payment and breaks the check.
343
299
  const rejectTxb = toSuiTransaction(buildRejectTxb(this.address));
344
- rejectTxb.setGasPrice(input.gasPrice);
345
- await prepareGasFunding({
346
- tx: rejectTxb,
347
- suiClient: this.suiClient,
348
- owner: this.address,
349
- gasPrice: input.gasPrice,
350
- gasBudget: input.gasBudget,
351
- });
352
300
  const digest = await rejectTxb.getDigest({ client: this.suiClient });
353
301
  const payload = await rejectTxb.build({ client: this.suiClient });
354
302
  const signature = await this.wallet.signTransactionBlock({ transactionBlock: payload });
@@ -362,25 +310,12 @@ export class MSafeAccount {
362
310
 
363
311
  async simulateBuildTransaction(): Promise<SimulationResult> {
364
312
  const intention = await this.backend.getNextIntention({ msafeAddress: this.address });
365
- const appHelper = appHelpers.getAppHelper(intention.application);
366
- if (!appHelper) {
367
- throw new Error(`Can't find app helper for application ${intention.application}`);
368
- }
369
- const txb = toSuiTransaction(
370
- await appHelper.build({
371
- network: this.globals.config.network,
372
- intentionData: intention.intention,
373
- txType: intention.txType as TransactionType,
374
- txSubType: intention.txSubType,
375
- clientUrl: this.globals.config.suiClient.url,
376
- account: {
377
- address: this.address,
378
- publicKey: fromHex(this.address),
379
- chains: [SUI_MAINNET_CHAIN, SUI_TESTNET_CHAIN],
380
- features: [],
381
- },
382
- }),
383
- );
313
+ const txb = await this.buildTxFromIntention({
314
+ application: intention.application,
315
+ intention: intention.intention,
316
+ txType: intention.txType as TransactionType,
317
+ txSubType: intention.txSubType,
318
+ });
384
319
  txb.setSender(this.address);
385
320
  return this.simulator.simulate({ txb, sender: this.address });
386
321
  }
@@ -433,6 +368,56 @@ export class MSafeAccount {
433
368
  });
434
369
  }
435
370
 
371
+ /**
372
+ * Resolve a Transaction from propose/simulate input.
373
+ * 1. Caller-supplied `txb` (generic store path — do not rebuild).
374
+ * 2. Registered app helper `build`.
375
+ * 3. Unregistered app: restore from `intention.content` (JSON or hex).
376
+ */
377
+ private async buildTxFromIntention(input: {
378
+ application: string;
379
+ txType: TransactionType;
380
+ txSubType: string;
381
+ intention: unknown;
382
+ txb?: Transaction;
383
+ }): Promise<Transaction> {
384
+ if (input.txb) {
385
+ return input.txb;
386
+ }
387
+
388
+ let appHelper: ReturnType<typeof appHelpers.getAppHelper> | undefined;
389
+ try {
390
+ appHelper = appHelpers.getAppHelper(input.application);
391
+ } catch {
392
+ appHelper = undefined;
393
+ }
394
+
395
+ if (appHelper) {
396
+ return toSuiTransaction(
397
+ await appHelper.build({
398
+ network: this.globals.config.network,
399
+ intentionData: input.intention,
400
+ txType: input.txType,
401
+ txSubType: input.txSubType,
402
+ clientUrl: this.globals.config.suiClient.url,
403
+ account: {
404
+ address: this.address,
405
+ publicKey: fromHex(this.address),
406
+ chains: [SUI_MAINNET_CHAIN, SUI_TESTNET_CHAIN],
407
+ features: [],
408
+ },
409
+ }),
410
+ );
411
+ }
412
+
413
+ const content = getIntentionContent(input.intention);
414
+ if (content != null) {
415
+ return transactionFromContent(content);
416
+ }
417
+
418
+ throw new Error(`Can't find app helper for application ${input.application}`);
419
+ }
420
+
436
421
  private calculateWeightFromVotes(votes: { userAddress: string }[]) {
437
422
  return this.calculateWeight(votes.map((vote) => vote.userAddress));
438
423
  }
@@ -1,4 +1,5 @@
1
1
  import { Transaction, isTransaction } from '@mysten/sui/transactions';
2
+ import { fromHex } from '@mysten/sui/utils';
2
3
 
3
4
  /**
4
5
  * Normalize a transaction value to {@link Transaction}.
@@ -23,3 +24,34 @@ export function toSuiTransaction(txb: Transaction | unknown): Transaction {
23
24
 
24
25
  throw new Error('Unsupported transaction value for toSuiTransaction');
25
26
  }
27
+
28
+ function isHex(str: string): boolean {
29
+ return /^[0-9a-fA-F]+$/.test(str);
30
+ }
31
+
32
+ export function getIntentionContent(intention: unknown): unknown {
33
+ if (intention && typeof intention === 'object' && 'content' in intention) {
34
+ return (intention as { content: unknown }).content;
35
+ }
36
+ return undefined;
37
+ }
38
+
39
+ /**
40
+ * Restore a Transaction from plain-tx `intention.content`.
41
+ * Accepts V2 JSON (`tx.toJSON()`), hex BCS bytes, or a JSON object.
42
+ */
43
+ export function transactionFromContent(content: unknown): Transaction {
44
+ if (content instanceof Uint8Array) {
45
+ return Transaction.from(content);
46
+ }
47
+ if (typeof content === 'string') {
48
+ if (isHex(content)) {
49
+ return Transaction.from(fromHex(content));
50
+ }
51
+ return Transaction.from(content);
52
+ }
53
+ if (content && typeof content === 'object') {
54
+ return Transaction.from(JSON.stringify(content));
55
+ }
56
+ throw new Error('Invalid transaction content');
57
+ }