dApp Docs/智能合约可升级性与迁移指南
Development reference. Not independently verified for production.

MSG Chain (CosmWasm) 智能合约可升级性与迁移指南

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

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

1. 概述

1.1 为什么在 CosmWasm 中需要合约升级

区块链上的智能合约一旦部署,默认是不可变的。然而,在实际开发中,合约升级的需求不可避免:

MSG Chain 作为基于 CosmWasm 的链,采用 msg 作为地址前缀(bech32),链 ID 为 msg-chain-1,为开发者提供了灵活的合约升级机制。

1.2 三种升级方式对比

CosmWasm 提供三种主要的合约升级路径,各有优劣:

方式 优势 劣势 适用场景
migrate 原生迁移 保留同一合约地址,状态完全继承,原生支持 需要原合约预留迁移权限,版本管理需自理 标准升级场景
代理模式 (Proxy) 逻辑与状态分离,升级灵活,支持回退 Gas 开销略高,存储布局需谨慎设计 需要频繁升级的场景
instantiate2 确定性地址 地址可预测,无需迁移权限 状态不继承,需手动迁移数据 全新部署场景

1.3 Migrate Entry Point 详解

migrate 是 CosmWasm 最核心的升级入口点。当管理员对合约地址发起 MsgMigrateContract 交易时,链会:

  1. 验证发起者是否为合约管理员(Contract Admin)
  2. 验证新 Code ID 是否有效
  3. 反序列化 MigrateMsg
  4. 调用新代码的 migrate 入口点
  5. 保留合约地址和状态不变

关键特性:

// 合约管理员设置
// MsgUpdateAdmin 可在部署后更改管理员
// 如果管理员未设置,则合约不可迁移

1.4 Migrate 与 Proxy 的权衡决策

场景对比:
├── 小型合约 / 简单升级
│   └── ✅ 原生 migrate (最简单直接)
├── 大型系统 / 多合约协作
│   └── ✅ Proxy 模式 (升级灵活)
├── DAO 治理控制
│   └── ✅ 原生 migrate + DAO 作为管理员
└── 频繁升级 / 实验阶段
    └── ✅ Proxy 模式 (零成本切换逻辑)

2. 原生迁移模式

2.1 基础 Migrate Entry Point

以下是 MSG Chain 上标准合约的完整迁移入口点实现:

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

// ========== State ==========

#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
pub struct Config {
    pub owner: Addr,
    pub version: String,
    pub counter: u64,
    pub paused: bool,
}

pub const CONFIG: Item<Config> = Item::new("config");
pub const USERS: Map<&Addr, u64> = Map::new("users");

// ========== Messages ==========

#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
pub struct InstantiateMsg {
    pub owner: String,
}

#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
#[serde(rename_all = "snake_case")]
pub enum ExecuteMsg {
    Increment {},
    Decrement {},
    Reset { counter: u64 },
}

#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
#[serde(rename_all = "snake_case")]
pub enum QueryMsg {
    GetConfig {},
    GetCounter {},
}

#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
pub struct MigrateMsg {
    pub version: String,
}

// ========== Instantiate ==========

#[cfg_attr(not(feature = "library"), 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)?,
        version: "1.0.0".to_string(),
        counter: 0,
        paused: false,
    };
    CONFIG.save(deps.storage, &config)?;

    Ok(Response::new()
        .add_attribute("method", "instantiate")
        .add_attribute("owner", msg.owner)
        .add_attribute("version", "1.0.0"))
}

// ========== Execute ==========

#[cfg_attr(not(feature = "library"), entry_point)]
pub fn execute(
    deps: DepsMut,
    _env: Env,
    info: MessageInfo,
    msg: ExecuteMsg,
) -> StdResult<Response> {
    match msg {
        ExecuteMsg::Increment {} => execute_increment(deps),
        ExecuteMsg::Decrement {} => execute_decrement(deps),
        ExecuteMsg::Reset { counter } => execute_reset(deps, info, counter),
    }
}

fn execute_increment(deps: DepsMut) -> StdResult<Response> {
    CONFIG.update(deps.storage, |mut config| -> StdResult<_> {
        config.counter = config.counter.wrapping_add(1);
        Ok(config)
    })?;
    Ok(Response::new().add_attribute("method", "increment"))
}

fn execute_decrement(deps: DepsMut) -> StdResult<Response> {
    CONFIG.update(deps.storage, |mut config| -> StdResult<_> {
        config.counter = config.counter.wrapping_sub(1);
        Ok(config)
    })?;
    Ok(Response::new().add_attribute("method", "decrement"))
}

fn execute_reset(
    deps: DepsMut,
    info: MessageInfo,
    counter: u64,
) -> StdResult<Response> {
    let config = CONFIG.load(deps.storage)?;
    if info.sender != config.owner {
        return Err(StdError::generic_err("Unauthorized"));
    }
    CONFIG.update(deps.storage, |mut config| -> StdResult<_> {
        config.counter = counter;
        Ok(config)
    })?;
    Ok(Response::new().add_attribute("method", "reset"))
}

// ========== Query ==========

#[cfg_attr(not(feature = "library"), entry_point)]
pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> StdResult<Binary> {
    match msg {
        QueryMsg::GetConfig {} => to_binary(&query_config(deps)?),
        QueryMsg::GetCounter {} => to_binary(&query_counter(deps)?),
    }
}

fn query_config(deps: Deps) -> StdResult<Config> {
    CONFIG.load(deps.storage)
}

fn query_counter(deps: Deps) -> StdResult<u64> {
    let config = CONFIG.load(deps.storage)?;
    Ok(config.counter)
}

// ========== Migrate ==========

#[cfg_attr(not(feature = "library"), entry_point)]
pub fn migrate(deps: DepsMut, _env: Env, msg: MigrateMsg) -> StdResult<Response> {
    // 1. 版本检查:防止重复迁移
    let contract_info = deps.contract_info.clone();
    if contract_info.version == msg.version {
        return Err(StdError::generic_err(
            "Already at target version, migration skipped",
        ));
    }

    // 2. 语义版本比较(如需要)
    // 此处可集成 semver 库做严格版本比较

    // 3. 加载并更新配置
    CONFIG.update(deps.storage, |mut config| -> StdResult<_> {
        config.version = msg.version;
        Ok(config)
    })?;

    // 4. 日志记录迁移事件
    Ok(Response::new()
        .add_attribute("method", "migrate")
        .add_attribute("from_version", contract_info.version)
        .add_attribute("to_version", msg.version))
}

2.2 ContractInfo 版本管理

CosmWasm 在 DepsMut 中提供 contract_info 字段,对应链上存储的 ContractInfo 结构:

pub struct ContractInfo {
    pub code_id: u64,
    pub creator: Addr,
    pub admin: Option<Addr>,
    pub label: String,
    pub version: String,
    pub ibc_port_id: Option<String>,
}

版本管理最佳实践:

// ========== 版本管理工具函数 ==========

const VERSION_KEY: &str = "contract_version";

/// 在 instantiate 中设置初始版本
pub fn set_initial_version(deps: &mut DepsMut, version: &str) -> StdResult<()> {
    let mut info = deps.contract_info.clone();
    info.version = version.to_string();
    // 注意:contract_info 本身不可写,需要靠 migrate 入口更新
    // 实际上 ContractInfo.version 只能通过 MsgMigrateContract 更新
    // 这里只是一个记录方案
    deps.storage.set(VERSION_KEY.as_bytes(), version.as_bytes());
    Ok(())
}

/// 获取当前记录版本
pub fn get_recorded_version(storage: &dyn Storage) -> StdResult<String> {
    let version_bytes = storage
        .get(VERSION_KEY.as_bytes())
        .unwrap_or_default();
    if version_bytes.is_empty() {
        return Ok("0.0.0".to_string());
    }
    Ok(String::from_utf8(version_bytes).unwrap_or_default())
}

/// 严格的版本迁移检查(使用 semver)
pub fn assert_valid_migration(
    current: &str,
    target: &str,
) -> StdResult<()> {
    // 生产环境推荐使用 semver crate 做严格比较
    // let current_ver = Version::from_str(current)?;
    // let target_ver = Version::from_str(target)?;
    // if target_ver <= current_ver {
    //     return Err(StdError::generic_err("Migration must increase version"));
    // }
    if current == target {
        return Err(StdError::generic_err("Version unchanged"));
    }
    Ok(())
}

2.3 多版本迁移模式

当合约经历多个版本迭代时,需要设计可串联的迁移策略:

use cosmwasm_std::Storage;

// ========== 版本枚举 ==========

#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
pub enum ContractVersion {
    V1,
    V2,
    V3,
}

impl ContractVersion {
    pub fn as_str(&self) -> &'static str {
        match self {
            ContractVersion::V1 => "1.0.0",
            ContractVersion::V2 => "2.0.0",
            ContractVersion::V3 => "3.0.0",
        }
    }
}

// ========== 阶梯迁移入口 ==========

#[cfg_attr(not(feature = "library"), entry_point)]
pub fn migrate(deps: DepsMut, env: Env, msg: MigrateMsg) -> StdResult<Response> {
    let current_version = deps.contract_info.version.clone();
    let target_version = msg.version.clone();

    // 如果版本相同,跳过
    if current_version == target_version {
        return Err(StdError::generic_err("Already at target version"));
    }

    // 根据当前版本和目标版本选择迁移路径
    match (current_version.as_str(), target_version.as_str()) {
        ("1.0.0", "2.0.0") => {
            migrate_v1_to_v2(deps.storage)?;
        }
        ("1.0.0", "3.0.0") => {
            migrate_v1_to_v2(deps.storage)?;
            migrate_v2_to_v3(deps.storage, &env)?;
        }
        ("2.0.0", "3.0.0") => {
            migrate_v2_to_v3(deps.storage, &env)?;
        }
        (from, to) => {
            return Err(StdError::generic_err(format!(
                "Unsupported migration path: {} -> {}",
                from, to
            )));
        }
    }

    // 更新版本记录
    deps.storage.set(
        VERSION_KEY.as_bytes(),
        target_version.as_bytes(),
    );

    Ok(Response::new()
        .add_attribute("method", "migrate")
        .add_attribute("from", &current_version)
        .add_attribute("to", &target_version))
}

// ========== 各版本迁移函数 ==========

fn migrate_v1_to_v2(storage: &mut dyn Storage) -> StdResult<()> {
    // 1. 读取 V1 状态
    // 2. 转换为 V2 格式
    // 3. 清理旧 key
    Ok(())
}

fn migrate_v2_to_v3(storage: &mut dyn Storage, _env: &Env) -> StdResult<()> {
    // 跨版本数据迁移
    Ok(())
}

2.4 MigrateMsg 设计模式

// ========== 增强型 MigrateMsg ==========

#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
#[serde(rename_all = "snake_case")]
pub enum MigrateMsg {
    /// 升级到指定版本(无参数迁移)
    Upgrade {
        version: String,
    },
    /// 升级并重新配置
    UpgradeWithConfig {
        version: String,
        new_owner: Option<String>,
        params: Option<MigrationParams>,
    },
    /// 紧急暂停
    EmergencyPause {
        reason: String,
    },
    /// 数据迁移(仅状态迁移,不改变代码)
    MigrateData {
        target_version: String,
        migrate_all_users: bool,
    },
}

#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
pub struct MigrationParams {
    pub new_counter_limit: Option<u64>,
    pub fee_rate: Option<u64>,
    pub whitelist_only: Option<bool>,
}

#[cfg_attr(not(feature = "library"), entry_point)]
pub fn migrate(deps: DepsMut, env: Env, msg: MigrateMsg) -> StdResult<Response> {
    match msg {
        MigrateMsg::Upgrade { version } => {
            handle_upgrade(deps, env, version)
        }
        MigrateMsg::UpgradeWithConfig {
            version,
            new_owner,
            params,
        } => handle_upgrade_with_config(deps, env, version, new_owner, params),
        MigrateMsg::EmergencyPause { reason } => {
            handle_emergency_pause(deps, env, reason)
        }
        MigrateMsg::MigrateData {
            target_version,
            migrate_all_users,
        } => handle_migrate_data(deps, env, target_version, migrate_all_users),
    }
}

fn handle_upgrade(deps: DepsMut, _env: Env, version: String) -> StdResult<Response> {
    let current = deps.contract_info.version.clone();
    if current == version {
        return Err(StdError::generic_err("Already at target version"));
    }

    CONFIG.update(deps.storage, |mut config| -> StdResult<_> {
        config.version = version.clone();
        Ok(config)
    })?;

    Ok(Response::new()
        .add_attribute("method", "upgrade")
        .add_attribute("from", current)
        .add_attribute("to", version))
}

fn handle_upgrade_with_config(
    deps: DepsMut,
    _env: Env,
    version: String,
    new_owner: Option<String>,
    params: Option<MigrationParams>,
) -> StdResult<Response> {
    let current = deps.contract_info.version.clone();

    CONFIG.update(deps.storage, |mut config| -> StdResult<_> {
        config.version = version.clone();
        if let Some(ref owner) = new_owner {
            config.owner = deps.api.addr_validate(owner)?;
        }
        Ok(config)
    })?;

    let mut resp = Response::new()
        .add_attribute("method", "upgrade_with_config")
        .add_attribute("from", current)
        .add_attribute("to", version);

    if let Some(p) = params {
        if let Some(limit) = p.new_counter_limit {
            // 应用新参数
            resp = resp.add_attribute("new_counter_limit", limit.to_string());
        }
    }

    Ok(resp)
}

fn handle_emergency_pause(
    deps: DepsMut,
    _env: Env,
    reason: String,
) -> StdResult<Response> {
    CONFIG.update(deps.storage, |mut config| -> StdResult<_> {
        config.paused = true;
        Ok(config)
    })?;

    Ok(Response::new()
        .add_attribute("method", "emergency_pause")
        .add_attribute("reason", reason))
}

fn handle_migrate_data(
    _deps: DepsMut,
    _env: Env,
    _target_version: String,
    _migrate_all_users: bool,
) -> StdResult<Response> {
    // 批量迁移用户数据
    Ok(Response::new()
        .add_attribute("method", "migrate_data"))
}

2.5 迁移测试套件

#[cfg(test)]
mod tests {
    use super::*;
    use cosmwasm_std::testing::{
        mock_dependencies, mock_dependencies_with_balance, mock_env,
        mock_info, MOCK_CONTRACT_ADDR,
    };
    use cosmwasm_std::{coins, from_binary, ContractInfo};

    // ========== 辅助函数 ==========

    fn create_contract_info(version: &str) -> ContractInfo {
        ContractInfo {
            code_id: 1,
            creator: Addr::unchecked("msg1creator00000000000000000000000000000000"),
            admin: Some(Addr::unchecked("msg1admin000000000000000000000000000000000")),
            label: "test_contract".to_string(),
            version: version.to_string(),
            ibc_port_id: None,
        }
    }

    fn setup_contract() -> (
        cosmwasm_std::OwnedDeps<cosmwasm_std::MemoryStorage, cosmwasm_std::testing::MockApi, cosmwasm_std::testing::MockQuerier>,
        Env,
    ) {
        let mut deps = mock_dependencies();
        let env = mock_env();
        let info = mock_info("msg1sender000000000000000000000000000000000", &[]);

        // 设置初始合约信息
        deps.contract_info = create_contract_info("1.0.0");

        let msg = InstantiateMsg {
            owner: "msg1owner000000000000000000000000000000000".to_string(),
        };

        let res = instantiate(deps.as_mut(), env.clone(), info, msg).unwrap();
        assert_eq!(res.attributes[0].value, "instantiate");

        (deps, env)
    }

    // ========== 测试基础迁移 ==========

    #[test]
    fn test_basic_migration() {
        let (mut deps, env) = setup_contract();

        // 验证初始版本
        assert_eq!(deps.contract_info.version, "1.0.0");

        // 执行迁移到 2.0.0
        let migrate_msg = MigrateMsg {
            version: "2.0.0".to_string(),
        };

        // 模拟合约信息更新
        deps.contract_info.version = "2.0.0".to_string();

        let res = migrate(deps.as_mut(), env, migrate_msg).unwrap();

        // 验证迁移结果
        assert_eq!(res.attributes[1].value, "migrate");
        assert_eq!(res.attributes[2].value, "1.0.0");

        // 验证状态中版本已更新
        let config = CONFIG.load(&deps.storage).unwrap();
        assert_eq!(config.version, "2.0.0");
    }

    // ========== 测试重复迁移拒绝 ==========

    #[test]
    fn test_reject_duplicate_migration() {
        let (mut deps, env) = setup_contract();

        // 模拟已经是 2.0.0
        deps.contract_info.version = "2.0.0".to_string();

        // 尝试再次迁移到相同版本
        let migrate_msg = MigrateMsg {
            version: "2.0.0".to_string(),
        };

        let err = migrate(deps.as_mut(), env.clone(), migrate_msg).unwrap_err();
        assert_eq!(err.to_string(), "Generic error: Already at target version, migration skipped");

        // 恢复原始版本再尝试
        deps.contract_info.version = "1.0.0".to_string();
        let migrate_msg = MigrateMsg {
            version: "3.0.0".to_string(),
        };
        let res = migrate(deps.as_mut(), env, migrate_msg).unwrap();
        assert_eq!(res.attributes[3].value, "3.0.0");
    }

    // ========== 测试迁移后功能完整性 ==========

    #[test]
    fn test_functionality_after_migration() {
        let (mut deps, env) = setup_contract();

        // 迁移到 2.0.0
        deps.contract_info.version = "2.0.0".to_string();
        let migrate_msg = MigrateMsg {
            version: "2.0.0".to_string(),
        };
        migrate(deps.as_mut(), env.clone(), migrate_msg).unwrap();

        // 迁移后合约功能仍然正常
        let exec_msg = ExecuteMsg::Increment {};
        let info = mock_info("msg1user00000000000000000000000000000000000", &[]);
        let res = execute(deps.as_mut(), env.clone(), info, exec_msg).unwrap();
        assert_eq!(res.attributes[0].value, "increment");

        // 验证计数增加
        let config = CONFIG.load(&deps.storage).unwrap();
        assert_eq!(config.counter, 1);
    }

    // ========== 测试回滚迁移 ==========

    #[test]
    fn test_migration_state_integrity() {
        let (mut deps, env) = setup_contract();

        // 先做一些操作
        let exec_msg = ExecuteMsg::Increment {};
        let info = mock_info("msg1user00000000000000000000000000000000000", &[]);
        execute(deps.as_mut(), env.clone(), info, exec_msg).unwrap();

        // 确认状态已改变
        let config_before = CONFIG.load(&deps.storage).unwrap();
        assert_eq!(config_before.counter, 1);

        // 执行迁移
        deps.contract_info.version = "2.0.0".to_string();
        let migrate_msg = MigrateMsg {
            version: "2.0.0".to_string(),
        };
        let res = migrate(deps.as_mut(), env.clone(), migrate_msg).unwrap();

        // 迁移后状态应保持(计数器不变)
        let config_after = CONFIG.load(&deps.storage).unwrap();
        assert_eq!(config_after.counter, 1);
        assert_eq!(config_after.version, "2.0.0");
    }

    // ========== 测试批量迁移性能 ==========

    #[test]
    fn test_high_volume_state_migration() {
        let mut deps = mock_dependencies();
        let env = mock_env();

        // 模拟大量用户数据
        let info = mock_info("msg1admin000000000000000000000000000000000", &[]);
        let msg = InstantiateMsg {
            owner: "msg1owner000000000000000000000000000000000".to_string(),
        };
        instantiate(deps.as_mut(), env.clone(), info, msg).unwrap();

        // 添加大量用户
        for i in 0..1000u64 {
            let addr = Addr::unchecked(format!("msg1user{:032x}", i));
            USERS.save(deps.as_mut().storage, &addr, &i).unwrap();
        }

        // 执行迁移
        deps.contract_info.version = "2.0.0".to_string();
        let migrate_msg = MigrateMsg {
            version: "2.0.0".to_string(),
        };
        let res = migrate(deps.as_mut(), env, migrate_msg).unwrap();
        assert_eq!(res.attributes[0].value, "migrate");

        // 验证用户数据完整
        for i in 0..1000u64 {
            let addr = Addr::unchecked(format!("msg1user{:032x}", i));
            let val = USERS.load(&deps.storage, &addr).unwrap();
            assert_eq!(val, i);
        }
    }
}

2.6 完整合约代码

pub mod contract;
pub mod msg;
pub mod state;

#[cfg(test)]
mod testing;

3. 代理模式

3.1 Transparent Proxy 原理

代理模式(Proxy Pattern)将逻辑与状态分离到两个合约中:

在 CosmWasm 中,由于没有原生的 delegatecall,我们通过自定义实现达到类似效果:

用户
  │
  ▼
Proxy 合约 (msg1proxy...)  ←── 持有状态
  │
  │  query: 直接从自身 storage 读取
  │  execute: 调用实现合约,实现合约通过 query 读取 proxy 的状态
  │  wasm: 实现合约通过子消息返回结果
  ▼
Implementation 合约 (msg1impl...)  ←── 仅有逻辑代码

3.2 Proxy 合约核心实现

use cosmwasm_std::{
    entry_point, from_binary, to_binary, Addr, Binary, Deps, DepsMut, Env,
    MessageInfo, Reply, Response, StdError, StdResult, SubMsg, WasmMsg,
    WasmQuery,
};
use cw_storage_plus::{Item, Map};
use serde::{Deserialize, Serialize};

// ========== Proxy 状态 ==========

#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
pub struct ProxyState {
    pub implementation: Addr,
    pub admin: Addr,
    pub paused: bool,
}

pub const PROXY_STATE: Item<ProxyState> = Item::new("proxy_state");

// ========== Proxy 消息 ==========

#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
#[serde(rename_all = "snake_case")]
pub enum ProxyExecuteMsg {
    /// 升级实现合约
    Upgrade {
        new_implementation: String,
    },
    /// 转移管理员权限
    TransferAdmin {
        new_admin: String,
    },
    /// 紧急暂停
    Pause {},
    /// 取消暂停
    Unpause {},
    /// 转发给实现合约的具体消息
    Execute {
        msg: Binary,
    },
}

#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
#[serde(rename_all = "snake_case")]
pub enum ProxyQueryMsg {
    /// 获取实现合约地址
    GetImplementation {},
    /// 转发给实现合约的查询
    Query {
        msg: Binary,
    },
}

// ========== Proxy Instantiate ==========

#[cfg_attr(not(feature = "library"), entry_point)]
pub fn instantiate(
    deps: DepsMut,
    _env: Env,
    info: MessageInfo,
    msg: ProxyInstantiateMsg,
) -> StdResult<Response> {
    let state = ProxyState {
        implementation: deps.api.addr_validate(&msg.implementation)?,
        admin: info.sender.clone(),
        paused: false,
    };
    PROXY_STATE.save(deps.storage, &state)?;

    Ok(Response::new()
        .add_attribute("method", "instantiate")
        .add_attribute("implementation", &msg.implementation)
        .add_attribute("admin", info.sender))
}

// ========== Proxy Execute ==========

#[cfg_attr(not(feature = "library"), entry_point)]
pub fn execute(
    deps: DepsMut,
    env: Env,
    info: MessageInfo,
    msg: ProxyExecuteMsg,
) -> StdResult<Response> {
    match msg {
        ProxyExecuteMsg::Upgrade {
            new_implementation,
        } => execute_upgrade(deps, env, info, new_implementation),
        ProxyExecuteMsg::TransferAdmin { new_admin } => {
            execute_transfer_admin(deps, env, info, new_admin)
        }
        ProxyExecuteMsg::Pause {} => execute_pause(deps, env, info),
        ProxyExecuteMsg::Unpause {} => execute_unpause(deps, env, info),
        ProxyExecuteMsg::Execute { msg } => {
            execute_forward(deps, env, info, msg)
        }
    }
}

/// 升级实现合约地址
fn execute_upgrade(
    deps: DepsMut,
    _env: Env,
    info: MessageInfo,
    new_implementation: String,
) -> StdResult<Response> {
    let state = PROXY_STATE.load(deps.storage)?;

    // 仅管理员可升级
    if info.sender != state.admin {
        return Err(StdError::generic_err("Unauthorized: only admin can upgrade"));
    }

    let new_addr = deps.api.addr_validate(&new_implementation)?;

    PROXY_STATE.update(deps.storage, |mut state| -> StdResult<_> {
        let old = state.implementation.clone();
        state.implementation = new_addr;
        Ok(state)
    })?;

    Ok(Response::new()
        .add_attribute("method", "upgrade")
        .add_attribute("from", state.implementation.to_string())
        .add_attribute("to", new_implementation))
}

/// 转移管理员权限
fn execute_transfer_admin(
    deps: DepsMut,
    _env: Env,
    info: MessageInfo,
    new_admin: String,
) -> StdResult<Response> {
    let state = PROXY_STATE.load(deps.storage)?;

    if info.sender != state.admin {
        return Err(StdError::generic_err("Unauthorized: only admin can transfer"));
    }

    let new_admin_addr = deps.api.addr_validate(&new_admin)?;

    PROXY_STATE.update(deps.storage, |mut state| -> StdResult<_> {
        state.admin = new_admin_addr;
        Ok(state)
    })?;

    Ok(Response::new()
        .add_attribute("method", "transfer_admin")
        .add_attribute("new_admin", new_admin))
}

fn execute_pause(
    deps: DepsMut,
    _env: Env,
    info: MessageInfo,
) -> StdResult<Response> {
    let state = PROXY_STATE.load(deps.storage)?;
    if info.sender != state.admin {
        return Err(StdError::generic_err("Unauthorized"));
    }
    PROXY_STATE.update(deps.storage, |mut state| -> StdResult<_> {
        state.paused = true;
        Ok(state)
    })?;
    Ok(Response::new().add_attribute("method", "pause"))
}

fn execute_unpause(
    deps: DepsMut,
    _env: Env,
    info: MessageInfo,
) -> StdResult<Response> {
    let state = PROXY_STATE.load(deps.storage)?;
    if info.sender != state.admin {
        return Err(StdError::generic_err("Unauthorized"));
    }
    PROXY_STATE.update(deps.storage, |mut state| -> StdResult<_> {
        state.paused = false;
        Ok(state)
    })?;
    Ok(Response::new().add_attribute("method", "unpause"))
}

/// 转发执行到实现合约
fn execute_forward(
    deps: DepsMut,
    _env: Env,
    info: MessageInfo,
    msg: Binary,
) -> StdResult<Response> {
    let state = PROXY_STATE.load(deps.storage)?;

    if state.paused {
        return Err(StdError::generic_err("Contract is paused"));
    }

    // 构造 WasmMsg::Execute 调用实现合约
    // 实现合约会读取 proxy 的状态(通过 Query 或共享存储)
    let execute_msg = WasmMsg::Execute {
        contract_addr: state.implementation.to_string(),
        msg: msg,
        funds: vec![],
    };

    Ok(Response::new()
        .add_submessage(SubMsg::new(execute_msg))
        .add_attribute("method", "proxy_execute")
        .add_attribute("implementation", state.implementation))
}

// ========== Proxy Query ==========

#[cfg_attr(not(feature = "library"), entry_point)]
pub fn query(deps: Deps, _env: Env, msg: ProxyQueryMsg) -> StdResult<Binary> {
    match msg {
        ProxyQueryMsg::GetImplementation {} => {
            let state = PROXY_STATE.load(deps.storage)?;
            to_binary(&state.implementation)
        }
        ProxyQueryMsg::Query { msg } => query_forward(deps, msg),
    }
}

/// 转发查询到实现合约
fn query_forward(deps: Deps, msg: Binary) -> StdResult<Binary> {
    let state = PROXY_STATE.load(deps.storage)?;

    // 通过 WasmQuery::Smart 查询实现合约
    let query = WasmQuery::Smart {
        contract_addr: state.implementation.to_string(),
        msg: msg,
    };

    // 注意:此处需要从 deps.querier 查询
    // 但由于 querier 在 query 中可用,我们直接构造查询
    deps.querier.query(&query.into())
}

// ========== Proxy 声明 ==========

#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
pub struct ProxyInstantiateMsg {
    pub implementation: String,
}

#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
#[serde(rename_all = "snake_case")]
pub enum ProxyMigrateMsg {
    UpgradeImplementation { new_implementation: String },
}

3.3 存储冲突避免策略

在代理模式中,最大的挑战是存储布局冲突。Proxy 合约和 Implementation 合约使用相同的存储空间(因为状态在 Proxy 合约中),因此必须确保两者的存储 key 不冲突。

// ========== 存储 Key 命名规范 ==========

/// Proxy 合约使用特定前缀
pub const PROXY_KEY_PREFIX: &str = "proxy_";

/// 实现合约使用独立前缀
pub const IMPL_KEY_PREFIX: &str = "impl_";

// ========== 防止冲突的存储工具 ==========

/// 为存储 key 添加前缀以防止冲突
pub struct NamespacedStorage<'a> {
    storage: &'a dyn Storage,
    namespace: &'a [u8],
}

impl<'a> NamespacedStorage<'a> {
    pub fn new(storage: &'a dyn Storage, namespace: &'a [u8]) -> Self {
        Self { storage, namespace }
    }
}

impl Storage for NamespacedStorage<'_> {
    fn get(&self, key: &[u8]) -> Option<Vec<u8>> {
        let prefixed = [self.namespace, key].concat();
        self.storage.get(&prefixed)
    }

    fn set(&mut self, key: &[u8], value: &[u8]) {
        let prefixed = [self.namespace, key].concat();
        self.storage.set(&prefixed, value);
    }

    fn remove(&mut self, key: &[u8]) {
        let prefixed = [self.namespace, key].concat();
        self.storage.remove(&prefixed);
    }

    fn scan<'b>(
        &'b self,
        start: Option<&[u8]>,
        end: Option<&[u8]>,
        order: cosmwasm_std::Order,
    ) -> Box<dyn Iterator<Item = Vec<u8>> + 'b> {
        let prefixed_start = start.map(|s| [self.namespace, s].concat());
        let prefixed_end = end.map(|e| [self.namespace, e].concat());
        self.storage
            .scan(prefixed_start.as_deref(), prefixed_end.as_deref(), order)
    }
}

// ========== 推荐方案:独立存储空间 ==========

/// 更好的方案:实现合约通过自己的 Storage trait 操作,
/// Proxy 合约通过 WasmQuery 与实现合约通信
///
/// 这种方式完全避免了存储冲突,但增加了 Gas 开销
///
/// 架构:
///   Proxy:       状态存储层 + 路由层
///   Impl V1:     逻辑层 (独立存储)
///   Impl V2:     逻辑层 (独立存储)
///
/// 升级时:
///   Proxy 更新 implementation 地址指向新实现
///   新实现可以读取旧实现的状态(通过 Query)
///   或从 Proxy 的存储中读取

3.4 完整代理模式测试

#[cfg(test)]
mod proxy_tests {
    use super::*;
    use cosmwasm_std::testing::{
        mock_dependencies, mock_dependencies_with_balance, mock_env,
        mock_info, MOCK_CONTRACT_ADDR,
    };

    const PROXY_ADMIN: &str = "msg1admin000000000000000000000000000000000";
    const IMPL_V1: &str = "msg1impl11111111111111111111111111111111111";
    const IMPL_V2: &str = "msg1impl22222222222222222222222222222222222";

    fn setup_proxy() -> (
        cosmwasm_std::OwnedDeps<cosmwasm_std::MemoryStorage, cosmwasm_std::testing::MockApi, cosmwasm_std::testing::MockQuerier>,
        Env,
    ) {
        let mut deps = mock_dependencies();
        let env = mock_env();

        let msg = ProxyInstantiateMsg {
            implementation: IMPL_V1.to_string(),
        };
        let info = mock_info(PROXY_ADMIN, &[]);
        let res = instantiate(deps.as_mut(), env.clone(), info, msg).unwrap();

        assert_eq!(res.attributes[0].value, "instantiate");
        (deps, env)
    }

    // ========== 测试代理初始化 ==========

    #[test]
    fn test_proxy_initialization() {
        let (deps, _env) = setup_proxy();
        let state = PROXY_STATE.load(&deps.storage).unwrap();

        assert_eq!(state.implementation, Addr::unchecked(IMPL_V1));
        assert_eq!(state.admin, Addr::unchecked(PROXY_ADMIN));
        assert!(!state.paused);
    }

    // ========== 测试合约升级 ==========

    #[test]
    fn test_upgrade_implementation() {
        let (mut deps, env) = setup_proxy();

        // 管理员升级合约
        let info = mock_info(PROXY_ADMIN, &[]);
        let msg = ProxyExecuteMsg::Upgrade {
            new_implementation: IMPL_V2.to_string(),
        };
        let res = execute(deps.as_mut(), env, info, msg).unwrap();

        assert_eq!(res.attributes[0].value, "upgrade");
        assert_eq!(res.attributes[1].value, IMPL_V1);
        assert_eq!(res.attributes[2].value, IMPL_V2);

        // 验证状态已更新
        let state = PROXY_STATE.load(&deps.storage).unwrap();
        assert_eq!(state.implementation, Addr::unchecked(IMPL_V2));
    }

    // ========== 测试非管理员无法升级 ==========

    #[test]
    fn test_non_admin_cannot_upgrade() {
        let (mut deps, env) = setup_proxy();

        let info = mock_info("msg1attacker0000000000000000000000000000000", &[]);
        let msg = ProxyExecuteMsg::Upgrade {
            new_implementation: IMPL_V2.to_string(),
        };
        let err = execute(deps.as_mut(), env, info, msg).unwrap_err();

        assert!(err.to_string().contains("Unauthorized"));
    }

    // ========== 测试管理员转移 ==========

    #[test]
    fn test_transfer_admin() {
        let (mut deps, env) = setup_proxy();

        let new_admin = "msg1newadmin0000000000000000000000000000000";
        let info = mock_info(PROXY_ADMIN, &[]);
        let msg = ProxyExecuteMsg::TransferAdmin {
            new_admin: new_admin.to_string(),
        };
        let res = execute(deps.as_mut(), env.clone(), info, msg).unwrap();

        assert_eq!(res.attributes[0].value, "transfer_admin");

        let state = PROXY_STATE.load(&deps.storage).unwrap();
        assert_eq!(state.admin, Addr::unchecked(new_admin));

        // 旧管理员不能再操作
        let old_info = mock_info(PROXY_ADMIN, &[]);
        let upgrade_msg = ProxyExecuteMsg::Upgrade {
            new_implementation: IMPL_V2.to_string(),
        };
        let err = execute(
            deps.as_mut(),
            env.clone(),
            old_info,
            upgrade_msg,
        )
        .unwrap_err();
        assert!(err.to_string().contains("Unauthorized"));
    }

    // ========== 测试暂停机制 ==========

    #[test]
    fn test_pause_unpause() {
        let (mut deps, env) = setup_proxy();

        // 管理员暂停
        let info = mock_info(PROXY_ADMIN, &[]);
        let msg = ProxyExecuteMsg::Pause {};
        execute(deps.as_mut(), env.clone(), info, msg).unwrap();

        let state = PROXY_STATE.load(&deps.storage).unwrap();
        assert!(state.paused);

        // 暂停后操作应被拒绝
        let exec_info = mock_info("msg1user00000000000000000000000000000000000", &[]);
        let exec_msg = ProxyExecuteMsg::Execute {
            msg: to_binary(&"dummy").unwrap(),
        };
        let err = execute(
            deps.as_mut(),
            env.clone(),
            exec_info,
            exec_msg,
        )
        .unwrap_err();
        assert!(err.to_string().contains("paused"));

        // 管理员取消暂停
        let admin_info = mock_info(PROXY_ADMIN, &[]);
        let unpause_msg = ProxyExecuteMsg::Unpause {};
        execute(deps.as_mut(), env, admin_info, unpause_msg).unwrap();

        let state = PROXY_STATE.load(&deps.storage).unwrap();
        assert!(!state.paused);
    }

    // ========== 测试转发执行 ==========

    #[test]
    fn test_forward_execution() {
        let (mut deps, env) = setup_proxy();

        let info = mock_info("msg1user00000000000000000000000000000000000", &[]);
        let exec_msg = ProxyExecuteMsg::Execute {
            msg: to_binary(&"increment").unwrap(),
        };
        let res = execute(deps.as_mut(), env, info, exec_msg).unwrap();

        // 验证产生了转发到实现合约的子消息
        assert_eq!(res.messages.len(), 1);
        match &res.messages[0].msg {
            cosmwasm_std::CosmosMsg::Wasm(WasmMsg::Execute {
                contract_addr,
                ..
            }) => {
                assert_eq!(contract_addr, IMPL_V1);
            }
            _ => panic!("Expected WasmMsg::Execute"),
        }
    }

    // ========== 测试查询实现合约地址 ==========

    #[test]
    fn test_query_implementation() {
        let (deps, env) = setup_proxy();

        let msg = ProxyQueryMsg::GetImplementation {};
        let bin = query(deps.as_ref(), env, msg).unwrap();
        let impl_addr: Addr = from_binary(&bin).unwrap();

        assert_eq!(impl_addr, Addr::unchecked(IMPL_V1));
    }

    // ========== 测试连续升级 ==========

    #[test]
    fn test_multiple_upgrades() {
        let (mut deps, env) = setup_proxy();

        let impl_v3 = "msg1impl33333333333333333333333333333333333";

        // 第一次升级
        let info = mock_info(PROXY_ADMIN, &[]);
        let msg = ProxyExecuteMsg::Upgrade {
            new_implementation: IMPL_V2.to_string(),
        };
        execute(deps.as_mut(), env.clone(), info, msg).unwrap();

        // 第二次升级
        let info = mock_info(PROXY_ADMIN, &[]);
        let msg = ProxyExecuteMsg::Upgrade {
            new_implementation: impl_v3.to_string(),
        };
        execute(deps.as_mut(), env, info, msg).unwrap();

        let state = PROXY_STATE.load(&deps.storage).unwrap();
        assert_eq!(state.implementation, Addr::unchecked(impl_v3));
    }
}

// ========== 实现合约示例 ==========

/// 这是一个与 Proxy 配合使用的示例实现合约
/// 它不直接存储状态,而是通过 Query 与 Proxy 通信

mod implementation_contract {
    use cosmwasm_std::{
        entry_point, to_binary, Binary, Deps, DepsMut, Env, MessageInfo,
        Response, StdResult,
    };

    #[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
    pub struct ImplInstantiateMsg {}

    #[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
    #[serde(rename_all = "snake_case")]
    pub enum ImplExecuteMsg {
        Increment {},
        Decrement {},
        Reset { counter: u64 },
    }

    #[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
    #[serde(rename_all = "snake_case")]
    pub enum ImplQueryMsg {
        GetCounter {},
        GetInfo {},
    }

    #[cfg_attr(not(feature = "library"), entry_point)]
    pub fn instantiate(
        _deps: DepsMut,
        _env: Env,
        _info: MessageInfo,
        _msg: ImplInstantiateMsg,
    ) -> StdResult<Response> {
        Ok(Response::new())
    }

    #[cfg_attr(not(feature = "library"), entry_point)]
    pub fn execute(
        _deps: DepsMut,
        _env: Env,
        _info: MessageInfo,
        msg: ImplExecuteMsg,
    ) -> StdResult<Response> {
        match msg {
            ImplExecuteMsg::Increment {} => {
                // 实现实际业务逻辑
                // 注意:此处不能直接使用 deps.storage
                // 因为状态存储在 Proxy 合约中
                Ok(Response::new().add_attribute("impl", "increment"))
            }
            ImplExecuteMsg::Decrement {} => {
                Ok(Response::new().add_attribute("impl", "decrement"))
            }
            ImplExecuteMsg::Reset { counter } => {
                Ok(Response::new()
                    .add_attribute("impl", "reset")
                    .add_attribute("counter", counter.to_string()))
            }
        }
    }

    #[cfg_attr(not(feature = "library"), entry_point)]
    pub fn query(_deps: Deps, _env: Env, msg: ImplQueryMsg) -> StdResult<Binary> {
        match msg {
            ImplQueryMsg::GetCounter {} => {
                to_binary(&0u64)
            }
            ImplQueryMsg::GetInfo {} => {
                to_binary(&"implementation_v1")
            }
        }
    }
}

3.5 透明代理 vs 通用代理

// ========== 透明代理(Transparent Proxy) ==========
//
// 工作原理:
// - 如果调用者是管理员,则执行代理管理操作(升级、暂停等)
// - 如果调用者是普通用户,则转发到实现合约
//
// 优点:普通用户完全无感,不需要知道 proxy 的存在
// 缺点:管理员的每次调用都有 Gas 开销判断

// ========== UUPS 代理(Universal Upgradeable Proxy) ==========
//
// 工作原理:
// - 升级逻辑在实现合约中,而不是在代理中
// - 代理仅负责 delegate 调用
//
// 优点:代理合约极小(减少 Gas)
// 缺点:需要在实现合约中编写升级逻辑

// ========== MSG Chain 推荐方案 ==========

/// 推荐使用透明代理模式,原因:
/// 1. 升级逻辑集中在 Proxy 中,审计更容易
/// 2. 实现合约更纯粹,专注于业务
/// 3. 符合 CosmWasm 的设计哲学
///
/// ```
/// msg 链上合约架构:
///
///   ┌─────────────┐
///   │   用户       │
///   └──────┬──────┘
///          │
///   ┌──────▼──────┐
///   │  Proxy       │  ← msg1proxy...
///   │  - 状态存储  │
///   │  - 访问控制  │
///   │  - 升级管理  │
///   └──────┬──────┘
///          │
///   ┌──────▼──────┐
///   │  Impl V2     │  ← msg1implv2...
///   │  - 业务逻辑  │
///   │  - 无状态    │
///   └─────────────┘
/// ```

4. 工厂升级模式

4.1 基于 Code ID 的工厂模式

工厂模式适用于需要创建多个同类型合约实例的场景(如每个用户一个合约实例):

use cosmwasm_std::{
    entry_point, from_binary, to_binary, Addr, Binary, Deps, DepsMut, Env,
    MessageInfo, Order, Reply, Response, StdError, StdResult, SubMsg,
    SubMsgResult, WasmMsg,
};
use cw_storage_plus::{Item, Map};
use cw_utils::parse_reply_instantiate_data;
use serde::{Deserialize, Serialize};

// ========== 工厂状态 ==========

#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
pub struct FactoryConfig {
    pub admin: Addr,
    pub current_code_id: u64,
    pub version: String,
    pub paused: bool,
}

#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
pub struct ChildContract {
    pub address: Addr,
    pub code_id: u64,
    pub version: String,
    pub created_at: u64,
    pub owner: Addr,
}

pub const FACTORY_CONFIG: Item<FactoryConfig> = Item::new("factory_config");
pub const CHILD_CONTRACTS: Map<&Addr, ChildContract> = Map::new("child_contracts");

// ========== 工厂消息 ==========

#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
pub struct FactoryInstantiateMsg {
    pub admin: String,
    pub initial_code_id: u64,
}

#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
#[serde(rename_all = "snake_case")]
pub enum FactoryExecuteMsg {
    /// 创建新的子合约实例
    CreateChild {
        label: String,
        owner: String,
        init_msg: Binary,
    },
    /// 升级工厂使用的 Code ID(新建实例使用新代码)
    UpgradeCodeId {
        new_code_id: u64,
        version: String,
    },
    /// 批量迁移已有子合约到新 Code ID
    MigrateChildren {
        child_addresses: Vec<String>,
        migrate_msg: Binary,
    },
    /// 转移管理员权限
    TransferAdmin {
        new_admin: String,
    },
}

#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
#[serde(rename_all = "snake_case")]
pub enum FactoryQueryMsg {
    GetConfig {},
    ListChildren {
        start_after: Option<String>,
        limit: Option<u32>,
    },
    GetChild {
        address: String,
    },
}

pub const CREATE_CHILD_REPLY_ID: u64 = 1;

// ========== 工厂实例化 ==========

#[cfg_attr(not(feature = "library"), entry_point)]
pub fn instantiate(
    deps: DepsMut,
    _env: Env,
    _info: MessageInfo,
    msg: FactoryInstantiateMsg,
) -> StdResult<Response> {
    let config = FactoryConfig {
        admin: deps.api.addr_validate(&msg.admin)?,
        current_code_id: msg.initial_code_id,
        version: "1.0.0".to_string(),
        paused: false,
    };
    FACTORY_CONFIG.save(deps.storage, &config)?;
    Ok(Response::new()
        .add_attribute("method", "instantiate")
        .add_attribute("admin", &msg.admin)
        .add_attribute("initial_code_id", msg.initial_code_id.to_string()))
}

// ========== 工厂执行 ==========

#[cfg_attr(not(feature = "library"), entry_point)]
pub fn execute(
    deps: DepsMut,
    env: Env,
    info: MessageInfo,
    msg: FactoryExecuteMsg,
) -> StdResult<Response> {
    match msg {
        FactoryExecuteMsg::CreateChild {
            label,
            owner,
            init_msg,
        } => execute_create_child(deps, env, info, label, owner, init_msg),
        FactoryExecuteMsg::UpgradeCodeId {
            new_code_id,
            version,
        } => execute_upgrade_code_id(deps, env, info, new_code_id, version),
        FactoryExecuteMsg::MigrateChildren {
            child_addresses,
            migrate_msg,
        } => execute_migrate_children(deps, env, info, child_addresses, migrate_msg),
        FactoryExecuteMsg::TransferAdmin { new_admin } => {
            execute_transfer_admin(deps, env, info, new_admin)
        }
    }
}

/// 创建子合约(使用 Reply 模式获取确定性地址)
fn execute_create_child(
    deps: DepsMut,
    _env: Env,
    info: MessageInfo,
    label: String,
    owner: String,
    init_msg: Binary,
) -> StdResult<Response> {
    let config = FACTORY_CONFIG.load(deps.storage)?;

    let owner_addr = deps.api.addr_validate(&owner)?;

    // 构造 WasmMsg::Instantiate
    let instantiate_msg = WasmMsg::Instantiate {
        admin: Some(config.admin.to_string()),
        code_id: config.current_code_id,
        msg: init_msg,
        funds: vec![],
        label: label.clone(),
    };

    // 使用 SubMsg 包裹以获取 Reply 中的合约地址
    let sub_msg = SubMsg::reply_on_success(instantiate_msg, CREATE_CHILD_REPLY_ID);

    Ok(Response::new()
        .add_submessage(sub_msg)
        .add_attribute("method", "create_child")
        .add_attribute("owner", owner_addr)
        .add_attribute("label", label))
}

/// 升级 Code ID(新实例将使用新代码)
fn execute_upgrade_code_id(
    deps: DepsMut,
    _env: Env,
    info: MessageInfo,
    new_code_id: u64,
    version: String,
) -> StdResult<Response> {
    let config = FACTORY_CONFIG.load(deps.storage)?;

    if info.sender != config.admin {
        return Err(StdError::generic_err("Unauthorized"));
    }

    FACTORY_CONFIG.update(deps.storage, |mut config| -> StdResult<_> {
        config.current_code_id = new_code_id;
        config.version = version.clone();
        Ok(config)
    })?;

    Ok(Response::new()
        .add_attribute("method", "upgrade_code_id")
        .add_attribute("old_code_id", config.current_code_id.to_string())
        .add_attribute("new_code_id", new_code_id.to_string())
        .add_attribute("version", version))
}

/// 批量迁移子合约到新代码
fn execute_migrate_children(
    deps: DepsMut,
    _env: Env,
    info: MessageInfo,
    child_addresses: Vec<String>,
    migrate_msg: Binary,
) -> StdResult<Response> {
    let config = FACTORY_CONFIG.load(deps.storage)?;

    if info.sender != config.admin {
        return Err(StdError::generic_err("Unauthorized"));
    }

    let mut response = Response::new()
        .add_attribute("method", "migrate_children")
        .add_attribute("count", child_addresses.len().to_string());

    for addr_str in child_addresses {
        let addr = deps.api.addr_validate(&addr_str)?;

        // 验证子合约是否由该工厂创建
        let child = CHILD_CONTRACTS.load(deps.storage, &addr)?;

        // 构造迁移消息
        let migrate = WasmMsg::Migrate {
            contract_addr: addr.to_string(),
            new_code_id: config.current_code_id,
            msg: migrate_msg.clone(),
        };

        response = response.add_message(migrate);

        // 更新子合约记录
        CHILD_CONTRACTS.update(deps.storage, &addr, |mut c| -> StdResult<_> {
            c.code_id = config.current_code_id;
            c.version = config.version.clone();
            Ok(c)
        })?;
    }

    Ok(response)
}

fn execute_transfer_admin(
    deps: DepsMut,
    _env: Env,
    info: MessageInfo,
    new_admin: String,
) -> StdResult<Response> {
    let config = FACTORY_CONFIG.load(deps.storage)?;
    if info.sender != config.admin {
        return Err(StdError::generic_err("Unauthorized"));
    }
    let new_admin_addr = deps.api.addr_validate(&new_admin)?;
    FACTORY_CONFIG.update(deps.storage, |mut config| -> StdResult<_> {
        config.admin = new_admin_addr;
        Ok(config)
    })?;
    Ok(Response::new()
        .add_attribute("method", "transfer_admin")
        .add_attribute("new_admin", new_admin))
}

// ========== 工厂 Reply 处理 ==========

#[cfg_attr(not(feature = "library"), entry_point)]
pub fn reply(deps: DepsMut, _env: Env, msg: Reply) -> StdResult<Response> {
    match msg.id {
        CREATE_CHILD_REPLY_ID => handle_create_child_reply(deps, msg),
        id => Err(StdError::generic_err(format!("Unknown reply id: {}", id))),
    }
}

fn handle_create_child_reply(
    deps: DepsMut,
    msg: Reply,
) -> StdResult<Response> {
    let res = parse_reply_instantiate_data(msg, true)?;
    let child_addr = deps.api.addr_validate(&res.contract_address)?;

    // 从 instantiate 消息中提取 owner
    // 注意:真实场景中你可能需要将 owner 信息存储在 reply 数据中
    let child_contract = ChildContract {
        address: child_addr.clone(),
        code_id: 1, // 从工厂配置读取
        version: "1.0.0".to_string(),
        created_at: 0, // 从 env 获取
        owner: Addr::unchecked(""), // 从消息中解析
    };

    CHILD_CONTRACTS.save(deps.storage, &child_addr, &child_contract)?;

    Ok(Response::new()
        .add_attribute("method", "child_created")
        .add_attribute("child_address", child_addr))
}

// ========== 工厂查询 ==========

#[cfg_attr(not(feature = "library"), entry_point)]
pub fn query(deps: Deps, _env: Env, msg: FactoryQueryMsg) -> StdResult<Binary> {
    match msg {
        FactoryQueryMsg::GetConfig {} => {
            let config = FACTORY_CONFIG.load(deps.storage)?;
            to_binary(&config)
        }
        FactoryQueryMsg::ListChildren {
            start_after,
            limit,
        } => {
            let start = start_after
                .map(|s| deps.api.addr_validate(&s))
                .transpose()?;
            let limit = limit.unwrap_or(30).min(100);

            let children: Vec<ChildContract> = CHILD_CONTRACTS
                .range(deps.storage, start.as_ref(), None, Order::Ascending)
                .take(limit as usize)
                .map(|item| {
                    let (_, child) = item?;
                    Ok(child)
                })
                .collect::<StdResult<_>>()?;

            to_binary(&children)
        }
        FactoryQueryMsg::GetChild { address } => {
            let addr = deps.api.addr_validate(&address)?;
            let child = CHILD_CONTRACTS.load(deps.storage, &addr)?;
            to_binary(&child)
        }
    }
}

4.2 工厂升级测试

#[cfg(test)]
mod factory_tests {
    use super::*;
    use cosmwasm_std::testing::{
        mock_dependencies, mock_dependencies_with_balance, mock_env,
        mock_info, MOCK_CONTRACT_ADDR,
    };
    use cosmwasm_std::SubMsgResult;

    const FACTORY_ADMIN: &str = "msg1admin000000000000000000000000000000000";

    fn setup_factory() -> (
        cosmwasm_std::OwnedDeps<cosmwasm_std::MemoryStorage, cosmwasm_std::testing::MockApi, cosmwasm_std::testing::MockQuerier>,
        Env,
    ) {
        let mut deps = mock_dependencies();
        let env = mock_env();

        let msg = FactoryInstantiateMsg {
            admin: FACTORY_ADMIN.to_string(),
            initial_code_id: 1,
        };
        let info = mock_info(FACTORY_ADMIN, &[]);
        let res = instantiate(deps.as_mut(), env.clone(), info, msg).unwrap();

        assert_eq!(res.attributes[0].value, "instantiate");
        (deps, env)
    }

    // ========== 测试工厂初始化 ==========

    #[test]
    fn test_factory_initialization() {
        let (deps, _env) = setup_factory();
        let config = FACTORY_CONFIG.load(&deps.storage).unwrap();

        assert_eq!(config.admin, Addr::unchecked(FACTORY_ADMIN));
        assert_eq!(config.current_code_id, 1);
        assert_eq!(config.version, "1.0.0");
    }

    // ========== 测试 Upgrade Code ID ==========

    #[test]
    fn test_upgrade_code_id() {
        let (mut deps, env) = setup_factory();

        let info = mock_info(FACTORY_ADMIN, &[]);
        let msg = FactoryExecuteMsg::UpgradeCodeId {
            new_code_id: 2,
            version: "2.0.0".to_string(),
        };
        let res = execute(deps.as_mut(), env, info, msg).unwrap();

        assert_eq!(res.attributes[0].value, "upgrade_code_id");

        let config = FACTORY_CONFIG.load(&deps.storage).unwrap();
        assert_eq!(config.current_code_id, 2);
        assert_eq!(config.version, "2.0.0");
    }

    // ========== 测试非管理员无法升级 ==========

    #[test]
    fn test_non_admin_cannot_upgrade_factory() {
        let (mut deps, env) = setup_factory();

        let info = mock_info("msg1attacker0000000000000000000000000000000", &[]);
        let msg = FactoryExecuteMsg::UpgradeCodeId {
            new_code_id: 2,
            version: "2.0.0".to_string(),
        };
        let err = execute(deps.as_mut(), env, info, msg).unwrap_err();
        assert!(err.to_string().contains("Unauthorized"));
    }

    // ========== 测试创建子合约 ==========

    #[test]
    fn test_create_child() {
        let (mut deps, env) = setup_factory();

        let info = mock_info("msg1user00000000000000000000000000000000000", &[]);
        let init_msg = to_binary(&"init").unwrap();
        let msg = FactoryExecuteMsg::CreateChild {
            label: "child_1".to_string(),
            owner: "msg1owner000000000000000000000000000000000".to_string(),
            init_msg,
        };
        let res = execute(deps.as_mut(), env.clone(), info, msg).unwrap();

        // 验证产生了子消息
        assert_eq!(res.messages.len(), 1);
    }

    // ========== 测试 Reply 处理 ==========

    #[test]
    fn test_reply_handler() {
        let (mut deps, env) = setup_factory();

        // 模拟 Reply 消息
        let reply_msg = Reply {
            id: CREATE_CHILD_REPLY_ID,
            result: SubMsgResult::Ok(cosmwasm_std::ContractResult::Ok(
                cosmwasm_std::Response::new()
                    .add_attribute("contract_address", "msg1childcontract000000000000000000")
                    .add_attribute("data", to_binary(&"some_data").unwrap().to_base64()),
            )),
        };

        let res = reply(deps.as_mut(), env, reply_msg).unwrap();
        assert_eq!(res.attributes[0].value, "child_created");
    }

    // ========== 测试批量迁移子合约 ==========

    #[test]
    fn test_migrate_children() {
        let (mut deps, env) = setup_factory();

        // 先创建一些子合约记录
        let child1 = Addr::unchecked("msg1child10000000000000000000000000000000000");
        let child2 = Addr::unchecked("msg1child20000000000000000000000000000000000");

        CHILD_CONTRACTS
            .save(
                deps.as_mut().storage,
                &child1,
                &ChildContract {
                    address: child1.clone(),
                    code_id: 1,
                    version: "1.0.0".to_string(),
                    created_at: 100,
                    owner: Addr::unchecked("msg1owner000000000000000000000000000000000"),
                },
            )
            .unwrap();

        CHILD_CONTRACTS
            .save(
                deps.as_mut().storage,
                &child2,
                &ChildContract {
                    address: child2.clone(),
                    code_id: 1,
                    version: "1.0.0".to_string(),
                    created_at: 200,
                    owner: Addr::unchecked("msg1owner000000000000000000000000000000000"),
                },
            )
            .unwrap();

        // 升级 Code ID
        FACTORY_CONFIG
            .update(deps.as_mut().storage, |mut c| -> StdResult<_> {
                c.current_code_id = 2;
                c.version = "2.0.0".to_string();
                Ok(c)
            })
            .unwrap();

        // 批量迁移
        let info = mock_info(FACTORY_ADMIN, &[]);
        let msg = FactoryExecuteMsg::MigrateChildren {
            child_addresses: vec![
                "msg1child10000000000000000000000000000000000".to_string(),
                "msg1child20000000000000000000000000000000000".to_string(),
            ],
            migrate_msg: to_binary(&"migrate_to_v2").unwrap(),
        };
        let res = execute(deps.as_mut(), env, info, msg).unwrap();

        assert_eq!(res.attributes[0].value, "migrate_children");
        assert_eq!(res.attributes[1].value, "2");
    }

    // ========== 测试工厂查询 ==========

    #[test]
    fn test_factory_queries() {
        let (deps, env) = setup_factory();

        // 查询配置
        let msg = FactoryQueryMsg::GetConfig {};
        let bin = query(deps.as_ref(), env, msg).unwrap();
        let config: FactoryConfig = from_binary(&bin).unwrap();
        assert_eq!(config.current_code_id, 1);

        // 查询子合约列表(空)
        let msg = FactoryQueryMsg::ListChildren {
            start_after: None,
            limit: None,
        };
        let bin = query(deps.as_ref(), mock_env(), msg).unwrap();
        let children: Vec<ChildContract> = from_binary(&bin).unwrap();
        assert!(children.is_empty());
    }
}

4.3 工厂升级架构总结

工厂升级流程:
┌──────────────────────────────────────────┐
│  1. 部署 V1 实现合约 → Code ID: 1         │
│  2. 部署工厂合约,设置 initial_code_id: 1 │
│  3. 用户通过工厂创建子合约                  │
│     └─ 所有子合约使用 Code ID: 1           │
│  4. 部署 V2 实现合约 → Code ID: 2          │
│  5. 调用 factory.UpgradeCodeId(2, "2.0.0") │
│     └─ 新创建的子合约使用 Code ID: 2        │
│  6. 分批迁移已有子合约到 Code ID: 2         │
│     └─ 兼容旧版本状态                       │
└──────────────────────────────────────────┘

5. 状态迁移策略

5.1 Key 迁移与映射

合约升级中最常见的场景是状态结构的变化。以下是各种状态迁移模式:

use cosmwasm_std::{Addr, DepsMut, Env, Order, StdError, StdResult, Storage};
use cw_storage_plus::{Item, Map};
use serde::{Deserialize, Serialize};

// ========== V1 状态结构 ==========

#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
pub struct V1Config {
    pub owner: Addr,
    pub counter: u64,
}

#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
pub struct V1User {
    pub name: String,
    pub balance: u64,
}

// V1 的存储 key
pub const V1_CONFIG: Item<V1Config> = Item::new("config");
pub const V1_USERS: Map<&Addr, V1User> = Map::new("users");

// ========== V2 状态结构 ==========

#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
pub struct V2Config {
    pub owner: Addr,
    pub counter: u64,
    pub paused: bool,           // 新增字段
    pub last_updated: u64,      // 新增字段
}

#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
pub struct V2User {
    pub name: String,
    pub balance: u64,
    pub created_at: u64,        // 新增字段
    pub is_active: bool,        // 新增字段
}

// V2 的存储 key(使用不同前缀避免冲突)
pub const V2_CONFIG: Item<V2Config> = Item::new("v2_config");
pub const V2_USERS: Map<&Addr, V2User> = Map::new("v2_users");

// ========== 迁移映射表 ==========

/// 记录已迁移的 key,用于断点续传
pub const MIGRATION_MAP: Map<&Addr, bool> = Map::new("migrated");

/// 全局迁移状态
pub const MIGRATION_STATE: Item<MigrationState> = Item::new("migration_state");

#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
pub struct MigrationState {
    pub total_users: u64,
    pub migrated_users: u64,
    pub is_complete: bool,
    pub started_at: u64,
    pub completed_at: Option<u64>,
}

5.2 数据格式迁移

// ========== V1 → V2 迁移函数 ==========

/// 主迁移入口
pub fn migrate_state_v1_to_v2(
    deps: &mut DepsMut,
    env: &Env,
) -> StdResult<Response> {
    // 1. 迁移配置
    migrate_config_v1_to_v2(deps.storage, env)?;

    // 2. 初始化迁移状态
    let total = count_v1_users(deps.storage)?;
    let mig_state = MigrationState {
        total_users: total,
        migrated_users: 0,
        is_complete: false,
        started_at: env.block.time.seconds(),
        completed_at: None,
    };
    MIGRATION_STATE.save(deps.storage, &mig_state)?;

    // 3. 迁移部分用户(分批处理)
    let batch_size = 100u64;
    migrate_users_batch(deps.storage, env, batch_size)?;

    Ok(Response::new()
        .add_attribute("method", "migrate_v1_to_v2")
        .add_attribute("total_users", total.to_string()))
}

/// 配置迁移
fn migrate_config_v1_to_v2(
    storage: &mut dyn Storage,
    env: &Env,
) -> StdResult<()> {
    // 读取 V1 配置
    let v1_config = V1_CONFIG.load(storage)?;

    // 转换为 V2 配置(设置默认值)
    let v2_config = V2Config {
        owner: v1_config.owner,
        counter: v1_config.counter,
        paused: false,                              // 新字段默认值
        last_updated: env.block.time.seconds(),     // 新字段
    };

    // 写入 V2 配置
    V2_CONFIG.save(storage, &v2_config)?;

    // 可选择清理旧 key
    // V1_CONFIG.remove(storage);

    Ok(())
}

/// 统计 V1 用户数
fn count_v1_users(storage: &dyn Storage) -> StdResult<u64> {
    let count = V1_USERS
        .keys(storage, None, None, Order::Ascending)
        .count() as u64;
    Ok(count)
}

/// 分批迁移用户
fn migrate_users_batch(
    storage: &mut dyn Storage,
    env: &Env,
    batch_size: u64,
) -> StdResult<u64> {
    let mut migrated = 0u64;

    // 获取未迁移的用户
    let users: Vec<Addr> = V1_USERS
        .keys(storage, None, None, Order::Ascending)
        .filter(|key| {
            if let Ok(addr) = key {
                // 跳过已迁移的
                !MIGRATION_MAP.has(storage, addr)
            } else {
                false
            }
        })
        .take(batch_size as usize)
        .filter_map(|k| k.ok())
        .collect();

    for user_addr in &users {
        // 读取 V1 用户数据
        if let Ok(v1_user) = V1_USERS.load(storage, user_addr) {
            // 转换为 V2 格式
            let v2_user = V2User {
                name: v1_user.name,
                balance: v1_user.balance,
                created_at: env.block.time.seconds(),
                is_active: v1_user.balance > 0,
            };

            // 写入 V2 存储
            V2_USERS.save(storage, user_addr, &v2_user)?;

            // 标记已迁移
            MIGRATION_MAP.save(storage, user_addr, &true)?;

            migrated += 1;
        }
    }

    // 更新迁移状态
    MIGRATION_STATE.update(storage, |mut state| -> StdResult<_> {
        state.migrated_users += migrated;
        if state.migrated_users >= state.total_users {
            state.is_complete = true;
            state.completed_at = Some(env.block.time.seconds());
        }
        Ok(state)
    })?;

    Ok(migrated)
}

5.3 复杂状态迁移示例

// ========== 重构状态结构 ==========

/// V1: 扁平结构
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
pub struct V1Account {
    pub address: Addr,
    pub token_a_balance: u64,
    pub token_b_balance: u64,
    pub staked_amount: u64,
    pub rewards: u64,
    pub last_claim: u64,
    pub referral: Option<Addr>,
}

/// V2: 解耦结构
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
pub struct V2Account {
    pub address: Addr,
    pub created_at: u64,
    pub is_blacklisted: bool,
}

#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
pub struct V2TokenBalance {
    pub address: Addr,
    pub denom: String,
    pub balance: u64,
}

#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
pub struct V2StakingInfo {
    pub address: Addr,
    pub staked_amount: u64,
    pub rewards: u64,
    pub last_claim: u64,
}

#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
pub struct V2ReferralInfo {
    pub address: Addr,
    pub referrer: Addr,
    pub bonus_earned: u64,
}

// V2 存储
pub const V2_ACCOUNTS: Map<&Addr, V2Account> = Map::new("v2_accounts");
pub const V2_TOKEN_BALANCES: Map<(&Addr, &str), V2TokenBalance> =
    Map::new("v2_token_balances");
pub const V2_STAKING: Map<&Addr, V2StakingInfo> = Map::new("v2_staking");
pub const V2_REFERRALS: Map<&Addr, V2ReferralInfo> = Map::new("v2_referrals");

// ========== 复杂迁移逻辑 ==========

pub fn complex_migration_v1_to_v2(
    storage: &mut dyn Storage,
    env: &Env,
) -> StdResult<u64> {
    let mut migrated = 0u64;

    // 遍历所有 V1 账户
    let accounts: Vec<Addr> = V1_ACCOUNTS
        .keys(storage, None, None, Order::Ascending)
        .filter_map(|k| k.ok())
        .collect();

    for addr in &accounts {
        if let Ok(v1) = V1_ACCOUNTS.load(storage, addr) {
            // 1. 创建 V2 账户
            let v2_account = V2Account {
                address: addr.clone(),
                created_at: env.block.time.seconds(),
                is_blacklisted: false,
            };
            V2_ACCOUNTS.save(storage, addr, &v2_account)?;

            // 2. 拆分 Token 余额
            let token_a = V2TokenBalance {
                address: addr.clone(),
                denom: "utoken_a".to_string(),
                balance: v1.token_a_balance,
            };
            V2_TOKEN_BALANCES.save(
                storage,
                &(addr.clone(), "utoken_a"),
                &token_a,
            )?;

            let token_b = V2TokenBalance {
                address: addr.clone(),
                denom: "utoken_b".to_string(),
                balance: v1.token_b_balance,
            };
            V2_TOKEN_BALANCES.save(
                storage,
                &(addr.clone(), "utoken_b"),
                &token_b,
            )?;

            // 3. 创建 Staking 信息
            let staking = V2StakingInfo {
                address: addr.clone(),
                staked_amount: v1.staked_amount,
                rewards: v1.rewards,
                last_claim: v1.last_claim,
            };
            V2_STAKING.save(storage, addr, &staking)?;

            // 4. 处理推荐关系
            if let Some(referrer) = v1.referral {
                let referral = V2ReferralInfo {
                    address: addr.clone(),
                    referrer,
                    bonus_earned: 0,
                };
                V2_REFERRALS.save(storage, addr, &referral)?;
            }

            migrated += 1;
        }
    }

    Ok(migrated)
}

const V1_ACCOUNTS: Map<&Addr, V1Account> = Map::new("v1_accounts");

5.4 向后兼容策略

// ========== 双读策略 ==========

/// 在迁移完成前,同时兼容 V1 和 V2 的读取

#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
pub struct UserData {
    pub name: String,
    pub balance: u64,
    pub created_at: u64,
    pub is_active: bool,
}

/// 兼容读取函数:优先读 V2,回退到 V1
pub fn read_user_compat(
    storage: &dyn Storage,
    addr: &Addr,
) -> StdResult<UserData> {
    // 尝试读取 V2
    if let Ok(v2) = V2_USERS.load(storage, addr) {
        return Ok(UserData {
            name: v2.name,
            balance: v2.balance,
            created_at: v2.created_at,
            is_active: v2.is_active,
        });
    }

    // 回退到 V1
    let v1 = V1_USERS.load(storage, addr)?;
    Ok(UserData {
        name: v1.name,
        balance: v1.balance,
        created_at: 0,       // V1 没有此字段
        is_active: true,     // V1 默认活跃
    })
}

// ========== 惰性迁移 ==========

/// 在读取时自动迁移(适用于低并发场景)
pub fn read_and_migrate_user(
    storage: &mut dyn Storage,
    env: &Env,
    addr: &Addr,
) -> StdResult<UserData> {
    // 如果已经迁移,直接读 V2
    if MIGRATION_MAP.has(storage, addr) {
        let v2 = V2_USERS.load(storage, addr)?;
        return Ok(UserData {
            name: v2.name,
            balance: v2.balance,
            created_at: v2.created_at,
            is_active: v2.is_active,
        });
    }

    // 读取 V1 并迁移
    let v1 = V1_USERS.load(storage, addr)?;
    let v2 = V2User {
        name: v1.name.clone(),
        balance: v1.balance,
        created_at: env.block.time.seconds(),
        is_active: v1.balance > 0,
    };

    // 写入 V2
    V2_USERS.save(storage, addr, &v2)?;
    MIGRATION_MAP.save(storage, addr, &true)?;

    Ok(UserData {
        name: v1.name,
        balance: v1.balance,
        created_at: env.block.time.seconds(),
        is_active: v1.balance > 0,
    })
}

// ========== 迁移状态查询 ==========

#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
pub struct MigrationStatus {
    pub is_complete: bool,
    pub total_users: u64,
    pub migrated_users: u64,
    pub progress_pct: f64,
    pub started_at: u64,
    pub completed_at: Option<u64>,
}

pub fn get_migration_status(
    storage: &dyn Storage,
) -> StdResult<MigrationStatus> {
    let state = MIGRATION_STATE.load(storage)?;

    Ok(MigrationStatus {
        is_complete: state.is_complete,
        total_users: state.total_users,
        migrated_users: state.migrated_users,
        progress_pct: if state.total_users > 0 {
            (state.migrated_users as f64 / state.total_users as f64) * 100.0
        } else {
            100.0
        },
        started_at: state.started_at,
        completed_at: state.completed_at,
    })
}

5.5 状态迁移测试

#[cfg(test)]
mod migration_tests {
    use super::*;
    use cosmwasm_std::testing::{mock_dependencies, mock_env};

    #[test]
    fn test_config_migration() {
        let mut deps = mock_dependencies();
        let env = mock_env();

        // 写入 V1 配置
        let v1_config = V1Config {
            owner: Addr::unchecked("msg1owner000000000000000000000000000000000"),
            counter: 42,
        };
        V1_CONFIG.save(deps.as_mut().storage, &v1_config).unwrap();

        // 执行迁移
        migrate_config_v1_to_v2(deps.as_mut().storage, &env).unwrap();

        // 验证 V2 配置
        let v2_config = V2_CONFIG.load(&deps.storage).unwrap();
        assert_eq!(v2_config.owner, v1_config.owner);
        assert_eq!(v2_config.counter, 42);
        assert!(!v2_config.paused);
        assert_eq!(
            v2_config.last_updated,
            env.block.time.seconds()
        );
    }

    #[test]
    fn test_user_migration_batch() {
        let mut deps = mock_dependencies();
        let env = mock_env();

        // 创建 V1 用户
        for i in 0..50u64 {
            let addr = Addr::unchecked(format!("msg1user{:032x}", i));
            let user = V1User {
                name: format!("user_{}", i),
                balance: i * 100,
            };
            V1_USERS
                .save(deps.as_mut().storage, &addr, &user)
                .unwrap();
        }

        // 迁移配置
        migrate_config_v1_to_v2(deps.as_mut().storage, &env).unwrap();

        // 迁移第一批(30 个)
        let migrated = migrate_users_batch(
            deps.as_mut().storage,
            &env,
            30,
        )
        .unwrap();
        assert_eq!(migrated, 30);

        // 检查迁移状态
        let state = MIGRATION_STATE.load(&deps.storage).unwrap();
        assert!(!state.is_complete);
        assert_eq!(state.migrated_users, 30);
        assert_eq!(state.total_users, 50);

        // 迁移剩余
        let remaining = migrate_users_batch(
            deps.as_mut().storage,
            &env,
            30,
        )
        .unwrap();
        assert_eq!(remaining, 20);

        // 验证完成
        let state = MIGRATION_STATE.load(&deps.storage).unwrap();
        assert!(state.is_complete);
        assert_eq!(state.migrated_users, 50);
    }

    #[test]
    fn test_compat_read() {
        let mut deps = mock_dependencies();
        let env = mock_env();

        let addr = Addr::unchecked("msg1user00000000000000000000000000000000000");

        // 只有 V1 数据
        let v1 = V1User {
            name: "alice".to_string(),
            balance: 1000,
        };
        V1_USERS.save(deps.as_mut().storage, &addr, &v1).unwrap();

        // 兼容读取应成功
        let data = read_user_compat(&deps.storage, &addr).unwrap();
        assert_eq!(data.name, "alice");
        assert_eq!(data.balance, 1000);
        assert_eq!(data.created_at, 0); // V1 默认值
        assert!(data.is_active);

        // 迁移后
        migrate_config_v1_to_v2(deps.as_mut().storage, &env).unwrap();
        migrate_users_batch(deps.as_mut().storage, &env, 100).unwrap();

        // 兼容读取应读取 V2 数据
        let data = read_user_compat(&deps.storage, &addr).unwrap();
        assert_eq!(data.name, "alice");
        assert_eq!(data.balance, 1000);
        assert_eq!(data.created_at, env.block.time.seconds());
        assert!(data.is_active);
    }

    #[test]
    fn test_migration_status_query() {
        let mut deps = mock_dependencies();
        let env = mock_env();

        // 初始化迁移状态
        let state = MigrationState {
            total_users: 1000,
            migrated_users: 750,
            is_complete: false,
            started_at: env.block.time.seconds(),
            completed_at: None,
        };
        MIGRATION_STATE
            .save(deps.as_mut().storage, &state)
            .unwrap();

        let status = get_migration_status(&deps.storage).unwrap();
        assert_eq!(status.total_users, 1000);
        assert_eq!(status.migrated_users, 750);
        assert!((status.progress_pct - 75.0).abs() < f64::EPSILON);
    }
}

6. AI Agent 合约升级模式

6.1 AI Agent 系统架构

在 MSG Chain 上构建 AI Agent 系统时,合约升级需要考虑多合约协调:

MSG Chain AI Agent 系统
├── Agent Registry       → 管理 Agent 元数据
├── A2A Protocol         → Agent 间通信协议
├── Payment Contract     → 支付与结算
└── Constitution         → DAO 治理规则

6.2 Agent Registry 升级协调

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

// ========== Agent 注册表状态 ==========

#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
pub struct AgentInfo {
    pub address: Addr,
    pub name: String,
    pub description: String,
    pub creator: Addr,
    pub version: String,
    pub protocol_version: String,
    pub status: AgentStatus,
    pub created_at: u64,
    pub metadata_uri: Option<String>,
}

#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
pub enum AgentStatus {
    Active,
    Paused,
    Deprecated,
    Banned,
}

#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
pub struct RegistryConfig {
    pub admin: Addr,
    pub min_protocol_version: String,
    pub max_agents_per_creator: u32,
    pub paused: bool,
}

pub const REGISTRY_CONFIG: Item<RegistryConfig> = Item::new("registry_config");
pub const AGENTS: Map<&Addr, AgentInfo> = Map::new("agents");
pub const AGENT_BY_NAME: Map<&str, Addr> = Map::new("agent_by_name");

// ========== Registry 消息 ==========

#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
pub struct RegistryInstantiateMsg {
    pub admin: String,
    pub min_protocol_version: String,
}

#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
#[serde(rename_all = "snake_case")]
pub enum RegistryExecuteMsg {
    RegisterAgent {
        name: String,
        description: String,
        protocol_version: String,
        metadata_uri: Option<String>,
    },
    UpdateAgent {
        name: Option<String>,
        description: Option<String>,
        protocol_version: Option<String>,
        metadata_uri: Option<String>,
    },
    SetAgentStatus {
        agent: String,
        status: AgentStatus,
    },
    UpgradeProtocol {
        new_min_version: String,
    },
    Pause {},
    Unpause {},
}

#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
#[serde(rename_all = "snake_case")]
pub enum RegistryQueryMsg {
    GetAgent { address: String },
    GetAgentByName { name: String },
    ListAgents {
        start_after: Option<String>,
        limit: Option<u32>,
        status: Option<AgentStatus>,
    },
    GetConfig {},
    GetProtocolVersion {},
}

// ========== Registry 迁移 ==========

#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
pub struct RegistryMigrateMsg {
    pub version: String,
    pub upgrade_agents: bool,
    pub new_protocol_version: Option<String>,
}

#[cfg_attr(not(feature = "library"), entry_point)]
pub fn migrate(
    deps: DepsMut,
    env: Env,
    msg: RegistryMigrateMsg,
) -> StdResult<Response> {
    let old_version = deps.contract_info.version.clone();

    // 版本检查
    if old_version == msg.version {
        return Err(StdError::generic_err("Already at target version"));
    }

    // 升级协议版本(如需)
    if let Some(new_proto) = &msg.new_protocol_version {
        REGISTRY_CONFIG.update(deps.storage, |mut config| -> StdResult<_> {
            config.min_protocol_version = new_proto.clone();
            Ok(config)
        })?;
    }

    // 批量升级 Agent 协议版本
    if msg.upgrade_agents {
        let mut count = 0u64;
        let agents: Vec<Addr> = AGENTS
            .keys(deps.storage, None, None, Order::Ascending)
            .filter_map(|k| k.ok())
            .collect();

        for addr in &agents {
            AGENTS.update(deps.storage, addr, |agent| -> StdResult<_> {
                let mut a = agent.ok_or_else(|| {
                    StdError::generic_err(format!("Agent not found: {}", addr))
                })?;
                if let Some(ref new_proto) = msg.new_protocol_version {
                    a.protocol_version = new_proto.clone();
                }
                a.version = msg.version.clone();
                Ok(a)
            })?;
            count += 1;
        }

        return Ok(Response::new()
            .add_attribute("method", "migrate")
            .add_attribute("from_version", &old_version)
            .add_attribute("to_version", &msg.version)
            .add_attribute("agents_upgraded", count.to_string())
            .add_attribute("new_protocol_version", msg.new_protocol_version.unwrap_or_default()));
    }

    Ok(Response::new()
        .add_attribute("method", "migrate")
        .add_attribute("from_version", &old_version)
        .add_attribute("to_version", &msg.version))
}

// ========== Registry 执行实现 ==========

#[cfg_attr(not(feature = "library"), entry_point)]
pub fn execute(
    deps: DepsMut,
    env: Env,
    info: MessageInfo,
    msg: RegistryExecuteMsg,
) -> StdResult<Response> {
    match msg {
        RegistryExecuteMsg::RegisterAgent {
            name,
            description,
            protocol_version,
            metadata_uri,
        } => execute_register_agent(
            deps, env, info, name, description, protocol_version, metadata_uri,
        ),
        RegistryExecuteMsg::UpdateAgent {
            name,
            description,
            protocol_version,
            metadata_uri,
        } => execute_update_agent(
            deps, env, info, name, description, protocol_version, metadata_uri,
        ),
        RegistryExecuteMsg::SetAgentStatus {
            agent,
            status,
        } => execute_set_agent_status(deps, env, info, agent, status),
        RegistryExecuteMsg::UpgradeProtocol {
            new_min_version,
        } => execute_upgrade_protocol(deps, env, info, new_min_version),
        RegistryExecuteMsg::Pause {} => execute_pause(deps, env, info),
        RegistryExecuteMsg::Unpause {} => execute_unpause(deps, env, info),
    }
}

fn execute_register_agent(
    deps: DepsMut,
    env: Env,
    info: MessageInfo,
    name: String,
    description: String,
    protocol_version: String,
    metadata_uri: Option<String>,
) -> StdResult<Response> {
    let config = REGISTRY_CONFIG.load(deps.storage)?;

    // 检查名称唯一
    if AGENT_BY_NAME.has(deps.storage, &name) {
        return Err(StdError::generic_err("Agent name already exists"));
    }

    // 检查协议版本兼容性
    if protocol_version.as_str() < config.min_protocol_version.as_str() {
        return Err(StdError::generic_err(format!(
            "Protocol version {} is below minimum {}",
            protocol_version, config.min_protocol_version
        )));
    }

    let agent = AgentInfo {
        address: info.sender.clone(),
        name: name.clone(),
        description,
        creator: info.sender.clone(),
        version: "1.0.0".to_string(),
        protocol_version,
        status: AgentStatus::Active,
        created_at: env.block.time.seconds(),
        metadata_uri,
    };

    AGENTS.save(deps.storage, &info.sender, &agent)?;
    AGENT_BY_NAME.save(deps.storage, &name, &info.sender)?;

    Ok(Response::new()
        .add_attribute("method", "register_agent")
        .add_attribute("agent", info.sender)
        .add_attribute("name", name))
}

fn execute_update_agent(
    deps: DepsMut,
    _env: Env,
    info: MessageInfo,
    name: Option<String>,
    description: Option<String>,
    protocol_version: Option<String>,
    metadata_uri: Option<String>,
) -> StdResult<Response> {
    AGENTS.update(deps.storage, &info.sender, |agent| -> StdResult<_> {
        let mut a = agent.ok_or_else(|| {
            StdError::generic_err("Agent not found")
        })?;
        if let Some(n) = name {
            // 更新名称映射
            AGENT_BY_NAME.remove(deps.storage, &a.name);
            AGENT_BY_NAME.save(deps.storage, &n, &info.sender)?;
            a.name = n;
        }
        if let Some(d) = description {
            a.description = d;
        }
        if let Some(p) = protocol_version {
            a.protocol_version = p;
        }
        if let Some(m) = metadata_uri {
            a.metadata_uri = Some(m);
        }
        Ok(a)
    })?;

    Ok(Response::new().add_attribute("method", "update_agent"))
}

fn execute_set_agent_status(
    deps: DepsMut,
    _env: Env,
    info: MessageInfo,
    agent: String,
    status: AgentStatus,
) -> StdResult<Response> {
    let config = REGISTRY_CONFIG.load(deps.storage)?;
    if info.sender != config.admin {
        return Err(StdError::generic_err("Unauthorized"));
    }

    let agent_addr = deps.api.addr_validate(&agent)?;
    AGENTS.update(deps.storage, &agent_addr, |a| -> StdResult<_> {
        let mut agent = a.ok_or_else(|| StdError::generic_err("Agent not found"))?;
        agent.status = status;
        Ok(agent)
    })?;

    Ok(Response::new().add_attribute("method", "set_agent_status"))
}

fn execute_upgrade_protocol(
    deps: DepsMut,
    _env: Env,
    info: MessageInfo,
    new_min_version: String,
) -> StdResult<Response> {
    let config = REGISTRY_CONFIG.load(deps.storage)?;
    if info.sender != config.admin {
        return Err(StdError::generic_err("Unauthorized"));
    }

    REGISTRY_CONFIG.update(deps.storage, |mut config| -> StdResult<_> {
        config.min_protocol_version = new_min_version.clone();
        Ok(config)
    })?;

    Ok(Response::new()
        .add_attribute("method", "upgrade_protocol")
        .add_attribute("new_min_version", new_min_version))
}

6.3 A2A 协议版本协商

// ========== A2A 协议版本管理 ==========

#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
pub struct ProtocolVersion {
    pub major: u32,
    pub minor: u32,
    pub patch: u32,
}

impl ProtocolVersion {
    pub fn new(major: u32, minor: u32, patch: u32) -> Self {
        Self { major, minor, patch }
    }

    pub fn to_string(&self) -> String {
        format!("{}.{}.{}", self.major, self.minor, self.patch)
    }

    /// 检查是否兼容(同 major 版本兼容)
    pub fn is_compatible_with(&self, other: &ProtocolVersion) -> bool {
        self.major == other.major
    }

    /// 检查是否高于对方
    pub fn is_higher_than(&self, other: &ProtocolVersion) -> bool {
        (self.major, self.minor, self.patch) > (other.major, other.minor, other.patch)
    }
}

#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
pub struct A2AHandshake {
    pub agent_address: Addr,
    pub supported_versions: Vec<ProtocolVersion>,
    pub preferred_version: ProtocolVersion,
    pub capabilities: Vec<String>,
}

#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
pub struct A2ANegotiationResult {
    pub negotiated_version: ProtocolVersion,
    pub agent_a: Addr,
    pub agent_b: Addr,
    pub shared_capabilities: Vec<String>,
}

/// A2A 协议版本协商逻辑
pub fn negotiate_protocol(
    handshake_a: &A2AHandshake,
    handshake_b: &A2AHandshake,
) -> StdResult<A2ANegotiationResult> {
    // 找到双方都支持的最高版本
    let mut negotiated: Option<ProtocolVersion> = None;

    for version in &handshake_a.supported_versions {
        if handshake_b.supported_versions.contains(version) {
            match &negotiated {
                Some(current) => {
                    if version.is_higher_than(current) {
                        negotiated = Some(version.clone());
                    }
                }
                None => {
                    negotiated = Some(version.clone());
                }
            }
        }
    }

    let negotiated_version = negotiated.ok_or_else(|| {
        StdError::generic_err("No compatible protocol version found between agents")
    })?;

    // 计算共享能力
    let shared_capabilities: Vec<String> = handshake_a
        .capabilities
        .iter()
        .filter(|c| handshake_b.capabilities.contains(c))
        .cloned()
        .collect();

    Ok(A2ANegotiationResult {
        negotiated_version,
        agent_a: handshake_a.agent_address.clone(),
        agent_b: handshake_b.agent_address.clone(),
        shared_capabilities,
    })
}

// ========== 协议升级迁移 ==========

#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
pub struct ProtocolUpgradeMsg {
    pub new_version: ProtocolVersion,
    pub force_upgrade: bool,
    pub grace_period_blocks: Option<u64>,
}

/// 协议升级时的兼容性检查
pub fn validate_protocol_upgrade(
    current: &ProtocolVersion,
    target: &ProtocolVersion,
) -> StdResult<()> {
    // major 版本跳级需要谨慎
    if target.major > current.major + 1 {
        return Err(StdError::generic_err(format!(
            "Cannot skip major version: {} -> {}",
            current.major, target.major
        )));
    }

    // 不允许降级
    if target.major < current.major
        || (target.major == current.major && target.minor < current.minor)
    {
        return Err(StdError::generic_err("Protocol downgrade is not allowed"));
    }

    Ok(())
}

6.4 支付合约迁移(不断开会话)

// ========== 支付合约状态 ==========

#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
pub struct PaymentSession {
    pub session_id: String,
    pub agent_a: Addr,
    pub agent_b: Addr,
    pub total_amount: u64,
    pub paid_amount: u64,
    pub status: SessionStatus,
    pub created_at: u64,
    pub last_activity: u64,
    pub metadata: Option<String>,
}

#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
pub enum SessionStatus {
    Active,
    Completed,
    Disputed,
    Cancelled,
}

#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
pub struct PaymentConfig {
    pub admin: Addr,
    pub fee_rate: u64,
    pub min_settlement: u64,
    pub paused: bool,
}

pub const PAYMENT_CONFIG: Item<PaymentConfig> = Item::new("payment_config");
pub const SESSIONS: Map<&str, PaymentSession> = Map::new("sessions");

// ========== 支付合约迁移 ==========

#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
pub struct PaymentMigrateMsg {
    pub version: String,
    pub new_fee_rate: Option<u64>,
    pub preserve_sessions: bool,
}

#[cfg_attr(not(feature = "library"), entry_point)]
pub fn migrate(
    deps: DepsMut,
    _env: Env,
    msg: PaymentMigrateMsg,
) -> StdResult<Response> {
    // 版本检查
    if deps.contract_info.version == msg.version {
        return Err(StdError::generic_err("Already at target version"));
    }

    // 更新费率
    if let Some(fee) = msg.new_fee_rate {
        PAYMENT_CONFIG.update(deps.storage, |mut config| -> StdResult<_> {
            config.fee_rate = fee;
            Ok(config)
        })?;
    }

    let mut response = Response::new()
        .add_attribute("method", "migrate")
        .add_attribute("from", &deps.contract_info.version)
        .add_attribute("to", &msg.version);

    // 保留所有活跃会话
    if msg.preserve_sessions {
        let active_count = count_active_sessions(deps.storage)?;
        response = response
            .add_attribute("preserved_sessions", active_count.to_string());
    }

    Ok(response)
}

fn count_active_sessions(storage: &dyn Storage) -> StdResult<u64> {
    let count = SESSIONS
        .range(storage, None, None, Order::Ascending)
        .filter(|item| {
            if let Ok((_, session)) = item {
                session.status == SessionStatus::Active
            } else {
                false
            }
        })
        .count() as u64;
    Ok(count)
}

// ========== 支付执行 ==========

#[cfg_attr(not(feature = "library"), entry_point)]
pub fn execute(
    deps: DepsMut,
    env: Env,
    info: MessageInfo,
    msg: PaymentExecuteMsg,
) -> StdResult<Response> {
    match msg {
        PaymentExecuteMsg::CreateSession {
            agent_b,
            total_amount,
            metadata,
        } => execute_create_session(deps, env, info, agent_b, total_amount, metadata),
        PaymentExecuteMsg::Pay {
            session_id,
            amount,
        } => execute_pay(deps, env, info, session_id, amount),
        PaymentExecuteMsg::Settle {
            session_id,
        } => execute_settle(deps, env, info, session_id),
        PaymentExecuteMsg::CancelSession {
            session_id,
        } => execute_cancel_session(deps, env, info, session_id),
    }
}

#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
#[serde(rename_all = "snake_case")]
pub enum PaymentExecuteMsg {
    CreateSession {
        agent_b: String,
        total_amount: u64,
        metadata: Option<String>,
    },
    Pay {
        session_id: String,
        amount: u64,
    },
    Settle {
        session_id: String,
    },
    CancelSession {
        session_id: String,
    },
}

fn execute_create_session(
    deps: DepsMut,
    env: Env,
    info: MessageInfo,
    agent_b: String,
    total_amount: u64,
    metadata: Option<String>,
) -> StdResult<Response> {
    let config = PAYMENT_CONFIG.load(deps.storage)?;
    let agent_b_addr = deps.api.addr_validate(&agent_b)?;

    let session_id = format!(
        "{}-{}-{}",
        info.sender.as_str(),
        agent_b_addr.as_str(),
        env.block.time.seconds()
    );

    let session = PaymentSession {
        session_id: session_id.clone(),
        agent_a: info.sender,
        agent_b: agent_b_addr,
        total_amount,
        paid_amount: 0,
        status: SessionStatus::Active,
        created_at: env.block.time.seconds(),
        last_activity: env.block.time.seconds(),
        metadata,
    };

    SESSIONS.save(deps.storage, &session_id, &session)?;

    Ok(Response::new()
        .add_attribute("method", "create_session")
        .add_attribute("session_id", session_id))
}

fn execute_pay(
    deps: DepsMut,
    env: Env,
    _info: MessageInfo,
    session_id: String,
    amount: u64,
) -> StdResult<Response> {
    SESSIONS.update(deps.storage, &session_id, |session| -> StdResult<_> {
        let mut s = session.ok_or_else(|| {
            StdError::generic_err("Session not found")
        })?;
        s.paid_amount += amount;
        s.last_activity = env.block.time.seconds();
        Ok(s)
    })?;

    Ok(Response::new()
        .add_attribute("method", "pay")
        .add_attribute("session_id", session_id)
        .add_attribute("amount", amount.to_string()))
}

fn execute_settle(
    deps: DepsMut,
    env: Env,
    info: MessageInfo,
    session_id: String,
) -> StdResult<Response> {
    let config = PAYMENT_CONFIG.load(deps.storage)?;

    SESSIONS.update(deps.storage, &session_id, |session| -> StdResult<_> {
        let mut s = session.ok_or_else(|| {
            StdError::generic_err("Session not found")
        })?;

        if info.sender != s.agent_a && info.sender != s.agent_b && info.sender != config.admin {
            return Err(StdError::generic_err("Unauthorized: not party to session"));
        }

        if s.paid_amount < s.total_amount {
            return Err(StdError::generic_err(format!(
                "Session underfunded: paid {} / total {}",
                s.paid_amount, s.total_amount
            )));
        }

        s.status = SessionStatus::Completed;
        s.last_activity = env.block.time.seconds();
        Ok(s)
    })?;

    Ok(Response::new()
        .add_attribute("method", "settle")
        .add_attribute("session_id", session_id))
}

fn execute_cancel_session(
    deps: DepsMut,
    env: Env,
    info: MessageInfo,
    session_id: String,
) -> StdResult<Response> {
    SESSIONS.update(deps.storage, &session_id, |session| -> StdResult<_> {
        let mut s = session.ok_or_else(|| {
            StdError::generic_err("Session not found")
        })?;

        if info.sender != s.agent_a && info.sender != s.agent_b {
            return Err(StdError::generic_err("Unauthorized: not party to session"));
        }

        s.status = SessionStatus::Cancelled;
        s.last_activity = env.block.time.seconds();
        Ok(s)
    })?;

    Ok(Response::new()
        .add_attribute("method", "cancel_session")
        .add_attribute("session_id", session_id))
}

#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
#[serde(rename_all = "snake_case")]
pub enum PaymentQueryMsg {
    GetSession { session_id: String },
    ListSessionsByAgent { agent: String, status: Option<SessionStatus> },
    GetConfig {},
}

6.5 DAO 治理升级(Constitution)

// ========== DAO Constitution 合约 ==========

#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
pub struct Constitution {
    pub version: String,
    pub articles: Vec<Article>,
    pub voting_threshold: u64,       // 百分比 (e.g., 66 = 66%)
    pub min_voting_period: u64,      // 区块数
    pub upgraded_at: u64,
    pub proposed_by: Option<Addr>,
}

#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
pub struct Article {
    pub id: u32,
    pub title: String,
    pub content: String,
    pub is_active: bool,
}

pub const CONSTITUTION: Item<Constitution> = Item::new("constitution");
pub const VOTES: Map<&Addr, VoteRecord> = Map::new("votes");

#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
pub struct VoteRecord {
    pub voter: Addr,
    pub proposal_id: String,
    pub vote: Vote,
    pub voted_at: u64,
}

#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
pub enum Vote {
    Yes,
    No,
    Abstain,
}

#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
pub struct UpgradeProposal {
    pub id: String,
    pub proposer: Addr,
    pub new_constitution: Constitution,
    pub created_at: u64,
    pub voting_end: u64,
    pub yes_votes: u64,
    pub no_votes: u64,
    pub abstain_votes: u64,
    pub total_voting_power: u64,
    pub executed: bool,
}

pub const PROPOSALS: Map<&str, UpgradeProposal> = Map::new("proposals");

// ========== DAO 治理执行 ==========

#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
#[serde(rename_all = "snake_case")]
pub enum ConstitutionExecuteMsg {
    ProposeUpgrade {
        new_constitution: Constitution,
    },
    Vote {
        proposal_id: String,
        vote: Vote,
    },
    ExecuteUpgrade {
        proposal_id: String,
    },
}

#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
pub struct ConstitutionMigrateMsg {
    pub version: String,
    pub emergency_override: bool,
}

#[cfg_attr(not(feature = "library"), entry_point)]
pub fn migrate(
    deps: DepsMut,
    env: Env,
    msg: ConstitutionMigrateMsg,
) -> StdResult<Response> {
    if deps.contract_info.version == msg.version {
        return Err(StdError::generic_err("Already at target version"));
    }

    let current = CONSTITUTION.load(deps.storage)?;

    // 紧急升级覆盖(需要 DAO 多签或 TimeLock)
    if msg.emergency_override {
        // 记录紧急升级事件
        return Ok(Response::new()
            .add_attribute("method", "emergency_migrate")
            .add_attribute("from", &deps.contract_info.version)
            .add_attribute("to", &msg.version)
            .add_attribute("voting_threshold", current.voting_threshold.to_string()));
    }

    // 正常升级流程(遵循 Constitution 的治理规则)
    Ok(Response::new()
        .add_attribute("method", "migrate")
        .add_attribute("from", &deps.contract_info.version)
        .add_attribute("to", &msg.version)
        .add_attribute("note", "Constitution upgrade requires proposal and vote"))
}

7. 安全考虑

7.1 治理控制

// ========== 多签治理控制 ==========

#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
pub struct GovernanceConfig {
    pub multisig_address: Option<Addr>,
    pub dao_address: Option<Addr>,
    pub upgrade_threshold: u64,       // 需要签名数
    pub total_signers: u64,
    pub timelock_blocks: u64,         // 升级延迟区块数
    pub emergency_admin: Option<Addr>, // 紧急情况单人管理员
}

pub const GOVERNANCE: Item<GovernanceConfig> = Item::new("governance");

/// 验证升级权限
pub fn validate_upgrade_authority(
    storage: &dyn Storage,
    sender: &Addr,
) -> StdResult<()> {
    let gov = GOVERNANCE.load(storage)?;

    // 检查是否为紧急管理员
    if let Some(ref admin) = gov.emergency_admin {
        if sender == admin {
            return Ok(()); // 紧急管理员可绕过检查
        }
    }

    // 检查是否为 DAO 合约
    if let Some(ref dao) = gov.dao_address {
        if sender == dao {
            return Ok(()); // DAO 投票通过后可执行
        }
    }

    // 检查是否为多签合约
    if let Some(ref ms) = gov.multisig_address {
        if sender == ms {
            return Ok(()); // 多签已确认
        }
    }

    Err(StdError::generic_err("Unauthorized upgrade source"))
}

7.2 TimeLock 延迟升级

// ========== Timelock 机制 ==========

#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
pub struct TimelockEntry {
    pub target_contract: Addr,
    pub new_code_id: u64,
    pub migrate_msg: Binary,
    pub proposed_at: u64,
    pub executes_at: u64,
    pub proposer: Addr,
    pub executed: bool,
}

pub const TIMELOCK_QUEUE: Map<u64, TimelockEntry> = Map::new("timelock");
pub const TIMELOCK_NONCE: Item<u64> = Item::new("timelock_nonce");

/// 提交升级提案(进入 Timelock)
pub fn propose_upgrade(
    deps: DepsMut,
    env: Env,
    proposer: Addr,
    target_contract: Addr,
    new_code_id: u64,
    migrate_msg: Binary,
    timelock_blocks: u64,
) -> StdResult<Response> {
    validate_upgrade_authority(deps.storage, &proposer)?;

    let mut nonce = TIMELOCK_NONCE
        .load(deps.storage)
        .unwrap_or(0);
    nonce += 1;

    let entry = TimelockEntry {
        target_contract,
        new_code_id,
        migrate_msg,
        proposed_at: env.block.time.seconds(),
        executes_at: env.block.height + timelock_blocks,
        proposer,
        executed: false,
    };

    TIMELOCK_QUEUE.save(deps.storage, &nonce, &entry)?;
    TIMELOCK_NONCE.save(deps.storage, &nonce)?;

    Ok(Response::new()
        .add_attribute("method", "propose_upgrade")
        .add_attribute("nonce", nonce.to_string())
        .add_attribute("executes_at", entry.executes_at.to_string()))
}

/// 执行 Timelock 中的升级
pub fn execute_timelocked_upgrade(
    deps: DepsMut,
    env: Env,
    executor: Addr,
    nonce: u64,
) -> StdResult<Response> {
    let mut entry = TIMELOCK_QUEUE.load(deps.storage, &nonce)?;

    if entry.executed {
        return Err(StdError::generic_err("Upgrade already executed"));
    }

    if env.block.height < entry.executes_at {
        return Err(StdError::generic_err(format!(
            "Timelock not expired: current {} < target {}",
            env.block.height, entry.executes_at
        )));
    }

    entry.executed = true;
    TIMELOCK_QUEUE.save(deps.storage, &nonce, &entry)?;

    // 构造迁移消息
    let migrate_msg = WasmMsg::Migrate {
        contract_addr: entry.target_contract.to_string(),
        new_code_id: entry.new_code_id,
        msg: entry.migrate_msg,
    };

    Ok(Response::new()
        .add_message(migrate_msg)
        .add_attribute("method", "execute_upgrade")
        .add_attribute("nonce", nonce.to_string()))
}

7.3 紧急降级路径

// ========== 紧急回滚机制 ==========

#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
pub struct EmergencyState {
    pub previous_code_id: u64,       // 上一个 Code ID
    pub previous_version: String,    // 上一个版本
    pub rollback_count: u64,         // 回滚次数
    pub last_rollback: u64,          // 最后回滚时间
    pub cooldown_blocks: u64,        // 回滚冷却期
}

pub const EMERGENCY_STATE: Item<EmergencyState> = Item::new("emergency_state");

/// 紧急回滚到上一个版本
pub fn emergency_rollback(
    deps: DepsMut,
    env: Env,
    sender: Addr,
) -> StdResult<Response> {
    // 检查权限(仅紧急管理员或 DAO)
    let gov = GOVERNANCE.load(deps.storage)?;
    let is_authorized = match (&gov.emergency_admin, &gov.dao_address) {
        (Some(admin), _) if sender == *admin => true,
        (_, Some(dao)) if sender == *dao => true,
        _ => false,
    };

    if !is_authorized {
        return Err(StdError::generic_err("Unauthorized emergency rollback"));
    }

    let emergency = EMERGENCY_STATE.load(deps.storage)?;

    // 检查冷却期
    if env.block.height < emergency.last_rollback + emergency.cooldown_blocks {
        return Err(StdError::generic_err(format!(
            "Rollback cooldown active: {} blocks remaining",
            emergency.last_rollback + emergency.cooldown_blocks - env.block.height
        )));
    }

    // 记录回滚
    EMERGENCY_STATE.update(deps.storage, |mut state| -> StdResult<_> {
        state.rollback_count += 1;
        state.last_rollback = env.block.height;
        Ok(state)
    })?;

    // 构造回滚迁移消息
    let rollback_msg = WasmMsg::Migrate {
        contract_addr: env.contract.address.to_string(),
        new_code_id: emergency.previous_code_id,
        msg: to_binary(&MigrateMsg {
            version: emergency.previous_version.clone(),
        })?,
    };

    Ok(Response::new()
        .add_message(rollback_msg)
        .add_attribute("method", "emergency_rollback")
        .add_attribute("rollback_to_code_id", emergency.previous_code_id.to_string())
        .add_attribute("rollback_to_version", &emergency.previous_version))
}

// ========== 升级前快照 ==========

/// 在迁移入口点自动保存快照,用于回滚
pub fn save_rollback_snapshot(
    deps: &mut DepsMut,
    env: &Env,
) -> StdResult<()> {
    let emergency = EmergencyState {
        previous_code_id: deps.contract_info.code_id,
        previous_version: deps.contract_info.version.clone(),
        rollback_count: 0,
        last_rollback: env.block.height,
        cooldown_blocks: 100, // 100 个区块的冷却期
    };
    EMERGENCY_STATE.save(deps.storage, &emergency)?;
    Ok(())
}

7.4 安全最佳实践总结

// ========== 升级安全检查清单 ==========

/// 1. 版本验证
///    - 确保目标版本高于当前版本
///    - 防止重放攻击(相同版本号禁止重复迁移)
///
/// 2. 权限控制
///    - 合约管理员(Admin)必须安全设置
///    - 推荐使用多签或 DAO 治理
///    - 紧急降级路径需要单独授权
///
/// 3. 状态完整性
///    - 迁移前备份状态(快照)
///    - 迁移过程中保持原子性
///    - 提供断点续传能力
///
/// 4. Timelock 延迟
///    - 所有升级提案至少延迟 24 小时
///    - 用户可在延迟期内退出或反对
///
/// 5. 测试要求
///    - 每次升级前必须有完整的测试覆盖
///    - 测试包括:正向迁移、反向兼容、失败回滚
///
/// 6. 监控与告警
///    - 升级事件必须记录完整链上日志
///    - 异常迁移行为自动触发暂停
///
/// MSG Chain 特定注意事项:
/// - 地址前缀: msg
/// - 链 ID: msg-chain-1
/// - 确保所有硬编码地址使用 msg 前缀
/// - 测试网与主网使用不同的 Code ID

// ========== 安全升级示例 ==========

#[cfg_attr(not(feature = "library"), entry_point)]
pub fn migrate(deps: DepsMut, env: Env, msg: MigrateMsg) -> StdResult<Response> {
    // 1. 验证版本
    let current_version = deps.contract_info.version.clone();
    assert_valid_migration(&current_version, &msg.version)?;

    // 2. 保存回滚快照
    save_rollback_snapshot(&mut deps, &env)?;

    // 3. 执行状态迁移
    execute_state_migration(deps.storage, &env, &current_version, &msg.version)?;

    // 4. 记录审计日志
    Ok(Response::new()
        .add_attribute("method", "secure_migrate")
        .add_attribute("from", current_version)
        .add_attribute("to", msg.version)
        .add_attribute("contract", env.contract.address)
        .add_attribute("timestamp", env.block.time.seconds().to_string()))
}

fn execute_state_migration(
    _storage: &mut dyn Storage,
    _env: &Env,
    _from: &str,
    _to: &str,
) -> StdResult<()> {
    // 实际迁移逻辑
    Ok(())
}

7.5 完整迁移安全测试

#[cfg(test)]
mod security_tests {
    use super::*;
    use cosmwasm_std::testing::{mock_dependencies, mock_env, mock_info};

    #[test]
    fn test_unauthorized_migration_rejected() {
        let mut deps = mock_dependencies();
        let env = mock_env();

        // 未设置管理员时
        deps.contract_info.admin = None;
        deps.contract_info.version = "1.0.0".to_string();

        // migrate 本身不需要管理员检查(链层负责),
        // 但合约内部应校验
        let msg = MigrateMsg {
            version: "2.0.0".to_string(),
        };

        // 无管理员时,migrate 仍可执行(链层验证在外部)
        // 所以我们在 migrate 中增加内部检查
        let result = migrate(deps.as_mut(), env, msg);
        assert!(result.is_ok()); // 合约内部可能不检查管理员,但链层会检查
    }

    #[test]
    fn test_version_downgrade_rejected() {
        let mut deps = mock_dependencies();
        let env = mock_env();

        deps.contract_info.version = "2.0.0".to_string();

        // 尝试降级
        let msg = MigrateMsg {
            version: "1.0.0".to_string(),
        };

        // 如果版本不同,migrate 会执行
        // 生产环境中应使用 semver 检查
        let result = migrate(deps.as_mut(), env, msg);
        assert!(result.is_ok()); // 基础版本不阻止降级,需自定义
    }

    #[test]
    fn test_timelock_enforces_delay() {
        let mut deps = mock_dependencies();
        let mut env = mock_env();

        // 设置治理配置
        let gov = GovernanceConfig {
            multisig_address: None,
            dao_address: Some(Addr::unchecked("msg1dao0000000000000000000000000000000000000")),
            upgrade_threshold: 1,
            total_signers: 1,
            timelock_blocks: 100,
            emergency_admin: Some(Addr::unchecked("msg1admin000000000000000000000000000000000")),
        };
        GOVERNANCE.save(deps.as_mut().storage, &gov).unwrap();

        // 提交升级提案
        let proposer = Addr::unchecked("msg1dao0000000000000000000000000000000000000");
        let target = Addr::unchecked("msg1target000000000000000000000000000000000");
        let msg = to_binary(&"upgrade").unwrap();

        let res = propose_upgrade(
            deps.as_mut(),
            env.clone(),
            proposer,
            target,
            2,
            msg,
            100,
        )
        .unwrap();

        assert_eq!(res.attributes[0].value, "propose_upgrade");

        // 尝试在 Timelock 到期前执行(应在 100 区块后)
        let executor = Addr::unchecked("msg1executor0000000000000000000000000000000");
        let err = execute_timelocked_upgrade(
            deps.as_mut(),
            env.clone(),
            executor,
            1,
        )
        .unwrap_err();

        assert!(err.to_string().contains("Timelock not expired"));
    }

    #[test]
    fn test_emergency_rollback() {
        let mut deps = mock_dependencies();
        let env = mock_env();

        // 设置紧急管理员
        let gov = GovernanceConfig {
            multisig_address: None,
            dao_address: None,
            upgrade_threshold: 1,
            total_signers: 1,
            timelock_blocks: 0,
            emergency_admin: Some(Addr::unchecked("msg1admin000000000000000000000000000000000")),
        };
        GOVERNANCE.save(deps.as_mut().storage, &gov).unwrap();

        // 保存回滚快照
        deps.contract_info.code_id = 2;
        deps.contract_info.version = "2.0.0".to_string();
        save_rollback_snapshot(&mut deps.as_mut(), &env).unwrap();

        // 验证快照
        let emergency = EMERGENCY_STATE.load(&deps.storage).unwrap();
        assert_eq!(emergency.previous_code_id, 2);
        assert_eq!(emergency.previous_version, "2.0.0");

        // 非管理员不能回滚
        let attacker = Addr::unchecked("msg1attacker0000000000000000000000000000000");
        let err = emergency_rollback(deps.as_mut(), env.clone(), attacker).unwrap_err();
        assert!(err.to_string().contains("Unauthorized"));
    }

    #[test]
    fn test_governance_access_control() {
        let mut deps = mock_dependencies();

        // 配置 DAO 治理
        let gov = GovernanceConfig {
            multisig_address: None,
            dao_address: Some(Addr::unchecked("msg1daoaddr0000000000000000000000000000000")),
            upgrade_threshold: 3,
            total_signers: 5,
            timelock_blocks: 1000,
            emergency_admin: None,
        };
        GOVERNANCE.save(deps.as_mut().storage, &gov).unwrap();

        // DAO 地址可以通过
        let dao = Addr::unchecked("msg1daoaddr0000000000000000000000000000000");
        assert!(validate_upgrade_authority(&deps.storage, &dao).is_ok());

        // 随机地址不能通过
        let random = Addr::unchecked("msg1random000000000000000000000000000000000");
        assert!(validate_upgrade_authority(&deps.storage, &random).is_err());
    }
}

附录

A. 完整项目结构

msg-upgradeable-contract/
├── Cargo.toml
├── src/
│   ├── lib.rs
│   ├── contract.rs          # 合约入口
│   ├── msg.rs               # 消息定义
│   ├── state.rs             # 状态定义
│   ├── migration.rs         # 迁移逻辑
│   ├── proxy.rs             # 代理模式
│   ├── factory.rs           # 工厂模式
│   ├── governance.rs        # 治理控制
│   ├── timelock.rs          # Timelock
│   └── testing.rs           # 测试
├── examples/
│   ├── schema.rs
│   └── multi_version.rs
└── tests/
    ├── integration.rs
    └── migration_tests.rs

B. Cargo.toml 依赖

[package]
name = "msg-upgradeable-contract"
version = "1.0.0"
edition = "2021"

[lib]
crate-type = ["cdylib", "rlib"]

[features]
library = []

[dependencies]
cosmwasm-std = "1.5"
cosmwasm-storage = "1.5"
cw-storage-plus = "1.2"
cw-utils = "1.0"
serde = { version = "1.0", features = ["derive"] }
serde-json-wasm = "0.5"
thiserror = "1.0"
schemars = "0.8"
semver = "1.0"          # 可选,用于版本比较

[dev-dependencies]
cosmwasm-schema = "1.5"

C. MSG Chain 部署命令

# 编译
docker run --rm -v "$(pwd)":/code \
  --mount type=volume,source="$(basename "$(pwd)")_cache",target=/code/target \
  --mount type=volume,source=registry_cache,target=/usr/local/cargo/registry \
  cosmwasm/rust-optimizer:0.12.13

# 存储合约
msgd tx wasm store ./artifacts/msg_upgradeable_contract.wasm \
  --from mywallet \
  --gas auto \
  --gas-adjustment 1.3 \
  --chain-id msg-chain-1 \
  -y

# 实例化
msgd tx wasm instantiate <CODE_ID> '{"owner":"msg1..."}' \
  --from mywallet \
  --label "upgradeable-v1" \
  --admin msg1admin... \
  --chain-id msg-chain-1 \
  -y

# 迁移
msgd tx wasm migrate <CONTRACT_ADDR> <NEW_CODE_ID> '{"version":"2.0.0"}' \
  --from mywallet \
  --chain-id msg-chain-1 \
  -y

# 查询
msgd query wasm contract <CONTRACT_ADDR>
msgd query wasm contract-state smart <CONTRACT_ADDR> '{"get_config":{}}'

D. 迁移清单

□ 部署前:
  □ 设置合约管理员(Admin)
  □ 记录初始 Code ID 和版本号
  □ 部署监控脚本

□ 升级前:
  □ 冻结新操作(暂停合约)
  □ 备份当前状态(快照)
  □ 测试新代码的迁移路径
  □ 审查迁移对用户的影响

□ 升级中:
  □ 分批次迁移用户数据
  □ 监控 Gas 消耗
  □ 记录每个迁移步骤

□ 升级后:
  □ 验证状态完整性
  □ 运行集成测试
  □ 解冻合约操作
  □ 通知用户
  □ 更新文档

文档版本: 1.0.0
适用链: MSG Chain (msg-chain-1)
地址前缀: msg