mirror of
https://github.com/serai-dex/serai.git
synced 2025-12-08 12:19:24 +00:00
Restore AllowMint to serai-validator-sets-pallet and reorganize TODOs
This commit is contained in:
@@ -1,6 +1,9 @@
|
||||
use borsh::{BorshSerialize, BorshDeserialize};
|
||||
|
||||
use serai_primitives::network_id::ExternalNetworkId;
|
||||
use serai_primitives::{
|
||||
network_id::ExternalNetworkId,
|
||||
balance::{Amount, ExternalBalance},
|
||||
};
|
||||
|
||||
/// An event from economic security.
|
||||
#[derive(Clone, PartialEq, Eq, Debug, BorshSerialize, BorshDeserialize)]
|
||||
@@ -14,7 +17,10 @@ pub enum Event {
|
||||
|
||||
/// A trait representing access to the information on economic security.
|
||||
pub trait EconomicSecurity {
|
||||
/// If am external network has _ever_ achieved economic security.
|
||||
/// If an external network has _ever_ achieved economic security.
|
||||
#[must_use]
|
||||
fn achieved_economic_security(network: ExternalNetworkId) -> bool;
|
||||
|
||||
/// The security oracle's valuation of this balance in SRI.
|
||||
fn sri_value(balance: ExternalBalance) -> Amount;
|
||||
}
|
||||
|
||||
@@ -46,7 +46,8 @@ mod pallet {
|
||||
};
|
||||
|
||||
use serai_core_pallet::Pallet as Core;
|
||||
use serai_coins_pallet::Pallet as Coins;
|
||||
use serai_coins_pallet::AllowMint;
|
||||
type Coins<T> = serai_coins_pallet::Pallet<T, serai_coins_pallet::CoinsInstance>;
|
||||
|
||||
use super::*;
|
||||
|
||||
@@ -274,88 +275,28 @@ mod pallet {
|
||||
}
|
||||
}
|
||||
|
||||
/* TODO
|
||||
/// Decreases a validator's allocation to a set.
|
||||
///
|
||||
/// Errors if the capacity provided by this allocation is in use.
|
||||
///
|
||||
/// Errors if a partial decrease of allocation which puts the remaining allocation below the
|
||||
/// minimum requirement.
|
||||
///
|
||||
/// The capacity prior provided by the allocation is immediately removed, in order to ensure it
|
||||
/// doesn't become used (preventing deallocation).
|
||||
///
|
||||
/// Returns if the amount is immediately eligible for deallocation.
|
||||
fn decrease_allocation(
|
||||
network: NetworkId,
|
||||
account: T::AccountId,
|
||||
amount: Amount,
|
||||
) -> Result<bool, DispatchError> {
|
||||
// Check it's safe to decrease this set's stake by this amount
|
||||
if let NetworkId::External(n) = network {
|
||||
let new_total_staked = Self::total_allocated_stake(NetworkId::from(n))
|
||||
.unwrap()
|
||||
.0
|
||||
.checked_sub(amount.0)
|
||||
.ok_or(Error::<T>::NotEnoughAllocated)?;
|
||||
let required_stake = Self::required_stake_for_network(n);
|
||||
if new_total_staked < required_stake {
|
||||
Err(Error::<T>::DeallocationWouldRemoveEconomicSecurity)?;
|
||||
}
|
||||
/// The required amount of stake for a balance.
|
||||
fn stake_requirement(balance: ExternalBalance) -> AmountRepr {
|
||||
let value = T::EconomicSecurity::sri_value(balance).0;
|
||||
// As 67% can misbehave, 67% of stake must be sufficient to secure this
|
||||
let requirement = value.saturating_mul(3) / 2;
|
||||
// We add an additional margin of 20%
|
||||
let margin = requirement / 5;
|
||||
requirement.saturating_add(margin)
|
||||
}
|
||||
|
||||
let decreased_key_shares =
|
||||
(old_allocation / allocation_per_key_share) > (new_allocation / allocation_per_key_share);
|
||||
|
||||
// If this decreases the validator's key shares, error if the new set is unable to handle
|
||||
// byzantine faults
|
||||
let mut was_bft = None;
|
||||
if decreased_key_shares {
|
||||
was_bft = Some(Self::is_bft(network));
|
||||
}
|
||||
|
||||
if let Some(was_bft) = was_bft {
|
||||
if was_bft && (!Self::is_bft(network)) {
|
||||
Err(Error::<T>::DeallocationWouldRemoveFaultTolerance)?;
|
||||
}
|
||||
}
|
||||
|
||||
Sessions::<T>::decrease_allocation(network, account, amount)
|
||||
}
|
||||
|
||||
/// Returns the required stake in terms SRI for a given `Balance`.
|
||||
pub fn required_stake(balance: &ExternalBalance) -> SubstrateAmount {
|
||||
use dex_pallet::HigherPrecisionBalance;
|
||||
|
||||
// This is inclusive to an increase in accuracy
|
||||
let sri_per_coin = Dex::<T>::security_oracle_value(balance.coin).unwrap_or(Amount(0));
|
||||
|
||||
// See dex-pallet for the reasoning on these
|
||||
let coin_decimals = balance.coin.decimals().max(5);
|
||||
let accuracy_increase = HigherPrecisionBalance::from(SubstrateAmount::pow(10, coin_decimals));
|
||||
|
||||
let total_coin_value = u64::try_from(
|
||||
HigherPrecisionBalance::from(balance.amount.0) *
|
||||
HigherPrecisionBalance::from(sri_per_coin.0) /
|
||||
accuracy_increase,
|
||||
)
|
||||
.unwrap_or(u64::MAX);
|
||||
|
||||
// required stake formula (COIN_VALUE * 1.5) + margin(20%)
|
||||
let required_stake = total_coin_value.saturating_mul(3).saturating_div(2);
|
||||
required_stake.saturating_add(total_coin_value.saturating_div(5))
|
||||
}
|
||||
|
||||
/// Returns the current total required stake for a given `network`.
|
||||
pub fn required_stake_for_network(network: ExternalNetworkId) -> SubstrateAmount {
|
||||
let mut total_required = 0;
|
||||
/// The required amount of stake for a network.
|
||||
fn network_stake_requirement(network: ExternalNetworkId) -> AmountRepr {
|
||||
let mut requirement = AmountRepr::zero();
|
||||
for coin in network.coins() {
|
||||
let supply = Coins::<T>::supply(Coin::from(coin));
|
||||
total_required += Self::required_stake(&ExternalBalance { coin, amount: Amount(supply) });
|
||||
requirement = requirement
|
||||
.saturating_add(Self::stake_requirement(ExternalBalance { coin, amount: supply }));
|
||||
}
|
||||
total_required
|
||||
requirement
|
||||
}
|
||||
|
||||
/* TODO
|
||||
pub fn distribute_block_rewards(
|
||||
network: NetworkId,
|
||||
account: T::AccountId,
|
||||
@@ -532,11 +473,7 @@ mod pallet {
|
||||
#[pallet::weight((0, DispatchClass::Normal))] // TODO
|
||||
pub fn allocate(origin: OriginFor<T>, network: NetworkId, amount: Amount) -> DispatchResult {
|
||||
let validator = ensure_signed(origin)?;
|
||||
Coins::<T, serai_coins_pallet::CoinsInstance>::transfer_fn(
|
||||
validator,
|
||||
Self::account(),
|
||||
Balance { coin: Coin::Serai, amount },
|
||||
)?;
|
||||
Coins::<T>::transfer_fn(validator, Self::account(), Balance { coin: Coin::Serai, amount })?;
|
||||
Abstractions::<T>::increase_allocation(network, validator, amount, false)
|
||||
.map_err(Error::<T>::AllocationError)?;
|
||||
Ok(())
|
||||
@@ -558,11 +495,7 @@ mod pallet {
|
||||
});
|
||||
|
||||
if matches!(timeline, DeallocationTimeline::Immediate) {
|
||||
Coins::<T, serai_coins_pallet::CoinsInstance>::transfer_fn(
|
||||
Self::account(),
|
||||
validator,
|
||||
Balance { coin: Coin::Serai, amount },
|
||||
)?;
|
||||
Coins::<T>::transfer_fn(Self::account(), validator, Balance { coin: Coin::Serai, amount })?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@@ -584,11 +517,7 @@ mod pallet {
|
||||
deallocation: ValidatorSet { network, session },
|
||||
});
|
||||
|
||||
Coins::<T, serai_coins_pallet::CoinsInstance>::transfer_fn(
|
||||
Self::account(),
|
||||
validator,
|
||||
Balance { coin: Coin::Serai, amount },
|
||||
)?;
|
||||
Coins::<T>::transfer_fn(Self::account(), validator, Balance { coin: Coin::Serai, amount })?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -693,20 +622,20 @@ mod pallet {
|
||||
}
|
||||
}
|
||||
|
||||
/* TODO
|
||||
/*
|
||||
TODO: Add an intent. While we shouldn't allow `Transfer`, `AddLiquidity` when we're within a
|
||||
certain range of the limit, we should still allow swaps.
|
||||
*/
|
||||
impl<T: Config> AllowMint for Pallet<T> {
|
||||
fn is_allowed(balance: &ExternalBalance) -> bool {
|
||||
// get the required stake
|
||||
let current_required = Self::required_stake_for_network(balance.coin.network());
|
||||
let new_required = current_required + Self::required_stake(balance);
|
||||
|
||||
// get the total stake for the network & compare.
|
||||
let current_requirement = Self::network_stake_requirement(balance.coin.network());
|
||||
let new_requirement = current_requirement.saturating_add(Self::stake_requirement(*balance));
|
||||
let staked =
|
||||
Self::total_allocated_stake(NetworkId::from(balance.coin.network())).unwrap_or(Amount(0));
|
||||
staked.0 >= new_required
|
||||
Abstractions::<T>::stake_for_current_validator_set(balance.coin.network().into())
|
||||
.unwrap_or(Amount(0));
|
||||
staked.0 >= new_requirement
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
||||
pub use pallet::*;
|
||||
|
||||
|
||||
@@ -494,6 +494,9 @@ impl<Storage: SessionsStorage> Sessions for Storage {
|
||||
validator: Public,
|
||||
amount: Amount,
|
||||
) -> Result<DeallocationTimeline, DeallocationError> {
|
||||
// TODO: Check if this would introduce a single point of failure
|
||||
// TODO: Check if this would violate economic security
|
||||
|
||||
/*
|
||||
Decrease the allocation.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user