BULL RUSH re-simulates your run on the server before it will pay out
🐂 BULL RUSH — a 3D neon endless runner for The Black Bull ($ANSEM). React Three Fiber game + Hono/Redis API + dynamic X share cards. Play → trybullrush.xyz
At a glance
- What is it?
- A neon endless runner where every Daily Grid is a deterministic course, the physics simulation in src/sim/ is byte-identical to the server's copy, and three contracts on BOT Chain mainnet record grids, verified runs and prize vaults. The contracts are live; the relayer and the migration branch are not.
- Who is it for?
- Adopt BULL RUSH as a reference implementation of verifiable skill games if you care about the submit, re-simulate, verify loop, since the deterministic engine, the fingerprint check and the three contract registries are all readable here. Do not adopt it expecting a finished onchain product today, because the README says the migration branch is undeployed and no relayer is running, which means the production URL is serving the build from before those contracts were wired in.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 83 days ago.
- What is it written in?
- Mainly Solidity, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 4, 2026, and from our analysis. They are not legal advice.
Editorial analysis
src/sim/ is byte-identical to the server copy, and a fingerprint check guards it
A game where a run can be verified needs a simulation that produces the same result on the player's machine and on the server, so this project treats the engine as a shared artefact rather than as game code.
src/sim/ is the deterministic engine, and the project layout describes it as shared byte-identical with the server. Keeping two copies identical is enforced by scripts: sim:sync pushes changes from one side to the other, sim:check verifies the copies agree, and sim:test runs the sim test suite and then scripts/check-fingerprint.mjs. The local backend exists to exercise the same loop, since docker-compose.yml describes its purpose as testing the full submit, re-simulate, verify loop.
That is the whole verification story in one sentence: the client submits a run, the server re-simulates it against the same engine, and only a matching result is a verified run. VerifiedRunRegistry on chain records them, DailyGridRegistry holds the grids that make every player's course identical, and SeasonPrizeVault holds the sponsor-funded money that is distributed only from verified results.
The design decision worth flagging is that the game has no inflationary game token. Rewards are funded before each competition, and the README states plainly that playing does not guarantee a reward.
npm run build runs four gates before vite touches the bundle
The build script is where this project's discipline shows, and it is not what a game project usually looks like.
npm run buildThat one command chains four checks: node scripts/check-forbidden-assets.mjs, then npm run sim:check, then tsc --noEmit, then vite build. An unlicensed asset fails the build. A simulation that has drifted from the server copy fails the build. A type error fails the build. Only then does the bundler run.
Around it sit the rest of the scripts. test runs vitest, test:integration runs vitest against server/vitest.config.ts, typecheck runs tsc --noEmit on its own, check-assets runs the asset gate alone, and release:check runs scripts/release-check.mjs. There is a knip configuration in the repository as well, which is how unused exports get caught.
Read together, those scripts describe a project that treats replay verification as an invariant to enforce in CI rather than a claim in a README. That is also why the deterministic engine gets a fingerprint check instead of a comment asking developers not to edit one copy.
The contracts are live on chain 677, the relayer is not running
The onchain side is real and named. Three contracts sit on BOT Chain mainnet, chain 677: DailyGridRegistry at 0x9794a7E9bECE87dEe375fE6Eb55620f8Aa788172, VerifiedRunRegistry at 0xcce26fFAd015ee01A4c0BEe9aaEd28C9785D43aF, and SeasonPrizeVault at 0x50D4129474c6204c870c7F141B9A6BE68264b6Ee. All three are source-verified on BOTScan, with deployment evidence in docs/MAINNET-DEPLOYMENT.md.
Then the status note, which is the part to read twice. The contracts are live, but the migration branch, named feat/botchain-mainnet-skill-rewards, is not yet deployed, the production URL currently serves the pre-migration build, and no relayer is running. docs/submission/TESTING-STATUS.md is pointed at as the document that says exactly what is and what is not live.
Those two statements are not contradictory, but they are easy to conflate. Verified contracts on a scanner do not mean the running game writes to them, and a game whose production URL predates the migration is not yet collecting prizes through a vault even though the vault exists.
If you are evaluating this as an onchain game, that document is the first thing to read and the only trustworthy answer to the question.
Redis holds the leaderboards and the one-time run tokens, Postgres holds the truth
The backend is Hono on Node with two stores, and the split is explicit. Redis carries the leaderboards through sorted sets, rate limiting, and one-time run tokens. Postgres is the source of truth. Deploying to Railway means a stateful service for the fast paths and a database behind them.
One-time run tokens are the security-relevant detail. A run token that can only be spent once is what stops a client from submitting the same successful run repeatedly, and rate limiting sits next to it for the same reason.
The configuration surface names five variables on the API side: DATABASE_URL, REDIS_URL, HMAC_SECRET, ALLOWED_ORIGIN and GAME_URL, all set in Railway and never committed. HMAC_SECRET is what signs submissions, and ALLOWED_ORIGIN is the browser-side gate. The game itself reads a single variable, VITE_API_URL, held in .env.production.
The client side is thin by design. src/api.ts is described as a thin client for the API that is offline-safe, which is the right call for an endless runner: losing the network should end the score submission, not the run.
docker compose gives you Postgres 16 and Redis 7 with healthchecks on both
Local work runs against the same loop as production, which means the databases first.
docker compose up -d
docker compose downThe compose file starts postgres:16-alpine on 5432 with database and password bullrush, and redis:7-alpine on 6379. Both have healthchecks, pg_isready for Postgres and redis-cli ping for Redis, with a 2 second interval and up to 30 retries. The scripts wrap this as local:db and local:db:down.
With the stores up, the API runs locally with an env file loaded explicitly.
cd server && node --env-file=.env --import tsx src/index.tsand the verification scripts run against it with npx tsx scripts/verify-local.ts and npx tsx scripts/verify-grid-local.ts, the second presumably aimed at the grid side. Database migrations have their own path through db:migrate, which delegates into server.
For the game itself the documented loop is npm install followed by npm run dev, which serves http://localhost:8080. The README also notes the API can be run with railway run npm run dev inside server, which uses the Railway-hosted Postgres and Redis through the CLI when you would rather not run containers.
The soundtrack is deliberately absent, and the build fails if it comes back
This is the cleanest asset-provenance story in a game repository. The production soundtrack is not included, and as of the migration it must not be deployed until it is replaced with licensed or commissioned music. ASSET_PROVENANCE.md tracks the status of every audio and visual asset, and scripts/check-forbidden-assets.mjs fails the build if any known-unlicensed filename appears. That script is the first thing npm run build runs.
The consequence is that the game ships silent apart from effects. SFX are synthesized in the browser, so it runs fine with no music files at all.
If you do have licensed tracks, the destination and the naming are specified: drop them into public/assets/audio/music/ using the names found in src/audio.ts, which are super-rush, butterfly-war, night-cloud, green-motion and vamp-charge. Those are mood-slot labels, not song titles, which is an important detail if you were about to search for a track called butterfly-war.
The rest of the tree supports the same care: a .gitleaks.toml for secret scanning, LOCAL-TESTING.md, and vitest.config.ts alongside vite.config.ts.
Sign-in is SIWE on BOT Chain, and the share card is rendered server-side
Wallet access is not a username system. src/wallet/ holds the wagmi configuration, the BOT Chain definitions, SIWE for Sign-In With Ethereum, and the useAuth hook, with viem and wagmi in the dependencies. That pairing is why the chain id 677 appears in the contract table rather than on a network switch in the UI.
The deployment split is two platforms. The game is a static site on Cloudflare Pages, deployed with wrangler against a bull-rush project on the main branch, and the API goes to Railway as the bull-rush-api service.
The share card is the piece that justifies a server. functions/ holds a Cloudflare Pages Function serving the /s share page, and share cards are rendered server-side with @napi-rs/canvas as a dynamic OG image, so a posted link carries a card generated for that specific run.
State lives in src/store.ts as zustand game state plus per-frame refs, UI overlays sit in src/ui/, and three/ holds the Canvas, Bull, Track, Obstacles and Game components. React 19, three.js 0.182, Vite 6 and vitest 4 are the current versions in package.json, where the project sits at 0.2.0 with no GitHub releases published. The README closes with the line that it is not financial advice and not an investment, which is the correct label for anything that pays out of a vault.
Editorial conclusion
Adopt BULL RUSH as a reference implementation of verifiable skill games if you care about the submit, re-simulate, verify loop, since the deterministic engine, the fingerprint check and the three contract registries are all readable here. Do not adopt it expecting a finished onchain product today, because the README says the migration branch is undeployed and no relayer is running, which means the production URL is serving the build from before those contracts were wired in. Verify three things first: what docs/submission/TESTING-STATUS.md says is live against the contract addresses on BOTScan, whether the deterministic engine in src/sim/ still matches the server copy in your own checkout, and whether your build passes the asset gate, since the soundtrack is deliberately absent and no unlicensed file may reappear. The licence is MIT, the package version is 0.2.0, and the last push was on 2026-07-20.
Frequently asked questions
What is the BULL RUSH game and how does verification work?
It is a verifiable skill runner on BOT Chain where every Daily Grid gives all players the same deterministic course. A run is replay-verified: the deterministic engine in src/sim/ is shared byte-identical with the server, so the server re-simulates a submitted run and only a matching result counts as verified.
Is the BULL RUSH onchain part live yet?
The three contracts, DailyGridRegistry, VerifiedRunRegistry and SeasonPrizeVault, are live and source-verified on BOTScan for BOT Chain mainnet, chain 677. The README states the migration branch feat/botchain-mainnet-skill-rewards is not yet deployed, the production URL serves the pre-migration build, and no relayer is running.
How do I run BULL RUSH locally?
docker compose up -d starts Postgres 16 and Redis 7, then npm install and npm run dev serves the game on http://localhost:8080. The API runs from server, either with railway run npm run dev or with node --env-file=.env --import tsx src/index.ts, and the verification scripts are verify-local.ts and verify-grid-local.ts.
Why does BULL RUSH have no music in the repository?
The production soundtrack is not included and must not be deployed until it is replaced with licensed or commissioned music. Sound effects are synthesized in the browser, ASSET_PROVENANCE.md tracks each asset, and the build fails if a known-unlicensed filename appears.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/winsznx-bull-rush)