shell-sdk 0.4.0 → 0.5.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 CHANGED
@@ -1,5 +1,36 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.5.0] — 2026-04-26
4
+
5
+ ### Added — AA Phase 2 (matches `shell-chain v0.19.0`)
6
+
7
+ #### Types (`types.ts`)
8
+ - **`SessionAuth`** interface: session PQ key authorization struct with `session_pubkey`, `session_algo`, `target?`, `value_cap`, `expiry_block`, `root_signature`, `session_signature`.
9
+ - **`GuardianConfig`** interface: on-chain guardian set descriptor.
10
+ - **`RecoveryProposal`** interface: active recovery proposal with votes and maturity block.
11
+ - **`AaBundle`** extended: added `paymaster_context?: number[] | null` (contract paymaster opaque bytes) and `session_auth?: SessionAuth | null` (session key authorization).
12
+ - Constants: `AA_MAX_PAYMASTER_CONTEXT = 256`, `AA_SESSION_KEY_GAS_SURCHARGE = 20_000`.
13
+
14
+ #### Transaction builders (`transactions.ts`)
15
+ - **`buildContractPaymasterTransaction`**: builds an AA batch tx with a contract paymaster (sets `paymaster_context`). Mutually exclusive with `paymaster_signature`.
16
+ - **`buildSessionKeyTransaction`**: builds an AA batch tx authorized by a session key (sets `session_auth`).
17
+ - **`hashBatchTransaction`**: updated signing hash to include `paymaster_context` as the 3rd RLP field in `bundleSigningFields`, matching `shell-chain` `AaBundle::encode_for_signing`.
18
+
19
+ #### System contract calldata (`system-contracts.ts`)
20
+ - **`setGuardiansSelector`** + **`encodeSetGuardiansCalldata`**: `setGuardians(address[],uint8,uint64)`.
21
+ - **`submitRecoverySelector`** + **`encodeSubmitRecoveryCalldata`**: `submitRecovery(address,bytes,uint8)`.
22
+ - **`executeRecoverySelector`** + **`encodeExecuteRecoveryCalldata`**: `executeRecovery(address)`.
23
+ - **`cancelRecoverySelector`** + **`encodeCancelRecoveryCalldata`**: `cancelRecovery(address)`.
24
+
25
+ ### Compatibility
26
+ - Fully backwards-compatible with SDK `0.4.x` usage. All new fields are optional.
27
+ - New AA Phase 2 features require `shell-chain v0.19.0+`.
28
+
29
+ ## [0.4.1] — 2026-04-25
30
+
31
+ ### Changed
32
+ - `estimateBatch` JSDoc updated to reflect snake_case field names (`total_gas`, `inner_gas`) aligning with `shell-chain v0.18.1` wire format.
33
+
3
34
  ## [0.4.0] — 2026-05-16
4
35
 
5
36
  ### Added
package/dist/index.d.ts CHANGED
@@ -1,8 +1,9 @@
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
- export { accountManagerAddress, accountManagerHexAddress, clearValidationCodeSelector, encodeClearValidationCodeCalldata, encodeRotateKeyCalldata, encodeSetValidationCodeCalldata, isSystemContractAddress, rotateKeySelector, setValidationCodeSelector, validatorRegistryAddress, validatorRegistryHexAddress, } from "./system-contracts.js";
4
- export { AA_BUNDLE_TX_TYPE, AA_MAX_INNER_CALLS, buildBatchTransaction, buildClearValidationCodeTransaction, buildInnerCall, buildInnerTransfer, buildRotateKeyTransaction, buildSetValidationCodeTransaction, buildSignature, buildSignedTransaction, buildSponsoredTransaction, buildSystemTransaction, buildTransaction, buildTransferTransaction, DEFAULT_AA_GAS_LIMIT, DEFAULT_MAX_FEE_PER_GAS, DEFAULT_MAX_PRIORITY_FEE_PER_GAS, DEFAULT_SYSTEM_GAS_LIMIT, DEFAULT_TRANSFER_GAS_LIMIT, DEFAULT_TX_TYPE, hashBatchTransaction, hashTransaction, type BuildBatchTransactionOptions, type BuildSponsoredTransactionOptions, } from "./transactions.js";
3
+ export { accountManagerAddress, accountManagerHexAddress, cancelRecoverySelector, clearValidationCodeSelector, encodeCancelRecoveryCalldata, encodeClearValidationCodeCalldata, encodeExecuteRecoveryCalldata, encodeRotateKeyCalldata, encodeSetGuardiansCalldata, encodeSetValidationCodeCalldata, encodeSubmitRecoveryCalldata, executeRecoverySelector, isSystemContractAddress, rotateKeySelector, setGuardiansSelector, setValidationCodeSelector, submitRecoverySelector, validatorRegistryAddress, validatorRegistryHexAddress, } from "./system-contracts.js";
4
+ export { AA_BUNDLE_TX_TYPE, AA_MAX_INNER_CALLS, buildBatchTransaction, buildClearValidationCodeTransaction, buildContractPaymasterTransaction, buildInnerCall, buildInnerTransfer, buildRotateKeyTransaction, buildSessionKeyTransaction, buildSetValidationCodeTransaction, buildSignature, buildSignedTransaction, buildSponsoredTransaction, buildSystemTransaction, buildTransaction, buildTransferTransaction, DEFAULT_AA_GAS_LIMIT, DEFAULT_MAX_FEE_PER_GAS, DEFAULT_MAX_PRIORITY_FEE_PER_GAS, DEFAULT_SYSTEM_GAS_LIMIT, DEFAULT_TRANSFER_GAS_LIMIT, DEFAULT_TX_TYPE, hashBatchTransaction, hashTransaction, type BuildBatchTransactionOptions, type BuildContractPaymasterTransactionOptions, type BuildSessionKeyTransactionOptions, type BuildSponsoredTransactionOptions, } from "./transactions.js";
5
5
  export { assertSignerMatchesKeystore, decryptKeystore, exportEncryptedKeyJson, parseEncryptedKey, validateEncryptedKeyAddress, type ParsedShellKeystore, } from "./keystore.js";
6
6
  export { adapterFromKeyPair, generateAdapter, generateMlDsa65KeyPair, generateSlhDsaKeyPair, MlDsa65Adapter, SlhDsaAdapter, type MlDsa65KeyPair, type SlhDsaKeyPair, } from "./adapters.js";
7
7
  export { buildShellSignature, publicKeyFromHex, ShellSigner, signatureTypeFromKeyType, type SignerAdapter, } from "./signer.js";
8
- export type { AaBundle, AaInnerCall, AddressLike, HexString, ShellAccessListItem, ShellBatchInnerCallRequest, ShellBatchInnerGas, ShellCipherParams, ShellEncryptedKey, ShellEstimateBatchRequest, ShellEstimateBatchResult, ShellIsSponsoredResult, ShellKdfParams, ShellNodeInfo, ShellPaymasterPolicy, ShellSendTransactionParams, ShellSignature, ShellStorageProfile, ShellTransactionRequest, ShellTxByAddressPage, ShellTxWitness, ShellWitnessBundle, ShellWitnessRootResult, SignedShellTransaction, SignatureTypeName, } from "./types.js";
8
+ export { AA_MAX_PAYMASTER_CONTEXT, AA_SESSION_KEY_GAS_SURCHARGE } from "./types.js";
9
+ export type { AaBundle, AaInnerCall, AddressLike, GuardianConfig, HexString, RecoveryProposal, SessionAuth, ShellAccessListItem, ShellBatchInnerCallRequest, ShellBatchInnerGas, ShellCipherParams, ShellEncryptedKey, ShellEstimateBatchRequest, ShellEstimateBatchResult, ShellIsSponsoredResult, ShellKdfParams, ShellNodeInfo, ShellPaymasterPolicy, ShellSendTransactionParams, ShellSignature, ShellStorageProfile, ShellTransactionRequest, ShellTxByAddressPage, ShellTxWitness, ShellWitnessBundle, ShellWitnessRootResult, SignedShellTransaction, SignatureTypeName, } from "./types.js";
package/dist/index.js CHANGED
@@ -1,7 +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, } from "./provider.js";
3
- export { accountManagerAddress, accountManagerHexAddress, clearValidationCodeSelector, encodeClearValidationCodeCalldata, encodeRotateKeyCalldata, encodeSetValidationCodeCalldata, isSystemContractAddress, rotateKeySelector, setValidationCodeSelector, validatorRegistryAddress, validatorRegistryHexAddress, } from "./system-contracts.js";
4
- export { AA_BUNDLE_TX_TYPE, AA_MAX_INNER_CALLS, buildBatchTransaction, buildClearValidationCodeTransaction, buildInnerCall, buildInnerTransfer, buildRotateKeyTransaction, buildSetValidationCodeTransaction, buildSignature, buildSignedTransaction, buildSponsoredTransaction, buildSystemTransaction, buildTransaction, buildTransferTransaction, DEFAULT_AA_GAS_LIMIT, DEFAULT_MAX_FEE_PER_GAS, DEFAULT_MAX_PRIORITY_FEE_PER_GAS, DEFAULT_SYSTEM_GAS_LIMIT, DEFAULT_TRANSFER_GAS_LIMIT, DEFAULT_TX_TYPE, hashBatchTransaction, hashTransaction, } from "./transactions.js";
3
+ export { accountManagerAddress, accountManagerHexAddress, cancelRecoverySelector, clearValidationCodeSelector, encodeCancelRecoveryCalldata, encodeClearValidationCodeCalldata, encodeExecuteRecoveryCalldata, encodeRotateKeyCalldata, encodeSetGuardiansCalldata, encodeSetValidationCodeCalldata, encodeSubmitRecoveryCalldata, executeRecoverySelector, isSystemContractAddress, rotateKeySelector, setGuardiansSelector, setValidationCodeSelector, submitRecoverySelector, validatorRegistryAddress, validatorRegistryHexAddress, } from "./system-contracts.js";
4
+ export { AA_BUNDLE_TX_TYPE, AA_MAX_INNER_CALLS, buildBatchTransaction, buildClearValidationCodeTransaction, buildContractPaymasterTransaction, buildInnerCall, buildInnerTransfer, buildRotateKeyTransaction, buildSessionKeyTransaction, buildSetValidationCodeTransaction, buildSignature, buildSignedTransaction, buildSponsoredTransaction, buildSystemTransaction, buildTransaction, buildTransferTransaction, DEFAULT_AA_GAS_LIMIT, DEFAULT_MAX_FEE_PER_GAS, DEFAULT_MAX_PRIORITY_FEE_PER_GAS, DEFAULT_SYSTEM_GAS_LIMIT, DEFAULT_TRANSFER_GAS_LIMIT, DEFAULT_TX_TYPE, hashBatchTransaction, hashTransaction, } from "./transactions.js";
5
5
  export { assertSignerMatchesKeystore, decryptKeystore, exportEncryptedKeyJson, parseEncryptedKey, validateEncryptedKeyAddress, } from "./keystore.js";
6
6
  export { adapterFromKeyPair, generateAdapter, generateMlDsa65KeyPair, generateSlhDsaKeyPair, MlDsa65Adapter, SlhDsaAdapter, } from "./adapters.js";
7
7
  export { buildShellSignature, publicKeyFromHex, ShellSigner, signatureTypeFromKeyType, } from "./signer.js";
8
+ export { AA_MAX_PAYMASTER_CONTEXT, AA_SESSION_KEY_GAS_SURCHARGE } from "./types.js";
@@ -189,14 +189,14 @@ export declare class ShellProvider {
189
189
  * Calls `shell_estimateBatch`.
190
190
  *
191
191
  * @param request - Batch estimate request with inner calls and optional paymaster.
192
- * @returns Gas estimates including `totalGas`, `perInner`, and breakdown fields.
192
+ * @returns Gas estimates including `total_gas`, `per_inner`, and breakdown fields.
193
193
  *
194
194
  * @example
195
195
  * ```typescript
196
196
  * const estimate = await provider.estimateBatch({
197
197
  * inner_calls: [{ to: "pq1…", value: "0x0", gas_limit: "0x5208" }],
198
198
  * });
199
- * const totalGas = parseInt(estimate.totalGas, 16);
199
+ * const totalGas = parseInt(estimate.total_gas, 16);
200
200
  * ```
201
201
  */
202
202
  estimateBatch(request: ShellEstimateBatchRequest): Promise<ShellEstimateBatchResult>;
package/dist/provider.js CHANGED
@@ -179,14 +179,14 @@ export class ShellProvider {
179
179
  * Calls `shell_estimateBatch`.
180
180
  *
181
181
  * @param request - Batch estimate request with inner calls and optional paymaster.
182
- * @returns Gas estimates including `totalGas`, `perInner`, and breakdown fields.
182
+ * @returns Gas estimates including `total_gas`, `per_inner`, and breakdown fields.
183
183
  *
184
184
  * @example
185
185
  * ```typescript
186
186
  * const estimate = await provider.estimateBatch({
187
187
  * inner_calls: [{ to: "pq1…", value: "0x0", gas_limit: "0x5208" }],
188
188
  * });
189
- * const totalGas = parseInt(estimate.totalGas, 16);
189
+ * const totalGas = parseInt(estimate.total_gas, 16);
190
190
  * ```
191
191
  */
192
192
  async estimateBatch(request) {
@@ -13,6 +13,14 @@ export declare const rotateKeySelector: `0x${string}`;
13
13
  export declare const setValidationCodeSelector: `0x${string}`;
14
14
  /** 4-byte ABI function selector for `clearValidationCode()`. */
15
15
  export declare const clearValidationCodeSelector: `0x${string}`;
16
+ /** 4-byte ABI function selector for `setGuardians(address[],uint8,uint64)`. */
17
+ export declare const setGuardiansSelector: `0x${string}`;
18
+ /** 4-byte ABI function selector for `submitRecovery(address,bytes,uint8)`. */
19
+ export declare const submitRecoverySelector: `0x${string}`;
20
+ /** 4-byte ABI function selector for `executeRecovery(address)`. */
21
+ export declare const executeRecoverySelector: `0x${string}`;
22
+ /** 4-byte ABI function selector for `cancelRecovery(address)`. */
23
+ export declare const cancelRecoverySelector: `0x${string}`;
16
24
  /**
17
25
  * ABI-encode calldata for `rotateKey(bytes publicKey, uint8 algorithmId)`.
18
26
  *
@@ -63,3 +71,67 @@ export declare function encodeClearValidationCodeCalldata(): HexString;
63
71
  * ```
64
72
  */
65
73
  export declare function isSystemContractAddress(address: AddressLike): boolean;
74
+ /**
75
+ * ABI-encode calldata for `setGuardians(address[] guardians, uint8 threshold, uint64 timelock)`.
76
+ *
77
+ * Configures the guardian recovery set for the caller's account. Only the
78
+ * account owner can call this (the call must be made as an inner call from
79
+ * the owner's AA bundle, or as a direct transaction).
80
+ *
81
+ * @param guardians - Array of guardian addresses (1..5). May be hex or `pq1…` form.
82
+ * @param threshold - k-of-n required votes (1 ≤ threshold ≤ guardians.length).
83
+ * @param timelock - Minimum blocks between threshold-reach and execution (≥ 100).
84
+ * @returns `HexString` with the 4-byte selector prepended.
85
+ *
86
+ * @example
87
+ * ```typescript
88
+ * const data = encodeSetGuardiansCalldata(
89
+ * ["0xGuardian1…", "0xGuardian2…", "0xGuardian3…"],
90
+ * 2, // 2-of-3 threshold
91
+ * 100, // 100-block timelock
92
+ * );
93
+ * ```
94
+ */
95
+ export declare function encodeSetGuardiansCalldata(guardians: AddressLike[], threshold: number, timelock: number): HexString;
96
+ /**
97
+ * ABI-encode calldata for `submitRecovery(address account, bytes newPubkey, uint8 newAlgo)`.
98
+ *
99
+ * Called by a guardian to vote for a new PQ public key on behalf of `account`.
100
+ * When k-of-n threshold is reached, the proposal becomes executable after
101
+ * `timelock` blocks.
102
+ *
103
+ * @param account - Account being recovered (0x hex or `pq1…` form).
104
+ * @param newPubkey - Raw bytes of the new PQ public key.
105
+ * @param newAlgo - Algorithm ID for the new key (ML-DSA-65 = 0, etc.).
106
+ * @returns `HexString` with the 4-byte selector prepended.
107
+ *
108
+ * @example
109
+ * ```typescript
110
+ * const data = encodeSubmitRecoveryCalldata(
111
+ * "0xAccountAddress…",
112
+ * newPubkeyBytes,
113
+ * 0, // ML-DSA-65
114
+ * );
115
+ * ```
116
+ */
117
+ export declare function encodeSubmitRecoveryCalldata(account: AddressLike, newPubkey: Uint8Array, newAlgo: number): HexString;
118
+ /**
119
+ * ABI-encode calldata for `executeRecovery(address account)`.
120
+ *
121
+ * Can be called by anyone once the recovery proposal has reached maturity
122
+ * (`current_block >= maturity_block && maturity_block != 0`).
123
+ *
124
+ * @param account - Account whose recovery proposal to execute.
125
+ * @returns `HexString` with the 4-byte selector prepended.
126
+ */
127
+ export declare function encodeExecuteRecoveryCalldata(account: AddressLike): HexString;
128
+ /**
129
+ * ABI-encode calldata for `cancelRecovery(address account)`.
130
+ *
131
+ * Owner-only: removes the active recovery proposal. Useful if the account
132
+ * owner regains access before the timelock expires.
133
+ *
134
+ * @param account - Account whose recovery proposal to cancel (must be caller).
135
+ * @returns `HexString` with the 4-byte selector prepended.
136
+ */
137
+ export declare function encodeCancelRecoveryCalldata(account: AddressLike): HexString;
@@ -4,12 +4,17 @@
4
4
  * Shell Chain ships two built-in system contracts at well-known addresses:
5
5
  *
6
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.
7
+ * - **AccountManager** (`0x…0002`) — handles per-account key rotation,
8
+ * custom AA validation code, and (v0.19.0+) guardian recovery.
9
9
  *
10
10
  * Transactions targeting these contracts should be built with the helpers in
11
11
  * `transactions.ts` (e.g. {@link buildRotateKeyTransaction}).
12
12
  *
13
+ * ## AA Phase 2 — Guardian Recovery (v0.19.0-dev)
14
+ *
15
+ * Use {@link encodeSetGuardiansCalldata}, {@link encodeSubmitRecoveryCalldata},
16
+ * {@link encodeExecuteRecoveryCalldata}, and {@link encodeCancelRecoveryCalldata}.
17
+ *
13
18
  * @module system-contracts
14
19
  */
15
20
  import { bytesToHex, encodeAbiParameters, keccak256, toBytes } from "viem";
@@ -37,6 +42,17 @@ export const rotateKeySelector = selector("rotateKey(bytes,uint8)");
37
42
  export const setValidationCodeSelector = selector("setValidationCode(bytes32)");
38
43
  /** 4-byte ABI function selector for `clearValidationCode()`. */
39
44
  export const clearValidationCodeSelector = selector("clearValidationCode()");
45
+ // ---------------------------------------------------------------------------
46
+ // AA Phase 2 — Guardian Recovery selectors (v0.19.0-dev)
47
+ // ---------------------------------------------------------------------------
48
+ /** 4-byte ABI function selector for `setGuardians(address[],uint8,uint64)`. */
49
+ export const setGuardiansSelector = selector("setGuardians(address[],uint8,uint64)");
50
+ /** 4-byte ABI function selector for `submitRecovery(address,bytes,uint8)`. */
51
+ export const submitRecoverySelector = selector("submitRecovery(address,bytes,uint8)");
52
+ /** 4-byte ABI function selector for `executeRecovery(address)`. */
53
+ export const executeRecoverySelector = selector("executeRecovery(address)");
54
+ /** 4-byte ABI function selector for `cancelRecovery(address)`. */
55
+ export const cancelRecoverySelector = selector("cancelRecovery(address)");
40
56
  /**
41
57
  * ABI-encode calldata for `rotateKey(bytes publicKey, uint8 algorithmId)`.
42
58
  *
@@ -103,3 +119,99 @@ export function isSystemContractAddress(address) {
103
119
  address === accountManagerHexAddress ||
104
120
  address === validatorRegistryHexAddress);
105
121
  }
122
+ // ---------------------------------------------------------------------------
123
+ // AA Phase 2 — Guardian Recovery calldata encoders (v0.19.0-dev)
124
+ // ---------------------------------------------------------------------------
125
+ /**
126
+ * ABI-encode calldata for `setGuardians(address[] guardians, uint8 threshold, uint64 timelock)`.
127
+ *
128
+ * Configures the guardian recovery set for the caller's account. Only the
129
+ * account owner can call this (the call must be made as an inner call from
130
+ * the owner's AA bundle, or as a direct transaction).
131
+ *
132
+ * @param guardians - Array of guardian addresses (1..5). May be hex or `pq1…` form.
133
+ * @param threshold - k-of-n required votes (1 ≤ threshold ≤ guardians.length).
134
+ * @param timelock - Minimum blocks between threshold-reach and execution (≥ 100).
135
+ * @returns `HexString` with the 4-byte selector prepended.
136
+ *
137
+ * @example
138
+ * ```typescript
139
+ * const data = encodeSetGuardiansCalldata(
140
+ * ["0xGuardian1…", "0xGuardian2…", "0xGuardian3…"],
141
+ * 2, // 2-of-3 threshold
142
+ * 100, // 100-block timelock
143
+ * );
144
+ * ```
145
+ */
146
+ export function encodeSetGuardiansCalldata(guardians, threshold, timelock) {
147
+ // Normalise to 0x… form; encodeAbiParameters expects `0x${string}` addresses.
148
+ const hexGuardians = guardians.map((g) => {
149
+ const s = typeof g === "string" ? g : bytesToHex(g);
150
+ return (s.startsWith("0x") ? s : `0x${s}`);
151
+ });
152
+ const encoded = encodeAbiParameters([{ type: "address[]" }, { type: "uint8" }, { type: "uint64" }], [hexGuardians, threshold, BigInt(timelock)]);
153
+ return `${setGuardiansSelector}${encoded.slice(2)}`;
154
+ }
155
+ /**
156
+ * ABI-encode calldata for `submitRecovery(address account, bytes newPubkey, uint8 newAlgo)`.
157
+ *
158
+ * Called by a guardian to vote for a new PQ public key on behalf of `account`.
159
+ * When k-of-n threshold is reached, the proposal becomes executable after
160
+ * `timelock` blocks.
161
+ *
162
+ * @param account - Account being recovered (0x hex or `pq1…` form).
163
+ * @param newPubkey - Raw bytes of the new PQ public key.
164
+ * @param newAlgo - Algorithm ID for the new key (ML-DSA-65 = 0, etc.).
165
+ * @returns `HexString` with the 4-byte selector prepended.
166
+ *
167
+ * @example
168
+ * ```typescript
169
+ * const data = encodeSubmitRecoveryCalldata(
170
+ * "0xAccountAddress…",
171
+ * newPubkeyBytes,
172
+ * 0, // ML-DSA-65
173
+ * );
174
+ * ```
175
+ */
176
+ export function encodeSubmitRecoveryCalldata(account, newPubkey, newAlgo) {
177
+ const hexAccount = normaliseToHex(account);
178
+ const encoded = encodeAbiParameters([{ type: "address" }, { type: "bytes" }, { type: "uint8" }], [hexAccount, bytesToHex(newPubkey), newAlgo]);
179
+ return `${submitRecoverySelector}${encoded.slice(2)}`;
180
+ }
181
+ /**
182
+ * ABI-encode calldata for `executeRecovery(address account)`.
183
+ *
184
+ * Can be called by anyone once the recovery proposal has reached maturity
185
+ * (`current_block >= maturity_block && maturity_block != 0`).
186
+ *
187
+ * @param account - Account whose recovery proposal to execute.
188
+ * @returns `HexString` with the 4-byte selector prepended.
189
+ */
190
+ export function encodeExecuteRecoveryCalldata(account) {
191
+ const hexAccount = normaliseToHex(account);
192
+ const encoded = encodeAbiParameters([{ type: "address" }], [hexAccount]);
193
+ return `${executeRecoverySelector}${encoded.slice(2)}`;
194
+ }
195
+ /**
196
+ * ABI-encode calldata for `cancelRecovery(address account)`.
197
+ *
198
+ * Owner-only: removes the active recovery proposal. Useful if the account
199
+ * owner regains access before the timelock expires.
200
+ *
201
+ * @param account - Account whose recovery proposal to cancel (must be caller).
202
+ * @returns `HexString` with the 4-byte selector prepended.
203
+ */
204
+ export function encodeCancelRecoveryCalldata(account) {
205
+ const hexAccount = normaliseToHex(account);
206
+ const encoded = encodeAbiParameters([{ type: "address" }], [hexAccount]);
207
+ return `${cancelRecoverySelector}${encoded.slice(2)}`;
208
+ }
209
+ // ---------------------------------------------------------------------------
210
+ // Internal helpers
211
+ // ---------------------------------------------------------------------------
212
+ function normaliseToHex(address) {
213
+ if (typeof address !== "string") {
214
+ return bytesToHex(address);
215
+ }
216
+ return (address.startsWith("0x") ? address : `0x${address}`);
217
+ }
@@ -1,4 +1,4 @@
1
- import type { AaBundle, AaInnerCall, AddressLike, HexString, ShellSignature, ShellTransactionRequest, SignedShellTransaction, SignatureTypeName } from "./types.js";
1
+ import type { AaBundle, AaInnerCall, AddressLike, HexString, SessionAuth, ShellSignature, ShellTransactionRequest, SignedShellTransaction, SignatureTypeName } from "./types.js";
2
2
  import { AA_BUNDLE_TX_TYPE, AA_MAX_INNER_CALLS } from "./types.js";
3
3
  export { AA_BUNDLE_TX_TYPE, AA_MAX_INNER_CALLS };
4
4
  /** Default transaction type: `2` (EIP-1559). */
@@ -334,3 +334,86 @@ export declare function buildInnerTransfer(to: AddressLike, value: bigint, gasLi
334
334
  * @returns An `AaInnerCall` ready for use in {@link buildBatchTransaction}.
335
335
  */
336
336
  export declare function buildInnerCall(to: AddressLike, data: HexString, gasLimit: number, value?: bigint): AaInnerCall;
337
+ /**
338
+ * Options for {@link buildContractPaymasterTransaction}.
339
+ *
340
+ * Extends {@link BuildBatchTransactionOptions} with contract paymaster fields.
341
+ */
342
+ export interface BuildContractPaymasterTransactionOptions extends BuildBatchTransactionOptions {
343
+ /** Contract paymaster address. */
344
+ paymaster: AddressLike;
345
+ /**
346
+ * Opaque context bytes forwarded to `IPaymaster.validatePaymasterOp`.
347
+ * Max 256 bytes.
348
+ */
349
+ paymasterContext: Uint8Array | number[];
350
+ }
351
+ /**
352
+ * Build an AA batch transaction using a **contract paymaster** (Phase 2).
353
+ *
354
+ * Unlike {@link buildSponsoredTransaction} (off-chain paymaster), a contract
355
+ * paymaster implements `IPaymaster.validatePaymasterOp` on-chain and receives
356
+ * `paymasterContext` as input. The bundle must NOT include `paymaster_signature`.
357
+ *
358
+ * @param options - Options including contract paymaster address and context bytes.
359
+ * @returns `{ tx, aa_bundle }` — an unsigned transaction skeleton plus bundle.
360
+ *
361
+ * @example
362
+ * ```typescript
363
+ * const { tx, aa_bundle } = buildContractPaymasterTransaction({
364
+ * chainId: 424242,
365
+ * nonce: 0,
366
+ * innerCalls: [...],
367
+ * paymaster: contractPaymasterAddress,
368
+ * paymasterContext: contextBytes,
369
+ * });
370
+ * const signingHash = hashBatchTransaction(tx, aa_bundle);
371
+ * const signed = await signer.buildSignedTransaction({ tx, txHash: signingHash, aaBundle: aa_bundle });
372
+ * ```
373
+ */
374
+ export declare function buildContractPaymasterTransaction(options: BuildContractPaymasterTransactionOptions): {
375
+ tx: ShellTransactionRequest;
376
+ aa_bundle: AaBundle;
377
+ };
378
+ /**
379
+ * Options for {@link buildSessionKeyTransaction}.
380
+ *
381
+ * Extends {@link BuildBatchTransactionOptions} with session key authorization.
382
+ */
383
+ export interface BuildSessionKeyTransactionOptions extends BuildBatchTransactionOptions {
384
+ /** Pre-built session key authorization. */
385
+ sessionAuth: SessionAuth;
386
+ }
387
+ /**
388
+ * Build an AA batch transaction authorized by a **session key** (Phase 2).
389
+ *
390
+ * The session key must have been authorized by the root account via a
391
+ * `root_signature` over its `auth_hash`. The transaction is then signed
392
+ * by the session key via `session_signature`.
393
+ *
394
+ * @param options - Options including the session key authorization struct.
395
+ * @returns `{ tx, aa_bundle }` — an unsigned transaction skeleton plus bundle.
396
+ *
397
+ * @example
398
+ * ```typescript
399
+ * const { tx, aa_bundle } = buildSessionKeyTransaction({
400
+ * chainId: 424242,
401
+ * nonce: 0,
402
+ * innerCalls: [...],
403
+ * sessionAuth: {
404
+ * session_pubkey: Array.from(sessionPubkeyBytes),
405
+ * session_algo: 0,
406
+ * target: null,
407
+ * value_cap: "0xde0b6b3a7640000",
408
+ * expiry_block: 500,
409
+ * root_signature: Array.from(rootSigBytes),
410
+ * session_signature: Array.from(sessionSigBytes),
411
+ * },
412
+ * });
413
+ * const signingHash = hashBatchTransaction(tx, aa_bundle);
414
+ * ```
415
+ */
416
+ export declare function buildSessionKeyTransaction(options: BuildSessionKeyTransactionOptions): {
417
+ tx: ShellTransactionRequest;
418
+ aa_bundle: AaBundle;
419
+ };
@@ -377,6 +377,10 @@ export function hashBatchTransaction(tx, bundle) {
377
377
  const paymasterField = bundle.paymaster
378
378
  ? normalizeHexAddress(bundle.paymaster)
379
379
  : "0x";
380
+ // paymaster_context: raw bytes or empty.
381
+ const paymasterContextField = bundle.paymaster_context && bundle.paymaster_context.length > 0
382
+ ? bytesToHex(new Uint8Array(bundle.paymaster_context))
383
+ : "0x";
380
384
  const txFields = [
381
385
  toRlpUint(tx.chain_id),
382
386
  toRlpUint(tx.nonce),
@@ -392,7 +396,7 @@ export function hashBatchTransaction(tx, bundle) {
392
396
  toRlpUint(tx.max_fee_per_blob_gas ?? 0),
393
397
  (tx.blob_versioned_hashes ?? []).map((hash) => hash),
394
398
  ];
395
- const bundleSigningFields = [innerCallsRlp, paymasterField];
399
+ const bundleSigningFields = [innerCallsRlp, paymasterField, paymasterContextField];
396
400
  const domainBuf = new Uint8Array([BATCH_SIGNING_HASH_DOMAIN]);
397
401
  const txRlp = hexToBytes(toRlp(txFields));
398
402
  const bundleRlp = hexToBytes(toRlp(bundleSigningFields));
@@ -425,3 +429,66 @@ export function buildInnerTransfer(to, value, gasLimit = 21_000) {
425
429
  export function buildInnerCall(to, data, gasLimit, value = 0n) {
426
430
  return { to, value: ("0x" + value.toString(16)), data, gas_limit: gasLimit };
427
431
  }
432
+ /**
433
+ * Build an AA batch transaction using a **contract paymaster** (Phase 2).
434
+ *
435
+ * Unlike {@link buildSponsoredTransaction} (off-chain paymaster), a contract
436
+ * paymaster implements `IPaymaster.validatePaymasterOp` on-chain and receives
437
+ * `paymasterContext` as input. The bundle must NOT include `paymaster_signature`.
438
+ *
439
+ * @param options - Options including contract paymaster address and context bytes.
440
+ * @returns `{ tx, aa_bundle }` — an unsigned transaction skeleton plus bundle.
441
+ *
442
+ * @example
443
+ * ```typescript
444
+ * const { tx, aa_bundle } = buildContractPaymasterTransaction({
445
+ * chainId: 424242,
446
+ * nonce: 0,
447
+ * innerCalls: [...],
448
+ * paymaster: contractPaymasterAddress,
449
+ * paymasterContext: contextBytes,
450
+ * });
451
+ * const signingHash = hashBatchTransaction(tx, aa_bundle);
452
+ * const signed = await signer.buildSignedTransaction({ tx, txHash: signingHash, aaBundle: aa_bundle });
453
+ * ```
454
+ */
455
+ export function buildContractPaymasterTransaction(options) {
456
+ const { tx, aa_bundle } = buildBatchTransaction(options);
457
+ aa_bundle.paymaster = options.paymaster;
458
+ aa_bundle.paymaster_context = Array.from(options.paymasterContext);
459
+ return { tx, aa_bundle };
460
+ }
461
+ /**
462
+ * Build an AA batch transaction authorized by a **session key** (Phase 2).
463
+ *
464
+ * The session key must have been authorized by the root account via a
465
+ * `root_signature` over its `auth_hash`. The transaction is then signed
466
+ * by the session key via `session_signature`.
467
+ *
468
+ * @param options - Options including the session key authorization struct.
469
+ * @returns `{ tx, aa_bundle }` — an unsigned transaction skeleton plus bundle.
470
+ *
471
+ * @example
472
+ * ```typescript
473
+ * const { tx, aa_bundle } = buildSessionKeyTransaction({
474
+ * chainId: 424242,
475
+ * nonce: 0,
476
+ * innerCalls: [...],
477
+ * sessionAuth: {
478
+ * session_pubkey: Array.from(sessionPubkeyBytes),
479
+ * session_algo: 0,
480
+ * target: null,
481
+ * value_cap: "0xde0b6b3a7640000",
482
+ * expiry_block: 500,
483
+ * root_signature: Array.from(rootSigBytes),
484
+ * session_signature: Array.from(sessionSigBytes),
485
+ * },
486
+ * });
487
+ * const signingHash = hashBatchTransaction(tx, aa_bundle);
488
+ * ```
489
+ */
490
+ export function buildSessionKeyTransaction(options) {
491
+ const { tx, aa_bundle } = buildBatchTransaction(options);
492
+ aa_bundle.session_auth = options.sessionAuth;
493
+ return { tx, aa_bundle };
494
+ }
package/dist/types.d.ts CHANGED
@@ -90,20 +90,125 @@ export interface AaInnerCall {
90
90
  *
91
91
  * All inner calls execute atomically under a single PQ signature covering
92
92
  * the outer envelope + bundle (via `batch_signing_hash`).
93
+ *
94
+ * ## AA Phase 2 extensions (v0.19.0-dev)
95
+ *
96
+ * - **Contract paymaster** (`paymaster_context`): pass opaque bytes to a
97
+ * contract paymaster's `validatePaymasterOp` on-chain. Mutually exclusive
98
+ * with `paymaster_signature` (off-chain/EOA paymaster).
99
+ * - **Session keys** (`session_auth`): attach a short-lived sub-key
100
+ * authorization instead of the root PQ key.
93
101
  */
94
102
  export interface AaBundle {
95
103
  /** Ordered list of inner calls to execute. Max {@link AA_MAX_INNER_CALLS}. */
96
104
  inner_calls: AaInnerCall[];
97
105
  /**
98
106
  * Optional paymaster address paying the gas cost.
99
- * When set, `paymaster_signature` must also be provided.
107
+ *
108
+ * - EOA/off-chain paymaster: set `paymaster_signature` (not `paymaster_context`).
109
+ * - Contract paymaster: set `paymaster_context` (not `paymaster_signature`).
100
110
  */
101
111
  paymaster?: AddressLike | null;
102
112
  /**
103
113
  * Paymaster's PQ signature over the `paymaster_signing_hash`.
104
- * Required when `paymaster` is set.
114
+ * Required for EOA/off-chain paymaster. Mutually exclusive with `paymaster_context`.
105
115
  */
106
116
  paymaster_signature?: number[] | null;
117
+ /**
118
+ * Opaque context bytes forwarded to `IPaymaster.validatePaymasterOp`.
119
+ * Required for contract paymaster. Mutually exclusive with `paymaster_signature`.
120
+ * Max 256 bytes.
121
+ */
122
+ paymaster_context?: number[] | null;
123
+ /**
124
+ * Session key authorization. When set the root signature field belongs to
125
+ * the session `root_signature` and `session_signature` pair, not the root
126
+ * key signing the tx directly.
127
+ */
128
+ session_auth?: SessionAuth | null;
129
+ }
130
+ /**
131
+ * Maximum length of `paymaster_context` bytes (256 bytes).
132
+ */
133
+ export declare const AA_MAX_PAYMASTER_CONTEXT = 256;
134
+ /**
135
+ * Extra PQ verify gas cost per session key authorization (2 × PQ_VERIFY_GAS = 20 000).
136
+ * Added to intrinsic gas when a session key is used.
137
+ */
138
+ export declare const AA_SESSION_KEY_GAS_SURCHARGE = 20000;
139
+ /**
140
+ * Session key authorization attached to an AA bundle.
141
+ *
142
+ * Allows a short-lived sub-key to authorize a transaction on behalf of the
143
+ * root account, with optional restrictions on target address and value cap.
144
+ *
145
+ * ## Spec (AA_PHASE2_SPEC.md §4)
146
+ *
147
+ * 1. `session_pubkey` is authorized by the root key's `root_signature` over
148
+ * `auth_hash = blake3(0x81 || session_pubkey || target(20B) || value_cap(32B BE) || expiry_block(8B BE) || chain_id(8B BE))`.
149
+ * 2. The transaction is signed by `session_pubkey` via `session_signature`.
150
+ * 3. `expiry_block` must be > current block at validation time.
151
+ * 4. Σ(inner_call.value) ≤ `value_cap`.
152
+ * 5. If `target` is set, all inner calls must target that address.
153
+ *
154
+ * @example
155
+ * ```typescript
156
+ * const sessionAuth: SessionAuth = {
157
+ * session_pubkey: Array.from(sessionPubkeyBytes),
158
+ * session_algo: 0, // ML-DSA-65
159
+ * target: null,
160
+ * value_cap: "0xde0b6b3a7640000",
161
+ * expiry_block: 500,
162
+ * root_signature: Array.from(rootSigBytes),
163
+ * session_signature: Array.from(sessionSigBytes),
164
+ * };
165
+ * ```
166
+ */
167
+ export interface SessionAuth {
168
+ /** Raw bytes of the session public key. */
169
+ session_pubkey: number[];
170
+ /** Algorithm ID of the session key (Dilithium3/ML-DSA-65 = 0, SphincsSha2256f = 2). */
171
+ session_algo: number;
172
+ /** If set, every inner call in the bundle must target this address. */
173
+ target?: AddressLike | null;
174
+ /** Maximum total value (Σ inner_call.value) permitted in wei as a hex string. */
175
+ value_cap: HexString;
176
+ /** Block number after which the session key is no longer valid (exclusive). */
177
+ expiry_block: number;
178
+ /**
179
+ * Root account's PQ signature over the session key authorization hash.
180
+ * `auth_hash = blake3(0x81 || session_pubkey || target || value_cap || expiry_block || chain_id)`.
181
+ */
182
+ root_signature: number[];
183
+ /** Session key's PQ signature over the tx `sender_signing_hash()`. */
184
+ session_signature: number[];
185
+ }
186
+ /**
187
+ * Guardian recovery configuration stored on-chain for an account.
188
+ *
189
+ * Set via `setGuardians` calldata (use {@link encodeSetGuardiansCalldata}).
190
+ */
191
+ export interface GuardianConfig {
192
+ /** Guardian addresses (1..=5). */
193
+ guardians: AddressLike[];
194
+ /** Required votes (k-of-n). */
195
+ threshold: number;
196
+ /** Minimum blocks between threshold-reach and execution (≥ 100). */
197
+ timelock: number;
198
+ }
199
+ /**
200
+ * Active recovery proposal returned by the `shell_getRecoveryProposal` RPC method
201
+ * (if implemented by the node).
202
+ */
203
+ export interface RecoveryProposal {
204
+ /** Proposed new PQ public key as a hex string. */
205
+ new_pubkey: HexString;
206
+ /** Algorithm ID of the new public key. */
207
+ new_algo: number;
208
+ /** Guardian addresses that have voted for this proposal. */
209
+ votes: AddressLike[];
210
+ /** Block after which `executeRecovery` may be called (0 = threshold not yet met). */
211
+ maturity_block: number;
107
212
  }
108
213
  /**
109
214
  * A fully-signed Shell Chain transaction ready to broadcast via `shell_sendTransaction`.
package/dist/types.js CHANGED
@@ -11,3 +11,15 @@ export const AA_BUNDLE_TX_TYPE = 0x7e;
11
11
  * Maximum number of inner calls per AA bundle.
12
12
  */
13
13
  export const AA_MAX_INNER_CALLS = 16;
14
+ // ---------------------------------------------------------------------------
15
+ // AA Phase 2 types (v0.19.0-dev)
16
+ // ---------------------------------------------------------------------------
17
+ /**
18
+ * Maximum length of `paymaster_context` bytes (256 bytes).
19
+ */
20
+ export const AA_MAX_PAYMASTER_CONTEXT = 256;
21
+ /**
22
+ * Extra PQ verify gas cost per session key authorization (2 × PQ_VERIFY_GAS = 20 000).
23
+ * Added to intrinsic gas when a session key is used.
24
+ */
25
+ export const AA_SESSION_KEY_GAS_SURCHARGE = 20_000;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "shell-sdk",
3
- "version": "0.4.0",
3
+ "version": "0.5.0",
4
4
  "description": "TypeScript SDK for Shell Chain — build quantum-safe dApps before Q-Day.",
5
5
  "license": "MIT",
6
6
  "type": "module",