@openzeppelin/miden-multisig-client 0.12.6

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 (215) hide show
  1. package/README.md +189 -0
  2. package/dist/account/builder.d.ts +23 -0
  3. package/dist/account/builder.d.ts.map +1 -0
  4. package/dist/account/builder.js +85 -0
  5. package/dist/account/builder.js.map +1 -0
  6. package/dist/account/index.d.ts +6 -0
  7. package/dist/account/index.d.ts.map +1 -0
  8. package/dist/account/index.js +6 -0
  9. package/dist/account/index.js.map +1 -0
  10. package/dist/account/masm.d.ts +3 -0
  11. package/dist/account/masm.d.ts.map +1 -0
  12. package/dist/account/masm.js +605 -0
  13. package/dist/account/masm.js.map +1 -0
  14. package/dist/account/storage.d.ts +10 -0
  15. package/dist/account/storage.d.ts.map +1 -0
  16. package/dist/account/storage.js +56 -0
  17. package/dist/account/storage.js.map +1 -0
  18. package/dist/account.d.ts +104 -0
  19. package/dist/account.d.ts.map +1 -0
  20. package/dist/account.js +136 -0
  21. package/dist/account.js.map +1 -0
  22. package/dist/builder.d.ts +54 -0
  23. package/dist/builder.d.ts.map +1 -0
  24. package/dist/builder.js +78 -0
  25. package/dist/builder.js.map +1 -0
  26. package/dist/client.d.ts +73 -0
  27. package/dist/client.d.ts.map +1 -0
  28. package/dist/client.js +104 -0
  29. package/dist/client.js.map +1 -0
  30. package/dist/client.test.d.ts +2 -0
  31. package/dist/client.test.d.ts.map +1 -0
  32. package/dist/client.test.js +150 -0
  33. package/dist/client.test.js.map +1 -0
  34. package/dist/http.d.ts +64 -0
  35. package/dist/http.d.ts.map +1 -0
  36. package/dist/http.js +162 -0
  37. package/dist/http.js.map +1 -0
  38. package/dist/index.d.ts +53 -0
  39. package/dist/index.d.ts.map +1 -0
  40. package/dist/index.js +52 -0
  41. package/dist/index.js.map +1 -0
  42. package/dist/inspector.d.ts +49 -0
  43. package/dist/inspector.d.ts.map +1 -0
  44. package/dist/inspector.js +120 -0
  45. package/dist/inspector.js.map +1 -0
  46. package/dist/inspector.test.d.ts +2 -0
  47. package/dist/inspector.test.d.ts.map +1 -0
  48. package/dist/inspector.test.js +205 -0
  49. package/dist/inspector.test.js.map +1 -0
  50. package/dist/keystore.d.ts +65 -0
  51. package/dist/keystore.d.ts.map +1 -0
  52. package/dist/keystore.js +121 -0
  53. package/dist/keystore.js.map +1 -0
  54. package/dist/miden.d.ts +9 -0
  55. package/dist/miden.d.ts.map +1 -0
  56. package/dist/miden.js +26 -0
  57. package/dist/miden.js.map +1 -0
  58. package/dist/multisig/helpers.d.ts +4 -0
  59. package/dist/multisig/helpers.d.ts.map +1 -0
  60. package/dist/multisig/helpers.js +21 -0
  61. package/dist/multisig/helpers.js.map +1 -0
  62. package/dist/multisig/helpers.test.d.ts +2 -0
  63. package/dist/multisig/helpers.test.d.ts.map +1 -0
  64. package/dist/multisig/helpers.test.js +94 -0
  65. package/dist/multisig/helpers.test.js.map +1 -0
  66. package/dist/multisig/metadata.d.ts +18 -0
  67. package/dist/multisig/metadata.d.ts.map +1 -0
  68. package/dist/multisig/metadata.js +100 -0
  69. package/dist/multisig/metadata.js.map +1 -0
  70. package/dist/multisig/metadata.test.d.ts +2 -0
  71. package/dist/multisig/metadata.test.d.ts.map +1 -0
  72. package/dist/multisig/metadata.test.js +323 -0
  73. package/dist/multisig/metadata.test.js.map +1 -0
  74. package/dist/multisig.d.ts +199 -0
  75. package/dist/multisig.d.ts.map +1 -0
  76. package/dist/multisig.js +821 -0
  77. package/dist/multisig.js.map +1 -0
  78. package/dist/multisig.test.d.ts +2 -0
  79. package/dist/multisig.test.d.ts.map +1 -0
  80. package/dist/multisig.test.js +1121 -0
  81. package/dist/multisig.test.js.map +1 -0
  82. package/dist/procedures.d.ts +57 -0
  83. package/dist/procedures.d.ts.map +1 -0
  84. package/dist/procedures.js +62 -0
  85. package/dist/procedures.js.map +1 -0
  86. package/dist/signer.d.ts +36 -0
  87. package/dist/signer.d.ts.map +1 -0
  88. package/dist/signer.js +68 -0
  89. package/dist/signer.js.map +1 -0
  90. package/dist/signer.test.d.ts +2 -0
  91. package/dist/signer.test.d.ts.map +1 -0
  92. package/dist/signer.test.js +117 -0
  93. package/dist/signer.test.js.map +1 -0
  94. package/dist/transaction/consumeNotes.d.ts +7 -0
  95. package/dist/transaction/consumeNotes.d.ts.map +1 -0
  96. package/dist/transaction/consumeNotes.js +28 -0
  97. package/dist/transaction/consumeNotes.js.map +1 -0
  98. package/dist/transaction/index.d.ts +7 -0
  99. package/dist/transaction/index.d.ts.map +1 -0
  100. package/dist/transaction/index.js +6 -0
  101. package/dist/transaction/index.js.map +1 -0
  102. package/dist/transaction/options.d.ts +6 -0
  103. package/dist/transaction/options.d.ts.map +1 -0
  104. package/dist/transaction/options.js +2 -0
  105. package/dist/transaction/options.js.map +1 -0
  106. package/dist/transaction/p2id.d.ts +7 -0
  107. package/dist/transaction/p2id.d.ts.map +1 -0
  108. package/dist/transaction/p2id.js +43 -0
  109. package/dist/transaction/p2id.js.map +1 -0
  110. package/dist/transaction/summary.d.ts +3 -0
  111. package/dist/transaction/summary.d.ts.map +1 -0
  112. package/dist/transaction/summary.js +6 -0
  113. package/dist/transaction/summary.js.map +1 -0
  114. package/dist/transaction/updatePsm.d.ts +7 -0
  115. package/dist/transaction/updatePsm.d.ts.map +1 -0
  116. package/dist/transaction/updatePsm.js +43 -0
  117. package/dist/transaction/updatePsm.js.map +1 -0
  118. package/dist/transaction/updateSigners.d.ts +8 -0
  119. package/dist/transaction/updateSigners.d.ts.map +1 -0
  120. package/dist/transaction/updateSigners.js +60 -0
  121. package/dist/transaction/updateSigners.js.map +1 -0
  122. package/dist/transaction.d.ts +6 -0
  123. package/dist/transaction.d.ts.map +1 -0
  124. package/dist/transaction.js +8 -0
  125. package/dist/transaction.js.map +1 -0
  126. package/dist/transaction.test.d.ts +2 -0
  127. package/dist/transaction.test.d.ts.map +1 -0
  128. package/dist/transaction.test.js +117 -0
  129. package/dist/transaction.test.js.map +1 -0
  130. package/dist/transport/http.d.ts +64 -0
  131. package/dist/transport/http.d.ts.map +1 -0
  132. package/dist/transport/http.js +162 -0
  133. package/dist/transport/http.js.map +1 -0
  134. package/dist/transport/index.d.ts +5 -0
  135. package/dist/transport/index.d.ts.map +1 -0
  136. package/dist/transport/index.js +5 -0
  137. package/dist/transport/index.js.map +1 -0
  138. package/dist/types/proposal.d.ts +74 -0
  139. package/dist/types/proposal.d.ts.map +1 -0
  140. package/dist/types/proposal.js +2 -0
  141. package/dist/types/proposal.js.map +1 -0
  142. package/dist/types.d.ts +60 -0
  143. package/dist/types.d.ts.map +1 -0
  144. package/dist/types.js +2 -0
  145. package/dist/types.js.map +1 -0
  146. package/dist/utils/encoding.d.ts +7 -0
  147. package/dist/utils/encoding.d.ts.map +1 -0
  148. package/dist/utils/encoding.js +54 -0
  149. package/dist/utils/encoding.js.map +1 -0
  150. package/dist/utils/encoding.test.d.ts +2 -0
  151. package/dist/utils/encoding.test.d.ts.map +1 -0
  152. package/dist/utils/encoding.test.js +185 -0
  153. package/dist/utils/encoding.test.js.map +1 -0
  154. package/dist/utils/index.d.ts +5 -0
  155. package/dist/utils/index.d.ts.map +1 -0
  156. package/dist/utils/index.js +5 -0
  157. package/dist/utils/index.js.map +1 -0
  158. package/dist/utils/random.d.ts +3 -0
  159. package/dist/utils/random.d.ts.map +1 -0
  160. package/dist/utils/random.js +14 -0
  161. package/dist/utils/random.js.map +1 -0
  162. package/dist/utils/signature.d.ts +12 -0
  163. package/dist/utils/signature.d.ts.map +1 -0
  164. package/dist/utils/signature.js +28 -0
  165. package/dist/utils/signature.js.map +1 -0
  166. package/dist/utils/signature.test.d.ts +2 -0
  167. package/dist/utils/signature.test.d.ts.map +1 -0
  168. package/dist/utils/signature.test.js +149 -0
  169. package/dist/utils/signature.test.js.map +1 -0
  170. package/dist/utils/word.d.ts +4 -0
  171. package/dist/utils/word.d.ts.map +1 -0
  172. package/dist/utils/word.js +8 -0
  173. package/dist/utils/word.js.map +1 -0
  174. package/dist/utils/word.test.d.ts +2 -0
  175. package/dist/utils/word.test.d.ts.map +1 -0
  176. package/dist/utils/word.test.js +54 -0
  177. package/dist/utils/word.test.js.map +1 -0
  178. package/masm/multisig.masm +423 -0
  179. package/masm/psm.masm +178 -0
  180. package/package.json +42 -0
  181. package/src/account/builder.ts +112 -0
  182. package/src/account/index.ts +15 -0
  183. package/src/account/masm.ts +606 -0
  184. package/src/account/storage.ts +70 -0
  185. package/src/client.test.ts +177 -0
  186. package/src/client.ts +129 -0
  187. package/src/index.ts +118 -0
  188. package/src/inspector.test.ts +237 -0
  189. package/src/inspector.ts +142 -0
  190. package/src/multisig/helpers.test.ts +114 -0
  191. package/src/multisig/helpers.ts +23 -0
  192. package/src/multisig.test.ts +1294 -0
  193. package/src/multisig.ts +1097 -0
  194. package/src/procedures.ts +72 -0
  195. package/src/signer.test.ts +135 -0
  196. package/src/signer.ts +75 -0
  197. package/src/transaction/consumeNotes.ts +41 -0
  198. package/src/transaction/index.ts +7 -0
  199. package/src/transaction/options.ts +7 -0
  200. package/src/transaction/p2id.ts +105 -0
  201. package/src/transaction/summary.ts +12 -0
  202. package/src/transaction/updatePsm.ts +69 -0
  203. package/src/transaction/updateSigners.ts +96 -0
  204. package/src/transaction.test.ts +136 -0
  205. package/src/transaction.ts +16 -0
  206. package/src/types/proposal.ts +80 -0
  207. package/src/types.ts +81 -0
  208. package/src/utils/encoding.test.ts +233 -0
  209. package/src/utils/encoding.ts +60 -0
  210. package/src/utils/index.ts +5 -0
  211. package/src/utils/random.ts +15 -0
  212. package/src/utils/signature.test.ts +187 -0
  213. package/src/utils/signature.ts +36 -0
  214. package/src/utils/word.test.ts +64 -0
  215. package/src/utils/word.ts +11 -0
@@ -0,0 +1,72 @@
1
+ /**
2
+ * known procedure roots for multisig accounts.
3
+ *
4
+ * IMPORTANT: These values are extracted from the TypeScript SDK's compiled MASM output,
5
+ * NOT from the Rust crate. The SDK may compile MASM differently, resulting in different
6
+ * procedure roots. Use the debug output from builder.ts to update these values.
7
+ */
8
+
9
+ /**
10
+ * Static mapping of procedure names to their deterministic roots.
11
+ *
12
+ * Component ordering: Multisig (auth) -> PSM -> BasicWallet
13
+ */
14
+ export const PROCEDURE_ROOTS = {
15
+ // Multisig component procedures
16
+ /** Update signer list and threshold configuration */
17
+ update_signers: '0x53d0ad381a193de0cf6af3730141e498274103dfc3b8c8e7367bd49d4a66c72b',
18
+ /** Authenticate transaction with multisig (Falcon512) */
19
+ auth_tx: '0x474c613b38001cc36d68557e9d881495d6a461a9027033445c7672c586509026',
20
+
21
+ // PSM component procedures
22
+ /** Update PSM public key */
23
+ update_psm: '0xb103236807e5bf09c27efc2c5287ca8b03ab9efc1d852b62ebd31f5f1927ec26',
24
+ /** Verify PSM signature */
25
+ verify_psm: '0x30727fc23c6105a678fea8b4c1920f35fa85c03f16a0cf98372c8f56701f8a87',
26
+
27
+ // BasicWallet procedures
28
+ /** Send assets from account (move_asset_to_note) */
29
+ send_asset: '0x0e406b067ed2bcd7de745ca6517f519fd1a9be245f913347ac673ca1db30c1d6',
30
+ /** Receive assets into account */
31
+ receive_asset: '0x6f4bdbdc4b13d7ed933d590d88ac9dfb98020c9e917697845b5e169395b76a01',
32
+ } as const;
33
+
34
+ /**
35
+ * Valid procedure names that can be used for threshold overrides.
36
+ */
37
+ export type ProcedureName = keyof typeof PROCEDURE_ROOTS;
38
+
39
+ /**
40
+ * Get the procedure root for a given procedure name.
41
+ *
42
+ * @param name - The procedure name
43
+ * @returns The procedure root as a hex string
44
+ *
45
+ * @example
46
+ * ```typescript
47
+ * const root = getProcedureRoot('receive_asset');
48
+ * // '0x6f4bdbdc4b13d7ed933d590d88ac9dfb98020c9e917697845b5e169395b76a01'
49
+ * ```
50
+ */
51
+ export function getProcedureRoot(name: ProcedureName): string {
52
+ return PROCEDURE_ROOTS[name];
53
+ }
54
+
55
+ /**
56
+ * Check if a string is a valid procedure name.
57
+ *
58
+ * @param name - The string to check
59
+ * @returns true if the string is a valid procedure name
60
+ */
61
+ export function isProcedureName(name: string): name is ProcedureName {
62
+ return name in PROCEDURE_ROOTS;
63
+ }
64
+
65
+ /**
66
+ * Get all available procedure names.
67
+ *
68
+ * @returns Array of all valid procedure names
69
+ */
70
+ export function getProcedureNames(): ProcedureName[] {
71
+ return Object.keys(PROCEDURE_ROOTS) as ProcedureName[];
72
+ }
@@ -0,0 +1,135 @@
1
+ import { describe, it, expect, vi, beforeEach } from 'vitest';
2
+ import { FalconSigner } from './signer.js';
3
+
4
+ // Mock the Miden SDK
5
+ vi.mock('@demox-labs/miden-sdk', () => {
6
+ const mockSignature = {
7
+ serialize: () => new Uint8Array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]),
8
+ };
9
+
10
+ const mockPublicKey = {
11
+ toCommitment: () => ({
12
+ toHex: () => '0x' + 'a'.repeat(64),
13
+ }),
14
+ serialize: () => new Uint8Array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]),
15
+ };
16
+
17
+ const mockSecretKey = {
18
+ publicKey: vi.fn().mockReturnValue(mockPublicKey),
19
+ sign: vi.fn().mockReturnValue(mockSignature),
20
+ };
21
+
22
+ return {
23
+ SecretKey: {
24
+ rpoFalconWithRNG: vi.fn().mockReturnValue(mockSecretKey),
25
+ },
26
+ Word: {
27
+ fromHex: vi.fn((hex: string) => ({
28
+ toHex: () => hex,
29
+ toFelts: () => [1, 2, 3, 4],
30
+ })),
31
+ },
32
+ AccountId: {
33
+ fromHex: vi.fn((hex: string) => ({
34
+ prefix: () => ({ asInt: () => BigInt(1) }),
35
+ suffix: () => ({ asInt: () => BigInt(2) }),
36
+ })),
37
+ },
38
+ Felt: vi.fn().mockImplementation((v: bigint) => ({ value: v })),
39
+ FeltArray: vi.fn().mockImplementation((arr: any[]) => arr),
40
+ Rpo256: {
41
+ hashElements: vi.fn().mockReturnValue({
42
+ toHex: () => '0x' + 'b'.repeat(64),
43
+ }),
44
+ },
45
+ };
46
+ });
47
+
48
+ describe('FalconSigner', () => {
49
+ let mockSecretKey: any;
50
+ let signer: FalconSigner;
51
+
52
+ beforeEach(async () => {
53
+ vi.clearAllMocks();
54
+ const { SecretKey } = await import('@demox-labs/miden-sdk');
55
+ mockSecretKey = SecretKey.rpoFalconWithRNG(new Uint8Array(32));
56
+ signer = new FalconSigner(mockSecretKey);
57
+ });
58
+
59
+ describe('constructor', () => {
60
+ it('extracts commitment from public key', () => {
61
+ expect(signer.commitment).toBe('0x' + 'a'.repeat(64));
62
+ });
63
+
64
+ it('extracts public key without first byte', () => {
65
+ // Public key serialized is [0, 1, 2, ...15], slice(1) removes first byte
66
+ expect(signer.publicKey).toBe('0x0102030405060708090a0b0c0d0e0f');
67
+ });
68
+
69
+ it('calls publicKey method on secret key', () => {
70
+ expect(mockSecretKey.publicKey).toHaveBeenCalled();
71
+ });
72
+ });
73
+
74
+ describe('signAccountIdWithTimestamp', () => {
75
+ it('handles account ID with 0x prefix', () => {
76
+ const signature = signer.signAccountIdWithTimestamp('0x' + 'a'.repeat(30), 1700000000);
77
+ expect(signature).toMatch(/^0x[a-f0-9]+$/);
78
+ });
79
+
80
+ it('handles account ID without 0x prefix', () => {
81
+ const signature = signer.signAccountIdWithTimestamp('a'.repeat(30), 1700000000);
82
+ expect(signature).toMatch(/^0x[a-f0-9]+$/);
83
+ });
84
+
85
+ it('calls sign method with hashed digest', () => {
86
+ signer.signAccountIdWithTimestamp('0x' + 'a'.repeat(30), 1700000000);
87
+ expect(mockSecretKey.sign).toHaveBeenCalled();
88
+ });
89
+
90
+ it('returns signature without first byte', () => {
91
+ // Signature serialized is [0, 1, 2, ...9], slice(1) returns [1, 2, ...9]
92
+ const signature = signer.signAccountIdWithTimestamp('0x' + 'a'.repeat(30), 1700000000);
93
+ expect(signature).toBe('0x010203040506070809');
94
+ });
95
+
96
+ it('includes timestamp in signed payload', async () => {
97
+ const { Felt, FeltArray } = await import('@demox-labs/miden-sdk');
98
+ signer.signAccountIdWithTimestamp('0x' + 'a'.repeat(30), 1700000000);
99
+ // Verify FeltArray was called with timestamp in the third position
100
+ expect(FeltArray).toHaveBeenCalledWith(expect.arrayContaining([
101
+ expect.anything(), // prefix
102
+ expect.anything(), // suffix
103
+ expect.anything(), // timestamp
104
+ expect.anything(), // zero padding
105
+ ]));
106
+ });
107
+ });
108
+
109
+ describe('signCommitment', () => {
110
+ it('handles commitment with 0x prefix', () => {
111
+ const signature = signer.signCommitment('0x' + 'c'.repeat(64));
112
+ expect(signature).toMatch(/^0x[a-f0-9]+$/);
113
+ });
114
+
115
+ it('handles commitment without 0x prefix', () => {
116
+ const signature = signer.signCommitment('c'.repeat(64));
117
+ expect(signature).toMatch(/^0x[a-f0-9]+$/);
118
+ });
119
+
120
+ it('pads short commitment hex to 64 characters', () => {
121
+ const signature = signer.signCommitment('abc');
122
+ expect(signature).toMatch(/^0x[a-f0-9]+$/);
123
+ });
124
+
125
+ it('calls sign method with word from hex', () => {
126
+ signer.signCommitment('0x' + 'c'.repeat(64));
127
+ expect(mockSecretKey.sign).toHaveBeenCalled();
128
+ });
129
+
130
+ it('returns signature without first byte', () => {
131
+ const signature = signer.signCommitment('0x' + 'c'.repeat(64));
132
+ expect(signature).toBe('0x010203040506070809');
133
+ });
134
+ });
135
+ });
package/src/signer.ts ADDED
@@ -0,0 +1,75 @@
1
+ /**
2
+ * Falcon Signer implementation for PSM client authentication.
3
+ *
4
+ * This implements the Signer interface using the Miden SDK's SecretKey
5
+ * for Falcon signatures.
6
+ */
7
+
8
+ import { SecretKey, Word, AccountId, Felt, FeltArray, Rpo256 } from '@demox-labs/miden-sdk';
9
+ import type { Signer } from './types.js';
10
+ import { bytesToHex } from './utils/encoding.js';
11
+
12
+ /**
13
+ * A Falcon signer that implements the Signer interface.
14
+ *
15
+ * @example
16
+ * ```typescript
17
+ * const secretKey = SecretKey.rpoFalconWithRNG(seed);
18
+ * const signer = new FalconSigner(secretKey);
19
+ * console.log(signer.commitment);
20
+ * ```
21
+ */
22
+ export class FalconSigner implements Signer {
23
+ readonly commitment: string;
24
+ readonly publicKey: string;
25
+ private readonly secretKey: SecretKey;
26
+
27
+ constructor(secretKey: SecretKey) {
28
+ this.secretKey = secretKey;
29
+ const pubKey = secretKey.publicKey();
30
+ this.commitment = pubKey.toCommitment().toHex();
31
+ const serialized = pubKey.serialize();
32
+ const falconPubKey = serialized.slice(1);
33
+ this.publicKey = bytesToHex(falconPubKey);
34
+ }
35
+
36
+ /**
37
+ * Signs an account ID with a timestamp.
38
+ * @param accountId - The account ID to sign
39
+ * @param timestamp - Unix timestamp in milliseconds (use Date.now())
40
+ * @returns Hex-encoded Falcon signature
41
+ */
42
+ signAccountIdWithTimestamp(accountId: string, timestamp: number): string {
43
+ // Parse the account ID from hex
44
+ const paddedHex = accountId.startsWith('0x') ? accountId : `0x${accountId}`;
45
+ const parsedAccountId = AccountId.fromHex(paddedHex);
46
+ const prefix = parsedAccountId.prefix();
47
+ const suffix = parsedAccountId.suffix();
48
+
49
+ const feltArray = new FeltArray([
50
+ prefix,
51
+ suffix,
52
+ new Felt(BigInt(timestamp)),
53
+ new Felt(BigInt(0)),
54
+ ]);
55
+
56
+ const digest = Rpo256.hashElements(feltArray);
57
+ const signature = this.secretKey.sign(digest);
58
+ const signatureBytes = signature.serialize();
59
+ const falconSignature = signatureBytes.slice(1);
60
+ return bytesToHex(falconSignature);
61
+ }
62
+
63
+ /**
64
+ * Signs a commitment/word for proposal signing.
65
+ */
66
+ signCommitment(commitmentHex: string): string {
67
+ const paddedHex = commitmentHex.startsWith('0x') ? commitmentHex : `0x${commitmentHex}`;
68
+ const cleanHex = paddedHex.slice(2).padStart(64, '0');
69
+ const word = Word.fromHex(`0x${cleanHex}`);
70
+ const signature = this.secretKey.sign(word);
71
+ const signatureBytes = signature.serialize();
72
+ const falconSignature = signatureBytes.slice(1);
73
+ return bytesToHex(falconSignature);
74
+ }
75
+ }
@@ -0,0 +1,41 @@
1
+ import type { TransactionRequest, Word } from '@demox-labs/miden-sdk';
2
+ import { NoteId, NoteIdAndArgs, NoteIdAndArgsArray, TransactionRequestBuilder, Word as WordType } from '@demox-labs/miden-sdk';
3
+ import { randomWord } from '../utils/random.js';
4
+ import { normalizeHexWord } from '../utils/encoding.js';
5
+ import type { SignatureOptions } from './options.js';
6
+
7
+ export function buildConsumeNotesTransactionRequest(
8
+ noteIds: string[],
9
+ options: SignatureOptions = {},
10
+ ): { request: TransactionRequest; salt: Word } {
11
+ if (noteIds.length === 0) {
12
+ throw new Error('At least one note ID is required');
13
+ }
14
+
15
+ const noteIdAndArgsArray = new NoteIdAndArgsArray();
16
+ for (const noteIdHex of noteIds) {
17
+ const noteId = NoteId.fromHex(noteIdHex);
18
+ const noteIdAndArgs = new NoteIdAndArgs(noteId, null);
19
+ noteIdAndArgsArray.push(noteIdAndArgs);
20
+ }
21
+
22
+ const authSaltHex = options.salt ? options.salt.toHex() : randomWord().toHex();
23
+
24
+ const authSaltForBuilder = WordType.fromHex(normalizeHexWord(authSaltHex));
25
+
26
+ let txBuilder = new TransactionRequestBuilder();
27
+ txBuilder = txBuilder.withAuthenticatedInputNotes(noteIdAndArgsArray);
28
+ txBuilder = txBuilder.withAuthArg(authSaltForBuilder);
29
+
30
+ if (options.signatureAdviceMap) {
31
+ txBuilder = txBuilder.extendAdviceMap(options.signatureAdviceMap);
32
+ }
33
+
34
+ const authSaltForReturn = WordType.fromHex(normalizeHexWord(authSaltHex));
35
+
36
+ return {
37
+ request: txBuilder.build(),
38
+ salt: authSaltForReturn,
39
+ };
40
+ }
41
+
@@ -0,0 +1,7 @@
1
+ export { executeForSummary } from './summary.js';
2
+ export { buildUpdateSignersTransactionRequest } from './updateSigners.js';
3
+ export { buildUpdatePsmTransactionRequest } from './updatePsm.js';
4
+ export { buildConsumeNotesTransactionRequest } from './consumeNotes.js';
5
+ export { buildP2idTransactionRequest } from './p2id.js';
6
+ export type { SignatureOptions } from './options.js';
7
+
@@ -0,0 +1,7 @@
1
+ import type { AdviceMap, Word } from '@demox-labs/miden-sdk';
2
+
3
+ export interface SignatureOptions {
4
+ salt?: Word;
5
+ signatureAdviceMap?: AdviceMap;
6
+ }
7
+
@@ -0,0 +1,105 @@
1
+ import type { TransactionRequest, Word } from '@demox-labs/miden-sdk';
2
+ import {
3
+ AccountId,
4
+ Felt,
5
+ FeltArray,
6
+ FungibleAsset,
7
+ MidenArrays,
8
+ Note,
9
+ NoteAssets,
10
+ NoteExecutionHint,
11
+ NoteInputs,
12
+ NoteMetadata,
13
+ NoteRecipient,
14
+ NoteScript,
15
+ NoteTag,
16
+ NoteType,
17
+ OutputNote,
18
+ Rpo256,
19
+ TransactionRequestBuilder,
20
+ Word as WordType,
21
+ } from '@demox-labs/miden-sdk';
22
+ import { randomWord } from '../utils/random.js';
23
+ import { normalizeHexWord } from '../utils/encoding.js';
24
+ import type { SignatureOptions } from './options.js';
25
+
26
+ function buildP2idNote(
27
+ sender: AccountId,
28
+ recipient: AccountId,
29
+ noteAssets: NoteAssets,
30
+ noteType: NoteType,
31
+ aux: Felt,
32
+ saltHex: string,
33
+ ): Note {
34
+ const salt = WordType.fromHex(normalizeHexWord(saltHex));
35
+ const serialNum = Rpo256.hashElements(new FeltArray([
36
+ ...salt.toFelts(),
37
+ new Felt(0n),
38
+ ]));
39
+
40
+ const noteScript = NoteScript.p2id();
41
+ const noteInputs = new NoteInputs(new FeltArray([
42
+ recipient.suffix(),
43
+ recipient.prefix(),
44
+ ]));
45
+
46
+ const noteRecipient = new NoteRecipient(serialNum, noteScript, noteInputs);
47
+ const noteTag = NoteTag.fromAccountId(recipient);
48
+
49
+ const noteMetadata = new NoteMetadata(
50
+ sender,
51
+ noteType,
52
+ noteTag,
53
+ NoteExecutionHint.always(),
54
+ aux,
55
+ );
56
+
57
+ return new Note(noteAssets, noteMetadata, noteRecipient);
58
+ }
59
+
60
+ export function buildP2idTransactionRequest(
61
+ senderId: string,
62
+ recipientId: string,
63
+ faucetId: string,
64
+ amount: bigint,
65
+ options: SignatureOptions = {},
66
+ ): { request: TransactionRequest; salt: Word } {
67
+ const sender = AccountId.fromHex(senderId);
68
+ const recipient = AccountId.fromHex(recipientId);
69
+ const faucet = AccountId.fromHex(faucetId);
70
+
71
+ const authSaltHex = options.salt ? options.salt.toHex() : randomWord().toHex();
72
+
73
+ const asset = new FungibleAsset(faucet, amount);
74
+ const noteAssets = new NoteAssets([asset]);
75
+
76
+ const note = buildP2idNote(
77
+ sender,
78
+ recipient,
79
+ noteAssets,
80
+ NoteType.Public,
81
+ new Felt(0n),
82
+ authSaltHex,
83
+ );
84
+
85
+ const outputNote = OutputNote.full(note);
86
+ const outputNotes = new MidenArrays.OutputNoteArray([outputNote]);
87
+
88
+ const authSaltForBuilder = WordType.fromHex(normalizeHexWord(authSaltHex));
89
+
90
+ let txBuilder = new TransactionRequestBuilder();
91
+ txBuilder = txBuilder.withOwnOutputNotes(outputNotes);
92
+ txBuilder = txBuilder.withAuthArg(authSaltForBuilder);
93
+
94
+ if (options.signatureAdviceMap) {
95
+ txBuilder = txBuilder.extendAdviceMap(options.signatureAdviceMap);
96
+ }
97
+
98
+ const authSaltForReturn = WordType.fromHex(normalizeHexWord(authSaltHex));
99
+
100
+ return {
101
+ request: txBuilder.build(),
102
+ salt: authSaltForReturn,
103
+ };
104
+ }
105
+
@@ -0,0 +1,12 @@
1
+ import type { TransactionRequest, TransactionSummary, WebClient } from '@demox-labs/miden-sdk';
2
+ import { AccountId } from '@demox-labs/miden-sdk';
3
+
4
+ export async function executeForSummary(
5
+ client: WebClient,
6
+ accountId: string,
7
+ txRequest: TransactionRequest,
8
+ ): Promise<TransactionSummary> {
9
+ const acc = AccountId.fromHex(accountId);
10
+ return (client as any).executeForSummary(acc, txRequest);
11
+ }
12
+
@@ -0,0 +1,69 @@
1
+ import {
2
+ AdviceMap,
3
+ FeltArray,
4
+ TransactionRequest,
5
+ TransactionRequestBuilder,
6
+ TransactionScript,
7
+ WebClient,
8
+ Word,
9
+ Word as WordType,
10
+ } from '@demox-labs/miden-sdk';
11
+ import { PSM_MASM } from '../account/masm.js';
12
+ import { normalizeHexWord } from '../utils/encoding.js';
13
+ import { randomWord } from '../utils/random.js';
14
+ import type { SignatureOptions } from './options.js';
15
+
16
+ function buildUpdatePsmScript(webClient: WebClient): TransactionScript {
17
+ const libBuilder = webClient.createScriptBuilder();
18
+ const psmLib = libBuilder.buildLibrary('openzeppelin::psm', PSM_MASM);
19
+ libBuilder.linkDynamicLibrary(psmLib);
20
+
21
+ const scriptSource = `
22
+ use.openzeppelin::psm
23
+
24
+ begin
25
+ adv.push_mapval
26
+ dropw
27
+ call.psm::update_psm_public_key
28
+ end
29
+ `;
30
+
31
+ return libBuilder.compileTxScript(scriptSource);
32
+ }
33
+
34
+ export async function buildUpdatePsmTransactionRequest(
35
+ webClient: WebClient,
36
+ newPsmPubkey: string,
37
+ options: SignatureOptions = {},
38
+ ): Promise<{ request: TransactionRequest; salt: Word }> {
39
+ const script = buildUpdatePsmScript(webClient);
40
+
41
+ const authSaltHex = options.salt ? options.salt.toHex() : randomWord().toHex();
42
+
43
+ const pubkeyWordForAdvice = WordType.fromHex(normalizeHexWord(newPsmPubkey));
44
+ const pubkeyWordForFelts = WordType.fromHex(normalizeHexWord(newPsmPubkey));
45
+ const pubkeyWordForScript = WordType.fromHex(normalizeHexWord(newPsmPubkey));
46
+
47
+ const advice = new AdviceMap();
48
+ advice.insert(pubkeyWordForAdvice, new FeltArray(pubkeyWordForFelts.toFelts()));
49
+
50
+ const authSaltForBuilder = WordType.fromHex(normalizeHexWord(authSaltHex));
51
+
52
+ let txBuilder = new TransactionRequestBuilder();
53
+ txBuilder = txBuilder.withCustomScript(script);
54
+ txBuilder = txBuilder.withScriptArg(pubkeyWordForScript);
55
+ txBuilder = txBuilder.extendAdviceMap(advice);
56
+ txBuilder = txBuilder.withAuthArg(authSaltForBuilder);
57
+
58
+ if (options.signatureAdviceMap) {
59
+ txBuilder = txBuilder.extendAdviceMap(options.signatureAdviceMap);
60
+ }
61
+
62
+ const authSaltForReturn = WordType.fromHex(normalizeHexWord(authSaltHex));
63
+
64
+ return {
65
+ request: txBuilder.build(),
66
+ salt: authSaltForReturn,
67
+ };
68
+ }
69
+
@@ -0,0 +1,96 @@
1
+ import {
2
+ AdviceMap,
3
+ Felt,
4
+ FeltArray,
5
+ Rpo256,
6
+ TransactionRequest,
7
+ TransactionRequestBuilder,
8
+ TransactionScript,
9
+ WebClient,
10
+ Word,
11
+ Word as WordType,
12
+ } from '@demox-labs/miden-sdk';
13
+ import { MULTISIG_MASM, PSM_MASM } from '../account/masm.js';
14
+ import { normalizeHexWord } from '../utils/encoding.js';
15
+ import { randomWord } from '../utils/random.js';
16
+ import type { SignatureOptions } from './options.js';
17
+
18
+ function buildMultisigConfigAdvice(
19
+ threshold: number,
20
+ signerCommitments: string[],
21
+ ): { configHash: Word; payload: FeltArray } {
22
+ const numApprovers = signerCommitments.length;
23
+ const felts: Felt[] = [
24
+ new Felt(BigInt(threshold)),
25
+ new Felt(BigInt(numApprovers)),
26
+ new Felt(0n),
27
+ new Felt(0n),
28
+ ];
29
+ for (const commitment of [...signerCommitments].reverse()) {
30
+ const word = WordType.fromHex(normalizeHexWord(commitment));
31
+ felts.push(...word.toFelts());
32
+ }
33
+ const payload = new FeltArray(felts);
34
+ const configHash = Rpo256.hashElements(payload);
35
+ return { configHash, payload };
36
+ }
37
+
38
+ function buildUpdateSignersScript(webClient: WebClient): TransactionScript {
39
+ const libBuilder = webClient.createScriptBuilder();
40
+ const psmLib = libBuilder.buildLibrary('openzeppelin::psm', PSM_MASM);
41
+ libBuilder.linkStaticLibrary(psmLib);
42
+
43
+ const multisigLib = libBuilder.buildLibrary('auth::multisig', MULTISIG_MASM);
44
+ libBuilder.linkDynamicLibrary(multisigLib);
45
+
46
+ const scriptSource = `
47
+ use.auth::multisig
48
+
49
+ begin
50
+ call.multisig::update_signers_and_threshold
51
+ end
52
+ `;
53
+
54
+ return libBuilder.compileTxScript(scriptSource);
55
+ }
56
+
57
+ export async function buildUpdateSignersTransactionRequest(
58
+ webClient: WebClient,
59
+ threshold: number,
60
+ signerCommitments: string[],
61
+ options: SignatureOptions = {},
62
+ ): Promise<{ request: TransactionRequest; salt: Word; configHash: Word }> {
63
+ const { configHash: configHashForAdvice, payload } = buildMultisigConfigAdvice(threshold, signerCommitments);
64
+
65
+ const { configHash: configHashForScript } = buildMultisigConfigAdvice(threshold, signerCommitments);
66
+
67
+ const { configHash: configHashForReturn } = buildMultisigConfigAdvice(threshold, signerCommitments);
68
+
69
+ const advice = new AdviceMap();
70
+ advice.insert(configHashForAdvice, payload);
71
+
72
+ const script = buildUpdateSignersScript(webClient);
73
+
74
+ const authSaltHex = options.salt ? options.salt.toHex() : randomWord().toHex();
75
+
76
+ const authSaltForBuilder = WordType.fromHex(normalizeHexWord(authSaltHex));
77
+
78
+ let txBuilder = new TransactionRequestBuilder();
79
+ txBuilder = txBuilder.withCustomScript(script);
80
+ txBuilder = txBuilder.withScriptArg(configHashForScript);
81
+ txBuilder = txBuilder.extendAdviceMap(advice);
82
+ txBuilder = txBuilder.withAuthArg(authSaltForBuilder);
83
+
84
+ if (options.signatureAdviceMap) {
85
+ txBuilder = txBuilder.extendAdviceMap(options.signatureAdviceMap);
86
+ }
87
+
88
+ const authSaltForReturn = WordType.fromHex(normalizeHexWord(authSaltHex));
89
+
90
+ return {
91
+ request: txBuilder.build(),
92
+ salt: authSaltForReturn,
93
+ configHash: configHashForReturn,
94
+ };
95
+ }
96
+