Skip to content
LogoLogo
@tevm/mud

Optimistic MUD state on a real EVM

@tevm/mud runs your MUD system calls locally, in the browser, on a real EVM before they are confirmed on chain — and exposes the resulting state through the same MUD Stash APIs your game already uses.

pnpm add @tevm/mud tevm

Quickstart →

It is a MUD plugin, not a fork: your mud.config.ts, your systems, your Stash and your SyncProvider stay exactly as they are.

The problem

A MUD client writes through writeContract, then waits for the transaction to be mined and indexed before the Stash reflects the change. That round trip is hundreds of milliseconds at best, and seconds on a congested chain. Games feel broken at those latencies.

The usual fix is hand-written optimistic updates: for every system call you also write a JavaScript function that guesses what the contract would have done. That guess drifts from Solidity the moment anyone touches a system, and it cannot see the parts of your logic that read other tables.

The solution

Instead of guessing, execute. @tevm/mud intercepts writeContract, runs the call against a Tevm MemoryClient forked from your RPC, decodes the resulting MUD Store events, and applies them to an optimistic overlay on top of your Stash. The prediction is your Solidity, so it cannot drift.

When the canonical transaction lands, the sync adapter reconciles it and drops the optimistic overlay for that transaction.

import { createOptimisticHandler } from '@tevm/mud'
 
const optimistic = createOptimisticHandler({ client, storeAddress, stash, config })
 
// Reads that include not-yet-confirmed writes
optimistic.getOptimisticState()
optimistic.getOptimisticRecord({ table: config.tables.Position, key: { player } })

Or in React:

import { useOptimisticRecord } from '@tevm/mud/react'
 
const position = useOptimisticRecord({ table: config.tables.Position, key: { player } })

What you get

Correct predictions

The optimistic state comes from running your actual system bytecode, including cross-table reads and reverts.

Zero per-system code

No optimistic reducer to write or maintain — the prediction is your Solidity.

Real transaction lifecycle

Subscribe to simulating → optimistic → confirmed | reverted for each write.

Drop-in Stash reads

getOptimisticRecord / getOptimisticRecords mirror @latticexyz/stash's getRecord / getRecords, with React hooks that re-render on optimistic and canonical updates.

Try it

Send increment() and watch the Stash update optimistically — then reconcile on confirmation, or roll back when the canonical transaction reverts:

stash — app:Counteridle
keyfieldvalue
0x00…01value41
0x00…01lastUpdatedBy0xf39F…2266
subscribeTxStatus — events
send a transaction to watch the TxStatus stream

Next

The Tevm family

This site is one of the *.tevm.sh docs sites — jump between them: