Docs

What the protocol does, what the contracts guarantee, and what is actually deployed. Written to be checkable against the code, not to sell anything.

Status. The factory and six vaults are live on Robinhood Chain mainnet and the app reads them directly. The contracts pass a full test suite but have not been through a third-party audit. Private routing is designed and not yet shipped; vaults came first.

YieldShares

A vault holds one asset. You deposit that asset and the vault mints you a share token. The share token is an ordinary ERC-20: it has a symbol, it shows in your wallet, and it transfers without the protocol's permission.

The share price is:

pricePerShare = (totalAssets + 1) / (totalSupply + 1)

The + 1 on each side is a virtual asset and a virtual share. It exists so that the very first deposit cannot be used to manipulate the price for the second one. Without it, an attacker deposits 1 wei, donates a large amount to the vault, and the next depositor's shares round down to zero.

Yield

Fee income is pushed into the vault by the harvester (in production, the pool hook that collects swap fees). The vault takes the protocol's cut, adds the rest to totalAssets, and mints no new shares. The supply is unchanged and the assets grew, so every share is now worth more.

There is nothing to claim. If you transfer the share, the accrued yield transfers with it, because it lives in the redemption price rather than in a per-user reward balance.

Redemption

Burn shares, receive the underlying at the current price. No lockup, no queue, no epoch. Rounding on withdrawals and mints always goes in the vault's favour, by at most a few wei, so a sequence of deposits and redemptions cannot drain other holders.

What the contract guarantees

PropertyHow it is enforced
A donation cannot move the share pricetotalAssets is a storage variable, not balanceOf(this). Raw transfers into the vault are invisible to pricing.
The first depositor cannot round out the secondVirtual asset and virtual share in every conversion.
The protocol fee has a ceilingMAX_FEE_BPS is a constant checked in the constructor and in setFee. The owner cannot exceed it.
The owner cannot take shareholders' assetsskim can only move the balance above totalAssets. The backing is out of reach.
Only the harvester can add yieldharvest checks msg.sender == harvester and pulls the tokens from the caller.
Fee-on-transfer assets are rejectedDeposits verify the received amount matches the requested amount, and revert otherwise.

What it does not guarantee

Private execution

The intended design: you sign an intent describing the outcome you want rather than a swap call. Solvers compete to fill it and one of them submits the transaction. Your order is never a pending transaction anyone can read and sandwich, and because the solver pays gas, the fee can be settled in the traded asset.

Vaults shipped first. Routing is designed and not yet live, and the app does not offer it.

Contracts

ContractPurposeAddress
VaultFactoryDeploys and registers vaults. The app's entire market list is one read against this.not deployed
YieldSharesOne per asset. ERC-4626 accounting with an ERC-20 share token.six live, listed in the app

Parameters

ParameterValue
Protocol cut of harvested fees
Hard cap on that cut
Pool fee tiers
Chain
Withdrawal delaynone

Running it yourself

Tests:

forge test

Deploy the factory, then create a vault per asset:

forge create contracts/VaultFactory.sol:VaultFactory \
  --rpc-url $RPC --private-key $KEY \
  --constructor-args 1000 $TREASURY

cast send $FACTORY "createVault(address,string,string)" \
  $ASSET "YieldShares NVDA" "ys-NVDA" \
  --rpc-url $RPC --private-key $KEY

vaults.factory in config.js is the only address the front end holds. Everything else is read from the registry at that address.

Front end

Static HTML, CSS and vanilla JavaScript. config.js is the only source of truth for the brand, the chain and the addresses. There is one serverless function, /api/prices, which proxies public stock quotes; a symbol with no quote is omitted rather than filled with a placeholder.