Skip to content

Getting Started

Before we dive deep into the structure of a contract and the SDK's semantics, we require some background information about the semantics enforced by ParallelChain Mainnet. The full capabilities of ParallelChain Mainnet continue to grow with active developers and a growing community. Let's see how smart contracts are called and processed by a node in the form of transactions.

First, the ParallelChain Client (pchain_client) will submit a transaction when making a smart contract call to the validating node using a serialization crate pchain-types. The node's RPC service and mempool will process the list of transactions and check for validity and correctness.

Transactions that fail the validation check may not be included in a block. The transactions are then packed in a block and sent to the execution engine in the node to execute the transactions.

The executor will call wasmer (Web Assembly Engine) which provides an isolated context to perform the execution. This enables the smart contract code to read the current state of the blockchain and interact with it. However, the execution results are temporarily stored and subject to further checks before the block may be committed or rolled back on error.

wasmer also computes the gas fees through a metering module, which is responsible for limiting the execution to the amount of gas paid. Each transaction returns a result in the form of receipts along with logs. The receipts (with or without log), transactions and block itself will contain its hash (or Merkle proof) and will be all included in the same block.

Smart Contract Development Kit

Smart Contract can be created by using ParallelChain SDK. Example contracts can be found in example-smart-contracts.

A ParallelChain Smart Contract is a rust crate that imports the SDK. It uses the SDK's features to interact with the blockchain. The folder structure of a typical ParallelChain Mainnet Smart Contract looks like this:

my_first_contract
├── src/
│   └── lib.rs # The main source code of your smart contract.  
└── Cargo.toml # You import your packages and the SDK here

For more information on Rust's crate system, see Rust Book Chapter 7: Packages and Crates

Please specify the dependency in Cargo.toml for using SDK by fetching from crates.io or repository in Github.

[lib]
crate-type = ["cdylib"]

[dependencies]
pchain-sdk = "0.4"
[lib]
crate-type = ["cdylib"]

[dependencies]
pchain-sdk = { git = "https://github.com/parallelchain-io/pchain-sdk" }

Implement Smart Contract

Let's start with a sample smart contract for illustration in this guide. The smart contract code is written in the file lib.rs.

use pchain_sdk::{call, contract, contract_methods};

#[contract]
struct MyContract { }

#[contract_methods]
impl MyContract {

    #[call]
    fn hello_from(name :String) -> u32 {
        pchain_sdk::log(
            "topic: Hello From".as_bytes(), 
            format!("Hello, Contract. From: {}", name).as_bytes()
        );
        name.len() as u32
    }
}

The details about the macros (e.g. contract, contract_methods, call) used in the code can be found in later sections Contract Methods and Contract Storage. Let's skip knowing them first, and continue reading about building and deploying the contract in following sections.

Building The Contract With pchain_compile

pchain_compile is a CLI build tool for smart contract developers to build their source code to WASM binaries for deployment on ParallelChain Mainnet.

The WASM binary generated from a generic cargo build process has the following disadvantages:

  • The build process is not toolchain version agnostic i.e. it does not guarantee uniform WASM binaries with different versions of compiler and rust crate dependencies. This will make it difficult for smart contract developers to verify their source code on the blockchain.

  • The build process generates a large file size, which leads to higher gas costs for deployment and contract calls.

pchain_compile solves these shortcomings by:

  • Executing the build process in a docker environment with pre-defined toolchain versioning.

  • Optimizing and compressing the size of the WASM binary with the help of wasm-snip and wasm-opt packages.

Download pchain_compile

pchain_compile supports Linux, macOS and Windows. Depending on the operating system, they can be downloaded from Assets of ParallelChain Lab's Git Hub release page.

The binary can also be installed and built by executing the following commands:

cargo install pchain_compile

Pre-requisites

pchain_compile builds the source code in a docker environment. To know more about Docker and install it, refer to the official instructions.

Build WASM Binary

In order to build a WASM binary of the smart contract, run the following command:

./pchain_compile build --source <PATH_TO_SMART_CONTRACT_CODE>
./pchain_compile.exe build --source <PATH_TO_SMART_CONTRACT_CODE>

If you installed pchain_compile with cargo install, you can simply run:

pchain_compile build --source <PATH_TO_SMART_CONTRACT_CODE>

A .wasm file will be generated in the current directory.

Deploying a Smart Contract

You can deploy the contract using the pchain_client command line tool. You should make sure to know your latest account nonce before submitting the transaction.

./pchain_client transaction create \
--nonce <NONCE> \
--gas-limit <GAS_LIMIT> \
--max-base-fee-per-gas <MAX_BASE_FEE_PER_GAS> \
--priority-fee-per-gas <PRIORITY_FEE_PER_GAS> \
deploy \
--contract-code <WASM_BINARY_PATH> \
--cbi-version <CBI_VERSION> 
./pchain_client.exe transaction create `
--nonce <NONCE> `
--gas-limit <GAS_LIMIT> `
--max-base-fee-per-gas <MAX_BASE_FEE_PER_GAS> `
--priority-fee-per-gas <PRIORITY_FEE_PER_GAS> `
deploy `
--contract-code <WASM_BINARY_PATH> `
--cbi-version <CBI_VERSION> 

You can refer to the instruction and example arguments in Create & Submit Transaction to create and submit a transaction through pchain-client.

Checking Contract In State

To verify that the smart contract is deployed correctly, you can run the command query with the flag contract. It queries the state of the blockchain and saves the wasm code as code.wasm in the current directory. If you want to store the contract file in your preferred location, you need to provide the flag destination to specify the path with your preferred file extension:

./pchain_client query contract --address <CONTRACT_ADDRESS>
./pchain_client.exe query contract --address <CONTRACT_ADDRESS>

Calling Contract

Suppose you have already deployed a contract that contains a call method named hello_from as mentioned in previous section Implement Smart Contract:

#[call]
fn hello_from(name :String) -> u32

To invoke this contract by using pchain_client, you can prepare a JSON file that contains a list of arguments and matches with the call method. For example, this call method takes a string argument. Then, the content of the JSON file should be as follows:

{
    "arguments": [
        {"argument_type":"String", "argument_value": "\"Alice\""}
    ]
}

This JSON file will be used in creating a transaction with Call Command.

Submit Transaction With Call Command

To call a smart contract, submit a transaction with your account nonce and contract address using the pchain_client.

Here is the command to call a contract:

./pchain_client transaction create \
--nonce <NONCE> \
--gas-limit <GAS_LIMIT> \
--max-base-fee-per-gas <MAX_BASE_FEE_PER_GAS> \
--priority-fee-per-gas <PRIORITY_FEE_PER_GAS> \
call \
--target <CONTRACT_ADDRESS> \
--method <contract_METHOD> \
--arguments <CALL_ARGUMENT_FILE_PATH_WITH_FILE_NAME>
--amount <AMOUNT_TO_CONTRACT> \
./pchain_client.exe transaction create `
--nonce <NONCE> `
--gas-limit <GAS_LIMIT> `
--max-base-fee-per-gas <MAX_BASE_FEE_PER_GAS> `
--priority-fee-per-gas <PRIORITY_FEE_PER_GAS> `
call `
--target <CONTRACT_ADDRESS> `
--method <contract_METHOD> `
--arguments <CALL_ARGUMENT_FILE_PATH_WITH_FILE_NAME> `
--amount <AMOUNT_TO_CONTRACT>

The command argument method is the name of the method with macro #[call] in the code. Take the contract in Implement Smart Contract as example, "hello_from" should be set as the value of the command argument method.

The command argument arguments is the JSON file that contains the method arguments to the method being called in the contract. The way to create this JSON file is described in previous section Calling Contract.

The gas limit required for the transaction depends on the complexity of the smart contract. For safety reasons, you can always set a higher gas limit. You can also test contract calls on testnet to reassure.

You can refer to the instruction and example arguments in Create & Submit Transaction to create and submit a transaction through pchain-client.

To query the resulting receipt of the transaction,

./pchain_client query tx --hash <TRANSACTION_HASH> 
./pchain_client.exe query tx --hash <TRANSACTION_HASH>

The commands stored in transaction and command receipts in receipt are following the same order. That means you can always find the corresponding transaction from a command receipt.

Parse Call Result

To parse the response from the contract method, represented in the field named return value , which is in CallResult format, you can use the parse call-result command in ParallelChain Client.

For example, if the contract method returns a u32 integer, the return value is "BQAAAA" you can parse the CallResult data structure using the --data-type u32 flag:

pchain_client parse call-result --value BQAAAA --data-type u32
./pchain_client.exe parse call-result --value BQAAAA --data-type u32

The output will be the parsed value of the CallResult, which in this case is 5. For more details, you can use the help command to see the usage of the tool or take a look at the example argument,json.