GreenFloor is a long-running Python application for Chia CAT market making.
greenfloor-manager: manager CLI for config validation, key onboarding, coin inventory/reshaping, offer building/posting, and operational checks.greenfloord: daemon process that evaluates configured markets, executes offers, and emits low-inventory alerts.greenfloor-webui: local aiohttp API server that backs the browser-based operator dashboard.- Sage Wallet integration: when Sage is installed and running locally,
greenfloor-manager build-and-post-offeruses the Sage RPC (127.0.0.1:9257) to sign and construct offers via mTLS — no separate key-onboarding required.
- The current implementation plan is tracked in
docs/plan.md. - Operator deployment/recovery runbook is in
docs/runbook.md. - Syncing, signing, and offer-generation baseline: GreenFloor uses
chia-wallet-sdk(included as a repo submodule) for default blockchain sync/sign and offer-file execution paths.
- Offer files are plaintext Bech32m payloads with prefix
offer1.... - In
chia-wallet-sdk, offer text is an encoded/compressedSpendBundle(encode_offer/decode_offer). - Manager offer publishing validates offer text with
chia-wallet-sdkparse semantics (Offer::from_spend_bundle) before Dexie submission. - Venue submission (
DexieAdapter.post_offer) sends that exact offer text string as theofferfield toPOST /v1/offers.
- Every posted offer should have an expiry.
- Stable-vs-unstable pairs should use shorter expiries than other pair types.
- Explicit cancel operations are rare and policy-gated.
- Cancel is only for stable-vs-unstable pairs and only when unstable-leg price movement is strong enough to justify early withdrawal.
- Default behavior remains expiry-driven rotation instead of frequent cancel/repost churn.
Bootstrap home directory first (required for real deployment):
python -m pip install -e ".[dev]"
greenfloor-manager bootstrap-homeValidate config and check readiness:
greenfloor-manager config-validate
greenfloor-manager doctor
# Script-friendly compact JSON (default output is pretty JSON):
greenfloor-manager --json doctorOnboard signing keys (BLS / cloud-wallet path only — not needed for Sage):
greenfloor-manager keys-onboard --key-id <your-key-id>Build and post offers:
# Dry-run preflight
greenfloor-manager build-and-post-offer --pair ECO.181.2022:xch --size-base-units 1 --dry-run
# Sell side: offer base CAT, request XCH (default)
greenfloor-manager build-and-post-offer --pair ECO.181.2022:xch --size-base-units 1 --side sell
# Buy side: offer XCH, request base CAT
greenfloor-manager build-and-post-offer --pair ECO.181.2022:xch --size-base-units 1 --side buy
# testnet11
greenfloor-manager build-and-post-offer --pair TDBX:txch --size-base-units 1 --network testnet11--side controls which leg is offered:
sell(default): offersizeunits of the base CAT, request XCH atsell_usd_per_basepricing.buy: offer XCH equivalent, requestsizeunits of the base CAT atbuy_usd_per_basepricing.
When Sage is installed and running, GreenFloor detects its mTLS certificates automatically and routes offer construction through the Sage RPC instead of the BLS signing path. No key-onboarding step is needed.
# Sage certs are auto-detected from the OS data directory:
# Windows: %APPDATA%\com.rigidnetwork.sage\ssl\wallet.{crt,key}
# macOS: ~/Library/Application Support/com.rigidnetwork.sage/ssl/wallet.{crt,key}
# Linux: ~/.local/share/com.rigidnetwork.sage/ssl/wallet.{crt,key}
# With Sage running, build-and-post-offer automatically uses Sage make_offer:
greenfloor-manager build-and-post-offer --pair BYC:xch --size-base-units 1 --side sell --dry-run
greenfloor-manager build-and-post-offer --pair BYC:xch --size-base-units 1 --side buy --dry-runSee SAGE_WALLET_API.md for the full Sage RPC client reference.
Coin operations (cloud wallet / BLS path):
# List coin inventory (XCH + CAT)
greenfloor-manager coins-list
# Split one coin into target denominations (waits through signature + mempool + confirmation + reorg watch)
greenfloor-manager coin-split --pair TDBX:txch --coin-id <coin-id> --amount-per-coin 1000 --number-of-coins 10
# Combine small coins into one larger coin (waits through signature + mempool + confirmation + reorg watch)
greenfloor-manager coin-combine --pair TDBX:txch --input-coin-count 10 --asset-id xchCoin-op wait diagnostics include:
signature_wait_warningandsignature_wait_escalation(soft-timeout style, manager keeps waiting).in_mempoolwith acoinset_urlon every mempool user event.confirmedplus read-only Coinset reconciliation metadata.reorg_watch_*events while waiting for six additional blocks after first confirmation.
On testnet11, use txch as the quote symbol in pair arguments (for example TDBX:txch).
Check offer status and reconcile:
greenfloor-manager offers-status
greenfloor-manager offers-reconcileoffers-reconcile now emits canonical taker-detection signals from offer-state transitions (taker_signal) and keeps mempool/chain status pattern checks as advisory diagnostics (taker_diagnostic).
Run the daemon:
greenfloord --program-config config/program.yaml --markets-config config/markets.yaml --onceGreenFloor ships a browser-based operator dashboard backed by a local aiohttp server.
# Install Node dependencies once:
npm install
# Start API server + Vite dev server together:
npm run dev
# API: http://127.0.0.1:8765
# UI: http://127.0.0.1:3000npm run dev launches two processes via concurrently:
node scripts/dev-api.js— spawnspython -m greenfloor.webui --port 8765from the project.venv.vite --port 3000— servessrc/main.js+src/style.csswith hot-module replacement.
npm run build # outputs to dist/
greenfloor-webui # serves dist/ + API on 127.0.0.1:8765All endpoints are served by greenfloor.webui.server (registered in create_app()):
| Method | Path | Description |
|---|---|---|
| GET | /api/doctor |
Run doctor check; returns problems/warnings/config paths |
| GET | /api/config-validate |
Run config-validate |
| GET | /api/config-paths |
Return resolved program + markets config paths |
| GET | /api/config-read |
Read raw YAML config files |
| POST | /api/config-write |
Write updated YAML config files |
| GET | /api/offers-status |
List stored offer states with pair enrichment (base_symbol, quote_asset) |
| POST | /api/offers-reconcile |
Reconcile open offers against Dexie/chain |
| GET | /api/coins-list |
List coin inventory |
| POST | /api/build-offer/stream |
SSE stream — builds + posts an offer (calls build-and-post-offer) |
| POST | /api/coin-split/stream |
SSE stream — split a coin |
| POST | /api/coin-combine/stream |
SSE stream — combine coins |
| GET | /api/sage-rpc/status |
Sage RPC reachability + sync status |
| GET | /api/sage-rpc/keys |
List Sage wallet keys |
| POST | /api/sage-rpc/login |
Login to a Sage fingerprint |
| POST | /api/sage-rpc/call |
Raw passthrough to any Sage RPC endpoint |
| GET | /api/sage-rpc/coins |
List coins held by the active Sage key |
| GET | /api/sage-rpc/cats |
List CAT tokens held by the active Sage key |
| GET | /api/prices |
Fetch current XCH price (CoinCodex) |
| GET | /api/markets-list |
List configured markets with enabled/disabled status |
| POST | /api/markets-write |
Enable/disable or update a market in markets.yaml |
Install dev dependencies (includes pre-commit), then run local checks:
python3 -m venv .venv
source .venv/bin/activate
python -m pip install -e ".[dev]"
pre-commit run --all-filesOperator overrides (all optional):
GREENFLOOR_WALLET_EXECUTOR_CMD— override the default in-process signing path with an external executor subprocess for daemon coin-op execution.GREENFLOOR_OFFER_BUILDER_CMD— override the default in-process offer builder with an external subprocess for manager offer construction.GREENFLOOR_KEY_ID_FINGERPRINT_MAP_JSON— JSON map for key ID -> fingerprint; normally injected fromprogram.yamlsigner key registry by daemon path.GREENFLOOR_CHIA_KEYS_DERIVATION_SCAN_LIMIT— integer derivation depth scan limit for matching selected coin puzzle hashes (default200).GREENFLOOR_COINSET_BASE_URL— custom Coinset API base URL for coin queries andpush_tx; when unset,CoinsetAdapterdefaults to mainnet and can be forced to testnet11 by network selection.coin_ops.minimum_fee_mojos(in program config) — fallback minimum fee for coin operations when Coinset advice is unavailable (default10000000mojos /0.00001 XCH; can be set to0).
Cloud Wallet program config contract (program.yaml):
cloud_wallet.base_url— GraphQL API root URL.cloud_wallet.user_key_id— user auth key id used inchia-user-key-id.cloud_wallet.private_key_pem_path— PEM private key path for RSA-SHA256 header signatures.cloud_wallet.vault_id— target wallet/vault ID for coin and offer operations.
CI secret for optional live testnet workflow:
TESTNET_WALLET_MNEMONIC— importable wallet mnemonic used by.github/workflows/live-testnet-e2e.ymlforkeys-onboardmnemonic import.- Format: whitespace-delimited
12or24words (plain text mnemonic). - Testnet receive-address example for this wallet:
txch1t37dk4kxmptw9eceyjvxn55cfrh827yf5f0nnnm2t6r882nkl66qknnt9k.
Live testnet11 proof workflow (CI-only mnemonic path):
- Dispatch
.github/workflows/live-testnet-e2e.ymlfrom GitHub Actions. - Inputs:
network_profile(default:testnet11)pair(default:TDBX:txch)size_base_units(default:1)dry_run(falseto run live post/status/reconcile evidence path)
- The workflow always runs
doctor+ dry-runbuild-and-post-offer; whendry_run=falseit additionally runs livebuild-and-post-offer,offers-status, andoffers-reconcile. - Logs are uploaded as artifact
live-testnet-e2e-artifacts.
Testnet11 asset bootstrap helper workflow (G2):
- Dispatch
.github/workflows/testnet11-asset-bootstrap-helper.yml. - It discovers Dexie testnet CAT candidates and uploads
g2-testnet-asset-bootstrap-artifacts. - Use
markets-snippet.yamlfrom the artifact as a starter config snippet forsupported_assets_exampleandmarketsstanzas.
Signer key resolution contract is repo-managed through program.yaml:
keys.registry[].key_idmust match marketsigner_key_idkeys.registry[].fingerprintis required for deterministic signer mapping- optional
keys.registry[].networkis validated againstapp.network
V1 intentionally sends only one push notification type:
- low-inventory alerts for sell-side CAT/XCH assets.
Alert content includes ticker, remaining amount, and receive address.