@a3stack/accounts 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,64 @@
1
+ /**
2
+ * Constants for @a3stack/accounts
3
+ */
4
+ /** ERC-8004 Identity Registry — same address on all chains */
5
+ export declare const REGISTRY_ADDRESS: "0x8004A169FB4a3325136EB29fA0ceB6D2e539a432";
6
+ /** ERC-8004 registration type URI */
7
+ export declare const REGISTRATION_TYPE = "https://eips.ethereum.org/EIPS/eip-8004#registration-v1";
8
+ /** Zero address */
9
+ export declare const ZERO_ADDRESS: "0x0000000000000000000000000000000000000000";
10
+ /** Minimal ABI for ERC-8004 register + Transfer event */
11
+ export declare const REGISTRY_ABI: readonly [{
12
+ readonly name: "register";
13
+ readonly type: "function";
14
+ readonly stateMutability: "nonpayable";
15
+ readonly inputs: readonly [{
16
+ readonly name: "agentURI";
17
+ readonly type: "string";
18
+ }];
19
+ readonly outputs: readonly [{
20
+ readonly name: "";
21
+ readonly type: "uint256";
22
+ }];
23
+ }, {
24
+ readonly name: "setAgentURI";
25
+ readonly type: "function";
26
+ readonly stateMutability: "nonpayable";
27
+ readonly inputs: readonly [{
28
+ readonly name: "agentId";
29
+ readonly type: "uint256";
30
+ }, {
31
+ readonly name: "newURI";
32
+ readonly type: "string";
33
+ }];
34
+ readonly outputs: readonly [];
35
+ }, {
36
+ readonly name: "balanceOf";
37
+ readonly type: "function";
38
+ readonly stateMutability: "view";
39
+ readonly inputs: readonly [{
40
+ readonly name: "owner";
41
+ readonly type: "address";
42
+ }];
43
+ readonly outputs: readonly [{
44
+ readonly name: "";
45
+ readonly type: "uint256";
46
+ }];
47
+ }, {
48
+ readonly name: "Transfer";
49
+ readonly type: "event";
50
+ readonly inputs: readonly [{
51
+ readonly name: "from";
52
+ readonly type: "address";
53
+ readonly indexed: true;
54
+ }, {
55
+ readonly name: "to";
56
+ readonly type: "address";
57
+ readonly indexed: true;
58
+ }, {
59
+ readonly name: "tokenId";
60
+ readonly type: "uint256";
61
+ readonly indexed: true;
62
+ }];
63
+ }];
64
+ //# sourceMappingURL=constants.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,8DAA8D;AAC9D,eAAO,MAAM,gBAAgB,EAAG,4CAAqD,CAAC;AAEtF,qCAAqC;AACrC,eAAO,MAAM,iBAAiB,4DAA4D,CAAC;AAE3F,mBAAmB;AACnB,eAAO,MAAM,YAAY,EAAG,4CAAqD,CAAC;AAElF,yDAAyD;AACzD,eAAO,MAAM,YAAY;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAkCf,CAAC"}
@@ -0,0 +1,46 @@
1
+ /**
2
+ * Constants for @a3stack/accounts
3
+ */
4
+ /** ERC-8004 Identity Registry — same address on all chains */
5
+ export const REGISTRY_ADDRESS = "0x8004A169FB4a3325136EB29fA0ceB6D2e539a432";
6
+ /** ERC-8004 registration type URI */
7
+ export const REGISTRATION_TYPE = "https://eips.ethereum.org/EIPS/eip-8004#registration-v1";
8
+ /** Zero address */
9
+ export const ZERO_ADDRESS = "0x0000000000000000000000000000000000000000";
10
+ /** Minimal ABI for ERC-8004 register + Transfer event */
11
+ export const REGISTRY_ABI = [
12
+ {
13
+ name: "register",
14
+ type: "function",
15
+ stateMutability: "nonpayable",
16
+ inputs: [{ name: "agentURI", type: "string" }],
17
+ outputs: [{ name: "", type: "uint256" }],
18
+ },
19
+ {
20
+ name: "setAgentURI",
21
+ type: "function",
22
+ stateMutability: "nonpayable",
23
+ inputs: [
24
+ { name: "agentId", type: "uint256" },
25
+ { name: "newURI", type: "string" },
26
+ ],
27
+ outputs: [],
28
+ },
29
+ {
30
+ name: "balanceOf",
31
+ type: "function",
32
+ stateMutability: "view",
33
+ inputs: [{ name: "owner", type: "address" }],
34
+ outputs: [{ name: "", type: "uint256" }],
35
+ },
36
+ {
37
+ name: "Transfer",
38
+ type: "event",
39
+ inputs: [
40
+ { name: "from", type: "address", indexed: true },
41
+ { name: "to", type: "address", indexed: true },
42
+ { name: "tokenId", type: "uint256", indexed: true },
43
+ ],
44
+ },
45
+ ];
46
+ //# sourceMappingURL=constants.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constants.js","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,8DAA8D;AAC9D,MAAM,CAAC,MAAM,gBAAgB,GAAG,4CAAqD,CAAC;AAEtF,qCAAqC;AACrC,MAAM,CAAC,MAAM,iBAAiB,GAAG,yDAAyD,CAAC;AAE3F,mBAAmB;AACnB,MAAM,CAAC,MAAM,YAAY,GAAG,4CAAqD,CAAC;AAElF,yDAAyD;AACzD,MAAM,CAAC,MAAM,YAAY,GAAG;IAC1B;QACE,IAAI,EAAE,UAAU;QAChB,IAAI,EAAE,UAAU;QAChB,eAAe,EAAE,YAAY;QAC7B,MAAM,EAAE,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC;QAC9C,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC;KACzC;IACD;QACE,IAAI,EAAE,aAAa;QACnB,IAAI,EAAE,UAAU;QAChB,eAAe,EAAE,YAAY;QAC7B,MAAM,EAAE;YACN,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,SAAS,EAAE;YACpC,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,QAAQ,EAAE;SACnC;QACD,OAAO,EAAE,EAAE;KACZ;IACD;QACE,IAAI,EAAE,WAAW;QACjB,IAAI,EAAE,UAAU;QAChB,eAAe,EAAE,MAAM;QACvB,MAAM,EAAE,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC;QAC5C,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC;KACzC;IACD;QACE,IAAI,EAAE,UAAU;QAChB,IAAI,EAAE,OAAO;QACb,MAAM,EAAE;YACN,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,IAAI,EAAE;YAChD,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,IAAI,EAAE;YAC9C,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,IAAI,EAAE;SACpD;KACF;CACO,CAAC"}
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Convenience function for one-shot gasless registration.
3
+ *
4
+ * For more control, use createAgentAccount() directly.
5
+ */
6
+ import type { CdpCredentials, RegisterOptions, RegisterResult } from "./types.js";
7
+ /**
8
+ * Register an agent on ERC-8004 with zero gas cost (one-shot).
9
+ *
10
+ * Creates a smart account and registers in a single call.
11
+ *
12
+ * @example
13
+ * ```ts
14
+ * import { gaslessRegister } from "@a3stack/accounts";
15
+ *
16
+ * const result = await gaslessRegister(
17
+ * {
18
+ * apiKeyId: process.env.CDP_API_KEY_ID!,
19
+ * apiKeySecret: process.env.CDP_API_KEY_SECRET!,
20
+ * walletSecret: process.env.CDP_WALLET_SECRET!,
21
+ * },
22
+ * {
23
+ * name: "my-agent",
24
+ * description: "An AI agent that does cool things",
25
+ * services: [{ type: "website", url: "https://myagent.ai" }],
26
+ * }
27
+ * );
28
+ *
29
+ * console.log(`Registered at ${result.smartAccountAddress}`);
30
+ * console.log(`Tx: ${result.transactionHash}`);
31
+ * console.log(`Gas cost: $${result.gasCost}`); // $0
32
+ * ```
33
+ */
34
+ export declare function gaslessRegister(credentials: CdpCredentials, options: RegisterOptions & {
35
+ accountName?: string;
36
+ testnet?: boolean;
37
+ }): Promise<RegisterResult>;
38
+ //# sourceMappingURL=gasless-register.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gasless-register.d.ts","sourceRoot":"","sources":["../src/gasless-register.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,KAAK,EAAE,cAAc,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAElF;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,wBAAsB,eAAe,CACnC,WAAW,EAAE,cAAc,EAC3B,OAAO,EAAE,eAAe,GAAG;IAAE,WAAW,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,OAAO,CAAA;CAAE,GACrE,OAAO,CAAC,cAAc,CAAC,CAOzB"}
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Convenience function for one-shot gasless registration.
3
+ *
4
+ * For more control, use createAgentAccount() directly.
5
+ */
6
+ import { createAgentAccount } from "./smart-account.js";
7
+ /**
8
+ * Register an agent on ERC-8004 with zero gas cost (one-shot).
9
+ *
10
+ * Creates a smart account and registers in a single call.
11
+ *
12
+ * @example
13
+ * ```ts
14
+ * import { gaslessRegister } from "@a3stack/accounts";
15
+ *
16
+ * const result = await gaslessRegister(
17
+ * {
18
+ * apiKeyId: process.env.CDP_API_KEY_ID!,
19
+ * apiKeySecret: process.env.CDP_API_KEY_SECRET!,
20
+ * walletSecret: process.env.CDP_WALLET_SECRET!,
21
+ * },
22
+ * {
23
+ * name: "my-agent",
24
+ * description: "An AI agent that does cool things",
25
+ * services: [{ type: "website", url: "https://myagent.ai" }],
26
+ * }
27
+ * );
28
+ *
29
+ * console.log(`Registered at ${result.smartAccountAddress}`);
30
+ * console.log(`Tx: ${result.transactionHash}`);
31
+ * console.log(`Gas cost: $${result.gasCost}`); // $0
32
+ * ```
33
+ */
34
+ export async function gaslessRegister(credentials, options) {
35
+ const agent = await createAgentAccount(credentials, {
36
+ name: options.accountName ?? `agent-${options.name.toLowerCase().replace(/\s+/g, "-")}`,
37
+ testnet: options.testnet,
38
+ });
39
+ return agent.register(options);
40
+ }
41
+ //# sourceMappingURL=gasless-register.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gasless-register.js","sourceRoot":"","sources":["../src/gasless-register.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AAGxD;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe,CACnC,WAA2B,EAC3B,OAAsE;IAEtE,MAAM,KAAK,GAAG,MAAM,kBAAkB,CAAC,WAAW,EAAE;QAClD,IAAI,EAAE,OAAO,CAAC,WAAW,IAAI,SAAS,OAAO,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE;QACvF,OAAO,EAAE,OAAO,CAAC,OAAO;KACzB,CAAC,CAAC;IAEH,OAAO,KAAK,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;AACjC,CAAC"}
@@ -0,0 +1,28 @@
1
+ /**
2
+ * @a3stack/accounts — Gasless smart accounts for AI agents
3
+ *
4
+ * Create ERC-4337 smart accounts with automatic gas sponsorship
5
+ * via CDP Paymaster. Register agents on ERC-8004 with zero gas cost.
6
+ *
7
+ * @example
8
+ * ```ts
9
+ * import { createAgentAccount } from "@a3stack/accounts";
10
+ *
11
+ * const agent = await createAgentAccount({
12
+ * apiKeyId: process.env.CDP_API_KEY_ID!,
13
+ * apiKeySecret: process.env.CDP_API_KEY_SECRET!,
14
+ * walletSecret: process.env.CDP_WALLET_SECRET!,
15
+ * });
16
+ *
17
+ * // Register on-chain with zero gas
18
+ * const result = await agent.register({
19
+ * name: "My Agent",
20
+ * description: "Does cool stuff",
21
+ * });
22
+ * ```
23
+ */
24
+ export { createAgentAccount } from "./smart-account.js";
25
+ export { gaslessRegister } from "./gasless-register.js";
26
+ export { REGISTRY_ADDRESS, REGISTRY_ABI, REGISTRATION_TYPE } from "./constants.js";
27
+ export type { CdpCredentials, CreateAgentAccountOptions, AgentAccount, RegisterOptions, RegisterResult, UserOperationCall, UserOperationResult, } from "./types.js";
28
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,OAAO,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AACxD,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACxD,OAAO,EAAE,gBAAgB,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AACnF,YAAY,EACV,cAAc,EACd,yBAAyB,EACzB,YAAY,EACZ,eAAe,EACf,cAAc,EACd,iBAAiB,EACjB,mBAAmB,GACpB,MAAM,YAAY,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,27 @@
1
+ /**
2
+ * @a3stack/accounts — Gasless smart accounts for AI agents
3
+ *
4
+ * Create ERC-4337 smart accounts with automatic gas sponsorship
5
+ * via CDP Paymaster. Register agents on ERC-8004 with zero gas cost.
6
+ *
7
+ * @example
8
+ * ```ts
9
+ * import { createAgentAccount } from "@a3stack/accounts";
10
+ *
11
+ * const agent = await createAgentAccount({
12
+ * apiKeyId: process.env.CDP_API_KEY_ID!,
13
+ * apiKeySecret: process.env.CDP_API_KEY_SECRET!,
14
+ * walletSecret: process.env.CDP_WALLET_SECRET!,
15
+ * });
16
+ *
17
+ * // Register on-chain with zero gas
18
+ * const result = await agent.register({
19
+ * name: "My Agent",
20
+ * description: "Does cool stuff",
21
+ * });
22
+ * ```
23
+ */
24
+ export { createAgentAccount } from "./smart-account.js";
25
+ export { gaslessRegister } from "./gasless-register.js";
26
+ export { REGISTRY_ADDRESS, REGISTRY_ABI, REGISTRATION_TYPE } from "./constants.js";
27
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,OAAO,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AACxD,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACxD,OAAO,EAAE,gBAAgB,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC"}
@@ -0,0 +1,35 @@
1
+ /**
2
+ * CDP Smart Account wrapper for gasless agent operations
3
+ *
4
+ * Uses Coinbase Developer Platform SDK for:
5
+ * - Server wallet management (EOA owner)
6
+ * - Smart account creation (ERC-4337)
7
+ * - Automatic gas sponsorship via CDP Paymaster on Base
8
+ */
9
+ import type { CdpCredentials, CreateAgentAccountOptions, AgentAccount } from "./types.js";
10
+ /**
11
+ * Create a gasless smart account for an AI agent.
12
+ *
13
+ * The account is an ERC-4337 smart account on Base, owned by a CDP-managed
14
+ * EOA. All transactions are gas-sponsored by the CDP Paymaster.
15
+ *
16
+ * @example
17
+ * ```ts
18
+ * import { createAgentAccount } from "@a3stack/accounts";
19
+ *
20
+ * const agent = await createAgentAccount({
21
+ * apiKeyId: process.env.CDP_API_KEY_ID!,
22
+ * apiKeySecret: process.env.CDP_API_KEY_SECRET!,
23
+ * walletSecret: process.env.CDP_WALLET_SECRET!,
24
+ * }, { name: "my-agent" });
25
+ *
26
+ * // Register on ERC-8004 with zero gas
27
+ * const result = await agent.register({
28
+ * name: "My Agent",
29
+ * description: "An autonomous AI agent",
30
+ * });
31
+ * console.log(`Registered! Tx: ${result.transactionHash}`);
32
+ * ```
33
+ */
34
+ export declare function createAgentAccount(credentials: CdpCredentials, options?: CreateAgentAccountOptions): Promise<AgentAccount>;
35
+ //# sourceMappingURL=smart-account.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"smart-account.d.ts","sourceRoot":"","sources":["../src/smart-account.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAKH,OAAO,KAAK,EACV,cAAc,EACd,yBAAyB,EACzB,YAAY,EAKb,MAAM,YAAY,CAAC;AAEpB;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAsB,kBAAkB,CACtC,WAAW,EAAE,cAAc,EAC3B,OAAO,GAAE,yBAA8B,GACtC,OAAO,CAAC,YAAY,CAAC,CA6FvB"}
@@ -0,0 +1,115 @@
1
+ /**
2
+ * CDP Smart Account wrapper for gasless agent operations
3
+ *
4
+ * Uses Coinbase Developer Platform SDK for:
5
+ * - Server wallet management (EOA owner)
6
+ * - Smart account creation (ERC-4337)
7
+ * - Automatic gas sponsorship via CDP Paymaster on Base
8
+ */
9
+ import { CdpClient } from "@coinbase/cdp-sdk";
10
+ import { encodeFunctionData } from "viem";
11
+ import { REGISTRY_ADDRESS, REGISTRY_ABI, REGISTRATION_TYPE } from "./constants.js";
12
+ /**
13
+ * Create a gasless smart account for an AI agent.
14
+ *
15
+ * The account is an ERC-4337 smart account on Base, owned by a CDP-managed
16
+ * EOA. All transactions are gas-sponsored by the CDP Paymaster.
17
+ *
18
+ * @example
19
+ * ```ts
20
+ * import { createAgentAccount } from "@a3stack/accounts";
21
+ *
22
+ * const agent = await createAgentAccount({
23
+ * apiKeyId: process.env.CDP_API_KEY_ID!,
24
+ * apiKeySecret: process.env.CDP_API_KEY_SECRET!,
25
+ * walletSecret: process.env.CDP_WALLET_SECRET!,
26
+ * }, { name: "my-agent" });
27
+ *
28
+ * // Register on ERC-8004 with zero gas
29
+ * const result = await agent.register({
30
+ * name: "My Agent",
31
+ * description: "An autonomous AI agent",
32
+ * });
33
+ * console.log(`Registered! Tx: ${result.transactionHash}`);
34
+ * ```
35
+ */
36
+ export async function createAgentAccount(credentials, options = {}) {
37
+ const { apiKeyId, apiKeySecret, walletSecret } = credentials;
38
+ const network = options.testnet ? "base-sepolia" : "base";
39
+ const accountName = options.name ?? "a3stack-agent";
40
+ // Initialize CDP client
41
+ const cdp = new CdpClient({ apiKeyId, apiKeySecret, walletSecret });
42
+ // Create or retrieve the owner EOA
43
+ const owner = await cdp.evm.getOrCreateAccount({
44
+ name: `${accountName}-owner`,
45
+ });
46
+ // Create or retrieve the smart account
47
+ const smartAccount = await cdp.evm.getOrCreateSmartAccount({
48
+ name: accountName,
49
+ owner,
50
+ });
51
+ // Scope to Base network
52
+ const scopedAccount = await smartAccount.useNetwork(network);
53
+ // Build the AgentAccount interface
54
+ const agentAccount = {
55
+ address: smartAccount.address,
56
+ ownerAddress: owner.address,
57
+ network: network,
58
+ async sendUserOperation(calls) {
59
+ const userOp = await scopedAccount.sendUserOperation({
60
+ calls: calls.map((c) => ({
61
+ to: c.to,
62
+ value: c.value ?? 0n,
63
+ data: c.data ?? "0x",
64
+ })),
65
+ });
66
+ const receipt = await scopedAccount.waitForUserOperation(userOp);
67
+ const txHash = receipt.transactionHash ?? String(receipt);
68
+ return {
69
+ userOpHash: userOp.userOpHash ?? String(userOp),
70
+ transactionHash: txHash,
71
+ };
72
+ },
73
+ async register(opts) {
74
+ // Build the agent registration JSON
75
+ const registration = {
76
+ type: REGISTRATION_TYPE,
77
+ name: opts.name,
78
+ description: opts.description,
79
+ ...(opts.image ? { image: opts.image } : {}),
80
+ services: opts.services ?? [],
81
+ x402Support: opts.x402Support ?? false,
82
+ active: true,
83
+ registrations: opts.existingRegistrations ?? [],
84
+ };
85
+ const json = JSON.stringify(registration);
86
+ const agentURI = `data:application/json;base64,${Buffer.from(json).toString("base64")}`;
87
+ // Encode the register() call
88
+ const callData = encodeFunctionData({
89
+ abi: REGISTRY_ABI,
90
+ functionName: "register",
91
+ args: [agentURI],
92
+ });
93
+ // Send gasless UserOperation
94
+ const userOp = await scopedAccount.sendUserOperation({
95
+ calls: [{
96
+ to: REGISTRY_ADDRESS,
97
+ value: 0n,
98
+ data: callData,
99
+ }],
100
+ });
101
+ const receipt = await scopedAccount.waitForUserOperation(userOp);
102
+ const txHash = receipt.transactionHash ?? String(receipt);
103
+ return {
104
+ transactionHash: txHash,
105
+ userOpHash: userOp.userOpHash ?? String(userOp),
106
+ smartAccountAddress: smartAccount.address,
107
+ network,
108
+ gasCost: 0,
109
+ sponsored: true,
110
+ };
111
+ },
112
+ };
113
+ return agentAccount;
114
+ }
115
+ //# sourceMappingURL=smart-account.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"smart-account.js","sourceRoot":"","sources":["../src/smart-account.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,73 @@
1
+ /**
2
+ * Types for @a3stack/accounts
3
+ */
4
+ export interface CdpCredentials {
5
+ /** CDP API Key ID */
6
+ apiKeyId: string;
7
+ /** CDP API Key Secret */
8
+ apiKeySecret: string;
9
+ /** CDP Wallet Secret (EC P-256 PKCS8 key, base64-encoded) */
10
+ walletSecret: string;
11
+ }
12
+ export interface CreateAgentAccountOptions {
13
+ /** Human-readable name for the account (used for idempotent getOrCreate) */
14
+ name?: string;
15
+ /** Use testnet (Base Sepolia) instead of mainnet */
16
+ testnet?: boolean;
17
+ }
18
+ export interface AgentAccount {
19
+ /** Smart account address (ERC-4337) */
20
+ address: `0x${string}`;
21
+ /** Owner EOA address */
22
+ ownerAddress: `0x${string}`;
23
+ /** The network used */
24
+ network: "base" | "base-sepolia";
25
+ /** Send a gasless UserOperation */
26
+ sendUserOperation: (calls: UserOperationCall[]) => Promise<UserOperationResult>;
27
+ /** Register this agent on ERC-8004 with zero gas */
28
+ register: (options: RegisterOptions) => Promise<RegisterResult>;
29
+ }
30
+ export interface UserOperationCall {
31
+ to: `0x${string}`;
32
+ value?: bigint;
33
+ data?: `0x${string}`;
34
+ }
35
+ export interface UserOperationResult {
36
+ userOpHash: string;
37
+ transactionHash: string;
38
+ }
39
+ export interface RegisterOptions {
40
+ /** Agent name */
41
+ name: string;
42
+ /** Agent description */
43
+ description: string;
44
+ /** Agent image URL (optional) */
45
+ image?: string;
46
+ /** Service endpoints */
47
+ services?: Array<{
48
+ type: string;
49
+ url: string;
50
+ }>;
51
+ /** Whether agent supports x402 payments */
52
+ x402Support?: boolean;
53
+ /** Existing registrations from other chains */
54
+ existingRegistrations?: Array<{
55
+ agentId: number;
56
+ agentRegistry: string;
57
+ }>;
58
+ }
59
+ export interface RegisterResult {
60
+ /** Transaction hash on Base */
61
+ transactionHash: string;
62
+ /** UserOperation hash */
63
+ userOpHash: string;
64
+ /** Smart account address */
65
+ smartAccountAddress: `0x${string}`;
66
+ /** Network used */
67
+ network: string;
68
+ /** Gas cost to user (always 0 with Paymaster) */
69
+ gasCost: number;
70
+ /** Whether gas was sponsored */
71
+ sponsored: boolean;
72
+ }
73
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,MAAM,WAAW,cAAc;IAC7B,qBAAqB;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,yBAAyB;IACzB,YAAY,EAAE,MAAM,CAAC;IACrB,6DAA6D;IAC7D,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,yBAAyB;IACxC,4EAA4E;IAC5E,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,oDAAoD;IACpD,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,WAAW,YAAY;IAC3B,uCAAuC;IACvC,OAAO,EAAE,KAAK,MAAM,EAAE,CAAC;IACvB,wBAAwB;IACxB,YAAY,EAAE,KAAK,MAAM,EAAE,CAAC;IAC5B,uBAAuB;IACvB,OAAO,EAAE,MAAM,GAAG,cAAc,CAAC;IACjC,mCAAmC;IACnC,iBAAiB,EAAE,CAAC,KAAK,EAAE,iBAAiB,EAAE,KAAK,OAAO,CAAC,mBAAmB,CAAC,CAAC;IAChF,oDAAoD;IACpD,QAAQ,EAAE,CAAC,OAAO,EAAE,eAAe,KAAK,OAAO,CAAC,cAAc,CAAC,CAAC;CACjE;AAED,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,KAAK,MAAM,EAAE,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,KAAK,MAAM,EAAE,CAAC;CACtB;AAED,MAAM,WAAW,mBAAmB;IAClC,UAAU,EAAE,MAAM,CAAC;IACnB,eAAe,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,eAAe;IAC9B,iBAAiB;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,wBAAwB;IACxB,WAAW,EAAE,MAAM,CAAC;IACpB,iCAAiC;IACjC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,wBAAwB;IACxB,QAAQ,CAAC,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAChD,2CAA2C;IAC3C,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,+CAA+C;IAC/C,qBAAqB,CAAC,EAAE,KAAK,CAAC;QAC5B,OAAO,EAAE,MAAM,CAAC;QAChB,aAAa,EAAE,MAAM,CAAC;KACvB,CAAC,CAAC;CACJ;AAED,MAAM,WAAW,cAAc;IAC7B,+BAA+B;IAC/B,eAAe,EAAE,MAAM,CAAC;IACxB,yBAAyB;IACzB,UAAU,EAAE,MAAM,CAAC;IACnB,4BAA4B;IAC5B,mBAAmB,EAAE,KAAK,MAAM,EAAE,CAAC;IACnC,mBAAmB;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB,iDAAiD;IACjD,OAAO,EAAE,MAAM,CAAC;IAChB,gCAAgC;IAChC,SAAS,EAAE,OAAO,CAAC;CACpB"}
package/dist/types.js ADDED
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Types for @a3stack/accounts
3
+ */
4
+ export {};
5
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;GAEG"}
package/package.json ADDED
@@ -0,0 +1,42 @@
1
+ {
2
+ "name": "@a3stack/accounts",
3
+ "version": "0.2.0",
4
+ "description": "Gasless smart accounts for ERC-8004 agent registration via CDP Paymaster",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "types": "dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "import": "./dist/index.js",
11
+ "types": "./dist/index.d.ts"
12
+ }
13
+ },
14
+ "scripts": {
15
+ "build": "tsc",
16
+ "typecheck": "tsc --noEmit",
17
+ "dev": "tsc --watch"
18
+ },
19
+ "dependencies": {
20
+ "@coinbase/cdp-sdk": "^1.44.1",
21
+ "viem": "^2.46.2"
22
+ },
23
+ "publishConfig": {
24
+ "access": "public"
25
+ },
26
+ "keywords": [
27
+ "erc-8004",
28
+ "agent",
29
+ "identity",
30
+ "gasless",
31
+ "smart-account",
32
+ "cdp",
33
+ "paymaster",
34
+ "base"
35
+ ],
36
+ "license": "MIT",
37
+ "repository": {
38
+ "type": "git",
39
+ "url": "https://github.com/arcabotai/a3stack",
40
+ "directory": "packages/accounts"
41
+ }
42
+ }
@@ -0,0 +1,49 @@
1
+ /**
2
+ * Constants for @a3stack/accounts
3
+ */
4
+
5
+ /** ERC-8004 Identity Registry — same address on all chains */
6
+ export const REGISTRY_ADDRESS = "0x8004A169FB4a3325136EB29fA0ceB6D2e539a432" as const;
7
+
8
+ /** ERC-8004 registration type URI */
9
+ export const REGISTRATION_TYPE = "https://eips.ethereum.org/EIPS/eip-8004#registration-v1";
10
+
11
+ /** Zero address */
12
+ export const ZERO_ADDRESS = "0x0000000000000000000000000000000000000000" as const;
13
+
14
+ /** Minimal ABI for ERC-8004 register + Transfer event */
15
+ export const REGISTRY_ABI = [
16
+ {
17
+ name: "register",
18
+ type: "function",
19
+ stateMutability: "nonpayable",
20
+ inputs: [{ name: "agentURI", type: "string" }],
21
+ outputs: [{ name: "", type: "uint256" }],
22
+ },
23
+ {
24
+ name: "setAgentURI",
25
+ type: "function",
26
+ stateMutability: "nonpayable",
27
+ inputs: [
28
+ { name: "agentId", type: "uint256" },
29
+ { name: "newURI", type: "string" },
30
+ ],
31
+ outputs: [],
32
+ },
33
+ {
34
+ name: "balanceOf",
35
+ type: "function",
36
+ stateMutability: "view",
37
+ inputs: [{ name: "owner", type: "address" }],
38
+ outputs: [{ name: "", type: "uint256" }],
39
+ },
40
+ {
41
+ name: "Transfer",
42
+ type: "event",
43
+ inputs: [
44
+ { name: "from", type: "address", indexed: true },
45
+ { name: "to", type: "address", indexed: true },
46
+ { name: "tokenId", type: "uint256", indexed: true },
47
+ ],
48
+ },
49
+ ] as const;
@@ -0,0 +1,47 @@
1
+ /**
2
+ * Convenience function for one-shot gasless registration.
3
+ *
4
+ * For more control, use createAgentAccount() directly.
5
+ */
6
+
7
+ import { createAgentAccount } from "./smart-account.js";
8
+ import type { CdpCredentials, RegisterOptions, RegisterResult } from "./types.js";
9
+
10
+ /**
11
+ * Register an agent on ERC-8004 with zero gas cost (one-shot).
12
+ *
13
+ * Creates a smart account and registers in a single call.
14
+ *
15
+ * @example
16
+ * ```ts
17
+ * import { gaslessRegister } from "@a3stack/accounts";
18
+ *
19
+ * const result = await gaslessRegister(
20
+ * {
21
+ * apiKeyId: process.env.CDP_API_KEY_ID!,
22
+ * apiKeySecret: process.env.CDP_API_KEY_SECRET!,
23
+ * walletSecret: process.env.CDP_WALLET_SECRET!,
24
+ * },
25
+ * {
26
+ * name: "my-agent",
27
+ * description: "An AI agent that does cool things",
28
+ * services: [{ type: "website", url: "https://myagent.ai" }],
29
+ * }
30
+ * );
31
+ *
32
+ * console.log(`Registered at ${result.smartAccountAddress}`);
33
+ * console.log(`Tx: ${result.transactionHash}`);
34
+ * console.log(`Gas cost: $${result.gasCost}`); // $0
35
+ * ```
36
+ */
37
+ export async function gaslessRegister(
38
+ credentials: CdpCredentials,
39
+ options: RegisterOptions & { accountName?: string; testnet?: boolean }
40
+ ): Promise<RegisterResult> {
41
+ const agent = await createAgentAccount(credentials, {
42
+ name: options.accountName ?? `agent-${options.name.toLowerCase().replace(/\s+/g, "-")}`,
43
+ testnet: options.testnet,
44
+ });
45
+
46
+ return agent.register(options);
47
+ }
package/src/index.ts ADDED
@@ -0,0 +1,36 @@
1
+ /**
2
+ * @a3stack/accounts — Gasless smart accounts for AI agents
3
+ *
4
+ * Create ERC-4337 smart accounts with automatic gas sponsorship
5
+ * via CDP Paymaster. Register agents on ERC-8004 with zero gas cost.
6
+ *
7
+ * @example
8
+ * ```ts
9
+ * import { createAgentAccount } from "@a3stack/accounts";
10
+ *
11
+ * const agent = await createAgentAccount({
12
+ * apiKeyId: process.env.CDP_API_KEY_ID!,
13
+ * apiKeySecret: process.env.CDP_API_KEY_SECRET!,
14
+ * walletSecret: process.env.CDP_WALLET_SECRET!,
15
+ * });
16
+ *
17
+ * // Register on-chain with zero gas
18
+ * const result = await agent.register({
19
+ * name: "My Agent",
20
+ * description: "Does cool stuff",
21
+ * });
22
+ * ```
23
+ */
24
+
25
+ export { createAgentAccount } from "./smart-account.js";
26
+ export { gaslessRegister } from "./gasless-register.js";
27
+ export { REGISTRY_ADDRESS, REGISTRY_ABI, REGISTRATION_TYPE } from "./constants.js";
28
+ export type {
29
+ CdpCredentials,
30
+ CreateAgentAccountOptions,
31
+ AgentAccount,
32
+ RegisterOptions,
33
+ RegisterResult,
34
+ UserOperationCall,
35
+ UserOperationResult,
36
+ } from "./types.js";
@@ -0,0 +1,143 @@
1
+ /**
2
+ * CDP Smart Account wrapper for gasless agent operations
3
+ *
4
+ * Uses Coinbase Developer Platform SDK for:
5
+ * - Server wallet management (EOA owner)
6
+ * - Smart account creation (ERC-4337)
7
+ * - Automatic gas sponsorship via CDP Paymaster on Base
8
+ */
9
+
10
+ import { CdpClient } from "@coinbase/cdp-sdk";
11
+ import { encodeFunctionData } from "viem";
12
+ import { REGISTRY_ADDRESS, REGISTRY_ABI, REGISTRATION_TYPE } from "./constants.js";
13
+ import type {
14
+ CdpCredentials,
15
+ CreateAgentAccountOptions,
16
+ AgentAccount,
17
+ RegisterOptions,
18
+ RegisterResult,
19
+ UserOperationCall,
20
+ UserOperationResult,
21
+ } from "./types.js";
22
+
23
+ /**
24
+ * Create a gasless smart account for an AI agent.
25
+ *
26
+ * The account is an ERC-4337 smart account on Base, owned by a CDP-managed
27
+ * EOA. All transactions are gas-sponsored by the CDP Paymaster.
28
+ *
29
+ * @example
30
+ * ```ts
31
+ * import { createAgentAccount } from "@a3stack/accounts";
32
+ *
33
+ * const agent = await createAgentAccount({
34
+ * apiKeyId: process.env.CDP_API_KEY_ID!,
35
+ * apiKeySecret: process.env.CDP_API_KEY_SECRET!,
36
+ * walletSecret: process.env.CDP_WALLET_SECRET!,
37
+ * }, { name: "my-agent" });
38
+ *
39
+ * // Register on ERC-8004 with zero gas
40
+ * const result = await agent.register({
41
+ * name: "My Agent",
42
+ * description: "An autonomous AI agent",
43
+ * });
44
+ * console.log(`Registered! Tx: ${result.transactionHash}`);
45
+ * ```
46
+ */
47
+ export async function createAgentAccount(
48
+ credentials: CdpCredentials,
49
+ options: CreateAgentAccountOptions = {}
50
+ ): Promise<AgentAccount> {
51
+ const { apiKeyId, apiKeySecret, walletSecret } = credentials;
52
+ const network = options.testnet ? "base-sepolia" : "base";
53
+ const accountName = options.name ?? "a3stack-agent";
54
+
55
+ // Initialize CDP client
56
+ const cdp = new CdpClient({ apiKeyId, apiKeySecret, walletSecret });
57
+
58
+ // Create or retrieve the owner EOA
59
+ const owner = await cdp.evm.getOrCreateAccount({
60
+ name: `${accountName}-owner`,
61
+ });
62
+
63
+ // Create or retrieve the smart account
64
+ const smartAccount = await cdp.evm.getOrCreateSmartAccount({
65
+ name: accountName,
66
+ owner,
67
+ });
68
+
69
+ // Scope to Base network
70
+ const scopedAccount = await smartAccount.useNetwork(network);
71
+
72
+ // Build the AgentAccount interface
73
+ const agentAccount: AgentAccount = {
74
+ address: smartAccount.address as `0x${string}`,
75
+ ownerAddress: owner.address as `0x${string}`,
76
+ network: network as "base" | "base-sepolia",
77
+
78
+ async sendUserOperation(calls: UserOperationCall[]): Promise<UserOperationResult> {
79
+ const userOp = await scopedAccount.sendUserOperation({
80
+ calls: calls.map((c) => ({
81
+ to: c.to,
82
+ value: c.value ?? 0n,
83
+ data: c.data ?? "0x",
84
+ })),
85
+ });
86
+
87
+ const receipt = await scopedAccount.waitForUserOperation(userOp);
88
+ const txHash = (receipt as any).transactionHash ?? String(receipt);
89
+
90
+ return {
91
+ userOpHash: (userOp as any).userOpHash ?? String(userOp),
92
+ transactionHash: txHash,
93
+ };
94
+ },
95
+
96
+ async register(opts: RegisterOptions): Promise<RegisterResult> {
97
+ // Build the agent registration JSON
98
+ const registration = {
99
+ type: REGISTRATION_TYPE,
100
+ name: opts.name,
101
+ description: opts.description,
102
+ ...(opts.image ? { image: opts.image } : {}),
103
+ services: opts.services ?? [],
104
+ x402Support: opts.x402Support ?? false,
105
+ active: true,
106
+ registrations: opts.existingRegistrations ?? [],
107
+ };
108
+
109
+ const json = JSON.stringify(registration);
110
+ const agentURI = `data:application/json;base64,${Buffer.from(json).toString("base64")}`;
111
+
112
+ // Encode the register() call
113
+ const callData = encodeFunctionData({
114
+ abi: REGISTRY_ABI,
115
+ functionName: "register",
116
+ args: [agentURI],
117
+ });
118
+
119
+ // Send gasless UserOperation
120
+ const userOp = await scopedAccount.sendUserOperation({
121
+ calls: [{
122
+ to: REGISTRY_ADDRESS,
123
+ value: 0n,
124
+ data: callData,
125
+ }],
126
+ });
127
+
128
+ const receipt = await scopedAccount.waitForUserOperation(userOp);
129
+ const txHash = (receipt as any).transactionHash ?? String(receipt);
130
+
131
+ return {
132
+ transactionHash: txHash,
133
+ userOpHash: (userOp as any).userOpHash ?? String(userOp),
134
+ smartAccountAddress: smartAccount.address as `0x${string}`,
135
+ network,
136
+ gasCost: 0,
137
+ sponsored: true,
138
+ };
139
+ },
140
+ };
141
+
142
+ return agentAccount;
143
+ }
package/src/types.ts ADDED
@@ -0,0 +1,76 @@
1
+ /**
2
+ * Types for @a3stack/accounts
3
+ */
4
+
5
+ export interface CdpCredentials {
6
+ /** CDP API Key ID */
7
+ apiKeyId: string;
8
+ /** CDP API Key Secret */
9
+ apiKeySecret: string;
10
+ /** CDP Wallet Secret (EC P-256 PKCS8 key, base64-encoded) */
11
+ walletSecret: string;
12
+ }
13
+
14
+ export interface CreateAgentAccountOptions {
15
+ /** Human-readable name for the account (used for idempotent getOrCreate) */
16
+ name?: string;
17
+ /** Use testnet (Base Sepolia) instead of mainnet */
18
+ testnet?: boolean;
19
+ }
20
+
21
+ export interface AgentAccount {
22
+ /** Smart account address (ERC-4337) */
23
+ address: `0x${string}`;
24
+ /** Owner EOA address */
25
+ ownerAddress: `0x${string}`;
26
+ /** The network used */
27
+ network: "base" | "base-sepolia";
28
+ /** Send a gasless UserOperation */
29
+ sendUserOperation: (calls: UserOperationCall[]) => Promise<UserOperationResult>;
30
+ /** Register this agent on ERC-8004 with zero gas */
31
+ register: (options: RegisterOptions) => Promise<RegisterResult>;
32
+ }
33
+
34
+ export interface UserOperationCall {
35
+ to: `0x${string}`;
36
+ value?: bigint;
37
+ data?: `0x${string}`;
38
+ }
39
+
40
+ export interface UserOperationResult {
41
+ userOpHash: string;
42
+ transactionHash: string;
43
+ }
44
+
45
+ export interface RegisterOptions {
46
+ /** Agent name */
47
+ name: string;
48
+ /** Agent description */
49
+ description: string;
50
+ /** Agent image URL (optional) */
51
+ image?: string;
52
+ /** Service endpoints */
53
+ services?: Array<{ type: string; url: string }>;
54
+ /** Whether agent supports x402 payments */
55
+ x402Support?: boolean;
56
+ /** Existing registrations from other chains */
57
+ existingRegistrations?: Array<{
58
+ agentId: number;
59
+ agentRegistry: string;
60
+ }>;
61
+ }
62
+
63
+ export interface RegisterResult {
64
+ /** Transaction hash on Base */
65
+ transactionHash: string;
66
+ /** UserOperation hash */
67
+ userOpHash: string;
68
+ /** Smart account address */
69
+ smartAccountAddress: `0x${string}`;
70
+ /** Network used */
71
+ network: string;
72
+ /** Gas cost to user (always 0 with Paymaster) */
73
+ gasCost: number;
74
+ /** Whether gas was sponsored */
75
+ sponsored: boolean;
76
+ }
@@ -0,0 +1,121 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Integration test for @a3stack/accounts
4
+ *
5
+ * Tests:
6
+ * 1. Import from compiled dist
7
+ * 2. createAgentAccount() — creates smart account via CDP
8
+ * 3. agent.register() — gasless ERC-8004 registration on Base
9
+ * 4. gaslessRegister() — one-shot convenience function
10
+ *
11
+ * Usage:
12
+ * node test/integration.mjs [--live]
13
+ *
14
+ * Without --live: tests imports and account creation only (no on-chain tx)
15
+ * With --live: executes a real gasless registration on Base mainnet
16
+ */
17
+
18
+ import { execSync } from "child_process";
19
+
20
+ // Import from compiled dist
21
+ const {
22
+ createAgentAccount,
23
+ gaslessRegister,
24
+ REGISTRY_ADDRESS,
25
+ REGISTRY_ABI,
26
+ REGISTRATION_TYPE,
27
+ } = await import("../dist/index.js");
28
+
29
+ const LIVE = process.argv.includes("--live");
30
+
31
+ function keychain(key) {
32
+ try {
33
+ return execSync(`security find-generic-password -s "${key}" -w 2>/dev/null`, {
34
+ encoding: "utf8",
35
+ }).trim();
36
+ } catch {
37
+ return null;
38
+ }
39
+ }
40
+
41
+ function assert(condition, msg) {
42
+ if (!condition) {
43
+ console.error(` ❌ FAIL: ${msg}`);
44
+ process.exit(1);
45
+ }
46
+ console.log(` ✅ ${msg}`);
47
+ }
48
+
49
+ async function main() {
50
+ console.log("🧪 @a3stack/accounts Integration Tests\n");
51
+
52
+ // --- Test 1: Exports ---
53
+ console.log("1️⃣ Exports");
54
+ assert(typeof createAgentAccount === "function", "createAgentAccount is a function");
55
+ assert(typeof gaslessRegister === "function", "gaslessRegister is a function");
56
+ assert(REGISTRY_ADDRESS === "0x8004A169FB4a3325136EB29fA0ceB6D2e539a432", "REGISTRY_ADDRESS correct");
57
+ assert(Array.isArray(REGISTRY_ABI), "REGISTRY_ABI is an array");
58
+ assert(REGISTRATION_TYPE.includes("eip-8004"), "REGISTRATION_TYPE contains eip-8004");
59
+
60
+ // --- Test 2: Credentials ---
61
+ console.log("\n2️⃣ Credentials");
62
+ const credentials = {
63
+ apiKeyId: process.env.CDP_API_KEY_ID || keychain("cdp-api-key-id"),
64
+ apiKeySecret: process.env.CDP_API_KEY_SECRET || keychain("cdp-api-secret"),
65
+ walletSecret: process.env.CDP_WALLET_SECRET || keychain("cdp-wallet-secret"),
66
+ };
67
+ assert(!!credentials.apiKeyId, "CDP API Key ID loaded");
68
+ assert(!!credentials.apiKeySecret, "CDP API Key Secret loaded");
69
+ assert(!!credentials.walletSecret, "CDP Wallet Secret loaded");
70
+
71
+ // --- Test 3: Account creation ---
72
+ console.log("\n3️⃣ Account Creation");
73
+ const testName = `a3stack-test-${Date.now()}`;
74
+ const agent = await createAgentAccount(credentials, { name: testName });
75
+
76
+ assert(agent.address.startsWith("0x"), `Smart account: ${agent.address}`);
77
+ assert(agent.ownerAddress.startsWith("0x"), `Owner EOA: ${agent.ownerAddress}`);
78
+ assert(agent.network === "base", `Network: ${agent.network}`);
79
+ assert(typeof agent.sendUserOperation === "function", "sendUserOperation is a function");
80
+ assert(typeof agent.register === "function", "register is a function");
81
+
82
+ // --- Test 4: Idempotent retrieval ---
83
+ console.log("\n4️⃣ Idempotent Retrieval");
84
+ const agent2 = await createAgentAccount(credentials, { name: testName });
85
+ assert(agent2.address === agent.address, `Same name returns same address: ${agent2.address}`);
86
+ assert(agent2.ownerAddress === agent.ownerAddress, "Same owner address");
87
+
88
+ if (!LIVE) {
89
+ console.log("\n⏭️ Skipping live registration (run with --live to execute on-chain)");
90
+ console.log("\n✅ All tests passed!");
91
+ return;
92
+ }
93
+
94
+ // --- Test 5: Live gasless registration ---
95
+ console.log("\n5️⃣ Live Gasless Registration (Base mainnet)");
96
+ console.log(" ⛽ Sending UserOperation (CDP Paymaster sponsors gas)...");
97
+
98
+ const result = await agent.register({
99
+ name: "A3Stack Test Agent (integration)",
100
+ description: "Integration test for @a3stack/accounts package",
101
+ services: [{ type: "website", url: "https://a3stack.arcabot.ai" }],
102
+ });
103
+
104
+ assert(result.transactionHash.length > 0, `Tx hash: ${result.transactionHash}`);
105
+ assert(result.userOpHash.length > 0, `UserOp hash: ${result.userOpHash}`);
106
+ assert(result.smartAccountAddress === agent.address, "Smart account matches");
107
+ assert(result.network === "base", "Network is base");
108
+ assert(result.gasCost === 0, "Gas cost is $0");
109
+ assert(result.sponsored === true, "Gas was sponsored");
110
+
111
+ console.log(`\n🎉 Live registration successful!`);
112
+ console.log(` BaseScan: https://basescan.org/tx/${result.transactionHash}`);
113
+
114
+ console.log("\n✅ All tests passed!");
115
+ }
116
+
117
+ main().catch((err) => {
118
+ console.error("\n❌ Test failed:", err.message || err);
119
+ if (err.cause) console.error(" Cause:", err.cause);
120
+ process.exit(1);
121
+ });
package/tsconfig.json ADDED
@@ -0,0 +1,21 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "module": "Node16",
5
+ "moduleResolution": "node16",
6
+ "lib": ["ES2022"],
7
+ "strict": true,
8
+ "declaration": true,
9
+ "declarationMap": true,
10
+ "sourceMap": true,
11
+ "outDir": "dist",
12
+ "rootDir": "src",
13
+ "esModuleInterop": true,
14
+ "skipLibCheck": true,
15
+ "resolveJsonModule": true,
16
+ "allowImportingTsExtensions": false,
17
+ "noEmitOnError": false
18
+ },
19
+ "include": ["src/**/*"],
20
+ "exclude": ["node_modules", "dist", "**/*.test.ts"]
21
+ }