Skip to content

Commit b29228c

Browse files
committed
feat(staking): calculate block rewards by timestamp
1 parent 6fda82c commit b29228c

File tree

5 files changed

+72
-60
lines changed

5 files changed

+72
-60
lines changed

src/flow_test/flow_ideas.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,3 +49,8 @@ more ideas:
4949
- enable token, update rewards, advance epoch, update rewards, advance epoch, update rewards - token does not get rewards until after 2 epochs
5050
- same as above with disable (can be implemented together as one test)
5151
- enable token A and disable token B, next epoch upgrade, test views and rewards.
52+
53+
## block rewards by timestamp
54+
- advance blocks with different block times and check the avg is calculated correctly
55+
- update_rewards for blocks in same epoch - same rewards, then advance epoch, different rewards, update rewards for blocks in same epoch - same rewards.
56+
- update rewards is not called every block, still rewards is updated correctly (miss block, miss first block in epoch, miss epoch)

src/flow_test/test.cairo

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2720,11 +2720,7 @@ fn update_rewards_transition_from_attestation_to_consensus_flow_test() {
27202720
system.update_rewards(:staker, disable_rewards: false);
27212721
let rewards = system.staker_claim_rewards(:staker);
27222722
let (expected_rewards_v3, _) = calculate_staker_strk_rewards_with_balances_v3(
2723-
amount_own: stake_amount,
2724-
pool_amount: Zero::zero(),
2725-
:commission,
2726-
:staking_contract,
2727-
:minting_curve_contract,
2723+
amount_own: stake_amount, pool_amount: Zero::zero(), :commission, :minting_curve_contract,
27282724
);
27292725
assert!(expected_rewards_v3.is_non_zero());
27302726
assert!(rewards == expected_rewards_v3);
@@ -2873,7 +2869,6 @@ fn update_rewards_disable_rewards_consensus_rewards_flow_test() {
28732869
amount_own: stake_amount,
28742870
pool_amount: Zero::zero(),
28752871
:commission,
2876-
staking_contract: system.staking.address,
28772872
minting_curve_contract: system.minting_curve.address,
28782873
);
28792874
assert!(expected_rewards.is_non_zero());
@@ -2920,7 +2915,6 @@ fn update_rewards_strk_pool_flow_test() {
29202915
amount_own: stake_amount,
29212916
pool_amount: delegation_amount,
29222917
:commission,
2923-
staking_contract: system.staking.address,
29242918
minting_curve_contract: system.minting_curve.address,
29252919
);
29262920
let staker_rewards = system.staker_claim_rewards(:staker);

src/staking/staking.cairo

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,10 @@ pub mod Staking {
188188
pool_eic_class_hash: ClassHash,
189189
/// Map staker address to its version.
190190
staker_version: Map<ContractAddress, StakerVersion>,
191+
/// Last epoch for which block rewards were calculated.
192+
last_calculated_epoch: Epoch,
193+
/// Block rewards (STRK, BTC) for the current epoch.
194+
block_rewards: (Amount, Amount),
191195
}
192196

193197
#[event]
@@ -1431,7 +1435,7 @@ pub mod Staking {
14311435
// Get current block data and update rewards.
14321436
let reward_supplier_dispatcher = self.reward_supplier_dispatcher.read();
14331437
let (strk_block_rewards, btc_block_rewards) = self
1434-
.calculate_block_rewards(:reward_supplier_dispatcher);
1438+
.calculate_block_rewards(:reward_supplier_dispatcher, :curr_epoch);
14351439
self
14361440
._update_rewards(
14371441
:staker_address,
@@ -1490,13 +1494,24 @@ pub mod Staking {
14901494
}
14911495

14921496
/// Calculates the rewards for a block in the current epoch (for STRK and BTC).
1497+
///
1498+
/// Precondition: `curr_epoch` must be `get_current_epoch()`, it's passed as a param to save
1499+
/// storage reads.
1500+
// TODO: Consider view?
1501+
// TODO: Migration.
14931502
fn calculate_block_rewards(
1494-
self: @ContractState, reward_supplier_dispatcher: IRewardSupplierDispatcher,
1503+
ref self: ContractState,
1504+
reward_supplier_dispatcher: IRewardSupplierDispatcher,
1505+
curr_epoch: Epoch,
14951506
) -> (Amount, Amount) {
1496-
let (strk_rewards, btc_rewards) = reward_supplier_dispatcher
1497-
.calculate_current_epoch_rewards();
1498-
let epoch_len_in_blocks = self.get_epoch_info().epoch_len_in_blocks();
1499-
(strk_rewards / epoch_len_in_blocks.into(), btc_rewards / epoch_len_in_blocks.into())
1507+
if curr_epoch > self.last_calculated_epoch.read() {
1508+
self.last_calculated_epoch.write(curr_epoch);
1509+
let block_rewards = reward_supplier_dispatcher.update_current_epoch_block_rewards();
1510+
self.block_rewards.write(block_rewards);
1511+
block_rewards
1512+
} else {
1513+
self.block_rewards.read()
1514+
}
15001515
}
15011516

15021517
fn update_commission(

src/staking/tests/test.cairo

Lines changed: 15 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -86,16 +86,17 @@ use starkware_utils_testing::test_utils::{
8686
};
8787
use test_utils::{
8888
StakingInitConfig, advance_block_into_attestation_window, advance_epoch_global,
89-
advance_k_epochs_global, approve, calculate_staker_btc_pool_rewards_v2,
90-
calculate_staker_btc_pool_rewards_v3, calculate_staker_strk_rewards_v2,
91-
calculate_staker_strk_rewards_with_balances_v3, cheat_target_attestation_block_hash, constants,
92-
custom_decimals_token, declare_pool_contract, declare_pool_eic_contract,
93-
declare_staking_contract, declare_staking_eic_contract, deploy_mock_erc20_decimals_contract,
94-
deploy_staking_contract, enter_delegation_pool_for_testing_using_dispatcher, fund,
95-
general_contract_system_deployment, load_from_simple_map, load_one_felt, pause_staking_contract,
96-
setup_btc_token, stake_for_testing_using_dispatcher, stake_from_zero_address,
97-
stake_with_strk_pool_enabled, store_internal_staker_info_v0_to_map, store_to_simple_map,
98-
to_amount_18_decimals, upgrade_implementation,
89+
advance_k_epochs_global, approve, calculate_current_block_rewards_v3,
90+
calculate_staker_btc_pool_rewards_v2, calculate_staker_btc_pool_rewards_v3,
91+
calculate_staker_strk_rewards_v2, calculate_staker_strk_rewards_with_balances_v3,
92+
cheat_target_attestation_block_hash, constants, custom_decimals_token, declare_pool_contract,
93+
declare_pool_eic_contract, declare_staking_contract, declare_staking_eic_contract,
94+
deploy_mock_erc20_decimals_contract, deploy_staking_contract,
95+
enter_delegation_pool_for_testing_using_dispatcher, fund, general_contract_system_deployment,
96+
load_from_simple_map, load_one_felt, pause_staking_contract, setup_btc_token,
97+
stake_for_testing_using_dispatcher, stake_from_zero_address, stake_with_strk_pool_enabled,
98+
store_internal_staker_info_v0_to_map, store_to_simple_map, to_amount_18_decimals,
99+
upgrade_implementation,
99100
};
100101

101102
#[test]
@@ -3490,10 +3491,7 @@ fn test_update_rewards_only_staker() {
34903491
contract_address: staking_contract,
34913492
};
34923493
let staking_config_dispatcher = IStakingConfigDispatcher { contract_address: staking_contract };
3493-
let reward_supplier = cfg.staking_contract_info.reward_supplier;
3494-
let reward_supplier_dispatcher = IRewardSupplierDispatcher {
3495-
contract_address: reward_supplier,
3496-
};
3494+
let minting_curve_contract = cfg.reward_supplier.minting_curve_contract;
34973495
let current_epoch = staking_dispatcher.get_current_epoch();
34983496
cheat_caller_address_once(
34993497
contract_address: staking_contract, caller_address: cfg.test_info.app_governor,
@@ -3505,9 +3503,7 @@ fn test_update_rewards_only_staker() {
35053503
advance_k_epochs_global();
35063504
let staker_address = cfg.test_info.staker_address;
35073505
let staker_info_before = staking_dispatcher.staker_info_v1(:staker_address);
3508-
let (strk_epoch_rewards, _) = reward_supplier_dispatcher.calculate_current_epoch_rewards();
3509-
let epoch_len_in_blocks = cfg.staking_contract_info.epoch_info.epoch_len_in_blocks();
3510-
let strk_block_rewards = strk_epoch_rewards / epoch_len_in_blocks.into();
3506+
let (strk_block_rewards, _) = calculate_current_block_rewards_v3(:minting_curve_contract);
35113507
let staker_info_expected = StakerInfoV1 {
35123508
unclaimed_rewards_own: strk_block_rewards, ..staker_info_before,
35133509
};
@@ -3536,10 +3532,7 @@ fn test_update_rewards_miss_blocks() {
35363532
contract_address: staking_contract,
35373533
};
35383534
let staking_config_dispatcher = IStakingConfigDispatcher { contract_address: staking_contract };
3539-
let reward_supplier = cfg.staking_contract_info.reward_supplier;
3540-
let reward_supplier_dispatcher = IRewardSupplierDispatcher {
3541-
contract_address: reward_supplier,
3542-
};
3535+
let minting_curve_contract = cfg.reward_supplier.minting_curve_contract;
35433536
let current_epoch = staking_dispatcher.get_current_epoch();
35443537
cheat_caller_address_once(
35453538
contract_address: staking_contract, caller_address: cfg.test_info.app_governor,
@@ -3551,9 +3544,7 @@ fn test_update_rewards_miss_blocks() {
35513544
advance_k_epochs_global();
35523545
let staker_address = cfg.test_info.staker_address;
35533546
let staker_info_before = staking_dispatcher.staker_info_v1(:staker_address);
3554-
let (strk_epoch_rewards, _) = reward_supplier_dispatcher.calculate_current_epoch_rewards();
3555-
let epoch_len_in_blocks = cfg.staking_contract_info.epoch_info.epoch_len_in_blocks();
3556-
let strk_block_rewards = strk_epoch_rewards / epoch_len_in_blocks.into();
3547+
let (strk_block_rewards, _) = calculate_current_block_rewards_v3(:minting_curve_contract);
35573548
let staker_info_expected = StakerInfoV1 {
35583549
unclaimed_rewards_own: strk_block_rewards * 2, ..staker_info_before,
35593550
};
@@ -3603,7 +3594,6 @@ fn test_update_rewards_with_strk_pool() {
36033594
amount_own: stake_amount,
36043595
pool_amount: delegated_amount,
36053596
:commission,
3606-
:staking_contract,
36073597
:minting_curve_contract,
36083598
);
36093599
// Assert staker rewards, delegator rewards, and pool balance before update.
@@ -3733,7 +3723,6 @@ fn test_update_rewards_with_both_strk_and_btc() {
37333723
amount_own: stake_amount,
37343724
pool_amount: strk_delegated_amount,
37353725
:commission,
3736-
:staking_contract,
37373726
:minting_curve_contract,
37383727
);
37393728
// Same calculation for both BTC pools (both have the same decimals).
@@ -3748,7 +3737,6 @@ fn test_update_rewards_with_both_strk_and_btc() {
37483737
:normalized_pool_balance,
37493738
:normalized_staker_total_btc_balance,
37503739
:commission,
3751-
:staking_contract,
37523740
:minting_curve_contract,
37533741
token_address: btc_token_address,
37543742
);

src/test_utils.cairo

Lines changed: 30 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ use snforge_std::{
2727
};
2828
use staking::attestation::attestation::Attestation::MIN_ATTESTATION_WINDOW;
2929
use staking::attestation::interface::{IAttestationDispatcher, IAttestationDispatcherTrait};
30-
use staking::constants::{K, STARTING_EPOCH, STRK_IN_FRIS, STRK_TOKEN_ADDRESS};
30+
use staking::constants::{K, SECONDS_IN_YEAR, STARTING_EPOCH, STRK_IN_FRIS, STRK_TOKEN_ADDRESS};
3131
use staking::errors::InternalError;
3232
use staking::minting_curve::interface::{
3333
IMintingCurveConfigDispatcher, IMintingCurveConfigDispatcherTrait, IMintingCurveDispatcher,
@@ -40,6 +40,7 @@ use staking::pool::pool::Pool;
4040
use staking::pool::pool_member_balance_trace::trace::PoolMemberCheckpointTrait;
4141
use staking::pool::utils::compute_rewards_rounded_down;
4242
use staking::reward_supplier::reward_supplier::RewardSupplier;
43+
use staking::reward_supplier::reward_supplier::RewardSupplier::BLOCK_DURATION_SCALE;
4344
use staking::reward_supplier::utils::calculate_btc_rewards;
4445
use staking::staking::interface::{
4546
IStakingDispatcher, IStakingDispatcherTrait, IStakingPauseDispatcher,
@@ -1121,12 +1122,9 @@ pub(crate) fn calculate_staker_strk_rewards_with_balances_v3(
11211122
amount_own: Amount,
11221123
pool_amount: Amount,
11231124
commission: Commission,
1124-
staking_contract: ContractAddress,
11251125
minting_curve_contract: ContractAddress,
11261126
) -> (Amount, Amount) {
1127-
let (strk_block_rewards, _) = _calculate_current_block_rewards_v3(
1128-
:staking_contract, :minting_curve_contract,
1129-
);
1127+
let (strk_block_rewards, _) = calculate_current_block_rewards_v3(:minting_curve_contract);
11301128
let total_stake = amount_own + pool_amount;
11311129
// Calculate staker own rewards.
11321130
let mut staker_rewards = mul_wide_and_div(
@@ -1166,7 +1164,6 @@ pub(crate) fn calculate_strk_pool_rewards_v3(
11661164
amount_own: staker_info.amount_own,
11671165
pool_amount: pool_info.amount,
11681166
commission: pool_info.commission,
1169-
:staking_contract,
11701167
:minting_curve_contract,
11711168
);
11721169
pool_rewards
@@ -1180,13 +1177,10 @@ pub(crate) fn calculate_staker_btc_pool_rewards_v3(
11801177
normalized_pool_balance: NormalizedAmount,
11811178
normalized_staker_total_btc_balance: NormalizedAmount,
11821179
commission: Commission,
1183-
staking_contract: ContractAddress,
11841180
minting_curve_contract: ContractAddress,
11851181
token_address: ContractAddress,
11861182
) -> (Amount, Amount) {
1187-
let (_, btc_block_rewards) = _calculate_current_block_rewards_v3(
1188-
:staking_contract, :minting_curve_contract,
1189-
);
1183+
let (_, btc_block_rewards) = calculate_current_block_rewards_v3(:minting_curve_contract);
11901184
// Calculate pool rewards including commission.
11911185
let pool_rewards_including_commission = mul_wide_and_div(
11921186
lhs: btc_block_rewards,
@@ -1203,17 +1197,33 @@ pub(crate) fn calculate_staker_btc_pool_rewards_v3(
12031197
}
12041198

12051199

1206-
fn _calculate_current_block_rewards_v3(
1207-
staking_contract: ContractAddress, minting_curve_contract: ContractAddress,
1200+
/// Calculate block rewards for the current epoch. Use `AVG_BLOCK_TIME` (default value for testing)
1201+
/// for `avg_block_time`.
1202+
pub(crate) fn calculate_current_block_rewards_v3(
1203+
minting_curve_contract: ContractAddress,
12081204
) -> (Amount, Amount) {
1209-
let (strk_epoch_rewards, btc_epoch_rewards) = _calculate_current_epoch_rewards_v2(
1210-
:staking_contract, :minting_curve_contract,
1211-
);
1212-
let staking_dispatcher = IStakingDispatcher { contract_address: staking_contract };
1213-
let epoch_len_in_blocks = staking_dispatcher.get_epoch_info().epoch_len_in_blocks();
1214-
let strk_block_rewards = strk_epoch_rewards / epoch_len_in_blocks.into();
1215-
let btc_block_rewards = btc_epoch_rewards / epoch_len_in_blocks.into();
1216-
(strk_block_rewards, btc_block_rewards)
1205+
calculate_current_block_rewards_with_avg_block_time_v3(
1206+
:minting_curve_contract, avg_block_time: AVG_BLOCK_DURATION * BLOCK_DURATION_SCALE,
1207+
)
1208+
}
1209+
1210+
/// Calculate block rewards for the current epoch based on the given `avg_block_time`.
1211+
pub(crate) fn calculate_current_block_rewards_with_avg_block_time_v3(
1212+
minting_curve_contract: ContractAddress, avg_block_time: u64,
1213+
) -> (Amount, Amount) {
1214+
let minting_curve_dispatcher = IMintingCurveDispatcher {
1215+
contract_address: minting_curve_contract,
1216+
};
1217+
let yearly_mint = minting_curve_dispatcher.yearly_mint();
1218+
let total_rewards = mul_wide_and_div(
1219+
lhs: yearly_mint,
1220+
rhs: avg_block_time.into(),
1221+
div: BLOCK_DURATION_SCALE.into() * SECONDS_IN_YEAR.into(),
1222+
)
1223+
.expect_with_err(err: InternalError::REWARDS_COMPUTATION_OVERFLOW);
1224+
let btc_rewards = calculate_btc_rewards(:total_rewards);
1225+
let strk_rewards = total_rewards - btc_rewards;
1226+
(strk_rewards, btc_rewards)
12171227
}
12181228

12191229
// ---- Calculate Rewards - Helpers -----

0 commit comments

Comments
 (0)