Skip to content

Commit 27d5709

Browse files
refactor fuse
1 parent 17aff4a commit 27d5709

1 file changed

Lines changed: 137 additions & 68 deletions

File tree

Lines changed: 137 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -1,107 +1,176 @@
11
use crate::traits::AssetDepositLimiter;
22
use crate::types::LockdownStatus;
3-
use crate::{AssetLockdownState, Config, Event, Pallet};
3+
use crate::{AssetLockdownState, Config, Pallet};
44
use frame_support::traits::Get;
55
use frame_system::pallet_prelude::BlockNumberFor;
66
use orml_traits::currency::OnDeposit;
7-
use orml_traits::{GetByKey, Handler, Happened};
7+
use orml_traits::GetByKey;
88
use sp_runtime::{SaturatedConversion, Saturating};
99
use sp_std::marker::PhantomData;
1010

11-
//TODO: check every he usage of saturaring
11+
#[derive(Debug)]
12+
enum DepositAction<Balance> {
13+
InitialDeposit,
14+
LockdownActive,
15+
LockdownExpired,
16+
PeriodExpired,
17+
WithinPeriod { last_issuance: Balance },
18+
}
1219

13-
//TODO: add prop tests, also for save deposit, so only the claimed and specified amount is returned
20+
struct DepositContext<T: Config> {
21+
action: DepositAction<T::Balance>,
22+
limit: T::Balance,
23+
lockdown_until: BlockNumberFor<T>,
24+
asset_issuance: T::Balance,
25+
}
1426

15-
//TODO: integration tests
16-
// when other parachain is hacked, we should be able to lock down the asset issuance
17-
// sending VDOT to us and exchange it
18-
// Other parachain can send us VDOT, we exchange it for DOT and lock down the issuance
19-
// example: byfrist xcm transfer to our asset crossing, so we should mint it, but we should not alllow
20-
// Other test: replicate the problem we had last week, where you could mint any amount of sharetoken in stablepool
21-
// --- we set limit for sharetoken in asset registry, when add liquidty, but when this cross this, it should reserve
27+
pub struct IssuanceIncreaseFuse<T: Config>(PhantomData<T>);
2228

23-
//TODO: SET GLOBAL LIMIT TO 1 DAY
29+
impl<T: Config> IssuanceIncreaseFuse<T> {
30+
/// Check if the given amount can be minted for the asset
31+
pub fn can_mint(currency_id: T::AssetId, amount: T::Balance) -> bool {
32+
let Some(context) = Self::get_context(currency_id) else {
33+
return true;
34+
};
2435

25-
pub struct IssuanceIncreaseFuse<T: Config>(PhantomData<T>);
36+
match &context.action {
37+
DepositAction::LockdownActive => false,
38+
DepositAction::InitialDeposit | DepositAction::LockdownExpired | DepositAction::PeriodExpired => {
39+
amount <= context.limit
40+
}
41+
DepositAction::WithinPeriod { last_issuance } => {
42+
let issuance_increase_in_period = context.asset_issuance.saturating_sub(*last_issuance);
43+
issuance_increase_in_period <= context.limit
44+
}
45+
}
46+
}
2647

27-
impl<T: Config> OnDeposit<T::AccountId, T::AssetId, T::Balance> for IssuanceIncreaseFuse<T> {
28-
fn on_deposit(currency_id: T::AssetId, who: &T::AccountId, amount: T::Balance) -> sp_runtime::DispatchResult {
48+
fn get_context(currency_id: T::AssetId) -> Option<DepositContext<T>> {
2949
let period = <T::DepositLimiter as AssetDepositLimiter<T::AccountId, T::AssetId, T::Balance>>::Period::get();
3050
if period == 0u128 {
31-
// no limit
32-
return Ok(());
51+
return None;
3352
}
3453

3554
let Some(limit) =
3655
<T::DepositLimiter as AssetDepositLimiter<T::AccountId, T::AssetId, T::Balance>>::DepositLimit::get(
3756
&currency_id,
3857
)
3958
else {
40-
return Ok(());
59+
return None;
4160
};
4261

4362
let current_block = <frame_system::Pallet<T>>::block_number();
44-
let lockdown_until: BlockNumberFor<T> = current_block.saturating_add(period.saturated_into());
63+
let lockdown_until = current_block.saturating_add(period.saturated_into());
4564
let asset_issuance =
4665
<T::DepositLimiter as AssetDepositLimiter<T::AccountId, T::AssetId, T::Balance>>::Issuance::get(
4766
&currency_id,
4867
);
4968

50-
match AssetLockdownState::<T>::get(currency_id) {
51-
None => {
52-
// This happens only once - to set the initial state
53-
// Still check if this new deposit does not exceed the limit
54-
// Set the issuance without this amount because we want the amount to count towards the limit per period.
55-
if amount > limit {
56-
let to_lock = amount.saturating_sub(limit);
57-
Pallet::<T>::do_lock_deposit(&who, currency_id, to_lock)?;
58-
Pallet::<T>::do_lockdown_asset(currency_id, lockdown_until)?;
59-
} else {
60-
Pallet::<T>::do_reset_deposit_limits(currency_id, amount)?;
61-
}
62-
}
63-
Some(LockdownStatus::Locked(until)) if until > current_block => {
64-
// Asset in lockdown
65-
Pallet::<T>::do_lock_deposit(&who, currency_id, amount)?;
66-
}
67-
Some(LockdownStatus::Locked(_)) => {
68-
// Lockdown expired
69-
// Check if this new deposit does not exceed the limit and lock it down again if it does.
70-
if amount > limit {
71-
let to_lock = amount.saturating_sub(limit);
72-
Pallet::<T>::do_lock_deposit(&who, currency_id, to_lock)?;
73-
Pallet::<T>::do_lockdown_asset(currency_id, lockdown_until)?;
69+
let state = AssetLockdownState::<T>::get(currency_id);
70+
let action = Self::classify_state(state, current_block, period);
71+
72+
Some(DepositContext {
73+
action,
74+
limit,
75+
lockdown_until,
76+
asset_issuance,
77+
})
78+
}
79+
80+
fn classify_state(
81+
state: Option<LockdownStatus<BlockNumberFor<T>, T::Balance>>,
82+
current_block: BlockNumberFor<T>,
83+
period: u128,
84+
) -> DepositAction<T::Balance> {
85+
match state {
86+
None => DepositAction::InitialDeposit,
87+
Some(LockdownStatus::Locked(until)) if until > current_block => DepositAction::LockdownActive,
88+
Some(LockdownStatus::Locked(_)) => DepositAction::LockdownExpired,
89+
Some(LockdownStatus::Unlocked((last_reset_at, last_issuance))) => {
90+
if last_reset_at.saturating_add(period.saturated_into()) <= current_block {
91+
DepositAction::PeriodExpired
7492
} else {
75-
Pallet::<T>::do_lift_lockdown(currency_id, amount)?;
93+
DepositAction::WithinPeriod { last_issuance }
7694
}
7795
}
78-
Some(LockdownStatus::Unlocked((last_reset_at, _)))
79-
if last_reset_at.saturating_add(period.saturated_into()) <= current_block =>
80-
{
81-
// The period is over, so we can reset the limit.
82-
// But first, we must check if this new deposit does not exceed the limit.
83-
if amount > limit {
84-
let to_lock = amount.saturating_sub(limit);
85-
Pallet::<T>::do_lock_deposit(&who, currency_id, to_lock)?;
86-
Pallet::<T>::do_lockdown_asset(currency_id, lockdown_until)?;
87-
} else {
88-
Pallet::<T>::do_reset_deposit_limits(currency_id, amount)?;
89-
}
96+
}
97+
}
98+
99+
fn process_deposit_action(
100+
context: &DepositContext<T>,
101+
who: &T::AccountId,
102+
currency_id: T::AssetId,
103+
amount: T::Balance,
104+
) -> sp_runtime::DispatchResult {
105+
match &context.action {
106+
DepositAction::InitialDeposit | DepositAction::PeriodExpired => {
107+
Self::handle_limit_reset(context, who, currency_id, amount)
90108
}
91-
Some(LockdownStatus::Unlocked((_, last_issuance))) => {
92-
// If the period is not over, we check the limit by comparing issuance increase.
93-
let issuance_increase_in_period = asset_issuance.saturating_sub(last_issuance);
94-
if issuance_increase_in_period > limit {
95-
// We should lock only the excess, not all new deposit
96-
// Formula: to_lock = current_issuance - (last_issuance + limit)
97-
let to_lock = asset_issuance.saturating_sub(last_issuance.saturating_add(limit));
98-
debug_assert!(to_lock <= amount);
99-
Pallet::<T>::do_lock_deposit(&who, currency_id, to_lock)?;
100-
Pallet::<T>::do_lockdown_asset(currency_id, lockdown_until)?;
101-
}
109+
DepositAction::LockdownExpired => Self::handle_lockdown_expired(context, who, currency_id, amount),
110+
DepositAction::LockdownActive => Pallet::<T>::do_lock_deposit(who, currency_id, amount),
111+
DepositAction::WithinPeriod { last_issuance } => {
112+
Self::handle_within_period(context, who, currency_id, amount, *last_issuance)
102113
}
103-
};
114+
}
115+
}
104116

117+
fn handle_limit_reset(
118+
context: &DepositContext<T>,
119+
who: &T::AccountId,
120+
currency_id: T::AssetId,
121+
amount: T::Balance,
122+
) -> sp_runtime::DispatchResult {
123+
if amount > context.limit {
124+
let to_lock = amount.saturating_sub(context.limit);
125+
Pallet::<T>::do_lock_deposit(who, currency_id, to_lock)?;
126+
Pallet::<T>::do_lockdown_asset(currency_id, context.lockdown_until)?;
127+
} else {
128+
Pallet::<T>::do_reset_deposit_limits(currency_id, amount)?;
129+
}
130+
Ok(())
131+
}
132+
133+
fn handle_lockdown_expired(
134+
context: &DepositContext<T>,
135+
who: &T::AccountId,
136+
currency_id: T::AssetId,
137+
amount: T::Balance,
138+
) -> sp_runtime::DispatchResult {
139+
if amount > context.limit {
140+
let to_lock = amount.saturating_sub(context.limit);
141+
Pallet::<T>::do_lock_deposit(who, currency_id, to_lock)?;
142+
Pallet::<T>::do_lockdown_asset(currency_id, context.lockdown_until)?;
143+
} else {
144+
Pallet::<T>::do_lift_lockdown(currency_id, amount)?;
145+
}
105146
Ok(())
106147
}
148+
149+
fn handle_within_period(
150+
context: &DepositContext<T>,
151+
who: &T::AccountId,
152+
currency_id: T::AssetId,
153+
_amount: T::Balance,
154+
last_issuance: T::Balance,
155+
) -> sp_runtime::DispatchResult {
156+
let issuance_increase_in_period = context.asset_issuance.saturating_sub(last_issuance);
157+
if issuance_increase_in_period > context.limit {
158+
let to_lock = context
159+
.asset_issuance
160+
.saturating_sub(last_issuance.saturating_add(context.limit));
161+
Pallet::<T>::do_lock_deposit(who, currency_id, to_lock)?;
162+
Pallet::<T>::do_lockdown_asset(currency_id, context.lockdown_until)?;
163+
}
164+
Ok(())
165+
}
166+
}
167+
168+
impl<T: Config> OnDeposit<T::AccountId, T::AssetId, T::Balance> for IssuanceIncreaseFuse<T> {
169+
fn on_deposit(currency_id: T::AssetId, who: &T::AccountId, amount: T::Balance) -> sp_runtime::DispatchResult {
170+
let Some(context) = Self::get_context(currency_id) else {
171+
return Ok(());
172+
};
173+
174+
Self::process_deposit_action(&context, who, currency_id, amount)
175+
}
107176
}

0 commit comments

Comments
 (0)