@agentlayer.tech/wallet 0.1.65 → 0.1.67

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 (28) hide show
  1. package/.openclaw/extensions/agent-wallet/index.ts +82 -0
  2. package/.openclaw/extensions/agent-wallet/openclaw.plugin.json +7 -1
  3. package/.openclaw/extensions/agent-wallet/package.json +1 -1
  4. package/VERSION +1 -1
  5. package/agent-wallet/README.md +6 -0
  6. package/agent-wallet/agent_wallet/__init__.py +1 -1
  7. package/agent-wallet/agent_wallet/openclaw_adapter.py +485 -4
  8. package/agent-wallet/agent_wallet/providers/kamino.py +169 -3
  9. package/agent-wallet/agent_wallet/providers/x402.py +229 -148
  10. package/agent-wallet/agent_wallet/transaction_policy.py +60 -0
  11. package/agent-wallet/agent_wallet/wallet_layer/base.py +124 -0
  12. package/agent-wallet/agent_wallet/wallet_layer/solana.py +563 -0
  13. package/agent-wallet/agent_wallet/wallet_layer/wdk_evm.py +16 -0
  14. package/agent-wallet/openclaw.plugin.json +2 -2
  15. package/agent-wallet/pyproject.toml +1 -1
  16. package/agent-wallet/scripts/install_openclaw_local_config.py +6 -0
  17. package/agent-wallet/skills/wallet-operator/SKILL.md +6 -4
  18. package/claude-code/plugins/agent-wallet/.claude-plugin/plugin.json +1 -1
  19. package/claude-code/plugins/agent-wallet/skills/wallet-operator/SKILL.md +1 -1
  20. package/codex/plugins/agent-wallet/.codex-plugin/plugin.json +1 -1
  21. package/codex/plugins/agent-wallet/server.py +52 -3
  22. package/codex/plugins/agent-wallet/skills/wallet-operator/SKILL.md +1 -1
  23. package/hermes/plugins/agent_wallet/plugin.yaml +1 -1
  24. package/install-from-github.sh +6 -0
  25. package/package.json +1 -1
  26. package/wdk-btc-wallet/package.json +1 -1
  27. package/wdk-evm-wallet/package.json +1 -1
  28. package/wdk-evm-wallet/src/wdk_evm_wallet.js +126 -19
@@ -2,6 +2,8 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
+ import json
6
+ import time
5
7
  from typing import Any
6
8
 
7
9
  from agent_wallet.config import normalize_solana_network, settings
@@ -9,6 +11,41 @@ from agent_wallet.exceptions import ProviderError
9
11
  from agent_wallet.http_client import get_client
10
12
 
11
13
  KAMINO_BUILD_TIMEOUT_SECONDS = 20.0
14
+ # Discovery data only (market/vault catalogs and rate metrics) — never
15
+ # user-specific data such as obligations, positions, rewards, or portfolios.
16
+ # Catalogs change when Kamino ships a market/vault (days); rate metrics drift
17
+ # slower than the API's own aggregation window over these TTLs. The cache is
18
+ # long-lived only inside the resident read worker; cold CLI runs start empty.
19
+ MARKETS_CACHE_TTL_SECONDS = 600.0
20
+ RESERVES_CACHE_TTL_SECONDS = 180.0
21
+ EARN_VAULTS_CACHE_TTL_SECONDS = 600.0
22
+ EARN_VAULT_METRICS_CACHE_TTL_SECONDS = 180.0
23
+ _DISCOVERY_CACHE_MAX_ENTRIES = 128
24
+ _discovery_cache: dict[str, tuple[float, str]] = {}
25
+
26
+
27
+ def _discovery_cache_get(cache_key: str) -> Any | None:
28
+ entry = _discovery_cache.get(cache_key)
29
+ if entry is None:
30
+ return None
31
+ expires_at, payload_text = entry
32
+ if expires_at <= time.monotonic():
33
+ _discovery_cache.pop(cache_key, None)
34
+ return None
35
+ return json.loads(payload_text)
36
+
37
+
38
+ def _discovery_cache_set(cache_key: str, payload: Any, ttl_seconds: float) -> None:
39
+ try:
40
+ payload_text = json.dumps(payload)
41
+ except (TypeError, ValueError):
42
+ return
43
+ if len(_discovery_cache) >= _DISCOVERY_CACHE_MAX_ENTRIES:
44
+ for stale_key in sorted(_discovery_cache, key=lambda key: _discovery_cache[key][0])[
45
+ : len(_discovery_cache) - _DISCOVERY_CACHE_MAX_ENTRIES + 1
46
+ ]:
47
+ _discovery_cache.pop(stale_key, None)
48
+ _discovery_cache[cache_key] = (time.monotonic() + ttl_seconds, payload_text)
12
49
 
13
50
 
14
51
  def _normalized_api_base() -> str:
@@ -48,6 +85,10 @@ def _env_name(network: str) -> str:
48
85
 
49
86
  async def fetch_lend_markets() -> dict[str, Any]:
50
87
  """Fetch Kamino lending markets for the configured program id."""
88
+ cache_key = f"markets:{settings.kamino_program_id}"
89
+ cached = _discovery_cache_get(cache_key)
90
+ if cached is not None:
91
+ return cached
51
92
  client = get_client()
52
93
  response = await client.get(
53
94
  f"{_normalized_api_base()}/v2/kamino-market",
@@ -55,11 +96,85 @@ async def fetch_lend_markets() -> dict[str, Any]:
55
96
  )
56
97
  if response.status_code != 200:
57
98
  raise ProviderError("kamino", f"HTTP {response.status_code}: {response.text[:300]}")
58
- return _normalize_named_list_response(
99
+ result = _normalize_named_list_response(
59
100
  response.json(),
60
101
  key="markets",
61
102
  provider_name="kamino",
62
103
  )
104
+ _discovery_cache_set(cache_key, result, MARKETS_CACHE_TTL_SECONDS)
105
+ return result
106
+
107
+
108
+ async def fetch_portfolio(*, user: str) -> dict[str, Any]:
109
+ """Fetch the unified Kamino product portfolio for one wallet."""
110
+ client = get_client()
111
+ response = await client.get(f"{_normalized_api_base()}/portfolio/{user}")
112
+ if response.status_code != 200:
113
+ raise ProviderError("kamino", f"HTTP {response.status_code}: {response.text[:300]}")
114
+ data = response.json()
115
+ if not isinstance(data, dict):
116
+ raise ProviderError("kamino", "Unexpected portfolio response from Kamino.")
117
+ return data
118
+
119
+
120
+ async def fetch_earn_vaults() -> dict[str, Any]:
121
+ """Fetch the list of Kamino Earn vaults."""
122
+ cache_key = "earn_vaults"
123
+ cached = _discovery_cache_get(cache_key)
124
+ if cached is not None:
125
+ return cached
126
+ client = get_client()
127
+ response = await client.get(f"{_normalized_api_base()}/kvaults/vaults")
128
+ if response.status_code != 200:
129
+ raise ProviderError("kamino", f"HTTP {response.status_code}: {response.text[:300]}")
130
+ result = _normalize_named_list_response(
131
+ response.json(),
132
+ key="vaults",
133
+ provider_name="kamino",
134
+ )
135
+ _discovery_cache_set(cache_key, result, EARN_VAULTS_CACHE_TTL_SECONDS)
136
+ return result
137
+
138
+
139
+ async def fetch_earn_vault_metrics(*, vault: str, network: str) -> dict[str, Any]:
140
+ """Fetch APY/TVL metrics for one Kamino Earn vault."""
141
+ env = _env_name(network)
142
+ cache_key = f"earn_vault_metrics:{vault}:{env}"
143
+ cached = _discovery_cache_get(cache_key)
144
+ if cached is not None:
145
+ return cached
146
+ client = get_client()
147
+ response = await client.get(
148
+ f"{_normalized_api_base()}/kvaults/{vault}/metrics",
149
+ params={"env": env},
150
+ )
151
+ if response.status_code != 200:
152
+ raise ProviderError("kamino", f"HTTP {response.status_code}: {response.text[:300]}")
153
+ data = response.json()
154
+ if not isinstance(data, dict):
155
+ raise ProviderError("kamino", "Unexpected vault metrics response from Kamino.")
156
+ _discovery_cache_set(cache_key, data, EARN_VAULT_METRICS_CACHE_TTL_SECONDS)
157
+ return data
158
+
159
+
160
+ async def fetch_earn_user_positions(
161
+ *,
162
+ user: str,
163
+ network: str,
164
+ ) -> dict[str, Any]:
165
+ """Fetch all Kamino Earn vault positions for a wallet."""
166
+ client = get_client()
167
+ response = await client.get(
168
+ f"{_normalized_api_base()}/kvaults/users/{user}/positions",
169
+ params={"env": _env_name(network)},
170
+ )
171
+ if response.status_code != 200:
172
+ raise ProviderError("kamino", f"HTTP {response.status_code}: {response.text[:300]}")
173
+ return _normalize_named_list_response(
174
+ response.json(),
175
+ key="positions",
176
+ provider_name="kamino",
177
+ )
63
178
 
64
179
 
65
180
  async def fetch_lend_market_reserves(
@@ -68,18 +183,25 @@ async def fetch_lend_market_reserves(
68
183
  network: str,
69
184
  ) -> dict[str, Any]:
70
185
  """Fetch reserve metrics for one Kamino lending market."""
186
+ env = _env_name(network)
187
+ cache_key = f"reserves:{market}:{env}"
188
+ cached = _discovery_cache_get(cache_key)
189
+ if cached is not None:
190
+ return cached
71
191
  client = get_client()
72
192
  response = await client.get(
73
193
  f"{_normalized_api_base()}/kamino-market/{market}/reserves/metrics",
74
- params={"env": _env_name(network)},
194
+ params={"env": env},
75
195
  )
76
196
  if response.status_code != 200:
77
197
  raise ProviderError("kamino", f"HTTP {response.status_code}: {response.text[:300]}")
78
- return _normalize_named_list_response(
198
+ result = _normalize_named_list_response(
79
199
  response.json(),
80
200
  key="reserves",
81
201
  provider_name="kamino",
82
202
  )
203
+ _discovery_cache_set(cache_key, result, RESERVES_CACHE_TTL_SECONDS)
204
+ return result
83
205
 
84
206
 
85
207
  async def fetch_lend_user_obligations(
@@ -236,3 +358,47 @@ async def build_lend_repay_transaction(
236
358
  if response.status_code != 200:
237
359
  raise ProviderError("kamino", f"HTTP {response.status_code}: {response.text[:300]}")
238
360
  return _normalized_tx_response(response.json(), provider_name="kamino")
361
+
362
+
363
+ async def build_earn_deposit_transaction(
364
+ *,
365
+ wallet: str,
366
+ kvault: str,
367
+ amount_ui: str,
368
+ ) -> dict[str, Any]:
369
+ """Build an unsigned Kamino Earn deposit transaction."""
370
+ client = get_client()
371
+ response = await client.post(
372
+ f"{_normalized_api_base()}/ktx/kvault/deposit",
373
+ json={
374
+ "wallet": wallet,
375
+ "kvault": kvault,
376
+ "amount": amount_ui,
377
+ },
378
+ timeout=KAMINO_BUILD_TIMEOUT_SECONDS,
379
+ )
380
+ if response.status_code != 200:
381
+ raise ProviderError("kamino", f"HTTP {response.status_code}: {response.text[:300]}")
382
+ return _normalized_tx_response(response.json(), provider_name="kamino")
383
+
384
+
385
+ async def build_earn_withdraw_transaction(
386
+ *,
387
+ wallet: str,
388
+ kvault: str,
389
+ amount_ui: str,
390
+ ) -> dict[str, Any]:
391
+ """Build an unsigned Kamino Earn withdraw transaction."""
392
+ client = get_client()
393
+ response = await client.post(
394
+ f"{_normalized_api_base()}/ktx/kvault/withdraw",
395
+ json={
396
+ "wallet": wallet,
397
+ "kvault": kvault,
398
+ "amount": amount_ui,
399
+ },
400
+ timeout=KAMINO_BUILD_TIMEOUT_SECONDS,
401
+ )
402
+ if response.status_code != 200:
403
+ raise ProviderError("kamino", f"HTTP {response.status_code}: {response.text[:300]}")
404
+ return _normalized_tx_response(response.json(), provider_name="kamino")