diff --git a/src/flow_test/flow_ideas.md b/src/flow_test/flow_ideas.md index fc97570b..ae9c05d9 100644 --- a/src/flow_test/flow_ideas.md +++ b/src/flow_test/flow_ideas.md @@ -40,18 +40,12 @@ - find sigma: Enter V0, change in V2, catch all ifs. - find sigma_edge_cases: catch the comments. - find sigma - cover all branches with member from V0, V1, V2(V2 DONE). -- member from V0, update balance at V3 before getting rewards, pool gets rewards, test rewards. -- member from V0, pool gets rewards at V1, update balance at V3 before getting rewards, pool gets rewards, test rewards. -- member from V0, pool gets rewards at V1, pool gets rewards at V3, update balance, test rewards. -- member from V0, pool gets rewards at V1, pool gets rewards at V3, test rewards without update balance. - member from V1(+V2), update balance at V3, pool gets rewards at V3, test rewards. (IDX=0) - member from V1(+V2), update balance at V1, update balance at V3, test rewards. (IDX=1=LEN) - member from V1(+V2), update balance at V1, pool gets rewards at V1, update balance at V3, test rewards. (IDX=1!=LEN) - member from V1(+V2), pool gets rewards at V1, pool gets rewards at V1, update balance at V1, update balance at V3, test rewards (IDX=LEN) - member from V1(+V2), pool gets_rewards at V1, pool gets rewards at V1, update balance at V1, pool gets rewards at V3, update balance at V3, test rewards. (REGULAR CASE) - member from V1(+V2), pool gets_rewards at V1, pool gets rewards at V1, update balance at V1, pool gets rewards at V1 same epoch, pool gets rewards at V3, update balance at V3, test rewards. (REGULAR CASE) -- V0->V1->V3: - - enter in V0, pool gets rewards in V1, change balance at V1, pool gets rewards in V1, pool gets rewards in V3, update balance at V3, pool gets rewards in V3, test rewards. more ideas: - member from V1, pool gets rewards at V1, update balance at V1, update balance at V3, pool gets rewards at V3, test rewards. - member from V1, pool gets rewards at V1, pool gets rewards at V3, update balance at V3, test rewards. diff --git a/src/flow_test/flows.cairo b/src/flow_test/flows.cairo index 719037cc..08a0fb8b 100644 --- a/src/flow_test/flows.cairo +++ b/src/flow_test/flows.cairo @@ -35,11 +35,11 @@ use staking::test_utils::constants::{ }; use staking::test_utils::{ calculate_pool_member_rewards, calculate_staker_btc_pool_rewards_v2, - calculate_staker_strk_rewards_v2, calculate_strk_pool_rewards_v2, - calculate_strk_pool_rewards_with_pool_balance_v2, compute_rewards_per_unit, - declare_pool_contract, declare_pool_eic_contract, declare_staking_contract, - load_from_simple_map, load_one_felt, strk_pool_update_rewards_v0, to_amount_18_decimals, - upgrade_implementation, + calculate_staker_strk_rewards_v2, calculate_strk_pool_rewards_v1, + calculate_strk_pool_rewards_v2, calculate_strk_pool_rewards_with_pool_balance_v2, + compute_rewards_per_unit, declare_pool_contract, declare_pool_eic_contract, + declare_staking_contract, load_from_simple_map, load_one_felt, strk_pool_update_rewards_v0, + to_amount_18_decimals, upgrade_implementation, }; use staking::types::{Amount, Commission}; use starknet::{ClassHash, ContractAddress, Store}; @@ -7141,3 +7141,378 @@ pub(crate) impl ToggleTokensBeforeAfterUpgradeFlowImpl of FlowTrait< assert!(tokens == expected_tokens); } } + +/// Flow: +/// Delegator enter in V0 +/// Attest in V1 (pool gets rewards) +/// Delegator change balance in V1 +/// Attest in V1 (pool gets rewards) +/// Attest in V3 (pool gets rewards) +/// Delegator change balance in V3 +/// Attest in V3 (pool gets rewards) +/// Test delegator rewards +#[derive(Drop, Copy)] +pub(crate) struct DelegatorChangeBalanceOverVersionsFlow { + pub(crate) staker: Option, + pub(crate) pool: Option, + pub(crate) delegator: Option, + pub(crate) expected_rewards: Option, +} +pub(crate) impl DelegatorChangeBalanceOverVersionsFlowImpl of FlowTrait< + DelegatorChangeBalanceOverVersionsFlow, +> { + fn setup(ref self: DelegatorChangeBalanceOverVersionsFlow, ref system: SystemState) { + let amount = system.staking.get_min_stake(); + let staker = system.new_staker(:amount); + let commission = 200; + system.stake(:staker, :amount, pool_enabled: true, :commission); + let pool = system.staking.get_pool(:staker); + let delegator = system.new_delegator(:amount); + system.delegate(:delegator, :pool, :amount); + self.staker = Option::Some(staker); + self.pool = Option::Some(pool); + self.delegator = Option::Some(delegator); + + let one_week = Time::weeks(count: 1); + system.advance_time(time: one_week); + system.staking.update_global_index_if_needed(); + + let expected_rewards = system.delegator_unclaimed_rewards(:delegator, :pool); + self.expected_rewards = Option::Some(expected_rewards); + + system.set_pool_for_upgrade(pool_address: pool); + system.set_staker_for_migration(staker_address: staker.staker.address); + } + + fn setup_v1(ref self: DelegatorChangeBalanceOverVersionsFlow, ref system: SystemState) { + let staker = self.staker.unwrap(); + let delegator = self.delegator.unwrap(); + let pool = self.pool.unwrap(); + + // Pool gets rewards. + system.advance_k_epochs_and_attest(:staker); + let mut expected_rewards_v1 = calculate_strk_pool_rewards_v1( + staker_address: staker.staker.address, + staking_contract: system.staking.address, + minting_curve_contract: system.minting_curve.address, + ); + + // Partial delegator intent. + let delegator_amount = system.pool_member_info_v1(:delegator, :pool).amount; + system.delegator_exit_intent(:delegator, :pool, amount: delegator_amount / 2); + + // Pool gets rewards. + system.advance_k_epochs_and_attest(:staker); + expected_rewards_v1 += + calculate_strk_pool_rewards_v1( + staker_address: staker.staker.address, + staking_contract: system.staking.address, + minting_curve_contract: system.minting_curve.address, + ); + + let expected_rewards_v0 = self.expected_rewards.unwrap(); + self.expected_rewards = Option::Some(expected_rewards_v0 + expected_rewards_v1); + } + + fn test(self: DelegatorChangeBalanceOverVersionsFlow, ref system: SystemState) { + let staker = self.staker.unwrap(); + let delegator = self.delegator.unwrap(); + let pool = self.pool.unwrap(); + + // Pool gets rewards. + system.advance_k_epochs_and_attest(:staker); + let mut expected_rewards_v3 = calculate_strk_pool_rewards_v2( + staker_address: staker.staker.address, + staking_contract: system.staking.address, + minting_curve_contract: system.minting_curve.address, + ); + + // Zero delegator intent. + system.delegator_exit_intent(:delegator, :pool, amount: Zero::zero()); + + // Pool gets rewards. + system.advance_k_epochs_and_attest(:staker); + expected_rewards_v3 += + calculate_strk_pool_rewards_v2( + staker_address: staker.staker.address, + staking_contract: system.staking.address, + minting_curve_contract: system.minting_curve.address, + ); + + // Test rewards. + system.advance_epoch(); + let expected_rewards = self.expected_rewards.unwrap() + expected_rewards_v3; + assert!( + wide_abs_diff( + system.delegator_unclaimed_rewards(:delegator, :pool), expected_rewards, + ) < 10, + ); + let actual_rewards = system.delegator_claim_rewards(:delegator, :pool); + let pool_balance = system.token.balance_of(account: pool); + assert!(pool_balance < 10); + assert_eq!(wide_abs_diff(actual_rewards, expected_rewards), pool_balance.into()); + assert_eq!( + system.token.balance_of(account: delegator.reward.address), actual_rewards.into(), + ); + } +} + +/// Flow: +/// Delegator enter in V0. +/// Attest in V1 (pool gets rewards). +/// Attest in V2 (pool gets rewards). +/// Attest in V3 (pool gets rewards). +/// test rewards with view function. +/// Delegator change balance in V3. +/// Attest in V3 (pool gets rewards). +/// Test delegator rewards. +#[derive(Drop, Copy)] +pub(crate) struct DelegatorFromV0Flow { + pub(crate) staker: Option, + pub(crate) pool: Option, + pub(crate) delegator: Option, + pub(crate) expected_rewards: Option, +} +pub(crate) impl DelegatorFromV0FlowImpl of FlowTrait { + fn setup(ref self: DelegatorFromV0Flow, ref system: SystemState) { + let amount = system.staking.get_min_stake(); + let staker = system.new_staker(:amount); + let commission = 200; + system.stake(:staker, :amount, pool_enabled: true, :commission); + let pool = system.staking.get_pool(:staker); + let delegator = system.new_delegator(:amount); + system.delegate(:delegator, :pool, :amount); + self.staker = Option::Some(staker); + self.pool = Option::Some(pool); + self.delegator = Option::Some(delegator); + + let one_week = Time::weeks(count: 1); + system.advance_time(time: one_week); + system.staking.update_global_index_if_needed(); + + let expected_rewards = system.delegator_unclaimed_rewards(:delegator, :pool); + self.expected_rewards = Option::Some(expected_rewards); + + system.set_pool_for_upgrade(pool_address: pool); + system.set_staker_for_migration(staker_address: staker.staker.address); + } + + fn setup_v1(ref self: DelegatorFromV0Flow, ref system: SystemState) { + let staker = self.staker.unwrap(); + + // Pool gets rewards. + system.advance_k_epochs_and_attest(:staker); + let expected_rewards_v1 = calculate_strk_pool_rewards_v1( + staker_address: staker.staker.address, + staking_contract: system.staking.address, + minting_curve_contract: system.minting_curve.address, + ); + + let expected_rewards = self.expected_rewards.unwrap() + expected_rewards_v1; + self.expected_rewards = Option::Some(expected_rewards); + } + + fn setup_v2(ref self: DelegatorFromV0Flow, ref system: SystemState) { + let staker = self.staker.unwrap(); + + // Pool gets rewards. + system.advance_k_epochs_and_attest(:staker); + let expected_rewards_v2 = calculate_strk_pool_rewards_v2( + staker_address: staker.staker.address, + staking_contract: system.staking.address, + minting_curve_contract: system.minting_curve.address, + ); + + let expected_rewards = self.expected_rewards.unwrap() + expected_rewards_v2; + self.expected_rewards = Option::Some(expected_rewards); + } + + fn test(self: DelegatorFromV0Flow, ref system: SystemState) { + let staker = self.staker.unwrap(); + let delegator = self.delegator.unwrap(); + let pool = self.pool.unwrap(); + + // Pool gets rewards. + system.advance_k_epochs_and_attest(:staker); + let expected_rewards_v3 = calculate_strk_pool_rewards_v2( + staker_address: staker.staker.address, + staking_contract: system.staking.address, + minting_curve_contract: system.minting_curve.address, + ); + + // Test rewards with view function. + system.advance_epoch(); + let mut expected_rewards = self.expected_rewards.unwrap() + expected_rewards_v3; + let unclaimed_rewards = system.delegator_unclaimed_rewards(:delegator, :pool); + assert_eq!(unclaimed_rewards, expected_rewards); + + // Delegator change balance. + let delegator_amount = system.pool_member_info_v1(:delegator, :pool).amount; + system.delegator_exit_intent(:delegator, :pool, amount: delegator_amount / 2); + + // Test rewards with view function. + let unclaimed_rewards = system.delegator_unclaimed_rewards(:delegator, :pool); + assert_eq!(unclaimed_rewards, expected_rewards); + + // Pool gets rewards. + system.advance_k_epochs_and_attest(:staker); + expected_rewards += + calculate_strk_pool_rewards_v2( + staker_address: staker.staker.address, + staking_contract: system.staking.address, + minting_curve_contract: system.minting_curve.address, + ); + + // Test delegator rewards. + system.advance_epoch(); + let actual_rewards = system.delegator_claim_rewards(:delegator, :pool); + let pool_balance = system.token.balance_of(account: pool); + assert!(pool_balance < 10); + assert_eq!(actual_rewards, expected_rewards); + assert_eq!( + system.token.balance_of(account: delegator.reward.address), actual_rewards.into(), + ); + } +} + +/// Flow: +/// Delegator enter in V0. +/// Delegator change balance in V3. +/// Attest in V3 (pool gets rewards). +/// Test delegator rewards. +#[derive(Drop, Copy)] +pub(crate) struct DelegatorV0ChangeBalanceBeforeRewardsFlow { + pub(crate) staker: Option, + pub(crate) pool: Option, + pub(crate) delegator: Option, +} +pub(crate) impl DelegatorV0ChangeBalanceBeforeRewardsFlowImpl of FlowTrait< + DelegatorV0ChangeBalanceBeforeRewardsFlow, +> { + fn setup(ref self: DelegatorV0ChangeBalanceBeforeRewardsFlow, ref system: SystemState) { + let amount = system.staking.get_min_stake(); + let staker = system.new_staker(:amount); + let commission = 200; + system.stake(:staker, :amount, pool_enabled: true, :commission); + let pool = system.staking.get_pool(:staker); + let delegator = system.new_delegator(:amount); + system.delegate(:delegator, :pool, :amount); + self.staker = Option::Some(staker); + self.pool = Option::Some(pool); + self.delegator = Option::Some(delegator); + + system.set_pool_for_upgrade(pool_address: pool); + system.set_staker_for_migration(staker_address: staker.staker.address); + } + + fn test(self: DelegatorV0ChangeBalanceBeforeRewardsFlow, ref system: SystemState) { + let staker = self.staker.unwrap(); + let delegator = self.delegator.unwrap(); + let pool = self.pool.unwrap(); + + // Delegator change balance. + let delegator_amount = system.pool_member_info_v1(:delegator, :pool).amount; + system.delegator_exit_intent(:delegator, :pool, amount: delegator_amount / 2); + + // Pool gets rewards. + system.advance_k_epochs_and_attest(:staker); + let expected_rewards = calculate_strk_pool_rewards_v2( + staker_address: staker.staker.address, + staking_contract: system.staking.address, + minting_curve_contract: system.minting_curve.address, + ); + + // Test delegator rewards. + system.advance_epoch(); + let unclaimed_rewards = system.delegator_unclaimed_rewards(:delegator, :pool); + let actual_rewards = system.delegator_claim_rewards(:delegator, :pool); + let pool_balance = system.token.balance_of(account: pool); + assert!(pool_balance < 10); + assert_eq!(unclaimed_rewards, actual_rewards); + assert_eq!(actual_rewards, expected_rewards); + assert_eq!( + system.token.balance_of(account: delegator.reward.address), actual_rewards.into(), + ); + } +} + +/// Flow: +/// Delegator enter in V0. +/// Attest in V1 (pool gets rewards). +/// Delegator change balance in V3. +/// Attest in V3 (pool gets rewards). +/// Test delegator rewards. +#[derive(Drop, Copy)] +pub(crate) struct DelegatorV0RewardsV1ChangeBalanceBeforeRewardsFlow { + pub(crate) staker: Option, + pub(crate) pool: Option, + pub(crate) delegator: Option, + pub(crate) expected_rewards: Option, +} +pub(crate) impl DelegatorV0RewardsV1ChangeBalanceBeforeRewardsFlowImpl of FlowTrait< + DelegatorV0RewardsV1ChangeBalanceBeforeRewardsFlow, +> { + fn setup( + ref self: DelegatorV0RewardsV1ChangeBalanceBeforeRewardsFlow, ref system: SystemState, + ) { + let amount = system.staking.get_min_stake(); + let staker = system.new_staker(:amount); + let commission = 200; + system.stake(:staker, :amount, pool_enabled: true, :commission); + let pool = system.staking.get_pool(:staker); + let delegator = system.new_delegator(:amount); + system.delegate(:delegator, :pool, :amount); + self.staker = Option::Some(staker); + self.pool = Option::Some(pool); + self.delegator = Option::Some(delegator); + + system.set_pool_for_upgrade(pool_address: pool); + system.set_staker_for_migration(staker_address: staker.staker.address); + } + + fn setup_v1( + ref self: DelegatorV0RewardsV1ChangeBalanceBeforeRewardsFlow, ref system: SystemState, + ) { + let staker = self.staker.unwrap(); + + // Pool gets rewards. + system.advance_k_epochs_and_attest(:staker); + let expected_rewards = calculate_strk_pool_rewards_v1( + staker_address: staker.staker.address, + staking_contract: system.staking.address, + minting_curve_contract: system.minting_curve.address, + ); + self.expected_rewards = Option::Some(expected_rewards); + } + + fn test(self: DelegatorV0RewardsV1ChangeBalanceBeforeRewardsFlow, ref system: SystemState) { + let staker = self.staker.unwrap(); + let delegator = self.delegator.unwrap(); + let pool = self.pool.unwrap(); + let expected_rewards_v1 = self.expected_rewards.unwrap(); + + // Delegator change balance. + let delegator_amount = system.pool_member_info_v1(:delegator, :pool).amount; + system.delegator_exit_intent(:delegator, :pool, amount: delegator_amount / 2); + + // Pool gets rewards. + system.advance_k_epochs_and_attest(:staker); + let expected_rewards = calculate_strk_pool_rewards_v2( + staker_address: staker.staker.address, + staking_contract: system.staking.address, + minting_curve_contract: system.minting_curve.address, + ); + + // Test delegator rewards. + system.advance_epoch(); + let unclaimed_rewards = system.delegator_unclaimed_rewards(:delegator, :pool); + let actual_rewards = system.delegator_claim_rewards(:delegator, :pool); + let pool_balance = system.token.balance_of(account: pool); + assert!(pool_balance < 10); + assert_eq!(unclaimed_rewards, actual_rewards); + assert_eq!(actual_rewards, expected_rewards_v1 + expected_rewards); + assert_eq!( + system.token.balance_of(account: delegator.reward.address), actual_rewards.into(), + ); + } +} diff --git a/src/flow_test/fork_test.cairo b/src/flow_test/fork_test.cairo index c54fcb7e..a0f451f7 100644 --- a/src/flow_test/fork_test.cairo +++ b/src/flow_test/fork_test.cairo @@ -452,3 +452,48 @@ fn toggle_tokens_before_after_upgrade_flow_test() { }; test_flow_mainnet(ref :flow); } + +#[test] +#[fork("MAINNET_LATEST")] +fn delegator_change_balance_over_versions_flow_test() { + let mut flow = flows::DelegatorChangeBalanceOverVersionsFlow { + staker: Option::None, + pool: Option::None, + delegator: Option::None, + expected_rewards: Option::None, + }; + test_flow_mainnet(ref :flow); +} + +#[test] +#[fork("MAINNET_LATEST")] +fn delegator_from_v0_flow_test() { + let mut flow = flows::DelegatorFromV0Flow { + staker: Option::None, + pool: Option::None, + delegator: Option::None, + expected_rewards: Option::None, + }; + test_flow_mainnet(ref :flow); +} + +#[test] +#[fork("MAINNET_LATEST")] +fn delegator_v0_change_balance_before_rewards_flow_test() { + let mut flow = flows::DelegatorV0ChangeBalanceBeforeRewardsFlow { + staker: Option::None, pool: Option::None, delegator: Option::None, + }; + test_flow_mainnet(ref :flow); +} + +#[test] +#[fork("MAINNET_LATEST")] +fn delegator_v0_rewards_v1_change_balance_before_rewards_flow_test() { + let mut flow = flows::DelegatorV0RewardsV1ChangeBalanceBeforeRewardsFlow { + staker: Option::None, + pool: Option::None, + delegator: Option::None, + expected_rewards: Option::None, + }; + test_flow_mainnet(ref :flow); +}