Security review
Last updated 2026-09-11. Covers the Fling contracts, the transaction path the website asks you to sign, and the deployment as run end to end on a public server against a fork of live Robinhood Chain.
This is an internal review, not an independent audit
The review below was carried out by the same party that wrote the code. That is the weakest position from which to audit anything: the assumptions that cause a bug tend to hide it again on re-reading. Fling has not been audited by an external security firm.
It is published anyway because knowing what was found is better than a silent claim that everything is fine. Treat it as a statement of what we checked and what we still do not know — not as a safety guarantee. Assume the contracts contain bugs nobody has found yet, and never trade more than you can lose.
Findings
16 issues found and fixed: 5 high, 6 medium, 5 low. Each one is pinned by a test that fails if it comes back. None are open.
Severity means impact on funds or on the protocol working at all, not how easily an attacker could trigger it. Three of the four high findings are not attacks: they are states the protocol could reach from ordinary use, and from which there is no way back. A token that sells out and can never graduate is as bad as a theft, because the money raised is locked with no way out.
- F-01HighFixed
Excess ETH on a curve buy was stranded in the launchpad
A buy accepted more ETH than it wrapped, and the remainder sat in the launchpad counted toward no curve. It now has to match exactly.
- F-02HighFixed
Raising the fee above 2% broke the first second of every launch
Fee and launch tax were subtracted from the same amount, which underflowed at higher fee settings. The tax is now charged after the fee.
- F-03HighFixed
Graduation price could overflow, stranding a sold-out token
For extreme price ratios the pool's starting price overflowed, so a token could sell out and never graduate, locking the raise. The maths now covers the full range Uniswap v4 supports.
- F-04HighFixed
A one-wei rounding difference could block graduation forever
Liquidity was computed rounding down while the pool charged rounding up. The locker now fits the position to what it actually holds.
- F-05MediumFixed
Sell payout was calculated before the reserve draw was capped
A rounding surplus could have paid out more than the curve took in. The cap now comes first.
- F-06MediumFixed
The router paid out its whole balance as a refund
Tokens mistakenly sent to the router went to whoever traded next. Only what arrives during your own transaction is refunded to you.
- F-07MediumFixed
A bad raise target could make tokens ungraduatable
Some raise targets produce a price outside the range Uniswap v4 can hold. Those are now rejected when the asset is configured, not discovered later.
- F-08MediumFixed
The launch tax was charged again after graduation
Graduation seeds the pool at the curve's exact final price, so there is no gap to snipe. The tax punished ordinary buyers for nothing and has been removed.
- F-09MediumFixed
One kind of swap was charged a smaller fee than the others
Exact-output buys under-paid relative to every other route. All four swap shapes now pay the same 1%.
- F-10MediumFixed
Other routers and aggregators could not trade Fling pools
The hook assumed any extra transaction data was a referrer address and reverted on anything else. It now ignores data it does not recognise.
- F-11LowFixed
Fees for an unregistered token were burned
They were credited to the zero address and became unclaimable. They now go to the treasury.
- F-12LowFixed
Claiming several assets failed if any one was empty
Bots could not simply pass every asset they know about. Empty balances are skipped.
- F-13LowFixed
One hook entry point did not check the pool was registered
An asymmetry with its sibling that future changes could have turned into a real hole. Both check now.
- F-14LowFixed
Sells through the router were credited to the router
The trade event named the caller rather than the seller, so holder analytics were wrong for router sells. It now names the seller.
- F-16HighFixed
The site checked which contract you were signing to, but not what you were signing
The app refuses to pass on a transaction from the gateway unless it points at a Fling contract. It did not read the call itself, and every one of those contracts has a method that sends value to an address the caller names. A compromised gateway could therefore hand you a real call to the real fee vault that paid your fees to someone else — or, worse, hand a creator the one call that signs their fee stream away permanently. The app now decodes the call, refuses any method the site never asks for, and refuses any call whose recipient is not the wallet about to sign. It also holds the call to what you asked for: the token whose page you are on, the amount you typed, a slippage floor that is actually set, and a deadline within the hour — and, on a launch, the name, ticker, metadata link and dev buy from the form.
- F-15LowFixed
The deploy script could produce an unrecoverable deployment
It could launch with an unset owner or treasury, and those settings cannot be corrected afterwards. It now refuses, and also refuses test keys and local addresses on a real chain.
What the protocol guarantees
- Supply is fixed at one billion and minted once. There is no mint, burn, pause, blacklist or transfer-fee code in a Fling token, because those functions do not exist in it.
- Graduated liquidity is locked permanently. The locker has no code path that removes liquidity at all — this is not a timelock that expires.
- Anyone can graduate a sold-out token. It needs no permission from us, and we cannot prevent it.
- The pool opens at the curve's exact final price, so the first buyer after graduation has no gap to arbitrage against.
- Launch protections apply to buys only. They never restrict selling or transferring, and they expire on their own after a few seconds with no way to extend them.
- The trade fee can never exceed 3%, enforced in the contract.
What the team can do
Centralisation is the risk that survives a clean review, so here it is in full.
Can
- Enable or disable a quote asset for new launches.
- Change the launch fee, the trade fee up to the 3% cap, and how the fee is split.
- Reassign any token's creator fee stream. This exists so a community can take over an abandoned token, and it is the most abusable power in the system.
- Point the launchpad at a different router. Treat this as equivalent to a code upgrade.
Cannot
- Mint, burn, pause or seize any token.
- Touch a curve's reserves or your claimable fees.
- Withdraw locked liquidity.
- Block a sell or extend the launch protections.
- Stop a sold-out token from graduating.
Testing
Every suite below was run to completion at this commit. The fork suite runs against live Robinhood Chain rather than a simulation.
| Suite | Result | Covers |
|---|---|---|
| Contracts: unit and fuzz | 43 pass | Creation, curve trading, anti-snipe, refunds, graduation, router, fees |
| Contracts: economic properties | 9 pass | Buy-then-sell never profits, fee splits conserve, every rounding favours the curve |
| Contracts: invariants | 5 pass | Random walk over many tokens asserting solvency and conservation after every action |
| Contracts: Uniswap v4 hook | 8 pass | All four swap shapes through a third-party router, on 18- and 6-decimal assets |
| Contracts: FLING token and vesting | 14 pass | Fixed supply, no owner, no revoke, and vesting that can never over-release |
| Contracts: deploy guards | 10 pass | Refusal to launch a real chain with test keys, local addresses or the wrong block height |
| Contracts: live-chain fork | 6 pass | Full lifecycle against the real Robinhood Chain pool manager, WETH and USDG |
| API: end to end | 33 pass | Sign-in, webhooks, live stream, create, buy, graduate, pool sell, fee claim |
| API: soak | 253 pass | Many wallets and tokens at once, with malformed requests mixed in |
| Indexer: consistency | 157 pass | Every indexed balance, trade and fee checked against on-chain truth, token by token |
| SDK, MCP, Python | 34 pass | Curve maths matching the contracts exactly, in TypeScript and in Python |
Deployment findings
6 issues found and fixed by deploying the whole system to a public server and running it against live chain data, rather than on a laptop. None are contract bugs. All are fixed.
They are on this page because the contracts being correct is not the same as the product being correct. Each of these could have put Fling in front of people while it was quietly broken, and the first one would not have shown up in any local test, including a simulated copy of this chain.
- D-01HighFixed
The site would have launched empty, and looked healthy doing it
Robinhood Chain is an Arbitrum chain, where a contract that asks for the current block number is given Ethereum's, not this chain's — on the live chain those differ by about 105 million. The deploy script wrote that number down as the point from which to read history, so a real launch would have started reading 105 million blocks before the contracts existed, shown no tokens and no trades, and never caught up. Every service would have reported healthy throughout. It now asks the chain for its own height.
- D-02MediumFixed
A first install on a clean server stopped early without saying so
The setup script read a settings file that does not exist yet on a fresh machine, and treated that as a fatal error while printing nothing. It now expects the file to be missing the first time.
- D-03MediumFixed
The website could not be built without a domain name
With no domain configured the build assembled an invalid address for the site and failed at the last step. Deploying by IP address, which is what you do before DNS is ready, now works.
- D-04MediumFixed
The indexer could not start against a real database
It needs to be told which database schema to use; the local development database fills that in by itself, so the requirement only appeared on a real server, where the indexer then refused to start at all.
- D-05MediumFixed
An incomplete build produced a contract address file that was quietly missing a piece
The generator skipped anything it could not find instead of stopping. The result was a file that looked complete and crashed the API on startup, a long way from the actual cause. It now refuses to write a partial file.
- D-06LowFixed
Health reported a fully synced indexer as millions of blocks behind
It measured progress by the last block that happened to contain one of our events, not by how far the indexer had actually read. Any alert built on that number would have been firing from the first day forever.
Deliberate choices people mistake for bugs
- You can name yourself as your own referrer and get part of the fee back. That is how integrators are paid and it is capped at the referrer share.
- The per-wallet cap at launch is per address, so someone with many wallets can exceed it. It raises the cost of sniping; the decaying tax is the real deterrent.
- Anyone can add liquidity to a graduated pool and will earn nothing from it, because the pool's own fee is zero.
What this review does not cover
- It was not independent. The reviewer wrote most of the code.
- No formal verification and no symbolic execution. Fuzzing and invariants find bugs; they never prove there are none left.
- The economics were not modelled. Whether the curve, raise target and fee split make healthy markets is a different question, and this review did not ask it.
- Dependencies were assumed sound. Uniswap v4, OpenZeppelin and the chain's own WETH and USDG were treated as correct; only our use of them was reviewed.
- No penetration test and no load test of the website or API.
Found something? Please report it before disclosing it publicly, via the contact on the docs page.
This page describes an internal review of software and is not an independent audit, a security guarantee, a warranty, or financial advice. See the terms and disclaimer.