A subgraph, but it’s just a database
arkiv-sync is the always-on worker that watches a chain and writes each event as a queryable, expiring Arkiv entity. The chain is the source of truth; Arkiv is a derived view that can always be re-derived — which is exactly what makes reorgs and restarts safe.
Any EVM chain (a contract's events) ──▶ arkiv-sync ──▶ Arkiv / Braga (queryable entities)
viem getLogs decode · dedup · your app queries this
RPC pool + rotation reorg · cursor · TTL (no RPC, no gas)Template, library, or your AI
One npm package is the backbone. Scaffold a project, embed the library in your app, or hand your AI the skill and describe what to index — all three drive the same engine.
Scaffold a project
A ready project that indexes on npm start.
npm create arkiv-sync@latest my-indexer
Embed in your app
Import createIndexer / createArkivReader as a dependency.
npm i arkiv-syncView on npm
Let your AI wire it
Give your AI this skill, then just describe what to index — it writes the config and wires the package, gotchas and all.
/tools-md/arkiv-sync
One declarative config
Everything is one arkiv.config.ts — the contract, the events, how long entities live, and which fields are queryable. Adding another contract or chain is just another config; the engine never changes.
import { defineConfig, days, type NormalizedEvent } from 'arkiv-sync'
export default defineConfig({
source: {
chain: 'sepolia',
contract: '0xfFf9976782d46CC05630D1f6eBAb18b2324d6B14',
events: ['Transfer(address indexed from, address indexed to, uint256 value)'],
fromBlock: 'latest',
},
ttlSeconds: days(30),
map: (e: NormalizedEvent) => ({
attributes: { // queryable fields
from: String(e.args.from).toLowerCase(),
to: String(e.args.to).toLowerCase(),
value: String(e.args.value),
},
}),
})Then query the derived database with createArkivReader (always owner-scoped, injection-safe). Predicate operators: = != > >= < <= combined with && ||; sort client-side (Arkiv has no server-side orderBy).
What the engine does for you
Reorg-safe
Indexes only head − confirmations, tracks recent block hashes, and on a reorg rolls back to the common ancestor and re-derives. The chain is the source of truth; Arkiv is a view that can always be rebuilt.
Idempotent
Every event's key is chainId:txHash:logIndex, written create-or-skip by content hash — restarts and overlaps never duplicate.
Resumable
The cursor is persisted atomically; the worker resumes exactly where it stopped.
Zero-friction RPCs
A rotating pool of public EVM endpoints (no signup); a throttled/dead node is skipped, and a too-wide getLogs range auto-splits.
Batched & cheap
Writes are batched (~50 events per tx). Measured cost ≈ 1–3 ×10⁻⁸ GLM/event on Braga (1 GLM ≈ tens of millions of events).
Testnet-locked sink
Source = any EVM chain (mainnets are READ-ONLY — reading logs signs nothing). The sink that holds a key is always Braga testnet; mainnet sinks are refused by allowlist.