@agentlayer.tech/wallet 0.1.88 → 0.1.90
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/dist/index.js +5 -5
- package/.openclaw/extensions/agent-wallet/index.ts +5 -5
- package/.openclaw/extensions/agent-wallet/openclaw.plugin.json +2 -1
- package/.openclaw/extensions/agent-wallet/package.json +1 -1
- package/VERSION +1 -1
- package/agent-wallet/agent_wallet/__init__.py +1 -1
- package/agent-wallet/agent_wallet/autonomous_permissions.py +43 -1
- package/agent-wallet/agent_wallet/openclaw_adapter.py +696 -375
- package/agent-wallet/agent_wallet/providers/dexscreener.py +145 -0
- package/agent-wallet/agent_wallet/providers/x402.py +84 -0
- package/agent-wallet/agent_wallet/wallet_layer/base.py +12 -0
- package/agent-wallet/agent_wallet/wallet_layer/wdk_evm.py +38 -1
- package/agent-wallet/openclaw.plugin.json +1 -1
- package/agent-wallet/pyproject.toml +1 -1
- package/claude-code/plugins/agent-wallet/.claude-plugin/plugin.json +1 -1
- package/claude-code/plugins/agent-wallet/README.md +3 -1
- package/claude-code/plugins/agent-wallet/commands/agentlayer-autonomous-approve.md +7 -8
- package/claude-code/plugins/agent-wallet/commands/agentlayer-autonomous-revoke.md +2 -2
- package/claude-code/plugins/agent-wallet/commands/wallet-base.md +38 -15
- package/codex/plugins/agent-wallet/.codex-plugin/plugin.json +1 -1
- package/codex/plugins/agent-wallet/README.md +11 -6
- package/codex/plugins/agent-wallet/skills/wallet-base/SKILL.md +56 -0
- 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/package.json +1 -1
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
"""DexScreener provider for onchain token/pair discovery (price, volume, liquidity).
|
|
2
|
+
|
|
3
|
+
Public API, no key required: https://docs.dexscreener.com/api/reference. Used to
|
|
4
|
+
answer "which pairs exist for this token/ticker" the way app.uniswap.org's own
|
|
5
|
+
search box does, since the Uniswap Trading API itself only exposes quote/order/swap
|
|
6
|
+
for a pair you already know, not a search-by-name/address endpoint.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
import time
|
|
12
|
+
from typing import Any
|
|
13
|
+
|
|
14
|
+
from agent_wallet.exceptions import ProviderError
|
|
15
|
+
from agent_wallet.http_client import get_client
|
|
16
|
+
|
|
17
|
+
DEXSCREENER_BASE_URL = "https://api.dexscreener.com"
|
|
18
|
+
|
|
19
|
+
# DexScreener's own responses are served with `cache-control: public, max-age=30`;
|
|
20
|
+
# mirroring that TTL avoids redundant lookups within a session without serving
|
|
21
|
+
# data staler than DexScreener itself would.
|
|
22
|
+
_CACHE_TTL_SECONDS = 30
|
|
23
|
+
_CACHE_MAX_ENTRIES = 128
|
|
24
|
+
_cache: dict[str, tuple[float, list[dict[str, Any]]]] = {}
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def _cache_get(key: str) -> list[dict[str, Any]] | None:
|
|
28
|
+
entry = _cache.get(key)
|
|
29
|
+
if entry is None:
|
|
30
|
+
return None
|
|
31
|
+
stored_at, pairs = entry
|
|
32
|
+
if time.monotonic() - stored_at > _CACHE_TTL_SECONDS:
|
|
33
|
+
_cache.pop(key, None)
|
|
34
|
+
return None
|
|
35
|
+
return pairs
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def _cache_set(key: str, pairs: list[dict[str, Any]]) -> None:
|
|
39
|
+
if len(_cache) >= _CACHE_MAX_ENTRIES and key not in _cache:
|
|
40
|
+
oldest_key = min(_cache, key=lambda existing: _cache[existing][0])
|
|
41
|
+
_cache.pop(oldest_key, None)
|
|
42
|
+
_cache[key] = (time.monotonic(), pairs)
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
async def _get(path: str, *, params: dict[str, Any] | None = None) -> Any:
|
|
46
|
+
client = get_client()
|
|
47
|
+
response = await client.get(f"{DEXSCREENER_BASE_URL}{path}", params=params)
|
|
48
|
+
if response.status_code != 200:
|
|
49
|
+
raise ProviderError("dexscreener", f"HTTP {response.status_code}: {response.text[:300]}")
|
|
50
|
+
try:
|
|
51
|
+
return response.json()
|
|
52
|
+
except ValueError as exc:
|
|
53
|
+
raise ProviderError("dexscreener", "Unexpected non-JSON response from DexScreener.") from exc
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def _extract_pairs(data: Any) -> list[dict[str, Any]]:
|
|
57
|
+
if isinstance(data, dict):
|
|
58
|
+
pairs = data.get("pairs")
|
|
59
|
+
return pairs if isinstance(pairs, list) else []
|
|
60
|
+
if isinstance(data, list):
|
|
61
|
+
return [item for item in data if isinstance(item, dict)]
|
|
62
|
+
return []
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
async def search_pairs(query: str) -> list[dict[str, Any]]:
|
|
66
|
+
"""Free-text search across every chain/DEX DexScreener indexes."""
|
|
67
|
+
query = query.strip()
|
|
68
|
+
if not query:
|
|
69
|
+
raise ProviderError("dexscreener", "query must not be empty.")
|
|
70
|
+
cache_key = f"search:{query.lower()}"
|
|
71
|
+
cached = _cache_get(cache_key)
|
|
72
|
+
if cached is not None:
|
|
73
|
+
return cached
|
|
74
|
+
data = await _get("/latest/dex/search", params={"q": query})
|
|
75
|
+
pairs = _extract_pairs(data)
|
|
76
|
+
_cache_set(cache_key, pairs)
|
|
77
|
+
return pairs
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
async def get_pairs_for_token(*, chain: str, token_address: str) -> list[dict[str, Any]]:
|
|
81
|
+
"""All pairs for a single token address on one chain, across every DEX on it."""
|
|
82
|
+
chain = chain.strip().lower()
|
|
83
|
+
token_address = token_address.strip()
|
|
84
|
+
if not chain:
|
|
85
|
+
raise ProviderError("dexscreener", "chain must not be empty.")
|
|
86
|
+
if not token_address:
|
|
87
|
+
raise ProviderError("dexscreener", "token_address must not be empty.")
|
|
88
|
+
cache_key = f"token:{chain}:{token_address.lower()}"
|
|
89
|
+
cached = _cache_get(cache_key)
|
|
90
|
+
if cached is not None:
|
|
91
|
+
return cached
|
|
92
|
+
data = await _get(f"/tokens/v1/{chain}/{token_address}")
|
|
93
|
+
pairs = _extract_pairs(data)
|
|
94
|
+
_cache_set(cache_key, pairs)
|
|
95
|
+
return pairs
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def normalize_pair(pair: dict[str, Any]) -> dict[str, Any]:
|
|
99
|
+
"""Flatten a DexScreener pair payload into the shape handed back to the agent."""
|
|
100
|
+
base_token = pair.get("baseToken") or {}
|
|
101
|
+
quote_token = pair.get("quoteToken") or {}
|
|
102
|
+
volume = pair.get("volume") or {}
|
|
103
|
+
liquidity = pair.get("liquidity") or {}
|
|
104
|
+
price_change = pair.get("priceChange") or {}
|
|
105
|
+
txns = pair.get("txns") or {}
|
|
106
|
+
return {
|
|
107
|
+
"chain_id": pair.get("chainId"),
|
|
108
|
+
"dex_id": pair.get("dexId"),
|
|
109
|
+
"pair_address": pair.get("pairAddress"),
|
|
110
|
+
"url": pair.get("url"),
|
|
111
|
+
"labels": pair.get("labels") or [],
|
|
112
|
+
"base_token": {
|
|
113
|
+
"address": base_token.get("address"),
|
|
114
|
+
"name": base_token.get("name"),
|
|
115
|
+
"symbol": base_token.get("symbol"),
|
|
116
|
+
},
|
|
117
|
+
"quote_token": {
|
|
118
|
+
"address": quote_token.get("address"),
|
|
119
|
+
"name": quote_token.get("name"),
|
|
120
|
+
"symbol": quote_token.get("symbol"),
|
|
121
|
+
},
|
|
122
|
+
"price_usd": pair.get("priceUsd"),
|
|
123
|
+
"price_native": pair.get("priceNative"),
|
|
124
|
+
"price_change_pct": {
|
|
125
|
+
"m5": price_change.get("m5"),
|
|
126
|
+
"h1": price_change.get("h1"),
|
|
127
|
+
"h6": price_change.get("h6"),
|
|
128
|
+
"h24": price_change.get("h24"),
|
|
129
|
+
},
|
|
130
|
+
"volume_usd": {
|
|
131
|
+
"m5": volume.get("m5"),
|
|
132
|
+
"h1": volume.get("h1"),
|
|
133
|
+
"h6": volume.get("h6"),
|
|
134
|
+
"h24": volume.get("h24"),
|
|
135
|
+
},
|
|
136
|
+
"txns": {
|
|
137
|
+
window: {"buys": counts.get("buys"), "sells": counts.get("sells")}
|
|
138
|
+
for window, counts in txns.items()
|
|
139
|
+
if isinstance(counts, dict)
|
|
140
|
+
},
|
|
141
|
+
"liquidity_usd": liquidity.get("usd"),
|
|
142
|
+
"fdv_usd": pair.get("fdv"),
|
|
143
|
+
"market_cap_usd": pair.get("marketCap"),
|
|
144
|
+
"pair_created_at": pair.get("pairCreatedAt"),
|
|
145
|
+
}
|
|
@@ -1600,6 +1600,90 @@ def _reusable_approved_preview(
|
|
|
1600
1600
|
return dict(approved_preview)
|
|
1601
1601
|
|
|
1602
1602
|
|
|
1603
|
+
#: USD amount below which an x402 payment never requires host/session/
|
|
1604
|
+
#: permission approval, regardless of network (including mainnet) -- the
|
|
1605
|
+
#: same rationale as in-person card payments skipping a signature/PIN below
|
|
1606
|
+
#: a floor limit. Only ever applies when the payment asset is confidently
|
|
1607
|
+
#: identified as USDC (see _looks_like_usdc); any other asset always
|
|
1608
|
+
#: requires approval, since its USD value can't be determined here.
|
|
1609
|
+
DE_MINIMIS_USD_THRESHOLD = 2.0
|
|
1610
|
+
|
|
1611
|
+
|
|
1612
|
+
def de_minimis_usd_amount(preview: dict[str, Any]) -> float | None:
|
|
1613
|
+
"""Return *preview*'s payment amount in USD, or None if it isn't confidently USDC.
|
|
1614
|
+
|
|
1615
|
+
``x402_amount_display`` is only populated by normalize_payment_requirement
|
|
1616
|
+
when the asset is recognized as USDC (see _looks_like_usdc) -- for any
|
|
1617
|
+
other asset it's None, since its USD value is unknown here.
|
|
1618
|
+
"""
|
|
1619
|
+
amount_display = preview.get("x402_amount_display")
|
|
1620
|
+
if not isinstance(amount_display, str) or not amount_display.strip():
|
|
1621
|
+
return None
|
|
1622
|
+
try:
|
|
1623
|
+
return float(amount_display)
|
|
1624
|
+
except ValueError:
|
|
1625
|
+
return None
|
|
1626
|
+
|
|
1627
|
+
|
|
1628
|
+
def is_de_minimis_payment(
|
|
1629
|
+
preview: dict[str, Any],
|
|
1630
|
+
*,
|
|
1631
|
+
threshold_usd: float | None = None,
|
|
1632
|
+
) -> bool:
|
|
1633
|
+
"""Whether *preview*'s payment is small enough to skip approval entirely.
|
|
1634
|
+
|
|
1635
|
+
threshold_usd defaults to the current DE_MINIMIS_USD_THRESHOLD, read at
|
|
1636
|
+
call time (not bound at import time) so tests can monkeypatch the module
|
|
1637
|
+
attribute directly.
|
|
1638
|
+
"""
|
|
1639
|
+
usd_amount = de_minimis_usd_amount(preview)
|
|
1640
|
+
if usd_amount is None:
|
|
1641
|
+
return False
|
|
1642
|
+
effective_threshold = DE_MINIMIS_USD_THRESHOLD if threshold_usd is None else threshold_usd
|
|
1643
|
+
return usd_amount < effective_threshold
|
|
1644
|
+
|
|
1645
|
+
|
|
1646
|
+
async def resolve_payment_preview(
|
|
1647
|
+
*,
|
|
1648
|
+
backend: AgentWalletBackend,
|
|
1649
|
+
url: str,
|
|
1650
|
+
method: str = "GET",
|
|
1651
|
+
headers: dict[str, Any] | None = None,
|
|
1652
|
+
query: dict[str, Any] | None = None,
|
|
1653
|
+
json_body: Any | None = None,
|
|
1654
|
+
text_body: str | None = None,
|
|
1655
|
+
approved_preview: dict[str, Any] | None = None,
|
|
1656
|
+
) -> dict[str, Any]:
|
|
1657
|
+
"""Return the payment preview for this exact request.
|
|
1658
|
+
|
|
1659
|
+
Reuses *approved_preview* when it still matches (same fingerprint check
|
|
1660
|
+
``pay_and_fetch`` applies before deciding whether to skip a fresh probe),
|
|
1661
|
+
otherwise makes a fresh unpaid probe. Callers that need a summary to bind
|
|
1662
|
+
an approval token to -- before paying -- can call this instead of
|
|
1663
|
+
duplicating the reuse-or-probe logic ``pay_and_fetch`` already has.
|
|
1664
|
+
"""
|
|
1665
|
+
request = _build_request_metadata(
|
|
1666
|
+
url=url,
|
|
1667
|
+
method=method,
|
|
1668
|
+
headers=headers,
|
|
1669
|
+
query=query,
|
|
1670
|
+
json_body=json_body,
|
|
1671
|
+
text_body=text_body,
|
|
1672
|
+
)
|
|
1673
|
+
reused = _reusable_approved_preview(approved_preview, request=request)
|
|
1674
|
+
if reused is not None:
|
|
1675
|
+
return reused
|
|
1676
|
+
return await preview_request(
|
|
1677
|
+
backend=backend,
|
|
1678
|
+
url=url,
|
|
1679
|
+
method=method,
|
|
1680
|
+
headers=headers,
|
|
1681
|
+
query=query,
|
|
1682
|
+
json_body=json_body,
|
|
1683
|
+
text_body=text_body,
|
|
1684
|
+
)
|
|
1685
|
+
|
|
1686
|
+
|
|
1603
1687
|
async def pay_and_fetch(
|
|
1604
1688
|
*,
|
|
1605
1689
|
backend: AgentWalletBackend,
|
|
@@ -325,6 +325,18 @@ class AgentWalletBackend(ABC):
|
|
|
325
325
|
) -> dict[str, Any]:
|
|
326
326
|
raise WalletBackendError(f"{self.name} does not support Uniswap swap previews.")
|
|
327
327
|
|
|
328
|
+
async def search_uniswap_pairs(
|
|
329
|
+
self,
|
|
330
|
+
*,
|
|
331
|
+
query: str | None = None,
|
|
332
|
+
token_address: str | None = None,
|
|
333
|
+
chain: str | None = None,
|
|
334
|
+
dex_id: str | None = None,
|
|
335
|
+
all_chains: bool = False,
|
|
336
|
+
limit: int = 10,
|
|
337
|
+
) -> dict[str, Any]:
|
|
338
|
+
raise WalletBackendError(f"{self.name} does not support Uniswap pair search.")
|
|
339
|
+
|
|
328
340
|
async def send_uniswap_swap(
|
|
329
341
|
self,
|
|
330
342
|
*,
|
|
@@ -7,7 +7,7 @@ from typing import Any
|
|
|
7
7
|
|
|
8
8
|
from agent_wallet.config import normalize_evm_network
|
|
9
9
|
from agent_wallet.providers.evm_portfolio import build_portfolio_snapshot
|
|
10
|
-
from agent_wallet.providers import lifi
|
|
10
|
+
from agent_wallet.providers import dexscreener, lifi
|
|
11
11
|
from agent_wallet.providers.wdk_evm_local import WdkEvmLocalClient
|
|
12
12
|
from agent_wallet.wallet_layer.base import AgentWalletBackend, WalletBackendError, WalletCapabilities
|
|
13
13
|
|
|
@@ -1920,6 +1920,43 @@ class WdkEvmLocalWalletBackend(AgentWalletBackend):
|
|
|
1920
1920
|
"swap_transaction": swap_transaction,
|
|
1921
1921
|
}
|
|
1922
1922
|
|
|
1923
|
+
async def search_uniswap_pairs(
|
|
1924
|
+
self,
|
|
1925
|
+
*,
|
|
1926
|
+
query: str | None = None,
|
|
1927
|
+
token_address: str | None = None,
|
|
1928
|
+
chain: str | None = None,
|
|
1929
|
+
dex_id: str | None = None,
|
|
1930
|
+
all_chains: bool = False,
|
|
1931
|
+
limit: int = 10,
|
|
1932
|
+
) -> dict[str, Any]:
|
|
1933
|
+
query = query.strip() if isinstance(query, str) else None
|
|
1934
|
+
token_address = token_address.strip() if isinstance(token_address, str) else None
|
|
1935
|
+
if not query and not token_address:
|
|
1936
|
+
raise WalletBackendError("Either query or token_address is required.")
|
|
1937
|
+
resolved_chain = (chain or self.network).strip().lower()
|
|
1938
|
+
if token_address:
|
|
1939
|
+
pairs = await dexscreener.get_pairs_for_token(chain=resolved_chain, token_address=token_address)
|
|
1940
|
+
else:
|
|
1941
|
+
pairs = await dexscreener.search_pairs(query)
|
|
1942
|
+
if not all_chains:
|
|
1943
|
+
pairs = [p for p in pairs if str(p.get("chainId") or "").lower() == resolved_chain]
|
|
1944
|
+
if dex_id:
|
|
1945
|
+
wanted_dex = dex_id.strip().lower()
|
|
1946
|
+
pairs = [p for p in pairs if str(p.get("dexId") or "").lower() == wanted_dex]
|
|
1947
|
+
bounded_limit = max(1, min(int(limit or 10), 30))
|
|
1948
|
+
normalized = [dexscreener.normalize_pair(pair) for pair in pairs[:bounded_limit]]
|
|
1949
|
+
return {
|
|
1950
|
+
"chain": None if all_chains else resolved_chain,
|
|
1951
|
+
"all_chains": all_chains,
|
|
1952
|
+
"query": query,
|
|
1953
|
+
"token_address": token_address,
|
|
1954
|
+
"dex_id": dex_id,
|
|
1955
|
+
"count": len(normalized),
|
|
1956
|
+
"pairs": normalized,
|
|
1957
|
+
"source": "dexscreener",
|
|
1958
|
+
}
|
|
1959
|
+
|
|
1923
1960
|
async def send_uniswap_swap(
|
|
1924
1961
|
self,
|
|
1925
1962
|
*,
|
|
@@ -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.90",
|
|
6
6
|
"skills": ["skills/wallet-operator"],
|
|
7
7
|
"configSchema": {
|
|
8
8
|
"type": "object",
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agent-wallet",
|
|
3
3
|
"displayName": "Agent Wallet",
|
|
4
|
-
"version": "0.1.
|
|
4
|
+
"version": "0.1.90",
|
|
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"
|
|
@@ -54,7 +54,9 @@ no-op once the backend is healthy.
|
|
|
54
54
|
- `/wallet-sol` — print the current Solana wallet overview directly in chat.
|
|
55
55
|
- `/wallet-evm` — print the wallet overview for the current/default EVM network
|
|
56
56
|
directly in chat.
|
|
57
|
-
- `/wallet-base` — print the Base EVM wallet overview directly in chat
|
|
57
|
+
- `/wallet-base` — print the Base EVM wallet overview directly in chat and
|
|
58
|
+
switch the session's active wallet backend to Base so follow-up wallet
|
|
59
|
+
requests default to it.
|
|
58
60
|
- `/wallet-ethereum` — print the Ethereum EVM wallet overview directly in chat.
|
|
59
61
|
- `/agentlayer-autonomous-approve` — enable high-trust autonomous Base swaps
|
|
60
62
|
(`swap_evm_tokens` / `swap_evm_uniswap_tokens` on Base only) without
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
---
|
|
2
|
-
description: Enable AgentLayer autonomous
|
|
2
|
+
description: Enable AgentLayer autonomous wallet execution (every write tool) without per-transaction approvals.
|
|
3
3
|
allowed-tools: AskUserQuestion, mcp__agent_wallet__agentlayer_autonomous_approve, mcp__agent_wallet__agentlayer_autonomous_status
|
|
4
4
|
disable-model-invocation: true
|
|
5
5
|
---
|
|
@@ -13,17 +13,17 @@ If both `base_swaps` and `defi_tools` are already enabled, report that the combi
|
|
|
13
13
|
Otherwise, use `AskUserQuestion` to confirm:
|
|
14
14
|
|
|
15
15
|
- header: `Autonomy`
|
|
16
|
-
- question: `Enable AgentLayer autonomous
|
|
16
|
+
- question: `Enable AgentLayer autonomous execution for every wallet write tool (transfers, swaps, bridges, staking, x402 payments, generic contract calls, DeFi management) without per-transaction approvals, with no spend cap?`
|
|
17
17
|
- options:
|
|
18
|
-
- `Enable` — `Turns on
|
|
18
|
+
- `Enable` — `Turns on unbounded autonomous execution for every wallet write tool until revoked.`
|
|
19
19
|
- `Cancel` — `Leaves per-transaction approvals in place and does not change permissions.`
|
|
20
20
|
|
|
21
21
|
Only if the user selects `Enable`, call `agentlayer_autonomous_approve` with:
|
|
22
22
|
|
|
23
23
|
```json
|
|
24
24
|
{
|
|
25
|
-
"scope": "
|
|
26
|
-
"purpose": "User requested autonomous
|
|
25
|
+
"scope": "all",
|
|
26
|
+
"purpose": "User requested autonomous wallet execution from Claude Code.",
|
|
27
27
|
"user_intent": true
|
|
28
28
|
}
|
|
29
29
|
```
|
|
@@ -34,7 +34,6 @@ If the user selects `Cancel`, do not call any write tool. State that autonomous
|
|
|
34
34
|
|
|
35
35
|
Be explicit in the response:
|
|
36
36
|
|
|
37
|
-
- This removes per-transaction approvals for Base Velora/Uniswap swap execute calls and supported EVM DeFi management tools.
|
|
38
|
-
-
|
|
39
|
-
- It does not authorize transfers, bridges, Solana swaps, or generic contract calls.
|
|
37
|
+
- This removes per-transaction approvals for every wallet write tool: transfers, bridges, Solana swaps, staking, x402 payments, generic contract calls, Base Velora/Uniswap swap execute calls, and supported EVM DeFi management tools.
|
|
38
|
+
- This command enables the combined autonomous permission group, which has no per-tool allow-list, spend cap, or session TTL -- it is unbounded by amount within its scope.
|
|
40
39
|
- The user can run `/agentlayer-autonomous-revoke` to disable it.
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
---
|
|
2
|
-
description: Disable AgentLayer autonomous
|
|
2
|
+
description: Disable AgentLayer autonomous wallet execution (every write tool).
|
|
3
3
|
allowed-tools: mcp__agent_wallet__agentlayer_autonomous_revoke, mcp__agent_wallet__agentlayer_autonomous_status
|
|
4
4
|
disable-model-invocation: true
|
|
5
5
|
---
|
|
@@ -10,7 +10,7 @@ Call `agentlayer_autonomous_revoke` with:
|
|
|
10
10
|
|
|
11
11
|
```json
|
|
12
12
|
{
|
|
13
|
-
"scope": "
|
|
13
|
+
"scope": "all"
|
|
14
14
|
}
|
|
15
15
|
```
|
|
16
16
|
|
|
@@ -1,37 +1,60 @@
|
|
|
1
1
|
---
|
|
2
|
-
description: Show the connected EVM wallet
|
|
3
|
-
allowed-tools: mcp__agent_wallet__get_wallet_overview
|
|
2
|
+
description: Show the connected Base EVM wallet portfolio directly in chat and switch the active wallet backend to Base.
|
|
3
|
+
allowed-tools: mcp__agent_wallet__get_wallet_overview, mcp__agent_wallet__set_wallet_backend
|
|
4
4
|
disable-model-invocation: true
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
-
Show the connected EVM wallet
|
|
7
|
+
Show the connected Base EVM wallet portfolio directly in chat, and leave the
|
|
8
|
+
session's active wallet backend switched to Base so follow-up wallet calls in
|
|
9
|
+
this conversation don't need the backend specified again.
|
|
8
10
|
|
|
9
|
-
1. Call `get_wallet_overview`
|
|
11
|
+
1. Call `get_wallet_overview` and `set_wallet_backend` together — they are
|
|
12
|
+
independent, so issue both in the same turn rather than sequentially:
|
|
10
13
|
|
|
11
14
|
```json
|
|
15
|
+
// get_wallet_overview
|
|
12
16
|
{
|
|
13
17
|
"backend": "evm",
|
|
14
18
|
"network": "base"
|
|
15
19
|
}
|
|
16
20
|
```
|
|
17
21
|
|
|
18
|
-
|
|
22
|
+
```json
|
|
23
|
+
// set_wallet_backend
|
|
24
|
+
{
|
|
25
|
+
"backend": "base",
|
|
26
|
+
"network": "base"
|
|
27
|
+
}
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
2. Format the `get_wallet_overview` response as a compact wallet report:
|
|
19
31
|
|
|
20
32
|
- state the wallet as `EVM (Base)`
|
|
21
|
-
- include `chain`, `network` (or `requested_network`), `address`, and `total_value_usd` when present
|
|
33
|
+
- include `chain`, `network` (or `requested_network` when `network` is absent), `address`, and `total_value_usd` when present
|
|
22
34
|
- render a Markdown table with columns: `Asset | Type | Amount | USD Value`
|
|
23
35
|
- use `assets` when present
|
|
24
36
|
- for each asset row, prefer:
|
|
25
|
-
- asset label: `symbol`, then `token_address`, then `asset_type`
|
|
37
|
+
- asset label: `symbol`, then `name`, then `token_address`, then `asset_type`
|
|
38
|
+
- when the label came from `symbol`/`name` (not the token_address itself) and a `token_address` is present, append it shortened in parentheses next to the label as `first6…last4` (e.g. `USDC (0x833589…029139)`) — keep the contract visible but compact, never show it twice
|
|
26
39
|
- amount: `amount_ui`, then `balance_ui`, then `balance_native`, then `amount_raw`
|
|
27
40
|
- usd value: `value_usd`, then `balance_usd`
|
|
28
41
|
- omit zero-value rows only when both the amount and USD value are clearly zero
|
|
29
42
|
- if no asset rows are available, still report the native balance summary in prose
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
-
|
|
36
|
-
|
|
37
|
-
|
|
43
|
+
- do not include source metadata lines or footer fields such as `source`, `token_discovery_source`, or `pricing_source`
|
|
44
|
+
|
|
45
|
+
3. Assets with no `symbol`/`name` and no `price_usd` are unverified contracts
|
|
46
|
+
(commonly spam or airdropped tokens on Base). Do not list them individually
|
|
47
|
+
in the table — instead add one short line after the table noting how many
|
|
48
|
+
were omitted, e.g. "Plus 46 unverified ERC-20 contracts with no symbol or
|
|
49
|
+
price data (not included in the total)."
|
|
50
|
+
|
|
51
|
+
4. After the table, add one short line confirming the session wallet backend
|
|
52
|
+
is now Base so the user knows follow-up wallet requests will target it by
|
|
53
|
+
default. If `set_wallet_backend` failed, add one short warning line instead
|
|
54
|
+
(e.g. a stale local EVM daemon holding its port after a plugin upgrade) —
|
|
55
|
+
note that the portfolio above is still accurate, and that the backend
|
|
56
|
+
switch can be retried. Never drop the portfolio report because the switch
|
|
57
|
+
failed.
|
|
58
|
+
|
|
59
|
+
5. Do not suggest transfers, swaps, bridging, or other write actions unless
|
|
60
|
+
the user explicitly asks for them.
|
|
@@ -22,7 +22,8 @@ Primary design rules:
|
|
|
22
22
|
- EVM network selection with `set_evm_network`
|
|
23
23
|
- auto-managed approval binding for `preview -> execute` write flows
|
|
24
24
|
- bundled Codex skills, including `wallet-sol` for showing the Solana wallet
|
|
25
|
-
portfolio directly in chat
|
|
25
|
+
portfolio directly in chat and `wallet-base` for showing the Base EVM
|
|
26
|
+
wallet portfolio and switching the session's active backend to Base
|
|
26
27
|
|
|
27
28
|
## Runtime requirements
|
|
28
29
|
|
|
@@ -30,12 +31,16 @@ Primary design rules:
|
|
|
30
31
|
- keep the local wallet files and `~/.openclaw/sealed_keys.json` in place
|
|
31
32
|
- use `wallet codex install --yes` to install this plugin into Codex
|
|
32
33
|
|
|
33
|
-
## Bundled
|
|
34
|
+
## Bundled skills
|
|
34
35
|
|
|
35
|
-
After `wallet codex install --yes` and a Codex restart, the plugin ships
|
|
36
|
-
bundled
|
|
37
|
-
|
|
38
|
-
|
|
36
|
+
After `wallet codex install --yes` and a Codex restart, the plugin ships
|
|
37
|
+
bundled skills:
|
|
38
|
+
|
|
39
|
+
- `wallet-sol` — invoke from the slash menu or explicitly as `$wallet-sol`
|
|
40
|
+
to render the connected Solana wallet portfolio as a compact chat table.
|
|
41
|
+
- `wallet-base` — invoke from the slash menu or explicitly as `$wallet-base`
|
|
42
|
+
to render the connected Base EVM wallet portfolio as a compact chat table
|
|
43
|
+
and switch the session's active wallet backend to Base.
|
|
39
44
|
|
|
40
45
|
## Path resolution
|
|
41
46
|
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: "wallet-base"
|
|
3
|
+
description: "Show the current Base EVM wallet portfolio from the local AgentLayer wallet and switch the session's active wallet backend to Base. Use when the user asks for /wallet-base or wants their Base wallet shown directly in chat."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Base EVM Wallet Portfolio
|
|
7
|
+
|
|
8
|
+
Use the local AgentLayer wallet MCP tools only.
|
|
9
|
+
|
|
10
|
+
Workflow:
|
|
11
|
+
|
|
12
|
+
1. Call `get_wallet_overview` with `backend=evm, network=base` and
|
|
13
|
+
`set_wallet_backend` with `backend=base, network=base` — they are
|
|
14
|
+
independent, so issue both in the same turn rather than sequentially.
|
|
15
|
+
2. Render the `get_wallet_overview` result directly in chat in this shape:
|
|
16
|
+
- title: `EVM (Base) Wallet Portfolio`
|
|
17
|
+
- bullets for `Chain`, `Network` (or `requested_network` when `network`
|
|
18
|
+
is absent), `Address`, and `Total Value (USD)` when available
|
|
19
|
+
- one compact Markdown table with columns: `Asset | Type | Amount | USD Value`
|
|
20
|
+
|
|
21
|
+
Formatting rules:
|
|
22
|
+
|
|
23
|
+
- Use `assets` when present.
|
|
24
|
+
- For the asset label, prefer `symbol`, then `name`, then `token_address`,
|
|
25
|
+
then `asset_type`.
|
|
26
|
+
- When the label came from `symbol`/`name` (not the token_address itself)
|
|
27
|
+
and a `token_address` is present, append it shortened in parentheses next
|
|
28
|
+
to the label as `first6…last4` (e.g. `USDC (0x833589…029139)`) — keep the
|
|
29
|
+
contract visible but compact, never show it twice.
|
|
30
|
+
- For the amount, prefer `amount_ui`, then `balance_ui`, then
|
|
31
|
+
`balance_native`, then `amount_raw`.
|
|
32
|
+
- For the USD value, prefer `value_usd`, then `balance_usd`.
|
|
33
|
+
- Omit zero-value rows only when both the amount and USD value are clearly
|
|
34
|
+
zero. If no asset rows are available, still report the native balance
|
|
35
|
+
summary in prose.
|
|
36
|
+
- Assets with no `symbol`/`name` and no `price_usd` are unverified
|
|
37
|
+
contracts (commonly spam or airdropped tokens on Base). Do not list them
|
|
38
|
+
individually in the table — instead add one short line after the table
|
|
39
|
+
noting how many were omitted, e.g. "Plus 46 unverified ERC-20 contracts
|
|
40
|
+
with no symbol or price data (not included in the total)."
|
|
41
|
+
- Do not include source metadata lines or footer fields such as `source`,
|
|
42
|
+
`token_discovery_source`, or `pricing_source`.
|
|
43
|
+
|
|
44
|
+
After the table:
|
|
45
|
+
|
|
46
|
+
- Add one short line confirming the session wallet backend is now Base, so
|
|
47
|
+
the user knows follow-up wallet requests will target it by default. If
|
|
48
|
+
`set_wallet_backend` failed, add one short warning line instead (e.g. a
|
|
49
|
+
stale local EVM daemon holding its port after a plugin upgrade) — note
|
|
50
|
+
that the portfolio above is still accurate, and that the backend switch
|
|
51
|
+
can be retried. Never drop the portfolio report because the switch
|
|
52
|
+
failed.
|
|
53
|
+
|
|
54
|
+
Keep the response concise. Do not suggest transfers, swaps, bridging, or
|
|
55
|
+
other write actions unless the user explicitly asks for them. If
|
|
56
|
+
`get_wallet_overview` fails, surface the tool error plainly and stop.
|
package/package.json
CHANGED