tapimo 0.4.0 → 0.4.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/dist/apps/data/App.d.ts.map +1 -1
- package/dist/apps/data/App.js +20 -3
- package/dist/apps/data/App.js.map +1 -1
- package/dist/apps/data/routes/balances.d.ts +45 -45
- package/dist/apps/data/routes/coingecko.d.ts +176 -176
- package/dist/apps/data/routes/rpc.d.ts +31 -31
- package/dist/apps/data/routes/transactions.d.ts +281 -281
- package/dist/apps/data/routes/transfers.d.ts +58 -58
- package/dist/apps/management/routes/me.d.ts +17 -17
- package/dist/apps/management/routes/scopes.d.ts +11 -11
- package/dist/handlers/relay.d.ts +13 -10
- package/dist/handlers/relay.d.ts.map +1 -1
- package/dist/handlers/relay.js +46 -16
- package/dist/handlers/relay.js.map +1 -1
- package/package.json +1 -1
- package/src/apps/data/App.ts +24 -3
- package/src/apps/data/routes/rpc.test.ts +33 -0
- package/src/handlers/relay.test-d.ts +8 -0
- package/src/handlers/relay.test.ts +2 -2
- package/src/handlers/relay.ts +62 -22
|
@@ -184,46 +184,9 @@ export declare namespace schema {
|
|
|
184
184
|
export declare function transfers(): import("hono/hono-base").HonoBase<App.Environment, {
|
|
185
185
|
"/v1/transfers": {
|
|
186
186
|
$get: {
|
|
187
|
-
output:
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
blockNumber: number;
|
|
191
|
-
destinationToken?: {
|
|
192
|
-
address: `0x${string}`;
|
|
193
|
-
amount: string;
|
|
194
|
-
currency?: string | undefined;
|
|
195
|
-
decimals?: number | undefined;
|
|
196
|
-
logoUri?: string | undefined;
|
|
197
|
-
name?: string | undefined;
|
|
198
|
-
symbol?: string | undefined;
|
|
199
|
-
verified?: boolean | undefined;
|
|
200
|
-
} | undefined;
|
|
201
|
-
id: string;
|
|
202
|
-
logIndex: number;
|
|
203
|
-
memo?: string | undefined;
|
|
204
|
-
recipient: `0x${string}`;
|
|
205
|
-
sender: `0x${string}`;
|
|
206
|
-
sourceToken: {
|
|
207
|
-
address: `0x${string}`;
|
|
208
|
-
amount: string;
|
|
209
|
-
currency?: string | undefined;
|
|
210
|
-
decimals?: number | undefined;
|
|
211
|
-
logoUri?: string | undefined;
|
|
212
|
-
name?: string | undefined;
|
|
213
|
-
symbol?: string | undefined;
|
|
214
|
-
verified?: boolean | undefined;
|
|
215
|
-
};
|
|
216
|
-
timestamp: string;
|
|
217
|
-
transactionHash: `0x${string}`;
|
|
218
|
-
}[];
|
|
219
|
-
meta?: {
|
|
220
|
-
totalCountCapped: boolean;
|
|
221
|
-
totalCount: number;
|
|
222
|
-
} | undefined;
|
|
223
|
-
nextCursor: string | null;
|
|
224
|
-
};
|
|
225
|
-
outputFormat: "json";
|
|
226
|
-
status: 200;
|
|
187
|
+
output: null;
|
|
188
|
+
outputFormat: "body";
|
|
189
|
+
status: 402;
|
|
227
190
|
input: {
|
|
228
191
|
query?: {
|
|
229
192
|
recipient?: string | string[];
|
|
@@ -274,14 +237,14 @@ export declare function transfers(): import("hono/hono-base").HonoBase<App.Envir
|
|
|
274
237
|
} | {
|
|
275
238
|
output: {
|
|
276
239
|
error: {
|
|
277
|
-
code: "
|
|
240
|
+
code: "api_key_invalid" | "api_key_missing";
|
|
278
241
|
details?: readonly Response.error.Detail[] | undefined;
|
|
279
242
|
message: string;
|
|
280
243
|
};
|
|
281
244
|
requestId: string;
|
|
282
245
|
};
|
|
283
246
|
outputFormat: "json";
|
|
284
|
-
status:
|
|
247
|
+
status: 401;
|
|
285
248
|
input: {
|
|
286
249
|
query?: {
|
|
287
250
|
recipient?: string | string[];
|
|
@@ -303,14 +266,14 @@ export declare function transfers(): import("hono/hono-base").HonoBase<App.Envir
|
|
|
303
266
|
} | {
|
|
304
267
|
output: {
|
|
305
268
|
error: {
|
|
306
|
-
code: "
|
|
269
|
+
code: "api_key_forbidden";
|
|
307
270
|
details?: readonly Response.error.Detail[] | undefined;
|
|
308
271
|
message: string;
|
|
309
272
|
};
|
|
310
273
|
requestId: string;
|
|
311
274
|
};
|
|
312
275
|
outputFormat: "json";
|
|
313
|
-
status:
|
|
276
|
+
status: 403;
|
|
314
277
|
input: {
|
|
315
278
|
query?: {
|
|
316
279
|
recipient?: string | string[];
|
|
@@ -330,9 +293,16 @@ export declare function transfers(): import("hono/hono-base").HonoBase<App.Envir
|
|
|
330
293
|
} | undefined;
|
|
331
294
|
};
|
|
332
295
|
} | {
|
|
333
|
-
output:
|
|
334
|
-
|
|
335
|
-
|
|
296
|
+
output: {
|
|
297
|
+
error: {
|
|
298
|
+
code: "payment_required" | "rate_limit_exceeded";
|
|
299
|
+
details?: readonly Response.error.Detail[] | undefined;
|
|
300
|
+
message: string;
|
|
301
|
+
};
|
|
302
|
+
requestId: string;
|
|
303
|
+
};
|
|
304
|
+
outputFormat: "json";
|
|
305
|
+
status: 429;
|
|
336
306
|
input: {
|
|
337
307
|
query?: {
|
|
338
308
|
recipient?: string | string[];
|
|
@@ -354,14 +324,14 @@ export declare function transfers(): import("hono/hono-base").HonoBase<App.Envir
|
|
|
354
324
|
} | {
|
|
355
325
|
output: {
|
|
356
326
|
error: {
|
|
357
|
-
code: "
|
|
327
|
+
code: "query_invalid";
|
|
358
328
|
details?: readonly Response.error.Detail[] | undefined;
|
|
359
329
|
message: string;
|
|
360
330
|
};
|
|
361
331
|
requestId: string;
|
|
362
332
|
};
|
|
363
333
|
outputFormat: "json";
|
|
364
|
-
status:
|
|
334
|
+
status: 400;
|
|
365
335
|
input: {
|
|
366
336
|
query?: {
|
|
367
337
|
recipient?: string | string[];
|
|
@@ -383,14 +353,14 @@ export declare function transfers(): import("hono/hono-base").HonoBase<App.Envir
|
|
|
383
353
|
} | {
|
|
384
354
|
output: {
|
|
385
355
|
error: {
|
|
386
|
-
code: "
|
|
356
|
+
code: "upstream_error";
|
|
387
357
|
details?: readonly Response.error.Detail[] | undefined;
|
|
388
358
|
message: string;
|
|
389
359
|
};
|
|
390
360
|
requestId: string;
|
|
391
361
|
};
|
|
392
362
|
outputFormat: "json";
|
|
393
|
-
status:
|
|
363
|
+
status: 502;
|
|
394
364
|
input: {
|
|
395
365
|
query?: {
|
|
396
366
|
recipient?: string | string[];
|
|
@@ -411,15 +381,45 @@ export declare function transfers(): import("hono/hono-base").HonoBase<App.Envir
|
|
|
411
381
|
};
|
|
412
382
|
} | {
|
|
413
383
|
output: {
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
384
|
+
data: {
|
|
385
|
+
attribution?: string | undefined;
|
|
386
|
+
blockNumber: number;
|
|
387
|
+
destinationToken?: {
|
|
388
|
+
address: `0x${string}`;
|
|
389
|
+
amount: string;
|
|
390
|
+
currency?: string | undefined;
|
|
391
|
+
decimals?: number | undefined;
|
|
392
|
+
logoUri?: string | undefined;
|
|
393
|
+
name?: string | undefined;
|
|
394
|
+
symbol?: string | undefined;
|
|
395
|
+
verified?: boolean | undefined;
|
|
396
|
+
} | undefined;
|
|
397
|
+
id: string;
|
|
398
|
+
logIndex: number;
|
|
399
|
+
memo?: string | undefined;
|
|
400
|
+
recipient: `0x${string}`;
|
|
401
|
+
sender: `0x${string}`;
|
|
402
|
+
sourceToken: {
|
|
403
|
+
address: `0x${string}`;
|
|
404
|
+
amount: string;
|
|
405
|
+
currency?: string | undefined;
|
|
406
|
+
decimals?: number | undefined;
|
|
407
|
+
logoUri?: string | undefined;
|
|
408
|
+
name?: string | undefined;
|
|
409
|
+
symbol?: string | undefined;
|
|
410
|
+
verified?: boolean | undefined;
|
|
411
|
+
};
|
|
412
|
+
timestamp: string;
|
|
413
|
+
transactionHash: `0x${string}`;
|
|
414
|
+
}[];
|
|
415
|
+
meta?: {
|
|
416
|
+
totalCountCapped: boolean;
|
|
417
|
+
totalCount: number;
|
|
418
|
+
} | undefined;
|
|
419
|
+
nextCursor: string | null;
|
|
420
420
|
};
|
|
421
421
|
outputFormat: "json";
|
|
422
|
-
status:
|
|
422
|
+
status: 200;
|
|
423
423
|
input: {
|
|
424
424
|
query?: {
|
|
425
425
|
recipient?: string | string[];
|
|
@@ -19,17 +19,6 @@ export declare namespace schema {
|
|
|
19
19
|
export declare function me(): import("hono/hono-base").HonoBase<App.Environment, {
|
|
20
20
|
"/v1/me": {
|
|
21
21
|
$get: {
|
|
22
|
-
output: {
|
|
23
|
-
address?: string | undefined;
|
|
24
|
-
createdAt: string;
|
|
25
|
-
email?: string | undefined;
|
|
26
|
-
id: string;
|
|
27
|
-
updatedAt: string;
|
|
28
|
-
};
|
|
29
|
-
outputFormat: "json";
|
|
30
|
-
status: 200;
|
|
31
|
-
input: {};
|
|
32
|
-
} | {
|
|
33
22
|
output: {
|
|
34
23
|
error: {
|
|
35
24
|
code: "api_key_malformed";
|
|
@@ -56,38 +45,49 @@ export declare function me(): import("hono/hono-base").HonoBase<App.Environment,
|
|
|
56
45
|
} | {
|
|
57
46
|
output: {
|
|
58
47
|
error: {
|
|
59
|
-
code: "
|
|
48
|
+
code: "upstream_error";
|
|
60
49
|
details?: readonly Response.error.Detail[] | undefined;
|
|
61
50
|
message: string;
|
|
62
51
|
};
|
|
63
52
|
requestId: string;
|
|
64
53
|
};
|
|
65
54
|
outputFormat: "json";
|
|
66
|
-
status:
|
|
55
|
+
status: 502;
|
|
67
56
|
input: {};
|
|
68
57
|
} | {
|
|
69
58
|
output: {
|
|
70
59
|
error: {
|
|
71
|
-
code: "
|
|
60
|
+
code: "forbidden";
|
|
72
61
|
details?: readonly Response.error.Detail[] | undefined;
|
|
73
62
|
message: string;
|
|
74
63
|
};
|
|
75
64
|
requestId: string;
|
|
76
65
|
};
|
|
77
66
|
outputFormat: "json";
|
|
78
|
-
status:
|
|
67
|
+
status: 403;
|
|
79
68
|
input: {};
|
|
80
69
|
} | {
|
|
81
70
|
output: {
|
|
82
71
|
error: {
|
|
83
|
-
code: "
|
|
72
|
+
code: "user_not_found";
|
|
84
73
|
details?: readonly Response.error.Detail[] | undefined;
|
|
85
74
|
message: string;
|
|
86
75
|
};
|
|
87
76
|
requestId: string;
|
|
88
77
|
};
|
|
89
78
|
outputFormat: "json";
|
|
90
|
-
status:
|
|
79
|
+
status: 404;
|
|
80
|
+
input: {};
|
|
81
|
+
} | {
|
|
82
|
+
output: {
|
|
83
|
+
address?: string | undefined;
|
|
84
|
+
createdAt: string;
|
|
85
|
+
email?: string | undefined;
|
|
86
|
+
id: string;
|
|
87
|
+
updatedAt: string;
|
|
88
|
+
};
|
|
89
|
+
outputFormat: "json";
|
|
90
|
+
status: 200;
|
|
91
91
|
input: {};
|
|
92
92
|
};
|
|
93
93
|
};
|
|
@@ -43,17 +43,6 @@ export declare namespace schema {
|
|
|
43
43
|
export declare function scopes(): import("hono/hono-base").HonoBase<App.Environment, {
|
|
44
44
|
"/v1/scopes": {
|
|
45
45
|
$get: {
|
|
46
|
-
output: {
|
|
47
|
-
data: {
|
|
48
|
-
description: string;
|
|
49
|
-
scope: "data:read" | "indexer:query" | "rpc-relay:read" | "rpc-relay:sponsor" | "webhooks:read" | "webhooks:write";
|
|
50
|
-
selfServe: boolean;
|
|
51
|
-
}[];
|
|
52
|
-
};
|
|
53
|
-
outputFormat: "json";
|
|
54
|
-
status: 200;
|
|
55
|
-
input: {};
|
|
56
|
-
} | {
|
|
57
46
|
output: {
|
|
58
47
|
error: {
|
|
59
48
|
code: "api_key_malformed";
|
|
@@ -101,6 +90,17 @@ export declare function scopes(): import("hono/hono-base").HonoBase<App.Environm
|
|
|
101
90
|
outputFormat: "json";
|
|
102
91
|
status: 429;
|
|
103
92
|
input: {};
|
|
93
|
+
} | {
|
|
94
|
+
output: {
|
|
95
|
+
data: {
|
|
96
|
+
description: string;
|
|
97
|
+
scope: "data:read" | "indexer:query" | "rpc-relay:read" | "rpc-relay:sponsor" | "webhooks:read" | "webhooks:write";
|
|
98
|
+
selfServe: boolean;
|
|
99
|
+
}[];
|
|
100
|
+
};
|
|
101
|
+
outputFormat: "json";
|
|
102
|
+
status: 200;
|
|
103
|
+
input: {};
|
|
104
104
|
};
|
|
105
105
|
};
|
|
106
106
|
}, "/", "/v1/scopes">;
|
package/dist/handlers/relay.d.ts
CHANGED
|
@@ -12,9 +12,9 @@ import * as Sponsorship from './internal/sponsorship.js';
|
|
|
12
12
|
*
|
|
13
13
|
* @example
|
|
14
14
|
* ```ts
|
|
15
|
-
* import { Handler } from '
|
|
15
|
+
* import { Handler } from 'tapimo'
|
|
16
16
|
*
|
|
17
|
-
* const handler = Handler.relay({
|
|
17
|
+
* const handler = Handler.relay({ apiKey: process.env.TEMPO_API_KEY })
|
|
18
18
|
*
|
|
19
19
|
* // Plug handler into your server framework of choice:
|
|
20
20
|
* createServer(handler.listener) // Node.js
|
|
@@ -31,10 +31,9 @@ import * as Sponsorship from './internal/sponsorship.js';
|
|
|
31
31
|
*
|
|
32
32
|
* ```ts
|
|
33
33
|
* import { privateKeyToAccount } from 'viem/accounts'
|
|
34
|
-
* import { Handler } from '
|
|
34
|
+
* import { Handler } from 'tapimo'
|
|
35
35
|
*
|
|
36
36
|
* const handler = Handler.relay({
|
|
37
|
-
* getClient,
|
|
38
37
|
* feePayer: {
|
|
39
38
|
* account: privateKeyToAccount('0x...'),
|
|
40
39
|
* },
|
|
@@ -44,9 +43,14 @@ import * as Sponsorship from './internal/sponsorship.js';
|
|
|
44
43
|
* @param options - Options.
|
|
45
44
|
* @returns Request handler.
|
|
46
45
|
*/
|
|
47
|
-
export declare function relay(options
|
|
46
|
+
export declare function relay(options?: relay.Options): Handler;
|
|
48
47
|
export declare namespace relay {
|
|
49
48
|
type Options = from.Options & {
|
|
49
|
+
/**
|
|
50
|
+
* Tempo API key used by the default RPC clients and token resolver. When
|
|
51
|
+
* omitted, RPC clients use each chain's default transport.
|
|
52
|
+
*/
|
|
53
|
+
apiKey?: string | undefined;
|
|
50
54
|
/**
|
|
51
55
|
* Auto-swap options.
|
|
52
56
|
*/
|
|
@@ -57,9 +61,9 @@ export declare namespace relay {
|
|
|
57
61
|
/**
|
|
58
62
|
* Resolves a viem client for a chain id. The handler resolves the client
|
|
59
63
|
* from the `chainId` in the incoming transaction (falling back to the
|
|
60
|
-
* client's default chain).
|
|
64
|
+
* client's default chain). Defaults to Tempo mainnet and testnet clients.
|
|
61
65
|
*/
|
|
62
|
-
getClient
|
|
66
|
+
getClient?: ((chainId?: number) => Client) | undefined;
|
|
63
67
|
/**
|
|
64
68
|
* Fee payer / sponsor configuration. When provided, the relay will
|
|
65
69
|
* sign `feePayerSignature` on the filled transaction.
|
|
@@ -113,9 +117,8 @@ export declare namespace relay {
|
|
|
113
117
|
/** Path to use for the handler. @default "/" */
|
|
114
118
|
path?: string | undefined;
|
|
115
119
|
/**
|
|
116
|
-
* Resolves the known fee-token candidates for a chain.
|
|
117
|
-
*
|
|
118
|
-
* fee-token resolution. Omit to disable fee-token resolution.
|
|
120
|
+
* Resolves the known fee-token candidates for a chain. Defaults to the
|
|
121
|
+
* Tempo API verified token list for mainnet and testnet.
|
|
119
122
|
*/
|
|
120
123
|
resolveTokens?: ((chainId: number) => readonly Address[] | Promise<readonly Address[]>) | undefined;
|
|
121
124
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"relay.d.ts","sourceRoot":"","sources":["../../src/handlers/relay.ts"],"names":[],"mappings":"AAAA,OAAO,EAAiB,UAAU,EAAe,MAAM,IAAI,CAAA;AAE3D,OAAO,EACL,KAAK,OAAO,EAEZ,KAAK,MAAM,EASZ,MAAM,MAAM,CAAA;AACb,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,eAAe,CAAA;
|
|
1
|
+
{"version":3,"file":"relay.d.ts","sourceRoot":"","sources":["../../src/handlers/relay.ts"],"names":[],"mappings":"AAAA,OAAO,EAAiB,UAAU,EAAe,MAAM,IAAI,CAAA;AAE3D,OAAO,EACL,KAAK,OAAO,EAEZ,KAAK,MAAM,EASZ,MAAM,MAAM,CAAA;AACb,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,eAAe,CAAA;AAQjD,OAAO,EAAE,KAAK,OAAO,EAAE,IAAI,EAAE,MAAM,eAAe,CAAA;AAClD,OAAO,KAAK,KAAK,MAAM,sBAAsB,CAAA;AAC7C,OAAO,KAAK,QAAQ,MAAM,wBAAwB,CAAA;AAClD,OAAO,KAAK,WAAW,MAAM,2BAA2B,CAAA;AAKxD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AACH,wBAAgB,KAAK,CAAC,OAAO,GAAE,KAAK,CAAC,OAAY,GAAG,OAAO,CAskB1D;AAmCD,yBAAiB,KAAK,CAAC;IACrB,KAAY,OAAO,GAAG,IAAI,CAAC,OAAO,GAAG;QACnC;;;WAGG;QACH,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;QAC3B;;WAEG;QACH,QAAQ,CAAC,EACL,KAAK,GACL;YACE,yDAAyD;YACzD,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;SAC9B,GACD,SAAS,CAAA;QACb;;;;WAIG;QACH,SAAS,CAAC,EAAE,CAAC,CAAC,OAAO,CAAC,EAAE,MAAM,KAAK,MAAM,CAAC,GAAG,SAAS,CAAA;QACtD;;;WAGG;QACH,QAAQ,CAAC,EACL;YACE,uCAAuC;YACvC,OAAO,EAAE,YAAY,CAAA;YACrB,4DAA4D;YAC5D,QAAQ,CAAC,EAAE,OAAO,GAAG,SAAS,CAAA;YAC9B;;eAEG;YACH,WAAW,CAAC,EAAE,WAAW,CAAC,oBAAoB,CAAC,OAAO,CAAC,aAAa,CAAC,CAAA;YACrE;;;eAGG;YACH,QAAQ,CAAC,EAAE,WAAW,CAAC,QAAQ,GAAG,SAAS,CAAA;YAC3C,gEAAgE;YAChE,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;YACzB,uDAAuD;YACvD,GAAG,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;SACzB,GACD,SAAS,CAAA;QACb;;;;WAIG;QACH,QAAQ,CAAC,EAAE,QAAQ,CAAC,OAAO,GAAG,SAAS,CAAA;QACvC;;;;;;WAMG;QACH,QAAQ,CAAC,EAAE,KAAK,GAAG,SAAS,CAAA;QAC5B;;;;WAIG;QACH,wBAAwB,CAAC,EAAE,OAAO,GAAG,SAAS,CAAA;QAC9C;;;WAGG;QACH,KAAK,CAAC,EAAE,KAAK,CAAC,KAAK,GAAG,SAAS,CAAA;QAC/B,oDAAoD;QACpD,SAAS,CAAC,EAAE,CAAC,CAAC,OAAO,EAAE,UAAU,CAAC,UAAU,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC,GAAG,SAAS,CAAA;QAC3E,gDAAgD;QAChD,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;QACzB;;;WAGG;QACH,aAAa,CAAC,EACV,CAAC,CAAC,OAAO,EAAE,MAAM,KAAK,SAAS,OAAO,EAAE,GAAG,OAAO,CAAC,SAAS,OAAO,EAAE,CAAC,CAAC,GACvE,SAAS,CAAA;KACd,CAAA;CACF"}
|
package/dist/handlers/relay.js
CHANGED
|
@@ -3,12 +3,15 @@ import { Transaction as core_Transaction } from 'ox/tempo';
|
|
|
3
3
|
import { createClient, decodeFunctionData, formatUnits, http, isAddress, parseEventLogs, zeroAddress, } from 'viem';
|
|
4
4
|
import { simulateCalls } from 'viem/actions';
|
|
5
5
|
import { Abis, Actions, Addresses, Capabilities, VirtualAddress } from 'viem/tempo';
|
|
6
|
+
import { tempoMainnet, tempoTestnet } from 'viem/tempo/chains';
|
|
6
7
|
import * as ExecutionError from './internal/executionError.js';
|
|
8
|
+
import * as TempoApi from '../Client.js';
|
|
7
9
|
import { from } from '../Handler.js';
|
|
8
10
|
import * as Store from '../internal/Store.js';
|
|
9
11
|
import * as Multisig from './internal/multisig.js';
|
|
10
12
|
import * as Sponsorship from './internal/sponsorship.js';
|
|
11
13
|
import * as Utils from './internal/utils.js';
|
|
14
|
+
const defaultChains = [tempoMainnet, tempoTestnet];
|
|
12
15
|
/**
|
|
13
16
|
* Instantiates a relay handler that proxies `eth_fillTransaction`
|
|
14
17
|
* with wallet-aware enrichment (fee token resolution, simulation,
|
|
@@ -16,9 +19,9 @@ import * as Utils from './internal/utils.js';
|
|
|
16
19
|
*
|
|
17
20
|
* @example
|
|
18
21
|
* ```ts
|
|
19
|
-
* import { Handler } from '
|
|
22
|
+
* import { Handler } from 'tapimo'
|
|
20
23
|
*
|
|
21
|
-
* const handler = Handler.relay({
|
|
24
|
+
* const handler = Handler.relay({ apiKey: process.env.TEMPO_API_KEY })
|
|
22
25
|
*
|
|
23
26
|
* // Plug handler into your server framework of choice:
|
|
24
27
|
* createServer(handler.listener) // Node.js
|
|
@@ -35,10 +38,9 @@ import * as Utils from './internal/utils.js';
|
|
|
35
38
|
*
|
|
36
39
|
* ```ts
|
|
37
40
|
* import { privateKeyToAccount } from 'viem/accounts'
|
|
38
|
-
* import { Handler } from '
|
|
41
|
+
* import { Handler } from 'tapimo'
|
|
39
42
|
*
|
|
40
43
|
* const handler = Handler.relay({
|
|
41
|
-
* getClient,
|
|
42
44
|
* feePayer: {
|
|
43
45
|
* account: privateKeyToAccount('0x...'),
|
|
44
46
|
* },
|
|
@@ -48,18 +50,17 @@ import * as Utils from './internal/utils.js';
|
|
|
48
50
|
* @param options - Options.
|
|
49
51
|
* @returns Request handler.
|
|
50
52
|
*/
|
|
51
|
-
export function relay(options) {
|
|
52
|
-
const { getClient, internal_allowUnsafeUrls = false, onRequest, path = '/', resolveTokens, ...rest } = options;
|
|
53
|
+
export function relay(options = {}) {
|
|
54
|
+
const { apiKey, getClient: getClient_, internal_allowUnsafeUrls = false, onRequest, path = '/', resolveTokens: resolveTokens_, ...rest } = options;
|
|
55
|
+
const getClient = getClient_ ?? defaultGetClient({ apiKey });
|
|
56
|
+
const resolveTokens = resolveTokens_ ?? defaultResolveTokens({ apiKey });
|
|
53
57
|
const feePayerOptions = options.feePayer;
|
|
54
58
|
// Adapt the state store into the multisig operation store once, up front.
|
|
55
59
|
const multisigOptions = options.multisig
|
|
56
60
|
? { ...options.multisig, store: Multisig.fromStore(options.multisig.store) }
|
|
57
61
|
: undefined;
|
|
58
|
-
// Resolves the chain's fee-token candidates through the caller-provided
|
|
59
|
-
// resolver. An unconfigured resolver (or a chain it has no list for) yields
|
|
60
|
-
// an empty candidate set.
|
|
61
62
|
const getTokens = async (chainId) => {
|
|
62
|
-
if (
|
|
63
|
+
if (chainId === undefined)
|
|
63
64
|
return [];
|
|
64
65
|
return await resolveTokens(chainId);
|
|
65
66
|
};
|
|
@@ -69,7 +70,7 @@ export function relay(options) {
|
|
|
69
70
|
const store = options.cache;
|
|
70
71
|
const features = {
|
|
71
72
|
autoSwap: options.autoSwap ?? options.features === 'all',
|
|
72
|
-
feeTokenResolution: options.features === 'all' || Boolean(
|
|
73
|
+
feeTokenResolution: options.features === 'all' || Boolean(resolveTokens_),
|
|
73
74
|
simulate: options.features === 'all',
|
|
74
75
|
};
|
|
75
76
|
const autoSwap = (() => {
|
|
@@ -130,13 +131,14 @@ export function relay(options) {
|
|
|
130
131
|
let filled;
|
|
131
132
|
let sponsored = false;
|
|
132
133
|
let feeToken = sponsoredFeeToken;
|
|
134
|
+
const tokens = getTokens(chainId);
|
|
133
135
|
// Lazily resolve a swap source token when autoSwap needs one.
|
|
134
136
|
const resolveFeeTokenForSwap = from
|
|
135
137
|
? async (insufficientToken) => resolveFeeToken(client, {
|
|
136
138
|
account: from,
|
|
137
139
|
feeToken: undefined,
|
|
138
140
|
store,
|
|
139
|
-
tokens: (await
|
|
141
|
+
tokens: (await tokens).filter((t) => t.toLowerCase() !== insufficientToken.toLowerCase()),
|
|
140
142
|
})
|
|
141
143
|
: undefined;
|
|
142
144
|
// When no sponsor will pay, prefer fee tokens the user actually
|
|
@@ -144,7 +146,7 @@ export function relay(options) {
|
|
|
144
146
|
// the transaction is calling — typically the token being
|
|
145
147
|
// transferred — so a user transferring USDC.e can pay gas in
|
|
146
148
|
// USDC.e even when the configured list defaults to pathUSD.
|
|
147
|
-
const configuredTokens = await
|
|
149
|
+
const configuredTokens = await tokens;
|
|
148
150
|
const unsponsoredTokens = [
|
|
149
151
|
...configuredTokens,
|
|
150
152
|
...callTargetTokens(baseTx).filter((t) => !configuredTokens.some((rt) => rt.toLowerCase() === t.toLowerCase())),
|
|
@@ -168,7 +170,7 @@ export function relay(options) {
|
|
|
168
170
|
// Default to the chain's first token so the broadcast envelope
|
|
169
171
|
// always carries a feeToken the chain can charge.
|
|
170
172
|
if (!feeToken)
|
|
171
|
-
feeToken =
|
|
173
|
+
feeToken = configuredTokens[0];
|
|
172
174
|
const transaction = {
|
|
173
175
|
...baseTx,
|
|
174
176
|
feePayer: true,
|
|
@@ -282,8 +284,7 @@ export function relay(options) {
|
|
|
282
284
|
const transaction_filled = filled.transaction;
|
|
283
285
|
const swap = 'swap' in filled ? filled.swap : undefined;
|
|
284
286
|
if (!feeToken)
|
|
285
|
-
feeToken =
|
|
286
|
-
transaction_filled.feeToken ?? (await getTokens(chainId))[0];
|
|
287
|
+
feeToken = transaction_filled.feeToken ?? configuredTokens[0];
|
|
287
288
|
// Parallelize: simulate, fee payer signing, and autoSwap metadata.
|
|
288
289
|
const alreadySigned = 'feePayerSignature' in transaction_filled &&
|
|
289
290
|
transaction_filled.feePayerSignature != null;
|
|
@@ -553,6 +554,35 @@ export function relay(options) {
|
|
|
553
554
|
router.post(`${path === '/' ? '' : path}/:chainId`, handlePost);
|
|
554
555
|
return router;
|
|
555
556
|
}
|
|
557
|
+
function defaultGetClient(options) {
|
|
558
|
+
const clients = new Map();
|
|
559
|
+
for (const chain of defaultChains) {
|
|
560
|
+
const transport = options.apiKey
|
|
561
|
+
? http(new URL(`/rpc/${chain.id}`, TempoApi.defaultUrl).toString(), {
|
|
562
|
+
fetchOptions: { headers: { 'tempo-api-key': options.apiKey } },
|
|
563
|
+
})
|
|
564
|
+
: http();
|
|
565
|
+
clients.set(chain.id, createClient({ batch: { multicall: { deployless: true } }, chain, transport }));
|
|
566
|
+
}
|
|
567
|
+
return (chainId = tempoMainnet.id) => {
|
|
568
|
+
const client = clients.get(chainId);
|
|
569
|
+
if (!client)
|
|
570
|
+
throw new Error(`Chain ${chainId} not configured`);
|
|
571
|
+
return client;
|
|
572
|
+
};
|
|
573
|
+
}
|
|
574
|
+
function defaultResolveTokens(options) {
|
|
575
|
+
const client = TempoApi.create({ apiKey: options.apiKey });
|
|
576
|
+
return async (chainId) => {
|
|
577
|
+
if (!defaultChains.some((chain) => chain.id === chainId))
|
|
578
|
+
return [];
|
|
579
|
+
const response = await client.v1.tokenlist.$get({ query: { chainId: String(chainId) } });
|
|
580
|
+
if (response.status !== 200)
|
|
581
|
+
return [];
|
|
582
|
+
const { tokens } = await response.json();
|
|
583
|
+
return tokens.map((token) => token.address);
|
|
584
|
+
};
|
|
585
|
+
}
|
|
556
586
|
// TODO: cleanup
|
|
557
587
|
async function fill(client, options) {
|
|
558
588
|
const { autoSwap, feeToken, store, transaction: request } = options;
|