Built on Robinhood Chain Seaport 1.6 · already on chain

Taper

A descending-price auction for tokenized stock. Offer your shares, start the ask at Robinhood's own price, and let it taper to a floor you choose. Anybody may take it at any moment, at whatever the ask has fallen to — and your shares stay in your wallet until they do.

One Seaport order No contract of ours holds anything Cancel any time

The premise

A pool charges you for depth you did not ask for

A Uniswap pool will sell your shares instantly, and it prices that instant. Walk it for $25,000 and you pay for every tick you cross, whether or not you were in a hurry. Across all 45 tokenized stocks with pools on this chain, quoted at one block against both Uniswap v3 and v4:

What a sale costs, by size

$1,0005 bp
$5,00021 bp
$25,00078 bp
$100,000191 bp

Median across the names that can absorb it, against the same venue's own price for $100. The spread is wide: a quarter of them are under 15 bp at $25,000 and a tenth are over 438 bp.

The honest half that kills this product

For the deep names a pool is nearly free: SPY 2 bp, QQQ 2 bp, NVDA 2 bp, SGOV 3 bp, SPCX 3 bp. If you are selling SPY, an auction is a waste of your afternoon and 2 bp is already a good price.


And the half that does not

For the thin ones it is brutal: NU 3,191 bp, ORCL 641 bp, ASML 575 bp, RIVN 482 bp, MRNA 439 bp. NU costs 31.9% of the position to sell $25,000 of it. 1 name (SOUN) cannot absorb $25,000 at all.

A separate fact, never added in

The pools also simply disagree with Robinhood's feed, and not always downwards. Of 27 names with a feed, 21 trade below it and 6 above.

That disagreement is reported on its own and is never added into a cost. A venue that pays more than an oracle is not a saving; a venue that pays less is not a fee. It matters here for one reason only: it is what decides whether an auction can beat the pool at all.

So: where Taper helps, and where it does not

23 of 45

names where an auction can beat simply dumping into the pool. On the typical one — TSLA — the pool nets you 0.75% under Robinhood's price, which on $25,000 is $188 left on the table.

18 with no feed

have a pool but no Robinhood price feed, so Taper's floor has nothing to hold against and the app refuses them — 13 of them cost over 100 bp to sell, including NU at 31.9%. Taper cannot serve the names that need it most.

4 pool wins

have a feed, and the pool already pays at or above what Robinhood says the shares are worth: AAPL (10 bp above), GME (2 bp below), GOOGL (10 bp below), NVDA (10 bp below). An auction starting at the feed price would be worse for these, and the app will not build one.

How it works

Three numbers and a signature

An auction is one Seaport order. Seaport was already on this chain; we did not deploy it and have no power over it.

The example chart draws here.

Step 01

Say where the ask starts and where it stops

The start is Robinhood's own price by default. The floor's default is not a guess — it is what the pool would actually hand you for that size, measured. Everything the ask stops above that floor is money the pool would have taken.

Step 02

Sign it once; the ask tapers on its own

Seaport interpolates the price linearly between your start and your floor over the window you chose (15 minutes, 30 minutes, 60 minutes, 4.0 hours, 24 hours). Nothing has to be re-signed and no bot has to keep it moving. Your shares never leave your wallet.

Step 03

Anybody takes it, whole or in slices

A buyer can take any 1% of it at whatever the ask has fallen to. Both sides move in one transaction or neither does. You can cancel what is left at any moment.

The one contract

A floor fixed in dollars goes stale

This is the whole reason Taper adds anything at all to Seaport.

The problem

A plain Seaport auction's floor is a number of dollars, fixed when you signed. The stock keeps moving. Across 27 Robinhood feeds the median move between one print and the next is 0.53% — so a half-hour auction on a stock that rallies is an auction whose whole remaining schedule is priced off a market that has gone. An arbitrageur takes your shares at yesterday's number.

What the gate does

Seaport lets an order name a zone, which it calls before and after every fill and which may refuse. Taper's gate reads the amounts Seaport is about to move, asks Robinhood's feed what the shares are worth right now, and refuses any fill more than your chosen discount below that.

The ask still tapers. The floor walks with the market.

What it is

5,450 bytes of Solidity (22.2% of the EVM's limit). No storage, no owner, no upgrade path, no allowlist. It holds nothing and can do exactly one thing: say no.

It has not been audited. It is short on purpose — read it.

What it can't do

Four things this does not fix

Nobody has to bid

An auction that nobody takes sells nothing. When the window closes the order expires and you still hold the shares — the app offers the pool route in one click at that point, which is what you would have done anyway.

It sleeps when the market does

Robinhood's stock feeds stop printing when the market shuts. The gate refuses a fill priced off a dead feed, so auctions do not run overnight or at the weekend. Right now the feeds are 3.4 hours old.

Eighteen names are out of reach

No Robinhood feed exists for them on this chain, so there is nothing for a relative floor to hold against: NU (3,191 bp), RIVN (482 bp), MRNA (439 bp), QUBT (437 bp), NET (434 bp), AVGO (346 bp), NFLX (200 bp), TTWO (176 bp) and more.

Robinhood can still stop the token

Every tokenized stock here carries a pause, a blocklist and an admin burn held by its issuer. Taper changes none of that and could not.

Checked

What was actually run

Every property below executes against the real Seaport, the real tokens and Robinhood's real feeds on Robinhood Chain, as a single eth_call that deploys nothing and spends nothing.

Properties, on chain

29/29

758 assertions, at block 68,309,287. Each one is a single eth_call with no to: the harness's creation code runs against the real Seaport, the real tokens and Robinhood's real feeds, and writes nothing.

Deliberate breaks, and who caught them

45/46

Credit goes to the property named for each break, not to "the suite went red" — a sibling's sweep read 8 of 8 when the truth was 1 of 8, because one wrong check was taking credit for everything. 1 escaped, 1 of those declared with a reason.

Random sequences

6/10

6 seeds of random operations with 10 invariants checked after every one, then replayed against deliberately broken builds. Bugs live in sequences; a property builds one state and asserts one thing.

And what the checks missed

A sweep that has never reported a miss has not shown that it can. So 9 further breaks were written to be genuinely subtle and replayed against the suite as it was before the properties for them existed — 2 of them were invisible to all 23. Those holes are why the last few properties exist. The fuzzer's own misses are named on the docs page, not hidden.