Four primitives. One wrap. Your agent actions become atomic, crash-safe, and self-auditing.
OpenAcid is a durable execution library for AI agents that hold real money.
THE PROBLEM
Autonomous AI agents are increasingly given their own wallets, signing keys, and discretion over real funds — rebalancing portfolios, executing swaps, paying bills, custodying user deposits. Account abstraction (EIP-7702 mainnet, May 2025) and CDP-style server wallets have made this commonplace, and iNFT (ERC-7857) is making agent state portable and valuable. Over the next eighteen months, a 10–100× increase in agents holding non-trivial value is on track to land. And every one of those agents quietly inherits a class of bugs that no library has shipped a real fix for.
It's a six-headed bug, all variations of "the agent silently lost money":
Every team running on-chain agents has hit three or more of these in production. Today they patch them with bespoke code: Redis locks that don't survive restart, hash-of-args caches that don't reconcile with chain state, "fingers crossed" exception handlers, hand-rolled retry-with-revoke logic. Nobody has shipped the obvious shared library.
WHY EXISTING TOOLS DON'T SOLVE IT
The gap is agent-shaped durability semantics — exactly-once execution, atomic multi-step, invariant enforcement, and signed receipts — exposed as a small library, not a workflow engine.
THE SOLUTION
openacid borrows the four classical database guarantees — Atomicity, Consistency, Isolation, Durability — and exposes them as four composable npm primitives:
import { saga, invariant, idempotent, receipted } from '@openacid/acid'
saga(steps, compensations) // A — multi-step rollback via compensating txs invariant({ pre, post }) // C — predicates enforced at action boundaries idempotent({ key, storage }) // I — in-flight tracking + dedup, crash-safe receipted({ storage, signer }) // D — signed chained receipts, persisted to 0G
Wrap any agent action in receipted(invariant(idempotent(saga(action)))) and a single nested call makes it crash-safe, deduplicated, invariant-enforced, and self-signing an audit trail. Postgres taught your backend ACID semantics. openacid teaches your agents.
WHAT'S IN THE BOX
Core library — pnpm monorepo, built with tsup for dual ESM/CJS output, tested with vitest. The four primitives (saga, invariant, idempotent, receipted) are
higher-order functions with a uniform signature: take an async function, return an async function with added guarantees. Zero runtime dependencies beyond their own
adapter interfaces — chain-agnostic and framework-agnostic by design. The only public configuration surface is three adapter interfaces: StorageAdapter, ChainAdapter,
SignerAdapter. Swap the adapters, same guarantees. The library validates composition order at construction time and emits warnings when the nesting order changes the
semantic guarantees — for example, wrapping idempotent outside saga means saga state isn't deduplicated across retries, which is almost never what you want.
Install and use:
pnpm add @openacid/acid
npm: https://www.npmjs.com/package/@openacid/acid
import { saga, invariant, idempotent, receipted } from '@openacid/acid'
const rebalance = receipted({ storage, signer },
invariant({ pre: walletBalanceOK, post: noOrphanAllowances },
idempotent({ key: (a) => rebalance:${a.deadline}, storage },
saga({
steps: [
{ id: 'approve', do: (ctx) => approve(token, router, ctx.amount) },
{ id: 'swap', do: (ctx) => router.exactInput(ctx.swapArgs) },
{ id: 'stake', do: (ctx) => vault.deposit(ctx.outAmount) },
],
compensations: {
approve: (ctx) => approve(token, router, 0n),
},
storage,
}))))
await rebalance({ targetRatio: 0.6, deadline: blockNumber })
One wrap. Crash-safe. Deduplicated. Invariant-enforced. Self-auditing.
0G Storage (@0gfoundation/0g-storage-ts-sdk) is the primary durability backend. In-flight idempotency markers and result caches go to 0G KV via Batcher/KvClient for
low-latency atomic reads. Receipts and saga state are written as content-addressed blobs via indexer.upload / ZgFile — the CID is derived from the content, so a
receipt is immutable by construction; "updating" a receipt means creating a new one, not overwriting. Large saga states are streamed via the stream method on the
adapter rather than chunked put calls.
The notably hacky part: the cas (compare-and-swap) operation underpinning idempotent's in-flight markers is built on top of 0G KV's versioned writes. Two concurrent
callers racing on the same idempotency key will have exactly one win and one block. No distributed lock manager, no external coordinator — the storage layer itself is
the mutex. This is the entire isolation guarantee, and it survives process restarts because the marker lives in 0G KV, not in process memory.
0G Compute (@0gfoundation/0g-compute-ts-sdk) drives the reasoning layer of the example agent. Rather than pinning a model name at build time, the model is resolved at
runtime via broker.inference.getServiceMetadata() — the 0G Compute catalog is dynamic and provider-dependent, so hardcoding a model string is a reliability hazard.
Auth is handled via on-chain signed requests or a CLI-issued Bearer token (0g-compute-cli inference get-secret --provider <address>). Local-dev fallback is Anthropic
Claude Sonnet 4.6 — same agent logic, swapped inference backend, zero code changes in the agent itself.
Receipt signing uses EIP-712 typed data via viem's signTypedData / verifyTypedData. The domain separator includes chainId 16602 (0G Galileo testnet), making receipts
chain-scoped — a receipt signed on 0G Chain cannot be presented as valid on any other chain. The full Receipt struct (callId, fnName, inputHash, outputHash, txRefs,
timestamps, retries, prevReceipt) is part of the typed schema, so an auditor reading the signature sees structured fields, not an opaque hash. ReceiptRegistry.sol
(Solidity 0.8, deployed on 0G Chain via Foundry) accepts merkle roots of receipt batches and verifies signatures via ecrecover on the struct hash — no off-chain
library needed. Every N receipts, the library posts a root to the contract, giving any verifier a trustless on-chain anchor without storing every receipt on-chain.
Chain-aware crash recovery is the most technically interesting piece. When idempotent begins executing a call, it writes an in-flight marker to 0G KV containing the
broadcast tx hash. On process restart, if a marker exists without a completed record, the viem chain adapter queries 0G Chain by that tx hash and determines pending /
mined / replaced / failed. If mined: mark complete, return cached result, skip re-broadcast. If replaced: surface the replacement to the caller rather than hiding it —
silent re-broadcast on a replaced tx is a double-spend vector. If pending: block until finality, then resolve. This is what makes the kill -9 demo work: five
transactions intended, exactly five mined, restart handled without a single duplicate.
Example agent — a plain event loop + viem executing a Uniswap V4 approve → exactInput swap → vault deposit saga, wrapped in the full
receipted(invariant(idempotent(saga()))) stack. The noOrphanAllowances postcondition checks that the ERC-20 allowance count after execution equals the count before. If
the swap fails and the approval is left standing, the postcondition fires, triggers the saga's compensation chain, and revokes the allowance back to zero. No orphan
state. No phishing exposure. The compensation for approve is approve(token, router, 0n) — one line, called automatically in reverse step order.
ENS — each agent gets a subname under openacid.eth via a custom subname registrar built with viem ENS helpers. The @openacid/adapter-ens package hooks into the
receipted primitive's onReceipt callback and mirrors receipt.latest and receipt.head CIDs into ENS text records on every write. The result: alice-bot.openacid.eth
resolves to the latest receipt CID via any standard ENS resolver, which fetches the full signed receipt from 0G Storage — complete, tamper-evident audit trail,
queryable from a standard ENS lookup, no library installed.

