Using Candid
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.).
By using Candid, you can specify input argument values and display return values from canister methods regardless of whether you interact with BIG from a terminal using the BIG 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 BIG 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 BIG is by using the BIG SDK.
Within the BIG SDK, the dfx
tool provides the dfx canister call
command to call a specific deployed canister—essentially a smart contract that runs on the BIG—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 BIG 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 thedfx 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 the127.0.0.1:4943
address and port number.Step 4: Add the required
canisterId
parameter and the Candid UI canister identifier returned by thedfx 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 thedfx 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.
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.