@aastar/operator 0.16.11

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.
@@ -0,0 +1,375 @@
1
+ import { parseEther } from 'viem';
2
+ import { BaseClient, PaymasterABI } from '@aastar/core';
3
+ import { superPaymasterActions, tokenActions, paymasterActions, registryActions, paymasterFactoryActions } from '@aastar/core';
4
+ /**
5
+ * Client for Paymaster Operators (ROLE_PAYMASTER_SUPER)
6
+ */
7
+ export class PaymasterOperatorClient extends BaseClient {
8
+ superPaymasterAddress;
9
+ tokenAddress;
10
+ ethUsdPriceFeed;
11
+ xpntsFactory;
12
+ constructor(config) {
13
+ super(config);
14
+ this.superPaymasterAddress = config.superPaymasterAddress;
15
+ this.tokenAddress = config.tokenAddress;
16
+ this.ethUsdPriceFeed = config.ethUsdPriceFeedAddress || '0x694AA1769357215DE4FAC081bf1f309aDC325306'; // Default Sepolia
17
+ this.xpntsFactory = config.xpntsFactoryAddress || '0x0000000000000000000000000000000000000000'; // Should be provided
18
+ }
19
+ // ========================================
20
+ // 0. 注册与入驻 (One-Stop Registration)
21
+ // ========================================
22
+ /**
23
+ * Register as SuperPaymaster Operator (one-stop API).
24
+ * This method handles all necessary steps:
25
+ * 1. Checks prerequisites (must have ROLE_COMMUNITY)
26
+ * 2. Checks and approves GToken to GTokenStaking
27
+ * 3. Registers ROLE_PAYMASTER_SUPER
28
+ * 4. Optionally deposits collateral to SuperPaymaster
29
+ *
30
+ * @param params Registration parameters
31
+ * @param options Transaction options
32
+ * @returns Transaction hash of role registration
33
+ */
34
+ async registerAsSuperPaymasterOperator(params, options) {
35
+ try {
36
+ const registryAddr = this.requireRegistry();
37
+ const gTokenAddr = this.requireGToken();
38
+ const gTokenStakingAddr = this.requireGTokenStaking();
39
+ const registry = registryActions(registryAddr);
40
+ const gToken = tokenActions();
41
+ const publicClient = this.getStartPublicClient();
42
+ // 1. Check prerequisites
43
+ const ROLE_COMMUNITY = await registry(publicClient).ROLE_COMMUNITY();
44
+ const hasCommunity = await registry(publicClient).hasRole({
45
+ user: this.getAddress(),
46
+ roleId: ROLE_COMMUNITY
47
+ });
48
+ if (!hasCommunity) {
49
+ throw new Error('Must have ROLE_COMMUNITY before registering as SuperPaymaster operator');
50
+ }
51
+ // 2. Check if already has role
52
+ const ROLE_PAYMASTER_SUPER = await registry(publicClient).ROLE_PAYMASTER_SUPER();
53
+ const hasSuper = await registry(publicClient).hasRole({
54
+ user: this.getAddress(),
55
+ roleId: ROLE_PAYMASTER_SUPER
56
+ });
57
+ if (hasSuper) {
58
+ // Still handle deposit if requested
59
+ if (params?.depositAmount) {
60
+ return this.depositCollateral(params.depositAmount, options);
61
+ }
62
+ throw new Error('Already registered as SuperPaymaster operator');
63
+ }
64
+ // 3. Prepare stake amount (default 50 GToken as per Registry config)
65
+ const stakeAmount = params?.stakeAmount || parseEther('50');
66
+ // 4. Check and approve GToken to GTokenStaking
67
+ const allowance = await gToken(publicClient).allowance({
68
+ token: gTokenAddr,
69
+ owner: this.getAddress(),
70
+ spender: gTokenStakingAddr
71
+ });
72
+ if (allowance < stakeAmount) {
73
+ const approveHash = await gToken(this.client).approve({
74
+ token: gTokenAddr,
75
+ spender: gTokenStakingAddr,
76
+ amount: stakeAmount * 2n, // Approve 2x for future use
77
+ account: options?.account
78
+ });
79
+ await publicClient.waitForTransactionReceipt({ hash: approveHash });
80
+ }
81
+ // 5. Register ROLE_PAYMASTER_SUPER
82
+ const registerHash = await registry(this.client).registerRoleSelf({
83
+ roleId: ROLE_PAYMASTER_SUPER,
84
+ data: '0x', // SuperPaymaster role doesn't need special data
85
+ account: options?.account
86
+ });
87
+ // Wait for registration to complete
88
+ await publicClient.waitForTransactionReceipt({ hash: registerHash });
89
+ // 6. Optional: Deposit collateral to SuperPaymaster
90
+ if (params?.depositAmount) {
91
+ await this.depositCollateral(params.depositAmount, options);
92
+ }
93
+ return registerHash;
94
+ }
95
+ catch (error) {
96
+ throw error;
97
+ }
98
+ }
99
+ /**
100
+ * Deploy a new Paymaster V4 and Register as AOA Operator (one-stop API).
101
+ * This method handles:
102
+ * 1. Checks prerequisites (ROLE_COMMUNITY)
103
+ * 2. Predicts new Paymaster address
104
+ * 3. Deploys Paymaster V4 via Factory
105
+ * 4. Registers ROLE_PAYMASTER_AOA with staking
106
+ *
107
+ * @param params Deployment parameters
108
+ * @param options Transaction options
109
+ * @returns Object containing new paymaster address and transaction hashes
110
+ */
111
+ async deployAndRegisterPaymasterV4(params, options) {
112
+ try {
113
+ const registryAddr = this.requireRegistry();
114
+ const gTokenAddr = this.requireGToken();
115
+ const gTokenStakingAddr = this.requireGTokenStaking();
116
+ const factoryAddr = this.requirePaymasterFactory();
117
+ const registry = registryActions(registryAddr);
118
+ const gToken = tokenActions();
119
+ const factory = paymasterFactoryActions(factoryAddr);
120
+ const publicClient = this.getStartPublicClient();
121
+ const account = options?.account || this.client.account || this.getAddress();
122
+ const accountAddr = typeof account === 'string' ? account : account.address;
123
+ // 1. Check prerequisites (ROLE_COMMUNITY)
124
+ const ROLE_COMMUNITY = await registry(publicClient).ROLE_COMMUNITY();
125
+ const hasCommunity = await registry(publicClient).hasRole({
126
+ user: accountAddr,
127
+ roleId: ROLE_COMMUNITY
128
+ });
129
+ if (!hasCommunity) {
130
+ throw new Error('Must have ROLE_COMMUNITY before deploying Paymaster V4');
131
+ }
132
+ // 2. Deployment (Idempotent Check)
133
+ const existingPaymaster = await factory(publicClient).getPaymaster({ owner: accountAddr });
134
+ let deployHash = '0x0000000000000000000000000000000000000000000000000000000000000000';
135
+ let paymasterAddress;
136
+ if (existingPaymaster && existingPaymaster !== '0x0000000000000000000000000000000000000000') {
137
+ console.log(` ℹ️ Paymaster already deployed at: ${existingPaymaster}`);
138
+ paymasterAddress = existingPaymaster;
139
+ }
140
+ else {
141
+ console.log(' 🛠️ Deploying Paymaster V4 with args:', {
142
+ entryPoint: this.requireEntryPoint(),
143
+ owner: accountAddr,
144
+ priceFeed: this.ethUsdPriceFeed,
145
+ factory: factoryAddr
146
+ });
147
+ const { encodeFunctionData } = await import('viem');
148
+ const initData = encodeFunctionData({
149
+ abi: PaymasterABI,
150
+ functionName: 'initialize',
151
+ args: [
152
+ this.requireEntryPoint(), // EntryPoint v0.7
153
+ accountAddr,
154
+ accountAddr, // Treasury defaults to owner
155
+ this.ethUsdPriceFeed,
156
+ 200n, // serviceFeeRate (2%)
157
+ parseEther('0.1'), // maxGasCostCap
158
+ 3600n // priceStalenessThreshold
159
+ ]
160
+ });
161
+ deployHash = await factory(this.client).deployPaymaster({
162
+ version: params?.version,
163
+ initData,
164
+ account
165
+ });
166
+ await publicClient.waitForTransactionReceipt({ hash: deployHash });
167
+ paymasterAddress = await factory(publicClient).getPaymaster({ owner: accountAddr });
168
+ }
169
+ if (!paymasterAddress || paymasterAddress === '0x0000000000000000000000000000000000000000') {
170
+ throw new Error('Failed to retrieve Paymaster address from Factory');
171
+ }
172
+ // 3. Register ROLE_PAYMASTER_AOA
173
+ const ROLE_PAYMASTER_AOA = await registry(publicClient).ROLE_PAYMASTER_AOA();
174
+ const hasAOA = await registry(publicClient).hasRole({
175
+ user: accountAddr,
176
+ roleId: ROLE_PAYMASTER_AOA
177
+ });
178
+ if (hasAOA) {
179
+ return { paymasterAddress, deployHash, registerHash: '0x0000000000000000000000000000000000000000000000000000000000000000' };
180
+ }
181
+ const stakeAmount = params?.stakeAmount || parseEther('30');
182
+ const allowance = await gToken(publicClient).allowance({
183
+ token: gTokenAddr,
184
+ owner: accountAddr,
185
+ spender: gTokenStakingAddr
186
+ });
187
+ if (allowance < stakeAmount) {
188
+ const approveHash = await gToken(this.client).approve({
189
+ token: gTokenAddr,
190
+ spender: gTokenStakingAddr,
191
+ amount: stakeAmount * 2n,
192
+ account: account
193
+ });
194
+ await publicClient.waitForTransactionReceipt({ hash: approveHash });
195
+ }
196
+ const { encodeAbiParameters, parseAbiParameters } = await import('viem');
197
+ let roleData = '0x';
198
+ if (stakeAmount > 0) {
199
+ roleData = encodeAbiParameters(parseAbiParameters('uint256'), [stakeAmount]);
200
+ }
201
+ const registerHash = await registry(this.client).registerRoleSelf({
202
+ roleId: ROLE_PAYMASTER_AOA,
203
+ data: roleData,
204
+ account: account
205
+ });
206
+ await publicClient.waitForTransactionReceipt({ hash: registerHash });
207
+ return {
208
+ paymasterAddress,
209
+ deployHash,
210
+ registerHash
211
+ };
212
+ }
213
+ catch (error) {
214
+ throw error;
215
+ }
216
+ }
217
+ /**
218
+ * Deposit collateral (aPNTs/GToken) to SuperPaymaster.
219
+ * This is a helper method used by registerAsSuperPaymasterOperator.
220
+ */
221
+ async depositCollateral(amount, options) {
222
+ try {
223
+ const pm = superPaymasterActions(this.superPaymasterAddress);
224
+ const publicClient = this.getStartPublicClient();
225
+ // V3.7: Dynamically fetch the token expected by SuperPaymaster
226
+ const depositToken = await pm(publicClient).APNTS_TOKEN();
227
+ const token = tokenActions();
228
+ // Approve SuperPaymaster to spend the token (usually aPNTs on Sepolia)
229
+ const allowance = await token(publicClient).allowance({
230
+ token: depositToken,
231
+ owner: this.getAddress(),
232
+ spender: this.superPaymasterAddress
233
+ });
234
+ if (allowance < amount) {
235
+ const approveHash = await token(this.client).approve({
236
+ token: depositToken,
237
+ spender: this.superPaymasterAddress,
238
+ amount,
239
+ account: options?.account
240
+ });
241
+ await publicClient.waitForTransactionReceipt({ hash: approveHash });
242
+ }
243
+ // Deposit to SuperPaymaster
244
+ return pm(this.client).deposit({
245
+ amount,
246
+ account: options?.account
247
+ });
248
+ }
249
+ catch (error) {
250
+ throw error;
251
+ }
252
+ }
253
+ async updateExchangeRate(exchangeRate, options) {
254
+ return this.configureOperator(undefined, undefined, exchangeRate, options);
255
+ }
256
+ /**
257
+ * Configure operator parameters (Token, Treasury, Exchange Rate).
258
+ * If parameters are undefined, existing values are preserved.
259
+ */
260
+ async configureOperator(xPNTsToken, treasury, exchangeRate, options) {
261
+ try {
262
+ const sp = superPaymasterActions(this.superPaymasterAddress);
263
+ const publicClient = this.getStartPublicClient();
264
+ // Fetch current config to preserve missing values
265
+ const currentConfig = await sp(publicClient).operators({ operator: this.getAddress() });
266
+ const currentToken = currentConfig.xPNTsToken;
267
+ const currentTreasury = currentConfig.treasury;
268
+ const currentRate = currentConfig.exchangeRate;
269
+ return await sp(this.client).configureOperator({
270
+ xPNTsToken: xPNTsToken || currentToken,
271
+ opTreasury: treasury || currentTreasury,
272
+ exchangeRate: exchangeRate ?? currentRate,
273
+ account: options?.account
274
+ });
275
+ }
276
+ catch (error) {
277
+ throw error;
278
+ }
279
+ }
280
+ async withdrawCollateral(to, amount, options) {
281
+ try {
282
+ const sp = superPaymasterActions(this.superPaymasterAddress);
283
+ return await sp(this.client).withdrawTo({
284
+ to,
285
+ amount,
286
+ account: options?.account
287
+ });
288
+ }
289
+ catch (error) {
290
+ throw error;
291
+ }
292
+ }
293
+ async isOperator(operator) {
294
+ try {
295
+ const sp = superPaymasterActions(this.superPaymasterAddress);
296
+ const config = await sp(this.getStartPublicClient()).operators({ operator });
297
+ return config.isConfigured;
298
+ }
299
+ catch (error) {
300
+ return false;
301
+ }
302
+ }
303
+ async getOperatorDetails(operator) {
304
+ try {
305
+ const target = operator || this.getAddress();
306
+ const sp = superPaymasterActions(this.superPaymasterAddress);
307
+ return await sp(this.getStartPublicClient()).operators({ operator: target });
308
+ }
309
+ catch (error) {
310
+ throw error;
311
+ }
312
+ }
313
+ async initiateExit(options) {
314
+ try {
315
+ const sp = superPaymasterActions(this.superPaymasterAddress);
316
+ return await sp(this.client).unlockStake({
317
+ account: options?.account
318
+ });
319
+ }
320
+ catch (error) {
321
+ throw error;
322
+ }
323
+ }
324
+ async withdrawStake(to, options) {
325
+ try {
326
+ const sp = superPaymasterActions(this.superPaymasterAddress);
327
+ return await sp(this.client).withdrawStake({
328
+ to,
329
+ account: options?.account
330
+ });
331
+ }
332
+ catch (error) {
333
+ throw error;
334
+ }
335
+ }
336
+ // ========================================
337
+ // 3. 支付代币管理 (基于 PaymasterActions)
338
+ // ========================================
339
+ async addGasToken(token, price, options) {
340
+ try {
341
+ const pm = paymasterActions(this.superPaymasterAddress);
342
+ return await pm(this.client).setTokenPrice({
343
+ token,
344
+ price,
345
+ account: options?.account
346
+ });
347
+ }
348
+ catch (error) {
349
+ throw error;
350
+ }
351
+ }
352
+ async getTokenPrice(token) {
353
+ try {
354
+ const pm = paymasterActions(this.superPaymasterAddress);
355
+ return await pm(this.getStartPublicClient()).tokenPrices({ token });
356
+ }
357
+ catch (error) {
358
+ throw error;
359
+ }
360
+ }
361
+ async setupPaymasterDeposit(params, options) {
362
+ try {
363
+ const pm = paymasterActions(params.paymaster);
364
+ return await pm(this.client).depositFor({
365
+ user: params.user,
366
+ token: params.token,
367
+ amount: params.amount,
368
+ account: options?.account
369
+ });
370
+ }
371
+ catch (error) {
372
+ throw error;
373
+ }
374
+ }
375
+ }
@@ -0,0 +1,38 @@
1
+ import { type Address, type Hash, type Hex } from 'viem';
2
+ import { BaseClient, type ClientConfig, type TransactionOptions } from '@aastar/core';
3
+ export interface ProtocolClientConfig extends ClientConfig {
4
+ dvtValidatorAddress: Address;
5
+ blsAggregatorAddress?: Address;
6
+ superPaymasterAddress?: Address;
7
+ }
8
+ export declare enum ProposalState {
9
+ Pending = 0,
10
+ Active = 1,
11
+ Canceled = 2,
12
+ Defeated = 3,
13
+ Succeeded = 4,
14
+ Queued = 5,
15
+ Expired = 6,
16
+ Executed = 7
17
+ }
18
+ /**
19
+ * Client for Protocol Governors and Validators (Infrastructure)
20
+ */
21
+ export declare class ProtocolClient extends BaseClient {
22
+ dvtValidatorAddress: Address;
23
+ blsAggregatorAddress?: Address;
24
+ superPaymasterAddress?: Address;
25
+ constructor(config: ProtocolClientConfig);
26
+ /**
27
+ * Create a new proposal
28
+ */
29
+ createProposal(target: Address, calldata: Hex, description: string, options?: TransactionOptions): Promise<Hash>;
30
+ signProposal(proposalId: bigint, signature?: Hex, options?: TransactionOptions): Promise<Hash>;
31
+ /**
32
+ * Execute a proposal with collected signatures
33
+ */
34
+ executeWithProof(proposalId: bigint, signatures: Hex[], options?: TransactionOptions): Promise<Hash>;
35
+ registerBLSKey(publicKey: Hex, options?: TransactionOptions): Promise<Hash>;
36
+ setProtocolFee(bps: bigint, options?: TransactionOptions): Promise<Hash>;
37
+ setTreasury(treasury: Address, options?: TransactionOptions): Promise<Hash>;
38
+ }
@@ -0,0 +1,137 @@
1
+ import { BaseClient } from '@aastar/core';
2
+ import { dvtActions, aggregatorActions, superPaymasterActions } from '@aastar/core';
3
+ export var ProposalState;
4
+ (function (ProposalState) {
5
+ ProposalState[ProposalState["Pending"] = 0] = "Pending";
6
+ ProposalState[ProposalState["Active"] = 1] = "Active";
7
+ ProposalState[ProposalState["Canceled"] = 2] = "Canceled";
8
+ ProposalState[ProposalState["Defeated"] = 3] = "Defeated";
9
+ ProposalState[ProposalState["Succeeded"] = 4] = "Succeeded";
10
+ ProposalState[ProposalState["Queued"] = 5] = "Queued";
11
+ ProposalState[ProposalState["Expired"] = 6] = "Expired";
12
+ ProposalState[ProposalState["Executed"] = 7] = "Executed";
13
+ })(ProposalState || (ProposalState = {}));
14
+ /**
15
+ * Client for Protocol Governors and Validators (Infrastructure)
16
+ */
17
+ export class ProtocolClient extends BaseClient {
18
+ dvtValidatorAddress;
19
+ blsAggregatorAddress;
20
+ superPaymasterAddress;
21
+ constructor(config) {
22
+ super(config);
23
+ this.dvtValidatorAddress = config.dvtValidatorAddress;
24
+ this.blsAggregatorAddress = config.blsAggregatorAddress;
25
+ this.superPaymasterAddress = config.superPaymasterAddress;
26
+ }
27
+ // ========================================
28
+ // 1. 提案管理 (DVT)
29
+ // ========================================
30
+ /**
31
+ * Create a new proposal
32
+ */
33
+ async createProposal(target, calldata, description, options) {
34
+ try {
35
+ const dvt = dvtActions(this.dvtValidatorAddress)(this.client);
36
+ // Mapping general "createProposal" to "createSlashProposal" for now
37
+ // Assuming Governance uses Validator logic or this Client is for Slash.
38
+ // Using createSlashProposal as the available action.
39
+ return await dvt.createSlashProposal({
40
+ operator: target,
41
+ level: 1, // Default level
42
+ reason: description,
43
+ account: options?.account
44
+ });
45
+ }
46
+ catch (error) {
47
+ throw error;
48
+ }
49
+ }
50
+ async signProposal(proposalId, signature = '0x', options) {
51
+ try {
52
+ const dvt = dvtActions(this.dvtValidatorAddress)(this.client);
53
+ return await dvt.signSlashProposal({
54
+ proposalId,
55
+ signature,
56
+ account: options?.account
57
+ });
58
+ }
59
+ catch (error) {
60
+ throw error;
61
+ }
62
+ }
63
+ /**
64
+ * Execute a proposal with collected signatures
65
+ */
66
+ async executeWithProof(proposalId, signatures, options) {
67
+ try {
68
+ // Mock proof generation logic or placeholder
69
+ const proof = '0x';
70
+ const dvt = dvtActions(this.dvtValidatorAddress)(this.client);
71
+ return await dvt.executeSlashWithProof({
72
+ proposalId,
73
+ repUsers: [], // Needs real data in production
74
+ newScores: [],
75
+ epoch: 0n,
76
+ proof,
77
+ account: options?.account
78
+ });
79
+ }
80
+ catch (error) {
81
+ throw error;
82
+ }
83
+ }
84
+ // ========================================
85
+ // 2. 验证器管理 / BLS
86
+ // ========================================
87
+ async registerBLSKey(publicKey, options) {
88
+ try {
89
+ if (!this.blsAggregatorAddress) {
90
+ throw new Error('BLS Aggregator address required for this client');
91
+ }
92
+ // Aggregator actions now handle the type internally or via mapping
93
+ const agg = aggregatorActions(this.blsAggregatorAddress)(this.client);
94
+ return await agg.registerBLSPublicKey({
95
+ validator: this.getAddress(),
96
+ publicKey,
97
+ account: options?.account
98
+ });
99
+ }
100
+ catch (error) {
101
+ throw error;
102
+ }
103
+ }
104
+ // ========================================
105
+ // 3. 全局参数管理 (Admin)
106
+ // ========================================
107
+ async setProtocolFee(bps, options) {
108
+ try {
109
+ if (!this.superPaymasterAddress) {
110
+ throw new Error('SuperPaymaster address required for this client');
111
+ }
112
+ const sp = superPaymasterActions(this.superPaymasterAddress);
113
+ return await sp(this.client).setProtocolFee({
114
+ newFeeBPS: bps,
115
+ account: options?.account
116
+ });
117
+ }
118
+ catch (error) {
119
+ throw error;
120
+ }
121
+ }
122
+ async setTreasury(treasury, options) {
123
+ try {
124
+ if (!this.superPaymasterAddress) {
125
+ throw new Error('SuperPaymaster address required for this client');
126
+ }
127
+ const sp = superPaymasterActions(this.superPaymasterAddress);
128
+ return await sp(this.client).setTreasury({
129
+ treasury,
130
+ account: options?.account
131
+ });
132
+ }
133
+ catch (error) {
134
+ throw error;
135
+ }
136
+ }
137
+ }
@@ -0,0 +1,2 @@
1
+ export * from './PaymasterOperatorClient.js';
2
+ export * from './ProtocolClient.js';
package/dist/index.js ADDED
@@ -0,0 +1,2 @@
1
+ export * from './PaymasterOperatorClient.js';
2
+ export * from './ProtocolClient.js';
package/package.json ADDED
@@ -0,0 +1,26 @@
1
+ {
2
+ "name": "@aastar/operator",
3
+ "version": "0.16.11",
4
+ "description": "Operator client for AAstar SDK",
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
7
+ "keywords": [
8
+ "aastar",
9
+ "operator",
10
+ "web3"
11
+ ],
12
+ "author": "AAstar Team",
13
+ "license": "MIT",
14
+ "dependencies": {
15
+ "viem": "2.43.3",
16
+ "@aastar/core": "0.16.11"
17
+ },
18
+ "devDependencies": {
19
+ "typescript": "5.7.2"
20
+ },
21
+ "type": "module",
22
+ "scripts": {
23
+ "build": "tsc",
24
+ "test": "vitest run"
25
+ }
26
+ }