@agentlayer.tech/wallet 0.1.15 → 0.1.16
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 +117 -1
- package/.openclaw/extensions/agent-wallet/index.ts +117 -1
- package/.openclaw/extensions/agent-wallet/openclaw.plugin.json +4 -0
- package/.openclaw/extensions/agent-wallet/package.json +1 -1
- package/.openclaw/extensions/pay-bridge/package.json +1 -1
- package/CHANGELOG.md +14 -0
- package/RELEASING.md +97 -0
- package/agent-wallet/.env.example +5 -0
- package/agent-wallet/README.md +24 -0
- package/agent-wallet/agent_wallet/config.py +4 -0
- package/agent-wallet/agent_wallet/openclaw_adapter.py +504 -0
- package/agent-wallet/agent_wallet/providers/flash.py +186 -0
- package/agent-wallet/agent_wallet/providers/flash_sdk_bridge.py +251 -0
- package/agent-wallet/agent_wallet/transaction_policy.py +79 -0
- package/agent-wallet/agent_wallet/wallet_layer/base.py +78 -0
- package/agent-wallet/agent_wallet/wallet_layer/solana.py +623 -1
- package/agent-wallet/pyproject.toml +1 -1
- package/agent-wallet/scripts/flash-sdk-bridge/README.md +33 -0
- package/agent-wallet/scripts/flash-sdk-bridge/bridge.mjs +1179 -0
- package/agent-wallet/scripts/flash-sdk-bridge/package-lock.json +2377 -0
- package/agent-wallet/scripts/flash-sdk-bridge/package.json +12 -0
- package/agent-wallet/scripts/install_agent_wallet.py +46 -11
- package/agent-wallet/scripts/install_openclaw_local_config.py +4 -0
- package/package.json +2 -1
|
@@ -483,6 +483,45 @@ class OpenClawWalletAdapter:
|
|
|
483
483
|
"claim_fingerprint": claim_fingerprint,
|
|
484
484
|
}
|
|
485
485
|
|
|
486
|
+
if asset_type in {"flash-trade-open-position", "flash-trade-close-position"}:
|
|
487
|
+
flash_binding = {
|
|
488
|
+
"pool_name": payload.get("pool_name"),
|
|
489
|
+
"market_symbol": payload.get("market_symbol"),
|
|
490
|
+
"collateral_symbol": payload.get("collateral_symbol"),
|
|
491
|
+
"collateral_amount_raw": payload.get("collateral_amount_raw"),
|
|
492
|
+
"leverage": payload.get("leverage"),
|
|
493
|
+
"side": payload.get("side"),
|
|
494
|
+
"estimated_size_usd": payload.get("estimated_size_usd"),
|
|
495
|
+
"estimated_entry_price": payload.get("estimated_entry_price"),
|
|
496
|
+
"estimated_liquidation_price": payload.get("estimated_liquidation_price"),
|
|
497
|
+
"position_size_usd": payload.get("position_size_usd"),
|
|
498
|
+
"close_amount_raw": payload.get("close_amount_raw"),
|
|
499
|
+
}
|
|
500
|
+
flash_fingerprint = hashlib.sha256(
|
|
501
|
+
json.dumps(
|
|
502
|
+
flash_binding,
|
|
503
|
+
sort_keys=True,
|
|
504
|
+
separators=(",", ":"),
|
|
505
|
+
).encode("utf-8")
|
|
506
|
+
).hexdigest()
|
|
507
|
+
return {
|
|
508
|
+
"operation": action_label,
|
|
509
|
+
"network": str(payload.get("network") or getattr(self.backend, "network", "unknown")),
|
|
510
|
+
"owner": payload.get("owner"),
|
|
511
|
+
"pool_name": payload.get("pool_name"),
|
|
512
|
+
"market_symbol": payload.get("market_symbol"),
|
|
513
|
+
"collateral_symbol": payload.get("collateral_symbol"),
|
|
514
|
+
"collateral_amount_raw": payload.get("collateral_amount_raw"),
|
|
515
|
+
"leverage": payload.get("leverage"),
|
|
516
|
+
"side": payload.get("side"),
|
|
517
|
+
"estimated_size_usd": payload.get("estimated_size_usd"),
|
|
518
|
+
"estimated_entry_price": payload.get("estimated_entry_price"),
|
|
519
|
+
"estimated_liquidation_price": payload.get("estimated_liquidation_price"),
|
|
520
|
+
"position_size_usd": payload.get("position_size_usd"),
|
|
521
|
+
"close_amount_raw": payload.get("close_amount_raw"),
|
|
522
|
+
"flash_preview_fingerprint": flash_fingerprint,
|
|
523
|
+
}
|
|
524
|
+
|
|
486
525
|
if asset_type == "btc-transfer":
|
|
487
526
|
btc_binding = {
|
|
488
527
|
"recipient": payload.get("recipient"),
|
|
@@ -1878,6 +1917,161 @@ class OpenClawWalletAdapter:
|
|
|
1878
1917
|
read_only=True,
|
|
1879
1918
|
risk_level="low",
|
|
1880
1919
|
),
|
|
1920
|
+
AgentToolSpec(
|
|
1921
|
+
name="get_flash_trade_markets",
|
|
1922
|
+
description="List Flash Trade perpetual markets currently available on Solana mainnet.",
|
|
1923
|
+
input_schema={
|
|
1924
|
+
"type": "object",
|
|
1925
|
+
"properties": {
|
|
1926
|
+
"pool_name": {
|
|
1927
|
+
"type": "string",
|
|
1928
|
+
"description": "Optional Flash pool identifier such as Crypto.1.",
|
|
1929
|
+
}
|
|
1930
|
+
},
|
|
1931
|
+
"additionalProperties": False,
|
|
1932
|
+
},
|
|
1933
|
+
read_only=True,
|
|
1934
|
+
risk_level="low",
|
|
1935
|
+
),
|
|
1936
|
+
AgentToolSpec(
|
|
1937
|
+
name="get_flash_trade_positions",
|
|
1938
|
+
description="Get Flash Trade perpetual positions for a Solana wallet on mainnet.",
|
|
1939
|
+
input_schema={
|
|
1940
|
+
"type": "object",
|
|
1941
|
+
"properties": {
|
|
1942
|
+
"owner": {
|
|
1943
|
+
"type": "string",
|
|
1944
|
+
"description": "Optional Solana wallet address override. If omitted, use the configured wallet.",
|
|
1945
|
+
},
|
|
1946
|
+
"pool_name": {
|
|
1947
|
+
"type": "string",
|
|
1948
|
+
"description": "Optional Flash pool identifier such as Crypto.1.",
|
|
1949
|
+
},
|
|
1950
|
+
},
|
|
1951
|
+
"additionalProperties": False,
|
|
1952
|
+
},
|
|
1953
|
+
read_only=True,
|
|
1954
|
+
risk_level="low",
|
|
1955
|
+
),
|
|
1956
|
+
AgentToolSpec(
|
|
1957
|
+
name="flash_trade_open_position",
|
|
1958
|
+
description=(
|
|
1959
|
+
"Preview, prepare, or execute a Flash Trade same-collateral perpetual open on Solana mainnet."
|
|
1960
|
+
),
|
|
1961
|
+
input_schema={
|
|
1962
|
+
"type": "object",
|
|
1963
|
+
"properties": {
|
|
1964
|
+
"pool_name": {
|
|
1965
|
+
"type": "string",
|
|
1966
|
+
"description": "Flash pool identifier such as Crypto.1.",
|
|
1967
|
+
},
|
|
1968
|
+
"market_symbol": {
|
|
1969
|
+
"type": "string",
|
|
1970
|
+
"description": "Flash market symbol such as SOL or BTC.",
|
|
1971
|
+
},
|
|
1972
|
+
"collateral_symbol": {
|
|
1973
|
+
"type": "string",
|
|
1974
|
+
"description": "Collateral symbol. Phase 2 requires the same symbol as market_symbol.",
|
|
1975
|
+
},
|
|
1976
|
+
"collateral_amount_raw": {
|
|
1977
|
+
"type": "string",
|
|
1978
|
+
"description": "Collateral amount in raw token units.",
|
|
1979
|
+
},
|
|
1980
|
+
"leverage": {
|
|
1981
|
+
"type": "string",
|
|
1982
|
+
"description": "Requested leverage as a decimal string such as 5 or 7.5.",
|
|
1983
|
+
},
|
|
1984
|
+
"side": {
|
|
1985
|
+
"type": "string",
|
|
1986
|
+
"enum": ["long", "short"],
|
|
1987
|
+
"description": "Position direction.",
|
|
1988
|
+
},
|
|
1989
|
+
"mode": {
|
|
1990
|
+
"type": "string",
|
|
1991
|
+
"enum": ["preview", "prepare", "execute"],
|
|
1992
|
+
"description": "preview returns trade details; prepare returns an execution plan; execute broadcasts after host approval.",
|
|
1993
|
+
},
|
|
1994
|
+
"purpose": {
|
|
1995
|
+
"type": "string",
|
|
1996
|
+
"description": "Short explanation of why the position should be opened.",
|
|
1997
|
+
},
|
|
1998
|
+
"user_intent": {
|
|
1999
|
+
"type": "boolean",
|
|
2000
|
+
"description": "Must be true for prepare mode.",
|
|
2001
|
+
},
|
|
2002
|
+
"approval_token": {
|
|
2003
|
+
"type": "string",
|
|
2004
|
+
"description": "Host-issued approval token required for execute mode.",
|
|
2005
|
+
},
|
|
2006
|
+
},
|
|
2007
|
+
"required": [
|
|
2008
|
+
"pool_name",
|
|
2009
|
+
"market_symbol",
|
|
2010
|
+
"collateral_symbol",
|
|
2011
|
+
"collateral_amount_raw",
|
|
2012
|
+
"leverage",
|
|
2013
|
+
"side",
|
|
2014
|
+
"mode",
|
|
2015
|
+
"purpose",
|
|
2016
|
+
],
|
|
2017
|
+
"additionalProperties": False,
|
|
2018
|
+
},
|
|
2019
|
+
read_only=False,
|
|
2020
|
+
requires_explicit_user_intent=True,
|
|
2021
|
+
risk_level="high",
|
|
2022
|
+
),
|
|
2023
|
+
AgentToolSpec(
|
|
2024
|
+
name="flash_trade_close_position",
|
|
2025
|
+
description=(
|
|
2026
|
+
"Preview, prepare, or execute a Flash Trade same-collateral perpetual close on Solana mainnet."
|
|
2027
|
+
),
|
|
2028
|
+
input_schema={
|
|
2029
|
+
"type": "object",
|
|
2030
|
+
"properties": {
|
|
2031
|
+
"pool_name": {
|
|
2032
|
+
"type": "string",
|
|
2033
|
+
"description": "Flash pool identifier such as Crypto.1.",
|
|
2034
|
+
},
|
|
2035
|
+
"market_symbol": {
|
|
2036
|
+
"type": "string",
|
|
2037
|
+
"description": "Flash market symbol such as SOL or BTC.",
|
|
2038
|
+
},
|
|
2039
|
+
"side": {
|
|
2040
|
+
"type": "string",
|
|
2041
|
+
"enum": ["long", "short"],
|
|
2042
|
+
"description": "Position direction to close.",
|
|
2043
|
+
},
|
|
2044
|
+
"mode": {
|
|
2045
|
+
"type": "string",
|
|
2046
|
+
"enum": ["preview", "prepare", "execute"],
|
|
2047
|
+
"description": "preview returns close details; prepare returns an execution plan; execute broadcasts after host approval.",
|
|
2048
|
+
},
|
|
2049
|
+
"purpose": {
|
|
2050
|
+
"type": "string",
|
|
2051
|
+
"description": "Short explanation of why the position should be closed.",
|
|
2052
|
+
},
|
|
2053
|
+
"user_intent": {
|
|
2054
|
+
"type": "boolean",
|
|
2055
|
+
"description": "Must be true for prepare mode.",
|
|
2056
|
+
},
|
|
2057
|
+
"approval_token": {
|
|
2058
|
+
"type": "string",
|
|
2059
|
+
"description": "Host-issued approval token required for execute mode.",
|
|
2060
|
+
},
|
|
2061
|
+
},
|
|
2062
|
+
"required": [
|
|
2063
|
+
"pool_name",
|
|
2064
|
+
"market_symbol",
|
|
2065
|
+
"side",
|
|
2066
|
+
"mode",
|
|
2067
|
+
"purpose",
|
|
2068
|
+
],
|
|
2069
|
+
"additionalProperties": False,
|
|
2070
|
+
},
|
|
2071
|
+
read_only=False,
|
|
2072
|
+
requires_explicit_user_intent=True,
|
|
2073
|
+
risk_level="high",
|
|
2074
|
+
),
|
|
1881
2075
|
AgentToolSpec(
|
|
1882
2076
|
name="get_kamino_lend_markets",
|
|
1883
2077
|
description="List Kamino lending markets currently available on Solana mainnet.",
|
|
@@ -3813,6 +4007,316 @@ class OpenClawWalletAdapter:
|
|
|
3813
4007
|
)
|
|
3814
4008
|
return AgentToolResult(tool=tool_name, ok=True, data=data)
|
|
3815
4009
|
|
|
4010
|
+
if tool_name == "get_flash_trade_markets":
|
|
4011
|
+
pool_name = args.get("pool_name")
|
|
4012
|
+
if pool_name is not None and not isinstance(pool_name, str):
|
|
4013
|
+
raise WalletBackendError("pool_name must be a string when provided.")
|
|
4014
|
+
data = await active_backend.get_flash_trade_markets(pool_name=pool_name)
|
|
4015
|
+
return AgentToolResult(tool=tool_name, ok=True, data=data)
|
|
4016
|
+
|
|
4017
|
+
if tool_name == "get_flash_trade_positions":
|
|
4018
|
+
owner = args.get("owner")
|
|
4019
|
+
pool_name = args.get("pool_name")
|
|
4020
|
+
if owner is not None and not isinstance(owner, str):
|
|
4021
|
+
raise WalletBackendError("owner must be a string when provided.")
|
|
4022
|
+
if pool_name is not None and not isinstance(pool_name, str):
|
|
4023
|
+
raise WalletBackendError("pool_name must be a string when provided.")
|
|
4024
|
+
data = await active_backend.get_flash_trade_positions(
|
|
4025
|
+
owner=owner,
|
|
4026
|
+
pool_name=pool_name,
|
|
4027
|
+
)
|
|
4028
|
+
return AgentToolResult(tool=tool_name, ok=True, data=data)
|
|
4029
|
+
|
|
4030
|
+
if tool_name == "flash_trade_open_position":
|
|
4031
|
+
pool_name = args.get("pool_name")
|
|
4032
|
+
market_symbol = args.get("market_symbol")
|
|
4033
|
+
collateral_symbol = args.get("collateral_symbol")
|
|
4034
|
+
collateral_amount_raw = args.get("collateral_amount_raw")
|
|
4035
|
+
leverage = args.get("leverage")
|
|
4036
|
+
side = args.get("side")
|
|
4037
|
+
mode = args.get("mode")
|
|
4038
|
+
purpose = args.get("purpose")
|
|
4039
|
+
user_intent = args.get("user_intent", False)
|
|
4040
|
+
approval_token = args.get("approval_token")
|
|
4041
|
+
|
|
4042
|
+
if not isinstance(pool_name, str) or not pool_name.strip():
|
|
4043
|
+
raise WalletBackendError("pool_name is required.")
|
|
4044
|
+
if not isinstance(market_symbol, str) or not market_symbol.strip():
|
|
4045
|
+
raise WalletBackendError("market_symbol is required.")
|
|
4046
|
+
if not isinstance(collateral_symbol, str) or not collateral_symbol.strip():
|
|
4047
|
+
raise WalletBackendError("collateral_symbol is required.")
|
|
4048
|
+
if not isinstance(collateral_amount_raw, str) or not collateral_amount_raw.strip():
|
|
4049
|
+
raise WalletBackendError("collateral_amount_raw is required.")
|
|
4050
|
+
if not isinstance(leverage, str) or not leverage.strip():
|
|
4051
|
+
raise WalletBackendError("leverage is required.")
|
|
4052
|
+
if mode not in {"preview", "prepare", "execute"}:
|
|
4053
|
+
raise WalletBackendError("mode must be 'preview', 'prepare' or 'execute'.")
|
|
4054
|
+
if not isinstance(purpose, str) or not purpose.strip():
|
|
4055
|
+
raise WalletBackendError("purpose is required.")
|
|
4056
|
+
|
|
4057
|
+
if mode == "preview":
|
|
4058
|
+
preview = await active_backend.preview_flash_trade_open_position(
|
|
4059
|
+
pool_name=pool_name.strip(),
|
|
4060
|
+
market_symbol=market_symbol.strip(),
|
|
4061
|
+
collateral_symbol=collateral_symbol.strip(),
|
|
4062
|
+
collateral_amount_raw=collateral_amount_raw.strip(),
|
|
4063
|
+
leverage=leverage.strip(),
|
|
4064
|
+
side=side,
|
|
4065
|
+
)
|
|
4066
|
+
return AgentToolResult(
|
|
4067
|
+
tool=tool_name,
|
|
4068
|
+
ok=True,
|
|
4069
|
+
data=self._annotate_sensitive_payload(
|
|
4070
|
+
preview,
|
|
4071
|
+
action_label="Flash Trade open position",
|
|
4072
|
+
mode="preview",
|
|
4073
|
+
),
|
|
4074
|
+
)
|
|
4075
|
+
|
|
4076
|
+
if mode == "prepare":
|
|
4077
|
+
self._require_prepare_intent(user_intent)
|
|
4078
|
+
prepared = await active_backend.prepare_flash_trade_open_position(
|
|
4079
|
+
pool_name=pool_name.strip(),
|
|
4080
|
+
market_symbol=market_symbol.strip(),
|
|
4081
|
+
collateral_symbol=collateral_symbol.strip(),
|
|
4082
|
+
collateral_amount_raw=collateral_amount_raw.strip(),
|
|
4083
|
+
leverage=leverage.strip(),
|
|
4084
|
+
side=side,
|
|
4085
|
+
)
|
|
4086
|
+
return AgentToolResult(
|
|
4087
|
+
tool=tool_name,
|
|
4088
|
+
ok=True,
|
|
4089
|
+
data=self._annotate_sensitive_payload(
|
|
4090
|
+
self._build_prepare_plan(
|
|
4091
|
+
preview_payload=prepared,
|
|
4092
|
+
action_label="Flash Trade open position",
|
|
4093
|
+
),
|
|
4094
|
+
action_label="Flash Trade open position",
|
|
4095
|
+
mode="prepare",
|
|
4096
|
+
),
|
|
4097
|
+
)
|
|
4098
|
+
|
|
4099
|
+
approval_payload = inspect_approval_token(
|
|
4100
|
+
approval_token,
|
|
4101
|
+
tool_name=tool_name,
|
|
4102
|
+
network=str(getattr(active_backend, "network", "unknown")),
|
|
4103
|
+
require_mainnet_confirmation=self._is_mainnet_for_backend(active_backend),
|
|
4104
|
+
)
|
|
4105
|
+
approval_summary = approval_payload.get("binding", {}).get("summary")
|
|
4106
|
+
if not isinstance(approval_summary, dict):
|
|
4107
|
+
raise WalletBackendError(
|
|
4108
|
+
"approval_token does not match the requested operation. Generate a new approval after previewing the exact action."
|
|
4109
|
+
)
|
|
4110
|
+
expected_summary = {
|
|
4111
|
+
"operation": "Flash Trade open position",
|
|
4112
|
+
"pool_name": pool_name.strip(),
|
|
4113
|
+
"market_symbol": market_symbol.strip(),
|
|
4114
|
+
"collateral_symbol": collateral_symbol.strip(),
|
|
4115
|
+
"collateral_amount_raw": collateral_amount_raw.strip(),
|
|
4116
|
+
"leverage": leverage.strip(),
|
|
4117
|
+
"side": side,
|
|
4118
|
+
}
|
|
4119
|
+
for key, expected_value in expected_summary.items():
|
|
4120
|
+
if approval_summary.get(key) != expected_value:
|
|
4121
|
+
raise WalletBackendError(
|
|
4122
|
+
"approval_token does not match the requested operation. Generate a new approval after previewing the exact action."
|
|
4123
|
+
)
|
|
4124
|
+
|
|
4125
|
+
approval_summary_copy = dict(approval_summary)
|
|
4126
|
+
approved_preview = args.get("_approved_preview")
|
|
4127
|
+
execute_preview = None
|
|
4128
|
+
if isinstance(approval_summary_copy.get("_preview_digest"), str):
|
|
4129
|
+
if not isinstance(approved_preview, dict):
|
|
4130
|
+
raise WalletBackendError(
|
|
4131
|
+
"Approved Flash Trade preview payload is required for execute mode. Generate a new preview and approval before execute."
|
|
4132
|
+
)
|
|
4133
|
+
if preview_payload_digest(approved_preview) != approval_summary_copy["_preview_digest"]:
|
|
4134
|
+
raise WalletBackendError(
|
|
4135
|
+
"approved preview payload does not match the approval token. Generate a new preview and approval before execute."
|
|
4136
|
+
)
|
|
4137
|
+
preview_summary = self._build_confirmation_summary(
|
|
4138
|
+
action_label="Flash Trade open position",
|
|
4139
|
+
payload=approved_preview,
|
|
4140
|
+
)
|
|
4141
|
+
summary_without_digest = {
|
|
4142
|
+
key: value
|
|
4143
|
+
for key, value in approval_summary_copy.items()
|
|
4144
|
+
if key != "_preview_digest"
|
|
4145
|
+
}
|
|
4146
|
+
if preview_summary != summary_without_digest:
|
|
4147
|
+
raise WalletBackendError(
|
|
4148
|
+
"approved preview payload does not match the approval token. Generate a new preview and approval before execute."
|
|
4149
|
+
)
|
|
4150
|
+
execute_preview = dict(approved_preview)
|
|
4151
|
+
else:
|
|
4152
|
+
execute_preview = await active_backend.preview_flash_trade_open_position(
|
|
4153
|
+
pool_name=pool_name.strip(),
|
|
4154
|
+
market_symbol=market_symbol.strip(),
|
|
4155
|
+
collateral_symbol=collateral_symbol.strip(),
|
|
4156
|
+
collateral_amount_raw=collateral_amount_raw.strip(),
|
|
4157
|
+
leverage=leverage.strip(),
|
|
4158
|
+
side=side,
|
|
4159
|
+
)
|
|
4160
|
+
|
|
4161
|
+
self._require_execute_approval(
|
|
4162
|
+
approval_token=approval_token,
|
|
4163
|
+
tool_name=tool_name,
|
|
4164
|
+
summary=approval_summary_copy,
|
|
4165
|
+
action_label="Flash Trade open position",
|
|
4166
|
+
backend=active_backend,
|
|
4167
|
+
)
|
|
4168
|
+
result = await active_backend.execute_flash_trade_open_position(
|
|
4169
|
+
pool_name=pool_name.strip(),
|
|
4170
|
+
market_symbol=market_symbol.strip(),
|
|
4171
|
+
collateral_symbol=collateral_symbol.strip(),
|
|
4172
|
+
collateral_amount_raw=collateral_amount_raw.strip(),
|
|
4173
|
+
leverage=leverage.strip(),
|
|
4174
|
+
side=side,
|
|
4175
|
+
approved_preview=execute_preview,
|
|
4176
|
+
)
|
|
4177
|
+
return AgentToolResult(
|
|
4178
|
+
tool=tool_name,
|
|
4179
|
+
ok=True,
|
|
4180
|
+
data=self._annotate_sensitive_payload(
|
|
4181
|
+
result,
|
|
4182
|
+
action_label="Flash Trade open position",
|
|
4183
|
+
mode="execute",
|
|
4184
|
+
),
|
|
4185
|
+
)
|
|
4186
|
+
|
|
4187
|
+
if tool_name == "flash_trade_close_position":
|
|
4188
|
+
pool_name = args.get("pool_name")
|
|
4189
|
+
market_symbol = args.get("market_symbol")
|
|
4190
|
+
side = args.get("side")
|
|
4191
|
+
mode = args.get("mode")
|
|
4192
|
+
purpose = args.get("purpose")
|
|
4193
|
+
user_intent = args.get("user_intent", False)
|
|
4194
|
+
approval_token = args.get("approval_token")
|
|
4195
|
+
|
|
4196
|
+
if not isinstance(pool_name, str) or not pool_name.strip():
|
|
4197
|
+
raise WalletBackendError("pool_name is required.")
|
|
4198
|
+
if not isinstance(market_symbol, str) or not market_symbol.strip():
|
|
4199
|
+
raise WalletBackendError("market_symbol is required.")
|
|
4200
|
+
if mode not in {"preview", "prepare", "execute"}:
|
|
4201
|
+
raise WalletBackendError("mode must be 'preview', 'prepare' or 'execute'.")
|
|
4202
|
+
if not isinstance(purpose, str) or not purpose.strip():
|
|
4203
|
+
raise WalletBackendError("purpose is required.")
|
|
4204
|
+
|
|
4205
|
+
if mode == "preview":
|
|
4206
|
+
preview = await active_backend.preview_flash_trade_close_position(
|
|
4207
|
+
pool_name=pool_name.strip(),
|
|
4208
|
+
market_symbol=market_symbol.strip(),
|
|
4209
|
+
side=side,
|
|
4210
|
+
)
|
|
4211
|
+
return AgentToolResult(
|
|
4212
|
+
tool=tool_name,
|
|
4213
|
+
ok=True,
|
|
4214
|
+
data=self._annotate_sensitive_payload(
|
|
4215
|
+
preview,
|
|
4216
|
+
action_label="Flash Trade close position",
|
|
4217
|
+
mode="preview",
|
|
4218
|
+
),
|
|
4219
|
+
)
|
|
4220
|
+
|
|
4221
|
+
if mode == "prepare":
|
|
4222
|
+
self._require_prepare_intent(user_intent)
|
|
4223
|
+
prepared = await active_backend.prepare_flash_trade_close_position(
|
|
4224
|
+
pool_name=pool_name.strip(),
|
|
4225
|
+
market_symbol=market_symbol.strip(),
|
|
4226
|
+
side=side,
|
|
4227
|
+
)
|
|
4228
|
+
return AgentToolResult(
|
|
4229
|
+
tool=tool_name,
|
|
4230
|
+
ok=True,
|
|
4231
|
+
data=self._annotate_sensitive_payload(
|
|
4232
|
+
self._build_prepare_plan(
|
|
4233
|
+
preview_payload=prepared,
|
|
4234
|
+
action_label="Flash Trade close position",
|
|
4235
|
+
),
|
|
4236
|
+
action_label="Flash Trade close position",
|
|
4237
|
+
mode="prepare",
|
|
4238
|
+
),
|
|
4239
|
+
)
|
|
4240
|
+
|
|
4241
|
+
approval_payload = inspect_approval_token(
|
|
4242
|
+
approval_token,
|
|
4243
|
+
tool_name=tool_name,
|
|
4244
|
+
network=str(getattr(active_backend, "network", "unknown")),
|
|
4245
|
+
require_mainnet_confirmation=self._is_mainnet_for_backend(active_backend),
|
|
4246
|
+
)
|
|
4247
|
+
approval_summary = approval_payload.get("binding", {}).get("summary")
|
|
4248
|
+
if not isinstance(approval_summary, dict):
|
|
4249
|
+
raise WalletBackendError(
|
|
4250
|
+
"approval_token does not match the requested operation. Generate a new approval after previewing the exact action."
|
|
4251
|
+
)
|
|
4252
|
+
expected_summary = {
|
|
4253
|
+
"operation": "Flash Trade close position",
|
|
4254
|
+
"pool_name": pool_name.strip(),
|
|
4255
|
+
"market_symbol": market_symbol.strip(),
|
|
4256
|
+
"side": side,
|
|
4257
|
+
}
|
|
4258
|
+
for key, expected_value in expected_summary.items():
|
|
4259
|
+
if approval_summary.get(key) != expected_value:
|
|
4260
|
+
raise WalletBackendError(
|
|
4261
|
+
"approval_token does not match the requested operation. Generate a new approval after previewing the exact action."
|
|
4262
|
+
)
|
|
4263
|
+
|
|
4264
|
+
approval_summary_copy = dict(approval_summary)
|
|
4265
|
+
approved_preview = args.get("_approved_preview")
|
|
4266
|
+
execute_preview = None
|
|
4267
|
+
if isinstance(approval_summary_copy.get("_preview_digest"), str):
|
|
4268
|
+
if not isinstance(approved_preview, dict):
|
|
4269
|
+
raise WalletBackendError(
|
|
4270
|
+
"Approved Flash Trade preview payload is required for execute mode. Generate a new preview and approval before execute."
|
|
4271
|
+
)
|
|
4272
|
+
if preview_payload_digest(approved_preview) != approval_summary_copy["_preview_digest"]:
|
|
4273
|
+
raise WalletBackendError(
|
|
4274
|
+
"approved preview payload does not match the approval token. Generate a new preview and approval before execute."
|
|
4275
|
+
)
|
|
4276
|
+
preview_summary = self._build_confirmation_summary(
|
|
4277
|
+
action_label="Flash Trade close position",
|
|
4278
|
+
payload=approved_preview,
|
|
4279
|
+
)
|
|
4280
|
+
summary_without_digest = {
|
|
4281
|
+
key: value
|
|
4282
|
+
for key, value in approval_summary_copy.items()
|
|
4283
|
+
if key != "_preview_digest"
|
|
4284
|
+
}
|
|
4285
|
+
if preview_summary != summary_without_digest:
|
|
4286
|
+
raise WalletBackendError(
|
|
4287
|
+
"approved preview payload does not match the approval token. Generate a new preview and approval before execute."
|
|
4288
|
+
)
|
|
4289
|
+
execute_preview = dict(approved_preview)
|
|
4290
|
+
else:
|
|
4291
|
+
execute_preview = await active_backend.preview_flash_trade_close_position(
|
|
4292
|
+
pool_name=pool_name.strip(),
|
|
4293
|
+
market_symbol=market_symbol.strip(),
|
|
4294
|
+
side=side,
|
|
4295
|
+
)
|
|
4296
|
+
|
|
4297
|
+
self._require_execute_approval(
|
|
4298
|
+
approval_token=approval_token,
|
|
4299
|
+
tool_name=tool_name,
|
|
4300
|
+
summary=approval_summary_copy,
|
|
4301
|
+
action_label="Flash Trade close position",
|
|
4302
|
+
backend=active_backend,
|
|
4303
|
+
)
|
|
4304
|
+
result = await active_backend.execute_flash_trade_close_position(
|
|
4305
|
+
pool_name=pool_name.strip(),
|
|
4306
|
+
market_symbol=market_symbol.strip(),
|
|
4307
|
+
side=side,
|
|
4308
|
+
approved_preview=execute_preview,
|
|
4309
|
+
)
|
|
4310
|
+
return AgentToolResult(
|
|
4311
|
+
tool=tool_name,
|
|
4312
|
+
ok=True,
|
|
4313
|
+
data=self._annotate_sensitive_payload(
|
|
4314
|
+
result,
|
|
4315
|
+
action_label="Flash Trade close position",
|
|
4316
|
+
mode="execute",
|
|
4317
|
+
),
|
|
4318
|
+
)
|
|
4319
|
+
|
|
3816
4320
|
if tool_name == "get_kamino_lend_markets":
|
|
3817
4321
|
data = await self.backend.get_kamino_lend_markets()
|
|
3818
4322
|
return AgentToolResult(tool=tool_name, ok=True, data=data)
|