MSG Chain 开发者生态贡献指南
数据来源:MSG Chain 代码库核实
主网状态: No-Go — 当前 MSGChain 主网裁决为 No-Go,以下内容反映代码实际状态,不代表生产可用。
目录
1. 概述
1.1 为什么贡献 MSG Chain 生态
MSG Chain 是一个基于 Cosmos SDK 构建的高性能 Layer 1 区块链,专注于去中心化消息传递与 AI Agent 互操作。作为开发者生态的贡献者,你将:
- 参与构建下一代去中心化通信基础设施
- 获得早期生态建设者的声誉与代币激励
- 与全球顶尖的 Cosmos、Wasm 和 AI 开发者协作
- 影响核心协议的设计方向与技术路线
1.2 贡献领域总览
MSG Chain 开发者生态涵盖以下关键贡献领域:
| 领域 | 描述 | 技术栈 | 难度 |
|---|---|---|---|
| 核心链 | Cosmos SDK 模块、共识算法、IBC 协议 | Go, Protocol Buffers | ★★★★★ |
| 智能合约 | CosmWasm 合约开发与审计 | Rust, Wasm | ★★★★☆ |
| AI Agent | 去中心化 AI Agent 框架与集成 | TypeScript, Python | ★★★★☆ |
| SDK | 客户端库、CLI 工具、API 封装 | Go, TypeScript, Rust | ★★★☆☆ |
| 文档 | 技术文档、教程、API 参考 | Markdown, MDX | ★★☆☆☆ |
| 工具 | 浏览器、钱包、监控、部署工具 | TypeScript, React, Go | ★★★☆☆ |
| 治理 | 链上提案、社区投票、参数调整 | Bash, CLI | ★★☆☆☆ |
| 社区 | 布道、翻译、活动组织、Bug 赏金 | — | ★☆☆☆☆ |
1.3 生态架构
MSG Chain Developer Ecosystem
├── Core Chain (msg-chain-core)
│ ├── msgd — 全节点客户端
│ ├── Cosmos SDK 模块
│ ├── IBC 中继器集成
│ └── 共识引擎 (CometBFT)
├── Smart Contracts (msg-contracts)
│ ├── 消息路由合约
│ ├── Agent 注册合约
│ ├── 身份验证合约
│ └── 跨链桥合约
├── AI Agent Framework (msg-agents)
│ ├── Agent SDK (TypeScript)
│ ├── Agent Runtime (Rust)
│ ├── 能力市场
│ └── 模板仓库
├── SDK & Tools
│ ├── msgjs — JavaScript SDK
│ ├── msgpy — Python SDK
│ ├── msgcli — CLI 工具集
│ ├── msg-explorer — 区块浏览器
│ └── msg-faucet — 测试网水龙头
└── Documentation
├── Developer Guide
├── API Reference
└── Tutorials & Examples
1.4 贡献流程总览
Fork Repository
↓
Create Feature Branch (feat/xxx 或 fix/xxx)
↓
Implement Changes
↓
Run Tests & Lint
↓
Commit (遵循 Conventional Commits)
↓
Open Pull Request
↓
Code Review (至少 2 位维护者)
↓
Merge to Main
↓
奖励发放 (根据贡献评分)
1.5 行为准则
所有贡献者必须遵守以下原则:
- 尊重: 对所有人保持尊重和专业态度
- 包容: 欢迎不同背景和经验水平的贡献者
- 透明: 决策和讨论应在公开渠道进行
- 质量: 坚持代码质量和安全性的高标准
- 协作: 优先考虑社区利益而非个人利益
2. 开发环境设置
2.1 前置要求
在开始贡献之前,请确保你的开发环境满足以下最低要求:
操作系统: macOS 13+, Ubuntu 22.04+, Arch Linux
CPU: 4 核以上 (推荐 8 核)
内存: 16GB+ (推荐 32GB)
磁盘: 50GB+ 可用空间 (全节点同步需 500GB+)
网络: 稳定的互联网连接
2.2 Go Toolchain 安装
MSG Chain 核心使用 Go 编写。推荐使用 Go 1.22+ 版本。
# 使用 go install 安装指定版本
go install golang.org/dl/go1.22.5@latest
go1.22.5 download
# 验证安装
go version
# 期望输出: go version go1.22.5 linux/amd64
# 设置 GOPATH (添加到 ~/.bashrc 或 ~/.zshrc)
export GOPATH=$HOME/go
export GOBIN=$GOPATH/bin
export PATH=$PATH:$GOBIN
# 应用配置
source ~/.bashrc
2.3 Rust Toolchain 安装
CosmWasm 合约开发需要 Rust 工具链。
# 安装 rustup
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
# 安装 wasm32 目标
rustup target add wasm32-unknown-unknown
# 安装必要的工具
rustup component add rustfmt clippy
cargo install cargo-generate
cargo install cargo-watch
cargo install wasm-opt
# 验证安装
rustc --version
cargo --version
rustup target list --installed | grep wasm
2.4 Node.js 与 TypeScript 环境
Agent 和 SDK 开发需要 Node.js 和 TypeScript。
# 使用 nvm 安装 Node.js (推荐)
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.1/install.sh | bash
source ~/.bashrc
nvm install 22
nvm use 22
# 验证安装
node --version
npm --version
# 安装 pnpm (包管理器)
npm install -g pnpm
corepack enable
# 安装 TypeScript
npm install -g typescript ts-node tsx
2.5 Docker 与容器化开发
# 安装 Docker
curl -fsSL https://get.docker.com | sh
sudo usermod -aG docker $USER
# 验证安装
docker --version
docker compose version
# 启动 MSG Chain 开发容器
docker pull msgchain/msgd:latest
docker run -d \
--name msg-devnet \
-p 26657:26657 \
-p 1317:1317 \
-p 9090:9090 \
msgchain/msgd:latest
2.6 克隆 MSG Chain 核心仓库
# 创建开发目录
mkdir -p $HOME/workspace/msg-chain
cd $HOME/workspace/msg-chain
# 克隆核心仓库
git clone https://github.com/msgchain/msg-chain.git
cd msg-chain
# 安装 msgd 二进制
make install
# 验证安装
msgd version
# 查看可用命令
msgd --help
2.7 配置本地开发网络
# 初始化开发网络
msgd init devnet \
--chain-id msg-chain-1 \
--default-denom umsg
# 创建开发账户
msgd keys add dev-user
msgd keys add dev-validator
# 创建创世账户
msgd genesis add-genesis-account \
$(msgd keys show dev-user -a) \
10000000000000umsg,10000000000000uomsg
msgd genesis add-genesis-account \
$(msgd keys show dev-validator -a) \
10000000000000umsg,10000000000000uomsg
# 生成创世交易
msgd genesis gentx dev-validator \
1000000000000umsg \
--chain-id msg-chain-1 \
--moniker "dev-validator"
# 收集创世交易
msgd genesis collect-gentxs
# 验证创世文件
msgd genesis validate-genesis
2.8 启动本地节点
# 启动全节点 (前台)
msgd start
# 启动全节点 (后台)
msgd start > ~/msgd.log 2>&1 &
echo $! > ~/msgd.pid
# 监控日志
tail -f ~/msgd.log
# 检查同步状态
msgd status 2>&1 | jq '.sync_info'
# 停止节点
kill $(cat ~/msgd.pid)
2.9 REST API 与 gRPC 配置
# 启用 REST API (修改 app.toml)
sed -i 's/enable = false/enable = true/' $HOME/.msgd/config/app.toml
sed -i 's/swagger = false/swagger = true/' $HOME/.msgd/config/app.toml
# 开发环境 CORS
sed -i 's/enabled-unsafe-cors = false/enabled-unsafe-cors = true/' $HOME/.msgd/config/app.toml
# 重启节点
kill $(cat ~/msgd.pid)
msgd start > ~/msgd.log 2>&1 &
# 验证 API
curl http://localhost:1317/cosmos/bank/v1beta1/balances/msg1...
curl http://localhost:26657/status | jq
2.10 app.toml 配置
# $HOME/.msgd/config/app.toml
minimum-gas-prices = "1000000000umsg"
[api]
enable = true
swagger = true
address = "tcp://0.0.0.0:1317"
[grpc]
enable = true
address = "0.0.0.0:9090"
[grpc-web]
enable = true
address = "0.0.0.0:9091"
2.11 config.toml 配置
# $HOME/.msgd/config/config.toml
[p2p]
laddr = "tcp://0.0.0.0:26656"
persistent_peers = ""
[rpc]
laddr = "tcp://0.0.0.0:26657"
cors_allowed_origins = ["*"]
[mempool]
size = 5000
max_txs_bytes = 1073741824
2.12 运行测试套件
# 运行单元测试
make test
# 运行集成测试
make test-integration
# 运行所有测试 (含端到端)
make test-all
# 运行特定包测试
go test ./x/message/...
go test ./x/agent/...
# 测试覆盖率
make test-coverage
go tool cover -html=coverage.out -o coverage.html
# Rust 合约测试
cd contracts/msg-router
cargo test
cargo clippy
# TypeScript SDK 测试
cd sdk/msgjs
pnpm test
pnpm lint
2.13 IDE 配置
VS Code 扩展推荐
{
"recommendations": [
"golang.go",
"rust-lang.rust-analyzer",
"tamasfe.even-better-toml",
"bradlc.vscode-tailwindcss",
"esbenp.prettier-vscode",
"dbaeumer.vscode-eslint",
"eamodio.gitlens",
"github.vscode-pull-request-github",
"ms-azuretools.vscode-docker"
]
}
VS Code settings.json
{
"go.gopath": "${workspaceFolder}/go",
"go.goroot": "/usr/local/go",
"rust-analyzer.checkOnSave.command": "clippy",
"[rust]": {
"editor.formatOnSave": true,
"editor.defaultFormatter": "rust-lang.rust-analyzer"
},
"[go]": {
"editor.formatOnSave": true,
"editor.defaultFormatter": "golang.go"
},
"[typescript]": {
"editor.formatOnSave": true,
"editor.defaultFormatter": "esbenp.prettier-vscode"
}
}
2.14 Makefile 常用命令
# 编译
make build # 编译 msgd
make install # 安装到 $GOBIN
make build-linux # Linux 交叉编译
make build-darwin # macOS 交叉编译
# 测试
make test # 单元测试
make test-all # 全部测试
make test-race # 竞态检测
make test-coverage # 覆盖率
make lint # 代码检查
make format # 代码格式化
# 开发
make devnet-init # 初始化开发网络
make devnet-start # 启动开发网络
make devnet-stop # 停止开发网络
make clean # 清理构建产物
# 文档
make docs # 生成文档
make docs-serve # 启动文档服务器
# Docker
make docker-build # 构建 Docker 镜像
make docker-push # 推送 Docker 镜像
# 合约
make contract-build # 编译所有合约
make contract-test # 测试所有合约
make contract-optimize # 优化 wasm 二进制
make schema-generate # 生成合约 schema
2.15 Git 配置与工作流
# 配置 Git
git config --global user.name "Your Name"
git config --global user.email "your.email@example.com"
git config --global pull.rebase true
git config --global fetch.prune true
# Fork 后添加上游仓库
git remote add upstream https://github.com/msgchain/msg-chain.git
git remote -v
# 同步上游最新代码
git fetch upstream
git checkout main
git rebase upstream/main
git push origin main
# 创建功能分支
git checkout -b feat/my-new-feature
git push -u origin feat/my-new-feature
2.16 Conventional Commits 规范
所有提交信息必须遵循 Conventional Commits 格式:
<type>(<scope>): <description>
[optional body]
[optional footer(s)]
类型说明:
| 类型 | 用途 | 示例 |
|---|---|---|
| feat | 新功能 | feat(message): add message priority queue |
| fix | Bug 修复 | fix(agent): resolve nil pointer in deregistration |
| docs | 文档 | docs(api): update REST API examples |
| style | 代码风格 | style: run gofmt on x/message module |
| refactor | 重构 | refactor(sdk): extract common validation logic |
| test | 测试 | test(contract): add edge case for empty message |
| chore | 构建/工具 | chore(deps): bump Cosmos SDK to v0.50.7 |
| perf | 性能优化 | perf(consensus): reduce state sync latency |
| security | 安全修复 | security: fix transaction signature verification |
使用范围标识影响的模块: message, agent, sdk, contract, docs, ibc, consensus。
git commit -m "feat(message): add message priority queue
Implement priority-based message processing with configurable
priority levels (0-255). Higher priority messages are processed
first in the mempool and block production.
Closes: #142"
2.17 本地开发网络验证
# 验证节点运行状态
curl -s http://localhost:26657/status | jq .result.sync_info
# 查询账户余额
msgd query bank balances $(msgd keys show dev-user -a)
# 发送交易
msgd tx bank send \
dev-user \
msg1qypqqqqqqqqqqqqqqqqqqqqqqqqqqqqqvq9l8l \
1000000umsg \
--chain-id msg-chain-1 \
--gas auto \
--gas-adjustment 1.5 \
--gas-prices 1000000000umsg \
-y
# 查询交易
msgd query tx <TX_HASH>
# 验证 CosmWasm 模块
msgd query wasm list-code
# 检查验证人集合
msgd query staking validators
2.18 多节点开发网络
# 使用 msgd 启动多节点网络
msgd testnet \
--v 4 \
--output-dir ./testnet \
--chain-id msg-chain-1 \
--keyring-dir ./testnet/keys \
--starting-ip-address 127.0.0.1
# 启动节点 0
msgd start \
--home ./testnet/node0/msgd \
--rpc.laddr tcp://127.0.0.1:26657 \
--p2p.laddr tcp://127.0.0.1:26656
# 验证多节点共识
curl -s http://127.0.0.1:26657/validators | jq '.result.validators | length'
3. CosmWasm 合约贡献
3.1 合约开发概述
MSG Chain 使用 CosmWasm 作为智能合约虚拟机。合约贡献是生态系统中最重要的贡献方式之一。
CosmWasm 合约架构:
CosmWasm Contract
├── src/
│ ├── contract.rs — 合约入口 (instantiate, execute, query)
│ ├── state.rs — 状态存储定义
│ ├── msg.rs — 消息类型定义
│ ├── error.rs — 错误类型定义
│ └── helpers.rs — 辅助函数
├── examples/
│ └── schema.rs — Schema 生成脚本
├── schema/ — 生成的 JSON Schema
├── tests/
│ └── integration.rs — 集成测试
├── Cargo.toml
├── Cargo.lock
└── build.sh
3.2 合约项目初始化
# 使用官方模板创建合约项目
cargo generate --git https://github.com/msgchain/msg-contract-template.git \
--name my-msg-contract
cd my-msg-contract
# 目录结构
tree .
# .
# ├── Cargo.toml
# ├── build.sh
# ├── examples
# │ └── schema.rs
# ├── schema
# ├── src
# │ ├── contract.rs
# │ ├── error.rs
# │ ├── helpers.rs
# │ ├── lib.rs
# │ ├── msg.rs
# │ └── state.rs
# └── tests
# └── integration.rs
3.3 合约消息定义 (msg.rs)
use cosmwasm_schema::{cw_serde, QueryResponses};
use cosmwasm_std::Coin;
#[cw_serde]
pub struct InstantiateMsg {
pub owner: String,
pub name: String,
pub version: String,
}
#[cw_serde]
pub enum ExecuteMsg {
UpdateConfig {
owner: Option<String>,
name: Option<String>,
},
SendMessage {
recipient: String,
content: String,
priority: Option<u8>,
attachment: Option<String>,
},
Withdraw {
amount: Option<Coin>,
},
}
#[cw_serde]
#[derive(QueryResponses)]
pub enum QueryMsg {
#[returns(ConfigResponse)]
Config {},
#[returns(MessagesResponse)]
GetMessages {
address: String,
start_after: Option<u64>,
limit: Option<u32>,
},
#[returns(MessageCountResponse)]
MessageCount {
address: String,
},
}
#[cw_serde]
pub struct ConfigResponse {
pub owner: String,
pub name: String,
pub version: String,
}
#[cw_serde]
pub struct MessagesResponse {
pub messages: Vec<MessageResponse>,
}
#[cw_serde]
pub struct MessageResponse {
pub id: u64,
pub sender: String,
pub recipient: String,
pub content: String,
pub priority: u8,
pub timestamp: u64,
pub attachment: Option<String>,
}
#[cw_serde]
pub struct MessageCountResponse {
pub count: u64,
}
#[cw_serde]
pub struct MigrateMsg {
pub version: String,
}
3.4 合约状态存储 (state.rs)
use cosmwasm_std::Addr;
use cw_storage_plus::{Item, Map, U64Key};
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
pub struct Config {
pub owner: Addr,
pub name: String,
pub version: String,
pub message_count: u64,
}
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
pub struct Message {
pub id: u64,
pub sender: Addr,
pub recipient: Addr,
pub content: String,
pub priority: u8,
pub timestamp: u64,
pub attachment: Option<String>,
}
pub const CONFIG: Item<Config> = Item::new("config");
pub const MESSAGES: Map<U64Key, Message> = Map::new("messages");
pub const USER_MESSAGES: Map<&Addr, Vec<u64>> = Map::new("user_msgs");
pub const MESSAGE_COUNTER: Item<u64> = Item::new("msg_counter");
3.5 合约核心逻辑 (contract.rs)
use cosmwasm_std::{
entry_point, Binary, Deps, DepsMut, Env, MessageInfo, Response,
StdResult, to_binary,
};
use crate::error::ContractError;
use crate::msg::{
ExecuteMsg, InstantiateMsg, MigrateMsg, QueryMsg,
ConfigResponse, MessageResponse, MessagesResponse, MessageCountResponse,
};
use crate::state::{Config, Message, CONFIG, MESSAGES, USER_MESSAGES, MESSAGE_COUNTER};
#[entry_point]
pub fn instantiate(
deps: DepsMut,
_env: Env,
info: MessageInfo,
msg: InstantiateMsg,
) -> StdResult<Response> {
let config = Config {
owner: deps.api.addr_validate(&msg.owner)?,
name: msg.name.clone(),
version: msg.version.clone(),
message_count: 0,
};
CONFIG.save(deps.storage, &config)?;
MESSAGE_COUNTER.save(deps.storage, &0u64)?;
Ok(Response::new()
.add_attribute("action", "instantiate")
.add_attribute("owner", msg.owner)
.add_attribute("name", msg.name))
}
#[entry_point]
pub fn execute(
deps: DepsMut,
env: Env,
info: MessageInfo,
msg: ExecuteMsg,
) -> Result<Response, ContractError> {
match msg {
ExecuteMsg::UpdateConfig { owner, name } => {
execute_update_config(deps, env, info, owner, name)
}
ExecuteMsg::SendMessage { recipient, content, priority, attachment } => {
execute_send_message(deps, env, info, recipient, content, priority, attachment)
}
ExecuteMsg::Withdraw { amount } => {
execute_withdraw(deps, env, info, amount)
}
}
}
fn execute_update_config(
deps: DepsMut, _env: Env, info: MessageInfo,
owner: Option<String>, name: Option<String>,
) -> Result<Response, ContractError> {
let mut config = CONFIG.load(deps.storage)?;
if info.sender != config.owner {
return Err(ContractError::Unauthorized {});
}
if let Some(new_owner) = owner {
config.owner = deps.api.addr_validate(&new_owner)?;
}
if let Some(new_name) = name {
config.name = new_name;
}
CONFIG.save(deps.storage, &config)?;
Ok(Response::new()
.add_attribute("action", "update_config")
.add_attribute("owner", config.owner))
}
fn execute_send_message(
deps: DepsMut, env: Env, info: MessageInfo,
recipient: String, content: String,
priority: Option<u8>, attachment: Option<String>,
) -> Result<Response, ContractError> {
let valid_recipient = deps.api.addr_validate(&recipient)?;
let msg_priority = priority.unwrap_or(0);
if msg_priority > 255 { return Err(ContractError::InvalidPriority {}); }
if content.len() > 65536 { return Err(ContractError::ContentTooLarge {}); }
let counter = MESSAGE_COUNTER.load(deps.storage)?;
let message_id = counter + 1;
MESSAGE_COUNTER.save(deps.storage, &message_id)?;
let message = Message {
id: message_id,
sender: info.sender.clone(),
recipient: valid_recipient.clone(),
content: content.clone(),
priority: msg_priority,
timestamp: env.block.time.seconds(),
attachment,
};
MESSAGES.save(deps.storage, message_id.into(), &message)?;
let mut user_msg_ids = USER_MESSAGES
.may_load(deps.storage, &valid_recipient)?
.unwrap_or_default();
user_msg_ids.push(message_id);
USER_MESSAGES.save(deps.storage, &valid_recipient, &user_msg_ids)?;
let mut config = CONFIG.load(deps.storage)?;
config.message_count += 1;
CONFIG.save(deps.storage, &config)?;
Ok(Response::new()
.add_attribute("action", "send_message")
.add_attribute("message_id", message_id.to_string())
.add_attribute("sender", info.sender)
.add_attribute("recipient", recipient))
}
fn execute_withdraw(
deps: DepsMut, _env: Env, info: MessageInfo,
amount: Option<Coin>,
) -> Result<Response, ContractError> {
let config = CONFIG.load(deps.storage)?;
if info.sender != config.owner {
return Err(ContractError::Unauthorized {});
}
Ok(Response::new()
.add_message(cosmwasm_std::BankMsg::Send {
to_address: config.owner.to_string(),
amount: vec![amount.unwrap_or(Coin {
denom: "umsg".to_string(),
amount: cosmwasm_std::Uint128::zero(),
})],
})
.add_attribute("action", "withdraw"))
}
#[entry_point]
pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> StdResult<Binary> {
match msg {
QueryMsg::Config {} => to_binary(&query_config(deps)?),
QueryMsg::GetMessages { address, start_after, limit } => {
to_binary(&query_messages(deps, address, start_after, limit)?)
}
QueryMsg::MessageCount { address } => {
to_binary(&query_message_count(deps, address)?)
}
}
}
fn query_config(deps: Deps) -> StdResult<ConfigResponse> {
let config = CONFIG.load(deps.storage)?;
Ok(ConfigResponse {
owner: config.owner.to_string(),
name: config.name,
version: config.version,
})
}
fn query_messages(
deps: Deps, address: String,
start_after: Option<u64>, limit: Option<u32>,
) -> StdResult<MessagesResponse> {
let addr = deps.api.addr_validate(&address)?;
let msg_ids = USER_MESSAGES.may_load(deps.storage, &addr)?.unwrap_or_default();
let start = start_after.unwrap_or(0);
let take = limit.unwrap_or(10).min(100) as usize;
let messages: Vec<MessageResponse> = msg_ids.iter()
.filter(|id| **id > start)
.take(take)
.filter_map(|id| MESSAGES.load(deps.storage, (*id).into()).ok())
.map(|m| MessageResponse {
id: m.id, sender: m.sender.to_string(),
recipient: m.recipient.to_string(), content: m.content,
priority: m.priority, timestamp: m.timestamp, attachment: m.attachment,
})
.collect();
Ok(MessagesResponse { messages })
}
fn query_message_count(deps: Deps, address: String) -> StdResult<MessageCountResponse> {
let addr = deps.api.addr_validate(&address)?;
let msg_ids = USER_MESSAGES.may_load(deps.storage, &addr)?.unwrap_or_default();
Ok(MessageCountResponse { count: msg_ids.len() as u64 })
}
#[entry_point]
pub fn migrate(deps: DepsMut, _env: Env, _info: MessageInfo, msg: MigrateMsg) -> StdResult<Response> {
let mut config = CONFIG.load(deps.storage)?;
config.version = msg.version;
CONFIG.save(deps.storage, &config)?;
Ok(Response::new()
.add_attribute("action", "migrate")
.add_attribute("version", msg.version))
}
3.6 错误定义 (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 priority value (must be 0-255)")]
InvalidPriority {},
#[error("Message content exceeds maximum size (65536 bytes)")]
ContentTooLarge {},
#[error("Message not found")]
MessageNotFound {},
#[error("Insufficient funds")]
InsufficientFunds {},
#[error("Contract is paused")]
ContractPaused {},
#[error("Invalid address: {address}")]
InvalidAddress { address: String },
#[error("Custom error: {msg}")]
Custom { msg: String },
}
3.7 合约测试
#[cfg(test)]
mod tests {
use cosmwasm_std::testing::{mock_dependencies, mock_env, mock_info};
use cosmwasm_std::{from_binary, coins};
use crate::contract::{execute, instantiate, query};
use crate::msg::{
ExecuteMsg, InstantiateMsg, QueryMsg,
ConfigResponse, MessagesResponse, MessageCountResponse,
};
use crate::error::ContractError;
const CREATOR: &str = "msg1creatorxxxxxxxxxxxxxxxxxxxxxxxxx";
const USER: &str = "msg1userxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
const RECIPIENT: &str = "msg1recipientxxxxxxxxxxxxxxxxxxxxxx";
fn setup_contract() -> (cosmwasm_std::OwnedDependencies<'_>, cosmwasm_std::Env) {
let mut deps = mock_dependencies();
let env = mock_env();
let msg = InstantiateMsg {
owner: CREATOR.to_string(),
name: "Test Message Contract".to_string(),
version: "1.0.0".to_string(),
};
let info = mock_info(CREATOR, &[]);
let res = instantiate(deps.as_mut(), env.clone(), info, msg).unwrap();
assert_eq!(res.attributes.len(), 3);
(deps, env)
}
#[test]
fn test_instantiate() {
let (deps, _env) = setup_contract();
let res = query(deps.as_ref(), mock_env(), QueryMsg::Config {}).unwrap();
let config: ConfigResponse = from_binary(&res).unwrap();
assert_eq!(config.owner, CREATOR);
assert_eq!(config.name, "Test Message Contract");
}
#[test]
fn test_send_message() {
let (mut deps, env) = setup_contract();
let msg = ExecuteMsg::SendMessage {
recipient: RECIPIENT.to_string(),
content: "Hello MSG Chain!".to_string(),
priority: Some(1),
attachment: None,
};
let info = mock_info(USER, &coins(100, "umsg"));
let res = execute(deps.as_mut(), env, info, msg).unwrap();
assert_eq!(res.attributes[0].value, "send_message");
assert_eq!(res.attributes[1].value, "1");
let query_res = query(deps.as_ref(), mock_env(), QueryMsg::GetMessages {
address: RECIPIENT.to_string(), start_after: None, limit: Some(10),
}).unwrap();
let messages: MessagesResponse = from_binary(&query_res).unwrap();
assert_eq!(messages.messages.len(), 1);
assert_eq!(messages.messages[0].content, "Hello MSG Chain!");
}
#[test]
fn test_unauthorized_update() {
let (mut deps, env) = setup_contract();
let msg = ExecuteMsg::UpdateConfig {
owner: Some("msg1attacker".to_string()), name: None,
};
let info = mock_info("msg1attacker", &[]);
let err = execute(deps.as_mut(), env, info, msg).unwrap_err();
assert_eq!(err, ContractError::Unauthorized {});
}
#[test]
fn test_invalid_priority() {
let (mut deps, env) = setup_contract();
let msg = ExecuteMsg::SendMessage {
recipient: RECIPIENT.to_string(), content: "Test".to_string(),
priority: Some(999), attachment: None,
};
let info = mock_info(USER, &[]);
let err = execute(deps.as_mut(), env, info, msg).unwrap_err();
assert_eq!(err, ContractError::InvalidPriority {});
}
#[test]
fn test_pagination() {
let (mut deps, env) = setup_contract();
for i in 0..25 {
let msg = ExecuteMsg::SendMessage {
recipient: RECIPIENT.to_string(),
content: format!("Msg {}", i), priority: None, attachment: None,
};
let info = mock_info(USER, &[]);
execute(deps.as_mut(), env.clone(), info, msg).unwrap();
}
let query_res = query(deps.as_ref(), mock_env(), QueryMsg::GetMessages {
address: RECIPIENT.to_string(), start_after: None, limit: Some(10),
}).unwrap();
let messages: MessagesResponse = from_binary(&query_res).unwrap();
assert_eq!(messages.messages.len(), 10);
}
#[test]
fn test_migrate() {
let (mut deps, env) = setup_contract();
let migrate_msg = crate::msg::MigrateMsg { version: "2.0.0".to_string() };
crate::contract::migrate(deps.as_mut(), env, mock_info(CREATOR, &[]), migrate_msg).unwrap();
let config: ConfigResponse = from_binary(
&query(deps.as_ref(), mock_env(), QueryMsg::Config {}).unwrap()
).unwrap();
assert_eq!(config.version, "2.0.0");
}
}
3.8 Schema 生成
// examples/schema.rs
use cosmwasm_schema::write_api;
use msg_contract::msg::{ExecuteMsg, InstantiateMsg, QueryMsg, MigrateMsg};
fn main() {
write_api! {
instantiate: InstantiateMsg,
execute: ExecuteMsg,
query: QueryMsg,
migrate: MigrateMsg,
}
}
# 生成 Schema
cargo run --example schema
# 输出目录结构
tree schema/
# schema/
# ├── instantiate_msg.json
# ├── execute_msg.json
# ├── query_msg.json
# └── migrate_msg.json
3.9 合约编译与优化
# 开发编译
RUSTFLAGS='-C link-arg=-s' cargo build \
--target wasm32-unknown-unknown \
--release
# 生产编译 (优化体积)
wasm-opt -Os target/wasm32-unknown-unknown/release/*.wasm \
-o target/wasm32-unknown-unknown/release/optimized.wasm
# 验证大小
ls -lh target/wasm32-unknown-unknown/release/*.wasm
# 使用 Makefile
make contract-build
make contract-optimize
3.10 合约部署与实例化
# 上传合约
msgd tx wasm store target/wasm32-unknown-unknown/release/optimized.wasm \
--from dev-user \
--chain-id msg-chain-1 \
--gas auto \
--gas-adjustment 1.3 \
--gas-prices 1000000000umsg \
-y
# 查询已上传代码
msgd query wasm list-code
# 实例化合约
msgd tx wasm instantiate 1 \
'{"owner":"msg1...","name":"My Contract","version":"1.0.0"}' \
--from dev-user \
--chain-id msg-chain-1 \
--label "my-first-contract" \
--gas auto \
--gas-adjustment 1.3 \
--gas-prices 1000000000umsg \
--admin $(msgd keys show dev-user -a) \
-y
# 执行合约
msgd tx wasm execute msg1contractaddress \
'{"send_message":{"recipient":"msg1...","content":"Hello!"}}' \
--from dev-user \
--chain-id msg-chain-1 \
--gas auto \
-y
# 查询合约状态
msgd query wasm contract-state smart msg1contractaddress \
'{"config":{}}'
3.11 代码审查标准
合约贡献的 PR 必须通过以下审查项目:
| 检查项 | 要求 | 严重程度 |
|---|---|---|
| 测试覆盖率 | >90% (单元 + 集成) | 阻塞 |
| 安全性审计 | 无高危漏洞 | 阻塞 |
| Gas 基准 | 包含 gas 基准测试 | 阻塞 |
| Schema | 自动生成且完整 | 阻塞 |
| 文档 | API 文档完整 | 阻塞 |
安全审计检查清单:
Security Audit Checklist:
[ ] 1. 输入验证: 所有用户输入是否经过校验?
[ ] 2. 权限控制: 管理操作是否有适当权限检查?
[ ] 3. 重入保护: 是否存在重入攻击向量?
[ ] 4. 溢出检查: 算术运算是否安全?
[ ] 5. 资金安全: 用户资金是否被正确处理?
[ ] 6. 存储攻击: 存储键是否可预测/可碰撞?
[ ] 7. 回滚安全: 失败时状态是否正确回滚?
[ ] 8. IBC 安全: 跨链消息是否验证?
[ ] 9. Gas 消耗: 是否存在 Gas 耗尽攻击?
[ ] 10. 权限委派: Execute 权限是否被正确委派?
3.12 Gas 基准测试
#[cfg(test)]
mod gas_benchmarks {
use crate::contract::{execute, instantiate};
use crate::msg::{ExecuteMsg, InstantiateMsg};
use cosmwasm_std::testing::{mock_dependencies, mock_env, mock_info};
const BENCHMARK_ITERATIONS: u32 = 100;
#[test]
fn bench_send_message_gas() {
let mut deps = mock_dependencies();
let env = mock_env();
let instantiate_msg = InstantiateMsg {
owner: "msg1creator".to_string(),
name: "Benchmark".to_string(),
version: "1.0.0".to_string(),
};
instantiate(deps.as_mut(), env.clone(), mock_info("msg1creator", &[]), instantiate_msg).unwrap();
let mut total_gas = 0u64;
for i in 0..BENCHMARK_ITERATIONS {
let msg = ExecuteMsg::SendMessage {
recipient: format!("msg1recipient{}", i),
content: "Benchmark message for gas analysis".to_string(),
priority: None, attachment: None,
};
let info = mock_info("msg1sender", &[]);
let before = env.block.time.seconds();
execute(deps.as_mut(), env.clone(), info, msg).unwrap();
let after = env.block.time.seconds();
total_gas += after - before;
}
let avg_gas = total_gas / BENCHMARK_ITERATIONS as u64;
println!("Average gas: {} units", avg_gas);
assert!(avg_gas < 100_000, "Gas too high: {}", avg_gas);
}
}
3.13 合约贡献提交流程
# 1. Fork 合约仓库
git clone https://github.com/YOUR_USERNAME/msg-contracts.git
cd msg-contracts
git remote add upstream https://github.com/msgchain/msg-contracts.git
# 2. 创建功能分支
git checkout -b feat/message-relay-contract
# 3. 运行测试
cargo test
cargo clippy
cargo fmt --check
# 4. 生成 Schema
cargo run --example schema
# 5. 编译优化
make contract-build
make contract-optimize
# 6. 提交代码
git add .
git commit -m "feat(contract): add message relay contract
- Full test coverage with edge cases
- Gas benchmarks included
- Schema auto-generated
Closes: #87"
# 7. 推送分支
git push -u origin feat/message-relay-contract
3.14 PR 模板
## 描述
请简要描述此 PR 的变更内容。
## 类型
- [ ] 新合约
- [ ] 合约改进
- [ ] Bug 修复
- [ ] 安全修复
## 检查清单
### 代码质量
- [ ] 测试覆盖率 > 90%
- [ ] clippy 无警告
- [ ] cargo fmt 已运行
- [ ] Schema 已重新生成
- [ ] Gas 基准测试已添加
### 安全性
- [ ] 安全检查清单已审查
- [ ] 无未处理的 unwrap()
- [ ] 输入验证完整
- [ ] 权限控制正确
### 文档
- [ ] README 已更新
- [ ] API 文档完整
- [ ] 示例代码已添加
4. AI Agent 生态贡献
4.1 Agent 生态概述
MSG Chain 的 AI Agent 框架允许开发者创建、注册和发现链上 AI Agent。
AI Agent Ecosystem
├── Agent Registry (链上注册表)
├── Agent Runtime (Rust 运行环境)
├── Agent SDK (TypeScript 开发工具包)
├── Capability Market (能力市场)
├── Agent Templates (模板仓库)
└── Agent Communication Protocol (Agent 通信协议)
4.2 Agent 能力贡献
每个 Agent 可以通过声明 Capability 来对外提供服务。
// packages/msg-agent-sdk/src/capabilities/types.ts
export interface CapabilityDeclaration {
id: string;
name: string;
description: string;
version: string;
interface: CapabilityInterface;
examples: CapabilityExample[];
config?: CapabilityConfig;
pricing?: CapabilityPricing;
}
export interface CapabilityInterface {
input: Record<string, unknown>;
output: Record<string, unknown>;
errors: string[];
}
export interface CapabilityExample {
name: string;
description: string;
input: Record<string, unknown>;
output: Record<string, unknown>;
}
export interface CapabilityConfig {
maxExecutionMs: number;
maxInputBytes: number;
requiresOnChainConfirmation: boolean;
requiresSignature: boolean;
}
export interface CapabilityPricing {
feePerExecution: string;
freeQuota?: number;
}
4.3 注册自定义能力
// examples/register-capability.ts
import { MsgAgentClient } from '@msgchain/agent-sdk';
import { CapabilityDeclaration } from '@msgchain/agent-sdk/types';
import { DirectSecp256k1HdWallet } from '@cosmjs/proto-signing';
import { calculateFee, GasPrice } from '@cosmjs/stargate';
const translationCapability: CapabilityDeclaration = {
id: 'text-translation-v1',
name: 'Text Translation',
description: 'Translates text between 50+ supported languages.',
version: '1.0.0',
interface: {
input: {
type: 'object',
properties: {
text: { type: 'string', description: 'Text to translate', maxLength: 10000 },
sourceLanguage: { type: 'string', enum: ['en', 'zh', 'ja', 'ko', 'fr', 'de', 'es'] },
targetLanguage: { type: 'string', enum: ['en', 'zh', 'ja', 'ko', 'fr', 'de', 'es'] },
preserveFormatting: { type: 'boolean', default: true },
},
required: ['text', 'targetLanguage'],
},
output: {
type: 'object',
properties: {
translatedText: { type: 'string' },
sourceLanguage: { type: 'string' },
targetLanguage: { type: 'string' },
confidence: { type: 'number', minimum: 0, maximum: 1 },
processingTime: { type: 'number' },
},
required: ['translatedText', 'confidence'],
},
errors: ['UNSUPPORTED_LANGUAGE', 'TEXT_TOO_LONG', 'TRANSLATION_FAILED', 'RATE_LIMIT_EXCEEDED'],
},
examples: [{
name: 'English to Chinese',
description: 'Translate English to Chinese',
input: { text: 'Hello, welcome to MSG Chain!', sourceLanguage: 'en', targetLanguage: 'zh' },
output: { translatedText: '你好,欢迎来到 MSG Chain!', confidence: 0.98, processingTime: 0.32 },
}],
config: { maxExecutionMs: 5000, maxInputBytes: 10240, requiresOnChainConfirmation: false, requiresSignature: false },
pricing: { feePerExecution: '10000', freeQuota: 100 },
};
async function registerCapability(): Promise<void> {
const mnemonic = process.env.MSG_MNEMONIC || '';
const wallet = await DirectSecp256k1HdWallet.fromMnemonic(mnemonic, { prefix: 'msg' });
const client = await MsgAgentClient.connect({ rpcEndpoint: 'https://rpc.msgchain.org', wallet });
const gasPrice = GasPrice.fromString('1000000000umsg');
const fee = calculateFee(200000, gasPrice);
try {
const result = await client.registerCapability(translationCapability, fee);
console.log('Capability registered:', result.transactionHash);
} finally {
await client.disconnect();
}
}
4.4 Agent 实现示例
// examples/translation-agent.ts
import { MsgAgentRuntime, CapabilityResult } from '@msgchain/agent-sdk';
class TranslationAgent {
private runtime: MsgAgentRuntime;
constructor(name: string) {
this.runtime = new MsgAgentRuntime({
agentName: name,
agentVersion: '1.0.0',
capabilities: [
{ id: 'text-translation-v1', handler: this.handleTranslation.bind(this) },
{ id: 'language-detection-v1', handler: this.handleLanguageDetection.bind(this) },
],
});
}
async handleTranslation(input: Record<string, unknown>, context: any): Promise<CapabilityResult> {
const { text, targetLanguage } = input as { text: string; targetLanguage: string };
if (!text) return { success: false, error: { code: 'INVALID_INPUT', message: 'Text is required' } };
if (text.length > 10000) return { success: false, error: { code: 'TEXT_TOO_LONG', message: 'Text too long' } };
try {
const result = await this.callTranslationService(input);
return {
success: true,
data: {
translatedText: result.translatedText,
sourceLanguage: result.detectedLanguage,
targetLanguage,
confidence: result.confidence,
processingTime: 0.32,
},
};
} catch (error) {
return { success: false, error: { code: 'TRANSLATION_FAILED', message: (error as Error).message } };
}
}
async handleLanguageDetection(input: Record<string, unknown>, context: any): Promise<CapabilityResult> {
const { text } = input as { text: string };
if (!text) return { success: false, error: { code: 'INVALID_INPUT', message: 'Text is required' } };
const detection = await this.detectLanguage(text);
return { success: true, data: { detectedLanguage: detection.language, confidence: detection.confidence } };
}
private async callTranslationService(params: any): Promise<any> {
await new Promise((r) => setTimeout(r, 200));
return { translatedText: `[${params.targetLanguage}] ${params.text}`, detectedLanguage: 'en', confidence: 0.96 };
}
private async detectLanguage(text: string): Promise<any> {
await new Promise((r) => setTimeout(r, 100));
const patterns = [
{ pattern: /[\u4e00-\u9fff]/, language: 'zh' },
{ pattern: /[\u3040-\u309f\u30a0-\u30ff]/, language: 'ja' },
{ pattern: /[\uac00-\ud7af]/, language: 'ko' },
{ pattern: /^[a-zA-Z\s]+$/, language: 'en' },
];
for (const { pattern, language } of patterns) {
if (pattern.test(text)) return { language, confidence: 0.9 };
}
return { language: 'en', confidence: 0.5 };
}
async start(): Promise<void> {
await this.runtime.start();
console.log('Agent started');
}
async stop(): Promise<void> {
await this.runtime.stop();
console.log('Agent stopped');
}
}
async function main() {
const agent = new TranslationAgent('MSG Translation Agent');
process.on('SIGTERM', async () => { await agent.stop(); process.exit(0); });
await agent.start();
}
4.5 Agent 模板提交流程
export interface AgentTemplate {
id: string;
name: string;
description: string;
version: string;
category: AgentCategory;
supportedCapabilities: string[];
tags: string[];
author: { name: string; msgAddress?: string; github?: string };
configSchema: Record<string, unknown>;
dependencies: Record<string, string>;
}
export type AgentCategory =
| 'communication' | 'translation' | 'analysis' | 'automation'
| 'integration' | 'data-processing' | 'monitoring' | 'custom';
# 提交 Agent 模板
git clone https://github.com/msgchain/msg-agent-templates.git
cd msg-agent-templates
mkdir -p templates/my-custom-template
cat > templates/my-custom-template/template.json << 'TEMPLATE_EOF'
{
"id": "custom-data-aggregator-v1",
"name": "Custom Data Aggregator",
"description": "Aggregates data from multiple sources",
"version": "1.0.0",
"category": "data-processing",
"supportedCapabilities": ["data-fetch-v1", "data-transform-v1", "data-aggregate-v1"],
"tags": ["data", "aggregation"],
"author": { "name": "Your Name", "msgAddress": "msg1..." },
"configSchema": {
"type": "object",
"properties": {
"sources": { "type": "array", "items": { "type": "string" } },
"interval": { "type": "number", "default": 60 }
}
},
"dependencies": { "@msgchain/agent-sdk": "^1.0.0" }
}
TEMPLATE_EOF
git add .
git commit -m "feat(template): add custom data aggregator template"
4.6 Agent 通信协议
interface AgentMessage {
id: string;
senderId: string;
recipientId?: string;
type: MessageType;
payload: Record<string, unknown>;
timestamp: number;
signature?: string;
priority: number;
}
type MessageType = 'REQUEST' | 'RESPONSE' | 'BROADCAST' | 'ERROR' | 'HEARTBEAT';
async function sendAgentMessage(runtime: MsgAgentRuntime, recipientId: string, payload: any) {
const message: AgentMessage = {
id: crypto.randomUUID(), senderId: runtime.getAgentId(),
recipientId, type: 'REQUEST', payload,
timestamp: Date.now(), priority: 0,
};
message.signature = await runtime.signMessage(JSON.stringify(message));
await runtime.sendMessage({ recipient: recipientId, content: JSON.stringify(message) });
return message;
}
async function listenForMessages(runtime: MsgAgentRuntime, handler: (msg: AgentMessage) => Promise<any>) {
runtime.on('message', async (rawMessage: string) => {
const message: AgentMessage = JSON.parse(rawMessage);
const agentId = runtime.getAgentId();
if (message.recipientId && message.recipientId !== agentId) return;
const isValid = await runtime.verifySignature(
JSON.stringify({ ...message, signature: undefined }),
message.signature || '', message.senderId,
);
if (!isValid) { console.warn('Invalid signature'); return; }
const response = await handler(message);
if (response && message.type === 'REQUEST') {
response.id = crypto.randomUUID();
response.senderId = agentId;
response.recipientId = message.senderId;
response.type = 'RESPONSE';
response.timestamp = Date.now();
await runtime.sendMessage({ recipient: message.senderId, content: JSON.stringify(response) });
}
});
}
4.7 Agent 部署
# 编译
pnpm build
# Docker 构建
cat > Dockerfile << 'DOCKER_EOF'
FROM node:22-alpine
WORKDIR /app
COPY package.json pnpm-lock.yaml ./
RUN pnpm install --prod
COPY dist/ ./dist/
ENV NODE_ENV=production
CMD ["node", "dist/index.js"]
DOCKER_EOF
docker build -t msg-agent:v1 .
# 链上注册 Agent
msgd tx agent register \
--name "My Translation Agent" \
--description "AI-powered translation agent" \
--capabilities text-translation-v1,language-detection-v1 \
--endpoint "https://my-agent.example.com/webhook" \
--from dev-user \
--chain-id msg-chain-1 \
--gas auto \
-y
# 查询 Agent
msgd query agent list
5. SDK 与工具贡献
5.1 SDK 生态架构
SDK & Tools
├── msgjs — JavaScript/TypeScript SDK
├── msgpy — Python SDK
├── msgcli — 命令行工具 (Go)
├── msg-rust-sdk — Rust SDK
└── contrib/ — 社区维护的 SDK
5.2 msgjs SDK 贡献
// packages/msgjs/src/core/msgClient.ts
import { SigningStargateClient } from '@cosmjs/stargate';
import { DirectSecp256k1HdWallet, OfflineDirectSigner } from '@cosmjs/proto-signing';
import { Coin, StdFee, calculateFee, GasPrice } from '@cosmjs/stargate';
import { QueryClient } from '../query/queryClient';
import { EventEmitter } from 'events';
export interface MsgClientOptions {
rpcEndpoint: string;
chainId: string;
gasPrice?: string;
gasAdjustment?: number;
}
export class MsgClient extends EventEmitter {
private stargateClient: SigningStargateClient | null = null;
private queryClient: QueryClient | null = null;
private wallet: OfflineDirectSigner | null = null;
private options: Required<MsgClientOptions>;
private gasPrice: GasPrice;
constructor(options: MsgClientOptions) {
super();
this.options = {
rpcEndpoint: options.rpcEndpoint,
chainId: options.chainId,
gasPrice: options.gasPrice || '1000000000umsg',
gasAdjustment: options.gasAdjustment || 1.3,
};
this.gasPrice = GasPrice.fromString(this.options.gasPrice);
}
static async connect(options: MsgClientOptions): Promise<MsgClient> {
const client = new MsgClient(options);
await client.initialize();
return client;
}
private async initialize(): Promise<void> {
this.queryClient = await QueryClient.connect(this.options.rpcEndpoint);
this.emit('connected', { endpoint: this.options.rpcEndpoint, chainId: this.options.chainId });
}
async loginWithMnemonic(mnemonic: string): Promise<void> {
this.wallet = await DirectSecp256k1HdWallet.fromMnemonic(mnemonic, { prefix: 'msg' });
this.stargateClient = await SigningStargateClient.connectWithSigner(
this.options.rpcEndpoint, this.wallet,
);
const [account] = await this.wallet.getAccounts();
this.emit('login', { address: account.address });
}
async getBalance(address: string, denom: string = 'umsg'): Promise<Coin | null> {
return this.queryClient!.getBalance(address, denom);
}
async sendTokens(recipient: string, amount: string, denom: string = 'umsg'): Promise<string> {
this.ensureSignedClient();
const [account] = await this.wallet!.getAccounts();
const fee = this.calculateFee(200000);
const result = await this.stargateClient!.sendTokens(
account.address, recipient, [{ denom, amount }], fee,
);
this.emit('transaction', { hash: result.transactionHash, type: 'send' });
return result.transactionHash;
}
async registerAgent(agent: { name: string; description: string; capabilities: string[]; endpoint: string }): Promise<string> {
this.ensureSignedClient();
const [account] = await this.wallet!.getAccounts();
const fee = this.calculateFee(500000);
const result = await this.stargateClient!.signAndBroadcast(
account.address,
[{ typeUrl: '/msgchain.agent.MsgRegisterAgent', value: { sender: account.address, ...agent } }],
fee,
);
return result.transactionHash;
}
async sendMessage(recipient: string, content: string, priority?: number): Promise<string> {
this.ensureSignedClient();
const [account] = await this.wallet!.getAccounts();
const fee = this.calculateFee(300000);
const result = await this.stargateClient!.signAndBroadcast(
account.address,
[{ typeUrl: '/msgchain.message.MsgSendMessage', value: { sender: account.address, recipient, content, priority: priority || 0 } }],
fee,
);
return result.transactionHash;
}
private calculateFee(gasLimit: number): StdFee {
return calculateFee(Math.floor(gasLimit * this.options.gasAdjustment), this.gasPrice);
}
private ensureSignedClient(): void {
if (!this.stargateClient || !this.wallet) {
throw new Error('Not authenticated. Call loginWithMnemonic() first.');
}
}
async disconnect(): Promise<void> {
if (this.stargateClient) this.stargateClient.disconnect();
this.emit('disconnected');
}
}
5.3 SDK 贡献标准
// 命名规范:
// - 类: PascalCase (MsgClient)
// - 函数/变量: camelCase (sendTokens)
// - 接口: PascalCase (AgentInfo)
// - 常量: UPPER_SNAKE_CASE (MAX_MESSAGE_SIZE)
export class MsgClientError extends Error {
constructor(
message: string,
public readonly code: ErrorCode,
public readonly details?: Record<string, unknown>,
) {
super(message);
this.name = 'MsgClientError';
}
}
export enum ErrorCode {
CONNECTION_FAILED = 'CONNECTION_FAILED',
NOT_AUTHENTICATED = 'NOT_AUTHENTICATED',
INVALID_ADDRESS = 'INVALID_ADDRESS',
INSUFFICIENT_FUNDS = 'INSUFFICIENT_FUNDS',
TRANSACTION_FAILED = 'TRANSACTION_FAILED',
TIMEOUT = 'TIMEOUT',
RATE_LIMITED = 'RATE_LIMITED',
UNKNOWN = 'UNKNOWN',
}
5.4 SDK 测试
import { MsgClient } from '../src/core/msgClient';
describe('MsgClient', () => {
const rpc = 'https://rpc.msgchain.org';
const chainId = 'msg-chain-1';
const mnemonic = '[未公开凭证]';
it('should connect', async () => {
const client = await MsgClient.connect({ rpcEndpoint: rpc, chainId });
expect(client).toBeInstanceOf(MsgClient);
await client.disconnect();
});
it('should throw on invalid endpoint', async () => {
await expect(MsgClient.connect({ rpcEndpoint: 'https://invalid:26657', chainId })).rejects.toThrow();
});
it('should login and emit event', async () => {
const client = await MsgClient.connect({ rpcEndpoint: rpc, chainId });
await client.loginWithMnemonic(mnemonic);
const handler = jest.fn();
client.on('login', handler);
expect(handler.mock.calls[0][0].address).toMatch(/^msg1/);
await client.disconnect();
});
it('should get balance', async () => {
const client = await MsgClient.connect({ rpcEndpoint: rpc, chainId });
const balance = await client.getBalance('msg1qypqqqqqqqqqqqqqqqqqqqqqqqqqqqqqvq9l8l');
expect(balance).toBeDefined();
expect(balance!.denom).toBe('umsg');
await client.disconnect();
});
it('should throw when sending without auth', async () => {
const client = await MsgClient.connect({ rpcEndpoint: rpc, chainId });
await expect(client.sendMessage('msg1recipient', 'Hello')).rejects.toThrow();
await client.disconnect();
});
});
5.5 msgcli 工具
# 安装
go install github.com/msgchain/msgcli@latest
# 使用
msgcli config set node https://rpc.msgchain.org
msgcli config set chain-id msg-chain-1
msgcli keys add my-wallet
msgcli query balance msg1...
msgcli tx send msg1... 1000000umsg --from my-wallet
msgcli agent register --name "My Agent" --capabilities translation-v1 --from my-wallet
msgcli message send msg1... "Hello" --priority 5 --from my-wallet
5.6 msgpy SDK
# sdk/msgpy/msgpy/client.py
from typing import Optional, List, Dict
class MsgClient:
def __init__(self, rpc_endpoint: str, chain_id: str = "msg-chain-1"):
self.rpc_endpoint = rpc_endpoint
self.chain_id = chain_id
def get_balance(self, address: str, denom: str = "umsg") -> Optional[Dict[str, str]]:
"""获取账户余额"""
pass
def send_tokens(self, recipient: str, amount: str, denom: str = "umsg") -> str:
"""发送代币"""
pass
def register_agent(self, name: str, description: str, capabilities: List[str]) -> str:
"""注册 Agent"""
pass
def send_message(self, recipient: str, content: str, priority: int = 0) -> str:
"""发送消息"""
pass
5.7 SDK 文档要求
MODULE_NAME/
├── README.md # 模块说明与快速开始
├── CONTRIBUTING.md # 模块特定贡献指南
├── CHANGELOG.md # 变更日志
├── API.md # API 参考文档
├── examples/
│ ├── basic-usage.ts # 基础用法示例
│ └── advanced-usage.ts # 高级用法示例
└── docs/
├── getting-started.md # 入门指南
└── troubleshooting.md # 常见问题
6. 文档贡献
6.1 文档体系
docs/
├── user/
│ ├── getting-started.md
│ ├── wallet-setup.md
│ └── staking-guide.md
├── developer/
│ ├── architecture.md
│ ├── smart-contracts/
│ ├── agent-sdk/
│ ├── api-reference/
│ └── tutorials/
├── validator/
├── governance/
├── translations/
│ ├── zh-CN/
│ ├── ja-JP/
│ └── ko-KR/
└── contribution/
└── style-guide.md
6.2 文档风格指南
语言风格:
- 简洁明了,使用短句和简单词汇
- 主动语态,"执行以下命令" 而非 "以下命令应该被执行"
- 一致性,统一术语翻译避免混用
- 代码优先,多用实际代码示例
- 用户导向,从用户视角出发
术语翻译对照表:
| English | 中文 |
|---|---|
| Transaction | 交易 |
| Validator | 验证人 |
| Delegate | 委托 |
| Staking | 质押 |
| Governance | 治理 |
| Proposal | 提案 |
| Smart Contract | 智能合约 |
| Agent | Agent |
| Capability | 能力 |
| Wallet | 钱包 |
| Mainnet | 主网 |
| Testnet | 测试网 |
Markdown 格式:
- 标题层级最多四级
- 代码块必须指定语言标识
- 同级列表使用相同标记
- 使用相对路径链接本站文档
6.3 文档生成流水线
git clone https://github.com/msgchain/docs.git
cd docs
pnpm install
pnpm run start # 本地预览
pnpm run build # 构建
pnpm run deploy # 部署
# 自动生成 API 文档
protoc --doc_out=./docs/developer/api-reference \
--doc_opt=markdown,msgchain-api.md \
-I ./proto ./proto/msgchain/*.proto
pnpm exec typedoc --out docs/developer/api-reference/msgjs packages/msgjs/src/index.ts
cargo doc --no-deps --target-dir ./docs/developer/api-reference/msg-contracts
6.4 文档贡献步骤
git clone https://github.com/YOUR_USERNAME/docs.git
cd docs
git remote add upstream https://github.com/msgchain/docs.git
git fetch upstream
git checkout main
git rebase upstream/main
git checkout -b docs/zh-agent-guide
# 编写文档后检查
pnpm run check-links
pnpm run spellcheck
git add .
git commit -m "docs(zh): add Chinese translation of Agent guide"
git push -u origin docs/zh-agent-guide
6.5 翻译贡献指南
翻译流程:
- 选择待翻译文件
- Fork 文档仓库
- 在 translations/{lang}/ 目录下创建对应路径
- 提交 PR,等待两位翻译审核者批准
注意事项:
- 技术术语严格遵守对照表
- 代码注释需要翻译,代码本身保持不变
- 对外链接保持不变,对内链接改为目标语言路径
- Front Matter 只翻译 title 和 description
6.6 文档审查检查清单
结构检查:
[ ] 标题层级正确
[ ] 包含目录和前置条件
内容检查:
[ ] 技术准确性
[ ] 完整性
[ ] 术语一致性
格式检查:
[ ] 代码块指定了语言
[ ] 链接可访问
[ ] 表格格式正确
翻译检查:
[ ] 术语符合对照表
[ ] 代码注释已翻译
[ ] 链接路径正确
7. 治理参与
7.1 治理模型
MSG Chain 使用链上治理系统。
MSG Chain Governance
├── 文本提案 (Text Proposal)
├── 参数变更提案 (Parameter Change)
├── 社区基金支出提案 (Community Spend)
├── 软件升级提案 (Software Upgrade)
└── 取消升级提案 (Cancel Upgrade)
7.2 查看治理状态
# 查看治理参数
msgd query gov params
# 查看当前活跃提案
msgd query gov proposals --status voting_period
# 查看特定提案
msgd query gov proposal 1 --output json | jq
# 查看提案投票情况
msgd query gov votes 1
# 查看我的投票权
msgd query staking delegations $(msgd keys show my-wallet -a)
7.3 提交治理提案
# 提交文本提案
msgd tx gov submit-proposal \
--title "Add Message Priority Feature" \
--description "This proposal adds message priority support." \
--type Text \
--deposit 1000000umsg \
--from my-wallet \
--chain-id msg-chain-1 \
--gas auto \
--gas-prices 1000000000umsg \
-y
# 查询提案状态
msgd query gov proposal 42 | jq '.proposal.status'
# 为提案添加存款
msgd tx gov deposit 42 500000umsg \
--from my-wallet \
--chain-id msg-chain-1 \
--gas auto \
-y
7.4 参数变更提案
msgd tx gov submit-proposal param-change \
--title "Update Message Parameters" \
--description "Increase max message size to 128KB" \
--changes '{"subspace":"message","key":"MaxMessageSize","value":"131072"}' \
--deposit 1000000umsg \
--from my-wallet \
--chain-id msg-chain-1 \
--gas auto \
-y
# 验证参数
msgd query message params
7.5 社区基金支出提案
msgd tx gov submit-proposal community-pool-spend \
--title "Community Grant: Explorer" \
--description "Request 50,000 MSG for Explorer development" \
--recipient msg1recipientaddress \
--amount 50000000000umsg \
--deposit 1000000umsg \
--from my-wallet \
--chain-id msg-chain-1 \
--gas auto \
-y
# 查看社区池余额
msgd query distribution community-pool
7.6 软件升级提案
msgd tx gov submit-proposal software-upgrade v2.0.0 \
--title "MSG Chain v2.0.0 Upgrade" \
--description "Upgrade to version 2.0.0" \
--upgrade-height 10000000 \
--upgrade-info '{"binaries":{"linux/amd64":"https://..."}}' \
--from my-wallet \
--chain-id msg-chain-1 \
--gas auto \
-y
# 取消升级
msgd tx gov submit-proposal cancel-software-upgrade \
--title "Cancel Upgrade" \
--from my-wallet --chain-id msg-chain-1 -y
7.7 投票指南
# 投票选项: yes, no, abstain, no_with_veto
msgd tx gov vote 42 yes --from my-wallet --chain-id msg-chain-1 --gas auto -y
msgd tx gov vote 42 no --from my-wallet --chain-id msg-chain-1 --gas auto -y
msgd tx gov vote 42 abstain --from my-wallet --chain-id msg-chain-1 --gas auto -y
msgd tx gov vote 42 no_with_veto --from my-wallet --chain-id msg-chain-1 --gas auto -y
# 委托投票权
msgd tx staking delegate msgvaloper1validatoraddress 1000000umsg \
--from my-wallet --chain-id msg-chain-1 -y
# 查看投票权重
msgd query gov tally 42 --output json | jq
7.8 治理参与清单
投票前:
[ ] 阅读完整提案描述
[ ] 查看提案讨论
[ ] 理解技术变更
[ ] 评估潜在影响
[ ] 做出知情决策
提案者:
[ ] 明确的标题和描述
[ ] 技术细节充分
[ ] 实施时间线合理
[ ] 社区已提前讨论
[ ] 格式正确
7.9 链下治理讨论
- 论坛: https://forum.msgchain.org
- Discord: https://discord.gg/msgchain (#governance)
- 每周治理会议: 每周四 14:00 UTC
- 提案流程: 论坛讨论 → 草案 → 提交 → 存款期 → 投票期 (7天)
8. 社区运营
8.1 社区角色体系
MSG Chain Community Roles
├── 贡献者 (Contributor)
├── 活跃贡献者 (Active Contributor)
├── 社区大使 (Ambassador)
├── 版主 (Moderator)
├── 技术审核 (Technical Reviewer)
├── 维护者 (Maintainer)
└── 核心贡献者 (Core Contributor)
8.2 社区大使计划
大使职责: 组织活动、创建内容、管理本地社区、翻译文档、参加会议
申请条件: 深入了解 MSG Chain、良好沟通能力、每月 10+ 小时投入
大使权益: 每月 500-5000 MSG、专属频道、优先赞助、核心团队沟通、年度峰会
申请流程: 填写表单 → 面试 → 试用期 (1个月) → 正式任命
8.3 活动组织指南
| 类型 | 规模 | 预算 |
|---|---|---|
| 线上分享 | 50-200人 | 0-500 MSG |
| Workshop | 20-50人 | 500-2000 MSG |
| Meetup | 50-200人 | 2000-10000 MSG |
| 黑客松 | 50-500人 | 10000-50000 MSG |
活动前 4-6 周: 确定主题、申请赞助、确定场地、邀请嘉宾、创建活动页面
活动前 1-2 周: 确认演讲者、测试平台、发送提醒、准备签到
活动当天: 布置场地、签到管理、现场协调、摄影记录
活动后: 发布总结、上传录像、发送感谢、提交报告
8.4 Bug 赏金计划
范围:
- 智能合约: 重入攻击、权限提升、资金盗取、逻辑错误
- 核心链: 共识漏洞、交易验证绕过、P2P 攻击、IBC 漏洞
- SDK: 私钥泄露、交易签名伪造、RPC 漏洞
奖励标准:
| 严重程度 | 奖励 (MSG) |
|---|---|
| 危急 | 50,000-200,000 |
| 高危 | 10,000-50,000 |
| 中危 | 1,000-10,000 |
| 低危 | 100-1,000 |
提交流程: 加密发送至 security@msgchain.org,包含描述、复现步骤、影响评估
8.5 Bug 报告脚本
#!/bin/bash
# scripts/submit-bug-report.sh
set -euo pipefail
BUG_FILE="${1:-}"
[ -z "$BUG_FILE" ] && { echo "用法: $0 <bug_file>"; exit 1; }
[ ! -f "$BUG_FILE" ] && { echo "文件不存在"; exit 1; }
gpg --encrypt --recipient security@msgchain.org --output "${BUG_FILE}.gpg" "$BUG_FILE"
HASH=$(sha256sum "$BUG_FILE" | cut -d' ' -f1)
msgd tx bank send $(msgd keys show reporter -a) \
msg1burnxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 1umsg \
--memo "BUG:0x${HASH}" \
--chain-id msg-chain-1 --gas auto --gas-prices 1000000000umsg -y
echo "发送 ${BUG_FILE}.gpg 至 security@msgchain.org"
8.6 社区贡献统计
interface ContributorStats {
address: string;
contributions: {
code: number; reviews: number; docs: number;
translation: number; issues: number; events: number;
};
score: number;
level: 'bronze' | 'silver' | 'gold' | 'platinum' | 'diamond';
}
async function calculateScore(stats: ContributorStats['contributions']): Promise<number> {
const w = { code: 10, reviews: 5, docs: 3, translation: 2, issues: 1, events: 4 };
return stats.code * w.code + stats.reviews * w.reviews + stats.docs * w.docs
+ stats.translation * w.translation + stats.issues * w.issues + stats.events * w.events;
}
function getLevel(score: number): string {
if (score >= 10000) return 'diamond';
if (score >= 5000) return 'platinum';
if (score >= 2000) return 'gold';
if (score >= 500) return 'silver';
return 'bronze';
}
9. 贡献者奖励
9.1 奖励体系
Reward System
├── 即时奖励: Bug 赏金、PR 合并奖励、文档翻译奖励
├── 季度奖励: 活跃贡献者、最佳文档、最佳新人
└── 年度奖励: 年度贡献者大奖、核心贡献者任命
9.2 贡献评分模型
interface ContributionEvent {
type: ContributionType;
timestamp: Date;
contributor: string;
metadata: Record<string, unknown>;
}
type ContributionType =
| 'CODE_COMMIT' | 'CODE_REVIEW' | 'BUG_REPORT' | 'BUG_FIX'
| 'DOCS_UPDATE' | 'TRANSLATION' | 'FEATURE_REQUEST'
| 'COMMUNITY_HELP' | 'EVENT_ORGANIZE' | 'CONTENT_CREATE';
const SCORE_CONFIGS: Record<ContributionType, { base: number; mult: number[] }> = {
CODE_COMMIT: { base: 100, mult: [3, 2, 1.2, 1] },
CODE_REVIEW: { base: 50, mult: [2, 2, 1, 1] },
BUG_REPORT: { base: 30, mult: [1, 3, 1, 1.2] },
BUG_FIX: { base: 80, mult: [3, 3, 1.2, 1] },
DOCS_UPDATE: { base: 20, mult: [1, 1, 1, 1] },
TRANSLATION: { base: 15, mult: [1, 1, 1.5, 0.8] },
FEATURE_REQUEST:{ base: 10, mult: [1, 1.5, 0.8, 0.8] },
COMMUNITY_HELP: { base: 5, mult: [1, 1, 1, 1] },
EVENT_ORGANIZE: { base: 200, mult: [3, 2.5, 1.2, 1] },
CONTENT_CREATE: { base: 40, mult: [2, 2, 1.5, 0.9] },
};
function calculateTotalScore(events: ContributionEvent[]): number {
return events.reduce((total, event) => {
const c = SCORE_CONFIGS[event.type];
return c ? total + Math.round(c.base * c.mult[0] * c.mult[1] * c.mult[2] * c.mult[3] * 100) / 100 : total;
}, 0);
}
function getRewardTier(score: number): { tier: string; multiplier: number; bonus: number } {
if (score >= 50000) return { tier: 'diamond', multiplier: 3.0, bonus: 50000 };
if (score >= 20000) return { tier: 'platinum', multiplier: 2.5, bonus: 20000 };
if (score >= 10000) return { tier: 'gold', multiplier: 2.0, bonus: 10000 };
if (score >= 5000) return { tier: 'silver', multiplier: 1.5, bonus: 5000 };
return { tier: 'bronze', multiplier: 1.0, bonus: 0 };
}
9.3 代币奖励发放
#!/bin/bash
# scripts/distribute-rewards.sh
set -euo pipefail
REWARD_FILE="${1:-rewards.json}"
[ ! -f "$REWARD_FILE" ] && { echo "提供奖励 JSON 文件"; exit 1; }
for row in $(cat "$REWARD_FILE" | jq -c '.[]'); do
addr=$(echo "$row" | jq -r '.address')
amt=$(echo "$row" | jq -r '.amount')
reason=$(echo "$row" | jq -r '.reason')
msgd tx bank send treasury-wallet "$addr" "$amt" \
--chain-id msg-chain-1 --note "Reward: $reason" \
--gas auto --gas-prices 1000000000umsg -y
echo "发送 $amt 到 $addr"
done
echo "奖励发放完成!"
9.4 贡献者等级权益
| 等级 | 最低评分 | 代币乘数 | 季度奖励 | 特殊权益 |
|---|---|---|---|---|
| Bronze | 0 | 1.0x | 0 | Discord 身份组 |
| Silver | 5,000 | 1.5x | 5,000 MSG | 专属频道 |
| Gold | 10,000 | 2.0x | 10,000 MSG | 优先技术支持 |
| Platinum | 20,000 | 2.5x | 20,000 MSG | 治理双倍权重 |
| Diamond | 50,000 | 3.0x | 50,000 MSG | 核心贡献者身份 |
9.5 季度奖项
- 最佳代码贡献者: 10,000 MSG
- 最佳文档贡献者: 5,000 MSG
- 最佳社区贡献者: 5,000 MSG
- 最佳新人奖: 3,000 MSG
- 年度贡献者大奖: 50,000 MSG + NFT + 峰会赞助
9.6 奖励领取
# 查询个人评分
msgd query rewards contributor $(msgd keys show my-wallet -a)
# 查看待领取奖励
msgd query rewards pending $(msgd keys show my-wallet -a)
# 领取奖励
msgd tx rewards claim --from my-wallet --chain-id msg-chain-1 --gas auto -y
# 查看排行榜
msgd query rewards leaderboard --limit 10
9.7 奖励透明化
- 总发放: 链上可查的所有奖励历史
- 季度预算: 社区基金分配的奖励预算
- 分配公式: 个人奖励 = 季度预算 × (个人评分 / 总评分) × 等级乘数
- 审核流程: 奖励分配方案需经治理投票通过
- 申诉机制: 通过论坛或 Discord 提出申诉
10. 附录
10.1 资源链接
| 资源 | 链接 |
|---|---|
| 官方文档 | https://msgchain.org |
| GitHub | https://github.com/msgchain |
| 区块浏览器 | https://explorer.msgchain.org |
| 测试网水龙头 | https://faucet.msgchain.org |
| 论坛 | https://forum.msgchain.org |
| Discord | https://discord.gg/msgchain |
10.2 常用参数
Bech32 前缀: msg
验证人前缀: msgvaloper
链 ID: msg-chain-1
测试网链 ID: msg-chain-testnet-1
主网 RPC: https://rpc.msgchain.org
测试网 RPC: https://rpc-testnet.msgchain.org
主网 REST API: https://api.msgchain.org
主网 gRPC: msg-grpc.msgchain.org:9090
10.3 版本兼容性
| 组件 | 版本 | 要求 |
|---|---|---|
| msgd | v1.0.0 | Go 1.22 |
| msgjs | v1.0.0 | Node.js 20+ |
| msgpy | v1.0.0 | Python 3.11+ |
| msgcli | v1.0.0 | Go 1.22 |
| CosmWasm | v1.5.0 | Rust 1.75+ |
10.4 常见问题
Q: 如何获得测试网代币?
A: 访问 https://faucet.msgchain.org,输入你的 msg 地址。
Q: PR 多久被审核?
A: 通常在 48 小时内首次反馈。
Q: 贡献评分多久更新?
A: 每周自动更新一次。
Q: 如何成为核心贡献者?
A: 在两个以上领域持续贡献,评分超过 50,000,由核心团队邀请。
本指南由 MSG Chain 开发者生态团队维护。欢迎通过 GitHub PR 改进。
