Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update liquid crowdloan #2573

Merged
merged 5 commits into from
Jul 11, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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,
}));
});
}
61 changes: 43 additions & 18 deletions modules/liquid-crowdloan/src/weights.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@
//! 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-08, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]`
//! HOSTNAME: `ip-172-31-40-230`, CPU: `Intel(R) Xeon(R) Platinum 8375C CPU @ 2.90GHz`
//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024

// Executed Command:
Expand Down Expand Up @@ -49,11 +49,14 @@ 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.
pub struct AcalaWeight<T>(PhantomData<T>);
impl<T: frame_system::Config> WeightInfo for AcalaWeight<T> {
// Storage: LiquidCrowdloan RedeemCurrencyId (r:1 w:0)
// Proof: LiquidCrowdloan RedeemCurrencyId (max_values: Some(1), max_size: Some(43), added: 538, mode: MaxEncodedLen)
// Storage: Tokens Accounts (r:3 w:3)
// Proof: Tokens Accounts (max_values: None, max_size: Some(147), added: 2622, mode: MaxEncodedLen)
// Storage: Tokens TotalIssuance (r:1 w:1)
Expand All @@ -64,11 +67,11 @@ impl<T: frame_system::Config> WeightInfo for AcalaWeight<T> {
// 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: 128_360 nanoseconds.
Weight::from_parts(130_413_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,16 +85,28 @@ 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: 46_836 nanoseconds.
Weight::from_parts(47_418_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: 20_739 nanoseconds.
Weight::from_parts(21_135_000, 0)
.saturating_add(T::DbWeight::get().writes(1))
}
}

// For backwards compatibility and tests
impl WeightInfo for () {
// Storage: LiquidCrowdloan RedeemCurrencyId (r:1 w:0)
// Proof: LiquidCrowdloan RedeemCurrencyId (max_values: Some(1), max_size: Some(43), added: 538, mode: MaxEncodedLen)
// Storage: Tokens Accounts (r:3 w:3)
// Proof: Tokens Accounts (max_values: None, max_size: Some(147), added: 2622, mode: MaxEncodedLen)
// Storage: Tokens TotalIssuance (r:1 w:1)
Expand All @@ -102,11 +117,11 @@ impl WeightInfo for () {
// 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: 128_360 nanoseconds.
Weight::from_parts(130_413_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: 46_836 nanoseconds.
Weight::from_parts(47_418_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: 20_739 nanoseconds.
Weight::from_parts(21_135_000, 0)
.saturating_add(RocksDbWeight::get().writes(1))
}
}
2 changes: 1 addition & 1 deletion runtime/acala/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1680,7 +1680,7 @@ impl module_liquid_crowdloan::Config for Runtime {
type LiquidCrowdloanCurrencyId = LiquidCrowdloanCurrencyId;
type RelayChainCurrencyId = GetStakingCurrencyId;
type PalletId = LiquidCrowdloanPalletId;
type GovernanceOrigin = EnsureRootOrHalfGeneralCouncil;
type GovernanceOrigin = EnsureRootOrThreeFourthsGeneralCouncil;
type CrowdloanVault = CrowdloanVault;
type XcmTransfer = XcmInterface;
type WeightInfo = weights::module_liquid_crowdloan::WeightInfo<Runtime>;
Expand Down
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))
}
}
10 changes: 8 additions & 2 deletions 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, GetStakingCurrencyId, LiquidCrowdloan, LiquidCrowdloanCurrencyId, PolkadotXcm,
Runtime, RuntimeOrigin, System,
};

use super::utils::{set_balance, STAKING};
use frame_benchmarking::whitelisted_caller;
Expand All @@ -34,7 +37,7 @@ runtime_benchmarks! {
set_balance(STAKING, &LiquidCrowdloan::account_id(), amount);
}: _(RawOrigin::Signed(caller), amount)
verify {
System::assert_last_event(module_liquid_crowdloan::Event::Redeemed { amount }.into());
System::assert_last_event(module_liquid_crowdloan::Event::Redeemed { currency_id: GetStakingCurrencyId::get(), amount }.into());
}

transfer_from_crowdloan_vault {
Expand All @@ -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
Loading