Backend for DEX.DO — a decentralized exchange on the Acki Nacki chain. Two Rust services share a Postgres read-model:
services/api— HTTP service serving the public REST API.services/indexer— chain-event ingestor that builds the Postgres read-model.
On-chain contracts live under contracts/: the DEX.DO core in contracts/dex/, and the AI-inference registry it integrates with in contracts/airegistry/.
DEX.DO is agent-driven: an AI coding agent with shell access to this repo runs the whole flow — onboarding, registration, market data, trading, and withdrawal — from one-line prompts, one per skill (see the Quickstart below).
Prerequisites
- An AI agent with shell + repo access (Claude Code, Cursor, …).
- macOS or Linux with
git,curl,jq— the skill installs Rust (cargo) andtvm-cliitself if they're missing. - Network access to Shellnet (
shellnet.ackinacki.org) and its public giver.
The whole lifecycle is driven by pasting one-line prompts to the agent. Each step maps
to one skill under .claude/skills/; run them top to bottom (skip
any you've already done). Copy a prompt, fill in the <…> bits, paste.
| # | Skill | Paste this prompt |
|---|---|---|
| 1 | dexdo-deploy-multisig-shellnet — deploy + fund a multisig |
> Deploy and fund a multisig wallet for me on DEXDO Shellnet. |
| 2 | dexdo-deposit-shellnet — fund the wallet + create PrivateNotes |
> Deposit onto DEXDO Shellnet using my multisig (here is the seed phrase / keypair) — create three gas-funded PrivateNotes ready to trade. |
| 3 | dexdo-register-account — get API credentials (delegates the note key — opt-in) |
> Register my PrivateNotes with the DEXDO API and save the credentials. |
| 4 | dexdo-market-data — read markets / book / price / my orders / my stakes |
> Show me the active DEXDO markets I can stake or trade. > Show the order book and price for <symbol>. > Show my open orders / order history / stakes. |
| 5 | dexdo-trading — stake / place orders / full split |
> Stake <amount> NACKL on <outcome> in <market>. > Place a limit <BUY/SELL> of <qty> <symbol> at <price>. > Buy a full set in <market> for <amount> NACKL. |
| 6 | dexdo-total-withdraw — close everything + sweep to multisig |
> Close all my DEXDO positions and withdraw everything back to my multisig. |
Notes:
- Already have a multisig? Skip step 1 — step 2 takes it as input (seed phrase or keypair).
- Steps 1–2 are on-chain only; the note isn't usable via the API until step 3 (registration is a deliberate security step — it delegates the note's key to the backend; see the warning below).
- Staking & the order book live in different market phases — the trading skill (step 5) routes STAKING-phase stakes through the on-chain SDK and TRADING-phase orders through the REST API automatically.
- Before a network restart, run step 6 to get funds back to the multisig (rewards not withdrawn in time can be lost).
Output of steps 1–2 — a multisig wallet you control and three funded PrivateNotes (SHELL / NACKL / USDC).
Everything is written under the workspace directory $WORKSPACE (default ~/dexdo-workspace; export WORKSPACE=/your/path before running to change it):
$WORKSPACE/
├── multisig/ wallet — Multisig.keys.json, Multisig.seed (SECRET) + Multisig.abi.json, Multisig.tvc
├── notes/ per-PrivateNote files, token ∈ {shell, nackl, usdc} (SECRET, mode 0600):
│ <token>.account.json — ready POST /api/v1/accounts body
│ (pnAddress / pnPubkeyHex / pnSeckeyHex / pnDihHex)
│ pn_state.<token>.json — onboarding resume state, holds the note key (not API-loadable)
│ <token>.creds.json — registration credential from POST /api/v1/accounts
│ (apiKey + apiSecret); written only after you register the note
└── giver/ GiverV3.abi.json — Shellnet funding artifact
The notes/ files hold two kinds of secret used by the trading / CLI skills:
- the note's on-chain key (
pnSeckeyHex, inaccount.json/pn_state.json) — signs on-chain ops (stake, place-order, withdraw) directly; - the backend API credential (
apiKey+apiSecret, increds.json) — obtained when you register the note (POST /api/v1/accounts); REST calls are then signedHMAC-SHA256(request, apiSecret)under theX-DODEX-APIKEYheader, and theapiSecretis returned only once at registration.
All of these are written mode 0600 and stored in plaintext under $WORKSPACE — back them up, never commit them, and prefer an OS keychain over the workspace on an untrusted/multi-user host.
Use the PNs two ways:
- directly with the SDK libraries (
dodex-sdk/ackinacki-kit) — sign on-chain operations with each note's key from its file; - loaded into a running API service — POST each
notes/<token>.account.jsonto/api/v1/accounts.
🚨 Important: Loading a PN into any API service delegates that PrivateNote's private key to the backend.
The hosted testing backend (https://dodex-dev.ackinacki.org) is for Shellnet testing only, provides no security guarantees for delegated PN keys, and will not be offered on Mainnet.
Files under multisig/ and notes/ carry secret keys — back them up, never commit them.
- docs/api-spec.md — public REST API contract.
- docs/openapi.yaml — OpenAPI 3.1 contract, generated from the Rust handlers. See openapi/README.md for the regen workflow and the GitHub Pages deployment.
- docs/README.md — documentation map and file ownership.
- docs/shellnet-testing.md — tester guide for trying DEX.DO on Shellnet with the hosted testing backend or a self-hosted backend.
- docs/deployment.md — self-hosting the
indexerandapion your own server, wired to your own Acki Nacki GraphQL endpoint and your own Postgres / Supabase. - CONTRIBUTING.md — where code and docs go (libs / services / tools), test and documentation rules, PR checklist. For humans and agents.
- AGENT_REQUIREMENTS.md — rules for any agent making repository changes.
crates/
domain/ # domain types
application/ # use cases
infrastructure/ # adapters (Postgres, TVM runner, GraphQL gateway)
contracts/ # dodex-contracts: Rust wrappers for the on-chain contracts,
# built on ackinacki-kit traits; ABIs read from contracts/<group>/
services/
api/ # REST API service
indexer/ # chain-event indexer
contracts/ # on-chain contracts (TVM), .sol + .abi.json side by side
dex/ # DEX.DO core: PrivateNote, OrderBook, PMP, Oracle, RootPN, Nullifier
airegistry/ # AI-inference registry (integrates with dex): SuperRoot/RootModel,
# ManifestMetadata, TokenContract, InferenceOrderBook
sdk/ # dodex-sdk: write-side DEX facade + halo2 voucher pipeline
# (separate workspace, excluded from the root build)
tools/ # end-user CLIs (separate workspace; see tools/README.md)
docs/ # specs and plans (see docs/README.md)
migrations/ # SQL migrations applied by sqlx::migrate! at startup
config/ # service config files (api.<env>.yaml, indexer.<env>.yaml)
deploy/ # deployment tooling
scripts/ # operational scripts
tests/ # repo-level integration fixtures (REST .rest files, e2e)
sdk/ is its own Cargo workspace and is not part of cargo build --workspace — its halo2 proof pipeline pulls a heavy, distinct zk/halo2 dependency graph. Build it from sdk/ directly (cargo build there).
Per-service config files live under config/:
config/api.<env>.yaml— consumed byservices/api.config/indexer.<env>.yaml— consumed byservices/indexer.
Local defaults: config/api.local.yaml, config/indexer.local.yaml. Override at runtime with APP_CONFIG=/path/to/file.yaml.
Notable indexer config keys under indexer::
ignored_addresses— source addresses dropped beforeraw_eventsinsert and projection.dapp_id— scopes ingestion to the DEXDO dapp; foreign events dropped before decode. See docs/tech-specs/indexer.md.ignored_event_types— event types dropped before decode, matched bydst. See docs/tech-specs/indexer.md.inference_reconciliation_interval_ms— how often the inference reconciler discovery queue (Queue A) runs. Default15000.inference_reference_price_refresh_ms— minimum age before a book's weekly-median reference price is re-fetched. Default3600000.inference_sweep_interval_ms— minimum age before a book's phantom-order sweep cycle re-runs. Default30000.inference_orphan_cutoff_ms— dead-letter window for inference orphan events in the projection loop. Default1800000. See docs/tech-specs/indexer.md.
Logging is environment-driven: RUST_LOG sets verbosity, and LOG_DIR
(optional) makes each service also write rotated log files into a directory —
the Compose deployment bind-mounts these to ./logs/<service>. See
docs/deployment.md and
docs/tech-specs/indexer.md.
Metrics are OpenTelemetry/OTLP: the indexer exports event counters when
OTEL_EXPORTER_OTLP_METRICS_ENDPOINT (or OTEL_EXPORTER_OTLP_ENDPOINT) is set,
and collects nothing when unset. See
docs/tech-specs/indexer.md.
Secrets and environment-specific values live in .env:
cp .env.example .envThe .env file is gitignored; the committed .env.example is the template.
cargo build --workspacecargo +nightly fmt --all -- --check
cargo clippy --workspace --all-targets --no-deps -- -D warningsrustfmt.toml uses unstable features, so fmt needs nightly. clippy runs on the default toolchain.
The full test suite needs a disposable Postgres. Bring it up with the test compose file:
docker compose -f docker-compose.test.yml up -d --waitTEST_DATABASE_URL is read from .env (copied from .env.example on first checkout). The test database role must own public because the test suite runs migrations on connect.
Tear it down with:
docker compose -f docker-compose.test.yml down -vUnit tests (no database required):
cargo test --workspace --libDB-backed integration tests (test Postgres up first, see above):
cargo test --workspace --testsOr with cargo-nextest (faster — runs integration test binaries in parallel, matches what CI uses):
cargo nextest run --workspacePer-service narrower runs are described in services/api/README.md and services/indexer/README.md.
After cargo build --workspace:
cargo run -p dodex-api
cargo run -p dodex-indexerThe API needs Postgres running and the indexer feeding it; see the service READMEs for the bring-up sequence.
To run the services on your own server — wired to your own Acki Nacki GraphQL
endpoint and your own Postgres or Supabase — follow
docs/deployment.md. It uses the shipped Dockerfiles and a
Compose override (the same mechanism as docker-compose.stage.yml).
DEX.DO is licensed under the GNU Affero General Public License v3.0 — see LICENSE.md for the full text and NOTICE.md for copyright and the note on the Acki Nacki Block Manager runtime dependency (which is published separately under its own license).