Skip to content

Commit

Permalink
able to change redeem currency
Browse files Browse the repository at this point in the history
  • Loading branch information
xlc committed Jul 8, 2023
1 parent 5b98796 commit 3d4f0fe
Show file tree
Hide file tree
Showing 7 changed files with 199 additions and 33 deletions.
53 changes: 48 additions & 5 deletions modules/liquid-crowdloan/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ use frame_support::{pallet_prelude::*, traits::EnsureOrigin, PalletId};
use frame_system::pallet_prelude::*;
use orml_traits::MultiCurrency;
use primitives::{Balance, CurrencyId};
use sp_runtime::traits::AccountIdConversion;
use sp_runtime::{traits::AccountIdConversion, ArithmeticError};

use support::CrowdloanVaultXcm;

Expand Down Expand Up @@ -79,11 +79,17 @@ pub mod module {
#[pallet::generate_deposit(fn deposit_event)]
pub enum Event<T: Config> {
/// Liquid Crowdloan asset was redeemed.
Redeemed { amount: Balance },
Redeemed { currency_id: CurrencyId, amount: Balance },
/// The transfer from relay chain crowdloan vault was requested.
TransferFromCrowdloanVaultRequested { amount: Balance },
/// The redeem currency id was updated.
RedeemCurrencyIdUpdated { currency_id: CurrencyId },
}

/// The redeem currency id.
#[pallet::storage]
pub(crate) type RedeemCurrencyId<T: Config> = StorageValue<_, CurrencyId, OptionQuery>;

#[pallet::pallet]
pub struct Pallet<T>(_);
#[pallet::call]
Expand All @@ -94,11 +100,33 @@ pub mod module {
pub fn redeem(origin: OriginFor<T>, #[pallet::compact] amount: Balance) -> DispatchResult {
let who = ensure_signed(origin)?;

T::Currency::withdraw(T::LiquidCrowdloanCurrencyId::get(), &who, amount)?;
let (currency_id, redeem_amount) = if let Some(redeem_currency_id) = RedeemCurrencyId::<T>::get() {
// redeem the RedeemCurrencyId
// amount_pect = amount / lcdot_total_supply
// amount_redeem = amount_pect * redeem_currency_balance

let redeem_currency_balance = T::Currency::free_balance(redeem_currency_id, &Self::account_id());
let lcdot_total_supply = T::Currency::total_issuance(T::LiquidCrowdloanCurrencyId::get());

let amount_redeem = amount
.checked_mul(redeem_currency_balance)
.and_then(|x| x.checked_div(lcdot_total_supply))
.ok_or(ArithmeticError::Overflow)?;

T::Currency::transfer(T::RelayChainCurrencyId::get(), &Self::account_id(), &who, amount)?;
(redeem_currency_id, amount_redeem)
} else {
// redeem DOT
let currency_id = T::RelayChainCurrencyId::get();
(currency_id, amount)
};

Self::deposit_event(Event::Redeemed { amount });
T::Currency::withdraw(T::LiquidCrowdloanCurrencyId::get(), &who, amount)?;
T::Currency::transfer(currency_id, &Self::account_id(), &who, redeem_amount)?;

Self::deposit_event(Event::Redeemed {
currency_id,
amount: redeem_amount,
});

Ok(())
}
Expand All @@ -125,6 +153,21 @@ pub mod module {

Ok(())
}

/// Set the redeem currency id.
///
/// This call requires `GovernanceOrigin`.
#[pallet::call_index(2)]
#[pallet::weight(<T as Config>::WeightInfo::set_redeem_currency_id())]
pub fn set_redeem_currency_id(origin: OriginFor<T>, currency_id: CurrencyId) -> DispatchResult {
T::GovernanceOrigin::ensure_origin(origin)?;

<RedeemCurrencyId<T>>::put(currency_id);

Self::deposit_event(Event::RedeemCurrencyIdUpdated { currency_id });

Ok(())
}
}
}

Expand Down
10 changes: 6 additions & 4 deletions modules/liquid-crowdloan/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ pub type BlockNumber = u64;
pub const ACA: CurrencyId = CurrencyId::Token(TokenSymbol::ACA);
pub const DOT: CurrencyId = CurrencyId::Token(TokenSymbol::DOT);
pub const LDOT: CurrencyId = CurrencyId::Token(TokenSymbol::LDOT);
pub const LCDOT: CurrencyId = CurrencyId::LiquidCrowdloan(13);

pub const ALICE: AccountId = AccountId32::new([1u8; 32]);
pub const BOB: AccountId = AccountId32::new([2u8; 32]);
Expand Down Expand Up @@ -116,8 +117,9 @@ parameter_types! {
pub Erc20HoldingAccount: H160 = H160::from_low_u64_be(1);
pub CrowdloanVault: AccountId = VAULT;
pub LiquidCrowdloanPalletId: PalletId = PalletId(*b"aca/lqcl");
pub const Ldot: CurrencyId = LDOT;
pub const Dot: CurrencyId = DOT;
pub const GetLDOT: CurrencyId = LDOT;
pub const GetDOT: CurrencyId = DOT;
pub const GetLCDOT: CurrencyId = LCDOT;
}

impl module_currencies::Config for Runtime {
Expand Down Expand Up @@ -162,8 +164,8 @@ ord_parameter_types! {
impl liquid_crowdloan::Config for Runtime {
type RuntimeEvent = RuntimeEvent;
type Currency = Currencies;
type LiquidCrowdloanCurrencyId = Ldot;
type RelayChainCurrencyId = Dot;
type LiquidCrowdloanCurrencyId = GetLCDOT;
type RelayChainCurrencyId = GetDOT;
type PalletId = LiquidCrowdloanPalletId;
type GovernanceOrigin = EnsureSignedBy<Alice, AccountId>;
type CrowdloanVault = CrowdloanVault;
Expand Down
78 changes: 74 additions & 4 deletions modules/liquid-crowdloan/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,14 +29,17 @@ use sp_runtime::traits::BadOrigin;
#[test]
fn redeem_works() {
ExtBuilder::default()
.balances(vec![(BOB, LDOT, 100), (LiquidCrowdloan::account_id(), DOT, 100)])
.balances(vec![(BOB, LCDOT, 100), (LiquidCrowdloan::account_id(), DOT, 100)])
.build()
.execute_with(|| {
assert_ok!(LiquidCrowdloan::redeem(RuntimeOrigin::signed(BOB), 100));
assert_eq!(Currencies::free_balance(LDOT, &BOB), 0);
assert_eq!(Currencies::free_balance(LCDOT, &BOB), 0);
assert_eq!(Currencies::free_balance(DOT, &BOB), 100);
assert_eq!(Currencies::free_balance(DOT, &LiquidCrowdloan::account_id()), 0);
System::assert_last_event(RuntimeEvent::LiquidCrowdloan(crate::Event::Redeemed { amount: 100 }));
System::assert_last_event(RuntimeEvent::LiquidCrowdloan(crate::Event::Redeemed {
currency_id: DOT,
amount: 100,
}));
});
}

Expand All @@ -47,13 +50,18 @@ fn redeem_fails_if_not_enough_liquid_crowdloan_token() {
LiquidCrowdloan::redeem(RuntimeOrigin::signed(BOB), 100),
orml_tokens::Error::<Runtime>::BalanceTooLow
);

assert_err!(
LiquidCrowdloan::redeem(RuntimeOrigin::signed(BOB), u128::MAX),
orml_tokens::Error::<Runtime>::BalanceTooLow
);
});
}

#[test]
fn redeem_fails_if_not_enough_relay_chain_token() {
ExtBuilder::default()
.balances(vec![(BOB, LDOT, 100)])
.balances(vec![(BOB, LCDOT, 100)])
.build()
.execute_with(|| {
assert_err!(
Expand Down Expand Up @@ -95,3 +103,65 @@ fn transfer_from_crowdloan_vault_fails_if_sending_xcm_failed() {
);
})
}

#[test]
fn set_redeem_currency_id() {
ExtBuilder::default()
.balances(vec![
(ALICE, LCDOT, 100),
(BOB, LCDOT, 100),
(LiquidCrowdloan::account_id(), LDOT, 2200),
])
.build()
.execute_with(|| {
assert_ok!(LiquidCrowdloan::set_redeem_currency_id(
RuntimeOrigin::signed(ALICE),
LDOT
));

assert_err!(
LiquidCrowdloan::redeem(RuntimeOrigin::signed(ALICE), u128::MAX),
sp_runtime::ArithmeticError::Overflow
);

assert_ok!(LiquidCrowdloan::redeem(RuntimeOrigin::signed(ALICE), 10));
assert_eq!(Currencies::free_balance(LCDOT, &ALICE), 90);
assert_eq!(Currencies::free_balance(LDOT, &ALICE), 110);
assert_eq!(Currencies::free_balance(LDOT, &LiquidCrowdloan::account_id()), 2090);
assert_eq!(Currencies::total_issuance(LCDOT), 190);
System::assert_last_event(RuntimeEvent::LiquidCrowdloan(crate::Event::Redeemed {
currency_id: LDOT,
amount: 110,
}));

assert_ok!(LiquidCrowdloan::redeem(RuntimeOrigin::signed(ALICE), 10));
assert_eq!(Currencies::free_balance(LCDOT, &ALICE), 80);
assert_eq!(Currencies::free_balance(LDOT, &ALICE), 220);
assert_eq!(Currencies::free_balance(LDOT, &LiquidCrowdloan::account_id()), 1980);
assert_eq!(Currencies::total_issuance(LCDOT), 180);
System::assert_last_event(RuntimeEvent::LiquidCrowdloan(crate::Event::Redeemed {
currency_id: LDOT,
amount: 110,
}));

assert_ok!(LiquidCrowdloan::redeem(RuntimeOrigin::signed(ALICE), 80));
assert_eq!(Currencies::free_balance(LCDOT, &ALICE), 0);
assert_eq!(Currencies::free_balance(LDOT, &ALICE), 1100);
assert_eq!(Currencies::free_balance(LDOT, &LiquidCrowdloan::account_id()), 1100);
assert_eq!(Currencies::total_issuance(LCDOT), 100);
System::assert_last_event(RuntimeEvent::LiquidCrowdloan(crate::Event::Redeemed {
currency_id: LDOT,
amount: 880,
}));

assert_ok!(LiquidCrowdloan::redeem(RuntimeOrigin::signed(BOB), 100));
assert_eq!(Currencies::free_balance(LCDOT, &BOB), 0);
assert_eq!(Currencies::free_balance(LDOT, &BOB), 1100);
assert_eq!(Currencies::free_balance(LDOT, &LiquidCrowdloan::account_id()), 0);
assert_eq!(Currencies::total_issuance(LCDOT), 0);
System::assert_last_event(RuntimeEvent::LiquidCrowdloan(crate::Event::Redeemed {
currency_id: LDOT,
amount: 1100,
}));
});
}
63 changes: 44 additions & 19 deletions modules/liquid-crowdloan/src/weights.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,12 @@
//! Autogenerated weights for module_liquid_crowdloan
//!
//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev
//! DATE: 2023-06-02, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]`
//! HOSTNAME: `ip-172-31-40-233`, CPU: `Intel(R) Xeon(R) Platinum 8375C CPU @ 2.90GHz`
//! DATE: 2023-07-07, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]`
//! HOSTNAME: `bryan-mbp-m1.local`, CPU: `<UNKNOWN>`
//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024

// Executed Command:
// target/release/acala
// target/debug/acala
// benchmark
// pallet
// --chain=dev
Expand All @@ -49,6 +49,7 @@ use sp_std::marker::PhantomData;
pub trait WeightInfo {
fn redeem() -> Weight;
fn transfer_from_crowdloan_vault() -> Weight;
fn set_redeem_currency_id() -> Weight;
}

/// Weights for module_liquid_crowdloan using the Acala node and recommended hardware.
Expand All @@ -60,15 +61,17 @@ impl<T: frame_system::Config> WeightInfo for AcalaWeight<T> {
// Proof: Tokens TotalIssuance (max_values: None, max_size: Some(67), added: 2542, mode: MaxEncodedLen)
// Storage: EvmAccounts EvmAddresses (r:2 w:0)
// Proof: EvmAccounts EvmAddresses (max_values: None, max_size: Some(60), added: 2535, mode: MaxEncodedLen)
// Storage: LiquidCrowdloan RedeemCurrencyId (r:1 w:0)
// Proof: LiquidCrowdloan RedeemCurrencyId (max_values: Some(1), max_size: Some(43), added: 538, mode: MaxEncodedLen)
// Storage: System Account (r:1 w:1)
// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen)
fn redeem() -> Weight {
// Proof Size summary in bytes:
// Measured: `2927`
// Estimated: `22041`
// Minimum execution time: 125_572 nanoseconds.
Weight::from_parts(128_915_000, 22041)
.saturating_add(T::DbWeight::get().reads(7))
// Measured: `2931`
// Estimated: `8856`
// Minimum execution time: 1_062_000 nanoseconds.
Weight::from_parts(1_075_000_000, 8856)
.saturating_add(T::DbWeight::get().reads(8))
.saturating_add(T::DbWeight::get().writes(5))
}
// Storage: XcmInterface XcmDestWeightAndFee (r:1 w:0)
Expand All @@ -82,12 +85,22 @@ impl<T: frame_system::Config> WeightInfo for AcalaWeight<T> {
fn transfer_from_crowdloan_vault() -> Weight {
// Proof Size summary in bytes:
// Measured: `1255`
// Estimated: `11689`
// Minimum execution time: 46_004 nanoseconds.
Weight::from_parts(46_711_000, 11689)
// Estimated: `4720`
// Minimum execution time: 357_000 nanoseconds.
Weight::from_parts(362_000_000, 4720)
.saturating_add(T::DbWeight::get().reads(4))
.saturating_add(T::DbWeight::get().writes(1))
}
// Storage: LiquidCrowdloan RedeemCurrencyId (r:0 w:1)
// Proof: LiquidCrowdloan RedeemCurrencyId (max_values: Some(1), max_size: Some(43), added: 538, mode: MaxEncodedLen)
fn set_redeem_currency_id() -> Weight {
// Proof Size summary in bytes:
// Measured: `1001`
// Estimated: `0`
// Minimum execution time: 156_000 nanoseconds.
Weight::from_parts(163_000_000, 0)
.saturating_add(T::DbWeight::get().writes(1))
}
}

// For backwards compatibility and tests
Expand All @@ -98,15 +111,17 @@ impl WeightInfo for () {
// Proof: Tokens TotalIssuance (max_values: None, max_size: Some(67), added: 2542, mode: MaxEncodedLen)
// Storage: EvmAccounts EvmAddresses (r:2 w:0)
// Proof: EvmAccounts EvmAddresses (max_values: None, max_size: Some(60), added: 2535, mode: MaxEncodedLen)
// Storage: LiquidCrowdloan RedeemCurrencyId (r:1 w:0)
// Proof: LiquidCrowdloan RedeemCurrencyId (max_values: Some(1), max_size: Some(43), added: 538, mode: MaxEncodedLen)
// Storage: System Account (r:1 w:1)
// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen)
fn redeem() -> Weight {
// Proof Size summary in bytes:
// Measured: `2927`
// Estimated: `22041`
// Minimum execution time: 125_572 nanoseconds.
Weight::from_parts(128_915_000, 22041)
.saturating_add(RocksDbWeight::get().reads(7))
// Measured: `2931`
// Estimated: `8856`
// Minimum execution time: 1_062_000 nanoseconds.
Weight::from_parts(1_075_000_000, 8856)
.saturating_add(RocksDbWeight::get().reads(8))
.saturating_add(RocksDbWeight::get().writes(5))
}
// Storage: XcmInterface XcmDestWeightAndFee (r:1 w:0)
Expand All @@ -120,10 +135,20 @@ impl WeightInfo for () {
fn transfer_from_crowdloan_vault() -> Weight {
// Proof Size summary in bytes:
// Measured: `1255`
// Estimated: `11689`
// Minimum execution time: 46_004 nanoseconds.
Weight::from_parts(46_711_000, 11689)
// Estimated: `4720`
// Minimum execution time: 357_000 nanoseconds.
Weight::from_parts(362_000_000, 4720)
.saturating_add(RocksDbWeight::get().reads(4))
.saturating_add(RocksDbWeight::get().writes(1))
}
// Storage: LiquidCrowdloan RedeemCurrencyId (r:0 w:1)
// Proof: LiquidCrowdloan RedeemCurrencyId (max_values: Some(1), max_size: Some(43), added: 538, mode: MaxEncodedLen)
fn set_redeem_currency_id() -> Weight {
// Proof Size summary in bytes:
// Measured: `1001`
// Estimated: `0`
// Minimum execution time: 156_000 nanoseconds.
Weight::from_parts(163_000_000, 0)
.saturating_add(RocksDbWeight::get().writes(1))
}
}
10 changes: 10 additions & 0 deletions runtime/acala/src/weights/module_liquid_crowdloan.rs
Original file line number Diff line number Diff line change
Expand Up @@ -88,4 +88,14 @@ impl<T: frame_system::Config> module_liquid_crowdloan::WeightInfo for WeightInfo
.saturating_add(T::DbWeight::get().reads(7))
.saturating_add(T::DbWeight::get().writes(2))
}

fn set_redeem_currency_id() -> Weight {
// Proof Size summary in bytes:
// Measured: `1392`
// Estimated: `22711`
// Minimum execution time: 56_316 nanoseconds.
Weight::from_parts(57_458_000, 22711)
.saturating_add(T::DbWeight::get().reads(7))
.saturating_add(T::DbWeight::get().writes(2))
}
}
8 changes: 7 additions & 1 deletion runtime/mandala/src/benchmarking/liquid_crowdloan.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,10 @@
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.

use crate::{AccountId, LiquidCrowdloan, LiquidCrowdloanCurrencyId, PolkadotXcm, Runtime, RuntimeOrigin, System};
use crate::{
AccountId, GetLiquidCurrencyId, LiquidCrowdloan, LiquidCrowdloanCurrencyId, PolkadotXcm, Runtime, RuntimeOrigin,
System,
};

use super::utils::{set_balance, STAKING};
use frame_benchmarking::whitelisted_caller;
Expand Down Expand Up @@ -44,6 +47,9 @@ runtime_benchmarks! {
verify {
System::assert_last_event(module_liquid_crowdloan::Event::TransferFromCrowdloanVaultRequested { amount }.into());
}

set_redeem_currency_id {
}: _(RawOrigin::Root, GetLiquidCurrencyId::get())
}

#[cfg(test)]
Expand Down
10 changes: 10 additions & 0 deletions runtime/mandala/src/weights/module_liquid_crowdloan.rs
Original file line number Diff line number Diff line change
Expand Up @@ -82,4 +82,14 @@ impl<T: frame_system::Config> module_liquid_crowdloan::WeightInfo for WeightInfo
.saturating_add(T::DbWeight::get().reads(4))
.saturating_add(T::DbWeight::get().writes(1))
}

fn set_redeem_currency_id() -> Weight {
// Proof Size summary in bytes:
// Measured: `1255`
// Estimated: `11689`
// Minimum execution time: 32_468 nanoseconds.
Weight::from_parts(33_256_000, 11689)
.saturating_add(T::DbWeight::get().reads(4))
.saturating_add(T::DbWeight::get().writes(1))
}
}

0 comments on commit 3d4f0fe

Please sign in to comment.