Skip to content

feat(elections): add adaptive staking strategy#41

Open
Keshoid wants to merge 10 commits intorelease/nodectl/v0.4.0from
feature/sma-35-add-adaptive-staking-strategy
Open

feat(elections): add adaptive staking strategy#41
Keshoid wants to merge 10 commits intorelease/nodectl/v0.4.0from
feature/sma-35-add-adaptive-staking-strategy

Conversation

@Keshoid
Copy link
Copy Markdown
Contributor

@Keshoid Keshoid commented Mar 31, 2026

Summary

  • Add AdaptiveSplit50 staking strategy that adapts stake amount based on current election state
  • Emulate TON Elector's selection algorithm to compute minimum effective stake
  • Split funds in half when viable, stake everything when half is below threshold
  • Top-up stake when competitors raise theirs

Changes

  • election_emulator.rs — new module: emulates Elector's try_elect + compute_min_effective_stake
  • adaptive_strategy.rs — new module: is_adaptive_split50_ready (wait/sleep logic) + calc_adaptive_stake
  • runner.rs — integrate adaptive strategy into election runner, filter own stake from participants
  • app_config.rs — add sleep_period_pct, waiting_period_pct config fields
  • staking-strategies.md — documentation for the strategy
  • Unit + integration tests covering emulation, stake calculation, and 3-tick top-up cycle

Closes SMA-35

Copilot AI review requested due to automatic review settings March 31, 2026 16:30
@linear
Copy link
Copy Markdown

linear bot commented Mar 31, 2026

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Copilot reviewed 13 out of 13 changed files in this pull request and generated 2 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +513 to +521
if !(0.0..=1.0).contains(&self.sleep_period_pct) {
anyhow::bail!("sleep_period_pct must be in range [0.0..1.0]");
}
if !(0.0..=1.0).contains(&self.waiting_period_pct) {
anyhow::bail!("waiting_period_pct must be in range [0.0..1.0]");
}
if self.sleep_period_pct > self.waiting_period_pct {
anyhow::bail!("sleep_period_pct must be <= waiting_period_pct");
}
Copy link

Copilot AI Mar 31, 2026

Choose a reason for hiding this comment

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

Configuration parameter validation only applies when the full ElectionsConfig is validated, but sleep_period_pct and waiting_period_pct are added to the config regardless of the selected stake policy. These parameters should only be required/validated when AdaptiveSplit50 strategy is in use. Consider documenting this design choice or conditionally validating these parameters only for AdaptiveSplit50 policy.

Suggested change
if !(0.0..=1.0).contains(&self.sleep_period_pct) {
anyhow::bail!("sleep_period_pct must be in range [0.0..1.0]");
}
if !(0.0..=1.0).contains(&self.waiting_period_pct) {
anyhow::bail!("waiting_period_pct must be in range [0.0..1.0]");
}
if self.sleep_period_pct > self.waiting_period_pct {
anyhow::bail!("sleep_period_pct must be <= waiting_period_pct");
}
// `sleep_period_pct` and `waiting_period_pct` are only relevant for the
// AdaptiveSplit50 stake policy. We only validate them if that policy is
// used either as the default or in any per-node override.
let uses_adaptive_split50 = matches!(self.policy, StakePolicy::AdaptiveSplit50 { .. })
|| self
.policy_overrides
.values()
.any(|policy| matches!(policy, StakePolicy::AdaptiveSplit50 { .. }));
if uses_adaptive_split50 {
if !(0.0..=1.0).contains(&self.sleep_period_pct) {
anyhow::bail!("sleep_period_pct must be in range [0.0..1.0]");
}
if !(0.0..=1.0).contains(&self.waiting_period_pct) {
anyhow::bail!("waiting_period_pct must be in range [0.0..1.0]");
}
if self.sleep_period_pct > self.waiting_period_pct {
anyhow::bail!("sleep_period_pct must be <= waiting_period_pct");
}
}

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Skipped. Validating always is simpler and catches misconfigs early.

@@ -46,7 +52,7 @@ const NPOOL_COMPUTE_FEE: u64 = 200_000_000;
/// Gas fee consumed by validator wallet
const WALLET_COMPUTE_FEE: u64 = 200_000_000;
/// Reserved minimum balance on the wallet (or pool) balance for stake calculations
Copy link

Copilot AI Mar 31, 2026

Choose a reason for hiding this comment

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

The MIN_NANOTON_FOR_STORAGE constant was increased from 1_000_000_000 to 1_050_000_000 nanotons (1.0 to 1.05 TON). While the increase improves safety margins, the rationale for this specific increase should be documented in the code comment to explain why 1.05 TON was chosen.

Suggested change
/// Reserved minimum balance on the wallet (or pool) balance for stake calculations
/// Reserved minimum balance on the wallet (or pool) balance for stake calculations.
/// 1.05 TON (1_050_000_000 nanotons) is used instead of 1.0 TON to include a safety
/// margin that covers storage rent and small gas/fee fluctuations, reducing the risk
/// that stake or recovery operations fail due to the wallet balance dropping too close
/// to the protocol's minimal storage requirements.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Added a descriptive comment.

/// 3) Phase 2: fine_step = max(1, (340k - 330k) / 10) = 1k
/// 331k(out) → 332k(out) → 333k(out) → 334k(in, eff_min=334k)
/// 4) return Some(334k)
/// ```
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

for me it's hard to read this comment. Should i use some special extension for ide?)(e.g. /// ```text - shoud it has formatting in comments?)

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.

3 participants