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

Commit

Permalink
Do not allow death on Crowdloan contribute + Introduce `contribute_…
Browse files Browse the repository at this point in the history
…all` (#4233)

* Add contirbute all, do not allow death on contribute

* fmt

* Revert "fmt"

This reverts commit c4746e3.

* fmt
  • Loading branch information
shawntabrizi authored Dec 13, 2021
1 parent 806ac3f commit c82f4a7
Showing 1 changed file with 103 additions and 77 deletions.
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

0 comments on commit c82f4a7

Please sign in to comment.