axis-synome 0.1.dev184__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.
Files changed (39) hide show
  1. axis_synome/__init__.py +0 -0
  2. axis_synome/_version.py +24 -0
  3. axis_synome/spec/__init__.py +0 -0
  4. axis_synome/spec/asc/README.md +419 -0
  5. axis_synome/spec/asc/entities/__init__.py +1 -0
  6. axis_synome/spec/asc/entities/assets.py +17 -0
  7. axis_synome/spec/asc/entities/assets_by_prime.py +618 -0
  8. axis_synome/spec/asc/entities/networks.py +15 -0
  9. axis_synome/spec/asc/entities/primes.py +96 -0
  10. axis_synome/spec/asc/entities/protocol_sets.py +68 -0
  11. axis_synome/spec/asc/entities/tokens.py +58 -0
  12. axis_synome/spec/asc/entities/types.py +15 -0
  13. axis_synome/spec/asc/formulas/asc.py +32 -0
  14. axis_synome/spec/asc/formulas/asc_collateral_ratio.py +53 -0
  15. axis_synome/spec/asc/formulas/asc_incentive.py +28 -0
  16. axis_synome/spec/asc/formulas/dab.py +30 -0
  17. axis_synome/spec/asc/formulas/latent_asc.py +39 -0
  18. axis_synome/spec/asc/formulas/ratio_latent_asc.py +40 -0
  19. axis_synome/spec/asc/formulas/resting_asc.py +56 -0
  20. axis_synome/spec/codegen_test/entities/agents.py +12 -0
  21. axis_synome/spec/crypto_lending/__init__.py +0 -0
  22. axis_synome/spec/crypto_lending/formulas/__init__.py +0 -0
  23. axis_synome/spec/crypto_lending/formulas/lif.py +43 -0
  24. axis_synome/spec/risk_capital/__init__.py +0 -0
  25. axis_synome/spec/risk_capital/formulas/__init__.py +0 -0
  26. axis_synome/spec/risk_capital/formulas/required_risk_capital.py +39 -0
  27. axis_synome/spec_support/__init__.py +0 -0
  28. axis_synome/spec_support/evm_address.py +48 -0
  29. axis_synome/spec_support/metadata.py +22 -0
  30. axis_synome/spec_support/validated_dataclass.py +36 -0
  31. axis_synome/spec_support/validated_str.py +40 -0
  32. axis_synome/spec_validator/__init__.py +5 -0
  33. axis_synome/spec_validator/checker.py +355 -0
  34. axis_synome/spec_validator/flake8_plugin.py +52 -0
  35. axis_synome/spec_validator/python_subset.py +106 -0
  36. axis_synome-0.1.dev184.dist-info/METADATA +73 -0
  37. axis_synome-0.1.dev184.dist-info/RECORD +39 -0
  38. axis_synome-0.1.dev184.dist-info/WHEEL +4 -0
  39. axis_synome-0.1.dev184.dist-info/entry_points.txt +2 -0
File without changes
@@ -0,0 +1,24 @@
1
+ # file generated by vcs-versioning
2
+ # don't change, don't track in version control
3
+ from __future__ import annotations
4
+
5
+ __all__ = [
6
+ "__version__",
7
+ "__version_tuple__",
8
+ "version",
9
+ "version_tuple",
10
+ "__commit_id__",
11
+ "commit_id",
12
+ ]
13
+
14
+ version: str
15
+ __version__: str
16
+ __version_tuple__: tuple[int | str, ...]
17
+ version_tuple: tuple[int | str, ...]
18
+ commit_id: str | None
19
+ __commit_id__: str | None
20
+
21
+ __version__ = version = '0.1.dev184'
22
+ __version_tuple__ = version_tuple = (0, 1, 'dev184')
23
+
24
+ __commit_id__ = commit_id = None
File without changes
@@ -0,0 +1,419 @@
1
+ # Actively Stabilizing Capital (ASC) Specification
2
+
3
+ This module is the executable specification for Sky Protocol's **Actively Stabilizing Capital** framework. It encodes the rules from the Sky Atlas into typed Python so that constraints can be evaluated, tested, and audited against live position data.
4
+
5
+ ---
6
+
7
+ ## Background
8
+
9
+ Sky Protocol issues **USDS**, a stablecoin pegged to one US dollar. The peg is only as strong as the liquid capital standing behind it. The Atlas defines **Actively Stabilizing Capital (ASC)** as the safety net every Prime Agent must maintain to absorb redemption pressure and defend the peg.
10
+
11
+ This specification answers: *does a given Prime, given its current positions, satisfy all ASC-related obligations?*
12
+
13
+ ---
14
+
15
+ ## Module Layout
16
+
17
+ ```
18
+ entities/ : data types and constants (the inputs)
19
+ formulas/ : pure functions that compute values from entities (the logic)
20
+ ```
21
+
22
+ All formulas follow one rule: **they accept raw entity inputs / placeholders and compute everything internally**. No formula accepts a precomputed intermediate value as an argument.
23
+
24
+ ---
25
+
26
+ ## Entities
27
+
28
+ ### `Position` (`entities/types.py`)
29
+
30
+ It is one of the fundamental units. It represents a single holding of a token in a protocol on a network.
31
+
32
+ | Field | Type | Description |
33
+ |---|---|---|
34
+ | `token` | `str` | Token symbol, e.g. `"USDC"` |
35
+ | `protocol` | `Protocol` | Protocol name, e.g. `Protocol.LITE_PSM`, `Protocol.CURVE` |
36
+ | `network` | `Network` | Chain, e.g. `Network.ETHEREUM_MAINNET`, `Network.BASE` |
37
+ | `value_usd` | `float` | USD value of this position |
38
+ | `owner` | `str \| None` | Prime name this position belongs to |
39
+ | `pool_paired_with_usds` | `bool \| None` | For pool positions: whether the pair token is USDS |
40
+ | `fee_tier` | `float \| None` | For G-UNI positions: fee tier (e.g. `0.0001`) |
41
+
42
+ Every formula filters positions by `p.owner == prime.name` so each Prime is evaluated against only its own capital.
43
+
44
+ ---
45
+
46
+ ### `PrimeAgent` (`entities/primes.py`)
47
+
48
+ It represents one Sky Star Agent & holds both identity data and governance parameters.
49
+
50
+ | Field | Type | Default | Description |
51
+ |---|---|---|---|
52
+ | `name` | `str` | — | Prime identifier, e.g. `"Spark"` |
53
+ | `output_stablecoins` | `set[str]` | `{}` | Stablecoins this Prime issues (used for DAB) |
54
+ | `psm_protocols` | `set[str]` | `{}` | Prime's own PSM protocol names (used for DAB) |
55
+ | `alm_proxy` | `str \| None` | `None` | ALM proxy protocol name; if set, positions there count as Latent ASC |
56
+ | `allocation_buffer_protocol` | `str \| None` | `None` | Allocation buffer protocol name (used for DAB) |
57
+ | `networks` | `set[Network]` | `{}` | Networks this Prime operates on |
58
+ | `max_pct_latent_asc` | `float \| None` | `None` | Max fraction of total ASC that may be Latent ASC. Must be ≤ 0.25. Must be set before calling `ratio_latent_asc`. |
59
+ | `asc_exempt` | `bool` | `False` | If `True`, this Prime is exempt from ASC calculations. Exempt primes are excluded from `EligiblePrimeAgentASC` (used by `asc()`). |
60
+ | `min_pct_asc_of_collateral` | `float` | `0.05` | Minimum fraction of the Collateral Portfolio that must be held as ASC. Fixed at 5% by protocol rule. |
61
+
62
+ **Validation:** `max_pct_latent_asc` must be set to a value ≤ 0.25 before calling `ratio_latent_asc`. It is not enforced at construction time.
63
+
64
+ #### Registered Primes
65
+
66
+ | Prime | `asc_exempt` | Notes |
67
+ |---|---|---|
68
+ | Spark | `False` | Largest Sky Direct exposure |
69
+ | Grove | `False` | Institutional tokenized credit |
70
+ | Keel | **`True`** | Operates on Solana; exempt from minimum ASC |
71
+ | Skybase | `False` | — |
72
+ | Pattern | `False` | Onboarding 2026 |
73
+ | Launch Agent 6 | `False` | — |
74
+ | Launch Agent 7 | `False` | — |
75
+
76
+ ### `EligiblePrimeAgentASC` (`entities/primes.py`)
77
+
78
+ Enum of non-exempt `PrimeAgent` instances eligible for ASC calculations. Excludes ASC-exempt primes (e.g., Keel). Each member's `.value` is the underlying `PrimeAgent` instance.
79
+
80
+ Members: `SPARK`, `GROVE`, `SKYBASE`, `PATTERN`, `LAUNCH_AGENT_6`, `LAUNCH_AGENT_7`.
81
+
82
+ ---
83
+
84
+ ### Constants (`entities/tokens.py`, `entities/networks.py`, `entities/protocol_sets.py`)
85
+
86
+ | Constant | Values |
87
+ |---|---|
88
+ | `CASH_STABLECOINS` | `USDC`, `USDS`, `DAI`, `USDT` |
89
+ | `L2_NETWORKS` | `Base`, `Arbitrum`, `Optimism`, `Unichain` |
90
+ | `L1_PSM_PROTOCOLS` | `LitePSM` |
91
+ | `L2_PSM_PROTOCOLS` | `PSM3` |
92
+ | `LENDING_PROTOCOLS` | SparkLend, Aave, Morpho (and variants), Kamino, Maple |
93
+ | `POOL_PROTOCOLS` | `Curve`, `Uniswap` |
94
+ | `GUNI_PROTOCOLS` | `GUNI` |
95
+ | `GUNI_RESTING_FEE_TIERS` | `0.0001` (0.01%), `0.0005` (0.05%) |
96
+
97
+ ---
98
+
99
+ ## Formulas
100
+
101
+ Each ASC related formula is a pure function `(prime, positions) -> float`. Slack formulas return a value that must be `>= 0` for the constraint to be satisfied; a negative result means a breach.
102
+
103
+ ---
104
+
105
+ ### `resting_asc(prime, positions)` → `float`
106
+
107
+ **File:** `formulas/resting_asc.py`
108
+
109
+ Capital that is immediately liquid and already defending the peg. These are positions that can absorb USDS redemptions right now, without any conversion step.
110
+
111
+ **Counts positions where `p.owner == prime.name` AND:**
112
+
113
+ | Condition | What it captures |
114
+ |---|---|
115
+ | `protocol in L1_PSM_PROTOCOLS` and `token == "USDC"` | USDC in LitePSM (Ethereum) |
116
+ | `protocol in L2_PSM_PROTOCOLS` and `token == "USDC"` and `network in L2_NETWORKS` | USDC in PSM3 on Base, Arbitrum, Unichain, Optimism |
117
+ | `protocol in POOL_PROTOCOLS` and `token in CASH_STABLECOINS` and `pool_paired_with_usds == True` | Cash stablecoins in Curve/Uniswap pools paired with USDS |
118
+ | `protocol in GUNI_PROTOCOLS` and `token == "USDC"` and `fee_tier in GUNI_RESTING_FEE_TIERS` | USDC in G-UNI at 0.01% or 0.05% fee tiers |
119
+
120
+ ---
121
+
122
+ ### `latent_asc(prime, positions)` → `float`
123
+
124
+ **File:** `formulas/latent_asc.py`
125
+
126
+ Capital that could be mobilised within 15 minutes but is not currently defending the peg. It is earning yield while standing ready. The conversion to Resting ASC must be fully automated.
127
+
128
+ **Counts positions where `p.owner == prime.name` AND `token in CASH_STABLECOINS` AND:**
129
+
130
+ | Condition | What it captures |
131
+ |---|---|
132
+ | `protocol in POOL_PROTOCOLS` and `pool_paired_with_usds == False` | Cash stablecoins in Curve/Uniswap pools NOT paired with USDS |
133
+ | `protocol in LENDING_PROTOCOLS` | Cash stablecoins deposited in SparkLend, Aave, Morpho, etc. |
134
+ | `protocol == prime.alm_proxy` (when set) | Cash stablecoins in the Prime's ALM proxy |
135
+
136
+ ---
137
+
138
+ ### `asc(prime: EligiblePrimeAgentASC, positions)` → `float`
139
+
140
+ **File:** `formulas/asc.py`
141
+
142
+ Total Actively Stabilizing Capital. The sum of all stabilizing capital, both immediately liquid and held in reserve.
143
+
144
+ ```
145
+ ASC = RestingASC + LatentASC
146
+ ```
147
+
148
+ `asc()` accepts only members of `EligiblePrimeAgentASC`, which by design excludes exempt Primes. As a result, exempt Primes (e.g., Keel) cannot be evaluated with `asc()`.
149
+
150
+ ---
151
+
152
+ ### Constraints
153
+
154
+ The following formulas are **slack functions**: they return a number that must be `>= 0`. A negative value signals a breach of the corresponding Atlas rule.
155
+
156
+ ---
157
+
158
+ #### `ratio_latent_asc(prime, positions)` → `float`
159
+
160
+ **File:** `formulas/ratio_latent_asc.py`
161
+
162
+ Enforces the rule: *Latent ASC may not exceed `max_pct_latent_asc` of total ASC.*
163
+
164
+ ```
165
+ slack = ASC × max_pct_latent_asc − LatentASC
166
+ ```
167
+
168
+ - Positive (or zero): Latent ASC is within the permitted fraction.
169
+ - Negative: Latent ASC is too high — the Prime must convert reserves to Resting positions.
170
+
171
+ **Parameter on Prime:** `max_pct_latent_asc` — must be set (not `None`) and must be ≤ 0.25. The Atlas cap is 25%; individual Primes may be held to a stricter limit.
172
+
173
+ **Raises `ValueError`** if `max_pct_latent_asc` has not been configured on the Prime.
174
+
175
+ ---
176
+
177
+ #### `dab_actual(prime, positions)` → `float`
178
+
179
+ **File:** `formulas/dab.py`
180
+
181
+ The Prime's actual Demand Absorption Buffer: prime-issued stablecoins held in the Prime's own PSM protocols or allocation buffer, available to be sold at no more than $1.001 per USDS to absorb upward peg pressure.
182
+
183
+ **Counts positions where:**
184
+ - `token in prime.output_stablecoins` (the Prime's own issued stablecoins)
185
+ - AND (`protocol in prime.psm_protocols` OR `protocol == prime.allocation_buffer_protocol`)
186
+
187
+ ---
188
+
189
+ #### `dab_required(prime, positions)` → `float`
190
+
191
+ **File:** `formulas/dab.py`
192
+
193
+ The minimum DAB the Prime must hold, computed as a fixed fraction of its total ASC.
194
+
195
+ ```
196
+ DABRequired = ASC × DAB_REQUIRED_PCT
197
+ ```
198
+
199
+ **Global constant:** `DAB_REQUIRED_PCT = 0.25` (25%), fixed by Atlas rule. Every Prime must maintain a DAB equal to 25% of their required ASC.
200
+
201
+ ---
202
+
203
+ #### `dab_satisfied(prime, positions)` → `float`
204
+
205
+ **File:** `formulas/dab.py`
206
+
207
+ Slack for the DAB constraint. Must be `>= 0`.
208
+
209
+ ```
210
+ slack = DABActual − DABRequired
211
+ ```
212
+
213
+ ---
214
+
215
+ #### `asc_collateral_ratio(prime, positions)` → `float`
216
+
217
+ **File:** `formulas/asc_collateral_ratio.py`
218
+
219
+ Enforces the rule: *ASC must be at least `min_pct_asc_of_collateral` of the Prime's total Collateral Portfolio.*
220
+
221
+ ```
222
+ CollateralPortfolio = sum of value_usd for all positions owned by the Prime
223
+ slack = ASC − CollateralPortfolio × min_pct_asc_of_collateral
224
+ ```
225
+
226
+ - Positive (or zero): ASC meets the minimum threshold.
227
+ - Negative: The Prime's ASC is too small relative to its total portfolio.
228
+
229
+ **Parameter on Prime:** `min_pct_asc_of_collateral` — defaults to `0.05` (5%), fixed by Atlas rule.
230
+
231
+ ---
232
+
233
+ ### `asc_incentive(prime, positions, base_rate, treasury_bill_rate)` → `float`
234
+
235
+ **File:** `formulas/asc_incentive.py`
236
+
237
+ The payment a Prime receives for maintaining ASC. If the base rate exceeds the T-bill rate, maintaining ASC is profitable. If not, it is a cost the Prime bears for operating in the Sky ecosystem.
238
+
239
+ ```
240
+ ASCIncentive = ASC × (base_rate − treasury_bill_rate)
241
+ ```
242
+
243
+ `base_rate` and `treasury_bill_rate` are external market/governance inputs passed directly as parameters — they are not properties of a Prime.
244
+
245
+ ---
246
+
247
+ ## How the Constraints Fit Together
248
+
249
+ For every member of `EligiblePrimeAgentASC`, all of the following must hold simultaneously:
250
+
251
+ | Constraint | Slack formula | Must be |
252
+ |---|---|---|
253
+ | Latent ASC ≤ `max_pct_latent_asc` of total ASC | `ratio_latent_asc` | ≥ 0 |
254
+ | DAB actual ≥ 25% of ASC | `dab_satisfied` | ≥ 0 |
255
+ | ASC ≥ 5% of Collateral Portfolio | `asc_collateral_ratio` | ≥ 0 |
256
+
257
+ **Keel is automatically skipped**: it is not a member of `EligiblePrimeAgentASC`, so `asc()` is never called for Keel. Iterate over `EligiblePrimeAgentASC` when computing ASC-dependent constraints.
258
+
259
+ ---
260
+
261
+ ## Slack Formulas
262
+
263
+ A slack formula measures **how far above (or below) a constraint threshold** a Prime currently sits.
264
+
265
+ The pattern is always:
266
+
267
+ ```
268
+ slack = actual − required
269
+ ```
270
+
271
+ | Slack value | Meaning |
272
+ |---|---|
273
+ | Positive | Compliant with headroom — the Prime exceeds the requirement by that amount |
274
+ | Zero | Exactly at the limit — compliant, but no buffer |
275
+ | Negative | Breaching the constraint — the shortfall is the absolute value |
276
+
277
+ For example, `dab_satisfied` returns `DABActual − DABRequired`. A result of `−50,000` means the Prime is $50,000 short of its required DAB. A result of `+200,000` means it has $200,000 of buffer above the minimum.
278
+
279
+ The advantage over a simple boolean pass/fail check is that you can see **how close to the edge** a Prime is, not just whether it passed or failed. This makes slack values directly useful for monitoring, graduated alerting (e.g. warn when slack < 10%), and audit reporting.
280
+
281
+ ---
282
+
283
+ ## Design Principles
284
+
285
+ 1. **No precomputed inputs.** Every formula accepts raw entities (`PrimeAgent`, `list[Position]`) and computes all intermediates internally. This makes formulas independently testable and prevents silent staleness bugs.
286
+
287
+ 2. **Governance parameters live on the entity.** Values that can change per-prime (e.g. `max_pct_latent_asc`, `min_pct_asc_of_collateral`) are fields on `PrimeAgent`, not magic numbers buried in formulas. Protocol-wide constants (e.g. `DAB_REQUIRED_PCT`) are module-level constants in the relevant formula file. To change a limit, update the entity or constant — the formula picks it up automatically.
288
+
289
+ 3. **Optional parameters signal missing configuration.** Parameters like `max_pct_latent_asc` that have no universal default are `None` until explicitly set. Formulas raise a clear `ValueError` rather than silently producing wrong results.
290
+
291
+ 4. **Slack semantics are consistent.** All constraint functions return a value where `>= 0` means compliant and `< 0` means breached, by the same amount. This makes threshold monitoring uniform across all constraints.
292
+
293
+ ---
294
+
295
+ ## Connecting External Data
296
+
297
+ ### Overview
298
+
299
+ Data you fetch from an on-chain indexer, a REST API, or a database arrives as raw records — dictionaries, CSV rows, or ORM objects. Before passing data to any formula, it must be converted into `Position` objects. This conversion is the **adapter step**.
300
+
301
+ The order matters:
302
+
303
+ ```
304
+ 1. Fetch : pull raw records from your data source (could be multiple)
305
+ 2. Adapt : map raw records to Position objects
306
+ 3. Evaluate : pass positions to the formula
307
+ ```
308
+
309
+ ### Step 1: Fetch
310
+
311
+ Pull position records from your source. The exact method depends on what you have (could be an API client, a database query, or a file reader). The result is anticipated be a list of dicts (or any iterable of mappings). This needs to be clarified.
312
+
313
+ ```python
314
+ # From a REST API
315
+ raw_records = api_client.get("/positions?prime=Spark").json()
316
+
317
+ # From a database
318
+ raw_records = db.execute(
319
+ "SELECT token, protocol, network, usd_value FROM positions WHERE prime = ?", ("Spark",)
320
+ ).fetchall()
321
+
322
+ # From a CSV file (via the standard library)
323
+ import csv
324
+
325
+ with open("positions.csv") as f:
326
+ raw_records = list(csv.DictReader(f))
327
+ ```
328
+
329
+ ### Step 2: Adapt
330
+
331
+ Convert each raw record into a `Position`. The `owner` is always passed separately, it is governance metadata that your data source does not carry.
332
+
333
+ ```python
334
+ from axis_synome.spec.asc.entities.types import Position
335
+
336
+
337
+ def adapt(records: list[dict], owner: str) -> list[Position]:
338
+ return [
339
+ Position(
340
+ token=r["token"],
341
+ protocol=r["protocol"],
342
+ network=r["network"],
343
+ value_usd=float(r["value_usd"]),
344
+ owner=owner, # For ex. the prime's name
345
+ pool_paired_with_usds=r.get("pool_paired_with_usds"),
346
+ fee_tier=r.get("fee_tier"),
347
+ )
348
+ for r in records
349
+ ]
350
+ ```
351
+
352
+ `pool_paired_with_usds` and `fee_tier` are optional so one can use `.get()` so that records that do not carry them default to `None`.
353
+
354
+ ### Step 3: Evaluate
355
+
356
+ Pass the adapted positions directly to any formula.
357
+
358
+ ```python
359
+ from axis_synome.spec.asc.entities.primes import SPARK, EligiblePrimeAgentASC
360
+ from axis_synome.spec.asc.formulas.asc import asc
361
+ from axis_synome.spec.asc.formulas.dab import dab_satisfied
362
+ from axis_synome.spec.asc.formulas.asc_collateral_ratio import asc_collateral_ratio
363
+
364
+ positions = adapt(raw_records, owner="Spark")
365
+
366
+ # Or make a loop for all Primes
367
+ print(asc(EligiblePrimeAgentASC.SPARK, positions))
368
+ print(dab_satisfied(SPARK, positions))
369
+ print(asc_collateral_ratio(SPARK, positions))
370
+ ```
371
+
372
+ ---
373
+
374
+ ### When the field names do not match
375
+
376
+ `Position` expects specific field names. If your data source uses different names, the call `Position(**row)` will raise a `TypeError`. Fix this once in the adapter, not in the formula.
377
+
378
+ Example: your API returns `usd_value` instead of `value_usd`, and `asset` instead of `token`.
379
+
380
+ ```python
381
+ # Your raw record
382
+ {"asset": "USDC", "protocol": "LitePSM", "network": "Ethereum", "usd_value": 1_000_000.0}
383
+ ```
384
+
385
+ Adapter that renames fields before construction:
386
+
387
+ ```python
388
+ def adapt(records: list[dict], owner: str) -> list[Position]:
389
+ return [
390
+ Position(
391
+ token=r["asset"], # renamed: asset -> token
392
+ protocol=r["protocol"],
393
+ network=r["network"],
394
+ value_usd=float(r["usd_value"]), # renamed: usd_value -> value_usd
395
+ owner=owner,
396
+ pool_paired_with_usds=r.get("pool_paired_with_usds"),
397
+ fee_tier=r.get("fee_tier"),
398
+ )
399
+ for r in records
400
+ ]
401
+ ```
402
+
403
+ PS: **your data source dictates whatever naming it chooses; the adapter is where you reconcile that with `Position`'s field names**. Formulas and tests are not aware of the user source's naming for now.
404
+
405
+ ---
406
+
407
+ ### Required vs Optional Fields
408
+
409
+ | Field | Required | Notes |
410
+ |---|---|---|
411
+ | `token` | Yes | Token symbol, e.g. `"USDC"` |
412
+ | `protocol` | Yes | Protocol name, e.g. `"LitePSM"` |
413
+ | `network` | Yes | Chain name, e.g. `"Ethereum"`, `"Base"` |
414
+ | `value_usd` | Yes | USD value as a float |
415
+ | `owner` | Passed separately | The prime's name; not expected to be in raw records |
416
+ | `pool_paired_with_usds` | No | Needed for pool positions; `None` otherwise |
417
+ | `fee_tier` | No | Needed for G-UNI positions; `None` otherwise |
418
+
419
+ If a required field is missing from a record, the adapter will raise a `KeyError`. That is intentional — it means your data source is incomplete and you should fix the fetch query, not add a fallback.
@@ -0,0 +1 @@
1
+
@@ -0,0 +1,17 @@
1
+ from axis_synome.spec.asc.entities.networks import Network
2
+ from axis_synome.spec.asc.entities.protocol_sets import Protocol
3
+ from axis_synome.spec.asc.entities.tokens import Token
4
+ from axis_synome.spec_support.evm_address import EvmAddress
5
+ from axis_synome.spec_support.validated_dataclass import validated_dataclass
6
+
7
+
8
+ @validated_dataclass
9
+ class Asset:
10
+ token: Token
11
+ network: Network
12
+ protocol: Protocol
13
+ address: EvmAddress
14
+ underlying_asset: Token
15
+ underlying_asset_address: EvmAddress
16
+ underlying_asset_source: str
17
+ source: str | None = None