@keelcodes/sdk 2.2.0 → 2.3.1

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,28 @@
1
+ import type { Address, Hex } from 'viem';
2
+ import type { BundlerLike, UserOpReceipt, UserOpResult, UserOperationV7 } from './types';
3
+ export interface BundlerClientOptions {
4
+ url: string;
5
+ entryPoint: Address;
6
+ headers?: Record<string, string>;
7
+ /** 单次 HTTP 超时(ms),默认 30s */
8
+ timeoutMs?: number;
9
+ /** 收据轮询间隔(ms),默认 1s */
10
+ pollIntervalMs?: number;
11
+ /** 等收据总超时(ms),默认 120s */
12
+ waitTimeoutMs?: number;
13
+ }
14
+ export declare class BundlerClient implements BundlerLike {
15
+ private readonly url;
16
+ private readonly entryPoint;
17
+ private readonly headers;
18
+ private readonly timeoutMs;
19
+ private readonly pollIntervalMs;
20
+ private readonly waitTimeoutMs;
21
+ private nextId;
22
+ constructor(opts: BundlerClientOptions);
23
+ estimateUserOperationGas(op: UserOperationV7): Promise<Partial<Pick<UserOperationV7, 'callGasLimit' | 'verificationGasLimit' | 'preVerificationGas'>>>;
24
+ sendUserOperation(op: UserOperationV7): Promise<UserOpResult>;
25
+ /** 轮询 `eth_getUserOperationReceipt`;超时抛错(不静默返回未确认)。 */
26
+ waitForReceipt(userOpHash: Hex): Promise<UserOpReceipt>;
27
+ private rpc;
28
+ }
@@ -0,0 +1,95 @@
1
+ "use strict";
2
+ // ---------------------------------------------------------------------------
3
+ // 最小 ERC-4337 bundler 客户端(JSON-RPC over HTTP)
4
+ //
5
+ // 只覆盖 session 付款需要的三件事:
6
+ // eth_estimateUserOperationGas / eth_sendUserOperation / eth_getUserOperationReceipt
7
+ // Keel 托管端点:`https://keel.codes/v1/bundler/{chain}`(可自定义 headers 带鉴权)。
8
+ // ---------------------------------------------------------------------------
9
+ Object.defineProperty(exports, "__esModule", { value: true });
10
+ exports.BundlerClient = void 0;
11
+ const userop_1 = require("./userop");
12
+ const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
13
+ class BundlerClient {
14
+ url;
15
+ entryPoint;
16
+ headers;
17
+ timeoutMs;
18
+ pollIntervalMs;
19
+ waitTimeoutMs;
20
+ nextId = 1;
21
+ constructor(opts) {
22
+ this.url = opts.url;
23
+ this.entryPoint = opts.entryPoint;
24
+ this.headers = { 'Content-Type': 'application/json', ...(opts.headers ?? {}) };
25
+ this.timeoutMs = opts.timeoutMs ?? 30_000;
26
+ this.pollIntervalMs = opts.pollIntervalMs ?? 1_000;
27
+ this.waitTimeoutMs = opts.waitTimeoutMs ?? 120_000;
28
+ }
29
+ async estimateUserOperationGas(op) {
30
+ const r = await this.rpc('eth_estimateUserOperationGas', [(0, userop_1.userOpToRpc)(op), this.entryPoint]);
31
+ return {
32
+ callGasLimit: BigInt(r.callGasLimit),
33
+ verificationGasLimit: BigInt(r.verificationGasLimit),
34
+ preVerificationGas: BigInt(r.preVerificationGas),
35
+ };
36
+ }
37
+ async sendUserOperation(op) {
38
+ const userOpHash = await this.rpc('eth_sendUserOperation', [
39
+ (0, userop_1.userOpToRpc)(op),
40
+ this.entryPoint,
41
+ ]);
42
+ const receipt = await this.waitForReceipt(userOpHash);
43
+ return { userOpHash, bundlerUrl: this.url, receipt };
44
+ }
45
+ /** 轮询 `eth_getUserOperationReceipt`;超时抛错(不静默返回未确认)。 */
46
+ async waitForReceipt(userOpHash) {
47
+ const deadline = Date.now() + this.waitTimeoutMs;
48
+ let lastErr;
49
+ while (Date.now() < deadline) {
50
+ try {
51
+ const r = await this.rpc('eth_getUserOperationReceipt', [userOpHash]);
52
+ if (r) {
53
+ return {
54
+ txHash: r.transactionHash ?? r.receipt?.transactionHash,
55
+ success: r.success,
56
+ actualGasCost: BigInt(r.actualGasCost),
57
+ actualGasUsed: BigInt(r.actualGasUsed),
58
+ logs: r.logs ?? [],
59
+ };
60
+ }
61
+ }
62
+ catch (e) {
63
+ // 轮询期 RPC 抖动不致命
64
+ lastErr = e;
65
+ }
66
+ await sleep(this.pollIntervalMs);
67
+ }
68
+ throw new Error(`[keel-sdk] userOp receipt timeout after ${this.waitTimeoutMs}ms (${userOpHash})` +
69
+ (lastErr instanceof Error ? `: ${lastErr.message}` : ''));
70
+ }
71
+ async rpc(method, params) {
72
+ const controller = new AbortController();
73
+ const timer = setTimeout(() => controller.abort(), this.timeoutMs);
74
+ let res;
75
+ try {
76
+ res = await fetch(this.url, {
77
+ method: 'POST',
78
+ headers: this.headers,
79
+ body: JSON.stringify({ jsonrpc: '2.0', id: this.nextId++, method, params }),
80
+ signal: controller.signal,
81
+ });
82
+ }
83
+ finally {
84
+ clearTimeout(timer);
85
+ }
86
+ const json = (await res.json().catch(() => null));
87
+ if (!res.ok || !json || json.error) {
88
+ const msg = json?.error?.message ?? `HTTP ${res.status}`;
89
+ const data = json?.error?.data ? ` ${JSON.stringify(json.error.data).slice(0, 300)}` : '';
90
+ throw new Error(`[keel-sdk] bundler ${method} failed: ${msg}${data}`);
91
+ }
92
+ return json.result;
93
+ }
94
+ }
95
+ exports.BundlerClient = BundlerClient;
@@ -0,0 +1,30 @@
1
+ import { type Address, type PublicClient } from 'viem';
2
+ import type { ChainAAConfig } from './types';
3
+ export type KeelChain = 'base' | 'bsc' | 'ethereum';
4
+ /** EntryPoint v0.7(三链同址) */
5
+ export declare const KEEL_ENTRYPOINT_V07: Address;
6
+ /** KeelSessionValidator 副本(slot 0,三链同址) */
7
+ export declare const KEEL_SESSION_VALIDATOR: Address;
8
+ /** KeelPolicyHook 副本(slot 0,与 validator 成对,三链同址) */
9
+ export declare const KEEL_SESSION_HOOK: Address;
10
+ /** KeelTokenPaymaster(三链同址;gas 垫付 + 稳定币结算) */
11
+ export declare const KEEL_TOKEN_PAYMASTER: Address;
12
+ export declare const KEEL_CHAIN_IDS: Record<KeelChain, number>;
13
+ /** 各链 USDC(BSC 为 Binance-Peg USDC,18 位) */
14
+ export declare const KEEL_USDC: Record<KeelChain, Address>;
15
+ export declare const KEEL_USDC_DECIMALS: Record<KeelChain, number>;
16
+ /** Keel 托管 bundler(ERC-4337 JSON-RPC,路径即链别名) */
17
+ export declare const KEEL_BUNDLER_URL: Record<KeelChain, string>;
18
+ export interface KeelChainOptions {
19
+ rpcUrl?: string;
20
+ /** 覆盖 bundler(缺省 `KEEL_BUNDLER_URL[chain]`) */
21
+ bundlerUrl?: string;
22
+ bundlerHeaders?: Record<string, string>;
23
+ /** 覆盖 validator/hook 副本(仅重部署后需要) */
24
+ sessionValidator?: Address;
25
+ sessionHook?: Address;
26
+ }
27
+ /** 取某条链的 ChainAAConfig(内置默认 + 可选覆盖)。 */
28
+ export declare function keelChainConfig(chain: KeelChain, opts?: KeelChainOptions): ChainAAConfig;
29
+ /** 便捷:创建该链的只读 PublicClient(用于读 EntryPoint nonce 等)。 */
30
+ export declare function keelPublicClient(chain: KeelChain, rpcUrl?: string): PublicClient;
@@ -0,0 +1,63 @@
1
+ "use strict";
2
+ // ---------------------------------------------------------------------------
3
+ // @keelcodes/sdk — 内置三链 AA 栈地址(B 端零配置起步)
4
+ //
5
+ // 来源:infraX 生产部署(2026-10 三链同址核验)。
6
+ // - EntryPoint v0.7 / Kernel 官方栈:三链同址(ZeroDev 确定性部署)
7
+ // - KeelSessionValidator 副本 / KeelPolicyHook / KeelTokenPaymaster:三链同址
8
+ // - USDC:三链异址,且 **BSC 为 18 位精度**(其余 6 位)
9
+ //
10
+ // ⚠️ 均为不可变部署;链上重部署须同步本表。
11
+ // ---------------------------------------------------------------------------
12
+ Object.defineProperty(exports, "__esModule", { value: true });
13
+ exports.KEEL_BUNDLER_URL = exports.KEEL_USDC_DECIMALS = exports.KEEL_USDC = exports.KEEL_CHAIN_IDS = exports.KEEL_TOKEN_PAYMASTER = exports.KEEL_SESSION_HOOK = exports.KEEL_SESSION_VALIDATOR = exports.KEEL_ENTRYPOINT_V07 = void 0;
14
+ exports.keelChainConfig = keelChainConfig;
15
+ exports.keelPublicClient = keelPublicClient;
16
+ const viem_1 = require("viem");
17
+ const chains_1 = require("viem/chains");
18
+ /** EntryPoint v0.7(三链同址) */
19
+ exports.KEEL_ENTRYPOINT_V07 = '0x0000000071727De22E5E9d8BAf0edAc6f37da032';
20
+ /** KeelSessionValidator 副本(slot 0,三链同址) */
21
+ exports.KEEL_SESSION_VALIDATOR = '0x8BCB3F5d855F5b2a1f1398690Daaa7866992bDE7';
22
+ /** KeelPolicyHook 副本(slot 0,与 validator 成对,三链同址) */
23
+ exports.KEEL_SESSION_HOOK = '0x466b5DC3796D44b0B63FdF2d3bC7a8Ea371a891F';
24
+ /** KeelTokenPaymaster(三链同址;gas 垫付 + 稳定币结算) */
25
+ exports.KEEL_TOKEN_PAYMASTER = '0x4500A1FbAe335F5E20Fa85C70Daa98a89faD6415';
26
+ exports.KEEL_CHAIN_IDS = { base: 8453, bsc: 56, ethereum: 1 };
27
+ /** 各链 USDC(BSC 为 Binance-Peg USDC,18 位) */
28
+ exports.KEEL_USDC = {
29
+ base: '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913',
30
+ bsc: '0x8AC76a51cc950d9822D68b83fE1Ad97B32Cd580d',
31
+ ethereum: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48',
32
+ };
33
+ exports.KEEL_USDC_DECIMALS = { base: 6, bsc: 18, ethereum: 6 };
34
+ /** Keel 托管 bundler(ERC-4337 JSON-RPC,路径即链别名) */
35
+ exports.KEEL_BUNDLER_URL = {
36
+ base: 'https://keel.codes/v1/bundler/base',
37
+ bsc: 'https://keel.codes/v1/bundler/bsc',
38
+ ethereum: 'https://keel.codes/v1/bundler/ethereum',
39
+ };
40
+ const VIEM_CHAIN = { base: chains_1.base, bsc: chains_1.bsc, ethereum: chains_1.mainnet };
41
+ /** 取某条链的 ChainAAConfig(内置默认 + 可选覆盖)。 */
42
+ function keelChainConfig(chain, opts = {}) {
43
+ return {
44
+ chainId: exports.KEEL_CHAIN_IDS[chain],
45
+ entryPoint: exports.KEEL_ENTRYPOINT_V07,
46
+ rpcUrl: opts.rpcUrl,
47
+ kernelVersion: '0.3.1',
48
+ sessionValidatorCopies: [opts.sessionValidator ?? exports.KEEL_SESSION_VALIDATOR],
49
+ sessionHookCopies: [opts.sessionHook ?? exports.KEEL_SESSION_HOOK],
50
+ bundlers: [
51
+ {
52
+ url: opts.bundlerUrl ?? exports.KEEL_BUNDLER_URL[chain],
53
+ priority: 0,
54
+ ...(opts.bundlerHeaders ? { headers: opts.bundlerHeaders } : {}),
55
+ },
56
+ ],
57
+ };
58
+ }
59
+ /** 便捷:创建该链的只读 PublicClient(用于读 EntryPoint nonce 等)。 */
60
+ function keelPublicClient(chain, rpcUrl) {
61
+ const transport = rpcUrl ? (0, viem_1.http)(rpcUrl) : (0, viem_1.http)();
62
+ return (0, viem_1.createPublicClient)({ chain: VIEM_CHAIN[chain], transport });
63
+ }
@@ -0,0 +1,10 @@
1
+ export * from './types';
2
+ export * from './chain-defaults';
3
+ export * from './nonce';
4
+ export * from './token-paymaster';
5
+ export * from './signer';
6
+ export * from './userop';
7
+ export * from './keel-session';
8
+ export * from './x402';
9
+ export * from './x402-session';
10
+ export * from './bundler';
@@ -0,0 +1,29 @@
1
+ "use strict";
2
+ // ---------------------------------------------------------------------------
3
+ // @keelcodes/sdk — AA / x402 客户端入口
4
+ // ---------------------------------------------------------------------------
5
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
6
+ if (k2 === undefined) k2 = k;
7
+ var desc = Object.getOwnPropertyDescriptor(m, k);
8
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
9
+ desc = { enumerable: true, get: function() { return m[k]; } };
10
+ }
11
+ Object.defineProperty(o, k2, desc);
12
+ }) : (function(o, m, k, k2) {
13
+ if (k2 === undefined) k2 = k;
14
+ o[k2] = m[k];
15
+ }));
16
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
17
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
18
+ };
19
+ Object.defineProperty(exports, "__esModule", { value: true });
20
+ __exportStar(require("./types"), exports);
21
+ __exportStar(require("./chain-defaults"), exports);
22
+ __exportStar(require("./nonce"), exports);
23
+ __exportStar(require("./token-paymaster"), exports);
24
+ __exportStar(require("./signer"), exports);
25
+ __exportStar(require("./userop"), exports);
26
+ __exportStar(require("./keel-session"), exports);
27
+ __exportStar(require("./x402"), exports);
28
+ __exportStar(require("./x402-session"), exports);
29
+ __exportStar(require("./bundler"), exports);
@@ -0,0 +1,70 @@
1
+ import { type Address, type Hex, type PublicClient } from 'viem';
2
+ import type { ChainAAConfig, SessionPolicy, Signer, UserOperationV7 } from './types';
3
+ export declare const KEEL_POLICY_VERSION = 1n;
4
+ /** bytes32 归一化(已是 0x+64 hex 直接用,否则左对齐补零) */
5
+ export declare function toBytes32(id: string): Hex;
6
+ /** 链上 Rule(字段顺序即 ABI 顺序,不可调换) */
7
+ export interface KeelRule {
8
+ target: Address;
9
+ selectors: Hex[];
10
+ maxPerTx: bigint;
11
+ maxDaily: bigint;
12
+ maxCalls: bigint;
13
+ tokenLimits: KeelTokenLimit[];
14
+ }
15
+ export interface KeelTokenLimit {
16
+ token: Address;
17
+ maxPerTx: bigint;
18
+ maxDaily: bigint;
19
+ }
20
+ /**
21
+ * SessionPermission[] → 链上 Rule[](每条 target 一个 Rule)。
22
+ * tokenLimits 只挂到 `token == target` 的那条 Rule(链上强制同址)。
23
+ */
24
+ export declare function toKeelRules(policy: SessionPolicy): KeelRule[];
25
+ /** `policyData = abi.encode(version, validAfter, validUntil, Rule[])` */
26
+ export declare function encodeKeelPolicyData(policy: SessionPolicy): Hex;
27
+ /** 解析 slot 对应的 validator 副本地址(缺配置直接抛错,不回退旧模块)。 */
28
+ export declare function resolveKeelValidator(chainConfig: ChainAAConfig, slot: number): Address;
29
+ /** agent 侧签名对象:digest = keccak256(userOpHash ‖ keccak256(policyData)) */
30
+ export declare function keelSessionDigest(userOpHash: Hex, policyData: Hex): Hex;
31
+ /** agent 侧 userOp.signature 组装:sessionId(32B) ‖ policyData ‖ sessionKeySig(65B)。 */
32
+ export declare function encodeKeelSessionSignature(p: {
33
+ sessionId: string;
34
+ policyData: Hex;
35
+ sig: Hex;
36
+ }): Hex;
37
+ export interface BuildKeelSessionUserOpParams {
38
+ /** 未传 nonce 时用于读链上 getNonce;传 nonce(relay 预分配)时可不给 */
39
+ client?: PublicClient;
40
+ chainConfig: ChainAAConfig;
41
+ account: Address;
42
+ /** session 策略(复算 policyData = 链上 commitment,须与 enable 时逐字节一致) */
43
+ policy: SessionPolicy;
44
+ /** slot 序号(= validator 副本下标;一 key 一副本) */
45
+ slot: number;
46
+ /** 该 key 的 agent 通道号(nonceKey 隔离多 agent 并发);缺省 0 */
47
+ agentIndex?: number | bigint;
48
+ /**
49
+ * nonce 整值(= `nonceKey<<64 | sequence`),由 relay `POST /v1/wallet/session/nonce/allocate`
50
+ * 预分配后回传。缺省则读链上当前 getNonce(单 agent 串行场景)。
51
+ */
52
+ nonce?: bigint;
53
+ call: {
54
+ target: Address;
55
+ value?: bigint;
56
+ data: Hex;
57
+ };
58
+ /** agent(session key)签名者 */
59
+ agentSigner: Signer;
60
+ gas?: Partial<Pick<UserOperationV7, 'callGasLimit' | 'verificationGasLimit' | 'preVerificationGas' | 'maxFeePerGas' | 'maxPriorityFeePerGas'>>;
61
+ }
62
+ /**
63
+ * v1(Keel)agent 侧调用 UserOp 构造(已签名):
64
+ * nonce = 预分配整值,或 `EntryPoint.getNonce(account, validatorNonceKey(validator, agentIndex))`
65
+ * callData = `executeUserOp( execute(target,value,data) )` —— 挂真 hook 时必需
66
+ * signature= sessionId ‖ policyData ‖ agentSig(keccak256(userOpHash ‖ keccak256(policyData)))
67
+ *
68
+ * 与 aa-sdk 的 `buildKeelSessionUserOp` 逐字节一致(同输入同输出)。
69
+ */
70
+ export declare function buildKeelSessionUserOp(p: BuildKeelSessionUserOpParams): Promise<UserOperationV7>;
@@ -0,0 +1,132 @@
1
+ "use strict";
2
+ // ---------------------------------------------------------------------------
3
+ // Keel(v1) session 编码:policyData / 摘要 / 签名布局
4
+ //
5
+ // 链上契约(KeelSessionValidator + KeelPolicyHook 共用同一份 policyData):
6
+ // policyData = abi.encode(version=1, validAfter, validUntil, Rule[])
7
+ // UserOp 摘要 = keccak256(userOpHash ‖ keccak256(policyData))
8
+ // signature = sessionId(32B) ‖ policyData ‖ sessionKeySig(65B)
9
+ // 链上只存 keccak256(policyData) 作为 commitment,因此这里必须逐字节一致。
10
+ // ---------------------------------------------------------------------------
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.KEEL_POLICY_VERSION = void 0;
13
+ exports.toBytes32 = toBytes32;
14
+ exports.toKeelRules = toKeelRules;
15
+ exports.encodeKeelPolicyData = encodeKeelPolicyData;
16
+ exports.resolveKeelValidator = resolveKeelValidator;
17
+ exports.keelSessionDigest = keelSessionDigest;
18
+ exports.encodeKeelSessionSignature = encodeKeelSessionSignature;
19
+ exports.buildKeelSessionUserOp = buildKeelSessionUserOp;
20
+ const viem_1 = require("viem");
21
+ const userop_1 = require("./userop");
22
+ const nonce_1 = require("./nonce");
23
+ exports.KEEL_POLICY_VERSION = 1n;
24
+ /** bytes32 归一化(已是 0x+64 hex 直接用,否则左对齐补零) */
25
+ function toBytes32(id) {
26
+ return ((0, viem_1.isHex)(id) && id.length === 66 ? id : (0, viem_1.toHex)(id, { size: 32 }));
27
+ }
28
+ const KeelTokenLimitTuple = [
29
+ { name: 'token', type: 'address' },
30
+ { name: 'maxPerTx', type: 'uint256' },
31
+ { name: 'maxDaily', type: 'uint256' },
32
+ ];
33
+ const KeelRuleTuple = [
34
+ { name: 'target', type: 'address' },
35
+ { name: 'selectors', type: 'bytes4[]' },
36
+ { name: 'maxPerTx', type: 'uint256' },
37
+ { name: 'maxDaily', type: 'uint256' },
38
+ { name: 'maxCalls', type: 'uint256' },
39
+ { name: 'tokenLimits', type: 'tuple[]', components: KeelTokenLimitTuple },
40
+ ];
41
+ /**
42
+ * SessionPermission[] → 链上 Rule[](每条 target 一个 Rule)。
43
+ * tokenLimits 只挂到 `token == target` 的那条 Rule(链上强制同址)。
44
+ */
45
+ function toKeelRules(policy) {
46
+ const rules = [];
47
+ for (const p of policy.permissions) {
48
+ const limits = p.tokenLimits ?? [];
49
+ for (const target of p.targets) {
50
+ rules.push({
51
+ target,
52
+ selectors: [...(p.selectors ?? [])],
53
+ maxPerTx: p.valueLimit ?? 0n,
54
+ maxDaily: p.dailyLimit ?? 0n,
55
+ maxCalls: BigInt(p.countLimit ?? 0),
56
+ tokenLimits: limits
57
+ .filter((t) => t.token.toLowerCase() === target.toLowerCase())
58
+ .map((t) => ({ token: t.token, maxPerTx: t.maxPerTx, maxDaily: t.maxDaily })),
59
+ });
60
+ }
61
+ }
62
+ if (rules.length === 0) {
63
+ throw new Error('[keel-sdk] session policy needs at least one target');
64
+ }
65
+ return rules;
66
+ }
67
+ /** `policyData = abi.encode(version, validAfter, validUntil, Rule[])` */
68
+ function encodeKeelPolicyData(policy) {
69
+ return (0, viem_1.encodeAbiParameters)([
70
+ { type: 'uint256' },
71
+ { type: 'uint256' },
72
+ { type: 'uint256' },
73
+ { type: 'tuple[]', components: KeelRuleTuple },
74
+ ], [exports.KEEL_POLICY_VERSION, policy.validAfter, policy.validUntil, toKeelRules(policy)]);
75
+ }
76
+ /** 解析 slot 对应的 validator 副本地址(缺配置直接抛错,不回退旧模块)。 */
77
+ function resolveKeelValidator(chainConfig, slot) {
78
+ if (!Number.isInteger(slot) || slot < 0) {
79
+ throw new Error(`[keel-sdk] invalid slot: ${slot}`);
80
+ }
81
+ const copy = chainConfig.sessionValidatorCopies?.[slot];
82
+ if (copy)
83
+ return copy;
84
+ throw new Error(`[keel-sdk] no keel session validator for slot ${slot}`);
85
+ }
86
+ /** agent 侧签名对象:digest = keccak256(userOpHash ‖ keccak256(policyData)) */
87
+ function keelSessionDigest(userOpHash, policyData) {
88
+ return (0, viem_1.keccak256)((0, viem_1.concatHex)([userOpHash, (0, viem_1.keccak256)(policyData)]));
89
+ }
90
+ /** agent 侧 userOp.signature 组装:sessionId(32B) ‖ policyData ‖ sessionKeySig(65B)。 */
91
+ function encodeKeelSessionSignature(p) {
92
+ return (0, viem_1.concatHex)([toBytes32(p.sessionId), p.policyData, p.sig]);
93
+ }
94
+ /** EntryPoint v0.7 getNonce(address,uint192) */
95
+ const ENTRYPOINT_GETNONCE_ABI = (0, viem_1.parseAbi)([
96
+ 'function getNonce(address sender, uint192 key) view returns (uint256)',
97
+ ]);
98
+ /**
99
+ * v1(Keel)agent 侧调用 UserOp 构造(已签名):
100
+ * nonce = 预分配整值,或 `EntryPoint.getNonce(account, validatorNonceKey(validator, agentIndex))`
101
+ * callData = `executeUserOp( execute(target,value,data) )` —— 挂真 hook 时必需
102
+ * signature= sessionId ‖ policyData ‖ agentSig(keccak256(userOpHash ‖ keccak256(policyData)))
103
+ *
104
+ * 与 aa-sdk 的 `buildKeelSessionUserOp` 逐字节一致(同输入同输出)。
105
+ */
106
+ async function buildKeelSessionUserOp(p) {
107
+ const validator = resolveKeelValidator(p.chainConfig, p.slot);
108
+ // agentIndex(通道号)落在 nonceKey 低 16 位(uint16)⇒ 合法 0..65535;
109
+ // 超界会被 ValidatorLib 静默截断到别的通道,故在 SDK 边界显式拒绝。
110
+ const agentIndex = p.agentIndex ?? 0;
111
+ if (BigInt(agentIndex) < 0n || BigInt(agentIndex) > 65535n) {
112
+ throw new Error(`[keel-sdk] buildKeelSessionUserOp: agentIndex 必须在 0..65535(uint16 通道号),got ${agentIndex}`);
113
+ }
114
+ let nonce = p.nonce;
115
+ if (nonce === undefined) {
116
+ if (!p.client) {
117
+ throw new Error('[keel-sdk] buildKeelSessionUserOp: 未传 nonce 时必须提供 client(用于读链上 getNonce)');
118
+ }
119
+ nonce = (await p.client.readContract({
120
+ address: p.chainConfig.entryPoint,
121
+ abi: ENTRYPOINT_GETNONCE_ABI,
122
+ functionName: 'getNonce',
123
+ args: [p.account, (0, nonce_1.validatorNonceKey)(validator, agentIndex)],
124
+ }));
125
+ }
126
+ const callData = (0, userop_1.encodeExecuteUserOp)((0, userop_1.encodeExecute)(p.call.target, p.call.value ?? 0n, p.call.data));
127
+ const op = (0, userop_1.buildUserOpWithCallData)({ sender: p.account, nonce, callData, gas: p.gas });
128
+ const userOpHash = (0, userop_1.getUserOpHash)(op, p.chainConfig.entryPoint, p.chainConfig.chainId);
129
+ const policyData = encodeKeelPolicyData(p.policy);
130
+ const sig = await p.agentSigner.signUserOp(keelSessionDigest(userOpHash, policyData));
131
+ return { ...op, signature: encodeKeelSessionSignature({ sessionId: p.policy.sessionId, policyData, sig }) };
132
+ }
@@ -0,0 +1,8 @@
1
+ import type { Address } from 'viem';
2
+ export declare const VALIDATION_MODE_DEFAULT = 0;
3
+ export declare const VALIDATION_MODE_ENABLE = 1;
4
+ export declare const VALIDATION_TYPE_ROOT = 0;
5
+ export declare const VALIDATION_TYPE_VALIDATOR = 1;
6
+ export declare function encodeAsNonceKey(mode: number, vType: number, validator: Address, nonceKey?: number | bigint): bigint;
7
+ /** 已安装 validator 的常规 UserOp(agent session key 走此通道)。 */
8
+ export declare function validatorNonceKey(validator: Address, nonceKey?: number | bigint): bigint;
@@ -0,0 +1,22 @@
1
+ "use strict";
2
+ // ---------------------------------------------------------------------------
3
+ // Kernel v3 nonce key 编码(ValidatorLib.encodeAsNonceKey)
4
+ //
5
+ // EntryPoint nonce = nonceKey << 64 | sequence;Kernel 用 nonceKey 路由到
6
+ // 具体 validator:key = (mode << 184) | (vType << 176) | (validator << 16) | nonceKey
7
+ // ---------------------------------------------------------------------------
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.VALIDATION_TYPE_VALIDATOR = exports.VALIDATION_TYPE_ROOT = exports.VALIDATION_MODE_ENABLE = exports.VALIDATION_MODE_DEFAULT = void 0;
10
+ exports.encodeAsNonceKey = encodeAsNonceKey;
11
+ exports.validatorNonceKey = validatorNonceKey;
12
+ exports.VALIDATION_MODE_DEFAULT = 0;
13
+ exports.VALIDATION_MODE_ENABLE = 1;
14
+ exports.VALIDATION_TYPE_ROOT = 0;
15
+ exports.VALIDATION_TYPE_VALIDATOR = 1;
16
+ function encodeAsNonceKey(mode, vType, validator, nonceKey = 0) {
17
+ return (BigInt(mode) << 184n) | (BigInt(vType) << 176n) | (BigInt(validator) << 16n) | BigInt(nonceKey);
18
+ }
19
+ /** 已安装 validator 的常规 UserOp(agent session key 走此通道)。 */
20
+ function validatorNonceKey(validator, nonceKey = 0) {
21
+ return encodeAsNonceKey(exports.VALIDATION_MODE_DEFAULT, exports.VALIDATION_TYPE_VALIDATOR, validator, nonceKey);
22
+ }
@@ -0,0 +1,3 @@
1
+ import type { Hex } from 'viem';
2
+ import type { Signer } from './types';
3
+ export declare function privateKeySigner(privateKey: Hex): Signer;
@@ -0,0 +1,16 @@
1
+ "use strict";
2
+ // ---------------------------------------------------------------------------
3
+ // 自持私钥签名器(最简单接入形态;MPC / 外部钱包请自行实现 Signer)
4
+ // ---------------------------------------------------------------------------
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.privateKeySigner = privateKeySigner;
7
+ const accounts_1 = require("viem/accounts");
8
+ function privateKeySigner(privateKey) {
9
+ const account = (0, accounts_1.privateKeyToAccount)(privateKey);
10
+ return {
11
+ type: 'private-key',
12
+ address: account.address,
13
+ signUserOp: (hash) => account.sign({ hash }),
14
+ signMessage: (message) => account.signMessage({ message: { raw: message } }),
15
+ };
16
+ }
@@ -0,0 +1,9 @@
1
+ import { type Address, type Hex } from 'viem';
2
+ /**
3
+ * token 计费分支的 `paymasterData`:`tokenAddress`(可选再追 32B 客户端报价)。
4
+ * 客户端报价只有比链上价更保守时才会被采用(不会多扣)。
5
+ */
6
+ export declare function buildTokenPaymasterData(args: {
7
+ token: Address;
8
+ clientPrice?: bigint;
9
+ }): Hex;
@@ -0,0 +1,18 @@
1
+ "use strict";
2
+ // ---------------------------------------------------------------------------
3
+ // KeelTokenPaymaster 客户端数据编码
4
+ // ---------------------------------------------------------------------------
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.buildTokenPaymasterData = buildTokenPaymasterData;
7
+ const viem_1 = require("viem");
8
+ /**
9
+ * token 计费分支的 `paymasterData`:`tokenAddress`(可选再追 32B 客户端报价)。
10
+ * 客户端报价只有比链上价更保守时才会被采用(不会多扣)。
11
+ */
12
+ function buildTokenPaymasterData(args) {
13
+ if (args.clientPrice == null)
14
+ return args.token;
15
+ if (args.clientPrice <= 0n)
16
+ throw new Error('[keel-sdk] clientPrice must be > 0');
17
+ return (0, viem_1.concatHex)([args.token, (0, viem_1.toHex)(args.clientPrice, { size: 32 })]);
18
+ }
@@ -0,0 +1,92 @@
1
+ import type { Address, Hex } from 'viem';
2
+ export type EvmNetwork = 'evm';
3
+ export interface BundlerConfig {
4
+ url: string;
5
+ /** 0 = 主端点(缺省) */
6
+ priority?: number;
7
+ headers?: Record<string, string>;
8
+ timeoutMs?: number;
9
+ }
10
+ /** 链配置:B 端可用 `keelChainConfig()` 取内置三链默认,再按需覆盖。 */
11
+ export interface ChainAAConfig {
12
+ chainId: number;
13
+ /** EntryPoint v0.7 */
14
+ entryPoint: Address;
15
+ rpcUrl?: string;
16
+ /** Kernel 版本(缺省 '0.3.1') */
17
+ kernelVersion?: string;
18
+ /** Keel(v1) session validator 副本(slot i → 第 i 个;append-only) */
19
+ sessionValidatorCopies?: Address[];
20
+ /** Keel(v1) policy hook 副本(与 validator 成对) */
21
+ sessionHookCopies?: Address[];
22
+ bundlers?: BundlerConfig[];
23
+ }
24
+ /** 统一签名器(自持私钥 / MPC / 外部钱包)。 */
25
+ export interface Signer {
26
+ readonly type: 'private-key' | 'mpc' | 'external-wallet';
27
+ readonly address: Address;
28
+ /** 对 UserOp hash(或等价的 EIP-712 摘要)签名,返回 65 字节 ECDSA */
29
+ signUserOp(hash: Hex): Promise<Hex>;
30
+ /** 对任意消息签名(EIP-191) */
31
+ signMessage(message: Hex): Promise<Hex>;
32
+ }
33
+ /** ERC-20 金额级限额(单笔 / 日累计;0 = 不限) */
34
+ export interface TokenLimit {
35
+ token: Address;
36
+ maxPerTx: bigint;
37
+ maxDaily: bigint;
38
+ }
39
+ /** 单条策略(target 维度) */
40
+ export interface SessionPermission {
41
+ targets: Address[];
42
+ selectors?: Hex[];
43
+ valueLimit?: bigint;
44
+ countLimit?: number;
45
+ dailyLimit?: bigint;
46
+ tokenLimits?: TokenLimit[];
47
+ }
48
+ /** session 策略:链上 `policyData` 的唯一来源(须与 enable 时逐字节一致)。 */
49
+ export interface SessionPolicy {
50
+ network?: EvmNetwork;
51
+ sessionId: string;
52
+ signer: Address;
53
+ validUntil: bigint;
54
+ validAfter: bigint;
55
+ permissions: SessionPermission[];
56
+ }
57
+ /** ERC-4337 v0.7 UserOperation(非打包形态,bundler RPC 字段) */
58
+ export interface UserOperationV7 {
59
+ sender: Address;
60
+ nonce: bigint;
61
+ factory?: Address;
62
+ factoryData?: Hex;
63
+ callData: Hex;
64
+ callGasLimit: bigint;
65
+ verificationGasLimit: bigint;
66
+ preVerificationGas: bigint;
67
+ maxFeePerGas: bigint;
68
+ maxPriorityFeePerGas: bigint;
69
+ paymaster?: Address;
70
+ paymasterVerificationGasLimit?: bigint;
71
+ paymasterPostOpGasLimit?: bigint;
72
+ paymasterData?: Hex;
73
+ signature: Hex;
74
+ }
75
+ export interface UserOpReceipt {
76
+ txHash?: Hex;
77
+ success: boolean;
78
+ actualGasCost: bigint;
79
+ actualGasUsed: bigint;
80
+ logs: unknown[];
81
+ }
82
+ export interface UserOpResult {
83
+ userOpHash: Hex;
84
+ bundlerUrl: string;
85
+ /** 轮询到的收据(可能为 null = 未确认) */
86
+ receipt?: UserOpReceipt;
87
+ }
88
+ /** payX402WithSession 所需的最小 bundler 能力(HTTP 实现见 ./bundler)。 */
89
+ export interface BundlerLike {
90
+ estimateUserOperationGas(op: UserOperationV7): Promise<Partial<Pick<UserOperationV7, 'callGasLimit' | 'verificationGasLimit' | 'preVerificationGas'>>>;
91
+ sendUserOperation(op: UserOperationV7): Promise<UserOpResult>;
92
+ }
@@ -0,0 +1,8 @@
1
+ "use strict";
2
+ // ---------------------------------------------------------------------------
3
+ // @keelcodes/sdk — AA 客户端共享类型(EVM / Kernel v3 / ERC-4337 v0.7)
4
+ //
5
+ // 这里只保留「客户端自己签 UserOp + 自己组 x402 凭证」所需的最小形状,
6
+ // 与 @0xinfrax/aa-sdk 的 ChainAAConfig / SessionPolicy 逐字节兼容。
7
+ // ---------------------------------------------------------------------------
8
+ Object.defineProperty(exports, "__esModule", { value: true });