dApp Docs/AI Agent Gas 优化与合约性能调优实战指南
Development reference. Not independently verified for production.

AI Agent Gas 优化与合约性能调优实战指南

面向 AI Agent 的 msg-chain-1 智能合约 Gas 效率手册

链 ID: msg-chain-1 | 共识: DAR | 虚拟机: CosmWasm (WasmVM)
状态: 规划文档 — 主网裁决为 No-Go,所有数据均为主网预演
Gas: 1,000,000,000 attoMSG/gas
Gas 分配: 40% 验证者 / 30% 开发者 / 20% 燃烧 / 10% 基金会金库
地址格式: SHA3-512(前40位) + SHA-256 校验 | 签名: Dilithium-5 (公钥2592字节 / 私钥4864字节 / 签名4595字节)
AI Agent安全边界: 永不自主创建新合约,永不自主调整 Gas 参数,永不自主铸造/销毁代币


1. 概述

1.1 为什么 Gas 优化对 AI Agent 至关重要

AI Agent 默认倾向于生成"能跑"而非"高效"的代码。在 msg-chain-1 上,Gas 直接对应 umsg 成本,低效的存储模式、冗余的跨合约调用和不合理的查询逻辑会显著推高用户费用。

AI Agent 生成的合约可能出现的典型低效模式:

核心目标:降低用户成本、提高交易成功率(不超过 50,000,000 区块 Gas 上限)、提升区块吞吐量。

优化潜在收益:经验表明,系统性的 Gas 优化可为用户节省 60–90% 的合约交互成本。

1.2 MSG Chain Gas 模型

总 Gas = 基础交易 Gas + Wasm 执行 Gas + 存储 Gas + 跨合约调用 Gas
组件 说明 典型消耗
基础交易 签名验证、消息序列化 ~50,000 – 100,000
Wasm 执行 合约代码执行 按指令计费
存储访问 KV 读写 读 ~5,000,写 ~30,000
跨合约调用 Submessage + Reply 每次 ~30,000 – 100,000
GAS_CONFIG = {
    'prices': {
        'price': '1000000000000000000attoMSG',
    },
    'block_gas_limit': 50000000,
    'block_time': 5,
}

COSMWASM_GAS_COSTS = {
    'store_code':          '~300,000 gas per KB',
    'instantiate':         '~100,000 gas base',
    'execute_read':        '~50,000 gas',
    'execute_write':       '~150,000 gas',
    'execute_complex':     '~300,000 – 1,000,000 gas',
    'query_simple':        '~10,000 gas',
    'query_complex':       '~50,000 – 200,000 gas',
    'ibc_transfer':        '~200,000 gas',
    'reply_handler':       '~50,000 – 100,000 gas',
    'submessage':          '~30,000 gas base',
    'storage_read_per_kb': '~5,000 gas',
    'storage_write_per_kb': '~30,000 gas',
}

def estimate_tx_cost(gas_used: int, price: str = 'average') -> str:
    p = {'low': 1_000_000_000, 'average': 1_000_000_000, 'high': 1_000_000_000}
    cost = gas_used * p[price]
    return f"{cost} attoMSG (≈ {cost / 1_000_000_000} MSG)"

1.3 优化原则速览


2. Gas 模型基础

2.1 Wasm 指令级 Gas

CosmWasm 的 WasmVM 对指令分级计费:

指令类别 示例 每指令 Gas
简单算术 i32.add, i64.sub 1
复杂算术 i64.mul, i64.div 5
内存操作 i32.load, i64.store 10
控制流 br, call 2
主机函数 cosmwasm_* 调用 50–500

2.2 存储 Gas 公式

CosmWasm 的 KV 存储基于 Merkle 树(IAVL),每次写入涉及树路径更新。

fn storage_write_gas(key_len: usize, val_len: usize, is_new: bool) -> u64 {
    let base = 15000;
    let key_gas = key_len as u64 * 100;
    let val_gas = val_len as u64 * 20;
    let penalty = if is_new { 10000 } else { 0 };
    base + key_gas + val_gas + penalty
}

fn storage_read_gas(key_len: usize) -> u64 {
    3000 + key_len as u64 * 50
}

关键洞察:

2.3 CosmWasm Gas 计费流水线

一笔交易从提交到确认经历的 Gas 计费阶段:

1. CheckTx(mempool 验证)
   ├── 反序列化交易:    ~5,000 Gas
   ├── 验证签名:        ~10,000 Gas
   ├── 基本格式检查:    ~5,000 Gas
   └── 非执行验证:      通过则计入区块

2. DeliverTx(实际执行)
   ├── WasmVM 初始化:   ~15,000 Gas
   ├── 消息路由:        ~5,000 Gas
   ├── 合约执行:        ~50,000 – ∞ Gas(取决于逻辑)
   │   ├── 存储读取:    每次 ~5,000 Gas
   │   ├── 存储写入:    每次 ~30,000 Gas
   │   ├── Wasm 指令:   按条计费
   │   └── 主机函数:    与链交互每次 50–500 Gas
   ├── 子消息(可选):   ~30,000 – 100,000 Gas/次
   ├── Reply(可选):    ~50,000 – 100,000 Gas
   └── 序列化 Response: ~5,000 Gas

3. 后处理
   ├── 事件日志写入:    ~2,000 Gas/事件
   └── 手续费扣除:      ~3,000 Gas

理解此流水线有助于 AI Agent 识别 Gas 热点:存储操作和跨合约调用占全部 Gas 的 70–90%。

2.3 Gas 估算模型

def estimate_execute_gas(
    reads: int, writes: int, submsgs: int,
    data_kb: int, complex: bool = False
) -> int:
    gas = 75000 + 30000  # base + wasm overhead
    gas += reads * 5000
    gas += writes * 30000
    gas += submsgs * 25000
    gas += data_kb * 10000
    if complex:
        gas = int(gas * 1.5)
    return gas

2.4 区块容量

每区块 50,000,000 Gas,不同操作类型的区块容量:

操作类型 单笔 Gas 每区块笔数 TPS
简单转账 100,000 500 100
存储写入 350,000 142 28
复杂操作 800,000 62 12
批量操作 2,000,000 25 5

3. 存储优化

3.1 Map vs Vector

// ❌ BAD: Vec 在 Item 中 — 修改任意元素需读写整个数组
pub struct InefficientContract {
    pub all_positions: Item<Vec<UserPosition>>,
    pub user_profiles: Map<&'static [u8], UserProfile>,  // value 过大
}

// ✅ GOOD: Map 的 O(1) 键值访问 + 冷热分离
pub struct EfficientContract {
    pub user_count: Item<u64>,
    pub user_balance: Map<&'static Addr, Uint128>,   // 高频 → 轻量
    pub user_stakes: Map<&'static Addr, Uint128>,
    pub user_positions: Map<(&'static Addr, u64), Position>,  // 分页
    pub user_position_count: Map<&'static Addr, u64>,
}

#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
pub struct Position {
    pub token: String,
    pub amount: Uint128,
    pub entry_price: Uint128,
    pub timestamp: u64,
}

核心原则:Vec 在 Item 中 → 每次修改读写全量;Map<K, V> → O(1) 访问,天然分页。

3.2 存储模式决策树

AI Agent 在选择存储模式时可参考以下决策树:

需要持久化存储?
├── 否 → 使用内存变量(零存储 Gas)
└── 是 → 是否为单个全局值?
    ├── 是 → Item<T>
    └── 否 → 数据量是否超过 100 条?
        ├── 否 → Vec 在 Item 中可接受
        └── 是 → Map<K, V>
            ├── 需要二级索引?
            │   ├── 是 → IndexedMap(方便但写入略贵)
            │   └── 否 → Map + prefix 迭代
            └── 需要复合键?
                ├── 是 → Map<(K1, K2), V>
                └── 否 → Map<K, V>

3.3 键设计模式

// ✅ 短键名 + 前缀枚举
pub mod prefixes {
    pub const BALANCE: &[u8] = b"b";
    pub const STAKE:   &[u8] = b"s";
    pub const REWARD:  &[u8] = b"r";
    pub const ORDER:   &[u8] = b"o";
}

// ✅ 复合键实现分区
const BALANCE: Map<(&Addr, &Addr), Uint128> = Map::new("b");

// ❌ 长键名浪费 Gas
// const BALANCE: Map<String, Uint128> = Map::new("user_balance_for_each_token_pair");

// ✅ 辅助 Map 替代 IndexedMap(按需索引,节省写入 Gas)
const USERS_BY_TOKEN: Map<(&Addr, &Addr), bool> = Map::new("ubt");

pub fn set_balance(store: &mut dyn Storage, user: &Addr, token: &Addr, amount: Uint128) -> StdResult<()> {
    BALANCE.save(store, (user, token), &amount)?;
    if amount > Uint128::zero() {
        USERS_BY_TOKEN.save(store, (token, user), &true)?;
    } else {
        USERS_BY_TOKEN.remove(store, (token, user));
    }
    Ok(())
}

3.4 Snapshot 模式:减少重复读取

当合约需要多次读取同一数据时,使用 Snapshot 模式缓存结果:

// ✅ GOOD: Snapshot 模式 — 一次读取,多次使用
pub fn execute_complex_operation(
    deps: DepsMut,
    _env: Env,
    info: MessageInfo,
) -> StdResult<Response> {
    let user = &info.sender;

    // 只读取一次 UserCore
    let snapshot = USER_CORE.load(deps.storage, user)?;
    // 后续所有操作使用内存中的 snapshot,不触及存储

    let available = snapshot.balance - snapshot.staked;
    // ... 使用 available 进行计算 ...
    // ... 使用 snapshot 的多个字段 ...

    // 最后写回(如果修改了)
    USER_CORE.save(deps.storage, user, &snapshot)?;

    Ok(Response::new())
}

// ❌ BAD: 每次需要数据都重新读取存储
pub fn bad_complex_operation(
    deps: DepsMut,
    _env: Env,
    info: MessageInfo,
) -> StdResult<Response> {
    let user = &info.sender;

    // 第一次读取
    let balance = USER_CORE.load(deps.storage, user)?.balance;
    // 第二次读取(相同的存储访问!)
    let staked = USER_CORE.load(deps.storage, user)?.staked;
    // ... 更多操作再次读取 ...

    Ok(Response::new())
}

关键点:SnapShot 模式将 N 次存储读取降为 1 次,对于有 3–5 个字段的结构体可节省 60–80% 的读取 Gas。

3.5 数据结构精简

// ❌ BAD: 大而全的结构体(序列化 ~350+ 字节 → ~12,000 Gas/次)
pub struct FullUserData {
    pub address: String, pub email: String, pub display_name: String,
    pub avatar_url: String, pub bio: String,
    pub balance: Uint128, pub staked: Uint128, pub rewards: Uint128,
    pub last_active: u64, pub created_at: u64, pub is_verified: bool,
    pub referral_count: u32, pub flags: Vec<bool>,
}

// ✅ GOOD: 冷热数据分离
// 高频(每次 execute 都读)
pub struct UserCore {
    pub balance: Uint128,    // 16 字节
    pub staked: Uint128,     // 16 字节
    pub rewards: Uint128,    // 16 字节
    pub flags: u32,          // 4 字节(位标志)
}
// ~60 字节 → ~3,000 Gas

// 低频(仅特定查询)
pub struct UserProfile {
    pub email: String, pub display_name: String, pub avatar_url: String, pub bio: String,
}

// ✅ 使用位标志代替 Vec<bool>
pub struct UserFlags(u32);
impl UserFlags {
    pub const IS_VERIFIED: u32 = 1 << 0;
    pub const IS_FROZEN: u32 = 1 << 1;
    pub fn set(&mut self, flag: u32) { self.0 |= flag; }
    pub fn has(&self, flag: u32) -> bool { self.0 & flag != 0 }
}

// ✅ 最小数值类型
let _: u64 = 42;          // 8 字节 ✓
let _: Uint128 = Uint128::zero(); // 16 字节 ✓(支持 ~3.4e38)
// ❌ Uint256 不必要地消耗双倍空间

3.6 批量操作模式

// ✅ GOOD: 批量消息 — 单次消息处理 N 个操作
#[cw_serde]
pub enum ExecuteMsg {
    BatchTransfer { recipients: Vec<(Addr, Uint128)> },
    BatchUpdatePositions { updates: Vec<PositionUpdate> },
}

// ✅ GOOD: 仅值变化时写入
pub fn update_balance(store: &mut dyn Storage, user: &Addr, new: Uint128) -> StdResult<()> {
    let old = BALANCES.load(store, user).unwrap_or_default();
    if new != old {
        BALANCES.save(store, user, &new)?;  // 值未变 → 跳过写入
    }
    Ok(())
}

3.7 分页策略

use cw_storage_plus::Bound;

const MAX_PAGE_SIZE: u32 = 100;
const DEFAULT_PAGE_SIZE: u32 = 30;

// ✅ GOOD: 分页查询 — Gas 不随数据量增长
pub fn query_positions(
    store: &dyn Storage, user: &Addr,
    start_after: Option<u64>, limit: Option<u32>,
) -> StdResult<Vec<(u64, Position)>> {
    let limit = limit.unwrap_or(DEFAULT_PAGE_SIZE).min(MAX_PAGE_SIZE) as usize;
    let start = start_after.map(Bound::exclusive);
    POSITIONS.prefix(user)
        .range(store, start, None, Order::Ascending)
        .take(limit)
        .collect::<StdResult<Vec<_>>>()
}

// ❌ BAD: 加载所有数据 — Gas 随数据量线性增长
pub fn bad_query_all(store: &dyn Storage, user: &Addr) -> StdResult<Vec<(u64, Position)>> {
    POSITIONS.prefix(user)
        .range(store, None, None, Order::Ascending)
        .collect::<StdResult<Vec<_>>>()
}

3.8 存储清理

// ✅ GOOD: 提供清理接口,删除无用存储条目
pub fn execute_clear_user(deps: DepsMut, info: MessageInfo) -> StdResult<Response> {
    let user = &info.sender;
    BALANCES.remove(deps.storage, user);
    let keys: Vec<u64> = POSITIONS.prefix(user)
        .keys(deps.storage, None, None, Order::Ascending)
        .collect::<StdResult<Vec<_>>>()?;
    for k in keys { POSITIONS.remove(deps.storage, (user, k))?; }
    POSITION_COUNT.remove(deps.storage, user);
    Ok(Response::new().add_attribute("action", "clear_user"))
}

// ✅ 缓存 TTL 模式
pub struct CachedData<T> {
    pub data: T,
    pub expires_at: u64,
}
pub const PRICE_CACHE: Map<&str, CachedData<Uint128>> = Map::new("pc");

pub fn get_cached_price(store: &dyn Storage, symbol: &str, height: u64) -> StdResult<Option<Uint128>> {
    if let Some(c) = PRICE_CACHE.may_load(store, symbol)? {
        if c.expires_at > height { return Ok(Some(c.data)); }
    }
    Ok(None)
}

3.9 存储优化优先级

1. ⭐ Map 替代 Vec           → 节省 60–80%
2. ⭐ 冷热数据分离            → 节省 40–60%
3. ⭐ 分页查询               → 避免 OOG
4. 短键名 + 前缀枚举          → 节省 10–20%
5. 条件写入(值不变不写)     → 节省 30–50%
6. 位标志替代 Vec<bool>       → 节省 20–30%
7. 最小数值类型              → 节省 10–15%
8. 清理无用数据              → 长期维护成本降低
9. 缓存高频查询              → 节省 50–70%
10. 批量操作                → 节省 40–60%

4. 消息传递优化

跨合约调用是 Gas 消耗的隐藏杀手。每次 Submessage 都涉及完整的消息生命周期:序列化、路由、目标合约执行、结果返回。AI Agent 必须理解这些调用的真实成本。

4.1 消息类型成本对比

消息类型                      | Gas 成本      | 说明
-----------------------------|--------------|-------------------------------------
CosmosMsg::Bank (转账)       | ~50,000      | 原生代币转账,最高效
WasmMsg::Execute (同合约)    | ~60,000      | 自调用,无跨合约开销
WasmMsg::Execute (跨合约)    | ~100,000+    | 目标合约执行 Gas
SubMsg (无 Reply)            | ~130,000+    | 基础 25,000 + 目标执行
SubMsg (ReplyOn::Success)    | ~200,000+    | 含 Reply 处理
SubMsg (ReplyOn::Always)     | ~250,000+    | 最高成本,谨慎使用
WasmMsg::Instantiate         | ~200,000+    | 创建新合约
IbcMsg::Transfer             | ~300,000+    | IBC 跨链转账

4.2 最小化跨合约调用

// ❌ BAD: 每条消息都触发跨合约调用
pub fn bad_mint(deps: DepsMut, info: MessageInfo, token_id: String, minter: String) -> StdResult<Response> {
    Ok(Response::new().add_message(WasmMsg::Execute {
        contract_addr: minter,
        msg: to_binary(&MintMsg::Mint { token_id })?,
        funds: vec![],
    }))
}

// ✅ GOOD: 批量跨合约调用
pub fn good_batch_mint(deps: DepsMut, info: MessageInfo, minter: String, ids: Vec<String>) -> StdResult<Response> {
    Ok(Response::new().add_message(WasmMsg::Execute {
        contract_addr: minter,
        msg: to_binary(&MintMsg::BatchMint { tokens: ids })?,
        funds: vec![],
    }))
}

// ✅ GOOD: 复合操作合并到同一合约
pub fn execute_deposit_stake_claim(deps: DepsMut, info: MessageInfo, amount: Uint128) -> StdResult<Response> {
    let user = &info.sender;
    // 在单次 execute 中完成三步 → 节省 2 次基础交易 Gas
    let mut core = USER_CORE.load(deps.storage, user)?;
    core.balance += amount;
    core.staked += amount;
    // claim rewards
    let rewards = core.rewards;
    core.rewards = Uint128::zero();
    core.balance += rewards;
    USER_CORE.save(deps.storage, user, &core)?;
    Ok(Response::new().add_attribute("action", "deposit_stake_claim"))
}

4.3 消息设计模式:合并 vs 拆分

AI Agent 在设计消息接口时,需要权衡灵活性(小消息)和效率(大消息):

// ✅ GOOD: 分层消息设计
// 提供细粒度消息(灵活)和粗粒度复合消息(高效)

#[cw_serde]
pub enum ExecuteMsg {
    // 细粒度 — 单独操作
    Deposit { amount: Uint128 },
    Stake { amount: Uint128 },
    Claim {},

    // ✅ 粗粒度 — 复合操作(推荐使用)
    DepositAndStake { amount: Uint128 },
    StakeAndClaim {},
    DepositStakeClaim { deposit: Uint128 },
}

// 在客户端中:默认推荐使用复合操作
// 前端 UI 可以根据用户行为模式推荐最合适的复合消息

决策原则:

4.4 Submessage Gas 核算

Submessage 成本 = 基础 ~25,000 + 序列化 ~5,000 + 目标执行 Gas + Reply ~75,000
// ✅ GOOD: 不需要 Reply 时使用 add_message 而非 add_submessage
// 节省 ~75,000 Gas/次

// ✅ ReplyOn 策略
let submsg = SubMsg {
    msg: msg.into(),
    gas_limit: Some(100_000),
    reply_on: ReplyOn::Success,  // 仅成功时触发,比 Always 省 Gas
    id: 1,
    payload: Binary::default(),
};

// ❌ ReplyOn::Always 是最昂贵的选项

4.5 Gas 限制策略

// ✅ GOOD: 为 Submessage 设置精确的 Gas 限制
// 好处:防止子消息 Out-of-Gas、提升父消息确定性

/// 根据操作类型返回推荐的 Gas 限制
pub fn suggested_gas_limit(op: &str) -> Option<u64> {
    match op {
        "transfer"       => Some(60_000),
        "swap"           => Some(200_000),
        "stake"          => Some(150_000),
        "batch_swap"     => Some(500_000),
        "oracle_query"   => Some(80_000),
        "nft_mint"       => Some(300_000),
        _                => None,  // 未知操作不设限制
    }
}

// ❌ BAD: 不设 Gas 限制
// 子消息可能消耗父消息的 Gas,导致整个交易 OOG
SubMsg {
    msg: msg.into(),
    gas_limit: None,  // ❌ 依赖默认限制,风险高
    reply_on: ReplyOn::Success,
    id: 1,
    payload: Binary::default(),
}

4.6 Reply 处理器优化

// ✅ GOOD: 轻量 Reply 处理器
pub fn reply_handler(deps: DepsMut, _env: Env, msg: Reply) -> StdResult<Response> {
    match msg.id {
        1 => {
            match msg.result {
                SubMsgResult::Ok(_) => Ok(Response::new().add_attribute("result", "ok")),
                SubMsgResult::Err(e) => Ok(Response::new().add_attribute("error", e)),
            }
        }
        _ => Err(StdError::generic_err("Unknown reply id")),
    }
}

// ❌ BAD: Reply 中执行复杂逻辑 — 容易 OOG
// ❌ BAD: Reply 中再次读写大量存储

4.7 子消息 Gas 限制

// ✅ GOOD: 设置明确的 Gas 限制,防止子消息消耗过多 Gas
pub enum GasBudget {
    SimpleRead = 50_000,
    SimpleWrite = 100_000,
    ComplexOp = 300_000,
    BatchOp = 500_000,
}

pub fn budgeted_submsg(msg: impl Into<CosmosMsg<Empty>>, budget: GasBudget) -> SubMsg {
    SubMsg {
        msg: msg.into(),
        gas_limit: Some(budget as u64),
        reply_on: ReplyOn::Error,  // 仅失败时回复
        id: 0,
        payload: Binary::default(),
    }
}

5. 查询优化

查询不直接消耗用户 Gas,但消耗节点资源并影响用户体验。高效的查询设计是合约质量的重要指标。

5.1 查询 Gas 对比

查询类型                     | Gas 消耗         | 数据量 100 条时
----------------------------|------------------|-----------------
O(1) 直接键查询              | ~5,000           | ~5,000
O(log N) 范围查询(分页)     | ~35,000 + N*2,000 | ~235,000
O(N) 全表扫描                | ~5,000 * N        | ~500,000
O(N*M) 嵌套查询              | ~5,000 * N * M    | OOG ❌

5.2 批量查询

// ❌ BAD: N 次独立存储访问 = N 倍读取 Gas
pub fn bad_snapshot(deps: Deps, user: Addr) -> StdResult<UserSnapshot> {
    let balance = BALANCES.load(deps.storage, &user)?;
    let stake = STAKES.load(deps.storage, &user)?;
    let rewards = REWARDS.load(deps.storage, &user)?;
    Ok(UserSnapshot { balance, stake, rewards })
}

// ✅ GOOD: 聚合加载函数 — 单次调用获取所有相关数据
impl UserSnapshot {
    pub fn load(store: &dyn Storage, user: &Addr) -> StdResult<Self> {
        Ok(Self {
            balance: BALANCES.load(store, user).unwrap_or_default(),
            stake: STAKES.load(store, user).unwrap_or_default(),
            rewards: REWARDS.load(store, user).unwrap_or_default(),
        })
    }
}

5.3 SmartQuery vs RawQuery

// ✅ GOOD: 合约间通信优先使用 RawQuery(跳过反序列化)
pub fn get_raw_balance(
    querier: &QuerierWrapper, contract: String, user: &Addr,
) -> StdResult<Vec<u8>> {
    let key = [b"b", b"\x00", user.as_bytes()].concat();
    querier.query(&QueryRequest::Wasm(WasmQuery::Raw {
        contract_addr: contract,
        key: key.into(),
    }))
}
特性 SmartQuery RawQuery
Gas 较高(反序列化) 较低(原始字节)
类型安全 是 需手动解析
适用场景 API/前端 合约内部查询

5.4 查询缓存

// ✅ GOOD: 缓存高频查询结果(如价格预言机)
pub struct CachedPrice {
    pub price: Uint128,
    pub updated_at: u64,
}

pub fn get_price(store: &dyn Storage, querier: &QuerierWrapper, symbol: &str, height: u64, ttl: u64) -> StdResult<Uint128> {
    if let Some(cached) = PRICE_CACHE.may_load(store, symbol)? {
        if height < cached.updated_at + ttl {
            return Ok(cached.price);  // 缓存命中 → 零外部查询
        }
    }
    let price = query_oracle(querier, symbol)?;
    PRICE_CACHE.save(store, symbol, &CachedPrice { price, updated_at: height })?;
    Ok(price)
}

5.5 查询复杂度管理

// ❌ BAD: O(N) 全表扫描 — 10,000 用户时消耗 50,000,000+ Gas
pub fn bad_all_balances(deps: Deps) -> StdResult<Vec<(Addr, Uint128)>> {
    BALANCES.range(deps.storage, None, None, Order::Ascending).collect::<StdResult<Vec<_>>>()
}

// ✅ GOOD: O(1) 直接查询 + 分页列表
pub fn good_balance(deps: Deps, user: Addr) -> StdResult<Uint128> {
    BALANCES.load(deps.storage, &user)  // O(1) — 一次存储读取
}

// ✅ GOOD: 聚合计数器避免实时统计
pub struct AggregatedStats {
    pub high_balance_count: Item<u64>,
    pub total_balance: Item<Uint128>,
}

impl AggregatedStats {
    pub fn on_balance_change(&self, store: &mut dyn Storage, old: Uint128, new: Uint128) -> StdResult<()> {
        let mut total = self.total_balance.load(store)?;
        total = total - old + new;
        self.total_balance.save(store, &total)?;
        Ok(())
    }
}

5.6 查询接口设计原则

好的查询接口设计不仅提升用户体验,还降低节点负载:

// ✅ GOOD: 灵活的查询接口
#[cw_serde]
pub enum QueryMsg {
    // 单用户 O(1) 查询
    Balance { user: String },

    // 批量用户查询(明确指定用户集,有限范围)
    Balances { users: Vec<String> },

    // 分页查询所有用户
    AllBalances {
        start_after: Option<String>,
        limit: Option<u32>,
    },

    // 聚合统计 — 由计数器提供,无需遍历
    Stats {},
}

// ✅ GOOD: 查询响应精简,只含必要字段
#[derive(Serialize, Deserialize)]
pub struct UserSummaryResponse {
    pub balance: Uint128,
    pub staked: Uint128,
    // ❌ 不包含 full_history: Vec<Transaction> — 可通过专用查询获取
}

5.7 查询性能监控

AI Agent 应在合约中内置查询性能监控点:

/// 查询性能日志(用于 AI Agent 分析)
pub struct QueryMetrics {
    pub query_type: &'static str,
    pub items_returned: u32,
    pub storage_accesses: u32,
}

impl QueryMetrics {
    pub fn record(&self) {
        // 在测试环境中输出查询性能指标
        // 生产环境中建议移除或通过 feature flag 控制
        debug!(
            "Query: type={}, items={}, storage_accesses={}",
            self.query_type, self.items_returned, self.storage_accesses,
        );
    }
}
1. 最小数据 — 只返回需要的字段
2. 分页默认 — 所有列表查询支持 start_after + limit
3. O(1) 优先 — 直接键访问优先于遍历
4. 聚合缓存 — 高频统计值预计算
5. 深度控制 — 限制嵌套查询

6. 合约实例化与升级优化

6.1 实例化优化

// ❌ BAD: 实例化时预创建大量数据
pub fn bad_instantiate(deps: DepsMut, _env: Env, _info: MessageInfo, msg: InstantiateMsg) -> StdResult<Response> {
    for token in &msg.initial_tokens {
        TOKEN_INFO.save(deps.storage, token, &TokenInfo::default())?;  // 可能 1000+ 条
    }
    Ok(Response::new())
}

// ✅ GOOD: 惰性初始化 — 需要时创建
pub fn good_instantiate(deps: DepsMut, _env: Env, info: MessageInfo, msg: InstantiateMsg) -> StdResult<Response> {
    CONFIG.save(deps.storage, &Config { owner: info.sender, paused: false })?;
    TOKEN_COUNT.save(deps.storage, &0)?;  // 计数器,而非预创建
    Ok(Response::new().add_attribute("action", "instantiate"))
}

// ✅ GOOD: 工厂模式复用代码
pub fn execute_create_child(deps: DepsMut, info: MessageInfo, code_id: u64, label: String, msg: Binary) -> StdResult<Response> {
    Ok(Response::new().add_message(WasmMsg::Instantiate {
        admin: Some(info.sender.to_string()),
        code_id, msg, label,
        funds: vec![],
    }).add_attribute("action", "create_child"))
}

6.2 代码大小优化

合约代码大小直接影响 store_code 的 Gas 成本(~300,000 Gas/KB):

# Cargo.toml: 优化编译选项
[profile.release]
opt-level = "z"     # 优化大小
lto = true          # 链接时优化
codegen-units = 1   # 单代码生成单元
strip = true        # 移除符号信息
panic = "abort"     # 移除 panic 格式化
// ✅ GOOD: 避免不必要的依赖增加合约体积
// ❌ BAD: 引入整个库仅使用一个函数
// use cosmwasm_std::Uint256;  // 不必要时避免
// use cw20::Cw20Coin;          // 非 cw20 合约时避免

// ✅ GOOD: 使用 #[cfg(not(target_arch = "wasm32"))] 隔离测试代码
#[cfg(not(target_arch = "wasm32"))]
mod tests {
    // 测试代码不会编译进 Wasm 二进制
}

6.3 迁移优化

// ❌ BAD: 全量迁移 — 大数据量时 OOG
pub fn bad_migrate(deps: DepsMut, _env: Env, _msg: MigrateMsg) -> StdResult<Response> {
    let old_data: Vec<_> = OLD_STORAGE.range(deps.storage, None, None, Order::Ascending).collect::<StdResult<Vec<_>>>()?;
    for (key, old) in old_data {
        NEW_STORAGE.save(deps.storage, key, &NewFormat::from_old(old))?;
    }
    Ok(Response::new())
}

// ✅ GOOD: 惰性迁移 — 仅更新版本号,数据按需转换
pub fn good_migrate(deps: DepsMut, _env: Env, _msg: MigrateMsg) -> StdResult<Response> {
    VERSION.save(deps.storage, &CONTRACT_VERSION)?;
    Ok(Response::new().add_attribute("action", "migrate").add_attribute("version", CONTRACT_VERSION))
}

// ✅ GOOD: 按需迁移模式
pub fn migrate_on_access(store: &mut dyn Storage, user: &Addr) -> StdResult<UserDataNew> {
    if let Some(data) = USER_DATA_NEW.may_load(store, user)? { return Ok(data); }
    let old = USER_DATA_OLD.load(store, user)?;
    let new = UserDataNew::from_old(&old);
    USER_DATA_NEW.save(store, user, &new)?;
    USER_DATA_OLD.remove(store, user);
    Ok(new)
}

6.4 升级友好的合约设计

AI Agent 应从一开始就考虑合约的可升级性,避免未来迁移的高额 Gas:

// ✅ GOOD: 使用版本号跟踪
pub const CONTRACT_VERSION: &str = "1.2.0";
pub const VERSION: Item<String> = Item::new("v");

// ✅ GOOD: 配置可升级(非硬编码)
pub struct Config {
    pub owner: Addr,
    pub paused: bool,
    pub fee_rate: u64,        // 可变
    pub min_stake: Uint128,   // 可变
    pub emergency_addr: Option<Addr>, // 可变
}

// ✅ GOOD: 预留迁移数据槽
pub struct MigrateState {
    pub previous_version: String,
    pub current_version: String,
    pub migrated_at: u64,
    pub migrated_count: u64,
}
pub const MIGRATE_STATE: Item<MigrateState> = Item::new("ms");

// ❌ BAD: 硬编码常量 — 修改需要完整迁移
const FEE_RATE: u64 = 25;            // 修改此值需重新部署
const MIN_STAKE: Uint128 = Uint128::new(1000);  // 同前

6.5 迁移策略对比

策略 Gas 消耗 适用场景
全量迁移 O(N) — 极易 OOG 数据量 < 100 条
惰性迁移 O(1) 推荐默认
按需迁移 O(1) per access 最推荐,用户无感

7. AI Agent Gas 最佳实践

7.1 AI Agent 代码生成策略

AI Agent 在生成 CosmWasm 合约代码时,应遵循以下策略确保 Gas 效率:

策略 1: "默认高效"原则
  默认使用 Map 而非 Vec,默认使用分页,默认使用惰性初始化。
  仅在确需灵活性的场景使用替代模式。

策略 2: "冷热分离"原则
  分析数据结构访问频率,将高频字段(余额、质押)和低频字段(简介、设置)分离。
  同一 execute 中多次读取的结构体使用 Snapshot 模式。

策略 3: "批量优先"原则
  为所有涉及循环的操作提供批量接口。
  复合操作(如 存款+质押+领取)提供单一消息入口。

策略 4: "懒惰求值"原则
  实例化时不创建默认数据,迁移时不转换所有数据。
  按需创建、按需转换、按需加载。

策略 5: "估算先行"原则
  在生成代码前使用 Gas 估算器评估关键路径的 Gas 消耗。
  确保所有路径不会接近区块 Gas 上限 50,000,000。

策略 6: "测试验证"原则
  为每个合约编写 Gas 基准测试。
  持续对比优化前后的 Gas 消耗变化。

7.2 Gas 优化检查清单

存储优化:
☐ 使用 Map 替代 Vec 存储集合
☐ 所有列表查询实现分页(default=30, max=100)
☐ 短键名前缀(单字节最佳)
☐ 冷热数据分离(大结构体拆分)
☐ 位标志替代多个 bool 字段
☐ 最小数值类型(u64 > Uint128 > Uint256)
☐ 值未变化时不写入
☐ 及时清理无用存储

消息传递:
☐ 批量操作封装为单条消息
☐ 无 Reply 需求时用 add_message 而非 add_submessage
☐ Submessage 设合理的 Gas 限制
☐ ReplyOn 避免使用 Always
☐ 合并多次跨合约调用为批量

查询优化:
☐ 优先 RawQuery 进行内部查询
☐ 高频查询结果缓存
☐ 响应只含必要字段
☐ 避免 O(N) 遍历 → 用分页或聚合计数器

合约设计:
☐ 实例化使用惰性初始化
☐ 迁移使用惰性/按需模式
☐ 工厂模式复用合约代码
☐ 关键参数可配置(存储在 Item 中)

7.3 Gas 消耗基准

操作 Gas 消耗
Item::load (8 字节) ~3,000
Item::save (8 字节) ~18,000
Map::load (短键, 16 字节值) ~5,000
Map::save (短键, 16 字节值) ~30,000
Map::range (前 10 条) ~35,000
Map::range (100 条) ~200,000
Submessage (无 Reply) ~25,000
Submessage (有 Reply) ~100,000
IBC Transfer ~200,000
JSON 序列化 (1 KB) ~10,000
JSON 反序列化 (1 KB) ~15,000

参考: msg-chain-1 flat 1,000,000,000 attoMSG/gas → 典型转账 ~150,000 Gas = 150,000,000,000,000,000 attoMSG (≈0.15 MSG)

7.4 Gas 测试与验证

AI Agent 在生成合约后,应通过以下步骤验证 Gas 效率:

验证流程:
1. 静态分析: 用 ContractPatternAnalyzer 检查代码模式
2. 预估: 使用 GasEstimator 估算关键路径 Gas
3. 基准测试: 在测试网执行并记录实际 Gas
4. 对比: 实际 Gas 与估算值偏差 < 20%
5. 优化: 对超出预算的路径进行重构
6. 回归: 确认优化未引入新问题
#[cfg(test)]
mod gas_benchmarks {
    use super::*;
    use cosmwasm_std::testing::{mock_dependencies, mock_env, mock_info};

    /// Gas 基准测试 — 确保关键路径在预算内
    #[test]
    fn benchmark_batch_transfer() {
        let mut deps = mock_dependencies();
        let env = mock_env();

        // 准备:设置 10 个用户的余额
        let user = Addr::unchecked("sender");
        USER_CORE.save(&mut deps.storage, &user, &UserCore {
            balance: Uint128::new(1_000_000),
            staked: Uint128::zero(),
            flags: 0,
        }).unwrap();

        // 执行:批量转账给 20 个接收方
        let recipients: Vec<(String, Uint128)> = (0..20)
            .map(|i| (format!("user{}", i), Uint128::new(1000)))
            .collect();

        let info = mock_info("sender", &[]);
        let result = execute_batch_transfer(
            deps.as_mut(), info, recipients
        );

        assert!(result.is_ok());

        // 验证:批量转账消耗应 < 单次转账 * 20
        // 实际的 Gas 测量需要在集成测试环境中进行
        // 这里作为结构性验证
    }
}

7.5 msg-chain-1 Gas 速查表

"""
msg-chain-1 Gas 速查表

Gas 价格:
  flat:  1,000,000,000 attoMSG/gas  → 150k Gas = 150,000,000,000,000,000 attoMSG (≈0.15 MSG)

区块容量:
  最大 Gas: 50,000,000
  出块时间: 5 秒
  理论 TPS: ~100(简单转账场景)

常见操作成本:
  操作                    Gas       low(umsg)   avg(umsg)   high(umsg)
  ─────────────────────────────────────────────────────────────────
  简单转账              150,000    1,500       3,750       6,000
  代币交换              500,000    5,000       12,500      20,000
  质押/解押            250,000    2,500       6,250       10,000
  领取奖励             200,000    2,000       5,000       8,000
  批量转账 (20 笔)      600,000    6,000       15,000      24,000
  合约部署 (100KB)    30,000,000  300,000     750,000     1,200,000

阈值警报:
  ⚡ Gas < 200,000:   绿色 — 高效
  ⚡ Gas 200k–500k:   黄色 — 正常
  ⚡ Gas 500k–1M:     橙色 — 需要审查
  ⚡ Gas 1M–5M:       红色 — 建议优化
  ⚡ Gas > 5M:        危险 — 极可能 OOG
"""

7.6 AI Agent Gas 估算器

class GasEstimator:
    STORAGE_READ = 5_000
    STORAGE_WRITE = 30_000
    ITER_STEP = 2_000
    SUBMSG = 25_000
    REPLY = 75_000
    BASE = 75_000
    WASM = 30_000

    def estimate(self, reads=0, writes=0, iter_steps=0, submsgs=0, replies=0, complex_logic=False, data_kb=0):
        gas = self.BASE + self.WASM
        gas += reads * self.STORAGE_READ
        gas += writes * self.STORAGE_WRITE
        gas += iter_steps * self.ITER_STEP
        gas += submsgs * self.SUBMSG
        gas += replies * self.REPLY
        gas += data_kb * 10_000
        if complex_logic:
            gas = int(gas * 1.5)
        return gas

    def cost_attoMSG(self, gas, price='average'):
        p = {'low': 1_000_000_000, 'average': 1_000_000_000, 'high': 1_000_000_000}
        return gas * p[price]

7.7 AI Agent 自动分析工具

class ContractPatternAnalyzer:
    PATTERNS = {
        "full_table_scan": {
            "risk": "HIGH",
            "suggestion": "Add pagination with start_after + limit",
            "saving": "up to 90%",
        },
        "vec_in_item": {
            "risk": "HIGH",
            "suggestion": "Replace Vec with Map<K, V>",
            "saving": "up to 80%",
        },
        "large_struct": {
            "risk": "MEDIUM",
            "suggestion": "Split into hot/cold structs",
            "saving": "up to 60%",
        },
        "missing_pagination": {
            "risk": "MEDIUM",
            "suggestion": "Add start_after and limit to list queries",
            "saving": "prevents OOG",
        },
    }

    @classmethod
    def analyze(cls, code: str):
        findings = []
        if ".range(" in code and "start_after" not in code:
            findings.append({**cls.PATTERNS["full_table_scan"]})
        if "Item<Vec<" in code:
            findings.append({**cls.PATTERNS["vec_in_item"]})
        if "Uint256" in code or code.count("String,") > 5:
            findings.append({**cls.PATTERNS["large_struct"]})
        if "fn query" in code.lower() and "start_after" not in code:
            findings.append({**cls.PATTERNS["missing_pagination"]})
        return findings

7.8 msg-chain-1 专属提示

"""
推荐 Gas 预算(msg-chain-1):
  简单转账/更新:     100,000 – 200,000 Gas
  质押/领取:         200,000 – 400,000 Gas
  交换/AMM:          400,000 – 800,000 Gas
  批量操作 (>10):    500,000 – 2,000,000 Gas
  合约部署:          ~3,000,000 + 代码大小

Gas 节省速算:
  减少 1 次存储写入     ≈ 节省 30,000 Gas ≈ 0.75 umsg
  减少 1 次存储读取     ≈ 节省  5,000 Gas ≈ 0.125 umsg
  合并 2 次消息为 1 次   ≈ 节省 75,000 Gas ≈ 1.875 umsg
  缓存外部查询 1 次     ≈ 节省 50,000 Gas ≈ 1.25 umsg
  避免全表扫描 1 次     ≈ 节省 ∞ Gas(可能 OOG)

AI Agent 自我检查:
  ✓ Map 是否使用短前缀?
  ✓ 所有 range 是否有 limit?
  ✓ 大结构体是否已拆分?
  ✓ 子消息是否设置 Gas 限制?
  ✓ 迁移是否使用惰性模式?
  ✓ 实例化是否避免预创建?
  ✓ 数值类型是否最小?
"""

7.9 完整合约模板

use cosmwasm_std::{
    entry_point, Binary, Deps, DepsMut, Env, MessageInfo,
    Response, StdResult, StdError, Addr, Uint128,
    Order, to_binary, from_binary,
};
use cw_storage_plus::{Item, Map, Bound};
use serde::{Deserialize, Serialize};

// --- Gas 高效状态 ---
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
pub struct Config { pub owner: Addr, pub paused: bool, pub fee_rate: u64 }

#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
pub struct UserCore { pub balance: Uint128, pub staked: Uint128, pub flags: u32 }

#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
pub struct Position { pub token: String, pub amount: Uint128, pub entry_price: Uint128 }

pub const CONFIG: Item<Config> = Item::new("c");
pub const USER_COUNT: Item<u64> = Item::new("n");
pub const USER_CORE: Map<&Addr, UserCore> = Map::new("u");
pub const POSITIONS: Map<(&Addr, u64), Position> = Map::new("p");
pub const POSITION_COUNT: Map<&Addr, u64> = Map::new("pc");

// --- 消息 ---
#[cw_serde]
pub enum ExecuteMsg {
    BatchTransfer { recipients: Vec<(String, Uint128)> },
    DepositAndStake { amount: Uint128 },
}

#[cw_serde]
pub enum QueryMsg {
    UserOverview { user: String },
    UserPositions { user: String, start_after: Option<u64>, limit: Option<u32> },
}

// --- 实例化(惰性)---
#[entry_point]
pub fn instantiate(deps: DepsMut, _env: Env, info: MessageInfo, msg: InstantiateMsg) -> StdResult<Response> {
    CONFIG.save(deps.storage, &Config { owner: deps.api.addr_validate(&msg.owner)?, paused: false, fee_rate: msg.fee_rate })?;
    USER_COUNT.save(deps.storage, &0)?;
    Ok(Response::new().add_attribute("chain", "msg-chain-1"))
}

// --- 执行(批量 + 复合)---
#[entry_point]
pub fn execute(deps: DepsMut, _env: Env, info: MessageInfo, msg: ExecuteMsg) -> StdResult<Response> {
    match msg {
        ExecuteMsg::BatchTransfer { recipients } => {
            let sender = &info.sender;
            let total: Uint128 = recipients.iter().map(|(_, a)| a).sum();
            let mut core = USER_CORE.load(deps.storage, sender)?;
            if core.balance < total { return Err(StdError::generic_err("Insufficient balance")); }
            core.balance -= total;
            USER_CORE.save(deps.storage, sender, &core)?;
            for (addr_str, amount) in &recipients {
                let r = deps.api.addr_validate(addr_str)?;
                let mut rc = USER_CORE.load(deps.storage, &r).unwrap_or(UserCore { balance: Uint128::zero(), staked: Uint128::zero(), flags: 0 });
                rc.balance += *amount;
                USER_CORE.save(deps.storage, &r, &rc)?;
            }
            Ok(Response::new().add_attribute("action", "batch_transfer").add_attribute("count", recipients.len().to_string()))
        }
        ExecuteMsg::DepositAndStake { amount } => {
            let user = &info.sender;
            let mut core = USER_CORE.load(deps.storage, user)?;
            core.balance += amount;
            core.staked += amount;
            USER_CORE.save(deps.storage, user, &core)?;
            Ok(Response::new().add_attribute("action", "deposit_and_stake").add_attribute("amount", amount))
        }
    }
}

// --- 查询(聚合 + 分页)---
#[entry_point]
pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> StdResult<Binary> {
    match msg {
        QueryMsg::UserOverview { user } => {
            let user = deps.api.addr_validate(&user)?;
            let core = USER_CORE.load(deps.storage, &user).unwrap_or(UserCore { balance: Uint128::zero(), staked: Uint128::zero(), flags: 0 });
            let count = POSITION_COUNT.load(deps.storage, &user).unwrap_or(0);
            to_binary(&(core.balance, core.staked, core.flags, count))
        }
        QueryMsg::UserPositions { user, start_after, limit } => {
            let user = deps.api.addr_validate(&user)?;
            let limit = limit.unwrap_or(30).min(100) as usize;
            let start = start_after.map(Bound::exclusive);
            to_binary(&POSITIONS.prefix(&user).range(deps.storage, start, None, Order::Ascending).take(limit).collect::<StdResult<Vec<(u64, Position)>>>()?)
        }
    }
}

// --- 迁移(惰性)---
#[entry_point]
pub fn migrate(deps: DepsMut, _env: Env, _msg: Empty) -> StdResult<Response> {
    CONFIG.update(deps.storage, |mut c| { c.paused = false; Ok(c) })?;
    Ok(Response::new().add_attribute("action", "migrate"))
}

7.10 常见 Gas 陷阱速查

// 陷阱 1: 循环内重复读取存储
// ❌ BAD
for user in users {
    total += BALANCES.load(store, user)?;  // N 次存储读取
}
// ✅ GOOD: 先 collect 再计算(但注意数据量)
let balances: Vec<_> = users.iter().map(|u| BALANCES.load(store, u)).collect::<StdResult<Vec<_>>>()?;
let total = balances.iter().sum();

// 陷阱 2: 不必要的序列化
// ❌ BAD: 读取完整大结构体仅改一个字段
let mut data = BIG_DATA.load(store, key)?;
data.small_field = new_value;
BIG_DATA.save(store, key, &data)?;
// ✅ GOOD: 将 small_field 独立存储
SMALL_FIELD.save(store, key, &new_value)?;

// 陷阱 3: unwrap 导致 panic → OOG
// ❌ BAD
let val = SOME_ITEM.load(store).unwrap();
// ✅ GOOD
let val = SOME_ITEM.may_load(store)?;

// 陷阱 4: 全表遍历不加限制
// ❌ BAD
for item in MAP.range(store, None, None, Order::Ascending) { ... }
// ✅ GOOD
for item in MAP.range(store, start, end, Order::Ascending).take(100) { ... }

// 陷阱 5: 子消息不设 Gas 限制
// ❌ BAD
SubMsg { msg, gas_limit: None, ... };
// ✅ GOOD
SubMsg { msg, gas_limit: Some(200_000), ... };

7.11 核心原则总结

1. 存储最贵         → 减少读写 = 直接降 Gas
2. 批量 > 逐条      → 单次消息处理 N 个操作
3. 惰性 > 即时      → 按需创建,按需迁移
4. 短 > 长          → 短键名、小类型、精简结构
5. 聚合 > 遍历      → 计数器替代全表扫描
6. 本地 > 外部      → 合约内计算优于跨合约调用
7. 预估 > 盲写      → 生成代码前估算 Gas
8. 监控 > 猜测      → 持续跟踪,优化热点路径

在 msg-chain-1 上,一次优化查询仅消耗 ~15,000 Gas (≈0.375 umsg),
一次未优化的全表扫描可达 5,000,000+ Gas (≈125 umsg),
相差超过 300 倍。AI Agent 的每次 Gas 优化,都是为用户节省真金白银。

文档版本: v1.0 | 目标链: msg-chain-1 | 基础代币: umsg
虚拟机: CosmWasm (WasmVM) | 适用: AI Agent / 合约开发者