Docs
Vetra is a vault that provides liquidity to tokenized equities only while the New York stock market is shut, and holds plain USDG while it is open. This page is what it does, call by call, including the parts that can lose you money.
What it is
A tokenized stock trades every hour of the week. The exchange it tracks is open for 32 and a half of them. Vetra posts a liquidity band into the USDG pool at the closing bell, collects trading fees overnight, and unwinds back to USDG an hour before the next open.
The reason to bother is where an equity liquidity provider actually loses money. It is not spread across the day. It arrives in the minutes after the open, when everything that happened overnight is priced at once and a passive range is the last to know. Vetra is not there for that. It sits in cash for the 32 and a half hours of the regular session and quotes the rest.
One caveat worth putting near the top, because it is the honest limit of the idea. Pre-market trading starts at 04:00 New York and after-hours runs until 20:00, and both are real price discovery. The window in which no venue anywhere is quoting the underlying is about 88 hours a week, not the 130 the vault currently quotes. Roughly 42 of the hours it works have a live print behind them somewhere. Those hours are thin and the fees are real, but the pool is not blind in them, and a tighter schedule would hand them back.
It has never traded a night. There is no track record and neither contract has been audited.
A night, end to end
- 16:00 ET, deploy. The manager calls
deploy. Half the committed USDG is swapped into the stock token, and both sides are minted as a concentrated position around the current price. The call reverts unless the calendar says the market is shut. - Overnight, quote. The position sits in the Uniswap v4 USDG pool taking the 0.30% fee from whoever trades against it. Fees accrue to the position, not to a claimable balance.
- 08:30 ET, unwind. Anyone at all calls
unwind. The band burns, fees come in, and any stock left over is sold back to USDG in the same transaction. The caller keeps 0.05% of the vault for having done it. - 09:30 ET, flat. The bell rings against an empty position. The vault holds USDG only, and stays that way until the next close.
The calendar
0xF1b5c3dE1462eeACfa93D69410C209467DA1c2BF
Given a unix timestamp it returns one of three states: Closed, Unwind, or Open. It derives the New York civil date from the timestamp itself, including US Eastern daylight saving, then checks the weekday, the holiday list and the session bounds. There is no oracle, no keeper and no off-chain feed anywhere in it.
- Regular session
- 09:30 to 16:00 New York
- Early close
- 13:00 on a marked half day
- Unwind window
- the hour before the open
- Daylight saving
- second Sunday in March to first Sunday in November, computed on chain
- Holidays loaded
- 27 closures and early closes, through 2028
The holiday list is the only human input, and the setter only moves one way: a day can be made more closed and never more open. The session bounds themselves are constants that no key can change. So the worst a stolen calendar key can do is mark a trading day as a holiday, which costs fees and risks nothing. Once the calendar is filled far enough ahead, ownership can be renounced and the contract becomes a pure function.
It is free for anyone to call. If you are building anything on this chain that should not act while the equity market is shut, use it.
The vault
0x5De64bDdbbC27322f3Ad7dcBdAf5Ddb19a279F5C
One vault, one pool: the AAPL and USDG pool at the 0.30% tier. Four calls matter.
deposit(assets)- Takes USDG, mints shares. Only while the vault is flat.
redeem(shares)- Burns shares, returns USDG. Only while the vault is flat.
deploy(...)- Manager only. Reverts unless the calendar says Closed.
unwind(minOut)- Anyone. Reverts while the calendar still says Closed.
Deposits and redemptions being open only while flat is the shape of the strategy rather than a restriction. With no position, a redemption is a token transfer instead of an unwind, and price per share is a balance instead of a valuation. It is also why nothing in the contract reads a price oracle: it never needs to mark a position it is holding, because during the hours you can enter and leave, it is not holding one.
The liquidity number passed to deploy is worked out off chain, in the browser. Too
large a number and the settlement runs out of funds and the whole transaction reverts. Too small
and the band is simply smaller than intended. A maxUsdgIn argument caps what the
call may take out of the vault either way, checked against the vault's own balance afterwards.
The unwind bounty
A band left out through the opening bell is the one thing this design exists to prevent, and a
contract cannot wake anybody up. So the unwind is permissionless and paid: from the start of
the unwind window, any address can call unwind, and the contract pays the caller
0.05% of the vault for doing it.
This is not a courtesy. It is the mechanism. It means an unwound band is somebody's payday rather than everybody's problem, and it removes the operator from the critical path entirely.
Why it is not a hook
The obvious way to enforce a schedule on a Uniswap v4 pool is a hook, and it cannot work here. A hook is bound to a pool when the pool is initialised, and the USDG pools these tokens actually trade in were created without one. Nothing can be attached afterwards.
So the rule lives in the contract that owns the liquidity instead. That trades a physical
guarantee for two weaker but real ones: a band cannot be opened into a live session,
because deploy reverts, and a band is unlikely to survive into one,
because unwinding it pays. The upside of doing it this way is that it works on pools that
already exist, with the depth already in them.
What you are trusting
The manager address can do exactly one thing: choose the band and open it, during the hours the calendar permits. It cannot:
- move funds out of the vault
- open a band while the market is open
- stop anyone else from unwinding
- block a deposit or a redemption
- change the session hours, which are constants
The worst version of a malicious or absent manager is a vault that never opens a band and therefore earns nothing. The calendar owner is separate and can only add days off.
How it loses money
- The gap. This is the trade. An earnings print at 16:05 walks straight through the band and the vault unwinds into it the next morning. One of those can undo a month of quiet nights on a concentrated name.
- The round trip. Opening a band swaps half the commitment into stock and unwinding sells it back. Both legs pay the pool fee and move the price. On a quiet night that cost can exceed the fees earned, which is exactly what the fork tests show.
- Extended hours. The vault quotes through pre-market and after-hours, where a real price exists elsewhere and somebody can arbitrage the pool against it. This is the weakest part of the current schedule and the first thing a second version would tighten.
- Thin books. Wide overnight spreads are the reason there is a fee worth collecting and the reason unwinding costs more than it should.
- Unaudited code. Two contracts, written by one person, holding real money on a young chain. This is the risk that does not show up in any model.
- A wrong calendar. The holiday list is set by hand. A missing entry means the vault treats a closed day as a session and stays flat, which costs fees rather than money.
Addresses
- Chain
- Robinhood Chain, 4663
- Shares
- vUSDG, 6 decimals, minted by the vault
- USDG
- 0x5fc5360D0400a0Fd4f2af552ADD042D716F1d168
- Pool
- AAPL / USDG, fee 3000, spacing 60, no hook
- PoolManager
- 0x8366a39CC670B4001A1121B8F6A443A643e40951
Checking it yourself
Ask the calendar what time it thinks it is:
cast call 0xF1b5c3dE1462eeACfa93D69410C209467DA1c2BF \
"stateNow()(uint8)" --rpc-url https://rpc.mainnet.chain.robinhood.com
# 0 closed, 1 unwinding, 2 open
Then watch the vault refuse to open a band while it says open:
cast call 0x5De64bDdbbC27322f3Ad7dcBdAf5Ddb19a279F5C \
"deploy(int24,int24,uint128,uint256,uint256)" 218640 219300 1000000 0 1000000 \
--rpc-url https://rpc.mainnet.chain.robinhood.com
# reverts 0x36bd0147, which is MarketIsOpen()
Nothing on this page needs to be taken on trust. Those two calls are free, read only, and answer the only question that matters about the design.
Call reference
| Call | Who | When | Does |
|---|---|---|---|
| deposit(uint256) | anyone | vault flat | pulls USDG, mints vUSDG at price per share |
| redeem(uint256) | anyone | vault flat | burns vUSDG, returns the USDG behind it |
| deploy(int24,int24,uint128,uint256,uint256) | manager | calendar Closed | swaps half, mints the band |
| unwind(uint256) | anyone | calendar not Closed | burns the band, sells inventory, pays 0.05% |
| totalAssets() | view | any | USDG held by the vault |
| bandLiquidity() | view | any | 0 when flat |
| state(uint256) | view | any | calendar: 0 closed, 1 unwind, 2 open |
| nextChange(uint256) | view | any | calendar: the next boundary, as a timestamp |
Errors you may see
| MarketIsOpen | 0x36bd0147 | the session is live, no band may be opened |
| MarketIsShut | 0x1fed0c6e | too early to unwind, the night is not over |
| BandIsOut | 0x591bcca1 | deposits and redemptions are closed until the unwind |
| NoBand | 0x4a25a7ea | there is nothing out to unwind |
| NotManager | 0xc0fc8a8a | only the manager opens bands |