@sovereign-sdk/multisig 0.3.2 → 0.4.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.
- package/README.md +25 -96
- package/dist/index.d.mts +12 -102
- package/dist/index.d.ts +12 -102
- package/dist/index.js +88 -184
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +87 -182
- package/dist/index.mjs.map +1 -1
- package/package.json +3 -6
package/README.md
CHANGED
|
@@ -3,7 +3,9 @@
|
|
|
3
3
|
[](https://www.npmjs.com/package/@sovereign-sdk/multisig)
|
|
4
4
|
[](https://github.com/Sovereign-Labs/sovereign-sdk/actions/workflows/typescript.ci.yml)
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
Payload-agnostic multisig signer state for Sovereign SDK applications.
|
|
7
|
+
|
|
8
|
+
Use `@sovereign-sdk/multisig` when you want to collect signatures outside the rollup client and finalize them into a standard `TransactionV1` once the threshold is met.
|
|
7
9
|
|
|
8
10
|
## Installation
|
|
9
11
|
|
|
@@ -11,119 +13,46 @@ A TypeScript library for creating and managing multisig transactions on Sovereig
|
|
|
11
13
|
npm install @sovereign-sdk/multisig
|
|
12
14
|
```
|
|
13
15
|
|
|
14
|
-
## Features
|
|
15
|
-
|
|
16
|
-
- **Multisig Transaction Management**: Create and manage transactions requiring multiple signatures
|
|
17
|
-
- **Threshold Signatures**: Configurable minimum signature requirements (M-of-N)
|
|
18
|
-
- **Signature Collection**: Collect signatures from multiple parties before submission
|
|
19
|
-
- **Transaction Conversion**: Convert between V0 (single-sig) and V1 (multisig) transaction formats
|
|
20
|
-
- **Safety Checks**: Validates transaction consistency and prevents replay attacks
|
|
21
|
-
|
|
22
16
|
## Usage
|
|
23
17
|
|
|
24
|
-
### Basic Multisig Workflow
|
|
25
|
-
|
|
26
18
|
```typescript
|
|
27
|
-
import {
|
|
28
|
-
import {
|
|
19
|
+
import { Multisig } from "@sovereign-sdk/multisig";
|
|
20
|
+
import { createStandardRollup } from "@sovereign-sdk/web3";
|
|
29
21
|
import type { UnsignedTransaction } from "@sovereign-sdk/types";
|
|
22
|
+
import { bytesToHex } from "@sovereign-sdk/utils";
|
|
23
|
+
|
|
24
|
+
const rollup = await createStandardRollup<YourRuntimeCall>();
|
|
30
25
|
|
|
31
|
-
// 1. Create an unsigned transaction
|
|
32
26
|
const unsignedTx: UnsignedTransaction<YourRuntimeCall> = {
|
|
33
27
|
runtime_call: {
|
|
34
28
|
// Your rollup-specific call data
|
|
35
|
-
value_setter: { set_value: { value: 100, gas: null } },
|
|
36
29
|
},
|
|
37
|
-
uniqueness: { nonce: 1 },
|
|
30
|
+
uniqueness: { nonce: 1 },
|
|
38
31
|
details: {
|
|
39
|
-
max_priority_fee_bips:
|
|
32
|
+
max_priority_fee_bips: 0,
|
|
40
33
|
max_fee: "1000000",
|
|
41
34
|
gas_limit: null,
|
|
42
|
-
|
|
35
|
+
chain_hash_fragment: "6654161651848106779",
|
|
43
36
|
},
|
|
44
37
|
};
|
|
45
38
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
39
|
+
const multisig = Multisig.fromPubKeys(
|
|
40
|
+
["pubkey1hex", "pubkey2hex", "pubkey3hex"],
|
|
41
|
+
2,
|
|
42
|
+
);
|
|
49
43
|
|
|
50
|
-
const
|
|
44
|
+
const signingBytes = await rollup.multisigSigningBytes(unsignedTx, multisig);
|
|
45
|
+
multisig.addSignature(
|
|
46
|
+
bytesToHex(await signer1.sign(signingBytes)),
|
|
47
|
+
bytesToHex(await signer1.publicKey()),
|
|
48
|
+
);
|
|
51
49
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
50
|
+
multisig.addSignature(
|
|
51
|
+
bytesToHex(await signer2.sign(signingBytes)),
|
|
52
|
+
bytesToHex(await signer2.publicKey()),
|
|
53
|
+
);
|
|
55
54
|
|
|
56
|
-
// 4. Check if ready for submission
|
|
57
55
|
if (multisig.isComplete) {
|
|
58
|
-
|
|
59
|
-
const finalTx = multisig.asTransaction();
|
|
60
|
-
const rollup = await createStandardRollup();
|
|
61
|
-
// Submit the transaction to the rollup
|
|
62
|
-
const result = await rollup.submitTransaction(finalTx);
|
|
63
|
-
console.log("Multisig transaction submitted", result);
|
|
56
|
+
await rollup.submitTransaction(multisig.toTransaction(unsignedTx));
|
|
64
57
|
}
|
|
65
58
|
```
|
|
66
|
-
|
|
67
|
-
### Creating from Existing Signed Transactions
|
|
68
|
-
|
|
69
|
-
```typescript
|
|
70
|
-
import { MultisigTransaction } from '@sovereign-sdk/multisig';
|
|
71
|
-
import type { Transaction } from '@sovereign-sdk/types';
|
|
72
|
-
|
|
73
|
-
// If you have individual V0 transactions from different signers
|
|
74
|
-
const signedTxns: Transaction<YourRuntimeCall>[] = [
|
|
75
|
-
{
|
|
76
|
-
V0: {
|
|
77
|
-
pub_key: "0xpubkey1",
|
|
78
|
-
signature: "0xsignature1",
|
|
79
|
-
runtime_call: /* same call data */,
|
|
80
|
-
uniqueness: { nonce: 1 },
|
|
81
|
-
details: /* same details */,
|
|
82
|
-
}
|
|
83
|
-
},
|
|
84
|
-
{
|
|
85
|
-
V0: {
|
|
86
|
-
pub_key: "0xpubkey2",
|
|
87
|
-
signature: "0xsignature2",
|
|
88
|
-
runtime_call: /* same call data */,
|
|
89
|
-
uniqueness: { nonce: 1 },
|
|
90
|
-
details: /* same details */,
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
];
|
|
94
|
-
|
|
95
|
-
// Create multisig from existing transactions
|
|
96
|
-
const multisig = MultisigTransaction.fromTransactions({
|
|
97
|
-
txns: signedTxns,
|
|
98
|
-
minSigners: 2,
|
|
99
|
-
allPubKeys: ["0xpubkey1", "0xpubkey2", "0xpubkey3"]
|
|
100
|
-
});
|
|
101
|
-
```
|
|
102
|
-
|
|
103
|
-
### Adding Individual Signed Transactions
|
|
104
|
-
|
|
105
|
-
```typescript
|
|
106
|
-
// Add a complete signed transaction to the multisig
|
|
107
|
-
const signedTx: Transaction<YourRuntimeCall> = {
|
|
108
|
-
V0: {
|
|
109
|
-
pub_key: "0xpubkey3",
|
|
110
|
-
signature: "0xsignature3",
|
|
111
|
-
// ... same transaction data
|
|
112
|
-
},
|
|
113
|
-
};
|
|
114
|
-
|
|
115
|
-
multisig.addSignedTransaction(signedTx);
|
|
116
|
-
|
|
117
|
-
// Now check if complete and submit
|
|
118
|
-
if (multisig.isComplete) {
|
|
119
|
-
const finalTx = multisig.asTransaction();
|
|
120
|
-
// Submit to rollup...
|
|
121
|
-
}
|
|
122
|
-
```
|
|
123
|
-
|
|
124
|
-
## Important Notes
|
|
125
|
-
|
|
126
|
-
- **Nonce-based transactions only**: Multisig currently only supports transactions with `{ nonce: number }` uniqueness
|
|
127
|
-
- **Transaction consistency**: All signatures must be for the exact same unsigned transaction
|
|
128
|
-
- **Public key validation**: Public keys must be known members of the multisig and can only sign once
|
|
129
|
-
|
package/dist/index.d.mts
CHANGED
|
@@ -1,22 +1,12 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { SignatureAndPubKey, UnsignedTransaction, TransactionV1 } from '@sovereign-sdk/types';
|
|
2
2
|
import { HexString } from '@sovereign-sdk/utils';
|
|
3
3
|
|
|
4
|
+
declare const MAX_SIGNERS = 21;
|
|
4
5
|
/**
|
|
5
6
|
* Base error class for multisig-related errors.
|
|
6
7
|
*/
|
|
7
8
|
declare class MultisigError extends Error {
|
|
8
9
|
}
|
|
9
|
-
/**
|
|
10
|
-
* Error thrown when a transaction has an unsupported version for multisig operations.
|
|
11
|
-
*/
|
|
12
|
-
declare class InvalidTransactionVersionError extends MultisigError {
|
|
13
|
-
}
|
|
14
|
-
/**
|
|
15
|
-
* Error thrown when a provided transaction doesn't match the expected unsigned transaction.
|
|
16
|
-
*/
|
|
17
|
-
declare class TransactionMismatchError extends Error {
|
|
18
|
-
constructor();
|
|
19
|
-
}
|
|
20
10
|
/**
|
|
21
11
|
* Error thrown when multisig is constructed or used with invalid parameters.
|
|
22
12
|
*/
|
|
@@ -24,113 +14,33 @@ declare class InvalidMultisigParameterError extends MultisigError {
|
|
|
24
14
|
constructor(desc: string);
|
|
25
15
|
}
|
|
26
16
|
/**
|
|
27
|
-
* Parameters for constructing a
|
|
17
|
+
* Parameters for constructing a Multisig.
|
|
28
18
|
*/
|
|
29
19
|
type MultisigParams = {
|
|
30
|
-
/** The unsigned transaction to be signed by multiple parties */
|
|
31
|
-
unsignedTx: UnsignedTransaction<unknown>;
|
|
32
20
|
/** Array of signatures with pubkeys already collected */
|
|
33
|
-
signatures
|
|
21
|
+
signatures?: SignatureAndPubKey[];
|
|
34
22
|
/** Array of public keys that haven't signed yet */
|
|
35
23
|
unusedPubKeys: HexString[];
|
|
36
24
|
/** Minimum number of signatures required */
|
|
37
25
|
minSigners: number;
|
|
38
26
|
};
|
|
39
27
|
/**
|
|
40
|
-
*
|
|
28
|
+
* Represents multisig signer membership and collected signatures.
|
|
41
29
|
*/
|
|
42
|
-
|
|
43
|
-
/** Array of signed transactions to extract signatures from */
|
|
44
|
-
txns: Transaction<unknown>[];
|
|
45
|
-
/** Minimum number of signatures required */
|
|
46
|
-
minSigners: number;
|
|
47
|
-
/** All public keys that are members of the multisig */
|
|
48
|
-
allPubKeys: HexString[];
|
|
49
|
-
};
|
|
50
|
-
/**
|
|
51
|
-
* Represents a multisig transaction that collects signatures from multiple parties.
|
|
52
|
-
* Only supports nonce-based transactions and V0 transaction variants for signature collection.
|
|
53
|
-
*/
|
|
54
|
-
declare class MultisigTransaction {
|
|
55
|
-
private unsignedTx;
|
|
30
|
+
declare class Multisig {
|
|
56
31
|
private signatures;
|
|
57
32
|
private minSigners;
|
|
58
33
|
private unusedPubKeys;
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
* @param params - The multisig parameters including unsigned transaction, signatures, and public keys
|
|
62
|
-
*/
|
|
63
|
-
constructor({ unsignedTx, signatures, unusedPubKeys, minSigners, }: MultisigParams);
|
|
64
|
-
/**
|
|
65
|
-
* Creates an empty MultisigTransaction with no signatures collected yet.
|
|
66
|
-
* @param unsignedTx - The unsigned transaction to be signed by multiple parties
|
|
67
|
-
* @param minSigners - Minimum number of signatures required for completion
|
|
68
|
-
* @param allPubKeys - All public keys that are members of the multisig
|
|
69
|
-
* @returns A new empty MultisigTransaction instance
|
|
70
|
-
*/
|
|
71
|
-
static empty(unsignedTx: UnsignedTransaction<unknown>, minSigners: number, allPubKeys: HexString[]): MultisigTransaction;
|
|
72
|
-
/**
|
|
73
|
-
* Creates a MultisigTransaction from an array of already-signed transactions.
|
|
74
|
-
* Extracts signatures and validates that all transactions are identical except for signatures.
|
|
75
|
-
* @param params - Parameters including the signed transactions, minimum signers, and all public keys
|
|
76
|
-
* @returns A new MultisigTransaction instance
|
|
77
|
-
* @throws {InvalidTransactionVersionError} If any transaction is not V0 variant
|
|
78
|
-
* @throws {TransactionMismatchError} If transactions don't match each other
|
|
79
|
-
* @throws {InvalidMultisigParameterError} If public keys are invalid or transaction is not nonce-based
|
|
80
|
-
*/
|
|
81
|
-
static fromTransactions({ txns, minSigners, allPubKeys, }: FromTransactionsParams): MultisigTransaction;
|
|
82
|
-
/**
|
|
83
|
-
* Adds a signature & public key from a signed transaction to the multisig transaction.
|
|
84
|
-
* @param tx - The signed transaction to extract signature from
|
|
85
|
-
* @throws {InvalidTransactionVersionError} If the transaction is not V0 variant
|
|
86
|
-
* @throws {TransactionMismatchError} If the transaction doesn't match the unsigned transaction
|
|
87
|
-
* @throws {InvalidMultisigParameterError} If the public key is not a member or has already signed
|
|
88
|
-
*/
|
|
89
|
-
addSignedTransaction(tx: Transaction<unknown>): void;
|
|
90
|
-
/**
|
|
91
|
-
* Adds a signature to the multisig transaction.
|
|
92
|
-
* @param signature - The signature to add
|
|
93
|
-
* @param pubKey - The public key corresponding to the signature
|
|
94
|
-
* @throws {InvalidMultisigParameterError} If the public key is not a member or has already signed
|
|
95
|
-
*/
|
|
34
|
+
constructor({ signatures, unusedPubKeys, minSigners }: MultisigParams);
|
|
35
|
+
static fromPubKeys(allPubKeys: HexString[], minSigners: number): Multisig;
|
|
96
36
|
addSignature(signature: HexString, pubKey: HexString): void;
|
|
97
|
-
/**
|
|
98
|
-
* Checks if the multisig has collected enough signatures to be complete.
|
|
99
|
-
* @returns True if the number of signatures meets or exceeds the minimum required
|
|
100
|
-
*/
|
|
101
37
|
get isComplete(): boolean;
|
|
102
|
-
|
|
103
|
-
* Gets the unsigned transaction.
|
|
104
|
-
* @returns A readonly view of the unsigned transaction
|
|
105
|
-
*/
|
|
106
|
-
get unsignedTransaction(): Readonly<UnsignedTransaction<unknown>>;
|
|
107
|
-
/**
|
|
108
|
-
* Gets the signatures and public keys collected so far.
|
|
109
|
-
* @returns A readonly view of the signatures and public keys
|
|
110
|
-
*/
|
|
38
|
+
get threshold(): number;
|
|
111
39
|
get signaturesAndPubKeys(): Readonly<SignatureAndPubKey[]>;
|
|
112
|
-
/**
|
|
113
|
-
* Gets the remaining public keys that haven't signed yet.
|
|
114
|
-
* @returns A readonly view of the remaining public keys
|
|
115
|
-
*/
|
|
116
40
|
get remainingPubKeys(): Readonly<Set<HexString>>;
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
* @returns The threshold number of signatures
|
|
120
|
-
*/
|
|
121
|
-
get threshold(): number;
|
|
122
|
-
/**
|
|
123
|
-
* Calculates the multisig address (credential ID) by hashing the threshold and sorted public keys.
|
|
124
|
-
* This matches the Rust implementation: hash(threshold || borsh(sorted_pubkeys))
|
|
125
|
-
* @param hasher - The hash algorithm to use (currently only 'sha256' is supported)
|
|
126
|
-
* @returns The 32-byte multisig address as a Uint8Array
|
|
127
|
-
*/
|
|
41
|
+
get allPubKeys(): Readonly<HexString[]>;
|
|
42
|
+
toTransaction<RuntimeCall>(unsignedTx: UnsignedTransaction<RuntimeCall>): TransactionV1<RuntimeCall>;
|
|
128
43
|
getMultisigAddress(hasher?: "sha256"): Uint8Array;
|
|
129
|
-
/**
|
|
130
|
-
* Converts the multisig to a V1 transaction that can be submitted to the network.
|
|
131
|
-
* @returns A V1 transaction containing all collected signatures and unused public keys
|
|
132
|
-
*/
|
|
133
|
-
asTransaction(): Transaction<unknown>;
|
|
134
44
|
}
|
|
135
45
|
|
|
136
|
-
export {
|
|
46
|
+
export { InvalidMultisigParameterError, MAX_SIGNERS, Multisig, MultisigError, type MultisigParams };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,22 +1,12 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { SignatureAndPubKey, UnsignedTransaction, TransactionV1 } from '@sovereign-sdk/types';
|
|
2
2
|
import { HexString } from '@sovereign-sdk/utils';
|
|
3
3
|
|
|
4
|
+
declare const MAX_SIGNERS = 21;
|
|
4
5
|
/**
|
|
5
6
|
* Base error class for multisig-related errors.
|
|
6
7
|
*/
|
|
7
8
|
declare class MultisigError extends Error {
|
|
8
9
|
}
|
|
9
|
-
/**
|
|
10
|
-
* Error thrown when a transaction has an unsupported version for multisig operations.
|
|
11
|
-
*/
|
|
12
|
-
declare class InvalidTransactionVersionError extends MultisigError {
|
|
13
|
-
}
|
|
14
|
-
/**
|
|
15
|
-
* Error thrown when a provided transaction doesn't match the expected unsigned transaction.
|
|
16
|
-
*/
|
|
17
|
-
declare class TransactionMismatchError extends Error {
|
|
18
|
-
constructor();
|
|
19
|
-
}
|
|
20
10
|
/**
|
|
21
11
|
* Error thrown when multisig is constructed or used with invalid parameters.
|
|
22
12
|
*/
|
|
@@ -24,113 +14,33 @@ declare class InvalidMultisigParameterError extends MultisigError {
|
|
|
24
14
|
constructor(desc: string);
|
|
25
15
|
}
|
|
26
16
|
/**
|
|
27
|
-
* Parameters for constructing a
|
|
17
|
+
* Parameters for constructing a Multisig.
|
|
28
18
|
*/
|
|
29
19
|
type MultisigParams = {
|
|
30
|
-
/** The unsigned transaction to be signed by multiple parties */
|
|
31
|
-
unsignedTx: UnsignedTransaction<unknown>;
|
|
32
20
|
/** Array of signatures with pubkeys already collected */
|
|
33
|
-
signatures
|
|
21
|
+
signatures?: SignatureAndPubKey[];
|
|
34
22
|
/** Array of public keys that haven't signed yet */
|
|
35
23
|
unusedPubKeys: HexString[];
|
|
36
24
|
/** Minimum number of signatures required */
|
|
37
25
|
minSigners: number;
|
|
38
26
|
};
|
|
39
27
|
/**
|
|
40
|
-
*
|
|
28
|
+
* Represents multisig signer membership and collected signatures.
|
|
41
29
|
*/
|
|
42
|
-
|
|
43
|
-
/** Array of signed transactions to extract signatures from */
|
|
44
|
-
txns: Transaction<unknown>[];
|
|
45
|
-
/** Minimum number of signatures required */
|
|
46
|
-
minSigners: number;
|
|
47
|
-
/** All public keys that are members of the multisig */
|
|
48
|
-
allPubKeys: HexString[];
|
|
49
|
-
};
|
|
50
|
-
/**
|
|
51
|
-
* Represents a multisig transaction that collects signatures from multiple parties.
|
|
52
|
-
* Only supports nonce-based transactions and V0 transaction variants for signature collection.
|
|
53
|
-
*/
|
|
54
|
-
declare class MultisigTransaction {
|
|
55
|
-
private unsignedTx;
|
|
30
|
+
declare class Multisig {
|
|
56
31
|
private signatures;
|
|
57
32
|
private minSigners;
|
|
58
33
|
private unusedPubKeys;
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
* @param params - The multisig parameters including unsigned transaction, signatures, and public keys
|
|
62
|
-
*/
|
|
63
|
-
constructor({ unsignedTx, signatures, unusedPubKeys, minSigners, }: MultisigParams);
|
|
64
|
-
/**
|
|
65
|
-
* Creates an empty MultisigTransaction with no signatures collected yet.
|
|
66
|
-
* @param unsignedTx - The unsigned transaction to be signed by multiple parties
|
|
67
|
-
* @param minSigners - Minimum number of signatures required for completion
|
|
68
|
-
* @param allPubKeys - All public keys that are members of the multisig
|
|
69
|
-
* @returns A new empty MultisigTransaction instance
|
|
70
|
-
*/
|
|
71
|
-
static empty(unsignedTx: UnsignedTransaction<unknown>, minSigners: number, allPubKeys: HexString[]): MultisigTransaction;
|
|
72
|
-
/**
|
|
73
|
-
* Creates a MultisigTransaction from an array of already-signed transactions.
|
|
74
|
-
* Extracts signatures and validates that all transactions are identical except for signatures.
|
|
75
|
-
* @param params - Parameters including the signed transactions, minimum signers, and all public keys
|
|
76
|
-
* @returns A new MultisigTransaction instance
|
|
77
|
-
* @throws {InvalidTransactionVersionError} If any transaction is not V0 variant
|
|
78
|
-
* @throws {TransactionMismatchError} If transactions don't match each other
|
|
79
|
-
* @throws {InvalidMultisigParameterError} If public keys are invalid or transaction is not nonce-based
|
|
80
|
-
*/
|
|
81
|
-
static fromTransactions({ txns, minSigners, allPubKeys, }: FromTransactionsParams): MultisigTransaction;
|
|
82
|
-
/**
|
|
83
|
-
* Adds a signature & public key from a signed transaction to the multisig transaction.
|
|
84
|
-
* @param tx - The signed transaction to extract signature from
|
|
85
|
-
* @throws {InvalidTransactionVersionError} If the transaction is not V0 variant
|
|
86
|
-
* @throws {TransactionMismatchError} If the transaction doesn't match the unsigned transaction
|
|
87
|
-
* @throws {InvalidMultisigParameterError} If the public key is not a member or has already signed
|
|
88
|
-
*/
|
|
89
|
-
addSignedTransaction(tx: Transaction<unknown>): void;
|
|
90
|
-
/**
|
|
91
|
-
* Adds a signature to the multisig transaction.
|
|
92
|
-
* @param signature - The signature to add
|
|
93
|
-
* @param pubKey - The public key corresponding to the signature
|
|
94
|
-
* @throws {InvalidMultisigParameterError} If the public key is not a member or has already signed
|
|
95
|
-
*/
|
|
34
|
+
constructor({ signatures, unusedPubKeys, minSigners }: MultisigParams);
|
|
35
|
+
static fromPubKeys(allPubKeys: HexString[], minSigners: number): Multisig;
|
|
96
36
|
addSignature(signature: HexString, pubKey: HexString): void;
|
|
97
|
-
/**
|
|
98
|
-
* Checks if the multisig has collected enough signatures to be complete.
|
|
99
|
-
* @returns True if the number of signatures meets or exceeds the minimum required
|
|
100
|
-
*/
|
|
101
37
|
get isComplete(): boolean;
|
|
102
|
-
|
|
103
|
-
* Gets the unsigned transaction.
|
|
104
|
-
* @returns A readonly view of the unsigned transaction
|
|
105
|
-
*/
|
|
106
|
-
get unsignedTransaction(): Readonly<UnsignedTransaction<unknown>>;
|
|
107
|
-
/**
|
|
108
|
-
* Gets the signatures and public keys collected so far.
|
|
109
|
-
* @returns A readonly view of the signatures and public keys
|
|
110
|
-
*/
|
|
38
|
+
get threshold(): number;
|
|
111
39
|
get signaturesAndPubKeys(): Readonly<SignatureAndPubKey[]>;
|
|
112
|
-
/**
|
|
113
|
-
* Gets the remaining public keys that haven't signed yet.
|
|
114
|
-
* @returns A readonly view of the remaining public keys
|
|
115
|
-
*/
|
|
116
40
|
get remainingPubKeys(): Readonly<Set<HexString>>;
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
* @returns The threshold number of signatures
|
|
120
|
-
*/
|
|
121
|
-
get threshold(): number;
|
|
122
|
-
/**
|
|
123
|
-
* Calculates the multisig address (credential ID) by hashing the threshold and sorted public keys.
|
|
124
|
-
* This matches the Rust implementation: hash(threshold || borsh(sorted_pubkeys))
|
|
125
|
-
* @param hasher - The hash algorithm to use (currently only 'sha256' is supported)
|
|
126
|
-
* @returns The 32-byte multisig address as a Uint8Array
|
|
127
|
-
*/
|
|
41
|
+
get allPubKeys(): Readonly<HexString[]>;
|
|
42
|
+
toTransaction<RuntimeCall>(unsignedTx: UnsignedTransaction<RuntimeCall>): TransactionV1<RuntimeCall>;
|
|
128
43
|
getMultisigAddress(hasher?: "sha256"): Uint8Array;
|
|
129
|
-
/**
|
|
130
|
-
* Converts the multisig to a V1 transaction that can be submitted to the network.
|
|
131
|
-
* @returns A V1 transaction containing all collected signatures and unused public keys
|
|
132
|
-
*/
|
|
133
|
-
asTransaction(): Transaction<unknown>;
|
|
134
44
|
}
|
|
135
45
|
|
|
136
|
-
export {
|
|
46
|
+
export { InvalidMultisigParameterError, MAX_SIGNERS, Multisig, MultisigError, type MultisigParams };
|
package/dist/index.js
CHANGED
|
@@ -31,178 +31,92 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
31
31
|
var src_exports = {};
|
|
32
32
|
__export(src_exports, {
|
|
33
33
|
InvalidMultisigParameterError: () => InvalidMultisigParameterError,
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
TransactionMismatchError: () => TransactionMismatchError
|
|
34
|
+
MAX_SIGNERS: () => MAX_SIGNERS,
|
|
35
|
+
Multisig: () => Multisig,
|
|
36
|
+
MultisigError: () => MultisigError
|
|
38
37
|
});
|
|
39
38
|
module.exports = __toCommonJS(src_exports);
|
|
40
39
|
var import_sha2 = require("@noble/hashes/sha2");
|
|
41
40
|
var import_utils = require("@sovereign-sdk/utils");
|
|
42
41
|
var borsh = __toESM(require("borsh"));
|
|
42
|
+
var MAX_SIGNERS = 21;
|
|
43
43
|
var MultisigError = class extends Error {
|
|
44
44
|
};
|
|
45
|
-
var InvalidTransactionVersionError = class extends MultisigError {
|
|
46
|
-
};
|
|
47
|
-
var TransactionMismatchError = class extends Error {
|
|
48
|
-
constructor() {
|
|
49
|
-
super(
|
|
50
|
-
"Provided transaction does not match the expected unsigned transaction"
|
|
51
|
-
);
|
|
52
|
-
}
|
|
53
|
-
};
|
|
54
45
|
var InvalidMultisigParameterError = class extends MultisigError {
|
|
55
46
|
constructor(desc) {
|
|
56
47
|
super(`Multisig was constructed with invalid parameter: ${desc}`);
|
|
57
48
|
}
|
|
58
49
|
};
|
|
59
|
-
var
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
this.signatures =
|
|
71
|
-
this.unusedPubKeys =
|
|
72
|
-
this.unsignedTx = unsignedTx;
|
|
73
|
-
this.signatures = signatures;
|
|
74
|
-
this.unusedPubKeys = new Set(unusedPubKeys);
|
|
50
|
+
var Multisig = class _Multisig {
|
|
51
|
+
constructor({ signatures = [], unusedPubKeys, minSigners }) {
|
|
52
|
+
const normalizedSignatures = signatures.map(normalizeSignatureAndPubKey);
|
|
53
|
+
const normalizedUnusedPubKeys = unusedPubKeys.map(
|
|
54
|
+
(pubKey) => normalizeMultisigHex("public key", pubKey)
|
|
55
|
+
);
|
|
56
|
+
assertValidSignerSet(
|
|
57
|
+
normalizedSignatures,
|
|
58
|
+
normalizedUnusedPubKeys,
|
|
59
|
+
minSigners
|
|
60
|
+
);
|
|
61
|
+
this.signatures = normalizedSignatures;
|
|
62
|
+
this.unusedPubKeys = new Set(normalizedUnusedPubKeys);
|
|
75
63
|
this.minSigners = minSigners;
|
|
76
64
|
}
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
* @param unsignedTx - The unsigned transaction to be signed by multiple parties
|
|
80
|
-
* @param minSigners - Minimum number of signatures required for completion
|
|
81
|
-
* @param allPubKeys - All public keys that are members of the multisig
|
|
82
|
-
* @returns A new empty MultisigTransaction instance
|
|
83
|
-
*/
|
|
84
|
-
static empty(unsignedTx, minSigners, allPubKeys) {
|
|
85
|
-
return new _MultisigTransaction({
|
|
86
|
-
unsignedTx,
|
|
65
|
+
static fromPubKeys(allPubKeys, minSigners) {
|
|
66
|
+
return new _Multisig({
|
|
87
67
|
signatures: [],
|
|
88
68
|
unusedPubKeys: allPubKeys,
|
|
89
69
|
minSigners
|
|
90
70
|
});
|
|
91
71
|
}
|
|
92
|
-
/**
|
|
93
|
-
* Creates a MultisigTransaction from an array of already-signed transactions.
|
|
94
|
-
* Extracts signatures and validates that all transactions are identical except for signatures.
|
|
95
|
-
* @param params - Parameters including the signed transactions, minimum signers, and all public keys
|
|
96
|
-
* @returns A new MultisigTransaction instance
|
|
97
|
-
* @throws {InvalidTransactionVersionError} If any transaction is not V0 variant
|
|
98
|
-
* @throws {TransactionMismatchError} If transactions don't match each other
|
|
99
|
-
* @throws {InvalidMultisigParameterError} If public keys are invalid or transaction is not nonce-based
|
|
100
|
-
*/
|
|
101
|
-
static fromTransactions({
|
|
102
|
-
txns,
|
|
103
|
-
minSigners,
|
|
104
|
-
allPubKeys
|
|
105
|
-
}) {
|
|
106
|
-
const signatures = [];
|
|
107
|
-
const unsignedTx = asUnsignedTransaction(txns[0]);
|
|
108
|
-
const unusedPubKeys = new Set(allPubKeys);
|
|
109
|
-
assertIsNonceBasedTx(unsignedTx);
|
|
110
|
-
for (const tx of txns) {
|
|
111
|
-
assertTxVariant(tx);
|
|
112
|
-
assertTxMatchesUnsignedTx(tx, unsignedTx);
|
|
113
|
-
const { pub_key, signature } = tx.V0;
|
|
114
|
-
if (!unusedPubKeys.delete(pub_key)) {
|
|
115
|
-
throw new InvalidMultisigParameterError(
|
|
116
|
-
`Public key is not a member of the multisig or has already signed: ${pub_key}`
|
|
117
|
-
);
|
|
118
|
-
}
|
|
119
|
-
signatures.push({ pub_key, signature });
|
|
120
|
-
}
|
|
121
|
-
return new _MultisigTransaction({
|
|
122
|
-
unsignedTx,
|
|
123
|
-
signatures,
|
|
124
|
-
unusedPubKeys: Array.from(unusedPubKeys),
|
|
125
|
-
minSigners
|
|
126
|
-
});
|
|
127
|
-
}
|
|
128
|
-
/**
|
|
129
|
-
* Adds a signature & public key from a signed transaction to the multisig transaction.
|
|
130
|
-
* @param tx - The signed transaction to extract signature from
|
|
131
|
-
* @throws {InvalidTransactionVersionError} If the transaction is not V0 variant
|
|
132
|
-
* @throws {TransactionMismatchError} If the transaction doesn't match the unsigned transaction
|
|
133
|
-
* @throws {InvalidMultisigParameterError} If the public key is not a member or has already signed
|
|
134
|
-
*/
|
|
135
|
-
addSignedTransaction(tx) {
|
|
136
|
-
assertTxVariant(tx);
|
|
137
|
-
assertTxMatchesUnsignedTx(tx, this.unsignedTx);
|
|
138
|
-
const { pub_key, signature } = tx.V0;
|
|
139
|
-
this.addSignature(signature, pub_key);
|
|
140
|
-
}
|
|
141
|
-
/**
|
|
142
|
-
* Adds a signature to the multisig transaction.
|
|
143
|
-
* @param signature - The signature to add
|
|
144
|
-
* @param pubKey - The public key corresponding to the signature
|
|
145
|
-
* @throws {InvalidMultisigParameterError} If the public key is not a member or has already signed
|
|
146
|
-
*/
|
|
147
72
|
addSignature(signature, pubKey) {
|
|
148
|
-
|
|
73
|
+
const pair = normalizeSignatureAndPubKey({ signature, pub_key: pubKey });
|
|
74
|
+
if (!this.unusedPubKeys.delete(pair.pub_key)) {
|
|
149
75
|
throw new InvalidMultisigParameterError(
|
|
150
|
-
`Public key is not a member of the multisig or has already signed: ${
|
|
76
|
+
`Public key is not a member of the multisig or has already signed: ${pair.pub_key}`
|
|
151
77
|
);
|
|
152
78
|
}
|
|
153
|
-
this.signatures.push(
|
|
79
|
+
this.signatures.push(pair);
|
|
154
80
|
}
|
|
155
|
-
/**
|
|
156
|
-
* Checks if the multisig has collected enough signatures to be complete.
|
|
157
|
-
* @returns True if the number of signatures meets or exceeds the minimum required
|
|
158
|
-
*/
|
|
159
81
|
get isComplete() {
|
|
160
82
|
return this.signatures.length >= this.minSigners;
|
|
161
83
|
}
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
* @returns A readonly view of the unsigned transaction
|
|
165
|
-
*/
|
|
166
|
-
get unsignedTransaction() {
|
|
167
|
-
return this.unsignedTx;
|
|
84
|
+
get threshold() {
|
|
85
|
+
return this.minSigners;
|
|
168
86
|
}
|
|
169
|
-
/**
|
|
170
|
-
* Gets the signatures and public keys collected so far.
|
|
171
|
-
* @returns A readonly view of the signatures and public keys
|
|
172
|
-
*/
|
|
173
87
|
get signaturesAndPubKeys() {
|
|
174
|
-
return this.signatures;
|
|
88
|
+
return this.signatures.map((pair) => ({ ...pair }));
|
|
175
89
|
}
|
|
176
|
-
/**
|
|
177
|
-
* Gets the remaining public keys that haven't signed yet.
|
|
178
|
-
* @returns A readonly view of the remaining public keys
|
|
179
|
-
*/
|
|
180
90
|
get remainingPubKeys() {
|
|
181
|
-
return this.unusedPubKeys;
|
|
91
|
+
return new Set(this.unusedPubKeys);
|
|
182
92
|
}
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
93
|
+
get allPubKeys() {
|
|
94
|
+
return [
|
|
95
|
+
...this.signatures.map((signature) => signature.pub_key),
|
|
96
|
+
...this.unusedPubKeys
|
|
97
|
+
];
|
|
98
|
+
}
|
|
99
|
+
toTransaction(unsignedTx) {
|
|
100
|
+
if (!this.isComplete) {
|
|
101
|
+
throw new MultisigError("Multisig transaction is incomplete");
|
|
102
|
+
}
|
|
103
|
+
return {
|
|
104
|
+
V1: {
|
|
105
|
+
...unsignedTx,
|
|
106
|
+
signatures: [...this.signaturesAndPubKeys],
|
|
107
|
+
unused_pub_keys: [...this.remainingPubKeys],
|
|
108
|
+
min_signers: this.threshold
|
|
109
|
+
}
|
|
110
|
+
};
|
|
189
111
|
}
|
|
190
|
-
/**
|
|
191
|
-
* Calculates the multisig address (credential ID) by hashing the threshold and sorted public keys.
|
|
192
|
-
* This matches the Rust implementation: hash(threshold || borsh(sorted_pubkeys))
|
|
193
|
-
* @param hasher - The hash algorithm to use (currently only 'sha256' is supported)
|
|
194
|
-
* @returns The 32-byte multisig address as a Uint8Array
|
|
195
|
-
*/
|
|
196
112
|
getMultisigAddress(hasher = "sha256") {
|
|
197
113
|
if (hasher !== "sha256") {
|
|
198
114
|
throw new Error(`Unsupported hasher: ${hasher}`);
|
|
199
115
|
}
|
|
200
|
-
const
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
const sortedPubKeys = allPubKeys.sort();
|
|
205
|
-
const pubKeyBytes = sortedPubKeys.map((pk) => Array.from((0, import_utils.hexToBytes)(pk)));
|
|
116
|
+
const sortedPubKeys = [...this.allPubKeys].sort();
|
|
117
|
+
const pubKeyBytes = sortedPubKeys.map(
|
|
118
|
+
(pubKey) => Array.from((0, import_utils.hexToBytes)(pubKey))
|
|
119
|
+
);
|
|
206
120
|
const buffer = new borsh.BinaryWriter();
|
|
207
121
|
buffer.writeU8(this.minSigners);
|
|
208
122
|
buffer.writeArray(pubKeyBytes, (pkBytes) => {
|
|
@@ -210,68 +124,58 @@ var MultisigTransaction = class _MultisigTransaction {
|
|
|
210
124
|
});
|
|
211
125
|
return (0, import_sha2.sha256)(buffer.toArray());
|
|
212
126
|
}
|
|
213
|
-
/**
|
|
214
|
-
* Converts the multisig to a V1 transaction that can be submitted to the network.
|
|
215
|
-
* @returns A V1 transaction containing all collected signatures and unused public keys
|
|
216
|
-
*/
|
|
217
|
-
asTransaction() {
|
|
218
|
-
return {
|
|
219
|
-
V1: {
|
|
220
|
-
runtime_call: this.unsignedTx.runtime_call,
|
|
221
|
-
uniqueness: this.unsignedTx.uniqueness,
|
|
222
|
-
details: this.unsignedTx.details,
|
|
223
|
-
unused_pub_keys: Array.from(this.unusedPubKeys),
|
|
224
|
-
signatures: this.signatures,
|
|
225
|
-
min_signers: this.minSigners
|
|
226
|
-
}
|
|
227
|
-
};
|
|
228
|
-
}
|
|
229
127
|
};
|
|
230
|
-
function
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
128
|
+
function assertValidSignerSet(signatures, unusedPubKeys, minSigners) {
|
|
129
|
+
const seenPubKeys = /* @__PURE__ */ new Set();
|
|
130
|
+
const allPubKeys = [
|
|
131
|
+
...signatures.map((signature) => signature.pub_key),
|
|
132
|
+
...unusedPubKeys
|
|
133
|
+
];
|
|
134
|
+
if (!Number.isInteger(minSigners) || minSigners < 1) {
|
|
135
|
+
throw new InvalidMultisigParameterError(
|
|
136
|
+
`minSigners must be a positive integer, got ${minSigners}`
|
|
234
137
|
);
|
|
235
138
|
}
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
runtime_call: tx.V0.runtime_call,
|
|
241
|
-
uniqueness: tx.V0.uniqueness,
|
|
242
|
-
details: tx.V0.details
|
|
243
|
-
};
|
|
139
|
+
if (allPubKeys.length < 2 || allPubKeys.length > MAX_SIGNERS) {
|
|
140
|
+
throw new InvalidMultisigParameterError(
|
|
141
|
+
`expected 2-${MAX_SIGNERS} total signers, got ${allPubKeys.length}`
|
|
142
|
+
);
|
|
244
143
|
}
|
|
245
|
-
if (
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
details: tx.V1.details
|
|
250
|
-
};
|
|
144
|
+
if (minSigners > allPubKeys.length) {
|
|
145
|
+
throw new InvalidMultisigParameterError(
|
|
146
|
+
`minSigners cannot exceed total signer count: ${minSigners} > ${allPubKeys.length}`
|
|
147
|
+
);
|
|
251
148
|
}
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
throw new TransactionMismatchError();
|
|
149
|
+
for (const pubKey of allPubKeys) {
|
|
150
|
+
if (seenPubKeys.has(pubKey)) {
|
|
151
|
+
throw new InvalidMultisigParameterError(
|
|
152
|
+
`Duplicate multisig public key: ${pubKey}`
|
|
153
|
+
);
|
|
154
|
+
}
|
|
155
|
+
seenPubKeys.add(pubKey);
|
|
260
156
|
}
|
|
261
157
|
}
|
|
262
|
-
function
|
|
263
|
-
|
|
158
|
+
function normalizeSignatureAndPubKey(pair) {
|
|
159
|
+
return {
|
|
160
|
+
signature: normalizeMultisigHex("signature", pair.signature),
|
|
161
|
+
pub_key: normalizeMultisigHex("public key", pair.pub_key)
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
function normalizeMultisigHex(kind, value) {
|
|
165
|
+
try {
|
|
166
|
+
return (0, import_utils.normalizeHexString)(value);
|
|
167
|
+
} catch (error) {
|
|
168
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
264
169
|
throw new InvalidMultisigParameterError(
|
|
265
|
-
|
|
170
|
+
`Invalid multisig ${kind} ${value}: ${message}`
|
|
266
171
|
);
|
|
267
172
|
}
|
|
268
173
|
}
|
|
269
174
|
// Annotate the CommonJS export names for ESM import in node:
|
|
270
175
|
0 && (module.exports = {
|
|
271
176
|
InvalidMultisigParameterError,
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
TransactionMismatchError
|
|
177
|
+
MAX_SIGNERS,
|
|
178
|
+
Multisig,
|
|
179
|
+
MultisigError
|
|
276
180
|
});
|
|
277
181
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["import { sha256 } from \"@noble/hashes/sha2\";\nimport type {\n SignatureAndPubKey,\n Transaction,\n TransactionV0,\n UnsignedTransaction,\n} from \"@sovereign-sdk/types\";\nimport type { HexString } from \"@sovereign-sdk/utils\";\nimport { bytesToHex, hexToBytes } from \"@sovereign-sdk/utils\";\nimport * as borsh from \"borsh\";\n\n/**\n * Base error class for multisig-related errors.\n */\nexport class MultisigError extends Error {}\n\n/**\n * Error thrown when a transaction has an unsupported version for multisig operations.\n */\nexport class InvalidTransactionVersionError extends MultisigError {}\n\n/**\n * Error thrown when a provided transaction doesn't match the expected unsigned transaction.\n */\nexport class TransactionMismatchError extends Error {\n constructor() {\n super(\n \"Provided transaction does not match the expected unsigned transaction\",\n );\n }\n}\n\n/**\n * Error thrown when multisig is constructed or used with invalid parameters.\n */\nexport class InvalidMultisigParameterError extends MultisigError {\n constructor(desc: string) {\n super(`Multisig was constructed with invalid parameter: ${desc}`);\n }\n}\n\n/**\n * Parameters for constructing a MultisigTransaction.\n */\nexport type MultisigParams = {\n /** The unsigned transaction to be signed by multiple parties */\n unsignedTx: UnsignedTransaction<unknown>;\n /** Array of signatures with pubkeys already collected */\n signatures: SignatureAndPubKey[];\n /** Array of public keys that haven't signed yet */\n unusedPubKeys: HexString[];\n /** Minimum number of signatures required */\n minSigners: number;\n};\n\n/**\n * Parameters for creating a MultisigTransaction from existing signed transactions.\n */\nexport type FromTransactionsParams = {\n /** Array of signed transactions to extract signatures from */\n txns: Transaction<unknown>[];\n /** Minimum number of signatures required */\n minSigners: number;\n /** All public keys that are members of the multisig */\n allPubKeys: HexString[];\n};\n\n/**\n * Represents a multisig transaction that collects signatures from multiple parties.\n * Only supports nonce-based transactions and V0 transaction variants for signature collection.\n */\nexport class MultisigTransaction {\n private unsignedTx: UnsignedTransaction<unknown>;\n private signatures: SignatureAndPubKey[] = [];\n private minSigners: number;\n private unusedPubKeys = new Set<HexString>();\n\n /**\n * Creates a new MultisigTransaction instance.\n * @param params - The multisig parameters including unsigned transaction, signatures, and public keys\n */\n constructor({\n unsignedTx,\n signatures,\n unusedPubKeys,\n minSigners,\n }: MultisigParams) {\n this.unsignedTx = unsignedTx;\n this.signatures = signatures;\n this.unusedPubKeys = new Set(unusedPubKeys);\n this.minSigners = minSigners;\n }\n\n /**\n * Creates an empty MultisigTransaction with no signatures collected yet.\n * @param unsignedTx - The unsigned transaction to be signed by multiple parties\n * @param minSigners - Minimum number of signatures required for completion\n * @param allPubKeys - All public keys that are members of the multisig\n * @returns A new empty MultisigTransaction instance\n */\n static empty(\n unsignedTx: UnsignedTransaction<unknown>,\n minSigners: number,\n allPubKeys: HexString[],\n ): MultisigTransaction {\n return new MultisigTransaction({\n unsignedTx,\n signatures: [],\n unusedPubKeys: allPubKeys,\n minSigners,\n });\n }\n\n /**\n * Creates a MultisigTransaction from an array of already-signed transactions.\n * Extracts signatures and validates that all transactions are identical except for signatures.\n * @param params - Parameters including the signed transactions, minimum signers, and all public keys\n * @returns A new MultisigTransaction instance\n * @throws {InvalidTransactionVersionError} If any transaction is not V0 variant\n * @throws {TransactionMismatchError} If transactions don't match each other\n * @throws {InvalidMultisigParameterError} If public keys are invalid or transaction is not nonce-based\n */\n static fromTransactions({\n txns,\n minSigners,\n allPubKeys,\n }: FromTransactionsParams): MultisigTransaction {\n const signatures: SignatureAndPubKey[] = [];\n const unsignedTx = asUnsignedTransaction(txns[0]);\n const unusedPubKeys = new Set<HexString>(allPubKeys);\n\n assertIsNonceBasedTx(unsignedTx);\n\n for (const tx of txns) {\n assertTxVariant(tx);\n assertTxMatchesUnsignedTx(tx, unsignedTx);\n\n const { pub_key, signature } = tx.V0;\n\n if (!unusedPubKeys.delete(pub_key)) {\n throw new InvalidMultisigParameterError(\n `Public key is not a member of the multisig or has already signed: ${pub_key}`,\n );\n }\n\n signatures.push({ pub_key, signature });\n }\n\n return new MultisigTransaction({\n unsignedTx,\n signatures,\n unusedPubKeys: Array.from(unusedPubKeys),\n minSigners,\n });\n }\n\n /**\n * Adds a signature & public key from a signed transaction to the multisig transaction.\n * @param tx - The signed transaction to extract signature from\n * @throws {InvalidTransactionVersionError} If the transaction is not V0 variant\n * @throws {TransactionMismatchError} If the transaction doesn't match the unsigned transaction\n * @throws {InvalidMultisigParameterError} If the public key is not a member or has already signed\n */\n addSignedTransaction(tx: Transaction<unknown>): void {\n assertTxVariant(tx);\n assertTxMatchesUnsignedTx(tx, this.unsignedTx);\n\n const { pub_key, signature } = tx.V0;\n\n this.addSignature(signature, pub_key);\n }\n\n /**\n * Adds a signature to the multisig transaction.\n * @param signature - The signature to add\n * @param pubKey - The public key corresponding to the signature\n * @throws {InvalidMultisigParameterError} If the public key is not a member or has already signed\n */\n addSignature(signature: HexString, pubKey: HexString): void {\n if (!this.unusedPubKeys.delete(pubKey)) {\n throw new InvalidMultisigParameterError(\n `Public key is not a member of the multisig or has already signed: ${pubKey}`,\n );\n }\n\n this.signatures.push({ pub_key: pubKey, signature });\n }\n\n /**\n * Checks if the multisig has collected enough signatures to be complete.\n * @returns True if the number of signatures meets or exceeds the minimum required\n */\n get isComplete(): boolean {\n return this.signatures.length >= this.minSigners;\n }\n\n /**\n * Gets the unsigned transaction.\n * @returns A readonly view of the unsigned transaction\n */\n get unsignedTransaction(): Readonly<UnsignedTransaction<unknown>> {\n return this.unsignedTx;\n }\n\n /**\n * Gets the signatures and public keys collected so far.\n * @returns A readonly view of the signatures and public keys\n */\n get signaturesAndPubKeys(): Readonly<SignatureAndPubKey[]> {\n return this.signatures;\n }\n\n /**\n * Gets the remaining public keys that haven't signed yet.\n * @returns A readonly view of the remaining public keys\n */\n get remainingPubKeys(): Readonly<Set<HexString>> {\n return this.unusedPubKeys;\n }\n\n /**\n * Gets the threshold number of signatures required for the multisig to be complete.\n * @returns The threshold number of signatures\n */\n get threshold(): number {\n return this.minSigners;\n }\n\n /**\n * Calculates the multisig address (credential ID) by hashing the threshold and sorted public keys.\n * This matches the Rust implementation: hash(threshold || borsh(sorted_pubkeys))\n * @param hasher - The hash algorithm to use (currently only 'sha256' is supported)\n * @returns The 32-byte multisig address as a Uint8Array\n */\n getMultisigAddress(hasher: \"sha256\" = \"sha256\"): Uint8Array {\n if (hasher !== \"sha256\") {\n throw new Error(`Unsupported hasher: ${hasher}`);\n }\n\n // Collect all public keys (signatures + unused)\n const allPubKeys = [\n ...this.signatures.map((s) => s.pub_key),\n ...Array.from(this.unusedPubKeys),\n ];\n const sortedPubKeys = allPubKeys.sort();\n const pubKeyBytes = sortedPubKeys.map((pk) => Array.from(hexToBytes(pk)));\n const buffer = new borsh.BinaryWriter();\n\n buffer.writeU8(this.minSigners);\n buffer.writeArray(pubKeyBytes, (pkBytes: number[]) => {\n buffer.writeFixedArray(new Uint8Array(pkBytes));\n });\n\n return sha256(buffer.toArray());\n }\n\n /**\n * Converts the multisig to a V1 transaction that can be submitted to the network.\n * @returns A V1 transaction containing all collected signatures and unused public keys\n */\n asTransaction(): Transaction<unknown> {\n return {\n V1: {\n runtime_call: this.unsignedTx.runtime_call,\n uniqueness: this.unsignedTx.uniqueness,\n details: this.unsignedTx.details,\n unused_pub_keys: Array.from(this.unusedPubKeys),\n signatures: this.signatures,\n min_signers: this.minSigners,\n },\n };\n }\n}\n\nfunction assertTxVariant(\n tx: Transaction<unknown>,\n): asserts tx is TransactionV0<unknown> {\n if (\"V1\" in tx) {\n throw new InvalidTransactionVersionError(\n \"Input transaction was an unsupported variant (V1)\",\n );\n }\n}\n\nfunction asUnsignedTransaction(\n tx: Transaction<unknown>,\n): UnsignedTransaction<unknown> {\n if (\"V0\" in tx) {\n return {\n runtime_call: tx.V0.runtime_call,\n uniqueness: tx.V0.uniqueness,\n details: tx.V0.details,\n };\n }\n\n if (\"V1\" in tx) {\n return {\n runtime_call: tx.V1.runtime_call,\n uniqueness: tx.V1.uniqueness,\n details: tx.V1.details,\n };\n }\n\n throw new InvalidTransactionVersionError(\n \"Transaction variant is neither V0 nor V1\",\n );\n}\n\nfunction assertTxMatchesUnsignedTx(\n tx: Transaction<unknown>,\n unsignedTx: UnsignedTransaction<unknown>,\n): void {\n const txAsUnsigned = asUnsignedTransaction(tx);\n\n if (JSON.stringify(txAsUnsigned) !== JSON.stringify(unsignedTx)) {\n throw new TransactionMismatchError();\n }\n}\n\nfunction assertIsNonceBasedTx(unsignedTx: UnsignedTransaction<unknown>): void {\n if (!(\"nonce\" in unsignedTx.uniqueness)) {\n throw new InvalidMultisigParameterError(\n \"Only nonce-based transactions are supported for multisig\",\n );\n }\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["import { sha256 } from \"@noble/hashes/sha2\";\nimport type {\n SignatureAndPubKey,\n TransactionV1,\n UnsignedTransaction,\n} from \"@sovereign-sdk/types\";\nimport type { HexString } from \"@sovereign-sdk/utils\";\nimport { hexToBytes, normalizeHexString } from \"@sovereign-sdk/utils\";\nimport * as borsh from \"borsh\";\n\nexport const MAX_SIGNERS = 21;\n\n/**\n * Base error class for multisig-related errors.\n */\nexport class MultisigError extends Error {}\n\n/**\n * Error thrown when multisig is constructed or used with invalid parameters.\n */\nexport class InvalidMultisigParameterError extends MultisigError {\n constructor(desc: string) {\n super(`Multisig was constructed with invalid parameter: ${desc}`);\n }\n}\n\n/**\n * Parameters for constructing a Multisig.\n */\nexport type MultisigParams = {\n /** Array of signatures with pubkeys already collected */\n signatures?: SignatureAndPubKey[];\n /** Array of public keys that haven't signed yet */\n unusedPubKeys: HexString[];\n /** Minimum number of signatures required */\n minSigners: number;\n};\n\n/**\n * Represents multisig signer membership and collected signatures.\n */\nexport class Multisig {\n private signatures: SignatureAndPubKey[];\n private minSigners: number;\n private unusedPubKeys: Set<HexString>;\n\n constructor({ signatures = [], unusedPubKeys, minSigners }: MultisigParams) {\n const normalizedSignatures = signatures.map(normalizeSignatureAndPubKey);\n const normalizedUnusedPubKeys = unusedPubKeys.map((pubKey) =>\n normalizeMultisigHex(\"public key\", pubKey),\n );\n\n assertValidSignerSet(\n normalizedSignatures,\n normalizedUnusedPubKeys,\n minSigners,\n );\n\n this.signatures = normalizedSignatures;\n this.unusedPubKeys = new Set(normalizedUnusedPubKeys);\n this.minSigners = minSigners;\n }\n\n static fromPubKeys(allPubKeys: HexString[], minSigners: number): Multisig {\n return new Multisig({\n signatures: [],\n unusedPubKeys: allPubKeys,\n minSigners,\n });\n }\n\n addSignature(signature: HexString, pubKey: HexString): void {\n const pair = normalizeSignatureAndPubKey({ signature, pub_key: pubKey });\n\n if (!this.unusedPubKeys.delete(pair.pub_key)) {\n throw new InvalidMultisigParameterError(\n `Public key is not a member of the multisig or has already signed: ${pair.pub_key}`,\n );\n }\n\n this.signatures.push(pair);\n }\n\n get isComplete(): boolean {\n return this.signatures.length >= this.minSigners;\n }\n\n get threshold(): number {\n return this.minSigners;\n }\n\n get signaturesAndPubKeys(): Readonly<SignatureAndPubKey[]> {\n return this.signatures.map((pair) => ({ ...pair }));\n }\n\n get remainingPubKeys(): Readonly<Set<HexString>> {\n return new Set(this.unusedPubKeys);\n }\n\n get allPubKeys(): Readonly<HexString[]> {\n return [\n ...this.signatures.map((signature) => signature.pub_key),\n ...this.unusedPubKeys,\n ];\n }\n\n toTransaction<RuntimeCall>(\n unsignedTx: UnsignedTransaction<RuntimeCall>,\n ): TransactionV1<RuntimeCall> {\n if (!this.isComplete) {\n throw new MultisigError(\"Multisig transaction is incomplete\");\n }\n\n return {\n V1: {\n ...unsignedTx,\n signatures: [...this.signaturesAndPubKeys],\n unused_pub_keys: [...this.remainingPubKeys],\n min_signers: this.threshold,\n },\n };\n }\n\n getMultisigAddress(hasher: \"sha256\" = \"sha256\"): Uint8Array {\n if (hasher !== \"sha256\") {\n throw new Error(`Unsupported hasher: ${hasher}`);\n }\n\n const sortedPubKeys = [...this.allPubKeys].sort();\n const pubKeyBytes = sortedPubKeys.map((pubKey) =>\n Array.from(hexToBytes(pubKey)),\n );\n const buffer = new borsh.BinaryWriter();\n\n buffer.writeU8(this.minSigners);\n buffer.writeArray(pubKeyBytes, (pkBytes: number[]) => {\n buffer.writeFixedArray(new Uint8Array(pkBytes));\n });\n\n return sha256(buffer.toArray());\n }\n}\n\nfunction assertValidSignerSet(\n signatures: SignatureAndPubKey[],\n unusedPubKeys: HexString[],\n minSigners: number,\n): void {\n // `signatures` and `unusedPubKeys` are expected to already be normalized and\n // validated hex strings by the time this runs.\n const seenPubKeys = new Set<HexString>();\n const allPubKeys = [\n ...signatures.map((signature) => signature.pub_key),\n ...unusedPubKeys,\n ];\n\n if (!Number.isInteger(minSigners) || minSigners < 1) {\n throw new InvalidMultisigParameterError(\n `minSigners must be a positive integer, got ${minSigners}`,\n );\n }\n\n if (allPubKeys.length < 2 || allPubKeys.length > MAX_SIGNERS) {\n throw new InvalidMultisigParameterError(\n `expected 2-${MAX_SIGNERS} total signers, got ${allPubKeys.length}`,\n );\n }\n\n if (minSigners > allPubKeys.length) {\n throw new InvalidMultisigParameterError(\n `minSigners cannot exceed total signer count: ${minSigners} > ${allPubKeys.length}`,\n );\n }\n\n for (const pubKey of allPubKeys) {\n if (seenPubKeys.has(pubKey)) {\n throw new InvalidMultisigParameterError(\n `Duplicate multisig public key: ${pubKey}`,\n );\n }\n\n seenPubKeys.add(pubKey);\n }\n}\n\nfunction normalizeSignatureAndPubKey(\n pair: SignatureAndPubKey,\n): SignatureAndPubKey {\n return {\n signature: normalizeMultisigHex(\"signature\", pair.signature),\n pub_key: normalizeMultisigHex(\"public key\", pair.pub_key),\n };\n}\n\nfunction normalizeMultisigHex(kind: string, value: HexString): HexString {\n try {\n return normalizeHexString(value);\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n throw new InvalidMultisigParameterError(\n `Invalid multisig ${kind} ${value}: ${message}`,\n );\n }\n}\n"],"mappings":"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","names":[]}
|
package/dist/index.mjs
CHANGED
|
@@ -1,170 +1,85 @@
|
|
|
1
1
|
// src/index.ts
|
|
2
2
|
import { sha256 } from "@noble/hashes/sha2";
|
|
3
|
-
import { hexToBytes } from "@sovereign-sdk/utils";
|
|
3
|
+
import { hexToBytes, normalizeHexString } from "@sovereign-sdk/utils";
|
|
4
4
|
import * as borsh from "borsh";
|
|
5
|
+
var MAX_SIGNERS = 21;
|
|
5
6
|
var MultisigError = class extends Error {
|
|
6
7
|
};
|
|
7
|
-
var InvalidTransactionVersionError = class extends MultisigError {
|
|
8
|
-
};
|
|
9
|
-
var TransactionMismatchError = class extends Error {
|
|
10
|
-
constructor() {
|
|
11
|
-
super(
|
|
12
|
-
"Provided transaction does not match the expected unsigned transaction"
|
|
13
|
-
);
|
|
14
|
-
}
|
|
15
|
-
};
|
|
16
8
|
var InvalidMultisigParameterError = class extends MultisigError {
|
|
17
9
|
constructor(desc) {
|
|
18
10
|
super(`Multisig was constructed with invalid parameter: ${desc}`);
|
|
19
11
|
}
|
|
20
12
|
};
|
|
21
|
-
var
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
this.signatures =
|
|
33
|
-
this.unusedPubKeys =
|
|
34
|
-
this.unsignedTx = unsignedTx;
|
|
35
|
-
this.signatures = signatures;
|
|
36
|
-
this.unusedPubKeys = new Set(unusedPubKeys);
|
|
13
|
+
var Multisig = class _Multisig {
|
|
14
|
+
constructor({ signatures = [], unusedPubKeys, minSigners }) {
|
|
15
|
+
const normalizedSignatures = signatures.map(normalizeSignatureAndPubKey);
|
|
16
|
+
const normalizedUnusedPubKeys = unusedPubKeys.map(
|
|
17
|
+
(pubKey) => normalizeMultisigHex("public key", pubKey)
|
|
18
|
+
);
|
|
19
|
+
assertValidSignerSet(
|
|
20
|
+
normalizedSignatures,
|
|
21
|
+
normalizedUnusedPubKeys,
|
|
22
|
+
minSigners
|
|
23
|
+
);
|
|
24
|
+
this.signatures = normalizedSignatures;
|
|
25
|
+
this.unusedPubKeys = new Set(normalizedUnusedPubKeys);
|
|
37
26
|
this.minSigners = minSigners;
|
|
38
27
|
}
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
* @param unsignedTx - The unsigned transaction to be signed by multiple parties
|
|
42
|
-
* @param minSigners - Minimum number of signatures required for completion
|
|
43
|
-
* @param allPubKeys - All public keys that are members of the multisig
|
|
44
|
-
* @returns A new empty MultisigTransaction instance
|
|
45
|
-
*/
|
|
46
|
-
static empty(unsignedTx, minSigners, allPubKeys) {
|
|
47
|
-
return new _MultisigTransaction({
|
|
48
|
-
unsignedTx,
|
|
28
|
+
static fromPubKeys(allPubKeys, minSigners) {
|
|
29
|
+
return new _Multisig({
|
|
49
30
|
signatures: [],
|
|
50
31
|
unusedPubKeys: allPubKeys,
|
|
51
32
|
minSigners
|
|
52
33
|
});
|
|
53
34
|
}
|
|
54
|
-
/**
|
|
55
|
-
* Creates a MultisigTransaction from an array of already-signed transactions.
|
|
56
|
-
* Extracts signatures and validates that all transactions are identical except for signatures.
|
|
57
|
-
* @param params - Parameters including the signed transactions, minimum signers, and all public keys
|
|
58
|
-
* @returns A new MultisigTransaction instance
|
|
59
|
-
* @throws {InvalidTransactionVersionError} If any transaction is not V0 variant
|
|
60
|
-
* @throws {TransactionMismatchError} If transactions don't match each other
|
|
61
|
-
* @throws {InvalidMultisigParameterError} If public keys are invalid or transaction is not nonce-based
|
|
62
|
-
*/
|
|
63
|
-
static fromTransactions({
|
|
64
|
-
txns,
|
|
65
|
-
minSigners,
|
|
66
|
-
allPubKeys
|
|
67
|
-
}) {
|
|
68
|
-
const signatures = [];
|
|
69
|
-
const unsignedTx = asUnsignedTransaction(txns[0]);
|
|
70
|
-
const unusedPubKeys = new Set(allPubKeys);
|
|
71
|
-
assertIsNonceBasedTx(unsignedTx);
|
|
72
|
-
for (const tx of txns) {
|
|
73
|
-
assertTxVariant(tx);
|
|
74
|
-
assertTxMatchesUnsignedTx(tx, unsignedTx);
|
|
75
|
-
const { pub_key, signature } = tx.V0;
|
|
76
|
-
if (!unusedPubKeys.delete(pub_key)) {
|
|
77
|
-
throw new InvalidMultisigParameterError(
|
|
78
|
-
`Public key is not a member of the multisig or has already signed: ${pub_key}`
|
|
79
|
-
);
|
|
80
|
-
}
|
|
81
|
-
signatures.push({ pub_key, signature });
|
|
82
|
-
}
|
|
83
|
-
return new _MultisigTransaction({
|
|
84
|
-
unsignedTx,
|
|
85
|
-
signatures,
|
|
86
|
-
unusedPubKeys: Array.from(unusedPubKeys),
|
|
87
|
-
minSigners
|
|
88
|
-
});
|
|
89
|
-
}
|
|
90
|
-
/**
|
|
91
|
-
* Adds a signature & public key from a signed transaction to the multisig transaction.
|
|
92
|
-
* @param tx - The signed transaction to extract signature from
|
|
93
|
-
* @throws {InvalidTransactionVersionError} If the transaction is not V0 variant
|
|
94
|
-
* @throws {TransactionMismatchError} If the transaction doesn't match the unsigned transaction
|
|
95
|
-
* @throws {InvalidMultisigParameterError} If the public key is not a member or has already signed
|
|
96
|
-
*/
|
|
97
|
-
addSignedTransaction(tx) {
|
|
98
|
-
assertTxVariant(tx);
|
|
99
|
-
assertTxMatchesUnsignedTx(tx, this.unsignedTx);
|
|
100
|
-
const { pub_key, signature } = tx.V0;
|
|
101
|
-
this.addSignature(signature, pub_key);
|
|
102
|
-
}
|
|
103
|
-
/**
|
|
104
|
-
* Adds a signature to the multisig transaction.
|
|
105
|
-
* @param signature - The signature to add
|
|
106
|
-
* @param pubKey - The public key corresponding to the signature
|
|
107
|
-
* @throws {InvalidMultisigParameterError} If the public key is not a member or has already signed
|
|
108
|
-
*/
|
|
109
35
|
addSignature(signature, pubKey) {
|
|
110
|
-
|
|
36
|
+
const pair = normalizeSignatureAndPubKey({ signature, pub_key: pubKey });
|
|
37
|
+
if (!this.unusedPubKeys.delete(pair.pub_key)) {
|
|
111
38
|
throw new InvalidMultisigParameterError(
|
|
112
|
-
`Public key is not a member of the multisig or has already signed: ${
|
|
39
|
+
`Public key is not a member of the multisig or has already signed: ${pair.pub_key}`
|
|
113
40
|
);
|
|
114
41
|
}
|
|
115
|
-
this.signatures.push(
|
|
42
|
+
this.signatures.push(pair);
|
|
116
43
|
}
|
|
117
|
-
/**
|
|
118
|
-
* Checks if the multisig has collected enough signatures to be complete.
|
|
119
|
-
* @returns True if the number of signatures meets or exceeds the minimum required
|
|
120
|
-
*/
|
|
121
44
|
get isComplete() {
|
|
122
45
|
return this.signatures.length >= this.minSigners;
|
|
123
46
|
}
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
*/
|
|
128
|
-
get unsignedTransaction() {
|
|
129
|
-
return this.unsignedTx;
|
|
130
|
-
}
|
|
131
|
-
/**
|
|
132
|
-
* Gets the signatures and public keys collected so far.
|
|
133
|
-
* @returns A readonly view of the signatures and public keys
|
|
134
|
-
*/
|
|
47
|
+
get threshold() {
|
|
48
|
+
return this.minSigners;
|
|
49
|
+
}
|
|
135
50
|
get signaturesAndPubKeys() {
|
|
136
|
-
return this.signatures;
|
|
51
|
+
return this.signatures.map((pair) => ({ ...pair }));
|
|
137
52
|
}
|
|
138
|
-
/**
|
|
139
|
-
* Gets the remaining public keys that haven't signed yet.
|
|
140
|
-
* @returns A readonly view of the remaining public keys
|
|
141
|
-
*/
|
|
142
53
|
get remainingPubKeys() {
|
|
143
|
-
return this.unusedPubKeys;
|
|
54
|
+
return new Set(this.unusedPubKeys);
|
|
144
55
|
}
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
56
|
+
get allPubKeys() {
|
|
57
|
+
return [
|
|
58
|
+
...this.signatures.map((signature) => signature.pub_key),
|
|
59
|
+
...this.unusedPubKeys
|
|
60
|
+
];
|
|
61
|
+
}
|
|
62
|
+
toTransaction(unsignedTx) {
|
|
63
|
+
if (!this.isComplete) {
|
|
64
|
+
throw new MultisigError("Multisig transaction is incomplete");
|
|
65
|
+
}
|
|
66
|
+
return {
|
|
67
|
+
V1: {
|
|
68
|
+
...unsignedTx,
|
|
69
|
+
signatures: [...this.signaturesAndPubKeys],
|
|
70
|
+
unused_pub_keys: [...this.remainingPubKeys],
|
|
71
|
+
min_signers: this.threshold
|
|
72
|
+
}
|
|
73
|
+
};
|
|
151
74
|
}
|
|
152
|
-
/**
|
|
153
|
-
* Calculates the multisig address (credential ID) by hashing the threshold and sorted public keys.
|
|
154
|
-
* This matches the Rust implementation: hash(threshold || borsh(sorted_pubkeys))
|
|
155
|
-
* @param hasher - The hash algorithm to use (currently only 'sha256' is supported)
|
|
156
|
-
* @returns The 32-byte multisig address as a Uint8Array
|
|
157
|
-
*/
|
|
158
75
|
getMultisigAddress(hasher = "sha256") {
|
|
159
76
|
if (hasher !== "sha256") {
|
|
160
77
|
throw new Error(`Unsupported hasher: ${hasher}`);
|
|
161
78
|
}
|
|
162
|
-
const
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
const sortedPubKeys = allPubKeys.sort();
|
|
167
|
-
const pubKeyBytes = sortedPubKeys.map((pk) => Array.from(hexToBytes(pk)));
|
|
79
|
+
const sortedPubKeys = [...this.allPubKeys].sort();
|
|
80
|
+
const pubKeyBytes = sortedPubKeys.map(
|
|
81
|
+
(pubKey) => Array.from(hexToBytes(pubKey))
|
|
82
|
+
);
|
|
168
83
|
const buffer = new borsh.BinaryWriter();
|
|
169
84
|
buffer.writeU8(this.minSigners);
|
|
170
85
|
buffer.writeArray(pubKeyBytes, (pkBytes) => {
|
|
@@ -172,67 +87,57 @@ var MultisigTransaction = class _MultisigTransaction {
|
|
|
172
87
|
});
|
|
173
88
|
return sha256(buffer.toArray());
|
|
174
89
|
}
|
|
175
|
-
/**
|
|
176
|
-
* Converts the multisig to a V1 transaction that can be submitted to the network.
|
|
177
|
-
* @returns A V1 transaction containing all collected signatures and unused public keys
|
|
178
|
-
*/
|
|
179
|
-
asTransaction() {
|
|
180
|
-
return {
|
|
181
|
-
V1: {
|
|
182
|
-
runtime_call: this.unsignedTx.runtime_call,
|
|
183
|
-
uniqueness: this.unsignedTx.uniqueness,
|
|
184
|
-
details: this.unsignedTx.details,
|
|
185
|
-
unused_pub_keys: Array.from(this.unusedPubKeys),
|
|
186
|
-
signatures: this.signatures,
|
|
187
|
-
min_signers: this.minSigners
|
|
188
|
-
}
|
|
189
|
-
};
|
|
190
|
-
}
|
|
191
90
|
};
|
|
192
|
-
function
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
91
|
+
function assertValidSignerSet(signatures, unusedPubKeys, minSigners) {
|
|
92
|
+
const seenPubKeys = /* @__PURE__ */ new Set();
|
|
93
|
+
const allPubKeys = [
|
|
94
|
+
...signatures.map((signature) => signature.pub_key),
|
|
95
|
+
...unusedPubKeys
|
|
96
|
+
];
|
|
97
|
+
if (!Number.isInteger(minSigners) || minSigners < 1) {
|
|
98
|
+
throw new InvalidMultisigParameterError(
|
|
99
|
+
`minSigners must be a positive integer, got ${minSigners}`
|
|
196
100
|
);
|
|
197
101
|
}
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
runtime_call: tx.V0.runtime_call,
|
|
203
|
-
uniqueness: tx.V0.uniqueness,
|
|
204
|
-
details: tx.V0.details
|
|
205
|
-
};
|
|
102
|
+
if (allPubKeys.length < 2 || allPubKeys.length > MAX_SIGNERS) {
|
|
103
|
+
throw new InvalidMultisigParameterError(
|
|
104
|
+
`expected 2-${MAX_SIGNERS} total signers, got ${allPubKeys.length}`
|
|
105
|
+
);
|
|
206
106
|
}
|
|
207
|
-
if (
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
details: tx.V1.details
|
|
212
|
-
};
|
|
107
|
+
if (minSigners > allPubKeys.length) {
|
|
108
|
+
throw new InvalidMultisigParameterError(
|
|
109
|
+
`minSigners cannot exceed total signer count: ${minSigners} > ${allPubKeys.length}`
|
|
110
|
+
);
|
|
213
111
|
}
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
throw new TransactionMismatchError();
|
|
112
|
+
for (const pubKey of allPubKeys) {
|
|
113
|
+
if (seenPubKeys.has(pubKey)) {
|
|
114
|
+
throw new InvalidMultisigParameterError(
|
|
115
|
+
`Duplicate multisig public key: ${pubKey}`
|
|
116
|
+
);
|
|
117
|
+
}
|
|
118
|
+
seenPubKeys.add(pubKey);
|
|
222
119
|
}
|
|
223
120
|
}
|
|
224
|
-
function
|
|
225
|
-
|
|
121
|
+
function normalizeSignatureAndPubKey(pair) {
|
|
122
|
+
return {
|
|
123
|
+
signature: normalizeMultisigHex("signature", pair.signature),
|
|
124
|
+
pub_key: normalizeMultisigHex("public key", pair.pub_key)
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
function normalizeMultisigHex(kind, value) {
|
|
128
|
+
try {
|
|
129
|
+
return normalizeHexString(value);
|
|
130
|
+
} catch (error) {
|
|
131
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
226
132
|
throw new InvalidMultisigParameterError(
|
|
227
|
-
|
|
133
|
+
`Invalid multisig ${kind} ${value}: ${message}`
|
|
228
134
|
);
|
|
229
135
|
}
|
|
230
136
|
}
|
|
231
137
|
export {
|
|
232
138
|
InvalidMultisigParameterError,
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
TransactionMismatchError
|
|
139
|
+
MAX_SIGNERS,
|
|
140
|
+
Multisig,
|
|
141
|
+
MultisigError
|
|
237
142
|
};
|
|
238
143
|
//# sourceMappingURL=index.mjs.map
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["import { sha256 } from \"@noble/hashes/sha2\";\nimport type {\n SignatureAndPubKey,\n Transaction,\n TransactionV0,\n UnsignedTransaction,\n} from \"@sovereign-sdk/types\";\nimport type { HexString } from \"@sovereign-sdk/utils\";\nimport { bytesToHex, hexToBytes } from \"@sovereign-sdk/utils\";\nimport * as borsh from \"borsh\";\n\n/**\n * Base error class for multisig-related errors.\n */\nexport class MultisigError extends Error {}\n\n/**\n * Error thrown when a transaction has an unsupported version for multisig operations.\n */\nexport class InvalidTransactionVersionError extends MultisigError {}\n\n/**\n * Error thrown when a provided transaction doesn't match the expected unsigned transaction.\n */\nexport class TransactionMismatchError extends Error {\n constructor() {\n super(\n \"Provided transaction does not match the expected unsigned transaction\",\n );\n }\n}\n\n/**\n * Error thrown when multisig is constructed or used with invalid parameters.\n */\nexport class InvalidMultisigParameterError extends MultisigError {\n constructor(desc: string) {\n super(`Multisig was constructed with invalid parameter: ${desc}`);\n }\n}\n\n/**\n * Parameters for constructing a MultisigTransaction.\n */\nexport type MultisigParams = {\n /** The unsigned transaction to be signed by multiple parties */\n unsignedTx: UnsignedTransaction<unknown>;\n /** Array of signatures with pubkeys already collected */\n signatures: SignatureAndPubKey[];\n /** Array of public keys that haven't signed yet */\n unusedPubKeys: HexString[];\n /** Minimum number of signatures required */\n minSigners: number;\n};\n\n/**\n * Parameters for creating a MultisigTransaction from existing signed transactions.\n */\nexport type FromTransactionsParams = {\n /** Array of signed transactions to extract signatures from */\n txns: Transaction<unknown>[];\n /** Minimum number of signatures required */\n minSigners: number;\n /** All public keys that are members of the multisig */\n allPubKeys: HexString[];\n};\n\n/**\n * Represents a multisig transaction that collects signatures from multiple parties.\n * Only supports nonce-based transactions and V0 transaction variants for signature collection.\n */\nexport class MultisigTransaction {\n private unsignedTx: UnsignedTransaction<unknown>;\n private signatures: SignatureAndPubKey[] = [];\n private minSigners: number;\n private unusedPubKeys = new Set<HexString>();\n\n /**\n * Creates a new MultisigTransaction instance.\n * @param params - The multisig parameters including unsigned transaction, signatures, and public keys\n */\n constructor({\n unsignedTx,\n signatures,\n unusedPubKeys,\n minSigners,\n }: MultisigParams) {\n this.unsignedTx = unsignedTx;\n this.signatures = signatures;\n this.unusedPubKeys = new Set(unusedPubKeys);\n this.minSigners = minSigners;\n }\n\n /**\n * Creates an empty MultisigTransaction with no signatures collected yet.\n * @param unsignedTx - The unsigned transaction to be signed by multiple parties\n * @param minSigners - Minimum number of signatures required for completion\n * @param allPubKeys - All public keys that are members of the multisig\n * @returns A new empty MultisigTransaction instance\n */\n static empty(\n unsignedTx: UnsignedTransaction<unknown>,\n minSigners: number,\n allPubKeys: HexString[],\n ): MultisigTransaction {\n return new MultisigTransaction({\n unsignedTx,\n signatures: [],\n unusedPubKeys: allPubKeys,\n minSigners,\n });\n }\n\n /**\n * Creates a MultisigTransaction from an array of already-signed transactions.\n * Extracts signatures and validates that all transactions are identical except for signatures.\n * @param params - Parameters including the signed transactions, minimum signers, and all public keys\n * @returns A new MultisigTransaction instance\n * @throws {InvalidTransactionVersionError} If any transaction is not V0 variant\n * @throws {TransactionMismatchError} If transactions don't match each other\n * @throws {InvalidMultisigParameterError} If public keys are invalid or transaction is not nonce-based\n */\n static fromTransactions({\n txns,\n minSigners,\n allPubKeys,\n }: FromTransactionsParams): MultisigTransaction {\n const signatures: SignatureAndPubKey[] = [];\n const unsignedTx = asUnsignedTransaction(txns[0]);\n const unusedPubKeys = new Set<HexString>(allPubKeys);\n\n assertIsNonceBasedTx(unsignedTx);\n\n for (const tx of txns) {\n assertTxVariant(tx);\n assertTxMatchesUnsignedTx(tx, unsignedTx);\n\n const { pub_key, signature } = tx.V0;\n\n if (!unusedPubKeys.delete(pub_key)) {\n throw new InvalidMultisigParameterError(\n `Public key is not a member of the multisig or has already signed: ${pub_key}`,\n );\n }\n\n signatures.push({ pub_key, signature });\n }\n\n return new MultisigTransaction({\n unsignedTx,\n signatures,\n unusedPubKeys: Array.from(unusedPubKeys),\n minSigners,\n });\n }\n\n /**\n * Adds a signature & public key from a signed transaction to the multisig transaction.\n * @param tx - The signed transaction to extract signature from\n * @throws {InvalidTransactionVersionError} If the transaction is not V0 variant\n * @throws {TransactionMismatchError} If the transaction doesn't match the unsigned transaction\n * @throws {InvalidMultisigParameterError} If the public key is not a member or has already signed\n */\n addSignedTransaction(tx: Transaction<unknown>): void {\n assertTxVariant(tx);\n assertTxMatchesUnsignedTx(tx, this.unsignedTx);\n\n const { pub_key, signature } = tx.V0;\n\n this.addSignature(signature, pub_key);\n }\n\n /**\n * Adds a signature to the multisig transaction.\n * @param signature - The signature to add\n * @param pubKey - The public key corresponding to the signature\n * @throws {InvalidMultisigParameterError} If the public key is not a member or has already signed\n */\n addSignature(signature: HexString, pubKey: HexString): void {\n if (!this.unusedPubKeys.delete(pubKey)) {\n throw new InvalidMultisigParameterError(\n `Public key is not a member of the multisig or has already signed: ${pubKey}`,\n );\n }\n\n this.signatures.push({ pub_key: pubKey, signature });\n }\n\n /**\n * Checks if the multisig has collected enough signatures to be complete.\n * @returns True if the number of signatures meets or exceeds the minimum required\n */\n get isComplete(): boolean {\n return this.signatures.length >= this.minSigners;\n }\n\n /**\n * Gets the unsigned transaction.\n * @returns A readonly view of the unsigned transaction\n */\n get unsignedTransaction(): Readonly<UnsignedTransaction<unknown>> {\n return this.unsignedTx;\n }\n\n /**\n * Gets the signatures and public keys collected so far.\n * @returns A readonly view of the signatures and public keys\n */\n get signaturesAndPubKeys(): Readonly<SignatureAndPubKey[]> {\n return this.signatures;\n }\n\n /**\n * Gets the remaining public keys that haven't signed yet.\n * @returns A readonly view of the remaining public keys\n */\n get remainingPubKeys(): Readonly<Set<HexString>> {\n return this.unusedPubKeys;\n }\n\n /**\n * Gets the threshold number of signatures required for the multisig to be complete.\n * @returns The threshold number of signatures\n */\n get threshold(): number {\n return this.minSigners;\n }\n\n /**\n * Calculates the multisig address (credential ID) by hashing the threshold and sorted public keys.\n * This matches the Rust implementation: hash(threshold || borsh(sorted_pubkeys))\n * @param hasher - The hash algorithm to use (currently only 'sha256' is supported)\n * @returns The 32-byte multisig address as a Uint8Array\n */\n getMultisigAddress(hasher: \"sha256\" = \"sha256\"): Uint8Array {\n if (hasher !== \"sha256\") {\n throw new Error(`Unsupported hasher: ${hasher}`);\n }\n\n // Collect all public keys (signatures + unused)\n const allPubKeys = [\n ...this.signatures.map((s) => s.pub_key),\n ...Array.from(this.unusedPubKeys),\n ];\n const sortedPubKeys = allPubKeys.sort();\n const pubKeyBytes = sortedPubKeys.map((pk) => Array.from(hexToBytes(pk)));\n const buffer = new borsh.BinaryWriter();\n\n buffer.writeU8(this.minSigners);\n buffer.writeArray(pubKeyBytes, (pkBytes: number[]) => {\n buffer.writeFixedArray(new Uint8Array(pkBytes));\n });\n\n return sha256(buffer.toArray());\n }\n\n /**\n * Converts the multisig to a V1 transaction that can be submitted to the network.\n * @returns A V1 transaction containing all collected signatures and unused public keys\n */\n asTransaction(): Transaction<unknown> {\n return {\n V1: {\n runtime_call: this.unsignedTx.runtime_call,\n uniqueness: this.unsignedTx.uniqueness,\n details: this.unsignedTx.details,\n unused_pub_keys: Array.from(this.unusedPubKeys),\n signatures: this.signatures,\n min_signers: this.minSigners,\n },\n };\n }\n}\n\nfunction assertTxVariant(\n tx: Transaction<unknown>,\n): asserts tx is TransactionV0<unknown> {\n if (\"V1\" in tx) {\n throw new InvalidTransactionVersionError(\n \"Input transaction was an unsupported variant (V1)\",\n );\n }\n}\n\nfunction asUnsignedTransaction(\n tx: Transaction<unknown>,\n): UnsignedTransaction<unknown> {\n if (\"V0\" in tx) {\n return {\n runtime_call: tx.V0.runtime_call,\n uniqueness: tx.V0.uniqueness,\n details: tx.V0.details,\n };\n }\n\n if (\"V1\" in tx) {\n return {\n runtime_call: tx.V1.runtime_call,\n uniqueness: tx.V1.uniqueness,\n details: tx.V1.details,\n };\n }\n\n throw new InvalidTransactionVersionError(\n \"Transaction variant is neither V0 nor V1\",\n );\n}\n\nfunction assertTxMatchesUnsignedTx(\n tx: Transaction<unknown>,\n unsignedTx: UnsignedTransaction<unknown>,\n): void {\n const txAsUnsigned = asUnsignedTransaction(tx);\n\n if (JSON.stringify(txAsUnsigned) !== JSON.stringify(unsignedTx)) {\n throw new TransactionMismatchError();\n }\n}\n\nfunction assertIsNonceBasedTx(unsignedTx: UnsignedTransaction<unknown>): void {\n if (!(\"nonce\" in unsignedTx.uniqueness)) {\n throw new InvalidMultisigParameterError(\n \"Only nonce-based transactions are supported for multisig\",\n );\n }\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["import { sha256 } from \"@noble/hashes/sha2\";\nimport type {\n SignatureAndPubKey,\n TransactionV1,\n UnsignedTransaction,\n} from \"@sovereign-sdk/types\";\nimport type { HexString } from \"@sovereign-sdk/utils\";\nimport { hexToBytes, normalizeHexString } from \"@sovereign-sdk/utils\";\nimport * as borsh from \"borsh\";\n\nexport const MAX_SIGNERS = 21;\n\n/**\n * Base error class for multisig-related errors.\n */\nexport class MultisigError extends Error {}\n\n/**\n * Error thrown when multisig is constructed or used with invalid parameters.\n */\nexport class InvalidMultisigParameterError extends MultisigError {\n constructor(desc: string) {\n super(`Multisig was constructed with invalid parameter: ${desc}`);\n }\n}\n\n/**\n * Parameters for constructing a Multisig.\n */\nexport type MultisigParams = {\n /** Array of signatures with pubkeys already collected */\n signatures?: SignatureAndPubKey[];\n /** Array of public keys that haven't signed yet */\n unusedPubKeys: HexString[];\n /** Minimum number of signatures required */\n minSigners: number;\n};\n\n/**\n * Represents multisig signer membership and collected signatures.\n */\nexport class Multisig {\n private signatures: SignatureAndPubKey[];\n private minSigners: number;\n private unusedPubKeys: Set<HexString>;\n\n constructor({ signatures = [], unusedPubKeys, minSigners }: MultisigParams) {\n const normalizedSignatures = signatures.map(normalizeSignatureAndPubKey);\n const normalizedUnusedPubKeys = unusedPubKeys.map((pubKey) =>\n normalizeMultisigHex(\"public key\", pubKey),\n );\n\n assertValidSignerSet(\n normalizedSignatures,\n normalizedUnusedPubKeys,\n minSigners,\n );\n\n this.signatures = normalizedSignatures;\n this.unusedPubKeys = new Set(normalizedUnusedPubKeys);\n this.minSigners = minSigners;\n }\n\n static fromPubKeys(allPubKeys: HexString[], minSigners: number): Multisig {\n return new Multisig({\n signatures: [],\n unusedPubKeys: allPubKeys,\n minSigners,\n });\n }\n\n addSignature(signature: HexString, pubKey: HexString): void {\n const pair = normalizeSignatureAndPubKey({ signature, pub_key: pubKey });\n\n if (!this.unusedPubKeys.delete(pair.pub_key)) {\n throw new InvalidMultisigParameterError(\n `Public key is not a member of the multisig or has already signed: ${pair.pub_key}`,\n );\n }\n\n this.signatures.push(pair);\n }\n\n get isComplete(): boolean {\n return this.signatures.length >= this.minSigners;\n }\n\n get threshold(): number {\n return this.minSigners;\n }\n\n get signaturesAndPubKeys(): Readonly<SignatureAndPubKey[]> {\n return this.signatures.map((pair) => ({ ...pair }));\n }\n\n get remainingPubKeys(): Readonly<Set<HexString>> {\n return new Set(this.unusedPubKeys);\n }\n\n get allPubKeys(): Readonly<HexString[]> {\n return [\n ...this.signatures.map((signature) => signature.pub_key),\n ...this.unusedPubKeys,\n ];\n }\n\n toTransaction<RuntimeCall>(\n unsignedTx: UnsignedTransaction<RuntimeCall>,\n ): TransactionV1<RuntimeCall> {\n if (!this.isComplete) {\n throw new MultisigError(\"Multisig transaction is incomplete\");\n }\n\n return {\n V1: {\n ...unsignedTx,\n signatures: [...this.signaturesAndPubKeys],\n unused_pub_keys: [...this.remainingPubKeys],\n min_signers: this.threshold,\n },\n };\n }\n\n getMultisigAddress(hasher: \"sha256\" = \"sha256\"): Uint8Array {\n if (hasher !== \"sha256\") {\n throw new Error(`Unsupported hasher: ${hasher}`);\n }\n\n const sortedPubKeys = [...this.allPubKeys].sort();\n const pubKeyBytes = sortedPubKeys.map((pubKey) =>\n Array.from(hexToBytes(pubKey)),\n );\n const buffer = new borsh.BinaryWriter();\n\n buffer.writeU8(this.minSigners);\n buffer.writeArray(pubKeyBytes, (pkBytes: number[]) => {\n buffer.writeFixedArray(new Uint8Array(pkBytes));\n });\n\n return sha256(buffer.toArray());\n }\n}\n\nfunction assertValidSignerSet(\n signatures: SignatureAndPubKey[],\n unusedPubKeys: HexString[],\n minSigners: number,\n): void {\n // `signatures` and `unusedPubKeys` are expected to already be normalized and\n // validated hex strings by the time this runs.\n const seenPubKeys = new Set<HexString>();\n const allPubKeys = [\n ...signatures.map((signature) => signature.pub_key),\n ...unusedPubKeys,\n ];\n\n if (!Number.isInteger(minSigners) || minSigners < 1) {\n throw new InvalidMultisigParameterError(\n `minSigners must be a positive integer, got ${minSigners}`,\n );\n }\n\n if (allPubKeys.length < 2 || allPubKeys.length > MAX_SIGNERS) {\n throw new InvalidMultisigParameterError(\n `expected 2-${MAX_SIGNERS} total signers, got ${allPubKeys.length}`,\n );\n }\n\n if (minSigners > allPubKeys.length) {\n throw new InvalidMultisigParameterError(\n `minSigners cannot exceed total signer count: ${minSigners} > ${allPubKeys.length}`,\n );\n }\n\n for (const pubKey of allPubKeys) {\n if (seenPubKeys.has(pubKey)) {\n throw new InvalidMultisigParameterError(\n `Duplicate multisig public key: ${pubKey}`,\n );\n }\n\n seenPubKeys.add(pubKey);\n }\n}\n\nfunction normalizeSignatureAndPubKey(\n pair: SignatureAndPubKey,\n): SignatureAndPubKey {\n return {\n signature: normalizeMultisigHex(\"signature\", pair.signature),\n pub_key: normalizeMultisigHex(\"public key\", pair.pub_key),\n };\n}\n\nfunction normalizeMultisigHex(kind: string, value: HexString): HexString {\n try {\n return normalizeHexString(value);\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n throw new InvalidMultisigParameterError(\n `Invalid multisig ${kind} ${value}: ${message}`,\n );\n }\n}\n"],"mappings":";AAAA,SAAS,cAAc;AAOvB,SAAS,YAAY,0BAA0B;AAC/C,YAAY,WAAW;AAEhB,IAAM,cAAc;AAKpB,IAAM,gBAAN,cAA4B,MAAM;AAAC;AAKnC,IAAM,gCAAN,cAA4C,cAAc;AAAA,EAC/D,YAAY,MAAc;AACxB,UAAM,oDAAoD,IAAI,EAAE;AAAA,EAClE;AACF;AAiBO,IAAM,WAAN,MAAM,UAAS;AAAA,EAKpB,YAAY,EAAE,aAAa,CAAC,GAAG,eAAe,WAAW,GAAmB;AAC1E,UAAM,uBAAuB,WAAW,IAAI,2BAA2B;AACvE,UAAM,0BAA0B,cAAc;AAAA,MAAI,CAAC,WACjD,qBAAqB,cAAc,MAAM;AAAA,IAC3C;AAEA;AAAA,MACE;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAEA,SAAK,aAAa;AAClB,SAAK,gBAAgB,IAAI,IAAI,uBAAuB;AACpD,SAAK,aAAa;AAAA,EACpB;AAAA,EAEA,OAAO,YAAY,YAAyB,YAA8B;AACxE,WAAO,IAAI,UAAS;AAAA,MAClB,YAAY,CAAC;AAAA,MACb,eAAe;AAAA,MACf;AAAA,IACF,CAAC;AAAA,EACH;AAAA,EAEA,aAAa,WAAsB,QAAyB;AAC1D,UAAM,OAAO,4BAA4B,EAAE,WAAW,SAAS,OAAO,CAAC;AAEvE,QAAI,CAAC,KAAK,cAAc,OAAO,KAAK,OAAO,GAAG;AAC5C,YAAM,IAAI;AAAA,QACR,qEAAqE,KAAK,OAAO;AAAA,MACnF;AAAA,IACF;AAEA,SAAK,WAAW,KAAK,IAAI;AAAA,EAC3B;AAAA,EAEA,IAAI,aAAsB;AACxB,WAAO,KAAK,WAAW,UAAU,KAAK;AAAA,EACxC;AAAA,EAEA,IAAI,YAAoB;AACtB,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,IAAI,uBAAuD;AACzD,WAAO,KAAK,WAAW,IAAI,CAAC,UAAU,EAAE,GAAG,KAAK,EAAE;AAAA,EACpD;AAAA,EAEA,IAAI,mBAA6C;AAC/C,WAAO,IAAI,IAAI,KAAK,aAAa;AAAA,EACnC;AAAA,EAEA,IAAI,aAAoC;AACtC,WAAO;AAAA,MACL,GAAG,KAAK,WAAW,IAAI,CAAC,cAAc,UAAU,OAAO;AAAA,MACvD,GAAG,KAAK;AAAA,IACV;AAAA,EACF;AAAA,EAEA,cACE,YAC4B;AAC5B,QAAI,CAAC,KAAK,YAAY;AACpB,YAAM,IAAI,cAAc,oCAAoC;AAAA,IAC9D;AAEA,WAAO;AAAA,MACL,IAAI;AAAA,QACF,GAAG;AAAA,QACH,YAAY,CAAC,GAAG,KAAK,oBAAoB;AAAA,QACzC,iBAAiB,CAAC,GAAG,KAAK,gBAAgB;AAAA,QAC1C,aAAa,KAAK;AAAA,MACpB;AAAA,IACF;AAAA,EACF;AAAA,EAEA,mBAAmB,SAAmB,UAAsB;AAC1D,QAAI,WAAW,UAAU;AACvB,YAAM,IAAI,MAAM,uBAAuB,MAAM,EAAE;AAAA,IACjD;AAEA,UAAM,gBAAgB,CAAC,GAAG,KAAK,UAAU,EAAE,KAAK;AAChD,UAAM,cAAc,cAAc;AAAA,MAAI,CAAC,WACrC,MAAM,KAAK,WAAW,MAAM,CAAC;AAAA,IAC/B;AACA,UAAM,SAAS,IAAU,mBAAa;AAEtC,WAAO,QAAQ,KAAK,UAAU;AAC9B,WAAO,WAAW,aAAa,CAAC,YAAsB;AACpD,aAAO,gBAAgB,IAAI,WAAW,OAAO,CAAC;AAAA,IAChD,CAAC;AAED,WAAO,OAAO,OAAO,QAAQ,CAAC;AAAA,EAChC;AACF;AAEA,SAAS,qBACP,YACA,eACA,YACM;AAGN,QAAM,cAAc,oBAAI,IAAe;AACvC,QAAM,aAAa;AAAA,IACjB,GAAG,WAAW,IAAI,CAAC,cAAc,UAAU,OAAO;AAAA,IAClD,GAAG;AAAA,EACL;AAEA,MAAI,CAAC,OAAO,UAAU,UAAU,KAAK,aAAa,GAAG;AACnD,UAAM,IAAI;AAAA,MACR,8CAA8C,UAAU;AAAA,IAC1D;AAAA,EACF;AAEA,MAAI,WAAW,SAAS,KAAK,WAAW,SAAS,aAAa;AAC5D,UAAM,IAAI;AAAA,MACR,cAAc,WAAW,uBAAuB,WAAW,MAAM;AAAA,IACnE;AAAA,EACF;AAEA,MAAI,aAAa,WAAW,QAAQ;AAClC,UAAM,IAAI;AAAA,MACR,gDAAgD,UAAU,MAAM,WAAW,MAAM;AAAA,IACnF;AAAA,EACF;AAEA,aAAW,UAAU,YAAY;AAC/B,QAAI,YAAY,IAAI,MAAM,GAAG;AAC3B,YAAM,IAAI;AAAA,QACR,kCAAkC,MAAM;AAAA,MAC1C;AAAA,IACF;AAEA,gBAAY,IAAI,MAAM;AAAA,EACxB;AACF;AAEA,SAAS,4BACP,MACoB;AACpB,SAAO;AAAA,IACL,WAAW,qBAAqB,aAAa,KAAK,SAAS;AAAA,IAC3D,SAAS,qBAAqB,cAAc,KAAK,OAAO;AAAA,EAC1D;AACF;AAEA,SAAS,qBAAqB,MAAc,OAA6B;AACvE,MAAI;AACF,WAAO,mBAAmB,KAAK;AAAA,EACjC,SAAS,OAAO;AACd,UAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AACrE,UAAM,IAAI;AAAA,MACR,oBAAoB,IAAI,IAAI,KAAK,KAAK,OAAO;AAAA,IAC/C;AAAA,EACF;AACF;","names":[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sovereign-sdk/multisig",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "Multisig functionality for Sovereign SDK applications",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -25,8 +25,7 @@
|
|
|
25
25
|
"tsup": "^8.3.0",
|
|
26
26
|
"typescript": "^5.6.3",
|
|
27
27
|
"vitest": "4.0.7",
|
|
28
|
-
"@sovereign-sdk/types": "^0.
|
|
29
|
-
"@sovereign-sdk/web3": "^0.14.1"
|
|
28
|
+
"@sovereign-sdk/types": "^0.2.0"
|
|
30
29
|
},
|
|
31
30
|
"peerDependencies": {
|
|
32
31
|
"tsup": "^8.3.0",
|
|
@@ -38,8 +37,7 @@
|
|
|
38
37
|
"dependencies": {
|
|
39
38
|
"@noble/hashes": "^1.5.0",
|
|
40
39
|
"borsh": "0.7.0",
|
|
41
|
-
"@sovereign-sdk/
|
|
42
|
-
"@sovereign-sdk/utils": "^0.0.4"
|
|
40
|
+
"@sovereign-sdk/utils": "^0.1.0"
|
|
43
41
|
},
|
|
44
42
|
"scripts": {
|
|
45
43
|
"build": "tsup",
|
|
@@ -47,7 +45,6 @@
|
|
|
47
45
|
"fix": "biome check --write",
|
|
48
46
|
"lint": "biome lint",
|
|
49
47
|
"test": "vitest run **/*.test.ts",
|
|
50
|
-
"test:integration": "vitest run **/*.integration-test.ts",
|
|
51
48
|
"typecheck": "tsc --noEmit"
|
|
52
49
|
}
|
|
53
50
|
}
|