gokite-aa-sdk 1.0.3 ā 1.0.4
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.d.ts +5 -0
- package/dist/config.js +5 -1
- package/dist/example-token-paymaster.d.ts +1 -0
- package/dist/example-token-paymaster.js +33 -0
- package/dist/example.js +48 -32
- package/dist/gokite-aa-sdk.d.ts +24 -3
- package/dist/gokite-aa-sdk.js +151 -6
- package/dist/types.d.ts +17 -0
- package/package.json +1 -1
- package/dist/example.old.d.ts +0 -47
- package/dist/example.old.js +0 -426
package/dist/config.d.ts
CHANGED
|
@@ -5,5 +5,10 @@ export interface NetworkConfig {
|
|
|
5
5
|
accountImplementation: string;
|
|
6
6
|
bundlerUrl?: string;
|
|
7
7
|
paymaster?: string;
|
|
8
|
+
supportedTokens: {
|
|
9
|
+
address: string;
|
|
10
|
+
symbol: string;
|
|
11
|
+
decimals: number;
|
|
12
|
+
}[];
|
|
8
13
|
}
|
|
9
14
|
export declare const NETWORKS: Record<string, NetworkConfig>;
|
package/dist/config.js
CHANGED
|
@@ -7,6 +7,10 @@ exports.NETWORKS = {
|
|
|
7
7
|
entryPoint: '0x4337084D9E255Ff0702461CF8895CE9E3b5Ff108',
|
|
8
8
|
accountFactory: '0xF0Fc19F0dc393867F19351d25EDfc5E099561cb7',
|
|
9
9
|
accountImplementation: '0x93F5310eFd0f09db0666CA5146E63CA6Cdc6FC21',
|
|
10
|
-
paymaster: '
|
|
10
|
+
paymaster: '0x9Adcbf85D5c724611a490Ba9eDc4d38d6F39e92d',
|
|
11
|
+
supportedTokens: [
|
|
12
|
+
{ address: '0x0000000000000000000000000000000000000000', symbol: 'KITE', decimals: 18 },
|
|
13
|
+
{ address: '0x0fF5393387ad2f9f691FD6Fd28e07E3969e27e63', symbol: 'Test USD', decimals: 18 },
|
|
14
|
+
],
|
|
11
15
|
},
|
|
12
16
|
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const ethers_1 = require("ethers");
|
|
4
|
+
const gokite_aa_sdk_1 = require("./gokite-aa-sdk");
|
|
5
|
+
async function simpleExample() {
|
|
6
|
+
const sdk = new gokite_aa_sdk_1.GokiteAASDK('kite_testnet', 'https://rpc-testnet.gokite.ai', 'https://bundler-service.staging.gokite.ai/rpc/');
|
|
7
|
+
const signer = new ethers_1.ethers.Wallet(process.env.PRIVATE_KEY);
|
|
8
|
+
const request = {
|
|
9
|
+
target: '0x0000000000000000000000000000000000000000',
|
|
10
|
+
value: BigInt(0),
|
|
11
|
+
callData: '0x'
|
|
12
|
+
};
|
|
13
|
+
console.log('š Estimate transaction...');
|
|
14
|
+
const estimate = await sdk.estimateUserOperation(signer.address, request);
|
|
15
|
+
console.log('Total cost:', estimate.totalCostKITEFormatted);
|
|
16
|
+
console.log('Cost with token:', estimate.supportedTokens);
|
|
17
|
+
console.log('Sponsorship available:', estimate.sponsorshipAvailable);
|
|
18
|
+
console.log('Remaining sponsorships:', estimate.remainingSponsorships);
|
|
19
|
+
console.log('Payment options:', estimate.supportedTokens.map(t => `${t.tokenSymbol}: ${t.formattedCost}`));
|
|
20
|
+
// Step 2: Select payment method and execute
|
|
21
|
+
const tokenAddress = estimate.sponsorshipAvailable
|
|
22
|
+
? '0x0000000000000000000000000000000000000000' // Use sponsorship/KITE
|
|
23
|
+
: '0x0fF5393387ad2f9f691FD6Fd28e07E3969e27e63'; // Use Test USD
|
|
24
|
+
console.log('\nš Execute transaction...');
|
|
25
|
+
const result = await sdk.sendUserOperationWithPayment(signer.address, request, estimate.userOp, tokenAddress, async (hash) => signer.signMessage(ethers_1.ethers.getBytes(hash)));
|
|
26
|
+
console.log('ā
Done!');
|
|
27
|
+
console.log('UserOp Hash:', result.userOpHash);
|
|
28
|
+
console.log('Status:', result.status.status);
|
|
29
|
+
}
|
|
30
|
+
// Run
|
|
31
|
+
if (require.main === module) {
|
|
32
|
+
simpleExample().catch(console.error);
|
|
33
|
+
}
|
package/dist/example.js
CHANGED
|
@@ -437,42 +437,58 @@ async function exampleUsage() {
|
|
|
437
437
|
const userEoa = '0x4A50DCA63d541372ad36E5A36F1D542d51164F19';
|
|
438
438
|
// actual token recipient address
|
|
439
439
|
const toAddress = '0x4A50DCA63d541372ad36E5A36F1D542d51164F19';
|
|
440
|
-
// 1. Transfer ERC20 tokens
|
|
441
|
-
console.log('\n=== 1. ERC20 Transfer ===');
|
|
442
|
-
const transferResult = await transferERC20(
|
|
443
|
-
|
|
440
|
+
// // 1. Transfer ERC20 tokens
|
|
441
|
+
// console.log('\n=== 1. ERC20 Transfer ===');
|
|
442
|
+
// const transferResult = await transferERC20(
|
|
443
|
+
// userEoa,
|
|
444
|
+
// toAddress,
|
|
445
|
+
// '10',
|
|
446
|
+
// ADDRESSES.SETTLEMENT_TOKEN,
|
|
447
|
+
// 18,
|
|
448
|
+
// signFunction
|
|
449
|
+
// );
|
|
450
|
+
// console.log('Transfer result:', transferResult);
|
|
444
451
|
// 2. Deploy KitePass
|
|
445
452
|
console.log('\n=== 2. Deploy KitePass ===');
|
|
446
453
|
const deployResult = await deployKitePass(userEoa, signFunction);
|
|
447
454
|
console.log('Deploy result:', deployResult);
|
|
448
|
-
if (deployResult.success && deployResult.proxyAddress) {
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
//
|
|
459
|
-
|
|
460
|
-
//
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
455
|
+
// if (deployResult.success && deployResult.proxyAddress) {
|
|
456
|
+
// // 3. Configure spending rules
|
|
457
|
+
// console.log('\n=== 3. Configure Spending Rules ===');
|
|
458
|
+
// const configResult = await configureSpendingRules(
|
|
459
|
+
// userEoa,
|
|
460
|
+
// deployResult.proxyAddress,
|
|
461
|
+
// signFunction
|
|
462
|
+
// );
|
|
463
|
+
// console.log('Config result:', configResult);
|
|
464
|
+
// // 4. View spending rules
|
|
465
|
+
// console.log('\n=== 4. View Spending Rules ===');
|
|
466
|
+
// const rulesResult = await viewSpendingRules(deployResult.proxyAddress);
|
|
467
|
+
// console.log('Rules result:', rulesResult);
|
|
468
|
+
// }
|
|
469
|
+
// // 5. Register service
|
|
470
|
+
// console.log('\n=== 5. Register Service ===');
|
|
471
|
+
// // serviceId, get from backend
|
|
472
|
+
// const serviceId = ethers.hexlify(randomBytes(32));
|
|
473
|
+
// const sdk = initializeSDK();
|
|
474
|
+
// const serviceOwner = sdk.getAccountAddress(userEoa);
|
|
475
|
+
// const serviceResult = await registerService(
|
|
476
|
+
// userEoa,
|
|
477
|
+
// serviceId,
|
|
478
|
+
// {
|
|
479
|
+
// serviceOwner: serviceOwner,
|
|
480
|
+
// priceModel: 0,
|
|
481
|
+
// unitPrice: 100,
|
|
482
|
+
// // provider Id, bytes32
|
|
483
|
+
// provider: ethers.hexlify(randomBytes(32)),
|
|
484
|
+
// // service metadata, currently not used
|
|
485
|
+
// metadata: '0x',
|
|
486
|
+
// name: 'Test Service',
|
|
487
|
+
// isPublic: true
|
|
488
|
+
// },
|
|
489
|
+
// signFunction
|
|
490
|
+
// );
|
|
491
|
+
// console.log('Service result:', serviceResult);
|
|
476
492
|
}
|
|
477
493
|
// Run example if executed directly
|
|
478
494
|
if (require.main === module) {
|
package/dist/gokite-aa-sdk.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { UserOperation, UserOperationRequest, BatchUserOperationRequest, UserOperationGasEstimate, UserOperationStatus, SignFunction, PollingOptions } from './types';
|
|
1
|
+
import { UserOperation, UserOperationRequest, BatchUserOperationRequest, UserOperationGasEstimate, UserOperationStatus, SignFunction, PollingOptions, UserOperationEstimate } from './types';
|
|
2
2
|
/**
|
|
3
3
|
* Main Gokite AA SDK class
|
|
4
4
|
*/
|
|
@@ -38,7 +38,7 @@ export declare class GokiteAASDK {
|
|
|
38
38
|
/**
|
|
39
39
|
* Create user operation
|
|
40
40
|
*/
|
|
41
|
-
createUserOperation(owner: string, request: UserOperationRequest | BatchUserOperationRequest, salt?: bigint, paymasterAddress?: string): Promise<Partial<UserOperation>>;
|
|
41
|
+
createUserOperation(owner: string, request: UserOperationRequest | BatchUserOperationRequest, salt?: bigint, paymasterAddress?: string, tokenAddress?: string): Promise<Partial<UserOperation>>;
|
|
42
42
|
/**
|
|
43
43
|
* Estimate gas for user operation
|
|
44
44
|
*/
|
|
@@ -46,7 +46,7 @@ export declare class GokiteAASDK {
|
|
|
46
46
|
/**
|
|
47
47
|
* Sign and send user operation
|
|
48
48
|
*/
|
|
49
|
-
sendUserOperation(owner: string, request: UserOperationRequest | BatchUserOperationRequest, signFn: SignFunction, salt?: bigint, paymasterAddress?: string): Promise<string>;
|
|
49
|
+
sendUserOperation(owner: string, request: UserOperationRequest | BatchUserOperationRequest, signFn: SignFunction, salt?: bigint, paymasterAddress?: string, tokenAddress?: string): Promise<string>;
|
|
50
50
|
/**
|
|
51
51
|
* Send user operation directly to EntryPoint contract (bypassing bundler)
|
|
52
52
|
* Note: This requires a signer to pay for gas. For now, we'll try with the read-only provider.
|
|
@@ -60,9 +60,30 @@ export declare class GokiteAASDK {
|
|
|
60
60
|
* Enhanced polling for user operation status with detailed result parsing
|
|
61
61
|
*/
|
|
62
62
|
pollUserOperationStatus(userOpHash: string, options?: PollingOptions): Promise<UserOperationStatus>;
|
|
63
|
+
/**
|
|
64
|
+
* Estimate user operation costs and get paymaster information
|
|
65
|
+
*/
|
|
66
|
+
estimateUserOperation(owner: string, request: UserOperationRequest | BatchUserOperationRequest): Promise<UserOperationEstimate>;
|
|
67
|
+
/**
|
|
68
|
+
* Get paymaster information including sponsorship status and supported tokens
|
|
69
|
+
*/
|
|
70
|
+
private getPaymasterInfo;
|
|
63
71
|
/**
|
|
64
72
|
* Send user operation and wait for completion with detailed status
|
|
65
73
|
*/
|
|
74
|
+
/**
|
|
75
|
+
* Send user operation with specified payment token and wait for completion
|
|
76
|
+
*/
|
|
77
|
+
sendUserOperationWithPayment(owner: string, request: UserOperationRequest | BatchUserOperationRequest, baseUserOp: Partial<UserOperation>, // base userOp from estimate
|
|
78
|
+
tokenAddress: string, // token address for payment
|
|
79
|
+
signFn: SignFunction, salt?: bigint, pollingOptions?: PollingOptions): Promise<{
|
|
80
|
+
userOpHash: string;
|
|
81
|
+
status: UserOperationStatus;
|
|
82
|
+
}>;
|
|
83
|
+
/**
|
|
84
|
+
* Add ERC20 approve operations to the request (approve 0 first, then max)
|
|
85
|
+
*/
|
|
86
|
+
private addApproveOperation;
|
|
66
87
|
sendUserOperationAndWait(owner: string, request: UserOperationRequest | BatchUserOperationRequest, signFn: SignFunction, salt?: bigint, paymasterAddress?: string, pollingOptions?: PollingOptions): Promise<{
|
|
67
88
|
userOpHash: string;
|
|
68
89
|
status: UserOperationStatus;
|
package/dist/gokite-aa-sdk.js
CHANGED
|
@@ -254,7 +254,7 @@ class GokiteAASDK {
|
|
|
254
254
|
/**
|
|
255
255
|
* Create user operation
|
|
256
256
|
*/
|
|
257
|
-
async createUserOperation(owner, request, salt, paymasterAddress) {
|
|
257
|
+
async createUserOperation(owner, request, salt, paymasterAddress, tokenAddress) {
|
|
258
258
|
const actualSalt = salt || (0, utils_1.generateSalt)();
|
|
259
259
|
const accountAddress = this.getAccountAddress(owner, actualSalt);
|
|
260
260
|
const isDeployed = await this.isAccountDeloyed(accountAddress);
|
|
@@ -274,9 +274,13 @@ class GokiteAASDK {
|
|
|
274
274
|
paymasterAddress = this.config.paymaster;
|
|
275
275
|
}
|
|
276
276
|
// Pack paymaster data (paymasterAddress, paymasterVerificationGasLimit, postOpGasLimit, paymasterData)
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
277
|
+
let paymasterAndData = '0x';
|
|
278
|
+
if (paymasterAddress) {
|
|
279
|
+
// For token payments, append token address to paymaster data
|
|
280
|
+
tokenAddress = tokenAddress || this.config.supportedTokens[1].address;
|
|
281
|
+
const paymasterData = ethers_1.ethers.solidityPacked(['address'], [tokenAddress]);
|
|
282
|
+
paymasterAndData = (0, utils_1.packPaymasterAndData)(paymasterAddress, BigInt(500000), BigInt(500000), paymasterData);
|
|
283
|
+
}
|
|
280
284
|
return {
|
|
281
285
|
sender: accountAddress,
|
|
282
286
|
nonce: await this.getAccountNonce(accountAddress),
|
|
@@ -300,9 +304,9 @@ class GokiteAASDK {
|
|
|
300
304
|
/**
|
|
301
305
|
* Sign and send user operation
|
|
302
306
|
*/
|
|
303
|
-
async sendUserOperation(owner, request, signFn, salt, paymasterAddress) {
|
|
307
|
+
async sendUserOperation(owner, request, signFn, salt, paymasterAddress, tokenAddress) {
|
|
304
308
|
// Create user operation
|
|
305
|
-
const userOp = await this.createUserOperation(owner, request, salt, paymasterAddress);
|
|
309
|
+
const userOp = await this.createUserOperation(owner, request, salt, paymasterAddress, tokenAddress);
|
|
306
310
|
// Add dummy signature for gas estimation
|
|
307
311
|
const userOpWithDummy = (0, utils_1.createUserOpForEstimation)(userOp);
|
|
308
312
|
// Estimate gas using bundler
|
|
@@ -410,9 +414,150 @@ class GokiteAASDK {
|
|
|
410
414
|
}
|
|
411
415
|
throw new Error(`UserOp polling timeout: ${userOpHash} (attempt ${retryCount})`);
|
|
412
416
|
}
|
|
417
|
+
/**
|
|
418
|
+
* Estimate user operation costs and get paymaster information
|
|
419
|
+
*/
|
|
420
|
+
async estimateUserOperation(owner, request) {
|
|
421
|
+
console.log('Estimating UserOp costs...');
|
|
422
|
+
const paymasterAddress = this.config.paymaster;
|
|
423
|
+
try {
|
|
424
|
+
// Always estimate with paymaster for higher gas limits
|
|
425
|
+
const userOp = await this.createUserOperation(owner, request, undefined, paymasterAddress);
|
|
426
|
+
console.log('[estimateUserOperation]userOp:', userOp);
|
|
427
|
+
const gasEstimate = await this.estimateGas(userOp);
|
|
428
|
+
gasEstimate.callGasLimit = gasEstimate.callGasLimit + 5000000n;
|
|
429
|
+
gasEstimate.verificationGasLimit = gasEstimate.verificationGasLimit + 1000000n;
|
|
430
|
+
gasEstimate.preVerificationGas = gasEstimate.preVerificationGas + 1000000n;
|
|
431
|
+
gasEstimate.maxFeePerGas = BigInt(10000000);
|
|
432
|
+
// Calculate total cost in KITE
|
|
433
|
+
const totalGas = gasEstimate.callGasLimit + gasEstimate.verificationGasLimit + gasEstimate.preVerificationGas;
|
|
434
|
+
const totalCostWei = totalGas * gasEstimate.maxFeePerGas;
|
|
435
|
+
const totalCostKITE = totalCostWei.toString();
|
|
436
|
+
const totalCostKITEFormatted = ethers_1.ethers.formatEther(totalCostWei);
|
|
437
|
+
// Get paymaster info
|
|
438
|
+
const { sponsorshipAvailable, remainingSponsorships, supportedTokens } = await this.getPaymasterInfo(paymasterAddress, userOp.sender, totalCostWei);
|
|
439
|
+
return {
|
|
440
|
+
totalCostKITE,
|
|
441
|
+
totalCostKITEFormatted,
|
|
442
|
+
gasEstimate,
|
|
443
|
+
sponsorshipAvailable,
|
|
444
|
+
remainingSponsorships,
|
|
445
|
+
supportedTokens,
|
|
446
|
+
paymasterAddress,
|
|
447
|
+
userOp
|
|
448
|
+
};
|
|
449
|
+
}
|
|
450
|
+
catch (error) {
|
|
451
|
+
throw new types_1.AASDKError(classifyError(error, 'estimate user operation'));
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
/**
|
|
455
|
+
* Get paymaster information including sponsorship status and supported tokens
|
|
456
|
+
*/
|
|
457
|
+
async getPaymasterInfo(paymasterAddress, userAddress, estimatedCost) {
|
|
458
|
+
const paymasterABI = [
|
|
459
|
+
'function maxSponsoredTransactions() view returns (uint256)',
|
|
460
|
+
'function userSponsorship(address user) view returns (uint256)',
|
|
461
|
+
'function supportedTokens(address token) view returns (uint256)',
|
|
462
|
+
'function maxCostPerSponsoredTransaction() view returns (uint256)'
|
|
463
|
+
];
|
|
464
|
+
const paymaster = new ethers_1.ethers.Contract(paymasterAddress, paymasterABI, this.ethersProvider);
|
|
465
|
+
// Get sponsorship info
|
|
466
|
+
const [maxSponsorships, userSponsorships, maxCostPerSponsorship] = await Promise.all([
|
|
467
|
+
paymaster.maxSponsoredTransactions(),
|
|
468
|
+
paymaster.userSponsorship(userAddress),
|
|
469
|
+
paymaster.maxCostPerSponsoredTransaction()
|
|
470
|
+
]);
|
|
471
|
+
const remainingSponsorships = Math.max(0, Number(maxSponsorships) - Number(userSponsorships));
|
|
472
|
+
const sponsorshipAvailable = remainingSponsorships > 0 && estimatedCost <= maxCostPerSponsorship;
|
|
473
|
+
// Check supported tokens from config
|
|
474
|
+
const supportedTokens = [];
|
|
475
|
+
for (const token of this.config.supportedTokens) {
|
|
476
|
+
try {
|
|
477
|
+
const exchangeRate = await paymaster.supportedTokens(token.address);
|
|
478
|
+
if (exchangeRate > 0) {
|
|
479
|
+
const tokenCost = (estimatedCost * BigInt(1e18)) / exchangeRate;
|
|
480
|
+
const formattedCost = `${ethers_1.ethers.formatUnits(tokenCost, token.decimals)} ${token.symbol}`;
|
|
481
|
+
supportedTokens.push({
|
|
482
|
+
tokenAddress: token.address,
|
|
483
|
+
tokenSymbol: token.symbol,
|
|
484
|
+
tokenDecimals: token.decimals,
|
|
485
|
+
estimatedCost: tokenCost.toString(),
|
|
486
|
+
formattedCost
|
|
487
|
+
});
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
catch (error) {
|
|
491
|
+
console.warn(`Failed to check token ${token.symbol}:`, error);
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
return {
|
|
495
|
+
sponsorshipAvailable,
|
|
496
|
+
remainingSponsorships,
|
|
497
|
+
supportedTokens
|
|
498
|
+
};
|
|
499
|
+
}
|
|
413
500
|
/**
|
|
414
501
|
* Send user operation and wait for completion with detailed status
|
|
415
502
|
*/
|
|
503
|
+
/**
|
|
504
|
+
* Send user operation with specified payment token and wait for completion
|
|
505
|
+
*/
|
|
506
|
+
async sendUserOperationWithPayment(owner, request, baseUserOp, // base userOp from estimate
|
|
507
|
+
tokenAddress, // token address for payment
|
|
508
|
+
signFn, salt, pollingOptions) {
|
|
509
|
+
console.log('Sending UserOp with token:', tokenAddress);
|
|
510
|
+
let finalRequest = request;
|
|
511
|
+
let finalUserOp = { ...baseUserOp };
|
|
512
|
+
// If using ERC20 token (not 0x0), add approve operations
|
|
513
|
+
if (tokenAddress !== '0x0000000000000000000000000000000000000000') {
|
|
514
|
+
// Add approve operations to the request
|
|
515
|
+
finalRequest = await this.addApproveOperation(owner, request, tokenAddress, this.config.paymaster);
|
|
516
|
+
// Rebuild userOp with approve operations
|
|
517
|
+
finalUserOp = await this.createUserOperation(owner, finalRequest, salt, this.config.paymaster, tokenAddress);
|
|
518
|
+
// Copy gas estimates from base userOp
|
|
519
|
+
finalUserOp.accountGasLimits = baseUserOp.accountGasLimits;
|
|
520
|
+
finalUserOp.preVerificationGas = baseUserOp.preVerificationGas;
|
|
521
|
+
finalUserOp.gasFees = baseUserOp.gasFees;
|
|
522
|
+
}
|
|
523
|
+
// Get user operation hash
|
|
524
|
+
const userOpHash = await this.getUserOpHash(finalUserOp);
|
|
525
|
+
// Sign user operation
|
|
526
|
+
const signature = await signFn(userOpHash);
|
|
527
|
+
finalUserOp.signature = signature;
|
|
528
|
+
// Send to bundler
|
|
529
|
+
const sentUserOpHash = await this.provider.sendUserOperation(finalUserOp, this.config.entryPoint);
|
|
530
|
+
console.log(`UserOp sent: ${sentUserOpHash}`);
|
|
531
|
+
// Poll for status
|
|
532
|
+
const status = await this.pollUserOperationStatus(sentUserOpHash, pollingOptions);
|
|
533
|
+
return {
|
|
534
|
+
userOpHash: sentUserOpHash,
|
|
535
|
+
status
|
|
536
|
+
};
|
|
537
|
+
}
|
|
538
|
+
/**
|
|
539
|
+
* Add ERC20 approve operations to the request (approve 0 first, then max)
|
|
540
|
+
*/
|
|
541
|
+
async addApproveOperation(owner, request, tokenAddress, paymasterAddress) {
|
|
542
|
+
// Create approve call data - first approve 0, then approve max
|
|
543
|
+
const approve0CallData = (0, utils_1.encodeFunctionCall)(['function approve(address,uint256) returns (bool)'], 'approve', [paymasterAddress, '0']);
|
|
544
|
+
const approveMaxCallData = (0, utils_1.encodeFunctionCall)(['function approve(address,uint256) returns (bool)'], 'approve', [paymasterAddress, ethers_1.ethers.MaxUint256.toString()]);
|
|
545
|
+
// Convert single request to batch if needed
|
|
546
|
+
if ('target' in request) {
|
|
547
|
+
return {
|
|
548
|
+
targets: [tokenAddress, tokenAddress, request.target],
|
|
549
|
+
values: [BigInt(0), BigInt(0), request.value || BigInt(0)],
|
|
550
|
+
callDatas: [approve0CallData, approveMaxCallData, request.callData]
|
|
551
|
+
};
|
|
552
|
+
}
|
|
553
|
+
else {
|
|
554
|
+
return {
|
|
555
|
+
targets: [tokenAddress, tokenAddress, ...request.targets],
|
|
556
|
+
values: [BigInt(0), BigInt(0), ...(request.values || [])],
|
|
557
|
+
callDatas: [approve0CallData, approveMaxCallData, ...request.callDatas]
|
|
558
|
+
};
|
|
559
|
+
}
|
|
560
|
+
}
|
|
416
561
|
async sendUserOperationAndWait(owner, request, signFn, salt, paymasterAddress, pollingOptions) {
|
|
417
562
|
console.log('Sending UserOp and waiting for completion...');
|
|
418
563
|
// Step 1: Send user operation
|
package/dist/types.d.ts
CHANGED
|
@@ -41,6 +41,23 @@ export interface PollingOptions {
|
|
|
41
41
|
timeout?: number;
|
|
42
42
|
maxRetries?: number;
|
|
43
43
|
}
|
|
44
|
+
export interface PaymasterTokenCost {
|
|
45
|
+
tokenAddress: string;
|
|
46
|
+
tokenSymbol?: string;
|
|
47
|
+
tokenDecimals?: number;
|
|
48
|
+
estimatedCost: string;
|
|
49
|
+
formattedCost: string;
|
|
50
|
+
}
|
|
51
|
+
export interface UserOperationEstimate {
|
|
52
|
+
totalCostKITE: string;
|
|
53
|
+
totalCostKITEFormatted: string;
|
|
54
|
+
gasEstimate: UserOperationGasEstimate;
|
|
55
|
+
sponsorshipAvailable: boolean;
|
|
56
|
+
remainingSponsorships: number;
|
|
57
|
+
supportedTokens: PaymasterTokenCost[];
|
|
58
|
+
paymasterAddress?: string;
|
|
59
|
+
userOp: Partial<UserOperation>;
|
|
60
|
+
}
|
|
44
61
|
export interface AAError {
|
|
45
62
|
type: 'ESTIMATE_GAS_FAILED' | 'SEND_USEROP_FAILED' | 'INSUFFICIENT_FUNDS' | 'INVALID_SIGNATURE' | 'BUNDLER_ERROR' | 'NETWORK_ERROR' | 'UNKNOWN_ERROR';
|
|
46
63
|
message: string;
|
package/package.json
CHANGED
package/dist/example.old.d.ts
DELETED
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
import { GokiteAASDK } from './gokite-aa-sdk';
|
|
2
|
-
import "dotenv/config";
|
|
3
|
-
declare function handleAAError(error: any): {
|
|
4
|
-
type: string;
|
|
5
|
-
message: string;
|
|
6
|
-
details?: any;
|
|
7
|
-
};
|
|
8
|
-
/**
|
|
9
|
-
* ClientAgentVault Integration Tests
|
|
10
|
-
*
|
|
11
|
-
* these tests show how to use the AA wallet to:
|
|
12
|
-
* 1. deploy the ClientAgentVault proxy contract (via performCreate)
|
|
13
|
-
* 2. configure spending rules (Spending Rules)
|
|
14
|
-
* 3. view spending rules (read-only)
|
|
15
|
-
* 4. withdraw funds (withdrawFunds)
|
|
16
|
-
* 5. check token balance (read-only)
|
|
17
|
-
*
|
|
18
|
-
* how to use:
|
|
19
|
-
* - modify the contract addresses and parameters as needed
|
|
20
|
-
*/
|
|
21
|
-
declare function clientAgentVaultIntegrationTests(): Promise<void>;
|
|
22
|
-
/**
|
|
23
|
-
* Test 1: Deploy ClientAgentVault Proxy via AA wallet's performCreate
|
|
24
|
-
*/
|
|
25
|
-
declare function DeployClientAgentVaultProxy(sdk: GokiteAASDK, eoa: string, aa: string, signFunction: any): Promise<string | null>;
|
|
26
|
-
/**
|
|
27
|
-
* Test 2: Configure Spending Rules
|
|
28
|
-
*/
|
|
29
|
-
declare function ConfigureSpendingRules(sdk: GokiteAASDK, owner: string, proxyAddress: string, signFunction: any): Promise<void>;
|
|
30
|
-
/**
|
|
31
|
-
* Test 3: View Spending Rules (Read-only, no transaction sent)
|
|
32
|
-
*/
|
|
33
|
-
declare function ViewSpendingRules(sdk: GokiteAASDK, proxyAddress: string): Promise<void>;
|
|
34
|
-
/**
|
|
35
|
-
* Test 4: Withdraw Funds
|
|
36
|
-
*/
|
|
37
|
-
declare function WithdrawFunds(sdk: GokiteAASDK, owner: string, proxyAddress: string, signFunction: any): Promise<void>;
|
|
38
|
-
/**
|
|
39
|
-
* Test 5: Check Token Balance (Read-only)
|
|
40
|
-
*/
|
|
41
|
-
declare function CheckTokenBalance(sdk: GokiteAASDK, proxyAddress: string): Promise<void>;
|
|
42
|
-
/**
|
|
43
|
-
* Legacy example for basic AA operations
|
|
44
|
-
*/
|
|
45
|
-
declare function basicExample(): Promise<void>;
|
|
46
|
-
export { clientAgentVaultIntegrationTests, basicExample, DeployClientAgentVaultProxy, ConfigureSpendingRules, ViewSpendingRules, WithdrawFunds, CheckTokenBalance, handleAAError, };
|
|
47
|
-
export declare const example: typeof basicExample;
|
package/dist/example.old.js
DELETED
|
@@ -1,426 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.example = exports.handleAAError = exports.CheckTokenBalance = exports.WithdrawFunds = exports.ViewSpendingRules = exports.ConfigureSpendingRules = exports.DeployClientAgentVaultProxy = exports.basicExample = exports.clientAgentVaultIntegrationTests = void 0;
|
|
4
|
-
const gokite_aa_sdk_1 = require("./gokite-aa-sdk");
|
|
5
|
-
const types_1 = require("./types");
|
|
6
|
-
const ethers_1 = require("ethers");
|
|
7
|
-
require("dotenv/config");
|
|
8
|
-
// Contract addresses on kite_testnet
|
|
9
|
-
const ADDRESSES = {
|
|
10
|
-
SETTLEMENT_TOKEN: '0x0fF5393387ad2f9f691FD6Fd28e07E3969e27e63',
|
|
11
|
-
SETTLEMENT_CONTRACT: '0x8d9FaD78d5Ce247aA01C140798B9558fd64a63E3',
|
|
12
|
-
CLIENT_AGENT_VAULT_IMPL: '0xB5AAFCC6DD4DFc2B80fb8BCcf406E1a2Fd559e23'
|
|
13
|
-
};
|
|
14
|
-
function handleAAError(error) {
|
|
15
|
-
if (error instanceof types_1.AASDKError) {
|
|
16
|
-
return {
|
|
17
|
-
type: error.type,
|
|
18
|
-
message: error.message,
|
|
19
|
-
details: error.details
|
|
20
|
-
};
|
|
21
|
-
}
|
|
22
|
-
// Fallback for other errors
|
|
23
|
-
return {
|
|
24
|
-
type: 'UNKNOWN_ERROR',
|
|
25
|
-
message: error.message || 'An unknown error occurred',
|
|
26
|
-
details: error
|
|
27
|
-
};
|
|
28
|
-
}
|
|
29
|
-
exports.handleAAError = handleAAError;
|
|
30
|
-
/**
|
|
31
|
-
* Get TransparentUpgradeableProxy bytecode
|
|
32
|
-
*/
|
|
33
|
-
function getTransparentProxyBytecode() {
|
|
34
|
-
// This is the bytecode for TransparentUpgradeableProxy
|
|
35
|
-
return '0x60a0604052610b278038038061001481610293565b928339810160608282031261028e5761002c826102b8565b610038602084016102b8565b604084015190936001600160401b03821161028e570182601f8201121561028e5780519061006d610068836102cc565b610293565b938285526020838301011161028e5760005b828110610279575050602060009184010152803b15610258577f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc80546001600160a01b0319166001600160a01b0383169081179091557fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b600080a281511561023f5760008083602061013595519101845af43d15610237573d91610125610068846102cc565b9283523d6000602085013e6102e7565b505b604051906104918083016001600160401b0381118482101761022157602092849261067684396001600160a01b031681520301906000f080156102155760018060a01b031680608052600080516020610b07833981519152547f7e644d79422f17c01e4894b5f4f588d331ebfa28653d42ae832dc59e38c9798f6040805160018060a01b0384168152846020820152a181156101ff576001600160a01b03191617600080516020610b078339815191525560405161032d908161034982396080518160070152f35b633173bdd160e11b600052600060045260246000fd5b6040513d6000823e3d90fd5b634e487b7160e01b600052604160045260246000fd5b6060916102e7565b505034156101375763b398979f60e01b60005260046000fd5b634c9c8ce360e01b60009081526001600160a01b0391909116600452602490fd5b8060208092840101518282880101520161007f565b600080fd5b6040519190601f01601f191682016001600160401b0381118382101761022157604052565b51906001600160a01b038216820361028e57565b6001600160401b03811161022157601f01601f191660200190565b9061030d57508051156102fc57805190602001fd5b630a12f52160e11b60005260046000fd5b8151158061033f575b61031e575090565b639996b31560e01b60009081526001600160a01b0391909116600452602490fd5b50803b1561031656fe6080604052337f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031603610069576000356001600160e01b03191663278f794360e11b1461005f576334ad5dbb60e21b60005260046000fd5b610067610113565b005b7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc5460009081906001600160a01b0316368280378136915af43d6000803e156100b1573d6000f35b3d6000fd5b634e487b7160e01b600052604160045260246000fd5b6040519190601f01601f1916820167ffffffffffffffff8111838210176100f257604052565b6100b6565b67ffffffffffffffff81116100f257601f01601f191660200190565b3660041161019d57604036600319011261019d576004356001600160a01b0381169081900361019d576024359067ffffffffffffffff821161019d573660238301121561019d5781600401359061017161016c836100f7565b6100cc565b91808352366024828601011161019d57602081600092602461019b970183870137840101526101a2565b565b600080fd5b90813b15610239577f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc80546001600160a01b0319166001600160a01b0384169081179091557fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b600080a280511561021f5761021c9161025b565b50565b50503461022857565b63b398979f60e01b60005260046000fd5b50634c9c8ce360e01b60009081526001600160a01b0391909116600452602490fd5b60008061028f93602081519101845af43d15610292573d9161027f61016c846100f7565b9283523d6000602085013e610296565b90565b6060915b906102bc57508051156102ab57805190602001fd5b630a12f52160e11b60005260046000fd5b815115806102ee575b6102cd575090565b639996b31560e01b60009081526001600160a01b0391909116600452602490fd5b50803b156102c556fea2646970667358221220597147005a6fe654561cbab25a93153cc233180473a65a90bd427f0c1f41018764736f6c634300081c003360803460bc57601f61049138819003918201601f19168301916001600160401b0383118484101760c15780849260209460405283398101031260bc57516001600160a01b0381169081900360bc57801560a657600080546001600160a01b031981168317825560405192916001600160a01b03909116907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09080a36103b990816100d88239f35b631e4fbdf760e01b600052600060045260246000fd5b600080fd5b634e487b7160e01b600052604160045260246000fdfe6080604052600436101561001257600080fd5b6000803560e01c8063715018a6146102875780638da5cb5b146102605780639623609d1461012f578063ad3cb1cc146100e25763f2fde38b1461005457600080fd5b346100df5760203660031901126100df576004356001600160a01b038116908190036100dd5761008261035a565b80156100c95781546001600160a01b03198116821783556001600160a01b03167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08380a380f35b631e4fbdf760e01b82526004829052602482fd5b505b80fd5b50346100df57806003193601126100df575061012b6040516101056040826102e1565b60058152640352e302e360dc1b6020820152604051918291602083526020830190610319565b0390f35b5060603660031901126100df576004356001600160a01b038116908190036100dd576024356001600160a01b038116908190036102405760443567ffffffffffffffff811161025c573660238201121561025c57806004013567ffffffffffffffff8111610248576040518593929091906101b4601f8301601f1916602001846102e1565b81835236602483830101116102445781859260246020930183860137830101526101dc61035a565b833b156102405761021293839260405180968194829363278f794360e11b84526004840152604060248401526044830190610319565b039134905af18015610233576102255780f35b61022e916102e1565b388180f35b50604051903d90823e3d90fd5b8280fd5b8480fd5b634e487b7160e01b85526041600452602485fd5b8380fd5b50346100df57806003193601126100df57546040516001600160a01b039091168152602090f35b50346100df57806003193601126100df576102a061035a565b80546001600160a01b03198116825581906001600160a01b03167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08280a380f35b90601f8019910116810190811067ffffffffffffffff82111761030357604052565b634e487b7160e01b600052604160045260246000fd5b919082519283825260005b848110610345575050826000602080949584010152601f8019910116010190565b80602080928401015182828601015201610324565b6000546001600160a01b0316330361036e57565b63118cdaa760e01b6000523360045260246000fdfea26469706673582212207bc32ec723f6be34b228b59aef2ef61b4e6a8eb5bc67fcdd495248566e3b6e0c64736f6c634300081c0033b53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103';
|
|
36
|
-
}
|
|
37
|
-
/**
|
|
38
|
-
* ClientAgentVault Integration Tests
|
|
39
|
-
*
|
|
40
|
-
* these tests show how to use the AA wallet to:
|
|
41
|
-
* 1. deploy the ClientAgentVault proxy contract (via performCreate)
|
|
42
|
-
* 2. configure spending rules (Spending Rules)
|
|
43
|
-
* 3. view spending rules (read-only)
|
|
44
|
-
* 4. withdraw funds (withdrawFunds)
|
|
45
|
-
* 5. check token balance (read-only)
|
|
46
|
-
*
|
|
47
|
-
* how to use:
|
|
48
|
-
* - modify the contract addresses and parameters as needed
|
|
49
|
-
*/
|
|
50
|
-
async function clientAgentVaultIntegrationTests() {
|
|
51
|
-
console.log('\n=== ClientAgentVault Integration Tests ===');
|
|
52
|
-
// Initialize SDK
|
|
53
|
-
const sdk = new gokite_aa_sdk_1.GokiteAASDK('kite_testnet', 'https://rpc-testnet.gokite.ai', 'https://bundler-service.staging.gokite.ai/rpc/');
|
|
54
|
-
// user's EOA address and AA wallet address
|
|
55
|
-
const eoa = '0x4A50DCA63d541372ad36E5A36F1D542d51164F19';
|
|
56
|
-
const aa = sdk.getAccountAddress(eoa);
|
|
57
|
-
console.log('AA Wallet Address:', aa);
|
|
58
|
-
// Sign function, replace with particle or privy sdk
|
|
59
|
-
const signFunction = async (userOpHash) => {
|
|
60
|
-
const signer_pk = process.env.PRIVATE_KEY;
|
|
61
|
-
const signer = new ethers_1.ethers.Wallet(signer_pk);
|
|
62
|
-
return signer.signMessage(ethers_1.ethers.getBytes(userOpHash));
|
|
63
|
-
};
|
|
64
|
-
// 1. Deploy ClientAgentVault Proxy via performCreate
|
|
65
|
-
console.log('\n--- Test 1: Deploy ClientAgentVault Proxy ---');
|
|
66
|
-
const deployedProxyAddress = await DeployClientAgentVaultProxy(sdk, eoa, aa, signFunction);
|
|
67
|
-
if (!deployedProxyAddress) {
|
|
68
|
-
console.log('ā Failed to deploy proxy, cannot continue with other tests');
|
|
69
|
-
return;
|
|
70
|
-
}
|
|
71
|
-
console.log(`ā
Using deployed proxy address: ${deployedProxyAddress}`);
|
|
72
|
-
// 2. Configure Spending Rules
|
|
73
|
-
console.log('\n--- Test 2: Configure Spending Rules ---');
|
|
74
|
-
await ConfigureSpendingRules(sdk, eoa, deployedProxyAddress, signFunction);
|
|
75
|
-
// 3. View Spending Rules
|
|
76
|
-
console.log('\n--- Test 3: View Spending Rules ---');
|
|
77
|
-
await ViewSpendingRules(sdk, deployedProxyAddress);
|
|
78
|
-
// 4. Withdraw Funds
|
|
79
|
-
console.log('\n--- Test 4: Withdraw Funds ---');
|
|
80
|
-
await WithdrawFunds(sdk, eoa, deployedProxyAddress, signFunction);
|
|
81
|
-
// 5. Check Token Balance
|
|
82
|
-
console.log('\n--- Test 5: Check Token Balance ---');
|
|
83
|
-
await CheckTokenBalance(sdk, deployedProxyAddress);
|
|
84
|
-
}
|
|
85
|
-
exports.clientAgentVaultIntegrationTests = clientAgentVaultIntegrationTests;
|
|
86
|
-
/**
|
|
87
|
-
* Test 1: Deploy ClientAgentVault Proxy via AA wallet's performCreate
|
|
88
|
-
*/
|
|
89
|
-
async function DeployClientAgentVaultProxy(sdk, eoa, aa, signFunction) {
|
|
90
|
-
try {
|
|
91
|
-
// Prepare initialization data for ClientAgentVault
|
|
92
|
-
const initializeCallData = ethers_1.ethers.Interface.from([
|
|
93
|
-
'function initialize(address allowedToken, address owner)'
|
|
94
|
-
]).encodeFunctionData('initialize', [
|
|
95
|
-
ADDRESSES.SETTLEMENT_TOKEN,
|
|
96
|
-
aa // owner (AA wallet address)
|
|
97
|
-
]);
|
|
98
|
-
// Create TransparentUpgradeableProxy deployment bytecode
|
|
99
|
-
// constructor(address _logic, address admin_, bytes memory _data)
|
|
100
|
-
const proxyConstructorData = ethers_1.ethers.AbiCoder.defaultAbiCoder().encode(['address', 'address', 'bytes'], [
|
|
101
|
-
ADDRESSES.CLIENT_AGENT_VAULT_IMPL,
|
|
102
|
-
aa,
|
|
103
|
-
initializeCallData // initialization data
|
|
104
|
-
]);
|
|
105
|
-
// Full deployment bytecode = creation code + constructor params
|
|
106
|
-
const transparentProxyBytecode = getTransparentProxyBytecode();
|
|
107
|
-
const fullInitCode = transparentProxyBytecode + proxyConstructorData.slice(2);
|
|
108
|
-
// Create UserOperation to call performCreate
|
|
109
|
-
const deployRequest = {
|
|
110
|
-
target: aa,
|
|
111
|
-
value: 0n,
|
|
112
|
-
callData: ethers_1.ethers.Interface.from([
|
|
113
|
-
'function performCreate(uint256 value, bytes calldata initCode) returns (address)'
|
|
114
|
-
]).encodeFunctionData('performCreate', [
|
|
115
|
-
0n,
|
|
116
|
-
fullInitCode // proxy deployment bytecode + constructor params
|
|
117
|
-
])
|
|
118
|
-
};
|
|
119
|
-
console.log('Deploying ClientAgentVault proxy...');
|
|
120
|
-
console.log('- Implementation:', ADDRESSES.CLIENT_AGENT_VAULT_IMPL);
|
|
121
|
-
console.log('- AllowedToken:', ADDRESSES.SETTLEMENT_TOKEN);
|
|
122
|
-
const result = await sdk.sendUserOperationAndWait(eoa, deployRequest, signFunction);
|
|
123
|
-
if (result.status.status === 'success') {
|
|
124
|
-
console.log('ā
Proxy deployed successfully!');
|
|
125
|
-
console.log('Transaction hash:', result.status.transactionHash);
|
|
126
|
-
// Parse the deployed proxy address from transaction logs
|
|
127
|
-
const proxyAddress = await parseContractCreatedEvent(result.status.transactionHash);
|
|
128
|
-
if (proxyAddress) {
|
|
129
|
-
console.log('šÆ Deployed proxy address:', proxyAddress);
|
|
130
|
-
return proxyAddress; // Return the actual proxy address
|
|
131
|
-
}
|
|
132
|
-
else {
|
|
133
|
-
console.log('ā ļø Could not parse proxy address from logs');
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
else {
|
|
137
|
-
console.log('ā Deployment failed:', result.status.reason);
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
catch (error) {
|
|
141
|
-
console.error('ā Test 1 failed:', error);
|
|
142
|
-
}
|
|
143
|
-
return null;
|
|
144
|
-
}
|
|
145
|
-
exports.DeployClientAgentVaultProxy = DeployClientAgentVaultProxy;
|
|
146
|
-
/**
|
|
147
|
-
* Test 2: Configure Spending Rules
|
|
148
|
-
*/
|
|
149
|
-
async function ConfigureSpendingRules(sdk, owner, proxyAddress, signFunction) {
|
|
150
|
-
// get today's 00:00:00 timestamp
|
|
151
|
-
const today = new Date();
|
|
152
|
-
const todayStart = new Date(today.getFullYear(), today.getMonth(), today.getDate());
|
|
153
|
-
const todayStartTimestamp = Math.floor(todayStart.getTime() / 1000);
|
|
154
|
-
// get current week's monday 00:00:00 timestamp
|
|
155
|
-
const dayOfWeek = today.getDay(); // 0=Sunday, 1=Monday, ..., 6=Saturday
|
|
156
|
-
const daysFromMonday = dayOfWeek === 0 ? 6 : dayOfWeek - 1; // Convert Sunday=0 to Sunday=6
|
|
157
|
-
const currentWeekStartTimestamp = todayStartTimestamp - (daysFromMonday * 86400); // 86400 = seconds in a day
|
|
158
|
-
// rules fields:
|
|
159
|
-
// - TimeWindow: time window in seconds, 0 means per transaction, >0 means per time window
|
|
160
|
-
// - Budget: budget in wei, 18 decimals, like 1e18 means 1 token
|
|
161
|
-
// - InitialWindowStartTime: start time of the time window in unix timestamp
|
|
162
|
-
// should be set to the start time of the day/week/month in local time (or utc) 00:00:00
|
|
163
|
-
// - TargetProviders: target service providers array, empty array means applies to all providers
|
|
164
|
-
try {
|
|
165
|
-
// Define spending rules
|
|
166
|
-
const rulesToAdd = [
|
|
167
|
-
{
|
|
168
|
-
timeWindow: 86400n,
|
|
169
|
-
budget: ethers_1.ethers.parseUnits('100', 18),
|
|
170
|
-
initialWindowStartTime: todayStartTimestamp,
|
|
171
|
-
targetProviders: [] // Empty means applies to all providers
|
|
172
|
-
},
|
|
173
|
-
{
|
|
174
|
-
timeWindow: 604800n,
|
|
175
|
-
budget: ethers_1.ethers.parseUnits('1000', 18),
|
|
176
|
-
initialWindowStartTime: currentWeekStartTimestamp,
|
|
177
|
-
targetProviders: []
|
|
178
|
-
},
|
|
179
|
-
{
|
|
180
|
-
timeWindow: 0,
|
|
181
|
-
budget: ethers_1.ethers.parseUnits('10', 18),
|
|
182
|
-
initialWindowStartTime: 0n,
|
|
183
|
-
targetProviders: [ethers_1.ethers.keccak256(ethers_1.ethers.toUtf8Bytes('provider1'))]
|
|
184
|
-
}
|
|
185
|
-
];
|
|
186
|
-
const configureRequest = {
|
|
187
|
-
target: proxyAddress,
|
|
188
|
-
value: 0n,
|
|
189
|
-
callData: ethers_1.ethers.Interface.from([
|
|
190
|
-
'function configureSpendingRule(uint256[] calldata indicesToRemove, tuple(uint256 timeWindow, uint160 budget, uint96 initialWindowStartTime, bytes32[] targetProviders)[] calldata rulesToAdd)'
|
|
191
|
-
]).encodeFunctionData('configureSpendingRule', [
|
|
192
|
-
[],
|
|
193
|
-
rulesToAdd // rules to add
|
|
194
|
-
])
|
|
195
|
-
};
|
|
196
|
-
console.log('Configuring spending rules...');
|
|
197
|
-
console.log('- Rule 1: 100 tokens per 24 hours (all providers)');
|
|
198
|
-
console.log('- Rule 2: 10 tokens per hour (specific provider)');
|
|
199
|
-
const result = await sdk.sendUserOperationAndWait(owner, configureRequest, signFunction);
|
|
200
|
-
if (result.status.status === 'success') {
|
|
201
|
-
console.log('ā
Spending rules configured successfully!');
|
|
202
|
-
console.log('Transaction hash:', result.status.transactionHash);
|
|
203
|
-
}
|
|
204
|
-
else {
|
|
205
|
-
console.log('ā Configuration failed:', result.status.reason);
|
|
206
|
-
}
|
|
207
|
-
}
|
|
208
|
-
catch (error) {
|
|
209
|
-
const aaError = handleAAError(error);
|
|
210
|
-
console.error('ā Test 2 failed:', {
|
|
211
|
-
type: aaError.type,
|
|
212
|
-
message: aaError.message
|
|
213
|
-
});
|
|
214
|
-
// Frontend can handle different error types
|
|
215
|
-
switch (aaError.type) {
|
|
216
|
-
case 'ESTIMATE_GAS_FAILED':
|
|
217
|
-
console.log('Suggestion: Check if the contract call is valid');
|
|
218
|
-
break;
|
|
219
|
-
case 'SEND_USEROP_FAILED':
|
|
220
|
-
console.log('Suggestion: Try again or check bundler status');
|
|
221
|
-
break;
|
|
222
|
-
case 'NETWORK_ERROR':
|
|
223
|
-
console.log('Suggestion: Check your internet connection');
|
|
224
|
-
break;
|
|
225
|
-
default:
|
|
226
|
-
console.log('Suggestion: Check console for more details');
|
|
227
|
-
}
|
|
228
|
-
}
|
|
229
|
-
}
|
|
230
|
-
exports.ConfigureSpendingRules = ConfigureSpendingRules;
|
|
231
|
-
/**
|
|
232
|
-
* Test 3: View Spending Rules (Read-only, no transaction sent)
|
|
233
|
-
*/
|
|
234
|
-
async function ViewSpendingRules(sdk, proxyAddress) {
|
|
235
|
-
try {
|
|
236
|
-
const provider = new ethers_1.ethers.JsonRpcProvider('https://rpc-testnet.gokite.ai');
|
|
237
|
-
const contract = new ethers_1.ethers.Contract(proxyAddress, [
|
|
238
|
-
'function getSpendingRules() view returns (tuple(tuple(uint256 timeWindow, uint160 budget, uint96 initialWindowStartTime, bytes32[] targetProviders) rule, tuple(uint128 amountUsed, uint128 currentTimeWindowStartTime) usage)[])'
|
|
239
|
-
], provider);
|
|
240
|
-
console.log('Fetching spending rules...');
|
|
241
|
-
const spendingRules = await contract.getSpendingRules();
|
|
242
|
-
console.log('ā
Spending Rules:');
|
|
243
|
-
spendingRules.forEach((rule, index) => {
|
|
244
|
-
console.log(` Rule ${index + 1}:`);
|
|
245
|
-
console.log(` - Time Window: ${rule.rule.timeWindow} seconds`);
|
|
246
|
-
console.log(` - Budget: ${ethers_1.ethers.formatUnits(rule.rule.budget, 18)} tokens`);
|
|
247
|
-
console.log(` - Used: ${ethers_1.ethers.formatUnits(rule.usage.amountUsed, 18)} tokens`);
|
|
248
|
-
console.log(` - Providers: ${rule.rule.targetProviders.length === 0 ? 'All' : rule.rule.targetProviders.length}`);
|
|
249
|
-
});
|
|
250
|
-
}
|
|
251
|
-
catch (error) {
|
|
252
|
-
console.error('ā Test 3 failed:', error);
|
|
253
|
-
}
|
|
254
|
-
}
|
|
255
|
-
exports.ViewSpendingRules = ViewSpendingRules;
|
|
256
|
-
// transfer erc20 tokens from aa to another address
|
|
257
|
-
async function transferERC20(sdk, owner, proxyAddress, signFunction, to, amount, token, tokenDecimals) {
|
|
258
|
-
try {
|
|
259
|
-
const transferRequest = {
|
|
260
|
-
target: proxyAddress,
|
|
261
|
-
value: 0n,
|
|
262
|
-
callData: ethers_1.ethers.Interface.from([
|
|
263
|
-
'function transfer(address to, uint256 amount)'
|
|
264
|
-
]).encodeFunctionData('transfer', [
|
|
265
|
-
to,
|
|
266
|
-
ethers_1.ethers.parseUnits(amount.toString(), tokenDecimals) // amount
|
|
267
|
-
])
|
|
268
|
-
};
|
|
269
|
-
console.log('Transferring ERC20 tokens...');
|
|
270
|
-
console.log(`- Token: ${token}`);
|
|
271
|
-
console.log(`- Amount: ${ethers_1.ethers.formatUnits(amount, tokenDecimals)} tokens`);
|
|
272
|
-
const result = await sdk.sendUserOperationAndWait(owner, transferRequest, signFunction);
|
|
273
|
-
if (result.status.status === 'success') {
|
|
274
|
-
console.log('ā
ERC20 transfer successful!');
|
|
275
|
-
console.log('Transaction hash:', result.status.transactionHash);
|
|
276
|
-
}
|
|
277
|
-
else {
|
|
278
|
-
console.log('ā ERC20 transfer failed:', result.status.reason);
|
|
279
|
-
}
|
|
280
|
-
}
|
|
281
|
-
catch (error) {
|
|
282
|
-
console.error('ā Test 4 failed:', error);
|
|
283
|
-
}
|
|
284
|
-
}
|
|
285
|
-
/**
|
|
286
|
-
* Test 4: Withdraw Funds
|
|
287
|
-
*/
|
|
288
|
-
async function WithdrawFunds(sdk, owner, proxyAddress, signFunction) {
|
|
289
|
-
try {
|
|
290
|
-
const withdrawAmount = ethers_1.ethers.parseUnits('50', 18); // Withdraw 50 tokens
|
|
291
|
-
const withdrawRequest = {
|
|
292
|
-
target: proxyAddress,
|
|
293
|
-
value: 0n,
|
|
294
|
-
callData: ethers_1.ethers.Interface.from([
|
|
295
|
-
'function withdrawFunds(address token, uint256 amount)'
|
|
296
|
-
]).encodeFunctionData('withdrawFunds', [
|
|
297
|
-
ADDRESSES.SETTLEMENT_TOKEN,
|
|
298
|
-
withdrawAmount // amount to withdraw
|
|
299
|
-
])
|
|
300
|
-
};
|
|
301
|
-
console.log('Withdrawing funds...');
|
|
302
|
-
console.log(`- Token: ${ADDRESSES.SETTLEMENT_TOKEN}`);
|
|
303
|
-
console.log(`- Amount: ${ethers_1.ethers.formatUnits(withdrawAmount, 18)} tokens`);
|
|
304
|
-
const result = await sdk.sendUserOperationAndWait(owner, withdrawRequest, signFunction);
|
|
305
|
-
if (result.status.status === 'success') {
|
|
306
|
-
console.log('ā
Funds withdrawn successfully!');
|
|
307
|
-
console.log('Transaction hash:', result.status.transactionHash);
|
|
308
|
-
}
|
|
309
|
-
else {
|
|
310
|
-
console.log('ā Withdrawal failed:', result.status.reason);
|
|
311
|
-
}
|
|
312
|
-
}
|
|
313
|
-
catch (error) {
|
|
314
|
-
console.error('ā Test 4 failed:', error);
|
|
315
|
-
}
|
|
316
|
-
}
|
|
317
|
-
exports.WithdrawFunds = WithdrawFunds;
|
|
318
|
-
/**
|
|
319
|
-
* Test 5: Check Token Balance (Read-only)
|
|
320
|
-
*/
|
|
321
|
-
async function CheckTokenBalance(sdk, proxyAddress) {
|
|
322
|
-
try {
|
|
323
|
-
const provider = new ethers_1.ethers.JsonRpcProvider('https://rpc-testnet.gokite.ai');
|
|
324
|
-
const tokenContract = new ethers_1.ethers.Contract(ADDRESSES.SETTLEMENT_TOKEN, [
|
|
325
|
-
'function balanceOf(address account) view returns (uint256)',
|
|
326
|
-
'function symbol() view returns (string)',
|
|
327
|
-
'function decimals() view returns (uint8)'
|
|
328
|
-
], provider);
|
|
329
|
-
console.log('Checking token balance...');
|
|
330
|
-
const [balance, symbol, decimals] = await Promise.all([
|
|
331
|
-
tokenContract.balanceOf(proxyAddress),
|
|
332
|
-
tokenContract.symbol(),
|
|
333
|
-
tokenContract.decimals()
|
|
334
|
-
]);
|
|
335
|
-
console.log('ā
Token Balance:');
|
|
336
|
-
console.log(` - Address: ${proxyAddress}`);
|
|
337
|
-
console.log(` - Token: ${symbol} (${ADDRESSES.SETTLEMENT_TOKEN})`);
|
|
338
|
-
console.log(` - Balance: ${ethers_1.ethers.formatUnits(balance, decimals)} ${symbol}`);
|
|
339
|
-
}
|
|
340
|
-
catch (error) {
|
|
341
|
-
console.error('ā Test 5 failed:', error);
|
|
342
|
-
}
|
|
343
|
-
}
|
|
344
|
-
exports.CheckTokenBalance = CheckTokenBalance;
|
|
345
|
-
/**
|
|
346
|
-
* Legacy example for basic AA operations
|
|
347
|
-
*/
|
|
348
|
-
async function basicExample() {
|
|
349
|
-
console.log('\n=== Basic AA SDK Example ===');
|
|
350
|
-
// Initialize SDK
|
|
351
|
-
const sdk = new gokite_aa_sdk_1.GokiteAASDK('kite_testnet', 'https://rpc-testnet.gokite.ai', 'http://localhost:14337/rpc/');
|
|
352
|
-
const owner = '0x4A50DCA63d541372ad36E5A36F1D542d51164F19';
|
|
353
|
-
const accountAddress = sdk.getAccountAddress(owner);
|
|
354
|
-
console.log('Account address:', accountAddress);
|
|
355
|
-
// Simple ETH transfer example
|
|
356
|
-
const sendEthRequest = {
|
|
357
|
-
target: '0x4A50DCA63d541372ad36E5A36F1D542d51164F19',
|
|
358
|
-
value: ethers_1.ethers.parseEther('0.001'),
|
|
359
|
-
callData: '0x'
|
|
360
|
-
};
|
|
361
|
-
const signFunction = async (userOpHash) => {
|
|
362
|
-
const signer_pk = process.env.PRIVATE_KEY;
|
|
363
|
-
const signer = new ethers_1.ethers.Wallet(signer_pk);
|
|
364
|
-
return signer.signMessage(ethers_1.ethers.getBytes(userOpHash));
|
|
365
|
-
};
|
|
366
|
-
try {
|
|
367
|
-
const result = await sdk.sendUserOperationAndWait(owner, sendEthRequest, signFunction);
|
|
368
|
-
if (result.status.status === 'success') {
|
|
369
|
-
console.log('ā
ETH transfer successful!');
|
|
370
|
-
console.log('Transaction hash:', result.status.transactionHash);
|
|
371
|
-
}
|
|
372
|
-
else {
|
|
373
|
-
console.log('ā ETH transfer failed:', result.status.reason);
|
|
374
|
-
}
|
|
375
|
-
}
|
|
376
|
-
catch (error) {
|
|
377
|
-
console.error('ā Basic example failed:', error);
|
|
378
|
-
}
|
|
379
|
-
}
|
|
380
|
-
exports.basicExample = basicExample;
|
|
381
|
-
/**
|
|
382
|
-
* Parse ContractCreated event from transaction logs to get deployed proxy address
|
|
383
|
-
* Event: ContractCreated(address indexed contractAddress)
|
|
384
|
-
*/
|
|
385
|
-
async function parseContractCreatedEvent(transactionHash) {
|
|
386
|
-
try {
|
|
387
|
-
const provider = new ethers_1.ethers.JsonRpcProvider('https://rpc-testnet.gokite.ai');
|
|
388
|
-
const receipt = await provider.getTransactionReceipt(transactionHash);
|
|
389
|
-
if (!receipt) {
|
|
390
|
-
console.error('Transaction receipt not found');
|
|
391
|
-
return null;
|
|
392
|
-
}
|
|
393
|
-
// ContractCreated event signature: ContractCreated(address indexed contractAddress)
|
|
394
|
-
const contractCreatedEventSignature = ethers_1.ethers.id('ContractCreated(address)');
|
|
395
|
-
console.log('Parsing transaction logs...');
|
|
396
|
-
console.log(`Total logs: ${receipt.logs.length}`);
|
|
397
|
-
for (const log of receipt.logs) {
|
|
398
|
-
// Check if this log matches the ContractCreated event
|
|
399
|
-
if (log.topics[0] === contractCreatedEventSignature) {
|
|
400
|
-
// The first topic is the event signature
|
|
401
|
-
// The second topic is the indexed contractAddress
|
|
402
|
-
const contractAddress = ethers_1.ethers.AbiCoder.defaultAbiCoder().decode(['address'], log.topics[1])[0];
|
|
403
|
-
console.log('Found ContractCreated event:');
|
|
404
|
-
console.log('- Contract Address:', contractAddress);
|
|
405
|
-
console.log('- From Address:', log.address);
|
|
406
|
-
return contractAddress;
|
|
407
|
-
}
|
|
408
|
-
}
|
|
409
|
-
console.log('No ContractCreated event found in transaction logs');
|
|
410
|
-
return null;
|
|
411
|
-
}
|
|
412
|
-
catch (error) {
|
|
413
|
-
console.error('Error parsing ContractCreated event:', error);
|
|
414
|
-
return null;
|
|
415
|
-
}
|
|
416
|
-
}
|
|
417
|
-
async function main() {
|
|
418
|
-
// Run ClientAgentVault integration tests
|
|
419
|
-
await clientAgentVaultIntegrationTests();
|
|
420
|
-
// // Run basic AA example
|
|
421
|
-
// await basicExample();
|
|
422
|
-
console.log('\nā
All examples completed!');
|
|
423
|
-
}
|
|
424
|
-
// Legacy export for backwards compatibility
|
|
425
|
-
exports.example = basicExample;
|
|
426
|
-
main().catch(console.error);
|