Skip to content

Commit

Permalink
Improve deposit (#160)
Browse files Browse the repository at this point in the history
* Improve deposit

* Test more cases

* Fix tests
  • Loading branch information
AurevoirXavier authored Dec 26, 2022
1 parent 5c1e385 commit 5c4a9c0
Show file tree
Hide file tree
Showing 10 changed files with 228 additions and 45 deletions.
112 changes: 92 additions & 20 deletions pallet/deposit/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,13 +55,16 @@ use sp_runtime::traits::AccountIdConversion;
/// Milliseconds per month.
pub const MILLISECS_PER_MONTH: Moment = MILLISECS_PER_YEAR / 12;

/// Asset's minting API.
pub trait Minting {
/// Simple asset APIs.
pub trait SimpleAsset {
/// Account type.
type AccountId;

/// Mint API.
fn mint(beneficiary: &Self::AccountId, amount: Balance) -> DispatchResult;

/// Burn API.
fn burn(who: &Self::AccountId, amount: Balance) -> DispatchResult;
}

/// Deposit identifier.
Expand All @@ -79,6 +82,8 @@ pub struct Deposit {
pub id: DepositId,
/// Deposited RING.
pub value: Balance,
/// Start timestamp.
pub start_time: Moment,
/// Expired timestamp.
pub expired_time: Moment,
/// Deposit state.
Expand All @@ -102,7 +107,7 @@ pub mod pallet {
type Ring: Currency<Self::AccountId, Balance = Balance>;

/// KTON asset.
type Kton: Minting<AccountId = Self::AccountId>;
type Kton: SimpleAsset<AccountId = Self::AccountId>;

/// Minimum amount to lock at least.
#[pallet::constant]
Expand All @@ -115,10 +120,28 @@ pub mod pallet {
type MaxDeposits: Get<u32>;
}

#[allow(missing_docs)]
#[pallet::event]
// TODO: event?
// #[pallet::generate_deposit(pub(super) fn deposit_event)]
pub enum Event<T: Config> {}
#[pallet::generate_deposit(pub(super) fn deposit_event)]
pub enum Event<T: Config> {
/// A new deposit has been created.
DepositCreated {
owner: T::AccountId,
deposit_id: DepositId,
value: Balance,
start_time: Moment,
expired_time: Moment,
kton_reward: Balance,
},
/// An expired deposit has been claimed.
DepositClaimed { owner: T::AccountId, deposit_id: DepositId },
/// An unexpired deposit has been claimed by paying the KTON penalty.
DepositClaimedWithPenalty {
owner: T::AccountId,
deposit_id: DepositId,
kton_penalty: Balance,
},
}

#[pallet::error]
pub enum Error<T> {
Expand All @@ -136,6 +159,8 @@ pub mod pallet {
DepositInUse,
/// Deposit is not in use.
DepositNotInUse,
/// Deposit is already expired.
DepositAlreadyExpired,
}

/// All deposits.
Expand Down Expand Up @@ -169,7 +194,7 @@ pub mod pallet {
Err(<Error<T>>::ExceedMaxDeposits)?;
}

<Deposits<T>>::try_mutate(&who, |ds| {
let (deposit_id, start_time, expired_time) = <Deposits<T>>::try_mutate(&who, |ds| {
let ds = if let Some(ds) = ds {
ds
} else {
Expand All @@ -190,25 +215,32 @@ pub mod pallet {
Continue(c) => c,
Break(b) => b,
};
let start_time = T::UnixTime::now().as_millis();
let expired_time = start_time + MILLISECS_PER_MONTH * months as Moment;

ds.try_insert(
id as _,
Deposit {
id,
value: amount,
expired_time: T::UnixTime::now().as_millis()
+ MILLISECS_PER_MONTH * months as Moment,
in_use: false,
},
Deposit { id, value: amount, start_time, expired_time, in_use: false },
)
.map_err(|_| <Error<T>>::ExceedMaxDeposits)?;

DispatchResult::Ok(())
<Result<_, DispatchError>>::Ok((id, start_time, expired_time))
})?;

T::Ring::transfer(&who, &account_id(), amount, KeepAlive)?;
T::Kton::mint(&who, dc_inflation::deposit_interest(amount, months))?;

// TODO: event?
let kton_reward = dc_inflation::deposit_interest(amount, months);

T::Kton::mint(&who, kton_reward)?;

Self::deposit_event(Event::DepositCreated {
owner: who,
deposit_id,
value: amount,
start_time,
expired_time,
kton_reward,
});

Ok(())
}
Expand All @@ -226,6 +258,11 @@ pub mod pallet {
if d.expired_time <= now && !d.in_use {
claimed += d.value;

Self::deposit_event(Event::DepositClaimed {
owner: who.clone(),
deposit_id: d.id,
});

false
} else {
true
Expand All @@ -243,12 +280,47 @@ pub mod pallet {

T::Ring::transfer(&account_id(), &who, claimed, AllowDeath)?;

// TODO: event?

Ok(())
}

// TODO: claim_with_penalty
/// Claim the unexpired-locked RING by paying the KTON penalty.
#[pallet::weight(0)]
pub fn claim_with_penalty(origin: OriginFor<T>, id: DepositId) -> DispatchResult {
let who = ensure_signed(origin)?;
let d = <Deposits<T>>::try_mutate(&who, |maybe_ds| {
let ds = maybe_ds.as_mut().ok_or(<Error<T>>::DepositNotFound)?;
let d = ds
.remove(ds.iter().position(|d| d.id == id).ok_or(<Error<T>>::DepositNotFound)?);

if ds.is_empty() {
<frame_system::Pallet<T>>::dec_consumers(&who);

*maybe_ds = None;
}

<Result<_, DispatchError>>::Ok(d)
})?;
let now = T::UnixTime::now().as_millis();

if d.expired_time <= now {
Err(<Error<T>>::DepositAlreadyExpired)?;
}

let promise_m = (d.expired_time - d.start_time) / MILLISECS_PER_MONTH;
let elapsed_m = (now - d.start_time) / MILLISECS_PER_MONTH;
let kton_penalty = dc_inflation::deposit_interest(d.value, promise_m as _)
.saturating_sub(dc_inflation::deposit_interest(d.value, elapsed_m as _))
.max(1) * 3;

T::Kton::burn(&who, kton_penalty)?;
Self::deposit_event(Event::DepositClaimedWithPenalty {
owner: who,
deposit_id: id,
kton_penalty,
});

Ok(())
}
}
}
pub use pallet::*;
Expand Down
17 changes: 14 additions & 3 deletions pallet/deposit/tests/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -82,16 +82,27 @@ impl pallet_assets::Config for Runtime {
type WeightInfo = ();
}

pub enum KtonMinting {}
impl darwinia_deposit::Minting for KtonMinting {
pub enum KtonAsset {}
impl darwinia_deposit::SimpleAsset for KtonAsset {
type AccountId = u32;

fn mint(beneficiary: &Self::AccountId, amount: Balance) -> sp_runtime::DispatchResult {
Assets::mint(RuntimeOrigin::signed(0), 0, *beneficiary, amount)
}

fn burn(
who: &Self::AccountId,
amount: Balance,
) -> sp_runtime::DispatchResult {
if Assets::balance(0, who) < amount {
Err(<pallet_assets::Error<Runtime>>::BalanceLow)?;
}

Assets::burn(RuntimeOrigin::signed(0), 0, *who, amount)
}
}
impl darwinia_deposit::Config for Runtime {
type Kton = KtonMinting;
type Kton = KtonAsset;
type MaxDeposits = frame_support::traits::ConstU32<16>;
type MinLockingAmount = frame_support::traits::ConstU128<UNIT>;
type Ring = Balances;
Expand Down
53 changes: 52 additions & 1 deletion pallet/deposit/tests/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,28 +64,38 @@ fn unique_identity_should_work() {
assert_eq!(
Deposit::deposit_of(&1).unwrap().as_slice(),
&[
DepositS { id: 0, value: UNIT, expired_time: MILLISECS_PER_MONTH, in_use: false },
DepositS {
id: 0,
value: UNIT,
start_time: 0,
expired_time: MILLISECS_PER_MONTH,
in_use: false
},
DepositS {
id: 1,
value: 2 * UNIT,
start_time: 0,
expired_time: 2 * MILLISECS_PER_MONTH,
in_use: false
},
DepositS {
id: 2,
value: 3 * UNIT,
start_time: 0,
expired_time: MILLISECS_PER_MONTH,
in_use: false
},
DepositS {
id: 3,
value: 4 * UNIT,
start_time: 0,
expired_time: 2 * MILLISECS_PER_MONTH,
in_use: false
},
DepositS {
id: 4,
value: 5 * UNIT,
start_time: 0,
expired_time: MILLISECS_PER_MONTH,
in_use: false
}
Expand All @@ -102,18 +112,21 @@ fn unique_identity_should_work() {
DepositS {
id: 0,
value: 6 * UNIT,
start_time: MILLISECS_PER_MONTH,
expired_time: 2 * MILLISECS_PER_MONTH,
in_use: false
},
DepositS {
id: 1,
value: 2 * UNIT,
start_time: 0,
expired_time: 2 * MILLISECS_PER_MONTH,
in_use: false
},
DepositS {
id: 3,
value: 4 * UNIT,
start_time: 0,
expired_time: 2 * MILLISECS_PER_MONTH,
in_use: false
},
Expand All @@ -127,24 +140,28 @@ fn unique_identity_should_work() {
DepositS {
id: 0,
value: 6 * UNIT,
start_time: MILLISECS_PER_MONTH,
expired_time: 2 * MILLISECS_PER_MONTH,
in_use: false
},
DepositS {
id: 1,
value: 2 * UNIT,
start_time: 0,
expired_time: 2 * MILLISECS_PER_MONTH,
in_use: false
},
DepositS {
id: 2,
value: 7 * UNIT,
start_time: MILLISECS_PER_MONTH,
expired_time: 2 * MILLISECS_PER_MONTH,
in_use: false
},
DepositS {
id: 3,
value: 4 * UNIT,
start_time: 0,
expired_time: 2 * MILLISECS_PER_MONTH,
in_use: false
},
Expand All @@ -158,30 +175,35 @@ fn unique_identity_should_work() {
DepositS {
id: 0,
value: 6 * UNIT,
start_time: MILLISECS_PER_MONTH,
expired_time: 2 * MILLISECS_PER_MONTH,
in_use: false
},
DepositS {
id: 1,
value: 2 * UNIT,
start_time: 0,
expired_time: 2 * MILLISECS_PER_MONTH,
in_use: false
},
DepositS {
id: 2,
value: 7 * UNIT,
start_time: MILLISECS_PER_MONTH,
expired_time: 2 * MILLISECS_PER_MONTH,
in_use: false
},
DepositS {
id: 3,
value: 4 * UNIT,
start_time: 0,
expired_time: 2 * MILLISECS_PER_MONTH,
in_use: false
},
DepositS {
id: 4,
value: 8 * UNIT,
start_time: MILLISECS_PER_MONTH,
expired_time: 2 * MILLISECS_PER_MONTH,
in_use: false
},
Expand All @@ -203,6 +225,7 @@ fn expire_time_should_work() {
.map(|i| DepositS {
id: i - 1,
value: UNIT,
start_time: (i - 1) as Moment * MILLISECS_PER_MONTH,
expired_time: i as Moment * MILLISECS_PER_MONTH,
in_use: false
})
Expand Down Expand Up @@ -282,3 +305,31 @@ fn claim_should_work() {
assert!(Deposit::deposit_of(&1).is_none());
});
}

#[test]
fn claim_with_penalty_should_work() {
new_test_ext().execute_with(|| {
assert!(Deposit::deposit_of(&1).is_none());
assert_ok!(Deposit::lock(RuntimeOrigin::signed(1), UNIT, 1));
assert!(Deposit::deposit_of(&1).is_some());

assert_noop!(
Deposit::claim_with_penalty(RuntimeOrigin::signed(1), 0),
<pallet_assets::Error<Runtime>>::BalanceLow
);

assert_ok!(KtonAsset::mint(&1, UNIT));
assert_ok!(Deposit::claim_with_penalty(RuntimeOrigin::signed(1), 0));
assert_eq!(Assets::balance(0, 1), 999_984_771_573_604_062);
assert!(Deposit::deposit_of(&1).is_none());

assert_ok!(Deposit::lock(RuntimeOrigin::signed(1), UNIT, 1));
efflux(MILLISECS_PER_MONTH);
assert!(Deposit::deposit_of(&1).is_some());

assert_noop!(
Deposit::claim_with_penalty(RuntimeOrigin::signed(1), 0),
<Error<Runtime>>::DepositAlreadyExpired
);
});
}
Loading

0 comments on commit 5c4a9c0

Please sign in to comment.