@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.
- package/.claude-plugin/marketplace.json +17 -0
- package/.openclaw/extensions/agent-wallet/openclaw.plugin.json +1 -1
- package/.openclaw/extensions/agent-wallet/package.json +1 -1
- package/CHANGELOG.md +37 -0
- package/README.md +14 -0
- package/VERSION +1 -1
- package/agent-wallet/agent_wallet/__init__.py +1 -1
- package/agent-wallet/agent_wallet/evm_user_wallets.py +114 -6
- package/agent-wallet/agent_wallet/openclaw_adapter.py +331 -0
- package/agent-wallet/agent_wallet/openclaw_cli.py +22 -0
- package/agent-wallet/agent_wallet/providers/wdk_evm_local.py +2 -0
- package/agent-wallet/agent_wallet/telemetry.py +306 -0
- package/agent-wallet/agent_wallet/wallet_layer/base.py +32 -0
- package/agent-wallet/agent_wallet/wallet_layer/wdk_evm.py +156 -0
- package/agent-wallet/openclaw.plugin.json +1 -1
- package/agent-wallet/pyproject.toml +1 -1
- package/agent-wallet/scripts/bootstrap_openclaw_evm.py +20 -2
- package/claude-code/plugins/agent-wallet/.claude-plugin/plugin.json +1 -1
- package/claude-code/plugins/agent-wallet/README.md +30 -1
- package/claude-code/plugins/agent-wallet/commands/wallet-setup.md +24 -0
- package/claude-code/plugins/agent-wallet/hooks/hooks.json +16 -0
- package/claude-code/plugins/agent-wallet/scripts/bootstrap_backend.sh +136 -0
- package/claude-code/plugins/agent-wallet/scripts/run_mcp.sh +6 -1
- package/codex/plugins/agent-wallet/.codex-plugin/plugin.json +1 -1
- package/codex/plugins/agent-wallet/scripts/run_mcp.sh +5 -0
- package/hermes/plugins/agent_wallet/plugin.yaml +1 -1
- package/hermes/plugins/agent_wallet/tools.py +3 -0
- package/package.json +2 -1
- package/wdk-btc-wallet/package.json +1 -1
- package/wdk-evm-wallet/README.md +25 -0
- package/wdk-evm-wallet/package.json +4 -2
- package/wdk-evm-wallet/src/config.js +47 -0
- package/wdk-evm-wallet/src/server.js +25 -3
- 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"
|
|
@@ -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"
|
|
@@ -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.
|
|
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/",
|
package/wdk-evm-wallet/README.md
CHANGED
|
@@ -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.
|
|
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:
|
|
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);
|