@router402/sdk 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +430 -0
- package/dist/config.d.ts +35 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +491 -0
- package/dist/kernel.d.ts +49 -0
- package/dist/kernel.d.ts.map +1 -0
- package/dist/sdk.d.ts +153 -0
- package/dist/sdk.d.ts.map +1 -0
- package/dist/session-keys.d.ts +53 -0
- package/dist/session-keys.d.ts.map +1 -0
- package/dist/transactions.d.ts +12 -0
- package/dist/transactions.d.ts.map +1 -0
- package/dist/types.d.ts +236 -0
- package/dist/types.d.ts.map +1 -0
- package/package.json +56 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,491 @@
|
|
|
1
|
+
// src/config.ts
|
|
2
|
+
import { KERNEL_V3_1 } from "@zerodev/sdk/constants";
|
|
3
|
+
|
|
4
|
+
// src/types.ts
|
|
5
|
+
class SmartAccountError extends Error {
|
|
6
|
+
type;
|
|
7
|
+
constructor(type, message) {
|
|
8
|
+
super(message);
|
|
9
|
+
this.name = "SmartAccountError";
|
|
10
|
+
this.type = type;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
// src/config.ts
|
|
15
|
+
var ENTRY_POINT_ADDRESS = "0x0000000071727De22E5E9d8BAf0edAc6f37da032";
|
|
16
|
+
var DEFAULT_SESSION_KEY_VALIDITY = 365 * 24 * 60 * 60;
|
|
17
|
+
var KERNEL_VERSION = KERNEL_V3_1;
|
|
18
|
+
var DEFAULT_API_BASE_URL = "https://api.router402.xyz";
|
|
19
|
+
function resolveConfig(config) {
|
|
20
|
+
const chain = config.chain;
|
|
21
|
+
const chainId = chain?.id;
|
|
22
|
+
const pimlicoUrl = config.pimlicoApiKey && chainId ? `https://api.pimlico.io/v2/${chainId}/rpc?apikey=${config.pimlicoApiKey}` : undefined;
|
|
23
|
+
return {
|
|
24
|
+
chain,
|
|
25
|
+
chainId,
|
|
26
|
+
pimlicoApiKey: config.pimlicoApiKey,
|
|
27
|
+
pimlicoUrl,
|
|
28
|
+
entryPointVersion: config.entryPointVersion ?? "0.7",
|
|
29
|
+
sessionKeyValidityPeriod: config.sessionKeyValidityPeriod ?? DEFAULT_SESSION_KEY_VALIDITY,
|
|
30
|
+
apiBaseUrl: config.apiBaseUrl ?? DEFAULT_API_BASE_URL
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
function validateSmartAccountConfig(config) {
|
|
34
|
+
if (!config.chain) {
|
|
35
|
+
throw new SmartAccountError("NOT_CONFIGURED", "Chain is required for smart account operations. Provide `chain` in the configuration.");
|
|
36
|
+
}
|
|
37
|
+
if (!config.pimlicoApiKey) {
|
|
38
|
+
throw new SmartAccountError("NOT_CONFIGURED", "Pimlico API key is required for smart account operations. Provide `pimlicoApiKey` in the configuration.");
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
function validateConfig(_config) {}
|
|
42
|
+
// src/kernel.ts
|
|
43
|
+
import { signerToEcdsaValidator } from "@zerodev/ecdsa-validator";
|
|
44
|
+
import {
|
|
45
|
+
deserializePermissionAccount,
|
|
46
|
+
serializePermissionAccount,
|
|
47
|
+
toPermissionValidator
|
|
48
|
+
} from "@zerodev/permissions";
|
|
49
|
+
import {
|
|
50
|
+
toSignatureCallerPolicy,
|
|
51
|
+
toSudoPolicy,
|
|
52
|
+
toTimestampPolicy
|
|
53
|
+
} from "@zerodev/permissions/policies";
|
|
54
|
+
import { toECDSASigner } from "@zerodev/permissions/signers";
|
|
55
|
+
import { addressToEmptyAccount, createKernelAccount } from "@zerodev/sdk";
|
|
56
|
+
import { getEntryPoint } from "@zerodev/sdk/constants";
|
|
57
|
+
import { createSmartAccountClient } from "permissionless";
|
|
58
|
+
import { createPimlicoClient } from "permissionless/clients/pimlico";
|
|
59
|
+
import { createPublicClient, http } from "viem";
|
|
60
|
+
import { entryPoint07Address } from "viem/account-abstraction";
|
|
61
|
+
import { privateKeyToAccount } from "viem/accounts";
|
|
62
|
+
function createKernelPublicClient(config) {
|
|
63
|
+
return createPublicClient({
|
|
64
|
+
chain: config.chain,
|
|
65
|
+
transport: http()
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
function createPimlicoPaymasterClient(config) {
|
|
69
|
+
return createPimlicoClient({
|
|
70
|
+
chain: config.chain,
|
|
71
|
+
transport: http(config.pimlicoUrl),
|
|
72
|
+
entryPoint: {
|
|
73
|
+
address: entryPoint07Address,
|
|
74
|
+
version: "0.7"
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
async function createEcdsaValidator(walletClient, config) {
|
|
79
|
+
if (!walletClient.account) {
|
|
80
|
+
throw new SmartAccountError("USER_REJECTED", "Wallet client account is not available.");
|
|
81
|
+
}
|
|
82
|
+
const publicClient = createKernelPublicClient(config);
|
|
83
|
+
const entryPoint = getEntryPoint(config.entryPointVersion);
|
|
84
|
+
const ownerClient = walletClient;
|
|
85
|
+
return signerToEcdsaValidator(publicClient, {
|
|
86
|
+
signer: ownerClient,
|
|
87
|
+
entryPoint,
|
|
88
|
+
kernelVersion: KERNEL_VERSION
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
async function createKernelAccountFromWallet(walletClient, config) {
|
|
92
|
+
const publicClient = createKernelPublicClient(config);
|
|
93
|
+
const entryPoint = getEntryPoint(config.entryPointVersion);
|
|
94
|
+
const ecdsaValidator = await createEcdsaValidator(walletClient, config);
|
|
95
|
+
return createKernelAccount(publicClient, {
|
|
96
|
+
plugins: {
|
|
97
|
+
sudo: ecdsaValidator
|
|
98
|
+
},
|
|
99
|
+
entryPoint,
|
|
100
|
+
kernelVersion: KERNEL_VERSION
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
function createKernelSmartAccountClient(account, config) {
|
|
104
|
+
const pimlicoClient = createPimlicoPaymasterClient(config);
|
|
105
|
+
return createSmartAccountClient({
|
|
106
|
+
account,
|
|
107
|
+
chain: config.chain,
|
|
108
|
+
bundlerTransport: http(config.pimlicoUrl),
|
|
109
|
+
paymaster: pimlicoClient,
|
|
110
|
+
userOperation: {
|
|
111
|
+
estimateFeesPerGas: async () => {
|
|
112
|
+
const prices = await pimlicoClient.getUserOperationGasPrice();
|
|
113
|
+
return prices.fast;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
async function getKernelAccountAddress(walletClient, config) {
|
|
119
|
+
const account = await createKernelAccountFromWallet(walletClient, config);
|
|
120
|
+
return account.address;
|
|
121
|
+
}
|
|
122
|
+
async function isKernelAccountDeployed(address, config) {
|
|
123
|
+
const publicClient = createKernelPublicClient(config);
|
|
124
|
+
const code = await publicClient.getCode({ address });
|
|
125
|
+
return code !== undefined && code !== "0x";
|
|
126
|
+
}
|
|
127
|
+
async function createSessionKeyApproval(walletClient, sessionKeyAddress, config, expiresAt, allowedCallers) {
|
|
128
|
+
const publicClient = createKernelPublicClient(config);
|
|
129
|
+
const entryPoint = getEntryPoint(config.entryPointVersion);
|
|
130
|
+
const ecdsaValidator = await createEcdsaValidator(walletClient, config);
|
|
131
|
+
const emptyAccount = addressToEmptyAccount(sessionKeyAddress);
|
|
132
|
+
const emptySessionKeySigner = await toECDSASigner({ signer: emptyAccount });
|
|
133
|
+
const validUntil = Math.floor(expiresAt / 1000);
|
|
134
|
+
const policies = [
|
|
135
|
+
toSudoPolicy({}),
|
|
136
|
+
toTimestampPolicy({ validUntil }),
|
|
137
|
+
...allowedCallers && allowedCallers.length > 0 ? [toSignatureCallerPolicy({ allowedCallers })] : []
|
|
138
|
+
];
|
|
139
|
+
const permissionPlugin = await toPermissionValidator(publicClient, {
|
|
140
|
+
entryPoint,
|
|
141
|
+
signer: emptySessionKeySigner,
|
|
142
|
+
policies,
|
|
143
|
+
kernelVersion: KERNEL_VERSION
|
|
144
|
+
});
|
|
145
|
+
const sessionKeyAccount = await createKernelAccount(publicClient, {
|
|
146
|
+
entryPoint,
|
|
147
|
+
plugins: {
|
|
148
|
+
sudo: ecdsaValidator,
|
|
149
|
+
regular: permissionPlugin
|
|
150
|
+
},
|
|
151
|
+
kernelVersion: KERNEL_VERSION
|
|
152
|
+
});
|
|
153
|
+
return serializePermissionAccount(sessionKeyAccount);
|
|
154
|
+
}
|
|
155
|
+
async function createKernelClientFromSessionKey(sessionKeyPrivateKey, serializedApproval, config) {
|
|
156
|
+
const publicClient = createKernelPublicClient(config);
|
|
157
|
+
const entryPoint = getEntryPoint(config.entryPointVersion);
|
|
158
|
+
const sessionKeyAccount = privateKeyToAccount(sessionKeyPrivateKey);
|
|
159
|
+
const sessionKeySigner = await toECDSASigner({
|
|
160
|
+
signer: sessionKeyAccount
|
|
161
|
+
});
|
|
162
|
+
const kernelAccount = await deserializePermissionAccount(publicClient, entryPoint, KERNEL_VERSION, serializedApproval, sessionKeySigner);
|
|
163
|
+
return createKernelSmartAccountClient(kernelAccount, config);
|
|
164
|
+
}
|
|
165
|
+
// src/sdk.ts
|
|
166
|
+
import { encodeFunctionData, erc20Abi } from "viem";
|
|
167
|
+
|
|
168
|
+
// src/session-keys.ts
|
|
169
|
+
import { generatePrivateKey, privateKeyToAccount as privateKeyToAccount2 } from "viem/accounts";
|
|
170
|
+
function generateSessionKey(smartAccountAddress, ownerAddress, config) {
|
|
171
|
+
const privateKey = generatePrivateKey();
|
|
172
|
+
const account = privateKeyToAccount2(privateKey);
|
|
173
|
+
const now = Date.now();
|
|
174
|
+
const validityPeriodMs = config.sessionKeyValidityPeriod * 1000;
|
|
175
|
+
return {
|
|
176
|
+
privateKey,
|
|
177
|
+
publicKey: account.address,
|
|
178
|
+
createdAt: now,
|
|
179
|
+
expiresAt: now + validityPeriodMs,
|
|
180
|
+
smartAccountAddress,
|
|
181
|
+
ownerAddress,
|
|
182
|
+
isApproved: false
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
function isSessionKeyExpired(sessionKey) {
|
|
186
|
+
return Date.now() > sessionKey.expiresAt;
|
|
187
|
+
}
|
|
188
|
+
function isSessionKeyValid(sessionKey) {
|
|
189
|
+
if (!sessionKey)
|
|
190
|
+
return false;
|
|
191
|
+
if (isSessionKeyExpired(sessionKey))
|
|
192
|
+
return false;
|
|
193
|
+
if (!sessionKey.isApproved || !sessionKey.serializedSessionKey)
|
|
194
|
+
return false;
|
|
195
|
+
return true;
|
|
196
|
+
}
|
|
197
|
+
function canUseSessionKey(sessionKey) {
|
|
198
|
+
return sessionKey.isApproved && !!sessionKey.serializedSessionKey && !isSessionKeyExpired(sessionKey);
|
|
199
|
+
}
|
|
200
|
+
function getSessionKeyRemainingTime(sessionKey) {
|
|
201
|
+
const remaining = sessionKey.expiresAt - Date.now();
|
|
202
|
+
return Math.max(0, remaining);
|
|
203
|
+
}
|
|
204
|
+
function getSessionKeyAccount(sessionKey) {
|
|
205
|
+
return privateKeyToAccount2(sessionKey.privateKey);
|
|
206
|
+
}
|
|
207
|
+
function exportSessionKeyForBackend(sessionKey, chainId) {
|
|
208
|
+
if (!sessionKey.isApproved || !sessionKey.serializedSessionKey) {
|
|
209
|
+
return null;
|
|
210
|
+
}
|
|
211
|
+
return {
|
|
212
|
+
privateKey: sessionKey.privateKey,
|
|
213
|
+
serializedSessionKey: sessionKey.serializedSessionKey,
|
|
214
|
+
smartAccountAddress: sessionKey.smartAccountAddress,
|
|
215
|
+
chainId
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
function markSessionKeyApproved(sessionKey, serializedSessionKey) {
|
|
219
|
+
return {
|
|
220
|
+
...sessionKey,
|
|
221
|
+
isApproved: true,
|
|
222
|
+
serializedSessionKey
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// src/transactions.ts
|
|
227
|
+
async function sendOwnerTransaction(walletClient, calls, config) {
|
|
228
|
+
try {
|
|
229
|
+
const account = await createKernelAccountFromWallet(walletClient, config);
|
|
230
|
+
const client = createKernelSmartAccountClient(account, config);
|
|
231
|
+
const userOpHash = await client.sendUserOperation({
|
|
232
|
+
calls: calls.map((call) => ({
|
|
233
|
+
to: call.to,
|
|
234
|
+
data: call.data ?? "0x",
|
|
235
|
+
value: call.value ?? 0n
|
|
236
|
+
}))
|
|
237
|
+
});
|
|
238
|
+
const receipt = await client.waitForUserOperationReceipt({
|
|
239
|
+
hash: userOpHash
|
|
240
|
+
});
|
|
241
|
+
return {
|
|
242
|
+
success: true,
|
|
243
|
+
txHash: receipt.receipt.transactionHash,
|
|
244
|
+
userOpHash
|
|
245
|
+
};
|
|
246
|
+
} catch (error) {
|
|
247
|
+
return {
|
|
248
|
+
success: false,
|
|
249
|
+
error: error instanceof Error ? error.message : "Unknown error"
|
|
250
|
+
};
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
async function sendSessionKeyTransaction(sessionKeyPrivateKey, serializedApproval, calls, config) {
|
|
254
|
+
try {
|
|
255
|
+
const client = await createKernelClientFromSessionKey(sessionKeyPrivateKey, serializedApproval, config);
|
|
256
|
+
const userOpHash = await client.sendUserOperation({
|
|
257
|
+
calls: calls.map((call) => ({
|
|
258
|
+
to: call.to,
|
|
259
|
+
data: call.data ?? "0x",
|
|
260
|
+
value: call.value ?? 0n
|
|
261
|
+
}))
|
|
262
|
+
});
|
|
263
|
+
const receipt = await client.waitForUserOperationReceipt({
|
|
264
|
+
hash: userOpHash
|
|
265
|
+
});
|
|
266
|
+
return {
|
|
267
|
+
success: true,
|
|
268
|
+
txHash: receipt.receipt.transactionHash,
|
|
269
|
+
userOpHash
|
|
270
|
+
};
|
|
271
|
+
} catch (error) {
|
|
272
|
+
return {
|
|
273
|
+
success: false,
|
|
274
|
+
error: error instanceof Error ? error.message : "Unknown error"
|
|
275
|
+
};
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
// src/sdk.ts
|
|
280
|
+
class Router402Sdk {
|
|
281
|
+
config;
|
|
282
|
+
token = null;
|
|
283
|
+
constructor(userConfig) {
|
|
284
|
+
this.config = resolveConfig(userConfig);
|
|
285
|
+
if (userConfig.token) {
|
|
286
|
+
this.token = userConfig.token;
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
requireSmartAccountConfig() {
|
|
290
|
+
validateSmartAccountConfig(this.config);
|
|
291
|
+
return this.config;
|
|
292
|
+
}
|
|
293
|
+
getConfig() {
|
|
294
|
+
return this.config;
|
|
295
|
+
}
|
|
296
|
+
getChainId() {
|
|
297
|
+
const config = this.requireSmartAccountConfig();
|
|
298
|
+
return config.chainId;
|
|
299
|
+
}
|
|
300
|
+
async getSmartAccountAddress(walletClient) {
|
|
301
|
+
const config = this.requireSmartAccountConfig();
|
|
302
|
+
return getKernelAccountAddress(walletClient, config);
|
|
303
|
+
}
|
|
304
|
+
async isSmartAccountDeployed(address) {
|
|
305
|
+
const config = this.requireSmartAccountConfig();
|
|
306
|
+
return isKernelAccountDeployed(address, config);
|
|
307
|
+
}
|
|
308
|
+
async getSmartAccountInfo(walletClient, eoaAddress) {
|
|
309
|
+
const config = this.requireSmartAccountConfig();
|
|
310
|
+
const account = await createKernelAccountFromWallet(walletClient, config);
|
|
311
|
+
const isDeployed = await account.isDeployed();
|
|
312
|
+
return {
|
|
313
|
+
address: account.address,
|
|
314
|
+
eoaAddress,
|
|
315
|
+
isDeployed,
|
|
316
|
+
chainId: config.chainId
|
|
317
|
+
};
|
|
318
|
+
}
|
|
319
|
+
async getSmartAccountBalance(address) {
|
|
320
|
+
const config = this.requireSmartAccountConfig();
|
|
321
|
+
const publicClient = createKernelPublicClient(config);
|
|
322
|
+
return publicClient.getBalance({ address });
|
|
323
|
+
}
|
|
324
|
+
async deploySmartAccount(walletClient) {
|
|
325
|
+
this.requireSmartAccountConfig();
|
|
326
|
+
const address = await this.getSmartAccountAddress(walletClient);
|
|
327
|
+
return this.sendOwnerTransaction(walletClient, [
|
|
328
|
+
{ to: address, value: 0n }
|
|
329
|
+
]);
|
|
330
|
+
}
|
|
331
|
+
async sendOwnerTransaction(walletClient, calls) {
|
|
332
|
+
const config = this.requireSmartAccountConfig();
|
|
333
|
+
return sendOwnerTransaction(walletClient, calls, config);
|
|
334
|
+
}
|
|
335
|
+
async sendSessionKeyTransaction(sessionKey, calls) {
|
|
336
|
+
const config = this.requireSmartAccountConfig();
|
|
337
|
+
if (isSessionKeyExpired(sessionKey)) {
|
|
338
|
+
throw new SmartAccountError("SESSION_KEY_EXPIRED", "Session key has expired");
|
|
339
|
+
}
|
|
340
|
+
if (!sessionKey.isApproved || !sessionKey.serializedSessionKey) {
|
|
341
|
+
throw new SmartAccountError("SESSION_KEY_NOT_APPROVED", "Session key is not approved or missing serialized data");
|
|
342
|
+
}
|
|
343
|
+
return sendSessionKeyTransaction(sessionKey.privateKey, sessionKey.serializedSessionKey, calls, config);
|
|
344
|
+
}
|
|
345
|
+
async sendSessionKeyTransactionFromBackend(sessionKeyData, calls) {
|
|
346
|
+
const config = this.requireSmartAccountConfig();
|
|
347
|
+
return sendSessionKeyTransaction(sessionKeyData.privateKey, sessionKeyData.serializedSessionKey, calls, config);
|
|
348
|
+
}
|
|
349
|
+
generateSessionKey(smartAccountAddress, ownerAddress) {
|
|
350
|
+
const config = this.requireSmartAccountConfig();
|
|
351
|
+
return generateSessionKey(smartAccountAddress, ownerAddress, config);
|
|
352
|
+
}
|
|
353
|
+
async approveSessionKey(walletClient, sessionKey, allowedCallers) {
|
|
354
|
+
const config = this.requireSmartAccountConfig();
|
|
355
|
+
const callers = allowedCallers ?? sessionKey.allowedCallers;
|
|
356
|
+
const serializedApproval = await createSessionKeyApproval(walletClient, sessionKey.publicKey, config, sessionKey.expiresAt, callers);
|
|
357
|
+
return {
|
|
358
|
+
...markSessionKeyApproved(sessionKey, serializedApproval),
|
|
359
|
+
allowedCallers: callers
|
|
360
|
+
};
|
|
361
|
+
}
|
|
362
|
+
isSessionKeyValid(sessionKey) {
|
|
363
|
+
return isSessionKeyValid(sessionKey);
|
|
364
|
+
}
|
|
365
|
+
canUseSessionKey(sessionKey) {
|
|
366
|
+
return canUseSessionKey(sessionKey);
|
|
367
|
+
}
|
|
368
|
+
isSessionKeyExpired(sessionKey) {
|
|
369
|
+
return isSessionKeyExpired(sessionKey);
|
|
370
|
+
}
|
|
371
|
+
getSessionKeyRemainingTime(sessionKey) {
|
|
372
|
+
return getSessionKeyRemainingTime(sessionKey);
|
|
373
|
+
}
|
|
374
|
+
exportSessionKeyForBackend(sessionKey) {
|
|
375
|
+
const config = this.requireSmartAccountConfig();
|
|
376
|
+
return exportSessionKeyForBackend(sessionKey, config.chainId);
|
|
377
|
+
}
|
|
378
|
+
async enableSessionKeyOnChain(sessionKey, usdcAddress, smartAccountAddress) {
|
|
379
|
+
const config = this.requireSmartAccountConfig();
|
|
380
|
+
if (!sessionKey.isApproved || !sessionKey.serializedSessionKey) {
|
|
381
|
+
throw new SmartAccountError("SESSION_KEY_NOT_APPROVED", "Session key must be approved before enabling on-chain");
|
|
382
|
+
}
|
|
383
|
+
const approveData = encodeFunctionData({
|
|
384
|
+
abi: erc20Abi,
|
|
385
|
+
functionName: "approve",
|
|
386
|
+
args: [smartAccountAddress, BigInt(0)]
|
|
387
|
+
});
|
|
388
|
+
return sendSessionKeyTransaction(sessionKey.privateKey, sessionKey.serializedSessionKey, [{ to: usdcAddress, value: BigInt(0), data: approveData }], config);
|
|
389
|
+
}
|
|
390
|
+
async setupAccount(walletClient, eoaAddress, options) {
|
|
391
|
+
this.requireSmartAccountConfig();
|
|
392
|
+
const { usdcAddress, existingSessionKey, onStatus } = options;
|
|
393
|
+
onStatus?.("initializing");
|
|
394
|
+
const info = await this.getSmartAccountInfo(walletClient, eoaAddress);
|
|
395
|
+
if (!info.isDeployed) {
|
|
396
|
+
onStatus?.("deploying");
|
|
397
|
+
const deployResult = await this.deploySmartAccount(walletClient);
|
|
398
|
+
if (!deployResult.success) {
|
|
399
|
+
throw new SmartAccountError("DEPLOYMENT_FAILED", "Failed to deploy Smart Account");
|
|
400
|
+
}
|
|
401
|
+
info.isDeployed = true;
|
|
402
|
+
}
|
|
403
|
+
let sessionKey;
|
|
404
|
+
if (existingSessionKey && this.canUseSessionKey(existingSessionKey)) {
|
|
405
|
+
sessionKey = existingSessionKey;
|
|
406
|
+
} else {
|
|
407
|
+
onStatus?.("creating_session_key");
|
|
408
|
+
const newKey = this.generateSessionKey(info.address, eoaAddress);
|
|
409
|
+
onStatus?.("approving_session_key");
|
|
410
|
+
sessionKey = await this.approveSessionKey(walletClient, newKey);
|
|
411
|
+
if (!sessionKey.serializedSessionKey) {
|
|
412
|
+
throw new SmartAccountError("SESSION_KEY_NOT_APPROVED", "Failed to approve session key");
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
onStatus?.("enabling_session_key");
|
|
416
|
+
const enableResult = await this.enableSessionKeyOnChain(sessionKey, usdcAddress, info.address);
|
|
417
|
+
if (!enableResult.success) {
|
|
418
|
+
throw new SmartAccountError("UNKNOWN_ERROR", `Failed to enable session key on-chain: ${enableResult.error}`);
|
|
419
|
+
}
|
|
420
|
+
onStatus?.("complete");
|
|
421
|
+
return { info, sessionKey, enableResult };
|
|
422
|
+
}
|
|
423
|
+
setToken(token) {
|
|
424
|
+
this.token = token;
|
|
425
|
+
}
|
|
426
|
+
async chat(prompt, options) {
|
|
427
|
+
if (!this.token) {
|
|
428
|
+
throw new SmartAccountError("NOT_CONFIGURED", "JWT token is required. Pass `token` in the constructor or call setToken() before using chat().");
|
|
429
|
+
}
|
|
430
|
+
const body = {
|
|
431
|
+
model: options?.model ?? "anthropic/claude-opus-4.6",
|
|
432
|
+
messages: [{ role: "user", content: prompt }],
|
|
433
|
+
...options?.temperature !== undefined && {
|
|
434
|
+
temperature: options.temperature
|
|
435
|
+
},
|
|
436
|
+
...options?.max_tokens !== undefined && {
|
|
437
|
+
max_tokens: options.max_tokens
|
|
438
|
+
}
|
|
439
|
+
};
|
|
440
|
+
const response = await fetch(`${this.config.apiBaseUrl}/v1/chat/completions`, {
|
|
441
|
+
method: "POST",
|
|
442
|
+
headers: {
|
|
443
|
+
"Content-Type": "application/json",
|
|
444
|
+
Authorization: `Bearer ${this.token}`
|
|
445
|
+
},
|
|
446
|
+
body: JSON.stringify(body)
|
|
447
|
+
});
|
|
448
|
+
if (!response.ok) {
|
|
449
|
+
const errorData = await response.json().catch(() => null);
|
|
450
|
+
const error = errorData?.error;
|
|
451
|
+
const message = error?.message ?? `Chat request failed with status ${response.status}`;
|
|
452
|
+
throw new SmartAccountError("UNKNOWN_ERROR", message);
|
|
453
|
+
}
|
|
454
|
+
const data = await response.json();
|
|
455
|
+
return data.choices[0].message.content;
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
function createRouter402Sdk(config) {
|
|
459
|
+
return new Router402Sdk(config);
|
|
460
|
+
}
|
|
461
|
+
export {
|
|
462
|
+
validateSmartAccountConfig,
|
|
463
|
+
validateConfig,
|
|
464
|
+
sendSessionKeyTransaction,
|
|
465
|
+
sendOwnerTransaction,
|
|
466
|
+
resolveConfig,
|
|
467
|
+
markSessionKeyApproved,
|
|
468
|
+
isSessionKeyValid,
|
|
469
|
+
isSessionKeyExpired,
|
|
470
|
+
isKernelAccountDeployed,
|
|
471
|
+
getSessionKeyRemainingTime,
|
|
472
|
+
getSessionKeyAccount,
|
|
473
|
+
getKernelAccountAddress,
|
|
474
|
+
generateSessionKey,
|
|
475
|
+
exportSessionKeyForBackend,
|
|
476
|
+
createSessionKeyApproval,
|
|
477
|
+
createRouter402Sdk,
|
|
478
|
+
createPimlicoPaymasterClient,
|
|
479
|
+
createKernelSmartAccountClient,
|
|
480
|
+
createKernelPublicClient,
|
|
481
|
+
createKernelClientFromSessionKey,
|
|
482
|
+
createKernelAccountFromWallet,
|
|
483
|
+
createEcdsaValidator,
|
|
484
|
+
canUseSessionKey,
|
|
485
|
+
SmartAccountError,
|
|
486
|
+
Router402Sdk,
|
|
487
|
+
KERNEL_VERSION,
|
|
488
|
+
ENTRY_POINT_ADDRESS,
|
|
489
|
+
DEFAULT_SESSION_KEY_VALIDITY,
|
|
490
|
+
DEFAULT_API_BASE_URL
|
|
491
|
+
};
|
package/dist/kernel.d.ts
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { createKernelAccount } from "@zerodev/sdk";
|
|
2
|
+
import { createPimlicoClient } from "permissionless/clients/pimlico";
|
|
3
|
+
import type { Address, Chain, Hex, PublicClient, Transport, WalletClient } from "viem";
|
|
4
|
+
import type { SmartAccountResolvedConfig as ResolvedConfig } from "./types";
|
|
5
|
+
/**
|
|
6
|
+
* Create a public client for kernel account operations.
|
|
7
|
+
*/
|
|
8
|
+
export declare function createKernelPublicClient(config: ResolvedConfig): PublicClient<Transport, Chain>;
|
|
9
|
+
/**
|
|
10
|
+
* Create a Pimlico client for bundler and paymaster operations.
|
|
11
|
+
*/
|
|
12
|
+
export declare function createPimlicoPaymasterClient(config: ResolvedConfig): ReturnType<typeof createPimlicoClient>;
|
|
13
|
+
/**
|
|
14
|
+
* Create an ECDSA validator from a wallet client.
|
|
15
|
+
*/
|
|
16
|
+
export declare function createEcdsaValidator(walletClient: WalletClient, config: ResolvedConfig): Promise<import("@zerodev/sdk").KernelValidator<"ECDSAValidator">>;
|
|
17
|
+
/**
|
|
18
|
+
* Create a Kernel Smart Account instance from a wallet client (owner).
|
|
19
|
+
*/
|
|
20
|
+
export declare function createKernelAccountFromWallet(walletClient: WalletClient, config: ResolvedConfig): Promise<import("@zerodev/sdk").CreateKernelAccountReturnType<"0.7">>;
|
|
21
|
+
/**
|
|
22
|
+
* Create a Smart Account Client with Pimlico bundler and paymaster.
|
|
23
|
+
*/
|
|
24
|
+
export declare function createKernelSmartAccountClient<TAccount extends Awaited<ReturnType<typeof createKernelAccount>>>(account: TAccount, config: ResolvedConfig): import("permissionless").SmartAccountClient<import("viem").HttpTransport<undefined, false>, Chain, TAccount, undefined, undefined>;
|
|
25
|
+
/**
|
|
26
|
+
* Get the Kernel Smart Account address for a wallet.
|
|
27
|
+
*/
|
|
28
|
+
export declare function getKernelAccountAddress(walletClient: WalletClient, config: ResolvedConfig): Promise<Address>;
|
|
29
|
+
/**
|
|
30
|
+
* Check if the Kernel Smart Account is deployed.
|
|
31
|
+
*/
|
|
32
|
+
export declare function isKernelAccountDeployed(address: Address, config: ResolvedConfig): Promise<boolean>;
|
|
33
|
+
/**
|
|
34
|
+
* Create a session key approval (serialized permission account).
|
|
35
|
+
* This creates an approval that the session key holder can use to send transactions.
|
|
36
|
+
*
|
|
37
|
+
* @param walletClient - The owner wallet client
|
|
38
|
+
* @param sessionKeyAddress - The session key's public address
|
|
39
|
+
* @param config - Resolved SDK configuration
|
|
40
|
+
* @param expiresAt - Expiry timestamp in milliseconds (enforced on-chain via toTimestampPolicy)
|
|
41
|
+
* @param allowedCallers - Optional array of addresses allowed to use this session key (enforced on-chain via toSignatureCallerPolicy)
|
|
42
|
+
*/
|
|
43
|
+
export declare function createSessionKeyApproval(walletClient: WalletClient, sessionKeyAddress: Address, config: ResolvedConfig, expiresAt: number, allowedCallers?: Address[]): Promise<string>;
|
|
44
|
+
/**
|
|
45
|
+
* Deserialize and create a kernel client from a session key.
|
|
46
|
+
* Used by the backend or frontend to send transactions with the session key.
|
|
47
|
+
*/
|
|
48
|
+
export declare function createKernelClientFromSessionKey(sessionKeyPrivateKey: Hex, serializedApproval: string, config: ResolvedConfig): Promise<import("permissionless").SmartAccountClient<import("viem").HttpTransport<undefined, false>, Chain, import("@zerodev/sdk").CreateKernelAccountReturnType<"0.7">, undefined, undefined>>;
|
|
49
|
+
//# sourceMappingURL=kernel.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"kernel.d.ts","sourceRoot":"","sources":["../src/kernel.ts"],"names":[],"mappings":"AAYA,OAAO,EAAyB,mBAAmB,EAAE,MAAM,cAAc,CAAC;AAG1E,OAAO,EAAE,mBAAmB,EAAE,MAAM,gCAAgC,CAAC;AACrE,OAAO,KAAK,EAEV,OAAO,EACP,KAAK,EACL,GAAG,EACH,YAAY,EACZ,SAAS,EACT,YAAY,EACb,MAAM,MAAM,CAAC;AAKd,OAAO,KAAK,EAAE,0BAA0B,IAAI,cAAc,EAAE,MAAM,SAAS,CAAC;AAG5E;;GAEG;AACH,wBAAgB,wBAAwB,CACtC,MAAM,EAAE,cAAc,GACrB,YAAY,CAAC,SAAS,EAAE,KAAK,CAAC,CAKhC;AAED;;GAEG;AACH,wBAAgB,4BAA4B,CAC1C,MAAM,EAAE,cAAc,GACrB,UAAU,CAAC,OAAO,mBAAmB,CAAC,CASxC;AAED;;GAEG;AACH,wBAAsB,oBAAoB,CACxC,YAAY,EAAE,YAAY,EAC1B,MAAM,EAAE,cAAc,qEAsBvB;AAED;;GAEG;AACH,wBAAsB,6BAA6B,CACjD,YAAY,EAAE,YAAY,EAC1B,MAAM,EAAE,cAAc,wEAavB;AAED;;GAEG;AACH,wBAAgB,8BAA8B,CAC5C,QAAQ,SAAS,OAAO,CAAC,UAAU,CAAC,OAAO,mBAAmB,CAAC,CAAC,EAChE,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,cAAc,sIAe1C;AAED;;GAEG;AACH,wBAAsB,uBAAuB,CAC3C,YAAY,EAAE,YAAY,EAC1B,MAAM,EAAE,cAAc,GACrB,OAAO,CAAC,OAAO,CAAC,CAGlB;AAED;;GAEG;AACH,wBAAsB,uBAAuB,CAC3C,OAAO,EAAE,OAAO,EAChB,MAAM,EAAE,cAAc,GACrB,OAAO,CAAC,OAAO,CAAC,CAIlB;AAED;;;;;;;;;GASG;AACH,wBAAsB,wBAAwB,CAC5C,YAAY,EAAE,YAAY,EAC1B,iBAAiB,EAAE,OAAO,EAC1B,MAAM,EAAE,cAAc,EACtB,SAAS,EAAE,MAAM,EACjB,cAAc,CAAC,EAAE,OAAO,EAAE,GACzB,OAAO,CAAC,MAAM,CAAC,CAyCjB;AAED;;;GAGG;AACH,wBAAsB,gCAAgC,CACpD,oBAAoB,EAAE,GAAG,EACzB,kBAAkB,EAAE,MAAM,EAC1B,MAAM,EAAE,cAAc,kMAsBvB"}
|
package/dist/sdk.d.ts
ADDED
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
import type { Address, WalletClient } from "viem";
|
|
2
|
+
import type { CallData, ChatOptions, ResolvedConfig, Router402Config, SessionKeyData, SessionKeyForBackend, SetupAccountOptions, SetupAccountResult, SmartAccountInfo, TransactionExecutionResult } from "./types.js";
|
|
3
|
+
/**
|
|
4
|
+
* Router402 SDK - Main entry point for chat completions and smart account operations
|
|
5
|
+
*
|
|
6
|
+
* Usage (chat only):
|
|
7
|
+
* ```typescript
|
|
8
|
+
* const sdk = new Router402Sdk({ token: "your-jwt-token" });
|
|
9
|
+
* const response = await sdk.chat("What is ERC-4337?");
|
|
10
|
+
* ```
|
|
11
|
+
*
|
|
12
|
+
* Usage (with smart accounts):
|
|
13
|
+
* ```typescript
|
|
14
|
+
* const sdk = new Router402Sdk({
|
|
15
|
+
* chain: baseSepolia,
|
|
16
|
+
* pimlicoApiKey: "your-api-key",
|
|
17
|
+
* token: "your-jwt-token",
|
|
18
|
+
* });
|
|
19
|
+
* ```
|
|
20
|
+
*/
|
|
21
|
+
export declare class Router402Sdk {
|
|
22
|
+
private config;
|
|
23
|
+
private token;
|
|
24
|
+
constructor(userConfig: Router402Config);
|
|
25
|
+
/**
|
|
26
|
+
* Ensures chain and pimlicoApiKey are configured.
|
|
27
|
+
* Returns the config with smart account fields guaranteed.
|
|
28
|
+
* Throws SmartAccountError if not configured.
|
|
29
|
+
*/
|
|
30
|
+
private requireSmartAccountConfig;
|
|
31
|
+
/**
|
|
32
|
+
* Get the resolved configuration
|
|
33
|
+
*/
|
|
34
|
+
getConfig(): ResolvedConfig;
|
|
35
|
+
/**
|
|
36
|
+
* Get the chain ID (requires chain to be configured)
|
|
37
|
+
*/
|
|
38
|
+
getChainId(): number;
|
|
39
|
+
/**
|
|
40
|
+
* Get the deterministic smart account address for a wallet
|
|
41
|
+
*/
|
|
42
|
+
getSmartAccountAddress(walletClient: WalletClient): Promise<Address>;
|
|
43
|
+
/**
|
|
44
|
+
* Check if a smart account is deployed on-chain
|
|
45
|
+
*/
|
|
46
|
+
isSmartAccountDeployed(address: Address): Promise<boolean>;
|
|
47
|
+
/**
|
|
48
|
+
* Get complete smart account information
|
|
49
|
+
*/
|
|
50
|
+
getSmartAccountInfo(walletClient: WalletClient, eoaAddress: Address): Promise<SmartAccountInfo>;
|
|
51
|
+
/**
|
|
52
|
+
* Get the ETH balance of a smart account
|
|
53
|
+
*/
|
|
54
|
+
getSmartAccountBalance(address: Address): Promise<bigint>;
|
|
55
|
+
/**
|
|
56
|
+
* Deploy a smart account (sends a no-op transaction to trigger deployment)
|
|
57
|
+
*/
|
|
58
|
+
deploySmartAccount(walletClient: WalletClient): Promise<TransactionExecutionResult>;
|
|
59
|
+
/**
|
|
60
|
+
* Send a transaction using the owner wallet
|
|
61
|
+
*/
|
|
62
|
+
sendOwnerTransaction(walletClient: WalletClient, calls: CallData[]): Promise<TransactionExecutionResult>;
|
|
63
|
+
/**
|
|
64
|
+
* Send a transaction using a session key
|
|
65
|
+
*/
|
|
66
|
+
sendSessionKeyTransaction(sessionKey: SessionKeyData, calls: CallData[]): Promise<TransactionExecutionResult>;
|
|
67
|
+
/**
|
|
68
|
+
* Send a transaction using raw session key data (for backend use)
|
|
69
|
+
*/
|
|
70
|
+
sendSessionKeyTransactionFromBackend(sessionKeyData: SessionKeyForBackend, calls: CallData[]): Promise<TransactionExecutionResult>;
|
|
71
|
+
/**
|
|
72
|
+
* Generate a new session key pair
|
|
73
|
+
*/
|
|
74
|
+
generateSessionKey(smartAccountAddress: Address, ownerAddress: Address): SessionKeyData;
|
|
75
|
+
/**
|
|
76
|
+
* Approve a session key (requires owner signature)
|
|
77
|
+
* Returns the updated session key with approval data.
|
|
78
|
+
*
|
|
79
|
+
* The session key's expiresAt is enforced on-chain via toTimestampPolicy.
|
|
80
|
+
* If allowedCallers are provided (or already set on the session key),
|
|
81
|
+
* only those addresses can submit transactions with this session key.
|
|
82
|
+
*
|
|
83
|
+
* @param walletClient - The owner wallet client
|
|
84
|
+
* @param sessionKey - The session key to approve
|
|
85
|
+
* @param allowedCallers - Optional addresses allowed to use this session key (enforced on-chain)
|
|
86
|
+
*/
|
|
87
|
+
approveSessionKey(walletClient: WalletClient, sessionKey: SessionKeyData, allowedCallers?: Address[]): Promise<SessionKeyData>;
|
|
88
|
+
/**
|
|
89
|
+
* Check if a session key is valid and can be used
|
|
90
|
+
*/
|
|
91
|
+
isSessionKeyValid(sessionKey: SessionKeyData | undefined): boolean;
|
|
92
|
+
/**
|
|
93
|
+
* Check if a session key can be used for transactions
|
|
94
|
+
*/
|
|
95
|
+
canUseSessionKey(sessionKey: SessionKeyData): boolean;
|
|
96
|
+
/**
|
|
97
|
+
* Check if a session key is expired
|
|
98
|
+
*/
|
|
99
|
+
isSessionKeyExpired(sessionKey: SessionKeyData): boolean;
|
|
100
|
+
/**
|
|
101
|
+
* Get remaining validity time for a session key in milliseconds
|
|
102
|
+
*/
|
|
103
|
+
getSessionKeyRemainingTime(sessionKey: SessionKeyData): number;
|
|
104
|
+
/**
|
|
105
|
+
* Export session key data for backend use
|
|
106
|
+
*/
|
|
107
|
+
exportSessionKeyForBackend(sessionKey: SessionKeyData): SessionKeyForBackend | null;
|
|
108
|
+
/**
|
|
109
|
+
* Enable a session key on-chain by sending a dummy ERC20 approve transaction.
|
|
110
|
+
* The first UserOp through `sendSessionKeyTransaction` activates the
|
|
111
|
+
* permission validator module on-chain via the `enableSignature` mechanism.
|
|
112
|
+
*
|
|
113
|
+
* @param sessionKey - An approved session key with serializedSessionKey
|
|
114
|
+
* @param usdcAddress - The USDC contract address on the target chain
|
|
115
|
+
* @param smartAccountAddress - The smart account address (used as the spender in the dummy approve)
|
|
116
|
+
*/
|
|
117
|
+
enableSessionKeyOnChain(sessionKey: SessionKeyData, usdcAddress: Address, smartAccountAddress: Address): Promise<TransactionExecutionResult>;
|
|
118
|
+
/**
|
|
119
|
+
* Full account setup flow: get info → deploy → generate session key → approve → enable on-chain.
|
|
120
|
+
*
|
|
121
|
+
* This method orchestrates all the steps needed to set up a smart account with a session key.
|
|
122
|
+
* It does NOT handle storage or backend authorization — those are app-specific concerns.
|
|
123
|
+
*
|
|
124
|
+
* @param walletClient - The owner wallet client
|
|
125
|
+
* @param eoaAddress - The owner EOA address
|
|
126
|
+
* @param options - Setup options including USDC address and optional callbacks
|
|
127
|
+
*/
|
|
128
|
+
setupAccount(walletClient: WalletClient, eoaAddress: Address, options: SetupAccountOptions): Promise<SetupAccountResult>;
|
|
129
|
+
/**
|
|
130
|
+
* Set the JWT token for authenticated API requests
|
|
131
|
+
*/
|
|
132
|
+
setToken(token: string): void;
|
|
133
|
+
/**
|
|
134
|
+
* Send a chat completion request to the Router402 API.
|
|
135
|
+
*
|
|
136
|
+
* @param prompt - The user message to send
|
|
137
|
+
* @param options - Optional model, temperature, and max_tokens overrides
|
|
138
|
+
* @returns The assistant's response content
|
|
139
|
+
*
|
|
140
|
+
* @example
|
|
141
|
+
* ```typescript
|
|
142
|
+
* sdk.setToken(jwtToken);
|
|
143
|
+
* const response = await sdk.chat("What is ERC-4337?");
|
|
144
|
+
* console.log(response);
|
|
145
|
+
* ```
|
|
146
|
+
*/
|
|
147
|
+
chat(prompt: string, options?: ChatOptions): Promise<string>;
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Create a new Router402Sdk instance
|
|
151
|
+
*/
|
|
152
|
+
export declare function createRouter402Sdk(config: Router402Config): Router402Sdk;
|
|
153
|
+
//# sourceMappingURL=sdk.d.ts.map
|