@openzeppelin/miden-multisig-client 0.12.6 → 0.12.7
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.
- package/README.md +70 -75
- package/dist/account/builder.d.ts +0 -18
- package/dist/account/builder.d.ts.map +1 -1
- package/dist/account/builder.js +16 -24
- package/dist/account/builder.js.map +1 -1
- package/dist/account/masm.d.ts +2 -0
- package/dist/account/masm.d.ts.map +1 -1
- package/dist/account/masm.js +428 -1
- package/dist/account/masm.js.map +1 -1
- package/dist/account/storage.d.ts.map +1 -1
- package/dist/account/storage.js +5 -6
- package/dist/account/storage.js.map +1 -1
- package/dist/client.d.ts +5 -57
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +7 -53
- package/dist/client.js.map +1 -1
- package/dist/client.test.js +28 -0
- package/dist/client.test.js.map +1 -1
- package/dist/index.d.ts +3 -46
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -45
- package/dist/index.js.map +1 -1
- package/dist/inspector.d.ts +4 -30
- package/dist/inspector.d.ts.map +1 -1
- package/dist/inspector.js +13 -43
- package/dist/inspector.js.map +1 -1
- package/dist/multisig/helpers.d.ts.map +1 -1
- package/dist/multisig/helpers.js.map +1 -1
- package/dist/multisig.d.ts +39 -161
- package/dist/multisig.d.ts.map +1 -1
- package/dist/multisig.js +190 -241
- package/dist/multisig.js.map +1 -1
- package/dist/multisig.test.js +41 -31
- package/dist/multisig.test.js.map +1 -1
- package/dist/procedures.d.ts +20 -47
- package/dist/procedures.d.ts.map +1 -1
- package/dist/procedures.js +17 -50
- package/dist/procedures.js.map +1 -1
- package/dist/signer.d.ts +11 -26
- package/dist/signer.d.ts.map +1 -1
- package/dist/signer.js +41 -26
- package/dist/signer.js.map +1 -1
- package/dist/transaction/options.d.ts +2 -0
- package/dist/transaction/options.d.ts.map +1 -1
- package/dist/transaction/updatePsm.d.ts.map +1 -1
- package/dist/transaction/updatePsm.js +21 -3
- package/dist/transaction/updatePsm.js.map +1 -1
- package/dist/transaction/updateSigners.d.ts.map +1 -1
- package/dist/transaction/updateSigners.js +21 -3
- package/dist/transaction/updateSigners.js.map +1 -1
- package/dist/transaction.d.ts.map +1 -1
- package/dist/transaction.js +0 -2
- package/dist/transaction.js.map +1 -1
- package/dist/transaction.test.js +5 -0
- package/dist/transaction.test.js.map +1 -1
- package/dist/types/proposal.d.ts +16 -8
- package/dist/types/proposal.d.ts.map +1 -1
- package/dist/types.d.ts +18 -14
- package/dist/types.d.ts.map +1 -1
- package/dist/utils/signature.d.ts +10 -2
- package/dist/utils/signature.d.ts.map +1 -1
- package/dist/utils/signature.js +77 -5
- package/dist/utils/signature.js.map +1 -1
- package/dist/utils/signature.test.js +16 -1
- package/dist/utils/signature.test.js.map +1 -1
- package/masm/multisig_ecdsa.masm +424 -0
- package/masm/psm_ecdsa.masm +179 -0
- package/package.json +2 -2
- package/src/account/builder.ts +18 -25
- package/src/account/masm.ts +433 -2
- package/src/account/storage.ts +5 -6
- package/src/client.test.ts +36 -0
- package/src/client.ts +10 -59
- package/src/index.ts +13 -62
- package/src/inspector.ts +15 -44
- package/src/multisig/helpers.ts +1 -2
- package/src/multisig.test.ts +45 -34
- package/src/multisig.ts +246 -273
- package/src/procedures.ts +21 -56
- package/src/signer.ts +48 -28
- package/src/transaction/options.ts +2 -0
- package/src/transaction/updatePsm.ts +24 -3
- package/src/transaction/updateSigners.ts +25 -3
- package/src/transaction.test.ts +6 -0
- package/src/transaction.ts +0 -2
- package/src/types/proposal.ts +15 -8
- package/src/types.ts +26 -16
- package/src/utils/signature.test.ts +24 -1
- package/src/utils/signature.ts +86 -4
package/src/client.ts
CHANGED
|
@@ -1,49 +1,14 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* MultisigClient - Factory for creating and loading multisig accounts.
|
|
3
|
-
*
|
|
4
|
-
* This is the main entry point for the multisig SDK. It provides methods
|
|
5
|
-
* to create new multisig accounts and load existing ones.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
1
|
import { type WebClient, Account, AccountId } from '@demox-labs/miden-sdk';
|
|
9
|
-
import { PsmHttpClient } from '@openzeppelin/psm-client';
|
|
2
|
+
import { PsmHttpClient, type SignatureScheme } from '@openzeppelin/psm-client';
|
|
10
3
|
import { Multisig } from './multisig.js';
|
|
11
4
|
import { createMultisigAccount } from './account/index.js';
|
|
12
5
|
import { AccountInspector } from './inspector.js';
|
|
13
6
|
import type { MultisigConfig, Signer } from './types.js';
|
|
14
7
|
|
|
15
|
-
/**
|
|
16
|
-
* Configuration for MultisigClient.
|
|
17
|
-
*/
|
|
18
8
|
export interface MultisigClientConfig {
|
|
19
|
-
/** PSM server endpoint */
|
|
20
9
|
psmEndpoint?: string;
|
|
21
10
|
}
|
|
22
11
|
|
|
23
|
-
/**
|
|
24
|
-
* Client for creating and loading multisig accounts.
|
|
25
|
-
*
|
|
26
|
-
* @example
|
|
27
|
-
* ```typescript
|
|
28
|
-
* import { MultisigClient, FalconSigner } from '@openzeppelin/miden-multisig-client';
|
|
29
|
-
* import { WebClient, SecretKey } from '@demox-labs/miden-sdk';
|
|
30
|
-
*
|
|
31
|
-
* // Initialize
|
|
32
|
-
* const webClient = await WebClient.createClient('https://rpc.testnet.miden.io:443');
|
|
33
|
-
* const secretKey = SecretKey.rpoFalconWithRNG(seed);
|
|
34
|
-
* const signer = new FalconSigner(secretKey);
|
|
35
|
-
*
|
|
36
|
-
* // Create client
|
|
37
|
-
* const client = new MultisigClient(webClient, { psmEndpoint: 'http://localhost:3000' });
|
|
38
|
-
*
|
|
39
|
-
* // Get PSM pubkey for config
|
|
40
|
-
* const psmCommitment = await client.psmClient.getPubkey();
|
|
41
|
-
*
|
|
42
|
-
* // Create multisig
|
|
43
|
-
* const config = { threshold: 2, signerCommitments: [...], psmCommitment };
|
|
44
|
-
* const multisig = await client.create(config, signer);
|
|
45
|
-
* ```
|
|
46
|
-
*/
|
|
47
12
|
export class MultisigClient {
|
|
48
13
|
private readonly webClient: WebClient;
|
|
49
14
|
private _psmClient: PsmHttpClient;
|
|
@@ -53,29 +18,19 @@ export class MultisigClient {
|
|
|
53
18
|
this._psmClient = new PsmHttpClient(config.psmEndpoint ?? 'http://localhost:3000');
|
|
54
19
|
}
|
|
55
20
|
|
|
56
|
-
/**
|
|
57
|
-
* Change the PSM endpoint.
|
|
58
|
-
*
|
|
59
|
-
* @param endpoint - The new PSM server endpoint URL
|
|
60
|
-
*/
|
|
61
21
|
setPsmEndpoint(endpoint: string): void {
|
|
62
22
|
this._psmClient = new PsmHttpClient(endpoint);
|
|
63
23
|
}
|
|
64
24
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
25
|
+
async initialize(scheme?: SignatureScheme): Promise<{ psmCommitment: string; psmPublicKey?: string }> {
|
|
26
|
+
const { commitment, pubkey } = await this._psmClient.getPubkey(scheme);
|
|
27
|
+
return { psmCommitment: commitment, psmPublicKey: pubkey };
|
|
28
|
+
}
|
|
29
|
+
|
|
68
30
|
get psmClient(): PsmHttpClient {
|
|
69
31
|
return this._psmClient;
|
|
70
32
|
}
|
|
71
33
|
|
|
72
|
-
/**
|
|
73
|
-
* Create a new multisig account.
|
|
74
|
-
*
|
|
75
|
-
* @param config - Multisig configuration (threshold, signers, PSM commitment)
|
|
76
|
-
* @param signer - The signer for this client (one of the cosigners)
|
|
77
|
-
* @returns A Multisig instance wrapping the created account
|
|
78
|
-
*/
|
|
79
34
|
async create(config: MultisigConfig, signer: Signer): Promise<Multisig> {
|
|
80
35
|
this._psmClient.setSigner(signer);
|
|
81
36
|
|
|
@@ -84,13 +39,6 @@ export class MultisigClient {
|
|
|
84
39
|
return new Multisig(account, config, this._psmClient, signer, this.webClient);
|
|
85
40
|
}
|
|
86
41
|
|
|
87
|
-
/**
|
|
88
|
-
* Load an existing multisig account from PSM.
|
|
89
|
-
*
|
|
90
|
-
* @param accountId - The account ID to load
|
|
91
|
-
* @param signer - The signer for this client
|
|
92
|
-
* @returns A Multisig instance for the loaded account
|
|
93
|
-
*/
|
|
94
42
|
async load(accountId: string, signer: Signer): Promise<Multisig> {
|
|
95
43
|
this._psmClient.setSigner(signer);
|
|
96
44
|
|
|
@@ -108,12 +56,15 @@ export class MultisigClient {
|
|
|
108
56
|
}
|
|
109
57
|
const account = Account.deserialize(accountBytes);
|
|
110
58
|
|
|
111
|
-
const
|
|
59
|
+
const detectedScheme: SignatureScheme = signer.scheme;
|
|
60
|
+
|
|
61
|
+
const detected = AccountInspector.fromAccount(account, detectedScheme);
|
|
112
62
|
const config: MultisigConfig = {
|
|
113
63
|
threshold: detected.threshold,
|
|
114
64
|
signerCommitments: detected.signerCommitments,
|
|
115
65
|
psmCommitment: detected.psmCommitment ?? '',
|
|
116
66
|
psmEnabled: detected.psmEnabled,
|
|
67
|
+
signatureScheme: detected.signatureScheme,
|
|
117
68
|
procedureThresholds: Array.from(detected.procedureThresholds.entries()).map(
|
|
118
69
|
([procedure, threshold]) => ({ procedure, threshold })
|
|
119
70
|
),
|
package/src/index.ts
CHANGED
|
@@ -1,47 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @openzeppelin/miden-multisig-client
|
|
3
|
-
*
|
|
4
|
-
* TypeScript SDK for Miden multisig accounts with PSM (Private State Manager) integration.
|
|
5
|
-
*
|
|
6
|
-
* @example
|
|
7
|
-
* ```typescript
|
|
8
|
-
* import {
|
|
9
|
-
* MultisigClient,
|
|
10
|
-
* FalconSigner,
|
|
11
|
-
* } from '@openzeppelin/miden-multisig-client';
|
|
12
|
-
* import { WebClient, SecretKey } from '@demox-labs/miden-sdk';
|
|
13
|
-
*
|
|
14
|
-
* // Initialize WebClient
|
|
15
|
-
* const webClient = await WebClient.createClient('https://rpc.testnet.miden.io:443');
|
|
16
|
-
* await webClient.syncState();
|
|
17
|
-
*
|
|
18
|
-
* // Generate a key dynamically
|
|
19
|
-
* const seed = new Uint8Array(32);
|
|
20
|
-
* crypto.getRandomValues(seed);
|
|
21
|
-
* const secretKey = SecretKey.rpoFalconWithRNG(seed);
|
|
22
|
-
*
|
|
23
|
-
* // Store in miden-sdk's keystore
|
|
24
|
-
* await webClient.addAccountSecretKeyToWebStore(secretKey);
|
|
25
|
-
*
|
|
26
|
-
* // Create a signer
|
|
27
|
-
* const signer = new FalconSigner(secretKey);
|
|
28
|
-
*
|
|
29
|
-
* // Create multisig client
|
|
30
|
-
* const client = new MultisigClient(webClient, { psmEndpoint: 'http://localhost:3000' });
|
|
31
|
-
*
|
|
32
|
-
* // Get PSM pubkey for config
|
|
33
|
-
* const psmCommitment = await client.psmClient.getPubkey();
|
|
34
|
-
*
|
|
35
|
-
* // Create multisig account
|
|
36
|
-
* const config = { threshold: 2, signerCommitments: [signer.commitment, ...], psmCommitment };
|
|
37
|
-
* const multisig = await client.create(config, signer);
|
|
38
|
-
*
|
|
39
|
-
* // Register on PSM and work with proposals
|
|
40
|
-
* await multisig.registerOnPsm();
|
|
41
|
-
* await multisig.syncProposals();
|
|
42
|
-
* ```
|
|
43
|
-
*/
|
|
44
|
-
|
|
45
1
|
export { MultisigClient, type MultisigClientConfig } from './client.js';
|
|
46
2
|
export { Multisig, type AccountState } from './multisig.js';
|
|
47
3
|
export { AccountInspector, type DetectedMultisigConfig, type VaultBalance } from './inspector.js';
|
|
@@ -55,7 +11,7 @@ export {
|
|
|
55
11
|
|
|
56
12
|
export { PsmHttpClient, PsmHttpError } from '@openzeppelin/psm-client';
|
|
57
13
|
|
|
58
|
-
export { FalconSigner } from './signer.js';
|
|
14
|
+
export { FalconSigner, EcdsaSigner } from './signer.js';
|
|
59
15
|
|
|
60
16
|
export {
|
|
61
17
|
createMultisigAccount,
|
|
@@ -68,6 +24,8 @@ export {
|
|
|
68
24
|
|
|
69
25
|
export {
|
|
70
26
|
PROCEDURE_ROOTS,
|
|
27
|
+
PROCEDURE_ROOTS_FALCON,
|
|
28
|
+
PROCEDURE_ROOTS_ECDSA,
|
|
71
29
|
getProcedureRoot,
|
|
72
30
|
isProcedureName,
|
|
73
31
|
getProcedureNames,
|
|
@@ -75,39 +33,32 @@ export {
|
|
|
75
33
|
} from './procedures.js';
|
|
76
34
|
|
|
77
35
|
export type {
|
|
78
|
-
// Account types
|
|
79
36
|
MultisigAccountState,
|
|
80
37
|
MultisigConfig,
|
|
81
38
|
CreateAccountResult,
|
|
82
39
|
ProcedureThreshold,
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
40
|
+
TransactionProposal,
|
|
41
|
+
TransactionProposalStatus,
|
|
42
|
+
TransactionProposalSignature,
|
|
43
|
+
ExportedTransactionProposal,
|
|
44
|
+
SignTransactionProposalParams,
|
|
45
|
+
SyncResult,
|
|
46
|
+
TransactionProposalResult,
|
|
88
47
|
ProposalMetadata,
|
|
89
48
|
ProposalType,
|
|
90
|
-
ExportedProposal,
|
|
91
|
-
|
|
92
|
-
// Transaction types
|
|
93
49
|
TransactionType,
|
|
94
|
-
|
|
95
|
-
// Note types
|
|
96
50
|
ConsumableNote,
|
|
97
51
|
NoteAsset,
|
|
98
|
-
|
|
99
|
-
// Signature types
|
|
100
52
|
FalconSignature,
|
|
53
|
+
EcdsaSignature,
|
|
54
|
+
ProposalSignature,
|
|
55
|
+
SignatureScheme,
|
|
101
56
|
Signer,
|
|
102
|
-
|
|
103
|
-
// PSM API types
|
|
104
57
|
AuthConfig,
|
|
105
58
|
DeltaObject,
|
|
106
59
|
DeltaStatus,
|
|
107
60
|
StateObject,
|
|
108
61
|
CosignerSignature,
|
|
109
|
-
|
|
110
|
-
// Request/Response types
|
|
111
62
|
ConfigureRequest,
|
|
112
63
|
ConfigureResponse,
|
|
113
64
|
DeltaProposalRequest,
|
package/src/inspector.ts
CHANGED
|
@@ -1,11 +1,8 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Account Inspector - Inspects account storage to detect multisig configuration.
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
1
|
import { Account, Word } from '@demox-labs/miden-sdk';
|
|
6
2
|
import { base64ToUint8Array } from './utils/encoding.js';
|
|
7
3
|
import { wordElementToBigInt, wordToHex } from './utils/word.js';
|
|
8
4
|
import { getProcedureRoot, getProcedureNames, type ProcedureName } from './procedures.js';
|
|
5
|
+
import type { SignatureScheme } from './types.js';
|
|
9
6
|
|
|
10
7
|
export interface VaultBalance {
|
|
11
8
|
faucetId: string;
|
|
@@ -20,44 +17,19 @@ export interface DetectedMultisigConfig {
|
|
|
20
17
|
psmCommitment: string | null;
|
|
21
18
|
vaultBalances: VaultBalance[];
|
|
22
19
|
procedureThresholds: Map<ProcedureName, number>;
|
|
20
|
+
signatureScheme: SignatureScheme;
|
|
23
21
|
}
|
|
24
22
|
|
|
25
|
-
/**
|
|
26
|
-
* Inspects an account to detect its multisig configuration.
|
|
27
|
-
*
|
|
28
|
-
* @example
|
|
29
|
-
* ```typescript
|
|
30
|
-
* // From base64-encoded state
|
|
31
|
-
* const config = AccountInspector.fromBase64(stateDataBase64);
|
|
32
|
-
* console.log(`${config.threshold}-of-${config.numSigners} multisig`);
|
|
33
|
-
*
|
|
34
|
-
* // From Miden SDK Account
|
|
35
|
-
* const config = AccountInspector.fromAccount(account);
|
|
36
|
-
* ```
|
|
37
|
-
*/
|
|
38
23
|
export class AccountInspector {
|
|
39
24
|
private constructor() {}
|
|
40
25
|
|
|
41
|
-
|
|
42
|
-
* Inspect a base64-encoded serialized account.
|
|
43
|
-
*
|
|
44
|
-
* @param base64Data - Base64-encoded Account bytes
|
|
45
|
-
* @returns Detected multisig configuration
|
|
46
|
-
*/
|
|
47
|
-
static fromBase64(base64Data: string): DetectedMultisigConfig {
|
|
26
|
+
static fromBase64(base64Data: string, signatureScheme: SignatureScheme = 'falcon'): DetectedMultisigConfig {
|
|
48
27
|
const bytes = base64ToUint8Array(base64Data);
|
|
49
28
|
const account = Account.deserialize(bytes);
|
|
50
|
-
return AccountInspector.fromAccount(account);
|
|
29
|
+
return AccountInspector.fromAccount(account, signatureScheme);
|
|
51
30
|
}
|
|
52
31
|
|
|
53
|
-
|
|
54
|
-
* Inspect a Miden SDK Account object.
|
|
55
|
-
*
|
|
56
|
-
* @param account - The Account object from Miden SDK
|
|
57
|
-
* @returns Detected multisig configuration
|
|
58
|
-
*/
|
|
59
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
60
|
-
static fromAccount(account: Account): DetectedMultisigConfig {
|
|
32
|
+
static fromAccount(account: Account, signatureScheme: SignatureScheme = 'falcon'): DetectedMultisigConfig {
|
|
61
33
|
const storage = account.storage();
|
|
62
34
|
|
|
63
35
|
const slot0 = storage.getItem(0) as Word;
|
|
@@ -72,8 +44,8 @@ export class AccountInspector {
|
|
|
72
44
|
if (commitment) {
|
|
73
45
|
signerCommitments.push(wordToHex(commitment));
|
|
74
46
|
}
|
|
75
|
-
} catch
|
|
76
|
-
|
|
47
|
+
} catch {
|
|
48
|
+
// Signer not found in storage map
|
|
77
49
|
}
|
|
78
50
|
}
|
|
79
51
|
|
|
@@ -92,8 +64,8 @@ export class AccountInspector {
|
|
|
92
64
|
psmCommitment = wordToHex(psmKey);
|
|
93
65
|
}
|
|
94
66
|
}
|
|
95
|
-
} catch
|
|
96
|
-
|
|
67
|
+
} catch {
|
|
68
|
+
// PSM not configured
|
|
97
69
|
}
|
|
98
70
|
|
|
99
71
|
const vaultBalances: VaultBalance[] = [];
|
|
@@ -106,16 +78,14 @@ export class AccountInspector {
|
|
|
106
78
|
amount: BigInt(asset.amount()),
|
|
107
79
|
});
|
|
108
80
|
}
|
|
109
|
-
} catch
|
|
110
|
-
|
|
81
|
+
} catch {
|
|
82
|
+
// Vault access failed
|
|
111
83
|
}
|
|
112
84
|
|
|
113
|
-
// Read procedure threshold overrides from storage slot 3
|
|
114
|
-
// Storage layout: slot 3 is a map of PROC_ROOT => [threshold, 0, 0, 0]
|
|
115
85
|
const procedureThresholds = new Map<ProcedureName, number>();
|
|
116
|
-
for (const procName of getProcedureNames()) {
|
|
86
|
+
for (const procName of getProcedureNames(signatureScheme)) {
|
|
117
87
|
try {
|
|
118
|
-
const rootHex = getProcedureRoot(procName);
|
|
88
|
+
const rootHex = getProcedureRoot(procName, signatureScheme);
|
|
119
89
|
const rootWord = Word.fromHex(rootHex);
|
|
120
90
|
const value = storage.getMapItem(3, rootWord) as Word;
|
|
121
91
|
if (value) {
|
|
@@ -125,7 +95,7 @@ export class AccountInspector {
|
|
|
125
95
|
}
|
|
126
96
|
}
|
|
127
97
|
} catch {
|
|
128
|
-
//
|
|
98
|
+
// Not set
|
|
129
99
|
}
|
|
130
100
|
}
|
|
131
101
|
|
|
@@ -137,6 +107,7 @@ export class AccountInspector {
|
|
|
137
107
|
psmCommitment,
|
|
138
108
|
vaultBalances,
|
|
139
109
|
procedureThresholds,
|
|
110
|
+
signatureScheme,
|
|
140
111
|
};
|
|
141
112
|
}
|
|
142
113
|
}
|
package/src/multisig/helpers.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Account, TransactionSummary
|
|
1
|
+
import { Account, TransactionSummary } from '@demox-labs/miden-sdk';
|
|
2
2
|
import { base64ToUint8Array } from '../utils/encoding.js';
|
|
3
3
|
|
|
4
4
|
export function computeCommitmentFromTxSummary(txSummaryBase64: string): string {
|
|
@@ -7,7 +7,6 @@ export function computeCommitmentFromTxSummary(txSummaryBase64: string): string
|
|
|
7
7
|
const commitment = summary.toCommitment();
|
|
8
8
|
return commitment.toHex();
|
|
9
9
|
}
|
|
10
|
-
|
|
11
10
|
export function accountIdToHex(account: Account): string {
|
|
12
11
|
const accountId = account.id();
|
|
13
12
|
const str = accountId.toString();
|
package/src/multisig.test.ts
CHANGED
|
@@ -94,6 +94,7 @@ describe('Multisig', () => {
|
|
|
94
94
|
psm = new PsmHttpClient('http://localhost:3000');
|
|
95
95
|
|
|
96
96
|
mockSigner = {
|
|
97
|
+
scheme: 'falcon',
|
|
97
98
|
commitment: '0x' + '1'.repeat(64),
|
|
98
99
|
publicKey: '0x' + '2'.repeat(64),
|
|
99
100
|
signAccountIdWithTimestamp: vi.fn().mockReturnValue('0x' + 'a'.repeat(128)),
|
|
@@ -294,7 +295,7 @@ describe('Multisig', () => {
|
|
|
294
295
|
});
|
|
295
296
|
});
|
|
296
297
|
|
|
297
|
-
describe('
|
|
298
|
+
describe('syncTransactionProposals', () => {
|
|
298
299
|
it('should sync proposals from PSM', async () => {
|
|
299
300
|
const config = {
|
|
300
301
|
threshold: 2,
|
|
@@ -339,11 +340,12 @@ describe('Multisig', () => {
|
|
|
339
340
|
json: async () => ({ proposals: mockProposals }),
|
|
340
341
|
});
|
|
341
342
|
|
|
342
|
-
const proposals = await multisig.
|
|
343
|
+
const proposals = await multisig.syncTransactionProposals();
|
|
343
344
|
|
|
344
345
|
expect(proposals.length).toBe(1);
|
|
345
346
|
expect(proposals[0].nonce).toBe(1);
|
|
346
347
|
expect(proposals[0].status.type).toBe('pending');
|
|
348
|
+
expect(proposals[0].commitment).toBe(proposals[0].id);
|
|
347
349
|
});
|
|
348
350
|
|
|
349
351
|
it('should return ready status when enough signatures', async () => {
|
|
@@ -390,13 +392,13 @@ describe('Multisig', () => {
|
|
|
390
392
|
json: async () => ({ proposals: mockProposals }),
|
|
391
393
|
});
|
|
392
394
|
|
|
393
|
-
const proposals = await multisig.
|
|
395
|
+
const proposals = await multisig.syncTransactionProposals();
|
|
394
396
|
|
|
395
397
|
expect(proposals[0].status.type).toBe('ready');
|
|
396
398
|
});
|
|
397
399
|
});
|
|
398
400
|
|
|
399
|
-
describe('
|
|
401
|
+
describe('listTransactionProposals', () => {
|
|
400
402
|
it('should return empty list initially', () => {
|
|
401
403
|
const config = {
|
|
402
404
|
threshold: 1,
|
|
@@ -405,7 +407,7 @@ describe('Multisig', () => {
|
|
|
405
407
|
};
|
|
406
408
|
|
|
407
409
|
const multisig = new Multisig(mockAccount, config, psm, mockSigner, mockWebClient);
|
|
408
|
-
expect(multisig.
|
|
410
|
+
expect(multisig.listTransactionProposals()).toEqual([]);
|
|
409
411
|
});
|
|
410
412
|
});
|
|
411
413
|
|
|
@@ -455,7 +457,7 @@ describe('Multisig', () => {
|
|
|
455
457
|
});
|
|
456
458
|
});
|
|
457
459
|
|
|
458
|
-
describe('
|
|
460
|
+
describe('signTransactionProposal', () => {
|
|
459
461
|
it('should sign a proposal', async () => {
|
|
460
462
|
const config = {
|
|
461
463
|
threshold: 1,
|
|
@@ -471,7 +473,7 @@ describe('Multisig', () => {
|
|
|
471
473
|
nonce: 1,
|
|
472
474
|
prev_commitment: '0x' + 'b'.repeat(64),
|
|
473
475
|
delta_payload: {
|
|
474
|
-
tx_summary: { data: '
|
|
476
|
+
tx_summary: { data: 'AQID' },
|
|
475
477
|
signatures: [],
|
|
476
478
|
},
|
|
477
479
|
status: {
|
|
@@ -523,21 +525,30 @@ describe('Multisig', () => {
|
|
|
523
525
|
},
|
|
524
526
|
};
|
|
525
527
|
|
|
528
|
+
// Mock for signDeltaProposal
|
|
526
529
|
mockFetch.mockResolvedValueOnce({
|
|
527
530
|
ok: true,
|
|
528
531
|
json: async () => signedDelta,
|
|
529
532
|
});
|
|
530
533
|
|
|
534
|
+
// Mock for auto-sync (syncTransactionProposals)
|
|
535
|
+
mockFetch.mockResolvedValueOnce({
|
|
536
|
+
ok: true,
|
|
537
|
+
json: async () => ({ proposals: [signedDelta] }),
|
|
538
|
+
});
|
|
539
|
+
|
|
531
540
|
const proposalId = '0x' + 'd'.repeat(64);
|
|
532
|
-
const
|
|
541
|
+
const proposals = await multisig.signTransactionProposal(proposalId);
|
|
533
542
|
|
|
534
543
|
expect(mockSigner.signCommitment).toHaveBeenCalledWith(proposalId);
|
|
535
|
-
expect(
|
|
544
|
+
expect(proposals.length).toBeGreaterThanOrEqual(1);
|
|
545
|
+
expect(proposals[0].signatures.length).toBe(1);
|
|
546
|
+
expect(proposals[0].commitment).toBe(proposals[0].id);
|
|
536
547
|
});
|
|
537
548
|
});
|
|
538
549
|
|
|
539
|
-
describe('
|
|
540
|
-
it('should export proposal
|
|
550
|
+
describe('exportTransactionProposalToJson', () => {
|
|
551
|
+
it('should export proposal to JSON from local cache', async () => {
|
|
541
552
|
const config = {
|
|
542
553
|
threshold: 2,
|
|
543
554
|
signerCommitments: ['0x' + 'a'.repeat(64), '0x' + 'b'.repeat(64)],
|
|
@@ -546,6 +557,7 @@ describe('Multisig', () => {
|
|
|
546
557
|
|
|
547
558
|
const multisig = new Multisig(mockAccount, config, psm, mockSigner, mockWebClient);
|
|
548
559
|
|
|
560
|
+
// First sync proposals to populate local cache
|
|
549
561
|
const mockProposals = [
|
|
550
562
|
{
|
|
551
563
|
account_id: '0x' + 'a'.repeat(30),
|
|
@@ -581,16 +593,20 @@ describe('Multisig', () => {
|
|
|
581
593
|
json: async () => ({ proposals: mockProposals }),
|
|
582
594
|
});
|
|
583
595
|
|
|
596
|
+
await multisig.syncTransactionProposals();
|
|
597
|
+
|
|
584
598
|
// The proposal ID is computed from tx_summary, which is mocked to return 'c'.repeat(64)
|
|
585
|
-
const
|
|
599
|
+
const json = multisig.exportTransactionProposalToJson('0x' + 'c'.repeat(64));
|
|
600
|
+
const exported = JSON.parse(json);
|
|
586
601
|
|
|
587
602
|
expect(exported.accountId).toBe('0x' + 'a'.repeat(30));
|
|
588
603
|
expect(exported.nonce).toBe(1);
|
|
589
604
|
expect(exported.txSummaryBase64).toBe('AQID');
|
|
590
605
|
expect(exported.signatures.length).toBe(1);
|
|
606
|
+
expect(exported.commitment).toBe('0x' + 'c'.repeat(64));
|
|
591
607
|
});
|
|
592
608
|
|
|
593
|
-
it('should throw if proposal not found',
|
|
609
|
+
it('should throw if proposal not found in cache', () => {
|
|
594
610
|
const config = {
|
|
595
611
|
threshold: 1,
|
|
596
612
|
signerCommitments: ['0x' + 'a'.repeat(64)],
|
|
@@ -599,18 +615,13 @@ describe('Multisig', () => {
|
|
|
599
615
|
|
|
600
616
|
const multisig = new Multisig(mockAccount, config, psm, mockSigner, mockWebClient);
|
|
601
617
|
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
});
|
|
606
|
-
|
|
607
|
-
await expect(
|
|
608
|
-
multisig.exportProposal('0x' + 'nonexistent'.repeat(5))
|
|
609
|
-
).rejects.toThrow('Proposal not found');
|
|
618
|
+
expect(() =>
|
|
619
|
+
multisig.exportTransactionProposalToJson('0x' + 'nonexistent'.repeat(5))
|
|
620
|
+
).toThrow('Proposal not found');
|
|
610
621
|
});
|
|
611
622
|
});
|
|
612
623
|
|
|
613
|
-
describe('
|
|
624
|
+
describe('executeTransactionProposal', () => {
|
|
614
625
|
it('should throw if proposal not found locally', async () => {
|
|
615
626
|
const config = {
|
|
616
627
|
threshold: 1,
|
|
@@ -621,7 +632,7 @@ describe('Multisig', () => {
|
|
|
621
632
|
const multisig = new Multisig(mockAccount, config, psm, mockSigner, mockWebClient);
|
|
622
633
|
|
|
623
634
|
await expect(
|
|
624
|
-
multisig.
|
|
635
|
+
multisig.executeTransactionProposal('0x' + 'nonexistent'.repeat(5))
|
|
625
636
|
).rejects.toThrow('Proposal not found');
|
|
626
637
|
});
|
|
627
638
|
|
|
@@ -670,11 +681,11 @@ describe('Multisig', () => {
|
|
|
670
681
|
json: async () => ({ proposals: mockProposals }),
|
|
671
682
|
});
|
|
672
683
|
|
|
673
|
-
await multisig.
|
|
684
|
+
await multisig.syncTransactionProposals();
|
|
674
685
|
|
|
675
686
|
// Proposal ID is mocked to return 'c'.repeat(64)
|
|
676
687
|
await expect(
|
|
677
|
-
multisig.
|
|
688
|
+
multisig.executeTransactionProposal('0x' + 'c'.repeat(64))
|
|
678
689
|
).rejects.toThrow('not ready for execution');
|
|
679
690
|
});
|
|
680
691
|
|
|
@@ -722,7 +733,7 @@ describe('Multisig', () => {
|
|
|
722
733
|
ok: true,
|
|
723
734
|
json: async () => ({ proposals: [readyDelta] }),
|
|
724
735
|
});
|
|
725
|
-
await multisig.
|
|
736
|
+
await multisig.syncTransactionProposals();
|
|
726
737
|
|
|
727
738
|
// executeProposal: getDeltaProposals
|
|
728
739
|
mockFetch.mockResolvedValueOnce({
|
|
@@ -735,7 +746,7 @@ describe('Multisig', () => {
|
|
|
735
746
|
json: async () => ({ ...readyDelta, ack_sig: null }),
|
|
736
747
|
});
|
|
737
748
|
|
|
738
|
-
await expect(multisig.
|
|
749
|
+
await expect(multisig.executeTransactionProposal(proposalId)).rejects.toThrow(
|
|
739
750
|
'PSM did not return acknowledgment signature'
|
|
740
751
|
);
|
|
741
752
|
});
|
|
@@ -794,7 +805,7 @@ describe('Multisig', () => {
|
|
|
794
805
|
}),
|
|
795
806
|
});
|
|
796
807
|
|
|
797
|
-
const syncedProposals = await multisig.
|
|
808
|
+
const syncedProposals = await multisig.syncTransactionProposals();
|
|
798
809
|
const syncedProposal = syncedProposals.find(p => p.nonce === 1);
|
|
799
810
|
|
|
800
811
|
expect(syncedProposal?.metadata?.proposalType).toBe('add_signer');
|
|
@@ -839,7 +850,7 @@ describe('Multisig', () => {
|
|
|
839
850
|
json: async () => ({ proposals: mockProposals }),
|
|
840
851
|
});
|
|
841
852
|
|
|
842
|
-
const proposals = await multisig.
|
|
853
|
+
const proposals = await multisig.syncTransactionProposals();
|
|
843
854
|
|
|
844
855
|
expect(proposals.length).toBe(1);
|
|
845
856
|
expect(proposals[0].metadata?.proposalType).toBe('p2id');
|
|
@@ -1041,7 +1052,7 @@ describe('Multisig', () => {
|
|
|
1041
1052
|
json: async () => ({ proposals: mockProposalsPending }),
|
|
1042
1053
|
});
|
|
1043
1054
|
|
|
1044
|
-
let proposals = await multisig.
|
|
1055
|
+
let proposals = await multisig.syncTransactionProposals();
|
|
1045
1056
|
expect(proposals[0].status.type).toBe('pending');
|
|
1046
1057
|
|
|
1047
1058
|
// Second sync with 2 signatures (ready)
|
|
@@ -1082,7 +1093,7 @@ describe('Multisig', () => {
|
|
|
1082
1093
|
json: async () => ({ proposals: mockProposalsReady }),
|
|
1083
1094
|
});
|
|
1084
1095
|
|
|
1085
|
-
proposals = await multisig.
|
|
1096
|
+
proposals = await multisig.syncTransactionProposals();
|
|
1086
1097
|
expect(proposals[0].status.type).toBe('ready');
|
|
1087
1098
|
});
|
|
1088
1099
|
});
|
|
@@ -1175,7 +1186,7 @@ describe('Multisig', () => {
|
|
|
1175
1186
|
json: async () => ({ proposals: rustProposals }),
|
|
1176
1187
|
});
|
|
1177
1188
|
|
|
1178
|
-
const proposals = await multisig.
|
|
1189
|
+
const proposals = await multisig.syncTransactionProposals();
|
|
1179
1190
|
|
|
1180
1191
|
expect(proposals.length).toBe(1);
|
|
1181
1192
|
// The TS client should normalize snake_case to camelCase
|
|
@@ -1232,7 +1243,7 @@ describe('Multisig', () => {
|
|
|
1232
1243
|
json: async () => ({ proposals: p2idProposals }),
|
|
1233
1244
|
});
|
|
1234
1245
|
|
|
1235
|
-
const proposals = await multisig.
|
|
1246
|
+
const proposals = await multisig.syncTransactionProposals();
|
|
1236
1247
|
|
|
1237
1248
|
expect(proposals.length).toBe(1);
|
|
1238
1249
|
expect(proposals[0].metadata?.proposalType).toBe('p2id');
|
|
@@ -1281,7 +1292,7 @@ describe('Multisig', () => {
|
|
|
1281
1292
|
json: async () => ({ proposals: switchPsmProposals }),
|
|
1282
1293
|
});
|
|
1283
1294
|
|
|
1284
|
-
const proposals = await multisig.
|
|
1295
|
+
const proposals = await multisig.syncTransactionProposals();
|
|
1285
1296
|
|
|
1286
1297
|
expect(proposals.length).toBe(1);
|
|
1287
1298
|
expect(proposals[0].metadata?.proposalType).toBe('switch_psm');
|