How Meed pays
Meed turns "pay these people these amounts" into one transaction that sells exactly enough of your tokenized stocks on Robinhood Chain (chain id 4663) and pays each person in USDG. This page is the whole mechanism, the checks, the numbers and what is not built. The code is plain JavaScript that the page loads as-is.
The transaction
Every payment is one call to execute() on Uniswap's Universal Router, in this order:
| Command | What it does |
|---|---|
PERMIT2_PERMIT_BATCH | Your one signature: for each token, exactly the amount this payment may pull, for the router only, expiring in 30 minutes. Left out when an allowance already covers it. |
PERMIT2_TRANSFER_FROM | Per stock: the ceiling — the planned shares plus your price allowance — pulled into the router once. |
V3_SWAP_EXACT_OUT / V4_SWAP | Per pool: "give me exactly this many dollars", paid for out of the shares the router holds. Up to 4 pools per stock. |
PERMIT2_TRANSFER_FROM | Your own USDG, if you chose to spend it first. |
TRANSFER ×n | Each person, their exact amount, in USDG. |
SWEEP per stock | Every share the pools did not need, back to you. |
SWEEP USDG | Any dollar left in the router, back to you. There should be none. |
Because every sale is exact-output, what each person receives is fixed by construction; what varies is the number of shares sold, and that is capped by the ceiling. If any sale falls short, a TRANSFER fails and the whole transaction is undone. Meed deploys no contract of its own.
The layouts are the ones the router deployed on this chain actually uses, read from its verified source: V3_SWAP_EXACT_OUT takes a trailing uint256[] minHopPriceX36, and v4's ExactOutputSingleParams carry a minHopPriceX36 before hookData. The five-field layout in Uniswap's public docs reverts on this router.
The split
With Keep my mix, the payment is divided across the stocks you ticked in proportion to what each holding is worth at Robinhood's price, so a payment worth a tenth of those holdings sells a tenth of each. A stock is capped at what it can actually raise — Meed prices the sale, and if the shares needed plus your allowance would exceed what you hold, or the pools cannot raise that much, it lowers that stock's cap and divides the rest again. A stock asked for less than $1.00 is dropped and its share divided among the others. With Cheapest, Meed prices the whole payment against each ticked stock alone and sells only the one that costs least (falling back to the mix if none can raise it alone). With Choose amounts, you type the dollars per stock and they must add up. The division is exact to the last unit of USDG (six decimals): the parts always add to the total.
Exact-dollar sales across pools
Most stocks trade in several Uniswap v3 and v4 pools. To raise a fixed number of dollars as cheaply as possible, every pool used should finish at the same marginal price — the next share would fetch the same wherever it went — so Meed searches for that one price and reads off how far each pool must move. It reads each pool's price, liquidity and the tick bitmap around the price, walks the pool word by word exactly as TickBitmap does, and prices every sale to the unit without asking the chain. A sale that would need tick data it has not read is reported as unknown rather than guessed. Splitting is used only when it is cheaper than the best single pool; a pool that would take under 1.5% of the sale is dropped. This engine was built for KERF, a sibling site, and is reused unchanged.
How it is checked: tools/test-sim.mjs prices 800 sales — every listed stock, every pool, four sizes — and compares each with Uniswap's own QuoterV2 and V4Quoter at the same block. 604 agreed to the unit, 0 disagreed, and 196 were beyond the tick data read and were not guessed. Two controls must disagree: the same pool with its liquidity nudged by one part in a million, and the same sale in the wrong direction.
What the page checks before your wallet sees anything
The page decodes its own transaction (assertPay in js/route.js) and refuses to hand it to your wallet unless:
- it calls the Universal Router and uses only the seven commands above, none of them allowed to fail;
- every
TRANSFERpays USDG to an address on your list, for exactly the amount you typed, once — and everyone on the list is paid; - every stock is pulled once, for exactly its planned ceiling, into the router, and swept back to you once;
- every swap is a single pool, sells a stock you ticked for USDG, keeps its dollars in the router, uses no hook and may spend no more than that stock's ceiling;
- each stock's pools raise exactly the dollars planned for it, and the order is permit, pulls and sales, payments, change;
- the signature covers only tokens this payment spends, for at least what it pulls, for the router.
Then it simulates the transaction from your address. A refusal there becomes a sentence — "prices moved past your limit" — instead of a failed transaction.
Approvals
The first time you pay from a stock, your wallet asks you to let Permit2 — Uniswap's approval contract — move it. That is an unlimited approval to Permit2, which is how Uniswap's own apps work: Permit2 still moves nothing without a signature that names the spender, the token, the exact amount and an expiry. Every payment after that is one signature and one transaction. You can revoke an approval at any time from your wallet or the explorer.
Limits, and why
These are decisions, not measurements, and each is written down with its reason in js/config.js.
| People in one payment | 10 |
| Stocks in one payment | 8 |
| Most in one payment | $25,000 |
| Least on one line | $1.00 |
| Default price allowance | 0.5% |
| Signature and transaction lifetime | 30 and 20 minutes |
| Highest pool fee used | 5% |
| A stock is listed if $1,000 sells within | 3% |
A stock is listed only when a Morpho market lends the real USDG against it, which is where Meed reads Robinhood's price for it. At the last scan 10 other tokens were left out, each with its reason: CLSK (a $1,000 sale costs 97.80% against Robinhood's price); CRWV (a $1,000 sale costs 5.09% against Robinhood's price); IONQ (no pool can sell $1,000 of it); RGTI (no pool can sell $1,000 of it); AVGO (no Robinhood price feed through a USDG market); JNJ (no Robinhood price feed through a USDG market); NET (no Robinhood price feed through a USDG market); NU (no Robinhood price feed through a USDG market); QUBT (no Robinhood price feed through a USDG market); SOUN (no Robinhood price feed through a USDG market).
What it costs
Measured at block 64,910,289 by tools/probe-pay.mjs, against Robinhood's price for each of the 45 stocks. Medians across stocks; the three parts are medians of their own, so they need not add up.
| Payment | Cost | Pool fee | Price move | Pool vs Robinhood |
|---|---|---|---|---|
| $100 | 0.26% | 0.24% | 0.01% | 0.02% |
| $1,000 | 0.27% | 0.28% | 0.05% | −0.00% |
| $5,000 | 0.36% | 0.29% | 0.13% | 0.00% |
Gas, measured on a fork of the chain: 460,499 gas for two people paid from two stocks (about $0.06), and 874,180 for ten people from three stocks (about $0.11), at a gas price of 0.0509 gwei and ETH at $2,416.
Tests
Nothing here has been audited by anybody. These are the checks we ran ourselves, what each one does, and how it last came out.
- 19/19 properties on Robinhood Chain itself (
tools/props.mjs, block 64,938,280). The page's planner and builder make the payment; a contract run inside aneth_callwith no deployment sends it through the real Universal Router and pools, funded by a state override, and measures balances itself: every person paid exactly, the router keeps nothing, every share sold arrives in a pool, the change comes back. Four refusals — a payee owed one unit too many, a ceiling one share short, an expired deadline, a missing approval — are each classified by their revert reason and paired with a control that goes through. Six ABI-surface checks require the page's own reader to accept every payment it built and refuse tampered copies. A control proves a wrong storage slot funds nothing. - 30/30 on a fork of this chain (
tools/test-tx.mjs): real Permit2 signatures, real transactions, and every payment read back two ways — from the tokens' own transfer logs and from balances. Two people from two stocks; a replayed signature refused (InvalidNonce); a signature for another spender refused (InvalidSigner); cash spent first; a payment made stale by an $8,000 sale ahead of it, refused and then re-planned; ten people in one transaction; a sale through Uniswap v4 alone; a wallet that no longer holds the shares. - 604 of 800 sales priced exactly, 0 off (
tools/test-sim.mjs): every stock, every pool, four sizes, against QuoterV2 and V4Quoter at one block; 196 were past the tick data read and were reported as unknown, not guessed. Two controls must disagree. - 45/45 offline checks over 200 random draws (
tools/test-plan.mjs): the split's invariants after every draw (the parts add to the total to the unit, no stock past its cap, the mix kept, an honest refusal), every refusal the payee form promises, and the transaction reader fuzzed with twelve kinds of tamper. - 2/2 fuzz seeds clean, 10/10 deliberate breaks caught (
tools/fuzz.mjs): random sequences of operations on a fork, every invariant checked after every step, and each break caught by the invariant named for it. - 27/27 deliberate breaks caught (
tools/sabotage.mjs): the transaction builder broken one line at a time and run through the real chain, the split and the payee form broken and run through the offline checks, and the simulator broken and compared with Uniswap's quoters — each caught by the check named for it; 3 cannot be seen on chain (the router sweeps the difference straight back) and are caught by the page's own reader instead, with the reason written down. The sweep found a real bug: the loop that divides a payment could spin forever once a cap was wrong upstream. It is bounded now. - 23/23 through the real pages in a real browser (
tools/e2e.mjs): a request link made, opened in the app, paid by clicking, and the receipt page reading it back — against a fork, under the deployed Content-Security-Policy.
The case against it
Put fairly: if all you want is dollars for a stock, a brokerage selling at the market usually costs less than a Uniswap pool's fee, and Meed's median cost of 0.27% on $1,000 is mostly that fee. Pools on this chain are also thinner than the stock market, so large payments cost more — 0.36% at $5,000 — and above $25,000 Meed will not try. Keeping your mix costs more than it has to, as well: the landing page's $1,200 example cost −$1.75 spread over four stocks and −$3.58 from AAPL alone, a difference of $1.83. PLUCK, a sibling site built at the same time, measured the same thing first; it is why Meed has a Cheapest mode. What Meed is for is the payment itself: an exact amount, to anyone on chain, in one step, with nothing left sitting in between.
Before this, the first idea for this site was a way to bet against a stock by borrowing it. It died in ten minutes of measurement: every Morpho market on this chain that lends a stock — NVDA, SPY, AAPL, GOOGL and TSLA among them — held zero shares to borrow.
Addresses
Every address the site uses, read back on chain by tools/verify-addresses.mjs.
| Uniswap Universal Router | 0x8876789976dEcBfCbBbe364623C63652db8C0904 |
| Permit2 | 0x000000000022D473030F116dDEE9F6B43aC78BA3 |
| Uniswap v3 factory | 0x1f7d7550B1b028f7571E69A784071F0205FD2EfA |
| Uniswap v4 PoolManager | 0x8366a39CC670B4001A1121B8F6A443A643e40951 |
| Uniswap v4 StateView | 0xF3334192D15450CdD385c8B70e03f9A6bD9E673b |
| QuoterV2 (v3) | 0x33e885eD0Ec9bF04EcfB19341582aADCb4c8A9E7 |
| Quoter (v4) | 0x8Dc178eFB8111BB0973Dd9d722ebeFF267c98F94 |
| Morpho Blue (price feeds) | 0x9D53d5E3bd5E8d4Cbfa6DB1ca238AEA02E651010 |
| USDG — Global Dollar | 0x5fc5360D0400a0Fd4f2af552ADD042D716F1d168 |
| Multicall3 | 0xcA11bde05977b3631167028862bE2a173976CA11 |
Not built
- No ETH as a source. Payments come from stocks and USDG only.
- Payees receive USDG only, not stocks and not ETH.
- No schedule. Meed cannot pay rent every month by itself; there is no contract to hold a standing order.
- No history of your own. Receipts are read from the chain one hash at a time; nothing is stored.
- No stock without a Robinhood price feed on a Morpho market, even if it trades in a pool.
- Cheapest picks one stock. It does not search for the cheapest division across several stocks; when no single stock can raise the payment it falls back to keeping your mix.