@agentlayer.tech/wallet 0.1.52 → 0.1.53

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.
@@ -18,6 +18,17 @@ const PREVIEW_BOUND_SWAP_TOOLS = new Set([
18
18
  "flash_trade_open_position",
19
19
  "flash_trade_close_position",
20
20
  ]);
21
+ const AUTONOMOUS_BASE_SWAP_TOOLS = new Set([
22
+ "swap_evm_tokens",
23
+ "swap_evm_uniswap_tokens",
24
+ ]);
25
+ const AUTONOMOUS_DEFI_TOOLS = new Set([
26
+ "manage_evm_aave_position",
27
+ "manage_evm_lido_position",
28
+ "manage_evm_lido_withdrawal",
29
+ "manage_evm_morpho_market_position",
30
+ "manage_evm_morpho_vault_position",
31
+ ]);
21
32
  const approvalPreviewCache = new Map();
22
33
  const WALLET_TOOL_ONLY_GUIDANCE =
23
34
  "Use this wallet tool instead of shelling out to solana CLI, spl-token CLI, curl, or exec. If it fails, surface the wallet-tool error and stop rather than falling back to terminal commands.";
@@ -127,6 +138,17 @@ function requiresApprovedPreviewPayload(toolName, params = null) {
127
138
  return PREVIEW_BOUND_SWAP_TOOLS.has(toolName);
128
139
  }
129
140
 
141
+ function shouldLetBackendAuthorizeAutonomousExecution(toolName, params, config) {
142
+ const isBaseSwapTool = AUTONOMOUS_BASE_SWAP_TOOLS.has(toolName);
143
+ const isDefiTool = AUTONOMOUS_DEFI_TOOLS.has(toolName);
144
+ if (!isBaseSwapTool && !isDefiTool) return false;
145
+ if (String(params?.mode || "") !== "execute") return false;
146
+ if (typeof params?.approval_token === "string" && params.approval_token.trim()) return false;
147
+ const network = String(params?.network || config?.network || selectedEvmNetwork || "").trim().toLowerCase();
148
+ if (isBaseSwapTool) return network === "base";
149
+ return network === "base" || network === "ethereum";
150
+ }
151
+
130
152
  function looksLikeApprovalContextError(message) {
131
153
  const text = String(message || "").toLowerCase();
132
154
  return (
@@ -516,6 +538,7 @@ async function attachApprovalForExecute(api, config, userId, toolName, effective
516
538
  }
517
539
 
518
540
  if (effectiveParams.approval_token) return null;
541
+ if (shouldLetBackendAuthorizeAutonomousExecution(toolName, effectiveParams, config)) return null;
519
542
 
520
543
  throw new Error(APPROVAL_CONTEXT_MISSING_MESSAGE);
521
544
  }
@@ -697,6 +720,38 @@ const walletSessionToolDefinitions = [
697
720
  additionalProperties: false,
698
721
  },
699
722
  },
723
+ {
724
+ name: "agentlayer_autonomous_status",
725
+ description: "Return AgentLayer high-trust autonomous permission status for the combined base_swaps + defi_tools permission group.",
726
+ parameters: { type: "object", properties: {}, additionalProperties: false },
727
+ },
728
+ {
729
+ name: "agentlayer_autonomous_approve",
730
+ description:
731
+ "Enable high-trust autonomous execution for the combined permission group. The scope parameter is kept for compatibility; choosing base_swaps or defi_tools enables both Base Velora/Uniswap swaps and supported EVM DeFi management tools until revoked. This does not cover transfers, bridges, Solana swaps, or generic contract calls.",
732
+ parameters: {
733
+ type: "object",
734
+ properties: {
735
+ scope: { type: "string", enum: ["base_swaps", "defi_tools"], description: "Compatibility scope; either value enables the full autonomous permission group." },
736
+ purpose: { type: "string" },
737
+ user_intent: { type: "boolean" },
738
+ },
739
+ required: ["scope", "purpose", "user_intent"],
740
+ additionalProperties: false,
741
+ },
742
+ },
743
+ {
744
+ name: "agentlayer_autonomous_revoke",
745
+ description: "Disable the full high-trust autonomous permission group. The scope parameter is kept for compatibility; either value revokes base_swaps and defi_tools together.",
746
+ parameters: {
747
+ type: "object",
748
+ properties: {
749
+ scope: { type: "string", enum: ["base_swaps", "defi_tools"], description: "Compatibility scope; either value revokes the full autonomous permission group." },
750
+ },
751
+ required: ["scope"],
752
+ additionalProperties: false,
753
+ },
754
+ },
700
755
  {
701
756
  name: "x402_search_services",
702
757
  description:
@@ -1689,6 +1744,105 @@ const evmToolDefinitions = [
1689
1744
  additionalProperties: false,
1690
1745
  },
1691
1746
  },
1747
+ {
1748
+ name: "get_evm_morpho_vaults",
1749
+ 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.",
1750
+ parameters: {
1751
+ type: "object",
1752
+ properties: {
1753
+ vault_address: { type: "string", description: "Optional explicit vault address for a single-vault lookup." },
1754
+ limit: { type: "integer", minimum: 1, maximum: 500 },
1755
+ listed_only: { type: "boolean", description: "Filter to listed vaults only. Defaults to true." },
1756
+ asset_address: { type: "string", description: "Optional underlying asset address filter (e.g. only USDC vaults)." },
1757
+ order_by: {
1758
+ type: "string",
1759
+ enum: ["TotalAssetsUsd", "TotalAssets", "TotalSupply", "Liquidity", "LiquidityUsd", "Apy", "NetApy", "Address"],
1760
+ description: "Sort field when listing. Defaults to TotalAssetsUsd.",
1761
+ },
1762
+ order_direction: { type: "string", enum: ["asc", "desc"], description: "Sort direction. Defaults to desc." },
1763
+ network: { type: "string", enum: ["ethereum", "base"] },
1764
+ },
1765
+ additionalProperties: false,
1766
+ },
1767
+ },
1768
+ {
1769
+ name: "get_evm_morpho_markets",
1770
+ 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 free-text search or by collateral/loan asset.",
1771
+ parameters: {
1772
+ type: "object",
1773
+ properties: {
1774
+ market_id: { type: "string", description: "Optional explicit market id (32-byte hex) for a single-market lookup." },
1775
+ limit: { type: "integer", minimum: 1, maximum: 500 },
1776
+ listed_only: { type: "boolean", description: "Filter to listed markets only. Defaults to true." },
1777
+ search: { type: "string", description: "Optional free-text search over market/asset symbols (e.g. 'wstETH')." },
1778
+ collateral_asset_address: { type: "string", description: "Optional collateral asset address filter." },
1779
+ loan_asset_address: { type: "string", description: "Optional loan asset address filter." },
1780
+ order_by: {
1781
+ type: "string",
1782
+ enum: ["SupplyAssetsUsd", "BorrowAssetsUsd", "SupplyApy", "NetSupplyApy", "BorrowApy", "NetBorrowApy", "Utilization", "TotalLiquidityUsd", "Lltv"],
1783
+ description: "Sort field when listing. Defaults to SupplyAssetsUsd.",
1784
+ },
1785
+ order_direction: { type: "string", enum: ["asc", "desc"], description: "Sort direction. Defaults to desc." },
1786
+ network: { type: "string", enum: ["ethereum", "base"] },
1787
+ },
1788
+ additionalProperties: false,
1789
+ },
1790
+ },
1791
+ {
1792
+ name: "get_evm_morpho_positions",
1793
+ description: "Get read-only Morpho vault and market positions for the configured EVM wallet on supported mainnet chains.",
1794
+ parameters: {
1795
+ type: "object",
1796
+ properties: {
1797
+ network: { type: "string", enum: ["ethereum", "base"] },
1798
+ },
1799
+ additionalProperties: false,
1800
+ },
1801
+ },
1802
+ {
1803
+ name: "manage_evm_morpho_vault_position",
1804
+ description: "Preview, prepare, or execute a narrow Morpho vault operation on supported EVM mainnet networks. Supported operations are supply and withdraw. Preview or prepare first. After the user explicitly confirms the shown summary in chat, call execute; the OpenClaw plugin handles the internal execution authorization automatically.",
1805
+ optional: true,
1806
+ parameters: {
1807
+ type: "object",
1808
+ properties: {
1809
+ operation: { type: "string", enum: ["supply", "withdraw"] },
1810
+ token_address: { type: "string" },
1811
+ vault_address: { type: "string" },
1812
+ vault_preset: { type: "string" },
1813
+ amount_raw: { type: "string" },
1814
+ native_amount_raw: { type: "string" },
1815
+ mode: { type: "string", enum: ["preview", "prepare", "execute"] },
1816
+ purpose: { type: "string" },
1817
+ user_intent: { type: "boolean" },
1818
+ network: { type: "string", enum: ["ethereum", "base"] },
1819
+ },
1820
+ required: ["operation", "token_address", "mode", "purpose"],
1821
+ additionalProperties: false,
1822
+ },
1823
+ },
1824
+ {
1825
+ name: "manage_evm_morpho_market_position",
1826
+ description: "Preview, prepare, or execute a narrow Morpho market operation on supported EVM mainnet networks. Supported operations are supply_collateral, borrow, repay, and withdraw_collateral. Preview or prepare first. After the user explicitly confirms the shown summary in chat, call execute; the OpenClaw plugin handles the internal execution authorization automatically.",
1827
+ optional: true,
1828
+ parameters: {
1829
+ type: "object",
1830
+ properties: {
1831
+ operation: { type: "string", enum: ["supply_collateral", "borrow", "repay", "withdraw_collateral"] },
1832
+ token_address: { type: "string" },
1833
+ market_id: { type: "string" },
1834
+ market_preset: { type: "string" },
1835
+ amount_raw: { type: "string" },
1836
+ native_amount_raw: { type: "string" },
1837
+ mode: { type: "string", enum: ["preview", "prepare", "execute"] },
1838
+ purpose: { type: "string" },
1839
+ user_intent: { type: "boolean" },
1840
+ network: { type: "string", enum: ["ethereum", "base"] },
1841
+ },
1842
+ required: ["operation", "token_address", "mode", "purpose"],
1843
+ additionalProperties: false,
1844
+ },
1845
+ },
1692
1846
  {
1693
1847
  name: "get_evm_swap_quote",
1694
1848
  description: "Get a read-only Velora quote for an ERC-20 or native ETH swap on supported EVM mainnet networks. This does not approve or execute a swap.",
@@ -22,6 +22,13 @@ const AUTONOMOUS_BASE_SWAP_TOOLS = new Set([
22
22
  "swap_evm_tokens",
23
23
  "swap_evm_uniswap_tokens",
24
24
  ]);
25
+ const AUTONOMOUS_DEFI_TOOLS = new Set([
26
+ "manage_evm_aave_position",
27
+ "manage_evm_lido_position",
28
+ "manage_evm_lido_withdrawal",
29
+ "manage_evm_morpho_market_position",
30
+ "manage_evm_morpho_vault_position",
31
+ ]);
25
32
  const approvalPreviewCache = new Map();
26
33
  const WALLET_TOOL_ONLY_GUIDANCE =
27
34
  "Use this wallet tool instead of shelling out to solana CLI, spl-token CLI, curl, or exec. If it fails, surface the wallet-tool error and stop rather than falling back to terminal commands.";
@@ -131,12 +138,15 @@ function requiresApprovedPreviewPayload(toolName, params = null) {
131
138
  return PREVIEW_BOUND_SWAP_TOOLS.has(toolName);
132
139
  }
133
140
 
134
- function shouldLetBackendAuthorizeAutonomousBaseSwap(toolName, params, config) {
135
- if (!AUTONOMOUS_BASE_SWAP_TOOLS.has(toolName)) return false;
141
+ function shouldLetBackendAuthorizeAutonomousExecution(toolName, params, config) {
142
+ const isBaseSwapTool = AUTONOMOUS_BASE_SWAP_TOOLS.has(toolName);
143
+ const isDefiTool = AUTONOMOUS_DEFI_TOOLS.has(toolName);
144
+ if (!isBaseSwapTool && !isDefiTool) return false;
136
145
  if (String(params?.mode || "") !== "execute") return false;
137
146
  if (typeof params?.approval_token === "string" && params.approval_token.trim()) return false;
138
147
  const network = String(params?.network || config?.network || selectedEvmNetwork || "").trim().toLowerCase();
139
- return network === "base";
148
+ if (isBaseSwapTool) return network === "base";
149
+ return network === "base" || network === "ethereum";
140
150
  }
141
151
 
142
152
  function looksLikeApprovalContextError(message) {
@@ -528,7 +538,7 @@ async function attachApprovalForExecute(api, config, userId, toolName, effective
528
538
  }
529
539
 
530
540
  if (effectiveParams.approval_token) return null;
531
- if (shouldLetBackendAuthorizeAutonomousBaseSwap(toolName, effectiveParams, config)) return null;
541
+ if (shouldLetBackendAuthorizeAutonomousExecution(toolName, effectiveParams, config)) return null;
532
542
 
533
543
  throw new Error(APPROVAL_CONTEXT_MISSING_MESSAGE);
534
544
  }
@@ -712,17 +722,17 @@ const walletSessionToolDefinitions = [
712
722
  },
713
723
  {
714
724
  name: "agentlayer_autonomous_status",
715
- description: "Return AgentLayer high-trust autonomous permission status. Currently supports only scope=base_swaps.",
725
+ description: "Return AgentLayer high-trust autonomous permission status for the combined base_swaps + defi_tools permission group.",
716
726
  parameters: { type: "object", properties: {}, additionalProperties: false },
717
727
  },
718
728
  {
719
729
  name: "agentlayer_autonomous_approve",
720
730
  description:
721
- "Enable high-trust autonomous Base swaps. scope=base_swaps lets Base Velora/Uniswap swap execute calls run without per-transaction human approval until revoked. This does not cover transfers, withdrawals, lending, staking, bridges, Solana swaps, or non-Base networks.",
731
+ "Enable high-trust autonomous execution for the combined permission group. The scope parameter is kept for compatibility; choosing base_swaps or defi_tools enables both Base Velora/Uniswap swaps and supported EVM DeFi management tools until revoked. This does not cover transfers, bridges, Solana swaps, or generic contract calls.",
722
732
  parameters: {
723
733
  type: "object",
724
734
  properties: {
725
- scope: { type: "string", enum: ["base_swaps"] },
735
+ scope: { type: "string", enum: ["base_swaps", "defi_tools"], description: "Compatibility scope; either value enables the full autonomous permission group." },
726
736
  purpose: { type: "string" },
727
737
  user_intent: { type: "boolean" },
728
738
  },
@@ -732,11 +742,11 @@ const walletSessionToolDefinitions = [
732
742
  },
733
743
  {
734
744
  name: "agentlayer_autonomous_revoke",
735
- description: "Disable high-trust autonomous execution for a scope. Currently supports only scope=base_swaps.",
745
+ description: "Disable the full high-trust autonomous permission group. The scope parameter is kept for compatibility; either value revokes base_swaps and defi_tools together.",
736
746
  parameters: {
737
747
  type: "object",
738
748
  properties: {
739
- scope: { type: "string", enum: ["base_swaps"] },
749
+ scope: { type: "string", enum: ["base_swaps", "defi_tools"], description: "Compatibility scope; either value revokes the full autonomous permission group." },
740
750
  },
741
751
  required: ["scope"],
742
752
  additionalProperties: false,
@@ -2,7 +2,7 @@
2
2
  "id": "agent-wallet",
3
3
  "name": "Agent Wallet",
4
4
  "description": "Official OpenClaw plugin bridge for the agent-wallet backends, including Solana, local BTC, and local EVM.",
5
- "version": "0.1.52",
5
+ "version": "0.1.53",
6
6
  "contracts": {
7
7
  "tools": [
8
8
  "agentlayer_autonomous_approve",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agentlayertech/agent-wallet-plugin",
3
- "version": "0.1.52",
3
+ "version": "0.1.53",
4
4
  "description": "OpenClaw plugin bridge for the AgentLayer wallet runtime.",
5
5
  "type": "module",
6
6
  "license": "SEE LICENSE IN ../../../LICENSE",
package/CHANGELOG.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  ## Unreleased
4
4
 
5
- ## v0.1.52 - 2026-06-26
5
+ ## v0.1.53 - 2026-06-26
6
6
 
7
7
  - Fixed Morpho vault and market quote (preview) requests timing out under the
8
8
  default 10 s HTTP budget. The Morpho SDK fetches vault state from
package/VERSION CHANGED
@@ -1 +1 @@
1
- 0.1.52
1
+ 0.1.53
@@ -2,7 +2,7 @@
2
2
 
3
3
  # Keep in sync with package.json, pyproject.toml, and the npm installer version.
4
4
  # scripts/check_release_version.mjs enforces this on release.
5
- __version__ = "0.1.52"
5
+ __version__ = "0.1.53"
6
6
 
7
7
  __all__ = [
8
8
  "config",
@@ -2,7 +2,7 @@
2
2
 
3
3
  This module is intentionally narrower than ``autonomous_session``. It models
4
4
  the "CLI permissions" UX where a user grants a standing capability, not a
5
- budgeted policy envelope. The first supported scope is Base swaps only.
5
+ budgeted policy envelope.
6
6
  """
7
7
 
8
8
  from __future__ import annotations
@@ -22,6 +22,19 @@ BASE_SWAP_SCOPE = "base_swaps"
22
22
  BASE_SWAP_NETWORK = "base"
23
23
  BASE_SWAP_TOOLS = frozenset({"swap_evm_tokens", "swap_evm_uniswap_tokens"})
24
24
  BASE_SWAP_ISSUER = "autonomous-permission:base-swaps"
25
+ DEFI_TOOLS_SCOPE = "defi_tools"
26
+ DEFI_TOOLS_NETWORKS = frozenset({"base", "ethereum"})
27
+ DEFI_TOOLS = frozenset(
28
+ {
29
+ "manage_evm_aave_position",
30
+ "manage_evm_lido_position",
31
+ "manage_evm_lido_withdrawal",
32
+ "manage_evm_morpho_market_position",
33
+ "manage_evm_morpho_vault_position",
34
+ }
35
+ )
36
+ DEFI_TOOLS_ISSUER = "autonomous-permission:defi-tools"
37
+ SUPPORTED_SCOPES = frozenset({BASE_SWAP_SCOPE, DEFI_TOOLS_SCOPE})
25
38
  _PERMISSIONS_FILENAME = "autonomous_permissions.json"
26
39
 
27
40
 
@@ -68,6 +81,7 @@ def _scope_status(record: dict[str, Any], scope: str) -> dict[str, Any]:
68
81
  "approved_at": raw.get("approved_at"),
69
82
  "approved_by": raw.get("approved_by"),
70
83
  "network": raw.get("network"),
84
+ "networks": raw.get("networks") or ([raw.get("network")] if raw.get("network") else []),
71
85
  "tools": raw.get("tools") or [],
72
86
  "warning": raw.get("warning"),
73
87
  }
@@ -77,17 +91,16 @@ def status() -> dict[str, Any]:
77
91
  """Return current high-trust autonomous permission status."""
78
92
  record = _load_record()
79
93
  base_swaps = _scope_status(record, BASE_SWAP_SCOPE)
94
+ defi_tools = _scope_status(record, DEFI_TOOLS_SCOPE)
95
+ group_enabled = all(bool(scope.get("enabled")) for scope in (base_swaps, defi_tools))
80
96
  return {
81
- "active": bool(base_swaps.get("enabled")),
82
- "scopes": {BASE_SWAP_SCOPE: base_swaps},
97
+ "active": group_enabled,
98
+ "scopes": {BASE_SWAP_SCOPE: base_swaps, DEFI_TOOLS_SCOPE: defi_tools},
83
99
  "permission_file": str(_permissions_path()),
84
100
  }
85
101
 
86
102
 
87
- def approve_base_swaps(*, approved_by: str = "user") -> dict[str, Any]:
88
- """Enable unattended Base swap execution for supported EVM swap tools."""
89
- record = _load_record()
90
- scopes = record.setdefault("scopes", {})
103
+ def _set_base_swap_scope(scopes: dict[str, Any], *, approved_by: str) -> None:
91
104
  scopes[BASE_SWAP_SCOPE] = {
92
105
  "enabled": True,
93
106
  "approved_at": _now(),
@@ -99,26 +112,81 @@ def approve_base_swaps(*, approved_by: str = "user") -> dict[str, Any]:
99
112
  "per-transaction human approval until revoked."
100
113
  ),
101
114
  }
115
+
116
+
117
+ def _set_defi_tools_scope(scopes: dict[str, Any], *, approved_by: str) -> None:
118
+ scopes[DEFI_TOOLS_SCOPE] = {
119
+ "enabled": True,
120
+ "approved_at": _now(),
121
+ "approved_by": str(approved_by or "user"),
122
+ "networks": sorted(DEFI_TOOLS_NETWORKS),
123
+ "tools": sorted(DEFI_TOOLS),
124
+ "warning": (
125
+ "High-trust permission: supported EVM DeFi execute calls can run "
126
+ "without per-transaction human approval until revoked."
127
+ ),
128
+ }
129
+
130
+
131
+ def approve_all(*, approved_by: str = "user") -> dict[str, Any]:
132
+ """Enable all high-trust autonomous permission scopes as one group."""
133
+ record = _load_record()
134
+ scopes = record.setdefault("scopes", {})
135
+ normalized_approved_by = str(approved_by or "user")
136
+ _set_base_swap_scope(scopes, approved_by=normalized_approved_by)
137
+ _set_defi_tools_scope(scopes, approved_by=normalized_approved_by)
102
138
  _write_record(record)
103
139
  return status()
104
140
 
105
141
 
142
+ def approve_base_swaps(*, approved_by: str = "user") -> dict[str, Any]:
143
+ """Enable all autonomous permissions; base_swaps is kept as a compatibility scope."""
144
+ return approve_all(approved_by=approved_by)
145
+
146
+
147
+ def approve_defi_tools(*, approved_by: str = "user") -> dict[str, Any]:
148
+ """Enable all autonomous permissions; defi_tools is kept as a compatibility scope."""
149
+ return approve_all(approved_by=approved_by)
150
+
151
+
106
152
  def revoke_base_swaps() -> dict[str, Any]:
107
- """Disable unattended Base swap execution."""
153
+ """Disable all autonomous permissions; base_swaps is kept as a compatibility scope."""
154
+ return revoke_all()
155
+
156
+
157
+ def revoke_defi_tools() -> dict[str, Any]:
158
+ """Disable all autonomous permissions; defi_tools is kept as a compatibility scope."""
159
+ return revoke_all()
160
+
161
+
162
+ def revoke_all() -> dict[str, Any]:
163
+ """Disable every high-trust autonomous permission scope as one group."""
108
164
  record = _load_record()
109
165
  scopes = record.setdefault("scopes", {})
110
- existing = scopes.get(BASE_SWAP_SCOPE)
111
- if isinstance(existing, dict):
112
- existing["enabled"] = False
113
- existing["revoked_at"] = _now()
114
- else:
115
- scopes[BASE_SWAP_SCOPE] = {"enabled": False, "revoked_at": _now()}
166
+ for supported_scope in sorted(SUPPORTED_SCOPES):
167
+ existing = scopes.get(supported_scope)
168
+ if isinstance(existing, dict):
169
+ existing["enabled"] = False
170
+ existing["revoked_at"] = _now()
171
+ else:
172
+ scopes[supported_scope] = {"enabled": False, "revoked_at": _now()}
116
173
  _write_record(record)
117
174
  return status()
118
175
 
119
176
 
177
+ def revoke_scope(scope: str) -> dict[str, Any]:
178
+ normalized_scope = str(scope or "").strip()
179
+ if normalized_scope not in SUPPORTED_SCOPES:
180
+ raise WalletBackendError("Unsupported autonomous permission scope.")
181
+ return revoke_all()
182
+
183
+
120
184
  def is_base_swap_approved() -> bool:
121
- return bool(status()["scopes"][BASE_SWAP_SCOPE].get("enabled"))
185
+ return bool(status()["active"])
186
+
187
+
188
+ def is_defi_tools_approved() -> bool:
189
+ return bool(status()["active"])
122
190
 
123
191
 
124
192
  def authorize_base_swap(*, tool_name: str, network: str, summary: dict[str, Any]) -> str:
@@ -129,8 +197,8 @@ def authorize_base_swap(*, tool_name: str, network: str, summary: dict[str, Any]
129
197
  raise WalletBackendError("Autonomous Base swap permission only applies on network=base.")
130
198
  if not is_base_swap_approved():
131
199
  raise WalletBackendError(
132
- "Autonomous Base swap permission is not enabled. Ask the user to run "
133
- "agentlayer_autonomous_approve for scope=base_swaps first."
200
+ "Autonomous execution is not enabled. Ask the user to run "
201
+ "agentlayer_autonomous_approve first."
134
202
  )
135
203
  summary_network = str((summary or {}).get("network") or "").strip().lower()
136
204
  if summary_network and summary_network != BASE_SWAP_NETWORK:
@@ -143,3 +211,28 @@ def authorize_base_swap(*, tool_name: str, network: str, summary: dict[str, Any]
143
211
  ttl_seconds=120,
144
212
  issued_by=BASE_SWAP_ISSUER,
145
213
  )
214
+
215
+
216
+ def authorize_defi_tool(*, tool_name: str, network: str, summary: dict[str, Any]) -> str:
217
+ """Issue an internal approval token for one exact EVM DeFi operation."""
218
+ normalized_network = str(network or "").strip().lower()
219
+ if str(tool_name) not in DEFI_TOOLS:
220
+ raise WalletBackendError("Autonomous DeFi permission only covers supported EVM DeFi tools.")
221
+ if normalized_network not in DEFI_TOOLS_NETWORKS:
222
+ raise WalletBackendError("Autonomous DeFi permission only applies on ethereum or base.")
223
+ if not is_defi_tools_approved():
224
+ raise WalletBackendError(
225
+ "Autonomous execution is not enabled. Ask the user to run "
226
+ "agentlayer_autonomous_approve first."
227
+ )
228
+ summary_network = str((summary or {}).get("network") or "").strip().lower()
229
+ if summary_network and summary_network != normalized_network:
230
+ raise WalletBackendError("Autonomous DeFi summary is not bound to the active network.")
231
+ return issue_approval_token(
232
+ tool_name=tool_name,
233
+ network=normalized_network,
234
+ summary=summary,
235
+ mainnet_confirmed=True,
236
+ ttl_seconds=120,
237
+ issued_by=DEFI_TOOLS_ISSUER,
238
+ )