dApp Docs/AI Agent 网络配置预设与沙箱开发策略
Development reference. Not independently verified for production.

AI Agent 网络配置预设与沙箱开发策略

本文档基于 MSG Chain chain_config/ 真实配置数据编写。数据来源:
msgchain.org/whitepaper/chain_config/index.json
msgchain.org/whitepaper/chain_config/network_presets.json
msgchain.org/whitepaper/chain_config/developer_sandbox_strategy.json
msgchain.org/whitepaper/developer_capability_matrix.json
msgchain.org/whitepaper/developer_entry.json


目录

  1. 概述
  2. 链配置与网络预设
  3. 开发者沙箱策略
  4. 网络配置生产指引
  5. AI Agent 网络配置最佳实践
  6. 边界声明
  7. 附录:完整配置文件参考

1. 概述

1.1 什么是 chain_config

chain_config/ 是 MSG Chain 白皮书中的链配置包目录,作为机器可消费的网络元数据入口,
为 AI Agent、dApp 前端和 SDK 接入提供标准化的链配置基线。

根据 developer_entry.json 的 bootstrap 顺序,chain_config/index.json 和
chain_config/developer_sandbox_strategy.json 是合约/dApp开发者的前序引导资源,
位于 quickstart 之后、api_specs 之前。

1.2 chain_config 的文件清单

根据 chain_config/index.json,该目录包含以下文件:

文件 描述 machine_readiness
network_presets.json 链 ID、端点、钱包与货币默认值 starter_ready
keplr_chain_info.json Keplr 钱包链配置基线 starter_ready
keplr_suggest_chain.js Keplr suggestChain 辅助脚本 starter_ready
developer_sandbox_strategy.json 沙箱/水龙头策略(fail-closed) fail_closed_reference

1.3 两个配置表面的能力等级

来自 developer_capability_matrix.json 的官方能力分层:

chain_config_pack(链配置与钱包注入包)

字段 值
machine_readiness starter_ready
production_supported true
machine_safe_for_codegen true
write_path_ready false
example_available true
best_for 前端链配置生成、Keplr suggestChain、CosmJS/钱包接入初始化

public_sandbox_strategy(Public Devnet/Testnet/Faucet 策略)

字段 值
machine_readiness fail_closed_reference
production_supported false
machine_safe_for_codegen true
write_path_ready false
example_available true
blocking_gaps 当前选择的是 Option B fail-closed 策略;signed disabled manifest、public faucet absence proof 与 final signatures 仍未关闭

1.4 核心区别:配置基线 vs 沙箱可用性

1.5 为什么 AI Agent 需要理解这两个层级

AI Agent 在接入 MSG Chain 时,必须区分:

  1. 哪些配置是可靠的基线(chain_config_pack → 可用)
  2. 哪些环境可以实际发送交易(public_sandbox_strategy → 不可用)
  3. 本地开发是当前唯一的主路径

1.6 配置文件的元数据档案

所有 chain_config/ 文件均标记为 metadata_profile: "public_stable",
表示这些配置已经过白皮书管线的稳定发布,AI Agent 可以依赖其结构定义,
但不能替代生产环境复核。

1.7 从 developer_entry.json 看引导顺序

"recommended_contract_bootstrap_order": [
  "product_delivery_entry.json",
  "developer_capability_matrix.json",
  "quickstart/contract_and_dapp_minimal.json",
  "chain_config/index.json",
  "chain_config/developer_sandbox_strategy.json",
  ...
]

chain_config/index.json 作为配置入口,developer_sandbox_strategy.json 作为
沙箱边界声明,两者构成了开发者在了解技术细节之前的"环境认知基座"。

1.8 网络预设的架构角色

network_presets.json 是 chain_config 的核心,它提供了:

AI Agent 应当使用 network_presets 作为配置生成的输入,但必须在运行时
根据环境变量或用户指定的目标网络进行切换。

1.9 关于 keplr 配置文件的额外说明

keplr_chain_info.json 是 Keplr 钱包的链信息配置模板,包含 suggestChain API
所需的全部字段。keplr_suggest_chain.js 是建议的辅助脚本,可直接嵌入前端项目。

这两个文件属于 starter_ready 级别,生产支持为 true,适合前端项目直接使用。

1.10 本文档的目标读者


2. 链配置与网络预设

2.1 network_presets.json 完整结构

network_presets.json 是 MSG Chain 的机器可消费网络配置预设,其顶层结构如下:

{
  "schema_version": "v1",
  "generated_by": "msg_whitepaper_pipeline_v1",
  "chain_id": "msg-chain-1",
  "numeric_chain_id": 1,
  "chain_name": "MSG Chain",
  "rpc": "https://rpc.msgchain.org",
  "rest": "https://api.msgchain.org",
  "wallet": {
    "bech32": { ... },
    "bip44": { ... },
    "currencies": [ ... ],
    "fee_currencies": [ ... ],
    "stake_currency": { ... },
    "features": [ "cosmwasm" ]
  },
  "source_files": [ ... ],
  "boundary": [ ... ],
  "metadata_profile": "public_stable"
}

2.2 链标识

参数 值
chain_id msg-chain-1
numeric_chain_id 1
chain_name MSG Chain
地址前缀 (bech32) msg
BIP44 coin type 118(Cosmos 标准)

2.3 Bech32 地址前缀配置

MSG Chain 使用标准 Cosmos bech32 地址体系:

角色 前缀
账户地址 msg
账户公钥 msgpub
验证者地址 msgvaloper
验证者公钥 msgvaloperpub
共识地址 msgvalcons
共识公钥 msgvalconspub

2.4 主网端点

服务 URL
RPC https://rpc.msgchain.org
REST API https://api.msgchain.org

当前公开的 network_presets 仅包含 mainnet 端点。testnet 和 devnet 的域名已预留
但处于 reserved_fail_closed 状态,不可用。

2.5 货币与通证配置

原生通证:MSG

字段 值
coinDenom MSG
coinMinimalDenom umsg
coinDecimals 18
coinGeckoId msg-chain

Gas 费用阶梯

等级 Gas 价格
flat 1,000,000,000 attoMSG/gas

质押货币

与原生通证一致,使用 umsg(18 位小数)。

链特性

"features": ["cosmwasm"]

MSG Chain 原生支持 CosmWasm 智能合约,所有钱包配置均已包含该特性标志。

2.6 Python 配置获取示例

import requests
import json
from typing import TypedDict, Optional

NETWORK_PRESETS_URL = \
    "https://msgchain.org/whitepaper/chain_config/network_presets.json"

class ChainConfig(TypedDict):
    chain_id: str
    bech32_prefix: str
    rpc_url: str
    rest_url: str
    gas_price_step: dict

async def get_network_config(env: str = "mainnet") -> ChainConfig:
    """从 network_presets 获取网络配置"""
    resp = requests.get(NETWORK_PRESETS_URL)
    presets = resp.json()

    if env == "testnet":
        # 测试网域名已预留但处于 fail-closed 状态
        raise RuntimeError(
            "testnet endpoints are reserved_fail_closed. "
            "Use 'local' for development."
        )

    # 当前 preset 仅包含主网配置
    # 本地开发需手动指定 localhost 端点
    rpc = presets["rpc"] if env == "mainnet" else "http://localhost:26657"
    rest = presets["rest"] if env == "mainnet" else "http://localhost:1317"

    return {
        "chain_id": presets["chain_id"],
        "bech32_prefix": presets["wallet"]["bech32"]["bech32PrefixAccAddr"],
        "rpc_url": rpc,
        "rest_url": rest,
        "gas_price_step": presets["wallet"]["fee_currencies"][0]["gasPriceStep"],
    }

async def main():
    config = await get_network_config("mainnet")
    print(f"Connected to {config['chain_id']}")
    print(f"RPC: {config['rpc_url']}")
    print(f"REST: {config['rest_url']}")
    print(f"Prefix: {config['bech32_prefix']}")

if __name__ == "__main__":
    import asyncio
    asyncio.run(main())

2.7 JavaScript / TypeScript 配置获取示例

interface ChainConfig {
  chainId: string;
  chainName: string;
  rpc: string;
  rest: string;
  bech32Prefix: string;
  gasPriceStep: {
    low: number;
    average: number;
    high: number;
  };
}

const NETWORK_PRESETS_URL =
  "https://msgchain.org/whitepaper/chain_config/network_presets.json";

async function fetchChainConfig(): Promise<ChainConfig> {
  const resp = await fetch(NETWORK_PRESETS_URL);
  const presets = await resp.json();

  return {
    chainId: presets.chain_id,
    chainName: presets.chain_name,
    rpc: presets.rpc,
    rest: presets.rest,
    bech32Prefix: presets.wallet.bech32.bech32PrefixAccAddr,
    gasPriceStep: presets.wallet.fee_currencies[0].gasPriceStep,
  };
}

2.8 Keplr 钱包链信息配置(keplr_chain_info.json)

Keplr 链信息配置模板,可直接用于 window.keplr.experimentalSuggestChain():

{
  "$schema": "https://keplr.app/schemas/chain-info.json",
  "chainId": "msg-chain-1",
  "chainName": "MSG Chain",
  "chainSymbolImageUrl": "https://msgchain.org/logo.png",
  "rpc": "https://rpc.msgchain.org",
  "rest": "https://api.msgchain.org",
  "nodeProvider": {
    "name": "MSG Chain Foundation",
    "email": "support@msgchain.org",
    "website": "https://msgchain.org"
  },
  "bip44": {
    "coinType": 118
  },
  "bech32Config": {
    "bech32PrefixAccAddr": "msg",
    "bech32PrefixAccPub": "msgpub",
    "bech32PrefixValAddr": "msgvaloper",
    "bech32PrefixValPub": "msgvaloperpub",
    "bech32PrefixConsAddr": "msgvalcons",
    "bech32PrefixConsPub": "msgvalconspub"
  },
  "currencies": [
    {
      "coinDenom": "MSG",
      "coinMinimalDenom": "umsg",
      "coinDecimals": 6,
      "coinGeckoId": "msg-chain"
    }
  ],
  "feeCurrencies": [
    {
      "coinDenom": "MSG",
      "coinMinimalDenom": "umsg",
      "coinDecimals": 6,
      "coinGeckoId": "msg-chain",
      "gasPriceStep": {
        "low": 1000000000,
        "average": 1000000000,
        "high": 1000000000
      }
    }
  ],
  "stakeCurrency": {
    "coinDenom": "MSG",
    "coinMinimalDenom": "umsg",
    "coinDecimals": 6,
    "coinGeckoId": "msg-chain"
  },
  "features": ["cosmwasm"]
}

2.9 Keplr suggestChain 辅助脚本

keplr_suggest_chain.js 提供了一个可直接嵌入前端项目的辅助模块:

export async function suggestMSGChain() {
  if (!window.keplr) {
    throw new Error("Keplr extension not found");
  }
  try {
    await window.keplr.experimentalSuggestChain(MSG_CHAIN_INFO);
    console.log("MSG Chain added to Keplr successfully");
  } catch (error) {
    console.error("Failed to add MSG Chain to Keplr:", error);
    throw error;
  }
}

export function isKeplrAvailable() {
  return typeof window !== "undefined" && !!window.keplr;
}

export function getMSGChainConfig() {
  return MSG_CHAIN_INFO;
}

2.10 CosmJS 接入初始化

import { SigningStargateClient } from "@cosmjs/stargate";
import { GasPrice } from "@cosmjs/stargate";
import { getMSGChainConfig } from "./keplr_suggest_chain";

async function initCosmJSClient() {
  const config = getMSGChainConfig();
  const gasPrice = GasPrice.fromString("1000000000attoMSG");

  const client = await SigningStargateClient.connectWithSigner(
    config.rpc,
    window.getOfflineSigner?.(config.chainId),
    { gasPrice }
  );

  return client;
}

2.11 通过注册中心验证链配置

除了直接使用 network_presets,AI Agent 还可以通过 MSG Chain 的
Genesis Registry v1 验证当前链状态:

# 查询链 ID
curl -s https://rpc.msgchain.org/status | jq '.result.node_info.network'

# 查询节点状态
curl -s https://api.msgchain.org/cosmos/base/tendermint/v1beta1/node_info

# 查询区块头确认链 ID
curl -s https://rpc.msgchain.org/block?height=1 | jq '.result.block.header.chain_id'

2.12 网络预设的源文件引用

network_presets.json 的 source_files 字段记录了配置的原始来源:

"source_files": [
  "configs/keplr_chain_info.json",
  "configs/keplr_suggest_chain.js"
]

这意味着 network_presets 是基于仓库中的配置文件生成的 AI 友好摘要,
并非独立的配置来源。如果需要最新配置,应参考仓库中的原始文件。

2.13 network_presets 的边界声明

"boundary": [
  "链配置是接入基线,不等于 live/public 端点已经被正式接受为生产网络。",
  "生产接入前仍需复核 endpoint 可用性、域名控制、证书、SLO 与发布权限。"
]

2.14 配置验证函数

def validate_chain_config(config: dict) -> list[str]:
    """
    验证链配置的完整性,返回缺失字段列表。
    """
    required = [
        "chain_id", "rpc", "rest",
        "wallet.bech32.bech32PrefixAccAddr",
        "wallet.bip44.coinType",
        "wallet.currencies[0].coinMinimalDenom",
        "wallet.fee_currencies[0].gasPriceStep",
    ]
    missing = []
    # 简化的嵌套检查
    checks = {
        "chain_id": config.get("chain_id"),
        "rpc": config.get("rpc"),
        "rest": config.get("rest"),
        "prefix": config.get("wallet", {})
                      .get("bech32", {})
                      .get("bech32PrefixAccAddr"),
        "coin_type": config.get("wallet", {})
                       .get("bip44", {})
                       .get("coinType"),
        "denom": config.get("wallet", {})
                  .get("currencies", [{}])[0]
                  .get("coinMinimalDenom"),
        "gas_steps": config.get("wallet", {})
                      .get("fee_currencies", [{}])[0]
                      .get("gasPriceStep"),
    }
    for key, value in checks.items():
        if value is None:
            missing.append(key)
    return missing

2.15 网络预设的版本控制

network_presets.json 使用 schema_version: "v1" 字段进行版本管理,
当前为 v1 版本。当链配置发生结构性变更时,schema_version 应递增。

AI Agent 应检查 schema_version 以确保使用兼容的配置解析逻辑。

2.16 生成 pipeline 与配置一致性

所有 chain_config 文件均通过 msg_whitepaper_pipeline_v1 生成。
这意味着:

AI Agent 可以利用这一一致性来自动解析和验证配置数据。

2.17 钱包配置的详细说明

WALLET_CONFIG = {
    "bech32": {
        "bech32PrefixAccAddr": "msg",
        "bech32PrefixAccPub": "msgpub",
        "bech32PrefixValAddr": "msgvaloper",
        "bech32PrefixValPub": "msgvaloperpub",
        "bech32PrefixConsAddr": "msgvalcons",
        "bech32PrefixConsPub": "msgvalconspub",
    },
    "bip44": {
        "coinType": 118,  # Cosmos 标准 HD 路径
    },
    "currencies": [{
        "coinDenom": "MSG",
        "coinMinimalDenom": "umsg",
        "coinDecimals": 6,
        "coinGeckoId": "msg-chain",
    }],
    "features": ["cosmwasm"],
}

2.18 验证者与委托相关的前缀

开发者在使用 MSG Chain 的验证者相关功能时,需要注意地址前缀的区分:

场景 地址前缀 示例地址开头
用户转账 msg msg1...
验证者注册 msgvaloper msgvaloper1...
共识签名 msgvalcons msgvalcons1...
公钥展示 msgpub msgpub1...

2.19 从网络预设到前端注入的完整链路

network_presets.json
    │
    ├── keplr_chain_info.json
    │       │
    │       └── keplr_suggest_chain.js
    │               │
    │               └── window.keplr.experimentalSuggestChain()
    │
    ├── CosmJS 客户端初始化
    │       │
    │       └── SigningStargateClient.connectWithSigner()
    │
    └── 前端组件配置注入
            │
            └── chainProvider / WalletProvider 初始化

2.20 各环境对应的 chain_id

环境 chain_id 状态
mainnet msg-chain-1 production,端点可用
testnet msg-chain-testnet-1 reserved_fail_closed,不可用
local msg-chain-local-1 本地开发,需自行启动节点

3. 开发者沙箱策略

3.1 概述:什么是 fail-closed 策略

根据 developer_sandbox_strategy.json,MSG Chain 当前选择的沙箱策略是
Option B: disabled public sandbox with explicit local development path,
即"禁用公开沙箱,明确本地开发路径"的 fail-closed 策略。

fail-closed 意味着所有公开环境(testnet、devnet、faucet)默认关闭,
除非有 signed manifest 证明它们已通过验收。

3.2 策略的完整 JSON 结构

{
  "schema_version": "v1",
  "mainnet_verdict": "No-Go",
  "mainnet_ready_claim": false,
  "selected_strategy": "option_b_disabled_public_sandbox",
  "public_sandbox_enabled": false,
  "public_faucet_enabled": false,
  "public_testnet_enabled": false,
  "signed_disabled_manifest_claim": false,
  "strategy": {
    "option": "B",
    "name": "disabled public sandbox with explicit local development path",
    "production_ready": false,
    "public_endpoint_enabled": false,
    "default_sdk_enabled": false,
    "faucet_mode": "disabled_public_faucet",
    "testnet_preset_mode": "unverified_fail_closed",
    "devnet_preset_mode": "not_public",
    "local_development_path": "examples/ai-agent-dapp-starter with explicit endpoints only",
    "required_sdk_guards": [ ... ],
    "failure_mode": "fail-closed until a signed disabled manifest ..."
  }
}

3.3 策略的核心开关

开关 值 含义
public_sandbox_enabled false 公开沙箱未开放
public_faucet_enabled false 公开水龙头未开放
public_testnet_enabled false 公开测试网未开放
signed_disabled_manifest_claim false 未签署关闭声明
mainnet_ready_claim false 未宣称主网就绪

3.4 fail_closed_reference 的含义

来自 developer_capability_matrix.json 的官方定义:

public_sandbox_strategy 的 machine_readiness 为 fail_closed_reference,
production_supported 为 false。

这个等级意味着:

3.5 策略的阻塞项(known_blockers)

当前有五个已知阻塞项阻止公开沙箱就绪:

阻塞项 缺失的证据
signed_disabled_public_sandbox_manifest signed disabled manifest + release signer key + Dilithium signature
sdk_preset_fail_closed_public_evidence SDK mainnet/testnet preset fail-closed proof + indexer proof
local_development_runbook_evidence local starter command output + endpoint override warning + no-secret scan
public_faucet_absence_boundary public faucet disabled statement + admin faucet not public proof
external_audit_and_final_signatures external audit final report + final readiness signatures + C total Go-No-Go

每个阻塞项都带有 failure_mode: "fail-closed until ..." 声明,
意味着在提供完整证据之前,该功能不可用。

3.6 五个生产要求(production_requirements)

{
  "production_requirements": [
    {
      "id": "signed_disabled_public_sandbox_manifest",
      "status": "missing_production_evidence",
      "failure_mode": "fail-closed until signed disabled public sandbox manifest is accepted"
    },
    {
      "id": "sdk_preset_fail_closed_public_evidence",
      "status": "missing_production_evidence",
      "failure_mode": "fail-closed until SDK preset fail-closed proof is accepted"
    },
    {
      "id": "local_development_runbook_evidence",
      "status": "missing_production_evidence",
      "failure_mode": "fail-closed until local development runbook evidence is accepted"
    },
    {
      "id": "public_faucet_absence_boundary",
      "status": "missing_production_evidence",
      "failure_mode": "fail-closed until public faucet absence boundary is signed and accepted"
    },
    {
      "id": "external_audit_and_final_signatures",
      "status": "missing_production_evidence",
      "failure_mode": "fail-closed until external audit, final signatures, and C total Go-No-Go are accepted"
    }
  ]
}

3.7 预留域名(reserved_testnet_domains)

虽然 testnet 功能未开放,但其域名架构已经预定:

域名 用途
testnet-rpc.msgchain.org Testnet RPC 端点
testnet-nodes.msgchain.org Testnet 节点发现/控制面
testnet-api.msgchain.org Testnet REST/API 网关
testnet-grpc.msgchain.org Testnet gRPC 端点
testnet-indexer.msgchain.org Testnet indexer API
testnet-explorer.msgchain.org Testnet 浏览器
testnet-faucet.msgchain.org Testnet 水龙头
testnet-wallet.msgchain.org Testnet 钱包前端
testnet-agent.msgchain.org Testnet AI Agent 网关
testnet-agents.msgchain.org Testnet AI Agent 网关别名

所有域名的当前状态均为 reserved_fail_closed,附注均为 testnet_not_launched_fail_closed。

3.8 SDK 防护守卫

当前策略要求 SDK 和 indexer 在 fail-closed 模式下必须包含以下防护:

"required_sdk_guards": [
  "mainnet_preset_verified_false",
  "testnet_preset_verified_false",
  "client_rejects_unverified_presets_by_default",
  "indexer_rejects_unverified_presets_by_default"
]

这意味着:

3.9 本地开发路径

当前策略明确指出的本地开发路径为:

examples/ai-agent-dapp-starter with explicit endpoints only

开发者在本地运行时必须显式指定端点,不能依赖 SDK 默认的主网/测试网预设。

3.10 对 AI Agent 的实际影响

AI Agent 在读取 developer_sandbox_strategy.json 后,应当:

SANDOX_VERDICT = {
    "public_testnet_available": False,
    "public_devnet_available": False,
    "public_faucet_available": False,
    "local_development_required": True,
    "reason": "Option B fail-closed strategy; all 5 production requirements missing evidence",
    "local_path": "examples/ai-agent-dapp-starter with explicit endpoints only",
    "sdk_auto_connect": False,
    "sdk_guard_active": True,
}

def get_sandbox_status() -> dict:
    """
    返回当前沙箱策略的状态总结。
    AI Agent 应调用此函数而非直接访问公开端点。
    """
    return SANDOX_VERDICT

3.11 本地沙箱启动示例

# 本地开发环境启动(示例)
git clone https://github.com/msgchain/ai-agent-dapp-starter
cd ai-agent-dapp-starter

# 安装依赖
npm install

# 配置本地端点(必须显式指定)
export MSG_RPC_ENDPOINT="http://localhost:26657"
export MSG_REST_ENDPOINT="http://localhost:1317"

# 启动本地开发链(需要本地节点)
# 请参考 chain_config/network_presets.json 的 local 配置
npm run dev

3.12 沙箱策略的源文件引用

developer_sandbox_strategy.json 的 source_files 字段记录了策略的源代码来源:

"source_files": [
  { "path": "sdk/src/types.ts", "role": "sdk_network_presets" },
  { "path": "sdk/src/client/index.ts", "role": "sdk_unverified_network_guard" },
  { "path": "sdk/src/indexer/index.ts", "role": "sdk_indexer_unverified_network_guard" },
  { "path": "release/developer_capability_manifest.json", "role": "..." },
  { "path": "release/developer_dapp_starter_e2e_manifest.json", "role": "..." },
  { "path": "examples/ai-agent-dapp-starter/README.md", "role": "local_dapp_starter_runbook" }
]

3.13 策略的白皮书模块依赖

"whitepaper_modules": [
  "sdk_dev_surface",
  "agent_api_surface",
  "l4_l5_acceptance_matrix",
  "ecosystem_platform_extensibility",
  "observability_monitoring"
]

沙箱策略不是一个独立决策,而是与 SDK 表面、Agent API 表面、验收矩阵、
生态平台扩展性和可观测性监控等多个模块协同的结果。

3.14 关闭边界声明

"closing_boundary": "This manifest chooses the conservative Option B local fail-closed
strategy only. It does not provide a public devnet, public testnet, public faucet,
signed disabled manifest, public SDK preset, external audit, final signatures,
or C total Go-No-Go."

3.15 对于水龙头(Faucet)的限制

当前 faucet_mode 为 disabled_public_faucet。这意味着:

如果 AI Agent 的代码中包含水龙头调用逻辑,在未确认 public faucet 已开放前,
必须跳过或报错。

3.16 策略的技术背景:为什么选择 fail-closed

根据 developer_sandbox_strategy.json 的完整上下文,选择 fail-closed 策略的
原因包括:

  1. 签名清单缺失:没有经过签名的 disabled sandbox manifest
  2. SDK 预设未验证:SDK 中的 mainnet 和 testnet 预设均标记为未验证
  3. 本地开发验证缺失:本地开发启动器的运行证据尚未收集
  4. 水龙头边界未签名:公开水龙头的关闭声明未经签名确认
  5. 外部审计未完成:最终的外部审计报告和签名未关闭

3.17 AI Agent 的环境检测函数

type EnvironmentStatus = "available" | "fail_closed" | "local_only";

interface EnvironmentInfo {
  status: EnvironmentStatus;
  chainId: string;
  endpoints: string[];
  faucetAvailable: boolean;
  notes: string[];
}

function checkEnvironment(env: string): EnvironmentInfo {
  const sandboxStatus = {
    status: "fail_closed" as EnvironmentStatus,
    chainId: "msg-chain-testnet-1",
    endpoints: [
      "testnet-rpc.msgchain.org (reserved_fail_closed)",
      "testnet-api.msgchain.org (reserved_fail_closed)",
    ],
    faucetAvailable: false,
    notes: [
      "Testnet is NOT launched; all domains are reserved_fail_closed",
      "Use local development path instead",
      "See: chain_config/developer_sandbox_strategy.json",
    ],
  };

  const localInfo = {
    status: "local_only" as EnvironmentStatus,
    chainId: "msg-chain-local-1",
    endpoints: [
      "http://localhost:26657",
      "http://localhost:1317",
    ],
    faucetAvailable: false,
    notes: [
      "Local development only",
      "You must run a local node",
      "No public testnet faucet available",
    ],
  };

  const mainnetInfo = {
    status: "available" as EnvironmentStatus,
    chainId: "msg-chain-1",
    endpoints: [
      "https://rpc.msgchain.org",
      "https://api.msgchain.org",
    ],
    faucetAvailable: false,
    notes: [
      "Mainnet is available for production",
      "Do NOT use mainnet for testing",
      "Transactions cost real MSG tokens",
    ],
  };

  switch (env) {
    case "mainnet": return mainnetInfo;
    case "testnet": return sandboxStatus;
    case "local": return localInfo;
    default: return localInfo;
  }
}

3.18 策略的消费边界

"public_consumption_boundary": [
  "当前公开口径是 Option B: disabled public sandbox with explicit local development path。",
  "未看到 signed disabled manifest、public faucet absence proof 与 final signatures 前,外部 AI 必须把 public devnet/testnet/faucet 视为不可用。"
]

AI Agent 必须将这两条边界声明作为消费沙箱策略文件的前提条件。

3.19 从 fail-closed 到公开的路径

如果未来策略从 Option B 切换到开放状态,需要以下条件全部满足:

  1. 所有五个 production_requirements 的状态从 missing_production_evidence 变为 accepted
  2. signed_disabled_manifest_claim 变为 true
  3. public_sandbox_enabled 变为 true
  4. mainnet_ready_claim 变为 true
  5. mainnet_verdict 从 No-Go 变为 Go

在此之前,AI Agent 必须假设所有公开非主网环境不可用。

3.20 本地开发入口点

"local_baseline_entry_points": [
  "chain_config/network_presets.json",
  "chain_config/keplr_chain_info.json",
  "examples/index.json",
  "release_pack/developer_dapp_starter_e2e_manifest.json",
  "release_pack/developer_public_sandbox_strategy_manifest.json"
]

4. 网络配置生产指引

4.1 主网配置

NETWORK_CONFIGS = {
    "mainnet": {
        "chain_id": "msg-chain-1",
        "chain_name": "MSG Chain",
        "rpc": "https://rpc.msgchain.org",
        "rest": "https://api.msgchain.org",
        "grpc": "msgchain-grpc.msgchain.org:9090",
        "bech32_prefix": "msg",
        "coin_denom": "MSG",
        "coin_minimal_denom": "umsg",
        "coin_decimals": 6,
        "gas_low": 1000000000,
        "gas_average": 1000000000,
        "gas_high": 1000000000,
        "features": ["cosmwasm"],
        "bip44_coin_type": 118,
    },
    "testnet": {
        "chain_id": "msg-chain-testnet-1",
        "chain_name": "MSG Chain Testnet",
        "rpc": "testnet-rpc.msgchain.org",
        "rest": "testnet-api.msgchain.org",
        "grpc": "testnet-grpc.msgchain.org:9090",
        "bech32_prefix": "msg",
        "status": "reserved_fail_closed",
        "available": False,
    },
    "local": {
        "chain_id": "msg-chain-local-1",
        "chain_name": "MSG Chain Local",
        "rpc": "http://localhost:26657",
        "rest": "http://localhost:1317",
        "grpc": "localhost:9090",
        "bech32_prefix": "msg",
        "coin_denom": "MSG",
        "coin_minimal_denom": "umsg",
        "coin_decimals": 6,
        "gas_low": 1000000000,
        "gas_average": 1000000000,
        "gas_high": 1000000000,
    },
}

4.2 主网接入的类型化配置

interface MainnetConfig {
  chainId: "msg-chain-1";
  chainName: "MSG Chain";
  rpc: "https://rpc.msgchain.org";
  rest: "https://api.msgchain.org";
  grpc: "msgchain-grpc.msgchain.org:9090";
  bech32Prefix: "msg";
  currencies: Array<{
    coinDenom: "MSG";
    coinMinimalDenom: "umsg";
    coinDecimals: 6;
  }>;
  gasPriceStep: {
    low: 1000000000;
    average: 1000000000;
    high: 1000000000;
  };
  features: ["cosmwasm"];
}

const MAINNET_CONFIG: MainnetConfig = {
  chainId: "msg-chain-1",
  chainName: "MSG Chain",
  rpc: "https://rpc.msgchain.org",
  rest: "https://api.msgchain.org",
  grpc: "msgchain-grpc.msgchain.org:9090",
  bech32Prefix: "msg",
  currencies: [{
    coinDenom: "MSG",
    coinMinimalDenom: "umsg",
    coinDecimals: 6,
  }],
  gasPriceStep: {
    low: 1000000000,
    average: 1000000000,
    high: 1000000000,
  },
  features: ["cosmwasm"],
};

4.3 CosmJS 生产客户端初始化

import { SigningStargateClient, StargateClient } from "@cosmjs/stargate";
import { GasPrice } from "@cosmjs/stargate";

async function createMainnetReadClient(): Promise<StargateClient> {
  return StargateClient.connect(MAINNET_CONFIG.rpc);
}

async function createMainnetSigningClient(
  signer: OfflineSigner
): Promise<SigningStargateClient> {
  const gasPrice = GasPrice.fromString("1000000000attoMSG");
  return SigningStargateClient.connectWithSigner(
    MAINNET_CONFIG.rpc,
    signer,
    { gasPrice }
  );
}

4.4 测试网配置的 fail-closed 处理

由于 testnet 处于 reserved_fail_closed 状态,代码必须包含防护逻辑:

function getTestnetConfig(): never {
  throw new Error(
    "Testnet is NOT available. " +
    "All testnet domains (testnet-rpc/api/grpc.msgchain.org) " +
    "are reserved_fail_closed. " +
    "Use local development (msg-chain-local-1) instead. " +
    "See: chain_config/developer_sandbox_strategy.json"
  );
}

4.5 本地开发环境完整配置

# local-dev-config.yaml
chain:
  id: msg-chain-local-1
  name: MSG Chain Local
  endpoints:
    rpc: http://localhost:26657
    rest: http://localhost:1317
    grpc: localhost:9090
  bech32_prefix: msg

wallet:
  bip44_coin_type: 118
  denom: umsg
  decimals: 6

gas:
  low: 1000000000
  average: 1000000000
  high: 1000000000

faucet:
  available: false
  note: "Public faucet disabled. Use local node --faucet flag if supported."

4.6 多环境配置选择器

import os
from enum import Enum

class NetworkEnvironment(Enum):
    MAINNET = "mainnet"
    TESTNET = "testnet"
    LOCAL = "local"

def resolve_network_config() -> dict:
    """
    根据环境变量 MSG_NETWORK 解析网络配置。
    默认使用 local 环境,防止误触主网。
    """
    env_name = os.getenv("MSG_NETWORK", "local").lower()

    try:
        env = NetworkEnvironment(env_name)
    except ValueError:
        print(f"Warning: Unknown network {env_name}, falling back to local")
        env = NetworkEnvironment.LOCAL

    configs = {
        NetworkEnvironment.MAINNET: {
            "chain_id": "msg-chain-1",
            "rpc": "https://rpc.msgchain.org",
            "rest": "https://api.msgchain.org",
            "bech32_prefix": "msg",
            "is_production": True,
            "require_confirmation": True,
        },
        NetworkEnvironment.TESTNET: {
            "chain_id": "msg-chain-testnet-1",
            "rpc": None,
            "rest": None,
            "bech32_prefix": "msg",
            "is_production": False,
            "available": False,
            "error": "Testnet is reserved_fail_closed. Not available.",
        },
        NetworkEnvironment.LOCAL: {
            "chain_id": "msg-chain-local-1",
            "rpc": "http://localhost:26657",
            "rest": "http://localhost:1317",
            "bech32_prefix": "msg",
            "is_production": False,
            "require_confirmation": False,
        },
    }

    return configs[env]

4.7 Rust SDK 环境配置示例

use std::env;

#[derive(Debug)]
pub enum MsgNetwork {
    Mainnet,
    Testnet,
    Local,
}

#[derive(Debug)]
pub struct MsgChainConfig {
    pub chain_id: String,
    pub rpc_url: String,
    pub rest_url: String,
    pub bech32_prefix: String,
}

impl MsgChainConfig {
    pub fn from_env() -> Result<Self, String> {
        let network = env::var("MSG_NETWORK")
            .unwrap_or_else(|_| "local".to_string());

        match network.as_str() {
            "mainnet" => Ok(Self {
                chain_id: "msg-chain-1".into(),
                rpc_url: "https://rpc.msgchain.org".into(),
                rest_url: "https://api.msgchain.org".into(),
                bech32_prefix: "msg".into(),
            }),
            "testnet" => Err(
                "Testnet is reserved_fail_closed. Use local.".into()
            ),
            "local" => Ok(Self {
                chain_id: "msg-chain-local-1".into(),
                rpc_url: "http://localhost:26657".into(),
                rest_url: "http://localhost:1317".into(),
                bech32_prefix: "msg".into(),
            }),
            _ => Err(format!("Unknown network: {}", network)),
        }
    }
}

4.8 Keplr 生产配置注入

import { MAINNET_CONFIG } from "./network-config";

async function suggestMainnetToKeplr() {
  if (!window.keplr) {
    throw new Error("Please install Keplr extension");
  }

  await window.keplr.experimentalSuggestChain({
    chainId: MAINNET_CONFIG.chainId,
    chainName: MAINNET_CONFIG.chainName,
    rpc: MAINNET_CONFIG.rpc,
    rest: MAINNET_CONFIG.rest,
    bip44: { coinType: 118 },
    bech32Config: {
      bech32PrefixAccAddr: MAINNET_CONFIG.bech32Prefix,
      bech32PrefixAccPub: MAINNET_CONFIG.bech32Prefix + "pub",
      bech32PrefixValAddr: MAINNET_CONFIG.bech32Prefix + "valoper",
      bech32PrefixValPub: MAINNET_CONFIG.bech32Prefix + "valoperpub",
      bech32PrefixConsAddr: MAINNET_CONFIG.bech32Prefix + "valcons",
      bech32PrefixConsPub: MAINNET_CONFIG.bech32Prefix + "valconspub",
    },
    currencies: MAINNET_CONFIG.currencies,
    feeCurrencies: MAINNET_CONFIG.currencies.map(c => ({
      ...c,
      gasPriceStep: MAINNET_CONFIG.gasPriceStep,
    })),
    stakeCurrency: MAINNET_CONFIG.currencies[0],
    features: MAINNET_CONFIG.features,
  });

  await window.keplr.enable(MAINNET_CONFIG.chainId);
}

4.9 gRPC 端点配置

对于需要使用 gRPC 的应用(如 indexer 或事件监听服务):

GRPC_ENDPOINTS = {
    "mainnet": {
        "host": "msgchain-grpc.msgchain.org",
        "port": 9090,
        "tls": True,
    },
    "local": {
        "host": "localhost",
        "port": 9090,
        "tls": False,
    },
}

4.10 生产配置复核清单

在将 chain_config 的配置用于生产之前,必须复核以下项目:

## 生产配置复核清单

- [ ] endpoint 可用性:RPC/REST/gRPC 端点是否可访问
- [ ] 域名控制:域名是否在生产 DNS 控制之下
- [ ] 证书有效期:TLS 证书是否有效、未过期
- [ ] SLO:端点是否有服务等级目标
- [ ] 发布权限:配置变更是否有发布审批流程
- [ ] chain_id:与实际链上区块头的 chain_id 是否一致
- [ ] bech32 前缀:生成的地址前缀是否正确
- [ ] gas 价格:当前的 gas 价格阶梯是否合理
- [ ] coin decimals:通证精度是否正确(18 位)
- [ ] 特性标志:features 是否包含 cosmwasm

4.11 从链上验证配置的脚本

#!/bin/bash
# verify-chain-config.sh
# 验证 chain_config 中的端点配置是否与链上一致

set -e

RPC_ENDPOINT=${1:-"https://rpc.msgchain.org"}
EXPECTED_CHAIN_ID="msg-chain-1"

echo "Verifying chain config at $RPC_ENDPOINT ..."

# 验证链 ID
CHAIN_ID=$(curl -s "$RPC_ENDPOINT/status" | \
  python3 -c "import sys,json; print(json.load(sys.stdin)['result']['node_info']['network'])")

if [ "$CHAIN_ID" != "$EXPECTED_CHAIN_ID" ]; then
  echo "ERROR: Expected chain_id=$EXPECTED_CHAIN_ID, got $CHAIN_ID"
  exit 1
fi
echo "OK: chain_id=$CHAIN_ID"

# 验证 REST API
REST_ENDPOINT=${2:-"https://api.msgchain.org"}
echo "Verifying REST API at $REST_ENDPOINT ..."
curl -s "$REST_ENDPOINT/cosmos/base/tendermint/v1beta1/node_info" > /dev/null
echo "OK: REST API reachable"

# 验证 bech32 地址前缀
echo "Verifying bech32 prefix ..."
ACCOUNT_RESP=$(curl -s "$REST_ENDPOINT/cosmos/auth/v1beta1/accounts?pagination.limit=1")
PREFIX=$(echo "$ACCOUNT_RESP" | python3 -c "
import sys,json
data = json.load(sys.stdin)
addr = data['accounts'][0]['address']
print(addr[:3])
" 2>/dev/null || echo "unknown")

if [ "$PREFIX" != "msg" ]; then
  echo "WARNING: Expected bech32 prefix 'msg', got '$PREFIX'"
fi
echo "OK: bech32_prefix=$PREFIX"

echo ""
echo "All checks passed!"

4.12 环境变量配置模式

# .env.mainnet
MSG_NETWORK=mainnet
MSG_CHAIN_ID=msg-chain-1
MSG_RPC_URL=https://rpc.msgchain.org
MSG_REST_URL=https://api.msgchain.org
MSG_GRPC_HOST=msgchain-grpc.msgchain.org
MSG_GRPC_PORT=9090
MSG_BECH32_PREFIX=msg
MSG_GAS_PRICE=0.025
MSG_GAS_DENOM=umsg

# .env.local
MSG_NETWORK=local
MSG_CHAIN_ID=msg-chain-local-1
MSG_RPC_URL=http://localhost:26657
MSG_REST_URL=http://localhost:1317
MSG_GRPC_HOST=localhost
MSG_GRPC_PORT=9090
MSG_BECH32_PREFIX=msg
MSG_GAS_PRICE=0.01
MSG_GAS_DENOM=umsg

4.13 网络配置的热切换

对于需要在运行时切换网络的应用:

type NetworkName = "mainnet" | "testnet" | "local";

class NetworkManager {
  private currentNetwork: NetworkName = "local";

  setNetwork(network: NetworkName): void {
    if (network === "testnet") {
      throw new Error(
        "Testnet is reserved_fail_closed. Cannot switch to testnet."
      );
    }
    this.currentNetwork = network;
    console.log(`Switched to ${network}`);
  }

  getConfig() {
    const configs: Record<NetworkName, object> = {
      mainnet: {
        chainId: "msg-chain-1",
        rpc: "https://rpc.msgchain.org",
        rest: "https://api.msgchain.org",
      },
      testnet: {
        chainId: "msg-chain-testnet-1",
        available: false,
      },
      local: {
        chainId: "msg-chain-local-1",
        rpc: "http://localhost:26657",
        rest: "http://localhost:1317",
      },
    };
    return configs[this.currentNetwork];
  }
}

4.14 生产环境的安全配置建议

PRODUCTION_SAFETY_RULES = {
    "never_use_mainnet_for_testing": True,
    "require_user_confirmation_for_mainnet_tx": True,
    "log_all_mainnet_transactions": True,
    "rate_limit_mainnet_queries": True,
    "validate_chain_id_before_signing": True,
    "check_bech32_prefix_before_signing": True,
    "gas_price_minimum": 1000000000,
    "max_gas_per_tx": 500_000,
}

4.15 配置错误处理模式

class NetworkConfigError(Exception):
    """网络配置相关的错误基类"""
    pass

class TestnetNotAvailableError(NetworkConfigError):
    """测试网不可用错误"""
    def __init__(self):
        super().__init__(
            "Testnet is reserved_fail_closed. "
            "See developer_sandbox_strategy.json for details."
        )

class MainnetConfirmationRequired(NetworkConfigError):
    """主网操作需要确认"""
    def __init__(self, action: str):
        super().__init__(
            f"Operation '{action}' targets mainnet (msg-chain-1). "
            "User confirmation required."
        )

5. AI Agent 网络配置最佳实践

5.1 核心原则

AI Agent 在使用 MSG Chain 的链配置时,必须遵循以下五个核心原则:

  1. 以 local 为默认开发环境:除非用户明确指定 mainnet,否则所有开发活动
    应基于 msg-chain-local-1
  2. mainnet 需要确认:任何涉及主网的操作必须经过用户确认,且不得自动执行
  3. testnet 不可用:测试网域名已预留但处于 fail-closed 状态,代码中不应包含
    自动连接到 testnet 的逻辑
  4. 配置需验证:chain_config 的配置数据应作为生成起点,而非最终权威来源
  5. faucet 不可用:公开水龙头不存在,不可在代码中假设有免费测试代币

5.2 AI Agent 的网络选择流程

async def select_network(agent_context: dict) -> dict:
    """
    AI Agent 的网络选择决策流程。
    根据用户意图、环境变量和安全约束选择合适的网络。
    """
    # 步骤 1:读取用户意图
    user_intent = agent_context.get("intent", "development")

    # 步骤 2:检查环境变量
    env_override = agent_context.get("env", {}).get("MSG_NETWORK")

    # 步骤 3:根据意图选择
    if user_intent == "production":
        # 生产环境必须使用 mainnet,但需确认
        if not agent_context.get("user_confirmed_mainnet", False):
            return {
                "selected": "mainnet",
                "status": "pending_confirmation",
                "message": "Production deployment requires explicit user confirmation",
            }
        return _get_mainnet_config()

    elif user_intent == "development":
        # 开发环境默认使用 local
        return _get_local_config()

    elif user_intent == "testing":
        # 测试环境不能使用 testnet(fail-closed)
        # 建议用户搭建本地节点
        return {
            "selected": "local",
            "status": "testnet_unavailable",
            "message": (
                "Testnet is reserved_fail_closed. "
                "Please use local development with a local node."
            ),
        }

    else:
        # 未知意图,默认 local
        return _get_local_config()

def _get_mainnet_config() -> dict:
    return {
        "chain_id": "msg-chain-1",
        "rpc": "https://rpc.msgchain.org",
        "rest": "https://api.msgchain.org",
        "bech32_prefix": "msg",
        "warning": "MAINNET - real assets. Confirm before transactions.",
    }

def _get_local_config() -> dict:
    return {
        "chain_id": "msg-chain-local-1",
        "rpc": "http://localhost:26657",
        "rest": "http://localhost:1317",
        "bech32_prefix": "msg",
        "note": "Local development only.",
    }

5.3 沙箱检测机制

AI Agent 应实现沙箱检测函数,用于在运行时确认当前环境是否为已授权的沙箱:

def is_sandbox_available(environment: str) -> bool:
    """
    检测指定环境的沙箱是否可用。
    基于 developer_sandbox_strategy.json 的真实策略。
    """
    sandbox_checklist = {
        "mainnet": {
            "available": True,
            "faucet": False,
            "note": "Production network. REAL tokens.",
        },
        "testnet": {
            "available": False,
            "faucet": False,
            "note": "reserved_fail_closed - NOT launched",
            "blockers": [
                "signed_disabled_manifest_missing",
                "sdk_preset_fail_closed_evidence_missing",
                "public_faucet_absence_boundary_missing",
            ],
        },
        "devnet": {
            "available": False,
            "faucet": False,
            "note": "not_public per strategy",
        },
        "local": {
            "available": True,
            "faucet": False,
            "note": "Local development - requires local node",
            "manual_setup_required": True,
        },
    }

    return sandbox_checklist.get(environment, {}).get("available", False)

5.4 链配置验证函数

interface VerificationResult {
  valid: boolean;
  errors: string[];
  warnings: string[];
}

function verifyChainConfig(config: any): VerificationResult {
  const errors: string[] = [];
  const warnings: string[] = [];

  // 必填字段检查
  if (!config.chainId) errors.push("Missing chainId");
  if (!config.rpc) errors.push("Missing RPC endpoint");
  if (!config.rest) errors.push("Missing REST endpoint");

  // chain_id 格式检查
  if (config.chainId && !config.chainId.startsWith("msg-chain-")) {
    errors.push(`Invalid chain_id format: ${config.chainId}`);
  }

  // 端点格式检查
  if (config.rpc && !config.rpc.startsWith("http")) {
    errors.push(`Invalid RPC URL format: ${config.rpc}`);
  }

  // bech32 前缀检查
  if (config.bech32Prefix) {
    if (config.bech32Prefix !== "msg") {
      errors.push(`Unexpected bech32 prefix: ${config.bech32Prefix}. Expected: msg`);
    }
  } else {
    // 检查嵌套的 bech32 配置
    const prefix = config.bech32Config?.bech32PrefixAccAddr;
    if (prefix && prefix !== "msg") {
      errors.push(`Unexpected bech32 prefix: ${prefix}. Expected: msg`);
    }
  }

  // 安全警告
  if (config.chainId === "msg-chain-1") {
    warnings.push("MAINNET CONFIG - this is the production network");
  }

  return { valid: errors.length === 0, errors, warnings };
}

5.5 安全:避免误用主网

import hashlib
import json

class MainnetGuard:
    """
    主网防护守卫。
    在执行任何链上操作前验证环境,防止误触主网。
    """

    MAINNET_CHAIN_ID = "msg-chain-1"

    @staticmethod
    def require_confirmation(config: dict, operation: str) -> bool:
        """要求用户确认主网操作"""
        if config.get("chain_id") != MainnetGuard.MAINNET_CHAIN_ID:
            return True  # 非主网不需要确认

        print(f"""
╔══════════════════════════════════════════════════╗
║  MAINNET CONFIRMATION REQUIRED                  ║
╠══════════════════════════════════════════════════╣
║  Operation: {operation:<40}║
║  Network:   {config.get('chain_id'):<40}║
║  RPC:       {config.get('rpc'):<40}║
╠══════════════════════════════════════════════════╣
║  WARNING: This is the MAINNET. Real assets      ║
║  will be affected. Confirm to proceed.          ║
╚══════════════════════════════════════════════════╝
        """)
        response = input("Type 'yes' to confirm: ")
        return response.lower() == "yes"

    @staticmethod
    def fingerprint_config(config: dict) -> str:
        """生成配置指纹,用于追踪配置的来源"""
        serialized = json.dumps(config, sort_keys=True)
        return hashlib.sha256(serialized.encode()).hexdigest()[:16]

5.6 AI Agent 的配置加载与缓存

class ChainConfigService {
  private cache: Map<string, any> = new Map();
  private refreshInterval: number = 5 * 60 * 1000; // 5 分钟
  private lastFetch: number = 0;

  async getConfig(network: "mainnet" | "local"): Promise<any> {
    const cacheKey = `chain_config_${network}`;

    // 检查缓存
    if (this.cache.has(cacheKey)) {
      const cached = this.cache.get(cacheKey);
      if (Date.now() - this.lastFetch < this.refreshInterval) {
        return cached;
      }
    }

    // 从 network_presets 获取
    try {
      const resp = await fetch(
        "https://msgchain.org/whitepaper/chain_config/network_presets.json"
      );
      const presets = await resp.json();
      this.cache.set(cacheKey, presets);
      this.lastFetch = Date.now();
      return presets;
    } catch (error) {
      // 降级到缓存或默认值
      if (this.cache.has(cacheKey)) {
        console.warn("Failed to refresh config, using cached version");
        return this.cache.get(cacheKey);
      }
      throw error;
    }
  }
}

5.7 交易前验证流程

async def preflight_check(config: dict, tx_payload: dict) -> dict:
    """
    交易发送前的安全检查。
    返回是否允许发送以及原因。
    """
    checks = []

    # 1. 验证 chain_id 一致性
    chain_id_check = await verify_chain_id(config.get("rpc"), config.get("chain_id"))
    checks.append(("chain_id", chain_id_check))

    # 2. 检查接收方地址前缀
    if "recipient" in tx_payload:
        addr = tx_payload["recipient"]
        if not addr.startswith("msg"):
            checks.append(("recipient_prefix", {
                "passed": False,
                "message": f"Recipient address does not start with 'msg': {addr}"
            }))
        else:
            checks.append(("recipient_prefix", {"passed": True}))

    # 3. 检查 gas 价格是否在合理范围
    gas_check = validate_gas_price(
        tx_payload.get("gas_price", 0),
        config.get("gas_price_step", {})
    )
    checks.append(("gas_price", gas_check))

    # 4. 如果是 mainnet,要求确认
    if config.get("chain_id") == "msg-chain-1":
        checks.append(("mainnet_confirmation", {
            "passed": False,
            "requires_user_input": True,
            "message": "Mainnet transaction requires user confirmation",
        }))

    # 汇总结果
    all_passed = all(c[1].get("passed", False) for c in checks)
    return {
        "passed": all_passed,
        "checks": checks,
        "blocking_issues": [
            c[1]["message"] for c in checks
            if not c[1].get("passed", False) and "message" in c[1]
        ],
    }

async def verify_chain_id(rpc_url: str, expected_chain_id: str) -> dict:
    """通过 RPC 验证链 ID"""
    import aiohttp
    try:
        async with aiohttp.ClientSession() as session:
            payload = {
                "jsonrpc": "2.0",
                "id": 1,
                "method": "status",
                "params": [],
            }
            async with session.post(rpc_url, json=payload) as resp:
                data = await resp.json()
                actual = data.get("result", {}).get("node_info", {}).get("network")
                if actual == expected_chain_id:
                    return {"passed": True}
                else:
                    return {
                        "passed": False,
                        "message": f"Chain ID mismatch: expected {expected_chain_id}, got {actual}",
                    }
    except Exception as e:
        return {
            "passed": False,
            "message": f"Failed to verify chain ID: {e}",
        }

def validate_gas_price(gas_price: float, gas_price_step: dict) -> dict:
    """验证 gas 价格是否在合理范围"""
    if not gas_price_step:
        return {"passed": True, "message": "No gas price step config for comparison"}
    low = gas_price_step.get("low", 0)
    high = gas_price_step.get("high", 1000000000)
    if gas_price < low:
        return {
            "passed": False,
            "message": f"Gas price {gas_price} below minimum {low}",
        }
    if gas_price > high * 10:
        return {
            "passed": False,
            "message": f"Gas price {gas_price} seems excessively high (max recommended: {high})",
        }
    return {"passed": True}

5.8 AI Agent 的代码生成规则

当 AI Agent 生成与 MSG Chain 交互的代码时,应遵守以下规则:

AI_CODE_GENERATION_RULES = {
    "network_selection": {
        "default": "local",
        "require_confirmation_for": ["mainnet"],
        "never_auto_select": ["testnet"],
    },
    "endpoint_usage": {
        "never_hardcode": False,
        "prefer_env_vars": True,
        "fallback_to_config": True,
    },
    "faucet_calls": {
        "never_auto_call": True,
        "document_as_unavailable": True,
    },
    "transaction_generation": {
        "include_chain_id_validation": True,
        "include_address_prefix_check": True,
        "include_gas_price_bounds": True,
    },
    "sdk_usage": {
        "respect_verified_flag": True,
        "do_not_bypass_unverified_guard": True,
    },
}

5.9 地址生成的验证

def validate_msg_address(address: str) -> bool:
    """
    验证 MSG Chain 地址的格式。
    地址应为 bech32 格式,以 'msg' 开头。
    """
    if not address:
        return False
    if not address.startswith("msg"):
        return False
    if len(address) < 20 or len(address) > 63:
        return False
    # bech32 字符集:qpzry9x8gf2tvdw0s3jn54khce6mua7l
    valid_chars = set("qpzry9x8gf2tvdw0s3jn54khce6mua7l")
    # 跳过前缀和分隔符 '1'
    data_part = address.split("1")[-1] if "1" in address else address
    for c in data_part:
        if c not in valid_chars:
            return False
    return True

5.10 Gas 价格管理

def get_gas_price_for_priority(priority: str = "average") -> str:
    """
    根据优先级获取 gas 价格字符串。
    用于 CosmJS SigningStargateClient 的 GasPrice 初始化。
    """
    gas_steps = {
        "low": 1000000000,
        "average": 1000000000,
        "high": 1000000000,
    }
    price = gas_steps.get(priority, 1000000000)
    return f"{price}attoMSG"

5.11 AI Agent 的配置决策树

flowchart TD
    A[AI Agent 需要连接 MSG Chain] --> B{用户指定了网络?}
    B -->|是| C[使用指定网络]
    B -->|否| D{开发/生产?}
    D -->|开发| E[local: msg-chain-local-1]
    D -->|生产| F[mainnet: msg-chain-1]
    D -->|测试| G[testnet 不可用<br/>fallback 到 local]
    C --> H{指定了 testnet?}
    H -->|是| I[报错: testnet fail-closed]
    H -->|否| J[使用指定配置]
    E --> K[验证 localhost 可达]
    F --> L[要求用户确认]
    L -->|确认| M[配置 mainnet 连接]
    L -->|拒绝| N[不可执行操作]

5.12 默认配置与用户指定配置的合并

def merge_network_config(
    default_config: dict,
    user_overrides: dict,
) -> dict:
    """
    合并默认配置与用户指定的覆盖配置。
    用户覆盖优先。
    """
    merged = default_config.copy()

    for key, value in user_overrides.items():
        if key in merged and isinstance(merged[key], dict) and isinstance(value, dict):
            merged[key] = {**merged[key], **value}
        else:
            merged[key] = value

    return merged

5.13 环境变量驱动的配置模式

import os

def config_from_environment() -> dict:
    """
    从环境变量读取网络配置。
    如果未设置环境变量,返回默认的 local 配置。
    """
    network = os.getenv("MSG_NETWORK", "local")

    if network == "mainnet":
        return {
            "chain_id": os.getenv("MSG_CHAIN_ID", "msg-chain-1"),
            "rpc": os.getenv("MSG_RPC_URL", "https://rpc.msgchain.org"),
            "rest": os.getenv("MSG_REST_URL", "https://api.msgchain.org"),
            "grpc_host": os.getenv("MSG_GRPC_HOST", "msgchain-grpc.msgchain.org"),
            "grpc_port": int(os.getenv("MSG_GRPC_PORT", "9090")),
            "bech32_prefix": os.getenv("MSG_BECH32_PREFIX", "msg"),
        }
    elif network == "local":
        return {
            "chain_id": os.getenv("MSG_CHAIN_ID", "msg-chain-local-1"),
            "rpc": os.getenv("MSG_RPC_URL", "http://localhost:26657"),
            "rest": os.getenv("MSG_REST_URL", "http://localhost:1317"),
            "grpc_host": os.getenv("MSG_GRPC_HOST", "localhost"),
            "grpc_port": int(os.getenv("MSG_GRPC_PORT", "9090")),
            "bech32_prefix": os.getenv("MSG_BECH32_PREFIX", "msg"),
        }
    else:
        raise ValueError(f"Unknown MSG_NETWORK: {network}")

5.14 对 AI Agent 的最终建议

AI_AGENT_RECOMMENDATIONS = [
    "始终从 chain_config/network_presets.json 获取链元数据基线",
    "不要硬编码端点 URL,除非是从 verified source 获取",
    "生成代码时默认使用 local 环境(msg-chain-local-1)",
    "testnet 相关域名不可用,不要生成连接 testnet 的代码",
    "不要在代码中假设有公开 faucet",
    "生产部署前复核 endpoint 可用性、域名和证书",
    "使用 chain_id 验证来确认连接的是正确的网络",
    "每次生成交易代码时包含地址前缀验证",
    "mainnet 操作必须要求用户确认",
    "保持 SDK 的 verified 标记检查机制,不要绕过",
]

6. 边界声明

6.1 核心边界:公开沙箱未就绪

当前默认口径不是 public sandbox ready,而是 disabled/fail-closed + 明确 local development path。

来自 developer_sandbox_strategy.json 的原始边界:

当前公开口径是 Option B: disabled public sandbox with explicit local development path。
未看到 signed disabled manifest、public faucet absence proof 与 final signatures 前,
外部 AI 必须把 public devnet/testnet/faucet 视为不可用。

6.2 开发者能力矩阵中的边界

来自 developer_capability_matrix.json 中 public_sandbox_strategy 的边界:

当前默认口径不是 public sandbox ready,而是 disabled/fail-closed + 明确 local development path。
判断 public sandbox 是否可用 | 决定走 public testnet 还是 local-only starter |
避免把 admin/local faucet 误读成 public developer faucet

6.3 chain_config_pack 的边界

当前链配置来自仓库配置基线,生产接入前仍应复核 endpoint 可用性。

6.4 network_presets.json 的边界声明

"boundary": [
  "链配置是接入基线,不等于 live/public 端点已经被正式接受为生产网络。",
  "生产接入前仍需复核 endpoint 可用性、域名控制、证书、SLO 与发布权限。"
]

6.5 sandbox 策略的关闭边界

"closing_boundary": "This manifest chooses the conservative Option B local fail-closed
strategy only. It does not provide a public devnet, public testnet, public faucet,
signed disabled manifest, public SDK preset, external audit, final signatures,
or C total Go-No-Go."

6.6 五个已知阻塞项

来自 developer_sandbox_strategy.json 的 known_blockers:

# 阻塞项 状态
1 signed_disabled_public_sandbox_manifest_missing 未关闭
2 sdk_preset_fail_closed_public_evidence_missing 未关闭
3 local_development_runbook_evidence_missing 未关闭
4 public_faucet_absence_boundary_missing 未关闭
5 external_audit_final_signatures_missing 未关闭

AI Agent 需要注意:所有五个阻塞仍处于 missing 状态,
任何关于 testnet 或 faucet 可用的断言都是错误的。

6.7 留域名的 fail-closed 声明

所有 testnet 域名的状态均为 reserved_fail_closed,附注均为
testnet_not_launched_fail_closed。这意味着:

{
  "testnet-rpc.msgchain.org": {
    "state": "reserved_fail_closed",
    "note": "testnet_not_launched_fail_closed"
  },
  "testnet-api.msgchain.org": {
    "state": "reserved_fail_closed",
    "note": "testnet_not_launched_fail_closed"
  },
  "testnet-faucet.msgchain.org": {
    "state": "reserved_fail_closed",
    "note": "testnet_not_launched_fail_closed"
  },
  "testnet-agent.msgchain.org": {
    "state": "reserved_fail_closed",
    "note": "testnet_not_launched_fail_closed"
  }
}

6.8 水龙头不可用声明

项目 值
faucet_mode disabled_public_faucet
public_faucet_enabled false
admin faucet not public true(需证明)
SDK faucet path disabled

没有公开开发者水龙头。任何 AI 生成的代码中如果出现了水龙头调用,
应当被标记为潜在错误。

6.9 主网未就绪声明

"mainnet_verdict": "No-Go",
"mainnet_ready_claim": false

尽管主网端点(https://rpc.msgchain.org)在 network_presets 中列出,
但开发者沙箱策略中明确标记 mainnet verdict 为 "No-Go"。

这意味着:

6.10 SDK 预设未验证

SDK 中的网络预设标记为 unverified:

SDK 的客户端默认拒绝未验证的预设:

AI Agent 在使用 SDK 时,不应绕过这些防护。
如果需要连接到指定端点,应使用显式的端点参数而非 SDK 默认预设。

6.11 本地开发优先

local_development_path: "examples/ai-agent-dapp-starter with explicit endpoints only"

本地开发是当前唯一推荐的开发路径。
没有公开的 testnet、devnet 或 faucet 可用。

对于 AI Agent,这意味着:

6.12 配置文件的元数据边界

所有 chain_config 文件标记为 metadata_profile: "public_stable",
但这仅表示文件的格式和结构已稳定,不代表其中引用的端点已就绪。

6.13 开发者引导中的边界

来自 developer_entry.json 的 current_boundaries:

当前开发协议层可显著提升 AI coding 的可执行性,
但仍不能诚实承诺"只靠入口即可 100% 自动完成任何产品上线"。
当前已补 Quick Start、source-backed 合约消费索引、正式 API/Schema 契约索引
与 fail-closed sandbox 策略,但仍不等于 signed public SDK、public sandbox
或 not independently verified for production 交付。
涉及私钥、部署权限、生产域名、资金操作、DAO/timelock/threshold 的动作,
必须保留人类确认与审批门禁。

6.14 需要人工输入的环节

来自 developer_entry.json 的 human_inputs_required:

[
  "产品目标与业务规则",
  "真实部署权限与签名账户",
  "生产环境变量、域名、CI/CD 或发布权限",
  "治理、多签、金库、审批等高风险动作的授权与窗口"
]

AI Agent 不能替代人类在这些环节中的决策。

6.15 本文档的边界说明

本文档基于以下真实数据源编写:

所有数据均取自 msgchain.org/whitepaper/ 的公开白皮书。
读者应直接参考上述源文件获取最新信息,本文档可能落后于源文件的更新。

6.16 对于 AI Agent 的最后边界声明

╔══════════════════════════════════════════════════════════════╗
║                   AI AGENT BOUNDARY SUMMARY                ║
╠══════════════════════════════════════════════════════════════╣
║  chain_config_pack      → starter_ready, production: True  ║
║  public_sandbox_strategy → fail_closed_ref, production: No ║
╠══════════════════════════════════════════════════════════════╣
║  Available: mainnet (needs confirmation), local             ║
║  NOT available: testnet, devnet, public faucet             ║
║  Primary dev path: local development only                  ║
║  SDK presets: unverified (do not bypass guards)            ║
║  All testnet domains: reserved_fail_closed                 ║
╚══════════════════════════════════════════════════════════════╝

7. 附录:完整配置文件参考

7.1 chain_config/index.json

{
  "schema_version": "v1",
  "generated_by": "msg_whitepaper_pipeline_v1",
  "files": [
    {
      "id": "network_presets",
      "path": "chain_config/network_presets.json",
      "public_url": "https://msgchain.org/whitepaper/chain_config/network_presets.json",
      "description": "Machine-friendly MSG chain ID, endpoints, wallets, and currency defaults."
    },
    {
      "id": "keplr_chain_info",
      "path": "chain_config/keplr_chain_info.json",
      "public_url": "https://msgchain.org/whitepaper/chain_config/keplr_chain_info.json",
      "description": "Copied Keplr chain info baseline from repo configs."
    },
    {
      "id": "keplr_suggest_chain",
      "path": "chain_config/keplr_suggest_chain.js",
      "public_url": "https://msgchain.org/whitepaper/chain_config/keplr_suggest_chain.js",
      "description": "Keplr suggestChain helper copied from repo configs."
    },
    {
      "id": "developer_sandbox_strategy",
      "path": "chain_config/developer_sandbox_strategy.json",
      "public_url": "https://msgchain.org/whitepaper/chain_config/developer_sandbox_strategy.json",
      "description": "Current fail-closed public sandbox, testnet, and faucet strategy."
    }
  ],
  "metadata_profile": "public_stable"
}

7.2 developer_capability_matrix.json 相关条目

chain_config_pack

{
  "surface_id": "chain_config_pack",
  "title": "链配置与钱包注入包",
  "machine_readiness": "starter_ready",
  "machine_safe_for_codegen": true,
  "write_path_ready": false,
  "schema_available": true,
  "example_available": true,
  "production_supported": true,
  "best_for": [
    "前端链配置生成",
    "Keplr suggestChain",
    "CosmJS/钱包接入初始化"
  ],
  "blocking_gaps": [
    "缺少从 live/public 节点自动探测并签名确认的网络元数据机制"
  ],
  "boundaries": [
    "当前链配置来自仓库配置基线,生产接入前仍应复核 endpoint 可用性。"
  ]
}

public_sandbox_strategy

{
  "surface_id": "public_sandbox_strategy",
  "title": "Public Devnet /Testnet /Faucet 策略",
  "machine_readiness": "fail_closed_reference",
  "machine_safe_for_codegen": true,
  "write_path_ready": false,
  "schema_available": true,
  "example_available": true,
  "production_supported": false,
  "best_for": [
    "判断 public sandbox 是否可用",
    "决定走 public testnet 还是 local-only starter",
    "避免把 admin/local faucet 误读成 public developer faucet"
  ],
  "blocking_gaps": [
    "当前选择的是 Option B fail-closed 策略",
    "signed disabled manifest、public faucet absence proof 与 final signatures 仍未关闭"
  ],
  "boundaries": [
    "当前默认口径不是 public sandbox ready,而是 disabled/fail-closed + 明确 local development path。"
  ]
}

7.3 developer_entry.json 中的引导顺序

"recommended_contract_bootstrap_order": [
  "product_delivery_entry.json",
  "developer_capability_matrix.json",
  "quickstart/contract_and_dapp_minimal.json",
  "chain_config/index.json",
  "chain_config/developer_sandbox_strategy.json",
  "api_specs/rpc_methods.json",
  "api_specs/openapi/contract_surface.yaml",
  "api_specs/formal_contracts.json",
  "contract_reference/index.json",
  "contract_reference/core_contracts.json",
  "contract_templates/index.json",
  "recipes/contract_minimal.json",
  "execution_pack/index.json",
  "execution_pack/command_registry.json",
  "e2e_fixtures/index.json",
  "modules/contract.html",
  "modules/registry.html",
  "modules/rpc.html",
  "module_exports/contract.json"
]

7.4 常用工具命令速查

# 获取链状态
curl -s https://rpc.msgchain.org/status | jq .

# 获取链 ID
curl -s https://rpc.msgchain.org/status | jq -r '.result.node_info.network'

# 查询最新区块
curl -s https://rpc.msgchain.org/block | jq .

# 查询地址余额(替换地址)
curl -s https://api.msgchain.org/cosmos/bank/v1beta1/balances/msg1...

# 验证 bech32 地址格式
echo "msg1..." | grep -E '^msg[0-9a-z]{38,45}$'

# 本地开发节点(需要独立安装)
# msgd start --rpc.laddr tcp://0.0.0.0:26657

7.5 术语对照表

英文 中文 说明
chain_config 链配置 MSG Chain 的机器可消费配置包
network_presets 网络预设 包含链 ID、端点、钱包、货币的预设文件
sandbox strategy 沙箱策略 关于 testnet/devnet/faucet 的可用性策略
fail-closed 故障关闭 默认关闭,除非明确证明可用
reserved_fail_closed 预留故障关闭 域名已预留但不可用
starter_ready 入门就绪 可以作为开发起点使用
fail_closed_reference 故障关闭参考 仅可读取参考,不可用于生成执行代码
signed manifest 签名清单 经过数字签名确认的声明文件
production_ready 生产就绪 经过完整验收,可用于生产环境
bech32 prefix 地址前缀 Cosmos 标准地址的人类可读前缀部分

文档版本: v1.0
数据源: msgchain.org/whitepaper/
⚠️ No-Go Disclaimer: MSGChain 主网裁决为 No-Go。本文件所有内容反映的是开发阶段的技术设计,不代表主网未独立核验上线状态。生产部署状态请以白皮书为准:https://msgchain.org/whitepaper/
数据截止: chain_config 文件以 public_stable 元数据配置管线上一次发布为准
免责声明: 本文档基于公开数据编写,不构成 MSG Chain 的官方承诺。
所有配置和策略以 msgchainorg 上的最新版本为准。