Inspiration

Dupe bugs have wrecked online-game economies for twenty-five years. Amazon's New World repeatedly froze all trading and gold transfers in 2021 to stop item and gold duplication; Diablo II's economy ran on dupes for years; RuneScape has rolled back its entire economy after dupe incidents. These are teams of hundreds, and they keep shipping the same bug. We kept asking why — and the answer is uncomfortable: a dupe is not a gameplay bug, it's a distributed-systems consistency bug. A trade-window race is a lost update. Drop-and-relog is a non-atomic transaction. A cross-region dupe is split-brain. So we stopped trying to detect duplication and asked a different question: what if the authoritative state simply had no way to represent an item being owned twice?

What it does

Duped is an economy kernel a studio integrates so every item and coin is provably conserved — under concurrency, across regions. It protects two kinds of object two different ways, because a sword is not money. A unique item is exactly one row with one owner and a version; every trade, drop, pickup, or mail is the same conditional update that must match the item's current owner and version, so two concurrent transfers can never both win. Fungible gold is sharded balances that can't go negative plus a balanced double-entry ledger, so supply in always equals supply out. The live demo lets you watch this: run a trade race against a naive database and the one legendary duplicates into about 20 copies; run the exact same race through Duped and it stays exactly one. Then you can run the real invariant SQL yourself and watch it return count = 1, ledger drift = 0.

How we built it

Aurora DSQL is the single source of truth. The whole guarantee lives in one statement — UPDATE item_instances SET owner_id = :to, version = version + 1 WHERE instance_id = :id AND owner_id = :from AND version = :expected — where a row count of 1 is required; two racing transfers can't both match, and DSQL's optimistic concurrency surfaces the loser as SQLSTATE 40001, which the kernel retries with the same idempotency key. Every economic action goes through one function, executeTrade, which does the version-guarded item moves, the sharded conditional gold debit/credit, the balanced ledger, an idempotency-registry write for exactly-once, and a transactional-outbox event — all in a single DSQL transaction. A projector drains that outbox into DynamoDB, which powers the live world read model. The front end is Next.js on Vercel; auth to AWS is IAM/OIDC federation, so there are no passwords in code. DSQL has no foreign keys and builds indexes asynchronously, so integrity lives in the service layer plus audit queries, and money is always BIGINT minor units.

Challenges we ran into

Aurora DSQL is PostgreSQL-compatible but not PostgreSQL: no foreign keys, one DDL statement per transaction, and indexes built asynchronously — so we gate traffic until the unique idempotency indexes are ACTIVE. We hit a real table-name collision in a shared DSQL database and isolated our tables (idempotency_registry and event_outbox became trade_idempotency and world_outbox) so a seed could never wipe a neighbor's data. We caught a nasty concurrency bug in our own kernel — a trade id briefly held in module scope instead of per-attempt, which under massive concurrency would corrupt everything — and fixed it to a strictly local value. Making the cloud demo self-sufficient meant replacing a background worker with project-on-read, so the live feed populates with no cron and no laptop. And to make the contrast beat honest, we built a deliberately broken model that genuinely duplicates the item under a real race — then proved the same race does nothing to the real kernel.

Accomplishments that we're proud of

The numbers are measured, not claimed. A 10,000-attempt dupe storm against the single legendary blocked 9,992 attempts, settled 8 legitimate transfers, surfaced 632 optimistic-concurrency retries, and finished with the legendary count at exactly 1 and zero errors. On a real peered Aurora DSQL cluster spanning Tokyo (ap-northeast-1) and Seoul (ap-northeast-2), 482 of 482 simultaneous cross-region grabs of the same item were blocked. A 1,000-transfer gold double-spend left supply unchanged at 600,000 minor units. Independent marketplace trades scaled linearly — 42, 103, 204, then 383 trades per second at 10, 25, 50, 100 concurrency, with zero contention. Twelve unit tests pass and CI is green, and pnpm reconcile reports every critical invariant holding: legendary count = 1, no instance owned twice, gold supply = minted, ledger drift = 0.

What we learned

The cleanest correctness guarantees aren't checks you run — they're shapes you give the data, so the bad state can't be written down. We learned that one contested item is supposed to be serial: that single hot row is exactly why duplication is impossible, while a million players trading their own items are a million independent rows that scale out. Aurora DSQL's active-active model turned the scariest case — a cross-region dupe — into the easiest, because both regions serialize against one logical database. And we learned to say the quiet part out loud: the guarantees hold as long as every economic action goes through the kernel.

What's next

Wider multi-region topologies and a measured failover story; a thin client SDK so a studio can route trades, drops, mail, and auction settlements through the kernel in an afternoon; and leaning into the honest positioning — Duped is the economy settlement layer (trades, marketplaces, cross-region transfers), not the real-time combat loop. The bigger the virtual economy and the realer the money behind the items, the more this matters.

Built With

Share this project:

Updates