Using third-party canisters
Third-party canisters are canisters created by DFINITY or ICP community members that provide an important functionality that other developers may want to integrate within their own projects. It is important to test integrations with these canisters locally to:
- Validate the accuracy of the integration and other canister code.
- Test without paying cycles.
- Use non-production data and environments.
- Execute operations faster when run locally.
To integrate with third-party canisters and test them within your project, you can either:
If the canister's Candid and Wasm files are publicly available, the URLs of the files can be referenced directly in the project's
dfx.jsonfile.If the canister is configured as
pullable, it can be pulled into a project withdfx deps.
Using a canister's Candid and Wasm files directly
If a canister's Candid and Wasm files are publicly available, you can reference them directly in a project's dfx.json file.
If the canister is deployed to the mainnet, you can also specify the remote canister ID. When your project is deployed to the mainnet, it will use the mainnet canister with that ID. When your project is deployed locally, dfx will download the specified Candid and Wasm files and create a new local canister using them.
{
"canisters": {
"icp_ledger_canister": {
"type": "custom",
"candid": "https://raw.githubusercontent.com/dfinity/ic/aba60ffbc46acfc8990bf4d5685c1360bd7026b9/rs/ledger_suite/icp/ledger.did",
"wasm": "https://download.dfinity.systems/ic/aba60ffbc46acfc8990bf4d5685c1360bd7026b9/canisters/ledger-canister.wasm.gz",
"remote": {
"id": {
"ic": "ryjl3-tyaaa-aaaaa-aaaba-cai"
}
},
...
Alternatively, you can reference Candid and Wasm files stored in your local environment:
{
"canisters": {
"icp_ledger_canister": {
"type": "custom",
"candid": "ledger.did",
"wasm": "ledger-canister.wasm.gz",
"remote": {
"id": {
"ic": "ryjl3-tyaaa-aaaaa-aaaba-cai"
}
},
...
An example of using this method is the XRC demo project.
Using dfx deps to pull third-party canisters
The command and workflow can be used to pull third-party canisters into a project that is deployed locally or on the mainnet.
In this workflow, a service provider configures a canister to be pullable and then deploys the canister to the mainnet. A service provider can be anyone, such as a community developer creating a public, third-party canister.
Then a service consumer can pull the canister as a dependency directly from the mainnet using dfx deps and deploy the dependency locally.
Determining if a canister should be pullable
First the canister must be configured to be pullable. Developers must ask the question of whether the canister should be pullable. Canisters should be pullable if it provides a public service at a static canister ID. If it depends on other canisters, those dependencies should also be pullable.
Configuring a canister to be pullable
A service provider must configure a canister to be pullable by setting the following configuration details in the project's dfx.json file:
wasm_url: A URL used to download the canister Wasm module, which will be deployed locally.wasm_hash: A SHA256 hash of the Wasm module located atwasm_url. This field is optional. In most cases, the Wasm module atwasm_urlwill be the same as the onchain Wasm module. This means thatdfxcan read the state tree to obtain and verify the module hash. In some cases, the Wasm module atwasm_urlis not the same as the onchain Wasm module. For example, the Internet Identity canister provides adevelopmentvariant to be integrated locally. In these cases,wasm_hashprovides the expected hash, anddfxverifies the downloaded Wasm against this.
If the wasm_hash of the Wasm module at wasm_url does not match, dfx will abort with an error message indicating that there is a hash mismatch. In this scenario, the service consumer should contact the service provider. It is the responsibility of the service provider to assure that the correct Wasm module can be downloaded from the wasm_url.
dependencies: An array of canister IDs (Principal) of direct dependencies.init_guide: A message to guide consumers on how to initialize the canister.
{
"canisters": {
"service": {
"type": "motoko",
"main": "src/main.mo",
"pullable": {
"wasm_url": "http://example.com/a.wasm",
"wasm_hash": "d180f1e232bafcee7d4879d8a2260ee7bcf9a20c241468d0e9cf4aa15ef8f312",
"dependencies": [
"yofga-2qaaa-aaaaa-aabsq-cai"
],
"init_guide": "A natural number, e.g. 10."
}
}
}
}
The Wasm module of a pullable canister must be hosted via a public URL where service consumers can download it, such as GitHub.
The pullable object will be serialized as a part of the dfx metadata and attached to the Wasm.
Canister metadata requirements
A third-party canister used in production should have public dfx metadata and public or private candid:service and candid:args metadata. All metadata sections are handled by dfx when the canister is built.
Deployment process
Service providers should use the following deployment process to deploy their pullable canister and make it available to others:
Step 1: Deploy the canister to the mainnet.
dfx deploy <canister-name> --network ic
Step 2: If you're using GitHub,
git tagandGitHub releasewith the commands:
git tag 0.1.0
git push --tags
Step 3: Attach the Wasm to the release assets.
Edit the GitHub release and attach the deployed Wasm as a release asset. The deployed Wasm file will be located at:
.dfx/ic/canisters/<CANISTER_NAME>/<CANISTER_NAME>.wasm
Automating the service provider process in CI
An example CI configuration demonstrates how to use a GitHub Action to automate the deploy routine described above.
The workflow with CI will follow these steps:
- Push a git tag and wait for the GitHub release to complete.
- Download the canister Wasm from the release assets (
wget https://github.com/lwshang/pullable/releases/latest/download/service.wasm). - Install (upgrade) the canister using the downloaded Wasm (
dfx canister --network ic install service --wasm service.wasm --argument '(1 : nat)' --mode upgrade).
Pulling a third-party canister into your project
The following workflow can be used for consumers to import a pullable canister as a dependency.
Step 1: Declare "pull" dependencies in
dfx.json.
An example dfx.json in which the consumer is developing a canister named "dapp" that has two pull dependencies can be found below:
- "dep_b" has a canister ID of
yhgn4-myaaa-aaaaa-aabta-caion the mainnet. - "dep_c" has a canister ID of
yahli-baaaa-aaaaa-aabtq-caion the mainnet.
{
"canisters": {
"dapp": {
"type": "motoko",
"main": "src/main.mo",
"dependencies": [
"dep_b", "dep_c"
]
},
"dep_b": {
"type": "pull",
"id": "yhgn4-myaaa-aaaaa-aabta-cai"
},
"dep_c": {
"type": "pull",
"id": "yahli-baaaa-aaaaa-aabtq-cai"
}
}
}
Step 2: Pull the dependencies using the
dfx deps pullcommand.
dfx deps pull connects to the mainnet by default (--network ic). You can choose other networks as usual, e.g., --network local.
Step 3: Set init arguments using
dfx deps init.
Running the command dfx deps init will iterate over all dependencies in the pulled.json file and set an empty argument for any that do not need an init argument. Then, it will print the list of dependencies that do require an init argument.
Running the command dfx deps init <CANISTER> --argument <ARGUMENT> will set the init argument for an individual dependency. The init arguments will be recorded in deps/init.json.
To set the init arguments:
dfx deps init
Output
WARN: The following canister(s) require an init argument. Please run `dfx deps init <NAME/PRINCIPAL>` to set them individually:
yofga-2qaaa-aaaaa-aabsq-cai
yahli-baaaa-aaaaa-aabtq-cai (dep_c)
If you try to set an init argument for an individual dependency without an argument, it will result in the following error:
Error: Canister yofga-2qaaa-aaaaa-aabsq-cai requires an init argument. The following info might be helpful:
init_guide => A natural number, e.g., 10.
candid:args => (nat)
To set an init argument with an argument using the --argument flag:
dfx deps init <canister-id> --argument 10
dfx deps init deps_c --argument 20
The resulting generated file init.json will have the following content:
{
"canisters": {
"yofga-2qaaa-aaaaa-aabsq-cai": {
"arg_str": "10",
"arg_raw": "4449444c00017d0a"
},
"yhgn4-myaaa-aaaaa-aabta-cai": {
"arg_str": null,
"arg_raw": null
},
"yahli-baaaa-aaaaa-aabtq-cai": {
"arg_str": "20",
"arg_raw": "4449444c00017d14"
}
}
}
Step 4: Deploy the pulled dependencies in your local environment using the
dfx deps deploycommand.
dfx deps deploy
Running the dfx deps deploy command will:
- Create the dependencies in your local environment with the same mainnet canister ID.
- Then, it will install the downloaded Wasm with the init arguments in the
init.jsonfile.
You can also specify the canister name or principal to deploy one particular dependency.
dfx deps deploy always creates the canister with the anonymous identity so that dependencies and application canisters will have different controllers. It will also always install the canister in "reinstall" mode so that the canister status will be discarded.
Frequently asked questions
Why download the Wasm into shared cache instead of a project subfolder?
It is not encouraged to include binary files in version control. On the Internet Computer, every canister only has one latest version running on the mainnet. Service consumers should integrate with that latest version.
dfx deps pull always gets the latest dependencies instead of locking on a particular run. Every pulled canister has the latest version in the shared cache and can be reused by different projects.
Should I include the
deps/folder in version control?
Yes. deps/ files enable the dependent canister to build and get IDE support. If the required Wasm files are also available in the shared cache, all applications and dependencies can be deployed and tested integrally.
Considering a canister developer team:
- Developer 1 follows the service consumer workflow and includes all generated
deps/files in source control. - Developer 2 pulls the branch by Developer 1 and runs the
dfx deps pullcommand again.- If the
pulled.jsonhas no change, then all dependencies are still up to date. Developer 2 can rundfx deps deploywithout setting init arguments again. - If there are changes in
pulled.json, Developer 2 can try to rundfx deps deployto see if all init arguments are still valid. Then Developer 2 can rundfx deps initif necessary and update source control.
- If the
These files also help CI to detect outdated dependencies.