@agentlayer.tech/wallet 0.1.93 → 0.1.95

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.
Files changed (30) hide show
  1. package/.openclaw/extensions/agent-wallet/dist/index.js +79 -0
  2. package/.openclaw/extensions/agent-wallet/index.ts +79 -0
  3. package/.openclaw/extensions/agent-wallet/openclaw.plugin.json +6 -1
  4. package/.openclaw/extensions/agent-wallet/package.json +1 -1
  5. package/CHANGELOG.md +18 -0
  6. package/VERSION +1 -1
  7. package/agent-wallet/agent_wallet/__init__.py +1 -1
  8. package/agent-wallet/agent_wallet/autonomous_permissions.py +1 -0
  9. package/agent-wallet/agent_wallet/openclaw_adapter.py +262 -0
  10. package/agent-wallet/agent_wallet/providers/wdk_evm_local.py +2 -0
  11. package/agent-wallet/agent_wallet/wallet_layer/base.py +37 -0
  12. package/agent-wallet/agent_wallet/wallet_layer/wdk_evm.py +157 -0
  13. package/agent-wallet/openclaw.plugin.json +1 -1
  14. package/agent-wallet/pyproject.toml +1 -1
  15. package/agent-wallet/scripts/install_agent_wallet.py +29 -1
  16. package/claude-code/plugins/agent-wallet/.claude-plugin/plugin.json +1 -1
  17. package/claude-code/plugins/agent-wallet/AGENTLAYER_AGENT_GUIDE.md +282 -0
  18. package/claude-code/plugins/agent-wallet/README.md +2 -0
  19. package/claude-code/plugins/agent-wallet/commands/guide.md +40 -0
  20. package/codex/plugins/agent-wallet/.codex-plugin/plugin.json +1 -1
  21. package/codex/plugins/agent-wallet/server.py +1 -0
  22. package/hermes/plugins/agent_wallet/plugin.yaml +1 -1
  23. package/package.json +1 -1
  24. package/wdk-btc-wallet/package.json +1 -1
  25. package/wdk-evm-wallet/.env.example +3 -0
  26. package/wdk-evm-wallet/README.md +6 -0
  27. package/wdk-evm-wallet/package.json +1 -1
  28. package/wdk-evm-wallet/src/config.js +9 -0
  29. package/wdk-evm-wallet/src/server.js +24 -0
  30. package/wdk-evm-wallet/src/wdk_evm_wallet.js +469 -0
@@ -29,6 +29,7 @@ const AUTONOMOUS_DEFI_TOOLS = new Set([
29
29
  "manage_evm_lido_withdrawal",
30
30
  "manage_evm_morpho_market_position",
31
31
  "manage_evm_morpho_vault_position",
32
+ "manage_evm_uniswap_liquidity",
32
33
  ]);
33
34
  const approvalPreviewCache = new Map();
34
35
  const WALLET_TOOL_ONLY_GUIDANCE =
@@ -1977,6 +1978,61 @@ const evmToolDefinitions = [
1977
1978
  additionalProperties: false,
1978
1979
  },
1979
1980
  },
1981
+ {
1982
+ name: "search_uniswap_pairs",
1983
+ description: "Search read-only onchain Uniswap-tradeable pairs by name, ticker, or ERC-20 address. Verify any returned token address independently before quoting or trading.",
1984
+ parameters: {
1985
+ type: "object",
1986
+ properties: {
1987
+ query: { type: "string" },
1988
+ token_address: { type: "string" },
1989
+ chain: { type: "string" },
1990
+ dex_id: { type: "string" },
1991
+ all_chains: { type: "boolean" },
1992
+ limit: { type: "integer" },
1993
+ network: { type: "string", enum: EVM_CORE_NETWORKS },
1994
+ },
1995
+ additionalProperties: false,
1996
+ },
1997
+ },
1998
+ {
1999
+ name: "get_evm_uniswap_pools",
2000
+ description: "Find existing Uniswap V3/V4 pools through the official Pool Info API on ethereum, base, or robinhood. Returns the canonical poolReferenceIdentifier needed for an LP create request. Read-only: it does not approve, sign, or execute anything.",
2001
+ parameters: {
2002
+ type: "object",
2003
+ properties: {
2004
+ protocol: { type: "string", enum: ["V3", "V4"] },
2005
+ pool_parameters: {
2006
+ type: "object",
2007
+ description: "Official Pool Info API token-pair parameters, such as tokenAddressA, tokenAddressB and optional fee/tickSpacing/hooks.",
2008
+ additionalProperties: true,
2009
+ },
2010
+ pool_references: {
2011
+ type: "array",
2012
+ maxItems: 20,
2013
+ items: { type: "object", additionalProperties: true },
2014
+ },
2015
+ page_size: { type: "integer", minimum: 1, maximum: 20 },
2016
+ current_page: { type: "integer", minimum: 1 },
2017
+ network: { type: "string", enum: EVM_CORE_NETWORKS },
2018
+ },
2019
+ required: ["protocol"],
2020
+ additionalProperties: false,
2021
+ },
2022
+ },
2023
+ {
2024
+ name: "get_evm_uniswap_positions",
2025
+ description: "List the active wallet's Uniswap V3 LP NFTs on ethereum, base, or robinhood. Returns token ids, token pair, fee tier, ticks, liquidity, and owed fees. Read-only. V4 is excluded because its PositionManager is not enumerable without a verified indexed source.",
2026
+ parameters: {
2027
+ type: "object",
2028
+ properties: {
2029
+ protocol: { type: "string", enum: ["V3"] },
2030
+ limit: { type: "integer", minimum: 1, maximum: 100 },
2031
+ network: { type: "string", enum: EVM_CORE_NETWORKS },
2032
+ },
2033
+ additionalProperties: false,
2034
+ },
2035
+ },
1980
2036
  {
1981
2037
  name: "swap_evm_uniswap_tokens",
1982
2038
  description: "Preview, prepare, or execute a supported Uniswap path on ethereum, base, or robinhood: CLASSIC, UniswapX orders, or canonical ETH↔WETH wrap/unwrap. ERC-20 paths may use Permit2 EIP-712 or an UniswapX order signature. 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.",
@@ -1997,6 +2053,29 @@ const evmToolDefinitions = [
1997
2053
  additionalProperties: false,
1998
2054
  },
1999
2055
  },
2056
+ {
2057
+ name: "manage_evm_uniswap_liquidity",
2058
+ description: "Preview, prepare, or execute a Uniswap V3/V4 liquidity action on ethereum, base, or robinhood. Supported actions are create, increase, decrease, and claim_fees. Create needs existingPool.poolReference and position actions need an NFT token id: use get_evm_uniswap_pools or the V3 get_evm_uniswap_positions scanner to obtain them, never guess. The official Liquidity API builds the transaction again immediately before signing, so confirmation is scoped to the LP intent rather than fragile ticks or calldata.",
2059
+ optional: true,
2060
+ parameters: {
2061
+ type: "object",
2062
+ properties: {
2063
+ action: { type: "string", enum: ["create", "increase", "decrease", "claim_fees"] },
2064
+ protocol: { type: "string", enum: ["V3", "V4"] },
2065
+ request: {
2066
+ type: "object",
2067
+ description: "Official Uniswap Liquidity API fields. walletAddress, chainId, protocol, simulateTransaction, and permit/signature fields are controlled by the wallet.",
2068
+ additionalProperties: true,
2069
+ },
2070
+ mode: { type: "string", enum: ["preview", "prepare", "execute"] },
2071
+ purpose: { type: "string" },
2072
+ user_intent: { type: "boolean" },
2073
+ network: { type: "string", enum: EVM_CORE_NETWORKS },
2074
+ },
2075
+ required: ["action", "protocol", "request", "mode", "purpose"],
2076
+ additionalProperties: false,
2077
+ },
2078
+ },
2000
2079
  {
2001
2080
  name: "swap_evm_lifi_cross_chain_tokens",
2002
2081
  description: "Preview, prepare, or execute an EVM-origin cross-chain swap through LI.FI. This currently supports ethereum/base as the source network and ethereum/base/solana as the destination chain. 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.",
@@ -29,6 +29,7 @@ const AUTONOMOUS_DEFI_TOOLS = new Set([
29
29
  "manage_evm_lido_withdrawal",
30
30
  "manage_evm_morpho_market_position",
31
31
  "manage_evm_morpho_vault_position",
32
+ "manage_evm_uniswap_liquidity",
32
33
  ]);
33
34
  const approvalPreviewCache = new Map();
34
35
  const WALLET_TOOL_ONLY_GUIDANCE =
@@ -1977,6 +1978,61 @@ const evmToolDefinitions = [
1977
1978
  additionalProperties: false,
1978
1979
  },
1979
1980
  },
1981
+ {
1982
+ name: "search_uniswap_pairs",
1983
+ description: "Search read-only onchain Uniswap-tradeable pairs by name, ticker, or ERC-20 address. Verify any returned token address independently before quoting or trading.",
1984
+ parameters: {
1985
+ type: "object",
1986
+ properties: {
1987
+ query: { type: "string" },
1988
+ token_address: { type: "string" },
1989
+ chain: { type: "string" },
1990
+ dex_id: { type: "string" },
1991
+ all_chains: { type: "boolean" },
1992
+ limit: { type: "integer" },
1993
+ network: { type: "string", enum: EVM_CORE_NETWORKS },
1994
+ },
1995
+ additionalProperties: false,
1996
+ },
1997
+ },
1998
+ {
1999
+ name: "get_evm_uniswap_pools",
2000
+ description: "Find existing Uniswap V3/V4 pools through the official Pool Info API on ethereum, base, or robinhood. Returns the canonical poolReferenceIdentifier needed for an LP create request. Read-only: it does not approve, sign, or execute anything.",
2001
+ parameters: {
2002
+ type: "object",
2003
+ properties: {
2004
+ protocol: { type: "string", enum: ["V3", "V4"] },
2005
+ pool_parameters: {
2006
+ type: "object",
2007
+ description: "Official Pool Info API token-pair parameters, such as tokenAddressA, tokenAddressB and optional fee/tickSpacing/hooks.",
2008
+ additionalProperties: true,
2009
+ },
2010
+ pool_references: {
2011
+ type: "array",
2012
+ maxItems: 20,
2013
+ items: { type: "object", additionalProperties: true },
2014
+ },
2015
+ page_size: { type: "integer", minimum: 1, maximum: 20 },
2016
+ current_page: { type: "integer", minimum: 1 },
2017
+ network: { type: "string", enum: EVM_CORE_NETWORKS },
2018
+ },
2019
+ required: ["protocol"],
2020
+ additionalProperties: false,
2021
+ },
2022
+ },
2023
+ {
2024
+ name: "get_evm_uniswap_positions",
2025
+ description: "List the active wallet's Uniswap V3 LP NFTs on ethereum, base, or robinhood. Returns token ids, token pair, fee tier, ticks, liquidity, and owed fees. Read-only. V4 is excluded because its PositionManager is not enumerable without a verified indexed source.",
2026
+ parameters: {
2027
+ type: "object",
2028
+ properties: {
2029
+ protocol: { type: "string", enum: ["V3"] },
2030
+ limit: { type: "integer", minimum: 1, maximum: 100 },
2031
+ network: { type: "string", enum: EVM_CORE_NETWORKS },
2032
+ },
2033
+ additionalProperties: false,
2034
+ },
2035
+ },
1980
2036
  {
1981
2037
  name: "swap_evm_uniswap_tokens",
1982
2038
  description: "Preview, prepare, or execute a supported Uniswap path on ethereum, base, or robinhood: CLASSIC, UniswapX orders, or canonical ETH↔WETH wrap/unwrap. ERC-20 paths may use Permit2 EIP-712 or an UniswapX order signature. 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.",
@@ -1997,6 +2053,29 @@ const evmToolDefinitions = [
1997
2053
  additionalProperties: false,
1998
2054
  },
1999
2055
  },
2056
+ {
2057
+ name: "manage_evm_uniswap_liquidity",
2058
+ description: "Preview, prepare, or execute a Uniswap V3/V4 liquidity action on ethereum, base, or robinhood. Supported actions are create, increase, decrease, and claim_fees. Create needs existingPool.poolReference and position actions need an NFT token id: use get_evm_uniswap_pools or the V3 get_evm_uniswap_positions scanner to obtain them, never guess. The official Liquidity API builds the transaction again immediately before signing, so confirmation is scoped to the LP intent rather than fragile ticks or calldata.",
2059
+ optional: true,
2060
+ parameters: {
2061
+ type: "object",
2062
+ properties: {
2063
+ action: { type: "string", enum: ["create", "increase", "decrease", "claim_fees"] },
2064
+ protocol: { type: "string", enum: ["V3", "V4"] },
2065
+ request: {
2066
+ type: "object",
2067
+ description: "Official Uniswap Liquidity API fields. walletAddress, chainId, protocol, simulateTransaction, and permit/signature fields are controlled by the wallet.",
2068
+ additionalProperties: true,
2069
+ },
2070
+ mode: { type: "string", enum: ["preview", "prepare", "execute"] },
2071
+ purpose: { type: "string" },
2072
+ user_intent: { type: "boolean" },
2073
+ network: { type: "string", enum: EVM_CORE_NETWORKS },
2074
+ },
2075
+ required: ["action", "protocol", "request", "mode", "purpose"],
2076
+ additionalProperties: false,
2077
+ },
2078
+ },
2000
2079
  {
2001
2080
  name: "swap_evm_lifi_cross_chain_tokens",
2002
2081
  description: "Preview, prepare, or execute an EVM-origin cross-chain swap through LI.FI. This currently supports ethereum/base as the source network and ethereum/base/solana as the destination chain. 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.",
@@ -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.93",
5
+ "version": "0.1.95",
6
6
  "contracts": {
7
7
  "tools": [
8
8
  "agentlayer_autonomous_approve",
@@ -30,6 +30,7 @@
30
30
  "get_evm_token_balance",
31
31
  "get_evm_token_metadata",
32
32
  "get_evm_transaction_receipt",
33
+ "get_uniswap_swap_quote",
33
34
  "get_flash_trade_markets",
34
35
  "get_flash_trade_positions",
35
36
  "get_kamino_earn_positions",
@@ -61,11 +62,15 @@
61
62
  "manage_evm_morpho_vault_position",
62
63
  "manage_evm_lido_position",
63
64
  "manage_evm_lido_withdrawal",
65
+ "manage_evm_uniswap_liquidity",
64
66
  "search_uniswap_pairs",
67
+ "get_evm_uniswap_pools",
68
+ "get_evm_uniswap_positions",
65
69
  "set_evm_network",
66
70
  "set_wallet_backend",
67
71
  "sign_wallet_message",
68
72
  "swap_evm_lifi_cross_chain_tokens",
73
+ "swap_evm_uniswap_tokens",
69
74
  "swap_evm_tokens",
70
75
  "swap_solana_lifi_cross_chain_tokens",
71
76
  "swap_solana_tokens",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agentlayertech/agent-wallet-plugin",
3
- "version": "0.1.93",
3
+ "version": "0.1.95",
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,6 +2,24 @@
2
2
 
3
3
  ## Unreleased
4
4
 
5
+ ## v0.1.94 - 2026-08-13
6
+
7
+ - **Added Uniswap V3/V4 liquidity provisioning for Ethereum, Base, and
8
+ Robinhood Chain.** The EVM wallet now previews, prepares, and executes
9
+ create, increase, decrease, and fee-claim actions through Uniswap's
10
+ Liquidity API. Final LP calldata is refreshed and simulated immediately
11
+ before signing; executable transactions are accepted only for pinned
12
+ PositionManager deployments and matching wallet/network fields.
13
+ - **Added bounded LP approval handling and intent-safe confirmations.** Token
14
+ approvals are restricted to LP-request assets and approved spenders, while
15
+ confirmations bind the action, network, protocol, pool/position, and assets
16
+ rather than fast-expiring ticks or calldata. Single-sided concentrated
17
+ positions with a zero amount on one ERC-20 side no longer fail approval
18
+ discovery.
19
+ - **Exposed LP actions through the provider gateway, OpenClaw, and Codex.**
20
+ The provider gateway forwards the existing shared Uniswap API key to the
21
+ Liquidity API; OpenClaw's pair-search contract was restored and registered.
22
+
5
23
  ## v0.1.93 - 2026-08-10
6
24
 
7
25
  - **Added `/cards` (Claude Code) and `cards` (Codex) for Laso Finance card
package/VERSION CHANGED
@@ -1 +1 @@
1
- 0.1.93
1
+ 0.1.95
@@ -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.93"
5
+ __version__ = "0.1.95"
6
6
 
7
7
  __all__ = [
8
8
  "config",
@@ -31,6 +31,7 @@ DEFI_TOOLS = frozenset(
31
31
  "manage_evm_lido_withdrawal",
32
32
  "manage_evm_morpho_market_position",
33
33
  "manage_evm_morpho_vault_position",
34
+ "manage_evm_uniswap_liquidity",
34
35
  }
35
36
  )
36
37
  DEFI_TOOLS_ISSUER = "autonomous-permission:defi-tools"
@@ -751,6 +751,28 @@ class OpenClawWalletAdapter:
751
751
  "quote_fingerprint": provided_fingerprint,
752
752
  }
753
753
 
754
+ if asset_type == "evm-uniswap-liquidity":
755
+ request = payload.get("request") if isinstance(payload.get("request"), dict) else {}
756
+ existing_pool = request.get("existingPool") if isinstance(request.get("existingPool"), dict) else {}
757
+ return {
758
+ "operation": action_label,
759
+ "network": str(payload.get("network") or getattr(self.backend, "network", "unknown")),
760
+ "from_address": payload.get("from_address"),
761
+ "protocol": payload.get("protocol"),
762
+ "liquidity_action": payload.get("liquidity_action"),
763
+ "liquidity_protocol": payload.get("liquidity_protocol"),
764
+ # Intent binds to the action and caller-provided LP scope, not a
765
+ # per-block transaction hash, adjusted ticks, or calculated amount.
766
+ "token0_address": request.get("token0Address") or existing_pool.get("token0Address"),
767
+ "token1_address": request.get("token1Address") or existing_pool.get("token1Address"),
768
+ "pool_reference": existing_pool.get("poolReference"),
769
+ "position_token_id": request.get("nftTokenId") or request.get("tokenId") or payload.get("position_token_id"),
770
+ "independent_token": request.get("independentToken"),
771
+ "liquidity_percentage_to_decrease": request.get("liquidityPercentageToDecrease"),
772
+ "slippage_tolerance": request.get("slippageTolerance"),
773
+ "position_manager": payload.get("position_manager"),
774
+ }
775
+
754
776
  if asset_type == "evm-morpho-vault":
755
777
  return {
756
778
  "operation": action_label,
@@ -1722,6 +1744,85 @@ class OpenClawWalletAdapter:
1722
1744
  risk_level="low",
1723
1745
  ),
1724
1746
  )
1747
+ tools.insert(
1748
+ 13,
1749
+ AgentToolSpec(
1750
+ name="get_evm_uniswap_pools",
1751
+ description=(
1752
+ "Find existing Uniswap V3/V4 pool metadata on ethereum, base, or robinhood using the official "
1753
+ "Uniswap Pool Info API. Returns the canonical poolReferenceIdentifier required to create liquidity; "
1754
+ "this is read-only and does not approve, sign, or execute anything."
1755
+ ),
1756
+ input_schema={
1757
+ "type": "object",
1758
+ "properties": {
1759
+ "protocol": {"type": "string", "enum": ["V3", "V4"]},
1760
+ "pool_parameters": {"type": "object", "description": "Official Pool Info API token-pair parameters, such as tokenAddressA, tokenAddressB and optional fee/tickSpacing/hooks.", "additionalProperties": True},
1761
+ "pool_references": {"type": "array", "maxItems": 20, "items": {"type": "object", "additionalProperties": True}, "description": "One to twenty official Pool Info API pool reference objects."},
1762
+ "page_size": {"type": "integer", "minimum": 1, "maximum": 20},
1763
+ "current_page": {"type": "integer", "minimum": 1},
1764
+ "network": {"type": "string", "enum": ["ethereum", "base", "robinhood"]},
1765
+ },
1766
+ "required": ["protocol"],
1767
+ "additionalProperties": False,
1768
+ },
1769
+ read_only=True,
1770
+ risk_level="low",
1771
+ ),
1772
+ )
1773
+ tools.insert(
1774
+ 14,
1775
+ AgentToolSpec(
1776
+ name="get_evm_uniswap_positions",
1777
+ description=(
1778
+ "List read-only Uniswap V3 LP position NFTs owned by the active wallet on ethereum, base, or robinhood. "
1779
+ "Returns NFT token ids, tokens, fee tier, tick range, liquidity, and currently owed fees. V4 discovery is "
1780
+ "not exposed until a verified indexed source is configured because V4 PositionManager is not enumerable."
1781
+ ),
1782
+ input_schema={
1783
+ "type": "object",
1784
+ "properties": {
1785
+ "protocol": {"type": "string", "enum": ["V3"], "description": "V3 is the currently enumerable, on-chain-supported position scanner."},
1786
+ "limit": {"type": "integer", "minimum": 1, "maximum": 100},
1787
+ "network": {"type": "string", "enum": ["ethereum", "base", "robinhood"]},
1788
+ },
1789
+ "additionalProperties": False,
1790
+ },
1791
+ read_only=True,
1792
+ risk_level="low",
1793
+ ),
1794
+ )
1795
+ tools.insert(
1796
+ 15,
1797
+ AgentToolSpec(
1798
+ name="manage_evm_uniswap_liquidity",
1799
+ description=(
1800
+ "Preview, prepare, or execute a Uniswap V3/V4 liquidity action on ethereum, base, or robinhood. "
1801
+ "Supported actions are create, increase, decrease, and claim_fees. The request is passed to the "
1802
+ "official Uniswap Liquidity API; execute refreshes the transaction immediately before signing. "
1803
+ "Create requires existingPool.poolReference; increase/decrease/claim_fees require a position NFT token id. "
1804
+ "Use get_evm_uniswap_pools or get_evm_uniswap_positions to obtain these identifiers; never guess them."
1805
+ ),
1806
+ input_schema={
1807
+ "type": "object",
1808
+ "properties": {
1809
+ "action": {"type": "string", "enum": ["create", "increase", "decrease", "claim_fees"]},
1810
+ "protocol": {"type": "string", "enum": ["V3", "V4"]},
1811
+ "request": {"type": "object", "description": "Uniswap LP API fields excluding walletAddress, chainId, protocol, and simulateTransaction; those are set from the active wallet.", "additionalProperties": True},
1812
+ "mode": {"type": "string", "enum": ["preview", "prepare", "execute"]},
1813
+ "purpose": {"type": "string"},
1814
+ "user_intent": {"type": "boolean"},
1815
+ "approval_token": {"type": "string"},
1816
+ "network": {"type": "string", "enum": ["ethereum", "base", "robinhood"]},
1817
+ },
1818
+ "required": ["action", "protocol", "request", "mode", "purpose"],
1819
+ "additionalProperties": False,
1820
+ },
1821
+ read_only=False,
1822
+ requires_explicit_user_intent=True,
1823
+ risk_level="high",
1824
+ ),
1825
+ )
1725
1826
  tools.insert(
1726
1827
  14,
1727
1828
  AgentToolSpec(
@@ -5631,6 +5732,46 @@ class OpenClawWalletAdapter:
5631
5732
  )
5632
5733
  return AgentToolResult(tool=tool_name, ok=True, data=data)
5633
5734
 
5735
+ if tool_name == "get_evm_uniswap_pools":
5736
+ protocol = args.get("protocol")
5737
+ pool_parameters = args.get("pool_parameters")
5738
+ pool_references = args.get("pool_references")
5739
+ page_size = args.get("page_size", 20)
5740
+ current_page = args.get("current_page", 1)
5741
+ if protocol not in {"V3", "V4"}:
5742
+ raise WalletBackendError("protocol must be V3 or V4.")
5743
+ if (pool_parameters is None) == (pool_references is None):
5744
+ raise WalletBackendError("Provide exactly one of pool_parameters or pool_references.")
5745
+ if pool_parameters is not None and not isinstance(pool_parameters, dict):
5746
+ raise WalletBackendError("pool_parameters must be an object.")
5747
+ if pool_references is not None:
5748
+ if not isinstance(pool_references, list) or not 1 <= len(pool_references) <= 20:
5749
+ raise WalletBackendError("pool_references must contain between 1 and 20 objects.")
5750
+ if any(not isinstance(reference, dict) for reference in pool_references):
5751
+ raise WalletBackendError("pool_references must contain only objects.")
5752
+ if not isinstance(page_size, int) or not 1 <= page_size <= 20:
5753
+ raise WalletBackendError("page_size must be an integer between 1 and 20.")
5754
+ if not isinstance(current_page, int) or current_page < 1:
5755
+ raise WalletBackendError("current_page must be a positive integer.")
5756
+ data = await active_backend.get_uniswap_liquidity_pools(
5757
+ protocol=protocol,
5758
+ pool_parameters=pool_parameters,
5759
+ pool_references=pool_references,
5760
+ page_size=page_size,
5761
+ current_page=current_page,
5762
+ )
5763
+ return AgentToolResult(tool=tool_name, ok=True, data=data)
5764
+
5765
+ if tool_name == "get_evm_uniswap_positions":
5766
+ protocol = args.get("protocol", "V3")
5767
+ limit = args.get("limit", 20)
5768
+ if protocol != "V3":
5769
+ raise WalletBackendError("Only V3 position discovery is currently available; V4 PositionManager is not enumerable.")
5770
+ if not isinstance(limit, int) or not 1 <= limit <= 100:
5771
+ raise WalletBackendError("limit must be an integer between 1 and 100.")
5772
+ data = await active_backend.get_uniswap_liquidity_positions(protocol=protocol, limit=limit)
5773
+ return AgentToolResult(tool=tool_name, ok=True, data=data)
5774
+
5634
5775
  if tool_name == "swap_evm_uniswap_tokens":
5635
5776
  token_in = args.get("token_in")
5636
5777
  token_out = args.get("token_out")
@@ -5757,6 +5898,127 @@ class OpenClawWalletAdapter:
5757
5898
  ),
5758
5899
  )
5759
5900
 
5901
+ if tool_name == "manage_evm_uniswap_liquidity":
5902
+ action = args.get("action")
5903
+ protocol = args.get("protocol")
5904
+ request = args.get("request")
5905
+ mode = args.get("mode")
5906
+ purpose = args.get("purpose")
5907
+ user_intent = args.get("user_intent", False)
5908
+ approval_token = args.get("approval_token")
5909
+ if action not in {"create", "increase", "decrease", "claim_fees"}:
5910
+ raise WalletBackendError("action must be create, increase, decrease, or claim_fees.")
5911
+ if protocol not in {"V3", "V4"}:
5912
+ raise WalletBackendError("protocol must be V3 or V4.")
5913
+ if not isinstance(request, dict):
5914
+ raise WalletBackendError("request must be an object.")
5915
+ if mode not in {"preview", "prepare", "execute"}:
5916
+ raise WalletBackendError("mode must be 'preview', 'prepare' or 'execute'.")
5917
+ if not isinstance(purpose, str) or not purpose.strip():
5918
+ raise WalletBackendError("purpose is required.")
5919
+ normalized_request = dict(request)
5920
+ if action == "create":
5921
+ existing_pool = normalized_request.get("existingPool")
5922
+ if not isinstance(existing_pool, dict):
5923
+ raise WalletBackendError(
5924
+ "create requires existingPool with token0Address, token1Address, and poolReference. "
5925
+ "Use get_evm_uniswap_pools to obtain the exact existing pool; do not infer it from market search data."
5926
+ )
5927
+ missing_pool_fields = [
5928
+ field
5929
+ for field in ("token0Address", "token1Address", "poolReference")
5930
+ if not isinstance(existing_pool.get(field), str) or not existing_pool[field].strip()
5931
+ ]
5932
+ if missing_pool_fields:
5933
+ raise WalletBackendError(
5934
+ "create existingPool is missing "
5935
+ f"{', '.join(missing_pool_fields)}. Ask the user for the exact existing Uniswap pool; do not infer it from market search data."
5936
+ )
5937
+ elif action in {"increase", "decrease"}:
5938
+ nft_token_id = normalized_request.get("nftTokenId")
5939
+ if not isinstance(nft_token_id, str) or not nft_token_id.strip().isdigit() or int(nft_token_id.strip()) <= 0:
5940
+ raise WalletBackendError(
5941
+ f"{action} requires a positive nftTokenId. Use get_evm_uniswap_positions for V3 or request a verified V4 token id; never infer it."
5942
+ )
5943
+ normalized_request["nftTokenId"] = nft_token_id.strip()
5944
+ else: # claim_fees
5945
+ token_id = normalized_request.get("tokenId", normalized_request.get("nftTokenId"))
5946
+ if not isinstance(token_id, str) or not token_id.strip().isdigit() or int(token_id.strip()) <= 0:
5947
+ raise WalletBackendError(
5948
+ "claim_fees requires the user's positive tokenId (the LP position NFT id). "
5949
+ "Use get_evm_uniswap_positions for V3 or request a verified V4 token id; never infer it."
5950
+ )
5951
+ # The Liquidity API names this field tokenId for fee claims;
5952
+ # accept nftTokenId as the user-facing alias used by other LP actions.
5953
+ normalized_request["tokenId"] = token_id.strip()
5954
+ normalized_request.pop("nftTokenId", None)
5955
+ # The caller must not override values that are derived from the
5956
+ # active wallet/network in the WDK layer.
5957
+ forbidden = {"walletAddress", "chainId", "protocol", "simulateTransaction", "signature", "batchPermitData", "v4BatchPermitData", "v3NftPermitData"}
5958
+ overlap = forbidden.intersection(request)
5959
+ if overlap:
5960
+ raise WalletBackendError(f"request must not set wallet-controlled fields: {', '.join(sorted(overlap))}.")
5961
+ preview_kwargs = {"action": action, "protocol": protocol, "request": normalized_request}
5962
+ if mode == "preview":
5963
+ preview = await active_backend.preview_uniswap_liquidity(**preview_kwargs)
5964
+ return AgentToolResult(tool=tool_name, ok=True, data=self._annotate_sensitive_payload(preview, action_label="Uniswap liquidity", mode="preview"))
5965
+ if mode == "prepare":
5966
+ self._require_prepare_intent(user_intent)
5967
+ preview = await active_backend.preview_uniswap_liquidity(**preview_kwargs)
5968
+ return AgentToolResult(tool=tool_name, ok=True, data=self._annotate_sensitive_payload(self._build_prepare_plan(preview_payload=preview, action_label="Uniswap liquidity"), action_label="Uniswap liquidity", mode="prepare"))
5969
+ if isinstance(approval_token, str) and approval_token.strip():
5970
+ approval_payload = inspect_approval_token(
5971
+ approval_token,
5972
+ tool_name=tool_name,
5973
+ network=str(getattr(active_backend, "network", "unknown")),
5974
+ require_mainnet_confirmation=self._is_mainnet_for_backend(active_backend),
5975
+ )
5976
+ approval_summary = approval_payload.get("binding", {}).get("summary")
5977
+ if not isinstance(approval_summary, dict):
5978
+ raise WalletBackendError("approval_token does not match the requested LP operation. Generate a new approval after prepare.")
5979
+ approval_summary_copy = dict(approval_summary)
5980
+ else:
5981
+ fresh_preview = await active_backend.preview_uniswap_liquidity(**preview_kwargs)
5982
+ approval_summary_copy = self._build_confirmation_summary(action_label="Uniswap liquidity", payload=fresh_preview)
5983
+ expected = {
5984
+ "operation": "Uniswap liquidity",
5985
+ "network": str(getattr(active_backend, "network", "unknown")),
5986
+ "liquidity_action": action,
5987
+ "liquidity_protocol": protocol,
5988
+ }
5989
+ for key, value in expected.items():
5990
+ if approval_summary_copy.get(key) != value:
5991
+ raise WalletBackendError("approval_token does not match the requested LP operation. Generate a new approval after prepare.")
5992
+ # An LP intent deliberately does not bind API-calculated ticks,
5993
+ # price, calldata, or quote amounts: those are regenerated just
5994
+ # before broadcast. It must still be scoped to the selected
5995
+ # assets/pool/position so an approval cannot be reused for a
5996
+ # different liquidity position.
5997
+ requested_scope = self._build_confirmation_summary(
5998
+ action_label="Uniswap liquidity",
5999
+ payload={
6000
+ "asset_type": "evm-uniswap-liquidity",
6001
+ "network": str(getattr(active_backend, "network", "unknown")),
6002
+ "protocol": "uniswap",
6003
+ "liquidity_action": action,
6004
+ "liquidity_protocol": protocol,
6005
+ "request": preview_kwargs["request"],
6006
+ },
6007
+ )
6008
+ for key in ("token0_address", "token1_address", "pool_reference", "position_token_id", "independent_token"):
6009
+ requested_value = requested_scope.get(key)
6010
+ if requested_value is not None and approval_summary_copy.get(key) != requested_value:
6011
+ raise WalletBackendError("approval_token does not match the requested LP assets or position. Generate a new approval after prepare.")
6012
+ self._require_execute_approval(
6013
+ approval_token=approval_token,
6014
+ tool_name=tool_name,
6015
+ summary=approval_summary_copy,
6016
+ action_label="Uniswap liquidity",
6017
+ backend=active_backend,
6018
+ )
6019
+ result = await active_backend.send_uniswap_liquidity(**preview_kwargs)
6020
+ return AgentToolResult(tool=tool_name, ok=True, data=self._annotate_sensitive_payload(result, action_label="Uniswap liquidity", mode="execute"))
6021
+
5760
6022
  if tool_name == "issue_wallet_approval":
5761
6023
  from agent_wallet.approval import issue_approval_token
5762
6024
 
@@ -40,6 +40,8 @@ LONG_RUNNING_POST_PATHS = {
40
40
  "/v1/evm/swap/send",
41
41
  "/v1/evm/uniswap/swap/quote",
42
42
  "/v1/evm/uniswap/swap/send",
43
+ "/v1/evm/uniswap/liquidity/quote",
44
+ "/v1/evm/uniswap/liquidity/send",
43
45
  "/v1/evm/lifi/quote",
44
46
  "/v1/evm/lifi/send",
45
47
  "/v1/evm/transfer/send",
@@ -337,6 +337,25 @@ class AgentWalletBackend(ABC):
337
337
  ) -> dict[str, Any]:
338
338
  raise WalletBackendError(f"{self.name} does not support Uniswap pair search.")
339
339
 
340
+ async def get_uniswap_liquidity_pools(
341
+ self,
342
+ *,
343
+ protocol: str,
344
+ pool_parameters: dict[str, Any] | None = None,
345
+ pool_references: list[dict[str, Any]] | None = None,
346
+ page_size: int = 20,
347
+ current_page: int = 1,
348
+ ) -> dict[str, Any]:
349
+ raise WalletBackendError(f"{self.name} does not support Uniswap liquidity pool discovery.")
350
+
351
+ async def get_uniswap_liquidity_positions(
352
+ self,
353
+ *,
354
+ protocol: str = "V3",
355
+ limit: int = 20,
356
+ ) -> dict[str, Any]:
357
+ raise WalletBackendError(f"{self.name} does not support Uniswap liquidity position discovery.")
358
+
340
359
  async def send_uniswap_swap(
341
360
  self,
342
361
  *,
@@ -349,6 +368,24 @@ class AgentWalletBackend(ABC):
349
368
  ) -> dict[str, Any]:
350
369
  raise WalletBackendError(f"{self.name} does not support Uniswap swaps.")
351
370
 
371
+ async def preview_uniswap_liquidity(
372
+ self,
373
+ *,
374
+ action: str,
375
+ protocol: str,
376
+ request: dict[str, Any],
377
+ ) -> dict[str, Any]:
378
+ raise WalletBackendError(f"{self.name} does not support Uniswap liquidity previews.")
379
+
380
+ async def send_uniswap_liquidity(
381
+ self,
382
+ *,
383
+ action: str,
384
+ protocol: str,
385
+ request: dict[str, Any],
386
+ ) -> dict[str, Any]:
387
+ raise WalletBackendError(f"{self.name} does not support Uniswap liquidity operations.")
388
+
352
389
  async def preview_evm_native_transfer(
353
390
  self,
354
391
  *,