Developers Home»how to guides»Implement a Lockable Currency

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

Rust docs

Last edit: on

Was This Guide Helpful?
Help us improve