Quantum Shielddocs

The vault

Share accounting, donations, exits and the on-chain price average of QuantumVault.

QuantumVault holds every shielded token and all the state of every account. It has no owner, no upgrade path and no initializer: the token, the market, the EntryPoint and the fee limit are immutable constructor arguments.

Shares

Accounts hold shares. The tokens backing all accounts are totalBacking (B); the shares are totalShares (S).

convertToShares(assets) = assets * (S + VIRTUAL_SHARES) / (B + 1)
convertToAssets(shares) = shares * (B + 1) / (S + VIRTUAL_SHARES)

VIRTUAL_SHARES = 1e6 are shares that belong to nobody. They fix the opening exchange rate, so the first shield cannot be priced out by a donation made in front of it.

FunctionEffect on BEffect on S
shield(account, amount)+ amount+ shares minted to the account
donate(amount)+ amountnone
absorb()+ any token balance above the booksnone
transfer between accountsnonenone (shares move)
exit− tokens paid out− shares burned
network feenone− shares burned

Donations and fees raise B/S: every account's balance in the token goes up at once, with no loop over accounts and no claim step.

Donations

Two functions add tokens for everyone without minting shares:

  • donate(amount) pulls tokens from the caller.
  • absorb() books any tokens that reached the vault by a plain transfer. It is permissionless.

The books and the token balance always satisfy balance ≥ totalBacking + pendingExitTotal, and absorb closes any gap in the holders' favour.

Exits

An operation with exitShares burns those shares and removes their tokens from the backing. What happens next depends on exitTarget:

  • No target: the tokens are transferred to recipient.
  • A target: the vault approves the target for exactly the exit's amount and calls onShieldExit(amount, recipient, data). The target pulls what it uses. Whatever it leaves, read from the remaining allowance, goes to recipient, and the approval is cleared.

Exited tokens stay counted in pendingExitTotal until they have physically left the vault, so nothing a target does in the meantime can make them look like unowned tokens. shield, donate, absorb, transact and exit settlement share one transient reentrancy lock.

Through transact, a failing target reverts the whole call. Through ERC-4337, the books are updated during validation and the exit is escrowed in pendingExits[id], then settled in execute4337; there a failing target is caught and the tokens go to the recipient. If execution itself ran out of gas, anyone can call claimExit(id) to release the tokens to the recipient.

The price average

The vault keeps tokensPerEthEma: an exponential moving average of the market price, in tokens per ETH scaled by 1e18. It exists to price the network fee and to give the harvester a reference that a single block cannot move.

poke() is permissionless and is called by the router and the harvester on every trade:

  • The spot price is read from the live venue: the Pons curve before graduation, the Uniswap v4 pool after.
  • The first update in a block folds the previous update's price into the average: ema = (7·ema + close) / 8.
  • close is clamped to within MAX_PRICE_STEP_BPS (10%) of the average.

A price that lasts a single block enters the average at most once, clamped, and only when a later block updates it. Moving the average takes sustained prices across blocks.

The guardian

One role exists. The guardian can call setEntriesPaused, which makes shield revert, and can hand the role on or renounce it.

It cannot touch anything already inside. Transfers, sales, exits and key changes have no pause, no allowlist and no admin path: nobody can be locked in.

On this page