shell-sdk 0.6.0 → 0.7.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/CHANGELOG.md +53 -0
- package/dist/adapters.d.ts +4 -6
- package/dist/adapters.js +4 -6
- package/dist/address.d.ts +5 -32
- package/dist/address.js +10 -62
- package/dist/index.d.ts +2 -2
- package/dist/index.js +2 -2
- package/dist/keystore.d.ts +3 -8
- package/dist/keystore.js +17 -26
- package/dist/provider.d.ts +2 -2
- package/dist/provider.js +2 -2
- package/dist/signer.d.ts +5 -13
- package/dist/signer.js +12 -20
- package/dist/system-contracts.d.ts +7 -15
- package/dist/system-contracts.js +13 -29
- package/dist/transactions.js +6 -6
- package/dist/types.d.ts +2 -2
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,58 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.7.0] — 2026-04-30 ⚠️ BREAKING
|
|
4
|
+
|
|
5
|
+
### Breaking Changes — F-PQ1-ONLY (pq1 bech32m addresses everywhere)
|
|
6
|
+
|
|
7
|
+
This is a **breaking release**. All `0x` hex address support has been removed.
|
|
8
|
+
|
|
9
|
+
#### Removed
|
|
10
|
+
- `hexAddress()` / `hexAddressFromPubkey()` exports deleted — use `pq1Address()` instead.
|
|
11
|
+
- `normalizePqAddress()` no longer accepts `0x...` hex strings — only `pq1...` bech32m.
|
|
12
|
+
- Keystore `address` field: `encryptKeystore()` now always emits `pq1...` bech32m (was `0x...` hex).
|
|
13
|
+
- `decryptKeystore()` rejects keystores with `0x` address fields (use `shell-node key migrate` first).
|
|
14
|
+
|
|
15
|
+
#### Added
|
|
16
|
+
- `pq1Address(pubkey, sigType)` — canonical address derivation returning `pq1...` bech32m string.
|
|
17
|
+
- SK-only keystore format: `encryptKeystore` stores only the secret key in ciphertext (no pk concatenation), matching `shell-node key generate` output for full cross-language compatibility.
|
|
18
|
+
- Full cross-format compatibility: SDK keystores are now byte-for-byte compatible with Rust CLI keystores.
|
|
19
|
+
|
|
20
|
+
#### Updated
|
|
21
|
+
- All SDK test fixtures regenerated with `pq1` addresses.
|
|
22
|
+
- `SIGNATURE_TYPE_IDS.MlDsa65 = 1` (was incorrectly aliased to `0` in v0.6.x).
|
|
23
|
+
- ML-DSA-65 (FIPS 204) via `@noble/post-quantum` — real implementation, not a Dilithium3 alias.
|
|
24
|
+
|
|
25
|
+
### Migration Guide
|
|
26
|
+
```js
|
|
27
|
+
// Before (0.6.x)
|
|
28
|
+
import { hexAddress } from 'shell-sdk';
|
|
29
|
+
const addr = hexAddress(pubkey, sigType); // "0x..."
|
|
30
|
+
|
|
31
|
+
// After (0.7.0)
|
|
32
|
+
import { pq1Address } from 'shell-sdk';
|
|
33
|
+
const addr = pq1Address(pubkey, sigType); // "pq1..."
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
For existing keystores with `0x` addresses: run `shell-node key migrate <keystore.json>` to upgrade to pq1 format.
|
|
37
|
+
|
|
38
|
+
### Compatibility
|
|
39
|
+
- Requires `shell-chain v0.21.0+` (pq1-only RPC).
|
|
40
|
+
- Not backwards-compatible with SDK `0.6.x` address handling.
|
|
41
|
+
|
|
42
|
+
## [0.6.0] — 2026-04-27
|
|
43
|
+
|
|
44
|
+
### Added — ML-DSA-65 (FIPS 204) + Keystore SK-only format
|
|
45
|
+
|
|
46
|
+
#### Crypto (`keystore.ts`, `address.ts`)
|
|
47
|
+
- **`MlDsa65Adapter`**: real FIPS 204 ML-DSA-65 implementation via `@noble/post-quantum`.
|
|
48
|
+
- **`SIGNATURE_TYPE_IDS.MlDsa65 = 1`**: ML-DSA-65 is now a distinct algorithm (was aliased to Dilithium3=0).
|
|
49
|
+
- **SK-only keystore**: `encryptKeystore` stores only secret key bytes in ciphertext, matching `shell-node` CLI format. Cross-language compatible.
|
|
50
|
+
- **`decryptCliKeystore`** helper removed — standard `decryptKeystore` now handles CLI keystores.
|
|
51
|
+
|
|
52
|
+
#### Compatibility
|
|
53
|
+
- Fully cross-language compatible: Rust CLI keystores decryptable by SDK and vice versa.
|
|
54
|
+
- `shell-chain v0.20.0+` required for ML-DSA-65 transaction signing.
|
|
55
|
+
|
|
3
56
|
## [0.5.0] — 2026-04-26
|
|
4
57
|
|
|
5
58
|
### Added — AA Phase 2 (matches `shell-chain v0.19.0`)
|
package/dist/adapters.d.ts
CHANGED
|
@@ -4,12 +4,10 @@
|
|
|
4
4
|
* `@noble/post-quantum` provides ML-DSA-65 and SLH-DSA-SHA2-256f via
|
|
5
5
|
* WebAssembly-accelerated pure-JS implementations.
|
|
6
6
|
*
|
|
7
|
-
* **Dilithium3
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
* wire-compatible with the chain's Dilithium3 verifier. Both `"Dilithium3"`
|
|
12
|
-
* and `"MlDsa65"` algorithm names route to the same adapter.
|
|
7
|
+
* **Algorithm distinction**: `"Dilithium3"` (type_id=0) and `"ML-DSA-65"` /
|
|
8
|
+
* `"MlDsa65"` (type_id=1) are now separate algorithms on chain with distinct
|
|
9
|
+
* address derivation. `MlDsa65Adapter` and `DilithiumAdapter` both use the
|
|
10
|
+
* same underlying `ml_dsa65` crypto (FIPS 204), but produce different addresses.
|
|
13
11
|
*
|
|
14
12
|
* @module adapters
|
|
15
13
|
*/
|
package/dist/adapters.js
CHANGED
|
@@ -4,12 +4,10 @@
|
|
|
4
4
|
* `@noble/post-quantum` provides ML-DSA-65 and SLH-DSA-SHA2-256f via
|
|
5
5
|
* WebAssembly-accelerated pure-JS implementations.
|
|
6
6
|
*
|
|
7
|
-
* **Dilithium3
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
* wire-compatible with the chain's Dilithium3 verifier. Both `"Dilithium3"`
|
|
12
|
-
* and `"MlDsa65"` algorithm names route to the same adapter.
|
|
7
|
+
* **Algorithm distinction**: `"Dilithium3"` (type_id=0) and `"ML-DSA-65"` /
|
|
8
|
+
* `"MlDsa65"` (type_id=1) are now separate algorithms on chain with distinct
|
|
9
|
+
* address derivation. `MlDsa65Adapter` and `DilithiumAdapter` both use the
|
|
10
|
+
* same underlying `ml_dsa65` crypto (FIPS 204), but produce different addresses.
|
|
13
11
|
*
|
|
14
12
|
* @module adapters
|
|
15
13
|
*/
|
package/dist/address.d.ts
CHANGED
|
@@ -4,7 +4,6 @@ export declare const PQ_ADDRESS_HRP = "pq";
|
|
|
4
4
|
export declare const PQ_ADDRESS_LENGTH = 20;
|
|
5
5
|
/** Version byte for V1 Shell addresses (`0x01`). */
|
|
6
6
|
export declare const PQ_ADDRESS_VERSION_V1 = 1;
|
|
7
|
-
type HexAddress = `0x${string}`;
|
|
8
7
|
/**
|
|
9
8
|
* Encode 20 raw address bytes as a `pq1…` bech32m address.
|
|
10
9
|
*
|
|
@@ -45,40 +44,15 @@ export declare function pqAddressToBytes(address: string): Uint8Array;
|
|
|
45
44
|
*/
|
|
46
45
|
export declare function pqAddressVersion(address: string): number;
|
|
47
46
|
/**
|
|
48
|
-
*
|
|
47
|
+
* Normalise an address: validates it is a `pq1…` bech32m address and returns it.
|
|
49
48
|
*
|
|
50
|
-
*
|
|
51
|
-
* @returns The 20-byte address payload.
|
|
52
|
-
* @throws {Error} If the string does not start with `"0x"` or is not exactly 20 bytes.
|
|
53
|
-
*/
|
|
54
|
-
export declare function hexAddressToBytes(address: string): Uint8Array;
|
|
55
|
-
/**
|
|
56
|
-
* Encode 20 raw address bytes as a `0x…` hex address.
|
|
57
|
-
*
|
|
58
|
-
* @param bytes - Exactly 20 address bytes.
|
|
59
|
-
* @returns A `0x`-prefixed 40-character hex string.
|
|
60
|
-
* @throws {Error} If `bytes.length !== 20`.
|
|
61
|
-
*/
|
|
62
|
-
export declare function bytesToHexAddress(bytes: Uint8Array): HexAddress;
|
|
63
|
-
/**
|
|
64
|
-
* Normalise an address to `pq1…` bech32m form.
|
|
49
|
+
* Hex (`0x…`) addresses are no longer accepted. Use `pq1…` format everywhere.
|
|
65
50
|
*
|
|
66
|
-
*
|
|
67
|
-
*
|
|
68
|
-
*
|
|
69
|
-
* @param address - A `pq1…` or `0x…` address.
|
|
70
|
-
* @returns The `pq1…` bech32m address.
|
|
51
|
+
* @param address - A `pq1…` bech32m address.
|
|
52
|
+
* @returns The same `pq1…` address (validated).
|
|
53
|
+
* @throws {Error} If the address is not a valid `pq1…` bech32m address.
|
|
71
54
|
*/
|
|
72
55
|
export declare function normalizePqAddress(address: string): string;
|
|
73
|
-
/**
|
|
74
|
-
* Normalise an address to `0x…` hex form.
|
|
75
|
-
*
|
|
76
|
-
* Accepts either a `pq1…` or `0x…` address and always returns the hex form.
|
|
77
|
-
*
|
|
78
|
-
* @param address - A `pq1…` or `0x…` address.
|
|
79
|
-
* @returns The `0x`-prefixed hex address.
|
|
80
|
-
*/
|
|
81
|
-
export declare function normalizeHexAddress(address: string): HexAddress;
|
|
82
56
|
/**
|
|
83
57
|
* Derive a `pq1…` address from a raw post-quantum public key.
|
|
84
58
|
*
|
|
@@ -116,4 +90,3 @@ export declare function derivePqAddressFromPublicKey(publicKey: Uint8Array, algo
|
|
|
116
90
|
* ```
|
|
117
91
|
*/
|
|
118
92
|
export declare function isPqAddress(address: string): boolean;
|
|
119
|
-
export {};
|
package/dist/address.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* PQ address utilities for Shell Chain.
|
|
3
3
|
*
|
|
4
|
-
* Shell Chain uses **bech32m**-encoded addresses (prefix `"pq"`)
|
|
5
|
-
*
|
|
6
|
-
*
|
|
4
|
+
* Shell Chain uses **bech32m**-encoded addresses (prefix `"pq"`) exclusively.
|
|
5
|
+
* Each address encodes a version byte and 20 address bytes derived from the
|
|
6
|
+
* account's post-quantum public key:
|
|
7
7
|
*
|
|
8
8
|
* ```
|
|
9
9
|
* address_bytes = blake3(version || algo_id || public_key)[0..20]
|
|
@@ -12,14 +12,10 @@
|
|
|
12
12
|
*
|
|
13
13
|
* Algorithm IDs: Dilithium3=0, MlDsa65=1, SphincsSha2256f=2.
|
|
14
14
|
*
|
|
15
|
-
* Both pq1… and 0x… representations refer to the same underlying 20 bytes;
|
|
16
|
-
* the SDK accepts either form in most places via the `AddressLike` type.
|
|
17
|
-
*
|
|
18
15
|
* @module address
|
|
19
16
|
*/
|
|
20
17
|
import { blake3 } from "@noble/hashes/blake3";
|
|
21
18
|
import { bech32m } from "@scure/base";
|
|
22
|
-
import { bytesToHex, hexToBytes } from "viem";
|
|
23
19
|
/** Human-readable part (HRP) used in Shell bech32m addresses. */
|
|
24
20
|
export const PQ_ADDRESS_HRP = "pq";
|
|
25
21
|
/** Number of raw address bytes (excluding the version byte). */
|
|
@@ -31,11 +27,6 @@ function assertBech32Address(value) {
|
|
|
31
27
|
throw new Error("invalid bech32m address");
|
|
32
28
|
}
|
|
33
29
|
}
|
|
34
|
-
function assertHexAddress(value) {
|
|
35
|
-
if (!value.startsWith("0x")) {
|
|
36
|
-
throw new Error("invalid hex address");
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
30
|
/**
|
|
40
31
|
* Encode 20 raw address bytes as a `pq1…` bech32m address.
|
|
41
32
|
*
|
|
@@ -106,61 +97,18 @@ export function pqAddressVersion(address) {
|
|
|
106
97
|
return bytes[0];
|
|
107
98
|
}
|
|
108
99
|
/**
|
|
109
|
-
*
|
|
100
|
+
* Normalise an address: validates it is a `pq1…` bech32m address and returns it.
|
|
110
101
|
*
|
|
111
|
-
*
|
|
112
|
-
* @returns The 20-byte address payload.
|
|
113
|
-
* @throws {Error} If the string does not start with `"0x"` or is not exactly 20 bytes.
|
|
114
|
-
*/
|
|
115
|
-
export function hexAddressToBytes(address) {
|
|
116
|
-
assertHexAddress(address);
|
|
117
|
-
const bytes = hexToBytes(address);
|
|
118
|
-
if (bytes.length !== PQ_ADDRESS_LENGTH) {
|
|
119
|
-
throw new Error(`expected ${PQ_ADDRESS_LENGTH} address bytes, got ${bytes.length}`);
|
|
120
|
-
}
|
|
121
|
-
return bytes;
|
|
122
|
-
}
|
|
123
|
-
/**
|
|
124
|
-
* Encode 20 raw address bytes as a `0x…` hex address.
|
|
102
|
+
* Hex (`0x…`) addresses are no longer accepted. Use `pq1…` format everywhere.
|
|
125
103
|
*
|
|
126
|
-
* @param
|
|
127
|
-
* @returns
|
|
128
|
-
* @throws {Error} If `
|
|
129
|
-
*/
|
|
130
|
-
export function bytesToHexAddress(bytes) {
|
|
131
|
-
if (bytes.length !== PQ_ADDRESS_LENGTH) {
|
|
132
|
-
throw new Error(`expected ${PQ_ADDRESS_LENGTH} address bytes, got ${bytes.length}`);
|
|
133
|
-
}
|
|
134
|
-
return bytesToHex(bytes);
|
|
135
|
-
}
|
|
136
|
-
/**
|
|
137
|
-
* Normalise an address to `pq1…` bech32m form.
|
|
138
|
-
*
|
|
139
|
-
* Accepts either a `pq1…` or `0x…` address and always returns the canonical
|
|
140
|
-
* bech32m form.
|
|
141
|
-
*
|
|
142
|
-
* @param address - A `pq1…` or `0x…` address.
|
|
143
|
-
* @returns The `pq1…` bech32m address.
|
|
104
|
+
* @param address - A `pq1…` bech32m address.
|
|
105
|
+
* @returns The same `pq1…` address (validated).
|
|
106
|
+
* @throws {Error} If the address is not a valid `pq1…` bech32m address.
|
|
144
107
|
*/
|
|
145
108
|
export function normalizePqAddress(address) {
|
|
146
|
-
if (isPqAddress(address)) {
|
|
147
|
-
|
|
148
|
-
}
|
|
149
|
-
return bytesToPqAddress(hexAddressToBytes(address));
|
|
150
|
-
}
|
|
151
|
-
/**
|
|
152
|
-
* Normalise an address to `0x…` hex form.
|
|
153
|
-
*
|
|
154
|
-
* Accepts either a `pq1…` or `0x…` address and always returns the hex form.
|
|
155
|
-
*
|
|
156
|
-
* @param address - A `pq1…` or `0x…` address.
|
|
157
|
-
* @returns The `0x`-prefixed hex address.
|
|
158
|
-
*/
|
|
159
|
-
export function normalizeHexAddress(address) {
|
|
160
|
-
if (isPqAddress(address)) {
|
|
161
|
-
return bytesToHexAddress(pqAddressToBytes(address));
|
|
109
|
+
if (!isPqAddress(address)) {
|
|
110
|
+
throw new Error(`expected a pq1… bech32m address, got: "${address.slice(0, 10)}…"`);
|
|
162
111
|
}
|
|
163
|
-
assertHexAddress(address);
|
|
164
112
|
return address;
|
|
165
113
|
}
|
|
166
114
|
/**
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
export {
|
|
1
|
+
export { PQ_ADDRESS_HRP, PQ_ADDRESS_LENGTH, PQ_ADDRESS_VERSION_V1, bytesToPqAddress, derivePqAddressFromPublicKey, isPqAddress, normalizePqAddress, pqAddressVersion, pqAddressToBytes, } from "./address.js";
|
|
2
2
|
export { createShellProvider, createShellPublicClient, createShellWsClient, ShellProvider, shellDevnet, type CreateShellPublicClientOptions, } from "./provider.js";
|
|
3
|
-
export { accountManagerAddress,
|
|
3
|
+
export { accountManagerAddress, cancelRecoverySelector, clearValidationCodeSelector, encodeCancelRecoveryCalldata, encodeClearValidationCodeCalldata, encodeExecuteRecoveryCalldata, encodeRotateKeyCalldata, encodeSetGuardiansCalldata, encodeSetValidationCodeCalldata, encodeSubmitRecoveryCalldata, executeRecoverySelector, isSystemContractAddress, rotateKeySelector, setGuardiansSelector, setValidationCodeSelector, submitRecoverySelector, validatorRegistryAddress, } from "./system-contracts.js";
|
|
4
4
|
export { AA_BUNDLE_TX_TYPE, AA_MAX_INNER_CALLS, buildBatchTransaction, buildClearValidationCodeTransaction, buildContractPaymasterTransaction, buildInnerCall, buildInnerTransfer, buildRotateKeyTransaction, buildSessionKeyTransaction, buildSetValidationCodeTransaction, buildSignature, buildSignedTransaction, buildSponsoredTransaction, buildSystemTransaction, buildTransaction, buildTransferTransaction, DEFAULT_AA_GAS_LIMIT, DEFAULT_MAX_FEE_PER_GAS, DEFAULT_MAX_PRIORITY_FEE_PER_GAS, DEFAULT_SYSTEM_GAS_LIMIT, DEFAULT_TRANSFER_GAS_LIMIT, DEFAULT_TX_TYPE, hashBatchTransaction, hashTransaction, type BuildBatchTransactionOptions, type BuildContractPaymasterTransactionOptions, type BuildSessionKeyTransactionOptions, type BuildSponsoredTransactionOptions, } from "./transactions.js";
|
|
5
5
|
export { assertSignerMatchesKeystore, decryptKeystore, exportEncryptedKeyJson, parseEncryptedKey, validateEncryptedKeyAddress, type ParsedShellKeystore, } from "./keystore.js";
|
|
6
6
|
export { adapterFromKeyPair, generateAdapter, generateMlDsa65KeyPair, generateSlhDsaKeyPair, MlDsa65Adapter, SlhDsaAdapter, type MlDsa65KeyPair, type SlhDsaKeyPair, } from "./adapters.js";
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
export {
|
|
1
|
+
export { PQ_ADDRESS_HRP, PQ_ADDRESS_LENGTH, PQ_ADDRESS_VERSION_V1, bytesToPqAddress, derivePqAddressFromPublicKey, isPqAddress, normalizePqAddress, pqAddressVersion, pqAddressToBytes, } from "./address.js";
|
|
2
2
|
export { createShellProvider, createShellPublicClient, createShellWsClient, ShellProvider, shellDevnet, } from "./provider.js";
|
|
3
|
-
export { accountManagerAddress,
|
|
3
|
+
export { accountManagerAddress, cancelRecoverySelector, clearValidationCodeSelector, encodeCancelRecoveryCalldata, encodeClearValidationCodeCalldata, encodeExecuteRecoveryCalldata, encodeRotateKeyCalldata, encodeSetGuardiansCalldata, encodeSetValidationCodeCalldata, encodeSubmitRecoveryCalldata, executeRecoverySelector, isSystemContractAddress, rotateKeySelector, setGuardiansSelector, setValidationCodeSelector, submitRecoverySelector, validatorRegistryAddress, } from "./system-contracts.js";
|
|
4
4
|
export { AA_BUNDLE_TX_TYPE, AA_MAX_INNER_CALLS, buildBatchTransaction, buildClearValidationCodeTransaction, buildContractPaymasterTransaction, buildInnerCall, buildInnerTransfer, buildRotateKeyTransaction, buildSessionKeyTransaction, buildSetValidationCodeTransaction, buildSignature, buildSignedTransaction, buildSponsoredTransaction, buildSystemTransaction, buildTransaction, buildTransferTransaction, DEFAULT_AA_GAS_LIMIT, DEFAULT_MAX_FEE_PER_GAS, DEFAULT_MAX_PRIORITY_FEE_PER_GAS, DEFAULT_SYSTEM_GAS_LIMIT, DEFAULT_TRANSFER_GAS_LIMIT, DEFAULT_TX_TYPE, hashBatchTransaction, hashTransaction, } from "./transactions.js";
|
|
5
5
|
export { assertSignerMatchesKeystore, decryptKeystore, exportEncryptedKeyJson, parseEncryptedKey, validateEncryptedKeyAddress, } from "./keystore.js";
|
|
6
6
|
export { adapterFromKeyPair, generateAdapter, generateMlDsa65KeyPair, generateSlhDsaKeyPair, MlDsa65Adapter, SlhDsaAdapter, } from "./adapters.js";
|
package/dist/keystore.d.ts
CHANGED
|
@@ -16,8 +16,6 @@ export interface ParsedShellKeystore {
|
|
|
16
16
|
publicKey: Uint8Array;
|
|
17
17
|
/** Canonical `pq1…` address derived from `publicKey`. */
|
|
18
18
|
canonicalAddress: string;
|
|
19
|
-
/** `0x`-prefixed hex representation of the same address. */
|
|
20
|
-
hexAddress: string;
|
|
21
19
|
}
|
|
22
20
|
/**
|
|
23
21
|
* Parse a Shell keystore file (string or object) and extract public metadata.
|
|
@@ -33,7 +31,7 @@ export interface ParsedShellKeystore {
|
|
|
33
31
|
* ```typescript
|
|
34
32
|
* const parsed = parseEncryptedKey(readFileSync("key.json", "utf8"));
|
|
35
33
|
* console.log(parsed.canonicalAddress); // pq1…
|
|
36
|
-
* console.log(parsed.signatureType); // "
|
|
34
|
+
* console.log(parsed.signatureType); // "ML-DSA-65"
|
|
37
35
|
* ```
|
|
38
36
|
*/
|
|
39
37
|
export declare function parseEncryptedKey(input: string | ShellEncryptedKey): ParsedShellKeystore;
|
|
@@ -77,17 +75,14 @@ export declare function assertSignerMatchesKeystore(signer: ShellSigner, keystor
|
|
|
77
75
|
*
|
|
78
76
|
* **KDF**: argon2id (parameters from `kdf_params`)
|
|
79
77
|
* **Cipher**: xchacha20-poly1305 (24-byte nonce from `cipher_params`)
|
|
80
|
-
* **Plaintext layout**:
|
|
81
|
-
*
|
|
82
|
-
* The decrypted public key is compared against `raw.public_key` to detect
|
|
83
|
-
* wrong passwords or corrupt files before returning the signer.
|
|
78
|
+
* **Plaintext layout (v1)**: secret key bytes only (sk-only format).
|
|
79
|
+
* The public key is read from the `public_key` JSON field directly.
|
|
84
80
|
*
|
|
85
81
|
* @param input - Keystore JSON string or object.
|
|
86
82
|
* @param password - The passphrase used to encrypt the key.
|
|
87
83
|
* @returns A fully configured `ShellSigner` ready for signing transactions.
|
|
88
84
|
* @throws {Error} If the KDF or cipher is unsupported.
|
|
89
85
|
* @throws {Error} If decryption fails (wrong password or corrupt ciphertext).
|
|
90
|
-
* @throws {Error} If the decrypted public key does not match the stored one.
|
|
91
86
|
*
|
|
92
87
|
* @example
|
|
93
88
|
* ```typescript
|
package/dist/keystore.js
CHANGED
|
@@ -6,19 +6,23 @@
|
|
|
6
6
|
* - **KDF**: argon2id (memory-hard password derivation)
|
|
7
7
|
* - **Cipher**: xchacha20-poly1305 (authenticated encryption)
|
|
8
8
|
*
|
|
9
|
-
*
|
|
10
|
-
* compatible with the SDK's `decryptKeystore` function.
|
|
9
|
+
* ## Keystore format (v1 — canonical)
|
|
11
10
|
*
|
|
12
|
-
*
|
|
11
|
+
* The **ciphertext** contains **only the secret key bytes** (sk-only format).
|
|
12
|
+
* The public key is stored separately in the `public_key` field as plain hex.
|
|
13
|
+
* This matches the format produced by `shell-node key generate`.
|
|
14
|
+
*
|
|
15
|
+
* Previous SDK versions expected `sk || pk` in the ciphertext. That format
|
|
16
|
+
* is no longer produced or accepted; all new keystores use sk-only.
|
|
13
17
|
*
|
|
14
18
|
* @module keystore
|
|
15
19
|
*/
|
|
16
20
|
import { xchacha20poly1305 } from "@noble/ciphers/chacha.js";
|
|
17
21
|
import { argon2id } from "hash-wasm";
|
|
18
|
-
import { derivePqAddressFromPublicKey,
|
|
22
|
+
import { derivePqAddressFromPublicKey, normalizePqAddress } from "./address.js";
|
|
19
23
|
import { adapterFromKeyPair } from "./adapters.js";
|
|
20
|
-
import { ShellSigner, publicKeyFromHex, signatureTypeFromKeyType } from "./signer.js";
|
|
21
|
-
const SIG_IDS = { "ML-DSA-65":
|
|
24
|
+
import { ShellSigner, canonicalSignatureType, publicKeyFromHex, signatureTypeFromKeyType, } from "./signer.js";
|
|
25
|
+
const SIG_IDS = { "ML-DSA-65": 1, Dilithium3: 0, MlDsa65: 1, SphincsSha2256f: 2 };
|
|
22
26
|
/**
|
|
23
27
|
* Parse a Shell keystore file (string or object) and extract public metadata.
|
|
24
28
|
*
|
|
@@ -33,7 +37,7 @@ const SIG_IDS = { "ML-DSA-65": 0, Dilithium3: 0, MlDsa65: 0, SphincsSha2256f: 2
|
|
|
33
37
|
* ```typescript
|
|
34
38
|
* const parsed = parseEncryptedKey(readFileSync("key.json", "utf8"));
|
|
35
39
|
* console.log(parsed.canonicalAddress); // pq1…
|
|
36
|
-
* console.log(parsed.signatureType); // "
|
|
40
|
+
* console.log(parsed.signatureType); // "ML-DSA-65"
|
|
37
41
|
* ```
|
|
38
42
|
*/
|
|
39
43
|
export function parseEncryptedKey(input) {
|
|
@@ -42,8 +46,7 @@ export function parseEncryptedKey(input) {
|
|
|
42
46
|
const algorithmId = SIG_IDS[signatureType];
|
|
43
47
|
const publicKey = publicKeyFromHex(raw.public_key);
|
|
44
48
|
const canonicalAddress = derivePqAddressFromPublicKey(publicKey, algorithmId);
|
|
45
|
-
|
|
46
|
-
return { raw, signatureType, algorithmId, publicKey, canonicalAddress, hexAddress };
|
|
49
|
+
return { raw, signatureType, algorithmId, publicKey, canonicalAddress };
|
|
47
50
|
}
|
|
48
51
|
/**
|
|
49
52
|
* Parse a keystore and verify that the declared address matches the public key.
|
|
@@ -89,7 +92,7 @@ export function exportEncryptedKeyJson(input) {
|
|
|
89
92
|
* ```
|
|
90
93
|
*/
|
|
91
94
|
export function assertSignerMatchesKeystore(signer, keystore) {
|
|
92
|
-
if (signer.signatureType !== keystore.signatureType) {
|
|
95
|
+
if (canonicalSignatureType(signer.signatureType) !== keystore.signatureType) {
|
|
93
96
|
throw new Error("algorithm mismatch: signer=" + signer.signatureType + " keystore=" + keystore.signatureType);
|
|
94
97
|
}
|
|
95
98
|
const addr = signer.getAddress();
|
|
@@ -110,17 +113,14 @@ function hexToBytes(hex) {
|
|
|
110
113
|
*
|
|
111
114
|
* **KDF**: argon2id (parameters from `kdf_params`)
|
|
112
115
|
* **Cipher**: xchacha20-poly1305 (24-byte nonce from `cipher_params`)
|
|
113
|
-
* **Plaintext layout**:
|
|
114
|
-
*
|
|
115
|
-
* The decrypted public key is compared against `raw.public_key` to detect
|
|
116
|
-
* wrong passwords or corrupt files before returning the signer.
|
|
116
|
+
* **Plaintext layout (v1)**: secret key bytes only (sk-only format).
|
|
117
|
+
* The public key is read from the `public_key` JSON field directly.
|
|
117
118
|
*
|
|
118
119
|
* @param input - Keystore JSON string or object.
|
|
119
120
|
* @param password - The passphrase used to encrypt the key.
|
|
120
121
|
* @returns A fully configured `ShellSigner` ready for signing transactions.
|
|
121
122
|
* @throws {Error} If the KDF or cipher is unsupported.
|
|
122
123
|
* @throws {Error} If decryption fails (wrong password or corrupt ciphertext).
|
|
123
|
-
* @throws {Error} If the decrypted public key does not match the stored one.
|
|
124
124
|
*
|
|
125
125
|
* @example
|
|
126
126
|
* ```typescript
|
|
@@ -150,17 +150,8 @@ export async function decryptKeystore(input, password) {
|
|
|
150
150
|
});
|
|
151
151
|
const derivedKey = hexToBytes(derivedKeyHex);
|
|
152
152
|
const chacha = xchacha20poly1305(derivedKey, nonce);
|
|
153
|
-
|
|
154
|
-
const
|
|
155
|
-
const skLen = plaintext.length - pubkeyLen;
|
|
156
|
-
if (skLen <= 0) {
|
|
157
|
-
throw new Error("payload too short: " + plaintext.length + " bytes");
|
|
158
|
-
}
|
|
159
|
-
const secretKey = plaintext.slice(0, skLen);
|
|
160
|
-
const derivedPubkey = plaintext.slice(skLen);
|
|
161
|
-
if (!derivedPubkey.every((b, i) => b === parsed.publicKey[i])) {
|
|
162
|
-
throw new Error("decrypted public key mismatch");
|
|
163
|
-
}
|
|
153
|
+
// Plaintext is sk-only; public key comes from the JSON `public_key` field.
|
|
154
|
+
const secretKey = chacha.decrypt(ciphertext);
|
|
164
155
|
const adapter = adapterFromKeyPair(parsed.signatureType, parsed.publicKey, secretKey);
|
|
165
156
|
return new ShellSigner(parsed.signatureType, adapter);
|
|
166
157
|
}
|
package/dist/provider.d.ts
CHANGED
|
@@ -113,7 +113,7 @@ export declare class ShellProvider {
|
|
|
113
113
|
* Calls `shell_getPqPubkey`. Returns `null` if the address has not yet
|
|
114
114
|
* submitted a transaction (public key is only recorded on first send).
|
|
115
115
|
*
|
|
116
|
-
* @param address - A `pq1…`
|
|
116
|
+
* @param address - A `pq1…` bech32m address.
|
|
117
117
|
* @returns Hex-encoded public key string, or `null` if unknown.
|
|
118
118
|
*/
|
|
119
119
|
getPqPubkey(address: string): Promise<string | null>;
|
|
@@ -279,7 +279,7 @@ export declare function createShellWsClient(options?: CreateShellPublicClientOpt
|
|
|
279
279
|
* @example
|
|
280
280
|
* ```typescript
|
|
281
281
|
* const provider = createShellProvider();
|
|
282
|
-
* const balance = await provider.
|
|
282
|
+
* const balance = await provider.getBalance(signer.getAddress());
|
|
283
283
|
* const hash = await provider.sendTransaction(signedTx);
|
|
284
284
|
* ```
|
|
285
285
|
*/
|
package/dist/provider.js
CHANGED
|
@@ -86,7 +86,7 @@ export class ShellProvider {
|
|
|
86
86
|
* Calls `shell_getPqPubkey`. Returns `null` if the address has not yet
|
|
87
87
|
* submitted a transaction (public key is only recorded on first send).
|
|
88
88
|
*
|
|
89
|
-
* @param address - A `pq1…`
|
|
89
|
+
* @param address - A `pq1…` bech32m address.
|
|
90
90
|
* @returns Hex-encoded public key string, or `null` if unknown.
|
|
91
91
|
*/
|
|
92
92
|
async getPqPubkey(address) {
|
|
@@ -294,7 +294,7 @@ export function createShellWsClient(options = {}) {
|
|
|
294
294
|
* @example
|
|
295
295
|
* ```typescript
|
|
296
296
|
* const provider = createShellProvider();
|
|
297
|
-
* const balance = await provider.
|
|
297
|
+
* const balance = await provider.getBalance(signer.getAddress());
|
|
298
298
|
* const hash = await provider.sendTransaction(signedTx);
|
|
299
299
|
* ```
|
|
300
300
|
*/
|
package/dist/signer.d.ts
CHANGED
|
@@ -4,9 +4,8 @@ import type { SignedShellTransaction, SignatureTypeName } from "./types.js";
|
|
|
4
4
|
* Maps each {@link SignatureTypeName} to its numeric algorithm ID used in
|
|
5
5
|
* address derivation and on-chain records.
|
|
6
6
|
*
|
|
7
|
-
* - `"
|
|
8
|
-
* - `"
|
|
9
|
-
* - `"MlDsa65"` → `0` (camelCase alias, same algorithm)
|
|
7
|
+
* - `"Dilithium3"` → `0` (Round 3 Dilithium, legacy compat)
|
|
8
|
+
* - `"ML-DSA-65"` / `"MlDsa65"` → `1` (FIPS 204 ML-DSA-65, canonical)
|
|
10
9
|
* - `"SphincsSha2256f"` → `2`
|
|
11
10
|
*/
|
|
12
11
|
export declare const SIGNATURE_TYPE_IDS: Record<SignatureTypeName, number>;
|
|
@@ -15,9 +14,9 @@ export declare const SIGNATURE_TYPE_IDS: Record<SignatureTypeName, number>;
|
|
|
15
14
|
* corresponding {@link SignatureTypeName}.
|
|
16
15
|
*
|
|
17
16
|
* Keys are lowercase; matching is done after calling `.toLowerCase()`.
|
|
18
|
-
* Always returns the FIPS 204 canonical name `"ML-DSA-65"` for ML-DSA-65 variants.
|
|
19
17
|
*/
|
|
20
18
|
export declare const KEY_TYPE_TO_SIGNATURE_TYPE: Record<string, SignatureTypeName>;
|
|
19
|
+
export declare function canonicalSignatureType(signatureType: SignatureTypeName): SignatureTypeName;
|
|
21
20
|
/**
|
|
22
21
|
* Minimal interface that any post-quantum signing implementation must satisfy
|
|
23
22
|
* to be used with {@link ShellSigner}.
|
|
@@ -55,8 +54,7 @@ export interface SignerAdapter {
|
|
|
55
54
|
* const adapter = MlDsa65Adapter.generate();
|
|
56
55
|
* const signer = new ShellSigner("MlDsa65", adapter);
|
|
57
56
|
*
|
|
58
|
-
* console.log(signer.getAddress());
|
|
59
|
-
* console.log(signer.getHexAddress()); // 0x…
|
|
57
|
+
* console.log(signer.getAddress()); // pq1…
|
|
60
58
|
* ```
|
|
61
59
|
*/
|
|
62
60
|
export declare class ShellSigner {
|
|
@@ -83,12 +81,6 @@ export declare class ShellSigner {
|
|
|
83
81
|
* The address is computed deterministically from the public key and algorithm ID.
|
|
84
82
|
*/
|
|
85
83
|
getAddress(): string;
|
|
86
|
-
/**
|
|
87
|
-
* Return the `0x…` hex representation of this signer's address.
|
|
88
|
-
*
|
|
89
|
-
* Equivalent to `normalizeHexAddress(signer.getAddress())`.
|
|
90
|
-
*/
|
|
91
|
-
getHexAddress(): `0x${string}`;
|
|
92
84
|
/**
|
|
93
85
|
* Sign a raw byte message with the underlying adapter.
|
|
94
86
|
*
|
|
@@ -132,7 +124,7 @@ export declare class ShellSigner {
|
|
|
132
124
|
*
|
|
133
125
|
* @example
|
|
134
126
|
* ```typescript
|
|
135
|
-
* signatureTypeFromKeyType("mldsa65"); // "
|
|
127
|
+
* signatureTypeFromKeyType("mldsa65"); // "ML-DSA-65"
|
|
136
128
|
* signatureTypeFromKeyType("sphincs-sha2-256f"); // "SphincsSha2256f"
|
|
137
129
|
* ```
|
|
138
130
|
*/
|
package/dist/signer.js
CHANGED
|
@@ -10,21 +10,20 @@
|
|
|
10
10
|
* @module signer
|
|
11
11
|
*/
|
|
12
12
|
import { hexToBytes } from "viem";
|
|
13
|
-
import { derivePqAddressFromPublicKey,
|
|
13
|
+
import { derivePqAddressFromPublicKey, normalizePqAddress } from "./address.js";
|
|
14
14
|
import { buildSignature, buildSignedTransaction, } from "./transactions.js";
|
|
15
15
|
/**
|
|
16
16
|
* Maps each {@link SignatureTypeName} to its numeric algorithm ID used in
|
|
17
17
|
* address derivation and on-chain records.
|
|
18
18
|
*
|
|
19
|
-
* - `"
|
|
20
|
-
* - `"
|
|
21
|
-
* - `"MlDsa65"` → `0` (camelCase alias, same algorithm)
|
|
19
|
+
* - `"Dilithium3"` → `0` (Round 3 Dilithium, legacy compat)
|
|
20
|
+
* - `"ML-DSA-65"` / `"MlDsa65"` → `1` (FIPS 204 ML-DSA-65, canonical)
|
|
22
21
|
* - `"SphincsSha2256f"` → `2`
|
|
23
22
|
*/
|
|
24
23
|
export const SIGNATURE_TYPE_IDS = {
|
|
25
|
-
"ML-DSA-65":
|
|
24
|
+
"ML-DSA-65": 1,
|
|
26
25
|
Dilithium3: 0,
|
|
27
|
-
MlDsa65:
|
|
26
|
+
MlDsa65: 1,
|
|
28
27
|
SphincsSha2256f: 2,
|
|
29
28
|
};
|
|
30
29
|
/**
|
|
@@ -32,14 +31,16 @@ export const SIGNATURE_TYPE_IDS = {
|
|
|
32
31
|
* corresponding {@link SignatureTypeName}.
|
|
33
32
|
*
|
|
34
33
|
* Keys are lowercase; matching is done after calling `.toLowerCase()`.
|
|
35
|
-
* Always returns the FIPS 204 canonical name `"ML-DSA-65"` for ML-DSA-65 variants.
|
|
36
34
|
*/
|
|
37
35
|
export const KEY_TYPE_TO_SIGNATURE_TYPE = {
|
|
38
36
|
"ml-dsa-65": "ML-DSA-65",
|
|
39
37
|
mldsa65: "ML-DSA-65",
|
|
40
|
-
dilithium3: "
|
|
38
|
+
dilithium3: "Dilithium3",
|
|
41
39
|
"sphincs-sha2-256f": "SphincsSha2256f",
|
|
42
40
|
};
|
|
41
|
+
export function canonicalSignatureType(signatureType) {
|
|
42
|
+
return signatureType === "MlDsa65" ? "ML-DSA-65" : signatureType;
|
|
43
|
+
}
|
|
43
44
|
/**
|
|
44
45
|
* High-level Shell Chain signer.
|
|
45
46
|
*
|
|
@@ -54,8 +55,7 @@ export const KEY_TYPE_TO_SIGNATURE_TYPE = {
|
|
|
54
55
|
* const adapter = MlDsa65Adapter.generate();
|
|
55
56
|
* const signer = new ShellSigner("MlDsa65", adapter);
|
|
56
57
|
*
|
|
57
|
-
* console.log(signer.getAddress());
|
|
58
|
-
* console.log(signer.getHexAddress()); // 0x…
|
|
58
|
+
* console.log(signer.getAddress()); // pq1…
|
|
59
59
|
* ```
|
|
60
60
|
*/
|
|
61
61
|
export class ShellSigner {
|
|
@@ -68,7 +68,7 @@ export class ShellSigner {
|
|
|
68
68
|
* @param adapter - An adapter providing `sign` and `getPublicKey`.
|
|
69
69
|
*/
|
|
70
70
|
constructor(signatureType, adapter) {
|
|
71
|
-
this.signatureType = signatureType;
|
|
71
|
+
this.signatureType = canonicalSignatureType(signatureType);
|
|
72
72
|
this.adapter = adapter;
|
|
73
73
|
}
|
|
74
74
|
/**
|
|
@@ -91,14 +91,6 @@ export class ShellSigner {
|
|
|
91
91
|
getAddress() {
|
|
92
92
|
return derivePqAddressFromPublicKey(this.getPublicKey(), this.algorithmId);
|
|
93
93
|
}
|
|
94
|
-
/**
|
|
95
|
-
* Return the `0x…` hex representation of this signer's address.
|
|
96
|
-
*
|
|
97
|
-
* Equivalent to `normalizeHexAddress(signer.getAddress())`.
|
|
98
|
-
*/
|
|
99
|
-
getHexAddress() {
|
|
100
|
-
return normalizeHexAddress(this.getAddress());
|
|
101
|
-
}
|
|
102
94
|
/**
|
|
103
95
|
* Sign a raw byte message with the underlying adapter.
|
|
104
96
|
*
|
|
@@ -150,7 +142,7 @@ export class ShellSigner {
|
|
|
150
142
|
*
|
|
151
143
|
* @example
|
|
152
144
|
* ```typescript
|
|
153
|
-
* signatureTypeFromKeyType("mldsa65"); // "
|
|
145
|
+
* signatureTypeFromKeyType("mldsa65"); // "ML-DSA-65"
|
|
154
146
|
* signatureTypeFromKeyType("sphincs-sha2-256f"); // "SphincsSha2256f"
|
|
155
147
|
* ```
|
|
156
148
|
*/
|
|
@@ -1,8 +1,4 @@
|
|
|
1
1
|
import type { AddressLike, HexString } from "./types.js";
|
|
2
|
-
/** Hex address of the ValidatorRegistry system contract (`0x…0001`). */
|
|
3
|
-
export declare const validatorRegistryHexAddress = "0x0000000000000000000000000000000000000001";
|
|
4
|
-
/** Hex address of the AccountManager system contract (`0x…0002`). */
|
|
5
|
-
export declare const accountManagerHexAddress = "0x0000000000000000000000000000000000000002";
|
|
6
2
|
/** `pq1…` bech32m address of the ValidatorRegistry system contract. */
|
|
7
3
|
export declare const validatorRegistryAddress: string;
|
|
8
4
|
/** `pq1…` bech32m address of the AccountManager system contract. */
|
|
@@ -57,17 +53,13 @@ export declare function encodeClearValidationCodeCalldata(): HexString;
|
|
|
57
53
|
/**
|
|
58
54
|
* Return `true` if `address` refers to one of the Shell system contracts.
|
|
59
55
|
*
|
|
60
|
-
*
|
|
61
|
-
* ValidatorRegistry addresses.
|
|
62
|
-
*
|
|
63
|
-
* @param address - Address to test (any format accepted by `AddressLike`).
|
|
56
|
+
* @param address - `pq1…` address to test.
|
|
64
57
|
* @returns `true` if the address matches AccountManager or ValidatorRegistry.
|
|
65
58
|
*
|
|
66
59
|
* @example
|
|
67
60
|
* ```typescript
|
|
68
|
-
* isSystemContractAddress(
|
|
69
|
-
* isSystemContractAddress(
|
|
70
|
-
* isSystemContractAddress("pq1someuser…"); // false
|
|
61
|
+
* isSystemContractAddress(accountManagerAddress); // true
|
|
62
|
+
* isSystemContractAddress("pq1someuser…"); // false
|
|
71
63
|
* ```
|
|
72
64
|
*/
|
|
73
65
|
export declare function isSystemContractAddress(address: AddressLike): boolean;
|
|
@@ -78,7 +70,7 @@ export declare function isSystemContractAddress(address: AddressLike): boolean;
|
|
|
78
70
|
* account owner can call this (the call must be made as an inner call from
|
|
79
71
|
* the owner's AA bundle, or as a direct transaction).
|
|
80
72
|
*
|
|
81
|
-
* @param guardians - Array of guardian addresses (1..5).
|
|
73
|
+
* @param guardians - Array of guardian addresses (1..5). Must be `pq1…` bech32m form.
|
|
82
74
|
* @param threshold - k-of-n required votes (1 ≤ threshold ≤ guardians.length).
|
|
83
75
|
* @param timelock - Minimum blocks between threshold-reach and execution (≥ 100).
|
|
84
76
|
* @returns `HexString` with the 4-byte selector prepended.
|
|
@@ -86,7 +78,7 @@ export declare function isSystemContractAddress(address: AddressLike): boolean;
|
|
|
86
78
|
* @example
|
|
87
79
|
* ```typescript
|
|
88
80
|
* const data = encodeSetGuardiansCalldata(
|
|
89
|
-
* ["
|
|
81
|
+
* ["pq1guardian1…", "pq1guardian2…", "pq1guardian3…"],
|
|
90
82
|
* 2, // 2-of-3 threshold
|
|
91
83
|
* 100, // 100-block timelock
|
|
92
84
|
* );
|
|
@@ -100,7 +92,7 @@ export declare function encodeSetGuardiansCalldata(guardians: AddressLike[], thr
|
|
|
100
92
|
* When k-of-n threshold is reached, the proposal becomes executable after
|
|
101
93
|
* `timelock` blocks.
|
|
102
94
|
*
|
|
103
|
-
* @param account - Account being recovered (
|
|
95
|
+
* @param account - Account being recovered (`pq1…` bech32m form).
|
|
104
96
|
* @param newPubkey - Raw bytes of the new PQ public key.
|
|
105
97
|
* @param newAlgo - Algorithm ID for the new key (ML-DSA-65 = 0, etc.).
|
|
106
98
|
* @returns `HexString` with the 4-byte selector prepended.
|
|
@@ -108,7 +100,7 @@ export declare function encodeSetGuardiansCalldata(guardians: AddressLike[], thr
|
|
|
108
100
|
* @example
|
|
109
101
|
* ```typescript
|
|
110
102
|
* const data = encodeSubmitRecoveryCalldata(
|
|
111
|
-
* "
|
|
103
|
+
* "pq1accountaddress…",
|
|
112
104
|
* newPubkeyBytes,
|
|
113
105
|
* 0, // ML-DSA-65
|
|
114
106
|
* );
|
package/dist/system-contracts.js
CHANGED
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
* @module system-contracts
|
|
19
19
|
*/
|
|
20
20
|
import { bytesToHex, encodeAbiParameters, keccak256, toBytes } from "viem";
|
|
21
|
-
import { bytesToPqAddress } from "./address.js";
|
|
21
|
+
import { bytesToPqAddress, pqAddressToBytes } from "./address.js";
|
|
22
22
|
const SYSTEM_ADDRESS_LENGTH = 20;
|
|
23
23
|
function systemAddress(lastByte) {
|
|
24
24
|
const bytes = new Uint8Array(SYSTEM_ADDRESS_LENGTH);
|
|
@@ -28,10 +28,6 @@ function systemAddress(lastByte) {
|
|
|
28
28
|
function selector(signature) {
|
|
29
29
|
return keccak256(toBytes(signature)).slice(0, 10);
|
|
30
30
|
}
|
|
31
|
-
/** Hex address of the ValidatorRegistry system contract (`0x…0001`). */
|
|
32
|
-
export const validatorRegistryHexAddress = "0x0000000000000000000000000000000000000001";
|
|
33
|
-
/** Hex address of the AccountManager system contract (`0x…0002`). */
|
|
34
|
-
export const accountManagerHexAddress = "0x0000000000000000000000000000000000000002";
|
|
35
31
|
/** `pq1…` bech32m address of the ValidatorRegistry system contract. */
|
|
36
32
|
export const validatorRegistryAddress = bytesToPqAddress(systemAddress(1));
|
|
37
33
|
/** `pq1…` bech32m address of the AccountManager system contract. */
|
|
@@ -100,24 +96,17 @@ export function encodeClearValidationCodeCalldata() {
|
|
|
100
96
|
/**
|
|
101
97
|
* Return `true` if `address` refers to one of the Shell system contracts.
|
|
102
98
|
*
|
|
103
|
-
*
|
|
104
|
-
* ValidatorRegistry addresses.
|
|
105
|
-
*
|
|
106
|
-
* @param address - Address to test (any format accepted by `AddressLike`).
|
|
99
|
+
* @param address - `pq1…` address to test.
|
|
107
100
|
* @returns `true` if the address matches AccountManager or ValidatorRegistry.
|
|
108
101
|
*
|
|
109
102
|
* @example
|
|
110
103
|
* ```typescript
|
|
111
|
-
* isSystemContractAddress(
|
|
112
|
-
* isSystemContractAddress(
|
|
113
|
-
* isSystemContractAddress("pq1someuser…"); // false
|
|
104
|
+
* isSystemContractAddress(accountManagerAddress); // true
|
|
105
|
+
* isSystemContractAddress("pq1someuser…"); // false
|
|
114
106
|
* ```
|
|
115
107
|
*/
|
|
116
108
|
export function isSystemContractAddress(address) {
|
|
117
|
-
return
|
|
118
|
-
address === validatorRegistryAddress ||
|
|
119
|
-
address === accountManagerHexAddress ||
|
|
120
|
-
address === validatorRegistryHexAddress);
|
|
109
|
+
return address === accountManagerAddress || address === validatorRegistryAddress;
|
|
121
110
|
}
|
|
122
111
|
// ---------------------------------------------------------------------------
|
|
123
112
|
// AA Phase 2 — Guardian Recovery calldata encoders (v0.19.0-dev)
|
|
@@ -129,7 +118,7 @@ export function isSystemContractAddress(address) {
|
|
|
129
118
|
* account owner can call this (the call must be made as an inner call from
|
|
130
119
|
* the owner's AA bundle, or as a direct transaction).
|
|
131
120
|
*
|
|
132
|
-
* @param guardians - Array of guardian addresses (1..5).
|
|
121
|
+
* @param guardians - Array of guardian addresses (1..5). Must be `pq1…` bech32m form.
|
|
133
122
|
* @param threshold - k-of-n required votes (1 ≤ threshold ≤ guardians.length).
|
|
134
123
|
* @param timelock - Minimum blocks between threshold-reach and execution (≥ 100).
|
|
135
124
|
* @returns `HexString` with the 4-byte selector prepended.
|
|
@@ -137,18 +126,15 @@ export function isSystemContractAddress(address) {
|
|
|
137
126
|
* @example
|
|
138
127
|
* ```typescript
|
|
139
128
|
* const data = encodeSetGuardiansCalldata(
|
|
140
|
-
* ["
|
|
129
|
+
* ["pq1guardian1…", "pq1guardian2…", "pq1guardian3…"],
|
|
141
130
|
* 2, // 2-of-3 threshold
|
|
142
131
|
* 100, // 100-block timelock
|
|
143
132
|
* );
|
|
144
133
|
* ```
|
|
145
134
|
*/
|
|
146
135
|
export function encodeSetGuardiansCalldata(guardians, threshold, timelock) {
|
|
147
|
-
//
|
|
148
|
-
const hexGuardians = guardians.map((g) =>
|
|
149
|
-
const s = typeof g === "string" ? g : bytesToHex(g);
|
|
150
|
-
return (s.startsWith("0x") ? s : `0x${s}`);
|
|
151
|
-
});
|
|
136
|
+
// Decode pq1… addresses to raw 20 bytes, then to 0x-hex for ABI encoding.
|
|
137
|
+
const hexGuardians = guardians.map((g) => bytesToHex(pqAddressToBytes(g)));
|
|
152
138
|
const encoded = encodeAbiParameters([{ type: "address[]" }, { type: "uint8" }, { type: "uint64" }], [hexGuardians, threshold, BigInt(timelock)]);
|
|
153
139
|
return `${setGuardiansSelector}${encoded.slice(2)}`;
|
|
154
140
|
}
|
|
@@ -159,7 +145,7 @@ export function encodeSetGuardiansCalldata(guardians, threshold, timelock) {
|
|
|
159
145
|
* When k-of-n threshold is reached, the proposal becomes executable after
|
|
160
146
|
* `timelock` blocks.
|
|
161
147
|
*
|
|
162
|
-
* @param account - Account being recovered (
|
|
148
|
+
* @param account - Account being recovered (`pq1…` bech32m form).
|
|
163
149
|
* @param newPubkey - Raw bytes of the new PQ public key.
|
|
164
150
|
* @param newAlgo - Algorithm ID for the new key (ML-DSA-65 = 0, etc.).
|
|
165
151
|
* @returns `HexString` with the 4-byte selector prepended.
|
|
@@ -167,7 +153,7 @@ export function encodeSetGuardiansCalldata(guardians, threshold, timelock) {
|
|
|
167
153
|
* @example
|
|
168
154
|
* ```typescript
|
|
169
155
|
* const data = encodeSubmitRecoveryCalldata(
|
|
170
|
-
* "
|
|
156
|
+
* "pq1accountaddress…",
|
|
171
157
|
* newPubkeyBytes,
|
|
172
158
|
* 0, // ML-DSA-65
|
|
173
159
|
* );
|
|
@@ -209,9 +195,7 @@ export function encodeCancelRecoveryCalldata(account) {
|
|
|
209
195
|
// ---------------------------------------------------------------------------
|
|
210
196
|
// Internal helpers
|
|
211
197
|
// ---------------------------------------------------------------------------
|
|
198
|
+
/** Convert a pq1… address to a 0x-hex form for EVM ABI encoding. */
|
|
212
199
|
function normaliseToHex(address) {
|
|
213
|
-
|
|
214
|
-
return bytesToHex(address);
|
|
215
|
-
}
|
|
216
|
-
return (address.startsWith("0x") ? address : `0x${address}`);
|
|
200
|
+
return bytesToHex(pqAddressToBytes(address));
|
|
217
201
|
}
|
package/dist/transactions.js
CHANGED
|
@@ -11,7 +11,7 @@ import { bytesToHex, keccak256, toRlp, hexToBytes } from "viem";
|
|
|
11
11
|
import { AA_BUNDLE_TX_TYPE, AA_MAX_INNER_CALLS } from "./types.js";
|
|
12
12
|
export { AA_BUNDLE_TX_TYPE, AA_MAX_INNER_CALLS };
|
|
13
13
|
import { accountManagerAddress, encodeClearValidationCodeCalldata, encodeRotateKeyCalldata, encodeSetValidationCodeCalldata, } from "./system-contracts.js";
|
|
14
|
-
import {
|
|
14
|
+
import { pqAddressToBytes } from "./address.js";
|
|
15
15
|
/** Default transaction type: `2` (EIP-1559). */
|
|
16
16
|
export const DEFAULT_TX_TYPE = 2;
|
|
17
17
|
/** Default gas limit for simple SHELL token transfers (`21_000`). */
|
|
@@ -40,7 +40,7 @@ function toRlpAccessList(accessList) {
|
|
|
40
40
|
return [];
|
|
41
41
|
}
|
|
42
42
|
return accessList.map((item) => [
|
|
43
|
-
|
|
43
|
+
bytesToHex(pqAddressToBytes(item.address)),
|
|
44
44
|
item.storage_keys.map((key) => key),
|
|
45
45
|
]);
|
|
46
46
|
}
|
|
@@ -249,7 +249,7 @@ export function hashTransaction(tx) {
|
|
|
249
249
|
const fields = [
|
|
250
250
|
toRlpUint(tx.chain_id),
|
|
251
251
|
toRlpUint(tx.nonce),
|
|
252
|
-
tx.to ?
|
|
252
|
+
tx.to ? bytesToHex(pqAddressToBytes(tx.to)) : "0x",
|
|
253
253
|
toRlpUint(tx.value),
|
|
254
254
|
tx.data,
|
|
255
255
|
toRlpUint(tx.gas_limit),
|
|
@@ -368,14 +368,14 @@ export function hashBatchTransaction(tx, bundle) {
|
|
|
368
368
|
}
|
|
369
369
|
// Encode inner calls for signing (matches chain's encode_for_signing).
|
|
370
370
|
const innerCallsRlp = bundle.inner_calls.map((call) => [
|
|
371
|
-
call.to ?
|
|
371
|
+
call.to ? bytesToHex(pqAddressToBytes(call.to)) : "0x",
|
|
372
372
|
toRlpUint(call.value),
|
|
373
373
|
call.data,
|
|
374
374
|
toRlpUint(call.gas_limit),
|
|
375
375
|
]);
|
|
376
376
|
// Paymaster: 20-byte address or empty bytes.
|
|
377
377
|
const paymasterField = bundle.paymaster
|
|
378
|
-
?
|
|
378
|
+
? bytesToHex(pqAddressToBytes(bundle.paymaster))
|
|
379
379
|
: "0x";
|
|
380
380
|
// paymaster_context: raw bytes or empty.
|
|
381
381
|
const paymasterContextField = bundle.paymaster_context && bundle.paymaster_context.length > 0
|
|
@@ -384,7 +384,7 @@ export function hashBatchTransaction(tx, bundle) {
|
|
|
384
384
|
const txFields = [
|
|
385
385
|
toRlpUint(tx.chain_id),
|
|
386
386
|
toRlpUint(tx.nonce),
|
|
387
|
-
tx.to ?
|
|
387
|
+
tx.to ? bytesToHex(pqAddressToBytes(tx.to)) : "0x",
|
|
388
388
|
toRlpUint(tx.value),
|
|
389
389
|
tx.data,
|
|
390
390
|
toRlpUint(tx.gas_limit),
|
package/dist/types.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/** A `0x`-prefixed hex string, e.g. `"0xdeadbeef"`. */
|
|
2
2
|
export type HexString = `0x${string}`;
|
|
3
|
-
/**
|
|
3
|
+
/** A `pq1…` bech32m address string (Shell Chain canonical address format). */
|
|
4
4
|
export type AddressLike = string;
|
|
5
5
|
/** A single entry in an EIP-2930 access list. */
|
|
6
6
|
export interface ShellAccessListItem {
|
|
@@ -18,7 +18,7 @@ export interface ShellTransactionRequest {
|
|
|
18
18
|
chain_id: number;
|
|
19
19
|
/** Sender account nonce. */
|
|
20
20
|
nonce: number;
|
|
21
|
-
/** Recipient address (pq1
|
|
21
|
+
/** Recipient address (`pq1…` bech32m format), or `null` for contract deployment. */
|
|
22
22
|
to: AddressLike | null;
|
|
23
23
|
/** Transfer value as a hex-encoded bigint string, e.g. `"0xde0b6b3a7640000"`. */
|
|
24
24
|
value: string;
|