Add the contracts pallet

This guide will show you how to add the Contracts pallet to your runtime so that your blockchain can support Wasm smart contracts. You can follow similar patterns to add additional FRAME pallets to your runtime, however you should note that each pallet is a little different in terms of the specific configuration settings needed to use it correctly.

Before you begin

Make sure you have the latest version of the Substrate node template compiled on your computer. If you haven't already done so, see Build a local blockchain.

Import the dependencies

  1. Add Contracts to your runtime. To learn how to include Contracts in your runtime, see Basic pallet integration.
  2. Update runtime/Cargo.toml with the following entries:

    • pallet-contracts
    • pallet-contracts-primitives

Add the Contracts pallet to your runtime

Implement the Contracts pallet in order for your runtime to use it properly.

  1. Implement pallet_contracts.

    Make sure you've included all of the types that pallet_contracts exposes. You can copy these from the source code. Always check that versioning is equivalent to the imported crate.

    Add these types to runtime/src/lib.rs. The following code snippet shows four types:

    impl pallet_contracts::Config for Runtime {
    	type Time = Timestamp;
    	type Randomness = RandomnessCollectiveFlip;
    	type Currency = Balances;
    	type Event = Event;
    	/* --snip-- */
  2. Add parameter types.

    Note that some of these types require parameter_types.

    Add the parameter types directly above impl pallet_contracts::Config for Runtime.

    /* --snip-- */
    Contracts: pallet_contracts::{Pallet, Call, Config<T>, Storage, Event<T>},
    /* --snip-- */

    For example, the following shows how the parameter type, DeletionQueueDepth, should be added:

    parameter_types! {
    	/* --snip-- */
    	pub DeletionQueueDepth: u32 = ((DeletionWeightLimit::get() / (
    		<Runtime as pallet_contracts::Config>::WeightInfo::on_initialize_per_queue_item(1) -
    		<Runtime as pallet_contracts::Config>::WeightInfo::on_initialize_per_queue_item(0)
    		)) / 5) as u32;
    		/* --snip-- */
    	}

    Notice how the above parameter type depends on WeightInfo. This requires you to add the following to the top of runtime/src/lib.rs:

    use pallet_contracts::weights::WeightInfo;

    Similarly, other parameter types use constants such as DAYS, MILLICENTS and AVERAGE_ON_INITIALIZE_RATIO.

    Define these towards the top of your runtime/src/lib.rs file where the other constants exist:

    // Contracts price units.
    pub const MILLICENTS: Balance = 1_000_000_000;
    pub const CENTS: Balance = 1_000 * MILLICENTS;
    pub const DOLLARS: Balance = 100 * CENTS;
    
    const fn deposit(items: u32, bytes: u32) -> Balance {
    	items as Balance * 15 * CENTS + (bytes as Balance) * 6 * CENTS
    }
    
    /// We assume that ~10% of the block weight is consumed by `on_initialize` handlers.
    /// This is used to limit the maximal weight of a single extrinsic.
    const AVERAGE_ON_INITIALIZE_RATIO: Perbill = Perbill::from_percent(10);
  3. Add an instance in runtime for pallet_contracts.

    Create an instance of the Contracts pallet in construct_macro! inside runtime/src/lib.rs:

    /* --snip-- */
    Contracts: pallet_contracts::{Pallet, Call, Config<T>, Storage, Event<T>},
    /* --snip-- */

Add API dependencies

The Contracts pallet exposes custom runtime APIs and RPC endpoints which enables reading a contracts state from off chain. To use the Contracts pallet to make calls to your node's storage without making a transaction, you'll use two other pallets: the Contracts RPC Runtime API pallet and the Contracts RPC pallet.

  1. Import the dependencies. As in the first step of this guide, update /runtime/Cargo.toml to add pallet-contracts-rpc-runtime-api.
  2. Include pallet-contracts and pallet-contracts-rpc inside node/Cargo.toml so that your runtime can interact with your node.
  3. Add the ContractsApi dependency required to implement the Contracts runtime API.

    Add this with the other use statements in node/src/rpc.rs:

    use pallet_contracts_rpc::{Contracts, ContractsApi};

    Navigate to theimpl_runtime_apis! macro near the end of your runtime.

    Add the following functions that the ContractsApi exposes:

    • call(): returns pallet_contracts_primitives::ContractExecResult { Contracts::bare_call(origin, dest, value, gas_limit, input_data)}
    • get_storage(): returns pallet_contracts_primitives::GetStorageResult {Contracts::get_storage(address, key)}
    • rent_projection(): returns pallet_contracts_primitives::RentProjectionResult<BlockNumber> {Contracts::rent_projection(address)}
  4. Add the RPC API extension.

    To be able to call the runtime API, you must add the RPC to the node's service.

    // Contracts RPC API extension
    io.extend_with(
    	ContractsApi::to_delegate(Contracts::new(client.clone()))
    );

    This RPC does not contain access to the Contracts pallet by default. To interact with it, you must extend the existing RPC and add the Contracts pallet along with the API.

    In node/src/rpc.rs, add the following line to the where clause in create_full<C, P>:

    C::Api: pallet_contracts_rpc::ContractsRuntimeApi<Block, AccountId, Balance, BlockNumber>,

    Add the contracts RPC API extension.

    // Contracts RPC API extension
    io.extend_with(
    	ContractsApi::to_delegate(Contracts::new(client.clone()))
    );

Start your upgraded chain

Your node template now includes the Contracts pallet and is ready to execute WASM smart contracts.

Build and run it using the following commands:

# Build chain
cargo build --release
# Launch chain in development mode
./target/release/node-template --dev --tmp

Examples