Trust model
Who can do what. The honest answer, not the marketing one.
Since 2026-10-02 almost every contract of the protocol is upgradeable: StockFun's owner can replace its code, with immediate effect. Since 2026-10-05 the protocol's numbers, from the tax to the airdrop's cycle, are also onchain settings of the owner, with immediate effect too: the figures this book gives are their defaults, listed below with what stays fixed. Every rule this book attributes to a module is the rule of its current implementation. Outside the upgrade power: the tokens, the liquidity lock and, on Robinhood Chain, the remote vault deployer.
What nobody can do
These rest on contracts that cannot be upgraded. They hold whatever the owner upgrades or sets.
- Mint new tokens. Supply is minted once, in the constructor, and the tokens cannot be upgraded
- Move a holder's tokens without an allowance from them. The tokens' setter,
setRecorder, moves no balance, and their rescues, since 2026-10-05, move only what was sent to the token's own address - Remove a pool's liquidity without 30 days' public notice. The lock cannot be upgraded, its only exit is the end mode, below, and its 30 days are a constant, not a setting. Its rescues, since 2026-10-05, reach neither the positions nor the fee shares it keeps for their recipients
- Change a live pool's LP fee or tick spacing. Each pool keeps those it was created with: they are part of its key, which the lock keeps
What the current code rules out
Nobody can do these under the current implementations, the owner included. Each rests on a module the owner can upgrade.
- Add liquidity to a StockFun pool. Only the liquidity lock can, since 2026-10-01
- Block trading through a fee wallet or a vault. Only the treasury share is paid during a trade, and since 2026-10-05 a vault that refuses it is owed the amount instead; the others are claimed afterwards
- Change a basket's composition after creation
- Change the price feeds an existing vault reads. Each feed is written once, when the oracle is initialized, and each vault keeps its oracle; the feeds' heartbeats are settings, and since 2026-10-06 the oracle's two guards on Robinhood Chain, which can only hold a price back, never change one
- Send
$STOCKFUNbuyback tokens anywhere but the burn - Choose who receives an airdrop, or claim someone else's share. The split follows from the recorded holdings, pro rata, and only the holder claims
What a creator can do
- Earn the creator line of every trade on their market, 2 % by default, and claim it, since
2026-10-05 to another address if they wish (
claimCreatorFeesTo) - Set their market's anti-snipe whitelist, whose addresses pay the normal tax during the first blocks: at most 20 addresses by default, fixed in the creation transaction, public, and unchangeable afterwards
- Buy and sell their own token, like anyone
They receive no allocation, do not control the liquidity, and cannot pause their market. They have no power over the treasury: they receive stocks only as any holder does.
What the keeper can do
Trigger conversions, at a moment of its choosing, in amounts it names, on routes it proposes. Always inside the vault's oracle bounds and each stock's reservation, and never choosing who receives what. Since 2026-10-01, skipping a stock no longer changes the basket's weights: that stock keeps its share for later. Since 2026-10-05 the vault holds the minimum the keeper names on what actually arrives. Since 2026-10-06 the keeper converts a vault's ETH once per airdrop window, as decided on 2026-09-27: that is the keeper's own rule, which the vault does not enforce; the vault only records when its ETH last converted.
Since 2026-10-05 it is also the only one that sends a bridge batch (bridgeReady) and, with
StockFun's owner, the only one that pre-deploys a market's mirror vault on Robinhood Chain
(predeploy). Until then both were open to anyone. The remote hub learns the keeper from a
batch, so before the first batch StockFun's owner pre-deploys the first market's vault, and a
market whose vault the keeper cannot pre-deploy is left out of its batch.
On the airdrop, since 2026-10-04: open a market's cycle (openCycle), send a vault's
stocks to the airdrop contract (sendToAirdrop, paying the LayerZero fees from Robinhood
Chain) and place held-aside stocks (assignUnassigned). It chooses when, never how much,
what, where or for whom: the amount is the vault's balance, the destination is the
contract the protocol names, and the split follows from holdings. Anyone can open a cycle
or place held-aside stocks, with the same result whoever calls. A send that arrives after
the next closing time, 13:00 UTC by default, is measured over the next day's window. Its
daily step is written since 2026-10-05: see The keeper. Since the seventh audit
loop, on 2026-10-06, it sends a stock only once it is worth what sending it costs, which
decides only when that stock goes: what waits stays in the vault for a later window. Since
the ninth, the same day, it also names the gas each delivery gets on Ethereum, which the
remote hub holds between the floor and the ceiling StockFun's owner sets, and runs again from
its own key a delivery stuck on LayerZero's endpoint on Ethereum, which anyone may do: neither
changes what is delivered, nor where.
Since 2026-10-05 it also pays, every cycle, what the hook and the lock keep for a recipient
that refused it (payTreasury, payOwed), and collects the LP fees of a pool created with
one (collectFees). Anyone can make those three calls, which pay only the recipients the
contracts name.
Its key is a hot key. It never chooses where an asset goes.
What StockFun's owner can do
This is where the real trust sits. StockFun's owner is the protocol owner: the owner of the factory on Ethereum, which the remote hub mirrors on Robinhood Chain.
- Upgrade every module except the tokens, the liquidity lock and the remote vault deployer, with immediate effect and no notice. The vaults are upgraded one by one, market by market, on both chains
- Change the protocol's numbers, with immediate effect and no notice: the tax, its split and the anti-snipe, the creation fee, the shape of new markets, the conversion threshold and the vaults' price bounds, the airdrop's cycle, and the others listed below
- Start the lock's end mode and, 30 days later, recover the whole liquidity of every
pool,
$STOCKFUN's included, to any address - Name the holding recorder each token reports its transfers to. If the report fails, the
transfer fails: the one deliberate exception to the rule that one failure never blocks
the rest (see Architecture), with two immediate levers,
setRecorder(0), which stops the token's record, and an upgrade of the recorder in place. Since 2026-10-05 a token refuses a recorder bound to another UniswapPoolManagerthan its pool's. The recorder is upgraded in place, which keeps its history, and is not replaced on a live token: since 2026-10-05 a replacement starts from the token's supply outside the UniswapPoolManager, so trading continues, and the airdrop measures no window that started before it; but it reads the holders it has not seen move as having held nothing until their next move, so a window they span pays them less, and nobody more. Until 2026-10-05 a fresh recorder made every sell fail and broke the airdrop shares of the cycles opened afterwards - Register baskets, before they are frozen
- Set the write-once addresses, once
- Set the keeper and the fee wallets. A claim pays the wallet set at the time of the claim, so a change also redirects the team or buyback balance not yet claimed
- Change the official swap router recorded in the factory, the one through which the hook recognises the anti-snipe whitelist
- Point future vaults at another stock router or oracle registry; existing vaults keep theirs
- Map each basket stock to its Robinhood Chain token, append-only
- Set each token's airdrop exclusion list, for the windows that close afterwards
(
setExclusions); register each stock's OFT on Ethereum (registerStockOft) - Name the airdrop contract the vaults send to (
setAirdropDistributor); a replaced one keeps every cycle claimable where it is. On Robinhood Chain, name the airdrop route, the contract and the gas of each delivery (setAirdrop), and each stock's adapter (setStockAdapter); since 2026-10-06, set the default, the floor and the ceiling of the gas each delivery gets on Ethereum (setAirdropReceiveGas,setAirdropComposeGas) - Replace the bridge adapter for future sends, at once (
changeAdapter), with an adapter that keeps exactly the same pins: the same hub, factory, cash tokens, remote hub and destination. The 2026-09-29 security audit found that the remote hub would refuse the new adapter's batches (M-2); on 2026-10-05 the owner decided to keep it as it is, so an adapter is changed by upgrading it in place, at the same address - Pause conversions and bridging, immediately. A paused remote hub still applies the role changes each batch carries. On the airdrop contract, the pause stops the sends, the openings and the placing of held-aside stocks, never a claim
- Move any asset out of a treasury, a bridge hub or the airdrop contract, to any address,
at once and without notice (
emergencyTransfer), on either chain, at any time; on the remote hub, since 2026-10-05, also charged to one market, whose books are settled in the same call (emergencyTransferFromPending,emergencyTransferRecord) - After such a transfer out of the airdrop contract or the remote hub, write the loss off
the cycle or the market that suffered it (
writeDownCycle,writeDownUnassigned,writeOffPending,writeOffRecord, since 2026-10-05). A cycle can be written down in part only while nobody has claimed the stock from it, every holder then losing the same share; once some holders have been paid, only by its whole remainder, which the holders not yet paid lose, and what reaches the cycle afterwards is shared pro rata among all its holders again. No other market pays for it; until the loss is written off or the assets come back throughrestore, which anyone can call, the payouts of what is missing wait, and the others go on: a claim pays the other stocks. Both contracts count what backs their books, never their balance: assets on their way to another market never pay for the loss, and a plain transfer to them backs nothing - Move out what a module holds by mistake (
rescue, since 2026-10-05): ETH or tokens sent to a module that keeps nothing of anyone's; the hook's strays, never what it owes; what the lock holds beyond the fee shares it keeps, never its positions; theBuybackBurner's ETH only once no burn could spend it; what was sent to a token's own address. See Emergency mode - Change the LayerZero configuration of the airdrop's stock adapters, without notice and with no cap on withdrawals
The upgrade is the widest of these powers: it reaches every rule of the second list above, at once. With the settings, the end mode, the emergency transfer and the stock adapters' configuration, it means StockFun is not trustless and does not claim to be. The treasury is guarded by the protocol with a recovery path controlled by the owner.
The settings
Since 2026-10-05 the protocol's numbers are onchain settings of StockFun's owner, on both chains. Each takes effect in the transaction that sets it, with no notice, emits an event, and refuses impossible values: a rate above 100 %, a split larger than the tax, a launch shape a pool cannot hold. The values below are the defaults, the ones this book gives.
| Setting | Default | Set on |
|---|---|---|
| The tax on every trade, buys and sells | 5 % | Hook, setTaxSettings |
| Its lines on a launched market: treasury, creator, team; the buyback takes the rest | 2 %, 2 %, 0.5 %; buyback 0.5 % | Hook, setTaxSettings |
Its lines on the $STOCKFUN market: treasury, team; the buyback takes the rest |
2 %, 2.5 %; buyback 0.5 % | Hook, setTaxSettings |
| The anti-snipe: tax in the opening block, decrease per block, blocks it lasts | 80 %, 8 points, 10 blocks | Hook, setTaxSettings |
| The largest anti-snipe whitelist of a market | 20 addresses | Hook, setTaxSettings |
| The creation fee, paid to the team wallet | 0.001 ETH | Factory, setCreationFee |
| The length limits of a new market: ticker, name, image URI, description | 2 to 10, 48, 256, 512 bytes | Factory, setStringLimits |
| The shape of new markets: supply, band 1, ticks, tick spacing, LP fee | 1,000,000,000 tokens, 700,000,000 in band 1, ticks 195,000 / 171,960 / −887,220, spacing 60, no LP fee | Factory, setLaunchConfig |
What the locked positions collect, ETH side: shares of the market's vault and of the team; the creator, or the team on $STOCKFUN, takes the rest |
50 %, 25 %; creator 25 % | Factory, setLpFeeShares |
| The smallest amount a vault converts, and its price bounds against the oracle on ETH → USDC and on a stock purchase | 0.1 ETH, 50 bps, 200 bps | Factory, setConversionParams |
| The price bound of the mirror vaults' purchases | 200 bps | Remote hub, setStockMaxSlippageBps |
| The oracles' heartbeats: ETH/USD, each stock | 1 hour and 1 day in the deployment scripts | Each chain's oracle, setEthUsdHeartbeat, setHeartbeat |
| The sequencer check: Chainlink's L2 sequencer uptime feed and the grace period after a restart, during which every price of the oracle is held back (since 2026-10-06) | Off: no such feed exists for Robinhood Chain, so the deployment runs with the check off; 3,600 seconds of grace when a feed is set | Each chain's oracle, setSequencerUptimeFeed; off on Ethereum |
| Each stock's oracle pause: its price held back while its token says its oracle is paused for a corporate action (since 2026-10-06) | On for every Robinhood Chain stock, off on Ethereum | Each chain's oracle, setOraclePauseCheck, per stock |
| The airdrop's windows: length, closing time | 24 hours, 13:00 UTC | Airdrop contract, setCycleSchedule |
The airdrop's limits: largest exclusion list, windows one assignUnassigned checks |
16, 30 | Airdrop contract, setMaxExcluded, setMaxWindowsPerAssign |
| The airdrop's LayerZero endpoint and source chain | As deployed | Airdrop contract, setLayerZero |
| The USDG bridge: worst USDG-per-USDC rate on Curve, and the part of a batch's last step on Robinhood Chain every batch needs (one figure for the whole step, 1,200,000, until the tenth audit loop) | 30 bps, 200,000 gas | UsdgOftAdapter, setSettings |
| The USDG bridge's batch: the gas each market of a batch adds to that step, and the most markets one batch carries, which LayerZero's message size sets (since the tenth audit loop; the largest batch's gas at most 24,000,000 whatever the settings) | 400,000 gas, 17 markets | UsdgOftAdapter, setBatchGas |
| The canonical bridge: gas of its two tickets, whose submission costs are floors | As deployed | ArbitrumCanonicalAdapter, setTicketGas |
| The canonical bridge: bytes the deposit ticket's cost is computed on | 1,024 | ArbitrumCanonicalAdapter, setDepositCalldataLength |
| The records one sweep pays on the remote hub | 64 | Remote hub, setMaxRecordsPerSweep |
The lzReceive gas of each airdrop delivery on Ethereum, on top of what the stock's OFT enforces: the default, the floor and the ceiling of what the keeper asks for (since 2026-10-06) |
650,000, 200,000, 1,500,000 | Remote hub, setAirdropReceiveGas |
| The compose gas of each airdrop delivery on the airdrop contract: the default, the floor and the ceiling (one figure, 600,000, until 2026-10-06) | 1,250,000, 600,000, 4,000,000 | Remote hub, setAirdropComposeGas; the default also with setAirdrop |
- Every pool, from the next swap. A change of the tax or the anti-snipe applies from the next swap on every pool, a pool still inside its anti-snipe blocks included: the decay is computed with the settings in force, from the pool's launch block. The anti-snipe never charges less than the tax. The whitelist cap applies when a market is created
- New markets only, for their shape. A new supply, new bands, a new tick spacing or a
new LP fee apply to the markets created afterwards. Each pool keeps the LP fee and tick
spacing it was created with, and the Lens gives them, market by market; the
$STOCKFUNpool takes those in force when it is launched - Live, for the vaults. Every vault reads the conversion threshold and its price bounds at each conversion, and the lock reads the LP fee shares at each collection
- Open cycles keep their window. A new airdrop schedule applies to the windows not opened yet, and re-cuts them, those that held-aside stocks are still to look at included
What stays fixed:
- The end mode's 30-day notice,
END_DELAY, a constant of the liquidity lock, which cannot be upgraded - The immediacy of the emergency transfer: it has no delay, and no setting can add one
- Units and encodings: the basis point, the hour as the unit of the holding record and of the airdrop's windows, the burn address, the message formats
- The wiring: the price feeds, the pools the stock router buys on, on Ethereum, the USDG OFT, the bridge's LayerZero endpoints, the remote hub, the Curve pool. They are fixed at deployment or written once; changing one means pointing at another contract, which takes an upgrade
Upgrades
Since 2026-10-02 every module is a proxy upgraded through UUPS, except the contracts below. How it is built: see Architecture.
| Chain | Who upgrades | Modules |
|---|---|---|
| Ethereum | The factory's owner: every module asks the factory who it is | The factory itself, the hook, the holding recorder, every TreasuryVault, the oracle, the stock routers, the official swap router, the BuybackBurner, the Lens, the bridge hub and its adapters, the airdrop contract |
| Robinhood Chain | The remote hub's emergency admin: the Ethereum owner as the last bridge batch carried it and, before the first batch, the initial admin named at deployment | The remote hub itself, every mirror vault, the stock router, the oracle |
- Immediate. An upgrade takes effect in the transaction that makes it: no timelock, no notice, as for the settings and the emergency transfer
- Checked. Only the protocol owner can upgrade. A new implementation must answer to the
same upgrade authority, and the hook's and the holding recorder's must keep the same
PoolManager - Market by market. Each vault is its own proxy. A new vault implementation named in the factory applies to the markets created afterwards; existing vaults are upgraded one by one
- Storage appended, never reordered. Each module's storage layout is recorded, and a script checks it before every upgrade, at every depth of every struct since 2026-10-05
These cannot be upgraded:
- The tokens, market tokens and
$STOCKFUN: plain ERC-20s whose supply is minted once, with no mint, burn, pause or blacklist. Their one setter,setRecorder, and their rescues belong to the protocol owner - The liquidity lock, whose code is what keeps the liquidity in the pools. Its only exit for the liquidity is the end mode; its rescues reach neither the positions nor the shares it keeps
- The remote vault deployer, on Robinhood Chain: every mirror vault's address is derived from its own
The two deployers on Ethereum, which hold no state, are replaced through the factory rather than upgraded.
The end mode
The liquidity lock's one exit, added on 2026-10-02 for the case where the project shuts
down. StockFun's owner calls end(). From then on no new market can launch, while every
pool keeps trading. Thirty days later the owner can recover the whole liquidity of every
pool, to any address. The owner can cancel at any time while the end is pending; pools
already recovered stay empty. The 30 days are the holders' notice. Detail in
The two-position launch.
External dependencies
| Dependency | What it can break |
|---|---|
| Paxos / USDG | Can pause or freeze. The rail's cash leg stops. Since 2026-10-05 a freeze of one mirror vault stops only that vault's deliveries: its share waits on the remote hub, owed to its market, and the other markets are paid (R2H-2, closed) |
| LayerZero | A lost message strands funds in transit until replay or recovery. Since 2026-09-28 it also secures the wrapped stocks of the airdrop |
| Robinhood Chain | A young chain. An outage freezes the stocks held. Since 2026-10-06 the oracle can also hold every price back while Chainlink's sequencer uptime feed says the sequencer is down or just back up; that check is off until Chainlink publishes such a feed for Robinhood Chain, which it has not |
| Chainlink feeds | A stale feed blocks the conversion that needs it; since 2026-10-05, for a stock, that stock's leg only. Since 2026-10-06 so does a corporate action: while a stock's token says its oracle is paused, the feed holds its last value and the oracle holds that stock's price back, so its leg waits, its cash kept for it, and the others buy. That is the intended behaviour |
| Secondary liquidity | If pools are too thin, the keeper buys in smaller slices, and the price bound, 200 bps by default, refuses what still cannot fill |
None of these dependencies is hidden, and none can move an asset out of a vault to an address.
Accepted risks
- An upgrade, a change of setting and an emergency transfer take effect at once: holders get no notice of them, unlike the end mode, announced 30 days ahead
- Every token transfer depends on the holding recorder: a report that fails makes the
transfer fail. It is the one deliberate exception to the rule that one failure never
blocks the rest: a report allowed to fail would let a holder skip the record and grow
their airdrop share. Its levers are immediate:
setRecorder(0)on the token, or an upgrade of the recorder in place - A share whose recipient refuses it waits for that recipient, on the hook, the lock, the remote hub or the airdrop contract, instead of stopping the rest; and an emergency transfer charged to no market makes the payouts of that asset wait until it is settled
- No treasury accumulates: everything is distributed, and an airdrop may be zero if nobody trades
- The airdrop is paid in wrapped stocks on Ethereum: they are worth only the stocks locked on Robinhood Chain and the LayerZero configuration, and a freeze of the stock tokens by their issuer would block the locked stocks
- A market may never exhaust its first liquidity band
- A round trip costs 9.75 % before any price movement, at the default 5 % tax
- The cross-chain rail had not been exercised under real conditions at the time this book was written