Skip to main content

Slashing

Walrus protects the network from misbehaving storage nodes through an onchain slashing mechanism. Committee members vote against a candidate node, and when their votes reach a quorum of voting weight, anyone can finalize the slashing to burn the candidate's accumulated commission. The operator pays the penalty, never the delegators staking with that pool.

The mechanism deliberately mirrors the contract upgrade vote. The same governance_authorized entity on the node's staking pool gates both actions, both use a quorum of more than two-thirds of the shards, and both reset their state at the boundary of each epoch. If you have voted on a contract upgrade before, the wallet, signing flow, and threshold semantics are identical.

warning

Slashing is a serious action that permanently destroys WAL by burning the commission balance of the slashed pool. Only vote to slash a node when you have confirmed misbehavior such as repeated unavailability, withholding shards, or other clear protocol violations. Coordinate with other operators in the Walrus operator channels before initiating a vote, and prefer giving the candidate operator a chance to respond first.

How slashing works​

The walrus::slashing module on Sui owns a shared SlashingManager object that holds an onchain table of open proposals. The SlashingManager keys each proposal by the candidate's node ID, which is the object ID of the candidate's StakingPool. A proposal tracks three pieces of state:

  1. The epoch in which a vote created or last refreshed the proposal. The proposal is only valid for execution in this epoch.
  2. The accumulated voting weight, expressed in shards. The current Walrus deployment has 1000 shards, so the quorum threshold is 667.
  3. The set of voter node IDs that have already cast a vote. The same node cannot vote twice for the same proposal.

The workflow has three stages:

  1. Vote. Each voting node calls slashing::vote_for_slashing from the wallet authorized for governance on that node's pool. The first vote creates the proposal; subsequent votes add to its weight. The candidate node itself can technically vote on its own slashing proposal, but in practice no honest operator would.

  2. Execute. Once the accumulated voting weight meets the quorum threshold, anyone (not only voters) can call slashing::execute_slashing to burn the candidate's commission balance. The transaction removes the proposal from the table and atomically extracts and burns the WAL.

  3. Cleanup (optional). Stale proposals from past epochs remain in the SlashingManager table until someone explicitly removes them. Anyone can call slashing::cleanup_slashing_proposals to clear them, which slightly reduces onchain state.

Quorum and voting weights​

A node's voting weight is its current shard count, which the staking pool stores and committee selection refreshes each epoch. Both Mainnet and Testnet use 1000 shards, so a slashing proposal reaches quorum when the votes recorded against it total at least 667 shards. The Move source computes this through the formula 3 * weight >= 2 * n_shards + 1, which is the same threshold the rest of the Walrus protocol uses for finality.

There is no separate supermajority-of-stake requirement beyond the shard quorum. Because Walrus assigns shards to nodes in proportion to their stake (subject to the active-set caps), a quorum of shards is a faithful proxy for a quorum of honest operators.

Epoch-bound proposals​

You must collect a quorum for a proposal and execute it within a single epoch. When the epoch advances, the next vote on an existing proposal replaces it with a fresh one for the new epoch: the voting weight resets to zero and the set of prior voters becomes empty. The execute path checks the proposal's stored epoch against the staking object's current epoch and aborts if they differ.

This design has two important consequences:

  1. Votes you collect near the end of an epoch go to waste if the epoch boundary passes before the proposal reaches quorum. On Mainnet, epochs are two weeks long, so there is generous time. On Testnet, epochs are one day, so coordinate quickly.
  2. You can re-open the same proposal in the next epoch, but the voting starts from scratch. Operators who voted in the previous epoch must vote again.

If you are coordinating a vote, plan to gather a quorum well before the end of the epoch and announce a target execute time so a single party submits the finalization transaction.

What gets burned​

execute_slashing calls into the slashed node's staking pool, extracts the full accumulated commission balance, and burns it through wal::ProtectedTreasury. The total WAL supply visible on the ProtectedTreasury object decreases by exactly that amount. The slashing affects only the operator's commission. Slashing does not touch staked WAL belonging to delegators, the protocol continues to honor withdrawals, and the node remains a member of the committee until the normal lifecycle deactivates it.

Because the penalty is bounded by the accumulated commission, the deterrent grows with how long an operator has gone without collecting commission. Operators who collect frequently have less at risk, but the slashing event is still public and signals operator quality to delegators.

Preparing to vote​

Before you submit a vote, confirm three things:

  1. Your governance_authorized wallet is the one you intend to use. The vote checks the transaction sender (or a controlling object) against the governance_authorized field on your pool. If you have not yet designated a dedicated governance wallet, follow Update the commission receiver and entity authorized for governance first. The hot wallet on the storage node machine should not hold this authorization.

  2. The wallet holds enough SUI for gas. The vote and execute transactions are small, so a few SUI is more than enough.

  3. The Sui CLI uses the correct network. Run sui client switch --env mainnet or sui client switch --env testnet and verify with sui client active-env.

Look up the candidate node ID​

The candidate node ID is the object ID of the candidate's StakingPool. You can find it through any of the following:

  • The Walrus staking app lists every node along with its ID.
  • Other operators in the slashing discussion typically share the ID directly.
  • The walrus info all command lists committee members. The node ID is the same as the StakingPool object ID.

Walrus onchain object IDs​

The following table lists the live shared objects on each network. You can substitute these IDs directly into the commands in Vote on a slashing proposal, Execute the slashing, and Clean up stale proposals. The values are stable across contract upgrades because the SlashingManager, Staking, and ProtectedTreasury are shared objects rather than packages. The system object, staking object, and original Walrus package IDs also appear in the Network Reference.

ObjectID
Walrus package (current)0x98da433aa0139512c210597b1c5e3df6cd121d8d77f8652691bb66fadfc8aa1b
Walrus package (original)0xfdc88f7d7cf30afab2f82e8380d11ee8f70efb90e863d1de8616fae1bb09ea77
Staking object0x10b9d30c28448939ce6c4d6c6e0ffce4a7f8a4ada8248bdad09ef8b70e4a3904
System object0x2134d52768ea07e8c43570ef975eb3e4c27a39fa6396bef985b5abc58d03ddd2
SlashingManager0xfe343f7b72f67a1eca9bfc9dff0dac0520e11dca3bda5a0ea32816af6f722109
WAL ProtectedTreasury0x3a19b564728b56df21e0e2bd3582b2be00be992e27b44faf30611e15ae6eac91

The Walrus package ID changes after every contract upgrade. To get the latest package ID, read the package_id field on the Staking object on Suiscan and use whatever value is there. The original package address is the one used for Move type identifiers and does not change.

Vote on a slashing proposal​

There is no dedicated walrus node-admin subcommand for slashing, so you submit the vote directly through the Sui CLI as a programmable transaction block (PTB). You need a PTB because vote_for_slashing takes an Authenticated argument that the same transaction must construct onchain. You cannot pass an Authenticated value from the command line.

The PTB has two move calls:

  1. walrus::auth::authenticate_sender constructs an Authenticated value tied to the transaction sender.
  2. walrus::slashing::vote_for_slashing consumes that value, checks it against your pool's governance_authorized, and records the vote.

If you delegated your governance authorization to an object rather than an address, replace the first call with walrus::auth::authenticate_with_object and pass the controlling object as its argument.

Before you run the commands, set NODE_ID to the StakingPool object ID of the node you operate and CANDIDATE_NODE_ID to the StakingPool object ID of the candidate. You can get both IDs from the sources in Look up the candidate node ID, such as the walrus info all command.

$ NODE_ID=             # Your storage node ID (the StakingPool you operate).
$ CANDIDATE_NODE_ID= # The StakingPool ID of the candidate to slash.
$ WALRUS_PACKAGE=0x98da433aa0139512c210597b1c5e3df6cd121d8d77f8652691bb66fadfc8aa1b
$ SLASHING_MANAGER=0xfe343f7b72f67a1eca9bfc9dff0dac0520e11dca3bda5a0ea32816af6f722109
$ STAKING_OBJECT=0x10b9d30c28448939ce6c4d6c6e0ffce4a7f8a4ada8248bdad09ef8b70e4a3904
$ sui client ptb \
--move-call $WALRUS_PACKAGE::auth::authenticate_sender \
--assign auth \
--move-call $WALRUS_PACKAGE::slashing::vote_for_slashing \
@$SLASHING_MANAGER @$STAKING_OBJECT auth @$NODE_ID @$CANDIDATE_NODE_ID \
--gas-budget 100000000

If the transaction succeeds, the contract has recorded your vote. The Sui CLI prints the transaction digest, which you should share with other operators so they can confirm the vote landed.

Voting through an authorizing object​

If you set your governance_authorized to an object (for example, a multisig admin cap held in a secure wallet), replace the first move call. The following example assumes a generic object of type T that you own; substitute the concrete type tag for your authorizing object.

$ AUTH_OBJECT=        # The object ID that authorizes governance for your pool.
$ AUTH_OBJECT_TYPE= # The fully qualified Move type of the object.
$ sui client ptb \
--move-call $WALRUS_PACKAGE::auth::authenticate_with_object \
"<$AUTH_OBJECT_TYPE>" @$AUTH_OBJECT \
--assign auth \
--move-call $WALRUS_PACKAGE::slashing::vote_for_slashing \
@$SLASHING_MANAGER @$STAKING_OBJECT auth @$NODE_ID @$CANDIDATE_NODE_ID \
--gas-budget 100000000

Vote abort codes​

The vote transaction aborts cleanly when something is wrong with the request. The abort codes you might see are:

CodeNameMeaning
0ENotAuthorizedThe sender (or authorizing object) does not match the governance_authorized set on your pool. Check the pool on Suiscan.
1EDuplicateVoteYour node already voted on the current proposal. If you intended to revoke and re-cast, wait for the next epoch to refresh.
2ENoProposalForNodeOnly execute_slashing or cleanup_slashing_proposals emits this code. You should not see this from a vote transaction.

Sui prints the abort code along with the module name in the failure output, so you can match the value against this table directly. The error names live in walrus-sui/types/move_errors.rs, which contains constants auto-generated from the Move source.

Monitoring the proposal​

You can inspect the live state of the proposal at any time by reading the SlashingManager object. Either look it up directly on Suiscan using the object ID from Walrus onchain object IDs, or call sui client object from a shell:

$ sui client object 0xfe343f7b72f67a1eca9bfc9dff0dac0520e11dca3bda5a0ea32816af6f722109 --json

The object exposes a slashing_candidates table. Each entry is a SlashingProposal with epoch, node_id, voting_weight, and voters fields. You can confirm:

  • Whether a proposal for the candidate exists in the current epoch
  • How many shards have voted so far, and how far from the 667-shard quorum
  • Which voter node IDs have already cast a vote

If your wallet has already cast a vote but the proposal does not show your node ID in voters, the transaction might have aborted silently in your wallet UI. Re-check the digest on Suiscan to confirm.

Execute the slashing​

Once the accumulated voting weight has reached or exceeded the quorum threshold in the current epoch, anyone can finalize the slashing. The execute call does not take an Authenticated argument, so a plain sui client call works.

Coordinate with the other operators so a single party submits the execute transaction. Multiple operators racing to execute waste gas on the losing transactions, which all abort with ENoProposalForNode after the first one succeeds.

Set CANDIDATE_NODE_ID to the same StakingPool object ID that you used for the vote.

$ CANDIDATE_NODE_ID=  # The StakingPool ID of the candidate being slashed.
$ WALRUS_PACKAGE=0x98da433aa0139512c210597b1c5e3df6cd121d8d77f8652691bb66fadfc8aa1b
$ SLASHING_MANAGER=0xfe343f7b72f67a1eca9bfc9dff0dac0520e11dca3bda5a0ea32816af6f722109
$ STAKING_OBJECT=0x10b9d30c28448939ce6c4d6c6e0ffce4a7f8a4ada8248bdad09ef8b70e4a3904
$ TREASURY_OBJECT=0x3a19b564728b56df21e0e2bd3582b2be00be992e27b44faf30611e15ae6eac91
$ sui client call \
--package $WALRUS_PACKAGE \
--module slashing \
--function execute_slashing \
--args $SLASHING_MANAGER $STAKING_OBJECT $TREASURY_OBJECT $CANDIDATE_NODE_ID \
--gas-budget 100000000

Execute abort codes​

CodeNameMeaning
2ENoProposalForNodeNo open proposal exists for the candidate. Someone might have already executed the proposal, or no one has cast a vote.
3EWrongEpochA vote created the proposal in a past epoch, and no one has cast a fresh vote in the current epoch. Restart the vote.
4ENotEnoughVotesThe accumulated voting weight is below 667. Collect more votes before retrying.

Verify the result​

After the execute transaction lands, confirm three things:

  1. The slashed pool's commission balance is zero. Look up the candidate's StakingPool object on Suiscan and read the commission field, or run sui client object <CANDIDATE_NODE_ID> --json and grep for commission.
  2. The total WAL supply has decreased by the burned amount. Read the ProtectedTreasury object on Suiscan, or call the read-only wal::wal::total_supply function.
  3. The SlashingManager table no longer contains the proposal. The candidate's entry should no longer appear in slashing_candidates.

Sui also emits standard transaction effects for the execute call, so an offchain indexer that monitors the SlashingManager object's transaction history can correlate the digest with the slashing event. There is no dedicated event type for slashing in the current contract.

Clean up stale proposals​

Proposals from previous epochs remain in the SlashingManager table until someone explicitly removes them. Cleanup is permissionless: any wallet can call cleanup_slashing_proposals with a batch of candidate node IDs. The function removes only proposals whose stored epoch is strictly less than the current epoch. The function leaves active proposals in the current epoch untouched. Calling cleanup on a node ID that has no entry is a no-op.

Batching reduces gas. You can include any number of candidate IDs in the same call, subject to Sui's per-transaction gas budget.

$ WALRUS_PACKAGE=0x98da433aa0139512c210597b1c5e3df6cd121d8d77f8652691bb66fadfc8aa1b
$ SLASHING_MANAGER=0xfe343f7b72f67a1eca9bfc9dff0dac0520e11dca3bda5a0ea32816af6f722109
$ STAKING_OBJECT=0x10b9d30c28448939ce6c4d6c6e0ffce4a7f8a4ada8248bdad09ef8b70e4a3904
$ CANDIDATES='["0x...","0x..."]' # JSON array of candidate node IDs to clean up.
$ sui client call \
--package $WALRUS_PACKAGE \
--module slashing \
--function cleanup_slashing_proposals \
--args $SLASHING_MANAGER $STAKING_OBJECT "$CANDIDATES" \
--gas-budget 100000000

For a worked walkthrough that ties the vote, execute, and cleanup commands into a single timeline, see Slashing walkthrough.

Operational practices​

Treat slashing as a coordination problem with onchain enforcement. The contract enforces quorum and authorization, but the social process around it is what keeps the network healthy. The following practices help keep that process predictable:

  • Use a dedicated governance wallet. Do not authorize the hot wallet on your storage node machine for governance. If you have not yet set this up, the commission and governance page walks through it. Contract upgrade votes and slashing votes use the same authorization.
  • Coordinate in advance. Reach out to other operators in the Walrus operator channels before voting. A slashing event is high-impact and operators expect to discuss evidence before voting.
  • Give the candidate a chance to respond. Many apparent misbehavior signals (a node returning stale data, missing shards, refusing reads) have benign explanations like a crashloop or a misconfigured upgrade. A quick message often resolves the issue.
  • Aim for a single execute. Once you and your peers agree quorum is reached, choose one operator to submit the execute transaction so the others do not waste gas on aborted races.
  • Plan around the epoch boundary. On Mainnet, epochs are two weeks long, so timing is rarely tight. On Testnet, epochs are one day, so a slashing vote must move quickly or restart at the boundary.
  • Document the rationale. Slashing is public and permanent. A short post-mortem describing what you observed, what you tried, and what you decided helps the community understand the event and build trust in the process.

Frequently asked questions​

The following answers cover the questions operators ask most often when preparing to vote.

Can a node revoke its own vote?​

No. Once you cast a vote, it remains until the epoch advances and a new vote refreshes the proposal (or replaces it with a new one). If you cast a vote in error, your only recovery is to wait for the next epoch.

What happens if the candidate's commission balance is empty?​

execute_slashing still succeeds. It extracts whatever balance is currently in the commission pool, including zero, and burns it. The function removes the proposal from the table. In this case the onchain WAL supply does not decrease, but the transaction effect still records the slashing event, and the social signal stands.

Does slashing affect the candidate's stake or delegators?​

No. Slashing burns only the operator's accumulated commission. Staked WAL belonging to delegators is unaffected. Delegators can continue to withdraw stake, the node remains a committee member until separately deactivated, and shard assignments do not change as a result of slashing.

Can the candidate vote on its own slashing?​

The contract does not prevent self-voting: the governance_authorized check is the only gate. In practice, an honest operator would not vote on their own slashing, and the candidate's voting weight alone is far below quorum.

Is there a slashing event in the contract?​

The current contract does not emit a dedicated event for slashing. The transaction effects (object changes on the SlashingManager, the StakingPool, and the ProtectedTreasury) are the canonical record. If you need to track slashing programmatically, monitor transactions that touch the SlashingManager shared object and filter by the slashing module's execute_slashing function.

What stops malicious operators from spamming slashing votes?​

The governance_authorized check gates votes, which means a vote can only come from an existing committee member's governance wallet. A malicious minority can create proposals, but they cannot reach quorum without honest operators joining. Stale proposals occupy a small amount of onchain storage, and anyone can clean them up permissionlessly.

Can I see slashing activity in the staking app?​

Not at the time of writing. Plans exist for a staking app surface for slashing once operators exercise the mechanism more broadly. For now, use Suiscan and the sui client object command to inspect the SlashingManager directly.

Reference​

How does a vote handle an invalid candidate node ID?​

vote_for_slashing does not check that the candidate node ID belongs to an existing StakingPool. The function runs the authorization check (ENotAuthorized) and reads voting weight only for your voter node ID. It uses the candidate ID only as the key of the proposal in the SlashingManager table. If you pass a mistyped or nonexistent candidate ID, the vote does not abort. Instead, it creates or adds weight to a proposal for that ID, and the intended node never receives your vote. Verify the candidate node ID against the staking app or walrus info all before you submit.