tapimo 0.0.2 → 0.1.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 (265) hide show
  1. package/README.md +11 -2
  2. package/dist/ApiKey.d.ts +12 -11
  3. package/dist/ApiKey.d.ts.map +1 -1
  4. package/dist/ApiKey.js +10 -12
  5. package/dist/ApiKey.js.map +1 -1
  6. package/dist/ApiKeys.d.ts +139 -0
  7. package/dist/ApiKeys.d.ts.map +1 -0
  8. package/dist/ApiKeys.js +166 -0
  9. package/dist/ApiKeys.js.map +1 -0
  10. package/dist/App.d.ts +920 -910
  11. package/dist/App.d.ts.map +1 -1
  12. package/dist/App.js +36 -12
  13. package/dist/App.js.map +1 -1
  14. package/dist/Handler.d.ts +86 -0
  15. package/dist/Handler.d.ts.map +1 -0
  16. package/dist/Handler.js +83 -0
  17. package/dist/Handler.js.map +1 -0
  18. package/dist/Scope.d.ts +4 -1
  19. package/dist/Scope.d.ts.map +1 -1
  20. package/dist/Scope.js +5 -1
  21. package/dist/Scope.js.map +1 -1
  22. package/dist/Webhooks.d.ts +3 -5
  23. package/dist/Webhooks.d.ts.map +1 -1
  24. package/dist/Webhooks.js +9 -2
  25. package/dist/Webhooks.js.map +1 -1
  26. package/dist/admin/Access.d.ts +1 -3
  27. package/dist/admin/Access.d.ts.map +1 -1
  28. package/dist/admin/Access.js +1 -3
  29. package/dist/admin/Access.js.map +1 -1
  30. package/dist/admin/App.d.ts +98 -209
  31. package/dist/admin/App.d.ts.map +1 -1
  32. package/dist/admin/App.js +2 -4
  33. package/dist/admin/App.js.map +1 -1
  34. package/dist/admin/apps/api-keys.d.ts +7 -5
  35. package/dist/admin/apps/api-keys.d.ts.map +1 -1
  36. package/dist/admin/apps/api-keys.js +8 -9
  37. package/dist/admin/apps/api-keys.js.map +1 -1
  38. package/dist/admin/apps/scopes.d.ts +3 -0
  39. package/dist/admin/apps/scopes.d.ts.map +1 -1
  40. package/dist/admin/apps/verified-tokens.d.ts +101 -215
  41. package/dist/admin/apps/verified-tokens.d.ts.map +1 -1
  42. package/dist/admin/apps/verified-tokens.js +17 -38
  43. package/dist/admin/apps/verified-tokens.js.map +1 -1
  44. package/dist/admin/ui.generated.d.ts +1 -1
  45. package/dist/admin/ui.generated.js +1 -1
  46. package/dist/apps/balances.d.ts +2 -2
  47. package/dist/apps/receipts.d.ts +43 -43
  48. package/dist/apps/tokens.d.ts +454 -454
  49. package/dist/apps/transactions.d.ts +43 -43
  50. package/dist/apps/transfers.d.ts +29 -29
  51. package/dist/apps/webhooks.d.ts +5 -2
  52. package/dist/apps/webhooks.d.ts.map +1 -1
  53. package/dist/apps/webhooks.js +38 -34
  54. package/dist/apps/webhooks.js.map +1 -1
  55. package/dist/{Cli.d.ts → cli/Cli.d.ts} +13 -2
  56. package/dist/cli/Cli.d.ts.map +1 -0
  57. package/dist/cli/Cli.js +19 -0
  58. package/dist/cli/Cli.js.map +1 -0
  59. package/dist/cli/bin.d.ts +3 -0
  60. package/dist/cli/bin.d.ts.map +1 -0
  61. package/dist/cli/bin.js +4 -0
  62. package/dist/cli/bin.js.map +1 -0
  63. package/dist/cli/commands/admin.d.ts +15 -0
  64. package/dist/cli/commands/admin.d.ts.map +1 -0
  65. package/dist/cli/commands/admin.js +60 -0
  66. package/dist/cli/commands/admin.js.map +1 -0
  67. package/dist/cloudflare.d.ts +7 -0
  68. package/dist/cloudflare.d.ts.map +1 -1
  69. package/dist/cloudflare.js +9 -0
  70. package/dist/cloudflare.js.map +1 -1
  71. package/dist/db/Db.d.ts +103 -0
  72. package/dist/db/Db.d.ts.map +1 -0
  73. package/dist/db/Db.js +274 -0
  74. package/dist/db/Db.js.map +1 -0
  75. package/dist/db/Schema.d.ts +88 -0
  76. package/dist/db/Schema.d.ts.map +1 -0
  77. package/dist/db/Schema.js +130 -0
  78. package/dist/db/Schema.js.map +1 -0
  79. package/dist/db/tables/organizations.d.ts +79 -0
  80. package/dist/db/tables/organizations.d.ts.map +1 -0
  81. package/dist/db/tables/organizations.js +81 -0
  82. package/dist/db/tables/organizations.js.map +1 -0
  83. package/dist/db/tables/verifiedTokens.d.ts +53 -0
  84. package/dist/db/tables/verifiedTokens.d.ts.map +1 -0
  85. package/dist/db/tables/verifiedTokens.js +74 -0
  86. package/dist/db/tables/verifiedTokens.js.map +1 -0
  87. package/dist/db/tables/webhookDeliveries.d.ts +71 -0
  88. package/dist/db/tables/webhookDeliveries.d.ts.map +1 -0
  89. package/dist/db/tables/webhookDeliveries.js +119 -0
  90. package/dist/db/tables/webhookDeliveries.js.map +1 -0
  91. package/dist/db/tables/webhookSubscriptions.d.ts +158 -0
  92. package/dist/db/tables/webhookSubscriptions.d.ts.map +1 -0
  93. package/dist/db/tables/webhookSubscriptions.js +272 -0
  94. package/dist/db/tables/webhookSubscriptions.js.map +1 -0
  95. package/dist/handlers/internal/executionError.d.ts +25 -0
  96. package/dist/handlers/internal/executionError.d.ts.map +1 -0
  97. package/dist/handlers/internal/executionError.js +184 -0
  98. package/dist/handlers/internal/executionError.js.map +1 -0
  99. package/dist/handlers/internal/multisig.d.ts +237 -0
  100. package/dist/handlers/internal/multisig.d.ts.map +1 -0
  101. package/dist/handlers/internal/multisig.js +567 -0
  102. package/dist/handlers/internal/multisig.js.map +1 -0
  103. package/dist/handlers/internal/requestListener.d.ts +124 -0
  104. package/dist/handlers/internal/requestListener.d.ts.map +1 -0
  105. package/dist/handlers/internal/requestListener.js +173 -0
  106. package/dist/handlers/internal/requestListener.js.map +1 -0
  107. package/dist/handlers/internal/sponsorship.d.ts +83 -0
  108. package/dist/handlers/internal/sponsorship.d.ts.map +1 -0
  109. package/dist/handlers/internal/sponsorship.js +103 -0
  110. package/dist/handlers/internal/sponsorship.js.map +1 -0
  111. package/dist/handlers/internal/types.d.ts +284 -0
  112. package/dist/handlers/internal/types.d.ts.map +1 -0
  113. package/dist/handlers/internal/types.js +2 -0
  114. package/dist/handlers/internal/types.js.map +1 -0
  115. package/dist/handlers/internal/utils.d.ts +34 -0
  116. package/dist/handlers/internal/utils.d.ts.map +1 -0
  117. package/dist/handlers/internal/utils.js +131 -0
  118. package/dist/handlers/internal/utils.js.map +1 -0
  119. package/dist/handlers/relay.d.ts +119 -0
  120. package/dist/handlers/relay.d.ts.map +1 -0
  121. package/dist/handlers/relay.js +1264 -0
  122. package/dist/handlers/relay.js.map +1 -0
  123. package/dist/index.d.ts +4 -2
  124. package/dist/index.d.ts.map +1 -1
  125. package/dist/index.js +4 -2
  126. package/dist/index.js.map +1 -1
  127. package/dist/internal/Auth.d.ts +16 -10
  128. package/dist/internal/Auth.d.ts.map +1 -1
  129. package/dist/internal/Auth.js +72 -12
  130. package/dist/internal/Auth.js.map +1 -1
  131. package/dist/internal/RpcSchema.d.ts +10 -0
  132. package/dist/internal/RpcSchema.d.ts.map +1 -0
  133. package/dist/internal/RpcSchema.js +2 -0
  134. package/dist/internal/RpcSchema.js.map +1 -0
  135. package/dist/internal/Store.d.ts +42 -3
  136. package/dist/internal/Store.d.ts.map +1 -1
  137. package/dist/internal/Store.js +65 -3
  138. package/dist/internal/Store.js.map +1 -1
  139. package/dist/internal/VerifiedTokens.d.ts +15 -34
  140. package/dist/internal/VerifiedTokens.d.ts.map +1 -1
  141. package/dist/internal/VerifiedTokens.js +115 -159
  142. package/dist/internal/VerifiedTokens.js.map +1 -1
  143. package/dist/internal/Webhooks.d.ts +49 -35
  144. package/dist/internal/Webhooks.d.ts.map +1 -1
  145. package/dist/internal/Webhooks.js +92 -205
  146. package/dist/internal/Webhooks.js.map +1 -1
  147. package/package.json +7 -2
  148. package/src/ApiKey.test.ts +3 -3
  149. package/src/ApiKey.ts +10 -12
  150. package/src/ApiKeys.test.ts +175 -0
  151. package/src/ApiKeys.ts +205 -0
  152. package/src/App.test-d.ts +1 -1
  153. package/src/App.test.ts +10768 -351
  154. package/src/App.ts +79 -36
  155. package/src/Client.test-d.ts +1 -1
  156. package/src/Handler.ts +154 -0
  157. package/src/Scope.ts +5 -1
  158. package/src/Webhooks.test.ts +8 -7
  159. package/src/Webhooks.ts +11 -5
  160. package/src/admin/Access.ts +1 -3
  161. package/src/admin/App.ts +19 -24
  162. package/src/admin/apps/api-keys.test.ts +19 -18
  163. package/src/admin/apps/api-keys.ts +8 -9
  164. package/src/admin/apps/scopes.test.ts +4 -0
  165. package/src/admin/apps/verified-tokens.test.ts +3 -30
  166. package/src/admin/apps/verified-tokens.ts +17 -36
  167. package/src/admin/ui.generated.ts +1 -1
  168. package/src/apps/balances.test.ts +4 -3
  169. package/src/apps/blocks.test.ts +1 -1
  170. package/src/apps/exchanges.test.ts +1 -1
  171. package/src/apps/indexer.test.ts +1 -1
  172. package/src/apps/receipts.test.ts +1 -1
  173. package/src/apps/rpc.test.ts +100 -10
  174. package/src/apps/tokenlist.test.ts +5 -7
  175. package/src/apps/tokens.test.ts +4 -3
  176. package/src/apps/transactions.test.ts +1 -1
  177. package/src/apps/transfers.test.ts +1 -1
  178. package/src/apps/verified-tokens.test.ts +24 -12
  179. package/src/apps/webhooks.test.ts +130 -143
  180. package/src/apps/webhooks.ts +45 -47
  181. package/src/{Cli.test.ts → cli/Cli.test.ts} +10 -9
  182. package/src/{Cli.ts → cli/Cli.ts} +16 -9
  183. package/src/cli/bin.ts +4 -0
  184. package/src/cli/commands/admin.test.ts +71 -0
  185. package/src/cli/commands/admin.ts +60 -0
  186. package/src/cloudflare.ts +10 -0
  187. package/src/db/Db.test.ts +84 -0
  188. package/src/db/Db.ts +383 -0
  189. package/src/db/Schema.ts +164 -0
  190. package/src/db/tables/organizations.test.ts +124 -0
  191. package/src/db/tables/organizations.ts +127 -0
  192. package/src/db/tables/verifiedTokens.ts +111 -0
  193. package/src/db/tables/webhookDeliveries.ts +159 -0
  194. package/src/db/tables/webhookSubscriptions.ts +358 -0
  195. package/src/handlers/internal/executionError.ts +205 -0
  196. package/src/handlers/internal/multisig.localnet.test.ts +146 -0
  197. package/src/handlers/internal/multisig.test.ts +744 -0
  198. package/src/handlers/internal/multisig.ts +858 -0
  199. package/src/handlers/internal/relay.localnet.test.ts +2035 -0
  200. package/src/handlers/internal/requestListener.ts +273 -0
  201. package/src/handlers/internal/sponsorship.ts +186 -0
  202. package/src/handlers/internal/types.ts +378 -0
  203. package/src/handlers/internal/utils.ts +153 -0
  204. package/src/handlers/relay.ts +1605 -0
  205. package/src/index.ts +4 -2
  206. package/src/internal/Auth.test.ts +21 -21
  207. package/src/internal/Auth.ts +92 -19
  208. package/src/internal/RateLimit.test.ts +1 -1
  209. package/src/internal/RpcSchema.ts +10 -0
  210. package/src/internal/Store.test.ts +98 -10
  211. package/src/internal/Store.ts +112 -6
  212. package/src/internal/VerifiedTokens.test.ts +99 -115
  213. package/src/internal/VerifiedTokens.ts +123 -175
  214. package/src/internal/Webhooks.test.ts +107 -107
  215. package/src/internal/Webhooks.ts +102 -220
  216. package/dist/ApiKeySource.d.ts +0 -166
  217. package/dist/ApiKeySource.d.ts.map +0 -1
  218. package/dist/ApiKeySource.js +0 -234
  219. package/dist/ApiKeySource.js.map +0 -1
  220. package/dist/Cli.d.ts.map +0 -1
  221. package/dist/Cli.js +0 -13
  222. package/dist/Cli.js.map +0 -1
  223. package/seeds/31318/icons/0x20c0000000000000000000000000000000000000.svg +0 -1
  224. package/seeds/31318/icons/0x20c0000000000000000000000000000000000001.svg +0 -1
  225. package/seeds/31318/icons/0x20c0000000000000000000000000000000000002.svg +0 -1
  226. package/seeds/31318/icons/0x20c0000000000000000000000000000000000003.svg +0 -1
  227. package/seeds/31318/verified-tokens.json +0 -30
  228. package/seeds/4217/icons/0x20c0000000000000000000000000000000000000.svg +0 -25
  229. package/seeds/4217/icons/0x20c000000000000000000000048c8f36df1c9a4a.svg +0 -1
  230. package/seeds/4217/icons/0x20c0000000000000000000000520792dcccccccc.svg +0 -1
  231. package/seeds/4217/icons/0x20c0000000000000000000000a6da882d075a4c3.svg +0 -4
  232. package/seeds/4217/icons/0x20c000000000000000000000111111111e910f0f.svg +0 -30
  233. package/seeds/4217/icons/0x20c00000000000000000000014f22ca97301eb73.svg +0 -1
  234. package/seeds/4217/icons/0x20c0000000000000000000001621e21f71cf12fb.svg +0 -1
  235. package/seeds/4217/icons/0x20c0000000000000000000002f52d5cc21a3207b.svg +0 -1
  236. package/seeds/4217/icons/0x20c0000000000000000000003158081efd85bfc2.svg +0 -11
  237. package/seeds/4217/icons/0x20c0000000000000000000003554d28269e0f3c2.svg +0 -1
  238. package/seeds/4217/icons/0x20c000000000000000000000383a23bacb546ab9.svg +0 -1
  239. package/seeds/4217/icons/0x20c00000000000000000000042109aef2f8b28e1.png +0 -0
  240. package/seeds/4217/icons/0x20c0000000000000000000005c0bac7cef389a11.svg +0 -1
  241. package/seeds/4217/icons/0x20c0000000000000000000006fd9a167923ba194.png +0 -0
  242. package/seeds/4217/icons/0x20c0000000000000000000007f7ba549dd0251b9.svg +0 -1
  243. package/seeds/4217/icons/0x20c0000000000000000000008191667423f70e67.svg +0 -5
  244. package/seeds/4217/icons/0x20c0000000000000000000008ee4fcff88888888.svg +0 -1
  245. package/seeds/4217/icons/0x20c0000000000000000000009a4a4b17e0dc6651.svg +0 -1
  246. package/seeds/4217/icons/0x20c000000000000000000000ab02d39df30bd17e.svg +0 -1
  247. package/seeds/4217/icons/0x20c000000000000000000000ae247a1130450f09.svg +0 -1
  248. package/seeds/4217/icons/0x20c000000000000000000000aeed2ec36a54d0e5.svg +0 -1
  249. package/seeds/4217/icons/0x20c000000000000000000000b9537d11c60e8b50.svg +0 -1
  250. package/seeds/4217/icons/0x20c000000000000000000000bd95bfb69fbe6ce3.svg +0 -1
  251. package/seeds/4217/icons/0x20c000000000000000000000c412ec89d0c08be5.svg +0 -1
  252. package/seeds/4217/icons/0x20c000000000000000000000f047dd7018e50367.png +0 -0
  253. package/seeds/4217/verified-tokens.json +0 -177
  254. package/seeds/42431/icons/0x20c0000000000000000000000000000000000000.svg +0 -25
  255. package/seeds/42431/icons/0x20c0000000000000000000000000000000000001.svg +0 -1
  256. package/seeds/42431/icons/0x20c0000000000000000000000000000000000002.svg +0 -1
  257. package/seeds/42431/icons/0x20c0000000000000000000000000000000000003.svg +0 -1
  258. package/seeds/42431/icons/0x20c0000000000000000000009e8d7eb59b783726.svg +0 -1
  259. package/seeds/42431/icons/0x20c000000000000000000000d72572838bbee59c.svg +0 -1
  260. package/seeds/42431/verified-tokens.json +0 -44
  261. package/seeds/openrpc/README.md +0 -27
  262. package/seeds/openrpc/execution-apis.json +0 -8989
  263. package/src/ApiKeySource.test-d.ts +0 -11
  264. package/src/ApiKeySource.test.ts +0 -295
  265. package/src/ApiKeySource.ts +0 -303
@@ -0,0 +1,273 @@
1
+ // @ts-nocheck
2
+ // Credit: https://github.com/mjackson/remix-the-web/blob/main/packages/node-fetch-server/src/lib/request-listener.ts
3
+
4
+ import type * as http from 'node:http'
5
+ import type * as http2 from 'node:http2'
6
+
7
+ export interface RequestListenerOptions {
8
+ /**
9
+ * Overrides the host portion of the incoming request URL. By default the request URL host is
10
+ * derived from the HTTP `Host` header.
11
+ *
12
+ * For example, if you have a `$HOST` environment variable that contains the hostname of your
13
+ * server, you can use it to set the host of all incoming request URLs like so:
14
+ *
15
+ * ```ts
16
+ * createRequestListener(handler, { host: process.env.HOST })
17
+ * ```
18
+ */
19
+ host?: string
20
+ /**
21
+ * An error handler that determines the response when the request handler throws an error. By
22
+ * default a 500 Internal Server Error response will be sent.
23
+ */
24
+ onError?: ErrorHandler
25
+ /**
26
+ * Overrides the protocol of the incoming request URL. By default the request URL protocol is
27
+ * derived from the connection protocol. So e.g. when serving over HTTPS (using
28
+ * `https.createServer()`), the request URL will begin with `https:`.
29
+ */
30
+ protocol?: string
31
+ }
32
+
33
+ /**
34
+ * Wraps a fetch handler in a Node.js request listener that can be used with:
35
+ *
36
+ * - [`http.createServer()`](https://nodejs.org/api/http.html#httpcreateserveroptions-requestlistener)
37
+ * - [`https.createServer()`](https://nodejs.org/api/https.html#httpscreateserveroptions-requestlistener)
38
+ * - [`http2.createServer()`](https://nodejs.org/api/http2.html#http2createserveroptions-onrequesthandler)
39
+ * - [`http2.createSecureServer()`](https://nodejs.org/api/http2.html#http2createsecureserveroptions-onrequesthandler)
40
+ *
41
+ * Example:
42
+ *
43
+ * ```ts
44
+ * import * as http from 'node:http';
45
+ * import { createRequestListener } from '@mjackson/node-fetch-server';
46
+ *
47
+ * async function handler(request) {
48
+ * return new Response('Hello, world!');
49
+ * }
50
+ *
51
+ * let server = http.createServer(
52
+ * createRequestListener(handler)
53
+ * );
54
+ *
55
+ * server.listen(3000);
56
+ * ```
57
+ *
58
+ * @param handler The fetch handler to use for processing incoming requests.
59
+ * @param options Request listener options.
60
+ * @returns A Node.js request listener function.
61
+ */
62
+ export function fromFetchHandler(
63
+ handler: FetchHandler,
64
+ options?: RequestListenerOptions,
65
+ ): http.RequestListener {
66
+ const onError = options?.onError ?? defaultErrorHandler
67
+
68
+ return async (req, res) => {
69
+ const request = createRequest(req, res, options)
70
+ const client = {
71
+ address: req.socket.remoteAddress!,
72
+ family: req.socket.remoteFamily! as ClientAddress['family'],
73
+ port: req.socket.remotePort!,
74
+ }
75
+
76
+ let response: Response
77
+ try {
78
+ response = await handler(request, client)
79
+ } catch (error) {
80
+ try {
81
+ response = (await onError(error)) ?? internalServerError()
82
+ } catch (error) {
83
+ console.error(`There was an error in the error handler: ${error}`)
84
+ response = internalServerError()
85
+ }
86
+ }
87
+
88
+ await sendResponse(res, response)
89
+ }
90
+ }
91
+
92
+ function defaultErrorHandler(error: unknown): Response {
93
+ console.error(error)
94
+ return internalServerError()
95
+ }
96
+
97
+ function internalServerError(): Response {
98
+ return new Response(
99
+ // "Internal Server Error"
100
+ new Uint8Array([
101
+ 73, 110, 116, 101, 114, 110, 97, 108, 32, 83, 101, 114, 118, 101, 114, 32, 69, 114, 114, 111,
102
+ 114,
103
+ ]),
104
+ {
105
+ headers: {
106
+ 'Content-Type': 'text/plain',
107
+ },
108
+ status: 500,
109
+ },
110
+ )
111
+ }
112
+
113
+ export type RequestOptions = Omit<RequestListenerOptions, 'onError'>
114
+
115
+ /**
116
+ * Creates a [`Request`](https://developer.mozilla.org/en-US/docs/Web/API/Request) object from
117
+ *
118
+ * - a [`http.IncomingMessage`](https://nodejs.org/api/http.html#class-httpincomingmessage)/[`http.ServerResponse`](https://nodejs.org/api/http.html#class-httpserverresponse) pair
119
+ * - a [`http2.Http2ServerRequest`](https://nodejs.org/api/http2.html#class-http2http2serverrequest)/[`http2.Http2ServerResponse`](https://nodejs.org/api/http2.html#class-http2http2serverresponse) pair
120
+ *
121
+ * @param req The incoming request object.
122
+ * @param res The server response object.
123
+ * @param options
124
+ * @returns A request object.
125
+ */
126
+ export function createRequest(
127
+ req: http.IncomingMessage | http2.Http2ServerRequest,
128
+ res: http.ServerResponse | http2.Http2ServerResponse,
129
+ options?: RequestOptions,
130
+ ): Request {
131
+ const controller = new AbortController()
132
+ res.on('close', () => {
133
+ controller.abort()
134
+ })
135
+
136
+ const method = req.method ?? 'GET'
137
+ const headers = createHeaders(req)
138
+
139
+ const protocol =
140
+ options?.protocol ?? ('encrypted' in req.socket && req.socket.encrypted ? 'https:' : 'http:')
141
+ const host = options?.host ?? headers.get('Host') ?? 'localhost'
142
+ const url = new URL(req.url!, `${protocol}//${host}`)
143
+
144
+ const init: RequestInit = { headers, method, signal: controller.signal }
145
+
146
+ if (method !== 'GET' && method !== 'HEAD') {
147
+ init.body = new ReadableStream({
148
+ start(controller) {
149
+ req.on('data', (chunk) => {
150
+ controller.enqueue(new Uint8Array(chunk.buffer, chunk.byteOffset, chunk.byteLength))
151
+ })
152
+ req.on('end', () => {
153
+ controller.close()
154
+ })
155
+ },
156
+ })
157
+
158
+ // init.duplex = 'half' must be set when body is a ReadableStream, and Node follows the spec.
159
+ // However, this property is not defined in the TypeScript types for RequestInit, so we have
160
+ // to cast it here in order to set it without a type error.
161
+ // See https://fetch.spec.whatwg.org/#dom-requestinit-duplex
162
+ ;(init as { duplex: 'half' }).duplex = 'half'
163
+ }
164
+
165
+ return new Request(url, init)
166
+ }
167
+
168
+ /**
169
+ * Creates a [`Headers`](https://developer.mozilla.org/en-US/docs/Web/API/Headers) object from the headers in a Node.js
170
+ * [`http.IncomingMessage`](https://nodejs.org/api/http.html#class-httpincomingmessage)/[`http2.Http2ServerRequest`](https://nodejs.org/api/http2.html#class-http2http2serverrequest).
171
+ *
172
+ * @param req The incoming request object.
173
+ * @returns A headers object.
174
+ */
175
+ export function createHeaders(req: http.IncomingMessage | http2.Http2ServerRequest): Headers {
176
+ const headers = new Headers()
177
+
178
+ const rawHeaders = req.rawHeaders
179
+ for (let i = 0; i < rawHeaders.length; i += 2) {
180
+ if (rawHeaders[i]?.startsWith(':')) continue
181
+ headers.append(rawHeaders[i]!, rawHeaders[i + 1]!)
182
+ }
183
+
184
+ return headers
185
+ }
186
+
187
+ /**
188
+ * Sends a [`Response`](https://developer.mozilla.org/en-US/docs/Web/API/Response) to the client using a Node.js
189
+ * [`http.ServerResponse`](https://nodejs.org/api/http.html#class-httpserverresponse)/[`http2.Http2ServerResponse`](https://nodejs.org/api/http2.html#class-http2http2serverresponse)
190
+ * object.
191
+ *
192
+ * @param res The server response object.
193
+ * @param response The response to send.
194
+ */
195
+ export async function sendResponse(
196
+ res: http.ServerResponse | http2.Http2ServerResponse,
197
+ response: Response,
198
+ ): Promise<void> {
199
+ // Iterate over response.headers so we are sure to send multiple Set-Cookie headers correctly.
200
+ // These would incorrectly be merged into a single header if we tried to use
201
+ // `Object.fromEntries(response.headers.entries())`.
202
+ const headers: Record<string, string | string[]> = {}
203
+ for (const [key, value] of response.headers as any) {
204
+ if (key in headers) {
205
+ if (Array.isArray(headers[key])) {
206
+ headers[key].push(value)
207
+ } else {
208
+ headers[key] = [headers[key] as string, value]
209
+ }
210
+ } else {
211
+ headers[key] = value
212
+ }
213
+ }
214
+
215
+ ;(res as any).writeHead(response.status, headers)
216
+
217
+ if (response.body != null && res.req.method !== 'HEAD') {
218
+ for await (const chunk of readStream(response.body)) {
219
+ // @ts-expect-error - Node typings for http2 require a 2nd parameter to write but it's optional
220
+ res.write(chunk)
221
+ }
222
+ }
223
+
224
+ res.end()
225
+ }
226
+
227
+ export async function* readStream(stream: ReadableStream<Uint8Array>): AsyncIterable<Uint8Array> {
228
+ const reader = stream.getReader()
229
+
230
+ while (true) {
231
+ const { done, value } = await reader.read()
232
+ if (done) break
233
+ yield value
234
+ }
235
+ }
236
+
237
+ export interface ClientAddress {
238
+ /**
239
+ * The IP address of the client that sent the request.
240
+ *
241
+ * [Node.js Reference](https://nodejs.org/api/net.html#socketremoteaddress)
242
+ */
243
+ address: string
244
+ /**
245
+ * The family of the client IP address.
246
+ *
247
+ * [Node.js Reference](https://nodejs.org/api/net.html#socketremotefamily)
248
+ */
249
+ family: 'IPv4' | 'IPv6'
250
+ /**
251
+ * The remote port of the client that sent the request.
252
+ *
253
+ * [Node.js Reference](https://nodejs.org/api/net.html#socketremoteport)
254
+ */
255
+ port: number
256
+ }
257
+
258
+ /**
259
+ * A function that handles an error that occurred during request handling. May return a response to
260
+ * send to the client, or `undefined` to allow the server to send a default error response.
261
+ *
262
+ * [MDN `Response` Reference](https://developer.mozilla.org/en-US/docs/Web/API/Response)
263
+ */
264
+ export type ErrorHandler = (error: unknown) => undefined | Response | Promise<undefined | Response>
265
+
266
+ /**
267
+ * A function that handles an incoming request and returns a response.
268
+ *
269
+ * [MDN `Request` Reference](https://developer.mozilla.org/en-US/docs/Web/API/Request)
270
+ *
271
+ * [MDN `Response` Reference](https://developer.mozilla.org/en-US/docs/Web/API/Response)
272
+ */
273
+ export type FetchHandler = (request: Request, client: ClientAddress) => Response | Promise<Response>
@@ -0,0 +1,186 @@
1
+ import { RpcResponse, Signature } from 'ox'
2
+ import { TxEnvelopeTempo } from 'ox/tempo'
3
+ import type { Address, Client } from 'viem'
4
+ import type { LocalAccount } from 'viem/accounts'
5
+ import { Transaction } from 'viem/tempo'
6
+
7
+ import * as Utils from './utils.js'
8
+
9
+ /** Returns sponsor metadata for `eth_fillTransaction` responses. */
10
+ export function getSponsor(options: getSponsor.Options): getSponsor.ReturnType {
11
+ const { account, name, url } = options
12
+ return {
13
+ address: account.address,
14
+ ...(name ? { name } : {}),
15
+ ...(url ? { url } : {}),
16
+ }
17
+ }
18
+
19
+ export declare namespace getSponsor {
20
+ type Options = {
21
+ /** Account used for sponsorship. */
22
+ account: LocalAccount
23
+ /** Optional display name. */
24
+ name?: string | undefined
25
+ /** Optional display URL. */
26
+ url?: string | undefined
27
+ }
28
+
29
+ type ReturnType = {
30
+ /** Sponsor address. */
31
+ address: Address
32
+ /** Sponsor display name. */
33
+ name?: string | undefined
34
+ /** Sponsor display URL. */
35
+ url?: string | undefined
36
+ }
37
+ }
38
+
39
+ /** Returns whether the fee payer approves a filled transaction. */
40
+ export async function shouldSponsor(options: shouldSponsor.Options) {
41
+ const { sender, transaction, validate } = options
42
+ if (!validate) return true
43
+ return await validate({
44
+ ...transaction,
45
+ from: sender,
46
+ } as Transaction.TransactionRequest)
47
+ }
48
+
49
+ export declare namespace shouldSponsor {
50
+ type Options = {
51
+ /** Sender address from the original request. */
52
+ sender?: Address | undefined
53
+ /** Filled transaction to validate. */
54
+ transaction: Record<string, unknown>
55
+ /** Optional sponsorship approval callback. */
56
+ validate?: ((request: Transaction.TransactionRequest) => boolean | Promise<boolean>) | undefined
57
+ }
58
+ }
59
+
60
+ /** Returns whether a raw Tempo transaction is explicitly requesting sponsorship. */
61
+ export function requestsRawSponsorship(serialized: `0x${string}`) {
62
+ if (!Utils.isSerializedTempoTransaction(serialized)) return false
63
+ const transaction = Transaction.deserialize(serialized)
64
+ return 'feePayerSignature' in transaction && transaction.feePayerSignature === null
65
+ }
66
+
67
+ /** Returns `true` when a fill request already has the fields needed for sponsorship signing. */
68
+ export function isPreparedTransaction(value: Record<string, unknown>) {
69
+ return (
70
+ typeof value['from'] === 'string' &&
71
+ typeof Utils.resolveChainId(value['chainId']) === 'number' &&
72
+ typeof value['gas'] !== 'undefined' &&
73
+ typeof value['nonce'] !== 'undefined' &&
74
+ (typeof value['maxFeePerGas'] !== 'undefined' || typeof value['gasPrice'] !== 'undefined')
75
+ )
76
+ }
77
+
78
+ /** Signs a filled transaction as the fee payer. */
79
+ export async function sign(options: sign.Options) {
80
+ const { account, transaction, sender } = options
81
+ const from = (transaction['from'] as Address | undefined) ?? sender
82
+ const { signature: _, ...withoutSenderSig } = transaction
83
+ const prepared = { ...withoutSenderSig, from }
84
+
85
+ if (!prepared.from)
86
+ throw new RpcResponse.InvalidParamsError({
87
+ message: 'Transaction sender must be provided before fee payer signing.',
88
+ })
89
+ if (!account.sign) throw new Error('Fee payer account cannot sign transactions.')
90
+
91
+ const feePayerSignature = Signature.from(
92
+ await account.sign({
93
+ hash: TxEnvelopeTempo.getFeePayerSignPayload(TxEnvelopeTempo.from(prepared as never), {
94
+ sender: prepared.from,
95
+ }),
96
+ }),
97
+ )
98
+
99
+ return { ...prepared, feePayerSignature }
100
+ }
101
+
102
+ export declare namespace sign {
103
+ type Options = {
104
+ /** Account used as the fee payer. */
105
+ account: LocalAccount
106
+ /** Filled transaction to sign. */
107
+ transaction: Record<string, unknown>
108
+ /** Sender address from the original request. */
109
+ sender?: Address | undefined
110
+ }
111
+ }
112
+
113
+ /** Handles `eth_signRawTransaction` and broadcast methods for sponsored Tempo transactions. */
114
+ export async function handleRawTransaction(options: handleRawTransaction.Options) {
115
+ const { account, feeToken: sponsorFeeToken, getClient, method, request, validate } = options
116
+ const serialized = request.params?.[0] as `0x76${string}` | undefined
117
+
118
+ if (!Utils.isSerializedTempoTransaction(serialized))
119
+ throw new RpcResponse.InvalidParamsError({
120
+ message: 'Only Tempo (0x76/0x78) transactions are supported.',
121
+ })
122
+
123
+ const transaction = Transaction.deserialize(serialized)
124
+ // Prefer sender recovered from raw envelope; multisig finalize path supplies fallback sender.
125
+ const sender = transaction.from ?? options.sender
126
+
127
+ // Sponsorship only applies after sender has signed original transaction.
128
+ if (!transaction.signature || !sender)
129
+ throw new RpcResponse.InvalidParamsError({
130
+ message: 'Transaction must be signed by the sender before fee payer signing.',
131
+ })
132
+ if (!account.sign) throw new Error('Fee payer account cannot sign transactions.')
133
+
134
+ const client = getClient(transaction.chainId)
135
+ const feeToken_chain = (client.chain as { feeToken?: Address | undefined } | undefined)?.feeToken
136
+ const feeToken =
137
+ (transaction.feeToken as Address | null | undefined) ??
138
+ sponsorFeeToken ??
139
+ (await options.getFeeToken?.(transaction.chainId)) ??
140
+ feeToken_chain
141
+ const transaction_sponsored = feeToken ? { ...transaction, feeToken } : transaction
142
+
143
+ if (validate && !(await validate(transaction_sponsored as Transaction.TransactionRequest)))
144
+ throw new RpcResponse.InvalidParamsError({
145
+ message: 'Sponsorship rejected.',
146
+ })
147
+
148
+ const envelope = TxEnvelopeTempo.from(transaction_sponsored as never)
149
+ const feePayerSignature = Signature.from(
150
+ await account.sign({
151
+ hash: TxEnvelopeTempo.getFeePayerSignPayload(envelope, { sender }),
152
+ }),
153
+ )
154
+ const serializedTransaction = TxEnvelopeTempo.serialize(envelope, {
155
+ feePayerSignature,
156
+ signature: transaction.signature,
157
+ })
158
+
159
+ // Raw-sign requests stop after fee-payer signature is added; send methods broadcast it.
160
+ if (method === 'eth_signRawTransaction') return serializedTransaction
161
+ return await client.request({
162
+ method: method as never,
163
+ params: [serializedTransaction],
164
+ })
165
+ }
166
+
167
+ export declare namespace handleRawTransaction {
168
+ type Options = {
169
+ /** Account used as the fee payer. */
170
+ account: LocalAccount
171
+ /** Optional token the fee payer prefers for sponsored raw transactions. */
172
+ feeToken?: Address | undefined
173
+ /** Optional fee-token resolver used when the raw envelope omits `feeToken`. */
174
+ getFeeToken?: ((chainId: number) => Promise<Address | undefined>) | undefined
175
+ /** Client resolver keyed by transaction `chainId`. */
176
+ getClient: (chainId?: number) => Client
177
+ /** Raw transaction method to handle. */
178
+ method: 'eth_signRawTransaction' | 'eth_sendRawTransaction' | 'eth_sendRawTransactionSync'
179
+ /** Incoming JSON-RPC request. */
180
+ request: { params?: readonly unknown[] | undefined }
181
+ /** Sender address to use if it cannot be recovered from the raw envelope. */
182
+ sender?: Address | undefined
183
+ /** Optional sponsorship approval callback. */
184
+ validate?: ((request: Transaction.TransactionRequest) => boolean | Promise<boolean>) | undefined
185
+ }
186
+ }