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.
@@ -0,0 +1,161 @@
1
+ import { type BuildSignedTransactionOptions } from "./transactions.js";
2
+ import type { SignedShellTransaction, SignatureTypeName } from "./types.js";
3
+ /**
4
+ * Maps each {@link SignatureTypeName} to its numeric algorithm ID used in
5
+ * address derivation and on-chain records.
6
+ *
7
+ * - `Dilithium3` → `0`
8
+ * - `MlDsa65` → `1`
9
+ * - `SphincsSha2256f` → `2`
10
+ */
11
+ export declare const SIGNATURE_TYPE_IDS: Record<SignatureTypeName, number>;
12
+ /**
13
+ * Maps the `key_type` strings found in Shell keystore files to their
14
+ * corresponding {@link SignatureTypeName}.
15
+ *
16
+ * Keys are lowercase; matching is done after calling `.toLowerCase()`.
17
+ */
18
+ export declare const KEY_TYPE_TO_SIGNATURE_TYPE: Record<string, SignatureTypeName>;
19
+ /**
20
+ * Minimal interface that any post-quantum signing implementation must satisfy
21
+ * to be used with {@link ShellSigner}.
22
+ *
23
+ * @example
24
+ * ```typescript
25
+ * class MyAdapter implements SignerAdapter {
26
+ * getPublicKey(): Uint8Array { … }
27
+ * async sign(message: Uint8Array): Promise<Uint8Array> { … }
28
+ * }
29
+ * ```
30
+ */
31
+ export interface SignerAdapter {
32
+ /**
33
+ * Sign a raw message (the transaction hash bytes) and return the signature.
34
+ *
35
+ * @param message - The bytes to sign (typically an RLP-encoded tx hash).
36
+ * @returns The raw signature bytes.
37
+ */
38
+ sign(message: Uint8Array): Promise<Uint8Array>;
39
+ /** Return the raw public key bytes for this signer. */
40
+ getPublicKey(): Uint8Array;
41
+ }
42
+ /**
43
+ * High-level Shell Chain signer.
44
+ *
45
+ * Wraps a {@link SignerAdapter} and provides address derivation, signing, and
46
+ * transaction assembly for Shell Chain.
47
+ *
48
+ * @example
49
+ * ```typescript
50
+ * import { MlDsa65Adapter } from "shell-sdk/adapters";
51
+ * import { ShellSigner } from "shell-sdk/signer";
52
+ *
53
+ * const adapter = MlDsa65Adapter.generate();
54
+ * const signer = new ShellSigner("MlDsa65", adapter);
55
+ *
56
+ * console.log(signer.getAddress()); // pq1…
57
+ * console.log(signer.getHexAddress()); // 0x…
58
+ * ```
59
+ */
60
+ export declare class ShellSigner {
61
+ /** The signature algorithm this signer uses. */
62
+ readonly signatureType: SignatureTypeName;
63
+ /** The underlying adapter that performs the actual cryptographic operations. */
64
+ readonly adapter: SignerAdapter;
65
+ /**
66
+ * @param signatureType - The PQ algorithm name.
67
+ * @param adapter - An adapter providing `sign` and `getPublicKey`.
68
+ */
69
+ constructor(signatureType: SignatureTypeName, adapter: SignerAdapter);
70
+ /**
71
+ * Numeric algorithm ID for this signer's signature type.
72
+ *
73
+ * Used in address derivation and in `rotateKey` calldata.
74
+ */
75
+ get algorithmId(): number;
76
+ /** Return the raw public key bytes from the underlying adapter. */
77
+ getPublicKey(): Uint8Array;
78
+ /**
79
+ * Derive and return the `pq1…` bech32m address for this signer.
80
+ *
81
+ * The address is computed deterministically from the public key and algorithm ID.
82
+ */
83
+ getAddress(): string;
84
+ /**
85
+ * Return the `0x…` hex representation of this signer's address.
86
+ *
87
+ * Equivalent to `normalizeHexAddress(signer.getAddress())`.
88
+ */
89
+ getHexAddress(): `0x${string}`;
90
+ /**
91
+ * Sign a raw byte message with the underlying adapter.
92
+ *
93
+ * @param message - Bytes to sign (e.g. RLP-encoded transaction hash).
94
+ * @returns Raw signature bytes.
95
+ */
96
+ sign(message: Uint8Array): Promise<Uint8Array>;
97
+ /**
98
+ * Sign a transaction hash and assemble a complete {@link SignedShellTransaction}.
99
+ *
100
+ * @param options.tx - The unsigned `ShellTransactionRequest` to embed.
101
+ * @param options.txHash - The bytes to sign (RLP-encoded EIP-1559 signing hash).
102
+ * @param options.includePublicKey - When `true`, embeds `sender_pubkey` in the
103
+ * result. Required for accounts that have not yet appeared on-chain.
104
+ * @returns A fully-signed transaction ready for {@link ShellProvider.sendTransaction}.
105
+ *
106
+ * @example
107
+ * ```typescript
108
+ * const signed = await signer.buildSignedTransaction({
109
+ * tx,
110
+ * txHash: rlpHashBytes,
111
+ * includePublicKey: true,
112
+ * });
113
+ * const hash = await provider.sendTransaction(signed);
114
+ * ```
115
+ */
116
+ buildSignedTransaction(options: Omit<BuildSignedTransactionOptions, "from" | "signature" | "signatureType"> & {
117
+ txHash: Uint8Array;
118
+ includePublicKey?: boolean;
119
+ }): Promise<SignedShellTransaction>;
120
+ }
121
+ /**
122
+ * Convert a keystore `key_type` string to a {@link SignatureTypeName}.
123
+ *
124
+ * Matching is case-insensitive and ignores leading/trailing whitespace.
125
+ *
126
+ * @param keyType - The `key_type` field from a Shell keystore file (e.g. `"mldsa65"`).
127
+ * @returns The corresponding `SignatureTypeName`.
128
+ * @throws {Error} If the key type is not recognised.
129
+ *
130
+ * @example
131
+ * ```typescript
132
+ * signatureTypeFromKeyType("mldsa65"); // "MlDsa65"
133
+ * signatureTypeFromKeyType("sphincs-sha2-256f"); // "SphincsSha2256f"
134
+ * ```
135
+ */
136
+ export declare function signatureTypeFromKeyType(keyType: string): SignatureTypeName;
137
+ /**
138
+ * Convert a hex-encoded public key string to a `Uint8Array`.
139
+ *
140
+ * Accepts both `0x`-prefixed and bare hex strings.
141
+ *
142
+ * @param publicKeyHex - Hex-encoded public key (with or without `0x` prefix).
143
+ * @returns The decoded public key bytes.
144
+ *
145
+ * @example
146
+ * ```typescript
147
+ * const pk = publicKeyFromHex("0xabcdef…");
148
+ * const pk = publicKeyFromHex("abcdef…");
149
+ * ```
150
+ */
151
+ export declare function publicKeyFromHex(publicKeyHex: string): Uint8Array;
152
+ /**
153
+ * Build a {@link ShellSignature} object from raw signature bytes.
154
+ *
155
+ * A thin wrapper around {@link buildSignature} from `transactions.ts`.
156
+ *
157
+ * @param signatureType - The algorithm that produced the signature.
158
+ * @param signature - Raw signature bytes.
159
+ * @returns A `ShellSignature` with `sig_type` and `data` fields.
160
+ */
161
+ export declare function buildShellSignature(signatureType: SignatureTypeName, signature: Uint8Array): import("./types.js").ShellSignature;
package/dist/signer.js ADDED
@@ -0,0 +1,188 @@
1
+ /**
2
+ * ShellSigner and SignerAdapter — the signing layer for Shell Chain transactions.
3
+ *
4
+ * `SignerAdapter` is a minimal interface for plugging in any PQ signing
5
+ * implementation. Concrete adapters live in `adapters.ts`.
6
+ *
7
+ * `ShellSigner` wraps an adapter and adds address derivation, transaction
8
+ * building, and Shell-specific helpers.
9
+ *
10
+ * @module signer
11
+ */
12
+ import { hexToBytes } from "viem";
13
+ import { derivePqAddressFromPublicKey, normalizeHexAddress, normalizePqAddress } from "./address.js";
14
+ import { buildSignature, buildSignedTransaction, } from "./transactions.js";
15
+ /**
16
+ * Maps each {@link SignatureTypeName} to its numeric algorithm ID used in
17
+ * address derivation and on-chain records.
18
+ *
19
+ * - `Dilithium3` → `0`
20
+ * - `MlDsa65` → `1`
21
+ * - `SphincsSha2256f` → `2`
22
+ */
23
+ export const SIGNATURE_TYPE_IDS = {
24
+ Dilithium3: 0,
25
+ MlDsa65: 1,
26
+ SphincsSha2256f: 2,
27
+ };
28
+ /**
29
+ * Maps the `key_type` strings found in Shell keystore files to their
30
+ * corresponding {@link SignatureTypeName}.
31
+ *
32
+ * Keys are lowercase; matching is done after calling `.toLowerCase()`.
33
+ */
34
+ export const KEY_TYPE_TO_SIGNATURE_TYPE = {
35
+ dilithium3: "Dilithium3",
36
+ "sphincs-sha2-256f": "SphincsSha2256f",
37
+ mldsa65: "MlDsa65",
38
+ };
39
+ /**
40
+ * High-level Shell Chain signer.
41
+ *
42
+ * Wraps a {@link SignerAdapter} and provides address derivation, signing, and
43
+ * transaction assembly for Shell Chain.
44
+ *
45
+ * @example
46
+ * ```typescript
47
+ * import { MlDsa65Adapter } from "shell-sdk/adapters";
48
+ * import { ShellSigner } from "shell-sdk/signer";
49
+ *
50
+ * const adapter = MlDsa65Adapter.generate();
51
+ * const signer = new ShellSigner("MlDsa65", adapter);
52
+ *
53
+ * console.log(signer.getAddress()); // pq1…
54
+ * console.log(signer.getHexAddress()); // 0x…
55
+ * ```
56
+ */
57
+ export class ShellSigner {
58
+ /** The signature algorithm this signer uses. */
59
+ signatureType;
60
+ /** The underlying adapter that performs the actual cryptographic operations. */
61
+ adapter;
62
+ /**
63
+ * @param signatureType - The PQ algorithm name.
64
+ * @param adapter - An adapter providing `sign` and `getPublicKey`.
65
+ */
66
+ constructor(signatureType, adapter) {
67
+ this.signatureType = signatureType;
68
+ this.adapter = adapter;
69
+ }
70
+ /**
71
+ * Numeric algorithm ID for this signer's signature type.
72
+ *
73
+ * Used in address derivation and in `rotateKey` calldata.
74
+ */
75
+ get algorithmId() {
76
+ return SIGNATURE_TYPE_IDS[this.signatureType];
77
+ }
78
+ /** Return the raw public key bytes from the underlying adapter. */
79
+ getPublicKey() {
80
+ return this.adapter.getPublicKey();
81
+ }
82
+ /**
83
+ * Derive and return the `pq1…` bech32m address for this signer.
84
+ *
85
+ * The address is computed deterministically from the public key and algorithm ID.
86
+ */
87
+ getAddress() {
88
+ return derivePqAddressFromPublicKey(this.getPublicKey(), this.algorithmId);
89
+ }
90
+ /**
91
+ * Return the `0x…` hex representation of this signer's address.
92
+ *
93
+ * Equivalent to `normalizeHexAddress(signer.getAddress())`.
94
+ */
95
+ getHexAddress() {
96
+ return normalizeHexAddress(this.getAddress());
97
+ }
98
+ /**
99
+ * Sign a raw byte message with the underlying adapter.
100
+ *
101
+ * @param message - Bytes to sign (e.g. RLP-encoded transaction hash).
102
+ * @returns Raw signature bytes.
103
+ */
104
+ async sign(message) {
105
+ return this.adapter.sign(message);
106
+ }
107
+ /**
108
+ * Sign a transaction hash and assemble a complete {@link SignedShellTransaction}.
109
+ *
110
+ * @param options.tx - The unsigned `ShellTransactionRequest` to embed.
111
+ * @param options.txHash - The bytes to sign (RLP-encoded EIP-1559 signing hash).
112
+ * @param options.includePublicKey - When `true`, embeds `sender_pubkey` in the
113
+ * result. Required for accounts that have not yet appeared on-chain.
114
+ * @returns A fully-signed transaction ready for {@link ShellProvider.sendTransaction}.
115
+ *
116
+ * @example
117
+ * ```typescript
118
+ * const signed = await signer.buildSignedTransaction({
119
+ * tx,
120
+ * txHash: rlpHashBytes,
121
+ * includePublicKey: true,
122
+ * });
123
+ * const hash = await provider.sendTransaction(signed);
124
+ * ```
125
+ */
126
+ async buildSignedTransaction(options) {
127
+ const signature = await this.sign(options.txHash);
128
+ return buildSignedTransaction({
129
+ from: normalizePqAddress(this.getAddress()),
130
+ tx: options.tx,
131
+ signature,
132
+ signatureType: this.signatureType,
133
+ senderPubkey: options.includePublicKey ? this.getPublicKey() : undefined,
134
+ });
135
+ }
136
+ }
137
+ /**
138
+ * Convert a keystore `key_type` string to a {@link SignatureTypeName}.
139
+ *
140
+ * Matching is case-insensitive and ignores leading/trailing whitespace.
141
+ *
142
+ * @param keyType - The `key_type` field from a Shell keystore file (e.g. `"mldsa65"`).
143
+ * @returns The corresponding `SignatureTypeName`.
144
+ * @throws {Error} If the key type is not recognised.
145
+ *
146
+ * @example
147
+ * ```typescript
148
+ * signatureTypeFromKeyType("mldsa65"); // "MlDsa65"
149
+ * signatureTypeFromKeyType("sphincs-sha2-256f"); // "SphincsSha2256f"
150
+ * ```
151
+ */
152
+ export function signatureTypeFromKeyType(keyType) {
153
+ const normalized = keyType.trim().toLowerCase();
154
+ const value = KEY_TYPE_TO_SIGNATURE_TYPE[normalized];
155
+ if (!value) {
156
+ throw new Error(`unsupported key type: ${keyType}`);
157
+ }
158
+ return value;
159
+ }
160
+ /**
161
+ * Convert a hex-encoded public key string to a `Uint8Array`.
162
+ *
163
+ * Accepts both `0x`-prefixed and bare hex strings.
164
+ *
165
+ * @param publicKeyHex - Hex-encoded public key (with or without `0x` prefix).
166
+ * @returns The decoded public key bytes.
167
+ *
168
+ * @example
169
+ * ```typescript
170
+ * const pk = publicKeyFromHex("0xabcdef…");
171
+ * const pk = publicKeyFromHex("abcdef…");
172
+ * ```
173
+ */
174
+ export function publicKeyFromHex(publicKeyHex) {
175
+ return hexToBytes(`0x${publicKeyHex.replace(/^0x/i, "")}`);
176
+ }
177
+ /**
178
+ * Build a {@link ShellSignature} object from raw signature bytes.
179
+ *
180
+ * A thin wrapper around {@link buildSignature} from `transactions.ts`.
181
+ *
182
+ * @param signatureType - The algorithm that produced the signature.
183
+ * @param signature - Raw signature bytes.
184
+ * @returns A `ShellSignature` with `sig_type` and `data` fields.
185
+ */
186
+ export function buildShellSignature(signatureType, signature) {
187
+ return buildSignature(signatureType, signature);
188
+ }
@@ -0,0 +1,65 @@
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
+ /** `pq1…` bech32m address of the ValidatorRegistry system contract. */
7
+ export declare const validatorRegistryAddress: string;
8
+ /** `pq1…` bech32m address of the AccountManager system contract. */
9
+ export declare const accountManagerAddress: string;
10
+ /** 4-byte ABI function selector for `rotateKey(bytes,uint8)`. */
11
+ export declare const rotateKeySelector: `0x${string}`;
12
+ /** 4-byte ABI function selector for `setValidationCode(bytes32)`. */
13
+ export declare const setValidationCodeSelector: `0x${string}`;
14
+ /** 4-byte ABI function selector for `clearValidationCode()`. */
15
+ export declare const clearValidationCodeSelector: `0x${string}`;
16
+ /**
17
+ * ABI-encode calldata for `rotateKey(bytes publicKey, uint8 algorithmId)`.
18
+ *
19
+ * @param publicKey - Raw bytes of the new public key.
20
+ * @param algorithmId - Numeric algorithm ID (Dilithium3=0, MlDsa65=1, SphincsSha2256f=2).
21
+ * @returns `HexString` with the 4-byte selector prepended.
22
+ *
23
+ * @example
24
+ * ```typescript
25
+ * const data = encodeRotateKeyCalldata(newPublicKey, 1 /* MlDsa65 *\/);
26
+ * ```
27
+ */
28
+ export declare function encodeRotateKeyCalldata(publicKey: Uint8Array, algorithmId: number): HexString;
29
+ /**
30
+ * ABI-encode calldata for `setValidationCode(bytes32 codeHash)`.
31
+ *
32
+ * @param codeHash - `bytes32` hash of the custom validation contract.
33
+ * @returns `HexString` with the 4-byte selector prepended.
34
+ *
35
+ * @example
36
+ * ```typescript
37
+ * const data = encodeSetValidationCodeCalldata("0xabc123…");
38
+ * ```
39
+ */
40
+ export declare function encodeSetValidationCodeCalldata(codeHash: HexString): HexString;
41
+ /**
42
+ * Return the 4-byte selector for `clearValidationCode()`.
43
+ *
44
+ * No parameters are encoded since the function takes no arguments.
45
+ *
46
+ * @returns The 4-byte function selector as a `HexString`.
47
+ */
48
+ export declare function encodeClearValidationCodeCalldata(): HexString;
49
+ /**
50
+ * Return `true` if `address` refers to one of the Shell system contracts.
51
+ *
52
+ * Accepts both `pq1…` and `0x…` forms for the AccountManager and
53
+ * ValidatorRegistry addresses.
54
+ *
55
+ * @param address - Address to test (any format accepted by `AddressLike`).
56
+ * @returns `true` if the address matches AccountManager or ValidatorRegistry.
57
+ *
58
+ * @example
59
+ * ```typescript
60
+ * isSystemContractAddress("0x0000000000000000000000000000000000000002"); // true
61
+ * isSystemContractAddress(accountManagerAddress); // true
62
+ * isSystemContractAddress("pq1someuser…"); // false
63
+ * ```
64
+ */
65
+ export declare function isSystemContractAddress(address: AddressLike): boolean;
@@ -0,0 +1,105 @@
1
+ /**
2
+ * System contract addresses and calldata encoders for Shell Chain.
3
+ *
4
+ * Shell Chain ships two built-in system contracts at well-known addresses:
5
+ *
6
+ * - **ValidatorRegistry** (`0x…0001`) — manages the set of active validators.
7
+ * - **AccountManager** (`0x…0002`) — handles per-account key rotation and
8
+ * custom AA validation code.
9
+ *
10
+ * Transactions targeting these contracts should be built with the helpers in
11
+ * `transactions.ts` (e.g. {@link buildRotateKeyTransaction}).
12
+ *
13
+ * @module system-contracts
14
+ */
15
+ import { bytesToHex, encodeAbiParameters, keccak256, toBytes } from "viem";
16
+ import { bytesToPqAddress } from "./address.js";
17
+ const SYSTEM_ADDRESS_LENGTH = 20;
18
+ function systemAddress(lastByte) {
19
+ const bytes = new Uint8Array(SYSTEM_ADDRESS_LENGTH);
20
+ bytes[SYSTEM_ADDRESS_LENGTH - 1] = lastByte;
21
+ return bytes;
22
+ }
23
+ function selector(signature) {
24
+ return keccak256(toBytes(signature)).slice(0, 10);
25
+ }
26
+ /** Hex address of the ValidatorRegistry system contract (`0x…0001`). */
27
+ export const validatorRegistryHexAddress = "0x0000000000000000000000000000000000000001";
28
+ /** Hex address of the AccountManager system contract (`0x…0002`). */
29
+ export const accountManagerHexAddress = "0x0000000000000000000000000000000000000002";
30
+ /** `pq1…` bech32m address of the ValidatorRegistry system contract. */
31
+ export const validatorRegistryAddress = bytesToPqAddress(systemAddress(1));
32
+ /** `pq1…` bech32m address of the AccountManager system contract. */
33
+ export const accountManagerAddress = bytesToPqAddress(systemAddress(2));
34
+ /** 4-byte ABI function selector for `rotateKey(bytes,uint8)`. */
35
+ export const rotateKeySelector = selector("rotateKey(bytes,uint8)");
36
+ /** 4-byte ABI function selector for `setValidationCode(bytes32)`. */
37
+ export const setValidationCodeSelector = selector("setValidationCode(bytes32)");
38
+ /** 4-byte ABI function selector for `clearValidationCode()`. */
39
+ export const clearValidationCodeSelector = selector("clearValidationCode()");
40
+ /**
41
+ * ABI-encode calldata for `rotateKey(bytes publicKey, uint8 algorithmId)`.
42
+ *
43
+ * @param publicKey - Raw bytes of the new public key.
44
+ * @param algorithmId - Numeric algorithm ID (Dilithium3=0, MlDsa65=1, SphincsSha2256f=2).
45
+ * @returns `HexString` with the 4-byte selector prepended.
46
+ *
47
+ * @example
48
+ * ```typescript
49
+ * const data = encodeRotateKeyCalldata(newPublicKey, 1 /* MlDsa65 *\/);
50
+ * ```
51
+ */
52
+ export function encodeRotateKeyCalldata(publicKey, algorithmId) {
53
+ const encoded = encodeAbiParameters([
54
+ { type: "bytes" },
55
+ { type: "uint8" },
56
+ ], [bytesToHex(publicKey), algorithmId]);
57
+ return `${rotateKeySelector}${encoded.slice(2)}`;
58
+ }
59
+ /**
60
+ * ABI-encode calldata for `setValidationCode(bytes32 codeHash)`.
61
+ *
62
+ * @param codeHash - `bytes32` hash of the custom validation contract.
63
+ * @returns `HexString` with the 4-byte selector prepended.
64
+ *
65
+ * @example
66
+ * ```typescript
67
+ * const data = encodeSetValidationCodeCalldata("0xabc123…");
68
+ * ```
69
+ */
70
+ export function encodeSetValidationCodeCalldata(codeHash) {
71
+ const encoded = encodeAbiParameters([{ type: "bytes32" }], [codeHash]);
72
+ return `${setValidationCodeSelector}${encoded.slice(2)}`;
73
+ }
74
+ /**
75
+ * Return the 4-byte selector for `clearValidationCode()`.
76
+ *
77
+ * No parameters are encoded since the function takes no arguments.
78
+ *
79
+ * @returns The 4-byte function selector as a `HexString`.
80
+ */
81
+ export function encodeClearValidationCodeCalldata() {
82
+ return clearValidationCodeSelector;
83
+ }
84
+ /**
85
+ * Return `true` if `address` refers to one of the Shell system contracts.
86
+ *
87
+ * Accepts both `pq1…` and `0x…` forms for the AccountManager and
88
+ * ValidatorRegistry addresses.
89
+ *
90
+ * @param address - Address to test (any format accepted by `AddressLike`).
91
+ * @returns `true` if the address matches AccountManager or ValidatorRegistry.
92
+ *
93
+ * @example
94
+ * ```typescript
95
+ * isSystemContractAddress("0x0000000000000000000000000000000000000002"); // true
96
+ * isSystemContractAddress(accountManagerAddress); // true
97
+ * isSystemContractAddress("pq1someuser…"); // false
98
+ * ```
99
+ */
100
+ export function isSystemContractAddress(address) {
101
+ return (address === accountManagerAddress ||
102
+ address === validatorRegistryAddress ||
103
+ address === accountManagerHexAddress ||
104
+ address === validatorRegistryHexAddress);
105
+ }