Conversation
Zanzibar is the next fork after Yap. It activates IIP-59 on-chain voter reward distribution, the BLS proof-of-possession requirement at candidate register/update, the in-contract transfer log topic fix, and the GetCommittedState prestate fix. 46850041 starts an epoch ((h-1) % 360 == 0, testnet epoch is numDelegates 24 * numSubEpochs 15) and sits roughly three days ahead of the chain tip at the time of writing (46750267, ~2.6s/block). epochsPerRewardEra (24) and voterBudgetPerBlock (256) are left at their iotex-core defaults; the genesis loader layers this YAML over them. Requires iotex-core v2.5.0.
- genesis_testnet.yaml: autoDepositContractAddress and delegateProfileContractAddress, the two contracts IIP-59 reads. The AutoDepositRegister was deployed on TestNet for this release from the same runtime bytecode as MainNet's, so the storage layout the direct-slot reader depends on matches; DelegateProfile already existed. - changelog/v2.5.0-release-note.md. - v2.4.4 -> v2.5.0 across READMEs, archive-node.md and the all_in_one scripts. config_mainnet.yaml / config_testnet.yaml are untouched: v2.5.0 adds no new node-config fields, only the Zanzibar height-ordering check, which lives in genesis. MainNet has no zanzibarHeight, so v2.5.0 is a no-op upgrade there.
Zanzibar is scheduled on TestNet only, so v2.5.0 is a TestNet release and MainNet operators should not upgrade yet. Reverts the version bump in the MainNet-facing files -- README.md, README_CN.md, archive-node.md and all_in_one_mainnet.sh -- which also un-does an incorrect rewrite of README.md's transaction-log patch section, where the patch and its release note genuinely belong to v2.4.4. TestNet docs, the all_in_one_testnet.sh docker tags and the changelog stay on v2.5.0. CLAUDE.md and the release note now state the split explicitly.
…ibar IIP-59 only pays out for candidates with VoterRewardOnchainOptIn set. The fork-block migration sets it automatically for candidates whose reward address is one of hermesRewardVaultAddresses, but genesis_testnet.yaml does not override that list, so it inherits the two MainNet Hermes vaults -- and every TestNet delegate sampled uses its own address as its reward address. The migration therefore matches nobody on TestNet and delegates have to send SetVoterRewardOptIn themselves. Paired with the existing note about DelegateProfile commission portions, since either one being unset leaves the distribution path with nothing to pay out.
Fifth change on the fork. Notes that the height moved out of node config into genesis, so blackListRemovalHeight in config.yaml is no longer read.
Sixth change on the fork. Spells out which ABI entry a BLS-less registration uses, since the amount travels in a different field there.
…r fixes The opt-in section claimed the fork-block migration "matches nobody" on TestNet. That was written from a four-delegate sample and is wrong: three of the 35 delegates route rewards through a Hermes vault and will be opted in automatically. Replaced with the full survey -- 3 vault-routed, 2 with commission configured, 1 with both -- which makes the same point without overstating it, plus the ioctl commands to check any delegate. Also records what landed in v2.5.0 after this note was first written: the three fixes carried over from master (#4910 SELFDESTRUCT transaction log, #4921 sender eviction on a mint panic, #4937 block-level gasUsed), the abandoned-drain terminal state, and the new ioctl surface. Notes that the DelegateProfile write must come from the candidate's identifier address, since sending it from the operator address writes a record nothing reads.
Height moves from 46850041 to 46880641, an era's first epoch rather than a point 12 epochs into one. Both are safe -- the constraint that matters is leaving enough room before the settling epoch, and 46850041 had 12 epochs of it -- but starting an era means the first settlement Zanzibar takes part in is one it has been active for from the beginning, with the full 23 epochs of margin. height 46880641 epoch aligned (h-1) % 360 == 0, epoch 130225 era start 130225 % 24 == 1 settles at epoch 130248, 23 epochs later ETA 2026-08-21 ~10:00 CST Note the era's *first* epoch is epochNum % 24 == 1, not == 0: the latter is where IsEraBoundary fires, which is the epoch the era settles *at the end of*. Scheduling activation there is the one case that loses that era's rewards, because the freeze it needs happens ~1.5 epochs earlier, before the fork is live. TestNet docs, the all_in_one_testnet script and the release note's upgrade steps now name v2.5.0-rc0, which is what is tagged and what delegates will run; v2.5.0 proper is tagged after the fork is observed. MainNet docs stay on v2.4.4.
…osit
Both addresses previously named deployments whose owner keys are not
available to this release.
autoDepositContractAddress named a deployment whose `owner()` is the zero
address — its bytecode carries pause()/unpause()/transferOwnership, so
those are permanently uncallable. It also differed from the address
iotex-hub writes compound registrations to, which would have left the
protocol reading one contract while users registered against another:
compound preferences silently ignored, rewards credited to unclaimed
balance instead.
The replacements were deployed by replaying the mainnet creation
transactions, so both runtimes are byte-identical to mainnet's and the
constructors ran normally, leaving the owner set. Verified on chain:
owner is the release key, paused is false, DelegateProfile has the three
reward-portion fields registered against the existing PermyriadVerifier,
and an arbitrary address can publish portions.
The three delegates holding published portions on the old DelegateProfile
were migrated across and their values reconciled value by value.
delegateProfile io19l8qpk08rw0jr4vwguyufva9w4t6aq75q2kt90
0x2fcE00d9E71B9f21D58e4709c4B3A57557ae83D4
autoDeposit io1pvlpc02xft2va4f38ae2nvgqglcxtfytez75c4
0x0b3e1C3d464AD4CED5313f72A9b10047f065A48B
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…he fork Three things were out of sync with genesis_testnet.yaml. The contract addresses. 02b0476 replaced both deployments; the note still named the old ones, including in a yaml block operators might copy. It now carries both the bech32 and 0x forms and explains what changed: replaying the MainNet creation transactions keeps each runtime byte-identical -- which is what AutoDepositRegister needs, since IIP-59 reads its storage slots directly -- while letting the constructors run, so the owner is set. It also records that the AutoDeposit address is the one iotex-hub writes to, which is the property the previous deployment lacked. A stale claim. The note told operators to refresh config_testnet.yaml as well. Only the genesis changed. The READMEs. README_testnet.md and README_CN_testnet.md -- the docs a delegate actually follows -- said nothing about the fork. Bumping the image tag there was not enough: an operator upgrading an existing node has no reason to re-download the genesis, and nothing told them there is a deadline. Both now carry a notice above the body with the height, the date, the requirement to re-download the genesis specifically (it changed twice, so a stale copy is plausible), and the two ioctl steps for IIP-59. Drops the "two things to do before the fork" section: the ioctl section below already covers opt-in and the reward-portion setup, and the survey numbers in it were a snapshot that would go stale before the fork.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Prepares the v2.5.0 release. The headline is the Zanzibar hardfork, scheduled on TestNet at block 46880641 and not scheduled on MainNet.
zanzibarHeightBecause Zanzibar is TestNet-only, this is a TestNet-only release:
README.md,README_CN.md,archive-node.mdandall_in_one_mainnet.shdeliberately remain on v2.4.4, and only the TestNet-facing files are bumped.Zanzibar activates IIP-59 on-chain voter reward distribution, the BLS proof-of-possession requirement at candidate register/update, the in-contract transfer log topic fix, and the
GetCommittedStateprestate fix.Changes
genesis_testnet.yaml—zanzibarHeight: 46880641, plus the two contract addresses IIP-59 reads:autoDepositContractAddress: io1pvlpc02xft2va4f38ae2nvgqglcxtfytez75c4delegateProfileContractAddress: io19l8qpk08rw0jr4vwguyufva9w4t6aq75q2kt90changelog/v2.5.0-release-note.md— new.README_testnet.md,README_CN_testnet.md,scripts/all_in_one_testnet.shandCLAUDE.md.v2.5.0-rc0is what is tagged in iotex-core and what TestNet delegates will run;v2.5.0proper is tagged once the fork has been observed.config_mainnet.yaml/config_testnet.yaml— unchanged. v2.5.0 adds no node-config fields; the only config-package change is the Zanzibar height-ordering check, which validates genesis.On the two contract addresses
Both were deployed on TestNet for this release by replaying the MainNet creation transactions:
io1pvlpc02xft2va4f38ae2nvgqglcxtfytez75c4/0x0b3e1C3d464AD4CED5313f72A9b10047f065A48Bio19l8qpk08rw0jr4vwguyufva9w4t6aq75q2kt90/0x2fcE00d9E71B9f21D58e4709c4B3A57557ae83D4Replaying the creation transaction keeps each runtime byte-identical to MainNet's, which is what matters for
AutoDepositRegister— IIP-59 reads its storage slots directly, bypassing thebucket()view for performance, so the slot constants inaction/protocol/rewarding/autodeposit/slot_reader.goare tied to that exact layout. Unlike installing the runtime bytecode alone, it also runs the constructors, soowner()is set andpause()/unpause()remain usable.The AutoDeposit address is the one iotex-hub writes compound registrations to. An earlier draft named a different deployment, which would have left the protocol reading one contract while users registered against another — preferences silently ignored, rewards credited to unclaimed balance instead.
Verification
Loaded
genesis_testnet.yamlthroughgenesis.New(): it passes validation,zanzibarHeightstarts an epoch ((46880641-1) % 360 == 0) and is an era's first epoch (130225 % 24 == 1), leaving the full 23 epochs before the epoch that era settles at, andepochsPerRewardEra(24) /voterBudgetPerBlock(256) inherit their iotex-core defaults as intended.Open items for the TestNet rollout
Two things must happen before 46880641 or Zanzibar activates with nothing to pay out:
1. Delegates must opt in. IIP-59 distributes only for candidates with
VoterRewardOnchainOptInset. At the fork blockmigrateHermesRewardOptInsets it for any candidate whose reward address is inhermesRewardVaultAddresses— butgenesis_testnet.yamldoes not override that list, so it inherits the two MainNet Hermes vaults (io19604a05...,io12mgttmf..., which are exactly the two vault addresses in the MainNet contract set). Every TestNet delegate sampled viaioctl bc delegateuses its own address as its reward address, so the migration matches nobody. TestNet delegates have to sendSetVoterRewardOptInthemselves.Optionally
genesis_testnet.yamlcould carryhermesRewardVaultAddresses: []to make that explicit — behaviour is identical either way, since an empty list and a list that matches nothing both opt in nobody. Left inherited here; say the word and I will make it explicit.2. Delegates must set commission portions. A delegate without
blockRewardPortion/epochRewardPortionin the DelegateProfile contract is snapshotted at 100% commission, so its voters get nothing. Whether the current TestNet delegate set has these configured is not confirmed here.Neither is a safety issue — rewards keep flowing through the existing off-chain path — but without them IIP-59 is not actually exercised on TestNet.
Requires iotex-core
rc_2.5.0.