tempo.ts 0.6.2 → 0.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +134 -0
- package/README.md +6 -2
- package/dist/ox/Transaction.js +1 -1
- package/dist/ox/Transaction.js.map +1 -1
- package/dist/server/Handler.d.ts +346 -0
- package/dist/server/Handler.d.ts.map +1 -0
- package/dist/server/Handler.js +441 -0
- package/dist/server/Handler.js.map +1 -0
- package/dist/server/Kv.d.ts +16 -0
- package/dist/server/Kv.d.ts.map +1 -0
- package/dist/server/Kv.js +25 -0
- package/dist/server/Kv.js.map +1 -0
- package/dist/server/index.d.ts +3 -0
- package/dist/server/index.d.ts.map +1 -0
- package/dist/server/index.js +3 -0
- package/dist/server/index.js.map +1 -0
- package/dist/server/internal/requestListener.d.ts +124 -0
- package/dist/server/internal/requestListener.d.ts.map +1 -0
- package/dist/server/internal/requestListener.js +174 -0
- package/dist/server/internal/requestListener.js.map +1 -0
- package/dist/viem/Actions/amm.d.ts +9 -41
- package/dist/viem/Actions/amm.d.ts.map +1 -1
- package/dist/viem/Actions/amm.js +15 -26
- package/dist/viem/Actions/amm.js.map +1 -1
- package/dist/viem/Actions/reward.d.ts +0 -1067
- package/dist/viem/Actions/reward.d.ts.map +1 -1
- package/dist/viem/Actions/reward.js +4 -212
- package/dist/viem/Actions/reward.js.map +1 -1
- package/dist/viem/Decorator.d.ts +0 -263
- package/dist/viem/Decorator.d.ts.map +1 -1
- package/dist/viem/Decorator.js +0 -10
- package/dist/viem/Decorator.js.map +1 -1
- package/dist/viem/Storage.d.ts +23 -0
- package/dist/viem/Storage.d.ts.map +1 -0
- package/dist/viem/Storage.js +47 -0
- package/dist/viem/Storage.js.map +1 -0
- package/dist/viem/Transport.d.ts +10 -1
- package/dist/viem/Transport.d.ts.map +1 -1
- package/dist/viem/Transport.js +22 -3
- package/dist/viem/Transport.js.map +1 -1
- package/dist/viem/internal/utils.d.ts +6 -0
- package/dist/viem/internal/utils.d.ts.map +1 -1
- package/dist/viem/internal/utils.js +24 -0
- package/dist/viem/internal/utils.js.map +1 -1
- package/dist/wagmi/Actions/reward.d.ts +0 -110
- package/dist/wagmi/Actions/reward.d.ts.map +1 -1
- package/dist/wagmi/Actions/reward.js +0 -121
- package/dist/wagmi/Actions/reward.js.map +1 -1
- package/dist/wagmi/Connector.d.ts +6 -17
- package/dist/wagmi/Connector.d.ts.map +1 -1
- package/dist/wagmi/Connector.js +17 -43
- package/dist/wagmi/Connector.js.map +1 -1
- package/dist/wagmi/Hooks/reward.d.ts +0 -88
- package/dist/wagmi/Hooks/reward.d.ts.map +1 -1
- package/dist/wagmi/Hooks/reward.js +0 -103
- package/dist/wagmi/Hooks/reward.js.map +1 -1
- package/dist/wagmi/KeyManager.d.ts +57 -0
- package/dist/wagmi/KeyManager.d.ts.map +1 -0
- package/dist/wagmi/KeyManager.js +101 -0
- package/dist/wagmi/KeyManager.js.map +1 -0
- package/dist/wagmi/index.d.ts +1 -0
- package/dist/wagmi/index.d.ts.map +1 -1
- package/dist/wagmi/index.js +1 -0
- package/dist/wagmi/index.js.map +1 -1
- package/package.json +8 -2
- package/src/ox/Transaction.ts +1 -1
- package/src/ox/e2e.test.ts +7 -0
- package/src/server/Handler.test.ts +566 -0
- package/src/server/Handler.ts +577 -0
- package/src/server/Kv.ts +40 -0
- package/src/server/index.ts +2 -0
- package/src/server/internal/requestListener.ts +285 -0
- package/src/viem/Actions/amm.test.ts +10 -284
- package/src/viem/Actions/amm.ts +32 -40
- package/src/viem/Actions/reward.test.ts +4 -212
- package/src/viem/Actions/reward.ts +4 -291
- package/src/viem/Decorator.ts +0 -294
- package/src/viem/Storage.ts +88 -0
- package/src/viem/Transport.ts +40 -2
- package/src/viem/e2e.test.ts +106 -3
- package/src/viem/internal/utils.ts +21 -0
- package/src/wagmi/Actions/amm.test.ts +7 -85
- package/src/wagmi/Actions/reward.test.ts +0 -99
- package/src/wagmi/Actions/reward.ts +0 -203
- package/src/wagmi/Connector.test.ts +4 -1
- package/src/wagmi/Connector.ts +24 -58
- package/src/wagmi/Hooks/amm.test.ts +8 -200
- package/src/wagmi/Hooks/reward.test.ts +1 -142
- package/src/wagmi/Hooks/reward.ts +0 -196
- package/src/wagmi/KeyManager.ts +159 -0
- package/src/wagmi/index.ts +1 -0
|
@@ -0,0 +1,577 @@
|
|
|
1
|
+
import { createRouter, type Router } from '@remix-run/fetch-router'
|
|
2
|
+
import { RpcRequest, RpcResponse } from 'ox'
|
|
3
|
+
import * as Base64 from 'ox/Base64'
|
|
4
|
+
import * as Hex from 'ox/Hex'
|
|
5
|
+
import type * as WebAuthnP256 from 'ox/WebAuthnP256'
|
|
6
|
+
import { type Chain, type Client, createClient, type Transport } from 'viem'
|
|
7
|
+
import type { LocalAccount } from 'viem/accounts'
|
|
8
|
+
import { signTransaction } from 'viem/actions'
|
|
9
|
+
import type { OneOf } from '../internal/types.js'
|
|
10
|
+
import { formatTransaction } from '../viem/Formatters.js'
|
|
11
|
+
import * as Transaction from '../viem/Transaction.js'
|
|
12
|
+
import * as RequestListener from './internal/requestListener.js'
|
|
13
|
+
import type * as Kv from './Kv.js'
|
|
14
|
+
|
|
15
|
+
export type Handler = Router & {
|
|
16
|
+
listener: (req: any, res: any) => void
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Instantiates a new request handler.
|
|
21
|
+
*
|
|
22
|
+
* @param options - constructor options
|
|
23
|
+
* @returns Handler instance
|
|
24
|
+
*/
|
|
25
|
+
export function from(): Handler {
|
|
26
|
+
const router = createRouter()
|
|
27
|
+
return {
|
|
28
|
+
...router,
|
|
29
|
+
listener: RequestListener.fromFetchHandler((request) => {
|
|
30
|
+
return router.fetch(request)
|
|
31
|
+
}),
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Defines a Key Manager request handler.
|
|
37
|
+
*
|
|
38
|
+
* @example
|
|
39
|
+
* ### Cloudflare Worker
|
|
40
|
+
*
|
|
41
|
+
* ```ts
|
|
42
|
+
* import { env } from 'cloudflare:workers'
|
|
43
|
+
* import { Handler } from 'tempo.ts/server'
|
|
44
|
+
*
|
|
45
|
+
* export default {
|
|
46
|
+
* fetch(request) {
|
|
47
|
+
* return Handler.keyManager({
|
|
48
|
+
* kv: Kv.cloudflare(env.KEY_STORE),
|
|
49
|
+
* }).fetch(request)
|
|
50
|
+
* }
|
|
51
|
+
* }
|
|
52
|
+
* ```
|
|
53
|
+
*
|
|
54
|
+
* @example
|
|
55
|
+
* ### Next.js
|
|
56
|
+
*
|
|
57
|
+
* ```ts
|
|
58
|
+
* import { Handler } from 'tempo.ts/server'
|
|
59
|
+
*
|
|
60
|
+
* const handler = Handler.keyManager({
|
|
61
|
+
* kv: Kv.memory(),
|
|
62
|
+
* })
|
|
63
|
+
*
|
|
64
|
+
* export GET = handler.fetch
|
|
65
|
+
* export POST = handler.fetch
|
|
66
|
+
* ```
|
|
67
|
+
*
|
|
68
|
+
* @example
|
|
69
|
+
* ### Hono
|
|
70
|
+
*
|
|
71
|
+
* ```ts
|
|
72
|
+
* import { Handler } from 'tempo.ts/server'
|
|
73
|
+
*
|
|
74
|
+
* const handler = Handler.keyManager({
|
|
75
|
+
* kv: Kv.memory(),
|
|
76
|
+
* })
|
|
77
|
+
*
|
|
78
|
+
* const app = new Hono()
|
|
79
|
+
* app.all('*', handler)
|
|
80
|
+
*
|
|
81
|
+
* export default app
|
|
82
|
+
* ```
|
|
83
|
+
*
|
|
84
|
+
* @example
|
|
85
|
+
* ### Node.js
|
|
86
|
+
*
|
|
87
|
+
* ```ts
|
|
88
|
+
* import { Handler } from 'tempo.ts/server'
|
|
89
|
+
*
|
|
90
|
+
* const handler = Handler.keyManager({
|
|
91
|
+
* kv: Kv.memory(),
|
|
92
|
+
* })
|
|
93
|
+
*
|
|
94
|
+
* const server = createServer(handler.listener)
|
|
95
|
+
* server.listen(3000)
|
|
96
|
+
* ```
|
|
97
|
+
*
|
|
98
|
+
* @example
|
|
99
|
+
* ### Bun
|
|
100
|
+
*
|
|
101
|
+
* ```ts
|
|
102
|
+
* import { Handler } from 'tempo.ts/server'
|
|
103
|
+
*
|
|
104
|
+
* const handler = Handler.keyManager({
|
|
105
|
+
* kv: Kv.memory(),
|
|
106
|
+
* })
|
|
107
|
+
*
|
|
108
|
+
* Bun.serve(handler)
|
|
109
|
+
* ```
|
|
110
|
+
*
|
|
111
|
+
* @example
|
|
112
|
+
* ### Deno
|
|
113
|
+
*
|
|
114
|
+
* ```ts
|
|
115
|
+
* import { Handler } from 'tempo.ts/server'
|
|
116
|
+
*
|
|
117
|
+
* const handler = Handler.keyManager({
|
|
118
|
+
* kv: Kv.memory(),
|
|
119
|
+
* })
|
|
120
|
+
*
|
|
121
|
+
* Deno.serve(handler)
|
|
122
|
+
* ```
|
|
123
|
+
*
|
|
124
|
+
* @example
|
|
125
|
+
* ### Express
|
|
126
|
+
*
|
|
127
|
+
* ```ts
|
|
128
|
+
* import { Handler } from 'tempo.ts/server'
|
|
129
|
+
*
|
|
130
|
+
* const handler = Handler.keyManager({
|
|
131
|
+
* kv: Kv.memory(),
|
|
132
|
+
* })
|
|
133
|
+
*
|
|
134
|
+
* const app = express()
|
|
135
|
+
* app.use(handler.listener)
|
|
136
|
+
* app.listen(3000)
|
|
137
|
+
* ```
|
|
138
|
+
*
|
|
139
|
+
* @param options - Options.
|
|
140
|
+
* @returns Request handler.
|
|
141
|
+
*/
|
|
142
|
+
export function keyManager(options: keyManager.Options) {
|
|
143
|
+
const { kv } = options
|
|
144
|
+
|
|
145
|
+
const path = options.path ?? ''
|
|
146
|
+
|
|
147
|
+
const rp = (() => {
|
|
148
|
+
if (typeof options.rp === 'string')
|
|
149
|
+
return { id: options.rp, name: options.rp }
|
|
150
|
+
if (options.rp)
|
|
151
|
+
return {
|
|
152
|
+
id: options.rp.id,
|
|
153
|
+
name: options.rp.name ?? options.rp.id,
|
|
154
|
+
}
|
|
155
|
+
return undefined
|
|
156
|
+
})()
|
|
157
|
+
|
|
158
|
+
const router = from()
|
|
159
|
+
|
|
160
|
+
// Get challenge for WebAuthn credential creation
|
|
161
|
+
router.get(`${path}/challenge`, async () => {
|
|
162
|
+
// Generate a random challenge
|
|
163
|
+
const challenge = Hex.random(32)
|
|
164
|
+
|
|
165
|
+
// Store challenge in KV with 5 minute expiration
|
|
166
|
+
await kv.set(`challenge:${challenge}`, '1')
|
|
167
|
+
|
|
168
|
+
return Response.json({
|
|
169
|
+
challenge,
|
|
170
|
+
...(rp ? { rp } : {}),
|
|
171
|
+
} satisfies keyManager.ChallengeResponse)
|
|
172
|
+
})
|
|
173
|
+
|
|
174
|
+
// Get public key for a credential
|
|
175
|
+
router.get(`${path}/:id`, async ({ params }) => {
|
|
176
|
+
const { id } = params
|
|
177
|
+
|
|
178
|
+
const publicKey = await kv.get<Hex.Hex>(`credential:${id}`)
|
|
179
|
+
|
|
180
|
+
if (!publicKey) return new Response('Credential not found', { status: 404 })
|
|
181
|
+
|
|
182
|
+
return Response.json({
|
|
183
|
+
publicKey,
|
|
184
|
+
})
|
|
185
|
+
})
|
|
186
|
+
|
|
187
|
+
// Set public key for a credential
|
|
188
|
+
router.post(`${path}/:id`, async ({ params, request }) => {
|
|
189
|
+
const { id } = params
|
|
190
|
+
const { credential, publicKey } = (await request.json()) as any
|
|
191
|
+
|
|
192
|
+
if (!credential)
|
|
193
|
+
return Response.json({ error: 'Missing `credential`' }, { status: 400 })
|
|
194
|
+
if (!publicKey)
|
|
195
|
+
return Response.json({ error: 'Missing `publicKey`' }, { status: 400 })
|
|
196
|
+
|
|
197
|
+
// Decode and verify clientDataJSON
|
|
198
|
+
const clientDataJSON = JSON.parse(
|
|
199
|
+
Base64.toString(credential.response.clientDataJSON as unknown as string),
|
|
200
|
+
)
|
|
201
|
+
|
|
202
|
+
// Verify challenge
|
|
203
|
+
const challenge = Base64.toHex(clientDataJSON.challenge)
|
|
204
|
+
|
|
205
|
+
if (!(await kv.get<string>(`challenge:${challenge}`)))
|
|
206
|
+
return Response.json(
|
|
207
|
+
{ error: 'Invalid or expired `challenge`' },
|
|
208
|
+
{ status: 400 },
|
|
209
|
+
)
|
|
210
|
+
|
|
211
|
+
// Verify type
|
|
212
|
+
if (clientDataJSON.type !== 'webauthn.create')
|
|
213
|
+
return Response.json(
|
|
214
|
+
{ error: 'Invalid `clientDataJSON.type`' },
|
|
215
|
+
{ status: 400 },
|
|
216
|
+
)
|
|
217
|
+
|
|
218
|
+
// Verify origin
|
|
219
|
+
if (
|
|
220
|
+
rp?.id &&
|
|
221
|
+
!rp.id.includes('localhost') &&
|
|
222
|
+
clientDataJSON.origin !== new URL(`https://${rp.id}`).origin
|
|
223
|
+
)
|
|
224
|
+
return Response.json(
|
|
225
|
+
{ error: 'Invalid `clientDataJSON.origin`' },
|
|
226
|
+
{ status: 400 },
|
|
227
|
+
)
|
|
228
|
+
|
|
229
|
+
// Parse authenticatorData
|
|
230
|
+
const authenticatorData = Base64.toBytes(
|
|
231
|
+
(credential.response as any).authenticatorData,
|
|
232
|
+
)
|
|
233
|
+
|
|
234
|
+
// Parse flags (byte 32)
|
|
235
|
+
const flags = authenticatorData[32]
|
|
236
|
+
if (!flags)
|
|
237
|
+
return Response.json(
|
|
238
|
+
{ error: 'Invalid `authenticatorData`' },
|
|
239
|
+
{ status: 400 },
|
|
240
|
+
)
|
|
241
|
+
|
|
242
|
+
// Check User Present (UP) flag (bit 0)
|
|
243
|
+
const userPresent = (flags & 0x01) !== 0
|
|
244
|
+
if (!userPresent)
|
|
245
|
+
return Response.json({ error: 'User not present' }, { status: 400 })
|
|
246
|
+
|
|
247
|
+
// Consume the challenge (delete it so it can't be reused)
|
|
248
|
+
await kv.delete(`challenge:${challenge}`)
|
|
249
|
+
|
|
250
|
+
// Store the public key
|
|
251
|
+
await kv.set(`credential:${id}`, publicKey)
|
|
252
|
+
|
|
253
|
+
return new Response(null, { status: 204 })
|
|
254
|
+
})
|
|
255
|
+
|
|
256
|
+
return router
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
export declare namespace keyManager {
|
|
260
|
+
export type Options = {
|
|
261
|
+
/** The KV store to use for key management. */
|
|
262
|
+
kv: Kv.Kv
|
|
263
|
+
/** The path to use for the handler. */
|
|
264
|
+
path?: string | undefined
|
|
265
|
+
/** The RP to use for WebAuthn. */
|
|
266
|
+
rp?:
|
|
267
|
+
| string
|
|
268
|
+
| {
|
|
269
|
+
id: string
|
|
270
|
+
name?: string | undefined
|
|
271
|
+
}
|
|
272
|
+
| undefined
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
export type ChallengeResponse = {
|
|
276
|
+
challenge: Hex.Hex
|
|
277
|
+
rp?:
|
|
278
|
+
| {
|
|
279
|
+
id: string
|
|
280
|
+
name: string
|
|
281
|
+
}
|
|
282
|
+
| undefined
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
export type GetPublicKeyParameters = {
|
|
286
|
+
credential: WebAuthnP256.P256Credential['raw']
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
export type SetPublicKeyParameters = {
|
|
290
|
+
credential: WebAuthnP256.P256Credential['raw']
|
|
291
|
+
publicKey: Hex.Hex
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
/**
|
|
296
|
+
* Instantiates a fee payer service request handler that can be used to
|
|
297
|
+
* sponsor the fee for user transactions.
|
|
298
|
+
*
|
|
299
|
+
* @example
|
|
300
|
+
* ### Cloudflare Worker
|
|
301
|
+
*
|
|
302
|
+
* ```ts
|
|
303
|
+
* import { createClient, http } from 'viem'
|
|
304
|
+
* import { privateKeyToAccount } from 'viem/accounts'
|
|
305
|
+
* import { tempo } from 'tempo.ts/chains'
|
|
306
|
+
* import { Handler } from 'tempo.ts/server'
|
|
307
|
+
*
|
|
308
|
+
* const client = createClient({
|
|
309
|
+
* chain: tempo({ feeToken: '0x20c0000000000000000000000000000000000001' }),
|
|
310
|
+
* transport: http(),
|
|
311
|
+
* })
|
|
312
|
+
*
|
|
313
|
+
* export default {
|
|
314
|
+
* fetch(request) {
|
|
315
|
+
* return Handler.feePayer({
|
|
316
|
+
* account: privateKeyToAccount('0x...'),
|
|
317
|
+
* client,
|
|
318
|
+
* }).fetch(request)
|
|
319
|
+
* }
|
|
320
|
+
* }
|
|
321
|
+
* ```
|
|
322
|
+
*
|
|
323
|
+
* @example
|
|
324
|
+
* ### Next.js
|
|
325
|
+
*
|
|
326
|
+
* ```ts
|
|
327
|
+
* import { createClient, http } from 'viem'
|
|
328
|
+
* import { privateKeyToAccount } from 'viem/accounts'
|
|
329
|
+
* import { tempo } from 'tempo.ts/chains'
|
|
330
|
+
* import { Handler } from 'tempo.ts/server'
|
|
331
|
+
*
|
|
332
|
+
* const client = createClient({
|
|
333
|
+
* chain: tempo({ feeToken: '0x20c0000000000000000000000000000000000001' }),
|
|
334
|
+
* transport: http(),
|
|
335
|
+
* })
|
|
336
|
+
*
|
|
337
|
+
* const handler = Handler.feePayer({
|
|
338
|
+
* account: privateKeyToAccount('0x...'),
|
|
339
|
+
* client,
|
|
340
|
+
* })
|
|
341
|
+
*
|
|
342
|
+
* export GET = handler.fetch
|
|
343
|
+
* export POST = handler.fetch
|
|
344
|
+
* ```
|
|
345
|
+
*
|
|
346
|
+
* @example
|
|
347
|
+
* ### Hono
|
|
348
|
+
*
|
|
349
|
+
* ```ts
|
|
350
|
+
* import { createClient, http } from 'viem'
|
|
351
|
+
* import { privateKeyToAccount } from 'viem/accounts'
|
|
352
|
+
* import { tempo } from 'tempo.ts/chains'
|
|
353
|
+
* import { Handler } from 'tempo.ts/server'
|
|
354
|
+
*
|
|
355
|
+
* const client = createClient({
|
|
356
|
+
* chain: tempo({ feeToken: '0x20c0000000000000000000000000000000000001' }),
|
|
357
|
+
* transport: http(),
|
|
358
|
+
* })
|
|
359
|
+
*
|
|
360
|
+
* const handler = Handler.feePayer({
|
|
361
|
+
* account: privateKeyToAccount('0x...'),
|
|
362
|
+
* client,
|
|
363
|
+
* })
|
|
364
|
+
*
|
|
365
|
+
* const app = new Hono()
|
|
366
|
+
* app.all('*', handler)
|
|
367
|
+
*
|
|
368
|
+
* export default app
|
|
369
|
+
* ```
|
|
370
|
+
*
|
|
371
|
+
* @example
|
|
372
|
+
* ### Node.js
|
|
373
|
+
*
|
|
374
|
+
* ```ts
|
|
375
|
+
* import { createClient, http } from 'viem'
|
|
376
|
+
* import { privateKeyToAccount } from 'viem/accounts'
|
|
377
|
+
* import { tempo } from 'tempo.ts/chains'
|
|
378
|
+
* import { Handler } from 'tempo.ts/server'
|
|
379
|
+
*
|
|
380
|
+
* const client = createClient({
|
|
381
|
+
* chain: tempo({ feeToken: '0x20c0000000000000000000000000000000000001' }),
|
|
382
|
+
* transport: http(),
|
|
383
|
+
* })
|
|
384
|
+
*
|
|
385
|
+
* const handler = Handler.feePayer({
|
|
386
|
+
* account: privateKeyToAccount('0x...'),
|
|
387
|
+
* client,
|
|
388
|
+
* })
|
|
389
|
+
*
|
|
390
|
+
* const server = createServer(handler.listener)
|
|
391
|
+
* server.listen(3000)
|
|
392
|
+
* ```
|
|
393
|
+
*
|
|
394
|
+
* @example
|
|
395
|
+
* ### Bun
|
|
396
|
+
*
|
|
397
|
+
* ```ts
|
|
398
|
+
* import { createClient, http } from 'viem'
|
|
399
|
+
* import { privateKeyToAccount } from 'viem/accounts'
|
|
400
|
+
* import { tempo } from 'tempo.ts/chains'
|
|
401
|
+
* import { Handler } from 'tempo.ts/server'
|
|
402
|
+
*
|
|
403
|
+
* const client = createClient({
|
|
404
|
+
* account: privateKeyToAccount('0x...'),
|
|
405
|
+
* chain: tempo({
|
|
406
|
+
* feeToken: '0x20c0000000000000000000000000000000000001',
|
|
407
|
+
* }),
|
|
408
|
+
* transport: http(),
|
|
409
|
+
* })
|
|
410
|
+
*
|
|
411
|
+
* const handler = Handler.feePayer({
|
|
412
|
+
* account: privateKeyToAccount('0x...'),
|
|
413
|
+
* client,
|
|
414
|
+
* })
|
|
415
|
+
*
|
|
416
|
+
* Bun.serve(handler)
|
|
417
|
+
* ```
|
|
418
|
+
*
|
|
419
|
+
* @example
|
|
420
|
+
* ### Deno
|
|
421
|
+
*
|
|
422
|
+
* ```ts
|
|
423
|
+
* import { createClient, http } from 'viem'
|
|
424
|
+
* import { privateKeyToAccount } from 'viem/accounts'
|
|
425
|
+
* import { tempo } from 'tempo.ts/chains'
|
|
426
|
+
* import { Handler } from 'tempo.ts/server'
|
|
427
|
+
*
|
|
428
|
+
* const client = createClient({
|
|
429
|
+
* chain: tempo({ feeToken: '0x20c0000000000000000000000000000000000001' }),
|
|
430
|
+
* transport: http(),
|
|
431
|
+
* })
|
|
432
|
+
*
|
|
433
|
+
* const handler = Handler.feePayer({
|
|
434
|
+
* account: privateKeyToAccount('0x...'),
|
|
435
|
+
* client,
|
|
436
|
+
* })
|
|
437
|
+
*
|
|
438
|
+
* Deno.serve(handler)
|
|
439
|
+
* ```
|
|
440
|
+
*
|
|
441
|
+
* @example
|
|
442
|
+
* ### Express
|
|
443
|
+
*
|
|
444
|
+
* ```ts
|
|
445
|
+
* import { createClient, http } from 'viem'
|
|
446
|
+
* import { privateKeyToAccount } from 'viem/accounts'
|
|
447
|
+
* import { tempo } from 'tempo.ts/chains'
|
|
448
|
+
* import { Handler } from 'tempo.ts/server'
|
|
449
|
+
*
|
|
450
|
+
* const client = createClient({
|
|
451
|
+
* chain: tempo({ feeToken: '0x20c0000000000000000000000000000000000001' }),
|
|
452
|
+
* transport: http(),
|
|
453
|
+
* })
|
|
454
|
+
*
|
|
455
|
+
* const handler = Handler.feePayer({
|
|
456
|
+
* account: privateKeyToAccount('0x...'),
|
|
457
|
+
* client,
|
|
458
|
+
* })
|
|
459
|
+
*
|
|
460
|
+
* const app = express()
|
|
461
|
+
* app.use(handler.listener)
|
|
462
|
+
* app.listen(3000)
|
|
463
|
+
* ```
|
|
464
|
+
*
|
|
465
|
+
* @param options - Options.
|
|
466
|
+
* @returns Request handler.
|
|
467
|
+
*/
|
|
468
|
+
export function feePayer(options: feePayer.Options) {
|
|
469
|
+
const { account, onRequest, path = '/' } = options
|
|
470
|
+
|
|
471
|
+
const client = (() => {
|
|
472
|
+
if ('client' in options) return options.client!
|
|
473
|
+
if ('chain' in options && 'transport' in options)
|
|
474
|
+
return createClient({
|
|
475
|
+
chain: options.chain,
|
|
476
|
+
transport: options.transport,
|
|
477
|
+
})
|
|
478
|
+
throw new Error('No client or chain provided')
|
|
479
|
+
})()
|
|
480
|
+
|
|
481
|
+
const router = from()
|
|
482
|
+
|
|
483
|
+
router.post(path, async ({ request: req }) => {
|
|
484
|
+
const request = RpcRequest.from((await req.json()) as any)
|
|
485
|
+
|
|
486
|
+
await onRequest?.(request)
|
|
487
|
+
|
|
488
|
+
if (request.method === 'eth_signTransaction') {
|
|
489
|
+
const transactionRequest = formatTransaction(request.params?.[0] as never)
|
|
490
|
+
|
|
491
|
+
const serializedTransaction = await signTransaction(client, {
|
|
492
|
+
...transactionRequest,
|
|
493
|
+
account,
|
|
494
|
+
// @ts-expect-error
|
|
495
|
+
feePayer: account,
|
|
496
|
+
})
|
|
497
|
+
|
|
498
|
+
return Response.json(
|
|
499
|
+
RpcResponse.from({ result: serializedTransaction }, { request }),
|
|
500
|
+
)
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
if ((request as any).method === 'eth_signRawTransaction') {
|
|
504
|
+
const serialized = request.params?.[0] as `0x76${string}`
|
|
505
|
+
const transaction = Transaction.deserialize(serialized)
|
|
506
|
+
|
|
507
|
+
const serializedTransaction = await signTransaction(client, {
|
|
508
|
+
...transaction,
|
|
509
|
+
account,
|
|
510
|
+
// @ts-expect-error
|
|
511
|
+
feePayer: account,
|
|
512
|
+
})
|
|
513
|
+
|
|
514
|
+
return Response.json(
|
|
515
|
+
RpcResponse.from({ result: serializedTransaction }, { request }),
|
|
516
|
+
)
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
if (
|
|
520
|
+
request.method === 'eth_sendRawTransaction' ||
|
|
521
|
+
request.method === 'eth_sendRawTransactionSync'
|
|
522
|
+
) {
|
|
523
|
+
const serialized = request.params?.[0] as `0x76${string}`
|
|
524
|
+
const transaction = Transaction.deserialize(serialized)
|
|
525
|
+
|
|
526
|
+
const serializedTransaction = await signTransaction(client, {
|
|
527
|
+
...transaction,
|
|
528
|
+
account,
|
|
529
|
+
// @ts-expect-error
|
|
530
|
+
feePayer: account,
|
|
531
|
+
})
|
|
532
|
+
|
|
533
|
+
const result = await client.request({
|
|
534
|
+
method: request.method,
|
|
535
|
+
params: [serializedTransaction],
|
|
536
|
+
})
|
|
537
|
+
|
|
538
|
+
return Response.json(RpcResponse.from({ result }, { request }))
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
return Response.json(
|
|
542
|
+
RpcResponse.from(
|
|
543
|
+
{
|
|
544
|
+
error: new RpcResponse.MethodNotSupportedError({
|
|
545
|
+
message: `Method not supported: ${request.method}`,
|
|
546
|
+
}),
|
|
547
|
+
},
|
|
548
|
+
{ request },
|
|
549
|
+
),
|
|
550
|
+
{ status: 400 },
|
|
551
|
+
)
|
|
552
|
+
})
|
|
553
|
+
|
|
554
|
+
return router
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
export declare namespace feePayer {
|
|
558
|
+
export type Options = {
|
|
559
|
+
/** Account to use as the fee payer. */
|
|
560
|
+
account: LocalAccount
|
|
561
|
+
/** Function to call before handling the request. */
|
|
562
|
+
onRequest?: (request: RpcRequest.RpcRequest) => Promise<void>
|
|
563
|
+
/** Path to use for the handler. */
|
|
564
|
+
path?: string | undefined
|
|
565
|
+
} & OneOf<
|
|
566
|
+
| {
|
|
567
|
+
/** Client to use. */
|
|
568
|
+
client: Client
|
|
569
|
+
}
|
|
570
|
+
| {
|
|
571
|
+
/** Chain to use. */
|
|
572
|
+
chain: Chain
|
|
573
|
+
/** Transport to use. */
|
|
574
|
+
transport: Transport
|
|
575
|
+
}
|
|
576
|
+
>
|
|
577
|
+
}
|
package/src/server/Kv.ts
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
export type Kv = {
|
|
2
|
+
get: <value = unknown>(key: string) => Promise<value>
|
|
3
|
+
set: (key: string, value: unknown) => Promise<void>
|
|
4
|
+
delete: (key: string) => Promise<void>
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export function from<kv extends Kv>(kv: kv): kv {
|
|
8
|
+
return kv
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function cloudflare(kv: cloudflare.Parameters): Kv {
|
|
12
|
+
return from({
|
|
13
|
+
delete: kv.delete.bind(kv),
|
|
14
|
+
get: kv.get.bind(kv),
|
|
15
|
+
set: kv.put.bind(kv),
|
|
16
|
+
})
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export declare namespace cloudflare {
|
|
20
|
+
export type Parameters = {
|
|
21
|
+
get: <value = unknown>(key: string) => Promise<value>
|
|
22
|
+
put: (key: string, value: any) => Promise<void>
|
|
23
|
+
delete: (key: string) => Promise<void>
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function memory(): Kv {
|
|
28
|
+
const store = new Map<string, unknown>()
|
|
29
|
+
return from({
|
|
30
|
+
async delete(key) {
|
|
31
|
+
Promise.resolve(store.delete(key))
|
|
32
|
+
},
|
|
33
|
+
async get(key) {
|
|
34
|
+
return store.get(key) as any
|
|
35
|
+
},
|
|
36
|
+
async set(key, value) {
|
|
37
|
+
store.set(key, value)
|
|
38
|
+
},
|
|
39
|
+
})
|
|
40
|
+
}
|