icp-cli
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseICP CLI
ICP CLI
What This Is
什么是ICP CLI
The command-line tool builds and deploys applications on the Internet Computer. It replaces the legacy tool with YAML configuration, a recipe system for reusable build templates, and an environment model that separates deployment targets from network connections. Never use — always use .
icpdfxdfxicpicpdfxicpdfxPrerequisites
前置要求
- For Rust canisters:
rustup target add wasm32-unknown-unknown - For Motoko canisters: and
npm i -g ic-mopsversion defined inmoc(templates include this; for manual projects addmops.tomlwith[toolchain])moc = "<version>" - For frontend assets: Node.js >= 20
- 针对Rust canister:
rustup target add wasm32-unknown-unknown - 针对Motoko canister:,且
npm i -g ic-mops中定义了mop.toml版本(模板默认已包含,手动创建的项目需要添加moc配置项,指定[toolchain])moc = "<版本号>" - 针对前端资产:Node.js >= 20
Common Pitfalls
常见误区
-
Usinginstead of
dfx. Theicptool is legacy. All commands havedfxequivalents — see the migration table below. Never generateicpcommands or referencedfxdocumentation. Configuration usesdfx, noticp.yaml— and the structure differs: canisters are an array of objects, not a keyed object.dfx.json -
Usingto deploy to mainnet. icp-cli uses environments, not direct network targeting. The correct flag is
--network ic(short for-e ic).--environment icbash# Wrong icp deploy --network ic # Correct icp deploy -e icNote:/-ntargets a network directly and works with canister IDs (principals). Use--network/-ewhen referencing canisters by name. For token and cycles operations, use--environmentsince they don't reference project canisters.-n -
Using a recipe without a version pin. Always pin recipe versions to avoid breaking changes. Unpinned recipes resolve towhich can change at any time. Official recipes are hosted at dfinity/icp-cli-recipes.
latestyaml# Wrong — unpinned, may break recipe: type: "@dfinity/rust" # Correct — pinned version recipe: type: "@dfinity/rust@v3.2.0" -
Writing manual build steps when a recipe exists. Official recipes handle Rust, Motoko, and asset canister builds. Use them instead of writing shell commands:yaml
# Unnecessary — use a recipe instead build: steps: - type: script commands: - cargo build --target wasm32-unknown-unknown --release - cp target/.../backend.wasm "$ICP_WASM_OUTPUT_PATH" # Preferred recipe: type: "@dfinity/rust@v3.2.0" configuration: package: backend -
Not committingto version control. Mainnet canister IDs are stored in
.icp/data/. Losing this file means losing the mapping between canister names and on-chain IDs. Always commit.icp/data/mappings/<environment>.ids.json— never delete it. Add.icp/data/to.icp/cache/(it is ephemeral and rebuilt automatically)..gitignore -
Usinginstead of
icp identity use. The dfx commandicp identity defaultbecamedfx identity use. Similarly,icp identity defaultbecamedfx identity get-principal, andicp identity principalbecamedfx identity remove.icp identity delete -
Confusing networks and environments. A network is a connection endpoint (URL). An environment combines a network + canisters + settings. You deploy to environments (), not networks. Multiple environments can target the same network with different settings (e.g., staging and production both on
-e).ic -
Forgetting that local networks are project-local. Unlike dfx which runs one shared global network, icp-cli runs a local network per project. You must runin your project directory before deploying locally. The local network auto-starts with system canisters and seeds accounts with ICP and cycles.
icp network start -d -
Not specifying build commands for asset canisters. dfx automatically runsfor asset canisters. icp-cli requires explicit build commands in the recipe configuration:
npm run buildyamlcanisters: - name: frontend recipe: type: "@dfinity/asset-canister@v2.1.0" configuration: dir: dist build: - npm install - npm run build -
Expectingor
output_env_filewith canister IDs. dfx writes canister IDs to a.envfile (.env) viaCANISTER_ID_BACKEND=.... icp-cli does not generateoutput_env_filefiles. Instead, it injects canister IDs as environment variables (.env) directly into canisters duringPUBLIC_CANISTER_ID:<name>. Frontends read these from theicp deploycookie set by the asset canister. Removeic_envfrom your config and any code that readsoutput_env_filefromCANISTER_ID_*— use the.envcookie instead (see Canister Environment Variables below).ic_env -
Expectingfor TypeScript bindings. icp-cli does not have a
dfx generateequivalent. Usedfx generate(a Vite plugin) to generate TypeScript bindings from@icp-sdk/bindgenfiles at build time. The.didfile must exist on disk — either commit it to the repo, or generate it with.didfirst (recipes auto-generate it whenicp buildis not specified). See Binding Generation below.candid -
Misunderstanding Candid file generation with recipes. When using the Rust or Motoko recipe:
- If is specified: the file must already exist (checked in or manually created). The recipe uses it as-is and does not generate one.
candid - If is omitted: the recipe auto-generates the
candidfile from the compiled WASM (via.didfor Rust,candid-extractorfor Motoko). The generated file is placed in the build cache, not at a predictable project path.moc
For projects that need afile on disk (e.g., for.did), the recommended pattern is: generate the@icp-sdk/bindgenfile once, commit it, and specify.didin the recipe config. To generate it manually:candidRust — build the WASM first, then extract the Candid interface:bashcargo install candid-extractor # one-time setup icp build backend candid-extractor target/wasm32-unknown-unknown/release/backend.wasm > backend/backend.didMotoko — usedirectly with themocflag:--idlbash$(mops toolchain bin moc) --idl $(mops sources) -o backend/backend.did backend/app.mo - If
-
使用而非
dfx。icp是旧版工具,所有命令都有对应的dfx版本,可参考下方的迁移对照表。请勿生成icp命令或引用dfx文档。配置文件使用dfx而非icp.yaml,且结构存在差异:canisters是对象数组,不是键值对象。dfx.json -
使用部署到主网。icp-cli使用环境(environment)而非直接指定网络,正确的参数是
--network ic(是-e ic的缩写)。--environment icbash# 错误写法 icp deploy --network ic # 正确写法 icp deploy -e ic注意:/-n用于直接指定网络,搭配canister ID(principal)使用。当通过名称引用canister时,请使用--network/-e。针对代币和cycles操作请使用--environment,因为这类操作不涉及项目内的canister引用。-n -
使用未锁定版本的recipe。请始终锁定recipe的版本以避免破坏性变更,未锁定版本的recipe会默认拉取随时可能变动的版本。官方recipe托管在 dfinity/icp-cli-recipes。
latestyaml# 错误写法 —— 未锁定版本,可能出现兼容性问题 recipe: type: "@dfinity/rust" # 正确写法 —— 锁定版本 recipe: type: "@dfinity/rust@v3.2.0" -
已有对应recipe的场景下手动编写构建步骤。官方recipe已经支持Rust、Motoko和资产canister的构建,无需手动编写shell命令:yaml
# 不必要的写法 —— 直接使用recipe即可 build: steps: - type: script commands: - cargo build --target wasm32-unknown-unknown --release - cp target/.../backend.wasm "$ICP_WASM_OUTPUT_PATH" # 推荐写法 recipe: type: "@dfinity/rust@v3.2.0" configuration: package: backend -
未将提交到版本控制。主网canister ID存储在
.icp/data/中,丢失该文件会导致canister名称和链上ID的映射关系丢失。请始终提交.icp/data/mappings/<环境名>.ids.json目录,不要删除它。将.icp/data/添加到.icp/cache/中(该目录是临时缓存,会自动重建)。.gitignore -
使用而非
icp identity use。原icp identity default命令对应的icp命令是dfx identity use。同理,icp identity default对应dfx identity get-principal,icp identity principal对应dfx identity remove。icp identity delete -
混淆网络和环境的概念。网络(network)是连接端点(URL),环境(environment)则是网络+canister+配置的组合。你应该部署到环境(使用参数)而非网络。多个环境可以指向同一个网络但使用不同的配置(比如预发环境和生产环境都使用
-e主网)。ic -
不知道本地网络是项目级别的。和dfx运行全局共享的本地网络不同,icp-cli为每个项目运行独立的本地网络。在本地部署前你必须在项目目录下执行,本地网络会自动启动系统canister,并为测试账户注入ICP和cycles。
icp network start -d -
未为资产canister指定构建命令。dfx会自动为资产canister执行,而icp-cli需要在recipe配置中显式指定构建命令:
npm run buildyamlcanisters: - name: frontend recipe: type: "@dfinity/asset-canister@v2.1.0" configuration: dir: dist build: - npm install - npm run build -
期望通过或者
output_env_file文件获取canister ID。dfx会通过.env配置将canister ID写入output_env_file文件(格式为.env),而icp-cli不会生成CANISTER_ID_BACKEND=...文件,它会在.env时直接将canister ID作为环境变量(格式为icp deploy)注入到canister中。前端可以通过资产canister设置的PUBLIC_CANISTER_ID:<canister名称>Cookie读取这些变量。请移除配置中的ic_env和所有从output_env_file读取.env的代码,改用CANISTER_ID_*Cookie(参考下方的Canister环境变量章节)。ic_env -
期望存在类似的命令生成TypeScript绑定。icp-cli没有对应的
dfx generate命令,请使用dfx generate(Vite插件)在构建时从@icp-sdk/bindgen文件生成TypeScript绑定。.did文件必须存在于本地磁盘——要么提交到代码仓库,要么先执行.did生成(未指定icp build配置时recipe会自动生成)。参考下方的绑定生成章节。candid -
对recipe生成Candid文件的逻辑理解有误。使用Rust或Motoko recipe时:
- 如果指定了配置:对应文件必须已经存在(已提交到仓库或手动创建),recipe会直接使用该文件,不会自动生成。
candid - 如果省略了配置:recipe会从编译后的WASM自动生成
candid文件(Rust使用.did,Motoko使用candid-extractor),生成的文件存放在构建缓存中,不会存放在固定的项目路径下。moc
如果项目需要本地磁盘上的文件(比如供.did使用),推荐的做法是:先生成一次@icp-sdk/bindgen文件,提交到仓库,然后在recipe配置中指定.did路径。手动生成的方法如下:candidRust —— 先构建WASM,然后提取Candid接口:bashcargo install candid-extractor # 仅需执行一次安装 icp build backend candid-extractor target/wasm32-unknown-unknown/release/backend.wasm > backend/backend.didMotoko —— 直接使用的moc参数:--idlbash$(mops toolchain bin moc) --idl $(mops sources) -o backend/backend.did backend/app.mo - 如果指定了
How It Works
工作原理
Project Creation
项目创建
icp new--subfolder--defineicp new--subfolder--defineBuild → Deploy → Sync
构建 → 部署 → 同步
text
Source Code → [Build] → WASM → [Deploy] → Running Canister → [Sync] → Configured Stateicp deploy- Build — Compile canisters to WASM (via recipes or explicit build steps)
- Deploy — Create canisters (if new), apply settings, install WASM
- Sync — Post-deployment operations (e.g., upload assets to asset canisters)
Run phases separately for more control:
bash
icp build # Build only
icp deploy # Full pipeline (build + deploy + sync)
icp sync my-canister # Sync only (e.g., re-upload assets)text
源代码 → [构建] → WASM → [部署] → 运行中的Canister → [同步] → 配置完成的状态icp deploy- 构建 —— 通过recipe或显式的构建步骤将canister编译为WASM
- 部署 —— 创建canister(如果是新canister)、应用配置、安装WASM
- 同步 —— 执行部署后操作(比如上传资产到资产canister)
如果需要更精细的控制,可以单独执行各个阶段:
bash
icp build # 仅执行构建
icp deploy # 执行完整流程(构建+部署+同步)
icp sync my-canister # 仅执行同步(比如重新上传资产)Environments and Networks
环境与网络
Two implicit environments are always available:
| Environment | Network | Purpose |
|---|---|---|
| | Local development |
| | Mainnet production |
The network is protected and cannot be overridden.
icCustom environments enable multiple deployment targets on the same network:
yaml
environments:
- name: staging
network: ic
canisters: [frontend, backend]
settings:
backend:
compute_allocation: 5
- name: production
network: ic
canisters: [frontend, backend]
settings:
backend:
compute_allocation: 20
freezing_threshold: 7776000默认自带两个内置环境:
| 环境名 | 网络 | 用途 |
|---|---|---|
| | 本地开发 |
| | 主网生产环境 |
ic自定义环境可以实现同一个网络下的多个部署目标:
yaml
environments:
- name: staging
network: ic
canisters: [frontend, backend]
settings:
backend:
compute_allocation: 5
- name: production
network: ic
canisters: [frontend, backend]
settings:
backend:
compute_allocation: 20
freezing_threshold: 7776000Install Modes
安装模式
bash
icp deploy # Auto: install new, upgrade existing (default)
icp deploy --mode upgrade # Preserve state, run upgrade hooks
icp deploy --mode reinstall # Clear all state (dangerous)bash
icp deploy # 自动模式:新canister执行安装,已有canister执行升级(默认)
icp deploy --mode upgrade # 保留状态,执行升级钩子
icp deploy --mode reinstall # 清空所有状态(高危操作)Configuration
配置说明
Rust canister
Rust Canister
yaml
canisters:
- name: backend
recipe:
type: "@dfinity/rust@v3.2.0"
configuration:
package: backend
candid: backend.did # optional — if specified, file must exist (auto-generated when omitted)yaml
canisters:
- name: backend
recipe:
type: "@dfinity/rust@v3.2.0"
configuration:
package: backend
candid: backend.did # 可选 —— 如果指定,对应文件必须存在;省略则自动生成Motoko canister
Motoko Canister
yaml
canisters:
- name: backend
recipe:
type: "@dfinity/motoko@v4.1.0"
configuration:
main: src/backend/main.mo
candid: backend.did # optional — if specified, file must exist (auto-generated when omitted)yaml
canisters:
- name: backend
recipe:
type: "@dfinity/motoko@v4.1.0"
configuration:
main: src/backend/main.mo
candid: backend.did # 可选 —— 如果指定,对应文件必须存在;省略则自动生成Asset canister (frontend)
资产Canister(前端)
yaml
canisters:
- name: frontend
recipe:
type: "@dfinity/asset-canister@v2.1.0"
configuration:
dir: dist
build:
- npm install
- npm run buildFor multi-canister projects, list all canisters in the same array. icp-cli builds them in parallel. There is no field — use Canister Environment Variables for inter-canister communication.
canistersdependenciesyaml
canisters:
- name: frontend
recipe:
type: "@dfinity/asset-canister@v2.1.0"
configuration:
dir: dist
build:
- npm install
- npm run build针对多canister项目,将所有canister列在同一个数组中即可,icp-cli会并行构建它们。无需配置字段,跨canister通信请使用Canister环境变量。
canistersdependenciesCustom build steps (no recipe)
自定义构建步骤(不使用recipe)
When not using a recipe, only , , , , and are valid canister-level fields. There are no , , or fields — handle these in the build script instead:
namebuildsyncsettingsinit_argswasmcandidmetadata- WASM output: copy the final WASM to
$ICP_WASM_OUTPUT_PATH - Candid metadata: use to embed
ic-wasmmetadatacandid:service - Candid file: the file is referenced only in the
.didcommand, not as a YAML fieldic-wasm
yaml
canisters:
- name: backend
build:
steps:
- type: script
commands:
- cargo build --target wasm32-unknown-unknown --release
- cp target/wasm32-unknown-unknown/release/backend.wasm "$ICP_WASM_OUTPUT_PATH"
- ic-wasm "$ICP_WASM_OUTPUT_PATH" -o "$ICP_WASM_OUTPUT_PATH" metadata candid:service -f backend/backend.did -v public --keep-name-section不使用recipe时,canister层级仅支持、、、和字段,没有、或字段——这些逻辑需要在构建脚本中处理:
namebuildsyncsettingsinit_argswasmcandidmetadata- WASM输出:将最终的WASM文件复制到
$ICP_WASM_OUTPUT_PATH - Candid元数据:使用嵌入
ic-wasm元数据candid:service - Candid文件:文件仅在
.did命令中引用,不需要作为YAML字段配置ic-wasm
yaml
canisters:
- name: backend
build:
steps:
- type: script
commands:
- cargo build --target wasm32-unknown-unknown --release
- cp target/wasm32-unknown-unknown/release/backend.wasm "$ICP_WASM_OUTPUT_PATH"
- ic-wasm "$ICP_WASM_OUTPUT_PATH" -o "$ICP_WASM_OUTPUT_PATH" metadata candid:service -f backend/backend.did -v public --keep-name-sectionAvailable recipes
可用Recipe
| Recipe | Purpose |
|---|---|
| Rust canisters with Cargo |
| Motoko canisters |
| Asset canisters for static files |
| Pre-compiled WASM files |
Use to see the effective configuration after recipe expansion.
icp project show| Recipe | 用途 |
|---|---|
| 基于Cargo的Rust canister |
| Motoko canister |
| 存放静态文件的资产canister |
| 预编译的WASM文件 |
使用可以查看recipe展开后的最终生效配置。
icp project showCanister Environment Variables
Canister环境变量
icp-cli automatically injects all canister IDs as environment variables during . Variables are formatted as and injected into every canister in the environment.
icp deployPUBLIC_CANISTER_ID:<canister-name>Frontend → Backend (reading canister IDs in JavaScript):
Asset canisters expose injected variables through a cookie named , set on all HTML responses. Use to read it:
ic_env@icp-sdk/corejs
import { safeGetCanisterEnv } from "@icp-sdk/core/agent/canister-env";
const canisterEnv = safeGetCanisterEnv();
const backendId = canisterEnv?.["PUBLIC_CANISTER_ID:backend"];Backend → Backend (reading canister IDs in canister code):
- Rust:
ic_cdk::api::env_var_value("PUBLIC_CANISTER_ID:other_canister") - Motoko (motoko-core v2.1.0+):
motoko
import Runtime "mo:core/Runtime"; let otherId = Runtime.envVar("PUBLIC_CANISTER_ID:other_canister");
Note: variables are only updated for canisters being deployed. When adding a new canister, run (without specifying a canister name) to update all canisters with the complete ID set.
icp deployicp-cli会在时自动将所有canister ID作为环境变量注入,变量格式为,会注入到当前环境的所有canister中。
icp deployPUBLIC_CANISTER_ID:<canister名称>前端 → 后端(在JavaScript中读取canister ID):
资产canister会通过所有HTML响应中设置的名为的Cookie暴露注入的变量,可使用读取:
ic_env@icp-sdk/corejs
import { safeGetCanisterEnv } from "@icp-sdk/core/agent/canister-env";
const canisterEnv = safeGetCanisterEnv();
const backendId = canisterEnv?.["PUBLIC_CANISTER_ID:backend"];后端 → 后端(在canister代码中读取canister ID):
- Rust:
ic_cdk::api::env_var_value("PUBLIC_CANISTER_ID:other_canister") - Motoko(motoko-core v2.1.0+):
motoko
import Runtime "mo:core/Runtime"; let otherId = Runtime.envVar("PUBLIC_CANISTER_ID:other_canister");
注意:变量仅会在部署的canister中更新。新增canister后,请执行不带canister名称的命令,为所有canister更新完整的ID集合。
icp deployBinding Generation
绑定生成
icp-cli does not have a built-in command. Use to generate TypeScript bindings from files.
dfx generate@icp-sdk/bindgen.didVite plugin (recommended for Vite-based frontend projects):
js
// vite.config.js
import { icpBindgen } from "@icp-sdk/bindgen/plugins/vite";
export default defineConfig({
plugins: [
// Add one icpBindgen() call per canister the frontend needs to access
icpBindgen({
didFile: "../backend/backend.did",
outDir: "./src/bindings/backend",
}),
icpBindgen({
didFile: "../other/other.did",
outDir: "./src/bindings/other",
}),
],
});Each instance generates a function in its . Add to .
icpBindgen()createActoroutDir**/src/bindings/.gitignoreCreating actors from bindings — connect the generated bindings with the cookie:
ic_envjs
// src/actor.js
import { safeGetCanisterEnv } from "@icp-sdk/core/agent/canister-env";
import { createActor } from "./bindings/backend";
// For additional canisters: import { createActor as createOther } from "./bindings/other";
const canisterEnv = safeGetCanisterEnv();
const agentOptions = {
host: window.location.origin,
rootKey: canisterEnv?.IC_ROOT_KEY,
};
export const backend = createActor(
canisterEnv?.["PUBLIC_CANISTER_ID:backend"],
{ agentOptions }
);
// Repeat for each canister: createOther(canisterEnv?.["PUBLIC_CANISTER_ID:other"], { agentOptions })Non-Vite frontends — use the CLI to generate bindings manually:
@icp-sdk/bindgenbash
npx @icp-sdk/bindgen --did ../backend/backend.did --out ./src/bindings/backendRequirements:
- The file must exist on disk. If using a recipe with
.didspecified, the file must be committed. Ifcandidis omitted, runcandidfirst to auto-generate it.icp build - generates code that depends on
@icp-sdk/bindgen. Projects using@icp-sdk/coremust upgrade to@dfinity/agent+@icp-sdk/core. This is not optional — there is no way to generate TypeScript bindings with icp-cli while staying on@icp-sdk/bindgen.@dfinity/agent
icp-cli没有内置的命令,请使用从文件生成TypeScript绑定。
dfx generate@icp-sdk/bindgen.didVite插件(推荐基于Vite的前端项目使用):
js
// vite.config.js
import { icpBindgen } from "@icp-sdk/bindgen/plugins/vite";
export default defineConfig({
plugins: [
// 前端需要访问的每个canister都对应一个icpBindgen()配置
icpBindgen({
didFile: "../backend/backend.did",
outDir: "./src/bindings/backend",
}),
icpBindgen({
didFile: "../other/other.did",
outDir: "./src/bindings/other",
}),
],
});每个实例都会在其中生成一个函数。请将添加到中。
icpBindgen()outDircreateActor**/src/bindings/.gitignore通过绑定创建Actor —— 将生成的绑定与Cookie关联:
ic_envjs
// src/actor.js
import { safeGetCanisterEnv } from "@icp-sdk/core/agent/canister-env";
import { createActor } from "./bindings/backend";
// 其他canister的引入方式:import { createActor as createOther } from "./bindings/other";
const canisterEnv = safeGetCanisterEnv();
const agentOptions = {
host: window.location.origin,
rootKey: canisterEnv?.IC_ROOT_KEY,
};
export const backend = createActor(
canisterEnv?.["PUBLIC_CANISTER_ID:backend"],
{ agentOptions }
);
// 其他canister重复该流程:createOther(canisterEnv?.["PUBLIC_CANISTER_ID:other"], { agentOptions })非Vite前端 —— 使用的CLI手动生成绑定:
@icp-sdk/bindgenbash
npx @icp-sdk/bindgen --did ../backend/backend.did --out ./src/bindings/backend要求:
- 文件必须存在于本地磁盘。如果使用的recipe指定了
.did配置,该文件必须已提交到仓库;如果省略了candid配置,请先执行candid自动生成。icp build - 生成的代码依赖
@icp-sdk/bindgen,使用@icp-sdk/core的项目必须升级到@dfinity/agent+@icp-sdk/core,这是强制要求——使用icp-cli时没有其他方式可以在保留@icp-sdk/bindgen的前提下生成TypeScript绑定。@dfinity/agent
Dev Server Configuration (Vite)
开发服务端配置(Vite)
In development, the Vite dev server must simulate the cookie that the asset canister provides in production. Query the local network for the root key, canister IDs, and API URL:
ic_envjs
// vite.config.js
import { execSync } from "child_process";
const environment = process.env.ICP_ENVIRONMENT || "local";
// List all backend canisters the frontend needs to access
const CANISTER_NAMES = ["backend", "other"];
function getCanisterId(name) {
// `-i` makes the command return only the identity of the canister
return execSync(`icp canister status ${name} -e ${environment} -i`, {
encoding: "utf-8", stdio: "pipe",
}).trim();
}
function getDevServerConfig() {
const networkStatus = JSON.parse(
execSync(`icp network status -e ${environment} --json`, {
encoding: "utf-8",
})
);
const canisterParams = CANISTER_NAMES
.map((name) => `PUBLIC_CANISTER_ID:${name}=${getCanisterId(name)}`)
.join("&");
return {
headers: {
"Set-Cookie": `ic_env=${encodeURIComponent(
`${canisterParams}&ic_root_key=${networkStatus.root_key}`
)}; SameSite=Lax;`,
},
proxy: {
"/api": { target: networkStatus.api_url, changeOrigin: true },
},
};
}Key differences from dfx:
- The proxy target and root key come from (no hardcoded ports)
icp network status --json - Canister IDs come from (no
icp canister status <name> -e <env> -ifile).env - The cookie replaces dfx's
ic_envenvironment variablesCANISTER_ID_* - lets the dev server target any environment (local, staging, ic)
ICP_ENVIRONMENT
开发环境下,Vite开发服务端需要模拟生产环境下资产canister提供的Cookie,从本地网络查询根密钥、canister ID和API URL:
ic_envjs
// vite.config.js
import { execSync } from "child_process";
const environment = process.env.ICP_ENVIRONMENT || "local";
// 列出前端需要访问的所有后端canister
const CANISTER_NAMES = ["backend", "other"];
function getCanisterId(name) {
// `-i`参数让命令仅返回canister的identity
return execSync(`icp canister status ${name} -e ${environment} -i`, {
encoding: "utf-8", stdio: "pipe",
}).trim();
}
function getDevServerConfig() {
const networkStatus = JSON.parse(
execSync(`icp network status -e ${environment} --json`, {
encoding: "utf-8",
})
);
const canisterParams = CANISTER_NAMES
.map((name) => `PUBLIC_CANISTER_ID:${name}=${getCanisterId(name)}`)
.join("&");
return {
headers: {
"Set-Cookie": `ic_env=${encodeURIComponent(
`${canisterParams}&ic_root_key=${networkStatus.root_key}`
)}; SameSite=Lax;`,
},
proxy: {
"/api": { target: networkStatus.api_url, changeOrigin: true },
},
};
}与dfx的核心差异:
- 代理目标和根密钥来自(无硬编码端口)
icp network status --json - Canister ID来自(无需
icp canister status <名称> -e <环境> -i文件).env - Cookie替代了dfx的
ic_env环境变量CANISTER_ID_* - 允许开发服务端指向任意环境(local、staging、ic)
ICP_ENVIRONMENT
dfx → icp Migration
dfx → icp 迁移指南
Local network port change
本地网络端口变更
dfx serves the local network on port . icp-cli uses port . When migrating, search the project for hardcoded references to (or ) and update them to . Better yet, use to get the dynamically (see Dev Server Configuration above). Common locations to check:
494380004943localhost:49438000icp network status --jsonapi_url- Vite/webpack proxy configs (e.g., )
vite.config.ts - README documentation
- Test fixtures and scripts
dfx的本地网络运行在端口,icp-cli使用端口。迁移时请搜索项目中硬编码的(或)引用,更新为。更推荐的做法是通过动态获取(参考上方的开发服务端配置章节)。需要检查的常见位置:
494380004943localhost:49438000icp network status --jsonapi_url- Vite/webpack代理配置(比如)
vite.config.ts - README文档
- 测试用例和脚本
Remove .env
file and output_env_file
.envoutput_env_file移除.env
文件和output_env_file
配置
.envoutput_env_filedfx generates a file with variables via in . icp-cli does not use files for canister IDs — remove from config and delete any dfx-generated file. Also remove dfx-specific environment variables from files (e.g., , ).
.envCANISTER_ID_*output_env_filedfx.json.envoutput_env_file.env.envDFX_NETWORKNETWORKReplace code that reads with the cookie pattern (see Canister Environment Variables above).
process.env.CANISTER_ID_*ic_envdfx通过中的配置生成包含变量的文件,icp-cli不使用文件存储canister ID——请移除配置中的,删除所有dfx生成的文件,同时移除文件中dfx专属的环境变量(比如、)。
dfx.jsonoutput_env_fileCANISTER_ID_*.env.envoutput_env_file.env.envDFX_NETWORKNETWORK将读取的代码替换为Cookie的读取逻辑(参考上方的Canister环境变量章节)。
process.env.CANISTER_ID_*ic_envFrontend package migration
前端包迁移
Since generates code that depends on , projects with TypeScript bindings must upgrade from packages. This is not optional — does not exist in icp-cli, and is the only supported way to generate bindings.
@icp-sdk/bindgen@icp-sdk/core@dfinity/*dfx generate@icp-sdk/bindgen| Remove | Replace with |
|---|---|
| |
| |
| |
| |
| Not needed — use |
| |
Steps:
npm uninstall @dfinity/agent @dfinity/candid @dfinity/principal vite-plugin-environmentnpm install @icp-sdk/core @icp-sdk/bindgen- Delete (dfx-generated bindings)
src/declarations/ - Add to
**/src/bindings/.gitignore - Commit the file(s) used by bindgen
.did - Add to
icpBindgen()(see Binding Generation above)vite.config.js - Replace actor setup code: use from
safeGetCanisterEnv+@icp-sdk/corefrom generated bindings (see Creating actors from bindings above)createActor - Remove references — use the
process.env.CANISTER_ID_*cookie insteadic_env
由于生成的代码依赖,使用TypeScript绑定的项目必须从包升级,这是强制要求——icp-cli没有命令,是唯一官方支持的绑定生成方式。
@icp-sdk/bindgen@icp-sdk/core@dfinity/*dfx generate@icp-sdk/bindgen| 移除的包/功能 | 替换为 |
|---|---|
| |
| |
| |
| |
| 不需要 —— 使用 |
| |
迁移步骤:
npm uninstall @dfinity/agent @dfinity/candid @dfinity/principal vite-plugin-environmentnpm install @icp-sdk/core @icp-sdk/bindgen- 删除目录(dfx生成的绑定)
src/declarations/ - 将添加到
**/src/bindings/.gitignore - 提交bindgen使用的文件
.did - 在中添加
vite.config.js配置(参考上方的绑定生成章节)icpBindgen() - 替换Actor初始化代码:使用提供的
@icp-sdk/core+ 生成的绑定中的safeGetCanisterEnv(参考上方的通过绑定创建Actor章节)createActor - 移除引用,改用
process.env.CANISTER_ID_*Cookieic_env
Command mapping
命令对照表
| Task | dfx | icp |
|---|---|---|
| Create project | | |
| Start local network | | |
| Stop local network | | |
| Build | | |
| Deploy all | | |
| Deploy to mainnet | | |
| Call canister | | |
| Get canister ID | | |
| Canister status | | |
| List canisters | | |
| Create identity | | |
| Set default identity | | |
| Show principal | | |
| Export identity | | |
| Delete identity | | |
| Get account ID | | |
| Check ICP balance | | |
| Check cycles | | |
| 任务 | dfx命令 | icp命令 |
|---|---|---|
| 创建项目 | | |
| 启动本地网络 | | |
| 停止本地网络 | | |
| 构建 | | |
| 部署所有canister | | |
| 部署到主网 | | |
| 调用canister | | |
| 获取canister ID | | |
| 查看canister状态 | | |
| 列出canister | | |
| 创建身份 | | |
| 设置默认身份 | | |
| 查看principal | | |
| 导出身份 | | |
| 删除身份 | | |
| 获取账户ID | | |
| 查询ICP余额 | | |
| 查询cycles余额 | | |
Configuration mapping
配置对照表
| dfx.json | icp.yaml |
|---|---|
| |
| |
| |
| |
| |
| |
| |
| Not needed — use Canister Environment Variables |
| Not needed — use |
| |
| |
| dfx.json配置 | icp.yaml配置 |
|---|---|
| |
| |
| |
| |
| |
| |
| |
| 不需要 —— 使用Canister环境变量 |
| 不需要 —— 使用 |
| |
| |
Identity migration
身份迁移
bash
undefinedbash
undefinedExport from dfx, import to icp-cli
从dfx导出身份,导入到icp-cli
dfx identity export my-identity > /tmp/my-identity.pem
icp identity import my-identity --from-pem /tmp/my-identity.pem
rm /tmp/my-identity.pem
dfx identity export my-identity > /tmp/my-identity.pem
icp identity import my-identity --from-pem /tmp/my-identity.pem
rm /tmp/my-identity.pem
Verify principals match
验证principal一致
dfx identity get-principal --identity my-identity
icp identity principal --identity my-identity
undefineddfx identity get-principal --identity my-identity
icp identity principal --identity my-identity
undefinedCanister ID migration
Canister ID迁移
If you have existing mainnet canisters managed by dfx, migrate the IDs from to icp-cli's mapping file:
canister_ids.jsonbash
undefined如果你已有dfx管理的主网canister,将ID从迁移到icp-cli的映射文件:
canister_ids.jsonbash
undefinedGet IDs from dfx
从dfx获取ID
dfx canister id frontend --network ic
dfx canister id backend --network ic
dfx canister id frontend --network ic
dfx canister id backend --network ic
Create mapping file for icp-cli
为icp-cli创建映射文件
mkdir -p .icp/data/mappings
cat > .icp/data/mappings/ic.ids.json << 'EOF'
{
"frontend": "xxxxx-xxxxx-xxxxx-xxxxx-cai",
"backend": "yyyyy-yyyyy-yyyyy-yyyyy-cai"
}
EOF
mkdir -p .icp/data/mappings
cat > .icp/data/mappings/ic.ids.json << 'EOF'
{
"frontend": "xxxxx-xxxxx-xxxxx-xxxxx-cai",
"backend": "yyyyy-yyyyy-yyyyy-yyyyy-cai"
}
EOF
Delete the dfx canister ID file — icp-cli uses .icp/data/mappings/ instead
删除dfx的canister ID文件 —— icp-cli使用.icp/data/mappings/存储映射
rm -f canister_ids.json
rm -f canister_ids.json
Commit to version control
提交到版本控制
git add .icp/data/
undefinedgit add .icp/data/
undefinedPost-Migration Verification
迁移后验证
After migrating a project from dfx to icp-cli, verify the following:
- Deleted files: and
dfx.jsonno longer existcanister_ids.json - Created files: exists.
icp.yamlexists and is committed (if project has mainnet canisters).icp/data/mappings/ic.ids.json - : contains
.gitignore, does not contain.icp/cache/.dfx - No stale port references: search the codebase for — there should be zero matches
4943 - No dfx env patterns: search for ,
output_env_file,CANISTER_ID_— there should be zero matches in config and source filesDFX_NETWORK - Frontend packages (if project has TypeScript bindings): is not in
@dfinity/agent,package.jsonand@icp-sdk/coreare.@icp-sdk/bindgenis deleted,src/declarations/is insrc/bindings/.gitignore - Candid files: files used by
.didare committed@icp-sdk/bindgen - Build succeeds: completes without errors
icp build - Config is correct: displays the expected expanded configuration
icp project show - README: references commands (not
icp), says "local network" (not "replica"), shows correct portdfx
将项目从dfx迁移到icp-cli后,请验证以下项:
- 已删除的文件:和
dfx.json已不存在canister_ids.json - 已创建的文件:已存在。如果项目有主网canister,
icp.yaml已存在且已提交到版本控制.icp/data/mappings/ic.ids.json - 配置:包含
.gitignore,不包含.icp/cache/相关配置.dfx - 无遗留端口引用:代码库中搜索无匹配结果
4943 - 无dfx环境变量模式:搜索、
output_env_file、CANISTER_ID_,配置和源码中无匹配结果DFX_NETWORK - 前端包配置(如果项目使用TypeScript绑定):中没有
package.json,已安装@dfinity/agent和@icp-sdk/core。@icp-sdk/bindgen已删除,src/declarations/已添加到src/bindings/.gitignore - Candid文件:使用的
@icp-sdk/bindgen文件已提交到仓库.did - 构建成功:执行无错误
icp build - 配置正确:显示的展开配置符合预期
icp project show - README文档:引用的是命令(而非
icp),描述为“local network”(而非“replica”),端口正确dfx