What Weir is
Weir is a page, not a protocol. It builds standard Uniswap v3 transactions in your browser for the tokenized-stock pools on Robinhood Chain, and your wallet sends them. Weir has no contract, no admin key, no fee and no custody: every position is minted straight to your address as a Uniswap v3 NFT, and the whole transaction builder is one file you can read, js/uni.js.
The transactions
Deposit: one call to SwapRouter02
multicall(deadline, [...]) on Uniswap's SwapRouter02 at 0xCaf681a66D020601342297493863E78C959E5cb2, with these steps in order:
exactInputSingle(USDG → stock, amountIn = the part to swap, recipient = the router itself, amountOutMinimum) pull(USDG, the rest) approveMax(token0) · approveMax(token1) // lets the position manager take the router's balances mint(token0, token1, fee, tickLower, tickUpper, amount0Min, amount1Min, recipient = you) sweepToken(token0, 0, you) · sweepToken(token1, 0, you) // any change comes straight back
How much to swap is solved from the band and the pool fee, so that what is left is in the proportion the band needs at today's price. Before you sign, the app runs this exact transaction against the live pool and sets the floors from that run: the swap must return at least 99.5% of it, and the position must take at least 99% of each amount, or Uniswap refuses the whole thing.
Collect fees
NonfungiblePositionManager.collect(tokenId, recipient = you, max, max)
Withdraw
NonfungiblePositionManager.multicall([ decreaseLiquidity(tokenId, all of it, amount0Min, amount1Min, deadline), collect(tokenId, recipient = you, max, max) ])
Sell the stock
SwapRouter02.multicall(deadline, [exactInputSingle(stock → USDG, amountIn, recipient = you, amountOutMinimum)])
The bands
- Narrow, ±5%: roughly twice the fee rate of the balanced band, and the price leaves it soonest.
- Balanced, ±10%: the band every fee rate on this site is measured on.
- Wide, ±25%: under half the balanced rate, rarely left.
- Full: every price there is. It never needs moving and earns a few percent of the balanced rate on these pools, because the other liquidity in them is concentrated near the price.
Each band is rounded out to the pool's tick spacing. A position earns only while the price is inside its band; out of it, it holds just one of the two tokens and waits.
Addresses
NonfungiblePositionManager ("Uniswap V3 Positions NFT-V1"): 0x73991a25C818Bf1f1128dEAaB1492D45638DE0D3, 859,253 positions minted.
SwapRouter02: 0xCaf681a66D020601342297493863E78C959E5cb2. Its factory() and positionManager() are checked on every build.
Uniswap v3 factory: 0x1f7d7550B1b028f7571E69A784071F0205FD2EfA, which created every stock/USDG pool listed here.
Tests
tools/e2e.mjs builds every transaction with the same js/uni.js the page ships and runs it against live Robinhood Chain with eth_simulateV1: a funded test wallet deposits $10,000 into the real pool, other traders trade through it, and the wallet collects, withdraws and sells. Each check computes what it expects from balances and the pool, never from the code under test. Nothing is broadcast.
| Pool | Checks | Result | What the run did |
|---|---|---|---|
| NVDA | 29 | pass | Deposited $10,000; $9,991.22 went into the position; collected, withdrew and sold at block 71334130. |
| SPCX | 29 | pass | Deposited $10,000; $9,979.68 went into the position; collected, withdrew and sold at block 71334130. |
| CRCL | 29 | pass | Deposited $10,000; $9,913.41 went into the position; collected, withdrew and sold at block 71334130. |
Deliberate bugs
A suite is only worth something if it fails when the code is wrong. Each row is a bug planted in a copy of js/uni.js; the suite must go red on it, on the check named for it. 13/13 caught.
| Bug | What it breaks | Result |
|---|---|---|
no-sweep | leftover stock stays in the router for the next caller | caught |
nft-to-router | the position NFT is minted to the router, not the user | caught |
swap-everything | the whole deposit is swapped and most of it handed back | caught |
swap-wrong-way | the zap swaps in the wrong direction | caught |
band-off-centre | the band is not centred on the price | caught |
withdraw-no-collect | withdraw leaves the tokens and fees owed inside the position manager | caught |
withdraw-fees-only-one | withdraw collects only token0 | caught |
amount0-rounds-up | the JS position maths overstates token0 | caught |
sale-to-router | the sale pays the router instead of the user | caught |
no-deadline | a deposit that sat in a queue for hours still executes | caught |
swap-floor-ignored | the page's swap slippage floor is dropped | caught |
mint-floor-ignored | the page's position slippage floors are dropped | caught |
tick-maths-off | two of TickMath's constants are swapped | caught |
Risks
- Price moves. A position sells the stock as it rises and buys it as it falls; after a large move it is worth less than the two tokens would have been, and the fees may not make up the difference.
- Out of band. Earns nothing until you move it.
- The stock tokens. Robinhood can pause any stock token, block an address or burn from it.
- This page. Uniswap's contracts are long-proven; the code that builds your transactions is new and unaudited. Your wallet shows every call before you sign.