Skip to content

feat(staking): add staking contract - #482

Open
hedwig0x wants to merge 31 commits into
develfrom
feat/flu-989-rust-staking
Open

feat(staking): add staking contract#482
hedwig0x wants to merge 31 commits into
develfrom
feat/flu-989-rust-staking

Conversation

@hedwig0x

@hedwig0x hedwig0x commented Jul 29, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • deploy staking at the fixed genesis address 0x0000000000000000000000000000000000520011 as a normal rWasm contract using SharedAPI
  • reserve governance at 0x0000000000000000000000000000000000520012
  • initialize the owner, chain configuration, external dependencies, and genesis validators atomically through one initialize handler
  • custody and pay the canonical BLEND ERC-20 through real contract calls
  • embed the former ChainConfig state and governance surface in staking
  • isolate initializer, chain configuration, staking, and consensus state in separate ERC-7201 namespaces
  • preserve Solidity ABI compatibility with derived selectors, pinned selector hex values, and documented public handlers

Architecture

Staking is a genesis-deployed rWasm smart contract rather than a system precompile because delegation, reward claims, slashing payouts, and genesis bootstrap require BLEND ERC-20 calls. getChainConfig() resolves to the staking contract itself.

Initialization and governance

The one-shot initialize handler configures the complete contract in one transaction:

  • staking owner
  • embedded chain configuration
  • BLS verifier, evidence decoder, liveness-slashing, and BLEND-reserve dependencies
  • genesis validators, commission, and initial stakes

Initialization is permissionless because genesis deployment installs and initializes the contract atomically before public transactions can execute. The initializer flag is stored separately and prevents later reinitialization. Runtime configuration and validator administration remain restricted to the reserved genesis-governance address.

Storage and source layout

State is split across four ERC-7201 roots:

  • Fluent.storage.Initializer
  • Fluent.storage.ChainConfig
  • Fluent.storage.Consensus
  • Fluent.storage.StakingStorage

lib.rs contains grouped selector dispatch for Initializer, ChainConfig, Staking, and Consensus methods. Domain implementation is kept in flat files:

  • initializer.rs: atomic one-shot initialization
  • config.rs: chain configuration, dependencies, getters, and governance setters
  • staking.rs: validator lifecycle, selection, delegation, snapshots, and rewards
  • consensus.rs: consensus keys, committees, liveness, jail handling, and equivocation
  • storage.rs: namespaced storage layouts
  • util.rs: shared ABI, validation, revert, epoch, and ERC-20 helpers

All 90 public handlers document their selector hex, ABI method name, and behavior.

Implemented parity

  • initialization, registry reads, validator registration, lifecycle, and epoch snapshots
  • BLEND delegation, undelegation, delayed principal claims, redelegation, validator/delegator rewards, and bounded claims
  • complete embedded ChainConfig constants, getters, governance setters, activation locks, caps, and undelegation safety floor
  • consensus-key proof-of-possession validation and peer-key uniqueness
  • historical keyed registries, canonical top-k selection, committee persistence/pruning, DKG mint bits, and signer resolution
  • liveness slashing, halt guard, jail release, bounded automatic readmission, and extended jail deadlines
  • notarize/finalize/nullify-finalize equivocation verification, beneficiary-owned commit/reveal protection, permanent tombstones, and self-stake seizure
  • Solidity-compatible dynamic calldata, return tuples, custom errors, selectors, and event data
  • fail-closed external dependency calls and ERC-20 handling for tokens returning true or no data

Validator identity is permanent. Governance may disable and reactivate validators, but disabling does not delete validator records, consensus keys, ownership mappings, or stake history. changeValidatorOwner remains in the compatibility ABI but always reverts because the owner is the immutable administrative, reward, self-stake, and slashing identity.

Supporting changes

  • wire the staking contract into genesis and expose it as the chain-configuration contract
  • improve Solidity bytes decoding bounds and dynamic event-data encoding
  • add SDK storage primitives and testing-host support required by the contract
  • update universal-token and full-genesis fixtures for real staking custody and payout flows

Intentional differences from Solidity

The rWasm contract omits UUPS/Ownable proxy-management scaffolding and the unused legacy getSystemReward() / getStakingPool() dependency getters. These are not staking behavior in the non-proxy genesis deployment.

Validation

  • cargo test --manifest-path contracts/Cargo.toml -p fluentbase-contracts-staking --lib: 48 passed
  • cargo test -p fluentbase-e2e staking:: -- --nocapture: 2 passed
  • staking clippy with -D warnings: passed
  • staking formatting check: passed
  • CI-style --no-default-features --features std check: passed

Linear: FLU-989

Summary by CodeRabbit

  • New Features
    • Added/extended staking contract capabilities: validator registration & delegation lifecycle, epoch committee commitment, liveness jail management, and equivocation slashing with permissionless commitment/reveal.
    • Added governance staking configuration, consensus-key management, new read endpoints, and snapshot-based fee/reward claiming with stipend settlement.
    • Wired the staking contract into genesis.
  • Documentation
    • Expanded staking and equivocation reporting documentation, including commitment/reward rules and ABI/event parity notes.
  • Bug Fixes
    • Improved Solidity bytes decoding bounds checks and made event ABI encoding failures return an error instead of aborting.
  • Tests
    • Expanded selector/ABI conformance, equivocation commitment/reveal coverage, and end-to-end staking flow tests.

@coderabbitai

coderabbitai Bot commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Changes

The PR adds a genesis staking rWasm contract with ABI dispatch, namespaced storage, validator lifecycle management, delegation and rewards, consensus committees, liveness and equivocation slashing, governance configuration, genesis integration, unit tests, and end-to-end staking coverage.

Genesis staking contract

Layer / File(s) Summary
Storage, ABI, and shared staking foundations
contracts/staking/src/{consts,types,storage,math,events,util}.rs, crates/{codec,sdk,sdk-derive,types}/*
Defines staking storage layouts, ABI types and selectors, compact arithmetic, events, validator selection, ERC-20 helpers, storage aliases, codec validation, and supporting SDK exports.
Contract bootstrap, dispatch, and governance configuration
contracts/staking/src/{lib,handlers,config}.rs, contracts/staking/src/tests.rs
Adds contract dispatch, initialization, chain configuration, governance parameter updates, validator lifecycle handlers, and ABI/selector validation.
Delegation, undelegation, and rewards
contracts/staking/src/{economics,rewards}.rs, e2e/src/staking.rs
Implements validator registration, delegation queues, undelegation maturity, snapshot-based claims, redelegation, stipend settlement, and end-to-end token custody validation.
Consensus committees and validator penalties
contracts/staking/src/{consensus,liveness,equivocation}.rs, contracts/staking/src/tests.rs
Adds consensus-key registration, committee commitment and pruning, liveness jail management, beneficiary-owned equivocation report commitments, and equivocation tombstoning and stake seizure.
Genesis integration
contracts/README.md, contracts/staking/README.md, crates/genesis/build.rs, crates/types/src/genesis.rs, e2e/src/lib.rs
Documents the staking crate and commit/reveal reporting flow, registers staking genesis addresses, includes the staking contract in genesis builds, and wires its e2e test module.

Estimated code review effort: 5 (Critical) | ~120 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Beneficiary
  participant StakingContract
  participant BLSVerifier
  Beneficiary->>StakingContract: commitEquivocationReport(commitment)
  StakingContract-->>Beneficiary: store commitment and block number
  Beneficiary->>StakingContract: slashEquivocation*(evidence, beneficiary, salt)
  StakingContract->>StakingContract: verify commitment maturity and match
  StakingContract->>BLSVerifier: verify evidence signatures
  StakingContract-->>Beneficiary: pay committed reporter reward
Loading

Possibly related PRs

Suggested reviewers: d1r1

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 46.04% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely identifies the main change: adding the staking contract.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/flu-989-rust-staking

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions

github-actions Bot commented Jul 29, 2026

Copy link
Copy Markdown

Criterion results (vs baseline)


running 120 tests


Heads-up: runner perf is noisy; treat deltas as a smoke check.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🧹 Nitpick comments (6)
contracts/staking/src/rewards.rs (2)

683-692: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Collapse the two consecutive if !assigned.is_zero() blocks.

Both branches test the same condition; merging them (with the else { StipendSkipped } attached once) removes a redundant check and keeps disbursement and crediting visibly atomic.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@contracts/staking/src/rewards.rs` around lines 683 - 692, Merge the
consecutive `if !assigned.is_zero()` blocks in the surrounding reward-stipend
flow into one conditional. Keep the reserve disbursement, short-disbursement
validation, and accounting credit together in the true branch, and attach the
existing `StipendSkipped` else branch once so the behavior remains unchanged.

116-185: 📐 Maintainability & Code Quality | 🔵 Trivial | 🏗️ Heavy lift

Deduplicate the claimable computation shared with consume_delegator_claim.

Lines 131-183 and Lines 247-307 implement the same epoch-walk math; only cursor persistence differs. Divergence between the read path (getDelegatorFee) and the settlement path would silently mis-report or mis-pay rewards. Consider one core walk parameterized by a commit: bool (or returning the final gaps for the caller to persist).

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@contracts/staking/src/rewards.rs` around lines 116 - 185, Deduplicate the
epoch-walk and claimable calculation currently implemented by
delegator_claimable and consume_delegator_claim into one shared helper,
parameterized by whether cursor gaps should be persisted or by returning the
final gaps for the caller. Keep read-only getDelegatorFee behavior non-mutating
while ensuring settlement persists its updated cursors, with both paths using
identical reward and undelegation calculations.
e2e/src/staking.rs (1)

191-195: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Set the block number explicitly instead of relying on the default.

The comment cites block 1_400, but no with_block_number call precedes it, so atEpoch == 2 depends on EvmTestingContext::default()'s implicit block height. Any change to that default silently breaks this assertion and makes the comment stale.

♻️ Make the epoch derivation explicit
+    context = context.with_block_number(1_400);
     let output = call(
         &mut context,
         OWNER,
         GENESIS_STAKING,
         IStakingRwasm::getValidatorDelegationCall {
             validator: VALIDATOR,
             delegator: OWNER,
         }
         .abi_encode(),
     );
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@e2e/src/staking.rs` around lines 191 - 195, Update the test setup before the
IStakingRwasm::getValidatorDelegationCall assertion to explicitly set the EVM
block number to 1_400 using the existing with_block_number mechanism. Keep the
atEpoch == 2 assertion and its epoch calculation comment aligned with that
explicit block height.
contracts/staking/src/consensus.rs (1)

26-47: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Duplicated external_call helper across two modules. The same selector-prefixing, ABI-encoding, and revert-data-propagating helper is defined twice, so any change to call semantics (gas limit, error mapping, revert bubbling) must be made in both places.

  • contracts/staking/src/consensus.rs#L26-L47: remove this definition and import the shared helper from contracts/staking/src/util.rs.
  • contracts/staking/src/equivocation.rs#L25-L46: remove this definition and import the same shared helper.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@contracts/staking/src/consensus.rs` around lines 26 - 47, Remove the
duplicated external_call helper from contracts/staking/src/consensus.rs lines
26-47 and contracts/staking/src/equivocation.rs lines 25-46, then import and use
the shared external_call from contracts/staking/src/util.rs in both modules.
Preserve the existing selector prefixing, ABI encoding, call behavior, and
revert-data propagation through the shared implementation.
contracts/staking/src/config.rs (1)

92-109: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

No event emitted for dependency changes.

Every other governance setter in this module emits a *Changed event; configure_dependencies silently rewrites liveness_slashing and blend_reserve, both consensus/economics-critical. Consider emitting change events for indexer/audit parity.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@contracts/staking/src/config.rs` around lines 92 - 109, Update
configure_dependencies to emit the established *Changed events after
successfully setting liveness_slashing and blend_reserve, matching the event
patterns used by other governance setters in this module and including both
updated values.
contracts/staking/src/util.rs (1)

463-559: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Consider extracting the shared backward snapshot scan.

touch_snapshot_at_or_before and validator_total_at implement the same "walk back from min(epoch, changed_at) to floor until a non-empty snapshot" predicate, duplicated field-by-field. A shared helper returning the resolved base epoch would keep the two in sync if the emptiness predicate ever changes. Also, lines 537-538 read changed_at_accessor twice; reuse one read.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@contracts/staking/src/util.rs` around lines 463 - 559, Extract the duplicated
backward snapshot scan from touch_snapshot_at_or_before and validator_total_at
into a shared helper that returns the resolved base epoch using the existing
non-empty snapshot predicate and floor bounds. Update both callers to use that
helper while preserving their current result handling, and reuse the single
changed_at value in validator_total_at instead of reading changed_at_accessor
twice.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@contracts/staking/src/economics.rs`:
- Around line 136-148: Align the snapshot epoch used by delegate and undelegate
accounting: update the delegate flow around touch_validator_snapshot so its
total_delegated credit uses the same epoch boundary as undelegate_from’s debit
via next_epoch(current_epoch). Reuse the existing epoch calculation or shared
accounting delay, preserving overflow checks and snapshot updates.
- Around line 220-264: Update undelegate_from around the delegation queue
handling and validator snapshot updates to reject undelegations when the latest
delegator checkpoint is scheduled after before_epoch, unless the pending
delegation is explicitly cancelled and its corresponding validator-total
checkpoints are adjusted. Ensure every affected future delegator and validator
checkpoint applies the undelegation delta consistently, preventing validator
totals from retaining stake without a matching delegator balance.

In `@contracts/staking/src/handlers.rs`:
- Around line 590-597: Update the ERR_VALIDATOR_OWNER_ALREADY_IN_USE revert in
the owner_validators_accessor conflict check to pass command.value, the new
owner address, instead of command.validator. Keep the existing validation
condition and error code unchanged, matching set_validator’s payload behavior.
- Around line 505-545: Update remove_validator after the total_delegated check
to clear/reset the validator’s self-delegation delegate_queue in
validator_delegations[validator][validator]. Ensure this cleanup occurs before
the validator is removed, while preserving the existing active-delegation
validation and removal flow.

In `@contracts/staking/src/liveness.rs`:
- Around line 183-215: Update the STATUS_JAIL branch in slash so repeated
slashes extend jailed_before to the maximum of its existing deadline and the
newly calculated jail_until. Ensure this deadline extension occurs even when the
quorum halt guard skips removal, while preserving the guard behavior for
non-jailed validators.

---

Nitpick comments:
In `@contracts/staking/src/config.rs`:
- Around line 92-109: Update configure_dependencies to emit the established
*Changed events after successfully setting liveness_slashing and blend_reserve,
matching the event patterns used by other governance setters in this module and
including both updated values.

In `@contracts/staking/src/consensus.rs`:
- Around line 26-47: Remove the duplicated external_call helper from
contracts/staking/src/consensus.rs lines 26-47 and
contracts/staking/src/equivocation.rs lines 25-46, then import and use the
shared external_call from contracts/staking/src/util.rs in both modules.
Preserve the existing selector prefixing, ABI encoding, call behavior, and
revert-data propagation through the shared implementation.

In `@contracts/staking/src/rewards.rs`:
- Around line 683-692: Merge the consecutive `if !assigned.is_zero()` blocks in
the surrounding reward-stipend flow into one conditional. Keep the reserve
disbursement, short-disbursement validation, and accounting credit together in
the true branch, and attach the existing `StipendSkipped` else branch once so
the behavior remains unchanged.
- Around line 116-185: Deduplicate the epoch-walk and claimable calculation
currently implemented by delegator_claimable and consume_delegator_claim into
one shared helper, parameterized by whether cursor gaps should be persisted or
by returning the final gaps for the caller. Keep read-only getDelegatorFee
behavior non-mutating while ensuring settlement persists its updated cursors,
with both paths using identical reward and undelegation calculations.

In `@contracts/staking/src/util.rs`:
- Around line 463-559: Extract the duplicated backward snapshot scan from
touch_snapshot_at_or_before and validator_total_at into a shared helper that
returns the resolved base epoch using the existing non-empty snapshot predicate
and floor bounds. Update both callers to use that helper while preserving their
current result handling, and reuse the single changed_at value in
validator_total_at instead of reading changed_at_accessor twice.

In `@e2e/src/staking.rs`:
- Around line 191-195: Update the test setup before the
IStakingRwasm::getValidatorDelegationCall assertion to explicitly set the EVM
block number to 1_400 using the existing with_block_number mechanism. Keep the
atEpoch == 2 assertion and its epoch calculation comment aligned with that
explicit block height.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: a80eea23-80c3-4c65-99a3-e05965533565

📥 Commits

Reviewing files that changed from the base of the PR and between ed33ee4 and 970936d.

⛔ Files ignored due to path filters (6)
  • contracts/Cargo.lock is excluded by !**/*.lock
  • crates/sdk-derive/derive-core/src/snapshots/fluentbase_sdk_derive_core__event__tests__all_indexed.snap is excluded by !**/*.snap
  • crates/sdk-derive/derive-core/src/snapshots/fluentbase_sdk_derive_core__event__tests__anonymous.snap is excluded by !**/*.snap
  • crates/sdk-derive/derive-core/src/snapshots/fluentbase_sdk_derive_core__event__tests__basic_transfer.snap is excluded by !**/*.snap
  • crates/sdk-derive/derive-core/src/snapshots/fluentbase_sdk_derive_core__event__tests__dynamic_indexed.snap is excluded by !**/*.snap
  • crates/sdk-derive/derive-core/src/snapshots/fluentbase_sdk_derive_core__event__tests__no_indexed.snap is excluded by !**/*.snap
📒 Files selected for processing (25)
  • contracts/README.md
  • contracts/staking/Cargo.toml
  • contracts/staking/README.md
  • contracts/staking/src/config.rs
  • contracts/staking/src/consensus.rs
  • contracts/staking/src/consts.rs
  • contracts/staking/src/economics.rs
  • contracts/staking/src/equivocation.rs
  • contracts/staking/src/events.rs
  • contracts/staking/src/handlers.rs
  • contracts/staking/src/lib.rs
  • contracts/staking/src/liveness.rs
  • contracts/staking/src/math.rs
  • contracts/staking/src/rewards.rs
  • contracts/staking/src/storage.rs
  • contracts/staking/src/tests.rs
  • contracts/staking/src/types.rs
  • contracts/staking/src/util.rs
  • crates/codec/src/lib.rs
  • crates/genesis/build.rs
  • crates/sdk-derive/derive-core/src/event.rs
  • crates/sdk/src/storage/primitive.rs
  • crates/types/src/genesis.rs
  • e2e/src/lib.rs
  • e2e/src/staking.rs

Comment thread contracts/staking/src/economics.rs Outdated
Comment thread contracts/staking/src/economics.rs Outdated
Comment thread contracts/staking/src/handlers.rs Outdated
Comment thread contracts/staking/src/handlers.rs Outdated
Comment thread contracts/staking/src/liveness.rs Outdated
@hedwig0x
hedwig0x force-pushed the feat/flu-989-rust-staking branch 2 times, most recently from f91bbc5 to 6467f0f Compare July 29, 2026 15:45
@dmitry123
dmitry123 force-pushed the feat/flu-989-rust-staking branch from 293c320 to 3f0614e Compare July 30, 2026 11:29

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
contracts/staking/README.md (1)

67-69: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Clarify the Solidity parity warning.

“Remains affected” does not identify the actual behavior or the required deployment action. State explicitly that the Solidity self-stake lookup depends on mutable validator ownership and that owner changes must be disabled before deployment/use.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@contracts/staking/README.md` around lines 67 - 69, Update the Solidity parity
warning in the staking README to explicitly state that the self-stake lookup
depends on mutable validator ownership, and instruct users to disable validator
ownership changes before deploying or using it as the canonical implementation.
🧹 Nitpick comments (2)
contracts/staking/src/equivocation.rs (2)

112-138: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

commit_report silently overwrites an existing commitment.

Re-committing resets committed_at, so a beneficiary with a mature commitment that re-commits (e.g. duplicate/retried tx) loses maturity and must wait again. Self-inflicted only, but an explicit revert or no-op on identical commitment would make the flow less surprising.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@contracts/staking/src/equivocation.rs` around lines 112 - 138, The
commit_report function overwrites existing commitments and resets their maturity
timestamp. Before setting commitment and committed_at, read the beneficiary’s
current commitment and explicitly revert or no-op when one already exists, with
identical commitments handled without resetting committed_at; preserve the
existing initialization, validation, storage, and event behavior for new
commitments.

89-97: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Maturity window is exactly one block.

current_block <= committed_at only forces the reveal into a later block. That blocks same-block copy/front-running, but an observer can still commit in block N+1 after seeing your commitment in N and reveal in N+2 — they gain nothing here because the reward is bound to command.beneficiary, so the guarantee holds, but consider a configurable minimum delay if the intent is a real reveal delay.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@contracts/staking/src/equivocation.rs` around lines 89 - 97, Review the
maturity check in the equivocation handling flow around
committed_at_accessor().get_checked and make the minimum reveal delay
configurable if the feature requires more than preventing same-block reveals.
Use the configured delay when comparing current_block with committed_at,
preserve ERR_EQUIVOCATION_COMMITMENT_NOT_MATURE and its diagnostic values, and
keep the beneficiary-bound reward behavior unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@contracts/staking/README.md`:
- Around line 67-69: Update the Solidity parity warning in the staking README to
explicitly state that the self-stake lookup depends on mutable validator
ownership, and instruct users to disable validator ownership changes before
deploying or using it as the canonical implementation.

---

Nitpick comments:
In `@contracts/staking/src/equivocation.rs`:
- Around line 112-138: The commit_report function overwrites existing
commitments and resets their maturity timestamp. Before setting commitment and
committed_at, read the beneficiary’s current commitment and explicitly revert or
no-op when one already exists, with identical commitments handled without
resetting committed_at; preserve the existing initialization, validation,
storage, and event behavior for new commitments.
- Around line 89-97: Review the maturity check in the equivocation handling flow
around committed_at_accessor().get_checked and make the minimum reveal delay
configurable if the feature requires more than preventing same-block reveals.
Use the configured delay when comparing current_block with committed_at,
preserve ERR_EQUIVOCATION_COMMITMENT_NOT_MATURE and its diagnostic values, and
keep the beneficiary-bound reward behavior unchanged.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 3b065254-866b-465e-a4f5-d0aac93b46ac

📥 Commits

Reviewing files that changed from the base of the PR and between 6467f0f and 293c320.

📒 Files selected for processing (9)
  • contracts/staking/README.md
  • contracts/staking/src/consts.rs
  • contracts/staking/src/equivocation.rs
  • contracts/staking/src/events.rs
  • contracts/staking/src/lib.rs
  • contracts/staking/src/storage.rs
  • contracts/staking/src/tests.rs
  • contracts/staking/src/types.rs
  • contracts/staking/src/util.rs
🚧 Files skipped from review as they are similar to previous changes (1)
  • contracts/staking/src/util.rs

Copy link
Copy Markdown
Member

FLU-1004 is implemented in 451448780 (fix(staking): use Solidity bytes ABI).

Changes:

  • use the Solidity bytes codec across staking consensus keys, equivocation inputs, verifier calls/returns, nested ConsensusKeys returns, and ConsensusKeysSet event data
  • centralize external-call ABI encoding and pin calldata/returndata/event compatibility to cast vectors
  • reject truncated dynamic-bytes bodies with a codec error instead of panicking
  • add a real Solidity EVM caller/verifier → rWasm staking happy-path test

Validation:

  • cargo test -p fluentbase-contracts-staking — 38 passed
  • cargo test -p fluentbase-codec — 66 unit, 17 integration, 2 doc tests passed
  • cargo test -p fluentbase-e2e staking — 2 passed
  • clippy with -D warnings for staking, codec, and E2E — passed
  • release wasm32-unknown-unknown no-default-features staking build — passed
  • direct rustfmt check for all touched Rust files and git diff --check — passed

Copy link
Copy Markdown
Member

FLU-1005 is implemented in ef14be7bf (fix(staking): encode consensus keys return tuple).

Root cause and fix:

  • getConsensusKeys(address) returns one dynamic Solidity struct, but the handler used function-argument encoding and omitted the required outer 0x20 tuple offset
  • the handler now encodes the derived ConsensusKeys value with the full Solidity ABI shape

Coverage:

  • reproduced the pre-fix output beginning with the internal 0x60 bytes offset
  • pinned complete empty and non-empty returndata to independent cast abi-encode "f((bytes,bytes32,uint64))" ... vectors
  • rechecked getValidatorsWithKeys and getEpochCommitteeWithStakes multi-value return shapes
  • added a real Solidity ABI decoder assertion through the EVM→rWasm E2E path

Validation:

  • staking suite: 39 passed
  • staking E2E subset: 2 passed
  • staking and E2E clippy with -D warnings: passed
  • release wasm32 no-default-features staking build: passed
  • touched-file rustfmt and git diff --check: passed

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
contracts/staking/src/consensus.rs (1)

25-42: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Duplicate external_call helper across modules.

This function is byte-identical to external_call in contracts/staking/src/equivocation.rs. Since util.rs already centralizes encode_external_call/write_abi/write_returns, consider moving this wrapper (encode + call + write-on-revert) there too so both call sites share one implementation.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@contracts/staking/src/consensus.rs` around lines 25 - 42, Move the shared
external_call wrapper from consensus.rs into util.rs alongside
encode_external_call, write_abi, and write_returns. Update both consensus.rs and
equivocation.rs to use the centralized helper, preserving its encoding, SDK
call, revert-data writing, and error propagation behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@contracts/staking/src/equivocation.rs`:
- Around line 410-412: Update seize_self_stake to use only the latest cumulative
delegate_queue snapshot for the validator’s self-delegation, rather than summing
historical entries. Preserve the existing reporter, validator, and owner flow
while ensuring stake seizure uses the latest queued amount and does not include
prior snapshots.

---

Nitpick comments:
In `@contracts/staking/src/consensus.rs`:
- Around line 25-42: Move the shared external_call wrapper from consensus.rs
into util.rs alongside encode_external_call, write_abi, and write_returns.
Update both consensus.rs and equivocation.rs to use the centralized helper,
preserving its encoding, SDK call, revert-data writing, and error propagation
behavior.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 9a3c058b-c763-4beb-99bc-6d1700fbf59b

📥 Commits

Reviewing files that changed from the base of the PR and between 293c320 and ef14be7.

📒 Files selected for processing (13)
  • contracts/staking/README.md
  • contracts/staking/src/consensus.rs
  • contracts/staking/src/consts.rs
  • contracts/staking/src/equivocation.rs
  • contracts/staking/src/events.rs
  • contracts/staking/src/handlers.rs
  • contracts/staking/src/lib.rs
  • contracts/staking/src/storage.rs
  • contracts/staking/src/tests.rs
  • contracts/staking/src/types.rs
  • contracts/staking/src/util.rs
  • crates/codec/src/bytes_codec.rs
  • e2e/src/staking.rs
🚧 Files skipped from review as they are similar to previous changes (1)
  • contracts/staking/src/util.rs

Comment thread contracts/staking/src/equivocation.rs Outdated
@dmitry123
dmitry123 force-pushed the feat/flu-989-rust-staking branch from 8e17acf to aef526e Compare July 30, 2026 12:50

Copy link
Copy Markdown
Member

FLU-996 is implemented in db43ada90.

  • Removed the removeValidator(address) ABI/dispatcher/handler and all removal-only event, error, helper, and selector-pin code.
  • Registered validator identities are now permanent; disabling preserves validator and owner records, while disable/activate, jail/readmission, and tombstoning remain supported.
  • Added lifecycle assertions for record and owner-mapping preservation after disable.

Validation: staking tests (38 passed), staking clippy, release no-std wasm build, staking E2E tests (2 passed), and E2E clippy all pass. Touched-file formatting passes; the package-wide formatting check still encounters the existing unrelated contracts/staking/src/math.rs formatting diff.

Copy link
Copy Markdown
Member

FLU-997 is implemented in 7219b6672.

  • Successful zero/partial reserve returns now skip the epoch, credit zero, emit StipendSkipped plus EpochBlendRewardsCommitted(epoch, 0), and advance the cursor without retry.
  • Exact delivery credits the reserve-capped assignment.
  • Reverted or malformed/no-data reserve responses remain atomic failures and keep the epoch retryable.
  • Added adversarial coverage for balance capping, zero/partial/full returns, repeated settlement, malformed output, and reserve reverts.

Validation: staking tests (42 passed), staking/testing/E2E clippy, testing crate tests, release no-std wasm build, and staking E2E tests (2 passed) all pass. Touched-file formatting passes; package-wide formatting still encounters the existing unrelated contracts/staking/src/math.rs diff.

Copy link
Copy Markdown
Member

FLU-1006 is implemented in c2f823516.

  • Added independent cast-pinned coverage for successful setConsensusKeys calldata, equivocation dispatcher calldata, verifier calldata/bytes returndata, full nested dynamic getter output, and both staking dynamic events.
  • Removed the tautological selector recomputation test while retaining the independent hexadecimal selector pins.
  • Documented the repository-wide dynamic non-indexed event audit and the four event payloads whose byte shape changes under function-argument encoding.

Validation: staking tests (44 passed), SDK event-derive tests (7 passed), staking and SDK-derive clippy with warnings denied, and touched-file rustfmt all pass. The contracts package-wide fmt check still encounters the pre-existing unrelated contracts/staking/src/math.rs diff.

Reject undelegation while a later cumulative checkpoint is pending so validator totals and delegator balances cannot diverge. Add regression coverage for pending checkpoints and latest-only equivocation seizure.
@hedwig0x
hedwig0x force-pushed the feat/flu-989-rust-staking branch from c2f8235 to ea7c6da Compare July 30, 2026 14:03
@hedwig0x

Copy link
Copy Markdown
Collaborator Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 30, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🧹 Nitpick comments (9)
crates/testing/src/host.rs (1)

347-363: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Nested calls surface a misleading panic.

If the installed handler itself triggers SharedAPI::call (mock calling back into the contract), the handler is already take()n and the expect reports "call handler is not configured", which points debugging in the wrong direction. Distinguishing the two states makes mock failures self-explanatory.

♻️ Suggested tweak
-        let mut handler = self
-            .inner
-            .borrow_mut()
-            .call_handler
-            .take()
-            .expect("call handler is not configured for testing context");
+        let mut handler = self.inner.borrow_mut().call_handler.take().expect(
+            "call handler is missing: either it was never configured, or a nested call was made \
+             from within the handler itself",
+        );
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/testing/src/host.rs` around lines 347 - 363, Update the call-handler
retrieval in call to distinguish an initially unconfigured handler from a
handler temporarily absent during a nested call. Preserve the existing
configuration panic for the first case, but use a distinct, self-explanatory
panic message when call_handler is already taken by an active handler
invocation, and continue restoring the handler after the invocation.
contracts/staking/src/rewards.rs (2)

19-55: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

external_call/call_decode are re-implemented in three staking modules. All three copies do the same thing — encode selector + params, sdk.call, forward revert data, decode the result — differing only in the encoder trait bound (Encoder vs FunctionArgs). One shared helper in util.rs (alongside encode_external_call) would keep revert-propagation and decode-error mapping uniform.

  • contracts/staking/src/rewards.rs#L19-L55: move both helpers into util.rs (generic over the encoder bound) and import them here.
  • contracts/staking/src/consensus.rs#L23-L40: delete the local external_call and use the shared helper.
  • contracts/staking/src/equivocation.rs#L177-L209: delete the local external_call/call_decode pair and use the shared helpers.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@contracts/staking/src/rewards.rs` around lines 19 - 55, Consolidate the
duplicated external-call and result-decoding logic into shared generic helpers
in util.rs alongside encode_external_call, preserving revert-data forwarding and
decode-error mapping while supporting the differing Encoder and FunctionArgs
bounds. In contracts/staking/src/rewards.rs#L19-55, move external_call and
call_decode to util.rs and import them; in
contracts/staking/src/consensus.rs#L23-40, remove the local external_call and
use the shared helper; in contracts/staking/src/equivocation.rs#L177-209, remove
both local helpers and use the shared implementations.

108-177: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Consider extracting the shared claim-walk to avoid reward-math divergence.

delegator_claimable and consume_delegator_claim duplicate the delegate/undelegate queue traversal and payout math almost line for line; the only real difference is cursor persistence. A shared iterator/walker that takes a &mut "commit" callback (or a flag) would keep view and mutating paths provably identical.

Also applies to: 223-304

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@contracts/staking/src/rewards.rs` around lines 108 - 177, Extract the
duplicated delegate/undelegate queue traversal and reward calculation from
delegator_claimable and consume_delegator_claim into one shared walker. Make the
walker own the common payout and balance logic while accepting the
caller-specific cursor commit behavior, so the read-only and mutating paths
remain identical except for persistence. Update both functions to use this
shared implementation.
contracts/staking/src/handlers.rs (2)

165-180: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Consider rejecting a past dpos_activation_block here too. set_dpos_activation_block guards with ERR_ACTIVATION_BLOCK_IN_PAST (config.rs Lines 440-442), but configure only checks interval alignment, so a one-shot misconfiguration can pin activation in the past and immediately activate DPoS.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@contracts/staking/src/handlers.rs` around lines 165 - 180, Update the
validation in configure near the dpos_activation_block alignment check to reject
activation blocks earlier than the current block, using the existing
ERR_ACTIVATION_BLOCK_IN_PAST error and the established current-block value.
Preserve the interval-alignment validation for non-past activation blocks.

28-35: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Drop the redundant InitializeCommand re-wrap. The tuple from decode_args is immediately repackaged and then destructured field-by-field; using the tuple bindings directly removes a whole indirection.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@contracts/staking/src/handlers.rs` around lines 28 - 35, Remove the
intermediate InitializeCommand construction after decode_args in the handler.
Destructure or pass the decoded tuple bindings directly into the subsequent
logic, eliminating the redundant field-by-field re-wrap while preserving the
existing argument order and behavior.
contracts/staking/src/math.rs (1)

50-52: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Run cargo fmt on this file. The PR notes package-wide formatting still reports a diff in contracts/staking/src/math.rs; line 52 is the likely offender (rustfmt wraps the assert_eq! args at width 100 given the nested expression). CI treats fmt/clippy as errors.

As per coding guidelines: "Run cargo fmt or cargo fmt --check for all touched Rust code to ensure formatting compliance."

♻️ Suggested formatting
-        assert_eq!(expand_balance(U112::MAX), overflow - BALANCE_COMPACT_PRECISION);
+        assert_eq!(
+            expand_balance(U112::MAX),
+            overflow - BALANCE_COMPACT_PRECISION
+        );
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@contracts/staking/src/math.rs` around lines 50 - 52, Run cargo fmt on
contracts/staking/src/math.rs, ensuring the assert_eq! involving
expand_balance(U112::MAX) is formatted according to rustfmt’s line-width rules.
Verify the touched Rust code passes cargo fmt --check without unrelated changes.

Source: Coding guidelines

contracts/staking/src/config.rs (1)

106-123: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

No event for dependency changes. Every other setter in this module emits a *Changed event; liveness_slashing/blend_reserve mutations are silent, which hurts off-chain governance auditing.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@contracts/staking/src/config.rs` around lines 106 - 123, The
configure_dependencies function updates liveness_slashing and blend_reserve
without emitting change events. After successfully setting each dependency, emit
the corresponding *Changed event using the module’s existing event pattern and
ensure events are only emitted for mutations that complete successfully.
contracts/staking/src/util.rs (1)

526-604: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Deduplicate the ERC-20 call paths. safe_transfer_from and safe_transfer differ only in the encoded input and the zero-amount shortcut; the token lookup, sdk.call, status propagation, and boolean-return validation are copy/paste. Extract a single token_call(sdk, input) helper.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@contracts/staking/src/util.rs` around lines 526 - 604, Extract the shared
staking-token call logic from safe_transfer_from and safe_transfer into a
token_call helper that accepts the SDK and encoded input, handling token lookup,
sdk.call status propagation, and boolean-return validation. Update both callers
to build their existing ERC-20 inputs and delegate to token_call, preserving
safe_transfer’s zero-amount early return.
contracts/staking/src/tests.rs (1)

2755-2807: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Reduce the repeated inline base struct via a small helper.

The same EquivocationCommand base literal is re-inlined three times just to vary one field. A helper (or deriving Clone on EquivocationCommand and using ..base.clone()) makes the intent of each case obvious.

♻️ Sketch
let base = |beneficiary, salt, evidence: Bytes| EquivocationCommand {
    evidence,
    pk_uncompressed: Bytes::new(),
    sig1_uncompressed: Bytes::new(),
    sig2_uncompressed: Bytes::new(),
    beneficiary,
    salt,
};
let redirected = base(front_runner, salt, evidence.clone());
let wrong_salt = base(beneficiary, B256::with_last_byte(0x53), evidence.clone());
let wrong_evidence = base(beneficiary, salt, Bytes::from_static(b"modified evidence"));
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@contracts/staking/src/tests.rs` around lines 2755 - 2807, Reduce duplication
in the equivocation commitment tests by defining a small local
constructor/helper for the shared EquivocationCommand fields, then use it to
create redirected, wrong_salt, and wrong_evidence with only their differing
values specified. Preserve each case’s existing beneficiary, salt, and evidence
inputs.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@contracts/staking/src/config.rs`:
- Around line 475-508: Update set_min_validator_stake_amount and
set_min_staking_amount to validate that the decoded value is a multiple of
BALANCE_COMPACT_PRECISION, matching handlers::configure, while retaining the
existing non-zero validation. Reject dust-precision values before writing
storage or emitting the change events.

In `@contracts/staking/src/handlers.rs`:
- Around line 224-263: Update configure’s change-event emissions to use the
actual previously stored configuration values instead of 0 or U256::ZERO. Read
each relevant field before overwriting it, including active_validators_length,
epoch_block_interval, felony_threshold, validator_jail_epoch_length,
undelegate_period, min_validator_stake_amount, min_staking_amount, and
dpos_activation_block, then pass those captured values as each event’s
prev_value.
- Around line 21-51: Restrict the staking initialize flow in initialize to the
trusted genesis/system caller before writing owner_accessor or any
initialization state. Validate the caller against the existing
GENESIS_GOVERNANCE or SYSTEM_CALLER mechanism and revert unauthorized
invocations, while preserving the current validation and setup behavior for the
authorized genesis transition.

In `@contracts/staking/src/util.rs`:
- Around line 505-524: Replace the per-epoch decrementing scan in
validator_total_at at contracts/staking/src/util.rs:505-524 with a stored
back-pointer lookup to the previous materialized snapshot epoch, preserving the
existing floor and value checks. Apply the same back-pointer-based lookup in
touch_snapshot_at_or_before at contracts/staking/src/util.rs:457-483; both sites
should jump directly to the prior materialized snapshot instead of iterating
epoch by epoch.

In `@e2e/src/staking.rs`:
- Around line 300-301: Update the staking test around delegation.atEpoch to
explicitly set the block to the intended pre-activation state before calling
delegate, or otherwise derive the assertion from the established current epoch
and WARMUP_DELAY setup. Make the test setup clearly demonstrate why atEpoch
equals 2 instead of relying on an implicit default block.

---

Nitpick comments:
In `@contracts/staking/src/config.rs`:
- Around line 106-123: The configure_dependencies function updates
liveness_slashing and blend_reserve without emitting change events. After
successfully setting each dependency, emit the corresponding *Changed event
using the module’s existing event pattern and ensure events are only emitted for
mutations that complete successfully.

In `@contracts/staking/src/handlers.rs`:
- Around line 165-180: Update the validation in configure near the
dpos_activation_block alignment check to reject activation blocks earlier than
the current block, using the existing ERR_ACTIVATION_BLOCK_IN_PAST error and the
established current-block value. Preserve the interval-alignment validation for
non-past activation blocks.
- Around line 28-35: Remove the intermediate InitializeCommand construction
after decode_args in the handler. Destructure or pass the decoded tuple bindings
directly into the subsequent logic, eliminating the redundant field-by-field
re-wrap while preserving the existing argument order and behavior.

In `@contracts/staking/src/math.rs`:
- Around line 50-52: Run cargo fmt on contracts/staking/src/math.rs, ensuring
the assert_eq! involving expand_balance(U112::MAX) is formatted according to
rustfmt’s line-width rules. Verify the touched Rust code passes cargo fmt
--check without unrelated changes.

In `@contracts/staking/src/rewards.rs`:
- Around line 19-55: Consolidate the duplicated external-call and
result-decoding logic into shared generic helpers in util.rs alongside
encode_external_call, preserving revert-data forwarding and decode-error mapping
while supporting the differing Encoder and FunctionArgs bounds. In
contracts/staking/src/rewards.rs#L19-55, move external_call and call_decode to
util.rs and import them; in contracts/staking/src/consensus.rs#L23-40, remove
the local external_call and use the shared helper; in
contracts/staking/src/equivocation.rs#L177-209, remove both local helpers and
use the shared implementations.
- Around line 108-177: Extract the duplicated delegate/undelegate queue
traversal and reward calculation from delegator_claimable and
consume_delegator_claim into one shared walker. Make the walker own the common
payout and balance logic while accepting the caller-specific cursor commit
behavior, so the read-only and mutating paths remain identical except for
persistence. Update both functions to use this shared implementation.

In `@contracts/staking/src/tests.rs`:
- Around line 2755-2807: Reduce duplication in the equivocation commitment tests
by defining a small local constructor/helper for the shared EquivocationCommand
fields, then use it to create redirected, wrong_salt, and wrong_evidence with
only their differing values specified. Preserve each case’s existing
beneficiary, salt, and evidence inputs.

In `@contracts/staking/src/util.rs`:
- Around line 526-604: Extract the shared staking-token call logic from
safe_transfer_from and safe_transfer into a token_call helper that accepts the
SDK and encoded input, handling token lookup, sdk.call status propagation, and
boolean-return validation. Update both callers to build their existing ERC-20
inputs and delegate to token_call, preserving safe_transfer’s zero-amount early
return.

In `@crates/testing/src/host.rs`:
- Around line 347-363: Update the call-handler retrieval in call to distinguish
an initially unconfigured handler from a handler temporarily absent during a
nested call. Preserve the existing configuration panic for the first case, but
use a distinct, self-explanatory panic message when call_handler is already
taken by an active handler invocation, and continue restoring the handler after
the invocation.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 70be606f-b121-422a-a7d3-ef03ac40b249

📥 Commits

Reviewing files that changed from the base of the PR and between 293c320 and 7cc2761.

⛔ Files ignored due to path filters (6)
  • contracts/Cargo.lock is excluded by !**/*.lock
  • crates/sdk-derive/derive-core/src/snapshots/fluentbase_sdk_derive_core__event__tests__all_indexed.snap is excluded by !**/*.snap
  • crates/sdk-derive/derive-core/src/snapshots/fluentbase_sdk_derive_core__event__tests__anonymous.snap is excluded by !**/*.snap
  • crates/sdk-derive/derive-core/src/snapshots/fluentbase_sdk_derive_core__event__tests__basic_transfer.snap is excluded by !**/*.snap
  • crates/sdk-derive/derive-core/src/snapshots/fluentbase_sdk_derive_core__event__tests__dynamic_indexed.snap is excluded by !**/*.snap
  • crates/sdk-derive/derive-core/src/snapshots/fluentbase_sdk_derive_core__event__tests__no_indexed.snap is excluded by !**/*.snap
📒 Files selected for processing (27)
  • contracts/README.md
  • contracts/staking/Cargo.toml
  • contracts/staking/README.md
  • contracts/staking/src/config.rs
  • contracts/staking/src/consensus.rs
  • contracts/staking/src/consts.rs
  • contracts/staking/src/economics.rs
  • contracts/staking/src/equivocation.rs
  • contracts/staking/src/events.rs
  • contracts/staking/src/handlers.rs
  • contracts/staking/src/lib.rs
  • contracts/staking/src/liveness.rs
  • contracts/staking/src/math.rs
  • contracts/staking/src/rewards.rs
  • contracts/staking/src/storage.rs
  • contracts/staking/src/tests.rs
  • contracts/staking/src/types.rs
  • contracts/staking/src/util.rs
  • crates/codec/src/bytes_codec.rs
  • crates/codec/src/lib.rs
  • crates/genesis/build.rs
  • crates/sdk-derive/derive-core/src/event.rs
  • crates/sdk/src/storage/primitive.rs
  • crates/testing/src/host.rs
  • crates/types/src/genesis.rs
  • e2e/src/lib.rs
  • e2e/src/staking.rs
🚧 Files skipped from review as they are similar to previous changes (3)
  • contracts/README.md
  • crates/codec/src/lib.rs
  • crates/types/src/genesis.rs

Comment thread contracts/staking/src/config.rs
Comment thread contracts/staking/src/handlers.rs Outdated
Comment thread contracts/staking/src/handlers.rs Outdated
Comment thread contracts/staking/src/util.rs Outdated
Comment thread e2e/src/staking.rs Outdated
@dmitry123
dmitry123 force-pushed the feat/flu-989-rust-staking branch from fc97ab7 to 50ea34d Compare July 31, 2026 13:15
Require verified consensus keys in every validator creation path, store compressed BLS keys as fixed words, and filter keyless validators before top-k committee selection.
Prevent validator and delegator reward cursors from consuming unsettled epochs while keeping matured undelegated principal independently claimable.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants