dApp Docs/MSG Chain 开发者生态贡献指南
Development reference. Not independently verified for production.

MSG Chain 开发者生态贡献指南

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

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


目录

  1. 概述
  2. 开发环境设置
  3. CosmWasm 合约贡献
  4. AI Agent 生态贡献
  5. SDK 与工具贡献
  6. 文档贡献
  7. 治理参与
  8. 社区运营
  9. 贡献者奖励

1. 概述

1.1 为什么贡献 MSG Chain 生态

MSG Chain 是一个基于 Cosmos SDK 构建的高性能 Layer 1 区块链,专注于去中心化消息传递与 AI Agent 互操作。作为开发者生态的贡献者,你将:

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 文档风格指南

语言风格:

  1. 简洁明了,使用短句和简单词汇
  2. 主动语态,"执行以下命令" 而非 "以下命令应该被执行"
  3. 一致性,统一术语翻译避免混用
  4. 代码优先,多用实际代码示例
  5. 用户导向,从用户视角出发

术语翻译对照表:

English 中文
Transaction 交易
Validator 验证人
Delegate 委托
Staking 质押
Governance 治理
Proposal 提案
Smart Contract 智能合约
Agent Agent
Capability 能力
Wallet 钱包
Mainnet 主网
Testnet 测试网

Markdown 格式:

  1. 标题层级最多四级
  2. 代码块必须指定语言标识
  3. 同级列表使用相同标记
  4. 使用相对路径链接本站文档

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 翻译贡献指南

翻译流程:

  1. 选择待翻译文件
  2. Fork 文档仓库
  3. 在 translations/{lang}/ 目录下创建对应路径
  4. 提交 PR,等待两位翻译审核者批准

注意事项:

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 链下治理讨论


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 赏金计划

范围:

奖励标准:

严重程度 奖励 (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 季度奖项

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 奖励透明化


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 改进。