@agentlayer.tech/wallet 0.1.43 → 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/.claude-plugin/marketplace.json +17 -0
- 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 +79 -0
- package/README.md +16 -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/openclaw_cli.py +22 -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/telemetry.py +306 -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/claude-code/plugins/agent-wallet/README.md +30 -1
- package/claude-code/plugins/agent-wallet/commands/wallet-setup.md +24 -0
- package/claude-code/plugins/agent-wallet/hooks/hooks.json +16 -0
- package/claude-code/plugins/agent-wallet/scripts/bootstrap_backend.sh +136 -0
- package/claude-code/plugins/agent-wallet/scripts/run_mcp.sh +6 -1
- package/codex/plugins/agent-wallet/.codex-plugin/plugin.json +1 -1
- package/codex/plugins/agent-wallet/scripts/run_mcp.sh +5 -0
- package/hermes/plugins/agent_wallet/plugin.yaml +1 -1
- package/hermes/plugins/agent_wallet/tools.py +3 -0
- package/package.json +2 -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
|
@@ -496,6 +496,46 @@ class OpenClawWalletAdapter:
|
|
|
496
496
|
"quote_fingerprint": provided_fingerprint,
|
|
497
497
|
}
|
|
498
498
|
|
|
499
|
+
if asset_type == "evm-morpho-vault":
|
|
500
|
+
return {
|
|
501
|
+
"operation": action_label,
|
|
502
|
+
"network": str(payload.get("network") or getattr(self.backend, "network", "unknown")),
|
|
503
|
+
"wallet": payload.get("wallet"),
|
|
504
|
+
"from_address": payload.get("from_address"),
|
|
505
|
+
"protocol": payload.get("protocol"),
|
|
506
|
+
"morpho_surface": payload.get("surface"),
|
|
507
|
+
"morpho_operation": payload.get("operation"),
|
|
508
|
+
"target": payload.get("target"),
|
|
509
|
+
"token_address": payload.get("token_address"),
|
|
510
|
+
"amount_raw": payload.get("amount_raw"),
|
|
511
|
+
"native_amount_raw": payload.get("native_amount_raw"),
|
|
512
|
+
"estimated_fee_wei": payload.get("estimated_fee_wei"),
|
|
513
|
+
"estimated_operation_fee_wei": payload.get("estimated_operation_fee_wei"),
|
|
514
|
+
"estimated_requirements_fee_wei": payload.get("estimated_requirements_fee_wei"),
|
|
515
|
+
"quote_fingerprint": payload.get("quote_fingerprint"),
|
|
516
|
+
"requirements": payload.get("requirements"),
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
if asset_type == "evm-morpho-market":
|
|
520
|
+
return {
|
|
521
|
+
"operation": action_label,
|
|
522
|
+
"network": str(payload.get("network") or getattr(self.backend, "network", "unknown")),
|
|
523
|
+
"wallet": payload.get("wallet"),
|
|
524
|
+
"from_address": payload.get("from_address"),
|
|
525
|
+
"protocol": payload.get("protocol"),
|
|
526
|
+
"morpho_surface": payload.get("surface"),
|
|
527
|
+
"morpho_operation": payload.get("operation"),
|
|
528
|
+
"target": payload.get("target"),
|
|
529
|
+
"token_address": payload.get("token_address"),
|
|
530
|
+
"amount_raw": payload.get("amount_raw"),
|
|
531
|
+
"native_amount_raw": payload.get("native_amount_raw"),
|
|
532
|
+
"estimated_fee_wei": payload.get("estimated_fee_wei"),
|
|
533
|
+
"estimated_operation_fee_wei": payload.get("estimated_operation_fee_wei"),
|
|
534
|
+
"estimated_requirements_fee_wei": payload.get("estimated_requirements_fee_wei"),
|
|
535
|
+
"quote_fingerprint": payload.get("quote_fingerprint"),
|
|
536
|
+
"requirements": payload.get("requirements"),
|
|
537
|
+
}
|
|
538
|
+
|
|
499
539
|
if asset_type == "evm-aave-v3":
|
|
500
540
|
return {
|
|
501
541
|
"operation": action_label,
|
|
@@ -1465,6 +1505,263 @@ class OpenClawWalletAdapter:
|
|
|
1465
1505
|
risk_level="high",
|
|
1466
1506
|
),
|
|
1467
1507
|
)
|
|
1508
|
+
tools.insert(
|
|
1509
|
+
15,
|
|
1510
|
+
AgentToolSpec(
|
|
1511
|
+
name="get_evm_morpho_vaults",
|
|
1512
|
+
description="Get read-only Morpho vault discovery and detail data for the configured EVM network on supported mainnet chains. When listing, results are ordered (default: largest TVL first) and can be filtered by underlying asset.",
|
|
1513
|
+
input_schema={
|
|
1514
|
+
"type": "object",
|
|
1515
|
+
"properties": {
|
|
1516
|
+
"vault_address": {
|
|
1517
|
+
"type": "string",
|
|
1518
|
+
"description": "Optional explicit Morpho vault address for a single-vault lookup.",
|
|
1519
|
+
},
|
|
1520
|
+
"limit": {
|
|
1521
|
+
"type": "integer",
|
|
1522
|
+
"minimum": 1,
|
|
1523
|
+
"maximum": 500,
|
|
1524
|
+
"description": "Optional max number of vaults to return when listing.",
|
|
1525
|
+
},
|
|
1526
|
+
"listed_only": {
|
|
1527
|
+
"type": "boolean",
|
|
1528
|
+
"description": "Filter to listed vaults only. Defaults to true.",
|
|
1529
|
+
},
|
|
1530
|
+
"asset_address": {
|
|
1531
|
+
"type": "string",
|
|
1532
|
+
"description": "Optional underlying asset address to filter vaults (e.g. only USDC vaults).",
|
|
1533
|
+
},
|
|
1534
|
+
"order_by": {
|
|
1535
|
+
"type": "string",
|
|
1536
|
+
"enum": [
|
|
1537
|
+
"TotalAssetsUsd",
|
|
1538
|
+
"TotalAssets",
|
|
1539
|
+
"TotalSupply",
|
|
1540
|
+
"Liquidity",
|
|
1541
|
+
"LiquidityUsd",
|
|
1542
|
+
"Apy",
|
|
1543
|
+
"NetApy",
|
|
1544
|
+
"Address",
|
|
1545
|
+
],
|
|
1546
|
+
"description": "Optional sort field when listing. Defaults to TotalAssetsUsd (largest TVL first).",
|
|
1547
|
+
},
|
|
1548
|
+
"order_direction": {
|
|
1549
|
+
"type": "string",
|
|
1550
|
+
"enum": ["asc", "desc"],
|
|
1551
|
+
"description": "Optional sort direction. Defaults to desc.",
|
|
1552
|
+
},
|
|
1553
|
+
"network": {
|
|
1554
|
+
"type": "string",
|
|
1555
|
+
"enum": ["ethereum", "base"],
|
|
1556
|
+
"description": "Optional EVM network override for this request.",
|
|
1557
|
+
},
|
|
1558
|
+
},
|
|
1559
|
+
"additionalProperties": False,
|
|
1560
|
+
},
|
|
1561
|
+
read_only=True,
|
|
1562
|
+
risk_level="low",
|
|
1563
|
+
),
|
|
1564
|
+
)
|
|
1565
|
+
tools.insert(
|
|
1566
|
+
16,
|
|
1567
|
+
AgentToolSpec(
|
|
1568
|
+
name="get_evm_morpho_markets",
|
|
1569
|
+
description="Get read-only Morpho market discovery and detail data for the configured EVM network on supported mainnet chains. When listing, results are ordered (default: largest supply first) and can be filtered by a free-text search or by collateral/loan asset.",
|
|
1570
|
+
input_schema={
|
|
1571
|
+
"type": "object",
|
|
1572
|
+
"properties": {
|
|
1573
|
+
"market_id": {
|
|
1574
|
+
"type": "string",
|
|
1575
|
+
"description": "Optional explicit Morpho market id (32-byte hex) for a single-market lookup.",
|
|
1576
|
+
},
|
|
1577
|
+
"limit": {
|
|
1578
|
+
"type": "integer",
|
|
1579
|
+
"minimum": 1,
|
|
1580
|
+
"maximum": 500,
|
|
1581
|
+
"description": "Optional max number of markets to return when listing.",
|
|
1582
|
+
},
|
|
1583
|
+
"listed_only": {
|
|
1584
|
+
"type": "boolean",
|
|
1585
|
+
"description": "Filter to listed markets only. Defaults to true.",
|
|
1586
|
+
},
|
|
1587
|
+
"search": {
|
|
1588
|
+
"type": "string",
|
|
1589
|
+
"description": "Optional free-text search over market/asset symbols (e.g. 'wstETH').",
|
|
1590
|
+
},
|
|
1591
|
+
"collateral_asset_address": {
|
|
1592
|
+
"type": "string",
|
|
1593
|
+
"description": "Optional collateral asset address to filter markets.",
|
|
1594
|
+
},
|
|
1595
|
+
"loan_asset_address": {
|
|
1596
|
+
"type": "string",
|
|
1597
|
+
"description": "Optional loan asset address to filter markets.",
|
|
1598
|
+
},
|
|
1599
|
+
"order_by": {
|
|
1600
|
+
"type": "string",
|
|
1601
|
+
"enum": [
|
|
1602
|
+
"SupplyAssetsUsd",
|
|
1603
|
+
"BorrowAssetsUsd",
|
|
1604
|
+
"SupplyApy",
|
|
1605
|
+
"NetSupplyApy",
|
|
1606
|
+
"BorrowApy",
|
|
1607
|
+
"NetBorrowApy",
|
|
1608
|
+
"Utilization",
|
|
1609
|
+
"TotalLiquidityUsd",
|
|
1610
|
+
"Lltv",
|
|
1611
|
+
],
|
|
1612
|
+
"description": "Optional sort field when listing. Defaults to SupplyAssetsUsd (largest supply first).",
|
|
1613
|
+
},
|
|
1614
|
+
"order_direction": {
|
|
1615
|
+
"type": "string",
|
|
1616
|
+
"enum": ["asc", "desc"],
|
|
1617
|
+
"description": "Optional sort direction. Defaults to desc.",
|
|
1618
|
+
},
|
|
1619
|
+
"network": {
|
|
1620
|
+
"type": "string",
|
|
1621
|
+
"enum": ["ethereum", "base"],
|
|
1622
|
+
"description": "Optional EVM network override for this request.",
|
|
1623
|
+
},
|
|
1624
|
+
},
|
|
1625
|
+
"additionalProperties": False,
|
|
1626
|
+
},
|
|
1627
|
+
read_only=True,
|
|
1628
|
+
risk_level="low",
|
|
1629
|
+
),
|
|
1630
|
+
)
|
|
1631
|
+
tools.insert(
|
|
1632
|
+
17,
|
|
1633
|
+
AgentToolSpec(
|
|
1634
|
+
name="get_evm_morpho_positions",
|
|
1635
|
+
description="Get read-only Morpho vault and market positions for the configured EVM wallet on supported mainnet chains.",
|
|
1636
|
+
input_schema={
|
|
1637
|
+
"type": "object",
|
|
1638
|
+
"properties": {
|
|
1639
|
+
"network": {
|
|
1640
|
+
"type": "string",
|
|
1641
|
+
"enum": ["ethereum", "base"],
|
|
1642
|
+
"description": "Optional EVM network override for this request.",
|
|
1643
|
+
},
|
|
1644
|
+
},
|
|
1645
|
+
"additionalProperties": False,
|
|
1646
|
+
},
|
|
1647
|
+
read_only=True,
|
|
1648
|
+
risk_level="low",
|
|
1649
|
+
),
|
|
1650
|
+
)
|
|
1651
|
+
tools.insert(
|
|
1652
|
+
18,
|
|
1653
|
+
AgentToolSpec(
|
|
1654
|
+
name="manage_evm_morpho_vault_position",
|
|
1655
|
+
description=(
|
|
1656
|
+
"Preview, prepare, or execute a narrow Morpho vault operation on supported EVM mainnet networks. "
|
|
1657
|
+
"Supported operations are supply and withdraw. Prepare returns an execution plan only, "
|
|
1658
|
+
"and execute requires a host-issued approval token bound to the previewed operation."
|
|
1659
|
+
),
|
|
1660
|
+
input_schema={
|
|
1661
|
+
"type": "object",
|
|
1662
|
+
"properties": {
|
|
1663
|
+
"operation": {
|
|
1664
|
+
"type": "string",
|
|
1665
|
+
"enum": ["supply", "withdraw"],
|
|
1666
|
+
},
|
|
1667
|
+
"token_address": {
|
|
1668
|
+
"type": "string",
|
|
1669
|
+
"description": "Vault asset token address.",
|
|
1670
|
+
},
|
|
1671
|
+
"vault_address": {
|
|
1672
|
+
"type": "string",
|
|
1673
|
+
"description": "Optional explicit Morpho vault address.",
|
|
1674
|
+
},
|
|
1675
|
+
"vault_preset": {
|
|
1676
|
+
"type": "string",
|
|
1677
|
+
"description": "Optional Morpho vault preset name.",
|
|
1678
|
+
},
|
|
1679
|
+
"amount_raw": {
|
|
1680
|
+
"type": "string",
|
|
1681
|
+
"description": "Asset amount in base units. Required for withdraw and optional for supply when native_amount_raw is used.",
|
|
1682
|
+
},
|
|
1683
|
+
"native_amount_raw": {
|
|
1684
|
+
"type": "string",
|
|
1685
|
+
"description": "Optional native-wrap amount in base units for supply.",
|
|
1686
|
+
},
|
|
1687
|
+
"mode": {
|
|
1688
|
+
"type": "string",
|
|
1689
|
+
"enum": ["preview", "prepare", "execute"],
|
|
1690
|
+
},
|
|
1691
|
+
"purpose": {"type": "string"},
|
|
1692
|
+
"user_intent": {"type": "boolean"},
|
|
1693
|
+
"approval_token": {"type": "string"},
|
|
1694
|
+
"network": {
|
|
1695
|
+
"type": "string",
|
|
1696
|
+
"enum": ["ethereum", "base"],
|
|
1697
|
+
"description": "Optional EVM network override for this request.",
|
|
1698
|
+
},
|
|
1699
|
+
},
|
|
1700
|
+
"required": ["operation", "token_address", "mode", "purpose"],
|
|
1701
|
+
"additionalProperties": False,
|
|
1702
|
+
},
|
|
1703
|
+
read_only=False,
|
|
1704
|
+
requires_explicit_user_intent=True,
|
|
1705
|
+
risk_level="high",
|
|
1706
|
+
),
|
|
1707
|
+
)
|
|
1708
|
+
tools.insert(
|
|
1709
|
+
19,
|
|
1710
|
+
AgentToolSpec(
|
|
1711
|
+
name="manage_evm_morpho_market_position",
|
|
1712
|
+
description=(
|
|
1713
|
+
"Preview, prepare, or execute a narrow Morpho market operation on supported EVM mainnet networks. "
|
|
1714
|
+
"Supported operations are supply_collateral, borrow, repay, and withdraw_collateral. "
|
|
1715
|
+
"Prepare returns an execution plan only, and execute requires a host-issued approval token bound to the previewed operation."
|
|
1716
|
+
),
|
|
1717
|
+
input_schema={
|
|
1718
|
+
"type": "object",
|
|
1719
|
+
"properties": {
|
|
1720
|
+
"operation": {
|
|
1721
|
+
"type": "string",
|
|
1722
|
+
"enum": ["supply_collateral", "borrow", "repay", "withdraw_collateral"],
|
|
1723
|
+
},
|
|
1724
|
+
"token_address": {
|
|
1725
|
+
"type": "string",
|
|
1726
|
+
"description": "Loan token or collateral token address for the selected operation.",
|
|
1727
|
+
},
|
|
1728
|
+
"market_id": {
|
|
1729
|
+
"type": "string",
|
|
1730
|
+
"description": "Optional explicit Morpho market id.",
|
|
1731
|
+
},
|
|
1732
|
+
"market_preset": {
|
|
1733
|
+
"type": "string",
|
|
1734
|
+
"description": "Optional Morpho market preset name.",
|
|
1735
|
+
},
|
|
1736
|
+
"amount_raw": {
|
|
1737
|
+
"type": "string",
|
|
1738
|
+
"description": "Operation amount in base units. For repay, use a positive integer string or the literal max.",
|
|
1739
|
+
},
|
|
1740
|
+
"native_amount_raw": {
|
|
1741
|
+
"type": "string",
|
|
1742
|
+
"description": "Optional native-wrap amount in base units for supply_collateral.",
|
|
1743
|
+
},
|
|
1744
|
+
"mode": {
|
|
1745
|
+
"type": "string",
|
|
1746
|
+
"enum": ["preview", "prepare", "execute"],
|
|
1747
|
+
},
|
|
1748
|
+
"purpose": {"type": "string"},
|
|
1749
|
+
"user_intent": {"type": "boolean"},
|
|
1750
|
+
"approval_token": {"type": "string"},
|
|
1751
|
+
"network": {
|
|
1752
|
+
"type": "string",
|
|
1753
|
+
"enum": ["ethereum", "base"],
|
|
1754
|
+
"description": "Optional EVM network override for this request.",
|
|
1755
|
+
},
|
|
1756
|
+
},
|
|
1757
|
+
"required": ["operation", "token_address", "mode", "purpose"],
|
|
1758
|
+
"additionalProperties": False,
|
|
1759
|
+
},
|
|
1760
|
+
read_only=False,
|
|
1761
|
+
requires_explicit_user_intent=True,
|
|
1762
|
+
risk_level="high",
|
|
1763
|
+
),
|
|
1764
|
+
)
|
|
1468
1765
|
tools.insert(
|
|
1469
1766
|
8,
|
|
1470
1767
|
AgentToolSpec(
|
|
@@ -3607,6 +3904,395 @@ class OpenClawWalletAdapter:
|
|
|
3607
3904
|
),
|
|
3608
3905
|
)
|
|
3609
3906
|
|
|
3907
|
+
if tool_name == "get_evm_morpho_vaults":
|
|
3908
|
+
data = await active_backend.get_evm_morpho_vaults(
|
|
3909
|
+
vault_address=(
|
|
3910
|
+
str(args.get("vault_address")).strip()
|
|
3911
|
+
if isinstance(args.get("vault_address"), str) and args.get("vault_address").strip()
|
|
3912
|
+
else None
|
|
3913
|
+
),
|
|
3914
|
+
limit=int(args.get("limit")) if args.get("limit") is not None else None,
|
|
3915
|
+
listed_only=bool(args.get("listed_only", True)),
|
|
3916
|
+
asset_address=(
|
|
3917
|
+
str(args.get("asset_address")).strip()
|
|
3918
|
+
if isinstance(args.get("asset_address"), str) and args.get("asset_address").strip()
|
|
3919
|
+
else None
|
|
3920
|
+
),
|
|
3921
|
+
order_by=(
|
|
3922
|
+
str(args.get("order_by")).strip()
|
|
3923
|
+
if isinstance(args.get("order_by"), str) and args.get("order_by").strip()
|
|
3924
|
+
else None
|
|
3925
|
+
),
|
|
3926
|
+
order_direction=(
|
|
3927
|
+
str(args.get("order_direction")).strip()
|
|
3928
|
+
if isinstance(args.get("order_direction"), str) and args.get("order_direction").strip()
|
|
3929
|
+
else None
|
|
3930
|
+
),
|
|
3931
|
+
)
|
|
3932
|
+
return AgentToolResult(tool=tool_name, ok=True, data=data)
|
|
3933
|
+
|
|
3934
|
+
if tool_name == "get_evm_morpho_markets":
|
|
3935
|
+
data = await active_backend.get_evm_morpho_markets(
|
|
3936
|
+
market_id=(
|
|
3937
|
+
str(args.get("market_id")).strip()
|
|
3938
|
+
if isinstance(args.get("market_id"), str) and args.get("market_id").strip()
|
|
3939
|
+
else None
|
|
3940
|
+
),
|
|
3941
|
+
limit=int(args.get("limit")) if args.get("limit") is not None else None,
|
|
3942
|
+
listed_only=bool(args.get("listed_only", True)),
|
|
3943
|
+
search=(
|
|
3944
|
+
str(args.get("search")).strip()
|
|
3945
|
+
if isinstance(args.get("search"), str) and args.get("search").strip()
|
|
3946
|
+
else None
|
|
3947
|
+
),
|
|
3948
|
+
collateral_asset_address=(
|
|
3949
|
+
str(args.get("collateral_asset_address")).strip()
|
|
3950
|
+
if isinstance(args.get("collateral_asset_address"), str)
|
|
3951
|
+
and args.get("collateral_asset_address").strip()
|
|
3952
|
+
else None
|
|
3953
|
+
),
|
|
3954
|
+
loan_asset_address=(
|
|
3955
|
+
str(args.get("loan_asset_address")).strip()
|
|
3956
|
+
if isinstance(args.get("loan_asset_address"), str)
|
|
3957
|
+
and args.get("loan_asset_address").strip()
|
|
3958
|
+
else None
|
|
3959
|
+
),
|
|
3960
|
+
order_by=(
|
|
3961
|
+
str(args.get("order_by")).strip()
|
|
3962
|
+
if isinstance(args.get("order_by"), str) and args.get("order_by").strip()
|
|
3963
|
+
else None
|
|
3964
|
+
),
|
|
3965
|
+
order_direction=(
|
|
3966
|
+
str(args.get("order_direction")).strip()
|
|
3967
|
+
if isinstance(args.get("order_direction"), str) and args.get("order_direction").strip()
|
|
3968
|
+
else None
|
|
3969
|
+
),
|
|
3970
|
+
)
|
|
3971
|
+
return AgentToolResult(tool=tool_name, ok=True, data=data)
|
|
3972
|
+
|
|
3973
|
+
if tool_name == "get_evm_morpho_positions":
|
|
3974
|
+
data = await active_backend.get_evm_morpho_positions()
|
|
3975
|
+
return AgentToolResult(tool=tool_name, ok=True, data=data)
|
|
3976
|
+
|
|
3977
|
+
if tool_name == "manage_evm_morpho_vault_position":
|
|
3978
|
+
operation = args.get("operation")
|
|
3979
|
+
token_address = args.get("token_address")
|
|
3980
|
+
vault_address = args.get("vault_address")
|
|
3981
|
+
vault_preset = args.get("vault_preset")
|
|
3982
|
+
amount_raw = args.get("amount_raw")
|
|
3983
|
+
native_amount_raw = args.get("native_amount_raw")
|
|
3984
|
+
mode = args.get("mode")
|
|
3985
|
+
purpose = args.get("purpose")
|
|
3986
|
+
user_intent = args.get("user_intent", False)
|
|
3987
|
+
approval_token = args.get("approval_token")
|
|
3988
|
+
|
|
3989
|
+
if operation not in {"supply", "withdraw"}:
|
|
3990
|
+
raise WalletBackendError("operation must be one of: supply, withdraw.")
|
|
3991
|
+
if not isinstance(token_address, str) or not token_address.strip():
|
|
3992
|
+
raise WalletBackendError("token_address is required.")
|
|
3993
|
+
if bool(vault_address) == bool(vault_preset):
|
|
3994
|
+
raise WalletBackendError("Provide exactly one of vault_address or vault_preset.")
|
|
3995
|
+
if operation == "supply":
|
|
3996
|
+
has_amount = isinstance(amount_raw, str) and amount_raw.strip().isdigit() and int(amount_raw.strip()) > 0
|
|
3997
|
+
has_native_amount = (
|
|
3998
|
+
isinstance(native_amount_raw, str)
|
|
3999
|
+
and native_amount_raw.strip().isdigit()
|
|
4000
|
+
and int(native_amount_raw.strip()) > 0
|
|
4001
|
+
)
|
|
4002
|
+
if not has_amount and not has_native_amount:
|
|
4003
|
+
raise WalletBackendError(
|
|
4004
|
+
"amount_raw or native_amount_raw must be a positive integer string for Morpho vault supply."
|
|
4005
|
+
)
|
|
4006
|
+
else:
|
|
4007
|
+
if not isinstance(amount_raw, str) or not amount_raw.strip().isdigit():
|
|
4008
|
+
raise WalletBackendError("amount_raw must be a positive integer string.")
|
|
4009
|
+
if int(amount_raw.strip()) <= 0:
|
|
4010
|
+
raise WalletBackendError("amount_raw must be greater than zero.")
|
|
4011
|
+
if mode not in {"preview", "prepare", "execute"}:
|
|
4012
|
+
raise WalletBackendError("mode must be 'preview', 'prepare' or 'execute'.")
|
|
4013
|
+
if not isinstance(purpose, str) or not purpose.strip():
|
|
4014
|
+
raise WalletBackendError("purpose is required.")
|
|
4015
|
+
|
|
4016
|
+
preview_kwargs = {
|
|
4017
|
+
"operation": str(operation),
|
|
4018
|
+
"token_address": token_address.strip(),
|
|
4019
|
+
**({"vault_address": vault_address.strip()} if isinstance(vault_address, str) and vault_address.strip() else {}),
|
|
4020
|
+
**({"vault_preset": vault_preset.strip()} if isinstance(vault_preset, str) and vault_preset.strip() else {}),
|
|
4021
|
+
**({"amount_raw": amount_raw.strip()} if isinstance(amount_raw, str) and amount_raw.strip() else {}),
|
|
4022
|
+
**(
|
|
4023
|
+
{"native_amount_raw": native_amount_raw.strip()}
|
|
4024
|
+
if isinstance(native_amount_raw, str) and native_amount_raw.strip()
|
|
4025
|
+
else {}
|
|
4026
|
+
),
|
|
4027
|
+
}
|
|
4028
|
+
|
|
4029
|
+
if mode == "preview":
|
|
4030
|
+
preview = await active_backend.preview_evm_morpho_vault_operation(**preview_kwargs)
|
|
4031
|
+
return AgentToolResult(
|
|
4032
|
+
tool=tool_name,
|
|
4033
|
+
ok=True,
|
|
4034
|
+
data=self._annotate_sensitive_payload(
|
|
4035
|
+
preview,
|
|
4036
|
+
action_label="EVM Morpho vault operation",
|
|
4037
|
+
mode="preview",
|
|
4038
|
+
),
|
|
4039
|
+
)
|
|
4040
|
+
|
|
4041
|
+
if mode == "prepare":
|
|
4042
|
+
self._require_prepare_intent(user_intent)
|
|
4043
|
+
preview = await active_backend.preview_evm_morpho_vault_operation(**preview_kwargs)
|
|
4044
|
+
return AgentToolResult(
|
|
4045
|
+
tool=tool_name,
|
|
4046
|
+
ok=True,
|
|
4047
|
+
data=self._annotate_sensitive_payload(
|
|
4048
|
+
self._build_prepare_plan(
|
|
4049
|
+
preview_payload=preview,
|
|
4050
|
+
action_label="EVM Morpho vault operation",
|
|
4051
|
+
),
|
|
4052
|
+
action_label="EVM Morpho vault operation",
|
|
4053
|
+
mode="prepare",
|
|
4054
|
+
),
|
|
4055
|
+
)
|
|
4056
|
+
|
|
4057
|
+
approval_payload = inspect_approval_token(
|
|
4058
|
+
approval_token,
|
|
4059
|
+
tool_name=tool_name,
|
|
4060
|
+
network=str(getattr(active_backend, "network", "unknown")),
|
|
4061
|
+
require_mainnet_confirmation=self._is_mainnet_for_backend(active_backend),
|
|
4062
|
+
)
|
|
4063
|
+
approval_summary = approval_payload.get("binding", {}).get("summary")
|
|
4064
|
+
if not isinstance(approval_summary, dict):
|
|
4065
|
+
raise WalletBackendError(
|
|
4066
|
+
"approval_token does not match the requested operation. Generate a new approval after previewing the exact action."
|
|
4067
|
+
)
|
|
4068
|
+
expected_summary = {
|
|
4069
|
+
"operation": "EVM Morpho vault operation",
|
|
4070
|
+
"network": str(getattr(active_backend, "network", "unknown")),
|
|
4071
|
+
"morpho_surface": "vault",
|
|
4072
|
+
"morpho_operation": str(operation),
|
|
4073
|
+
"token_address": token_address.strip(),
|
|
4074
|
+
}
|
|
4075
|
+
if isinstance(amount_raw, str) and amount_raw.strip():
|
|
4076
|
+
expected_summary["amount_raw"] = amount_raw.strip()
|
|
4077
|
+
if isinstance(native_amount_raw, str) and native_amount_raw.strip():
|
|
4078
|
+
expected_summary["native_amount_raw"] = native_amount_raw.strip()
|
|
4079
|
+
target = approval_summary.get("target")
|
|
4080
|
+
if not isinstance(target, dict):
|
|
4081
|
+
raise WalletBackendError(
|
|
4082
|
+
"approval_token does not match the requested operation. Generate a new approval after previewing the exact action."
|
|
4083
|
+
)
|
|
4084
|
+
expected_target_key = "vaultAddress" if vault_address else "vaultPreset"
|
|
4085
|
+
expected_target_value = vault_address.strip() if vault_address else vault_preset.strip()
|
|
4086
|
+
for key, expected_value in expected_summary.items():
|
|
4087
|
+
if approval_summary.get(key) != expected_value:
|
|
4088
|
+
raise WalletBackendError(
|
|
4089
|
+
"approval_token does not match the requested operation. Generate a new approval after previewing the exact action."
|
|
4090
|
+
)
|
|
4091
|
+
actual_target_value = target.get(expected_target_key)
|
|
4092
|
+
if vault_address:
|
|
4093
|
+
# The runtime lowercases the resolved vault address in the
|
|
4094
|
+
# approval binding, so compare addresses case-insensitively.
|
|
4095
|
+
target_matches = (
|
|
4096
|
+
isinstance(actual_target_value, str)
|
|
4097
|
+
and actual_target_value.lower() == expected_target_value.lower()
|
|
4098
|
+
)
|
|
4099
|
+
else:
|
|
4100
|
+
target_matches = actual_target_value == expected_target_value
|
|
4101
|
+
if not target_matches:
|
|
4102
|
+
raise WalletBackendError(
|
|
4103
|
+
"approval_token does not match the requested operation. Generate a new approval after previewing the exact action."
|
|
4104
|
+
)
|
|
4105
|
+
|
|
4106
|
+
approval_summary_copy = dict(approval_summary)
|
|
4107
|
+
self._require_execute_approval(
|
|
4108
|
+
approval_token=approval_token,
|
|
4109
|
+
tool_name=tool_name,
|
|
4110
|
+
summary=approval_summary_copy,
|
|
4111
|
+
action_label="EVM Morpho vault operation",
|
|
4112
|
+
backend=active_backend,
|
|
4113
|
+
)
|
|
4114
|
+
result = await active_backend.send_evm_morpho_vault_operation(
|
|
4115
|
+
**preview_kwargs,
|
|
4116
|
+
expected_quote_fingerprint=(
|
|
4117
|
+
str(approval_summary_copy.get("quote_fingerprint")).strip()
|
|
4118
|
+
if approval_summary_copy.get("quote_fingerprint") is not None
|
|
4119
|
+
else None
|
|
4120
|
+
),
|
|
4121
|
+
)
|
|
4122
|
+
return AgentToolResult(
|
|
4123
|
+
tool=tool_name,
|
|
4124
|
+
ok=True,
|
|
4125
|
+
data=self._annotate_sensitive_payload(
|
|
4126
|
+
result,
|
|
4127
|
+
action_label="EVM Morpho vault operation",
|
|
4128
|
+
mode="execute",
|
|
4129
|
+
),
|
|
4130
|
+
)
|
|
4131
|
+
|
|
4132
|
+
if tool_name == "manage_evm_morpho_market_position":
|
|
4133
|
+
operation = args.get("operation")
|
|
4134
|
+
token_address = args.get("token_address")
|
|
4135
|
+
market_id = args.get("market_id")
|
|
4136
|
+
market_preset = args.get("market_preset")
|
|
4137
|
+
amount_raw = args.get("amount_raw")
|
|
4138
|
+
native_amount_raw = args.get("native_amount_raw")
|
|
4139
|
+
mode = args.get("mode")
|
|
4140
|
+
purpose = args.get("purpose")
|
|
4141
|
+
user_intent = args.get("user_intent", False)
|
|
4142
|
+
approval_token = args.get("approval_token")
|
|
4143
|
+
|
|
4144
|
+
if operation not in {"supply_collateral", "borrow", "repay", "withdraw_collateral"}:
|
|
4145
|
+
raise WalletBackendError(
|
|
4146
|
+
"operation must be one of: supply_collateral, borrow, repay, withdraw_collateral."
|
|
4147
|
+
)
|
|
4148
|
+
if not isinstance(token_address, str) or not token_address.strip():
|
|
4149
|
+
raise WalletBackendError("token_address is required.")
|
|
4150
|
+
if bool(market_id) == bool(market_preset):
|
|
4151
|
+
raise WalletBackendError("Provide exactly one of market_id or market_preset.")
|
|
4152
|
+
if operation == "supply_collateral":
|
|
4153
|
+
has_amount = isinstance(amount_raw, str) and amount_raw.strip().isdigit() and int(amount_raw.strip()) > 0
|
|
4154
|
+
has_native_amount = (
|
|
4155
|
+
isinstance(native_amount_raw, str)
|
|
4156
|
+
and native_amount_raw.strip().isdigit()
|
|
4157
|
+
and int(native_amount_raw.strip()) > 0
|
|
4158
|
+
)
|
|
4159
|
+
if not has_amount and not has_native_amount:
|
|
4160
|
+
raise WalletBackendError(
|
|
4161
|
+
"amount_raw or native_amount_raw must be a positive integer string for Morpho supply_collateral."
|
|
4162
|
+
)
|
|
4163
|
+
elif operation == "repay":
|
|
4164
|
+
normalized_amount = str(amount_raw or "").strip().lower()
|
|
4165
|
+
if normalized_amount != "max":
|
|
4166
|
+
if not normalized_amount.isdigit() or int(normalized_amount) <= 0:
|
|
4167
|
+
raise WalletBackendError(
|
|
4168
|
+
"amount_raw must be a positive integer string or the literal max for Morpho repay."
|
|
4169
|
+
)
|
|
4170
|
+
else:
|
|
4171
|
+
if not isinstance(amount_raw, str) or not amount_raw.strip().isdigit():
|
|
4172
|
+
raise WalletBackendError("amount_raw must be a positive integer string.")
|
|
4173
|
+
if int(amount_raw.strip()) <= 0:
|
|
4174
|
+
raise WalletBackendError("amount_raw must be greater than zero.")
|
|
4175
|
+
if mode not in {"preview", "prepare", "execute"}:
|
|
4176
|
+
raise WalletBackendError("mode must be 'preview', 'prepare' or 'execute'.")
|
|
4177
|
+
if not isinstance(purpose, str) or not purpose.strip():
|
|
4178
|
+
raise WalletBackendError("purpose is required.")
|
|
4179
|
+
|
|
4180
|
+
preview_kwargs = {
|
|
4181
|
+
"operation": str(operation),
|
|
4182
|
+
"token_address": token_address.strip(),
|
|
4183
|
+
**({"market_id": market_id.strip()} if isinstance(market_id, str) and market_id.strip() else {}),
|
|
4184
|
+
**({"market_preset": market_preset.strip()} if isinstance(market_preset, str) and market_preset.strip() else {}),
|
|
4185
|
+
**({"amount_raw": amount_raw.strip()} if isinstance(amount_raw, str) and amount_raw.strip() else {}),
|
|
4186
|
+
**(
|
|
4187
|
+
{"native_amount_raw": native_amount_raw.strip()}
|
|
4188
|
+
if isinstance(native_amount_raw, str) and native_amount_raw.strip()
|
|
4189
|
+
else {}
|
|
4190
|
+
),
|
|
4191
|
+
}
|
|
4192
|
+
|
|
4193
|
+
if mode == "preview":
|
|
4194
|
+
preview = await active_backend.preview_evm_morpho_market_operation(**preview_kwargs)
|
|
4195
|
+
return AgentToolResult(
|
|
4196
|
+
tool=tool_name,
|
|
4197
|
+
ok=True,
|
|
4198
|
+
data=self._annotate_sensitive_payload(
|
|
4199
|
+
preview,
|
|
4200
|
+
action_label="EVM Morpho market operation",
|
|
4201
|
+
mode="preview",
|
|
4202
|
+
),
|
|
4203
|
+
)
|
|
4204
|
+
|
|
4205
|
+
if mode == "prepare":
|
|
4206
|
+
self._require_prepare_intent(user_intent)
|
|
4207
|
+
preview = await active_backend.preview_evm_morpho_market_operation(**preview_kwargs)
|
|
4208
|
+
return AgentToolResult(
|
|
4209
|
+
tool=tool_name,
|
|
4210
|
+
ok=True,
|
|
4211
|
+
data=self._annotate_sensitive_payload(
|
|
4212
|
+
self._build_prepare_plan(
|
|
4213
|
+
preview_payload=preview,
|
|
4214
|
+
action_label="EVM Morpho market operation",
|
|
4215
|
+
),
|
|
4216
|
+
action_label="EVM Morpho market operation",
|
|
4217
|
+
mode="prepare",
|
|
4218
|
+
),
|
|
4219
|
+
)
|
|
4220
|
+
|
|
4221
|
+
approval_payload = inspect_approval_token(
|
|
4222
|
+
approval_token,
|
|
4223
|
+
tool_name=tool_name,
|
|
4224
|
+
network=str(getattr(active_backend, "network", "unknown")),
|
|
4225
|
+
require_mainnet_confirmation=self._is_mainnet_for_backend(active_backend),
|
|
4226
|
+
)
|
|
4227
|
+
approval_summary = approval_payload.get("binding", {}).get("summary")
|
|
4228
|
+
if not isinstance(approval_summary, dict):
|
|
4229
|
+
raise WalletBackendError(
|
|
4230
|
+
"approval_token does not match the requested operation. Generate a new approval after previewing the exact action."
|
|
4231
|
+
)
|
|
4232
|
+
expected_summary = {
|
|
4233
|
+
"operation": "EVM Morpho market operation",
|
|
4234
|
+
"network": str(getattr(active_backend, "network", "unknown")),
|
|
4235
|
+
"morpho_surface": "market",
|
|
4236
|
+
"morpho_operation": str(operation),
|
|
4237
|
+
"token_address": token_address.strip(),
|
|
4238
|
+
}
|
|
4239
|
+
if isinstance(amount_raw, str) and amount_raw.strip():
|
|
4240
|
+
expected_summary["amount_raw"] = amount_raw.strip()
|
|
4241
|
+
if isinstance(native_amount_raw, str) and native_amount_raw.strip():
|
|
4242
|
+
expected_summary["native_amount_raw"] = native_amount_raw.strip()
|
|
4243
|
+
target = approval_summary.get("target")
|
|
4244
|
+
if not isinstance(target, dict):
|
|
4245
|
+
raise WalletBackendError(
|
|
4246
|
+
"approval_token does not match the requested operation. Generate a new approval after previewing the exact action."
|
|
4247
|
+
)
|
|
4248
|
+
expected_target_key = "marketId" if market_id else "marketPreset"
|
|
4249
|
+
expected_target_value = market_id.strip() if market_id else market_preset.strip()
|
|
4250
|
+
for key, expected_value in expected_summary.items():
|
|
4251
|
+
if approval_summary.get(key) != expected_value:
|
|
4252
|
+
raise WalletBackendError(
|
|
4253
|
+
"approval_token does not match the requested operation. Generate a new approval after previewing the exact action."
|
|
4254
|
+
)
|
|
4255
|
+
actual_target_value = target.get(expected_target_key)
|
|
4256
|
+
if market_id:
|
|
4257
|
+
# Market ids are hex hashes; the runtime may normalize their
|
|
4258
|
+
# case in the approval binding, so compare case-insensitively.
|
|
4259
|
+
target_matches = (
|
|
4260
|
+
isinstance(actual_target_value, str)
|
|
4261
|
+
and actual_target_value.lower() == expected_target_value.lower()
|
|
4262
|
+
)
|
|
4263
|
+
else:
|
|
4264
|
+
target_matches = actual_target_value == expected_target_value
|
|
4265
|
+
if not target_matches:
|
|
4266
|
+
raise WalletBackendError(
|
|
4267
|
+
"approval_token does not match the requested operation. Generate a new approval after previewing the exact action."
|
|
4268
|
+
)
|
|
4269
|
+
|
|
4270
|
+
approval_summary_copy = dict(approval_summary)
|
|
4271
|
+
self._require_execute_approval(
|
|
4272
|
+
approval_token=approval_token,
|
|
4273
|
+
tool_name=tool_name,
|
|
4274
|
+
summary=approval_summary_copy,
|
|
4275
|
+
action_label="EVM Morpho market operation",
|
|
4276
|
+
backend=active_backend,
|
|
4277
|
+
)
|
|
4278
|
+
result = await active_backend.send_evm_morpho_market_operation(
|
|
4279
|
+
**preview_kwargs,
|
|
4280
|
+
expected_quote_fingerprint=(
|
|
4281
|
+
str(approval_summary_copy.get("quote_fingerprint")).strip()
|
|
4282
|
+
if approval_summary_copy.get("quote_fingerprint") is not None
|
|
4283
|
+
else None
|
|
4284
|
+
),
|
|
4285
|
+
)
|
|
4286
|
+
return AgentToolResult(
|
|
4287
|
+
tool=tool_name,
|
|
4288
|
+
ok=True,
|
|
4289
|
+
data=self._annotate_sensitive_payload(
|
|
4290
|
+
result,
|
|
4291
|
+
action_label="EVM Morpho market operation",
|
|
4292
|
+
mode="execute",
|
|
4293
|
+
),
|
|
4294
|
+
)
|
|
4295
|
+
|
|
3610
4296
|
if tool_name == "get_evm_swap_quote":
|
|
3611
4297
|
token_in = args.get("token_in")
|
|
3612
4298
|
token_out = args.get("token_out")
|