Skip to main content

Using Candid

Beginner
Tutorial

Overview

As discussed in the Candid concepts page, Candid provides a language-agnostic way to interact with canisters.

While Candid is a robust, type safe, and future-proof way to facilitate communication between canisters, it is not required. Arbitrary bytes can be exchanged if all canisters that are part of the communication stream are designed to understand them (such that they are using the same underlying protocol, written in the same language, etc.).

Learn more in the Candid reference documentation.

By using Candid, you can specify input argument values and display return values from canister methods regardless of whether you interact with ICP from a terminal using the IC SDK, through a web browser, or from a program written in JavaScript, Motoko, Rust, or any other language.

As a concrete example, let’s assume there is a counter canister already deployed on the network with the following Candid interface:

service Counter : {
inc : (step: nat) -> (nat);
}

Now, let’s explore how to interact with this canister in different scenarios with the help of Candid.

The .did file

Candid types can be used to describe a service via a Candid service description file (.did file), which can either be manually written or generated from a service implementation.

Auto-generated .did files

If you write a canister in Motoko, for example, the compiler automatically generates a Candid description when you compile the program. If you use dfx, you will typically see the auto-generated .did files in the /declarations directory of your project. Since these files are auto-generated, it is recommended that they should not be manually edited. Even if you change the .did files in your project, they will be overwritten in the next dfx build.

For canisters written in Rust, the Candid extractor tool can be used with the IC CDK version 0.11.0 and newer. View the full instructions for using the Candid extractor.

Writing .did files

In other languages, you will have to write the Candid interface description manually. The most simple interface description can be found below, which defines a service with no public methods:

service : {}

To add a public method, add the method's name, followed by the data types that the method accepts and returns:

service : {
ping : () -> ();
}

In this ping method, it does not accept any types and does not return any types.

Here is an example that defines the public method greet, which accepts type text and returns type text using a query call.

service : {
"greet" : (text) -> (text) query;
};

Learn more about supported data types in the Candid reference documentation.

Splitting interface descriptions across multiple files (Motoko)

In some workflows, it may be beneficial to split interface descriptions across multiple .did files. For example, one file may be used to define type definitions that are used by several canisters, while another file is used to define the main service for one of those canisters.

This workflow is for Motoko projects.

You can import both interface descriptions into your canister with two import statements:

import A "a.did";  // Imports only type definitions
import service B "b.did"; // Imports both type definitions and main service

Interact with a service in a terminal

One of the most common ways you interact with canisters and the ICP is by using the IC SDK.

Within the IC SDK, the dfx tool provides the dfx canister call command to call a specific deployed canister—essentially a smart contract that runs on the IC—and, if applicable, a method of the service provided by the smart contract.

When you run the dfx canister call command, you can pass arguments to a method by specifying them as Candid textual values.

When you pass Candid textual values on the command-line, you can specify multiple argument values separated by commas (,) and wrapped inside parentheses. For example, specifying (42, true) represents two argument values, where the first argument is the number 42 and the second argument is a boolean value of true.

The following example illustrates using the dfx canister call command to call the counter canister service and pass arguments for the inc method:

$ dfx canister call counter inc '(42)'
(43)

To figure out how to create more complex Candid arguments, please refer to the Candid reference. And for Candid arguments too long to fit the command line, please use the --argument-file flag of dfx canister call.

You can also omit the arguments and let the IC SDK generate a random value that matches the method type. For example:

$ dfx canister call counter inc
Unspecified argument, sending the following random argument:
(1_543_454_453)

(1_543_454_454)

For more information about using dfx and the dfx canister call command, see command-line reference and dfx canister documentation.

Interact with a service from a browser

The Candid interface description language provides a common language for specifying the signature of a canister. Based on the type signature of the service offered by the smart contract, Candid provides a web interface—the Candid UI—that allows you to call canister functions for testing and debugging from a web browser without writing any frontend code.

To use the Candid web interface to test the counter canister:

  • Step 1: Find the Candid UI canister identifier associated with the counter canister using the dfx canister id __Candid_UI command.

    dfx canister id __Candid_UI

    The command displays the canister identifier for the Candid UI with output similar to the following:

      r7inp-6aaaa-aaaaa-aaabq-cai
  • Step 2: Start the canister execution environment locally by running the following command:

    dfx start --clean --background
  • Step 3: Open a browser and navigate to the address and port number specified in the dfx.json configuration file.

    By default, the local canister execution environment binds to the 127.0.0.1:4943 address and port number.

  • Step 4: Add the required canisterId parameter and the Candid UI canister identifier returned by the dfx canister id command.

    For example, the full URL should look similar to the following but with the CANDID-UI-CANISTER-IDENTIFIER that was returned by the dfx canister id command:

http://127.0.0.1:4943/?canisterId=<CANDID-UI-CANISTER-IDENTIFIER>

The browser displays a form for you to specify a canister identifier or choose a Candid description (.did) file.

candid ui select id

If you aren’t sure which canister identifier to use, you can run the dfx canister id command to look up the identifier for a specific canister name.

For example, if you want to view the functions for the counter service description, you could look up the canister identifier by running the following command:

    dfx canister id counter
  • Step 5: Specify a canister identifier or description file, then click Go to display the service description.

  • Step 6: Review the list of function calls and types defined in the program.

  • Step 7: Type a value of the appropriate type for a function or click Random to generate a value, then click Call or Query to see the result.

For more information about the tool that creates a Web interface from the Candid interface of any canister, see the Candid UI repository.

Interact with a service from a Motoko canister

If you are writing a canister in Motoko, the Motoko compiler automatically translates the signature of your canister’s top-level actor or actor class into a Candid description, and the dfx build command ensures that the service description is properly referenced where it needs to be.

For example, if you want to write a hello canister that calls the counter canister in Motoko:

import Counter "canister:Counter";
import Nat "mo:base/Nat";
actor {
public func greet() : async Text {
let result = await Counter.inc(1);
"The current counter is " # Nat.toText(result)
};
}

In this example, when the import dependency on the counter canister—the import Counter "canister:Counter" declaration—is processed by the dfx build command, the dfx build command ensures that the counter canister identifier and the Candid description are passed to the Motoko compiler correctly. The Motoko compiler then translates the Candid type into the appropriate native Motoko type. This translation enables you to call the inc method natively—as if it were a Motoko function—even if the counter canister is implemented in a different language and even if you do not have the source code for the imported canister. For additional information on the type mapping between Candid and Motoko, you can consult the supported types reference section.

The Motoko compiler and dfx build command also auto-generate the Candid description for the hello canister to allow other canisters or tools to interact with the hello canister seamlessly. The generated Candid description is located in your project build directory at .dfx/local/canisters/hello/hello.did.

Interact with a service from a Rust canister

If you write a canister in Rust, the dfx build command ensures that the service description is properly referenced where it needs to be. However, you need to write the Candid service description manually following the conventions described in the Candid specification.

For example, if you want to write a hello canister that calls the counter canister in Rust:

use ic_cdk_macros::*;

#[import(canister = "counter")]
struct Counter;

#[update]
async fn greet() -> String {
let result = Counter::inc(1.into()).await;
format!("The current counter is {}", result)
}

When the import macro on the counter canister—the #[import(canister = "counter")] declaration—is processed by the dfx build command, the dfx build command ensures that the counter canister identifier and the Candid description are passed to the Rust CDK correctly. The Rust CDK then translates the Candid type into the appropriate native Rust type. This translation enables you to call the inc method natively—as if it were a Rust function—even if the counter canister is implemented in a different language and even if you do not have the source code for the imported canister. For additional information on the type mapping between Candid and Rust, you can consult the supported types reference section.

For other canisters and tools to interact with the hello canister, you need to manually create a .did file:

service : {
greet : () -> (text);
}

There is also an experimental feature to generate a Candid service description automatically, see this test case as an example.

For additional information and libraries to help you create Candid services or canisters in Rust, see the documentation for the Candid crate, Rust CDK examples and the Rust tutorials.

Interact with a service from JavaScript

The dfinity/agent npm package includes support for importing canisters using Candid.

For example, if you want to call the counter canister, you can write the following JavaScript program:

import counter from 'ic:canisters/counter';
import BigNumber from 'bignumber.js';
(async () => {
const result = await counter.inc(new BigNumber(42));
console.log("The current counter is " + result.toString());
})();

When the import dependency of counter canister is processed by the dfx build command and the webpack configuration, this processing ensures that the canister identifier and the Candid description are passed to the JavaScript program correctly. Behind the scenes, the Candid service description is translated into a JavaScript module, located at .dfx/local/canister/counter/counter.did.js, by dfx build. The dfinity/agent package then translates the Candid type into native JavaScript values and enables you to call the inc method natively—as if it were a JavaScript function—even if the counter canister is implemented in a different language and even if you do not have the source code for the imported canister. For additional information on the type mapping between Candid and JavaScript, you can consult the supported types reference section.

Create a new Candid implementation

In addition to the Candid implementations for Motoko, Rust, and JavaScript, there are community-supported Candid libraries for the following host languages:

If you want to create a Candid implementation to support a language or tool for which an implementation is not currently available, you should consult the Candid specification.

If you add a Candid implementation for a new language or tool, you can use the official Candid test data to test and verify that your implementation is compatible with Candid, even in slightly more obscure corner cases.