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.
Both land at height 47141281 — epoch 32737's first block, which every other fork height on this chain is also aligned to, and roughly 06:00 CST on 2026-08-29, about six hours after this is written. zanzibarBetaHeight carries the three corrections that landed after v2.5.0-rc0: the epoch-settlement fault handling, the DelegateProfile requirement for the Hermes opt-in migration, and the era freeze log. They correct behaviour Zanzibar already turned on, so they cannot share Zanzibar's height — this chain committed 250k blocks under the pre-correction rules and re-deciding them would change those blocks' receipt roots. Verified locally: a node given zanzibarBetaHeight == zanzibarHeight diverges at the activation block itself. testnetGrants credits 1e9 IOTX to a fresh address so the delegate set can be re-registered. The credit goes through the state manager, so it reaches the Erigon secondary store as well as the trie, and it adds to whatever the account already holds rather than overwriting it. Both heights must stay ahead of the tip until every node is upgraded. Note that a v2.5.0-rc0 node cannot read this file at all — it rejects the unknown zanzibarBetaHeight key and refuses to start — so the binary has to be rolled before this genesis is distributed. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Zanzibar activated on TestNet at 46880641 on 2026-08-21. Zanzibar Beta follows at 47141281 with three corrections to what it turned on, plus the scheduled balance grant, so every version reference moves to v2.5.0-rc1. Both READMEs and the release note now lead with the upgrade ordering requirement: rc0 cannot parse the new genesis at all -- it rejects the zanzibarBetaHeight and account.testnetGrants keys and refuses to start -- so operators must pull the new binary before swapping the genesis in. Verified by loading this genesis under both rc0 (rejects, with the two field errors quoted in the release note) and rc1 (accepts, all four values as scheduled). The release note also records that the Hermes opt-in migration guard is permanently inert on TestNet: it runs only in the block at Zanzibar's height, which TestNet has passed, so a later Beta height cannot restore it. Chains that have not yet activated Zanzibar should set zanzibarBetaHeight equal to zanzibarHeight and get all three corrections. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Zanzibar, Beta and Gamma all land on block 52813081, around 2026-09-28 10:30 CST. This release stops being TestNet-only, so the MainNet docs, scripts and archive guide move from v2.4.4 to v2.5.0 and the release note covers both networks. The banners lead with what makes a MainNet upgrade different: there is no genesis change. MainNet keeps its fork heights and both IIP-59 contract addresses in the binary rather than in genesis_mainnet.yaml, so operators pull the image and restart -- none of the re-download-and-order-it-right care the TestNet rollout needed. Verified by loading the unmodified genesis_mainnet.yaml against the scheduling change: all three heights, both contract addresses, epochsPerRewardEra, voterBudgetPerBlock and the two Hermes vaults come through from the code defaults. Adds a Gamma section, which the note had no entry for at all. Its four corrections are gated together, and letting Gamma trail Zanzibar costs something concrete: EnforceBLSPoP rides Zanzibar and candidate register writes its self-stake bucket before verifying the proof, so a rejected proof would leave that bucket behind until Gamma activates. It also records that TestNet has not scheduled Gamma -- it activated Zanzibar and Beta on builds predating the fork, so those four corrections stay off there until a height is set. The delegate-facing part is the Hermes migration, which runs in exactly the activation block and never again. Against the live candidate set at block 52,004,121: 88 of 123 candidates have a Hermes vault reward address and complete DelegateProfile portions and will be migrated, 9 have the vault but incomplete portions and are left on the Hermes path, and 26 are unaffected. Being left behind breaks nothing but forfeits on-chain distribution with no second chance, so both READMEs and the note tell Hermes delegates to check both portion fields before the fork. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
TestNet activated Zanzibar at 46880641 and Beta at 47141281 on builds that predate Gamma, so its four corrections -- ValidateHeaderGasUsed, CorrectStakeMigrationGas, CheckedBlockGasDeduction and RevertStakingStateOnFailedReceipt -- have been off since IIP-59 went live there. This schedules them. 47468161 starts an epoch: (h - 47141281) % 1440 == 0, exactly 227 epochs past Beta and on the same grid zanzibarHeight and zanzibarBetaHeight sit on. It is ~11,200 blocks ahead of the tip at the time of writing (47,456,979 on 2026-09-07 10:13 CST), which at 2.507 s/block lands around 18:00 CST the same day. Verified by loading this file under v2.5.0-rc2 (30714c91e): it reports 46880641/47141281/47468161 with the grant entry intact. Upgrade the binary before distributing this file. v2.5.0-rc1 rejects it -- "field zanzibarGammaHeight not found in type genesis.Blockchain" -- and refuses to start, the same trap the rc0-to-rc1 rollout had with zanzibarBetaHeight. Roll every node to v2.5.0-rc2 first, where Gamma stays unscheduled and behaviour is unchanged, then replace the genesis. The deferred-Gamma case this puts TestNet in was rehearsed locally at the same commit: a chain with Zanzibar and Beta at 200 and Gamma at 400 crossed 400 and kept producing, five nodes agreeing, zero digest mismatches. Every prior local round had activated all three together, so that path was untested until now. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
# Conflicts: # CLAUDE.md # README.md # README_CN.md # README_CN_testnet.md # README_testnet.md # archive-node.md # scripts/all_in_one_mainnet.sh # scripts/all_in_one_testnet.sh
guo
approved these changes
Sep 16, 2026
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.
Summary
Prepares the v2.5.0 MainNet release and records the completed TestNet rollout.
MainNet
Zanzibar, Beta, and Gamma activate together as one MainNet hardfork:
MainNet has not activated any Zanzibar generation yet, so all three internal feature gates use the same height. Operators and exchanges only need to coordinate one hardfork height.
This is a mandatory binary upgrade to
v2.5.0. MainNet carries the fork heights and both IIP-59 contract addresses in the binary;genesis_mainnet.yamlandconfig_mainnet.yamlare unchanged.Depends on iotex-core#5009 being merged and tagged as
v2.5.0.TestNet
The staged rollout has completed:
TestNet operators must use
v2.5.0-rc2with the currentgenesis_testnet.yaml. Binary-first ordering is required because rc1 cannot parsezanzibarGammaHeight.Changes
genesis_testnet.yaml.Verification
(53155801 - 51918841) % 1440 == 0.