Skip to content

Commit

Permalink
Feat: distribute crowdloan rewards (#1864)
Browse files Browse the repository at this point in the history
* exact do_create

* implement Streaming trait

* use in crowdloan

* Update lib.rs

* add test

* add update_leases_bonus & add LeasesBonus

* add to runtime & add test

* exact minimum_deposit
  • Loading branch information
mclyk authored Sep 16, 2022
1 parent e8205e4 commit 4f2091f
Show file tree
Hide file tree
Showing 19 changed files with 379 additions and 37 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

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

16 changes: 16 additions & 0 deletions pallets/crowdloans/src/benchmarking.rs
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,22 @@ benchmarks! {
assert_last_event::<T>(Event::ProxyUpdated(caller).into())
}

update_leases_bonus {
let bonus_config = BonusConfig {
bonus_per_token: 5,
start_time: Default::default(),
end_time: Default::default(),
};
}: _(
SystemOrigin::Root,
6,
13,
bonus_config
)
verify {
assert_last_event::<T>(Event::LeasesBonusUpdated((6,13),bonus_config).into())
}

reopen {
let ctoken = 13;
let caller: T::AccountId = whitelisted_caller();
Expand Down
55 changes: 54 additions & 1 deletion pallets/crowdloans/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ pub mod pallet {
use sp_std::{boxed::Box, vec::Vec};
use xcm::latest::prelude::*;

use pallet_traits::{VaultTokenCurrenciesFilter, VaultTokenExchangeRateProvider};
use pallet_traits::{Streaming, VaultTokenCurrenciesFilter, VaultTokenExchangeRateProvider};

use parallel_support::math_helper::f64::{
fixed_u128_from_float, fixed_u128_to_float, power_float,
Expand Down Expand Up @@ -188,6 +188,12 @@ pub mod pallet {

/// To expose XCM helper functions
type XCM: XcmHelper<Self, BalanceOf<Self>, Self::AccountId>;

/// To expose Streaming related functions
type Streaming: Streaming<Self::AccountId, AssetIdOf<Self>, BalanceOf<Self>>;

#[pallet::constant]
type GetNativeCurrencyId: Get<AssetIdOf<Self>>;
}

#[pallet::event]
Expand Down Expand Up @@ -282,6 +288,8 @@ pub mod pallet {
/// Update proxy address
/// [account]
ProxyUpdated(T::AccountId),
/// Update leases bonus
LeasesBonusUpdated(VaultId, BonusConfig<BalanceOf<T>>),
}

#[pallet::error]
Expand Down Expand Up @@ -374,6 +382,18 @@ pub mod pallet {
#[pallet::getter(fn proxy_address)]
pub type ProxyAddress<T: Config> = StorageValue<_, AccountIdOf<T>, OptionQuery>;

#[pallet::storage]
#[pallet::getter(fn leases_bonus)]
pub type LeasesBonus<T: Config> = StorageNMap<
_,
(
NMapKey<Blake2_128Concat, LeasePeriod>,
NMapKey<Blake2_128Concat, LeasePeriod>,
),
BonusConfig<BalanceOf<T>>,
ValueQuery,
>;

#[pallet::call]
impl<T: Config> Pallet<T> {
/// Create a new vault via a governance decision
Expand Down Expand Up @@ -1141,6 +1161,24 @@ pub mod pallet {

Ok(())
}

/// Update crowdloans proxy address in relaychain
#[pallet::weight(<T as Config>::WeightInfo::update_leases_bonus())]
#[transactional]
pub fn update_leases_bonus(
origin: OriginFor<T>,
lease_start: LeasePeriod,
lease_end: LeasePeriod,
bonus_config: BonusConfig<BalanceOf<T>>,
) -> DispatchResult {
ensure_origin!(UpdateOrigin, origin)?;
LeasesBonus::<T>::insert((&lease_start, &lease_end), bonus_config);
Self::deposit_event(Event::<T>::LeasesBonusUpdated(
(lease_start, lease_end),
bonus_config,
));
Ok(())
}
}

impl<T: Config> Pallet<T> {
Expand Down Expand Up @@ -1593,6 +1631,21 @@ pub mod pallet {

Self::contribution_kill(vault.trie_index, &who, ChildStorageKind::Contributed);

// Bonus for PARA, Not applicable for HKO
let bonus_config = Self::leases_bonus((&lease_start, &lease_end));
let bonus_amount = amount.saturating_mul(bonus_config.bonus_per_token);
if !bonus_amount.is_zero() {
T::Streaming::create(
Self::account_id(),
who.clone(),
bonus_amount,
T::GetNativeCurrencyId::get(),
bonus_config.start_time,
bonus_config.end_time,
false,
)?;
}

Self::deposit_event(Event::<T>::VaultClaimed(
crowdloan,
(lease_start, lease_end),
Expand Down
2 changes: 2 additions & 0 deletions pallets/crowdloans/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -514,6 +514,8 @@ impl crate::Config for Test {
type LeasePeriod = LeasePeriod;
type LeaseOffset = LeaseOffset;
type LeasePerYear = LeasePerYear;
type Streaming = ();
type GetNativeCurrencyId = NativeCurrencyId;
}

parameter_types! {
Expand Down
18 changes: 18 additions & 0 deletions pallets/crowdloans/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1849,3 +1849,21 @@ fn xcm_proxy_contribute_should_work() {
// assert_eq!(ctoken_balance, amount);
});
}

#[test]
fn update_leases_bonus_should_work() {
new_test_ext().execute_with(|| {
let start_lease = 6;
let end_lease = 13;

let mut config = BonusConfig::default();
config.bonus_per_token = 5;
assert_ok!(Crowdloans::update_leases_bonus(
frame_system::RawOrigin::Root.into(),
start_lease,
end_lease,
config,
));
assert_eq!(Crowdloans::leases_bonus((&start_lease, &end_lease)), config,);
})
}
22 changes: 21 additions & 1 deletion pallets/crowdloans/src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ use super::{AccountIdOf, AssetIdOf, BalanceOf, Config};
use codec::{Decode, Encode};

use frame_system::pallet_prelude::BlockNumberFor;
use primitives::{LeasePeriod, ParaId, TrieIndex, VaultId};
use primitives::{LeasePeriod, ParaId, Timestamp, TrieIndex, VaultId};
use scale_info::TypeInfo;
use sp_runtime::{traits::Zero, RuntimeDebug};
use sp_std::vec::Vec;
Expand Down Expand Up @@ -144,3 +144,23 @@ impl Default for Releases {
Releases::V0_0_0
}
}

#[derive(PartialEq, Eq, Copy, Clone, Encode, Decode, RuntimeDebug, TypeInfo)]
pub struct BonusConfig<Balance> {
// The bonus per-value of the contribute
pub bonus_per_token: Balance,
// The start time of the stream
pub start_time: Timestamp,
// The end time of the stream
pub end_time: Timestamp,
}

impl<Balance: Default> Default for BonusConfig<Balance> {
fn default() -> Self {
BonusConfig {
bonus_per_token: Default::default(),
start_time: Default::default(),
end_time: Default::default(),
}
}
}
12 changes: 12 additions & 0 deletions pallets/crowdloans/src/weights.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ pub trait WeightInfo {
fn dissolve_vault() -> Weight;
fn refund_for() -> Weight;
fn update_proxy() -> Weight;
fn update_leases_bonus() -> Weight;
}

/// Weights for pallet_crowdloans using the Substrate node and recommended hardware.
Expand Down Expand Up @@ -291,6 +292,12 @@ impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> {
.saturating_add(T::DbWeight::get().reads(1 as Weight))
.saturating_add(T::DbWeight::get().writes(2 as Weight))
}

fn update_leases_bonus() -> Weight {
(31_127_000 as Weight)
.saturating_add(T::DbWeight::get().reads(1 as Weight))
.saturating_add(T::DbWeight::get().writes(2 as Weight))
}
}

// For backwards compatibility and tests
Expand Down Expand Up @@ -517,4 +524,9 @@ impl WeightInfo for () {
.saturating_add(RocksDbWeight::get().reads(1 as Weight))
.saturating_add(RocksDbWeight::get().writes(2 as Weight))
}
fn update_leases_bonus() -> Weight {
(31_127_000 as Weight)
.saturating_add(RocksDbWeight::get().reads(1 as Weight))
.saturating_add(RocksDbWeight::get().writes(2 as Weight))
}
}
2 changes: 2 additions & 0 deletions pallets/streaming/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ scale-info = { version = '2.1', default-features = false, features = ['d
serde = { version = '1.0.136', features = ['derive'], optional = true }
sp-runtime = { git = 'https://github.com/paritytech/substrate.git', branch = 'polkadot-v0.9.28', default-features = false }
sp-std = { git = 'https://github.com/paritytech/substrate.git', branch = 'polkadot-v0.9.28', default-features = false }
pallet-traits = { path = '../traits', default-features = false }

[dev-dependencies]
orml-oracle = { version = '0.4.1-dev' }
Expand All @@ -47,6 +48,7 @@ std = [
'pallet-timestamp/std',
'serde',
'scale-info/std',
'pallet-traits/std',
]
try-runtime = ['frame-support/try-runtime']

Expand Down
118 changes: 84 additions & 34 deletions pallets/streaming/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ use frame_support::{
transactional, PalletId,
};
use frame_system::pallet_prelude::*;
use pallet_traits::Streaming as StreamingTrait;
use primitives::*;
use sp_runtime::{
traits::{AccountIdConversion, One, Zero},
Expand Down Expand Up @@ -227,54 +228,24 @@ pub mod pallet {
cancellable: bool,
) -> DispatchResultWithPostInfo {
let sender = ensure_signed(origin)?;
ensure!(sender != recipient, Error::<T>::RecipientIsAlsoSender);

let minimum_deposit =
Self::minimum_deposit(asset_id).ok_or(Error::<T>::InvalidAssetId)?;
ensure!(
deposit >= minimum_deposit,
Error::<T>::DepositLowerThanMinimum
);

let duration = Self::ensure_valid_duration(start_time, end_time)?;
let rate_per_sec = deposit
.checked_div(duration as u128)
.ok_or(Error::<T>::InvalidRatePerSecond)?;
ensure!(!rate_per_sec.is_zero(), Error::<T>::InvalidRatePerSecond);

// Transfer deposit asset from sender to global EOA
T::Assets::transfer(asset_id, &sender, &Self::account_id(), deposit, false)?;

// The remaining balance will be the same value as the deposit due to initialization
let stream: Stream<T> = Stream::new(
deposit,
asset_id,
rate_per_sec,
let stream_id = Self::do_create(
sender.clone(),
recipient.clone(),
deposit,
asset_id,
start_time,
end_time,
cancellable,
);

let stream_id = NextStreamId::<T>::get();
// Increment next stream id and store the new created stream
NextStreamId::<T>::set(
stream_id
.checked_add(One::one())
.ok_or(ArithmeticError::Overflow)?,
);
Streams::<T>::insert(stream_id, stream);

)?;
// Add the stream_id to stream_library for both the sender and receiver.
Self::try_push_stream_library(&sender, stream_id, StreamKind::Send)?;
Self::try_push_stream_library(&recipient, stream_id, StreamKind::Receive)?;
// Remove the outdated and finished streams, should do update after push
Self::update_finished_stream_library(&sender, &recipient)?;

Self::deposit_event(Event::<T>::StreamCreated(
stream_id, sender, recipient, deposit, asset_id, start_time, end_time, true,
));
Ok(().into())
}

Expand Down Expand Up @@ -517,4 +488,83 @@ impl<T: Config> Pallet<T> {

Ok(())
}

pub fn do_create(
sender: AccountOf<T>,
recipient: AccountOf<T>,
deposit: BalanceOf<T>,
asset_id: AssetIdOf<T>,
start_time: Timestamp,
end_time: Timestamp,
cancellable: bool,
) -> Result<StreamId, DispatchError> {
ensure!(sender != recipient, Error::<T>::RecipientIsAlsoSender);

let duration = Self::ensure_valid_duration(start_time, end_time)?;
let rate_per_sec = deposit
.checked_div(duration as u128)
.ok_or(Error::<T>::InvalidRatePerSecond)?;
ensure!(!rate_per_sec.is_zero(), Error::<T>::InvalidRatePerSecond);

// Transfer deposit asset from sender to global EOA
T::Assets::transfer(asset_id, &sender, &Self::account_id(), deposit, false)?;

// The remaining balance will be the same value as the deposit due to initialization
let stream: Stream<T> = Stream::new(
deposit,
asset_id,
rate_per_sec,
sender.clone(),
recipient.clone(),
start_time,
end_time,
cancellable,
);

let stream_id = NextStreamId::<T>::get();
// Increment next stream id and store the new created stream
NextStreamId::<T>::set(
stream_id
.checked_add(One::one())
.ok_or(ArithmeticError::Overflow)?,
);
Streams::<T>::insert(stream_id, stream);

// Remove the outdated and finished streams, should do update after push
Self::update_finished_stream_library(&sender, &recipient)?;

Self::deposit_event(Event::<T>::StreamCreated(
stream_id, sender, recipient, deposit, asset_id, start_time, end_time, true,
));
Ok(stream_id)
}
}

impl<T: Config> StreamingTrait<AccountOf<T>, AssetIdOf<T>, BalanceOf<T>> for Pallet<T> {
fn create(
sender: AccountOf<T>,
recipient: AccountOf<T>,
deposit: BalanceOf<T>,
asset_id: AssetIdOf<T>,
start_time: Timestamp,
end_time: Timestamp,
cancellable: bool,
) -> Result<(), DispatchError> {
ensure!(
Self::minimum_deposit(asset_id).is_some(),
Error::<T>::InvalidAssetId
);
let stream_id = Self::do_create(
sender,
recipient.clone(),
deposit,
asset_id,
start_time,
end_time,
cancellable,
)?;
// Add the stream_id to stream_library for receiver.
Self::try_push_stream_library(&recipient, stream_id, StreamKind::Receive)?;
Ok(())
}
}
Loading

0 comments on commit 4f2091f

Please sign in to comment.