gokite-aa-sdk 1.0.8 → 1.0.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/config.js CHANGED
@@ -6,7 +6,7 @@ exports.NETWORKS = {
6
6
  chainId: 2368,
7
7
  entryPoint: '0x4337084D9E255Ff0702461CF8895CE9E3b5Ff108',
8
8
  accountFactory: '0xAba80c4c8748c114Ba8b61cda3b0112333C3b96E',
9
- accountImplementation: '0xc033eC55a6c6fa7aDe6EcE95835113fC64Fc2e3F',
9
+ accountImplementation: '0x376c32FcE28aa8496465a9E5C8a03ED8B1a7fB4D',
10
10
  paymaster: '0x9Adcbf85D5c724611a490Ba9eDc4d38d6F39e92d',
11
11
  supportedTokens: [
12
12
  { address: '0x0000000000000000000000000000000000000000', symbol: 'KITE', decimals: 18 },
@@ -6,122 +6,246 @@ require("dotenv/config");
6
6
  const NETWORK = "kite_testnet";
7
7
  const RPC_URL = "https://rpc-testnet.gokite.ai";
8
8
  const BUNDLER_URL = "https://bundler-service.staging.gokite.ai/rpc/";
9
- // const BUNDLER_URL = "http://localhost:14337/rpc/";
10
9
  const SETTLEMENT_TOKEN = "0x0fF5393387ad2f9f691FD6Fd28e07E3969e27e63";
11
10
  const ZERO_ADDRESS = "0x0000000000000000000000000000000000000000";
12
11
  const sdk = new gokite_aa_sdk_1.GokiteAASDK(NETWORK, RPC_URL, BUNDLER_URL);
13
- const sessionInterface = new ethers_1.ethers.Interface([
14
- "function createSession(bytes32 sessionId, address settlementToken, tuple(uint256 timeWindow, uint160 budget, uint96 initialWindowStartTime, bytes32[] targetProviders)[] rules)",
15
- "function setSpendingRules(bytes32 sessionId, tuple(uint256 timeWindow, uint160 budget, uint96 initialWindowStartTime, bytes32[] targetProviders)[] rules)",
16
- ]);
17
- const erc20Interface = new ethers_1.ethers.Interface([
18
- "function transfer(address to, uint256 amount)",
12
+ // GokiteAccount interface (includes SessionManager functions)
13
+ const gokiteAccountInterface = new ethers_1.ethers.Interface([
14
+ // Token whitelist
15
+ "function addSupportedToken(address token)",
16
+ // Master budget
17
+ "function setMasterBudgetRules(uint256[] timeWindows, uint160[] budgets)",
18
+ // Session management
19
+ "function createSession(bytes32 sessionId, address agent, tuple(uint256 timeWindow, uint160 budget, uint96 initialWindowStartTime, bytes32[] targetProviders)[] rules)",
20
+ // Transfer execution
21
+ "function executeTransferWithAuthorization(bytes32 sessionId, tuple(address from, address to, address token, uint256 value, uint256 validAfter, uint256 validBefore, bytes32 nonce) auth, bytes signature)",
22
+ // Batch execution
23
+ "function executeBatch(address[] dest, uint256[] value, bytes[] func)",
24
+ // ============ View Functions (for frontend queries) ============
25
+ // Token whitelist queries
26
+ "function isTokenSupported(address token) view returns (bool)",
27
+ "function getTokenDecimals(address token) view returns (uint8)",
28
+ "function getAvailableBalance(address token) view returns (uint256)",
29
+ // Session queries
30
+ "function sessionExists(bytes32 sessionId) view returns (bool)",
31
+ "function getSessionAgent(bytes32 sessionId) view returns (address)",
32
+ "function getSpendingRules(bytes32 sessionId) view returns (tuple(tuple(uint256 timeWindow, uint160 budget, uint96 initialWindowStartTime, bytes32[] targetProviders) rule, tuple(uint128 amountUsed, uint128 currentTimeWindowStartTime) usage)[])",
33
+ "function getUsage(bytes32 sessionId, uint256 index) view returns (uint256)",
34
+ "function checkSpendingRules(bytes32 sessionId, uint256 normalizedAmount, bytes32 serviceProvider) view returns (bool)",
35
+ // Master budget queries
36
+ "function getMasterBudgetRules() view returns (tuple(tuple(uint256 timeWindow, uint160 budget, uint96 initialWindowStartTime, bytes32[] targetProviders) rule, tuple(uint128 amountUsed, uint128 currentTimeWindowStartTime) usage)[])",
37
+ "function getMasterBudgetRuleCount() view returns (uint256)",
38
+ // Nonce queries
39
+ "function isNonceUsed(bytes32 nonce) view returns (bool)",
19
40
  ]);
20
41
  /**
21
- * Frontend note:
22
- * 1. Creates an AA session and seeds it with example spending rules (daily, weekly, per-transaction caps).
23
- * 2. Updates that session to demonstrate how rule changes propagate on-chain.
24
- * 3. Executes an ERC20 transfer
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
25
47
  */
26
- const PRIVATE_KEY = process.env.PRIVATE_KEY;
48
+ // Owner: controls AA wallet, signs UserOps
49
+ const OWNER_PRIVATE_KEY = process.env.PRIVATE_KEY;
50
+ // Agent: signs transfer authorizations (can be same as owner for demo)
51
+ const AGENT_PRIVATE_KEY = process.env.AGENT_PRIVATE_KEY ?? OWNER_PRIVATE_KEY;
27
52
  const PAYMENT_TOKEN = process.env.PAYMENT_TOKEN ?? SETTLEMENT_TOKEN;
28
- if (!PRIVATE_KEY)
53
+ if (!OWNER_PRIVATE_KEY)
29
54
  throw new Error("Missing PRIVATE_KEY env variable");
30
- const signer = new ethers_1.ethers.Wallet(PRIVATE_KEY);
31
- const sessionId = (0, ethers_1.randomBytes)(32);
32
- const secondsPerDay = 86400;
55
+ const ownerSigner = new ethers_1.ethers.Wallet(OWNER_PRIVATE_KEY);
56
+ const agentSigner = new ethers_1.ethers.Wallet(AGENT_PRIVATE_KEY);
57
+ const sessionId = ethers_1.ethers.hexlify((0, ethers_1.randomBytes)(32));
58
+ const SECONDS_PER_DAY = 86400;
33
59
  function currentDayStart() {
34
60
  const now = Math.floor(Date.now() / 1000);
35
- return BigInt(Math.floor(now / secondsPerDay) * secondsPerDay);
36
- }
37
- function currentWeekStart() {
38
- const now = new Date();
39
- const dayStart = Number(currentDayStart());
40
- const weekday = now.getUTCDay();
41
- const daysFromMonday = (weekday + 6) % 7;
42
- return BigInt(dayStart - daysFromMonday * secondsPerDay);
61
+ return BigInt(Math.floor(now / SECONDS_PER_DAY) * SECONDS_PER_DAY);
43
62
  }
44
63
  async function signUserOp(hash) {
45
- return signer.signMessage(ethers_1.ethers.getBytes(hash));
64
+ return ownerSigner.signMessage(ethers_1.ethers.getBytes(hash));
46
65
  }
47
66
  async function sendWithTokenPayment(target, callData) {
48
- const request = {
49
- target,
50
- value: 0n,
51
- callData,
52
- };
53
- const estimate = await sdk.estimateUserOperation(signer.address, request);
67
+ const request = { target, value: 0n, callData };
68
+ const estimate = await sdk.estimateUserOperation(ownerSigner.address, request);
54
69
  const tokenAddress = estimate.sponsorshipAvailable ? ZERO_ADDRESS : PAYMENT_TOKEN;
55
- const response = await sdk.sendUserOperationWithPayment(signer.address, request, estimate.userOp, tokenAddress, signUserOp);
70
+ const response = await sdk.sendUserOperationWithPayment(ownerSigner.address, request, estimate.userOp, tokenAddress, signUserOp);
56
71
  if (response.status.status !== "success") {
57
72
  throw new Error(response.status.reason ?? "User operation failed");
58
73
  }
59
74
  return response.status.transactionHash ?? "";
60
75
  }
61
- function initialRules() {
76
+ // EIP-712 domain for GokiteAccount
77
+ function getEIP712Domain(aaWallet, chainId) {
78
+ return {
79
+ name: "GokiteAccount",
80
+ version: "1",
81
+ chainId: chainId,
82
+ verifyingContract: aaWallet,
83
+ };
84
+ }
85
+ // EIP-712 types for TransferWithAuthorization
86
+ const TRANSFER_AUTH_TYPES = {
87
+ TransferWithAuthorization: [
88
+ { name: "from", type: "address" },
89
+ { name: "to", type: "address" },
90
+ { name: "token", type: "address" },
91
+ { name: "value", type: "uint256" },
92
+ { name: "validAfter", type: "uint256" },
93
+ { name: "validBefore", type: "uint256" },
94
+ { name: "nonce", type: "bytes32" },
95
+ ],
96
+ };
97
+ // Sign transfer authorization using EIP-712
98
+ async function signTransferAuthorization(aaWallet, chainId, auth) {
99
+ const domain = getEIP712Domain(aaWallet, chainId);
100
+ return agentSigner.signTypedData(domain, TRANSFER_AUTH_TYPES, auth);
101
+ }
102
+ // Session spending rules
103
+ function sessionRules() {
62
104
  return [
63
105
  {
64
- timeWindow: 86400n,
106
+ timeWindow: BigInt(SECONDS_PER_DAY),
65
107
  budget: ethers_1.ethers.parseUnits("100", 18),
66
108
  initialWindowStartTime: currentDayStart(),
67
109
  targetProviders: [],
68
110
  },
69
- {
70
- timeWindow: 604800n,
71
- budget: ethers_1.ethers.parseUnits("1000", 18),
72
- initialWindowStartTime: currentWeekStart(),
73
- targetProviders: [],
74
- },
75
111
  {
76
112
  timeWindow: 0n,
77
113
  budget: ethers_1.ethers.parseUnits("10", 18),
78
114
  initialWindowStartTime: 0n,
79
- targetProviders: [
80
- ethers_1.ethers.keccak256(ethers_1.ethers.toUtf8Bytes("provider1")),
81
- ],
82
- },
83
- ];
84
- }
85
- function updatedRules() {
86
- return [
87
- {
88
- timeWindow: 86400n,
89
- budget: ethers_1.ethers.parseUnits("200", 18),
90
- initialWindowStartTime: currentDayStart(),
91
- targetProviders: [],
92
- },
93
- {
94
- timeWindow: 0n,
95
- budget: ethers_1.ethers.parseUnits("5", 18),
96
- initialWindowStartTime: 0n,
97
115
  targetProviders: [],
98
116
  },
99
117
  ];
100
118
  }
119
+ // ============ Frontend View Functions Examples ============
120
+ async function queryWalletStatus(aaWallet, provider) {
121
+ const contract = new ethers_1.ethers.Contract(aaWallet, gokiteAccountInterface, provider);
122
+ console.log("\n[Query] AA Wallet Status");
123
+ console.log("=".repeat(50));
124
+ // 1. Check if token is supported
125
+ const isSupported = await contract.isTokenSupported(SETTLEMENT_TOKEN);
126
+ console.log(` Token ${SETTLEMENT_TOKEN} supported: ${isSupported}`);
127
+ // 2. Get token balance in AA wallet
128
+ const balance = await contract.getAvailableBalance(SETTLEMENT_TOKEN);
129
+ console.log(` Token balance: ${ethers_1.ethers.formatUnits(balance, 18)}`);
130
+ // 3. Get master budget rules
131
+ const masterRules = await contract.getMasterBudgetRules();
132
+ console.log(` Master budget rules count: ${masterRules.length}`);
133
+ for (let i = 0; i < masterRules.length; i++) {
134
+ const rule = masterRules[i];
135
+ const timeWindow = rule.rule.timeWindow;
136
+ const budget = ethers_1.ethers.formatUnits(rule.rule.budget, 18);
137
+ const used = ethers_1.ethers.formatUnits(rule.usage.amountUsed, 18);
138
+ const windowType = timeWindow === 0n ? "per-tx" : `${Number(timeWindow) / 86400} day(s)`;
139
+ console.log(` Rule ${i}: ${windowType}, budget: ${budget}, used: ${used}`);
140
+ }
141
+ return { isSupported, balance };
142
+ }
143
+ async function querySessionStatus(aaWallet, sessionId, provider) {
144
+ const contract = new ethers_1.ethers.Contract(aaWallet, gokiteAccountInterface, provider);
145
+ console.log("\n[Query] Session Status");
146
+ console.log("=".repeat(50));
147
+ // 1. Check if session exists
148
+ const exists = await contract.sessionExists(sessionId);
149
+ console.log(` Session ${sessionId.slice(0, 18)}... exists: ${exists}`);
150
+ if (!exists)
151
+ return { exists };
152
+ // 2. Get session agent
153
+ const agent = await contract.getSessionAgent(sessionId);
154
+ console.log(` Session agent: ${agent}`);
155
+ // 3. Get spending rules
156
+ const spendingRules = await contract.getSpendingRules(sessionId);
157
+ console.log(` Spending rules count: ${spendingRules.length}`);
158
+ for (let i = 0; i < spendingRules.length; i++) {
159
+ const rule = spendingRules[i];
160
+ const timeWindow = rule.rule.timeWindow;
161
+ const budget = ethers_1.ethers.formatUnits(rule.rule.budget, 18);
162
+ const used = ethers_1.ethers.formatUnits(rule.usage.amountUsed, 18);
163
+ const windowType = timeWindow === 0n ? "per-tx" : `${Number(timeWindow) / 86400} day(s)`;
164
+ console.log(` Rule ${i}: ${windowType}, budget: ${budget}, used: ${used}`);
165
+ }
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
+ return { exists, agent, spendingRules };
171
+ }
172
+ async function checkNonceStatus(aaWallet, nonce, provider) {
173
+ const contract = new ethers_1.ethers.Contract(aaWallet, gokiteAccountInterface, provider);
174
+ const isUsed = await contract.isNonceUsed(nonce);
175
+ console.log(` Nonce ${nonce.slice(0, 18)}... used: ${isUsed}`);
176
+ return isUsed;
177
+ }
101
178
  async function main() {
102
- const aaWallet = sdk.getAccountAddress(signer.address);
103
- const createSessionData = sessionInterface.encodeFunctionData("createSession", [
104
- sessionId,
179
+ const aaWallet = sdk.getAccountAddress(ownerSigner.address);
180
+ const provider = new ethers_1.ethers.JsonRpcProvider(RPC_URL);
181
+ 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", [
105
187
  SETTLEMENT_TOKEN,
106
- initialRules(),
107
188
  ]);
108
- const createTx = await sendWithTokenPayment(aaWallet, createSessionData);
109
- console.log(`createSession tx: ${createTx}`);
110
- await new Promise(resolve => setTimeout(resolve, 10000));
111
- const updateRulesData = sessionInterface.encodeFunctionData("setSpendingRules", [
112
- sessionId,
113
- updatedRules(),
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
114
192
  ]);
115
- const updateTx = await sendWithTokenPayment(aaWallet, updateRulesData);
116
- console.log(`setSpendingRules tx: ${updateTx}`);
117
- await new Promise(resolve => setTimeout(resolve, 10000));
118
- const RECIPIENT = signer.address;
119
- const transferData = erc20Interface.encodeFunctionData("transfer", [
120
- RECIPIENT,
121
- ethers_1.ethers.parseUnits("0", 18),
193
+ const setupBatchData = gokiteAccountInterface.encodeFunctionData("executeBatch", [
194
+ [aaWallet, aaWallet],
195
+ [],
196
+ [addTokenData, masterBudgetData],
122
197
  ]);
123
- const transferTx = await sendWithTokenPayment(SETTLEMENT_TOKEN, transferData);
124
- console.log(`transfer tx: ${transferTx}`);
198
+ // const setupTx = await sendWithTokenPayment(aaWallet, setupBatchData);
199
+ // console.log(` Setup (addSupportedToken + setMasterBudgetRules) tx: ${setupTx}`);
200
+ // await new Promise((r) => setTimeout(r, 5000));
201
+ // ============ Step 2: Create Session ============
202
+ console.log("\n[Step 2] Creating session...");
203
+ const createSessionData = gokiteAccountInterface.encodeFunctionData("createSession", [
204
+ sessionId,
205
+ agentSigner.address,
206
+ sessionRules(),
207
+ ]);
208
+ const createSessionTx = await sendWithTokenPayment(aaWallet, createSessionData);
209
+ console.log(` Session ID: ${sessionId}`);
210
+ console.log(` createSession tx: ${createSessionTx}`);
211
+ await new Promise((r) => setTimeout(r, 5000));
212
+ // Query session status after creation
213
+ 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}`);
237
+ 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)
243
+ 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));
125
249
  }
126
250
  main().catch((error) => {
127
251
  console.error(error);
@@ -78,8 +78,10 @@ class BundlerProvider {
78
78
  callGasLimit: BigInt(result.result.callGasLimit),
79
79
  verificationGasLimit: BigInt(result.result.verificationGasLimit),
80
80
  preVerificationGas: BigInt(result.result.preVerificationGas),
81
- maxFeePerGas: BigInt(result.result.maxFeePerGas || 1000000000n),
82
- maxPriorityFeePerGas: BigInt(result.result.maxPriorityFeePerGas || 1000000000n), // 1 gwei
81
+ // maxFeePerGas: BigInt(result.result.maxFeePerGas || 1000000000n), // 1 gwei
82
+ // maxPriorityFeePerGas: BigInt(result.result.maxPriorityFeePerGas || 1000000000n), // 1 gwei
83
+ maxFeePerGas: BigInt(1000000000n),
84
+ maxPriorityFeePerGas: BigInt(1000000000n),
83
85
  };
84
86
  }
85
87
  catch (error) {
@@ -314,6 +316,7 @@ class GokiteAASDK {
314
316
  // Update gas fields in packed format (verificationGasLimit first, callGasLimit second)
315
317
  userOp.accountGasLimits = (0, utils_1.packAccountGasLimits)(gasEstimate.verificationGasLimit, gasEstimate.callGasLimit);
316
318
  userOp.preVerificationGas = gasEstimate.preVerificationGas;
319
+ userOp.gasFees = (0, utils_1.packAccountGasLimits)(gasEstimate.maxPriorityFeePerGas, gasEstimate.maxFeePerGas);
317
320
  const userOpHash = await this.getUserOpHash(userOp);
318
321
  // Sign user operation
319
322
  const signature = await signFn(userOpHash);
@@ -426,8 +429,10 @@ class GokiteAASDK {
426
429
  gasEstimate.callGasLimit = gasEstimate.callGasLimit + 5000000n;
427
430
  gasEstimate.verificationGasLimit = gasEstimate.verificationGasLimit + 1000000n;
428
431
  gasEstimate.preVerificationGas = gasEstimate.preVerificationGas + 1000000n;
429
- gasEstimate.maxFeePerGas = BigInt(100000000);
430
- gasEstimate.maxPriorityFeePerGas = BigInt(1);
432
+ // gasEstimate.maxFeePerGas = BigInt(gasEstimate.maxFeePerGas || 1000000000n); // 1 gwei
433
+ // gasEstimate.maxPriorityFeePerGas = BigInt(gasEstimate.maxPriorityFeePerGas || 1000000000n); // 1 gwei
434
+ gasEstimate.maxFeePerGas = BigInt(1000000000n);
435
+ gasEstimate.maxPriorityFeePerGas = BigInt(1000000000n);
431
436
  userOp.gasFees = (0, utils_1.packAccountGasLimits)(gasEstimate.maxPriorityFeePerGas, gasEstimate.maxFeePerGas);
432
437
  // Pack gas limits and fees (note: verificationGasLimit first, callGasLimit second)
433
438
  userOp.accountGasLimits = (0, utils_1.packAccountGasLimits)(gasEstimate.verificationGasLimit, gasEstimate.callGasLimit);
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(5);
133
+ return BigInt(0);
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.8",
3
+ "version": "1.0.10",
4
4
  "description": "Simple and clean Account Abstraction SDK for Gokite",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",