How we made battles deterministic and server-verified in a Telegram mini-game
We are building Seraph Legion, an angel-collector RPG that runs inside Telegram as a Mini App. This is a writeup of the most interesting engineering decision in the project: how battles work , and what a farming exploit taught us about trusting clients. The constraint A Telegram Mini App is just a webview. Anything your client computes, a motivated player can fake - the JS is right there, the API…
Seraph Legion is an angel-collecting RPG built as a Mini App inside Telegram. One key engineering decision was ensuring battles were deterministic and server-verified. Since the client has full access to the code, the design required making the server the arbiter of outcomes. The battle simulator resides in a pure TypeScript module shared between client and server.
It uses a seeded random number generator to ensure identical results given the same inputs. The client reports its perceived outcome to the server, which then re-runs the simulation with the exact same seed and inputs. If the server's replay matches the client's, the rewards are calculated server-side from predefined data tables and entered into the ledger.
If the results don't match, the client's request is rejected. This process prevents cheating by ensuring the server controls all final outcomes. The team learned a valuable lesson from an exploit where a farm of accounts abused a migration endpoint to mint millions. As a result, most write operations are now server-verified, accepting only signed inputs and flagging anomalies for review.
While the client does render the battle animations, it cannot alter the final result. The game can be played at seraphlegion.com for more details on the simulation and the Telegram Open Network (TON) jetton implementation.
Written by urgent.news from Dev.to's reporting — not their text. Machine-written — may contain errors; check the original before relying on it.