arete-sdk 0.11.0__py3-none-any.whl

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.
arete/__init__.py ADDED
@@ -0,0 +1,310 @@
1
+ """Arete Python SDK — idiomatic Python projection of the Arete core SDK API.
2
+
3
+ See docs/internal/sdk-core-api.md (canonical surface) and
4
+ docs/internal/sdk-python-alignment.md (Python projection).
5
+
6
+ import arete
7
+ a4 = await arete.Arete.connect(STACK, wallet=wallet)
8
+ async for round in a4.views.ore_round.latest.use(take=10):
9
+ ...
10
+ """
11
+
12
+ from __future__ import annotations
13
+
14
+ __version__ = "0.4.0"
15
+
16
+
17
+ class _Unset:
18
+ """Sentinel distinguishing "no active subscription" from an empty result."""
19
+
20
+ _instance = None
21
+
22
+ def __new__(cls):
23
+ if cls._instance is None:
24
+ cls._instance = super().__new__(cls)
25
+ return cls._instance
26
+
27
+ def __repr__(self) -> str:
28
+ return "arete.UNSET"
29
+
30
+ def __bool__(self) -> bool:
31
+ return False
32
+
33
+
34
+ UNSET = _Unset()
35
+
36
+ # NOTE: UNSET must exist before the submodule imports below (arete.views
37
+ # imports it at module load).
38
+
39
+ from arete.errors import ( # noqa: E402
40
+ AreteConnectionError,
41
+ AreteError,
42
+ AuthError,
43
+ HttpRequestError,
44
+ ProcessedSlotTimeoutError,
45
+ SubscriptionError,
46
+ )
47
+ from arete.auth import AuthConfig # noqa: E402
48
+ from arete.wire import RichUpdate, Update # noqa: E402
49
+ from arete.views import ( # noqa: E402
50
+ DEFAULT_INITIAL_DATA_TIMEOUT,
51
+ InitialDataTimeoutError,
52
+ ListViewHandle,
53
+ StateViewHandle,
54
+ ViewDef,
55
+ ViewsNamespace,
56
+ )
57
+ from arete.chain import ( # noqa: E402
58
+ ChainClient,
59
+ ChainClock,
60
+ ChainError,
61
+ HttpChainClient,
62
+ MintAccountInfo,
63
+ NativeBalanceInfo,
64
+ RawAccountInfo,
65
+ TokenAccountInfo,
66
+ TokenBalanceInfo,
67
+ )
68
+ from arete.transactions import ( # noqa: E402
69
+ HttpTransactionTransport,
70
+ TransactionTransport,
71
+ TransactionTransportError,
72
+ )
73
+ from arete.gateway import ( # noqa: E402
74
+ HostedSolanaGatewayBindings,
75
+ create_hosted_solana_gateway_transports,
76
+ )
77
+ from arete.amounts import ( # noqa: E402
78
+ format_raw_to_ui,
79
+ get_mint_decimals,
80
+ parse_ui_amount_to_raw,
81
+ resolve_amount,
82
+ resolve_amount_to_raw,
83
+ resolve_amounts_to_raw,
84
+ to_raw_amount,
85
+ )
86
+ from arete.spl import ( # noqa: E402
87
+ ASSOCIATED_TOKEN_PROGRAM_ADDRESS,
88
+ SPL_TOKEN_PROGRAM_ADDRESS,
89
+ SYSTEM_PROGRAM_ADDRESS,
90
+ TOKEN_2022_PROGRAM_ADDRESS,
91
+ derive_associated_token_account,
92
+ resolve_token_program_address,
93
+ )
94
+ from arete.instructions import ( # noqa: E402
95
+ BuiltAccountMeta,
96
+ BuiltInstruction,
97
+ ErrorMetadata,
98
+ InstructionError,
99
+ InstructionHandler,
100
+ PdaConfig,
101
+ derive_pda,
102
+ find_program_address,
103
+ format_program_error,
104
+ parse_program_error,
105
+ )
106
+ from arete.read import ( # noqa: E402
107
+ AccountBatchItem,
108
+ AccountBatchResult,
109
+ AccountReader,
110
+ ProgramAccountReadDef,
111
+ ProgramQueryDef,
112
+ ReadRequestError,
113
+ StackQueryDef,
114
+ )
115
+ from arete.program_read_transport import ( # noqa: E402
116
+ ProgramReadDescriptor,
117
+ ProgramReleaseReference,
118
+ validate_program_read_descriptor,
119
+ )
120
+ from arete.wallet import ( # noqa: E402
121
+ ConfirmedTransactionOutcome,
122
+ SendOptions,
123
+ SendResult,
124
+ TransactionFailureOutcome,
125
+ TransactionInspectionResult,
126
+ WalletAdapter,
127
+ WalletError,
128
+ WalletExecutionContext,
129
+ )
130
+ from arete.operations import ( # noqa: E402
131
+ OperationCallbackError,
132
+ OperationExecutionError,
133
+ OperationReceipt,
134
+ OperationTransactionReceipt,
135
+ PreparedFlow,
136
+ PreparedInstruction,
137
+ PreparedOperation,
138
+ PreparedTransaction,
139
+ PreparedTransactionBody,
140
+ SignerRegistry,
141
+ TransactionExecutionError,
142
+ append_flow_transactions,
143
+ append_transaction_instructions,
144
+ create_prepared_flow,
145
+ create_prepared_instruction,
146
+ create_prepared_transaction,
147
+ create_prepared_transaction_body,
148
+ create_signer_registry,
149
+ describe_prepared_operation,
150
+ execute_prepared_operation,
151
+ format_prepared_operation,
152
+ get_transaction_failure_outcome,
153
+ inspect_prepared_operation,
154
+ prepend_flow_transaction_instructions,
155
+ prepend_transaction_instructions,
156
+ unwrap_operation_execution_error,
157
+ )
158
+ from arete.stack import ( # noqa: E402
159
+ ConnectedProgram,
160
+ Operation,
161
+ ProgramDef,
162
+ ProgramOperationContext,
163
+ ProgramOperations,
164
+ StackDef,
165
+ StackEndpoints,
166
+ flow_operation,
167
+ instruction_operation,
168
+ transaction_operation,
169
+ with_programs,
170
+ )
171
+ from arete.extensions import ( # noqa: E402
172
+ apply_connected_stack_extensions,
173
+ extend_program,
174
+ extend_programs,
175
+ extend_stack,
176
+ )
177
+ from arete.client import Arete # noqa: E402
178
+ from arete.session import Session, SessionError, create_session # noqa: E402
179
+
180
+ __all__ = [
181
+ "UNSET",
182
+ "__version__",
183
+ # client & session
184
+ "Arete",
185
+ "Session",
186
+ "SessionError",
187
+ "create_session",
188
+ # errors
189
+ "AreteError",
190
+ "AreteConnectionError",
191
+ "AuthError",
192
+ "ChainError",
193
+ "HttpRequestError",
194
+ "InitialDataTimeoutError",
195
+ "InstructionError",
196
+ "OperationCallbackError",
197
+ "OperationExecutionError",
198
+ "ProcessedSlotTimeoutError",
199
+ "ReadRequestError",
200
+ "SubscriptionError",
201
+ "TransactionExecutionError",
202
+ "TransactionTransportError",
203
+ "WalletError",
204
+ # auth
205
+ "AuthConfig",
206
+ # views & updates
207
+ "Update",
208
+ "RichUpdate",
209
+ "ViewDef",
210
+ "ViewsNamespace",
211
+ "DEFAULT_INITIAL_DATA_TIMEOUT",
212
+ "ListViewHandle",
213
+ "StateViewHandle",
214
+ # stack binding model
215
+ "StackDef",
216
+ "StackEndpoints",
217
+ "ProgramDef",
218
+ "ProgramOperations",
219
+ "ProgramOperationContext",
220
+ "ConnectedProgram",
221
+ "with_programs",
222
+ # extensions
223
+ "extend_stack",
224
+ "extend_program",
225
+ "extend_programs",
226
+ "apply_connected_stack_extensions",
227
+ "Operation",
228
+ "instruction_operation",
229
+ "transaction_operation",
230
+ "flow_operation",
231
+ # wallet & outcomes
232
+ "WalletAdapter",
233
+ "WalletExecutionContext",
234
+ "SendOptions",
235
+ "SendResult",
236
+ "TransactionInspectionResult",
237
+ "ConfirmedTransactionOutcome",
238
+ "TransactionFailureOutcome",
239
+ "get_transaction_failure_outcome",
240
+ # prepared operations
241
+ "PreparedInstruction",
242
+ "PreparedTransaction",
243
+ "PreparedFlow",
244
+ "PreparedOperation",
245
+ "PreparedTransactionBody",
246
+ "create_prepared_instruction",
247
+ "create_prepared_transaction",
248
+ "create_prepared_flow",
249
+ "create_prepared_transaction_body",
250
+ "prepend_transaction_instructions",
251
+ "append_transaction_instructions",
252
+ "append_flow_transactions",
253
+ "prepend_flow_transaction_instructions",
254
+ "execute_prepared_operation",
255
+ "inspect_prepared_operation",
256
+ "describe_prepared_operation",
257
+ "format_prepared_operation",
258
+ "unwrap_operation_execution_error",
259
+ "OperationReceipt",
260
+ "OperationTransactionReceipt",
261
+ "SignerRegistry",
262
+ "create_signer_registry",
263
+ # chain & transactions
264
+ "ChainClient",
265
+ "HttpChainClient",
266
+ "ChainClock",
267
+ "RawAccountInfo",
268
+ "MintAccountInfo",
269
+ "TokenAccountInfo",
270
+ "TokenBalanceInfo",
271
+ "NativeBalanceInfo",
272
+ "TransactionTransport",
273
+ "HttpTransactionTransport",
274
+ "HostedSolanaGatewayBindings",
275
+ "create_hosted_solana_gateway_transports",
276
+ # amounts & SPL
277
+ "parse_ui_amount_to_raw",
278
+ "format_raw_to_ui",
279
+ "to_raw_amount",
280
+ "get_mint_decimals",
281
+ "resolve_amount",
282
+ "resolve_amount_to_raw",
283
+ "resolve_amounts_to_raw",
284
+ "SPL_TOKEN_PROGRAM_ADDRESS",
285
+ "TOKEN_2022_PROGRAM_ADDRESS",
286
+ "ASSOCIATED_TOKEN_PROGRAM_ADDRESS",
287
+ "SYSTEM_PROGRAM_ADDRESS",
288
+ "derive_associated_token_account",
289
+ "resolve_token_program_address",
290
+ # instruction runtime
291
+ "InstructionHandler",
292
+ "BuiltInstruction",
293
+ "BuiltAccountMeta",
294
+ "ErrorMetadata",
295
+ "PdaConfig",
296
+ "derive_pda",
297
+ "find_program_address",
298
+ "parse_program_error",
299
+ "format_program_error",
300
+ # program reads
301
+ "AccountReader",
302
+ "AccountBatchItem",
303
+ "AccountBatchResult",
304
+ "ProgramAccountReadDef",
305
+ "ProgramQueryDef",
306
+ "StackQueryDef",
307
+ "ProgramReadDescriptor",
308
+ "ProgramReleaseReference",
309
+ "validate_program_read_descriptor",
310
+ ]
arete/amounts.py ADDED
@@ -0,0 +1,157 @@
1
+ """Token amount helpers (port of ``typescript/core/src/amounts.ts``).
2
+
3
+ An :data:`AmountInput` is either raw base units (a bare ``int``, or
4
+ ``{"raw": int | str}``) or UI units (``{"ui": str | int | float}``). UI
5
+ parsing is exact string math pinned to the mint's decimals — no float
6
+ precision loss. Decimals are fetched from the chain read endpoint only when
7
+ they are unknown and actually needed.
8
+ """
9
+
10
+ from __future__ import annotations
11
+
12
+ import re
13
+ from dataclasses import dataclass
14
+ from typing import Any, Dict, Mapping, Optional, Union
15
+
16
+ from arete.chain import ChainClient
17
+
18
+ AmountInput = Union[int, Mapping[str, Any]]
19
+
20
+ _UI_AMOUNT_RE = re.compile(r"^\d+(?:\.\d+)?$")
21
+
22
+
23
+ @dataclass(frozen=True)
24
+ class ResolvedAmount:
25
+ raw: int
26
+ decimals: int
27
+
28
+
29
+ def parse_ui_amount_to_raw(value: Union[str, int, float], decimals: int) -> int:
30
+ """Convert a UI amount ("1.5") to raw base units using string math."""
31
+ trimmed = str(value).strip()
32
+ if not _UI_AMOUNT_RE.match(trimmed):
33
+ raise ValueError(f"Invalid UI amount: {value}")
34
+
35
+ whole_part, _, fraction_part = trimmed.partition(".")
36
+ if len(fraction_part) > decimals:
37
+ excess = fraction_part[decimals:]
38
+ if any(ch != "0" for ch in excess):
39
+ raise ValueError(
40
+ f"UI amount {value} has more fractional digits than the mint's "
41
+ f"{decimals} decimals"
42
+ )
43
+ fraction = fraction_part.ljust(decimals, "0")[:decimals]
44
+ whole = int(whole_part or "0") * 10**decimals
45
+ return whole + int(fraction or "0")
46
+
47
+
48
+ def format_raw_to_ui(raw: Union[int, str], decimals: int) -> str:
49
+ """Format raw base units as a UI decimal string (inverse of
50
+ :func:`parse_ui_amount_to_raw`)."""
51
+ value = _to_int(raw, "raw amount")
52
+ negative = value < 0
53
+ magnitude = -value if negative else value
54
+ scale = 10**decimals
55
+ whole, fraction = divmod(magnitude, scale)
56
+ sign = "-" if negative else ""
57
+ if decimals == 0 or fraction == 0:
58
+ return f"{sign}{whole}"
59
+ fraction_text = str(fraction).rjust(decimals, "0").rstrip("0")
60
+ return f"{sign}{whole}.{fraction_text}"
61
+
62
+
63
+ def _to_int(value: Any, name: str) -> int:
64
+ if isinstance(value, bool):
65
+ raise ValueError(f"Invalid {name}: {value!r}")
66
+ if isinstance(value, int):
67
+ return value
68
+ if isinstance(value, str):
69
+ text = value.strip()
70
+ if re.match(r"^-?\d+$", text):
71
+ return int(text)
72
+ raise ValueError(f"Invalid {name}: {value!r}")
73
+
74
+
75
+ def _is_raw_input(amount: AmountInput) -> bool:
76
+ return isinstance(amount, int) or (
77
+ isinstance(amount, Mapping) and "raw" in amount
78
+ )
79
+
80
+
81
+ def to_raw_amount(amount: AmountInput, decimals: int) -> int:
82
+ """Resolve an :data:`AmountInput` to raw base units with known decimals."""
83
+ if isinstance(amount, bool):
84
+ raise ValueError(f"Invalid amount input: {amount!r}")
85
+ if isinstance(amount, int):
86
+ return amount
87
+ if isinstance(amount, Mapping):
88
+ if "raw" in amount:
89
+ return _to_int(amount["raw"], "raw amount")
90
+ if "ui" in amount:
91
+ return parse_ui_amount_to_raw(amount["ui"], decimals)
92
+ raise ValueError(f"Invalid amount input: {amount!r}")
93
+
94
+
95
+ async def get_mint_decimals(chain: ChainClient, mint: str) -> int:
96
+ """Fetch a mint's decimals via the chain read endpoint, raising when
97
+ unavailable."""
98
+ account = await chain.mint(mint)
99
+ if account is None or account.decimals is None:
100
+ raise ValueError(
101
+ f"Mint {mint} is missing decimals on the configured read endpoint."
102
+ )
103
+ return account.decimals
104
+
105
+
106
+ async def resolve_amount(
107
+ chain: ChainClient,
108
+ *,
109
+ mint: str,
110
+ amount: AmountInput,
111
+ decimals: Optional[int] = None,
112
+ ) -> ResolvedAmount:
113
+ """Resolve an :data:`AmountInput` to raw base units, fetching the mint's
114
+ decimals only when they are unknown (a bare int or ``{"raw"}`` input with
115
+ explicit ``decimals`` never touches the network)."""
116
+ if _is_raw_input(amount):
117
+ raw = amount if isinstance(amount, int) else _to_int(amount["raw"], "raw amount")
118
+ resolved = decimals if decimals is not None else await get_mint_decimals(chain, mint)
119
+ return ResolvedAmount(raw=raw, decimals=resolved)
120
+
121
+ resolved = decimals if decimals is not None else await get_mint_decimals(chain, mint)
122
+ return ResolvedAmount(raw=to_raw_amount(amount, resolved), decimals=resolved)
123
+
124
+
125
+ async def resolve_amount_to_raw(
126
+ chain: ChainClient,
127
+ *,
128
+ mint: str,
129
+ amount: AmountInput,
130
+ decimals: Optional[int] = None,
131
+ ) -> int:
132
+ """Resolve an :data:`AmountInput` to raw base units without forcing a
133
+ decimals fetch when the input is already expressed in raw units."""
134
+ if isinstance(amount, int) and not isinstance(amount, bool):
135
+ return amount
136
+ if isinstance(amount, Mapping) and "raw" in amount:
137
+ return _to_int(amount["raw"], "raw amount")
138
+
139
+ resolved = decimals if decimals is not None else await get_mint_decimals(chain, mint)
140
+ return to_raw_amount(amount, resolved)
141
+
142
+
143
+ async def resolve_amounts_to_raw(
144
+ chain: ChainClient,
145
+ inputs: Mapping[str, Mapping[str, Any]],
146
+ ) -> Dict[str, int]:
147
+ """Resolve a named set of amount inputs (each ``{"mint", "amount"[,
148
+ "decimals"]}``) to raw base units, preserving keys."""
149
+ resolved: Dict[str, int] = {}
150
+ for name, entry in inputs.items():
151
+ resolved[name] = await resolve_amount_to_raw(
152
+ chain,
153
+ mint=entry["mint"],
154
+ amount=entry["amount"],
155
+ decimals=entry.get("decimals"),
156
+ )
157
+ return resolved