Implement a Lockable Currency
Goal
Write a pallet that allows users to lock funds.
Use Cases
- Staking
- Conviction voting
Overview
The LockableCurrency
trait can be very useful in the context of economic systems that enforce accountability by collateralizing fungible resources.
Substrate's staking pallet makes use of the same trait to handle locked funds in time-based increments. In
this guide, we will make use of it in our own custom pallet.
Steps
1. Declare relevant imports
Import the following traits:
use frame_support::{
dispatch::DispatchResult,
traits::{Currency, LockIdentifier, LockableCurrency, WithdrawReasons},
};
2.Declare the LockIdentifier
constant
In order to use LockableCurrency
, we need to declare a LockIdentifier
(must be 8 characters long):
const EXAMPLE_ID: LockIdentifier = *b"example ";
3. Define the required types
Define the lockable currency type:
type BalanceOf<T> =
<<T as Config>::Currency as Currency<<T as frame_system::Config>::AccountId>>::Balance;
/*--snip--*/
type Currency: LockableCurrency<Self::AccountId, Moment = Self::BlockNumber>;
The new BalanceOf<T>
type satisfies the type constraints of Self::Balance
for all provided
methods inCurrency
.
4. Write out the required functions
Our pallet must contain the following key functions to ensure it handles the basic logical components for locking and unlocking a curency for a period of time. These are:
fn lock_capital
: Locks the specified amount of tokens from the caller.fn extend_lock
: Extends the lock period.fn unlock_all
: Releases all locked tokens.
fn lock_capital
Call the set_lock()
method from Currency
:
#[pallet::weight(10_000 + T::DbWeight::get().writes(1))]
pub(super) fn lock_capital(
origin: OriginFor<T>,
#[pallet::compact] amount: BalanceOf<T>
) -> DispatchResultWithPostInfo {
let user = ensure_signed(origin)?;
T::Currency::set_lock(
EXAMPLE_ID,
&user,
amount,
WithdrawReasons::all(),
);
Self::deposit_event(Event::Locked(user, amount));
Ok(().into())
}
fn extend_lock
Call the extend_lock()
method from Currency
:
#[pallet::weight(1_000)]
pub(super) fn extend_lock(
origin: OriginFor<T>,
#[pallet::compact] amount: BalanceOf<T>,
) -> DispatchResultWithPostInfo {
let user = ensure_signed(origin)?;
T::Currency::extend_lock(
EXAMPLE_ID,
&user,
amount,
WithdrawReasons::all(),
);
Self::deposit_event(Event::ExtendedLock(user, amount));
Ok(().into())
}
fn unlock_all
Call the remove_lock()
method from Currency
:
#[pallet::weight(1_000)]
pub(super) fn unlock_all(
origin: OriginFor<T>,
) -> DispatchResultWithPostInfo {
let user = ensure_signed(origin)?;
T::Currency::remove_lock(EXAMPLE_ID, &user);
Self::deposit_event(Event::Unlocked(user));
Ok(().into())
}
Examples
lockable-currency
example pallet