@openzeppelin/miden-multisig-client 0.15.2 → 0.16.0

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 (98) hide show
  1. package/README.md +25 -1
  2. package/dist/account/builder.d.ts +2 -1
  3. package/dist/account/builder.d.ts.map +1 -1
  4. package/dist/account/builder.js +1 -0
  5. package/dist/account/builder.js.map +1 -1
  6. package/dist/account/builder.test.js +2 -2
  7. package/dist/account/builder.test.js.map +1 -1
  8. package/dist/client.d.ts +9 -5
  9. package/dist/client.d.ts.map +1 -1
  10. package/dist/client.js +5 -5
  11. package/dist/client.js.map +1 -1
  12. package/dist/client.test.js +46 -15
  13. package/dist/client.test.js.map +1 -1
  14. package/dist/connectivity.d.ts +38 -0
  15. package/dist/connectivity.d.ts.map +1 -0
  16. package/dist/connectivity.js +97 -0
  17. package/dist/connectivity.js.map +1 -0
  18. package/dist/connectivity.test.d.ts +2 -0
  19. package/dist/connectivity.test.d.ts.map +1 -0
  20. package/dist/connectivity.test.js +61 -0
  21. package/dist/connectivity.test.js.map +1 -0
  22. package/dist/index.d.ts +9 -3
  23. package/dist/index.d.ts.map +1 -1
  24. package/dist/index.js +9 -3
  25. package/dist/index.js.map +1 -1
  26. package/dist/multisig.d.ts +68 -7
  27. package/dist/multisig.d.ts.map +1 -1
  28. package/dist/multisig.js +90 -18
  29. package/dist/multisig.js.map +1 -1
  30. package/dist/multisig.test.js +214 -79
  31. package/dist/multisig.test.js.map +1 -1
  32. package/dist/proposal/metadata.d.ts.map +1 -1
  33. package/dist/proposal/metadata.js +12 -0
  34. package/dist/proposal/metadata.js.map +1 -1
  35. package/dist/proposal/metadata.test.js +49 -0
  36. package/dist/proposal/metadata.test.js.map +1 -1
  37. package/dist/raw-client.d.ts +2 -2
  38. package/dist/raw-client.d.ts.map +1 -1
  39. package/dist/raw-client.js +14 -4
  40. package/dist/raw-client.js.map +1 -1
  41. package/dist/raw-client.test.js +25 -3
  42. package/dist/raw-client.test.js.map +1 -1
  43. package/dist/transaction/consumeNotes.d.ts +6 -2
  44. package/dist/transaction/consumeNotes.d.ts.map +1 -1
  45. package/dist/transaction/consumeNotes.js +0 -4
  46. package/dist/transaction/consumeNotes.js.map +1 -1
  47. package/dist/transaction/options.d.ts +3 -0
  48. package/dist/transaction/options.d.ts.map +1 -1
  49. package/dist/transaction/p2id.d.ts +20 -2
  50. package/dist/transaction/p2id.d.ts.map +1 -1
  51. package/dist/transaction/p2id.js +46 -3
  52. package/dist/transaction/p2id.js.map +1 -1
  53. package/dist/transaction/p2id.test.js +66 -6
  54. package/dist/transaction/p2id.test.js.map +1 -1
  55. package/dist/transaction/summary.d.ts +2 -1
  56. package/dist/transaction/summary.d.ts.map +1 -1
  57. package/dist/transaction/summary.js.map +1 -1
  58. package/dist/transaction/updateGuardian.d.ts +6 -2
  59. package/dist/transaction/updateGuardian.d.ts.map +1 -1
  60. package/dist/transaction/updateGuardian.js.map +1 -1
  61. package/dist/transaction/updateProcedureThreshold.d.ts +7 -2
  62. package/dist/transaction/updateProcedureThreshold.d.ts.map +1 -1
  63. package/dist/transaction/updateProcedureThreshold.js.map +1 -1
  64. package/dist/transaction/updateSigners.d.ts +7 -2
  65. package/dist/transaction/updateSigners.d.ts.map +1 -1
  66. package/dist/transaction/updateSigners.js.map +1 -1
  67. package/dist/transaction.d.ts +1 -1
  68. package/dist/transaction.d.ts.map +1 -1
  69. package/dist/transaction.js +1 -1
  70. package/dist/transaction.js.map +1 -1
  71. package/dist/types/proposal.d.ts +5 -0
  72. package/dist/types/proposal.d.ts.map +1 -1
  73. package/dist/types/proposal.js +3 -0
  74. package/dist/types/proposal.js.map +1 -1
  75. package/package.json +3 -3
  76. package/src/account/builder.test.ts +19 -11
  77. package/src/account/builder.ts +2 -1
  78. package/src/client.test.ts +67 -15
  79. package/src/client.ts +18 -10
  80. package/src/connectivity.test.ts +67 -0
  81. package/src/connectivity.ts +111 -0
  82. package/src/index.ts +20 -1
  83. package/src/multisig.test.ts +267 -80
  84. package/src/multisig.ts +109 -20
  85. package/src/proposal/metadata.test.ts +60 -0
  86. package/src/proposal/metadata.ts +14 -0
  87. package/src/raw-client.test.ts +41 -3
  88. package/src/raw-client.ts +15 -5
  89. package/src/transaction/consumeNotes.ts +11 -1
  90. package/src/transaction/options.ts +4 -0
  91. package/src/transaction/p2id.test.ts +98 -8
  92. package/src/transaction/p2id.ts +64 -4
  93. package/src/transaction/summary.ts +12 -0
  94. package/src/transaction/updateGuardian.ts +11 -1
  95. package/src/transaction/updateProcedureThreshold.ts +13 -1
  96. package/src/transaction/updateSigners.ts +13 -1
  97. package/src/transaction.ts +3 -0
  98. package/src/types/proposal.ts +9 -0
@@ -22,6 +22,10 @@ vi.mock('@miden-sdk/miden-sdk', () => ({
22
22
  AccountId: {
23
23
  fromHex: vi.fn((hex: string) => ({ toString: () => hex })),
24
24
  },
25
+ NoteType: {
26
+ Private: 0,
27
+ Public: 1,
28
+ },
25
29
  TransactionSummary: {
26
30
  deserialize: vi.fn().mockReturnValue({
27
31
  toCommitment: () => ({
@@ -87,6 +91,14 @@ vi.mock('./transaction.js', () => ({
87
91
  request: {},
88
92
  salt: { toHex: () => '0x' + 'd'.repeat(64) },
89
93
  }),
94
+ // Mirrors the real implementations against the mocked NoteType values
95
+ // (Private = 0, Public = 1).
96
+ parseP2idNoteType: vi.fn((value?: string) => {
97
+ if (value === undefined || value === 'public') return 1;
98
+ if (value === 'private') return 0;
99
+ throw new Error(`unsupported metadata.noteType '${value}': expected 'public' or 'private'`);
100
+ }),
101
+ p2idNoteTypeToMetadata: vi.fn((noteType?: number) => (noteType === 0 ? 'private' : undefined)),
90
102
  }));
91
103
 
92
104
  vi.mock('./utils/signature.js', async () => {
@@ -119,6 +131,8 @@ vi.mock('./inspector.js', () => ({
119
131
  const mockFetch = vi.fn();
120
132
  vi.stubGlobal('fetch', mockFetch);
121
133
 
134
+ const MIDEN_RPC_ENDPOINT = 'https://rpc.devnet.miden.io';
135
+
122
136
  function mockedAccount(commitmentHex: string, nonce = 0): any {
123
137
  return {
124
138
  commitment: () => ({
@@ -139,6 +153,22 @@ describe('Multisig', () => {
139
153
  let mockAccount: any;
140
154
  let mockWebClient: any;
141
155
 
156
+ function createTestMultisig(
157
+ config: ConstructorParameters<typeof Multisig>[1],
158
+ signer: Signer = mockSigner,
159
+ accountId?: string
160
+ ): Multisig {
161
+ return new Multisig(
162
+ mockAccount,
163
+ config,
164
+ guardian,
165
+ signer,
166
+ mockWebClient,
167
+ accountId,
168
+ MIDEN_RPC_ENDPOINT
169
+ );
170
+ }
171
+
142
172
  beforeEach(() => {
143
173
  mockFetch.mockReset();
144
174
  vi.mocked(executeForSummary).mockResolvedValue({
@@ -213,7 +243,7 @@ describe('Multisig', () => {
213
243
  guardianCommitment: '0x' + 'c'.repeat(64),
214
244
  };
215
245
 
216
- const multisig = new Multisig(mockAccount, config, guardian, mockSigner, mockWebClient);
246
+ const multisig = createTestMultisig(config);
217
247
 
218
248
  expect(multisig.threshold).toBe(2);
219
249
  expect(multisig.signerCommitments).toEqual(config.signerCommitments);
@@ -229,11 +259,31 @@ describe('Multisig', () => {
229
259
  };
230
260
 
231
261
  const accountId = '0x' + 'd'.repeat(30);
232
- const multisig = new Multisig(mockAccount, config, guardian, mockSigner, mockWebClient, accountId);
262
+ const multisig = createTestMultisig(config, mockSigner, accountId);
233
263
 
234
264
  expect(multisig.account).toBe(mockAccount);
235
265
  expect(multisig.accountId).toBe(accountId);
236
266
  });
267
+
268
+ it('should reject a missing Miden RPC endpoint immediately', () => {
269
+ const config = {
270
+ threshold: 2,
271
+ signerCommitments: ['0x' + 'a'.repeat(64), '0x' + 'b'.repeat(64)],
272
+ guardianCommitment: '0x' + 'c'.repeat(64),
273
+ };
274
+
275
+ expect(
276
+ () => new Multisig(
277
+ mockAccount,
278
+ config,
279
+ guardian,
280
+ mockSigner,
281
+ mockWebClient,
282
+ undefined,
283
+ undefined as unknown as string
284
+ )
285
+ ).toThrow('missing required configuration: midenRpcEndpoint');
286
+ });
237
287
  });
238
288
 
239
289
  describe('accountId', () => {
@@ -244,7 +294,7 @@ describe('Multisig', () => {
244
294
  guardianCommitment: '0x' + 'c'.repeat(64),
245
295
  };
246
296
 
247
- const multisig = new Multisig(mockAccount, config, guardian, mockSigner, mockWebClient);
297
+ const multisig = createTestMultisig(config);
248
298
  expect(multisig.accountId).toBe('0x' + 'a'.repeat(30));
249
299
  });
250
300
 
@@ -256,7 +306,7 @@ describe('Multisig', () => {
256
306
  };
257
307
 
258
308
  const accountId = '0x' + 'e'.repeat(30);
259
- const multisig = new Multisig(mockAccount, config, guardian, mockSigner, mockWebClient, accountId);
309
+ const multisig = createTestMultisig(config, mockSigner, accountId);
260
310
  expect(multisig.accountId).toBe(accountId);
261
311
  });
262
312
  });
@@ -269,7 +319,7 @@ describe('Multisig', () => {
269
319
  guardianCommitment: '0x' + 'c'.repeat(64),
270
320
  };
271
321
 
272
- const multisig = new Multisig(mockAccount, config, guardian, mockSigner, mockWebClient);
322
+ const multisig = createTestMultisig(config);
273
323
  expect(multisig.signerCommitment).toBe(mockSigner.commitment);
274
324
  });
275
325
  });
@@ -282,7 +332,7 @@ describe('Multisig', () => {
282
332
  guardianCommitment: '0x' + 'c'.repeat(64),
283
333
  };
284
334
 
285
- const multisig = new Multisig(mockAccount, config, guardian, mockSigner, mockWebClient);
335
+ const multisig = createTestMultisig(config);
286
336
 
287
337
  mockFetch.mockResolvedValueOnce({
288
338
  ok: true,
@@ -503,7 +553,7 @@ describe('Multisig', () => {
503
553
  expect(mockWebClient.newAccount).not.toHaveBeenCalled();
504
554
  });
505
555
 
506
- it('should throw when incoming state nonce is lower than local nonce', async () => {
556
+ it('keeps local state and refreshes config from it when GUARDIAN nonce is behind local', async () => {
507
557
  const config = {
508
558
  threshold: 1,
509
559
  signerCommitments: ['0x' + 'a'.repeat(64)],
@@ -520,7 +570,8 @@ describe('Multisig', () => {
520
570
  'https://rpc.devnet.miden.io'
521
571
  );
522
572
 
523
- mockWebClient.getAccount.mockResolvedValueOnce(mockedAccount('0x' + 'a'.repeat(64), 3));
573
+ const localAccount = mockedAccount('0x' + 'a'.repeat(64), 3);
574
+ mockWebClient.getAccount.mockResolvedValueOnce(localAccount);
524
575
  mockAccountDeserialize.mockReturnValueOnce(mockedAccount('0x' + 'b'.repeat(64), 2));
525
576
  mockFetch.mockResolvedValueOnce({
526
577
  ok: true,
@@ -532,11 +583,24 @@ describe('Multisig', () => {
532
583
  updated_at: '2024-01-02T00:00:00Z',
533
584
  }),
534
585
  });
586
+ mockDetectConfig.mockReturnValueOnce({
587
+ threshold: 2,
588
+ numSigners: 2,
589
+ signerCommitments: ['0x' + '1'.repeat(64), '0x' + '2'.repeat(64)],
590
+ guardianEnabled: true,
591
+ guardianCommitment: '0x' + 'd'.repeat(64),
592
+ vaultBalances: [],
593
+ procedureThresholds: new Map(),
594
+ });
535
595
 
536
- await expect(multisig.syncState()).rejects.toThrow(
537
- 'incoming nonce 2 is not greater than local nonce 3'
538
- );
596
+ // GUARDIAN behind local (nonce 2 < 3): no throw, local kept, no overwrite,
597
+ // and the decision needs no on-chain round-trip.
598
+ await expect(multisig.syncState()).resolves.toBeDefined();
539
599
  expect(mockWebClient.newAccount).not.toHaveBeenCalled();
600
+ expect(mockRpcGetAccountDetails).not.toHaveBeenCalled();
601
+ // Config refreshed from the authoritative local account (UI unfreezes).
602
+ expect(multisig.account).toBe(localAccount);
603
+ expect(multisig.threshold).toBe(2);
540
604
  });
541
605
 
542
606
  it('should throw when incoming state nonce equals local nonce but commitment differs', async () => {
@@ -570,10 +634,49 @@ describe('Multisig', () => {
570
634
  });
571
635
 
572
636
  await expect(multisig.syncState()).rejects.toThrow(
573
- 'incoming nonce 2 is not greater than local nonce 2'
637
+ 'incoming nonce 2 equals local nonce 2 but commitments differ'
574
638
  );
575
639
  expect(mockWebClient.newAccount).not.toHaveBeenCalled();
576
640
  });
641
+
642
+ it('unfreezes Multisig.account after execute when GUARDIAN still lags by one nonce (regression, #343)', async () => {
643
+ const config = {
644
+ threshold: 1,
645
+ signerCommitments: ['0x' + 'a'.repeat(64)],
646
+ guardianCommitment: '0x' + 'c'.repeat(64),
647
+ };
648
+
649
+ const multisig = new Multisig(
650
+ mockAccount,
651
+ config,
652
+ guardian,
653
+ mockSigner,
654
+ mockWebClient,
655
+ undefined,
656
+ 'https://rpc.devnet.miden.io'
657
+ );
658
+
659
+ // Post-execute: local advanced to nonce 1, GUARDIAN still reports nonce 0
660
+ // (candidate not canonicalized yet). Before the fix this threw and left
661
+ // Multisig.account frozen at the pre-execute snapshot.
662
+ const localAccount = mockedAccount('0x' + 'a'.repeat(64), 1);
663
+ mockWebClient.getAccount.mockResolvedValueOnce(localAccount);
664
+ mockAccountDeserialize.mockReturnValueOnce(mockedAccount('0x' + 'b'.repeat(64), 0));
665
+ mockFetch.mockResolvedValueOnce({
666
+ ok: true,
667
+ json: async () => ({
668
+ account_id: multisig.accountId,
669
+ commitment: '0x' + 'b'.repeat(64),
670
+ state_json: { data: 'AQID' },
671
+ created_at: '2024-01-01T00:00:00Z',
672
+ updated_at: '2024-01-02T00:00:00Z',
673
+ }),
674
+ });
675
+
676
+ await expect(multisig.syncState()).resolves.toBeDefined();
677
+ expect(mockWebClient.newAccount).not.toHaveBeenCalled();
678
+ expect(multisig.account).toBe(localAccount);
679
+ });
577
680
  });
578
681
 
579
682
  describe('verifyStateCommitment', () => {
@@ -662,7 +765,7 @@ describe('Multisig', () => {
662
765
  guardianCommitment: '0x' + 'c'.repeat(64),
663
766
  };
664
767
 
665
- const multisig = new Multisig(mockAccount, config, guardian, mockSigner, mockWebClient);
768
+ const multisig = createTestMultisig(config);
666
769
 
667
770
  mockFetch.mockResolvedValueOnce({
668
771
  ok: true,
@@ -690,7 +793,7 @@ describe('Multisig', () => {
690
793
  };
691
794
 
692
795
  guardian.setSigner(ecdsaSigner);
693
- const multisig = new Multisig(mockAccount, config, guardian, ecdsaSigner, mockWebClient);
796
+ const multisig = createTestMultisig(config, ecdsaSigner);
694
797
 
695
798
  mockFetch.mockResolvedValueOnce({
696
799
  ok: true,
@@ -726,6 +829,7 @@ describe('Multisig', () => {
726
829
  mockSigner,
727
830
  mockWebClient,
728
831
  '0x' + 'e'.repeat(30),
832
+ MIDEN_RPC_ENDPOINT,
729
833
  );
730
834
 
731
835
  mockFetch.mockResolvedValueOnce({
@@ -746,7 +850,7 @@ describe('Multisig', () => {
746
850
  guardianCommitment: '0x' + 'c'.repeat(64),
747
851
  };
748
852
 
749
- const multisig = new Multisig(mockAccount, config, guardian, mockSigner, mockWebClient);
853
+ const multisig = createTestMultisig(config);
750
854
 
751
855
  mockFetch.mockResolvedValueOnce({
752
856
  ok: true,
@@ -768,7 +872,7 @@ describe('Multisig', () => {
768
872
  guardianCommitment: '0x' + 'c'.repeat(64),
769
873
  };
770
874
 
771
- const multisig = new Multisig(mockAccount, config, guardian, mockSigner, mockWebClient);
875
+ const multisig = createTestMultisig(config);
772
876
 
773
877
  const mockProposals = [
774
878
  {
@@ -819,7 +923,7 @@ describe('Multisig', () => {
819
923
  guardianCommitment: '0x' + 'c'.repeat(64),
820
924
  };
821
925
 
822
- const multisig = new Multisig(mockAccount, config, guardian, mockSigner, mockWebClient);
926
+ const multisig = createTestMultisig(config);
823
927
 
824
928
  const mockProposals = [
825
929
  {
@@ -868,7 +972,7 @@ describe('Multisig', () => {
868
972
  guardianCommitment: '0x' + 'c'.repeat(64),
869
973
  };
870
974
 
871
- const multisig = new Multisig(mockAccount, config, guardian, mockSigner, mockWebClient);
975
+ const multisig = createTestMultisig(config);
872
976
 
873
977
  mockFetch.mockResolvedValueOnce({
874
978
  ok: true,
@@ -917,7 +1021,7 @@ describe('Multisig', () => {
917
1021
  guardianCommitment: '0x' + 'c'.repeat(64),
918
1022
  };
919
1023
 
920
- const multisig = new Multisig(mockAccount, config, guardian, mockSigner, mockWebClient);
1024
+ const multisig = createTestMultisig(config);
921
1025
 
922
1026
  const mockProposals = [
923
1027
  {
@@ -964,7 +1068,7 @@ describe('Multisig', () => {
964
1068
  guardianCommitment: '0x' + 'c'.repeat(64),
965
1069
  };
966
1070
 
967
- const multisig = new Multisig(mockAccount, config, guardian, mockSigner, mockWebClient);
1071
+ const multisig = createTestMultisig(config);
968
1072
 
969
1073
  const mockProposals = [
970
1074
  {
@@ -1018,7 +1122,7 @@ describe('Multisig', () => {
1018
1122
  guardianCommitment: '0x' + 'c'.repeat(64),
1019
1123
  };
1020
1124
 
1021
- const multisig = new Multisig(mockAccount, config, guardian, mockSigner, mockWebClient);
1125
+ const multisig = createTestMultisig(config);
1022
1126
  expect(multisig.listProposals()).toEqual([]);
1023
1127
  });
1024
1128
  });
@@ -1031,7 +1135,7 @@ describe('Multisig', () => {
1031
1135
  guardianCommitment: '0x' + 'c'.repeat(64),
1032
1136
  };
1033
1137
 
1034
- const multisig = new Multisig(mockAccount, config, guardian, mockSigner, mockWebClient);
1138
+ const multisig = createTestMultisig(config);
1035
1139
 
1036
1140
  const mockDelta = {
1037
1141
  account_id: '0x' + 'a'.repeat(30),
@@ -1075,7 +1179,7 @@ describe('Multisig', () => {
1075
1179
  guardianCommitment: '0x' + 'c'.repeat(64),
1076
1180
  };
1077
1181
 
1078
- const multisig = new Multisig(mockAccount, config, guardian, mockSigner, mockWebClient);
1182
+ const multisig = createTestMultisig(config);
1079
1183
 
1080
1184
  const mockDelta = {
1081
1185
  account_id: '0x' + 'a'.repeat(30),
@@ -1120,7 +1224,7 @@ describe('Multisig', () => {
1120
1224
  guardianCommitment: '0x' + 'c'.repeat(64),
1121
1225
  };
1122
1226
 
1123
- const multisig = new Multisig(mockAccount, config, guardian, mockSigner, mockWebClient);
1227
+ const multisig = createTestMultisig(config);
1124
1228
 
1125
1229
  const mockDelta = {
1126
1230
  account_id: '0x' + 'a'.repeat(30),
@@ -1179,7 +1283,7 @@ describe('Multisig', () => {
1179
1283
  guardianCommitment: '0x' + 'c'.repeat(64),
1180
1284
  };
1181
1285
 
1182
- const multisig = new Multisig(mockAccount, config, guardian, mockSigner, mockWebClient);
1286
+ const multisig = createTestMultisig(config);
1183
1287
 
1184
1288
  const mockDelta = {
1185
1289
  account_id: '0x' + 'a'.repeat(30),
@@ -1216,6 +1320,82 @@ describe('Multisig', () => {
1216
1320
 
1217
1321
  expect(proposal.metadata.description).toBe('Send 100 of asset 0xfaucet... to 0xrecipien...');
1218
1322
  });
1323
+
1324
+ it('threads a private noteType into the request and wire metadata (issue #322)', async () => {
1325
+ const { executeForSummary, buildP2idTransactionRequest } = await import('./transaction.js');
1326
+ const { NoteType } = await import('@miden-sdk/miden-sdk');
1327
+ vi.mocked(executeForSummary).mockResolvedValue({
1328
+ toCommitment: () => ({
1329
+ toHex: () => '0x' + 'c'.repeat(64),
1330
+ }),
1331
+ serialize: () => new Uint8Array([1, 2, 3]),
1332
+ } as any);
1333
+
1334
+ const config = {
1335
+ threshold: 1,
1336
+ signerCommitments: ['0x' + 'a'.repeat(64)],
1337
+ guardianCommitment: '0x' + 'c'.repeat(64),
1338
+ };
1339
+
1340
+ const multisig = createTestMultisig(config);
1341
+
1342
+ const mockDelta = {
1343
+ account_id: '0x' + 'a'.repeat(30),
1344
+ nonce: 1,
1345
+ prev_commitment: '0x' + 'b'.repeat(64),
1346
+ delta_payload: {
1347
+ tx_summary: { data: 'AQID' },
1348
+ signatures: [],
1349
+ metadata: {
1350
+ proposal_type: 'p2id',
1351
+ recipient_id: '0xrecipient',
1352
+ faucet_id: '0xfaucet',
1353
+ amount: '100',
1354
+ note_type: 'private',
1355
+ description: '',
1356
+ },
1357
+ },
1358
+ status: {
1359
+ status: 'pending',
1360
+ timestamp: '2024-01-01T00:00:00Z',
1361
+ proposer_id: '0x' + 'c'.repeat(64),
1362
+ cosigner_sigs: [],
1363
+ },
1364
+ };
1365
+
1366
+ mockFetch.mockResolvedValueOnce({
1367
+ ok: true,
1368
+ json: async () => ({
1369
+ delta: mockDelta,
1370
+ commitment: '0x' + 'c'.repeat(64),
1371
+ }),
1372
+ });
1373
+
1374
+ const proposal = await multisig.createP2idProposal('0xrecipient', '0xfaucet', 100n, 1, {
1375
+ noteType: NoteType.Private,
1376
+ });
1377
+
1378
+ // Propose path builds the private note...
1379
+ expect(vi.mocked(buildP2idTransactionRequest)).toHaveBeenCalledWith(
1380
+ expect.any(String),
1381
+ '0xrecipient',
1382
+ '0xfaucet',
1383
+ 100n,
1384
+ expect.anything(),
1385
+ { noteType: NoteType.Private },
1386
+ );
1387
+ // ...and the rebuild-from-metadata path parses note_type back to Private.
1388
+ const lastCall = vi.mocked(buildP2idTransactionRequest).mock.calls.at(-1)!;
1389
+ expect(lastCall[5]).toMatchObject({ noteType: NoteType.Private });
1390
+
1391
+ // The pushed wire metadata carries note_type so cosigners rebuild the
1392
+ // same private note at verification/execution.
1393
+ const pushBody = JSON.parse(mockFetch.mock.calls.at(-1)![1].body as string);
1394
+ expect(pushBody.delta_payload.metadata.note_type).toBe('private');
1395
+
1396
+ expect(proposal.metadata.proposalType).toBe('p2id');
1397
+ expect((proposal.metadata as { noteType?: string }).noteType).toBe('private');
1398
+ });
1219
1399
  });
1220
1400
 
1221
1401
  describe('createChangeThresholdProposal', () => {
@@ -1269,7 +1449,7 @@ describe('Multisig', () => {
1269
1449
  }),
1270
1450
  });
1271
1451
 
1272
- const multisig = new Multisig(mockAccount, config, guardian, ecdsaSigner, mockWebClient);
1452
+ const multisig = createTestMultisig(config, ecdsaSigner);
1273
1453
  await multisig.createChangeThresholdProposal(2, 1);
1274
1454
 
1275
1455
  expect(buildUpdateSignersTransactionRequest).toHaveBeenCalledWith(
@@ -1293,7 +1473,7 @@ describe('Multisig', () => {
1293
1473
  guardianCommitment: '0x' + 'c'.repeat(64),
1294
1474
  };
1295
1475
 
1296
- const multisig = new Multisig(mockAccount, config, guardian, mockSigner, mockWebClient);
1476
+ const multisig = createTestMultisig(config);
1297
1477
  const newGuardianPubkey = '0x' + '1'.repeat(64);
1298
1478
 
1299
1479
  mockFetch.mockResolvedValueOnce({
@@ -1342,7 +1522,7 @@ describe('Multisig', () => {
1342
1522
  guardianCommitment: '0x' + 'c'.repeat(64),
1343
1523
  };
1344
1524
 
1345
- const multisig = new Multisig(mockAccount, config, guardian, mockSigner, mockWebClient);
1525
+ const multisig = createTestMultisig(config);
1346
1526
 
1347
1527
  mockFetch.mockResolvedValueOnce({
1348
1528
  ok: true,
@@ -1372,7 +1552,7 @@ describe('Multisig', () => {
1372
1552
  guardianCommitment: '0x' + 'c'.repeat(64),
1373
1553
  };
1374
1554
 
1375
- const multisig = new Multisig(mockAccount, config, guardian, ecdsaSigner, mockWebClient);
1555
+ const multisig = createTestMultisig(config, ecdsaSigner);
1376
1556
  const newGuardianCommitment = '0x' + '1'.repeat(64);
1377
1557
 
1378
1558
  mockFetch.mockResolvedValueOnce({
@@ -1427,7 +1607,7 @@ describe('Multisig', () => {
1427
1607
  guardianCommitment: '0x' + 'c'.repeat(64),
1428
1608
  };
1429
1609
 
1430
- const multisig = new Multisig(mockAccount, config, guardian, mockSigner, mockWebClient);
1610
+ const multisig = createTestMultisig(config);
1431
1611
 
1432
1612
  const mockDelta = {
1433
1613
  account_id: '0x' + 'a'.repeat(30),
@@ -1495,7 +1675,7 @@ describe('Multisig', () => {
1495
1675
  guardianCommitment: '0x' + 'c'.repeat(64),
1496
1676
  };
1497
1677
 
1498
- const multisig = new Multisig(mockAccount, config, guardian, ecdsaSigner, mockWebClient);
1678
+ const multisig = createTestMultisig(config, ecdsaSigner);
1499
1679
 
1500
1680
  const mockDelta = {
1501
1681
  account_id: '0x' + 'a'.repeat(30),
@@ -1546,7 +1726,7 @@ describe('Multisig', () => {
1546
1726
  guardianCommitment: '0x' + 'c'.repeat(64),
1547
1727
  };
1548
1728
 
1549
- const multisig = new Multisig(mockAccount, config, guardian, mockSigner, mockWebClient);
1729
+ const multisig = createTestMultisig(config);
1550
1730
 
1551
1731
  // First create a proposal
1552
1732
  const mockDelta = {
@@ -1624,7 +1804,7 @@ describe('Multisig', () => {
1624
1804
  guardianCommitment: '0x' + 'c'.repeat(64),
1625
1805
  };
1626
1806
 
1627
- const multisig = new Multisig(mockAccount, config, guardian, mockSigner, mockWebClient);
1807
+ const multisig = createTestMultisig(config);
1628
1808
 
1629
1809
  const mockDelta = {
1630
1810
  account_id: '0x' + 'a'.repeat(30),
@@ -1675,7 +1855,7 @@ describe('Multisig', () => {
1675
1855
  guardianCommitment: '0x' + 'c'.repeat(64),
1676
1856
  };
1677
1857
 
1678
- const multisig = new Multisig(mockAccount, config, guardian, mockSigner, mockWebClient);
1858
+ const multisig = createTestMultisig(config);
1679
1859
  const proposalId = '0x' + 'd'.repeat(64);
1680
1860
 
1681
1861
  mockFetch.mockResolvedValueOnce({
@@ -1722,7 +1902,7 @@ describe('Multisig', () => {
1722
1902
  guardianCommitment: '0x' + 'c'.repeat(64),
1723
1903
  };
1724
1904
 
1725
- const multisig = new Multisig(mockAccount, config, guardian, mockSigner, mockWebClient);
1905
+ const multisig = createTestMultisig(config);
1726
1906
 
1727
1907
  vi.mocked(executeForSummary).mockResolvedValueOnce({
1728
1908
  toCommitment: () => ({
@@ -1758,7 +1938,7 @@ describe('Multisig', () => {
1758
1938
  guardianCommitment: '0x' + 'c'.repeat(64),
1759
1939
  };
1760
1940
 
1761
- const multisig = new Multisig(mockAccount, config, guardian, mockSigner, mockWebClient);
1941
+ const multisig = createTestMultisig(config);
1762
1942
 
1763
1943
  vi.mocked(executeForSummary).mockResolvedValueOnce({
1764
1944
  toCommitment: () => ({
@@ -1809,7 +1989,7 @@ describe('Multisig', () => {
1809
1989
  guardianCommitment: '0x' + 'c'.repeat(64),
1810
1990
  };
1811
1991
 
1812
- const multisig = new Multisig(mockAccount, config, guardian, mockSigner, mockWebClient);
1992
+ const multisig = createTestMultisig(config);
1813
1993
 
1814
1994
  const mockProposals = [
1815
1995
  {
@@ -1862,7 +2042,7 @@ describe('Multisig', () => {
1862
2042
  guardianCommitment: '0x' + 'c'.repeat(64),
1863
2043
  };
1864
2044
 
1865
- const multisig = new Multisig(mockAccount, config, guardian, mockSigner, mockWebClient);
2045
+ const multisig = createTestMultisig(config);
1866
2046
  const publicKey = '0x' + 'd'.repeat(66);
1867
2047
 
1868
2048
  mockFetch.mockResolvedValueOnce({
@@ -1920,13 +2100,20 @@ describe('Multisig', () => {
1920
2100
  guardianCommitment: '0x' + 'c'.repeat(64),
1921
2101
  };
1922
2102
 
1923
- const multisig = new Multisig(mockAccount, config, guardian, mockSigner, mockWebClient);
2103
+ const multisig = createTestMultisig(config);
1924
2104
 
1925
2105
  mockFetch.mockResolvedValueOnce({
1926
2106
  ok: false,
1927
2107
  status: 404,
1928
2108
  statusText: 'Not Found',
1929
- text: async () => 'Proposal not found',
2109
+ // Feature 009: only a conforming { code, message, meta } envelope is
2110
+ // folded into the error message; raw text bodies are dropped.
2111
+ text: async () =>
2112
+ JSON.stringify({
2113
+ code: 'GUARDIAN_PROPOSAL_NOT_FOUND',
2114
+ message: 'Proposal not found',
2115
+ meta: { retryable: false },
2116
+ }),
1930
2117
  });
1931
2118
 
1932
2119
  await expect(
@@ -1943,7 +2130,7 @@ describe('Multisig', () => {
1943
2130
  guardianCommitment: '0x' + 'c'.repeat(64),
1944
2131
  };
1945
2132
 
1946
- const multisig = new Multisig(mockAccount, config, guardian, mockSigner, mockWebClient);
2133
+ const multisig = createTestMultisig(config);
1947
2134
 
1948
2135
  const exported = {
1949
2136
  accountId: multisig.accountId,
@@ -1976,7 +2163,7 @@ describe('Multisig', () => {
1976
2163
  guardianCommitment: '0x' + 'c'.repeat(64),
1977
2164
  };
1978
2165
 
1979
- const multisig = new Multisig(mockAccount, config, guardian, mockSigner, mockWebClient);
2166
+ const multisig = createTestMultisig(config);
1980
2167
  const publicKey = '0x' + 'd'.repeat(66);
1981
2168
 
1982
2169
  const proposal = await multisig.importProposal(
@@ -2023,7 +2210,7 @@ describe('Multisig', () => {
2023
2210
  guardianCommitment: '0x' + 'c'.repeat(64),
2024
2211
  };
2025
2212
 
2026
- const multisig = new Multisig(mockAccount, config, guardian, mockSigner, mockWebClient);
2213
+ const multisig = createTestMultisig(config);
2027
2214
 
2028
2215
  await expect(
2029
2216
  multisig.importProposal(
@@ -2057,7 +2244,7 @@ describe('Multisig', () => {
2057
2244
  guardianCommitment: '0x' + 'c'.repeat(64),
2058
2245
  };
2059
2246
 
2060
- const multisig = new Multisig(mockAccount, config, guardian, mockSigner, mockWebClient);
2247
+ const multisig = createTestMultisig(config);
2061
2248
 
2062
2249
  const exported = {
2063
2250
  accountId: multisig.accountId,
@@ -2102,7 +2289,7 @@ describe('Multisig', () => {
2102
2289
  publicKey: '0x' + '2'.repeat(66),
2103
2290
  };
2104
2291
 
2105
- const multisig = new Multisig(mockAccount, config, guardian, ecdsaSigner, mockWebClient);
2292
+ const multisig = createTestMultisig(config, ecdsaSigner);
2106
2293
  const cachedProposalId = '0x' + 'c'.repeat(64);
2107
2294
  const requestedProposalId = '0x' + 'C'.repeat(64);
2108
2295
  const cosignerPubkey = '0x' + '3'.repeat(66);
@@ -2225,7 +2412,7 @@ describe('Multisig', () => {
2225
2412
  guardianCommitment: '0x' + 'c'.repeat(64),
2226
2413
  };
2227
2414
 
2228
- const multisig = new Multisig(mockAccount, config, guardian, mockSigner, mockWebClient);
2415
+ const multisig = createTestMultisig(config);
2229
2416
  const proposalId = '0x' + 'c'.repeat(64);
2230
2417
  const newGuardianPubkey = '0x' + '1'.repeat(64);
2231
2418
  const finalRequest = { kind: 'final-switch-guardian-request' };
@@ -2280,7 +2467,7 @@ describe('Multisig', () => {
2280
2467
  guardianCommitment: '0x' + 'c'.repeat(64),
2281
2468
  };
2282
2469
 
2283
- const multisig = new Multisig(mockAccount, config, guardian, mockSigner, mockWebClient);
2470
+ const multisig = createTestMultisig(config);
2284
2471
 
2285
2472
  await expect(
2286
2473
  multisig.createTransactionProposalRequest('0x' + 'nonexistent'.repeat(5))
@@ -2294,7 +2481,7 @@ describe('Multisig', () => {
2294
2481
  guardianCommitment: '0x' + 'c'.repeat(64),
2295
2482
  };
2296
2483
 
2297
- const multisig = new Multisig(mockAccount, config, guardian, mockSigner, mockWebClient);
2484
+ const multisig = createTestMultisig(config);
2298
2485
 
2299
2486
  const mockProposals = [
2300
2487
  {
@@ -2345,7 +2532,7 @@ describe('Multisig', () => {
2345
2532
  guardianCommitment: '0x' + 'c'.repeat(64),
2346
2533
  };
2347
2534
 
2348
- const multisig = new Multisig(mockAccount, config, guardian, mockSigner, mockWebClient);
2535
+ const multisig = createTestMultisig(config);
2349
2536
  const proposalId = '0x' + 'c'.repeat(64);
2350
2537
 
2351
2538
  vi.mocked(executeForSummary).mockResolvedValueOnce({
@@ -2388,7 +2575,7 @@ describe('Multisig', () => {
2388
2575
  guardianCommitment: '0x' + 'c'.repeat(64),
2389
2576
  };
2390
2577
 
2391
- const multisig = new Multisig(mockAccount, config, guardian, mockSigner, mockWebClient);
2578
+ const multisig = createTestMultisig(config);
2392
2579
  const proposalId = '0x' + 'c'.repeat(64);
2393
2580
 
2394
2581
  (multisig as any).proposals.set(proposalId, {
@@ -2430,7 +2617,7 @@ describe('Multisig', () => {
2430
2617
  guardianCommitment: '0x' + 'c'.repeat(64),
2431
2618
  };
2432
2619
 
2433
- const multisig = new Multisig(mockAccount, config, guardian, mockSigner, mockWebClient);
2620
+ const multisig = createTestMultisig(config);
2434
2621
  const proposalId = '0x' + 'c'.repeat(64);
2435
2622
 
2436
2623
  (multisig as any).proposals.set(proposalId, {
@@ -2502,7 +2689,7 @@ describe('Multisig', () => {
2502
2689
  guardianCommitment: '0x' + 'c'.repeat(64),
2503
2690
  };
2504
2691
 
2505
- const multisig = new Multisig(mockAccount, config, guardian, mockSigner, mockWebClient);
2692
+ const multisig = createTestMultisig(config);
2506
2693
  const proposalId = '0x' + 'c'.repeat(64);
2507
2694
  const finalRequest = { kind: 'fresh-message-word-request' };
2508
2695
 
@@ -2601,7 +2788,7 @@ describe('Multisig', () => {
2601
2788
  guardianCommitment: '0x' + 'c'.repeat(64),
2602
2789
  };
2603
2790
 
2604
- const multisig = new Multisig(mockAccount, config, guardian, mockSigner, mockWebClient);
2791
+ const multisig = createTestMultisig(config);
2605
2792
  const proposalId = '0x' + 'c'.repeat(64);
2606
2793
 
2607
2794
  (multisig as any).proposals.set(proposalId, {
@@ -2644,7 +2831,7 @@ describe('Multisig', () => {
2644
2831
  guardianCommitment: '0x' + 'c'.repeat(64),
2645
2832
  };
2646
2833
 
2647
- const multisig = new Multisig(mockAccount, config, guardian, mockSigner, mockWebClient);
2834
+ const multisig = createTestMultisig(config);
2648
2835
 
2649
2836
  await expect(
2650
2837
  multisig.executeProposal('0x' + 'nonexistent'.repeat(5))
@@ -2658,7 +2845,7 @@ describe('Multisig', () => {
2658
2845
  guardianCommitment: '0x' + 'c'.repeat(64),
2659
2846
  };
2660
2847
 
2661
- const multisig = new Multisig(mockAccount, config, guardian, mockSigner, mockWebClient);
2848
+ const multisig = createTestMultisig(config);
2662
2849
 
2663
2850
  // Sync with pending proposal (only 1 signature)
2664
2851
  const mockProposals = [
@@ -2711,7 +2898,7 @@ describe('Multisig', () => {
2711
2898
  guardianCommitment: '0x' + 'c'.repeat(64),
2712
2899
  };
2713
2900
 
2714
- const multisig = new Multisig(mockAccount, config, guardian, mockSigner, mockWebClient);
2901
+ const multisig = createTestMultisig(config);
2715
2902
 
2716
2903
  const readyDelta = {
2717
2904
  account_id: '0x' + 'a'.repeat(30),
@@ -2784,7 +2971,7 @@ describe('Multisig', () => {
2784
2971
  publicKey: '0x' + '2'.repeat(66),
2785
2972
  };
2786
2973
 
2787
- const multisig = new Multisig(mockAccount, config, guardian, ecdsaSigner, mockWebClient);
2974
+ const multisig = createTestMultisig(config, ecdsaSigner);
2788
2975
  const proposalId = '0x' + 'c'.repeat(64);
2789
2976
  const cosignerPubkey = '0x' + '3'.repeat(66);
2790
2977
  const ackPubkey = '0x' + '4'.repeat(66);
@@ -2900,7 +3087,7 @@ describe('Multisig', () => {
2900
3087
  publicKey: '0x' + '2'.repeat(66),
2901
3088
  };
2902
3089
 
2903
- const multisig = new Multisig(mockAccount, config, guardian, ecdsaSigner, mockWebClient);
3090
+ const multisig = createTestMultisig(config, ecdsaSigner);
2904
3091
  const proposalId = '0x' + 'c'.repeat(64);
2905
3092
  const cosignerPubkey = '0x' + '3'.repeat(66);
2906
3093
  const ackPubkey = '0x' + '4'.repeat(66);
@@ -3003,7 +3190,7 @@ describe('Multisig', () => {
3003
3190
  guardianCommitment: '0x' + 'c'.repeat(64),
3004
3191
  };
3005
3192
 
3006
- const multisig = new Multisig(mockAccount, config, guardian, mockSigner, mockWebClient);
3193
+ const multisig = createTestMultisig(config);
3007
3194
  const proposalId = '0x' + 'c'.repeat(64);
3008
3195
  const newGuardianPubkey = '0x' + '1'.repeat(64);
3009
3196
 
@@ -3086,7 +3273,7 @@ describe('Multisig', () => {
3086
3273
  guardianCommitment: '0x' + 'c'.repeat(64),
3087
3274
  };
3088
3275
 
3089
- const multisig = new Multisig(mockAccount, config, guardian, mockSigner, mockWebClient);
3276
+ const multisig = createTestMultisig(config);
3090
3277
  const proposalId = '0x' + 'c'.repeat(64);
3091
3278
  const newGuardianPubkey = '0x' + '1'.repeat(64);
3092
3279
 
@@ -3137,7 +3324,7 @@ describe('Multisig', () => {
3137
3324
  guardianCommitment: '0x' + 'c'.repeat(64),
3138
3325
  };
3139
3326
 
3140
- const multisig = new Multisig(mockAccount, config, guardian, mockSigner, mockWebClient);
3327
+ const multisig = createTestMultisig(config);
3141
3328
  const proposalId = '0x' + 'c'.repeat(64);
3142
3329
 
3143
3330
  (multisig as any).proposals.set(proposalId, {
@@ -3179,7 +3366,7 @@ describe('Multisig', () => {
3179
3366
  guardianCommitment: '0x' + 'c'.repeat(64),
3180
3367
  };
3181
3368
 
3182
- const multisig = new Multisig(mockAccount, config, guardian, mockSigner, mockWebClient);
3369
+ const multisig = createTestMultisig(config);
3183
3370
  const proposalId = '0x' + 'c'.repeat(64);
3184
3371
 
3185
3372
  (multisig as any).proposals.set(proposalId, {
@@ -3231,7 +3418,7 @@ describe('Multisig', () => {
3231
3418
  guardianCommitment: '0x' + 'c'.repeat(64),
3232
3419
  };
3233
3420
 
3234
- const multisig = new Multisig(mockAccount, config, guardian, mockSigner, mockWebClient);
3421
+ const multisig = createTestMultisig(config);
3235
3422
  const proposalId = '0x' + 'c'.repeat(64);
3236
3423
 
3237
3424
  (multisig as any).proposals.set(proposalId, {
@@ -3308,7 +3495,7 @@ describe('Multisig', () => {
3308
3495
  signerCommitments: ['0x' + 'a'.repeat(64)],
3309
3496
  guardianCommitment: '0x' + 'c'.repeat(64),
3310
3497
  };
3311
- const multisig = new Multisig(mockAccount, config, guardian, mockSigner, mockWebClient);
3498
+ const multisig = createTestMultisig(config);
3312
3499
 
3313
3500
  const builtinDelta = {
3314
3501
  ...customDelta('change_threshold', [falconSig('0x' + 'a'.repeat(64))]),
@@ -3336,7 +3523,7 @@ describe('Multisig', () => {
3336
3523
  signerCommitments: ['0x' + 'a'.repeat(64), '0x' + 'b'.repeat(64)],
3337
3524
  guardianCommitment: '0x' + 'c'.repeat(64),
3338
3525
  };
3339
- const multisig = new Multisig(mockAccount, config, guardian, mockSigner, mockWebClient);
3526
+ const multisig = createTestMultisig(config);
3340
3527
 
3341
3528
  mockFetch.mockResolvedValueOnce({
3342
3529
  ok: true,
@@ -3354,7 +3541,7 @@ describe('Multisig', () => {
3354
3541
  signerCommitments: ['0x' + 'a'.repeat(64)],
3355
3542
  guardianCommitment: '0x' + 'c'.repeat(64),
3356
3543
  };
3357
- const multisig = new Multisig(mockAccount, config, guardian, mockSigner, mockWebClient);
3544
+ const multisig = createTestMultisig(config);
3358
3545
 
3359
3546
  // Signed commitment comes from TransactionSummary.deserialize -> 'c' * 64.
3360
3547
  // Make the binding request derive a different commitment so the check fails.
@@ -3381,7 +3568,7 @@ describe('Multisig', () => {
3381
3568
  signerCommitments: ['0x' + 'a'.repeat(64)],
3382
3569
  guardianCommitment: '0x' + 'c'.repeat(64),
3383
3570
  };
3384
- const multisig = new Multisig(mockAccount, config, guardian, mockSigner, mockWebClient);
3571
+ const multisig = createTestMultisig(config);
3385
3572
 
3386
3573
  const ready = customDelta('b2agg', [falconSig('0x' + 'a'.repeat(64))]);
3387
3574
 
@@ -3410,7 +3597,7 @@ describe('Multisig', () => {
3410
3597
  guardianCommitment: '0x' + 'c'.repeat(64),
3411
3598
  };
3412
3599
 
3413
- const multisig = new Multisig(mockAccount, config, guardian, mockSigner, mockWebClient);
3600
+ const multisig = createTestMultisig(config);
3414
3601
 
3415
3602
  // Create a proposal with metadata
3416
3603
  const mockDelta = {
@@ -3472,7 +3659,7 @@ describe('Multisig', () => {
3472
3659
  guardianCommitment: '0x' + 'c'.repeat(64),
3473
3660
  };
3474
3661
 
3475
- const multisig = new Multisig(mockAccount, config, guardian, mockSigner, mockWebClient);
3662
+ const multisig = createTestMultisig(config);
3476
3663
 
3477
3664
  // Sync proposals - no local proposals exist
3478
3665
  const mockProposals = [
@@ -3519,7 +3706,7 @@ describe('Multisig', () => {
3519
3706
  guardianCommitment: '0x' + 'c'.repeat(64),
3520
3707
  };
3521
3708
 
3522
- const multisig = new Multisig(mockAccount, config, guardian, mockSigner, mockWebClient);
3709
+ const multisig = createTestMultisig(config);
3523
3710
 
3524
3711
  const mockDelta = {
3525
3712
  account_id: '0x' + 'a'.repeat(30),
@@ -3567,7 +3754,7 @@ describe('Multisig', () => {
3567
3754
  guardianCommitment: '0x' + 'c'.repeat(64),
3568
3755
  };
3569
3756
 
3570
- const multisig = new Multisig(mockAccount, config, guardian, mockSigner, mockWebClient);
3757
+ const multisig = createTestMultisig(config);
3571
3758
 
3572
3759
  const mockDelta = {
3573
3760
  account_id: '0x' + 'a'.repeat(30),
@@ -3617,7 +3804,7 @@ describe('Multisig', () => {
3617
3804
  guardianCommitment: '0x' + 'c'.repeat(64),
3618
3805
  };
3619
3806
 
3620
- const multisig = new Multisig(mockAccount, config, guardian, mockSigner, mockWebClient);
3807
+ const multisig = createTestMultisig(config);
3621
3808
 
3622
3809
  const mockDelta = {
3623
3810
  account_id: '0x' + 'a'.repeat(30),
@@ -3668,7 +3855,7 @@ describe('Multisig', () => {
3668
3855
  guardianCommitment: '0x' + 'c'.repeat(64),
3669
3856
  };
3670
3857
 
3671
- const multisig = new Multisig(mockAccount, config, guardian, mockSigner, mockWebClient);
3858
+ const multisig = createTestMultisig(config);
3672
3859
 
3673
3860
  // First sync with 1 signature (pending)
3674
3861
  const mockProposalsPending = [
@@ -3760,7 +3947,7 @@ describe('Multisig', () => {
3760
3947
  guardianCommitment: '0x' + 'd'.repeat(64),
3761
3948
  };
3762
3949
 
3763
- const multisig = new Multisig(mockAccount, config, guardian, mockSigner, mockWebClient);
3950
+ const multisig = createTestMultisig(config);
3764
3951
  expect(multisig.threshold).toBe(3);
3765
3952
  });
3766
3953
 
@@ -3772,7 +3959,7 @@ describe('Multisig', () => {
3772
3959
  guardianCommitment: '0x' + 'd'.repeat(64),
3773
3960
  };
3774
3961
 
3775
- const multisig = new Multisig(mockAccount, config, guardian, mockSigner, mockWebClient);
3962
+ const multisig = createTestMultisig(config);
3776
3963
  expect(multisig.signerCommitments).toEqual(signerCommitments);
3777
3964
  });
3778
3965
 
@@ -3784,7 +3971,7 @@ describe('Multisig', () => {
3784
3971
  guardianCommitment,
3785
3972
  };
3786
3973
 
3787
- const multisig = new Multisig(mockAccount, config, guardian, mockSigner, mockWebClient);
3974
+ const multisig = createTestMultisig(config);
3788
3975
  expect(multisig.guardianCommitment).toBe(guardianCommitment);
3789
3976
  });
3790
3977
 
@@ -3795,7 +3982,7 @@ describe('Multisig', () => {
3795
3982
  guardianCommitment: '0x' + 'd'.repeat(64),
3796
3983
  };
3797
3984
 
3798
- const multisig = new Multisig(mockAccount, config, guardian, mockSigner, mockWebClient);
3985
+ const multisig = createTestMultisig(config);
3799
3986
  expect(multisig.account).toBe(mockAccount);
3800
3987
  });
3801
3988
  });
@@ -3808,7 +3995,7 @@ describe('Multisig', () => {
3808
3995
  guardianCommitment: '0x' + 'c'.repeat(64),
3809
3996
  };
3810
3997
 
3811
- const multisig = new Multisig(mockAccount, config, guardian, mockSigner, mockWebClient);
3998
+ const multisig = createTestMultisig(config);
3812
3999
 
3813
4000
  // Simulates a GUARDIAN response with canonical snake_case metadata
3814
4001
  const rustProposals = [
@@ -3858,7 +4045,7 @@ describe('Multisig', () => {
3858
4045
  guardianCommitment: '0x' + 'c'.repeat(64),
3859
4046
  };
3860
4047
 
3861
- const multisig = new Multisig(mockAccount, config, guardian, mockSigner, mockWebClient);
4048
+ const multisig = createTestMultisig(config);
3862
4049
 
3863
4050
  // P2ID proposal with canonical snake_case fields
3864
4051
  const p2idProposals = [
@@ -3915,7 +4102,7 @@ describe('Multisig', () => {
3915
4102
  guardianCommitment: '0x' + 'c'.repeat(64),
3916
4103
  };
3917
4104
 
3918
- const multisig = new Multisig(mockAccount, config, guardian, mockSigner, mockWebClient);
4105
+ const multisig = createTestMultisig(config);
3919
4106
 
3920
4107
  const switchGuardianProposals = [
3921
4108
  {