About

One ledger,
two runtimes.

Portaldot V3 runs one chain in two execution environments: Solidity logs on the EVM side, typed pallet events on the ink! side. Every app reading through an indexer has to believe what it is told. Rootmark is the middleware that removes the belief: one record shape across both VMs, every batch reduced to a Merkle root on chain, every result carrying the proof your own tab rebuilds.

01 — What it does

Clerical work,
done in public.

Read both runtimes, normalise into one shape, stamp each batch on chain.

Read

Tails eth_getBlockByNumber plus receipts on EVM, system.events on Substrate, at the same time.

Normalise

Both sides become one EventRecord: vm, chainId, block, transaction, contract, event, args, leaf. Known contracts decode by ABI, unknown ones are kept by topic hash, never dropped.

Anchor

Leaves are keccak256 of canonical JSON. Each batch builds a binary Merkle tree, root committed to PortalAnchor, batch id claimable once so nobody rewrites history.

02 — Verification

Four hashes,
checked in your tab.

  1. 1. Leaf. keccak256 of the canonical record. Recomputed from the record you see.
  2. 2. Siblings. The Merkle path from /api/v1/proof/:id, walked in your browser with applyProof.
  3. 3. Batch root. The root your tab rebuilds. If the index invented a record, this root moves.
  4. 4. Anchor contract. The root read from contract storage via ethCall. When all four agree, the panel says verified, plainly. When they do not, it says so too.

Unsigned batches are normal on a quiet chain, not an error. The console labels them unsigned instead of implying proof it does not have.

03 — Live on V3

The contracts
you can read.

Chain 420420777. No hidden deploy, every address checkable.

PortalAnchor (EVM)

0x92B79C5cD9CC8235e3e2718a739BE825e2bd5430

holds one 32 byte root per batch, plus block range

Emitter (EVM test events)

0x178B9ED9dB766d2ecda393ca0b780A643a12C31E

emits Ping events used for live batches

anchor (ink! / WASM)

not yet deployed

same shape, deploys with npm run deploy:wasm once funded

First anchored batch on V3 covers 3 Ping records (blocks 204669 to 204672). Verify records 11 to 13 at GET /api/v1/proof/:id, each returns anchorStatus anchored, onChainRootSource contract-storage, verified true.

04 — Fit and limits

Why middleware,
honestly bounded.

Track 1, Modular Cloud and Middleware: Rootmark is the query layer between chain and app. One shape across VMs, one proof per result, REST plus console, permissionless anchoring so any writer can commit a batch but none can rewrite one.

  • · Reorgs are not handled, the indexer reads head, fine on a quiet testnet, needs a finality lag on a busy chain.
  • · The tree duplicates the last node on odd levels, simple and verifiable, not RFC 6962 portable.
  • · SQLite assumes a single writer, one indexer process.
  • · Ink! anchoring needs a funded native account, until then batches are EVM anchored with honest unsigned labels.