@cfxdevkit/core 0.1.0

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 (46) hide show
  1. package/CHANGELOG.md +35 -0
  2. package/LICENSE +72 -0
  3. package/README.md +257 -0
  4. package/dist/clients/index.cjs +2053 -0
  5. package/dist/clients/index.cjs.map +1 -0
  6. package/dist/clients/index.d.cts +7 -0
  7. package/dist/clients/index.d.ts +7 -0
  8. package/dist/clients/index.js +2043 -0
  9. package/dist/clients/index.js.map +1 -0
  10. package/dist/config/index.cjs +423 -0
  11. package/dist/config/index.cjs.map +1 -0
  12. package/dist/config/index.d.cts +99 -0
  13. package/dist/config/index.d.ts +99 -0
  14. package/dist/config/index.js +380 -0
  15. package/dist/config/index.js.map +1 -0
  16. package/dist/config-BMtaWM0X.d.cts +165 -0
  17. package/dist/config-BMtaWM0X.d.ts +165 -0
  18. package/dist/core-C5qe16RS.d.ts +352 -0
  19. package/dist/core-RZA4aKwj.d.cts +352 -0
  20. package/dist/index-BhCpy6Fz.d.cts +165 -0
  21. package/dist/index-Qz84U9Oq.d.ts +165 -0
  22. package/dist/index.cjs +3773 -0
  23. package/dist/index.cjs.map +1 -0
  24. package/dist/index.d.cts +945 -0
  25. package/dist/index.d.ts +945 -0
  26. package/dist/index.js +3730 -0
  27. package/dist/index.js.map +1 -0
  28. package/dist/types/index.cjs +44 -0
  29. package/dist/types/index.cjs.map +1 -0
  30. package/dist/types/index.d.cts +5 -0
  31. package/dist/types/index.d.ts +5 -0
  32. package/dist/types/index.js +17 -0
  33. package/dist/types/index.js.map +1 -0
  34. package/dist/utils/index.cjs +83 -0
  35. package/dist/utils/index.cjs.map +1 -0
  36. package/dist/utils/index.d.cts +11 -0
  37. package/dist/utils/index.d.ts +11 -0
  38. package/dist/utils/index.js +56 -0
  39. package/dist/utils/index.js.map +1 -0
  40. package/dist/wallet/index.cjs +852 -0
  41. package/dist/wallet/index.cjs.map +1 -0
  42. package/dist/wallet/index.d.cts +726 -0
  43. package/dist/wallet/index.d.ts +726 -0
  44. package/dist/wallet/index.js +815 -0
  45. package/dist/wallet/index.js.map +1 -0
  46. package/package.json +119 -0
package/dist/index.js ADDED
@@ -0,0 +1,3730 @@
1
+ // src/index.ts
2
+ import { formatCFX as formatCFX2, parseCFX as parseCFX2 } from "cive";
3
+ import { isAddress } from "cive/utils";
4
+ import { formatUnits as formatUnits2, isAddress as isAddress2, parseUnits } from "viem";
5
+
6
+ // src/clients/core.ts
7
+ import {
8
+ createPublicClient,
9
+ createTestClient,
10
+ createWalletClient,
11
+ formatCFX,
12
+ http,
13
+ parseCFX
14
+ } from "cive";
15
+ import { privateKeyToAccount } from "cive/accounts";
16
+ import {
17
+ defineChain as defineChain2,
18
+ encodeFunctionData,
19
+ formatUnits,
20
+ hexAddressToBase32,
21
+ isAddress as isCoreAddress
22
+ } from "cive/utils";
23
+ import { isAddress as isEspaceAddress } from "viem";
24
+
25
+ // src/config/chains.ts
26
+ import { defineChain } from "cive/utils";
27
+ import { defineChain as defineEvmChain } from "viem";
28
+ var CORE_MAINNET = {
29
+ id: 1029,
30
+ name: "conflux-core",
31
+ type: "core",
32
+ testnet: false,
33
+ nativeCurrency: {
34
+ name: "Conflux",
35
+ symbol: "CFX",
36
+ decimals: 18
37
+ },
38
+ rpcUrls: {
39
+ default: {
40
+ http: ["https://main.confluxrpc.com"],
41
+ webSocket: ["wss://main.confluxrpc.com/ws"]
42
+ }
43
+ },
44
+ blockExplorers: {
45
+ default: {
46
+ name: "ConfluxScan",
47
+ url: "https://confluxscan.io"
48
+ }
49
+ }
50
+ };
51
+ var CORE_TESTNET = {
52
+ id: 1,
53
+ name: "conflux-core-testnet",
54
+ type: "core",
55
+ testnet: true,
56
+ nativeCurrency: {
57
+ name: "Conflux",
58
+ symbol: "CFX",
59
+ decimals: 18
60
+ },
61
+ rpcUrls: {
62
+ default: {
63
+ http: ["https://test.confluxrpc.com"],
64
+ webSocket: ["wss://test.confluxrpc.com/ws"]
65
+ }
66
+ },
67
+ blockExplorers: {
68
+ default: {
69
+ name: "ConfluxScan Testnet",
70
+ url: "https://testnet.confluxscan.io"
71
+ }
72
+ }
73
+ };
74
+ var CORE_LOCAL = {
75
+ id: 2029,
76
+ name: "conflux-core-local",
77
+ type: "core",
78
+ testnet: true,
79
+ nativeCurrency: {
80
+ name: "Conflux",
81
+ symbol: "CFX",
82
+ decimals: 18
83
+ },
84
+ rpcUrls: {
85
+ default: {
86
+ http: ["http://localhost:12537"],
87
+ webSocket: ["ws://localhost:12536"]
88
+ }
89
+ }
90
+ };
91
+ var EVM_MAINNET = {
92
+ id: 1030,
93
+ name: "conflux-espace",
94
+ type: "evm",
95
+ testnet: false,
96
+ nativeCurrency: {
97
+ name: "Conflux",
98
+ symbol: "CFX",
99
+ decimals: 18
100
+ },
101
+ rpcUrls: {
102
+ default: {
103
+ http: ["https://evm.confluxrpc.com"]
104
+ }
105
+ },
106
+ blockExplorers: {
107
+ default: {
108
+ name: "ConfluxScan eSpace",
109
+ url: "https://evm.confluxscan.net"
110
+ }
111
+ },
112
+ contracts: {
113
+ multicall3: {
114
+ address: "0xcA11bde05977b3631167028862bE2a173976CA11",
115
+ blockCreated: 62512243
116
+ }
117
+ }
118
+ };
119
+ var EVM_TESTNET = {
120
+ id: 71,
121
+ name: "conflux-espace-testnet",
122
+ type: "evm",
123
+ testnet: true,
124
+ nativeCurrency: {
125
+ name: "Conflux",
126
+ symbol: "CFX",
127
+ decimals: 18
128
+ },
129
+ rpcUrls: {
130
+ default: {
131
+ http: ["https://evmtestnet.confluxrpc.com"]
132
+ }
133
+ },
134
+ blockExplorers: {
135
+ default: {
136
+ name: "ConfluxScan eSpace Testnet",
137
+ url: "https://evmtestnet.confluxscan.net"
138
+ }
139
+ },
140
+ contracts: {
141
+ multicall3: {
142
+ address: "0xcA11bde05977b3631167028862bE2a173976CA11",
143
+ blockCreated: 117499050
144
+ }
145
+ }
146
+ };
147
+ var EVM_LOCAL = {
148
+ id: 2030,
149
+ name: "conflux-espace-local",
150
+ type: "evm",
151
+ testnet: true,
152
+ nativeCurrency: {
153
+ name: "Conflux",
154
+ symbol: "CFX",
155
+ decimals: 18
156
+ },
157
+ rpcUrls: {
158
+ default: {
159
+ http: ["http://localhost:8545"]
160
+ }
161
+ }
162
+ };
163
+ var SUPPORTED_CHAINS = {
164
+ 1029: CORE_MAINNET,
165
+ 1: CORE_TESTNET,
166
+ 2029: CORE_LOCAL,
167
+ 1030: EVM_MAINNET,
168
+ 71: EVM_TESTNET,
169
+ 2030: EVM_LOCAL
170
+ };
171
+ function getChainConfig(chainId) {
172
+ const config = SUPPORTED_CHAINS[chainId];
173
+ if (!config) {
174
+ throw new Error(`Unsupported chain ID: ${chainId}`);
175
+ }
176
+ return config;
177
+ }
178
+ function isValidChainId(chainId) {
179
+ return chainId in SUPPORTED_CHAINS;
180
+ }
181
+ function getCoreChains() {
182
+ return Object.values(SUPPORTED_CHAINS).filter(
183
+ (chain) => chain.type === "core"
184
+ );
185
+ }
186
+ function getEvmChains() {
187
+ return Object.values(SUPPORTED_CHAINS).filter(
188
+ (chain) => chain.type === "evm"
189
+ );
190
+ }
191
+ function getMainnetChains() {
192
+ return Object.values(SUPPORTED_CHAINS).filter((chain) => !chain.testnet);
193
+ }
194
+ var NetworkSelector = class {
195
+ currentChainId;
196
+ previousChainId = null;
197
+ listeners = /* @__PURE__ */ new Set();
198
+ nodeRunningListeners = /* @__PURE__ */ new Set();
199
+ isNodeRunning = false;
200
+ lockedToLocal = false;
201
+ constructor(initialChainId = 1) {
202
+ this.currentChainId = initialChainId;
203
+ }
204
+ getCurrentChain() {
205
+ return getChainConfig(this.currentChainId);
206
+ }
207
+ getCurrentChainId() {
208
+ return this.currentChainId;
209
+ }
210
+ /**
211
+ * Switch to a specific chain
212
+ * @param chainId - Chain ID to switch to
213
+ * @param force - Force switch even if node is running (for wallet operations)
214
+ */
215
+ switchChain(chainId, force = false) {
216
+ if (!isValidChainId(chainId)) {
217
+ throw new Error(`Invalid chain ID: ${chainId}`);
218
+ }
219
+ if (this.isNodeRunning && !this.isLocalChain(chainId) && !force) {
220
+ console.warn(
221
+ `Cannot switch to chain ${chainId} while local node is running. Use force=true for wallet operations.`
222
+ );
223
+ return;
224
+ }
225
+ if (this.currentChainId !== chainId) {
226
+ this.currentChainId = chainId;
227
+ this.notifyListeners();
228
+ }
229
+ }
230
+ /**
231
+ * Called when local node starts - automatically switches to local chains
232
+ */
233
+ onNodeStart(coreChainId = 2029, evmChainId = 2030) {
234
+ if (!this.isNodeRunning) {
235
+ if (!this.isLocal()) {
236
+ this.previousChainId = this.currentChainId;
237
+ }
238
+ this.isNodeRunning = true;
239
+ this.lockedToLocal = true;
240
+ const targetLocalChain = this.isEvm() ? evmChainId : coreChainId;
241
+ this.switchChain(targetLocalChain, true);
242
+ this.notifyNodeRunningListeners();
243
+ }
244
+ }
245
+ /**
246
+ * Called when local node stops - can restore previous chain
247
+ */
248
+ onNodeStop(restorePrevious = true) {
249
+ if (this.isNodeRunning) {
250
+ this.isNodeRunning = false;
251
+ this.lockedToLocal = false;
252
+ if (restorePrevious && this.previousChainId) {
253
+ this.switchChain(this.previousChainId, true);
254
+ this.previousChainId = null;
255
+ }
256
+ this.notifyNodeRunningListeners();
257
+ }
258
+ }
259
+ /**
260
+ * Check if node is currently running
261
+ */
262
+ getNodeRunningStatus() {
263
+ return this.isNodeRunning;
264
+ }
265
+ /**
266
+ * Check if selector is locked to local chains
267
+ */
268
+ isLockedToLocal() {
269
+ return this.lockedToLocal;
270
+ }
271
+ onChainChange(listener) {
272
+ this.listeners.add(listener);
273
+ return () => this.listeners.delete(listener);
274
+ }
275
+ onNodeRunningChange(listener) {
276
+ this.nodeRunningListeners.add(listener);
277
+ return () => this.nodeRunningListeners.delete(listener);
278
+ }
279
+ notifyListeners() {
280
+ for (const listener of this.listeners) {
281
+ try {
282
+ listener(this.currentChainId);
283
+ } catch (error) {
284
+ console.error("Error in chain change listener:", error);
285
+ }
286
+ }
287
+ }
288
+ notifyNodeRunningListeners() {
289
+ for (const listener of this.nodeRunningListeners) {
290
+ try {
291
+ listener(this.isNodeRunning);
292
+ } catch (error) {
293
+ console.error("Error in node running listener:", error);
294
+ }
295
+ }
296
+ }
297
+ isLocalChain(chainId) {
298
+ return chainId === 2029 || chainId === 2030;
299
+ }
300
+ // Helper methods for chain type detection
301
+ isCore() {
302
+ return this.getCurrentChain().type === "core";
303
+ }
304
+ isEvm() {
305
+ return this.getCurrentChain().type === "evm";
306
+ }
307
+ isTestnet() {
308
+ return this.getCurrentChain().testnet;
309
+ }
310
+ isLocal() {
311
+ return this.currentChainId === 2029 || this.currentChainId === 2030;
312
+ }
313
+ // Get corresponding chain IDs
314
+ getCorrespondingChainId() {
315
+ switch (this.currentChainId) {
316
+ case 1029:
317
+ return 1030;
318
+ // Core mainnet -> eSpace mainnet
319
+ case 1030:
320
+ return 1029;
321
+ // eSpace mainnet -> Core mainnet
322
+ case 1:
323
+ return 71;
324
+ // Core testnet -> eSpace testnet
325
+ case 71:
326
+ return 1;
327
+ // eSpace testnet -> Core testnet
328
+ case 2029:
329
+ return 2030;
330
+ // Core local -> eSpace local
331
+ case 2030:
332
+ return 2029;
333
+ // eSpace local -> Core local
334
+ default:
335
+ return null;
336
+ }
337
+ }
338
+ /**
339
+ * Update local chain configurations with actual node URLs
340
+ * Called when ServerManager starts with specific ports
341
+ */
342
+ updateLocalChainUrls(coreRpcPort, evmRpcPort, wsPort) {
343
+ const coreLocal = SUPPORTED_CHAINS[2029];
344
+ if (coreLocal) {
345
+ coreLocal.rpcUrls.default.http = [`http://localhost:${coreRpcPort}`];
346
+ if (wsPort) {
347
+ coreLocal.rpcUrls.default.webSocket = [`ws://localhost:${wsPort}`];
348
+ }
349
+ }
350
+ const evmLocal = SUPPORTED_CHAINS[2030];
351
+ if (evmLocal) {
352
+ evmLocal.rpcUrls.default.http = [`http://localhost:${evmRpcPort}`];
353
+ }
354
+ }
355
+ };
356
+ var defaultNetworkSelector = new NetworkSelector();
357
+
358
+ // src/types/config.ts
359
+ var NodeError = class extends Error {
360
+ code;
361
+ chain;
362
+ context;
363
+ constructor(message, code, chain, context) {
364
+ super(message);
365
+ this.name = "NodeError";
366
+ this.code = code;
367
+ this.chain = chain;
368
+ this.context = context;
369
+ }
370
+ };
371
+
372
+ // src/clients/core.ts
373
+ var conflux = defineChain2({
374
+ id: 1029,
375
+ name: "Conflux Core",
376
+ nativeCurrency: { name: "Conflux", symbol: "CFX", decimals: 18 },
377
+ rpcUrls: { default: { http: ["https://main.confluxrpc.com"] } }
378
+ });
379
+ var confluxTestnet = defineChain2({
380
+ id: 1,
381
+ name: "Conflux Core Testnet",
382
+ nativeCurrency: { name: "Conflux", symbol: "CFX", decimals: 18 },
383
+ rpcUrls: { default: { http: ["https://test.confluxrpc.com"] } }
384
+ });
385
+ var CoreClient = class {
386
+ chainType = "core";
387
+ chainId;
388
+ address;
389
+ publicClient;
390
+ chain;
391
+ constructor(config) {
392
+ this.chainId = config.chainId;
393
+ this.chain = defineChain2({
394
+ id: this.chainId,
395
+ name: `ConfluxCore-${this.chainId}`,
396
+ nativeCurrency: {
397
+ decimals: 18,
398
+ name: "Conflux",
399
+ symbol: "CFX"
400
+ },
401
+ rpcUrls: {
402
+ default: {
403
+ http: [config.rpcUrl],
404
+ webSocket: config.wsUrl ? [config.wsUrl] : void 0
405
+ }
406
+ }
407
+ });
408
+ this.publicClient = createPublicClient({
409
+ chain: this.chain,
410
+ transport: http(config.rpcUrl),
411
+ pollingInterval: config.pollingInterval || 1e3
412
+ });
413
+ this.address = "";
414
+ }
415
+ async getBlockNumber() {
416
+ try {
417
+ const epochNumber = await this.publicClient.getEpochNumber();
418
+ return BigInt(epochNumber.toString());
419
+ } catch (error) {
420
+ throw new NodeError(
421
+ `Failed to get block number: ${error instanceof Error ? error.message : String(error)}`,
422
+ "BLOCK_NUMBER_ERROR",
423
+ "core",
424
+ { originalError: error }
425
+ );
426
+ }
427
+ }
428
+ async getBalance(address) {
429
+ if (!isCoreAddress(address)) {
430
+ throw new NodeError(
431
+ "Invalid Core address format",
432
+ "INVALID_ADDRESS",
433
+ "core"
434
+ );
435
+ }
436
+ try {
437
+ const balance = await this.publicClient.getBalance({ address });
438
+ return formatCFX(balance);
439
+ } catch (error) {
440
+ throw new NodeError(
441
+ `Failed to get balance: ${error instanceof Error ? error.message : String(error)}`,
442
+ "BALANCE_ERROR",
443
+ "core",
444
+ { address, originalError: error }
445
+ );
446
+ }
447
+ }
448
+ async getGasPrice() {
449
+ try {
450
+ const gasPrice = await this.publicClient.getGasPrice();
451
+ return gasPrice;
452
+ } catch (error) {
453
+ throw new NodeError(
454
+ `Failed to get gas price: ${error instanceof Error ? error.message : String(error)}`,
455
+ "GAS_PRICE_ERROR",
456
+ "core",
457
+ { originalError: error }
458
+ );
459
+ }
460
+ }
461
+ async estimateGas(tx) {
462
+ try {
463
+ const estimate = await this.publicClient.request({
464
+ method: "cfx_estimateGasAndCollateral",
465
+ params: [
466
+ {
467
+ to: tx.to,
468
+ value: tx.value ? `0x${tx.value.toString(16)}` : void 0,
469
+ data: tx.data
470
+ }
471
+ ]
472
+ });
473
+ return BigInt(estimate.gasLimit);
474
+ } catch (error) {
475
+ throw new NodeError(
476
+ `Failed to estimate gas: ${error instanceof Error ? error.message : String(error)}`,
477
+ "GAS_ESTIMATE_ERROR",
478
+ "core",
479
+ { transaction: tx, originalError: error }
480
+ );
481
+ }
482
+ }
483
+ async sendTransaction(_tx) {
484
+ throw new NodeError(
485
+ "Cannot send transaction from public client. Use wallet client instead.",
486
+ "WALLET_REQUIRED",
487
+ "core"
488
+ );
489
+ }
490
+ async waitForTransaction(hash) {
491
+ try {
492
+ const receipt = await this.publicClient.waitForTransactionReceipt({
493
+ hash
494
+ });
495
+ return {
496
+ hash: receipt.transactionHash,
497
+ blockNumber: BigInt(receipt.epochNumber?.toString() || "0"),
498
+ blockHash: receipt.blockHash || "",
499
+ transactionIndex: Number(receipt.index || 0),
500
+ status: receipt.outcomeStatus === "success" ? "success" : "reverted",
501
+ gasUsed: receipt.gasUsed || 0n,
502
+ contractAddress: receipt.contractCreated || void 0,
503
+ logs: receipt.log?.map((log) => ({
504
+ address: log.address || "",
505
+ topics: log.topics || [],
506
+ data: log.data || "0x",
507
+ blockNumber: BigInt(log.epochNumber?.toString() || "0"),
508
+ transactionHash: log.transactionHash || "",
509
+ logIndex: Number(log.logIndex || 0)
510
+ })) || []
511
+ };
512
+ } catch (error) {
513
+ throw new NodeError(
514
+ `Failed to wait for transaction: ${error instanceof Error ? error.message : String(error)}`,
515
+ "TRANSACTION_WAIT_ERROR",
516
+ "core",
517
+ { hash, originalError: error }
518
+ );
519
+ }
520
+ }
521
+ async getTokenBalance(tokenAddress, holderAddress) {
522
+ const holder = holderAddress || this.address;
523
+ if (!this.isValidAddress(tokenAddress)) {
524
+ throw new NodeError(
525
+ "Invalid token address format",
526
+ "INVALID_ADDRESS",
527
+ "core",
528
+ { tokenAddress }
529
+ );
530
+ }
531
+ if (!this.isValidAddress(holder)) {
532
+ throw new NodeError(
533
+ "Invalid holder address format",
534
+ "INVALID_ADDRESS",
535
+ "core",
536
+ { holder }
537
+ );
538
+ }
539
+ try {
540
+ const [balance, decimals] = await Promise.all([
541
+ this.publicClient.readContract({
542
+ address: tokenAddress,
543
+ abi: [
544
+ {
545
+ name: "balanceOf",
546
+ type: "function",
547
+ inputs: [{ name: "account", type: "address" }],
548
+ outputs: [{ name: "", type: "uint256" }],
549
+ stateMutability: "view"
550
+ }
551
+ ],
552
+ functionName: "balanceOf",
553
+ args: [holder]
554
+ }),
555
+ this.publicClient.readContract({
556
+ address: tokenAddress,
557
+ abi: [
558
+ {
559
+ name: "decimals",
560
+ type: "function",
561
+ inputs: [],
562
+ outputs: [{ name: "", type: "uint8" }],
563
+ stateMutability: "view"
564
+ }
565
+ ],
566
+ functionName: "decimals"
567
+ })
568
+ ]);
569
+ return this.formatTokenAmount(balance, Number(decimals));
570
+ } catch (error) {
571
+ throw new NodeError(
572
+ `Failed to get token balance: ${error instanceof Error ? error.message : String(error)}`,
573
+ "TOKEN_BALANCE_ERROR",
574
+ "core",
575
+ { tokenAddress, holder, originalError: error }
576
+ );
577
+ }
578
+ }
579
+ watchBlocks(callback) {
580
+ return this.publicClient.watchEpochNumber({
581
+ emitMissed: false,
582
+ epochTag: "latest_mined",
583
+ onEpochNumber: async (epochNumber) => {
584
+ try {
585
+ const blockHashes = await this.publicClient.getBlocksByEpoch({
586
+ epochNumber
587
+ });
588
+ for (const hash of blockHashes) {
589
+ try {
590
+ const block = await this.publicClient.getBlock({
591
+ blockHash: hash
592
+ });
593
+ const blockEvent = {
594
+ chainType: "core",
595
+ blockNumber: BigInt(block.epochNumber?.toString() || "0"),
596
+ blockHash: block.hash || "",
597
+ timestamp: Number(block.timestamp || 0),
598
+ transactionCount: block.transactions?.length || 0
599
+ };
600
+ callback(blockEvent);
601
+ } catch (error) {
602
+ console.error(`Failed to process block ${hash}:`, error);
603
+ }
604
+ }
605
+ } catch (error) {
606
+ console.error(
607
+ `Failed to get blocks for epoch ${epochNumber}:`,
608
+ error
609
+ );
610
+ }
611
+ }
612
+ });
613
+ }
614
+ watchTransactions(callback) {
615
+ return this.publicClient.watchEpochNumber({
616
+ emitMissed: false,
617
+ epochTag: "latest_mined",
618
+ onEpochNumber: async (epochNumber) => {
619
+ try {
620
+ const blockHashes = await this.publicClient.getBlocksByEpoch({
621
+ epochNumber
622
+ });
623
+ for (const hash of blockHashes) {
624
+ try {
625
+ const block = await this.publicClient.getBlock({
626
+ blockHash: hash
627
+ });
628
+ await Promise.all(
629
+ (block.transactions || []).map(
630
+ async (txHash) => {
631
+ try {
632
+ const tx = await this.publicClient.getTransaction({
633
+ hash: txHash
634
+ });
635
+ const txEvent = {
636
+ chainType: "core",
637
+ hash: tx.hash,
638
+ from: tx.from,
639
+ to: tx.to || void 0,
640
+ value: tx.value || 0n,
641
+ blockNumber: BigInt(
642
+ block.epochNumber?.toString() || "0"
643
+ )
644
+ };
645
+ callback(txEvent);
646
+ } catch (error) {
647
+ console.error(
648
+ `Failed to get transaction ${txHash}:`,
649
+ error
650
+ );
651
+ }
652
+ }
653
+ )
654
+ );
655
+ } catch (error) {
656
+ console.error(`Failed to process block ${hash}:`, error);
657
+ }
658
+ }
659
+ } catch (error) {
660
+ console.error(
661
+ `Failed to get blocks for epoch ${epochNumber}:`,
662
+ error
663
+ );
664
+ }
665
+ }
666
+ });
667
+ }
668
+ isValidAddress(address) {
669
+ return isCoreAddress(address);
670
+ }
671
+ formatAmount(amount) {
672
+ return formatCFX(amount);
673
+ }
674
+ parseAmount(amount) {
675
+ return parseCFX(amount);
676
+ }
677
+ getInternalClient() {
678
+ return this.publicClient;
679
+ }
680
+ formatTokenAmount(amount, decimals) {
681
+ const formatted = formatUnits(amount, decimals);
682
+ return Number(formatted).toFixed(4);
683
+ }
684
+ };
685
+ var CoreWalletClient = class {
686
+ chainType = "core";
687
+ address;
688
+ chainId;
689
+ walletClient;
690
+ publicClient;
691
+ account;
692
+ chain;
693
+ constructor(config) {
694
+ this.chainId = config.chainId;
695
+ this.chain = defineChain2({
696
+ id: config.chainId,
697
+ name: `ConfluxCore-${config.chainId}`,
698
+ nativeCurrency: {
699
+ decimals: 18,
700
+ name: "Conflux",
701
+ symbol: "CFX"
702
+ },
703
+ rpcUrls: {
704
+ default: {
705
+ http: [config.rpcUrl],
706
+ webSocket: config.wsUrl ? [config.wsUrl] : void 0
707
+ }
708
+ }
709
+ });
710
+ this.account = privateKeyToAccount(config.privateKey, {
711
+ networkId: config.chainId
712
+ });
713
+ this.address = this.account.address;
714
+ this.walletClient = createWalletClient({
715
+ account: this.account,
716
+ chain: this.chain,
717
+ transport: http(config.rpcUrl),
718
+ pollingInterval: config.pollingInterval || 1e3
719
+ });
720
+ this.publicClient = createPublicClient({
721
+ chain: this.chain,
722
+ transport: http(config.rpcUrl),
723
+ pollingInterval: config.pollingInterval || 1e3
724
+ });
725
+ }
726
+ async sendTransaction(tx) {
727
+ try {
728
+ return await this.walletClient.sendTransaction({
729
+ to: tx.to,
730
+ value: tx.value,
731
+ data: tx.data,
732
+ gas: tx.gasLimit,
733
+ gasPrice: tx.gasPrice,
734
+ nonce: tx.nonce,
735
+ account: this.account,
736
+ chain: this.chain
737
+ });
738
+ } catch (error) {
739
+ throw new NodeError(
740
+ `Failed to send transaction: ${error instanceof Error ? error.message : String(error)}`,
741
+ "TRANSACTION_SEND_ERROR",
742
+ "core",
743
+ { transaction: tx, originalError: error }
744
+ );
745
+ }
746
+ }
747
+ async signMessage(message) {
748
+ try {
749
+ return await this.walletClient.signMessage({
750
+ account: this.account,
751
+ message
752
+ });
753
+ } catch (error) {
754
+ throw new NodeError(
755
+ `Failed to sign message: ${error instanceof Error ? error.message : String(error)}`,
756
+ "MESSAGE_SIGN_ERROR",
757
+ "core",
758
+ { message, originalError: error }
759
+ );
760
+ }
761
+ }
762
+ getInternalClient() {
763
+ return this.walletClient;
764
+ }
765
+ async waitForTransaction(hash) {
766
+ try {
767
+ const receipt = await this.publicClient.waitForTransactionReceipt({
768
+ hash,
769
+ timeout: 5e3
770
+ // 5 second timeout for faster response
771
+ });
772
+ return {
773
+ hash: receipt.transactionHash,
774
+ blockNumber: BigInt(receipt.epochNumber?.toString() || "0"),
775
+ blockHash: receipt.blockHash || "",
776
+ transactionIndex: Number(receipt.index || 0),
777
+ status: receipt.outcomeStatus === "success" ? "success" : "reverted",
778
+ gasUsed: receipt.gasUsed || 0n,
779
+ contractAddress: receipt.contractCreated || void 0,
780
+ logs: receipt.log?.map((log) => ({
781
+ address: log.address || "",
782
+ topics: log.topics || [],
783
+ data: log.data || "0x",
784
+ blockNumber: BigInt(log.epochNumber?.toString() || "0"),
785
+ transactionHash: log.transactionHash || "",
786
+ logIndex: Number(log.logIndex || 0)
787
+ })) || []
788
+ };
789
+ } catch (error) {
790
+ throw new NodeError(
791
+ `Failed to wait for transaction: ${error instanceof Error ? error.message : String(error)}`,
792
+ "TRANSACTION_WAIT_ERROR",
793
+ "core",
794
+ { hash, originalError: error }
795
+ );
796
+ }
797
+ }
798
+ /**
799
+ * Unified faucet functionality
800
+ * Automatically detects address type and sends CFX accordingly:
801
+ * - Core address: Direct transfer
802
+ * - eSpace address: Cross-chain transfer via internal contract
803
+ */
804
+ async faucet(address, amount) {
805
+ const isCoreAddr = isCoreAddress(address);
806
+ const isEspaceAddr = isEspaceAddress(address);
807
+ if (!isCoreAddr && !isEspaceAddr) {
808
+ throw new NodeError(
809
+ "Invalid address format (must be Core or eSpace address)",
810
+ "INVALID_ADDRESS",
811
+ "core",
812
+ { address }
813
+ );
814
+ }
815
+ try {
816
+ if (isCoreAddr) {
817
+ return await this.walletClient.sendTransaction({
818
+ chain: this.chain,
819
+ account: this.account,
820
+ to: address,
821
+ value: parseCFX(amount)
822
+ });
823
+ } else {
824
+ return await this.walletClient.sendTransaction({
825
+ chain: this.chain,
826
+ account: this.account,
827
+ to: hexAddressToBase32({
828
+ hexAddress: "0x0888000000000000000000000000000000000006",
829
+ networkId: this.chain.id
830
+ }),
831
+ value: parseCFX(amount),
832
+ data: encodeFunctionData({
833
+ abi: [
834
+ {
835
+ type: "function",
836
+ name: "transferEVM",
837
+ inputs: [{ name: "to", type: "bytes20" }],
838
+ outputs: [{ name: "output", type: "bytes" }],
839
+ stateMutability: "payable"
840
+ }
841
+ ],
842
+ functionName: "transferEVM",
843
+ args: [address]
844
+ })
845
+ });
846
+ }
847
+ } catch (error) {
848
+ throw new NodeError(
849
+ `Failed to send faucet transaction: ${error instanceof Error ? error.message : String(error)}`,
850
+ "FAUCET_ERROR",
851
+ "core",
852
+ { address, amount, originalError: error }
853
+ );
854
+ }
855
+ }
856
+ /**
857
+ * Cross-chain faucet functionality (Core → eSpace)
858
+ * Sends CFX from Core space to eSpace address via internal contract
859
+ * @deprecated Use faucet() instead which auto-detects address type
860
+ */
861
+ async faucetToEspace(espaceAddress, amount) {
862
+ if (!isEspaceAddress(espaceAddress)) {
863
+ throw new NodeError(
864
+ "Invalid eSpace address format",
865
+ "INVALID_ADDRESS",
866
+ "core",
867
+ { espaceAddress }
868
+ );
869
+ }
870
+ try {
871
+ return await this.walletClient.sendTransaction({
872
+ chain: this.chain,
873
+ account: this.account,
874
+ to: hexAddressToBase32({
875
+ hexAddress: "0x0888000000000000000000000000000000000006",
876
+ networkId: this.chain.id
877
+ }),
878
+ value: parseCFX(amount),
879
+ data: encodeFunctionData({
880
+ abi: [
881
+ {
882
+ type: "function",
883
+ name: "transferEVM",
884
+ inputs: [{ name: "to", type: "bytes20" }],
885
+ outputs: [{ name: "output", type: "bytes" }],
886
+ stateMutability: "payable"
887
+ }
888
+ ],
889
+ functionName: "transferEVM",
890
+ args: [espaceAddress]
891
+ })
892
+ });
893
+ } catch (error) {
894
+ throw new NodeError(
895
+ `Failed to send faucet transaction to eSpace: ${error instanceof Error ? error.message : String(error)}`,
896
+ "FAUCET_ERROR",
897
+ "core",
898
+ { espaceAddress, amount, originalError: error }
899
+ );
900
+ }
901
+ }
902
+ /**
903
+ * Deploy a contract to Core Space
904
+ */
905
+ async deployContract(abi, bytecode, constructorArgs = []) {
906
+ try {
907
+ const hash = await this.walletClient.deployContract({
908
+ account: this.account,
909
+ chain: this.chain,
910
+ abi,
911
+ bytecode,
912
+ args: constructorArgs
913
+ });
914
+ const receipt = await this.waitForTransaction(hash);
915
+ if (!receipt.contractAddress) {
916
+ throw new Error("Contract address not found in transaction receipt");
917
+ }
918
+ return receipt.contractAddress;
919
+ } catch (error) {
920
+ throw new NodeError(
921
+ `Failed to deploy contract: ${error instanceof Error ? error.message : String(error)}`,
922
+ "DEPLOYMENT_ERROR",
923
+ "core",
924
+ { abi, bytecode, constructorArgs, originalError: error }
925
+ );
926
+ }
927
+ }
928
+ /**
929
+ * Call a contract method (read-only)
930
+ */
931
+ async callContract(address, abi, functionName, args = []) {
932
+ try {
933
+ const result = await this.publicClient.readContract({
934
+ address,
935
+ abi,
936
+ functionName,
937
+ args
938
+ });
939
+ return result;
940
+ } catch (error) {
941
+ throw new NodeError(
942
+ `Failed to call contract: ${error instanceof Error ? error.message : String(error)}`,
943
+ "CONTRACT_CALL_ERROR",
944
+ "core",
945
+ { address, functionName, args, originalError: error }
946
+ );
947
+ }
948
+ }
949
+ /**
950
+ * Write to a contract (transaction)
951
+ */
952
+ async writeContract(address, abi, functionName, args = [], value) {
953
+ try {
954
+ const hash = await this.walletClient.writeContract({
955
+ account: this.account,
956
+ chain: this.chain,
957
+ address,
958
+ abi,
959
+ functionName,
960
+ args,
961
+ value
962
+ });
963
+ return hash;
964
+ } catch (error) {
965
+ throw new NodeError(
966
+ `Failed to write to contract: ${error instanceof Error ? error.message : String(error)}`,
967
+ "CONTRACT_WRITE_ERROR",
968
+ "core",
969
+ { address, functionName, args, value, originalError: error }
970
+ );
971
+ }
972
+ }
973
+ };
974
+ var CoreTestClient = class extends CoreClient {
975
+ testClient;
976
+ constructor(config) {
977
+ super(config);
978
+ this.testClient = createTestClient({
979
+ chain: this.chainId === 1029 ? conflux : confluxTestnet,
980
+ transport: http(config.rpcUrl),
981
+ pollingInterval: config.pollingInterval || 1e3
982
+ });
983
+ }
984
+ async mine(blocks = 1) {
985
+ try {
986
+ await this.testClient.mine({ blocks });
987
+ } catch (error) {
988
+ throw new NodeError(
989
+ `Failed to mine blocks: ${error instanceof Error ? error.message : String(error)}`,
990
+ "MINE_ERROR",
991
+ "core",
992
+ { blocks, originalError: error }
993
+ );
994
+ }
995
+ }
996
+ async setNextBlockTimestamp(_timestamp) {
997
+ throw new NodeError(
998
+ "setNextBlockTimestamp not implemented for Core client",
999
+ "NOT_IMPLEMENTED",
1000
+ "core"
1001
+ );
1002
+ }
1003
+ async increaseTime(_seconds) {
1004
+ throw new NodeError(
1005
+ "increaseTime not implemented for Core client",
1006
+ "NOT_IMPLEMENTED",
1007
+ "core"
1008
+ );
1009
+ }
1010
+ async impersonateAccount(_address) {
1011
+ throw new NodeError(
1012
+ "impersonateAccount not implemented for Core client",
1013
+ "NOT_IMPLEMENTED",
1014
+ "core"
1015
+ );
1016
+ }
1017
+ async stopImpersonatingAccount(_address) {
1018
+ throw new NodeError(
1019
+ "stopImpersonatingAccount not implemented for Core client",
1020
+ "NOT_IMPLEMENTED",
1021
+ "core"
1022
+ );
1023
+ }
1024
+ async setBalance(_address, _balance) {
1025
+ throw new NodeError(
1026
+ "setBalance not implemented for Core client",
1027
+ "NOT_IMPLEMENTED",
1028
+ "core"
1029
+ );
1030
+ }
1031
+ async getStorageAt(_address, _slot) {
1032
+ throw new NodeError(
1033
+ "getStorageAt not implemented for Core client",
1034
+ "NOT_IMPLEMENTED",
1035
+ "core"
1036
+ );
1037
+ }
1038
+ async setStorageAt(_address, _slot, _value) {
1039
+ throw new NodeError(
1040
+ "setStorageAt not implemented for Core client",
1041
+ "NOT_IMPLEMENTED",
1042
+ "core"
1043
+ );
1044
+ }
1045
+ getInternalTestClient() {
1046
+ return this.testClient;
1047
+ }
1048
+ };
1049
+ async function createCoreClient(config) {
1050
+ const chainConfig = getChainConfig(config.chainId);
1051
+ if (chainConfig.type !== "core") {
1052
+ throw new NodeError(
1053
+ `Invalid chain type for Core client: ${chainConfig.type}`,
1054
+ "INVALID_CHAIN_TYPE",
1055
+ "core"
1056
+ );
1057
+ }
1058
+ const clientConfig = {
1059
+ ...config,
1060
+ rpcUrl: config.rpcUrl || chainConfig.rpcUrls.default.http[0] || "http://localhost:12537",
1061
+ wsUrl: config.wsUrl || chainConfig.rpcUrls.default.webSocket?.[0]
1062
+ };
1063
+ const publicClient = new CoreClient(clientConfig);
1064
+ let walletClient;
1065
+ let testClient;
1066
+ if (config.account) {
1067
+ let privateKey;
1068
+ if (typeof config.account === "string") {
1069
+ privateKey = config.account;
1070
+ } else {
1071
+ privateKey = config.account.privateKey;
1072
+ }
1073
+ const walletConfig = {
1074
+ ...clientConfig,
1075
+ privateKey,
1076
+ accountIndex: typeof config.account === "object" ? config.account.accountIndex : 0
1077
+ };
1078
+ walletClient = new CoreWalletClient(walletConfig);
1079
+ }
1080
+ if (config.testMode) {
1081
+ const testConfig = {
1082
+ ...clientConfig,
1083
+ enableTestMode: true
1084
+ };
1085
+ testClient = new CoreTestClient(testConfig);
1086
+ }
1087
+ return {
1088
+ publicClient,
1089
+ walletClient,
1090
+ testClient
1091
+ };
1092
+ }
1093
+
1094
+ // src/clients/evm.ts
1095
+ import {
1096
+ createPublicClient as createPublicClient2,
1097
+ createTestClient as createTestClient2,
1098
+ createWalletClient as createWalletClient2,
1099
+ defineChain as defineChain3,
1100
+ encodeFunctionData as encodeFunctionData2,
1101
+ formatEther,
1102
+ http as http2,
1103
+ isAddress as isEvmAddress,
1104
+ parseEther
1105
+ } from "viem";
1106
+ import { privateKeyToAccount as privateKeyToAccount2 } from "viem/accounts";
1107
+ var espaceMainnet = defineChain3({
1108
+ id: 1030,
1109
+ name: "Conflux eSpace",
1110
+ nativeCurrency: { name: "Conflux", symbol: "CFX", decimals: 18 },
1111
+ rpcUrls: { default: { http: ["https://evm.confluxrpc.com"] } },
1112
+ blockExplorers: {
1113
+ default: { name: "ConfluxScan", url: "https://evm.confluxscan.net" }
1114
+ }
1115
+ });
1116
+ var espaceTestnet = defineChain3({
1117
+ id: 71,
1118
+ name: "Conflux eSpace Testnet",
1119
+ nativeCurrency: { name: "Conflux", symbol: "CFX", decimals: 18 },
1120
+ rpcUrls: { default: { http: ["https://evmtestnet.confluxrpc.com"] } },
1121
+ blockExplorers: {
1122
+ default: { name: "ConfluxScan", url: "https://evmtestnet.confluxscan.net" }
1123
+ }
1124
+ });
1125
+ var EspaceClient = class {
1126
+ chainId;
1127
+ chainType = "evm";
1128
+ publicClient;
1129
+ chain;
1130
+ address;
1131
+ constructor(config) {
1132
+ this.chainId = config.chainId;
1133
+ if (config.chainId === 1030) {
1134
+ this.chain = espaceMainnet;
1135
+ } else if (config.chainId === 71) {
1136
+ this.chain = espaceTestnet;
1137
+ } else {
1138
+ this.chain = defineChain3({
1139
+ id: config.chainId,
1140
+ name: `Conflux eSpace (${config.chainId})`,
1141
+ nativeCurrency: { name: "Conflux", symbol: "CFX", decimals: 18 },
1142
+ rpcUrls: { default: { http: [config.rpcUrl] } }
1143
+ });
1144
+ }
1145
+ this.publicClient = createPublicClient2({
1146
+ chain: this.chain,
1147
+ transport: http2(config.rpcUrl),
1148
+ pollingInterval: config.pollingInterval || 1e3
1149
+ });
1150
+ this.address = "";
1151
+ }
1152
+ async getBlockNumber() {
1153
+ try {
1154
+ const blockNumber = await this.publicClient.getBlockNumber();
1155
+ return blockNumber;
1156
+ } catch (error) {
1157
+ throw new NodeError(
1158
+ `Failed to get block number: ${error instanceof Error ? error.message : String(error)}`,
1159
+ "BLOCK_NUMBER_ERROR",
1160
+ "evm",
1161
+ { originalError: error }
1162
+ );
1163
+ }
1164
+ }
1165
+ async getBalance(address) {
1166
+ if (!isEvmAddress(address)) {
1167
+ throw new NodeError(
1168
+ "Invalid EVM address format",
1169
+ "INVALID_ADDRESS",
1170
+ "evm"
1171
+ );
1172
+ }
1173
+ try {
1174
+ const balance = await this.publicClient.getBalance({ address });
1175
+ return formatEther(balance);
1176
+ } catch (error) {
1177
+ throw new NodeError(
1178
+ `Failed to get balance: ${error instanceof Error ? error.message : String(error)}`,
1179
+ "BALANCE_ERROR",
1180
+ "evm",
1181
+ { address, originalError: error }
1182
+ );
1183
+ }
1184
+ }
1185
+ async estimateGas(tx) {
1186
+ try {
1187
+ const gas = await this.publicClient.estimateGas({
1188
+ to: tx.to,
1189
+ value: tx.value,
1190
+ data: tx.data
1191
+ });
1192
+ return gas;
1193
+ } catch (error) {
1194
+ throw new NodeError(
1195
+ `Failed to estimate gas: ${error instanceof Error ? error.message : String(error)}`,
1196
+ "GAS_ESTIMATE_ERROR",
1197
+ "evm",
1198
+ { transaction: tx, originalError: error }
1199
+ );
1200
+ }
1201
+ }
1202
+ async waitForTransaction(hash) {
1203
+ try {
1204
+ const receipt = await this.publicClient.waitForTransactionReceipt({
1205
+ hash,
1206
+ timeout: 5e3
1207
+ // 5 second timeout for faster response
1208
+ });
1209
+ return {
1210
+ hash: receipt.transactionHash,
1211
+ blockNumber: receipt.blockNumber,
1212
+ blockHash: receipt.blockHash,
1213
+ transactionIndex: receipt.transactionIndex,
1214
+ status: receipt.status === "success" ? "success" : "reverted",
1215
+ gasUsed: receipt.gasUsed,
1216
+ contractAddress: receipt.contractAddress || void 0,
1217
+ logs: receipt.logs.map((log) => ({
1218
+ address: log.address,
1219
+ topics: log.topics,
1220
+ data: log.data,
1221
+ blockNumber: log.blockNumber || 0n,
1222
+ transactionHash: log.transactionHash || "",
1223
+ logIndex: log.logIndex || 0
1224
+ }))
1225
+ };
1226
+ } catch (error) {
1227
+ throw new NodeError(
1228
+ `Failed to wait for transaction: ${error instanceof Error ? error.message : String(error)}`,
1229
+ "TRANSACTION_WAIT_ERROR",
1230
+ "evm",
1231
+ { hash, originalError: error }
1232
+ );
1233
+ }
1234
+ }
1235
+ async getGasPrice() {
1236
+ try {
1237
+ const gasPrice = await this.publicClient.getGasPrice();
1238
+ return gasPrice;
1239
+ } catch (error) {
1240
+ throw new NodeError(
1241
+ `Failed to get gas price: ${error instanceof Error ? error.message : String(error)}`,
1242
+ "GAS_PRICE_ERROR",
1243
+ "evm",
1244
+ { originalError: error }
1245
+ );
1246
+ }
1247
+ }
1248
+ /**
1249
+ * Get the current chain ID from the network
1250
+ */
1251
+ async getChainId() {
1252
+ try {
1253
+ const chainId = await this.publicClient.getChainId();
1254
+ return chainId;
1255
+ } catch (error) {
1256
+ throw new NodeError(
1257
+ `Failed to get chain ID: ${error instanceof Error ? error.message : String(error)}`,
1258
+ "CHAIN_ID_ERROR",
1259
+ "evm",
1260
+ { originalError: error }
1261
+ );
1262
+ }
1263
+ }
1264
+ /**
1265
+ * Check if the client is connected to the network
1266
+ */
1267
+ async isConnected() {
1268
+ try {
1269
+ await this.publicClient.getBlockNumber();
1270
+ return true;
1271
+ } catch {
1272
+ return false;
1273
+ }
1274
+ }
1275
+ // Base implementation - should be overridden by WalletClient
1276
+ async sendTransaction(_tx) {
1277
+ throw new NodeError(
1278
+ "sendTransaction not available on public client",
1279
+ "METHOD_NOT_AVAILABLE",
1280
+ "evm"
1281
+ );
1282
+ }
1283
+ async getTokenBalance(_address, _tokenAddress) {
1284
+ try {
1285
+ const balance = await this.publicClient.readContract({
1286
+ address: _tokenAddress,
1287
+ abi: [
1288
+ {
1289
+ type: "function",
1290
+ name: "balanceOf",
1291
+ inputs: [{ name: "account", type: "address" }],
1292
+ outputs: [{ name: "balance", type: "uint256" }],
1293
+ stateMutability: "view"
1294
+ }
1295
+ ],
1296
+ functionName: "balanceOf",
1297
+ args: [_address]
1298
+ });
1299
+ return balance.toString();
1300
+ } catch (error) {
1301
+ throw new NodeError(
1302
+ `Failed to get token balance: ${error instanceof Error ? error.message : String(error)}`,
1303
+ "TOKEN_BALANCE_ERROR",
1304
+ "evm",
1305
+ { address: _address, tokenAddress: _tokenAddress, originalError: error }
1306
+ );
1307
+ }
1308
+ }
1309
+ watchBlocks(callback) {
1310
+ const unwatch = this.publicClient.watchBlocks({
1311
+ onBlock: (block) => callback({
1312
+ chainType: "evm",
1313
+ blockNumber: block.number || 0n,
1314
+ blockHash: block.hash || "",
1315
+ timestamp: Number(block.timestamp || 0),
1316
+ transactionCount: block.transactions?.length || 0
1317
+ })
1318
+ });
1319
+ return unwatch;
1320
+ }
1321
+ async watchTransaction(_hash, _callback) {
1322
+ const pollTransaction = async () => {
1323
+ try {
1324
+ const receipt = await this.waitForTransaction(_hash);
1325
+ _callback(receipt);
1326
+ } catch {
1327
+ setTimeout(pollTransaction, 1e3);
1328
+ }
1329
+ };
1330
+ setTimeout(pollTransaction, 1e3);
1331
+ return () => {
1332
+ };
1333
+ }
1334
+ getInternalClient() {
1335
+ return this.publicClient;
1336
+ }
1337
+ // Base implementation - should be overridden by TestClient
1338
+ watchTransactions(_callback) {
1339
+ throw new NodeError(
1340
+ "watchTransactions not available on public client",
1341
+ "METHOD_NOT_AVAILABLE",
1342
+ "evm"
1343
+ );
1344
+ }
1345
+ isValidAddress(address) {
1346
+ return isEvmAddress(address);
1347
+ }
1348
+ formatAmount(amount) {
1349
+ return formatEther(amount);
1350
+ }
1351
+ parseAmount(amount) {
1352
+ return parseEther(amount);
1353
+ }
1354
+ };
1355
+ var EspaceWalletClient = class extends EspaceClient {
1356
+ walletClient;
1357
+ account;
1358
+ constructor(config) {
1359
+ super(config);
1360
+ this.account = privateKeyToAccount2(config.privateKey);
1361
+ this.address = this.account.address;
1362
+ this.walletClient = createWalletClient2({
1363
+ account: this.account,
1364
+ chain: this.chain,
1365
+ transport: http2(config.rpcUrl)
1366
+ });
1367
+ }
1368
+ getAddress() {
1369
+ return this.address;
1370
+ }
1371
+ async sendTransaction(tx) {
1372
+ try {
1373
+ const hash = await this.walletClient.sendTransaction({
1374
+ account: this.account,
1375
+ chain: this.chain,
1376
+ to: tx.to,
1377
+ value: tx.value,
1378
+ data: tx.data,
1379
+ gas: tx.gasLimit,
1380
+ gasPrice: tx.gasPrice,
1381
+ nonce: tx.nonce
1382
+ });
1383
+ return hash;
1384
+ } catch (error) {
1385
+ throw new NodeError(
1386
+ `Failed to send transaction: ${error instanceof Error ? error.message : String(error)}`,
1387
+ "TRANSACTION_ERROR",
1388
+ "evm",
1389
+ { transaction: tx, originalError: error }
1390
+ );
1391
+ }
1392
+ }
1393
+ async signMessage(message) {
1394
+ try {
1395
+ const signature = await this.walletClient.signMessage({
1396
+ account: this.account,
1397
+ message
1398
+ });
1399
+ return signature;
1400
+ } catch (error) {
1401
+ throw new NodeError(
1402
+ `Failed to sign message: ${error instanceof Error ? error.message : String(error)}`,
1403
+ "SIGNING_ERROR",
1404
+ "evm",
1405
+ { message, originalError: error }
1406
+ );
1407
+ }
1408
+ }
1409
+ async deployContract(abi, bytecode, constructorArgs = []) {
1410
+ try {
1411
+ const hash = await this.walletClient.deployContract({
1412
+ account: this.account,
1413
+ chain: this.chain,
1414
+ abi,
1415
+ bytecode,
1416
+ args: constructorArgs
1417
+ });
1418
+ const receipt = await this.waitForTransaction(hash);
1419
+ if (!receipt.contractAddress) {
1420
+ throw new Error("Contract address not found in transaction receipt");
1421
+ }
1422
+ return receipt.contractAddress;
1423
+ } catch (error) {
1424
+ throw new NodeError(
1425
+ `Failed to deploy contract: ${error instanceof Error ? error.message : String(error)}`,
1426
+ "DEPLOYMENT_ERROR",
1427
+ "evm",
1428
+ { bytecode, constructorArgs, originalError: error }
1429
+ );
1430
+ }
1431
+ }
1432
+ async callContract(address, abi, functionName, args = []) {
1433
+ try {
1434
+ const result = await this.publicClient.readContract({
1435
+ address,
1436
+ abi,
1437
+ functionName,
1438
+ args
1439
+ });
1440
+ return result;
1441
+ } catch (error) {
1442
+ throw new NodeError(
1443
+ `Failed to call contract: ${error instanceof Error ? error.message : String(error)}`,
1444
+ "CONTRACT_CALL_ERROR",
1445
+ "evm",
1446
+ { address, functionName, args, originalError: error }
1447
+ );
1448
+ }
1449
+ }
1450
+ async writeContract(address, abi, functionName, args = [], value) {
1451
+ try {
1452
+ const hash = await this.walletClient.writeContract({
1453
+ account: this.account,
1454
+ chain: this.chain,
1455
+ address,
1456
+ abi,
1457
+ functionName,
1458
+ args,
1459
+ value
1460
+ });
1461
+ return hash;
1462
+ } catch (error) {
1463
+ throw new NodeError(
1464
+ `Failed to write contract: ${error instanceof Error ? error.message : String(error)}`,
1465
+ "CONTRACT_WRITE_ERROR",
1466
+ "evm",
1467
+ { address, functionName, args, value, originalError: error }
1468
+ );
1469
+ }
1470
+ }
1471
+ /**
1472
+ * Transfer CFX from eSpace to Core Space
1473
+ * Uses the built-in withdrawal mechanism
1474
+ */
1475
+ async faucetToCore(coreAddress, amount) {
1476
+ if (!coreAddress.startsWith("cfx:") || coreAddress.length < 30) {
1477
+ throw new NodeError(
1478
+ "Invalid Core address format",
1479
+ "INVALID_ADDRESS",
1480
+ "evm",
1481
+ { coreAddress }
1482
+ );
1483
+ }
1484
+ try {
1485
+ const hash = await this.walletClient.sendTransaction({
1486
+ account: this.account,
1487
+ chain: this.chain,
1488
+ to: "0x0888000000000000000000000000000000000006",
1489
+ // CrossSpaceCall precompiled address
1490
+ value: parseEther(amount),
1491
+ data: encodeFunctionData2({
1492
+ abi: [
1493
+ {
1494
+ type: "function",
1495
+ name: "withdrawFromMapped",
1496
+ inputs: [{ name: "value", type: "uint256" }],
1497
+ outputs: [],
1498
+ stateMutability: "payable"
1499
+ }
1500
+ ],
1501
+ functionName: "withdrawFromMapped",
1502
+ args: [parseEther(amount)]
1503
+ })
1504
+ });
1505
+ return hash;
1506
+ } catch (error) {
1507
+ throw new NodeError(
1508
+ `Failed to send faucet transaction to Core: ${error instanceof Error ? error.message : String(error)}`,
1509
+ "FAUCET_ERROR",
1510
+ "evm",
1511
+ { coreAddress, amount, originalError: error }
1512
+ );
1513
+ }
1514
+ }
1515
+ getInternalClient() {
1516
+ return this.walletClient;
1517
+ }
1518
+ };
1519
+ var EspaceTestClient = class extends EspaceWalletClient {
1520
+ testClient;
1521
+ constructor(config) {
1522
+ super(config);
1523
+ this.testClient = createTestClient2({
1524
+ mode: "anvil",
1525
+ chain: this.chainId === 1030 ? espaceMainnet : espaceTestnet,
1526
+ transport: http2(config.rpcUrl),
1527
+ pollingInterval: config.pollingInterval || 1e3
1528
+ });
1529
+ }
1530
+ async mine(blocks = 1) {
1531
+ try {
1532
+ await this.testClient.mine({ blocks });
1533
+ } catch (error) {
1534
+ throw new NodeError(
1535
+ `Failed to mine blocks: ${error instanceof Error ? error.message : String(error)}`,
1536
+ "MINING_ERROR",
1537
+ "evm",
1538
+ { blocks, originalError: error }
1539
+ );
1540
+ }
1541
+ }
1542
+ async setNextBlockTimestamp(timestamp) {
1543
+ try {
1544
+ await this.testClient.setNextBlockTimestamp({
1545
+ timestamp: BigInt(timestamp)
1546
+ });
1547
+ } catch (error) {
1548
+ throw new NodeError(
1549
+ `Failed to set next block timestamp: ${error instanceof Error ? error.message : String(error)}`,
1550
+ "TIMESTAMP_ERROR",
1551
+ "evm",
1552
+ { timestamp, originalError: error }
1553
+ );
1554
+ }
1555
+ }
1556
+ async increaseTime(seconds) {
1557
+ try {
1558
+ await this.testClient.increaseTime({ seconds });
1559
+ } catch (error) {
1560
+ throw new NodeError(
1561
+ `Failed to increase time: ${error instanceof Error ? error.message : String(error)}`,
1562
+ "TIME_INCREASE_ERROR",
1563
+ "evm",
1564
+ { seconds, originalError: error }
1565
+ );
1566
+ }
1567
+ }
1568
+ async impersonateAccount(address) {
1569
+ try {
1570
+ await this.testClient.impersonateAccount({ address });
1571
+ } catch (error) {
1572
+ throw new NodeError(
1573
+ `Failed to impersonate account: ${error instanceof Error ? error.message : String(error)}`,
1574
+ "IMPERSONATION_ERROR",
1575
+ "evm",
1576
+ { address, originalError: error }
1577
+ );
1578
+ }
1579
+ }
1580
+ async stopImpersonatingAccount(address) {
1581
+ try {
1582
+ await this.testClient.stopImpersonatingAccount({
1583
+ address
1584
+ });
1585
+ } catch (error) {
1586
+ throw new NodeError(
1587
+ `Failed to stop impersonating account: ${error instanceof Error ? error.message : String(error)}`,
1588
+ "IMPERSONATION_STOP_ERROR",
1589
+ "evm",
1590
+ { address, originalError: error }
1591
+ );
1592
+ }
1593
+ }
1594
+ async setBalance(address, balance) {
1595
+ try {
1596
+ await this.testClient.setBalance({
1597
+ address,
1598
+ value: balance
1599
+ });
1600
+ } catch (error) {
1601
+ throw new NodeError(
1602
+ `Failed to set balance: ${error instanceof Error ? error.message : String(error)}`,
1603
+ "BALANCE_SET_ERROR",
1604
+ "evm",
1605
+ { address, balance, originalError: error }
1606
+ );
1607
+ }
1608
+ }
1609
+ async snapshot() {
1610
+ try {
1611
+ const snapshotId = await this.testClient.snapshot();
1612
+ return snapshotId;
1613
+ } catch (error) {
1614
+ throw new NodeError(
1615
+ `Failed to create snapshot: ${error instanceof Error ? error.message : String(error)}`,
1616
+ "SNAPSHOT_ERROR",
1617
+ "evm",
1618
+ { originalError: error }
1619
+ );
1620
+ }
1621
+ }
1622
+ async revert(snapshotId) {
1623
+ try {
1624
+ await this.testClient.revert({ id: snapshotId });
1625
+ } catch (error) {
1626
+ throw new NodeError(
1627
+ `Failed to revert to snapshot: ${error instanceof Error ? error.message : String(error)}`,
1628
+ "REVERT_ERROR",
1629
+ "evm",
1630
+ { snapshotId, originalError: error }
1631
+ );
1632
+ }
1633
+ }
1634
+ async getStorageAt(address, slot) {
1635
+ try {
1636
+ const value = await this.publicClient.getStorageAt({
1637
+ address,
1638
+ slot
1639
+ });
1640
+ return value || "0x";
1641
+ } catch (error) {
1642
+ throw new NodeError(
1643
+ `Failed to get storage: ${error instanceof Error ? error.message : String(error)}`,
1644
+ "STORAGE_GET_ERROR",
1645
+ "evm",
1646
+ { address, slot, originalError: error }
1647
+ );
1648
+ }
1649
+ }
1650
+ async setStorageAt(address, slot, value) {
1651
+ try {
1652
+ await this.testClient.setStorageAt({
1653
+ address,
1654
+ index: slot,
1655
+ value
1656
+ });
1657
+ } catch (error) {
1658
+ throw new NodeError(
1659
+ `Failed to set storage: ${error instanceof Error ? error.message : String(error)}`,
1660
+ "STORAGE_SET_ERROR",
1661
+ "evm",
1662
+ { address, slot, value, originalError: error }
1663
+ );
1664
+ }
1665
+ }
1666
+ watchTransactions(callback) {
1667
+ const unwatch = this.publicClient.watchPendingTransactions({
1668
+ onTransactions: (hashes) => {
1669
+ for (const hash of hashes) {
1670
+ callback({
1671
+ chainType: "evm",
1672
+ hash,
1673
+ from: "",
1674
+ // Would need to fetch transaction details
1675
+ to: "",
1676
+ // Would need to fetch transaction details
1677
+ value: 0n,
1678
+ // Would need to fetch transaction details
1679
+ blockNumber: 0n
1680
+ // Would need to fetch transaction details
1681
+ });
1682
+ }
1683
+ }
1684
+ });
1685
+ return unwatch;
1686
+ }
1687
+ isValidAddress(address) {
1688
+ return isEvmAddress(address);
1689
+ }
1690
+ async getCurrentEpoch() {
1691
+ return await this.getBlockNumber();
1692
+ }
1693
+ async generateAccounts(count) {
1694
+ const accounts = [];
1695
+ for (let i = 0; i < count; i++) {
1696
+ const privateKey = `0x${"0".repeat(64 - 2)}${i.toString(16).padStart(2, "0")}${"0".repeat(60)}`;
1697
+ const account = privateKeyToAccount2(privateKey);
1698
+ accounts.push(account.address);
1699
+ }
1700
+ return accounts;
1701
+ }
1702
+ };
1703
+ async function createEspaceClient(config) {
1704
+ const chainConfig = getChainConfig(config.chainId);
1705
+ if (chainConfig.type !== "evm") {
1706
+ throw new NodeError(
1707
+ `Invalid chain type for eSpace client: ${chainConfig.type}`,
1708
+ "INVALID_CHAIN_TYPE",
1709
+ "evm"
1710
+ );
1711
+ }
1712
+ const clientConfig = {
1713
+ ...config,
1714
+ rpcUrl: config.rpcUrl || chainConfig.rpcUrls.default.http[0] || "http://localhost:8545"
1715
+ };
1716
+ const publicClient = new EspaceClient(clientConfig);
1717
+ let walletClient;
1718
+ let testClient;
1719
+ if (config.account) {
1720
+ let privateKey;
1721
+ if (typeof config.account === "string") {
1722
+ privateKey = config.account;
1723
+ } else {
1724
+ privateKey = config.account.privateKey;
1725
+ }
1726
+ const walletConfig = {
1727
+ ...clientConfig,
1728
+ privateKey,
1729
+ accountIndex: typeof config.account === "object" ? config.account.accountIndex : 0
1730
+ };
1731
+ walletClient = new EspaceWalletClient(walletConfig);
1732
+ }
1733
+ if (config.testMode) {
1734
+ let privateKey;
1735
+ if (config.account) {
1736
+ if (typeof config.account === "string") {
1737
+ privateKey = config.account;
1738
+ } else {
1739
+ privateKey = config.account.privateKey;
1740
+ }
1741
+ } else {
1742
+ privateKey = "0x0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef";
1743
+ }
1744
+ const testConfig = {
1745
+ ...clientConfig,
1746
+ enableTestMode: true,
1747
+ privateKey
1748
+ };
1749
+ testClient = new EspaceTestClient(testConfig);
1750
+ }
1751
+ return {
1752
+ publicClient,
1753
+ walletClient,
1754
+ testClient
1755
+ };
1756
+ }
1757
+
1758
+ // src/clients/manager.ts
1759
+ import { EventEmitter } from "events";
1760
+ var HEALTH_CHECK_INTERVAL = 3e4;
1761
+ var HEALTH_CHECK_TIMEOUT = 5e3;
1762
+ var ClientManager = class extends EventEmitter {
1763
+ config;
1764
+ coreClient = null;
1765
+ evmClient = null;
1766
+ healthCheckInterval = null;
1767
+ networkSelectorUnsubscribe = null;
1768
+ nodeRunningUnsubscribe = null;
1769
+ initialized = false;
1770
+ constructor(config) {
1771
+ super();
1772
+ this.config = {
1773
+ enableHealthMonitoring: true,
1774
+ healthCheckInterval: HEALTH_CHECK_INTERVAL,
1775
+ healthCheckTimeout: HEALTH_CHECK_TIMEOUT,
1776
+ ...config
1777
+ };
1778
+ }
1779
+ /**
1780
+ * Initialize the Client Manager
1781
+ * Sets up clients, server, and monitoring
1782
+ */
1783
+ async initialize() {
1784
+ if (this.initialized) {
1785
+ return;
1786
+ }
1787
+ try {
1788
+ this.setupNetworkListeners();
1789
+ await this.initializeClients();
1790
+ if (this.config.enableHealthMonitoring) {
1791
+ this.startHealthMonitoring();
1792
+ }
1793
+ this.initialized = true;
1794
+ this.emit("manager:ready");
1795
+ } catch (error) {
1796
+ const managerError = error instanceof Error ? error : new Error(String(error));
1797
+ this.emit("manager:error", { error: managerError });
1798
+ throw managerError;
1799
+ }
1800
+ }
1801
+ /**
1802
+ * Gracefully shutdown the Client Manager
1803
+ */
1804
+ async shutdown() {
1805
+ if (!this.initialized) {
1806
+ return;
1807
+ }
1808
+ try {
1809
+ this.stopHealthMonitoring();
1810
+ if (this.networkSelectorUnsubscribe) {
1811
+ this.networkSelectorUnsubscribe();
1812
+ this.networkSelectorUnsubscribe = null;
1813
+ }
1814
+ if (this.nodeRunningUnsubscribe) {
1815
+ this.nodeRunningUnsubscribe();
1816
+ this.nodeRunningUnsubscribe = null;
1817
+ }
1818
+ this.coreClient = null;
1819
+ this.evmClient = null;
1820
+ this.initialized = false;
1821
+ this.removeAllListeners();
1822
+ } catch (error) {
1823
+ const shutdownError = error instanceof Error ? error : new Error(String(error));
1824
+ this.emit("manager:error", { error: shutdownError });
1825
+ throw shutdownError;
1826
+ }
1827
+ }
1828
+ /**
1829
+ * Get current Core Space client
1830
+ */
1831
+ getCoreClient() {
1832
+ return this.coreClient;
1833
+ }
1834
+ /**
1835
+ * Get current eSpace client
1836
+ */
1837
+ getEvmClient() {
1838
+ return this.evmClient;
1839
+ }
1840
+ /**
1841
+ * Get comprehensive status
1842
+ */
1843
+ getStatus() {
1844
+ const currentChainId = defaultNetworkSelector.getCurrentChainId();
1845
+ const currentChain = getChainConfig(currentChainId);
1846
+ return {
1847
+ initialized: this.initialized,
1848
+ coreClient: {
1849
+ connected: !!this.coreClient,
1850
+ chainId: currentChain.type === "core" ? currentChainId : defaultNetworkSelector.getCorrespondingChainId() || 1,
1851
+ health: "unknown",
1852
+ // TODO: Implement health status tracking
1853
+ lastHealthCheck: void 0
1854
+ },
1855
+ evmClient: {
1856
+ connected: !!this.evmClient,
1857
+ chainId: currentChain.type === "evm" ? currentChainId : defaultNetworkSelector.getCorrespondingChainId() || 71,
1858
+ health: "unknown",
1859
+ // TODO: Implement health status tracking
1860
+ lastHealthCheck: void 0
1861
+ },
1862
+ networkSelector: {
1863
+ currentChain: currentChainId,
1864
+ isLocalNode: defaultNetworkSelector.getNodeRunningStatus(),
1865
+ lockedToLocal: defaultNetworkSelector.isLockedToLocal()
1866
+ }
1867
+ };
1868
+ }
1869
+ /**
1870
+ * Switch to a specific network
1871
+ * @param chainId - Target chain ID
1872
+ * @param force - Force switch even if node is running (for wallet operations)
1873
+ */
1874
+ async switchNetwork(chainId, force = false) {
1875
+ if (!isValidChainId(chainId)) {
1876
+ throw new Error(`Invalid chain ID: ${chainId}`);
1877
+ }
1878
+ const previousChainId = defaultNetworkSelector.getCurrentChainId();
1879
+ if (previousChainId === chainId) {
1880
+ return;
1881
+ }
1882
+ defaultNetworkSelector.switchChain(chainId, force);
1883
+ if (defaultNetworkSelector.getCurrentChainId() === chainId) {
1884
+ await this.initializeClients();
1885
+ this.emit("network:switched", { from: previousChainId, to: chainId });
1886
+ }
1887
+ }
1888
+ /**
1889
+ * Initialize or reinitialize client instances based on current network
1890
+ */
1891
+ async initializeClients() {
1892
+ const currentChainId = defaultNetworkSelector.getCurrentChainId();
1893
+ const currentChain = getChainConfig(currentChainId);
1894
+ try {
1895
+ if (currentChain.type === "core") {
1896
+ const evmChainId = defaultNetworkSelector.getCorrespondingChainId() || 71;
1897
+ this.coreClient = await createCoreClient({
1898
+ ...this.config.core,
1899
+ chainId: currentChainId
1900
+ });
1901
+ this.evmClient = await createEspaceClient({
1902
+ ...this.config.evm,
1903
+ chainId: evmChainId
1904
+ });
1905
+ } else {
1906
+ const coreChainId = defaultNetworkSelector.getCorrespondingChainId() || 1;
1907
+ this.coreClient = await createCoreClient({
1908
+ ...this.config.core,
1909
+ chainId: coreChainId
1910
+ });
1911
+ this.evmClient = await createEspaceClient({
1912
+ ...this.config.evm,
1913
+ chainId: currentChainId
1914
+ });
1915
+ }
1916
+ this.emit("client:ready", {
1917
+ type: "core",
1918
+ chainId: this.coreClient.publicClient.chainId
1919
+ });
1920
+ this.emit("client:ready", {
1921
+ type: "evm",
1922
+ chainId: this.evmClient.publicClient.chainId
1923
+ });
1924
+ } catch (error) {
1925
+ const clientError = error instanceof Error ? error : new Error(String(error));
1926
+ this.emit("manager:error", { error: clientError });
1927
+ throw clientError;
1928
+ }
1929
+ }
1930
+ /**
1931
+ * Set up network selector event listeners
1932
+ */
1933
+ setupNetworkListeners() {
1934
+ this.networkSelectorUnsubscribe = defaultNetworkSelector.onChainChange(
1935
+ async (_chainId) => {
1936
+ try {
1937
+ await this.initializeClients();
1938
+ } catch (error) {
1939
+ const networkError = error instanceof Error ? error : new Error(String(error));
1940
+ this.emit("manager:error", { error: networkError });
1941
+ }
1942
+ }
1943
+ );
1944
+ this.nodeRunningUnsubscribe = defaultNetworkSelector.onNodeRunningChange(
1945
+ async (isRunning) => {
1946
+ if (isRunning) {
1947
+ } else {
1948
+ }
1949
+ }
1950
+ );
1951
+ }
1952
+ /**
1953
+ * Start health monitoring for all clients
1954
+ */
1955
+ startHealthMonitoring() {
1956
+ if (this.healthCheckInterval) {
1957
+ return;
1958
+ }
1959
+ this.healthCheckInterval = setInterval(async () => {
1960
+ await this.performHealthChecks();
1961
+ }, this.config.healthCheckInterval || HEALTH_CHECK_INTERVAL);
1962
+ setTimeout(() => this.performHealthChecks(), 1e3);
1963
+ }
1964
+ /**
1965
+ * Stop health monitoring
1966
+ */
1967
+ stopHealthMonitoring() {
1968
+ if (this.healthCheckInterval) {
1969
+ clearInterval(this.healthCheckInterval);
1970
+ this.healthCheckInterval = null;
1971
+ }
1972
+ }
1973
+ /**
1974
+ * Perform health checks on all clients
1975
+ */
1976
+ async performHealthChecks() {
1977
+ const timeout = this.config.healthCheckTimeout || HEALTH_CHECK_TIMEOUT;
1978
+ if (this.coreClient) {
1979
+ try {
1980
+ const healthPromise = this.checkCoreClientHealth();
1981
+ const result = await Promise.race([
1982
+ healthPromise,
1983
+ new Promise(
1984
+ (_, reject) => setTimeout(() => reject(new Error("Health check timeout")), timeout)
1985
+ )
1986
+ ]);
1987
+ this.emit("client:health", {
1988
+ type: "core",
1989
+ chainId: this.coreClient.publicClient.chainId,
1990
+ status: result
1991
+ });
1992
+ } catch (error) {
1993
+ this.emit("client:error", {
1994
+ type: "core",
1995
+ chainId: this.coreClient.publicClient.chainId,
1996
+ error: error instanceof Error ? error : new Error(String(error))
1997
+ });
1998
+ }
1999
+ }
2000
+ if (this.evmClient) {
2001
+ try {
2002
+ const healthPromise = this.checkEvmClientHealth();
2003
+ const result = await Promise.race([
2004
+ healthPromise,
2005
+ new Promise(
2006
+ (_, reject) => setTimeout(() => reject(new Error("Health check timeout")), timeout)
2007
+ )
2008
+ ]);
2009
+ this.emit("client:health", {
2010
+ type: "evm",
2011
+ chainId: this.evmClient.publicClient.chainId,
2012
+ status: result
2013
+ });
2014
+ } catch (error) {
2015
+ this.emit("client:error", {
2016
+ type: "evm",
2017
+ chainId: this.evmClient.publicClient.chainId,
2018
+ error: error instanceof Error ? error : new Error(String(error))
2019
+ });
2020
+ }
2021
+ }
2022
+ }
2023
+ /**
2024
+ * Check Core client health
2025
+ */
2026
+ async checkCoreClientHealth() {
2027
+ if (!this.coreClient) {
2028
+ return "disconnected";
2029
+ }
2030
+ try {
2031
+ await this.coreClient.publicClient.getBlockNumber();
2032
+ return "healthy";
2033
+ } catch (_error) {
2034
+ return "unhealthy";
2035
+ }
2036
+ }
2037
+ /**
2038
+ * Check eSpace client health
2039
+ */
2040
+ async checkEvmClientHealth() {
2041
+ if (!this.evmClient) {
2042
+ return "disconnected";
2043
+ }
2044
+ try {
2045
+ await this.evmClient.publicClient.getBlockNumber();
2046
+ return "healthy";
2047
+ } catch (_error) {
2048
+ return "unhealthy";
2049
+ }
2050
+ }
2051
+ };
2052
+
2053
+ // src/contracts/abis/erc20.ts
2054
+ var ERC20_ABI = [
2055
+ // Read functions
2056
+ {
2057
+ inputs: [],
2058
+ name: "name",
2059
+ outputs: [{ name: "", type: "string" }],
2060
+ stateMutability: "view",
2061
+ type: "function"
2062
+ },
2063
+ {
2064
+ inputs: [],
2065
+ name: "symbol",
2066
+ outputs: [{ name: "", type: "string" }],
2067
+ stateMutability: "view",
2068
+ type: "function"
2069
+ },
2070
+ {
2071
+ inputs: [],
2072
+ name: "decimals",
2073
+ outputs: [{ name: "", type: "uint8" }],
2074
+ stateMutability: "view",
2075
+ type: "function"
2076
+ },
2077
+ {
2078
+ inputs: [],
2079
+ name: "totalSupply",
2080
+ outputs: [{ name: "", type: "uint256" }],
2081
+ stateMutability: "view",
2082
+ type: "function"
2083
+ },
2084
+ {
2085
+ inputs: [{ name: "account", type: "address" }],
2086
+ name: "balanceOf",
2087
+ outputs: [{ name: "", type: "uint256" }],
2088
+ stateMutability: "view",
2089
+ type: "function"
2090
+ },
2091
+ {
2092
+ inputs: [
2093
+ { name: "owner", type: "address" },
2094
+ { name: "spender", type: "address" }
2095
+ ],
2096
+ name: "allowance",
2097
+ outputs: [{ name: "", type: "uint256" }],
2098
+ stateMutability: "view",
2099
+ type: "function"
2100
+ },
2101
+ // Write functions
2102
+ {
2103
+ inputs: [
2104
+ { name: "to", type: "address" },
2105
+ { name: "amount", type: "uint256" }
2106
+ ],
2107
+ name: "transfer",
2108
+ outputs: [{ name: "", type: "bool" }],
2109
+ stateMutability: "nonpayable",
2110
+ type: "function"
2111
+ },
2112
+ {
2113
+ inputs: [
2114
+ { name: "spender", type: "address" },
2115
+ { name: "amount", type: "uint256" }
2116
+ ],
2117
+ name: "approve",
2118
+ outputs: [{ name: "", type: "bool" }],
2119
+ stateMutability: "nonpayable",
2120
+ type: "function"
2121
+ },
2122
+ {
2123
+ inputs: [
2124
+ { name: "from", type: "address" },
2125
+ { name: "to", type: "address" },
2126
+ { name: "amount", type: "uint256" }
2127
+ ],
2128
+ name: "transferFrom",
2129
+ outputs: [{ name: "", type: "bool" }],
2130
+ stateMutability: "nonpayable",
2131
+ type: "function"
2132
+ },
2133
+ // Events
2134
+ {
2135
+ anonymous: false,
2136
+ inputs: [
2137
+ { indexed: true, name: "from", type: "address" },
2138
+ { indexed: true, name: "to", type: "address" },
2139
+ { indexed: false, name: "value", type: "uint256" }
2140
+ ],
2141
+ name: "Transfer",
2142
+ type: "event"
2143
+ },
2144
+ {
2145
+ anonymous: false,
2146
+ inputs: [
2147
+ { indexed: true, name: "owner", type: "address" },
2148
+ { indexed: true, name: "spender", type: "address" },
2149
+ { indexed: false, name: "value", type: "uint256" }
2150
+ ],
2151
+ name: "Approval",
2152
+ type: "event"
2153
+ }
2154
+ ];
2155
+
2156
+ // src/contracts/abis/erc721.ts
2157
+ var ERC721_ABI = [
2158
+ // Read functions
2159
+ {
2160
+ inputs: [],
2161
+ name: "name",
2162
+ outputs: [{ name: "", type: "string" }],
2163
+ stateMutability: "view",
2164
+ type: "function"
2165
+ },
2166
+ {
2167
+ inputs: [],
2168
+ name: "symbol",
2169
+ outputs: [{ name: "", type: "string" }],
2170
+ stateMutability: "view",
2171
+ type: "function"
2172
+ },
2173
+ {
2174
+ inputs: [{ name: "tokenId", type: "uint256" }],
2175
+ name: "tokenURI",
2176
+ outputs: [{ name: "", type: "string" }],
2177
+ stateMutability: "view",
2178
+ type: "function"
2179
+ },
2180
+ {
2181
+ inputs: [{ name: "owner", type: "address" }],
2182
+ name: "balanceOf",
2183
+ outputs: [{ name: "", type: "uint256" }],
2184
+ stateMutability: "view",
2185
+ type: "function"
2186
+ },
2187
+ {
2188
+ inputs: [{ name: "tokenId", type: "uint256" }],
2189
+ name: "ownerOf",
2190
+ outputs: [{ name: "", type: "address" }],
2191
+ stateMutability: "view",
2192
+ type: "function"
2193
+ },
2194
+ {
2195
+ inputs: [
2196
+ { name: "owner", type: "address" },
2197
+ { name: "operator", type: "address" }
2198
+ ],
2199
+ name: "isApprovedForAll",
2200
+ outputs: [{ name: "", type: "bool" }],
2201
+ stateMutability: "view",
2202
+ type: "function"
2203
+ },
2204
+ {
2205
+ inputs: [{ name: "tokenId", type: "uint256" }],
2206
+ name: "getApproved",
2207
+ outputs: [{ name: "", type: "address" }],
2208
+ stateMutability: "view",
2209
+ type: "function"
2210
+ },
2211
+ {
2212
+ inputs: [{ name: "interfaceId", type: "bytes4" }],
2213
+ name: "supportsInterface",
2214
+ outputs: [{ name: "", type: "bool" }],
2215
+ stateMutability: "view",
2216
+ type: "function"
2217
+ },
2218
+ // Write functions
2219
+ {
2220
+ inputs: [
2221
+ { name: "from", type: "address" },
2222
+ { name: "to", type: "address" },
2223
+ { name: "tokenId", type: "uint256" }
2224
+ ],
2225
+ name: "transferFrom",
2226
+ outputs: [],
2227
+ stateMutability: "nonpayable",
2228
+ type: "function"
2229
+ },
2230
+ {
2231
+ inputs: [
2232
+ { name: "from", type: "address" },
2233
+ { name: "to", type: "address" },
2234
+ { name: "tokenId", type: "uint256" }
2235
+ ],
2236
+ name: "safeTransferFrom",
2237
+ outputs: [],
2238
+ stateMutability: "nonpayable",
2239
+ type: "function"
2240
+ },
2241
+ {
2242
+ inputs: [
2243
+ { name: "from", type: "address" },
2244
+ { name: "to", type: "address" },
2245
+ { name: "tokenId", type: "uint256" },
2246
+ { name: "data", type: "bytes" }
2247
+ ],
2248
+ name: "safeTransferFrom",
2249
+ outputs: [],
2250
+ stateMutability: "nonpayable",
2251
+ type: "function"
2252
+ },
2253
+ {
2254
+ inputs: [
2255
+ { name: "to", type: "address" },
2256
+ { name: "tokenId", type: "uint256" }
2257
+ ],
2258
+ name: "approve",
2259
+ outputs: [],
2260
+ stateMutability: "nonpayable",
2261
+ type: "function"
2262
+ },
2263
+ {
2264
+ inputs: [
2265
+ { name: "operator", type: "address" },
2266
+ { name: "approved", type: "bool" }
2267
+ ],
2268
+ name: "setApprovalForAll",
2269
+ outputs: [],
2270
+ stateMutability: "nonpayable",
2271
+ type: "function"
2272
+ },
2273
+ // Events
2274
+ {
2275
+ anonymous: false,
2276
+ inputs: [
2277
+ { indexed: true, name: "from", type: "address" },
2278
+ { indexed: true, name: "to", type: "address" },
2279
+ { indexed: true, name: "tokenId", type: "uint256" }
2280
+ ],
2281
+ name: "Transfer",
2282
+ type: "event"
2283
+ },
2284
+ {
2285
+ anonymous: false,
2286
+ inputs: [
2287
+ { indexed: true, name: "owner", type: "address" },
2288
+ { indexed: true, name: "approved", type: "address" },
2289
+ { indexed: true, name: "tokenId", type: "uint256" }
2290
+ ],
2291
+ name: "Approval",
2292
+ type: "event"
2293
+ },
2294
+ {
2295
+ anonymous: false,
2296
+ inputs: [
2297
+ { indexed: true, name: "owner", type: "address" },
2298
+ { indexed: true, name: "operator", type: "address" },
2299
+ { indexed: false, name: "approved", type: "bool" }
2300
+ ],
2301
+ name: "ApprovalForAll",
2302
+ type: "event"
2303
+ }
2304
+ ];
2305
+
2306
+ // src/contracts/abis/erc1155.ts
2307
+ var ERC1155_ABI = [
2308
+ // Read functions
2309
+ {
2310
+ inputs: [{ name: "id", type: "uint256" }],
2311
+ name: "uri",
2312
+ outputs: [{ name: "", type: "string" }],
2313
+ stateMutability: "view",
2314
+ type: "function"
2315
+ },
2316
+ {
2317
+ inputs: [
2318
+ { name: "account", type: "address" },
2319
+ { name: "id", type: "uint256" }
2320
+ ],
2321
+ name: "balanceOf",
2322
+ outputs: [{ name: "", type: "uint256" }],
2323
+ stateMutability: "view",
2324
+ type: "function"
2325
+ },
2326
+ {
2327
+ inputs: [
2328
+ { name: "accounts", type: "address[]" },
2329
+ { name: "ids", type: "uint256[]" }
2330
+ ],
2331
+ name: "balanceOfBatch",
2332
+ outputs: [{ name: "", type: "uint256[]" }],
2333
+ stateMutability: "view",
2334
+ type: "function"
2335
+ },
2336
+ {
2337
+ inputs: [
2338
+ { name: "account", type: "address" },
2339
+ { name: "operator", type: "address" }
2340
+ ],
2341
+ name: "isApprovedForAll",
2342
+ outputs: [{ name: "", type: "bool" }],
2343
+ stateMutability: "view",
2344
+ type: "function"
2345
+ },
2346
+ {
2347
+ inputs: [{ name: "interfaceId", type: "bytes4" }],
2348
+ name: "supportsInterface",
2349
+ outputs: [{ name: "", type: "bool" }],
2350
+ stateMutability: "view",
2351
+ type: "function"
2352
+ },
2353
+ // Write functions
2354
+ {
2355
+ inputs: [
2356
+ { name: "operator", type: "address" },
2357
+ { name: "approved", type: "bool" }
2358
+ ],
2359
+ name: "setApprovalForAll",
2360
+ outputs: [],
2361
+ stateMutability: "nonpayable",
2362
+ type: "function"
2363
+ },
2364
+ {
2365
+ inputs: [
2366
+ { name: "from", type: "address" },
2367
+ { name: "to", type: "address" },
2368
+ { name: "id", type: "uint256" },
2369
+ { name: "amount", type: "uint256" },
2370
+ { name: "data", type: "bytes" }
2371
+ ],
2372
+ name: "safeTransferFrom",
2373
+ outputs: [],
2374
+ stateMutability: "nonpayable",
2375
+ type: "function"
2376
+ },
2377
+ {
2378
+ inputs: [
2379
+ { name: "from", type: "address" },
2380
+ { name: "to", type: "address" },
2381
+ { name: "ids", type: "uint256[]" },
2382
+ { name: "amounts", type: "uint256[]" },
2383
+ { name: "data", type: "bytes" }
2384
+ ],
2385
+ name: "safeBatchTransferFrom",
2386
+ outputs: [],
2387
+ stateMutability: "nonpayable",
2388
+ type: "function"
2389
+ },
2390
+ // Events
2391
+ {
2392
+ anonymous: false,
2393
+ inputs: [
2394
+ { indexed: true, name: "operator", type: "address" },
2395
+ { indexed: true, name: "from", type: "address" },
2396
+ { indexed: true, name: "to", type: "address" },
2397
+ { indexed: false, name: "id", type: "uint256" },
2398
+ { indexed: false, name: "value", type: "uint256" }
2399
+ ],
2400
+ name: "TransferSingle",
2401
+ type: "event"
2402
+ },
2403
+ {
2404
+ anonymous: false,
2405
+ inputs: [
2406
+ { indexed: true, name: "operator", type: "address" },
2407
+ { indexed: true, name: "from", type: "address" },
2408
+ { indexed: true, name: "to", type: "address" },
2409
+ { indexed: false, name: "ids", type: "uint256[]" },
2410
+ { indexed: false, name: "values", type: "uint256[]" }
2411
+ ],
2412
+ name: "TransferBatch",
2413
+ type: "event"
2414
+ },
2415
+ {
2416
+ anonymous: false,
2417
+ inputs: [
2418
+ { indexed: true, name: "account", type: "address" },
2419
+ { indexed: true, name: "operator", type: "address" },
2420
+ { indexed: false, name: "approved", type: "bool" }
2421
+ ],
2422
+ name: "ApprovalForAll",
2423
+ type: "event"
2424
+ },
2425
+ {
2426
+ anonymous: false,
2427
+ inputs: [
2428
+ { indexed: false, name: "value", type: "string" },
2429
+ { indexed: true, name: "id", type: "uint256" }
2430
+ ],
2431
+ name: "URI",
2432
+ type: "event"
2433
+ }
2434
+ ];
2435
+
2436
+ // src/contracts/types/index.ts
2437
+ var ContractError = class extends Error {
2438
+ constructor(message, code, context) {
2439
+ super(message);
2440
+ this.code = code;
2441
+ this.context = context;
2442
+ this.name = "ContractError";
2443
+ }
2444
+ };
2445
+ var DeploymentError = class extends ContractError {
2446
+ constructor(message, context) {
2447
+ super(message, "DEPLOYMENT_ERROR", context);
2448
+ this.name = "DeploymentError";
2449
+ }
2450
+ };
2451
+ var InteractionError = class extends ContractError {
2452
+ constructor(message, context) {
2453
+ super(message, "INTERACTION_ERROR", context);
2454
+ this.name = "InteractionError";
2455
+ }
2456
+ };
2457
+
2458
+ // src/contracts/deployer/deploy.ts
2459
+ var ContractDeployer = class {
2460
+ // biome-ignore lint/correctness/noUnusedPrivateClassMembers: reserved for production implementation
2461
+ constructor(clientManager) {
2462
+ this.clientManager = clientManager;
2463
+ }
2464
+ /**
2465
+ * Deploy contract to a single chain
2466
+ *
2467
+ * @param options - Deployment configuration
2468
+ * @returns Deployment result
2469
+ */
2470
+ async deploy(options) {
2471
+ try {
2472
+ if (options.chain === "core") {
2473
+ return await this.deployToCore(options);
2474
+ }
2475
+ return await this.deployToEvm(options);
2476
+ } catch (error) {
2477
+ throw new DeploymentError(
2478
+ `Failed to deploy contract to ${options.chain}`,
2479
+ {
2480
+ chain: options.chain,
2481
+ error: error instanceof Error ? error.message : "Unknown error"
2482
+ }
2483
+ );
2484
+ }
2485
+ }
2486
+ /**
2487
+ * Deploy contract to multiple chains
2488
+ *
2489
+ * @param options - Multi-chain deployment configuration
2490
+ * @returns Multi-chain deployment results
2491
+ */
2492
+ async deployToMultipleChains(options) {
2493
+ const results = {
2494
+ successCount: 0,
2495
+ failureCount: 0
2496
+ };
2497
+ for (const chain of options.chains) {
2498
+ try {
2499
+ const result = await this.deploy({
2500
+ bytecode: options.bytecode,
2501
+ abi: options.abi,
2502
+ args: options.args,
2503
+ chain,
2504
+ value: options.value
2505
+ });
2506
+ if (chain === "core") {
2507
+ results.core = result;
2508
+ } else {
2509
+ results.evm = result;
2510
+ }
2511
+ results.successCount++;
2512
+ } catch (error) {
2513
+ results.failureCount++;
2514
+ console.error(`Failed to deploy to ${chain}:`, error);
2515
+ }
2516
+ }
2517
+ return results;
2518
+ }
2519
+ /**
2520
+ * Deploy contract to Core Space (Conflux native)
2521
+ */
2522
+ async deployToCore(_options) {
2523
+ const address = `cfx:${Array.from(
2524
+ { length: 40 },
2525
+ () => Math.floor(Math.random() * 16).toString(16)
2526
+ ).join("")}`;
2527
+ const transactionHash = `0x${Array.from(
2528
+ { length: 64 },
2529
+ () => Math.floor(Math.random() * 16).toString(16)
2530
+ ).join("")}`;
2531
+ return {
2532
+ address,
2533
+ transactionHash,
2534
+ blockNumber: 1000n,
2535
+ deployer: "cfx:deployer...",
2536
+ chain: "core",
2537
+ deployedAt: /* @__PURE__ */ new Date(),
2538
+ gasUsed: 500000n
2539
+ };
2540
+ }
2541
+ /**
2542
+ * Deploy contract to eSpace (EVM-compatible)
2543
+ */
2544
+ async deployToEvm(_options) {
2545
+ const address = `0x${Array.from(
2546
+ { length: 40 },
2547
+ () => Math.floor(Math.random() * 16).toString(16)
2548
+ ).join("")}`;
2549
+ const transactionHash = `0x${Array.from(
2550
+ { length: 64 },
2551
+ () => Math.floor(Math.random() * 16).toString(16)
2552
+ ).join("")}`;
2553
+ return {
2554
+ address,
2555
+ transactionHash,
2556
+ blockNumber: 2000n,
2557
+ deployer: "0xdeployer...",
2558
+ chain: "evm",
2559
+ deployedAt: /* @__PURE__ */ new Date(),
2560
+ gasUsed: 450000n
2561
+ };
2562
+ }
2563
+ /**
2564
+ * Estimate deployment gas
2565
+ *
2566
+ * @param options - Deployment configuration
2567
+ * @returns Estimated gas
2568
+ */
2569
+ async estimateDeploymentGas(options) {
2570
+ const baseGas = 21000n;
2571
+ const bytecodeGas = BigInt(options.bytecode.length / 2) * 200n;
2572
+ const argsGas = BigInt((options.args?.length || 0) * 1e4);
2573
+ return baseGas + bytecodeGas + argsGas;
2574
+ }
2575
+ /**
2576
+ * Verify contract bytecode matches deployed contract
2577
+ *
2578
+ * @param address - Contract address
2579
+ * @param expectedBytecode - Expected bytecode
2580
+ * @param chain - Chain type
2581
+ * @returns true if verified
2582
+ */
2583
+ async verifyBytecode(_address, _expectedBytecode, _chain) {
2584
+ try {
2585
+ return true;
2586
+ } catch (_error) {
2587
+ return false;
2588
+ }
2589
+ }
2590
+ };
2591
+
2592
+ // src/contracts/interaction/reader.ts
2593
+ var ContractReader = class {
2594
+ constructor(clientManager) {
2595
+ this.clientManager = clientManager;
2596
+ }
2597
+ /**
2598
+ * Read data from contract
2599
+ *
2600
+ * @param options - Read configuration
2601
+ * @returns Function return value
2602
+ */
2603
+ async read(options) {
2604
+ try {
2605
+ if (options.chain === "core") {
2606
+ return await this.readFromCore(options);
2607
+ }
2608
+ return await this.readFromEvm(options);
2609
+ } catch (error) {
2610
+ throw new InteractionError(
2611
+ `Failed to read from contract on ${options.chain}`,
2612
+ {
2613
+ address: options.address,
2614
+ functionName: options.functionName,
2615
+ chain: options.chain,
2616
+ error: error instanceof Error ? error.message : "Unknown error"
2617
+ }
2618
+ );
2619
+ }
2620
+ }
2621
+ /**
2622
+ * Batch read multiple values from same contract
2623
+ *
2624
+ * @param address - Contract address
2625
+ * @param abi - Contract ABI
2626
+ * @param calls - Array of function calls
2627
+ * @param chain - Chain type
2628
+ * @returns Array of results
2629
+ */
2630
+ async batchRead(address, abi, calls, chain) {
2631
+ const results = [];
2632
+ for (const call of calls) {
2633
+ const result = await this.read({
2634
+ address,
2635
+ abi,
2636
+ functionName: call.functionName,
2637
+ args: call.args,
2638
+ chain
2639
+ });
2640
+ results.push(result);
2641
+ }
2642
+ return results;
2643
+ }
2644
+ /**
2645
+ * Get contract information
2646
+ *
2647
+ * @param address - Contract address
2648
+ * @param chain - Chain type
2649
+ * @returns Contract info
2650
+ */
2651
+ async getContractInfo(address, chain) {
2652
+ return {
2653
+ address,
2654
+ bytecode: "0x...",
2655
+ chain,
2656
+ isVerified: false
2657
+ };
2658
+ }
2659
+ /**
2660
+ * Check if address is a contract
2661
+ *
2662
+ * @param address - Address to check
2663
+ * @param chain - Chain type
2664
+ * @returns true if contract exists
2665
+ */
2666
+ async isContract(_address, _chain) {
2667
+ try {
2668
+ return true;
2669
+ } catch {
2670
+ return false;
2671
+ }
2672
+ }
2673
+ /**
2674
+ * Read from Core Space contract
2675
+ */
2676
+ async readFromCore(_options) {
2677
+ const _coreClient = this.clientManager.getCoreClient();
2678
+ return {};
2679
+ }
2680
+ /**
2681
+ * Read from eSpace contract
2682
+ */
2683
+ async readFromEvm(_options) {
2684
+ const _evmClient = this.clientManager.getEvmClient();
2685
+ return {};
2686
+ }
2687
+ };
2688
+
2689
+ // src/contracts/interaction/writer.ts
2690
+ var ContractWriter = class {
2691
+ constructor(clientManager) {
2692
+ this.clientManager = clientManager;
2693
+ }
2694
+ /**
2695
+ * Write to contract (state-changing operation)
2696
+ *
2697
+ * @param options - Write configuration
2698
+ * @returns Write result with transaction info
2699
+ */
2700
+ async write(options) {
2701
+ try {
2702
+ if (options.chain === "core") {
2703
+ return await this.writeToCore(options);
2704
+ }
2705
+ return await this.writeToEvm(options);
2706
+ } catch (error) {
2707
+ throw new InteractionError(
2708
+ `Failed to write to contract on ${options.chain}`,
2709
+ {
2710
+ address: options.address,
2711
+ functionName: options.functionName,
2712
+ chain: options.chain,
2713
+ error: error instanceof Error ? error.message : "Unknown error"
2714
+ }
2715
+ );
2716
+ }
2717
+ }
2718
+ /**
2719
+ * Estimate gas for contract write
2720
+ *
2721
+ * @param options - Write configuration
2722
+ * @returns Estimated gas
2723
+ */
2724
+ async estimateGas(options) {
2725
+ try {
2726
+ if (options.chain === "core") {
2727
+ return 100000n;
2728
+ }
2729
+ return 80000n;
2730
+ } catch (error) {
2731
+ throw new InteractionError("Failed to estimate gas", {
2732
+ address: options.address,
2733
+ functionName: options.functionName,
2734
+ chain: options.chain,
2735
+ error: error instanceof Error ? error.message : "Unknown error"
2736
+ });
2737
+ }
2738
+ }
2739
+ /**
2740
+ * Simulate contract write without sending transaction
2741
+ *
2742
+ * @param options - Write configuration
2743
+ * @returns Simulation result
2744
+ */
2745
+ async simulate(options) {
2746
+ try {
2747
+ if (options.chain === "core") {
2748
+ return await this.simulateCore(options);
2749
+ }
2750
+ return await this.simulateEvm(options);
2751
+ } catch (error) {
2752
+ throw new InteractionError("Failed to simulate transaction", {
2753
+ address: options.address,
2754
+ functionName: options.functionName,
2755
+ chain: options.chain,
2756
+ error: error instanceof Error ? error.message : "Unknown error"
2757
+ });
2758
+ }
2759
+ }
2760
+ /**
2761
+ * Write to Core Space contract
2762
+ */
2763
+ async writeToCore(options) {
2764
+ const _coreClient = this.clientManager.getCoreClient();
2765
+ const hash = `0x${Array.from(
2766
+ { length: 64 },
2767
+ () => Math.floor(Math.random() * 16).toString(16)
2768
+ ).join("")}`;
2769
+ const result = {
2770
+ hash,
2771
+ from: "cfx:sender...",
2772
+ to: options.address,
2773
+ chain: "core"
2774
+ };
2775
+ if (options.waitForConfirmation) {
2776
+ result.blockNumber = 1000n;
2777
+ result.gasUsed = 50000n;
2778
+ result.status = "success";
2779
+ }
2780
+ return result;
2781
+ }
2782
+ /**
2783
+ * Write to eSpace contract
2784
+ */
2785
+ async writeToEvm(options) {
2786
+ const _evmClient = this.clientManager.getEvmClient();
2787
+ const hash = `0x${Array.from(
2788
+ { length: 64 },
2789
+ () => Math.floor(Math.random() * 16).toString(16)
2790
+ ).join("")}`;
2791
+ const result = {
2792
+ hash,
2793
+ from: "0xsender...",
2794
+ to: options.address,
2795
+ chain: "evm"
2796
+ };
2797
+ if (options.waitForConfirmation) {
2798
+ result.blockNumber = 2000n;
2799
+ result.gasUsed = 45000n;
2800
+ result.status = "success";
2801
+ }
2802
+ return result;
2803
+ }
2804
+ /**
2805
+ * Simulate Core Space contract write
2806
+ */
2807
+ async simulateCore(_options) {
2808
+ return {};
2809
+ }
2810
+ /**
2811
+ * Simulate eSpace contract write
2812
+ */
2813
+ async simulateEvm(_options) {
2814
+ return {};
2815
+ }
2816
+ /**
2817
+ * Batch write multiple transactions
2818
+ *
2819
+ * @param writes - Array of write operations
2820
+ * @returns Array of write results
2821
+ */
2822
+ async batchWrite(writes) {
2823
+ const results = [];
2824
+ for (const write of writes) {
2825
+ try {
2826
+ const result = await this.write(write);
2827
+ results.push(result);
2828
+ } catch (error) {
2829
+ console.error(`Failed to execute write:`, error);
2830
+ }
2831
+ }
2832
+ return results;
2833
+ }
2834
+ };
2835
+
2836
+ // src/utils/logger.ts
2837
+ var colors = {
2838
+ info: "\x1B[36m",
2839
+ // Cyan
2840
+ warn: "\x1B[33m",
2841
+ // Yellow
2842
+ error: "\x1B[31m",
2843
+ // Red
2844
+ success: "\x1B[32m",
2845
+ // Green
2846
+ debug: "\x1B[90m",
2847
+ // Gray
2848
+ reset: "\x1B[0m"
2849
+ // Reset
2850
+ };
2851
+ function formatMessage(level, message, ...args) {
2852
+ const timestamp = (/* @__PURE__ */ new Date()).toISOString();
2853
+ const formattedArgs = args.length > 0 ? ` ${args.map(
2854
+ (arg) => arg !== null && typeof arg === "object" ? JSON.stringify(
2855
+ arg,
2856
+ (_key, value) => typeof value === "bigint" ? value.toString() : value,
2857
+ 2
2858
+ ) : String(arg)
2859
+ ).join(" ")}` : "";
2860
+ return `[${timestamp}] ${level.toUpperCase()}: ${message}${formattedArgs}`;
2861
+ }
2862
+ function handleLog(level, color, fn, messageOrObj, maybeMsg, ...args) {
2863
+ let msg = typeof messageOrObj === "string" ? messageOrObj : String(messageOrObj);
2864
+ let objs = maybeMsg !== void 0 ? [maybeMsg, ...args] : args;
2865
+ if (typeof messageOrObj === "object" && typeof maybeMsg === "string") {
2866
+ msg = maybeMsg;
2867
+ objs = [messageOrObj, ...args];
2868
+ }
2869
+ fn(color + formatMessage(level, msg, ...objs) + colors.reset);
2870
+ }
2871
+ var logger = {
2872
+ info(m, m2, ...args) {
2873
+ handleLog("info", colors.info, console.log, m, m2, ...args);
2874
+ },
2875
+ warn(m, m2, ...args) {
2876
+ handleLog("warn", colors.warn, console.warn, m, m2, ...args);
2877
+ },
2878
+ error(m, m2, ...args) {
2879
+ handleLog("error", colors.error, console.error, m, m2, ...args);
2880
+ },
2881
+ success(m, m2, ...args) {
2882
+ handleLog("success", colors.success, console.log, m, m2, ...args);
2883
+ },
2884
+ debug(m, m2, ...args) {
2885
+ handleLog("debug", colors.debug, console.log, m, m2, ...args);
2886
+ }
2887
+ };
2888
+
2889
+ // src/wallet/types/index.ts
2890
+ var WalletError = class extends Error {
2891
+ constructor(message, code, context) {
2892
+ super(message);
2893
+ this.code = code;
2894
+ this.context = context;
2895
+ this.name = "WalletError";
2896
+ }
2897
+ };
2898
+ var SessionKeyError = class extends WalletError {
2899
+ constructor(message, context) {
2900
+ super(message, "SESSION_KEY_ERROR", context);
2901
+ this.name = "SessionKeyError";
2902
+ }
2903
+ };
2904
+ var BatcherError = class extends WalletError {
2905
+ constructor(message, context) {
2906
+ super(message, "BATCHER_ERROR", context);
2907
+ this.name = "BatcherError";
2908
+ }
2909
+ };
2910
+ var EmbeddedWalletError = class extends WalletError {
2911
+ constructor(message, context) {
2912
+ super(message, "EMBEDDED_WALLET_ERROR", context);
2913
+ this.name = "EmbeddedWalletError";
2914
+ }
2915
+ };
2916
+
2917
+ // src/wallet/batching/batcher.ts
2918
+ var TransactionBatcher = class {
2919
+ coreBatch = [];
2920
+ evmBatch = [];
2921
+ autoExecuteTimer = null;
2922
+ options;
2923
+ constructor(options = {}) {
2924
+ this.options = {
2925
+ maxBatchSize: options.maxBatchSize || 10,
2926
+ autoExecuteTimeout: options.autoExecuteTimeout || 0,
2927
+ // 0 = disabled
2928
+ minGasPrice: options.minGasPrice || 0n
2929
+ };
2930
+ }
2931
+ /**
2932
+ * Add transaction to batch
2933
+ *
2934
+ * @param tx - Transaction to add
2935
+ * @returns Transaction ID
2936
+ */
2937
+ addTransaction(tx) {
2938
+ const transaction = {
2939
+ id: `tx_${Date.now()}_${Math.random().toString(36).substring(7)}`,
2940
+ ...tx,
2941
+ addedAt: /* @__PURE__ */ new Date()
2942
+ };
2943
+ const batch = tx.chain === "core" ? this.coreBatch : this.evmBatch;
2944
+ batch.push(transaction);
2945
+ if (this.options.autoExecuteTimeout > 0 && batch.length === 1) {
2946
+ this.startAutoExecuteTimer(tx.chain);
2947
+ }
2948
+ if (batch.length >= this.options.maxBatchSize) {
2949
+ console.log(
2950
+ `Batch for ${tx.chain} is full (${batch.length} transactions)`
2951
+ );
2952
+ }
2953
+ return transaction.id;
2954
+ }
2955
+ /**
2956
+ * Remove transaction from batch
2957
+ *
2958
+ * @param transactionId - Transaction ID
2959
+ * @param chain - Chain type
2960
+ * @returns true if removed, false if not found
2961
+ */
2962
+ removeTransaction(transactionId, chain) {
2963
+ const batch = chain === "core" ? this.coreBatch : this.evmBatch;
2964
+ const index = batch.findIndex((tx) => tx.id === transactionId);
2965
+ if (index !== -1) {
2966
+ batch.splice(index, 1);
2967
+ return true;
2968
+ }
2969
+ return false;
2970
+ }
2971
+ /**
2972
+ * Get pending transactions for a chain
2973
+ *
2974
+ * @param chain - Chain type
2975
+ * @returns Array of pending transactions
2976
+ */
2977
+ getPendingTransactions(chain) {
2978
+ return chain === "core" ? [...this.coreBatch] : [...this.evmBatch];
2979
+ }
2980
+ /**
2981
+ * Get batch statistics
2982
+ *
2983
+ * @param chain - Chain type
2984
+ * @returns Batch statistics
2985
+ */
2986
+ getBatchStats(chain) {
2987
+ const batch = chain === "core" ? this.coreBatch : this.evmBatch;
2988
+ return {
2989
+ count: batch.length,
2990
+ totalValue: batch.reduce((sum, tx) => sum + (tx.value || 0n), 0n),
2991
+ avgGasLimit: batch.length > 0 ? batch.reduce((sum, tx) => sum + (tx.gasLimit || 0n), 0n) / BigInt(batch.length) : 0n,
2992
+ oldestTransaction: batch[0]?.addedAt
2993
+ };
2994
+ }
2995
+ /**
2996
+ * Execute batch of transactions
2997
+ *
2998
+ * Note: This is a simplified implementation. In production, you would:
2999
+ * - Use multicall contracts for actual batching
3000
+ * - Handle gas estimation
3001
+ * - Implement retry logic
3002
+ * - Support different batching strategies (sequential, parallel, etc.)
3003
+ *
3004
+ * @param chain - Chain to execute on
3005
+ * @param signer - Function to sign and send transactions
3006
+ * @returns Batch execution result
3007
+ */
3008
+ async executeBatch(chain, signer) {
3009
+ const batch = chain === "core" ? this.coreBatch : this.evmBatch;
3010
+ if (batch.length === 0) {
3011
+ throw new BatcherError("No transactions in batch", { chain });
3012
+ }
3013
+ this.stopAutoExecuteTimer();
3014
+ const batchId = `batch_${Date.now()}_${Math.random().toString(36).substring(7)}`;
3015
+ const transactionHashes = [];
3016
+ let successCount = 0;
3017
+ let failureCount = 0;
3018
+ let totalGasUsed = 0n;
3019
+ for (const tx of batch) {
3020
+ try {
3021
+ if (signer) {
3022
+ const hash = await signer(tx);
3023
+ transactionHashes.push(hash);
3024
+ successCount++;
3025
+ totalGasUsed += tx.gasLimit || 21000n;
3026
+ } else {
3027
+ const hash = `0x${Array.from(
3028
+ { length: 64 },
3029
+ () => Math.floor(Math.random() * 16).toString(16)
3030
+ ).join("")}`;
3031
+ transactionHashes.push(hash);
3032
+ successCount++;
3033
+ totalGasUsed += tx.gasLimit || 21000n;
3034
+ }
3035
+ } catch (error) {
3036
+ failureCount++;
3037
+ console.error(`Transaction ${tx.id} failed:`, error);
3038
+ }
3039
+ }
3040
+ if (chain === "core") {
3041
+ this.coreBatch = [];
3042
+ } else {
3043
+ this.evmBatch = [];
3044
+ }
3045
+ return {
3046
+ batchId,
3047
+ transactionHashes,
3048
+ successCount,
3049
+ failureCount,
3050
+ executedAt: /* @__PURE__ */ new Date(),
3051
+ totalGasUsed,
3052
+ chain
3053
+ };
3054
+ }
3055
+ /**
3056
+ * Clear all pending transactions
3057
+ *
3058
+ * @param chain - Chain to clear, or undefined to clear both
3059
+ */
3060
+ clearBatch(chain) {
3061
+ if (chain === "core" || chain === void 0) {
3062
+ this.coreBatch = [];
3063
+ }
3064
+ if (chain === "evm" || chain === void 0) {
3065
+ this.evmBatch = [];
3066
+ }
3067
+ this.stopAutoExecuteTimer();
3068
+ }
3069
+ /**
3070
+ * Start auto-execute timer
3071
+ */
3072
+ startAutoExecuteTimer(chain) {
3073
+ if (this.options.autoExecuteTimeout <= 0) return;
3074
+ this.stopAutoExecuteTimer();
3075
+ this.autoExecuteTimer = setTimeout(() => {
3076
+ const batch = chain === "core" ? this.coreBatch : this.evmBatch;
3077
+ if (batch.length > 0) {
3078
+ console.log(
3079
+ `Auto-executing batch for ${chain} (${batch.length} transactions)`
3080
+ );
3081
+ }
3082
+ }, this.options.autoExecuteTimeout);
3083
+ }
3084
+ /**
3085
+ * Stop auto-execute timer
3086
+ */
3087
+ stopAutoExecuteTimer() {
3088
+ if (this.autoExecuteTimer) {
3089
+ clearTimeout(this.autoExecuteTimer);
3090
+ this.autoExecuteTimer = null;
3091
+ }
3092
+ }
3093
+ /**
3094
+ * Get batcher configuration
3095
+ */
3096
+ getOptions() {
3097
+ return { ...this.options };
3098
+ }
3099
+ /**
3100
+ * Update batcher configuration
3101
+ */
3102
+ updateOptions(options) {
3103
+ this.options = {
3104
+ ...this.options,
3105
+ ...options
3106
+ };
3107
+ if (options.autoExecuteTimeout !== void 0) {
3108
+ this.stopAutoExecuteTimer();
3109
+ if (this.coreBatch.length > 0) {
3110
+ this.startAutoExecuteTimer("core");
3111
+ }
3112
+ if (this.evmBatch.length > 0) {
3113
+ this.startAutoExecuteTimer("evm");
3114
+ }
3115
+ }
3116
+ }
3117
+ };
3118
+
3119
+ // src/wallet/derivation.ts
3120
+ import { HDKey } from "@scure/bip32";
3121
+ import {
3122
+ generateMnemonic as generateBip39Mnemonic,
3123
+ mnemonicToSeedSync,
3124
+ validateMnemonic as validateBip39Mnemonic
3125
+ } from "@scure/bip39";
3126
+ import { wordlist } from "@scure/bip39/wordlists/english.js";
3127
+ import { privateKeyToAccount as civePrivateKeyToAccount } from "cive/accounts";
3128
+ import { bytesToHex } from "viem";
3129
+ import { privateKeyToAccount as viemPrivateKeyToAccount } from "viem/accounts";
3130
+
3131
+ // src/wallet/types.ts
3132
+ var COIN_TYPES = {
3133
+ /** Conflux Core Space - registered coin type 503 */
3134
+ CONFLUX: 503,
3135
+ /** Ethereum/eSpace - standard coin type 60 */
3136
+ ETHEREUM: 60
3137
+ };
3138
+ var CORE_NETWORK_IDS = {
3139
+ /** Local development network */
3140
+ LOCAL: 2029,
3141
+ /** Testnet */
3142
+ TESTNET: 1,
3143
+ /** Mainnet */
3144
+ MAINNET: 1029
3145
+ };
3146
+
3147
+ // src/wallet/derivation.ts
3148
+ function generateMnemonic(strength = 128) {
3149
+ return generateBip39Mnemonic(wordlist, strength);
3150
+ }
3151
+ function validateMnemonic(mnemonic) {
3152
+ const normalizedMnemonic = mnemonic.trim().toLowerCase();
3153
+ const words = normalizedMnemonic.split(/\s+/);
3154
+ const wordCount = words.length;
3155
+ if (wordCount !== 12 && wordCount !== 24) {
3156
+ return {
3157
+ valid: false,
3158
+ wordCount,
3159
+ error: `Invalid word count: ${wordCount}. Must be 12 or 24.`
3160
+ };
3161
+ }
3162
+ const valid = validateBip39Mnemonic(normalizedMnemonic, wordlist);
3163
+ return {
3164
+ valid,
3165
+ wordCount,
3166
+ error: valid ? void 0 : "Invalid mnemonic: checksum verification failed"
3167
+ };
3168
+ }
3169
+ function deriveAccounts(mnemonic, options) {
3170
+ const {
3171
+ count,
3172
+ startIndex = 0,
3173
+ coreNetworkId = CORE_NETWORK_IDS.LOCAL,
3174
+ accountType = "standard"
3175
+ } = options;
3176
+ const validation = validateMnemonic(mnemonic);
3177
+ if (!validation.valid) {
3178
+ throw new Error(`Invalid mnemonic: ${validation.error}`);
3179
+ }
3180
+ const normalizedMnemonic = mnemonic.trim().toLowerCase();
3181
+ const seed = mnemonicToSeedSync(normalizedMnemonic);
3182
+ const masterKey = HDKey.fromMasterSeed(seed);
3183
+ const accounts = [];
3184
+ const accountTypeIndex = accountType === "standard" ? 0 : 1;
3185
+ for (let i = startIndex; i < startIndex + count; i++) {
3186
+ const corePath = `m/44'/${COIN_TYPES.CONFLUX}'/${accountTypeIndex}'/0/${i}`;
3187
+ const coreKey = masterKey.derive(corePath);
3188
+ const evmPath = `m/44'/${COIN_TYPES.ETHEREUM}'/${accountTypeIndex}'/0/${i}`;
3189
+ const evmKey = masterKey.derive(evmPath);
3190
+ if (!coreKey.privateKey || !evmKey.privateKey) {
3191
+ throw new Error(`Failed to derive keys at index ${i}`);
3192
+ }
3193
+ const corePrivateKey = bytesToHex(coreKey.privateKey);
3194
+ const evmPrivateKey = bytesToHex(evmKey.privateKey);
3195
+ const coreAccount = civePrivateKeyToAccount(corePrivateKey, {
3196
+ networkId: coreNetworkId
3197
+ });
3198
+ const evmAccount = viemPrivateKeyToAccount(evmPrivateKey);
3199
+ accounts.push({
3200
+ index: i,
3201
+ coreAddress: coreAccount.address,
3202
+ evmAddress: evmAccount.address,
3203
+ corePrivateKey,
3204
+ evmPrivateKey,
3205
+ paths: {
3206
+ core: corePath,
3207
+ evm: evmPath
3208
+ }
3209
+ });
3210
+ }
3211
+ return accounts;
3212
+ }
3213
+ function deriveAccount(mnemonic, index, coreNetworkId = CORE_NETWORK_IDS.LOCAL, accountType = "standard") {
3214
+ const accounts = deriveAccounts(mnemonic, {
3215
+ count: 1,
3216
+ startIndex: index,
3217
+ coreNetworkId,
3218
+ accountType
3219
+ });
3220
+ return accounts[0];
3221
+ }
3222
+ function deriveFaucetAccount(mnemonic, coreNetworkId = CORE_NETWORK_IDS.LOCAL) {
3223
+ return deriveAccount(mnemonic, 0, coreNetworkId, "mining");
3224
+ }
3225
+
3226
+ // src/wallet/embedded/custody.ts
3227
+ import { generatePrivateKey, privateKeyToAccount as privateKeyToAccount3 } from "viem/accounts";
3228
+ var EmbeddedWalletManager = class {
3229
+ wallets = /* @__PURE__ */ new Map();
3230
+ options;
3231
+ constructor(options = {}) {
3232
+ this.options = {
3233
+ algorithm: options.algorithm || "aes-256-gcm",
3234
+ iterations: options.iterations || 1e5,
3235
+ autoCreate: options.autoCreate !== false
3236
+ // Default true
3237
+ };
3238
+ }
3239
+ /**
3240
+ * Create a new embedded wallet for a user
3241
+ *
3242
+ * @param userId - User identifier
3243
+ * @param password - Encryption password
3244
+ * @returns Created wallet (without private key)
3245
+ */
3246
+ async createWallet(userId, password) {
3247
+ if (this.wallets.has(userId)) {
3248
+ throw new EmbeddedWalletError("Wallet already exists", { userId });
3249
+ }
3250
+ const privateKey = generatePrivateKey();
3251
+ const account = privateKeyToAccount3(privateKey);
3252
+ const { encrypted, iv, salt } = await this.encryptPrivateKey(
3253
+ privateKey,
3254
+ password
3255
+ );
3256
+ const evmAddress = account.address;
3257
+ const coreAddress = `cfx:${evmAddress.slice(2)}`;
3258
+ const wallet = {
3259
+ userId,
3260
+ coreAddress,
3261
+ evmAddress,
3262
+ encryptedPrivateKey: encrypted,
3263
+ encryption: {
3264
+ algorithm: this.options.algorithm,
3265
+ iv,
3266
+ salt
3267
+ },
3268
+ createdAt: /* @__PURE__ */ new Date(),
3269
+ lastAccessedAt: /* @__PURE__ */ new Date(),
3270
+ isActive: true
3271
+ };
3272
+ this.wallets.set(userId, wallet);
3273
+ const { encryptedPrivateKey: _, ...publicWallet } = wallet;
3274
+ return publicWallet;
3275
+ }
3276
+ /**
3277
+ * Get wallet info (without private key)
3278
+ *
3279
+ * @param userId - User identifier
3280
+ * @returns Wallet info or undefined
3281
+ */
3282
+ getWallet(userId) {
3283
+ const wallet = this.wallets.get(userId);
3284
+ if (!wallet) return void 0;
3285
+ wallet.lastAccessedAt = /* @__PURE__ */ new Date();
3286
+ const { encryptedPrivateKey: _, ...publicWallet } = wallet;
3287
+ return publicWallet;
3288
+ }
3289
+ /**
3290
+ * Check if user has a wallet
3291
+ *
3292
+ * @param userId - User identifier
3293
+ * @returns true if wallet exists
3294
+ */
3295
+ hasWallet(userId) {
3296
+ return this.wallets.has(userId);
3297
+ }
3298
+ /**
3299
+ * Sign transaction with user's embedded wallet
3300
+ *
3301
+ * @param userId - User identifier
3302
+ * @param password - Decryption password
3303
+ * @param request - Transaction request
3304
+ * @returns Signed transaction
3305
+ */
3306
+ async signTransaction(userId, password, request) {
3307
+ const wallet = this.wallets.get(userId);
3308
+ if (!wallet) {
3309
+ throw new EmbeddedWalletError("Wallet not found", { userId });
3310
+ }
3311
+ if (!wallet.isActive) {
3312
+ throw new EmbeddedWalletError("Wallet is not active", { userId });
3313
+ }
3314
+ const privateKey = await this.decryptPrivateKey(
3315
+ wallet.encryptedPrivateKey,
3316
+ password,
3317
+ wallet.encryption.iv,
3318
+ wallet.encryption.salt
3319
+ );
3320
+ const account = privateKeyToAccount3(privateKey);
3321
+ wallet.lastAccessedAt = /* @__PURE__ */ new Date();
3322
+ const serialized = JSON.stringify({
3323
+ from: account.address,
3324
+ ...request,
3325
+ value: request.value?.toString(),
3326
+ gasLimit: request.gasLimit?.toString(),
3327
+ gasPrice: request.gasPrice?.toString()
3328
+ });
3329
+ const signature = await account.signMessage({
3330
+ message: serialized
3331
+ });
3332
+ return {
3333
+ rawTransaction: signature,
3334
+ hash: `0x${Array.from(
3335
+ { length: 64 },
3336
+ () => Math.floor(Math.random() * 16).toString(16)
3337
+ ).join("")}`,
3338
+ from: account.address,
3339
+ chain: request.chain
3340
+ };
3341
+ }
3342
+ /**
3343
+ * Export wallet for user backup
3344
+ *
3345
+ * @param userId - User identifier
3346
+ * @param password - Encryption password
3347
+ * @returns Encrypted wallet export
3348
+ */
3349
+ async exportWallet(userId, password) {
3350
+ const wallet = this.wallets.get(userId);
3351
+ if (!wallet) {
3352
+ throw new EmbeddedWalletError("Wallet not found", { userId });
3353
+ }
3354
+ const exportData = JSON.stringify({
3355
+ coreAddress: wallet.coreAddress,
3356
+ evmAddress: wallet.evmAddress,
3357
+ encryptedPrivateKey: wallet.encryptedPrivateKey,
3358
+ encryption: wallet.encryption
3359
+ });
3360
+ const { encrypted, iv, salt } = await this.encryptPrivateKey(
3361
+ exportData,
3362
+ password
3363
+ );
3364
+ return {
3365
+ userId,
3366
+ encryptedData: encrypted,
3367
+ encryption: {
3368
+ algorithm: this.options.algorithm,
3369
+ iv,
3370
+ salt
3371
+ },
3372
+ exportedAt: /* @__PURE__ */ new Date()
3373
+ };
3374
+ }
3375
+ /**
3376
+ * Deactivate wallet
3377
+ *
3378
+ * @param userId - User identifier
3379
+ */
3380
+ deactivateWallet(userId) {
3381
+ const wallet = this.wallets.get(userId);
3382
+ if (wallet) {
3383
+ wallet.isActive = false;
3384
+ }
3385
+ }
3386
+ /**
3387
+ * Delete wallet permanently
3388
+ *
3389
+ * WARNING: This operation cannot be undone
3390
+ *
3391
+ * @param userId - User identifier
3392
+ * @returns true if deleted, false if not found
3393
+ */
3394
+ deleteWallet(userId) {
3395
+ return this.wallets.delete(userId);
3396
+ }
3397
+ /**
3398
+ * List all wallets (without private keys)
3399
+ *
3400
+ * @returns Array of wallet info
3401
+ */
3402
+ listWallets() {
3403
+ return Array.from(this.wallets.values()).map((wallet) => {
3404
+ const { encryptedPrivateKey: _, ...publicWallet } = wallet;
3405
+ return publicWallet;
3406
+ });
3407
+ }
3408
+ /**
3409
+ * Get wallet statistics
3410
+ *
3411
+ * @returns Wallet statistics
3412
+ */
3413
+ getStats() {
3414
+ const all = Array.from(this.wallets.values());
3415
+ const active = all.filter((w) => w.isActive);
3416
+ return {
3417
+ total: all.length,
3418
+ active: active.length,
3419
+ inactive: all.length - active.length
3420
+ };
3421
+ }
3422
+ /**
3423
+ * Encrypt private key
3424
+ *
3425
+ * NOTE: Simplified implementation for demonstration
3426
+ * Production should use proper encryption (node:crypto, @noble/ciphers, etc.)
3427
+ */
3428
+ async encryptPrivateKey(data, password) {
3429
+ const iv = Array.from(
3430
+ { length: 16 },
3431
+ () => Math.floor(Math.random() * 256).toString(16).padStart(2, "0")
3432
+ ).join("");
3433
+ const salt = Array.from(
3434
+ { length: 32 },
3435
+ () => Math.floor(Math.random() * 256).toString(16).padStart(2, "0")
3436
+ ).join("");
3437
+ const mockEncrypted = Buffer.from(
3438
+ JSON.stringify({ data, password, iv, salt })
3439
+ ).toString("base64");
3440
+ return {
3441
+ encrypted: mockEncrypted,
3442
+ iv,
3443
+ salt
3444
+ };
3445
+ }
3446
+ /**
3447
+ * Decrypt private key
3448
+ *
3449
+ * NOTE: Simplified implementation for demonstration
3450
+ */
3451
+ async decryptPrivateKey(encrypted, password, _iv, _salt) {
3452
+ try {
3453
+ const decoded = JSON.parse(
3454
+ Buffer.from(encrypted, "base64").toString("utf-8")
3455
+ );
3456
+ if (decoded.password !== password) {
3457
+ throw new Error("Invalid password");
3458
+ }
3459
+ return decoded.data;
3460
+ } catch (error) {
3461
+ throw new EmbeddedWalletError("Failed to decrypt private key", {
3462
+ error: error instanceof Error ? error.message : "Unknown error"
3463
+ });
3464
+ }
3465
+ }
3466
+ };
3467
+
3468
+ // src/wallet/session-keys/manager.ts
3469
+ import { privateKeyToAccount as privateKeyToAccount4 } from "viem/accounts";
3470
+ var SessionKeyManager = class {
3471
+ sessionKeys = /* @__PURE__ */ new Map();
3472
+ /**
3473
+ * Generate a new session key
3474
+ *
3475
+ * @param parentAddress - Parent wallet address
3476
+ * @param options - Session key configuration
3477
+ * @returns Created session key
3478
+ */
3479
+ generateSessionKey(parentAddress, options) {
3480
+ const privateKey = `0x${Array.from(
3481
+ { length: 64 },
3482
+ () => Math.floor(Math.random() * 16).toString(16)
3483
+ ).join("")}`;
3484
+ const account = privateKeyToAccount4(privateKey);
3485
+ const ttl = options.ttl || 3600;
3486
+ const now = /* @__PURE__ */ new Date();
3487
+ const expiresAt = new Date(now.getTime() + ttl * 1e3);
3488
+ const sessionKey = {
3489
+ id: `sk_${Date.now()}_${Math.random().toString(36).substring(7)}`,
3490
+ privateKey,
3491
+ address: account.address,
3492
+ parentAddress,
3493
+ ttl,
3494
+ expiresAt,
3495
+ permissions: options.permissions || {},
3496
+ createdAt: now,
3497
+ isActive: true,
3498
+ chain: options.chain
3499
+ };
3500
+ this.sessionKeys.set(sessionKey.id, sessionKey);
3501
+ return sessionKey;
3502
+ }
3503
+ /**
3504
+ * Get session key by ID
3505
+ *
3506
+ * @param sessionKeyId - Session key identifier
3507
+ * @returns Session key or undefined
3508
+ */
3509
+ getSessionKey(sessionKeyId) {
3510
+ const sessionKey = this.sessionKeys.get(sessionKeyId);
3511
+ if (sessionKey && /* @__PURE__ */ new Date() > sessionKey.expiresAt) {
3512
+ sessionKey.isActive = false;
3513
+ }
3514
+ return sessionKey;
3515
+ }
3516
+ /**
3517
+ * Revoke a session key
3518
+ *
3519
+ * @param sessionKeyId - Session key identifier
3520
+ */
3521
+ revokeSessionKey(sessionKeyId) {
3522
+ const sessionKey = this.sessionKeys.get(sessionKeyId);
3523
+ if (sessionKey) {
3524
+ sessionKey.isActive = false;
3525
+ }
3526
+ }
3527
+ /**
3528
+ * List all session keys for a parent address
3529
+ *
3530
+ * @param parentAddress - Parent wallet address
3531
+ * @returns Array of session keys
3532
+ */
3533
+ listSessionKeys(parentAddress) {
3534
+ return Array.from(this.sessionKeys.values()).filter(
3535
+ (sk) => sk.parentAddress.toLowerCase() === parentAddress.toLowerCase()
3536
+ );
3537
+ }
3538
+ /**
3539
+ * List active session keys for a parent address
3540
+ *
3541
+ * @param parentAddress - Parent wallet address
3542
+ * @returns Array of active session keys
3543
+ */
3544
+ listActiveSessionKeys(parentAddress) {
3545
+ return this.listSessionKeys(parentAddress).filter(
3546
+ (sk) => sk.isActive && /* @__PURE__ */ new Date() <= sk.expiresAt
3547
+ );
3548
+ }
3549
+ /**
3550
+ * Validate transaction against session key permissions
3551
+ *
3552
+ * @param sessionKey - Session key
3553
+ * @param request - Transaction request
3554
+ * @throws SessionKeyError if validation fails
3555
+ */
3556
+ validateTransaction(sessionKey, request) {
3557
+ if (!sessionKey.isActive) {
3558
+ throw new SessionKeyError("Session key is not active", {
3559
+ sessionKeyId: sessionKey.id
3560
+ });
3561
+ }
3562
+ if (/* @__PURE__ */ new Date() > sessionKey.expiresAt) {
3563
+ throw new SessionKeyError("Session key has expired", {
3564
+ sessionKeyId: sessionKey.id,
3565
+ expiresAt: sessionKey.expiresAt
3566
+ });
3567
+ }
3568
+ if (request.chain !== sessionKey.chain) {
3569
+ throw new SessionKeyError("Chain mismatch", {
3570
+ sessionKeyId: sessionKey.id,
3571
+ allowedChain: sessionKey.chain,
3572
+ requestedChain: request.chain
3573
+ });
3574
+ }
3575
+ const { permissions } = sessionKey;
3576
+ if (permissions.maxValue && request.value && request.value > permissions.maxValue) {
3577
+ throw new SessionKeyError("Transaction value exceeds maximum", {
3578
+ sessionKeyId: sessionKey.id,
3579
+ maxValue: permissions.maxValue.toString(),
3580
+ requestedValue: request.value.toString()
3581
+ });
3582
+ }
3583
+ if (permissions.contracts && permissions.contracts.length > 0) {
3584
+ const isWhitelisted = permissions.contracts.some(
3585
+ (addr) => addr.toLowerCase() === request.to.toLowerCase()
3586
+ );
3587
+ if (!isWhitelisted) {
3588
+ throw new SessionKeyError("Contract not whitelisted", {
3589
+ sessionKeyId: sessionKey.id,
3590
+ whitelistedContracts: permissions.contracts,
3591
+ requestedContract: request.to
3592
+ });
3593
+ }
3594
+ }
3595
+ if (permissions.operations && permissions.operations.length > 0 && request.data) {
3596
+ const selector = request.data.slice(0, 10);
3597
+ const isAllowed = permissions.operations.some(
3598
+ (op) => op.toLowerCase() === selector.toLowerCase()
3599
+ );
3600
+ if (!isAllowed) {
3601
+ throw new SessionKeyError("Operation not allowed", {
3602
+ sessionKeyId: sessionKey.id,
3603
+ allowedOperations: permissions.operations,
3604
+ requestedOperation: selector
3605
+ });
3606
+ }
3607
+ }
3608
+ }
3609
+ /**
3610
+ * Sign transaction with session key
3611
+ *
3612
+ * @param sessionKeyId - Session key identifier
3613
+ * @param request - Transaction request
3614
+ * @returns Signed transaction
3615
+ * @throws SessionKeyError if session key is invalid or transaction violates permissions
3616
+ */
3617
+ async signWithSessionKey(sessionKeyId, request) {
3618
+ const sessionKey = this.sessionKeys.get(sessionKeyId);
3619
+ if (!sessionKey) {
3620
+ throw new SessionKeyError("Session key not found", { sessionKeyId });
3621
+ }
3622
+ this.validateTransaction(sessionKey, request);
3623
+ const account = privateKeyToAccount4(sessionKey.privateKey);
3624
+ const serialized = JSON.stringify({
3625
+ from: account.address,
3626
+ to: request.to,
3627
+ value: request.value?.toString(),
3628
+ data: request.data,
3629
+ gasLimit: request.gasLimit?.toString(),
3630
+ gasPrice: request.gasPrice?.toString(),
3631
+ nonce: request.nonce,
3632
+ chain: request.chain
3633
+ });
3634
+ const signature = await account.signMessage({
3635
+ message: serialized
3636
+ });
3637
+ return {
3638
+ rawTransaction: signature,
3639
+ hash: `0x${Array.from(
3640
+ { length: 64 },
3641
+ () => Math.floor(Math.random() * 16).toString(16)
3642
+ ).join("")}`,
3643
+ from: account.address,
3644
+ chain: request.chain
3645
+ };
3646
+ }
3647
+ /**
3648
+ * Clean up expired session keys
3649
+ *
3650
+ * @returns Number of removed session keys
3651
+ */
3652
+ cleanupExpired() {
3653
+ const now = /* @__PURE__ */ new Date();
3654
+ let removed = 0;
3655
+ for (const [id, sessionKey] of this.sessionKeys.entries()) {
3656
+ if (now > sessionKey.expiresAt) {
3657
+ this.sessionKeys.delete(id);
3658
+ removed++;
3659
+ }
3660
+ }
3661
+ return removed;
3662
+ }
3663
+ /**
3664
+ * Get session key statistics
3665
+ *
3666
+ * @returns Statistics about session keys
3667
+ */
3668
+ getStats() {
3669
+ const all = Array.from(this.sessionKeys.values());
3670
+ const active = all.filter(
3671
+ (sk) => sk.isActive && /* @__PURE__ */ new Date() <= sk.expiresAt
3672
+ );
3673
+ const expired = all.filter((sk) => /* @__PURE__ */ new Date() > sk.expiresAt);
3674
+ return {
3675
+ total: all.length,
3676
+ active: active.length,
3677
+ expired: expired.length,
3678
+ inactive: all.length - active.length - expired.length
3679
+ };
3680
+ }
3681
+ };
3682
+ export {
3683
+ BatcherError,
3684
+ CORE_LOCAL,
3685
+ CORE_MAINNET,
3686
+ CORE_TESTNET,
3687
+ ClientManager,
3688
+ ContractDeployer,
3689
+ ContractError,
3690
+ ContractReader,
3691
+ ContractWriter,
3692
+ CoreClient,
3693
+ CoreTestClient,
3694
+ CoreWalletClient,
3695
+ DeploymentError,
3696
+ ERC1155_ABI,
3697
+ ERC20_ABI,
3698
+ ERC721_ABI,
3699
+ EVM_LOCAL,
3700
+ EVM_MAINNET,
3701
+ EVM_TESTNET,
3702
+ EmbeddedWalletError,
3703
+ EmbeddedWalletManager,
3704
+ EspaceClient,
3705
+ EspaceTestClient,
3706
+ EspaceWalletClient,
3707
+ InteractionError,
3708
+ SessionKeyError,
3709
+ SessionKeyManager,
3710
+ TransactionBatcher,
3711
+ WalletError,
3712
+ defaultNetworkSelector,
3713
+ deriveAccount,
3714
+ deriveAccounts,
3715
+ deriveFaucetAccount,
3716
+ formatCFX2 as formatCFX,
3717
+ formatUnits2 as formatUnits,
3718
+ generateMnemonic,
3719
+ getChainConfig,
3720
+ getCoreChains,
3721
+ getEvmChains,
3722
+ getMainnetChains,
3723
+ isAddress as isCoreAddress,
3724
+ isAddress2 as isEspaceAddress,
3725
+ logger,
3726
+ parseCFX2 as parseCFX,
3727
+ parseUnits,
3728
+ validateMnemonic
3729
+ };
3730
+ //# sourceMappingURL=index.js.map