This repo houses the Ruby app and Solidity predeploys that build the Ethscriptions chain on top of Ethereum. It started life as a Postgres-backed indexer; it now runs the derivation pipeline that turns L1 activity into canonical L2 blocks. You run it alongside an ethscriptions-geth execution client.
- Observe Ethereum L1 via JSON-RPC. The importer follows L1 blocks, receipts, and logs to find Ethscriptions intents (Data URIs plus ESIP events).
- Translate matching intents into deposit-style EVM transactions that call Ethscriptions predeploy contracts (storage, transfers, collections tooling).
- Send those transactions to geth through the Engine API, producing new L2 payloads. Geth seals the block, the predeploys mutate state, and the chain advances with the Ethscriptions rules baked in.
The result is an OP-style Stage-2 “app chain” that keeps Ethscriptions UX unchanged while providing Merkle-state, receipts, and compatibility with standard EVM tooling.
- Ruby derivation app — importer loop and Engine API driver; it is meant to stay stateless across runs.
- Solidity contracts — the Ethscriptions and token/collection predeploys plus Foundry scripts for generating the L2 genesis allocations. The Ethscriptions contract stores content with SSTORE2 chunked pointers and routes protocol calls through on-chain handlers.
- Genesis + tooling — scripts in
lib/andcontracts/script/to produce the genesis file consumed by geth. - Reference validator — optional job queue that compares L2 receipts/storage against a reference Ethscriptions API to make sure derivation matches expectations.
Anything that executes L2 transactions (the ethscriptions-geth client) runs out-of-repo. This project focuses on deriving state and providing reference contracts.
- Install Docker Desktop (includes the Compose plugin) and ensure you have access to an Ethereum L1 RPC endpoint.
- Copy
docker-compose/.env.exampletodocker-compose/.env, then adjust the values for your environment (see below). - From the repo root, bring the stack up:
cd docker-compose docker compose --env-file .env up -d - Follow logs while it syncs:
docker compose logs -f importer
- Shut the stack down when you are done:
docker compose down
Key variables in docker-compose/.env:
COMPOSE_PROJECT_NAME,COMPOSE_BAKE— control how Compose names resources and whether to usedocker buildx bake.JWT_SECRET— 32-byte hex value shared with geth’s Engine API JWT config.GENESIS_FILE,L1_NETWORK,L1_GENESIS_BLOCK— define which genesis snapshot to mount and which L1 epoch anchors the rollup.L1_RPC_URL— archive-quality L1 RPC the importer will follow.ETHSCRIPTIONS_API_BASE_URL,ETHSCRIPTIONS_API_KEY,VALIDATION_ENABLED— toggle and configure the validator (point it at the API you want to reconcile against).L1_PREFETCH_FORWARD,L1_PREFETCH_THREADS,PROFILE_IMPORT— tune importer throughput and profiling output.JOB_CONCURRENCY,JOB_THREADS— SolidQueue worker sizing for validator jobs.GC_MODE,STATE_HISTORY,TX_HISTORY,ENABLE_PREIMAGES— pass-throughs toethscriptions-gethto control archive depth and trie retention.GETH_EXTERNAL_PORT— port exposed on the host for the L2 RPC.
Any variable omitted from the file falls back to the defaults baked into the Compose file.
The validator reads expected creations/transfers from your Ethscriptions API and compares them with receipts and storage pulled from geth. It pauses the importer when discrepancies appear so you can investigate mismatches or RPC issues. Enable it with VALIDATION_ENABLED=true, point it at ETHSCRIPTIONS_API_BASE_URL, and keep the SolidQueue workers running. The temporary SQLite databases in storage/ and the SolidQueue worker pool exist only to support this reconciliation; once historical import is verified the goal is to remove that persistence and keep the derivation app stateless.
- Content storage — Raw inscription bytes are chunked and written with SSTORE2; the contract keeps a hash index to prevent duplicate content and always returns the exact payload that appeared on L1.
- Protocol hooks —
registerProtocollets you associate a protocol name with a handler contract. When an ethscription is created or transferred, the Ethscriptions contract records the protocol and calls into the handler (op_*functions) so new behaviors can be layered on. - Token + collections plumbing — Predeploys provide standard operations for fungible-style tokens and curated collections while still flowing through the same protocol registration pipeline.
app/services/— derivation logic that turns L1 data into deposit transactions and steers Engine API calls.app/models/— Ethscription transaction models plus the validation result schema.contracts/src/— Ethscriptions, TokenManager, CollectionsManager predeploys.contracts/script/andlib/— genesis builders and helper utilities.spec/andcontracts/test/— RSpec and Foundry tests.
Run bundle exec rspec for Ruby tests and cd contracts && forge test for solidity tests.
If you want to modify the Ruby code outside of Docker, install Ruby 3.4.x, run bundle install, and use bin/setup to create local SQLite files. The importer still expects a running ethscriptions-geth and L1 RPC; the Compose stack is the recommended path for production-like runs.
Ethscriptions behavior and APIs remain identical to the pre-chain era: inscribe and transfer as before, and existing clients can keep using the public API. The difference is that the data now lives in an L2 with cryptographic state, receipts, and interoperability with EVM tooling.
Questions or contributions? Open an issue or reach out in the Ethscriptions community channels.