@agentlayer.tech/wallet 0.1.76 → 0.1.84
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/README.md +4 -4
- package/.openclaw/extensions/agent-wallet/dist/index.js +57 -19
- package/.openclaw/extensions/agent-wallet/index.ts +57 -19
- package/.openclaw/extensions/agent-wallet/openclaw.plugin.json +1 -1
- package/.openclaw/extensions/agent-wallet/package.json +1 -1
- package/CHANGELOG.md +19 -0
- package/VERSION +1 -1
- package/agent-wallet/agent_wallet/__init__.py +1 -1
- package/agent-wallet/agent_wallet/autonomous_permissions.py +2 -2
- package/agent-wallet/agent_wallet/autonomous_policy.py +1 -1
- package/agent-wallet/agent_wallet/config.py +3 -3
- package/agent-wallet/agent_wallet/openclaw_adapter.py +28 -26
- package/agent-wallet/agent_wallet/providers/evm_portfolio.py +2 -2
- package/agent-wallet/agent_wallet/wallet_layer/wdk_evm.py +25 -10
- package/agent-wallet/openclaw.plugin.json +1 -1
- package/agent-wallet/pyproject.toml +1 -1
- package/agent-wallet/skills/wallet-operator/SKILL.md +4 -1
- 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 +13 -10
- package/codex/plugins/agent-wallet/skills/wallet-operator/SKILL.md +1 -1
- 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/.env.example +6 -2
- package/wdk-evm-wallet/package.json +5 -2
- package/wdk-evm-wallet/src/config.js +48 -2
- package/wdk-evm-wallet/src/network_state.js +5 -2
- package/wdk-evm-wallet/src/wdk_evm_wallet.js +570 -63
|
@@ -39,7 +39,7 @@ If the preview result includes a confirmation_summary or mainnet_warning, surfac
|
|
|
39
39
|
Never bypass the approval token requirement for wallet writes.
|
|
40
40
|
In OpenClaw, switch between Solana, EVM, and Bitcoin wallets with set_wallet_backend.
|
|
41
41
|
The plugin config is the startup default, not something to edit during a normal conversation.
|
|
42
|
-
For EVM wallets, switch between Ethereum and
|
|
42
|
+
For EVM wallets, switch between Ethereum, Base, and Robinhood with set_evm_network or by passing the
|
|
43
43
|
network argument to EVM tools. Do not edit code, plugin config, or environment variables
|
|
44
44
|
just to switch the active EVM network.
|
|
45
45
|
""".strip()
|
|
@@ -74,7 +74,7 @@ class OpenClawWalletAdapter:
|
|
|
74
74
|
if chain == "bitcoin":
|
|
75
75
|
return normalized == "bitcoin"
|
|
76
76
|
if chain == "evm":
|
|
77
|
-
return normalized in {"ethereum", "base", "eip155:1", "eip155:8453"}
|
|
77
|
+
return normalized in {"ethereum", "base", "robinhood", "eip155:1", "eip155:8453", "eip155:4663"}
|
|
78
78
|
if chain == "solana":
|
|
79
79
|
return normalized in {"mainnet", "solana:5eykt4usfv8p8njdtrepy1vzkqzkvdp"}
|
|
80
80
|
return normalized == "mainnet"
|
|
@@ -101,9 +101,9 @@ class OpenClawWalletAdapter:
|
|
|
101
101
|
}
|
|
102
102
|
network = aliases.get(network, network)
|
|
103
103
|
if network in {"sepolia", "base-sepolia", "base_sepolia"}:
|
|
104
|
-
raise WalletBackendError("EVM testnets are no longer supported. Use ethereum or
|
|
105
|
-
if network not in {"ethereum", "base"}:
|
|
106
|
-
raise WalletBackendError("EVM network must be 'ethereum' or '
|
|
104
|
+
raise WalletBackendError("EVM testnets are no longer supported. Use ethereum, base, or robinhood.")
|
|
105
|
+
if network not in {"ethereum", "base", "robinhood"}:
|
|
106
|
+
raise WalletBackendError("EVM network must be 'ethereum', 'base', or 'robinhood'.")
|
|
107
107
|
return network
|
|
108
108
|
|
|
109
109
|
def _resolve_backend_for_args(self, args: dict[str, Any]) -> AgentWalletBackend:
|
|
@@ -386,7 +386,7 @@ class OpenClawWalletAdapter:
|
|
|
386
386
|
elif scope == autonomous_permissions.DEFI_TOOLS_SCOPE:
|
|
387
387
|
if network not in autonomous_permissions.DEFI_TOOLS_NETWORKS:
|
|
388
388
|
raise WalletBackendError(
|
|
389
|
-
"Autonomous DeFi permission only applies on ethereum or
|
|
389
|
+
"Autonomous DeFi permission only applies on ethereum, base, or robinhood. "
|
|
390
390
|
"Use set_evm_network or the network parameter to select a supported network."
|
|
391
391
|
)
|
|
392
392
|
if not autonomous_permissions.is_defi_tools_approved():
|
|
@@ -1178,7 +1178,7 @@ class OpenClawWalletAdapter:
|
|
|
1178
1178
|
"properties": {
|
|
1179
1179
|
"network": {
|
|
1180
1180
|
"type": "string",
|
|
1181
|
-
"enum": ["ethereum", "base"],
|
|
1181
|
+
"enum": ["ethereum", "base", "robinhood"],
|
|
1182
1182
|
"description": "Optional EVM network override for this request.",
|
|
1183
1183
|
},
|
|
1184
1184
|
},
|
|
@@ -1195,7 +1195,7 @@ class OpenClawWalletAdapter:
|
|
|
1195
1195
|
"properties": {
|
|
1196
1196
|
"network": {
|
|
1197
1197
|
"type": "string",
|
|
1198
|
-
"enum": ["ethereum", "base"],
|
|
1198
|
+
"enum": ["ethereum", "base", "robinhood"],
|
|
1199
1199
|
"description": "Optional EVM network override for this request.",
|
|
1200
1200
|
},
|
|
1201
1201
|
},
|
|
@@ -1220,7 +1220,7 @@ class OpenClawWalletAdapter:
|
|
|
1220
1220
|
},
|
|
1221
1221
|
"network": {
|
|
1222
1222
|
"type": "string",
|
|
1223
|
-
"enum": ["ethereum", "base"],
|
|
1223
|
+
"enum": ["ethereum", "base", "robinhood"],
|
|
1224
1224
|
"description": "Optional EVM network override for this request.",
|
|
1225
1225
|
},
|
|
1226
1226
|
},
|
|
@@ -1301,7 +1301,7 @@ class OpenClawWalletAdapter:
|
|
|
1301
1301
|
"properties": {
|
|
1302
1302
|
"network": {
|
|
1303
1303
|
"type": "string",
|
|
1304
|
-
"enum": ["ethereum", "base"],
|
|
1304
|
+
"enum": ["ethereum", "base", "robinhood"],
|
|
1305
1305
|
"description": "Optional EVM network override for this request.",
|
|
1306
1306
|
},
|
|
1307
1307
|
},
|
|
@@ -1314,7 +1314,7 @@ class OpenClawWalletAdapter:
|
|
|
1314
1314
|
name="set_evm_network",
|
|
1315
1315
|
description=(
|
|
1316
1316
|
"Select the active EVM network for subsequent wallet tool calls in this "
|
|
1317
|
-
"runtime session. Use this to switch between ethereum and
|
|
1317
|
+
"runtime session. Use this to switch between ethereum, base, and robinhood instead "
|
|
1318
1318
|
"of editing code or plugin configuration."
|
|
1319
1319
|
),
|
|
1320
1320
|
input_schema={
|
|
@@ -1322,7 +1322,7 @@ class OpenClawWalletAdapter:
|
|
|
1322
1322
|
"properties": {
|
|
1323
1323
|
"network": {
|
|
1324
1324
|
"type": "string",
|
|
1325
|
-
"enum": ["ethereum", "base"],
|
|
1325
|
+
"enum": ["ethereum", "base", "robinhood"],
|
|
1326
1326
|
"description": "EVM network to make active for subsequent calls.",
|
|
1327
1327
|
},
|
|
1328
1328
|
},
|
|
@@ -1344,7 +1344,7 @@ class OpenClawWalletAdapter:
|
|
|
1344
1344
|
},
|
|
1345
1345
|
"network": {
|
|
1346
1346
|
"type": "string",
|
|
1347
|
-
"enum": ["ethereum", "base"],
|
|
1347
|
+
"enum": ["ethereum", "base", "robinhood"],
|
|
1348
1348
|
"description": "Optional EVM network override for this request.",
|
|
1349
1349
|
},
|
|
1350
1350
|
},
|
|
@@ -1366,7 +1366,7 @@ class OpenClawWalletAdapter:
|
|
|
1366
1366
|
},
|
|
1367
1367
|
"network": {
|
|
1368
1368
|
"type": "string",
|
|
1369
|
-
"enum": ["ethereum", "base"],
|
|
1369
|
+
"enum": ["ethereum", "base", "robinhood"],
|
|
1370
1370
|
"description": "Optional EVM network override for this request.",
|
|
1371
1371
|
},
|
|
1372
1372
|
},
|
|
@@ -1384,7 +1384,7 @@ class OpenClawWalletAdapter:
|
|
|
1384
1384
|
"properties": {
|
|
1385
1385
|
"network": {
|
|
1386
1386
|
"type": "string",
|
|
1387
|
-
"enum": ["ethereum", "base"],
|
|
1387
|
+
"enum": ["ethereum", "base", "robinhood"],
|
|
1388
1388
|
"description": "Optional EVM network override for this request.",
|
|
1389
1389
|
},
|
|
1390
1390
|
},
|
|
@@ -1405,7 +1405,7 @@ class OpenClawWalletAdapter:
|
|
|
1405
1405
|
},
|
|
1406
1406
|
"network": {
|
|
1407
1407
|
"type": "string",
|
|
1408
|
-
"enum": ["ethereum", "base"],
|
|
1408
|
+
"enum": ["ethereum", "base", "robinhood"],
|
|
1409
1409
|
"description": "Optional EVM network override for this request.",
|
|
1410
1410
|
},
|
|
1411
1411
|
},
|
|
@@ -1438,7 +1438,7 @@ class OpenClawWalletAdapter:
|
|
|
1438
1438
|
"approval_token": {"type": "string"},
|
|
1439
1439
|
"network": {
|
|
1440
1440
|
"type": "string",
|
|
1441
|
-
"enum": ["ethereum", "base"],
|
|
1441
|
+
"enum": ["ethereum", "base", "robinhood"],
|
|
1442
1442
|
"description": "Optional EVM network override for this request.",
|
|
1443
1443
|
},
|
|
1444
1444
|
},
|
|
@@ -1473,7 +1473,7 @@ class OpenClawWalletAdapter:
|
|
|
1473
1473
|
"approval_token": {"type": "string"},
|
|
1474
1474
|
"network": {
|
|
1475
1475
|
"type": "string",
|
|
1476
|
-
"enum": ["ethereum", "base"],
|
|
1476
|
+
"enum": ["ethereum", "base", "robinhood"],
|
|
1477
1477
|
"description": "Optional EVM network override for this request.",
|
|
1478
1478
|
},
|
|
1479
1479
|
},
|
|
@@ -2174,8 +2174,9 @@ class OpenClawWalletAdapter:
|
|
|
2174
2174
|
AgentToolSpec(
|
|
2175
2175
|
name="get_uniswap_swap_quote",
|
|
2176
2176
|
description=(
|
|
2177
|
-
"Get a read-only Uniswap
|
|
2178
|
-
"on ethereum or
|
|
2177
|
+
"Get a read-only Uniswap execution preview for an ERC-20 or native ETH swap "
|
|
2178
|
+
"on ethereum, base, or robinhood. Supported paths are CLASSIC, UniswapX orders, "
|
|
2179
|
+
"and canonical ETH↔WETH wrap/unwrap. Supports Permit2 where required. "
|
|
2179
2180
|
"This does not approve, sign, or execute a swap."
|
|
2180
2181
|
),
|
|
2181
2182
|
input_schema={
|
|
@@ -2199,8 +2200,8 @@ class OpenClawWalletAdapter:
|
|
|
2199
2200
|
},
|
|
2200
2201
|
"network": {
|
|
2201
2202
|
"type": "string",
|
|
2202
|
-
"enum": ["ethereum", "base"],
|
|
2203
|
-
"description": "EVM network. Uniswap Trading API supports ethereum and
|
|
2203
|
+
"enum": ["ethereum", "base", "robinhood"],
|
|
2204
|
+
"description": "EVM network. Uniswap Trading API supports ethereum, base, and robinhood.",
|
|
2204
2205
|
},
|
|
2205
2206
|
},
|
|
2206
2207
|
"required": ["token_in", "token_out", "amount_in_raw"],
|
|
@@ -2215,8 +2216,9 @@ class OpenClawWalletAdapter:
|
|
|
2215
2216
|
AgentToolSpec(
|
|
2216
2217
|
name="swap_evm_uniswap_tokens",
|
|
2217
2218
|
description=(
|
|
2218
|
-
"Preview, prepare, or execute
|
|
2219
|
-
"
|
|
2219
|
+
"Preview, prepare, or execute a supported Uniswap path on ethereum, base, or robinhood: "
|
|
2220
|
+
"CLASSIC, UniswapX orders, or canonical ETH↔WETH wrap/unwrap. ERC-20 paths may use Permit2 "
|
|
2221
|
+
"EIP-712 or an UniswapX order signature. "
|
|
2220
2222
|
"Prepare returns an execution plan only. Execute requires a host-issued approval token bound to the previewed operation "
|
|
2221
2223
|
"unless the high-trust autonomous permission group is enabled."
|
|
2222
2224
|
),
|
|
@@ -2248,8 +2250,8 @@ class OpenClawWalletAdapter:
|
|
|
2248
2250
|
"approval_token": {"type": "string"},
|
|
2249
2251
|
"network": {
|
|
2250
2252
|
"type": "string",
|
|
2251
|
-
"enum": ["ethereum", "base"],
|
|
2252
|
-
"description": "EVM network. Uniswap Trading API supports ethereum and
|
|
2253
|
+
"enum": ["ethereum", "base", "robinhood"],
|
|
2254
|
+
"description": "EVM network. Uniswap Trading API supports ethereum, base, and robinhood.",
|
|
2253
2255
|
},
|
|
2254
2256
|
},
|
|
2255
2257
|
"required": ["token_in", "token_out", "amount_in_raw", "mode", "purpose"],
|
|
@@ -142,7 +142,7 @@ _PRICE_CACHE: dict[str, tuple[float, float]] = {}
|
|
|
142
142
|
|
|
143
143
|
def _normalize_network(network: str) -> str:
|
|
144
144
|
normalized = str(network or "").strip().lower()
|
|
145
|
-
if normalized not in {"ethereum", "base"}:
|
|
145
|
+
if normalized not in {"ethereum", "base", "robinhood"}:
|
|
146
146
|
raise ProviderError("evm-portfolio", f"Unsupported EVM portfolio network: {network}")
|
|
147
147
|
return normalized
|
|
148
148
|
|
|
@@ -207,7 +207,7 @@ async def _gateway_rpc_call(network: str, method: str, params: list[Any]) -> dic
|
|
|
207
207
|
if not gateway_url:
|
|
208
208
|
raise ProviderError(
|
|
209
209
|
"evm-portfolio",
|
|
210
|
-
"Provider gateway URL is required for EVM portfolio lookup on ethereum/base.",
|
|
210
|
+
"Provider gateway URL is required for EVM portfolio lookup on ethereum/base/robinhood.",
|
|
211
211
|
)
|
|
212
212
|
try:
|
|
213
213
|
response = await client.post(
|
|
@@ -668,8 +668,8 @@ class WdkEvmLocalWalletBackend(AgentWalletBackend):
|
|
|
668
668
|
"configured_network": self.network,
|
|
669
669
|
"service_active_network": str(data.get("activeNetwork") or "").strip() or None,
|
|
670
670
|
"available_networks": sorted(str(key) for key in profiles.keys()),
|
|
671
|
-
"agent_selectable_networks": ["ethereum", "base"],
|
|
672
|
-
"swap_supported_networks": ["ethereum", "base"],
|
|
671
|
+
"agent_selectable_networks": ["ethereum", "base", "robinhood"],
|
|
672
|
+
"swap_supported_networks": ["ethereum", "base", "robinhood"],
|
|
673
673
|
"network_profiles": {
|
|
674
674
|
str(network): {
|
|
675
675
|
**dict(profile),
|
|
@@ -1835,6 +1835,7 @@ class WdkEvmLocalWalletBackend(AgentWalletBackend):
|
|
|
1835
1835
|
"slippage_bps": int(data.get("slippageBps")) if data.get("slippageBps") is not None else None,
|
|
1836
1836
|
"protocol": str(data.get("protocol") or "uniswap"),
|
|
1837
1837
|
"routing": str(data.get("routing") or "CLASSIC"),
|
|
1838
|
+
"execution_kind": str(data.get("executionKind") or "classic"),
|
|
1838
1839
|
"permit_required": bool(data.get("permitRequired")),
|
|
1839
1840
|
"allowance": _normalize_swap_allowance(data.get("allowance")),
|
|
1840
1841
|
"router": str(data.get("router") or "").strip() or None,
|
|
@@ -1845,7 +1846,7 @@ class WdkEvmLocalWalletBackend(AgentWalletBackend):
|
|
|
1845
1846
|
"token_in_metadata": _normalize_token_metadata(data.get("tokenInMetadata"), token_in),
|
|
1846
1847
|
"token_out_metadata": _normalize_token_metadata(data.get("tokenOutMetadata"), token_out),
|
|
1847
1848
|
"chain_id": int(data.get("chainId") or 0),
|
|
1848
|
-
"source": "wdk-evm-wallet",
|
|
1849
|
+
"source": str(data.get("source") or "wdk-evm-wallet"),
|
|
1849
1850
|
}
|
|
1850
1851
|
|
|
1851
1852
|
async def get_uniswap_swap_quote(
|
|
@@ -1893,6 +1894,19 @@ class WdkEvmLocalWalletBackend(AgentWalletBackend):
|
|
|
1893
1894
|
body["slippageBps"] = slippage_bps
|
|
1894
1895
|
data = await self.client.post("/v1/evm/uniswap/swap/quote", body)
|
|
1895
1896
|
payload = self._normalize_uniswap_quote_payload(data, token_in, token_out, amount_in_raw)
|
|
1897
|
+
prepared_transaction = data.get("swapTransaction")
|
|
1898
|
+
if isinstance(prepared_transaction, dict):
|
|
1899
|
+
swap_transaction = {
|
|
1900
|
+
"to": str(prepared_transaction.get("to") or "").strip() or payload["router"],
|
|
1901
|
+
"value": str(prepared_transaction.get("value") or "0"),
|
|
1902
|
+
"data_hash": str(prepared_transaction.get("dataHash") or "").strip() or None,
|
|
1903
|
+
}
|
|
1904
|
+
else:
|
|
1905
|
+
swap_transaction = {
|
|
1906
|
+
"to": payload["router"],
|
|
1907
|
+
"value": "0",
|
|
1908
|
+
"data_hash": None,
|
|
1909
|
+
}
|
|
1896
1910
|
return {
|
|
1897
1911
|
**payload,
|
|
1898
1912
|
"asset_type": "evm-uniswap-swap",
|
|
@@ -1903,11 +1917,7 @@ class WdkEvmLocalWalletBackend(AgentWalletBackend):
|
|
|
1903
1917
|
"input_amount_ui": payload["amount_in_ui"],
|
|
1904
1918
|
"swap_provider": payload["protocol"],
|
|
1905
1919
|
"route_plan": None,
|
|
1906
|
-
"swap_transaction":
|
|
1907
|
-
"to": payload["router"],
|
|
1908
|
-
"value": "0",
|
|
1909
|
-
"data_hash": None,
|
|
1910
|
-
},
|
|
1920
|
+
"swap_transaction": swap_transaction,
|
|
1911
1921
|
}
|
|
1912
1922
|
|
|
1913
1923
|
async def send_uniswap_swap(
|
|
@@ -1938,6 +1948,7 @@ class WdkEvmLocalWalletBackend(AgentWalletBackend):
|
|
|
1938
1948
|
body["minimumTokenOutAmount"] = minimum_output_amount_raw.strip()
|
|
1939
1949
|
data = await self.client.post("/v1/evm/uniswap/swap/send", body)
|
|
1940
1950
|
result = dict(data.get("result") or {})
|
|
1951
|
+
order_id = str(result.get("orderId") or "").strip() or None
|
|
1941
1952
|
return {
|
|
1942
1953
|
"chain": self.chain,
|
|
1943
1954
|
"network": self.network,
|
|
@@ -1955,6 +1966,7 @@ class WdkEvmLocalWalletBackend(AgentWalletBackend):
|
|
|
1955
1966
|
"minimum_output_amount_raw": str(data.get("minimumOutputAmountRaw")) if data.get("minimumOutputAmountRaw") is not None else None,
|
|
1956
1967
|
"swap_provider": str(data.get("protocol") or "uniswap"),
|
|
1957
1968
|
"routing": str(data.get("routing") or "CLASSIC"),
|
|
1969
|
+
"execution_kind": str(data.get("executionKind") or "classic"),
|
|
1958
1970
|
"quote_fingerprint": str(data.get("quoteFingerprint") or "").strip() or None,
|
|
1959
1971
|
"router": str(data.get("router") or "").strip() or None,
|
|
1960
1972
|
"allowance": _normalize_swap_allowance(data.get("allowance")),
|
|
@@ -1967,13 +1979,16 @@ class WdkEvmLocalWalletBackend(AgentWalletBackend):
|
|
|
1967
1979
|
"token_in_metadata": _normalize_token_metadata(data.get("tokenInMetadata"), token_in),
|
|
1968
1980
|
"token_out_metadata": _normalize_token_metadata(data.get("tokenOutMetadata"), token_out),
|
|
1969
1981
|
"hash": result.get("hash"),
|
|
1982
|
+
"order_id": order_id,
|
|
1983
|
+
"order_status": str(result.get("orderStatus") or "").strip() or None,
|
|
1970
1984
|
"approve_hash": result.get("approveHash"),
|
|
1971
1985
|
"reset_allowance_hash": result.get("resetAllowanceHash"),
|
|
1972
1986
|
"result": result,
|
|
1973
1987
|
"chain_id": int(data.get("chainId") or 0),
|
|
1974
|
-
"broadcasted":
|
|
1988
|
+
"broadcasted": bool(result.get("hash")),
|
|
1989
|
+
"order_submitted": order_id is not None,
|
|
1975
1990
|
"confirmed": False,
|
|
1976
|
-
"source": "wdk-evm-wallet",
|
|
1991
|
+
"source": str(data.get("source") or "wdk-evm-wallet"),
|
|
1977
1992
|
}
|
|
1978
1993
|
|
|
1979
1994
|
async def preview_evm_lifi_cross_chain_swap(
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"id": "agent-wallet",
|
|
3
3
|
"name": "Agent Wallet",
|
|
4
4
|
"description": "Plugin-friendly wallet backend for OpenClaw agents with safe wallet tools and runtime instructions across Solana, local BTC, and local EVM.",
|
|
5
|
-
"version": "0.1.
|
|
5
|
+
"version": "0.1.84",
|
|
6
6
|
"skills": ["skills/wallet-operator"],
|
|
7
7
|
"configSchema": {
|
|
8
8
|
"type": "object",
|
|
@@ -23,7 +23,7 @@ Use this skill before calling OpenClaw wallet tools. It is the routing guide for
|
|
|
23
23
|
## Provider Map
|
|
24
24
|
|
|
25
25
|
- Solana same-chain swap: `swap_solana_tokens` via Jupiter.
|
|
26
|
-
- EVM same-chain swap: `swap_evm_tokens` via Velora, ERC-20 to ERC-20 on `ethereum` or `base`.
|
|
26
|
+
- EVM same-chain swap: `swap_evm_tokens` via Velora, ERC-20 to ERC-20 on `ethereum` or `base`; `swap_evm_uniswap_tokens` via Uniswap Trading API on `ethereum`, `base`, or `robinhood`.
|
|
27
27
|
- Cross-chain Solana -> EVM: `swap_solana_lifi_cross_chain_tokens` via LI.FI.
|
|
28
28
|
- Cross-chain EVM -> EVM/Solana: `swap_evm_lifi_cross_chain_tokens` via LI.FI.
|
|
29
29
|
- SOL/SPL transfers: `transfer_sol`, `transfer_spl_token`.
|
|
@@ -97,6 +97,9 @@ Use this skill before calling OpenClaw wallet tools. It is the routing guide for
|
|
|
97
97
|
- EVM same-chain Velora swap: `swap_evm_tokens`
|
|
98
98
|
- Params: `token_in`, `token_out`, `amount_in_raw` base-unit string, `mode`, `purpose`, optional `network`.
|
|
99
99
|
- Current intended path is ERC-20 to ERC-20 on `ethereum` or `base`.
|
|
100
|
+
- EVM same-chain Uniswap swap: `swap_evm_uniswap_tokens`
|
|
101
|
+
- Params: `token_in`, `token_out`, `amount_in_raw` base-unit string, `mode`, `purpose`, optional `network`.
|
|
102
|
+
- Supported paths on `ethereum`, `base`, and `robinhood` are CLASSIC, UniswapX orders, and canonical ETH↔WETH wrap/unwrap; other routes fail closed before signing.
|
|
100
103
|
- EVM swap quote only: `get_evm_swap_quote`
|
|
101
104
|
- Params: `token_in`, `token_out`, `amount_in_raw`, optional `network`.
|
|
102
105
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agent-wallet",
|
|
3
3
|
"displayName": "Agent Wallet",
|
|
4
|
-
"version": "0.1.
|
|
4
|
+
"version": "0.1.84",
|
|
5
5
|
"description": "Claude Code bridge for the existing AgentLayer wallet runtime. Connects to Solana, Bitcoin, and EVM wallets without creating a new one.",
|
|
6
6
|
"author": {
|
|
7
7
|
"name": "AgentLayer"
|
|
@@ -370,6 +370,7 @@ def _normalize_wallet_backend(value: Any) -> str:
|
|
|
370
370
|
"ethereum": "wdk_evm_local",
|
|
371
371
|
"eth": "wdk_evm_local",
|
|
372
372
|
"base": "wdk_evm_local",
|
|
373
|
+
"robinhood": "wdk_evm_local",
|
|
373
374
|
"wdk_evm_local": "wdk_evm_local",
|
|
374
375
|
"wdk-evm-local": "wdk_evm_local",
|
|
375
376
|
"evm_local": "wdk_evm_local",
|
|
@@ -383,7 +384,7 @@ def _normalize_wallet_backend(value: Any) -> str:
|
|
|
383
384
|
}
|
|
384
385
|
backend = aliases.get(normalized, normalized)
|
|
385
386
|
if backend not in BACKENDS:
|
|
386
|
-
raise RuntimeError("Wallet backend must be solana, evm, base,
|
|
387
|
+
raise RuntimeError("Wallet backend must be solana, evm, ethereum, base, robinhood, btc, or bitcoin.")
|
|
387
388
|
return backend
|
|
388
389
|
|
|
389
390
|
|
|
@@ -409,9 +410,9 @@ def _normalize_evm_network(value: Any) -> str:
|
|
|
409
410
|
def _normalize_selectable_evm_network(value: Any) -> str:
|
|
410
411
|
network = _normalize_evm_network(value)
|
|
411
412
|
if network in {"sepolia", "base-sepolia", "base_sepolia"}:
|
|
412
|
-
raise RuntimeError("EVM testnets are no longer supported. Use ethereum or
|
|
413
|
-
if network not in {"ethereum", "base"}:
|
|
414
|
-
raise RuntimeError("EVM network must be 'ethereum' or '
|
|
413
|
+
raise RuntimeError("EVM testnets are no longer supported. Use ethereum, base, or robinhood.")
|
|
414
|
+
if network not in {"ethereum", "base", "robinhood"}:
|
|
415
|
+
raise RuntimeError("EVM network must be 'ethereum', 'base', or 'robinhood'.")
|
|
415
416
|
return network
|
|
416
417
|
|
|
417
418
|
|
|
@@ -419,6 +420,8 @@ def _implied_evm_network_from_backend_alias(value: Any) -> str | None:
|
|
|
419
420
|
normalized = str(value or "").strip().lower()
|
|
420
421
|
if normalized in {"base", "base-mainnet"}:
|
|
421
422
|
return "base"
|
|
423
|
+
if normalized == "robinhood":
|
|
424
|
+
return "robinhood"
|
|
422
425
|
if normalized in {"ethereum", "eth", "mainnet", "eth-mainnet"}:
|
|
423
426
|
return "ethereum"
|
|
424
427
|
return None
|
|
@@ -471,7 +474,7 @@ def _default_backend() -> str:
|
|
|
471
474
|
|
|
472
475
|
def _default_evm_network() -> str | None:
|
|
473
476
|
configured = _normalize_evm_network(os.getenv("WDK_EVM_NETWORK"))
|
|
474
|
-
if configured in {"ethereum", "base"}:
|
|
477
|
+
if configured in {"ethereum", "base", "robinhood"}:
|
|
475
478
|
return configured
|
|
476
479
|
return _configured_network_for_backend("wdk_evm_local")
|
|
477
480
|
|
|
@@ -1205,11 +1208,11 @@ def _manual_tool_definitions() -> list[dict[str, Any]]:
|
|
|
1205
1208
|
"properties": {
|
|
1206
1209
|
"backend": {
|
|
1207
1210
|
"type": "string",
|
|
1208
|
-
"description": "solana, evm, base,
|
|
1211
|
+
"description": "solana, evm, ethereum, base, robinhood, btc, or bitcoin.",
|
|
1209
1212
|
},
|
|
1210
1213
|
"network": {
|
|
1211
1214
|
"type": "string",
|
|
1212
|
-
"description": "Optional network override. Use base or
|
|
1215
|
+
"description": "Optional network override. Use ethereum, base, or robinhood for EVM.",
|
|
1213
1216
|
},
|
|
1214
1217
|
"address": {
|
|
1215
1218
|
"type": "string",
|
|
@@ -1244,7 +1247,7 @@ def _manual_tool_definitions() -> list[dict[str, Any]]:
|
|
|
1244
1247
|
"properties": {
|
|
1245
1248
|
"backend": {
|
|
1246
1249
|
"type": "string",
|
|
1247
|
-
"description": "solana, evm, base,
|
|
1250
|
+
"description": "solana, evm, ethereum, base, robinhood, btc, or bitcoin.",
|
|
1248
1251
|
},
|
|
1249
1252
|
"wallet": {
|
|
1250
1253
|
"type": "string",
|
|
@@ -1262,14 +1265,14 @@ def _manual_tool_definitions() -> list[dict[str, Any]]:
|
|
|
1262
1265
|
{
|
|
1263
1266
|
"name": "set_evm_network",
|
|
1264
1267
|
"description": (
|
|
1265
|
-
"Set the active EVM network for this Codex MCP session to ethereum or
|
|
1268
|
+
"Set the active EVM network for this Codex MCP session to ethereum, base, or robinhood."
|
|
1266
1269
|
),
|
|
1267
1270
|
"input_schema": {
|
|
1268
1271
|
"type": "object",
|
|
1269
1272
|
"properties": {
|
|
1270
1273
|
"network": {
|
|
1271
1274
|
"type": "string",
|
|
1272
|
-
"description": "ethereum or
|
|
1275
|
+
"description": "ethereum, base, or robinhood.",
|
|
1273
1276
|
}
|
|
1274
1277
|
},
|
|
1275
1278
|
"required": ["network"],
|
|
@@ -16,4 +16,4 @@ Rules:
|
|
|
16
16
|
- On mainnet, restate the network, asset, amount, and destination before execute.
|
|
17
17
|
- Do not ask the user for `approval_token`. The bridge manages approval binding internally.
|
|
18
18
|
- If approval context is missing or stale, repeat preview instead of improvising.
|
|
19
|
-
- Use `set_wallet_backend` to switch between Solana, EVM, and Bitcoin wallets within a session, and `set_evm_network` to pick ethereum or
|
|
19
|
+
- Use `set_wallet_backend` to switch between Solana, EVM, and Bitcoin wallets within a session, and `set_evm_network` to pick ethereum, base, or robinhood.
|
package/package.json
CHANGED
|
@@ -10,14 +10,15 @@ WDK_EVM_RPC_PROVIDER_MODE=gateway
|
|
|
10
10
|
WDK_EVM_RPC_GATEWAY_PROVIDER=alchemy
|
|
11
11
|
PROVIDER_GATEWAY_URL=https://agent-layer-production.up.railway.app
|
|
12
12
|
PROVIDER_GATEWAY_BEARER_TOKEN=
|
|
13
|
-
# Mainnet ethereum/base are forced through provider-gateway -> Alchemy.
|
|
13
|
+
# Mainnet ethereum/base/robinhood are forced through provider-gateway -> Alchemy.
|
|
14
14
|
# Direct per-network URLs below are only relevant for testnet-style paths.
|
|
15
15
|
WDK_EVM_ETHEREUM_RPC_URL=
|
|
16
16
|
WDK_EVM_SEPOLIA_RPC_URL=https://sepolia.drpc.org
|
|
17
17
|
WDK_EVM_BASE_RPC_URL=
|
|
18
18
|
WDK_EVM_BASE_SEPOLIA_RPC_URL=https://sepolia.base.org
|
|
19
|
+
WDK_EVM_ROBINHOOD_RPC_URL=
|
|
19
20
|
MORPHO_API_BASE_URL=https://api.morpho.org/graphql
|
|
20
|
-
# Uniswap Trading API swap provider (ethereum/base, CLASSIC routing).
|
|
21
|
+
# Uniswap Trading API swap provider (ethereum/base/robinhood, CLASSIC routing).
|
|
21
22
|
# Uniswap Trading API. By default the daemon routes quotes/swaps through the
|
|
22
23
|
# provider gateway (PROVIDER_GATEWAY_URL/v1/evm/uniswap), which holds the Uniswap
|
|
23
24
|
# key and authenticates via the gateway bearer — so no UNISWAP_API_KEY is needed
|
|
@@ -26,4 +27,7 @@ MORPHO_API_BASE_URL=https://api.morpho.org/graphql
|
|
|
26
27
|
UNISWAP_API_KEY=
|
|
27
28
|
# UNISWAP_TRADING_API_BASE_URL= # defaults to PROVIDER_GATEWAY_URL/v1/evm/uniswap
|
|
28
29
|
UNISWAP_ROUTER_VERSION=2.0
|
|
30
|
+
# Optional per-network override; each value must exist in the wallet's reviewed
|
|
31
|
+
# execution profile and match the provider-gateway's per-chain configuration.
|
|
32
|
+
UNISWAP_ROUTER_VERSION_BY_NETWORK={"ethereum":"2.0","base":"2.0","robinhood":"2.0"}
|
|
29
33
|
UNISWAP_DEFAULT_SLIPPAGE_BPS=300
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "wdk-evm-wallet",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.84",
|
|
4
4
|
"private": true,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"description": "Separate EVM wallet service built on Tether WDK.",
|
|
@@ -15,7 +15,10 @@
|
|
|
15
15
|
"test:lido-runtime": "node --test --test-concurrency=1 tests/smoke_lido_runtime.mjs",
|
|
16
16
|
"test:uniswap-runtime": "node --test --test-concurrency=1 tests/smoke_uniswap_runtime.mjs",
|
|
17
17
|
"test:unit": "node --test tests/unit_uniswap_helpers.mjs",
|
|
18
|
-
"test:identity": "node --test tests/unit_instance_identity.mjs"
|
|
18
|
+
"test:identity": "node --test tests/unit_instance_identity.mjs",
|
|
19
|
+
"test:network-config": "node --test tests/unit_network_config.mjs",
|
|
20
|
+
"test:network-state": "node --test tests/unit_network_state.mjs",
|
|
21
|
+
"test:wallet-network": "node --test tests/unit_wdk_wallet_network.mjs"
|
|
19
22
|
},
|
|
20
23
|
"dependencies": {
|
|
21
24
|
"@morpho-org/morpho-sdk": "^3.2.0",
|
|
@@ -27,7 +27,7 @@ function readPackageVersion() {
|
|
|
27
27
|
|
|
28
28
|
const PACKAGE_VERSION = readPackageVersion();
|
|
29
29
|
const DEFAULT_PROVIDER_GATEWAY_URL = "https://agent-layer-production.up.railway.app";
|
|
30
|
-
const ENFORCED_GATEWAY_MAINNETS = new Set(["ethereum", "base"]);
|
|
30
|
+
const ENFORCED_GATEWAY_MAINNETS = new Set(["ethereum", "base", "robinhood"]);
|
|
31
31
|
|
|
32
32
|
const DEFAULT_NETWORK_PROFILES = {
|
|
33
33
|
ethereum: {
|
|
@@ -50,6 +50,11 @@ const DEFAULT_NETWORK_PROFILES = {
|
|
|
50
50
|
providerUrl: "https://sepolia.base.org",
|
|
51
51
|
nativeSymbol: "ETH",
|
|
52
52
|
},
|
|
53
|
+
robinhood: {
|
|
54
|
+
chainId: 4663,
|
|
55
|
+
providerUrl: "https://rpc.mainnet.chain.robinhood.com",
|
|
56
|
+
nativeSymbol: "ETH",
|
|
57
|
+
},
|
|
53
58
|
};
|
|
54
59
|
|
|
55
60
|
const SUPPORTED_GATEWAY_PROVIDERS = new Set(["auto", "shared", "alchemy"]);
|
|
@@ -179,10 +184,40 @@ function normalizeNetworkKey(value) {
|
|
|
179
184
|
"eth-mainnet": "ethereum",
|
|
180
185
|
"base-mainnet": "base",
|
|
181
186
|
base_sepolia: "base-sepolia",
|
|
187
|
+
"robinhood-mainnet": "robinhood",
|
|
182
188
|
};
|
|
183
189
|
return aliases[normalized] || normalized;
|
|
184
190
|
}
|
|
185
191
|
|
|
192
|
+
function parseUniswapRouterVersionsByNetwork(value) {
|
|
193
|
+
const raw = String(value ?? "").trim();
|
|
194
|
+
if (!raw) {
|
|
195
|
+
return {};
|
|
196
|
+
}
|
|
197
|
+
let parsed;
|
|
198
|
+
try {
|
|
199
|
+
parsed = JSON.parse(raw);
|
|
200
|
+
} catch {
|
|
201
|
+
throw new Error("UNISWAP_ROUTER_VERSION_BY_NETWORK must be a JSON object.");
|
|
202
|
+
}
|
|
203
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
204
|
+
throw new Error("UNISWAP_ROUTER_VERSION_BY_NETWORK must be a JSON object.");
|
|
205
|
+
}
|
|
206
|
+
const normalized = {};
|
|
207
|
+
for (const [network, version] of Object.entries(parsed)) {
|
|
208
|
+
const networkKey = normalizeNetworkKey(network);
|
|
209
|
+
if (!Object.hasOwn(DEFAULT_NETWORK_PROFILES, networkKey)) {
|
|
210
|
+
throw new Error(`UNISWAP_ROUTER_VERSION_BY_NETWORK contains unsupported network '${network}'.`);
|
|
211
|
+
}
|
|
212
|
+
const normalizedVersion = String(version ?? "").trim();
|
|
213
|
+
if (!normalizedVersion) {
|
|
214
|
+
throw new Error(`UNISWAP_ROUTER_VERSION_BY_NETWORK.${network} must be a non-empty string.`);
|
|
215
|
+
}
|
|
216
|
+
normalized[networkKey] = normalizedVersion;
|
|
217
|
+
}
|
|
218
|
+
return normalized;
|
|
219
|
+
}
|
|
220
|
+
|
|
186
221
|
function joinUrl(base, pathname) {
|
|
187
222
|
const normalizedBase = String(base || "").trim();
|
|
188
223
|
if (!normalizedBase) {
|
|
@@ -211,7 +246,7 @@ export function loadConfig(env = process.env) {
|
|
|
211
246
|
const network = normalizeNetworkKey(env.WDK_EVM_NETWORK ?? DEFAULTS.network) || DEFAULTS.network;
|
|
212
247
|
if (!Object.hasOwn(DEFAULT_NETWORK_PROFILES, network)) {
|
|
213
248
|
throw new Error(
|
|
214
|
-
"WDK_EVM_NETWORK must be one of: ethereum, sepolia, base, base-sepolia."
|
|
249
|
+
"WDK_EVM_NETWORK must be one of: ethereum, sepolia, base, base-sepolia, robinhood."
|
|
215
250
|
);
|
|
216
251
|
}
|
|
217
252
|
|
|
@@ -295,6 +330,14 @@ export function loadConfig(env = process.env) {
|
|
|
295
330
|
DEFAULT_NETWORK_PROFILES["base-sepolia"].providerUrl
|
|
296
331
|
),
|
|
297
332
|
},
|
|
333
|
+
robinhood: {
|
|
334
|
+
...DEFAULT_NETWORK_PROFILES.robinhood,
|
|
335
|
+
providerUrl: resolveProviderUrl(
|
|
336
|
+
"robinhood",
|
|
337
|
+
env.WDK_EVM_ROBINHOOD_RPC_URL,
|
|
338
|
+
DEFAULT_NETWORK_PROFILES.robinhood.providerUrl
|
|
339
|
+
),
|
|
340
|
+
},
|
|
298
341
|
};
|
|
299
342
|
|
|
300
343
|
// Route Uniswap Trading API calls through the provider-gateway by default so the
|
|
@@ -346,6 +389,9 @@ export function loadConfig(env = process.env) {
|
|
|
346
389
|
providerGatewayToken,
|
|
347
390
|
uniswapApiKey: String(env.UNISWAP_API_KEY ?? "").trim(),
|
|
348
391
|
uniswapRouterVersion: String(env.UNISWAP_ROUTER_VERSION ?? "").trim() || "2.0",
|
|
392
|
+
uniswapRouterVersionsByNetwork: parseUniswapRouterVersionsByNetwork(
|
|
393
|
+
env.UNISWAP_ROUTER_VERSION_BY_NETWORK
|
|
394
|
+
),
|
|
349
395
|
// 300 bps (3%) default mirrors the Solana swap-intent floor. Active markets
|
|
350
396
|
// (Base re-prices every block) drift during the multi-step preview -> approval
|
|
351
397
|
// -> execute window; a 0.5% floor rejected ordinary drift. The quote
|
|
@@ -11,10 +11,13 @@ function assertValidNetwork(network, fieldName = "network") {
|
|
|
11
11
|
"eth-mainnet": "ethereum",
|
|
12
12
|
"base-mainnet": "base",
|
|
13
13
|
base_sepolia: "base-sepolia",
|
|
14
|
+
"robinhood-mainnet": "robinhood",
|
|
14
15
|
};
|
|
15
16
|
const effective = aliases[normalized] || normalized;
|
|
16
|
-
if (!["ethereum", "sepolia", "base", "base-sepolia"].includes(effective)) {
|
|
17
|
-
throw new Error(
|
|
17
|
+
if (!["ethereum", "sepolia", "base", "base-sepolia", "robinhood"].includes(effective)) {
|
|
18
|
+
throw new Error(
|
|
19
|
+
`${fieldName} must be one of: ethereum, sepolia, base, base-sepolia, robinhood.`
|
|
20
|
+
);
|
|
18
21
|
}
|
|
19
22
|
return effective;
|
|
20
23
|
}
|