@agentlayer.tech/wallet 0.1.65 → 0.1.67

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 (28) hide show
  1. package/.openclaw/extensions/agent-wallet/index.ts +82 -0
  2. package/.openclaw/extensions/agent-wallet/openclaw.plugin.json +7 -1
  3. package/.openclaw/extensions/agent-wallet/package.json +1 -1
  4. package/VERSION +1 -1
  5. package/agent-wallet/README.md +6 -0
  6. package/agent-wallet/agent_wallet/__init__.py +1 -1
  7. package/agent-wallet/agent_wallet/openclaw_adapter.py +485 -4
  8. package/agent-wallet/agent_wallet/providers/kamino.py +169 -3
  9. package/agent-wallet/agent_wallet/providers/x402.py +229 -148
  10. package/agent-wallet/agent_wallet/transaction_policy.py +60 -0
  11. package/agent-wallet/agent_wallet/wallet_layer/base.py +124 -0
  12. package/agent-wallet/agent_wallet/wallet_layer/solana.py +563 -0
  13. package/agent-wallet/agent_wallet/wallet_layer/wdk_evm.py +16 -0
  14. package/agent-wallet/openclaw.plugin.json +2 -2
  15. package/agent-wallet/pyproject.toml +1 -1
  16. package/agent-wallet/scripts/install_openclaw_local_config.py +6 -0
  17. package/agent-wallet/skills/wallet-operator/SKILL.md +6 -4
  18. package/claude-code/plugins/agent-wallet/.claude-plugin/plugin.json +1 -1
  19. package/claude-code/plugins/agent-wallet/skills/wallet-operator/SKILL.md +1 -1
  20. package/codex/plugins/agent-wallet/.codex-plugin/plugin.json +1 -1
  21. package/codex/plugins/agent-wallet/server.py +52 -3
  22. package/codex/plugins/agent-wallet/skills/wallet-operator/SKILL.md +1 -1
  23. package/hermes/plugins/agent_wallet/plugin.yaml +1 -1
  24. package/install-from-github.sh +6 -0
  25. package/package.json +1 -1
  26. package/wdk-btc-wallet/package.json +1 -1
  27. package/wdk-evm-wallet/package.json +1 -1
  28. package/wdk-evm-wallet/src/wdk_evm_wallet.js +126 -19
@@ -29,6 +29,10 @@ LEGACY_ALLOWLIST_TOOLS = [
29
29
  "set_wallet_backend",
30
30
  "get_wallet_portfolio",
31
31
  "get_solana_token_prices",
32
+ "get_kamino_portfolio",
33
+ "get_kamino_vaults",
34
+ "get_kamino_earn_positions",
35
+ "get_kamino_liquidity_positions",
32
36
  "get_kamino_lend_markets",
33
37
  "get_kamino_lend_market_reserves",
34
38
  "get_kamino_lend_user_obligations",
@@ -37,6 +41,8 @@ LEGACY_ALLOWLIST_TOOLS = [
37
41
  "kamino_lend_withdraw",
38
42
  "kamino_lend_borrow",
39
43
  "kamino_lend_repay",
44
+ "kamino_earn_deposit",
45
+ "kamino_earn_withdraw",
40
46
  "sign_wallet_message",
41
47
  "transfer_sol",
42
48
  "transfer_btc",
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: wallet-operator
3
- description: Use when operating OpenClaw wallet tools: balances, transfers, swaps, LI.FI cross-chain swaps, Jupiter swaps, Velora EVM swaps, BTC transfers, staking, Kamino lending, Bags launches, and wallet execution safety.
3
+ description: Use whenever the user's own funds are involved, even if they never say "wallet": balances, portfolio, transfers, swaps, LI.FI cross-chain swaps, Jupiter swaps, Velora EVM swaps, BTC transfers, staking, Kamino lending, Bags launches, and wallet execution safety.
4
4
  ---
5
5
 
6
6
  # Wallet Operator
@@ -30,7 +30,7 @@ Use this skill before calling OpenClaw wallet tools. It is the routing guide for
30
30
  - EVM transfers: `transfer_evm_native`, `transfer_evm_token`.
31
31
  - BTC transfer: `transfer_btc`.
32
32
  - Solana staking: `stake_sol_native`, `deactivate_solana_stake`, `withdraw_solana_stake`.
33
- - Kamino: `kamino_lend_deposit`, `kamino_lend_withdraw`, `kamino_lend_borrow`, `kamino_lend_repay`.
33
+ - Kamino: `get_kamino_portfolio`, `get_kamino_vaults`, `get_kamino_earn_positions`, `get_kamino_liquidity_positions`, `kamino_lend_deposit`, `kamino_lend_withdraw`, `kamino_lend_borrow`, `kamino_lend_repay`, `kamino_earn_deposit`, `kamino_earn_withdraw`.
34
34
  - Bags: `launch_bags_token`.
35
35
 
36
36
  ## Common Token IDs
@@ -118,8 +118,10 @@ Use this skill before calling OpenClaw wallet tools. It is the routing guide for
118
118
  - `stake_sol_native`: `vote_account`, `amount` in SOL, `mode`, `purpose`.
119
119
  - `deactivate_solana_stake`: `stake_account`, `mode`, `purpose`.
120
120
  - `withdraw_solana_stake`: `stake_account`, `amount` in SOL, optional `recipient`, `mode`, `purpose`.
121
- - Before Kamino writes, use `get_kamino_lend_markets`, `get_kamino_lend_market_reserves`, `get_kamino_lend_user_obligations`, and `get_kamino_lend_user_rewards`.
122
- - Kamino write params: `market`, `reserve`, `amount_ui` decimal string, `mode`, `purpose`.
121
+ - Before Kamino lend writes, use `get_kamino_lend_markets`, `get_kamino_lend_market_reserves`, `get_kamino_lend_user_obligations`, and `get_kamino_lend_user_rewards`.
122
+ - For broader Kamino read coverage, prefer `get_kamino_portfolio` first, then drill into `get_kamino_vaults`, `get_kamino_earn_positions`, or `get_kamino_liquidity_positions` as needed.
123
+ - Kamino lend write params: `market`, `reserve`, `amount_ui` decimal string, `mode`, `purpose`.
124
+ - Kamino Earn write params: `kvault`, `amount_ui` decimal string, `mode`, `purpose`.
123
125
  - `launch_bags_token`: `name`, `symbol`, `description`, `base_mint`, `claimers`, `basis_points`, `initial_buy_sol`, `mode`, `purpose`; optional socials/image/config type.
124
126
  - `close_empty_token_accounts`: `limit`, `mode` (`preview` or `execute`), `purpose`.
125
127
 
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "agent-wallet",
3
3
  "displayName": "Agent Wallet",
4
- "version": "0.1.65",
4
+ "version": "0.1.67",
5
5
  "description": "Claude Code bridge for the existing AgentLayer wallet runtime. Connects to Solana, Bitcoin, and EVM wallets without creating a new one.",
6
6
  "author": {
7
7
  "name": "AgentLayer"
@@ -1,5 +1,5 @@
1
1
  ---
2
- description: "Use when the user asks Claude Code to interact with the local AgentLayer wallet runtime: check balances, transfer tokens, swap, DeFi operations, or x402 payments. Prefer wallet tools over shell commands. Preview writes first and execute only after explicit user confirmation."
2
+ description: "Use whenever the user's own funds or wallet are involved — balances, portfolio, transfers, swaps, bridging, staking, lending, or x402 payments — even if the user never says \"wallet\". Trigger phrases include \"my balance\", \"how much SOL/ETH/BTC/USDC do I have\", \"send/transfer X to\", \"swap\", \"bridge\", \"stake\", \"my portfolio\", \"pay for this API\", \"x402\". Prefer agent-wallet MCP tools over shell commands, raw RPC, or crypto data servers. Preview writes first and execute only after explicit user confirmation."
3
3
  ---
4
4
 
5
5
  # Agent Wallet Operator
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agent-wallet",
3
- "version": "0.1.65",
3
+ "version": "0.1.67",
4
4
  "description": "Codex plugin bridge for the AgentLayer wallet runtime.",
5
5
  "author": {
6
6
  "name": "AgentLayer"
@@ -273,6 +273,44 @@ def _cache_preview_for_approval(user_id: str, tool_name: str, payload: dict[str,
273
273
  }
274
274
 
275
275
 
276
+ X402_PREVIEW_REUSE_TTL_SECONDS = 300
277
+
278
+
279
+ def _cache_x402_preview_payload(user_id: str, payload: dict[str, Any]) -> None:
280
+ """Cache a paid-endpoint preview so x402_pay_request can skip its unpaid probe.
281
+
282
+ x402 previews run on the read-only resident-worker path, so they never reach
283
+ _cache_preview_for_approval (and preview-mode annotation strips
284
+ confirmation_summary anyway). Stored under the x402_preview_request key that
285
+ the pay alias resolves to. The wallet runtime revalidates the payload by
286
+ request fingerprint and falls back to a fresh probe on any mismatch, so a
287
+ stale or unrelated cached preview can never redirect a payment.
288
+ """
289
+ if not isinstance(payload, dict) or payload.get("ok") is False:
290
+ return
291
+ data = payload.get("data")
292
+ if not isinstance(data, dict):
293
+ return
294
+ if not data.get("payment_required") or not str(data.get("request_fingerprint") or "").strip():
295
+ return
296
+ with _approval_cache_lock:
297
+ _prune_approval_preview_cache()
298
+ approval_preview_cache[_approval_cache_key(user_id, "x402_preview_request")] = {
299
+ "digest": _preview_digest(data),
300
+ "expires_at": time.time() + X402_PREVIEW_REUSE_TTL_SECONDS,
301
+ "preview": data,
302
+ "summary": None,
303
+ }
304
+
305
+
306
+ def _attach_x402_approved_preview(tool_name: str, effective_params: dict[str, Any]) -> None:
307
+ if tool_name != "x402_pay_request" or "_approved_preview" in effective_params:
308
+ return
309
+ cached = _latest_cached_preview(_user_id(), tool_name)
310
+ if cached and isinstance(cached.get("preview"), dict):
311
+ effective_params["_approved_preview"] = cached["preview"]
312
+
313
+
276
314
  def _latest_cached_preview(user_id: str, tool_name: str) -> dict[str, Any] | None:
277
315
  with _approval_cache_lock:
278
316
  _prune_approval_preview_cache()
@@ -1403,6 +1441,7 @@ def _invoke_wallet_tool_blocking(
1403
1441
  cancellation stay responsive).
1404
1442
  """
1405
1443
  used_cache, approval_args = _attach_approval_for_execute(tool_name, config, effective_params)
1444
+ _attach_x402_approved_preview(tool_name, effective_params)
1406
1445
  try:
1407
1446
  payload = _invoke_tool(
1408
1447
  tool_name,
@@ -1413,6 +1452,10 @@ def _invoke_wallet_tool_blocking(
1413
1452
  )
1414
1453
  except Exception as exc:
1415
1454
  raise _normalize_approval_context_error(exc) from exc
1455
+ if tool_name == "x402_pay_request":
1456
+ # The cached x402 preview quote is single-use: whether the payment
1457
+ # succeeded or the endpoint re-priced, the next pay must re-preview.
1458
+ _consume_cached_preview(_user_id(), tool_name)
1416
1459
  _cache_preview_for_approval(_user_id(), tool_name, payload)
1417
1460
  # A bridge-managed preview that was just executed successfully is single-use:
1418
1461
  # drop it so a duplicate execute cannot silently re-run the operation.
@@ -1451,15 +1494,21 @@ async def _handle_wallet_tool(tool_name: str, params: dict[str, Any]) -> dict[st
1451
1494
  _invoke_wallet_tool_blocking, tool_name, config, effective_params
1452
1495
  )
1453
1496
 
1497
+ if tool_name == "x402_preview_request":
1498
+ _cache_x402_preview_payload(_user_id(), payload)
1454
1499
  if payload.get("ok") is False:
1455
1500
  raise RuntimeError(str(payload.get("error") or f"{tool_name} failed"))
1456
1501
  return payload.get("data", {})
1457
1502
 
1458
1503
 
1459
1504
  BASE_INSTRUCTIONS = (
1460
- "Use the local AgentLayer wallet runtime through explicit wallet tools. Keep wallet "
1461
- "secrets local. Preview writes first when supported, and execute only after explicit "
1462
- "user confirmation."
1505
+ "This server is the user's own local AgentLayer wallet (Solana, EVM, Bitcoin). Use its "
1506
+ "tools whenever the user asks about their balances, portfolio, addresses, transfers, "
1507
+ "swaps, bridging, staking, lending, or x402 payments — even when they never say the "
1508
+ "word 'wallet'. For anything touching the user's own funds, prefer these tools over "
1509
+ "shell commands, raw RPC calls, or other crypto data servers (those are for arbitrary "
1510
+ "addresses and market data, not the user's wallet). Keep wallet secrets local. Preview "
1511
+ "writes first when supported, and execute only after explicit user confirmation."
1463
1512
  )
1464
1513
 
1465
1514
 
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: "wallet-operator"
3
- description: "Use when the user asks Codex to interact with the local AgentLayer wallet runtime. Prefer wallet tools over shell commands, preview writes first, and keep approval/signing semantics intact."
3
+ description: "Use whenever the user's own funds or wallet are involved — balances, portfolio, transfers, swaps, bridging, staking, lending, or x402 payments — even if the user never says \"wallet\". Trigger phrases include \"my balance\", \"how much SOL/ETH/BTC/USDC do I have\", \"send/transfer X to\", \"swap\", \"bridge\", \"stake\", \"my portfolio\". Prefer wallet tools over shell commands, preview writes first, and keep approval/signing semantics intact."
4
4
  ---
5
5
 
6
6
  # Agent Wallet Operator
@@ -1,5 +1,5 @@
1
1
  name: agent-wallet
2
- version: 0.1.65
2
+ version: 0.1.67
3
3
  description: Thin Hermes Agent bridge to the existing AgentLayer/OpenClaw wallet backend
4
4
  provides_tools:
5
5
  - agent_wallet_tools
@@ -120,9 +120,15 @@ if [ -z "$SOURCE_ROOT" ] || [ ! -d "$SOURCE_ROOT" ]; then
120
120
  exit 1
121
121
  fi
122
122
 
123
+ # Pre-flight must be a superset of setup.sh's require_path list: everything is
124
+ # checked on the EXTRACTED temp tree, before the destructive swap below. A
125
+ # bundle that would fail setup.sh must never replace the user's runtime.
123
126
  require_path "$SOURCE_ROOT/setup.sh" "local setup entrypoint"
124
127
  require_path "$SOURCE_ROOT/agent-wallet" "agent-wallet package"
128
+ require_path "$SOURCE_ROOT/agent-wallet/scripts/install_agent_wallet.py" "Python installer"
125
129
  require_path "$SOURCE_ROOT/.openclaw/extensions/agent-wallet" "OpenClaw extension"
130
+ require_path "$SOURCE_ROOT/codex/plugins/agent-wallet/.codex-plugin/plugin.json" "Codex plugin"
131
+ require_path "$SOURCE_ROOT/claude-code/plugins/agent-wallet/.claude-plugin/plugin.json" "Claude Code plugin"
126
132
  require_path "$SOURCE_ROOT/wdk-btc-wallet/package.json" "wdk-btc-wallet runtime"
127
133
  require_path "$SOURCE_ROOT/wdk-evm-wallet/package.json" "wdk-evm-wallet runtime"
128
134
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agentlayer.tech/wallet",
3
- "version": "0.1.65",
3
+ "version": "0.1.67",
4
4
  "description": "NPM installer for the OpenClaw Agent Wallet local runtime.",
5
5
  "type": "module",
6
6
  "repository": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wdk-btc-wallet",
3
- "version": "0.1.65",
3
+ "version": "0.1.67",
4
4
  "private": true,
5
5
  "type": "module",
6
6
  "description": "Separate BTC-only wallet service built on Tether WDK.",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wdk-evm-wallet",
3
- "version": "0.1.65",
3
+ "version": "0.1.67",
4
4
  "private": true,
5
5
  "type": "module",
6
6
  "description": "Separate EVM wallet service built on Tether WDK.",
@@ -94,8 +94,38 @@ const LIFI_CHAIN_ALIASES = {
94
94
  sol: "1151111081099710",
95
95
  solana: "1151111081099710",
96
96
  };
97
- const MORPHO_DEFAULT_LIST_LIMIT = 100;
97
+ // Default is deliberately small: list responses land in an LLM agent's
98
+ // context window (a 100-item vault list is ~80 KB of JSON). Agents can raise
99
+ // the limit explicitly when they really need more.
100
+ const MORPHO_DEFAULT_LIST_LIMIT = 20;
98
101
  const MORPHO_MAX_LIST_LIMIT = 500;
102
+ // Discovery lists/details only — never user positions. APY/TVL drift over
103
+ // two minutes is below the indexer's own aggregation noise.
104
+ const MORPHO_DISCOVERY_CACHE_TTL_MS = 120_000;
105
+ const MORPHO_DISCOVERY_CACHE_MAX_ENTRIES = 64;
106
+ const morphoDiscoveryCache = new Map();
107
+
108
+ function morphoDiscoveryCacheGet(key) {
109
+ const entry = morphoDiscoveryCache.get(key);
110
+ if (!entry) {
111
+ return null;
112
+ }
113
+ if (entry.expiresAt <= Date.now()) {
114
+ morphoDiscoveryCache.delete(key);
115
+ return null;
116
+ }
117
+ return JSON.parse(entry.payload);
118
+ }
119
+
120
+ function morphoDiscoveryCacheSet(key, payload) {
121
+ if (morphoDiscoveryCache.size >= MORPHO_DISCOVERY_CACHE_MAX_ENTRIES) {
122
+ morphoDiscoveryCache.delete(morphoDiscoveryCache.keys().next().value);
123
+ }
124
+ morphoDiscoveryCache.set(key, {
125
+ expiresAt: Date.now() + MORPHO_DISCOVERY_CACHE_TTL_MS,
126
+ payload: JSON.stringify(payload),
127
+ });
128
+ }
99
129
  const MORPHO_VAULT_LIST_QUERY = `
100
130
  query MorphoVaultV2List($first: Int!, $where: VaultV2sFilters, $orderBy: VaultV2OrderBy, $orderDirection: OrderDirection) {
101
131
  vaultV2s(first: $first, where: $where, orderBy: $orderBy, orderDirection: $orderDirection) {
@@ -112,6 +142,7 @@ const MORPHO_VAULT_LIST_QUERY = `
112
142
  idleAssets
113
143
  idleAssetsUsd
114
144
  sharePrice
145
+ netApy
115
146
  avgNetApy
116
147
  avgNetApyExcludingRewards
117
148
  performanceFee
@@ -666,6 +697,17 @@ function normalizeMorphoSearch(value) {
666
697
  return value.trim() || null;
667
698
  }
668
699
 
700
+ function normalizeOptionalNonNegativeNumber(value, fieldName) {
701
+ if (value === undefined || value === null || value === "") {
702
+ return null;
703
+ }
704
+ const num = Number(value);
705
+ if (!Number.isFinite(num) || num < 0) {
706
+ throw new Error(`${fieldName} must be a non-negative number.`);
707
+ }
708
+ return num;
709
+ }
710
+
669
711
  function normalizeOptionalAddressFilter(value, fieldName) {
670
712
  if (value === undefined || value === null || value === "") {
671
713
  return null;
@@ -1925,21 +1967,29 @@ export class WdkEvmWalletService {
1925
1967
  limit,
1926
1968
  listedOnly = true,
1927
1969
  assetAddress = null,
1970
+ minTotalAssetsUsd = null,
1971
+ minNetApy = null,
1928
1972
  orderBy,
1929
1973
  orderDirection,
1930
1974
  }) {
1931
1975
  const runtimeConfig = this.#resolveRuntimeConfig(network);
1932
1976
  assertMorphoSupportedNetwork(runtimeConfig.network);
1933
1977
  if (vaultAddress !== null && vaultAddress !== undefined && String(vaultAddress).trim()) {
1978
+ const address = normalizeAddress(vaultAddress, "vaultAddress");
1979
+ const cacheKey = JSON.stringify(["vault", runtimeConfig.chainId, address]);
1980
+ const cached = morphoDiscoveryCacheGet(cacheKey);
1981
+ if (cached) {
1982
+ return cached;
1983
+ }
1934
1984
  const data = await this.#morphoGraphqlRequest({
1935
1985
  query: MORPHO_VAULT_BY_ADDRESS_QUERY,
1936
1986
  variables: {
1937
- address: normalizeAddress(vaultAddress, "vaultAddress"),
1987
+ address,
1938
1988
  chainId: runtimeConfig.chainId,
1939
1989
  },
1940
1990
  operationName: "MorphoVaultV2ByAddress",
1941
1991
  });
1942
- return {
1992
+ const result = {
1943
1993
  network: runtimeConfig.network,
1944
1994
  chainId: runtimeConfig.chainId,
1945
1995
  protocol: "morpho",
@@ -1947,10 +1997,17 @@ export class WdkEvmWalletService {
1947
1997
  found: Boolean(data.vaultV2ByAddress),
1948
1998
  source: "morpho-api",
1949
1999
  };
2000
+ morphoDiscoveryCacheSet(cacheKey, result);
2001
+ return result;
1950
2002
  }
1951
2003
  const first = normalizeMorphoListLimit(limit);
1952
2004
  const onlyListed = normalizeMorphoListedOnly(listedOnly);
1953
2005
  const assetAddressFilter = normalizeOptionalAddressFilter(assetAddress, "assetAddress");
2006
+ const minTotalAssetsUsdFilter = normalizeOptionalNonNegativeNumber(
2007
+ minTotalAssetsUsd,
2008
+ "minTotalAssetsUsd"
2009
+ );
2010
+ const minNetApyFilter = normalizeOptionalNonNegativeNumber(minNetApy, "minNetApy");
1954
2011
  const resolvedOrderBy = normalizeMorphoOrderBy(
1955
2012
  orderBy,
1956
2013
  MORPHO_VAULT_ORDER_LOOKUP,
@@ -1964,18 +2021,30 @@ export class WdkEvmWalletService {
1964
2021
  if (assetAddressFilter) {
1965
2022
  where.assetAddress_in = assetAddressFilter;
1966
2023
  }
2024
+ if (minTotalAssetsUsdFilter !== null) {
2025
+ where.totalAssetsUsd_gte = minTotalAssetsUsdFilter;
2026
+ }
2027
+ if (minNetApyFilter !== null) {
2028
+ where.netApy_gte = minNetApyFilter;
2029
+ }
2030
+ const variables = {
2031
+ first,
2032
+ where,
2033
+ orderBy: resolvedOrderBy,
2034
+ orderDirection: resolvedOrderDirection,
2035
+ };
2036
+ const cacheKey = JSON.stringify(["vaults", variables]);
2037
+ const cached = morphoDiscoveryCacheGet(cacheKey);
2038
+ if (cached) {
2039
+ return cached;
2040
+ }
1967
2041
  const data = await this.#morphoGraphqlRequest({
1968
2042
  query: MORPHO_VAULT_LIST_QUERY,
1969
- variables: {
1970
- first,
1971
- where,
1972
- orderBy: resolvedOrderBy,
1973
- orderDirection: resolvedOrderDirection,
1974
- },
2043
+ variables,
1975
2044
  operationName: "MorphoVaultV2List",
1976
2045
  });
1977
2046
  const vaults = Array.isArray(data?.vaultV2s?.items) ? data.vaultV2s.items : [];
1978
- return {
2047
+ const result = {
1979
2048
  network: runtimeConfig.network,
1980
2049
  chainId: runtimeConfig.chainId,
1981
2050
  protocol: "morpho",
@@ -1984,10 +2053,14 @@ export class WdkEvmWalletService {
1984
2053
  orderBy: resolvedOrderBy,
1985
2054
  orderDirection: resolvedOrderDirection,
1986
2055
  assetAddressFilter,
2056
+ minTotalAssetsUsd: minTotalAssetsUsdFilter,
2057
+ minNetApy: minNetApyFilter,
1987
2058
  vaultCount: vaults.length,
1988
2059
  vaults,
1989
2060
  source: "morpho-api",
1990
2061
  };
2062
+ morphoDiscoveryCacheSet(cacheKey, result);
2063
+ return result;
1991
2064
  }
1992
2065
 
1993
2066
  async getMorphoMarkets({
@@ -1998,21 +2071,29 @@ export class WdkEvmWalletService {
1998
2071
  search,
1999
2072
  collateralAssetAddress = null,
2000
2073
  loanAssetAddress = null,
2074
+ minSupplyAssetsUsd = null,
2075
+ minNetSupplyApy = null,
2001
2076
  orderBy,
2002
2077
  orderDirection,
2003
2078
  }) {
2004
2079
  const runtimeConfig = this.#resolveRuntimeConfig(network);
2005
2080
  assertMorphoSupportedNetwork(runtimeConfig.network);
2006
2081
  if (marketId !== null && marketId !== undefined && String(marketId).trim()) {
2082
+ const normalizedMarketId = assertMorphoMarketId(marketId, "marketId");
2083
+ const cacheKey = JSON.stringify(["market", runtimeConfig.chainId, normalizedMarketId]);
2084
+ const cached = morphoDiscoveryCacheGet(cacheKey);
2085
+ if (cached) {
2086
+ return cached;
2087
+ }
2007
2088
  const data = await this.#morphoGraphqlRequest({
2008
2089
  query: MORPHO_MARKET_BY_ID_QUERY,
2009
2090
  variables: {
2010
- marketId: assertMorphoMarketId(marketId, "marketId"),
2091
+ marketId: normalizedMarketId,
2011
2092
  chainId: runtimeConfig.chainId,
2012
2093
  },
2013
2094
  operationName: "MorphoMarketById",
2014
2095
  });
2015
- return {
2096
+ const result = {
2016
2097
  network: runtimeConfig.network,
2017
2098
  chainId: runtimeConfig.chainId,
2018
2099
  protocol: "morpho",
@@ -2020,6 +2101,8 @@ export class WdkEvmWalletService {
2020
2101
  found: Boolean(data.marketById),
2021
2102
  source: "morpho-api",
2022
2103
  };
2104
+ morphoDiscoveryCacheSet(cacheKey, result);
2105
+ return result;
2023
2106
  }
2024
2107
  const first = normalizeMorphoListLimit(limit);
2025
2108
  const onlyListed = normalizeMorphoListedOnly(listedOnly);
@@ -2029,6 +2112,14 @@ export class WdkEvmWalletService {
2029
2112
  "collateralAssetAddress"
2030
2113
  );
2031
2114
  const loanFilter = normalizeOptionalAddressFilter(loanAssetAddress, "loanAssetAddress");
2115
+ const minSupplyAssetsUsdFilter = normalizeOptionalNonNegativeNumber(
2116
+ minSupplyAssetsUsd,
2117
+ "minSupplyAssetsUsd"
2118
+ );
2119
+ const minNetSupplyApyFilter = normalizeOptionalNonNegativeNumber(
2120
+ minNetSupplyApy,
2121
+ "minNetSupplyApy"
2122
+ );
2032
2123
  const resolvedOrderBy = normalizeMorphoOrderBy(
2033
2124
  orderBy,
2034
2125
  MORPHO_MARKET_ORDER_LOOKUP,
@@ -2048,18 +2139,30 @@ export class WdkEvmWalletService {
2048
2139
  if (loanFilter) {
2049
2140
  where.loanAssetAddress_in = loanFilter;
2050
2141
  }
2142
+ if (minSupplyAssetsUsdFilter !== null) {
2143
+ where.supplyAssetsUsd_gte = minSupplyAssetsUsdFilter;
2144
+ }
2145
+ if (minNetSupplyApyFilter !== null) {
2146
+ where.netSupplyApy_gte = minNetSupplyApyFilter;
2147
+ }
2148
+ const variables = {
2149
+ first,
2150
+ where,
2151
+ orderBy: resolvedOrderBy,
2152
+ orderDirection: resolvedOrderDirection,
2153
+ };
2154
+ const cacheKey = JSON.stringify(["markets", variables]);
2155
+ const cached = morphoDiscoveryCacheGet(cacheKey);
2156
+ if (cached) {
2157
+ return cached;
2158
+ }
2051
2159
  const data = await this.#morphoGraphqlRequest({
2052
2160
  query: MORPHO_MARKET_LIST_QUERY,
2053
- variables: {
2054
- first,
2055
- where,
2056
- orderBy: resolvedOrderBy,
2057
- orderDirection: resolvedOrderDirection,
2058
- },
2161
+ variables,
2059
2162
  operationName: "MorphoMarketList",
2060
2163
  });
2061
2164
  const markets = Array.isArray(data?.markets?.items) ? data.markets.items : [];
2062
- return {
2165
+ const result = {
2063
2166
  network: runtimeConfig.network,
2064
2167
  chainId: runtimeConfig.chainId,
2065
2168
  protocol: "morpho",
@@ -2070,10 +2173,14 @@ export class WdkEvmWalletService {
2070
2173
  search: searchTerm,
2071
2174
  collateralAssetFilter: collateralFilter,
2072
2175
  loanAssetFilter: loanFilter,
2176
+ minSupplyAssetsUsd: minSupplyAssetsUsdFilter,
2177
+ minNetSupplyApy: minNetSupplyApyFilter,
2073
2178
  marketCount: markets.length,
2074
2179
  markets,
2075
2180
  source: "morpho-api",
2076
2181
  };
2182
+ morphoDiscoveryCacheSet(cacheKey, result);
2183
+ return result;
2077
2184
  }
2078
2185
 
2079
2186
  async getMorphoPositions({ seedPhrase, address, accountIndex = 0, network }) {