accounts 0.6.4 → 0.6.6
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 +12 -0
- package/dist/core/Schema.d.ts +2 -2
- package/dist/core/zod/rpc.d.ts +1 -1
- package/dist/core/zod/rpc.js +1 -1
- package/dist/core/zod/rpc.js.map +1 -1
- package/dist/server/internal/handlers/relay.d.ts.map +1 -1
- package/dist/server/internal/handlers/relay.js +171 -155
- package/dist/server/internal/handlers/relay.js.map +1 -1
- package/dist/server/internal/handlers/utils.d.ts +19 -1
- package/dist/server/internal/handlers/utils.d.ts.map +1 -1
- package/dist/server/internal/handlers/utils.js +11 -7
- package/dist/server/internal/handlers/utils.js.map +1 -1
- package/package.json +1 -1
- package/src/core/zod/rpc.ts +1 -1
- package/src/server/internal/handlers/relay.test.ts +21 -1
- package/src/server/internal/handlers/relay.ts +219 -198
- package/src/server/internal/handlers/utils.ts +20 -19
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { RpcRequest } from 'ox';
|
|
1
|
+
import { RpcRequest, RpcResponse } from 'ox';
|
|
2
2
|
import { Transaction as core_Transaction } from 'ox/tempo';
|
|
3
3
|
import { type Client } from 'viem';
|
|
4
4
|
import * as z from 'zod/mini';
|
|
@@ -6,6 +6,24 @@ export declare function resolveChainId(value: unknown): number | undefined;
|
|
|
6
6
|
export declare function formatFillTransactionRequest(client: Client, value: Record<string, unknown>): Record<string, unknown>;
|
|
7
7
|
export declare function normalizeFillTransactionRequest(value: Record<string, unknown>): Record<string, unknown>;
|
|
8
8
|
export declare function normalizeTempoTransaction(value: Record<string, unknown> | undefined): core_Transaction.Transaction<false, bigint, number>;
|
|
9
|
+
/** Returns a raw JSON-RPC error response object (not wrapped in a `Response`). */
|
|
10
|
+
export declare function rpcErrorJson(request: RpcRequest.RpcRequest, error: unknown): {
|
|
11
|
+
readonly error: RpcResponse.InvalidParamsError;
|
|
12
|
+
readonly id: number;
|
|
13
|
+
readonly jsonrpc: "2.0";
|
|
14
|
+
} | {
|
|
15
|
+
readonly error: RpcResponse.MethodNotSupportedError;
|
|
16
|
+
readonly id: number;
|
|
17
|
+
readonly jsonrpc: "2.0";
|
|
18
|
+
} | {
|
|
19
|
+
readonly error: {
|
|
20
|
+
readonly data?: {};
|
|
21
|
+
readonly code: number;
|
|
22
|
+
readonly message: string;
|
|
23
|
+
};
|
|
24
|
+
readonly id: number;
|
|
25
|
+
readonly jsonrpc: "2.0";
|
|
26
|
+
};
|
|
9
27
|
export declare function rpcError(request: RpcRequest.RpcRequest, error: unknown): Response;
|
|
10
28
|
export declare function rpcResult(request: RpcRequest.RpcRequest, result: unknown): Response;
|
|
11
29
|
export declare const parseParams: z.ZodMiniReadonly<z.ZodMiniTuple<readonly [z.ZodMiniRecord<z.ZodMiniString<string>, z.ZodMiniUnknown>], null>>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../../../src/server/internal/handlers/utils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAO,UAAU,
|
|
1
|
+
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../../../src/server/internal/handlers/utils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAO,UAAU,EAAE,WAAW,EAAE,MAAM,IAAI,CAAA;AACjD,OAAO,EAAE,WAAW,IAAI,gBAAgB,EAAE,MAAM,UAAU,CAAA;AAC1D,OAAO,EAAE,KAAK,MAAM,EAAa,MAAM,MAAM,CAAA;AAC7C,OAAO,KAAK,CAAC,MAAM,UAAU,CAAA;AAE7B,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,sBAK5C;AAED,wBAAgB,4BAA4B,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,2BAI1F;AAED,wBAAgB,+BAA+B,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,2BAW7E;AAOD,wBAAgB,yBAAyB,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,uDAGnF;AAED,kFAAkF;AAClF,wBAAgB,YAAY,CAAC,OAAO,EAAE,UAAU,CAAC,UAAU,EAAE,KAAK,EAAE,OAAO;;;;;;;;;;;;;;;;EA2B1E;AAED,wBAAgB,QAAQ,CAAC,OAAO,EAAE,UAAU,CAAC,UAAU,EAAE,KAAK,EAAE,OAAO,YAEtE;AAED,wBAAgB,SAAS,CAAC,OAAO,EAAE,UAAU,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,YAExE;AAED,eAAO,MAAM,WAAW,gHAA2D,CAAA"}
|
|
@@ -41,24 +41,28 @@ export function normalizeTempoTransaction(value) {
|
|
|
41
41
|
throw new Error('Expected `tx` in eth_fillTransaction response.');
|
|
42
42
|
return core_Transaction.fromRpc({ type: '0x76', ...value });
|
|
43
43
|
}
|
|
44
|
-
|
|
44
|
+
/** Returns a raw JSON-RPC error response object (not wrapped in a `Response`). */
|
|
45
|
+
export function rpcErrorJson(request, error) {
|
|
45
46
|
if (error instanceof RpcResponse.InvalidParamsError)
|
|
46
|
-
return
|
|
47
|
+
return RpcResponse.from({ error }, { request });
|
|
47
48
|
if (error instanceof RpcResponse.MethodNotSupportedError)
|
|
48
|
-
return
|
|
49
|
+
return RpcResponse.from({ error }, { request });
|
|
49
50
|
if (error.name === 'ZodError')
|
|
50
|
-
return
|
|
51
|
+
return RpcResponse.from({
|
|
51
52
|
error: new RpcResponse.InvalidParamsError({
|
|
52
53
|
message: error.message,
|
|
53
54
|
}),
|
|
54
|
-
}, { request })
|
|
55
|
+
}, { request });
|
|
55
56
|
const inner = resolveError(error);
|
|
56
57
|
const message = inner.message ?? error.message;
|
|
57
58
|
const code = inner.code ?? -32603;
|
|
58
59
|
const data = inner.data;
|
|
59
|
-
return
|
|
60
|
+
return RpcResponse.from({
|
|
60
61
|
error: { code, message, ...(data ? { data } : {}) },
|
|
61
|
-
}, { request })
|
|
62
|
+
}, { request });
|
|
63
|
+
}
|
|
64
|
+
export function rpcError(request, error) {
|
|
65
|
+
return Response.json(rpcErrorJson(request, error));
|
|
62
66
|
}
|
|
63
67
|
export function rpcResult(request, result) {
|
|
64
68
|
return Response.json(RpcResponse.from({ result }, { request }));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.js","sourceRoot":"","sources":["../../../../src/server/internal/handlers/utils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,IAAI,CAAA;AACjD,OAAO,EAAE,WAAW,IAAI,gBAAgB,EAAE,MAAM,UAAU,CAAA;AAC1D,OAAO,EAAe,SAAS,EAAE,MAAM,MAAM,CAAA;AAC7C,OAAO,KAAK,CAAC,MAAM,UAAU,CAAA;AAE7B,MAAM,UAAU,cAAc,CAAC,KAAc;IAC3C,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAA;IAC3C,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,MAAM,CAAC,KAAK,CAAC,CAAA;IACnD,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,GAAG,CAAC,QAAQ,CAAC,KAAK,CAAC;QAAE,OAAO,GAAG,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAA;IAChF,OAAO,SAAS,CAAA;AAClB,CAAC;AAED,MAAM,UAAU,4BAA4B,CAAC,MAAc,EAAE,KAA8B;IACzF,MAAM,MAAM,GAAG,MAAM,CAAC,KAAK,EAAE,UAAU,EAAE,kBAAkB,EAAE,MAAM,CAAA;IACnE,IAAI,CAAC,MAAM;QAAE,OAAO,KAAK,CAAA;IACzB,OAAO,MAAM,CAAC,EAAE,GAAG,KAAK,EAAW,EAAE,iBAAiB,CAA4B,CAAA;AACpF,CAAC;AAED,MAAM,UAAU,+BAA+B,CAAC,KAA8B;IAC5E,IAAI,OAAO,KAAK,CAAC,EAAE,KAAK,WAAW,IAAI,OAAO,KAAK,CAAC,IAAI,KAAK,WAAW;QAAE,OAAO,KAAK,CAAA;IACtF,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,KAAK,CAAC,KAAK,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,KAAK,CAAA;IACzE,MAAM,CAAC,IAAI,CAAC,GAAG,KAAK,CAAC,KAAuC,CAAA;IAC5D,MAAM,EAAE,KAAK,EAAE,CAAC,EAAE,GAAG,IAAI,EAAE,GAAG,KAAK,CAAA;IACnC,OAAO;QACL,GAAG,IAAI;QACP,GAAG,CAAC,OAAO,IAAI,EAAE,IAAI,KAAK,WAAW,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACjE,GAAG,CAAC,OAAO,IAAI,EAAE,EAAE,KAAK,WAAW,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,IAAI,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC3D,GAAG,CAAC,OAAO,IAAI,EAAE,KAAK,KAAK,WAAW,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,kBAAkB,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KACzF,CAAA;AACH,CAAC;AAED,SAAS,kBAAkB,CAAC,KAAc;IACxC,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,IAAI,CAAC;QAAE,OAAO,KAAK,CAAA;IACtE,OAAO,MAAM,CAAC,KAAK,KAAK,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAA;AAC/C,CAAC;AAED,MAAM,UAAU,yBAAyB,CAAC,KAA0C;IAClF,IAAI,CAAC,KAAK;QAAE,MAAM,IAAI,KAAK,CAAC,gDAAgD,CAAC,CAAA;IAC7E,OAAO,gBAAgB,CAAC,OAAO,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,KAAK,EAA0B,CAAE,CAAA;AACtF,CAAC;AAED,MAAM,UAAU,
|
|
1
|
+
{"version":3,"file":"utils.js","sourceRoot":"","sources":["../../../../src/server/internal/handlers/utils.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
package/src/core/zod/rpc.ts
CHANGED
|
@@ -148,7 +148,7 @@ export namespace eth_fillTransaction {
|
|
|
148
148
|
method: z.literal('eth_fillTransaction'),
|
|
149
149
|
params: z.readonly(z.tuple([transactionRequest])),
|
|
150
150
|
returns: z.object({
|
|
151
|
-
|
|
151
|
+
capabilities: z.object({
|
|
152
152
|
balanceDiffs: z.optional(z.record(u.address(), z.readonly(z.array(balanceDiff)))),
|
|
153
153
|
fee: z.nullable(
|
|
154
154
|
z.object({
|
|
@@ -73,6 +73,26 @@ describe('behavior: without feePayer', () => {
|
|
|
73
73
|
const chainId = await client.request({ method: 'eth_chainId' })
|
|
74
74
|
expect(Number(chainId)).toMatchInlineSnapshot(`${chain.id}`)
|
|
75
75
|
})
|
|
76
|
+
|
|
77
|
+
test('behavior: handles JSON-RPC batch requests', async () => {
|
|
78
|
+
const response = await fetch(server.url, {
|
|
79
|
+
method: 'POST',
|
|
80
|
+
headers: { 'Content-Type': 'application/json' },
|
|
81
|
+
body: JSON.stringify([
|
|
82
|
+
{ jsonrpc: '2.0', id: 1, method: 'eth_chainId', params: [] },
|
|
83
|
+
{ jsonrpc: '2.0', id: 2, method: 'eth_chainId', params: [] },
|
|
84
|
+
]),
|
|
85
|
+
})
|
|
86
|
+
|
|
87
|
+
expect(response.status).toBe(200)
|
|
88
|
+
const body = (await response.json()) as { id: number; result: string }[]
|
|
89
|
+
expect(Array.isArray(body)).toBe(true)
|
|
90
|
+
expect(body).toHaveLength(2)
|
|
91
|
+
expect(body[0]!.id).toBe(1)
|
|
92
|
+
expect(body[1]!.id).toBe(2)
|
|
93
|
+
expect(Number(body[0]!.result)).toBe(chain.id)
|
|
94
|
+
expect(Number(body[1]!.result)).toBe(chain.id)
|
|
95
|
+
})
|
|
76
96
|
})
|
|
77
97
|
|
|
78
98
|
describe('behavior: capabilities', () => {
|
|
@@ -831,6 +851,6 @@ describe('behavior: fee token resolution', () => {
|
|
|
831
851
|
],
|
|
832
852
|
})
|
|
833
853
|
|
|
834
|
-
expect(transaction.feeToken).
|
|
854
|
+
expect(transaction.feeToken).toBeUndefined()
|
|
835
855
|
})
|
|
836
856
|
})
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { AbiEvent, Hex, RpcRequest } from 'ox'
|
|
1
|
+
import { AbiEvent, Hex, RpcRequest, RpcResponse } from 'ox'
|
|
2
2
|
import { Transaction as core_Transaction } from 'ox/tempo'
|
|
3
3
|
import {
|
|
4
4
|
type Address,
|
|
@@ -18,6 +18,7 @@ import { simulateCalls } from 'viem/actions'
|
|
|
18
18
|
import { tempo, tempoLocalnet, tempoMainnet, tempoModerato } from 'viem/chains'
|
|
19
19
|
import { Abis, Actions, Addresses, Capabilities, Transaction } from 'viem/tempo'
|
|
20
20
|
|
|
21
|
+
import * as Schema from '../../../core/Schema.js'
|
|
21
22
|
import { type Handler, from } from '../../Handler.js'
|
|
22
23
|
import * as FeePayer from './feePayer.js'
|
|
23
24
|
import * as Utils from './utils.js'
|
|
@@ -98,213 +99,166 @@ export function relay(options: relay.Options = {}): Handler {
|
|
|
98
99
|
return clients.get(chains[0]!.id)!
|
|
99
100
|
}
|
|
100
101
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
router.post(path, async (c) => {
|
|
104
|
-
const request = RpcRequest.from((await c.req.raw.json()) as never)
|
|
105
|
-
|
|
106
|
-
try {
|
|
107
|
-
await onRequest?.(request)
|
|
108
|
-
|
|
109
|
-
const method = request.method as string
|
|
110
|
-
|
|
111
|
-
// Resolve chainId + client from the first param object (if present).
|
|
112
|
-
const params = Array.isArray(request.params) ? request.params : []
|
|
113
|
-
const firstParam =
|
|
114
|
-
typeof params[0] === 'object' && params[0]
|
|
115
|
-
? (params[0] as Record<string, unknown>)
|
|
116
|
-
: undefined
|
|
117
|
-
const chainId = Utils.resolveChainId(firstParam?.chainId) ?? chains[0]!.id
|
|
118
|
-
const client = getClient(chainId)
|
|
119
|
-
|
|
120
|
-
// Proxy non-fill methods directly to the RPC node.
|
|
121
|
-
if (method !== 'eth_fillTransaction') {
|
|
122
|
-
const result = await client.request({
|
|
123
|
-
method: method as never,
|
|
124
|
-
params: params as never,
|
|
125
|
-
})
|
|
126
|
-
return Utils.rpcResult(request, result)
|
|
127
|
-
}
|
|
102
|
+
async function handleRequest(request: RpcRequest.RpcRequest<Schema.Ox>) {
|
|
103
|
+
await onRequest?.(request)
|
|
128
104
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
// 3. Check if the fee payer approves this transaction.
|
|
150
|
-
const sponsored =
|
|
151
|
-
feePayerOptions &&
|
|
152
|
-
(!feePayerOptions.validate ||
|
|
153
|
-
// @ts-expect-error - TODO: Convert to `TransactionRequest` properly.
|
|
154
|
-
(await feePayerOptions.validate({
|
|
155
|
-
...filled.transaction,
|
|
156
|
-
from: sender,
|
|
157
|
-
} as Transaction.TransactionRequest)))
|
|
158
|
-
|
|
159
|
-
// Re-fill without feePayer when sponsorship is rejected so the
|
|
160
|
-
// gas estimate and nonce are correct for a self-paid transaction.
|
|
161
|
-
if (feePayerOptions && !sponsored) {
|
|
162
|
-
const { feePayer: _, ...withoutFeePayer } = withOverrides
|
|
163
|
-
filled = await fill(client, { autoSwap, feeToken, transaction: withoutFeePayer })
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
const { transaction, swap } = filled
|
|
167
|
-
|
|
168
|
-
// 4. Simulate and compute balance diffs + fee.
|
|
169
|
-
const calls = extractCalls(transaction)
|
|
170
|
-
const { balanceDiffs, fee } = await simulateAndParseDiffs(client, {
|
|
171
|
-
account: sender,
|
|
172
|
-
calls,
|
|
173
|
-
swap,
|
|
174
|
-
feeToken: (transaction as { feeToken?: Address | undefined }).feeToken,
|
|
175
|
-
gas: (transaction as { gas?: bigint | undefined }).gas,
|
|
176
|
-
maxFeePerGas: (transaction as { maxFeePerGas?: bigint | undefined }).maxFeePerGas,
|
|
177
|
-
})
|
|
178
|
-
|
|
179
|
-
// 5. Sign as fee payer (when approved).
|
|
180
|
-
const tx = sponsored
|
|
181
|
-
? await FeePayer.sign({
|
|
182
|
-
account: feePayerOptions.account,
|
|
183
|
-
transaction,
|
|
184
|
-
sender,
|
|
105
|
+
// Resolve chainId + client from the first param object (if present).
|
|
106
|
+
const params = 'params' in request && Array.isArray(request.params) ? request.params : []
|
|
107
|
+
const first =
|
|
108
|
+
typeof params[0] === 'object' && params[0]
|
|
109
|
+
? (params[0] as Record<string, unknown>)
|
|
110
|
+
: undefined
|
|
111
|
+
const chainId = Utils.resolveChainId(first?.chainId) ?? chains[0]!.id
|
|
112
|
+
const client = getClient(chainId)
|
|
113
|
+
|
|
114
|
+
switch (request.method) {
|
|
115
|
+
case 'eth_fillTransaction': {
|
|
116
|
+
try {
|
|
117
|
+
const parameters = request.params[0]
|
|
118
|
+
|
|
119
|
+
// 1. Resolve fee token.
|
|
120
|
+
const feeToken = await resolveFeeToken(client, {
|
|
121
|
+
account: parameters.from,
|
|
122
|
+
feeToken: parameters.feeToken,
|
|
123
|
+
tokens: resolveTokens(chainId),
|
|
185
124
|
})
|
|
186
|
-
: transaction
|
|
187
125
|
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
...
|
|
192
|
-
...(
|
|
126
|
+
// 2. Fill transaction via RPC node (with AMM resolution on InsufficientBalance).
|
|
127
|
+
const normalized = Utils.normalizeFillTransactionRequest(parameters)
|
|
128
|
+
const transaction = {
|
|
129
|
+
...normalized,
|
|
130
|
+
...(typeof chainId !== 'undefined' ? { chainId } : {}),
|
|
131
|
+
...(feePayerOptions ? { feePayer: true } : {}),
|
|
132
|
+
...(feeToken ? { feeToken } : {}),
|
|
193
133
|
}
|
|
194
|
-
|
|
134
|
+
let filled = await fill(client, { autoSwap, feeToken, transaction })
|
|
135
|
+
|
|
136
|
+
// 3. Check if the fee payer approves this transaction.
|
|
137
|
+
const sponsored =
|
|
138
|
+
feePayerOptions &&
|
|
139
|
+
(!feePayerOptions.validate ||
|
|
140
|
+
// @ts-expect-error - TODO: Convert to `TransactionRequest` properly.
|
|
141
|
+
(await feePayerOptions.validate({
|
|
142
|
+
...filled.transaction,
|
|
143
|
+
from: parameters.from,
|
|
144
|
+
} as Transaction.TransactionRequest)))
|
|
145
|
+
|
|
146
|
+
// Re-fill without feePayer when sponsorship is rejected so the
|
|
147
|
+
// gas estimate and nonce are correct for a self-paid transaction.
|
|
148
|
+
if (feePayerOptions && !sponsored) {
|
|
149
|
+
const { feePayer: _, ...tx } = transaction
|
|
150
|
+
filled = await fill(client, { autoSwap, feeToken, transaction: tx })
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
const { transaction: transaction_filled, swap } = filled
|
|
154
|
+
|
|
155
|
+
// 4. Simulate and compute balance diffs + fee.
|
|
156
|
+
const calls = extractCalls(transaction_filled)
|
|
157
|
+
const { balanceDiffs, fee } = await simulateAndParseDiffs(client, {
|
|
158
|
+
account: parameters.from,
|
|
159
|
+
calls,
|
|
160
|
+
swap,
|
|
161
|
+
feeToken: transaction_filled.feeToken,
|
|
162
|
+
gas: transaction_filled.gas,
|
|
163
|
+
maxFeePerGas: transaction_filled.maxFeePerGas,
|
|
164
|
+
})
|
|
195
165
|
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
166
|
+
// 5. Sign as fee payer (if sponsored).
|
|
167
|
+
const transaction_final = await (async () => {
|
|
168
|
+
if (!sponsored) return transaction_filled
|
|
169
|
+
return await FeePayer.sign({
|
|
170
|
+
account: feePayerOptions.account,
|
|
171
|
+
sender: parameters.from,
|
|
172
|
+
transaction: transaction_filled,
|
|
173
|
+
})
|
|
174
|
+
})()
|
|
175
|
+
|
|
176
|
+
const sponsor = (() => {
|
|
177
|
+
if (!sponsored) return undefined
|
|
178
|
+
return {
|
|
179
|
+
address: feePayerOptions.account.address,
|
|
180
|
+
...(feePayerOptions.name ? { name: feePayerOptions.name } : {}),
|
|
181
|
+
...(feePayerOptions.url ? { url: feePayerOptions.url } : {}),
|
|
182
|
+
}
|
|
183
|
+
})()
|
|
184
|
+
|
|
185
|
+
// 6. Resolve autoSwap metadata (when AMM path was taken).
|
|
186
|
+
const autoSwap_ = await (async () => {
|
|
187
|
+
if (!swap) return undefined
|
|
188
|
+
const [inMeta, outMeta] = await Promise.all([
|
|
189
|
+
resolveTokenMetadata(client, swap.tokenIn).catch(() => undefined),
|
|
190
|
+
resolveTokenMetadata(client, swap.tokenOut).catch(() => undefined),
|
|
191
|
+
])
|
|
192
|
+
if (!inMeta || !outMeta) return undefined
|
|
193
|
+
return {
|
|
194
|
+
calls: swap.calls.map((c) => ({ to: c.to, data: c.data })),
|
|
195
|
+
slippage: autoSwap!.slippage,
|
|
196
|
+
maxIn: {
|
|
197
|
+
token: swap.tokenIn,
|
|
198
|
+
value: Hex.fromNumber(swap.maxAmountIn) as `0x${string}`,
|
|
199
|
+
formatted: formatUnits(swap.maxAmountIn, inMeta.decimals),
|
|
200
|
+
decimals: inMeta.decimals,
|
|
201
|
+
symbol: inMeta.symbol,
|
|
202
|
+
name: inMeta.name,
|
|
203
|
+
},
|
|
204
|
+
minOut: {
|
|
205
|
+
token: swap.tokenOut,
|
|
206
|
+
value: Hex.fromNumber(swap.amountOut) as `0x${string}`,
|
|
207
|
+
formatted: formatUnits(swap.amountOut, outMeta.decimals),
|
|
208
|
+
decimals: outMeta.decimals,
|
|
209
|
+
symbol: outMeta.symbol,
|
|
210
|
+
name: outMeta.name,
|
|
211
|
+
},
|
|
212
|
+
}
|
|
213
|
+
})()
|
|
214
|
+
|
|
215
|
+
return RpcResponse.from(
|
|
216
|
+
{
|
|
217
|
+
result: {
|
|
218
|
+
tx: core_Transaction.toRpc(transaction_final as core_Transaction.Transaction),
|
|
219
|
+
capabilities: {
|
|
220
|
+
balanceDiffs,
|
|
221
|
+
fee,
|
|
222
|
+
sponsored: !!sponsor,
|
|
223
|
+
...(sponsor ? { sponsor } : {}),
|
|
224
|
+
...(autoSwap_ ? { autoSwap: autoSwap_ } : {}),
|
|
225
|
+
},
|
|
226
|
+
},
|
|
227
|
+
},
|
|
228
|
+
{ request },
|
|
229
|
+
)
|
|
230
|
+
} catch (error) {
|
|
231
|
+
return Utils.rpcErrorJson(request, error)
|
|
223
232
|
}
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
fee,
|
|
231
|
-
sponsored: !!sponsor,
|
|
232
|
-
...(sponsor ? { sponsor } : {}),
|
|
233
|
-
...(autoSwap_ ? { autoSwap: autoSwap_ } : {}),
|
|
234
|
-
},
|
|
235
|
-
})
|
|
236
|
-
} catch (error) {
|
|
237
|
-
return Utils.rpcError(request, error)
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
default: {
|
|
236
|
+
const result = await client.request(request as never)
|
|
237
|
+
return RpcResponse.from({ result }, { request })
|
|
238
|
+
}
|
|
238
239
|
}
|
|
239
|
-
}
|
|
240
|
+
}
|
|
240
241
|
|
|
241
|
-
|
|
242
|
-
}
|
|
242
|
+
const router = from(rest)
|
|
243
243
|
|
|
244
|
-
async
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
autoSwap?: { slippage: number } | undefined
|
|
248
|
-
feeToken?: Address | undefined
|
|
249
|
-
transaction: Record<string, unknown>
|
|
250
|
-
},
|
|
251
|
-
) {
|
|
252
|
-
const { autoSwap, feeToken, transaction: request } = options
|
|
244
|
+
router.post(path, async (c) => {
|
|
245
|
+
const body = await c.req.raw.json()
|
|
246
|
+
const isBatch = Array.isArray(body)
|
|
253
247
|
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
248
|
+
if (!isBatch) {
|
|
249
|
+
const request = RpcRequest.from(body) as RpcRequest.RpcRequest<Schema.Ox>
|
|
250
|
+
return Response.json(await handleRequest(request))
|
|
251
|
+
}
|
|
257
252
|
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
return
|
|
264
|
-
}
|
|
265
|
-
if (!(error instanceof Error)) throw error
|
|
266
|
-
const parsed = parseInsufficientBalance(error)
|
|
267
|
-
if (!parsed || !feeToken || !autoSwap) throw error
|
|
268
|
-
if (parsed.token.toLowerCase() === feeToken.toLowerCase()) throw error
|
|
253
|
+
const responses = await Promise.all(
|
|
254
|
+
(body as unknown[]).map((item) =>
|
|
255
|
+
handleRequest(RpcRequest.from(item as never) as RpcRequest.RpcRequest<Schema.Ox>),
|
|
256
|
+
),
|
|
257
|
+
)
|
|
258
|
+
return Response.json(responses)
|
|
259
|
+
})
|
|
269
260
|
|
|
270
|
-
|
|
271
|
-
const maxAmountIn = deficit + (deficit * BigInt(Math.round(autoSwap.slippage * 1000))) / 1000n
|
|
272
|
-
const swapCalls = buildSwapCalls(feeToken, parsed.token, deficit, maxAmountIn)
|
|
273
|
-
const existingCalls = request.calls as Call[] | undefined
|
|
274
|
-
// If the request was normalized to top-level to/data/value (single call),
|
|
275
|
-
// convert back to a calls array so we can prepend swap calls.
|
|
276
|
-
const originalCalls: Call[] = existingCalls
|
|
277
|
-
? [...existingCalls]
|
|
278
|
-
: request.to
|
|
279
|
-
? [
|
|
280
|
-
{
|
|
281
|
-
to: request.to as Address,
|
|
282
|
-
data: request.data as `0x${string}`,
|
|
283
|
-
value: (request.value as bigint) ?? 0n,
|
|
284
|
-
},
|
|
285
|
-
]
|
|
286
|
-
: []
|
|
287
|
-
const { to: _, data: __, value: ___, calls: ____, ...rest } = request
|
|
288
|
-
const result = await client.request({
|
|
289
|
-
method: 'eth_fillTransaction',
|
|
290
|
-
params: [
|
|
291
|
-
format({
|
|
292
|
-
...rest,
|
|
293
|
-
calls: [...swapCalls, ...originalCalls],
|
|
294
|
-
}) as never,
|
|
295
|
-
],
|
|
296
|
-
})
|
|
297
|
-
return {
|
|
298
|
-
transaction: Utils.normalizeTempoTransaction(result.tx),
|
|
299
|
-
swap: {
|
|
300
|
-
calls: swapCalls,
|
|
301
|
-
tokenIn: feeToken,
|
|
302
|
-
tokenOut: parsed.token,
|
|
303
|
-
amountOut: deficit,
|
|
304
|
-
maxAmountIn,
|
|
305
|
-
},
|
|
306
|
-
}
|
|
307
|
-
}
|
|
261
|
+
return router
|
|
308
262
|
}
|
|
309
263
|
|
|
310
264
|
export namespace relay {
|
|
@@ -381,6 +335,73 @@ export namespace relay {
|
|
|
381
335
|
}
|
|
382
336
|
}
|
|
383
337
|
|
|
338
|
+
// TODO: cleanup
|
|
339
|
+
async function fill(
|
|
340
|
+
client: Client,
|
|
341
|
+
options: {
|
|
342
|
+
autoSwap?: { slippage: number } | undefined
|
|
343
|
+
feeToken?: Address | undefined
|
|
344
|
+
transaction: Record<string, unknown>
|
|
345
|
+
},
|
|
346
|
+
) {
|
|
347
|
+
const { autoSwap, feeToken, transaction: request } = options
|
|
348
|
+
|
|
349
|
+
// Skip re-formatting if already in RPC format (e.g. from viem's fillTransaction).
|
|
350
|
+
const format = (value: Record<string, unknown>) =>
|
|
351
|
+
value.type === '0x76' ? value : Utils.formatFillTransactionRequest(client, value)
|
|
352
|
+
|
|
353
|
+
try {
|
|
354
|
+
const result = await client.request({
|
|
355
|
+
method: 'eth_fillTransaction',
|
|
356
|
+
params: [format(request) as never],
|
|
357
|
+
})
|
|
358
|
+
return { transaction: Utils.normalizeTempoTransaction(result.tx) }
|
|
359
|
+
} catch (error) {
|
|
360
|
+
if (!(error instanceof Error)) throw error
|
|
361
|
+
const parsed = parseInsufficientBalance(error)
|
|
362
|
+
if (!parsed || !feeToken || !autoSwap) throw error
|
|
363
|
+
if (parsed.token.toLowerCase() === feeToken.toLowerCase()) throw error
|
|
364
|
+
|
|
365
|
+
const deficit = parsed.required - parsed.available
|
|
366
|
+
const maxAmountIn = deficit + (deficit * BigInt(Math.round(autoSwap.slippage * 1000))) / 1000n
|
|
367
|
+
const swapCalls = buildSwapCalls(feeToken, parsed.token, deficit, maxAmountIn)
|
|
368
|
+
const existingCalls = request.calls as Call[] | undefined
|
|
369
|
+
// If the request was normalized to top-level to/data/value (single call),
|
|
370
|
+
// convert back to a calls array so we can prepend swap calls.
|
|
371
|
+
const originalCalls: Call[] = existingCalls
|
|
372
|
+
? [...existingCalls]
|
|
373
|
+
: request.to
|
|
374
|
+
? [
|
|
375
|
+
{
|
|
376
|
+
to: request.to as Address,
|
|
377
|
+
data: request.data as `0x${string}`,
|
|
378
|
+
value: (request.value as bigint) ?? 0n,
|
|
379
|
+
},
|
|
380
|
+
]
|
|
381
|
+
: []
|
|
382
|
+
const { to: _, data: __, value: ___, calls: ____, ...rest } = request
|
|
383
|
+
const result = await client.request({
|
|
384
|
+
method: 'eth_fillTransaction',
|
|
385
|
+
params: [
|
|
386
|
+
format({
|
|
387
|
+
...rest,
|
|
388
|
+
calls: [...swapCalls, ...originalCalls],
|
|
389
|
+
}) as never,
|
|
390
|
+
],
|
|
391
|
+
})
|
|
392
|
+
return {
|
|
393
|
+
transaction: Utils.normalizeTempoTransaction(result.tx),
|
|
394
|
+
swap: {
|
|
395
|
+
calls: swapCalls,
|
|
396
|
+
tokenIn: feeToken,
|
|
397
|
+
tokenOut: parsed.token,
|
|
398
|
+
amountOut: deficit,
|
|
399
|
+
maxAmountIn,
|
|
400
|
+
},
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
|
|
384
405
|
async function resolveFeeToken(
|
|
385
406
|
client: Client,
|
|
386
407
|
options: {
|
|
@@ -431,10 +452,9 @@ async function resolveFeeToken(
|
|
|
431
452
|
if (!best || asset.balance > best.balance) best = asset
|
|
432
453
|
}
|
|
433
454
|
if (best) return best.address
|
|
434
|
-
|
|
435
|
-
return Addresses.pathUsd as Address
|
|
436
455
|
}
|
|
437
456
|
|
|
457
|
+
// TODO: cleanup/remove
|
|
438
458
|
function extractCalls(transaction: Record<string, unknown>): readonly Call[] {
|
|
439
459
|
const calls = transaction.calls as readonly Call[] | undefined
|
|
440
460
|
if (calls && calls.length > 0)
|
|
@@ -452,6 +472,7 @@ function extractCalls(transaction: Record<string, unknown>): readonly Call[] {
|
|
|
452
472
|
] as readonly Call[]
|
|
453
473
|
}
|
|
454
474
|
|
|
475
|
+
// TODO: cleanup/remove
|
|
455
476
|
async function simulate(
|
|
456
477
|
client: Client,
|
|
457
478
|
options: {
|