Development workflow
The Internet Computer Protocol (ICP) accepts and executes smart contracts in the WebAssembly (Wasm) binary format. In theory, developers could write valid smart contracts directly in the Wasm bytecode. Since that would be too tedious and time consuming, the standard practice is to write smart contract code in a higher-level language, such as JavaScript/TypeScript, Motoko, Python, or Rust, then compile it into Wasm.
The primary developer tool in the ICP ecosystem is dfx
.
It is a command line multi-tool that assists the developer throughout the entire development process, starting from the generation of developer keys and setting up a new project, to compiling, deploying, and managing smart contracts.
By default, dfx
generates a project structure consisting of two smart contracts: the frontend canister and the backend canister.
The frontend canister contains web assets such as JavaScript, HTML, CSS, and images that are served to the browser.
The actual program logic of the smart contract is defined in the backend canister.
The configuration with a frontend and a backend canister is just a convention. It is possible to write a single canister that contains both the program logic and hosts the web assets.
Since smart contracts can be written in different languages, it is important that they all agree on the binary format in which they accept their arguments and return results. This common format allows users and smart contracts to call other smart contracts regardless of the language they were written in. In traditional programming, this concept is known as Application Binary Interface (ABI). ICP has its own ABI language called Candid, which is similar to JSON or Protobuf, but tailored for ICP.
During the build process, dfx
uses the backend ABI to automatically generate JavaScript boilerplate code for the frontend.
Under the hood, the boilerplate code uses a library called agent-js to encode Candid values and make HTTP requests from the browser to the backend canister.
This is similar to web3.js
in Ethereum.
The Wasm standard defines only the instructions and the memory of the Wasm virtual machine; how a Wasm program interacts with other programs and users is left up to the host that embeds the virtual machine. ICP as a Wasm host provides a set of functions that Wasm code can use to read the incoming arguments, call the system and other smart contracts, and return results. Collectively these functions are referred to as the System API. Developers are not intended to use the System API directly because that would be too low-level and error-prone. Instead, developers should use language-specific Canister Development Kit (CDK) libraries that are high-level wrappers around the System API.
Development environments
There are several different environments that can be used to write, deploy, and test smart contracts. Each environment has different characteristics and benefits.
dfx
:dfx
is the 'native' way to develop smart contracts. It is a command-line tool that enables canister creation and deployment on macOS and Linux systems.dfx
is developed and maintained by DFINITY, with new features and improvements being released regularly.Windows Subsystem for Linux (WSL):
dfx
does not natively support Windows operating systems. To usedfx
locally on a Windows machine, you can install the Windows Subsystem for Linux. Since this workflow isn't the primary development testing environment thatdfx
is designed for, there may be bugs or errors when usingdfx
on WSL. Learn more about common WSL errors and troubleshooting.Gitpod: Gitpod is an open-source cloud-based development environment that can be used to develop in the web browser without downloading tools or dependencies to your local machine. Gitpod can be used for long-term projects, as it does not use a temporary resource allocation system such as the playground. Smart contract projects can be developed using Gitpod by spawning a new Gitpod workspace and configuring it to download and install
dfx
. The Hello, world! Gitpod example can be used to learn more about Gitpod configuration.GitHub Codespaces: GitHub Codespaces is a proprietary cloud-based environment similar to Gitpod that can be used to develop smart contracts from within a web browser. To develop smart contracts, you can deploy a new codespace environment and download
dfx
. The Hello, world! GitHub Codespaces example can be used to learn more about using codespaces.
Differences between Gitpod and GitHub Codespaces
- Open-source.
- Choose a repository to deploy into the Gitpod. Must pick an existing repo.
- Choose from several different IDEs to be used in the workspace (VSCode, Terminal, PyCharm, etc).
- Supports snapshots.
- GitLab and Bitbucket integrations.
- Proprietary GitHub software.
- Do not need to select an existing repo to deploy into the Codespace. You can choose between several different template development environments that come with pre-installed dependencies, or choose an existing repo.
- Cannot choose between different IDEs to be used in the Codespace.
- Cannot choose between different workspace resources.
- Does not support snapshots.
- Does not integrate with GitLab or Bitbucket.
Using containers: Developer containers, such as Docker containers, can be used for creating smart contracts and deploying them locally in a containerized environment. Containers can be useful for running several different projects simultaneously in your local environment that may be using different versions of
dfx
and different dependencies.Juno: Juno is a blockchain-as-a-service platform that can be used to create and deploy smart contracts on ICP without using the traditional
dfx
development workflow. Instead, Juno manages ICP-specific tooling for you on the backend through a Juno 'satellite', allowing you to deploy on-chain dapps by interacting with just the Juno web console or through the CLI.Playground: The playground is a sandbox environment that can be used for testing smart contract code in any language. It is only designed for temporary, short-term testing, as there is a 20 minute limit for each deployment. After 20 minutes, the canister will be deleted so the resources can be used for another deployment.
Deploying smart contracts
A developer can deploy smart contracts either on a local testnet or on the mainnet.
The local testnet comes built-in with dfx
and can be run with the dfx start
command.
The local testnet consists of a single node – the local machine.
Note that as of writing, ICP doesn’t have an official public testnet.
In order to deploy smart contracts on the mainnet, the developer needs either ICP tokens or cycles.
When using ICP tokens, dfx
automatically converts the necessary amount of tokens to cycles since all operations with smart contracts require payment in cycles.
See this guide for more information on how to obtain cycles.
The command for deploying smart contracts is dfx deploy
.
It automatically builds the source code into Wasm binaries and deploys them to the target network.
By default the command targets the local testnet.
Passing an additional --network ic
parameter instructs commands to use the mainnet.
There is no need to specify the URL of a node to connect to because dfx
is already pre-configured with URLs of the boundary (RPC) nodes of the mainnet.
However, it is possible to change the URL by editing the dfx configuration file.
Calling smart contracts
There are three ways to call the functions of the backend smart contract:
- Use the browser to load the webpage hosted in the frontend canister and use the UI of the webpage to interact with the backend canister.
Under the hood, the UI uses JavaScript and
agent-js
to send messages to the backend smart contract. This is the standard way to interact with the smart contract that regular users would also use. - Use the
dfx canister call
command and pass the input arguments as command line arguments. Under the hood,dfx
uses a Rust library called ic-agent to send messages to the smart contract. - Write an off-chain program that uses an agent library to send messages to the smart contract.