@agentlayer.tech/wallet 0.1.14 → 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.
Files changed (29) hide show
  1. package/.openclaw/extensions/agent-wallet/README.md +20 -0
  2. package/.openclaw/extensions/agent-wallet/dist/index.js +2079 -0
  3. package/.openclaw/extensions/agent-wallet/index.ts +125 -2
  4. package/.openclaw/extensions/agent-wallet/openclaw.plugin.json +4 -0
  5. package/.openclaw/extensions/agent-wallet/package.json +44 -5
  6. package/.openclaw/extensions/pay-bridge/README.md +6 -0
  7. package/.openclaw/extensions/pay-bridge/dist/core.mjs +287 -0
  8. package/.openclaw/extensions/pay-bridge/dist/index.js +196 -0
  9. package/.openclaw/extensions/pay-bridge/package.json +43 -5
  10. package/CHANGELOG.md +33 -0
  11. package/README.md +32 -0
  12. package/RELEASING.md +167 -0
  13. package/agent-wallet/.env.example +5 -0
  14. package/agent-wallet/README.md +38 -0
  15. package/agent-wallet/agent_wallet/config.py +4 -0
  16. package/agent-wallet/agent_wallet/openclaw_adapter.py +504 -0
  17. package/agent-wallet/agent_wallet/providers/flash.py +186 -0
  18. package/agent-wallet/agent_wallet/providers/flash_sdk_bridge.py +251 -0
  19. package/agent-wallet/agent_wallet/transaction_policy.py +79 -0
  20. package/agent-wallet/agent_wallet/wallet_layer/base.py +78 -0
  21. package/agent-wallet/agent_wallet/wallet_layer/solana.py +623 -1
  22. package/agent-wallet/pyproject.toml +1 -1
  23. package/agent-wallet/scripts/flash-sdk-bridge/README.md +33 -0
  24. package/agent-wallet/scripts/flash-sdk-bridge/bridge.mjs +1179 -0
  25. package/agent-wallet/scripts/flash-sdk-bridge/package-lock.json +2377 -0
  26. package/agent-wallet/scripts/flash-sdk-bridge/package.json +12 -0
  27. package/agent-wallet/scripts/install_agent_wallet.py +46 -11
  28. package/agent-wallet/scripts/install_openclaw_local_config.py +4 -0
  29. package/package.json +4 -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)