Skip to content
This repository has been archived by the owner on Nov 15, 2023. It is now read-only.

Do not allow death on Crowdloan contribute + Introduce contribute_all #4233

Merged
merged 4 commits into from
Dec 13, 2021
Merged
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
180 changes: 103 additions & 77 deletions runtime/common/src/crowdloan.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,9 +55,13 @@ use crate::{
};
use frame_support::{
ensure,
pallet_prelude::Weight,
pallet_prelude::{DispatchResult, Weight},
storage::{child, ChildTriePrefixIterator},
traits::{Currency, ExistenceRequirement::AllowDeath, Get, ReservableCurrency},
traits::{
Currency,
ExistenceRequirement::{self, AllowDeath, KeepAlive},
Get, ReservableCurrency,
},
Identity, PalletId,
};
pub use pallet::*;
Expand Down Expand Up @@ -444,81 +448,7 @@ pub mod pallet {
signature: Option<MultiSignature>,
) -> DispatchResult {
let who = ensure_signed(origin)?;

ensure!(value >= T::MinContribution::get(), Error::<T>::ContributionTooSmall);
let mut fund = Self::funds(index).ok_or(Error::<T>::InvalidParaId)?;
fund.raised = fund.raised.checked_add(&value).ok_or(Error::<T>::Overflow)?;
ensure!(fund.raised <= fund.cap, Error::<T>::CapExceeded);

// Make sure crowdloan has not ended
let now = <frame_system::Pallet<T>>::block_number();
ensure!(now < fund.end, Error::<T>::ContributionPeriodOver);

// Make sure crowdloan is in a valid lease period
let now = frame_system::Pallet::<T>::block_number();
let (current_lease_period, _) =
T::Auctioneer::lease_period_index(now).ok_or(Error::<T>::NoLeasePeriod)?;
ensure!(current_lease_period <= fund.first_period, Error::<T>::ContributionPeriodOver);

// Make sure crowdloan has not already won.
let fund_account = Self::fund_account_id(index);
ensure!(
!T::Auctioneer::has_won_an_auction(index, &fund_account),
Error::<T>::BidOrLeaseActive
);

// We disallow any crowdloan contributions during the VRF Period, so that people do not sneak their
// contributions into the auction when it would not impact the outcome.
ensure!(!T::Auctioneer::auction_status(now).is_vrf(), Error::<T>::VrfDelayInProgress);

let (old_balance, memo) = Self::contribution_get(fund.trie_index, &who);

if let Some(ref verifier) = fund.verifier {
let signature = signature.ok_or(Error::<T>::InvalidSignature)?;
let payload = (index, &who, old_balance, value);
let valid = payload.using_encoded(|encoded| {
signature.verify(encoded, &verifier.clone().into_account())
});
ensure!(valid, Error::<T>::InvalidSignature);
}

CurrencyOf::<T>::transfer(&who, &fund_account, value, AllowDeath)?;

let balance = old_balance.saturating_add(value);
Self::contribution_put(fund.trie_index, &who, &balance, &memo);

if T::Auctioneer::auction_status(now).is_ending().is_some() {
match fund.last_contribution {
// In ending period; must ensure that we are in NewRaise.
LastContribution::Ending(n) if n == now => {
// do nothing - already in NewRaise
},
_ => {
NewRaise::<T>::append(index);
fund.last_contribution = LastContribution::Ending(now);
},
}
} else {
let endings_count = Self::endings_count();
match fund.last_contribution {
LastContribution::PreEnding(a) if a == endings_count => {
// Not in ending period and no auctions have ended ending since our
// previous bid which was also not in an ending period.
// `NewRaise` will contain our ID still: Do nothing.
},
_ => {
// Not in ending period; but an auction has been ending since our previous
// bid, or we never had one to begin with. Add bid.
NewRaise::<T>::append(index);
fund.last_contribution = LastContribution::PreEnding(endings_count);
},
}
}

Funds::<T>::insert(index, &fund);

Self::deposit_event(Event::<T>::Contributed(who, index, value));
Ok(())
Self::do_contribute(who, index, value, signature, KeepAlive)
}

/// Withdraw full balance of a specific contributor.
Expand Down Expand Up @@ -706,6 +636,19 @@ pub mod pallet {
Self::deposit_event(Event::<T>::AddedToNewRaise(index));
Ok(())
}

/// Contribute your entire balance to a crowd sale. This will transfer the entire balance of a user over to fund a parachain
/// slot. It will be withdrawable when the crowdloan has ended and the funds are unused.
#[pallet::weight(T::WeightInfo::contribute())]
pub fn contribute_all(
origin: OriginFor<T>,
#[pallet::compact] index: ParaId,
signature: Option<MultiSignature>,
) -> DispatchResult {
let who = ensure_signed(origin)?;
let value = CurrencyOf::<T>::free_balance(&who);
Self::do_contribute(who, index, value, signature, AllowDeath)
}
}
}

Expand Down Expand Up @@ -784,6 +727,89 @@ impl<T: Config> Pallet<T> {

Ok(())
}

fn do_contribute(
who: T::AccountId,
index: ParaId,
value: BalanceOf<T>,
signature: Option<MultiSignature>,
existence: ExistenceRequirement,
) -> DispatchResult {
ensure!(value >= T::MinContribution::get(), Error::<T>::ContributionTooSmall);
let mut fund = Self::funds(index).ok_or(Error::<T>::InvalidParaId)?;
fund.raised = fund.raised.checked_add(&value).ok_or(Error::<T>::Overflow)?;
ensure!(fund.raised <= fund.cap, Error::<T>::CapExceeded);

// Make sure crowdloan has not ended
let now = <frame_system::Pallet<T>>::block_number();
ensure!(now < fund.end, Error::<T>::ContributionPeriodOver);

// Make sure crowdloan is in a valid lease period
let now = frame_system::Pallet::<T>::block_number();
let (current_lease_period, _) =
T::Auctioneer::lease_period_index(now).ok_or(Error::<T>::NoLeasePeriod)?;
ensure!(current_lease_period <= fund.first_period, Error::<T>::ContributionPeriodOver);

// Make sure crowdloan has not already won.
let fund_account = Self::fund_account_id(index);
ensure!(
!T::Auctioneer::has_won_an_auction(index, &fund_account),
Error::<T>::BidOrLeaseActive
);

// We disallow any crowdloan contributions during the VRF Period, so that people do not sneak their
// contributions into the auction when it would not impact the outcome.
ensure!(!T::Auctioneer::auction_status(now).is_vrf(), Error::<T>::VrfDelayInProgress);

let (old_balance, memo) = Self::contribution_get(fund.trie_index, &who);

if let Some(ref verifier) = fund.verifier {
let signature = signature.ok_or(Error::<T>::InvalidSignature)?;
let payload = (index, &who, old_balance, value);
let valid = payload.using_encoded(|encoded| {
signature.verify(encoded, &verifier.clone().into_account())
});
ensure!(valid, Error::<T>::InvalidSignature);
}

CurrencyOf::<T>::transfer(&who, &fund_account, value, existence)?;

let balance = old_balance.saturating_add(value);
Self::contribution_put(fund.trie_index, &who, &balance, &memo);

if T::Auctioneer::auction_status(now).is_ending().is_some() {
match fund.last_contribution {
// In ending period; must ensure that we are in NewRaise.
LastContribution::Ending(n) if n == now => {
// do nothing - already in NewRaise
},
_ => {
NewRaise::<T>::append(index);
fund.last_contribution = LastContribution::Ending(now);
},
}
} else {
let endings_count = Self::endings_count();
match fund.last_contribution {
LastContribution::PreEnding(a) if a == endings_count => {
// Not in ending period and no auctions have ended ending since our
// previous bid which was also not in an ending period.
// `NewRaise` will contain our ID still: Do nothing.
},
_ => {
// Not in ending period; but an auction has been ending since our previous
// bid, or we never had one to begin with. Add bid.
NewRaise::<T>::append(index);
fund.last_contribution = LastContribution::PreEnding(endings_count);
},
}
}

Funds::<T>::insert(index, &fund);

Self::deposit_event(Event::<T>::Contributed(who, index, value));
Ok(())
}
}

impl<T: Config> crate::traits::OnSwap for Pallet<T> {
Expand Down