@aastar/core 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.
Files changed (136) hide show
  1. package/LICENSE +21 -0
  2. package/dist/abis/BLSAggregator.json +686 -0
  3. package/dist/abis/BLSValidator.json +42 -0
  4. package/dist/abis/DVTValidator.json +368 -0
  5. package/dist/abis/Eip7702Support.json +24 -0
  6. package/dist/abis/EntryPoint.json +1382 -0
  7. package/dist/abis/GToken.json +513 -0
  8. package/dist/abis/GTokenStaking.json +949 -0
  9. package/dist/abis/LegacyAccount.json +625 -0
  10. package/dist/abis/MySBT.json +1518 -0
  11. package/dist/abis/Paymaster.json +1143 -0
  12. package/dist/abis/PaymasterFactory.json +640 -0
  13. package/dist/abis/Registry.json +1942 -0
  14. package/dist/abis/ReputationSystem.json +699 -0
  15. package/dist/abis/SenderCreator.json +99 -0
  16. package/dist/abis/Simple7702Account.json +395 -0
  17. package/dist/abis/SimpleAccount.json +560 -0
  18. package/dist/abis/SimpleAccountFactory.json +111 -0
  19. package/dist/abis/SimpleAccountFactoryV08.json +87 -0
  20. package/dist/abis/SimpleAccountV08.json +557 -0
  21. package/dist/abis/SuperPaymaster.json +1781 -0
  22. package/dist/abis/UserOperationLib.json +57 -0
  23. package/dist/abis/abi.config.json +24 -0
  24. package/dist/abis/index.d.ts +1126 -0
  25. package/dist/abis/index.js +91 -0
  26. package/dist/abis/xPNTsFactory.json +718 -0
  27. package/dist/abis/xPNTsToken.json +1280 -0
  28. package/dist/actions/StateValidator.d.ts +68 -0
  29. package/dist/actions/StateValidator.js +187 -0
  30. package/dist/actions/StateValidator.test.d.ts +1 -0
  31. package/dist/actions/StateValidator.test.js +144 -0
  32. package/dist/actions/account.d.ts +55 -0
  33. package/dist/actions/account.js +133 -0
  34. package/dist/actions/account.test.d.ts +1 -0
  35. package/dist/actions/account.test.js +118 -0
  36. package/dist/actions/aggregator.d.ts +17 -0
  37. package/dist/actions/aggregator.js +31 -0
  38. package/dist/actions/aggregator.test.d.ts +1 -0
  39. package/dist/actions/aggregator.test.js +67 -0
  40. package/dist/actions/dvt.d.ts +30 -0
  41. package/dist/actions/dvt.js +41 -0
  42. package/dist/actions/dvt.test.d.ts +1 -0
  43. package/dist/actions/dvt.test.js +98 -0
  44. package/dist/actions/entryPoint.d.ts +90 -0
  45. package/dist/actions/entryPoint.js +211 -0
  46. package/dist/actions/entryPoint.test.d.ts +1 -0
  47. package/dist/actions/entryPoint.test.js +139 -0
  48. package/dist/actions/factory.d.ts +215 -0
  49. package/dist/actions/factory.js +442 -0
  50. package/dist/actions/factory.test.d.ts +1 -0
  51. package/dist/actions/factory.test.js +197 -0
  52. package/dist/actions/faucet.d.ts +48 -0
  53. package/dist/actions/faucet.js +337 -0
  54. package/dist/actions/faucet.test.d.ts +1 -0
  55. package/dist/actions/faucet.test.js +120 -0
  56. package/dist/actions/gtokenExtended.d.ts +39 -0
  57. package/dist/actions/gtokenExtended.js +115 -0
  58. package/dist/actions/gtokenExtended.test.d.ts +1 -0
  59. package/dist/actions/gtokenExtended.test.js +118 -0
  60. package/dist/actions/index.d.ts +15 -0
  61. package/dist/actions/index.js +17 -0
  62. package/dist/actions/paymasterV4.d.ts +170 -0
  63. package/dist/actions/paymasterV4.js +334 -0
  64. package/dist/actions/paymasterV4.test.d.ts +1 -0
  65. package/dist/actions/paymasterV4.test.js +159 -0
  66. package/dist/actions/registry.d.ts +246 -0
  67. package/dist/actions/registry.js +667 -0
  68. package/dist/actions/registry.test.d.ts +1 -0
  69. package/dist/actions/registry.test.js +360 -0
  70. package/dist/actions/reputation.d.ts +129 -0
  71. package/dist/actions/reputation.js +281 -0
  72. package/dist/actions/reputation.test.d.ts +1 -0
  73. package/dist/actions/reputation.test.js +169 -0
  74. package/dist/actions/sbt.d.ts +191 -0
  75. package/dist/actions/sbt.js +533 -0
  76. package/dist/actions/sbt.test.d.ts +1 -0
  77. package/dist/actions/sbt.test.js +256 -0
  78. package/dist/actions/staking.d.ts +132 -0
  79. package/dist/actions/staking.js +330 -0
  80. package/dist/actions/staking.test.d.ts +1 -0
  81. package/dist/actions/staking.test.js +223 -0
  82. package/dist/actions/superPaymaster.d.ts +237 -0
  83. package/dist/actions/superPaymaster.js +644 -0
  84. package/dist/actions/superPaymaster.test.d.ts +1 -0
  85. package/dist/actions/superPaymaster.test.js +287 -0
  86. package/dist/actions/tokens.d.ts +229 -0
  87. package/dist/actions/tokens.js +415 -0
  88. package/dist/actions/tokens.test.d.ts +1 -0
  89. package/dist/actions/tokens.test.js +53 -0
  90. package/dist/actions/validators.d.ts +194 -0
  91. package/dist/actions/validators.js +433 -0
  92. package/dist/actions/validators.test.d.ts +1 -0
  93. package/dist/actions/validators.test.js +215 -0
  94. package/dist/branding.d.ts +30 -0
  95. package/dist/branding.js +30 -0
  96. package/dist/clients/BaseClient.d.ts +25 -0
  97. package/dist/clients/BaseClient.js +66 -0
  98. package/dist/clients/types.d.ts +60 -0
  99. package/dist/clients/types.js +1 -0
  100. package/dist/clients.d.ts +5 -0
  101. package/dist/clients.js +11 -0
  102. package/dist/communities.d.ts +52 -0
  103. package/dist/communities.js +73 -0
  104. package/dist/config/ContractConfigManager.d.ts +20 -0
  105. package/dist/config/ContractConfigManager.js +48 -0
  106. package/dist/constants.d.ts +88 -0
  107. package/dist/constants.js +125 -0
  108. package/dist/contract-addresses.d.ts +110 -0
  109. package/dist/contract-addresses.js +99 -0
  110. package/dist/contracts.d.ts +424 -0
  111. package/dist/contracts.js +343 -0
  112. package/dist/contracts.test.d.ts +1 -0
  113. package/dist/contracts.test.js +40 -0
  114. package/dist/crypto/blsSigner.d.ts +64 -0
  115. package/dist/crypto/blsSigner.js +98 -0
  116. package/dist/crypto/index.d.ts +1 -0
  117. package/dist/crypto/index.js +1 -0
  118. package/dist/index.d.ts +21 -0
  119. package/dist/index.js +21 -0
  120. package/dist/networks.d.ts +127 -0
  121. package/dist/networks.js +118 -0
  122. package/dist/requirementChecker.d.ts +38 -0
  123. package/dist/requirementChecker.js +139 -0
  124. package/dist/requirementChecker.test.d.ts +1 -0
  125. package/dist/requirementChecker.test.js +60 -0
  126. package/dist/roles.d.ts +204 -0
  127. package/dist/roles.js +211 -0
  128. package/dist/roles.test.d.ts +1 -0
  129. package/dist/roles.test.js +23 -0
  130. package/dist/utils/validation.d.ts +24 -0
  131. package/dist/utils/validation.js +56 -0
  132. package/dist/utils/validation.test.d.ts +1 -0
  133. package/dist/utils/validation.test.js +40 -0
  134. package/dist/utils.d.ts +12 -0
  135. package/dist/utils.js +14 -0
  136. package/package.json +33 -0
@@ -0,0 +1,337 @@
1
+ import { parseEther, parseAbi, formatEther, zeroAddress, encodeAbiParameters } from 'viem';
2
+ import { sepolia } from 'viem/chains';
3
+ // Standard ERC20 ABI for minting/approving
4
+ const ERC20_ABI = parseAbi([
5
+ 'function mint(address to, uint256 amount) external',
6
+ 'function approve(address spender, uint256 amount) external returns (bool)',
7
+ 'function balanceOf(address account) external view returns (uint256)',
8
+ 'function allowance(address owner, address spender) external view returns (uint256)',
9
+ 'function transfer(address to, uint256 amount) external returns (bool)'
10
+ ]);
11
+ const PAYMASTER_ABI = parseAbi([
12
+ 'function depositFor(address target, uint256 amount) external',
13
+ 'function deposit() external payable'
14
+ ]);
15
+ export class SepoliaFaucetAPI {
16
+ /**
17
+ * Orchestrates the complete setup for a test account.
18
+ * 1. Funds ETH
19
+ * 2. Registers ENDUSER role
20
+ * 3. Mints potential Paymaster Tokens (cPNTs/dPNTs)
21
+ * 4. Deposits to Paymaster V4 (if address provided) using Admin's tokens
22
+ */
23
+ static async prepareTestAccount(adminWallet, publicClient, config) {
24
+ console.log(`\n🚰 SepoliaFaucetAPI: Preparing ${config.targetAA}...`);
25
+ const results = {
26
+ ethFunded: false,
27
+ roleRegistered: false,
28
+ tokenMinted: false,
29
+ paymasterDeposited: false
30
+ };
31
+ // 1. Fund ETH
32
+ const ethAmount = config.ethAmount ?? parseEther('0.1');
33
+ results.ethFunded = await this.fundETH(adminWallet, publicClient, config.targetAA, ethAmount);
34
+ // 2. Register EndUser
35
+ results.roleRegistered = await this.registerEndUser(adminWallet, publicClient, config.registry, config.targetAA, config.token, config.community);
36
+ // 3. Mint Tokens (User Holding Logic - for SuperPaymaster)
37
+ // If SuperPaymaster is involved, User needs to HOLD the token.
38
+ // If Paymaster V4 is involved, User might need to HOLD (Pull mode) or Admin Deposits (Push mode).
39
+ // Strategy: Always mint some to User for flexibility.
40
+ const tokenAmount = config.tokenAmount ?? parseEther('1000');
41
+ results.tokenMinted = await this.mintTestTokens(adminWallet, publicClient, config.token, config.targetAA, tokenAmount);
42
+ // 4. Admin Deposit for User (Paymaster V4 Logic)
43
+ if (config.paymasterV4) {
44
+ results.paymasterDeposited = await this.adminDepositForUser(adminWallet, publicClient, config.paymasterV4, config.targetAA, config.token, parseEther('10') // Deposit 10 Tokens for testing
45
+ );
46
+ }
47
+ console.log(`✅ Preparation Complete!`, results);
48
+ return results;
49
+ }
50
+ /**
51
+ * Funds the target with ETH if balance is below threshold.
52
+ */
53
+ static async fundETH(adminWallet, publicClient, target, amount) {
54
+ const balance = await publicClient.getBalance({ address: target });
55
+ // Threshold: 50% of target amount
56
+ if (balance < (amount / 2n)) {
57
+ console.log(` 💰 Funding ETH... (Before: ${formatEther(balance)})`);
58
+ const hash = await adminWallet.sendTransaction({
59
+ to: target,
60
+ value: amount,
61
+ chain: sepolia,
62
+ account: adminWallet.account
63
+ });
64
+ await publicClient.waitForTransactionReceipt({ hash, timeout: 300000 });
65
+ console.log(` -> Sent ${formatEther(amount)} ETH. Tx: ${hash}`);
66
+ return true;
67
+ }
68
+ console.log(` ✅ ETH Balance adequate (${formatEther(balance)} ETH).`);
69
+ return false;
70
+ }
71
+ /**
72
+ * Registers the ENDUSER role via Registry.
73
+ */
74
+ /**
75
+ * Registers the ENDUSER role using Sponsor Mode (Admin pays stake).
76
+ */
77
+ static async registerEndUser(adminWallet, publicClient, registryAddr, target, gasToken, // Re-using the 'token' passed in config which is GToken
78
+ community) {
79
+ // ENDUSER Hash: keccak256("ENDUSER")
80
+ const ENDUSER_ROLE = '0x0c34ecc75d3bf122e0609d2576e167f53fb42429262ce8c9b33cab91ff670e3a';
81
+ // 1. Check if already registered
82
+ const hasRole = await publicClient.readContract({
83
+ address: registryAddr,
84
+ abi: parseAbi(['function hasRole(bytes32 role, address account) view returns (bool)']),
85
+ functionName: 'hasRole',
86
+ args: [ENDUSER_ROLE, target]
87
+ });
88
+ if (hasRole) {
89
+ console.log(` ✅ ENDUSER Role already held.`);
90
+ return false;
91
+ }
92
+ console.log(` 👤 Registering ENDUSER role (Sponsor Mode)...`);
93
+ try {
94
+ // 2. Get Staking Contract Address
95
+ const stakingAddr = await publicClient.readContract({
96
+ address: registryAddr,
97
+ abi: parseAbi(['function GTOKEN_STAKING() view returns (address)']),
98
+ functionName: 'GTOKEN_STAKING'
99
+ });
100
+ // Fetch the actual GToken required by Staking
101
+ const stakingToken = await publicClient.readContract({
102
+ address: stakingAddr,
103
+ abi: parseAbi(['function GTOKEN() view returns (address)']),
104
+ functionName: 'GTOKEN'
105
+ });
106
+ // 3. Admin Approves Staking Contract (if needed)
107
+ const adminAddr = adminWallet.account.address;
108
+ console.log(` 🔎 Debug Staking: Registry=${registryAddr}, Staking=${stakingAddr}`);
109
+ console.log(` 🔎 Debug Token: Contract=${stakingToken}`);
110
+ // Check allowance for the STAKING TOKEN
111
+ const allowance = await publicClient.readContract({
112
+ address: stakingToken,
113
+ abi: ERC20_ABI,
114
+ functionName: 'allowance',
115
+ args: [adminAddr, stakingAddr]
116
+ });
117
+ console.log(` 🔎 Debug Allowance: ${formatEther(allowance)} (Needed: >0.5)`);
118
+ // Approve if needed (standard stake ~0.3 ETH, verify enough)
119
+ const approveAmount = parseEther('1000'); // A sufficiently large amount
120
+ if (allowance < parseEther('500')) {
121
+ console.log(' 🔓 Approving Staking Contract (Core Token)...');
122
+ const hashApprove = await adminWallet.writeContract({
123
+ address: stakingToken,
124
+ abi: ERC20_ABI,
125
+ functionName: 'approve',
126
+ args: [stakingAddr, approveAmount],
127
+ chain: sepolia,
128
+ account: adminWallet.account
129
+ });
130
+ await publicClient.waitForTransactionReceipt({ hash: hashApprove, timeout: 300000 });
131
+ console.log(` 🔓 Approving Staking Contract... ${hashApprove}`);
132
+ // Ensure Admin has GTokens (Staking Token) balance
133
+ const adminGBal = await publicClient.readContract({
134
+ address: stakingToken,
135
+ abi: ERC20_ABI,
136
+ functionName: 'balanceOf',
137
+ args: [adminAddr]
138
+ });
139
+ if (adminGBal < parseEther('100')) {
140
+ console.log(` 🪙 Minting GTokens (Staking Token) to Admin...`);
141
+ try {
142
+ const hashGMint = await adminWallet.writeContract({
143
+ address: stakingToken,
144
+ abi: ERC20_ABI,
145
+ functionName: 'mint',
146
+ args: [adminAddr, parseEther('1000')],
147
+ chain: sepolia,
148
+ account: adminWallet.account
149
+ });
150
+ await publicClient.waitForTransactionReceipt({ hash: hashGMint, timeout: 300000 });
151
+ console.log(` ✅ GTokens Minted. Tx: ${hashGMint}`);
152
+ }
153
+ catch (e) {
154
+ console.warn(` ⚠️ Failed to mint GTokens (Admin might not be minter):`, e);
155
+ }
156
+ }
157
+ }
158
+ // ALSO Approve Registry (just in case it pulls first)
159
+ const allowReg = await publicClient.readContract({
160
+ address: stakingToken,
161
+ abi: ERC20_ABI,
162
+ functionName: 'allowance',
163
+ args: [adminAddr, registryAddr]
164
+ });
165
+ if (allowReg < parseEther('500')) {
166
+ console.log(' 🔓 Approving Registry Contract...');
167
+ const hash = await adminWallet.writeContract({
168
+ address: stakingToken,
169
+ abi: ERC20_ABI,
170
+ functionName: 'approve',
171
+ args: [registryAddr, parseEther('1000')],
172
+ chain: sepolia,
173
+ account: adminWallet.account
174
+ });
175
+ await publicClient.waitForTransactionReceipt({ hash, timeout: 300000 });
176
+ console.log(` ✅ Approved Registry. Tx: ${hash}`);
177
+ }
178
+ else {
179
+ console.log(' ✅ Allowance sufficient.');
180
+ }
181
+ // 5. Register Role (registerRole)
182
+ // EndUser Role Data: (address account, address community, string avatarURI, string ensName, uint256 stakeAmount)
183
+ const userData = encodeAbiParameters([
184
+ { name: 'account', type: 'address' },
185
+ { name: 'community', type: 'address' },
186
+ { name: 'avatarURI', type: 'string' },
187
+ { name: 'ensName', type: 'string' },
188
+ { name: 'stakeAmount', type: 'uint256' }
189
+ ],
190
+ // Stake Amount 0, relying on Registry to enforce minStake and charge msg.sender
191
+ [target, community || zeroAddress, '', '', 0n]);
192
+ const hashMint = await adminWallet.writeContract({
193
+ address: registryAddr,
194
+ abi: parseAbi([
195
+ 'function safeMintForRole(bytes32 roleId, address user, bytes calldata data) external returns (uint256)',
196
+ 'error InvalidParameter(string message)'
197
+ ]),
198
+ functionName: 'safeMintForRole',
199
+ args: [ENDUSER_ROLE, target, userData],
200
+ chain: sepolia,
201
+ account: adminWallet.account
202
+ });
203
+ await publicClient.waitForTransactionReceipt({ hash: hashMint, timeout: 300000 });
204
+ console.log(` -> Role Sponsored & Granted. Tx: ${hashMint}`);
205
+ return true;
206
+ }
207
+ catch (error) {
208
+ console.warn(` ⚠️ Failed to sponsor ENDUSER role. Error:`, error);
209
+ return false;
210
+ }
211
+ }
212
+ /**
213
+ * Mints tokens directly to the target.
214
+ */
215
+ static async mintTestTokens(adminWallet, publicClient, token, target, amount) {
216
+ const balance = await publicClient.readContract({
217
+ address: token,
218
+ abi: ERC20_ABI,
219
+ functionName: 'balanceOf',
220
+ args: [target]
221
+ });
222
+ if (balance < amount) {
223
+ console.log(` 🪙 Funding Tokens... (Current: ${formatEther(balance)})`);
224
+ const adminAddr = adminWallet.account.address;
225
+ // 1. Try Transfer from Admin (if Admin has balance)
226
+ const adminBal = await publicClient.readContract({
227
+ address: token,
228
+ abi: ERC20_ABI,
229
+ functionName: 'balanceOf',
230
+ args: [adminAddr]
231
+ });
232
+ if (adminBal >= amount) {
233
+ console.log(` 💸 Transferring from Admin...`);
234
+ const hash = await adminWallet.writeContract({
235
+ address: token,
236
+ abi: ERC20_ABI,
237
+ functionName: 'transfer',
238
+ args: [target, amount],
239
+ chain: sepolia,
240
+ account: adminWallet.account
241
+ });
242
+ await publicClient.waitForTransactionReceipt({ hash, timeout: 300000 });
243
+ console.log(` ✅ Transferred ${formatEther(amount)}. Tx: ${hash}`);
244
+ return true;
245
+ }
246
+ // 2. Try Mint (Assuming Admin has Minter Role or is Owner)
247
+ console.log(` 🪙 Admin balance low. Attempting Mint...`);
248
+ try {
249
+ const hash = await adminWallet.writeContract({
250
+ address: token,
251
+ abi: ERC20_ABI,
252
+ functionName: 'mint',
253
+ args: [target, amount],
254
+ chain: sepolia,
255
+ account: adminWallet.account
256
+ });
257
+ await publicClient.waitForTransactionReceipt({ hash, timeout: 300000 });
258
+ console.log(` ✅ Minted ${formatEther(amount)}. Tx: ${hash}`);
259
+ return true;
260
+ }
261
+ catch (e) {
262
+ console.warn(` ⚠️ Failed to mint/transfer tokens. Check Admin permissions/balance.`, e);
263
+ return false;
264
+ }
265
+ }
266
+ console.log(` ✅ Token Balance adequate.`);
267
+ return false;
268
+ }
269
+ /**
270
+ * Complex Flow:
271
+ * 1. Admin mints tokens to SELF.
272
+ * 2. Admin approves Paymaster.
273
+ * 3. Admin calls depositFor(target) on Paymaster.
274
+ */
275
+ static async adminDepositForUser(adminWallet, publicClient, paymaster, target, token, amount) {
276
+ // 1. Mint to Admin First
277
+ const adminAddr = adminWallet.account.address;
278
+ const adminBal = await publicClient.readContract({
279
+ address: token,
280
+ abi: ERC20_ABI,
281
+ functionName: 'balanceOf',
282
+ args: [adminAddr]
283
+ });
284
+ if (adminBal < amount) {
285
+ console.log(` 🏧 Admin Minting to Self (for deposit)...`);
286
+ const hash = await adminWallet.writeContract({
287
+ address: token,
288
+ abi: ERC20_ABI,
289
+ functionName: 'mint',
290
+ args: [adminAddr, amount * 10n], // bulk mint
291
+ chain: sepolia,
292
+ account: adminWallet.account
293
+ });
294
+ await publicClient.waitForTransactionReceipt({ hash, timeout: 300000 });
295
+ console.log(` -> Admin Minted. Tx: ${hash}`);
296
+ }
297
+ // 2. Approve Paymaster
298
+ const allowance = await publicClient.readContract({
299
+ address: token,
300
+ abi: ERC20_ABI,
301
+ functionName: 'allowance',
302
+ args: [adminAddr, paymaster]
303
+ });
304
+ if (allowance < amount) {
305
+ console.log(` 🔓 Admin Approving Paymaster...`);
306
+ const hash = await adminWallet.writeContract({
307
+ address: token,
308
+ abi: ERC20_ABI,
309
+ functionName: 'approve',
310
+ args: [paymaster, 115792089237316195423570985008687907853269984665640564039457584007913129639935n], // Max Uint
311
+ chain: sepolia,
312
+ account: adminWallet.account
313
+ });
314
+ await publicClient.waitForTransactionReceipt({ hash, timeout: 300000 });
315
+ console.log(` -> Approved Paymaster. Tx: ${hash}`);
316
+ }
317
+ // 3. Deposit For
318
+ console.log(` 🏦 Depositing FOR User...`);
319
+ try {
320
+ const hash = await adminWallet.writeContract({
321
+ address: paymaster,
322
+ abi: PAYMASTER_ABI,
323
+ functionName: 'depositFor',
324
+ args: [target, amount],
325
+ chain: sepolia,
326
+ account: adminWallet.account
327
+ });
328
+ await publicClient.waitForTransactionReceipt({ hash, timeout: 300000 });
329
+ console.log(` -> Deposited ${formatEther(amount)} to PM. Tx: ${hash}`);
330
+ return true;
331
+ }
332
+ catch (e) {
333
+ console.warn(` ⚠️ Deposit Failed (Maybe not supported on this PM?):`, e);
334
+ return false;
335
+ }
336
+ }
337
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,120 @@
1
+ import { describe, it, expect, vi, beforeEach } from 'vitest';
2
+ import { parseEther } from 'viem';
3
+ import { SepoliaFaucetAPI } from './faucet.js';
4
+ describe('SepoliaFaucetAPI', () => {
5
+ let mockAdminWallet;
6
+ let mockPublicClient;
7
+ let mockAccount;
8
+ const MOCK_ADMIN = '0x1111111111111111111111111111111111111111';
9
+ const MOCK_TARGET = '0x2222222222222222222222222222222222222222';
10
+ const MOCK_TOKEN = '0x3333333333333333333333333333333333333333';
11
+ const MOCK_REGISTRY = '0x4444444444444444444444444444444444444444';
12
+ const MOCK_PM_V4 = '0x5555555555555555555555555555555555555555';
13
+ beforeEach(() => {
14
+ mockAccount = {
15
+ address: MOCK_ADMIN,
16
+ type: 'json-rpc'
17
+ };
18
+ mockAdminWallet = {
19
+ account: mockAccount,
20
+ sendTransaction: vi.fn(),
21
+ writeContract: vi.fn(),
22
+ };
23
+ mockPublicClient = {
24
+ getBalance: vi.fn(),
25
+ readContract: vi.fn(),
26
+ waitForTransactionReceipt: vi.fn(),
27
+ };
28
+ });
29
+ describe('fundETH', () => {
30
+ it('should fund ETH if balance is low', async () => {
31
+ mockPublicClient.getBalance.mockResolvedValue(parseEther('0.01'));
32
+ mockAdminWallet.sendTransaction.mockResolvedValue('0xhash');
33
+ mockPublicClient.waitForTransactionReceipt.mockResolvedValue({});
34
+ const funded = await SepoliaFaucetAPI.fundETH(mockAdminWallet, mockPublicClient, MOCK_TARGET, parseEther('0.1'));
35
+ expect(funded).toBe(true);
36
+ expect(mockAdminWallet.sendTransaction).toHaveBeenCalled();
37
+ });
38
+ it('should not fund ETH if balance is sufficient', async () => {
39
+ mockPublicClient.getBalance.mockResolvedValue(parseEther('0.1'));
40
+ const funded = await SepoliaFaucetAPI.fundETH(mockAdminWallet, mockPublicClient, MOCK_TARGET, parseEther('0.1'));
41
+ expect(funded).toBe(false);
42
+ expect(mockAdminWallet.sendTransaction).not.toHaveBeenCalled();
43
+ });
44
+ });
45
+ describe('registerEndUser', () => {
46
+ it('should register end user if role not held', async () => {
47
+ mockPublicClient.readContract
48
+ .mockResolvedValueOnce(false) // hasRole
49
+ .mockResolvedValueOnce('0xstaking') // GTOKEN_STAKING
50
+ .mockResolvedValueOnce(parseEther('1000')); // allowance
51
+ mockAdminWallet.writeContract.mockResolvedValue('0xhash');
52
+ mockPublicClient.waitForTransactionReceipt.mockResolvedValue({});
53
+ const registered = await SepoliaFaucetAPI.registerEndUser(mockAdminWallet, mockPublicClient, MOCK_REGISTRY, MOCK_TARGET, MOCK_TOKEN);
54
+ expect(registered).toBe(true);
55
+ expect(mockAdminWallet.writeContract).toHaveBeenCalledWith(expect.objectContaining({
56
+ functionName: 'safeMintForRole',
57
+ }));
58
+ });
59
+ it('should skip registration if role already held', async () => {
60
+ mockPublicClient.readContract.mockResolvedValue(true);
61
+ const registered = await SepoliaFaucetAPI.registerEndUser(mockAdminWallet, mockPublicClient, MOCK_REGISTRY, MOCK_TARGET, MOCK_TOKEN);
62
+ expect(registered).toBe(false);
63
+ });
64
+ });
65
+ describe('mintTestTokens', () => {
66
+ it('should mint tokens if balance is low', async () => {
67
+ mockPublicClient.readContract.mockResolvedValue(0n);
68
+ mockAdminWallet.writeContract.mockResolvedValue('0xhash');
69
+ mockPublicClient.waitForTransactionReceipt.mockResolvedValue({});
70
+ const minted = await SepoliaFaucetAPI.mintTestTokens(mockAdminWallet, mockPublicClient, MOCK_TOKEN, MOCK_TARGET, parseEther('1000'));
71
+ expect(minted).toBe(true);
72
+ expect(mockAdminWallet.writeContract).toHaveBeenCalledWith(expect.objectContaining({
73
+ functionName: 'mint',
74
+ args: [MOCK_TARGET, parseEther('1000')],
75
+ }));
76
+ });
77
+ it('should skip minting if balance is sufficient', async () => {
78
+ mockPublicClient.readContract.mockResolvedValue(parseEther('1000'));
79
+ const minted = await SepoliaFaucetAPI.mintTestTokens(mockAdminWallet, mockPublicClient, MOCK_TOKEN, MOCK_TARGET, parseEther('1000'));
80
+ expect(minted).toBe(false);
81
+ });
82
+ });
83
+ describe('adminDepositForUser', () => {
84
+ it('should perform admin deposit flow', async () => {
85
+ mockPublicClient.readContract
86
+ .mockResolvedValueOnce(parseEther('100')) // admin balance
87
+ .mockResolvedValueOnce(parseEther('10000')); // allowance
88
+ mockAdminWallet.writeContract.mockResolvedValue('0xhash');
89
+ mockPublicClient.waitForTransactionReceipt.mockResolvedValue({});
90
+ const deposited = await SepoliaFaucetAPI.adminDepositForUser(mockAdminWallet, mockPublicClient, MOCK_PM_V4, MOCK_TARGET, MOCK_TOKEN, parseEther('10'));
91
+ expect(deposited).toBe(true);
92
+ expect(mockAdminWallet.writeContract).toHaveBeenCalledWith(expect.objectContaining({
93
+ functionName: 'depositFor',
94
+ args: [MOCK_TARGET, parseEther('10')],
95
+ }));
96
+ });
97
+ });
98
+ describe('prepareTestAccount', () => {
99
+ it('should orchestrate complete setup', async () => {
100
+ // Mock fundETH
101
+ vi.spyOn(SepoliaFaucetAPI, 'fundETH').mockResolvedValue(true);
102
+ // Mock registerEndUser
103
+ vi.spyOn(SepoliaFaucetAPI, 'registerEndUser').mockResolvedValue(true);
104
+ // Mock mintTestTokens
105
+ vi.spyOn(SepoliaFaucetAPI, 'mintTestTokens').mockResolvedValue(true);
106
+ // Mock adminDepositForUser
107
+ vi.spyOn(SepoliaFaucetAPI, 'adminDepositForUser').mockResolvedValue(true);
108
+ const result = await SepoliaFaucetAPI.prepareTestAccount(mockAdminWallet, mockPublicClient, {
109
+ targetAA: MOCK_TARGET,
110
+ token: MOCK_TOKEN,
111
+ registry: MOCK_REGISTRY,
112
+ paymasterV4: MOCK_PM_V4
113
+ });
114
+ expect(result.ethFunded).toBe(true);
115
+ expect(result.roleRegistered).toBe(true);
116
+ expect(result.tokenMinted).toBe(true);
117
+ expect(result.paymasterDeposited).toBe(true);
118
+ });
119
+ });
120
+ });
@@ -0,0 +1,39 @@
1
+ import { type Address, type PublicClient, type WalletClient, type Hash, type Account } from 'viem';
2
+ export type GTokenExtendedActions = {
3
+ mint: (args: {
4
+ to: Address;
5
+ amount: bigint;
6
+ account?: Account | Address;
7
+ }) => Promise<Hash>;
8
+ burn: (args: {
9
+ amount: bigint;
10
+ account?: Account | Address;
11
+ }) => Promise<Hash>;
12
+ burnFrom: (args: {
13
+ from: Address;
14
+ amount: bigint;
15
+ account?: Account | Address;
16
+ }) => Promise<Hash>;
17
+ cap: () => Promise<bigint>;
18
+ minter: () => Promise<Address>;
19
+ setMinter: (args: {
20
+ minter: Address;
21
+ account?: Account | Address;
22
+ }) => Promise<Hash>;
23
+ pause: (args: {
24
+ account?: Account | Address;
25
+ }) => Promise<Hash>;
26
+ unpause: (args: {
27
+ account?: Account | Address;
28
+ }) => Promise<Hash>;
29
+ paused: () => Promise<boolean>;
30
+ owner: () => Promise<Address>;
31
+ transferOwnership: (args: {
32
+ newOwner: Address;
33
+ account?: Account | Address;
34
+ }) => Promise<Hash>;
35
+ renounceOwnership: (args: {
36
+ account?: Account | Address;
37
+ }) => Promise<Hash>;
38
+ };
39
+ export declare const gTokenExtendedActions: (address: Address) => (client: PublicClient | WalletClient) => GTokenExtendedActions;
@@ -0,0 +1,115 @@
1
+ import { GTokenABI } from '../abis/index.js';
2
+ export const gTokenExtendedActions = (address) => (client) => ({
3
+ async mint({ to, amount, account }) {
4
+ return client.writeContract({
5
+ address,
6
+ abi: GTokenABI,
7
+ functionName: 'mint',
8
+ args: [to, amount],
9
+ account: account,
10
+ chain: client.chain
11
+ });
12
+ },
13
+ async burn({ amount, account }) {
14
+ return client.writeContract({
15
+ address,
16
+ abi: GTokenABI,
17
+ functionName: 'burn',
18
+ args: [amount],
19
+ account: account,
20
+ chain: client.chain
21
+ });
22
+ },
23
+ async burnFrom({ from, amount, account }) {
24
+ return client.writeContract({
25
+ address,
26
+ abi: GTokenABI,
27
+ functionName: 'burnFrom',
28
+ args: [from, amount],
29
+ account: account,
30
+ chain: client.chain
31
+ });
32
+ },
33
+ async cap() {
34
+ return client.readContract({
35
+ address,
36
+ abi: GTokenABI,
37
+ functionName: 'cap',
38
+ args: []
39
+ });
40
+ },
41
+ async minter() {
42
+ return client.readContract({
43
+ address,
44
+ abi: GTokenABI,
45
+ functionName: 'minter',
46
+ args: []
47
+ });
48
+ },
49
+ async setMinter({ minter, account }) {
50
+ return client.writeContract({
51
+ address,
52
+ abi: GTokenABI,
53
+ functionName: 'setMinter',
54
+ args: [minter],
55
+ account: account,
56
+ chain: client.chain
57
+ });
58
+ },
59
+ async pause({ account }) {
60
+ return client.writeContract({
61
+ address,
62
+ abi: GTokenABI,
63
+ functionName: 'pause',
64
+ args: [],
65
+ account: account,
66
+ chain: client.chain
67
+ });
68
+ },
69
+ async unpause({ account }) {
70
+ return client.writeContract({
71
+ address,
72
+ abi: GTokenABI,
73
+ functionName: 'unpause',
74
+ args: [],
75
+ account: account,
76
+ chain: client.chain
77
+ });
78
+ },
79
+ async paused() {
80
+ return client.readContract({
81
+ address,
82
+ abi: GTokenABI,
83
+ functionName: 'paused',
84
+ args: []
85
+ });
86
+ },
87
+ async owner() {
88
+ return client.readContract({
89
+ address,
90
+ abi: GTokenABI,
91
+ functionName: 'owner',
92
+ args: []
93
+ });
94
+ },
95
+ async transferOwnership({ newOwner, account }) {
96
+ return client.writeContract({
97
+ address,
98
+ abi: GTokenABI,
99
+ functionName: 'transferOwnership',
100
+ args: [newOwner],
101
+ account: account,
102
+ chain: client.chain
103
+ });
104
+ },
105
+ async renounceOwnership({ account }) {
106
+ return client.writeContract({
107
+ address,
108
+ abi: GTokenABI,
109
+ functionName: 'renounceOwnership',
110
+ args: [],
111
+ account: account,
112
+ chain: client.chain
113
+ });
114
+ }
115
+ });
@@ -0,0 +1 @@
1
+ export {};