How Tare works

Everything the page does, in the order it does it, and everything it is built on. If a number on this site is wrong, the way to find out is here.

What Tare does

A wallet knows your balance. Nothing on the chain records your cost — what you gave up to get what you hold. Tare works it out from the only evidence there is: the transactions themselves. In the transaction where shares arrived, something usually left in the same breath — dollars, ether, or other shares — and that is what they cost.

It then values what you still hold at what a sale would actually get for it, tells you what you are up or down on each stock and on the lot, and — if you connect the wallet that holds them — sells the ones you tick in one transaction through Uniswap's own Universal Router.

Tare deploys no contract and asks for no approval of its own. There is nothing of Tare's between you and the pool.

Reading a history

Two log queries per window of 16,000,000 blocks, from block 140,000 to the head: every Transfer of a listed stock, of USDG or of wrapped ether with your address as sender, and the same with your address as recipient. Nothing is asked of any other source, and nothing is inferred from a balance.

Three things about this chain shaped that design, each of which cost a rebuild:

  • Only Robinhood's own node serves log history. The public alternative answers an old query with an empty list rather than an error — which on this site would read as "you never bought anything". History is asked of one node and no other.
  • A timeout means split, but not always. The node refuses a query matching more than 10,000 logs ("exceeds limit") and gives up on one that takes too long ("log query timed out"). The first is a size problem; the second is often just a busy node, so Tare retries once at the same width before halving — halving a busy node's range doubles the requests it is already failing.
  • Receipts do not scale. The first version fetched the receipt of every transaction in which stock moved, to see what had been paid. On a wallet with 254 of them that is 500 requests, and the node throttles after about six in a burst: the read took over ten minutes and looked like a hang. The dollars are now read as logs, in the same queries as the shares.

Only one thing still needs the transaction itself: a purchase paid in native ETH, which leaves no Transfer. Tare asks for those transactions only where shares arrived from a pool or a router and no token left — a handful of requests, not hundreds — and subtracts any ether the router unwrapped and sent back.

Deciding a cost

One rule, applied to every transaction in which shares moved. It is js/ledger.js, it is pure, and it is the file the page, the property run and the fuzzer all load.

  1. What left is valued in dollars: USDG at one dollar, ETH and wrapped ether at the chain's own ETH price in that block, any stock at its own price in that block.
  2. Shares that arrived cost that, split between them in proportion to what each was worth at that block.
  3. If nothing left, the lot is marked received and valued at what the chain says it was worth on arrival.
  4. Shares that left are sold for whatever arrived, split the same way; if nothing arrived they are sent, the lots go at cost, and nothing is realised.
  5. A sale takes the oldest lots first.

Then the ledger checks itself: the lots it holds for a stock must add up to the balance the chain reports at the block it read. When they do not, the row says so and the cost is labelled as what Tare could account for — not as the whole of it.

Where prices come from

Every price on this site is the chain's own. For "what is it worth now", Tare asks Uniswap's quoters what $100 would actually buy in the best pool — a price you could trade at, not a mid nobody can reach. For a past block it reads the Swap events of the same pools and takes the nearest one.

A price is only ever needed where the transaction itself does not answer the question: a stock bought with plain dollars needs no price at all, because the dollars are the cost. That is why an ordinary wallet's read makes almost no price queries, and it is the same rule read backwards — needs() lives beside the rule in the same file so the two cannot drift.

51 stocks are priced, through 221 reference pools, all checked at block 65,570,610. A pool whose fee is above 5% is not treated as a price: this chain carries 50%, 80% and 90% fee pools that quote happily.

Selling

Every sale is one call to Uniswap's Universal Router, which was on this chain before Tare existed. Each leg is quoted on both Uniswap v3 and v4 and routed to whichever pays more. Shares reach the pool through Uniswap's Permit2: an ordinary approval of Permit2 for the exact number of shares, then one signature covering every stock in the sale, good for 30 minutes.

Before the wallet is asked to send anything, the whole transaction is run against the chain from your own address. A refusal is then a sentence on the screen rather than a signature that fails. Each leg carries its own floor (0.50% of slippage by default), so a price that moves against you cancels the sale instead of filling it badly.

At most 12 stocks go in one transaction. That is a measured limit, not a guess: each extra stock costs 114,468 gas (measured on a fork, from the receipt of the page's own sale), so a block could hold thousands of them. The limit is not about gas: it is about how much a person should sign in one go, and how stale the quotes get while they read it.

What it cannot see

  • Anything off this chain. Shares bought elsewhere and bridged or sent in have no price the chain knows. Those lots are received, valued on arrival, and marked on every row they appear in.
  • Gas. The fee you paid to make a trade is not counted as cost.
  • Liquidity positions. Shares put into a Uniswap position leave the wallet and come back as a different amount; Tare books them out at cost and back in as received. The fees you earned do not show up as a gain.
  • Tax rules of any kind. No wash sales, no holding periods, no lot selection other than first in first out, no view about whether these tokens are securities where you live. Tare is a ledger of what the chain shows, and nothing here is advice.
  • Addresses that trade like market makers. Past 40,000 token movements Tare stops and says so rather than read for many minutes in a browser.
  • Stocks with no USDG pool cannot be priced, valued or sold, so they are left out rather than shown at zero.

The census

Before asking anybody to trust it, Tare was run over real wallets: the first 25 addresses of the 775 stock holders on this chain, in an order fixed by keccak256(address) so nobody chose it — the same pool a sibling site built from Blockscout's holder pages, with contracts removed.

Of the 142 lots in those wallets, 10 (7%) were priced from the dollars that left the wallet in the same transaction and 132 had to be valued at the chain's own price when they arrived. Together the wallets hold $1.02m that cost $988,001; 14 of the 17 holders are up and 2 are down.

The whole run is in data/census.json, wallet by wallet, including how long each took and how many requests it cost.

How it was checked

Every figure on this site is written by a tool into data/ and filled into the HTML by the build, which refuses to publish a page containing a number nobody computed. These are the runs behind it:

  • Properties, on the real chain: 19/19 — at block 65,699,369, selling AAPL through v3 and AMC through v4.
  • Sabotage: 12/12 — broken builders, each caught by the property named for it.
  • Fuzz: 2×10 — 11/11 deliberate breaks caught by the invariant named for each.
  • The app, clicked through: 25/25 — a real browser on a fork, under the deployed policy.
  • The live page, real chain: 7/7 — compared with a node read at the page's own block.

Tare has not been audited. It deploys no contract of its own, so there is nothing of Tare's to audit — but the page can still be wrong about your money, and the runs above are the reason to think it is not, in the ways they cover.

Addresses

Everything Tare touches belongs to somebody else and was on this chain before Tare existed. Each was read back on chain before it was written into js/config.js.