VimenDocs
Protocol

Architecture

One immutable contract per basket, prices kept strictly off-chain.

Vimen's core is intentionally small: one non-upgradeable BasketToken contract per basket (~160 nSLOC of custom logic on OpenZeppelin v5), plus a stateless zap router beside it and a display-only frontend above it.

                     ┌─────────────────────────────┐
   Chainlink feeds ──┤  scripts/computeUnits.ts    │   deploy-time only
   (off-chain read)  │  equal weights → raw units  │
                     └──────────────┬──────────────┘
                                    │ baskets/*.json

                     ┌─────────────────────────────┐
        user ──────► │  BasketToken (per basket)   │ ◄────── guardian (Safe)
   mint / redeem     │  immutable, no oracles      │   pause-mint / cap /
   (in-kind, ERC-20) │  holds constituents 1:1     │   fee recipient ONLY
                     └──────────────┬──────────────┘
                                    │ reads (units, supply, events)

                     ┌─────────────────────────────┐
   Chainlink feeds ──┤  frontend (Next.js, edge)   │   display-time only
   (client read)     │  NAV, drift, badges, geoblk │
                     └─────────────────────────────┘

Design invariants

  1. Full backing. For every constituent i: balanceOf(vault) ≥ ceil(totalSupply × units[i] / 1e18) after any call sequence. Enforced by ceil-on-mint, balance-delta checks and floor-on-redeem; proven by the invariant test suite. Details: Full backing & rounding.
  2. Redeem is ungated. No state (pause, cap, guardian action) can make redeem revert for a solvent holder. The only external failure mode is a constituent token itself reverting transfers (issuer freeze).
  3. Zero oracle dependence on-chain. Prices exist only (a) off-chain at deploy to pick units, (b) in the frontend for display with staleness rules. A manipulated price can't mint underbacked tokens because prices never enter the mint path.
  4. Raw amounts only. The ERC-8056 uiMultiplier is never read on-chain, so corporate actions cannot desync the vault (why).

What is deliberately absent

  • No proxy, no upgradeability, no selfdestruct, no delegatecall. The only external calls are ERC-20 transfers of the constituents.
  • No rebalancing. Rebalancing needs trading authority over vault funds or an oracle in the core; Vimen refuses both. Weights drift like an unrebalanced ETF.
  • No admin over funds. The guardian's complete power is: pause minting, move the supply cap under an immutable ceiling, change the fee recipient. See Guardian & governance.
  • No rescue paths. If a basket were misconfigured, the remedy is pausing its mints and deploying a corrected one, never mutating the old, whose redemption keeps working forever.

Units: computed once, immutable forever

scripts/computeUnits.ts reads live Chainlink prices during US market hours (it refuses to run on stale feeds), converts equal USD weights into raw constituent wei per 1e18 basket wei targeting ≈$100 per basket token, and writes the deploy config. From that moment on, the units are constructor arguments to an immutable contract; nothing on-chain can ever change the recipe.

The chain underneath

Robinhood Chain is an Arbitrum Orbit/Nitro L2: standard EVM, ~100ms blocks with preconfirmations, a single Robinhood-operated sequencer, settlement on Ethereum. Finality is soft on sequencer confirmation and hard on Ethereum. Stock Tokens are ERC-20s with 18 decimals, freely transferable by contracts; "indices & baskets" is an explicitly invited use case in the official docs.

Fake tokens with identical tickers exist on the chain. Vimen's tooling re-verifies every address against the official docs page and on-chain symbol()/decimals() before any deploy. Do the same in your own integrations, and take addresses only from Reference → Addresses.

On this page