@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.
@@ -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.44",
5
+ "version": "0.1.48",
6
6
  "skills": ["skills/wallet-operator"],
7
7
  "configSchema": {
8
8
  "type": "object",
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "openclaw-agent-wallet"
7
- version = "0.1.44"
7
+ version = "0.1.48"
8
8
  description = "Plugin-friendly wallet backend for OpenClaw agents"
9
9
  requires-python = ">=3.10"
10
10
  dependencies = [
@@ -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
- subprocess.run(
487
- [str(python_bin), "-m", "pip", "install", "-e", str(package_root)],
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
- subprocess.run(
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.44",
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"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agent-wallet",
3
- "version": "0.1.44",
3
+ "version": "0.1.48",
4
4
  "description": "Codex plugin bridge for the AgentLayer wallet runtime.",
5
5
  "author": {
6
6
  "name": "AgentLayer"
@@ -1,5 +1,5 @@
1
1
  name: agent-wallet
2
- version: 0.1.44
2
+ version: 0.1.48
3
3
  description: Thin Hermes Agent bridge to the existing AgentLayer/OpenClaw wallet backend
4
4
  provides_tools:
5
5
  - agent_wallet_tools
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agentlayer.tech/wallet",
3
- "version": "0.1.44",
3
+ "version": "0.1.48",
4
4
  "description": "NPM installer for the OpenClaw Agent Wallet local runtime.",
5
5
  "type": "module",
6
6
  "repository": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wdk-btc-wallet",
3
- "version": "0.1.44",
3
+ "version": "0.1.48",
4
4
  "private": true,
5
5
  "type": "module",
6
6
  "description": "Separate BTC-only wallet service built on Tether WDK.",
@@ -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/*`),