Skip to content
crowdloan.rs 63.1 KiB
Newer Older
// Copyright 2017-2020 Parity Technologies (UK) Ltd.
// This file is part of Polkadot.

// Polkadot is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// Polkadot is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with Polkadot.  If not, see <http://www.gnu.org/licenses/>.

Shawn Tabrizi's avatar
Shawn Tabrizi committed
//! # Parachain Crowdloaning module
//!
//! The point of this module is to allow parachain projects to offer the ability to help fund a
//! deposit for the parachain. When the crowdloan has ended, the funds are returned.
//! Each fund has a child-trie which stores all contributors account IDs together with the amount
//! they contributed; the root of this can then be used by the parachain to allow contributors to
//! prove that they made some particular contribution to the project (e.g. to be rewarded through
//! some token or badge). The trie is retained for later (efficient) redistribution back to the
//! contributors.
//!
//! Contributions must be of at least `MinContribution` (to account for the resources taken in
//! tracking contributions), and may never tally greater than the fund's `cap`, set and fixed at the
//! time of creation. The `create` call may be used to create a new fund. In order to do this, then
//! a deposit must be paid of the amount `SubmissionDeposit`. Substantial resources are taken on
//! the main trie in tracking a fund and this accounts for that.
//!
//! Funds may be set up during an auction period; their closing time is fixed at creation (as a
//! block number) and if the fund is not successful by the closing time, then it can be dissolved.
//! Funds may span multiple auctions, and even auctions that sell differing periods. However, for a
//! fund to be active in bidding for an auction, it *must* have had *at least one bid* since the end
//! of the last auction. Until a fund takes a further bid following the end of an auction, then it
//! will be inactive.
//!
//! Contributors will get a refund of their contributions from completed funds before the crowdloan
//! can be dissolved.
//! Funds may accept contributions at any point before their success or end. When a parachain
//! slot auction enters its ending period, then parachains will each place a bid; the bid will be
//! raised once per block if the parachain had additional funds contributed since the last bid.
//!
//! Successful funds remain tracked (in the `Funds` storage item and the associated child trie) as long as
//! the parachain remains active. Users can withdraw their funds once the slot is completed and funds are
//! returned to the crowdloan account.
use frame_support::{
	decl_module, decl_storage, decl_event, decl_error, ensure, Identity,
	storage::{child, ChildTriePrefixIterator},
		Currency, ReservableCurrency, Get, ExistenceRequirement::AllowDeath
	pallet_prelude::{Weight, DispatchResultWithPostInfo},
use frame_system::{ensure_signed, ensure_root};
use sp_runtime::{
	ModuleId, DispatchResult, RuntimeDebug, MultiSignature, MultiSigner,
		AccountIdConversion, Hash, Saturating, Zero, One, CheckedAdd, Verify, IdentifyAccount,
use crate::traits::{Registrar, Auctioneer};
use crate::slot_range::SlotRange;
use parity_scale_codec::{Encode, Decode};
use sp_std::vec::Vec;
use primitives::v1::Id as ParaId;

type CurrencyOf<T> = <<T as Config>::Auctioneer as Auctioneer>::Currency;
type LeasePeriodOf<T> = <<T as Config>::Auctioneer as Auctioneer>::LeasePeriod;
type BalanceOf<T> = <CurrencyOf<T> as Currency<<T as frame_system::Config>::AccountId>>::Balance;
#[allow(dead_code)]
type NegativeImbalanceOf<T> = <CurrencyOf<T> as Currency<<T as frame_system::Config>::AccountId>>::NegativeImbalance;

type TrieIndex = u32;

pub trait WeightInfo {
	fn create() -> Weight;
	fn contribute() -> Weight;
	fn withdraw() -> Weight;
	fn refund(k: u32, ) -> Weight;
	fn dissolve() -> Weight;
	fn add_memo() -> Weight;
	fn on_initialize(n: u32, ) -> Weight;
}
pub struct TestWeightInfo;
impl WeightInfo for TestWeightInfo {
	fn create() -> Weight { 0 }
	fn contribute() -> Weight { 0 }
	fn withdraw() -> Weight { 0 }
	fn refund(_k: u32, ) -> Weight { 0 }
	fn dissolve() -> Weight { 0 }
	fn edit() -> Weight { 0 }
	fn add_memo() -> Weight { 0 }
	fn on_initialize(_n: u32, ) -> Weight { 0 }
}

pub trait Config: frame_system::Config {
	type Event: From<Event<Self>> + Into<<Self as frame_system::Config>::Event>;
Shawn Tabrizi's avatar
Shawn Tabrizi committed
	/// ModuleID for the crowdloan module. An appropriate value could be ```ModuleId(*b"py/cfund")```
	type ModuleId: Get<ModuleId>;

	/// The amount to be held on deposit by the depositor of a crowdloan.
	type SubmissionDeposit: Get<BalanceOf<Self>>;

Shawn Tabrizi's avatar
Shawn Tabrizi committed
	/// The minimum amount that may be contributed into a crowdloan. Should almost certainly be at
	/// least ExistentialDeposit.
	type MinContribution: Get<BalanceOf<Self>>;

Shawn Tabrizi's avatar
Shawn Tabrizi committed
	/// Max number of storage keys to remove per extrinsic call.
	type RemoveKeysLimit: Get<u32>;
	/// The parachain registrar type. We jus use this to ensure that only the manager of a para is able to
	/// start a crowdloan for its slot.
	type Registrar: Registrar<AccountId=Self::AccountId>;

	/// The type representing the auctioning system.
	type Auctioneer: Auctioneer<
		AccountId=Self::AccountId,
		BlockNumber=Self::BlockNumber,
		LeasePeriod=Self::BlockNumber,
	>;
	/// The maximum length for the memo attached to a crowdloan contribution.
	type MaxMemoLength: Get<u8>;

	/// Weight Information for the Extrinsics in the Pallet
	type WeightInfo: WeightInfo;
}

#[derive(Encode, Decode, Copy, Clone, PartialEq, Eq, RuntimeDebug)]
pub enum LastContribution<BlockNumber> {
	Never,
	PreEnding(u32),
	Ending(BlockNumber),
}

/// Information on a funding effort for a pre-existing parachain. We assume that the parachain ID
/// is known as it's used for the key of the storage item for which this is the value (`Funds`).
#[derive(Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug)]
#[codec(dumb_trait_bound)]
pub struct FundInfo<AccountId, Balance, BlockNumber, LeasePeriod> {
	/// The owning account who placed the deposit.
	depositor: AccountId,
	/// An optional verifier. If exists, contributions must be signed by verifier.
	verifier: Option<MultiSigner>,
	/// The amount of deposit placed.
	deposit: Balance,
	/// The total amount raised.
	raised: Balance,
	/// Block number after which the funding must have succeeded. If not successful at this number
	/// then everyone may withdraw their funds.
	end: BlockNumber,
	/// A hard-cap on the amount that may be contributed.
	cap: Balance,
	/// The most recent block that this had a contribution. Determines if we make a bid or not.
	/// If this is `Never`, this fund has never received a contribution.
	/// If this is `PreEnding(n)`, this fund received a contribution sometime in auction
	/// number `n` before the ending period.
	/// If this is `Ending(n)`, this fund received a contribution during the current ending period,
	/// where `n` is how far into the ending period the contribution was made.
	last_contribution: LastContribution<BlockNumber>,
	/// First lease period in range to bid on; it's actually a LeasePeriod, but that's the same type
	/// as BlockNumber.
	first_period: LeasePeriod,
	/// Last lease period in range to bid on; it's actually a LeasePeriod, but that's the same type
	/// as BlockNumber.
	last_period: LeasePeriod,
	/// Index used for the child trie of this fund
	trie_index: TrieIndex,
}

decl_storage! {
Shawn Tabrizi's avatar
Shawn Tabrizi committed
	trait Store for Module<T: Config> as Crowdloan {
		/// Info on all of the funds.
		Funds get(fn funds):
			map hasher(twox_64_concat) ParaId
			=> Option<FundInfo<T::AccountId, BalanceOf<T>, T::BlockNumber, LeasePeriodOf<T>>>;

		/// The funds that have had additional contributions during the last block. This is used
		/// in order to determine which funds should submit new or updated bids.
		NewRaise get(fn new_raise): Vec<ParaId>;

		/// The number of auctions that have entered into their ending period so far.
		EndingsCount get(fn endings_count): u32;

		/// Tracker for the next available trie index
		NextTrieIndex get(fn next_trie_index): u32;
Loading full blame...