Appearance
Messages
Every execute message, who can call it, and what it does. All messages use snake_case — the CosmWasm wire convention.
User
stake
json
{ "stake": {} }Attach ATOM as funds. Mints vATOM at the current rate and delegates across the validator set.
Refused when: paused, the rewards vault is unset, the amount is below the minimum, the deposit cap would be exceeded, or the amount is too small to mint any vATOM. Also refused while a previous cohort is still winding down; the departing cohort's batches must all finalize first. See errors.
When it is the first stake after a total exit, it additionally disposes of the rewards vault's balance to the community pool in the same transaction, reported as orphaned_rewards_disposed.
redeem
json
{ "redeem": { "receiver": null } }Attach vATOM as funds. Burns it, records the ATOM owed at the current rate, and adds the request to the open batch. receiver may nominate another address that can claim; null means the sender. Either can claim.
The chain undelegation is deferred to process_unbond_batch.
claim
json
{ "claim": { "request_id": 7 } }Pays out a finalized withdrawal. Callable by the owner or the receiver.
Refused when: the batch is not finalized, the withdrawal is not mature, or it is already claimed.
Permissionless
Anyone may call these. See keeping it running.
compound
json
{ "compound": { "start_after": null, "limit": 30 } }Withdraws staking rewards to the vault for a page of validators. Paginated by index; default page 30, maximum 64. Iterates live delegations, not the configured set.
Each withdrawal is isolated, so one un-withdrawable validator cannot abort the call.
restake_rewards
json
{ "restake_rewards": {} }Sweeps the vault and re-delegates it across the set, raising the pool without minting vATOM. The lump is recognised into the rate linearly over 24 hours.
Refused while a vesting window is still running.
sweep_orphaned_rewards
json
{ "sweep_orphaned_rewards": {} }Sends the rewards vault's entire balance to the community pool. Only callable while no vATOM is outstanding, when that balance was earned by holders who have all since exited and belongs to nobody.
Not required to reopen deposits — the first stake after a total exit disposes of the vault balance itself, in the same transaction, so there is no window in which a donation could block it. This is for disposing of that balance when nobody is depositing.
reconcile
json
{ "reconcile": {} }Re-prices the pool against the live delegation, lowering it to reflect slashing. Splits any shortfall proportionally between continuing holders and the open batch. Only ever lowers.
process_unbond_batch
json
{ "process_unbond_batch": {} }Closes the open batch, dispatches one undelegation per validator proportional to current delegation, and opens a fresh batch. This is when the unbonding clock starts.
Refused when the batch is not old enough, is empty, or a validator is already at the 7-entry unbonding limit.
finalize_unbond_batch
json
{ "finalize_unbond_batch": { "batch_id": 3 } }Settles a matured batch by measuring the contract's actual balance. Mandatory before any claim from that batch.
Two calls. The first records an arrival observation (action = observe_arrival, settled = false); a call at any later height settles (settled = true). Necessary because transactions run before the staking module delivers matured ATOM — see stage 3.
Refused when more than one batch has arrived, or the batch's maturity is a forecast — both need the governed path.
Admin
set_rewards_vault
json
{ "set_rewards_vault": { "vault": "cosmos1…", "expected_code_id": 42 } }Binds the vault and points the reward withdraw-address at it. Required before staking. expected_code_id is mandatory and verified on chain, along with the vault being immutable.
set_paused
json
{ "set_paused": { "paused": true } }set_deposit_cap
json
{ "set_deposit_cap": { "cap": "1000000000000" } }null uncaps. In uatom.
update_config
json
{ "update_config": {
"admin": null,
"guardian": null,
"clear_guardian": false,
"min_stake_amount": null,
"min_redeem_amount": null,
"unbond_batch_seconds": null
} }null leaves a field unchanged. The unbonding period is deliberately not updatable.
guardian appoints the guardian; clear_guardian removes it entirely. Two fields rather than one because null has to keep meaning "unchanged" for callers that omit it — and because pointing the guardian at the admin's own address is not retirement: it stops being equivalent the moment the admin role moves, leaving a former admin holding pause and validator-removal authority. Rotate both together, or clear it explicitly.
Sending both is refused, not resolved: a request that names a guardian and also asks to clear one states two intentions, and picking either silently would let a malformed rotation report success while leaving the emergency role empty.
update_validators
Also clears a halt left by a guardian's removal: declaring a set with weights is the review the halt waits for. There is no separate resume message, and no guardian message that lifts it.
json
{ "update_validators": { "validators": [ { "address": "cosmosvaloper1…", "weight_bps": 10000 } ] } }Weights must sum to 10000. Every validator must be bonded on chain.
admin_finalize_unbond_batches
json
{ "admin_finalize_unbond_batches": { "finalizations": [
{ "batch_id": 3, "slashing_effect": "1.0", "accept_shortfall": false }
] } }Must cover every batch whose ATOM has arrived. See stage 3. Subject to the same two-call arrival rule as the permissionless path: being admin does not make the balance evidence arrive any sooner.
admin_finalize_unbond_batch
json
{ "admin_finalize_unbond_batch": { "batch_id": 3, "slashing_effect": "1.0", "accept_shortfall": false } }Convenience form for one batch, with the same requirement — it refuses unless that batch is the only one whose ATOM has arrived.
admin_redelegate
json
{ "admin_redelegate": {
"src_validator": "cosmosvaloper1…",
"dst_validator": "cosmosvaloper1…",
"amount": null
} }Move an existing delegation from one validator to another. null moves everything the chain says is there, slash-adjusted.
src may be a validator that has already been removed from the set — that is the point. dst must be in the set, so stake is never scattered across validators the configured allocation would not have chosen.
This is the protocol's only fast exit from a bad validator: removing one from the set stops future delegation but leaves existing stake in place, whereas a redelegation takes effect immediately on the Hub and does not restart anyone's unbonding clock.
Guardian
A second key, whose entire power is to make the contract do less. Every message below is also callable by the admin — a guardian is a restriction of the admin, not a separate authority.
There is no guardian message to unpause, raise a cap, add a validator, move funds or transfer a role. That absence is the security property: a stolen guardian key has nothing to steal with.
guardian → pause
json
{ "guardian": { "pause": {} } }Freezes staking, redeeming and claiming. One-way — unpausing is set_paused, which is admin-only, so whoever raised the alarm does not also decide it is over.
guardian → lower_deposit_cap
json
{ "guardian": { "lower_deposit_cap": { "cap": "1000000000" } } }Reduces the cap, or introduces one where the pool was uncapped. Refused unless it is strictly a reduction. Existing positions are untouched; a cap only refuses new deposits.
guardian → remove_validators
json
{ "guardian": { "remove_validators": { "validators": ["cosmosvaloper1…"] } } }Drops validators from the set so no further stake is delegated to them, and halts new delegation until an admin calls update_validators.
Surviving weights are re-normalised in proportion to what they held; the caller does not supply new weights. That alone is not enough, though — the survivors absorb the removed share, so reducing the set concentrates it, and reducing it to one validator would point everything at that one. Hence the halt: stake and restake_rewards are refused with new delegation is halted… until an admin declares a set with weights.
redeem and claim are unaffected, deliberately. At least one validator must remain, and naming one that is not in the set is an error rather than a no-op.
Governs future delegations only; use admin_redelegate to move what is already staked.