agentvault 1.0.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 (188) hide show
  1. package/.dfx/local/network-id +4 -0
  2. package/.next/trace +2 -0
  3. package/.vercel/README.txt +11 -0
  4. package/.vercel/project.json +1 -0
  5. package/AGENTS.md +43 -0
  6. package/CHANGELOG.md +196 -0
  7. package/LICENSE +21 -0
  8. package/PLAN_VAULT_INTEGRATION.md +318 -0
  9. package/README.md +253 -0
  10. package/backups/agentvault-backup-test-agent-2026-02-12T17-54-28-967Z.json +28 -0
  11. package/backups/agentvault-backup-test-agent-2026-02-12T17-54-29-032Z.backup +1 -0
  12. package/backups/agentvault-backup-test-agent-2026-02-12T17-57-42-373Z.json +28 -0
  13. package/backups/agentvault-backup-test-agent-2026-02-12T17-57-42-428Z.backup +1 -0
  14. package/backups/agentvault-backup-test-agent-2026-02-12T18-52-25-132Z.json +28 -0
  15. package/backups/agentvault-backup-test-agent-2026-02-12T18-52-25-247Z.backup +1 -0
  16. package/backups/agentvault-backup-test-agent-2026-02-12T18-54-09-216Z.json +28 -0
  17. package/backups/agentvault-backup-test-agent-2026-02-12T18-54-09-283Z.backup +1 -0
  18. package/backups/agentvault-backup-test-agent-2026-02-12T22-18-22-772Z.backup +1 -0
  19. package/backups/agentvault-backup-test-agent-2026-02-12T22-18-22-793Z.json +28 -0
  20. package/backups/test-backup.json +28 -0
  21. package/dist/cli/commands/approve.d.ts +4 -0
  22. package/dist/cli/commands/approve.js +232 -0
  23. package/dist/cli/commands/archive.d.ts +4 -0
  24. package/dist/cli/commands/archive.js +192 -0
  25. package/dist/cli/commands/backup.d.ts +4 -0
  26. package/dist/cli/commands/backup.js +164 -0
  27. package/dist/cli/commands/cloud-backup.d.ts +4 -0
  28. package/dist/cli/commands/cloud-backup.js +221 -0
  29. package/dist/cli/commands/cycles.d.ts +8 -0
  30. package/dist/cli/commands/cycles.js +83 -0
  31. package/dist/cli/commands/decrypt.d.ts +16 -0
  32. package/dist/cli/commands/decrypt.js +101 -0
  33. package/dist/cli/commands/deploy.d.ts +32 -0
  34. package/dist/cli/commands/deploy.js +208 -0
  35. package/dist/cli/commands/exec.d.ts +26 -0
  36. package/dist/cli/commands/exec.js +109 -0
  37. package/dist/cli/commands/fetch.d.ts +23 -0
  38. package/dist/cli/commands/fetch.js +164 -0
  39. package/dist/cli/commands/health.d.ts +8 -0
  40. package/dist/cli/commands/health.js +72 -0
  41. package/dist/cli/commands/identity.d.ts +8 -0
  42. package/dist/cli/commands/identity.js +140 -0
  43. package/dist/cli/commands/inference.d.ts +4 -0
  44. package/dist/cli/commands/inference.js +225 -0
  45. package/dist/cli/commands/info.d.ts +8 -0
  46. package/dist/cli/commands/info.js +59 -0
  47. package/dist/cli/commands/init.d.ts +19 -0
  48. package/dist/cli/commands/init.js +135 -0
  49. package/dist/cli/commands/instrument.d.ts +8 -0
  50. package/dist/cli/commands/instrument.js +35 -0
  51. package/dist/cli/commands/list.d.ts +36 -0
  52. package/dist/cli/commands/list.js +173 -0
  53. package/dist/cli/commands/logs.d.ts +8 -0
  54. package/dist/cli/commands/logs.js +96 -0
  55. package/dist/cli/commands/monitor.d.ts +8 -0
  56. package/dist/cli/commands/monitor.js +84 -0
  57. package/dist/cli/commands/network.d.ts +14 -0
  58. package/dist/cli/commands/network.js +258 -0
  59. package/dist/cli/commands/package.d.ts +36 -0
  60. package/dist/cli/commands/package.js +188 -0
  61. package/dist/cli/commands/profile.d.ts +8 -0
  62. package/dist/cli/commands/profile.js +76 -0
  63. package/dist/cli/commands/promote.d.ts +8 -0
  64. package/dist/cli/commands/promote.js +89 -0
  65. package/dist/cli/commands/rebuild.d.ts +21 -0
  66. package/dist/cli/commands/rebuild.js +140 -0
  67. package/dist/cli/commands/rollback.d.ts +8 -0
  68. package/dist/cli/commands/rollback.js +120 -0
  69. package/dist/cli/commands/show.d.ts +36 -0
  70. package/dist/cli/commands/show.js +200 -0
  71. package/dist/cli/commands/stats.d.ts +8 -0
  72. package/dist/cli/commands/stats.js +34 -0
  73. package/dist/cli/commands/status.d.ts +14 -0
  74. package/dist/cli/commands/status.js +83 -0
  75. package/dist/cli/commands/test.d.ts +8 -0
  76. package/dist/cli/commands/test.js +109 -0
  77. package/dist/cli/commands/tokens.d.ts +8 -0
  78. package/dist/cli/commands/tokens.js +62 -0
  79. package/dist/cli/commands/trace.d.ts +8 -0
  80. package/dist/cli/commands/trace.js +68 -0
  81. package/dist/cli/commands/wallet-export.d.ts +13 -0
  82. package/dist/cli/commands/wallet-export.js +140 -0
  83. package/dist/cli/commands/wallet-history.d.ts +10 -0
  84. package/dist/cli/commands/wallet-history.js +127 -0
  85. package/dist/cli/commands/wallet-import.d.ts +10 -0
  86. package/dist/cli/commands/wallet-import.js +209 -0
  87. package/dist/cli/commands/wallet-multi-send.d.ts +17 -0
  88. package/dist/cli/commands/wallet-multi-send.js +195 -0
  89. package/dist/cli/commands/wallet-process-queue.d.ts +19 -0
  90. package/dist/cli/commands/wallet-process-queue.js +209 -0
  91. package/dist/cli/commands/wallet-sign.d.ts +13 -0
  92. package/dist/cli/commands/wallet-sign.js +207 -0
  93. package/dist/cli/commands/wallet.d.ts +12 -0
  94. package/dist/cli/commands/wallet.js +794 -0
  95. package/dist/cli/index.d.ts +10 -0
  96. package/dist/cli/index.js +96 -0
  97. package/dist/vitest.config.d.ts +3 -0
  98. package/dist/vitest.config.js +14 -0
  99. package/fixup_1_0_OSS_release.md +136 -0
  100. package/fixup_REALEASE_PRD.md +136 -0
  101. package/package.json +79 -0
  102. package/pnpm-workspace.yaml +5 -0
  103. package/scripts/dev-dashboard.mjs +84 -0
  104. package/site/README.md +63 -0
  105. package/site/docusaurus.config.ts +148 -0
  106. package/site/package-lock.json +18383 -0
  107. package/site/package.json +47 -0
  108. package/site/sidebars.ts +86 -0
  109. package/site/static/.gitkeep +0 -0
  110. package/site/static/img/logo.svg +28 -0
  111. package/site/static/img/og-image.svg +35 -0
  112. package/src/archival/archive-manager.ts +372 -0
  113. package/src/archival/arweave-client.ts +289 -0
  114. package/src/archival/index.ts +8 -0
  115. package/src/backup/backup.ts +315 -0
  116. package/src/backup/index.ts +7 -0
  117. package/src/cloud-storage/cloud-sync.ts +461 -0
  118. package/src/cloud-storage/index.ts +11 -0
  119. package/src/cloud-storage/provider-detector.ts +198 -0
  120. package/src/cloud-storage/types.ts +104 -0
  121. package/src/debugging/index.ts +6 -0
  122. package/src/debugging/logs.ts +193 -0
  123. package/src/debugging/types.ts +100 -0
  124. package/src/deployment/deployer.ts +274 -0
  125. package/src/deployment/icpClient.ts +620 -0
  126. package/src/deployment/index.ts +46 -0
  127. package/src/deployment/promotion.ts +161 -0
  128. package/src/deployment/types.ts +111 -0
  129. package/src/icp/batch.ts +374 -0
  130. package/src/icp/cycles.ts +50 -0
  131. package/src/icp/environment.ts +215 -0
  132. package/src/icp/icpcli.ts +438 -0
  133. package/src/icp/icwasm.ts +222 -0
  134. package/src/icp/identity.ts +77 -0
  135. package/src/icp/index.ts +94 -0
  136. package/src/icp/optimization.ts +242 -0
  137. package/src/icp/tokens.ts +36 -0
  138. package/src/icp/tool-detector.ts +110 -0
  139. package/src/icp/types.ts +574 -0
  140. package/src/index.ts +25 -0
  141. package/src/inference/bittensor-client.ts +304 -0
  142. package/src/inference/index.ts +8 -0
  143. package/src/inference/inference-manager.ts +327 -0
  144. package/src/metrics/index.ts +7 -0
  145. package/src/metrics/metrics.ts +186 -0
  146. package/src/monitoring/alerting.ts +190 -0
  147. package/src/monitoring/health.ts +197 -0
  148. package/src/monitoring/index.ts +38 -0
  149. package/src/monitoring/info.ts +114 -0
  150. package/src/monitoring/types.ts +99 -0
  151. package/src/network/index.ts +5 -0
  152. package/src/network/network-config.ts +129 -0
  153. package/src/packaging/compiler.ts +647 -0
  154. package/src/packaging/config-persistence.ts +135 -0
  155. package/src/packaging/config-schemas.ts +156 -0
  156. package/src/packaging/detector.ts +220 -0
  157. package/src/packaging/index.ts +90 -0
  158. package/src/packaging/packager.ts +118 -0
  159. package/src/packaging/parsers/clawdbot.ts +278 -0
  160. package/src/packaging/parsers/cline.ts +223 -0
  161. package/src/packaging/parsers/generic.ts +266 -0
  162. package/src/packaging/parsers/goose.ts +214 -0
  163. package/src/packaging/parsers/index.ts +11 -0
  164. package/src/packaging/serializer.ts +260 -0
  165. package/src/packaging/types.ts +144 -0
  166. package/src/packaging/wasmedge-compiler.ts +406 -0
  167. package/src/security/index.ts +17 -0
  168. package/src/security/multisig.ts +415 -0
  169. package/src/security/types.ts +416 -0
  170. package/src/security/vetkeys.ts +655 -0
  171. package/src/testing/index.ts +6 -0
  172. package/src/testing/local-runner.ts +264 -0
  173. package/src/testing/types.ts +104 -0
  174. package/src/wallet/cbor-serializer.ts +323 -0
  175. package/src/wallet/chain-dispatcher.ts +313 -0
  176. package/src/wallet/cross-chain-aggregator.ts +346 -0
  177. package/src/wallet/index.ts +76 -0
  178. package/src/wallet/key-derivation.ts +425 -0
  179. package/src/wallet/providers/base-provider.ts +154 -0
  180. package/src/wallet/providers/cketh-provider.ts +434 -0
  181. package/src/wallet/providers/polkadot-provider.ts +503 -0
  182. package/src/wallet/providers/solana-provider.ts +490 -0
  183. package/src/wallet/transaction-queue.ts +284 -0
  184. package/src/wallet/types.ts +178 -0
  185. package/src/wallet/vetkeys-adapter.ts +431 -0
  186. package/src/wallet/wallet-manager.ts +597 -0
  187. package/src/wallet/wallet-storage.ts +380 -0
  188. package/vercel.json +8 -0
@@ -0,0 +1,434 @@
1
+ /**
2
+ * ckETH Wallet Provider
3
+ *
4
+ * Provider for Ethereum-compatible wallets on ICP ckETH.
5
+ * Supports balance queries, transactions, and signing.
6
+ */
7
+
8
+ import { ethers } from 'ethers';
9
+ import type {
10
+ Balance,
11
+ Transaction,
12
+ TransactionRequest,
13
+ SignedTransaction,
14
+ ProviderConfig,
15
+ } from '../types.js';
16
+ import { BaseWalletProvider } from './base-provider.js';
17
+
18
+ /**
19
+ * Environment variable names for RPC configuration
20
+ */
21
+ const ENV_RPC_URL = 'ETHEREUM_RPC_URL';
22
+ const ENV_SEPOLIA_RPC_URL = 'SEPOLIA_RPC_URL';
23
+ const ENV_INFURA_KEY = 'INFURA_API_KEY';
24
+
25
+ /**
26
+ * Public RPC endpoints (rate-limited, for fallback only)
27
+ */
28
+ const PUBLIC_RPC_URLS = {
29
+ mainnet: [
30
+ 'https://eth.llamarpc.com',
31
+ 'https://rpc.ankr.com/eth',
32
+ 'https://ethereum.publicnode.com',
33
+ ],
34
+ sepolia: [
35
+ 'https://rpc.sepolia.org',
36
+ 'https://ethereum-sepolia.publicnode.com',
37
+ 'https://rpc2.sepolia.org',
38
+ ],
39
+ };
40
+
41
+ /**
42
+ * Environment variable names for Etherscan configuration
43
+ */
44
+ const ENV_ETHERSCAN_API_KEY = 'ETHERSCAN_API_KEY';
45
+
46
+ /**
47
+ * ckETH provider configuration
48
+ */
49
+ interface CkEthConfig extends ProviderConfig {
50
+ /** Ethereum JSON-RPC endpoint */
51
+ rpcUrl: string;
52
+ /** Chain ID (1 = Mainnet, 5 = Goerli, 11155111 = Sepolia) */
53
+ chainId?: number;
54
+ /** Etherscan API key (optional, for transaction history) */
55
+ etherscanApiKey?: string;
56
+ }
57
+
58
+ /**
59
+ * ckETH wallet provider
60
+ */
61
+ export class CkEthProvider extends BaseWalletProvider {
62
+ private provider: ethers.JsonRpcProvider | null = null;
63
+ private chainId: number;
64
+ private etherscanApiKey: string | undefined;
65
+
66
+ constructor(config: CkEthConfig) {
67
+ super(config);
68
+ this.chainId = config.chainId ?? 1;
69
+ this.etherscanApiKey = config.etherscanApiKey;
70
+ }
71
+
72
+ /**
73
+ * Resolve the RPC URL to use for connections
74
+ *
75
+ * Priority:
76
+ * 1. Explicitly configured URL
77
+ * 2. Environment variable (ETHEREUM_RPC_URL or SEPOLIA_RPC_URL)
78
+ * 3. Infura with API key from environment
79
+ * 4. Public RPC endpoints (fallback, rate-limited)
80
+ *
81
+ * @returns RPC URL
82
+ */
83
+ private resolveRpcUrl(): string {
84
+ const configUrl = super.getRpcUrl();
85
+ if (configUrl && !configUrl.includes('YOUR-API-KEY')) {
86
+ return configUrl;
87
+ }
88
+
89
+ const isTestnet = this.chainId !== 1;
90
+ const envVar = isTestnet ? ENV_SEPOLIA_RPC_URL : ENV_RPC_URL;
91
+ const envUrl = process.env[envVar];
92
+
93
+ if (envUrl) {
94
+ return envUrl;
95
+ }
96
+
97
+ const infuraKey = process.env[ENV_INFURA_KEY];
98
+ if (infuraKey) {
99
+ return isTestnet
100
+ ? `https://sepolia.infura.io/v3/${infuraKey}`
101
+ : `https://mainnet.infura.io/v3/${infuraKey}`;
102
+ }
103
+
104
+ const publicUrls = isTestnet ? PUBLIC_RPC_URLS.sepolia : PUBLIC_RPC_URLS.mainnet;
105
+ const publicUrl = publicUrls[0];
106
+
107
+ if (!publicUrl) {
108
+ throw new Error(
109
+ `No RPC URL configured. Set ${envVar} environment variable or provide rpcUrl in config. ` +
110
+ `Example: export ${envVar}=https://eth.example.com/v3/your-api-key`
111
+ );
112
+ }
113
+
114
+ console.warn(
115
+ `Warning: Using public RPC endpoint (${publicUrl}). ` +
116
+ `For production, set ${envVar} environment variable for better reliability.`
117
+ );
118
+
119
+ return publicUrl;
120
+ }
121
+
122
+ /**
123
+ * Connect to Ethereum network via RPC
124
+ */
125
+ async connect(): Promise<void> {
126
+ try {
127
+ // Create provider
128
+ this.provider = new ethers.JsonRpcProvider(this.resolveRpcUrl());
129
+
130
+ // Verify connection
131
+ const network = await this.provider.getNetwork();
132
+ this.chainId = Number(network.chainId);
133
+
134
+ this.connected = true;
135
+ } catch (error) {
136
+ const message = error instanceof Error ? error.message : 'Unknown error';
137
+ throw new Error(`Failed to connect to ckETH network: ${message}`);
138
+ }
139
+ }
140
+
141
+ /**
142
+ * Disconnect from network
143
+ */
144
+ async disconnect(): Promise<void> {
145
+ this.provider = null;
146
+ this.connected = false;
147
+ }
148
+
149
+ /**
150
+ * Get wallet balance
151
+ */
152
+ async getBalance(address: string): Promise<Balance> {
153
+ if (!this.provider || !this.connected) {
154
+ throw new Error('Provider not connected');
155
+ }
156
+
157
+ try {
158
+ const balance = await this.provider.getBalance(address);
159
+ const etherBalance = ethers.formatEther(balance);
160
+
161
+ return {
162
+ amount: etherBalance,
163
+ denomination: 'ETH',
164
+ chain: this.getChain(),
165
+ address,
166
+ blockNumber: await this.provider.getBlockNumber(),
167
+ };
168
+ } catch (error) {
169
+ const message = error instanceof Error ? error.message : 'Unknown error';
170
+ throw new Error(`Failed to get balance: ${message}`);
171
+ }
172
+ }
173
+
174
+ /**
175
+ * Send transaction
176
+ */
177
+ async sendTransaction(
178
+ from: string,
179
+ request: TransactionRequest
180
+ ): Promise<Transaction> {
181
+ if (!this.provider || !this.connected) {
182
+ throw new Error('Provider not connected');
183
+ }
184
+
185
+ try {
186
+ const tx = await this.populateTransaction(from, request);
187
+ const signedTx = await this.signTransaction(tx, from);
188
+
189
+ // Send transaction
190
+ const txResponse = await this.provider.broadcastTransaction(signedTx.signedTx);
191
+
192
+ return {
193
+ hash: txResponse.hash,
194
+ from,
195
+ to: request.to,
196
+ amount: request.amount,
197
+ chain: this.getChain(),
198
+ timestamp: Date.now(),
199
+ status: 'pending',
200
+ fee: tx.gasPrice ? ethers.formatEther(BigInt(tx.gasPrice) * BigInt(tx.gasLimit || 21000)) : undefined,
201
+ };
202
+ } catch (error) {
203
+ const message = error instanceof Error ? error.message : 'Unknown error';
204
+ throw new Error(`Failed to send transaction: ${message}`);
205
+ }
206
+ }
207
+
208
+ /**
209
+ * Sign transaction
210
+ */
211
+ async signTransaction(
212
+ tx: any,
213
+ privateKey: string
214
+ ): Promise<SignedTransaction> {
215
+ try {
216
+ const wallet = new ethers.Wallet(privateKey);
217
+ const signedTxSerialized = await wallet.signTransaction(tx);
218
+
219
+ return {
220
+ txHash: tx.hash || '0x0',
221
+ signedTx: signedTxSerialized,
222
+ signature: '0x',
223
+ request: tx,
224
+ };
225
+ } catch (error) {
226
+ const message = error instanceof Error ? error.message : 'Unknown error';
227
+ throw new Error(`Failed to sign transaction: ${message}`);
228
+ }
229
+ }
230
+
231
+ /**
232
+ * Get transaction history
233
+ *
234
+ * Uses Etherscan API to fetch transaction history.
235
+ * Requires ETHERSCAN_API_KEY environment variable or etherscanApiKey in config.
236
+ *
237
+ * @param address - Wallet address to get history for
238
+ * @returns Array of transactions
239
+ */
240
+ async getTransactionHistory(address: string): Promise<Transaction[]> {
241
+ const apiKey = this.etherscanApiKey || process.env[ENV_ETHERSCAN_API_KEY];
242
+
243
+ if (!apiKey) {
244
+ console.warn(
245
+ 'Etherscan API key not configured. Set ETHERSCAN_API_KEY environment variable ' +
246
+ 'or provide etherscanApiKey in config for transaction history.'
247
+ );
248
+ return [];
249
+ }
250
+
251
+ try {
252
+ const isTestnet = this.chainId !== 1;
253
+ const baseUrl = isTestnet
254
+ ? 'https://api-sepolia.etherscan.io/api'
255
+ : 'https://api.etherscan.io/api';
256
+
257
+ const url = `${baseUrl}?module=account&action=txlist&address=${address}&startblock=0&endblock=99999999&sort=desc&apikey=${apiKey}`;
258
+
259
+ const response = await fetch(url);
260
+ const data = await response.json() as { status: string; result: Array<{
261
+ hash: string;
262
+ from: string;
263
+ to: string;
264
+ value: string;
265
+ timeStamp: string;
266
+ gasUsed: string;
267
+ gasPrice: string;
268
+ isError: string;
269
+ }> };
270
+
271
+ if (data.status !== '1' || !Array.isArray(data.result)) {
272
+ console.warn('Etherscan API returned no results or error');
273
+ return [];
274
+ }
275
+
276
+ return data.result.slice(0, 50).map((tx) => ({
277
+ hash: tx.hash,
278
+ from: tx.from,
279
+ to: tx.to,
280
+ amount: ethers.formatEther(BigInt(tx.value)),
281
+ chain: this.getChain(),
282
+ timestamp: parseInt(tx.timeStamp, 10) * 1000,
283
+ status: tx.isError === '1' ? 'failed' : 'confirmed',
284
+ fee: ethers.formatEther(BigInt(tx.gasUsed) * BigInt(tx.gasPrice)),
285
+ }));
286
+ } catch (error) {
287
+ const message = error instanceof Error ? error.message : 'Unknown error';
288
+ console.warn(`Failed to get transaction history: ${message}`);
289
+ return [];
290
+ }
291
+ }
292
+
293
+ /**
294
+ * Validate Ethereum address
295
+ */
296
+ validateAddress(address: string): boolean {
297
+ try {
298
+ return ethers.isAddress(address);
299
+ } catch {
300
+ return false;
301
+ }
302
+ }
303
+
304
+ /**
305
+ * Estimate transaction fee
306
+ */
307
+ async estimateFee(request: TransactionRequest): Promise<string> {
308
+ if (!this.provider || !this.connected) {
309
+ throw new Error('Provider not connected');
310
+ }
311
+
312
+ try {
313
+ const feeData = await this.provider.getFeeData();
314
+ const gasLimit = await this.provider.estimateGas({
315
+ to: request.to,
316
+ value: ethers.parseEther(request.amount),
317
+ });
318
+
319
+ const gasPrice = feeData.gasPrice || feeData.maxFeePerGas || BigInt(0);
320
+ const fee = gasPrice * gasLimit;
321
+ return ethers.formatEther(fee);
322
+ } catch (error) {
323
+ const message = error instanceof Error ? error.message : 'Unknown error';
324
+ throw new Error(`Failed to estimate fee: ${message}`);
325
+ }
326
+ }
327
+
328
+ /**
329
+ * Get current block number
330
+ */
331
+ async getBlockNumber(): Promise<number> {
332
+ if (!this.provider || !this.connected) {
333
+ throw new Error('Provider not connected');
334
+ }
335
+
336
+ return await this.provider.getBlockNumber();
337
+ }
338
+
339
+ /**
340
+ * Get transaction by hash
341
+ */
342
+ async getTransaction(txHash: string): Promise<Transaction | null> {
343
+ if (!this.provider || !this.connected) {
344
+ throw new Error('Provider not connected');
345
+ }
346
+
347
+ try {
348
+ const tx = await this.provider.getTransaction(txHash);
349
+ if (!tx) {
350
+ return null;
351
+ }
352
+
353
+ const receipt = await this.provider.getTransactionReceipt(txHash);
354
+
355
+ return {
356
+ hash: tx.hash,
357
+ from: tx.from,
358
+ to: tx.to || '',
359
+ amount: ethers.formatEther(tx.value),
360
+ chain: this.getChain(),
361
+ timestamp: (await this.provider.getBlock(tx.blockNumber || 0))?.timestamp || 0,
362
+ status: receipt ? (receipt.status ? 'confirmed' : 'failed') : 'pending',
363
+ fee: tx.gasPrice ? ethers.formatEther(BigInt(tx.gasPrice) * BigInt(tx.gasLimit || 21000)) : undefined,
364
+ };
365
+ } catch (error) {
366
+ const message = error instanceof Error ? error.message : 'Unknown error';
367
+ throw new Error(`Failed to get transaction: ${message}`);
368
+ }
369
+ }
370
+
371
+ /**
372
+ * Create transaction from request
373
+ */
374
+ private async populateTransaction(
375
+ from: string,
376
+ request: TransactionRequest
377
+ ): Promise<any> {
378
+ const tx = {
379
+ to: request.to,
380
+ value: ethers.parseEther(request.amount),
381
+ from,
382
+ gasLimit: request.gasLimit ? BigInt(parseInt(request.gasLimit)) : undefined,
383
+ gasPrice: request.gasPrice ? BigInt(parseInt(request.gasPrice)) : undefined,
384
+ };
385
+
386
+ return tx;
387
+ }
388
+
389
+ /**
390
+ * Get default RPC URL for chain
391
+ *
392
+ * @param isTestnet - Whether to get testnet URL
393
+ * @returns RPC URL from environment or public endpoint
394
+ * @throws Error if no RPC URL is configured
395
+ */
396
+ static getDefaultRpcUrl(isTestnet: boolean = false): string {
397
+ const envVar = isTestnet ? ENV_SEPOLIA_RPC_URL : ENV_RPC_URL;
398
+ const envUrl = process.env[envVar];
399
+
400
+ if (envUrl) {
401
+ return envUrl;
402
+ }
403
+
404
+ const infuraKey = process.env[ENV_INFURA_KEY];
405
+ if (infuraKey) {
406
+ return isTestnet
407
+ ? `https://sepolia.infura.io/v3/${infuraKey}`
408
+ : `https://mainnet.infura.io/v3/${infuraKey}`;
409
+ }
410
+
411
+ const publicUrls = isTestnet ? PUBLIC_RPC_URLS.sepolia : PUBLIC_RPC_URLS.mainnet;
412
+ const publicUrl = publicUrls[0];
413
+
414
+ if (publicUrl) {
415
+ console.warn(
416
+ `Warning: Using public RPC endpoint (${publicUrl}). ` +
417
+ `For production, set ${envVar} environment variable.`
418
+ );
419
+ return publicUrl;
420
+ }
421
+
422
+ throw new Error(
423
+ `No RPC URL configured. Set ${envVar} environment variable. ` +
424
+ `Example: export ${envVar}=https://mainnet.infura.io/v3/your-api-key`
425
+ );
426
+ }
427
+
428
+ /**
429
+ * Get chain ID
430
+ */
431
+ getChainId(): number {
432
+ return this.chainId;
433
+ }
434
+ }