@@ -6,15 +6,15 @@ pub mod RewardSupplier {
66 use openzeppelin :: access :: accesscontrol :: AccessControlComponent ;
77 use openzeppelin :: introspection :: src5 :: SRC5Component ;
88 use openzeppelin :: token :: erc20 :: interface :: {IERC20Dispatcher , IERC20DispatcherTrait };
9- use staking :: constants :: {ALPHA , STRK_IN_FRIS , STRK_TOKEN_ADDRESS };
9+ use staking :: constants :: {ALPHA , SECONDS_IN_YEAR , STRK_IN_FRIS , STRK_TOKEN_ADDRESS };
1010 use staking :: errors :: {GenericError , InternalError };
1111 use staking :: minting_curve :: interface :: {IMintingCurveDispatcher , IMintingCurveDispatcherTrait };
1212 use staking :: reward_supplier :: errors :: Error ;
1313 use staking :: reward_supplier :: interface :: {Events , IRewardSupplier , RewardSupplierInfoV1 };
1414 use staking :: reward_supplier :: utils :: {calculate_btc_rewards, compute_threshold};
1515 use staking :: staking :: interface :: {IStakingDispatcher , IStakingDispatcherTrait };
1616 use staking :: staking :: objects :: EpochInfoTrait ;
17- use staking :: types :: Amount ;
17+ use staking :: types :: { Amount , BlockNumber } ;
1818 use starknet :: storage :: {StoragePointerReadAccess , StoragePointerWriteAccess };
1919 use starknet :: syscalls :: send_message_to_l1_syscall;
2020 use starknet :: {
@@ -25,9 +25,14 @@ pub mod RewardSupplier {
2525 use starkware_utils :: erc20 :: erc20_utils :: CheckedIERC20DispatcherTrait ;
2626 use starkware_utils :: errors :: OptionAuxTrait ;
2727 use starkware_utils :: interfaces :: identity :: Identity ;
28- use starkware_utils :: math :: utils :: ceil_of_division;
28+ use starkware_utils :: math :: utils :: {ceil_of_division, mul_wide_and_div};
29+ use starkware_utils :: time :: time :: Timestamp ;
2930 pub const CONTRACT_IDENTITY : felt252 = ' Reward Supplier' ;
3031 pub const CONTRACT_VERSION : felt252 = ' 3.0.0' ;
32+ /// Scale factor for block duration measurements. 100 implies granularity of 100th of second.
33+ pub (crate ) const BLOCK_DURATION_SCALE : u64 = 100 ;
34+ /// Default avg block duration.
35+ pub (crate ) const DEFAULT_AVG_BLOCK_DURATION : u64 = 3 * BLOCK_DURATION_SCALE ;
3136
3237 component! (path : ReplaceabilityComponent , storage : replaceability , event : ReplaceabilityEvent );
3338 component! (path : RolesComponent , storage : roles , event : RolesEvent );
@@ -71,6 +76,14 @@ pub mod RewardSupplier {
7176 l1_reward_supplier : felt252 ,
7277 /// Token bridge address.
7378 starkgate_address : ContractAddress ,
79+ /// Average block duration in units of 1 / BLOCK_TIME_SCALE seconds.
80+ // TODO: Initial in EIC.
81+ // TODO: Setter.
82+ // TODO: View?
83+ avg_block_duration : u64 ,
84+ /// The latest block data used for average block duration calculation.
85+ /// Updated at the start of each epoch.
86+ block_snapshot : (BlockNumber , Timestamp ),
7487 }
7588
7689 #[event]
@@ -109,6 +122,7 @@ pub mod RewardSupplier {
109122 self . minting_curve_dispatcher. contract_address. write (minting_curve_contract );
110123 self . l1_reward_supplier. write (l1_reward_supplier );
111124 self . starkgate_address. write (starkgate_address );
125+ self . avg_block_duration. write (DEFAULT_AVG_BLOCK_DURATION );
112126 }
113127
114128 #[abi(embed_v0)]
@@ -139,6 +153,30 @@ pub mod RewardSupplier {
139153 (strk_rewards , btc_rewards )
140154 }
141155
156+ // TODO: Emit event?
157+ fn update_current_epoch_block_rewards (ref self : ContractState ) -> (Amount , Amount ) {
158+ let staking_contract = self . staking_contract. read ();
159+ assert! (
160+ get_caller_address () == staking_contract ,
161+ " {}" ,
162+ GenericError :: CALLER_IS_NOT_STAKING_CONTRACT ,
163+ );
164+ self . set_avg_block_duration ();
165+ // Calculate block rewards for the current epoch.
166+ let minting_curve_dispatcher = self . minting_curve_dispatcher. read ();
167+ let yearly_mint = minting_curve_dispatcher . yearly_mint ();
168+ let avg_block_duration = self . avg_block_duration. read ();
169+ let total_rewards = mul_wide_and_div (
170+ lhs : yearly_mint ,
171+ rhs : avg_block_duration . into (),
172+ div : BLOCK_DURATION_SCALE . into () * SECONDS_IN_YEAR . into (),
173+ )
174+ . expect_with_err (err : InternalError :: REWARDS_COMPUTATION_OVERFLOW );
175+ let btc_rewards = calculate_btc_rewards (: total_rewards );
176+ let strk_rewards = total_rewards - btc_rewards ;
177+ (strk_rewards , btc_rewards )
178+ }
179+
142180 fn update_unclaimed_rewards_from_staking_contract (
143181 ref self : ContractState , rewards : Amount ,
144182 ) {
@@ -259,5 +297,43 @@ pub mod RewardSupplier {
259297 let to_address = self . l1_reward_supplier. read ();
260298 send_message_to_l1_syscall (: to_address , : payload ). unwrap_syscall ();
261299 }
300+
301+ fn set_avg_block_duration (ref self : ContractState ) {
302+ let current_block_number = starknet :: get_block_number ();
303+ let current_timestamp = starknet :: get_block_timestamp ();
304+ let (snapshot_block_number , snapshot_timestamp ) = self . block_snapshot. read ();
305+ // Sanity asserts.
306+ assert! (
307+ current_block_number > snapshot_block_number ,
308+ " {}" ,
309+ InternalError :: INVALID_BLOCK_NUMBER ,
310+ );
311+ assert! (
312+ current_timestamp > snapshot_timestamp . into (),
313+ " {}" ,
314+ InternalError :: INVALID_BLOCK_TIMESTAMP ,
315+ );
316+ self
317+ . block_snapshot
318+ . write ((current_block_number , Timestamp { seconds : current_timestamp }));
319+ // If this is the first time we're setting the block snapshot, can't calculate avg block
320+ // time yet.
321+ if snapshot_block_number . is_zero () || snapshot_timestamp . is_zero () {
322+ return ;
323+ }
324+ let time_delta = current_timestamp - snapshot_timestamp . into ();
325+ // *Note*: `num_blocks` should match the epoch length in blocks. This calculation is
326+ // expected to run on the first block of each epoch, assuming `update_rewards` is called
327+ // every block.
328+ // We calculate `num_blocks` instead of using the configured value to keep the average
329+ // accurate even if some calls are missed.
330+ let num_blocks = current_block_number - snapshot_block_number ;
331+ let calculated_block_duration = mul_wide_and_div (
332+ lhs : time_delta , rhs : BLOCK_DURATION_SCALE , div : num_blocks ,
333+ )
334+ . expect_with_err (err : Error :: BLOCK_DURATION_OVERFLOW );
335+ // TODO: Adjust calculated_block_duration with MIN_BLOCK_TIME and MAX_BLOCK_TIME.
336+ self . avg_block_duration. write (calculated_block_duration );
337+ }
262338 }
263339}
0 commit comments