cryptochief-crypto-processing-python 0.2.0__tar.gz → 0.4.0__tar.gz

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 (43) hide show
  1. {cryptochief_crypto_processing_python-0.2.0 → cryptochief_crypto_processing_python-0.4.0}/PKG-INFO +35 -2
  2. {cryptochief_crypto_processing_python-0.2.0 → cryptochief_crypto_processing_python-0.4.0}/README.md +34 -1
  3. {cryptochief_crypto_processing_python-0.2.0 → cryptochief_crypto_processing_python-0.4.0}/src/cryptochief/__init__.py +18 -0
  4. {cryptochief_crypto_processing_python-0.2.0 → cryptochief_crypto_processing_python-0.4.0}/src/cryptochief/_version.py +1 -1
  5. cryptochief_crypto_processing_python-0.4.0/src/cryptochief/sentinels.py +38 -0
  6. {cryptochief_crypto_processing_python-0.2.0 → cryptochief_crypto_processing_python-0.4.0}/src/cryptochief/services/payins.py +32 -0
  7. cryptochief_crypto_processing_python-0.4.0/src/cryptochief/services/sweeps.py +282 -0
  8. cryptochief_crypto_processing_python-0.2.0/src/cryptochief/services/sweeps.py +0 -85
  9. {cryptochief_crypto_processing_python-0.2.0 → cryptochief_crypto_processing_python-0.4.0}/.gitignore +0 -0
  10. {cryptochief_crypto_processing_python-0.2.0 → cryptochief_crypto_processing_python-0.4.0}/LICENSE +0 -0
  11. {cryptochief_crypto_processing_python-0.2.0 → cryptochief_crypto_processing_python-0.4.0}/pyproject.toml +0 -0
  12. {cryptochief_crypto_processing_python-0.2.0 → cryptochief_crypto_processing_python-0.4.0}/src/cryptochief/_models.py +0 -0
  13. {cryptochief_crypto_processing_python-0.2.0 → cryptochief_crypto_processing_python-0.4.0}/src/cryptochief/amount.py +0 -0
  14. {cryptochief_crypto_processing_python-0.2.0 → cryptochief_crypto_processing_python-0.4.0}/src/cryptochief/assets.py +0 -0
  15. {cryptochief_crypto_processing_python-0.2.0 → cryptochief_crypto_processing_python-0.4.0}/src/cryptochief/chains.py +0 -0
  16. {cryptochief_crypto_processing_python-0.2.0 → cryptochief_crypto_processing_python-0.4.0}/src/cryptochief/client.py +0 -0
  17. {cryptochief_crypto_processing_python-0.2.0 → cryptochief_crypto_processing_python-0.4.0}/src/cryptochief/contract/__init__.py +0 -0
  18. {cryptochief_crypto_processing_python-0.2.0 → cryptochief_crypto_processing_python-0.4.0}/src/cryptochief/contract/base58.py +0 -0
  19. {cryptochief_crypto_processing_python-0.2.0 → cryptochief_crypto_processing_python-0.4.0}/src/cryptochief/contract/borsh.py +0 -0
  20. {cryptochief_crypto_processing_python-0.2.0 → cryptochief_crypto_processing_python-0.4.0}/src/cryptochief/contract/evm_abi.py +0 -0
  21. {cryptochief_crypto_processing_python-0.2.0 → cryptochief_crypto_processing_python-0.4.0}/src/cryptochief/contract/keccak.py +0 -0
  22. {cryptochief_crypto_processing_python-0.2.0 → cryptochief_crypto_processing_python-0.4.0}/src/cryptochief/contract/tron_address.py +0 -0
  23. {cryptochief_crypto_processing_python-0.2.0 → cryptochief_crypto_processing_python-0.4.0}/src/cryptochief/errors.py +0 -0
  24. {cryptochief_crypto_processing_python-0.2.0 → cryptochief_crypto_processing_python-0.4.0}/src/cryptochief/pagination.py +0 -0
  25. {cryptochief_crypto_processing_python-0.2.0 → cryptochief_crypto_processing_python-0.4.0}/src/cryptochief/poll.py +0 -0
  26. {cryptochief_crypto_processing_python-0.2.0 → cryptochief_crypto_processing_python-0.4.0}/src/cryptochief/rsa.py +0 -0
  27. {cryptochief_crypto_processing_python-0.2.0 → cryptochief_crypto_processing_python-0.4.0}/src/cryptochief/services/__init__.py +0 -0
  28. {cryptochief_crypto_processing_python-0.2.0 → cryptochief_crypto_processing_python-0.4.0}/src/cryptochief/services/base.py +0 -0
  29. {cryptochief_crypto_processing_python-0.2.0 → cryptochief_crypto_processing_python-0.4.0}/src/cryptochief/services/blockchain.py +0 -0
  30. {cryptochief_crypto_processing_python-0.2.0 → cryptochief_crypto_processing_python-0.4.0}/src/cryptochief/services/credits.py +0 -0
  31. {cryptochief_crypto_processing_python-0.2.0 → cryptochief_crypto_processing_python-0.4.0}/src/cryptochief/services/currencies.py +0 -0
  32. {cryptochief_crypto_processing_python-0.2.0 → cryptochief_crypto_processing_python-0.4.0}/src/cryptochief/services/payouts.py +0 -0
  33. {cryptochief_crypto_processing_python-0.2.0 → cryptochief_crypto_processing_python-0.4.0}/src/cryptochief/services/static_deposits.py +0 -0
  34. {cryptochief_crypto_processing_python-0.2.0 → cryptochief_crypto_processing_python-0.4.0}/src/cryptochief/services/transactions.py +0 -0
  35. {cryptochief_crypto_processing_python-0.2.0 → cryptochief_crypto_processing_python-0.4.0}/src/cryptochief/services/wallets.py +0 -0
  36. {cryptochief_crypto_processing_python-0.2.0 → cryptochief_crypto_processing_python-0.4.0}/src/cryptochief/services/withdrawals.py +0 -0
  37. {cryptochief_crypto_processing_python-0.2.0 → cryptochief_crypto_processing_python-0.4.0}/src/cryptochief/sign.py +0 -0
  38. {cryptochief_crypto_processing_python-0.2.0 → cryptochief_crypto_processing_python-0.4.0}/src/cryptochief/ton/__init__.py +0 -0
  39. {cryptochief_crypto_processing_python-0.2.0 → cryptochief_crypto_processing_python-0.4.0}/src/cryptochief/ton/address.py +0 -0
  40. {cryptochief_crypto_processing_python-0.2.0 → cryptochief_crypto_processing_python-0.4.0}/src/cryptochief/ton/messages.py +0 -0
  41. {cryptochief_crypto_processing_python-0.2.0 → cryptochief_crypto_processing_python-0.4.0}/src/cryptochief/ton/rpc.py +0 -0
  42. {cryptochief_crypto_processing_python-0.2.0 → cryptochief_crypto_processing_python-0.4.0}/src/cryptochief/transport.py +0 -0
  43. {cryptochief_crypto_processing_python-0.2.0 → cryptochief_crypto_processing_python-0.4.0}/src/cryptochief/webhook.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: cryptochief-crypto-processing-python
3
- Version: 0.2.0
3
+ Version: 0.4.0
4
4
  Summary: Official async Python SDK for the Crypto Chief crypto payment gateway and crypto processing API. Accept crypto payments, send single and mass crypto payouts, sign on-chain transactions and smart-contract calls, manage wallets, convert fiat to crypto, and verify webhooks across Ethereum, BNB Smart Chain, Polygon, Tron, TON, Solana, Bitcoin, XRP and 20+ blockchains. USDT and USDC stablecoin support with int-precise amounts and asyncio/httpx.
5
5
  Project-URL: Homepage, https://crypto-chief.com/processing/
6
6
  Project-URL: Documentation, https://docs-sdk.crypto-chief.com/processing/python
@@ -121,7 +121,7 @@ Both credentials come from the Dashboard -> Project.
121
121
  | TON contract calls (Jetton / NFT / text) | `client.transactions` | `jetton_transfer`, `nft_transfer`, `send_ton_comment`, `sign_ton_call` |
122
122
  | Accept incoming payments | `client.pay_ins` | `create`, `select_asset`, `reset_asset`, `cancel`, `info`, `history`, `wait_for` |
123
123
  | Wallet management + RSA decrypt | `client.wallets` | `generate`, `list`, `info`, `freeze`, `decrypt_private_key` |
124
- | Treasury sweeps | `client.sweeps` | `force`, `history`, `wallet_history` |
124
+ | Treasury sweeps | `client.sweeps` | `force`, `history`, `wallet_history`, `settings`, `update_settings` |
125
125
  | Withdrawals (read-only) | `client.withdrawals` | `info`, `history` |
126
126
  | Static-deposit history | `client.static_deposits` | `info`, `history` |
127
127
  | On-chain queries | `client.blockchain` | `contracts_available`, `wallet_balance`, `transaction_status` |
@@ -335,6 +335,39 @@ priv = client.wallets.decrypt_private_key(wallet.private_key_encrypted)
335
335
  - **How do I do a crypto swap?** A swap is a payout with `auto_convert=True`.
336
336
  - **How do I call a smart contract?** `client.transactions.sign_evm_call` /
337
337
  `sign_anchor_call` / `jetton_transfer`, then `transactions.execute`.
338
+ - **How do I control when a deposit wallet is swept?**
339
+ `client.sweeps.settings(...)` reads the policy in force for one wallet and
340
+ `client.sweeps.update_settings(...)` changes it - sweep on arrival
341
+ (`SweepPolicyMode.MOMENTUM`), sweep once the balance reaches an amount
342
+ (`SweepPolicyMode.THRESHOLD` plus `threshold_amount_usd`), or never on its own
343
+ (`SweepPolicyMode.OFF`, force still works). The read comes back in three
344
+ layers - what will happen, what this wallet overrides, and what it inherits
345
+ from the project - so a value of your own is distinguishable from an inherited
346
+ one:
347
+
348
+ ```python
349
+ s = await client.sweeps.update_settings(
350
+ deposit_address,
351
+ type_work=SweepPolicyMode.THRESHOLD,
352
+ threshold_amount_usd="250",
353
+ )
354
+ # s.effective is the resolved policy; s.effective.source names the layer it came from.
355
+ ```
356
+
357
+ Inheritance is per field: overriding the mode leaves the fee mode inherited.
358
+ To stop overriding a field, pass `CLEAR` - `None` already means "leave this
359
+ field alone", so it cannot also mean "reset it".
360
+ - **How do I know a sweep actually settled?** Check `status`.
361
+ `SweepStatus.BROADCASTED` means the transaction is out and not yet confirmed;
362
+ `SweepStatus.COMPLETED` means confirmed, with `sweep_confirmations` and
363
+ `completed_at` filled in. Earlier platform versions reported `completed` at
364
+ broadcast, so a sweep could read as settled while its transaction was still
365
+ unconfirmed.
366
+ - **How do I keep test payments off real chains?** Set `environment` on
367
+ `CreatePayInRequest` to `Environment.TESTNET` or `Environment.MAINNET`. It
368
+ constrains the asset the platform picks when you have not named a concrete
369
+ network - fiat mode and `ANY` - so an unconstrained pick cannot put a real
370
+ payment on a test chain. Omit it to use the project's default.
338
371
 
339
372
  ## Documentation
340
373
 
@@ -82,7 +82,7 @@ Both credentials come from the Dashboard -> Project.
82
82
  | TON contract calls (Jetton / NFT / text) | `client.transactions` | `jetton_transfer`, `nft_transfer`, `send_ton_comment`, `sign_ton_call` |
83
83
  | Accept incoming payments | `client.pay_ins` | `create`, `select_asset`, `reset_asset`, `cancel`, `info`, `history`, `wait_for` |
84
84
  | Wallet management + RSA decrypt | `client.wallets` | `generate`, `list`, `info`, `freeze`, `decrypt_private_key` |
85
- | Treasury sweeps | `client.sweeps` | `force`, `history`, `wallet_history` |
85
+ | Treasury sweeps | `client.sweeps` | `force`, `history`, `wallet_history`, `settings`, `update_settings` |
86
86
  | Withdrawals (read-only) | `client.withdrawals` | `info`, `history` |
87
87
  | Static-deposit history | `client.static_deposits` | `info`, `history` |
88
88
  | On-chain queries | `client.blockchain` | `contracts_available`, `wallet_balance`, `transaction_status` |
@@ -296,6 +296,39 @@ priv = client.wallets.decrypt_private_key(wallet.private_key_encrypted)
296
296
  - **How do I do a crypto swap?** A swap is a payout with `auto_convert=True`.
297
297
  - **How do I call a smart contract?** `client.transactions.sign_evm_call` /
298
298
  `sign_anchor_call` / `jetton_transfer`, then `transactions.execute`.
299
+ - **How do I control when a deposit wallet is swept?**
300
+ `client.sweeps.settings(...)` reads the policy in force for one wallet and
301
+ `client.sweeps.update_settings(...)` changes it - sweep on arrival
302
+ (`SweepPolicyMode.MOMENTUM`), sweep once the balance reaches an amount
303
+ (`SweepPolicyMode.THRESHOLD` plus `threshold_amount_usd`), or never on its own
304
+ (`SweepPolicyMode.OFF`, force still works). The read comes back in three
305
+ layers - what will happen, what this wallet overrides, and what it inherits
306
+ from the project - so a value of your own is distinguishable from an inherited
307
+ one:
308
+
309
+ ```python
310
+ s = await client.sweeps.update_settings(
311
+ deposit_address,
312
+ type_work=SweepPolicyMode.THRESHOLD,
313
+ threshold_amount_usd="250",
314
+ )
315
+ # s.effective is the resolved policy; s.effective.source names the layer it came from.
316
+ ```
317
+
318
+ Inheritance is per field: overriding the mode leaves the fee mode inherited.
319
+ To stop overriding a field, pass `CLEAR` - `None` already means "leave this
320
+ field alone", so it cannot also mean "reset it".
321
+ - **How do I know a sweep actually settled?** Check `status`.
322
+ `SweepStatus.BROADCASTED` means the transaction is out and not yet confirmed;
323
+ `SweepStatus.COMPLETED` means confirmed, with `sweep_confirmations` and
324
+ `completed_at` filled in. Earlier platform versions reported `completed` at
325
+ broadcast, so a sweep could read as settled while its transaction was still
326
+ unconfirmed.
327
+ - **How do I keep test payments off real chains?** Set `environment` on
328
+ `CreatePayInRequest` to `Environment.TESTNET` or `Environment.MAINNET`. It
329
+ constrains the asset the platform picks when you have not named a concrete
330
+ network - fiat mode and `ANY` - so an unconstrained pick cannot put a real
331
+ payment on a test chain. Omit it to use the project's default.
299
332
 
300
333
  ## Documentation
301
334
 
@@ -80,6 +80,7 @@ from .services.currencies import ConvertRequest, ConvertResponse, CurrenciesServ
80
80
  from .services.payins import (
81
81
  CoinOption,
82
82
  CreatePayInRequest,
83
+ Environment,
83
84
  PayIn,
84
85
  PayInHistoryResponse,
85
86
  PayInMode,
@@ -110,13 +111,20 @@ from .services.static_deposits import (
110
111
  StaticDepositsService,
111
112
  StaticDepositStatus,
112
113
  )
114
+ from .sentinels import CLEAR, Clear
113
115
  from .services.sweeps import (
114
116
  ForceSweepResponse,
115
117
  Sweep,
118
+ SweepFeeMode,
116
119
  SweepHistoryQuery,
117
120
  SweepHistoryResponse,
118
121
  SweepMode,
122
+ SweepOverride,
123
+ SweepPolicy,
124
+ SweepPolicyMode,
125
+ SweepSettings,
119
126
  SweepsService,
127
+ SweepStatus,
120
128
  )
121
129
  from .services.transactions import (
122
130
  AnchorCallRequest,
@@ -171,6 +179,10 @@ from .webhook import (
171
179
 
172
180
  __all__ = [
173
181
  "__version__",
182
+ # Sentinels
183
+ "CLEAR",
184
+ "Clear",
185
+ "Environment",
174
186
  # Client
175
187
  "CryptoChiefClient",
176
188
  "VERSION",
@@ -278,10 +290,16 @@ __all__ = [
278
290
  "WalletType",
279
291
  # Sweep types
280
292
  "Sweep",
293
+ "SweepFeeMode",
281
294
  "SweepHistoryQuery",
282
295
  "SweepHistoryResponse",
283
296
  "ForceSweepResponse",
284
297
  "SweepMode",
298
+ "SweepOverride",
299
+ "SweepPolicy",
300
+ "SweepPolicyMode",
301
+ "SweepSettings",
302
+ "SweepStatus",
285
303
  # Withdrawal types
286
304
  "Withdrawal",
287
305
  "WithdrawalHistoryResponse",
@@ -1,3 +1,3 @@
1
1
  """Single source of truth for the package version."""
2
2
 
3
- __version__ = "0.2.0"
3
+ __version__ = "0.4.0"
@@ -0,0 +1,38 @@
1
+ """Sentinels for values that ``None`` cannot express.
2
+
3
+ Python has one "absent" value and some APIs need two. Where an argument
4
+ distinguishes "not supplied" from "supplied as nothing", ``None`` takes the
5
+ first meaning and a sentinel from this module takes the second.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+
11
+ class Clear:
12
+ """Stop overriding a field and go back to inheriting it.
13
+
14
+ Used with :meth:`cryptochief.SweepsService.update_settings`, where the API
15
+ expresses "inherit this again" by naming a field and sending no value for
16
+ it. ``None`` already means "leave this field alone", so it cannot also mean
17
+ "reset it".
18
+
19
+ Use the :data:`CLEAR` singleton rather than constructing this.
20
+ """
21
+
22
+ _instance: "Clear | None" = None
23
+
24
+ def __new__(cls) -> "Clear":
25
+ if cls._instance is None:
26
+ cls._instance = super().__new__(cls)
27
+ return cls._instance
28
+
29
+ def __repr__(self) -> str:
30
+ return "CLEAR"
31
+
32
+ def __bool__(self) -> bool:
33
+ # Truthy: `if value:` on a CLEAR must not read as "nothing was passed".
34
+ return True
35
+
36
+
37
+ #: The singleton :class:`Clear`.
38
+ CLEAR = Clear()
@@ -38,12 +38,40 @@ def is_payin_terminal(status: str) -> bool:
38
38
  return status in _PAYIN_TERMINAL
39
39
 
40
40
 
41
+ class Environment(str, Enum):
42
+ """The two environments an order can belong to.
43
+
44
+ A project may be allowed one or both; asking for testnet on a project that
45
+ does not permit it is refused with ``TESTNET_NOT_ALLOWED`` rather than
46
+ quietly served on mainnet, and a value that is neither is
47
+ ``ENVIRONMENT_INVALID`` rather than a silent fallback.
48
+ """
49
+
50
+ MAINNET = "mainnet"
51
+ TESTNET = "testnet"
52
+
53
+
41
54
  @dataclass(kw_only=True)
42
55
  class CreatePayInRequest:
43
56
  order_id: str
44
57
  user_id: str
45
58
  mode: str
46
59
  to_address: Optional[str] = None
60
+ #: Pin the transit deposit wallet of THIS order to the given master wallet of
61
+ #: the project - the address the funds are swept to. The order's
62
+ #: asset/network chain family must match the master wallet's; a foreign or
63
+ #: mismatched address is rejected with 400. Omit for the project-default
64
+ #: behaviour.
65
+ master_wallet_address: Optional[str] = None
66
+ #: Constrain the asset the platform PICKS for this order to the real chains
67
+ #: or the test ones - ``Environment.MAINNET`` or ``Environment.TESTNET``.
68
+ #: Omit to use the project's own default.
69
+ #:
70
+ #: It changes nothing when ``asset`` names a concrete network - that is the
71
+ #: caller's choice. It matters in fiat mode and when the network is ``ANY``,
72
+ #: where the platform selects the asset and an unconstrained pick could put
73
+ #: a real payment on a test network.
74
+ environment: Optional[str] = None
47
75
  lifetime_sec: Optional[int] = None
48
76
  url_callback: Optional[str] = None
49
77
  url_success: Optional[str] = None
@@ -105,6 +133,10 @@ class SelectAssetRequest:
105
133
  uuid: str
106
134
  coin: str
107
135
  network: str
136
+ #: Pin the order's transit deposit wallet to the given project master
137
+ #: wallet; see :class:`CreatePayInRequest`. A value here overrides one
138
+ #: supplied at order create.
139
+ master_wallet_address: Optional[str] = None
108
140
 
109
141
 
110
142
  class PayInsService(BaseService):
@@ -0,0 +1,282 @@
1
+ """Treasury sweeps (transit -> master)."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from dataclasses import dataclass
6
+ from enum import Enum
7
+ from typing import Any, List, Optional, Union
8
+
9
+ from .._models import from_dict
10
+ from ..sentinels import Clear
11
+ from ..pagination import HistoryMeta
12
+ from .base import BaseService
13
+
14
+
15
+ class SweepMode(str, Enum):
16
+ AUTO = "auto"
17
+ FORCE = "force"
18
+
19
+
20
+ @dataclass(kw_only=True)
21
+ class SweepHistoryQuery:
22
+ mode: Optional[str] = None
23
+ page: Optional[int] = None
24
+ page_size: Optional[int] = None
25
+
26
+
27
+ class SweepStatus(str, Enum):
28
+ """A sweep is broadcast first and confirmed after.
29
+
30
+ ``BROADCASTED`` means the transaction is out and not yet confirmed;
31
+ ``COMPLETED`` means the chain confirmed it. The platform used to report
32
+ ``completed`` at broadcast, so a sweep could read as settled while its
33
+ transaction was still unconfirmed or had been dropped.
34
+
35
+ ``SKIPPED`` is a sweep the platform decided against - almost always a
36
+ balance below the wallet's threshold. A normal outcome, not a failure.
37
+ """
38
+
39
+ PENDING = "pending"
40
+ WAITING_GAS = "waiting_gas"
41
+ BROADCASTED = "broadcasted"
42
+ COMPLETED = "completed"
43
+ FAILED = "failed"
44
+ SKIPPED = "skipped"
45
+
46
+
47
+ class SweepPolicyMode(str, Enum):
48
+ """Auto-sweep modes.
49
+
50
+ ``OFF`` is never swept on its own (:meth:`SweepsService.force` still works),
51
+ ``MOMENTUM`` sweeps as soon as funds arrive, and ``THRESHOLD`` sweeps once
52
+ the balance reaches ``threshold_amount_usd``. A held balance is re-checked
53
+ periodically, so a wallet that crosses the threshold through price movement
54
+ alone is still swept.
55
+ """
56
+
57
+ OFF = "turned_off"
58
+ MOMENTUM = "momentum"
59
+ THRESHOLD = "threshold"
60
+
61
+
62
+ class SweepFeeMode(str, Enum):
63
+ """Who pays the gas for a sweep.
64
+
65
+ ``CLIENT`` takes it from the swept wallet, ``SERVICE`` from the platform's
66
+ service wallet, and ``MIX`` funds the gas from the service wallet and
67
+ reclaims the cost from the sweep.
68
+ """
69
+
70
+ CLIENT = "client"
71
+ SERVICE = "service"
72
+ MIX = "mix"
73
+
74
+
75
+ @dataclass(kw_only=True)
76
+ class Sweep:
77
+ task_id: str = ""
78
+ status: str = ""
79
+ sweep_tx_hash: Optional[str] = None
80
+ gas_pump_tx_hash: Optional[str] = None
81
+ wallet_address: Optional[str] = None
82
+ chain: Optional[str] = None
83
+ chain_family: Optional[str] = None
84
+ asset_symbol: Optional[str] = None
85
+ asset_type: Optional[str] = None
86
+ amount_human: Optional[str] = None
87
+ #: What triggered this sweep: momentum, threshold or force.
88
+ type_work: Optional[str] = None
89
+
90
+ #: Confirmations seen on the sweep transaction, and when it reached the
91
+ #: network's confirmation target. Read them with ``status``:
92
+ #: ``completed_at`` is absent while the sweep is still in flight.
93
+ sweep_confirmations: Optional[int] = None
94
+ completed_at: Optional[str] = None
95
+
96
+ #: Fees. ``total_fee_usd`` is the whole cost of the sweep; the gas-pump half
97
+ #: is the funding transfer that pays for it on chains needing one. The
98
+ #: ``real_*`` figures are what the chain actually charged, filled in once the
99
+ #: transaction settles; the others are the estimate made up front.
100
+ total_fee_usd: Optional[str] = None
101
+ gas_pump_source: Optional[str] = None
102
+ gas_pump_fee_human: Optional[str] = None
103
+ gas_pump_fee_usd: Optional[str] = None
104
+ sweep_fee_human: Optional[str] = None
105
+ sweep_fee_usd: Optional[str] = None
106
+ real_gas_pump_fee_human: Optional[str] = None
107
+ real_gas_pump_fee_usd: Optional[str] = None
108
+ real_sweep_fee_human: Optional[str] = None
109
+ real_sweep_fee_usd: Optional[str] = None
110
+
111
+ created_at: Optional[str] = None
112
+
113
+ #: Deprecated: never populated. The API reports fees under the names above;
114
+ #: these were guesses at a shape it does not send.
115
+ gas_fee_human: Optional[str] = None
116
+ gas_fee_fiat: Optional[str] = None
117
+ service_fee_fiat: Optional[str] = None
118
+ #: Deprecated: never populated - sweeps carry ``created_at`` and
119
+ #: ``completed_at``.
120
+ updated_at: Optional[str] = None
121
+
122
+
123
+ @dataclass(kw_only=True)
124
+ class SweepPolicy:
125
+ """A resolved set of sweep rules."""
126
+
127
+ type_work: str = ""
128
+ #: Meaningful only when ``type_work`` is ``threshold``.
129
+ threshold_amount_usd: Optional[str] = None
130
+ fee_mode: str = ""
131
+ #: Which layer the mode came from: ``wallet_network``, ``wallet``,
132
+ #: ``project`` or ``default``. Present on the effective policy, where the
133
+ #: question arises.
134
+ source: Optional[str] = None
135
+
136
+
137
+ @dataclass(kw_only=True)
138
+ class SweepOverride:
139
+ """What one wallet decides for itself.
140
+
141
+ A field of ``None`` is not overridden - it is inherited, which no ordinary
142
+ value can express.
143
+ """
144
+
145
+ #: Empty covers the address on every network it exists on; set, it covers
146
+ #: that one network and takes precedence over the address-wide override.
147
+ network_code: Optional[str] = None
148
+ type_work: Optional[str] = None
149
+ threshold_amount_usd: Optional[str] = None
150
+ fee_mode: Optional[str] = None
151
+ #: Who wrote it: ``merchant`` or ``operator``.
152
+ source: Optional[str] = None
153
+ #: An operator pinned this policy. While it is set, a merchant write answers
154
+ #: ``SWEEP_SETTINGS_LOCKED`` and changes nothing.
155
+ locked: bool = False
156
+
157
+
158
+ @dataclass(kw_only=True)
159
+ class SweepSettings:
160
+ """Three layers, on purpose.
161
+
162
+ ``effective`` is what will actually happen, ``override`` is what this wallet
163
+ decides for itself (``None`` if it decides nothing), and ``project_default``
164
+ is what it falls back to. Only the three together answer "is this value mine
165
+ or inherited" - the difference between changing it here and changing it on
166
+ the project. Inheritance is per field: a wallet can override the mode and
167
+ keep inheriting the fee mode.
168
+ """
169
+
170
+ wallet_address: Optional[str] = None
171
+ network_code: Optional[str] = None
172
+ effective: Optional[SweepPolicy] = None
173
+ override: Optional[SweepOverride] = None
174
+ project_default: Optional[SweepPolicy] = None
175
+
176
+
177
+ @dataclass(kw_only=True)
178
+ class SweepHistoryResponse:
179
+ items: Optional[List[Sweep]] = None
180
+ meta: Optional[HistoryMeta] = None
181
+
182
+
183
+ @dataclass(kw_only=True)
184
+ class ForceSweepResponse:
185
+ status: str = ""
186
+
187
+
188
+ class SweepsService(BaseService):
189
+ async def force(self, address: str, network: str) -> ForceSweepResponse:
190
+ """Trigger an immediate transit->master sweep for one address.
191
+
192
+ The status acknowledges acceptance; the resulting :class:`Sweep` record
193
+ appears via :meth:`wallet_history` once the on-chain tx is built.
194
+ """
195
+ return from_dict(
196
+ ForceSweepResponse,
197
+ await self._post("/v1/sweeps/force", {"address": address, "network_code": network}),
198
+ )
199
+
200
+ async def history(self, query: Optional[SweepHistoryQuery] = None) -> SweepHistoryResponse:
201
+ """Recent sweeps across the whole project."""
202
+ return from_dict(
203
+ SweepHistoryResponse, await self._post("/v1/sweeps/history", query or SweepHistoryQuery())
204
+ )
205
+
206
+ async def wallet_history(
207
+ self, address: str, query: Optional[SweepHistoryQuery] = None
208
+ ) -> SweepHistoryResponse:
209
+ """Recent sweeps scoped to one wallet."""
210
+ body: dict[str, Any] = {"address": address}
211
+ if query is not None:
212
+ if query.mode is not None:
213
+ body["mode"] = query.mode
214
+ if query.page is not None:
215
+ body["page"] = query.page
216
+ if query.page_size is not None:
217
+ body["page_size"] = query.page_size
218
+ return from_dict(SweepHistoryResponse, await self._post("/v1/sweeps/wallet/history", body))
219
+
220
+ async def settings(
221
+ self, address: Optional[str] = None, network_code: Optional[str] = None
222
+ ) -> SweepSettings:
223
+ """The auto-sweep policy in force for one wallet.
224
+
225
+ Returns what will happen, what the wallet overrides, and what it
226
+ inherits. Omitting ``address`` asks for the project's own default rather
227
+ than any wallet's policy.
228
+
229
+ Scoped to the caller's own wallets: an address that is not the project's
230
+ answers ``WALLET_NOT_FOUND``.
231
+ """
232
+ body: dict[str, Any] = {}
233
+ if address:
234
+ body["address"] = address
235
+ if network_code:
236
+ body["network_code"] = network_code
237
+ return from_dict(SweepSettings, await self._post("/v1/sweeps/settings", body))
238
+
239
+ async def update_settings(
240
+ self,
241
+ address: str,
242
+ *,
243
+ network_code: Optional[str] = None,
244
+ type_work: Union[str, Clear, None] = None,
245
+ threshold_amount_usd: Union[str, Clear, None] = None,
246
+ fee_mode: Union[str, Clear, None] = None,
247
+ ) -> SweepSettings:
248
+ """Write a wallet's auto-sweep policy.
249
+
250
+ Returns the settings as they stand afterwards, so the caller sees what
251
+ the write resolved to without asking again.
252
+
253
+ ``None`` leaves a field alone. :data:`~cryptochief.CLEAR` stops
254
+ overriding it and goes back to inheriting - the only way to drop one
255
+ field while keeping the others. The API expresses that by naming the
256
+ field with no value, which ``None`` cannot say in Python because it
257
+ already means "not supplied".
258
+
259
+ Refusals are named: ``TYPE_WORK_INVALID``, ``FEE_MODE_INVALID``,
260
+ ``THRESHOLD_INVALID``, ``THRESHOLD_MUST_BE_POSITIVE``,
261
+ ``THRESHOLD_REQUIRED_FOR_THRESHOLD_MODE``, and
262
+ ``SWEEP_SETTINGS_LOCKED`` when an operator has pinned the policy.
263
+ """
264
+ body: dict[str, Any] = {"address": address}
265
+ if network_code:
266
+ body["network_code"] = network_code
267
+
268
+ fields: List[str] = []
269
+ for name, value in (
270
+ ("type_work", type_work),
271
+ ("threshold_amount_usd", threshold_amount_usd),
272
+ ("fee_mode", fee_mode),
273
+ ):
274
+ if value is None:
275
+ continue
276
+ fields.append(name)
277
+ if not isinstance(value, Clear):
278
+ body[name] = value.value if isinstance(value, Enum) else value
279
+ if fields:
280
+ body["fields"] = fields
281
+
282
+ return from_dict(SweepSettings, await self._post("/v1/sweeps/settings/update", body))
@@ -1,85 +0,0 @@
1
- """Treasury sweeps (transit -> master)."""
2
-
3
- from __future__ import annotations
4
-
5
- from dataclasses import dataclass
6
- from enum import Enum
7
- from typing import Any, List, Optional
8
-
9
- from .._models import from_dict
10
- from ..pagination import HistoryMeta
11
- from .base import BaseService
12
-
13
-
14
- class SweepMode(str, Enum):
15
- AUTO = "auto"
16
- FORCE = "force"
17
-
18
-
19
- @dataclass(kw_only=True)
20
- class SweepHistoryQuery:
21
- mode: Optional[str] = None
22
- page: Optional[int] = None
23
- page_size: Optional[int] = None
24
-
25
-
26
- @dataclass(kw_only=True)
27
- class Sweep:
28
- task_id: str = ""
29
- status: str = ""
30
- sweep_tx_hash: Optional[str] = None
31
- wallet_address: Optional[str] = None
32
- chain: Optional[str] = None
33
- chain_family: Optional[str] = None
34
- asset_symbol: Optional[str] = None
35
- asset_type: Optional[str] = None
36
- amount_human: Optional[str] = None
37
- gas_fee_human: Optional[str] = None
38
- gas_fee_fiat: Optional[str] = None
39
- service_fee_fiat: Optional[str] = None
40
- created_at: Optional[str] = None
41
- updated_at: Optional[str] = None
42
-
43
-
44
- @dataclass(kw_only=True)
45
- class SweepHistoryResponse:
46
- items: Optional[List[Sweep]] = None
47
- meta: Optional[HistoryMeta] = None
48
-
49
-
50
- @dataclass(kw_only=True)
51
- class ForceSweepResponse:
52
- status: str = ""
53
-
54
-
55
- class SweepsService(BaseService):
56
- async def force(self, address: str, network: str) -> ForceSweepResponse:
57
- """Trigger an immediate transit->master sweep for one address.
58
-
59
- The status acknowledges acceptance; the resulting :class:`Sweep` record
60
- appears via :meth:`wallet_history` once the on-chain tx is built.
61
- """
62
- return from_dict(
63
- ForceSweepResponse,
64
- await self._post("/v1/sweeps/force", {"address": address, "network_code": network}),
65
- )
66
-
67
- async def history(self, query: Optional[SweepHistoryQuery] = None) -> SweepHistoryResponse:
68
- """Recent sweeps across the whole project."""
69
- return from_dict(
70
- SweepHistoryResponse, await self._post("/v1/sweeps/history", query or SweepHistoryQuery())
71
- )
72
-
73
- async def wallet_history(
74
- self, address: str, query: Optional[SweepHistoryQuery] = None
75
- ) -> SweepHistoryResponse:
76
- """Recent sweeps scoped to one wallet."""
77
- body: dict[str, Any] = {"address": address}
78
- if query is not None:
79
- if query.mode is not None:
80
- body["mode"] = query.mode
81
- if query.page is not None:
82
- body["page"] = query.page
83
- if query.page_size is not None:
84
- body["page_size"] = query.page_size
85
- return from_dict(SweepHistoryResponse, await self._post("/v1/sweeps/wallet/history", body))