@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
@@ -2,10 +2,13 @@ import { beforeEach, describe, expect, it, vi } from 'vitest';
2
2
  import type { Word } from '@miden-sdk/miden-sdk';
3
3
 
4
4
  const {
5
+ mockFromVaultKey,
6
+ mockFungibleAssetConstructor,
5
7
  mockHashElements,
6
8
  mockNormalizeHexWord,
7
9
  mockRandomWord,
8
10
  mockWordFromHex,
11
+ noteMetadataCalls,
9
12
  saltFelts,
10
13
  } = vi.hoisted(() => {
11
14
  const saltFelts = [
@@ -16,6 +19,13 @@ const {
16
19
  ];
17
20
 
18
21
  return {
22
+ mockFromVaultKey: vi.fn((vaultKey: unknown, amount: bigint) => ({
23
+ kind: 'asset-from-vault-key',
24
+ vaultKey,
25
+ amount,
26
+ })),
27
+ mockFungibleAssetConstructor: vi.fn(),
28
+ noteMetadataCalls: [] as unknown[][],
19
29
  mockHashElements: vi.fn().mockReturnValue({ toString: () => 'serial' }),
20
30
  mockNormalizeHexWord: vi.fn((hex: string) => hex),
21
31
  mockRandomWord: vi.fn().mockReturnValue({
@@ -66,10 +76,12 @@ vi.mock('@miden-sdk/miden-sdk', () => {
66
76
 
67
77
  class NoteMetadata {
68
78
  constructor(
69
- _sender: unknown,
70
- _noteType: unknown,
71
- _noteTag: unknown,
72
- ) {}
79
+ sender: unknown,
80
+ noteType: unknown,
81
+ noteTag: unknown,
82
+ ) {
83
+ noteMetadataCalls.push([sender, noteType, noteTag]);
84
+ }
73
85
  }
74
86
 
75
87
  class NoteRecipient {
@@ -89,7 +101,11 @@ vi.mock('@miden-sdk/miden-sdk', () => {
89
101
  }
90
102
 
91
103
  class FungibleAsset {
92
- constructor(_faucet: unknown, _amount: bigint) {}
104
+ constructor(faucet: unknown, amount: bigint) {
105
+ mockFungibleAssetConstructor(faucet, amount);
106
+ }
107
+
108
+ static fromVaultKey = mockFromVaultKey;
93
109
  }
94
110
 
95
111
  class NoteArray {
@@ -120,6 +136,7 @@ vi.mock('@miden-sdk/miden-sdk', () => {
120
136
  hex,
121
137
  prefix: () => 1,
122
138
  suffix: () => 2,
139
+ toString: () => hex,
123
140
  })),
124
141
  },
125
142
  Felt,
@@ -140,7 +157,8 @@ vi.mock('@miden-sdk/miden-sdk', () => {
140
157
  withAccountTarget: vi.fn(() => ({ kind: 'tag' })),
141
158
  },
142
159
  NoteType: {
143
- Public: 'public',
160
+ Private: 0,
161
+ Public: 1,
144
162
  },
145
163
  OutputNote: {
146
164
  full: vi.fn((note: unknown) => ({ note })),
@@ -163,14 +181,32 @@ vi.mock('../utils/random.js', () => ({
163
181
  randomWord: mockRandomWord,
164
182
  }));
165
183
 
166
- import { buildP2idTransactionRequest } from './p2id.js';
184
+ import { buildP2idTransactionRequest, parseP2idNoteType, p2idNoteTypeToMetadata } from './p2id.js';
185
+ import type { Account } from '@miden-sdk/miden-sdk';
186
+
187
+ const FAUCET_ID = '0x7bfb0f38b0fafa103f86a805594171';
188
+
189
+ const mockAccount = {
190
+ vault: () => ({
191
+ fungibleAssets: () => [
192
+ {
193
+ faucetId: () => ({ toString: () => FAUCET_ID }),
194
+ vaultKey: () => ({ kind: 'vault-key' }),
195
+ },
196
+ ],
197
+ }),
198
+ } as unknown as Account;
199
+ import { NoteType } from '@miden-sdk/miden-sdk';
167
200
 
168
201
  describe('buildP2idTransactionRequest', () => {
169
202
  beforeEach(() => {
203
+ mockFromVaultKey.mockClear();
204
+ mockFungibleAssetConstructor.mockClear();
170
205
  mockHashElements.mockClear();
171
206
  mockNormalizeHexWord.mockClear();
172
207
  mockRandomWord.mockClear();
173
208
  mockWordFromHex.mockClear();
209
+ noteMetadataCalls.length = 0;
174
210
  });
175
211
 
176
212
  it('derives serial number from salt felts plus four zero felts', () => {
@@ -179,8 +215,9 @@ describe('buildP2idTransactionRequest', () => {
179
215
  buildP2idTransactionRequest(
180
216
  '0x7bfb0f38b0fafa103f86a805594170',
181
217
  '0x8a65fc5a39e4cd106d648e3eb4ab5f',
182
- '0x7bfb0f38b0fafa103f86a805594171',
218
+ FAUCET_ID,
183
219
  10n,
220
+ mockAccount,
184
221
  { salt },
185
222
  );
186
223
 
@@ -197,4 +234,57 @@ describe('buildP2idTransactionRequest', () => {
197
234
  expect((felt as { value: bigint }).value).toBe(0n);
198
235
  }
199
236
  });
237
+
238
+ it('creates a public note by default (issue #322)', () => {
239
+ buildP2idTransactionRequest(
240
+ '0x7bfb0f38b0fafa103f86a805594170',
241
+ '0x8a65fc5a39e4cd106d648e3eb4ab5f',
242
+ '0x7bfb0f38b0fafa103f86a805594171',
243
+ 10n,
244
+ mockAccount,
245
+ );
246
+
247
+ expect(noteMetadataCalls).toHaveLength(1);
248
+ expect(noteMetadataCalls[0][1]).toBe(NoteType.Public);
249
+ });
250
+
251
+ it('threads the requested noteType into the note metadata (issue #322)', () => {
252
+ buildP2idTransactionRequest(
253
+ '0x7bfb0f38b0fafa103f86a805594170',
254
+ '0x8a65fc5a39e4cd106d648e3eb4ab5f',
255
+ '0x7bfb0f38b0fafa103f86a805594171',
256
+ 10n,
257
+ mockAccount,
258
+ { noteType: NoteType.Private },
259
+ );
260
+
261
+ expect(noteMetadataCalls).toHaveLength(1);
262
+ expect(noteMetadataCalls[0][1]).toBe(NoteType.Private);
263
+ });
264
+ });
265
+
266
+ describe('parseP2idNoteType', () => {
267
+ it('maps absent to Public (pre-#322 proposals)', () => {
268
+ expect(parseP2idNoteType(undefined)).toBe(NoteType.Public);
269
+ });
270
+
271
+ it('maps wire values to note types', () => {
272
+ expect(parseP2idNoteType('public')).toBe(NoteType.Public);
273
+ expect(parseP2idNoteType('private')).toBe(NoteType.Private);
274
+ });
275
+
276
+ it('rejects unknown values instead of silently rebuilding a public note', () => {
277
+ expect(() => parseP2idNoteType('encrypted')).toThrow(/unsupported metadata.noteType/);
278
+ });
279
+ });
280
+
281
+ describe('p2idNoteTypeToMetadata', () => {
282
+ it('omits the default so public payloads keep the legacy wire shape', () => {
283
+ expect(p2idNoteTypeToMetadata(undefined)).toBeUndefined();
284
+ expect(p2idNoteTypeToMetadata(NoteType.Public)).toBeUndefined();
285
+ });
286
+
287
+ it('serializes private', () => {
288
+ expect(p2idNoteTypeToMetadata(NoteType.Private)).toBe('private');
289
+ });
200
290
  });
@@ -1,4 +1,4 @@
1
- import type { TransactionRequest, Word } from '@miden-sdk/miden-sdk';
1
+ import type { Account, TransactionRequest, Word } from '@miden-sdk/miden-sdk';
2
2
  import {
3
3
  AccountId,
4
4
  FeltArray,
@@ -19,6 +19,38 @@ import {
19
19
  import { randomWord } from '../utils/random.js';
20
20
  import { normalizeHexWord } from '../utils/encoding.js';
21
21
  import type { SignatureOptions } from './options.js';
22
+ import type { P2idNoteVisibility } from '../types/proposal.js';
23
+
24
+ export interface P2idTransactionOptions extends SignatureOptions {
25
+ /** Visibility of the created note. Defaults to `NoteType.Public`. */
26
+ noteType?: NoteType;
27
+ }
28
+
29
+ /**
30
+ * Parses the metadata wire value for a P2ID note visibility (issue #322).
31
+ * Absent => public, the only behavior before the field existed. An unknown
32
+ * value is rejected rather than silently rebuilt as a public note that could
33
+ * never match the signed tx_summary commitment.
34
+ */
35
+ export function parseP2idNoteType(value: string | undefined): NoteType {
36
+ switch (value) {
37
+ case undefined:
38
+ case 'public':
39
+ return NoteType.Public;
40
+ case 'private':
41
+ return NoteType.Private;
42
+ default:
43
+ throw new Error(`unsupported metadata.noteType '${value}': expected 'public' or 'private'`);
44
+ }
45
+ }
46
+
47
+ /**
48
+ * Maps a note type to its metadata wire value, omitting the default so
49
+ * public-note payloads keep the pre-#322 wire shape.
50
+ */
51
+ export function p2idNoteTypeToMetadata(noteType: NoteType | undefined): P2idNoteVisibility | undefined {
52
+ return noteType === NoteType.Private ? 'private' : undefined;
53
+ }
22
54
 
23
55
  export function deriveP2idSerialNumber(salt: Word): Word {
24
56
  const zeroWord = WordType.fromHex(`0x${'00'.repeat(32)}`);
@@ -28,6 +60,32 @@ export function deriveP2idSerialNumber(salt: Word): Word {
28
60
  ]));
29
61
  }
30
62
 
63
+ /**
64
+ * Builds the fungible asset to transfer, sourcing the callback flag from the held asset.
65
+ *
66
+ * In Miden 0.15 the callback flag is part of the vault key, so rebuilding the asset from
67
+ * `faucet`/`amount` with the default flag would not match the held asset and the transfer
68
+ * would abort. When the faucet is absent the default flag is used, surfacing the
69
+ * missing-asset error during execution.
70
+ */
71
+ function resolveFungibleAssetFromVault(
72
+ account: Account,
73
+ faucet: AccountId,
74
+ amount: bigint,
75
+ ): FungibleAsset {
76
+ const faucetHex = faucet.toString();
77
+ const assetFromVault = account
78
+ .vault()
79
+ .fungibleAssets()
80
+ .find(asset => asset.faucetId().toString() === faucetHex);
81
+
82
+ if (!assetFromVault) {
83
+ return new FungibleAsset(faucet, amount);
84
+ }
85
+
86
+ return FungibleAsset.fromVaultKey(assetFromVault.vaultKey(), amount);
87
+ }
88
+
31
89
  function buildP2idNote(
32
90
  sender: AccountId,
33
91
  recipient: AccountId,
@@ -61,7 +119,8 @@ export function buildP2idTransactionRequest(
61
119
  recipientId: string,
62
120
  faucetId: string,
63
121
  amount: bigint,
64
- options: SignatureOptions = {},
122
+ account: Account,
123
+ options: P2idTransactionOptions = {},
65
124
  ): { request: TransactionRequest; salt: Word } {
66
125
  const sender = AccountId.fromHex(senderId);
67
126
  const recipient = AccountId.fromHex(recipientId);
@@ -69,14 +128,15 @@ export function buildP2idTransactionRequest(
69
128
 
70
129
  const authSaltHex = options.salt ? options.salt.toHex() : randomWord().toHex();
71
130
 
72
- const asset = new FungibleAsset(faucet, amount);
131
+ const asset = resolveFungibleAssetFromVault(account, faucet, amount);
132
+
73
133
  const noteAssets = new NoteAssets([asset]);
74
134
 
75
135
  const note = buildP2idNote(
76
136
  sender,
77
137
  recipient,
78
138
  noteAssets,
79
- NoteType.Public,
139
+ options.noteType ?? NoteType.Public,
80
140
  authSaltHex,
81
141
  );
82
142
 
@@ -7,6 +7,18 @@ import type {
7
7
  import { AccountId } from '@miden-sdk/miden-sdk';
8
8
  import { getRawMidenClient } from '../raw-client.js';
9
9
 
10
+ export function executeForSummary(
11
+ client: MidenClient,
12
+ accountId: string,
13
+ txRequest: TransactionRequest,
14
+ midenRpcEndpoint: string,
15
+ ): Promise<TransactionSummary>;
16
+ export function executeForSummary(
17
+ client: WasmWebClient,
18
+ accountId: string,
19
+ txRequest: TransactionRequest,
20
+ midenRpcEndpoint?: string,
21
+ ): Promise<TransactionSummary>;
10
22
  export async function executeForSummary(
11
23
  client: MidenClient | WasmWebClient,
12
24
  accountId: string,
@@ -13,7 +13,7 @@ import { GUARDIAN_ECDSA_MASM, GUARDIAN_MASM } from '../account/masm/auth.js';
13
13
  import { compileTxScript } from '../raw-client.js';
14
14
  import { normalizeHexWord } from '../utils/encoding.js';
15
15
  import { randomWord } from '../utils/random.js';
16
- import type { SignatureOptions } from './options.js';
16
+ import type { MidenClientSignatureOptions, SignatureOptions } from './options.js';
17
17
  import type { SignatureScheme } from '../types.js';
18
18
 
19
19
  async function buildUpdateGuardianScript(
@@ -42,6 +42,16 @@ end
42
42
  );
43
43
  }
44
44
 
45
+ export function buildUpdateGuardianTransactionRequest(
46
+ client: MidenClient,
47
+ newGuardianPubkey: string,
48
+ options: MidenClientSignatureOptions,
49
+ ): Promise<{ request: TransactionRequest; salt: Word }>;
50
+ export function buildUpdateGuardianTransactionRequest(
51
+ client: WasmWebClient,
52
+ newGuardianPubkey: string,
53
+ options?: SignatureOptions,
54
+ ): Promise<{ request: TransactionRequest; salt: Word }>;
45
55
  export async function buildUpdateGuardianTransactionRequest(
46
56
  client: MidenClient | WasmWebClient,
47
57
  newGuardianPubkey: string,
@@ -18,7 +18,7 @@ import { getProcedureRoot, type ProcedureName } from '../procedures.js';
18
18
  import { compileTxScript } from '../raw-client.js';
19
19
  import { normalizeHexWord } from '../utils/encoding.js';
20
20
  import { randomWord } from '../utils/random.js';
21
- import type { SignatureOptions } from './options.js';
21
+ import type { MidenClientSignatureOptions, SignatureOptions } from './options.js';
22
22
  import type { SignatureScheme } from '../types.js';
23
23
 
24
24
  function buildProcedureThresholdFelts(procedure: ProcedureName, threshold: number): Felt[] {
@@ -76,6 +76,18 @@ end
76
76
  );
77
77
  }
78
78
 
79
+ export function buildUpdateProcedureThresholdTransactionRequest(
80
+ client: MidenClient,
81
+ procedure: ProcedureName,
82
+ threshold: number,
83
+ options: MidenClientSignatureOptions,
84
+ ): Promise<{ request: TransactionRequest; salt: Word; configHash: Word }>;
85
+ export function buildUpdateProcedureThresholdTransactionRequest(
86
+ client: WasmWebClient,
87
+ procedure: ProcedureName,
88
+ threshold: number,
89
+ options?: SignatureOptions,
90
+ ): Promise<{ request: TransactionRequest; salt: Word; configHash: Word }>;
79
91
  export async function buildUpdateProcedureThresholdTransactionRequest(
80
92
  client: MidenClient | WasmWebClient,
81
93
  procedure: ProcedureName,
@@ -18,7 +18,7 @@ import {
18
18
  import { compileTxScript } from '../raw-client.js';
19
19
  import { normalizeHexWord } from '../utils/encoding.js';
20
20
  import { randomWord } from '../utils/random.js';
21
- import type { SignatureOptions } from './options.js';
21
+ import type { MidenClientSignatureOptions, SignatureOptions } from './options.js';
22
22
  import type { SignatureScheme } from '../types.js';
23
23
 
24
24
  function buildMultisigConfigFelts(threshold: number, signerCommitments: string[]): Felt[] {
@@ -73,6 +73,18 @@ end
73
73
  );
74
74
  }
75
75
 
76
+ export function buildUpdateSignersTransactionRequest(
77
+ client: MidenClient,
78
+ threshold: number,
79
+ signerCommitments: string[],
80
+ options: MidenClientSignatureOptions,
81
+ ): Promise<{ request: TransactionRequest; salt: Word; configHash: Word }>;
82
+ export function buildUpdateSignersTransactionRequest(
83
+ client: WasmWebClient,
84
+ threshold: number,
85
+ signerCommitments: string[],
86
+ options?: SignatureOptions,
87
+ ): Promise<{ request: TransactionRequest; salt: Word; configHash: Word }>;
76
88
  export async function buildUpdateSignersTransactionRequest(
77
89
  client: MidenClient | WasmWebClient,
78
90
  threshold: number,
@@ -4,6 +4,9 @@ export {
4
4
  export { executeForSummary } from './transaction/summary.js';
5
5
  export {
6
6
  buildP2idTransactionRequest,
7
+ parseP2idNoteType,
8
+ p2idNoteTypeToMetadata,
9
+ type P2idTransactionOptions,
7
10
  } from './transaction/p2id.js';
8
11
  export {
9
12
  buildUpdateGuardianTransactionRequest,
@@ -82,11 +82,20 @@ export function isConsumeNotesV1(md: ConsumeNotesProposalMetadata): boolean {
82
82
  return md.metadataVersion === undefined || md.metadataVersion === 1;
83
83
  }
84
84
 
85
+ /** Wire values for a P2ID note's visibility (issue #322). */
86
+ export type P2idNoteVisibility = 'public' | 'private';
87
+
88
+ export function isP2idNoteVisibility(value: string): value is P2idNoteVisibility {
89
+ return value === 'public' || value === 'private';
90
+ }
91
+
85
92
  export interface P2IdProposalMetadata extends BaseProposalMetadata {
86
93
  proposalType: 'p2id';
87
94
  recipientId: string;
88
95
  faucetId: string;
89
96
  amount: string;
97
+ /** Visibility of the created note. Absent on the wire => 'public' (pre-#322 proposals). */
98
+ noteType?: P2idNoteVisibility;
90
99
  }
91
100
 
92
101
  export interface CustomProposalMetadata extends BaseProposalMetadata {