Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 43 additions & 0 deletions docs/spec.md
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,8 @@
- [contract\_parameters\_v1](#contract_parameters_v1-2)
- [on\_receive](#on_receive)
- [get\_alpha](#get_alpha)
- [get\_block\_duration\_config](#get_block_duration_config)
- [set\_block\_duration\_config](#set_block_duration_config)
- [Events](#events-2)
- [Mint Request](#mint-request)
- [Minting Curve Contract](#minting-curve-contract)
Expand Down Expand Up @@ -237,6 +239,7 @@
- [CONSENSUS\_REWARDS\_IS\_ACTIVE](#consensus_rewards_is_active)
- [INVALID\_STAKER](#invalid_staker)
- [INVALID\_TOKEN\_DECIMALS](#invalid_token_decimals)
- [INVALID\_MIN\_MAX\_BLOCK\_DURATION](#invalid_min_max_block_duration)
- [Structs](#structs)
- [StakerPoolInfoV1](#stakerpoolinfov1)
- [StakerInfoV1](#stakerinfov1)
Expand All @@ -254,6 +257,7 @@
- [AttestationInfo](#attestationinfo)
- [EpochInfo](#epochinfo)
- [MintingCurveContractInfo](#mintingcurvecontractinfo)
- [BlockDurationConfig](#blockdurationconfig)
- [Type aliases](#type-aliases)
- [Amount](#amount)
- [Commission](#commission)
Expand Down Expand Up @@ -2344,6 +2348,36 @@ Returns the alpha parameter, as percentage, used when computing BTC rewards.
#### access control <!-- omit from toc -->
Any address can execute.

### get_block_duration_config
```rust
fn get_block_duration_config(self: @TContractState) -> BlockdurationConfig;
```
#### description <!-- omit from toc -->
Returns [BlockdurationConfig](#blockdurationconfig).
#### emits <!-- omit from toc -->
#### errors <!-- omit from toc -->
#### pre-condition <!-- omit from toc -->
#### logic <!-- omit from toc -->
#### access control <!-- omit from toc -->
Any address can execute.

### get_block_duration_config
```rust
fn set_block_duration_config(ref self: TContractState, block_duration_config: BlockdurationConfig);
```
#### description <!-- omit from toc -->
Set the block duration configuration.
#### emits <!-- omit from toc -->
#### errors <!-- omit from toc -->
1. [ONLY\_APP\_GOVERNOR](#only_app_governor)
2. [INVALID\_MIN\_MAX\_BLOCK\_duration](#invalid_min_max_block_duration)
#### pre-condition <!-- omit from toc -->
1. 0 < `block_duration_config.weighted_avg_factor` <= 100
2. 0 < `block_duration_config.min_block_duration` <= `block_duration_config.max_block_duration`
#### logic <!-- omit from toc -->
#### access control <!-- omit from toc -->
Only app governor.

## Events
### Mint Request
| data | type | keyed |
Expand Down Expand Up @@ -2797,6 +2831,9 @@ Only token admin.
### INVALID_STAKER
"Staker is invalid for getting rewards"

### INVALID_MIN_MAX_BLOCK_DURATION
"Invalid min/max block duration"

# Structs
### StakerPoolInfoV1
| name | type |
Expand Down Expand Up @@ -2918,6 +2955,12 @@ Only token admin.
| c_num | Inflation |
| c_denom | Inflation |

### BlockDurationConfig
| name | type |
| ------------------- | ---- |
| min_block_duration | u64 |
| max_block_duration | u64 |

# Type aliases
### Amount
Amount: u128
Expand Down
2 changes: 2 additions & 0 deletions src/reward_supplier/errors.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ pub enum Error {
ON_RECEIVE_NOT_FROM_STARKGATE,
UNEXPECTED_TOKEN,
BLOCK_DURATION_OVERFLOW,
INVALID_MIN_MAX_BLOCK_DURATION,
}

impl DescribableError of Describable<Error> {
Expand All @@ -13,6 +14,7 @@ impl DescribableError of Describable<Error> {
Error::ON_RECEIVE_NOT_FROM_STARKGATE => "Only StarkGate can call on_receive",
Error::UNEXPECTED_TOKEN => "Unexpected token",
Error::BLOCK_DURATION_OVERFLOW => "Block duration calculation overflow",
Error::INVALID_MIN_MAX_BLOCK_DURATION => "Invalid min/max block duration",
}
}
}
31 changes: 31 additions & 0 deletions src/reward_supplier/interface.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,28 @@ pub trait IRewardSupplier<TContractState> {
fn contract_parameters_v1(self: @TContractState) -> RewardSupplierInfoV1;
/// Returns the alpha parameter, as percentage, used when computing BTC rewards.
fn get_alpha(self: @TContractState) -> u128;
/// Returns the block duration configuration.
fn get_block_duration_config(self: @TContractState) -> BlockDurationConfig;
}

#[starknet::interface]
pub trait IRewardSupplierConfig<TContractState> {
/// Sets the block duration configuration.
///
/// #### Preconditions:
/// - `block_duration_config.min_block_duration > 0`
/// - `block_duration_config.min_block_duration <= block_duration_config.max_block_duration`
///
/// #### Errors:
/// - [`ONLY_APP_GOVERNOR`](AccessErrors::ONLY_APP_GOVERNOR)
/// -
/// [`INVALID_MIN_MAX_BLOCK_DURATION`](staking::reward_supplier::errors::Error::INVALID_MIN_MAX_BLOCK_DURATION)
///
/// #### Access control:
/// Only app governor.
fn set_block_duration_config(
ref self: TContractState, block_duration_config: BlockDurationConfig,
);
}

pub mod Events {
Expand All @@ -95,3 +117,12 @@ pub struct RewardSupplierInfoV1 {
pub unclaimed_rewards: Amount,
pub l1_pending_requested_amount: Amount,
}

/// Configuration for block duration calculation.
#[derive(Debug, Copy, Drop, Serde, PartialEq, starknet::Store)]
pub struct BlockDurationConfig {
/// Minimum block duration, in units of 1 / BLOCK_DURATION_SCALE seconds.
pub min_block_duration: u64,
/// Maximum block duration, in units of 1 / BLOCK_DURATION_SCALE seconds.
pub max_block_duration: u64,
}
43 changes: 41 additions & 2 deletions src/reward_supplier/reward_supplier.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@ pub mod RewardSupplier {
use staking::errors::{GenericError, InternalError};
use staking::minting_curve::interface::{IMintingCurveDispatcher, IMintingCurveDispatcherTrait};
use staking::reward_supplier::errors::Error;
use staking::reward_supplier::interface::{Events, IRewardSupplier, RewardSupplierInfoV1};
use staking::reward_supplier::interface::{
BlockDurationConfig, Events, IRewardSupplier, IRewardSupplierConfig, RewardSupplierInfoV1,
};
use staking::reward_supplier::utils::{calculate_btc_rewards, compute_threshold};
use staking::staking::interface::{IStakingDispatcher, IStakingDispatcherTrait};
use staking::staking::objects::EpochInfoTrait;
Expand All @@ -33,6 +35,10 @@ pub mod RewardSupplier {
pub(crate) const BLOCK_DURATION_SCALE: u64 = 100;
/// Default avg block duration.
pub(crate) const DEFAULT_AVG_BLOCK_DURATION: u64 = 3 * BLOCK_DURATION_SCALE;
/// Default block duration configuration.
pub(crate) const DEFAULT_BLOCK_DURATION_CONFIG: BlockDurationConfig = BlockDurationConfig {
min_block_duration: 2 * BLOCK_DURATION_SCALE, max_block_duration: 5 * BLOCK_DURATION_SCALE,
};

component!(path: ReplaceabilityComponent, storage: replaceability, event: ReplaceabilityEvent);
component!(path: RolesComponent, storage: roles, event: RolesEvent);
Expand Down Expand Up @@ -76,14 +82,17 @@ pub mod RewardSupplier {
l1_reward_supplier: felt252,
/// Token bridge address.
starkgate_address: ContractAddress,
/// Average block duration in units of 1 / BLOCK_TIME_SCALE seconds.
/// Average block duration in units of 1 / BLOCK_DURATION_SCALE seconds.
// TODO: Initial in EIC.
// TODO: Setter.
// TODO: View?
avg_block_duration: u64,
/// The latest block data used for average block duration calculation.
/// Updated at the start of each epoch.
block_snapshot: (BlockNumber, Timestamp),
/// Configuration for block duration calculation.
// TODO: Initial in EIC.
block_duration_config: BlockDurationConfig,
}

#[event]
Expand Down Expand Up @@ -123,6 +132,7 @@ pub mod RewardSupplier {
self.l1_reward_supplier.write(l1_reward_supplier);
self.starkgate_address.write(starkgate_address);
self.avg_block_duration.write(DEFAULT_AVG_BLOCK_DURATION);
self.block_duration_config.write(DEFAULT_BLOCK_DURATION_CONFIG);
}

#[abi(embed_v0)]
Expand Down Expand Up @@ -255,6 +265,35 @@ pub mod RewardSupplier {
fn get_alpha(self: @ContractState) -> u128 {
ALPHA
}

fn get_block_duration_config(self: @ContractState) -> BlockDurationConfig {
self.block_duration_config.read()
}
}

#[abi(embed_v0)]
impl RewardSupplierConfigImpl of IRewardSupplierConfig<ContractState> {
fn set_block_duration_config(
ref self: ContractState, block_duration_config: BlockDurationConfig,
) {
self.roles.only_app_governor();
// TODO: Emit event?
// Assert that block_time_config is valid.
// TODO: More validations?
assert!(
block_duration_config.min_block_duration > 0,
"{}",
Error::INVALID_MIN_MAX_BLOCK_DURATION,
);
assert!(
block_duration_config
.min_block_duration <= block_duration_config
.max_block_duration,
"{}",
Error::INVALID_MIN_MAX_BLOCK_DURATION,
);
self.block_duration_config.write(block_duration_config);
}
}

#[generate_trait]
Expand Down
76 changes: 72 additions & 4 deletions src/reward_supplier/test.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,17 @@ use snforge_std::{
use staking::constants::{ALPHA, ALPHA_DENOMINATOR, SECONDS_IN_YEAR, STRK_IN_FRIS};
use staking::errors::{GenericError, InternalError};
use staking::minting_curve::interface::{IMintingCurveDispatcher, IMintingCurveDispatcherTrait};
use staking::reward_supplier::errors::Error;
use staking::reward_supplier::interface::{
IRewardSupplier, IRewardSupplierDispatcher, IRewardSupplierDispatcherTrait,
IRewardSupplierSafeDispatcher, IRewardSupplierSafeDispatcherTrait, RewardSupplierInfoV1,
BlockDurationConfig, IRewardSupplier, IRewardSupplierConfigDispatcher,
IRewardSupplierConfigDispatcherTrait, IRewardSupplierConfigSafeDispatcher,
IRewardSupplierConfigSafeDispatcherTrait, IRewardSupplierDispatcher,
IRewardSupplierDispatcherTrait, IRewardSupplierSafeDispatcher,
IRewardSupplierSafeDispatcherTrait, RewardSupplierInfoV1,
};
use staking::reward_supplier::reward_supplier::RewardSupplier;
use staking::reward_supplier::reward_supplier::RewardSupplier::{
BLOCK_DURATION_SCALE, DEFAULT_AVG_BLOCK_DURATION,
BLOCK_DURATION_SCALE, DEFAULT_AVG_BLOCK_DURATION, DEFAULT_BLOCK_DURATION_CONFIG,
};
use staking::reward_supplier::utils::compute_threshold;
use staking::staking::interface::{IStakingDispatcher, IStakingDispatcherTrait};
Expand All @@ -41,7 +45,6 @@ use test_utils::{
stake_for_testing_using_dispatcher,
};


#[test]
fn test_identity() {
assert!(reward_supplier_identity == 'Reward Supplier');
Expand Down Expand Up @@ -534,3 +537,68 @@ fn test_update_current_epoch_block_rewards_assertions() {
:result, expected_error: InternalError::INVALID_BLOCK_TIMESTAMP.describe(),
);
}

#[test]
fn test_get_block_duration_config() {
let mut cfg: StakingInitConfig = Default::default();
general_contract_system_deployment(ref :cfg);
let reward_supplier = cfg.staking_contract_info.reward_supplier;
let reward_supplier_dispatcher = IRewardSupplierDispatcher {
contract_address: reward_supplier,
};
let block_duration_config = reward_supplier_dispatcher.get_block_duration_config();
assert!(block_duration_config == DEFAULT_BLOCK_DURATION_CONFIG);
}

#[test]
fn test_set_block_duration_config() {
let mut cfg: StakingInitConfig = Default::default();
general_contract_system_deployment(ref :cfg);
let reward_supplier = cfg.staking_contract_info.reward_supplier;
let reward_supplier_dispatcher = IRewardSupplierDispatcher {
contract_address: reward_supplier,
};
let reward_supplier_config_dispatcher = IRewardSupplierConfigDispatcher {
contract_address: reward_supplier,
};
let app_governor = cfg.test_info.app_governor;
let block_duration_config = BlockDurationConfig {
min_block_duration: 90, max_block_duration: 350,
};
assert!(reward_supplier_dispatcher.get_block_duration_config() != block_duration_config);
cheat_caller_address_once(contract_address: reward_supplier, caller_address: app_governor);
reward_supplier_config_dispatcher.set_block_duration_config(:block_duration_config);
assert!(reward_supplier_dispatcher.get_block_duration_config() == block_duration_config);
}

#[test]
#[feature("safe_dispatcher")]
fn test_set_block_duration_config_assertions() {
let mut cfg: StakingInitConfig = Default::default();
general_contract_system_deployment(ref :cfg);
let reward_supplier = cfg.staking_contract_info.reward_supplier;
let reward_supplier_config_safe_dispatcher = IRewardSupplierConfigSafeDispatcher {
contract_address: reward_supplier,
};
let app_governor = cfg.test_info.app_governor;
let mut block_duration_config = DEFAULT_BLOCK_DURATION_CONFIG;
// Catch ONLY_APP_GOVERNOR.
let result = reward_supplier_config_safe_dispatcher
.set_block_duration_config(:block_duration_config);
assert_panic_with_error(:result, expected_error: "ONLY_APP_GOVERNOR");
// Catch INVALID_MIN_MAX_BLOCK_DURATION.
block_duration_config.min_block_duration = block_duration_config.max_block_duration + 1;
cheat_caller_address_once(contract_address: reward_supplier, caller_address: app_governor);
let result = reward_supplier_config_safe_dispatcher
.set_block_duration_config(:block_duration_config);
assert_panic_with_error(
:result, expected_error: Error::INVALID_MIN_MAX_BLOCK_DURATION.describe(),
);
block_duration_config.min_block_duration = 0;
cheat_caller_address_once(contract_address: reward_supplier, caller_address: app_governor);
let result = reward_supplier_config_safe_dispatcher
.set_block_duration_config(:block_duration_config);
assert_panic_with_error(
:result, expected_error: Error::INVALID_MIN_MAX_BLOCK_DURATION.describe(),
);
}
10 changes: 10 additions & 0 deletions src/test_utils.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -383,6 +383,16 @@ pub(crate) fn deploy_reward_supplier_contract(cfg: StakingInitConfig) -> Contrac
cfg.test_info.governance_admin.serialize(ref calldata);
let reward_supplier_contract = snforge_std::declare("RewardSupplier").unwrap().contract_class();
let (reward_supplier_contract_address, _) = reward_supplier_contract.deploy(@calldata).unwrap();
set_account_as_app_role_admin(
contract: reward_supplier_contract_address,
account: cfg.test_info.app_role_admin,
governance_admin: cfg.test_info.governance_admin,
);
set_account_as_app_governor(
contract: reward_supplier_contract_address,
account: cfg.test_info.app_governor,
app_role_admin: cfg.test_info.app_role_admin,
);
reward_supplier_contract_address
}

Expand Down
Loading