How to Deploy and Use Ink! Smart Contracts on the 3DPass Blockchain

Deploying Ink! smart contracts on the 3DPass blockchain involves compiling Rust code into a WebAssembly blob using cargo contract, uploading the binary via the contracts_uploadCode RPC to the chain's storage, and instantiating the contract through the contracts_instantiate dispatchable, all orchestrated by the native Substrate contracts pallet configured in runtime/src/lib.rs.

The 3DPass blockchain (repository 3dpass/3DP) provides native support for Ink! smart contracts through its integrated Substrate contracts pallet. This allows developers to write WebAssembly (Wasm)-based smart contracts in Rust and deploy them to the 3DPass network, leveraging the security and performance of the underlying Ledger of Things infrastructure.

The Contracts Pallet Architecture

The 3DPass node ships with the native Substrate contracts pallet, which supports Ink!—the Rust-based eDSL for writing WebAssembly smart contracts. According to the repository's README.md (lines 186-188), the node explicitly supports "native Substrate Smart contract trait using ink".

The pallet is declared as a dependency in runtime/Cargo.toml (lines 127-129) from the Parity Substrate repository:

pallet-contracts = { git = "https://github.com/paritytech/substrate", default-features = false }

The runtime configuration resides in runtime/src/lib.rs, where the pallet_contracts::Config implementation (lines 78-81) wires the contracts pallet into the 3DPass runtime. This configuration includes the weight schedule, maximum code size limits (lines 130-132), and the call filter security whitelist (lines 84-91).

Three-Stage Deployment Process

Deploying an Ink! smart contract to 3DPass follows a standardized three-stage pipeline that mirrors standard Substrate contract workflows:

1. Compile to WebAssembly

Your Ink! source code is compiled into a Wasm blob using cargo contract. No 3DPass-specific compilation flags are required—the output is a standard WASM contract that the contracts pallet understands. The build process produces a .contract bundle containing both the Wasm binary and the contract metadata.

2. Upload Code to Chain Storage

The compiled Wasm blob is stored in the chain's global storage via the pallet-contracts dispatchable. In runtime/src/lib.rs, the configuration defines the maximum code size at lines 130-132. This upload step is executed through the contracts_uploadCode RPC call or the cargo contract upload CLI command.

3. Instantiate the Contract

Once stored, the contract is instantiated via the contracts_instantiate dispatchable. This creates a new contract address and initializes the contract storage using your chosen constructor. The runtime validates this instantiation against the CallFilter configured at lines 84-91 of runtime/src/lib.rs, which defaults to Nothing (blocking runtime calls from contracts for security).

Step-by-Step Deployment Guide

Follow these steps to deploy an Ink! contract to a local 3DPass node:

Prerequisites

Ensure you have the Rust nightly toolchain and the Ink! CLI installed. The repository's README.md (lines 28-39) documents the required Rust toolchain configuration for building the 3DPass node.

1. Build Your Contract

Compile your Ink! smart contract to WebAssembly:

cargo contract build --release

This generates target/ink/<contract_name>.contract, which bundles the Wasm binary and JSON metadata.

2. Start a Local 3DPass Node

Launch a development node with the contracts pallet enabled:

cargo run --release -- --dev

The node exposes the WebSocket endpoint ws://127.0.0.1:9944 for RPC interactions.

3. Upload the Contract Code

Upload the compiled Wasm blob to the chain's storage using Alice's development account:

cargo contract upload \
    --suri //Alice \
    --url ws://127.0.0.1:9944 \
    target/ink/my_contract.contract

This submits a contracts_uploadCode extrinsic that stores the code on-chain and returns a codeHash.

4. Instantiate the Contract

Deploy an instance of the contract using a specific constructor:

cargo contract instantiate \
    --suri //Alice \
    --url ws://127.0.0.1:9944 \
    --constructor new \
    --args 42 \
    --endowment 1_000_000_000_000 \
    --gas-limit 500_000

This executes the contracts_instantiate RPC, creates the contract address, and initializes storage.

Runtime Configuration and Security

The 3DPass runtime enforces strict security boundaries for contract execution through the CallFilter type defined at lines 84-91 of runtime/src/lib.rs. By default, this is set to Nothing, meaning Ink! contracts cannot dispatch calls to other runtime pallets. This sandboxing prevents malicious contracts from manipulating core runtime functions.

To expose specific runtime functions to your contracts (such as calling into the balances or assets pallets), you must modify the CallFilter implementation:

// In runtime/src/lib.rs
type CallFilter = frame_support::traits::Everything; // Allow all (development only)
// OR
type CallFilter = frame_support::traits::EverythingExcept<SomeSpecificCalls>; // Custom filter

After modifying the filter, recompile the runtime with cargo build --release and restart your node.

Interacting with Deployed Contracts

Once instantiated, interact with your contract using the Polkadot-JS API or the contracts CLI.

Query and Transact via JavaScript

Use @polkadot/api and @polkadot/api-contract to interact with your deployed contract:

import { ApiPromise, WsProvider } from '@polkadot/api';
import { ContractPromise } from '@polkadot/api-contract';
import abi from './my_contract.json'; // Generated by cargo contract

async function interact() {
  const provider = new WsProvider('ws://127.0.0.1:9944');
  const api = await ApiPromise.create({ provider });
  
  // Replace with your contract address from instantiation
  const contract = new ContractPromise(api, abi, '5F...contractAddress');
  
  // Read-only query
  const { output } = await contract.query.getValue(
    '5F...aliceAddress', // Caller
    { value: 0, gasLimit: -1 }
  );
  console.log('Stored value:', output?.toString());
  
  // Mutable transaction
  const tx = await contract.tx.setValue(
    { value: 0, gasLimit: -1 },
    100 // New value
  );
  await tx.signAndSend('5F...aliceAddress');
}
interact();

CLI Interaction

Call contract messages directly from the command line:

cargo contract call \
    --suri //Alice \
    --url ws://127.0.0.1:9944 \
    --contract <CONTRACT_ADDRESS> \
    --message get_value \
    --gas-limit 500_000

Summary

  • The 3DPass blockchain supports Ink! smart contracts through the native Substrate contracts pallet configured in runtime/src/lib.rs (lines 78-81).
  • Deployment requires three steps: compile with cargo contract build, upload via contracts_uploadCode, and instantiate via contracts_instantiate.
  • Security defaults are strict: the CallFilter (lines 84-91) defaults to Nothing, blocking runtime calls from contracts unless explicitly whitelisted.
  • Size and weight limits are enforced through the Schedule and MaxCodeLen configurations (lines 130-132) in the runtime.
  • Interaction is standard Substrate: use Polkadot-JS UI, cargo contract CLI, or the JavaScript API to call contract messages.

Frequently Asked Questions

What Ink! version does 3DPass support?

The 3DPass runtime uses the standard pallet-contracts from the Parity Substrate repository (declared in runtime/Cargo.toml lines 127-129), which supports the latest stable Ink! language features. For specific version compatibility, check the Substrate commit referenced in the repository's Cargo.lock file, though standard Ink! 4.x and 5.x contracts compile and deploy without modification.

Why does my contract instantiation fail with "CallFiltered" errors?

The 3DPass runtime configures CallFilter = Nothing at lines 84-91 of runtime/src/lib.rs by default. This prevents contracts from calling into other runtime pallets as a security measure. If your contract logic requires dispatching calls to runtime functions (such as the Balances pallet), you must modify the CallFilter type to explicitly allow those calls and recompile the runtime.

How large can my Ink! contract be on 3DPass?

The maximum code size is defined in runtime/src/lib.rs at lines 130-132 through the MaxCodeLen configuration parameter. If your compiled Wasm blob exceeds this limit, the contracts_uploadCode transaction will fail. Optimize your contract using cargo contract build --release or split logic across multiple contracts to stay within the permitted size.

Can I deploy the same contract code multiple times?

Yes. After uploading the Wasm blob once via contracts_uploadCode, you can instantiate multiple independent contract instances using the same codeHash but different constructor arguments and endowments. Each instantiation creates a unique contract address with isolated storage, allowing you to deploy multiple copies of the same logic without re-uploading the binary.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →