@agentlayer.tech/wallet 0.1.89 → 0.1.91
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 +1 -1
- package/.openclaw/extensions/agent-wallet/package.json +1 -1
- package/CHANGELOG.md +14 -0
- package/README.md +46 -23
- package/VERSION +1 -1
- package/agent-wallet/README.md +26 -1
- package/agent-wallet/UPGRADE_COMPATIBILITY.md +2 -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 +604 -375
- package/agent-wallet/agent_wallet/providers/x402.py +84 -0
- package/agent-wallet/openclaw.plugin.json +1 -1
- package/agent-wallet/pyproject.toml +1 -1
- package/agent-wallet/scripts/install_agent_wallet.py +197 -11
- package/agent-wallet/scripts/install_openclaw_local_config.py +4 -1
- package/agent-wallet/scripts/install_openclaw_sealed_keys.py +6 -2
- package/bin/lib/host-detection.mjs +236 -0
- package/bin/lib/integrations.mjs +69 -5
- package/bin/openclaw-agent-wallet.mjs +398 -28
- 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 +8 -3
- 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
|
)
|
|
@@ -3867,7 +3896,9 @@ class OpenClawWalletAdapter:
|
|
|
3867
3896
|
name="agentlayer_autonomous_status",
|
|
3868
3897
|
description=(
|
|
3869
3898
|
"Return AgentLayer high-trust autonomous permission status. "
|
|
3870
|
-
"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."
|
|
3871
3902
|
),
|
|
3872
3903
|
input_schema={
|
|
3873
3904
|
"type": "object",
|
|
@@ -3883,17 +3914,18 @@ class OpenClawWalletAdapter:
|
|
|
3883
3914
|
name="agentlayer_autonomous_approve",
|
|
3884
3915
|
description=(
|
|
3885
3916
|
"Enable the high-trust autonomous permission group. The scope parameter is kept "
|
|
3886
|
-
"for compatibility; choosing base_swaps or defi_tools enables
|
|
3887
|
-
"
|
|
3888
|
-
"
|
|
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."
|
|
3889
3921
|
),
|
|
3890
3922
|
input_schema={
|
|
3891
3923
|
"type": "object",
|
|
3892
3924
|
"properties": {
|
|
3893
3925
|
"scope": {
|
|
3894
3926
|
"type": "string",
|
|
3895
|
-
"enum": ["base_swaps", "defi_tools"],
|
|
3896
|
-
"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.",
|
|
3897
3929
|
},
|
|
3898
3930
|
"purpose": {
|
|
3899
3931
|
"type": "string",
|
|
@@ -3923,8 +3955,8 @@ class OpenClawWalletAdapter:
|
|
|
3923
3955
|
"properties": {
|
|
3924
3956
|
"scope": {
|
|
3925
3957
|
"type": "string",
|
|
3926
|
-
"enum": ["base_swaps", "defi_tools"],
|
|
3927
|
-
"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.",
|
|
3928
3960
|
}
|
|
3929
3961
|
},
|
|
3930
3962
|
"required": ["scope"],
|
|
@@ -3949,7 +3981,9 @@ class OpenClawWalletAdapter:
|
|
|
3949
3981
|
name="agentlayer_autonomous_status",
|
|
3950
3982
|
description=(
|
|
3951
3983
|
"Return AgentLayer high-trust autonomous permission status. "
|
|
3952
|
-
"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."
|
|
3953
3987
|
),
|
|
3954
3988
|
input_schema={
|
|
3955
3989
|
"type": "object",
|
|
@@ -3963,17 +3997,18 @@ class OpenClawWalletAdapter:
|
|
|
3963
3997
|
name="agentlayer_autonomous_approve",
|
|
3964
3998
|
description=(
|
|
3965
3999
|
"Enable the high-trust autonomous permission group. The scope parameter is kept "
|
|
3966
|
-
"for compatibility; choosing base_swaps or defi_tools enables
|
|
3967
|
-
"
|
|
3968
|
-
"
|
|
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."
|
|
3969
4004
|
),
|
|
3970
4005
|
input_schema={
|
|
3971
4006
|
"type": "object",
|
|
3972
4007
|
"properties": {
|
|
3973
4008
|
"scope": {
|
|
3974
4009
|
"type": "string",
|
|
3975
|
-
"enum": ["base_swaps", "defi_tools"],
|
|
3976
|
-
"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.",
|
|
3977
4012
|
},
|
|
3978
4013
|
"purpose": {
|
|
3979
4014
|
"type": "string",
|
|
@@ -4001,8 +4036,8 @@ class OpenClawWalletAdapter:
|
|
|
4001
4036
|
"properties": {
|
|
4002
4037
|
"scope": {
|
|
4003
4038
|
"type": "string",
|
|
4004
|
-
"enum": ["base_swaps", "defi_tools"],
|
|
4005
|
-
"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.",
|
|
4006
4041
|
}
|
|
4007
4042
|
},
|
|
4008
4043
|
"required": ["scope"],
|
|
@@ -4040,7 +4075,7 @@ class OpenClawWalletAdapter:
|
|
|
4040
4075
|
scope = str(args.get("scope") or "").strip()
|
|
4041
4076
|
purpose = args.get("purpose")
|
|
4042
4077
|
if scope not in autonomous_permissions.SUPPORTED_SCOPES:
|
|
4043
|
-
raise WalletBackendError("scope must be one of: base_swaps, defi_tools.")
|
|
4078
|
+
raise WalletBackendError("scope must be one of: all, base_swaps, defi_tools.")
|
|
4044
4079
|
if not isinstance(purpose, str) or not purpose.strip():
|
|
4045
4080
|
raise WalletBackendError("purpose is required.")
|
|
4046
4081
|
if args.get("user_intent") is not True:
|
|
@@ -4061,7 +4096,7 @@ class OpenClawWalletAdapter:
|
|
|
4061
4096
|
|
|
4062
4097
|
scope = str(args.get("scope") or "").strip()
|
|
4063
4098
|
if scope not in autonomous_permissions.SUPPORTED_SCOPES:
|
|
4064
|
-
raise WalletBackendError("scope must be one of: base_swaps, defi_tools.")
|
|
4099
|
+
raise WalletBackendError("scope must be one of: all, base_swaps, defi_tools.")
|
|
4065
4100
|
return AgentToolResult(
|
|
4066
4101
|
tool=tool_name,
|
|
4067
4102
|
ok=True,
|
|
@@ -4206,7 +4241,14 @@ class OpenClawWalletAdapter:
|
|
|
4206
4241
|
approved_preview = args.get("_approved_preview")
|
|
4207
4242
|
if approved_preview is not None and not isinstance(approved_preview, dict):
|
|
4208
4243
|
raise WalletBackendError("_approved_preview must be an object when provided.")
|
|
4209
|
-
|
|
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(
|
|
4210
4252
|
backend=active_backend,
|
|
4211
4253
|
url=url.strip(),
|
|
4212
4254
|
method=method,
|
|
@@ -4216,6 +4258,32 @@ class OpenClawWalletAdapter:
|
|
|
4216
4258
|
text_body=text_body,
|
|
4217
4259
|
approved_preview=approved_preview,
|
|
4218
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
|
+
)
|
|
4219
4287
|
data["purpose"] = purpose.strip()
|
|
4220
4288
|
data = self._annotate_x402_payload(data, mode="execute")
|
|
4221
4289
|
return AgentToolResult(tool=tool_name, ok=True, data=data)
|
|
@@ -5410,57 +5478,68 @@ class OpenClawWalletAdapter:
|
|
|
5410
5478
|
),
|
|
5411
5479
|
)
|
|
5412
5480
|
|
|
5413
|
-
|
|
5414
|
-
|
|
5415
|
-
|
|
5416
|
-
|
|
5417
|
-
|
|
5418
|
-
|
|
5419
|
-
approval_summary = approval_payload.get("binding", {}).get("summary")
|
|
5420
|
-
if not isinstance(approval_summary, dict):
|
|
5421
|
-
raise WalletBackendError(
|
|
5422
|
-
"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),
|
|
5423
5487
|
)
|
|
5424
|
-
|
|
5425
|
-
|
|
5426
|
-
)
|
|
5427
|
-
destination_chain_id = self._canonicalize_lifi_chain_identifier(destination_chain)
|
|
5428
|
-
expected_summary = {
|
|
5429
|
-
"operation": "EVM LI.FI cross-chain swap",
|
|
5430
|
-
"network": str(getattr(active_backend, "network", "unknown")),
|
|
5431
|
-
"source_chain": source_chain_id,
|
|
5432
|
-
"destination_chain": destination_chain_id,
|
|
5433
|
-
"token_in": self._canonicalize_lifi_token_identifier(
|
|
5434
|
-
token_in,
|
|
5435
|
-
chain_id=source_chain_id,
|
|
5436
|
-
),
|
|
5437
|
-
"output_token": self._canonicalize_lifi_token_identifier(
|
|
5438
|
-
output_token,
|
|
5439
|
-
chain_id=destination_chain_id,
|
|
5440
|
-
),
|
|
5441
|
-
"destination_address": destination_address.strip(),
|
|
5442
|
-
"input_amount_raw": amount_in_raw.strip(),
|
|
5443
|
-
}
|
|
5444
|
-
for key, expected_value in expected_summary.items():
|
|
5445
|
-
actual_value = approval_summary.get(key)
|
|
5446
|
-
if key in {"source_chain", "destination_chain"}:
|
|
5447
|
-
actual_value = self._canonicalize_lifi_chain_identifier(actual_value)
|
|
5448
|
-
if key == "token_in":
|
|
5449
|
-
actual_value = self._canonicalize_lifi_token_identifier(
|
|
5450
|
-
actual_value,
|
|
5451
|
-
chain_id=source_chain_id,
|
|
5452
|
-
)
|
|
5453
|
-
if key == "output_token":
|
|
5454
|
-
actual_value = self._canonicalize_lifi_token_identifier(
|
|
5455
|
-
actual_value,
|
|
5456
|
-
chain_id=destination_chain_id,
|
|
5457
|
-
)
|
|
5458
|
-
if actual_value != expected_value:
|
|
5488
|
+
approval_summary = approval_payload.get("binding", {}).get("summary")
|
|
5489
|
+
if not isinstance(approval_summary, dict):
|
|
5459
5490
|
raise WalletBackendError(
|
|
5460
5491
|
"approval_token does not match the requested operation. Generate a new approval after previewing the exact action."
|
|
5461
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
|
+
)
|
|
5462
5531
|
|
|
5463
|
-
|
|
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
|
+
)
|
|
5464
5543
|
self._require_execute_approval(
|
|
5465
5544
|
approval_token=approval_token,
|
|
5466
5545
|
tool_name=tool_name,
|
|
@@ -5842,46 +5921,69 @@ class OpenClawWalletAdapter:
|
|
|
5842
5921
|
),
|
|
5843
5922
|
)
|
|
5844
5923
|
|
|
5845
|
-
|
|
5846
|
-
|
|
5847
|
-
|
|
5848
|
-
|
|
5849
|
-
|
|
5850
|
-
|
|
5851
|
-
|
|
5852
|
-
if not isinstance(approval_summary, dict):
|
|
5853
|
-
raise WalletBackendError(
|
|
5854
|
-
"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),
|
|
5855
5931
|
)
|
|
5856
|
-
|
|
5857
|
-
|
|
5858
|
-
"pool_name": pool_name.strip(),
|
|
5859
|
-
"market_symbol": market_symbol.strip(),
|
|
5860
|
-
"collateral_symbol": collateral_symbol.strip(),
|
|
5861
|
-
"collateral_amount_raw": collateral_amount_raw.strip(),
|
|
5862
|
-
"leverage": leverage.strip(),
|
|
5863
|
-
"side": side,
|
|
5864
|
-
}
|
|
5865
|
-
for key, expected_value in expected_summary.items():
|
|
5866
|
-
if approval_summary.get(key) != expected_value:
|
|
5932
|
+
approval_summary = approval_payload.get("binding", {}).get("summary")
|
|
5933
|
+
if not isinstance(approval_summary, dict):
|
|
5867
5934
|
raise WalletBackendError(
|
|
5868
5935
|
"approval_token does not match the requested operation. Generate a new approval after previewing the exact action."
|
|
5869
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
|
+
)
|
|
5870
5951
|
|
|
5871
|
-
|
|
5872
|
-
|
|
5873
|
-
|
|
5874
|
-
|
|
5875
|
-
|
|
5876
|
-
|
|
5877
|
-
|
|
5878
|
-
)
|
|
5879
|
-
|
|
5880
|
-
|
|
5881
|
-
|
|
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,
|
|
5882
5972
|
)
|
|
5883
|
-
execute_preview = dict(approved_preview)
|
|
5884
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
|
+
}
|
|
5885
5987
|
execute_preview = await active_backend.preview_flash_trade_open_position(
|
|
5886
5988
|
pool_name=pool_name.strip(),
|
|
5887
5989
|
market_symbol=market_symbol.strip(),
|
|
@@ -5971,43 +6073,60 @@ class OpenClawWalletAdapter:
|
|
|
5971
6073
|
),
|
|
5972
6074
|
)
|
|
5973
6075
|
|
|
5974
|
-
|
|
5975
|
-
|
|
5976
|
-
|
|
5977
|
-
|
|
5978
|
-
|
|
5979
|
-
|
|
5980
|
-
|
|
5981
|
-
if not isinstance(approval_summary, dict):
|
|
5982
|
-
raise WalletBackendError(
|
|
5983
|
-
"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),
|
|
5984
6083
|
)
|
|
5985
|
-
|
|
5986
|
-
|
|
5987
|
-
"pool_name": pool_name.strip(),
|
|
5988
|
-
"market_symbol": market_symbol.strip(),
|
|
5989
|
-
"side": side,
|
|
5990
|
-
}
|
|
5991
|
-
for key, expected_value in expected_summary.items():
|
|
5992
|
-
if approval_summary.get(key) != expected_value:
|
|
6084
|
+
approval_summary = approval_payload.get("binding", {}).get("summary")
|
|
6085
|
+
if not isinstance(approval_summary, dict):
|
|
5993
6086
|
raise WalletBackendError(
|
|
5994
6087
|
"approval_token does not match the requested operation. Generate a new approval after previewing the exact action."
|
|
5995
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
|
+
)
|
|
5996
6100
|
|
|
5997
|
-
|
|
5998
|
-
|
|
5999
|
-
|
|
6000
|
-
|
|
6001
|
-
|
|
6002
|
-
|
|
6003
|
-
|
|
6004
|
-
)
|
|
6005
|
-
|
|
6006
|
-
|
|
6007
|
-
|
|
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,
|
|
6008
6118
|
)
|
|
6009
|
-
execute_preview = dict(approved_preview)
|
|
6010
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
|
+
}
|
|
6011
6130
|
execute_preview = await active_backend.preview_flash_trade_close_position(
|
|
6012
6131
|
pool_name=pool_name.strip(),
|
|
6013
6132
|
market_symbol=market_symbol.strip(),
|
|
@@ -6524,44 +6643,60 @@ class OpenClawWalletAdapter:
|
|
|
6524
6643
|
)
|
|
6525
6644
|
|
|
6526
6645
|
if mode == "intent_execute":
|
|
6527
|
-
|
|
6528
|
-
|
|
6529
|
-
|
|
6530
|
-
|
|
6531
|
-
|
|
6532
|
-
|
|
6533
|
-
approval_summary = approval_payload.get("binding", {}).get("summary")
|
|
6534
|
-
if not isinstance(approval_summary, dict):
|
|
6535
|
-
raise WalletBackendError(
|
|
6536
|
-
"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),
|
|
6537
6652
|
)
|
|
6538
|
-
|
|
6539
|
-
|
|
6540
|
-
"network": str(getattr(self.backend, "network", "unknown")),
|
|
6541
|
-
"kvault": kvault.strip(),
|
|
6542
|
-
"amount_ui": amount_ui.strip(),
|
|
6543
|
-
}
|
|
6544
|
-
for key, expected_value in expected_summary.items():
|
|
6545
|
-
if approval_summary.get(key) != expected_value:
|
|
6653
|
+
approval_summary = approval_payload.get("binding", {}).get("summary")
|
|
6654
|
+
if not isinstance(approval_summary, dict):
|
|
6546
6655
|
raise WalletBackendError(
|
|
6547
|
-
|
|
6656
|
+
"approval_token does not match the requested operation. Generate a new intent preview and approval before execute."
|
|
6548
6657
|
)
|
|
6549
|
-
|
|
6550
|
-
|
|
6551
|
-
|
|
6552
|
-
|
|
6553
|
-
|
|
6554
|
-
|
|
6555
|
-
|
|
6556
|
-
|
|
6557
|
-
|
|
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,
|
|
6558
6695
|
)
|
|
6559
|
-
|
|
6560
|
-
|
|
6561
|
-
|
|
6562
|
-
"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,
|
|
6563
6699
|
)
|
|
6564
|
-
approval_summary_copy = dict(approval_summary)
|
|
6565
6700
|
self._require_execute_approval(
|
|
6566
6701
|
approval_token=approval_token,
|
|
6567
6702
|
tool_name=tool_name,
|
|
@@ -6617,35 +6752,46 @@ class OpenClawWalletAdapter:
|
|
|
6617
6752
|
),
|
|
6618
6753
|
)
|
|
6619
6754
|
|
|
6620
|
-
approved_preview = args.get("_approved_preview")
|
|
6621
6755
|
execute_preview = None
|
|
6622
|
-
|
|
6623
|
-
|
|
6624
|
-
|
|
6625
|
-
|
|
6626
|
-
|
|
6627
|
-
|
|
6628
|
-
|
|
6629
|
-
if not isinstance(approval_summary, dict):
|
|
6630
|
-
raise WalletBackendError(
|
|
6631
|
-
"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),
|
|
6632
6763
|
)
|
|
6633
|
-
|
|
6634
|
-
|
|
6635
|
-
if not isinstance(approved_preview, dict):
|
|
6764
|
+
approval_summary = approval_payload.get("binding", {}).get("summary")
|
|
6765
|
+
if not isinstance(approval_summary, dict):
|
|
6636
6766
|
raise WalletBackendError(
|
|
6637
|
-
|
|
6767
|
+
"approval_token does not match the requested operation. Generate a new approval after previewing the exact action."
|
|
6638
6768
|
)
|
|
6639
|
-
|
|
6640
|
-
|
|
6641
|
-
|
|
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(),
|
|
6642
6784
|
)
|
|
6643
|
-
execute_preview = dict(approved_preview)
|
|
6644
6785
|
else:
|
|
6786
|
+
# No host token: same fallback as intent_execute above.
|
|
6645
6787
|
execute_preview = await preview_method(
|
|
6646
6788
|
kvault=kvault.strip(),
|
|
6647
6789
|
amount_ui=amount_ui.strip(),
|
|
6648
6790
|
)
|
|
6791
|
+
approval_summary_copy = self._build_confirmation_summary(
|
|
6792
|
+
action_label=action_label,
|
|
6793
|
+
payload=execute_preview,
|
|
6794
|
+
)
|
|
6649
6795
|
self._require_execute_approval(
|
|
6650
6796
|
approval_token=approval_token,
|
|
6651
6797
|
tool_name=tool_name,
|
|
@@ -6768,60 +6914,83 @@ class OpenClawWalletAdapter:
|
|
|
6768
6914
|
)
|
|
6769
6915
|
|
|
6770
6916
|
if mode == "intent_execute":
|
|
6771
|
-
|
|
6772
|
-
|
|
6773
|
-
|
|
6774
|
-
|
|
6775
|
-
|
|
6776
|
-
|
|
6777
|
-
approval_summary = approval_payload.get("binding", {}).get("summary")
|
|
6778
|
-
if not isinstance(approval_summary, dict):
|
|
6779
|
-
raise WalletBackendError(
|
|
6780
|
-
"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),
|
|
6781
6923
|
)
|
|
6782
|
-
|
|
6783
|
-
|
|
6784
|
-
"network": str(getattr(self.backend, "network", "unknown")),
|
|
6785
|
-
"market": market.strip(),
|
|
6786
|
-
"reserve": reserve.strip(),
|
|
6787
|
-
"amount_ui": amount_ui.strip(),
|
|
6788
|
-
}
|
|
6789
|
-
for key, expected_value in expected_summary.items():
|
|
6790
|
-
if approval_summary.get(key) != expected_value:
|
|
6924
|
+
approval_summary = approval_payload.get("binding", {}).get("summary")
|
|
6925
|
+
if not isinstance(approval_summary, dict):
|
|
6791
6926
|
raise WalletBackendError(
|
|
6792
|
-
|
|
6927
|
+
"approval_token does not match the requested operation. Generate a new intent preview and approval before execute."
|
|
6793
6928
|
)
|
|
6794
|
-
|
|
6795
|
-
|
|
6796
|
-
|
|
6797
|
-
|
|
6798
|
-
|
|
6799
|
-
|
|
6800
|
-
|
|
6801
|
-
|
|
6802
|
-
|
|
6803
|
-
|
|
6804
|
-
|
|
6805
|
-
|
|
6806
|
-
|
|
6807
|
-
|
|
6808
|
-
|
|
6809
|
-
|
|
6810
|
-
|
|
6811
|
-
|
|
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,
|
|
6812
6978
|
)
|
|
6813
|
-
|
|
6814
|
-
|
|
6815
|
-
|
|
6816
|
-
|
|
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,
|
|
6817
6986
|
)
|
|
6818
|
-
approval_summary_copy = dict(approval_summary)
|
|
6819
6987
|
self._require_execute_approval(
|
|
6820
6988
|
approval_token=approval_token,
|
|
6821
6989
|
tool_name=tool_name,
|
|
6822
6990
|
summary=approval_summary_copy,
|
|
6823
6991
|
action_label=intent_action_label,
|
|
6824
6992
|
)
|
|
6993
|
+
approved_obligation = approval_summary_copy.get("obligation_address")
|
|
6825
6994
|
result = await execute_method(
|
|
6826
6995
|
market=market.strip(),
|
|
6827
6996
|
reserve=reserve.strip(),
|
|
@@ -6885,37 +7054,50 @@ class OpenClawWalletAdapter:
|
|
|
6885
7054
|
),
|
|
6886
7055
|
)
|
|
6887
7056
|
|
|
6888
|
-
approved_preview = args.get("_approved_preview")
|
|
6889
7057
|
execute_preview = None
|
|
6890
|
-
|
|
6891
|
-
|
|
6892
|
-
|
|
6893
|
-
|
|
6894
|
-
|
|
6895
|
-
|
|
6896
|
-
|
|
6897
|
-
if not isinstance(approval_summary, dict):
|
|
6898
|
-
raise WalletBackendError(
|
|
6899
|
-
"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),
|
|
6900
7065
|
)
|
|
6901
|
-
|
|
6902
|
-
|
|
6903
|
-
if not isinstance(approved_preview, dict):
|
|
7066
|
+
approval_summary = approval_payload.get("binding", {}).get("summary")
|
|
7067
|
+
if not isinstance(approval_summary, dict):
|
|
6904
7068
|
raise WalletBackendError(
|
|
6905
|
-
|
|
7069
|
+
"approval_token does not match the requested operation. Generate a new approval after previewing the exact action."
|
|
6906
7070
|
)
|
|
6907
|
-
|
|
6908
|
-
|
|
6909
|
-
|
|
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,
|
|
6910
7088
|
)
|
|
6911
|
-
execute_preview = dict(approved_preview)
|
|
6912
7089
|
else:
|
|
7090
|
+
# No host token: same fallback as intent_execute above.
|
|
6913
7091
|
execute_preview = await preview_method(
|
|
6914
7092
|
market=market.strip(),
|
|
6915
7093
|
reserve=reserve.strip(),
|
|
6916
7094
|
amount_ui=amount_ui.strip(),
|
|
6917
7095
|
obligation_address=obligation_address.strip() if isinstance(obligation_address, str) and obligation_address.strip() else None,
|
|
6918
7096
|
)
|
|
7097
|
+
approval_summary_copy = self._build_confirmation_summary(
|
|
7098
|
+
action_label=action_label,
|
|
7099
|
+
payload=execute_preview,
|
|
7100
|
+
)
|
|
6919
7101
|
self._require_execute_approval(
|
|
6920
7102
|
approval_token=approval_token,
|
|
6921
7103
|
tool_name=tool_name,
|
|
@@ -7215,55 +7397,75 @@ class OpenClawWalletAdapter:
|
|
|
7215
7397
|
)
|
|
7216
7398
|
|
|
7217
7399
|
if mode == "intent_execute":
|
|
7218
|
-
|
|
7219
|
-
|
|
7220
|
-
|
|
7221
|
-
|
|
7222
|
-
|
|
7223
|
-
|
|
7224
|
-
approval_summary = approval_payload.get("binding", {}).get("summary")
|
|
7225
|
-
if not isinstance(approval_summary, dict):
|
|
7226
|
-
raise WalletBackendError(
|
|
7227
|
-
"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),
|
|
7228
7406
|
)
|
|
7229
|
-
|
|
7230
|
-
|
|
7231
|
-
|
|
7232
|
-
|
|
7233
|
-
|
|
7234
|
-
|
|
7235
|
-
|
|
7236
|
-
|
|
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):
|
|
7237
7437
|
raise WalletBackendError(
|
|
7238
7438
|
"approval_token does not match the requested swap intent. Generate a fresh intent preview and approval before execute."
|
|
7239
7439
|
)
|
|
7240
|
-
|
|
7241
|
-
|
|
7242
|
-
|
|
7243
|
-
|
|
7244
|
-
|
|
7245
|
-
|
|
7246
|
-
|
|
7247
|
-
|
|
7248
|
-
|
|
7249
|
-
|
|
7250
|
-
|
|
7251
|
-
|
|
7252
|
-
)
|
|
7253
|
-
|
|
7254
|
-
|
|
7255
|
-
|
|
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,
|
|
7256
7464
|
)
|
|
7257
|
-
|
|
7258
|
-
|
|
7259
|
-
|
|
7465
|
+
approval_summary_copy = self._build_confirmation_summary(
|
|
7466
|
+
action_label="Swap intent",
|
|
7467
|
+
payload=intent_preview,
|
|
7260
7468
|
)
|
|
7261
|
-
if approval_summary.get("recipient_policy") != "owner-only":
|
|
7262
|
-
raise WalletBackendError("approved swap intent recipient policy is invalid.")
|
|
7263
|
-
if approval_summary.get("spend_policy") != "exact-input":
|
|
7264
|
-
raise WalletBackendError("approved swap intent spend policy is invalid.")
|
|
7265
|
-
|
|
7266
|
-
approval_summary_copy = dict(approval_summary)
|
|
7267
7469
|
self._require_execute_approval(
|
|
7268
7470
|
approval_token=approval_token,
|
|
7269
7471
|
tool_name=tool_name,
|
|
@@ -7311,59 +7513,75 @@ class OpenClawWalletAdapter:
|
|
|
7311
7513
|
),
|
|
7312
7514
|
)
|
|
7313
7515
|
|
|
7314
|
-
|
|
7315
|
-
|
|
7316
|
-
|
|
7317
|
-
|
|
7318
|
-
|
|
7319
|
-
|
|
7320
|
-
approval_summary = approval_payload.get("binding", {}).get("summary")
|
|
7321
|
-
if not isinstance(approval_summary, dict):
|
|
7322
|
-
raise WalletBackendError(
|
|
7323
|
-
"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),
|
|
7324
7522
|
)
|
|
7325
|
-
|
|
7326
|
-
|
|
7327
|
-
"network": str(getattr(self.backend, "network", "unknown")),
|
|
7328
|
-
"input_mint": input_mint.strip(),
|
|
7329
|
-
"output_mint": output_mint.strip(),
|
|
7330
|
-
"slippage_bps": slippage_bps,
|
|
7331
|
-
}
|
|
7332
|
-
for key, expected_value in expected_summary.items():
|
|
7333
|
-
if approval_summary.get(key) != expected_value:
|
|
7523
|
+
approval_summary = approval_payload.get("binding", {}).get("summary")
|
|
7524
|
+
if not isinstance(approval_summary, dict):
|
|
7334
7525
|
raise WalletBackendError(
|
|
7335
7526
|
"approval_token does not match the requested operation. Generate a new approval after previewing the exact action."
|
|
7336
7527
|
)
|
|
7337
|
-
|
|
7338
|
-
|
|
7339
|
-
|
|
7340
|
-
|
|
7341
|
-
"
|
|
7342
|
-
|
|
7343
|
-
|
|
7344
|
-
|
|
7345
|
-
|
|
7346
|
-
|
|
7347
|
-
|
|
7348
|
-
|
|
7349
|
-
|
|
7350
|
-
|
|
7351
|
-
|
|
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):
|
|
7352
7543
|
raise WalletBackendError(
|
|
7353
|
-
"
|
|
7544
|
+
"approval_token does not match the requested operation. Generate a new approval after previewing the exact action."
|
|
7354
7545
|
)
|
|
7355
|
-
if
|
|
7546
|
+
if approved_amount != float(amount):
|
|
7356
7547
|
raise WalletBackendError(
|
|
7357
|
-
"
|
|
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,
|
|
7358
7569
|
)
|
|
7359
|
-
execute_preview = dict(approved_preview)
|
|
7360
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.
|
|
7361
7575
|
execute_preview = await self.backend.preview_swap(
|
|
7362
7576
|
input_mint=input_mint.strip(),
|
|
7363
7577
|
output_mint=output_mint.strip(),
|
|
7364
7578
|
amount_ui=float(amount),
|
|
7365
7579
|
slippage_bps=slippage_bps,
|
|
7366
7580
|
)
|
|
7581
|
+
approval_summary_copy = self._build_confirmation_summary(
|
|
7582
|
+
action_label="Swap",
|
|
7583
|
+
payload=execute_preview,
|
|
7584
|
+
)
|
|
7367
7585
|
self._require_execute_approval(
|
|
7368
7586
|
approval_token=approval_token,
|
|
7369
7587
|
tool_name=tool_name,
|
|
@@ -7454,54 +7672,65 @@ class OpenClawWalletAdapter:
|
|
|
7454
7672
|
),
|
|
7455
7673
|
)
|
|
7456
7674
|
|
|
7457
|
-
|
|
7458
|
-
|
|
7459
|
-
|
|
7460
|
-
|
|
7461
|
-
|
|
7462
|
-
|
|
7463
|
-
approval_summary = approval_payload.get("binding", {}).get("summary")
|
|
7464
|
-
if not isinstance(approval_summary, dict):
|
|
7465
|
-
raise WalletBackendError(
|
|
7466
|
-
"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(),
|
|
7467
7681
|
)
|
|
7468
|
-
|
|
7469
|
-
|
|
7470
|
-
"operation": "Solana LI.FI cross-chain swap",
|
|
7471
|
-
"network": str(getattr(self.backend, "network", "unknown")),
|
|
7472
|
-
"source_chain": "solana",
|
|
7473
|
-
"destination_chain": destination_chain_id,
|
|
7474
|
-
"input_token": self._canonicalize_lifi_token_identifier(
|
|
7475
|
-
input_token,
|
|
7476
|
-
chain_id="1151111081099710",
|
|
7477
|
-
),
|
|
7478
|
-
"output_token": self._canonicalize_lifi_token_identifier(
|
|
7479
|
-
output_token,
|
|
7480
|
-
chain_id=destination_chain_id,
|
|
7481
|
-
),
|
|
7482
|
-
"destination_address": destination_address.strip(),
|
|
7483
|
-
"input_amount_raw": amount_in_raw.strip(),
|
|
7484
|
-
}
|
|
7485
|
-
for key, expected_value in expected_summary.items():
|
|
7486
|
-
actual_value = approval_summary.get(key)
|
|
7487
|
-
if key == "destination_chain":
|
|
7488
|
-
actual_value = self._canonicalize_lifi_chain_identifier(actual_value)
|
|
7489
|
-
if key == "input_token":
|
|
7490
|
-
actual_value = self._canonicalize_lifi_token_identifier(
|
|
7491
|
-
actual_value,
|
|
7492
|
-
chain_id="1151111081099710",
|
|
7493
|
-
)
|
|
7494
|
-
if key == "output_token":
|
|
7495
|
-
actual_value = self._canonicalize_lifi_token_identifier(
|
|
7496
|
-
actual_value,
|
|
7497
|
-
chain_id=destination_chain_id,
|
|
7498
|
-
)
|
|
7499
|
-
if actual_value != expected_value:
|
|
7682
|
+
approval_summary = approval_payload.get("binding", {}).get("summary")
|
|
7683
|
+
if not isinstance(approval_summary, dict):
|
|
7500
7684
|
raise WalletBackendError(
|
|
7501
7685
|
"approval_token does not match the requested operation. Generate a new approval after previewing the exact action."
|
|
7502
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
|
+
)
|
|
7503
7722
|
|
|
7504
|
-
|
|
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
|
+
)
|
|
7505
7734
|
self._require_execute_approval(
|
|
7506
7735
|
approval_token=approval_token,
|
|
7507
7736
|
tool_name=tool_name,
|