@openzeppelin/miden-multisig-client 0.12.7 → 0.13.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 (201) hide show
  1. package/README.md +42 -4
  2. package/dist/account/builder.d.ts +1 -1
  3. package/dist/account/builder.d.ts.map +1 -1
  4. package/dist/account/builder.js +7 -5
  5. package/dist/account/builder.js.map +1 -1
  6. package/dist/account/masm.d.ts +3 -3
  7. package/dist/account/masm.d.ts.map +1 -1
  8. package/dist/account/masm.js +101 -98
  9. package/dist/account/masm.js.map +1 -1
  10. package/dist/account/storage.d.ts +1 -1
  11. package/dist/account/storage.d.ts.map +1 -1
  12. package/dist/account/storage.js +17 -7
  13. package/dist/account/storage.js.map +1 -1
  14. package/dist/client.d.ts +1 -1
  15. package/dist/client.d.ts.map +1 -1
  16. package/dist/client.js +2 -2
  17. package/dist/client.js.map +1 -1
  18. package/dist/client.test.js +4 -4
  19. package/dist/client.test.js.map +1 -1
  20. package/dist/index.d.ts +8 -1
  21. package/dist/index.d.ts.map +1 -1
  22. package/dist/index.js +6 -1
  23. package/dist/index.js.map +1 -1
  24. package/dist/inspector.d.ts +1 -1
  25. package/dist/inspector.d.ts.map +1 -1
  26. package/dist/inspector.js +16 -10
  27. package/dist/inspector.js.map +1 -1
  28. package/dist/inspector.test.js +33 -22
  29. package/dist/inspector.test.js.map +1 -1
  30. package/dist/multisig/helpers.d.ts +1 -1
  31. package/dist/multisig/helpers.d.ts.map +1 -1
  32. package/dist/multisig/helpers.js +1 -1
  33. package/dist/multisig/helpers.js.map +1 -1
  34. package/dist/multisig/helpers.test.js +2 -2
  35. package/dist/multisig/helpers.test.js.map +1 -1
  36. package/dist/multisig/proposal/execution.d.ts +18 -0
  37. package/dist/multisig/proposal/execution.d.ts.map +1 -0
  38. package/dist/multisig/proposal/execution.js +147 -0
  39. package/dist/multisig/proposal/execution.js.map +1 -0
  40. package/dist/multisig/proposal/parser.d.ts +17 -0
  41. package/dist/multisig/proposal/parser.d.ts.map +1 -0
  42. package/dist/multisig/proposal/parser.js +147 -0
  43. package/dist/multisig/proposal/parser.js.map +1 -0
  44. package/dist/multisig/signing.d.ts +5 -0
  45. package/dist/multisig/signing.d.ts.map +1 -0
  46. package/dist/multisig/signing.js +14 -0
  47. package/dist/multisig/signing.js.map +1 -0
  48. package/dist/multisig.d.ts +9 -13
  49. package/dist/multisig.d.ts.map +1 -1
  50. package/dist/multisig.js +109 -337
  51. package/dist/multisig.js.map +1 -1
  52. package/dist/multisig.test.js +8 -4
  53. package/dist/multisig.test.js.map +1 -1
  54. package/dist/signer.d.ts +2 -20
  55. package/dist/signer.d.ts.map +1 -1
  56. package/dist/signer.js +2 -82
  57. package/dist/signer.js.map +1 -1
  58. package/dist/signer.test.js +24 -24
  59. package/dist/signer.test.js.map +1 -1
  60. package/dist/signers/ecdsa.d.ts +12 -0
  61. package/dist/signers/ecdsa.d.ts.map +1 -0
  62. package/dist/signers/ecdsa.js +30 -0
  63. package/dist/signers/ecdsa.js.map +1 -0
  64. package/dist/signers/falcon.d.ts +12 -0
  65. package/dist/signers/falcon.d.ts.map +1 -0
  66. package/dist/signers/falcon.js +31 -0
  67. package/dist/signers/falcon.js.map +1 -0
  68. package/dist/signers/falcon.test.d.ts +2 -0
  69. package/dist/signers/falcon.test.d.ts.map +1 -0
  70. package/dist/signers/falcon.test.js +113 -0
  71. package/dist/signers/falcon.test.js.map +1 -0
  72. package/dist/signers/index.d.ts +5 -0
  73. package/dist/signers/index.d.ts.map +1 -0
  74. package/dist/signers/index.js +5 -0
  75. package/dist/signers/index.js.map +1 -0
  76. package/dist/signers/miden-wallet.d.ts +16 -0
  77. package/dist/signers/miden-wallet.d.ts.map +1 -0
  78. package/dist/signers/miden-wallet.js +35 -0
  79. package/dist/signers/miden-wallet.js.map +1 -0
  80. package/dist/signers/miden-wallet.test.d.ts +2 -0
  81. package/dist/signers/miden-wallet.test.d.ts.map +1 -0
  82. package/dist/signers/miden-wallet.test.js +107 -0
  83. package/dist/signers/miden-wallet.test.js.map +1 -0
  84. package/dist/signers/para.d.ts +21 -0
  85. package/dist/signers/para.d.ts.map +1 -0
  86. package/dist/signers/para.js +43 -0
  87. package/dist/signers/para.js.map +1 -0
  88. package/dist/signers/para.test.d.ts +2 -0
  89. package/dist/signers/para.test.d.ts.map +1 -0
  90. package/dist/signers/para.test.js +88 -0
  91. package/dist/signers/para.test.js.map +1 -0
  92. package/dist/transaction/consumeNotes.d.ts +3 -3
  93. package/dist/transaction/consumeNotes.d.ts.map +1 -1
  94. package/dist/transaction/consumeNotes.js +11 -7
  95. package/dist/transaction/consumeNotes.js.map +1 -1
  96. package/dist/transaction/options.d.ts +2 -2
  97. package/dist/transaction/options.d.ts.map +1 -1
  98. package/dist/transaction/p2id.d.ts +1 -1
  99. package/dist/transaction/p2id.d.ts.map +1 -1
  100. package/dist/transaction/p2id.js +5 -5
  101. package/dist/transaction/p2id.js.map +1 -1
  102. package/dist/transaction/summary.d.ts +1 -1
  103. package/dist/transaction/summary.d.ts.map +1 -1
  104. package/dist/transaction/summary.js +1 -1
  105. package/dist/transaction/summary.js.map +1 -1
  106. package/dist/transaction/updatePsm.d.ts +1 -1
  107. package/dist/transaction/updatePsm.d.ts.map +1 -1
  108. package/dist/transaction/updatePsm.js +5 -5
  109. package/dist/transaction/updatePsm.js.map +1 -1
  110. package/dist/transaction/updateSigners.d.ts +1 -1
  111. package/dist/transaction/updateSigners.d.ts.map +1 -1
  112. package/dist/transaction/updateSigners.js +5 -5
  113. package/dist/transaction/updateSigners.js.map +1 -1
  114. package/dist/transaction.test.js +1 -1
  115. package/dist/transaction.test.js.map +1 -1
  116. package/dist/types.d.ts +1 -1
  117. package/dist/types.d.ts.map +1 -1
  118. package/dist/utils/digest.d.ts +6 -0
  119. package/dist/utils/digest.d.ts.map +1 -0
  120. package/dist/utils/digest.js +22 -0
  121. package/dist/utils/digest.js.map +1 -0
  122. package/dist/utils/ecdsa.d.ts +8 -0
  123. package/dist/utils/ecdsa.d.ts.map +1 -0
  124. package/dist/utils/ecdsa.js +60 -0
  125. package/dist/utils/ecdsa.js.map +1 -0
  126. package/dist/utils/ecdsa.test.d.ts +2 -0
  127. package/dist/utils/ecdsa.test.d.ts.map +1 -0
  128. package/dist/utils/ecdsa.test.js +85 -0
  129. package/dist/utils/ecdsa.test.js.map +1 -0
  130. package/dist/utils/index.d.ts +1 -0
  131. package/dist/utils/index.d.ts.map +1 -1
  132. package/dist/utils/index.js +1 -0
  133. package/dist/utils/index.js.map +1 -1
  134. package/dist/utils/key.d.ts +10 -0
  135. package/dist/utils/key.d.ts.map +1 -0
  136. package/dist/utils/key.js +38 -0
  137. package/dist/utils/key.js.map +1 -0
  138. package/dist/utils/key.test.d.ts +2 -0
  139. package/dist/utils/key.test.d.ts.map +1 -0
  140. package/dist/utils/key.test.js +80 -0
  141. package/dist/utils/key.test.js.map +1 -0
  142. package/dist/utils/random.d.ts +1 -1
  143. package/dist/utils/random.d.ts.map +1 -1
  144. package/dist/utils/random.js +1 -1
  145. package/dist/utils/random.js.map +1 -1
  146. package/dist/utils/signature.d.ts +2 -1
  147. package/dist/utils/signature.d.ts.map +1 -1
  148. package/dist/utils/signature.js +6 -3
  149. package/dist/utils/signature.js.map +1 -1
  150. package/dist/utils/signature.test.js +11 -11
  151. package/dist/utils/signature.test.js.map +1 -1
  152. package/dist/utils/word.d.ts +6 -1
  153. package/dist/utils/word.d.ts.map +1 -1
  154. package/dist/utils/word.js +11 -0
  155. package/dist/utils/word.js.map +1 -1
  156. package/dist/utils/word.test.js +54 -3
  157. package/dist/utils/word.test.js.map +1 -1
  158. package/package.json +4 -5
  159. package/src/account/builder.ts +7 -5
  160. package/src/account/masm.ts +103 -99
  161. package/src/account/storage.ts +19 -7
  162. package/src/client.test.ts +4 -4
  163. package/src/client.ts +3 -3
  164. package/src/index.ts +8 -1
  165. package/src/inspector.test.ts +38 -25
  166. package/src/inspector.ts +18 -10
  167. package/src/multisig/helpers.test.ts +2 -2
  168. package/src/multisig/helpers.ts +2 -1
  169. package/src/multisig/proposal/execution.ts +320 -0
  170. package/src/multisig/proposal/parser.ts +191 -0
  171. package/src/multisig/signing.ts +24 -0
  172. package/src/multisig.test.ts +8 -4
  173. package/src/multisig.ts +153 -434
  174. package/src/signer.ts +2 -95
  175. package/src/signers/ecdsa.ts +35 -0
  176. package/src/{signer.test.ts → signers/falcon.test.ts} +29 -33
  177. package/src/signers/falcon.ts +36 -0
  178. package/src/signers/index.ts +4 -0
  179. package/src/signers/miden-wallet.test.ts +119 -0
  180. package/src/signers/miden-wallet.ts +49 -0
  181. package/src/signers/para.test.ts +103 -0
  182. package/src/signers/para.ts +60 -0
  183. package/src/transaction/consumeNotes.ts +14 -10
  184. package/src/transaction/options.ts +2 -2
  185. package/src/transaction/p2id.ts +3 -8
  186. package/src/transaction/summary.ts +2 -2
  187. package/src/transaction/updatePsm.ts +5 -6
  188. package/src/transaction/updateSigners.ts +5 -6
  189. package/src/transaction.test.ts +1 -1
  190. package/src/types.ts +1 -1
  191. package/src/utils/digest.ts +25 -0
  192. package/src/utils/ecdsa.test.ts +104 -0
  193. package/src/utils/ecdsa.ts +77 -0
  194. package/src/utils/index.ts +1 -0
  195. package/src/utils/key.test.ts +89 -0
  196. package/src/utils/key.ts +49 -0
  197. package/src/utils/random.ts +1 -1
  198. package/src/utils/signature.test.ts +11 -11
  199. package/src/utils/signature.ts +10 -3
  200. package/src/utils/word.test.ts +57 -3
  201. package/src/utils/word.ts +13 -1
package/dist/multisig.js CHANGED
@@ -1,9 +1,11 @@
1
1
  import { AccountInspector } from './inspector.js';
2
- import { Account, AccountId, AdviceMap, FeltArray, Signature, TransactionSummary, Word, } from '@demox-labs/miden-sdk';
2
+ import { Account, AccountId, } from '@miden-sdk/miden-sdk';
3
3
  import { executeForSummary, buildUpdateSignersTransactionRequest, buildUpdatePsmTransactionRequest, buildConsumeNotesTransactionRequest, buildP2idTransactionRequest, } from './transaction.js';
4
4
  import { base64ToUint8Array, uint8ArrayToBase64, normalizeHexWord, } from './utils/encoding.js';
5
- import { buildSignatureAdviceEntry, signatureHexToBytes, tryComputeEcdsaCommitmentHex } from './utils/signature.js';
6
5
  import { computeCommitmentFromTxSummary, accountIdToHex } from './multisig/helpers.js';
6
+ import { buildPsmMetadata, deltaToProposal, resolveMetadata, signatureRequirementForProposal, } from './multisig/proposal/parser.js';
7
+ import { toPsmSignature, buildPsmSignatureFromSigner, } from './multisig/signing.js';
8
+ import { executeProposalWorkflow } from './multisig/proposal/execution.js';
7
9
  export class Multisig {
8
10
  account;
9
11
  threshold;
@@ -125,8 +127,7 @@ export class Multisig {
125
127
  const onChain = normalizeHexWord(this.psmCommitment);
126
128
  const server = normalizeHexWord(response.ackCommitment);
127
129
  if (onChain !== server) {
128
- throw new Error(`PSM commitment mismatch: on-chain=${onChain}, server=${server}. ` +
129
- `Re-create the account with getPubkey('${this.signatureScheme}') to get the correct PSM commitment.`);
130
+ throw new Error(`PSM commitment mismatch: on-chain=${onChain}, server=${server}`);
130
131
  }
131
132
  }
132
133
  }
@@ -135,6 +136,12 @@ export class Multisig {
135
136
  const state = await this.syncState();
136
137
  const notes = await this.getConsumableNotes();
137
138
  const config = AccountInspector.fromBase64(state.stateDataBase64, this.signatureScheme);
139
+ this.threshold = config.threshold;
140
+ this.signerCommitments = config.signerCommitments;
141
+ if (config.psmCommitment) {
142
+ this.psmCommitment = config.psmCommitment;
143
+ }
144
+ this.procedureThresholds = config.procedureThresholds;
138
145
  return { proposals, state, notes, config };
139
146
  }
140
147
  async getAccountConfig() {
@@ -142,9 +149,38 @@ export class Multisig {
142
149
  return AccountInspector.fromBase64(state.stateDataBase64, this.signatureScheme);
143
150
  }
144
151
  async switchPsm(psmClient) {
152
+ const accountId = AccountId.fromHex(this._accountId);
153
+ const localAccount = await this.webClient.getAccount(accountId);
154
+ if (localAccount) {
155
+ this.account = localAccount;
156
+ const config = AccountInspector.fromAccount(localAccount, this.signatureScheme);
157
+ if (config.psmCommitment) {
158
+ this.psmCommitment = config.psmCommitment;
159
+ }
160
+ this.threshold = config.threshold;
161
+ this.signerCommitments = config.signerCommitments;
162
+ this.procedureThresholds = config.procedureThresholds;
163
+ }
145
164
  this.setPsmClient(psmClient);
146
- const state = await this.fetchState();
147
- await this.registerOnPsm(state.stateDataBase64);
165
+ const accountBytes = this.account.serialize();
166
+ const stateData = uint8ArrayToBase64(accountBytes);
167
+ const auth = this.signer.scheme === 'ecdsa'
168
+ ? { MidenEcdsa: { cosigner_commitments: this.signerCommitments } }
169
+ : { MidenFalconRpo: { cosigner_commitments: this.signerCommitments } };
170
+ const response = await this.psm.configure({
171
+ accountId: this._accountId,
172
+ auth,
173
+ initialState: { data: stateData, accountId: this._accountId },
174
+ });
175
+ if (!response.success) {
176
+ throw new Error(`Failed to register on PSM: ${response.message}`);
177
+ }
178
+ if (response.ackCommitment) {
179
+ this.psmCommitment = normalizeHexWord(response.ackCommitment);
180
+ }
181
+ if (response.ackPubkey) {
182
+ this.psmPublicKey = response.ackPubkey;
183
+ }
148
184
  }
149
185
  async syncTransactionProposals() {
150
186
  const deltas = await this.psm.getDeltaProposals(this._accountId);
@@ -153,14 +189,17 @@ export class Multisig {
153
189
  const proposalId = computeCommitmentFromTxSummary(delta.deltaPayload.txSummary.data);
154
190
  serverProposalIds.add(proposalId);
155
191
  const existingProposal = this.proposals.get(proposalId);
156
- const resolvedMetadata = existingProposal?.metadata ??
157
- (delta.deltaPayload.metadata
158
- ? this.fromPsmMetadata(delta.deltaPayload.metadata)
159
- : undefined);
192
+ const resolvedMetadata = resolveMetadata(delta, existingProposal?.metadata);
160
193
  if (!resolvedMetadata) {
161
194
  throw new Error('Missing proposal metadata from PSM');
162
195
  }
163
- const proposal = this.deltaToProposal(delta, proposalId, resolvedMetadata, existingProposal?.signatures);
196
+ const proposal = deltaToProposal({
197
+ delta,
198
+ proposalId,
199
+ metadata: resolvedMetadata,
200
+ signaturesRequired: signatureRequirementForProposal(resolvedMetadata, this.threshold, (proposalType) => this.getEffectiveThreshold(proposalType)),
201
+ existingSignatures: existingProposal?.signatures,
202
+ });
164
203
  this.proposals.set(proposal.id, proposal);
165
204
  }
166
205
  for (const [id, proposal] of this.proposals) {
@@ -177,7 +216,7 @@ export class Multisig {
177
216
  return Array.from(this.proposals.values());
178
217
  }
179
218
  async createProposal(nonce, txSummaryBase64, metadata) {
180
- const psmMetadata = this.buildPsmMetadata(metadata);
219
+ const psmMetadata = buildPsmMetadata(metadata);
181
220
  const response = await this.psm.pushDeltaProposal({
182
221
  accountId: this._accountId,
183
222
  nonce,
@@ -187,7 +226,12 @@ export class Multisig {
187
226
  metadata: psmMetadata,
188
227
  },
189
228
  });
190
- const proposal = this.deltaToProposal(response.delta, response.commitment, metadata);
229
+ const proposal = deltaToProposal({
230
+ delta: response.delta,
231
+ proposalId: response.commitment,
232
+ metadata,
233
+ signaturesRequired: signatureRequirementForProposal(metadata, this.threshold, (proposalType) => this.getEffectiveThreshold(proposalType)),
234
+ });
191
235
  this.proposals.set(proposal.id, proposal);
192
236
  return proposal;
193
237
  }
@@ -195,9 +239,6 @@ export class Multisig {
195
239
  const targetThreshold = options?.newThreshold ?? this.threshold;
196
240
  const targetSignerCommitments = [...this.signerCommitments, newCommitment];
197
241
  const { request, salt } = await buildUpdateSignersTransactionRequest(this.webClient, targetThreshold, targetSignerCommitments, { signatureScheme: this.signatureScheme });
198
- const summary = await executeForSummary(this.webClient, this._accountId, request);
199
- const summaryBase64 = uint8ArrayToBase64(summary.serialize());
200
- const proposalNonce = options?.nonce ?? Date.now();
201
242
  const metadata = {
202
243
  proposalType: 'add_signer',
203
244
  targetThreshold,
@@ -205,8 +246,7 @@ export class Multisig {
205
246
  saltHex: salt.toHex(),
206
247
  description: `Add signer ${newCommitment.slice(0, 10)}...`,
207
248
  };
208
- const proposal = await this.createProposal(proposalNonce, summaryBase64, metadata);
209
- return this.syncAfterCreate(proposal);
249
+ return this.createAndSyncFromRequest(request, metadata, options?.nonce);
210
250
  }
211
251
  async createRemoveSignerProposal(signerToRemove, options) {
212
252
  const normalizedRemove = signerToRemove.toLowerCase();
@@ -223,9 +263,6 @@ export class Multisig {
223
263
  throw new Error(`Invalid threshold ${targetThreshold}. Must be between 1 and ${targetSignerCommitments.length}`);
224
264
  }
225
265
  const { request, salt } = await buildUpdateSignersTransactionRequest(this.webClient, targetThreshold, targetSignerCommitments, { signatureScheme: this.signatureScheme });
226
- const summary = await executeForSummary(this.webClient, this._accountId, request);
227
- const summaryBase64 = uint8ArrayToBase64(summary.serialize());
228
- const proposalNonce = options?.nonce ?? Date.now();
229
266
  const metadata = {
230
267
  proposalType: 'remove_signer',
231
268
  targetThreshold,
@@ -233,8 +270,7 @@ export class Multisig {
233
270
  saltHex: salt.toHex(),
234
271
  description: `Remove signer ${signerToRemove.slice(0, 10)}...`,
235
272
  };
236
- const proposal = await this.createProposal(proposalNonce, summaryBase64, metadata);
237
- return this.syncAfterCreate(proposal);
273
+ return this.createAndSyncFromRequest(request, metadata, options?.nonce);
238
274
  }
239
275
  async createChangeThresholdProposal(newThreshold, options) {
240
276
  if (newThreshold < 1 || newThreshold > this.signerCommitments.length) {
@@ -244,9 +280,6 @@ export class Multisig {
244
280
  throw new Error('New threshold is the same as current threshold');
245
281
  }
246
282
  const { request, salt } = await buildUpdateSignersTransactionRequest(this.webClient, newThreshold, this.signerCommitments, { signatureScheme: this.signatureScheme });
247
- const summary = await executeForSummary(this.webClient, this._accountId, request);
248
- const summaryBase64 = uint8ArrayToBase64(summary.serialize());
249
- const proposalNonce = options?.nonce ?? Date.now();
250
283
  const metadata = {
251
284
  proposalType: 'change_threshold',
252
285
  targetThreshold: newThreshold,
@@ -254,8 +287,7 @@ export class Multisig {
254
287
  saltHex: salt.toHex(),
255
288
  description: `Change threshold from ${this.threshold} to ${newThreshold}`,
256
289
  };
257
- const proposal = await this.createProposal(proposalNonce, summaryBase64, metadata);
258
- return this.syncAfterCreate(proposal);
290
+ return this.createAndSyncFromRequest(request, metadata, options?.nonce);
259
291
  }
260
292
  async createSwitchPsmProposal(newPsmEndpoint, newPsmPubkey, options) {
261
293
  const { request, salt } = await buildUpdatePsmTransactionRequest(this.webClient, newPsmPubkey, { signatureScheme: this.signatureScheme });
@@ -288,27 +320,20 @@ export class Multisig {
288
320
  if (noteIds.length === 0) {
289
321
  throw new Error('At least one note ID is required');
290
322
  }
291
- const { request, salt } = buildConsumeNotesTransactionRequest(noteIds);
292
- const summary = await executeForSummary(this.webClient, this._accountId, request);
293
- const summaryBase64 = uint8ArrayToBase64(summary.serialize());
294
- const proposalNonce = options?.nonce ?? Date.now();
323
+ const { request, salt } = await buildConsumeNotesTransactionRequest(this.webClient, noteIds);
295
324
  const metadata = {
296
325
  proposalType: 'consume_notes',
297
326
  noteIds,
298
327
  saltHex: salt.toHex(),
299
328
  description: `Consume ${noteIds.length} note(s)`,
300
329
  };
301
- const proposal = await this.createProposal(proposalNonce, summaryBase64, metadata);
302
- return this.syncAfterCreate(proposal);
330
+ return this.createAndSyncFromRequest(request, metadata, options?.nonce);
303
331
  }
304
332
  async createSendProposal(recipientId, faucetId, amount, options) {
305
333
  if (amount <= 0n) {
306
334
  throw new Error('Amount must be greater than 0');
307
335
  }
308
336
  const { request, salt } = buildP2idTransactionRequest(this._accountId, recipientId, faucetId, amount);
309
- const summary = await executeForSummary(this.webClient, this._accountId, request);
310
- const summaryBase64 = uint8ArrayToBase64(summary.serialize());
311
- const proposalNonce = options?.nonce ?? Date.now();
312
337
  const metadata = {
313
338
  proposalType: 'p2id',
314
339
  saltHex: salt.toHex(),
@@ -317,8 +342,7 @@ export class Multisig {
317
342
  amount: amount.toString(),
318
343
  description: `Send ${amount} to ${recipientId.slice(0, 10)}...`,
319
344
  };
320
- const proposal = await this.createProposal(proposalNonce, summaryBase64, metadata);
321
- return this.syncAfterCreate(proposal);
345
+ return this.createAndSyncFromRequest(request, metadata, options?.nonce);
322
346
  }
323
347
  async getConsumableNotes() {
324
348
  const accountId = AccountId.fromHex(this._accountId);
@@ -328,7 +352,7 @@ export class Multisig {
328
352
  const inputNote = record.inputNoteRecord();
329
353
  const consumability = record.noteConsumability();
330
354
  const canConsumeNow = consumability.some((c) => c.accountId().toString().toLowerCase() === this._accountId.toLowerCase() &&
331
- c.consumableAfterBlock() === undefined);
355
+ c.consumptionStatus().consumableAfterBlock() === undefined);
332
356
  if (canConsumeNow) {
333
357
  const noteId = inputNote.id().toString();
334
358
  const details = inputNote.details();
@@ -347,171 +371,67 @@ export class Multisig {
347
371
  }
348
372
  async signTransactionProposal(commitment) {
349
373
  const existingProposal = this.proposals.get(commitment);
350
- const signatureHex = this.signer.signCommitment(commitment);
351
- const signature = this.signer.scheme === 'ecdsa'
352
- ? { scheme: 'ecdsa', signature: signatureHex, publicKey: this.signer.publicKey }
353
- : { scheme: 'falcon', signature: signatureHex };
374
+ const signature = await buildPsmSignatureFromSigner(this.signer, commitment);
354
375
  const delta = await this.psm.signDeltaProposal({
355
376
  accountId: this._accountId,
356
377
  commitment,
357
378
  signature,
358
379
  });
359
- const proposal = this.deltaToProposal(delta, commitment, undefined, existingProposal?.signatures);
360
- if (existingProposal?.metadata) {
361
- proposal.metadata = existingProposal.metadata;
380
+ const resolvedMetadata = resolveMetadata(delta, existingProposal?.metadata);
381
+ if (!resolvedMetadata) {
382
+ throw new Error('Missing proposal metadata');
362
383
  }
384
+ const proposal = deltaToProposal({
385
+ delta,
386
+ proposalId: commitment,
387
+ metadata: resolvedMetadata,
388
+ signaturesRequired: signatureRequirementForProposal(resolvedMetadata, this.threshold, (proposalType) => this.getEffectiveThreshold(proposalType)),
389
+ existingSignatures: existingProposal?.signatures,
390
+ });
363
391
  this.proposals.set(proposal.id, proposal);
364
- return this.syncTransactionProposals();
392
+ return this.listTransactionProposals();
365
393
  }
366
394
  async signTransactionProposalExternal(params) {
367
395
  const { commitment, signature: signatureHex, publicKey, scheme } = params;
368
396
  const resolvedScheme = scheme ?? this.signatureScheme;
369
397
  const existingProposal = this.proposals.get(commitment);
370
- const signature = resolvedScheme === 'ecdsa'
371
- ? { scheme: 'ecdsa', signature: signatureHex, publicKey: publicKey }
372
- : { scheme: 'falcon', signature: signatureHex };
398
+ const signature = toPsmSignature(resolvedScheme, signatureHex, publicKey);
373
399
  const delta = await this.psm.signDeltaProposal({
374
400
  accountId: this._accountId,
375
401
  commitment,
376
402
  signature,
377
403
  });
378
- const proposal = this.deltaToProposal(delta, commitment, undefined, existingProposal?.signatures);
379
- if (existingProposal?.metadata) {
380
- proposal.metadata = existingProposal.metadata;
404
+ const resolvedMetadata = resolveMetadata(delta, existingProposal?.metadata);
405
+ if (!resolvedMetadata) {
406
+ throw new Error('Missing proposal metadata');
381
407
  }
408
+ const proposal = deltaToProposal({
409
+ delta,
410
+ proposalId: commitment,
411
+ metadata: resolvedMetadata,
412
+ signaturesRequired: signatureRequirementForProposal(resolvedMetadata, this.threshold, (proposalType) => this.getEffectiveThreshold(proposalType)),
413
+ existingSignatures: existingProposal?.signatures,
414
+ });
382
415
  this.proposals.set(proposal.id, proposal);
383
- return this.syncTransactionProposals();
416
+ return this.listTransactionProposals();
384
417
  }
385
418
  async executeTransactionProposal(commitment) {
386
419
  const proposal = this.proposals.get(commitment);
387
420
  if (!proposal) {
388
421
  throw new Error(`Proposal not found: ${commitment}`);
389
422
  }
390
- const proposalType = proposal.metadata?.proposalType;
391
- const effectiveThreshold = proposalType
392
- ? this.getEffectiveThreshold(proposalType)
393
- : this.threshold;
394
- if (proposal.signatures.length < effectiveThreshold) {
395
- throw new Error('Proposal is not ready for execution. Still pending signatures.');
396
- }
397
- const isSwitchPsm = proposalType === 'switch_psm';
398
- let txSummaryBase64;
399
- let delta;
400
- if (isSwitchPsm) {
401
- txSummaryBase64 = proposal.txSummary;
402
- }
403
- else {
404
- const deltas = await this.psm.getDeltaProposals(this._accountId);
405
- delta = deltas.find((d) => computeCommitmentFromTxSummary(d.deltaPayload.txSummary.data) === commitment);
406
- if (!delta) {
407
- throw new Error(`Proposal not found on server: ${commitment}`);
408
- }
409
- txSummaryBase64 = delta.deltaPayload.txSummary.data;
410
- }
411
- const txSummaryBytes = base64ToUint8Array(txSummaryBase64);
412
- const txSummary = TransactionSummary.deserialize(txSummaryBytes);
413
- const saltHex = txSummary.salt().toHex();
414
- const txCommitmentHex = txSummary.toCommitment().toHex();
415
- const adviceMap = new AdviceMap();
416
- const normalizedSignerCommitments = new Set(this.signerCommitments.map((c) => normalizeHexWord(c)));
417
- for (const cosignerSig of proposal.signatures) {
418
- let signerCommitmentHex = normalizeHexWord(cosignerSig.signerId);
419
- if (cosignerSig.signature.scheme === 'ecdsa' && cosignerSig.signature.publicKey) {
420
- const derived = tryComputeEcdsaCommitmentHex(cosignerSig.signature.publicKey);
421
- if (derived && derived !== signerCommitmentHex) {
422
- if (!normalizedSignerCommitments.has(derived)) {
423
- throw new Error(`ECDSA public key commitment mismatch: derived commitment ${derived} is not in signerCommitments.`);
424
- }
425
- signerCommitmentHex = derived;
426
- }
427
- }
428
- const signerCommitment = Word.fromHex(signerCommitmentHex);
429
- const sigBytes = signatureHexToBytes(cosignerSig.signature.signature, cosignerSig.signature.scheme);
430
- const signature = Signature.deserialize(sigBytes);
431
- const txCommitment = Word.fromHex(normalizeHexWord(txCommitmentHex));
432
- const isEcdsa = cosignerSig.signature.scheme === 'ecdsa' && cosignerSig.signature.publicKey;
433
- const { key, values } = buildSignatureAdviceEntry(signerCommitment, txCommitment, signature, isEcdsa ? cosignerSig.signature.publicKey : undefined, isEcdsa ? cosignerSig.signature.signature : undefined);
434
- adviceMap.insert(key, new FeltArray(values));
435
- }
436
- if (!isSwitchPsm && delta) {
437
- const executionDelta = {
438
- ...delta,
439
- deltaPayload: delta.deltaPayload.txSummary,
440
- };
441
- const pushResult = await this.psm.pushDelta(executionDelta);
442
- const ackSigHex = pushResult.ackSig;
443
- if (!ackSigHex) {
444
- throw new Error('PSM did not return acknowledgment signature');
445
- }
446
- const psmAckScheme = pushResult.ackScheme || this.signatureScheme;
447
- const psmAckPubkey = pushResult.ackPubkey || this.psmPublicKey;
448
- const psmCommitmentHex = normalizeHexWord(this.psmCommitment);
449
- if (psmAckScheme === 'ecdsa' && psmAckPubkey) {
450
- const derived = tryComputeEcdsaCommitmentHex(psmAckPubkey);
451
- if (derived && derived !== psmCommitmentHex) {
452
- throw new Error(`PSM public key commitment mismatch`);
453
- }
454
- }
455
- const psmCommitment = Word.fromHex(psmCommitmentHex);
456
- const ackSigBytes = signatureHexToBytes(ackSigHex, psmAckScheme);
457
- const ackSignature = Signature.deserialize(ackSigBytes);
458
- const txCommitmentForAck = Word.fromHex(normalizeHexWord(txCommitmentHex));
459
- const isAckEcdsa = psmAckScheme === 'ecdsa' && psmAckPubkey;
460
- const { key: ackKey, values: ackValues } = buildSignatureAdviceEntry(psmCommitment, txCommitmentForAck, ackSignature, isAckEcdsa ? psmAckPubkey : undefined, isAckEcdsa ? ackSigHex : undefined);
461
- adviceMap.insert(ackKey, new FeltArray(ackValues));
462
- }
463
- const metadata = proposal.metadata;
464
- if (!metadata) {
465
- throw new Error('Proposal missing metadata');
466
- }
467
- let finalRequest;
468
- switch (metadata.proposalType) {
469
- case 'consume_notes': {
470
- if (!metadata.noteIds || metadata.noteIds.length === 0) {
471
- throw new Error('Proposal missing noteIds. Was it created with createConsumeNotesProposal?');
472
- }
473
- const { request } = buildConsumeNotesTransactionRequest(metadata.noteIds, { salt: Word.fromHex(normalizeHexWord(saltHex)), signatureAdviceMap: adviceMap });
474
- finalRequest = request;
475
- break;
476
- }
477
- case 'switch_psm': {
478
- if (!metadata.newPsmPubkey) {
479
- throw new Error('Proposal missing newPsmPubkey. Was it created with createSwitchPsmProposal?');
480
- }
481
- const { request } = await buildUpdatePsmTransactionRequest(this.webClient, metadata.newPsmPubkey, {
482
- salt: Word.fromHex(normalizeHexWord(saltHex)),
483
- signatureAdviceMap: adviceMap,
484
- signatureScheme: this.signatureScheme,
485
- });
486
- finalRequest = request;
487
- break;
488
- }
489
- case 'p2id': {
490
- if (!metadata.recipientId || !metadata.faucetId || !metadata.amount) {
491
- throw new Error('Proposal missing P2ID metadata (recipientId, faucetId, amount). Was it created with createP2idProposal?');
492
- }
493
- const { request } = buildP2idTransactionRequest(this._accountId, metadata.recipientId, metadata.faucetId, BigInt(metadata.amount), { salt: Word.fromHex(normalizeHexWord(saltHex)), signatureAdviceMap: adviceMap });
494
- finalRequest = request;
495
- break;
496
- }
497
- case 'unknown': {
498
- throw new Error('Cannot execute proposal with unknown type. The proposal must have been imported without proper metadata.');
499
- }
500
- default: {
501
- const { request } = await buildUpdateSignersTransactionRequest(this.webClient, metadata.targetThreshold, metadata.targetSignerCommitments, {
502
- salt: Word.fromHex(normalizeHexWord(saltHex)),
503
- signatureAdviceMap: adviceMap,
504
- signatureScheme: this.signatureScheme,
505
- });
506
- finalRequest = request;
507
- break;
508
- }
509
- }
510
- const accountId = AccountId.fromHex(this._accountId);
511
- const result = await this.webClient.executeTransaction(accountId, finalRequest);
512
- const proven = await this.webClient.proveTransaction(result, null);
513
- const submissionHeight = await this.webClient.submitProvenTransaction(proven, result);
514
- await this.webClient.applyTransaction(result, submissionHeight);
423
+ await executeProposalWorkflow({
424
+ proposal,
425
+ accountId: this._accountId,
426
+ threshold: this.threshold,
427
+ signerCommitments: this.signerCommitments,
428
+ psmCommitment: this.psmCommitment,
429
+ psmPublicKey: this.psmPublicKey,
430
+ signatureScheme: this.signatureScheme,
431
+ getEffectiveThreshold: (proposalType) => this.getEffectiveThreshold(proposalType),
432
+ psm: this.psm,
433
+ webClient: this.webClient,
434
+ });
515
435
  proposal.status = { type: 'finalized' };
516
436
  }
517
437
  exportTransactionProposalToJson(commitment) {
@@ -577,7 +497,7 @@ export class Multisig {
577
497
  const proposals = this.listTransactionProposals();
578
498
  return { proposal, proposals };
579
499
  }
580
- signTransactionProposalOffline(commitment) {
500
+ async signTransactionProposalOffline(commitment) {
581
501
  const proposal = this.proposals.get(commitment);
582
502
  if (!proposal) {
583
503
  throw new Error(`Proposal not found: ${commitment}`);
@@ -586,10 +506,8 @@ export class Multisig {
586
506
  if (alreadySigned) {
587
507
  throw new Error('You have already signed this proposal');
588
508
  }
589
- const signatureHex = this.signer.signCommitment(commitment);
590
- const sigEntry = this.signer.scheme === 'ecdsa'
591
- ? { scheme: 'ecdsa', signature: signatureHex, publicKey: this.signer.publicKey }
592
- : { scheme: 'falcon', signature: signatureHex };
509
+ const signatureHex = await this.signer.signCommitment(commitment);
510
+ const sigEntry = toPsmSignature(this.signer.scheme, signatureHex, this.signer.publicKey);
593
511
  proposal.signatures.push({
594
512
  signerId: this.signer.commitment,
595
513
  signature: sigEntry,
@@ -613,158 +531,12 @@ export class Multisig {
613
531
  }
614
532
  return this.exportTransactionProposalToJson(commitment);
615
533
  }
616
- async syncAfterCreate(proposal) {
617
- let proposals;
618
- try {
619
- proposals = await this.syncTransactionProposals();
620
- if (!proposals.find((p) => p.id === proposal.id)) {
621
- proposals = [...proposals, proposal];
622
- }
623
- }
624
- catch {
625
- proposals = this.listTransactionProposals();
626
- }
627
- return { proposal, proposals };
628
- }
629
- deltaToProposal(delta, proposalId, metadata, existingSignatures) {
630
- const resolvedMetadata = metadata ??
631
- (delta.deltaPayload.metadata ? this.fromPsmMetadata(delta.deltaPayload.metadata) : undefined);
632
- if (!resolvedMetadata) {
633
- throw new Error('Missing proposal metadata');
634
- }
635
- const status = this.deltaStatusToProposalStatus(delta.status, resolvedMetadata.proposalType);
636
- const signaturesFromStatus = delta.status.status === 'pending'
637
- ? delta.status.cosignerSigs.map((s) => ({
638
- signerId: s.signerId,
639
- signature: s.signature,
640
- timestamp: s.timestamp,
641
- }))
642
- : [];
643
- const signaturesMap = new Map();
644
- for (const sig of existingSignatures ?? []) {
645
- signaturesMap.set(sig.signerId, sig);
646
- }
647
- for (const sig of signaturesFromStatus) {
648
- signaturesMap.set(sig.signerId, sig);
649
- }
650
- const signatures = Array.from(signaturesMap.values());
651
- return {
652
- id: proposalId,
653
- commitment: proposalId,
654
- accountId: delta.accountId,
655
- nonce: delta.nonce,
656
- status,
657
- txSummary: delta.deltaPayload.txSummary.data,
658
- signatures,
659
- metadata: resolvedMetadata,
660
- };
661
- }
662
- buildPsmMetadata(metadata) {
663
- const base = {
664
- proposalType: metadata.proposalType,
665
- description: metadata.description,
666
- salt: metadata.saltHex,
667
- };
668
- switch (metadata.proposalType) {
669
- case 'consume_notes':
670
- return {
671
- ...base,
672
- noteIds: metadata.noteIds,
673
- };
674
- case 'p2id':
675
- return {
676
- ...base,
677
- recipientId: metadata.recipientId,
678
- faucetId: metadata.faucetId,
679
- amount: metadata.amount,
680
- };
681
- case 'switch_psm':
682
- return {
683
- ...base,
684
- targetThreshold: metadata.targetThreshold,
685
- signerCommitments: metadata.targetSignerCommitments,
686
- newPsmPubkey: metadata.newPsmPubkey,
687
- newPsmEndpoint: metadata.newPsmEndpoint,
688
- };
689
- case 'add_signer':
690
- case 'remove_signer':
691
- case 'change_threshold':
692
- return {
693
- ...base,
694
- targetThreshold: metadata.targetThreshold,
695
- signerCommitments: metadata.targetSignerCommitments,
696
- };
697
- case 'unknown':
698
- return base;
699
- }
700
- }
701
- fromPsmMetadata(psm) {
702
- if (!psm.proposalType)
703
- return undefined;
704
- const base = {
705
- description: psm.description ?? '',
706
- saltHex: psm.salt,
707
- };
708
- switch (psm.proposalType) {
709
- case 'p2id':
710
- return {
711
- ...base,
712
- proposalType: 'p2id',
713
- recipientId: psm.recipientId ?? '',
714
- faucetId: psm.faucetId ?? '',
715
- amount: psm.amount ?? '0',
716
- };
717
- case 'consume_notes':
718
- return {
719
- ...base,
720
- proposalType: 'consume_notes',
721
- noteIds: psm.noteIds ?? [],
722
- };
723
- case 'switch_psm':
724
- return {
725
- ...base,
726
- proposalType: 'switch_psm',
727
- newPsmPubkey: psm.newPsmPubkey ?? '',
728
- newPsmEndpoint: psm.newPsmEndpoint,
729
- targetThreshold: psm.targetThreshold,
730
- targetSignerCommitments: psm.signerCommitments,
731
- };
732
- case 'add_signer':
733
- case 'remove_signer':
734
- case 'change_threshold':
735
- return {
736
- ...base,
737
- proposalType: psm.proposalType,
738
- targetThreshold: psm.targetThreshold ?? 0,
739
- targetSignerCommitments: psm.signerCommitments ?? [],
740
- };
741
- default:
742
- return undefined;
743
- }
744
- }
745
- deltaStatusToProposalStatus(status, proposalType) {
746
- switch (status.status) {
747
- case 'pending': {
748
- const signaturesCollected = status.cosignerSigs.length;
749
- const signaturesRequired = proposalType
750
- ? this.getEffectiveThreshold(proposalType)
751
- : this.threshold;
752
- if (signaturesCollected >= signaturesRequired) {
753
- return { type: 'ready' };
754
- }
755
- return {
756
- type: 'pending',
757
- signaturesCollected,
758
- signaturesRequired,
759
- signers: status.cosignerSigs.map((s) => s.signerId),
760
- };
761
- }
762
- case 'candidate':
763
- return { type: 'ready' };
764
- case 'canonical':
765
- case 'discarded':
766
- return { type: 'finalized' };
767
- }
534
+ async createAndSyncFromRequest(request, metadata, nonce) {
535
+ const summary = await executeForSummary(this.webClient, this._accountId, request);
536
+ const summaryBase64 = uint8ArrayToBase64(summary.serialize());
537
+ const proposalNonce = nonce ?? Date.now();
538
+ const proposal = await this.createProposal(proposalNonce, summaryBase64, metadata);
539
+ return { proposal, proposals: this.listTransactionProposals() };
768
540
  }
769
541
  }
770
542
  //# sourceMappingURL=multisig.js.map