How Posy works
Posy is one small contract, PosyPots, and these pages. A pot collects USDG from anyone who wants to chip in; when it closes, the contract buys up to 6 tokenized stocks through Uniswap's SwapRouter02 on Robinhood Chain and the shares go straight to the recipient. If it is cancelled instead, everyone takes back exactly what they put in.
A pot's life
1 · open
open(recipient, deadline, goal, slippageBps, stems[], title). Each stem is a stock, the fee tier of its USDG pool on Uniswap and its share of the pot in basis points; the shares must add up to exactly 100%. The contract refuses a pool that does not exist, a repeated stock, a deadline in the past or more than a year out, and a slippage limit above 15%. The goal is only for the progress bar.
2 · chip in
contribute(id, amount, note) after approving USDG. Allowed until the deadline second. The note (64 bytes at most) is emitted in the Chipped event and shown on the pot's page.
3 · close
close(id, minOut[]). The organiser may close at any time; after the deadline, anyone may. The pot's USDG is split by weight — each stock gets floor(raised × weight / 10,000) and the last takes the remainder — and each part is swapped with exactInputSingle, recipient fixed to the pot's recipient. Each leg must return at least its minimum, and each minimum must clear the price guard below.
4 · or cancel, and refund
cancel(id) by the organiser while the pot is open, or by anyone once 30 days have passed after the deadline without a close. Then refund(id) pays each contributor exactly their total, once.
The contract
Reading Robinhood Chain…
| PosyPots | 0xDc17e535370512Ea8d47CDFF0927B3e67be98788 |
| Init code hash | 0xae8508b90c6e15f8aff88864af98c9e55689005f7a5cee7dd9cae4ef5ce2feb8 |
| Runtime hash | 0x2c17f9046d2cee1bd5b9ee5a13602ff6dc7c1e3972a77d0a4c1984498881c0e4 |
| Size | 10,884 bytes of runtime · source (GPL-2.0-or-later) |
| Compiler | solc 0.8.26, optimizer 200 runs, EVM cancun |
| Deployed through | 0x4e59b44847b379578588920cA78FbF26c0B4956C (the deterministic-deployment proxy), salt 0x74151c72c6899cbaacc80d06476d2eefa15aaf6e3c3251d4291e859358153950 |
| Uniswap SwapRouter02 | 0xCaf681a66D020601342297493863E78C959E5cb2 |
| Uniswap QuoterV2 (quotes) | 0x33e885eD0Ec9bF04EcfB19341582aADCb4c8A9E7 |
| Uniswap v3 factory | 0x1f7d7550B1b028f7571E69A784071F0205FD2EfA |
| USDG (what a pot collects) | 0x5fc5360D0400a0Fd4f2af552ADD042D716F1d168 |
PosyPots has no owner, no fee, no upgrade and no pause. Its only state-changing functions are open, contribute, close, cancel and refund. Every address it calls is a constant in its code, so it has no constructor arguments.
Check it is this code
Nobody holds a key to PosyPots, including us, so it is deployed by its first user: the Start a pot page sends salt ++ initcode to the deterministic-deployment proxy, which creates it at keccak256(0xff ++ proxy ++ salt ++ keccak256(initcode)). Anyone can repeat that arithmetic and compile the source themselves:
solc 0.8.26 · optimizer on, 200 runs · evmVersion cancun keccak256(initcode) = 0xae8508b90c6e15f8aff88864af98c9e55689005f7a5cee7dd9cae4ef5ce2feb8 address = 0xDc17e535370512Ea8d47CDFF0927B3e67be98788 keccak256(eth_getCode(address)) must be 0x2c17f9046d2cee1bd5b9ee5a13602ff6dc7c1e3972a77d0a4c1984498881c0e4
The pages check the last line before they read anything: an address holding different code counts as "not deployed". The build refuses to publish if a fresh compile of the source moves any of the three.
The price guard
Whoever closes a pot chooses the minimum each stock must deliver — the page takes Uniswap's live QuoterV2 quote and asks for at least 99% of it. On its own that would let a closer set minimums of zero and trade against the pot in the same transaction. So the contract also computes each leg's value at the pool's 30-minute time-weighted average price (Uniswap's own observe()) and refuses any minimum below that value less the pot's slippage limit. A price pushed around inside the closing transaction does not move a 30-minute average.
What the limit means in the worst case: someone entitled to close could push the pool against the pot and fill it right at the floor — the 30-minute price less the limit — so the limit is the most a pot can lose to its closer, and the pool's own fee comes out of the same margin. Keep it small: the page suggests 3% and refuses a limit that a pool's fee alone would use up.
Measured at block 71,647,327, the gap between each giftable pool's current price and its 30-minute average had a median of 4 basis points (largest 38). The suggested limit is 3%.
A pool needs 30 minutes of recorded prices for this. 3 pools with a USDG pair keep too few observations today (TSLA, NU, RIVN), so the pages do not offer those stocks. What the guard does not do: it cannot tell whether the pool's price itself is fair compared with the stock market — it protects the pot from the person closing it, not from the market.
Refusals
Every refusal is a named error, and the pages translate it before your wallet opens.
| Error | Means |
|---|---|
BadRecipient | That recipient address cannot receive a pot. |
BadStocks | Each stock can appear once, and a pot holds one to six of them. |
BadWeights | The shares of the pot must add up to 100%, and none can be zero. |
BadFee | That Uniswap fee tier does not exist. |
NoPool | Uniswap has no USDG pool for that stock at that fee. |
BadDeadline | The deadline must be in the future and within a year. |
BadSlippage | Slippage must be between 0.01% and 15%. |
TextTooLong | That text is too long: 64 characters at most. |
NotOpen | This pot is not open any more. |
NotOrganiser | Only the organiser can cancel this pot before its deadline (or anyone, 30 days after it). |
TooEarly | Only the organiser can close this pot before its deadline. Anyone can after it. |
DeadlinePassed | This pot's deadline has passed, so it no longer takes contributions. |
ZeroAmount | Enter an amount. |
TooMuch | That amount is larger than any USDG balance could be. |
Empty | Nobody has chipped in yet, so there is nothing to buy. |
BadMinimums | The close was built for a different pot. Reload and try again. |
MinimumTooLow | That minimum is below what the pool's 30-minute price allows. Reload for a fresh quote. |
TwapUnavailable | One of the pools has less than 30 minutes of price history, so the pot cannot price it safely yet. |
NotCancelled | Refunds open only if the pot is cancelled. |
NothingToRefund | This wallet has nothing to take back from this pot. |
TransferFailed | The USDG transfer failed: check your balance, and approve the pot first. |
Reentrant | Refused a re-entrant call. |
Stocks a pot can buy
39 of the 42 Robinhood stock tokens with a USDG pool on Uniswap, scanned at block 71,647,327. Prices are the pool's, per share.
| Stock | Price | Pool fee | USDG in pool |
|---|---|---|---|
NVDANVIDIA | $224.39 | 0.05% | $3,634,978 |
USOUnited States Oil Fund | $152.91 | 0.3% | $1,197,698 |
CRCLCircle Internet Group | $92.66 | 0.3% | $1,189,490 |
SPCXSpace Exploration Technologies Corp. Class A Common Stock | $147.92 | 0.05% | $1,078,047 |
QQQInvesco QQQ | $740.59 | 0.05% | $580,507 |
MUMicron Technology | $1078.91 | 0.3% | $577,312 |
HIMSHims & Hers Health | $29.27 | 0.3% | $546,475 |
RDDTReddit | $152.04 | 1% | $532,725 |
GOOGLAlphabet Class A | $342.24 | 0.05% | $477,273 |
METAMeta Platforms | $776.37 | 0.3% | $388,114 |
GLDSPDR Gold Trust | $391.57 | 0.05% | $380,075 |
MSFTMicrosoft | $497.36 | 0.3% | $374,998 |
MSTRStrategy Inc. | $161.91 | 1% | $362,450 |
AMCAMC Entertainment | $2.87 | 0.3% | $321,576 |
AMZNAmazon | $249.51 | 0.3% | $303,912 |
GMEGameStop | $24.93 | 0.05% | $300,430 |
COSTCostco | $899.20 | 0.3% | $281,754 |
LLYEli Lilly | $1184.07 | 0.05% | $214,945 |
SPYSPDR S&P 500 ETF Trust | $767.24 | 0.05% | $209,323 |
AAPLApple | $336.07 | 0.05% | $190,143 |
INTCIntel | $127.14 | 0.3% | $183,562 |
SGOViShares 0-3 Month Treasury Bond | $100.57 | 0.05% | $154,544 |
DJTTrump Media & Technology Group | $9.03 | 1% | $153,893 |
AMDAMD | $630.25 | 0.3% | $141,103 |
DELLDell | $541.18 | 1% | $140,550 |
RBLXRoblox | $48.34 | 1% | $116,466 |
SLViShares Silver Trust | $57.71 | 0.3% | $111,649 |
AVGOBroadcom | $350.24 | 0.3% | $108,235 |
QUBTQuantum Computing | $9.14 | 0.3% | $93,182 |
TSMTaiwan Semiconductor Manufacturing | $447.70 | 1% | $89,880 |
PLTRPalantir Technologies | $192.32 | 0.3% | $87,391 |
NETCloudflare, Inc. Class A common stock | $358.96 | 0.3% | $84,094 |
MRNAModerna | $195.12 | 0.3% | $60,498 |
NFLXNetflix | $71.91 | 0.3% | $41,119 |
JNJJohnson & Johnson | $272.26 | 0.3% | $32,961 |
SNDKSandisk Corporation | $1744.16 | 1% | $32,314 |
TTWOTake-Two Interactive Software | $203.28 | 0.3% | $25,298 |
ASMLASML Holding NV | $1718.25 | 1% | $20,822 |
USARUSA Rare Earth | $15.40 | 0.3% | $18,934 |
Tests
Everything below ran against Robinhood Chain's live state — the real USDG, the real stock tokens and the real Uniswap pools — through the node's eth_simulateV1, with PosyPots deployed through the real proxy inside the simulation. Nothing was broadcast.
- Properties: 10/10 pass, 525 checks. Each refusal must be the specific named error; each amount is checked to the unit against numbers computed in the test from what the people in it typed. The re-entrancy lock, which no real sequence can reach (USDG makes no callbacks), is tested with one
eth_callthat installs the runtime by state override and sets the lock's slot, found by name in solc's storage layout. - Fuzz: 8 seeds, 126 operations (79 landed), each held against an independent model after every step (what the contract holds is tracked from USDG's own Transfer logs) and every pot and every person's record read back at the end. Each seed ends with one of four fixed scenarios — several friends chip in and the organiser closes; refused refunds and cancels, then exact refunds; a stranger refused before the deadline and closing after it; a minimum one unit under the floor refused by name, then cancel and refund — and the run fails unless all four ran and every operation landed.
- Sabotage: 31 deliberate bugs planted in the contract and the page's transaction builder; 31/31 were caught by the property named for them. None was missed.
- In a browser: 7/7 journeys, 37 checks — a first user deploys the contract from the page, an organiser opens a pot, two friends chip in from separate wallets, the pot closes and the recipient's balances rise; and a pot is cancelled and refunded.
Risks, plainly
- Not audited. The contract is small and tested, and it holds contributors' USDG until a pot closes or is cancelled.
- The recipient's address is final. A mistyped address receives the shares and nobody can take them back.
- Robinhood controls the stock tokens. They can pause a token or block an address; a pot whose purchase or delivery is blocked cannot close, and can be cancelled and refunded.
- Prices move. The pot holds dollars until it closes; the recipient gets what those dollars buy then.
- A pot nobody closes can be closed by anyone after its deadline, and cancelled by anyone 30 days later.
Older gift links
Before 25 September 2026 Posy sent single gifts as links holding a key. Those links still open at /claim; nothing about them depends on PosyPots.