@agentlayer.tech/wallet 0.1.44 → 0.1.48
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 +884 -37
- 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 +189 -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
|
@@ -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.48",
|
|
6
6
|
"skills": ["skills/wallet-operator"],
|
|
7
7
|
"configSchema": {
|
|
8
8
|
"type": "object",
|
|
@@ -469,6 +469,35 @@ def _replace_with_directory_symlink(link_path: Path, target_path: Path) -> None:
|
|
|
469
469
|
link_path.symlink_to(target_resolved, target_is_directory=True)
|
|
470
470
|
|
|
471
471
|
|
|
472
|
+
def _bootstrap_venv_pip(python_bin: Path) -> None:
|
|
473
|
+
"""Upgrade pip in a freshly created venv before installing dependencies.
|
|
474
|
+
|
|
475
|
+
A new venv ships with whatever pip ``ensurepip`` bundled, which on older
|
|
476
|
+
interpreters (e.g. a python.org 3.10 build) can be too old to select
|
|
477
|
+
prebuilt wheels for native dependencies such as ``cryptography`` and
|
|
478
|
+
``ckzg``. Without a matching wheel pip falls back to a source build that
|
|
479
|
+
needs a Rust/C toolchain and fails on machines that lack one. Upgrading pip
|
|
480
|
+
first keeps the editable install below on wheels.
|
|
481
|
+
"""
|
|
482
|
+
subprocess.run(
|
|
483
|
+
[str(python_bin), "-m", "pip", "install", "--upgrade", "pip", "setuptools", "wheel"],
|
|
484
|
+
check=True,
|
|
485
|
+
)
|
|
486
|
+
|
|
487
|
+
|
|
488
|
+
def _pip_install_editable(python_bin: Path, package_root: Path) -> None:
|
|
489
|
+
"""Editable-install the package, preferring prebuilt wheels for deps.
|
|
490
|
+
|
|
491
|
+
``--prefer-binary`` keeps pip on prebuilt wheels for native dependencies
|
|
492
|
+
instead of compiling them from source when a newer source-only release is
|
|
493
|
+
available, so the install does not require a local build toolchain.
|
|
494
|
+
"""
|
|
495
|
+
subprocess.run(
|
|
496
|
+
[str(python_bin), "-m", "pip", "install", "--prefer-binary", "-e", str(package_root)],
|
|
497
|
+
check=True,
|
|
498
|
+
)
|
|
499
|
+
|
|
500
|
+
|
|
472
501
|
def _ensure_python_runtime(
|
|
473
502
|
venv_path: Path,
|
|
474
503
|
package_root: Path,
|
|
@@ -483,10 +512,8 @@ def _ensure_python_runtime(
|
|
|
483
512
|
if not python_bin.exists():
|
|
484
513
|
venv.EnvBuilder(with_pip=True).create(shared_venv_path)
|
|
485
514
|
created = True
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
check=True,
|
|
489
|
-
)
|
|
515
|
+
_bootstrap_venv_pip(python_bin)
|
|
516
|
+
_pip_install_editable(python_bin, package_root)
|
|
490
517
|
shared_wrapper = _ensure_python_wrapper(shared_venv_path)
|
|
491
518
|
_replace_with_directory_symlink(venv_path, shared_venv_path)
|
|
492
519
|
plan["action"] = "create" if created else "reuse"
|
|
@@ -501,11 +528,9 @@ def _ensure_python_runtime(
|
|
|
501
528
|
if not python_bin.exists():
|
|
502
529
|
venv.EnvBuilder(with_pip=True).create(venv_path)
|
|
503
530
|
created = True
|
|
531
|
+
_bootstrap_venv_pip(python_bin)
|
|
504
532
|
|
|
505
|
-
|
|
506
|
-
[str(python_bin), "-m", "pip", "install", "-e", str(package_root)],
|
|
507
|
-
check=True,
|
|
508
|
-
)
|
|
533
|
+
_pip_install_editable(python_bin, package_root)
|
|
509
534
|
return (
|
|
510
535
|
_ensure_python_wrapper(venv_path),
|
|
511
536
|
created,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agent-wallet",
|
|
3
3
|
"displayName": "Agent Wallet",
|
|
4
|
-
"version": "0.1.
|
|
4
|
+
"version": "0.1.48",
|
|
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"
|
package/package.json
CHANGED
package/wdk-evm-wallet/README.md
CHANGED
|
@@ -26,6 +26,11 @@ Current scope:
|
|
|
26
26
|
- fetch Aave V3 reserve catalog on supported mainnet networks
|
|
27
27
|
- fetch Aave V3 per-reserve user positions on supported mainnet networks
|
|
28
28
|
- quote and send narrow Aave V3 `supply`, `withdraw`, `borrow`, and `repay` operations
|
|
29
|
+
- fetch Morpho vault discovery and detail data on supported mainnet networks
|
|
30
|
+
- fetch Morpho market discovery and detail data on supported mainnet networks
|
|
31
|
+
- fetch Morpho user vault and market positions on supported mainnet networks
|
|
32
|
+
- quote and send narrow Morpho vault `supply` and `withdraw` operations
|
|
33
|
+
- quote and send narrow Morpho market `supply_collateral`, `borrow`, `repay`, and `withdraw_collateral` operations
|
|
29
34
|
- quote and send native transfers
|
|
30
35
|
- quote and send ERC-20 transfers
|
|
31
36
|
- fetch transaction receipts
|
|
@@ -41,6 +46,8 @@ The implementation follows the official WDK documentation:
|
|
|
41
46
|
- Velora swap API reference: https://docs.wdk.tether.io/sdk/swap-modules/swap-velora-evm/api-reference
|
|
42
47
|
- Aave lending overview: https://docs.wdk.tether.io/sdk/lending-modules/lending-aave-evm
|
|
43
48
|
- Aave lending API reference: https://docs.wdk.tether.io/sdk/lending-modules/lending-aave-evm/api-reference
|
|
49
|
+
- Morpho Build overview: https://morpho.org/build
|
|
50
|
+
- Morpho offchain API docs: https://docs.morpho.org/tools/offchain/api/get-started/
|
|
44
51
|
|
|
45
52
|
## Why Separate
|
|
46
53
|
|
|
@@ -101,6 +108,21 @@ The active network is persistent and can be switched without changing code.
|
|
|
101
108
|
- `POST /v1/evm/aave/borrow/send`
|
|
102
109
|
- `POST /v1/evm/aave/repay/quote`
|
|
103
110
|
- `POST /v1/evm/aave/repay/send`
|
|
111
|
+
- `POST /v1/evm/morpho/vaults/get`
|
|
112
|
+
- `POST /v1/evm/morpho/markets/get`
|
|
113
|
+
- `POST /v1/evm/morpho/positions/get`
|
|
114
|
+
- `POST /v1/evm/morpho/vault/supply/quote`
|
|
115
|
+
- `POST /v1/evm/morpho/vault/supply/send`
|
|
116
|
+
- `POST /v1/evm/morpho/vault/withdraw/quote`
|
|
117
|
+
- `POST /v1/evm/morpho/vault/withdraw/send`
|
|
118
|
+
- `POST /v1/evm/morpho/market/supply_collateral/quote`
|
|
119
|
+
- `POST /v1/evm/morpho/market/supply_collateral/send`
|
|
120
|
+
- `POST /v1/evm/morpho/market/borrow/quote`
|
|
121
|
+
- `POST /v1/evm/morpho/market/borrow/send`
|
|
122
|
+
- `POST /v1/evm/morpho/market/repay/quote`
|
|
123
|
+
- `POST /v1/evm/morpho/market/repay/send`
|
|
124
|
+
- `POST /v1/evm/morpho/market/withdraw_collateral/quote`
|
|
125
|
+
- `POST /v1/evm/morpho/market/withdraw_collateral/send`
|
|
104
126
|
- `POST /v1/evm/swap/quote`
|
|
105
127
|
- `POST /v1/evm/swap/send`
|
|
106
128
|
- `POST /v1/evm/uniswap/swap/quote`
|
|
@@ -162,11 +184,20 @@ Environment variables:
|
|
|
162
184
|
- `WDK_EVM_SEPOLIA_RPC_URL`
|
|
163
185
|
- `WDK_EVM_BASE_RPC_URL`
|
|
164
186
|
- `WDK_EVM_BASE_SEPOLIA_RPC_URL`
|
|
187
|
+
- `MORPHO_API_BASE_URL`
|
|
165
188
|
- `UNISWAP_API_KEY`
|
|
166
189
|
- `UNISWAP_TRADING_API_BASE_URL`
|
|
167
190
|
- `UNISWAP_ROUTER_VERSION`
|
|
168
191
|
- `UNISWAP_DEFAULT_SLIPPAGE_BPS`
|
|
169
192
|
|
|
193
|
+
Morpho read-only support:
|
|
194
|
+
|
|
195
|
+
- the runtime exposes Morpho discovery and account-read routes through the public
|
|
196
|
+
Morpho GraphQL API at `https://api.morpho.org/graphql` by default
|
|
197
|
+
- Morpho support is currently limited to `ethereum` and `base` mainnet
|
|
198
|
+
- vault and market discovery use fixed first-party queries rather than caller-provided
|
|
199
|
+
GraphQL strings
|
|
200
|
+
|
|
170
201
|
Swap providers:
|
|
171
202
|
|
|
172
203
|
- the runtime exposes three independent swap surfaces: Velora (`/v1/evm/swap/*`),
|