Skip to main content

Slashing Walkthrough

This page walks through a hypothetical slashing event on Mainnet so you can see how the pieces fit together. It assumes you have already read Slashing for the conceptual model and the object IDs. The commands below are the ones operators actually run, with the live Mainnet object IDs filled in.

The scenario:

  • The candidate is a misbehaving storage node with StakingPool ID 0xCANDIDATE_NODE_ID (substitute the real ID before running anything).
  • Operators have agreed to slash after observing the behavior and reaching consensus through the Walrus operator channels.
  • The current epoch is N. Mainnet epochs are two weeks long, so there is generous time to collect votes.
  • The voting operator's storage node has StakingPool ID $NODE_ID and shard count W.

The Mainnet object IDs you'll reference throughout:

$ WALRUS_PACKAGE=0x98da433aa0139512c210597b1c5e3df6cd121d8d77f8652691bb66fadfc8aa1b
$ SLASHING_MANAGER=0xfe343f7b72f67a1eca9bfc9dff0dac0520e11dca3bda5a0ea32816af6f722109
$ STAKING_OBJECT=0x10b9d30c28448939ce6c4d6c6e0ffce4a7f8a4ada8248bdad09ef8b70e4a3904
$ TREASURY_OBJECT=0x3a19b564728b56df21e0e2bd3582b2be00be992e27b44faf30611e15ae6eac91
$ CANDIDATE_NODE_ID=0xCANDIDATE_NODE_ID # Replace with the real candidate ID.
$ NODE_ID=0xYOUR_NODE_ID # Replace with your own StakingPool ID.

Day 1 of epoch N: first vote creates the proposal

The first operator to act submits the vote PTB from their governance_authorized wallet. The PTB constructs an Authenticated value tied to the sender, then passes it to vote_for_slashing.

$ 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

After the transaction lands, the SlashingManager table has a new entry keyed by the candidate's node ID. The entry's epoch is N, the voting_weight equals the first voter's shard count W, and the voters set contains a single ID.

Confirm the new proposal by reading the SlashingManager object:

$ sui client object $SLASHING_MANAGER --json

The JSON output contains a slashing_candidates table. Look for an entry whose key matches $CANDIDATE_NODE_ID.

Days 1 to 10 of epoch N: collect quorum

Other operators repeat the same vote PTB from their own governance wallets. Each new vote adds the voter's shard count to voting_weight and appends the voter's node ID to the voters set. The contract aborts with EDuplicateVote if an operator tries to vote twice on the same proposal in the same epoch.

Operators can monitor progress by re-running the read command above and observing voting_weight climb toward 667 (the quorum on a 1000-shard committee).

Quorum reached: finalize the slashing

As soon as voting_weight >= 667, the proposal is finalizable. Operators pick one party to submit the execute call so the others do not waste gas on aborted races. The execute call does not take an Authenticated argument, so a plain sui client call works.

$ sui client call \
--package $WALRUS_PACKAGE \
--module slashing \
--function execute_slashing \
--args $SLASHING_MANAGER $STAKING_OBJECT $TREASURY_OBJECT $CANDIDATE_NODE_ID \
--gas-budget 100000000

The transaction does three things atomically:

  1. Removes the proposal from the SlashingManager table.
  2. Extracts the full commission balance from the candidate's StakingPool.
  3. Burns the extracted WAL through the ProtectedTreasury.

Verify the outcome:

$ sui client object $CANDIDATE_NODE_ID --json | grep -A1 commission
$ sui client object $TREASURY_OBJECT --json
$ sui client object $SLASHING_MANAGER --json

You should see the candidate's commission field at zero, the WAL total_supply reduced by the burned amount, and the candidate's entry removed from slashing_candidates.

Cleanup of dangling proposals from prior epochs

If the operators do not reach 667 votes by the end of epoch N, the next vote in epoch N+1 resets the proposal: the first vote in N+1 creates a fresh proposal with voting_weight starting from zero, and all prior voters are discarded. The vote must start over.

If a proposal was abandoned and the operators chose not to restart it in the new epoch, the entry remains in the SlashingManager table until an explicit cleanup. Anyone can clear stale entries:

$ CANDIDATES='["0xCANDIDATE_NODE_ID","0xANOTHER_ID"]'
$ sui client call \
--package $WALRUS_PACKAGE \
--module slashing \
--function cleanup_slashing_proposals \
--args $SLASHING_MANAGER $STAKING_OBJECT "$CANDIDATES" \
--gas-budget 100000000

The function removes only proposals whose stored epoch is strictly less than the current epoch. Active proposals are left untouched, and an entry that does not exist is silently skipped.

Recap

The same three primitives cover the full lifecycle:

ActionFunctionAuthorization
Voteslashing::vote_for_slashinggovernance_authorized on the voter's pool
Executeslashing::execute_slashingPermissionless after quorum
Cleanupslashing::cleanup_slashing_proposalsPermissionless

For abort codes, monitoring, FAQs, and the parallel with the contract upgrade vote, see the main Slashing page.