@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.
- package/LICENSE +21 -0
- package/__tests__/PaymasterOperatorClient.test.ts +258 -0
- package/__tests__/ProtocolClient.test.ts +135 -0
- package/__tests__/index.test.ts +16 -0
- package/__tests__/mocks/client.ts +22 -0
- package/dist/PaymasterOperatorClient.d.ts +82 -0
- package/dist/PaymasterOperatorClient.js +375 -0
- package/dist/ProtocolClient.d.ts +38 -0
- package/dist/ProtocolClient.js +137 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/package.json +26 -0
- package/src/PaymasterOperatorClient.ts +454 -0
- package/src/ProtocolClient.ts +154 -0
- package/src/index.ts +2 -0
- package/tsconfig.json +10 -0
|
@@ -0,0 +1,454 @@
|
|
|
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
|
+
}
|
|
@@ -0,0 +1,154 @@
|
|
|
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
ADDED