@agentlayer.tech/wallet 0.1.88 → 0.1.90
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.openclaw/extensions/agent-wallet/dist/index.js +5 -5
- package/.openclaw/extensions/agent-wallet/index.ts +5 -5
- package/.openclaw/extensions/agent-wallet/openclaw.plugin.json +2 -1
- package/.openclaw/extensions/agent-wallet/package.json +1 -1
- package/VERSION +1 -1
- package/agent-wallet/agent_wallet/__init__.py +1 -1
- package/agent-wallet/agent_wallet/autonomous_permissions.py +43 -1
- package/agent-wallet/agent_wallet/openclaw_adapter.py +696 -375
- package/agent-wallet/agent_wallet/providers/dexscreener.py +145 -0
- package/agent-wallet/agent_wallet/providers/x402.py +84 -0
- package/agent-wallet/agent_wallet/wallet_layer/base.py +12 -0
- package/agent-wallet/agent_wallet/wallet_layer/wdk_evm.py +38 -1
- package/agent-wallet/openclaw.plugin.json +1 -1
- package/agent-wallet/pyproject.toml +1 -1
- package/claude-code/plugins/agent-wallet/.claude-plugin/plugin.json +1 -1
- package/claude-code/plugins/agent-wallet/README.md +3 -1
- package/claude-code/plugins/agent-wallet/commands/agentlayer-autonomous-approve.md +7 -8
- package/claude-code/plugins/agent-wallet/commands/agentlayer-autonomous-revoke.md +2 -2
- package/claude-code/plugins/agent-wallet/commands/wallet-base.md +38 -15
- package/codex/plugins/agent-wallet/.codex-plugin/plugin.json +1 -1
- package/codex/plugins/agent-wallet/README.md +11 -6
- package/codex/plugins/agent-wallet/skills/wallet-base/SKILL.md +56 -0
- package/hermes/plugins/agent_wallet/plugin.yaml +1 -1
- package/package.json +1 -1
- package/wdk-btc-wallet/package.json +1 -1
- package/wdk-evm-wallet/package.json +1 -1
|
@@ -281,6 +281,10 @@ class OpenClawWalletAdapter:
|
|
|
281
281
|
"json_body": {},
|
|
282
282
|
"text_body": {"type": "string"},
|
|
283
283
|
"purpose": {"type": "string"},
|
|
284
|
+
"approval_token": {
|
|
285
|
+
"type": "string",
|
|
286
|
+
"description": "Host-issued approval token required for execute mode when the payment is mainnet/real-value and no autonomous session or permission grant covers it.",
|
|
287
|
+
},
|
|
284
288
|
},
|
|
285
289
|
"required": ["url", "purpose"],
|
|
286
290
|
"additionalProperties": False,
|
|
@@ -493,10 +497,25 @@ class OpenClawWalletAdapter:
|
|
|
493
497
|
)
|
|
494
498
|
)
|
|
495
499
|
else:
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
+
# No budgeted session either: fall back to the standing
|
|
501
|
+
# high-trust permission toggle (agentlayer_autonomous_approve).
|
|
502
|
+
# This is the single choke point every execute tool funnels
|
|
503
|
+
# through, so enabling that toggle covers every tool here --
|
|
504
|
+
# not just the Base-swap/EVM-DeFi tools that also have their
|
|
505
|
+
# own dedicated pre-authorization step before reaching here.
|
|
506
|
+
from agent_wallet import autonomous_permissions
|
|
507
|
+
|
|
508
|
+
if autonomous_permissions.is_autonomous_approved():
|
|
509
|
+
approval_token = autonomous_permissions.authorize_operation(
|
|
510
|
+
tool_name=tool_name,
|
|
511
|
+
network=network,
|
|
512
|
+
summary=summary,
|
|
513
|
+
)
|
|
514
|
+
else:
|
|
515
|
+
raise WalletBackendError(
|
|
516
|
+
f"{action_label} execution requires a host-issued approval_token "
|
|
517
|
+
"(or an active autonomous session/permission grant authorized by the host)."
|
|
518
|
+
)
|
|
500
519
|
verify_approval_token(
|
|
501
520
|
approval_token.strip(),
|
|
502
521
|
tool_name=tool_name,
|
|
@@ -1147,19 +1166,29 @@ class OpenClawWalletAdapter:
|
|
|
1147
1166
|
annotated["payment_summary"] = summary
|
|
1148
1167
|
requirements = dict(annotated.get("confirmation_requirements") or {})
|
|
1149
1168
|
requirements["prepare_requires_user_intent"] = False
|
|
1150
|
-
|
|
1151
|
-
|
|
1169
|
+
de_minimis = x402.is_de_minimis_payment(payload)
|
|
1170
|
+
if de_minimis:
|
|
1171
|
+
# Below x402.DE_MINIMIS_USD_THRESHOLD, this payment never needs
|
|
1172
|
+
# approval -- not even on mainnet -- so keep the advertised
|
|
1173
|
+
# requirement honest in both preview and execute responses.
|
|
1174
|
+
requirements["execute_requires_approval_token"] = False
|
|
1175
|
+
requirements["execute_requires_mainnet_confirmed_in_token"] = False
|
|
1176
|
+
annotated["de_minimis_payment"] = {
|
|
1177
|
+
"applied": True,
|
|
1178
|
+
"threshold_usd": x402.DE_MINIMIS_USD_THRESHOLD,
|
|
1179
|
+
"amount_usd": x402.de_minimis_usd_amount(payload),
|
|
1180
|
+
}
|
|
1152
1181
|
annotated.pop("approval_hint", None)
|
|
1153
1182
|
if mode == "preview":
|
|
1154
1183
|
annotated.pop("confirmation_summary", None)
|
|
1155
|
-
annotated
|
|
1156
|
-
if annotated.get("is_mainnet"):
|
|
1184
|
+
annotated["confirmation_requirements"] = requirements
|
|
1185
|
+
if annotated.get("is_mainnet") and not de_minimis:
|
|
1157
1186
|
annotated["preview_note"] = (
|
|
1158
1187
|
"This is a paid mainnet endpoint preview only. Review the service URL, network, asset, amount, and payment destination before calling x402_pay_request."
|
|
1159
1188
|
)
|
|
1160
1189
|
return annotated
|
|
1161
1190
|
annotated["confirmation_requirements"] = requirements
|
|
1162
|
-
if annotated.get("is_mainnet"):
|
|
1191
|
+
if annotated.get("is_mainnet") and not de_minimis:
|
|
1163
1192
|
annotated["mainnet_warning"] = (
|
|
1164
1193
|
"Mainnet x402 payment. Confirm the service URL, network, asset, amount, and payment destination before paying."
|
|
1165
1194
|
)
|
|
@@ -2211,6 +2240,67 @@ class OpenClawWalletAdapter:
|
|
|
2211
2240
|
risk_level="low",
|
|
2212
2241
|
),
|
|
2213
2242
|
)
|
|
2243
|
+
tools.append(
|
|
2244
|
+
AgentToolSpec(
|
|
2245
|
+
name="search_uniswap_pairs",
|
|
2246
|
+
description=(
|
|
2247
|
+
"Search for onchain Uniswap-tradeable token pairs by ticker/name or by exact "
|
|
2248
|
+
"ERC-20 address, with live price, 5m/1h/6h/24h volume, liquidity, FDV, market "
|
|
2249
|
+
"cap, and buy/sell counts. Backed by DexScreener's public index (covers "
|
|
2250
|
+
"Uniswap v2/v3/v4 pools and other DEXes on the same chain), since the Uniswap "
|
|
2251
|
+
"Trading API itself only quotes a pair you already know the addresses for, not "
|
|
2252
|
+
"search-by-name. Defaults to the currently active EVM network (e.g. robinhood); "
|
|
2253
|
+
"set all_chains to search across every chain DexScreener indexes. Read-only "
|
|
2254
|
+
"market data, not a swap quote. Free-text search can surface impersonator "
|
|
2255
|
+
"tokens reusing a legitimate ticker with fabricated liquidity/FDV — before "
|
|
2256
|
+
"quoting or swapping, verify the token_address independently (e.g. via "
|
|
2257
|
+
"get_evm_token_metadata) rather than trusting the top result by symbol match. "
|
|
2258
|
+
"Do not use liquidity/FDV size to pick a winner: observed impersonators on "
|
|
2259
|
+
"robinhood fabricate liquidity/FDV an order of magnitude above the genuine "
|
|
2260
|
+
"pool specifically to look most trustworthy, and a Uniswap quote succeeding "
|
|
2261
|
+
"for a token proves nothing either (Trading API routes any pool with reserves, "
|
|
2262
|
+
"impersonators included). This only matters for tickers claiming to represent "
|
|
2263
|
+
"a real-world asset (stocks/ETFs) - unrelated meme/community tokens on the "
|
|
2264
|
+
"same chain aren't impersonators just for lacking a canonical issuer. When the "
|
|
2265
|
+
"query is a real-world-asset ticker, cross-check the returned token_address "
|
|
2266
|
+
"against Robinhood's own canonical list at docs.robinhood.com/chain/contracts "
|
|
2267
|
+
"(or the equivalent official source for other chains) before relying on the result."
|
|
2268
|
+
),
|
|
2269
|
+
input_schema={
|
|
2270
|
+
"type": "object",
|
|
2271
|
+
"properties": {
|
|
2272
|
+
"query": {
|
|
2273
|
+
"type": "string",
|
|
2274
|
+
"description": "Free-text search, e.g. a ticker or name (\"AAPL\", \"Apple Robinhood\"). Required unless token_address is given.",
|
|
2275
|
+
},
|
|
2276
|
+
"token_address": {
|
|
2277
|
+
"type": "string",
|
|
2278
|
+
"description": "Exact ERC-20 contract address to look up instead of a free-text query.",
|
|
2279
|
+
},
|
|
2280
|
+
"chain": {
|
|
2281
|
+
"type": "string",
|
|
2282
|
+
"description": "DexScreener chain slug to filter to (e.g. \"robinhood\", \"ethereum\", \"base\"). Defaults to the active EVM network.",
|
|
2283
|
+
},
|
|
2284
|
+
"dex_id": {
|
|
2285
|
+
"type": "string",
|
|
2286
|
+
"description": "Optional exact DEX filter, e.g. \"uniswap\".",
|
|
2287
|
+
},
|
|
2288
|
+
"all_chains": {
|
|
2289
|
+
"type": "boolean",
|
|
2290
|
+
"description": "If true, do not filter query results to a single chain. Ignored when token_address is set.",
|
|
2291
|
+
},
|
|
2292
|
+
"limit": {
|
|
2293
|
+
"type": "integer",
|
|
2294
|
+
"description": "Max number of pairs to return (1-30). Defaults to 10.",
|
|
2295
|
+
},
|
|
2296
|
+
},
|
|
2297
|
+
"required": [],
|
|
2298
|
+
"additionalProperties": False,
|
|
2299
|
+
},
|
|
2300
|
+
read_only=True,
|
|
2301
|
+
risk_level="low",
|
|
2302
|
+
),
|
|
2303
|
+
)
|
|
2214
2304
|
tools.insert(
|
|
2215
2305
|
12,
|
|
2216
2306
|
AgentToolSpec(
|
|
@@ -3806,7 +3896,9 @@ class OpenClawWalletAdapter:
|
|
|
3806
3896
|
name="agentlayer_autonomous_status",
|
|
3807
3897
|
description=(
|
|
3808
3898
|
"Return AgentLayer high-trust autonomous permission status. "
|
|
3809
|
-
"The autonomous permission group
|
|
3899
|
+
"The autonomous permission group covers every wallet write tool (transfers, "
|
|
3900
|
+
"bridges, Solana swaps, staking, x402 payments, generic contract calls, Base "
|
|
3901
|
+
"swaps, and EVM DeFi management), not just base_swaps/defi_tools."
|
|
3810
3902
|
),
|
|
3811
3903
|
input_schema={
|
|
3812
3904
|
"type": "object",
|
|
@@ -3822,17 +3914,18 @@ class OpenClawWalletAdapter:
|
|
|
3822
3914
|
name="agentlayer_autonomous_approve",
|
|
3823
3915
|
description=(
|
|
3824
3916
|
"Enable the high-trust autonomous permission group. The scope parameter is kept "
|
|
3825
|
-
"for compatibility; choosing base_swaps or defi_tools enables
|
|
3826
|
-
"
|
|
3827
|
-
"
|
|
3917
|
+
"for compatibility; choosing base_swaps or defi_tools enables the full group, "
|
|
3918
|
+
"which covers every wallet write tool -- transfers, bridges, Solana swaps, "
|
|
3919
|
+
"staking, x402 payments, generic contract calls, Base swaps, and supported EVM "
|
|
3920
|
+
"DeFi management tools -- until revoked."
|
|
3828
3921
|
),
|
|
3829
3922
|
input_schema={
|
|
3830
3923
|
"type": "object",
|
|
3831
3924
|
"properties": {
|
|
3832
3925
|
"scope": {
|
|
3833
3926
|
"type": "string",
|
|
3834
|
-
"enum": ["base_swaps", "defi_tools"],
|
|
3835
|
-
"description": "
|
|
3927
|
+
"enum": ["all", "base_swaps", "defi_tools"],
|
|
3928
|
+
"description": "Scope to enable. Use \"all\" (recommended) -- base_swaps and defi_tools are deprecated aliases that enable the exact same full autonomous permission group, covering every wallet write tool.",
|
|
3836
3929
|
},
|
|
3837
3930
|
"purpose": {
|
|
3838
3931
|
"type": "string",
|
|
@@ -3862,8 +3955,8 @@ class OpenClawWalletAdapter:
|
|
|
3862
3955
|
"properties": {
|
|
3863
3956
|
"scope": {
|
|
3864
3957
|
"type": "string",
|
|
3865
|
-
"enum": ["base_swaps", "defi_tools"],
|
|
3866
|
-
"description": "
|
|
3958
|
+
"enum": ["all", "base_swaps", "defi_tools"],
|
|
3959
|
+
"description": "Scope to revoke. Use \"all\" (recommended) -- base_swaps and defi_tools are deprecated aliases that revoke the exact same full autonomous permission group.",
|
|
3867
3960
|
}
|
|
3868
3961
|
},
|
|
3869
3962
|
"required": ["scope"],
|
|
@@ -3888,7 +3981,9 @@ class OpenClawWalletAdapter:
|
|
|
3888
3981
|
name="agentlayer_autonomous_status",
|
|
3889
3982
|
description=(
|
|
3890
3983
|
"Return AgentLayer high-trust autonomous permission status. "
|
|
3891
|
-
"The autonomous permission group
|
|
3984
|
+
"The autonomous permission group covers every wallet write tool (transfers, "
|
|
3985
|
+
"bridges, Solana swaps, staking, x402 payments, generic contract calls, Base "
|
|
3986
|
+
"swaps, and EVM DeFi management), not just base_swaps/defi_tools."
|
|
3892
3987
|
),
|
|
3893
3988
|
input_schema={
|
|
3894
3989
|
"type": "object",
|
|
@@ -3902,17 +3997,18 @@ class OpenClawWalletAdapter:
|
|
|
3902
3997
|
name="agentlayer_autonomous_approve",
|
|
3903
3998
|
description=(
|
|
3904
3999
|
"Enable the high-trust autonomous permission group. The scope parameter is kept "
|
|
3905
|
-
"for compatibility; choosing base_swaps or defi_tools enables
|
|
3906
|
-
"
|
|
3907
|
-
"
|
|
4000
|
+
"for compatibility; choosing base_swaps or defi_tools enables the full group, "
|
|
4001
|
+
"which covers every wallet write tool -- transfers, bridges, Solana swaps, "
|
|
4002
|
+
"staking, x402 payments, generic contract calls, Base swaps, and supported EVM "
|
|
4003
|
+
"DeFi management tools -- until revoked."
|
|
3908
4004
|
),
|
|
3909
4005
|
input_schema={
|
|
3910
4006
|
"type": "object",
|
|
3911
4007
|
"properties": {
|
|
3912
4008
|
"scope": {
|
|
3913
4009
|
"type": "string",
|
|
3914
|
-
"enum": ["base_swaps", "defi_tools"],
|
|
3915
|
-
"description": "
|
|
4010
|
+
"enum": ["all", "base_swaps", "defi_tools"],
|
|
4011
|
+
"description": "Scope to enable. Use \"all\" (recommended) -- base_swaps and defi_tools are deprecated aliases that enable the exact same full autonomous permission group, covering every wallet write tool.",
|
|
3916
4012
|
},
|
|
3917
4013
|
"purpose": {
|
|
3918
4014
|
"type": "string",
|
|
@@ -3940,8 +4036,8 @@ class OpenClawWalletAdapter:
|
|
|
3940
4036
|
"properties": {
|
|
3941
4037
|
"scope": {
|
|
3942
4038
|
"type": "string",
|
|
3943
|
-
"enum": ["base_swaps", "defi_tools"],
|
|
3944
|
-
"description": "
|
|
4039
|
+
"enum": ["all", "base_swaps", "defi_tools"],
|
|
4040
|
+
"description": "Scope to revoke. Use \"all\" (recommended) -- base_swaps and defi_tools are deprecated aliases that revoke the exact same full autonomous permission group.",
|
|
3945
4041
|
}
|
|
3946
4042
|
},
|
|
3947
4043
|
"required": ["scope"],
|
|
@@ -3979,7 +4075,7 @@ class OpenClawWalletAdapter:
|
|
|
3979
4075
|
scope = str(args.get("scope") or "").strip()
|
|
3980
4076
|
purpose = args.get("purpose")
|
|
3981
4077
|
if scope not in autonomous_permissions.SUPPORTED_SCOPES:
|
|
3982
|
-
raise WalletBackendError("scope must be one of: base_swaps, defi_tools.")
|
|
4078
|
+
raise WalletBackendError("scope must be one of: all, base_swaps, defi_tools.")
|
|
3983
4079
|
if not isinstance(purpose, str) or not purpose.strip():
|
|
3984
4080
|
raise WalletBackendError("purpose is required.")
|
|
3985
4081
|
if args.get("user_intent") is not True:
|
|
@@ -4000,7 +4096,7 @@ class OpenClawWalletAdapter:
|
|
|
4000
4096
|
|
|
4001
4097
|
scope = str(args.get("scope") or "").strip()
|
|
4002
4098
|
if scope not in autonomous_permissions.SUPPORTED_SCOPES:
|
|
4003
|
-
raise WalletBackendError("scope must be one of: base_swaps, defi_tools.")
|
|
4099
|
+
raise WalletBackendError("scope must be one of: all, base_swaps, defi_tools.")
|
|
4004
4100
|
return AgentToolResult(
|
|
4005
4101
|
tool=tool_name,
|
|
4006
4102
|
ok=True,
|
|
@@ -4145,7 +4241,14 @@ class OpenClawWalletAdapter:
|
|
|
4145
4241
|
approved_preview = args.get("_approved_preview")
|
|
4146
4242
|
if approved_preview is not None and not isinstance(approved_preview, dict):
|
|
4147
4243
|
raise WalletBackendError("_approved_preview must be an object when provided.")
|
|
4148
|
-
|
|
4244
|
+
approval_token = args.get("approval_token")
|
|
4245
|
+
|
|
4246
|
+
# Resolve the exact payment about to be made -- reusing
|
|
4247
|
+
# _approved_preview when it still matches this request (same
|
|
4248
|
+
# fingerprint check pay_and_fetch applies internally), or a
|
|
4249
|
+
# fresh probe otherwise -- so the summary bound into the
|
|
4250
|
+
# approval token below is never stale or re-priced.
|
|
4251
|
+
preview_payload = await x402.resolve_payment_preview(
|
|
4149
4252
|
backend=active_backend,
|
|
4150
4253
|
url=url.strip(),
|
|
4151
4254
|
method=method,
|
|
@@ -4155,6 +4258,32 @@ class OpenClawWalletAdapter:
|
|
|
4155
4258
|
text_body=text_body,
|
|
4156
4259
|
approved_preview=approved_preview,
|
|
4157
4260
|
)
|
|
4261
|
+
de_minimis = False
|
|
4262
|
+
if preview_payload.get("payment_required"):
|
|
4263
|
+
de_minimis = x402.is_de_minimis_payment(preview_payload)
|
|
4264
|
+
if not de_minimis:
|
|
4265
|
+
summary = self._build_confirmation_summary(
|
|
4266
|
+
action_label="x402 paid request",
|
|
4267
|
+
payload=preview_payload,
|
|
4268
|
+
)
|
|
4269
|
+
self._require_execute_approval(
|
|
4270
|
+
approval_token=approval_token,
|
|
4271
|
+
tool_name=tool_name,
|
|
4272
|
+
summary=summary,
|
|
4273
|
+
action_label="x402 paid request",
|
|
4274
|
+
backend=active_backend,
|
|
4275
|
+
)
|
|
4276
|
+
|
|
4277
|
+
data = await x402.pay_and_fetch(
|
|
4278
|
+
backend=active_backend,
|
|
4279
|
+
url=url.strip(),
|
|
4280
|
+
method=method,
|
|
4281
|
+
headers=headers,
|
|
4282
|
+
query=query,
|
|
4283
|
+
json_body=json_body,
|
|
4284
|
+
text_body=text_body,
|
|
4285
|
+
approved_preview=preview_payload,
|
|
4286
|
+
)
|
|
4158
4287
|
data["purpose"] = purpose.strip()
|
|
4159
4288
|
data = self._annotate_x402_payload(data, mode="execute")
|
|
4160
4289
|
return AgentToolResult(tool=tool_name, ok=True, data=data)
|
|
@@ -5349,57 +5478,68 @@ class OpenClawWalletAdapter:
|
|
|
5349
5478
|
),
|
|
5350
5479
|
)
|
|
5351
5480
|
|
|
5352
|
-
|
|
5353
|
-
|
|
5354
|
-
|
|
5355
|
-
|
|
5356
|
-
|
|
5357
|
-
|
|
5358
|
-
approval_summary = approval_payload.get("binding", {}).get("summary")
|
|
5359
|
-
if not isinstance(approval_summary, dict):
|
|
5360
|
-
raise WalletBackendError(
|
|
5361
|
-
"approval_token does not match the requested operation. Generate a new approval after previewing the exact action."
|
|
5481
|
+
if isinstance(approval_token, str) and approval_token.strip():
|
|
5482
|
+
approval_payload = inspect_approval_token(
|
|
5483
|
+
approval_token,
|
|
5484
|
+
tool_name=tool_name,
|
|
5485
|
+
network=str(getattr(active_backend, "network", "unknown")),
|
|
5486
|
+
require_mainnet_confirmation=self._is_mainnet_for_backend(active_backend),
|
|
5362
5487
|
)
|
|
5363
|
-
|
|
5364
|
-
|
|
5365
|
-
)
|
|
5366
|
-
destination_chain_id = self._canonicalize_lifi_chain_identifier(destination_chain)
|
|
5367
|
-
expected_summary = {
|
|
5368
|
-
"operation": "EVM LI.FI cross-chain swap",
|
|
5369
|
-
"network": str(getattr(active_backend, "network", "unknown")),
|
|
5370
|
-
"source_chain": source_chain_id,
|
|
5371
|
-
"destination_chain": destination_chain_id,
|
|
5372
|
-
"token_in": self._canonicalize_lifi_token_identifier(
|
|
5373
|
-
token_in,
|
|
5374
|
-
chain_id=source_chain_id,
|
|
5375
|
-
),
|
|
5376
|
-
"output_token": self._canonicalize_lifi_token_identifier(
|
|
5377
|
-
output_token,
|
|
5378
|
-
chain_id=destination_chain_id,
|
|
5379
|
-
),
|
|
5380
|
-
"destination_address": destination_address.strip(),
|
|
5381
|
-
"input_amount_raw": amount_in_raw.strip(),
|
|
5382
|
-
}
|
|
5383
|
-
for key, expected_value in expected_summary.items():
|
|
5384
|
-
actual_value = approval_summary.get(key)
|
|
5385
|
-
if key in {"source_chain", "destination_chain"}:
|
|
5386
|
-
actual_value = self._canonicalize_lifi_chain_identifier(actual_value)
|
|
5387
|
-
if key == "token_in":
|
|
5388
|
-
actual_value = self._canonicalize_lifi_token_identifier(
|
|
5389
|
-
actual_value,
|
|
5390
|
-
chain_id=source_chain_id,
|
|
5391
|
-
)
|
|
5392
|
-
if key == "output_token":
|
|
5393
|
-
actual_value = self._canonicalize_lifi_token_identifier(
|
|
5394
|
-
actual_value,
|
|
5395
|
-
chain_id=destination_chain_id,
|
|
5396
|
-
)
|
|
5397
|
-
if actual_value != expected_value:
|
|
5488
|
+
approval_summary = approval_payload.get("binding", {}).get("summary")
|
|
5489
|
+
if not isinstance(approval_summary, dict):
|
|
5398
5490
|
raise WalletBackendError(
|
|
5399
5491
|
"approval_token does not match the requested operation. Generate a new approval after previewing the exact action."
|
|
5400
5492
|
)
|
|
5493
|
+
source_chain_id = self._canonicalize_lifi_chain_identifier(
|
|
5494
|
+
getattr(active_backend, "network", "unknown")
|
|
5495
|
+
)
|
|
5496
|
+
destination_chain_id = self._canonicalize_lifi_chain_identifier(destination_chain)
|
|
5497
|
+
expected_summary = {
|
|
5498
|
+
"operation": "EVM LI.FI cross-chain swap",
|
|
5499
|
+
"network": str(getattr(active_backend, "network", "unknown")),
|
|
5500
|
+
"source_chain": source_chain_id,
|
|
5501
|
+
"destination_chain": destination_chain_id,
|
|
5502
|
+
"token_in": self._canonicalize_lifi_token_identifier(
|
|
5503
|
+
token_in,
|
|
5504
|
+
chain_id=source_chain_id,
|
|
5505
|
+
),
|
|
5506
|
+
"output_token": self._canonicalize_lifi_token_identifier(
|
|
5507
|
+
output_token,
|
|
5508
|
+
chain_id=destination_chain_id,
|
|
5509
|
+
),
|
|
5510
|
+
"destination_address": destination_address.strip(),
|
|
5511
|
+
"input_amount_raw": amount_in_raw.strip(),
|
|
5512
|
+
}
|
|
5513
|
+
for key, expected_value in expected_summary.items():
|
|
5514
|
+
actual_value = approval_summary.get(key)
|
|
5515
|
+
if key in {"source_chain", "destination_chain"}:
|
|
5516
|
+
actual_value = self._canonicalize_lifi_chain_identifier(actual_value)
|
|
5517
|
+
if key == "token_in":
|
|
5518
|
+
actual_value = self._canonicalize_lifi_token_identifier(
|
|
5519
|
+
actual_value,
|
|
5520
|
+
chain_id=source_chain_id,
|
|
5521
|
+
)
|
|
5522
|
+
if key == "output_token":
|
|
5523
|
+
actual_value = self._canonicalize_lifi_token_identifier(
|
|
5524
|
+
actual_value,
|
|
5525
|
+
chain_id=destination_chain_id,
|
|
5526
|
+
)
|
|
5527
|
+
if actual_value != expected_value:
|
|
5528
|
+
raise WalletBackendError(
|
|
5529
|
+
"approval_token does not match the requested operation. Generate a new approval after previewing the exact action."
|
|
5530
|
+
)
|
|
5401
5531
|
|
|
5402
|
-
|
|
5532
|
+
approval_summary_copy = dict(approval_summary)
|
|
5533
|
+
else:
|
|
5534
|
+
# No host token: build the exact preview a human would
|
|
5535
|
+
# have seen and let _require_execute_approval's
|
|
5536
|
+
# autonomous_session / agentlayer_autonomous_approve
|
|
5537
|
+
# fallback authorize it.
|
|
5538
|
+
fresh_preview = await active_backend.preview_evm_lifi_cross_chain_swap(**preview_kwargs)
|
|
5539
|
+
approval_summary_copy = self._build_confirmation_summary(
|
|
5540
|
+
action_label="EVM LI.FI cross-chain swap",
|
|
5541
|
+
payload=fresh_preview,
|
|
5542
|
+
)
|
|
5403
5543
|
self._require_execute_approval(
|
|
5404
5544
|
approval_token=approval_token,
|
|
5405
5545
|
tool_name=tool_name,
|
|
@@ -5460,6 +5600,37 @@ class OpenClawWalletAdapter:
|
|
|
5460
5600
|
)
|
|
5461
5601
|
return AgentToolResult(tool=tool_name, ok=True, data=data)
|
|
5462
5602
|
|
|
5603
|
+
if tool_name == "search_uniswap_pairs":
|
|
5604
|
+
query = args.get("query")
|
|
5605
|
+
token_address = args.get("token_address")
|
|
5606
|
+
chain = args.get("chain")
|
|
5607
|
+
dex_id = args.get("dex_id")
|
|
5608
|
+
all_chains = bool(args.get("all_chains", False))
|
|
5609
|
+
limit = args.get("limit", 10)
|
|
5610
|
+
if query is not None and not isinstance(query, str):
|
|
5611
|
+
raise WalletBackendError("query must be a string.")
|
|
5612
|
+
if token_address is not None and not isinstance(token_address, str):
|
|
5613
|
+
raise WalletBackendError("token_address must be a string.")
|
|
5614
|
+
if not (isinstance(query, str) and query.strip()) and not (
|
|
5615
|
+
isinstance(token_address, str) and token_address.strip()
|
|
5616
|
+
):
|
|
5617
|
+
raise WalletBackendError("Either query or token_address is required.")
|
|
5618
|
+
if chain is not None and not isinstance(chain, str):
|
|
5619
|
+
raise WalletBackendError("chain must be a string.")
|
|
5620
|
+
if dex_id is not None and not isinstance(dex_id, str):
|
|
5621
|
+
raise WalletBackendError("dex_id must be a string.")
|
|
5622
|
+
if not isinstance(limit, int) or limit < 1 or limit > 30:
|
|
5623
|
+
raise WalletBackendError("limit must be an integer between 1 and 30.")
|
|
5624
|
+
data = await active_backend.search_uniswap_pairs(
|
|
5625
|
+
query=query,
|
|
5626
|
+
token_address=token_address,
|
|
5627
|
+
chain=chain,
|
|
5628
|
+
dex_id=dex_id,
|
|
5629
|
+
all_chains=all_chains,
|
|
5630
|
+
limit=limit,
|
|
5631
|
+
)
|
|
5632
|
+
return AgentToolResult(tool=tool_name, ok=True, data=data)
|
|
5633
|
+
|
|
5463
5634
|
if tool_name == "swap_evm_uniswap_tokens":
|
|
5464
5635
|
token_in = args.get("token_in")
|
|
5465
5636
|
token_out = args.get("token_out")
|
|
@@ -5750,46 +5921,69 @@ class OpenClawWalletAdapter:
|
|
|
5750
5921
|
),
|
|
5751
5922
|
)
|
|
5752
5923
|
|
|
5753
|
-
|
|
5754
|
-
|
|
5755
|
-
|
|
5756
|
-
|
|
5757
|
-
|
|
5758
|
-
|
|
5759
|
-
|
|
5760
|
-
if not isinstance(approval_summary, dict):
|
|
5761
|
-
raise WalletBackendError(
|
|
5762
|
-
"approval_token does not match the requested operation. Generate a new approval after previewing the exact action."
|
|
5924
|
+
execute_preview = None
|
|
5925
|
+
if isinstance(approval_token, str) and approval_token.strip():
|
|
5926
|
+
approval_payload = inspect_approval_token(
|
|
5927
|
+
approval_token,
|
|
5928
|
+
tool_name=tool_name,
|
|
5929
|
+
network=str(getattr(active_backend, "network", "unknown")),
|
|
5930
|
+
require_mainnet_confirmation=self._is_mainnet_for_backend(active_backend),
|
|
5763
5931
|
)
|
|
5764
|
-
|
|
5765
|
-
|
|
5766
|
-
"pool_name": pool_name.strip(),
|
|
5767
|
-
"market_symbol": market_symbol.strip(),
|
|
5768
|
-
"collateral_symbol": collateral_symbol.strip(),
|
|
5769
|
-
"collateral_amount_raw": collateral_amount_raw.strip(),
|
|
5770
|
-
"leverage": leverage.strip(),
|
|
5771
|
-
"side": side,
|
|
5772
|
-
}
|
|
5773
|
-
for key, expected_value in expected_summary.items():
|
|
5774
|
-
if approval_summary.get(key) != expected_value:
|
|
5932
|
+
approval_summary = approval_payload.get("binding", {}).get("summary")
|
|
5933
|
+
if not isinstance(approval_summary, dict):
|
|
5775
5934
|
raise WalletBackendError(
|
|
5776
5935
|
"approval_token does not match the requested operation. Generate a new approval after previewing the exact action."
|
|
5777
5936
|
)
|
|
5937
|
+
expected_summary = {
|
|
5938
|
+
"operation": "Flash Trade open position",
|
|
5939
|
+
"pool_name": pool_name.strip(),
|
|
5940
|
+
"market_symbol": market_symbol.strip(),
|
|
5941
|
+
"collateral_symbol": collateral_symbol.strip(),
|
|
5942
|
+
"collateral_amount_raw": collateral_amount_raw.strip(),
|
|
5943
|
+
"leverage": leverage.strip(),
|
|
5944
|
+
"side": side,
|
|
5945
|
+
}
|
|
5946
|
+
for key, expected_value in expected_summary.items():
|
|
5947
|
+
if approval_summary.get(key) != expected_value:
|
|
5948
|
+
raise WalletBackendError(
|
|
5949
|
+
"approval_token does not match the requested operation. Generate a new approval after previewing the exact action."
|
|
5950
|
+
)
|
|
5778
5951
|
|
|
5779
|
-
|
|
5780
|
-
|
|
5781
|
-
|
|
5782
|
-
|
|
5783
|
-
|
|
5784
|
-
|
|
5785
|
-
|
|
5786
|
-
)
|
|
5787
|
-
|
|
5788
|
-
|
|
5789
|
-
|
|
5952
|
+
approval_summary_copy = dict(approval_summary)
|
|
5953
|
+
approved_preview = args.get("_approved_preview")
|
|
5954
|
+
if isinstance(approval_summary_copy.get("_preview_digest"), str):
|
|
5955
|
+
if not isinstance(approved_preview, dict):
|
|
5956
|
+
raise WalletBackendError(
|
|
5957
|
+
"Approved Flash Trade preview payload is required for execute mode. Generate a new preview and approval before execute."
|
|
5958
|
+
)
|
|
5959
|
+
if preview_payload_digest(approved_preview) != approval_summary_copy["_preview_digest"]:
|
|
5960
|
+
raise WalletBackendError(
|
|
5961
|
+
"approved preview payload does not match the approval token. Generate a new preview and approval before execute."
|
|
5962
|
+
)
|
|
5963
|
+
execute_preview = dict(approved_preview)
|
|
5964
|
+
else:
|
|
5965
|
+
execute_preview = await active_backend.preview_flash_trade_open_position(
|
|
5966
|
+
pool_name=pool_name.strip(),
|
|
5967
|
+
market_symbol=market_symbol.strip(),
|
|
5968
|
+
collateral_symbol=collateral_symbol.strip(),
|
|
5969
|
+
collateral_amount_raw=collateral_amount_raw.strip(),
|
|
5970
|
+
leverage=leverage.strip(),
|
|
5971
|
+
side=side,
|
|
5790
5972
|
)
|
|
5791
|
-
execute_preview = dict(approved_preview)
|
|
5792
5973
|
else:
|
|
5974
|
+
# No host token: build the same identity summary the
|
|
5975
|
+
# human path validates, and let _require_execute_approval's
|
|
5976
|
+
# autonomous_session / agentlayer_autonomous_approve
|
|
5977
|
+
# fallback authorize it.
|
|
5978
|
+
approval_summary_copy = {
|
|
5979
|
+
"operation": "Flash Trade open position",
|
|
5980
|
+
"pool_name": pool_name.strip(),
|
|
5981
|
+
"market_symbol": market_symbol.strip(),
|
|
5982
|
+
"collateral_symbol": collateral_symbol.strip(),
|
|
5983
|
+
"collateral_amount_raw": collateral_amount_raw.strip(),
|
|
5984
|
+
"leverage": leverage.strip(),
|
|
5985
|
+
"side": side,
|
|
5986
|
+
}
|
|
5793
5987
|
execute_preview = await active_backend.preview_flash_trade_open_position(
|
|
5794
5988
|
pool_name=pool_name.strip(),
|
|
5795
5989
|
market_symbol=market_symbol.strip(),
|
|
@@ -5879,43 +6073,60 @@ class OpenClawWalletAdapter:
|
|
|
5879
6073
|
),
|
|
5880
6074
|
)
|
|
5881
6075
|
|
|
5882
|
-
|
|
5883
|
-
|
|
5884
|
-
|
|
5885
|
-
|
|
5886
|
-
|
|
5887
|
-
|
|
5888
|
-
|
|
5889
|
-
if not isinstance(approval_summary, dict):
|
|
5890
|
-
raise WalletBackendError(
|
|
5891
|
-
"approval_token does not match the requested operation. Generate a new approval after previewing the exact action."
|
|
6076
|
+
execute_preview = None
|
|
6077
|
+
if isinstance(approval_token, str) and approval_token.strip():
|
|
6078
|
+
approval_payload = inspect_approval_token(
|
|
6079
|
+
approval_token,
|
|
6080
|
+
tool_name=tool_name,
|
|
6081
|
+
network=str(getattr(active_backend, "network", "unknown")),
|
|
6082
|
+
require_mainnet_confirmation=self._is_mainnet_for_backend(active_backend),
|
|
5892
6083
|
)
|
|
5893
|
-
|
|
5894
|
-
|
|
5895
|
-
"pool_name": pool_name.strip(),
|
|
5896
|
-
"market_symbol": market_symbol.strip(),
|
|
5897
|
-
"side": side,
|
|
5898
|
-
}
|
|
5899
|
-
for key, expected_value in expected_summary.items():
|
|
5900
|
-
if approval_summary.get(key) != expected_value:
|
|
6084
|
+
approval_summary = approval_payload.get("binding", {}).get("summary")
|
|
6085
|
+
if not isinstance(approval_summary, dict):
|
|
5901
6086
|
raise WalletBackendError(
|
|
5902
6087
|
"approval_token does not match the requested operation. Generate a new approval after previewing the exact action."
|
|
5903
6088
|
)
|
|
6089
|
+
expected_summary = {
|
|
6090
|
+
"operation": "Flash Trade close position",
|
|
6091
|
+
"pool_name": pool_name.strip(),
|
|
6092
|
+
"market_symbol": market_symbol.strip(),
|
|
6093
|
+
"side": side,
|
|
6094
|
+
}
|
|
6095
|
+
for key, expected_value in expected_summary.items():
|
|
6096
|
+
if approval_summary.get(key) != expected_value:
|
|
6097
|
+
raise WalletBackendError(
|
|
6098
|
+
"approval_token does not match the requested operation. Generate a new approval after previewing the exact action."
|
|
6099
|
+
)
|
|
5904
6100
|
|
|
5905
|
-
|
|
5906
|
-
|
|
5907
|
-
|
|
5908
|
-
|
|
5909
|
-
|
|
5910
|
-
|
|
5911
|
-
|
|
5912
|
-
)
|
|
5913
|
-
|
|
5914
|
-
|
|
5915
|
-
|
|
6101
|
+
approval_summary_copy = dict(approval_summary)
|
|
6102
|
+
approved_preview = args.get("_approved_preview")
|
|
6103
|
+
if isinstance(approval_summary_copy.get("_preview_digest"), str):
|
|
6104
|
+
if not isinstance(approved_preview, dict):
|
|
6105
|
+
raise WalletBackendError(
|
|
6106
|
+
"Approved Flash Trade preview payload is required for execute mode. Generate a new preview and approval before execute."
|
|
6107
|
+
)
|
|
6108
|
+
if preview_payload_digest(approved_preview) != approval_summary_copy["_preview_digest"]:
|
|
6109
|
+
raise WalletBackendError(
|
|
6110
|
+
"approved preview payload does not match the approval token. Generate a new preview and approval before execute."
|
|
6111
|
+
)
|
|
6112
|
+
execute_preview = dict(approved_preview)
|
|
6113
|
+
else:
|
|
6114
|
+
execute_preview = await active_backend.preview_flash_trade_close_position(
|
|
6115
|
+
pool_name=pool_name.strip(),
|
|
6116
|
+
market_symbol=market_symbol.strip(),
|
|
6117
|
+
side=side,
|
|
5916
6118
|
)
|
|
5917
|
-
execute_preview = dict(approved_preview)
|
|
5918
6119
|
else:
|
|
6120
|
+
# No host token: build the same identity summary the
|
|
6121
|
+
# human path validates, and let _require_execute_approval's
|
|
6122
|
+
# autonomous_session / agentlayer_autonomous_approve
|
|
6123
|
+
# fallback authorize it.
|
|
6124
|
+
approval_summary_copy = {
|
|
6125
|
+
"operation": "Flash Trade close position",
|
|
6126
|
+
"pool_name": pool_name.strip(),
|
|
6127
|
+
"market_symbol": market_symbol.strip(),
|
|
6128
|
+
"side": side,
|
|
6129
|
+
}
|
|
5919
6130
|
execute_preview = await active_backend.preview_flash_trade_close_position(
|
|
5920
6131
|
pool_name=pool_name.strip(),
|
|
5921
6132
|
market_symbol=market_symbol.strip(),
|
|
@@ -6432,44 +6643,60 @@ class OpenClawWalletAdapter:
|
|
|
6432
6643
|
)
|
|
6433
6644
|
|
|
6434
6645
|
if mode == "intent_execute":
|
|
6435
|
-
|
|
6436
|
-
|
|
6437
|
-
|
|
6438
|
-
|
|
6439
|
-
|
|
6440
|
-
|
|
6441
|
-
approval_summary = approval_payload.get("binding", {}).get("summary")
|
|
6442
|
-
if not isinstance(approval_summary, dict):
|
|
6443
|
-
raise WalletBackendError(
|
|
6444
|
-
"approval_token does not match the requested operation. Generate a new intent preview and approval before execute."
|
|
6646
|
+
if isinstance(approval_token, str) and approval_token.strip():
|
|
6647
|
+
approval_payload = inspect_approval_token(
|
|
6648
|
+
approval_token,
|
|
6649
|
+
tool_name=tool_name,
|
|
6650
|
+
network=str(getattr(self.backend, "network", "unknown")),
|
|
6651
|
+
require_mainnet_confirmation=self._is_mainnet_for_backend(self.backend),
|
|
6445
6652
|
)
|
|
6446
|
-
|
|
6447
|
-
|
|
6448
|
-
"network": str(getattr(self.backend, "network", "unknown")),
|
|
6449
|
-
"kvault": kvault.strip(),
|
|
6450
|
-
"amount_ui": amount_ui.strip(),
|
|
6451
|
-
}
|
|
6452
|
-
for key, expected_value in expected_summary.items():
|
|
6453
|
-
if approval_summary.get(key) != expected_value:
|
|
6653
|
+
approval_summary = approval_payload.get("binding", {}).get("summary")
|
|
6654
|
+
if not isinstance(approval_summary, dict):
|
|
6454
6655
|
raise WalletBackendError(
|
|
6455
|
-
|
|
6656
|
+
"approval_token does not match the requested operation. Generate a new intent preview and approval before execute."
|
|
6456
6657
|
)
|
|
6457
|
-
|
|
6458
|
-
|
|
6459
|
-
|
|
6460
|
-
|
|
6461
|
-
|
|
6462
|
-
|
|
6463
|
-
|
|
6464
|
-
|
|
6465
|
-
|
|
6658
|
+
expected_summary = {
|
|
6659
|
+
"operation": intent_action_label,
|
|
6660
|
+
"network": str(getattr(self.backend, "network", "unknown")),
|
|
6661
|
+
"kvault": kvault.strip(),
|
|
6662
|
+
"amount_ui": amount_ui.strip(),
|
|
6663
|
+
}
|
|
6664
|
+
for key, expected_value in expected_summary.items():
|
|
6665
|
+
if approval_summary.get(key) != expected_value:
|
|
6666
|
+
raise WalletBackendError(
|
|
6667
|
+
f"approval_token does not match the requested {action_label} intent. Generate a fresh intent preview and approval before execute."
|
|
6668
|
+
)
|
|
6669
|
+
if approval_summary.get("recipient_policy") != "owner-only":
|
|
6670
|
+
raise WalletBackendError("approved Kamino intent recipient policy is invalid.")
|
|
6671
|
+
if approval_summary.get("spend_policy") != "exact-amount":
|
|
6672
|
+
raise WalletBackendError("approved Kamino intent spend policy is invalid.")
|
|
6673
|
+
current_owner = await self.backend.get_address()
|
|
6674
|
+
approved_owner = approval_summary.get("owner")
|
|
6675
|
+
if approved_owner and current_owner and str(approved_owner) != str(current_owner):
|
|
6676
|
+
raise WalletBackendError(
|
|
6677
|
+
"approval_token does not match the active wallet owner. Generate a fresh intent preview and approval before execute."
|
|
6678
|
+
)
|
|
6679
|
+
valid_until = approval_summary.get("valid_until_epoch_seconds")
|
|
6680
|
+
if valid_until is not None and int(time.time()) > int(valid_until):
|
|
6681
|
+
raise WalletBackendError(
|
|
6682
|
+
"Approved Kamino intent has expired. Create a fresh intent preview."
|
|
6683
|
+
)
|
|
6684
|
+
approval_summary_copy = dict(approval_summary)
|
|
6685
|
+
else:
|
|
6686
|
+
# No host token: build the exact intent preview a
|
|
6687
|
+
# human would have seen (same call as
|
|
6688
|
+
# mode=intent_preview), and let
|
|
6689
|
+
# _require_execute_approval's autonomous_session /
|
|
6690
|
+
# agentlayer_autonomous_approve fallback authorize it.
|
|
6691
|
+
intent_preview = await intent_preview_method(
|
|
6692
|
+
kvault=kvault.strip(),
|
|
6693
|
+
amount_ui=amount_ui.strip(),
|
|
6694
|
+
valid_for_seconds=valid_for_seconds,
|
|
6466
6695
|
)
|
|
6467
|
-
|
|
6468
|
-
|
|
6469
|
-
|
|
6470
|
-
"Approved Kamino intent has expired. Create a fresh intent preview."
|
|
6696
|
+
approval_summary_copy = self._build_confirmation_summary(
|
|
6697
|
+
action_label=intent_action_label,
|
|
6698
|
+
payload=intent_preview,
|
|
6471
6699
|
)
|
|
6472
|
-
approval_summary_copy = dict(approval_summary)
|
|
6473
6700
|
self._require_execute_approval(
|
|
6474
6701
|
approval_token=approval_token,
|
|
6475
6702
|
tool_name=tool_name,
|
|
@@ -6525,35 +6752,46 @@ class OpenClawWalletAdapter:
|
|
|
6525
6752
|
),
|
|
6526
6753
|
)
|
|
6527
6754
|
|
|
6528
|
-
approved_preview = args.get("_approved_preview")
|
|
6529
6755
|
execute_preview = None
|
|
6530
|
-
|
|
6531
|
-
|
|
6532
|
-
|
|
6533
|
-
|
|
6534
|
-
|
|
6535
|
-
|
|
6536
|
-
|
|
6537
|
-
if not isinstance(approval_summary, dict):
|
|
6538
|
-
raise WalletBackendError(
|
|
6539
|
-
"approval_token does not match the requested operation. Generate a new approval after previewing the exact action."
|
|
6756
|
+
if isinstance(approval_token, str) and approval_token.strip():
|
|
6757
|
+
approved_preview = args.get("_approved_preview")
|
|
6758
|
+
approval_payload = inspect_approval_token(
|
|
6759
|
+
approval_token,
|
|
6760
|
+
tool_name=tool_name,
|
|
6761
|
+
network=str(getattr(self.backend, "network", "unknown")),
|
|
6762
|
+
require_mainnet_confirmation=self._is_mainnet_for_backend(self.backend),
|
|
6540
6763
|
)
|
|
6541
|
-
|
|
6542
|
-
|
|
6543
|
-
if not isinstance(approved_preview, dict):
|
|
6764
|
+
approval_summary = approval_payload.get("binding", {}).get("summary")
|
|
6765
|
+
if not isinstance(approval_summary, dict):
|
|
6544
6766
|
raise WalletBackendError(
|
|
6545
|
-
|
|
6767
|
+
"approval_token does not match the requested operation. Generate a new approval after previewing the exact action."
|
|
6546
6768
|
)
|
|
6547
|
-
|
|
6548
|
-
|
|
6549
|
-
|
|
6769
|
+
approval_summary_copy = dict(approval_summary)
|
|
6770
|
+
if isinstance(approval_summary_copy.get("_preview_digest"), str):
|
|
6771
|
+
if not isinstance(approved_preview, dict):
|
|
6772
|
+
raise WalletBackendError(
|
|
6773
|
+
f"Approved {action_label} preview payload is required for execute mode. Generate a new preview and approval before execute."
|
|
6774
|
+
)
|
|
6775
|
+
if preview_payload_digest(approved_preview) != approval_summary_copy["_preview_digest"]:
|
|
6776
|
+
raise WalletBackendError(
|
|
6777
|
+
"approved preview payload does not match the approval token. Generate a new preview and approval before execute."
|
|
6778
|
+
)
|
|
6779
|
+
execute_preview = dict(approved_preview)
|
|
6780
|
+
else:
|
|
6781
|
+
execute_preview = await preview_method(
|
|
6782
|
+
kvault=kvault.strip(),
|
|
6783
|
+
amount_ui=amount_ui.strip(),
|
|
6550
6784
|
)
|
|
6551
|
-
execute_preview = dict(approved_preview)
|
|
6552
6785
|
else:
|
|
6786
|
+
# No host token: same fallback as intent_execute above.
|
|
6553
6787
|
execute_preview = await preview_method(
|
|
6554
6788
|
kvault=kvault.strip(),
|
|
6555
6789
|
amount_ui=amount_ui.strip(),
|
|
6556
6790
|
)
|
|
6791
|
+
approval_summary_copy = self._build_confirmation_summary(
|
|
6792
|
+
action_label=action_label,
|
|
6793
|
+
payload=execute_preview,
|
|
6794
|
+
)
|
|
6557
6795
|
self._require_execute_approval(
|
|
6558
6796
|
approval_token=approval_token,
|
|
6559
6797
|
tool_name=tool_name,
|
|
@@ -6676,60 +6914,83 @@ class OpenClawWalletAdapter:
|
|
|
6676
6914
|
)
|
|
6677
6915
|
|
|
6678
6916
|
if mode == "intent_execute":
|
|
6679
|
-
|
|
6680
|
-
|
|
6681
|
-
|
|
6682
|
-
|
|
6683
|
-
|
|
6684
|
-
|
|
6685
|
-
approval_summary = approval_payload.get("binding", {}).get("summary")
|
|
6686
|
-
if not isinstance(approval_summary, dict):
|
|
6687
|
-
raise WalletBackendError(
|
|
6688
|
-
"approval_token does not match the requested operation. Generate a new intent preview and approval before execute."
|
|
6917
|
+
if isinstance(approval_token, str) and approval_token.strip():
|
|
6918
|
+
approval_payload = inspect_approval_token(
|
|
6919
|
+
approval_token,
|
|
6920
|
+
tool_name=tool_name,
|
|
6921
|
+
network=str(getattr(self.backend, "network", "unknown")),
|
|
6922
|
+
require_mainnet_confirmation=self._is_mainnet_for_backend(self.backend),
|
|
6689
6923
|
)
|
|
6690
|
-
|
|
6691
|
-
|
|
6692
|
-
"network": str(getattr(self.backend, "network", "unknown")),
|
|
6693
|
-
"market": market.strip(),
|
|
6694
|
-
"reserve": reserve.strip(),
|
|
6695
|
-
"amount_ui": amount_ui.strip(),
|
|
6696
|
-
}
|
|
6697
|
-
for key, expected_value in expected_summary.items():
|
|
6698
|
-
if approval_summary.get(key) != expected_value:
|
|
6924
|
+
approval_summary = approval_payload.get("binding", {}).get("summary")
|
|
6925
|
+
if not isinstance(approval_summary, dict):
|
|
6699
6926
|
raise WalletBackendError(
|
|
6700
|
-
|
|
6927
|
+
"approval_token does not match the requested operation. Generate a new intent preview and approval before execute."
|
|
6701
6928
|
)
|
|
6702
|
-
|
|
6703
|
-
|
|
6704
|
-
|
|
6705
|
-
|
|
6706
|
-
|
|
6707
|
-
|
|
6708
|
-
|
|
6709
|
-
|
|
6710
|
-
|
|
6711
|
-
|
|
6712
|
-
|
|
6713
|
-
|
|
6714
|
-
|
|
6715
|
-
|
|
6716
|
-
|
|
6717
|
-
|
|
6718
|
-
|
|
6719
|
-
|
|
6929
|
+
expected_summary = {
|
|
6930
|
+
"operation": intent_action_label,
|
|
6931
|
+
"network": str(getattr(self.backend, "network", "unknown")),
|
|
6932
|
+
"market": market.strip(),
|
|
6933
|
+
"reserve": reserve.strip(),
|
|
6934
|
+
"amount_ui": amount_ui.strip(),
|
|
6935
|
+
}
|
|
6936
|
+
for key, expected_value in expected_summary.items():
|
|
6937
|
+
if approval_summary.get(key) != expected_value:
|
|
6938
|
+
raise WalletBackendError(
|
|
6939
|
+
f"approval_token does not match the requested {action_label} intent. Generate a fresh intent preview and approval before execute."
|
|
6940
|
+
)
|
|
6941
|
+
if approval_summary.get("recipient_policy") != "owner-only":
|
|
6942
|
+
raise WalletBackendError("approved Kamino intent recipient policy is invalid.")
|
|
6943
|
+
if approval_summary.get("spend_policy") != "exact-amount":
|
|
6944
|
+
raise WalletBackendError("approved Kamino intent spend policy is invalid.")
|
|
6945
|
+
current_owner = await self.backend.get_address()
|
|
6946
|
+
approved_owner = approval_summary.get("owner")
|
|
6947
|
+
if approved_owner and current_owner and str(approved_owner) != str(current_owner):
|
|
6948
|
+
raise WalletBackendError(
|
|
6949
|
+
"approval_token does not match the active wallet owner. Generate a fresh intent preview and approval before execute."
|
|
6950
|
+
)
|
|
6951
|
+
approved_obligation = approval_summary.get("obligation_address")
|
|
6952
|
+
if (
|
|
6953
|
+
normalized_obligation_address is not None
|
|
6954
|
+
and approved_obligation is not None
|
|
6955
|
+
and str(approved_obligation) != normalized_obligation_address
|
|
6956
|
+
):
|
|
6957
|
+
raise WalletBackendError(
|
|
6958
|
+
"approval_token does not match the requested obligation. Generate a fresh intent preview and approval before execute."
|
|
6959
|
+
)
|
|
6960
|
+
valid_until = approval_summary.get("valid_until_epoch_seconds")
|
|
6961
|
+
if valid_until is not None and int(time.time()) > int(valid_until):
|
|
6962
|
+
raise WalletBackendError(
|
|
6963
|
+
"Approved Kamino intent has expired. Create a fresh intent preview."
|
|
6964
|
+
)
|
|
6965
|
+
approval_summary_copy = dict(approval_summary)
|
|
6966
|
+
else:
|
|
6967
|
+
# No host token: build the exact intent preview a
|
|
6968
|
+
# human would have seen (same call as
|
|
6969
|
+
# mode=intent_preview), and let
|
|
6970
|
+
# _require_execute_approval's autonomous_session /
|
|
6971
|
+
# agentlayer_autonomous_approve fallback authorize it.
|
|
6972
|
+
intent_preview = await intent_preview_method(
|
|
6973
|
+
market=market.strip(),
|
|
6974
|
+
reserve=reserve.strip(),
|
|
6975
|
+
amount_ui=amount_ui.strip(),
|
|
6976
|
+
obligation_address=normalized_obligation_address,
|
|
6977
|
+
valid_for_seconds=valid_for_seconds,
|
|
6720
6978
|
)
|
|
6721
|
-
|
|
6722
|
-
|
|
6723
|
-
|
|
6724
|
-
|
|
6979
|
+
if bool(intent_preview.get("requires_obligation_address")):
|
|
6980
|
+
raise WalletBackendError(
|
|
6981
|
+
f"{action_label} requires obligation_address when multiple Kamino obligations match the selected position."
|
|
6982
|
+
)
|
|
6983
|
+
approval_summary_copy = self._build_confirmation_summary(
|
|
6984
|
+
action_label=intent_action_label,
|
|
6985
|
+
payload=intent_preview,
|
|
6725
6986
|
)
|
|
6726
|
-
approval_summary_copy = dict(approval_summary)
|
|
6727
6987
|
self._require_execute_approval(
|
|
6728
6988
|
approval_token=approval_token,
|
|
6729
6989
|
tool_name=tool_name,
|
|
6730
6990
|
summary=approval_summary_copy,
|
|
6731
6991
|
action_label=intent_action_label,
|
|
6732
6992
|
)
|
|
6993
|
+
approved_obligation = approval_summary_copy.get("obligation_address")
|
|
6733
6994
|
result = await execute_method(
|
|
6734
6995
|
market=market.strip(),
|
|
6735
6996
|
reserve=reserve.strip(),
|
|
@@ -6793,37 +7054,50 @@ class OpenClawWalletAdapter:
|
|
|
6793
7054
|
),
|
|
6794
7055
|
)
|
|
6795
7056
|
|
|
6796
|
-
approved_preview = args.get("_approved_preview")
|
|
6797
7057
|
execute_preview = None
|
|
6798
|
-
|
|
6799
|
-
|
|
6800
|
-
|
|
6801
|
-
|
|
6802
|
-
|
|
6803
|
-
|
|
6804
|
-
|
|
6805
|
-
if not isinstance(approval_summary, dict):
|
|
6806
|
-
raise WalletBackendError(
|
|
6807
|
-
"approval_token does not match the requested operation. Generate a new approval after previewing the exact action."
|
|
7058
|
+
if isinstance(approval_token, str) and approval_token.strip():
|
|
7059
|
+
approved_preview = args.get("_approved_preview")
|
|
7060
|
+
approval_payload = inspect_approval_token(
|
|
7061
|
+
approval_token,
|
|
7062
|
+
tool_name=tool_name,
|
|
7063
|
+
network=str(getattr(self.backend, "network", "unknown")),
|
|
7064
|
+
require_mainnet_confirmation=self._is_mainnet_for_backend(self.backend),
|
|
6808
7065
|
)
|
|
6809
|
-
|
|
6810
|
-
|
|
6811
|
-
if not isinstance(approved_preview, dict):
|
|
7066
|
+
approval_summary = approval_payload.get("binding", {}).get("summary")
|
|
7067
|
+
if not isinstance(approval_summary, dict):
|
|
6812
7068
|
raise WalletBackendError(
|
|
6813
|
-
|
|
7069
|
+
"approval_token does not match the requested operation. Generate a new approval after previewing the exact action."
|
|
6814
7070
|
)
|
|
6815
|
-
|
|
6816
|
-
|
|
6817
|
-
|
|
7071
|
+
approval_summary_copy = dict(approval_summary)
|
|
7072
|
+
if isinstance(approval_summary_copy.get("_preview_digest"), str):
|
|
7073
|
+
if not isinstance(approved_preview, dict):
|
|
7074
|
+
raise WalletBackendError(
|
|
7075
|
+
f"Approved {action_label} preview payload is required for execute mode. Generate a new preview and approval before execute."
|
|
7076
|
+
)
|
|
7077
|
+
if preview_payload_digest(approved_preview) != approval_summary_copy["_preview_digest"]:
|
|
7078
|
+
raise WalletBackendError(
|
|
7079
|
+
"approved preview payload does not match the approval token. Generate a new preview and approval before execute."
|
|
7080
|
+
)
|
|
7081
|
+
execute_preview = dict(approved_preview)
|
|
7082
|
+
else:
|
|
7083
|
+
execute_preview = await preview_method(
|
|
7084
|
+
market=market.strip(),
|
|
7085
|
+
reserve=reserve.strip(),
|
|
7086
|
+
amount_ui=amount_ui.strip(),
|
|
7087
|
+
obligation_address=obligation_address.strip() if isinstance(obligation_address, str) and obligation_address.strip() else None,
|
|
6818
7088
|
)
|
|
6819
|
-
execute_preview = dict(approved_preview)
|
|
6820
7089
|
else:
|
|
7090
|
+
# No host token: same fallback as intent_execute above.
|
|
6821
7091
|
execute_preview = await preview_method(
|
|
6822
7092
|
market=market.strip(),
|
|
6823
7093
|
reserve=reserve.strip(),
|
|
6824
7094
|
amount_ui=amount_ui.strip(),
|
|
6825
7095
|
obligation_address=obligation_address.strip() if isinstance(obligation_address, str) and obligation_address.strip() else None,
|
|
6826
7096
|
)
|
|
7097
|
+
approval_summary_copy = self._build_confirmation_summary(
|
|
7098
|
+
action_label=action_label,
|
|
7099
|
+
payload=execute_preview,
|
|
7100
|
+
)
|
|
6827
7101
|
self._require_execute_approval(
|
|
6828
7102
|
approval_token=approval_token,
|
|
6829
7103
|
tool_name=tool_name,
|
|
@@ -7123,55 +7397,75 @@ class OpenClawWalletAdapter:
|
|
|
7123
7397
|
)
|
|
7124
7398
|
|
|
7125
7399
|
if mode == "intent_execute":
|
|
7126
|
-
|
|
7127
|
-
|
|
7128
|
-
|
|
7129
|
-
|
|
7130
|
-
|
|
7131
|
-
|
|
7132
|
-
approval_summary = approval_payload.get("binding", {}).get("summary")
|
|
7133
|
-
if not isinstance(approval_summary, dict):
|
|
7134
|
-
raise WalletBackendError(
|
|
7135
|
-
"approval_token does not match the requested operation. Generate a new intent preview and approval before execute."
|
|
7400
|
+
if isinstance(approval_token, str) and approval_token.strip():
|
|
7401
|
+
approval_payload = inspect_approval_token(
|
|
7402
|
+
approval_token,
|
|
7403
|
+
tool_name=tool_name,
|
|
7404
|
+
network=str(getattr(self.backend, "network", "unknown")),
|
|
7405
|
+
require_mainnet_confirmation=self._is_mainnet_for_backend(self.backend),
|
|
7136
7406
|
)
|
|
7137
|
-
|
|
7138
|
-
|
|
7139
|
-
|
|
7140
|
-
|
|
7141
|
-
|
|
7142
|
-
|
|
7143
|
-
|
|
7144
|
-
|
|
7407
|
+
approval_summary = approval_payload.get("binding", {}).get("summary")
|
|
7408
|
+
if not isinstance(approval_summary, dict):
|
|
7409
|
+
raise WalletBackendError(
|
|
7410
|
+
"approval_token does not match the requested operation. Generate a new intent preview and approval before execute."
|
|
7411
|
+
)
|
|
7412
|
+
expected_summary = {
|
|
7413
|
+
"operation": "Swap intent",
|
|
7414
|
+
"network": str(getattr(self.backend, "network", "unknown")),
|
|
7415
|
+
"input_mint": input_mint.strip(),
|
|
7416
|
+
"output_mint": output_mint.strip(),
|
|
7417
|
+
}
|
|
7418
|
+
for key, expected_value in expected_summary.items():
|
|
7419
|
+
if approval_summary.get(key) != expected_value:
|
|
7420
|
+
raise WalletBackendError(
|
|
7421
|
+
"approval_token does not match the requested swap intent. Generate a fresh intent preview and approval before execute."
|
|
7422
|
+
)
|
|
7423
|
+
current_owner = await self.backend.get_address()
|
|
7424
|
+
approved_owner = approval_summary.get("owner")
|
|
7425
|
+
if approved_owner and current_owner and str(approved_owner) != str(current_owner):
|
|
7426
|
+
raise WalletBackendError(
|
|
7427
|
+
"approval_token does not match the active wallet owner. Generate a fresh intent preview and approval before execute."
|
|
7428
|
+
)
|
|
7429
|
+
try:
|
|
7430
|
+
approved_amount = float(approval_summary.get("input_amount_ui"))
|
|
7431
|
+
approved_slippage = int(
|
|
7432
|
+
approval_summary.get("max_slippage_bps")
|
|
7433
|
+
if approval_summary.get("max_slippage_bps") is not None
|
|
7434
|
+
else approval_summary.get("slippage_bps")
|
|
7435
|
+
)
|
|
7436
|
+
except (TypeError, ValueError):
|
|
7145
7437
|
raise WalletBackendError(
|
|
7146
7438
|
"approval_token does not match the requested swap intent. Generate a fresh intent preview and approval before execute."
|
|
7147
7439
|
)
|
|
7148
|
-
|
|
7149
|
-
|
|
7150
|
-
|
|
7151
|
-
|
|
7152
|
-
|
|
7153
|
-
|
|
7154
|
-
|
|
7155
|
-
|
|
7156
|
-
|
|
7157
|
-
|
|
7158
|
-
|
|
7159
|
-
|
|
7160
|
-
)
|
|
7161
|
-
|
|
7162
|
-
|
|
7163
|
-
|
|
7440
|
+
if approved_amount != float(amount) or approved_slippage != slippage_bps:
|
|
7441
|
+
raise WalletBackendError(
|
|
7442
|
+
"approval_token does not match the requested swap intent. Generate a fresh intent preview and approval before execute."
|
|
7443
|
+
)
|
|
7444
|
+
if approval_summary.get("recipient_policy") != "owner-only":
|
|
7445
|
+
raise WalletBackendError("approved swap intent recipient policy is invalid.")
|
|
7446
|
+
if approval_summary.get("spend_policy") != "exact-input":
|
|
7447
|
+
raise WalletBackendError("approved swap intent spend policy is invalid.")
|
|
7448
|
+
|
|
7449
|
+
approval_summary_copy = dict(approval_summary)
|
|
7450
|
+
else:
|
|
7451
|
+
# No host token: build the exact intent preview a human
|
|
7452
|
+
# would have seen (same call as mode=intent_preview),
|
|
7453
|
+
# and let _require_execute_approval's autonomous_session /
|
|
7454
|
+
# agentlayer_autonomous_approve fallback authorize it.
|
|
7455
|
+
intent_preview = await self.backend.preview_swap_intent(
|
|
7456
|
+
input_mint=input_mint.strip(),
|
|
7457
|
+
output_mint=output_mint.strip(),
|
|
7458
|
+
amount_ui=float(amount),
|
|
7459
|
+
slippage_bps=slippage_bps,
|
|
7460
|
+
minimum_output_amount_raw=minimum_output_amount_raw,
|
|
7461
|
+
max_fee_lamports=max_fee_lamports,
|
|
7462
|
+
valid_for_seconds=valid_for_seconds,
|
|
7463
|
+
max_attempts=max_attempts,
|
|
7164
7464
|
)
|
|
7165
|
-
|
|
7166
|
-
|
|
7167
|
-
|
|
7465
|
+
approval_summary_copy = self._build_confirmation_summary(
|
|
7466
|
+
action_label="Swap intent",
|
|
7467
|
+
payload=intent_preview,
|
|
7168
7468
|
)
|
|
7169
|
-
if approval_summary.get("recipient_policy") != "owner-only":
|
|
7170
|
-
raise WalletBackendError("approved swap intent recipient policy is invalid.")
|
|
7171
|
-
if approval_summary.get("spend_policy") != "exact-input":
|
|
7172
|
-
raise WalletBackendError("approved swap intent spend policy is invalid.")
|
|
7173
|
-
|
|
7174
|
-
approval_summary_copy = dict(approval_summary)
|
|
7175
7469
|
self._require_execute_approval(
|
|
7176
7470
|
approval_token=approval_token,
|
|
7177
7471
|
tool_name=tool_name,
|
|
@@ -7219,59 +7513,75 @@ class OpenClawWalletAdapter:
|
|
|
7219
7513
|
),
|
|
7220
7514
|
)
|
|
7221
7515
|
|
|
7222
|
-
|
|
7223
|
-
|
|
7224
|
-
|
|
7225
|
-
|
|
7226
|
-
|
|
7227
|
-
|
|
7228
|
-
approval_summary = approval_payload.get("binding", {}).get("summary")
|
|
7229
|
-
if not isinstance(approval_summary, dict):
|
|
7230
|
-
raise WalletBackendError(
|
|
7231
|
-
"approval_token does not match the requested operation. Generate a new approval after previewing the exact action."
|
|
7516
|
+
if isinstance(approval_token, str) and approval_token.strip():
|
|
7517
|
+
approval_payload = inspect_approval_token(
|
|
7518
|
+
approval_token,
|
|
7519
|
+
tool_name=tool_name,
|
|
7520
|
+
network=str(getattr(self.backend, "network", "unknown")),
|
|
7521
|
+
require_mainnet_confirmation=self._is_mainnet_for_backend(self.backend),
|
|
7232
7522
|
)
|
|
7233
|
-
|
|
7234
|
-
|
|
7235
|
-
"network": str(getattr(self.backend, "network", "unknown")),
|
|
7236
|
-
"input_mint": input_mint.strip(),
|
|
7237
|
-
"output_mint": output_mint.strip(),
|
|
7238
|
-
"slippage_bps": slippage_bps,
|
|
7239
|
-
}
|
|
7240
|
-
for key, expected_value in expected_summary.items():
|
|
7241
|
-
if approval_summary.get(key) != expected_value:
|
|
7523
|
+
approval_summary = approval_payload.get("binding", {}).get("summary")
|
|
7524
|
+
if not isinstance(approval_summary, dict):
|
|
7242
7525
|
raise WalletBackendError(
|
|
7243
7526
|
"approval_token does not match the requested operation. Generate a new approval after previewing the exact action."
|
|
7244
7527
|
)
|
|
7245
|
-
|
|
7246
|
-
|
|
7247
|
-
|
|
7248
|
-
|
|
7249
|
-
"
|
|
7250
|
-
|
|
7251
|
-
|
|
7252
|
-
|
|
7253
|
-
|
|
7254
|
-
|
|
7255
|
-
|
|
7256
|
-
|
|
7257
|
-
|
|
7258
|
-
|
|
7259
|
-
|
|
7528
|
+
expected_summary = {
|
|
7529
|
+
"operation": "Swap",
|
|
7530
|
+
"network": str(getattr(self.backend, "network", "unknown")),
|
|
7531
|
+
"input_mint": input_mint.strip(),
|
|
7532
|
+
"output_mint": output_mint.strip(),
|
|
7533
|
+
"slippage_bps": slippage_bps,
|
|
7534
|
+
}
|
|
7535
|
+
for key, expected_value in expected_summary.items():
|
|
7536
|
+
if approval_summary.get(key) != expected_value:
|
|
7537
|
+
raise WalletBackendError(
|
|
7538
|
+
"approval_token does not match the requested operation. Generate a new approval after previewing the exact action."
|
|
7539
|
+
)
|
|
7540
|
+
try:
|
|
7541
|
+
approved_amount = float(approval_summary.get("input_amount_ui"))
|
|
7542
|
+
except (TypeError, ValueError):
|
|
7260
7543
|
raise WalletBackendError(
|
|
7261
|
-
"
|
|
7544
|
+
"approval_token does not match the requested operation. Generate a new approval after previewing the exact action."
|
|
7262
7545
|
)
|
|
7263
|
-
if
|
|
7546
|
+
if approved_amount != float(amount):
|
|
7264
7547
|
raise WalletBackendError(
|
|
7265
|
-
"
|
|
7548
|
+
"approval_token does not match the requested operation. Generate a new approval after previewing the exact action."
|
|
7549
|
+
)
|
|
7550
|
+
|
|
7551
|
+
approval_summary_copy = dict(approval_summary)
|
|
7552
|
+
approved_preview = args.get("_approved_preview")
|
|
7553
|
+
if isinstance(approval_summary_copy.get("_preview_digest"), str):
|
|
7554
|
+
if not isinstance(approved_preview, dict):
|
|
7555
|
+
raise WalletBackendError(
|
|
7556
|
+
"Approved swap preview payload is required for execute mode. Generate a new preview and approval before execute."
|
|
7557
|
+
)
|
|
7558
|
+
if preview_payload_digest(approved_preview) != approval_summary_copy["_preview_digest"]:
|
|
7559
|
+
raise WalletBackendError(
|
|
7560
|
+
"approved preview payload does not match the approval token. Generate a new preview and approval before execute."
|
|
7561
|
+
)
|
|
7562
|
+
execute_preview = dict(approved_preview)
|
|
7563
|
+
else:
|
|
7564
|
+
execute_preview = await self.backend.preview_swap(
|
|
7565
|
+
input_mint=input_mint.strip(),
|
|
7566
|
+
output_mint=output_mint.strip(),
|
|
7567
|
+
amount_ui=float(amount),
|
|
7568
|
+
slippage_bps=slippage_bps,
|
|
7266
7569
|
)
|
|
7267
|
-
execute_preview = dict(approved_preview)
|
|
7268
7570
|
else:
|
|
7571
|
+
# No host token: same fallback as intent_execute above --
|
|
7572
|
+
# build a fresh preview and let _require_execute_approval's
|
|
7573
|
+
# autonomous_session / agentlayer_autonomous_approve
|
|
7574
|
+
# fallback authorize it.
|
|
7269
7575
|
execute_preview = await self.backend.preview_swap(
|
|
7270
7576
|
input_mint=input_mint.strip(),
|
|
7271
7577
|
output_mint=output_mint.strip(),
|
|
7272
7578
|
amount_ui=float(amount),
|
|
7273
7579
|
slippage_bps=slippage_bps,
|
|
7274
7580
|
)
|
|
7581
|
+
approval_summary_copy = self._build_confirmation_summary(
|
|
7582
|
+
action_label="Swap",
|
|
7583
|
+
payload=execute_preview,
|
|
7584
|
+
)
|
|
7275
7585
|
self._require_execute_approval(
|
|
7276
7586
|
approval_token=approval_token,
|
|
7277
7587
|
tool_name=tool_name,
|
|
@@ -7362,54 +7672,65 @@ class OpenClawWalletAdapter:
|
|
|
7362
7672
|
),
|
|
7363
7673
|
)
|
|
7364
7674
|
|
|
7365
|
-
|
|
7366
|
-
|
|
7367
|
-
|
|
7368
|
-
|
|
7369
|
-
|
|
7370
|
-
|
|
7371
|
-
approval_summary = approval_payload.get("binding", {}).get("summary")
|
|
7372
|
-
if not isinstance(approval_summary, dict):
|
|
7373
|
-
raise WalletBackendError(
|
|
7374
|
-
"approval_token does not match the requested operation. Generate a new approval after previewing the exact action."
|
|
7675
|
+
if isinstance(approval_token, str) and approval_token.strip():
|
|
7676
|
+
approval_payload = inspect_approval_token(
|
|
7677
|
+
approval_token,
|
|
7678
|
+
tool_name=tool_name,
|
|
7679
|
+
network=str(getattr(self.backend, "network", "unknown")),
|
|
7680
|
+
require_mainnet_confirmation=self._is_mainnet(),
|
|
7375
7681
|
)
|
|
7376
|
-
|
|
7377
|
-
|
|
7378
|
-
"operation": "Solana LI.FI cross-chain swap",
|
|
7379
|
-
"network": str(getattr(self.backend, "network", "unknown")),
|
|
7380
|
-
"source_chain": "solana",
|
|
7381
|
-
"destination_chain": destination_chain_id,
|
|
7382
|
-
"input_token": self._canonicalize_lifi_token_identifier(
|
|
7383
|
-
input_token,
|
|
7384
|
-
chain_id="1151111081099710",
|
|
7385
|
-
),
|
|
7386
|
-
"output_token": self._canonicalize_lifi_token_identifier(
|
|
7387
|
-
output_token,
|
|
7388
|
-
chain_id=destination_chain_id,
|
|
7389
|
-
),
|
|
7390
|
-
"destination_address": destination_address.strip(),
|
|
7391
|
-
"input_amount_raw": amount_in_raw.strip(),
|
|
7392
|
-
}
|
|
7393
|
-
for key, expected_value in expected_summary.items():
|
|
7394
|
-
actual_value = approval_summary.get(key)
|
|
7395
|
-
if key == "destination_chain":
|
|
7396
|
-
actual_value = self._canonicalize_lifi_chain_identifier(actual_value)
|
|
7397
|
-
if key == "input_token":
|
|
7398
|
-
actual_value = self._canonicalize_lifi_token_identifier(
|
|
7399
|
-
actual_value,
|
|
7400
|
-
chain_id="1151111081099710",
|
|
7401
|
-
)
|
|
7402
|
-
if key == "output_token":
|
|
7403
|
-
actual_value = self._canonicalize_lifi_token_identifier(
|
|
7404
|
-
actual_value,
|
|
7405
|
-
chain_id=destination_chain_id,
|
|
7406
|
-
)
|
|
7407
|
-
if actual_value != expected_value:
|
|
7682
|
+
approval_summary = approval_payload.get("binding", {}).get("summary")
|
|
7683
|
+
if not isinstance(approval_summary, dict):
|
|
7408
7684
|
raise WalletBackendError(
|
|
7409
7685
|
"approval_token does not match the requested operation. Generate a new approval after previewing the exact action."
|
|
7410
7686
|
)
|
|
7687
|
+
destination_chain_id = self._canonicalize_lifi_chain_identifier(destination_chain)
|
|
7688
|
+
expected_summary = {
|
|
7689
|
+
"operation": "Solana LI.FI cross-chain swap",
|
|
7690
|
+
"network": str(getattr(self.backend, "network", "unknown")),
|
|
7691
|
+
"source_chain": "solana",
|
|
7692
|
+
"destination_chain": destination_chain_id,
|
|
7693
|
+
"input_token": self._canonicalize_lifi_token_identifier(
|
|
7694
|
+
input_token,
|
|
7695
|
+
chain_id="1151111081099710",
|
|
7696
|
+
),
|
|
7697
|
+
"output_token": self._canonicalize_lifi_token_identifier(
|
|
7698
|
+
output_token,
|
|
7699
|
+
chain_id=destination_chain_id,
|
|
7700
|
+
),
|
|
7701
|
+
"destination_address": destination_address.strip(),
|
|
7702
|
+
"input_amount_raw": amount_in_raw.strip(),
|
|
7703
|
+
}
|
|
7704
|
+
for key, expected_value in expected_summary.items():
|
|
7705
|
+
actual_value = approval_summary.get(key)
|
|
7706
|
+
if key == "destination_chain":
|
|
7707
|
+
actual_value = self._canonicalize_lifi_chain_identifier(actual_value)
|
|
7708
|
+
if key == "input_token":
|
|
7709
|
+
actual_value = self._canonicalize_lifi_token_identifier(
|
|
7710
|
+
actual_value,
|
|
7711
|
+
chain_id="1151111081099710",
|
|
7712
|
+
)
|
|
7713
|
+
if key == "output_token":
|
|
7714
|
+
actual_value = self._canonicalize_lifi_token_identifier(
|
|
7715
|
+
actual_value,
|
|
7716
|
+
chain_id=destination_chain_id,
|
|
7717
|
+
)
|
|
7718
|
+
if actual_value != expected_value:
|
|
7719
|
+
raise WalletBackendError(
|
|
7720
|
+
"approval_token does not match the requested operation. Generate a new approval after previewing the exact action."
|
|
7721
|
+
)
|
|
7411
7722
|
|
|
7412
|
-
|
|
7723
|
+
approval_summary_copy = dict(approval_summary)
|
|
7724
|
+
else:
|
|
7725
|
+
# No host token: build the exact preview a human would
|
|
7726
|
+
# have seen and let _require_execute_approval's
|
|
7727
|
+
# autonomous_session / agentlayer_autonomous_approve
|
|
7728
|
+
# fallback authorize it.
|
|
7729
|
+
fresh_preview = await self.backend.preview_solana_lifi_cross_chain_swap(**preview_kwargs)
|
|
7730
|
+
approval_summary_copy = self._build_confirmation_summary(
|
|
7731
|
+
action_label="Solana LI.FI cross-chain swap",
|
|
7732
|
+
payload=fresh_preview,
|
|
7733
|
+
)
|
|
7413
7734
|
self._require_execute_approval(
|
|
7414
7735
|
approval_token=approval_token,
|
|
7415
7736
|
tool_name=tool_name,
|