Agent-first CLI and Python SDK monorepo for querying public WOOFi protocol data from the official WOOFi API.
The repository currently exposes these public read-only endpoints:
GET /statGET /source_statGET /yieldGET /stakingv2
It also reserves the future CLI namespaces quote and build-txes, which
currently return a deterministic placeholder response.
packages/woofi: synchronous Python client, endpoint fetchers, schemas, and normalization/summary servicespackages/woofi-cli:click-based CLI published as thewooficommand- root
pyproject.toml: shared dev tooling config for the monorepo cli_docs/: supporting product and technical notes
| Command | Upstream endpoint | Status |
|---|---|---|
stats series |
GET /stat |
implemented |
stats summary |
GET /stat |
implemented |
stats sources |
GET /source_stat |
implemented |
stats top-source |
GET /source_stat |
implemented |
yield vaults |
GET /yield |
implemented |
yield vault |
GET /yield |
implemented |
yield summary |
GET /yield |
implemented |
yield top |
GET /yield |
implemented |
staking info |
GET /stakingv2 |
implemented |
quote |
none yet | placeholder |
build-txes |
none yet | placeholder |
woofi-cli/
├── README.md
├── pyproject.toml
├── cli_docs/
├── packages/
│ ├── woofi/
│ │ ├── pyproject.toml
│ │ ├── src/woofi/
│ │ └── tests/
│ └── woofi-cli/
│ ├── pyproject.toml
│ ├── src/woofi_cli/
│ └── tests/
└── ...
The root package is workspace metadata. For local development, install the two leaf packages:
python3 -m venv .venv
source .venv/bin/activate
pip install -e ./packages/woofi
pip install -e "./packages/woofi-cli[dev]"
woofi --helpIf you use pyenv and the woofi command is missing after installation, run
pyenv rehash.
PYTHONPATH=packages/woofi/src:packages/woofi-cli/src \
python -m woofi_cli.main --help| Option | Default | Description |
|---|---|---|
| `--format json | table` | json |
--raw |
off | Return the raw upstream payload instead of the CLI envelope |
--pretty |
off | Pretty-print JSON |
--timeout INTEGER |
30 |
HTTP timeout in seconds |
Notes:
--networkis command-specific, not global.stakingis global and does not accept a network parameter.
Trading statistics built from /stat and /source_stat.
| Command | Purpose | Key options |
|---|---|---|
stats series |
bucketed trading series for one network and period | --period, --network, --select, --from-ts, --to-ts |
stats summary |
derived summary of total volume and tx counts | --period, --network, --from-ts, --to-ts |
stats sources |
source-level volume breakdown | --period, --network, --sort-by, --order, --limit, --include-other/--no-include-other |
stats top-source |
top source helper query | --period, --network, --exclude-other/--no-exclude-other |
Supported periods:
1d,1w,1m,3m,1y,all
Important guardrails:
tradersfrom/statis bucket-scoped only and must not be summed into a period-wide unique trader count.period=1wmay return only 6 days because of bucket boundaries. Useperiod=1mplus--from-tsand--to-tsif you need an exact 7-day window./source_statdoes not support Solana, sostats sourcesandstats top-sourcereject--network solana.
Earn vault data built from /yield.
| Command | Purpose | Key options |
|---|---|---|
yield vaults |
list vaults with filtering, sorting, and optional limit | --network, --symbol, --source, --sort-by, --order, --limit |
yield vault |
lookup a single vault by address | --network, --address |
yield summary |
derived network summary | --network |
yield top |
top N vaults ranked by APY or TVL-related metrics | --network, --sort-by, --limit |
Supported yield networks:
bsc,avax,polygon,arbitrum,optimism,linea,base,mantle,sonic,berachain
The CLI preserves raw vault fields and adds normalized helper fields such as:
addresstvl_token_decimaltvl_usd_decimal
Global WOO staking metrics from /stakingv2.
| Command | Purpose |
|---|---|
staking info |
APR breakdown and total WOO staked |
This endpoint is global and does not accept --network.
| Command | Current behavior | Exit code |
|---|---|---|
quote |
placeholder JSON error | 12 |
build-txes |
placeholder JSON error | 12 |
Both currently return:
{
"schema_version": "1.0",
"success": false,
"error": {
"code": "not_implemented",
"message": "building in progress..."
}
}| Network | stats |
stats sources |
yield |
staking |
|---|---|---|---|---|
bsc |
yes | yes | yes | global only |
avax |
yes | yes | yes | global only |
polygon |
yes | yes | yes | global only |
arbitrum |
yes | yes | yes | global only |
optimism |
yes | yes | yes | global only |
linea |
yes | yes | yes | global only |
base |
yes | yes | yes | global only |
mantle |
yes | yes | yes | global only |
sonic |
yes | yes | yes | global only |
berachain |
yes | yes | yes | global only |
hyperevm |
yes | yes | no | global only |
monad |
yes | yes | no | global only |
solana |
yes | no | no | global only |
Successful commands return a stable JSON envelope:
{
"schema_version": "1.0",
"success": true,
"command": "stats summary",
"params": {
"period": "1d",
"network": "arbitrum"
},
"data": {},
"meta": {
"endpoint": "/stat",
"generated_at": "2026-03-10T12:00:00+00:00",
"warnings": []
}
}--raw bypasses CLI normalization and returns the upstream payload for the
selected endpoint.
--format table prints a human-readable table instead of the JSON envelope.
This mode is for operators, not machine parsing.
Error payloads are written to stderr as JSON:
{
"schema_version": "1.0",
"success": false,
"error": {
"code": "invalid_argument",
"message": "network=solana is not supported for /source_stat",
"hint": "Use one of: bsc, avax, polygon, arbitrum, optimism, linea, base, mantle, sonic, berachain, hyperevm, monad"
}
}Precision is part of the public contract.
- raw integer-like upstream fields remain strings
- normalized money and quantity fields are emitted as strings
volume_usd,total_deposit, andtotal_woo_stakedare converted with 18 decimals- vault
tvlvalues from/yieldare converted using each vault's owndecimals
The implementation is layered on purpose:
- commands layer: CLI parsing, command-local validation, presentation choice
- API layer: one upstream endpoint per function, HTTP status handling, JSON parsing
- services layer: normalization, sorting, filtering, summaries, warnings
- presenters layer: JSON/table rendering and stdout/stderr separation
Current source layout:
packages/
├── woofi/
│ └── src/woofi/
│ ├── api/
│ ├── schemas/
│ ├── services/
│ ├── utils/
│ ├── client.py
│ ├── constants.py
│ └── exceptions.py
└── woofi-cli/
└── src/woofi_cli/
├── commands/
├── presenters/
├── schemas/
├── cli_context.py
├── exit_codes.py
└── main.py
| Code | Meaning |
|---|---|
0 |
success |
2 |
invalid argument |
3 |
upstream HTTP error |
4 |
upstream timeout |
5 |
invalid upstream payload |
12 |
not implemented |
# Trading stats
woofi stats summary --period 1d --network arbitrum
woofi stats series --period 1m --network base --select volume_usd
woofi stats sources --period 1d --network base --limit 3
woofi stats top-source --period 1d --network arbitrum
# Exact 7-day window using 1m plus timestamps
woofi stats summary --period 1m --network arbitrum --from-ts 1709251200 --to-ts 1709855999
# Earn vaults
woofi yield vaults --network arbitrum
woofi yield top --network base --sort-by tvl_usd --limit 2
woofi yield summary --network bsc
woofi yield vault --network arbitrum --address 0xA780432f495E5C6851fd7903FE49ad77C952F7D8
# Staking
woofi staking info
woofi --pretty staking info
# Human-friendly output
woofi --format table yield top --network arbitrum
# Raw upstream payload
woofi --raw stats series --period 1d --network solana# Test
pytest -q
# Lint
ruff check packages/woofi/src packages/woofi-cli/src packages/woofi/tests packages/woofi-cli/tests
# Type check
mypy packages/woofi/src packages/woofi-cli/srcRecommended verification loop after changes:
- Run focused tests for the modified package or command.
- Run
pytest -q. - Run
ruff checkon both package source trees and tests. - Run
mypyon both package source trees. - Run a few live sample commands against the real WOOFi API when network access is available.