icp-cli

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

ICP CLI

ICP CLI

What This Is

什么是ICP CLI

The
icp
command-line tool builds and deploys applications on the Internet Computer. It replaces the legacy
dfx
tool with YAML configuration, a recipe system for reusable build templates, and an environment model that separates deployment targets from network connections. Never use
dfx
— always use
icp
.
icp
命令行工具用于在Internet Computer上构建和部署应用,它通过YAML配置、支持可复用构建模板的recipe系统、以及将部署目标与网络连接分离的环境模型,取代了旧版的
dfx
工具。请始终使用
icp
,不要使用
dfx

Prerequisites

前置要求

  • For Rust canisters:
    rustup target add wasm32-unknown-unknown
  • For Motoko canisters:
    npm i -g ic-mops
    and
    moc
    version defined in
    mops.toml
    (templates include this; for manual projects add
    [toolchain]
    with
    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

常见误区

  1. Using
    dfx
    instead of
    icp
    .
    The
    dfx
    tool is legacy. All commands have
    icp
    equivalents — see the migration table below. Never generate
    dfx
    commands or reference
    dfx
    documentation. Configuration uses
    icp.yaml
    , not
    dfx.json
    — and the structure differs: canisters are an array of objects, not a keyed object.
  2. Using
    --network ic
    to deploy to mainnet.
    icp-cli uses environments, not direct network targeting. The correct flag is
    -e ic
    (short for
    --environment ic
    ).
    bash
    # Wrong
    icp deploy --network ic
    # Correct
    icp deploy -e ic
    Note:
    -n
    /
    --network
    targets a network directly and works with canister IDs (principals). Use
    -e
    /
    --environment
    when referencing canisters by name. For token and cycles operations, use
    -n
    since they don't reference project canisters.
  3. Using a recipe without a version pin. Always pin recipe versions to avoid breaking changes. Unpinned recipes resolve to
    latest
    which can change at any time. Official recipes are hosted at dfinity/icp-cli-recipes.
    yaml
    # Wrong — unpinned, may break
    recipe:
      type: "@dfinity/rust"
    
    # Correct — pinned version
    recipe:
      type: "@dfinity/rust@v3.2.0"
  4. 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
  5. Not committing
    .icp/data/
    to version control.
    Mainnet canister IDs are stored in
    .icp/data/mappings/<environment>.ids.json
    . Losing this file means losing the mapping between canister names and on-chain IDs. Always commit
    .icp/data/
    — never delete it. Add
    .icp/cache/
    to
    .gitignore
    (it is ephemeral and rebuilt automatically).
  6. Using
    icp identity use
    instead of
    icp identity default
    .
    The dfx command
    dfx identity use
    became
    icp identity default
    . Similarly,
    dfx identity get-principal
    became
    icp identity principal
    , and
    dfx identity remove
    became
    icp identity delete
    .
  7. Confusing networks and environments. A network is a connection endpoint (URL). An environment combines a network + canisters + settings. You deploy to environments (
    -e
    ), not networks. Multiple environments can target the same network with different settings (e.g., staging and production both on
    ic
    ).
  8. 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 run
    icp network start -d
    in your project directory before deploying locally. The local network auto-starts with system canisters and seeds accounts with ICP and cycles.
  9. Not specifying build commands for asset canisters. dfx automatically runs
    npm run build
    for asset canisters. icp-cli requires explicit build commands in the recipe configuration:
    yaml
    canisters:
      - name: frontend
        recipe:
          type: "@dfinity/asset-canister@v2.1.0"
          configuration:
            dir: dist
            build:
              - npm install
              - npm run build
  10. Expecting
    output_env_file
    or
    .env
    with canister IDs.
    dfx writes canister IDs to a
    .env
    file (
    CANISTER_ID_BACKEND=...
    ) via
    output_env_file
    . icp-cli does not generate
    .env
    files. Instead, it injects canister IDs as environment variables (
    PUBLIC_CANISTER_ID:<name>
    ) directly into canisters during
    icp deploy
    . Frontends read these from the
    ic_env
    cookie set by the asset canister. Remove
    output_env_file
    from your config and any code that reads
    CANISTER_ID_*
    from
    .env
    — use the
    ic_env
    cookie instead (see Canister Environment Variables below).
  11. Expecting
    dfx generate
    for TypeScript bindings.
    icp-cli does not have a
    dfx generate
    equivalent. Use
    @icp-sdk/bindgen
    (a Vite plugin) to generate TypeScript bindings from
    .did
    files at build time. The
    .did
    file must exist on disk — either commit it to the repo, or generate it with
    icp build
    first (recipes auto-generate it when
    candid
    is not specified). See Binding Generation below.
  12. Misunderstanding Candid file generation with recipes. When using the Rust or Motoko recipe:
    • If
      candid
      is specified: the file must already exist (checked in or manually created). The recipe uses it as-is and does not generate one.
    • If
      candid
      is omitted: the recipe auto-generates the
      .did
      file from the compiled WASM (via
      candid-extractor
      for Rust,
      moc
      for Motoko). The generated file is placed in the build cache, not at a predictable project path.
    For projects that need a
    .did
    file on disk (e.g., for
    @icp-sdk/bindgen
    ), the recommended pattern is: generate the
    .did
    file once, commit it, and specify
    candid
    in the recipe config. To generate it manually:
    Rust — build the WASM first, then extract the Candid interface:
    bash
    cargo install candid-extractor  # one-time setup
    icp build backend
    candid-extractor target/wasm32-unknown-unknown/release/backend.wasm > backend/backend.did
    Motoko — use
    moc
    directly with the
    --idl
    flag:
    bash
    $(mops toolchain bin moc) --idl $(mops sources) -o backend/backend.did backend/app.mo
  1. 使用
    dfx
    而非
    icp
    dfx
    是旧版工具,所有命令都有对应的
    icp
    版本,可参考下方的迁移对照表。请勿生成
    dfx
    命令或引用
    dfx
    文档。配置文件使用
    icp.yaml
    而非
    dfx.json
    ,且结构存在差异:canisters是对象数组,不是键值对象。
  2. 使用
    --network ic
    部署到主网
    。icp-cli使用环境(environment)而非直接指定网络,正确的参数是
    -e ic
    (是
    --environment ic
    的缩写)。
    bash
    # 错误写法
    icp deploy --network ic
    # 正确写法
    icp deploy -e ic
    注意:
    -n
    /
    --network
    用于直接指定网络,搭配canister ID(principal)使用。当通过名称引用canister时,请使用
    -e
    /
    --environment
    。针对代币和cycles操作请使用
    -n
    ,因为这类操作不涉及项目内的canister引用。
  3. 使用未锁定版本的recipe。请始终锁定recipe的版本以避免破坏性变更,未锁定版本的recipe会默认拉取随时可能变动的
    latest
    版本。官方recipe托管在 dfinity/icp-cli-recipes
    yaml
    # 错误写法 —— 未锁定版本,可能出现兼容性问题
    recipe:
      type: "@dfinity/rust"
    
    # 正确写法 —— 锁定版本
    recipe:
      type: "@dfinity/rust@v3.2.0"
  4. 已有对应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
  5. 未将
    .icp/data/
    提交到版本控制
    。主网canister ID存储在
    .icp/data/mappings/<环境名>.ids.json
    中,丢失该文件会导致canister名称和链上ID的映射关系丢失。请始终提交
    .icp/data/
    目录,不要删除它。将
    .icp/cache/
    添加到
    .gitignore
    中(该目录是临时缓存,会自动重建)。
  6. 使用
    icp identity use
    而非
    icp identity default
    。原
    dfx identity use
    命令对应的icp命令是
    icp identity default
    。同理,
    dfx identity get-principal
    对应
    icp identity principal
    dfx identity remove
    对应
    icp identity delete
  7. 混淆网络和环境的概念。网络(network)是连接端点(URL),环境(environment)则是网络+canister+配置的组合。你应该部署到环境(使用
    -e
    参数)而非网络。多个环境可以指向同一个网络但使用不同的配置(比如预发环境和生产环境都使用
    ic
    主网)。
  8. 不知道本地网络是项目级别的。和dfx运行全局共享的本地网络不同,icp-cli为每个项目运行独立的本地网络。在本地部署前你必须在项目目录下执行
    icp network start -d
    ,本地网络会自动启动系统canister,并为测试账户注入ICP和cycles。
  9. 未为资产canister指定构建命令。dfx会自动为资产canister执行
    npm run build
    ,而icp-cli需要在recipe配置中显式指定构建命令:
    yaml
    canisters:
      - name: frontend
        recipe:
          type: "@dfinity/asset-canister@v2.1.0"
          configuration:
            dir: dist
            build:
              - npm install
              - npm run build
  10. 期望通过
    output_env_file
    或者
    .env
    文件获取canister ID
    。dfx会通过
    output_env_file
    配置将canister ID写入
    .env
    文件(格式为
    CANISTER_ID_BACKEND=...
    ),而icp-cli不会生成
    .env
    文件,它会在
    icp deploy
    时直接将canister ID作为环境变量(格式为
    PUBLIC_CANISTER_ID:<canister名称>
    )注入到canister中。前端可以通过资产canister设置的
    ic_env
    Cookie读取这些变量。请移除配置中的
    output_env_file
    和所有从
    .env
    读取
    CANISTER_ID_*
    的代码,改用
    ic_env
    Cookie(参考下方的Canister环境变量章节)。
  11. 期望存在类似
    dfx generate
    的命令生成TypeScript绑定
    。icp-cli没有对应的
    dfx generate
    命令,请使用
    @icp-sdk/bindgen
    (Vite插件)在构建时从
    .did
    文件生成TypeScript绑定。
    .did
    文件必须存在于本地磁盘——要么提交到代码仓库,要么先执行
    icp build
    生成(未指定
    candid
    配置时recipe会自动生成)。参考下方的绑定生成章节。
  12. 对recipe生成Candid文件的逻辑理解有误。使用Rust或Motoko recipe时:
    • 如果指定了
      candid
      配置
      :对应文件必须已经存在(已提交到仓库或手动创建),recipe会直接使用该文件,不会自动生成。
    • 如果省略了
      candid
      配置
      :recipe会从编译后的WASM自动生成
      .did
      文件(Rust使用
      candid-extractor
      ,Motoko使用
      moc
      ),生成的文件存放在构建缓存中,不会存放在固定的项目路径下。
    如果项目需要本地磁盘上的
    .did
    文件(比如供
    @icp-sdk/bindgen
    使用),推荐的做法是:先生成一次
    .did
    文件,提交到仓库,然后在recipe配置中指定
    candid
    路径。手动生成的方法如下:
    Rust —— 先构建WASM,然后提取Candid接口:
    bash
    cargo install candid-extractor  # 仅需执行一次安装
    icp build backend
    candid-extractor target/wasm32-unknown-unknown/release/backend.wasm > backend/backend.did
    Motoko —— 直接使用
    moc
    --idl
    参数:
    bash
    $(mops toolchain bin moc) --idl $(mops sources) -o backend/backend.did backend/app.mo

How It Works

工作原理

Project Creation

项目创建

icp new
scaffolds projects from templates. Without flags, an interactive prompt launches. For scripted or non-interactive use, pass
--subfolder
and
--define
flags directly. Available templates and options: dfinity/icp-cli-templates.
icp new
命令基于模板生成项目骨架,不带参数时会启动交互式引导流程。如果需要脚本化或非交互式使用,可以直接传入
--subfolder
--define
参数。可用的模板和参数参考:dfinity/icp-cli-templates

Build → Deploy → Sync

构建 → 部署 → 同步

text
Source Code → [Build] → WASM → [Deploy] → Running Canister → [Sync] → Configured State
icp deploy
runs all three phases in sequence:
  1. Build — Compile canisters to WASM (via recipes or explicit build steps)
  2. Deploy — Create canisters (if new), apply settings, install WASM
  3. 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
会按顺序执行以上三个阶段:
  1. 构建 —— 通过recipe或显式的构建步骤将canister编译为WASM
  2. 部署 —— 创建canister(如果是新canister)、应用配置、安装WASM
  3. 同步 —— 执行部署后操作(比如上传资产到资产canister)
如果需要更精细的控制,可以单独执行各个阶段:
bash
icp build                     # 仅执行构建
icp deploy                    # 执行完整流程(构建+部署+同步)
icp sync my-canister          # 仅执行同步(比如重新上传资产)

Environments and Networks

环境与网络

Two implicit environments are always available:
EnvironmentNetworkPurpose
local
local
(managed, localhost:8000)
Local development
ic
ic
(connected, https://icp-api.io)
Mainnet production
The
ic
network is protected and cannot be overridden.
Custom 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
默认自带两个内置环境:
环境名网络用途
local
local
(托管的本地网络,localhost:8000)
本地开发
ic
ic
(已接入的主网,https://icp-api.io)
主网生产环境
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: 7776000

Install 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 build
For multi-canister projects, list all canisters in the same
canisters
array. icp-cli builds them in parallel. There is no
dependencies
field — use Canister Environment Variables for inter-canister communication.
yaml
canisters:
  - name: frontend
    recipe:
      type: "@dfinity/asset-canister@v2.1.0"
      configuration:
        dir: dist
        build:
          - npm install
          - npm run build
针对多canister项目,将所有canister列在同一个
canisters
数组中即可,icp-cli会并行构建它们。无需配置
dependencies
字段,跨canister通信请使用Canister环境变量。

Custom build steps (no recipe)

自定义构建步骤(不使用recipe)

When not using a recipe, only
name
,
build
,
sync
,
settings
, and
init_args
are valid canister-level fields. There are no
wasm
,
candid
, or
metadata
fields — handle these in the build script instead:
  • WASM output: copy the final WASM to
    $ICP_WASM_OUTPUT_PATH
  • Candid metadata: use
    ic-wasm
    to embed
    candid:service
    metadata
  • Candid file: the
    .did
    file is referenced only in the
    ic-wasm
    command, not as a YAML field
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层级仅支持
name
build
sync
settings
init_args
字段,没有
wasm
candid
metadata
字段——这些逻辑需要在构建脚本中处理:
  • WASM输出:将最终的WASM文件复制到
    $ICP_WASM_OUTPUT_PATH
  • Candid元数据:使用
    ic-wasm
    嵌入
    candid:service
    元数据
  • Candid文件
    .did
    文件仅在
    ic-wasm
    命令中引用,不需要作为YAML字段配置
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

Available recipes

可用Recipe

RecipePurpose
@dfinity/rust
Rust canisters with Cargo
@dfinity/motoko
Motoko canisters
@dfinity/asset-canister
Asset canisters for static files
@dfinity/prebuilt
Pre-compiled WASM files
Use
icp project show
to see the effective configuration after recipe expansion.
Recipe用途
@dfinity/rust
基于Cargo的Rust canister
@dfinity/motoko
Motoko canister
@dfinity/asset-canister
存放静态文件的资产canister
@dfinity/prebuilt
预编译的WASM文件
使用
icp project show
可以查看recipe展开后的最终生效配置。

Canister Environment Variables

Canister环境变量

icp-cli automatically injects all canister IDs as environment variables during
icp deploy
. Variables are formatted as
PUBLIC_CANISTER_ID:<canister-name>
and injected into every canister in the environment.
Frontend → Backend (reading canister IDs in JavaScript):
Asset canisters expose injected variables through a cookie named
ic_env
, set on all HTML responses. Use
@icp-sdk/core
to read it:
js
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
icp deploy
(without specifying a canister name) to update all canisters with the complete ID set.
icp-cli会在
icp deploy
时自动将所有canister ID作为环境变量注入,变量格式为
PUBLIC_CANISTER_ID:<canister名称>
,会注入到当前环境的所有canister中。
前端 → 后端(在JavaScript中读取canister ID):
资产canister会通过所有HTML响应中设置的名为
ic_env
的Cookie暴露注入的变量,可使用
@icp-sdk/core
读取:
js
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名称的
icp deploy
命令,为所有canister更新完整的ID集合。

Binding Generation

绑定生成

icp-cli does not have a built-in
dfx generate
command. Use
@icp-sdk/bindgen
to generate TypeScript bindings from
.did
files.
Vite 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
icpBindgen()
instance generates a
createActor
function in its
outDir
. Add
**/src/bindings/
to
.gitignore
.
Creating actors from bindings — connect the generated bindings with the
ic_env
cookie:
js
// 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
@icp-sdk/bindgen
CLI to generate bindings manually:
bash
npx @icp-sdk/bindgen --did ../backend/backend.did --out ./src/bindings/backend
Requirements:
  • The
    .did
    file must exist on disk. If using a recipe with
    candid
    specified, the file must be committed. If
    candid
    is omitted, run
    icp build
    first to auto-generate it.
  • @icp-sdk/bindgen
    generates code that depends on
    @icp-sdk/core
    . Projects using
    @dfinity/agent
    must upgrade to
    @icp-sdk/core
    +
    @icp-sdk/bindgen
    . This is not optional — there is no way to generate TypeScript bindings with icp-cli while staying on
    @dfinity/agent
    .
icp-cli没有内置的
dfx generate
命令,请使用
@icp-sdk/bindgen
.did
文件生成TypeScript绑定。
Vite插件(推荐基于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()
实例都会在其
outDir
中生成一个
createActor
函数。请将
**/src/bindings/
添加到
.gitignore
中。
通过绑定创建Actor —— 将生成的绑定与
ic_env
Cookie关联:
js
// 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前端 —— 使用
@icp-sdk/bindgen
的CLI手动生成绑定:
bash
npx @icp-sdk/bindgen --did ../backend/backend.did --out ./src/bindings/backend
要求:
  • .did
    文件必须存在于本地磁盘。如果使用的recipe指定了
    candid
    配置,该文件必须已提交到仓库;如果省略了
    candid
    配置,请先执行
    icp build
    自动生成。
  • @icp-sdk/bindgen
    生成的代码依赖
    @icp-sdk/core
    ,使用
    @dfinity/agent
    的项目必须升级到
    @icp-sdk/core
    +
    @icp-sdk/bindgen
    ,这是强制要求——使用icp-cli时没有其他方式可以在保留
    @dfinity/agent
    的前提下生成TypeScript绑定。

Dev Server Configuration (Vite)

开发服务端配置(Vite)

In development, the Vite dev server must simulate the
ic_env
cookie that the asset canister provides in production. Query the local network for the root key, canister IDs, and API URL:
js
// 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
    icp network status --json
    (no hardcoded ports)
  • Canister IDs come from
    icp canister status <name> -e <env> -i
    (no
    .env
    file)
  • The
    ic_env
    cookie replaces dfx's
    CANISTER_ID_*
    environment variables
  • ICP_ENVIRONMENT
    lets the dev server target any environment (local, staging, ic)
开发环境下,Vite开发服务端需要模拟生产环境下资产canister提供的
ic_env
Cookie,从本地网络查询根密钥、canister ID和API URL:
js
// 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
    文件)
  • ic_env
    Cookie替代了dfx的
    CANISTER_ID_*
    环境变量
  • ICP_ENVIRONMENT
    允许开发服务端指向任意环境(local、staging、ic)

dfx → icp Migration

dfx → icp 迁移指南

Local network port change

本地网络端口变更

dfx serves the local network on port
4943
. icp-cli uses port
8000
. When migrating, search the project for hardcoded references to
4943
(or
localhost:4943
) and update them to
8000
. Better yet, use
icp network status --json
to get the
api_url
dynamically (see Dev Server Configuration above). Common locations to check:
  • Vite/webpack proxy configs (e.g.,
    vite.config.ts
    )
  • README documentation
  • Test fixtures and scripts
dfx的本地网络运行在
4943
端口,icp-cli使用
8000
端口。迁移时请搜索项目中硬编码的
4943
(或
localhost:4943
)引用,更新为
8000
。更推荐的做法是通过
icp network status --json
动态获取
api_url
(参考上方的开发服务端配置章节)。需要检查的常见位置:
  • Vite/webpack代理配置(比如
    vite.config.ts
  • README文档
  • 测试用例和脚本

Remove
.env
file and
output_env_file

移除
.env
文件和
output_env_file
配置

dfx generates a
.env
file with
CANISTER_ID_*
variables via
output_env_file
in
dfx.json
. icp-cli does not use
.env
files for canister IDs — remove
output_env_file
from config and delete any dfx-generated
.env
file. Also remove dfx-specific environment variables from
.env
files (e.g.,
DFX_NETWORK
,
NETWORK
).
Replace code that reads
process.env.CANISTER_ID_*
with the
ic_env
cookie pattern (see Canister Environment Variables above).
dfx通过
dfx.json
中的
output_env_file
配置生成包含
CANISTER_ID_*
变量的
.env
文件,icp-cli不使用
.env
文件存储canister ID——请移除配置中的
output_env_file
,删除所有dfx生成的
.env
文件,同时移除
.env
文件中dfx专属的环境变量(比如
DFX_NETWORK
NETWORK
)。
将读取
process.env.CANISTER_ID_*
的代码替换为
ic_env
Cookie的读取逻辑(参考上方的Canister环境变量章节)。

Frontend package migration

前端包迁移

Since
@icp-sdk/bindgen
generates code that depends on
@icp-sdk/core
, projects with TypeScript bindings must upgrade from
@dfinity/*
packages. This is not optional —
dfx generate
does not exist in icp-cli, and
@icp-sdk/bindgen
is the only supported way to generate bindings.
RemoveReplace with
@dfinity/agent
@icp-sdk/core
@dfinity/candid
@icp-sdk/core
@dfinity/principal
@icp-sdk/core
dfx generate
(declarations)
@icp-sdk/bindgen
(Vite plugin or CLI)
vite-plugin-environment
Not needed — use
ic_env
cookie
src/declarations/
(generated by dfx)
src/bindings/
(generated by
@icp-sdk/bindgen
)
Steps:
  1. npm uninstall @dfinity/agent @dfinity/candid @dfinity/principal vite-plugin-environment
  2. npm install @icp-sdk/core @icp-sdk/bindgen
  3. Delete
    src/declarations/
    (dfx-generated bindings)
  4. Add
    **/src/bindings/
    to
    .gitignore
  5. Commit the
    .did
    file(s) used by bindgen
  6. Add
    icpBindgen()
    to
    vite.config.js
    (see Binding Generation above)
  7. Replace actor setup code: use
    safeGetCanisterEnv
    from
    @icp-sdk/core
    +
    createActor
    from generated bindings (see Creating actors from bindings above)
  8. Remove
    process.env.CANISTER_ID_*
    references — use the
    ic_env
    cookie instead
由于
@icp-sdk/bindgen
生成的代码依赖
@icp-sdk/core
,使用TypeScript绑定的项目必须
@dfinity/*
包升级,这是强制要求——icp-cli没有
dfx generate
命令,
@icp-sdk/bindgen
是唯一官方支持的绑定生成方式。
移除的包/功能替换为
@dfinity/agent
@icp-sdk/core
@dfinity/candid
@icp-sdk/core
@dfinity/principal
@icp-sdk/core
dfx generate
(声明文件生成)
@icp-sdk/bindgen
(Vite插件或CLI)
vite-plugin-environment
不需要 —— 使用
ic_env
Cookie
src/declarations/
(dfx生成的绑定)
src/bindings/
@icp-sdk/bindgen
生成的绑定)
迁移步骤:
  1. npm uninstall @dfinity/agent @dfinity/candid @dfinity/principal vite-plugin-environment
  2. npm install @icp-sdk/core @icp-sdk/bindgen
  3. 删除
    src/declarations/
    目录(dfx生成的绑定)
  4. **/src/bindings/
    添加到
    .gitignore
  5. 提交bindgen使用的
    .did
    文件
  6. vite.config.js
    中添加
    icpBindgen()
    配置(参考上方的绑定生成章节)
  7. 替换Actor初始化代码:使用
    @icp-sdk/core
    提供的
    safeGetCanisterEnv
    + 生成的绑定中的
    createActor
    (参考上方的通过绑定创建Actor章节)
  8. 移除
    process.env.CANISTER_ID_*
    引用,改用
    ic_env
    Cookie

Command mapping

命令对照表

Taskdfxicp
Create project
dfx new my_project
icp new my_project
Start local network
dfx start --background
icp network start -d
Stop local network
dfx stop
icp network stop
Build
dfx build
icp build
Deploy all
dfx deploy
icp deploy
Deploy to mainnet
dfx deploy --network ic
icp deploy -e ic
Call canister
dfx canister call X method '(args)'
icp canister call X method '(args)'
Get canister ID
dfx canister id X
icp canister status X --id-only
Canister status
dfx canister status X
icp canister status X
List canisters
dfx canister ls
icp canister list
Create identity
dfx identity new my_id
icp identity new my_id
Set default identity
dfx identity use my_id
icp identity default my_id
Show principal
dfx identity get-principal
icp identity principal
Export identity
dfx identity export my_id
icp identity export my_id
Delete identity
dfx identity remove my_id
icp identity delete my_id
Get account ID
dfx ledger account-id
icp identity account-id
Check ICP balance
dfx ledger balance
icp token balance
Check cycles
dfx wallet balance
icp cycles balance
任务dfx命令icp命令
创建项目
dfx new my_project
icp new my_project
启动本地网络
dfx start --background
icp network start -d
停止本地网络
dfx stop
icp network stop
构建
dfx build
icp build
部署所有canister
dfx deploy
icp deploy
部署到主网
dfx deploy --network ic
icp deploy -e ic
调用canister
dfx canister call X method '(args)'
icp canister call X method '(args)'
获取canister ID
dfx canister id X
icp canister status X --id-only
查看canister状态
dfx canister status X
icp canister status X
列出canister
dfx canister ls
icp canister list
创建身份
dfx identity new my_id
icp identity new my_id
设置默认身份
dfx identity use my_id
icp identity default my_id
查看principal
dfx identity get-principal
icp identity principal
导出身份
dfx identity export my_id
icp identity export my_id
删除身份
dfx identity remove my_id
icp identity delete my_id
获取账户ID
dfx ledger account-id
icp identity account-id
查询ICP余额
dfx ledger balance
icp token balance
查询cycles余额
dfx wallet balance
icp cycles balance

Configuration mapping

配置对照表

dfx.jsonicp.yaml
"type": "rust"
recipe.type: "@dfinity/rust@v3.2.0"
"type": "motoko"
recipe.type: "@dfinity/motoko@v4.1.0"
"type": "assets"
recipe.type: "@dfinity/asset-canister@v2.1.0"
"package": "X"
recipe.configuration.package: X
"candid": "X"
recipe.configuration.candid: X
"main": "X"
recipe.configuration.main: X
"source": ["dist"]
recipe.configuration.dir: dist
"dependencies": [...]
Not needed — use Canister Environment Variables
"output_env_file": ".env"
Not needed — use
ic_env
cookie
dfx generate
@icp-sdk/bindgen
Vite plugin
--network ic
-e ic
dfx.json配置icp.yaml配置
"type": "rust"
recipe.type: "@dfinity/rust@v3.2.0"
"type": "motoko"
recipe.type: "@dfinity/motoko@v4.1.0"
"type": "assets"
recipe.type: "@dfinity/asset-canister@v2.1.0"
"package": "X"
recipe.configuration.package: X
"candid": "X"
recipe.configuration.candid: X
"main": "X"
recipe.configuration.main: X
"source": ["dist"]
recipe.configuration.dir: dist
"dependencies": [...]
不需要 —— 使用Canister环境变量
"output_env_file": ".env"
不需要 —— 使用
ic_env
Cookie
dfx generate
@icp-sdk/bindgen
Vite插件
--network ic
-e ic

Identity migration

身份迁移

bash
undefined
bash
undefined

Export 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
undefined
dfx identity get-principal --identity my-identity icp identity principal --identity my-identity
undefined

Canister ID migration

Canister ID迁移

If you have existing mainnet canisters managed by dfx, migrate the IDs from
canister_ids.json
to icp-cli's mapping file:
bash
undefined
如果你已有dfx管理的主网canister,将ID从
canister_ids.json
迁移到icp-cli的映射文件:
bash
undefined

Get 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/
undefined
git add .icp/data/
undefined

Post-Migration Verification

迁移后验证

After migrating a project from dfx to icp-cli, verify the following:
  1. Deleted files:
    dfx.json
    and
    canister_ids.json
    no longer exist
  2. Created files:
    icp.yaml
    exists.
    .icp/data/mappings/ic.ids.json
    exists and is committed (if project has mainnet canisters)
  3. .gitignore
    : contains
    .icp/cache/
    , does not contain
    .dfx
  4. No stale port references: search the codebase for
    4943
    — there should be zero matches
  5. No dfx env patterns: search for
    output_env_file
    ,
    CANISTER_ID_
    ,
    DFX_NETWORK
    — there should be zero matches in config and source files
  6. Frontend packages (if project has TypeScript bindings):
    @dfinity/agent
    is not in
    package.json
    ,
    @icp-sdk/core
    and
    @icp-sdk/bindgen
    are.
    src/declarations/
    is deleted,
    src/bindings/
    is in
    .gitignore
  7. Candid files:
    .did
    files used by
    @icp-sdk/bindgen
    are committed
  8. Build succeeds:
    icp build
    completes without errors
  9. Config is correct:
    icp project show
    displays the expected expanded configuration
  10. README: references
    icp
    commands (not
    dfx
    ), says "local network" (not "replica"), shows correct port
将项目从dfx迁移到icp-cli后,请验证以下项:
  1. 已删除的文件
    dfx.json
    canister_ids.json
    已不存在
  2. 已创建的文件
    icp.yaml
    已存在。如果项目有主网canister,
    .icp/data/mappings/ic.ids.json
    已存在且已提交到版本控制
  3. .gitignore
    配置
    :包含
    .icp/cache/
    ,不包含
    .dfx
    相关配置
  4. 无遗留端口引用:代码库中搜索
    4943
    无匹配结果
  5. 无dfx环境变量模式:搜索
    output_env_file
    CANISTER_ID_
    DFX_NETWORK
    ,配置和源码中无匹配结果
  6. 前端包配置(如果项目使用TypeScript绑定):
    package.json
    中没有
    @dfinity/agent
    ,已安装
    @icp-sdk/core
    @icp-sdk/bindgen
    src/declarations/
    已删除,
    src/bindings/
    已添加到
    .gitignore
  7. Candid文件
    @icp-sdk/bindgen
    使用的
    .did
    文件已提交到仓库
  8. 构建成功
    icp build
    执行无错误
  9. 配置正确
    icp project show
    显示的展开配置符合预期
  10. README文档:引用的是
    icp
    命令(而非
    dfx
    ),描述为“local network”(而非“replica”),端口正确