What "restake" means when a validator says it
Nimiq has no protocol-level restaking switch. A validator that declares a restake payout is describing its own off-chain policy: when rewards arrive, it sends them back into staker records instead of paying them out as plain transfers. Each distribution is an ordinary add-stake transaction signed by the validator, so the policy is visible in the chain even though the code behind it is not. This article explains the code behind our policy, from the actual script that runs on our server.
The bot is one file, one cron line, and no databases. Everything it knows comes from the chain: the validator wallet's balance, the validator record's total stake, and the list of staker records with their balances. Every minute it asks those three questions and, when there is something to move, moves it.
The pipeline, minute by minute
The script runs on a one-minute cron schedule. A fired cycle does this:
- Take the lock. The first line of business is a file lock, so two cycles can never overlap even if cron double-fires.
- Run the sentinel. The bot reads the validator's recent inbound transactions and looks for anything that is not a coinbase reward and not a staking contract transfer. Such a transfer is presumed to be an accidental send and is held, never distributed. Details in the sentinel section below.
- Read the balance. The validator wallet's balance at the validator address. If it is below 0.02 NIM the cycle stops before spending anything.
- Read the validator total. The validator record's balance: the 100,000 NIM deposit plus every NIM delegated to the pool.
- Compute the available amount. The wallet balance minus anything held by the sentinel. There is no reserve: transaction fees on Nimiq are zero today.
- Split it proportionally. For every staker record, the share is
elected stake x available / validator total, where the elected stake is the smaller of the record's balance at the start of the epoch and its balance now (the epoch-start balances are written to a snapshot file at the first cycle of each epoch, before anything is paid). A share that rounds down to zero lunas is the only one skipped. - Send one add-stake per staker. Each share is sent from the validator wallet to the staker record with a stake transaction, so the NIM lands back in the staker's active balance.
- Log and flag. Every transaction is logged with its hash, and a flag file marks the cycle as done so the monitoring bridge can report it.
The constants, from the code
The thresholds are not buried: they sit at the top of the script as named constants, and this is the whole set:
MIN_TRIGGER = 2000 # 0.02 NIM: below this, a cycle sends nothing RESERVE = 0 # no wallet floor: transaction fees are zero HOLD_SINCE = 61600000 # only inbound transfers after this block count ELECTION_FROM_EPOCH = 1432 # pay on elected stake from this epoch on
Two of those matter to you directly. RESERVE is zero, so every luna the validator earns goes back to stakers; the lunas an integer split rounds away simply ride the next cycle. ELECTION_FROM_EPOCH is the switch to election pricing: from epoch 1,432 on, stake added mid-epoch starts earning at the next epoch, when the protocol counts it, and deactivated stake stops earning immediately.
A real cycle, worked through
The ledger logged a typical cycle at 20:24:02 UTC on 16 September 2026, under the live balance rule then in force; when no stake changes during an epoch, the election rule gives the same numbers. These are the numbers it read, and the shares it sent (stakes are the cycle-time balances to the nearest whole NIM; at this precision the arithmetic reproduces every logged share exactly):
| Staker record | Stake (NIM) | Share that minute (NIM) |
|---|---|---|
NQ12 A0LU ... | 25,000,542 | 4.92833 |
NQ27 NCB1 ... | 3,412,386 | 0.67268 |
NQ30 7D50 ... | 2,000,113 | 0.39428 |
NQ87 4C1K ... | 57,171 | 0.01127 |
The validator total that cycle was 30,574,558.30 NIM and the available amount was 6.02713 NIM. Check the largest row: 25,000,542 x 6.02713 / 30,574,558.30 is 4.92833, which is exactly what the ledger logged. The same arithmetic reproduces every other row, and the same formula runs every minute. Bigger cycles happen whenever a slot pays out; the 19:12 UTC cycle the same evening swept a 181.83 NIM payout in one go, 20.29391 NIM of it to NQ27, the pool's first large staker.
Every share is a real transaction. The 0.67268 NIM to NQ27 NCB1 ... went out as a stake transaction with hash e474484b6365f53064a373a909d47f5aad9085b1302bf69d17e968d92e3b352d, signed by the validator wallet. Paste that hash into the explorer and you can watch the amount land on the staker record.
The 100,000 NIM deposit: its rewards go to stakers too
The validator total includes the deposit, and the split divides by that total, so in any single cycle the deposit's slice is paid to nobody: there is no staker record to send it to. It stays in the validator wallet, and the next cycle splits it across the stakers along with the new rewards. Follow that loop and nothing is left behind. Stakers receive every NIM the validator earns, the deposit's share included, and the operator keeps none of it.
The ledger shows it. In epochs 1,424 to 1,430 (23 to 26 October 2026) the validator mined 40,384.56 NIM and the bot restaked 40,381.60 NIM; the small gap is the last minute's rewards waiting for the next sweep. Had the deposit's slice been kept back, stakers would have received about 0.21% less, roughly 86 NIM over those seven epochs.
So the deposit adds a little on top of your own share: about 0.2% more than your stake alone would earn, with 100,000 NIM beside roughly 46.6 million NIM of stake in October 2026. That is 0.2% of your rewards, not 0.2 percentage points of yield, and it shrinks as the pool grows (it was about 0.33% at 30.6 million NIM in September). The deposit also adds its weight to the pool's election chances, and it is skin in the game: 100,000 NIM that the protocol releases only through a slow, multi-step exit.
The sentinel: what the bot will not touch
Rewards arrive from the coinbase address and delegations move through the staking contract. Anything else landing in the validator wallet is, by definition, not a reward: likely an accidental send to the validator address. The bot treats that class of transfer as held: it records the transaction in a hold ledger, subtracts it from what a cycle may distribute, and raises a flag so a human reads it. It is fail-closed by design. If the chain read fails twice in a row, the cycle is skipped and flagged rather than run on stale guesses. A silent skip is never invisible, and a mis-send is never swept.
Read the source, then check the receipts
The script runs on our server, and the exact code is published on GitHub, under MIT, together with the watchdog that guards the node. You can read the constants, the sentinel, the retry wrapper, even the bug that taught us not to pass large RPC payloads through environment variables. And the honest test still stands: every distribution is an on-chain transaction, every hash is public, and the arithmetic above reproduces from the same two chain numbers the bot reads. The code is open; the receipts stay on the chain.