@aastar/paymaster 0.16.8 → 0.16.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/V4/BundlerCompat.d.ts +28 -0
- package/dist/V4/BundlerCompat.js +37 -0
- package/dist/{paymaster/src/V4 → V4}/PaymasterClient.d.ts +5 -0
- package/dist/{paymaster/src/V4 → V4}/PaymasterClient.js +108 -15
- package/dist/{paymaster/src/V4 → V4}/PaymasterUtils.d.ts +11 -0
- package/dist/{paymaster/src/V4 → V4}/PaymasterUtils.js +36 -19
- package/dist/{paymaster/src/V4 → V4}/SuperPaymasterClient.js +16 -10
- package/package.json +5 -8
- package/dist/core/src/abis/BLSAggregator.json +0 -686
- package/dist/core/src/abis/BLSValidator.json +0 -42
- package/dist/core/src/abis/DVTValidator.json +0 -368
- package/dist/core/src/abis/EntryPoint.json +0 -1382
- package/dist/core/src/abis/GToken.json +0 -513
- package/dist/core/src/abis/GTokenStaking.json +0 -949
- package/dist/core/src/abis/MySBT.json +0 -1518
- package/dist/core/src/abis/Paymaster.json +0 -1143
- package/dist/core/src/abis/PaymasterFactory.json +0 -640
- package/dist/core/src/abis/Registry.json +0 -1942
- package/dist/core/src/abis/ReputationSystem.json +0 -699
- package/dist/core/src/abis/SimpleAccount.json +0 -560
- package/dist/core/src/abis/SimpleAccountFactory.json +0 -111
- package/dist/core/src/abis/SuperPaymaster.json +0 -1781
- package/dist/core/src/abis/index.d.ts +0 -1126
- package/dist/core/src/abis/index.js +0 -91
- package/dist/core/src/abis/xPNTsFactory.json +0 -718
- package/dist/core/src/abis/xPNTsToken.json +0 -1280
- package/dist/core/src/actions/StateValidator.d.ts +0 -68
- package/dist/core/src/actions/StateValidator.js +0 -187
- package/dist/core/src/actions/account.d.ts +0 -55
- package/dist/core/src/actions/account.js +0 -133
- package/dist/core/src/actions/aggregator.d.ts +0 -17
- package/dist/core/src/actions/aggregator.js +0 -31
- package/dist/core/src/actions/dvt.d.ts +0 -30
- package/dist/core/src/actions/dvt.js +0 -41
- package/dist/core/src/actions/entryPoint.d.ts +0 -90
- package/dist/core/src/actions/entryPoint.js +0 -211
- package/dist/core/src/actions/factory.d.ts +0 -215
- package/dist/core/src/actions/factory.js +0 -442
- package/dist/core/src/actions/faucet.d.ts +0 -48
- package/dist/core/src/actions/faucet.js +0 -337
- package/dist/core/src/actions/gtokenExtended.d.ts +0 -39
- package/dist/core/src/actions/gtokenExtended.js +0 -115
- package/dist/core/src/actions/index.d.ts +0 -15
- package/dist/core/src/actions/index.js +0 -17
- package/dist/core/src/actions/paymasterV4.d.ts +0 -170
- package/dist/core/src/actions/paymasterV4.js +0 -334
- package/dist/core/src/actions/registry.d.ts +0 -246
- package/dist/core/src/actions/registry.js +0 -667
- package/dist/core/src/actions/reputation.d.ts +0 -129
- package/dist/core/src/actions/reputation.js +0 -281
- package/dist/core/src/actions/sbt.d.ts +0 -191
- package/dist/core/src/actions/sbt.js +0 -533
- package/dist/core/src/actions/staking.d.ts +0 -132
- package/dist/core/src/actions/staking.js +0 -330
- package/dist/core/src/actions/superPaymaster.d.ts +0 -237
- package/dist/core/src/actions/superPaymaster.js +0 -644
- package/dist/core/src/actions/tokens.d.ts +0 -229
- package/dist/core/src/actions/tokens.js +0 -415
- package/dist/core/src/branding.d.ts +0 -30
- package/dist/core/src/branding.js +0 -30
- package/dist/core/src/clients/BaseClient.d.ts +0 -25
- package/dist/core/src/clients/BaseClient.js +0 -66
- package/dist/core/src/clients/types.d.ts +0 -60
- package/dist/core/src/clients/types.js +0 -1
- package/dist/core/src/clients.d.ts +0 -5
- package/dist/core/src/clients.js +0 -11
- package/dist/core/src/communities.d.ts +0 -52
- package/dist/core/src/communities.js +0 -73
- package/dist/core/src/config/ContractConfigManager.d.ts +0 -20
- package/dist/core/src/config/ContractConfigManager.js +0 -48
- package/dist/core/src/constants.d.ts +0 -88
- package/dist/core/src/constants.js +0 -125
- package/dist/core/src/contract-addresses.d.ts +0 -110
- package/dist/core/src/contract-addresses.js +0 -99
- package/dist/core/src/contracts.d.ts +0 -424
- package/dist/core/src/contracts.js +0 -343
- package/dist/core/src/crypto/blsSigner.d.ts +0 -64
- package/dist/core/src/crypto/blsSigner.js +0 -98
- package/dist/core/src/crypto/index.d.ts +0 -1
- package/dist/core/src/crypto/index.js +0 -1
- package/dist/core/src/index.d.ts +0 -21
- package/dist/core/src/index.js +0 -21
- package/dist/core/src/networks.d.ts +0 -127
- package/dist/core/src/networks.js +0 -118
- package/dist/core/src/requirementChecker.d.ts +0 -38
- package/dist/core/src/requirementChecker.js +0 -139
- package/dist/core/src/roles.d.ts +0 -204
- package/dist/core/src/roles.js +0 -211
- package/dist/core/src/utils/validation.d.ts +0 -24
- package/dist/core/src/utils/validation.js +0 -56
- /package/dist/{paymaster/src/SuperPaymaster → SuperPaymaster}/index.d.ts +0 -0
- /package/dist/{paymaster/src/SuperPaymaster → SuperPaymaster}/index.js +0 -0
- /package/dist/{paymaster/src/V4 → V4}/PaymasterClient.test.d.ts +0 -0
- /package/dist/{paymaster/src/V4 → V4}/PaymasterClient.test.js +0 -0
- /package/dist/{paymaster/src/V4 → V4}/PaymasterOperator.d.ts +0 -0
- /package/dist/{paymaster/src/V4 → V4}/PaymasterOperator.js +0 -0
- /package/dist/{paymaster/src/V4 → V4}/PaymasterOperator.test.d.ts +0 -0
- /package/dist/{paymaster/src/V4 → V4}/PaymasterOperator.test.js +0 -0
- /package/dist/{paymaster/src/V4 → V4}/PaymasterUtils.test.d.ts +0 -0
- /package/dist/{paymaster/src/V4 → V4}/PaymasterUtils.test.js +0 -0
- /package/dist/{paymaster/src/V4 → V4}/SuperPaymasterClient.d.ts +0 -0
- /package/dist/{paymaster/src/V4 → V4}/SuperPaymasterClient.test.d.ts +0 -0
- /package/dist/{paymaster/src/V4 → V4}/SuperPaymasterClient.test.js +0 -0
- /package/dist/{paymaster/src/V4 → V4}/index.d.ts +0 -0
- /package/dist/{paymaster/src/V4 → V4}/index.js +0 -0
- /package/dist/{paymaster/src/index.d.ts → index.d.ts} +0 -0
- /package/dist/{paymaster/src/index.js → index.js} +0 -0
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { type Address } from 'viem';
|
|
2
|
+
/**
|
|
3
|
+
* Bundler types we support
|
|
4
|
+
*/
|
|
5
|
+
export declare enum BundlerType {
|
|
6
|
+
ALCHEMY = "alchemy",
|
|
7
|
+
PIMLICO = "pimlico",
|
|
8
|
+
STACKUP = "stackup",
|
|
9
|
+
CANDIDE = "candide",
|
|
10
|
+
UNKNOWN = "unknown"
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Detect bundler type from URL
|
|
14
|
+
*/
|
|
15
|
+
export declare function detectBundlerType(bundlerUrl: string): BundlerType;
|
|
16
|
+
/**
|
|
17
|
+
* Minimal interface to satisfy basic Pimlico/Bundler needs
|
|
18
|
+
* without bringing in heavy permissionless types that might conflict
|
|
19
|
+
*/
|
|
20
|
+
export interface BundlerConfig {
|
|
21
|
+
type: BundlerType;
|
|
22
|
+
url: string;
|
|
23
|
+
entryPoint: Address;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Create a bundler client config
|
|
27
|
+
*/
|
|
28
|
+
export declare function createBundlerClient(bundlerUrl: string, entryPoint: Address): BundlerConfig;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bundler types we support
|
|
3
|
+
*/
|
|
4
|
+
export var BundlerType;
|
|
5
|
+
(function (BundlerType) {
|
|
6
|
+
BundlerType["ALCHEMY"] = "alchemy";
|
|
7
|
+
BundlerType["PIMLICO"] = "pimlico";
|
|
8
|
+
BundlerType["STACKUP"] = "stackup";
|
|
9
|
+
BundlerType["CANDIDE"] = "candide";
|
|
10
|
+
BundlerType["UNKNOWN"] = "unknown";
|
|
11
|
+
})(BundlerType || (BundlerType = {}));
|
|
12
|
+
/**
|
|
13
|
+
* Detect bundler type from URL
|
|
14
|
+
*/
|
|
15
|
+
export function detectBundlerType(bundlerUrl) {
|
|
16
|
+
const url = bundlerUrl.toLowerCase();
|
|
17
|
+
if (url.includes('alchemy.com'))
|
|
18
|
+
return BundlerType.ALCHEMY;
|
|
19
|
+
if (url.includes('pimlico.io'))
|
|
20
|
+
return BundlerType.PIMLICO;
|
|
21
|
+
if (url.includes('stackup'))
|
|
22
|
+
return BundlerType.STACKUP;
|
|
23
|
+
if (url.includes('candide.dev'))
|
|
24
|
+
return BundlerType.CANDIDE;
|
|
25
|
+
return BundlerType.UNKNOWN;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Create a bundler client config
|
|
29
|
+
*/
|
|
30
|
+
export function createBundlerClient(bundlerUrl, entryPoint) {
|
|
31
|
+
const bundlerType = detectBundlerType(bundlerUrl);
|
|
32
|
+
return {
|
|
33
|
+
type: bundlerType,
|
|
34
|
+
url: bundlerUrl,
|
|
35
|
+
entryPoint
|
|
36
|
+
};
|
|
37
|
+
}
|
|
@@ -76,4 +76,9 @@ export declare class PaymasterClient {
|
|
|
76
76
|
* This is the payload signed by the user.
|
|
77
77
|
*/
|
|
78
78
|
static encodeExecution(target: Address, value: bigint, data: `0x${string}`): `0x${string}`;
|
|
79
|
+
/**
|
|
80
|
+
* More robust version of waitForUserOperationReceipt.
|
|
81
|
+
* Catches timeouts and returns a cleaner result.
|
|
82
|
+
*/
|
|
83
|
+
static waitForUserOperation(bundlerClient: any, hash: `0x${string}`, timeout?: number): Promise<any>;
|
|
79
84
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { concat, pad, toHex,
|
|
2
|
-
import { buildPaymasterData, buildSuperPaymasterData, formatUserOpV07, getUserOpHashV07 } from './PaymasterUtils';
|
|
1
|
+
import { parseAbi, concat, pad, toHex, encodeFunctionData } from 'viem';
|
|
2
|
+
import { buildPaymasterData, buildSuperPaymasterData, formatUserOpV07, getUserOpHashV07 } from './PaymasterUtils.js';
|
|
3
|
+
import { detectBundlerType } from './BundlerCompat.js';
|
|
3
4
|
/**
|
|
4
5
|
* PaymasterClient
|
|
5
6
|
* Focus: Integration, Funding, Interaction.
|
|
@@ -53,6 +54,44 @@ export class PaymasterClient {
|
|
|
53
54
|
* Estimate Gas for a UserOperation.
|
|
54
55
|
*/
|
|
55
56
|
static async estimateUserOperationGas(client, wallet, aaAddress, entryPoint, paymasterAddress, token, bundlerUrl, callData, options) {
|
|
57
|
+
// 0. Check cachedPrice (Critical for Paymaster V4)
|
|
58
|
+
if (!options?.operator) { // Only for Paymaster V4, not SuperPaymaster
|
|
59
|
+
try {
|
|
60
|
+
const cache = await client.readContract({
|
|
61
|
+
address: paymasterAddress,
|
|
62
|
+
abi: parseAbi(['function cachedPrice() view returns (uint208 price, uint48 updatedAt)']),
|
|
63
|
+
functionName: 'cachedPrice'
|
|
64
|
+
});
|
|
65
|
+
if (!cache || cache.price === 0n || cache[0] === 0n) {
|
|
66
|
+
console.log('[PaymasterClient] ⚠️ cachedPrice is 0! Auto-initializing...');
|
|
67
|
+
// Check if we're on testnet (chainId 11155111 = Sepolia, 11155420 = OP Sepolia)
|
|
68
|
+
const chainId = client.chain?.id || await client.getChainId();
|
|
69
|
+
const isTestnet = [11155111, 11155420, 31337].includes(chainId);
|
|
70
|
+
if (isTestnet) {
|
|
71
|
+
// Auto-call updatePrice on testnet
|
|
72
|
+
const updateHash = await wallet.writeContract({
|
|
73
|
+
address: paymasterAddress,
|
|
74
|
+
abi: parseAbi(['function updatePrice() external']),
|
|
75
|
+
functionName: 'updatePrice'
|
|
76
|
+
});
|
|
77
|
+
await client.waitForTransactionReceipt({ hash: updateHash });
|
|
78
|
+
console.log('[PaymasterClient] ✅ cachedPrice initialized via updatePrice()');
|
|
79
|
+
}
|
|
80
|
+
else {
|
|
81
|
+
// Mainnet: throw error, require Keeper
|
|
82
|
+
throw new Error(`Paymaster cachedPrice is 0 on Mainnet (chainId: ${chainId}). ` +
|
|
83
|
+
`This requires Keeper to call updatePrice(). Please ensure Keeper is running.`);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
catch (e) {
|
|
88
|
+
// If error is our mainnet check, re-throw
|
|
89
|
+
if (e.message?.includes('requires Keeper'))
|
|
90
|
+
throw e;
|
|
91
|
+
// Otherwise log and continue (might be old Paymaster without cachedPrice)
|
|
92
|
+
console.log('[PaymasterClient] ⚠️ Failed to check cachedPrice:', e.message?.slice(0, 50));
|
|
93
|
+
}
|
|
94
|
+
}
|
|
56
95
|
// 1. Construct a dummy UserOp for estimation
|
|
57
96
|
let paymasterAndData;
|
|
58
97
|
if (options?.operator) {
|
|
@@ -64,8 +103,8 @@ export class PaymasterClient {
|
|
|
64
103
|
else {
|
|
65
104
|
paymasterAndData = buildPaymasterData(paymasterAddress, token, {
|
|
66
105
|
validityWindow: options?.validityWindow,
|
|
67
|
-
verificationGasLimit:
|
|
68
|
-
postOpGasLimit:
|
|
106
|
+
verificationGasLimit: 250000n,
|
|
107
|
+
postOpGasLimit: 150000n
|
|
69
108
|
});
|
|
70
109
|
}
|
|
71
110
|
// 1.5. Get dynamic gas prices from network
|
|
@@ -88,8 +127,8 @@ export class PaymasterClient {
|
|
|
88
127
|
? concat([options.factory, options.factoryData])
|
|
89
128
|
: '0x',
|
|
90
129
|
callData,
|
|
91
|
-
accountGasLimits: concat([pad(toHex(
|
|
92
|
-
preVerificationGas:
|
|
130
|
+
accountGasLimits: concat([pad(toHex(250000n), { size: 16 }), pad(toHex(500000n), { size: 16 })]),
|
|
131
|
+
preVerificationGas: 100000n,
|
|
93
132
|
gasFees: concat([pad(toHex(maxPriorityFeePerGas), { size: 16 }), pad(toHex(maxFeePerGas), { size: 16 })]),
|
|
94
133
|
paymasterAndData,
|
|
95
134
|
signature: '0x'
|
|
@@ -118,14 +157,33 @@ export class PaymasterClient {
|
|
|
118
157
|
body: JSON.stringify(payload, (_, v) => typeof v === 'bigint' ? '0x' + v.toString(16) : v)
|
|
119
158
|
});
|
|
120
159
|
const result = await response.json();
|
|
160
|
+
// Debug logging for Candide
|
|
161
|
+
if (bundlerUrl.includes('candide')) {
|
|
162
|
+
console.log('[PaymasterClient] Candide Request:', JSON.stringify(payload.params[0], null, 2));
|
|
163
|
+
console.log('[PaymasterClient] Candide Response:', JSON.stringify(result, null, 2));
|
|
164
|
+
}
|
|
165
|
+
const data = result.result;
|
|
166
|
+
// Debug logging for estimation
|
|
167
|
+
console.log('[PaymasterClient] Gas Estimation Result:', JSON.stringify(data, null, 2));
|
|
168
|
+
// Anvil Fallback for Estimation
|
|
169
|
+
if (result.error && (result.error.code === -32601 || result.error.message?.includes('Method not found'))) {
|
|
170
|
+
console.log('[PaymasterClient] EstimateUserOp failed (Method not found). Using Anvil defaults.');
|
|
171
|
+
return {
|
|
172
|
+
preVerificationGas: 100000n,
|
|
173
|
+
verificationGasLimit: 1000000n,
|
|
174
|
+
callGasLimit: 2000000n,
|
|
175
|
+
paymasterVerificationGasLimit: 100000n,
|
|
176
|
+
paymasterPostOpGasLimit: 100000n
|
|
177
|
+
};
|
|
178
|
+
}
|
|
121
179
|
if (result.error)
|
|
122
180
|
throw new Error(`Estimation Error: ${JSON.stringify(result.error)}`);
|
|
123
|
-
|
|
124
|
-
//
|
|
181
|
+
// Dynamic tuning: use estimated values directly to maintain efficiency
|
|
182
|
+
// Bundler efficiency check: actual_used / limit >= 0.4
|
|
125
183
|
return {
|
|
126
184
|
preVerificationGas: BigInt(data.preVerificationGas),
|
|
127
|
-
verificationGasLimit: BigInt(data.verificationGasLimit),
|
|
128
|
-
callGasLimit: (BigInt(data.callGasLimit) * 110n) / 100n, // 1.1x
|
|
185
|
+
verificationGasLimit: BigInt(data.verificationGasLimit), // Use estimate as-is for efficiency
|
|
186
|
+
callGasLimit: (BigInt(data.callGasLimit) * 110n) / 100n, // Small 1.1x buffer
|
|
129
187
|
paymasterVerificationGasLimit: data.paymasterVerificationGasLimit ? BigInt(data.paymasterVerificationGasLimit) : undefined,
|
|
130
188
|
paymasterPostOpGasLimit: data.paymasterPostOpGasLimit ? BigInt(data.paymasterPostOpGasLimit) : 100000n
|
|
131
189
|
};
|
|
@@ -141,7 +199,7 @@ export class PaymasterClient {
|
|
|
141
199
|
verificationGasLimit: options?.verificationGasLimit,
|
|
142
200
|
callGasLimit: options?.callGasLimit,
|
|
143
201
|
paymasterVerificationGasLimit: options?.paymasterVerificationGasLimit,
|
|
144
|
-
paymasterPostOpGasLimit: options?.paymasterPostOpGasLimit ??
|
|
202
|
+
paymasterPostOpGasLimit: options?.paymasterPostOpGasLimit ?? 150000n
|
|
145
203
|
};
|
|
146
204
|
if (options?.autoEstimate !== false && (!gasLimits.verificationGasLimit || !gasLimits.callGasLimit)) {
|
|
147
205
|
const est = await this.estimateUserOperationGas(client, wallet, aaAddress, entryPoint, paymasterAddress, token, bundlerUrl, callData, {
|
|
@@ -198,9 +256,13 @@ export class PaymasterClient {
|
|
|
198
256
|
});
|
|
199
257
|
}
|
|
200
258
|
else {
|
|
259
|
+
// MATH: Target Efficiency = PVG / (PVG + VGL + PMVGL) >= 0.4
|
|
260
|
+
// Since PVG is ~100k, (VGL + PMVGL) must be <= 150k.
|
|
261
|
+
// We set each to 75k to safely pass the 0.4 efficiency guard.
|
|
262
|
+
const pmVerGas = 75000n;
|
|
201
263
|
paymasterAndData = buildPaymasterData(paymasterAddress, token, {
|
|
202
264
|
validityWindow: options?.validityWindow,
|
|
203
|
-
verificationGasLimit:
|
|
265
|
+
verificationGasLimit: pmVerGas,
|
|
204
266
|
postOpGasLimit: gasLimits.paymasterPostOpGasLimit ?? 100000n
|
|
205
267
|
});
|
|
206
268
|
}
|
|
@@ -213,8 +275,8 @@ export class PaymasterClient {
|
|
|
213
275
|
: '0x',
|
|
214
276
|
callData,
|
|
215
277
|
accountGasLimits: concat([
|
|
216
|
-
pad(toHex(
|
|
217
|
-
pad(toHex(gasLimits.callGasLimit ?? 500000n), { size: 16 }) // Call
|
|
278
|
+
pad(toHex(75000n), { size: 16 }), // Verification (Tuned for 0.4 efficiency)
|
|
279
|
+
pad(toHex(gasLimits.callGasLimit ?? 500000n), { size: 16 }) // Call
|
|
218
280
|
]),
|
|
219
281
|
preVerificationGas: gasLimits.preVerificationGas ?? 50000n,
|
|
220
282
|
gasFees: concat([
|
|
@@ -237,7 +299,10 @@ export class PaymasterClient {
|
|
|
237
299
|
const userOpHash = getUserOpHashV07(userOp, entryPoint, BigInt(client.chain.id));
|
|
238
300
|
const signature = (await wallet.account.signMessage({ message: { raw: userOpHash } }));
|
|
239
301
|
userOp.signature = signature;
|
|
240
|
-
// 6. Submit to Bundler
|
|
302
|
+
// 6. Submit to Bundler (Unified JSON-RPC)
|
|
303
|
+
const bundlerType = detectBundlerType(bundlerUrl);
|
|
304
|
+
console.log(`[PaymasterClient] Using ${bundlerType} Bundler`);
|
|
305
|
+
// Use standard JSON-RPC for all bundlers (Pimlico/Alchemy/Stackup/etc)
|
|
241
306
|
const response = await fetch(bundlerUrl, {
|
|
242
307
|
method: 'POST',
|
|
243
308
|
headers: { 'Content-Type': 'application/json' },
|
|
@@ -249,8 +314,21 @@ export class PaymasterClient {
|
|
|
249
314
|
}, (_, v) => typeof v === 'bigint' ? '0x' + v.toString(16) : v)
|
|
250
315
|
});
|
|
251
316
|
const result = await response.json();
|
|
317
|
+
if (result.error && (result.error.code === -32601 || result.error.message?.includes('Method not found'))) {
|
|
318
|
+
console.log('[PaymasterClient] SendUserOp failed (Method not found). Falling back to direct handleOps...');
|
|
319
|
+
const caller = wallet.account?.address ? wallet.account.address : wallet.account;
|
|
320
|
+
return await wallet.writeContract({
|
|
321
|
+
address: entryPoint,
|
|
322
|
+
abi: parseAbi(['function handleOps((address,uint256,bytes,bytes,bytes32,uint256,bytes32,bytes,bytes)[], address) external']),
|
|
323
|
+
functionName: 'handleOps',
|
|
324
|
+
args: [[userOp], caller],
|
|
325
|
+
chain: wallet.chain,
|
|
326
|
+
account: wallet.account
|
|
327
|
+
});
|
|
328
|
+
}
|
|
252
329
|
if (result.error)
|
|
253
330
|
throw new Error(`Bundler Error: ${JSON.stringify(result.error)}`);
|
|
331
|
+
console.log('[PaymasterClient] ✅ Submitted via', bundlerType, 'hash:', result.result);
|
|
254
332
|
return result.result;
|
|
255
333
|
}
|
|
256
334
|
/**
|
|
@@ -312,4 +390,19 @@ export class PaymasterClient {
|
|
|
312
390
|
args: [target, value, data]
|
|
313
391
|
});
|
|
314
392
|
}
|
|
393
|
+
/**
|
|
394
|
+
* More robust version of waitForUserOperationReceipt.
|
|
395
|
+
* Catches timeouts and returns a cleaner result.
|
|
396
|
+
*/
|
|
397
|
+
static async waitForUserOperation(bundlerClient, hash, timeout = 60000) {
|
|
398
|
+
try {
|
|
399
|
+
return await bundlerClient.waitForUserOperationReceipt({ hash, timeout });
|
|
400
|
+
}
|
|
401
|
+
catch (error) {
|
|
402
|
+
if (error.name === 'TimeoutError' || error.message?.includes('timed out')) {
|
|
403
|
+
return { timeout: true, hash };
|
|
404
|
+
}
|
|
405
|
+
throw error;
|
|
406
|
+
}
|
|
407
|
+
}
|
|
315
408
|
}
|
|
@@ -43,13 +43,24 @@ export declare function buildPaymasterData(paymasterAddress: Address, token: Add
|
|
|
43
43
|
/**
|
|
44
44
|
* Build paymasterAndData for SuperPaymaster V3.
|
|
45
45
|
* Layout: [Paymaster(20)] [verGas(16)] [postGas(16)] [operator(20)] [maxRate(32)]
|
|
46
|
+
* Total: 104 bytes
|
|
47
|
+
*
|
|
48
|
+
* IMPORTANT: SuperPaymaster contract generates validUntil internally using:
|
|
49
|
+
* validUntil = cachedPrice.updatedAt + priceStalenessThreshold
|
|
50
|
+
* Do NOT include validUntil/validAfter in paymasterAndData!
|
|
46
51
|
*/
|
|
47
52
|
export declare function buildSuperPaymasterData(paymasterAddress: Address, operator: Address, options?: {
|
|
48
53
|
verificationGasLimit?: bigint;
|
|
49
54
|
postOpGasLimit?: bigint;
|
|
55
|
+
maxRate?: bigint;
|
|
50
56
|
}): `0x${string}`;
|
|
51
57
|
/**
|
|
52
58
|
* Helper to format UserOp for Alchemy/Standard Bundlers (v0.7 Decomposed)
|
|
53
59
|
*/
|
|
54
60
|
export declare function formatUserOpV07(userOp: any): any;
|
|
55
61
|
export declare function getUserOpHashV07(userOp: any, entryPoint: Address, chainId: bigint): Hex;
|
|
62
|
+
/**
|
|
63
|
+
* Tune gas limit using a dynamic nominal ceiling to satisfy Bundler efficiency (0.4)
|
|
64
|
+
* Target: Actual / Limit >= targetEfficiency
|
|
65
|
+
*/
|
|
66
|
+
export declare function tuneGasLimit(estimate: bigint, nominalActual: bigint, targetEfficiency?: number): bigint;
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { concat, pad, toHex, keccak256, encodeAbiParameters, toBytes } from 'viem';
|
|
2
|
-
|
|
3
|
-
const
|
|
4
|
-
const DEFAULT_POSTOP_GAS_V4 = 100000n;
|
|
2
|
+
const DEFAULT_VERIFICATION_GAS_V4 = 1500000n; // ~1.5M for safety
|
|
3
|
+
const DEFAULT_POSTOP_GAS_V4 = 300000n; // ~300k for postOp logic
|
|
5
4
|
/**
|
|
6
5
|
* Constructs the middleware for Paymaster V4.
|
|
7
6
|
* Returns the `paymasterAndData` hex string.
|
|
@@ -32,11 +31,8 @@ export function getPaymasterV4Middleware(config) {
|
|
|
32
31
|
*/
|
|
33
32
|
export function buildPaymasterData(paymasterAddress, token, options) {
|
|
34
33
|
const validityWindow = options?.validityWindow ?? 3600;
|
|
35
|
-
const verGas = options?.verificationGasLimit ??
|
|
34
|
+
const verGas = options?.verificationGasLimit ?? 200000n; // Increased for estimation
|
|
36
35
|
const postGas = options?.postOpGasLimit ?? 100000n;
|
|
37
|
-
// Hardening: uint128 bounds check
|
|
38
|
-
validateUint128(verGas, 'verificationGasLimit');
|
|
39
|
-
validateUint128(postGas, 'postOpGasLimit');
|
|
40
36
|
const now = Math.floor(Date.now() / 1000);
|
|
41
37
|
const validUntil = now + validityWindow;
|
|
42
38
|
const validAfter = now - 100; // 100 seconds grace period
|
|
@@ -52,20 +48,22 @@ export function buildPaymasterData(paymasterAddress, token, options) {
|
|
|
52
48
|
/**
|
|
53
49
|
* Build paymasterAndData for SuperPaymaster V3.
|
|
54
50
|
* Layout: [Paymaster(20)] [verGas(16)] [postGas(16)] [operator(20)] [maxRate(32)]
|
|
51
|
+
* Total: 104 bytes
|
|
52
|
+
*
|
|
53
|
+
* IMPORTANT: SuperPaymaster contract generates validUntil internally using:
|
|
54
|
+
* validUntil = cachedPrice.updatedAt + priceStalenessThreshold
|
|
55
|
+
* Do NOT include validUntil/validAfter in paymasterAndData!
|
|
55
56
|
*/
|
|
56
57
|
export function buildSuperPaymasterData(paymasterAddress, operator, options) {
|
|
57
58
|
const verGas = options?.verificationGasLimit ?? 80000n;
|
|
58
59
|
const postGas = options?.postOpGasLimit ?? 100000n;
|
|
59
|
-
//
|
|
60
|
-
validateAddress(paymasterAddress, 'Paymaster Address');
|
|
61
|
-
validateAddress(operator, 'Operator Address');
|
|
62
|
-
validateUint128(verGas, 'verificationGasLimit');
|
|
63
|
-
validateUint128(postGas, 'postOpGasLimit');
|
|
60
|
+
const maxRate = options?.maxRate ?? ((1n << 256n) - 1n); // Default: max uint256, no rate limit
|
|
64
61
|
return concat([
|
|
65
62
|
paymasterAddress,
|
|
66
63
|
pad(toHex(verGas), { size: 16 }),
|
|
67
64
|
pad(toHex(postGas), { size: 16 }),
|
|
68
|
-
operator
|
|
65
|
+
operator,
|
|
66
|
+
pad(toHex(maxRate), { size: 32 }) // Optional rate commitment (rug pull protection)
|
|
69
67
|
]);
|
|
70
68
|
}
|
|
71
69
|
/**
|
|
@@ -77,14 +75,21 @@ export function formatUserOpV07(userOp) {
|
|
|
77
75
|
nonce: toHex(userOp.nonce),
|
|
78
76
|
callData: userOp.callData,
|
|
79
77
|
preVerificationGas: toHex(userOp.preVerificationGas),
|
|
80
|
-
signature: userOp.signature
|
|
81
|
-
initCode: userOp.initCode
|
|
78
|
+
signature: userOp.signature
|
|
82
79
|
};
|
|
83
|
-
//
|
|
84
|
-
if (userOp.initCode && userOp.initCode !== '0x') {
|
|
85
|
-
result.
|
|
86
|
-
|
|
80
|
+
// Only include factory/factoryData if account is NOT deployed (initCode not empty)
|
|
81
|
+
if (userOp.initCode && userOp.initCode !== '0x' && userOp.initCode.length > 2) {
|
|
82
|
+
result.initCode = userOp.initCode;
|
|
83
|
+
if (userOp.initCode.length > 42) {
|
|
84
|
+
result.factory = userOp.initCode.slice(0, 42);
|
|
85
|
+
result.factoryData = '0x' + userOp.initCode.slice(42);
|
|
86
|
+
}
|
|
87
|
+
else {
|
|
88
|
+
result.factory = '0x0000000000000000000000000000000000000000';
|
|
89
|
+
result.factoryData = '0x';
|
|
90
|
+
}
|
|
87
91
|
}
|
|
92
|
+
// If account is deployed, don't include any factory fields at all
|
|
88
93
|
// Unpack accountGasLimits: [verificationGasLimit(16)][callGasLimit(16)]
|
|
89
94
|
if (userOp.accountGasLimits && userOp.accountGasLimits !== '0x') {
|
|
90
95
|
const packed = userOp.accountGasLimits.replace('0x', '').padStart(64, '0');
|
|
@@ -122,3 +127,15 @@ export function getUserOpHashV07(userOp, entryPoint, chainId) {
|
|
|
122
127
|
]));
|
|
123
128
|
return keccak256(encodeAbiParameters(['bytes32', 'address', 'uint256'].map(t => ({ type: t })), [hashedUserOp, entryPoint, chainId]));
|
|
124
129
|
}
|
|
130
|
+
/**
|
|
131
|
+
* Tune gas limit using a dynamic nominal ceiling to satisfy Bundler efficiency (0.4)
|
|
132
|
+
* Target: Actual / Limit >= targetEfficiency
|
|
133
|
+
*/
|
|
134
|
+
export function tuneGasLimit(estimate, nominalActual, targetEfficiency = 0.45) {
|
|
135
|
+
if (estimate === 0n)
|
|
136
|
+
return 0n;
|
|
137
|
+
// targetEfficiency = actual / ceiling => ceiling = actual / targetEfficiency
|
|
138
|
+
const ceiling = (nominalActual * 100n) / BigInt(Math.floor(targetEfficiency * 100));
|
|
139
|
+
// Return the more restrictive limit to ensure efficiency ratio is met
|
|
140
|
+
return estimate < ceiling ? estimate : ceiling;
|
|
141
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { encodeFunctionData, parseAbi } from 'viem';
|
|
2
2
|
import { PaymasterClient } from './PaymasterClient';
|
|
3
|
-
import {
|
|
3
|
+
import { tuneGasLimit } from './PaymasterUtils';
|
|
4
4
|
/**
|
|
5
5
|
* SuperPaymasterClient
|
|
6
6
|
* High-level API for SuperPaymaster operations, including dynamic gas estimation.
|
|
@@ -11,12 +11,6 @@ export class SuperPaymasterClient {
|
|
|
11
11
|
* Automatically handles gas estimation with a smart efficiency buffer.
|
|
12
12
|
*/
|
|
13
13
|
static async submitGaslessTransaction(client, wallet, aaAddress, entryPoint, bundlerUrl, config) {
|
|
14
|
-
// Validation (Phase 0 Hardening)
|
|
15
|
-
validateAddress(config.token, 'Token Address');
|
|
16
|
-
validateAddress(config.recipient, 'Recipient Address');
|
|
17
|
-
validateAddress(config.operator, 'Operator Address');
|
|
18
|
-
validateAddress(config.paymasterAddress, 'Paymaster Address');
|
|
19
|
-
validateAmount(config.amount, 'Amount', 1n); // Must be > 0
|
|
20
14
|
// 1. Prepare Calldata (Standard ERC20 Transfer)
|
|
21
15
|
const callData = encodeFunctionData({
|
|
22
16
|
abi: parseAbi(['function execute(address dest, uint256 value, bytes func) external']),
|
|
@@ -53,18 +47,30 @@ export class SuperPaymasterClient {
|
|
|
53
47
|
// Safety Floor: If estimate is suspiciously low (e.g. < 50k), bump it for PM logic
|
|
54
48
|
// But if we bump it too high, we hit "Efficiency too low".
|
|
55
49
|
// Let's trust the bundler's estimate but add a fixed safety pad for dynamic storage
|
|
56
|
-
const SAFETY_PAD =
|
|
50
|
+
const SAFETY_PAD = 80000n;
|
|
57
51
|
const tunedVGL = vgl + SAFETY_PAD;
|
|
52
|
+
// CRITICAL FIX: Set paymasterVerificationGasLimit for optimal efficiency
|
|
53
|
+
// Bundler requires efficiency ratio >= 0.4 (actual_gas_used / gas_limit >= 0.4)
|
|
54
|
+
// SuperPaymaster validatePaymasterUserOp uses ~110-120k gas (measured)
|
|
55
|
+
// Test results:
|
|
56
|
+
// Analysis (Jan 19 Update-Final-Final):
|
|
57
|
+
// Instead of fixed percentage, use "Dynamic Nominal Gas Tuning".
|
|
58
|
+
// SuperPaymaster validation common case is ~58k-66k. Worst case (refresh) 115k.
|
|
59
|
+
// Setting nominal benchmark to 60k gives Ceiling = 60k / 0.45 = 133,333.
|
|
60
|
+
// This satisfies 0.4 efficiency (58/133=0.43) AND execution (115 < 133).
|
|
61
|
+
const bundlerEstimate = est.paymasterVerificationGasLimit || 100000n;
|
|
62
|
+
const tunedPMVerificationGas = tuneGasLimit(bundlerEstimate, 60000n, 0.45);
|
|
58
63
|
// Same for PostOp
|
|
59
64
|
const tunedPostOp = est.paymasterPostOpGasLimit + 10000n;
|
|
60
|
-
console.log(`[SuperPaymasterClient] 🔧 Tuned Limits: VGL=${tunedVGL}, PostOp=${tunedPostOp}`);
|
|
65
|
+
console.log(`[SuperPaymasterClient] 🔧 Tuned Limits: VGL=${tunedVGL}, PMVGL=${tunedPMVerificationGas}, PostOp=${tunedPostOp}`);
|
|
61
66
|
// 4. Submit with Tuned Limits
|
|
62
67
|
return PaymasterClient.submitGaslessUserOperation(client, wallet, aaAddress, entryPoint, config.paymasterAddress, config.token, bundlerUrl, callData, {
|
|
63
68
|
operator: config.operator,
|
|
64
69
|
verificationGasLimit: tunedVGL,
|
|
65
70
|
callGasLimit: est.callGasLimit,
|
|
66
71
|
preVerificationGas: est.preVerificationGas,
|
|
67
|
-
|
|
72
|
+
paymasterVerificationGasLimit: tunedPMVerificationGas, // EXPLICIT PM LIMIT
|
|
73
|
+
paymasterPostOpGasLimit: tunedPostOp,
|
|
68
74
|
autoEstimate: false, // We did it ourselves
|
|
69
75
|
factory: config.factory,
|
|
70
76
|
factoryData: config.factoryData
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aastar/paymaster",
|
|
3
|
-
"version": "0.16.
|
|
3
|
+
"version": "0.16.12",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -9,18 +9,15 @@
|
|
|
9
9
|
],
|
|
10
10
|
"dependencies": {
|
|
11
11
|
"viem": "2.43.3",
|
|
12
|
-
"@aastar/core": "0.16.
|
|
12
|
+
"@aastar/core": "0.16.12"
|
|
13
13
|
},
|
|
14
14
|
"devDependencies": {
|
|
15
|
-
"typescript": "5.
|
|
15
|
+
"typescript": "5.6.3"
|
|
16
16
|
},
|
|
17
|
-
"publishConfig": {
|
|
18
|
-
"access": "public"
|
|
19
|
-
},
|
|
20
|
-
"license": "MIT",
|
|
21
17
|
"scripts": {
|
|
22
18
|
"clean": "find src -name '*.js' -o -name '*.d.ts' -o -name '*.map' | xargs rm -f",
|
|
23
19
|
"prebuild": "pnpm clean",
|
|
24
|
-
"build": "tsc"
|
|
20
|
+
"build": "tsc",
|
|
21
|
+
"test": "vitest run"
|
|
25
22
|
}
|
|
26
23
|
}
|