@agentlayer.tech/wallet 0.1.103 → 0.1.106
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/.openclaw/extensions/agent-wallet/openclaw.plugin.json +1 -1
- package/.openclaw/extensions/agent-wallet/package.json +1 -1
- package/VERSION +1 -1
- package/agent-wallet/README.md +7 -2
- package/agent-wallet/agent_wallet/__init__.py +1 -1
- package/agent-wallet/agent_wallet/config.py +15 -1
- package/agent-wallet/agent_wallet/evm_user_wallets.py +101 -244
- package/agent-wallet/agent_wallet/openclaw_runtime.py +7 -2
- package/agent-wallet/agent_wallet/providers/wdk_evm_local.py +40 -9
- package/agent-wallet/agent_wallet/wallet_layer/factory.py +25 -2
- package/agent-wallet/openclaw.plugin.json +1 -1
- package/agent-wallet/pyproject.toml +1 -1
- package/agent-wallet/scripts/bootstrap_openclaw_evm.py +24 -14
- package/agent-wallet/scripts/install_agent_wallet.py +18 -6
- package/agent-wallet/scripts/manage_openclaw_evm_wallet.py +18 -15
- package/agent-wallet/scripts/setup_evm_wallet.sh +2 -3
- package/claude-code/plugins/agent-wallet/.claude-plugin/plugin.json +1 -1
- package/codex/plugins/agent-wallet/.codex-plugin/plugin.json +1 -1
- package/codex/plugins/agent-wallet/server.py +74 -4
- package/hermes/plugins/agent_wallet/plugin.yaml +1 -1
- package/package.json +1 -1
- package/wdk-btc-wallet/package.json +1 -1
- package/wdk-evm-wallet/README.md +6 -2
- package/wdk-evm-wallet/package.json +3 -2
- package/wdk-evm-wallet/src/config.js +9 -0
- package/wdk-evm-wallet/src/server.js +768 -692
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import "dotenv/config";
|
|
2
2
|
|
|
3
3
|
import crypto from "node:crypto";
|
|
4
|
+
import fs from "node:fs";
|
|
4
5
|
import { createServer } from "node:http";
|
|
6
|
+
import net from "node:net";
|
|
5
7
|
|
|
6
8
|
import { loadConfig } from "./config.js";
|
|
7
9
|
import { readJsonBody, sendJson } from "./json.js";
|
|
@@ -10,732 +12,806 @@ import { EvmNetworkState } from "./network_state.js";
|
|
|
10
12
|
import { createShutdownCoordinator, withTrackedRequest } from "./shutdown.js";
|
|
11
13
|
import { WdkEvmWalletService } from "./wdk_evm_wallet.js";
|
|
12
14
|
|
|
13
|
-
|
|
14
|
-
const service = new WdkEvmWalletService(config);
|
|
15
|
-
const vault = new LocalEvmVault(config);
|
|
16
|
-
const networkState = new EvmNetworkState(config);
|
|
17
|
-
|
|
18
|
-
function normalizeErrorCode(errorCode, pathname, message) {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
15
|
+
export async function startServer(config = loadConfig()) {
|
|
16
|
+
const service = new WdkEvmWalletService(config);
|
|
17
|
+
const vault = new LocalEvmVault(config);
|
|
18
|
+
const networkState = new EvmNetworkState(config);
|
|
19
|
+
|
|
20
|
+
function normalizeErrorCode(errorCode, pathname, message) {
|
|
21
|
+
const code = String(errorCode || "").trim().toLowerCase();
|
|
22
|
+
const lower = String(message || "").toLowerCase();
|
|
23
|
+
const isTokenPath = pathname.includes("/token");
|
|
24
|
+
const isTokenReadPath =
|
|
25
|
+
pathname.includes("/token-balance/") || pathname.includes("/token-metadata/");
|
|
26
|
+
|
|
27
|
+
if (code === "insufficient_funds") {
|
|
28
|
+
return "insufficient_funds";
|
|
29
|
+
}
|
|
30
|
+
if (code === "network_unavailable") {
|
|
31
|
+
return "network_unavailable";
|
|
32
|
+
}
|
|
33
|
+
if (
|
|
34
|
+
code === "swap_quote_changed" ||
|
|
35
|
+
code === "swap_simulation_failed" ||
|
|
36
|
+
code === "swap_approval_required" ||
|
|
37
|
+
code === "swap_approval_failed" ||
|
|
38
|
+
code === "swap_approval_timeout" ||
|
|
39
|
+
code === "swap_cleanup_failed" ||
|
|
40
|
+
code === "aave_quote_changed" ||
|
|
41
|
+
code === "aave_approval_required" ||
|
|
42
|
+
code === "aave_fee_unavailable" ||
|
|
43
|
+
code === "aave_operation_reverted" ||
|
|
44
|
+
code === "aave_operation_confirmation_timeout" ||
|
|
45
|
+
code === "aave_cleanup_failed" ||
|
|
46
|
+
code === "morpho_api_failed" ||
|
|
47
|
+
code === "morpho_quote_changed" ||
|
|
48
|
+
code === "morpho_requirements_unresolved" ||
|
|
49
|
+
code === "morpho_fee_unavailable" ||
|
|
50
|
+
code === "morpho_cleanup_failed" ||
|
|
51
|
+
code === "defi_gas_estimate_unavailable" ||
|
|
52
|
+
code === "lido_operation_reverted" ||
|
|
53
|
+
code === "lido_operation_confirmation_timeout" ||
|
|
54
|
+
code === "lido_withdrawal_reverted" ||
|
|
55
|
+
code === "lido_withdrawal_confirmation_timeout" ||
|
|
56
|
+
code === "swap_reverted" ||
|
|
57
|
+
code === "swap_confirmation_timeout" ||
|
|
58
|
+
code === "token_transfer_failed" ||
|
|
59
|
+
code === "fee_limit_exceeded" ||
|
|
60
|
+
code === "token_read_failed" ||
|
|
61
|
+
code === "uniswap_api_key_missing" ||
|
|
62
|
+
code === "uniswap_unsupported_route" ||
|
|
63
|
+
code === "uniswap_unexpected_router"
|
|
64
|
+
) {
|
|
65
|
+
return code;
|
|
66
|
+
}
|
|
67
|
+
if (
|
|
68
|
+
code === "call_exception" ||
|
|
69
|
+
code === "bad_data" ||
|
|
70
|
+
code === "execution_reverted" ||
|
|
71
|
+
code === "contract_not_found"
|
|
72
|
+
) {
|
|
73
|
+
if (isTokenReadPath) {
|
|
74
|
+
return "token_not_found";
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
if (
|
|
78
|
+
code === "econnrefused" ||
|
|
79
|
+
code === "enotfound" ||
|
|
80
|
+
code === "etimedout" ||
|
|
81
|
+
code === "fetch_failed"
|
|
82
|
+
) {
|
|
83
|
+
return "network_unavailable";
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
if (lower.includes("wallet is locked")) {
|
|
87
|
+
return "wallet_locked";
|
|
88
|
+
}
|
|
89
|
+
if (lower.includes("insufficient funds")) {
|
|
90
|
+
return "insufficient_funds";
|
|
91
|
+
}
|
|
92
|
+
if (
|
|
93
|
+
lower.includes("recipient must be a valid") ||
|
|
94
|
+
lower.includes("recipient must not be the zero address") ||
|
|
95
|
+
lower.includes("to must be a valid") ||
|
|
96
|
+
lower.includes("to must not be the zero address") ||
|
|
97
|
+
lower.includes("invalid address") ||
|
|
98
|
+
lower.includes("bad address checksum")
|
|
99
|
+
) {
|
|
100
|
+
return "recipient_invalid";
|
|
101
|
+
}
|
|
102
|
+
if (
|
|
103
|
+
isTokenReadPath &&
|
|
104
|
+
(lower.includes("missing revert data") ||
|
|
105
|
+
lower.includes("call exception") ||
|
|
106
|
+
lower.includes("could not decode result data") ||
|
|
107
|
+
lower.includes("no contract code") ||
|
|
108
|
+
lower.includes("execution reverted"))
|
|
109
|
+
) {
|
|
72
110
|
return "token_not_found";
|
|
73
111
|
}
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
112
|
+
if (
|
|
113
|
+
lower.includes("rpc network unavailable") ||
|
|
114
|
+
lower.includes("rpc request failed") ||
|
|
115
|
+
lower.includes("rpc returned invalid json") ||
|
|
116
|
+
lower.includes("fetch failed") ||
|
|
117
|
+
lower.includes("network unavailable") ||
|
|
118
|
+
lower.includes("timeout")
|
|
119
|
+
) {
|
|
120
|
+
return "network_unavailable";
|
|
121
|
+
}
|
|
122
|
+
if (lower.includes("unknown walletid")) {
|
|
123
|
+
return "wallet_not_found";
|
|
124
|
+
}
|
|
125
|
+
if (lower.includes("invalid password")) {
|
|
126
|
+
return "invalid_password";
|
|
127
|
+
}
|
|
83
128
|
|
|
84
|
-
|
|
85
|
-
return "wallet_locked";
|
|
86
|
-
}
|
|
87
|
-
if (lower.includes("insufficient funds")) {
|
|
88
|
-
return "insufficient_funds";
|
|
89
|
-
}
|
|
90
|
-
if (
|
|
91
|
-
lower.includes("recipient must be a valid") ||
|
|
92
|
-
lower.includes("recipient must not be the zero address") ||
|
|
93
|
-
lower.includes("to must be a valid") ||
|
|
94
|
-
lower.includes("to must not be the zero address") ||
|
|
95
|
-
lower.includes("invalid address") ||
|
|
96
|
-
lower.includes("bad address checksum")
|
|
97
|
-
) {
|
|
98
|
-
return "recipient_invalid";
|
|
129
|
+
return null;
|
|
99
130
|
}
|
|
100
|
-
if (
|
|
101
|
-
isTokenReadPath &&
|
|
102
|
-
(lower.includes("missing revert data") ||
|
|
103
|
-
lower.includes("call exception") ||
|
|
104
|
-
lower.includes("could not decode result data") ||
|
|
105
|
-
lower.includes("no contract code") ||
|
|
106
|
-
lower.includes("execution reverted"))
|
|
107
|
-
) {
|
|
108
|
-
return "token_not_found";
|
|
109
|
-
}
|
|
110
|
-
if (
|
|
111
|
-
lower.includes("rpc network unavailable") ||
|
|
112
|
-
lower.includes("rpc request failed") ||
|
|
113
|
-
lower.includes("rpc returned invalid json") ||
|
|
114
|
-
lower.includes("fetch failed") ||
|
|
115
|
-
lower.includes("network unavailable") ||
|
|
116
|
-
lower.includes("timeout")
|
|
117
|
-
) {
|
|
118
|
-
return "network_unavailable";
|
|
119
|
-
}
|
|
120
|
-
if (lower.includes("unknown walletid")) {
|
|
121
|
-
return "wallet_not_found";
|
|
122
|
-
}
|
|
123
|
-
if (lower.includes("invalid password")) {
|
|
124
|
-
return "invalid_password";
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
return null;
|
|
128
|
-
}
|
|
129
131
|
|
|
130
|
-
function errorStatusCode(errorCode, fallback = 400) {
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
132
|
+
function errorStatusCode(errorCode, fallback = 400) {
|
|
133
|
+
if (errorCode === "wallet_locked" || errorCode === "insufficient_funds") {
|
|
134
|
+
return 409;
|
|
135
|
+
}
|
|
136
|
+
if (errorCode === "swap_quote_changed") {
|
|
137
|
+
return 409;
|
|
138
|
+
}
|
|
139
|
+
if (errorCode === "aave_quote_changed") {
|
|
140
|
+
return 409;
|
|
141
|
+
}
|
|
142
|
+
if (errorCode === "morpho_quote_changed") {
|
|
143
|
+
return 409;
|
|
144
|
+
}
|
|
145
|
+
if (
|
|
146
|
+
errorCode === "swap_simulation_failed" ||
|
|
147
|
+
errorCode === "swap_approval_required" ||
|
|
148
|
+
errorCode === "swap_approval_failed" ||
|
|
149
|
+
errorCode === "swap_approval_timeout" ||
|
|
150
|
+
errorCode === "swap_cleanup_failed" ||
|
|
151
|
+
errorCode === "aave_approval_required" ||
|
|
152
|
+
errorCode === "aave_fee_unavailable" ||
|
|
153
|
+
errorCode === "aave_operation_reverted" ||
|
|
154
|
+
errorCode === "aave_operation_confirmation_timeout" ||
|
|
155
|
+
errorCode === "aave_cleanup_failed" ||
|
|
156
|
+
errorCode === "morpho_api_failed" ||
|
|
157
|
+
errorCode === "morpho_requirements_unresolved" ||
|
|
158
|
+
errorCode === "morpho_fee_unavailable" ||
|
|
159
|
+
errorCode === "morpho_cleanup_failed" ||
|
|
160
|
+
errorCode === "defi_gas_estimate_unavailable" ||
|
|
161
|
+
errorCode === "lido_operation_reverted" ||
|
|
162
|
+
errorCode === "lido_operation_confirmation_timeout" ||
|
|
163
|
+
errorCode === "lido_withdrawal_reverted" ||
|
|
164
|
+
errorCode === "lido_withdrawal_confirmation_timeout" ||
|
|
165
|
+
errorCode === "swap_reverted" ||
|
|
166
|
+
errorCode === "swap_confirmation_timeout" ||
|
|
167
|
+
errorCode === "token_transfer_failed" ||
|
|
168
|
+
errorCode === "fee_limit_exceeded" ||
|
|
169
|
+
errorCode === "uniswap_api_key_missing"
|
|
170
|
+
) {
|
|
171
|
+
return 400;
|
|
172
|
+
}
|
|
173
|
+
if (errorCode === "uniswap_unsupported_route") {
|
|
174
|
+
return 422;
|
|
175
|
+
}
|
|
176
|
+
if (errorCode === "uniswap_unexpected_router") {
|
|
177
|
+
return 502;
|
|
178
|
+
}
|
|
179
|
+
if (errorCode === "token_read_failed") {
|
|
180
|
+
return 502;
|
|
181
|
+
}
|
|
182
|
+
if (errorCode === "network_unavailable") {
|
|
183
|
+
return 503;
|
|
184
|
+
}
|
|
185
|
+
if (errorCode === "wallet_not_found" || errorCode === "token_not_found") {
|
|
186
|
+
return 404;
|
|
187
|
+
}
|
|
188
|
+
return fallback;
|
|
176
189
|
}
|
|
177
|
-
if (errorCode === "token_read_failed") {
|
|
178
|
-
return 502;
|
|
179
|
-
}
|
|
180
|
-
if (errorCode === "network_unavailable") {
|
|
181
|
-
return 503;
|
|
182
|
-
}
|
|
183
|
-
if (errorCode === "wallet_not_found" || errorCode === "token_not_found") {
|
|
184
|
-
return 404;
|
|
185
|
-
}
|
|
186
|
-
return fallback;
|
|
187
|
-
}
|
|
188
190
|
|
|
189
|
-
function sanitizeProviderUrl(value) {
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
191
|
+
function sanitizeProviderUrl(value) {
|
|
192
|
+
const raw = String(value || "").trim();
|
|
193
|
+
if (!raw) {
|
|
194
|
+
return raw;
|
|
195
|
+
}
|
|
196
|
+
try {
|
|
197
|
+
const url = new URL(raw);
|
|
198
|
+
if (url.searchParams.has("token")) {
|
|
199
|
+
url.searchParams.set("token", "***");
|
|
200
|
+
}
|
|
201
|
+
return url.toString();
|
|
202
|
+
} catch {
|
|
203
|
+
return raw;
|
|
198
204
|
}
|
|
199
|
-
return url.toString();
|
|
200
|
-
} catch {
|
|
201
|
-
return raw;
|
|
202
205
|
}
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
function sanitizeProfiles(profiles = {}) {
|
|
206
|
-
return Object.fromEntries(
|
|
207
|
-
Object.entries(profiles).map(([network, profile]) => [
|
|
208
|
-
network,
|
|
209
|
-
{
|
|
210
|
-
...profile,
|
|
211
|
-
providerUrl: sanitizeProviderUrl(profile?.providerUrl),
|
|
212
|
-
},
|
|
213
|
-
])
|
|
214
|
-
);
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
function toErrorResponse(error, pathname, fallbackStatus = 400) {
|
|
218
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
219
|
-
const explicitCode =
|
|
220
|
-
(typeof error?.errorCode === "string" && error.errorCode.trim()) ||
|
|
221
|
-
(typeof error?.code === "string" && error.code.trim()) ||
|
|
222
|
-
"";
|
|
223
|
-
const errorCode = normalizeErrorCode(explicitCode, pathname, message);
|
|
224
|
-
const details =
|
|
225
|
-
error && typeof error === "object" && error.errorDetails && typeof error.errorDetails === "object"
|
|
226
|
-
? { ...error.errorDetails }
|
|
227
|
-
: {};
|
|
228
|
-
details.source = "wdk-evm-wallet";
|
|
229
|
-
details.path = pathname;
|
|
230
|
-
return {
|
|
231
|
-
statusCode: errorStatusCode(errorCode, fallbackStatus),
|
|
232
|
-
payload: {
|
|
233
|
-
ok: false,
|
|
234
|
-
error: message,
|
|
235
|
-
...(errorCode ? { error_code: errorCode } : {}),
|
|
236
|
-
error_details: details,
|
|
237
|
-
},
|
|
238
|
-
};
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
function notFound(response) {
|
|
242
|
-
sendJson(response, 404, { ok: false, error: "Not Found" });
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
function unauthorized(response) {
|
|
246
|
-
response.setHeader("WWW-Authenticate", 'Bearer realm="wdk-evm-wallet"');
|
|
247
|
-
sendJson(response, 401, { ok: false, error: "Unauthorized." });
|
|
248
|
-
}
|
|
249
206
|
|
|
250
|
-
function
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
const expected = Buffer.from(String(config.authToken || ""), "utf8");
|
|
261
|
-
if (provided.length === 0 || provided.length !== expected.length) {
|
|
262
|
-
return false;
|
|
207
|
+
function sanitizeProfiles(profiles = {}) {
|
|
208
|
+
return Object.fromEntries(
|
|
209
|
+
Object.entries(profiles).map(([network, profile]) => [
|
|
210
|
+
network,
|
|
211
|
+
{
|
|
212
|
+
...profile,
|
|
213
|
+
providerUrl: sanitizeProviderUrl(profile?.providerUrl),
|
|
214
|
+
},
|
|
215
|
+
])
|
|
216
|
+
);
|
|
263
217
|
}
|
|
264
|
-
return crypto.timingSafeEqual(provided, expected);
|
|
265
|
-
}
|
|
266
218
|
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
async function withResolvedSeedOrAddress(body = {}) {
|
|
283
|
-
const address = typeof body.address === "string" ? body.address.trim() : "";
|
|
284
|
-
if (address) {
|
|
219
|
+
function toErrorResponse(error, pathname, fallbackStatus = 400) {
|
|
220
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
221
|
+
const explicitCode =
|
|
222
|
+
(typeof error?.errorCode === "string" && error.errorCode.trim()) ||
|
|
223
|
+
(typeof error?.code === "string" && error.code.trim()) ||
|
|
224
|
+
"";
|
|
225
|
+
const errorCode = normalizeErrorCode(explicitCode, pathname, message);
|
|
226
|
+
const details =
|
|
227
|
+
error && typeof error === "object" && error.errorDetails && typeof error.errorDetails === "object"
|
|
228
|
+
? { ...error.errorDetails }
|
|
229
|
+
: {};
|
|
230
|
+
details.source = "wdk-evm-wallet";
|
|
231
|
+
details.path = pathname;
|
|
285
232
|
return {
|
|
286
|
-
|
|
287
|
-
|
|
233
|
+
statusCode: errorStatusCode(errorCode, fallbackStatus),
|
|
234
|
+
payload: {
|
|
235
|
+
ok: false,
|
|
236
|
+
error: message,
|
|
237
|
+
...(errorCode ? { error_code: errorCode } : {}),
|
|
238
|
+
error_details: details,
|
|
239
|
+
},
|
|
288
240
|
};
|
|
289
241
|
}
|
|
290
|
-
return withResolvedSeed(body);
|
|
291
|
-
}
|
|
292
242
|
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
...body,
|
|
297
|
-
network: runtimeConfig.network,
|
|
298
|
-
};
|
|
299
|
-
}
|
|
300
|
-
|
|
301
|
-
async function handleRequest(request, response) {
|
|
302
|
-
try {
|
|
303
|
-
const url = new URL(request.url || "/", "http://localhost");
|
|
304
|
-
const { method = "GET" } = request;
|
|
305
|
-
|
|
306
|
-
if (pathRequiresAuth(url.pathname) && !isAuthorized(request)) {
|
|
307
|
-
return unauthorized(response);
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
if (method === "GET" && url.pathname === "/health") {
|
|
311
|
-
const runtimeConfig = await networkState.resolveRuntimeConfig();
|
|
312
|
-
const networkInfo = await networkState.getNetworkInfo();
|
|
313
|
-
return sendJson(response, 200, {
|
|
314
|
-
ok: true,
|
|
315
|
-
service: "wdk-evm-wallet",
|
|
316
|
-
version: config.version,
|
|
317
|
-
wallet: "evm",
|
|
318
|
-
network: runtimeConfig.network,
|
|
319
|
-
chainId: runtimeConfig.chainId,
|
|
320
|
-
host: config.host,
|
|
321
|
-
dataDir: config.dataDir,
|
|
322
|
-
instanceId: config.instanceId,
|
|
323
|
-
pid: process.pid,
|
|
324
|
-
authRequired: config.authRequired,
|
|
325
|
-
unlockTimeoutSeconds: config.unlockTimeoutSeconds,
|
|
326
|
-
availableNetworks: Object.keys(config.networkProfiles),
|
|
327
|
-
provider: sanitizeProviderUrl(runtimeConfig.providerUrl),
|
|
328
|
-
networkProfiles: sanitizeProfiles(networkInfo.profiles),
|
|
329
|
-
source: "wdk",
|
|
330
|
-
});
|
|
331
|
-
}
|
|
243
|
+
function notFound(response) {
|
|
244
|
+
sendJson(response, 404, { ok: false, error: "Not Found" });
|
|
245
|
+
}
|
|
332
246
|
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
data: service.generateSeedPhrase(body.words ?? 12),
|
|
338
|
-
});
|
|
339
|
-
}
|
|
247
|
+
function unauthorized(response) {
|
|
248
|
+
response.setHeader("WWW-Authenticate", 'Bearer realm="wdk-evm-wallet"');
|
|
249
|
+
sendJson(response, 401, { ok: false, error: "Unauthorized." });
|
|
250
|
+
}
|
|
340
251
|
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
ok: true,
|
|
345
|
-
data: (await vault.listWallets()).map((wallet) => ({
|
|
346
|
-
...wallet,
|
|
347
|
-
activeNetwork,
|
|
348
|
-
})),
|
|
349
|
-
});
|
|
350
|
-
}
|
|
252
|
+
function pathRequiresAuth(pathname) {
|
|
253
|
+
return pathname !== "/health";
|
|
254
|
+
}
|
|
351
255
|
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
});
|
|
256
|
+
function isAuthorized(request) {
|
|
257
|
+
const header = String(request.headers.authorization || "").trim();
|
|
258
|
+
if (!header.startsWith("Bearer ")) {
|
|
259
|
+
return false;
|
|
357
260
|
}
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
return
|
|
362
|
-
ok: true,
|
|
363
|
-
data: await networkState.setActiveNetwork(body),
|
|
364
|
-
});
|
|
261
|
+
const provided = Buffer.from(header.slice("Bearer ".length).trim(), "utf8");
|
|
262
|
+
const expected = Buffer.from(String(config.authToken || ""), "utf8");
|
|
263
|
+
if (provided.length === 0 || provided.length !== expected.length) {
|
|
264
|
+
return false;
|
|
365
265
|
}
|
|
266
|
+
return crypto.timingSafeEqual(provided, expected);
|
|
267
|
+
}
|
|
366
268
|
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
269
|
+
async function withResolvedSeed(body = {}) {
|
|
270
|
+
const resolved = await vault.resolveSeedPhrase({
|
|
271
|
+
walletId: body.walletId,
|
|
272
|
+
seedPhrase: body.seedPhrase,
|
|
273
|
+
password: body.password,
|
|
274
|
+
});
|
|
275
|
+
return {
|
|
276
|
+
...body,
|
|
277
|
+
seedPhrase: resolved.seedPhrase,
|
|
278
|
+
walletId: resolved.walletId ?? body.walletId ?? null,
|
|
279
|
+
credentialSource: resolved.source,
|
|
280
|
+
unlockExpiresAt: resolved.unlockExpiresAt ?? null,
|
|
281
|
+
};
|
|
282
|
+
}
|
|
378
283
|
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
284
|
+
async function withResolvedSeedOrAddress(body = {}) {
|
|
285
|
+
const address = typeof body.address === "string" ? body.address.trim() : "";
|
|
286
|
+
if (address) {
|
|
287
|
+
return {
|
|
288
|
+
...body,
|
|
289
|
+
address,
|
|
290
|
+
};
|
|
385
291
|
}
|
|
292
|
+
return withResolvedSeed(body);
|
|
293
|
+
}
|
|
386
294
|
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
295
|
+
async function withResolvedNetwork(body = {}) {
|
|
296
|
+
const runtimeConfig = await networkState.resolveRuntimeConfig(body.network);
|
|
297
|
+
return {
|
|
298
|
+
...body,
|
|
299
|
+
network: runtimeConfig.network,
|
|
300
|
+
};
|
|
301
|
+
}
|
|
394
302
|
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
303
|
+
async function handleRequest(request, response) {
|
|
304
|
+
try {
|
|
305
|
+
const url = new URL(request.url || "/", "http://localhost");
|
|
306
|
+
const { method = "GET" } = request;
|
|
307
|
+
|
|
308
|
+
if (pathRequiresAuth(url.pathname) && !isAuthorized(request)) {
|
|
309
|
+
return unauthorized(response);
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
if (method === "GET" && url.pathname === "/health") {
|
|
313
|
+
const runtimeConfig = await networkState.resolveRuntimeConfig();
|
|
314
|
+
const networkInfo = await networkState.getNetworkInfo();
|
|
315
|
+
return sendJson(response, 200, {
|
|
316
|
+
ok: true,
|
|
317
|
+
service: "wdk-evm-wallet",
|
|
318
|
+
version: config.version,
|
|
319
|
+
wallet: "evm",
|
|
320
|
+
network: runtimeConfig.network,
|
|
321
|
+
chainId: runtimeConfig.chainId,
|
|
322
|
+
host: config.host,
|
|
323
|
+
dataDir: config.dataDir,
|
|
324
|
+
instanceId: config.instanceId,
|
|
325
|
+
pid: process.pid,
|
|
326
|
+
authRequired: config.authRequired,
|
|
327
|
+
unlockTimeoutSeconds: config.unlockTimeoutSeconds,
|
|
328
|
+
availableNetworks: Object.keys(config.networkProfiles),
|
|
329
|
+
provider: sanitizeProviderUrl(runtimeConfig.providerUrl),
|
|
330
|
+
networkProfiles: sanitizeProfiles(networkInfo.profiles),
|
|
331
|
+
source: "wdk",
|
|
332
|
+
});
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
if (method === "POST" && url.pathname === "/v1/evm/seed-phrase/generate") {
|
|
336
|
+
const body = await readJsonBody(request);
|
|
337
|
+
return sendJson(response, 200, {
|
|
338
|
+
ok: true,
|
|
339
|
+
data: service.generateSeedPhrase(body.words ?? 12),
|
|
340
|
+
});
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
if (method === "GET" && url.pathname === "/v1/evm/wallets") {
|
|
344
|
+
const activeNetwork = await networkState.getActiveNetwork();
|
|
345
|
+
return sendJson(response, 200, {
|
|
346
|
+
ok: true,
|
|
347
|
+
data: (await vault.listWallets()).map((wallet) => ({
|
|
348
|
+
...wallet,
|
|
349
|
+
activeNetwork,
|
|
350
|
+
})),
|
|
351
|
+
});
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
if (method === "GET" && url.pathname === "/v1/evm/network") {
|
|
355
|
+
return sendJson(response, 200, {
|
|
356
|
+
ok: true,
|
|
357
|
+
data: await networkState.getNetworkInfo(),
|
|
358
|
+
});
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
if (method === "POST" && url.pathname === "/v1/evm/network/set") {
|
|
362
|
+
const body = await readJsonBody(request);
|
|
363
|
+
return sendJson(response, 200, {
|
|
364
|
+
ok: true,
|
|
365
|
+
data: await networkState.setActiveNetwork(body),
|
|
366
|
+
});
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
if (method === "POST" && url.pathname === "/v1/evm/wallets/get") {
|
|
370
|
+
const body = await readJsonBody(request);
|
|
371
|
+
const activeNetwork = await networkState.getActiveNetwork();
|
|
372
|
+
return sendJson(response, 200, {
|
|
373
|
+
ok: true,
|
|
374
|
+
data: {
|
|
375
|
+
...(await vault.getWallet(body)),
|
|
376
|
+
activeNetwork,
|
|
377
|
+
},
|
|
378
|
+
});
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
if (method === "POST" && url.pathname === "/v1/evm/wallets/create") {
|
|
382
|
+
const body = await withResolvedNetwork(await readJsonBody(request));
|
|
383
|
+
return sendJson(response, 200, {
|
|
384
|
+
ok: true,
|
|
385
|
+
data: await vault.createWallet(body),
|
|
386
|
+
});
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
if (method === "POST" && url.pathname === "/v1/evm/wallets/import") {
|
|
390
|
+
const body = await withResolvedNetwork(await readJsonBody(request));
|
|
391
|
+
return sendJson(response, 200, {
|
|
392
|
+
ok: true,
|
|
393
|
+
data: await vault.importWallet(body),
|
|
394
|
+
});
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
if (method === "POST" && url.pathname === "/v1/evm/wallets/unlock") {
|
|
398
|
+
const body = await readJsonBody(request);
|
|
399
|
+
return sendJson(response, 200, {
|
|
400
|
+
ok: true,
|
|
401
|
+
data: await vault.unlockWallet(body),
|
|
402
|
+
});
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
if (method === "POST" && url.pathname === "/v1/evm/wallets/lock") {
|
|
406
|
+
const body = await readJsonBody(request);
|
|
407
|
+
return sendJson(response, 200, {
|
|
408
|
+
ok: true,
|
|
409
|
+
data: await vault.lockWallet(body),
|
|
410
|
+
});
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
if (method === "POST" && url.pathname === "/v1/evm/wallets/reveal-seed") {
|
|
414
|
+
const body = await readJsonBody(request);
|
|
415
|
+
return sendJson(response, 200, {
|
|
416
|
+
ok: true,
|
|
417
|
+
data: await vault.revealSeedPhrase(body),
|
|
418
|
+
});
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
if (method === "POST" && url.pathname === "/v1/evm/wallets/change-password") {
|
|
422
|
+
const body = await readJsonBody(request);
|
|
423
|
+
return sendJson(response, 200, {
|
|
424
|
+
ok: true,
|
|
425
|
+
data: await vault.changePassword(body),
|
|
426
|
+
});
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
if (method === "POST" && url.pathname === "/v1/evm/address/resolve") {
|
|
430
|
+
const body = await withResolvedNetwork(await withResolvedSeed(await readJsonBody(request)));
|
|
431
|
+
const data = await service.resolveAddress(body);
|
|
432
|
+
return sendJson(response, 200, { ok: true, data });
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
if (method === "POST" && url.pathname === "/v1/evm/balance/get") {
|
|
436
|
+
const body = await withResolvedNetwork(await withResolvedSeedOrAddress(await readJsonBody(request)));
|
|
437
|
+
const data = await service.getBalance(body);
|
|
438
|
+
return sendJson(response, 200, { ok: true, data });
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
if (method === "POST" && url.pathname === "/v1/evm/token-balance/get") {
|
|
442
|
+
const body = await withResolvedNetwork(await withResolvedSeedOrAddress(await readJsonBody(request)));
|
|
443
|
+
const data = await service.getTokenBalance(body);
|
|
444
|
+
return sendJson(response, 200, { ok: true, data });
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
if (method === "POST" && url.pathname === "/v1/evm/token-metadata/get") {
|
|
448
|
+
const body = await withResolvedNetwork(await readJsonBody(request));
|
|
449
|
+
const data = await service.getTokenMetadata(body);
|
|
450
|
+
return sendJson(response, 200, { ok: true, data });
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
if (method === "POST" && url.pathname === "/v1/evm/fee-rates/get") {
|
|
454
|
+
const body = await withResolvedNetwork(await readJsonBody(request));
|
|
455
|
+
const data = await service.getFeeRates(body);
|
|
456
|
+
return sendJson(response, 200, { ok: true, data });
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
if (method === "POST" && url.pathname === "/v1/evm/transaction/receipt/get") {
|
|
460
|
+
const body = await withResolvedNetwork(await readJsonBody(request));
|
|
461
|
+
const data = await service.getTransactionReceipt(body);
|
|
462
|
+
return sendJson(response, 200, { ok: true, data });
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
if (method === "POST" && url.pathname === "/v1/evm/aave/account/get") {
|
|
466
|
+
const body = await withResolvedNetwork(await withResolvedSeedOrAddress(await readJsonBody(request)));
|
|
467
|
+
const data = await service.getAaveAccountData(body);
|
|
468
|
+
return sendJson(response, 200, { ok: true, data });
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
if (method === "POST" && url.pathname === "/v1/evm/aave/reserves/get") {
|
|
472
|
+
const body = await withResolvedNetwork(await withResolvedSeedOrAddress(await readJsonBody(request)));
|
|
473
|
+
const data = await service.getAaveReserves(body);
|
|
474
|
+
return sendJson(response, 200, { ok: true, data });
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
if (method === "POST" && url.pathname === "/v1/evm/aave/positions/get") {
|
|
478
|
+
const body = await withResolvedNetwork(await withResolvedSeedOrAddress(await readJsonBody(request)));
|
|
479
|
+
const data = await service.getAavePositions(body);
|
|
480
|
+
return sendJson(response, 200, { ok: true, data });
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
if (method === "POST" && url.pathname === "/v1/evm/morpho/vaults/get") {
|
|
484
|
+
const body = await withResolvedNetwork(await readJsonBody(request));
|
|
485
|
+
const data = await service.getMorphoVaults(body);
|
|
486
|
+
return sendJson(response, 200, { ok: true, data });
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
if (method === "POST" && url.pathname === "/v1/evm/morpho/markets/get") {
|
|
490
|
+
const body = await withResolvedNetwork(await readJsonBody(request));
|
|
491
|
+
const data = await service.getMorphoMarkets(body);
|
|
492
|
+
return sendJson(response, 200, { ok: true, data });
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
if (method === "POST" && url.pathname === "/v1/evm/morpho/positions/get") {
|
|
496
|
+
const body = await withResolvedNetwork(await withResolvedSeedOrAddress(await readJsonBody(request)));
|
|
497
|
+
const data = await service.getMorphoPositions(body);
|
|
498
|
+
return sendJson(response, 200, { ok: true, data });
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
const morphoVaultOperationMatch = url.pathname.match(
|
|
502
|
+
/^\/v1\/evm\/morpho\/vault\/(supply|withdraw)\/(quote|send)$/
|
|
503
|
+
);
|
|
504
|
+
if (method === "POST" && morphoVaultOperationMatch) {
|
|
505
|
+
const operation = morphoVaultOperationMatch[1];
|
|
506
|
+
const action = morphoVaultOperationMatch[2];
|
|
507
|
+
const rawBody = await readJsonBody(request);
|
|
508
|
+
const body =
|
|
509
|
+
action === "quote"
|
|
510
|
+
? await withResolvedNetwork(await withResolvedSeedOrAddress(rawBody))
|
|
511
|
+
: await withResolvedNetwork(await withResolvedSeed(rawBody));
|
|
512
|
+
const data =
|
|
513
|
+
action === "quote"
|
|
514
|
+
? await service.quoteMorphoVaultOperation({ ...body, operation })
|
|
515
|
+
: await service.sendMorphoVaultOperation({ ...body, operation });
|
|
516
|
+
return sendJson(response, 200, { ok: true, data });
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
const morphoMarketOperationMatch = url.pathname.match(
|
|
520
|
+
/^\/v1\/evm\/morpho\/market\/(supply_collateral|borrow|repay|withdraw_collateral)\/(quote|send)$/
|
|
521
|
+
);
|
|
522
|
+
if (method === "POST" && morphoMarketOperationMatch) {
|
|
523
|
+
const operation = morphoMarketOperationMatch[1];
|
|
524
|
+
const action = morphoMarketOperationMatch[2];
|
|
525
|
+
const rawBody = await readJsonBody(request);
|
|
526
|
+
const body =
|
|
527
|
+
action === "quote"
|
|
528
|
+
? await withResolvedNetwork(await withResolvedSeedOrAddress(rawBody))
|
|
529
|
+
: await withResolvedNetwork(await withResolvedSeed(rawBody));
|
|
530
|
+
const data =
|
|
531
|
+
action === "quote"
|
|
532
|
+
? await service.quoteMorphoMarketOperation({ ...body, operation })
|
|
533
|
+
: await service.sendMorphoMarketOperation({ ...body, operation });
|
|
534
|
+
return sendJson(response, 200, { ok: true, data });
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
if (method === "POST" && url.pathname === "/v1/evm/lido/overview/get") {
|
|
538
|
+
const body = await withResolvedNetwork(await withResolvedSeedOrAddress(await readJsonBody(request)));
|
|
539
|
+
const data = await service.getLidoOverview(body);
|
|
540
|
+
return sendJson(response, 200, { ok: true, data });
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
if (method === "POST" && url.pathname === "/v1/evm/lido/positions/get") {
|
|
544
|
+
const body = await withResolvedNetwork(await withResolvedSeedOrAddress(await readJsonBody(request)));
|
|
545
|
+
const data = await service.getLidoPositions(body);
|
|
546
|
+
return sendJson(response, 200, { ok: true, data });
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
if (method === "POST" && url.pathname === "/v1/evm/lido/withdrawals/get") {
|
|
550
|
+
const body = await withResolvedNetwork(await withResolvedSeedOrAddress(await readJsonBody(request)));
|
|
551
|
+
const data = await service.getLidoWithdrawalRequests(body);
|
|
552
|
+
return sendJson(response, 200, { ok: true, data });
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
const aaveOperationMatch = url.pathname.match(
|
|
556
|
+
/^\/v1\/evm\/aave\/(supply|withdraw|borrow|repay)\/(quote|send)$/
|
|
557
|
+
);
|
|
558
|
+
if (method === "POST" && aaveOperationMatch) {
|
|
559
|
+
const operation = aaveOperationMatch[1];
|
|
560
|
+
const action = aaveOperationMatch[2];
|
|
561
|
+
const rawBody = await readJsonBody(request);
|
|
562
|
+
const body =
|
|
563
|
+
action === "quote"
|
|
564
|
+
? await withResolvedNetwork(await withResolvedSeedOrAddress(rawBody))
|
|
565
|
+
: await withResolvedNetwork(await withResolvedSeed(rawBody));
|
|
566
|
+
const data =
|
|
567
|
+
action === "quote"
|
|
568
|
+
? await service.quoteAaveOperation({ ...body, operation })
|
|
569
|
+
: await service.sendAaveOperation({ ...body, operation });
|
|
570
|
+
return sendJson(response, 200, { ok: true, data });
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
const lidoOperationMatch = url.pathname.match(
|
|
574
|
+
/^\/v1\/evm\/lido\/(stake_eth_for_wsteth|wrap_steth|unwrap_wsteth)\/(quote|send)$/
|
|
575
|
+
);
|
|
576
|
+
if (method === "POST" && lidoOperationMatch) {
|
|
577
|
+
const operation = lidoOperationMatch[1];
|
|
578
|
+
const action = lidoOperationMatch[2];
|
|
579
|
+
const rawBody = await readJsonBody(request);
|
|
580
|
+
const body =
|
|
581
|
+
action === "quote"
|
|
582
|
+
? await withResolvedNetwork(await withResolvedSeedOrAddress(rawBody))
|
|
583
|
+
: await withResolvedNetwork(await withResolvedSeed(rawBody));
|
|
584
|
+
const data =
|
|
585
|
+
action === "quote"
|
|
586
|
+
? await service.quoteLidoOperation({ ...body, operation })
|
|
587
|
+
: await service.sendLidoOperation({ ...body, operation });
|
|
588
|
+
return sendJson(response, 200, { ok: true, data });
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
const lidoWithdrawalOperationMatch = url.pathname.match(
|
|
592
|
+
/^\/v1\/evm\/lido\/(request_withdrawal_steth|request_withdrawal_wsteth|claim_withdrawal)\/(quote|send)$/
|
|
593
|
+
);
|
|
594
|
+
if (method === "POST" && lidoWithdrawalOperationMatch) {
|
|
595
|
+
const operation = lidoWithdrawalOperationMatch[1];
|
|
596
|
+
const action = lidoWithdrawalOperationMatch[2];
|
|
597
|
+
const rawBody = await readJsonBody(request);
|
|
598
|
+
const body =
|
|
599
|
+
action === "quote"
|
|
600
|
+
? await withResolvedNetwork(await withResolvedSeedOrAddress(rawBody))
|
|
601
|
+
: await withResolvedNetwork(await withResolvedSeed(rawBody));
|
|
602
|
+
const data =
|
|
603
|
+
action === "quote"
|
|
604
|
+
? await service.quoteLidoWithdrawalOperation({ ...body, operation })
|
|
605
|
+
: await service.sendLidoWithdrawalOperation({ ...body, operation });
|
|
606
|
+
return sendJson(response, 200, { ok: true, data });
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
if (method === "POST" && url.pathname === "/v1/evm/swap/quote") {
|
|
610
|
+
const body = await withResolvedNetwork(await withResolvedSeedOrAddress(await readJsonBody(request)));
|
|
611
|
+
const data = await service.quoteSwap(body);
|
|
612
|
+
return sendJson(response, 200, { ok: true, data });
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
if (method === "POST" && url.pathname === "/v1/evm/swap/send") {
|
|
616
|
+
const body = await withResolvedNetwork(await withResolvedSeed(await readJsonBody(request)));
|
|
617
|
+
const data = await service.swap(body);
|
|
618
|
+
return sendJson(response, 200, { ok: true, data });
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
if (method === "POST" && url.pathname === "/v1/evm/lifi/quote") {
|
|
622
|
+
const body = await withResolvedNetwork(await withResolvedSeedOrAddress(await readJsonBody(request)));
|
|
623
|
+
const data = await service.quoteLifiSwap(body);
|
|
624
|
+
return sendJson(response, 200, { ok: true, data });
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
if (method === "POST" && url.pathname === "/v1/evm/lifi/send") {
|
|
628
|
+
const body = await withResolvedNetwork(await withResolvedSeed(await readJsonBody(request)));
|
|
629
|
+
const data = await service.sendLifiSwap(body);
|
|
630
|
+
return sendJson(response, 200, { ok: true, data });
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
if (method === "POST" && url.pathname === "/v1/evm/uniswap/swap/quote") {
|
|
634
|
+
const body = await withResolvedNetwork(await withResolvedSeedOrAddress(await readJsonBody(request)));
|
|
635
|
+
const data = await service.quoteUniswapSwap(body);
|
|
636
|
+
return sendJson(response, 200, { ok: true, data });
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
if (method === "POST" && url.pathname === "/v1/evm/uniswap/swap/send") {
|
|
640
|
+
const body = await withResolvedNetwork(await withResolvedSeed(await readJsonBody(request)));
|
|
641
|
+
const data = await service.sendUniswapSwap(body);
|
|
642
|
+
return sendJson(response, 200, { ok: true, data });
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
if (method === "POST" && url.pathname === "/v1/evm/uniswap/liquidity/quote") {
|
|
646
|
+
const body = await withResolvedNetwork(await withResolvedSeedOrAddress(await readJsonBody(request)));
|
|
647
|
+
const data = await service.quoteUniswapLiquidity(body);
|
|
648
|
+
return sendJson(response, 200, { ok: true, data });
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
if (method === "POST" && url.pathname === "/v1/evm/uniswap/liquidity/pools") {
|
|
652
|
+
const body = await withResolvedNetwork(await readJsonBody(request));
|
|
653
|
+
const data = await service.getUniswapLiquidityPools(body);
|
|
654
|
+
return sendJson(response, 200, { ok: true, data });
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
if (method === "POST" && url.pathname === "/v1/evm/uniswap/liquidity/positions") {
|
|
658
|
+
const body = await withResolvedNetwork(await withResolvedSeedOrAddress(await readJsonBody(request)));
|
|
659
|
+
const data = await service.getUniswapLiquidityPositions(body);
|
|
660
|
+
return sendJson(response, 200, { ok: true, data });
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
if (method === "POST" && url.pathname === "/v1/evm/uniswap/liquidity/send") {
|
|
664
|
+
const body = await withResolvedNetwork(await withResolvedSeed(await readJsonBody(request)));
|
|
665
|
+
const data = await service.sendUniswapLiquidity(body);
|
|
666
|
+
return sendJson(response, 200, { ok: true, data });
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
if (method === "POST" && url.pathname === "/v1/evm/transfer/quote") {
|
|
670
|
+
const body = await withResolvedNetwork(await withResolvedSeed(await readJsonBody(request)));
|
|
671
|
+
const data = await service.quoteNativeTransfer(body);
|
|
672
|
+
return sendJson(response, 200, { ok: true, data });
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
if (method === "POST" && url.pathname === "/v1/evm/transfer/send") {
|
|
676
|
+
const body = await withResolvedNetwork(await withResolvedSeed(await readJsonBody(request)));
|
|
677
|
+
const data = await service.sendNativeTransfer(body);
|
|
678
|
+
return sendJson(response, 200, { ok: true, data });
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
if (method === "POST" && url.pathname === "/v1/evm/token-transfer/quote") {
|
|
682
|
+
const body = await withResolvedNetwork(await withResolvedSeed(await readJsonBody(request)));
|
|
683
|
+
const data = await service.quoteTokenTransfer(body);
|
|
684
|
+
return sendJson(response, 200, { ok: true, data });
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
if (method === "POST" && url.pathname === "/v1/evm/token-transfer/send") {
|
|
688
|
+
const body = await withResolvedNetwork(await withResolvedSeed(await readJsonBody(request)));
|
|
689
|
+
const data = await service.sendTokenTransfer(body);
|
|
690
|
+
return sendJson(response, 200, { ok: true, data });
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
if (method === "POST" && url.pathname === "/v1/evm/x402/exact/sign") {
|
|
694
|
+
const body = await withResolvedNetwork(await withResolvedSeed(await readJsonBody(request)));
|
|
695
|
+
const data = await service.signX402ExactTypedData(body);
|
|
696
|
+
return sendJson(response, 200, { ok: true, data });
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
if (method === "POST" && url.pathname === "/v1/evm/message/sign") {
|
|
700
|
+
const body = await withResolvedNetwork(await withResolvedSeed(await readJsonBody(request)));
|
|
701
|
+
const data = await service.signPersonalMessage(body);
|
|
702
|
+
return sendJson(response, 200, { ok: true, data });
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
return notFound(response);
|
|
706
|
+
} catch (error) {
|
|
707
|
+
const shaped = toErrorResponse(error, new URL(request.url || "/", "http://localhost").pathname, 400);
|
|
708
|
+
return sendJson(response, shaped.statusCode, shaped.payload);
|
|
401
709
|
}
|
|
710
|
+
}
|
|
402
711
|
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
712
|
+
const server = createServer((request, response) => {
|
|
713
|
+
void withTrackedRequest(shutdown, async () => {
|
|
714
|
+
try {
|
|
715
|
+
await handleRequest(request, response);
|
|
716
|
+
} catch (error) {
|
|
717
|
+
const shaped = toErrorResponse(
|
|
718
|
+
error,
|
|
719
|
+
new URL(request.url || "/", "http://localhost").pathname,
|
|
720
|
+
500,
|
|
721
|
+
);
|
|
722
|
+
sendJson(response, shaped.statusCode, shaped.payload);
|
|
723
|
+
}
|
|
724
|
+
});
|
|
725
|
+
});
|
|
410
726
|
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
727
|
+
// 8s stays strictly inside the 10s SIGTERM->SIGKILL window the Python client
|
|
728
|
+
// allows (agent_wallet/evm_user_wallets.py), so the orderly path always wins.
|
|
729
|
+
const shutdown = createShutdownCoordinator({
|
|
730
|
+
closeServer: () => {
|
|
731
|
+
server.close();
|
|
732
|
+
if (config.transport === "socket") {
|
|
733
|
+
try {
|
|
734
|
+
fs.unlinkSync(config.socketPath);
|
|
735
|
+
} catch {
|
|
736
|
+
// already gone — nothing to clean up
|
|
737
|
+
}
|
|
738
|
+
}
|
|
739
|
+
},
|
|
740
|
+
exit: (code) => process.exit(code),
|
|
741
|
+
graceMs: 8000,
|
|
742
|
+
log: (message) => console.log(message),
|
|
743
|
+
});
|
|
418
744
|
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
745
|
+
// Named so a test's close() can remove exactly these listeners — startServer()
|
|
746
|
+
// runs multiple times in one process across tests, and anonymous listeners
|
|
747
|
+
// would accumulate on `process` for the life of the test run.
|
|
748
|
+
const onSigterm = () => shutdown.begin("SIGTERM");
|
|
749
|
+
const onSigint = () => shutdown.begin("SIGINT");
|
|
750
|
+
process.on("SIGTERM", onSigterm);
|
|
751
|
+
process.on("SIGINT", onSigint);
|
|
752
|
+
|
|
753
|
+
if (config.transport === "socket" && fs.existsSync(config.socketPath)) {
|
|
754
|
+
// A leftover socket file from an unclean shutdown makes bind() fail even
|
|
755
|
+
// though nothing is listening. Probe it first: a connection error means
|
|
756
|
+
// it's stale and safe to remove; a live response means something else is
|
|
757
|
+
// genuinely already serving this exact dataDir, which the caller's own
|
|
758
|
+
// health check should have caught before ever spawning us.
|
|
759
|
+
const alive = await new Promise((resolve) => {
|
|
760
|
+
const probe = net.connect(config.socketPath);
|
|
761
|
+
probe.once("connect", () => {
|
|
762
|
+
probe.destroy();
|
|
763
|
+
resolve(true);
|
|
424
764
|
});
|
|
765
|
+
probe.once("error", () => resolve(false));
|
|
766
|
+
});
|
|
767
|
+
if (!alive) {
|
|
768
|
+
fs.unlinkSync(config.socketPath);
|
|
425
769
|
}
|
|
426
|
-
|
|
427
|
-
if (method === "POST" && url.pathname === "/v1/evm/address/resolve") {
|
|
428
|
-
const body = await withResolvedNetwork(await withResolvedSeed(await readJsonBody(request)));
|
|
429
|
-
const data = await service.resolveAddress(body);
|
|
430
|
-
return sendJson(response, 200, { ok: true, data });
|
|
431
|
-
}
|
|
432
|
-
|
|
433
|
-
if (method === "POST" && url.pathname === "/v1/evm/balance/get") {
|
|
434
|
-
const body = await withResolvedNetwork(await withResolvedSeedOrAddress(await readJsonBody(request)));
|
|
435
|
-
const data = await service.getBalance(body);
|
|
436
|
-
return sendJson(response, 200, { ok: true, data });
|
|
437
|
-
}
|
|
438
|
-
|
|
439
|
-
if (method === "POST" && url.pathname === "/v1/evm/token-balance/get") {
|
|
440
|
-
const body = await withResolvedNetwork(await withResolvedSeedOrAddress(await readJsonBody(request)));
|
|
441
|
-
const data = await service.getTokenBalance(body);
|
|
442
|
-
return sendJson(response, 200, { ok: true, data });
|
|
443
|
-
}
|
|
444
|
-
|
|
445
|
-
if (method === "POST" && url.pathname === "/v1/evm/token-metadata/get") {
|
|
446
|
-
const body = await withResolvedNetwork(await readJsonBody(request));
|
|
447
|
-
const data = await service.getTokenMetadata(body);
|
|
448
|
-
return sendJson(response, 200, { ok: true, data });
|
|
449
|
-
}
|
|
450
|
-
|
|
451
|
-
if (method === "POST" && url.pathname === "/v1/evm/fee-rates/get") {
|
|
452
|
-
const body = await withResolvedNetwork(await readJsonBody(request));
|
|
453
|
-
const data = await service.getFeeRates(body);
|
|
454
|
-
return sendJson(response, 200, { ok: true, data });
|
|
455
|
-
}
|
|
456
|
-
|
|
457
|
-
if (method === "POST" && url.pathname === "/v1/evm/transaction/receipt/get") {
|
|
458
|
-
const body = await withResolvedNetwork(await readJsonBody(request));
|
|
459
|
-
const data = await service.getTransactionReceipt(body);
|
|
460
|
-
return sendJson(response, 200, { ok: true, data });
|
|
461
|
-
}
|
|
462
|
-
|
|
463
|
-
if (method === "POST" && url.pathname === "/v1/evm/aave/account/get") {
|
|
464
|
-
const body = await withResolvedNetwork(await withResolvedSeedOrAddress(await readJsonBody(request)));
|
|
465
|
-
const data = await service.getAaveAccountData(body);
|
|
466
|
-
return sendJson(response, 200, { ok: true, data });
|
|
467
|
-
}
|
|
468
|
-
|
|
469
|
-
if (method === "POST" && url.pathname === "/v1/evm/aave/reserves/get") {
|
|
470
|
-
const body = await withResolvedNetwork(await withResolvedSeedOrAddress(await readJsonBody(request)));
|
|
471
|
-
const data = await service.getAaveReserves(body);
|
|
472
|
-
return sendJson(response, 200, { ok: true, data });
|
|
473
|
-
}
|
|
474
|
-
|
|
475
|
-
if (method === "POST" && url.pathname === "/v1/evm/aave/positions/get") {
|
|
476
|
-
const body = await withResolvedNetwork(await withResolvedSeedOrAddress(await readJsonBody(request)));
|
|
477
|
-
const data = await service.getAavePositions(body);
|
|
478
|
-
return sendJson(response, 200, { ok: true, data });
|
|
479
|
-
}
|
|
480
|
-
|
|
481
|
-
if (method === "POST" && url.pathname === "/v1/evm/morpho/vaults/get") {
|
|
482
|
-
const body = await withResolvedNetwork(await readJsonBody(request));
|
|
483
|
-
const data = await service.getMorphoVaults(body);
|
|
484
|
-
return sendJson(response, 200, { ok: true, data });
|
|
485
|
-
}
|
|
486
|
-
|
|
487
|
-
if (method === "POST" && url.pathname === "/v1/evm/morpho/markets/get") {
|
|
488
|
-
const body = await withResolvedNetwork(await readJsonBody(request));
|
|
489
|
-
const data = await service.getMorphoMarkets(body);
|
|
490
|
-
return sendJson(response, 200, { ok: true, data });
|
|
491
|
-
}
|
|
492
|
-
|
|
493
|
-
if (method === "POST" && url.pathname === "/v1/evm/morpho/positions/get") {
|
|
494
|
-
const body = await withResolvedNetwork(await withResolvedSeedOrAddress(await readJsonBody(request)));
|
|
495
|
-
const data = await service.getMorphoPositions(body);
|
|
496
|
-
return sendJson(response, 200, { ok: true, data });
|
|
497
|
-
}
|
|
498
|
-
|
|
499
|
-
const morphoVaultOperationMatch = url.pathname.match(
|
|
500
|
-
/^\/v1\/evm\/morpho\/vault\/(supply|withdraw)\/(quote|send)$/
|
|
501
|
-
);
|
|
502
|
-
if (method === "POST" && morphoVaultOperationMatch) {
|
|
503
|
-
const operation = morphoVaultOperationMatch[1];
|
|
504
|
-
const action = morphoVaultOperationMatch[2];
|
|
505
|
-
const rawBody = await readJsonBody(request);
|
|
506
|
-
const body =
|
|
507
|
-
action === "quote"
|
|
508
|
-
? await withResolvedNetwork(await withResolvedSeedOrAddress(rawBody))
|
|
509
|
-
: await withResolvedNetwork(await withResolvedSeed(rawBody));
|
|
510
|
-
const data =
|
|
511
|
-
action === "quote"
|
|
512
|
-
? await service.quoteMorphoVaultOperation({ ...body, operation })
|
|
513
|
-
: await service.sendMorphoVaultOperation({ ...body, operation });
|
|
514
|
-
return sendJson(response, 200, { ok: true, data });
|
|
515
|
-
}
|
|
516
|
-
|
|
517
|
-
const morphoMarketOperationMatch = url.pathname.match(
|
|
518
|
-
/^\/v1\/evm\/morpho\/market\/(supply_collateral|borrow|repay|withdraw_collateral)\/(quote|send)$/
|
|
519
|
-
);
|
|
520
|
-
if (method === "POST" && morphoMarketOperationMatch) {
|
|
521
|
-
const operation = morphoMarketOperationMatch[1];
|
|
522
|
-
const action = morphoMarketOperationMatch[2];
|
|
523
|
-
const rawBody = await readJsonBody(request);
|
|
524
|
-
const body =
|
|
525
|
-
action === "quote"
|
|
526
|
-
? await withResolvedNetwork(await withResolvedSeedOrAddress(rawBody))
|
|
527
|
-
: await withResolvedNetwork(await withResolvedSeed(rawBody));
|
|
528
|
-
const data =
|
|
529
|
-
action === "quote"
|
|
530
|
-
? await service.quoteMorphoMarketOperation({ ...body, operation })
|
|
531
|
-
: await service.sendMorphoMarketOperation({ ...body, operation });
|
|
532
|
-
return sendJson(response, 200, { ok: true, data });
|
|
533
|
-
}
|
|
534
|
-
|
|
535
|
-
if (method === "POST" && url.pathname === "/v1/evm/lido/overview/get") {
|
|
536
|
-
const body = await withResolvedNetwork(await withResolvedSeedOrAddress(await readJsonBody(request)));
|
|
537
|
-
const data = await service.getLidoOverview(body);
|
|
538
|
-
return sendJson(response, 200, { ok: true, data });
|
|
539
|
-
}
|
|
540
|
-
|
|
541
|
-
if (method === "POST" && url.pathname === "/v1/evm/lido/positions/get") {
|
|
542
|
-
const body = await withResolvedNetwork(await withResolvedSeedOrAddress(await readJsonBody(request)));
|
|
543
|
-
const data = await service.getLidoPositions(body);
|
|
544
|
-
return sendJson(response, 200, { ok: true, data });
|
|
545
|
-
}
|
|
546
|
-
|
|
547
|
-
if (method === "POST" && url.pathname === "/v1/evm/lido/withdrawals/get") {
|
|
548
|
-
const body = await withResolvedNetwork(await withResolvedSeedOrAddress(await readJsonBody(request)));
|
|
549
|
-
const data = await service.getLidoWithdrawalRequests(body);
|
|
550
|
-
return sendJson(response, 200, { ok: true, data });
|
|
551
|
-
}
|
|
552
|
-
|
|
553
|
-
const aaveOperationMatch = url.pathname.match(
|
|
554
|
-
/^\/v1\/evm\/aave\/(supply|withdraw|borrow|repay)\/(quote|send)$/
|
|
555
|
-
);
|
|
556
|
-
if (method === "POST" && aaveOperationMatch) {
|
|
557
|
-
const operation = aaveOperationMatch[1];
|
|
558
|
-
const action = aaveOperationMatch[2];
|
|
559
|
-
const rawBody = await readJsonBody(request);
|
|
560
|
-
const body =
|
|
561
|
-
action === "quote"
|
|
562
|
-
? await withResolvedNetwork(await withResolvedSeedOrAddress(rawBody))
|
|
563
|
-
: await withResolvedNetwork(await withResolvedSeed(rawBody));
|
|
564
|
-
const data =
|
|
565
|
-
action === "quote"
|
|
566
|
-
? await service.quoteAaveOperation({ ...body, operation })
|
|
567
|
-
: await service.sendAaveOperation({ ...body, operation });
|
|
568
|
-
return sendJson(response, 200, { ok: true, data });
|
|
569
|
-
}
|
|
570
|
-
|
|
571
|
-
const lidoOperationMatch = url.pathname.match(
|
|
572
|
-
/^\/v1\/evm\/lido\/(stake_eth_for_wsteth|wrap_steth|unwrap_wsteth)\/(quote|send)$/
|
|
573
|
-
);
|
|
574
|
-
if (method === "POST" && lidoOperationMatch) {
|
|
575
|
-
const operation = lidoOperationMatch[1];
|
|
576
|
-
const action = lidoOperationMatch[2];
|
|
577
|
-
const rawBody = await readJsonBody(request);
|
|
578
|
-
const body =
|
|
579
|
-
action === "quote"
|
|
580
|
-
? await withResolvedNetwork(await withResolvedSeedOrAddress(rawBody))
|
|
581
|
-
: await withResolvedNetwork(await withResolvedSeed(rawBody));
|
|
582
|
-
const data =
|
|
583
|
-
action === "quote"
|
|
584
|
-
? await service.quoteLidoOperation({ ...body, operation })
|
|
585
|
-
: await service.sendLidoOperation({ ...body, operation });
|
|
586
|
-
return sendJson(response, 200, { ok: true, data });
|
|
587
|
-
}
|
|
588
|
-
|
|
589
|
-
const lidoWithdrawalOperationMatch = url.pathname.match(
|
|
590
|
-
/^\/v1\/evm\/lido\/(request_withdrawal_steth|request_withdrawal_wsteth|claim_withdrawal)\/(quote|send)$/
|
|
591
|
-
);
|
|
592
|
-
if (method === "POST" && lidoWithdrawalOperationMatch) {
|
|
593
|
-
const operation = lidoWithdrawalOperationMatch[1];
|
|
594
|
-
const action = lidoWithdrawalOperationMatch[2];
|
|
595
|
-
const rawBody = await readJsonBody(request);
|
|
596
|
-
const body =
|
|
597
|
-
action === "quote"
|
|
598
|
-
? await withResolvedNetwork(await withResolvedSeedOrAddress(rawBody))
|
|
599
|
-
: await withResolvedNetwork(await withResolvedSeed(rawBody));
|
|
600
|
-
const data =
|
|
601
|
-
action === "quote"
|
|
602
|
-
? await service.quoteLidoWithdrawalOperation({ ...body, operation })
|
|
603
|
-
: await service.sendLidoWithdrawalOperation({ ...body, operation });
|
|
604
|
-
return sendJson(response, 200, { ok: true, data });
|
|
605
|
-
}
|
|
606
|
-
|
|
607
|
-
if (method === "POST" && url.pathname === "/v1/evm/swap/quote") {
|
|
608
|
-
const body = await withResolvedNetwork(await withResolvedSeedOrAddress(await readJsonBody(request)));
|
|
609
|
-
const data = await service.quoteSwap(body);
|
|
610
|
-
return sendJson(response, 200, { ok: true, data });
|
|
611
|
-
}
|
|
612
|
-
|
|
613
|
-
if (method === "POST" && url.pathname === "/v1/evm/swap/send") {
|
|
614
|
-
const body = await withResolvedNetwork(await withResolvedSeed(await readJsonBody(request)));
|
|
615
|
-
const data = await service.swap(body);
|
|
616
|
-
return sendJson(response, 200, { ok: true, data });
|
|
617
|
-
}
|
|
618
|
-
|
|
619
|
-
if (method === "POST" && url.pathname === "/v1/evm/lifi/quote") {
|
|
620
|
-
const body = await withResolvedNetwork(await withResolvedSeedOrAddress(await readJsonBody(request)));
|
|
621
|
-
const data = await service.quoteLifiSwap(body);
|
|
622
|
-
return sendJson(response, 200, { ok: true, data });
|
|
623
|
-
}
|
|
624
|
-
|
|
625
|
-
if (method === "POST" && url.pathname === "/v1/evm/lifi/send") {
|
|
626
|
-
const body = await withResolvedNetwork(await withResolvedSeed(await readJsonBody(request)));
|
|
627
|
-
const data = await service.sendLifiSwap(body);
|
|
628
|
-
return sendJson(response, 200, { ok: true, data });
|
|
629
|
-
}
|
|
630
|
-
|
|
631
|
-
if (method === "POST" && url.pathname === "/v1/evm/uniswap/swap/quote") {
|
|
632
|
-
const body = await withResolvedNetwork(await withResolvedSeedOrAddress(await readJsonBody(request)));
|
|
633
|
-
const data = await service.quoteUniswapSwap(body);
|
|
634
|
-
return sendJson(response, 200, { ok: true, data });
|
|
635
|
-
}
|
|
636
|
-
|
|
637
|
-
if (method === "POST" && url.pathname === "/v1/evm/uniswap/swap/send") {
|
|
638
|
-
const body = await withResolvedNetwork(await withResolvedSeed(await readJsonBody(request)));
|
|
639
|
-
const data = await service.sendUniswapSwap(body);
|
|
640
|
-
return sendJson(response, 200, { ok: true, data });
|
|
641
|
-
}
|
|
642
|
-
|
|
643
|
-
if (method === "POST" && url.pathname === "/v1/evm/uniswap/liquidity/quote") {
|
|
644
|
-
const body = await withResolvedNetwork(await withResolvedSeedOrAddress(await readJsonBody(request)));
|
|
645
|
-
const data = await service.quoteUniswapLiquidity(body);
|
|
646
|
-
return sendJson(response, 200, { ok: true, data });
|
|
647
|
-
}
|
|
648
|
-
|
|
649
|
-
if (method === "POST" && url.pathname === "/v1/evm/uniswap/liquidity/pools") {
|
|
650
|
-
const body = await withResolvedNetwork(await readJsonBody(request));
|
|
651
|
-
const data = await service.getUniswapLiquidityPools(body);
|
|
652
|
-
return sendJson(response, 200, { ok: true, data });
|
|
653
|
-
}
|
|
654
|
-
|
|
655
|
-
if (method === "POST" && url.pathname === "/v1/evm/uniswap/liquidity/positions") {
|
|
656
|
-
const body = await withResolvedNetwork(await withResolvedSeedOrAddress(await readJsonBody(request)));
|
|
657
|
-
const data = await service.getUniswapLiquidityPositions(body);
|
|
658
|
-
return sendJson(response, 200, { ok: true, data });
|
|
659
|
-
}
|
|
660
|
-
|
|
661
|
-
if (method === "POST" && url.pathname === "/v1/evm/uniswap/liquidity/send") {
|
|
662
|
-
const body = await withResolvedNetwork(await withResolvedSeed(await readJsonBody(request)));
|
|
663
|
-
const data = await service.sendUniswapLiquidity(body);
|
|
664
|
-
return sendJson(response, 200, { ok: true, data });
|
|
665
|
-
}
|
|
666
|
-
|
|
667
|
-
if (method === "POST" && url.pathname === "/v1/evm/transfer/quote") {
|
|
668
|
-
const body = await withResolvedNetwork(await withResolvedSeed(await readJsonBody(request)));
|
|
669
|
-
const data = await service.quoteNativeTransfer(body);
|
|
670
|
-
return sendJson(response, 200, { ok: true, data });
|
|
671
|
-
}
|
|
672
|
-
|
|
673
|
-
if (method === "POST" && url.pathname === "/v1/evm/transfer/send") {
|
|
674
|
-
const body = await withResolvedNetwork(await withResolvedSeed(await readJsonBody(request)));
|
|
675
|
-
const data = await service.sendNativeTransfer(body);
|
|
676
|
-
return sendJson(response, 200, { ok: true, data });
|
|
677
|
-
}
|
|
678
|
-
|
|
679
|
-
if (method === "POST" && url.pathname === "/v1/evm/token-transfer/quote") {
|
|
680
|
-
const body = await withResolvedNetwork(await withResolvedSeed(await readJsonBody(request)));
|
|
681
|
-
const data = await service.quoteTokenTransfer(body);
|
|
682
|
-
return sendJson(response, 200, { ok: true, data });
|
|
683
|
-
}
|
|
684
|
-
|
|
685
|
-
if (method === "POST" && url.pathname === "/v1/evm/token-transfer/send") {
|
|
686
|
-
const body = await withResolvedNetwork(await withResolvedSeed(await readJsonBody(request)));
|
|
687
|
-
const data = await service.sendTokenTransfer(body);
|
|
688
|
-
return sendJson(response, 200, { ok: true, data });
|
|
689
|
-
}
|
|
690
|
-
|
|
691
|
-
if (method === "POST" && url.pathname === "/v1/evm/x402/exact/sign") {
|
|
692
|
-
const body = await withResolvedNetwork(await withResolvedSeed(await readJsonBody(request)));
|
|
693
|
-
const data = await service.signX402ExactTypedData(body);
|
|
694
|
-
return sendJson(response, 200, { ok: true, data });
|
|
695
|
-
}
|
|
696
|
-
|
|
697
|
-
if (method === "POST" && url.pathname === "/v1/evm/message/sign") {
|
|
698
|
-
const body = await withResolvedNetwork(await withResolvedSeed(await readJsonBody(request)));
|
|
699
|
-
const data = await service.signPersonalMessage(body);
|
|
700
|
-
return sendJson(response, 200, { ok: true, data });
|
|
701
|
-
}
|
|
702
|
-
|
|
703
|
-
return notFound(response);
|
|
704
|
-
} catch (error) {
|
|
705
|
-
const shaped = toErrorResponse(error, new URL(request.url || "/", "http://localhost").pathname, 400);
|
|
706
|
-
return sendJson(response, shaped.statusCode, shaped.payload);
|
|
707
770
|
}
|
|
708
|
-
}
|
|
709
771
|
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
772
|
+
return new Promise((resolve) => {
|
|
773
|
+
const onListening = () => {
|
|
774
|
+
if (config.transport === "socket") {
|
|
775
|
+
fs.chmodSync(config.socketPath, 0o600);
|
|
776
|
+
}
|
|
777
|
+
console.log(
|
|
778
|
+
config.transport === "socket"
|
|
779
|
+
? `wdk-evm-wallet listening on ${config.socketPath} (${config.network})`
|
|
780
|
+
: `wdk-evm-wallet listening on ${config.host}:${config.port} (${config.network})`,
|
|
719
781
|
);
|
|
720
|
-
|
|
782
|
+
resolve({
|
|
783
|
+
server,
|
|
784
|
+
// Deliberately does NOT go through `shutdown.begin` — that path ends
|
|
785
|
+
// in `exit(code)` (`process.exit`), which is correct for the real
|
|
786
|
+
// SIGTERM/SIGINT handlers below but would kill the whole test
|
|
787
|
+
// process the first time a test calls `close()`. This is a plain
|
|
788
|
+
// close: stop the server, clean up the socket file, done.
|
|
789
|
+
close: () =>
|
|
790
|
+
new Promise((closeResolve) => {
|
|
791
|
+
process.off("SIGTERM", onSigterm);
|
|
792
|
+
process.off("SIGINT", onSigint);
|
|
793
|
+
server.close(() => {
|
|
794
|
+
if (config.transport === "socket") {
|
|
795
|
+
try {
|
|
796
|
+
fs.unlinkSync(config.socketPath);
|
|
797
|
+
} catch {
|
|
798
|
+
// already gone — nothing to clean up
|
|
799
|
+
}
|
|
800
|
+
}
|
|
801
|
+
closeResolve();
|
|
802
|
+
});
|
|
803
|
+
}),
|
|
804
|
+
});
|
|
805
|
+
};
|
|
806
|
+
server.once("listening", onListening);
|
|
807
|
+
if (config.transport === "tcp") {
|
|
808
|
+
server.listen(config.port, config.host);
|
|
809
|
+
} else {
|
|
810
|
+
server.listen(config.socketPath);
|
|
721
811
|
}
|
|
722
812
|
});
|
|
723
|
-
}
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
closeServer: () => server.close(),
|
|
729
|
-
exit: (code) => process.exit(code),
|
|
730
|
-
graceMs: 8000,
|
|
731
|
-
log: (message) => console.log(message),
|
|
732
|
-
});
|
|
733
|
-
|
|
734
|
-
process.on("SIGTERM", () => shutdown.begin("SIGTERM"));
|
|
735
|
-
process.on("SIGINT", () => shutdown.begin("SIGINT"));
|
|
736
|
-
|
|
737
|
-
server.listen(config.port, config.host, () => {
|
|
738
|
-
console.log(
|
|
739
|
-
`wdk-evm-wallet listening on ${config.host}:${config.port} (${config.network})`
|
|
740
|
-
);
|
|
741
|
-
});
|
|
813
|
+
}
|
|
814
|
+
|
|
815
|
+
if (import.meta.url === `file://${process.argv[1]}`) {
|
|
816
|
+
startServer();
|
|
817
|
+
}
|