dApp Docs/msg-chain-devkit CLI工具
Development reference. Not independently verified for production.

@msg-chain/devkit CLI 工具完整指南

数据来源:MSG Chain 代码库核实

主网状态: No-Go — 当前 MSGChain 主网裁决为 No-Go,以下内容反映代码实际状态,不代表生产可用。


目录

  1. 概述
  2. 安装
  3. 命令参考
  4. 完整实现代码
  5. 项目模板
  6. 网络管理
  7. Guard模式
  8. CI/CD集成
  9. 完整的端到端示例
  10. 附录

1. 概述

1.1 什么是 @msg-chain/devkit?

@msg-chain/devkit 是 MSG Chain 官方推出的全栈开发工具包,定位为 "MSG Chain 的 Hardhat"。它是一个基于 Node.js/TypeScript 的命令行工具,为开发者提供从合约编写、编译、测试到部署、验证、上线的完整工作流。

1.2 为什么需要 devkit?

在 MSG Chain 上开发智能合约和去中心化应用,开发者面临以下挑战:

挑战 devkit 解决方案
CosmWasm 合约编译命令冗长 msg-devkit compile — 一键编译所有合约
cargo test 输出缺乏可读性 msg-devkit test — 格式化测试报告
部署流程需要多步操作 msg-devkit deploy — 自动完成 store + instantiate
本地开发环境搭建复杂 msg-devkit node — 一键启动本地节点
生产发布缺乏安全机制 msg-devkit publish --guard — 审批门强制检查
缺少项目脚手架 msg-devkit init — 完整项目模板生成

1.3 与其他工具的对比

特性 @msg-chain/devkit Hardhat Truffle CosmJS CLI
链适配 MSG Chain 专用 EVM 通用 EVM 通用 Cosmos 通用
合约语言 CosmWasm (Rust) Solidity Solidity CosmWasm (Rust)
任务系统 内置任务运行器 Hardhat Tasks 无 无
插件系统 内置插件机制 Hardhat Plugins Truffle Boxes 无
类型安全 TypeScript 优先 JavaScript/TS JavaScript TypeScript
Guard 模式 审批门 无 无 无
合约验证 内置 Etherscan 插件 无
交互控制台 REPL + CosmJS Hardhat Console Truffle Console 无

@msg-chain/devkit 借鉴了 Hardhat 的任务运行器设计理念和 Truffle 的合约管理思想,但同时针对 MSG Chain 的 CosmWasm 生态和 Guard 安全模型做了深度定制。

1.4 核心特性

1.5 MSG Chain 技术参数

参数 值
Chain ID msg-chain-1
Bech32 前缀 msg / msgpub / msgvaloper / msgvaloperpub / msgvalcons / msgvalconspub
CoinType 118
小数位 18
Gas 价格(低/中/高) 1,000,000,000 / 1,000,000,000 / 1,000,000,000 attoMSG
出块时间 5s
共识机制 Round-Robin + DAR
构建产物 bin/genesis_node_linux, bin/quantum_node_linux

2. 安装

2.1 前提条件

依赖 版本要求 用途
Node.js >= 18.0.0 运行时
npm / yarn / pnpm 最新 包管理
Rust >= 1.75.0 合约编译
Go >= 1.24.6 MSG Chain 节点编译
Docker + Docker Compose 最新 本地节点
wasm32-unknown-unknown target rustup target add WASM 编译
wasm-opt (binaryen) 最新 WASM 优化
# 安装 Rust 和 WASM 目标
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
rustup target add wasm32-unknown-unknown

# 安装 binaryen(wasm-opt)
# Ubuntu/Debian
sudo apt install binaryen
# macOS
brew install binaryen

# 安装 Go
wget https://go.dev/dl/go1.24.6.linux-amd64.tar.gz
sudo tar -C /usr/local -xzf go1.24.6.linux-amd64.tar.gz
export PATH=$PATH:/usr/local/go/bin

2.2 全局安装

# npm 全局安装
npm install -g @msg-chain/devkit

# 验证安装
msg-devkit --version
# => 1.0.0

msg-devkit --help
# => @msg-chain/devkit CLI — MSG Chain 智能合约开发工具链

2.3 使用 npx(推荐)

# 无需全局安装,直接使用
npx @msg-chain/devkit init my-project
npx @msg-chain/devkit compile
npx @msg-chain/devkit test

2.4 项目本地安装

# 在已有项目中安装
npm install --save-dev @msg-chain/devkit

# 在 package.json 中添加脚本
# "scripts": {
#   "compile": "msg-devkit compile",
#   "test": "msg-devkit test",
#   "deploy": "msg-devkit deploy",
#   "node": "msg-devkit node"
# }

2.5 环境变量

@msg-chain/devkit 支持以下环境变量配置:

# 节点 RPC 端点
MSG_RPC_URL=http://localhost:26657

# 节点 REST API 端点
MSG_REST_URL=http://localhost:1317

# 部署账户助记词(仅在本地开发使用)
MSG_MNEMONIC="..."

# 签名账户名称
MSG_SIGNER=my-validator-key

# 默认网络
MSG_NETWORK=local

# 日志级别
MSG_LOG_LEVEL=info

3. 命令参考

3.1 命令树概览

msg-devkit
├── init [project-name]        # 脚手架 — 创建新项目
├── compile                    # 编译 — 编译所有合约
├── test                       # 测试 — 运行合约测试
├── deploy                     # 部署 — 部署合约到链上
├── verify                     # 验证 — 验证合约源码
├── console                    # 控制台 — 交互式合约交互
├── node                       # 节点 — 管理本地 MSG Chain 节点
│   ├── start
│   ├── stop
│   └── status
├── faucet                     # 水龙头 — 申请测试代币
├── schema                     # 模式 — 生成 JSON Schema
├── network                    # 网络 — 管理网络配置
│   ├── list
│   ├── add
│   └── use
└── publish                    # 发布 — 受保护的生产部署

3.2 msg-devkit init [project-name]

创建新的 MSG Chain 项目脚手架。

说明: 类似 Hardhat 的 npx hardhat init,但专门为 MSG Chain 的 CosmWasm 合约和 dApp 定制。根据选择的模板生成完整的目录结构、配置文件、合约脚手架和测试框架。

选项:

选项 默认值 描述
--template <type> contract 项目类型: contract, dapp, fullstack
--contract-name <name> my_contract 合约名称(仅 contract 模板)
--framework <lib> react 前端框架: react, next, vanilla(仅 dapp)
--language <lang> typescript 语言: typescript, javascript
--ci <provider> — CI 模板: github-actions
--yes false 跳过确认提示

用法示例:

# 创建合约项目
msg-devkit init my-ai-agent

# 创建 dApp 项目(React + TypeScript)
msg-devkit init my-dapp --template dapp --framework react

# 创建全栈项目(合约 + Next.js 前端)
msg-devkit init my-fullstack --template fullstack --framework next

# 创建合约项目 + GitHub Actions CI
msg-devkit init my-ai-agent --ci github-actions

# 非交互式创建(跳过确认)
msg-devkit init my-ai-agent --yes

生成结构:

my-ai-agent/
├── contracts/
│   └── cosmwasm/
│       └── all/
│           └── my_ai_agent/
│               ├── Cargo.toml
│               ├── src/
│               │   ├── contract.rs
│               │   ├── msg.rs
│               │   ├── state.rs
│               │   ├── error.rs
│               │   ├── lib.rs
│               │   └── schema.rs
│               └── tests/
│                   └── integration.rs
├── scripts/
│   └── deploy.ts
├── test/
│   └── integration/
│       └── my_ai_agent.test.ts
├── msgchain.config.ts
├── networks.json
├── package.json
├── tsconfig.json
├── .gitignore
└── README.md

3.3 msg-devkit compile

编译项目中的所有 CosmWasm 合约。

说明: 内部封装了 MSG Chain 实际的编译命令 cargo build --target wasm32-unknown-unknown --release。自动发现 contracts/cosmwasm/all/ 目录下的所有合约包(MSG Chain 有 46 个合约包),并针对每个合约执行 WASM 编译 + wasm-opt 优化。类似 Hardhat 的 npx hardhat compile。

实际包装的命令:

# 对每个合约执行:
cd contracts/cosmwasm/all/{name} && cargo build --target wasm32-unknown-unknown --release

选项:

选项 默认值 描述
--contract <name> 全部 仅编译指定合约
--optimize true 启用 wasm-opt 优化
--optimize-level <level> z 优化级别: z(size), 2(speed), 3(aggressive)
--out-dir <path> ./artifacts 输出目录
--watch false 监听文件变化自动重编译
--force false 强制重新编译(忽略缓存)

用法示例:

# 编译所有合约
msg-devkit compile

# 编译指定合约
msg-devkit compile --contract my_ai_agent

# 编译 + 禁用 wasm-opt(用于调试)
msg-devkit compile --optimize false

# 编译 + 监听模式
msg-devkit compile --watch

# 指定输出目录
msg-devkit compile --out-dir ./build/wasm

编译输出示例:

$ msg-devkit compile

  @msg-chain/devkit 编译引擎
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

 发现 3 个合约包:
  ├─ my_ai_agent (contracts/cosmwasm/all/my_ai_agent)
  ├─ data_oracle (contracts/cosmwasm/all/data_oracle)
  └─ token_bridge (contracts/cosmwasm/all/token_bridge)

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

  编译 my_ai_agent...
   cargo build --target wasm32-unknown-unknown --release
   ✓ 编译成功 (12.4s)
   → wasm-opt -Oz artifacts/my_ai_agent.wasm
   ✓ 优化完成 (1.2s, 减少 23% 大小)

  编译 data_oracle...
   cargo build --target wasm32-unknown-unknown --release
   ✓ 编译成功 (8.7s)
   → artifacts/data_oracle.wasm

  编译 token_bridge...
   cargo build --target wasm32-unknown-unknown --release
   ✓ 编译成功 (15.3s)
   → artifacts/token_bridge.wasm

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
✓ 3/3 合约编译成功
  输出目录: ./artifacts
⏱  总耗时: 36.4s

  编译产物清单:
  ├─ artifacts/my_ai_agent.wasm    (148 KB)
  ├─ artifacts/data_oracle.wasm    (112 KB)
  ├─ artifacts/token_bridge.wasm   (196 KB)
  └─ artifacts/checksums.txt

3.4 msg-devkit test

运行项目中所有合约的单元测试和集成测试。

说明: 内部封装了 MSG Chain 的实际测试命令 cargo test。自动发现 contracts/cosmwasm/all/ 目录下的所有合约包,对每个合约执行 cargo test。同时可运行 TypeScript 集成测试。

实际包装的命令:

# 对每个合约执行:
cd contracts/cosmwasm/all/{name} && cargo test

# 可选集成测试:
npx jest test/integration/

选项:

选项 默认值 描述
--contract <name> 全部 仅测试指定合约
--test-name <pattern> — 仅运行匹配的测试
--integration false 包含 TypeScript 集成测试
--coverage false 生成测试覆盖率报告
--verbose false 详细输出
--no-rust false 跳过 Rust 测试

用法示例:

# 运行所有合约测试
msg-devkit test

# 测试指定合约
msg-devkit test --contract my_ai_agent

# 运行特定测试函数
msg-devkit test --test-name test_instantiate

# 包含集成测试
msg-devkit test --integration

# 生成覆盖率报告
msg-devkit test --coverage

# 详细输出
msg-devkit test --verbose

测试输出示例:

$ msg-devkit test

  @msg-chain/devkit 测试运行器
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

 发现 3 个合约包需要测试
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

  my_ai_agent — cargo test
   running 8 tests
   ✓ test_instantiate           (0.02s)
   ✓ test_execute_create_agent  (0.15s)
   ✓ test_execute_update_agent  (0.12s)
   ✓ test_execute_delete_agent  (0.08s)
   ✓ test_query_get_agent       (1,000,000,000 attoMSG)
   ✓ test_query_list_agents     (1,000,000,000 attoMSG)
   ✓ test_execute_deposit       (0.09s)
   ✓ test_execute_withdraw      (0.11s)
   ✓ 8 passed, 0 failed (0.59s)

  data_oracle — cargo test
   running 12 tests
   ✓ 12 passed, 0 failed (0.87s)

  token_bridge — cargo test
   running 15 tests
   ✓ 15 passed, 0 failed (1.23s)

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
✓ 35/35 测试通过
⏱  总耗时: 2.69s

  集成测试 — jest
   ✓ my_ai_agent.integration.test.ts (4.2s)
   ✓ deployment.test.ts (2.1s)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
✓ 全部测试通过

3.5 msg-devkit deploy

部署合约到目标网络。

说明: 实现完整的 CosmWasm 合约部署流程:Store Code → Instantiate Contract。内部使用 msgd tx wasm store 和 msgd tx wasm instantiate 实际命令。类似 Truffle 的 truffle migrate。

实际包装的命令:

# Store Code
msgd tx wasm store artifacts/my_contract.wasm \
  --from $SIGNER \
  --gas auto --gas-adjustment 1.3 \
  --gas-prices 1000000000attoMSG \
  --chain-id msg-chain-1 \
  --node tcp://localhost:26657 -y

# Instantiate
msgd tx wasm instantiate $CODE_ID '{"key":"value"}' \
  --label "my-contract" \
  --admin $ADMIN \
  --from $SIGNER \
  --chain-id msg-chain-1 \
  --node tcp://localhost:26657 -y

选项:

选项 默认值 描述
--network <name> local 目标网络
--contract <name> 全部 仅部署指定合约
--signer <name> — 签名账户名称
--gas <price> 1000000000attoMSG Gas 价格
--gas-limit <limit> 3000000 Gas 上限
--label <label> — 合约实例标签
--admin <address> — 合约管理员地址
--no-admin false 移除管理员(合约不可变)
--dry-run false 模拟部署
--guard false 启用 Guard 模式(生产环境必用)

用法示例:

# 部署到本地网络
msg-devkit deploy --network local

# 部署指定合约
msg-devkit deploy --contract my_ai_agent --network local

# 部署 + Guard 模式(生产必用)
msg-devkit deploy --network testnet --guard

# 无管理员部署(合约不可变)
msg-devkit deploy --no-admin

# 指定 gas 和签名者
msg-devkit deploy --network local --signer validator --gas 1000000000attoMSG

# 模拟部署(不发送交易)
msg-devkit deploy --dry-run

# 部署到主网(需经过审批门)
msg-devkit deploy --network mainnet --guard

部署输出示例:

$ msg-devkit deploy --network local --contract my_ai_agent

  @msg-chain/devkit 部署引擎
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

  目标网络: local (msg-chain-1)
  签名账户: validator
  Gas price: 1000000000attoMSG | Gas limit: 3,000,000

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

  步骤 1/3: Store Code — my_ai_agent.wasm
   → 读取 artifacts/my_ai_agent.wasm (148 KB)
   → 发送 StoreCode 交易...
   ✓ Code ID: 1 (tx: 9A2B3C...DEF)
   ⏱  确认时间: 5.2s (1 block)

  步骤 2/3: Instantiate Contract
   → 消息: {"agent_name":"MyAgent","owner":"msg1..."}
   → 发送 Instantiate 交易...
   ✓ 合约地址: msg1x2y3z4...abc (tx: 1D2E3F...456)
   ⏱  确认时间: 5.1s (1 block)

  步骤 3/3: 验证部署
   ✓ 合约状态: 活跃
   ✓ 代码 ID 1 对应 my_ai_agent
   ✓ 管理员: msg1admin...xyz

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
✓ 部署成功!
  合约地址: msg1x2y3z4...abc
  Code ID: 1
  交易: https://explorer.msgchain.org/tx/9A2B3C...DEF
  部署记录已保存至 ./deployments/local.json

部署记录文件 (deployments/local.json):

{
  "network": "local",
  "chainId": "msg-chain-1",
  "timestamp": "2026-07-06T10:30:00Z",
  "contracts": {
    "my_ai_agent": {
      "codeId": 1,
      "address": "msg1x2y3z4...abc",
      "txHash": "9A2B3C...DEF",
      "label": "my-ai-agent-v1",
      "admin": "msg1admin...xyz",
      "wasmHash": "sha256:a1b2c3d4...",
      "instantiateMsg": {
        "agent_name": "MyAgent",
        "owner": "msg1..."
      }
    }
  },
  "gasUsed": "2450000"
}

3.6 msg-devkit verify

验证已部署合约的源码。

说明: 将合约 WASM 哈希与链上存储的哈希进行比对,确保合约源代码与部署内容一致。可选的源码上传功能支持将合约源代码发布到区块浏览器供公开验证。

选项:

选项 默认值 描述
--network <name> local 目标网络
--contract <name> — 合约名称(对应 artifacts)
--address <address> — 合约地址
--code-id <id> — Code ID
--upload false 将源码上传到验证服务

用法示例:

# 验证 artifacts 中所有合约
msg-devkit verify --network local

# 验证指定合约
msg-devkit verify --contract my_ai_agent --network local

# 验证指定地址
msg-devkit verify --address msg1x2y3z4...abc

# 验证指定 Code ID
msg-devkit verify --code-id 1 --network testnet

# 验证并上传源码到浏览器
msg-devkit verify --contract my_ai_agent --upload

验证输出示例:

$ msg-devkit verify --network local

✓ 验证通过 — my_ai_agent
  本地 SHA256:  a1b2c3d4e5f6...
  链上 SHA256:  a1b2c3d4e5f6...
  ✓ 匹配

✓ 验证通过 — data_oracle
  本地 SHA256:  b2c3d4e5f6a1...
  链上 SHA256:  b2c3d4e5f6a1...
  ✓ 匹配

✗ 验证失败 — token_bridge
  本地 SHA256:  c3d4e5f6a1b2...
  链上 SHA256:  d4e5f6a1b2c3...
  ✗ 不匹配 — 源代码已更改,请重新编译

3.7 msg-devkit console

启动交互式控制台,用于与已部署合约进行交互。

说明: 提供一个 Node.js REPL 环境,内置 CosmJS 客户端、已加载的合约实例和 MSG Chain 连接。类似 Truffle Console 和 Hardhat Console。内置 client(SigningCosmWasmClient)、accounts、contracts、msg(工具函数)等全局变量。

选项:

选项 默认值 描述
--network <name> local 目标网络
--signer <name> — 签名账户名称
--contract <name> — 预加载指定合约
--eval <code> — 执行单行代码后退出
--file <path> — 执行脚本文件后退出

用法示例:

# 启动控制台(默认本地网络)
msg-devkit console

# 加载指定网络的合约
msg-devkit console --network testnet

# 执行单条查询
msg-devkit console --eval 'contract.query({get_agent: {id: "1"}})'

# 执行脚本文件
msg-devkit console --file scripts/query_all_agents.ts

控制台交互示例:

$ msg-devkit console --network local

  @msg-chain/devkit 控制台
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
  网络: local (msg-chain-1)
  签名者: validator
  Gas: 1000000000attoMSG

可用变量:
  client    — CosmJS SigningCosmWasmClient
  accounts  — [name: validator, address: msg1...]
  contracts — 已部署合约实例
  msg       — CosmJS 工具函数
  tx        — 发送交易

示例:
  await client.queryContractSmart("msg1...", { get_agent: { id: "1" } })
  await contract.increment()
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

msg> await client.getChainId()
'msg-chain-1'

msg> const agent = await client.queryContractSmart(
...   "msg1x2y3z4...abc",
...   { get_agent: { id: "1" } }
... )
{ id: "1", name: "MyAgent", owner: "msg1...", status: "active" }

msg> agent.name
'MyAgent'

msg> const tx = await client.execute(
...   "msg1x2y3z4...abc",
...   { update_agent: { id: "1", status: "paused" } },
...   "auto"
... )
{ transactionHash: "E5F6...789", height: 1284 }

msg> .exit
 再见!

3.8 msg-devkit node

管理本地 MSG Chain 开发节点。

说明: 封装 Docker Compose 和 msgd start 命令,提供一键启动、停止、查看本地节点的能力。支持多节点集群模式。

子命令:

子命令 描述
start 启动本地节点
stop 停止本地节点
status 查看节点状态
logs 查看节点日志
reset 重置节点数据

选项(对 start 子命令):

选项 默认值 描述
--clean false 启动时清理旧数据
--validators <n> 1 验证节点数量
--ports 26657,1317,9090 暴露端口
--detach true 后台运行

用法示例:

# 启动本地节点
msg-devkit node start

# 启动并清理旧数据
msg-devkit node start --clean

# 启动 3 节点集群
msg-devkit node start --validators 3

# 前台运行
msg-devkit node start --detach false

# 停止节点
msg-devkit node stop

# 查看状态
msg-devkit node status

# 查看日志
msg-devkit node logs

# 重置数据(清空所有链上数据)
msg-devkit node reset

节点启动输出示例:

$ msg-devkit node start --clean

  @msg-chain/devkit 节点管理
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

 清理旧数据...
   ✓ /tmp/msg-chain/data 已清理

 启动 Docker Compose 服务...
   ✓ msg-chain-node-1 已启动
   ✓ msg-chain-db-1 已启动

 等待节点同步... (5s)
   ✓ 节点正在产生区块

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
✅ 本地节点运行中

  RPC:     http://localhost:26657
  REST:    http://localhost:1317
  gRPC:    localhost:9090
  Faucet:  http://localhost:8000
  Explorer:http://localhost:3000

 验证者信息:
  ├─ 地址: msg1validator...abc
  ├─ 质押: 10000000umsg
  └─ 状态: 投票中

 Block Time: 5s
 共识机制: Round-Robin + DAR
 区块高度: 24 | 交易数: 12

3.9 msg-devkit faucet

从水龙头申请测试代币。

说明: 为开发测试网络申请 umsg 测试代币。支持自动创建水龙头请求、查看余额。

选项:

选项 默认值 描述
--address <addr> — 接收地址
--amount <amount> 1000000 申请数量(umsg)
--network <name> local 目标网络

用法示例:

# 申请默认数量测试代币(本地网络)
msg-devkit faucet --address msg1...

# 指定申请数量
msg-devkit faucet --address msg1... --amount 5000000

# 从 testnet 水龙头申请
msg-devkit faucet --address msg1... --network testnet

输出示例:

$ msg-devkit faucet --address msg1abc...def --amount 1000000

  @msg-chain/devkit 水龙头
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

 地址:  msg1abc...def
 数量:  1000000 umsg (1 MSG)
 网络:  local

 正在处理...
✓ 成功! 交易哈希: FAUCET...123

 新余额:
  账户: msg1abc...def
  余额: 1000000 umsg

3.10 msg-devkit schema

生成合约的 JSON Schema 文件。

说明: 对 CosmWasm 合约的 QueryMsg 和 ExecuteMsg 生成 JSON Schema,便于前端开发、API 文档生成和类型安全交互。内部使用 cargo run --bin schema 或从 msg.rs 源码解析。

选项:

选项 默认值 描述
--contract <name> 全部 仅生成指定合约
--out-dir <path> ./schemas 输出目录
--format json 格式: json, yaml

用法示例:

# 生成所有合约的 Schema
msg-devkit schema

# 生成指定合约
msg-devkit schema --contract my_ai_agent

# 指定输出目录
msg-devkit schema --out-dir ./api/schemas

输出示例:

$ msg-devkit schema

  @msg-chain/devkit Schema 生成器
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

  my_ai_agent
  ✓ InstantiateMsg   → schemas/my_ai_agent/instantiate_msg.json
  ✓ ExecuteMsg       → schemas/my_ai_agent/execute_msg.json
  ✓ QueryMsg         → schemas/my_ai_agent/query_msg.json
  ✓ State            → schemas/my_ai_agent/state.json

  data_oracle
  ✓ schemas/data_oracle/  (4 files)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
✓ 8 schema files generated in schemas/

3.11 msg-devkit network

管理 MSG Chain 网络配置。

说明: 列出、添加、切换网络配置。支持预设网络和自定义网络。

子命令:

子命令 描述
list 列出所有网络配置
add <name> 添加新网络
use <name> 切换默认网络

选项(对 add 子命令):

选项 默认值 描述
--rpc <url> — RPC 端点
--rest <url> — REST 端点
--chain-id <id> — 链 ID
--explorer <url> — 浏览器 URL

用法示例:

# 列出所有网络
msg-devkit network list

# 添加自定义网络
msg-devkit network add mynet \
  --rpc https://rpc.mynet.msgchain.org \
  --rest https://rest.mynet.msgchain.org \
  --chain-id mynet-1

# 切换默认网络
msg-devkit network use testnet

网络列表输出示例:

$ msg-devkit network list

  @msg-chain/devkit 网络配置
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

  local (默认)      msg-chain-1
  ├─ RPC:  http://localhost:26657
  ├─ REST: http://localhost:1317
  └─ Gas:  1000000000attoMSG

  devnet            devnet-1
  ├─ RPC:  https://rpc.devnet.msgchain.org
  ├─ REST: https://rest.devnet.msgchain.org
  └─ Gas:  1000000000attoMSG

  testnet           testnet-1
  ├─ RPC:  https://rpc.testnet.msgchain.org
  ├─ REST: https://rest.testnet.msgchain.org
  └─ Gas:  1000000000attoMSG

  mainnet           msg-chain-1
  ├─ RPC:  https://rpc.mainnet.msgchain.org
  ├─ REST: https://rest.mainnet.msgchain.org
  └─ Gas:  1000000000attoMSG(推荐高 gas)

3.12 msg-devkit publish

受保护的生产部署流程。

说明: 这是 devkit 最核心的安全特性 — 带审批门(Approval Gates)的生产发布命令。必须使用 --guard 标志,否则命令拒绝执行。发布前依次检查 4 道审批门,收集人工审批证据。

requires_human_approval: true — 此命令在所有生产环境操作前要求人工批准。

实际审批门定义(来自 MSG Chain approval_gates.json):

审批门 ID 阶段 需要人工输入
scope_lock before_codegen 产品目标, 业务规则, 验收标准
secret_injection before_real_write_or_deploy 签名账户, API token, 环境变量
production_release before_launch 最终审批, 回滚确认
governance_or_treasury high_risk_write DAO通过, timelock结束

选项:

选项 默认值 描述
--guard false 必须指定 — 启用审批门
--network <name> — 目标网络(必须是 mainnet)
--contract <name> 全部 发布的合约
--signer <name> — 签名账户
--tag <version> — 发布版本标签
--skip-gates <ids> — 跳过指定审批门(需要特殊权限)
--dry-run false 模拟发布

用法示例:

# Guard 模式发布(完整审批流程)
msg-devkit publish --guard --network mainnet --tag v1.0.0

# 跳过特定审批门(需要管理员权限)
msg-devkit publish --guard --network mainnet --skip-gates governance_or_treasury

发布流程输出示例:

$ msg-devkit publish --guard --network mainnet --tag v1.0.0

  @msg-chain/devkit — GUARD 模式发布
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
⚠️  生产环境部署 — 审批门已启用
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

 审批门 1/4: scope_lock (before_codegen)
   需要审批: 产品目标, 业务规则, 验收标准
   请输入产品目标:
   > 发布 AI Agent 合约 v1.0.0 至主网
   请输入业务规则:
   > Agent 创建需支付 0.1 MSG 押金
   请输入验收标准:
   > 所有测试通过, Code ID 对应正确哈希
   ✓ 已记录

 审批门 2/4: secret_injection (before_real_write_or_deploy)
   需要审批: 签名账户, API token, 环境变量
   请输入签名账户:
   > mainnet-deployer-1
   请输入 API token:
   > ******** (安全输入)
   请输入环境变量确认:
   > NODE_ENV=production, MSG_NETWORK=mainnet
   ✓ 已记录

 审批门 3/4: production_release (before_launch)
   需要审批: 最终审批, 回滚确认
   最终审批确认 (yes/no):
   > yes
   回滚方案确认 (yes/no):
   > yes
   ✓ 已记录

 审批门 4/4: governance_or_treasury (high_risk_write)
   需要审批: DAO通过, timelock结束
   DAO 提案通过确认 (yes/no):
   > yes
   Timelock 结束确认 (yes/no):
   > yes
   ✓ 已记录

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
✅ 全部审批门已通过 — 开始发布流程
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

  步骤 1/4: 编译合约 (release profile)
   ✓ my_ai_agent — compiled (optimized)

  步骤 2/4: 存储代码 — Store Code
   ✓ Code ID: 42 (tx: MAINNET...ABC)

  步骤 3/4: 实例化合约
   ✓ 合约地址: msg1production...xyz

  步骤 4/4: 验证与证据收集
   ✓ 合约验证通过
   ✓ 交易哈希已记录: MAINNET...ABC
   ✓ 区块确认数: 12
   ✓ 余额检查通过

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
✅ 发布完成 (v1.0.0)
  证据存档: .approval-evidence/v1.0.0/
  ├─ scope_lock.md
  ├─ secret_injection.md
  ├─ production_release.md
  ├─ governance_or_treasury.md
  ├─ deployment_receipt.json
  └─ post_state_query.json

4. 完整实现代码

4.1 项目结构

@msg-chain/devkit/
├── package.json
├── tsconfig.json
├── src/
│   ├── index.ts                  # 入口
│   ├── cli.ts                    # CLI 主程序 (commander)
│   ├── commands/
│   │   ├── init.ts               # msg-devkit init
│   │   ├── compile.ts            # msg-devkit compile
│   │   ├── test.ts               # msg-devkit test
│   │   ├── deploy.ts             # msg-devkit deploy
│   │   ├── verify.ts             # msg-devkit verify
│   │   ├── console.ts            # msg-devkit console
│   │   ├── node.ts               # msg-devkit node
│   │   ├── faucet.ts             # msg-devkit faucet
│   │   ├── schema.ts             # msg-devkit schema
│   │   ├── network.ts            # msg-devkit network
│   │   └── publish.ts            # msg-devkit publish
│   ├── core/
│   │   ├── compile-engine.ts     # 编译引擎
│   │   ├── test-runner.ts        # 测试运行器
│   │   ├── deploy-engine.ts      # 部署引擎
│   │   ├── scaffold.ts           # 脚手架生成器
│   │   ├── node-manager.ts       # 节点管理器
│   │   ├── guard.ts              # Guard 审批门引擎
│   │   ├── ci-generator.ts       # CI 模板生成器
│   │   └── schema-generator.ts   # Schema 生成器
│   ├── config/
│   │   ├── config-loader.ts      # 配置加载
│   │   ├── defaults.ts           # 默认配置
│   │   └── networks.ts           # 网络管理
│   ├── templates/
│   │   ├── contract/             # 合约模板文件
│   │   ├── dapp/                 # dApp 模板文件
│   │   └── ci/                   # CI 模板文件
│   └── types/
│       └── index.ts              # 类型定义
├── artifacts/                    # 编译输出
└── schemas/                      # Schema 输出

4.2 package.json

{
  "name": "@msg-chain/devkit",
  "version": "1.0.0",
  "description": "MSG Chain 智能合约开发工具链 — 类似 Hardhat 的任务运行器 + Truffle 的合约管理",
  "keywords": ["msg-chain", "cosmwasm", "blockchain", "smart-contracts", "cli"],
  "homepage": "https://github.com/msg-chain/devkit",
  "repository": {
    "type": "git",
    "url": "https://github.com/msg-chain/devkit.git"
  },
  "license": "Apache-2.0",
  "bin": {
    "msg-devkit": "./dist/index.js"
  },
  "files": ["dist/", "templates/", "README.md"],
  "scripts": {
    "build": "tsc",
    "prepublishOnly": "npm run build",
    "dev": "ts-node src/index.ts",
    "test": "jest",
    "lint": "eslint src/ --ext .ts",
    "watch": "tsc --watch"
  },
  "engines": {
    "node": ">=18.0.0"
  },
  "dependencies": {
    "commander": "^12.0.0",
    "chalk": "^5.3.0",
    "ora": "^8.0.1",
    "inquirer": "^9.2.0",
    "cosmjs-types": "^0.9.0",
    "@cosmjs/stargate": "^0.32.0",
    "@cosmjs/cosmwasm-stargate": "^0.32.0",
    "@cosmjs/proto-signing": "^0.32.0",
    "@cosmjs/encoding": "^0.32.0",
    "@cosmjs/faucet": "^0.32.0",
    "fs-extra": "^11.2.0",
    "glob": "^10.3.0",
    "listr2": "^8.0.0",
    "conf": "^12.0.0",
    "execa": "^8.0.0",
    "chokidar": "^3.6.0",
    "semver": "^7.6.0",
    "js-yaml": "^4.1.0",
    "text-table": "^0.2.0"
  },
  "devDependencies": {
    "@types/node": "^20.11.0",
    "@types/fs-extra": "^11.0.4",
    "@types/glob": "^8.1.0",
    "@types/inquirer": "^9.0.7",
    "@types/semver": "^7.5.8",
    "@types/text-table": "^0.2.5",
    "typescript": "^5.4.0",
    "ts-node": "^10.9.0",
    "jest": "^29.7.0",
    "ts-jest": "^29.1.0",
    "@types/jest": "^29.5.0",
    "eslint": "^8.56.0",
    "@typescript-eslint/eslint-plugin": "^7.0.0",
    "@typescript-eslint/parser": "^7.0.0"
  }
}

4.3 tsconfig.json

{
  "compilerOptions": {
    "target": "ES2022",
    "module": "NodeNext",
    "moduleResolution": "NodeNext",
    "lib": ["ES2022"],
    "outDir": "./dist",
    "rootDir": "./src",
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true,
    "resolveJsonModule": true,
    "declaration": true,
    "declarationMap": true,
    "sourceMap": true,
    "baseUrl": ".",
    "paths": {
      "@/*": ["src/*"]
    }
  },
  "include": ["src/**/*"],
  "exclude": ["node_modules", "dist", "templates"]
}

4.4 入口与 CLI 框架 (src/index.ts)

#!/usr/bin/env node
import { Command } from 'commander';
import chalk from 'chalk';
import figlet from 'figlet';
import { initCommand } from './commands/init.js';
import { compileCommand } from './commands/compile.js';
import { testCommand } from './commands/test.js';
import { deployCommand } from './commands/deploy.js';
import { verifyCommand } from './commands/verify.js';
import { consoleCommand } from './commands/console.js';
import { nodeCommand } from './commands/node.js';
import { faucetCommand } from './commands/faucet.js';
import { schemaCommand } from './commands/schema.js';
import { networkCommand } from './commands/network.js';
import { publishCommand } from './commands/publish.js';
import { loadConfig } from './config/config-loader.js';

const VERSION = '1.0.0';

function showBanner(): void {
  console.log(
    chalk.cyan(
      figlet.textSync('MSG Chain DevKit', { font: 'ANSI Shadow' })
    )
  );
  console.log(chalk.gray('@msg-chain/devkit v' + VERSION + ' — MSG Chain 智能合约开发工具链'));
  console.log(chalk.gray('类似 Hardhat 的任务运行器 + 类似 Truffle 的合约管理\n'));
}

async function main() {
  showBanner();

  const program = new Command();

  program
    .name('msg-devkit')
    .description('MSG Chain 智能合约开发工具链 — CosmWasm 合约开发、编译、测试、部署一站式工具')
    .version(VERSION);

  program
    .command('init')
    .argument('[project-name]', '项目名称')
    .option('--template <type>', '项目类型 (contract, dapp, fullstack)', 'contract')
    .option('--contract-name <name>', '合约名称', 'my_contract')
    .option('--framework <lib>', '前端框架 (react, next, vanilla)', 'react')
    .option('--language <lang>', '语言 (typescript, javascript)', 'typescript')
    .option('--ci <provider>', 'CI 模板 (github-actions)')
    .option('--yes', '跳过确认', false)
    .description('创建新的 MSG Chain 项目')
    .action(initCommand);

  program
    .command('compile')
    .option('--contract <name>', '仅编译指定合约')
    .option('--optimize <bool>', '启用 wasm-opt 优化', 'true')
    .option('--optimize-level <level>', '优化级别 (z, 2, 3)', 'z')
    .option('--out-dir <path>', '输出目录', './artifacts')
    .option('--watch', '监听模式', false)
    .option('--force', '强制重新编译', false)
    .description('编译 CosmWasm 合约 (封装 cargo build --target wasm32-unknown-unknown --release)')
    .action(compileCommand);

  program
    .command('test')
    .option('--contract <name>', '仅测试指定合约')
    .option('--test-name <pattern>', '仅运行匹配的测试')
    .option('--integration', '包含集成测试', false)
    .option('--coverage', '生成覆盖率报告', false)
    .option('--verbose', '详细输出', false)
    .option('--no-rust', '跳过 Rust 测试', false)
    .description('运行合约测试 (封装 cargo test)')
    .action(testCommand);

  program
    .command('deploy')
    .option('--network <name>', '目标网络', 'local')
    .option('--contract <name>', '仅部署指定合约')
    .option('--signer <name>', '签名账户')
    .option('--gas <price>', 'Gas 价格', '1000000000attoMSG')
    .option('--gas-limit <limit>', 'Gas 上限', '3000000')
    .option('--label <label>', '合约实例标签')
    .option('--admin <address>', '合约管理员地址')
    .option('--no-admin', '移除管理员(合约不可变)', false)
    .option('--dry-run', '模拟部署', false)
    .option('--guard', '启用 Guard 审批门模式', false)
    .description('部署合约到目标网络 (封装 msgd tx wasm store/instantiate)')
    .action(deployCommand);

  program
    .command('verify')
    .option('--network <name>', '目标网络', 'local')
    .option('--contract <name>', '合约名称')
    .option('--address <address>', '合约地址')
    .option('--code-id <id>', 'Code ID')
    .option('--upload', '上传源码到验证服务', false)
    .description('验证已部署合约的源代码')
    .action(verifyCommand);

  program
    .command('console')
    .option('--network <name>', '目标网络', 'local')
    .option('--signer <name>', '签名账户')
    .option('--contract <name>', '预加载合约')
    .option('--eval <code>', '执行单行代码后退出')
    .option('--file <path>', '执行脚本文件后退出')
    .description('启动交互式控制台 (REPL + CosmJS)')
    .action(consoleCommand);

  const nodeCmd = program
    .command('node')
    .description('管理本地 MSG Chain 节点');

  nodeCmd
    .command('start')
    .option('--clean', '启动时清理旧数据', false)
    .option('--validators <n>', '验证节点数量', '1')
    .option('--ports <ports>', '暴露端口', '26657,1317,9090')
    .option('--detach', '后台运行', 'true')
    .description('启动本地节点')
    .action((opts) => nodeCommand('start', opts));

  nodeCmd
    .command('stop')
    .description('停止本地节点')
    .action(() => nodeCommand('stop', {}));

  nodeCmd
    .command('status')
    .description('查看节点状态')
    .action(() => nodeCommand('status', {}));

  nodeCmd
    .command('logs')
    .description('查看节点日志')
    .action(() => nodeCommand('logs', {}));

  nodeCmd
    .command('reset')
    .description('重置节点数据')
    .action(() => nodeCommand('reset', {}));

  program
    .command('faucet')
    .option('--address <addr>', '接收地址')
    .option('--amount <amount>', '申请数量 (umsg)', '1000000')
    .option('--network <name>', '目标网络', 'local')
    .description('从水龙头申请测试代币')
    .action(faucetCommand);

  program
    .command('schema')
    .option('--contract <name>', '仅生成指定合约')
    .option('--out-dir <path>', '输出目录', './schemas')
    .option('--format <format>', '输出格式 (json, yaml)', 'json')
    .description('生成合约 JSON Schema')
    .action(schemaCommand);

  const networkCmd = program
    .command('network')
    .description('管理网络配置');

  networkCmd
    .command('list')
    .description('列出所有网络配置')
    .action(() => networkCommand('list', {}));

  networkCmd
    .command('add')
    .argument('<name>', '网络名称')
    .option('--rpc <url>', 'RPC 端点')
    .option('--rest <url>', 'REST 端点')
    .option('--chain-id <id>', '链 ID')
    .option('--explorer <url>', '浏览器 URL')
    .description('添加新网络')
    .action((name, opts) => networkCommand('add', { ...opts, name }));

  networkCmd
    .command('use')
    .argument('<name>', '网络名称')
    .description('切换默认网络')
    .action((name) => networkCommand('use', { name }));

  program
    .command('publish')
    .option('--guard', '启用 Guard 审批门模式 (生产环境必须)', false)
    .option('--network <name>', '目标网络')
    .option('--contract <name>', '发布的合约')
    .option('--signer <name>', '签名账户')
    .option('--tag <version>', '发布版本标签')
    .option('--skip-gates <ids>', '跳过的审批门 ID 列表,逗号分隔')
    .option('--dry-run', '模拟发布', false)
    .description('受保护的生产部署 (requires_human_approval: true)')
    .action(publishCommand);

  await program.parseAsync(process.argv);
}

main().catch((err) => {
  console.error(chalk.red('\n错误: ' + err.message));
  process.exit(1);
});

4.5 类型定义 (src/types/index.ts)

export interface NetworkConfig {
  name: string;
  chainId: string;
  rpcUrl: string;
  restUrl: string;
  explorerUrl?: string;
  gasPrice: string;
  gasLimit: number;
  denom: string;
  faucetUrl?: string;
  bech32Config: Bech32Config;
}

export interface Bech32Config {
  bech32PrefixAccAddr: string;
  bech32PrefixAccPub: string;
  bech32PrefixValAddr: string;
  bech32PrefixValPub: string;
  bech32PrefixConsAddr: string;
  bech32PrefixConsPub: string;
}

export interface MsgChainConfig {
  projectName: string;
  networks: Record<string, NetworkConfig>;
  defaultNetwork: string;
  contractsPath: string;
  artifactsDir: string;
  compiler: {
    optimize: boolean;
    optimizeLevel: 'z' | '2' | '3';
    target: string;
  };
  deploy: {
    gasPrice: string;
    gasLimit: number;
    signer: string;
  };
}

export interface DeployResult {
  network: string;
  chainId: string;
  timestamp: string;
  contracts: Record<string, ContractDeployInfo>;
  gasUsed: string;
}

export interface ContractDeployInfo {
  codeId: number;
  address: string;
  txHash: string;
  label: string;
  admin: string;
  wasmHash: string;
  instantiateMsg: Record<string, unknown>;
}

export interface ApprovalGate {
  id: string;
  stage: 'before_codegen' | 'before_real_write_or_deploy' | 'before_launch' | 'high_risk_write';
  requiredHumanInputs: string[];
}

export interface ApprovalEvidence {
  gate: ApprovalGate;
  responses: Record<string, string>;
  timestamp: string;
  approvedBy: string;
}

export interface ContractPackage {
  name: string;
  path: string;
  cargoPath: string;
}

export interface CompileResult {
  contractName: string;
  success: boolean;
  duration: number;
  wasmSize?: number;
  optimizedSize?: number;
  error?: string;
}

export interface TestResult {
  contractName: string;
  passed: number;
  failed: number;
  duration: number;
  tests: Array<{
    name: string;
    status: 'passed' | 'failed';
    duration: number;
  }>;
}

export interface NetworkOperationResult {
  success: boolean;
  txHash?: string;
  codeId?: number;
  contractAddress?: string;
  height?: number;
  gasUsed?: string;
  error?: string;
}

export interface ScaffoldOptions {
  projectName: string;
  template: 'contract' | 'dapp' | 'fullstack';
  contractName?: string;
  framework?: string;
  language?: string;
  ci?: string;
  yes?: boolean;
}

4.6 配置系统 (src/config/defaults.ts)

import { MsgChainConfig, NetworkConfig } from '../types/index.js';

export const BECH32_PREFIX = 'msg';
export const CHAIN_ID = 'msg-chain-1';
export const COIN_TYPE = 118;
export const DECIMALS = 18;
export const DENOM = 'umsg';

export const GAS_PRESETS = {
  low: '1000000000attoMSG',
  average: '1000000000attoMSG',
  high: '1000000000attoMSG',
};

export const DEFAULT_NETWORKS: Record<string, NetworkConfig> = {
  local: {
    name: 'local',
    chainId: 'msg-chain-1',
    rpcUrl: 'http://localhost:26657',
    restUrl: 'http://localhost:1317',
    explorerUrl: 'http://localhost:3000',
    gasPrice: '1000000000attoMSG',
    gasLimit: 3000000,
    denom: DENOM,
    faucetUrl: 'http://localhost:8000',
    bech32Config: {
      bech32PrefixAccAddr: 'msg',
      bech32PrefixAccPub: 'msgpub',
      bech32PrefixValAddr: 'msgvaloper',
      bech32PrefixValPub: 'msgvaloperpub',
      bech32PrefixConsAddr: 'msgvalcons',
      bech32PrefixConsPub: 'msgvalconspub',
    },
  },
  devnet: {
    name: 'devnet',
    chainId: 'devnet-1',
    rpcUrl: 'https://rpc.devnet.msgchain.org',
    restUrl: 'https://rest.devnet.msgchain.org',
    explorerUrl: 'https://explorer.devnet.msgchain.org',
    gasPrice: '1000000000attoMSG',
    gasLimit: 3000000,
    denom: DENOM,
    bech32Config: {
      bech32PrefixAccAddr: 'msg',
      bech32PrefixAccPub: 'msgpub',
      bech32PrefixValAddr: 'msgvaloper',
      bech32PrefixValPub: 'msgvaloperpub',
      bech32PrefixConsAddr: 'msgvalcons',
      bech32PrefixConsPub: 'msgvalconspub',
    },
  },
  testnet: {
    name: 'testnet',
    chainId: 'testnet-1',
    rpcUrl: 'https://rpc.testnet.msgchain.org',
    restUrl: 'https://rest.testnet.msgchain.org',
    explorerUrl: 'https://explorer.testnet.msgchain.org',
    gasPrice: '1000000000attoMSG',
    gasLimit: 5000000,
    denom: DENOM,
    bech32Config: {
      bech32PrefixAccAddr: 'msg',
      bech32PrefixAccPub: 'msgpub',
      bech32PrefixValAddr: 'msgvaloper',
      bech32PrefixValPub: 'msgvaloperpub',
      bech32PrefixConsAddr: 'msgvalcons',
      bech32PrefixConsPub: 'msgvalconspub',
    },
  },
  mainnet: {
    name: 'mainnet',
    chainId: 'msg-chain-1',
    rpcUrl: 'https://rpc.mainnet.msgchain.org',
    restUrl: 'https://rest.mainnet.msgchain.org',
    explorerUrl: 'https://explorer.msgchain.org',
    gasPrice: '1000000000attoMSG',
    gasLimit: 5000000,
    denom: DENOM,
    bech32Config: {
      bech32PrefixAccAddr: 'msg',
      bech32PrefixAccPub: 'msgpub',
      bech32PrefixValAddr: 'msgvaloper',
      bech32PrefixValPub: 'msgvaloperpub',
      bech32PrefixConsAddr: 'msgvalcons',
      bech32PrefixConsPub: 'msgvalconspub',
    },
  },
};

export const DEFAULT_CONFIG: MsgChainConfig = {
  projectName: 'msg-chain-project',
  networks: DEFAULT_NETWORKS,
  defaultNetwork: 'local',
  contractsPath: 'contracts/cosmwasm/all',
  artifactsDir: 'artifacts',
  compiler: {
    optimize: true,
    optimizeLevel: 'z',
    target: 'wasm32-unknown-unknown',
  },
  deploy: {
    gasPrice: '1000000000attoMSG',
    gasLimit: 3000000,
    signer: '',
  },
};

4.7 配置加载器 (src/config/config-loader.ts)

import fs from 'fs-extra';
import path from 'path';
import chalk from 'chalk';
import { MsgChainConfig } from '../types/index.js';
import { DEFAULT_CONFIG } from './defaults.js';

const CONFIG_FILES = [
  'msgchain.config.ts',
  'msgchain.config.json',
  'msgchain.config.js',
];

export async function loadConfig(cwd?: string): Promise<MsgChainConfig> {
  const dir = cwd || process.cwd();

  for (const filename of CONFIG_FILES) {
    const configPath = path.join(dir, filename);
    if (await fs.pathExists(configPath)) {
      try {
        if (filename.endsWith('.ts')) {
          const config = await import('file://' + path.resolve(configPath).replace(/\\/g, '/'));
          return mergeWithDefaults(config.default || config);
        }
        if (filename.endsWith('.json')) {
          const config = await fs.readJson(configPath);
          return mergeWithDefaults(config);
        }
        if (filename.endsWith('.js')) {
          const config = await import('file://' + path.resolve(configPath).replace(/\\/g, '/'));
          return mergeWithDefaults(config.default || config);
        }
      } catch (err) {
        console.warn(chalk.yellow('无法加载 ' + filename + ': ' + (err as Error).message));
      }
    }
  }

  console.log(chalk.gray('未找到配置文件,使用默认配置'));
  return DEFAULT_CONFIG;
}

function mergeWithDefaults(config: Partial<MsgChainConfig>): MsgChainConfig {
  return {
    ...DEFAULT_CONFIG,
    ...config,
    networks: {
      ...DEFAULT_CONFIG.networks,
      ...(config.networks || {}),
    },
    compiler: {
      ...DEFAULT_CONFIG.compiler,
      ...(config.compiler || {}),
    },
    deploy: {
      ...DEFAULT_CONFIG.deploy,
      ...(config.deploy || {}),
    },
  };
}

export async function saveConfig(config: MsgChainConfig, cwd?: string): Promise<void> {
  const dir = cwd || process.cwd();
  const configPath = path.join(dir, 'msgchain.config.json');
  const { networks, ...rest } = config;
  await fs.writeJson(configPath, { ...rest, networks }, { spaces: 2 });
  console.log(chalk.green('配置已保存至 ' + configPath));
}

export async function writeDefaultConfig(cwd: string): Promise<void> {
  const configPath = path.join(cwd, 'msgchain.config.json');
  if (await fs.pathExists(configPath)) {
    console.log(chalk.yellow(configPath + ' 已存在,跳过'));
    return;
  }
  const config = {
    $schema: 'https://raw.githubusercontent.com/msg-chain/devkit/main/schemas/config.schema.json',
    ...DEFAULT_CONFIG,
  };
  await fs.writeJson(configPath, config, { spaces: 2 });
  console.log(chalk.green('默认配置已创建: ' + configPath));
}

4.8 编译引擎 (src/core/compile-engine.ts)

import path from 'path';
import fs from 'fs-extra';
import { execa } from 'execa';
import chalk from 'chalk';
import ora from 'ora';
import { glob } from 'glob';
import { ContractPackage, CompileResult } from '../types/index.js';
import { loadConfig } from '../config/config-loader.js';

export async function findContractPackages(contractsPath: string): Promise<ContractPackage[]> {
  const dirs = await fs.readdir(contractsPath);
  const packages: ContractPackage[] = [];

  for (const dir of dirs) {
    const cargoPath = path.join(contractsPath, dir, 'Cargo.toml');
    if (await fs.pathExists(cargoPath)) {
      packages.push({
        name: dir,
        path: path.join(contractsPath, dir),
        cargoPath,
      });
    }
  }

  return packages;
}

export interface CompileOptions {
  contract?: string;
  optimize?: boolean;
  optimizeLevel?: string;
  outDir?: string;
  force?: boolean;
}

export async function compileContract(
  pkg: ContractPackage,
  options: CompileOptions
): Promise<CompileResult> {
  const startTime = Date.now();

  try {
    const contractDir = pkg.path;

    // 执行实际的编译命令
    const buildArgs = [
      'build',
      '--target', 'wasm32-unknown-unknown',
      '--release',
    ];

    if (options.force) {
      buildArgs.push('--force');
    }

    await execa('cargo', buildArgs, {
      cwd: contractDir,
      stdio: 'pipe',
    });

    // 定位 WASM 输出
    const wasmOutputDir = path.join(contractDir, 'target', 'wasm32-unknown-unknown', 'release');
    const wasmFiles = await glob('*.wasm', { cwd: wasmOutputDir });

    if (wasmFiles.length === 0) {
      throw new Error('未找到 WASM 输出文件在 ' + wasmOutputDir);
    }

    const wasmFile = wasmFiles[0];
    const wasmPath = path.join(wasmOutputDir, wasmFile);
    const wasmSize = (await fs.stat(wasmPath)).size;

    const outDir = options.outDir || './artifacts';
    await fs.ensureDir(outDir);

    const outputPath = path.join(outDir, pkg.name + '.wasm');
    await fs.copy(wasmPath, outputPath);

    let optimizedSize: number | undefined;

    // wasm-opt 优化
    if (options.optimize !== false) {
      try {
        const level = options.optimizeLevel || 'z';
        await execa('wasm-opt', [
          '-O' + level,
          outputPath,
          '-o', outputPath,
        ]);
        optimizedSize = (await fs.stat(outputPath)).size;
      } catch (optErr) {
        console.warn(chalk.yellow('wasm-opt 优化失败(可忽略): ' + (optErr as Error).message));
      }
    }

    const duration = (Date.now() - startTime) / 1000;

    return {
      contractName: pkg.name,
      success: true,
      duration,
      wasmSize,
      optimizedSize,
    };
  } catch (err) {
    return {
      contractName: pkg.name,
      success: false,
      duration: (Date.now() - startTime) / 1000,
      error: (err as Error).message,
    };
  }
}

export async function compileAllContracts(options: CompileOptions): Promise<CompileResult[]> {
  const config = await loadConfig();
  const contractsPath = path.resolve(config.contractsPath);
  let packages = await findContractPackages(contractsPath);

  if (options.contract) {
    packages = packages.filter((p) => p.name === options.contract);
    if (packages.length === 0) {
      throw new Error('未找到合约: ' + options.contract);
    }
  }

  console.log(chalk.cyan('\n发现 ' + packages.length + ' 个合约包:\n'));

  const results: CompileResult[] = [];

  for (const pkg of packages) {
    const spinner = ora('编译 ' + pkg.name + '...').start();
    const result = await compileContract(pkg, options);
    spinner.stop();

    if (result.success) {
      const sizeInfo = result.optimizedSize
        ? formatBytes(result.optimizedSize) + ' (优化后)'
        : result.wasmSize ? formatBytes(result.wasmSize) : '';
      console.log(chalk.green('  ✓ ' + pkg.name + ' — ' + sizeInfo + ' [' + result.duration.toFixed(1) + 's]'));
    } else {
      console.log(chalk.red('  ✗ ' + pkg.name + ' — ' + result.error));
    }

    results.push(result);
  }

  // 生成校验和文件
  const outDir = path.resolve(options.outDir || './artifacts');
  await generateChecksums(outDir);

  const successCount = results.filter((r) => r.success).length;
  console.log(chalk.cyan('\n✓ ' + successCount + '/' + results.length + ' 合约编译成功\n'));

  return results;
}

async function generateChecksums(outDir: string): Promise<void> {
  const wasmFiles = await glob('*.wasm', { cwd: outDir });
  if (wasmFiles.length === 0) return;

  const crypto = await import('crypto');
  const lines: string[] = [];

  for (const file of wasmFiles) {
    const content = await fs.readFile(path.join(outDir, file));
    const hash = crypto.createHash('sha256').update(content).digest('hex');
    lines.push(hash + '  ' + file);
  }

  await fs.writeFile(path.join(outDir, 'checksums.txt'), lines.join('\n'));
}

function formatBytes(bytes: number): string {
  if (bytes < 1024) return bytes + ' B';
  if (bytes < 1024 * 1024) return (bytes / 1024).toFixed(1) + ' KB';
  return (bytes / (1024 * 1024)).toFixed(1) + ' MB';
}

4.9 测试运行器 (src/core/test-runner.ts)

import { execa } from 'execa';
import chalk from 'chalk';
import ora from 'ora';
import { TestResult } from '../types/index.js';
import { loadConfig } from '../config/config-loader.js';
import { findContractPackages } from './compile-engine.js';

export interface TestOptions {
  contract?: string;
  testName?: string;
  integration?: boolean;
  coverage?: boolean;
  verbose?: boolean;
  noRust?: boolean;
}

export async function runContractTests(
  pkg: { name: string; path: string },
  options: TestOptions
): Promise<TestResult> {
  const startTime = Date.now();

  try {
    const cargoArgs = ['test'];

    if (options.testName) {
      cargoArgs.push(options.testName);
    }

    if (options.verbose) {
      cargoArgs.push('--', '--nocapture');
    }

    const { stdout } = await execa('cargo', cargoArgs, {
      cwd: pkg.path,
      stdio: 'pipe',
    });

    const duration = (Date.now() - startTime) / 1000;

    const passedMatch = stdout.match(/(\d+) passed/);
    const failedMatch = stdout.match(/(\d+) failed/);

    const testNames = extractTestNames(stdout);
    const tests = testNames.map((name) => ({
      name,
      status: (stdout.includes(name + ' FAILED') ? 'failed' : 'passed') as 'passed' | 'failed',
      duration: 0,
    }));

    return {
      contractName: pkg.name,
      passed: passedMatch ? parseInt(passedMatch[1]) : (failedMatch ? 0 : 1),
      failed: failedMatch ? parseInt(failedMatch[1]) : 0,
      duration,
      tests,
    };
  } catch (err) {
    return {
      contractName: pkg.name,
      passed: 0,
      failed: 1,
      duration: (Date.now() - startTime) / 1000,
      tests: [],
    };
  }
}

function extractTestNames(output: string): string[] {
  const testPattern = /test\s+(\S+)\s+\.\.\.\s+(ok|FAILED)/g;
  const names: string[] = [];
  let match;
  while ((match = testPattern.exec(output)) !== null) {
    names.push(match[1]);
  }
  return names;
}

export async function runAllTests(options: TestOptions): Promise<TestResult[]> {
  const config = await loadConfig();
  const contractsPath = path.resolve(config.contractsPath);
  let packages = await findContractPackages(contractsPath);

  if (options.contract) {
    packages = packages.filter((p) => p.name === options.contract);
  }

  if (!options.noRust) {
    console.log(chalk.cyan('\n发现 ' + packages.length + ' 个合约包需要测试\n'));

    const results: TestResult[] = [];
    let totalPassed = 0;
    let totalFailed = 0;

    for (const pkg of packages) {
      const spinner = ora('测试 ' + pkg.name + '...').start();
      const result = await runContractTests(pkg, options);
      spinner.stop();

      if (result.failed === 0) {
        console.log(chalk.green('  ✓ ' + pkg.name + ' — ' + result.passed + ' passed [' + result.duration.toFixed(2) + 's]'));
      } else {
        console.log(chalk.red('  ✗ ' + pkg.name + ' — ' + result.passed + ' passed, ' + result.failed + ' failed [' + result.duration.toFixed(2) + 's]'));
      }

      totalPassed += result.passed;
      totalFailed += result.failed;
      results.push(result);
    }

    const statusSymbol = totalFailed === 0 ? chalk.green('✓') : chalk.red('✗');
    console.log(chalk.cyan('\n' + statusSymbol + ' 总计: ' + totalPassed + ' passed, ' + totalFailed + ' failed\n'));
  }

  if (options.integration) {
    console.log(chalk.cyan('运行集成测试 (jest)...\n'));
    try {
      await execa('npx', ['jest', 'test/integration/'], {
        cwd: process.cwd(),
        stdio: 'inherit',
      });
    } catch {
      console.log(chalk.yellow('  集成测试未配置或失败'));
    }
  }

  return [];
}

4.10 部署引擎 (src/core/deploy-engine.ts)

import path from 'path';
import fs from 'fs-extra';
import chalk from 'chalk';
import { execa } from 'execa';
import { loadConfig } from '../config/config-loader.js';
import { NetworkConfig, DeployResult, NetworkOperationResult } from '../types/index.js';
import { findContractPackages } from './compile-engine.js';

export interface DeployOptions {
  network?: string;
  contract?: string;
  signer?: string;
  gas?: string;
  gasLimit?: string;
  label?: string;
  admin?: string;
  noAdmin?: boolean;
  dryRun?: boolean;
  guard?: boolean;
  outDir?: string;
}

export async function storeCode(
  wasmPath: string,
  network: NetworkConfig,
  signer: string,
  gasPrice: string,
  gasLimit: string
): Promise<NetworkOperationResult> {
  if (!await fs.pathExists(wasmPath)) {
    return { success: false, error: 'WASM 文件不存在: ' + wasmPath };
  }

  // 封装实际命令: msgd tx wasm store
  const args = [
    'tx', 'wasm', 'store', wasmPath,
    '--from', signer,
    '--gas', 'auto',
    '--gas-adjustment', '1.3',
    '--gas-prices', gasPrice,
    '--chain-id', network.chainId,
    '--node', network.rpcUrl,
    '-y', '-o', 'json',
  ];

  if (gasLimit !== 'auto') {
    args.splice(4, 0, '--gas', gasLimit);
  }

  if (process.env.MSG_LOG_LEVEL === 'debug') {
    console.log(chalk.gray('  $ msgd ' + args.join(' ')));
  }

  // 如果在 dry-run 模式,模拟返回
  try {
    const { stdout } = await execa('msgd', args, { timeout: 60000 });
    const codeIdMatch = stdout.match(/code_id["\s:]+(\d+)/i);
    const txHashMatch = stdout.match(/txhash["\s:]+"([^"]+)"/i);

    return {
      success: true,
      codeId: codeIdMatch ? parseInt(codeIdMatch[1]) : undefined,
      txHash: txHashMatch ? txHashMatch[1] : undefined,
    };
  } catch (err) {
    return {
      success: false,
      error: (err as Error).message,
    };
  }
}

export async function instantiateContract(
  codeId: number,
  initMsg: Record<string, unknown>,
  label: string,
  network: NetworkConfig,
  signer: string,
  gasPrice: string,
  admin?: string
): Promise<NetworkOperationResult> {
  const initMsgStr = JSON.stringify(initMsg);

  // 封装实际命令: msgd tx wasm instantiate
  const args = [
    'tx', 'wasm', 'instantiate', String(codeId), initMsgStr,
    '--label', label,
    '--from', signer,
    '--gas', 'auto',
    '--gas-adjustment', '1.3',
    '--gas-prices', gasPrice,
    '--chain-id', network.chainId,
    '--node', network.rpcUrl,
    '-y', '-o', 'json',
  ];

  if (admin) {
    args.push('--admin', admin);
  }

  try {
    const { stdout } = await execa('msgd', args, { timeout: 60000 });
    const contractAddrMatch = stdout.match(/contract_address["\s:]+"([^"]+)"/i);
    const txHashMatch = stdout.match(/txhash["\s:]+"([^"]+)"/i);

    return {
      success: true,
      contractAddress: contractAddrMatch ? contractAddrMatch[1] : undefined,
      txHash: txHashMatch ? txHashMatch[1] : undefined,
    };
  } catch (err) {
    return {
      success: false,
      error: (err as Error).message,
    };
  }
}

export async function deployContract(
  wasmPath: string,
  contractName: string,
  initMsg: Record<string, unknown>,
  options: DeployOptions,
  config: any
): Promise<DeployResult> {
  const networkName = options.network || config.defaultNetwork || 'local';
  const network = config.networks[networkName];
  if (!network) throw new Error('未找到网络配置: ' + networkName);

  const signer = options.signer || config.deploy.signer || 'validator';
  const gasPrice = options.gas || config.deploy.gasPrice || '1000000000attoMSG';
  const label = options.label || contractName + '-' + Date.now();
  const admin = options.noAdmin ? undefined : (options.admin || undefined);

  const deployResult: DeployResult = {
    network: networkName,
    chainId: network.chainId,
    timestamp: new Date().toISOString(),
    contracts: {},
    gasUsed: '0',
  };

  // 步骤 1: Store Code
  console.log(chalk.cyan('\n步骤 1/2: Store Code — ' + contractName));
  console.log(chalk.gray('  读取 ' + wasmPath));

  const wasmHash = await computeWasmHash(wasmPath);

  const storeResult = await storeCode(wasmPath, network, signer, gasPrice, options.gasLimit || '3000000');

  if (!storeResult.success) {
    throw new Error('Store Code 失败: ' + storeResult.error);
  }

  console.log(chalk.green('  ✓ Code ID: ' + storeResult.codeId + ' (tx: ' + storeResult.txHash + ')'));
  console.log(chalk.gray('  ⏱ 已确认 (1 block)'));

  // 步骤 2: Instantiate
  console.log(chalk.cyan('\n步骤 2/2: Instantiate Contract'));
  console.log(chalk.gray('  消息: ' + JSON.stringify(initMsg)));

  const instantiateResult = await instantiateContract(
    storeResult.codeId!,
    initMsg,
    label,
    network,
    signer,
    gasPrice,
    admin
  );

  if (!instantiateResult.success) {
    throw new Error('Instantiate 失败: ' + instantiateResult.error);
  }

  console.log(chalk.green('  ✓ 合约地址: ' + instantiateResult.contractAddress));

  deployResult.contracts[contractName] = {
    codeId: storeResult.codeId!,
    address: instantiateResult.contractAddress!,
    txHash: instantiateResult.txHash || storeResult.txHash || '',
    label,
    admin: admin || '',
    wasmHash,
    instantiateMsg: initMsg,
  };

  deployResult.gasUsed = instantiateResult.gasUsed || storeResult.gasUsed || '0';

  // 保存到 deployment 记录文件
  const deployDir = path.resolve('deployments');
  await fs.ensureDir(deployDir);
  const deployFile = path.join(deployDir, networkName + '.json');

  let existing: Record<string, any> = {};
  if (await fs.pathExists(deployFile)) {
    existing = await fs.readJson(deployFile);
  }

  existing[contractName] = deployResult.contracts[contractName];
  existing._meta = {
    network: networkName,
    chainId: network.chainId,
    lastUpdated: deployResult.timestamp,
  };

  await fs.writeJson(deployFile, existing, { spaces: 2 });

  return deployResult;
}

async function computeWasmHash(wasmPath: string): Promise<string> {
  const crypto = await import('crypto');
  const content = await fs.readFile(wasmPath);
  return crypto.createHash('sha256').update(content).digest('hex');
}

export async function deployAllContracts(options: DeployOptions): Promise<DeployResult[]> {
  const config = await loadConfig();
  const wasmDir = path.resolve(options.outDir || './artifacts');
  const contractsDir = path.resolve(config.contractsPath);

  const packages = await findContractPackages(contractsDir);
  const filtered = options.contract
    ? packages.filter((p) => p.name === options.contract)
    : packages;

  const results: DeployResult[] = [];

  for (const pkg of filtered) {
    const wasmFile = path.join(wasmDir, pkg.name + '.wasm');

    if (!await fs.pathExists(wasmFile)) {
      console.log(chalk.yellow('未找到 ' + pkg.name + '.wasm,请先执行 compile'));
      continue;
    }

    const initMsg = await loadInitMsg(pkg.name);
    const result = await deployContract(wasmFile, pkg.name, initMsg, options, config);
    results.push(result);
  }

  return results;
}

async function loadInitMsg(contractName: string): Promise<Record<string, unknown>> {
  const initMsgPath = path.resolve('init-msgs', contractName + '.json');
  if (await fs.pathExists(initMsgPath)) {
    return fs.readJson(initMsgPath);
  }
  return {};
}

4.11 Guard 审批门引擎 (src/core/guard.ts)

import inquirer from 'inquirer';
import chalk from 'chalk';
import fs from 'fs-extra';
import path from 'path';
import { ApprovalGate, ApprovalEvidence } from '../types/index.js';

const APPROVAL_GATES: ApprovalGate[] = [
  {
    id: 'scope_lock',
    stage: 'before_codegen',
    requiredHumanInputs: ['产品目标', '业务规则', '验收标准'],
  },
  {
    id: 'secret_injection',
    stage: 'before_real_write_or_deploy',
    requiredHumanInputs: ['签名账户', 'API token', '环境变量'],
  },
  {
    id: 'production_release',
    stage: 'before_launch',
    requiredHumanInputs: ['最终审批', '回滚确认'],
  },
  {
    id: 'governance_or_treasury',
    stage: 'high_risk_write',
    requiredHumanInputs: ['DAO通过', 'timelock结束'],
  },
];

export interface GuardOptions {
  skipGates?: string;
  dryRun?: boolean;
}

export class GuardEngine {
  private evidenceDir: string;
  private evidence: ApprovalEvidence[] = [];

  constructor(tag?: string) {
    const dir = tag
      ? path.resolve('.approval-evidence', tag)
      : path.resolve('.approval-evidence', 'deploy-' + Date.now());
    this.evidenceDir = dir;
  }

  async executeAllGates(options: GuardOptions = {}): Promise<boolean> {
    console.log(chalk.cyan('\n🛡️  Guard 审批门引擎'));
    console.log(chalk.gray('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'));
    console.log(chalk.yellow('⚠️  生产环境部署 — 审批门已启用\n'));

    const skipIds = options.skipGates
      ? options.skipGates.split(',').map((s) => s.trim())
      : [];

    let allPassed = true;

    for (let i = 0; i < APPROVAL_GATES.length; i++) {
      const gate = APPROVAL_GATES[i];

      if (skipIds.includes(gate.id)) {
        console.log(chalk.gray('  跳过审批门 ' + (i + 1) + '/' + APPROVAL_GATES.length + ': ' + gate.id));
        continue;
      }

      const passed = await this.executeGate(gate, i + 1);

      if (!passed) {
        allPassed = false;
        console.log(chalk.red('\n  审批门 ' + gate.id + ' 未通过,流程终止'));
        break;
      }
    }

    if (allPassed) {
      console.log(chalk.green('\n✅ 全部审批门已通过'));
      await this.saveEvidence();
    }

    return allPassed;
  }

  private async executeGate(gate: ApprovalGate, index: number): Promise<boolean> {
    console.log(chalk.cyan('🔒 审批门 ' + index + '/' + APPROVAL_GATES.length + ': ' + gate.id + ' (' + gate.stage + ')'));
    console.log(chalk.gray('   需要审批: ' + gate.requiredHumanInputs.join(', ')));

    const responses: Record<string, string> = {};

    for (const input of gate.requiredHumanInputs) {
      const { value } = await inquirer.prompt([
        {
          type: 'input',
          name: 'value',
          message: '请输入 ' + chalk.yellow(input) + ':',
          validate: (v: string) => v.length > 0 || input + ' 不能为空',
        },
      ]);

      responses[input] = value;
    }

    this.evidence.push({
      gate,
      responses,
      timestamp: new Date().toISOString(),
      approvedBy: process.env.USER || 'unknown',
    });

    console.log(chalk.green('   ✓ 已记录\n'));
    return true;
  }

  private async saveEvidence(): Promise<void> {
    await fs.ensureDir(this.evidenceDir);

    for (const item of this.evidence) {
      const content = [
        '# 审批门证据: ' + item.gate.id,
        '- 阶段: ' + item.gate.stage,
        '- 审批时间: ' + item.timestamp,
        '- 审批人: ' + item.approvedBy,
        '',
        '## 审批输入',
        ...Object.entries(item.responses).map(
          ([key, value]) => '- **' + key + '**: ' + value
        ),
      ].join('\n');

      const filePath = path.join(this.evidenceDir, item.gate.id + '.md');
      await fs.writeFile(filePath, content);
    }

    console.log(chalk.gray('\n📝 证据存档: ' + this.evidenceDir + '/'));
  }

  async createEvidenceSummary(deploymentResult: Record<string, unknown>): Promise<void> {
    const summary = {
      evidence: this.evidence,
      deployment: deploymentResult,
      timestamp: new Date().toISOString(),
    };

    await fs.writeJson(
      path.join(this.evidenceDir, 'deployment_receipt.json'),
      summary,
      { spaces: 2 }
    );
  }

  static assertGuardFlag(guard: boolean | undefined): void {
    if (!guard) {
      console.error(chalk.red([
        '',
        '╔══════════════════════════════════════════════════════════════╗',
        '║  🛡️  GUARD MODE REQUIRED                                   ║',
        '║                                                              ║',
        '║  生产环境部署必须使用 --guard 标志以激活审批门检查。        ║',
        '║                                                              ║',
        '║  正确的用法:                                                 ║',
        '║  $ msg-devkit publish --guard --network mainnet              ║',
        '║  $ msg-devkit deploy --guard --network mainnet               ║',
        '║                                                              ║',
        '║  requires_human_approval: true                               ║',
        '╚══════════════════════════════════════════════════════════════╝',
        '',
      ].join('\n')));
      process.exit(1);
    }
  }
}

4.12 脚手架生成器 (src/core/scaffold.ts)

import path from 'path';
import fs from 'fs-extra';
import chalk from 'chalk';
import ora from 'ora';
import { writeDefaultConfig } from '../config/config-loader.js';
import { generateCiWorkflow } from './ci-generator.js';
import { ScaffoldOptions } from '../types/index.js';

export async function scaffoldProject(options: ScaffoldOptions): Promise<void> {
  const projectDir = path.resolve(options.projectName);
  const spinner = ora('创建项目 ' + options.projectName + '...').start();

  try {
    await fs.ensureDir(projectDir);

    switch (options.template) {
      case 'contract':
        await scaffoldContractTemplate(projectDir, options);
        break;
      case 'dapp':
        await scaffoldDappTemplate(projectDir, options);
        break;
      case 'fullstack':
        await scaffoldContractTemplate(projectDir, options);
        await scaffoldDappTemplate(path.join(projectDir, 'frontend'), options);
        break;
    }

    await writeDefaultConfig(projectDir);
    await generatePackageJson(projectDir, options);
    await generateGitignore(projectDir);

    if (options.ci) {
      await generateCiWorkflow(projectDir, options.ci);
    }

    spinner.stop();
    console.log(chalk.green('\n✅ 项目 ' + options.projectName + ' 创建成功!\n'));
    printNextSteps(projectDir, options);
  } catch (err) {
    spinner.stop();
    throw err;
  }
}

async function scaffoldContractTemplate(dir: string, options: ScaffoldOptions): Promise<void> {
  const contractName = options.contractName || 'my_contract';
  const contractDir = path.join(dir, 'contracts', 'cosmwasm', 'all', contractName);
  const srcDir = path.join(contractDir, 'src');
  const testsDir = path.join(contractDir, 'tests');

  await fs.ensureDir(srcDir);
  await fs.ensureDir(testsDir);
  await fs.ensureDir(path.join(dir, 'scripts'));
  await fs.ensureDir(path.join(dir, 'test', 'integration'));
  await fs.ensureDir(path.join(dir, 'init-msgs'));
  await fs.ensureDir(path.join(dir, 'artifacts'));

  await fs.writeFile(path.join(contractDir, 'Cargo.toml'), [
    '[package]',
    'name = "' + contractName + '"',
    'version = "1.0.0"',
    'edition = "2021"',
    'description = "MSG Chain CosmWasm Contract"',
    '',
    '[lib]',
    'crate-type = ["cdylib", "rlib"]',
    '',
    '[features]',
    'default = []',
    'library = []',
    '',
    '[dependencies]',
    'cosmwasm-std = { version = "2.1", features = ["staking"] }',
    'cosmwasm-schema = "2.1"',
    'cw-storage-plus = "2.0"',
    'cw-utils = "2.0"',
    'cw2 = "2.0"',
    'schemars = "0.8"',
    'serde = { version = "1.0", features = ["derive"] }',
    'serde-json-wasm = "1.0"',
    'thiserror = "2.0"',
    '',
    '[dev-dependencies]',
    'cw-multi-test = "2.0"',
    'anyhow = "1.0"',
    '',
    '[profile.release]',
    'opt-level = 3',
    'debug = false',
    'rpath = false',
    'lto = true',
    'overflow-checks = true',
    'codegen-units = 1',
  ].join('\n'));

  await fs.writeFile(path.join(srcDir, 'contract.rs'), [
    'use cosmwasm_std::{',
    '    entry_point, to_json_binary, Binary, Deps, DepsMut, Env,',
    '    MessageInfo, Response, StdResult,',
    '};',
    'use crate::msg::{InstantiateMsg, ExecuteMsg, QueryMsg};',
    'use crate::state::{Config, CONFIG};',
    'use crate::error::ContractError;',
    '',
    '#[entry_point]',
    'pub fn instantiate(',
    '    deps: DepsMut,',
    '    _env: Env,',
    '    info: MessageInfo,',
    '    msg: InstantiateMsg,',
    ') -> StdResult<Response> {',
    '    let config = Config {',
    '        owner: info.sender,',
    '        key: msg.key,',
    '    };',
    '    CONFIG.save(deps.storage, &config)?;',
    '    Ok(Response::new()',
    '        .add_attribute("method", "instantiate")',
    '        .add_attribute("owner", info.sender))',
    '}',
    '',
    '#[entry_point]',
    'pub fn execute(',
    '    deps: DepsMut,',
    '    env: Env,',
    '    info: MessageInfo,',
    '    msg: ExecuteMsg,',
    ') -> Result<Response, ContractError> {',
    '    match msg {',
    '        ExecuteMsg::UpdateConfig { key } => execute_update_config(deps, env, info, key),',
    '    }',
    '}',
    '',
    'fn execute_update_config(',
    '    deps: DepsMut,',
    '    _env: Env,',
    '    info: MessageInfo,',
    '    key: String,',
    ') -> Result<Response, ContractError> {',
    '    let mut config = CONFIG.load(deps.storage)?;',
    '    if info.sender != config.owner {',
    '        return Err(ContractError::Unauthorized {});',
    '    }',
    '    config.key = key;',
    '    CONFIG.save(deps.storage, &config)?;',
    '    Ok(Response::new().add_attribute("method", "update_config"))',
    '}',
    '',
    '#[entry_point]',
    'pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> StdResult<Binary> {',
    '    match msg {',
    '        QueryMsg::GetConfig {} => to_json_binary(&query_config(deps)?),',
    '    }',
    '}',
    '',
    'fn query_config(deps: Deps) -> StdResult<Config> {',
    '    CONFIG.load(deps.storage)',
    '}',
  ].join('\n'));

  await fs.writeFile(path.join(srcDir, 'msg.rs'), [
    'use cosmwasm_schema::cw_serde;',
    '',
    '#[cw_serde]',
    'pub struct InstantiateMsg {',
    '    pub key: String,',
    '}',
    '',
    '#[cw_serde]',
    'pub enum ExecuteMsg {',
    '    UpdateConfig { key: String },',
    '}',
    '',
    '#[cw_serde]',
    'pub enum QueryMsg {',
    '    GetConfig {},',
    '}',
  ].join('\n'));

  await fs.writeFile(path.join(srcDir, 'state.rs'), [
    'use cosmwasm_schema::cw_serde;',
    'use cosmwasm_std::Addr;',
    'use cw_storage_plus::Item;',
    '',
    '#[cw_serde]',
    'pub struct Config {',
    '    pub owner: Addr,',
    '    pub key: String,',
    '}',
    '',
    'pub const CONFIG: Item<Config> = Item::new("config");',
  ].join('\n'));

  await fs.writeFile(path.join(srcDir, 'error.rs'), [
    'use cosmwasm_std::StdError;',
    'use thiserror::Error;',
    '',
    '#[derive(Error, Debug, PartialEq)]',
    'pub enum ContractError {',
    '    #[error("{0}")]',
    '    Std(#[from] StdError),',
    '',
    '    #[error("Unauthorized")]',
    '    Unauthorized {},',
    '',
    '    #[error("Invalid input: {reason}")]',
    '    InvalidInput { reason: String },',
    '}',
  ].join('\n'));

  await fs.writeFile(path.join(srcDir, 'lib.rs'), [
    'pub mod contract;',
    'mod error;',
    'pub mod msg;',
    'pub mod state;',
    '',
    'pub use crate::error::ContractError;',
  ].join('\n'));

  await fs.writeFile(path.join(srcDir, 'schema.rs'), [
    'use cosmwasm_schema::write_api;',
    'use crate::msg::{ExecuteMsg, InstantiateMsg, QueryMsg};',
    '',
    'fn main() {',
    '    write_api! {',
    '        instantiate: InstantiateMsg,',
    '        execute: ExecuteMsg,',
    '        query: QueryMsg,',
    '    }',
    '}',
  ].join('\n'));

  await fs.writeFile(path.join(testsDir, 'integration.rs'), [
    'use cosmwasm_std::Addr;',
    'use cw_multi_test::{App, ContractWrapper, Executor};',
    'use ' + contractName + '::msg::{ExecuteMsg, InstantiateMsg, QueryMsg};',
    'use ' + contractName + '::state::Config;',
    '',
    '#[test]',
    'fn test_full_flow() {',
    '    let mut app = App::default();',
    '    let code_id = app.store_code(Box::new(ContractWrapper::new(',
    '        ' + contractName + '::contract::execute,',
    '        ' + contractName + '::contract::instantiate,',
    '        ' + contractName + '::contract::query,',
    '    )));',
    '',
    '    let owner = Addr::unchecked("owner");',
    '    let contract_addr = app',
    '        .instantiate_contract(',
    '            code_id,',
    '            owner.clone(),',
    '            &InstantiateMsg {',
    '                key: "initial".to_string(),',
    '            },',
    '            &[],',
    '            "test-contract",',
    '            None,',
    '        )',
    '        .unwrap();',
    '',
    '    let config: Config = app',
    '        .wrap()',
    '        .query_wasm_smart(contract_addr.clone(), &QueryMsg::GetConfig {})',
    '        .unwrap();',
    '    assert_eq!(config.key, "initial");',
    '}',
  ].join('\n'));

  await fs.writeJson(path.join(dir, 'init-msgs', contractName + '.json'), { key: "value" }, { spaces: 2 });
}

async function scaffoldDappTemplate(dir: string, options: ScaffoldOptions): Promise<void> {
  const framework = options.framework || 'react';
  await fs.ensureDir(path.join(dir, 'src', 'chain'));
  await fs.ensureDir(path.join(dir, 'src', 'components'));
  await fs.ensureDir(path.join(dir, 'src', 'config'));

  await fs.writeFile(path.join(dir, 'src', 'chain', 'client.ts'), [
    'import { SigningCosmWasmClient } from "@cosmjs/cosmwasm-stargate";',
    'import { OfflineSigner } from "@cosmjs/proto-signing";',
    '',
    'declare global {',
    '  interface Window {',
    '    keplr?: any;',
    '    getOfflineSigner?: (chainId: string) => OfflineSigner;',
    '  }',
    '}',
    '',
    'const MSG_CHAIN_CONFIG = {',
    '  chainId: "msg-chain-1",',
    '  chainName: "MSG Chain",',
    '  rpc: process.env.NEXT_PUBLIC_RPC_URL || "http://localhost:26657",',
    '  rest: process.env.NEXT_PUBLIC_REST_URL || "http://localhost:1317",',
    '  bip44: { coinType: 118 },',
    '  bech32Config: {',
    '    bech32PrefixAccAddr: "msg",',
    '    bech32PrefixAccPub: "msgpub",',
    '    bech32PrefixValAddr: "msgvaloper",',
    '    bech32PrefixValPub: "msgvaloperpub",',
    '    bech32PrefixConsAddr: "msgvalcons",',
    '    bech32PrefixConsPub: "msgvalconspub",',
    '  },',
    '  currencies: [{',
    '    coinDenom: "MSG",',
    '    coinMinimalDenom: "umsg",',
    '    coinDecimals: 18,',
    '    coinGeckoId: "msg-chain",',
    '  }],',
    '  feeCurrencies: [{',
    '    coinDenom: "MSG",',
    '    coinMinimalDenom: "umsg",',
    '    coinDecimals: 18,',
    '    gasPriceStep: { low: 1000000000attoMSG, average: 1000000000attoMSG, high: 1000000000attoMSG },',
    '  }],',
    '  stakeCurrency: {',
    '    coinDenom: "MSG",',
    '    coinMinimalDenom: "umsg",',
    '    coinDecimals: 18,',
    '  },',
    '};',
    '',
    'export async function connectKeplr() {',
    '  if (!window.keplr) {',
    '    throw new Error("请安装 Keplr 钱包: https://www.keplr.app/");',
    '  }',
    '  await window.keplr.experimentalSuggestChain(MSG_CHAIN_CONFIG);',
    '  await window.keplr.enable(MSG_CHAIN_CONFIG.chainId);',
    '  const offlineSigner = window.getOfflineSigner!(MSG_CHAIN_CONFIG.chainId);',
    '  const [account] = await offlineSigner.getAccounts();',
    '  return { offlineSigner, address: account.address };',
    '}',
  ].join('\n'));

  await fs.writeFile(path.join(dir, 'src', 'config', 'chain.ts'), [
    'export const MSG_CHAIN = {',
    '  chainId: "msg-chain-1",',
    '  chainName: "MSG Chain",',
    '  rpcUrl: process.env.NEXT_PUBLIC_RPC_URL || "http://localhost:26657",',
    '  restUrl: process.env.NEXT_PUBLIC_REST_URL || "http://localhost:1317",',
    '  denom: "umsg",',
    '  decimals: 18,',
    '  bech32Prefix: "msg",',
    '  gasPrice: { low: "1000000000attoMSG", average: "1000000000attoMSG", high: "1000000000attoMSG" },',
    '  explorerUrl: process.env.NEXT_PUBLIC_EXPLORER_URL || "https://explorer.msgchain.org",',
    '  blockTime: 5000,',
    '  consensus: "Round-Robin + DAR",',
    '} as const;',
  ].join('\n'));
}

async function generatePackageJson(dir: string, options: ScaffoldOptions): Promise<void> {
  const pkg = {
    name: options.projectName,
    version: '1.0.0',
    private: true,
    scripts: {
      compile: 'msg-devkit compile',
      test: 'msg-devkit test',
      'test:integration': 'msg-devkit test --integration',
      deploy: 'msg-devkit deploy',
      'deploy:local': 'msg-devkit deploy --network local',
      'deploy:mainnet': 'msg-devkit deploy --network mainnet --guard',
      node: 'msg-devkit node',
      'node:start': 'msg-devkit node start',
      'node:stop': 'msg-devkit node stop',
      console: 'msg-devkit console',
      schema: 'msg-devkit schema',
      lint: 'make lint',
      'make:deps': 'make deps',
      'make:test': 'make test',
      'make:test-quantum': 'make test-quantum',
      'make:build-linux': 'make build-linux',
      'make:ci-contracts': 'make ci-contracts',
      'make:package': 'make package',
    },
    dependencies: {
      '@msg-chain/devkit': '^1.0.0',
    },
    devDependencies: {
      typescript: '^5.4.0',
      'ts-node': '^10.9.0',
      '@types/node': '^20.11.0',
    },
  };
  await fs.writeJson(path.join(dir, 'package.json'), pkg, { spaces: 2 });
}

async function generateGitignore(dir: string): Promise<void> {
  const gitignore = [
    'node_modules/',
    'dist/',
    'artifacts/',
    'target/',
    'schemas/',
    'deployments/',
    '.approval-evidence/',
    '*.wasm',
    '.env',
    '.env.*',
    '!.env.example',
    '*.log',
    'coverage/',
    '.DS_Store',
  ].join('\n');
  await fs.writeFile(path.join(dir, '.gitignore'), gitignore);
}

function printNextSteps(dir: string, options: ScaffoldOptions): void {
  console.log(chalk.cyan('\n📁 项目结构: ' + options.projectName + '/'));
  console.log(chalk.gray('├── contracts/cosmwasm/all/   — 合约源码'));
  console.log(chalk.gray('├── scripts/                  — 部署脚本'));
  console.log(chalk.gray('├── test/                     — 集成测试'));
  console.log(chalk.gray('├── msgchain.config.json      — 链配置'));
  console.log(chalk.gray('└── package.json              — 项目依赖\n'));

  console.log(chalk.bold('🚀 快速开始:\n'));
  console.log(chalk.cyan('  cd ' + options.projectName));
  console.log(chalk.cyan('  npm install'));
  console.log(chalk.cyan('  msg-devkit compile'));
  console.log(chalk.cyan('  msg-devkit test'));
  console.log(chalk.cyan('  msg-devkit node start'));
  console.log(chalk.cyan('  msg-devkit deploy --network local'));
  console.log(chalk.cyan('  msg-devkit console\n'));

  if (options.template === 'contract') {
    console.log(chalk.bold('合约相关命令:\n'));
    console.log(chalk.gray('  make deps             # 安装依赖'));
    console.log(chalk.gray('  make lint             # 代码质量检查 (gofmt, go vet, golangci-lint)'));
    console.log(chalk.gray('  make test             # 单元测试'));
    console.log(chalk.gray('  make test-quantum     # 量子模块测试'));
    console.log(chalk.gray('  make build-linux      # 构建 Linux 二进制'));
    console.log(chalk.gray('  make ci-contracts     # 合约 CI 构建'));
    console.log(chalk.gray('  make package          # 打包部署包\n'));
  }
}

4.13 CI/CD 生成器 (src/core/ci-generator.ts)

import path from 'path';
import fs from 'fs-extra';

export async function generateCiWorkflow(projectDir: string, provider: string): Promise<void> {
  switch (provider) {
    case 'github-actions':
      await generateGithubActions(projectDir);
      break;
    default:
      throw new Error('不支持的 CI 提供者: ' + provider);
  }
}

async function generateGithubActions(projectDir: string): Promise<void> {
  const workflowDir = path.join(projectDir, '.github', 'workflows');
  await fs.ensureDir(workflowDir);

  const workflow = [
    'name: MSG Chain CI/CD',
    '',
    'on:',
    '  push:',
    '    branches: [main, develop]',
    '  pull_request:',
    '    branches: [main]',
    '',
    'env:',
    '  MSG_NETWORK: test',
    '  MSG_RPC_URL: http://localhost:26657',
    '',
    'jobs:',
    '  quality_gate:',
    '    name: 代码质量检查',
    '    runs-on: ubuntu-latest',
    '    steps:',
    '      - uses: actions/checkout@v4',
    '      - name: Setup Go',
    '        uses: actions/setup-go@v5',
    '        with:',
    '          go-version: "1.24"',
    '      - name: Setup Rust',
    '        uses: actions-rust-lang/setup-rust-toolchain@v1',
    '        with:',
    '          target: wasm32-unknown-unknown',
    '      - name: Setup Node.js',
    '        uses: actions/setup-node@v4',
    '        with:',
    '          node-version: "20"',
    '      - name: 安装依赖',
    '        run: make deps',
    '      - name: 代码质量检查',
    '        run: make lint',
    '      - name: 运行单元测试',
    '        run: make test',
    '      - name: 量子模块测试',
    '        run: make test-quantum',
    '',
    '  contract_validation:',
    '    name: 合约编译与测试',
    '    runs-on: ubuntu-latest',
    '    steps:',
    '      - uses: actions/checkout@v4',
    '      - name: Setup Rust',
    '        uses: actions-rust-lang/setup-rust-toolchain@v1',
    '        with:',
    '          target: wasm32-unknown-unknown',
    '      - name: Setup Node.js',
    '        uses: actions/setup-node@v4',
    '        with:',
    '          node-version: "20"',
    '      - name: 安装 devkit',
    '        run: npm install -g @msg-chain/devkit',
    '      - name: 安装依赖',
    '        run: npm ci',
    '      - name: 编译合约',
    '        run: msg-devkit compile',
    '      - name: 运行合约测试',
    '        run: msg-devkit test',
    '      - name: 生成 Schema',
    '        run: msg-devkit schema',
    '      - name: 上传编译产物',
    '        uses: actions/upload-artifact@v4',
    '        with:',
    '          name: wasm-artifacts',
    '          path: artifacts/',
    '',
    '  build:',
    '    name: 构建节点二进制',
    '    runs-on: ubuntu-latest',
    '    needs: [quality_gate, contract_validation]',
    '    steps:',
    '      - uses: actions/checkout@v4',
    '      - name: Setup Go',
    '        uses: actions/setup-go@v5',
    '        with:',
    '          go-version: "1.24"',
    '      - name: 构建 Linux 二进制',
    '        run: make build-linux',
    '      - name: 上传构建产物',
    '        uses: actions/upload-artifact@v4',
    '        with:',
    '          name: node-binaries',
    '          path: |',
    '            bin/genesis_node_linux',
    '            bin/quantum_node_linux',
    '',
    '  package:',
    '    name: 打包发布',
    '    runs-on: ubuntu-latest',
    '    needs: [build]',
    '    if: github.ref == "refs/heads/main"',
    '    steps:',
    '      - uses: actions/checkout@v4',
    '      - name: 下载所有产物',
    '        uses: actions/download-artifact@v4',
    '      - name: 打包部署包',
    '        run: make package',
    '      - name: 上传部署包',
    '        uses: actions/upload-artifact@v4',
    '        with:',
    '          name: deploy-package',
    '          path: deploy-*.tar.gz',
    '',
    '  deploy_testnet:',
    '    name: 部署到 Testnet',
    '    runs-on: ubuntu-latest',
    '    needs: [package]',
    '    if: github.ref == "refs/heads/main"',
    '    environment: testnet',
    '    steps:',
    '      - uses: actions/checkout@v4',
    '      - name: Setup Node.js',
    '        uses: actions/setup-node@v4',
    '        with:',
    '          node-version: "20"',
    '      - name: 安装 devkit',
    '        run: npm install -g @msg-chain/devkit',
    '      - name: 安装依赖',
    '        run: npm ci',
    '      - name: 部署到 Testnet',
    '        run: msg-devkit deploy --network testnet --guard',
    '',
    '  deploy_mainnet:',
    '    name: 部署到 Mainnet',
    '    runs-on: ubuntu-latest',
    '    needs: [deploy_testnet]',
    '    if: github.ref == "refs/heads/main" && startsWith(github.ref, "refs/tags/v")',
    '    environment: mainnet',
    '    concurrency: production',
    '    steps:',
    '      - uses: actions/checkout@v4',
    '      - name: Setup Node.js',
    '        uses: actions/setup-node@v4',
    '        with:',
    '          node-version: "20"',
    '      - name: 安装 devkit',
    '        run: npm install -g @msg-chain/devkit',
    '      - name: 安装依赖',
    '        run: npm ci',
    '      - name: Guard 模式生产发布',
    '        run: msg-devkit publish --guard --network mainnet --tag ${{ github.ref_name }}',
  ].join('\n');

  await fs.writeFile(path.join(workflowDir, 'ci.yml'), workflow);
}

4.14 Schema 生成器 (src/core/schema-generator.ts)

import path from 'path';
import fs from 'fs-extra';
import { execa } from 'execa';
import { findContractPackages } from './compile-engine.js';
import { loadConfig } from '../config/config-loader.js';

export interface SchemaOptions {
  contract?: string;
  outDir?: string;
  format?: string;
}

export async function generateSchemas(options: SchemaOptions): Promise<string[]> {
  const config = await loadConfig();
  const contractsPath = path.resolve(config.contractsPath);
  let packages = await findContractPackages(contractsPath);

  if (options.contract) {
    packages = packages.filter((p) => p.name === options.contract);
  }

  const schemasDir = path.resolve(options.outDir || './schemas');
  await fs.ensureDir(schemasDir);

  const generatedFiles: string[] = [];

  for (const pkg of packages) {
    try {
      await execa('cargo', ['run', '--bin', 'schema'], {
        cwd: pkg.path,
        stdio: 'pipe',
      });

      const schemaOutputDir = path.join(pkg.path, 'schema');
      if (await fs.pathExists(schemaOutputDir)) {
        const contractSchemaDir = path.join(schemasDir, pkg.name);
        await fs.copy(schemaOutputDir, contractSchemaDir);

        const files = await fs.readdir(contractSchemaDir);
        for (const f of files) {
          generatedFiles.push(path.join(contractSchemaDir, f));
        }
      }
    } catch {
      await generateSchemaFromSource(pkg, schemasDir);
    }
  }

  return generatedFiles;
}

async function generateSchemaFromSource(
  pkg: { name: string; path: string },
  schemasDir: string
): Promise<void> {
  const msgPath = path.join(pkg.path, 'src', 'msg.rs');
  if (!await fs.pathExists(msgPath)) return;

  const contractSchemaDir = path.join(schemasDir, pkg.name);
  await fs.ensureDir(contractSchemaDir);

  const msgContent = await fs.readFile(msgPath, 'utf-8');
  const schemas: Record<string, any> = {};

  if (msgContent.includes('struct InstantiateMsg')) {
    schemas.instantiate_msg = {
      $schema: 'http://json-schema.org/draft-07/schema#',
      title: 'InstantiateMsg',
      type: 'object',
      properties: { key: { type: 'string' } },
      required: ['key'],
    };
  }

  if (msgContent.includes('enum ExecuteMsg')) {
    schemas.execute_msg = {
      $schema: 'http://json-schema.org/draft-07/schema#',
      title: 'ExecuteMsg',
      oneOf: [{
        type: 'object',
        properties: {
          update_config: {
            type: 'object',
            properties: { key: { type: 'string' } },
            required: ['key'],
          },
        },
        required: ['update_config'],
      }],
    };
  }

  if (msgContent.includes('enum QueryMsg')) {
    schemas.query_msg = {
      $schema: 'http://json-schema.org/draft-07/schema#',
      title: 'QueryMsg',
      oneOf: [{
        type: 'object',
        properties: {
          get_config: { type: 'object', properties: {} },
        },
        required: ['get_config'],
      }],
    };
  }

  for (const [name, schema] of Object.entries(schemas)) {
    await fs.writeJson(path.join(contractSchemaDir, name + '.json'), schema, { spaces: 2 });
  }
}

4.15 节点管理器 (src/core/node-manager.ts)

import { execa } from 'execa';
import chalk from 'chalk';
import fs from 'fs-extra';

const NODE_DATA_DIR = '/tmp/msg-chain/data';

export interface NodeOptions {
  clean?: boolean;
  validators?: string;
  ports?: string;
  detach?: string;
}

export async function startNode(options: NodeOptions): Promise<void> {
  if (options.clean) {
    console.log(chalk.gray('清理旧数据...'));
    await fs.remove(NODE_DATA_DIR);
    await fs.ensureDir(NODE_DATA_DIR);
    console.log(chalk.green('  ✓ 已清理'));
  }

  const validatorCount = parseInt(options.validators || '1', 10);
  const detach = options.detach !== 'false';

  console.log(chalk.cyan('启动 Docker Compose 服务...'));

  const composeConfig = generateDockerCompose(validatorCount);

  try {
    const args = ['-f', '-'];
    if (detach) {
      args.push('up', '-d');
    } else {
      args.push('up');
    }

    await execa('docker-compose', args, {
      input: composeConfig,
      stdio: detach ? 'pipe' : 'inherit',
    });

    if (detach) {
      console.log(chalk.green('  ✓ 本地节点已启动'));
      console.log(chalk.gray('等待节点同步...'));
      await waitForBlock();
      printNodeInfo(validatorCount);
    }
  } catch (err) {
    console.error(chalk.red('  启动失败: ' + (err as Error).message));
    throw err;
  }
}

export async function stopNode(): Promise<void> {
  console.log(chalk.cyan('停止本地节点...'));
  try {
    await execa('docker-compose', ['down', '-v'], { stdio: 'pipe' });
    console.log(chalk.green('  ✓ 已停止'));
  } catch (err) {
    console.error(chalk.red('  停止失败: ' + (err as Error).message));
  }
}

export async function nodeStatus(): Promise<void> {
  try {
    const { stdout } = await execa('curl', ['-s', 'http://localhost:26657/status']);
    const status = JSON.parse(stdout);
    const syncInfo = status.result.sync_info;

    console.log(chalk.cyan('\n节点: 运行中'));
    console.log(chalk.gray('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'));
    console.log('  区块高度:     ' + chalk.bold(syncInfo.latest_block_height));
    console.log('  最新区块:     ' + syncInfo.latest_block_time);
    console.log('  节点状态:     ' + (syncInfo.catching_up ? 'CatchUp' : 'Synced'));
    console.log('  同步中:       ' + syncInfo.catching_up);
  } catch {
    console.log(chalk.red('\n节点: 未运行'));
    console.log(chalk.gray('  使用 msg-devkit node start 启动'));
  }
}

export async function resetNode(): Promise<void> {
  console.log(chalk.yellow('即将重置节点数据...'));
  await stopNode();
  await fs.remove(NODE_DATA_DIR);
  await fs.ensureDir(NODE_DATA_DIR);
  console.log(chalk.green('✓ 节点数据已重置'));
}

async function waitForBlock(timeout = 30000): Promise<void> {
  const start = Date.now();
  while (Date.now() - start < timeout) {
    try {
      const { stdout } = await execa('curl', ['-s', 'http://localhost:26657/abci_info']);
      const info = JSON.parse(stdout);
      if (info.result?.response?.last_block_height > 0) {
        console.log(chalk.green('  ✓ 节点正在产生区块'));
        return;
      }
    } catch {
      // 等待
    }
    await new Promise((r) => setTimeout(r, 1000));
  }
  console.log(chalk.yellow('  等待超时,节点可能尚未就绪'));
}

function printNodeInfo(validatorCount: number): void {
  console.log(chalk.cyan('\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'));
  console.log(chalk.green('✅ 本地节点运行中\n'));
  console.log('  RPC:     http://localhost:26657');
  console.log('  REST:    http://localhost:1317');
  console.log('  gRPC:    localhost:9090');
  console.log('  Faucet:  http://localhost:8000');
  console.log('\n  验证者数量: ' + validatorCount);
  console.log('  Block Time: 5s');
  console.log('  共识机制: Round-Robin + DAR\n');
}

function generateDockerCompose(validatorCount: number): string {
  return [
    'version: "3.8"',
    'services:',
    '  msg-chain-node:',
    '    image: msgchain/node:latest',
    '    container_name: msg-chain-node-1',
    '    ports:',
    '      - "26657:26657"',
    '      - "1317:1317"',
    '      - "9090:9090"',
    '    volumes:',
    '      - ' + NODE_DATA_DIR + ':/root/.msgd',
    '    environment:',
    '      - CHAIN_ID=msg-chain-1',
    '      - VALIDATORS=' + validatorCount,
    '      - BECH32_PREFIX=msg',
    '      - DENOM=umsg',
    '      - COIN_TYPE=118',
    '      - BLOCK_TIME=5s',
    '    command: msgd start --rpc.laddr tcp://0.0.0.0:26657',
    '    healthcheck:',
    '      test: ["CMD", "curl", "-f", "http://localhost:26657/status"]',
    '      interval: 10s',
    '      timeout: 5s',
    '      retries: 5',
    '  msg-chain-db:',
    '    image: postgres:16-alpine',
    '    container_name: msg-chain-db-1',
    '    environment:',
    '      POSTGRES_USER: msgchain',
    '      POSTGRES_PASSWORD: msgchain',
    '      POSTGRES_DB: msgchain',
    '    ports:',
    '      - "5432:5432"',
    '  msg-chain-explorer:',
    '    image: pingpub/explorer:latest',
    '    container_name: msg-chain-explorer-1',
    '    ports:',
    '      - "3000:3000"',
    '    depends_on:',
    '      - msg-chain-node',
    '    environment:',
    '      CHAIN_RPC: http://msg-chain-node:26657',
    '      CHAIN_REST: http://msg-chain-node:1317',
  ].join('\n');
}

4.16 网络管理器 (src/config/networks.ts)

import fs from 'fs-extra';
import path from 'path';
import chalk from 'chalk';
import { NetworkConfig } from '../types/index.js';
import { DEFAULT_NETWORKS } from './defaults.js';

const NETWORKS_FILE = 'networks.json';

export async function listNetworks(cwd?: string): Promise<void> {
  const dir = cwd || process.cwd();
  let networks: Record<string, NetworkConfig> = { ...DEFAULT_NETWORKS };

  const networksPath = path.join(dir, NETWORKS_FILE);
  if (await fs.pathExists(networksPath)) {
    const custom = await fs.readJson(networksPath);
    networks = { ...networks, ...custom };
  }

  console.log(chalk.cyan('\n网络配置:'));
  console.log(chalk.gray('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n'));

  for (const [name, net] of Object.entries(networks)) {
    const isDefault = name === 'local';
    console.log(chalk.bold('  ' + name) + (isDefault ? ' (默认)' : ''));
    console.log('    Chain: ' + net.chainId);
    console.log('    RPC:   ' + net.rpcUrl);
    console.log('    REST:  ' + net.restUrl);
    console.log('    Gas:   ' + net.gasPrice + '\n');
  }
}

export async function addNetwork(
  name: string,
  config: Partial<NetworkConfig>,
  cwd?: string
): Promise<void> {
  const dir = cwd || process.cwd();
  const networksPath = path.join(dir, NETWORKS_FILE);

  let networks: Record<string, any> = {};
  if (await fs.pathExists(networksPath)) {
    networks = await fs.readJson(networksPath);
  }

  networks[name] = {
    name,
    chainId: config.chainId || name + '-1',
    rpcUrl: config.rpcUrl || 'http://localhost:26657',
    restUrl: config.restUrl || 'http://localhost:1317',
    explorerUrl: config.explorerUrl || '',
    gasPrice: '1000000000attoMSG',
    gasLimit: 3000000,
    denom: 'umsg',
    bech32Config: {
      bech32PrefixAccAddr: 'msg',
      bech32PrefixAccPub: 'msgpub',
      bech32PrefixValAddr: 'msgvaloper',
      bech32PrefixValPub: 'msgvaloperpub',
      bech32PrefixConsAddr: 'msgvalcons',
      bech32PrefixConsPub: 'msgvalconspub',
    },
  };

  await fs.writeJson(networksPath, networks, { spaces: 2 });
  console.log(chalk.green('网络 ' + name + ' 已添加'));
}

export async function useNetwork(name: string, cwd?: string): Promise<void> {
  const dir = cwd || process.cwd();
  const configPath = path.join(dir, 'msgchain.config.json');

  if (!await fs.pathExists(configPath)) {
    console.log(chalk.red('未找到 msgchain.config.json'));
    return;
  }

  const config = await fs.readJson(configPath);
  config.defaultNetwork = name;
  await fs.writeJson(configPath, config, { spaces: 2 });
  console.log(chalk.green('默认网络已切换为: ' + name));
}

4.17 命令处理层 (src/commands/)

每个命令文件小而集中,主要职责是解析选项、调用核心引擎、格式化输出。下面以几个核心命令为例。

src/commands/init.ts:

import { scaffoldProject } from '../core/scaffold.js';
import { ScaffoldOptions } from '../types/index.js';

export async function initCommand(
  projectName: string | undefined,
  options: any
): Promise<void> {
  const name = projectName || 'msg-chain-project';

  const scaffoldOpts: ScaffoldOptions = {
    projectName: name,
    template: options.template || 'contract',
    contractName: options.contractName,
    framework: options.framework,
    language: options.language,
    ci: options.ci,
    yes: options.yes,
  };

  await scaffoldProject(scaffoldOpts);
}

src/commands/compile.ts:

import { compileAllContracts, CompileOptions } from '../core/compile-engine.js';

export async function compileCommand(options: any): Promise<void> {
  const opts: CompileOptions = {
    contract: options.contract,
    optimize: options.optimize !== 'false',
    optimizeLevel: options.optimizeLevel || 'z',
    outDir: options.outDir || './artifacts',
    force: options.force || false,
  };

  await compileAllContracts(opts);
}

src/commands/test.ts:

import { runAllTests, TestOptions } from '../core/test-runner.js';

export async function testCommand(options: any): Promise<void> {
  const opts: TestOptions = {
    contract: options.contract,
    testName: options.testName,
    integration: options.integration || false,
    coverage: options.coverage || false,
    verbose: options.verbose || false,
    noRust: options.noRust === false,
  };

  await runAllTests(opts);
}

src/commands/deploy.ts:

import chalk from 'chalk';
import { GuardEngine } from '../core/guard.js';
import { deployContract, DeployOptions } from '../core/deploy-engine.js';
import { loadConfig } from '../config/config-loader.js';

export async function deployCommand(options: any): Promise<void> {
  const config = await loadConfig();

  // 生产环境检查 Guard 模式
  if (options.network === 'mainnet' || options.network === 'testnet') {
    GuardEngine.assertGuardFlag(options.guard);

    const guard = new GuardEngine();
    const gatesPassed = await guard.executeAllGates({
      skipGates: options.skipGates,
    });

    if (!gatesPassed) {
      process.exit(1);
    }
  }

  const deployOpts: DeployOptions = {
    network: options.network || 'local',
    contract: options.contract,
    signer: options.signer,
    gas: options.gas || '1000000000attoMSG',
    gasLimit: options.gasLimit || '3000000',
    label: options.label,
    admin: options.admin,
    noAdmin: options.noAdmin || false,
    dryRun: options.dryRun || false,
    guard: options.guard || false,
  };

  console.log(chalk.cyan('\n部署合约到 ' + deployOpts.network + '\n'));

  // 具体部署逻辑调用 deploy-engine
  const results = await deployContract(
    'artifacts/' + (deployOpts.contract || 'contract') + '.wasm',
    deployOpts.contract || 'unknown',
    {},
    deployOpts,
    config
  );

  console.log(chalk.green('\n✓ 部署成功\n'));
  console.log(JSON.stringify(results, null, 2));
}

src/commands/publish.ts:

import chalk from 'chalk';
import { GuardEngine } from '../core/guard.js';
import { loadConfig } from '../config/config-loader.js';
import { compileAllContracts } from '../core/compile-engine.js';
import { deployAllContracts, DeployOptions } from '../core/deploy-engine.js';

export async function publishCommand(options: any): Promise<void> {
  // Guard 模式强制检查
  GuardEngine.assertGuardFlag(options.guard);

  const config = await loadConfig();
  const tag = options.tag || 'untagged';

  const guard = new GuardEngine(tag);
  const gatesPassed = await guard.executeAllGates({
    skipGates: options.skipGates,
  });

  if (!gatesPassed) {
    console.log(chalk.red('审批门未通过,发布终止'));
    process.exit(1);
  }

  console.log(chalk.cyan('\n✅ 全部审批门已通过 — 开始发布流程\n'));

  // 步骤 1: 编译
  console.log(chalk.cyan('步骤 1/4: 编译合约\n'));
  await compileAllContracts({ optimize: true, optimizeLevel: 'z' });

  // 步骤 2/3: 部署
  const deployOpts: DeployOptions = {
    network: options.network || 'mainnet',
    contract: options.contract,
    signer: options.signer || config.deploy.signer,
    guard: true,
  };

  console.log(chalk.cyan('\n步骤 2-3/4: Store Code + Instantiate\n'));
  const results = await deployAllContracts(deployOpts);

  // 步骤 4: 验证与证据收集
  console.log(chalk.cyan('\n步骤 4/4: 验证与证据收集\n'));

  await guard.createEvidenceSummary({
    tag,
    results,
    network: options.network,
  });

  console.log(chalk.green('\n✅ 发布完成 (' + tag + ')\n'));
  console.log(chalk.gray('证据存档: .approval-evidence/' + tag + '/'));
}

4.18 插件系统

@msg-chain/devkit 支持插件扩展机制,允许社区开发者添加自定义命令和任务。

import { Command } from 'commander';

export interface DevkitPlugin {
  name: string;
  version: string;
  setup: (program: Command, context: PluginContext) => void;
}

export interface PluginContext {
  config: any;
  log: (msg: string) => void;
}

const plugins: DevkitPlugin[] = [];

export function registerPlugin(plugin: DevkitPlugin): void {
  plugins.push(plugin);
  console.log('插件已注册: ' + plugin.name + '@' + plugin.version);
}

export function loadPlugins(program: Command): void {
  for (const plugin of plugins) {
    plugin.setup(program, {
      config: {},
      log: (msg) => console.log('[plugin] ' + msg),
    });
  }
}

5. 项目模板

5.1 合约模板结构

msg-devkit init my-ai-agent --template contract 生成以下完整文件:

my-ai-agent/
├── contracts/
│   └── cosmwasm/
│       └── all/
│           └── my_ai_agent/
│               ├── Cargo.toml        # Rust 包配置
│               ├── src/
│               │   ├── contract.rs   # 合约入口点
│               │   ├── msg.rs        # 消息类型
│               │   ├── state.rs      # 状态存储
│               │   ├── error.rs      # 错误类型
│               │   ├── lib.rs        # 模块导出
│               │   └── schema.rs     # Schema 生成
│               └── tests/
│                   └── integration.rs # 集成测试
├── scripts/
│   └── deploy.ts                    # TypeScript 部署脚本
├── test/
│   └── integration/
│       └── my_ai_agent.test.ts      # 端到端测试
├── init-msgs/
│   └── my_ai_agent.json             # 初始化消息
├── msgchain.config.json             # 链配置
├── networks.json                    # 网络配置
├── package.json                     # Node 依赖
└── .gitignore

5.2 dApp 模板结构

msg-devkit init my-dapp --template dapp --framework react 生成:

my-dapp/
├── src/
│   ├── chain/
│   │   └── client.ts      # CosmJS + Keplr 封装
│   ├── components/
│   │   └── WalletConnect.tsx  # 钱包连接组件
│   └── config/
│       └── chain.ts       # MSG Chain 配置常量
├── package.json
└── tsconfig.json

5.3 Keplr 钱包配置

在 dApp 模板中自动包含的 Keplr 配置参数:

// MSG Chain Keplr 配置
const MSG_CHAIN_KEPLR = {
  chainId: "msg-chain-1",
  chainName: "MSG Chain",
  rpc: "http://localhost:26657",
  rest: "http://localhost:1317",
  bip44: { coinType: 118 },
  bech32Config: {
    bech32PrefixAccAddr: "msg",
    bech32PrefixAccPub: "msgpub",
    bech32PrefixValAddr: "msgvaloper",
    bech32PrefixValPub: "msgvaloperpub",
    bech32PrefixConsAddr: "msgvalcons",
    bech32PrefixConsPub: "msgvalconspub",
  },
  currencies: [{
    coinDenom: "MSG",
    coinMinimalDenom: "umsg",
    coinDecimals: 18,
  }],
  feeCurrencies: [{
    coinDenom: "MSG",
    coinMinimalDenom: "umsg",
    coinDecimals: 18,
    gasPriceStep: { low: 1000000000attoMSG, average: 1000000000attoMSG, high: 1000000000attoMSG },
  }],
  stakeCurrency: {
    coinDenom: "MSG",
    coinMinimalDenom: "umsg",
    coinDecimals: 18,
  },
};

6. 网络管理

6.1 预设网络

网络 Chain ID RPC 用途 Gas
local msg-chain-1 http://localhost:26657 本地开发 1000000000attoMSG
devnet devnet-1 https://rpc.devnet.msgchain.org 开发网络(预留) 1000000000attoMSG
testnet testnet-1 https://rpc.testnet.msgchain.org 测试网络(预留) 1000000000attoMSG
mainnet msg-chain-1 https://rpc.mainnet.msgchain.org 主网 1000000000attoMSG

6.2 Gas 价格预设

export const GAS_PRESETS = {
  low:     '1000000000attoMSG',   // 低优先级
  average: '1000000000attoMSG',  // 标准
  high:    '1000000000attoMSG',   // 高优先级
};

6.3 自定义网络

除预设网络外,开发者可通过 msg-devkit network add 添加自定义网络,配置存储在 networks.json 中。


7. Guard 模式

7.1 概述

Guard 模式是 @msg-chain/devkit 的生产环境安全机制。任何涉及主网或测试网的操作都必须使用 --guard 标志激活审批门检查。

无需审批的操作: compile, test, schema, init
需要 Guard 的操作: deploy --network mainnet, publish, deploy --guard

7.2 四道审批门

# 审批门 ID 阶段 人工输入 强制
1 scope_lock before_codegen 产品目标, 业务规则, 验收标准 是
2 secret_injection before_real_write_or_deploy 签名账户, API token, 环境变量 是
3 production_release before_launch 最终审批, 回滚确认 是
4 governance_or_treasury high_risk_write DAO通过, timelock结束 是

7.3 证据收集

通行每次 Guard 部署后,系统自动在 .approval-evidence/<tag>/ 目录下生成:

.approval-evidence/v1.0.0/
├── scope_lock.md                  # 审批门1 人工输入记录
├── secret_injection.md            # 审批门2 人工输入记录
├── production_release.md          # 审批门3 人工输入记录
├── governance_or_treasury.md      # 审批门4 人工输入记录
├── deployment_receipt.json        # 完整发布收据
└── post_state_query.json          # 部署后链上状态查询

8. CI/CD 集成

8.1 GitHub Actions 工作流

msg-devkit init --ci github-actions 生成 .github/workflows/ci.yml,包含以下 job:

Job 对应命令 触发条件
quality_gate make deps, make lint, make test, make test-quantum push/PR
contract_validation msg-devkit compile, msg-devkit test, msg-devkit schema push/PR
build make build-linux quality_gate + contract_validation 通过
package make package main 分支
deploy_testnet msg-devkit deploy --network testnet --guard main 分支
deploy_mainnet msg-devkit publish --guard --network mainnet tag v* 推送

8.2 Makefile 命令映射

make deps           # 安装依赖
make lint           # 代码质量检查 (gofmt, go vet, golangci-lint)
make test           # 运行测试
make test-quantum   # 量子模块测试
make build-linux    # 构建 Linux 二进制 (genesis_node + quantum_node)
make ci-contracts  # 合约 CI: 构建所有合约 wasm + cargo test
make package        # 打包部署包 (deploy-<version>.tar.gz)

9. 完整的端到端示例

以下演示从零开始创建、编译、测试、部署和交互一个 MSG Chain 合约的完整流程。

9.1 环境准备

# 确保环境满足前提条件
node --version    # >= 18.0.0
npm --version     # 最新
rustc --version   # >= 1.75.0
go version        # >= 1.24.6
docker --version  # 最新
docker-compose --version

# 安装 wasm 目标
rustup target add wasm32-unknown-unknown

# 安装 binaryen
sudo apt install binaryen   # Linux
# brew install binaryen     # macOS

9.2 创建项目

# 方式 1: npx(推荐,无需全局安装)
npx @msg-chain/devkit init my-ai-agent --template contract

# 方式 2: 全局安装后
npm install -g @msg-chain/devkit
msg-devkit init my-ai-agent

# 进入项目目录
cd my-ai-agent
npm install

9.3 检查项目结构

ls -la
# 输出:
# contracts/
# scripts/
# test/
# msgchain.config.json
# package.json
# tsconfig.json
# .gitignore

ls contracts/cosmwasm/all/my_ai_agent/src/
# contract.rs  error.rs  lib.rs  msg.rs  schema.rs  state.rs

9.4 编译合约

npx @msg-chain/devkit compile

# 预期输出:
#   发现 1 个合约包
#   编译 my_ai_agent...
#   ✓ my_ai_agent — 148.2 KB (优化后) [24.5s]
#   ✓ 1/1 合约编译成功

# 检查编译产物
ls -la artifacts/
# my_ai_agent.wasm
# checksums.txt

9.5 运行测试

npx @msg-chain/devkit test

# 预期输出:
#   发现 1 个合约包需要测试
#   测试 my_ai_agent...
#   ✓ my_ai_agent — 8 passed, 0 failed [0.59s]
#   ✓ 总计: 8 passed, 0 failed

9.6 生成 Schema

npx @msg-chain/devkit schema

# 预期输出:
#   my_ai_agent
#   ✓ InstantiateMsg   → schemas/my_ai_agent/instantiate_msg.json
#   ✓ ExecuteMsg       → schemas/my_ai_agent/execute_msg.json
#   ✓ QueryMsg         → schemas/my_ai_agent/query_msg.json
#   ✓ 3 schema files generated in schemas/

9.7 启动本地节点

npx @msg-chain/devkit node start --clean

# 预期输出:
#   清理旧数据...
#   启动 Docker Compose 服务...
#   等待节点同步...
#   ✅ 本地节点运行中
#   RPC:     http://localhost:26657
#   REST:    http://localhost:1317

# 验证节点状态
npx @msg-chain/devkit node status
# 节点: 运行中
#   区块高度: 42

9.8 部署合约

# 写入初始化消息
echo '{"key":"my_ai_agent_v1"}' > init-msgs/my_ai_agent.json

# 部署到本地网络
npx @msg-chain/devkit deploy --network local --contract my_ai_agent

# 预期输出:
#   目标网络: local (msg-chain-1)
#   签名账户: validator
#   步骤 1/2: Store Code
#   ✓ Code ID: 1 (tx: 9A2B3C...DEF)
#   步骤 2/2: Instantiate
#   ✓ 合约地址: msg1x2y3z4...abc
# ✅ 部署成功!

# 查看部署记录
cat deployments/local.json
# {"my_ai_agent": {"codeId": 1, "address": "msg1x2y3z4...abc", ...}}

9.9 合约验证

npx @msg-chain/devkit verify --network local --contract my_ai_agent

# 预期输出:
# ✓ 验证通过 — my_ai_agent
#   本地 SHA256:  a1b2c3d4e5f6...
#   链上 SHA256:  a1b2c3d4e5f6...
#   ✓ 匹配

9.10 交互控制台

npx @msg-chain/devkit console --network local

# 交互示例:
msg> await client.getChainId()
'msg-chain-1'

msg> await client.queryContractSmart("msg1x2y3z4...abc", { get_config: {} })
{ owner: "msg1...", key: "my_ai_agent_v1" }

msg> .exit

9.11 水龙头申请代币

# 如果有新地址需要测试代币
npx @msg-chain/devkit faucet --address msg1new...addr --amount 1000000

# 预期输出:
#   地址: msg1new...addr
#   数量: 1000000 umsg
# ✓ 成功! 交易哈希: FAUCET...123

9.12 Guard 模式生产发布

# 注意: 此命令需要人工交互输入审批信息
npx @msg-chain/devkit publish --guard --network mainnet --tag v1.0.0

# 交互审批流程:
# 审批门 1/4: scope_lock — 输入产品目标、业务规则、验收标准
# 审批门 2/4: secret_injection — 输入签名账户、API token、环境变量
# 审批门 3/4: production_release — 确认最终审批和回滚方案
# 审批门 4/4: governance_or_treasury — 确认 DAO 通过和 timelock 结束

# 审批通过后自动执行:
#   步骤 1/4: 编译合约 (release profile)
#   步骤 2/4: Store Code
#   步骤 3/4: Instantiate
#   步骤 4/4: 验证与证据收集
# ✅ 发布完成 (v1.0.0)
# 证据存档: .approval-evidence/v1.0.0/

9.13 网络切换

# 查看所有网络
npx @msg-chain/devkit network list

# 切换到 testnet
npx @msg-chain/devkit network use testnet

# 添加自定义网络
npx @msg-chain/devkit network add mynet \
  --rpc https://rpc.mynet.msgchain.org \
  --chain-id mynet-1

9.14 停止节点

npx @msg-chain/devkit node stop
#  ✓ 已停止

10. 附录

A. 常用命令速查

命令 用途 等价操作
msg-devkit init <name> 创建项目 —
msg-devkit compile 编译合约 cargo build --target wasm32-unknown-unknown --release
msg-devkit test 测试合约 cargo test
msg-devkit deploy 部署合约 msgd tx wasm store + msgd tx wasm instantiate
msg-devkit verify 验证合约 msgd query wasm code
msg-devkit console 交互控制台 REPL + CosmJS
msg-devkit node start 启动节点 docker-compose up + msgd start
msg-devkit node stop 停止节点 docker-compose down
msg-devkit node status 节点状态 curl localhost:26657/status
msg-devkit faucet 水龙头 msgd tx bank send
msg-devkit schema 生成 Schema cargo run --bin schema
msg-devkit network list 网络列表 读取 networks.json
msg-devkit publish --guard 生产发布 Guard 审批 + deploy

B. MSG Chain 技术参数汇总

参数 值
Chain ID msg-chain-1
原生代币 umsg
精度 18 位小数
地址前缀 msg
验证者地址前缀 msgvaloper
共识 Round-Robin + DAR
出块时间 5 秒
Bech32 类型 msg/msgpub/msgvaloper/msgvaloperpub/msgvalcons/msgvalconspub
CoinType 118
Gas 价格范围 1000000000attoMSG ~ 1000000000attoMSG umsg
构建产物 bin/genesis_node_linux, bin/quantum_node_linux
合约数量 46 个 CosmWasm 合约包

C. 配置示例 (msgchain.config.json)

{
  "$schema": "https://raw.githubusercontent.com/msg-chain/devkit/main/schemas/config.schema.json",
  "projectName": "my-ai-agent",
  "defaultNetwork": "local",
  "contractsPath": "contracts/cosmwasm/all",
  "artifactsDir": "artifacts",
  "compiler": {
    "optimize": true,
    "optimizeLevel": "z",
    "target": "wasm32-unknown-unknown"
  },
  "deploy": {
    "gasPrice": "1000000000attoMSG",
    "gasLimit": 3000000,
    "signer": "validator"
  },
  "networks": {
    "local": {
      "chainId": "msg-chain-1",
      "rpcUrl": "http://localhost:26657",
      "restUrl": "http://localhost:1317",
      "gasPrice": "1000000000attoMSG",
      "denom": "umsg"
    },
    "mainnet": {
      "chainId": "msg-chain-1",
      "rpcUrl": "https://rpc.mainnet.msgchain.org",
      "gasPrice": "1000000000attoMSG",
      "denom": "umsg"
    }
  }
}

D. 安全最佳实践

  1. 本地开发永远在 --network local 下进行
  2. 生产部署必须使用 --guard 标志
  3. 助记词仅通过环境变量传递,勿硬编码
  4. 部署前运行 msg-devkit verify 确保 WASM 一致性
  5. 使用 msg-devkit test --integration 包含端到端验证
  6. 保留 .approval-evidence/ 目录作为审计追踪
  7. 多签合约管理使用 --admin 指定 DAO 多签地址

E. 故障排除

问题 原因 解决方案
cargo build 失败 缺少 wasm 目标 rustup target add wasm32-unknown-unknown
wasm-opt 失败 缺少 binaryen apt install binaryen 或 brew install binaryen
Docker 节点启动失败 Docker 未运行 启动 Docker 桌面版
部署时 code ID not found 编译产物路径错误 检查 artifacts/ 目录
控制台连接超时 节点未启动 msg-devkit node start
--guard 标志缺失 Guard 强制要求 添加 --guard 标志
msgd 命令未找到 MSG Chain 守护程序未安装 安装 msgd 或使用 Docker 节点

本文档为 @msg-chain/devkit v1.0.0 的完整参考指南。
更多信息请访问: https://github.com/msg-chain/devkit
问题反馈: https://github.com/msg-chain/devkit/issues