@agentlayer.tech/wallet 0.1.37 → 0.1.44

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. package/.claude-plugin/marketplace.json +17 -0
  2. package/.openclaw/extensions/agent-wallet/openclaw.plugin.json +1 -1
  3. package/.openclaw/extensions/agent-wallet/package.json +1 -1
  4. package/CHANGELOG.md +37 -0
  5. package/README.md +14 -0
  6. package/VERSION +1 -1
  7. package/agent-wallet/agent_wallet/__init__.py +1 -1
  8. package/agent-wallet/agent_wallet/evm_user_wallets.py +114 -6
  9. package/agent-wallet/agent_wallet/openclaw_adapter.py +331 -0
  10. package/agent-wallet/agent_wallet/openclaw_cli.py +22 -0
  11. package/agent-wallet/agent_wallet/providers/wdk_evm_local.py +2 -0
  12. package/agent-wallet/agent_wallet/telemetry.py +306 -0
  13. package/agent-wallet/agent_wallet/wallet_layer/base.py +32 -0
  14. package/agent-wallet/agent_wallet/wallet_layer/wdk_evm.py +156 -0
  15. package/agent-wallet/openclaw.plugin.json +1 -1
  16. package/agent-wallet/pyproject.toml +1 -1
  17. package/agent-wallet/scripts/bootstrap_openclaw_evm.py +20 -2
  18. package/claude-code/plugins/agent-wallet/.claude-plugin/plugin.json +1 -1
  19. package/claude-code/plugins/agent-wallet/README.md +30 -1
  20. package/claude-code/plugins/agent-wallet/commands/wallet-setup.md +24 -0
  21. package/claude-code/plugins/agent-wallet/hooks/hooks.json +16 -0
  22. package/claude-code/plugins/agent-wallet/scripts/bootstrap_backend.sh +136 -0
  23. package/claude-code/plugins/agent-wallet/scripts/run_mcp.sh +6 -1
  24. package/codex/plugins/agent-wallet/.codex-plugin/plugin.json +1 -1
  25. package/codex/plugins/agent-wallet/scripts/run_mcp.sh +5 -0
  26. package/hermes/plugins/agent_wallet/plugin.yaml +1 -1
  27. package/hermes/plugins/agent_wallet/tools.py +3 -0
  28. package/package.json +2 -1
  29. package/wdk-btc-wallet/package.json +1 -1
  30. package/wdk-evm-wallet/README.md +25 -0
  31. package/wdk-evm-wallet/package.json +4 -2
  32. package/wdk-evm-wallet/src/config.js +47 -0
  33. package/wdk-evm-wallet/src/server.js +25 -3
  34. package/wdk-evm-wallet/src/wdk_evm_wallet.js +545 -0
@@ -0,0 +1,136 @@
1
+ #!/bin/sh
2
+ set -eu
3
+
4
+ # bootstrap_backend.sh — the in-CLI bridge to the npm installer.
5
+ #
6
+ # Claude Code's plugin marketplace only copies the plugin files (this MCP bridge
7
+ # and its skill) into the cache. It does NOT lay down the Python backend runtime
8
+ # (venv + agent_wallet package + server.py + sealed-secret handling) that the
9
+ # bridge talks to. This script closes that gap: it detects whether the backend
10
+ # runtime is present and, if not, delegates to the existing npm installer
11
+ # (`npx @agentlayer.tech/wallet install --yes`) so the whole thing can be set up
12
+ # without leaving the Claude Code CLI.
13
+ #
14
+ # Modes:
15
+ # bootstrap_backend.sh check Report readiness only. Exit 0 if ready, 1 if not.
16
+ # Never installs anything.
17
+ # bootstrap_backend.sh install Ensure the backend is installed (default).
18
+ # Idempotent: a no-op when already healthy.
19
+ #
20
+ # Used by:
21
+ # - /wallet-setup slash command -> `install` (explicit, user-initiated).
22
+ # - SessionStart hook -> `install` by default, so the backend sets
23
+ # itself up on first session. Set
24
+ # AGENT_WALLET_AUTO_BOOTSTRAP=0 to opt out and
25
+ # get only a soft hint to run /wallet-setup.
26
+
27
+ MODE=${1:-install}
28
+
29
+ PACKAGE_SPEC=${AGENT_WALLET_BOOTSTRAP_PACKAGE:-"@agentlayer.tech/wallet@latest"}
30
+ OPENCLAW_HOME=${OPENCLAW_HOME:-"$HOME/.openclaw"}
31
+ RUNTIME_CURRENT="$OPENCLAW_HOME/agent-wallet-runtime/current"
32
+
33
+ # Resolve this script's plugin root with physical paths (pwd -P), the same way
34
+ # run_mcp.sh does, so a marketplace symlink does not break the "../../../codex"
35
+ # sibling math below.
36
+ SCRIPT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd -P)
37
+ PLUGIN_ROOT=$(CDPATH= cd -- "$SCRIPT_DIR/.." && pwd -P)
38
+
39
+ log() {
40
+ printf '%s\n' "$*" >&2
41
+ }
42
+
43
+ # Locate server.py exactly like run_mcp.sh: the plugin's own copy, then the codex
44
+ # sibling in a repo checkout, then the installed runtime package. Keeping this in
45
+ # lock-step with run_mcp.sh is what avoids a false "not installed" verdict in a
46
+ # dev checkout, where run_mcp.sh resolves the codex sibling but this script used
47
+ # to look only at the runtime path.
48
+ resolve_server() {
49
+ if [ -f "$PLUGIN_ROOT/server.py" ]; then
50
+ printf '%s' "$PLUGIN_ROOT/server.py"
51
+ elif [ -f "$PLUGIN_ROOT/../../../codex/plugins/agent-wallet/server.py" ]; then
52
+ printf '%s' "$PLUGIN_ROOT/../../../codex/plugins/agent-wallet/server.py"
53
+ elif [ -f "$RUNTIME_CURRENT/codex/plugins/agent-wallet/server.py" ]; then
54
+ printf '%s' "$RUNTIME_CURRENT/codex/plugins/agent-wallet/server.py"
55
+ else
56
+ printf '%s' ''
57
+ fi
58
+ }
59
+
60
+ resolve_python() {
61
+ if [ -n "${AGENT_WALLET_PYTHON:-}" ]; then
62
+ printf '%s' "$AGENT_WALLET_PYTHON"
63
+ elif [ -x "$RUNTIME_CURRENT/agent-wallet/.venv/bin/python" ]; then
64
+ printf '%s' "$RUNTIME_CURRENT/agent-wallet/.venv/bin/python"
65
+ elif [ -x "$RUNTIME_CURRENT/agent-wallet/.runtime-venv/bin/python" ]; then
66
+ printf '%s' "$RUNTIME_CURRENT/agent-wallet/.runtime-venv/bin/python"
67
+ elif command -v python3 >/dev/null 2>&1; then
68
+ printf '%s' python3
69
+ else
70
+ printf '%s' ''
71
+ fi
72
+ }
73
+
74
+ # Readiness proxy: the resolved server.py exists and parses (ast.parse, no
75
+ # bytecode written) — the same shallow check run_mcp.sh and `doctor` run before
76
+ # exec. It deliberately does NOT verify that the venv/dependencies are installed:
77
+ # importing or running the server would be too slow and flaky for a SessionStart
78
+ # hook and risks false-negative reinstall loops. run_mcp.sh surfaces any missing
79
+ # dependency with a clear error at first tool use, so this stays in lock-step
80
+ # with what actually runs the server rather than being stricter than it.
81
+ backend_ready() {
82
+ server=$(resolve_server)
83
+ [ -n "$server" ] || return 1
84
+ py=$(resolve_python)
85
+ [ -n "$py" ] || return 1
86
+ "$py" -c 'import sys, ast; ast.parse(open(sys.argv[1], encoding="utf-8").read())' "$server" 2>/dev/null
87
+ }
88
+
89
+ if backend_ready; then
90
+ [ "$MODE" = "check" ] || log "AgentLayer wallet backend already installed (server: $(resolve_server))."
91
+ exit 0
92
+ fi
93
+
94
+ if [ "$MODE" = "check" ]; then
95
+ log "AgentLayer wallet backend is not installed yet."
96
+ log "Run /wallet-setup inside Claude Code to install it."
97
+ exit 1
98
+ fi
99
+
100
+ # --- install path -----------------------------------------------------------
101
+
102
+ if ! command -v npx >/dev/null 2>&1; then
103
+ log "Cannot install the AgentLayer wallet backend: npx (Node.js) was not found on PATH."
104
+ log "Install Node.js 18+ and re-run /wallet-setup, or run manually:"
105
+ log " npx $PACKAGE_SPEC install --yes"
106
+ exit 1
107
+ fi
108
+
109
+ if ! command -v python3 >/dev/null 2>&1; then
110
+ log "Warning: python3 was not found on PATH. The installer needs Python >= 3.10 with venv."
111
+ log "Install Python and re-run /wallet-setup if the install below fails."
112
+ fi
113
+
114
+ log "Installing the AgentLayer wallet backend runtime via npm (this may take a minute)…"
115
+ log " -> npx $PACKAGE_SPEC install --yes"
116
+ if ! npx -y "$PACKAGE_SPEC" install --yes; then
117
+ log "Backend install failed. Ensure Node.js 18+ and Python >= 3.10 (with venv) are installed, then re-run /wallet-setup."
118
+ exit 1
119
+ fi
120
+
121
+ # Re-pin the Claude Code cache copies so run_mcp.sh resolves OPENCLAW_HOME and the
122
+ # freshly installed runtime correctly (pinClaudeCacheCopies / marketplace wiring).
123
+ # --skip-enable: the plugin is already registered via the marketplace, so we only
124
+ # want the file pinning, not another `claude plugin install`.
125
+ log "Wiring the Claude Code bridge to the installed runtime…"
126
+ npx -y "$PACKAGE_SPEC" claude-code install --yes --skip-enable || \
127
+ log "Note: could not re-pin the Claude Code bridge automatically; it will still resolve the default runtime."
128
+
129
+ if backend_ready; then
130
+ log "Done. The AgentLayer wallet backend is installed."
131
+ log "Restart Claude Code (or reload the agent-wallet plugin) to activate the wallet tools."
132
+ exit 0
133
+ fi
134
+
135
+ log "Install completed but the backend did not verify. Run: npx $PACKAGE_SPEC doctor --deep"
136
+ exit 1
@@ -27,7 +27,7 @@ elif [ -f "$CODEX_SERVER" ]; then
27
27
  elif [ -f "$RUNTIME_CODEX_DIR/server.py" ]; then
28
28
  SERVER_PY=$(CDPATH= cd -- "$RUNTIME_CODEX_DIR" && pwd -P)/server.py
29
29
  else
30
- printf '{"error":"agent-wallet server.py not found in plugin, codex sibling, or runtime package.","fix":"npx @agentlayer.tech/wallet install --yes"}\n' >&2
30
+ printf '{"error":"agent-wallet backend not installed yet (server.py not found in plugin, codex sibling, or runtime package).","fix":"Run /wallet-setup inside Claude Code, or: npx @agentlayer.tech/wallet install --yes"}\n' >&2
31
31
  exit 1
32
32
  fi
33
33
 
@@ -58,4 +58,9 @@ PY
58
58
  exit 1
59
59
  fi
60
60
 
61
+ # Tag anonymous telemetry with this frontend so adoption can be split per host
62
+ # (claude-code / codex / hermes / openclaw). An explicit override still wins.
63
+ : "${AGENT_WALLET_HOST:=claude-code}"
64
+ export AGENT_WALLET_HOST
65
+
61
66
  exec "$PYTHON_BIN" "$SERVER_PY"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agent-wallet",
3
- "version": "0.1.37",
3
+ "version": "0.1.44",
4
4
  "description": "Codex plugin bridge for the AgentLayer wallet runtime.",
5
5
  "author": {
6
6
  "name": "AgentLayer"
@@ -39,4 +39,9 @@ PY
39
39
  exit 1
40
40
  fi
41
41
 
42
+ # Tag anonymous telemetry with this frontend so adoption can be split per host
43
+ # (claude-code / codex / hermes / openclaw). An explicit override still wins.
44
+ : "${AGENT_WALLET_HOST:=codex}"
45
+ export AGENT_WALLET_HOST
46
+
42
47
  exec "$PYTHON_BIN" "$PLUGIN_ROOT/server.py"
@@ -1,5 +1,5 @@
1
1
  name: agent-wallet
2
- version: 0.1.37
2
+ version: 0.1.44
3
3
  description: Thin Hermes Agent bridge to the existing AgentLayer/OpenClaw wallet backend
4
4
  provides_tools:
5
5
  - agent_wallet_tools
@@ -319,6 +319,9 @@ def _cli_env(package_root: Path) -> dict[str, str]:
319
319
  env = dict(os.environ)
320
320
  prior = env.get("PYTHONPATH", "")
321
321
  env["PYTHONPATH"] = str(package_root) if not prior else f"{package_root}{os.pathsep}{prior}"
322
+ # Tag anonymous telemetry with this frontend so adoption can be split per
323
+ # host (claude-code / codex / hermes / openclaw). An explicit override wins.
324
+ env.setdefault("AGENT_WALLET_HOST", "hermes")
322
325
  if not env.get("AGENT_WALLET_BOOT_KEY"):
323
326
  key_file = env.get("AGENT_WALLET_BOOT_KEY_FILE", "").strip()
324
327
  if key_file:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agentlayer.tech/wallet",
3
- "version": "0.1.37",
3
+ "version": "0.1.44",
4
4
  "description": "NPM installer for the OpenClaw Agent Wallet local runtime.",
5
5
  "type": "module",
6
6
  "repository": {
@@ -46,6 +46,7 @@
46
46
  "agent-wallet/pyproject.toml",
47
47
  ".openclaw/AGENTS.md",
48
48
  ".openclaw/extensions/agent-wallet/",
49
+ ".claude-plugin/marketplace.json",
49
50
  "codex/plugins/agent-wallet/",
50
51
  "claude-code/plugins/agent-wallet/",
51
52
  "hermes/plugins/agent_wallet/",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wdk-btc-wallet",
3
- "version": "0.1.37",
3
+ "version": "0.1.44",
4
4
  "private": true,
5
5
  "type": "module",
6
6
  "description": "Separate BTC-only wallet service built on Tether WDK.",
@@ -20,6 +20,8 @@ Current scope:
20
20
  - fetch fee-rate suggestions
21
21
  - fetch read-only Velora swap quotes for supported mainnet ERC-20 and native ETH pairs
22
22
  - execute Velora ERC-20 and native ETH swaps on supported mainnet networks through the local wallet account
23
+ - fetch read-only Uniswap Trading API swap quotes (CLASSIC routing) for native ETH and ERC-20 pairs on ethereum/base
24
+ - execute Uniswap Trading API swaps (native ETH and ERC-20 inputs) with Permit2 EIP-712 signing for ERC-20 inputs
23
25
  - fetch Aave V3 account data on supported mainnet networks
24
26
  - fetch Aave V3 reserve catalog on supported mainnet networks
25
27
  - fetch Aave V3 per-reserve user positions on supported mainnet networks
@@ -101,6 +103,8 @@ The active network is persistent and can be switched without changing code.
101
103
  - `POST /v1/evm/aave/repay/send`
102
104
  - `POST /v1/evm/swap/quote`
103
105
  - `POST /v1/evm/swap/send`
106
+ - `POST /v1/evm/uniswap/swap/quote`
107
+ - `POST /v1/evm/uniswap/swap/send`
104
108
  - `POST /v1/evm/transfer/quote`
105
109
  - `POST /v1/evm/transfer/send`
106
110
  - `POST /v1/evm/token-transfer/quote`
@@ -158,6 +162,26 @@ Environment variables:
158
162
  - `WDK_EVM_SEPOLIA_RPC_URL`
159
163
  - `WDK_EVM_BASE_RPC_URL`
160
164
  - `WDK_EVM_BASE_SEPOLIA_RPC_URL`
165
+ - `UNISWAP_API_KEY`
166
+ - `UNISWAP_TRADING_API_BASE_URL`
167
+ - `UNISWAP_ROUTER_VERSION`
168
+ - `UNISWAP_DEFAULT_SLIPPAGE_BPS`
169
+
170
+ Swap providers:
171
+
172
+ - the runtime exposes three independent swap surfaces: Velora (`/v1/evm/swap/*`),
173
+ LI.FI cross-chain (`/v1/evm/lifi/*`), and Uniswap Trading API
174
+ (`/v1/evm/uniswap/swap/*`) — always keep more than one route available
175
+ - Uniswap Trading API support is limited to `ethereum` and `base`, `EXACT_INPUT`,
176
+ and CLASSIC routing only; non-CLASSIC quotes (UniswapX Dutch/Priority) are rejected
177
+ - native ETH inputs need no approval or signature; ERC-20 inputs are pulled via
178
+ Permit2 (`0x000000000022D473030F116dDEE9F6B43aC78BA3`) and require a per-swap
179
+ Permit2 EIP-712 signature produced locally by the wallet account
180
+ - the `/swap` response `to` address is checked against a pinned Universal Router
181
+ allow-list before broadcast, and every swap is simulated first
182
+ - `UNISWAP_API_KEY` is required for the Uniswap routes; it identifies the
183
+ integrator (this service), not an end user — swaps are scoped per request by the
184
+ active wallet address, so a single key never mixes users
161
185
 
162
186
  Gateway mode:
163
187
 
@@ -179,6 +203,7 @@ Local security note:
179
203
  - seed reveal is password-gated and separate from normal agent operations
180
204
  - Velora swap support is currently limited to `ethereum` and `base` ERC-20 and native ETH pairs
181
205
  - the underlying WDK Velora package is still beta; test swap execution carefully before relying on it
206
+ - Uniswap Trading API swaps perform a Permit2-scoped ERC-20 approval for ERC-20 inputs; if a send fails after approval, the service attempts to restore the original allowance
182
207
  - Aave V3 support is currently limited to `ethereum` and `base`
183
208
  - Aave `supply` and `repay` may perform pool-scoped ERC-20 approvals; if a send fails after approval, the service attempts to restore the original allowance
184
209
  - Aave delegated `onBehalfOf` operations and third-party withdraw destinations are intentionally not exposed in this runtime
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wdk-evm-wallet",
3
- "version": "0.1.37",
3
+ "version": "0.1.44",
4
4
  "private": true,
5
5
  "type": "module",
6
6
  "description": "Separate EVM wallet service built on Tether WDK.",
@@ -11,7 +11,9 @@
11
11
  "check": "node --check src/server.js && node --check src/wdk_evm_wallet.js && node --check src/config.js && node --check src/json.js && node --check src/local_vault.js && node --check src/network_state.js",
12
12
  "test:swap-runtime": "node --test --test-concurrency=1 tests/smoke_swap_runtime.mjs",
13
13
  "test:aave-runtime": "node --test --test-concurrency=1 tests/smoke_aave_runtime.mjs",
14
- "test:lido-runtime": "node --test --test-concurrency=1 tests/smoke_lido_runtime.mjs"
14
+ "test:lido-runtime": "node --test --test-concurrency=1 tests/smoke_lido_runtime.mjs",
15
+ "test:uniswap-runtime": "node --test --test-concurrency=1 tests/smoke_uniswap_runtime.mjs",
16
+ "test:unit": "node --test tests/unit_uniswap_helpers.mjs"
15
17
  },
16
18
  "dependencies": {
17
19
  "@tetherto/wdk": "^1.0.0-beta.9",
@@ -9,6 +9,23 @@ const DEFAULTS = {
9
9
  network: "sepolia",
10
10
  unlockTimeoutSeconds: 0,
11
11
  };
12
+
13
+ // Read this package's version once at module load. Surfaced via /health so the
14
+ // host autostart can detect a stale long-running daemon (old code in memory)
15
+ // after a release and restart it. Falls back to "0.0.0" if package.json is
16
+ // unreadable — a value that will simply look "stale" and trigger a restart.
17
+ function readPackageVersion() {
18
+ try {
19
+ const pkgUrl = new URL("../package.json", import.meta.url);
20
+ const pkg = JSON.parse(fs.readFileSync(pkgUrl, "utf8"));
21
+ const version = String(pkg.version || "").trim();
22
+ return version || "0.0.0";
23
+ } catch {
24
+ return "0.0.0";
25
+ }
26
+ }
27
+
28
+ const PACKAGE_VERSION = readPackageVersion();
12
29
  const DEFAULT_PROVIDER_GATEWAY_URL = "https://agent-layer-production.up.railway.app";
13
30
  const ENFORCED_GATEWAY_MAINNETS = new Set(["ethereum", "base"]);
14
31
 
@@ -257,6 +274,20 @@ export function loadConfig(env = process.env) {
257
274
  },
258
275
  };
259
276
 
277
+ // Route Uniswap Trading API calls through the provider-gateway by default so the
278
+ // Uniswap key lives only in the gateway (never in a per-release wallet .env). When
279
+ // the base URL points at the gateway we authenticate with the gateway bearer and
280
+ // let the gateway inject x-api-key; an explicit non-gateway base URL falls back to
281
+ // the legacy direct mode (local UNISWAP_API_KEY + x-api-key).
282
+ const gatewayBaseTrimmed = String(providerGatewayUrl || "").replace(/\/+$/, "");
283
+ const uniswapTradingApiBaseUrl =
284
+ String(env.UNISWAP_TRADING_API_BASE_URL ?? "").trim() ||
285
+ (gatewayBaseTrimmed
286
+ ? `${gatewayBaseTrimmed}/v1/evm/uniswap`
287
+ : "https://trade-api.gateway.uniswap.org/v1");
288
+ const uniswapViaGateway =
289
+ Boolean(gatewayBaseTrimmed) && uniswapTradingApiBaseUrl.startsWith(gatewayBaseTrimmed);
290
+
260
291
  return {
261
292
  host,
262
293
  port: parseInteger(env.PORT, DEFAULTS.port, "PORT"),
@@ -284,6 +315,22 @@ export function loadConfig(env = process.env) {
284
315
  lifiDefaultDenyBridges: String(env.LIFI_DEFAULT_DENY_BRIDGES ?? "").trim() || "mayan",
285
316
  lidoApiBaseUrl: String(env.LIDO_API_BASE_URL ?? "").trim() || "https://eth-api.lido.fi/v1",
286
317
  lidoReferralAddress: String(env.LIDO_REFERRAL_ADDRESS ?? "").trim(),
318
+ uniswapTradingApiBaseUrl,
319
+ uniswapViaGateway,
320
+ providerGatewayToken,
321
+ uniswapApiKey: String(env.UNISWAP_API_KEY ?? "").trim(),
322
+ uniswapRouterVersion: String(env.UNISWAP_ROUTER_VERSION ?? "").trim() || "2.0",
323
+ // 300 bps (3%) default mirrors the Solana swap-intent floor. Active markets
324
+ // (Base re-prices every block) drift during the multi-step preview -> approval
325
+ // -> execute window; a 0.5% floor rejected ordinary drift. The quote
326
+ // fingerprint binds only the swap intent, so this floor — not an exact-output
327
+ // pin — is the real slippage guard. Override per-call or via env for tight swaps.
328
+ uniswapDefaultSlippageBps: parseInteger(
329
+ env.UNISWAP_DEFAULT_SLIPPAGE_BPS,
330
+ 300,
331
+ "UNISWAP_DEFAULT_SLIPPAGE_BPS"
332
+ ),
333
+ version: PACKAGE_VERSION,
287
334
  networkProfiles,
288
335
  };
289
336
  }
@@ -40,7 +40,10 @@ function normalizeErrorCode(errorCode, pathname, message) {
40
40
  code === "aave_cleanup_failed" ||
41
41
  code === "token_transfer_failed" ||
42
42
  code === "fee_limit_exceeded" ||
43
- code === "token_read_failed"
43
+ code === "token_read_failed" ||
44
+ code === "uniswap_api_key_missing" ||
45
+ code === "uniswap_unsupported_route" ||
46
+ code === "uniswap_unexpected_router"
44
47
  ) {
45
48
  return code;
46
49
  }
@@ -129,10 +132,17 @@ function errorStatusCode(errorCode, fallback = 400) {
129
132
  errorCode === "aave_fee_unavailable" ||
130
133
  errorCode === "aave_cleanup_failed" ||
131
134
  errorCode === "token_transfer_failed" ||
132
- errorCode === "fee_limit_exceeded"
135
+ errorCode === "fee_limit_exceeded" ||
136
+ errorCode === "uniswap_api_key_missing"
133
137
  ) {
134
138
  return 400;
135
139
  }
140
+ if (errorCode === "uniswap_unsupported_route") {
141
+ return 422;
142
+ }
143
+ if (errorCode === "uniswap_unexpected_router") {
144
+ return 502;
145
+ }
136
146
  if (errorCode === "token_read_failed") {
137
147
  return 502;
138
148
  }
@@ -272,7 +282,7 @@ async function handleRequest(request, response) {
272
282
  return sendJson(response, 200, {
273
283
  ok: true,
274
284
  service: "wdk-evm-wallet",
275
- version: "0.1.0",
285
+ version: config.version,
276
286
  wallet: "evm",
277
287
  network: runtimeConfig.network,
278
288
  chainId: runtimeConfig.chainId,
@@ -531,6 +541,18 @@ async function handleRequest(request, response) {
531
541
  return sendJson(response, 200, { ok: true, data });
532
542
  }
533
543
 
544
+ if (method === "POST" && url.pathname === "/v1/evm/uniswap/swap/quote") {
545
+ const body = await withResolvedNetwork(await withResolvedSeedOrAddress(await readJsonBody(request)));
546
+ const data = await service.quoteUniswapSwap(body);
547
+ return sendJson(response, 200, { ok: true, data });
548
+ }
549
+
550
+ if (method === "POST" && url.pathname === "/v1/evm/uniswap/swap/send") {
551
+ const body = await withResolvedNetwork(await withResolvedSeed(await readJsonBody(request)));
552
+ const data = await service.sendUniswapSwap(body);
553
+ return sendJson(response, 200, { ok: true, data });
554
+ }
555
+
534
556
  if (method === "POST" && url.pathname === "/v1/evm/transfer/quote") {
535
557
  const body = await withResolvedNetwork(await withResolvedSeed(await readJsonBody(request)));
536
558
  const data = await service.quoteNativeTransfer(body);