@aastar/operator 0.16.11 → 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/package.json CHANGED
@@ -1,9 +1,12 @@
1
1
  {
2
2
  "name": "@aastar/operator",
3
- "version": "0.16.11",
3
+ "version": "0.16.12",
4
4
  "description": "Operator client for AAstar SDK",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
7
+ "files": [
8
+ "dist"
9
+ ],
7
10
  "keywords": [
8
11
  "aastar",
9
12
  "operator",
@@ -13,7 +16,7 @@
13
16
  "license": "MIT",
14
17
  "dependencies": {
15
18
  "viem": "2.43.3",
16
- "@aastar/core": "0.16.11"
19
+ "@aastar/core": "0.16.12"
17
20
  },
18
21
  "devDependencies": {
19
22
  "typescript": "5.7.2"
@@ -1,258 +0,0 @@
1
- import { describe, it, expect, vi, beforeEach } from 'vitest';
2
- import { PaymasterOperatorClient } from '../src/PaymasterOperatorClient';
3
- import { createMockPublicClient, createMockWalletClient, resetMocks } from './mocks/client';
4
- import { parseEther } from 'viem';
5
-
6
- // Define mocks
7
- const mocks = vi.hoisted(() => {
8
- const mockRegistry = { ROLE_COMMUNITY: vi.fn(), ROLE_PAYMASTER_SUPER: vi.fn(), ROLE_PAYMASTER_AOA: vi.fn(), hasRole: vi.fn(), registerRoleSelf: vi.fn() };
9
- const mockToken = { allowance: vi.fn(), approve: vi.fn() };
10
- const mockSuperPaymaster = { APNTS_TOKEN: vi.fn(), deposit: vi.fn(), configureOperator: vi.fn(), operators: vi.fn() };
11
- const mockPaymasterFactory = { deployPaymaster: vi.fn(), getPaymaster: vi.fn() };
12
- const mockPaymaster = { setTokenPrice: vi.fn(), tokenPrices: vi.fn(), depositFor: vi.fn() };
13
-
14
- return {
15
- mockRegistry,
16
- mockToken,
17
- mockSuperPaymaster,
18
- mockPaymasterFactory,
19
- mockPaymaster,
20
- // Factory Functions
21
- mockRegistryActions: vi.fn(() => () => mockRegistry),
22
- mockTokenActions: vi.fn(() => () => mockToken),
23
- mockSuperPaymasterActions: vi.fn(() => () => mockSuperPaymaster),
24
- mockPaymasterFactoryActions: vi.fn(() => () => mockPaymasterFactory),
25
- mockPaymasterActions: vi.fn(() => () => mockPaymaster),
26
- };
27
- });
28
-
29
- vi.mock('@aastar/core', async () => {
30
- const actual = await vi.importActual('@aastar/core');
31
- return {
32
- ...actual,
33
- registryActions: mocks.mockRegistryActions,
34
- tokenActions: mocks.mockTokenActions,
35
- superPaymasterActions: mocks.mockSuperPaymasterActions,
36
- paymasterFactoryActions: mocks.mockPaymasterFactoryActions,
37
- paymasterActions: mocks.mockPaymasterActions,
38
- };
39
- });
40
-
41
- vi.mock('viem', async () => {
42
- const actual = await vi.importActual('viem');
43
- return {
44
- ...actual,
45
- encodeFunctionData: vi.fn().mockReturnValue('0xInitData'),
46
- // encodeAbiParameters: vi.fn().mockReturnValue('0xAbiData') // Use real implementation if possible, or mock carefully
47
- };
48
- });
49
-
50
- describe('PaymasterOperatorClient', () => {
51
- let client: PaymasterOperatorClient;
52
- const mockPublicClient = createMockPublicClient();
53
- const mockWalletClient = createMockWalletClient();
54
-
55
- const config = {
56
- params: { chainId: 11155111, rpcUrl: 'https://rpc.sepolia.org' },
57
- services: {},
58
- client: mockWalletClient as any,
59
- superPaymasterAddress: '0x1111111111111111111111111111111111111111' as `0x${string}`,
60
- tokenAddress: '0x2222222222222222222222222222222222222222' as `0x${string}`,
61
- registryAddress: '0x3333333333333333333333333333333333333333' as `0x${string}`,
62
- gTokenAddress: '0x4444444444444444444444444444444444444444' as `0x${string}`,
63
- gTokenStakingAddress: '0x5555555555555555555555555555555555555555' as `0x${string}`,
64
- paymasterFactoryAddress: '0x6666666666666666666666666666666666666666' as `0x${string}`,
65
- entryPointAddress: '0x0000000071727De22E5E9d8BAf0edAc6f37da032' as `0x${string}`,
66
- };
67
-
68
- beforeEach(() => {
69
- resetMocks();
70
- vi.clearAllMocks();
71
-
72
- // Default Mocks
73
- mocks.mockRegistry.ROLE_COMMUNITY.mockResolvedValue('0x1111111111111111111111111111111111111111111111111111111111111111');
74
- mocks.mockRegistry.ROLE_PAYMASTER_SUPER.mockResolvedValue('0x2222222222222222222222222222222222222222222222222222222222222222');
75
- mocks.mockRegistry.ROLE_PAYMASTER_AOA.mockResolvedValue('0x3333333333333333333333333333333333333333333333333333333333333333');
76
- mocks.mockRegistry.hasRole.mockResolvedValue(true); // Default has prerequisite
77
- mocks.mockRegistry.registerRoleSelf.mockResolvedValue('0xTxHash');
78
- mocks.mockToken.allowance.mockResolvedValue(parseEther('1000'));
79
- mocks.mockToken.approve.mockResolvedValue('0xTxHash');
80
- mocks.mockSuperPaymaster.APNTS_TOKEN.mockResolvedValue('0xAPNTS');
81
- mocks.mockSuperPaymaster.deposit.mockResolvedValue('0xTxHash');
82
- mocks.mockSuperPaymaster.configureOperator.mockResolvedValue('0xTxHash');
83
- mocks.mockSuperPaymaster.operators.mockResolvedValue({
84
- aPNTsBalance: 0n,
85
- xPNTsToken: '0x2222222222222222222222222222222222222222',
86
- treasury: '0x3333333333333333333333333333333333333333',
87
- exchangeRate: 150n,
88
- isConfigured: true,
89
- isPaused: false,
90
- reputation: 100,
91
- minTxInterval: 60,
92
- totalSpent: 0n,
93
- totalTxSponsored: 0n
94
- });
95
- mocks.mockPaymasterFactory.deployPaymaster.mockResolvedValue('0xTxHash');
96
- mocks.mockPaymasterFactory.getPaymaster.mockResolvedValue('0x7777777777777777777777777777777777777777');
97
- mocks.mockPaymaster.setTokenPrice.mockResolvedValue('0xTxHash');
98
- mocks.mockPaymaster.tokenPrices.mockResolvedValue(100n);
99
- mocks.mockPaymaster.depositFor.mockResolvedValue('0xTxHash');
100
-
101
- client = new PaymasterOperatorClient(config);
102
- (client as any).client = mockWalletClient;
103
- (client as any).publicClient = mockPublicClient;
104
- });
105
-
106
- describe('Registration', () => {
107
- it('registerAsSuperPaymasterOperator should verify roles and register', async () => {
108
- mocks.mockRegistry.hasRole
109
- .mockResolvedValueOnce(true) // Has Community
110
- .mockResolvedValueOnce(false); // Does not have SuperRole yet
111
-
112
- await client.registerAsSuperPaymasterOperator({ stakeAmount: 100n });
113
-
114
- expect(mocks.mockRegistry.registerRoleSelf).toHaveBeenCalledWith(expect.objectContaining({
115
- roleId: '0x2222222222222222222222222222222222222222222222222222222222222222'
116
- }));
117
- });
118
-
119
- it('registerAsSuperPaymasterOperator should throw if no community role', async () => {
120
- mocks.mockRegistry.hasRole.mockResolvedValue(false);
121
- await expect(client.registerAsSuperPaymasterOperator()).rejects.toThrow('Must have ROLE_COMMUNITY');
122
- });
123
-
124
- it('registerAsSuperPaymasterOperator should approve if needed', async () => {
125
- mocks.mockRegistry.hasRole
126
- .mockResolvedValueOnce(true)
127
- .mockResolvedValueOnce(false);
128
- mocks.mockToken.allowance.mockResolvedValue(0n);
129
-
130
- await client.registerAsSuperPaymasterOperator({ stakeAmount: 100n });
131
-
132
- expect(mocks.mockToken.approve).toHaveBeenCalled();
133
- });
134
-
135
- it('registerAsSuperPaymasterOperator should handle optional deposit', async () => {
136
- mocks.mockRegistry.hasRole
137
- .mockResolvedValueOnce(true)
138
- .mockResolvedValueOnce(false);
139
-
140
- await client.registerAsSuperPaymasterOperator({ depositAmount: 50n });
141
-
142
- expect(mocks.mockSuperPaymaster.deposit).toHaveBeenCalled();
143
- });
144
- });
145
-
146
- describe('Deployment', () => {
147
- it('deployAndRegisterPaymasterV4 should deploy and register', async () => {
148
- // Mock role checks: Community exists, AOA doesn't
149
- mocks.mockRegistry.hasRole.mockImplementation(async ({ roleId }: any) => {
150
- if (roleId === '0x1111111111111111111111111111111111111111111111111111111111111111') return true;
151
- if (roleId === '0x3333333333333333333333333333333333333333333333333333333333333333') return false;
152
- return false;
153
- });
154
-
155
- mocks.mockPaymasterFactory.getPaymaster.mockResolvedValueOnce('0x0000000000000000000000000000000000000000'); // Clean deploy
156
- mocks.mockPaymasterFactory.getPaymaster.mockResolvedValueOnce('0x7777777777777777777777777777777777777777'); // After deploy
157
-
158
- const result = await client.deployAndRegisterPaymasterV4();
159
-
160
- expect(mocks.mockPaymasterFactory.deployPaymaster).toHaveBeenCalled();
161
- expect(mocks.mockPaymasterFactory.getPaymaster).toHaveBeenCalled();
162
- expect(mocks.mockRegistry.registerRoleSelf).toHaveBeenCalledWith(expect.objectContaining({
163
- roleId: '0x3333333333333333333333333333333333333333333333333333333333333333'
164
- }));
165
- expect(result.paymasterAddress).toBe('0x7777777777777777777777777777777777777777');
166
- });
167
-
168
- it('deployAndRegisterPaymasterV4 should skip register if already has role', async () => {
169
- // Mock getPaymaster to return existing address to exercise idempotency
170
- mocks.mockPaymasterFactory.getPaymaster.mockResolvedValue('0x7777777777777777777777777777777777777777');
171
-
172
- mocks.mockRegistry.hasRole.mockImplementation(async ({ roleId }: any) => {
173
- if (roleId === '0x1111111111111111111111111111111111111111111111111111111111111111') return true;
174
- if (roleId === '0x3333333333333333333333333333333333333333333333333333333333333333') return true; // Already registered
175
- return false;
176
- });
177
-
178
- const result = await client.deployAndRegisterPaymasterV4();
179
-
180
- expect(mocks.mockPaymasterFactory.deployPaymaster).not.toHaveBeenCalled();
181
- expect(mocks.mockRegistry.registerRoleSelf).not.toHaveBeenCalled();
182
- expect(result.registerHash).toBe('0x0000000000000000000000000000000000000000000000000000000000000000');
183
- });
184
- });
185
-
186
- describe('Management', () => {
187
- it('depositCollateral should approve and deposit', async () => {
188
- mocks.mockToken.allowance.mockResolvedValue(0n);
189
- await client.depositCollateral(100n);
190
-
191
- expect(mocks.mockSuperPaymaster.APNTS_TOKEN).toHaveBeenCalled();
192
- expect(mocks.mockToken.approve).toHaveBeenCalled();
193
- expect(mocks.mockSuperPaymaster.deposit).toHaveBeenCalledWith(expect.objectContaining({
194
- amount: 100n
195
- }));
196
- });
197
-
198
- it('updateExchangeRate should fetch config and call configureOperator', async () => {
199
- // Mock operators return [balance, token, treasury, rate]
200
- mocks.mockSuperPaymaster.operators.mockResolvedValue({
201
- aPNTsBalance: 1000n,
202
- xPNTsToken: '0x2222222222222222222222222222222222222222',
203
- treasury: '0x3333333333333333333333333333333333333333',
204
- exchangeRate: 150n,
205
- isConfigured: true,
206
- isPaused: false,
207
- reputation: 100,
208
- minTxInterval: 60,
209
- totalSpent: 0n,
210
- totalTxSponsored: 0n
211
- });
212
- mocks.mockSuperPaymaster.configureOperator.mockResolvedValue('0xTxHash');
213
-
214
- await client.updateExchangeRate(200n);
215
-
216
- expect(mocks.mockSuperPaymaster.operators).toHaveBeenCalledWith(expect.objectContaining({
217
- operator: '0x1234567890123456789012345678901234567890' // Default mock address from client.ts
218
- }));
219
-
220
- expect(mocks.mockSuperPaymaster.configureOperator).toHaveBeenCalledWith(expect.objectContaining({
221
- xPNTsToken: '0x2222222222222222222222222222222222222222',
222
- opTreasury: '0x3333333333333333333333333333333333333333',
223
- exchangeRate: 200n
224
- }));
225
- });
226
- });
227
-
228
- describe('Token Config', () => {
229
- it('addGasToken should set token price', async () => {
230
- await client.addGasToken('0x4444444444444444444444444444444444444444', 100n);
231
- expect(mocks.mockPaymasterActions).toHaveBeenCalled(); // Should be called with SP address? No, superPaymasterAddress IS the paymaster for addGasToken (based on code reading)
232
- // Wait, addGasToken uses paymasterActions(this.superPaymasterAddress)
233
- expect(mocks.mockPaymasterActions).toHaveBeenCalledWith(config.superPaymasterAddress);
234
- expect(mocks.mockPaymaster.setTokenPrice).toHaveBeenCalled();
235
- });
236
-
237
- it('getTokenPrice should read price', async () => {
238
- const price = await client.getTokenPrice('0x4444444444444444444444444444444444444444');
239
- expect(price).toBe(100n);
240
- });
241
- });
242
-
243
- describe('Deposit', () => {
244
- it('setupPaymasterDeposit should deposit for user', async () => {
245
- await client.setupPaymasterDeposit({
246
- paymaster: '0x5555555555555555555555555555555555555555',
247
- user: '0x6666666666666666666666666666666666666666',
248
- token: '0x4444444444444444444444444444444444444444',
249
- amount: 100n
250
- });
251
- expect(mocks.mockPaymasterActions).toHaveBeenCalledWith('0x5555555555555555555555555555555555555555');
252
- expect(mocks.mockPaymaster.depositFor).toHaveBeenCalledWith(expect.objectContaining({
253
- user: '0x6666666666666666666666666666666666666666',
254
- amount: 100n
255
- }));
256
- });
257
- });
258
- });
@@ -1,135 +0,0 @@
1
- import { describe, it, expect, vi, beforeEach } from 'vitest';
2
- import { ProtocolClient } from '../src/ProtocolClient';
3
- import { createMockPublicClient, createMockWalletClient, resetMocks } from './mocks/client';
4
-
5
- // Define mocks using vi.hoisted
6
- const mocks = vi.hoisted(() => {
7
- const mockDVT = { createSlashProposal: vi.fn(), signSlashProposal: vi.fn(), executeSlashWithProof: vi.fn() };
8
- const mockAggregator = { registerBLSPublicKey: vi.fn() };
9
- const mockSuperPaymaster = { setProtocolFee: vi.fn(), setTreasury: vi.fn() };
10
-
11
- return {
12
- mockDVT,
13
- mockAggregator,
14
- mockSuperPaymaster,
15
- // Factory functions
16
- mockDVTActions: vi.fn(() => () => mockDVT),
17
- mockAggregatorActions: vi.fn(() => () => mockAggregator),
18
- mockSuperPaymasterActions: vi.fn(() => () => mockSuperPaymaster),
19
- };
20
- });
21
-
22
- vi.mock('@aastar/core', async () => {
23
- const actual = await vi.importActual('@aastar/core');
24
- return {
25
- ...actual,
26
- dvtActions: mocks.mockDVTActions,
27
- aggregatorActions: mocks.mockAggregatorActions,
28
- superPaymasterActions: mocks.mockSuperPaymasterActions,
29
- };
30
- });
31
-
32
- describe('ProtocolClient', () => {
33
- let client: ProtocolClient;
34
- const mockPublicClient = createMockPublicClient();
35
- const mockWalletClient = createMockWalletClient();
36
-
37
- const config = {
38
- params: {
39
- chainId: 11155111,
40
- rpcUrl: 'https://rpc.sepolia.org',
41
- },
42
- services: {
43
- paymasterUrl: 'https://paymaster.example.com',
44
- },
45
- client: mockWalletClient as any,
46
- dvtValidatorAddress: '0x1111111111111111111111111111111111111111' as `0x${string}`,
47
- blsAggregatorAddress: '0x2222222222222222222222222222222222222222' as `0x${string}`,
48
- superPaymasterAddress: '0x3333333333333333333333333333333333333333' as `0x${string}`
49
- };
50
-
51
- beforeEach(() => {
52
- resetMocks();
53
- vi.clearAllMocks();
54
-
55
- // Default Resolves
56
- mocks.mockDVT.createSlashProposal.mockResolvedValue('0xTxHash');
57
- mocks.mockDVT.signSlashProposal.mockResolvedValue('0xTxHash');
58
- mocks.mockDVT.executeSlashWithProof.mockResolvedValue('0xTxHash');
59
- mocks.mockAggregator.registerBLSPublicKey.mockResolvedValue('0xTxHash');
60
- mocks.mockSuperPaymaster.setProtocolFee.mockResolvedValue('0xTxHash');
61
- mocks.mockSuperPaymaster.setTreasury.mockResolvedValue('0xTxHash');
62
-
63
- client = new ProtocolClient(config);
64
- (client as any).client = mockWalletClient;
65
- (client as any).publicClient = mockPublicClient;
66
- });
67
-
68
- describe('DVT Proposals', () => {
69
- it('createProposal should call createSlashProposal', async () => {
70
- const result = await client.createProposal('0x4444444444444444444444444444444444444444', '0x1234', 'Description');
71
-
72
- expect(mocks.mockDVTActions).toHaveBeenCalledWith(config.dvtValidatorAddress);
73
- expect(mocks.mockDVT.createSlashProposal).toHaveBeenCalledWith(expect.objectContaining({
74
- operator: '0x4444444444444444444444444444444444444444',
75
- level: 1,
76
- reason: 'Description'
77
- }));
78
- expect(result).toBe('0xTxHash');
79
- });
80
-
81
- it('signProposal should call signSlashProposal', async () => {
82
- await client.signProposal(1n, '0x5555');
83
- expect(mocks.mockDVT.signSlashProposal).toHaveBeenCalledWith(expect.objectContaining({
84
- proposalId: 1n,
85
- signature: '0x5555'
86
- }));
87
- });
88
-
89
- it('executeWithProof should call executeSlashWithProof', async () => {
90
- await client.executeWithProof(1n, ['0x5555']);
91
- expect(mocks.mockDVT.executeSlashWithProof).toHaveBeenCalledWith(expect.objectContaining({
92
- proposalId: 1n,
93
- // proof is mocked inside implementation
94
- }));
95
- });
96
- });
97
-
98
- describe('BLS Management', () => {
99
- it('registerBLSKey should call registerBLSPublicKey', async () => {
100
- await client.registerBLSKey('0x123456');
101
- expect(mocks.mockAggregatorActions).toHaveBeenCalledWith(config.blsAggregatorAddress);
102
- expect(mocks.mockAggregator.registerBLSPublicKey).toHaveBeenCalledWith(expect.objectContaining({
103
- publicKey: '0x123456'
104
- }));
105
- });
106
-
107
- it('should throw if blsAggregatorAddress missing', async () => {
108
- client.blsAggregatorAddress = undefined;
109
- await expect(client.registerBLSKey('0x12')).rejects.toThrow('BLS Aggregator address required');
110
- });
111
- });
112
-
113
- describe('Global Params', () => {
114
- it('setProtocolFee should call superPaymaster', async () => {
115
- await client.setProtocolFee(100n);
116
- expect(mocks.mockSuperPaymasterActions).toHaveBeenCalledWith(config.superPaymasterAddress);
117
- expect(mocks.mockSuperPaymaster.setProtocolFee).toHaveBeenCalledWith(expect.objectContaining({
118
- newFeeBPS: 100n
119
- }));
120
- });
121
-
122
- it('setTreasury should call superPaymaster', async () => {
123
- await client.setTreasury('0x7777777777777777777777777777777777777777');
124
- expect(mocks.mockSuperPaymaster.setTreasury).toHaveBeenCalledWith(expect.objectContaining({
125
- treasury: '0x7777777777777777777777777777777777777777'
126
- }));
127
- });
128
-
129
- it('should throw if superPaymasterAddress missing', async () => {
130
- client.superPaymasterAddress = undefined;
131
- await expect(client.setTreasury('0x1111111111111111111111111111111111111111')).rejects.toThrow('SuperPaymaster address required');
132
- });
133
- });
134
-
135
- });
@@ -1,16 +0,0 @@
1
-
2
- import { PaymasterOperatorClient } from '../src/PaymasterOperatorClient';
3
- import { ProtocolClient } from '../src/ProtocolClient';
4
- import * as Exports from '../src/index';
5
-
6
- describe('Operator Package Exports', () => {
7
- it('should export PaymasterOperatorClient', () => {
8
- expect(Exports.PaymasterOperatorClient).toBeDefined();
9
- expect(Exports.PaymasterOperatorClient).toBe(PaymasterOperatorClient);
10
- });
11
-
12
- it('should export ProtocolClient', () => {
13
- expect(Exports.ProtocolClient).toBeDefined();
14
- expect(Exports.ProtocolClient).toBe(ProtocolClient);
15
- });
16
- });
@@ -1,22 +0,0 @@
1
- import { vi } from 'vitest';
2
- import type { PublicClient, WalletClient } from 'viem';
3
-
4
- export const createMockPublicClient = (): PublicClient => {
5
- return {
6
- readContract: vi.fn(),
7
- chain: { id: 11155111 },
8
- waitForTransactionReceipt: vi.fn().mockResolvedValue({ status: 'success' }),
9
- } as any;
10
- };
11
-
12
- export const createMockWalletClient = (): WalletClient => {
13
- return {
14
- writeContract: vi.fn(),
15
- account: { address: '0x1234567890123456789012345678901234567890' },
16
- chain: { id: 11155111 },
17
- } as any;
18
- };
19
-
20
- export const resetMocks = () => {
21
- vi.clearAllMocks();
22
- };
@@ -1,454 +0,0 @@
1
- import { type Address, type Hash, parseEther } from 'viem';
2
- import { BaseClient, type ClientConfig, type TransactionOptions, PaymasterABI } from '@aastar/core';
3
- import { superPaymasterActions, tokenActions, paymasterActions, registryActions, paymasterFactoryActions } from '@aastar/core';
4
-
5
- export interface OperatorClientConfig extends ClientConfig {
6
- superPaymasterAddress: Address;
7
- tokenAddress?: Address;
8
- }
9
-
10
- export interface SponsorshipPolicy {
11
- globalLimit: bigint;
12
- userLimit: bigint;
13
- itemPrice: bigint;
14
- // ... logic for encoding this into bytes/storage
15
- }
16
-
17
- /**
18
- * Client for Paymaster Operators (ROLE_PAYMASTER_SUPER)
19
- */
20
- export class PaymasterOperatorClient extends BaseClient {
21
- public superPaymasterAddress: Address;
22
- public tokenAddress?: Address;
23
- public ethUsdPriceFeed: Address;
24
- public xpntsFactory: Address;
25
-
26
- constructor(config: OperatorClientConfig) {
27
- super(config);
28
- this.superPaymasterAddress = config.superPaymasterAddress;
29
- this.tokenAddress = config.tokenAddress;
30
- this.ethUsdPriceFeed = config.ethUsdPriceFeedAddress || '0x694AA1769357215DE4FAC081bf1f309aDC325306'; // Default Sepolia
31
- this.xpntsFactory = config.xpntsFactoryAddress || '0x0000000000000000000000000000000000000000'; // Should be provided
32
- }
33
-
34
- // ========================================
35
- // 0. 注册与入驻 (One-Stop Registration)
36
- // ========================================
37
-
38
- /**
39
- * Register as SuperPaymaster Operator (one-stop API).
40
- * This method handles all necessary steps:
41
- * 1. Checks prerequisites (must have ROLE_COMMUNITY)
42
- * 2. Checks and approves GToken to GTokenStaking
43
- * 3. Registers ROLE_PAYMASTER_SUPER
44
- * 4. Optionally deposits collateral to SuperPaymaster
45
- *
46
- * @param params Registration parameters
47
- * @param options Transaction options
48
- * @returns Transaction hash of role registration
49
- */
50
- async registerAsSuperPaymasterOperator(params?: {
51
- stakeAmount?: bigint; // Optional, defaults to 50 GToken (Registry requirement)
52
- depositAmount?: bigint; // Optional initial deposit to SuperPaymaster
53
- }, options?: TransactionOptions): Promise<Hash> {
54
- try {
55
- const registryAddr = this.requireRegistry();
56
- const gTokenAddr = this.requireGToken();
57
- const gTokenStakingAddr = this.requireGTokenStaking();
58
-
59
- const registry = registryActions(registryAddr);
60
- const gToken = tokenActions();
61
- const publicClient = this.getStartPublicClient();
62
-
63
- // 1. Check prerequisites
64
- const ROLE_COMMUNITY = await registry(publicClient).ROLE_COMMUNITY();
65
- const hasCommunity = await registry(publicClient).hasRole({
66
- user: this.getAddress(),
67
- roleId: ROLE_COMMUNITY
68
- });
69
-
70
- if (!hasCommunity) {
71
- throw new Error('Must have ROLE_COMMUNITY before registering as SuperPaymaster operator');
72
- }
73
-
74
- // 2. Check if already has role
75
- const ROLE_PAYMASTER_SUPER = await registry(publicClient).ROLE_PAYMASTER_SUPER();
76
- const hasSuper = await registry(publicClient).hasRole({
77
- user: this.getAddress(),
78
- roleId: ROLE_PAYMASTER_SUPER
79
- });
80
-
81
- if (hasSuper) {
82
- // Still handle deposit if requested
83
- if (params?.depositAmount) {
84
- return this.depositCollateral(params.depositAmount, options);
85
- }
86
- throw new Error('Already registered as SuperPaymaster operator');
87
- }
88
-
89
- // 3. Prepare stake amount (default 50 GToken as per Registry config)
90
- const stakeAmount = params?.stakeAmount || parseEther('50');
91
-
92
- // 4. Check and approve GToken to GTokenStaking
93
- const allowance = await gToken(publicClient).allowance({
94
- token: gTokenAddr,
95
- owner: this.getAddress(),
96
- spender: gTokenStakingAddr
97
- });
98
-
99
- if (allowance < stakeAmount) {
100
- const approveHash = await gToken(this.client).approve({
101
- token: gTokenAddr,
102
- spender: gTokenStakingAddr,
103
- amount: stakeAmount * 2n, // Approve 2x for future use
104
- account: options?.account
105
- });
106
- await (publicClient as any).waitForTransactionReceipt({ hash: approveHash });
107
- }
108
-
109
- // 5. Register ROLE_PAYMASTER_SUPER
110
- const registerHash = await registry(this.client).registerRoleSelf({
111
- roleId: ROLE_PAYMASTER_SUPER,
112
- data: '0x', // SuperPaymaster role doesn't need special data
113
- account: options?.account
114
- });
115
-
116
- // Wait for registration to complete
117
- await (publicClient as any).waitForTransactionReceipt({ hash: registerHash });
118
-
119
- // 6. Optional: Deposit collateral to SuperPaymaster
120
- if (params?.depositAmount) {
121
- await this.depositCollateral(params.depositAmount, options);
122
- }
123
-
124
- return registerHash;
125
- } catch (error) {
126
- throw error;
127
- }
128
- }
129
-
130
- /**
131
- * Deploy a new Paymaster V4 and Register as AOA Operator (one-stop API).
132
- * This method handles:
133
- * 1. Checks prerequisites (ROLE_COMMUNITY)
134
- * 2. Predicts new Paymaster address
135
- * 3. Deploys Paymaster V4 via Factory
136
- * 4. Registers ROLE_PAYMASTER_AOA with staking
137
- *
138
- * @param params Deployment parameters
139
- * @param options Transaction options
140
- * @returns Object containing new paymaster address and transaction hashes
141
- */
142
- async deployAndRegisterPaymasterV4(params?: {
143
- stakeAmount?: bigint; // Optional, defaults to 30 GToken (Registry requirement for AOA)
144
- version?: string; // Optional, defaults to Factory default or V4.0.0
145
- salt?: bigint; // Optional, for deterministic deployment
146
- }, options?: TransactionOptions): Promise<{
147
- paymasterAddress: Address;
148
- deployHash: Hash;
149
- registerHash: Hash;
150
- }> {
151
- try {
152
- const registryAddr = this.requireRegistry();
153
- const gTokenAddr = this.requireGToken();
154
- const gTokenStakingAddr = this.requireGTokenStaking();
155
- const factoryAddr = this.requirePaymasterFactory();
156
-
157
- const registry = registryActions(registryAddr);
158
- const gToken = tokenActions();
159
- const factory = paymasterFactoryActions(factoryAddr);
160
- const publicClient = this.getStartPublicClient();
161
-
162
- const account = options?.account || this.client.account || this.getAddress();
163
- const accountAddr = typeof account === 'string' ? account : account.address;
164
-
165
- // 1. Check prerequisites (ROLE_COMMUNITY)
166
- const ROLE_COMMUNITY = await registry(publicClient).ROLE_COMMUNITY();
167
- const hasCommunity = await registry(publicClient).hasRole({
168
- user: accountAddr,
169
- roleId: ROLE_COMMUNITY
170
- });
171
-
172
- if (!hasCommunity) {
173
- throw new Error('Must have ROLE_COMMUNITY before deploying Paymaster V4');
174
- }
175
-
176
- // 2. Deployment (Idempotent Check)
177
- const existingPaymaster = await factory(publicClient).getPaymaster({ owner: accountAddr });
178
- let deployHash: Hash = '0x0000000000000000000000000000000000000000000000000000000000000000';
179
- let paymasterAddress: Address;
180
-
181
- if (existingPaymaster && existingPaymaster !== '0x0000000000000000000000000000000000000000') {
182
- console.log(` ℹ️ Paymaster already deployed at: ${existingPaymaster}`);
183
- paymasterAddress = existingPaymaster;
184
- } else {
185
- console.log(' 🛠️ Deploying Paymaster V4 with args:', {
186
- entryPoint: this.requireEntryPoint(),
187
- owner: accountAddr,
188
- priceFeed: this.ethUsdPriceFeed,
189
- factory: factoryAddr
190
- });
191
-
192
- const { encodeFunctionData } = await import('viem');
193
- const initData = encodeFunctionData({
194
- abi: PaymasterABI,
195
- functionName: 'initialize',
196
- args: [
197
- this.requireEntryPoint(), // EntryPoint v0.7
198
- accountAddr,
199
- accountAddr, // Treasury defaults to owner
200
- this.ethUsdPriceFeed,
201
- 200n, // serviceFeeRate (2%)
202
- parseEther('0.1'), // maxGasCostCap
203
- 3600n // priceStalenessThreshold
204
- ]
205
- });
206
-
207
- deployHash = await factory(this.client).deployPaymaster({
208
- version: params?.version,
209
- initData,
210
- account
211
- });
212
-
213
- await (publicClient as any).waitForTransactionReceipt({ hash: deployHash });
214
-
215
- paymasterAddress = await factory(publicClient).getPaymaster({ owner: accountAddr });
216
- }
217
-
218
- if (!paymasterAddress || paymasterAddress === '0x0000000000000000000000000000000000000000') {
219
- throw new Error('Failed to retrieve Paymaster address from Factory');
220
- }
221
-
222
- // 3. Register ROLE_PAYMASTER_AOA
223
- const ROLE_PAYMASTER_AOA = await registry(publicClient).ROLE_PAYMASTER_AOA();
224
- const hasAOA = await registry(publicClient).hasRole({
225
- user: accountAddr,
226
- roleId: ROLE_PAYMASTER_AOA
227
- });
228
-
229
- if (hasAOA) {
230
- return { paymasterAddress, deployHash, registerHash: '0x0000000000000000000000000000000000000000000000000000000000000000' };
231
- }
232
-
233
- const stakeAmount = params?.stakeAmount || parseEther('30');
234
-
235
- const allowance = await gToken(publicClient).allowance({
236
- token: gTokenAddr,
237
- owner: accountAddr,
238
- spender: gTokenStakingAddr
239
- });
240
-
241
- if (allowance < stakeAmount) {
242
- const approveHash = await gToken(this.client).approve({
243
- token: gTokenAddr,
244
- spender: gTokenStakingAddr,
245
- amount: stakeAmount * 2n,
246
- account: account
247
- });
248
- await (publicClient as any).waitForTransactionReceipt({ hash: approveHash });
249
- }
250
-
251
- const { encodeAbiParameters, parseAbiParameters } = await import('viem');
252
- let roleData: Hash = '0x';
253
- if (stakeAmount > 0) {
254
- roleData = encodeAbiParameters(
255
- parseAbiParameters('uint256'),
256
- [stakeAmount]
257
- ) as Hash;
258
- }
259
-
260
- const registerHash = await registry(this.client).registerRoleSelf({
261
- roleId: ROLE_PAYMASTER_AOA,
262
- data: roleData,
263
- account: account
264
- });
265
-
266
- await (publicClient as any).waitForTransactionReceipt({ hash: registerHash });
267
-
268
- return {
269
- paymasterAddress,
270
- deployHash,
271
- registerHash
272
- };
273
- } catch (error) {
274
- throw error;
275
- }
276
- }
277
-
278
- /**
279
- * Deposit collateral (aPNTs/GToken) to SuperPaymaster.
280
- * This is a helper method used by registerAsSuperPaymasterOperator.
281
- */
282
- async depositCollateral(amount: bigint, options?: TransactionOptions): Promise<Hash> {
283
- try {
284
- const pm = superPaymasterActions(this.superPaymasterAddress);
285
- const publicClient = this.getStartPublicClient();
286
-
287
- // V3.7: Dynamically fetch the token expected by SuperPaymaster
288
- const depositToken = await pm(publicClient).APNTS_TOKEN();
289
- const token = tokenActions();
290
-
291
- // Approve SuperPaymaster to spend the token (usually aPNTs on Sepolia)
292
- const allowance = await token(publicClient).allowance({
293
- token: depositToken,
294
- owner: this.getAddress(),
295
- spender: this.superPaymasterAddress
296
- });
297
-
298
- if (allowance < amount) {
299
- const approveHash = await token(this.client).approve({
300
- token: depositToken,
301
- spender: this.superPaymasterAddress,
302
- amount,
303
- account: options?.account
304
- });
305
- await (publicClient as any).waitForTransactionReceipt({ hash: approveHash });
306
- }
307
-
308
- // Deposit to SuperPaymaster
309
- return pm(this.client).deposit({
310
- amount,
311
- account: options?.account
312
- });
313
- } catch (error) {
314
- throw error;
315
- }
316
- }
317
-
318
- async updateExchangeRate(exchangeRate: bigint, options?: TransactionOptions): Promise<Hash> {
319
- return this.configureOperator(undefined, undefined, exchangeRate, options);
320
- }
321
-
322
- /**
323
- * Configure operator parameters (Token, Treasury, Exchange Rate).
324
- * If parameters are undefined, existing values are preserved.
325
- */
326
- async configureOperator(
327
- xPNTsToken?: Address,
328
- treasury?: Address,
329
- exchangeRate?: bigint,
330
- options?: TransactionOptions
331
- ): Promise<Hash> {
332
- try {
333
- const sp = superPaymasterActions(this.superPaymasterAddress);
334
- const publicClient = this.getStartPublicClient();
335
-
336
- // Fetch current config to preserve missing values
337
- const currentConfig = await sp(publicClient).operators({ operator: this.getAddress() });
338
-
339
- const currentToken = currentConfig.xPNTsToken;
340
- const currentTreasury = currentConfig.treasury;
341
- const currentRate = currentConfig.exchangeRate;
342
-
343
- return await sp(this.client).configureOperator({
344
- xPNTsToken: xPNTsToken || currentToken,
345
- opTreasury: treasury || currentTreasury,
346
- exchangeRate: exchangeRate ?? currentRate,
347
- account: options?.account
348
- });
349
- } catch (error) {
350
- throw error;
351
- }
352
- }
353
-
354
- async withdrawCollateral(to: Address, amount: bigint, options?: TransactionOptions): Promise<Hash> {
355
- try {
356
- const sp = superPaymasterActions(this.superPaymasterAddress);
357
- return await sp(this.client).withdrawTo({
358
- to,
359
- amount,
360
- account: options?.account
361
- });
362
- } catch (error) {
363
- throw error;
364
- }
365
- }
366
-
367
- async isOperator(operator: Address): Promise<boolean> {
368
- try {
369
- const sp = superPaymasterActions(this.superPaymasterAddress);
370
- const config = await sp(this.getStartPublicClient()).operators({ operator });
371
- return config.isConfigured;
372
- } catch (error) {
373
- return false;
374
- }
375
- }
376
-
377
- async getOperatorDetails(operator?: Address): Promise<any> {
378
- try {
379
- const target = operator || this.getAddress();
380
- const sp = superPaymasterActions(this.superPaymasterAddress);
381
- return await sp(this.getStartPublicClient()).operators({ operator: target });
382
- } catch (error) {
383
- throw error;
384
- }
385
- }
386
-
387
- async initiateExit(options?: TransactionOptions): Promise<Hash> {
388
- try {
389
- const sp = superPaymasterActions(this.superPaymasterAddress);
390
- return await sp(this.client).unlockStake({
391
- account: options?.account
392
- });
393
- } catch (error) {
394
- throw error;
395
- }
396
- }
397
-
398
- async withdrawStake(to: Address, options?: TransactionOptions): Promise<Hash> {
399
- try {
400
- const sp = superPaymasterActions(this.superPaymasterAddress);
401
- return await sp(this.client).withdrawStake({
402
- to,
403
- account: options?.account
404
- });
405
- } catch (error) {
406
- throw error;
407
- }
408
- }
409
-
410
- // ========================================
411
- // 3. 支付代币管理 (基于 PaymasterActions)
412
- // ========================================
413
-
414
- async addGasToken(token: Address, price: bigint, options?: TransactionOptions): Promise<Hash> {
415
- try {
416
- const pm = paymasterActions(this.superPaymasterAddress);
417
- return await pm(this.client).setTokenPrice({
418
- token,
419
- price,
420
- account: options?.account
421
- });
422
- } catch (error) {
423
- throw error;
424
- }
425
- }
426
-
427
- async getTokenPrice(token: Address): Promise<bigint> {
428
- try {
429
- const pm = paymasterActions(this.superPaymasterAddress);
430
- return await pm(this.getStartPublicClient()).tokenPrices({ token });
431
- } catch (error) {
432
- throw error;
433
- }
434
- }
435
-
436
- async setupPaymasterDeposit(params: {
437
- paymaster: Address;
438
- user: Address;
439
- token: Address;
440
- amount: bigint;
441
- }, options?: TransactionOptions): Promise<Hash> {
442
- try {
443
- const pm = paymasterActions(params.paymaster);
444
- return await pm(this.client).depositFor({
445
- user: params.user,
446
- token: params.token,
447
- amount: params.amount,
448
- account: options?.account
449
- });
450
- } catch (error) {
451
- throw error;
452
- }
453
- }
454
- }
@@ -1,154 +0,0 @@
1
- import { type Address, type Hash, type Hex } from 'viem';
2
- import { BaseClient, type ClientConfig, type TransactionOptions } from '@aastar/core';
3
- import { dvtActions, aggregatorActions, superPaymasterActions } from '@aastar/core';
4
-
5
- export interface ProtocolClientConfig extends ClientConfig {
6
- dvtValidatorAddress: Address; // The DVT Validator contract (Governance)
7
- blsAggregatorAddress?: Address; // Optional BLS Aggregator
8
- superPaymasterAddress?: Address; // For Global Params
9
- }
10
-
11
- export enum ProposalState {
12
- Pending = 0,
13
- Active = 1,
14
- Canceled = 2,
15
- Defeated = 3,
16
- Succeeded = 4,
17
- Queued = 5,
18
- Expired = 6,
19
- Executed = 7
20
- }
21
-
22
- /**
23
- * Client for Protocol Governors and Validators (Infrastructure)
24
- */
25
- export class ProtocolClient extends BaseClient {
26
- public dvtValidatorAddress: Address;
27
- public blsAggregatorAddress?: Address;
28
- public superPaymasterAddress?: Address;
29
-
30
- constructor(config: ProtocolClientConfig) {
31
- super(config);
32
- this.dvtValidatorAddress = config.dvtValidatorAddress;
33
- this.blsAggregatorAddress = config.blsAggregatorAddress;
34
- this.superPaymasterAddress = config.superPaymasterAddress;
35
- }
36
-
37
- // ========================================
38
- // 1. 提案管理 (DVT)
39
- // ========================================
40
-
41
- /**
42
- * Create a new proposal
43
- */
44
- async createProposal(target: Address, calldata: Hex, description: string, options?: TransactionOptions): Promise<Hash> {
45
- try {
46
- const dvt = dvtActions(this.dvtValidatorAddress)(this.client);
47
-
48
- // Mapping general "createProposal" to "createSlashProposal" for now
49
- // Assuming Governance uses Validator logic or this Client is for Slash.
50
- // Using createSlashProposal as the available action.
51
- return await dvt.createSlashProposal({
52
- operator: target,
53
- level: 1, // Default level
54
- reason: description,
55
- account: options?.account
56
- });
57
- } catch (error) {
58
- throw error;
59
- }
60
- }
61
-
62
- async signProposal(proposalId: bigint, signature: Hex = '0x', options?: TransactionOptions): Promise<Hash> {
63
- try {
64
- const dvt = dvtActions(this.dvtValidatorAddress)(this.client);
65
- return await dvt.signSlashProposal({
66
- proposalId,
67
- signature,
68
- account: options?.account
69
- });
70
- } catch (error) {
71
- throw error;
72
- }
73
- }
74
-
75
- /**
76
- * Execute a proposal with collected signatures
77
- */
78
- async executeWithProof(proposalId: bigint, signatures: Hex[], options?: TransactionOptions): Promise<Hash> {
79
- try {
80
- // Mock proof generation logic or placeholder
81
- const proof = '0x' as Hex;
82
- const dvt = dvtActions(this.dvtValidatorAddress)(this.client);
83
-
84
- return await dvt.executeSlashWithProof({
85
- proposalId,
86
- repUsers: [], // Needs real data in production
87
- newScores: [],
88
- epoch: 0n,
89
- proof,
90
- account: options?.account
91
- });
92
- } catch (error) {
93
- throw error;
94
- }
95
- }
96
-
97
- // ========================================
98
- // 2. 验证器管理 / BLS
99
- // ========================================
100
-
101
- async registerBLSKey(publicKey: Hex, options?: TransactionOptions): Promise<Hash> {
102
- try {
103
- if (!this.blsAggregatorAddress) {
104
- throw new Error('BLS Aggregator address required for this client');
105
- }
106
- // Aggregator actions now handle the type internally or via mapping
107
- const agg = aggregatorActions(this.blsAggregatorAddress)(this.client);
108
-
109
- return await agg.registerBLSPublicKey({
110
- validator: this.getAddress(),
111
- publicKey,
112
- account: options?.account
113
- });
114
- } catch (error) {
115
- throw error;
116
- }
117
- }
118
-
119
- // ========================================
120
- // 3. 全局参数管理 (Admin)
121
- // ========================================
122
-
123
- async setProtocolFee(bps: bigint, options?: TransactionOptions): Promise<Hash> {
124
- try {
125
- if (!this.superPaymasterAddress) {
126
- throw new Error('SuperPaymaster address required for this client');
127
- }
128
- const sp = superPaymasterActions(this.superPaymasterAddress);
129
-
130
- return await sp(this.client).setProtocolFee({
131
- newFeeBPS: bps,
132
- account: options?.account
133
- });
134
- } catch (error) {
135
- throw error;
136
- }
137
- }
138
-
139
- async setTreasury(treasury: Address, options?: TransactionOptions): Promise<Hash> {
140
- try {
141
- if (!this.superPaymasterAddress) {
142
- throw new Error('SuperPaymaster address required for this client');
143
- }
144
- const sp = superPaymasterActions(this.superPaymasterAddress);
145
-
146
- return await sp(this.client).setTreasury({
147
- treasury,
148
- account: options?.account
149
- });
150
- } catch (error) {
151
- throw error;
152
- }
153
- }
154
- }
package/src/index.ts DELETED
@@ -1,2 +0,0 @@
1
- export * from './PaymasterOperatorClient.js';
2
- export * from './ProtocolClient.js';
package/tsconfig.json DELETED
@@ -1,10 +0,0 @@
1
- {
2
- "extends": "../../tsconfig.build.json",
3
- "compilerOptions": {
4
- "outDir": "./dist",
5
- "rootDir": "./src",
6
- "noEmit": false,
7
- "declaration": true
8
- },
9
- "include": ["src/**/*"]
10
- }