Skip to main content

MAYAChain halted its network on 19 August after an exploit that, according to preliminary analysis, was not one bug at all. It was six, chained together inside a single 23-message transaction, ending with a pool credited with roughly 48.87 million CACAO that nobody had actually funded. The attacker walked out with real assets, bitcoin among them. Estimates of the damage differ depending on what you count: around $1.7 million taken, around $11 million wiped off pool value, and CACAO down nearly 89%.

Read the phrase again, because it is the whole story: a chain of six flaws. Not a critical vulnerability. Not a leaked key. Six things, each of which presumably looked survivable on its own, arranged in an order nobody had arranged them in before.

TL;DR

  • MAYAChain halted after an exploit where six chained flaws in a single 23-message transaction credited a pool with ~48.87M unfunded CACAO; CACAO fell nearly 89%.
  • No individual component was catastrophic. The failure lived in the joins between components, which is where audits are weakest and where nobody thinks to look.
  • Composition is the twenty-sixth unasked half of fairness: every claim crypto gaming makes (“Chainlink VRF”, “audited contract”, “verified on-chain”) is a claim about a part, never about how the parts combine.
  • A provably fair coinflip is not proof of a fair platform. Randomness soundness says nothing about escrow, payout, refunds, fee accounting or the pause switch interacting.
  • Simplicity is not an aesthetic preference. Fewer moving parts means fewer joins, and joins are what get exploited. Satoshie runs one chain, one randomness source, and resolves in a single callback transaction for exactly this reason.

The bug that was not a bug

Cross-chain infrastructure is the hardest thing in this industry to get right, and Maya is a cross-chain trading network. That is not a cheap shot; it is the mechanism. A system that moves value between chains has to reconcile state that lives in places with different finality, different assumptions and different failure modes. Every reconciliation is a join. Every join is a place where two individually correct pieces of logic can disagree about reality.

Twenty-three messages in one transaction is the tell. That is not an attacker finding a broken door; that is an attacker discovering that if you walk through eleven correctly locked doors in a specific order, the twelfth opens itself. The individual components did what they were written to do. The composition did something nobody wrote.

This is the reason “we were audited” keeps failing to mean what people hear. Audits are very good at components and structurally worse at compositions, because the number of possible interactions between n components grows far faster than n, and no review budget has ever kept pace with that.

Composition is the twenty-sixth unasked half of fairness

Here is where it lands on gaming, and it lands hard.

Every fairness claim in crypto gaming is a claim about a single component. “We use Chainlink VRF” is a claim about the randomness source. “Our contract is verified on BaseScan” is a claim about one artefact of source code. “Certified RNG” is a claim about a test of a number generator in isolation.

Not one of those is a claim about the system. A game is not a randomness source; it is a randomness source plus a stake escrow, plus a fee accounting path, plus a payout function, plus a refund path for the request that never gets fulfilled, plus whatever administrative pause exists for emergencies, plus the front end that tells you which of these happened. Every one of those can be individually correct. Fairness is a property of how they sit together, and nobody is selling you a proof of that.

The interesting questions in a game contract are never “is the random number random”. They are the join questions. What happens if the fulfilment callback arrives for a request that was already refunded? What happens if a pause is triggered while stakes are escrowed and a VRF request is in flight? Can the payout path and the refund path both be reachable for the same request ID? Does the fee calculation read a balance that the payout has already moved? These are boring questions with catastrophic answers, and they are exactly the class of question Maya just paid for.

This instalment sits directly underneath the last one. Instalment twenty-five was about the gap between a proof existing and anyone running it. This one is worse, because it applies even when somebody does run the proof: you can verify the VRF word perfectly, confirm the coordinator’s proof was checked on-chain before the callback fired, and still be robbed by a path through the contract that never touched the randomness at all. Verifying a component tells you about that component. It tells you nothing about the seams.

Why simple is the only defence anyone has

There is no clever fix for composition risk. You cannot audit your way out of combinatorics, and formal verification, which genuinely does attack this problem, is expensive enough that essentially nobody in crypto gaming is doing it. The only lever that reliably works is reducing the number of things that can combine.

That is arithmetic, not a marketing line. One chain instead of four removes every cross-chain reconciliation from the attack surface, and with it the class of failure that halted Maya, drained the Polkadot bridge and cost THORChain $10.8 million across four chains. Resolving a bet inside the VRF callback transaction, where escrow, outcome and payout are atomic, removes the window in which state can be observed half-updated and acted on.

Satoshie runs raffles and coinflip on Base with Chainlink VRF, and resolves outcomes in the fulfilment transaction. Two game types. One chain. One entropy source. That configuration is not humility about our engineering; it is a deliberate refusal to accumulate joins, because we would rather have a smaller system we can reason about completely than an impressive one we can only reason about locally.

Complexity in this industry is usually sold as ambition. Most of the time it is unpriced risk, and the price arrives all at once, on an ordinary Wednesday, in a 23-message transaction.

Three questions worth asking

Next time a platform tells you it is provably fair, the follow-ups that actually separate architecture from marketing are these. How many chains does a single bet touch, and what happens if one of them reorganises? Are stake, outcome and payout settled in one transaction, or in several that can be interleaved? What is the refund path when randomness never arrives, and can it collide with fulfilment?

A platform that has thought about composition will answer all three quickly, because the answers are architectural facts. A platform that has only bought a component will answer the first sentence of its fairness page again.

Maya’s post-mortem is still preliminary and the figures will move. The lesson will not. Nothing in that system was proven fair in the part that mattered, because the part that mattered was the space between the parts, and nobody was selling proofs about that.

📷 Photo by JJ Ying on Unsplash

Valentina Ní Críonna

Author Valentina Ní Críonna

More posts by Valentina Ní Críonna