A Cosmos SDK realm where sworn Oracles scry the Ethereum void, seal their Prophecies on-chain, and enshrine the Chosen Vision in the Chronicle.
In the age before finality, the Ethereum realm wrote its truths in storage slots, and no Cosmos chain could see within. The elders forged ProphecyChain — a sovereign zone where chosen validators, now called Oracles, peer into the Ethereum void, swear oaths to what they witnessed, and anchor the majority Vision into an immutable Chronicle. False prophets are slashed. Honest seers are blessed. The on-chain oracle problem falls to the oath.
| Role | Keyword | What they do |
|---|---|---|
| Oracle | validator | Scry Ethereum storage, cast prophecies, keep the realm honest |
| Prophecy | MsgCastProphecy |
A sworn claim: "at block N, the target slot held value V" |
| Seal | Seal |
One Oracle's signature bound to a Prophecy |
| Chronicle | Chronicle |
The enshrined truth — the state agreed upon by the majority |
| Scryer | ProphecyScryer |
Off-chain familiar that reads Ethereum and carries the Prophecy to the altar |
| Curse | slashing | The penalty laid upon Oracles whose Seals defy the majority |
.
├── ProphecyChain/ ⛩️ the sovereign chain (Cosmos SDK + CometBFT)
│ ├── cmd/prophecyd/ ⚒️ the chain binary — the altar keeper
│ ├── proto/prophecy/ 📜 sacred proto scrolls (Msg, Query, state)
│ └── x/prophecy/ 🕯️ the module — keeper, msg_server, CLI, genesis
│
├── ProphecyScryer/ 👁️ off-chain familiar
│ └── main.go reads Ethereum storage, casts Prophecy to the altar
│
└── README.md this tome
┌──────────────┐ ① scry ┌──────────────┐
│ Ethereum │ ─────────────▶ │ Oracle │
│ storage slot│ │ (validator) │
└──────────────┘ └──────┬───────┘
│ ② CastProphecy(block, state)
▼
┌───────────────────┐
│ ProphecyChain │
│ msg_server ⛩️ │
└─────────┬─────────┘
│ ③ AppendSeal
▼
┌───────────────────┐
│ Seal ledger 🔏 │
└─────────┬─────────┘
│ ④ HasMaxSeals(block)
▼
┌───────────────────┐
│ Chronicle 📜 │ ⬅ majority Vision enshrined
└───────────────────┘
│ ⑤ curse dissenters
▼
┌───────────────────┐
│ slashing 🔥 │
└───────────────────┘
- Scry. Each Oracle independently queries an Ethereum storage slot at some block — default is
borrowIndexof Compound's cUSDT contract (0xf650C3d88D12dB855b8bf7D11Be6C55A4e07dCC9). - Cast. The Oracle broadcasts a
CastProphecy(blocknumber, state)transaction. - Seal. The module keeper appends a
Seallinking that Oracle's address to the Prophecy. Only bonded validators may seal — non-validators are rejected withErrUnauthorized. - Enshrine. Once seals are tallied for a block, the state with the most seals becomes the
Chronicleentry. Users can then query the Chronicle by block number. - Curse. Oracles whose seal contradicts the chosen Vision are slashed via the staking/slashing keeper — dishonesty costs stake.
Determinism note: the chain never calls Ethereum itself. It only counts the Seals that validators submit, so consensus stays pure.
- Go ≥ 1.19
- Ignite CLI (v0.27.x)
- An Infura (or any Ethereum JSON-RPC) endpoint
cd ProphecyChain
ignite chain serveignite chain serve installs deps, builds prophecyd, initializes genesis, and boots a dev node with preloaded accounts (alice, bob, cat).
# cast a prophecy (only validators are authorized)
prophecyd tx prophecy cast-prophecy <blocknumber> <state> --from <account>
# read the enshrined chronicle for a block
prophecyd q prophecy show-chronicle <blocknumber>
# read a specific seal by id
prophecyd q prophecy show-seal <id>cd ProphecyScryer
go mod tidy
export INFURA_URL="https://mainnet.infura.io/v3/<YOUR_KEY>"
go run main.goThe Scryer fetches borrowIndex from the Compound cUSDT contract at the latest Ethereum block, casts a Prophecy as alice, and queries the resulting Chronicle.
| File | Role |
|---|---|
keeper/seal.go |
Append / get / remove Seals; HasMaxSeals picks the chosen Vision |
keeper/chronicle.go |
CRUD for enshrined Chronicle entries |
keeper/msg_server_cast_prophecy.go |
Tx handler — validates Oracle, appends Seal |
types/message_cast_prophecy.go |
Msg validation — creator must be a bech32 val address |
module.go |
ABCI lifecycle + slashing of dissenting Oracles |
cd ProphecyChain
go test ./x/prophecy/...
⚠️ Known:testutil/keeperhas a pre-existing signature mismatch withNewKeeperinherited from the original scaffold. It does not affectprophecyd, which builds clean. To exercise the module end-to-end, runignite chain serveand submit acast-prophecytx manually (or via the Scryer).
EndBlockis O(n) in total seals. Every block walks the entire Seal ledger and re-iterates for each block touched — seex/prophecy/module.go:156. Fine for a prototype; production should index seals by(blocknumber, state)and process only the block just finalized.- No seal expiry or pruning. Seals persist indefinitely in the KV store, so state grows without bound.
- Slashing fraction is small and hardcoded.
sdkmath.LegacyNewDecWithPrec(2, 3)= 0.2% of stake (x/prophecy/module.go:186). Signal, not deterrent — tune before mainnet. - Majority is a simple plurality.
HasMaxSealspicks whichever state has the most seals, with no quorum threshold. A single seal can become the Chronicle if no other seals exist for that block. - No Ethereum re-org handling. The Scryer reads the latest block; re-orgs on Ethereum silently produce divergent Seals and can lead one honest Oracle to be slashed for a state that was once canonical.
MsgCastProphecy.GetSignerspanics on a malformed bech32 creator. Worth replacing with a proper error inx/prophecy/types/message_cast_prophecy.go:34before any production workload.
- Oracle = bonded validator. Non-validators'
CastProphecytxs are rejected withErrUnauthorized(keeper/msg_server_cast_prophecy.go). Decide staking requirements before inviting Oracles in. - Ethereum RPC reliability.
ProphecyScryeruses a single endpoint fromINFURA_URL. Real deployments should add retries + a multi-RPC fallback; a failed RPC causeslog.Fataland skips the cast. - ABI location. The Scryer reads
abi.jsonfrom its working directory. Run it fromProphecyScryer/or pass an absolute path. - Default slot is Compound cUSDT
borrowIndex. Both the contract address (0xf650C3d8…DCC9) and the called method are hardcoded inProphecyScryer/main.go. Fork + edit the Scryer to read a different slot — it is not configurable at runtime. - Genesis seeding.
ignite chain serveseedsalice,bob,cat. For a custom genesis, create keys (prophecyd keys add) and validators (prophecyd gentx) before the first boot; the Scryer expectsaliceto exist in the keyring.
For spelunkers of the git history:
| Ancient name | Current name |
|---|---|
EVMStoreChain |
ProphecyChain |
EVMStoreChaind |
prophecyd |
evmstorechain module |
prophecy module |
EVMStateVoteClient |
ProphecyScryer |
SubmitEthereumState |
CastProphecy |
EthereumState |
Prophecy |
Vote |
Seal |
Blockstoragestate |
Chronicle |
May your seals never be cursed, and your chronicles forever enshrined. 🕯️