Skip to content

Commit

Permalink
clouds-convert pallet (#1225)
Browse files Browse the repository at this point in the history
* clouds-convert pallet

* fix clippy

* fix test
  • Loading branch information
herryho committed May 7, 2024
1 parent ab150aa commit b4cb114
Show file tree
Hide file tree
Showing 13 changed files with 824 additions and 0 deletions.
23 changes: 23 additions & 0 deletions Cargo.lock

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

2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ members = [
"pallets/prices",
"pallets/leverage-staking",
"pallets/channel-commission",
"pallets/clouds-convert",

"runtime/bifrost-kusama",
"runtime/bifrost-polkadot",
Expand Down Expand Up @@ -91,6 +92,7 @@ pallet-prices = { path = "pallets/prices", default-featur
pallet-traits = { path = "pallets/traits", default-features = false }
leverage-staking = { path = "pallets/leverage-staking", default-features = false }
bifrost-channel-commission = { path = "pallets/channel-commission", default-features = false }
bifrost-clouds-convert = { path = "pallets/clouds-convert", default-features = false }

# Zenlink
merkle-distributor = { git = "https://github.com/bifrost-finance/Zenlink-DEX-Module", branch = "release-polkadot-v1.6.0", default-features = false }
Expand Down
54 changes: 54 additions & 0 deletions pallets/clouds-convert/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
[package]
name = "bifrost-clouds-convert"
version = "0.8.0"
authors = ["Herry Ho <herry.heyi@gmail.com>"]
edition = "2021"

[dependencies]
parity-scale-codec = { workspace = true, features = ["derive"] }
scale-info = { workspace = true, features = ["derive"] }
sp-std = { workspace = true }
frame-support = { workspace = true }
frame-system = { workspace = true }
sp-runtime = { workspace = true }
frame-benchmarking = { workspace = true, optional = true }
bifrost-primitives = { workspace = true }
orml-traits = { workspace = true }
bifrost-ve-minting = { workspace = true }
sp-core = { workspace = true }
pallet-balances = { workspace = true }

[dev-dependencies]
orml-tokens = { workspace = true }
bifrost-currencies = { workspace = true }
sp-io = { workspace = true }
bifrost-asset-registry = { workspace = true }

[features]
default = ["std"]
std = [
"parity-scale-codec/std",
"scale-info/std",
"sp-std/std",
"sp-runtime/std",
"sp-core/std",
"bifrost-primitives/std",
"frame-support/std",
"frame-system/std",
"frame-benchmarking?/std",
"orml-traits/std",
"bifrost-ve-minting/std",
"orml-tokens/std",
"bifrost-currencies/std",
"sp-io/std",
"bifrost-asset-registry/std",
"pallet-balances/std",
]

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

// Copyright (C) Liebi Technologies PTE. 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/>.

// Ensure we're `no_std` when compiling for Wasm.
#![cfg(feature = "runtime-benchmarks")]

use bifrost_primitives::{currency::CLOUD, VBNC};
use frame_benchmarking::{account, benchmarks};
use frame_system::RawOrigin;
use sp_runtime::traits::{UniqueSaturatedFrom, Zero};

use crate::{BalanceOf, Call, Config, Pallet as CloudsConvert, Pallet};
use orml_traits::MultiCurrency;

benchmarks! {
clouds_to_vebnc {
let test_account: T::AccountId = account("seed",1,1);

T::MultiCurrency::deposit(CLOUD, &test_account, BalanceOf::<T>::unique_saturated_from(100_000_000_000_000u128))?;
T::MultiCurrency::deposit(VBNC, &CloudsConvert::<T>::clouds_pool_account(), BalanceOf::<T>::unique_saturated_from(100_000_000_000_000_000_000u128))?;

}: _(RawOrigin::Signed(test_account), BalanceOf::<T>::unique_saturated_from(10_000_000_000_000u128), Zero::zero())

charge_vbnc {
let test_account: T::AccountId = account("seed",1,1);

T::MultiCurrency::deposit(VBNC, &test_account, BalanceOf::<T>::unique_saturated_from(100_000_000_000_000u128))?;

}: _(RawOrigin::Signed(test_account),BalanceOf::<T>::unique_saturated_from(50_000_000_000u128))

impl_benchmark_test_suite!(CloudsConvert,crate::mock::ExtBuilder::default().build(),crate::mock::Runtime);
}
190 changes: 190 additions & 0 deletions pallets/clouds-convert/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@
// This file is part of Bifrost.

// Copyright (C) Liebi Technologies PTE. 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/>.

// Ensure we're `no_std` when compiling for Wasm.
#![cfg_attr(not(feature = "std"), no_std)]

extern crate alloc;

use alloc::vec;
use bifrost_primitives::{
currency::{CLOUD, VBNC},
CurrencyId,
};
use bifrost_ve_minting::VeMintingInterface;
use frame_support::{
ensure,
pallet_prelude::*,
sp_runtime::{
traits::{AccountIdConversion, UniqueSaturatedFrom},
SaturatedConversion,
},
PalletId,
};
use frame_system::pallet_prelude::*;
use orml_traits::MultiCurrency;
use sp_core::U256;
pub use weights::WeightInfo;

#[cfg(feature = "runtime-benchmarks")]
mod benchmarking;
mod mock;
mod tests;
pub mod weights;

pub use pallet::*;

type BalanceOf<T> = <<T as Config>::MultiCurrency as MultiCurrency<
<T as frame_system::Config>::AccountId,
>>::Balance;
type AccountIdOf<T> = <T as frame_system::Config>::AccountId;
pub(crate) type CurrencyIdOf<T> = <<T as Config>::MultiCurrency as MultiCurrency<
<T as frame_system::Config>::AccountId,
>>::CurrencyId;

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

#[pallet::config]
pub trait Config: frame_system::Config {
type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
/// Currecny operation handler
type MultiCurrency: MultiCurrency<AccountIdOf<Self>, CurrencyId = CurrencyId>;

/// Clouds Pallet Id
type CloudsPalletId: Get<PalletId>;

// veMinting interface
type VeMinting: VeMintingInterface<
AccountIdOf<Self>,
CurrencyIdOf<Self>,
BalanceOf<Self>,
BlockNumberFor<Self>,
>;

/// Weight information for extrinsics in this module.
type WeightInfo: WeightInfo;

/// locked blocks for veBNC converted from clouds
#[pallet::constant]
type LockedBlocks: Get<BlockNumberFor<Self>>;
}

#[pallet::error]
pub enum Error<T> {
NotEnoughBalance,
CalculationOverflow,
LessThanExpected,
LessThanExistentialDeposit,
}

#[pallet::event]
#[pallet::generate_deposit(pub(crate) fn deposit_event)]
pub enum Event<T: Config> {
CloudsConverted { clouds: BalanceOf<T>, vebnc: BalanceOf<T> },

VbncCharged { vbnc: BalanceOf<T> },
}

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

#[pallet::hooks]
impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {}

#[pallet::call]
impl<T: Config> Pallet<T> {
#[pallet::call_index(0)]
#[pallet::weight(T::WeightInfo::clouds_to_vebnc())]
pub fn clouds_to_vebnc(
origin: OriginFor<T>,
value: BalanceOf<T>,
expected_min_vebnc: BalanceOf<T>,
) -> DispatchResult {
let who = ensure_signed(origin)?;

// check the user balance of clouds
T::MultiCurrency::ensure_can_withdraw(CLOUD, &who, value)
.map_err(|_| Error::<T>::NotEnoughBalance)?;

let can_get_vbnc = Self::calculate_can_get_vbnc(value)?;
ensure!(can_get_vbnc >= expected_min_vebnc, Error::<T>::LessThanExpected);
// ensure can_get_vbnc greater than existential deposit
let existential_deposit = T::MultiCurrency::minimum_balance(VBNC);
ensure!(can_get_vbnc >= existential_deposit, Error::<T>::LessThanExistentialDeposit);

// burn clouds
T::MultiCurrency::withdraw(CLOUD, &who, value)?;

// transfer vBNC from pool to user
let vbnc_pool_account = Self::clouds_pool_account();
T::MultiCurrency::transfer(VBNC, &vbnc_pool_account, &who, can_get_vbnc)?;

// mint veBNC for user
T::VeMinting::create_lock_inner(&who, can_get_vbnc, T::LockedBlocks::get())?;

// deposit event
Self::deposit_event(Event::CloudsConverted { clouds: value, vebnc: can_get_vbnc });

Ok(())
}

#[pallet::call_index(1)]
#[pallet::weight(T::WeightInfo::charge_vbnc())]
pub fn charge_vbnc(origin: OriginFor<T>, amount: BalanceOf<T>) -> DispatchResult {
let who = ensure_signed(origin)?;

// Transfer vBNC from user to clouds pool
let vbnc_pool_account = Self::clouds_pool_account();
T::MultiCurrency::transfer(VBNC, &who, &vbnc_pool_account, amount)
.map_err(|_| Error::<T>::NotEnoughBalance)?;

// deposit event
Self::deposit_event(Event::VbncCharged { vbnc: amount });

Ok(())
}
}

impl<T: Config> Pallet<T> {
fn calculate_can_get_vbnc(clouds: BalanceOf<T>) -> Result<BalanceOf<T>, Error<T>> {
// get the vBNC balance of clouds pool
let vbnc_pool_account = Self::clouds_pool_account();
let vbnc_balance = T::MultiCurrency::free_balance(VBNC, &vbnc_pool_account);

// get the total supply of clouds
let total_supply = T::MultiCurrency::total_issuance(CLOUD);

let can_get_amount = U256::from(vbnc_balance.saturated_into::<u128>())
.saturating_mul(clouds.saturated_into::<u128>().into())
.checked_div(total_supply.saturated_into::<u128>().into())
// first turn into u128,then use unique_saturated_into BalanceOf<T>
.map(|x| x.saturated_into::<u128>())
.map(|x| BalanceOf::<T>::unique_saturated_from(x))
.ok_or(Error::<T>::CalculationOverflow)?;

Ok(can_get_amount)
}

pub fn clouds_pool_account() -> AccountIdOf<T> {
T::CloudsPalletId::get().into_account_truncating()
}
}
}

0 comments on commit b4cb114

Please sign in to comment.