Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@ jobs:
# mock addresses from the mocked test environment, not real
# deployed addresses, and are committed deliberately for
# reproducibility (see CONTRIBUTING.md's testing section).
if grep -rnE --exclude-dir=.git --exclude-dir=target --exclude-dir=test_snapshots '\bC[A-Z2-7]{55}\b' .; then
# DEPLOYMENT.md is excluded: the canonical testnet instance's
# contract id is published there deliberately (see CONTRIBUTING.md).
if grep -rnE --exclude-dir=.git --exclude-dir=target --exclude-dir=test_snapshots --exclude=DEPLOYMENT.md '\bC[A-Z2-7]{55}\b' .; then
echo "::error::Found what looks like a Stellar contract address committed to the repo. Contract addresses must never be committed, since a deployed address can't be tied to a specific source commit without an independent rebuild. Use placeholder env vars in examples instead (see CONTRIBUTING.md)."
exit 1
fi
Expand Down
7 changes: 7 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,13 @@ deploy it yourself (or have CI do it) from the PR's actual source; a pasted addr
is never sufficient proof on its own. CI blocks any literal Stellar contract
address from being committed at all, as a backstop.

**One deliberate exception:** the canonical testnet instance's contract id is
published in `docs/src/DEPLOYMENT.md` ("Canonical testnet instance"), and CI
exempts that file. The point of a canonical instance is a stable, discoverable
id that integrators point at — the ban is on addresses in *application source*
and in PR *evidence*, not on documenting the canonical deployment. Everywhere
else the ban stands.

## Commit messages

One-line, imperative, conventional-commit style: `feat:`, `fix:`, `docs:`, `test:`,
Expand Down
4 changes: 2 additions & 2 deletions demos/freelance-escrow/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,8 @@ point this at a deployed Tholos instance yourself:

```sh
cp .env.example .env.local
# fill in VITE_THOLOS_CONTRACT_ID with a deployed contract id
# (see docs/src/DEPLOYMENT.md to deploy one)
# fill in VITE_THOLOS_CONTRACT_ID with the canonical testnet instance's
# contract id (published in docs/src/DEPLOYMENT.md, "Canonical testnet instance")

pnpm install
pnpm dev
Expand Down
44 changes: 44 additions & 0 deletions docs/src/DEPLOYMENT.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,50 @@ A practical guide for deploying a Tholos instance and operating it afterward. Fo
what each function does, see [CONTRACT.md](CONTRACT.md). For design rationale,
see [ARCHITECTURE.md](ARCHITECTURE.md).

## Canonical testnet instance

A long-lived, canonical Tholos instance is deployed on **testnet** for
integrators (this repo's own demos included) to point at by default, instead of
each deploying their own throwaway instance:

| | |
| --- | --- |
| Network | Testnet |
| Contract id | `CBFFQEFDGSLLLQKOZGHWNTSPBUWMAVKFFKYXUHCMP6WDR5Y4HN4I2C76` |
| Admin | `GCULSMS2EFKYYBGVYKFIMHEE4T4OFGNVVWXDQW7HZ4E2VWDPF5HJAQQH` |
| Token | Native XLM SAC (`stellar contract id asset --asset native`) |
| `bond_amount` | 50000000 (5 XLM) |
| `challenge_window_secs` | 21600 (6 hours) |
| `finalize_reward_bps` | 100 (1 %) |
| Resolver committee | `GBR745BR3WUHXPADLUKV5J25IUFNUAXGFWFQKQCWKNSK6LKZBTDRBA44`, `GDH2Z5JRUMZCIJ72TNWZ4I337QSCFU52O5KKVF2K6IDI3ZSIHS7DQMHC`, `GAJNSUJB624FUIEWUOSZRHRXGSCDPUTCIKCD2T4QY5CPJMC7C4IR3IYM` |

**Parameter rationale** (using the model in [BOND_SIZING.md](BOND_SIZING.md)):

- `bond_amount` = 5 XLM: with `R_case` ≈ 20 tokens of resolver review time and
`K_spam` = 10 tolerated spam assertions per window, the low-value assertion
spam floor is `R_case / K_spam` = 2 tokens; for a public deployment the model
recommends a 2x–5x multiple, putting the bond in the 4–10 token range. 5 XLM
sits inside that band and stays affordable for the smallest assertions this
low-value instance is meant to support.
- `challenge_window_secs` = 6 h: long enough for someone who'd actually catch a
bad assertion to see it and act, without stalling finality for integrators
that assert frequently.
- `finalize_reward_bps` = 100 (1 %): a small haircut that keeps prompt
third-party finalization economically worthwhile without materially
penalizing asserters.
- Resolver committee: three dedicated, non-reused testnet identities. **These
are testnet-only stopgaps** — a canonical instance needs real, accountable
resolver identities before it should hold anything beyond play value; see
[SECURITY.md](SECURITY.md).

This instance is the default target of the [integration guidance](INTEGRATION.md)
and of `demos/freelance-escrow` (via its `VITE_THOLOS_CONTRACT_ID` env var).
The contract id is published here deliberately; the repository's
never-commit-contract-addresses CI check exempts this file (see
[CONTRIBUTING.md](CONTRIBUTING.md)) because the whole point of a canonical
instance is a stable, discoverable id — the ban is on addresses in
*application source*, not in deployment documentation.

## Before you deploy

**This is testnet-only until audited.** See [SECURITY.md](SECURITY.md). Don't
Expand Down
15 changes: 11 additions & 4 deletions docs/src/INTEGRATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,19 @@ you're looking for the function-by-function reference instead, see

## Should you deploy your own instance, or share one?

Each Tholos deployment is initialized once with a single token, bond amount,
challenge window, and resolver committee (`initialize` in [CONTRACT.md](CONTRACT.md)).
There's no per-call override. That means:
A canonical testnet instance already exists (see the "Canonical testnet
instance" section of [DEPLOYMENT.md](DEPLOYMENT.md) for its contract id and
parameters). **Share that instance by default** and only track the assertion
`id`s that belong to you: an oracle's trust accumulates from one committee's
track record over time, and fragmenting across many bespoke deployments resets
that record for every integrator.

A separate deployment is the **exception**, not the default path — reach for it
only when your bond size, token, or challenge window is genuinely incompatible
with the canonical instance:

- If your markets all want the same bond size, token, and challenge window, they can
share one deployed instance and just track the assertion `id`s that belong to them.
share the canonical instance and just track the assertion `id`s that belong to them.
- If you need different bond sizes per market (a $10 market and a $10,000 market
probably shouldn't share a bond amount), deploy a separate instance per
configuration, or wait for a future version that supports per-call bonds.
Expand Down