Developers Home»how to guides»Integrate the Contracts Pallet

Integrate the Contracts Pallet

Tip

You should already have the latest version of the Substrate Node Template compiled on your computer to follow this guide. If you haven't already done so, refer to this tutorial.

Goal

Add the Contracts pallet to your runtime to be able to use Wasm smart contracts in your blockchain.

Use Cases

  • Developping a chain capable of handling smart contracts
  • On-chain execution of Wasm binaries

Overview

This guide will show you how you can add the Contracts pallet to your runtime in order to allow your blockchain to 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.

Steps

1. Import the dependencies

Refer to this guide to properly include Contracts in your runtime.

This includes updating runtime/Cargo.toml and runtime/Cargo.toml with:

  • pallet-contracts
  • pallet-contracts-primitives

2. Add the Contracts pallet to your runtime

Now you'll have to implement the Contract's pallet configuration traits in order for your runtime to use it properly.

Implement pallet_contracts

Start by making sure you've included all of the types that pallet_contracts exposes. You can copy these from FRAME's source code (always check that versioning is equivalent to the imported crate). Here's what you need to add inside runtime/src/lib.rs only the first 4 types are shown:

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

Parameter types

Notice how some of these types require parameter_types. Have a look at their implementation in this runtime to make sure you've included them correctly. They go right above impl pallet_contracts::Config for Runtime. We'll take DeletionQueueDepth as one example go ahead and add them in:

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_initalize` handlers.
/// This is used to limit the maximal weight of a single extrinsic.
const AVERAGE_ON_INITIALIZE_RATIO: Perbill = Perbill::from_percent(10);

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::{Module, Call, Config<T>, Storage, Event<T>},
 /* --snip-- */

3. Add API dependencies

Information

The Contracts pallet exposes custom runtime APIs and RPC endpoints which enables reading a contracts state from off chain. Since we want to use the Contracts pallet to make calls to our node's storage without making a transaction, we'll two other pallets: the Contracts RPC Runtime API pallet and the Contracts RPC pallet to help us achieve this.

Import dependencies

  1. Just like in the first step of this guide, update /runtime/Cargo.toml to add pallet-contracts-rpc-runtime-api.

  2. Similarly, we need to include pallet-contracts and pallet-contracts-rpc inside node/Cargo.toml so that our runtime can interact with our node.

Now we can add the ContractsApi dependency required to implement the Contracts runtime API.

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

use pallet_contracts_rpc::{Contracts, ContractsApi};
Tip

Unsure what version to import? Use the latest version as indicated in crates.io.

Implement the Contracts runtime API

We're now ready to implement the Contracts runtime API.

This happens in the impl_runtime_apis! macro near the end of your runtime.

Make sure to 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)}

Add RPC API extension

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

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

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

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

And add the contracts RPC API extension using:

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

4. 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:

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

Examples

Rust docs

Last edit: on

Was This Guide Helpful?
Help us improve