w3pk 0.5.2 → 0.7.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/README.md CHANGED
@@ -5,7 +5,7 @@
5
5
 
6
6
  Passwordless Web3 authentication SDK with encrypted wallets and privacy features.
7
7
 
8
- **Demo:** https://d2u.w3hc.org/voting
8
+ **Demo: [w3pk.w3hc.org](https://w3pk.w3hc.org)**
9
9
 
10
10
  ## Install
11
11
  ```bash
@@ -16,35 +16,38 @@ npm install w3pk ethers
16
16
  ```typescript
17
17
  import { createWeb3Passkey } from 'w3pk'
18
18
 
19
- const w3pk = createWeb3Passkey({
20
- apiBaseUrl: 'https://webauthn.w3hc.org'
21
- })
19
+ const w3pk = createWeb3Passkey()
22
20
 
23
- // Register
24
- await w3pk.register({
25
- username: 'alice',
26
- ethereumAddress: '0x...'
27
- })
21
+ // Register (auto-generates wallet and stores it securely)
22
+ const { mnemonic } = await w3pk.register({ username: 'alice' })
23
+ console.log('⚠️ Save this recovery phrase:', mnemonic)
28
24
 
29
- // Login
25
+ // Login (for subsequent sessions)
30
26
  await w3pk.login()
31
27
 
32
28
  // Sign message
33
29
  const signature = await w3pk.signMessage('Hello World')
34
30
 
35
31
  // Derive addresses
36
- const wallet = await w3pk.deriveWallet(0) // First address
37
- const wallet2 = await w3pk.deriveWallet(1) // Second address
32
+ const wallet0 = await w3pk.deriveWallet(0)
33
+ const wallet1 = await w3pk.deriveWallet(1)
34
+
35
+ // Get RPC endpoints for any chain
36
+ const endpoints = await w3pk.getEndpoints(1) // Ethereum
37
+ const rpcUrl = endpoints[0]
38
38
  ```
39
39
 
40
40
  ## Features
41
41
 
42
42
  **Core (Included)**
43
43
  - 🔐 Passwordless authentication (WebAuthn/FIDO2)
44
- - 💰 Encrypted wallet management (AES-GCM-256)
44
+ - 🔒 Client-only biometric-gated wallet encryption (AES-GCM-256)
45
+ - ⏱️ Session management (configurable duration, prevents repeated prompts)
45
46
  - 🌱 HD wallet generation (BIP39/BIP44)
46
47
  - 🔢 Multi-address derivation
47
- - 🥷 Stealth addresses (privacy-preserving transactions)
48
+ - 🥷 ERC-5564 stealth addresses (privacy-preserving transactions with view tags)
49
+ - 🔗 Chainlist support (2390+ networks, auto-filtered RPC endpoints)
50
+ - ⚡ EIP-7702 network detection (329+ supported networks)
48
51
 
49
52
  **Optional: Zero-Knowledge Proofs**
50
53
 
@@ -53,39 +56,41 @@ Requires additional dependencies (~70MB):
53
56
  npm install snarkjs circomlibjs
54
57
  ```
55
58
 
56
- See [ZK Integration Guide](./docs/ZK_INTEGRATION_GUIDE.md) for:
57
- - Anonymous membership proofs
58
- - Private balance verification
59
- - Range proofs without revealing values
60
- - NFT ownership proofs
61
-
62
- [Bundle size comparison →](./docs/BUNDLE_SIZES.md)
59
+ See [ZK Integration Guide](./docs/ZK_INTEGRATION_GUIDE.md) to get started.
63
60
 
64
61
  ## API
65
62
 
66
- ### Authentication
63
+ ### Authentication Flow
64
+
67
65
  ```typescript
68
- // Register new user
69
- await w3pk.register({ username, ethereumAddress })
66
+ // Register (auto-stores wallet)
67
+ const { mnemonic } = await w3pk.register({ username: 'alice' })
68
+ // Returns: { mnemonic } - SAVE THIS!
70
69
 
71
- // Login (usernameless)
70
+ // Subsequent sessions: just login
72
71
  await w3pk.login()
73
72
 
74
73
  // Logout
75
74
  await w3pk.logout()
76
75
 
77
- // Check status
76
+ // Status
78
77
  w3pk.isAuthenticated
79
78
  w3pk.user
80
79
  ```
81
80
 
82
- ### Wallet
81
+ **Advanced: Pre-generate wallet (optional)**
83
82
  ```typescript
84
- // Generate wallet
85
- const wallet = await w3pk.generateWallet()
83
+ // If you want to see the wallet before registering:
84
+ const { mnemonic } = await w3pk.generateWallet()
85
+ const { mnemonic } = await w3pk.register({ username: 'alice' })
86
+ // register() will use the pre-generated wallet
87
+ ```
88
+
89
+ ### Wallet Operations
86
90
 
87
- // Derive HD wallet addresses
88
- const derived = await w3pk.deriveWallet(index)
91
+ ```typescript
92
+ // Derive addresses
93
+ const wallet0 = await w3pk.deriveWallet(0)
89
94
  // Returns: { address, privateKey }
90
95
 
91
96
  // Export mnemonic
@@ -95,35 +100,146 @@ const mnemonic = await w3pk.exportMnemonic()
95
100
  const signature = await w3pk.signMessage(message)
96
101
  ```
97
102
 
98
- ### Stealth Addresses
103
+ ### Session Management
104
+
105
+ By default, after authentication, operations work for 1 hour without repeated biometric prompts:
106
+
107
+ ```typescript
108
+ // Configure session duration
109
+ const w3pk = createWeb3Passkey({
110
+ sessionDuration: 2 // 2 hours (default: 1)
111
+ })
112
+
113
+ // After login, mnemonic is cached in memory
114
+ await w3pk.login()
115
+
116
+ // These operations use the cached session
117
+ await w3pk.deriveWallet(0)
118
+ await w3pk.exportMnemonic()
119
+ await w3pk.signMessage('Hello')
120
+ await w3pk.stealth.getKeys()
121
+
122
+ // Check session status
123
+ w3pk.hasActiveSession() // true
124
+ w3pk.getSessionRemainingTime() // 3540 seconds
125
+
126
+ // Extend session
127
+ w3pk.extendSession() // Adds 2 more hours
128
+
129
+ // Clear session manually (force re-authentication)
130
+ w3pk.clearSession()
131
+
132
+ // Disable sessions (most secure - prompt every time)
133
+ const w3pk = createWeb3Passkey({ sessionDuration: 0 })
134
+ ```
135
+
136
+ #### Force Authentication for Sensitive Operations
137
+
138
+ Even with an active session, you can require fresh biometric authentication for specific operations:
139
+
140
+ ```typescript
141
+ // Session is active, but force authentication anyway
142
+ await w3pk.exportMnemonic({ requireAuth: true })
143
+ await w3pk.signMessage('Transfer $1000', { requireAuth: true })
144
+ await w3pk.deriveWallet(5, { requireAuth: true })
145
+ await w3pk.stealth.getKeys({ requireAuth: true })
146
+
147
+ // Example: Require auth for high-value transactions
148
+ async function transferFunds(amount: number, recipient: string) {
149
+ // For transfers above $100, require fresh authentication
150
+ const requireAuth = amount > 100
151
+
152
+ const signature = await w3pk.signMessage(
153
+ `Transfer ${amount} to ${recipient}`,
154
+ { requireAuth }
155
+ )
156
+
157
+ // ... submit transaction
158
+ }
159
+ ```
160
+
161
+ ### RPC Endpoints
162
+ ```typescript
163
+ // Get public RPC endpoints
164
+ const endpoints = await w3pk.getEndpoints(1) // Ethereum
165
+ const rpcUrl = endpoints[0]
166
+
167
+ // Other chains
168
+ await w3pk.getEndpoints(10) // Optimism
169
+ await w3pk.getEndpoints(42161) // Arbitrum
170
+ await w3pk.getEndpoints(8453) // Base
171
+
172
+ // Use with ethers.js
173
+ import { ethers } from 'ethers'
174
+
175
+ const endpoints = await w3pk.getEndpoints(137)
176
+ const provider = new ethers.JsonRpcProvider(endpoints[0])
177
+ const blockNumber = await provider.getBlockNumber()
178
+ ```
179
+
180
+ ### EIP-7702 Support
181
+ ```typescript
182
+ // Check network support
183
+ const supported = await w3pk.supportsEIP7702(1) // true
184
+
185
+ // Configure RPC testing
186
+ await w3pk.supportsEIP7702(999, {
187
+ maxEndpoints: 5,
188
+ timeout: 5000
189
+ })
190
+ ```
191
+
192
+ ### ERC-5564 Stealth Addresses
193
+
99
194
  ```typescript
100
195
  const w3pk = createWeb3Passkey({
101
- apiBaseUrl: 'https://webauthn.w3hc.org',
102
196
  stealthAddresses: {}
103
197
  })
104
198
 
105
- // Generate unlinkable address
106
- const stealth = await w3pk.stealth?.generateStealthAddress()
107
- // Returns: { stealthAddress, stealthPrivateKey, ephemeralPublicKey }
199
+ // Get stealth meta-address
200
+ const metaAddress = await w3pk.stealth?.getStealthMetaAddress()
201
+
202
+ // Generate stealth address
203
+ const announcement = await w3pk.stealth?.generateStealthAddress()
204
+
205
+ // Parse announcement
206
+ const result = await w3pk.stealth?.parseAnnouncement({
207
+ stealthAddress: announcement.stealthAddress,
208
+ ephemeralPublicKey: announcement.ephemeralPublicKey,
209
+ viewTag: announcement.viewTag
210
+ })
211
+
212
+ if (result.isForUser) {
213
+ // Use private key to spend funds
214
+ console.log('Private key:', result.stealthPrivateKey)
215
+ }
108
216
 
109
- // Get master keys
110
- const keys = await w3pk.stealth?.getKeys()
111
- // Returns: { metaAddress, viewingKey, spendingKey }
217
+ // Scan announcements
218
+ const myPayments = await w3pk.stealth?.scanAnnouncements(announcements)
112
219
  ```
113
220
 
114
- ## Documentation
221
+ ## Browser Compatibility
222
+
223
+ **Supported (95%+ of users):**
224
+ - Chrome 67+ (May 2018), Edge 18+ (Nov 2018), Firefox 60+ (May 2018), Safari 14+ (Sep 2020)
225
+ - iOS 14.5+ (April 2021), Android 9+ (August 2018)
226
+ - Windows Hello, Touch ID, Face ID, platform authenticators
115
227
 
116
- - [Quick Start Guide](./docs/QUICK_START.md)
117
- - [ZK Integration Guide](./docs/ZK_INTEGRATION_GUIDE.md)
118
- - [Bundle Size Comparison](./docs/BUNDLE_SIZES.md)
228
+ **Not Supported:**
229
+ - Safari < 14, Chrome/Firefox < 2018, IE11, Android < 9, iOS < 14.5
230
+ - Private/Incognito mode (credentials don't persist)
231
+ - WebView/embedded browsers (often disabled)
119
232
 
120
- ## Examples
233
+ ## Documentation
121
234
 
122
- - [Basic Authentication](./examples/basic-auth.ts)
123
- - [Wallet Management](./examples/wallet-demo.ts)
124
- - [Stealth Addresses](./examples/stealth-demo.ts)
125
- - [ZK Proofs](./examples/zk-proof-demo.ts) (requires ZK deps)
126
- - [NFT Ownership](./examples/nft-ownership-proof.ts) (requires ZK deps)
235
+ - [Quick Start Guide](./docs/QUICK_START.md) - Get started in 5 minutes
236
+ - [Browser Compatibility](./docs/BROWSER_COMPATIBILITY.md) - Supported browsers, devices, and OS versions
237
+ - [Security Architecture](./docs/SECURITY.md) - Integration best practices
238
+ - [ERC-5564 Stealth Addresses](./docs/ERC5564_STEALTH_ADDRESSES.md) - Complete guide with examples
239
+ - [ERC-5564 Flow Diagrams](./docs/ERC5564_FLOW_DIAGRAM.md) - Visual explanations of how stealth addresses work
240
+ - [RPC Endpoints](./docs/CHAINLIST.md) - Chainlist integration guide
241
+ - [ZK Integration Guide](./docs/ZK_INTEGRATION_GUIDE.md) - Zero-knowledge proofs (optional)
242
+ - [Bundle Size Comparison](./docs/BUNDLE_SIZES.md) - Core vs ZK bundle sizes
127
243
 
128
244
  ## Contributing
129
245
 
@@ -0,0 +1,96 @@
1
+ /**
2
+ * Chainlist types
3
+ */
4
+ interface Chain {
5
+ name: string;
6
+ chain: string;
7
+ icon?: string;
8
+ rpc: string[];
9
+ features?: Array<{
10
+ name: string;
11
+ }>;
12
+ faucets: string[];
13
+ nativeCurrency: {
14
+ name: string;
15
+ symbol: string;
16
+ decimals: number;
17
+ };
18
+ infoURL: string;
19
+ shortName: string;
20
+ chainId: number;
21
+ networkId: number;
22
+ slip44?: number;
23
+ ens?: {
24
+ registry: string;
25
+ };
26
+ explorers?: Array<{
27
+ name: string;
28
+ url: string;
29
+ icon?: string;
30
+ standard: string;
31
+ }>;
32
+ title?: string;
33
+ status?: string;
34
+ redFlags?: string[];
35
+ }
36
+ interface ChainlistOptions {
37
+ /**
38
+ * Custom URL for chains.json data
39
+ * @default 'https://chainid.network/chains.json'
40
+ */
41
+ chainsJsonUrl?: string;
42
+ /**
43
+ * Cache duration in milliseconds
44
+ * @default 3600000 (1 hour)
45
+ */
46
+ cacheDuration?: number;
47
+ }
48
+
49
+ /**
50
+ * Chainlist module for fetching RPC endpoints
51
+ */
52
+
53
+ /**
54
+ * Get RPC endpoints for a specific chain ID, excluding those that require API keys
55
+ *
56
+ * @param chainId - The chain ID to get endpoints for
57
+ * @param options - Optional configuration
58
+ * @returns Array of RPC URLs that don't require API keys
59
+ *
60
+ * @example
61
+ * ```typescript
62
+ * import { getEndpoints } from 'w3pk/chainlist'
63
+ *
64
+ * // Get Ethereum mainnet RPCs
65
+ * const endpoints = await getEndpoints(1)
66
+ * console.log(endpoints)
67
+ * // [
68
+ * // "https://api.mycryptoapi.com/eth",
69
+ * // "https://cloudflare-eth.com",
70
+ * // "https://ethereum-rpc.publicnode.com",
71
+ * // ...
72
+ * // ]
73
+ * ```
74
+ */
75
+ declare function getEndpoints(chainId: number, options?: ChainlistOptions): Promise<string[]>;
76
+ /**
77
+ * Get all available chains
78
+ *
79
+ * @param options - Optional configuration
80
+ * @returns Array of all chains
81
+ */
82
+ declare function getAllChains(options?: ChainlistOptions): Promise<Chain[]>;
83
+ /**
84
+ * Get chain information by chain ID
85
+ *
86
+ * @param chainId - The chain ID to get information for
87
+ * @param options - Optional configuration
88
+ * @returns Chain information or undefined if not found
89
+ */
90
+ declare function getChainById(chainId: number, options?: ChainlistOptions): Promise<Chain | undefined>;
91
+ /**
92
+ * Clear the chains data cache
93
+ */
94
+ declare function clearCache(): void;
95
+
96
+ export { type Chain, type ChainlistOptions, clearCache, getAllChains, getChainById, getEndpoints };
@@ -0,0 +1,96 @@
1
+ /**
2
+ * Chainlist types
3
+ */
4
+ interface Chain {
5
+ name: string;
6
+ chain: string;
7
+ icon?: string;
8
+ rpc: string[];
9
+ features?: Array<{
10
+ name: string;
11
+ }>;
12
+ faucets: string[];
13
+ nativeCurrency: {
14
+ name: string;
15
+ symbol: string;
16
+ decimals: number;
17
+ };
18
+ infoURL: string;
19
+ shortName: string;
20
+ chainId: number;
21
+ networkId: number;
22
+ slip44?: number;
23
+ ens?: {
24
+ registry: string;
25
+ };
26
+ explorers?: Array<{
27
+ name: string;
28
+ url: string;
29
+ icon?: string;
30
+ standard: string;
31
+ }>;
32
+ title?: string;
33
+ status?: string;
34
+ redFlags?: string[];
35
+ }
36
+ interface ChainlistOptions {
37
+ /**
38
+ * Custom URL for chains.json data
39
+ * @default 'https://chainid.network/chains.json'
40
+ */
41
+ chainsJsonUrl?: string;
42
+ /**
43
+ * Cache duration in milliseconds
44
+ * @default 3600000 (1 hour)
45
+ */
46
+ cacheDuration?: number;
47
+ }
48
+
49
+ /**
50
+ * Chainlist module for fetching RPC endpoints
51
+ */
52
+
53
+ /**
54
+ * Get RPC endpoints for a specific chain ID, excluding those that require API keys
55
+ *
56
+ * @param chainId - The chain ID to get endpoints for
57
+ * @param options - Optional configuration
58
+ * @returns Array of RPC URLs that don't require API keys
59
+ *
60
+ * @example
61
+ * ```typescript
62
+ * import { getEndpoints } from 'w3pk/chainlist'
63
+ *
64
+ * // Get Ethereum mainnet RPCs
65
+ * const endpoints = await getEndpoints(1)
66
+ * console.log(endpoints)
67
+ * // [
68
+ * // "https://api.mycryptoapi.com/eth",
69
+ * // "https://cloudflare-eth.com",
70
+ * // "https://ethereum-rpc.publicnode.com",
71
+ * // ...
72
+ * // ]
73
+ * ```
74
+ */
75
+ declare function getEndpoints(chainId: number, options?: ChainlistOptions): Promise<string[]>;
76
+ /**
77
+ * Get all available chains
78
+ *
79
+ * @param options - Optional configuration
80
+ * @returns Array of all chains
81
+ */
82
+ declare function getAllChains(options?: ChainlistOptions): Promise<Chain[]>;
83
+ /**
84
+ * Get chain information by chain ID
85
+ *
86
+ * @param chainId - The chain ID to get information for
87
+ * @param options - Optional configuration
88
+ * @returns Chain information or undefined if not found
89
+ */
90
+ declare function getChainById(chainId: number, options?: ChainlistOptions): Promise<Chain | undefined>;
91
+ /**
92
+ * Clear the chains data cache
93
+ */
94
+ declare function clearCache(): void;
95
+
96
+ export { type Chain, type ChainlistOptions, clearCache, getAllChains, getChainById, getEndpoints };
@@ -0,0 +1,2 @@
1
+ "use strict";var o=Object.defineProperty;var h=Object.getOwnPropertyDescriptor;var u=Object.getOwnPropertyNames;var C=Object.prototype.hasOwnProperty;var p=(t,n)=>{for(var s in n)o(t,s,{get:n[s],enumerable:!0})},f=(t,n,s,a)=>{if(n&&typeof n=="object"||typeof n=="function")for(let i of u(n))!C.call(t,i)&&i!==s&&o(t,i,{get:()=>n[i],enumerable:!(a=h(n,i))||a.enumerable});return t};var l=t=>f(o({},"__esModule",{value:!0}),t);var I={};p(I,{clearCache:()=>y,getAllChains:()=>w,getChainById:()=>A,getEndpoints:()=>d});module.exports=l(I);var c="https://chainid.network/chains.json";var e=null,m=[/\$\{[\w_]+\}/i,/\{[\w_]+\}/i,/<[\w_]+>/i,/YOUR[-_]?API[-_]?KEY/i,/INSERT[-_]?API[-_]?KEY/i,/API[-_]?KEY[-_]?HERE/i];function E(t){return m.some(n=>n.test(t))}async function _(t=c){let n=await fetch(t);if(!n.ok)throw new Error(`Failed to fetch chains data: ${n.status} ${n.statusText}`);return await n.json()}async function r(t){let n=t?.chainsJsonUrl??c,s=t?.cacheDuration??36e5,a=Date.now();if(e&&a-e.timestamp<s)return e.data;let i=await _(n);return e={data:i,timestamp:a},i}async function d(t,n){let a=(await r(n)).find(i=>i.chainId===t);return a?a.rpc.filter(i=>!E(i)&&!i.startsWith("wss://")&&!i.startsWith("ws://")):[]}async function w(t){return r(t)}async function A(t,n){return(await r(n)).find(a=>a.chainId===t)}function y(){e=null}
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +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"]}
@@ -0,0 +1,2 @@
1
+ var r="https://chainid.network/chains.json";var s=null,c=[/\$\{[\w_]+\}/i,/\{[\w_]+\}/i,/<[\w_]+>/i,/YOUR[-_]?API[-_]?KEY/i,/INSERT[-_]?API[-_]?KEY/i,/API[-_]?KEY[-_]?HERE/i];function h(n){return c.some(t=>t.test(n))}async function u(n=r){let t=await fetch(n);if(!t.ok)throw new Error(`Failed to fetch chains data: ${t.status} ${t.statusText}`);return await t.json()}async function o(n){let t=n?.chainsJsonUrl??r,e=n?.cacheDuration??36e5,a=Date.now();if(s&&a-s.timestamp<e)return s.data;let i=await u(t);return s={data:i,timestamp:a},i}async function C(n,t){let a=(await o(t)).find(i=>i.chainId===n);return a?a.rpc.filter(i=>!h(i)&&!i.startsWith("wss://")&&!i.startsWith("ws://")):[]}async function p(n){return o(n)}async function f(n,t){return(await o(t)).find(a=>a.chainId===n)}function l(){s=null}export{l as clearCache,p as getAllChains,f as getChainById,C as getEndpoints};
2
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +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"]}