Uptick

Connect a wallet

Uptick reads your balance and asks you to sign each transaction. It never holds your keys.

Docs ยท Contracts

Build on Uptick

Contracts

Three contracts run a vault: StockVault holds the position, UptickRouter is the USDG on-ramp, and VaultFactory deploys and registers every vault.

StockVault

An ERC-4626 vault over one Stock Token. Deposits are managed as a single Uniswap v3 position on the Stock Token / USDG pool, recentred at 02:00 UTC after each NYSE trading day within a band of plus or minus rangeBps around the Chainlink price. Shares are a pro-rata claim on idle balances plus both legs of the position. Valuation uses Chainlink for price and the pool TWAP for the position's composition, and every priced path (deposit, mint, withdraw, redeem, rebalance) reverts when feed, TWAP and spot disagree.

FunctionWhat it does
deposit(assets, receiver)Takes an asset amount in the Stock Token and mints shares. Requires a healthy oracle.
mint(shares, receiver)Takes a share amount. Same gate as deposit.
withdraw(assets, receiver, owner)Sends exactly assets of the Stock Token and burns the shares the exit cost.
redeem(shares, receiver, owner)Burns shares and sends the actual proceeds in the Stock Token, at least previewRedeem.
redeemInKind(shares, receiver, owner)Pays the pro-rata share of both tokens. No oracle, no swap, not pausable.
claimStockOwed(to)Pays the caller's credited Stock Token from a failed redeemInKind transfer.
rebalance()Collects fees, closes the position and reopens it around Chainlink. The keeper waits minRebalanceInterval; anyone may call it once 26 hours have passed.
pause() / unpause()Stop deposits and mints. Withdrawals, redeemInKind and rebalance stay open.
health()The oracle gate as the guarded paths see it. Never reverts for a bad feed.
maxDeposit(owner)The remaining room under the deposit cap. Zero when paused.
previewRedeem(shares)A floor on what redeem delivers: fair value less the exit buffer.
setParams, setKeeper, setTreasury, setInstantDeploy, setDepositCapOwner admin for the parameters, keeper, treasury, instant deploy and the cap.
External functions. Views include totalAssets, totalValueInUSDG, pricePerShare, feesEarnedLifetime, positionInfo and currentRange.

UptickRouter

The USDG on-ramp for a StockVault. It swaps USDG into the Stock Token through the same Uniswap v3 router and pool fee the vault uses, then deposits to the receiver. The swap floor is the Chainlink-implied stock amount from health().price less slippageBps, so a deposit reverts when the pool sits far below the oracle. The router holds no balances and leaves no allowances behind after any call.

FunctionWhat it does
depositUsdg(vault, usdgIn, minShares, receiver)Pulls USDG, swaps it to the Stock Token at the oracle value less slippage, deposits, and checks minShares.
redeemInKindMin(vault, shares, minStock, minUsdg, receiver)Exits in kind, checks both legs against the caller's minimums, and forwards them to the receiver.
Both functions are nonReentrant.

VaultFactory

Deploys one StockVault per (Stock Token, pool, Chainlink feed) and keeps the registry the app and the keeper read. The factory pins the keccak256 of StockVault's creation code, so the only contract it can ever register is the audited build. The owner passes the creation code with each call; the factory checks it against the pinned hash, appends the constructor arguments and deploys with CREATE2 at the salt keccak256(stock, pool, feed).

FunctionWhat it does
createVault(creationCode, stock, pool, feed, depositCap)Verifies the code hash, deploys with CREATE2 and registers the vault.
setDefaults(d)Applies new defaults to vaults created from now on. Existing vaults are untouched.
vaultOf(stock, pool)The vault deployed for a pair. Zero when none exists.
isVault(vault)True for every vault the factory deployed.
entries(offset, limit)A registry page of limit entries, fewer at the end.
vaultsOfStock(stock)Every vault on one Stock Token, one per pool.
vaultCount and entryAt round out the registry views.

Chain components in use

Each address returns bytecode from cast code against https://rpc.mainnet.chain.robinhood.com.

ComponentAddress
Uniswap v4 PoolManager0x8366a39CC670B4001A1121B8F6A443A643e40951
Uniswap v3 NonfungiblePositionManager0x73991a25c818bf1f1128deaab1492d45638de0d3
NVDA / USDG 0.05% pool0xd4EB21209C4D6093f80B5b84f5C45cc093EA14a3
NVDA0xd0601CE157Db5bdC3162BbaC2a2C8aF5320D9EEC
SPCX0x4a0E65A3EcceC6dBe60AE065F2e7bb85Fae35eEa
USDG0x5fc5360D0400a0Fd4f2af552ADD042D716F1d168
Robinhood Chain 4663. The components table from the repository README.

The v4 hook

StockVault trades on a Uniswap v3 pool. The separately deployed UptickHook is a dynamic-fee hook for a v4 Stock Token / USDG pool, and it is built on CappedTakeHook. The fee follows the NYSE session: regular hours, off hours, and weekend, with the 20 observed NYSE full-day holidays of 2026 and 2027 compiled in. A swap that pushes the pool further from Chainlink adds min(gapBps * surchargePipsPerBp, maxSurcharge); a swap that moves it toward the reference pays the session fee only.

The cap is a property of the bytecode. CappedTakeHook stores the declared maximum as immutable and registers it in CapRegistry; the constructor rejects a schedule whose worst fee plus surcharge exceeds it with BadSchedule, beforeSwap refuses to quote above the cap with FeeAboveCap, and afterSwap re-checks the realised take with TakeExceedsCap. The nested-swap guard counts an inner swap against the same ceiling.

Session pricing is a measurement, not a preference. Fee per dollar of one-hour markout on the NVDA, SPCX and TSLA pools ran 1.03 to 1.35 in NYSE hours and 3.94 to 33.27 on weekends; the pooled figures are 1.16 and 13.64. The schedule is a deploy parameter, so a new pool can set its own three fees within the same cap.

Test surface

The vault, router, factory and hook suites run against a fork of Robinhood Chain with the real pools, position manager, router and feeds. Only latestRoundData is mocked, to set price and age. Coverage includes fuzz and invariant suites for share accounting, safety tests for the owner's limits, dust exits, minimum-output bounds, the router path, the factory, and a hook suite that walks all 20 holidays and each daylight saving boundary with independently generated timestamps.

Sources

contracts/src/StockVault.solThe ERC-4626 vault, the position, the oracle gate and the exit paths.

contracts/src/UptickRouter.solThe USDG on-ramp and the in-kind exit with minimums.

contracts/src/VaultFactory.solCREATE2 deployment and the vault registry.

contracts/src/hook/README.mdThe session fees, the surcharge, the cap and the hook test coverage.

README.mdThe chain components table and the deployed addresses.