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.
- package/.dfx/local/network-id +4 -0
- package/.next/trace +2 -0
- package/.vercel/README.txt +11 -0
- package/.vercel/project.json +1 -0
- package/AGENTS.md +43 -0
- package/CHANGELOG.md +196 -0
- package/LICENSE +21 -0
- package/PLAN_VAULT_INTEGRATION.md +318 -0
- package/README.md +253 -0
- package/backups/agentvault-backup-test-agent-2026-02-12T17-54-28-967Z.json +28 -0
- package/backups/agentvault-backup-test-agent-2026-02-12T17-54-29-032Z.backup +1 -0
- package/backups/agentvault-backup-test-agent-2026-02-12T17-57-42-373Z.json +28 -0
- package/backups/agentvault-backup-test-agent-2026-02-12T17-57-42-428Z.backup +1 -0
- package/backups/agentvault-backup-test-agent-2026-02-12T18-52-25-132Z.json +28 -0
- package/backups/agentvault-backup-test-agent-2026-02-12T18-52-25-247Z.backup +1 -0
- package/backups/agentvault-backup-test-agent-2026-02-12T18-54-09-216Z.json +28 -0
- package/backups/agentvault-backup-test-agent-2026-02-12T18-54-09-283Z.backup +1 -0
- package/backups/agentvault-backup-test-agent-2026-02-12T22-18-22-772Z.backup +1 -0
- package/backups/agentvault-backup-test-agent-2026-02-12T22-18-22-793Z.json +28 -0
- package/backups/test-backup.json +28 -0
- package/dist/cli/commands/approve.d.ts +4 -0
- package/dist/cli/commands/approve.js +232 -0
- package/dist/cli/commands/archive.d.ts +4 -0
- package/dist/cli/commands/archive.js +192 -0
- package/dist/cli/commands/backup.d.ts +4 -0
- package/dist/cli/commands/backup.js +164 -0
- package/dist/cli/commands/cloud-backup.d.ts +4 -0
- package/dist/cli/commands/cloud-backup.js +221 -0
- package/dist/cli/commands/cycles.d.ts +8 -0
- package/dist/cli/commands/cycles.js +83 -0
- package/dist/cli/commands/decrypt.d.ts +16 -0
- package/dist/cli/commands/decrypt.js +101 -0
- package/dist/cli/commands/deploy.d.ts +32 -0
- package/dist/cli/commands/deploy.js +208 -0
- package/dist/cli/commands/exec.d.ts +26 -0
- package/dist/cli/commands/exec.js +109 -0
- package/dist/cli/commands/fetch.d.ts +23 -0
- package/dist/cli/commands/fetch.js +164 -0
- package/dist/cli/commands/health.d.ts +8 -0
- package/dist/cli/commands/health.js +72 -0
- package/dist/cli/commands/identity.d.ts +8 -0
- package/dist/cli/commands/identity.js +140 -0
- package/dist/cli/commands/inference.d.ts +4 -0
- package/dist/cli/commands/inference.js +225 -0
- package/dist/cli/commands/info.d.ts +8 -0
- package/dist/cli/commands/info.js +59 -0
- package/dist/cli/commands/init.d.ts +19 -0
- package/dist/cli/commands/init.js +135 -0
- package/dist/cli/commands/instrument.d.ts +8 -0
- package/dist/cli/commands/instrument.js +35 -0
- package/dist/cli/commands/list.d.ts +36 -0
- package/dist/cli/commands/list.js +173 -0
- package/dist/cli/commands/logs.d.ts +8 -0
- package/dist/cli/commands/logs.js +96 -0
- package/dist/cli/commands/monitor.d.ts +8 -0
- package/dist/cli/commands/monitor.js +84 -0
- package/dist/cli/commands/network.d.ts +14 -0
- package/dist/cli/commands/network.js +258 -0
- package/dist/cli/commands/package.d.ts +36 -0
- package/dist/cli/commands/package.js +188 -0
- package/dist/cli/commands/profile.d.ts +8 -0
- package/dist/cli/commands/profile.js +76 -0
- package/dist/cli/commands/promote.d.ts +8 -0
- package/dist/cli/commands/promote.js +89 -0
- package/dist/cli/commands/rebuild.d.ts +21 -0
- package/dist/cli/commands/rebuild.js +140 -0
- package/dist/cli/commands/rollback.d.ts +8 -0
- package/dist/cli/commands/rollback.js +120 -0
- package/dist/cli/commands/show.d.ts +36 -0
- package/dist/cli/commands/show.js +200 -0
- package/dist/cli/commands/stats.d.ts +8 -0
- package/dist/cli/commands/stats.js +34 -0
- package/dist/cli/commands/status.d.ts +14 -0
- package/dist/cli/commands/status.js +83 -0
- package/dist/cli/commands/test.d.ts +8 -0
- package/dist/cli/commands/test.js +109 -0
- package/dist/cli/commands/tokens.d.ts +8 -0
- package/dist/cli/commands/tokens.js +62 -0
- package/dist/cli/commands/trace.d.ts +8 -0
- package/dist/cli/commands/trace.js +68 -0
- package/dist/cli/commands/wallet-export.d.ts +13 -0
- package/dist/cli/commands/wallet-export.js +140 -0
- package/dist/cli/commands/wallet-history.d.ts +10 -0
- package/dist/cli/commands/wallet-history.js +127 -0
- package/dist/cli/commands/wallet-import.d.ts +10 -0
- package/dist/cli/commands/wallet-import.js +209 -0
- package/dist/cli/commands/wallet-multi-send.d.ts +17 -0
- package/dist/cli/commands/wallet-multi-send.js +195 -0
- package/dist/cli/commands/wallet-process-queue.d.ts +19 -0
- package/dist/cli/commands/wallet-process-queue.js +209 -0
- package/dist/cli/commands/wallet-sign.d.ts +13 -0
- package/dist/cli/commands/wallet-sign.js +207 -0
- package/dist/cli/commands/wallet.d.ts +12 -0
- package/dist/cli/commands/wallet.js +794 -0
- package/dist/cli/index.d.ts +10 -0
- package/dist/cli/index.js +96 -0
- package/dist/vitest.config.d.ts +3 -0
- package/dist/vitest.config.js +14 -0
- package/fixup_1_0_OSS_release.md +136 -0
- package/fixup_REALEASE_PRD.md +136 -0
- package/package.json +79 -0
- package/pnpm-workspace.yaml +5 -0
- package/scripts/dev-dashboard.mjs +84 -0
- package/site/README.md +63 -0
- package/site/docusaurus.config.ts +148 -0
- package/site/package-lock.json +18383 -0
- package/site/package.json +47 -0
- package/site/sidebars.ts +86 -0
- package/site/static/.gitkeep +0 -0
- package/site/static/img/logo.svg +28 -0
- package/site/static/img/og-image.svg +35 -0
- package/src/archival/archive-manager.ts +372 -0
- package/src/archival/arweave-client.ts +289 -0
- package/src/archival/index.ts +8 -0
- package/src/backup/backup.ts +315 -0
- package/src/backup/index.ts +7 -0
- package/src/cloud-storage/cloud-sync.ts +461 -0
- package/src/cloud-storage/index.ts +11 -0
- package/src/cloud-storage/provider-detector.ts +198 -0
- package/src/cloud-storage/types.ts +104 -0
- package/src/debugging/index.ts +6 -0
- package/src/debugging/logs.ts +193 -0
- package/src/debugging/types.ts +100 -0
- package/src/deployment/deployer.ts +274 -0
- package/src/deployment/icpClient.ts +620 -0
- package/src/deployment/index.ts +46 -0
- package/src/deployment/promotion.ts +161 -0
- package/src/deployment/types.ts +111 -0
- package/src/icp/batch.ts +374 -0
- package/src/icp/cycles.ts +50 -0
- package/src/icp/environment.ts +215 -0
- package/src/icp/icpcli.ts +438 -0
- package/src/icp/icwasm.ts +222 -0
- package/src/icp/identity.ts +77 -0
- package/src/icp/index.ts +94 -0
- package/src/icp/optimization.ts +242 -0
- package/src/icp/tokens.ts +36 -0
- package/src/icp/tool-detector.ts +110 -0
- package/src/icp/types.ts +574 -0
- package/src/index.ts +25 -0
- package/src/inference/bittensor-client.ts +304 -0
- package/src/inference/index.ts +8 -0
- package/src/inference/inference-manager.ts +327 -0
- package/src/metrics/index.ts +7 -0
- package/src/metrics/metrics.ts +186 -0
- package/src/monitoring/alerting.ts +190 -0
- package/src/monitoring/health.ts +197 -0
- package/src/monitoring/index.ts +38 -0
- package/src/monitoring/info.ts +114 -0
- package/src/monitoring/types.ts +99 -0
- package/src/network/index.ts +5 -0
- package/src/network/network-config.ts +129 -0
- package/src/packaging/compiler.ts +647 -0
- package/src/packaging/config-persistence.ts +135 -0
- package/src/packaging/config-schemas.ts +156 -0
- package/src/packaging/detector.ts +220 -0
- package/src/packaging/index.ts +90 -0
- package/src/packaging/packager.ts +118 -0
- package/src/packaging/parsers/clawdbot.ts +278 -0
- package/src/packaging/parsers/cline.ts +223 -0
- package/src/packaging/parsers/generic.ts +266 -0
- package/src/packaging/parsers/goose.ts +214 -0
- package/src/packaging/parsers/index.ts +11 -0
- package/src/packaging/serializer.ts +260 -0
- package/src/packaging/types.ts +144 -0
- package/src/packaging/wasmedge-compiler.ts +406 -0
- package/src/security/index.ts +17 -0
- package/src/security/multisig.ts +415 -0
- package/src/security/types.ts +416 -0
- package/src/security/vetkeys.ts +655 -0
- package/src/testing/index.ts +6 -0
- package/src/testing/local-runner.ts +264 -0
- package/src/testing/types.ts +104 -0
- package/src/wallet/cbor-serializer.ts +323 -0
- package/src/wallet/chain-dispatcher.ts +313 -0
- package/src/wallet/cross-chain-aggregator.ts +346 -0
- package/src/wallet/index.ts +76 -0
- package/src/wallet/key-derivation.ts +425 -0
- package/src/wallet/providers/base-provider.ts +154 -0
- package/src/wallet/providers/cketh-provider.ts +434 -0
- package/src/wallet/providers/polkadot-provider.ts +503 -0
- package/src/wallet/providers/solana-provider.ts +490 -0
- package/src/wallet/transaction-queue.ts +284 -0
- package/src/wallet/types.ts +178 -0
- package/src/wallet/vetkeys-adapter.ts +431 -0
- package/src/wallet/wallet-manager.ts +597 -0
- package/src/wallet/wallet-storage.ts +380 -0
- 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
|
+
}
|