Skip to content

Commit ca26d90

Browse files
test: add disable_rewards flow (#49)
<!-- Reviewable:start --> This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/starkware-libs/starknet-staking/49) <!-- Reviewable:end --> <!-- CURSOR_SUMMARY --> --- > [!NOTE] > Adds a flow test for `update_rewards` with `disable_rewards` across consensus-rewards states, and exposes RewardsManager dispatchers and config helper to support it. > > - **Tests (`src/flow_test/test.cairo`)**: > - Add `update_rewards_disable_rewards_consensus_rewards_flow_test` verifying: > - No rewards when `disable_rewards` is true (consensus off/on) and panic on same-block re-call. > - No rewards when `disable_rewards` is false before consensus epoch; rewards after consensus epoch. > - Uses `StakingError::REWARDS_ALREADY_UPDATED`, `advance_block_number_global`, and expected V3 rewards via `calculate_staker_strk_rewards_with_balances_v3`. > - Import updates to support new flow (errors, traits, constants). > - **Test utils (`src/flow_test/utils.cairo`)**: > - Add RewardsManager dispatchers: `rewards_manager_dispatcher`, `rewards_manager_safe_dispatcher` (interfaces wired in `staking::staking::interface`). > - Add `set_consensus_rewards_first_epoch(epoch_id)` helper via `IStakingConfigDispatcher`. > - Add `SystemState::update_rewards(staker, disable_rewards)` convenience wrapper. > - **Docs (`src/flow_test/flow_ideas.md`)**: > - Update flow ideas around `disable_rewards` scenarios. > > <sup>Written by [Cursor Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit ff2da76. This will update automatically on new commits. Configure [here](https://cursor.com/dashboard?tab=bugbot).</sup> <!-- /CURSOR_SUMMARY -->
1 parent ca11c5a commit ca26d90

File tree

3 files changed

+168
-10
lines changed

3 files changed

+168
-10
lines changed

src/flow_test/flow_ideas.md

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,6 @@
1717

1818
## v3 flag
1919
- update_rewards_from_attestation, update_rewards (not distribute), set_v3_epoch, update_rewards_from_attestation, update_rewards (not distribute), advance epoch to v3 epoch, update_rewards_from_attestation-panic, update_rewards-distribute, advance epoch, update_rewards_from_attestation-panic, update_rewards-distribute, advance epoch, update_rewards_from_attestation-panic, update_rewards with disable rewards-not distribute.
20-
- disable rewards with v3 off - no rewards, same block - panic
21-
- disable rewards with v3 on - no rewards, same block - panic
22-
- not disable rewards with v3 off - no rewards, same block - panic
23-
- not disable rewards with v3 on - rewards, same block - panic
2420

2521
## k=1 -> k=2 balances
2622
- staker with stake, upgrade, increase stake - before upgrade after 1 epoch, after 2 epochs (check also total stake)

src/flow_test/test.cairo

Lines changed: 140 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use core::num::traits::Zero;
22
use snforge_std::TokenImpl;
3-
use staking::constants::STRK_IN_FRIS;
3+
use staking::constants::{K, STRK_IN_FRIS};
44
use staking::flow_test::flows;
55
use staking::flow_test::utils::{
66
RewardSupplierTrait, StakingTrait, SystemConfigTrait, SystemDelegatorTrait, SystemStakerTrait,
@@ -9,22 +9,29 @@ use staking::flow_test::utils::{
99
use staking::pool::pool::Pool;
1010
use staking::pool::pool::Pool::STRK_CONFIG;
1111
use staking::pool::utils::compute_rewards_rounded_down;
12-
use staking::staking::interface::{IStakingConsensusDispatcherTrait, IStakingDispatcherTrait};
12+
use staking::staking::errors::Error as StakingError;
13+
use staking::staking::interface::{
14+
IStakingConsensusDispatcherTrait, IStakingDispatcherTrait,
15+
IStakingRewardsManagerSafeDispatcherTrait,
16+
};
1317
use staking::staking::utils::{BTC_WEIGHT_FACTOR, STAKING_POWER_BASE_VALUE, STRK_WEIGHT_FACTOR};
1418
use staking::test_utils::constants::{
1519
BTC_18D_CONFIG, BTC_5D_CONFIG, BTC_8D_CONFIG, PUBLIC_KEY, STRK_BASE_VALUE,
1620
TEST_MIN_BTC_FOR_REWARDS,
1721
};
1822
use staking::test_utils::{
1923
StakingInitConfig, calculate_staker_btc_pool_rewards_v2,
20-
calculate_staker_strk_rewards_with_balances_v2,
24+
calculate_staker_strk_rewards_with_balances_v2, calculate_staker_strk_rewards_with_balances_v3,
2125
calculate_strk_pool_rewards_with_pool_balance_v2, compute_rewards_per_unit,
2226
custom_decimals_token, deploy_mock_erc20_decimals_contract,
2327
};
28+
use starkware_utils::errors::{Describable, ErrorDisplay};
2429
use starkware_utils::math::abs::wide_abs_diff;
2530
use starkware_utils::math::utils::mul_wide_and_div;
2631
use starkware_utils::time::time::Time;
27-
use starkware_utils_testing::test_utils::cheat_caller_address_once;
32+
use starkware_utils_testing::test_utils::{
33+
advance_block_number_global, assert_panic_with_error, cheat_caller_address_once,
34+
};
2835
use crate::types::Amount;
2936

3037
#[test]
@@ -2619,3 +2626,132 @@ fn get_stakers_staking_power_100_flow_test() {
26192626
.span();
26202627
assert!(stakers == expected_stakers);
26212628
}
2629+
2630+
/// Flow:
2631+
/// Staker stake
2632+
/// Disable rewards is true with consensus off - test no rewards
2633+
/// Attempt again same block - panic
2634+
/// Disable rewards is false with consensus off - test no rewards
2635+
/// Attempt again same block - panic
2636+
/// Enable consensus rewards (in K epochs)
2637+
/// Disable rewards is true before consensus epoch - test no rewards
2638+
/// Attempt again same block - panic
2639+
/// Disable rewards is false before consensus epoch - test no rewards
2640+
/// Attempt again same block - panic
2641+
/// Disable rewards is true with consensus on - test no rewards
2642+
/// Attempt again same block - panic
2643+
/// Disable rewards is false with consensus on - test rewards
2644+
/// Attempt again same block - panic
2645+
#[test]
2646+
#[feature("safe_dispatcher")]
2647+
fn update_rewards_disable_rewards_consensus_rewards_flow_test() {
2648+
let cfg: StakingInitConfig = Default::default();
2649+
let mut system = SystemConfigTrait::basic_stake_flow_cfg(:cfg).deploy();
2650+
let stake_amount = system.staking.get_min_stake();
2651+
let staker = system.new_staker(amount: stake_amount);
2652+
let commission = 200;
2653+
system.stake(:staker, amount: stake_amount, pool_enabled: false, :commission);
2654+
system.advance_k_epochs();
2655+
2656+
// Disable rewards = true with consensus off - no rewards
2657+
system.update_rewards(:staker, disable_rewards: true);
2658+
let rewards = system.staker_claim_rewards(:staker);
2659+
assert!(rewards.is_zero());
2660+
2661+
// Attempt again same block - panic
2662+
let result = system
2663+
.staking
2664+
.rewards_manager_safe_dispatcher()
2665+
.update_rewards(staker_address: staker.staker.address, disable_rewards: true);
2666+
assert_panic_with_error(
2667+
:result, expected_error: StakingError::REWARDS_ALREADY_UPDATED.describe(),
2668+
);
2669+
advance_block_number_global(blocks: 1);
2670+
2671+
// Disable rewards = false with consensus off - no rewards
2672+
system.update_rewards(:staker, disable_rewards: false);
2673+
let rewards = system.staker_claim_rewards(:staker);
2674+
assert!(rewards.is_zero());
2675+
2676+
// Attempt again same block - panic
2677+
let result = system
2678+
.staking
2679+
.rewards_manager_safe_dispatcher()
2680+
.update_rewards(staker_address: staker.staker.address, disable_rewards: false);
2681+
assert_panic_with_error(
2682+
:result, expected_error: StakingError::REWARDS_ALREADY_UPDATED.describe(),
2683+
);
2684+
advance_block_number_global(blocks: 1);
2685+
2686+
// Enable consensus rewards
2687+
system
2688+
.staking
2689+
.set_consensus_rewards_first_epoch(epoch_id: system.staking.get_current_epoch() + K.into());
2690+
2691+
// Disable rewards = true before consensus epoch - no rewards
2692+
system.update_rewards(:staker, disable_rewards: true);
2693+
let rewards = system.staker_claim_rewards(:staker);
2694+
assert!(rewards.is_zero());
2695+
2696+
// Attempt again same block - panic
2697+
let result = system
2698+
.staking
2699+
.rewards_manager_safe_dispatcher()
2700+
.update_rewards(staker_address: staker.staker.address, disable_rewards: true);
2701+
assert_panic_with_error(
2702+
:result, expected_error: StakingError::REWARDS_ALREADY_UPDATED.describe(),
2703+
);
2704+
advance_block_number_global(blocks: 1);
2705+
2706+
// Disable rewards = false before consensus epoch - no rewards
2707+
system.update_rewards(:staker, disable_rewards: false);
2708+
let rewards = system.staker_claim_rewards(:staker);
2709+
assert!(rewards.is_zero());
2710+
2711+
// Attempt again same block - panic
2712+
let result = system
2713+
.staking
2714+
.rewards_manager_safe_dispatcher()
2715+
.update_rewards(staker_address: staker.staker.address, disable_rewards: false);
2716+
assert_panic_with_error(
2717+
:result, expected_error: StakingError::REWARDS_ALREADY_UPDATED.describe(),
2718+
);
2719+
system.advance_k_epochs();
2720+
2721+
// Disable rewards = true with consensus on - no rewards
2722+
system.update_rewards(:staker, disable_rewards: true);
2723+
let rewards = system.staker_claim_rewards(:staker);
2724+
assert!(rewards.is_zero());
2725+
2726+
// Attempt again same block - panic
2727+
let result = system
2728+
.staking
2729+
.rewards_manager_safe_dispatcher()
2730+
.update_rewards(staker_address: staker.staker.address, disable_rewards: true);
2731+
assert_panic_with_error(
2732+
:result, expected_error: StakingError::REWARDS_ALREADY_UPDATED.describe(),
2733+
);
2734+
advance_block_number_global(blocks: 1);
2735+
2736+
// Disable rewards = false with consensus on - rewards
2737+
system.update_rewards(:staker, disable_rewards: false);
2738+
let rewards = system.staker_claim_rewards(:staker);
2739+
let (expected_rewards, _) = calculate_staker_strk_rewards_with_balances_v3(
2740+
amount_own: stake_amount,
2741+
pool_amount: Zero::zero(),
2742+
:commission,
2743+
staking_contract: system.staking.address,
2744+
minting_curve_contract: system.minting_curve.address,
2745+
);
2746+
assert!(expected_rewards.is_non_zero());
2747+
assert!(rewards == expected_rewards);
2748+
2749+
// Attempt again same block - panic
2750+
let result = system
2751+
.staking
2752+
.rewards_manager_safe_dispatcher()
2753+
.update_rewards(staker_address: staker.staker.address, disable_rewards: false);
2754+
assert_panic_with_error(
2755+
:result, expected_error: StakingError::REWARDS_ALREADY_UPDATED.describe(),
2756+
);
2757+
}

src/flow_test/utils.cairo

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,9 @@ use staking::staking::interface::{
3939
IStakingConsensusDispatcher, IStakingConsensusSafeDispatcher, IStakingDispatcher,
4040
IStakingDispatcherTrait, IStakingMigrationDispatcher, IStakingMigrationDispatcherTrait,
4141
IStakingMigrationSafeDispatcher, IStakingPauseDispatcher, IStakingPauseDispatcherTrait,
42-
IStakingPoolDispatcher, IStakingPoolSafeDispatcher, IStakingSafeDispatcher,
43-
IStakingSafeDispatcherTrait, IStakingTokenManagerDispatcher,
42+
IStakingPoolDispatcher, IStakingPoolSafeDispatcher, IStakingRewardsManagerDispatcher,
43+
IStakingRewardsManagerDispatcherTrait, IStakingRewardsManagerSafeDispatcher,
44+
IStakingSafeDispatcher, IStakingSafeDispatcherTrait, IStakingTokenManagerDispatcher,
4445
IStakingTokenManagerDispatcherTrait, IStakingTokenManagerSafeDispatcher,
4546
IStakingTokenManagerSafeDispatcherTrait, StakerInfoV1, StakerInfoV1Trait, StakerPoolInfoV2,
4647
};
@@ -338,6 +339,16 @@ pub(crate) impl StakingImpl of StakingTrait {
338339
IStakingPoolSafeDispatcher { contract_address: self.address }
339340
}
340341

342+
fn rewards_manager_dispatcher(self: StakingState) -> IStakingRewardsManagerDispatcher nopanic {
343+
IStakingRewardsManagerDispatcher { contract_address: self.address }
344+
}
345+
346+
fn rewards_manager_safe_dispatcher(
347+
self: StakingState,
348+
) -> IStakingRewardsManagerSafeDispatcher nopanic {
349+
IStakingRewardsManagerSafeDispatcher { contract_address: self.address }
350+
}
351+
341352
fn set_roles(self: StakingState) {
342353
set_account_as_upgrade_governor(
343354
contract: self.address,
@@ -555,6 +566,14 @@ pub(crate) impl StakingImpl of StakingTrait {
555566
);
556567
self.safe_token_manager_dispatcher().enable_token(:token_address)
557568
}
569+
570+
fn set_consensus_rewards_first_epoch(self: StakingState, epoch_id: Epoch) {
571+
cheat_caller_address_once(
572+
contract_address: self.address, caller_address: self.roles.app_governor,
573+
);
574+
let config_dispatcher = IStakingConfigDispatcher { contract_address: self.address };
575+
config_dispatcher.set_consensus_rewards_first_epoch(:epoch_id);
576+
}
558577
}
559578

560579
/// The `MintingCurveRoles` struct represents the various roles involved in the minting curve
@@ -1554,6 +1573,13 @@ pub(crate) impl SystemStakerImpl of SystemStakerTrait {
15541573
self.advance_epoch();
15551574
}
15561575
}
1576+
1577+
fn update_rewards(self: SystemState, staker: Staker, disable_rewards: bool) {
1578+
self
1579+
.staking
1580+
.rewards_manager_dispatcher()
1581+
.update_rewards(staker_address: staker.staker.address, :disable_rewards);
1582+
}
15571583
}
15581584

15591585
/// The `Delegator` struct represents a delegator in the staking system.

0 commit comments

Comments
 (0)