Blockchain applications turn user intentions into transactions, and the path between the two is full of repetition. Buttons get clicked twice on slow connections, requests get retried after timeouts, and a reload forgets what was just sent. Each repeat can become another wallet prompt or another transaction.
Mutiq is a coordination layer for developers on EVM chains, starting on Robinhood Chain. It names each logical action with an idempotency key, queues it once and counts its repeats. It checks the action against rules, simulates it, sends it through the user's own wallet a single time, and reads its fate from the chain.
Mutiq is not a trading bot, a wallet or a new chain. It holds repeats only inside the pipeline an application controls, and it says so.
Chapter 01
Why this exists
A web request is usually cheap to repeat. A transaction is not: it costs gas, it takes a prompt, and it can change state that cannot be changed back. Yet most front ends treat the send button like any other button.
Backend engineers solved a version of this long ago with idempotency keys: a client names its request, and the server does the work once however many times the name arrives. Transaction pipelines need the same idea, adapted to a world with signers, nonces, replacement transactions and no server in the middle.
Chapter 02
One intention, many sends
Take the plainest case. A user presses a button to update a value on a contract. The connection is slow and nothing seems to happen, so the button is pressed again. Without care, the app sends two requests and may produce two transactions.
Other shapes of the same failure are common:
- Retry logic resends after a timeout, when the first attempt was only late.
- A reload clears what the component remembered sending.
- A transaction that was going to revert still costs gas to find out.
- A low-fee transaction holds up every later nonce of its signer.
- A wallet replaces a transaction, and the hash on the screen stops existing.
None of these needs an attacker. They come from ordinary behaviour meeting a system with no memory of intent.
Chapter 03
What we held to
- The user's wallet signs everything. The site holds no key and sends nothing on its own.
- The same rules in both places. The transition table and policy order are the same in TypeScript and in Solidity, and tests check the shared vectors.
- Say what is read and what is reported. The state monitor reads the chain. The ledger records what an operator reported.
- No central party. The contract has no global owner, no fee and no upgrade path.
- Works without us. Local tools run in the browser and the package is plain code in the repository.
Chapter 04
The moving parts
Idempotency key
The key is keccak(namespace, logical action). The namespace id includes its creator's address, so nobody can take a name from someone. The logical action is the signer, target, value, call data and a scope the developer chooses.
Action Queue and Repeat Request Detection
A new key is queued. A known key raises a counter and changes nothing else. Sending is wrapped in a single-flight guard: a second call for a key in flight gets the first call's promise. The guard is synchronous, so the second click of a double click meets it before anything has rendered.
Execution Policy
A namespace has rules by target and selector: allow, deny, and a count per fixed time window. An exact rule beats a rule for the whole target. Strict mode refuses anything not listed, and a pause stops anything new. The browser evaluates these locally; the contract enforces them when an action is registered.
Transaction Preflight
Parameter checks come first and need no chain. Then the target's code, a simulated call from the signer's address, a gas estimate, the fee at the current price and the balance that has to cover it. Reverts are decoded where the error is known.
Nonce Coordination
For a signer the app controls, Mutiq reads the latest and pending counts and compares them with what the app sent. It reports what is in flight, the next free number, gaps, a stuck front of the line and a nonce already used by something else.
Conflict Inspector
Actions can declare a conflict key for what they touch. Open actions sharing one are grouped, along with actions on one nonce and, as a softer hint, actions on the same function. An exclusive namespace refuses a new action while one with the same key is open.
State Monitor and Activity Timeline
A hash becomes one of five states from chain data alone. A hash the node does not know, on a nonce already used, reads as replaced. The timeline places local steps next to ledger events.
Chapter 05
The ledger
MutiqActionLedger gives coordination a public home. It is optional: everything above works without it.
Namespaces and roles
Anyone can create a namespace and receives every role in it: admin, policy editor, operator. Admins set roles, and a namespace cannot be left without an admin.
First registration wins
register computes the key from the namespace and the action hash. On a new key it runs the policy, queues the action and returns true. On a known key it only raises the repeat counter and returns false. It deliberately does not revert, so a double send stays visible.
State machine
queued -> submitted | cancelled | expired submitted -> replaced | confirmed | reverted replaced -> replaced | confirmed | reverted confirmed, reverted, cancelled, expired: final
Moving to submitted or replaced binds a transaction hash, and one hash can be bound once per namespace. Anyone may mark a queued action expired after its deadline. Everything else needs an operator. Each change emits an event, which is what the timeline reads.
Chapter 06
Limits
These are the points that matter most, so they are stated without softening.
- Repeats are held only inside the pipeline the app controls. Mutiq cannot stop a transaction sent outside it.
- Browser storage belongs to one browser. A second device has its own queue until the ledger answers.
- A simulation describes this moment. The real transaction runs later.
- The ledger cannot read other transactions, so a bound hash and a reported outcome are what an operator said.
- Mutiq manages only signers the app controls.
- Some rollups keep no public mempool, so pending counts can look the same as latest while a send is on its way.
More on the limits page.
Chapter 07
Security and risk
The contract holds no funds and makes no external calls, which removes whole families of failure. What remains is mostly about correctness of the record. Tests cover the role matrix, every illegal transition, window counting and random traffic against invariants such as “a namespace always has an admin” and “the open count matches the open actions”.
The relay never forwards a signed transaction. Simulations reach any address, because that is their purpose, with capped gas and data and a budget per network. Details are on the security page. The code is young, and we would rather you read it than trust it.
Chapter 08
Product
The app has one screen per tool: demo, queue, policy, preflight, nonce, conflicts, monitor, timeline and ledger. The client package holds the same logic for your own code, as plain TypeScript in the repository. It is not published to a registry.
A hosted Developer API and premium monitoring are planned, not built.
Chapter 09
The $MUTIQ token
$MUTIQ is the project's token, launching on Pons on Robinhood Chain. Every tool here works without it. Coordination credits, premium monitoring and API access are planned uses, and they depend on services that do not exist yet. It carries no promise of value, and nothing here is financial advice.
Chapter 10
Roadmap
Local tools are built. The ledger is deployed on Robinhood Chain with verified source. Hosted services and credits come after that, if they prove useful. The order and status are on the roadmap.
Chapter 11
Closing note
Most repeated transactions are not attacks. They are ordinary behaviour with nothing in the way. A key, a queue and a few honest reads put something in the way, inside the code you already control. That is a modest claim, and we think it is a useful one.