shell-sdk 0.1.0 → 0.3.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 +26 -0
- package/README.md +77 -4
- package/dist/adapters.d.ts +13 -4
- package/dist/adapters.js +15 -6
- package/dist/index.d.ts +3 -3
- package/dist/index.js +2 -2
- package/dist/provider.d.ts +29 -1
- package/dist/provider.js +35 -0
- package/dist/transactions.d.ts +2 -2
- package/dist/transactions.js +33 -19
- package/dist/types.d.ts +55 -0
- package/examples/minimal-dapp/README.md +59 -0
- package/examples/minimal-dapp/browser-demo.html +98 -0
- package/examples/minimal-dapp/node-demo.mjs +68 -0
- package/package.json +7 -4
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## [0.3.0] — 2026-04-22
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
- **API freeze**: `ShellSigner`, `ShellProvider`, and `ShellWallet` are now stable public APIs.
|
|
7
|
+
- ML-DSA-65 cross-validation tests confirming wire compatibility with `pqcrypto-dilithium` v0.5 (pk=1952 B, sk=4032 B, sig=3309 B).
|
|
8
|
+
- `examples/minimal-dapp`: Node.js (`node-demo.mjs`) and browser (`browser-demo.html`) integration examples.
|
|
9
|
+
- JSDoc complete for all public-facing exports.
|
|
10
|
+
- `MlDsa65Adapter` round-trip sign+verify test.
|
|
11
|
+
- `getNodeInfo()`, `getWitness()`, and `getStorageProfile()` helpers for Shell-specific node capabilities.
|
|
12
|
+
|
|
13
|
+
### Changed
|
|
14
|
+
- `adapters.ts`: both `"Dilithium3"` and `"MlDsa65"` aliases now explicitly document ML-DSA-65 (FIPS 204) wire compatibility with the chain's Dilithium3 verifier.
|
|
15
|
+
- `hashTransaction()` canonical RLP field ordering aligned with `shell-chain` deserialiser.
|
|
16
|
+
- Package root export surface narrowed to stable application-facing APIs.
|
|
17
|
+
- NodeInfo example version string now reflects current node naming (`shell-node/0.17.0`) without tying the SDK package version to the chain version.
|
|
18
|
+
|
|
19
|
+
## 0.2.0-rc.1
|
|
20
|
+
|
|
21
|
+
- add Browser and Node integration tests for signer, keystore, and provider flows
|
|
22
|
+
- add Rust compatibility vectors for address derivation and transaction hashing
|
|
23
|
+
- fix `hashTransaction()` to match the Rust node's canonical RLP field ordering
|
|
24
|
+
- fix `hashTransaction()` to accept canonical `pq1...` recipient addresses
|
|
25
|
+
- narrow the package root export surface to stable application-facing APIs
|
|
26
|
+
- document extension background flow, minimal dApp usage, and release checklist
|
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# shell-sdk
|
|
2
2
|
|
|
3
|
-
**TypeScript / JavaScript SDK for Shell Chain** —
|
|
3
|
+
**TypeScript / JavaScript SDK for Shell Chain** — build quantum-safe dApps on the first EVM chain secured before Q-Day.
|
|
4
4
|
|
|
5
5
|
[](https://nodejs.org/)
|
|
6
6
|
[](https://nodejs.org/api/esm.html)
|
|
@@ -22,10 +22,13 @@
|
|
|
22
22
|
- [System contracts](#system-contracts)
|
|
23
23
|
- [Keystore](#keystore)
|
|
24
24
|
- [End-to-end examples](#end-to-end-examples)
|
|
25
|
+
- [Wallet extension background flow](#wallet-extension-background-flow)
|
|
26
|
+
- [Minimal dApp flow](#minimal-dapp-flow)
|
|
25
27
|
- [Key rotation](#key-rotation)
|
|
26
28
|
- [Error handling](#error-handling)
|
|
27
29
|
- [TypeScript types reference](#typescript-types-reference)
|
|
28
30
|
- [Compatibility](#compatibility)
|
|
31
|
+
- [Release checklist](#release-checklist)
|
|
29
32
|
- [Chain reference](#chain-reference)
|
|
30
33
|
|
|
31
34
|
---
|
|
@@ -36,7 +39,8 @@
|
|
|
36
39
|
- **PQ addresses** — bech32m-encoded `pq1…` addresses derived from PQ public keys via BLAKE3
|
|
37
40
|
- **Native account abstraction** — key rotation and custom validation code via system contracts
|
|
38
41
|
- **viem integration** — standard Ethereum JSON-RPC methods via a typed `PublicClient`
|
|
39
|
-
- **Shell-specific RPC** — `shell_getPqPubkey`, `shell_sendTransaction`, `shell_getTransactionsByAddress`
|
|
42
|
+
- **Shell-specific RPC** — `shell_getPqPubkey`, `shell_sendTransaction`, `shell_getTransactionsByAddress`, `shell_getNodeInfo`, `shell_getWitness`
|
|
43
|
+
- **Node introspection** — `getNodeInfo()` returns version, block height, peer count, and storage profile; `getWitness()` fetches raw PQ signatures for any block
|
|
40
44
|
- **Encrypted keystore** — argon2id KDF + xchacha20-poly1305 cipher; compatible with the Shell CLI
|
|
41
45
|
|
|
42
46
|
---
|
|
@@ -128,6 +132,8 @@ These are sent as ordinary transactions whose `to` field is the AccountManager a
|
|
|
128
132
|
|
|
129
133
|
## Module reference
|
|
130
134
|
|
|
135
|
+
The package root (`shell-sdk`) is the **stable surface** for typical app usage. Lower-level constants and helpers that are more likely to change remain available from subpath imports such as `shell-sdk/signer` and `shell-sdk/transactions`.
|
|
136
|
+
|
|
131
137
|
### Types
|
|
132
138
|
|
|
133
139
|
Defined in `src/types.ts`. All types are re-exported from the package root.
|
|
@@ -232,6 +238,9 @@ import { shellDevnet } from "shell-sdk/provider";
|
|
|
232
238
|
| `sendTransaction(signed)` | `shell_sendTransaction` → tx hash string |
|
|
233
239
|
| `getTransactionsByAddress(address, opts)` | `shell_getTransactionsByAddress` with optional `fromBlock/toBlock/page/limit` |
|
|
234
240
|
| `getBlockReceipts(block)` | `eth_getBlockReceipts` → array of receipts |
|
|
241
|
+
| `getNodeInfo()` | `shell_getNodeInfo` → `ShellNodeInfo` (version, block height, peer count, storage profile) |
|
|
242
|
+
| `getWitness(blockNumberOrHash)` | `shell_getWitness` → `ShellWitnessBundle` or `null` if pruned |
|
|
243
|
+
| `getStorageProfile()` | Convenience wrapper around `getNodeInfo()` → `ShellStorageProfile \| undefined` |
|
|
235
244
|
|
|
236
245
|
**Examples:**
|
|
237
246
|
|
|
@@ -434,9 +443,11 @@ const signed = buildSignedTransaction({
|
|
|
434
443
|
|
|
435
444
|
#### `hashTransaction`
|
|
436
445
|
|
|
437
|
-
RLP-encode a `ShellTransactionRequest` and return its **keccak256** hash as a `Uint8Array`. This is the value you must pass as `txHash` to `signer.buildSignedTransaction`.
|
|
446
|
+
RLP-encode a `ShellTransactionRequest` using the Rust node's canonical field order and return its **keccak256** hash as a `Uint8Array`. This is the value you must pass as `txHash` to `signer.buildSignedTransaction`.
|
|
447
|
+
|
|
448
|
+
Shell Chain signs the full unsigned transaction payload in this order:
|
|
438
449
|
|
|
439
|
-
|
|
450
|
+
`[chainId, nonce, to, value, data, gasLimit, maxFeePerGas, maxPriorityFeePerGas, accessList, txType, blobFeeFlag, maxFeePerBlobGas, blobVersionedHashes]`
|
|
440
451
|
|
|
441
452
|
```typescript
|
|
442
453
|
import { buildTransferTransaction, hashTransaction } from "shell-sdk/transactions";
|
|
@@ -620,6 +631,55 @@ console.log(hash);
|
|
|
620
631
|
|
|
621
632
|
---
|
|
622
633
|
|
|
634
|
+
### Wallet extension background flow
|
|
635
|
+
|
|
636
|
+
This is the recommended shape for a Chrome extension background worker: keep the decrypted signer only in memory, fetch the latest nonce from RPC, and use the stable root entrypoint for the common path.
|
|
637
|
+
|
|
638
|
+
```typescript
|
|
639
|
+
import { createShellProvider, buildTransferTransaction, hashTransaction } from "shell-sdk";
|
|
640
|
+
|
|
641
|
+
async function submitTransfer({ signer, to, value, rpcHttpUrl }: {
|
|
642
|
+
signer: { getHexAddress(): `0x${string}`; buildSignedTransaction(args: { tx: unknown; txHash: Uint8Array; includePublicKey?: boolean }): Promise<unknown> };
|
|
643
|
+
to: string;
|
|
644
|
+
value: bigint;
|
|
645
|
+
rpcHttpUrl: string;
|
|
646
|
+
}) {
|
|
647
|
+
const provider = createShellProvider({ rpcHttpUrl });
|
|
648
|
+
const nonce = await provider.client.getTransactionCount({ address: signer.getHexAddress() });
|
|
649
|
+
|
|
650
|
+
const tx = buildTransferTransaction({
|
|
651
|
+
chainId: 424242,
|
|
652
|
+
nonce,
|
|
653
|
+
to,
|
|
654
|
+
value,
|
|
655
|
+
});
|
|
656
|
+
const txHash = hashTransaction(tx);
|
|
657
|
+
const signed = await signer.buildSignedTransaction({ tx, txHash, includePublicKey: nonce === 0 });
|
|
658
|
+
|
|
659
|
+
return provider.sendTransaction(signed);
|
|
660
|
+
}
|
|
661
|
+
```
|
|
662
|
+
|
|
663
|
+
### Minimal dApp flow
|
|
664
|
+
|
|
665
|
+
For a lightweight web app, keep the provider in the page and delegate signing to an injected wallet or background bridge:
|
|
666
|
+
|
|
667
|
+
```typescript
|
|
668
|
+
import { createShellProvider, normalizePqAddress } from "shell-sdk";
|
|
669
|
+
|
|
670
|
+
const provider = createShellProvider({
|
|
671
|
+
rpcHttpUrl: "https://rpc.testnet.shell.network",
|
|
672
|
+
});
|
|
673
|
+
|
|
674
|
+
const account = normalizePqAddress("0x1234...abcd");
|
|
675
|
+
const history = await provider.getTransactionsByAddress(account, { page: 1, limit: 10 });
|
|
676
|
+
|
|
677
|
+
console.log("recent txs:", history.transactions);
|
|
678
|
+
console.log("total:", history.total);
|
|
679
|
+
```
|
|
680
|
+
|
|
681
|
+
---
|
|
682
|
+
|
|
623
683
|
## Key rotation
|
|
624
684
|
|
|
625
685
|
Shell Chain accounts support **key rotation** — replacing the signing key without changing the account address. This is a critical security feature for post-quantum safety.
|
|
@@ -755,6 +815,19 @@ interface ShellSignature {
|
|
|
755
815
|
|
|
756
816
|
---
|
|
757
817
|
|
|
818
|
+
## Release checklist
|
|
819
|
+
|
|
820
|
+
Before publishing a `shell-sdk` release candidate:
|
|
821
|
+
|
|
822
|
+
1. Run `npm test` and `npm run typecheck`.
|
|
823
|
+
2. Confirm the stable root surface still excludes low-level helpers such as `hexBytes` and internal signer maps.
|
|
824
|
+
3. Verify Browser + Node integration tests both cover signer, keystore, and provider RPC flows.
|
|
825
|
+
4. Review README examples against the current public exports (`shell-sdk`, `shell-sdk/signer`, `shell-sdk/transactions`).
|
|
826
|
+
5. Check `package.json` `exports`, `files`, `version`, and repository metadata.
|
|
827
|
+
6. Build once from a clean tree and smoke-import the package root plus subpaths from `dist/`.
|
|
828
|
+
|
|
829
|
+
---
|
|
830
|
+
|
|
758
831
|
## Chain reference
|
|
759
832
|
|
|
760
833
|
| Parameter | Value |
|
package/dist/adapters.d.ts
CHANGED
|
@@ -1,8 +1,15 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Concrete SignerAdapter implementations for each PQ algorithm.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
4
|
+
* `@noble/post-quantum` provides ML-DSA-65 and SLH-DSA-SHA2-256f via
|
|
5
|
+
* WebAssembly-accelerated pure-JS implementations.
|
|
6
|
+
*
|
|
7
|
+
* **Dilithium3 compatibility note**: `pqcrypto-dilithium` v0.5 (used by
|
|
8
|
+
* shell-chain) implements ML-DSA-65 (FIPS 204) under the `dilithium3` name.
|
|
9
|
+
* `@noble/post-quantum` `ml_dsa65` produces byte-identical keys and
|
|
10
|
+
* signatures (pk=1952, sk=4032, sig=3309), so `MlDsa65Adapter` is fully
|
|
11
|
+
* wire-compatible with the chain's Dilithium3 verifier. Both `"Dilithium3"`
|
|
12
|
+
* and `"MlDsa65"` algorithm names route to the same adapter.
|
|
6
13
|
*
|
|
7
14
|
* @module adapters
|
|
8
15
|
*/
|
|
@@ -45,8 +52,10 @@ export declare function generateSlhDsaKeyPair(seed?: Uint8Array): SlhDsaKeyPair;
|
|
|
45
52
|
/**
|
|
46
53
|
* {@link SignerAdapter} for ML-DSA-65 (NIST FIPS 204).
|
|
47
54
|
*
|
|
48
|
-
*
|
|
49
|
-
*
|
|
55
|
+
* This is the primary signing adapter for Shell Chain. It is also used for
|
|
56
|
+
* `"Dilithium3"` keys since `pqcrypto-dilithium` v0.5 (the Rust crate used
|
|
57
|
+
* by shell-chain) implements FIPS 204 ML-DSA-65 — producing byte-identical
|
|
58
|
+
* keys and signatures (pk=1952 bytes, sk=4032 bytes, sig=3309 bytes).
|
|
50
59
|
*
|
|
51
60
|
* @example
|
|
52
61
|
* ```typescript
|
package/dist/adapters.js
CHANGED
|
@@ -1,8 +1,15 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Concrete SignerAdapter implementations for each PQ algorithm.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
4
|
+
* `@noble/post-quantum` provides ML-DSA-65 and SLH-DSA-SHA2-256f via
|
|
5
|
+
* WebAssembly-accelerated pure-JS implementations.
|
|
6
|
+
*
|
|
7
|
+
* **Dilithium3 compatibility note**: `pqcrypto-dilithium` v0.5 (used by
|
|
8
|
+
* shell-chain) implements ML-DSA-65 (FIPS 204) under the `dilithium3` name.
|
|
9
|
+
* `@noble/post-quantum` `ml_dsa65` produces byte-identical keys and
|
|
10
|
+
* signatures (pk=1952, sk=4032, sig=3309), so `MlDsa65Adapter` is fully
|
|
11
|
+
* wire-compatible with the chain's Dilithium3 verifier. Both `"Dilithium3"`
|
|
12
|
+
* and `"MlDsa65"` algorithm names route to the same adapter.
|
|
6
13
|
*
|
|
7
14
|
* @module adapters
|
|
8
15
|
*/
|
|
@@ -41,8 +48,10 @@ export function generateSlhDsaKeyPair(seed) {
|
|
|
41
48
|
/**
|
|
42
49
|
* {@link SignerAdapter} for ML-DSA-65 (NIST FIPS 204).
|
|
43
50
|
*
|
|
44
|
-
*
|
|
45
|
-
*
|
|
51
|
+
* This is the primary signing adapter for Shell Chain. It is also used for
|
|
52
|
+
* `"Dilithium3"` keys since `pqcrypto-dilithium` v0.5 (the Rust crate used
|
|
53
|
+
* by shell-chain) implements FIPS 204 ML-DSA-65 — producing byte-identical
|
|
54
|
+
* keys and signatures (pk=1952 bytes, sk=4032 bytes, sig=3309 bytes).
|
|
46
55
|
*
|
|
47
56
|
* @example
|
|
48
57
|
* ```typescript
|
|
@@ -86,7 +95,7 @@ export class MlDsa65Adapter {
|
|
|
86
95
|
* @param message - The bytes to sign (typically an RLP-encoded tx hash).
|
|
87
96
|
*/
|
|
88
97
|
async sign(message) {
|
|
89
|
-
return ml_dsa65.sign(this._secretKey
|
|
98
|
+
return ml_dsa65.sign(message, this._secretKey);
|
|
90
99
|
}
|
|
91
100
|
}
|
|
92
101
|
/**
|
|
@@ -134,7 +143,7 @@ export class SlhDsaAdapter {
|
|
|
134
143
|
* @param message - The bytes to sign (typically an RLP-encoded tx hash).
|
|
135
144
|
*/
|
|
136
145
|
async sign(message) {
|
|
137
|
-
return slh_dsa_sha2_256f.sign(this._secretKey
|
|
146
|
+
return slh_dsa_sha2_256f.sign(message, this._secretKey);
|
|
138
147
|
}
|
|
139
148
|
}
|
|
140
149
|
/**
|
package/dist/index.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
export { bytesToHexAddress, PQ_ADDRESS_HRP, PQ_ADDRESS_LENGTH, PQ_ADDRESS_VERSION_V1, bytesToPqAddress, derivePqAddressFromPublicKey, isPqAddress, normalizeHexAddress, normalizePqAddress, pqAddressVersion, pqAddressToBytes, } from "./address.js";
|
|
2
2
|
export { createShellProvider, createShellPublicClient, createShellWsClient, ShellProvider, shellDevnet, type CreateShellPublicClientOptions, } from "./provider.js";
|
|
3
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,
|
|
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, } 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";
|
|
7
|
-
export { buildShellSignature,
|
|
8
|
-
export type { AddressLike, HexString, ShellAccessListItem, ShellCipherParams, ShellEncryptedKey, ShellSendTransactionParams, ShellKdfParams, ShellSignature, ShellTransactionRequest, ShellTxByAddressPage, SignedShellTransaction, SignatureTypeName, } from "./types.js";
|
|
7
|
+
export { buildShellSignature, publicKeyFromHex, ShellSigner, signatureTypeFromKeyType, type SignerAdapter, } from "./signer.js";
|
|
8
|
+
export type { AddressLike, HexString, ShellAccessListItem, ShellCipherParams, ShellEncryptedKey, ShellNodeInfo, ShellSendTransactionParams, ShellKdfParams, ShellSignature, ShellStorageProfile, ShellTransactionRequest, ShellTxByAddressPage, ShellTxWitness, ShellWitnessBundle, SignedShellTransaction, SignatureTypeName, } from "./types.js";
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export { bytesToHexAddress, PQ_ADDRESS_HRP, PQ_ADDRESS_LENGTH, PQ_ADDRESS_VERSION_V1, bytesToPqAddress, derivePqAddressFromPublicKey, isPqAddress, normalizeHexAddress, normalizePqAddress, pqAddressVersion, pqAddressToBytes, } from "./address.js";
|
|
2
2
|
export { createShellProvider, createShellPublicClient, createShellWsClient, ShellProvider, shellDevnet, } from "./provider.js";
|
|
3
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,
|
|
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, } 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";
|
|
7
|
-
export { buildShellSignature,
|
|
7
|
+
export { buildShellSignature, publicKeyFromHex, ShellSigner, signatureTypeFromKeyType, } from "./signer.js";
|
package/dist/provider.d.ts
CHANGED
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
* @module provider
|
|
17
17
|
*/
|
|
18
18
|
import { type Chain, type PublicClient } from "viem";
|
|
19
|
-
import type { SignedShellTransaction } from "./types.js";
|
|
19
|
+
import type { ShellNodeInfo, ShellStorageProfile, ShellWitnessBundle, SignedShellTransaction } from "./types.js";
|
|
20
20
|
/**
|
|
21
21
|
* Pre-configured viem chain definition for Shell Devnet.
|
|
22
22
|
*
|
|
@@ -155,6 +155,34 @@ export declare class ShellProvider {
|
|
|
155
155
|
* @returns Array of transaction receipt objects.
|
|
156
156
|
*/
|
|
157
157
|
getBlockReceipts(block: string): Promise<unknown[]>;
|
|
158
|
+
/**
|
|
159
|
+
* Fetch metadata about the connected Shell Chain node.
|
|
160
|
+
*
|
|
161
|
+
* Calls `shell_getNodeInfo`.
|
|
162
|
+
*
|
|
163
|
+
* @returns Node info including version, block height, peer count, and storage profile.
|
|
164
|
+
*/
|
|
165
|
+
getNodeInfo(): Promise<ShellNodeInfo>;
|
|
166
|
+
/**
|
|
167
|
+
* Fetch the PQ witness bundle for a block.
|
|
168
|
+
*
|
|
169
|
+
* Calls `shell_getWitness`. Returns `null` if the witness has been pruned
|
|
170
|
+
* (the node is running with a `full` or `light` profile and the STARK proof
|
|
171
|
+
* has already replaced the raw signatures).
|
|
172
|
+
*
|
|
173
|
+
* @param blockNumberOrHash - Hex block number (`"0x1a"`) or block hash.
|
|
174
|
+
* @returns Witness bundle, or `null` if pruned.
|
|
175
|
+
*/
|
|
176
|
+
getWitness(blockNumberOrHash: string): Promise<ShellWitnessBundle | null>;
|
|
177
|
+
/**
|
|
178
|
+
* Fetch the active storage profile of the connected node.
|
|
179
|
+
*
|
|
180
|
+
* Convenience wrapper around {@link getNodeInfo}.
|
|
181
|
+
*
|
|
182
|
+
* @returns Storage profile string (`"archive"`, `"full"`, or `"light"`), or
|
|
183
|
+
* `undefined` if the node does not report it.
|
|
184
|
+
*/
|
|
185
|
+
getStorageProfile(): Promise<ShellStorageProfile | undefined>;
|
|
158
186
|
}
|
|
159
187
|
/**
|
|
160
188
|
* Create a viem `PublicClient` connected to Shell Chain over HTTP.
|
package/dist/provider.js
CHANGED
|
@@ -137,6 +137,41 @@ export class ShellProvider {
|
|
|
137
137
|
async getBlockReceipts(block) {
|
|
138
138
|
return this.request("eth_getBlockReceipts", [block]);
|
|
139
139
|
}
|
|
140
|
+
/**
|
|
141
|
+
* Fetch metadata about the connected Shell Chain node.
|
|
142
|
+
*
|
|
143
|
+
* Calls `shell_getNodeInfo`.
|
|
144
|
+
*
|
|
145
|
+
* @returns Node info including version, block height, peer count, and storage profile.
|
|
146
|
+
*/
|
|
147
|
+
async getNodeInfo() {
|
|
148
|
+
return this.request("shell_getNodeInfo", []);
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Fetch the PQ witness bundle for a block.
|
|
152
|
+
*
|
|
153
|
+
* Calls `shell_getWitness`. Returns `null` if the witness has been pruned
|
|
154
|
+
* (the node is running with a `full` or `light` profile and the STARK proof
|
|
155
|
+
* has already replaced the raw signatures).
|
|
156
|
+
*
|
|
157
|
+
* @param blockNumberOrHash - Hex block number (`"0x1a"`) or block hash.
|
|
158
|
+
* @returns Witness bundle, or `null` if pruned.
|
|
159
|
+
*/
|
|
160
|
+
async getWitness(blockNumberOrHash) {
|
|
161
|
+
return this.request("shell_getWitness", [blockNumberOrHash]);
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Fetch the active storage profile of the connected node.
|
|
165
|
+
*
|
|
166
|
+
* Convenience wrapper around {@link getNodeInfo}.
|
|
167
|
+
*
|
|
168
|
+
* @returns Storage profile string (`"archive"`, `"full"`, or `"light"`), or
|
|
169
|
+
* `undefined` if the node does not report it.
|
|
170
|
+
*/
|
|
171
|
+
async getStorageProfile() {
|
|
172
|
+
const info = await this.getNodeInfo();
|
|
173
|
+
return info.storage_profile;
|
|
174
|
+
}
|
|
140
175
|
}
|
|
141
176
|
/**
|
|
142
177
|
* Create a viem `PublicClient` connected to Shell Chain over HTTP.
|
package/dist/transactions.d.ts
CHANGED
|
@@ -190,8 +190,8 @@ export declare function hexBytes(bytes: Uint8Array): HexString;
|
|
|
190
190
|
* `keccak256(RLP(tx))` — the same scheme as Ethereum EIP-1559 signing.
|
|
191
191
|
*
|
|
192
192
|
* **Encoding order** (EIP-2718 type-2 fields):
|
|
193
|
-
* chainId, nonce,
|
|
194
|
-
*
|
|
193
|
+
* chainId, nonce, to, value, data, gasLimit, maxFeePerGas, maxPriorityFeePerGas,
|
|
194
|
+
* accessList, txType, blobFeeFlag, maxFeePerBlobGas, blobVersionedHashes
|
|
195
195
|
*
|
|
196
196
|
* @example
|
|
197
197
|
* ```typescript
|
package/dist/transactions.js
CHANGED
|
@@ -7,8 +7,9 @@
|
|
|
7
7
|
*
|
|
8
8
|
* @module transactions
|
|
9
9
|
*/
|
|
10
|
-
import { bytesToHex, keccak256, toRlp,
|
|
10
|
+
import { bytesToHex, keccak256, toRlp, hexToBytes } from "viem";
|
|
11
11
|
import { accountManagerAddress, encodeClearValidationCodeCalldata, encodeRotateKeyCalldata, encodeSetValidationCodeCalldata, } from "./system-contracts.js";
|
|
12
|
+
import { normalizeHexAddress } from "./address.js";
|
|
12
13
|
/** Default transaction type: `2` (EIP-1559). */
|
|
13
14
|
export const DEFAULT_TX_TYPE = 2;
|
|
14
15
|
/** Default gas limit for simple SHELL token transfers (`21_000`). */
|
|
@@ -25,6 +26,22 @@ function toByteArray(bytes) {
|
|
|
25
26
|
function toHexData(data) {
|
|
26
27
|
return data ?? "0x";
|
|
27
28
|
}
|
|
29
|
+
function toRlpUint(value) {
|
|
30
|
+
const numeric = typeof value === "string" ? BigInt(value) : BigInt(value);
|
|
31
|
+
if (numeric === 0n) {
|
|
32
|
+
return "0x";
|
|
33
|
+
}
|
|
34
|
+
return `0x${numeric.toString(16)}`;
|
|
35
|
+
}
|
|
36
|
+
function toRlpAccessList(accessList) {
|
|
37
|
+
if (!accessList || accessList.length === 0) {
|
|
38
|
+
return [];
|
|
39
|
+
}
|
|
40
|
+
return accessList.map((item) => [
|
|
41
|
+
normalizeHexAddress(item.address),
|
|
42
|
+
item.storage_keys.map((key) => key),
|
|
43
|
+
]);
|
|
44
|
+
}
|
|
28
45
|
/**
|
|
29
46
|
* Low-level transaction builder that maps camelCase options to the
|
|
30
47
|
* snake_case wire format expected by the Shell node.
|
|
@@ -210,8 +227,8 @@ export function hexBytes(bytes) {
|
|
|
210
227
|
* `keccak256(RLP(tx))` — the same scheme as Ethereum EIP-1559 signing.
|
|
211
228
|
*
|
|
212
229
|
* **Encoding order** (EIP-2718 type-2 fields):
|
|
213
|
-
* chainId, nonce,
|
|
214
|
-
*
|
|
230
|
+
* chainId, nonce, to, value, data, gasLimit, maxFeePerGas, maxPriorityFeePerGas,
|
|
231
|
+
* accessList, txType, blobFeeFlag, maxFeePerBlobGas, blobVersionedHashes
|
|
215
232
|
*
|
|
216
233
|
* @example
|
|
217
234
|
* ```typescript
|
|
@@ -226,24 +243,21 @@ export function hexBytes(bytes) {
|
|
|
226
243
|
* @returns 32-byte keccak256 hash as a `Uint8Array`.
|
|
227
244
|
*/
|
|
228
245
|
export function hashTransaction(tx) {
|
|
229
|
-
const to = tx.to ? hexToBytes(tx.to.startsWith("0x") ? tx.to : `0x${tx.to}`) : new Uint8Array(0);
|
|
230
|
-
const data = hexToBytes(tx.data);
|
|
231
|
-
const value = hexToBytes(tx.value);
|
|
232
246
|
const fields = [
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
247
|
+
toRlpUint(tx.chain_id),
|
|
248
|
+
toRlpUint(tx.nonce),
|
|
249
|
+
tx.to ? normalizeHexAddress(tx.to) : "0x",
|
|
250
|
+
toRlpUint(tx.value),
|
|
251
|
+
tx.data,
|
|
252
|
+
toRlpUint(tx.gas_limit),
|
|
253
|
+
toRlpUint(tx.max_fee_per_gas),
|
|
254
|
+
toRlpUint(tx.max_priority_fee_per_gas),
|
|
255
|
+
toRlpAccessList(tx.access_list),
|
|
256
|
+
toRlpUint(tx.tx_type ?? DEFAULT_TX_TYPE),
|
|
257
|
+
toRlpUint(tx.max_fee_per_blob_gas != null ? 1 : 0),
|
|
258
|
+
toRlpUint(tx.max_fee_per_blob_gas ?? 0),
|
|
259
|
+
(tx.blob_versioned_hashes ?? []).map((hash) => hash),
|
|
242
260
|
];
|
|
243
|
-
if (tx.max_fee_per_blob_gas != null) {
|
|
244
|
-
fields.push(numberToHex(tx.max_fee_per_blob_gas));
|
|
245
|
-
fields.push("0x"); // empty blob versioned hashes
|
|
246
|
-
}
|
|
247
261
|
const rlpEncoded = toRlp(fields);
|
|
248
262
|
const hash = hexToBytes(keccak256(rlpEncoded));
|
|
249
263
|
return hash;
|
package/dist/types.d.ts
CHANGED
|
@@ -82,6 +82,61 @@ export interface SignedShellTransaction {
|
|
|
82
82
|
*/
|
|
83
83
|
sender_pubkey?: number[] | null;
|
|
84
84
|
}
|
|
85
|
+
/**
|
|
86
|
+
* Node storage profile as advertised via the `StorageCapability` P2P message.
|
|
87
|
+
*
|
|
88
|
+
* - `"archive"` — all TX bodies and PQ witnesses kept forever; STARK proofs never replace witnesses.
|
|
89
|
+
* - `"full"` — TX bodies kept forever; PQ witnesses replaced by STARK proofs when they arrive.
|
|
90
|
+
* - `"light"` — rolling ~4096-block window; older data pruned.
|
|
91
|
+
*/
|
|
92
|
+
export type ShellStorageProfile = "archive" | "full" | "light";
|
|
93
|
+
/**
|
|
94
|
+
* Response from `shell_getNodeInfo`.
|
|
95
|
+
*
|
|
96
|
+
* Contains runtime metadata about the connected Shell Chain node.
|
|
97
|
+
*/
|
|
98
|
+
export interface ShellNodeInfo {
|
|
99
|
+
/** Node software version string, e.g. `"shell-node/0.17.0"`, independent of the SDK package version. */
|
|
100
|
+
version: string;
|
|
101
|
+
/** Chain ID as a decimal string. */
|
|
102
|
+
chain_id: string;
|
|
103
|
+
/** Current head block number (decimal). */
|
|
104
|
+
block_height: number;
|
|
105
|
+
/** libp2p peer ID of this node. */
|
|
106
|
+
peer_id: string;
|
|
107
|
+
/** Number of currently connected peers. */
|
|
108
|
+
peer_count: number;
|
|
109
|
+
/** Active storage profile. */
|
|
110
|
+
storage_profile?: ShellStorageProfile;
|
|
111
|
+
/** Oldest block number for which this node has full body data. */
|
|
112
|
+
oldest_body_block?: number;
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* A single PQ transaction witness from `shell_getBlockWitnesses` / `shell_getWitness`.
|
|
116
|
+
*/
|
|
117
|
+
export interface ShellTxWitness {
|
|
118
|
+
/** Zero-based transaction index within the block. */
|
|
119
|
+
tx_index: number;
|
|
120
|
+
/** Signature algorithm name. */
|
|
121
|
+
sig_type: SignatureTypeName;
|
|
122
|
+
/** Raw signature bytes as hex string. */
|
|
123
|
+
signature: string;
|
|
124
|
+
/** Raw public key bytes as hex string (only present on first-use txs). */
|
|
125
|
+
public_key?: string;
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Response from `shell_getWitness` for a single block.
|
|
129
|
+
*/
|
|
130
|
+
export interface ShellWitnessBundle {
|
|
131
|
+
/** Block hash (0x-prefixed). */
|
|
132
|
+
block_hash: string;
|
|
133
|
+
/** Block number. */
|
|
134
|
+
block_number: number;
|
|
135
|
+
/** Number of witnesses in this bundle. */
|
|
136
|
+
witness_count: number;
|
|
137
|
+
/** Individual transaction witnesses. */
|
|
138
|
+
witnesses: ShellTxWitness[];
|
|
139
|
+
}
|
|
85
140
|
/** Paginated response from `shell_getTransactionsByAddress`. */
|
|
86
141
|
export interface ShellTxByAddressPage {
|
|
87
142
|
address: AddressLike;
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# Shell SDK — Minimal dApp Example
|
|
2
|
+
|
|
3
|
+
Demonstrates the core Shell SDK flow:
|
|
4
|
+
1. Generate a post-quantum key pair (ML-DSA-65)
|
|
5
|
+
2. Create a signer and derive address
|
|
6
|
+
3. Query balance from a Shell node
|
|
7
|
+
4. Build + sign a transaction (without broadcasting)
|
|
8
|
+
|
|
9
|
+
## Node.js (server / CLI)
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
# From the shell-sdk root:
|
|
13
|
+
npm install
|
|
14
|
+
npm run build
|
|
15
|
+
|
|
16
|
+
# Run the demo (points at localhost:8545 by default)
|
|
17
|
+
node examples/minimal-dapp/node-demo.mjs
|
|
18
|
+
|
|
19
|
+
# Point at a different RPC endpoint:
|
|
20
|
+
SHELL_RPC_URL=http://my-node:8545 node examples/minimal-dapp/node-demo.mjs
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Expected output:
|
|
24
|
+
```
|
|
25
|
+
Address (pq1…): pq1…
|
|
26
|
+
Address (0x…): 0x…
|
|
27
|
+
Balance (wei): 0 ← 0 for a fresh account; fund it via the faucet
|
|
28
|
+
Signed tx type: 2
|
|
29
|
+
Signature length (bytes): 3309
|
|
30
|
+
Sender pubkey set: true
|
|
31
|
+
|
|
32
|
+
All done! Connect to a live node and call provider.sendTransaction(signed) to broadcast.
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Browser
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
# Build the SDK first
|
|
39
|
+
cd ../../ # shell-sdk root
|
|
40
|
+
npm run build
|
|
41
|
+
|
|
42
|
+
# Serve this directory with any HTTP server
|
|
43
|
+
npx serve examples/minimal-dapp/
|
|
44
|
+
# or
|
|
45
|
+
python3 -m http.server 8080
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
Then open `http://localhost:8080/browser-demo.html` in your browser.
|
|
49
|
+
|
|
50
|
+
## Key sizes (ML-DSA-65 / FIPS 204)
|
|
51
|
+
|
|
52
|
+
| Field | Bytes |
|
|
53
|
+
|-------------|-------|
|
|
54
|
+
| Public key | 1952 |
|
|
55
|
+
| Secret key | 4032 |
|
|
56
|
+
| Signature | 3309 |
|
|
57
|
+
|
|
58
|
+
These match `pqcrypto-dilithium` v0.5 (used by shell-chain), confirming wire
|
|
59
|
+
compatibility. Both `"Dilithium3"` and `"MlDsa65"` keys use this algorithm.
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8" />
|
|
5
|
+
<title>Shell SDK — Minimal Browser dApp</title>
|
|
6
|
+
<style>
|
|
7
|
+
body { font-family: monospace; max-width: 800px; margin: 2rem auto; padding: 0 1rem; }
|
|
8
|
+
pre { background: #1a1a1a; color: #d4d4d4; padding: 1rem; border-radius: 4px; overflow-x: auto; }
|
|
9
|
+
button { padding: .5rem 1.2rem; cursor: pointer; margin: .25rem; }
|
|
10
|
+
#log { white-space: pre-wrap; }
|
|
11
|
+
</style>
|
|
12
|
+
</head>
|
|
13
|
+
<body>
|
|
14
|
+
<h1>Shell SDK — Minimal Browser dApp</h1>
|
|
15
|
+
<p>This page runs entirely in the browser using ES modules from a CDN (or local build).</p>
|
|
16
|
+
|
|
17
|
+
<button onclick="generateKey()">1. Generate Key</button>
|
|
18
|
+
<button onclick="queryBalance()">2. Query Balance</button>
|
|
19
|
+
<button onclick="buildTx()">3. Build & Sign Tx</button>
|
|
20
|
+
|
|
21
|
+
<pre id="log">Click a button to start…</pre>
|
|
22
|
+
|
|
23
|
+
<script type="module">
|
|
24
|
+
// Adjust the import path to your local build or CDN URL.
|
|
25
|
+
// e.g. import { MlDsa65Adapter, ... } from "./shell-sdk.esm.js";
|
|
26
|
+
// For local dev: `cd shell-sdk && npm run build` then serve this folder.
|
|
27
|
+
const SDK_URL = "./../../dist/index.js";
|
|
28
|
+
|
|
29
|
+
let signer = null;
|
|
30
|
+
|
|
31
|
+
function log(...args) {
|
|
32
|
+
const el = document.getElementById("log");
|
|
33
|
+
el.textContent += args.map(String).join(" ") + "\n";
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
async function loadSdk() {
|
|
37
|
+
try {
|
|
38
|
+
return await import(SDK_URL);
|
|
39
|
+
} catch (e) {
|
|
40
|
+
log("Failed to load SDK:", e.message);
|
|
41
|
+
log("Make sure you have run `npm run build` in the shell-sdk directory");
|
|
42
|
+
log("and are serving this file from an HTTP server (not file://).");
|
|
43
|
+
throw e;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
window.generateKey = async function () {
|
|
48
|
+
const { MlDsa65Adapter, ShellSigner } = await loadSdk();
|
|
49
|
+
const adapter = MlDsa65Adapter.generate();
|
|
50
|
+
signer = new ShellSigner("MlDsa65", adapter);
|
|
51
|
+
log("=== Key Generated ===");
|
|
52
|
+
log("Address (pq1…):", signer.getAddress());
|
|
53
|
+
log("Address (0x…):", signer.getHexAddress());
|
|
54
|
+
log("Public key size:", adapter.getPublicKey().length, "bytes");
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
window.queryBalance = async function () {
|
|
58
|
+
if (!signer) { log("Generate a key first!"); return; }
|
|
59
|
+
const { createShellProvider } = await loadSdk();
|
|
60
|
+
const rpc = prompt("RPC URL?", "http://127.0.0.1:8545");
|
|
61
|
+
if (!rpc) return;
|
|
62
|
+
const provider = createShellProvider({ url: rpc });
|
|
63
|
+
try {
|
|
64
|
+
const bal = await provider.getBalance({ address: signer.getHexAddress() });
|
|
65
|
+
log("Balance:", bal.toString(), "wei");
|
|
66
|
+
} catch (e) {
|
|
67
|
+
log("getBalance error:", e.message);
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
window.buildTx = async function () {
|
|
72
|
+
if (!signer) { log("Generate a key first!"); return; }
|
|
73
|
+
const { hashTransaction } = await loadSdk();
|
|
74
|
+
const tx = {
|
|
75
|
+
from: signer.getHexAddress(),
|
|
76
|
+
chain_id: 1337,
|
|
77
|
+
nonce: 0,
|
|
78
|
+
to: "0x0000000000000000000000000000000000000001",
|
|
79
|
+
value: "0x1",
|
|
80
|
+
data: "0x",
|
|
81
|
+
gas_limit: 21000,
|
|
82
|
+
max_fee_per_gas: 1_000_000_000,
|
|
83
|
+
max_priority_fee_per_gas: 1_000_000_000,
|
|
84
|
+
};
|
|
85
|
+
const txHash = hashTransaction(tx);
|
|
86
|
+
const signed = await signer.buildSignedTransaction({ tx, txHash, includePublicKey: true });
|
|
87
|
+
log("=== Signed Transaction ===");
|
|
88
|
+
log("Type:", signed.type);
|
|
89
|
+
log("From:", signed.from);
|
|
90
|
+
log("To:", signed.to);
|
|
91
|
+
log("Value:", signed.value.toString());
|
|
92
|
+
log("Sig length:", signed.signature.data.length, "bytes");
|
|
93
|
+
log("Has pubkey:", signed.sender_pubkey != null);
|
|
94
|
+
log("\nCall provider.sendTransaction(signed) to broadcast to a live node.");
|
|
95
|
+
};
|
|
96
|
+
</script>
|
|
97
|
+
</body>
|
|
98
|
+
</html>
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shell SDK minimal dApp — Node.js script
|
|
3
|
+
*
|
|
4
|
+
* Demonstrates: connect provider → query balance → sign + build transaction
|
|
5
|
+
*
|
|
6
|
+
* Usage:
|
|
7
|
+
* npm install shell-sdk
|
|
8
|
+
* node node-demo.mjs
|
|
9
|
+
*
|
|
10
|
+
* By default this points at the Shell devnet RPC. Set SHELL_RPC_URL to
|
|
11
|
+
* override.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import { MlDsa65Adapter, ShellSigner, createShellProvider } from "shell-sdk";
|
|
15
|
+
|
|
16
|
+
const RPC_URL = process.env.SHELL_RPC_URL ?? "http://127.0.0.1:8545";
|
|
17
|
+
|
|
18
|
+
async function main() {
|
|
19
|
+
// ── 1. Create a provider ──────────────────────────────────────────────
|
|
20
|
+
const provider = createShellProvider({ url: RPC_URL });
|
|
21
|
+
|
|
22
|
+
// ── 2. Generate a key pair and build a signer ─────────────────────────
|
|
23
|
+
const adapter = MlDsa65Adapter.generate();
|
|
24
|
+
const signer = new ShellSigner("MlDsa65", adapter);
|
|
25
|
+
|
|
26
|
+
console.log("Address (pq1…):", signer.getAddress());
|
|
27
|
+
console.log("Address (0x…):", signer.getHexAddress());
|
|
28
|
+
|
|
29
|
+
// ── 3. Query balance ───────────────────────────────────────────────────
|
|
30
|
+
try {
|
|
31
|
+
const balance = await provider.client.getBalance({ address: signer.getHexAddress() });
|
|
32
|
+
console.log("Balance (wei):", balance.toString());
|
|
33
|
+
} catch (err) {
|
|
34
|
+
console.warn("getBalance failed (node may not be running):", err.message);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// ── 4. Build and sign a transaction ───────────────────────────────────
|
|
38
|
+
// NOTE: sending requires a funded account + live node.
|
|
39
|
+
// This section shows the sign-only flow without broadcasting.
|
|
40
|
+
const TO = "0x0000000000000000000000000000000000000001";
|
|
41
|
+
|
|
42
|
+
/** @type {import("shell-sdk").ShellTransactionRequest} */
|
|
43
|
+
const tx = {
|
|
44
|
+
from: signer.getHexAddress(),
|
|
45
|
+
chain_id: 1337,
|
|
46
|
+
nonce: 0,
|
|
47
|
+
to: TO,
|
|
48
|
+
value: "0x1",
|
|
49
|
+
data: "0x",
|
|
50
|
+
gas_limit: 21000,
|
|
51
|
+
max_fee_per_gas: 1_000_000_000,
|
|
52
|
+
max_priority_fee_per_gas: 1_000_000_000,
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
const { hashTransaction } = await import("shell-sdk");
|
|
56
|
+
const txHash = hashTransaction(tx);
|
|
57
|
+
const signed = await signer.buildSignedTransaction({ tx, txHash, includePublicKey: true });
|
|
58
|
+
console.log("Signed tx type:", signed.tx.tx_type ?? 2, "(EIP-1559)");
|
|
59
|
+
console.log("Signature length (bytes):", signed.signature.data.length);
|
|
60
|
+
console.log("Sender pubkey set:", signed.sender_pubkey != null);
|
|
61
|
+
|
|
62
|
+
console.log("\nAll done! Connect to a live node and call provider.sendTransaction(signed) to broadcast.");
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
main().catch((err) => {
|
|
66
|
+
console.error(err);
|
|
67
|
+
process.exit(1);
|
|
68
|
+
});
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "shell-sdk",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "TypeScript SDK for Shell Chain",
|
|
3
|
+
"version": "0.3.0",
|
|
4
|
+
"description": "TypeScript SDK for Shell Chain — build quantum-safe dApps before Q-Day.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"main": "./dist/index.js",
|
|
@@ -45,12 +45,15 @@
|
|
|
45
45
|
}
|
|
46
46
|
},
|
|
47
47
|
"files": [
|
|
48
|
-
"dist"
|
|
48
|
+
"dist",
|
|
49
|
+
"examples",
|
|
50
|
+
"CHANGELOG.md"
|
|
49
51
|
],
|
|
50
52
|
"scripts": {
|
|
51
53
|
"build": "tsc -p tsconfig.json",
|
|
52
54
|
"clean": "rm -rf dist",
|
|
53
|
-
"typecheck": "tsc -p tsconfig.json --noEmit"
|
|
55
|
+
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
56
|
+
"test": "npm run build && node --test tests/*.test.mjs"
|
|
54
57
|
},
|
|
55
58
|
"keywords": [
|
|
56
59
|
"shell-chain",
|