@openzeppelin/miden-multisig-client 0.13.4 → 0.14.3

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 (145) hide show
  1. package/README.md +10 -13
  2. package/dist/account/builder.d.ts +3 -3
  3. package/dist/account/builder.d.ts.map +1 -1
  4. package/dist/account/builder.js +10 -10
  5. package/dist/account/builder.js.map +1 -1
  6. package/dist/account/builder.test.js +16 -14
  7. package/dist/account/builder.test.js.map +1 -1
  8. package/dist/account/masm/account-components/auth.d.ts +4 -4
  9. package/dist/account/masm/account-components/auth.d.ts.map +1 -1
  10. package/dist/account/masm/account-components/auth.js +8 -12
  11. package/dist/account/masm/account-components/auth.js.map +1 -1
  12. package/dist/account/masm/auth.d.ts +4 -4
  13. package/dist/account/masm/auth.d.ts.map +1 -1
  14. package/dist/account/masm/auth.js +226 -124
  15. package/dist/account/masm/auth.js.map +1 -1
  16. package/dist/account/storage.d.ts.map +1 -1
  17. package/dist/account/storage.js +24 -6
  18. package/dist/account/storage.js.map +1 -1
  19. package/dist/account/storage.test.d.ts +2 -0
  20. package/dist/account/storage.test.d.ts.map +1 -0
  21. package/dist/account/storage.test.js +73 -0
  22. package/dist/account/storage.test.js.map +1 -0
  23. package/dist/client.d.ts +9 -9
  24. package/dist/client.d.ts.map +1 -1
  25. package/dist/client.js +24 -14
  26. package/dist/client.js.map +1 -1
  27. package/dist/client.test.js +45 -8
  28. package/dist/client.test.js.map +1 -1
  29. package/dist/index.d.ts +6 -12
  30. package/dist/index.d.ts.map +1 -1
  31. package/dist/index.js +6 -12
  32. package/dist/index.js.map +1 -1
  33. package/dist/multisig/proposal/execution.d.ts +3 -2
  34. package/dist/multisig/proposal/execution.d.ts.map +1 -1
  35. package/dist/multisig/proposal/execution.js +8 -12
  36. package/dist/multisig/proposal/execution.js.map +1 -1
  37. package/dist/multisig.d.ts +7 -4
  38. package/dist/multisig.d.ts.map +1 -1
  39. package/dist/multisig.js +53 -35
  40. package/dist/multisig.js.map +1 -1
  41. package/dist/multisig.test.js +17 -29
  42. package/dist/multisig.test.js.map +1 -1
  43. package/dist/procedures.d.ts +8 -8
  44. package/dist/procedures.js +8 -8
  45. package/dist/raw-client.d.ts +13 -0
  46. package/dist/raw-client.d.ts.map +1 -0
  47. package/dist/raw-client.js +42 -0
  48. package/dist/raw-client.js.map +1 -0
  49. package/dist/raw-client.test.d.ts +2 -0
  50. package/dist/raw-client.test.d.ts.map +1 -0
  51. package/dist/raw-client.test.js +89 -0
  52. package/dist/raw-client.test.js.map +1 -0
  53. package/dist/signers/ecdsa.d.ts +3 -1
  54. package/dist/signers/ecdsa.d.ts.map +1 -1
  55. package/dist/signers/ecdsa.js +15 -1
  56. package/dist/signers/ecdsa.js.map +1 -1
  57. package/dist/signers/ecdsa.test.js +27 -4
  58. package/dist/signers/ecdsa.test.js.map +1 -1
  59. package/dist/signers/falcon.d.ts +3 -1
  60. package/dist/signers/falcon.d.ts.map +1 -1
  61. package/dist/signers/falcon.js +15 -1
  62. package/dist/signers/falcon.js.map +1 -1
  63. package/dist/signers/falcon.test.js +39 -3
  64. package/dist/signers/falcon.test.js.map +1 -1
  65. package/dist/transaction/consumeNotes.d.ts +2 -2
  66. package/dist/transaction/consumeNotes.d.ts.map +1 -1
  67. package/dist/transaction/consumeNotes.js +5 -4
  68. package/dist/transaction/consumeNotes.js.map +1 -1
  69. package/dist/transaction/options.d.ts +1 -0
  70. package/dist/transaction/options.d.ts.map +1 -1
  71. package/dist/transaction/p2id.d.ts.map +1 -1
  72. package/dist/transaction/p2id.js +7 -10
  73. package/dist/transaction/p2id.js.map +1 -1
  74. package/dist/transaction/p2id.test.js +19 -9
  75. package/dist/transaction/p2id.test.js.map +1 -1
  76. package/dist/transaction/summary.d.ts +2 -2
  77. package/dist/transaction/summary.d.ts.map +1 -1
  78. package/dist/transaction/summary.js +4 -2
  79. package/dist/transaction/summary.js.map +1 -1
  80. package/dist/transaction/updateGuardian.d.ts +2 -2
  81. package/dist/transaction/updateGuardian.d.ts.map +1 -1
  82. package/dist/transaction/updateGuardian.js +8 -11
  83. package/dist/transaction/updateGuardian.js.map +1 -1
  84. package/dist/transaction/updateProcedureThreshold.d.ts +2 -2
  85. package/dist/transaction/updateProcedureThreshold.d.ts.map +1 -1
  86. package/dist/transaction/updateProcedureThreshold.js +8 -10
  87. package/dist/transaction/updateProcedureThreshold.js.map +1 -1
  88. package/dist/transaction/updateSigners.d.ts +2 -2
  89. package/dist/transaction/updateSigners.d.ts.map +1 -1
  90. package/dist/transaction/updateSigners.js +8 -10
  91. package/dist/transaction/updateSigners.js.map +1 -1
  92. package/dist/transaction.test.js +6 -6
  93. package/dist/transaction.test.js.map +1 -1
  94. package/dist/utils/key.d.ts.map +1 -1
  95. package/dist/utils/key.js +3 -3
  96. package/dist/utils/key.js.map +1 -1
  97. package/dist/utils/key.test.js +4 -2
  98. package/dist/utils/key.test.js.map +1 -1
  99. package/dist/utils/signature.d.ts +2 -0
  100. package/dist/utils/signature.d.ts.map +1 -1
  101. package/dist/utils/signature.js +10 -5
  102. package/dist/utils/signature.js.map +1 -1
  103. package/dist/utils/signature.test.js +9 -9
  104. package/dist/utils/signature.test.js.map +1 -1
  105. package/masm/account_components/auth/multisig.masm +2 -3
  106. package/masm/account_components/auth/multisig_ecdsa.masm +2 -3
  107. package/masm/account_components/auth/multisig_guardian.masm +2 -3
  108. package/masm/account_components/auth/multisig_guardian_ecdsa.masm +2 -3
  109. package/masm/auth/guardian.masm +30 -8
  110. package/masm/auth/guardian_ecdsa.masm +24 -7
  111. package/masm/auth/multisig.masm +86 -55
  112. package/masm/auth/multisig_ecdsa.masm +86 -54
  113. package/package.json +3 -3
  114. package/src/account/builder.test.ts +16 -14
  115. package/src/account/builder.ts +13 -12
  116. package/src/account/masm/account-components/auth.ts +8 -12
  117. package/src/account/masm/auth.ts +226 -124
  118. package/src/account/storage.test.ts +90 -0
  119. package/src/account/storage.ts +27 -6
  120. package/src/client.test.ts +52 -8
  121. package/src/client.ts +39 -16
  122. package/src/index.ts +6 -12
  123. package/src/multisig/proposal/execution.ts +18 -14
  124. package/src/multisig.test.ts +17 -29
  125. package/src/multisig.ts +61 -36
  126. package/src/procedures.ts +8 -8
  127. package/src/raw-client.test.ts +133 -0
  128. package/src/raw-client.ts +75 -0
  129. package/src/signers/ecdsa.test.ts +35 -4
  130. package/src/signers/ecdsa.ts +18 -2
  131. package/src/signers/falcon.test.ts +50 -3
  132. package/src/signers/falcon.ts +18 -2
  133. package/src/transaction/consumeNotes.ts +16 -5
  134. package/src/transaction/options.ts +1 -0
  135. package/src/transaction/p2id.test.ts +19 -9
  136. package/src/transaction/p2id.ts +8 -13
  137. package/src/transaction/summary.ts +11 -4
  138. package/src/transaction/updateGuardian.ts +22 -14
  139. package/src/transaction/updateProcedureThreshold.ts +20 -15
  140. package/src/transaction/updateSigners.ts +22 -14
  141. package/src/transaction.test.ts +6 -6
  142. package/src/utils/key.test.ts +4 -2
  143. package/src/utils/key.ts +7 -3
  144. package/src/utils/signature.test.ts +10 -10
  145. package/src/utils/signature.ts +12 -5
package/src/multisig.ts CHANGED
@@ -16,7 +16,12 @@ import type {
16
16
  ProposalType,
17
17
  } from './types.js';
18
18
  import type { ProcedureName } from './procedures.js';
19
- import type { WebClient, TransactionRequest } from '@miden-sdk/miden-sdk';
19
+ import type {
20
+ MidenClient,
21
+ TransactionProver,
22
+ TransactionRequest,
23
+ WasmWebClient,
24
+ } from '@miden-sdk/miden-sdk';
20
25
  import {
21
26
  Account,
22
27
  AccountId,
@@ -53,6 +58,7 @@ import { AccountInspector } from './inspector.js';
53
58
  import { ProposalFactory } from './proposal/factory.js';
54
59
  import { ProposalMetadataCodec } from './proposal/metadata.js';
55
60
  import { ProposalSignatures } from './proposal/signatures.js';
61
+ import { getRawMidenClient, getTransactionProver } from './raw-client.js';
56
62
 
57
63
  /**
58
64
  * Result of fetching account state from GUARDIAN.
@@ -87,7 +93,9 @@ export class Multisig {
87
93
 
88
94
  private guardian: GuardianHttpClient;
89
95
  private readonly signer: Signer;
90
- private readonly webClient: WebClient;
96
+ private readonly midenClient: MidenClient;
97
+ private readonly rawClientPromise: Promise<WasmWebClient>;
98
+ private readonly transactionProver: TransactionProver | null;
91
99
  private readonly _accountId: string;
92
100
  private readonly midenRpcEndpoint?: string;
93
101
  private proposals: Map<string, Proposal> = new Map();
@@ -97,7 +105,7 @@ export class Multisig {
97
105
  config: MultisigConfig,
98
106
  guardian: GuardianHttpClient,
99
107
  signer: Signer,
100
- webClient: WebClient,
108
+ midenClient: MidenClient,
101
109
  accountId?: string,
102
110
  midenRpcEndpoint?: string
103
111
  ) {
@@ -111,9 +119,11 @@ export class Multisig {
111
119
  );
112
120
  this.guardian = guardian;
113
121
  this.signer = signer;
114
- this.webClient = webClient;
122
+ this.midenClient = midenClient;
115
123
  this._accountId = accountId ?? (account ? accountIdToHex(account) : '');
116
124
  this.midenRpcEndpoint = midenRpcEndpoint;
125
+ this.rawClientPromise = getRawMidenClient(midenClient, midenRpcEndpoint);
126
+ this.transactionProver = getTransactionProver(midenClient);
117
127
  }
118
128
 
119
129
  private getMidenRpcEndpoint(): string {
@@ -123,6 +133,10 @@ export class Multisig {
123
133
  return this.midenRpcEndpoint;
124
134
  }
125
135
 
136
+ private async getRawClient(): Promise<WasmWebClient> {
137
+ return this.rawClientPromise;
138
+ }
139
+
126
140
  private proposalFactory(): ProposalFactory {
127
141
  return new ProposalFactory({
128
142
  accountId: this._accountId,
@@ -228,7 +242,7 @@ export class Multisig {
228
242
  }
229
243
 
230
244
  /**
231
- * Sync account state from GUARDIAN into the local WebClient store.
245
+ * Sync account state from GUARDIAN into the local Miden client store.
232
246
  *
233
247
  * If the GUARDIAN commitment differs from the local commitment (or the account
234
248
  * is missing locally), the local store is overwritten with the GUARDIAN state.
@@ -236,19 +250,20 @@ export class Multisig {
236
250
  async syncState(): Promise<AccountState> {
237
251
  const state = await this.fetchState();
238
252
  const accountId = AccountId.fromHex(this._accountId);
239
- const localAccount = await this.webClient.getAccount(accountId);
253
+ const webClient = await this.getRawClient();
254
+ const localAccount = await webClient.getAccount(accountId);
240
255
  let accountForConfigRefresh: Account | null = localAccount ?? null;
241
256
 
242
257
  const guardianCommitment = normalizeHexWord(state.commitment);
243
258
  const localCommitment = localAccount
244
- ? normalizeHexWord(localAccount.commitment().toHex())
259
+ ? normalizeHexWord(localAccount.to_commitment().toHex())
245
260
  : null;
246
261
 
247
262
  if (!localAccount || localCommitment !== guardianCommitment) {
248
263
  const accountBytes = base64ToUint8Array(state.stateDataBase64);
249
264
  const incomingAccount = Account.deserialize(accountBytes);
250
265
  await this.ensureSafeToOverwriteLocalState(incomingAccount, localAccount);
251
- await this.webClient.newAccount(incomingAccount, true);
266
+ await webClient.newAccount(incomingAccount, true);
252
267
  accountForConfigRefresh = incomingAccount;
253
268
  }
254
269
 
@@ -259,7 +274,8 @@ export class Multisig {
259
274
 
260
275
  async verifyStateCommitment(): Promise<AccountStateVerificationResult> {
261
276
  const accountId = AccountId.fromHex(this._accountId);
262
- const localAccount = await this.webClient.getAccount(accountId);
277
+ const webClient = await this.getRawClient();
278
+ const localAccount = await webClient.getAccount(accountId);
263
279
 
264
280
  if (!localAccount) {
265
281
  throw new Error(
@@ -267,7 +283,7 @@ export class Multisig {
267
283
  );
268
284
  }
269
285
 
270
- const localCommitment = normalizeHexWord(localAccount.commitment().toHex());
286
+ const localCommitment = normalizeHexWord(localAccount.to_commitment().toHex());
271
287
  const onChainCommitment = await this.getOnChainCommitment(accountId);
272
288
 
273
289
  if (!onChainCommitment) {
@@ -308,7 +324,7 @@ export class Multisig {
308
324
  return;
309
325
  }
310
326
 
311
- const incomingCommitment = normalizeHexWord(incomingAccount.commitment().toHex());
327
+ const incomingCommitment = normalizeHexWord(incomingAccount.to_commitment().toHex());
312
328
  if (incomingCommitment !== onChainCommitment) {
313
329
  throw new Error(
314
330
  `Refusing to overwrite local state: incoming commitment does not match on-chain commitment for account ${this._accountId}`
@@ -472,17 +488,18 @@ export class Multisig {
472
488
  nonce?: number,
473
489
  newThreshold?: number,
474
490
  ): Promise<Proposal> {
491
+ const webClient = await this.getRawClient();
475
492
  const targetThreshold = newThreshold ?? this.threshold;
476
493
  const targetSignerCommitments = [...this.signerCommitments, newCommitment];
477
494
 
478
495
  const { request, salt } = await buildUpdateSignersTransactionRequest(
479
- this.webClient,
496
+ webClient,
480
497
  targetThreshold,
481
498
  targetSignerCommitments,
482
499
  { signatureScheme: this.signer.scheme },
483
500
  );
484
501
 
485
- const summary = await executeForSummary(this.webClient, this._accountId, request);
502
+ const summary = await executeForSummary(webClient, this._accountId, request);
486
503
  const summaryBase64 = uint8ArrayToBase64(summary.serialize());
487
504
  const proposalNonce = nonce ?? Date.now();
488
505
 
@@ -510,6 +527,7 @@ export class Multisig {
510
527
  nonce?: number,
511
528
  newThreshold?: number,
512
529
  ): Promise<Proposal> {
530
+ const webClient = await this.getRawClient();
513
531
  const normalizedRemove = signerToRemove.toLowerCase();
514
532
  const targetSignerCommitments = this.signerCommitments.filter(
515
533
  (c) => c.toLowerCase() !== normalizedRemove
@@ -531,13 +549,13 @@ export class Multisig {
531
549
  }
532
550
 
533
551
  const { request, salt } = await buildUpdateSignersTransactionRequest(
534
- this.webClient,
552
+ webClient,
535
553
  targetThreshold,
536
554
  targetSignerCommitments,
537
555
  { signatureScheme: this.signer.scheme },
538
556
  );
539
557
 
540
- const summary = await executeForSummary(this.webClient, this._accountId, request);
558
+ const summary = await executeForSummary(webClient, this._accountId, request);
541
559
  const summaryBase64 = uint8ArrayToBase64(summary.serialize());
542
560
  const proposalNonce = nonce ?? Date.now();
543
561
 
@@ -563,6 +581,7 @@ export class Multisig {
563
581
  newThreshold: number,
564
582
  nonce?: number,
565
583
  ): Promise<Proposal> {
584
+ const webClient = await this.getRawClient();
566
585
  if (newThreshold < 1 || newThreshold > this.signerCommitments.length) {
567
586
  throw new Error(
568
587
  `Invalid threshold ${newThreshold}. Must be between 1 and ${this.signerCommitments.length}`
@@ -574,13 +593,13 @@ export class Multisig {
574
593
  }
575
594
 
576
595
  const { request, salt } = await buildUpdateSignersTransactionRequest(
577
- this.webClient,
596
+ webClient,
578
597
  newThreshold,
579
598
  this.signerCommitments,
580
599
  { signatureScheme: this.signer.scheme },
581
600
  );
582
601
 
583
- const summary = await executeForSummary(this.webClient, this._accountId, request);
602
+ const summary = await executeForSummary(webClient, this._accountId, request);
584
603
  const summaryBase64 = uint8ArrayToBase64(summary.serialize());
585
604
  const proposalNonce = nonce ?? Date.now();
586
605
 
@@ -601,6 +620,7 @@ export class Multisig {
601
620
  targetThreshold: number,
602
621
  nonce?: number,
603
622
  ): Promise<Proposal> {
623
+ const webClient = await this.getRawClient();
604
624
  if (targetThreshold < 0 || targetThreshold > this.signerCommitments.length) {
605
625
  throw new Error(
606
626
  `Invalid threshold ${targetThreshold}. Must be between 0 and ${this.signerCommitments.length}`
@@ -619,13 +639,13 @@ export class Multisig {
619
639
  }
620
640
 
621
641
  const { request, salt } = await buildUpdateProcedureThresholdTransactionRequest(
622
- this.webClient,
642
+ webClient,
623
643
  targetProcedure,
624
644
  targetThreshold,
625
645
  { signatureScheme: this.signer.scheme },
626
646
  );
627
647
 
628
- const summary = await executeForSummary(this.webClient, this._accountId, request);
648
+ const summary = await executeForSummary(webClient, this._accountId, request);
629
649
  const summaryBase64 = uint8ArrayToBase64(summary.serialize());
630
650
  const proposalNonce = nonce ?? Date.now();
631
651
  const action = targetThreshold === 0
@@ -656,15 +676,16 @@ export class Multisig {
656
676
  newGuardianPubkey: string,
657
677
  nonce?: number,
658
678
  ): Promise<Proposal> {
679
+ const webClient = await this.getRawClient();
659
680
  await this.verifyGuardianEndpointCommitment(newGuardianEndpoint, newGuardianPubkey);
660
681
 
661
682
  const { request, salt } = await buildUpdateGuardianTransactionRequest(
662
- this.webClient,
683
+ webClient,
663
684
  newGuardianPubkey,
664
685
  { signatureScheme: this.signer.scheme },
665
686
  );
666
687
 
667
- const summary = await executeForSummary(this.webClient, this._accountId, request);
688
+ const summary = await executeForSummary(webClient, this._accountId, request);
668
689
  const summaryBase64 = uint8ArrayToBase64(summary.serialize());
669
690
  const proposalNonce = nonce ?? Date.now();
670
691
 
@@ -702,13 +723,14 @@ export class Multisig {
702
723
  noteIds: string[],
703
724
  nonce?: number,
704
725
  ): Promise<Proposal> {
726
+ const webClient = await this.getRawClient();
705
727
  if (noteIds.length === 0) {
706
728
  throw new Error('At least one note ID is required');
707
729
  }
708
730
 
709
- const { request, salt } = await buildConsumeNotesTransactionRequest(this.webClient, noteIds);
731
+ const { request, salt } = await buildConsumeNotesTransactionRequest(webClient, noteIds);
710
732
 
711
- const summary = await executeForSummary(this.webClient, this._accountId, request);
733
+ const summary = await executeForSummary(webClient, this._accountId, request);
712
734
  const summaryBase64 = uint8ArrayToBase64(summary.serialize());
713
735
  const proposalNonce = nonce ?? Date.now();
714
736
 
@@ -737,6 +759,7 @@ export class Multisig {
737
759
  amount: bigint,
738
760
  nonce?: number,
739
761
  ): Promise<Proposal> {
762
+ const webClient = await this.getRawClient();
740
763
  if (amount <= 0n) {
741
764
  throw new Error('Amount must be greater than 0');
742
765
  }
@@ -748,7 +771,7 @@ export class Multisig {
748
771
  amount,
749
772
  );
750
773
 
751
- const summary = await executeForSummary(this.webClient, this._accountId, request);
774
+ const summary = await executeForSummary(webClient, this._accountId, request);
752
775
  const summaryBase64 = uint8ArrayToBase64(summary.serialize());
753
776
  const proposalNonce = nonce ?? Date.now();
754
777
 
@@ -773,9 +796,10 @@ export class Multisig {
773
796
  */
774
797
  async getConsumableNotes(): Promise<ConsumableNote[]> {
775
798
  const accountId = AccountId.fromHex(this._accountId);
799
+ const webClient = await this.getRawClient();
776
800
 
777
801
  // Get consumable notes for this account
778
- const consumableRecords = await this.webClient.getConsumableNotes(accountId);
802
+ const consumableRecords = await webClient.getConsumableNotes(accountId);
779
803
 
780
804
  // Convert to our simplified ConsumableNote type
781
805
  const notes: ConsumableNote[] = [];
@@ -880,10 +904,7 @@ export class Multisig {
880
904
  const { metadata, finalRequest, proposal } = await this.prepareProposalExecution(proposalId);
881
905
 
882
906
  const accountId = AccountId.fromHex(this._accountId);
883
- const result = await this.webClient.executeTransaction(accountId, finalRequest);
884
- const proven = await this.webClient.proveTransaction(result, null);
885
- const submissionHeight = await this.webClient.submitProvenTransaction(proven, result);
886
- await this.webClient.applyTransaction(result, submissionHeight);
907
+ await this.midenClient.transactions.submit(accountId, finalRequest);
887
908
 
888
909
  if (metadata.proposalType === 'switch_guardian') {
889
910
  if (!metadata.newGuardianEndpoint || !metadata.newGuardianPubkey) {
@@ -891,9 +912,10 @@ export class Multisig {
891
912
  }
892
913
 
893
914
  try {
894
- await this.webClient.syncState();
915
+ const webClient = await this.getRawClient();
916
+ await webClient.syncState();
895
917
 
896
- const updatedAccount = await this.webClient.getAccount(accountId);
918
+ const updatedAccount = await webClient.getAccount(accountId);
897
919
  if (!updatedAccount) {
898
920
  throw new Error(
899
921
  `Updated account ${this._accountId} is missing from local client`
@@ -1249,7 +1271,8 @@ export class Multisig {
1249
1271
  : summary.salt();
1250
1272
 
1251
1273
  const request = await this.buildTransactionRequestFromMetadata(proposal.metadata, salt);
1252
- const reconstructed = await executeForSummary(this.webClient, this._accountId, request);
1274
+ const webClient = await this.getRawClient();
1275
+ const reconstructed = await executeForSummary(webClient, this._accountId, request);
1253
1276
  const reconstructedCommitment = normalizeHexWord(reconstructed.toCommitment().toHex());
1254
1277
 
1255
1278
  if (reconstructedCommitment !== txSummaryCommitment) {
@@ -1264,12 +1287,14 @@ export class Multisig {
1264
1287
  salt: Word,
1265
1288
  signatureAdviceMap?: AdviceMap,
1266
1289
  ): Promise<TransactionRequest> {
1290
+ const webClient = await this.getRawClient();
1291
+
1267
1292
  switch (metadata.proposalType) {
1268
1293
  case 'add_signer':
1269
1294
  case 'remove_signer':
1270
1295
  case 'change_threshold': {
1271
1296
  const { request } = await buildUpdateSignersTransactionRequest(
1272
- this.webClient,
1297
+ webClient,
1273
1298
  metadata.targetThreshold,
1274
1299
  metadata.targetSignerCommitments,
1275
1300
  { salt, signatureAdviceMap, signatureScheme: this.signer.scheme }
@@ -1278,7 +1303,7 @@ export class Multisig {
1278
1303
  }
1279
1304
  case 'switch_guardian': {
1280
1305
  const { request } = await buildUpdateGuardianTransactionRequest(
1281
- this.webClient,
1306
+ webClient,
1282
1307
  metadata.newGuardianPubkey,
1283
1308
  { salt, signatureAdviceMap, signatureScheme: this.signer.scheme }
1284
1309
  );
@@ -1286,7 +1311,7 @@ export class Multisig {
1286
1311
  }
1287
1312
  case 'update_procedure_threshold': {
1288
1313
  const { request } = await buildUpdateProcedureThresholdTransactionRequest(
1289
- this.webClient,
1314
+ webClient,
1290
1315
  metadata.targetProcedure,
1291
1316
  metadata.targetThreshold,
1292
1317
  { salt, signatureAdviceMap, signatureScheme: this.signer.scheme }
@@ -1295,7 +1320,7 @@ export class Multisig {
1295
1320
  }
1296
1321
  case 'consume_notes': {
1297
1322
  const { request } = await buildConsumeNotesTransactionRequest(
1298
- this.webClient,
1323
+ webClient,
1299
1324
  metadata.noteIds,
1300
1325
  { salt, signatureAdviceMap }
1301
1326
  );
package/src/procedures.ts CHANGED
@@ -11,13 +11,13 @@
11
11
  * human-readable encoding and should not be copied into this table.
12
12
  */
13
13
  export const PROCEDURE_ROOTS = {
14
- update_signers: '0xafdea9abee19b02620520b11a165267b266403fa408f7eebc91fc83c33b507ca',
15
- update_procedure_threshold: '0xd6616423eebe012655a9db925363cc6751b94109a938e9bc5f488b52f75b7d1c',
16
- auth_tx: '0x611abcd570631ad98842cb6f0ef891fe8f9ee508b3245c55a5531d5a8f7fdca9',
17
- update_guardian: '0xcda1f9120a3ab2948d5cdc6b4b2982571c04e3f6af787a6d6b2f88eeedd872d7',
18
- verify_guardian: '0xe4be6069ba56570afecab5ae2ff77165d5230b703ba2cff1cafe06b92aa19e56',
19
- send_asset: '0x0e406b067ed2bcd7de745ca6517f519fd1a9be245f913347ac673ca1db30c1d6',
20
- receive_asset: '0x6f4bdbdc4b13d7ed933d590d88ac9dfb98020c9e917697845b5e169395b76a01',
14
+ update_signers: '0x3d382ad461f9914c487c6fe908991d088eb54ecbd4aa8560ef79c66c3746bf19',
15
+ update_procedure_threshold: '0x1f43e9d56ceff5d547ffdcb89896fb38cae0be1b74d9235ed2b4aa525df85f8d',
16
+ auth_tx: '0x415530d7169f849d7219e810065f9119bba9af2c55070de0bf4f082a1c0aea5c',
17
+ update_guardian: '0xc8ea876f1837e5cd1d6031becdbd40ce262ecd55930d65400f6890a37149d80c',
18
+ verify_guardian: '0x9bc6e7b25c8dbaa29d6ad41e354a545dd0a4bac7f3a521bb5195ba101f0213cc',
19
+ send_asset: '0x6d30df4312a2c44ec842db1bee227cc045396ca91e2c47d756dcb607f2bf5f89',
20
+ receive_asset: '0x75f638c65584d058542bcf4674b066ae394183021bc9b44dc2fdd97d52f9bcfb',
21
21
  } as const;
22
22
 
23
23
  /**
@@ -34,7 +34,7 @@ export type ProcedureName = keyof typeof PROCEDURE_ROOTS;
34
34
  * @example
35
35
  * ```typescript
36
36
  * const root = getProcedureRoot('send_asset');
37
- * // '0x0e406b067ed2bcd7de745ca6517f519fd1a9be245f913347ac673ca1db30c1d6'
37
+ * // '0x6d30df4312a2c44ec842db1bee227cc045396ca91e2c47d756dcb607f2bf5f89'
38
38
  * ```
39
39
  */
40
40
  export function getProcedureRoot(name: ProcedureName): string {
@@ -0,0 +1,133 @@
1
+ import { beforeEach, describe, expect, it, vi } from 'vitest';
2
+
3
+ const { mockCreateClient } = vi.hoisted(() => ({
4
+ mockCreateClient: vi.fn(),
5
+ }));
6
+
7
+ vi.mock('@miden-sdk/miden-sdk', () => ({
8
+ WasmWebClient: {
9
+ createClient: mockCreateClient,
10
+ },
11
+ }));
12
+
13
+ import {
14
+ compileTxScript,
15
+ getRawMidenClient,
16
+ getTransactionProver,
17
+ resolveMidenRpcEndpoint,
18
+ } from './raw-client.js';
19
+
20
+ describe('raw-client', () => {
21
+ beforeEach(() => {
22
+ mockCreateClient.mockReset();
23
+ });
24
+
25
+ it('defaults RPC endpoint resolution to devnet', () => {
26
+ expect(resolveMidenRpcEndpoint()).toBe('https://rpc.devnet.miden.io');
27
+ });
28
+
29
+ it('returns the default prover from a public MidenClient', () => {
30
+ const prover = { kind: 'devnet-prover' };
31
+ const client = {
32
+ accounts: {},
33
+ sync: vi.fn(),
34
+ defaultProver: prover,
35
+ storeIdentifier: vi.fn(() => 'browser-db'),
36
+ };
37
+
38
+ expect(getTransactionProver(client as any)).toBe(prover);
39
+ });
40
+
41
+ it('returns null for raw web clients', () => {
42
+ const rawClient = {
43
+ executeTransaction: vi.fn(),
44
+ proveTransaction: vi.fn(),
45
+ };
46
+
47
+ expect(getTransactionProver(rawClient as any)).toBeNull();
48
+ });
49
+
50
+ it('creates and caches a raw client for a public MidenClient', async () => {
51
+ const rawClient = { kind: 'raw-client' };
52
+ const client = {
53
+ accounts: {},
54
+ sync: vi.fn(),
55
+ defaultProver: null,
56
+ storeIdentifier: vi.fn(() => 'browser-db'),
57
+ };
58
+
59
+ mockCreateClient.mockResolvedValue(rawClient);
60
+
61
+ await expect(
62
+ getRawMidenClient(client as any, 'https://rpc.devnet.miden.io'),
63
+ ).resolves.toBe(rawClient);
64
+ await expect(
65
+ getRawMidenClient(client as any, 'https://rpc.devnet.miden.io'),
66
+ ).resolves.toBe(rawClient);
67
+
68
+ expect(mockCreateClient).toHaveBeenCalledTimes(1);
69
+ expect(mockCreateClient).toHaveBeenCalledWith(
70
+ 'https://rpc.devnet.miden.io',
71
+ undefined,
72
+ undefined,
73
+ 'browser-db',
74
+ );
75
+ });
76
+
77
+ it('uses the public compile resource when available', async () => {
78
+ const script = { kind: 'compiled-script' };
79
+ const client = {
80
+ accounts: {},
81
+ sync: vi.fn(),
82
+ compile: {
83
+ txScript: vi.fn().mockResolvedValue(script),
84
+ },
85
+ storeIdentifier: vi.fn(() => 'browser-db'),
86
+ };
87
+
88
+ await expect(
89
+ compileTxScript(
90
+ client as any,
91
+ 'begin end',
92
+ [{ namespace: 'auth::multisig', code: 'export.foo' }],
93
+ ),
94
+ ).resolves.toBe(script);
95
+
96
+ expect(client.compile.txScript).toHaveBeenCalledWith({
97
+ code: 'begin end',
98
+ libraries: [{ namespace: 'auth::multisig', code: 'export.foo' }],
99
+ });
100
+ expect(mockCreateClient).not.toHaveBeenCalled();
101
+ });
102
+
103
+ it('falls back to raw client compilation for low-level callers', async () => {
104
+ const compiledScript = { kind: 'compiled-script' };
105
+ const builtLibrary = { kind: 'built-library' };
106
+ const builder = {
107
+ buildLibrary: vi.fn().mockReturnValue(builtLibrary),
108
+ linkDynamicLibrary: vi.fn(),
109
+ linkStaticLibrary: vi.fn(),
110
+ compileTxScript: vi.fn().mockReturnValue(compiledScript),
111
+ };
112
+ const rawClient = {
113
+ createCodeBuilder: vi.fn().mockReturnValue(builder),
114
+ };
115
+
116
+ await expect(
117
+ compileTxScript(
118
+ rawClient as any,
119
+ 'begin end',
120
+ [
121
+ { namespace: 'auth::multisig', code: 'export.foo' },
122
+ { namespace: 'auth::guardian', code: 'export.bar', linking: 'static' },
123
+ ],
124
+ ),
125
+ ).resolves.toBe(compiledScript);
126
+
127
+ expect(builder.buildLibrary).toHaveBeenNthCalledWith(1, 'auth::multisig', 'export.foo');
128
+ expect(builder.buildLibrary).toHaveBeenNthCalledWith(2, 'auth::guardian', 'export.bar');
129
+ expect(builder.linkDynamicLibrary).toHaveBeenCalledWith(builtLibrary);
130
+ expect(builder.linkStaticLibrary).toHaveBeenCalledWith(builtLibrary);
131
+ expect(builder.compileTxScript).toHaveBeenCalledWith('begin end');
132
+ });
133
+ });
@@ -0,0 +1,75 @@
1
+ import {
2
+ MidenClient,
3
+ type TransactionProver,
4
+ type TransactionScript,
5
+ WasmWebClient,
6
+ } from '@miden-sdk/miden-sdk';
7
+
8
+ export const DEFAULT_MIDEN_RPC_URL = 'https://rpc.devnet.miden.io';
9
+
10
+ export type RawClientSource = MidenClient | WasmWebClient;
11
+ export interface ScriptLibrarySource {
12
+ namespace: string;
13
+ code: string;
14
+ linking?: 'dynamic' | 'static';
15
+ }
16
+
17
+ const rawClientCache = new WeakMap<MidenClient, Promise<WasmWebClient>>();
18
+
19
+ export function resolveMidenRpcEndpoint(endpoint?: string): string {
20
+ return endpoint ?? DEFAULT_MIDEN_RPC_URL;
21
+ }
22
+
23
+ function isPublicMidenClient(client: RawClientSource): client is MidenClient {
24
+ return 'accounts' in client && 'sync' in client;
25
+ }
26
+
27
+ export function getRawMidenClient(
28
+ client: RawClientSource,
29
+ rpcUrl?: string,
30
+ ): Promise<WasmWebClient> {
31
+ if (!isPublicMidenClient(client)) {
32
+ return Promise.resolve(client);
33
+ }
34
+
35
+ const cached = rawClientCache.get(client);
36
+ if (cached) {
37
+ return cached;
38
+ }
39
+
40
+ const rawClient = WasmWebClient.createClient(
41
+ resolveMidenRpcEndpoint(rpcUrl),
42
+ undefined,
43
+ undefined,
44
+ client.storeIdentifier(),
45
+ );
46
+ rawClientCache.set(client, rawClient);
47
+ return rawClient;
48
+ }
49
+
50
+ export function getTransactionProver(client: RawClientSource): TransactionProver | null {
51
+ return isPublicMidenClient(client) ? client.defaultProver : null;
52
+ }
53
+
54
+ export async function compileTxScript(
55
+ client: RawClientSource,
56
+ code: string,
57
+ libraries: ScriptLibrarySource[] = [],
58
+ rpcUrl?: string,
59
+ ): Promise<TransactionScript> {
60
+ if (isPublicMidenClient(client)) {
61
+ return client.compile.txScript({ code, libraries });
62
+ }
63
+
64
+ const rawClient = await getRawMidenClient(client, rpcUrl);
65
+ const builder = rawClient.createCodeBuilder();
66
+ for (const library of libraries) {
67
+ const builtLibrary = builder.buildLibrary(library.namespace, library.code);
68
+ if (library.linking === 'static') {
69
+ builder.linkStaticLibrary(builtLibrary);
70
+ } else {
71
+ builder.linkDynamicLibrary(builtLibrary);
72
+ }
73
+ }
74
+ return builder.compileTxScript(code);
75
+ }
@@ -6,10 +6,12 @@ vi.mock('@miden-sdk/miden-sdk', () => {
6
6
  serialize: () => new Uint8Array([0, 1, 2, 3, 4, 5]),
7
7
  };
8
8
 
9
+ const mockCommitment = {
10
+ toHex: () => '0x' + 'a'.repeat(64),
11
+ };
12
+
9
13
  const mockPublicKey = {
10
- toCommitment: () => ({
11
- toHex: () => '0x' + 'a'.repeat(64),
12
- }),
14
+ toCommitment: () => mockCommitment,
13
15
  serialize: () => new Uint8Array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]),
14
16
  };
15
17
 
@@ -27,7 +29,8 @@ vi.mock('@miden-sdk/miden-sdk', () => {
27
29
  })),
28
30
  },
29
31
  AccountId: {
30
- fromHex: vi.fn(() => ({
32
+ fromHex: vi.fn((hex: string) => ({
33
+ toString: () => hex,
31
34
  prefix: () => ({ asInt: () => BigInt(1) }),
32
35
  suffix: () => ({ asInt: () => BigInt(2) }),
33
36
  })),
@@ -77,4 +80,32 @@ describe('EcdsaSigner', () => {
77
80
  expect(mockSecretKey.sign).toHaveBeenCalled();
78
81
  expect(Rpo256.hashElements).toHaveBeenCalledTimes(2);
79
82
  });
83
+
84
+ it('skips rebinding when the signer key is already attached to the same account', async () => {
85
+ const accountId = '0x' + 'd'.repeat(30);
86
+ const midenClient = {
87
+ keystore: {
88
+ getAccountId: vi.fn().mockResolvedValue({ toString: () => accountId }),
89
+ insert: vi.fn().mockResolvedValue(undefined),
90
+ },
91
+ };
92
+
93
+ await signer.bindAccountKey(midenClient as never, accountId);
94
+
95
+ expect(midenClient.keystore.insert).not.toHaveBeenCalled();
96
+ });
97
+
98
+ it('throws when the signer key is already attached to a different account', async () => {
99
+ const midenClient = {
100
+ keystore: {
101
+ getAccountId: vi.fn().mockResolvedValue({ toString: () => '0x' + 'e'.repeat(30) }),
102
+ insert: vi.fn().mockResolvedValue(undefined),
103
+ },
104
+ };
105
+
106
+ await expect(
107
+ signer.bindAccountKey(midenClient as never, '0x' + 'd'.repeat(30)),
108
+ ).rejects.toThrow('already bound to account');
109
+ expect(midenClient.keystore.insert).not.toHaveBeenCalled();
110
+ });
80
111
  });