Skip to content

Commit

Permalink
unit tests done
Browse files Browse the repository at this point in the history
  • Loading branch information
herryho committed Oct 9, 2021
1 parent ac78d5a commit 47bd581
Show file tree
Hide file tree
Showing 10 changed files with 809 additions and 4 deletions.
21 changes: 21 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ members = [
"pallets/vtoken-mint",
"pallets/vtoken-mint/rpc",
"pallets/token-issuer",
"pallets/lightening-redeem",
"runtime/asgard",
"runtime/bifrost",
"utils/subkey",
Expand Down
47 changes: 47 additions & 0 deletions pallets/lightening-redeem/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
[package]
name = "bifrost-lightening-redeem"
version = "0.8.0"
authors = ["Herry Ho <herry.heyi@gmail.com>"]
edition = "2018"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
codec = { package = "parity-scale-codec", version = "2.0.0", default-features = false, features = ["derive"] }
serde = { version = "1.0.124", optional = true }
orml-traits = { version = "0.4.1-dev", default-features = false }
orml-tokens = { version = "0.4.1-dev", default-features = false }
node-primitives = { path = "../../node/primitives", default-features = false }
frame-benchmarking = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.9", default-features = false, optional = true }
frame-support = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.9", default-features = false }
frame-system = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.9", default-features = false }
sp-arithmetic = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.9", default-features = false }
sp-std = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.9", default-features = false }
sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.9", default-features = false }
pallet-collective = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.9", default-features = false }
sp-core = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.9", default-features = false }
sp-io = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.9", default-features = false }

[features]
default = ["std"]
std = [
"codec/std",
"frame-system/std",
"frame-support/std",
"sp-runtime/std",
"sp-arithmetic/std",
"serde/std",
"orml-traits/std",
"orml-tokens/std",
"node-primitives/std",
"sp-core/std",
"sp-io/std",
"sp-std/std",
"pallet-collective/std",
]

runtime-benchmarks = [
"frame-benchmarking",
"frame-support/runtime-benchmarks",
"frame-system/runtime-benchmarks",
]
277 changes: 277 additions & 0 deletions pallets/lightening-redeem/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,277 @@
// This file is part of Bifrost.

// Copyright (C) 2019-2021 Liebi Technologies (UK) Ltd.
// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0

// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.

#![cfg_attr(not(feature = "std"), no_std)]

use frame_support::{pallet_prelude::*, PalletId};
use frame_system::pallet_prelude::*;
use node_primitives::{CurrencyId, TokenSymbol};
use orml_traits::MultiCurrency;
use sp_arithmetic::per_things::Percent;
use sp_runtime::traits::{AccountIdConversion, Saturating, UniqueSaturatedFrom, Zero};
pub use weights::WeightInfo;

mod mock;
mod tests;
pub mod weights;

// #[cfg(feature = "runtime-benchmarks")]
// mod benchmarking;

pub use pallet::*;

type AccountIdOf<T> = <T as frame_system::Config>::AccountId;
type BalanceOf<T> = <<T as Config>::MultiCurrency as MultiCurrency<AccountIdOf<T>>>::Balance;

const TRILLION: u128 = 1_000_000_000_000;
// These time units are defined in number of blocks.
const BLOCKS_PER_DAY: u32 = 60 / 12 * 60 * 24;

#[frame_support::pallet]
pub mod pallet {
use super::*;

#[pallet::config]
pub trait Config: frame_system::Config {
type Event: From<Event<Self>> + IsType<<Self as frame_system::Config>::Event>;
/// Currency operations handler
type MultiCurrency: MultiCurrency<AccountIdOf<Self>, CurrencyId = CurrencyId>;
/// The only origin that can modify bootstrap params
type ControlOrigin: EnsureOrigin<Self::Origin>;
/// Set default weight.
type WeightInfo: WeightInfo;

/// ModuleID for creating sub account
#[pallet::constant]
type PalletId: Get<PalletId>;
}

#[pallet::error]
pub enum Error<T> {
NotKSM,
DenominatorZero,
NotGreaterThanZero,
ExceedPoolAmount,
NotEnoughBalance,
InvalidReleaseInterval,
Overflow,
}

#[pallet::event]
#[pallet::generate_deposit(pub (crate) fn deposit_event)]
pub enum Event<T: Config> {
/// [exchanger, ksm_amount]
KSMExchanged(AccountIdOf<T>, BalanceOf<T>),
/// [adder, ksm_amount]
KSMAdded(AccountIdOf<T>, BalanceOf<T>),
/// [original_prce, new_price]
PriceEdited(BalanceOf<T>, BalanceOf<T>),
/// [start, end]
BlockIntervalEdited(BlockNumberFor<T>, BlockNumberFor<T>),
/// [originla_amount_per_day, amount_per_day]
ReleasedPerDayEdited(BalanceOf<T>, BalanceOf<T>),
}

/// The remaining amount which can be exchanged for
#[pallet::storage]
#[pallet::getter(fn get_pool_amount)]
pub type PoolAmount<T: Config> = StorageValue<_, BalanceOf<T>, ValueQuery>;

/// token amount that is released everyday.
#[pallet::storage]
#[pallet::getter(fn get_token_release_per_round)]
pub type TokenReleasePerDay<T: Config> =
StorageValue<_, BalanceOf<T>, ValueQuery, DefaultReleaseAmount<T>>;

// Defult release amount is 30 KSM
#[pallet::type_value]
pub fn DefaultReleaseAmount<T: Config>() -> BalanceOf<T> {
BalanceOf::<T>::unique_saturated_from(TRILLION.saturating_mul(30))
}

/// Token release start block
#[pallet::storage]
#[pallet::getter(fn get_start_and_end_release_block)]
pub type StartEndReleaseBlock<T: Config> =
StorageValue<_, (BlockNumberFor<T>, BlockNumberFor<T>), ValueQuery>;

/// Exchange price discount: vsbond + vstoken => token
#[pallet::storage]
#[pallet::getter(fn get_exchange_price_discount)]
pub type ExchangePriceDiscount<T: Config> =
StorageValue<_, Percent, ValueQuery, DefaultPrice<T>>;

// Defult price is 90%
#[pallet::type_value]
pub fn DefaultPrice<T: Config>() -> Percent {
Percent::from_rational(90u32, 100u32)
}

#[pallet::pallet]
pub struct Pallet<T>(PhantomData<T>);

#[pallet::hooks]
impl<T: Config> Hooks<T::BlockNumber> for Pallet<T> {
fn on_initialize(n: T::BlockNumber) -> Weight {
let (start, end) = Self::get_start_and_end_release_block();
// relsease fixed amount every day if within release interval and has enough balance in
// the pool account
if (n <= end) & (n > start) {
if (n - start) % BLOCKS_PER_DAY.into() == Zero::zero() {
let ksm = CurrencyId::Token(TokenSymbol::KSM);
let pool_account: AccountIdOf<T> = T::PalletId::get().into_sub_account(0);
let releae_per_day = Self::get_token_release_per_round();
let total_amount = Self::get_pool_amount().saturating_add(releae_per_day);

if T::MultiCurrency::ensure_can_withdraw(ksm, &pool_account, total_amount)
.is_ok()
{
PoolAmount::<T>::mutate(|amt| *amt = total_amount);
}
}
}
T::WeightInfo::on_initialize()
}
}

#[pallet::call]
impl<T: Config> Pallet<T> {
/// Anyone can add KSM to the pool.
#[pallet::weight(T::WeightInfo::add_ksm_to_pool())]
pub fn add_ksm_to_pool(origin: OriginFor<T>, token_amount: BalanceOf<T>) -> DispatchResult {
let adder = ensure_signed(origin)?;
let ksm_id = CurrencyId::Token(TokenSymbol::KSM);

let token_balance = T::MultiCurrency::free_balance(ksm_id, &adder);
ensure!(token_balance >= token_amount, Error::<T>::NotEnoughBalance);

let pool_account: AccountIdOf<T> = T::PalletId::get().into_sub_account(0);
T::MultiCurrency::transfer(ksm_id, &adder, &pool_account, token_amount)?;

Self::deposit_event(Event::KSMAdded(adder, token_amount));

Ok(())
}

// exchange vsksm and vsbond for ksm
#[pallet::weight(T::WeightInfo::exchange_for_ksm())]
pub fn exchange_for_ksm(
origin: OriginFor<T>,
token_amount: BalanceOf<T>, // The KSM amount the user exchanges for
) -> DispatchResult {
// Check origin
let exchanger = ensure_signed(origin)?;
ensure!(token_amount <= Self::get_pool_amount(), Error::<T>::ExceedPoolAmount);

// Check exchanger's vsksm and vsbond balance
let vsksm = CurrencyId::VSToken(TokenSymbol::KSM);
let vsbond = CurrencyId::VSBond(TokenSymbol::BNC, 2001, 13, 20);
let ksm = CurrencyId::Token(TokenSymbol::KSM);

ensure!(
Self::get_exchange_price_discount() != Percent::zero(),
Error::<T>::DenominatorZero
);
let amount_needed =
Self::get_exchange_price_discount().saturating_reciprocal_mul(token_amount);

let vsksm_balance = T::MultiCurrency::free_balance(vsksm, &exchanger);
let vsbond_balance = T::MultiCurrency::free_balance(vsbond, &exchanger);
ensure!(vsksm_balance >= amount_needed, Error::<T>::NotEnoughBalance);
ensure!(vsbond_balance >= amount_needed, Error::<T>::NotEnoughBalance);

// Make changes to account token balances
let pool_account: AccountIdOf<T> = T::PalletId::get().into_sub_account(0);
T::MultiCurrency::ensure_can_withdraw(ksm, &pool_account, token_amount)?;
PoolAmount::<T>::mutate(|amt| *amt = amt.saturating_sub(token_amount));

T::MultiCurrency::transfer(vsksm, &exchanger, &pool_account, amount_needed)?;
T::MultiCurrency::transfer(vsbond, &exchanger, &pool_account, amount_needed)?;
T::MultiCurrency::transfer(ksm, &pool_account, &exchanger, token_amount)?;

Self::deposit_event(Event::KSMExchanged(exchanger, token_amount));

Ok(())
}

// edit exchange discount price
#[pallet::weight(T::WeightInfo::edit_exchange_price())]
pub fn edit_exchange_price(
origin: OriginFor<T>,
price: BalanceOf<T>, /* the mumber of ksm we can get by giving out 100 vsksm and 100
* vsbond */
) -> DispatchResult {
// Check origin
T::ControlOrigin::ensure_origin(origin)?;
ensure!(price <= BalanceOf::<T>::unique_saturated_from(100u128), Error::<T>::Overflow);

let price_percent: Percent =
Percent::from_rational(price, BalanceOf::<T>::unique_saturated_from(100u128));
let original_price: BalanceOf<T> = Self::get_exchange_price_discount()
.mul_floor(BalanceOf::<T>::unique_saturated_from(100u128));

ExchangePriceDiscount::<T>::mutate(|p| *p = price_percent);

Self::deposit_event(Event::PriceEdited(original_price, price));

Ok(())
}

// edit token release amount per day
#[pallet::weight(T::WeightInfo::edit_release_per_day())]
pub fn edit_release_per_day(
origin: OriginFor<T>,
amount_per_day: BalanceOf<T>,
) -> DispatchResult {
// Check origin
T::ControlOrigin::ensure_origin(origin)?;
ensure!(amount_per_day > Zero::zero(), Error::<T>::NotGreaterThanZero);

let originla_amount_per_day = Self::get_token_release_per_round();
TokenReleasePerDay::<T>::mutate(|amt| *amt = amount_per_day);

Self::deposit_event(Event::ReleasedPerDayEdited(
originla_amount_per_day,
amount_per_day,
));

Ok(())
}

// edit token release start and end block
#[pallet::weight(T::WeightInfo::edit_release_start_and_end_block())]
pub fn edit_release_start_and_end_block(
origin: OriginFor<T>,
start: BlockNumberFor<T>,
end: BlockNumberFor<T>,
) -> DispatchResult {
// Check origin
T::ControlOrigin::ensure_origin(origin)?;

let current_block_number = frame_system::Pallet::<T>::block_number(); // get current block number
ensure!(start > current_block_number, Error::<T>::InvalidReleaseInterval);
ensure!(end >= start, Error::<T>::InvalidReleaseInterval);

StartEndReleaseBlock::<T>::mutate(|interval| *interval = (start, end));

Self::deposit_event(Event::BlockIntervalEdited(start, end));

Ok(())
}
}
}
Loading

0 comments on commit 47bd581

Please sign in to comment.