@agentlayer.tech/wallet 0.1.26 → 0.1.28

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.
@@ -956,7 +956,7 @@ const walletSessionToolDefinitions = [
956
956
  {
957
957
  name: "x402_pay_request",
958
958
  description:
959
- `Prepare or execute an x402 paid request using the active wallet backend. This milestone executes the Solana exact buyer flow and keeps EVM as prepare-only. ${OPENCLAW_EXECUTE_APPROVAL_GUIDANCE}`,
959
+ "Pay for and call an x402 endpoint using the active wallet backend. The tool probes the endpoint, validates compatibility, signs the payment, and returns the service response in one call.",
960
960
  parameters: {
961
961
  type: "object",
962
962
  properties: {
@@ -966,18 +966,9 @@ const walletSessionToolDefinitions = [
966
966
  query: { type: "object", additionalProperties: true },
967
967
  json_body: {},
968
968
  text_body: { type: "string" },
969
- mode: {
970
- type: "string",
971
- enum: ["prepare", "execute"],
972
- description: "prepare validates the payment plan; execute sends the paid retry.",
973
- },
974
969
  purpose: { type: "string" },
975
- user_intent: {
976
- type: "boolean",
977
- description: "Must be true for prepare mode.",
978
- },
979
970
  },
980
- required: ["url", "mode", "purpose"],
971
+ required: ["url", "purpose"],
981
972
  additionalProperties: false,
982
973
  },
983
974
  },
@@ -1535,6 +1526,7 @@ const solanaToolDefinitions = [
1535
1526
  market: { type: "string" },
1536
1527
  reserve: { type: "string" },
1537
1528
  amount_ui: { type: "string" },
1529
+ obligation_address: { type: "string" },
1538
1530
  mode: { type: "string", enum: ["preview", "prepare", "execute"] },
1539
1531
  purpose: { type: "string" },
1540
1532
  user_intent: { type: "boolean" },
@@ -1553,6 +1545,7 @@ const solanaToolDefinitions = [
1553
1545
  market: { type: "string" },
1554
1546
  reserve: { type: "string" },
1555
1547
  amount_ui: { type: "string" },
1548
+ obligation_address: { type: "string" },
1556
1549
  mode: { type: "string", enum: ["preview", "prepare", "execute"] },
1557
1550
  purpose: { type: "string" },
1558
1551
  user_intent: { type: "boolean" },
@@ -1571,6 +1564,7 @@ const solanaToolDefinitions = [
1571
1564
  market: { type: "string" },
1572
1565
  reserve: { type: "string" },
1573
1566
  amount_ui: { type: "string" },
1567
+ obligation_address: { type: "string" },
1574
1568
  mode: { type: "string", enum: ["preview", "prepare", "execute"] },
1575
1569
  purpose: { type: "string" },
1576
1570
  user_intent: { type: "boolean" },
@@ -956,7 +956,7 @@ const walletSessionToolDefinitions = [
956
956
  {
957
957
  name: "x402_pay_request",
958
958
  description:
959
- `Prepare or execute an x402 paid request using the active wallet backend. This milestone executes the Solana exact buyer flow and keeps EVM as prepare-only. ${OPENCLAW_EXECUTE_APPROVAL_GUIDANCE}`,
959
+ "Pay for and call an x402 endpoint using the active wallet backend. The tool probes the endpoint, validates compatibility, signs the payment, and returns the service response in one call.",
960
960
  parameters: {
961
961
  type: "object",
962
962
  properties: {
@@ -966,18 +966,9 @@ const walletSessionToolDefinitions = [
966
966
  query: { type: "object", additionalProperties: true },
967
967
  json_body: {},
968
968
  text_body: { type: "string" },
969
- mode: {
970
- type: "string",
971
- enum: ["prepare", "execute"],
972
- description: "prepare validates the payment plan; execute sends the paid retry.",
973
- },
974
969
  purpose: { type: "string" },
975
- user_intent: {
976
- type: "boolean",
977
- description: "Must be true for prepare mode.",
978
- },
979
970
  },
980
- required: ["url", "mode", "purpose"],
971
+ required: ["url", "purpose"],
981
972
  additionalProperties: false,
982
973
  },
983
974
  },
@@ -1535,6 +1526,7 @@ const solanaToolDefinitions = [
1535
1526
  market: { type: "string" },
1536
1527
  reserve: { type: "string" },
1537
1528
  amount_ui: { type: "string" },
1529
+ obligation_address: { type: "string" },
1538
1530
  mode: { type: "string", enum: ["preview", "prepare", "execute"] },
1539
1531
  purpose: { type: "string" },
1540
1532
  user_intent: { type: "boolean" },
@@ -1553,6 +1545,7 @@ const solanaToolDefinitions = [
1553
1545
  market: { type: "string" },
1554
1546
  reserve: { type: "string" },
1555
1547
  amount_ui: { type: "string" },
1548
+ obligation_address: { type: "string" },
1556
1549
  mode: { type: "string", enum: ["preview", "prepare", "execute"] },
1557
1550
  purpose: { type: "string" },
1558
1551
  user_intent: { type: "boolean" },
@@ -1571,6 +1564,7 @@ const solanaToolDefinitions = [
1571
1564
  market: { type: "string" },
1572
1565
  reserve: { type: "string" },
1573
1566
  amount_ui: { type: "string" },
1567
+ obligation_address: { type: "string" },
1574
1568
  mode: { type: "string", enum: ["preview", "prepare", "execute"] },
1575
1569
  purpose: { type: "string" },
1576
1570
  user_intent: { type: "boolean" },
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agentlayertech/agent-wallet-plugin",
3
- "version": "0.1.24",
3
+ "version": "0.1.28",
4
4
  "description": "OpenClaw plugin bridge for the AgentLayer wallet runtime.",
5
5
  "type": "module",
6
6
  "license": "SEE LICENSE IN ../../../LICENSE",
@@ -13,6 +13,7 @@ Safety rules:
13
13
  - For transfers, native staking, swaps, Aave writes, Jupiter Earn writes, and Kamino writes, use `preview` before `prepare` or `execute`.
14
14
  - For Solana Jupiter swaps through `swap_solana_tokens`, prefer `intent_preview` then `intent_execute` after explicit chat confirmation. The user confirms risk limits; the backend refreshes the quote and only executes inside those limits.
15
15
  - Solana swap intent defaults to at least 300 bps (3%) slippage, 120 seconds validity, and 3 fresh execution attempts. The backend computes the approved minimum output from the indicative output and slippage, clamps hand-tightened minimums to that floor, then executes through Jupiter Swap API V2 `/order` + `/execute` when available.
16
+ - Metis `/swap` fallback builds use Jupiter dynamic slippage and a bounded `veryHigh` priority fee instead of the old `"auto"` priority mode.
16
17
  - Do not use legacy `execute` for Solana Jupiter swaps in OpenClaw. Exact quote-bound approval is too fragile for active markets and will be rejected by the bridge.
17
18
  - For `swap_solana_privately`, use `preview` and then `execute` after explicit user approval. Do not use `prepare` for this tool.
18
19
  - Use `prepare` only when the user clearly intends to produce an execution plan.
package/CHANGELOG.md CHANGED
@@ -2,6 +2,32 @@
2
2
 
3
3
  ## Unreleased
4
4
 
5
+ ## v0.1.28 - 2026-05-28
6
+
7
+ - Simplified `x402_pay_request` into a single-shot paid execution flow while
8
+ keeping `x402_preview_request` as an optional research tool.
9
+ - Hardened x402 execution with explicit payment requirement validation, longer
10
+ paid-request timeouts, structured settlement logging, and safer settlement
11
+ header parsing.
12
+ - Fixed Base x402 EVM signing by normalizing typed-data byte fields before
13
+ sending them through the local WDK signer bridge.
14
+ - Reduced x402 preview confusion in OpenClaw by exposing a payment summary
15
+ without approval-token style confirmation semantics.
16
+ - Added an early safety guard for `x402.alchemy.com` so unsupported wallet-auth
17
+ flows fail before spending funds.
18
+
19
+ ## v0.1.27 - 2026-05-27
20
+
21
+ - Improved Solana swap fallback landing by enabling Jupiter dynamic slippage
22
+ and bounded `veryHigh` priority fees on the Metis `/swap` fallback path.
23
+ - Hardened Kamino transaction execution with local simulation before send,
24
+ Kamino-specific build timeouts, and longer confirmation polling on mainnet.
25
+ - Reused approved Kamino preview payloads during execute so OpenClaw no longer
26
+ needs to rebuild the same write path just to satisfy approval binding.
27
+ - Added Kamino obligation pinning for `withdraw`, `borrow`, and `repay`, so
28
+ preview can require an explicit `obligation_address` and execute verifies the
29
+ built transaction references the selected obligation before signing.
30
+
5
31
  ## v0.1.26 - 2026-05-26
6
32
 
7
33
  - Reworked Solana Jupiter swaps to prefer intent approvals, so OpenClaw confirms
@@ -245,8 +245,9 @@ class OpenClawWalletAdapter:
245
245
  AgentToolSpec(
246
246
  name="x402_pay_request",
247
247
  description=(
248
- "Prepare or execute an x402 paid request using the active wallet backend. "
249
- "This milestone executes the Solana exact buyer flow and keeps EVM as prepare-only."
248
+ "Pay for and call an x402 endpoint using the active wallet backend. "
249
+ "The tool probes the endpoint, validates compatibility, signs the payment, "
250
+ "and returns the service response in one call."
250
251
  ),
251
252
  input_schema={
252
253
  "type": "object",
@@ -257,26 +258,13 @@ class OpenClawWalletAdapter:
257
258
  "query": {"type": "object", "additionalProperties": True},
258
259
  "json_body": {},
259
260
  "text_body": {"type": "string"},
260
- "mode": {
261
- "type": "string",
262
- "enum": ["prepare", "execute"],
263
- "description": "prepare validates the payment plan; execute sends the paid retry.",
264
- },
265
261
  "purpose": {"type": "string"},
266
- "user_intent": {
267
- "type": "boolean",
268
- "description": "Must be true for prepare mode.",
269
- },
270
- "approval_token": {
271
- "type": "string",
272
- "description": "Required for execute mode and must be issued against the exact x402 payment summary.",
273
- },
274
262
  },
275
- "required": ["url", "mode", "purpose"],
263
+ "required": ["url", "purpose"],
276
264
  "additionalProperties": False,
277
265
  },
278
266
  read_only=False,
279
- requires_explicit_user_intent=True,
267
+ requires_explicit_user_intent=False,
280
268
  risk_level="high",
281
269
  ),
282
270
  ]
@@ -760,6 +748,7 @@ class OpenClawWalletAdapter:
760
748
  "owner",
761
749
  "authority",
762
750
  "address",
751
+ "obligation_address",
763
752
  "market",
764
753
  "reserve",
765
754
  "amount_native",
@@ -871,6 +860,42 @@ class OpenClawWalletAdapter:
871
860
  )
872
861
  return annotated
873
862
 
863
+ def _annotate_x402_payload(
864
+ self,
865
+ payload: dict[str, Any],
866
+ *,
867
+ mode: str,
868
+ ) -> dict[str, Any]:
869
+ if not payload.get("payment_required"):
870
+ return dict(payload)
871
+ annotated = self._annotate_sensitive_payload(
872
+ payload,
873
+ action_label="x402 paid request",
874
+ mode=mode,
875
+ )
876
+ summary = dict(annotated.get("confirmation_summary") or {})
877
+ if summary:
878
+ annotated["payment_summary"] = summary
879
+ requirements = dict(annotated.get("confirmation_requirements") or {})
880
+ requirements["prepare_requires_user_intent"] = False
881
+ requirements["execute_requires_approval_token"] = False
882
+ requirements["execute_requires_mainnet_confirmed_in_token"] = False
883
+ annotated.pop("approval_hint", None)
884
+ if mode == "preview":
885
+ annotated.pop("confirmation_summary", None)
886
+ annotated.pop("confirmation_requirements", None)
887
+ if annotated.get("is_mainnet"):
888
+ annotated["preview_note"] = (
889
+ "This is a paid mainnet endpoint preview only. Review the service URL, network, asset, amount, and payment destination before calling x402_pay_request."
890
+ )
891
+ return annotated
892
+ annotated["confirmation_requirements"] = requirements
893
+ if annotated.get("is_mainnet"):
894
+ annotated["mainnet_warning"] = (
895
+ "Mainnet x402 payment. Confirm the service URL, network, asset, amount, and payment destination before paying."
896
+ )
897
+ return annotated
898
+
874
899
  def list_tools(self) -> list[AgentToolSpec]:
875
900
  """Return wallet tools suitable for agent registration."""
876
901
  capabilities = self.backend.get_capabilities()
@@ -2969,6 +2994,10 @@ class OpenClawWalletAdapter:
2969
2994
  "type": "string",
2970
2995
  "description": "Decimal token amount to withdraw, as a string.",
2971
2996
  },
2997
+ "obligation_address": {
2998
+ "type": "string",
2999
+ "description": "Optional Kamino obligation address. Required when preview shows multiple matching obligations.",
3000
+ },
2972
3001
  "mode": {
2973
3002
  "type": "string",
2974
3003
  "enum": ["preview", "prepare", "execute"],
@@ -3003,6 +3032,10 @@ class OpenClawWalletAdapter:
3003
3032
  "type": "string",
3004
3033
  "description": "Decimal token amount to borrow, as a string.",
3005
3034
  },
3035
+ "obligation_address": {
3036
+ "type": "string",
3037
+ "description": "Optional Kamino obligation address. Required when preview shows multiple obligations in the selected market.",
3038
+ },
3006
3039
  "mode": {
3007
3040
  "type": "string",
3008
3041
  "enum": ["preview", "prepare", "execute"],
@@ -3037,6 +3070,10 @@ class OpenClawWalletAdapter:
3037
3070
  "type": "string",
3038
3071
  "description": "Decimal token amount to repay, as a string.",
3039
3072
  },
3073
+ "obligation_address": {
3074
+ "type": "string",
3075
+ "description": "Optional Kamino obligation address. Required when preview shows multiple matching debt obligations.",
3076
+ },
3040
3077
  "mode": {
3041
3078
  "type": "string",
3042
3079
  "enum": ["preview", "prepare", "execute"],
@@ -3296,14 +3333,7 @@ class OpenClawWalletAdapter:
3296
3333
  text_body=text_body,
3297
3334
  )
3298
3335
  if data.get("payment_required"):
3299
- data = self._annotate_sensitive_payload(
3300
- data,
3301
- action_label="x402 paid request",
3302
- mode="preview",
3303
- )
3304
- approval_hint = dict(data.get("approval_hint") or {})
3305
- approval_hint["tool_name"] = "x402_pay_request"
3306
- data["approval_hint"] = approval_hint
3336
+ data = self._annotate_x402_payload(data, mode="preview")
3307
3337
  return AgentToolResult(tool=tool_name, ok=True, data=data)
3308
3338
 
3309
3339
  if tool_name == "x402_pay_request":
@@ -3313,10 +3343,7 @@ class OpenClawWalletAdapter:
3313
3343
  query = args.get("query")
3314
3344
  json_body = args.get("json_body")
3315
3345
  text_body = args.get("text_body")
3316
- mode = str(args.get("mode") or "").strip().lower()
3317
3346
  purpose = args.get("purpose")
3318
- user_intent = args.get("user_intent")
3319
- approval_token = args.get("approval_token")
3320
3347
  if not isinstance(url, str) or not url.strip():
3321
3348
  raise WalletBackendError("url is required.")
3322
3349
  if method is not None and not isinstance(method, str):
@@ -3327,51 +3354,9 @@ class OpenClawWalletAdapter:
3327
3354
  raise WalletBackendError("query must be an object when provided.")
3328
3355
  if text_body is not None and not isinstance(text_body, str):
3329
3356
  raise WalletBackendError("text_body must be a string when provided.")
3330
- if mode not in {"prepare", "execute"}:
3331
- raise WalletBackendError("mode must be 'prepare' or 'execute'.")
3332
3357
  if not isinstance(purpose, str) or not purpose.strip():
3333
3358
  raise WalletBackendError("purpose is required.")
3334
- if mode == "prepare":
3335
- self._require_prepare_intent(user_intent)
3336
- data = await x402.prepare_request(
3337
- backend=active_backend,
3338
- url=url.strip(),
3339
- method=method,
3340
- headers=headers,
3341
- query=query,
3342
- json_body=json_body,
3343
- text_body=text_body,
3344
- )
3345
- data["purpose"] = purpose.strip()
3346
- data = self._annotate_sensitive_payload(
3347
- data,
3348
- action_label="x402 paid request",
3349
- mode="prepare",
3350
- )
3351
- return AgentToolResult(tool=tool_name, ok=True, data=data)
3352
- preview = await x402.prepare_request(
3353
- backend=active_backend,
3354
- url=url.strip(),
3355
- method=method,
3356
- headers=headers,
3357
- query=query,
3358
- json_body=json_body,
3359
- text_body=text_body,
3360
- )
3361
- preview["purpose"] = purpose.strip()
3362
- preview = self._annotate_sensitive_payload(
3363
- preview,
3364
- action_label="x402 paid request",
3365
- mode="execute",
3366
- )
3367
- self._require_execute_approval(
3368
- approval_token=approval_token,
3369
- tool_name=tool_name,
3370
- summary=preview["confirmation_summary"],
3371
- action_label="x402 paid request",
3372
- backend=active_backend,
3373
- )
3374
- data = await x402.execute_request(
3359
+ data = await x402.pay_and_fetch(
3375
3360
  backend=active_backend,
3376
3361
  url=url.strip(),
3377
3362
  method=method,
@@ -3381,11 +3366,7 @@ class OpenClawWalletAdapter:
3381
3366
  text_body=text_body,
3382
3367
  )
3383
3368
  data["purpose"] = purpose.strip()
3384
- data = self._annotate_sensitive_payload(
3385
- data,
3386
- action_label="x402 paid request",
3387
- mode="execute",
3388
- )
3369
+ data = self._annotate_x402_payload(data, mode="execute")
3389
3370
  return AgentToolResult(tool=tool_name, ok=True, data=data)
3390
3371
 
3391
3372
  if tool_name == "get_wallet_capabilities":
@@ -5003,6 +4984,7 @@ class OpenClawWalletAdapter:
5003
4984
  market = args.get("market")
5004
4985
  reserve = args.get("reserve")
5005
4986
  amount_ui = args.get("amount_ui")
4987
+ obligation_address = args.get("obligation_address")
5006
4988
  mode = args.get("mode")
5007
4989
  purpose = args.get("purpose")
5008
4990
  user_intent = args.get("user_intent", False)
@@ -5014,6 +4996,8 @@ class OpenClawWalletAdapter:
5014
4996
  raise WalletBackendError("reserve is required.")
5015
4997
  if not isinstance(amount_ui, str) or not amount_ui.strip():
5016
4998
  raise WalletBackendError("amount_ui is required.")
4999
+ if obligation_address is not None and not isinstance(obligation_address, str):
5000
+ raise WalletBackendError("obligation_address must be a string when provided.")
5017
5001
  if mode not in {"preview", "prepare", "execute"}:
5018
5002
  raise WalletBackendError("mode must be 'preview', 'prepare' or 'execute'.")
5019
5003
  if not isinstance(purpose, str) or not purpose.strip():
@@ -5046,6 +5030,7 @@ class OpenClawWalletAdapter:
5046
5030
  market=market.strip(),
5047
5031
  reserve=reserve.strip(),
5048
5032
  amount_ui=amount_ui.strip(),
5033
+ obligation_address=obligation_address.strip() if isinstance(obligation_address, str) and obligation_address.strip() else None,
5049
5034
  )
5050
5035
  return AgentToolResult(
5051
5036
  tool=tool_name,
@@ -5063,7 +5048,12 @@ class OpenClawWalletAdapter:
5063
5048
  market=market.strip(),
5064
5049
  reserve=reserve.strip(),
5065
5050
  amount_ui=amount_ui.strip(),
5051
+ obligation_address=obligation_address.strip() if isinstance(obligation_address, str) and obligation_address.strip() else None,
5066
5052
  )
5053
+ if bool(preview.get("requires_obligation_address")):
5054
+ raise WalletBackendError(
5055
+ f"{action_label} requires obligation_address when multiple Kamino obligations match the selected position."
5056
+ )
5067
5057
  return AgentToolResult(
5068
5058
  tool=tool_name,
5069
5059
  ok=True,
@@ -5077,24 +5067,49 @@ class OpenClawWalletAdapter:
5077
5067
  ),
5078
5068
  )
5079
5069
 
5080
- execute_preview = await preview_method(
5081
- market=market.strip(),
5082
- reserve=reserve.strip(),
5083
- amount_ui=amount_ui.strip(),
5070
+ approved_preview = args.get("_approved_preview")
5071
+ execute_preview = None
5072
+ approval_payload = inspect_approval_token(
5073
+ approval_token,
5074
+ tool_name=tool_name,
5075
+ network=str(getattr(self.backend, "network", "unknown")),
5076
+ require_mainnet_confirmation=self._is_mainnet_for_backend(self.backend),
5084
5077
  )
5078
+ approval_summary = approval_payload.get("binding", {}).get("summary")
5079
+ if not isinstance(approval_summary, dict):
5080
+ raise WalletBackendError(
5081
+ "approval_token does not match the requested operation. Generate a new approval after previewing the exact action."
5082
+ )
5083
+ approval_summary_copy = dict(approval_summary)
5084
+ if isinstance(approval_summary_copy.get("_preview_digest"), str):
5085
+ if not isinstance(approved_preview, dict):
5086
+ raise WalletBackendError(
5087
+ f"Approved {action_label} preview payload is required for execute mode. Generate a new preview and approval before execute."
5088
+ )
5089
+ if preview_payload_digest(approved_preview) != approval_summary_copy["_preview_digest"]:
5090
+ raise WalletBackendError(
5091
+ "approved preview payload does not match the approval token. Generate a new preview and approval before execute."
5092
+ )
5093
+ execute_preview = dict(approved_preview)
5094
+ else:
5095
+ execute_preview = await preview_method(
5096
+ market=market.strip(),
5097
+ reserve=reserve.strip(),
5098
+ amount_ui=amount_ui.strip(),
5099
+ obligation_address=obligation_address.strip() if isinstance(obligation_address, str) and obligation_address.strip() else None,
5100
+ )
5085
5101
  self._require_execute_approval(
5086
5102
  approval_token=approval_token,
5087
5103
  tool_name=tool_name,
5088
- summary=self._build_confirmation_summary(
5089
- action_label=action_label,
5090
- payload=execute_preview,
5091
- ),
5104
+ summary=approval_summary_copy,
5092
5105
  action_label=action_label,
5093
5106
  )
5094
5107
  result = await execute_method(
5095
5108
  market=market.strip(),
5096
5109
  reserve=reserve.strip(),
5097
5110
  amount_ui=amount_ui.strip(),
5111
+ obligation_address=obligation_address.strip() if isinstance(obligation_address, str) and obligation_address.strip() else None,
5112
+ approved_preview=execute_preview,
5098
5113
  )
5099
5114
  return AgentToolResult(
5100
5115
  tool=tool_name,
@@ -10,6 +10,8 @@ from agent_wallet.config import settings
10
10
  from agent_wallet.exceptions import ProviderError
11
11
  from agent_wallet.http_client import get_client
12
12
 
13
+ JUPITER_SWAP_FALLBACK_PRIORITY_MAX_LAMPORTS = 2_000_000
14
+
13
15
 
14
16
  def _headers() -> dict[str, str]:
15
17
  headers = {"Accept": "application/json"}
@@ -91,6 +93,32 @@ def _direct_jupiter_enabled() -> bool:
91
93
  return bool(settings.jupiter_api_key.strip())
92
94
 
93
95
 
96
+ def _swap_fallback_prioritization_fee() -> dict[str, Any]:
97
+ return {
98
+ "priorityLevelWithMaxLamports": {
99
+ "priorityLevel": "veryHigh",
100
+ "maxLamports": JUPITER_SWAP_FALLBACK_PRIORITY_MAX_LAMPORTS,
101
+ "global": False,
102
+ }
103
+ }
104
+
105
+
106
+ def _swap_fallback_build_body(
107
+ *,
108
+ user_public_key: str,
109
+ quote_response: dict[str, Any],
110
+ wrap_and_unwrap_sol: bool,
111
+ ) -> dict[str, Any]:
112
+ return {
113
+ "userPublicKey": user_public_key,
114
+ "quoteResponse": quote_response,
115
+ "wrapAndUnwrapSol": wrap_and_unwrap_sol,
116
+ "dynamicComputeUnitLimit": True,
117
+ "dynamicSlippage": True,
118
+ "prioritizationFeeLamports": _swap_fallback_prioritization_fee(),
119
+ }
120
+
121
+
94
122
  def _swap_v2_base_url() -> str:
95
123
  return os.getenv(
96
124
  "JUPITER_SWAP_V2_API_BASE_URL",
@@ -456,13 +484,11 @@ async def _build_swap_direct(
456
484
  ) -> dict[str, Any]:
457
485
  """Build a swap transaction directly via Jupiter API."""
458
486
  client = get_client()
459
- body = {
460
- "userPublicKey": user_public_key,
461
- "quoteResponse": quote_response,
462
- "wrapAndUnwrapSol": wrap_and_unwrap_sol,
463
- "dynamicComputeUnitLimit": True,
464
- "prioritizationFeeLamports": "auto",
465
- }
487
+ body = _swap_fallback_build_body(
488
+ user_public_key=user_public_key,
489
+ quote_response=quote_response,
490
+ wrap_and_unwrap_sol=wrap_and_unwrap_sol,
491
+ )
466
492
  response = await client.post(
467
493
  f"{settings.jupiter_api_base_url.rstrip('/')}/swap",
468
494
  json=body,
@@ -483,13 +509,11 @@ async def _build_swap_via_gateway(
483
509
  wrap_and_unwrap_sol: bool = True,
484
510
  ) -> dict[str, Any]:
485
511
  """Build a swap transaction via provider gateway (uses API key)."""
486
- body = {
487
- "userPublicKey": user_public_key,
488
- "quoteResponse": quote_response,
489
- "wrapAndUnwrapSol": wrap_and_unwrap_sol,
490
- "dynamicComputeUnitLimit": True,
491
- "prioritizationFeeLamports": "auto",
492
- }
512
+ body = _swap_fallback_build_body(
513
+ user_public_key=user_public_key,
514
+ quote_response=quote_response,
515
+ wrap_and_unwrap_sol=wrap_and_unwrap_sol,
516
+ )
493
517
  status_code, payload = await _gateway_post("swap", body=body)
494
518
  if status_code != 200:
495
519
  error_msg = payload if isinstance(payload, str) else json.dumps(payload)
@@ -8,6 +8,8 @@ from agent_wallet.config import settings
8
8
  from agent_wallet.exceptions import ProviderError
9
9
  from agent_wallet.http_client import get_client
10
10
 
11
+ KAMINO_BUILD_TIMEOUT_SECONDS = 20.0
12
+
11
13
 
12
14
  def _normalized_api_base() -> str:
13
15
  return settings.kamino_api_base_url.rstrip("/")
@@ -140,6 +142,7 @@ async def build_lend_deposit_transaction(
140
142
  "reserve": reserve,
141
143
  "amount": amount_ui,
142
144
  },
145
+ timeout=KAMINO_BUILD_TIMEOUT_SECONDS,
143
146
  )
144
147
  if response.status_code != 200:
145
148
  raise ProviderError("kamino", f"HTTP {response.status_code}: {response.text[:300]}")
@@ -163,6 +166,7 @@ async def build_lend_withdraw_transaction(
163
166
  "reserve": reserve,
164
167
  "amount": amount_ui,
165
168
  },
169
+ timeout=KAMINO_BUILD_TIMEOUT_SECONDS,
166
170
  )
167
171
  if response.status_code != 200:
168
172
  raise ProviderError("kamino", f"HTTP {response.status_code}: {response.text[:300]}")
@@ -186,6 +190,7 @@ async def build_lend_borrow_transaction(
186
190
  "reserve": reserve,
187
191
  "amount": amount_ui,
188
192
  },
193
+ timeout=KAMINO_BUILD_TIMEOUT_SECONDS,
189
194
  )
190
195
  if response.status_code != 200:
191
196
  raise ProviderError("kamino", f"HTTP {response.status_code}: {response.text[:300]}")
@@ -209,6 +214,7 @@ async def build_lend_repay_transaction(
209
214
  "reserve": reserve,
210
215
  "amount": amount_ui,
211
216
  },
217
+ timeout=KAMINO_BUILD_TIMEOUT_SECONDS,
212
218
  )
213
219
  if response.status_code != 200:
214
220
  raise ProviderError("kamino", f"HTTP {response.status_code}: {response.text[:300]}")