documentation

the void, explained

The Void is a shared ETH vault that pays yield while a dice roll slowly eats it. You deposit, you earn, you age — and eventually the protocol chooses someone to consume. The rest of the vault gets richer. This page explains every mechanic, every contract, and every flow in plain language.

ethereum · ETH-only UXlido-backed yieldchainlink VRF randomness
01

introduction

The Void is an on-chain survival protocol. You deposit ETH into a shared vault. The vault converts your ETH to a yield-bearing asset (wstETH in production, a mock on public testnet). While your position is inside, three things happen at once:

  • Your position earns staking yield on the underlying asset.
  • Your position earns $VOID emissions that are auto-staked for additional ETH yield.
  • Your position ages into higher tiers that reduce your withdrawal fee — but every cycle, the protocol rolls a weighted die and consumes one active player, removing their entire position.

If you are consumed, your position is burned from the vault and distributed to the surviving depositors. In return you mint a soulbound Void Soul NFT that earns a share of every future withdrawal fee, forever. If you withdraw first, you exit with your yield minus a tier-dependent fee.

one-line summary

Deposit ETH. Earn yield. Age into safer tiers. Be consumed or withdraw. If consumed, collect protocol fees forever as a Void Soul.

02

the premise

Most yield protocols are zero-sum against an external market — they pay you rewards drained from trading flow, LP incentives, or token inflation. The Void is a positive-sum game against time.

Every consumption removes one depositor's shares and redistributes their value across the rest of the vault. The total ETH in the vault does not drop — it just belongs to fewer people. For survivors, every consumption is a pay raise. For the consumed, it is not a wipe: it is the moment your exposure converts from "share of the vault" to "share of every future fee the protocol ever earns."

Tiers make this geometric. The older a position gets, the lower its probability of being selected and the lower its withdrawal fee. The protocol intentionally rewards conviction: the longer you stay, the safer and cheaper you are to leave.

03

player lifecycle

Every position moves through the same five states. You can think of it as a pipeline — each phase has a clear on-chain trigger.

  1. 01enter

    Call depositETH() on VoidVault with any non-zero ETH value. The vault wraps your ETH to yield tokens, mints you non-transferable vault shares (vVOID), and places you at the Initiate rank. Your deposit timestamp is recorded.

  2. 02earn

    Your shares automatically accrue yield from the underlying asset (wstETH rebasing) and $VOID emissions (per-second Synthetix-style distribution). $VOID rewards are auto-staked with a 14-day lock so you earn ETH yield on top of ETH yield.

  3. 03age

    Once enough time has passed since your deposit timestamp, call upgradeTier() to move into the next tier. Each upgrade reduces your consumption weight and your withdrawal fee. Tier arrays are maintained with swap-and-pop for gas-efficient weighted random selection.

  4. 04fork — consumed or exit

    Every epoch, VoidConsumer requests randomness and runs a tier-weighted selection across all active players. If you are selected, your shares are burned and a Soul NFT is minted to you. Otherwise, you may leave at any time viarequestWithdrawETH() — this initiates an async unwind from the yield asset.

  5. 05settle

    For withdrawals, the yield token is routed through CoW Protocol to swap directly into ETH at the best available rate — no Lido unstake queue, no multi-day wait. A keeper advances the request (request → process → settle), then you call claimWithdrawETH(requestId) and receive ETH net of the tier fee. If you were consumed, you call VoidConsumedPool.claim(tokenId) to collect accumulated fees from your Soul NFT.

04

tiers & consumption

There are six ranks — Initiate, Resistant, Defiant, Unyielding, Relentless, and The Hardened. A rank has two effects: a consumption weight that biases random selection, and a withdrawal fee applied only if you exit voluntarily. Both decrease as you age.

rankageweightwithdraw fee
Initiate0 → 3d605.00%
Resistant3d → 7d454.00%
Defiant7d → 14d303.00%
Unyielding14d → 30d202.00%
Relentless30d → 60d121.50%
The Hardened60d+61.50%

Weights form a geometric falloff (60, 45, 30, 20, 12, 6). When randomness is drawn, the victim is chosen by first picking a rank proportional to its total weight × player count, then picking a uniformly random player within that rank. A fresh Initiate is ten times more exposed than one of The Hardened.

Rank boundaries on mainnet are 3d / 7d / 14d / 30d / 60d. On testnet the same boundaries are compressed to minutes so the full lifecycle can be observed end-to-end without waiting months.

05

consumption flow

Consumption is a fully on-chain, asynchronous ritual. It spans four transactions across two contracts, one keeper, and a VRF callback.

  1. atrigger

    When the epoch duration elapses and the player count is at or above the hibernation threshold, Chainlink Automation automatically calls triggerConsumption() on the consumer. It requests one random word from Chainlink VRF and sets pendingConsumption = true. No human trigger and no keeper intervention is required.

  2. bvrf callback

    Chainlink calls fulfillRandomWords(requestId, words). The consumer selects a weighted tier, samples a player, burns their shares inside the vault, and mints a Soul NFT with the consumption record.

  3. credistribution

    100% of the consumed player's value stays in the vault. Shares are burned but the underlying yield token is not, so every remaining share is instantly worth more. Survivors absorb the entire position — no slice is diverted at consumption time.

  4. dregister & claim

    VoidConsumer registers the new NFT in VoidConsumedPool. The NFT's per-token debt is snapshot so the holder earns from this moment forward, with any pre-registration ETH held in heldForFirstNFT distributed to the first Soul when the NFT supply goes from 0 to 1.

timeout safety

If VRF fails to respond within CONSUMPTION_TIMEOUT (1 hour), admins can call resetPendingConsumption() to unstick the state machine and retrigger. No player is ever consumed without a fulfilled VRF response.

06

yield & eth-only ux

The Void is an ETH-only product. At no point does a depositor handle stETH, wstETH, or any LST directly. The yield layer is hidden behind an adapter.

  • On mainnet, WstETHAdapter wraps incoming ETH into wstETH via Lido and exposes an ERC-20 1:1 token to the vault. Every deposit automatically earns Lido staking yield with no user action required.
  • On testnet, a mock yield token simulates the same interface without touching Lido. UX, indexer, and consumption flows are identical — only the yield source changes.
  • Withdrawals do not sit in Lido's unstake queue. Yield tokens are routed through CoW Protocol via CowSwapYieldLiquidator to find the best execution into ETH, so exits settle in minutes rather than days.
  • The vault holds adapter tokens and only ever quotes values in ETH via yieldToken.getETHValue(). Every event payload is denominated in ETH terms at the moment of the action.

Because the yield token rebases, vault shares (vVOID) are non-transferable. Allowing transfers would break the tier arrays that drive consumption. Deposits and withdrawals are the only legal share operations.

07

fees & value flow

Withdrawal fees are the single revenue source that powers the entire ecosystem. Consumption redistribution is zero-fee — the protocol never takes a cut when a player is consumed. Only voluntary exits pay fees, and those fees fan out to four destinations.

destinationsharewhat it funds
VoidBuyback40%buys $VOID from the open market and burns it, tightening circulating supply with every withdrawal.
VoidStaking25%distributed as real ETH yield to $VOID stakers, proportional to their stake.
VoidConsumedPool20%paid to Soul NFT holders — every consumed player earns from every future withdrawal.
Treasury15%protocol runway: audits, infrastructure, randomness subscriptions, buyback routing.

Example: a Defiant-rank player exits with 10 ETH of position. 3.00% (0.30 ETH) is taken as fee, split as 0.12 / 0.075 / 0.06 / 0.045 ETH across buyback / staking / consumed / treasury. The player keeps 9.70 ETH.

Fees are pull-based: destinations receive ETH by calling claimFees(recipient) on the vault, which drains that recipient's pending balance. This removes push-failure risk during the hot path of a withdrawal.

08

epochs & hibernation

Epoch duration is dynamic: the more players are in the vault, the faster epochs tick. VoidConsumer stores up to eight descending-order thresholds of the form (minPlayers, duration). The first threshold whoseminPlayers is met sets the current epoch duration.

If player count drops below hibernationThreshold, the protocol enters hibernation. Consumption is paused, existing positions keep earning yield, and epochs stop advancing. The vault wakes up as soon as player count recovers. Hibernation protects a tiny player set from being trivially wiped.

Chainlink Automation upkeep is registered against VoidConsumer. checkUpkeep() returns true when the epoch has elapsed and the vault is not in hibernation or a pending state; performUpkeep() calls triggerConsumption().

09

$void token

$VOID is an ERC-20 burnable token with a fixed supply of 100,000,000. There is no mint function: total supply is monotonically decreasing over time via buybacks.

supply
100,000,000 VOID
inflation
none (no mint)
decimals
18
sinks
buyback & burn, staking lock

Emissions flow to vault depositors using a Synthetix-style accumulator (voidAccRewardPerShare). Emission rate voidRewardRate is tokens-per-second, set by protocol admins, and bounded by voidRewardEndTimestamp to allow predictable runway. Claimed $VOID is auto-staked on the depositor's behalf with a 14-day lock.

10

staking

VoidStaking converts $VOID holders into ETH earners. Stakers get a proportional share of the 25% fee bucket using an immediate accumulator pattern — ETH is distributed the instant the contract receives it, not dripped over time.

  • Manual stake: call stake(amount). Tokens are freely unstakeable after the cooldown.
  • Auto-stake: the vault calls stakeFor(user, amount, lockDuration) when claiming $VOID emissions. Locked batches unlock at expiry and behave like manual stakes afterwards.
  • Claim: call claimRewards() to pull accrued ETH without touching your stake.
  • Unstake: call requestUnstake(amount), wait the cooldown, then completeUnstake().

Stakes are tracked as batches with optional unlockTime stamps. Unstaking consumes unlocked batches oldest-first using swap-and-pop, keeping the per-user array bounded.

11

soul nfts

VoidSoulNFT is a soulbound ERC-721 minted to every consumed player. Each token stores a permanent record of the event:

  • epoch — the epoch index in which consumption occurred.
  • timestamp — block time of the VRF callback.
  • vaultTVL — total assets in the vault at consumption (ETH terms).
  • sharesLost — the vault shares burned for this consumption.
  • player — the consumed address. Permanently bound to the recipient; transfers between non-zero addresses revert.

Holding a Soul entitles you to 20% of every future withdrawal fee, forever. VoidConsumedPool uses a MasterChef accumulator (accETHPerNFT), so distribution is O(1) regardless of how many NFTs exist. Holders call claim(tokenId) or claimAll() to pull accrued ETH.

12

buyback & burn

40% of withdrawal fees accumulate in VoidBuyback. Once the balance crosses minSwapAmount, anyone can call buybackAndBurn(), which swaps ETH for $VOID through an IBuybackRouter (CoW Protocol in production via CowSwapBuybackRouter) and immediately burns the received tokens.

maxSlippageBps caps acceptable slippage at swap time; the router reverts if the received amount is below the implied minimum. The burn is irrevocable — tokens are destroyed, not locked.

13

contract reference

Each contract below is deployed with identical bytecode across networks — adapters are the only network-specific piece. Source paths are relative to the repository root.

VoidVault
ERC-4626 (entry points disabled), AccessControl, ReentrancyGuard

Core vault. Accepts ETH deposits, wraps to yield tokens, manages shares, tier arrays, withdrawal fees, and consumption.

sourcecontracts/src/core/VoidVault.sol
sepolia0xa688dbD347CaE3b6f2443863363A4781605C1c39
hoodi0xf7A76E9087Fe84A88d7eE61F004dB77DeC848504
VoidConsumer
VRFConsumerBaseV2Plus, AutomationCompatibleInterface

Game engine. Runs epochs, requests Chainlink VRF randomness, selects a victim by weighted tier, and triggers consumption on the vault.

sourcecontracts/src/core/VoidConsumer.sol
sepolia0x0DAc17354bb8f967F0d0b137bc117E10b72f41Cc
hoodi0xc7eA8C042bdbd950B49f2cF3DA24984E33141fF2
VoidSoulNFT
ERC-721 Enumerable (non-transferable), AccessControl

Soulbound ERC-721 minted to every consumed player. Records the epoch, timestamp, vault TVL, and shares lost at the moment of consumption.

sourcecontracts/src/token/VoidSoulNFT.sol
sepolia0xa5B7EA9FcBFf29f41129940560Ca5A00B2514C7c
hoodi0xfFE3DF67dc26AcAC43Ff2d64D58B80A1A04d4734
VoidConsumedPool
AccessControl, accumulator distribution

Distributes ETH fees to Soul NFT holders using a MasterChef-style accumulator. Consumed players earn yield forever.

sourcecontracts/src/core/VoidConsumedPool.sol
sepolia0xe7C9C6Fd2d9a3369D06C2471862D6401Ff3EA3c5
hoodi0xD1ABda1dE76C5375FC28C8c234e0A8E6f9184236
VoidStaking
AccessControl, ReentrancyGuard, batch locks

Stake $VOID to earn real ETH yield from protocol fees. Supports manual stakes and auto-staked vault emissions with a 14-day lock.

sourcecontracts/src/token/VoidStaking.sol
sepolia0x4E8e68C714D97077A72dafD2e3DE42D231aD5149
hoodi0x48C61Fc3B577aCabd32DE1fB5a3703db36b82c17
VoidToken
ERC-20 Burnable, fixed supply

$VOID ERC-20. Fixed supply of 100,000,000. No mint function — supply is only decreased through burns.

sourcecontracts/src/token/VoidToken.sol
sepolia0x924AB60e38F3D8e6D11BADB35e77A103A9685C6b
hoodi0x637D581e7Bf74dB547247369abD0C514b7D34049
VoidBuyback
AccessControl, ReentrancyGuard, pluggable swap router

Receives the buyback share of withdrawal fees, swaps ETH for $VOID via an IBuybackRouter, and burns the tokens.

sourcecontracts/src/buyback/VoidBuyback.sol
sepolia0x97EB0Ae9C5eA667778bc112b7545D9E3ccEdEc26
hoodi0x025b3E6910eA395065C23dB7e40Ceb2ACac9E87D

Adapters: WstETHAdapter, CowSwapBuybackRouter, and CowSwapYieldLiquidator live under contracts/src/adapters/. Swap them per network without touching core logic.

14

networks

current public deployment

The live dashboard connects to Sepolia testnet. Vault at 0xa688…1c39, consumer at 0x0DAc…41Cc, soul NFT at 0xa5B7…4C7c.

15

glossary

vault
The shared ETH pool (VoidVault). Your share of the vault is represented by non-transferable vVOID tokens.
yield token
The ERC-20 the vault holds internally. wstETH (mainnet, Hoodi) or a mock (Sepolia). Abstracted behind IYieldToken.
tier
Your age bracket within the vault. Higher tier = lower consumption weight and lower withdrawal fee.
epoch
A consumption window. When an epoch elapses, the consumer triggers a VRF round and picks a victim.
consumption
The burn of a selected player's shares. Distributes their value to survivors and mints them a Soul NFT.
hibernation
A safety mode when player count falls below the threshold. Epochs and consumption pause; yield and emissions continue.
soul nft
Soulbound ERC-721 minted to consumed players. Entitles the holder to a perpetual share of withdrawal fees.
genesis period
The short window right after deployment where all deposits share the same timestamp. Prevents a first-block advantage.
keeper
An off-chain bot (or Chainlink Automation) that advances the async withdrawal pipeline: process → settle → claim.
16

faq

Can I lose more than I deposited?

No. The maximum loss is your deposited position at the time of consumption, and you simultaneously receive a Soul NFT entitled to a perpetual share of protocol fees. You never owe the protocol anything.

What stops an attacker from spamming deposits?

The Initiate rank carries the highest consumption weight (60) and the highest withdrawal fee (5%). Fresh positions are both most likely to be consumed and most expensive to exit. There is no incentive to churn.

Who controls consumption? Can the team pick a victim?

No human can influence selection. Consumption flows through Chainlink VRF, and the weighted lookup is deterministic in the received random word. Admin roles can only update parameters (tier weights, fee splits, thresholds) — never mint shares, pick a victim, or burn a Soul.

Why are vault shares non-transferable?

Tier arrays are indexed by address. Allowing share transfers would allow a player to dodge consumption by handing off their position mid-epoch. Non-transferability guarantees tier integrity.

What happens to yield earned during the epoch I was consumed?

All yield accrued up to the moment of the VRF callback is redistributed to survivors along with the rest of your position. There is no partial claim.

Is there a deposit cap?

depositCap is a protocol parameter. On testnet it is set to 0 (unlimited). Mainnet may launch with a cap to guard early liquidity.

How do I know when I can upgrade tier?

The dashboard reads depositTimestamp and tier boundaries from the vault and surfaces a countdown. When it hits zero, call upgradeTier() — anyone can pay the gas, but only you benefit.

Do Soul NFTs ever stop earning?

No. As long as the protocol exists and receives withdrawal fees, every registered Soul earns a proportional share. The accumulator does not expire.