@openzeppelin/miden-multisig-client 0.12.6 → 0.12.7

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.
Files changed (89) hide show
  1. package/README.md +70 -75
  2. package/dist/account/builder.d.ts +0 -18
  3. package/dist/account/builder.d.ts.map +1 -1
  4. package/dist/account/builder.js +16 -24
  5. package/dist/account/builder.js.map +1 -1
  6. package/dist/account/masm.d.ts +2 -0
  7. package/dist/account/masm.d.ts.map +1 -1
  8. package/dist/account/masm.js +428 -1
  9. package/dist/account/masm.js.map +1 -1
  10. package/dist/account/storage.d.ts.map +1 -1
  11. package/dist/account/storage.js +5 -6
  12. package/dist/account/storage.js.map +1 -1
  13. package/dist/client.d.ts +5 -57
  14. package/dist/client.d.ts.map +1 -1
  15. package/dist/client.js +7 -53
  16. package/dist/client.js.map +1 -1
  17. package/dist/client.test.js +28 -0
  18. package/dist/client.test.js.map +1 -1
  19. package/dist/index.d.ts +3 -46
  20. package/dist/index.d.ts.map +1 -1
  21. package/dist/index.js +2 -45
  22. package/dist/index.js.map +1 -1
  23. package/dist/inspector.d.ts +4 -30
  24. package/dist/inspector.d.ts.map +1 -1
  25. package/dist/inspector.js +13 -43
  26. package/dist/inspector.js.map +1 -1
  27. package/dist/multisig/helpers.d.ts.map +1 -1
  28. package/dist/multisig/helpers.js.map +1 -1
  29. package/dist/multisig.d.ts +39 -161
  30. package/dist/multisig.d.ts.map +1 -1
  31. package/dist/multisig.js +190 -241
  32. package/dist/multisig.js.map +1 -1
  33. package/dist/multisig.test.js +41 -31
  34. package/dist/multisig.test.js.map +1 -1
  35. package/dist/procedures.d.ts +20 -47
  36. package/dist/procedures.d.ts.map +1 -1
  37. package/dist/procedures.js +17 -50
  38. package/dist/procedures.js.map +1 -1
  39. package/dist/signer.d.ts +11 -26
  40. package/dist/signer.d.ts.map +1 -1
  41. package/dist/signer.js +41 -26
  42. package/dist/signer.js.map +1 -1
  43. package/dist/transaction/options.d.ts +2 -0
  44. package/dist/transaction/options.d.ts.map +1 -1
  45. package/dist/transaction/updatePsm.d.ts.map +1 -1
  46. package/dist/transaction/updatePsm.js +21 -3
  47. package/dist/transaction/updatePsm.js.map +1 -1
  48. package/dist/transaction/updateSigners.d.ts.map +1 -1
  49. package/dist/transaction/updateSigners.js +21 -3
  50. package/dist/transaction/updateSigners.js.map +1 -1
  51. package/dist/transaction.d.ts.map +1 -1
  52. package/dist/transaction.js +0 -2
  53. package/dist/transaction.js.map +1 -1
  54. package/dist/transaction.test.js +5 -0
  55. package/dist/transaction.test.js.map +1 -1
  56. package/dist/types/proposal.d.ts +16 -8
  57. package/dist/types/proposal.d.ts.map +1 -1
  58. package/dist/types.d.ts +18 -14
  59. package/dist/types.d.ts.map +1 -1
  60. package/dist/utils/signature.d.ts +10 -2
  61. package/dist/utils/signature.d.ts.map +1 -1
  62. package/dist/utils/signature.js +77 -5
  63. package/dist/utils/signature.js.map +1 -1
  64. package/dist/utils/signature.test.js +16 -1
  65. package/dist/utils/signature.test.js.map +1 -1
  66. package/masm/multisig_ecdsa.masm +424 -0
  67. package/masm/psm_ecdsa.masm +179 -0
  68. package/package.json +2 -2
  69. package/src/account/builder.ts +18 -25
  70. package/src/account/masm.ts +433 -2
  71. package/src/account/storage.ts +5 -6
  72. package/src/client.test.ts +36 -0
  73. package/src/client.ts +10 -59
  74. package/src/index.ts +13 -62
  75. package/src/inspector.ts +15 -44
  76. package/src/multisig/helpers.ts +1 -2
  77. package/src/multisig.test.ts +45 -34
  78. package/src/multisig.ts +246 -273
  79. package/src/procedures.ts +21 -56
  80. package/src/signer.ts +48 -28
  81. package/src/transaction/options.ts +2 -0
  82. package/src/transaction/updatePsm.ts +24 -3
  83. package/src/transaction/updateSigners.ts +25 -3
  84. package/src/transaction.test.ts +6 -0
  85. package/src/transaction.ts +0 -2
  86. package/src/types/proposal.ts +15 -8
  87. package/src/types.ts +26 -16
  88. package/src/utils/signature.test.ts +24 -1
  89. package/src/utils/signature.ts +86 -4
package/src/client.ts CHANGED
@@ -1,49 +1,14 @@
1
- /**
2
- * MultisigClient - Factory for creating and loading multisig accounts.
3
- *
4
- * This is the main entry point for the multisig SDK. It provides methods
5
- * to create new multisig accounts and load existing ones.
6
- */
7
-
8
1
  import { type WebClient, Account, AccountId } from '@demox-labs/miden-sdk';
9
- import { PsmHttpClient } from '@openzeppelin/psm-client';
2
+ import { PsmHttpClient, type SignatureScheme } from '@openzeppelin/psm-client';
10
3
  import { Multisig } from './multisig.js';
11
4
  import { createMultisigAccount } from './account/index.js';
12
5
  import { AccountInspector } from './inspector.js';
13
6
  import type { MultisigConfig, Signer } from './types.js';
14
7
 
15
- /**
16
- * Configuration for MultisigClient.
17
- */
18
8
  export interface MultisigClientConfig {
19
- /** PSM server endpoint */
20
9
  psmEndpoint?: string;
21
10
  }
22
11
 
23
- /**
24
- * Client for creating and loading multisig accounts.
25
- *
26
- * @example
27
- * ```typescript
28
- * import { MultisigClient, FalconSigner } from '@openzeppelin/miden-multisig-client';
29
- * import { WebClient, SecretKey } from '@demox-labs/miden-sdk';
30
- *
31
- * // Initialize
32
- * const webClient = await WebClient.createClient('https://rpc.testnet.miden.io:443');
33
- * const secretKey = SecretKey.rpoFalconWithRNG(seed);
34
- * const signer = new FalconSigner(secretKey);
35
- *
36
- * // Create client
37
- * const client = new MultisigClient(webClient, { psmEndpoint: 'http://localhost:3000' });
38
- *
39
- * // Get PSM pubkey for config
40
- * const psmCommitment = await client.psmClient.getPubkey();
41
- *
42
- * // Create multisig
43
- * const config = { threshold: 2, signerCommitments: [...], psmCommitment };
44
- * const multisig = await client.create(config, signer);
45
- * ```
46
- */
47
12
  export class MultisigClient {
48
13
  private readonly webClient: WebClient;
49
14
  private _psmClient: PsmHttpClient;
@@ -53,29 +18,19 @@ export class MultisigClient {
53
18
  this._psmClient = new PsmHttpClient(config.psmEndpoint ?? 'http://localhost:3000');
54
19
  }
55
20
 
56
- /**
57
- * Change the PSM endpoint.
58
- *
59
- * @param endpoint - The new PSM server endpoint URL
60
- */
61
21
  setPsmEndpoint(endpoint: string): void {
62
22
  this._psmClient = new PsmHttpClient(endpoint);
63
23
  }
64
24
 
65
- /**
66
- * Access the internal PSM client.
67
- */
25
+ async initialize(scheme?: SignatureScheme): Promise<{ psmCommitment: string; psmPublicKey?: string }> {
26
+ const { commitment, pubkey } = await this._psmClient.getPubkey(scheme);
27
+ return { psmCommitment: commitment, psmPublicKey: pubkey };
28
+ }
29
+
68
30
  get psmClient(): PsmHttpClient {
69
31
  return this._psmClient;
70
32
  }
71
33
 
72
- /**
73
- * Create a new multisig account.
74
- *
75
- * @param config - Multisig configuration (threshold, signers, PSM commitment)
76
- * @param signer - The signer for this client (one of the cosigners)
77
- * @returns A Multisig instance wrapping the created account
78
- */
79
34
  async create(config: MultisigConfig, signer: Signer): Promise<Multisig> {
80
35
  this._psmClient.setSigner(signer);
81
36
 
@@ -84,13 +39,6 @@ export class MultisigClient {
84
39
  return new Multisig(account, config, this._psmClient, signer, this.webClient);
85
40
  }
86
41
 
87
- /**
88
- * Load an existing multisig account from PSM.
89
- *
90
- * @param accountId - The account ID to load
91
- * @param signer - The signer for this client
92
- * @returns A Multisig instance for the loaded account
93
- */
94
42
  async load(accountId: string, signer: Signer): Promise<Multisig> {
95
43
  this._psmClient.setSigner(signer);
96
44
 
@@ -108,12 +56,15 @@ export class MultisigClient {
108
56
  }
109
57
  const account = Account.deserialize(accountBytes);
110
58
 
111
- const detected = AccountInspector.fromAccount(account);
59
+ const detectedScheme: SignatureScheme = signer.scheme;
60
+
61
+ const detected = AccountInspector.fromAccount(account, detectedScheme);
112
62
  const config: MultisigConfig = {
113
63
  threshold: detected.threshold,
114
64
  signerCommitments: detected.signerCommitments,
115
65
  psmCommitment: detected.psmCommitment ?? '',
116
66
  psmEnabled: detected.psmEnabled,
67
+ signatureScheme: detected.signatureScheme,
117
68
  procedureThresholds: Array.from(detected.procedureThresholds.entries()).map(
118
69
  ([procedure, threshold]) => ({ procedure, threshold })
119
70
  ),
package/src/index.ts CHANGED
@@ -1,47 +1,3 @@
1
- /**
2
- * @openzeppelin/miden-multisig-client
3
- *
4
- * TypeScript SDK for Miden multisig accounts with PSM (Private State Manager) integration.
5
- *
6
- * @example
7
- * ```typescript
8
- * import {
9
- * MultisigClient,
10
- * FalconSigner,
11
- * } from '@openzeppelin/miden-multisig-client';
12
- * import { WebClient, SecretKey } from '@demox-labs/miden-sdk';
13
- *
14
- * // Initialize WebClient
15
- * const webClient = await WebClient.createClient('https://rpc.testnet.miden.io:443');
16
- * await webClient.syncState();
17
- *
18
- * // Generate a key dynamically
19
- * const seed = new Uint8Array(32);
20
- * crypto.getRandomValues(seed);
21
- * const secretKey = SecretKey.rpoFalconWithRNG(seed);
22
- *
23
- * // Store in miden-sdk's keystore
24
- * await webClient.addAccountSecretKeyToWebStore(secretKey);
25
- *
26
- * // Create a signer
27
- * const signer = new FalconSigner(secretKey);
28
- *
29
- * // Create multisig client
30
- * const client = new MultisigClient(webClient, { psmEndpoint: 'http://localhost:3000' });
31
- *
32
- * // Get PSM pubkey for config
33
- * const psmCommitment = await client.psmClient.getPubkey();
34
- *
35
- * // Create multisig account
36
- * const config = { threshold: 2, signerCommitments: [signer.commitment, ...], psmCommitment };
37
- * const multisig = await client.create(config, signer);
38
- *
39
- * // Register on PSM and work with proposals
40
- * await multisig.registerOnPsm();
41
- * await multisig.syncProposals();
42
- * ```
43
- */
44
-
45
1
  export { MultisigClient, type MultisigClientConfig } from './client.js';
46
2
  export { Multisig, type AccountState } from './multisig.js';
47
3
  export { AccountInspector, type DetectedMultisigConfig, type VaultBalance } from './inspector.js';
@@ -55,7 +11,7 @@ export {
55
11
 
56
12
  export { PsmHttpClient, PsmHttpError } from '@openzeppelin/psm-client';
57
13
 
58
- export { FalconSigner } from './signer.js';
14
+ export { FalconSigner, EcdsaSigner } from './signer.js';
59
15
 
60
16
  export {
61
17
  createMultisigAccount,
@@ -68,6 +24,8 @@ export {
68
24
 
69
25
  export {
70
26
  PROCEDURE_ROOTS,
27
+ PROCEDURE_ROOTS_FALCON,
28
+ PROCEDURE_ROOTS_ECDSA,
71
29
  getProcedureRoot,
72
30
  isProcedureName,
73
31
  getProcedureNames,
@@ -75,39 +33,32 @@ export {
75
33
  } from './procedures.js';
76
34
 
77
35
  export type {
78
- // Account types
79
36
  MultisigAccountState,
80
37
  MultisigConfig,
81
38
  CreateAccountResult,
82
39
  ProcedureThreshold,
83
-
84
- // Proposal types
85
- Proposal,
86
- ProposalStatus,
87
- ProposalSignatureEntry,
40
+ TransactionProposal,
41
+ TransactionProposalStatus,
42
+ TransactionProposalSignature,
43
+ ExportedTransactionProposal,
44
+ SignTransactionProposalParams,
45
+ SyncResult,
46
+ TransactionProposalResult,
88
47
  ProposalMetadata,
89
48
  ProposalType,
90
- ExportedProposal,
91
-
92
- // Transaction types
93
49
  TransactionType,
94
-
95
- // Note types
96
50
  ConsumableNote,
97
51
  NoteAsset,
98
-
99
- // Signature types
100
52
  FalconSignature,
53
+ EcdsaSignature,
54
+ ProposalSignature,
55
+ SignatureScheme,
101
56
  Signer,
102
-
103
- // PSM API types
104
57
  AuthConfig,
105
58
  DeltaObject,
106
59
  DeltaStatus,
107
60
  StateObject,
108
61
  CosignerSignature,
109
-
110
- // Request/Response types
111
62
  ConfigureRequest,
112
63
  ConfigureResponse,
113
64
  DeltaProposalRequest,
package/src/inspector.ts CHANGED
@@ -1,11 +1,8 @@
1
- /**
2
- * Account Inspector - Inspects account storage to detect multisig configuration.
3
- */
4
-
5
1
  import { Account, Word } from '@demox-labs/miden-sdk';
6
2
  import { base64ToUint8Array } from './utils/encoding.js';
7
3
  import { wordElementToBigInt, wordToHex } from './utils/word.js';
8
4
  import { getProcedureRoot, getProcedureNames, type ProcedureName } from './procedures.js';
5
+ import type { SignatureScheme } from './types.js';
9
6
 
10
7
  export interface VaultBalance {
11
8
  faucetId: string;
@@ -20,44 +17,19 @@ export interface DetectedMultisigConfig {
20
17
  psmCommitment: string | null;
21
18
  vaultBalances: VaultBalance[];
22
19
  procedureThresholds: Map<ProcedureName, number>;
20
+ signatureScheme: SignatureScheme;
23
21
  }
24
22
 
25
- /**
26
- * Inspects an account to detect its multisig configuration.
27
- *
28
- * @example
29
- * ```typescript
30
- * // From base64-encoded state
31
- * const config = AccountInspector.fromBase64(stateDataBase64);
32
- * console.log(`${config.threshold}-of-${config.numSigners} multisig`);
33
- *
34
- * // From Miden SDK Account
35
- * const config = AccountInspector.fromAccount(account);
36
- * ```
37
- */
38
23
  export class AccountInspector {
39
24
  private constructor() {}
40
25
 
41
- /**
42
- * Inspect a base64-encoded serialized account.
43
- *
44
- * @param base64Data - Base64-encoded Account bytes
45
- * @returns Detected multisig configuration
46
- */
47
- static fromBase64(base64Data: string): DetectedMultisigConfig {
26
+ static fromBase64(base64Data: string, signatureScheme: SignatureScheme = 'falcon'): DetectedMultisigConfig {
48
27
  const bytes = base64ToUint8Array(base64Data);
49
28
  const account = Account.deserialize(bytes);
50
- return AccountInspector.fromAccount(account);
29
+ return AccountInspector.fromAccount(account, signatureScheme);
51
30
  }
52
31
 
53
- /**
54
- * Inspect a Miden SDK Account object.
55
- *
56
- * @param account - The Account object from Miden SDK
57
- * @returns Detected multisig configuration
58
- */
59
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
60
- static fromAccount(account: Account): DetectedMultisigConfig {
32
+ static fromAccount(account: Account, signatureScheme: SignatureScheme = 'falcon'): DetectedMultisigConfig {
61
33
  const storage = account.storage();
62
34
 
63
35
  const slot0 = storage.getItem(0) as Word;
@@ -72,8 +44,8 @@ export class AccountInspector {
72
44
  if (commitment) {
73
45
  signerCommitments.push(wordToHex(commitment));
74
46
  }
75
- } catch (error) {
76
- console.warn(error);
47
+ } catch {
48
+ // Signer not found in storage map
77
49
  }
78
50
  }
79
51
 
@@ -92,8 +64,8 @@ export class AccountInspector {
92
64
  psmCommitment = wordToHex(psmKey);
93
65
  }
94
66
  }
95
- } catch (error) {
96
- console.warn(error);
67
+ } catch {
68
+ // PSM not configured
97
69
  }
98
70
 
99
71
  const vaultBalances: VaultBalance[] = [];
@@ -106,16 +78,14 @@ export class AccountInspector {
106
78
  amount: BigInt(asset.amount()),
107
79
  });
108
80
  }
109
- } catch (error) {
110
- console.warn(error);
81
+ } catch {
82
+ // Vault access failed
111
83
  }
112
84
 
113
- // Read procedure threshold overrides from storage slot 3
114
- // Storage layout: slot 3 is a map of PROC_ROOT => [threshold, 0, 0, 0]
115
85
  const procedureThresholds = new Map<ProcedureName, number>();
116
- for (const procName of getProcedureNames()) {
86
+ for (const procName of getProcedureNames(signatureScheme)) {
117
87
  try {
118
- const rootHex = getProcedureRoot(procName);
88
+ const rootHex = getProcedureRoot(procName, signatureScheme);
119
89
  const rootWord = Word.fromHex(rootHex);
120
90
  const value = storage.getMapItem(3, rootWord) as Word;
121
91
  if (value) {
@@ -125,7 +95,7 @@ export class AccountInspector {
125
95
  }
126
96
  }
127
97
  } catch {
128
- // Procedure threshold not set - use default
98
+ // Not set
129
99
  }
130
100
  }
131
101
 
@@ -137,6 +107,7 @@ export class AccountInspector {
137
107
  psmCommitment,
138
108
  vaultBalances,
139
109
  procedureThresholds,
110
+ signatureScheme,
140
111
  };
141
112
  }
142
113
  }
@@ -1,4 +1,4 @@
1
- import { Account, TransactionSummary, Word } from '@demox-labs/miden-sdk';
1
+ import { Account, TransactionSummary } from '@demox-labs/miden-sdk';
2
2
  import { base64ToUint8Array } from '../utils/encoding.js';
3
3
 
4
4
  export function computeCommitmentFromTxSummary(txSummaryBase64: string): string {
@@ -7,7 +7,6 @@ export function computeCommitmentFromTxSummary(txSummaryBase64: string): string
7
7
  const commitment = summary.toCommitment();
8
8
  return commitment.toHex();
9
9
  }
10
-
11
10
  export function accountIdToHex(account: Account): string {
12
11
  const accountId = account.id();
13
12
  const str = accountId.toString();
@@ -94,6 +94,7 @@ describe('Multisig', () => {
94
94
  psm = new PsmHttpClient('http://localhost:3000');
95
95
 
96
96
  mockSigner = {
97
+ scheme: 'falcon',
97
98
  commitment: '0x' + '1'.repeat(64),
98
99
  publicKey: '0x' + '2'.repeat(64),
99
100
  signAccountIdWithTimestamp: vi.fn().mockReturnValue('0x' + 'a'.repeat(128)),
@@ -294,7 +295,7 @@ describe('Multisig', () => {
294
295
  });
295
296
  });
296
297
 
297
- describe('syncProposals', () => {
298
+ describe('syncTransactionProposals', () => {
298
299
  it('should sync proposals from PSM', async () => {
299
300
  const config = {
300
301
  threshold: 2,
@@ -339,11 +340,12 @@ describe('Multisig', () => {
339
340
  json: async () => ({ proposals: mockProposals }),
340
341
  });
341
342
 
342
- const proposals = await multisig.syncProposals();
343
+ const proposals = await multisig.syncTransactionProposals();
343
344
 
344
345
  expect(proposals.length).toBe(1);
345
346
  expect(proposals[0].nonce).toBe(1);
346
347
  expect(proposals[0].status.type).toBe('pending');
348
+ expect(proposals[0].commitment).toBe(proposals[0].id);
347
349
  });
348
350
 
349
351
  it('should return ready status when enough signatures', async () => {
@@ -390,13 +392,13 @@ describe('Multisig', () => {
390
392
  json: async () => ({ proposals: mockProposals }),
391
393
  });
392
394
 
393
- const proposals = await multisig.syncProposals();
395
+ const proposals = await multisig.syncTransactionProposals();
394
396
 
395
397
  expect(proposals[0].status.type).toBe('ready');
396
398
  });
397
399
  });
398
400
 
399
- describe('listProposals', () => {
401
+ describe('listTransactionProposals', () => {
400
402
  it('should return empty list initially', () => {
401
403
  const config = {
402
404
  threshold: 1,
@@ -405,7 +407,7 @@ describe('Multisig', () => {
405
407
  };
406
408
 
407
409
  const multisig = new Multisig(mockAccount, config, psm, mockSigner, mockWebClient);
408
- expect(multisig.listProposals()).toEqual([]);
410
+ expect(multisig.listTransactionProposals()).toEqual([]);
409
411
  });
410
412
  });
411
413
 
@@ -455,7 +457,7 @@ describe('Multisig', () => {
455
457
  });
456
458
  });
457
459
 
458
- describe('signProposal', () => {
460
+ describe('signTransactionProposal', () => {
459
461
  it('should sign a proposal', async () => {
460
462
  const config = {
461
463
  threshold: 1,
@@ -471,7 +473,7 @@ describe('Multisig', () => {
471
473
  nonce: 1,
472
474
  prev_commitment: '0x' + 'b'.repeat(64),
473
475
  delta_payload: {
474
- tx_summary: { data: 'base64summary' },
476
+ tx_summary: { data: 'AQID' },
475
477
  signatures: [],
476
478
  },
477
479
  status: {
@@ -523,21 +525,30 @@ describe('Multisig', () => {
523
525
  },
524
526
  };
525
527
 
528
+ // Mock for signDeltaProposal
526
529
  mockFetch.mockResolvedValueOnce({
527
530
  ok: true,
528
531
  json: async () => signedDelta,
529
532
  });
530
533
 
534
+ // Mock for auto-sync (syncTransactionProposals)
535
+ mockFetch.mockResolvedValueOnce({
536
+ ok: true,
537
+ json: async () => ({ proposals: [signedDelta] }),
538
+ });
539
+
531
540
  const proposalId = '0x' + 'd'.repeat(64);
532
- const signedProposal = await multisig.signProposal(proposalId);
541
+ const proposals = await multisig.signTransactionProposal(proposalId);
533
542
 
534
543
  expect(mockSigner.signCommitment).toHaveBeenCalledWith(proposalId);
535
- expect(signedProposal.signatures.length).toBe(1);
544
+ expect(proposals.length).toBeGreaterThanOrEqual(1);
545
+ expect(proposals[0].signatures.length).toBe(1);
546
+ expect(proposals[0].commitment).toBe(proposals[0].id);
536
547
  });
537
548
  });
538
549
 
539
- describe('exportProposal', () => {
540
- it('should export proposal for offline signing', async () => {
550
+ describe('exportTransactionProposalToJson', () => {
551
+ it('should export proposal to JSON from local cache', async () => {
541
552
  const config = {
542
553
  threshold: 2,
543
554
  signerCommitments: ['0x' + 'a'.repeat(64), '0x' + 'b'.repeat(64)],
@@ -546,6 +557,7 @@ describe('Multisig', () => {
546
557
 
547
558
  const multisig = new Multisig(mockAccount, config, psm, mockSigner, mockWebClient);
548
559
 
560
+ // First sync proposals to populate local cache
549
561
  const mockProposals = [
550
562
  {
551
563
  account_id: '0x' + 'a'.repeat(30),
@@ -581,16 +593,20 @@ describe('Multisig', () => {
581
593
  json: async () => ({ proposals: mockProposals }),
582
594
  });
583
595
 
596
+ await multisig.syncTransactionProposals();
597
+
584
598
  // The proposal ID is computed from tx_summary, which is mocked to return 'c'.repeat(64)
585
- const exported = await multisig.exportProposal('0x' + 'c'.repeat(64));
599
+ const json = multisig.exportTransactionProposalToJson('0x' + 'c'.repeat(64));
600
+ const exported = JSON.parse(json);
586
601
 
587
602
  expect(exported.accountId).toBe('0x' + 'a'.repeat(30));
588
603
  expect(exported.nonce).toBe(1);
589
604
  expect(exported.txSummaryBase64).toBe('AQID');
590
605
  expect(exported.signatures.length).toBe(1);
606
+ expect(exported.commitment).toBe('0x' + 'c'.repeat(64));
591
607
  });
592
608
 
593
- it('should throw if proposal not found', async () => {
609
+ it('should throw if proposal not found in cache', () => {
594
610
  const config = {
595
611
  threshold: 1,
596
612
  signerCommitments: ['0x' + 'a'.repeat(64)],
@@ -599,18 +615,13 @@ describe('Multisig', () => {
599
615
 
600
616
  const multisig = new Multisig(mockAccount, config, psm, mockSigner, mockWebClient);
601
617
 
602
- mockFetch.mockResolvedValueOnce({
603
- ok: true,
604
- json: async () => ({ proposals: [] }),
605
- });
606
-
607
- await expect(
608
- multisig.exportProposal('0x' + 'nonexistent'.repeat(5))
609
- ).rejects.toThrow('Proposal not found');
618
+ expect(() =>
619
+ multisig.exportTransactionProposalToJson('0x' + 'nonexistent'.repeat(5))
620
+ ).toThrow('Proposal not found');
610
621
  });
611
622
  });
612
623
 
613
- describe('executeProposal', () => {
624
+ describe('executeTransactionProposal', () => {
614
625
  it('should throw if proposal not found locally', async () => {
615
626
  const config = {
616
627
  threshold: 1,
@@ -621,7 +632,7 @@ describe('Multisig', () => {
621
632
  const multisig = new Multisig(mockAccount, config, psm, mockSigner, mockWebClient);
622
633
 
623
634
  await expect(
624
- multisig.executeProposal('0x' + 'nonexistent'.repeat(5))
635
+ multisig.executeTransactionProposal('0x' + 'nonexistent'.repeat(5))
625
636
  ).rejects.toThrow('Proposal not found');
626
637
  });
627
638
 
@@ -670,11 +681,11 @@ describe('Multisig', () => {
670
681
  json: async () => ({ proposals: mockProposals }),
671
682
  });
672
683
 
673
- await multisig.syncProposals();
684
+ await multisig.syncTransactionProposals();
674
685
 
675
686
  // Proposal ID is mocked to return 'c'.repeat(64)
676
687
  await expect(
677
- multisig.executeProposal('0x' + 'c'.repeat(64))
688
+ multisig.executeTransactionProposal('0x' + 'c'.repeat(64))
678
689
  ).rejects.toThrow('not ready for execution');
679
690
  });
680
691
 
@@ -722,7 +733,7 @@ describe('Multisig', () => {
722
733
  ok: true,
723
734
  json: async () => ({ proposals: [readyDelta] }),
724
735
  });
725
- await multisig.syncProposals();
736
+ await multisig.syncTransactionProposals();
726
737
 
727
738
  // executeProposal: getDeltaProposals
728
739
  mockFetch.mockResolvedValueOnce({
@@ -735,7 +746,7 @@ describe('Multisig', () => {
735
746
  json: async () => ({ ...readyDelta, ack_sig: null }),
736
747
  });
737
748
 
738
- await expect(multisig.executeProposal(proposalId)).rejects.toThrow(
749
+ await expect(multisig.executeTransactionProposal(proposalId)).rejects.toThrow(
739
750
  'PSM did not return acknowledgment signature'
740
751
  );
741
752
  });
@@ -794,7 +805,7 @@ describe('Multisig', () => {
794
805
  }),
795
806
  });
796
807
 
797
- const syncedProposals = await multisig.syncProposals();
808
+ const syncedProposals = await multisig.syncTransactionProposals();
798
809
  const syncedProposal = syncedProposals.find(p => p.nonce === 1);
799
810
 
800
811
  expect(syncedProposal?.metadata?.proposalType).toBe('add_signer');
@@ -839,7 +850,7 @@ describe('Multisig', () => {
839
850
  json: async () => ({ proposals: mockProposals }),
840
851
  });
841
852
 
842
- const proposals = await multisig.syncProposals();
853
+ const proposals = await multisig.syncTransactionProposals();
843
854
 
844
855
  expect(proposals.length).toBe(1);
845
856
  expect(proposals[0].metadata?.proposalType).toBe('p2id');
@@ -1041,7 +1052,7 @@ describe('Multisig', () => {
1041
1052
  json: async () => ({ proposals: mockProposalsPending }),
1042
1053
  });
1043
1054
 
1044
- let proposals = await multisig.syncProposals();
1055
+ let proposals = await multisig.syncTransactionProposals();
1045
1056
  expect(proposals[0].status.type).toBe('pending');
1046
1057
 
1047
1058
  // Second sync with 2 signatures (ready)
@@ -1082,7 +1093,7 @@ describe('Multisig', () => {
1082
1093
  json: async () => ({ proposals: mockProposalsReady }),
1083
1094
  });
1084
1095
 
1085
- proposals = await multisig.syncProposals();
1096
+ proposals = await multisig.syncTransactionProposals();
1086
1097
  expect(proposals[0].status.type).toBe('ready');
1087
1098
  });
1088
1099
  });
@@ -1175,7 +1186,7 @@ describe('Multisig', () => {
1175
1186
  json: async () => ({ proposals: rustProposals }),
1176
1187
  });
1177
1188
 
1178
- const proposals = await multisig.syncProposals();
1189
+ const proposals = await multisig.syncTransactionProposals();
1179
1190
 
1180
1191
  expect(proposals.length).toBe(1);
1181
1192
  // The TS client should normalize snake_case to camelCase
@@ -1232,7 +1243,7 @@ describe('Multisig', () => {
1232
1243
  json: async () => ({ proposals: p2idProposals }),
1233
1244
  });
1234
1245
 
1235
- const proposals = await multisig.syncProposals();
1246
+ const proposals = await multisig.syncTransactionProposals();
1236
1247
 
1237
1248
  expect(proposals.length).toBe(1);
1238
1249
  expect(proposals[0].metadata?.proposalType).toBe('p2id');
@@ -1281,7 +1292,7 @@ describe('Multisig', () => {
1281
1292
  json: async () => ({ proposals: switchPsmProposals }),
1282
1293
  });
1283
1294
 
1284
- const proposals = await multisig.syncProposals();
1295
+ const proposals = await multisig.syncTransactionProposals();
1285
1296
 
1286
1297
  expect(proposals.length).toBe(1);
1287
1298
  expect(proposals[0].metadata?.proposalType).toBe('switch_psm');