shell-sdk 0.1.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/LICENSE +21 -0
- package/README.md +773 -0
- package/dist/adapters.d.ts +155 -0
- package/dist/adapters.js +191 -0
- package/dist/address.d.ts +119 -0
- package/dist/address.js +220 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +7 -0
- package/dist/keystore.d.ts +99 -0
- package/dist/keystore.js +166 -0
- package/dist/provider.d.ts +204 -0
- package/dist/provider.js +208 -0
- package/dist/signer.d.ts +161 -0
- package/dist/signer.js +188 -0
- package/dist/system-contracts.d.ts +65 -0
- package/dist/system-contracts.js +105 -0
- package/dist/transactions.d.ts +208 -0
- package/dist/transactions.js +250 -0
- package/dist/types.d.ts +140 -0
- package/dist/types.js +1 -0
- package/package.json +82 -0
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Concrete SignerAdapter implementations for each PQ algorithm.
|
|
3
|
+
*
|
|
4
|
+
* @noble/post-quantum provides ML-DSA-65 and SLH-DSA-SHA2-256f.
|
|
5
|
+
* Dilithium3 (pre-FIPS Round-3) uses {@link MlDsa65Adapter} as a stand-in.
|
|
6
|
+
*
|
|
7
|
+
* @module adapters
|
|
8
|
+
*/
|
|
9
|
+
import type { SignerAdapter } from "./signer.js";
|
|
10
|
+
import type { SignatureTypeName } from "./types.js";
|
|
11
|
+
/** Key pair produced by {@link generateMlDsa65KeyPair}. */
|
|
12
|
+
export interface MlDsa65KeyPair {
|
|
13
|
+
publicKey: Uint8Array;
|
|
14
|
+
secretKey: Uint8Array;
|
|
15
|
+
}
|
|
16
|
+
/** Key pair produced by {@link generateSlhDsaKeyPair}. */
|
|
17
|
+
export interface SlhDsaKeyPair {
|
|
18
|
+
publicKey: Uint8Array;
|
|
19
|
+
secretKey: Uint8Array;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Generate a fresh ML-DSA-65 key pair.
|
|
23
|
+
*
|
|
24
|
+
* @param seed - Optional 32-byte deterministic seed. A random seed is used when omitted.
|
|
25
|
+
* @returns `{ publicKey, secretKey }` — public key is 1952 bytes, secret key is 4032 bytes.
|
|
26
|
+
*
|
|
27
|
+
* @example
|
|
28
|
+
* ```typescript
|
|
29
|
+
* const { publicKey, secretKey } = generateMlDsa65KeyPair();
|
|
30
|
+
* ```
|
|
31
|
+
*/
|
|
32
|
+
export declare function generateMlDsa65KeyPair(seed?: Uint8Array): MlDsa65KeyPair;
|
|
33
|
+
/**
|
|
34
|
+
* Generate a fresh SLH-DSA-SHA2-256f key pair.
|
|
35
|
+
*
|
|
36
|
+
* @param seed - Optional 96-byte deterministic seed. A random seed is used when omitted.
|
|
37
|
+
* @returns `{ publicKey, secretKey }` — public key is 64 bytes, secret key is 128 bytes.
|
|
38
|
+
*
|
|
39
|
+
* @example
|
|
40
|
+
* ```typescript
|
|
41
|
+
* const { publicKey, secretKey } = generateSlhDsaKeyPair();
|
|
42
|
+
* ```
|
|
43
|
+
*/
|
|
44
|
+
export declare function generateSlhDsaKeyPair(seed?: Uint8Array): SlhDsaKeyPair;
|
|
45
|
+
/**
|
|
46
|
+
* {@link SignerAdapter} for ML-DSA-65 (NIST FIPS 204).
|
|
47
|
+
*
|
|
48
|
+
* Also used as the implementation for `"Dilithium3"` (the pre-standardisation
|
|
49
|
+
* Round-3 variant) since the wire format is compatible.
|
|
50
|
+
*
|
|
51
|
+
* @example
|
|
52
|
+
* ```typescript
|
|
53
|
+
* // Generate a fresh key pair
|
|
54
|
+
* const adapter = MlDsa65Adapter.generate();
|
|
55
|
+
*
|
|
56
|
+
* // Load an existing key pair (e.g. from a keystore)
|
|
57
|
+
* const adapter = MlDsa65Adapter.fromKeyPair(publicKey, secretKey);
|
|
58
|
+
* ```
|
|
59
|
+
*/
|
|
60
|
+
export declare class MlDsa65Adapter implements SignerAdapter {
|
|
61
|
+
private readonly _publicKey;
|
|
62
|
+
private readonly _secretKey;
|
|
63
|
+
constructor(_publicKey: Uint8Array, _secretKey: Uint8Array);
|
|
64
|
+
/**
|
|
65
|
+
* Generate a fresh ML-DSA-65 key pair and wrap it in an adapter.
|
|
66
|
+
*
|
|
67
|
+
* @param seed - Optional 32-byte deterministic seed.
|
|
68
|
+
*/
|
|
69
|
+
static generate(seed?: Uint8Array): MlDsa65Adapter;
|
|
70
|
+
/**
|
|
71
|
+
* Wrap an existing ML-DSA-65 key pair in an adapter.
|
|
72
|
+
*
|
|
73
|
+
* @param pk - Raw public key bytes (1952 bytes).
|
|
74
|
+
* @param sk - Raw secret key bytes (4032 bytes).
|
|
75
|
+
*/
|
|
76
|
+
static fromKeyPair(pk: Uint8Array, sk: Uint8Array): MlDsa65Adapter;
|
|
77
|
+
/** Return the raw ML-DSA-65 public key bytes (1952 bytes). */
|
|
78
|
+
getPublicKey(): Uint8Array;
|
|
79
|
+
/**
|
|
80
|
+
* Sign `message` with ML-DSA-65 and return the raw signature bytes.
|
|
81
|
+
*
|
|
82
|
+
* @param message - The bytes to sign (typically an RLP-encoded tx hash).
|
|
83
|
+
*/
|
|
84
|
+
sign(message: Uint8Array): Promise<Uint8Array>;
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* {@link SignerAdapter} for SLH-DSA-SHA2-256f (NIST FIPS 205).
|
|
88
|
+
*
|
|
89
|
+
* Corresponds to the `"SphincsSha2256f"` signature type. Produces much larger
|
|
90
|
+
* signatures (~49 KB) than ML-DSA-65 but offers stronger security assumptions.
|
|
91
|
+
*
|
|
92
|
+
* @example
|
|
93
|
+
* ```typescript
|
|
94
|
+
* const adapter = SlhDsaAdapter.generate();
|
|
95
|
+
* const adapter = SlhDsaAdapter.fromKeyPair(publicKey, secretKey);
|
|
96
|
+
* ```
|
|
97
|
+
*/
|
|
98
|
+
export declare class SlhDsaAdapter implements SignerAdapter {
|
|
99
|
+
private readonly _publicKey;
|
|
100
|
+
private readonly _secretKey;
|
|
101
|
+
constructor(_publicKey: Uint8Array, _secretKey: Uint8Array);
|
|
102
|
+
/**
|
|
103
|
+
* Generate a fresh SLH-DSA-SHA2-256f key pair and wrap it in an adapter.
|
|
104
|
+
*
|
|
105
|
+
* @param seed - Optional 96-byte deterministic seed.
|
|
106
|
+
*/
|
|
107
|
+
static generate(seed?: Uint8Array): SlhDsaAdapter;
|
|
108
|
+
/**
|
|
109
|
+
* Wrap an existing SLH-DSA-SHA2-256f key pair in an adapter.
|
|
110
|
+
*
|
|
111
|
+
* @param pk - Raw public key bytes (64 bytes).
|
|
112
|
+
* @param sk - Raw secret key bytes (128 bytes).
|
|
113
|
+
*/
|
|
114
|
+
static fromKeyPair(pk: Uint8Array, sk: Uint8Array): SlhDsaAdapter;
|
|
115
|
+
/** Return the raw SLH-DSA public key bytes (64 bytes). */
|
|
116
|
+
getPublicKey(): Uint8Array;
|
|
117
|
+
/**
|
|
118
|
+
* Sign `message` with SLH-DSA-SHA2-256f and return the raw signature bytes.
|
|
119
|
+
*
|
|
120
|
+
* @param message - The bytes to sign (typically an RLP-encoded tx hash).
|
|
121
|
+
*/
|
|
122
|
+
sign(message: Uint8Array): Promise<Uint8Array>;
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Generate a fresh {@link SignerAdapter} for the given algorithm.
|
|
126
|
+
*
|
|
127
|
+
* A convenience factory that dispatches to the correct adapter class.
|
|
128
|
+
*
|
|
129
|
+
* @param algorithm - The PQ algorithm to use.
|
|
130
|
+
* @param seed - Optional deterministic seed (32 bytes for ML-DSA-65, 96 bytes for SLH-DSA).
|
|
131
|
+
* @returns A ready-to-use `SignerAdapter`.
|
|
132
|
+
* @throws {Error} If `algorithm` is not one of the supported values.
|
|
133
|
+
*
|
|
134
|
+
* @example
|
|
135
|
+
* ```typescript
|
|
136
|
+
* const adapter = generateAdapter("MlDsa65");
|
|
137
|
+
* const adapter = generateAdapter("SphincsSha2256f", mySeed);
|
|
138
|
+
* ```
|
|
139
|
+
*/
|
|
140
|
+
export declare function generateAdapter(algorithm: SignatureTypeName, seed?: Uint8Array): SignerAdapter;
|
|
141
|
+
/**
|
|
142
|
+
* Build a {@link SignerAdapter} from an existing key pair (e.g. loaded from a keystore).
|
|
143
|
+
*
|
|
144
|
+
* @param algorithm - The PQ algorithm.
|
|
145
|
+
* @param publicKey - Raw public key bytes.
|
|
146
|
+
* @param secretKey - Raw secret key bytes.
|
|
147
|
+
* @returns A `SignerAdapter` backed by the provided key pair.
|
|
148
|
+
* @throws {Error} If `algorithm` is not one of the supported values.
|
|
149
|
+
*
|
|
150
|
+
* @example
|
|
151
|
+
* ```typescript
|
|
152
|
+
* const adapter = adapterFromKeyPair("MlDsa65", publicKey, secretKey);
|
|
153
|
+
* ```
|
|
154
|
+
*/
|
|
155
|
+
export declare function adapterFromKeyPair(algorithm: SignatureTypeName, publicKey: Uint8Array, secretKey: Uint8Array): SignerAdapter;
|
package/dist/adapters.js
ADDED
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Concrete SignerAdapter implementations for each PQ algorithm.
|
|
3
|
+
*
|
|
4
|
+
* @noble/post-quantum provides ML-DSA-65 and SLH-DSA-SHA2-256f.
|
|
5
|
+
* Dilithium3 (pre-FIPS Round-3) uses {@link MlDsa65Adapter} as a stand-in.
|
|
6
|
+
*
|
|
7
|
+
* @module adapters
|
|
8
|
+
*/
|
|
9
|
+
import { ml_dsa65 } from "@noble/post-quantum/ml-dsa.js";
|
|
10
|
+
import { slh_dsa_sha2_256f } from "@noble/post-quantum/slh-dsa.js";
|
|
11
|
+
/**
|
|
12
|
+
* Generate a fresh ML-DSA-65 key pair.
|
|
13
|
+
*
|
|
14
|
+
* @param seed - Optional 32-byte deterministic seed. A random seed is used when omitted.
|
|
15
|
+
* @returns `{ publicKey, secretKey }` — public key is 1952 bytes, secret key is 4032 bytes.
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* ```typescript
|
|
19
|
+
* const { publicKey, secretKey } = generateMlDsa65KeyPair();
|
|
20
|
+
* ```
|
|
21
|
+
*/
|
|
22
|
+
export function generateMlDsa65KeyPair(seed) {
|
|
23
|
+
const s = seed ?? crypto.getRandomValues(new Uint8Array(32));
|
|
24
|
+
return ml_dsa65.keygen(s);
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Generate a fresh SLH-DSA-SHA2-256f key pair.
|
|
28
|
+
*
|
|
29
|
+
* @param seed - Optional 96-byte deterministic seed. A random seed is used when omitted.
|
|
30
|
+
* @returns `{ publicKey, secretKey }` — public key is 64 bytes, secret key is 128 bytes.
|
|
31
|
+
*
|
|
32
|
+
* @example
|
|
33
|
+
* ```typescript
|
|
34
|
+
* const { publicKey, secretKey } = generateSlhDsaKeyPair();
|
|
35
|
+
* ```
|
|
36
|
+
*/
|
|
37
|
+
export function generateSlhDsaKeyPair(seed) {
|
|
38
|
+
const s = seed ?? crypto.getRandomValues(new Uint8Array(96));
|
|
39
|
+
return slh_dsa_sha2_256f.keygen(s);
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* {@link SignerAdapter} for ML-DSA-65 (NIST FIPS 204).
|
|
43
|
+
*
|
|
44
|
+
* Also used as the implementation for `"Dilithium3"` (the pre-standardisation
|
|
45
|
+
* Round-3 variant) since the wire format is compatible.
|
|
46
|
+
*
|
|
47
|
+
* @example
|
|
48
|
+
* ```typescript
|
|
49
|
+
* // Generate a fresh key pair
|
|
50
|
+
* const adapter = MlDsa65Adapter.generate();
|
|
51
|
+
*
|
|
52
|
+
* // Load an existing key pair (e.g. from a keystore)
|
|
53
|
+
* const adapter = MlDsa65Adapter.fromKeyPair(publicKey, secretKey);
|
|
54
|
+
* ```
|
|
55
|
+
*/
|
|
56
|
+
export class MlDsa65Adapter {
|
|
57
|
+
_publicKey;
|
|
58
|
+
_secretKey;
|
|
59
|
+
constructor(_publicKey, _secretKey) {
|
|
60
|
+
this._publicKey = _publicKey;
|
|
61
|
+
this._secretKey = _secretKey;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Generate a fresh ML-DSA-65 key pair and wrap it in an adapter.
|
|
65
|
+
*
|
|
66
|
+
* @param seed - Optional 32-byte deterministic seed.
|
|
67
|
+
*/
|
|
68
|
+
static generate(seed) {
|
|
69
|
+
const kp = generateMlDsa65KeyPair(seed);
|
|
70
|
+
return new MlDsa65Adapter(kp.publicKey, kp.secretKey);
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Wrap an existing ML-DSA-65 key pair in an adapter.
|
|
74
|
+
*
|
|
75
|
+
* @param pk - Raw public key bytes (1952 bytes).
|
|
76
|
+
* @param sk - Raw secret key bytes (4032 bytes).
|
|
77
|
+
*/
|
|
78
|
+
static fromKeyPair(pk, sk) {
|
|
79
|
+
return new MlDsa65Adapter(pk, sk);
|
|
80
|
+
}
|
|
81
|
+
/** Return the raw ML-DSA-65 public key bytes (1952 bytes). */
|
|
82
|
+
getPublicKey() { return this._publicKey; }
|
|
83
|
+
/**
|
|
84
|
+
* Sign `message` with ML-DSA-65 and return the raw signature bytes.
|
|
85
|
+
*
|
|
86
|
+
* @param message - The bytes to sign (typically an RLP-encoded tx hash).
|
|
87
|
+
*/
|
|
88
|
+
async sign(message) {
|
|
89
|
+
return ml_dsa65.sign(this._secretKey, message);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* {@link SignerAdapter} for SLH-DSA-SHA2-256f (NIST FIPS 205).
|
|
94
|
+
*
|
|
95
|
+
* Corresponds to the `"SphincsSha2256f"` signature type. Produces much larger
|
|
96
|
+
* signatures (~49 KB) than ML-DSA-65 but offers stronger security assumptions.
|
|
97
|
+
*
|
|
98
|
+
* @example
|
|
99
|
+
* ```typescript
|
|
100
|
+
* const adapter = SlhDsaAdapter.generate();
|
|
101
|
+
* const adapter = SlhDsaAdapter.fromKeyPair(publicKey, secretKey);
|
|
102
|
+
* ```
|
|
103
|
+
*/
|
|
104
|
+
export class SlhDsaAdapter {
|
|
105
|
+
_publicKey;
|
|
106
|
+
_secretKey;
|
|
107
|
+
constructor(_publicKey, _secretKey) {
|
|
108
|
+
this._publicKey = _publicKey;
|
|
109
|
+
this._secretKey = _secretKey;
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Generate a fresh SLH-DSA-SHA2-256f key pair and wrap it in an adapter.
|
|
113
|
+
*
|
|
114
|
+
* @param seed - Optional 96-byte deterministic seed.
|
|
115
|
+
*/
|
|
116
|
+
static generate(seed) {
|
|
117
|
+
const kp = generateSlhDsaKeyPair(seed);
|
|
118
|
+
return new SlhDsaAdapter(kp.publicKey, kp.secretKey);
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Wrap an existing SLH-DSA-SHA2-256f key pair in an adapter.
|
|
122
|
+
*
|
|
123
|
+
* @param pk - Raw public key bytes (64 bytes).
|
|
124
|
+
* @param sk - Raw secret key bytes (128 bytes).
|
|
125
|
+
*/
|
|
126
|
+
static fromKeyPair(pk, sk) {
|
|
127
|
+
return new SlhDsaAdapter(pk, sk);
|
|
128
|
+
}
|
|
129
|
+
/** Return the raw SLH-DSA public key bytes (64 bytes). */
|
|
130
|
+
getPublicKey() { return this._publicKey; }
|
|
131
|
+
/**
|
|
132
|
+
* Sign `message` with SLH-DSA-SHA2-256f and return the raw signature bytes.
|
|
133
|
+
*
|
|
134
|
+
* @param message - The bytes to sign (typically an RLP-encoded tx hash).
|
|
135
|
+
*/
|
|
136
|
+
async sign(message) {
|
|
137
|
+
return slh_dsa_sha2_256f.sign(this._secretKey, message);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Generate a fresh {@link SignerAdapter} for the given algorithm.
|
|
142
|
+
*
|
|
143
|
+
* A convenience factory that dispatches to the correct adapter class.
|
|
144
|
+
*
|
|
145
|
+
* @param algorithm - The PQ algorithm to use.
|
|
146
|
+
* @param seed - Optional deterministic seed (32 bytes for ML-DSA-65, 96 bytes for SLH-DSA).
|
|
147
|
+
* @returns A ready-to-use `SignerAdapter`.
|
|
148
|
+
* @throws {Error} If `algorithm` is not one of the supported values.
|
|
149
|
+
*
|
|
150
|
+
* @example
|
|
151
|
+
* ```typescript
|
|
152
|
+
* const adapter = generateAdapter("MlDsa65");
|
|
153
|
+
* const adapter = generateAdapter("SphincsSha2256f", mySeed);
|
|
154
|
+
* ```
|
|
155
|
+
*/
|
|
156
|
+
export function generateAdapter(algorithm, seed) {
|
|
157
|
+
switch (algorithm) {
|
|
158
|
+
case "Dilithium3":
|
|
159
|
+
case "MlDsa65":
|
|
160
|
+
return MlDsa65Adapter.generate(seed);
|
|
161
|
+
case "SphincsSha2256f":
|
|
162
|
+
return SlhDsaAdapter.generate(seed ? seed.slice(0, 96) : undefined);
|
|
163
|
+
default:
|
|
164
|
+
throw new Error("unsupported algorithm: " + algorithm);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Build a {@link SignerAdapter} from an existing key pair (e.g. loaded from a keystore).
|
|
169
|
+
*
|
|
170
|
+
* @param algorithm - The PQ algorithm.
|
|
171
|
+
* @param publicKey - Raw public key bytes.
|
|
172
|
+
* @param secretKey - Raw secret key bytes.
|
|
173
|
+
* @returns A `SignerAdapter` backed by the provided key pair.
|
|
174
|
+
* @throws {Error} If `algorithm` is not one of the supported values.
|
|
175
|
+
*
|
|
176
|
+
* @example
|
|
177
|
+
* ```typescript
|
|
178
|
+
* const adapter = adapterFromKeyPair("MlDsa65", publicKey, secretKey);
|
|
179
|
+
* ```
|
|
180
|
+
*/
|
|
181
|
+
export function adapterFromKeyPair(algorithm, publicKey, secretKey) {
|
|
182
|
+
switch (algorithm) {
|
|
183
|
+
case "Dilithium3":
|
|
184
|
+
case "MlDsa65":
|
|
185
|
+
return MlDsa65Adapter.fromKeyPair(publicKey, secretKey);
|
|
186
|
+
case "SphincsSha2256f":
|
|
187
|
+
return SlhDsaAdapter.fromKeyPair(publicKey, secretKey);
|
|
188
|
+
default:
|
|
189
|
+
throw new Error("unsupported algorithm: " + algorithm);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
/** Human-readable part (HRP) used in Shell bech32m addresses. */
|
|
2
|
+
export declare const PQ_ADDRESS_HRP = "pq";
|
|
3
|
+
/** Number of raw address bytes (excluding the version byte). */
|
|
4
|
+
export declare const PQ_ADDRESS_LENGTH = 20;
|
|
5
|
+
/** Version byte for V1 Shell addresses (`0x01`). */
|
|
6
|
+
export declare const PQ_ADDRESS_VERSION_V1 = 1;
|
|
7
|
+
type HexAddress = `0x${string}`;
|
|
8
|
+
/**
|
|
9
|
+
* Encode 20 raw address bytes as a `pq1…` bech32m address.
|
|
10
|
+
*
|
|
11
|
+
* @param bytes - Exactly 20 address bytes (the 20-byte hash derived from a public key).
|
|
12
|
+
* @param version - Address version byte; defaults to {@link PQ_ADDRESS_VERSION_V1} (`0x01`).
|
|
13
|
+
* @returns The bech32m-encoded address string, e.g. `"pq1qx3f…"`.
|
|
14
|
+
* @throws {Error} If `bytes.length !== 20` or `version` is out of the 0–255 range.
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* ```typescript
|
|
18
|
+
* const addr = bytesToPqAddress(hashBytes);
|
|
19
|
+
* // → "pq1qx3f…"
|
|
20
|
+
* ```
|
|
21
|
+
*/
|
|
22
|
+
export declare function bytesToPqAddress(bytes: Uint8Array, version?: number): string;
|
|
23
|
+
/**
|
|
24
|
+
* Decode a `pq1…` bech32m address to its raw 20 address bytes.
|
|
25
|
+
*
|
|
26
|
+
* The version byte is stripped; use {@link pqAddressVersion} to retrieve it.
|
|
27
|
+
*
|
|
28
|
+
* @param address - A valid `pq1…` bech32m address.
|
|
29
|
+
* @returns The 20-byte address payload (version byte excluded).
|
|
30
|
+
* @throws {Error} If the prefix is not `"pq"` or the payload length is wrong.
|
|
31
|
+
*
|
|
32
|
+
* @example
|
|
33
|
+
* ```typescript
|
|
34
|
+
* const bytes = pqAddressToBytes("pq1qx3f…");
|
|
35
|
+
* // bytes.length === 20
|
|
36
|
+
* ```
|
|
37
|
+
*/
|
|
38
|
+
export declare function pqAddressToBytes(address: string): Uint8Array;
|
|
39
|
+
/**
|
|
40
|
+
* Extract the version byte from a `pq1…` bech32m address.
|
|
41
|
+
*
|
|
42
|
+
* @param address - A valid `pq1…` bech32m address.
|
|
43
|
+
* @returns The version byte (e.g. `1` for V1 addresses).
|
|
44
|
+
* @throws {Error} If the address is malformed.
|
|
45
|
+
*/
|
|
46
|
+
export declare function pqAddressVersion(address: string): number;
|
|
47
|
+
/**
|
|
48
|
+
* Parse a `0x…` hex address string into its raw 20 bytes.
|
|
49
|
+
*
|
|
50
|
+
* @param address - A `0x`-prefixed 40-character hex address.
|
|
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.
|
|
65
|
+
*
|
|
66
|
+
* Accepts either a `pq1…` or `0x…` address and always returns the canonical
|
|
67
|
+
* bech32m form.
|
|
68
|
+
*
|
|
69
|
+
* @param address - A `pq1…` or `0x…` address.
|
|
70
|
+
* @returns The `pq1…` bech32m address.
|
|
71
|
+
*/
|
|
72
|
+
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
|
+
/**
|
|
83
|
+
* Derive a `pq1…` address from a raw post-quantum public key.
|
|
84
|
+
*
|
|
85
|
+
* The derivation is:
|
|
86
|
+
* ```
|
|
87
|
+
* address_bytes = blake3(version || algo_id || public_key)[0..20]
|
|
88
|
+
* ```
|
|
89
|
+
*
|
|
90
|
+
* @param publicKey - Raw public key bytes (length depends on algorithm).
|
|
91
|
+
* @param algorithmId - Numeric algorithm ID: Dilithium3=0, MlDsa65=1, SphincsSha2256f=2.
|
|
92
|
+
* @param version - Address version byte; defaults to {@link PQ_ADDRESS_VERSION_V1}.
|
|
93
|
+
* @returns The derived `pq1…` bech32m address.
|
|
94
|
+
* @throws {Error} If `algorithmId` is outside 0–255.
|
|
95
|
+
*
|
|
96
|
+
* @example
|
|
97
|
+
* ```typescript
|
|
98
|
+
* const address = derivePqAddressFromPublicKey(publicKey, 1 /* MlDsa65 *\/);
|
|
99
|
+
* // → "pq1qx3f…"
|
|
100
|
+
* ```
|
|
101
|
+
*/
|
|
102
|
+
export declare function derivePqAddressFromPublicKey(publicKey: Uint8Array, algorithmId: number, version?: number): string;
|
|
103
|
+
/**
|
|
104
|
+
* Return `true` if `address` is a structurally valid `pq1…` bech32m address.
|
|
105
|
+
*
|
|
106
|
+
* Does **not** check whether the address exists on-chain.
|
|
107
|
+
*
|
|
108
|
+
* @param address - Any string to test.
|
|
109
|
+
* @returns `true` if the string is a valid Shell bech32m address.
|
|
110
|
+
*
|
|
111
|
+
* @example
|
|
112
|
+
* ```typescript
|
|
113
|
+
* isPqAddress("pq1qx3f…"); // true
|
|
114
|
+
* isPqAddress("0xabc…"); // false
|
|
115
|
+
* isPqAddress("garbage"); // false
|
|
116
|
+
* ```
|
|
117
|
+
*/
|
|
118
|
+
export declare function isPqAddress(address: string): boolean;
|
|
119
|
+
export {};
|
package/dist/address.js
ADDED
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PQ address utilities for Shell Chain.
|
|
3
|
+
*
|
|
4
|
+
* Shell Chain uses **bech32m**-encoded addresses (prefix `"pq"`) instead of
|
|
5
|
+
* Ethereum's checksummed hex format. Each address encodes a version byte and
|
|
6
|
+
* 20 address bytes derived from the account's post-quantum public key:
|
|
7
|
+
*
|
|
8
|
+
* ```
|
|
9
|
+
* address_bytes = blake3(version || algo_id || public_key)[0..20]
|
|
10
|
+
* bech32m_address = bech32m_encode("pq", [version, ...address_bytes])
|
|
11
|
+
* ```
|
|
12
|
+
*
|
|
13
|
+
* Algorithm IDs: Dilithium3=0, MlDsa65=1, SphincsSha2256f=2.
|
|
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
|
+
* @module address
|
|
19
|
+
*/
|
|
20
|
+
import { blake3 } from "@noble/hashes/blake3";
|
|
21
|
+
import { bech32m } from "@scure/base";
|
|
22
|
+
import { bytesToHex, hexToBytes } from "viem";
|
|
23
|
+
/** Human-readable part (HRP) used in Shell bech32m addresses. */
|
|
24
|
+
export const PQ_ADDRESS_HRP = "pq";
|
|
25
|
+
/** Number of raw address bytes (excluding the version byte). */
|
|
26
|
+
export const PQ_ADDRESS_LENGTH = 20;
|
|
27
|
+
/** Version byte for V1 Shell addresses (`0x01`). */
|
|
28
|
+
export const PQ_ADDRESS_VERSION_V1 = 0x01;
|
|
29
|
+
function assertBech32Address(value) {
|
|
30
|
+
if (!value.includes("1")) {
|
|
31
|
+
throw new Error("invalid bech32m address");
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
function assertHexAddress(value) {
|
|
35
|
+
if (!value.startsWith("0x")) {
|
|
36
|
+
throw new Error("invalid hex address");
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Encode 20 raw address bytes as a `pq1…` bech32m address.
|
|
41
|
+
*
|
|
42
|
+
* @param bytes - Exactly 20 address bytes (the 20-byte hash derived from a public key).
|
|
43
|
+
* @param version - Address version byte; defaults to {@link PQ_ADDRESS_VERSION_V1} (`0x01`).
|
|
44
|
+
* @returns The bech32m-encoded address string, e.g. `"pq1qx3f…"`.
|
|
45
|
+
* @throws {Error} If `bytes.length !== 20` or `version` is out of the 0–255 range.
|
|
46
|
+
*
|
|
47
|
+
* @example
|
|
48
|
+
* ```typescript
|
|
49
|
+
* const addr = bytesToPqAddress(hashBytes);
|
|
50
|
+
* // → "pq1qx3f…"
|
|
51
|
+
* ```
|
|
52
|
+
*/
|
|
53
|
+
export function bytesToPqAddress(bytes, version = PQ_ADDRESS_VERSION_V1) {
|
|
54
|
+
if (bytes.length !== PQ_ADDRESS_LENGTH) {
|
|
55
|
+
throw new Error(`expected ${PQ_ADDRESS_LENGTH} address bytes, got ${bytes.length}`);
|
|
56
|
+
}
|
|
57
|
+
if (version < 0 || version > 255) {
|
|
58
|
+
throw new Error(`invalid address version: ${version}`);
|
|
59
|
+
}
|
|
60
|
+
const payload = new Uint8Array(1 + PQ_ADDRESS_LENGTH);
|
|
61
|
+
payload[0] = version;
|
|
62
|
+
payload.set(bytes, 1);
|
|
63
|
+
return bech32m.encode(PQ_ADDRESS_HRP, bech32m.toWords(payload));
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Decode a `pq1…` bech32m address to its raw 20 address bytes.
|
|
67
|
+
*
|
|
68
|
+
* The version byte is stripped; use {@link pqAddressVersion} to retrieve it.
|
|
69
|
+
*
|
|
70
|
+
* @param address - A valid `pq1…` bech32m address.
|
|
71
|
+
* @returns The 20-byte address payload (version byte excluded).
|
|
72
|
+
* @throws {Error} If the prefix is not `"pq"` or the payload length is wrong.
|
|
73
|
+
*
|
|
74
|
+
* @example
|
|
75
|
+
* ```typescript
|
|
76
|
+
* const bytes = pqAddressToBytes("pq1qx3f…");
|
|
77
|
+
* // bytes.length === 20
|
|
78
|
+
* ```
|
|
79
|
+
*/
|
|
80
|
+
export function pqAddressToBytes(address) {
|
|
81
|
+
assertBech32Address(address);
|
|
82
|
+
const { prefix, words } = bech32m.decode(address);
|
|
83
|
+
if (prefix !== PQ_ADDRESS_HRP) {
|
|
84
|
+
throw new Error(`expected ${PQ_ADDRESS_HRP} address prefix, got ${prefix}`);
|
|
85
|
+
}
|
|
86
|
+
const bytes = Uint8Array.from(bech32m.fromWords(words));
|
|
87
|
+
if (bytes.length !== 1 + PQ_ADDRESS_LENGTH) {
|
|
88
|
+
throw new Error(`expected ${1 + PQ_ADDRESS_LENGTH} address bytes, got ${bytes.length}`);
|
|
89
|
+
}
|
|
90
|
+
return bytes.slice(1);
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Extract the version byte from a `pq1…` bech32m address.
|
|
94
|
+
*
|
|
95
|
+
* @param address - A valid `pq1…` bech32m address.
|
|
96
|
+
* @returns The version byte (e.g. `1` for V1 addresses).
|
|
97
|
+
* @throws {Error} If the address is malformed.
|
|
98
|
+
*/
|
|
99
|
+
export function pqAddressVersion(address) {
|
|
100
|
+
assertBech32Address(address);
|
|
101
|
+
const { words } = bech32m.decode(address);
|
|
102
|
+
const bytes = Uint8Array.from(bech32m.fromWords(words));
|
|
103
|
+
if (bytes.length !== 1 + PQ_ADDRESS_LENGTH) {
|
|
104
|
+
throw new Error(`expected ${1 + PQ_ADDRESS_LENGTH} address bytes, got ${bytes.length}`);
|
|
105
|
+
}
|
|
106
|
+
return bytes[0];
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Parse a `0x…` hex address string into its raw 20 bytes.
|
|
110
|
+
*
|
|
111
|
+
* @param address - A `0x`-prefixed 40-character hex address.
|
|
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.
|
|
125
|
+
*
|
|
126
|
+
* @param bytes - Exactly 20 address bytes.
|
|
127
|
+
* @returns A `0x`-prefixed 40-character hex string.
|
|
128
|
+
* @throws {Error} If `bytes.length !== 20`.
|
|
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.
|
|
144
|
+
*/
|
|
145
|
+
export function normalizePqAddress(address) {
|
|
146
|
+
if (isPqAddress(address)) {
|
|
147
|
+
return address;
|
|
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));
|
|
162
|
+
}
|
|
163
|
+
assertHexAddress(address);
|
|
164
|
+
return address;
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* Derive a `pq1…` address from a raw post-quantum public key.
|
|
168
|
+
*
|
|
169
|
+
* The derivation is:
|
|
170
|
+
* ```
|
|
171
|
+
* address_bytes = blake3(version || algo_id || public_key)[0..20]
|
|
172
|
+
* ```
|
|
173
|
+
*
|
|
174
|
+
* @param publicKey - Raw public key bytes (length depends on algorithm).
|
|
175
|
+
* @param algorithmId - Numeric algorithm ID: Dilithium3=0, MlDsa65=1, SphincsSha2256f=2.
|
|
176
|
+
* @param version - Address version byte; defaults to {@link PQ_ADDRESS_VERSION_V1}.
|
|
177
|
+
* @returns The derived `pq1…` bech32m address.
|
|
178
|
+
* @throws {Error} If `algorithmId` is outside 0–255.
|
|
179
|
+
*
|
|
180
|
+
* @example
|
|
181
|
+
* ```typescript
|
|
182
|
+
* const address = derivePqAddressFromPublicKey(publicKey, 1 /* MlDsa65 *\/);
|
|
183
|
+
* // → "pq1qx3f…"
|
|
184
|
+
* ```
|
|
185
|
+
*/
|
|
186
|
+
export function derivePqAddressFromPublicKey(publicKey, algorithmId, version = PQ_ADDRESS_VERSION_V1) {
|
|
187
|
+
if (algorithmId < 0 || algorithmId > 255) {
|
|
188
|
+
throw new Error(`invalid algorithm id: ${algorithmId}`);
|
|
189
|
+
}
|
|
190
|
+
const input = new Uint8Array(2 + publicKey.length);
|
|
191
|
+
input[0] = version;
|
|
192
|
+
input[1] = algorithmId;
|
|
193
|
+
input.set(publicKey, 2);
|
|
194
|
+
const hash = blake3(input);
|
|
195
|
+
return bytesToPqAddress(hash.slice(0, PQ_ADDRESS_LENGTH), version);
|
|
196
|
+
}
|
|
197
|
+
/**
|
|
198
|
+
* Return `true` if `address` is a structurally valid `pq1…` bech32m address.
|
|
199
|
+
*
|
|
200
|
+
* Does **not** check whether the address exists on-chain.
|
|
201
|
+
*
|
|
202
|
+
* @param address - Any string to test.
|
|
203
|
+
* @returns `true` if the string is a valid Shell bech32m address.
|
|
204
|
+
*
|
|
205
|
+
* @example
|
|
206
|
+
* ```typescript
|
|
207
|
+
* isPqAddress("pq1qx3f…"); // true
|
|
208
|
+
* isPqAddress("0xabc…"); // false
|
|
209
|
+
* isPqAddress("garbage"); // false
|
|
210
|
+
* ```
|
|
211
|
+
*/
|
|
212
|
+
export function isPqAddress(address) {
|
|
213
|
+
try {
|
|
214
|
+
pqAddressToBytes(address);
|
|
215
|
+
return true;
|
|
216
|
+
}
|
|
217
|
+
catch {
|
|
218
|
+
return false;
|
|
219
|
+
}
|
|
220
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export { bytesToHexAddress, PQ_ADDRESS_HRP, PQ_ADDRESS_LENGTH, PQ_ADDRESS_VERSION_V1, bytesToPqAddress, derivePqAddressFromPublicKey, isPqAddress, normalizeHexAddress, normalizePqAddress, pqAddressVersion, pqAddressToBytes, } from "./address.js";
|
|
2
|
+
export { createShellProvider, createShellPublicClient, createShellWsClient, ShellProvider, shellDevnet, type CreateShellPublicClientOptions, } from "./provider.js";
|
|
3
|
+
export { accountManagerAddress, accountManagerHexAddress, clearValidationCodeSelector, encodeClearValidationCodeCalldata, encodeRotateKeyCalldata, encodeSetValidationCodeCalldata, isSystemContractAddress, rotateKeySelector, setValidationCodeSelector, validatorRegistryAddress, validatorRegistryHexAddress, } from "./system-contracts.js";
|
|
4
|
+
export { buildClearValidationCodeTransaction, buildRotateKeyTransaction, buildSetValidationCodeTransaction, buildSignature, buildSignedTransaction, buildSystemTransaction, buildTransaction, buildTransferTransaction, DEFAULT_MAX_FEE_PER_GAS, DEFAULT_MAX_PRIORITY_FEE_PER_GAS, DEFAULT_SYSTEM_GAS_LIMIT, DEFAULT_TRANSFER_GAS_LIMIT, DEFAULT_TX_TYPE, hashTransaction, hexBytes, } from "./transactions.js";
|
|
5
|
+
export { assertSignerMatchesKeystore, decryptKeystore, exportEncryptedKeyJson, parseEncryptedKey, validateEncryptedKeyAddress, type ParsedShellKeystore, } from "./keystore.js";
|
|
6
|
+
export { adapterFromKeyPair, generateAdapter, generateMlDsa65KeyPair, generateSlhDsaKeyPair, MlDsa65Adapter, SlhDsaAdapter, type MlDsa65KeyPair, type SlhDsaKeyPair, } from "./adapters.js";
|
|
7
|
+
export { buildShellSignature, KEY_TYPE_TO_SIGNATURE_TYPE, publicKeyFromHex, ShellSigner, SIGNATURE_TYPE_IDS, signatureTypeFromKeyType, type SignerAdapter, } from "./signer.js";
|
|
8
|
+
export type { AddressLike, HexString, ShellAccessListItem, ShellCipherParams, ShellEncryptedKey, ShellSendTransactionParams, ShellKdfParams, ShellSignature, ShellTransactionRequest, ShellTxByAddressPage, SignedShellTransaction, SignatureTypeName, } from "./types.js";
|