@aastar/paymaster 0.16.7
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/LICENSE +21 -0
- package/dist/core/src/abis/BLSAggregator.json +686 -0
- package/dist/core/src/abis/BLSValidator.json +42 -0
- package/dist/core/src/abis/DVTValidator.json +368 -0
- package/dist/core/src/abis/EntryPoint.json +1382 -0
- package/dist/core/src/abis/GToken.json +513 -0
- package/dist/core/src/abis/GTokenStaking.json +949 -0
- package/dist/core/src/abis/MySBT.json +1518 -0
- package/dist/core/src/abis/Paymaster.json +1143 -0
- package/dist/core/src/abis/PaymasterFactory.json +640 -0
- package/dist/core/src/abis/Registry.json +1942 -0
- package/dist/core/src/abis/ReputationSystem.json +699 -0
- package/dist/core/src/abis/SimpleAccount.json +560 -0
- package/dist/core/src/abis/SimpleAccountFactory.json +111 -0
- package/dist/core/src/abis/SuperPaymaster.json +1781 -0
- package/dist/core/src/abis/index.d.ts +1126 -0
- package/dist/core/src/abis/index.js +91 -0
- package/dist/core/src/abis/xPNTsFactory.json +718 -0
- package/dist/core/src/abis/xPNTsToken.json +1280 -0
- package/dist/core/src/actions/StateValidator.d.ts +68 -0
- package/dist/core/src/actions/StateValidator.js +187 -0
- package/dist/core/src/actions/account.d.ts +55 -0
- package/dist/core/src/actions/account.js +133 -0
- package/dist/core/src/actions/aggregator.d.ts +17 -0
- package/dist/core/src/actions/aggregator.js +31 -0
- package/dist/core/src/actions/dvt.d.ts +30 -0
- package/dist/core/src/actions/dvt.js +41 -0
- package/dist/core/src/actions/entryPoint.d.ts +90 -0
- package/dist/core/src/actions/entryPoint.js +211 -0
- package/dist/core/src/actions/factory.d.ts +215 -0
- package/dist/core/src/actions/factory.js +442 -0
- package/dist/core/src/actions/faucet.d.ts +48 -0
- package/dist/core/src/actions/faucet.js +337 -0
- package/dist/core/src/actions/gtokenExtended.d.ts +39 -0
- package/dist/core/src/actions/gtokenExtended.js +115 -0
- package/dist/core/src/actions/index.d.ts +15 -0
- package/dist/core/src/actions/index.js +17 -0
- package/dist/core/src/actions/paymasterV4.d.ts +170 -0
- package/dist/core/src/actions/paymasterV4.js +334 -0
- package/dist/core/src/actions/registry.d.ts +246 -0
- package/dist/core/src/actions/registry.js +667 -0
- package/dist/core/src/actions/reputation.d.ts +129 -0
- package/dist/core/src/actions/reputation.js +281 -0
- package/dist/core/src/actions/sbt.d.ts +191 -0
- package/dist/core/src/actions/sbt.js +533 -0
- package/dist/core/src/actions/staking.d.ts +132 -0
- package/dist/core/src/actions/staking.js +330 -0
- package/dist/core/src/actions/superPaymaster.d.ts +237 -0
- package/dist/core/src/actions/superPaymaster.js +644 -0
- package/dist/core/src/actions/tokens.d.ts +229 -0
- package/dist/core/src/actions/tokens.js +415 -0
- package/dist/core/src/branding.d.ts +30 -0
- package/dist/core/src/branding.js +30 -0
- package/dist/core/src/clients/BaseClient.d.ts +25 -0
- package/dist/core/src/clients/BaseClient.js +66 -0
- package/dist/core/src/clients/types.d.ts +60 -0
- package/dist/core/src/clients/types.js +1 -0
- package/dist/core/src/clients.d.ts +5 -0
- package/dist/core/src/clients.js +11 -0
- package/dist/core/src/communities.d.ts +52 -0
- package/dist/core/src/communities.js +73 -0
- package/dist/core/src/config/ContractConfigManager.d.ts +20 -0
- package/dist/core/src/config/ContractConfigManager.js +48 -0
- package/dist/core/src/constants.d.ts +88 -0
- package/dist/core/src/constants.js +125 -0
- package/dist/core/src/contract-addresses.d.ts +110 -0
- package/dist/core/src/contract-addresses.js +99 -0
- package/dist/core/src/contracts.d.ts +424 -0
- package/dist/core/src/contracts.js +343 -0
- package/dist/core/src/crypto/blsSigner.d.ts +64 -0
- package/dist/core/src/crypto/blsSigner.js +98 -0
- package/dist/core/src/crypto/index.d.ts +1 -0
- package/dist/core/src/crypto/index.js +1 -0
- package/dist/core/src/index.d.ts +21 -0
- package/dist/core/src/index.js +21 -0
- package/dist/core/src/networks.d.ts +127 -0
- package/dist/core/src/networks.js +118 -0
- package/dist/core/src/requirementChecker.d.ts +38 -0
- package/dist/core/src/requirementChecker.js +139 -0
- package/dist/core/src/roles.d.ts +204 -0
- package/dist/core/src/roles.js +211 -0
- package/dist/core/src/utils/validation.d.ts +24 -0
- package/dist/core/src/utils/validation.js +56 -0
- package/dist/paymaster/src/SuperPaymaster/index.d.ts +44 -0
- package/dist/paymaster/src/SuperPaymaster/index.js +133 -0
- package/dist/paymaster/src/V4/PaymasterClient.d.ts +79 -0
- package/dist/paymaster/src/V4/PaymasterClient.js +315 -0
- package/dist/paymaster/src/V4/PaymasterClient.test.d.ts +1 -0
- package/dist/paymaster/src/V4/PaymasterClient.test.js +80 -0
- package/dist/paymaster/src/V4/PaymasterOperator.d.ts +41 -0
- package/dist/paymaster/src/V4/PaymasterOperator.js +241 -0
- package/dist/paymaster/src/V4/PaymasterOperator.test.d.ts +1 -0
- package/dist/paymaster/src/V4/PaymasterOperator.test.js +66 -0
- package/dist/paymaster/src/V4/PaymasterUtils.d.ts +55 -0
- package/dist/paymaster/src/V4/PaymasterUtils.js +124 -0
- package/dist/paymaster/src/V4/PaymasterUtils.test.d.ts +1 -0
- package/dist/paymaster/src/V4/PaymasterUtils.test.js +43 -0
- package/dist/paymaster/src/V4/SuperPaymasterClient.d.ts +21 -0
- package/dist/paymaster/src/V4/SuperPaymasterClient.js +73 -0
- package/dist/paymaster/src/V4/SuperPaymasterClient.test.d.ts +1 -0
- package/dist/paymaster/src/V4/SuperPaymasterClient.test.js +53 -0
- package/dist/paymaster/src/V4/index.d.ts +4 -0
- package/dist/paymaster/src/V4/index.js +4 -0
- package/dist/paymaster/src/index.d.ts +2 -0
- package/dist/paymaster/src/index.js +4 -0
- package/package.json +26 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
2
|
+
import { PaymasterOperator } from './PaymasterOperator.js';
|
|
3
|
+
describe('PaymasterOperator', () => {
|
|
4
|
+
const MOCK_PM = '0x1111111111111111111111111111111111111111';
|
|
5
|
+
const MOCK_USER = '0x2222222222222222222222222222222222222222';
|
|
6
|
+
const MOCK_TOKEN = '0x3333333333333333333333333333333333333333';
|
|
7
|
+
const MOCK_EP = '0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789';
|
|
8
|
+
let mockPublicClient;
|
|
9
|
+
let mockWallet;
|
|
10
|
+
beforeEach(() => {
|
|
11
|
+
vi.clearAllMocks();
|
|
12
|
+
mockPublicClient = {
|
|
13
|
+
readContract: vi.fn(),
|
|
14
|
+
waitForTransactionReceipt: vi.fn().mockResolvedValue({ status: 'success' })
|
|
15
|
+
};
|
|
16
|
+
mockWallet = {
|
|
17
|
+
writeContract: vi.fn().mockResolvedValue('0xhash'),
|
|
18
|
+
account: { address: MOCK_USER },
|
|
19
|
+
chain: { id: 31337 }
|
|
20
|
+
};
|
|
21
|
+
});
|
|
22
|
+
it('should update price', async () => {
|
|
23
|
+
const hash = await PaymasterOperator.updatePrice(mockWallet, MOCK_PM);
|
|
24
|
+
expect(hash).toBe('0xhash');
|
|
25
|
+
});
|
|
26
|
+
it('should get cached price', async () => {
|
|
27
|
+
mockPublicClient.readContract.mockResolvedValue([1000n, 123n]);
|
|
28
|
+
const { price, updatedAt } = await PaymasterOperator.getCachedPrice(mockPublicClient, MOCK_PM);
|
|
29
|
+
expect(price).toBe(1000n);
|
|
30
|
+
expect(updatedAt).toBe(123n);
|
|
31
|
+
});
|
|
32
|
+
it('should check gasless readiness', async () => {
|
|
33
|
+
mockPublicClient.readContract
|
|
34
|
+
.mockResolvedValueOnce([10n ** 18n, true, 10n ** 18n, 100000, 0]) // EntryPoint depositInfo
|
|
35
|
+
.mockResolvedValueOnce([2000n, 123n]) // cachedPrice
|
|
36
|
+
.mockResolvedValueOnce(1n) // tokenPrice
|
|
37
|
+
.mockResolvedValueOnce(1000n) // balance
|
|
38
|
+
.mockResolvedValueOnce(500n); // deposit
|
|
39
|
+
const report = await PaymasterOperator.checkGaslessReadiness(mockPublicClient, MOCK_EP, MOCK_PM, MOCK_USER, MOCK_TOKEN);
|
|
40
|
+
expect(report.isReady).toBe(true);
|
|
41
|
+
expect(report.issues.length).toBe(0);
|
|
42
|
+
});
|
|
43
|
+
it('should identify readiness issues', async () => {
|
|
44
|
+
mockPublicClient.readContract
|
|
45
|
+
.mockResolvedValueOnce([0n, false, 0n, 0, 0]) // EntryPoint depositInfo
|
|
46
|
+
.mockResolvedValueOnce([0n, 0n]) // cachedPrice
|
|
47
|
+
.mockResolvedValueOnce(0n) // tokenPrice
|
|
48
|
+
.mockResolvedValueOnce(0n) // balance
|
|
49
|
+
.mockResolvedValueOnce(0n); // deposit
|
|
50
|
+
const report = await PaymasterOperator.checkGaslessReadiness(mockPublicClient, MOCK_EP, MOCK_PM, MOCK_USER, MOCK_TOKEN);
|
|
51
|
+
expect(report.isReady).toBe(false);
|
|
52
|
+
expect(report.issues.length).toBeGreaterThan(0);
|
|
53
|
+
});
|
|
54
|
+
it('should prepare gasless environment', async () => {
|
|
55
|
+
// Mock readiness check to have issues
|
|
56
|
+
mockPublicClient.readContract
|
|
57
|
+
.mockResolvedValueOnce([0n, false, 0n, 0, 0]) // first check inside prepareGaslessEnvironment
|
|
58
|
+
.mockResolvedValueOnce([0n, 0n])
|
|
59
|
+
.mockResolvedValueOnce(0n)
|
|
60
|
+
.mockResolvedValueOnce(0n)
|
|
61
|
+
.mockResolvedValueOnce(0n);
|
|
62
|
+
const results = await PaymasterOperator.prepareGaslessEnvironment(mockWallet, mockPublicClient, MOCK_EP, MOCK_PM, MOCK_TOKEN);
|
|
63
|
+
expect(results.length).toBeGreaterThan(0);
|
|
64
|
+
expect(mockWallet.writeContract).toHaveBeenCalled();
|
|
65
|
+
});
|
|
66
|
+
});
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { type Address, type Hex } from 'viem';
|
|
2
|
+
export type PaymasterV4MiddlewareConfig = {
|
|
3
|
+
paymasterAddress: Address;
|
|
4
|
+
gasToken: Address;
|
|
5
|
+
verificationGasLimit?: bigint;
|
|
6
|
+
postOpGasLimit?: bigint;
|
|
7
|
+
};
|
|
8
|
+
export type GaslessReadinessReport = {
|
|
9
|
+
isReady: boolean;
|
|
10
|
+
issues: string[];
|
|
11
|
+
details: {
|
|
12
|
+
paymasterStake: bigint;
|
|
13
|
+
paymasterDeposit: bigint;
|
|
14
|
+
ethUsdPrice: bigint;
|
|
15
|
+
tokenSupported: boolean;
|
|
16
|
+
tokenPrice: bigint;
|
|
17
|
+
userTokenBalance: bigint;
|
|
18
|
+
userPaymasterDeposit: bigint;
|
|
19
|
+
};
|
|
20
|
+
};
|
|
21
|
+
/**
|
|
22
|
+
* Constructs the middleware for Paymaster V4.
|
|
23
|
+
* Returns the `paymasterAndData` hex string.
|
|
24
|
+
*/
|
|
25
|
+
export declare function getPaymasterV4Middleware(config: PaymasterV4MiddlewareConfig): {
|
|
26
|
+
sponsorUserOperation: (args: {
|
|
27
|
+
userOperation: any;
|
|
28
|
+
}) => Promise<{
|
|
29
|
+
paymasterAndData: `0x${string}`;
|
|
30
|
+
verificationGasLimit: bigint;
|
|
31
|
+
preVerificationGas: any;
|
|
32
|
+
}>;
|
|
33
|
+
};
|
|
34
|
+
/**
|
|
35
|
+
* Build paymasterAndData for gasless UserOperation.
|
|
36
|
+
* Layout: [Paymaster(20)] [VerificationGasLimit(16)] [PostOpGasLimit(16)] [Token(20)] [ValidUntil(6)] [ValidAfter(6)]
|
|
37
|
+
*/
|
|
38
|
+
export declare function buildPaymasterData(paymasterAddress: Address, token: Address, options?: {
|
|
39
|
+
validityWindow?: number;
|
|
40
|
+
verificationGasLimit?: bigint;
|
|
41
|
+
postOpGasLimit?: bigint;
|
|
42
|
+
}): `0x${string}`;
|
|
43
|
+
/**
|
|
44
|
+
* Build paymasterAndData for SuperPaymaster V3.
|
|
45
|
+
* Layout: [Paymaster(20)] [verGas(16)] [postGas(16)] [operator(20)] [maxRate(32)]
|
|
46
|
+
*/
|
|
47
|
+
export declare function buildSuperPaymasterData(paymasterAddress: Address, operator: Address, options?: {
|
|
48
|
+
verificationGasLimit?: bigint;
|
|
49
|
+
postOpGasLimit?: bigint;
|
|
50
|
+
}): `0x${string}`;
|
|
51
|
+
/**
|
|
52
|
+
* Helper to format UserOp for Alchemy/Standard Bundlers (v0.7 Decomposed)
|
|
53
|
+
*/
|
|
54
|
+
export declare function formatUserOpV07(userOp: any): any;
|
|
55
|
+
export declare function getUserOpHashV07(userOp: any, entryPoint: Address, chainId: bigint): Hex;
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { concat, pad, toHex, keccak256, encodeAbiParameters, toBytes } from 'viem';
|
|
2
|
+
import { validateAddress, validateUint128 } from '@aastar/core';
|
|
3
|
+
const DEFAULT_VERIFICATION_GAS_V4 = 80000n;
|
|
4
|
+
const DEFAULT_POSTOP_GAS_V4 = 100000n;
|
|
5
|
+
/**
|
|
6
|
+
* Constructs the middleware for Paymaster V4.
|
|
7
|
+
* Returns the `paymasterAndData` hex string.
|
|
8
|
+
*/
|
|
9
|
+
export function getPaymasterV4Middleware(config) {
|
|
10
|
+
return {
|
|
11
|
+
sponsorUserOperation: async (args) => {
|
|
12
|
+
const verGas = config.verificationGasLimit ?? DEFAULT_VERIFICATION_GAS_V4;
|
|
13
|
+
const postGas = config.postOpGasLimit ?? DEFAULT_POSTOP_GAS_V4;
|
|
14
|
+
// Layout: [Paymaster(20)] [VerGas(16)] [PostOpGas(16)] [Token(20)]
|
|
15
|
+
const paymasterAndData = concat([
|
|
16
|
+
config.paymasterAddress,
|
|
17
|
+
pad(toHex(verGas), { size: 16 }),
|
|
18
|
+
pad(toHex(postGas), { size: 16 }),
|
|
19
|
+
config.gasToken
|
|
20
|
+
]);
|
|
21
|
+
return {
|
|
22
|
+
paymasterAndData,
|
|
23
|
+
verificationGasLimit: verGas,
|
|
24
|
+
preVerificationGas: args.userOperation.preVerificationGas
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Build paymasterAndData for gasless UserOperation.
|
|
31
|
+
* Layout: [Paymaster(20)] [VerificationGasLimit(16)] [PostOpGasLimit(16)] [Token(20)] [ValidUntil(6)] [ValidAfter(6)]
|
|
32
|
+
*/
|
|
33
|
+
export function buildPaymasterData(paymasterAddress, token, options) {
|
|
34
|
+
const validityWindow = options?.validityWindow ?? 3600;
|
|
35
|
+
const verGas = options?.verificationGasLimit ?? 80000n;
|
|
36
|
+
const postGas = options?.postOpGasLimit ?? 100000n;
|
|
37
|
+
// Hardening: uint128 bounds check
|
|
38
|
+
validateUint128(verGas, 'verificationGasLimit');
|
|
39
|
+
validateUint128(postGas, 'postOpGasLimit');
|
|
40
|
+
const now = Math.floor(Date.now() / 1000);
|
|
41
|
+
const validUntil = now + validityWindow;
|
|
42
|
+
const validAfter = now - 100; // 100 seconds grace period
|
|
43
|
+
return concat([
|
|
44
|
+
paymasterAddress,
|
|
45
|
+
pad(toHex(verGas), { size: 16 }),
|
|
46
|
+
pad(toHex(postGas), { size: 16 }),
|
|
47
|
+
token,
|
|
48
|
+
pad(toHex(validUntil), { size: 6 }),
|
|
49
|
+
pad(toHex(validAfter), { size: 6 })
|
|
50
|
+
]);
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Build paymasterAndData for SuperPaymaster V3.
|
|
54
|
+
* Layout: [Paymaster(20)] [verGas(16)] [postGas(16)] [operator(20)] [maxRate(32)]
|
|
55
|
+
*/
|
|
56
|
+
export function buildSuperPaymasterData(paymasterAddress, operator, options) {
|
|
57
|
+
const verGas = options?.verificationGasLimit ?? 80000n;
|
|
58
|
+
const postGas = options?.postOpGasLimit ?? 100000n;
|
|
59
|
+
// Hardening
|
|
60
|
+
validateAddress(paymasterAddress, 'Paymaster Address');
|
|
61
|
+
validateAddress(operator, 'Operator Address');
|
|
62
|
+
validateUint128(verGas, 'verificationGasLimit');
|
|
63
|
+
validateUint128(postGas, 'postOpGasLimit');
|
|
64
|
+
return concat([
|
|
65
|
+
paymasterAddress,
|
|
66
|
+
pad(toHex(verGas), { size: 16 }),
|
|
67
|
+
pad(toHex(postGas), { size: 16 }),
|
|
68
|
+
operator
|
|
69
|
+
]);
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Helper to format UserOp for Alchemy/Standard Bundlers (v0.7 Decomposed)
|
|
73
|
+
*/
|
|
74
|
+
export function formatUserOpV07(userOp) {
|
|
75
|
+
const result = {
|
|
76
|
+
sender: userOp.sender,
|
|
77
|
+
nonce: toHex(userOp.nonce),
|
|
78
|
+
callData: userOp.callData,
|
|
79
|
+
preVerificationGas: toHex(userOp.preVerificationGas),
|
|
80
|
+
signature: userOp.signature,
|
|
81
|
+
initCode: userOp.initCode
|
|
82
|
+
};
|
|
83
|
+
// Extract Factory/FactoryData if present
|
|
84
|
+
if (userOp.initCode && userOp.initCode !== '0x') {
|
|
85
|
+
result.factory = userOp.initCode.slice(0, 42);
|
|
86
|
+
result.factoryData = '0x' + userOp.initCode.slice(42);
|
|
87
|
+
}
|
|
88
|
+
// Unpack accountGasLimits: [verificationGasLimit(16)][callGasLimit(16)]
|
|
89
|
+
if (userOp.accountGasLimits && userOp.accountGasLimits !== '0x') {
|
|
90
|
+
const packed = userOp.accountGasLimits.replace('0x', '').padStart(64, '0');
|
|
91
|
+
result.verificationGasLimit = '0x' + BigInt('0x' + packed.slice(0, 32)).toString(16);
|
|
92
|
+
result.callGasLimit = '0x' + BigInt('0x' + packed.slice(32, 64)).toString(16);
|
|
93
|
+
}
|
|
94
|
+
// Unpack gasFees: [maxPriorityFee(16)][maxFee(16)]
|
|
95
|
+
if (userOp.gasFees && userOp.gasFees !== '0x') {
|
|
96
|
+
const packed = userOp.gasFees.replace('0x', '').padStart(64, '0');
|
|
97
|
+
result.maxPriorityFeePerGas = '0x' + BigInt('0x' + packed.slice(0, 32)).toString(16);
|
|
98
|
+
result.maxFeePerGas = '0x' + BigInt('0x' + packed.slice(32, 64)).toString(16);
|
|
99
|
+
}
|
|
100
|
+
// Unpack paymasterAndData: [paymaster(20)][verificationGas(16)][postOpGas(16)][paymasterData]
|
|
101
|
+
if (userOp.paymasterAndData && userOp.paymasterAndData !== '0x') {
|
|
102
|
+
const packed = userOp.paymasterAndData.replace('0x', '');
|
|
103
|
+
if (packed.length >= 104) {
|
|
104
|
+
result.paymaster = '0x' + packed.slice(0, 40);
|
|
105
|
+
result.paymasterVerificationGasLimit = '0x' + BigInt('0x' + packed.slice(40, 72)).toString(16);
|
|
106
|
+
result.paymasterPostOpGasLimit = '0x' + BigInt('0x' + packed.slice(72, 104)).toString(16);
|
|
107
|
+
result.paymasterData = '0x' + packed.slice(104);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
return result;
|
|
111
|
+
}
|
|
112
|
+
export function getUserOpHashV07(userOp, entryPoint, chainId) {
|
|
113
|
+
const hashedUserOp = keccak256(encodeAbiParameters(['address', 'uint256', 'bytes32', 'bytes32', 'bytes32', 'uint256', 'bytes32', 'bytes32'].map(t => ({ type: t })), [
|
|
114
|
+
userOp.sender,
|
|
115
|
+
userOp.nonce,
|
|
116
|
+
keccak256(toBytes(userOp.initCode)),
|
|
117
|
+
keccak256(toBytes(userOp.callData)),
|
|
118
|
+
userOp.accountGasLimits,
|
|
119
|
+
toHex(userOp.preVerificationGas),
|
|
120
|
+
userOp.gasFees,
|
|
121
|
+
keccak256(toBytes(userOp.paymasterAndData))
|
|
122
|
+
]));
|
|
123
|
+
return keccak256(encodeAbiParameters(['bytes32', 'address', 'uint256'].map(t => ({ type: t })), [hashedUserOp, entryPoint, chainId]));
|
|
124
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { getPaymasterV4Middleware, buildPaymasterData, buildSuperPaymasterData, formatUserOpV07 } from './PaymasterUtils.js';
|
|
3
|
+
describe('PaymasterUtils', () => {
|
|
4
|
+
const MOCK_PM = '0x1111111111111111111111111111111111111111';
|
|
5
|
+
const MOCK_TOKEN = '0x2222222222222222222222222222222222222222';
|
|
6
|
+
const MOCK_OPERATOR = '0x3333333333333333333333333333333333333333';
|
|
7
|
+
it('should get V4 middleware', async () => {
|
|
8
|
+
const middleware = getPaymasterV4Middleware({
|
|
9
|
+
paymasterAddress: MOCK_PM,
|
|
10
|
+
gasToken: MOCK_TOKEN
|
|
11
|
+
});
|
|
12
|
+
const result = await middleware.sponsorUserOperation({
|
|
13
|
+
userOperation: { preVerificationGas: 100n }
|
|
14
|
+
});
|
|
15
|
+
expect(result.paymasterAndData).toBeDefined();
|
|
16
|
+
expect(result.paymasterAndData.toLowerCase()).toContain(MOCK_PM.toLowerCase().replace('0x', ''));
|
|
17
|
+
});
|
|
18
|
+
it('should build paymaster data for V4', () => {
|
|
19
|
+
const data = buildPaymasterData(MOCK_PM, MOCK_TOKEN);
|
|
20
|
+
expect(data.length).toBe(2 + 40 + 32 + 32 + 40 + 12 + 12); // Layout check
|
|
21
|
+
});
|
|
22
|
+
it('should build super paymaster data', () => {
|
|
23
|
+
const data = buildSuperPaymasterData(MOCK_PM, MOCK_OPERATOR);
|
|
24
|
+
expect(data.toLowerCase()).toContain(MOCK_OPERATOR.toLowerCase().replace('0x', ''));
|
|
25
|
+
});
|
|
26
|
+
it('should format UserOp for v0.7', () => {
|
|
27
|
+
const userOp = {
|
|
28
|
+
sender: MOCK_PM,
|
|
29
|
+
nonce: 1n,
|
|
30
|
+
callData: '0x123',
|
|
31
|
+
preVerificationGas: 1000n,
|
|
32
|
+
accountGasLimits: '0x' + '1'.repeat(32) + '2'.repeat(32),
|
|
33
|
+
gasFees: '0x' + '3'.repeat(32) + '4'.repeat(32),
|
|
34
|
+
paymasterAndData: '0x' + MOCK_PM.slice(2) + '5'.repeat(32) + '6'.repeat(32) + '7788',
|
|
35
|
+
signature: '0x'
|
|
36
|
+
};
|
|
37
|
+
const formatted = formatUserOpV07(userOp);
|
|
38
|
+
expect(formatted.sender).toBe(MOCK_PM);
|
|
39
|
+
expect(formatted.verificationGasLimit).toBeDefined();
|
|
40
|
+
expect(formatted.maxFeePerGas).toBeDefined();
|
|
41
|
+
expect(formatted.paymaster).toBe(MOCK_PM);
|
|
42
|
+
});
|
|
43
|
+
});
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { type Address, type Hex } from 'viem';
|
|
2
|
+
export type GaslessTransactionConfig = {
|
|
3
|
+
token: Address;
|
|
4
|
+
recipient: Address;
|
|
5
|
+
amount: bigint;
|
|
6
|
+
operator: Address;
|
|
7
|
+
paymasterAddress: Address;
|
|
8
|
+
factory?: Address;
|
|
9
|
+
factoryData?: Hex;
|
|
10
|
+
};
|
|
11
|
+
/**
|
|
12
|
+
* SuperPaymasterClient
|
|
13
|
+
* High-level API for SuperPaymaster operations, including dynamic gas estimation.
|
|
14
|
+
*/
|
|
15
|
+
export declare class SuperPaymasterClient {
|
|
16
|
+
/**
|
|
17
|
+
* Submit a gasless transaction using SuperPaymaster.
|
|
18
|
+
* Automatically handles gas estimation with a smart efficiency buffer.
|
|
19
|
+
*/
|
|
20
|
+
static submitGaslessTransaction(client: any, wallet: any, aaAddress: Address, entryPoint: Address, bundlerUrl: string, config: GaslessTransactionConfig): Promise<Hex>;
|
|
21
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { encodeFunctionData, parseAbi } from 'viem';
|
|
2
|
+
import { PaymasterClient } from './PaymasterClient';
|
|
3
|
+
import { validateAddress, validateAmount } from '@aastar/core';
|
|
4
|
+
/**
|
|
5
|
+
* SuperPaymasterClient
|
|
6
|
+
* High-level API for SuperPaymaster operations, including dynamic gas estimation.
|
|
7
|
+
*/
|
|
8
|
+
export class SuperPaymasterClient {
|
|
9
|
+
/**
|
|
10
|
+
* Submit a gasless transaction using SuperPaymaster.
|
|
11
|
+
* Automatically handles gas estimation with a smart efficiency buffer.
|
|
12
|
+
*/
|
|
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
|
+
// 1. Prepare Calldata (Standard ERC20 Transfer)
|
|
21
|
+
const callData = encodeFunctionData({
|
|
22
|
+
abi: parseAbi(['function execute(address dest, uint256 value, bytes func) external']),
|
|
23
|
+
functionName: 'execute',
|
|
24
|
+
args: [
|
|
25
|
+
config.token,
|
|
26
|
+
0n,
|
|
27
|
+
encodeFunctionData({
|
|
28
|
+
abi: parseAbi(['function transfer(address to, uint256 amount) external returns (bool)']),
|
|
29
|
+
functionName: 'transfer',
|
|
30
|
+
args: [config.recipient, config.amount]
|
|
31
|
+
})
|
|
32
|
+
]
|
|
33
|
+
});
|
|
34
|
+
// 2. Initial Gas Estimation (Bundler Query)
|
|
35
|
+
console.log('[SuperPaymasterClient] ☁️ Estimating Gas usage...');
|
|
36
|
+
const est = await PaymasterClient.estimateUserOperationGas(client, wallet, aaAddress, entryPoint, config.paymasterAddress, config.token, bundlerUrl, callData, {
|
|
37
|
+
operator: config.operator,
|
|
38
|
+
factory: config.factory,
|
|
39
|
+
factoryData: config.factoryData
|
|
40
|
+
});
|
|
41
|
+
console.log('[SuperPaymasterClient] ☁️ Bundler Estimates:', est);
|
|
42
|
+
// 3. Apply Smart Buffer Strategy
|
|
43
|
+
// We need to ensure:
|
|
44
|
+
// A. verificationGasLimit >= est.verificationGasLimit (Bundler's minimum)
|
|
45
|
+
// B. verificationGasLimit >= SuperPaymaster's Actual Usage (~80k-100k)
|
|
46
|
+
// C. verificationGasLimit / PreVerificationGas > 0.4 (Efficiency Ratio) PRE-CHECK
|
|
47
|
+
// Actually, the Bundler checks: (gas limits) / (actual gas used) > efficiency.
|
|
48
|
+
// So we must NOT set limits WAY higher than actual usage.
|
|
49
|
+
// Best strategy: Use Bundler's Estimate + Small Buffer (e.g. 10-20k).
|
|
50
|
+
// SuperPaymaster Logic Cost: ~80,000 to 120,000 gas depending on cold storage
|
|
51
|
+
// Bundler Estimate usually returns the *actual execution path* gas.
|
|
52
|
+
let vgl = est.verificationGasLimit;
|
|
53
|
+
// Safety Floor: If estimate is suspiciously low (e.g. < 50k), bump it for PM logic
|
|
54
|
+
// But if we bump it too high, we hit "Efficiency too low".
|
|
55
|
+
// Let's trust the bundler's estimate but add a fixed safety pad for dynamic storage
|
|
56
|
+
const SAFETY_PAD = 20000n;
|
|
57
|
+
const tunedVGL = vgl + SAFETY_PAD;
|
|
58
|
+
// Same for PostOp
|
|
59
|
+
const tunedPostOp = est.paymasterPostOpGasLimit + 10000n;
|
|
60
|
+
console.log(`[SuperPaymasterClient] 🔧 Tuned Limits: VGL=${tunedVGL}, PostOp=${tunedPostOp}`);
|
|
61
|
+
// 4. Submit with Tuned Limits
|
|
62
|
+
return PaymasterClient.submitGaslessUserOperation(client, wallet, aaAddress, entryPoint, config.paymasterAddress, config.token, bundlerUrl, callData, {
|
|
63
|
+
operator: config.operator,
|
|
64
|
+
verificationGasLimit: tunedVGL,
|
|
65
|
+
callGasLimit: est.callGasLimit,
|
|
66
|
+
preVerificationGas: est.preVerificationGas,
|
|
67
|
+
paymasterPostOpGasLimit: tunedPostOp, // Pass specific PM limits if supported
|
|
68
|
+
autoEstimate: false, // We did it ourselves
|
|
69
|
+
factory: config.factory,
|
|
70
|
+
factoryData: config.factoryData
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
2
|
+
import { SuperPaymasterClient } from './SuperPaymasterClient.js';
|
|
3
|
+
import { PaymasterClient } from './PaymasterClient.js';
|
|
4
|
+
// Mock PaymasterClient
|
|
5
|
+
vi.mock('./PaymasterClient.js', () => ({
|
|
6
|
+
PaymasterClient: {
|
|
7
|
+
estimateUserOperationGas: vi.fn(),
|
|
8
|
+
submitGaslessUserOperation: vi.fn()
|
|
9
|
+
}
|
|
10
|
+
}));
|
|
11
|
+
describe('SuperPaymasterClient', () => {
|
|
12
|
+
const MOCK_PM = '0x1111111111111111111111111111111111111111';
|
|
13
|
+
const MOCK_USER = '0x2222222222222222222222222222222222222222';
|
|
14
|
+
const MOCK_TOKEN = '0x3333333333333333333333333333333333333333';
|
|
15
|
+
const MOCK_RECIPIENT = '0x4444444444444444444444444444444444444444';
|
|
16
|
+
const MOCK_OPERATOR = '0x5555555555555555555555555555555555555555';
|
|
17
|
+
const MOCK_EP = '0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789';
|
|
18
|
+
beforeEach(() => {
|
|
19
|
+
vi.clearAllMocks();
|
|
20
|
+
});
|
|
21
|
+
it('should submit gasless transaction with tuned limits', async () => {
|
|
22
|
+
PaymasterClient.estimateUserOperationGas.mockResolvedValue({
|
|
23
|
+
verificationGasLimit: 100000n,
|
|
24
|
+
callGasLimit: 200000n,
|
|
25
|
+
preVerificationGas: 50000n,
|
|
26
|
+
paymasterPostOpGasLimit: 100000n
|
|
27
|
+
});
|
|
28
|
+
PaymasterClient.submitGaslessUserOperation.mockResolvedValue('0xuserOpHash');
|
|
29
|
+
const hash = await SuperPaymasterClient.submitGaslessTransaction({}, // client
|
|
30
|
+
{}, // wallet
|
|
31
|
+
MOCK_USER, MOCK_EP, 'http://bundler', {
|
|
32
|
+
token: MOCK_TOKEN,
|
|
33
|
+
recipient: MOCK_RECIPIENT,
|
|
34
|
+
amount: 100n,
|
|
35
|
+
operator: MOCK_OPERATOR,
|
|
36
|
+
paymasterAddress: MOCK_PM
|
|
37
|
+
});
|
|
38
|
+
expect(hash).toBe('0xuserOpHash');
|
|
39
|
+
expect(PaymasterClient.submitGaslessUserOperation).toHaveBeenCalledWith(expect.anything(), expect.anything(), expect.anything(), expect.anything(), expect.anything(), expect.anything(), expect.anything(), expect.anything(), expect.objectContaining({
|
|
40
|
+
verificationGasLimit: 120000n, // 100k + 20k buffer
|
|
41
|
+
autoEstimate: false
|
|
42
|
+
}));
|
|
43
|
+
});
|
|
44
|
+
it('should throw on invalid configuration', async () => {
|
|
45
|
+
await expect(SuperPaymasterClient.submitGaslessTransaction({}, {}, MOCK_USER, MOCK_EP, 'http://bundler', {
|
|
46
|
+
token: 'invalid',
|
|
47
|
+
recipient: MOCK_RECIPIENT,
|
|
48
|
+
amount: 100n,
|
|
49
|
+
operator: MOCK_OPERATOR,
|
|
50
|
+
paymasterAddress: MOCK_PM
|
|
51
|
+
})).rejects.toThrow();
|
|
52
|
+
});
|
|
53
|
+
});
|
package/package.json
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@aastar/paymaster",
|
|
3
|
+
"version": "0.16.7",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"files": [
|
|
8
|
+
"dist"
|
|
9
|
+
],
|
|
10
|
+
"dependencies": {
|
|
11
|
+
"viem": "2.43.3",
|
|
12
|
+
"@aastar/core": "0.16.7"
|
|
13
|
+
},
|
|
14
|
+
"devDependencies": {
|
|
15
|
+
"typescript": "5.7.2"
|
|
16
|
+
},
|
|
17
|
+
"publishConfig": {
|
|
18
|
+
"access": "public"
|
|
19
|
+
},
|
|
20
|
+
"license": "MIT",
|
|
21
|
+
"scripts": {
|
|
22
|
+
"clean": "find src -name '*.js' -o -name '*.d.ts' -o -name '*.map' | xargs rm -f",
|
|
23
|
+
"prebuild": "pnpm clean",
|
|
24
|
+
"build": "tsc"
|
|
25
|
+
}
|
|
26
|
+
}
|