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.
Files changed (45) hide show
  1. package/README.md +47 -46
  2. package/dist/chainlist/index.d.mts +2 -2
  3. package/dist/chainlist/index.d.ts +2 -2
  4. package/dist/chainlist/index.js.map +1 -1
  5. package/dist/chainlist/index.mjs.map +1 -1
  6. package/dist/index.d.mts +891 -745
  7. package/dist/index.d.ts +891 -745
  8. package/dist/index.js +1132 -440
  9. package/dist/index.js.map +1 -1
  10. package/dist/index.mjs +1132 -440
  11. package/dist/index.mjs.map +1 -1
  12. package/dist/zk/index.d.mts +177 -4
  13. package/dist/zk/index.d.ts +177 -4
  14. package/dist/zk/index.js +5 -5
  15. package/dist/zk/index.js.map +1 -1
  16. package/dist/zk/index.mjs +5 -5
  17. package/dist/zk/index.mjs.map +1 -1
  18. package/dist/zk/prove-worker.mjs.map +1 -0
  19. package/dist/zk/utils.js +3 -3
  20. package/dist/zk/utils.js.map +1 -1
  21. package/dist/zk/utils.mjs +3 -3
  22. package/dist/zk/utils.mjs.map +1 -1
  23. package/docs/API_REFERENCE.md +509 -636
  24. package/docs/ARCHITECTURE.md +86 -106
  25. package/docs/BUILD_VERIFICATION.md +35 -8
  26. package/docs/EIP-7951.md +1 -8
  27. package/docs/EIP_7702.md +35 -395
  28. package/docs/INTEGRATION_GUIDELINES.md +44 -515
  29. package/docs/JUNE_9_2026_AUDIT.md +207 -0
  30. package/docs/PORTABILITY.md +41 -186
  31. package/docs/POST_QUANTUM.md +61 -39
  32. package/docs/RECOVERY.md +165 -173
  33. package/docs/SECURITY.md +301 -755
  34. package/docs/ZK.md +250 -0
  35. package/package.json +27 -22
  36. package/dist/chainlist/index.js +0 -2
  37. package/dist/chainlist/index.mjs +0 -2
  38. package/docs/notes/IFRAME_IMPLEMENTATION_PLAN.md +0 -197
  39. package/docs/notes/JULY_13_ROADMAP.md +0 -29
  40. package/docs/notes/NOTES.md +0 -333
  41. package/docs/notes/SIWE_IMPLEMENTATION.md +0 -129
  42. package/docs/notes/SYNCING_ACROSS_DEVICES.md +0 -127
  43. package/docs/notes/ethereum-post-quantum-roadmap.md +0 -213
  44. package/docs/notes/old-computer.md +0 -99
  45. 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
- - Post-quantum encryption (ML-KEM-1024/Kyber NIST FIPS 203)
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
- // Check for existing wallet
69
- const hasWallet = await w3pk.hasExistingCredential()
67
+ // List all wallets on device
68
+ const wallets = await w3pk.listExistingCredentials()
70
69
 
71
70
  // Register or login
72
- if (hasWallet) {
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. Applications cannot access `exportMnemonic()`.
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 persistence:** persistent sessions are encrypted under a key derived from the WebAuthn PRF extension (an authenticator-held secret, released only during a biometric/PIN-verified assertion) and re-keyed at every real login. Authenticators without PRF support get in-memory sessions only — there is no weaker at-rest fallback. See [Security Architecture](./docs/SECURITY.md) for details.
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('Private key:', result.stealthPrivateKey)
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
- ### ML-KEM encryption
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
- // Encrypt with post-quantum security (NIST FIPS 203)
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 = 'MyS3cur3!Password@2042'
359
+ const password = 'violet lantern under seven bridges'
358
360
  if (!isStrongPassword(password)) {
359
- throw new Error('Password must be 12+ chars with uppercase, lowercase, number, special char')
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-100
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('password', password)
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
- import { SocialRecoveryManager } from 'w3pk'
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', phone: '+1234567890' },
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 invite = await socialRecovery.generateGuardianInvite(guardians[0])
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 shares - reconstructs encrypted backup file
388
- const { backupFileJson } = await socialRecovery.recoverFromGuardians([share1, share2])
389
- await w3pk.registerWithBackupFile(backupFileJson, password, 'username')
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
- const result = await w3pk.simulateRecoveryScenario({
396
- type: 'lost-device',
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 // Filter out RPC URLs that require API keys and websocket URLs\n return chain.rpc.filter(\n (rpcUrl) =>\n !requiresApiKey(rpcUrl) &&\n !rpcUrl.startsWith(\"wss://\") &&\n !rpcUrl.startsWith(\"ws://\")\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,EAKEA,EAAM,IAAI,OACdb,GACC,CAACD,EAAeC,CAAM,GACtB,CAACA,EAAO,WAAW,QAAQ,GAC3B,CAACA,EAAO,WAAW,OAAO,CAC9B,EATS,CAAC,CAUZ,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
+ {"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 // Filter out RPC URLs that require API keys and websocket URLs\n return chain.rpc.filter(\n (rpcUrl) =>\n !requiresApiKey(rpcUrl) &&\n !rpcUrl.startsWith(\"wss://\") &&\n !rpcUrl.startsWith(\"ws://\")\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,EAKEA,EAAM,IAAI,OACdb,GACC,CAACD,EAAeC,CAAM,GACtB,CAACA,EAAO,WAAW,QAAQ,GAC3B,CAACA,EAAO,WAAW,OAAO,CAC9B,EATS,CAAC,CAUZ,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"]}
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"]}