feat(action,staking,state): commissionRate schema + SetCommissionRate (IIP-59 PR 1/6)#4865
feat(action,staking,state): commissionRate schema + SetCommissionRate (IIP-59 PR 1/6)#4865envestcc wants to merge 1 commit into
Conversation
… (IIP-59 PR 1/6) First PR of the IIP-59 protocol-native voter reward distribution series (spec: iotexproject/iips#73). Introduces the on-chain delegate opt-in surface — a commissionRate field on the candidate schema plus the SetCommissionRate action delegates use to change it. No reward-distribution logic yet: that lands in PR 3, gated on the same feature flag as this PR. Chain behavior is unchanged pre-flag. Schema ------ - stakingpb.Candidate gains commissionRate=11; Go Candidate struct mirrors it. Equal / Clone / toProto / fromProto updated (the PoC at iotexproject#4811 missed Equal — flagged in review #2 there). - state.Candidate gains CommissionRate. Populated per epoch from staking.Candidate by PutPollResult in PR 2; the latest user-set value lives on staking.Candidate, and state.Candidate holds the frozen per-epoch value consumed by GrantEpochReward in PR 3. - iotextypes.Candidate.commissionRate is set/read in candidateToPb / pbToCandidate so the field travels through poll snapshots and RPC. Feature flag ------------ - FeatureCtx.NoVoterRewardDistribution, bound to !g.IsToBeEnabled(height) per AGENTS.md convention for WIP features (the gate will be swapped for a real hardfork height at release). - Named so the bool zero value (false) corresponds to the post-fork activated behavior, matching NoCandidateExitQueue / NotSlashUnproductiveDelegates. Action (SetCommissionRate) -------------------------- - action/set_commission_rate.go: SetCommissionRate{rate uint64} with IntrinsicGas (10000) and SanityCheck (rate in [0, 10000]). - Full Proto / LoadProto / FillAction wiring for the iotex-proto oneof slot (setCommissionRate = 56) shipped in iotex-proto v0.6.7 (iotexproject/iotex-proto#174). - EthCompatibleAction + NewSetCommissionRateFromABIBinary so MetaMask / hardhat can submit via the same path as candidateActivate / candidateDeactivate. - PackCommissionRateSetEvent helper producing keccak-anchored topic-0 + indexed candidate address for the receipt log indexers subscribe to. - action/native_staking_contract_interface.sol + native_staking_contract_abi.json: declare \`function setCommissionRate(uint64 rate)\` and \`event CommissionRateSet(address indexed candidate, uint64 newRate)\` so external tooling has an ABI to bind against. Handler (staking) ----------------- - action/protocol/staking/handler_set_commission_rate.go: resolves the caller to a registered candidate by owner, writes candidate.CommissionRate, emits CommissionRateSet on the receipt. No cooldown enforcement — voters already get a ~1.5 epoch reaction window from the PutPollResult snapshot (§3.4 of the IIP), so a separate per-rate-change gate is not required. - validations.go: rejects SetCommissionRate at Validate when the feature flag is off, so pre-flag the action never leaves mempool. - protocol.go: registers the action in the handler switch. Tests ----- - candidate_test.go / state/candidate_test.go: proto roundtrip + Equal / Clone coverage for the new field. - set_commission_rate_test.go: SanityCheck bounds, IntrinsicGas, ABI encode/decode roundtrip. - handler_set_commission_rate_test.go: owner check, rate cap, successful write path, pre-flag Validate rejection, receipt event encoding. go.mod ------ - Bumps github.com/iotexproject/iotex-proto to v0.6.7 for the new SetCommissionRate action + Candidate.commissionRate fields. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
7b0a25b to
45a343a
Compare
|
Bumped |
|
There was a problem hiding this comment.
Pull request overview
Adds the on-chain schema and action surface needed for IIP-59 delegate commission-rate configuration, wiring it through staking state, protocol validation/handling, and ETH ABI compatibility behind a feature flag.
Changes:
- Extends candidate schemas (staking pb + state/RPC types) with
commissionRateand updates clone/equality/proto conversions. - Introduces
SetCommissionRateaction with gas, sanity bounds, protobuf wiring, and ETH ABI encode/decode + event packing. - Wires
SetCommissionRateinto staking protocolValidate+ handler and adds targeted unit tests.
Reviewed changes
Copilot reviewed 16 out of 18 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| state/candidate.go | Adds CommissionRate to state.Candidate and includes it in equality/clone + proto conversions. |
| state/candidate_test.go | Tests CommissionRate preservation across Equal/Clone and serialize/deserialize. |
| go.mod | Updates github.com/iotexproject/iotex-proto requirement to v0.6.7. |
| go.sum | Updates sums corresponding to the iotex-proto version change. |
| action/set_commission_rate.go | New SetCommissionRate action (sanity check, intrinsic gas, proto wiring, ETH ABI encode/decode, event packing). |
| action/set_commission_rate_test.go | Unit tests for SanityCheck, proto roundtrip, ABI encode/decode, and event packing. |
| action/protocol/staking/validations.go | Adds validateSetCommissionRate to gate the action on NoVoterRewardDistribution and enforce rate bounds. |
| action/protocol/staking/stakingpb/staking.proto | Adds commissionRate = 11 to staking candidate protobuf schema. |
| action/protocol/staking/stakingpb/staking.pb.go | Regenerated staking protobuf bindings with the new field. |
| action/protocol/staking/protocol.go | Wires SetCommissionRate into staking protocol Handle and Validate switches. |
| action/protocol/staking/handler_set_commission_rate.go | New handler to persist the rate for the owner candidate and emit CommissionRateSet receipt event. |
| action/protocol/staking/handler_set_commission_rate_test.go | Handler/Validate tests for pre-flag rejection, owner write persistence, and receipt log emission. |
| action/protocol/staking/candidate.go | Adds CommissionRate to staking candidate model and includes it in clone/equality + proto/RPC conversions. |
| action/protocol/staking/candidate_test.go | Tests CommissionRate behavior through Equal/Clone and proto roundtrip. |
| action/protocol/context.go | Adds FeatureCtx.NoVoterRewardDistribution and binds it to !g.IsToBeEnabled(height). |
| action/native_staking_contract_interface.sol | Extends the native staking interface with setCommissionRate(uint64) and CommissionRateSet event. |
| action/native_staking_contract_abi.json | Updates ABI JSON with setCommissionRate and CommissionRateSet. |
| action/envelope.go | Enables decoding SetCommissionRate from iotextypes.ActionCore protobuf oneof. |
Files not reviewed (1)
- action/protocol/staking/stakingpb/staking.pb.go: Generated file
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| github.com/iotexproject/iotex-address v0.2.9-0.20251203033311-6e8aa4fd43ef | ||
| github.com/iotexproject/iotex-antenna-go/v2 v2.6.4 | ||
| github.com/iotexproject/iotex-election v0.3.8-0.20251015031218-8df952babca1 | ||
| github.com/iotexproject/iotex-proto v0.6.6-0.20260211020747-f26bd969ed16 | ||
| github.com/iotexproject/iotex-proto v0.6.7 | ||
| github.com/ipfs/go-ipfs-api v0.7.0 |
| // validateSetCommissionRate is the pre-mint validation hook for IIP-59's | ||
| // SetCommissionRate action. Feature-flag rejection happens here so a | ||
| // pre-fork node drops the tx at validation time without spending block | ||
| // space, and rate-range rejection happens here so invalid actions never | ||
| // reach the handler. | ||
| func (p *Protocol) validateSetCommissionRate(ctx context.Context, act *action.SetCommissionRate) error { | ||
| if protocol.MustGetFeatureCtx(ctx).NoVoterRewardDistribution { | ||
| return errors.Wrap(action.ErrInvalidAct, "set commission rate is disabled") | ||
| } | ||
| return act.SanityCheck() | ||
| } |
| // setCommissionTestCtx builds a context plumbed with all the protocol | ||
| // contexts handleSetCommissionRate / validateSetCommissionRate read from. | ||
| // `enabled` controls whether the IIP-59 feature flag is active at the | ||
| // fixed block height the tests use (1). |



First PR of the IIP-59 protocol-native voter reward distribution series. Spec: iotexproject/iips#73.
PR series
SetCommissionRateaction + handlerEvery PR in the series is independently reviewable and gated on
!NoVoterRewardDistribution. Pre-flag chain behavior is unchanged in each PR; the fork flip in PR 6 is what actually turns any of this on.Summary
Introduces the on-chain delegate opt-in surface — a
commissionRatefield on the candidate schema and theSetCommissionRateaction delegates use to change it. No reward-distribution logic yet: that arrives in PR 3, gated on the same feature flag as this PR.stakingpb.Candidate.commissionRate = 11(latest user-set value);state.Candidate.CommissionRate(per-epoch frozen, populated in PR 2, consumed in PR 3);iotextypes.Candidate.commissionRatetraverses poll snapshots and RPC.Equal/Clone/toProto/fromProtoall updated (the PoC at feat(iip-59): protocol-native voter reward distribution #4811 missedEqual).FeatureCtx.NoVoterRewardDistributionbound to!g.IsToBeEnabled(height)per the AGENTS.md WIP-feature convention. Named so the zero value (false) corresponds to post-fork behavior, matchingNoCandidateExitQueue/NotSlashUnproductiveDelegates.SetCommissionRate{rate uint64}—IntrinsicGas = 10000,SanityCheckbounds rate to[0, 10000]. Proto/LoadProto wired to iotex-proto oneof slotsetCommissionRate = 56.EthCompatibleAction+NewSetCommissionRateFromABIBinaryso MetaMask/hardhat can submit via the same path ascandidateActivate/candidateDeactivate.candidate.CommissionRate, emitsCommissionRateSet(candidate, newRate)on the receipt. No per-rate-change cooldown — voters already get a ~1.5 epoch reaction window from the PutPollResult snapshot (§3.4 of the amended IIP), so a separate gate is not required.SetCommissionRateat mempool admission, so the action never reaches state execution while the fork is off.External dependency
Depends on the parallel iotex-proto PR: iotexproject/iotex-proto#174 (
feat(action,state): add SetCommissionRate + commissionRate for IIP-59).To keep CI green while that PR is in review,
go.modpinsiotex-prototo envestcc's fork at the PR head via areplacedirective:Before merge: drop the replace once iotex-proto#174 lands and a tagged iotex-proto release ships, then re-pin
require github.com/iotexproject/iotex-prototo that tag.What this PR does not do
distributeToVoters— reward split logic ships in PR 3.ioctl stake2 setcommissioncommand — will ship alongside PR 3 in a follow-up (out of protocol scope).Test plan
go build ./...go test ./action/... ./action/protocol/staking/... ./state/...(998 pass)SetCommissionRatetx is rejected at Validate (ErrUnknownAction)SetCommissionRate(1000)→staking.Candidate.CommissionRate = 1000,CommissionRateSetevent in receiptrate > 10000→ SanityCheck rejectsRelated
🤖 Generated with Claude Code