Currency Types

pallets/lockable-currency Try on playground View on GitHub

pallets/reservable-currency Try on playground View on GitHub

pallets/currency-imbalances Try on playground View on GitHub

Just Plain Currency

To use a balance type in the runtime, import the Currency trait from frame_support.

use frame_support::traits::Currency;

The Currency trait provides an abstraction over a fungible assets system. To use such a fungible asset from your pallet, include an associated type with the Currency trait bound in your pallet's configuration trait.

pub trait Config: frame_system::Config {
	type Currency: Currency<Self::AccountId>;
}

Defining an associated type with this trait bound allows this pallet to access the provided methods of Currency. For example, it is straightforward to check the total issuance of the system:

// in decl_module block
T::Currency::total_issuance();

As promised, it is also possible to type alias a balances type for use in the runtime:

type BalanceOf<T> = <<T as Trait>::Currency as Currency<<T as frame_system::Config>::AccountId>>::Balance;

This new BalanceOf<T> type satisfies the type constraints of Self::Balance for the provided methods of Currency. This means that this type can be used for transfer, minting, and much more.

Reservable Currency

Substrate's Treasury pallet uses the Currency type for bonding spending proposals. To reserve and unreserve funds for bonding, treasury uses the ReservableCurrency trait. The import and associated type declaration follow convention

use frame_support::traits::{Currency, ReservableCurrency};

pub trait Config: frame_system::Config {
	type Currency: Currency<Self::AccountId> + ReservableCurrency<Self::AccountId>;
}

To lock or unlock some quantity of funds, it is sufficient to invoke reserve and unreserve respectively

#[pallet::weight(10_000)]
pub fn reserve_funds(
	origin: OriginFor<T>,
	amount: BalanceOf<T>,
	) -> DispatchResultWithPostInfo {
		let locker = ensure_signed(origin)?;

		T::Currency::reserve(&locker, amount)
			.map_err(|_| "locker can't afford to lock the amount requested")?;

		let now = <frame_system::Module<T>>::block_number();

		Self::deposit_event(Event::LockFunds(locker, amount, now));
		Ok(().into())
}
#[pallet::weight(10_000)]
pub fn unreserve_funds(
	origin: OriginFor<T>,
	amount: BalanceOf<T>,
	) -> DispatchResultWithPostInfo {
		let unlocker = ensure_signed(origin)?;

		T::Currency::unreserve(&unlocker, amount);
		// ReservableCurrency::unreserve does not fail (it will lock up as much as amount)

		let now = <frame_system::Module<T>>::block_number();

		Self::deposit_event(Event::UnlockFunds(unlocker, amount, now));
		Ok(().into())
}

Lockable Currency

Substrate's Staking pallet similarly uses LockableCurrency trait for more nuanced handling of capital locking based on time increments. This type can be very useful in the context of economic systems that enforce accountability by collateralizing fungible resources. Import this trait in the usual way

use frame_support::traits::{LockIdentifier, LockableCurrency}

To use LockableCurrency, it is necessary to define a LockIdentifier.

const EXAMPLE_ID: LockIdentifier = *b"example ";

By using this EXAMPLE_ID, it is straightforward to define logic within the runtime to schedule locking, unlocking, and extending existing locks.

#[pallet::weight(10_000)]
fn lock_capital(origin, amount: BalanceOf<T>) -> DispatchResultWithPostInfo {
	let user = ensure_signed(origin)?;

	T::Currency::set_lock(
		EXAMPLE_ID,
		&user,
		amount,
		WithdrawReasons::except(WithdrawReason::TransactionPayment),
	);

	Self::deposit_event(RawEvent::Locked(user, amount));
	Ok(().into())
}

Imbalances

Functions that alter balances return an object of the Imbalance type to express how much account balances have been altered in aggregate. This is useful in the context of state transitions that adjust the total supply of the Currency type in question.

To manage this supply adjustment, the OnUnbalanced handler is often used. An example might look something like

#[weight = 10_000]
pub fn reward_funds(origin, to_reward: T::AccountId, reward: BalanceOf<T>) {
	let _ = ensure_signed(origin)?;

	let mut total_imbalance = <PositiveImbalanceOf<T>>::zero();

	let r = T::Currency::deposit_into_existing(&to_reward, reward).ok();
	total_imbalance.maybe_subsume(r);
	T::Reward::on_unbalanced(total_imbalance);

	let now = <frame_system::Module<T>>::block_number();
	Self::deposit_event(RawEvent::RewardFunds(to_reward, reward, now));
}

takeaway

The way we represent value in the runtime dictates both the security and flexibility of the underlying transactional system. Likewise, it is convenient to be able to take advantage of Rust's flexible trait system when building systems intended to rethink how we exchange information and value 🚀