@alchemy/common 0.0.0-alpha.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (212) hide show
  1. package/LICENSE +21 -0
  2. package/dist/esm/actions/addBreadCrumb.d.ts +14 -0
  3. package/dist/esm/actions/addBreadCrumb.js +27 -0
  4. package/dist/esm/actions/addBreadCrumb.js.map +1 -0
  5. package/dist/esm/chains.d.ts +234 -0
  6. package/dist/esm/chains.js +113 -0
  7. package/dist/esm/chains.js.map +1 -0
  8. package/dist/esm/errors/AccountNotFoundError.d.ts +10 -0
  9. package/dist/esm/errors/AccountNotFoundError.js +19 -0
  10. package/dist/esm/errors/AccountNotFoundError.js.map +1 -0
  11. package/dist/esm/errors/BaseError.d.ts +23 -0
  12. package/dist/esm/errors/BaseError.js +40 -0
  13. package/dist/esm/errors/BaseError.js.map +1 -0
  14. package/dist/esm/errors/ChainNotFoundError.d.ts +11 -0
  15. package/dist/esm/errors/ChainNotFoundError.js +19 -0
  16. package/dist/esm/errors/ChainNotFoundError.js.map +1 -0
  17. package/dist/esm/errors/ConnectionConfigError.d.ts +13 -0
  18. package/dist/esm/errors/ConnectionConfigError.js +25 -0
  19. package/dist/esm/errors/ConnectionConfigError.js.map +1 -0
  20. package/dist/esm/errors/FetchError.d.ts +15 -0
  21. package/dist/esm/errors/FetchError.js +25 -0
  22. package/dist/esm/errors/FetchError.js.map +1 -0
  23. package/dist/esm/errors/InvalidRequestError.d.ts +13 -0
  24. package/dist/esm/errors/InvalidRequestError.js +22 -0
  25. package/dist/esm/errors/InvalidRequestError.js.map +1 -0
  26. package/dist/esm/errors/MethodUnsupportedError.d.ts +13 -0
  27. package/dist/esm/errors/MethodUnsupportedError.js +21 -0
  28. package/dist/esm/errors/MethodUnsupportedError.js.map +1 -0
  29. package/dist/esm/errors/ServerError.d.ts +15 -0
  30. package/dist/esm/errors/ServerError.js +25 -0
  31. package/dist/esm/errors/ServerError.js.map +1 -0
  32. package/dist/esm/index.d.ts +29 -0
  33. package/dist/esm/index.js +27 -0
  34. package/dist/esm/index.js.map +1 -0
  35. package/dist/esm/logging/config.d.ts +190 -0
  36. package/dist/esm/logging/config.js +279 -0
  37. package/dist/esm/logging/config.js.map +1 -0
  38. package/dist/esm/logging/index.d.ts +6 -0
  39. package/dist/esm/logging/index.js +5 -0
  40. package/dist/esm/logging/index.js.map +1 -0
  41. package/dist/esm/logging/local.d.ts +10 -0
  42. package/dist/esm/logging/local.js +35 -0
  43. package/dist/esm/logging/local.js.map +1 -0
  44. package/dist/esm/logging/logger.d.ts +80 -0
  45. package/dist/esm/logging/logger.js +111 -0
  46. package/dist/esm/logging/logger.js.map +1 -0
  47. package/dist/esm/logging/noop.d.ts +6 -0
  48. package/dist/esm/logging/noop.js +12 -0
  49. package/dist/esm/logging/noop.js.map +1 -0
  50. package/dist/esm/logging/sinks.d.ts +90 -0
  51. package/dist/esm/logging/sinks.js +111 -0
  52. package/dist/esm/logging/sinks.js.map +1 -0
  53. package/dist/esm/logging/types.d.ts +96 -0
  54. package/dist/esm/logging/types.js +2 -0
  55. package/dist/esm/logging/types.js.map +1 -0
  56. package/dist/esm/logging/utils.d.ts +7 -0
  57. package/dist/esm/logging/utils.js +21 -0
  58. package/dist/esm/logging/utils.js.map +1 -0
  59. package/dist/esm/rest/restClient.d.ts +34 -0
  60. package/dist/esm/rest/restClient.js +55 -0
  61. package/dist/esm/rest/restClient.js.map +1 -0
  62. package/dist/esm/rest/types.d.ts +24 -0
  63. package/dist/esm/rest/types.js +2 -0
  64. package/dist/esm/rest/types.js.map +1 -0
  65. package/dist/esm/tracing/traceHeader.d.ts +82 -0
  66. package/dist/esm/tracing/traceHeader.js +145 -0
  67. package/dist/esm/tracing/traceHeader.js.map +1 -0
  68. package/dist/esm/tracing/updateHeaders.d.ts +24 -0
  69. package/dist/esm/tracing/updateHeaders.js +61 -0
  70. package/dist/esm/tracing/updateHeaders.js.map +1 -0
  71. package/dist/esm/transport/alchemy.d.ts +110 -0
  72. package/dist/esm/transport/alchemy.js +164 -0
  73. package/dist/esm/transport/alchemy.js.map +1 -0
  74. package/dist/esm/transport/chainRegistry.d.ts +31 -0
  75. package/dist/esm/transport/chainRegistry.js +95 -0
  76. package/dist/esm/transport/chainRegistry.js.map +1 -0
  77. package/dist/esm/transport/connection.d.ts +20 -0
  78. package/dist/esm/transport/connection.js +2 -0
  79. package/dist/esm/transport/connection.js.map +1 -0
  80. package/dist/esm/transport/connectionSchema.d.ts +124 -0
  81. package/dist/esm/transport/connectionSchema.js +121 -0
  82. package/dist/esm/transport/connectionSchema.js.map +1 -0
  83. package/dist/esm/utils/assertNever.d.ts +8 -0
  84. package/dist/esm/utils/assertNever.js +12 -0
  85. package/dist/esm/utils/assertNever.js.map +1 -0
  86. package/dist/esm/utils/bigint.d.ts +24 -0
  87. package/dist/esm/utils/bigint.js +37 -0
  88. package/dist/esm/utils/bigint.js.map +1 -0
  89. package/dist/esm/utils/createEip1193HandlerFactory.d.ts +18 -0
  90. package/dist/esm/utils/createEip1193HandlerFactory.js +11 -0
  91. package/dist/esm/utils/createEip1193HandlerFactory.js.map +1 -0
  92. package/dist/esm/utils/headers.d.ts +7 -0
  93. package/dist/esm/utils/headers.js +29 -0
  94. package/dist/esm/utils/headers.js.map +1 -0
  95. package/dist/esm/utils/lowerAddress.d.ts +8 -0
  96. package/dist/esm/utils/lowerAddress.js +9 -0
  97. package/dist/esm/utils/lowerAddress.js.map +1 -0
  98. package/dist/esm/utils/raise.d.ts +8 -0
  99. package/dist/esm/utils/raise.js +14 -0
  100. package/dist/esm/utils/raise.js.map +1 -0
  101. package/dist/esm/utils/types.d.ts +10 -0
  102. package/dist/esm/utils/types.js +2 -0
  103. package/dist/esm/utils/types.js.map +1 -0
  104. package/dist/esm/version.d.ts +1 -0
  105. package/dist/esm/version.js +4 -0
  106. package/dist/esm/version.js.map +1 -0
  107. package/dist/types/actions/addBreadCrumb.d.ts +15 -0
  108. package/dist/types/actions/addBreadCrumb.d.ts.map +1 -0
  109. package/dist/types/chains.d.ts +235 -0
  110. package/dist/types/chains.d.ts.map +1 -0
  111. package/dist/types/errors/AccountNotFoundError.d.ts +11 -0
  112. package/dist/types/errors/AccountNotFoundError.d.ts.map +1 -0
  113. package/dist/types/errors/BaseError.d.ts +24 -0
  114. package/dist/types/errors/BaseError.d.ts.map +1 -0
  115. package/dist/types/errors/ChainNotFoundError.d.ts +12 -0
  116. package/dist/types/errors/ChainNotFoundError.d.ts.map +1 -0
  117. package/dist/types/errors/ConnectionConfigError.d.ts +14 -0
  118. package/dist/types/errors/ConnectionConfigError.d.ts.map +1 -0
  119. package/dist/types/errors/FetchError.d.ts +16 -0
  120. package/dist/types/errors/FetchError.d.ts.map +1 -0
  121. package/dist/types/errors/InvalidRequestError.d.ts +14 -0
  122. package/dist/types/errors/InvalidRequestError.d.ts.map +1 -0
  123. package/dist/types/errors/MethodUnsupportedError.d.ts +14 -0
  124. package/dist/types/errors/MethodUnsupportedError.d.ts.map +1 -0
  125. package/dist/types/errors/ServerError.d.ts +16 -0
  126. package/dist/types/errors/ServerError.d.ts.map +1 -0
  127. package/dist/types/index.d.ts +30 -0
  128. package/dist/types/index.d.ts.map +1 -0
  129. package/dist/types/logging/config.d.ts +191 -0
  130. package/dist/types/logging/config.d.ts.map +1 -0
  131. package/dist/types/logging/index.d.ts +7 -0
  132. package/dist/types/logging/index.d.ts.map +1 -0
  133. package/dist/types/logging/local.d.ts +11 -0
  134. package/dist/types/logging/local.d.ts.map +1 -0
  135. package/dist/types/logging/logger.d.ts +81 -0
  136. package/dist/types/logging/logger.d.ts.map +1 -0
  137. package/dist/types/logging/noop.d.ts +7 -0
  138. package/dist/types/logging/noop.d.ts.map +1 -0
  139. package/dist/types/logging/sinks.d.ts +91 -0
  140. package/dist/types/logging/sinks.d.ts.map +1 -0
  141. package/dist/types/logging/types.d.ts +97 -0
  142. package/dist/types/logging/types.d.ts.map +1 -0
  143. package/dist/types/logging/utils.d.ts +8 -0
  144. package/dist/types/logging/utils.d.ts.map +1 -0
  145. package/dist/types/rest/restClient.d.ts +35 -0
  146. package/dist/types/rest/restClient.d.ts.map +1 -0
  147. package/dist/types/rest/types.d.ts +25 -0
  148. package/dist/types/rest/types.d.ts.map +1 -0
  149. package/dist/types/tracing/traceHeader.d.ts +83 -0
  150. package/dist/types/tracing/traceHeader.d.ts.map +1 -0
  151. package/dist/types/tracing/updateHeaders.d.ts +25 -0
  152. package/dist/types/tracing/updateHeaders.d.ts.map +1 -0
  153. package/dist/types/transport/alchemy.d.ts +111 -0
  154. package/dist/types/transport/alchemy.d.ts.map +1 -0
  155. package/dist/types/transport/chainRegistry.d.ts +32 -0
  156. package/dist/types/transport/chainRegistry.d.ts.map +1 -0
  157. package/dist/types/transport/connection.d.ts +21 -0
  158. package/dist/types/transport/connection.d.ts.map +1 -0
  159. package/dist/types/transport/connectionSchema.d.ts +125 -0
  160. package/dist/types/transport/connectionSchema.d.ts.map +1 -0
  161. package/dist/types/utils/assertNever.d.ts +9 -0
  162. package/dist/types/utils/assertNever.d.ts.map +1 -0
  163. package/dist/types/utils/bigint.d.ts +25 -0
  164. package/dist/types/utils/bigint.d.ts.map +1 -0
  165. package/dist/types/utils/createEip1193HandlerFactory.d.ts +19 -0
  166. package/dist/types/utils/createEip1193HandlerFactory.d.ts.map +1 -0
  167. package/dist/types/utils/headers.d.ts +8 -0
  168. package/dist/types/utils/headers.d.ts.map +1 -0
  169. package/dist/types/utils/lowerAddress.d.ts +9 -0
  170. package/dist/types/utils/lowerAddress.d.ts.map +1 -0
  171. package/dist/types/utils/raise.d.ts +9 -0
  172. package/dist/types/utils/raise.d.ts.map +1 -0
  173. package/dist/types/utils/types.d.ts +11 -0
  174. package/dist/types/utils/types.d.ts.map +1 -0
  175. package/dist/types/version.d.ts +2 -0
  176. package/dist/types/version.d.ts.map +1 -0
  177. package/package.json +67 -0
  178. package/src/actions/addBreadCrumb.ts +38 -0
  179. package/src/chains.ts +118 -0
  180. package/src/errors/AccountNotFoundError.ts +16 -0
  181. package/src/errors/BaseError.ts +51 -0
  182. package/src/errors/ChainNotFoundError.ts +15 -0
  183. package/src/errors/ConnectionConfigError.ts +22 -0
  184. package/src/errors/FetchError.ts +21 -0
  185. package/src/errors/InvalidRequestError.ts +19 -0
  186. package/src/errors/MethodUnsupportedError.ts +17 -0
  187. package/src/errors/ServerError.ts +21 -0
  188. package/src/index.ts +60 -0
  189. package/src/logging/config.ts +365 -0
  190. package/src/logging/index.ts +20 -0
  191. package/src/logging/local.ts +39 -0
  192. package/src/logging/logger.ts +194 -0
  193. package/src/logging/noop.ts +13 -0
  194. package/src/logging/sinks.ts +115 -0
  195. package/src/logging/types.ts +111 -0
  196. package/src/logging/utils.ts +31 -0
  197. package/src/rest/restClient.ts +64 -0
  198. package/src/rest/types.ts +42 -0
  199. package/src/tracing/traceHeader.ts +154 -0
  200. package/src/tracing/updateHeaders.ts +66 -0
  201. package/src/transport/alchemy.ts +242 -0
  202. package/src/transport/chainRegistry.ts +115 -0
  203. package/src/transport/connection.ts +19 -0
  204. package/src/transport/connectionSchema.ts +145 -0
  205. package/src/utils/assertNever.ts +12 -0
  206. package/src/utils/bigint.ts +58 -0
  207. package/src/utils/createEip1193HandlerFactory.ts +25 -0
  208. package/src/utils/headers.ts +48 -0
  209. package/src/utils/lowerAddress.ts +10 -0
  210. package/src/utils/raise.ts +14 -0
  211. package/src/utils/types.ts +14 -0
  212. package/src/version.ts +3 -0
@@ -0,0 +1,242 @@
1
+ import {
2
+ createTransport,
3
+ http,
4
+ type Chain,
5
+ type EIP1193RequestFn,
6
+ type HttpTransportConfig,
7
+ type RpcSchema,
8
+ type Transport,
9
+ } from "viem";
10
+ import { BaseError } from "../errors/BaseError.js";
11
+ import { mutateRemoveTrackingHeaders } from "../tracing/updateHeaders.js";
12
+ import { ChainNotFoundError } from "../errors/ChainNotFoundError.js";
13
+ import { getAlchemyRpcUrl } from "./chainRegistry.js";
14
+ import {
15
+ convertHeadersToObject,
16
+ withAlchemyHeaders,
17
+ } from "../utils/headers.js";
18
+
19
+ /**
20
+ * Configuration options for the Alchemy transport.
21
+ * Extends viem's HttpTransportConfig with Alchemy-specific options while omitting
22
+ * options that are not relevant or supported by Alchemy.
23
+ */
24
+ export interface AlchemyTransportConfig
25
+ extends Omit<
26
+ HttpTransportConfig,
27
+ "batch" | "key" | "methods" | "name" | "raw"
28
+ > {
29
+ /** API key for Alchemy authentication */
30
+ apiKey?: string;
31
+ /** JWT token for authentication */
32
+ jwt?: string;
33
+ /** Custom RPC URL (optional - defaults to chain's Alchemy URL, but can be used to override the chain's Alchemy URL) */
34
+ url?: string;
35
+ }
36
+
37
+ type AlchemyTransportBase<rpcSchema extends RpcSchema | undefined = undefined> =
38
+ Transport<
39
+ "alchemyHttp",
40
+ {
41
+ alchemyRpcUrl: string;
42
+ fetchOptions: AlchemyTransportConfig["fetchOptions"];
43
+ config: AlchemyTransportConfig;
44
+ },
45
+ EIP1193RequestFn<rpcSchema>
46
+ >;
47
+
48
+ export type AlchemyTransport<
49
+ rpcSchema extends RpcSchema | undefined = undefined,
50
+ > = AlchemyTransportBase<rpcSchema> & {
51
+ updateHeaders(newHeaders: HeadersInit): void;
52
+ };
53
+
54
+ /**
55
+ * A type guard for the transport to determine if it is an Alchemy transport.
56
+ * Used in cases where we would like to do switching depending on the transport.
57
+ *
58
+ * @param {Transport} transport The transport to check
59
+ * @param {Chain} chain Chain for the transport to run its function to return the transport config
60
+ * @returns {boolean} `true` if the transport is an Alchemy transport, otherwise `false`
61
+ */
62
+ export function isAlchemyTransport(
63
+ transport: Transport,
64
+ chain: Chain,
65
+ ): transport is AlchemyTransport {
66
+ return transport({ chain }).config.type === "alchemyHttp";
67
+ }
68
+
69
+ /**
70
+ * Creates an Alchemy HTTP transport for connecting to Alchemy's services.
71
+ *
72
+ * @example
73
+ * Using API Key:
74
+ * ```ts
75
+ * import { alchemyTransport } from "@alchemy/common";
76
+ *
77
+ * const transport = alchemyTransport({ apiKey: "your-api-key" });
78
+ * ```
79
+ *
80
+ * @example
81
+ * Using JWT:
82
+ * ```ts
83
+ * import { alchemyTransport } from "@alchemy/common";
84
+ *
85
+ * const transport = alchemyTransport({ jwt: "your-jwt-token" });
86
+ * ```
87
+ *
88
+ * @example
89
+ * Using URL directly:
90
+ * ```ts
91
+ * import { alchemyTransport } from "@alchemy/common";
92
+ *
93
+ * const transport = alchemyTransport({ url: "https://eth-mainnet.g.alchemy.com/v2/your-key" });
94
+ * ```
95
+ *
96
+ * @example
97
+ * Using custom URL with API key:
98
+ * ```ts
99
+ * import { alchemyTransport } from "@alchemy/common";
100
+ *
101
+ * const transport = alchemyTransport({
102
+ * url: "https://custom-alchemy.com/v2",
103
+ * apiKey: "your-api-key"
104
+ * });
105
+ * ```
106
+ *
107
+ * @example
108
+ * Using custom URL with JWT:
109
+ * ```ts
110
+ * import { alchemyTransport } from "@alchemy/common";
111
+ *
112
+ * const transport = alchemyTransport({
113
+ * url: "https://custom-alchemy.com/v2",
114
+ * jwt: "your-jwt-token"
115
+ * });
116
+ * ```
117
+ *
118
+ * @example
119
+ * Using HTTP debugging options:
120
+ * ```ts
121
+ * import { alchemyTransport } from "@alchemy/common";
122
+ *
123
+ * const transport = alchemyTransport({
124
+ * apiKey: "your-api-key",
125
+ * onFetchRequest: (request) => console.log("Request:", request),
126
+ * onFetchResponse: (response) => console.log("Response:", response),
127
+ * timeout: 30000,
128
+ * retryCount: 3,
129
+ * retryDelay: 1000
130
+ * });
131
+ * ```
132
+ *
133
+ * @param {AlchemyTransportConfig} config - The configuration object for the Alchemy transport (extends viem's HttpTransportConfig)
134
+ * @param {string} [config.apiKey] - API key for Alchemy authentication
135
+ * @param {string} [config.jwt] - JWT token for authentication
136
+ * @param {string} [config.url] - Direct URL to Alchemy endpoint or a proxy URL
137
+ * @param {Function} [config.onFetchRequest] - Callback for debugging outgoing requests
138
+ * @param {Function} [config.onFetchResponse] - Callback for debugging responses
139
+ * @param {number} [config.timeout] - Request timeout in milliseconds
140
+ * @param {number} [config.retryCount] - The number of retry attempts
141
+ * @param {number} [config.retryDelay] - The delay between retries, in milliseconds
142
+ * @param {object} [config.fetchOptions] - Optional fetch options for HTTP requests
143
+ * @param {object} [config.httpOptions] - HTTP transport options for debugging (onFetchRequest, onFetchResponse, timeout, batch)
144
+ * @returns {AlchemyTransport} The configured Alchemy transport function
145
+ */
146
+ export function alchemyTransport<
147
+ rpcSchema extends RpcSchema | undefined = undefined,
148
+ >(config: AlchemyTransportConfig): AlchemyTransport<rpcSchema> {
149
+ const {
150
+ apiKey,
151
+ jwt,
152
+ url,
153
+ fetchOptions: fetchOptions_,
154
+ retryCount,
155
+ retryDelay,
156
+ ...httpTransportConfig
157
+ } = config;
158
+
159
+ // Create a copy of fetch options for modification
160
+ const fetchOptions = { ...fetchOptions_ };
161
+
162
+ fetchOptions.headers = withAlchemyHeaders({
163
+ headers: fetchOptions.headers,
164
+ apiKey,
165
+ jwt,
166
+ });
167
+
168
+ const transport: AlchemyTransportBase = (opts) => {
169
+ const { chain } = opts;
170
+
171
+ mutateRemoveTrackingHeaders(config?.fetchOptions?.headers);
172
+
173
+ const rpcUrl = (() => {
174
+ if (url) {
175
+ return url;
176
+ }
177
+
178
+ if (!chain) {
179
+ throw new ChainNotFoundError();
180
+ }
181
+
182
+ const alchemyUrl = getAlchemyRpcUrl(chain.id);
183
+ if (alchemyUrl) {
184
+ return alchemyUrl;
185
+ }
186
+
187
+ // Fallback: check for legacy alchemy RPC URLs in chain definition
188
+ if (chain.rpcUrls?.alchemy?.http?.[0]) {
189
+ return chain.rpcUrls.alchemy.http[0];
190
+ }
191
+
192
+ throw new BaseError(
193
+ `Chain ${chain.id} (${chain.name}) is not supported by Alchemy. To use this chain:\n\n` +
194
+ `1. Use a direct RPC URL:\n` +
195
+ ` alchemyTransport({ url: "https://your-alchemy-endpoint.com/v2/your-key" })\n\n` +
196
+ `2. Add alchemy RPC to your chain definition:\n` +
197
+ ` defineChain({ rpcUrls: { alchemy: { http: ["https://your-alchemy-url/v2"] }}})\n\n` +
198
+ `3. Or use a standard RPC provider:\n` +
199
+ ` import { http } from "viem";\n` +
200
+ ` http("https://your-standard-rpc.com")`,
201
+ );
202
+ })();
203
+
204
+ const innerTransport = http(rpcUrl, {
205
+ // Standard viem options are passed through to the underlying transport, with
206
+ // the exception of retryCount and retryDelay because those are handled by
207
+ // the outer Alchemy transport.
208
+ ...httpTransportConfig,
209
+ fetchOptions,
210
+ // Retry count must be 0 here in order to respect the retry
211
+ // count that is already specified on the underlying transport.
212
+ retryCount: 0,
213
+ });
214
+
215
+ return createTransport(
216
+ {
217
+ key: "alchemyHttp",
218
+ name: "Alchemy HTTP Transport",
219
+ request: innerTransport(opts).request,
220
+ retryCount: retryCount ?? opts?.retryCount,
221
+ retryDelay,
222
+ type: "alchemyHttp",
223
+ },
224
+ {
225
+ alchemyRpcUrl: rpcUrl,
226
+ fetchOptions,
227
+ config,
228
+ },
229
+ );
230
+ };
231
+
232
+ return Object.assign(transport, {
233
+ updateHeaders(newHeaders_: HeadersInit) {
234
+ const newHeaders = convertHeadersToObject(newHeaders_);
235
+
236
+ fetchOptions.headers = {
237
+ ...fetchOptions.headers,
238
+ ...newHeaders,
239
+ };
240
+ },
241
+ });
242
+ }
@@ -0,0 +1,115 @@
1
+ /**
2
+ * Internal registry mapping chain IDs to Alchemy RPC base URLs.
3
+ * This replaces the need for custom chain exports with embedded Alchemy URLs.
4
+ */
5
+ export const ALCHEMY_RPC_MAPPING: Record<number, string> = {
6
+ // Ethereum networks
7
+ 1: "https://eth-mainnet.g.alchemy.com/v2", // mainnet
8
+ 11155111: "https://eth-sepolia.g.alchemy.com/v2", // sepolia
9
+ 5: "https://eth-goerli.g.alchemy.com/v2", // goerli
10
+
11
+ // Arbitrum networks
12
+ 42161: "https://arb-mainnet.g.alchemy.com/v2", // arbitrum
13
+ 421613: "https://arb-goerli.g.alchemy.com/v2", // arbitrumGoerli
14
+ 421614: "https://arb-sepolia.g.alchemy.com/v2", // arbitrumSepolia
15
+
16
+ // Optimism networks
17
+ 10: "https://opt-mainnet.g.alchemy.com/v2", // optimism
18
+ 420: "https://opt-goerli.g.alchemy.com/v2", // optimismGoerli
19
+ 11155420: "https://opt-sepolia.g.alchemy.com/v2", // optimismSepolia
20
+
21
+ // Base networks
22
+ 8453: "https://base-mainnet.g.alchemy.com/v2", // base
23
+ 84531: "https://base-goerli.g.alchemy.com/v2", // baseGoerli
24
+ 84532: "https://base-sepolia.g.alchemy.com/v2", // baseSepolia
25
+
26
+ // Polygon networks
27
+ 137: "https://polygon-mainnet.g.alchemy.com/v2", // polygon
28
+ 80001: "https://polygon-mumbai.g.alchemy.com/v2", // polygonMumbai
29
+ 80002: "https://polygon-amoy.g.alchemy.com/v2", // polygonAmoy
30
+
31
+ // World Chain networks
32
+ 480: "https://worldchain-mainnet.g.alchemy.com/v2", // worldChain
33
+ 4801: "https://worldchain-sepolia.g.alchemy.com/v2", // worldChainSepolia
34
+
35
+ // Shape networks
36
+ 360: "https://shape-mainnet.g.alchemy.com/v2", // shape
37
+ 11011: "https://shape-sepolia.g.alchemy.com/v2", // shapeSepolia
38
+
39
+ // Unichain networks
40
+ 130: "https://unichain-mainnet.g.alchemy.com/v2", // unichainMainnet
41
+ 1301: "https://unichain-sepolia.g.alchemy.com/v2", // unichainSepolia
42
+
43
+ // Soneium networks
44
+ 1868: "https://soneium-mainnet.g.alchemy.com/v2", // soneiumMainnet
45
+ 1946: "https://soneium-minato.g.alchemy.com/v2", // soneiumMinato
46
+
47
+ // OPBNB networks
48
+ 204: "https://opbnb-mainnet.g.alchemy.com/v2", // opbnbMainnet
49
+ 5611: "https://opbnb-testnet.g.alchemy.com/v2", // opbnbTestnet
50
+
51
+ // BeraChain networks
52
+ 80084: "https://berachain-bartio.g.alchemy.com/v2", // beraChainBartio
53
+
54
+ // Ink networks
55
+ 57073: "https://ink-mainnet.g.alchemy.com/v2", // inkMainnet
56
+ 763373: "https://ink-sepolia.g.alchemy.com/v2", // inkSepolia
57
+
58
+ // Monad networks
59
+ 10143: "https://monad-testnet.g.alchemy.com/v2", // monadTestnet
60
+
61
+ // Openloot networks
62
+ 905905: "https://openloot-sepolia.g.alchemy.com/v2", // openlootSepolia
63
+
64
+ // Gensyn networks
65
+ 685685: "https://gensyn-testnet.g.alchemy.com/v2", // gensynTestnet
66
+
67
+ // Rise networks
68
+ 11155931: "https://rise-testnet.g.alchemy.com/v2", // riseTestnet
69
+
70
+ // Story networks
71
+ 1514: "https://story-mainnet.g.alchemy.com/v2", // storyMainnet
72
+ 1315: "https://story-aeneid.g.alchemy.com/v2", // storyAeneid
73
+
74
+ // Celo networks
75
+ 42220: "https://celo-mainnet.g.alchemy.com/v2", // celoMainnet
76
+ 44787: "https://celo-alfajores.g.alchemy.com/v2", // celoAlfajores
77
+
78
+ // Tea networks
79
+ 10218: "https://tea-sepolia.g.alchemy.com/v2", // teaSepolia
80
+ };
81
+
82
+ /**
83
+ * Gets the Alchemy RPC base URL for a given chain ID.
84
+ *
85
+ * @param {number} chainId The chain ID to lookup
86
+ * @returns {string | undefined} The Alchemy RPC base URL or undefined if not supported
87
+ *
88
+ * @example
89
+ * ```ts
90
+ * const rpcUrl = getAlchemyRpcUrl(1); // "https://eth-mainnet.g.alchemy.com/v2"
91
+ * const customUrl = getAlchemyRpcUrl(999); // undefined
92
+ * ```
93
+ */
94
+ export function getAlchemyRpcUrl(chainId: number): string | undefined {
95
+ return ALCHEMY_RPC_MAPPING[chainId];
96
+ }
97
+
98
+ /**
99
+ * Checks if a chain ID is supported by the Alchemy RPC registry.
100
+ *
101
+ * @param {number} chainId The chain ID to check
102
+ * @returns {boolean} True if the chain is supported, false otherwise
103
+ */
104
+ export function isChainSupported(chainId: number): boolean {
105
+ return chainId in ALCHEMY_RPC_MAPPING;
106
+ }
107
+
108
+ /**
109
+ * Gets all supported chain IDs from the registry.
110
+ *
111
+ * @returns {number[]} Array of supported chain IDs
112
+ */
113
+ export function getSupportedChainIds(): number[] {
114
+ return Object.keys(ALCHEMY_RPC_MAPPING).map(Number);
115
+ }
@@ -0,0 +1,19 @@
1
+ import type { Never } from "../utils/types";
2
+
3
+ // TODO(v5): remove this file and use connectionSchema.ts instead once other packages are migrated over
4
+ type AlchemyConnectionBaseConfig =
5
+ // TODO(v5): is this really the best devex? can we do better?
6
+ // basic configuration for connecting to alchemy.
7
+ // proxyUrl is used when making calls to the developer backend.
8
+ | { proxyUrl: string; apiKey?: never; jwt?: never }
9
+ | { proxyUrl?: never; apiKey: string; jwt?: never }
10
+ | { proxyUrl?: never; apiKey?: never; jwt: string };
11
+
12
+ type AAOnlyChainConfig = {
13
+ alchemyConnection: AlchemyConnectionBaseConfig;
14
+ nodeRpcUrl: string;
15
+ };
16
+
17
+ export type AlchemyConnectionConfig =
18
+ | (AlchemyConnectionBaseConfig & Never<AAOnlyChainConfig>)
19
+ | (AAOnlyChainConfig & Never<AlchemyConnectionBaseConfig>);
@@ -0,0 +1,145 @@
1
+ import { z } from "zod";
2
+ import { ConnectionConfigError } from "../errors/ConnectionConfigError.js";
3
+
4
+ /**
5
+ * Alchemy Connection Configuration Schema
6
+ *
7
+ * Provides three authentication options for connecting to Alchemy services:
8
+ *
9
+ * 1. **API Key**: Authenticate using your Alchemy API key
10
+ * 2. **JWT**: Authenticate using a JWT token
11
+ * 3. **URL**: Connect directly using a full RPC URL
12
+ *
13
+ * @example
14
+ * Using API Key (uses chain's Alchemy URL):
15
+ * ```ts
16
+ * { apiKey: 'abc123' }
17
+ * ```
18
+ *
19
+ * @example
20
+ * Using JWT (uses chain's Alchemy URL):
21
+ * ```ts
22
+ * { jwt: 'eyJhbGc...' }
23
+ * ```
24
+ *
25
+ * @example
26
+ * Using direct URL only:
27
+ * ```ts
28
+ * { url: 'https://eth-mainnet.g.alchemy.com/v2/your-key' }
29
+ * ```
30
+ *
31
+ * @example
32
+ * Using custom URL with API key:
33
+ * ```ts
34
+ * { url: 'https://custom-alchemy.com/v2', apiKey: 'abc123' }
35
+ * ```
36
+ *
37
+ * @example
38
+ * Using custom URL with JWT:
39
+ * ```ts
40
+ * { url: 'https://custom-alchemy.com/v2', jwt: 'eyJhbGc...' }
41
+ * ```
42
+ */
43
+
44
+ /**
45
+ * Main connection configuration allowing flexible combinations.
46
+ * Can specify URL, auth method, or both together.
47
+ */
48
+ export const AlchemyConnectionConfigSchema = z
49
+ .object({
50
+ /** API key for Alchemy authentication */
51
+ apiKey: z.string().min(1, "API key cannot be empty").optional(),
52
+ /** JWT token for authentication */
53
+ jwt: z.string().min(1, "JWT cannot be empty").optional(),
54
+ /** Custom RPC URL (optional - defaults to chain's Alchemy URL) */
55
+ url: z.string().url("Invalid URL format").optional(),
56
+ })
57
+ .strict()
58
+ .refine(
59
+ (data) => {
60
+ // Must have at least one field
61
+ return data.apiKey || data.jwt || data.url;
62
+ },
63
+ {
64
+ message: "Must specify at least one of: apiKey, jwt, or url",
65
+ },
66
+ )
67
+ .refine(
68
+ (data) => {
69
+ // Cannot have both apiKey and jwt
70
+ return !(data.apiKey && data.jwt);
71
+ },
72
+ {
73
+ message:
74
+ "Cannot specify both apiKey and jwt - choose only one authentication method",
75
+ },
76
+ );
77
+
78
+ /**
79
+ * TypeScript type derived from the schema for external consumption.
80
+ * This provides clean type inference without exposing Zod implementation details.
81
+ */
82
+ export type AlchemyConnectionConfig = z.infer<
83
+ typeof AlchemyConnectionConfigSchema
84
+ >;
85
+
86
+ /**
87
+ * Validates an Alchemy connection configuration object.
88
+ *
89
+ * @param {unknown} config - The configuration object to validate
90
+ * @returns {AlchemyConnectionConfig} The validated configuration object
91
+ * @throws {ConnectionConfigError} If the configuration is invalid
92
+ *
93
+ * @example
94
+ * ```ts
95
+ * try {
96
+ * const config = validateAlchemyConnectionConfig({
97
+ * apiKey: 'your-api-key'
98
+ * });
99
+ * // config is now typed as AlchemyConnectionConfig
100
+ * } catch (error) {
101
+ * if (error instanceof ConnectionConfigError) {
102
+ * console.error('Invalid config:', error.message);
103
+ * }
104
+ * }
105
+ * ```
106
+ */
107
+ export function validateAlchemyConnectionConfig(
108
+ config: unknown,
109
+ ): AlchemyConnectionConfig {
110
+ const result = AlchemyConnectionConfigSchema.safeParse(config);
111
+
112
+ if (!result.success) {
113
+ const firstError = result.error.issues[0];
114
+ const details = firstError?.message || "Invalid connection configuration";
115
+ throw new ConnectionConfigError(details);
116
+ }
117
+
118
+ return result.data;
119
+ }
120
+
121
+ /**
122
+ * Type guard to check if a value is a valid Alchemy connection config.
123
+ *
124
+ * @param {unknown} value - The value to check for validity
125
+ * @returns {boolean} True if the value is a valid Alchemy connection config
126
+ *
127
+ * @example
128
+ * ```ts
129
+ * const maybeConfig: unknown = { apiKey: 'test' };
130
+ * if (isAlchemyConnectionConfig(maybeConfig)) {
131
+ * // TypeScript knows maybeConfig is AlchemyConnectionConfig here
132
+ * if (maybeConfig.apiKey) {
133
+ * console.log('Using API key:', maybeConfig.apiKey);
134
+ * }
135
+ * if (maybeConfig.url) {
136
+ * console.log('Using custom URL:', maybeConfig.url);
137
+ * }
138
+ * }
139
+ * ```
140
+ */
141
+ export function isAlchemyConnectionConfig(
142
+ value: unknown,
143
+ ): value is AlchemyConnectionConfig {
144
+ return AlchemyConnectionConfigSchema.safeParse(value).success;
145
+ }
@@ -0,0 +1,12 @@
1
+ import { BaseError } from "../errors/BaseError.js";
2
+
3
+ /**
4
+ * Asserts that a value is never.
5
+ *
6
+ * @param {never} _x - The value to assert.
7
+ * @param {string} msg - The message to throw if the value is not never.
8
+ * @returns {never} Always throws an error.
9
+ */
10
+ export const assertNever = (_x: never, msg: string): never => {
11
+ throw new BaseError(msg);
12
+ };
@@ -0,0 +1,58 @@
1
+ /**
2
+ * BigNumberish represents values that can be converted to BigInt
3
+ */
4
+ export type BigNumberish = string | number | bigint;
5
+
6
+ /**
7
+ * Multiplier configuration for bigint multiplication
8
+ */
9
+ export type Multiplier = {
10
+ multiplier: number;
11
+ };
12
+
13
+ export enum RoundingMode {
14
+ ROUND_DOWN = 0,
15
+ ROUND_UP = 1,
16
+ }
17
+
18
+ /**
19
+ * Validates if a multiplier has acceptable precision (max 4 decimal places)
20
+ *
21
+ * @param {Multiplier} multiplier - the multiplier to validate
22
+ * @returns {boolean} true if valid, false otherwise
23
+ */
24
+ function isValidMultiplier(multiplier: Multiplier): boolean {
25
+ const decimalPlaces =
26
+ multiplier.multiplier.toString().split(".")[1]?.length ?? 0;
27
+ return decimalPlaces <= 4;
28
+ }
29
+
30
+ /**
31
+ * Given a bigint and a number (which can be a float), returns the bigint value.
32
+ * Note: this function has loss and will round down to the nearest integer.
33
+ *
34
+ * @param {BigNumberish} base - the number to be multiplied
35
+ * @param {number} multiplier - the amount to multiply by
36
+ * @param {RoundingMode} roundingMode - the rounding mode to use when calculating the percent. defaults to ROUND_UP
37
+ * @returns {bigint} the bigint value of the multiplication with the number rounded by the rounding mode
38
+ */
39
+ export const bigIntMultiply = (
40
+ base: BigNumberish,
41
+ multiplier: Multiplier["multiplier"],
42
+ roundingMode: RoundingMode = RoundingMode.ROUND_UP,
43
+ ) => {
44
+ if (!isValidMultiplier({ multiplier })) {
45
+ throw new Error(
46
+ "bigIntMultiply requires a multiplier validated number as the second argument (max 4 decimal places)",
47
+ );
48
+ }
49
+
50
+ // Get decimal places of b. Max decimal places is defined by the validation above.
51
+ const decimalPlaces = multiplier.toString().split(".")[1]?.length ?? 0;
52
+ const result =
53
+ BigInt(base) * BigInt(Math.round(multiplier * 10 ** decimalPlaces));
54
+ return roundingMode === RoundingMode.ROUND_UP &&
55
+ result % BigInt(10 ** decimalPlaces) > 0
56
+ ? result / BigInt(10 ** decimalPlaces) + 1n
57
+ : result / BigInt(10 ** decimalPlaces);
58
+ };
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Creates a typed handler factory for EIP1193 request methods.
3
+ * This helps with Viem's typing within custom EIP1193 request functions by
4
+ * automatically casting input params and ensuring the result matches what is required.
5
+ *
6
+ * @returns {(params: unknown) => unknown} A function that creates a handler factory for specific methods
7
+ */
8
+ export const createEip1193HandlerFactory =
9
+ <
10
+ TMethods extends readonly {
11
+ Method: string;
12
+ Parameters?: unknown;
13
+ ReturnType?: unknown;
14
+ }[],
15
+ >() =>
16
+ <TMethod extends TMethods[number]["Method"]>(
17
+ handle: (
18
+ params: Extract<TMethods[number], { Method: TMethod }>["Parameters"],
19
+ ) => Promise<Extract<TMethods[number], { Method: TMethod }>["ReturnType"]>,
20
+ ) =>
21
+ (params: unknown) => {
22
+ return handle(
23
+ params as Extract<TMethods[number], { Method: TMethod }>["Parameters"],
24
+ );
25
+ };
@@ -0,0 +1,48 @@
1
+ import { VERSION } from "../version.js";
2
+
3
+ export type WithAlchemyHeadersParams = {
4
+ headers?: HeadersInit;
5
+ apiKey?: string;
6
+ jwt?: string;
7
+ };
8
+
9
+ export function withAlchemyHeaders({
10
+ headers,
11
+ apiKey,
12
+ jwt,
13
+ }: WithAlchemyHeadersParams): Record<string, string> {
14
+ const bearerToken = jwt ?? apiKey;
15
+ return {
16
+ ...convertHeadersToObject(headers),
17
+ "Alchemy-AA-Sdk-Version": VERSION,
18
+ ...(bearerToken ? { Authorization: `Bearer ${bearerToken}` } : {}),
19
+ };
20
+ }
21
+
22
+ export function convertHeadersToObject(
23
+ headers?: HeadersInit,
24
+ ): Record<string, string> {
25
+ if (!headers) {
26
+ return {};
27
+ }
28
+
29
+ if (headers instanceof Headers) {
30
+ const headersObject = {} as Record<string, string>;
31
+ headers.forEach((value, key) => {
32
+ headersObject[key] = value;
33
+ });
34
+ return headersObject;
35
+ }
36
+
37
+ if (Array.isArray(headers)) {
38
+ return headers.reduce(
39
+ (acc, header) => {
40
+ acc[header[0]] = header[1];
41
+ return acc;
42
+ },
43
+ {} as Record<string, string>,
44
+ );
45
+ }
46
+
47
+ return headers;
48
+ }
@@ -0,0 +1,10 @@
1
+ import { type Address } from "viem";
2
+
3
+ /**
4
+ * Lowercase an address
5
+ *
6
+ * @param {Address} addr - The address to lowercase
7
+ * @returns {Address} The lowercase address
8
+ */
9
+ export const lowerAddress = (addr: Address): Address =>
10
+ addr.toLowerCase() as Address;
@@ -0,0 +1,14 @@
1
+ import { BaseError } from "viem";
2
+
3
+ /**
4
+ * Raises an error.
5
+ *
6
+ * @param {string | BaseError} err - The error to raise.
7
+ * @returns {never} Always throws an error.
8
+ */
9
+ export const raise = (err: string | BaseError): never => {
10
+ if (typeof err === "string") {
11
+ throw new BaseError(err);
12
+ }
13
+ throw err;
14
+ };