Every token launched on Pons V2, the largest launchpad on Robinhood Chain (4663), pays its creator a cut of every trade for as long as the token trades: 70% of the 1% hook fee, plus a creator tax of up to 10% set at launch. Pons writes that stream into its own contracts and pays it into an escrow the recipient claims.
OSKET turns one launch's stream into 1,000,000 transferable royalty shares. The vault that holds the stream is the launch's fee recipient and has no function that hands it on — Pons only lets the current recipient move the recipient — so the stream stays where it is. Creators sell shares for ETH today; holders harvest the fees and can take them as a stock token (NVDA, SPY, …) through a swap whose minimum they set.
Status: deployed on Robinhood Chain at 0x386B44bb8fbe812A60429F804ADf051B5DaA70EE. A share of someone else's trading revenue is almost certainly a security; not offered to persons in the United States or anywhere else it is not lawful. Nothing here is investment advice.
What is in here
| path | what it is |
|---|---|
contracts/src/RoyaltyVault.sol | the shares (ERC-20, pull-based per-share accrual that travels with transfers), harvest(), claim(), claimAsStock(minOut), the creator's listing and 30-day vesting of sale proceeds. No owner. |
contracts/src/RoyaltyFactory.sol | launchWithRoyalties (a new Pons launch with the vault as recipient from block one) and prepareAdoption / finishAdoption (an existing launch whose recipient moves the stream with Pons's own call) |
contracts/src/interfaces/IPons.sol | the slices of PonsV2LaunchFactory, the fee escrow, the meme hook and Uniswap's SwapRouter02 the vault uses, matching the verified contracts on 4663 |
contracts/test/RoyaltyVault.t.sol | 17 tests against mocks, including accrual travelling with transfers, the claimer-set swap minimum, re-entrancy, vesting, adoption order, the Pons owner override, a USDG launch and a fuzz over conservation |
contracts/test/Fork.t.sol | 3 tests on a fork of chain 4663 through the real Pons factory, curve, escrow and SwapRouter02: a launch through Osket makes the vault the recipient and the creator cannot move it; a real curve buy accrues creator tax that harvest sweeps and holders claim in ETH and as NVDA; an existing launch is adopted in three steps |
contracts/SECURITY.md | an internal review: the threats considered and what holds against each |
research/local-rpc.cjs | a local forwarder to the public RPC for the fork tests, resolving the host over DNS-over-HTTPS (this ISP hijacks it) |
contracts/scripts/deploy-royalties.cjs | the only script that can send a transaction. Dry-run unless --yes; cross-checks Pons's feeEscrow() and memeHook() live before simulating |
keeper/royalties-keeper.cjs | harvests vaults whose pending credit (creator tax still on the curve plus the escrow balance) clears a floor, finishes adoptions once Pons pays the vault, raises an alarm on owner-override proposals. Dry-run unless --send |
research/creator-fees-24h.cjs | reads 24 hours of PoolFeesSwept from the meme hook: what Pons paid creators, by quote asset, and the top streams |
research/stock-routes.cjs | which stock tokens a claim can be taken as: the official Robinhood asset list checked against symbol() on chain, and the best Uniswap v3 path from WETH and from USDG by live QuoterV2 quotes → evidence/stock-routes.json. The page ships only tokens with a route both ways |
site/ | the page: template.html + build-site.cjs → index.html, royalties.html, public/. Reads the chain from the browser; transactions sign through the reader's own wallet and go only to the factory, a vault, or Pons. Its motion (masked headlines, the stream that draws on scroll, counters, the live block number, the tape of top streams) is transform and opacity only; the block number and every figure still trace to the chain or an evidence file |
site/public/api/rpc.js | a read-only JSON-RPC relay (bounded eth_getLogs) for readers whose network cannot reach the public RPC host |
site/deploy.html + site/serve.cjs | local only: deploy the factory with the wallet in your browser; the page verifies the mined factory and the local server writes the deploy record |
contracts/scripts/verify-sourcify.cjs | publishes the exact standard-JSON build to Sourcify for a deployed factory or vault |
contracts/scripts/verify-vaults.cjs | lists every vault the factory has deployed and publishes the ones Sourcify does not know yet; part of the scheduled refresh |
scripts/refresh-evidence.cjs + scripts/refresh.cmd | re-measure fees and routes, verify new vaults, rebuild page and docs, test, deploy, commit; the .cmd is what Task Scheduler runs every 6 hours |
keeper/Dockerfile | the keeper as a container (dry-run without a key) |
The four facts it rests on
Read from PonsV2LaunchFactory (Sourcify exact match, 0x7ed598bcef8bd9edd8c97a195c6d13f40801ec7e):
transferCreatorFeeRecipient(token, new)reverts unlessmsg.sender == launch.creatorFeeRecipient. The vault is the recipient and never calls it.setCreatorFeeRecipientisonlyOwner(Pons's 2-of-3 multisig) behindCREATOR_FEE_RECIPIENT_TIMELOCK = 3 days, with a publicCreatorFeeRecipientChangeProposedevent. That override is the one thing that can move a stream;overridePending()exposes it and the keeper alarms on it._payOutcredits the fee escrow (credit{value}/creditToken); the recipient callsclaim()/claimToken().harvest()does exactly that.sweepPoolFeesand the curve'ssweepFeesaccept the recipient as caller when no internal swap is needed; Pons's sweep operator does the rest, into the same escrow.
Tests
node research/local-rpc.cjs & # forwarder for the fork tests; without it they skip
cd contracts && npx hardhat test solidity # 17 Solidity tests against mocks + 3 on a fork of the live chain
node --test keeper/royalties-logic.test.cjs # 4 keeper tests
node --test site/site.test.cjs # page tests: figures match files, transaction targets, relay is read-only
node --test site/abi.test.cjs # the page's ABI strings match the compiled contracts (needs artifacts)
Reproducing the evidence
node research/creator-fees-24h.cjs # -> evidence/creator-fees-24h.json
node site/build-site.cjs # rebuilds the page from it
Read-only. The RPC goes through curl --doh-url because the local ISP hijacks DNS for the RPC host; override with RH_RPC.
Deploying
Done on 23 Sep 2026: the factory is at 0x386B44bb8fbe812A60429F804ADf051B5DaA70EE on chain 4663 (see contracts/deploy/royalties-4663.deployed.json). To deploy again elsewhere, two ways, both run by a human:
With the wallet in your browser (no private key ever touches a file or a terminal):
cd contracts && npx hardhat compile && cd ..
node site/serve.cjs # then open http://localhost:4521/deploy
The page loads the compiled factory, runs the same live checks as the script (Pons answers, its escrow and hook match the pins, the router has code), lets you set the treasury, estimates gas, and hands one creation transaction to the wallet. When it is mined it reads the factory back, checks every immutable, and only then writes contracts/deploy/royalties-4663.deployed.json through the local server. It refuses to overwrite an existing record.
With a key file, if you prefer a terminal:
cd contracts
node scripts/deploy-royalties.cjs --chain 4663 # dry run
node scripts/deploy-royalties.cjs --chain 4663 --treasury 0x… --yes # signs with DEPLOYER_PRIVATE_KEY or --key-file
Afterwards, either way:
node contracts/scripts/verify-sourcify.cjs --address 0x… --tx 0x… # publish the source on Sourcify (exact match)
node site/build-site.cjs && node --test site/site.test.cjs # the page picks up the record and drops "not deployed"
node keeper/royalties-keeper.cjs --factory 0x… --once # dry run of the keeper
node keeper/royalties-keeper.cjs --factory 0x… --send # the keeper for real, with KEEPER_PRIVATE_KEY
docker build -f keeper/Dockerfile -t osket-keeper . && docker run -e FACTORY=0x… -e KEEPER_PRIVATE_KEY=0x… osket-keeper
site/abi.test.cjs checks that every function the page calls exists in the compiled contracts with the same selector and outputs, so the page and the bytecode cannot drift apart unnoticed.
Limits
- Pons's owner can move any stream with a 3-day public notice. Already-harvested fees stay with the holders.
- About one launch in a hundred graduates; the top 10 streams take most of the fees. A royalty share of a dead launch is worth nothing.
- A creator can wash-trade their own fees before selling shares. Proceeds vest over 30 days and every sweep is on chain; read the history.
claimAsStockneeds a Uniswap v3 path; the page offers only tokens with a measured route from both WETH and USDG (COIN today has none from USDG and is left out).- 17 tests run against mocks of the Pons contracts and 3 against the live ones on a fork; see
contracts/SECURITY.mdfor what was and was not considered.
Not affiliated with Pons, Robinhood Markets or Uniswap Labs.
Security notes
An internal review. Written 23 Sep 2026 against RoyaltyVault.sol and RoyaltyFactory.sol.
What the contracts are trusted with
- ETH or the quote token that Pons credits to a vault's escrow balance, between a harvest and the claims.
- Sale proceeds, between a purchase and the creator's vested claims.
- The fee recipient role of a Pons launch, which the vault holds and can never pass on.
Threats considered
| threat | where | what holds |
|---|---|---|
| Re-entrancy on claim / claimAsStock / buy / claimProceeds / harvest | vault | one nonReentrant lock on every state-changing entry point; state is updated before any external call; the test suite includes an attacker that re-enters claim from receive |
| A share moved after accrual takes its accrual with it | vault | points-per-share accumulator with signed per-holder corrections applied in _mint and _transfer (the ERC-2222 pattern); tested across transfer, claim and second transfer |
| Rounding | vault | the accumulator floors; at most a wei or two per harvest stays in the vault; the fuzz test bounds it and asserts the vault always holds what it owes |
| A harvester steals the swap | vault | there is no shared swap. harvest only claims from the escrow; the swap into a stock token happens per claimer, on their own claim, with a minimum they set |
| A creator re-points the stream after selling shares | Pons | transferCreatorFeeRecipient reverts unless msg.sender is the current recipient; the vault is the recipient and has no code path that calls it |
| Pons's owner re-points the stream | Pons | possible: setCreatorFeeRecipient is onlyOwner behind a 3-day timelock with a public event. Not preventable by design; overridePending() and isRecipient() expose it; the keeper alarms on it. Already-harvested funds are unaffected |
| A creator wash-trades fees, sells shares, stops | market | not preventable on chain; sale proceeds vest over 30 days from the first sale; every sweep is a public event; the page shows the 24-hour history |
A malicious payoutPath at launch | vault | the path is set once by the creator and only affects claimAsStock; a claimer can always use claim() instead; a path that routes through a bad pool can only hurt the claimer who sets a low minimum |
| Non-standard quote tokens (fee-on-transfer, no bool return) | vault | Pons only approves pair tokens it has checked; buy and _pay require a true return; a fee-on-transfer quote would break exact accounting — such tokens are not in Pons's approved list today |
| A vault bound to a token that is not a Pons launch | factory | bind reads getLaunchedToken and requires exists and creatorFeeRecipient == vault |
finishAdoption racing an unrelated transfer | factory | binding requires Pons to already pay the vault; the adopter recorded at prepareAdoption is the only address the shares are minted to |
| Escrow claim reverts (escrow paused/upgraded) | vault | harvest reverts; nothing is lost; funds stay in Pons's escrow under the vault's name |
| Sweep requires Pons's operator | vault | harvest wraps the sweep calls in try/catch; the operator's own sweeps land in the same escrow |
Griefing harvest with dust | vault | NothingHarvested reverts when the escrow holds nothing; there is no per-call fee to grief |
Not covered
- Legal: a share of another party's trading revenue is almost certainly a security in most jurisdictions.
- Pons's contracts themselves (their owner powers, upgrades, and the escrow's solvency).
- Uniswap v3 pool manipulation around a large
claimAsStock; the claimer's minimum is the only defence. - Anything an independent review would find.