Apple pulled Telegram from the App Store this week. It was back within hours, once the offending content was removed and the account behind it banned, and Gram recovered most of what it had dropped on the news. Nothing about the protocol changed while it was gone. No chain halted. No contract was touched. The only thing that stopped working was the layer sitting between a billion users and everything underneath them.
That layer is the one part of crypto gaming nobody audits.
TL;DR
- Provable fairness is a property of a smart contract. The website, app or mini app you actually played on inherits none of it.
- A compromised front end cannot forge a chain result or fake a Chainlink VRF proof. It can point your wallet at a different contract, misstate the odds, hide the requestId, or serve a “verification” page that quietly queries the operator’s own database.
- None of those attacks break cryptography. They break the link between you and the cryptography, which is cheaper and much harder to notice.
- It is the same defect as presence versus invocation, one floor up: a VRF integration can be entirely real and still have nothing to do with the bet you just placed.
- The fix needs no cooperation from the platform. Pin the contract address from a source that is not the site, read the address in your wallet before signing, and verify outcomes from your own transaction history on BaseScan.
The layer nobody verifies
We have spent a lot of this blog on the parts of a game you can genuinely check. Whether the randomness came from Chainlink VRF. Whether the coordinator verified the proof before the callback fired. Whether the outcome was computed inside that callback transaction. Whether stake, draw and payout settle atomically.
Every one of those is a property of a smart contract. Not one of them is a property of the thing you actually touched.
The front end is a Web2 application and it never stopped being one. It is served from a domain somebody owns, off infrastructure somebody rents, built from JavaScript that can be different this morning from what it was last night, distributed through an app store that can pull it on a Monday afternoon. It holds the contract address your wallet is asked to sign against. It renders the odds you read before you clicked. It shows you the result. From the player’s point of view it is the product, and it carries none of the guarantees people cite when they call a game provably fair.
What a lying front end can and cannot do
Worth being precise, because the honest version of this argument is narrower than the frightening one.
A malicious or compromised interface cannot forge a chain result. It cannot make BaseScan show a coinflip you lost as one you won. It cannot fabricate a VRF proof, because the coordinator verifies that proof on chain before the callback executes, and no quantity of client-side JavaScript can reach into it. Whatever happened on chain happened, and it stays happened, whether or not anybody ever looks.
What it can do is decide which chain events are yours.
It can point your wallet at a contract other than the one in the documentation: same interface, same name, different resolution logic. It can display a house edge the deployed code does not implement. It can show you a requestId belonging to somebody else’s draw, or never show one at all, which is far more common and works better. It can serve a “verify this result” page that looks like a block explorer and is reading the operator’s own database. It can stop rendering results during a window in which something went wrong. And it can do any of this to some users and not others, because it is a server, and a server always knows who is asking.
Notice the shape of it. None of these attacks break cryptography. They break the link between you and the cryptography, and leave the honest contract sitting there, being honest and being irrelevant.
Presence versus invocation, one floor up
We made an argument here recently about the Coldcard entropy flaw that survived five years of audits: the auditors confirmed the good RNG existed in the codebase, not that it was ever called. Presence and invocation are different claims, and randomness is uniquely exposed to the gap between them, because dead randomness returns output indistinguishable from live randomness.
The front-end problem is that defect one floor up. A platform can integrate Chainlink VRF for real. Deploy a real consumer contract. Request real randomness. Emit real events that a real explorer will really show you. And still ship an interface that routes your stake somewhere else entirely. The integration is genuine. The connection between it and your money is the part nobody checked.
Which is why “we use Chainlink VRF” is a statement about a codebase, not a statement about your last bet. The first is a claim a platform can make perfectly honestly. The second is a claim only you can settle.
Closing the gap yourself
This is the most fixable trust gap in the stack, and the fix has the property we keep insisting on: it needs no cooperation from the platform, and no awareness on its part that you are doing it.
- Pin the address from somewhere that is not the site. Take the contract address once, from documentation, a repository, a block explorer, ideally more than one. Then trust that string rather than whatever the page is serving today.
- Read the address in your wallet, not on the page. Your wallet shows the contract you are about to interact with before you sign, and it takes that from the transaction, not from marketing copy. If it does not match your pinned address, nothing else on the page matters.
- Check the contract is verified, then check whether it is a proxy. Verified source means you can read the resolution logic. An upgradeable proxy means that logic applies until somebody decides otherwise, which is presence versus invocation wearing a helmet.
- Verify from your own transaction history. Your wallet holds a record of what you signed that the operator cannot edit. Take the hash from there, open it on BaseScan, follow it to the resolution. If your only route to a result is a page the platform renders, you have not verified anything. You have been shown something.
- Ask what survives the site going dark. If the domain expired tonight, could you still establish what happened in last night’s game and whether you were paid? On chain, the answer is yes and the interface was a convenience. Everywhere else, the answer is a support ticket.
Where Satoshie actually stands
Satoshie’s raffles and coinflips resolve on Base, in verified contracts, with Chainlink VRF supplying randomness the coordinator proves before the callback runs. Coinflip is one modulo over the returned word. A raffle is keccak256 of the VRF word and the prior blockhash, modulo ticketsMinted, with equal-weight tickets and ticketsMinted readable as contract state before you enter. Stake escrow and payout happen in the transaction that decides the outcome. The hands-on version is here: auditing a Satoshie result on BaseScan.
None of that is a claim about our website. Our front end is a front end, and it is the least trustworthy component in the system, because every front end is. The distinction worth drawing is that everything determining whether you won and whether you were paid happens somewhere our interface cannot reach, in code you can read, on a ledger we do not own. If our site vanished tomorrow the games would still be there, still resolving, still auditable by anyone who kept the address.
Provable fairness that depends on the honesty of a web page is not provable fairness. It is a better-looking version of the promise everybody else is making.
Three questions to ask any crypto game
- Do you know the contract address you are playing against, from a source that is not the interface serving you the game?
- When you last verified a result, did the proof come from the chain, or from a page the platform rendered for you?
- If the app were pulled from the store tomorrow, as Telegram briefly was this week, which part of your position would still be verifiable?
📷 Photo by Rami Al-zayat on Unsplash


