@agentlayer.tech/wallet 0.1.44 → 0.1.47
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/index.ts +99 -0
- package/.openclaw/extensions/agent-wallet/openclaw.plugin.json +6 -1
- package/.openclaw/extensions/agent-wallet/package.json +1 -1
- package/CHANGELOG.md +62 -0
- package/README.md +2 -2
- package/VERSION +1 -1
- package/agent-wallet/agent_wallet/__init__.py +1 -1
- package/agent-wallet/agent_wallet/openclaw_adapter.py +686 -0
- package/agent-wallet/agent_wallet/providers/jupiter.py +5 -0
- package/agent-wallet/agent_wallet/providers/wdk_evm_local.py +6 -0
- package/agent-wallet/agent_wallet/wallet_layer/base.py +79 -0
- package/agent-wallet/agent_wallet/wallet_layer/solana.py +14 -0
- package/agent-wallet/agent_wallet/wallet_layer/wdk_evm.py +404 -0
- package/agent-wallet/openclaw.plugin.json +1 -1
- package/agent-wallet/pyproject.toml +1 -1
- package/agent-wallet/scripts/install_agent_wallet.py +33 -8
- package/claude-code/plugins/agent-wallet/.claude-plugin/plugin.json +1 -1
- package/codex/plugins/agent-wallet/.codex-plugin/plugin.json +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/README.md +31 -0
- package/wdk-evm-wallet/package-lock.json +268 -64
- package/wdk-evm-wallet/package.json +4 -1
- package/wdk-evm-wallet/src/config.js +2 -0
- package/wdk-evm-wallet/src/server.js +66 -0
- package/wdk-evm-wallet/src/wdk_evm_wallet.js +2725 -939
|
@@ -117,6 +117,7 @@ async def fetch_quote(
|
|
|
117
117
|
restrict_intermediate_tokens: bool = True,
|
|
118
118
|
only_direct_routes: bool = False,
|
|
119
119
|
swap_mode: str = "ExactIn",
|
|
120
|
+
exclude_dexes: list[str] | None = None,
|
|
120
121
|
) -> dict[str, Any]:
|
|
121
122
|
"""Fetch a Jupiter quote for an exact-in swap.
|
|
122
123
|
|
|
@@ -133,6 +134,7 @@ async def fetch_quote(
|
|
|
133
134
|
restrict_intermediate_tokens=restrict_intermediate_tokens,
|
|
134
135
|
only_direct_routes=only_direct_routes,
|
|
135
136
|
swap_mode=swap_mode,
|
|
137
|
+
exclude_dexes=exclude_dexes,
|
|
136
138
|
)
|
|
137
139
|
except ProviderError as exc:
|
|
138
140
|
error_msg = str(exc).lower()
|
|
@@ -168,6 +170,7 @@ async def _fetch_quote_direct(
|
|
|
168
170
|
restrict_intermediate_tokens: bool = True,
|
|
169
171
|
only_direct_routes: bool = False,
|
|
170
172
|
swap_mode: str = "ExactIn",
|
|
173
|
+
exclude_dexes: list[str] | None = None,
|
|
171
174
|
) -> dict[str, Any]:
|
|
172
175
|
"""Fetch a Jupiter quote directly from Jupiter API."""
|
|
173
176
|
client = get_client()
|
|
@@ -180,6 +183,8 @@ async def _fetch_quote_direct(
|
|
|
180
183
|
"restrictIntermediateTokens": str(restrict_intermediate_tokens).lower(),
|
|
181
184
|
"onlyDirectRoutes": str(only_direct_routes).lower(),
|
|
182
185
|
}
|
|
186
|
+
if exclude_dexes:
|
|
187
|
+
params["excludeDexes"] = ",".join(str(d).strip() for d in exclude_dexes if str(d).strip())
|
|
183
188
|
response = await client.get(
|
|
184
189
|
f"{settings.jupiter_api_base_url.rstrip('/')}/quote",
|
|
185
190
|
params=params,
|
|
@@ -18,6 +18,12 @@ LONG_RUNNING_POST_PATHS = {
|
|
|
18
18
|
"/v1/evm/aave/withdraw/send",
|
|
19
19
|
"/v1/evm/aave/borrow/send",
|
|
20
20
|
"/v1/evm/aave/repay/send",
|
|
21
|
+
"/v1/evm/morpho/vault/supply/send",
|
|
22
|
+
"/v1/evm/morpho/vault/withdraw/send",
|
|
23
|
+
"/v1/evm/morpho/market/supply_collateral/send",
|
|
24
|
+
"/v1/evm/morpho/market/borrow/send",
|
|
25
|
+
"/v1/evm/morpho/market/repay/send",
|
|
26
|
+
"/v1/evm/morpho/market/withdraw_collateral/send",
|
|
21
27
|
"/v1/evm/lido/stake_eth_for_wsteth/send",
|
|
22
28
|
"/v1/evm/lido/wrap_steth/send",
|
|
23
29
|
"/v1/evm/lido/unwrap_wsteth/send",
|
|
@@ -120,6 +120,35 @@ class AgentWalletBackend(ABC):
|
|
|
120
120
|
async def get_evm_lido_withdrawal_requests(self) -> dict[str, Any]:
|
|
121
121
|
raise WalletBackendError(f"{self.name} does not support EVM Lido withdrawal lookup.")
|
|
122
122
|
|
|
123
|
+
async def get_evm_morpho_vaults(
|
|
124
|
+
self,
|
|
125
|
+
*,
|
|
126
|
+
vault_address: str | None = None,
|
|
127
|
+
limit: int | None = None,
|
|
128
|
+
listed_only: bool = True,
|
|
129
|
+
asset_address: str | None = None,
|
|
130
|
+
order_by: str | None = None,
|
|
131
|
+
order_direction: str | None = None,
|
|
132
|
+
) -> dict[str, Any]:
|
|
133
|
+
raise WalletBackendError(f"{self.name} does not support EVM Morpho vault lookup.")
|
|
134
|
+
|
|
135
|
+
async def get_evm_morpho_markets(
|
|
136
|
+
self,
|
|
137
|
+
*,
|
|
138
|
+
market_id: str | None = None,
|
|
139
|
+
limit: int | None = None,
|
|
140
|
+
listed_only: bool = True,
|
|
141
|
+
search: str | None = None,
|
|
142
|
+
collateral_asset_address: str | None = None,
|
|
143
|
+
loan_asset_address: str | None = None,
|
|
144
|
+
order_by: str | None = None,
|
|
145
|
+
order_direction: str | None = None,
|
|
146
|
+
) -> dict[str, Any]:
|
|
147
|
+
raise WalletBackendError(f"{self.name} does not support EVM Morpho market lookup.")
|
|
148
|
+
|
|
149
|
+
async def get_evm_morpho_positions(self) -> dict[str, Any]:
|
|
150
|
+
raise WalletBackendError(f"{self.name} does not support EVM Morpho positions lookup.")
|
|
151
|
+
|
|
123
152
|
async def preview_evm_aave_operation(
|
|
124
153
|
self,
|
|
125
154
|
*,
|
|
@@ -175,6 +204,56 @@ class AgentWalletBackend(ABC):
|
|
|
175
204
|
) -> dict[str, Any]:
|
|
176
205
|
raise WalletBackendError(f"{self.name} does not support EVM Lido withdrawals.")
|
|
177
206
|
|
|
207
|
+
async def preview_evm_morpho_vault_operation(
|
|
208
|
+
self,
|
|
209
|
+
*,
|
|
210
|
+
operation: str,
|
|
211
|
+
token_address: str,
|
|
212
|
+
vault_address: str | None = None,
|
|
213
|
+
vault_preset: str | None = None,
|
|
214
|
+
amount_raw: str | None = None,
|
|
215
|
+
native_amount_raw: str | None = None,
|
|
216
|
+
) -> dict[str, Any]:
|
|
217
|
+
raise WalletBackendError(f"{self.name} does not support EVM Morpho vault previews.")
|
|
218
|
+
|
|
219
|
+
async def send_evm_morpho_vault_operation(
|
|
220
|
+
self,
|
|
221
|
+
*,
|
|
222
|
+
operation: str,
|
|
223
|
+
token_address: str,
|
|
224
|
+
vault_address: str | None = None,
|
|
225
|
+
vault_preset: str | None = None,
|
|
226
|
+
amount_raw: str | None = None,
|
|
227
|
+
native_amount_raw: str | None = None,
|
|
228
|
+
expected_quote_fingerprint: str | None = None,
|
|
229
|
+
) -> dict[str, Any]:
|
|
230
|
+
raise WalletBackendError(f"{self.name} does not support EVM Morpho vault operations.")
|
|
231
|
+
|
|
232
|
+
async def preview_evm_morpho_market_operation(
|
|
233
|
+
self,
|
|
234
|
+
*,
|
|
235
|
+
operation: str,
|
|
236
|
+
token_address: str,
|
|
237
|
+
market_id: str | None = None,
|
|
238
|
+
market_preset: str | None = None,
|
|
239
|
+
amount_raw: str | None = None,
|
|
240
|
+
native_amount_raw: str | None = None,
|
|
241
|
+
) -> dict[str, Any]:
|
|
242
|
+
raise WalletBackendError(f"{self.name} does not support EVM Morpho market previews.")
|
|
243
|
+
|
|
244
|
+
async def send_evm_morpho_market_operation(
|
|
245
|
+
self,
|
|
246
|
+
*,
|
|
247
|
+
operation: str,
|
|
248
|
+
token_address: str,
|
|
249
|
+
market_id: str | None = None,
|
|
250
|
+
market_preset: str | None = None,
|
|
251
|
+
amount_raw: str | None = None,
|
|
252
|
+
native_amount_raw: str | None = None,
|
|
253
|
+
expected_quote_fingerprint: str | None = None,
|
|
254
|
+
) -> dict[str, Any]:
|
|
255
|
+
raise WalletBackendError(f"{self.name} does not support EVM Morpho market operations.")
|
|
256
|
+
|
|
178
257
|
async def preview_evm_swap(
|
|
179
258
|
self,
|
|
180
259
|
*,
|
|
@@ -4704,6 +4704,7 @@ class SolanaWalletBackend(AgentWalletBackend):
|
|
|
4704
4704
|
amount_ui: float,
|
|
4705
4705
|
slippage_bps: int = SOLANA_SWAP_DEFAULT_SLIPPAGE_BPS,
|
|
4706
4706
|
exclude_routers: list[str] | None = None,
|
|
4707
|
+
exclude_dexes: list[str] | None = None,
|
|
4707
4708
|
) -> dict[str, Any]:
|
|
4708
4709
|
if self.network != "mainnet":
|
|
4709
4710
|
raise WalletBackendError("Provider-routed swaps are only enabled for Solana mainnet.")
|
|
@@ -4749,6 +4750,7 @@ class SolanaWalletBackend(AgentWalletBackend):
|
|
|
4749
4750
|
output_mint=output_mint,
|
|
4750
4751
|
amount_raw=raw_amount,
|
|
4751
4752
|
slippage_bps=slippage_bps,
|
|
4753
|
+
exclude_dexes=exclude_dexes,
|
|
4752
4754
|
)
|
|
4753
4755
|
quote_source = "jupiter-metis"
|
|
4754
4756
|
|
|
@@ -5006,17 +5008,27 @@ class SolanaWalletBackend(AgentWalletBackend):
|
|
|
5006
5008
|
|
|
5007
5009
|
attempts: list[dict[str, Any]] = []
|
|
5008
5010
|
last_error: str | None = None
|
|
5011
|
+
_simulation_failed = False
|
|
5009
5012
|
for attempt_index in range(max_attempts):
|
|
5010
5013
|
if valid_until_epoch_seconds is not None and int(time.time()) > int(valid_until_epoch_seconds):
|
|
5011
5014
|
break
|
|
5012
5015
|
try:
|
|
5013
5016
|
exclude_routers = ["jupiterz"] if attempt_index > 0 else None
|
|
5017
|
+
# On retries after a simulation failure, exclude DEXes known to fail
|
|
5018
|
+
# simulation for Token-2022 tokens with extensions such as
|
|
5019
|
+
# scaledUiAmountConfig, pausableConfig, or permanentDelegate
|
|
5020
|
+
# (e.g. Backpack xStock tokens). GoonFi V2 is the primary offender;
|
|
5021
|
+
# ZeroFi handles these tokens correctly.
|
|
5022
|
+
exclude_dexes: list[str] | None = None
|
|
5023
|
+
if _simulation_failed and attempt_index > 0:
|
|
5024
|
+
exclude_dexes = ["GoonFi V2"]
|
|
5014
5025
|
preview = await self.preview_swap(
|
|
5015
5026
|
input_mint=input_mint,
|
|
5016
5027
|
output_mint=output_mint,
|
|
5017
5028
|
amount_ui=amount_ui,
|
|
5018
5029
|
slippage_bps=slippage_bps,
|
|
5019
5030
|
exclude_routers=exclude_routers,
|
|
5031
|
+
exclude_dexes=exclude_dexes,
|
|
5020
5032
|
)
|
|
5021
5033
|
estimated_output_raw = int(preview.get("estimated_output_amount_raw") or 0)
|
|
5022
5034
|
if (
|
|
@@ -5073,6 +5085,8 @@ class SolanaWalletBackend(AgentWalletBackend):
|
|
|
5073
5085
|
return result
|
|
5074
5086
|
except (WalletBackendError, ProviderError) as exc:
|
|
5075
5087
|
last_error = str(exc)
|
|
5088
|
+
if "simulation failed" in str(exc).lower():
|
|
5089
|
+
_simulation_failed = True
|
|
5076
5090
|
attempts.append(
|
|
5077
5091
|
{
|
|
5078
5092
|
"attempt": attempt_index + 1,
|
|
@@ -119,6 +119,22 @@ def _normalize_lido_withdrawal_operation(value: str) -> str:
|
|
|
119
119
|
return operation
|
|
120
120
|
|
|
121
121
|
|
|
122
|
+
def _normalize_morpho_vault_operation(value: str) -> str:
|
|
123
|
+
operation = str(value or "").strip().lower()
|
|
124
|
+
if operation not in {"supply", "withdraw"}:
|
|
125
|
+
raise WalletBackendError("Morpho vault operation must be one of: supply, withdraw.")
|
|
126
|
+
return operation
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def _normalize_morpho_market_operation(value: str) -> str:
|
|
130
|
+
operation = str(value or "").strip().lower()
|
|
131
|
+
if operation not in {"supply_collateral", "borrow", "repay", "withdraw_collateral"}:
|
|
132
|
+
raise WalletBackendError(
|
|
133
|
+
"Morpho market operation must be one of: supply_collateral, borrow, repay, withdraw_collateral."
|
|
134
|
+
)
|
|
135
|
+
return operation
|
|
136
|
+
|
|
137
|
+
|
|
122
138
|
def _normalize_aave_payload(
|
|
123
139
|
*,
|
|
124
140
|
chain: str,
|
|
@@ -296,6 +312,88 @@ def _normalize_lido_withdrawal_payload(
|
|
|
296
312
|
}
|
|
297
313
|
|
|
298
314
|
|
|
315
|
+
def _normalize_morpho_payload(
|
|
316
|
+
*,
|
|
317
|
+
chain: str,
|
|
318
|
+
network: str,
|
|
319
|
+
wallet_id: str,
|
|
320
|
+
address: str,
|
|
321
|
+
operation: str,
|
|
322
|
+
token_address: str,
|
|
323
|
+
amount_raw: str | None,
|
|
324
|
+
native_amount_raw: str | None,
|
|
325
|
+
data: dict[str, Any],
|
|
326
|
+
sign_only: bool,
|
|
327
|
+
) -> dict[str, Any]:
|
|
328
|
+
result = dict(data.get("result") or {})
|
|
329
|
+
request = dict(data.get("operationRequest") or {})
|
|
330
|
+
requirements = dict(data.get("requirements") or {})
|
|
331
|
+
resolved_amount = (
|
|
332
|
+
str(request.get("amount"))
|
|
333
|
+
if request.get("amount") is not None
|
|
334
|
+
else (str(amount_raw) if amount_raw is not None else None)
|
|
335
|
+
)
|
|
336
|
+
resolved_native_amount = (
|
|
337
|
+
str(request.get("nativeAmount"))
|
|
338
|
+
if request.get("nativeAmount") is not None
|
|
339
|
+
else (str(native_amount_raw) if native_amount_raw is not None else None)
|
|
340
|
+
)
|
|
341
|
+
target = dict(data.get("target") or {})
|
|
342
|
+
surface = str(data.get("surface") or target.get("type") or "").strip().lower() or None
|
|
343
|
+
asset_type = "evm-morpho-vault" if surface == "vault" else "evm-morpho-market"
|
|
344
|
+
return {
|
|
345
|
+
"chain": chain,
|
|
346
|
+
"network": network,
|
|
347
|
+
"asset_type": asset_type,
|
|
348
|
+
"asset": "ERC20",
|
|
349
|
+
"wallet": wallet_id,
|
|
350
|
+
"from_address": str(data.get("address") or address),
|
|
351
|
+
"protocol": str(data.get("protocol") or "morpho"),
|
|
352
|
+
"surface": surface,
|
|
353
|
+
"operation": str(data.get("operation") or operation),
|
|
354
|
+
"target": target,
|
|
355
|
+
"token_address": str(request.get("token") or token_address),
|
|
356
|
+
"amount_raw": resolved_amount,
|
|
357
|
+
"native_amount_raw": resolved_native_amount,
|
|
358
|
+
"amount_ui": str(data.get("amountFormatted")) if data.get("amountFormatted") is not None else None,
|
|
359
|
+
"native_amount_ui": (
|
|
360
|
+
str(data.get("nativeAmountFormatted"))
|
|
361
|
+
if data.get("nativeAmountFormatted") is not None
|
|
362
|
+
else None
|
|
363
|
+
),
|
|
364
|
+
"estimated_fee_wei": str(data.get("estimatedFeeWei")) if data.get("estimatedFeeWei") is not None else None,
|
|
365
|
+
"estimated_operation_fee_wei": (
|
|
366
|
+
str(data.get("estimatedOperationFeeWei"))
|
|
367
|
+
if data.get("estimatedOperationFeeWei") is not None
|
|
368
|
+
else None
|
|
369
|
+
),
|
|
370
|
+
"estimated_requirements_fee_wei": (
|
|
371
|
+
str(data.get("estimatedRequirementsFeeWei"))
|
|
372
|
+
if data.get("estimatedRequirementsFeeWei") is not None
|
|
373
|
+
else None
|
|
374
|
+
),
|
|
375
|
+
"fee_estimate_available": bool(data.get("feeEstimateAvailable", True)),
|
|
376
|
+
"fee_estimate_error": data.get("feeEstimateError"),
|
|
377
|
+
"execution_supported": bool(data.get("executionSupported", True)) and not sign_only,
|
|
378
|
+
"quote_fingerprint": str(data.get("quoteFingerprint") or "").strip() or None,
|
|
379
|
+
"requirements": {
|
|
380
|
+
"required": bool(requirements.get("required")),
|
|
381
|
+
"requirement_count": int(requirements.get("requirementCount") or 0),
|
|
382
|
+
"approval_required": bool(requirements.get("approvalRequired")),
|
|
383
|
+
"authorization_required": bool(requirements.get("authorizationRequired")),
|
|
384
|
+
"sequence": list(requirements.get("sequence") or []),
|
|
385
|
+
},
|
|
386
|
+
"token_metadata": _normalize_token_metadata(
|
|
387
|
+
data.get("tokenMetadata"),
|
|
388
|
+
str(request.get("token") or token_address),
|
|
389
|
+
),
|
|
390
|
+
"hash": result.get("hash"),
|
|
391
|
+
"result": result,
|
|
392
|
+
"chain_id": int(data.get("chainId") or 0),
|
|
393
|
+
"source": "wdk-evm-wallet",
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
|
|
299
397
|
def _normalize_lifi_cross_chain_payload(
|
|
300
398
|
*,
|
|
301
399
|
chain: str,
|
|
@@ -921,6 +1019,122 @@ class WdkEvmLocalWalletBackend(AgentWalletBackend):
|
|
|
921
1019
|
"source": "wdk-evm-wallet",
|
|
922
1020
|
}
|
|
923
1021
|
|
|
1022
|
+
async def get_evm_morpho_vaults(
|
|
1023
|
+
self,
|
|
1024
|
+
*,
|
|
1025
|
+
vault_address: str | None = None,
|
|
1026
|
+
limit: int | None = None,
|
|
1027
|
+
listed_only: bool = True,
|
|
1028
|
+
asset_address: str | None = None,
|
|
1029
|
+
order_by: str | None = None,
|
|
1030
|
+
order_direction: str | None = None,
|
|
1031
|
+
) -> dict[str, Any]:
|
|
1032
|
+
payload: dict[str, Any] = {
|
|
1033
|
+
"network": self.network,
|
|
1034
|
+
"listedOnly": bool(listed_only),
|
|
1035
|
+
}
|
|
1036
|
+
if isinstance(vault_address, str) and vault_address.strip():
|
|
1037
|
+
payload["vaultAddress"] = vault_address.strip()
|
|
1038
|
+
if limit is not None:
|
|
1039
|
+
payload["limit"] = int(limit)
|
|
1040
|
+
if isinstance(asset_address, str) and asset_address.strip():
|
|
1041
|
+
payload["assetAddress"] = asset_address.strip()
|
|
1042
|
+
if isinstance(order_by, str) and order_by.strip():
|
|
1043
|
+
payload["orderBy"] = order_by.strip()
|
|
1044
|
+
if isinstance(order_direction, str) and order_direction.strip():
|
|
1045
|
+
payload["orderDirection"] = order_direction.strip()
|
|
1046
|
+
data = await self.client.post("/v1/evm/morpho/vaults/get", payload)
|
|
1047
|
+
return {
|
|
1048
|
+
"chain": self.chain,
|
|
1049
|
+
"network": self.network,
|
|
1050
|
+
"protocol": str(data.get("protocol") or "morpho"),
|
|
1051
|
+
"chain_id": int(data.get("chainId") or 0),
|
|
1052
|
+
"listed_only": bool(data.get("listedOnly", listed_only)),
|
|
1053
|
+
"requested_limit": int(data.get("requestedLimit") or 0),
|
|
1054
|
+
"order_by": data.get("orderBy"),
|
|
1055
|
+
"order_direction": data.get("orderDirection"),
|
|
1056
|
+
"asset_address_filter": data.get("assetAddressFilter"),
|
|
1057
|
+
"found": bool(data.get("found")) if "found" in data else None,
|
|
1058
|
+
"vault_count": int(data.get("vaultCount") or 0),
|
|
1059
|
+
"vault": dict(data.get("vault") or {}) if isinstance(data.get("vault"), dict) else None,
|
|
1060
|
+
"vaults": list(data.get("vaults") or []),
|
|
1061
|
+
"source": "wdk-evm-wallet",
|
|
1062
|
+
}
|
|
1063
|
+
|
|
1064
|
+
async def get_evm_morpho_markets(
|
|
1065
|
+
self,
|
|
1066
|
+
*,
|
|
1067
|
+
market_id: str | None = None,
|
|
1068
|
+
limit: int | None = None,
|
|
1069
|
+
listed_only: bool = True,
|
|
1070
|
+
search: str | None = None,
|
|
1071
|
+
collateral_asset_address: str | None = None,
|
|
1072
|
+
loan_asset_address: str | None = None,
|
|
1073
|
+
order_by: str | None = None,
|
|
1074
|
+
order_direction: str | None = None,
|
|
1075
|
+
) -> dict[str, Any]:
|
|
1076
|
+
payload: dict[str, Any] = {
|
|
1077
|
+
"network": self.network,
|
|
1078
|
+
"listedOnly": bool(listed_only),
|
|
1079
|
+
}
|
|
1080
|
+
if isinstance(market_id, str) and market_id.strip():
|
|
1081
|
+
payload["marketId"] = market_id.strip()
|
|
1082
|
+
if limit is not None:
|
|
1083
|
+
payload["limit"] = int(limit)
|
|
1084
|
+
if isinstance(search, str) and search.strip():
|
|
1085
|
+
payload["search"] = search.strip()
|
|
1086
|
+
if isinstance(collateral_asset_address, str) and collateral_asset_address.strip():
|
|
1087
|
+
payload["collateralAssetAddress"] = collateral_asset_address.strip()
|
|
1088
|
+
if isinstance(loan_asset_address, str) and loan_asset_address.strip():
|
|
1089
|
+
payload["loanAssetAddress"] = loan_asset_address.strip()
|
|
1090
|
+
if isinstance(order_by, str) and order_by.strip():
|
|
1091
|
+
payload["orderBy"] = order_by.strip()
|
|
1092
|
+
if isinstance(order_direction, str) and order_direction.strip():
|
|
1093
|
+
payload["orderDirection"] = order_direction.strip()
|
|
1094
|
+
data = await self.client.post("/v1/evm/morpho/markets/get", payload)
|
|
1095
|
+
return {
|
|
1096
|
+
"chain": self.chain,
|
|
1097
|
+
"network": self.network,
|
|
1098
|
+
"protocol": str(data.get("protocol") or "morpho"),
|
|
1099
|
+
"chain_id": int(data.get("chainId") or 0),
|
|
1100
|
+
"listed_only": bool(data.get("listedOnly", listed_only)),
|
|
1101
|
+
"requested_limit": int(data.get("requestedLimit") or 0),
|
|
1102
|
+
"order_by": data.get("orderBy"),
|
|
1103
|
+
"order_direction": data.get("orderDirection"),
|
|
1104
|
+
"search": data.get("search"),
|
|
1105
|
+
"collateral_asset_filter": data.get("collateralAssetFilter"),
|
|
1106
|
+
"loan_asset_filter": data.get("loanAssetFilter"),
|
|
1107
|
+
"found": bool(data.get("found")) if "found" in data else None,
|
|
1108
|
+
"market_count": int(data.get("marketCount") or 0),
|
|
1109
|
+
"market": dict(data.get("market") or {}) if isinstance(data.get("market"), dict) else None,
|
|
1110
|
+
"markets": list(data.get("markets") or []),
|
|
1111
|
+
"source": "wdk-evm-wallet",
|
|
1112
|
+
}
|
|
1113
|
+
|
|
1114
|
+
async def get_evm_morpho_positions(self) -> dict[str, Any]:
|
|
1115
|
+
resolved_address = await self.get_address()
|
|
1116
|
+
data = await self.client.post(
|
|
1117
|
+
"/v1/evm/morpho/positions/get",
|
|
1118
|
+
{
|
|
1119
|
+
"walletId": self.wallet_id,
|
|
1120
|
+
"address": resolved_address,
|
|
1121
|
+
"accountIndex": self.account_index,
|
|
1122
|
+
"network": self.network,
|
|
1123
|
+
},
|
|
1124
|
+
)
|
|
1125
|
+
return {
|
|
1126
|
+
"chain": self.chain,
|
|
1127
|
+
"network": self.network,
|
|
1128
|
+
"address": str(data.get("address") or resolved_address or ""),
|
|
1129
|
+
"protocol": str(data.get("protocol") or "morpho"),
|
|
1130
|
+
"chain_id": int(data.get("chainId") or 0),
|
|
1131
|
+
"market_position_count": int(data.get("marketPositionCount") or 0),
|
|
1132
|
+
"vault_position_count": int(data.get("vaultPositionCount") or 0),
|
|
1133
|
+
"market_positions": list(data.get("marketPositions") or []),
|
|
1134
|
+
"vault_positions": list(data.get("vaultPositions") or []),
|
|
1135
|
+
"source": "wdk-evm-wallet",
|
|
1136
|
+
}
|
|
1137
|
+
|
|
924
1138
|
async def preview_evm_aave_operation(
|
|
925
1139
|
self,
|
|
926
1140
|
*,
|
|
@@ -1140,6 +1354,196 @@ class WdkEvmLocalWalletBackend(AgentWalletBackend):
|
|
|
1140
1354
|
"confirmed": False,
|
|
1141
1355
|
}
|
|
1142
1356
|
|
|
1357
|
+
async def preview_evm_morpho_vault_operation(
|
|
1358
|
+
self,
|
|
1359
|
+
*,
|
|
1360
|
+
operation: str,
|
|
1361
|
+
token_address: str,
|
|
1362
|
+
vault_address: str | None = None,
|
|
1363
|
+
vault_preset: str | None = None,
|
|
1364
|
+
amount_raw: str | None = None,
|
|
1365
|
+
native_amount_raw: str | None = None,
|
|
1366
|
+
) -> dict[str, Any]:
|
|
1367
|
+
normalized_operation = _normalize_morpho_vault_operation(operation)
|
|
1368
|
+
resolved_address = await self.get_address()
|
|
1369
|
+
payload: dict[str, Any] = {
|
|
1370
|
+
"walletId": self.wallet_id,
|
|
1371
|
+
"address": resolved_address,
|
|
1372
|
+
"accountIndex": self.account_index,
|
|
1373
|
+
"network": self.network,
|
|
1374
|
+
"tokenAddress": token_address,
|
|
1375
|
+
}
|
|
1376
|
+
if isinstance(vault_address, str) and vault_address.strip():
|
|
1377
|
+
payload["vaultAddress"] = vault_address.strip()
|
|
1378
|
+
if isinstance(vault_preset, str) and vault_preset.strip():
|
|
1379
|
+
payload["vaultPreset"] = vault_preset.strip()
|
|
1380
|
+
if amount_raw is not None:
|
|
1381
|
+
payload["amount"] = amount_raw
|
|
1382
|
+
if native_amount_raw is not None:
|
|
1383
|
+
payload["nativeAmount"] = native_amount_raw
|
|
1384
|
+
data = await self.client.post(
|
|
1385
|
+
f"/v1/evm/morpho/vault/{normalized_operation}/quote",
|
|
1386
|
+
payload,
|
|
1387
|
+
)
|
|
1388
|
+
return _normalize_morpho_payload(
|
|
1389
|
+
chain=self.chain,
|
|
1390
|
+
network=self.network,
|
|
1391
|
+
wallet_id=self.wallet_id,
|
|
1392
|
+
address=resolved_address,
|
|
1393
|
+
operation=normalized_operation,
|
|
1394
|
+
token_address=token_address,
|
|
1395
|
+
amount_raw=amount_raw,
|
|
1396
|
+
native_amount_raw=native_amount_raw,
|
|
1397
|
+
data=data,
|
|
1398
|
+
sign_only=self.sign_only,
|
|
1399
|
+
)
|
|
1400
|
+
|
|
1401
|
+
async def send_evm_morpho_vault_operation(
|
|
1402
|
+
self,
|
|
1403
|
+
*,
|
|
1404
|
+
operation: str,
|
|
1405
|
+
token_address: str,
|
|
1406
|
+
vault_address: str | None = None,
|
|
1407
|
+
vault_preset: str | None = None,
|
|
1408
|
+
amount_raw: str | None = None,
|
|
1409
|
+
native_amount_raw: str | None = None,
|
|
1410
|
+
expected_quote_fingerprint: str | None = None,
|
|
1411
|
+
) -> dict[str, Any]:
|
|
1412
|
+
if self.sign_only:
|
|
1413
|
+
raise WalletBackendError("wdk_evm_local is configured as sign_only.")
|
|
1414
|
+
normalized_operation = _normalize_morpho_vault_operation(operation)
|
|
1415
|
+
payload: dict[str, Any] = {
|
|
1416
|
+
"walletId": self.wallet_id,
|
|
1417
|
+
"accountIndex": self.account_index,
|
|
1418
|
+
"network": self.network,
|
|
1419
|
+
"tokenAddress": token_address,
|
|
1420
|
+
}
|
|
1421
|
+
if isinstance(vault_address, str) and vault_address.strip():
|
|
1422
|
+
payload["vaultAddress"] = vault_address.strip()
|
|
1423
|
+
if isinstance(vault_preset, str) and vault_preset.strip():
|
|
1424
|
+
payload["vaultPreset"] = vault_preset.strip()
|
|
1425
|
+
if amount_raw is not None:
|
|
1426
|
+
payload["amount"] = amount_raw
|
|
1427
|
+
if native_amount_raw is not None:
|
|
1428
|
+
payload["nativeAmount"] = native_amount_raw
|
|
1429
|
+
if isinstance(expected_quote_fingerprint, str) and expected_quote_fingerprint.strip():
|
|
1430
|
+
payload["expectedQuoteFingerprint"] = expected_quote_fingerprint.strip()
|
|
1431
|
+
data = await self.client.post(
|
|
1432
|
+
f"/v1/evm/morpho/vault/{normalized_operation}/send",
|
|
1433
|
+
payload,
|
|
1434
|
+
)
|
|
1435
|
+
return {
|
|
1436
|
+
**_normalize_morpho_payload(
|
|
1437
|
+
chain=self.chain,
|
|
1438
|
+
network=self.network,
|
|
1439
|
+
wallet_id=self.wallet_id,
|
|
1440
|
+
address=await self.get_address(),
|
|
1441
|
+
operation=normalized_operation,
|
|
1442
|
+
token_address=token_address,
|
|
1443
|
+
amount_raw=amount_raw,
|
|
1444
|
+
native_amount_raw=native_amount_raw,
|
|
1445
|
+
data=data,
|
|
1446
|
+
sign_only=self.sign_only,
|
|
1447
|
+
),
|
|
1448
|
+
"broadcasted": True,
|
|
1449
|
+
"confirmed": False,
|
|
1450
|
+
}
|
|
1451
|
+
|
|
1452
|
+
async def preview_evm_morpho_market_operation(
|
|
1453
|
+
self,
|
|
1454
|
+
*,
|
|
1455
|
+
operation: str,
|
|
1456
|
+
token_address: str,
|
|
1457
|
+
market_id: str | None = None,
|
|
1458
|
+
market_preset: str | None = None,
|
|
1459
|
+
amount_raw: str | None = None,
|
|
1460
|
+
native_amount_raw: str | None = None,
|
|
1461
|
+
) -> dict[str, Any]:
|
|
1462
|
+
normalized_operation = _normalize_morpho_market_operation(operation)
|
|
1463
|
+
resolved_address = await self.get_address()
|
|
1464
|
+
payload: dict[str, Any] = {
|
|
1465
|
+
"walletId": self.wallet_id,
|
|
1466
|
+
"address": resolved_address,
|
|
1467
|
+
"accountIndex": self.account_index,
|
|
1468
|
+
"network": self.network,
|
|
1469
|
+
"tokenAddress": token_address,
|
|
1470
|
+
}
|
|
1471
|
+
if isinstance(market_id, str) and market_id.strip():
|
|
1472
|
+
payload["marketId"] = market_id.strip()
|
|
1473
|
+
if isinstance(market_preset, str) and market_preset.strip():
|
|
1474
|
+
payload["marketPreset"] = market_preset.strip()
|
|
1475
|
+
if amount_raw is not None:
|
|
1476
|
+
payload["amount"] = amount_raw
|
|
1477
|
+
if native_amount_raw is not None:
|
|
1478
|
+
payload["nativeAmount"] = native_amount_raw
|
|
1479
|
+
data = await self.client.post(
|
|
1480
|
+
f"/v1/evm/morpho/market/{normalized_operation}/quote",
|
|
1481
|
+
payload,
|
|
1482
|
+
)
|
|
1483
|
+
return _normalize_morpho_payload(
|
|
1484
|
+
chain=self.chain,
|
|
1485
|
+
network=self.network,
|
|
1486
|
+
wallet_id=self.wallet_id,
|
|
1487
|
+
address=resolved_address,
|
|
1488
|
+
operation=normalized_operation,
|
|
1489
|
+
token_address=token_address,
|
|
1490
|
+
amount_raw=amount_raw,
|
|
1491
|
+
native_amount_raw=native_amount_raw,
|
|
1492
|
+
data=data,
|
|
1493
|
+
sign_only=self.sign_only,
|
|
1494
|
+
)
|
|
1495
|
+
|
|
1496
|
+
async def send_evm_morpho_market_operation(
|
|
1497
|
+
self,
|
|
1498
|
+
*,
|
|
1499
|
+
operation: str,
|
|
1500
|
+
token_address: str,
|
|
1501
|
+
market_id: str | None = None,
|
|
1502
|
+
market_preset: str | None = None,
|
|
1503
|
+
amount_raw: str | None = None,
|
|
1504
|
+
native_amount_raw: str | None = None,
|
|
1505
|
+
expected_quote_fingerprint: str | None = None,
|
|
1506
|
+
) -> dict[str, Any]:
|
|
1507
|
+
if self.sign_only:
|
|
1508
|
+
raise WalletBackendError("wdk_evm_local is configured as sign_only.")
|
|
1509
|
+
normalized_operation = _normalize_morpho_market_operation(operation)
|
|
1510
|
+
payload: dict[str, Any] = {
|
|
1511
|
+
"walletId": self.wallet_id,
|
|
1512
|
+
"accountIndex": self.account_index,
|
|
1513
|
+
"network": self.network,
|
|
1514
|
+
"tokenAddress": token_address,
|
|
1515
|
+
}
|
|
1516
|
+
if isinstance(market_id, str) and market_id.strip():
|
|
1517
|
+
payload["marketId"] = market_id.strip()
|
|
1518
|
+
if isinstance(market_preset, str) and market_preset.strip():
|
|
1519
|
+
payload["marketPreset"] = market_preset.strip()
|
|
1520
|
+
if amount_raw is not None:
|
|
1521
|
+
payload["amount"] = amount_raw
|
|
1522
|
+
if native_amount_raw is not None:
|
|
1523
|
+
payload["nativeAmount"] = native_amount_raw
|
|
1524
|
+
if isinstance(expected_quote_fingerprint, str) and expected_quote_fingerprint.strip():
|
|
1525
|
+
payload["expectedQuoteFingerprint"] = expected_quote_fingerprint.strip()
|
|
1526
|
+
data = await self.client.post(
|
|
1527
|
+
f"/v1/evm/morpho/market/{normalized_operation}/send",
|
|
1528
|
+
payload,
|
|
1529
|
+
)
|
|
1530
|
+
return {
|
|
1531
|
+
**_normalize_morpho_payload(
|
|
1532
|
+
chain=self.chain,
|
|
1533
|
+
network=self.network,
|
|
1534
|
+
wallet_id=self.wallet_id,
|
|
1535
|
+
address=await self.get_address(),
|
|
1536
|
+
operation=normalized_operation,
|
|
1537
|
+
token_address=token_address,
|
|
1538
|
+
amount_raw=amount_raw,
|
|
1539
|
+
native_amount_raw=native_amount_raw,
|
|
1540
|
+
data=data,
|
|
1541
|
+
sign_only=self.sign_only,
|
|
1542
|
+
),
|
|
1543
|
+
"broadcasted": True,
|
|
1544
|
+
"confirmed": False,
|
|
1545
|
+
}
|
|
1546
|
+
|
|
1143
1547
|
async def get_evm_swap_quote(
|
|
1144
1548
|
self,
|
|
1145
1549
|
*,
|
|
@@ -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.47",
|
|
6
6
|
"skills": ["skills/wallet-operator"],
|
|
7
7
|
"configSchema": {
|
|
8
8
|
"type": "object",
|