SPICE is a temporal fee-timing research pipeline for EVM chains. It acquires canonical block data, builds feature tables, constructs temporal decision problems, trains neural models, tunes studies, stores artifacts, and evaluates fee-timing decisions under real delay budgets.
The project is organized around explicit seams: configs resolve into typed contracts, contracts compile concrete implementations, workflows orchestrate storage effects, and docs are split into generic architecture and concrete implementation guides.
Read in this order if you are new to the codebase and the domain:
README.md
-> ARCHITECTURE.md
-> src/spice/ARCHITECTURE.md
-> src/spice/conf/IMPLEMENTATIONS.md
-> package ARCHITECTURE.md files
-> package IMPLEMENTATIONS.md files
Use this rule:
| File | Purpose |
|---|---|
ARCHITECTURE.md |
Generic structure, boundaries, data flow, dependency direction. |
IMPLEMENTATIONS.md |
Current concrete engines, families, algorithms, YAML Surfaces/Config Groups, math, and failure modes. |
Architecture docs explain the shape of the system. Implementation docs explain what the current code actually runs.
This path explains how raw block rows become model predictions:
features
-> temporal problems
-> dataset builders
-> model families
-> prediction families
-> objectives
-> evaluation
Read:
- Feature implementations
- Temporal compiler implementations
- Execution policy implementation
- Input normalization implementations
- Dataset builder implementations
- Model family implementations
- Prediction family implementations
- Objective implementations
- Evaluator implementations
The key mental model:
canonical block rows
-> feature matrix
-> temporal problem store
-> sequence batches
-> neural model
-> decoded offsets
-> evaluator metrics
This path explains how chain data is downloaded, validated, committed, indexed, and transferred:
RPC acquisition
-> Corpus Assembly
-> corpus validation
-> storage roots
-> catalog
-> transfer
Read:
- RPC acquisition implementations
- Corpus implementations
- Storage implementations
- Catalog implementations
The key mental model:
timestamp window
-> block range
-> RPC batches
-> canonical parquet rows
-> root state DB
-> derived catalog row
This path explains how users run work:
YAML Surfaces and Config Groups
-> config resolution
-> CLI command
-> local workflow or remote submit
-> storage effect
Read:
- Config implementations
- Config resolution implementations
- Workflow implementations
- Execution implementations
- CLI command implementations
The key mental model:
surface name + overrides
-> resolved workflow config
-> acquire/train/tune/evaluate
-> corpus/study/artifact state
| Term | Meaning |
|---|---|
| Corpus | Stored canonical block data for one chain/dataset/evaluation date. |
| History rows | Rows before the evaluation window, used for training and warmup. |
| Evaluation rows | Rows in the evaluation day, used for diagnostic replay. |
| Feature | Numeric observable derived from block rows. |
| Sample | One temporal decision example. |
| Anchor row | Row representing the decision time for a sample. |
| Context rows | Past rows the model may observe. |
| Candidate window | Future row interval the model may choose from. |
| Candidate offset | Integer action relative to the candidate-window start. |
| Execution policy | Rule that maps decoded offsets to actual outcome rows. |
| Decoded Result ABI | Typed prediction output contract consumed by evaluators. |
| DecodedOffsets | Current candidate-offset decoded result ABI. |
| Artifact | Persisted trained model plus exact manifest and runtime state. |
| Study | Persisted tuning state and Optuna trial database. |
| Evaluator | Runtime scorer that turns decoded predictions into metrics. |
| Surface | High-level YAML recipe combining chain, dataset, model, features, problem, objective, and evaluation. |
| Tool | Use |
|---|---|
| Typer | CLI |
| Pydantic + PyYAML | Config models and YAML loading |
| SQLAlchemy Core | SPICE-owned SQLite state |
| Polars | Corpus IO and validation |
| PyTorch | Modeling |
| Optuna | Tuning |
| web3.py | RPC access |
brew install uv
uv sync --extra dev
source .venv/bin/activateuv manages the repo-local .venv/. Without activation, prefix commands with uv run.
Push to both remotes:
git push origin main
git push university mainLocal acquisition:
spice acquire --surface current_row_fee_dynamicsRemote train/tune/evaluate submission:
spice train --surface current_row_fee_dynamics --dataset-id cor_9a73b1e88edb488afb1e
spice tune --surface current_row_fee_dynamics --dataset-id cor_9a73b1e88edb488afb1e --trial-count 20
spice evaluate --artifact-id art_... --dataset-id cor_9a73b1e88edb488afb1e --evaluation poisson_replayThe CLI owns the default remote target, disi_l40. Train, tune, and evaluate submit remotely by default; Python workflow runners remain available for the remote runner and tests.
Config and storage inspection:
spice config list provider
spice config show dataset icdcs_2026
spice config edit problem current_row_nominal
spice show dataset
spice show artifact --artifact-id art_...
spice delete artifact --artifact-id art_...
spice refresh catalogTransfer:
spice transfer push dataset --dataset-id cor_9a73b1e88edb488afb1e
spice transfer pull artifact --artifact-id art_...| Seam | Current ids |
|---|---|
| Features | core_fee_dynamics |
| Temporal compilers | observed_time_window |
| Execution policies | strict_deadline_miss |
| Input normalization | row_standard, window_weighted_standard |
| Dataset builders | fixed_sequence_temporal |
| Model families | lstm, transformer, transformer_lstm |
| Prediction families | min_block_fee_multitask |
| Evaluators | poisson_replay, full_temporal_replay |
| Remote target | disi_l40 |
outputs/
.spice/catalog.sqlite
corpora/<chain>/<corpus_id>/
history/
evaluation/
.spice/state.sqlite
studies/<chain>/<study_id>/
.spice/state.sqlite
artifacts/<chain>/<artifact_id>/
model.pt
.spice/state.sqlite
Root state DBs and manifests are source of truth. The catalog is derived and can be refreshed.
uv run ruff check .
uv run pyright
uv run vulture
uv run pytest -qvulture runs at min_confidence = 90 from pyproject.toml. Treat its output as review input, not proof: manually verify every reported item before deleting code because dynamic Python usage can hide real references.
YAML specs can be validated through raw config group loading:
uv run python - <<'PY'
from spice.config.groups import load_named_group_payload, named_group_keys, list_group_names
count = 0
errors = []
for group in named_group_keys():
for name in list_group_names(group):
try:
load_named_group_payload(name, group)
except Exception as exc:
errors.append((group, name, type(exc).__name__, str(exc)))
count += 1
print(f"validated={count} errors={len(errors)}")
for error in errors:
print(error)
PY