w3pk 0.10.5 → 0.11.1
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/README.md +47 -46
- package/dist/chainlist/index.d.mts +2 -2
- package/dist/chainlist/index.d.ts +2 -2
- package/dist/chainlist/index.js.map +1 -1
- package/dist/chainlist/index.mjs.map +1 -1
- package/dist/index.d.mts +891 -745
- package/dist/index.d.ts +891 -745
- package/dist/index.js +1132 -440
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1132 -440
- package/dist/index.mjs.map +1 -1
- package/dist/zk/index.d.mts +177 -4
- package/dist/zk/index.d.ts +177 -4
- package/dist/zk/index.js +5 -5
- package/dist/zk/index.js.map +1 -1
- package/dist/zk/index.mjs +5 -5
- package/dist/zk/index.mjs.map +1 -1
- package/dist/zk/prove-worker.mjs.map +1 -0
- package/dist/zk/utils.js +3 -3
- package/dist/zk/utils.js.map +1 -1
- package/dist/zk/utils.mjs +3 -3
- package/dist/zk/utils.mjs.map +1 -1
- package/docs/API_REFERENCE.md +509 -636
- package/docs/ARCHITECTURE.md +86 -106
- package/docs/BUILD_VERIFICATION.md +35 -8
- package/docs/EIP-7951.md +1 -8
- package/docs/EIP_7702.md +35 -395
- package/docs/INTEGRATION_GUIDELINES.md +44 -515
- package/docs/JUNE_9_2026_AUDIT.md +207 -0
- package/docs/PORTABILITY.md +41 -186
- package/docs/POST_QUANTUM.md +61 -39
- package/docs/RECOVERY.md +165 -173
- package/docs/SECURITY.md +301 -755
- package/docs/ZK.md +250 -0
- package/package.json +27 -22
- package/dist/chainlist/index.js +0 -2
- package/dist/chainlist/index.mjs +0 -2
- package/docs/notes/IFRAME_IMPLEMENTATION_PLAN.md +0 -197
- package/docs/notes/JULY_13_ROADMAP.md +0 -29
- package/docs/notes/NOTES.md +0 -333
- package/docs/notes/SIWE_IMPLEMENTATION.md +0 -129
- package/docs/notes/SYNCING_ACROSS_DEVICES.md +0 -127
- package/docs/notes/ethereum-post-quantum-roadmap.md +0 -213
- package/docs/notes/old-computer.md +0 -99
- package/docs/notes/wulong-building-finding.md +0 -109
package/README.md
CHANGED
|
@@ -51,11 +51,10 @@ const endpoints = await w3pk.getEndpoints(1)
|
|
|
51
51
|
- On-chain transaction sending with automatic RPC resolution (`sendTransaction`)
|
|
52
52
|
- EIP-1193 provider for ethers, viem, wagmi, RainbowKit (`getEIP1193Provider`)
|
|
53
53
|
- ERC-5564 stealth addresses (opt-in)
|
|
54
|
-
- ZK primitives (zero-knowledge proof generation and verification)
|
|
55
|
-
-
|
|
54
|
+
- ZK primitives (zero-knowledge proof generation and verification, Baby Jubjub keys, exponential ElGamal and circuit artifact verification against onchain pins in `w3pk/zk`, passkey-bound Semaphore identities with `getSemaphoreIdentity()`, and proofs over a passkey-bound secret the app never sees with `zkProve()`)
|
|
55
|
+
- Hybrid post-quantum encryption (HPKE over ML-KEM-1024 + X25519)
|
|
56
56
|
- Chainlist support (2390+ networks)
|
|
57
57
|
- EIP-7702 network detection (329+ networks)
|
|
58
|
-
- External wallet integration (delegate MetaMask/Ledger to w3pk via EIP-7702)
|
|
59
58
|
- EIP-7951 PRIMARY mode (P-256 passkey signing)
|
|
60
59
|
- Build verification (IPFS CID hashing + DAO-maintained onchain registry)
|
|
61
60
|
- Three-layer backup & recovery (passkey sync, encrypted backups, social recovery)
|
|
@@ -65,19 +64,16 @@ const endpoints = await w3pk.getEndpoints(1)
|
|
|
65
64
|
### Authentication
|
|
66
65
|
|
|
67
66
|
```typescript
|
|
68
|
-
//
|
|
69
|
-
const
|
|
67
|
+
// List all wallets on device
|
|
68
|
+
const wallets = await w3pk.listExistingCredentials()
|
|
70
69
|
|
|
71
70
|
// Register or login
|
|
72
|
-
if (
|
|
71
|
+
if (wallets.length > 0) {
|
|
73
72
|
await w3pk.login()
|
|
74
73
|
} else {
|
|
75
74
|
const { address, username } = await w3pk.register({ username: 'alice' })
|
|
76
75
|
}
|
|
77
76
|
|
|
78
|
-
// List all wallets on device
|
|
79
|
-
const wallets = await w3pk.listExistingCredentials()
|
|
80
|
-
|
|
81
77
|
// Logout
|
|
82
78
|
await w3pk.logout()
|
|
83
79
|
```
|
|
@@ -111,7 +107,7 @@ const strictWallet = await w3pk.deriveWallet('STRICT', 'SECURE')
|
|
|
111
107
|
console.log(mainWallet.address !== gamingWallet.address) // true
|
|
112
108
|
```
|
|
113
109
|
|
|
114
|
-
**Security:** Master mnemonic is never exposed.
|
|
110
|
+
**Security:** Master mnemonic is never exposed. No method returns it, and restores (`restoreFromBackupFile()`, `syncWalletWithPasskey()`, `recoverFromGuardians()`) return only the address.
|
|
115
111
|
|
|
116
112
|
### Message Signing
|
|
117
113
|
|
|
@@ -133,7 +129,7 @@ const eip712Sig = await w3pk.signMessage(JSON.stringify(typedData), {
|
|
|
133
129
|
eip712PrimaryType: 'Transfer'
|
|
134
130
|
})
|
|
135
131
|
|
|
136
|
-
// Raw hash
|
|
132
|
+
// Raw hash (always prompts for the passkey)
|
|
137
133
|
const rawSig = await w3pk.signMessage(hash, {
|
|
138
134
|
signingMethod: 'rawHash'
|
|
139
135
|
})
|
|
@@ -217,7 +213,7 @@ const hash = await client.sendTransaction({ to: '0x...', value: parseEther('1')
|
|
|
217
213
|
| `eth_chainId` | Returns active chain as hex |
|
|
218
214
|
| `eth_sendTransaction` | Delegates to `sendTransaction()` |
|
|
219
215
|
| `personal_sign` / `eth_sign` | EIP-191 message signing |
|
|
220
|
-
| `eth_signTypedData_v4` | EIP-712 typed data signing |
|
|
216
|
+
| `eth_signTypedData_v4` | EIP-712 typed data signing, bound to the active chain |
|
|
221
217
|
| `wallet_switchEthereumChain` | Updates active chainId, emits `chainChanged` |
|
|
222
218
|
|
|
223
219
|
### Session Management
|
|
@@ -256,7 +252,7 @@ await w3pk.clearSession()
|
|
|
256
252
|
|
|
257
253
|
**Note:** STRICT mode never allows persistent sessions.
|
|
258
254
|
|
|
259
|
-
**PRF-keyed
|
|
255
|
+
**PRF-keyed storage:** the wallet stored on the device and persistent sessions are encrypted under keys derived from the WebAuthn PRF extension (an authenticator-held secret, released only during a biometric/PIN-verified assertion), so storage alone can't decrypt them. Authenticators without PRF support keep no wallet on the device and get in-memory sessions only: the wallet comes back from a backup on the next visit. There is no weaker at-rest fallback. See [Security Architecture](./docs/SECURITY.md) for details.
|
|
260
256
|
|
|
261
257
|
### RPC Endpoints
|
|
262
258
|
|
|
@@ -291,13 +287,6 @@ const authorization = await w3pk.signAuthorization({
|
|
|
291
287
|
nonce: 0n
|
|
292
288
|
})
|
|
293
289
|
// Returns: { chainId, address, nonce, yParity, r, s }
|
|
294
|
-
|
|
295
|
-
// Delegate external wallet (MetaMask, Ledger, etc.) to w3pk account
|
|
296
|
-
const auth = await w3pk.requestExternalWalletDelegation({
|
|
297
|
-
chainId: 1,
|
|
298
|
-
nonce: 0n
|
|
299
|
-
})
|
|
300
|
-
// User's external wallet account now controlled by w3pk WebAuthn
|
|
301
290
|
```
|
|
302
291
|
|
|
303
292
|
### EIP-7951 PRIMARY Mode
|
|
@@ -332,17 +321,30 @@ const result = await w3pk.stealth?.parseAnnouncement({
|
|
|
332
321
|
})
|
|
333
322
|
|
|
334
323
|
if (result.isForUser) {
|
|
335
|
-
console.log('
|
|
324
|
+
console.log('Payment received at:', result.stealthAddress)
|
|
336
325
|
}
|
|
337
326
|
|
|
338
327
|
// Scan multiple announcements
|
|
339
328
|
const myPayments = await w3pk.stealth?.scanAnnouncements(announcements)
|
|
329
|
+
|
|
330
|
+
// Sign a transaction from a stealth address (broadcast it with your own RPC)
|
|
331
|
+
const signedTx = await w3pk.stealth?.signTransaction(announcement, {
|
|
332
|
+
to: '0x...',
|
|
333
|
+
value: 1000000000000000n,
|
|
334
|
+
chainId: 11155111,
|
|
335
|
+
nonce: 0,
|
|
336
|
+
gasLimit: 21000n,
|
|
337
|
+
maxFeePerGas: 2000000000n,
|
|
338
|
+
maxPriorityFeePerGas: 1000000000n
|
|
339
|
+
})
|
|
340
340
|
```
|
|
341
341
|
|
|
342
|
-
|
|
342
|
+
Stealth keys are shared across origins, and w3pk never returns the spending private key or a stealth private key: it signs from stealth addresses itself.
|
|
343
|
+
|
|
344
|
+
### Post-quantum encryption
|
|
343
345
|
|
|
344
346
|
```typescript
|
|
345
|
-
//
|
|
347
|
+
// Hybrid ML-KEM-1024 + X25519 (HPKE, payload v3)
|
|
346
348
|
const pubKey = await w3pk.deriveMLKemPublicKey()
|
|
347
349
|
const encrypted = await w3pk.mlkemEncrypt('secret', [pubKey])
|
|
348
350
|
const plaintext = await w3pk.mlkemDecrypt(encrypted)
|
|
@@ -354,48 +356,47 @@ const plaintext = await w3pk.mlkemDecrypt(encrypted)
|
|
|
354
356
|
import { isStrongPassword } from 'w3pk'
|
|
355
357
|
|
|
356
358
|
// Validate password strength
|
|
357
|
-
const password = '
|
|
359
|
+
const password = 'violet lantern under seven bridges'
|
|
358
360
|
if (!isStrongPassword(password)) {
|
|
359
|
-
throw new Error('Password must be
|
|
361
|
+
throw new Error('Password must be 15+ characters and not a common password')
|
|
360
362
|
}
|
|
361
363
|
|
|
362
364
|
// Get backup status
|
|
363
365
|
const status = await w3pk.getBackupStatus()
|
|
364
|
-
console.log('Security Score:', status.securityScore.total) // 0-
|
|
366
|
+
console.log('Security Score:', status.securityScore.total) // 0-70
|
|
367
|
+
console.log('Next backup:', status.recommendedMode) // 'password', then 'prf'
|
|
365
368
|
|
|
366
|
-
// Create encrypted backup file
|
|
367
|
-
const { blob, filename } = await w3pk.createBackupFile(
|
|
369
|
+
// Create encrypted backup file (BackupFile v3: Argon2id, no address in the file)
|
|
370
|
+
const { blob, filename } = await w3pk.createBackupFile(password)
|
|
368
371
|
|
|
369
372
|
// Setup social recovery (M-of-N guardians) - guardians store backup file fragments
|
|
370
|
-
|
|
371
|
-
const backupFileJson = await blob.text()
|
|
372
|
-
const socialRecovery = new SocialRecoveryManager()
|
|
373
|
-
const guardians = await socialRecovery.setupSocialRecovery(
|
|
374
|
-
backupFileJson,
|
|
375
|
-
w3pk.user.ethereumAddress,
|
|
373
|
+
const { guardianShares } = await w3pk.setupSocialRecovery(
|
|
376
374
|
[
|
|
377
375
|
{ name: 'Alice', email: 'alice@example.com' },
|
|
378
|
-
{ name: 'Bob'
|
|
376
|
+
{ name: 'Bob' },
|
|
379
377
|
{ name: 'Charlie' }
|
|
380
378
|
],
|
|
381
|
-
2 // threshold
|
|
379
|
+
2, // threshold
|
|
380
|
+
password // encrypts the backup the shares rebuild
|
|
382
381
|
)
|
|
383
382
|
|
|
384
|
-
// Generate guardian invite
|
|
385
|
-
const
|
|
383
|
+
// Generate guardian invite (share file, printable document, optional QR code)
|
|
384
|
+
const { downloadBlob, filename, shareDocument } = await w3pk.generateGuardianInvite(guardianShares[0])
|
|
386
385
|
|
|
387
|
-
// Recover from guardian
|
|
388
|
-
|
|
389
|
-
await w3pk.
|
|
386
|
+
// Recover from guardian share files - rebuilds and decrypts the backup
|
|
387
|
+
// (logged out: the wallet stays in memory for register(); logged in: it replaces the stored one)
|
|
388
|
+
const { ethereumAddress } = await w3pk.recoverFromGuardians([share1, share2], password)
|
|
390
389
|
|
|
391
|
-
// Restore from backup file
|
|
390
|
+
// Restore from backup file (another wallet than the logged-in one needs
|
|
391
|
+
// the user's confirmation: { allowAddressChange: true })
|
|
392
392
|
await w3pk.restoreFromBackupFile(encryptedData, password)
|
|
393
393
|
|
|
394
394
|
// Simulate recovery scenarios
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
description: 'Device lost with iCloud Keychain enabled'
|
|
398
|
-
|
|
395
|
+
import { RecoverySimulator } from 'w3pk'
|
|
396
|
+
const result = await new RecoverySimulator().simulateScenario(
|
|
397
|
+
{ type: 'lost-device', description: 'Device lost with iCloud Keychain enabled' },
|
|
398
|
+
await w3pk.getBackupStatus()
|
|
399
|
+
)
|
|
399
400
|
```
|
|
400
401
|
|
|
401
402
|
### Build Verification
|
|
@@ -51,11 +51,11 @@ interface ChainlistOptions {
|
|
|
51
51
|
*/
|
|
52
52
|
|
|
53
53
|
/**
|
|
54
|
-
* Get RPC endpoints for a specific chain ID, excluding those that require API keys
|
|
54
|
+
* Get the HTTPS RPC endpoints for a specific chain ID, excluding those that require API keys
|
|
55
55
|
*
|
|
56
56
|
* @param chainId - The chain ID to get endpoints for
|
|
57
57
|
* @param options - Optional configuration
|
|
58
|
-
* @returns Array of RPC URLs that don't require API keys
|
|
58
|
+
* @returns Array of HTTPS RPC URLs that don't require API keys
|
|
59
59
|
*
|
|
60
60
|
* @example
|
|
61
61
|
* ```typescript
|
|
@@ -51,11 +51,11 @@ interface ChainlistOptions {
|
|
|
51
51
|
*/
|
|
52
52
|
|
|
53
53
|
/**
|
|
54
|
-
* Get RPC endpoints for a specific chain ID, excluding those that require API keys
|
|
54
|
+
* Get the HTTPS RPC endpoints for a specific chain ID, excluding those that require API keys
|
|
55
55
|
*
|
|
56
56
|
* @param chainId - The chain ID to get endpoints for
|
|
57
57
|
* @param options - Optional configuration
|
|
58
|
-
* @returns Array of RPC URLs that don't require API keys
|
|
58
|
+
* @returns Array of HTTPS RPC URLs that don't require API keys
|
|
59
59
|
*
|
|
60
60
|
* @example
|
|
61
61
|
* ```typescript
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/chainlist/index.ts"],"sourcesContent":["/**\n * Chainlist module for fetching RPC endpoints\n */\n\nimport type { Chain, ChainlistOptions } from \"./types\";\n\nconst DEFAULT_CHAINS_URL = \"https://chainid.network/chains.json\";\nconst DEFAULT_CACHE_DURATION = 3600000; // 1 hour\n\ninterface CacheEntry {\n data: Chain[];\n timestamp: number;\n}\n\nlet cache: CacheEntry | null = null;\n\n/**\n * Patterns that indicate an RPC URL requires an API key\n */\nconst API_KEY_PATTERNS = [\n /\\$\\{[\\w_]+\\}/i, // ${INFURA_API_KEY}, ${API_KEY}, etc.\n /\\{[\\w_]+\\}/i, // {API_KEY}, {INFURA_API_KEY}, etc.\n /<[\\w_]+>/i, // <API_KEY>, <INFURA_API_KEY>, etc.\n /YOUR[-_]?API[-_]?KEY/i,\n /INSERT[-_]?API[-_]?KEY/i,\n /API[-_]?KEY[-_]?HERE/i,\n];\n\n/**\n * Check if an RPC URL requires an API key\n */\nfunction requiresApiKey(rpcUrl: string): boolean {\n return API_KEY_PATTERNS.some((pattern) => pattern.test(rpcUrl));\n}\n\n/**\n * Fetch all chains data from chainid.network\n */\nasync function fetchChains(\n chainsJsonUrl: string = DEFAULT_CHAINS_URL\n): Promise<Chain[]> {\n const response = await fetch(chainsJsonUrl);\n\n if (!response.ok) {\n throw new Error(\n `Failed to fetch chains data: ${response.status} ${response.statusText}`\n );\n }\n\n return await response.json();\n}\n\n/**\n * Get all chains data with caching\n */\nasync function getChainsData(options?: ChainlistOptions): Promise<Chain[]> {\n const chainsJsonUrl = options?.chainsJsonUrl ?? DEFAULT_CHAINS_URL;\n const cacheDuration = options?.cacheDuration ?? DEFAULT_CACHE_DURATION;\n const now = Date.now();\n\n // Check if cache is valid\n if (cache && now - cache.timestamp < cacheDuration) {\n return cache.data;\n }\n\n // Fetch fresh data\n const data = await fetchChains(chainsJsonUrl);\n cache = { data, timestamp: now };\n\n return data;\n}\n\n/**\n * Get RPC endpoints for a specific chain ID, excluding those that require API keys\n *\n * @param chainId - The chain ID to get endpoints for\n * @param options - Optional configuration\n * @returns Array of RPC URLs that don't require API keys\n *\n * @example\n * ```typescript\n * import { getEndpoints } from 'w3pk/chainlist'\n *\n * // Get Ethereum mainnet RPCs\n * const endpoints = await getEndpoints(1)\n * console.log(endpoints)\n * // [\n * // \"https://api.mycryptoapi.com/eth\",\n * // \"https://cloudflare-eth.com\",\n * // \"https://ethereum-rpc.publicnode.com\",\n * // ...\n * // ]\n * ```\n */\nexport async function getEndpoints(\n chainId: number,\n options?: ChainlistOptions\n): Promise<string[]> {\n const chains = await getChainsData(options);\n const chain = chains.find((c) => c.chainId === chainId);\n\n if (!chain) {\n return [];\n }\n\n
|
|
1
|
+
{"version":3,"sources":["../../src/chainlist/index.ts"],"sourcesContent":["/**\n * Chainlist module for fetching RPC endpoints\n */\n\nimport type { Chain, ChainlistOptions } from \"./types\";\n\nconst DEFAULT_CHAINS_URL = \"https://chainid.network/chains.json\";\nconst DEFAULT_CACHE_DURATION = 3600000; // 1 hour\n\ninterface CacheEntry {\n data: Chain[];\n timestamp: number;\n}\n\nlet cache: CacheEntry | null = null;\n\n/**\n * Patterns that indicate an RPC URL requires an API key\n */\nconst API_KEY_PATTERNS = [\n /\\$\\{[\\w_]+\\}/i, // ${INFURA_API_KEY}, ${API_KEY}, etc.\n /\\{[\\w_]+\\}/i, // {API_KEY}, {INFURA_API_KEY}, etc.\n /<[\\w_]+>/i, // <API_KEY>, <INFURA_API_KEY>, etc.\n /YOUR[-_]?API[-_]?KEY/i,\n /INSERT[-_]?API[-_]?KEY/i,\n /API[-_]?KEY[-_]?HERE/i,\n];\n\n/**\n * Check if an RPC URL requires an API key\n */\nfunction requiresApiKey(rpcUrl: string): boolean {\n return API_KEY_PATTERNS.some((pattern) => pattern.test(rpcUrl));\n}\n\n/**\n * Fetch all chains data from chainid.network\n */\nasync function fetchChains(\n chainsJsonUrl: string = DEFAULT_CHAINS_URL\n): Promise<Chain[]> {\n const response = await fetch(chainsJsonUrl);\n\n if (!response.ok) {\n throw new Error(\n `Failed to fetch chains data: ${response.status} ${response.statusText}`\n );\n }\n\n return await response.json();\n}\n\n/**\n * Get all chains data with caching\n */\nasync function getChainsData(options?: ChainlistOptions): Promise<Chain[]> {\n const chainsJsonUrl = options?.chainsJsonUrl ?? DEFAULT_CHAINS_URL;\n const cacheDuration = options?.cacheDuration ?? DEFAULT_CACHE_DURATION;\n const now = Date.now();\n\n // Check if cache is valid\n if (cache && now - cache.timestamp < cacheDuration) {\n return cache.data;\n }\n\n // Fetch fresh data\n const data = await fetchChains(chainsJsonUrl);\n cache = { data, timestamp: now };\n\n return data;\n}\n\n/**\n * Get the HTTPS RPC endpoints for a specific chain ID, excluding those that require API keys\n *\n * @param chainId - The chain ID to get endpoints for\n * @param options - Optional configuration\n * @returns Array of HTTPS RPC URLs that don't require API keys\n *\n * @example\n * ```typescript\n * import { getEndpoints } from 'w3pk/chainlist'\n *\n * // Get Ethereum mainnet RPCs\n * const endpoints = await getEndpoints(1)\n * console.log(endpoints)\n * // [\n * // \"https://api.mycryptoapi.com/eth\",\n * // \"https://cloudflare-eth.com\",\n * // \"https://ethereum-rpc.publicnode.com\",\n * // ...\n * // ]\n * ```\n */\nexport async function getEndpoints(\n chainId: number,\n options?: ChainlistOptions\n): Promise<string[]> {\n const chains = await getChainsData(options);\n const chain = chains.find((c) => c.chainId === chainId);\n\n if (!chain) {\n return [];\n }\n\n return chain.rpc.filter(\n (rpcUrl) => rpcUrl.startsWith(\"https://\") && !requiresApiKey(rpcUrl)\n );\n}\n\n/**\n * Get all available chains\n *\n * @param options - Optional configuration\n * @returns Array of all chains\n */\nexport async function getAllChains(\n options?: ChainlistOptions\n): Promise<Chain[]> {\n return getChainsData(options);\n}\n\n/**\n * Get chain information by chain ID\n *\n * @param chainId - The chain ID to get information for\n * @param options - Optional configuration\n * @returns Chain information or undefined if not found\n */\nexport async function getChainById(\n chainId: number,\n options?: ChainlistOptions\n): Promise<Chain | undefined> {\n const chains = await getChainsData(options);\n return chains.find((c) => c.chainId === chainId);\n}\n\n/**\n * Clear the chains data cache\n */\nexport function clearCache(): void {\n cache = null;\n}\n\n// Export types\nexport type { Chain, ChainlistOptions } from \"./types\";\n"],"mappings":"yaAAA,IAAAA,EAAA,GAAAC,EAAAD,EAAA,gBAAAE,EAAA,iBAAAC,EAAA,iBAAAC,EAAA,iBAAAC,IAAA,eAAAC,EAAAN,GAMA,IAAMO,EAAqB,sCAQ3B,IAAIC,EAA2B,KAKzBC,EAAmB,CACvB,gBACA,cACA,YACA,wBACA,0BACA,uBACF,EAKA,SAASC,EAAeC,EAAyB,CAC/C,OAAOF,EAAiB,KAAMG,GAAYA,EAAQ,KAAKD,CAAM,CAAC,CAChE,CAKA,eAAeE,EACbC,EAAwBC,EACN,CAClB,IAAMC,EAAW,MAAM,MAAMF,CAAa,EAE1C,GAAI,CAACE,EAAS,GACZ,MAAM,IAAI,MACR,gCAAgCA,EAAS,MAAM,IAAIA,EAAS,UAAU,EACxE,EAGF,OAAO,MAAMA,EAAS,KAAK,CAC7B,CAKA,eAAeC,EAAcC,EAA8C,CACzE,IAAMJ,EAAgBI,GAAS,eAAiBH,EAC1CI,EAAgBD,GAAS,eAAiB,KAC1CE,EAAM,KAAK,IAAI,EAGrB,GAAIZ,GAASY,EAAMZ,EAAM,UAAYW,EACnC,OAAOX,EAAM,KAIf,IAAMa,EAAO,MAAMR,EAAYC,CAAa,EAC5C,OAAAN,EAAQ,CAAE,KAAAa,EAAM,UAAWD,CAAI,EAExBC,CACT,CAwBA,eAAsBC,EACpBC,EACAL,EACmB,CAEnB,IAAMM,GADS,MAAMP,EAAcC,CAAO,GACrB,KAAMO,GAAMA,EAAE,UAAYF,CAAO,EAEtD,OAAKC,EAIEA,EAAM,IAAI,OACdb,GAAWA,EAAO,WAAW,UAAU,GAAK,CAACD,EAAeC,CAAM,CACrE,EALS,CAAC,CAMZ,CAQA,eAAsBe,EACpBR,EACkB,CAClB,OAAOD,EAAcC,CAAO,CAC9B,CASA,eAAsBS,EACpBJ,EACAL,EAC4B,CAE5B,OADe,MAAMD,EAAcC,CAAO,GAC5B,KAAMO,GAAMA,EAAE,UAAYF,CAAO,CACjD,CAKO,SAASK,GAAmB,CACjCpB,EAAQ,IACV","names":["chainlist_exports","__export","clearCache","getAllChains","getChainById","getEndpoints","__toCommonJS","DEFAULT_CHAINS_URL","cache","API_KEY_PATTERNS","requiresApiKey","rpcUrl","pattern","fetchChains","chainsJsonUrl","DEFAULT_CHAINS_URL","response","getChainsData","options","cacheDuration","now","data","getEndpoints","chainId","chain","c","getAllChains","getChainById","clearCache"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/chainlist/index.ts"],"sourcesContent":["/**\n * Chainlist module for fetching RPC endpoints\n */\n\nimport type { Chain, ChainlistOptions } from \"./types\";\n\nconst DEFAULT_CHAINS_URL = \"https://chainid.network/chains.json\";\nconst DEFAULT_CACHE_DURATION = 3600000; // 1 hour\n\ninterface CacheEntry {\n data: Chain[];\n timestamp: number;\n}\n\nlet cache: CacheEntry | null = null;\n\n/**\n * Patterns that indicate an RPC URL requires an API key\n */\nconst API_KEY_PATTERNS = [\n /\\$\\{[\\w_]+\\}/i, // ${INFURA_API_KEY}, ${API_KEY}, etc.\n /\\{[\\w_]+\\}/i, // {API_KEY}, {INFURA_API_KEY}, etc.\n /<[\\w_]+>/i, // <API_KEY>, <INFURA_API_KEY>, etc.\n /YOUR[-_]?API[-_]?KEY/i,\n /INSERT[-_]?API[-_]?KEY/i,\n /API[-_]?KEY[-_]?HERE/i,\n];\n\n/**\n * Check if an RPC URL requires an API key\n */\nfunction requiresApiKey(rpcUrl: string): boolean {\n return API_KEY_PATTERNS.some((pattern) => pattern.test(rpcUrl));\n}\n\n/**\n * Fetch all chains data from chainid.network\n */\nasync function fetchChains(\n chainsJsonUrl: string = DEFAULT_CHAINS_URL\n): Promise<Chain[]> {\n const response = await fetch(chainsJsonUrl);\n\n if (!response.ok) {\n throw new Error(\n `Failed to fetch chains data: ${response.status} ${response.statusText}`\n );\n }\n\n return await response.json();\n}\n\n/**\n * Get all chains data with caching\n */\nasync function getChainsData(options?: ChainlistOptions): Promise<Chain[]> {\n const chainsJsonUrl = options?.chainsJsonUrl ?? DEFAULT_CHAINS_URL;\n const cacheDuration = options?.cacheDuration ?? DEFAULT_CACHE_DURATION;\n const now = Date.now();\n\n // Check if cache is valid\n if (cache && now - cache.timestamp < cacheDuration) {\n return cache.data;\n }\n\n // Fetch fresh data\n const data = await fetchChains(chainsJsonUrl);\n cache = { data, timestamp: now };\n\n return data;\n}\n\n/**\n * Get RPC endpoints for a specific chain ID, excluding those that require API keys\n *\n * @param chainId - The chain ID to get endpoints for\n * @param options - Optional configuration\n * @returns Array of RPC URLs that don't require API keys\n *\n * @example\n * ```typescript\n * import { getEndpoints } from 'w3pk/chainlist'\n *\n * // Get Ethereum mainnet RPCs\n * const endpoints = await getEndpoints(1)\n * console.log(endpoints)\n * // [\n * // \"https://api.mycryptoapi.com/eth\",\n * // \"https://cloudflare-eth.com\",\n * // \"https://ethereum-rpc.publicnode.com\",\n * // ...\n * // ]\n * ```\n */\nexport async function getEndpoints(\n chainId: number,\n options?: ChainlistOptions\n): Promise<string[]> {\n const chains = await getChainsData(options);\n const chain = chains.find((c) => c.chainId === chainId);\n\n if (!chain) {\n return [];\n }\n\n
|
|
1
|
+
{"version":3,"sources":["../../src/chainlist/index.ts"],"sourcesContent":["/**\n * Chainlist module for fetching RPC endpoints\n */\n\nimport type { Chain, ChainlistOptions } from \"./types\";\n\nconst DEFAULT_CHAINS_URL = \"https://chainid.network/chains.json\";\nconst DEFAULT_CACHE_DURATION = 3600000; // 1 hour\n\ninterface CacheEntry {\n data: Chain[];\n timestamp: number;\n}\n\nlet cache: CacheEntry | null = null;\n\n/**\n * Patterns that indicate an RPC URL requires an API key\n */\nconst API_KEY_PATTERNS = [\n /\\$\\{[\\w_]+\\}/i, // ${INFURA_API_KEY}, ${API_KEY}, etc.\n /\\{[\\w_]+\\}/i, // {API_KEY}, {INFURA_API_KEY}, etc.\n /<[\\w_]+>/i, // <API_KEY>, <INFURA_API_KEY>, etc.\n /YOUR[-_]?API[-_]?KEY/i,\n /INSERT[-_]?API[-_]?KEY/i,\n /API[-_]?KEY[-_]?HERE/i,\n];\n\n/**\n * Check if an RPC URL requires an API key\n */\nfunction requiresApiKey(rpcUrl: string): boolean {\n return API_KEY_PATTERNS.some((pattern) => pattern.test(rpcUrl));\n}\n\n/**\n * Fetch all chains data from chainid.network\n */\nasync function fetchChains(\n chainsJsonUrl: string = DEFAULT_CHAINS_URL\n): Promise<Chain[]> {\n const response = await fetch(chainsJsonUrl);\n\n if (!response.ok) {\n throw new Error(\n `Failed to fetch chains data: ${response.status} ${response.statusText}`\n );\n }\n\n return await response.json();\n}\n\n/**\n * Get all chains data with caching\n */\nasync function getChainsData(options?: ChainlistOptions): Promise<Chain[]> {\n const chainsJsonUrl = options?.chainsJsonUrl ?? DEFAULT_CHAINS_URL;\n const cacheDuration = options?.cacheDuration ?? DEFAULT_CACHE_DURATION;\n const now = Date.now();\n\n // Check if cache is valid\n if (cache && now - cache.timestamp < cacheDuration) {\n return cache.data;\n }\n\n // Fetch fresh data\n const data = await fetchChains(chainsJsonUrl);\n cache = { data, timestamp: now };\n\n return data;\n}\n\n/**\n * Get the HTTPS RPC endpoints for a specific chain ID, excluding those that require API keys\n *\n * @param chainId - The chain ID to get endpoints for\n * @param options - Optional configuration\n * @returns Array of HTTPS RPC URLs that don't require API keys\n *\n * @example\n * ```typescript\n * import { getEndpoints } from 'w3pk/chainlist'\n *\n * // Get Ethereum mainnet RPCs\n * const endpoints = await getEndpoints(1)\n * console.log(endpoints)\n * // [\n * // \"https://api.mycryptoapi.com/eth\",\n * // \"https://cloudflare-eth.com\",\n * // \"https://ethereum-rpc.publicnode.com\",\n * // ...\n * // ]\n * ```\n */\nexport async function getEndpoints(\n chainId: number,\n options?: ChainlistOptions\n): Promise<string[]> {\n const chains = await getChainsData(options);\n const chain = chains.find((c) => c.chainId === chainId);\n\n if (!chain) {\n return [];\n }\n\n return chain.rpc.filter(\n (rpcUrl) => rpcUrl.startsWith(\"https://\") && !requiresApiKey(rpcUrl)\n );\n}\n\n/**\n * Get all available chains\n *\n * @param options - Optional configuration\n * @returns Array of all chains\n */\nexport async function getAllChains(\n options?: ChainlistOptions\n): Promise<Chain[]> {\n return getChainsData(options);\n}\n\n/**\n * Get chain information by chain ID\n *\n * @param chainId - The chain ID to get information for\n * @param options - Optional configuration\n * @returns Chain information or undefined if not found\n */\nexport async function getChainById(\n chainId: number,\n options?: ChainlistOptions\n): Promise<Chain | undefined> {\n const chains = await getChainsData(options);\n return chains.find((c) => c.chainId === chainId);\n}\n\n/**\n * Clear the chains data cache\n */\nexport function clearCache(): void {\n cache = null;\n}\n\n// Export types\nexport type { Chain, ChainlistOptions } from \"./types\";\n"],"mappings":"AAMA,IAAMA,EAAqB,sCAQ3B,IAAIC,EAA2B,KAKzBC,EAAmB,CACvB,gBACA,cACA,YACA,wBACA,0BACA,uBACF,EAKA,SAASC,EAAeC,EAAyB,CAC/C,OAAOF,EAAiB,KAAMG,GAAYA,EAAQ,KAAKD,CAAM,CAAC,CAChE,CAKA,eAAeE,EACbC,EAAwBC,EACN,CAClB,IAAMC,EAAW,MAAM,MAAMF,CAAa,EAE1C,GAAI,CAACE,EAAS,GACZ,MAAM,IAAI,MACR,gCAAgCA,EAAS,MAAM,IAAIA,EAAS,UAAU,EACxE,EAGF,OAAO,MAAMA,EAAS,KAAK,CAC7B,CAKA,eAAeC,EAAcC,EAA8C,CACzE,IAAMJ,EAAgBI,GAAS,eAAiBH,EAC1CI,EAAgBD,GAAS,eAAiB,KAC1CE,EAAM,KAAK,IAAI,EAGrB,GAAIZ,GAASY,EAAMZ,EAAM,UAAYW,EACnC,OAAOX,EAAM,KAIf,IAAMa,EAAO,MAAMR,EAAYC,CAAa,EAC5C,OAAAN,EAAQ,CAAE,KAAAa,EAAM,UAAWD,CAAI,EAExBC,CACT,CAwBA,eAAsBC,EACpBC,EACAL,EACmB,CAEnB,IAAMM,GADS,MAAMP,EAAcC,CAAO,GACrB,KAAMO,GAAMA,EAAE,UAAYF,CAAO,EAEtD,OAAKC,EAIEA,EAAM,IAAI,OACdb,GAAWA,EAAO,WAAW,UAAU,GAAK,CAACD,EAAeC,CAAM,CACrE,EALS,CAAC,CAMZ,CAQA,eAAsBe,EACpBR,EACkB,CAClB,OAAOD,EAAcC,CAAO,CAC9B,CASA,eAAsBS,EACpBJ,EACAL,EAC4B,CAE5B,OADe,MAAMD,EAAcC,CAAO,GAC5B,KAAMO,GAAMA,EAAE,UAAYF,CAAO,CACjD,CAKO,SAASK,GAAmB,CACjCpB,EAAQ,IACV","names":["DEFAULT_CHAINS_URL","cache","API_KEY_PATTERNS","requiresApiKey","rpcUrl","pattern","fetchChains","chainsJsonUrl","DEFAULT_CHAINS_URL","response","getChainsData","options","cacheDuration","now","data","getEndpoints","chainId","chain","c","getAllChains","getChainById","clearCache"]}
|