VRAM
Reference

Docs

Everything the vault exposes, and the commands to fund it or read its ledger without this interface. One contract, src/StakeVault.sol, short enough to read in a sitting.

Deployment

Addresses

Read live from the vault you have selected above. Switch camp in the header and these follow.

Chain — · —. Both reward tokens are ERC-8056: the raw balance is frozen and uiMultiplier() carries splits and corporate actions. The vault moves raw units; this interface applies the multiplier so what you read matches your wallet.

Constructor

Parameters, frozen at deployment

All four are immutable. There is no setter, no owner and no upgrade path, so nobody can change them afterwards — us included.

epochDuration() → —

How long one epoch lasts. A deposit waits at most this long before it starts earning.

rampEpochs() → —

How long a position takes to reach full mining power, counted in epochs.

rampStart() → —

What a brand new position is worth on day one, as a fraction of its own size.

maxStake() → —

The largest position a single address may hold, so one holder cannot take most of every deposit.

Interface

Functions that change state

Five, and every one of them is open to anyone. None of them is restricted to an operator, because there isn't one.

writestake(uint256 amount)

Commits tokens. Requires an allowance first. The position becomes active at the next epoch boundary, not immediately, and adding to an existing position restarts its climb. Reverts with StakeCapExceeded past maxStake.

writeunstake(uint256 amount)

Returns exactly what was committed. No penalty, no exit fee, no lockup. A partial withdrawal keeps the progress made on what remains.

writeclaim(address to) → uint256

Sends everything owed to to. Reverts with NothingToClaim when the balance is zero.

writefund(uint256 amount)

Pays fees into the vault. Open to anyone — this is the vault's only inflow, and nothing is ever minted. The amount is credited to the current epoch and becomes distributable once that epoch has fully elapsed.

writesync(uint256 maxSteps)

Walks the accounting forward through elapsed epochs. stake, unstake and claim sync automatically for up to MAX_AUTO_SYNC (500) epochs; past that, call this first or they revert with SyncRequired. Anyone can call it and it costs only gas.

Interface

Functions that only read

viewpendingRewards(address) → uint256

What that address could claim right now, in raw units of the reward token.

viewpowerOf(address) → uint256

That address's mining power: its stake after the ramp has been applied.

viewtotalPower() → uint256

The sum of every position's power. A share is powerOf over this.

viewpositions(address) → (uint128 amount, uint32 startEpoch, …)

The raw position: how much is committed and which epoch it started earning in.

viewcurrentEpoch() → uint256 · syncTarget() → uint256 · lastSyncedEpoch() → uint256

Where the clock is, how far the accounting could be walked, and how far it actually has been. When the last two differ, the vault owes a sync.

viewtotalStaked() · totalFunded() · totalOwed() · undistributed() · carry()

Committed supply, everything ever paid in, everything owed to miners, what is waiting to be distributed, and the amount funded into an epoch that had no committed power — which goes to the first epoch that has some.

Log

Events

The ledger page is nothing more than a reader for FeesFunded. You can read it yourself.

FeesFunded(address indexed from, uint256 indexed epoch, uint256 amount)

Someone paid fees in. Indexed by sender and epoch.

Staked(address indexed account, uint256 amount, uint256 startEpoch)

A position opened or grew. startEpoch is when it begins earning.

Unstaked(address indexed account, uint256 amount)
Claimed(address indexed account, address indexed to, uint256 amount)
Synced(uint256 throughEpoch)

The accounting was walked forward to this epoch.

Reverts

Custom errors

ZeroAddress · ZeroAmount · SameToken · InvalidRamp

Constructor and argument checks.

StakeCapExceeded · InsufficientStake · NothingToClaim · SyncRequired

Over maxStake; withdrawing more than is committed; claiming nothing; and the vault needing a manual sync before it can move.

Without this interface

Do it from a terminal

Nothing here needs the front end. These are the same calls it makes.

Fund a vault

Each vault wants its own asset: the NVDA vault takes NVDA, the AMD vault takes AMD. Approve, then fund.

cast send $REWARD_TOKEN "approve(address,uint256)" $VAULT $AMOUNT \ --rpc-url https://rpc.mainnet.chain.robinhood.com --keystore <path>/deployer cast send $VAULT "fund(uint256)" $AMOUNT \ --rpc-url https://rpc.mainnet.chain.robinhood.com --keystore <path>/deployer

Read every deposit ever made

The ledger page scans this exact log. Running it yourself is the point: you never have to trust the interface.

cast logs --rpc-url https://rpc.mainnet.chain.robinhood.com \ --from-block $DEPLOY_BLOCK \ --address $VAULT \ "FeesFunded(address,uint256,uint256)"

Check a position

cast call $VAULT "pendingRewards(address)(uint256)" $YOU --rpc-url https://rpc.mainnet.chain.robinhood.com cast call $VAULT "powerOf(address)(uint256)" $YOU --rpc-url https://rpc.mainnet.chain.robinhood.com cast call $VAULT "totalPower()(uint256)" --rpc-url https://rpc.mainnet.chain.robinhood.com

Your share of the next deposit is powerOf divided by totalPower. That is the whole distribution rule.