Skip to content

Commit 3364779

Browse files
authored
docs: ✍️ Update documentation (#133)
* update readme * updated architecture * add diagram * format
1 parent cbd67c6 commit 3364779

File tree

2 files changed

+143
-133
lines changed

2 files changed

+143
-133
lines changed

ARCHITECTURE.md

Lines changed: 116 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,14 @@ flowchart TD
2424
## Core Components
2525

2626
### CLI & Configuration (`src/tq_oracle/main.py`, `settings.py`)
27+
2728
- Typer command parses CLI flags, applies overrides, and instantiates `OracleSettings`.
2829
- `OracleSettings` is the single truth for configuration: CLI args > environment/`.env` > TOML (`tq-oracle.toml` or `$HOME/.config/tq-oracle/config.toml`). Secrets are rejected from TOML and wrapped in `SecretStr`.
2930
- Network defaults come from `constants.py` (RPC URLs, OracleHelper contracts, and canonical asset maps for Mainnet, Sepolia, Base). Missing RPC/OracleHelper values are filled automatically; block numbers are resolved via Web3 when absent.
3031
- The CLI enforces required params (vault address, RPC, Safe creds when `--no-dry-run`) and exposes `--show-config` for redacted inspection before dispatching to `pipeline.run_report()`.
3132

3233
### Pipeline Orchestration (`pipeline/run.py`)
34+
3335
`run_report()` builds a `PipelineContext` and executes the pipeline sequentially:
3436
1. **Base asset discovery**`_discover_base_asset()` walks Vault → FeeManager contracts on the target chain to retrieve the canonical base asset used for pricing.
3537
2. **Preflight**`pipeline/preflight.py` runs adapters with backoff/retry to ensure it is safe to proceed.
@@ -38,24 +40,33 @@ flowchart TD
3840
5. **Report + publish** – packages results and either prints or submits to a Safe.
3941

4042
### Preflight Checks (`checks/pre_checks.py`, `adapters/check_adapters/*`)
41-
- **Safe state**: warns/blocks if no Safe configured or if duplicate/pending oracle reports exist (`safe_state.py`, `active_submit_report_proposal_check.py`).
43+
44+
- **Active proposal check**: blocks if a duplicate or pending oracle report already exists on the Safe (`active_submit_report_proposal_check.py`); can be bypassed via `--ignore-active-proposal-check`.
4245
- **Timeout enforcement**: queries the Oracle contract for last report timestamps and security params; can be bypassed via `--ignore-timeout-check` (`timeout_check.py`).
4346
- Each adapter returns a `CheckResult`, and `run_preflight()` retries failures up to `pre_check_retries` using `backoff` unless the adapter signals `retry_recommended=False`.
4447

4548
### Asset Collection (`pipeline/assets.py`, `adapters/asset_adapters/*`)
49+
4650
- Fetches subvault addresses via Vault ABI calls, validates `subvault_adapters` config entries, and optionally allows synthetic addresses when `skip_subvault_existence_check` is set.
47-
- Runs the default `IdleBalancesAdapter` across the vault and every subvault (unless skipped per-subvault) to gather on-chain balances for supported assets defined in `OracleSettings.assets`.
48-
- Additional adapters can be configured per subvault; each adapter inherits from `BaseAssetAdapter` and is registered in `ADAPTER_REGISTRY`.
51+
- Three default adapters run automatically based on configuration:
52+
- `IdleBalancesAdapter` – gathers on-chain token balances across vault and all subvaults (skippable per-subvault via `skip_idle_balances`).
53+
- `StrETHAdapter` – fetches strETH positions (skippable via `skip_streth`).
54+
- `StakeWiseAdapter` – fetches StakeWise vault positions when `stakewise_vault_addresses` is configured.
55+
- Additional adapters can be configured per subvault via `additional_adapters`; each adapter inherits from `BaseAssetAdapter` and is registered in `ADAPTER_REGISTRY`.
4956
- Results are combined asynchronously and folded into `AggregatedAssets` via `processors/asset_aggregator.py`.
5057

5158
### Pricing & Validation (`pipeline/pricing.py`, `adapters/price_adapters/*`, `adapters/price_validators/pyth.py`)
52-
- Price adapters are instantiated from `PRICE_ADAPTERS` (currently `CowSwapAdapter` for general assets and `ETHAdapter` for ETH/WETH;).
59+
60+
- Price adapters are instantiated from `PRICE_ADAPTERS`:
61+
- `CowSwapAdapter` – fetches prices from CoW Protocol for general assets.
62+
- `ETHAdapter` – handles ETH/WETH pricing.
5363
- Each adapter updates a shared `PriceData` accumulator keyed by asset address (base asset must already be known).
54-
- `run_price_validations()` invokes validators from `PRICE_VALIDATORS`. The active `PythValidator` re-fetches prices through the Pyth Hermes API, compares deviations against configurable warning/failure tolerances, and can be disabled via `pyth_enabled=False`.
64+
- `run_price_validations()` invokes validators from `PRICE_VALIDATORS`. The active `PythValidator` re-fetches prices through the Pyth Hermes API and compares deviations against configurable warning/failure tolerances (`price_warning_tolerance_percentage`, `price_failure_tolerance_percentage`).
5565
- `processors/total_assets.calculate_total_assets()` multiplies balances by prices (18-decimal math) and raises if any asset lacks a quote.
5666
- `processors/oracle_helper.derive_final_prices()` submits the total asset figure plus encoded prices to the OracleHelper contract to obtain finalized per-asset values (respecting `ignore_empty_vault`).
5767

5868
### Reporting & Publishing (`report/*`)
69+
5970
- `report/generator.py` converts context data into an `OracleReport` dataclass.
6071
- `report/encoder.py` encodes `submitReports(Report[] reports)` calldata sorted by address and forces the base asset price to zero, mirroring the Solidity helper expectations.
6172
- `report/publisher.py`:
@@ -72,6 +83,7 @@ Key `OracleSettings` fields:
7283
- Pricing knobs: `pyth_*` settings plus warning/failure tolerances.
7384

7485
### Subvault Adapter Configuration
86+
7587
Each entry under `subvault_adapters` may specify:
7688
- `subvault_address`: Target contract (checksummed matching Vault discovery unless `skip_subvault_existence_check` is true).
7789
- `skip_idle_balances`: Exclude default idle balance scan for that address.
@@ -98,6 +110,105 @@ sequenceDiagram
98110
end
99111
```
100112

113+
### How Mellow Vaults Work
114+
115+
Mellow flexible vaults are modular yield-bearing vaults where user deposits flow through a layered architecture:
116+
117+
```mermaid
118+
flowchart TB
119+
subgraph Vault
120+
SM[ShareManager<br/>mint/burn]
121+
FM[FeeManager<br/>perf/protocol fees]
122+
RM[RiskManager<br/>TVL limits]
123+
Q[Deposit/Redeem Queues<br/>batch requests, settle at oracle price]
124+
end
125+
Vault --> SV1[Subvault<br/>EigenLayer]
126+
Vault --> SV2[Subvault<br/>Symbiotic]
127+
Vault --> SV3[Subvault<br/>Aave]
128+
```
129+
130+
**Deposit-to-share lifecycle:**
131+
132+
```mermaid
133+
sequenceDiagram
134+
participant User
135+
participant DepositQueue
136+
participant RedeemQueue
137+
participant TQOracle as TQ Oracle
138+
participant Oracle
139+
participant Vault
140+
participant FeeManager
141+
participant ShareManager
142+
143+
opt DEPOSIT FLOW
144+
User->>DepositQueue: deposit(assets)
145+
Note over DepositQueue: Request queued<br/>(time-delayed)
146+
147+
Note over TQOracle: Scheduled run...
148+
TQOracle->>TQOracle: Aggregate balances<br/>(vault + subvaults + protocols)
149+
TQOracle->>TQOracle: Price assets, compute TVL
150+
TQOracle->>Oracle: submitReports(Report[])
151+
152+
Oracle->>Oracle: Validate deviation & cooldown
153+
Oracle->>Vault: handleReport(asset, priceD18, timestamps)
154+
155+
Vault->>FeeManager: calculateFee(priceD18, totalShares)
156+
FeeManager-->>Vault: feeShares
157+
Vault->>ShareManager: mint(feeRecipient, feeShares)
158+
Vault->>FeeManager: updateState(priceD18)
159+
160+
Vault->>DepositQueue: handleReport(priceD18, depositTimestamp)
161+
DepositQueue->>DepositQueue: shares = assets × priceD18 / 1e18
162+
Note over DepositQueue: Shares claimable
163+
164+
User->>DepositQueue: claim()
165+
DepositQueue->>ShareManager: mint(user, shares)
166+
ShareManager-->>User: Vault shares
167+
end
168+
169+
opt REDEEM FLOW
170+
User->>RedeemQueue: redeem(shares)
171+
Note over RedeemQueue: Request queued<br/>(time-delayed)
172+
173+
Note over TQOracle: Scheduled run...
174+
TQOracle->>Oracle: submitReports(Report[])
175+
Oracle->>Vault: handleReport(asset, priceD18, timestamps)
176+
177+
Vault->>RedeemQueue: handleReport(priceD18, redeemTimestamp)
178+
RedeemQueue->>RedeemQueue: assets = shares × 1e18 / priceD18
179+
Note over RedeemQueue: Batch created
180+
181+
Vault->>Vault: handleBatches() [Curator]
182+
Vault->>Vault: Pull liquidity from subvaults if needed
183+
Vault->>RedeemQueue: Transfer assets for batch
184+
185+
User->>RedeemQueue: claim()
186+
RedeemQueue->>ShareManager: burn(user, shares)
187+
RedeemQueue-->>User: Assets
188+
end
189+
```
190+
191+
**Key components:**
192+
193+
- **Vault**: Central coordinator that holds idle assets and delegates to subvaults.
194+
- **Subvaults**: Isolated contracts deploying assets to external protocols (EigenLayer, Symbiotic, etc.).
195+
- **ShareManager**: Controls share minting/burning with whitelist/blacklist permissions.
196+
- **FeeManager**: Calculates performance fees (on gains) and protocol fees (time-accrued).
197+
- **Queues**: Batch user deposit/redeem requests; settled atomically when oracle reports arrive.
198+
- **Oracle**: Validates price reports against deviation thresholds and enforces cooldown periods.
199+
200+
**The oracle report lifecycle:**
201+
202+
1. Users submit deposit/redeem requests to queues (time-delayed).
203+
2. TQ Oracle computes TVL by aggregating vault + subvault + external protocol balances.
204+
3. TQ Oracle submits `Report[]` via `Oracle.submitReports()` through a Safe multisig.
205+
4. Oracle contract validates price deviation and cooldown, then calls `Vault.handleReport()`.
206+
5. Vault mints fee shares, updates FeeManager state, and propagates price to queues.
207+
6. Queues convert pending deposits→shares and pending redemptions→assets at the reported price.
208+
7. Users claim their shares or redeemed assets.
209+
210+
TQ Oracle is the off-chain price authority—it computes what the on-chain Oracle consumes. The on-chain Oracle enforces security invariants (max deviation, timeouts, suspicious-report flagging), but relies on TQ Oracle for accurate TVL calculation across all asset positions.
211+
101212
## Extension Points
102213
- **Asset adapters**: Implement `BaseAssetAdapter`, register in `ADAPTER_REGISTRY`, and wire via `subvault_adapters`.
103214
- **Price adapters**: Implement `BasePriceAdapter` and append to `PRICE_ADAPTERS` to join the pricing chain.

README.md

Lines changed: 27 additions & 128 deletions
Original file line numberDiff line numberDiff line change
@@ -51,178 +51,77 @@ Run the CLI with a vault address and dry-run flag to preview reports without sub
5151
- Run with minimal CLI arguments - config file is auto-detected
5252
- See `tq-oracle.toml.example` for complete configuration template
5353

54-
### With Environment Variables
55-
56-
- Create `.env` file for environment-specific settings
57-
- Set RPC endpoints, subvault addresses, and other non-secret configuration
58-
- **Important**: Always use environment variables or CLI flags for secrets (private keys, API keys) - never store them in TOML files
59-
- Run with vault address and any additional CLI options
6054

6155
### Configuration Options
6256

63-
All configuration options can be set via CLI arguments, environment variables, or TOML config file.
64-
65-
| CLI Option | Environment Variable | TOML Key | Default | Description |
66-
|------------|---------------------|-----------|---------|-------------|
67-
| `vault_address` (argument) | `TQ_ORACLE_VAULT_ADDRESS` | `vault_address` | *required* | Vault contract address passed as positional argument |
68-
| `--config` `-c` | - | - | Auto-detect | Path to TOML configuration file |
69-
| `--network` `-n` | `TQ_ORACLE_NETWORK` | `network` | `"mainnet"` | Network to report on (`mainnet`, `sepolia`, `base`) |
70-
| `--block-number` | `TQ_ORACLE_BLOCK_NUMBER` | `block_number` | Latest block | Block number to snapshot vault state |
71-
| `--vault-rpc` | `TQ_ORACLE_VAULT_RPC` | `vault_rpc` | Network default | RPC endpoint for the selected vault network |
72-
| `--dry-run/--no-dry-run` | `TQ_ORACLE_DRY_RUN` | `dry_run` | `true` | Preview report without submitting a Safe transaction |
73-
| `--ignore-empty-vault/--require-nonempty-vault` | `TQ_ORACLE_IGNORE_EMPTY_VAULT` | `ignore_empty_vault` | `false` | Skip failure when vault holds zero assets |
74-
| `--ignore-timeout-check/--enforce-timeout-check` | `TQ_ORACLE_IGNORE_TIMEOUT_CHECK` | `ignore_timeout_check` | `false` | Skip minimum interval guard between reports |
75-
| `--ignore-active-proposal-check/--enforce-active-proposal-check` | `TQ_ORACLE_IGNORE_ACTIVE_PROPOSAL_CHECK` | `ignore_active_proposal_check` | `false` | Skip duplicate active proposal guard |
76-
| `--log-level` | `TQ_ORACLE_LOG_LEVEL` | `log_level` | `"INFO"` | Override logging verbosity (`TRACE`, `DEBUG`, `INFO`, `WARNING`, `ERROR`, `CRITICAL`) |
77-
| `--show-config` | - | - | `false` | Dump effective configuration (with secrets redacted) and exit |
78-
79-
#### TOML-Only Options (Not available via CLI)
80-
81-
| TOML Key | Default | Description |
82-
|----------|---------|-------------|
83-
| `max_calls` | `3` | Maximum number of RPC retry attempts |
84-
| `rpc_max_concurrent_calls` | `5` | Maximum concurrent RPC connections |
85-
| `rpc_delay` | `0.15` | Delay between RPC calls (seconds) |
86-
| `rpc_jitter` | `0.10` | Random jitter for RPC delays (seconds) |
87-
| `price_warning_tolerance_percentage` | `0.5` | Price deviation warning threshold (%) |
88-
| `price_failure_tolerance_percentage` | `1.0` | Price deviation failure threshold (%) |
89-
90-
### Advanced Configuration: Subvault Adapters
91-
92-
TQ Oracle supports configuring multiple asset adapters per subvault address, allowing you to compose TVL from various sources.
93-
94-
**Configuration via TOML:**
95-
96-
- Specify target subvault address
97-
- List additional adapters to run for this subvault
98-
- Option to skip default idle balances check
99-
- Option to skip subvault existence validation
100-
101-
**Available Asset Adapters:**
102-
103-
- `idle_balances` - Checks for idle USDC balances not yet deployed
104-
105-
See `tq-oracle.toml.example` for complete configuration examples.
57+
> [!IMPORTANT]
58+
> See [SETTINGS.md](./SETTINGS.md) for complete guide.
10659
10760
### Usage Examples
10861

10962
**Run with auto-detected config file:**
11063

11164
```bash
11265
# Loads from tq-oracle.toml or ~/.config/tq-oracle/config.toml
113-
tq-oracle
66+
uv run tq-oracle
11467
```
11568

11669
**Run with explicit vault address:**
11770

11871
```bash
11972
# Override vault address from config
120-
tq-oracle 0xYourVaultAddress
73+
uv run tq-oracle 0xYourVaultAddress
12174
```
12275

12376
**Run with custom config file:**
12477

12578
```bash
126-
tq-oracle --config path/to/custom-config.toml
79+
uv run tq-oracle --config path/to/custom-config.toml
12780
```
12881

12982
**Run with network override:**
13083

13184
```bash
132-
tq-oracle --network sepolia 0xYourVaultAddress
85+
uv run tq-oracle --network sepolia 0xYourVaultAddress
13386
```
13487

13588
**Preview configuration without running:**
13689

13790
```bash
138-
tq-oracle --show-config
91+
uv run tq-oracle --show-config
13992
```
14093

14194
**Increase verbosity for debugging:**
14295

14396
```bash
144-
tq-oracle --log-level DEBUG
97+
uv run tq-oracle --log-level DEBUG
14598
```
14699

147-
**Common Usage Patterns:**
148-
149-
- **Dry-run on mainnet**: Preview report generation without submitting to chain
150-
- **Testnet execution**: Run against testnet with Safe multi-sig for testing
151-
- **Pre-deployment testing**: Test with empty vaults using ignore flags
152-
153100
## Architecture
154101

155102
```sh
156103
src/tq_oracle/
157-
├── main.py # CLI entry point (Typer)
158-
├── settings.py # Configuration management (pydantic-settings)
159-
├── state.py # Application state container
160-
├── pipeline/ # Orchestration pipeline
161-
├── domain/ # Core domain models
162-
├── adapters/ # Protocol adapters
163-
├── processors/ # Data processing utilities
164-
├── report/ # Report generation and publishing
165-
├── safe/ # Safe transaction building
166-
├── checks/ # Pre-flight validation orchestration
167-
└── abis/ # Contract ABIs (JSON)
104+
├── main.py # CLI entry point (Typer)
105+
├── settings.py # Configuration management (pydantic-settings)
106+
├── state.py # Application state container
107+
├── abi.py # ABI loading utilities
108+
├── constants.py # Application constants
109+
├── logger.py # Logging configuration
110+
├── pipeline/ # Orchestration (preflight → assets → pricing → report)
111+
├── adapters/ # Protocol adapters
112+
│ ├── asset_adapters/ # Fetch asset balances (e.g. idle, stakewise, streth)
113+
│ ├── price_adapters/ # Fetch asset prices (cow_swap, eth, pyth)
114+
│ ├── check_adapters/ # Pre-flight checks (active_submit, timeout)
115+
│ └── price_validators/ # Price validation
116+
├── processors/ # Data aggregation and TVL computation
117+
├── checks/ # Pre-flight validation orchestration
118+
├── report/ # Report encoding, generation, and publishing
119+
├── abis/ # Contract ABIs (JSON)
120+
└── tests/ # Mirrors src structure
168121
```
169122

170-
## Pre-Flight Checks
171-
172-
Before processing TVL data, TQ Oracle runs automated pre-flight validation checks to ensure data integrity:
173-
174-
- **Safe State Validation**: Ensures no duplicate or pending reports exist
175-
- **Check Retry Logic**: Automatically retries failed checks with exponential backoff when recommended
176-
177-
These checks prevent race conditions and ensure accurate TVL snapshots by detecting ongoing cross-chain transfers that could affect asset balances. You can bypass individual guards when needed via the CLI flags `--ignore-empty-vault`, `--ignore-timeout-check/--enforce-timeout-check`, and `--ignore-active-proposal-check/--enforce-active-proposal-check`.
178-
179-
## Adding New Adapters
180-
181-
### Asset Adapters
182-
183-
Asset adapters fetch asset holdings from specific protocols (e.g., Aave, Lido).
184-
185-
Quick overview:
186-
187-
1. **Create adapter file** in `src/tq_oracle/adapters/asset_adapters/` implementing `BaseAssetAdapter`
188-
2. **Register adapter** in `src/tq_oracle/adapters/asset_adapters/__init__.py`'s `ADAPTER_REGISTRY`
189-
3. **Write integration tests** in `tests/adapters/asset_adapters/`
190-
4. **Add asset addresses** to `src/tq_oracle/constants.py` if needed
191-
192-
The adapter name in the registry is used in the `[[subvault_adapters]]` configuration's `additional_adapters` field.
193-
194-
### Price Adapters
195-
196-
Price adapters fetch USD prices for assets from price oracles (e.g., Pyth).
197-
198-
1. **Create adapter file** in `src/tq_oracle/adapters/price_adapters/` implementing `BasePriceAdapter`
199-
2. **Register adapter** in `src/tq_oracle/adapters/price_adapters/__init__.py`'s `PRICE_ADAPTERS` list
200-
3. **Implement async `fetch_prices()` method** to query oracle and return price data
201-
4. **Write unit tests** in `tests/adapters/price_adapters/`
202-
203-
### Price Validators
204-
205-
Price validators cross-check prices from the main price adapters against reference sources to detect anomalies or manipulation. They run after price fetching and can issue warnings or halt execution if prices deviate beyond configured thresholds.
206-
207-
1. **Create validator file** in `src/tq_oracle/adapters/price_validators/` implementing `BasePriceValidator`
208-
2. **Register validator** in `src/tq_oracle/adapters/price_validators/__init__.py`'s `PRICE_VALIDATORS` list
209-
3. **Implement async `validate_prices()` method** to cross-check prices and return validation results
210-
4. **Configure tolerance thresholds** in settings for warning and failure levels
211-
5. **Write unit tests** in `tests/adapters/price_validators/`
212-
213-
Validators respect tolerance thresholds configured in `settings.py`:
214-
215-
- `price_warning_tolerance_percentage` (default: 0.5%) - Issues warnings
216-
- `price_failure_tolerance_percentage` (default: 1.0%) - Halts execution
217-
218-
## Development
219-
220-
- **Install dependencies**: Use `uv sync --all-extras` for development dependencies
221-
- **Run tests**: Execute test suite with pytest
222-
- **Lint code**: Check code quality with ruff
223-
- **Format code**: Apply consistent formatting with ruff format
224-
225-
---
123+
> [!IMPORTANT]
124+
> See [ARCHITECTURE.md](./ARCHITECTURE.md) for complete rundown.
226125
227126
## External Links
228127

0 commit comments

Comments
 (0)