gokite-aa-sdk 1.0.10 → 1.0.12

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,20 @@
1
+ /**
2
+ * Buy Content Script
3
+ *
4
+ * This script demonstrates the complete x402 payment flow:
5
+ * 1. Request protected content (receive 402 + PaymentRequirements)
6
+ * 2. Sign EIP-712 transfer authorization
7
+ * 3. Send payment via X-Payment header
8
+ * 4. Receive content (service verifies & settles via facilitator)
9
+ *
10
+ * Usage:
11
+ * npm run buy
12
+ *
13
+ * Environment variables (from .env.local):
14
+ * AA_WALLET - AA wallet address
15
+ * SESSION_ID - Session ID for this agent
16
+ * AGENT_PRIVATE_KEY - Agent private key (signs authorizations)
17
+ * SERVICE_URL - Protected service URL
18
+ * FACILITATOR_URL - x402 facilitator URL (for health check)
19
+ */
20
+ import "dotenv/config";
@@ -0,0 +1,285 @@
1
+ "use strict";
2
+ /**
3
+ * Buy Content Script
4
+ *
5
+ * This script demonstrates the complete x402 payment flow:
6
+ * 1. Request protected content (receive 402 + PaymentRequirements)
7
+ * 2. Sign EIP-712 transfer authorization
8
+ * 3. Send payment via X-Payment header
9
+ * 4. Receive content (service verifies & settles via facilitator)
10
+ *
11
+ * Usage:
12
+ * npm run buy
13
+ *
14
+ * Environment variables (from .env.local):
15
+ * AA_WALLET - AA wallet address
16
+ * SESSION_ID - Session ID for this agent
17
+ * AGENT_PRIVATE_KEY - Agent private key (signs authorizations)
18
+ * SERVICE_URL - Protected service URL
19
+ * FACILITATOR_URL - x402 facilitator URL (for health check)
20
+ */
21
+ Object.defineProperty(exports, "__esModule", { value: true });
22
+ const ethers_1 = require("ethers");
23
+ require("dotenv/config");
24
+ // =============================================================================
25
+ // Configuration
26
+ // =============================================================================
27
+ const CHAIN_ID = 2368n;
28
+ const RPC_URL = "https://rpc-testnet.gokite.ai";
29
+ // From environment
30
+ const AA_WALLET = process.env.AA_WALLET;
31
+ const SESSION_ID = process.env.SESSION_ID;
32
+ const AGENT_PRIVATE_KEY = process.env.AGENT_PRIVATE_KEY;
33
+ const SERVICE_URL = process.env.SERVICE_URL || "http://localhost:8099";
34
+ const FACILITATOR_URL = process.env.FACILITATOR_URL || "http://localhost:8080";
35
+ // Validate environment
36
+ if (!AA_WALLET || !SESSION_ID || !AGENT_PRIVATE_KEY) {
37
+ console.error("❌ Missing environment variables. Please run setup first:");
38
+ console.error(" npm run setup");
39
+ console.error("\n Required: AA_WALLET, SESSION_ID, AGENT_PRIVATE_KEY");
40
+ process.exit(1);
41
+ }
42
+ const agentSigner = new ethers_1.ethers.Wallet(AGENT_PRIVATE_KEY);
43
+ // =============================================================================
44
+ // EIP-712 Signing
45
+ // =============================================================================
46
+ const TRANSFER_AUTH_TYPES = {
47
+ TransferWithAuthorization: [
48
+ { name: "from", type: "address" },
49
+ { name: "to", type: "address" },
50
+ { name: "token", type: "address" },
51
+ { name: "value", type: "uint256" },
52
+ { name: "validAfter", type: "uint256" },
53
+ { name: "validBefore", type: "uint256" },
54
+ { name: "nonce", type: "bytes32" },
55
+ ],
56
+ };
57
+ function getEIP712Domain(aaWallet, chainId) {
58
+ return {
59
+ name: "GokiteAccount",
60
+ version: "1",
61
+ chainId: chainId,
62
+ verifyingContract: aaWallet,
63
+ };
64
+ }
65
+ async function signTransferAuthorization(aaWallet, chainId, auth) {
66
+ const domain = getEIP712Domain(aaWallet, chainId);
67
+ return agentSigner.signTypedData(domain, TRANSFER_AUTH_TYPES, auth);
68
+ }
69
+ // =============================================================================
70
+ // Payment Flow Functions
71
+ // =============================================================================
72
+ /**
73
+ * Step 1: Request content without payment
74
+ * Expected: 402 Payment Required with PaymentRequirements
75
+ */
76
+ async function requestContent(endpoint) {
77
+ console.log("\n Step 1: Request Content (without payment)");
78
+ console.log("─".repeat(50));
79
+ console.log(` URL: ${SERVICE_URL}${endpoint}`);
80
+ const response = await fetch(`${SERVICE_URL}${endpoint}`);
81
+ if (response.status === 402) {
82
+ const data = (await response.json());
83
+ console.log(` Status: 402 Payment Required ✅`);
84
+ // Find kite-testnet payment option (we'll use gokite-aa scheme to pay)
85
+ const kiteOption = data.accepts?.find((req) => req.network === "kite-testnet");
86
+ if (kiteOption) {
87
+ console.log(` Payment options: ${data.accepts?.length || 0}`);
88
+ console.log(` Selected: ${kiteOption.network} (using gokite-aa scheme)`);
89
+ console.log(` Amount: ${ethers_1.ethers.formatUnits(kiteOption.maxAmountRequired, 18)} TestUSD`);
90
+ console.log(` Pay to: ${kiteOption.payTo}`);
91
+ return kiteOption;
92
+ }
93
+ else {
94
+ console.log(" ❌ No kite-testnet payment option available");
95
+ console.log(" Available networks:", data.accepts?.map((r) => r.network).join(", "));
96
+ return null;
97
+ }
98
+ }
99
+ else if (response.ok) {
100
+ console.log(" ⚠️ Content returned without payment (unexpected)");
101
+ const content = await response.text();
102
+ console.log(" Content:", content.slice(0, 100));
103
+ return null;
104
+ }
105
+ else {
106
+ console.log(` ❌ Unexpected status: ${response.status}`);
107
+ return null;
108
+ }
109
+ }
110
+ /**
111
+ * Step 2: Sign payment authorization
112
+ */
113
+ async function signPayment(requirements) {
114
+ console.log("\n Step 2: Sign Payment Authorization");
115
+ console.log("─".repeat(50));
116
+ const nonce = ethers_1.ethers.hexlify((0, ethers_1.randomBytes)(32));
117
+ const now = BigInt(Math.floor(Date.now() / 1000));
118
+ const auth = {
119
+ from: AA_WALLET,
120
+ to: requirements.payTo,
121
+ token: requirements.asset,
122
+ value: BigInt(requirements.maxAmountRequired),
123
+ validAfter: now - 60n,
124
+ validBefore: now + 3600n,
125
+ nonce,
126
+ };
127
+ console.log(` From: ${auth.from}`);
128
+ console.log(` To: ${auth.to}`);
129
+ console.log(` Token: ${auth.token}`);
130
+ console.log(` Value: ${ethers_1.ethers.formatUnits(auth.value, 18)} TestUSD`);
131
+ console.log(` Nonce: ${nonce.slice(0, 18)}...`);
132
+ const signature = await signTransferAuthorization(AA_WALLET, CHAIN_ID, auth);
133
+ console.log(` Signature: ${signature.slice(0, 20)}...${signature.slice(-8)}`);
134
+ console.log(" ✅ Authorization signed");
135
+ const paymentPayload = {
136
+ x402Version: 1,
137
+ scheme: "gokite-aa",
138
+ network: requirements.network,
139
+ payload: {
140
+ signature,
141
+ authorization: {
142
+ from: auth.from,
143
+ to: auth.to,
144
+ token: auth.token,
145
+ value: auth.value.toString(),
146
+ validAfter: auth.validAfter.toString(),
147
+ validBefore: auth.validBefore.toString(),
148
+ nonce: auth.nonce,
149
+ },
150
+ sessionId: SESSION_ID,
151
+ },
152
+ };
153
+ return { paymentPayload, nonce };
154
+ }
155
+ /**
156
+ * Step 3: Send payment and get content
157
+ */
158
+ async function buyContent(endpoint, paymentPayload) {
159
+ console.log("\n Step 3: Send Payment & Get Content");
160
+ console.log("─".repeat(50));
161
+ // Encode payment payload as base64
162
+ const xPayment = Buffer.from(JSON.stringify(paymentPayload)).toString("base64");
163
+ console.log(` X-Payment header length: ${xPayment.length} chars`);
164
+ const response = await fetch(`${SERVICE_URL}${endpoint}`, {
165
+ headers: {
166
+ "X-Payment": xPayment,
167
+ },
168
+ });
169
+ console.log(` Status: ${response.status}`);
170
+ if (response.ok) {
171
+ const contentType = response.headers.get("content-type");
172
+ let content;
173
+ if (contentType?.includes("application/json")) {
174
+ const json = await response.json();
175
+ content = JSON.stringify(json, null, 2);
176
+ }
177
+ else {
178
+ content = await response.text();
179
+ }
180
+ console.log(" ✅ Content received!");
181
+ return { success: true, content };
182
+ }
183
+ else {
184
+ const error = await response.text();
185
+ console.log(" ❌ Payment failed");
186
+ console.log(" Error:", error);
187
+ return { success: false, error };
188
+ }
189
+ }
190
+ // =============================================================================
191
+ // Health Checks
192
+ // =============================================================================
193
+ async function checkFacilitator() {
194
+ console.log("\n🏥 Facilitator Health Check");
195
+ console.log("─".repeat(50));
196
+ console.log(` URL: ${FACILITATOR_URL}`);
197
+ try {
198
+ const response = await fetch(`${FACILITATOR_URL}/health`);
199
+ if (response.ok) {
200
+ console.log(" ✅ Facilitator is healthy");
201
+ return true;
202
+ }
203
+ console.log(` ❌ Status: ${response.status}`);
204
+ return false;
205
+ }
206
+ catch (error) {
207
+ console.log(" ❌ Connection failed:", error.message);
208
+ return false;
209
+ }
210
+ }
211
+ async function checkService() {
212
+ console.log("\n🏥 Service Health Check");
213
+ console.log("─".repeat(50));
214
+ console.log(` URL: ${SERVICE_URL}`);
215
+ try {
216
+ const response = await fetch(`${SERVICE_URL}/health`);
217
+ if (response.ok) {
218
+ console.log(" ✅ Service is healthy");
219
+ return true;
220
+ }
221
+ console.log(` ❌ Status: ${response.status}`);
222
+ return false;
223
+ }
224
+ catch (error) {
225
+ console.log(" ❌ Connection failed:", error.message);
226
+ return false;
227
+ }
228
+ }
229
+ // =============================================================================
230
+ // Main Flow
231
+ // =============================================================================
232
+ async function main() {
233
+ const endpoint = process.argv[2] || "/protected-route";
234
+ console.log("═".repeat(60));
235
+ console.log(" x402 Gokite Payment Demo - Client");
236
+ console.log("═".repeat(60));
237
+ console.log("\n📍 Configuration");
238
+ console.log("─".repeat(50));
239
+ console.log(` AA Wallet: ${AA_WALLET}`);
240
+ console.log(` Session ID: ${SESSION_ID.slice(0, 18)}...`);
241
+ console.log(` Agent: ${agentSigner.address}`);
242
+ console.log(` Service: ${SERVICE_URL}`);
243
+ console.log(` Endpoint: ${endpoint}`);
244
+ // // Health checks
245
+ // const facilitatorOk = await checkFacilitator();
246
+ // const serviceOk = await checkService();
247
+ // if (!facilitatorOk || !serviceOk) {
248
+ // console.error("\n❌ Health checks failed. Please ensure services are running:");
249
+ // console.error(" 1. Facilitator: cargo run (port 8080)");
250
+ // console.error(" 2. Service: cd examples/x402-axum-example && cargo run (port 8099)");
251
+ // process.exit(1);
252
+ // }
253
+ // Step 1: Request content (expect 402)
254
+ const requirements = await requestContent(endpoint);
255
+ if (!requirements) {
256
+ console.error("\n❌ Could not get payment requirements");
257
+ process.exit(1);
258
+ }
259
+ // Step 2: Sign payment
260
+ const { paymentPayload, nonce } = await signPayment(requirements);
261
+ // Step 3: Send payment and get content
262
+ const result = await buyContent(endpoint, paymentPayload);
263
+ // Summary
264
+ console.log("\n" + "═".repeat(60));
265
+ console.log(" Result");
266
+ console.log("═".repeat(60));
267
+ if (result.success) {
268
+ console.log(" ✅ Purchase successful!");
269
+ console.log("\n Content:");
270
+ console.log(" " + "─".repeat(48));
271
+ console.log(result.content?.split("\n").map((l) => " " + l).join("\n"));
272
+ console.log(" " + "─".repeat(48));
273
+ console.log(`\n Payment nonce: ${nonce}`);
274
+ console.log(" (Nonce is now used - replay protection active)");
275
+ }
276
+ else {
277
+ console.log(" ❌ Purchase failed");
278
+ console.log(` Error: ${result.error}`);
279
+ }
280
+ console.log("═".repeat(60));
281
+ }
282
+ main().catch((error) => {
283
+ console.error(error);
284
+ process.exit(1);
285
+ });
package/dist/config.d.ts CHANGED
@@ -5,6 +5,7 @@ export interface NetworkConfig {
5
5
  accountImplementation: string;
6
6
  bundlerUrl?: string;
7
7
  paymaster?: string;
8
+ settlementToken: string;
8
9
  supportedTokens: {
9
10
  address: string;
10
11
  symbol: string;
package/dist/config.js CHANGED
@@ -8,6 +8,7 @@ exports.NETWORKS = {
8
8
  accountFactory: '0xAba80c4c8748c114Ba8b61cda3b0112333C3b96E',
9
9
  accountImplementation: '0x376c32FcE28aa8496465a9E5C8a03ED8B1a7fB4D',
10
10
  paymaster: '0x9Adcbf85D5c724611a490Ba9eDc4d38d6F39e92d',
11
+ settlementToken: '0x0fF5393387ad2f9f691FD6Fd28e07E3969e27e63',
11
12
  supportedTokens: [
12
13
  { address: '0x0000000000000000000000000000000000000000', symbol: 'KITE', decimals: 18 },
13
14
  { address: '0x0fF5393387ad2f9f691FD6Fd28e07E3969e27e63', symbol: 'Test USD', decimals: 18 },
@@ -1,13 +1,37 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ /**
4
+ * GokiteAccount E2E Demo with x402 Facilitator Integration
5
+ *
6
+ * This script demonstrates the complete AA wallet payment flow:
7
+ * 1. Setup AA wallet: addSupportedToken + setMasterBudgetRules
8
+ * 2. Create session with agent and spending rules
9
+ * 3. Agent signs EIP-712 transfer authorization
10
+ * 4. Verify payment via x402 facilitator
11
+ * 5. Settle payment via x402 facilitator (or direct UserOp execution)
12
+ *
13
+ * Usage:
14
+ * npx tsx example-token-paymaster.ts [--settle] [--direct]
15
+ *
16
+ * Options:
17
+ * --settle Execute real settlement via facilitator
18
+ * --direct Execute direct UserOp instead of facilitator
19
+ */
3
20
  const ethers_1 = require("ethers");
4
21
  const gokite_aa_sdk_1 = require("./gokite-aa-sdk");
5
22
  require("dotenv/config");
23
+ // =============================================================================
24
+ // Configuration
25
+ // =============================================================================
6
26
  const NETWORK = "kite_testnet";
7
27
  const RPC_URL = "https://rpc-testnet.gokite.ai";
8
28
  const BUNDLER_URL = "https://bundler-service.staging.gokite.ai/rpc/";
9
29
  const SETTLEMENT_TOKEN = "0x0fF5393387ad2f9f691FD6Fd28e07E3969e27e63";
10
30
  const ZERO_ADDRESS = "0x0000000000000000000000000000000000000000";
31
+ // x402 Facilitator configuration
32
+ const FACILITATOR_URL = process.env.FACILITATOR_URL || "http://localhost:8080";
33
+ const X402_NETWORK = "kite-testnet";
34
+ const CHAIN_ID = 2368;
11
35
  const sdk = new gokite_aa_sdk_1.GokiteAASDK(NETWORK, RPC_URL, BUNDLER_URL);
12
36
  // GokiteAccount interface (includes SessionManager functions)
13
37
  const gokiteAccountInterface = new ethers_1.ethers.Interface([
@@ -17,6 +41,7 @@ const gokiteAccountInterface = new ethers_1.ethers.Interface([
17
41
  "function setMasterBudgetRules(uint256[] timeWindows, uint160[] budgets)",
18
42
  // Session management
19
43
  "function createSession(bytes32 sessionId, address agent, tuple(uint256 timeWindow, uint160 budget, uint96 initialWindowStartTime, bytes32[] targetProviders)[] rules)",
44
+ "function setSpendingRules(bytes32 sessionId, tuple(uint256 timeWindow, uint160 budget, uint96 initialWindowStartTime, bytes32[] targetProviders)[] rules)",
20
45
  // Transfer execution
21
46
  "function executeTransferWithAuthorization(bytes32 sessionId, tuple(address from, address to, address token, uint256 value, uint256 validAfter, uint256 validBefore, bytes32 nonce) auth, bytes signature)",
22
47
  // Batch execution
@@ -38,13 +63,9 @@ const gokiteAccountInterface = new ethers_1.ethers.Interface([
38
63
  // Nonce queries
39
64
  "function isNonceUsed(bytes32 nonce) view returns (bool)",
40
65
  ]);
41
- /**
42
- * E2E Demo for AA Session, Budget, and Pay flow:
43
- * 1. Setup AA wallet: addSupportedToken + setMasterBudgetRules
44
- * 2. Create session with agent and spending rules
45
- * 3. Agent signs EIP-712 transfer authorization
46
- * 4. Execute transfer with authorization
47
- */
66
+ // =============================================================================
67
+ // Environment Setup
68
+ // =============================================================================
48
69
  // Owner: controls AA wallet, signs UserOps
49
70
  const OWNER_PRIVATE_KEY = process.env.PRIVATE_KEY;
50
71
  // Agent: signs transfer authorizations (can be same as owner for demo)
@@ -116,11 +137,140 @@ function sessionRules() {
116
137
  },
117
138
  ];
118
139
  }
119
- // ============ Frontend View Functions Examples ============
140
+ // =============================================================================
141
+ // x402 Facilitator API Functions
142
+ // =============================================================================
143
+ async function facilitatorHealthCheck() {
144
+ console.log("\n[x402] Health Check");
145
+ console.log("=".repeat(50));
146
+ try {
147
+ const response = await fetch(`${FACILITATOR_URL}/health`);
148
+ const data = await response.json();
149
+ console.log(" Response:", JSON.stringify(data, null, 2));
150
+ console.log(" ✅ Facilitator is healthy!");
151
+ return true;
152
+ }
153
+ catch (error) {
154
+ console.error(" ❌ Health check failed:", error);
155
+ return false;
156
+ }
157
+ }
158
+ async function getSupportedNetworks() {
159
+ console.log("\n[x402] Supported Networks");
160
+ console.log("=".repeat(50));
161
+ try {
162
+ const response = await fetch(`${FACILITATOR_URL}/supported`);
163
+ const data = (await response.json());
164
+ console.log(" Response:", JSON.stringify(data, null, 2));
165
+ const kinds = data.kinds || [];
166
+ const hasKite = kinds.some((k) => k.network === X402_NETWORK);
167
+ if (hasKite) {
168
+ console.log(" ✅ Kite testnet is supported!");
169
+ return true;
170
+ }
171
+ else {
172
+ console.log(" ❌ Kite testnet NOT found in supported networks");
173
+ return false;
174
+ }
175
+ }
176
+ catch (error) {
177
+ console.error(" ❌ Failed to get supported networks:", error);
178
+ return false;
179
+ }
180
+ }
181
+ function buildVerifyRequest(authorization, signature, sessionIdHex, recipient) {
182
+ return {
183
+ x402Version: 1,
184
+ paymentPayload: {
185
+ x402Version: 1,
186
+ scheme: "gokite-aa",
187
+ network: X402_NETWORK,
188
+ payload: {
189
+ signature,
190
+ authorization,
191
+ sessionId: sessionIdHex,
192
+ },
193
+ },
194
+ paymentRequirements: {
195
+ scheme: "gokite-aa",
196
+ network: X402_NETWORK,
197
+ maxAmountRequired: authorization.value,
198
+ resource: "https://example.com/api",
199
+ description: "Test payment via AA wallet",
200
+ mimeType: "application/json",
201
+ payTo: recipient,
202
+ maxTimeoutSeconds: 30,
203
+ asset: authorization.token,
204
+ },
205
+ };
206
+ }
207
+ async function verifyPayment(request) {
208
+ console.log("\n[x402] Verify Payment");
209
+ console.log("=".repeat(50));
210
+ console.log(" Request:", JSON.stringify(request, null, 2));
211
+ try {
212
+ const response = await fetch(`${FACILITATOR_URL}/verify`, {
213
+ method: "POST",
214
+ headers: { "Content-Type": "application/json" },
215
+ body: JSON.stringify(request),
216
+ });
217
+ const data = (await response.json());
218
+ console.log("\n Response:", JSON.stringify(data, null, 2));
219
+ if (data.isValid) {
220
+ console.log("\n ✅ Verification PASSED!");
221
+ }
222
+ else {
223
+ console.log("\n ❌ Verification FAILED:", data.invalidReason);
224
+ }
225
+ return data;
226
+ }
227
+ catch (error) {
228
+ console.error(" ❌ Verify request failed:", error);
229
+ return null;
230
+ }
231
+ }
232
+ async function settlePayment(request) {
233
+ console.log("\n[x402] Settle Payment");
234
+ console.log("=".repeat(50));
235
+ console.log(" Request:", JSON.stringify(request, null, 2));
236
+ try {
237
+ const response = await fetch(`${FACILITATOR_URL}/settle`, {
238
+ method: "POST",
239
+ headers: { "Content-Type": "application/json" },
240
+ body: JSON.stringify(request),
241
+ });
242
+ const data = (await response.json());
243
+ console.log("\n Response:", JSON.stringify(data, null, 2));
244
+ if (data.success) {
245
+ console.log("\n ✅ Settlement SUCCESSFUL!");
246
+ if (data.transaction) {
247
+ console.log(" Transaction Hash:", data.transaction);
248
+ }
249
+ }
250
+ else {
251
+ console.log("\n ❌ Settlement FAILED:", data.errorReason);
252
+ }
253
+ return data;
254
+ }
255
+ catch (error) {
256
+ console.error(" ❌ Settle request failed:", error);
257
+ return null;
258
+ }
259
+ }
260
+ // =============================================================================
261
+ // Query Functions
262
+ // =============================================================================
120
263
  async function queryWalletStatus(aaWallet, provider) {
121
- const contract = new ethers_1.ethers.Contract(aaWallet, gokiteAccountInterface, provider);
122
264
  console.log("\n[Query] AA Wallet Status");
123
265
  console.log("=".repeat(50));
266
+ // Check if wallet is deployed first
267
+ const code = await provider.getCode(aaWallet);
268
+ if (code === "0x") {
269
+ console.log(` AA Wallet ${aaWallet} is not deployed yet (counterfactual address)`);
270
+ console.log(` Wallet will be deployed on first UserOp execution`);
271
+ return { isDeployed: false, isSupported: false, balance: 0n };
272
+ }
273
+ const contract = new ethers_1.ethers.Contract(aaWallet, gokiteAccountInterface, provider);
124
274
  // 1. Check if token is supported
125
275
  const isSupported = await contract.isTokenSupported(SETTLEMENT_TOKEN);
126
276
  console.log(` Token ${SETTLEMENT_TOKEN} supported: ${isSupported}`);
@@ -138,22 +288,22 @@ async function queryWalletStatus(aaWallet, provider) {
138
288
  const windowType = timeWindow === 0n ? "per-tx" : `${Number(timeWindow) / 86400} day(s)`;
139
289
  console.log(` Rule ${i}: ${windowType}, budget: ${budget}, used: ${used}`);
140
290
  }
141
- return { isSupported, balance };
291
+ return { isDeployed: true, isSupported, balance };
142
292
  }
143
- async function querySessionStatus(aaWallet, sessionId, provider) {
293
+ async function querySessionStatus(aaWallet, sessionIdHex, provider) {
144
294
  const contract = new ethers_1.ethers.Contract(aaWallet, gokiteAccountInterface, provider);
145
295
  console.log("\n[Query] Session Status");
146
296
  console.log("=".repeat(50));
147
297
  // 1. Check if session exists
148
- const exists = await contract.sessionExists(sessionId);
149
- console.log(` Session ${sessionId.slice(0, 18)}... exists: ${exists}`);
298
+ const exists = await contract.sessionExists(sessionIdHex);
299
+ console.log(` Session ${sessionIdHex.slice(0, 18)}... exists: ${exists}`);
150
300
  if (!exists)
151
301
  return { exists };
152
302
  // 2. Get session agent
153
- const agent = await contract.getSessionAgent(sessionId);
303
+ const agent = await contract.getSessionAgent(sessionIdHex);
154
304
  console.log(` Session agent: ${agent}`);
155
305
  // 3. Get spending rules
156
- const spendingRules = await contract.getSpendingRules(sessionId);
306
+ const spendingRules = await contract.getSpendingRules(sessionIdHex);
157
307
  console.log(` Spending rules count: ${spendingRules.length}`);
158
308
  for (let i = 0; i < spendingRules.length; i++) {
159
309
  const rule = spendingRules[i];
@@ -163,10 +313,6 @@ async function querySessionStatus(aaWallet, sessionId, provider) {
163
313
  const windowType = timeWindow === 0n ? "per-tx" : `${Number(timeWindow) / 86400} day(s)`;
164
314
  console.log(` Rule ${i}: ${windowType}, budget: ${budget}, used: ${used}`);
165
315
  }
166
- // 4. Check if a specific amount would pass spending rules
167
- const testAmount = ethers_1.ethers.parseUnits("5", 18);
168
- const wouldPass = await contract.checkSpendingRules(sessionId, testAmount, ethers_1.ethers.ZeroHash);
169
- console.log(` Would ${ethers_1.ethers.formatUnits(testAmount, 18)} pass spending rules: ${wouldPass}`);
170
316
  return { exists, agent, spendingRules };
171
317
  }
172
318
  async function checkNonceStatus(aaWallet, nonce, provider) {
@@ -175,29 +321,49 @@ async function checkNonceStatus(aaWallet, nonce, provider) {
175
321
  console.log(` Nonce ${nonce.slice(0, 18)}... used: ${isUsed}`);
176
322
  return isUsed;
177
323
  }
324
+ // =============================================================================
325
+ // Main Flow
326
+ // =============================================================================
178
327
  async function main() {
328
+ const args = process.argv.slice(2);
329
+ const shouldSettle = args.includes("--settle");
330
+ const useDirect = args.includes("--direct");
331
+ console.log("=".repeat(60));
332
+ console.log("GokiteAccount E2E Demo with x402 Facilitator");
333
+ console.log("=".repeat(60));
179
334
  const aaWallet = sdk.getAccountAddress(ownerSigner.address);
180
335
  const provider = new ethers_1.ethers.JsonRpcProvider(RPC_URL);
181
336
  const chainId = (await provider.getNetwork()).chainId;
182
- // ============ Frontend Query Examples ============
183
- await queryWalletStatus(aaWallet, provider);
184
- // ============ Step 1: Setup AA Wallet (batch: addSupportedToken + setMasterBudgetRules) ============
185
- console.log("\n[Step 1] Setting up AA wallet...");
186
- const addTokenData = gokiteAccountInterface.encodeFunctionData("addSupportedToken", [
187
- SETTLEMENT_TOKEN,
188
- ]);
189
- const masterBudgetData = gokiteAccountInterface.encodeFunctionData("setMasterBudgetRules", [
190
- [BigInt(SECONDS_PER_DAY), 0n],
191
- [ethers_1.ethers.parseUnits("1000", 18), ethers_1.ethers.parseUnits("100", 18)], // 1000 USD/day, 100 USD/tx
192
- ]);
193
- const setupBatchData = gokiteAccountInterface.encodeFunctionData("executeBatch", [
194
- [aaWallet, aaWallet],
195
- [],
196
- [addTokenData, masterBudgetData],
197
- ]);
198
- // const setupTx = await sendWithTokenPayment(aaWallet, setupBatchData);
199
- // console.log(` Setup (addSupportedToken + setMasterBudgetRules) tx: ${setupTx}`);
200
- // await new Promise((r) => setTimeout(r, 5000));
337
+ console.log("\nConfiguration:");
338
+ console.log(" Facilitator URL:", FACILITATOR_URL);
339
+ console.log(" Network:", X402_NETWORK);
340
+ console.log(" Chain ID:", CHAIN_ID);
341
+ console.log(" AA Wallet:", aaWallet);
342
+ console.log(" Owner:", ownerSigner.address);
343
+ console.log(" Agent:", agentSigner.address);
344
+ console.log(" Token:", SETTLEMENT_TOKEN);
345
+ console.log(" Mode:", useDirect ? "Direct UserOp" : "x402 Facilitator");
346
+ // ============ Query Wallet Status ============
347
+ const walletStatus = await queryWalletStatus(aaWallet, provider);
348
+ // ============ Step 1: Setup AA Wallet (only if not deployed) ============
349
+ if (!walletStatus.isDeployed) {
350
+ console.log("\n[Step 1] Setting up AA wallet...");
351
+ const masterBudgetData = gokiteAccountInterface.encodeFunctionData("setMasterBudgetRules", [
352
+ [BigInt(SECONDS_PER_DAY), 0n],
353
+ [ethers_1.ethers.parseUnits("1000", 18), ethers_1.ethers.parseUnits("100", 18)], // 1000 USD/day, 100 USD/tx
354
+ ]);
355
+ const setupBatchData = gokiteAccountInterface.encodeFunctionData("executeBatch", [
356
+ [aaWallet],
357
+ [],
358
+ [masterBudgetData],
359
+ ]);
360
+ const setupTx = await sendWithTokenPayment(aaWallet, setupBatchData);
361
+ console.log(` Setup (addSupportedToken + setMasterBudgetRules) tx: ${setupTx}`);
362
+ await new Promise((r) => setTimeout(r, 5000));
363
+ }
364
+ else {
365
+ console.log("\n[Step 1] AA wallet already deployed, skipping setup");
366
+ }
201
367
  // ============ Step 2: Create Session ============
202
368
  console.log("\n[Step 2] Creating session...");
203
369
  const createSessionData = gokiteAccountInterface.encodeFunctionData("createSession", [
@@ -211,41 +377,117 @@ async function main() {
211
377
  await new Promise((r) => setTimeout(r, 5000));
212
378
  // Query session status after creation
213
379
  await querySessionStatus(aaWallet, sessionId, provider);
214
- // ============ Step 3: Sign Transfer Authorization ============
215
- console.log("\n[Step 3] Agent signing transfer authorization...");
216
- const recipient = ownerSigner.address; // send to self for demo
217
- const transferAmount = ethers_1.ethers.parseUnits("1", 18); // 1 token
218
- const nonce = ethers_1.ethers.hexlify((0, ethers_1.randomBytes)(32));
219
- const now = BigInt(Math.floor(Date.now() / 1000));
220
- const auth = {
221
- from: aaWallet,
222
- to: recipient,
223
- token: SETTLEMENT_TOKEN,
224
- value: transferAmount,
225
- validAfter: now - 60n,
226
- validBefore: now + 3600n,
227
- nonce: nonce,
228
- };
229
- const signature = await signTransferAuthorization(aaWallet, chainId, auth);
230
- console.log(` Authorization nonce: ${nonce}`);
231
- console.log(` Signature: ${signature.slice(0, 42)}...`);
232
- // ============ Step 4: Execute Transfer ============
233
- console.log("\n[Step 4] Executing transfer with authorization...");
234
- const executeTransferData = gokiteAccountInterface.encodeFunctionData("executeTransferWithAuthorization", [sessionId, auth, signature]);
235
- const executeTx = await sendWithTokenPayment(aaWallet, executeTransferData);
236
- console.log(` executeTransferWithAuthorization tx: ${executeTx}`);
380
+ // ============ Step 2b: Update Spending Rules (setSpendingRules example) ============
381
+ console.log("\n[Step 2b] Updating session spending rules...");
382
+ // Define new spending rules to replace existing ones
383
+ const newSpendingRules = [
384
+ {
385
+ timeWindow: BigInt(SECONDS_PER_DAY),
386
+ budget: ethers_1.ethers.parseUnits("200", 18),
387
+ initialWindowStartTime: currentDayStart(),
388
+ targetProviders: [],
389
+ },
390
+ {
391
+ timeWindow: 0n,
392
+ budget: ethers_1.ethers.parseUnits("20", 18),
393
+ initialWindowStartTime: 0n,
394
+ targetProviders: [],
395
+ },
396
+ {
397
+ timeWindow: BigInt(SECONDS_PER_DAY * 7),
398
+ budget: ethers_1.ethers.parseUnits("500", 18),
399
+ initialWindowStartTime: currentDayStart(),
400
+ targetProviders: [],
401
+ },
402
+ ];
403
+ const setSpendingRulesData = gokiteAccountInterface.encodeFunctionData("setSpendingRules", [
404
+ sessionId,
405
+ newSpendingRules,
406
+ ]);
407
+ const setSpendingRulesTx = await sendWithTokenPayment(aaWallet, setSpendingRulesData);
408
+ console.log(` setSpendingRules tx: ${setSpendingRulesTx}`);
237
409
  await new Promise((r) => setTimeout(r, 5000));
238
- // ============ Post-Transfer Queries ============
239
- console.log("\n[Post-Transfer] Checking status...");
240
- // Check nonce is now used (replay protection)
241
- await checkNonceStatus(aaWallet, nonce, provider);
242
- // Query updated session status (usage should be updated)
410
+ // Query session status after updating spending rules
243
411
  await querySessionStatus(aaWallet, sessionId, provider);
244
- // Query updated wallet status
245
- await queryWalletStatus(aaWallet, provider);
246
- console.log("\n" + "=".repeat(60));
247
- console.log("E2E Demo completed successfully!");
248
- console.log("=".repeat(60));
412
+ // // ============ Step 3: Sign Transfer Authorization ============
413
+ // console.log("\n[Step 3] Agent signing transfer authorization...");
414
+ // const recipient = ownerSigner.address; // send to self for demo
415
+ // const transferAmount = ethers.parseUnits("1", 18); // 1 token
416
+ // const nonce = ethers.hexlify(randomBytes(32));
417
+ // const now = BigInt(Math.floor(Date.now() / 1000));
418
+ // const auth = {
419
+ // from: aaWallet,
420
+ // to: recipient,
421
+ // token: SETTLEMENT_TOKEN,
422
+ // value: transferAmount,
423
+ // validAfter: now - 60n, // valid from 1 minute ago
424
+ // validBefore: now + 3600n, // valid for 1 hour
425
+ // nonce: nonce,
426
+ // };
427
+ // const signature = await signTransferAuthorization(aaWallet, chainId, auth);
428
+ // console.log(` Authorization nonce: ${nonce}`);
429
+ // console.log(` Signature: ${signature.slice(0, 42)}...`);
430
+ // // ============ Step 4: Execute Transfer ============
431
+ // if (useDirect) {
432
+ // // Direct UserOp execution
433
+ // console.log("\n[Step 4] Executing transfer via direct UserOp...");
434
+ // const executeTransferData = gokiteAccountInterface.encodeFunctionData(
435
+ // "executeTransferWithAuthorization",
436
+ // [sessionId, auth, signature]
437
+ // );
438
+ // const executeTx = await sendWithTokenPayment(aaWallet, executeTransferData);
439
+ // console.log(` executeTransferWithAuthorization tx: ${executeTx}`);
440
+ // } else {
441
+ // // x402 Facilitator flow
442
+ // console.log("\n[Step 4] Verifying via x402 facilitator...");
443
+ // // Check facilitator health
444
+ // const healthy = await facilitatorHealthCheck();
445
+ // if (!healthy) {
446
+ // console.error("\n❌ Facilitator not available, aborting");
447
+ // process.exit(1);
448
+ // }
449
+ // // Check supported networks
450
+ // await getSupportedNetworks();
451
+ // // Build x402 request
452
+ // const x402Auth: Authorization = {
453
+ // from: aaWallet,
454
+ // to: recipient,
455
+ // token: SETTLEMENT_TOKEN,
456
+ // value: transferAmount.toString(),
457
+ // validAfter: auth.validAfter.toString(),
458
+ // validBefore: auth.validBefore.toString(),
459
+ // nonce: nonce,
460
+ // };
461
+ // const verifyRequest = buildVerifyRequest(x402Auth, signature, sessionId, recipient);
462
+ // // Verify payment
463
+ // const verifyResult = await verifyPayment(verifyRequest);
464
+ // if (!verifyResult?.isValid) {
465
+ // console.error("\n❌ Verification failed, skipping settle");
466
+ // process.exit(1);
467
+ // }
468
+ // // Settle payment (optional)
469
+ // if (shouldSettle) {
470
+ // console.log("\n⚠️ Proceeding with REAL settlement transaction...");
471
+ // const settleResult = await settlePayment(verifyRequest);
472
+ // if (settleResult?.success && settleResult.transaction) {
473
+ // console.log(` Settlement tx: ${settleResult.transaction}`);
474
+ // }
475
+ // } else {
476
+ // console.log("\n💡 Skipping settle (use --settle flag to execute real transaction)");
477
+ // }
478
+ // }
479
+ // await new Promise((r) => setTimeout(r, 5000));
480
+ // // ============ Post-Transfer Queries ============
481
+ // console.log("\n[Post-Transfer] Checking status...");
482
+ // // Check nonce is now used (replay protection)
483
+ // await checkNonceStatus(aaWallet, nonce, provider);
484
+ // // Query updated session status (usage should be updated)
485
+ // await querySessionStatus(aaWallet, sessionId, provider);
486
+ // // Query updated wallet status
487
+ // await queryWalletStatus(aaWallet, provider);
488
+ // console.log("\n" + "=".repeat(60));
489
+ // console.log("E2E Demo completed successfully!");
490
+ // console.log("=".repeat(60));
249
491
  }
250
492
  main().catch((error) => {
251
493
  console.error(error);
@@ -35,8 +35,14 @@ export declare class GokiteAASDK {
35
35
  * Build call data for batch transactions
36
36
  */
37
37
  buildBatchCallData(request: BatchUserOperationRequest): string;
38
+ /**
39
+ * Prepend addSupportedToken call to request for first transaction (wallet deployment)
40
+ * Uses settlement token from config (supportedTokens[1])
41
+ */
42
+ private prependAddSupportedToken;
38
43
  /**
39
44
  * Create user operation
45
+ * If wallet is not deployed, automatically batch addSupportedToken into the first transaction
40
46
  */
41
47
  createUserOperation(owner: string, request: UserOperationRequest | BatchUserOperationRequest, salt?: bigint, paymasterAddress?: string, tokenAddress?: string): Promise<Partial<UserOperation>>;
42
48
  /**
@@ -251,16 +251,46 @@ class GokiteAASDK {
251
251
  const values = request.values || new Array(request.targets.length).fill(0n);
252
252
  return (0, utils_1.encodeFunctionCall)(['function executeBatch(address[],uint256[],bytes[])'], 'executeBatch', [request.targets, values, request.callDatas]);
253
253
  }
254
+ /**
255
+ * Prepend addSupportedToken call to request for first transaction (wallet deployment)
256
+ * Uses settlement token from config (supportedTokens[1])
257
+ */
258
+ prependAddSupportedToken(request, accountAddress) {
259
+ const settlementToken = this.config.settlementToken;
260
+ const addTokenCallData = (0, utils_1.encodeFunctionCall)(['function addSupportedToken(address)'], 'addSupportedToken', [settlementToken]);
261
+ if ('target' in request) {
262
+ // Convert single request to batch with addSupportedToken prepended
263
+ return {
264
+ targets: [accountAddress, request.target],
265
+ values: [BigInt(0), request.value || BigInt(0)],
266
+ callDatas: [addTokenCallData, request.callData]
267
+ };
268
+ }
269
+ else {
270
+ // Prepend to existing batch
271
+ return {
272
+ targets: [accountAddress, ...request.targets],
273
+ values: [BigInt(0), ...(request.values || new Array(request.targets.length).fill(BigInt(0)))],
274
+ callDatas: [addTokenCallData, ...request.callDatas]
275
+ };
276
+ }
277
+ }
254
278
  /**
255
279
  * Create user operation
280
+ * If wallet is not deployed, automatically batch addSupportedToken into the first transaction
256
281
  */
257
282
  async createUserOperation(owner, request, salt, paymasterAddress, tokenAddress) {
258
283
  const actualSalt = salt || (0, utils_1.generateSalt)();
259
284
  const accountAddress = this.getAccountAddress(owner, actualSalt);
260
285
  const isDeployed = await this.isAccountDeloyed(accountAddress);
261
- const callData = 'targets' in request
262
- ? this.buildBatchCallData(request)
263
- : this.buildCallData(request);
286
+ // If wallet not deployed, batch addSupportedToken into first transaction
287
+ let finalRequest = request;
288
+ if (!isDeployed) {
289
+ finalRequest = this.prependAddSupportedToken(request, accountAddress);
290
+ }
291
+ const callData = 'targets' in finalRequest
292
+ ? this.buildBatchCallData(finalRequest)
293
+ : this.buildCallData(finalRequest);
264
294
  // Gas settings (matching Go implementation)
265
295
  const callGasLimit = BigInt(300000);
266
296
  const verificationGasLimit = BigInt(300000);
package/dist/utils.js CHANGED
@@ -130,7 +130,7 @@ function serializeUserOperation(userOp) {
130
130
  exports.serializeUserOperation = serializeUserOperation;
131
131
  // default salt is 0
132
132
  function generateSalt() {
133
- return BigInt(0);
133
+ return BigInt(1);
134
134
  }
135
135
  exports.generateSalt = generateSalt;
136
136
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gokite-aa-sdk",
3
- "version": "1.0.10",
3
+ "version": "1.0.12",
4
4
  "description": "Simple and clean Account Abstraction SDK for Gokite",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",