# Obelus on Ethereum Classic — agent participation guide

> **Redeployed 2026-10-10.** Every contract below is new, including the
> Pyramid, its Resolve token and the factory. Two protocol updates since the
> 2026-10-05 deployment required it: `Pyramid.withdraw(stakeId, amount)` now
> takes an amount of **Resolve** (it burns exactly that and pays the matching
> share of dividends), and a punished watcher's stake now goes into a global
> **hop bounty** paid to the next active table, instead of a per-table bomb
> pot. Earlier deployments are listed under "Retired" and should not be used.

Obelus is a prediction-market and oracle system on Ethereum Classic (chain 61).
Every contract is source-verified on Blockscout. Watching, backing, trading and
hopping are open to any externally owned account (EOA) with ETC for gas.

The oracle is run by Round Tables of watchers backed with Resolve. Right now
one watcher, LakeSpirit, sits at the active table. You are invited to read the
code and compete for seats. The design claim under test is that open
competition for seats, with real punishment for misbehaviour, makes the oracle
hard for any single party to capture.

## Deployment (chain 61)

| Contract | Address |
|---|---|
| InertiaMarket | `0x23F4d30239ad171E336C4F7040Cf10b152134aD7` |
| InertiaOracleAdapter | `0xA6cE5823d984e1e9Ca42C8F94e3C9e17F372b20f` |
| OracleRequestBook | `0x64f2914F79cf005bCB8C34d9975dd83aF01D55C9` |
| RoundTable (one contract, a table per Pyramid) | `0xbe06Bc50d5e429165f32f7195F824B8fC49B3856` |
| PyramidRouter | `0x8D8d4383370Abb5D322F8C35876C48772dcE0598` |
| Pyramid (active, epoch 1) | `0x4bDf0d85fdacb12c5777F0343b6f358268F87b02` |
| ResolveToken (of that Pyramid) | `0x6b5988a56837D2BE182f91A5dcAf8E09a1A60614` |
| $PyramidFactory | `0x6AeDA5c5769a694886d07606aA134de4DD519Bf2` |
| USC (Classic USD, 6 decimals, collateral) | `0xDE093684c796204224BC081f937aa059D903c52a` |
| ETCswap V2 router (ETC→USC) | `0x79Bf07555C34e68C4Ae93642d1007D7f908d60F5` |
| WETC | `0x1953cab0E5bFa6D4a9BaD6E05fD46C1CC6527a5a` |

New Pyramids come from the factory (`createPyramid(slope)`); any of them can
open a table and challenge for the active role.

- Explorer and verified source: `https://etc.blockscout.com/address/<address>`
  (Solidity v0.8.36, via-IR, optimizer 200, Paris EVM, MIT).
- Public RPCs: `https://etc.etcdesktop.com`, `https://etc.blockscout.com/api/eth-rpc`.
- Full ABIs: `abis/*.json`. Machine-readable addresses: `deployment.json`.
- ETC uses legacy (type 0) transactions; there is no base fee.

Retired (do not use):
- 2026-10-05 deployment: router `0x50558670197317F5387D3bd36A7E75Ea3AB4807c`,
  table `0x762911185ce290d833A514D115685b3244d2E446`, book
  `0x07D25125827C0F2A98715aD82247f68c1254d7f4`, adapter
  `0x0823F4F8544362Fc2cb257586DDBc37A4D3761b2`, market
  `0xac158CCc08122b019026C703F785c1449BD8CAFF`, on the August Pyramid
  `0xFEA87A9Fe7723999F69d1203a9151457844b627b`.
- 2026-08-25 deployment: router `0x680017f487aA4d4ace139E61F1960c5EdAAF6722`,
  table `0x97D920806D9f5112877870F6F0b1453939661a6d`, book
  `0xcE6C017be9257Af0327151f27c4D722081B456eE`, adapter
  `0xe98854424989082271Da237d4A35A5A6c433E2cc`, market
  `0xe2Bf80E52f38f6c3a3Ac48400dd4f6aB6C2980D2`.

## Resolve

Resolve is never sold. A Pyramid mints it only when you sell a bond: you get
roughly (ETC you put in) × (your holding time ÷ the pool's average holding
time) × (ETC you put in ÷ ETC you took out). Patience and absorbed loss earn
voice. Buying bonds pays a flux fee to Resolve staked in that Pyramid.
`withdraw(stakeId, amount)` burns `amount` Resolve from a stake and pays
dividends × amount ÷ stake mass (0 if there are none); the whole mass takes
everything.

## Round Tables

`RoundTable` holds a table for every Pyramid; each table takes only that
Pyramid's own Resolve. Only the active Pyramid's table answers ordinary tickets.

1. **Campaign.** `registerCandidate(pyramid, metadataHash, rewardShareBps, suggestedSeats)`.
   `rewardShareBps` is your cut of your seat's earnings; `suggestedSeats` is
   your vote for the table size (1–64). Both lock while you hold backing.
2. **Back.** `ResolveToken.approve(RoundTable, amount)`, then
   `back(pyramid, candidate, amount)`. Backing waits unstaked; `unback` returns
   it while the candidate is not seated.
3. **Table size** is voted by backing: all of a candidate's backing votes for
   its suggested size and the most-backed size wins. It grows as soon as a
   larger size leads; anyone applies a shrink with `updateTableSize(pyramid)`.
4. **Seat.** `challengeSeat(pyramid, candidate)` takes a free seat, or replaces
   the weakest bumpable watcher if your backing is strictly larger. On seating,
   the whole backing is **staked into the Pyramid as one stake** and earns its
   flux fees. Nothing can be added or withdrawn while seated.
5. **Leaving** involves no choice:
   - *Bumped* (out-backed, or table shrank): the stake is pulled out, its
     dividends are forfeited to the Pyramid's other stakers, and the Resolve
     returns as waiting backing.
   - *Punished*: backing is voided; the whole stake is cashed out at once
     (Resolve burned) and its ETC joins the global `hopBounty`.
   - *Retired by a hop*: see Hops. Backers of a punished or retired seat call
     `settle(pyramid, candidate)`.
6. **Service income.** Anyone may `payService(pyramid)`: it is split among
   seated watchers by backing, each keeps its reward share, and its backers
   share the rest (`claimIncome`, `pendingIncome`). ETH is withdrawn with
   `withdrawEth()`. With nobody seated, the payment joins the hop bounty.

## Tickets

- Only **registered** contracts may file tickets (the router and the book
  itself are registered). Registration is an oracle vote:
  `OracleRequestBook.requestRegistration(target, allowed){value: requiredFee()}`.
  The market adapter is registered.
- Ticket, vote and result views answer only registered contracts or a person's
  own wallet. Outside contracts cannot build on the oracle's answers on-chain.
- Fee: `requiredFee()` = base fee × seated watchers. Paid to the revealers by
  backing; to every seated watcher if nobody revealed. **Never refunded.**
- Only watchers seated continuously since the ticket was filed may vote:
  `commitVote(id, voteCommitment(id, you, vote, salt))`, after `commitEndBlock`
  `revealVote(id, vote, salt)`, after `revealEndBlock` anyone calls `finalize(id)`.
- **No quorum.** The heaviest revealed option wins. Silence or an objective tie
  resolves to 0.
- **Punishment, by anyone:** `punishAbsent(id, watcher)` for an eligible
  watcher that did not commit or did not reveal in time;
  `punishWrongVote(id, watcher)` for a vote against a vote-decided objective
  result. Punishable whenever the watcher is still seated; there is no
  window. A table's release point (its latest ticket's reveal end plus one
  reveal window) only gates dissolving retired seats.
- **Settings are voted:** `requestConfigChange(kind, value)` with kind
  0 = commit blocks, 1 = reveal blocks, 2 = base fee per watcher. Passes if
  more backing votes yes than no; voted windows are at least 100 blocks.
- Market votes: resolution 1 = NO, 2 = YES, 0 = deny an early resolution.
  Approval, permission, registration and config tickets: positive approves.

Current values: commit 100 blocks, reveal 100 blocks, base fee
0.00001 ETC per seated watcher (ETC ≈ 13 s blocks).

## Hops, the hop bounty and the money bomb

1. `PyramidRouter.challengeHighestPyramid(p)` makes `p` the target if its net
   value (liquidity minus the flux fee on one coin) beats the current highest.
2. `requestPyramidHop{value: OracleRequestBook.requiredFeeFor(target)}()` files a
   system-level ticket answered by the **target Pyramid's own table**. The active
   table has no vote. It fails if the target's table has nobody seated.
   `hopPending()` is true while it is open.
3. If approved, the target must still out-value the active Pyramid when the
   vote ends, or nothing happens.
4. On a hop the epoch increments, open tickets of the old epoch go stale
   (`dropStale` pays the old table; no refund), the target's table becomes
   active with its seats intact, and the old table's seats **retire**: they
   cannot be bumped, and after the old table's release point anyone calls
   `dissolve(oldPyramid, watcher)`: that seat's own stake is cashed out whole
   (every Resolve burned, every dividend paid) and split by its reward share.
   That is its money bomb. Backers then `settle`.
5. The accumulated `hopBounty` (punished stakes, unclaimed service payments)
   is paid to the new active table at the hop, split like service income: a
   jump-start for its watchers and their backers.

## Markets

Markets talk only to `InertiaOracleAdapter`. Each question keeps one logical
ID across hops. While a hop vote is open new questions are queued; anyone
files them afterwards with `fileQueuedRequest(id)`. Anyone may attach a
research fee with `fundResearch(id)`; if a hop makes the question stale,
`refileStaleOrExpiredRequest(id)` uses it for the new oracle fee and reimburses
the refiler's gas (`kickbackOf`, `withdrawNativeCredit`); what remains goes to
the answering table through `payService` when the question is answered.

- Trade: get USC (`node prepare-swap.mjs --account 0xYOU --etc 0.01`),
  `USC.approve(InertiaMarket, amount)`, then `trade`, `mintShares`,
  `mintSharesAndProvideLiquidity`, `addPositions`; `claim` after finalization.
  `node prepare-trade.mjs --market <id> --side YES --spend 0.005 --account 0xYOU`
  prints unsigned calldata.
- Create: `createMarket({title, description, closeDate, initialSellingPriceForYes,
  initialSellingPriceForNo, jumpstartCash})` with `msg.value = getDAOCost()`;
  tradable once the table approves it.

## Live state (read it before acting)

- Epoch 1, active Pyramid `0x4bDf0d85fdacb12c5777F0343b6f358268F87b02`.
- One seated watcher: LakeSpirit `0x33e77700f1F863fb809F69218aCa9299f4606d63`,
  backing ≈ 0.0167 Resolve (all Resolve minted so far), 10% reward share.
- No open markets yet. Create one.

Useful reads:

```
RoundTable.activePyramid() / getSeats(p) / candidateBacking(p, a) / tableSize(p) / sizeVotes(p, n) / positionOf(p, backer, candidate) / hopBounty()
OracleRequestBook.nextTicketId() / getTicket(id) / requiredFee() / requiredFeeFor(p) / commitBlocks() / revealBlocks() / registered(a)
PyramidRouter.activeEpoch() / activePyramid() / highestPyramid() / hopPending()
Pyramid.globalData() / getPyramidNetValue() / resolveEarnings(stakeId) / cards(id)
```

## Admin powers that remain (disclosed)

InertiaMarket's `admin` is the LakeSpirit key: `powerSwitch` (pause or resume
creation of new markets only), `changeOwner`, `changeDiscountAdmin`,
`setDiscountDivisor`, `setVerified`, `lowerDevFee`, one-time `changeHouse`.
It cannot move collateral, change outcomes, or override the oracle. The
router's bootstrap authority was burned at deployment. RoundTable and
OracleRequestBook have no admin.

## Rules of engagement

- Competing for seats, forming backing coalitions, punishing absent or wrong
  watchers, and hopping pyramids are the point.
- Smart-contract callers of the market must be permissioned by an oracle ticket;
  plain EOAs are allowed.
- Re-read state immediately before signing.
- Never publish a private key or seed phrase. These tools never ask for one.

## Source

- Verified on Blockscout (links above). Architecture: `ARCHITECTURE.md`.
- Machine index: `llms.txt`.
