dApp Docs/AI Agent 跨链 IBC 互操作性进阶指南
Development reference. Not independently verified for production.

AI Agent 跨链 IBC 互操作性进阶指南 — MSG Chain

适用链:msg-chain-1 · Bech32 前缀:msg
⚠️ No-Go Disclaimer: MSGChain 主网裁决为 No-Go。本文件所有内容反映的是开发阶段的技术设计,不代表主网未独立核验上线状态。生产部署状态请以白皮书为准:https://msgchain.org/whitepaper/
前置阅读:AI Agent 跨链部署与 IBC 通信指南
本文假设读者已完成基础跨链身份注册与 A2A 通信的搭建


目录

  1. 引言:从基础 IBC 通信到互操作性进阶
  2. IBC 中间件架构
  3. ICS-721 跨链 NFT 转移
  4. Interchain Accounts (ICS-27)
  5. Interchain Queries (ICS-24/Stride 模式)
  6. 异步合约组合
  7. IBC 通道生命周期管理
  8. 跨链安全模式
  9. AI Agent 跨链工作流设计模式
  10. 实践:跨链 AI Agent 用例
  11. 总结与边界

1. 引言:从基础 IBC 通信到互操作性进阶

1.1 基础指南的边界

前文《跨链部署与 IBC 通信指南》覆盖了以下基础能力:

本进阶指南的目标是填补基础指南未深入的技术层:

主题 基础指南覆盖 进阶指南深入
ICS-20 基础转账 Fee Middleware、中间件组合
ICS-721 未涉及 NFT 跨链转移、Class 追踪
ICS-27 未涉及 Interchain Accounts 远程控制
ICQ 未涉及 跨链状态查询、Stride 模式
合约组合 基础编排 异步 ack/timeout、Task L2 回执
通道管理 通道开/关 通道升级、状态机、自动化
安全 基础签名 ICS-26 路由、MEV 保护、超时回退

1.2 AI Agent 多链协作场景进阶

基础指南中的 Agent 场景局限于"一条链上 Agent 与另一条链上 Agent 通信"。进阶场景要求 Agent 直接控制多条链上的合约,而非仅通过 IBC 消息交换:

┌─────────────────────────────────────────────────────────┐
│              AI Agent 跨链控制平面                         │
│  ┌─────────────┐    ┌─────────────┐    ┌─────────────┐  │
│  │  ICA 控制器    │    │  ICQ 查询器  │    │  NFT 管理器   │  │
│  │  (ICS-27)    │    │  (ICS-24)   │    │  (ICS-721)  │  │
│  └──────┬───────┘    └──────┬──────┘    └──────┬──────┘  │
│         │                   │                   │          │
│         └───────────────────┼───────────────────┘          │
│                             │                              │
│                      ┌──────┴──────┐                       │
│                      │  IBC 中间件  │                       │
│                      │  路由层      │                       │
│                      └─────────────┘                       │
└─────────────────────────────────────────────────────────┘
         │              │              │
    ┌────┴────┐   ┌────┴────┐   ┌────┴────┐
    │ Chain A  │   │ Chain B  │   │ Chain C  │
    │ (执行)    │   │ (查询)    │   │ (资产)    │
    └─────────┘   └─────────┘   └─────────┘

1.3 MSG Chain IBC 能力层

根据 MSG Chain 开发者能力矩阵,当前 production_supported = true 的表面包括:

能力表面 production_supported 影响 IBC 的相关能力
contract_runtime ✅ CosmWasm 合约原生支持 IBC 入口点
registry_resolution ✅ 创世注册中心支持合约地址解析
rpc_gateway ✅ RPC/REST 支持 IBC 交易查询与广播
chain_config_pack ✅ 链配置支持钱包注入

未生产化表面(以下功能在本文中标注为"开发中"或"规划中"):


2. IBC 中间件架构

2.1 ICS-20 中间件栈

Cosmos IBC 的核心设计是中间件栈。ICS-20 传送标准本身是一个中间件,其上可以叠加 Fee Middleware、Rate Limit Middleware 等。

AI Agent 在跨链支付时,实际经过的 IBC 栈为:

┌──────────────────────────────────────┐
│         应用层 (Agent 合约)            │
├──────────────────────────────────────┤
│  Fee Middleware (ICS-29)              │  ← 费用代付
├──────────────────────────────────────┤
│  Rate Limit Middleware                │  ← 速率限制
├──────────────────────────────────────┤
│  ICS-20 Transfer                      │  ← 代币转移
├──────────────────────────────────────┤
│  IBC Channel (ordered/unordered)      │
├──────────────────────────────────────┤
│  IBC Connection + Client              │
│  (07-tendermint)                      │
└──────────────────────────────────────┘

2.1.1 Fee Middleware (ICS-29)

Fee Middleware 允许 AI Agent 为跨链数据包支付中继费用,使非代币持有者也能跨链通信。

// ============================================================
// Fee Middleware 数据包结构 (Rust)
// ============================================================

/// ICS-29 Fee 数据包
#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct FeePacketData {
    /// 原始数据包
    pub packet: StdPacketData,
    /// 中继费用
    pub fee: Fee,
    /// 中继者奖励
    pub relayers: Vec<String>,
}

/// Fee 结构
#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct Fee {
    /// 发送端预付中继费
    pub recv_fee: Coin,
    /// 接收端确认回报费
    pub ack_fee: Coin,
    /// 超时回报费
    pub timeout_fee: Coin,
}

/// Agent 跨链操作的 Fee 预设
#[derive(Serialize, Deserialize, Clone, Debug)]
pub enum AgentFeeStrategy {
    /// 发送方全额承担
    SenderPays {
        recv_fee: Coin,
        ack_fee: Coin,
        timeout_fee: Coin,
    },
    /// 接收方支付(需目标链合约支持)
    ReceiverPays {
        max_fee: Coin,
    },
    /// 按比例分摊
    Split {
        sender_ratio: u8,  // 0-100
        max_total: Coin,
    },
}

2.1.2 Rate Limit Middleware

Rate Limit Middleware 防止跨链桥耗尽通道流动性的攻击场景。

#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct RateLimitConfig {
    pub channel_id: String,
    pub denom: String,
    pub max_egress: u128,
    pub max_ingress: u128,
    pub epoch_blocks: u64,
    pub current_egress: u128,
    pub current_ingress: u128,
    pub epoch_start: u64,
}

impl RateLimitConfig {
    pub fn check_egress(&self, amount: u128) -> Result<(), RateLimitError> {
        let remaining = self.max_egress.saturating_sub(self.current_egress);
        if amount > remaining {
            return Err(RateLimitError::EgressExceeded {
                attempted: amount,
                remaining,
                resets_at: self.epoch_start + self.epoch_blocks,
            });
        }
        Ok(())
    }
}

#[derive(Serialize, Deserialize, Clone, Debug)]
pub enum RateLimitError {
    EgressExceeded { attempted: u128, remaining: u128, resets_at: u64 },
    IngressExceeded { attempted: u128, remaining: u128, resets_at: u64 },
}

2.2 中间件组合模式

MSG Chain 上的 IBC 中间件组合遵循以下规则:

// IBC 模块初始化中的中间件栈
func NewIBCModule(app *MsgChainApp, transferIBC porttypes.IBCModule) porttypes.IBCModule {
    var transferStack porttypes.IBCModule = transferIBC

    // 中间件 1: Rate Limit
    transferStack = rateLimit.NewIBCMiddleware(transferStack, app.RateLimitKeeper)

    // 中间件 2: Fee
    transferStack = fee.NewIBCMiddleware(transferStack, app.FeeKeeper)

    return transferStack
}

CLI 层级指定 Fee 策略:

msgd tx ibc-transfer transfer \
  --packet-fee '{"recv_fee": "1000umsg", "ack_fee": "500umsg", "timeout_fee": "500umsg"}' \
  --src-port transfer \
  --src-channel channel-0 \
  --amount 1000000umsg \
  --receiver msg1agentaddress...

2.3 自定义中间件开发

AI Agent 可以为特定场景开发自定义 IBC 中间件:

/// 自定义中间件 — Agent 执行日志
pub trait AgentMiddleware {
    fn before_packet_send(
        &self,
        packet: &IbcPacket,
        agent_id: &str,
    ) -> Result<(), AgentMiddlewareError>;

    fn after_packet_recv(
        &self,
        packet: &IbcPacket,
        execution_result: &ExecutionResult,
    ) -> Result<(), AgentMiddlewareError>;

    fn on_acknowledgement(
        &self,
        ack: &IbcPacketAckMsg,
    ) -> Result<(), AgentMiddlewareError>;

    fn on_timeout(
        &self,
        timeout: &IbcPacketTimeoutMsg,
    ) -> Result<(), AgentMiddlewareError>;
}

#[derive(Serialize, Deserialize, Clone, Debug)]
pub enum AgentMiddlewareError {
    PacketRejected { reason: String },
    ExecutionFailed { details: String },
    ChannelCongested { channel_id: String },
}

2.4 MSG Chain 中间件实现状态

中间件 已实现 生产可用 适用场景
ICS-20 v2 ✅ ✅ 基础代币转移
ICS-29 Fee ✅ ✅ Agent 支付中继费用
Rate Limit ✅ ✅ 通道流动性保护
ICS-27 ICA 开发中 ❌ 跨链合约控制
ICS-721 NFT 转移 开发中 ❌ NFT 跨链转移
自定义中间件 通过 CosmWasm 实现 合约层可用 Agent 专用逻辑

3. ICS-721 跨链 NFT 转移

3.1 ICS-721 协议概述

ICS-721 定义了非同质化代币(NFT)在 IBC 通道间的转移标准。与 ICS-20 同质化代币不同,ICS-721 维护每个 NFT 的唯一性和类层级。

在 MSG Chain 上,AI Agent 通过 ICS-721 可以实现:

3.2 NFT Class 与 Token 的跨链映射

IBC NFT 的核心概念是 Class(类):

// ============================================================
// ICS-721 核心类型 (Rust - CosmWasm)
// ============================================================

/// NFT 类标识(链上全局)
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
pub struct NftClassId {
    pub source_chain: String,
    pub contract_addr: String,
    pub class_id: String,
}

/// ICS-721 NFT 数据包
#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct NftPacketData {
    pub sender: String,
    pub receiver: String,
    pub class_id: String,
    pub token_ids: Vec<String>,
    pub token_uris: Option<Vec<String>>,
    pub memo: Option<Binary>,
}

/// NFT 跨链类追踪
#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct IbcNftClass {
    pub local_class_id: String,
    pub source_chain: String,
    pub source_class_id: String,
    pub source_channel: String,
    pub is_native: bool,
    pub transfer_count: u64,
}

3.3 跨链类前缀解析

NFT 每经过一次 IBC 跳转,class_id 会增加前缀:

位置 class_id
原生链 (msg-chain-1) msg1nftclass...
第一次跨链后 (chain-a) ics721/msg-chain-1/msg1nftclass...
第二次跨链后 (chain-b) ics721/chain-a/ics721/msg-chain-1/msg1nftclass...
返回 msg-chain-1 msg1nftclass...(换回原生)
/// 解析跨链类前缀
pub fn resolve_ibc_class_id(
    class_id: &str,
    source_channel: &str,
) -> IbcNftClass {
    if class_id.starts_with("ics721/") {
        // 非原生类,解析前缀链
        let parts: Vec<&str> = class_id.splitn(3, '/').collect();
        IbcNftClass {
            local_class_id: class_id.to_string(),
            source_chain: parts[1].to_string(),
            source_class_id: parts[2].to_string(),
            source_channel: source_channel.to_string(),
            is_native: false,
            transfer_count: parts[1].matches("ics721").count() as u64 + 1,
        }
    } else {
        // 原生类
        IbcNftClass {
            local_class_id: class_id.to_string(),
            source_chain: "msg-chain-1".to_string(),
            source_class_id: class_id.to_string(),
            source_channel: "native".to_string(),
            is_native: true,
            transfer_count: 0,
        }
    }
}

3.4 NFT 跨链转移合约

// ============================================================
// NFT 跨链转移合约 (Rust - CosmWasm)
// ============================================================

use cosmwasm_std::{
    entry_point, to_binary, Binary, Deps, DepsMut, Env, MessageInfo, Response, StdResult,
    IbcMsg, IbcPacket, IbcReceiveResponse, IbcPacketAckMsg, IbcPacketTimeoutMsg,
    IbcChannelOpenMsg, IbcChannelConnectMsg, IbcChannelCloseMsg,
    IbcOrder, IbcTimeout, Storage, Addr, Coin,
};

#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct NftTransferState {
    pub token_id: String,
    pub original_class_id: String,
    pub current_owner: Addr,
    pub status: NftStatus,
    pub ibc_trace: Vec<IbcHop>,
    pub locked: bool,
    pub locked_on_chain: Option<String>,
}

#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
pub enum NftStatus {
    Native,
    IbcLocked,
    IbcTransferring,
}

#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct IbcHop {
    pub chain_id: String,
    pub channel_id: String,
    pub sequence: u64,
    pub timestamp: u64,
}

const NFT_CLASSES: &str = "nft_classes";
const NFT_TOKENS: &str = "nft_tokens";

#[derive(Serialize, Deserialize)]
pub enum ExecuteMsg {
    SendNftCrossChain {
        target_chain: String,
        class_id: String,
        token_id: String,
        receiver: String,
        memo: Option<Binary>,
    },
    RegisterNftClass {
        class_id: String,
        name: String,
        symbol: String,
    },
    MintNft {
        class_id: String,
        token_id: String,
        owner: String,
        token_uri: Option<String>,
    },
}

#[entry_point]
pub fn execute(
    deps: DepsMut,
    env: Env,
    info: MessageInfo,
    msg: ExecuteMsg,
) -> StdResult<Response> {
    match msg {
        ExecuteMsg::SendNftCrossChain { target_chain, class_id, token_id, receiver, memo } => {
            execute_send_nft(deps, env, info, target_chain, class_id, token_id, receiver, memo)
        }
        ExecuteMsg::RegisterNftClass { class_id, name, symbol } => {
            execute_register_class(deps, env, info, class_id, name, symbol)
        }
        ExecuteMsg::MintNft { class_id, token_id, owner, token_uri } => {
            execute_mint_nft(deps, env, info, class_id, token_id, owner, token_uri)
        }
    }
}

fn execute_send_nft(
    deps: DepsMut,
    env: Env,
    info: MessageInfo,
    target_chain: String,
    class_id: String,
    token_id: String,
    receiver: String,
    memo: Option<Binary>,
) -> StdResult<Response> {
    let store = deps.storage;
    let token_key = format!("{}{}:{}", NFT_TOKENS, class_id, token_id);
    let token_data = store.get(token_key.as_bytes())
        .ok_or_else(|| cosmwasm_std::StdError::generic_err("NFT not found"))?;
    let mut token: NftTransferState = bincode2::deserialize(&token_data)?;

    if token.current_owner != info.sender {
        return Err(cosmwasm_std::StdError::generic_err("Not the owner"));
    }
    if token.locked {
        return Err(cosmwasm_std::StdError::generic_err("NFT already locked for transfer"));
    }

    token.status = NftStatus::IbcLocked;
    token.locked = true;
    token.locked_on_chain = Some("msg-chain-1".to_string());
    token.ibc_trace.push(IbcHop {
        chain_id: target_chain.clone(),
        channel_id: get_nft_channel(&target_chain),
        sequence: env.block.height,
        timestamp: env.block.time.seconds(),
    });
    store.set(token_key.as_bytes(), &bincode2::serialize(&token)?);

    let packet = NftPacketData {
        sender: info.sender.to_string(),
        receiver,
        class_id: class_id.clone(),
        token_ids: vec![token_id.clone()],
        token_uris: None,
        memo,
    };

    let ibc_msg = IbcMsg::SendPacket {
        channel_id: get_nft_channel(&target_chain),
        data: Binary::from(bincode2::serialize(&packet)?),
        timeout: IbcTimeout::with_timestamp(env.block.time.plus_seconds(600)),
    };

    Ok(Response::new()
        .add_message(ibc_msg)
        .add_attribute("action", "send_nft_ibc")
        .add_attribute("class_id", &class_id)
        .add_attribute("token_id", &token_id))
}

fn execute_register_class(
    deps: DepsMut,
    _env: Env,
    info: MessageInfo,
    class_id: String,
    name: String,
    symbol: String,
) -> StdResult<Response> {
    let store = deps.storage;
    let key = format!("{}{}", NFT_CLASSES, class_id);
    if store.get(key.as_bytes()).is_some() {
        return Err(cosmwasm_std::StdError::generic_err("Class already registered"));
    }
    let nft_class = IbcNftClass {
        local_class_id: class_id.clone(),
        source_chain: "msg-chain-1".to_string(),
        source_class_id: class_id.clone(),
        source_channel: "native".to_string(),
        is_native: true,
        transfer_count: 0,
    };
    store.set(key.as_bytes(), &bincode2::serialize(&nft_class)?);
    Ok(Response::new()
        .add_attribute("action", "register_class")
        .add_attribute("class_id", &class_id)
        .add_attribute("registrar", info.sender))
}

fn execute_mint_nft(
    deps: DepsMut,
    _env: Env,
    info: MessageInfo,
    class_id: String,
    token_id: String,
    owner: String,
    token_uri: Option<String>,
) -> StdResult<Response> {
    let store = deps.storage;
    let class_key = format!("{}{}", NFT_CLASSES, class_id);
    if store.get(class_key.as_bytes()).is_none() {
        return Err(cosmwasm_std::StdError::generic_err("Class not registered"));
    }
    let token_key = format!("{}{}:{}", NFT_TOKENS, class_id, token_id);
    if store.get(token_key.as_bytes()).is_some() {
        return Err(cosmwasm_std::StdError::generic_err("Token already exists"));
    }
    let owner_addr = Addr::unchecked(owner);
    let token = NftTransferState {
        token_id: token_id.clone(),
        original_class_id: class_id.clone(),
        current_owner: owner_addr,
        status: NftStatus::Native,
        ibc_trace: vec![],
        locked: false,
        locked_on_chain: None,
    };
    store.set(token_key.as_bytes(), &bincode2::serialize(&token)?);
    Ok(Response::new()
        .add_attribute("action", "mint_nft")
        .add_attribute("class_id", &class_id)
        .add_attribute("token_id", &token_id)
        .add_attribute("minter", info.sender))
}

3.5 NFT 跨链回执与解锁

// ============================================================
// NFT IBC 回执处理 (Rust)
// ============================================================

#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct NftAcknowledgement {
    pub status: NftAckStatus,
    pub class_id: String,
    pub token_ids: Vec<String>,
    pub error_msg: Option<String>,
}

#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
pub enum NftAckStatus {
    Success,
    Failure,
    Timeout,
}

#[entry_point]
pub fn ibc_packet_ack(
    deps: DepsMut,
    _env: Env,
    msg: IbcPacketAckMsg,
) -> StdResult<Response> {
    let ack: NftAcknowledgement = bincode2::deserialize(&msg.acknowledgement.data)?;

    if ack.status == NftAckStatus::Success {
        for token_id in &ack.token_ids {
            let key = format!("{}{}:{}", NFT_TOKENS, ack.class_id, token_id);
            deps.storage.remove(key.as_bytes());
        }
    } else {
        for token_id in &ack.token_ids {
            let key = format!("{}{}:{}", NFT_TOKENS, ack.class_id, token_id);
            if let Some(data) = deps.storage.get(key.as_bytes()) {
                if let Ok(mut token) = bincode2::deserialize::<NftTransferState>(&data) {
                    token.status = NftStatus::Native;
                    token.locked = false;
                    token.locked_on_chain = None;
                    deps.storage.set(key.as_bytes(), &bincode2::serialize(&token)?);
                }
            }
        }
    }

    Ok(Response::new()
        .add_attribute("action", "nft_ibc_ack")
        .add_attribute("status", format!("{:?}", ack.status)))
}

#[entry_point]
pub fn ibc_packet_timeout(
    deps: DepsMut,
    _env: Env,
    msg: IbcPacketTimeoutMsg,
) -> StdResult<Response> {
    if let Ok(packet) = bincode2::deserialize::<NftPacketData>(&msg.packet.data) {
        for token_id in &packet.token_ids {
            let key = format!("{}{}:{}", NFT_TOKENS, packet.class_id, token_id);
            if let Some(data) = deps.storage.get(key.as_bytes()) {
                if let Ok(mut token) = bincode2::deserialize::<NftTransferState>(&data) {
                    token.status = NftStatus::Native;
                    token.locked = false;
                    token.locked_on_chain = None;
                    deps.storage.set(key.as_bytes(), &bincode2::serialize(&token)?);
                }
            }
        }
    }
    Ok(Response::new()
        .add_attribute("action", "nft_ibc_timeout")
        .add_attribute("class_id", "recovered"))
}

3.6 与 MSG Chain Registry 结合

NFT 类注册可以与 MSG Chain 的 genesis_registry_v1 结合,实现 canonical NFT class 解析:

/// 通过 Registry 解析 NFT 类地址
pub fn resolve_nft_class_via_registry(
    deps: &Deps,
    canonical_name: &str,
) -> StdResult<String> {
    let registry_addr = deps.querier.query_wasm_smart(
        get_registry_address(),
        &RegistryQuery::ResolveKey {
            key: format!("nft:class:{}", canonical_name),
        },
    )?;
    // registry_resolution 在生产中 production_supported = true
    Ok(registry_addr)
}

3.7 MSG Chain ICS-721 边界

特性 状态 说明
ICS-721 基础转移 开发中 核心数据包处理已设计
Class 追踪 开发中 跨链类前缀解析
NFT 锁定/解锁 开发中 原子化锁定保证资产安全
Registry 集成 设计阶段 canonical key 解析
批量转移 规划中 单包多 Token
与 CW-721 兼容 设计阶段 CosmWasm 标准接口封装

4. Interchain Accounts (ICS-27)

4.1 ICA 架构

Interchain Accounts (ICS-27) 允许 AI Agent 通过 IBC 在另一条链上控制一个账户,无需在目标链上部署合约。

┌───────────────┐         IBC 数据包           ┌───────────────┐
│  控制器链       │  ────────────────────────→  │   主机链       │
│  msg-chain-1  │                            │  chain-a      │
│               │  RegisterInterchainAccount   │               │
│  AI Agent     │  ────────────────────────→  │  ICA 账户     │
│  (msg1...)    │                            │  (msg1ica...)  │
│               │  Execute(cosmos.msgs...)     │               │
│  控制合约      │  ────────────────────────→  │  执行合约调用   │
└───────────────┘                            └───────────────┘

4.2 ICA 核心数据结构

// ============================================================
// ICA 核心类型 (Rust - CosmWasm)
// ============================================================

/// ICA 注册数据包
#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct IcaRegisterPacket {
    pub version: String,
    pub ordering: String,
    pub metadata: Option<IcaMetadata>,
}

#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct IcaMetadata {
    pub agent_id: String,
    pub purpose: String,
    pub timeout_seconds: u64,
}

/// ICA 执行数据包
#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct IcaExecutePacket {
    pub msgs: Vec<IcaCosmosMsg>,
    pub salt: Option<Binary>,
}

/// ICA 支持的 Cosmos 消息
#[derive(Serialize, Deserialize, Clone, Debug)]
pub enum IcaCosmosMsg {
    BankSend {
        to_address: String,
        amount: Vec<Coin>,
    },
    WasmExecute {
        contract_address: String,
        msg: Binary,
        funds: Vec<Coin>,
    },
    WasmInstantiate {
        code_id: u64,
        msg: Binary,
        label: String,
        funds: Vec<Coin>,
        admin: Option<String>,
    },
    StakeDelegate {
        validator_address: String,
        amount: Coin,
    },
    StakeUndelegate {
        validator_address: String,
        amount: Coin,
    },
}

/// ICA 账户状态
#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct IcaAccountState {
    pub agent_id: String,
    pub host_chain: String,
    pub host_channel: String,
    pub host_address: String,
    pub controller_address: String,
    pub registered_at: u64,
    pub last_sequence: u64,
    pub active: bool,
}

const ICA_ACCOUNTS: &str = "ica_accounts";

4.3 ICA 控制器合约

// ============================================================
// ICA 控制器合约 (Rust - CosmWasm)
// ============================================================

#[derive(Serialize, Deserialize)]
pub enum ExecuteMsg {
    RegisterIcaAccount {
        host_chain: String,
        metadata: Option<IcaMetadata>,
    },
    ExecuteOnHost {
        host_chain: String,
        msgs: Vec<IcaCosmosMsg>,
        timeout_seconds: u64,
    },
    CloseIcaAccount {
        host_chain: String,
    },
}

#[entry_point]
pub fn execute(
    deps: DepsMut,
    env: Env,
    info: MessageInfo,
    msg: ExecuteMsg,
) -> StdResult<Response> {
    match msg {
        ExecuteMsg::RegisterIcaAccount { host_chain, metadata } => {
            execute_register_ica(deps, env, info, host_chain, metadata)
        }
        ExecuteMsg::ExecuteOnHost { host_chain, msgs, timeout_seconds } => {
            execute_on_host(deps, env, info, host_chain, msgs, timeout_seconds)
        }
        ExecuteMsg::CloseIcaAccount { host_chain } => {
            execute_close_ica(deps, env, info, host_chain)
        }
    }
}

fn execute_register_ica(
    deps: DepsMut,
    env: Env,
    info: MessageInfo,
    host_chain: String,
    metadata: Option<IcaMetadata>,
) -> StdResult<Response> {
    let store = deps.storage;
    let ica_key = format!("ica:{}:{}", info.sender, host_chain);
    if store.get(ica_key.as_bytes()).is_some() {
        return Err(cosmwasm_std::StdError::generic_err(
            "ICA already registered for this agent on this chain",
        ));
    }

    let channel_id = get_ica_channel(&host_chain)?;

    let register_packet = IcaRegisterPacket {
        version: "ics-27-v1".to_string(),
        ordering: "ORDERED".to_string(),
        metadata: metadata.map(|m| IcaMetadata {
            agent_id: m.agent_id,
            purpose: m.purpose,
            timeout_seconds: m.timeout_seconds,
        }),
    };

    let ibc_msg = IbcMsg::SendPacket {
        channel_id: channel_id.clone(),
        data: Binary::from(bincode2::serialize(&register_packet)?),
        timeout: IbcTimeout::with_timestamp(env.block.time.plus_seconds(300)),
    };

    Ok(Response::new()
        .add_message(ibc_msg)
        .add_attribute("action", "register_ica")
        .add_attribute("agent", info.sender)
        .add_attribute("host_chain", &host_chain))
}

fn execute_on_host(
    deps: DepsMut,
    env: Env,
    info: MessageInfo,
    host_chain: String,
    msgs: Vec<IcaCosmosMsg>,
    timeout_seconds: u64,
) -> StdResult<Response> {
    let store = deps.storage;
    let ica_key = format!("ica:{}:{}", info.sender, host_chain);
    let ica_data = store.get(ica_key.as_bytes())
        .ok_or_else(|| cosmwasm_std::StdError::generic_err("ICA not registered"))?;
    let ica: IcaAccountState = bincode2::deserialize(&ica_data)?;

    if !ica.active {
        return Err(cosmwasm_std::StdError::generic_err("ICA is closed"));
    }

    let execute_packet = IcaExecutePacket {
        msgs,
        salt: None,
    };

    let ibc_msg = IbcMsg::SendPacket {
        channel_id: ica.host_channel.clone(),
        data: Binary::from(bincode2::serialize(&execute_packet)?),
        timeout: IbcTimeout::with_timestamp(env.block.time.plus_seconds(timeout_seconds)),
    };

    Ok(Response::new()
        .add_message(ibc_msg)
        .add_attribute("action", "ica_execute")
        .add_attribute("agent", info.sender)
        .add_attribute("host_chain", &host_chain))
}

fn execute_close_ica(
    deps: DepsMut,
    _env: Env,
    info: MessageInfo,
    host_chain: String,
) -> StdResult<Response> {
    let store = deps.storage;
    let ica_key = format!("ica:{}:{}", info.sender, host_chain);
    let data = store.get(ica_key.as_bytes())
        .ok_or_else(|| cosmwasm_std::StdError::generic_err("ICA not found"))?;
    let mut ica: IcaAccountState = bincode2::deserialize(&data)?;
    ica.active = false;
    store.set(ica_key.as_bytes(), &bincode2::serialize(&ica)?);
    Ok(Response::new()
        .add_attribute("action", "close_ica")
        .add_attribute("agent", info.sender)
        .add_attribute("host_chain", &host_chain))
}

4.4 ICA 状态机

     ┌──────────┐
     │  INIT    │  ← 控制合约调用 RegisterIcaAccount
     └────┬─────┘
          │ IBC 数据包
          ▼
     ┌──────────┐
     │  TRY_OPEN│  ← 主机链收到注册请求
     └────┬─────┘
          │ 主机链创建 ICA 地址
          ▼
     ┌──────────┐
     │  OPEN    │  ← ICA 可用,记录 host_address
     └────┬─────┘
          │
     ┌────┴────┐
     │ Active  │  ← 可以发送 ExecuteOnHost
     └────┬────┘
          │
     ┌────┴────┐
     │ Closed  │  ← 关闭 ICA 通道
     └─────────┘

4.5 ICA Ack 处理

// ============================================================
// ICA 回执处理 (Rust)
// ============================================================

#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct IcaExecuteResult {
    pub success: bool,
    pub data: Option<Binary>,
    pub error: Option<String>,
    pub agent_id: String,
    pub sequence: u64,
}

#[entry_point]
pub fn ibc_packet_ack(
    deps: DepsMut,
    _env: Env,
    msg: IbcPacketAckMsg,
) -> StdResult<Response> {
    let result: IcaExecuteResult = bincode2::deserialize(&msg.acknowledgement.data)?;
    if result.success {
        Ok(Response::new()
            .add_attribute("action", "ica_execute_success")
            .add_attribute("agent_id", &result.agent_id)
            .add_attribute("sequence", result.sequence.to_string()))
    } else {
        Ok(Response::new()
            .add_attribute("action", "ica_execute_failed")
            .add_attribute("agent_id", &result.agent_id)
            .add_attribute("error", result.error.unwrap_or_default()))
    }
}

4.6 AI Agent ICA 使用模式

// ============================================================
// AI Agent ICA 控制器 (TypeScript)
// ============================================================

type IcaFallbackStrategy =
    | { type: 'retry'; maxRetries: number; delayMs: number }
    | { type: 'revert'; compensatingMsgs: IcaCosmosMsg[] }
    | { type: 'notify'; notifyAgentId: string };

class AiAgentIcaController {
    constructor(
        private icaContractAddress: string,
        private agentAddress: string,
    ) {}

    async executeMultiStep(
        hostChain: string,
        steps: IcaOperation[],
    ): Promise<boolean> {
        for (const step of steps) {
            const success = await this.executeStep(hostChain, step);
            if (!success) {
                await this.handleFailure(hostChain, step);
                return false;
            }
        }
        return true;
    }

    private async executeStep(
        hostChain: string,
        op: IcaOperation,
    ): Promise<boolean> {
        for (let attempt = 0; attempt < (op.fallback.type === 'retry'
            ? op.fallback.maxRetries + 1 : 1); attempt++) {
            try {
                const result = await this.sendIcaExecute(hostChain, op.msgs, op.timeoutSeconds);
                if (result.success) return true;
            } catch (e) {
                console.warn(`ICA attempt ${attempt + 1} failed:`, e);
            }
            if (op.fallback.type === 'retry') {
                await this.delay(op.fallback.delayMs);
            }
        }
        return false;
    }

    private async handleFailure(hostChain: string, op: IcaOperation): Promise<void> {
        switch (op.fallback.type) {
            case 'revert':
                await this.sendIcaExecute(hostChain, op.fallback.compensatingMsgs, 60);
                break;
            case 'notify':
                await this.notifyAgent(op.fallback.notifyAgentId, hostChain, 'ICA_FAILED');
                break;
            case 'retry':
                console.error(`ICA failed after all retries on ${hostChain}`);
                break;
        }
    }

    private async sendIcaExecute(hostChain: string, msgs: IcaCosmosMsg[], timeout: number) {
        return { success: true };
    }

    private delay(ms: number): Promise<void> {
        return new Promise(resolve => setTimeout(resolve, ms));
    }

    private async notifyAgent(agentId: string, chain: string, event: string): Promise<void> {
        // 通过 A2A 消息通知其他 Agent
    }
}

interface IcaOperation {
    hostChain: string;
    msgs: IcaCosmosMsg[];
    timeoutSeconds: number;
    fallback: IcaFallbackStrategy;
}

5. Interchain Queries (ICS-24/Stride 模式)

5.1 ICQ 概述

Interchain Queries (ICQ) 允许 AI Agent 在不提交完整交易的情况下跨链读取状态。Stride 链的 ICQ 实现是最成熟的参考。

ICQ 与 IBC 数据包查询的区别:

特性 IBC 数据包查询 ICQ (Interchain Query)
状态流向 查询请求 + 响应通过 IBC 数据包 查询请求通过 IBC,响应自动返回
订阅能力 一次查询一次响应 持续查询、定时轮询
证明验证 可选 需要轻客户端证明验证
延迟 高(通过 Relayer) 低(Relayer 自动服务)
适用场景 一次性的跨链发现 持续的跨链监控、预言机

5.2 ICQ 数据流

┌──────────────┐        1. 注册查询          ┌──────────────┐
│  查询者链      │  ────────────────────────→  │  目标链        │
│  msg-chain-1 │                            │  chain-a      │
│              │        2. 查询请求            │              │
│  ICQ 合约     │  ────────────────────────→  │  状态 K/V     │
│              │                            │              │
│              │    3. Relayer 自动提交结果     │              │
│              │  ←────────────────────────  │              │
│              │                            │              │
│  存储结果     │                            │              │
│  触发 Agent   │                            │              │
└──────────────┘                            └──────────────┘

5.3 ICQ 查询类型

// ============================================================
// Interchain Query 类型定义 (Rust)
// ============================================================

/// 跨链查询请求
#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct InterchainQuery {
    pub query_id: String,
    pub source_chain: String,
    pub target_chain: String,
    pub query_type: QueryType,
    pub agent_id: String,
    pub callback_address: String,
    pub frequency: Option<u64>,
    pub created_at: u64,
    pub active: bool,
}

/// 跨链查询类型
#[derive(Serialize, Deserialize, Clone, Debug)]
pub enum QueryType {
    KeyValue {
        key: String,
        prove: bool,
    },
    SmartContractState {
        contract_address: String,
        query_msg: Binary,
    },
    PrefixKeys {
        prefix: String,
        max_results: u32,
    },
    IbcConnection {
        connection_id: String,
    },
    Balance {
        address: String,
        denom: String,
    },
}

/// 跨链查询结果
#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct InterchainQueryResult {
    pub query_id: String,
    pub height: u64,
    pub result: QueryResultData,
    pub proof: Option<Binary>,
    pub timestamp: u64,
}

#[derive(Serialize, Deserialize, Clone, Debug)]
pub enum QueryResultData {
    KeyValue { key: String, value: Option<Binary> },
    SmartContract(Vec<QueryResultItem>),
    PrefixKeys { keys: Vec<String>, values: Vec<Binary> },
    Balance { denom: String, amount: u128 },
}

5.4 ICQ 管理器合约

// ============================================================
// ICQ 管理器合约 (Rust - CosmWasm)
// ============================================================

use cosmwasm_std::{
    entry_point, to_binary, Binary, Deps, DepsMut, Env, MessageInfo, Response,
    StdResult, StdError, IbcMsg, Storage,
};

const ICQ_REGISTRY: &str = "icq_registry";
const ICQ_RESULTS: &str = "icq_results";

#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct RegisteredQuery {
    pub query_id: String,
    pub target_chain: String,
    pub query_type: QueryType,
    pub agent_id: String,
    pub callback: String,
    pub frequency: u64,
    pub last_queried: u64,
    pub active: bool,
}

#[derive(Serialize, Deserialize)]
pub enum ExecuteMsg {
    RegisterQuery {
        target_chain: String,
        query_type: QueryType,
        frequency: Option<u64>,
    },
    UnregisterQuery {
        query_id: String,
    },
    ExecuteQuery {
        query_id: String,
    },
}

#[entry_point]
pub fn execute(
    deps: DepsMut,
    env: Env,
    info: MessageInfo,
    msg: ExecuteMsg,
) -> StdResult<Response> {
    match msg {
        ExecuteMsg::RegisterQuery { target_chain, query_type, frequency } => {
            execute_register_query(deps, env, info, target_chain, query_type, frequency)
        }
        ExecuteMsg::UnregisterQuery { query_id } => {
            execute_unregister_query(deps, env, info, query_id)
        }
        ExecuteMsg::ExecuteQuery { query_id } => {
            execute_query_now(deps, env, info, query_id)
        }
    }
}

fn execute_register_query(
    deps: DepsMut,
    env: Env,
    info: MessageInfo,
    target_chain: String,
    query_type: QueryType,
    frequency: Option<u64>,
) -> StdResult<Response> {
    let store = deps.storage;
    let query_id = format!("icq:{:x}", sha256(
        format!("{}{}{}", info.sender, target_chain, env.block.height)
    ));

    let registered = RegisteredQuery {
        query_id: query_id.clone(),
        target_chain: target_chain.clone(),
        query_type,
        agent_id: info.sender.to_string(),
        callback: info.sender.to_string(),
        frequency: frequency.unwrap_or(0),
        last_queried: 0,
        active: true,
    };

    let key = format!("{}{}", ICQ_REGISTRY, query_id);
    store.set(key.as_bytes(), &bincode2::serialize(&registered)?);

    let ibc_msg = create_icq_packet(&env, &target_chain, &query_id, &registered.query_type);

    Ok(Response::new()
        .add_message(ibc_msg)
        .add_attribute("action", "register_icq")
        .add_attribute("query_id", &query_id)
        .add_attribute("target_chain", &target_chain))
}

fn create_icq_packet(
    env: &Env,
    target_chain: &str,
    query_id: &str,
    query_type: &QueryType,
) -> IbcMsg {
    let packet = InterchainQuery {
        query_id: query_id.to_string(),
        source_chain: "msg-chain-1".to_string(),
        target_chain: target_chain.to_string(),
        query_type: query_type.clone(),
        agent_id: String::new(),
        callback_address: String::new(),
        frequency: None,
        created_at: env.block.time.seconds(),
        active: true,
    };

    IbcMsg::SendPacket {
        channel_id: get_icq_channel(target_chain),
        data: Binary::from(bincode2::serialize(&packet).unwrap()),
        timeout: cosmwasm_std::IbcTimeout::with_timestamp(
            env.block.time.plus_seconds(300)
        ),
    }
}

fn execute_unregister_query(
    deps: DepsMut,
    _env: Env,
    info: MessageInfo,
    query_id: String,
) -> StdResult<Response> {
    let store = deps.storage;
    let key = format!("{}{}", ICQ_REGISTRY, query_id);
    if let Some(data) = store.get(key.as_bytes()) {
        let query: RegisteredQuery = bincode2::deserialize(&data)?;
        if query.agent_id != info.sender.to_string() {
            return Err(StdError::generic_err("Not the query owner"));
        }
        store.remove(key.as_bytes());
    }
    Ok(Response::new()
        .add_attribute("action", "unregister_icq")
        .add_attribute("query_id", &query_id))
}

fn execute_query_now(
    deps: DepsMut,
    env: Env,
    info: MessageInfo,
    query_id: String,
) -> StdResult<Response> {
    let store = deps.storage;
    let key = format!("{}{}", ICQ_REGISTRY, query_id);
    let data = store.get(key.as_bytes())
        .ok_or_else(|| StdError::generic_err("Query not found"))?;
    let query: RegisteredQuery = bincode2::deserialize(&data)?;
    if query.agent_id != info.sender.to_string() {
        return Err(StdError::generic_err("Not the query owner"));
    }
    let ibc_msg = create_icq_packet(&env, &query.target_chain, &query_id, &query.query_type);
    Ok(Response::new()
        .add_message(ibc_msg)
        .add_attribute("action", "execute_icq_now")
        .add_attribute("query_id", &query_id))
}

#[entry_point]
pub fn ibc_packet_receive(
    deps: DepsMut,
    _env: Env,
    msg: IbcPacket,
) -> StdResult<IbcReceiveResponse> {
    let result: InterchainQueryResult = bincode2::deserialize(&msg.data)?;

    if let Some(ref proof) = result.proof {
        let valid = verify_icq_proof(&result, proof);
        if !valid {
            return Ok(IbcReceiveResponse::new()
                .set_ack(Binary::from(b"invalid_proof"))
                .add_attribute("action", "icq_invalid_proof")
                .add_attribute("query_id", &result.query_id));
        }
    }

    let result_key = format!("{}:{}:{}", ICQ_RESULTS, result.query_id, result.height);
    deps.storage.set(result_key.as_bytes(), &bincode2::serialize(&result)?);

    let query_key = format!("{}{}", ICQ_REGISTRY, result.query_id);
    if let Some(data) = deps.storage.get(query_key.as_bytes()) {
        if let Ok(mut q) = bincode2::deserialize::<RegisteredQuery>(&data) {
            q.last_queried = result.timestamp;
            deps.storage.set(query_key.as_bytes(), &bincode2::serialize(&q)?);
        }
    }

    Ok(IbcReceiveResponse::new()
        .set_ack(Binary::from(b"accepted"))
        .add_attribute("action", "icq_result")
        .add_attribute("query_id", &result.query_id)
        .add_attribute("height", result.height.to_string()))
}

fn verify_icq_proof(_result: &InterchainQueryResult, _proof: &Binary) -> bool {
    // 实际需要 Merkle Proof 验证 + 轻客户端状态验证
    // 当前为简化实现
    true
}

#[derive(Serialize, Deserialize)]
pub enum QueryMsg {
    GetLatestResult { query_id: String },
    GetResultAtHeight { query_id: String, height: u64 },
    ListQueries { agent_id: Option<String> },
    GetQuery { query_id: String },
}

#[entry_point]
pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> StdResult<Binary> {
    match msg {
        QueryMsg::GetLatestResult { query_id } => {
            let prefix = format!("{}:{}:", ICQ_RESULTS, query_id);
            let mut latest: Option<InterchainQueryResult> = None;
            for (_, value) in deps.storage.range(
                Some(prefix.as_bytes().to_vec()), None, cosmwasm_std::Order::Descending,
            ) {
                if let Ok(r) = bincode2::deserialize::<InterchainQueryResult>(&value) {
                    latest = Some(r);
                    break;
                }
            }
            to_binary(&latest)
        }
        QueryMsg::GetResultAtHeight { query_id, height } => {
            let key = format!("{}:{}:{}", ICQ_RESULTS, query_id, height);
            let data = deps.storage.get(key.as_bytes())
                .and_then(|d| bincode2::deserialize::<InterchainQueryResult>(&d).ok());
            to_binary(&data)
        }
        QueryMsg::ListQueries { agent_id } => {
            let mut queries = Vec::new();
            for (_, value) in deps.storage.range(
                Some(ICQ_REGISTRY.as_bytes().to_vec()), None, cosmwasm_std::Order::Ascending,
            ) {
                if let Ok(q) = bincode2::deserialize::<RegisteredQuery>(&value) {
                    if let Some(ref aid) = agent_id {
                        if &q.agent_id == aid {
                            queries.push(q);
                        }
                    } else {
                        queries.push(q);
                    }
                }
            }
            to_binary(&queries)
        }
        QueryMsg::GetQuery { query_id } => {
            let key = format!("{}{}", ICQ_REGISTRY, query_id);
            let data = deps.storage.get(key.as_bytes())
                .and_then(|d| bincode2::deserialize::<RegisteredQuery>(&d).ok());
            to_binary(&data)
        }
    }
}

5.5 AI Agent ICQ 价格监控模式

// ============================================================
// AI Agent ICQ 价格监控 (TypeScript)
// ============================================================

interface PriceFeedConfig {
    targetChain: string;
    dexContract: string;
    tokenPair: string;
    intervalBlocks: number;
    deviationThreshold: number;
}

class AiAgentPriceFeed {
    private icqContract: string;

    constructor(private agentAddress: string) {
        this.icqContract = '';
    }

    async startPriceFeed(config: PriceFeedConfig): Promise<string> {
        const queryId = await this.registerICQ({
            targetChain: config.targetChain,
            queryType: {
                type: 'SmartContractState',
                contractAddress: config.dexContract,
                queryMsg: { pool: { pair: config.tokenPair } },
            },
            frequency: config.intervalBlocks,
        });

        this.pollPriceChanges(queryId, config);
        return queryId;
    }

    private async pollPriceChanges(queryId: string, config: PriceFeedConfig): Promise<void> {
        let lastPrice: number | null = null;

        setInterval(async () => {
            try {
                const result = await this.getLatestResult(queryId);
                const currentPrice = this.extractPrice(result);

                if (lastPrice !== null && currentPrice !== null) {
                    const deviation = Math.abs((currentPrice - lastPrice) / lastPrice) * 100;
                    if (deviation > config.deviationThreshold) {
                        await this.onPriceDeviation(config, lastPrice, currentPrice);
                    }
                }
                lastPrice = currentPrice;
            } catch (e) {
                console.error(`Price feed error for ${queryId}:`, e);
            }
        }, config.intervalBlocks * 6000);
    }

    private async onPriceDeviation(config: PriceFeedConfig, oldPrice: number, newPrice: number) {
        console.log(`Deviation on ${config.targetChain}: ${oldPrice} -> ${newPrice}`);
    }

    private async registerICQ(params: any): Promise<string> {
        return 'icq:' + Date.now().toString(16);
    }

    private async getLatestResult(queryId: string): Promise<any> {
        return null;
    }

    private extractPrice(result: any): number | null {
        if (!result || !result.result) return null;
        return null;
    }
}

6. 异步合约组合

6.1 跨链调用的异步本质

IBC 数据包的本质是异步的。AI Agent 发送 IBC 消息后,不能立即获得结果,通过 ack/timeout 回执感知执行结果:

时间线:
Agent 发送 IBC 数据包  ──────────→  ❓(等待中)
                              ↓
                    Relayer 转发到目标链
                              ↓
                   目标链执行 ibc_packet_receive
                              ↓
                   生成 Acknowledgement
                              ↓
                   Relayer 回传 ack 到源链
                              ↓
Agent 收到 ack/timeout  ←──── ibc_packet_ack/timeout

6.2 异步调用状态机与 Ack/Timeout 处理器

// ============================================================
// 异步跨链调用处理器 (Rust - CosmWasm)
// ============================================================

use cosmwasm_std::{
    entry_point, to_binary, Binary, Deps, DepsMut, Env, MessageInfo, Response,
    StdResult, StdError, IbcMsg, IbcPacket, IbcReceiveResponse,
    IbcPacketAckMsg, IbcPacketTimeoutMsg,
};

/// 异步调用状态
#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct AsyncCrossChainCall {
    pub call_id: String,
    pub agent_id: String,
    pub target_chain: String,
    pub target_contract: String,
    pub execute_msg: Binary,
    pub status: AsyncCallStatus,
    pub created_at: u64,
    pub completed_at: Option<u64>,
    pub result: Option<Binary>,
    pub retry_count: u8,
    pub max_retries: u8,
    pub compensating_call: Option<CompensatingCall>,
}

#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
pub enum AsyncCallStatus {
    Pending,
    AckReceived,
    Timeout,
    Failed,
    Compensated,
    Completed,
}

#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct CompensatingCall {
    pub target_chain: String,
    pub target_contract: String,
    pub execute_msg: Binary,
}

const ASYNC_CALLS: &str = "async_calls";

#[derive(Serialize, Deserialize)]
pub enum ExecuteMsg {
    InitiateCrossChainCall {
        target_chain: String,
        target_contract: String,
        execute_msg: Binary,
        max_retries: u8,
        compensating_call: Option<CompensatingCall>,
        timeout_seconds: u64,
    },
    RetryCall { call_id: String },
    CompensateCall { call_id: String },
}

#[entry_point]
pub fn execute(
    deps: DepsMut,
    env: Env,
    info: MessageInfo,
    msg: ExecuteMsg,
) -> StdResult<Response> {
    match msg {
        ExecuteMsg::InitiateCrossChainCall {
            target_chain, target_contract, execute_msg,
            max_retries, compensating_call, timeout_seconds,
        } => {
            execute_initiate_call(deps, env, info, target_chain, target_contract,
                execute_msg, max_retries, compensating_call, timeout_seconds)
        }
        ExecuteMsg::RetryCall { call_id } => {
            execute_retry_call(deps, env, info, call_id)
        }
        ExecuteMsg::CompensateCall { call_id } => {
            execute_compensate(deps, env, info, call_id)
        }
    }
}

fn execute_initiate_call(
    deps: DepsMut,
    env: Env,
    info: MessageInfo,
    target_chain: String,
    target_contract: String,
    execute_msg: Binary,
    max_retries: u8,
    compensating_call: Option<CompensatingCall>,
    timeout_seconds: u64,
) -> StdResult<Response> {
    let store = deps.storage;
    let call_id = format!("async:{:x}", sha256(
        format!("{}{}{}", info.sender, target_chain, env.block.time.nanos())
    ));

    let call = AsyncCrossChainCall {
        call_id: call_id.clone(),
        agent_id: info.sender.to_string(),
        target_chain: target_chain.clone(),
        target_contract: target_contract.clone(),
        execute_msg: execute_msg.clone(),
        status: AsyncCallStatus::Pending,
        created_at: env.block.time.seconds(),
        completed_at: None,
        result: None,
        retry_count: 0,
        max_retries,
        compensating_call,
    };

    let key = format!("{}{}", ASYNC_CALLS, call_id);
    store.set(key.as_bytes(), &bincode2::serialize(&call)?);

    let ibc_packet = IbcMsg::SendPacket {
        channel_id: get_call_channel(&target_chain),
        data: execute_msg,
        timeout: cosmwasm_std::IbcTimeout::with_timestamp(
            env.block.time.plus_seconds(timeout_seconds)
        ),
    };

    Ok(Response::new()
        .add_message(ibc_packet)
        .add_attribute("action", "initiate_cross_chain_call")
        .add_attribute("call_id", &call_id)
        .add_attribute("target_chain", &target_chain))
}

fn execute_retry_call(
    deps: DepsMut,
    env: Env,
    info: MessageInfo,
    call_id: String,
) -> StdResult<Response> {
    let store = deps.storage;
    let key = format!("{}{}", ASYNC_CALLS, call_id);
    let data = store.get(key.as_bytes())
        .ok_or_else(|| StdError::generic_err("Call not found"))?;
    let mut call: AsyncCrossChainCall = bincode2::deserialize(&data)?;

    if call.agent_id != info.sender.to_string() {
        return Err(StdError::generic_err("Not the call owner"));
    }
    if call.retry_count >= call.max_retries {
        return Err(StdError::generic_err("Max retries exceeded"));
    }

    call.retry_count += 1;
    call.status = AsyncCallStatus::Pending;
    store.set(key.as_bytes(), &bincode2::serialize(&call)?);

    let ibc_packet = IbcMsg::SendPacket {
        channel_id: get_call_channel(&call.target_chain),
        data: call.execute_msg,
        timeout: cosmwasm_std::IbcTimeout::with_timestamp(
            env.block.time.plus_seconds(300)
        ),
    };

    Ok(Response::new()
        .add_message(ibc_packet)
        .add_attribute("action", "retry_call")
        .add_attribute("call_id", &call_id)
        .add_attribute("retry", call.retry_count.to_string()))
}

fn execute_compensate(
    deps: DepsMut,
    env: Env,
    info: MessageInfo,
    call_id: String,
) -> StdResult<Response> {
    let store = deps.storage;
    let key = format!("{}{}", ASYNC_CALLS, call_id);
    let data = store.get(key.as_bytes())
        .ok_or_else(|| StdError::generic_err("Call not found"))?;
    let mut call: AsyncCrossChainCall = bincode2::deserialize(&data)?;

    if call.agent_id != info.sender.to_string() {
        return Err(StdError::generic_err("Not the call owner"));
    }

    let compensating = call.compensating_call.take()
        .ok_or_else(|| StdError::generic_err("No compensating call defined"))?;

    call.status = AsyncCallStatus::Compensated;
    store.set(key.as_bytes(), &bincode2::serialize(&call)?);

    let ibc_packet = IbcMsg::SendPacket {
        channel_id: get_call_channel(&compensating.target_chain),
        data: compensating.execute_msg,
        timeout: cosmwasm_std::IbcTimeout::with_timestamp(
            env.block.time.plus_seconds(300)
        ),
    };

    Ok(Response::new()
        .add_message(ibc_packet)
        .add_attribute("action", "compensate_call")
        .add_attribute("call_id", &call_id))
}

#[entry_point]
pub fn ibc_packet_ack(
    deps: DepsMut,
    _env: Env,
    msg: IbcPacketAckMsg,
) -> StdResult<Response> {
    // 通过 channel/sequence 查找 call_id,标记完成
    Ok(Response::new()
        .add_attribute("action", "cross_chain_ack"))
}

#[entry_point]
pub fn ibc_packet_timeout(
    deps: DepsMut,
    _env: Env,
    _msg: IbcPacketTimeoutMsg,
) -> StdResult<Response> {
    // 标记 Timeout,触发重试或补偿
    Ok(Response::new()
        .add_attribute("action", "cross_chain_timeout"))
}

6.3 与 MSG Chain Task L2 回执闭环的结合

MSG Chain 的 Task L2 回执机制为异步操作提供最终闭环:

// ============================================================
// Task L2 回执与 IBC 回调结合 (Rust)
// ============================================================

#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct TaskReceipt {
    pub task_id: String,
    pub call_id: String,
    pub status: TaskStatus,
    pub result: Option<Binary>,
    pub gas_used: u64,
    pub block_height: u64,
}

#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
pub enum TaskStatus {
    Submitted,
    Executing,
    Completed,
    Failed,
    TimedOut,
}

/// 将 Task L2 回执链接到异步调用
pub fn link_task_receipt_to_call(
    store: &mut dyn Storage,
    task_receipt: &TaskReceipt,
) -> StdResult<()> {
    let key = format!("{}{}", ASYNC_CALLS, task_receipt.call_id);
    if let Some(data) = store.get(key.as_bytes()) {
        if let Ok(mut call) = bincode2::deserialize::<AsyncCrossChainCall>(&data) {
            call.status = match task_receipt.status {
                TaskStatus::Completed => AsyncCallStatus::Completed,
                TaskStatus::Failed => AsyncCallStatus::Failed,
                TaskStatus::TimedOut => AsyncCallStatus::Timeout,
                _ => return Ok(()),
            };
            call.completed_at = Some(task_receipt.block_height);
            call.result = task_receipt.result.clone();
            store.set(key.as_bytes(), &bincode2::serialize(&call)?);
        }
    }
    Ok(())
}

6.4 Agent 超时回退管理器

// ============================================================
// AI Agent 超时回退策略 (TypeScript)
// ============================================================

type TimeoutStrategy = {
    maxBlocks: number;
    retries: number;
    compensateOnFail: boolean;
    onTimeout?: (callId: string) => Promise<void>;
    notifyOnTimeout?: string[];
};

class AgentTimeoutManager {
    private pendingCalls: Map<string, { startBlock: number; strategy: TimeoutStrategy }>;

    constructor(private agentAddress: string, private chainId: string) {
        this.pendingCalls = new Map();
    }

    async monitorCall(callId: string, strategy: TimeoutStrategy): Promise<void> {
        this.pendingCalls.set(callId, {
            startBlock: await this.getCurrentBlock(),
            strategy,
        });
        this.startBlockMonitor(callId);
    }

    private async startBlockMonitor(callId: string): Promise<void> {
        const call = this.pendingCalls.get(callId);
        if (!call) return;

        const currentBlock = await this.getCurrentBlock();
        const elapsed = currentBlock - call.startBlock;

        if (elapsed >= call.strategy.maxBlocks) {
            await this.handleTimeout(callId, call.strategy);
            return;
        }

        const status = await this.checkCallStatus(callId);
        if (status === 'completed' || status === 'failed') {
            this.pendingCalls.delete(callId);
            return;
        }

        setTimeout(() => this.startBlockMonitor(callId), 6000);
    }

    private async handleTimeout(callId: string, strategy: TimeoutStrategy): Promise<void> {
        const status = await this.checkCallStatus(callId);
        if (status === 'completed') {
            this.pendingCalls.delete(callId);
            return;
        }

        if (strategy.onTimeout) {
            await strategy.onTimeout(callId);
        }
        if (strategy.notifyOnTimeout) {
            for (const agentDid of strategy.notifyOnTimeout) {
                await this.sendNotification(agentDid, { type: 'CROSS_CHAIN_TIMEOUT', callId });
            }
        }
        if (strategy.compensateOnFail && (status === 'pending' || status === 'timeout')) {
            await this.executeCompensation(callId);
        }
        this.pendingCalls.delete(callId);
    }

    private async getCurrentBlock(): Promise<number> { return 0; }
    private async checkCallStatus(callId: string): Promise<string> { return 'pending'; }
    private async executeCompensation(callId: string): Promise<void> { }
    private async sendNotification(targetDid: string, payload: any): Promise<void> { }
}

7. IBC 通道生命周期管理

7.1 通道状态机

IBC 通道有严格的状态机转换:

     ┌──────────┐
     │  INIT    │  ← OpenInit 在源链发起
     └────┬─────┘
          │
     ┌────▼────┐
     │ TRYOPEN │  ← OpenTry 在目标链响应
     └────┬─────┘
          │
     ┌────▼────┐
     │   OPEN  │  ← OpenAck + OpenConfirm 完成握手
     └────┬─────┘
          │
     ┌────▼────┐
     │ CLOSED  │  ← CloseInit / CloseConfirm
     └─────────┘

升级路径 (IBC 通道升级):
     OPEN ──→ FLUSHING ──→ FLUSHCOMPLETE ──→ OPEN (新版本)

7.2 通道升级机制

通道升级允许在不重建连接的情况下修改参数:

// ============================================================
// 通道升级提案 (Rust)
// ============================================================

#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct ChannelUpgradeProposal {
    pub channel_id: String,
    pub new_ordering: Option<String>,
    pub new_connection_hops: Option<Vec<String>>,
    pub new_version: Option<String>,
    pub proposed_by: String,
    pub proposed_at: u64,
    pub status: UpgradeStatus,
}

#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
pub enum UpgradeStatus {
    Proposed,
    Approved,
    Executing,
    Completed,
    RolledBack,
}

/// Agent 通道指标
#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct ChannelMetrics {
    pub channel_id: String,
    pub avg_delay_ms: u64,
    pub max_delay_ms: u64,
    pub total_packets: u64,
    pub failed_packets: u64,
    pub timed_out_packets: u64,
    pub failure_rate: f64,
    pub last_updated: u64,
}

/// AI Agent 通道升级评估
pub struct AgentChannelManager;

impl AgentChannelManager {
    pub fn evaluate_channel_upgrade(
        metrics: &ChannelMetrics,
    ) -> Option<ChannelUpgradeProposal> {
        let mut new_version = None;
        let mut needs_upgrade = false;

        if metrics.avg_delay_ms > 5000 {
            new_version = Some("ics-20-v2-fast".to_string());
            needs_upgrade = true;
        }
        if metrics.failure_rate > 0.05 {
            needs_upgrade = true;
        }

        if needs_upgrade {
            Some(ChannelUpgradeProposal {
                channel_id: metrics.channel_id.clone(),
                new_ordering: Some("ORDERED".to_string()),
                new_connection_hops: None,
                new_version,
                proposed_by: "agent-channel-manager".to_string(),
                proposed_at: 0,
                status: UpgradeStatus::Proposed,
            })
        } else {
            None
        }
    }
}

7.3 通道自动化管理合约

// ============================================================
// 通道自动化管理器 (Rust - CosmWasm)
// ============================================================

const MANAGED_CHANNELS: &str = "managed_channels";
const CHANNEL_ALERTS: &str = "channel_alerts";

#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct ManagedChannel {
    pub channel_id: String,
    pub remote_chain: String,
    pub port_id: String,
    pub connection_id: String,
    pub state: String,
    pub last_activity: u64,
    pub heartbeat_interval: u64,
    pub last_heartbeat: u64,
    pub auto_reconnect: bool,
    pub alert_on_failure: bool,
}

#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct ChannelAlert {
    pub channel_id: String,
    pub alert_type: ChannelAlertType,
    pub message: String,
    pub block_height: u64,
    pub resolved: bool,
}

#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
pub enum ChannelAlertType {
    ChannelClosed,
    HighFailureRate,
    NoActivity,
    TimeoutSpike,
}

#[derive(Serialize, Deserialize)]
pub enum ExecuteMsg {
    RegisterManagedChannel {
        channel_id: String,
        remote_chain: String,
        port_id: String,
        connection_id: String,
    },
    SendHeartbeat { channel_id: String },
    CheckChannelHealth { channel_id: String },
    UpgradeChannel { channel_id: String, new_version: String },
    CloseChannel { channel_id: String },
}

#[entry_point]
pub fn execute(
    deps: DepsMut,
    env: Env,
    info: MessageInfo,
    msg: ExecuteMsg,
) -> StdResult<Response> {
    match msg {
        ExecuteMsg::RegisterManagedChannel { channel_id, remote_chain, port_id, connection_id } => {
            register_managed_channel(deps, env, info, channel_id, remote_chain, port_id, connection_id)
        }
        ExecuteMsg::SendHeartbeat { channel_id } => {
            send_channel_heartbeat(deps, env, info, channel_id)
        }
        ExecuteMsg::CheckChannelHealth { channel_id } => {
            check_channel_health(deps, env, info, channel_id)
        }
        ExecuteMsg::UpgradeChannel { channel_id, new_version } => {
            upgrade_channel(deps, env, info, channel_id, new_version)
        }
        ExecuteMsg::CloseChannel { channel_id } => {
            close_managed_channel(deps, env, info, channel_id)
        }
    }
}

fn register_managed_channel(
    deps: DepsMut,
    env: Env,
    _info: MessageInfo,
    channel_id: String,
    remote_chain: String,
    port_id: String,
    connection_id: String,
) -> StdResult<Response> {
    let channel = ManagedChannel {
        channel_id: channel_id.clone(),
        remote_chain,
        port_id,
        connection_id,
        state: "OPEN".to_string(),
        last_activity: env.block.time.seconds(),
        heartbeat_interval: 100,
        last_heartbeat: env.block.time.seconds(),
        auto_reconnect: true,
        alert_on_failure: true,
    };
    let key = format!("{}{}", MANAGED_CHANNELS, channel_id);
    deps.storage.set(key.as_bytes(), &bincode2::serialize(&channel)?);
    Ok(Response::new()
        .add_attribute("action", "register_channel")
        .add_attribute("channel_id", &channel_id))
}

fn send_channel_heartbeat(
    deps: DepsMut,
    env: Env,
    _info: MessageInfo,
    channel_id: String,
) -> StdResult<Response> {
    let key = format!("{}{}", MANAGED_CHANNELS, channel_id);
    if let Some(data) = deps.storage.get(key.as_bytes()) {
        if let Ok(mut ch) = bincode2::deserialize::<ManagedChannel>(&data) {
            ch.last_heartbeat = env.block.time.seconds();
            ch.last_activity = env.block.time.seconds();
            deps.storage.set(key.as_bytes(), &bincode2::serialize(&ch)?);
        }
    }
    Ok(Response::new()
        .add_attribute("action", "heartbeat")
        .add_attribute("channel_id", &channel_id))
}

fn check_channel_health(
    deps: DepsMut,
    env: Env,
    _info: MessageInfo,
    channel_id: String,
) -> StdResult<Response> {
    let key = format!("{}{}", MANAGED_CHANNELS, channel_id);
    let data = deps.storage.get(key.as_bytes())
        .ok_or_else(|| cosmwasm_std::StdError::generic_err("Channel not managed"))?;
    let ch: ManagedChannel = bincode2::deserialize(&data)?;

    let elapsed = env.block.time.seconds() - ch.last_activity;
    let mut alerts = Vec::new();

    if elapsed > ch.heartbeat_interval * 6 {
        alerts.push(ChannelAlert {
            channel_id: channel_id.clone(),
            alert_type: ChannelAlertType::NoActivity,
            message: format!("No activity for {}s", elapsed),
            block_height: env.block.height,
            resolved: false,
        });
    }

    for alert in &alerts {
        let alert_key = format!("{}{}:{}", CHANNEL_ALERTS, channel_id, env.block.height);
        deps.storage.set(alert_key.as_bytes(), &bincode2::serialize(alert)?);
    }

    Ok(Response::new()
        .add_attribute("action", "health_check")
        .add_attribute("channel_id", &channel_id)
        .add_attribute("alerts", alerts.len().to_string()))
}

fn upgrade_channel(
    deps: DepsMut,
    _env: Env,
    _info: MessageInfo,
    channel_id: String,
    new_version: String,
) -> StdResult<Response> {
    // 1. 调用 ibc_channel_upgrade 开始 flushing
    // 2. 等待 flush 完成
    // 3. 应用新版本
    Ok(Response::new()
        .add_attribute("action", "upgrade_channel")
        .add_attribute("channel_id", &channel_id)
        .add_attribute("new_version", &new_version))
}

fn close_managed_channel(
    deps: DepsMut,
    _env: Env,
    _info: MessageInfo,
    channel_id: String,
) -> StdResult<Response> {
    let key = format!("{}{}", MANAGED_CHANNELS, channel_id);
    deps.storage.remove(key.as_bytes());
    Ok(Response::new()
        .add_attribute("action", "close_managed_channel")
        .add_attribute("channel_id", &channel_id))
}

7.4 Agent 通道自动化

// ============================================================
// AI Agent 通道自动化 (TypeScript)
// ============================================================

interface ChannelAutomationConfig {
    heartbeatInterval: number;
    healthCheckInterval: number;
    autoUpgradeThresholds: {
        maxDelayMs: number;
        maxFailureRate: number;
        minPacketsForEvaluation: number;
    };
}

class ChannelAutomationAgent {
    private managedChannels: Map<string, ChannelAutomationConfig>;

    constructor(private managerContract: string, private agentAddress: string) {
        this.managedChannels = new Map();
    }

    async watchChannel(channelId: string, remoteChain: string, config: ChannelAutomationConfig) {
        this.managedChannels.set(channelId, config);
        await this.registerWithContract(channelId, remoteChain);
        this.startChannelMonitor(channelId, config);
    }

    private async startChannelMonitor(channelId: string, config: ChannelAutomationConfig) {
        setInterval(async () => {
            const health = await this.fetchChannelMetrics(channelId);

            if (health.total_packets >= config.autoUpgradeThresholds.minPacketsForEvaluation) {
                if (health.failure_rate > config.autoUpgradeThresholds.maxFailureRate ||
                    health.avg_delay_ms > config.autoUpgradeThresholds.maxDelayMs) {
                    await this.proposeUpgrade(channelId, 'ics-20-v2-fast');
                }
            }
        }, config.healthCheckInterval * 6000);
    }

    private async registerWithContract(channelId: string, remoteChain: string) { }
    private async fetchChannelMetrics(channelId: string): Promise<ChannelMetrics> {
        return {
            channel_id: channelId, avg_delay_ms: 0, max_delay_ms: 0,
            total_packets: 0, failed_packets: 0, timed_out_packets: 0,
            failure_rate: 0, last_updated: Date.now(),
        };
    }
    private async proposeUpgrade(channelId: string, newVersion: string) { }
}

8. 跨链安全模式

8.1 ICS-26 路由安全

ICS-26 定义了 IBC 路由模块,确保数据包路由的正确性。核心安全原则:

// ============================================================
// ICS-26 路由安全检查 (Rust)
// ============================================================

#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct PacketRouteCheck {
    pub source_port: String,
    pub source_channel: String,
    pub destination_port: String,
    pub destination_channel: String,
    pub source_chain: String,
    pub destination_chain: String,
}

impl PacketRouteCheck {
    /// 验证路由合法性
    pub fn validate(&self, channel_table: &ChannelTable) -> Result<(), RouteError> {
        let src_entry = channel_table.lookup(&self.source_channel)?;

        // 1. 源端口匹配
        if src_entry.port_id != self.source_port {
            return Err(RouteError::PortMismatch {
                expected: src_entry.port_id.clone(),
                got: self.source_port.clone(),
            });
        }

        // 2. 目标端口匹配
        if src_entry.counterparty_port_id != self.destination_port {
            return Err(RouteError::CounterpartyPortMismatch {
                expected: src_entry.counterparty_port_id.clone(),
                got: self.destination_port.clone(),
            });
        }

        // 3. 通道未关闭
        if src_entry.state != "OPEN" {
            return Err(RouteError::ChannelNotOpen {
                state: src_entry.state.clone(),
            });
        }

        // 4. Agent 白名单检查(可选)
        // 如果目标链不在 Agent 的允许列表中,拒绝

        Ok(())
    }
}

#[derive(Serialize, Deserialize, Clone, Debug)]
pub enum RouteError {
    PortMismatch { expected: String, got: String },
    CounterpartyPortMismatch { expected: String, got: String },
    ChannelNotOpen { state: String },
    ChannelNotFound { channel_id: String },
    AgentNotAuthorized { agent_id: String, target_chain: String },
}

pub struct ChannelTable;

impl ChannelTable {
    pub fn lookup(&self, channel_id: &str) -> Result<ChannelEntry, RouteError> {
        // 从链上 KV 存储读取通道信息
        Err(RouteError::ChannelNotFound { channel_id: channel_id.to_string() })
    }
}

#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct ChannelEntry {
    pub port_id: String,
    pub counterparty_port_id: String,
    pub state: String,
}

8.2 MEV 跨链保护

跨链 MEV(最大可提取价值)攻击是指中继者通过重排序或审查数据包获取不当收益。

// ============================================================
// MEV 跨链保护措施 (Rust)
// ============================================================

/// MEV 保护策略
#[derive(Serialize, Deserialize, Clone, Debug)]
pub enum MevProtectionStrategy {
    /// 使用有序通道,保证数据包顺序
    OrderedChannel,
    /// 对数据包内容加密封装
    EncryptedPayload {
        encryption_key_hash: String,
        /// 解谜难度(PoW)
        pow_difficulty: u32,
    },
    /// 提交-揭示模式(Commit-Reveal)
    CommitReveal {
        commit_hash: String,
        reveal_later: bool,
    },
    /// 最小延迟约束
    MinDelay {
        min_blocks: u64,
    },
}

/// 提交-揭示模式的提交阶段数据包
#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct CommitPacket {
    pub commitment: Binary,   // sha256(real_payload + nonce)
    pub nonce_hash: Binary,
    pub agent_id: String,
}

/// 提交-揭示模式的揭示阶段数据包
#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct RevealPacket {
    pub commitment_id: String,
    pub real_payload: Binary,
    pub nonce: Binary,
}

/// Agent MEV 保护配置
#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct AgentMevConfig {
    pub agent_id: String,
    pub strategy: MevProtectionStrategy,
    pub enabled_channels: Vec<String>,
    pub priority_fee_multiplier: u8,  // 针对抢跑,提高优先费倍数
}

8.3 超时回退策略

// ============================================================
// 超时回退策略 (Rust)
// ============================================================

#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct TimeoutFallbackPlan {
    pub agent_id: String,
    pub packet_sequence: u64,
    pub channel_id: String,
    pub strategy: TimeoutFallbackStrategy,
    pub created_at: u64,
    pub executed: bool,
}

#[derive(Serialize, Deserialize, Clone, Debug)]
pub enum TimeoutFallbackStrategy {
    /// 自动重试,指数退避
    AutoRetry {
        max_attempts: u8,
        base_delay_blocks: u64,
        backoff_factor: u8,  // 2^n
    },
    /// 执行补偿交易
    Compensate {
        compensation_msgs: Vec<IcaCosmosMsg>,
        compensation_chain: String,
    },
    /// 发送超时证明到目标链
    ProveTimeout {
        proof: Binary,
        claim_refund: bool,
    },
    /// 升级为治理提案
    EscalateToGovernance {
        proposal_description: String,
    },
}

impl TimeoutFallbackPlan {
    /// 执行回退
    pub fn execute(&self, store: &mut dyn Storage) -> StdResult<Response> {
        match &self.strategy {
            TimeoutFallbackStrategy::AutoRetry { max_attempts, base_delay_blocks, backoff_factor } => {
                // 构造重试 IBC 数据包,延迟 = base_delay_blocks * backoff_factor^attempt
                let delay = base_delay_blocks * backoff_factor.pow(
                    store.get(b"retry_count").map(|d| d[0]).unwrap_or(0) as u32
                );
                let mut count = store.get(b"retry_count").map(|d| d[0]).unwrap_or(0);
                if count < *max_attempts as u8 {
                    count += 1;
                    store.set(b"retry_count", &[count]);
                    Ok(Response::new()
                        .add_attribute("action", "timeout_retry")
                        .add_attribute("delay_blocks", delay.to_string())
                        .add_attribute("attempt", count.to_string()))
                } else {
                    Err(cosmwasm_std::StdError::generic_err("Max retries reached"))
                }
            }
            TimeoutFallbackStrategy::Compensate { compensation_msgs: _, compensation_chain: _ } => {
                // 通过 ICA 发送补偿交易
                Ok(Response::new()
                    .add_attribute("action", "timeout_compensate"))
            }
            TimeoutFallbackStrategy::ProveTimeout { proof: _, claim_refund: _ } => {
                // 将超时证明提交到目标链以解锁资产
                Ok(Response::new()
                    .add_attribute("action", "timeout_prove"))
            }
            TimeoutFallbackStrategy::EscalateToGovernance { proposal_description: _ } => {
                // 提交治理提案
                Ok(Response::new()
                    .add_attribute("action", "timeout_escalate"))
            }
        }
    }
}

8.4 Agent 白名单与权限控制

// ============================================================
// Agent 跨链权限控制 (Rust)
// ============================================================

#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct AgentIbcPermission {
    pub agent_id: String,
    pub allowed_channels: Vec<String>,
    pub allowed_target_chains: Vec<String>,
    pub allowed_message_types: Vec<String>,
    pub rate_limit_per_block: u32,
    pub expiry: u64,
}

const AGENT_PERMISSIONS: &str = "agent_permissions";

#[derive(Serialize, Deserialize)]
pub enum PermissionExecuteMsg {
    GrantPermission {
        agent_id: String,
        permission: AgentIbcPermission,
    },
    RevokePermission {
        agent_id: String,
    },
    CheckPermission {
        agent_id: String,
        channel_id: String,
        target_chain: String,
    },
}

pub fn check_agent_permission(
    store: &dyn Storage,
    agent_id: &str,
    channel_id: &str,
    target_chain: &str,
) -> Result<bool, StdError> {
    let key = format!("{}{}", AGENT_PERMISSIONS, agent_id);
    if let Some(data) = store.get(key.as_bytes()) {
        let perm: AgentIbcPermission = bincode2::deserialize(&data)?;
        if !perm.allowed_channels.contains(&channel_id.to_string()) {
            return Ok(false);
        }
        if !perm.allowed_target_chains.contains(&target_chain.to_string()) {
            return Ok(false);
        }
        if let Some(exp) = perm.expiry {
            if exp < 0 { return Ok(false); } // 简化处理
        }
        return Ok(true);
    }
    Ok(false)
}

9. AI Agent 跨链工作流设计模式

9.1 组合 ICA + ICQ + ICS-721

高级 AI Agent 将三种 IBC 协议组合使用:

┌─ AI Agent 工作流编排 ───────────────────────────────────────┐
│                                                              │
│  1. ICQ 查询跨链状态                                          │
│     └→ 定期查询 chain-a 上的 DEX 池状态                         │
│                                                              │
│  2. 条件评估                                                  │
│     └→ 如果价差 > 5%,执行套利                                  │
│                                                              │
│  3. ICA 远程执行                                              │
│     └→ 通过 ICA 在 chain-a 上买入                              │
│     └→ 通过 ICA 在 chain-b 上卖出                              │
│                                                              │
│  4. ICS-721 资产跨链                                          │
│     └→ 将获得的 NFT 跨链转移到 MSG Chain                        │
│                                                              │
│  5. ack/timeout 闭环                                          │
│     └→ 检查所有跨链操作是否成功,失败则补偿                       │
│                                                              │
└──────────────────────────────────────────────────────────────┘

9.2 工作流编排器合约

// ============================================================
// Agent 工作流编排器 (Rust - CosmWasm)
// ============================================================

#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct AgentWorkflow {
    pub workflow_id: String,
    pub agent_id: String,
    pub status: WorkflowStatus,
    pub steps: Vec<WorkflowStep>,
    pub current_step: usize,
    pub created_at: u64,
    pub max_retries: u8,
}

#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
pub enum WorkflowStatus {
    Running,
    Completed,
    Failed,
    Compensating,
}

#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct WorkflowStep {
    pub step_type: StepType,
    pub target_chain: String,
    pub input: Binary,
    pub output: Option<Binary>,
    pub status: StepStatus,
    pub retry_count: u8,
}

#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
pub enum StepType {
    IcqQuery,
    IcaExecute,
    Ics721Transfer,
    Ics20Transfer,
    A2AMessage,
    WaitForCondition,
}

#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
pub enum StepStatus {
    Pending,
    Running,
    Succeeded,
    Failed,
    Skipped,
}

const WORKFLOWS: &str = "workflows";

#[derive(Serialize, Deserialize)]
pub enum ExecuteMsg {
    StartWorkflow {
        steps: Vec<WorkflowStep>,
    },
    AdvanceWorkflow {
        workflow_id: String,
        step_result: Binary,
    },
    CompensateWorkflow {
        workflow_id: String,
    },
}

#[entry_point]
pub fn execute(
    deps: DepsMut,
    env: Env,
    info: MessageInfo,
    msg: ExecuteMsg,
) -> StdResult<Response> {
    match msg {
        ExecuteMsg::StartWorkflow { steps } => {
            execute_start_workflow(deps, env, info, steps)
        }
        ExecuteMsg::AdvanceWorkflow { workflow_id, step_result } => {
            execute_advance_workflow(deps, env, info, workflow_id, step_result)
        }
        ExecuteMsg::CompensateWorkflow { workflow_id } => {
            execute_compensate_workflow(deps, env, info, workflow_id)
        }
    }
}

fn execute_start_workflow(
    deps: DepsMut,
    env: Env,
    info: MessageInfo,
    steps: Vec<WorkflowStep>,
) -> StdResult<Response> {
    let store = deps.storage;
    let workflow_id = format!("wf:{:x}", sha256(
        format!("{}{}", info.sender, env.block.time.nanos())
    ));

    let workflow = AgentWorkflow {
        workflow_id: workflow_id.clone(),
        agent_id: info.sender.to_string(),
        status: WorkflowStatus::Running,
        steps,
        current_step: 0,
        created_at: env.block.time.seconds(),
        max_retries: 3,
    };

    let key = format!("{}{}", WORKFLOWS, workflow_id);
    store.set(key.as_bytes(), &bincode2::serialize(&workflow)?);

    // 执行第一个步骤
    let first_step = &workflow.steps[0];
    let msgs = execute_step(first_step, &env);

    Ok(Response::new()
        .add_messages(msgs)
        .add_attribute("action", "start_workflow")
        .add_attribute("workflow_id", &workflow_id)
        .add_attribute("steps", workflow.steps.len().to_string()))
}

fn execute_step(step: &WorkflowStep, env: &Env) -> Vec<cosmwasm_std::SubMsg> {
    // 根据步骤类型构造对应的 IBC 消息
    match step.step_type {
        StepType::IcqQuery => {
            // 发送 ICQ 查询数据包
            vec![]
        }
        StepType::IcaExecute => {
            // 通过 ICA 发送执行消息
            vec![]
        }
        StepType::Ics721Transfer => {
            // 发送 ICS-721 NFT 转移包
            vec![]
        }
        StepType::Ics20Transfer => {
            // 发送 ICS-20 代币转账
            vec![]
        }
        StepType::A2AMessage => {
            // 发送 A2A 消息
            vec![]
        }
        StepType::WaitForCondition => {
            // 挂起等待条件触发
            vec![]
        }
    }
}

fn execute_advance_workflow(
    deps: DepsMut,
    _env: Env,
    info: MessageInfo,
    workflow_id: String,
    step_result: Binary,
) -> StdResult<Response> {
    let store = deps.storage;
    let key = format!("{}{}", WORKFLOWS, workflow_id);
    let data = store.get(key.as_bytes())
        .ok_or_else(|| cosmwasm_std::StdError::generic_err("Workflow not found"))?;
    let mut workflow: AgentWorkflow = bincode2::deserialize(&data)?;

    if workflow.agent_id != info.sender.to_string() {
        return Err(cosmwasm_std::StdError::generic_err("Not the workflow owner"));
    }

    // 更新当前步骤状态
    if let Some(step) = workflow.steps.get_mut(workflow.current_step) {
        step.output = Some(step_result);
        step.status = StepStatus::Succeeded;
    }

    workflow.current_step += 1;

    if workflow.current_step >= workflow.steps.len() {
        workflow.status = WorkflowStatus::Completed;
        store.set(key.as_bytes(), &bincode2::serialize(&workflow)?);
        return Ok(Response::new()
            .add_attribute("action", "workflow_completed")
            .add_attribute("workflow_id", &workflow_id));
    }

    // 执行下一步
    let next_step = &workflow.steps[workflow.current_step];
    store.set(key.as_bytes(), &bincode2::serialize(&workflow)?);
    let msgs = execute_step(next_step, &_env);

    Ok(Response::new()
        .add_messages(msgs)
        .add_attribute("action", "advance_workflow")
        .add_attribute("workflow_id", &workflow_id)
        .add_attribute("step", workflow.current_step.to_string()))
}

fn execute_compensate_workflow(
    deps: DepsMut,
    _env: Env,
    info: MessageInfo,
    workflow_id: String,
) -> StdResult<Response> {
    let store = deps.storage;
    let key = format!("{}{}", WORKFLOWS, workflow_id);
    let data = store.get(key.as_bytes())
        .ok_or_else(|| cosmwasm_std::StdError::generic_err("Workflow not found"))?;
    let mut workflow: AgentWorkflow = bincode2::deserialize(&data)?;

    if workflow.agent_id != info.sender.to_string() {
        return Err(cosmwasm_std::StdError::generic_err("Not the workflow owner"));
    }

    workflow.status = WorkflowStatus::Compensating;
    store.set(key.as_bytes(), &bincode2::serialize(&workflow)?);

    // 反向遍历已完成的步骤,执行补偿操作
    let mut compensating_msgs = Vec::new();
    for step in workflow.steps.iter().rev() {
        if step.status == StepStatus::Succeeded {
            match step.step_type {
                StepType::Ics20Transfer => {
                    // 发送反向转账
                }
                StepType::Ics721Transfer => {
                    // 发送 NFT 回传
                }
                _ => {}
            }
        }
    }

    Ok(Response::new()
        .add_messages(compensating_msgs)
        .add_attribute("action", "compensate_workflow")
        .add_attribute("workflow_id", &workflow_id))
}

9.3 工作流设计模式分类

模式 组合 适用场景
查询-执行 ICQ → ICA 跨链监控自动响应
资产桥接 ICS-20/721 → ICA 跨链资产转移后执行操作
多链原子套利 ICQ → ICA(chain-a) → ICA(chain-b) DEX 套利
NFT 迁移 ICS-721 → ICQ → ICS-721 NFT 在多链间寻找最优市场
跨链治理 ICQ → A2A → ICA 跨链投票代理
数据聚合 ICQ×N → ICA 多链数据聚合后写入目标链

9.4 Agent 工作流编排 CLI

#!/bin/bash
# ============================================================
# AI Agent 跨链工作流编排 (Bash)
# ============================================================

MSG_RPC="https://rpc.msg-chain-1.example.com"
AGENT_ADDR="msg1agent..."
ORCHESTRATOR="msg1orchestrator..."

# 启动跨链监控-套利工作流
echo "=== 启动跨链套利工作流 ==="

WORKFLOW=$(cat <<EOF
{
  "start_workflow": {
    "steps": [
      {
        "step_type": "IcqQuery",
        "target_chain": "chain-a",
        "input": "{\"query_type\": \"SmartContractState\", \"contract_address\": \"chain-a-dex-addr\", \"query_msg\": {\"pool\": {\"pair\": \"uosmo/umsg\"}}}"
      },
      {
        "step_type": "IcqQuery",
        "target_chain": "chain-b",
        "input": "{\"query_type\": \"SmartContractState\", \"contract_address\": \"chain-b-dex-addr\", \"query_msg\": {\"pool\": {\"pair\": \"uatom/umsg\"}}}"
      },
      {
        "step_type": "WaitForCondition",
        "target_chain": "msg-chain-1",
        "input": "{\"condition\": \"spread > 5%\"}"
      },
      {
        "step_type": "IcaExecute",
        "target_chain": "chain-a",
        "input": "{\"msgs\": [{\"WasmExecute\": {\"contract_address\": \"chain-a-dex-addr\", \"msg\": {\"swap\": {\"token_in\": \"umsg\", \"token_out\": \"uosmo\", \"amount\": \"1000000\"}}}}]}"
      },
      {
        "step_type": "IcaExecute",
        "target_chain": "chain-b",
        "input": "{\"msgs\": [{\"WasmExecute\": {\"contract_address\": \"chain-b-dex-addr\", \"msg\": {\"swap\": {\"token_in\": \"uosmo\", \"token_out\": \"umsg\", \"amount\": \"1000000\"}}}}]}"
      }
    ]
  }
}
EOF
)

msgd tx wasm execute "$ORCHESTRATOR" "$WORKFLOW" \
  --from agent-key \
  --node "$MSG_RPC" \
  --gas auto \
  --gas-prices 1000000000attoMSG \
  -y

echo "工作流已提交"

10. 实践:跨链 AI Agent 用例

10.1 用例一:跨链 NFT 市场

场景:AI Agent 在 MSG Chain 上运营一个 NFT 市场,允许用户将其他链的 NFT 跨链挂单。

┌──────────────┐     ICS-721      ┌─────────────────┐
│ 链 A (OSMO)  │  ──────────────→  │  MSG Chain      │
│              │   NFT 跨链转移     │  NFT 市场 Agent   │
│ 用户 NFT      │                  │                  │
│              │                  │  ┌──────────────┐ │
│              │                  │  │ 挂单、竞价    │ │
│              │                  │  │ 匹配、成交    │ │
│              │                  │  └──────────────┘ │
│              │                  │                  │
│              │  ←────────────── │  成交后 NFT 回传   │
│              │    ICS-721       │                  │
└──────────────┘                  └─────────────────┘
// ============================================================
// 跨链 NFT 市场合约片段 (Rust)
// ============================================================

#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct CrossChainListing {
    pub listing_id: String,
    pub seller: String,
    pub original_chain: String,
    pub class_id: String,
    pub token_id: String,
    pub price: Coin,
    pub status: ListingStatus,
    pub created_at: u64,
    pub expires_at: u64,
}

#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
pub enum ListingStatus {
    Active,
    Sold,
    Cancelled,
    Expired,
}

#[derive(Serialize, Deserialize)]
pub enum ExecuteMsg {
    ListNftFromChain {
        target_chain: String,
        class_id: String,
        token_id: String,
        price: Coin,
        expires_in_blocks: u64,
    },
    BuyNft {
        listing_id: String,
    },
    CancelListing {
        listing_id: String,
    },
    SettleCrossChainSale {
        listing_id: String,
    },
}

impl CrossChainListing {
    /// 创建跨链 listing,自动发起 ICS-721 转账
    pub fn create_with_ibc(
        listing: CrossChainListing,
        env: &Env,
    ) -> Vec<IbcMsg> {
        let nft_packet = NftPacketData {
            sender: listing.seller.clone(),
            receiver: get_market_escrow_address(),
            class_id: listing.class_id.clone(),
            token_ids: vec![listing.token_id.clone()],
            token_uris: None,
            memo: Some(Binary::from(
                serde_json::json!({"listing_id": listing.listing_id}).to_string().as_bytes()
            )),
        };

        vec![
            IbcMsg::SendPacket {
                channel_id: get_nft_channel(&listing.original_chain),
                data: Binary::from(bincode2::serialize(&nft_packet).unwrap()),
                timeout: cosmwasm_std::IbcTimeout::with_timestamp(
                    env.block.time.plus_seconds(600)
                ),
            }
        ]
    }
}

10.2 用例二:跨链数据聚合器

场景:AI Agent 通过 ICQ 聚合多条链上的价格数据,提供统一喂价服务。

// ============================================================
// 跨链数据聚合器 (TypeScript)
// ============================================================

interface CrossChainPriceFeed {
    chainId: string;
    dexContract: string;
    pair: string;
    currentPrice: number;
    lastUpdate: number;
    confidence: number;  // 0-1
}

class CrossChainAggregator {
    private feeds: Map<string, CrossChainPriceFeed> = new Map();
    private icqQueryIds: Map<string, string> = new Map();

    constructor(
        private icqContract: string,
        private agentAddress: string,
        private chains: string[],
    ) {}

    async startAggregation(): Promise<void> {
        for (const chain of this.chains) {
            const queryId = await this.registerICQ({
                targetChain: chain,
                queryType: {
                    type: 'SmartContractState',
                    contractAddress: this.getDexForChain(chain),
                    queryMsg: { pool: { pair: 'umsg/usdc' } },
                },
                frequency: 10,
            });
            this.icqQueryIds.set(chain, queryId);
        }

        this.processAggregationCycle();
    }

    private async processAggregationCycle(): Promise<void> {
        setInterval(async () => {
            for (const [chain, queryId] of this.icqQueryIds) {
                const result = await this.fetchICQResult(queryId);
                if (result) {
                    const price = this.extractPrice(result);
                    const feed: CrossChainPriceFeed = {
                        chainId: chain,
                        dexContract: this.getDexForChain(chain),
                        pair: 'umsg/usdc',
                        currentPrice: price,
                        lastUpdate: Date.now(),
                        confidence: this.calculateConfidence(chain, price),
                    };
                    this.feeds.set(chain, feed);
                }
            }

            // 计算聚合价格
            const aggregated = this.computeAggregatedPrice();
            console.log('Aggregated price:', aggregated);

            // 如果 Agent 配置了跨链写入,通过 ICA 将聚合价格写入目标链
            if (aggregated.confidence > 0.8) {
                await this.pushPriceToChain(aggregated);
            }
        }, 60000);
    }

    private computeAggregatedPrice(): { price: number; confidence: number } {
        const validFeeds = Array.from(this.feeds.values())
            .filter(f => f.confidence > 0.5 && Date.now() - f.lastUpdate < 120000);

        if (validFeeds.length === 0) return { price: 0, confidence: 0 };

        // 加权平均(按 confidence 加权)
        const totalWeight = validFeeds.reduce((s, f) => s + f.confidence, 0);
        const weightedPrice = validFeeds.reduce(
            (s, f) => s + f.currentPrice * f.confidence, 0
        ) / totalWeight;

        return {
            price: weightedPrice,
            confidence: totalWeight / validFeeds.length,
        };
    }

    private calculateConfidence(chain: string, price: number): number {
        // 根据历史偏差、链延迟等因素计算置信度
        return 0.9;
    }

    private async pushPriceToChain(aggregated: { price: number; confidence: number }): Promise<void> {
        // 通过 ICA 在目标链上更新价格
    }

    private getDexForChain(chain: string): string { return ''; }
    private async fetchICQResult(queryId: string): Promise<any> { return null; }
    private extractPrice(result: any): number { return 0; }
    private async registerICQ(params: any): Promise<string> { return ''; }
}

10.3 用例三:跨链治理代理

场景:AI Agent 在 MSG Chain 上作为治理代理,接收用户委托后跨链投票。

// ============================================================
// 跨链治理代理合约 (Rust)
// ============================================================

#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct GovernanceProxy {
    pub proxy_id: String,
    pub agent_id: String,
    pub target_chain: String,
    pub governance_contract: String,
    pub delegated_power: u128,
    pub voting_strategy: VotingStrategy,
}

#[derive(Serialize, Deserialize, Clone, Debug)]
pub enum VotingStrategy {
    /// 跟随委托人投票
    DelegateVote,
    /// 自主分析后投票
    Autonomous { confidence_threshold: u8 },
    /// 按权重投票
    Weighted { weights: Vec<(String, u8)> },
}

#[derive(Serialize, Deserialize)]
pub enum ExecuteMsg {
    RegisterProxy {
        target_chain: String,
        governance_contract: String,
        voting_strategy: VotingStrategy,
    },
    ReceiveDelegation {
        proxy_id: String,
        amount: u128,
        delegator: String,
    },
    CastVote {
        proxy_id: String,
        proposal_id: String,
        vote: String,         // "yes", "no", "abstain"
        rationale: Option<String>,
    },
    CrossChainVote {
        proxy_id: String,
        proposal_id: String,
        vote: String,
    },
}

#[entry_point]
pub fn execute(
    deps: DepsMut,
    env: Env,
    info: MessageInfo,
    msg: ExecuteMsg,
) -> StdResult<Response> {
    match msg {
        ExecuteMsg::RegisterProxy { target_chain, governance_contract, voting_strategy } => {
            register_proxy(deps, env, info, target_chain, governance_contract, voting_strategy)
        }
        ExecuteMsg::ReceiveDelegation { proxy_id, amount, delegator } => {
            receive_delegation(deps, env, info, proxy_id, amount, delegator)
        }
        ExecuteMsg::CastVote { proxy_id, proposal_id, vote, rationale } => {
            cast_vote(deps, env, info, proxy_id, proposal_id, vote, rationale)
        }
        ExecuteMsg::CrossChainVote { proxy_id, proposal_id, vote } => {
            cross_chain_vote(deps, env, info, proxy_id, proposal_id, vote)
        }
    }
}

fn register_proxy(
    deps: DepsMut,
    _env: Env,
    info: MessageInfo,
    target_chain: String,
    governance_contract: String,
    voting_strategy: VotingStrategy,
) -> StdResult<Response> {
    let proxy_id = format!("proxy:{}:{}", info.sender, target_chain);
    let proxy = GovernanceProxy {
        proxy_id: proxy_id.clone(),
        agent_id: info.sender.to_string(),
        target_chain,
        governance_contract,
        delegated_power: 0,
        voting_strategy,
    };
    let key = format!("proxy:{}", proxy_id);
    deps.storage.set(key.as_bytes(), &bincode2::serialize(&proxy)?);
    Ok(Response::new()
        .add_attribute("action", "register_proxy")
        .add_attribute("proxy_id", &proxy_id))
}

fn receive_delegation(
    deps: DepsMut,
    _env: Env,
    _info: MessageInfo,
    proxy_id: String,
    amount: u128,
    delegator: String,
) -> StdResult<Response> {
    let key = format!("proxy:{}", proxy_id);
    let data = deps.storage.get(key.as_bytes())
        .ok_or_else(|| cosmwasm_std::StdError::generic_err("Proxy not found"))?;
    let mut proxy: GovernanceProxy = bincode2::deserialize(&data)?;
    proxy.delegated_power += amount;
    deps.storage.set(key.as_bytes(), &bincode2::serialize(&proxy)?);
    Ok(Response::new()
        .add_attribute("action", "receive_delegation")
        .add_attribute("proxy_id", &proxy_id)
        .add_attribute("delegator", &delegator)
        .add_attribute("amount", amount.to_string()))
}

fn cast_vote(
    deps: DepsMut,
    _env: Env,
    _info: MessageInfo,
    proxy_id: String,
    proposal_id: String,
    vote: String,
    rationale: Option<String>,
) -> StdResult<Response> {
    // 本地记录投票意图
    let vote_key = format!("vote:{}:{}", proxy_id, proposal_id);
    deps.storage.set(vote_key.as_bytes(), vote.as_bytes());
    Ok(Response::new()
        .add_attribute("action", "cast_vote")
        .add_attribute("proxy_id", &proxy_id)
        .add_attribute("proposal_id", &proposal_id)
        .add_attribute("vote", &vote))
}

fn cross_chain_vote(
    deps: DepsMut,
    env: Env,
    info: MessageInfo,
    proxy_id: String,
    proposal_id: String,
    vote: String,
) -> StdResult<Response> {
    let key = format!("proxy:{}", proxy_id);
    let data = deps.storage.get(key.as_bytes())
        .ok_or_else(|| cosmwasm_std::StdError::generic_err("Proxy not found"))?;
    let proxy: GovernanceProxy = bincode2::deserialize(&data)?;

    // 通过 ICA 在目标链上投票
    let vote_msg = serde_json::json!({
        "vote": {
            "proposal_id": proposal_id,
            "voter": proxy.agent_id,
            "option": vote,
        }
    });

    let ica_execute = IcaExecutePacket {
        msgs: vec![IcaCosmosMsg::WasmExecute {
            contract_address: proxy.governance_contract.clone(),
            msg: Binary::from(vote_msg.to_string().as_bytes()),
            funds: vec![],
        }],
        salt: None,
    };

    let ica_key = format!("ica:{}:{}", info.sender, proxy.target_chain);
    let ica_data = deps.storage.get(ica_key.as_bytes())
        .ok_or_else(|| cosmwasm_std::StdError::generic_err("ICA not found"))?;
    let ica_state: IcaAccountState = bincode2::deserialize(&ica_data)?;

    let ibc_msg = IbcMsg::SendPacket {
        channel_id: ica_state.host_channel,
        data: Binary::from(bincode2::serialize(&ica_execute)?),
        timeout: cosmwasm_std::IbcTimeout::with_timestamp(
            env.block.time.plus_seconds(300)
        ),
    };

    Ok(Response::new()
        .add_message(ibc_msg)
        .add_attribute("action", "cross_chain_vote")
        .add_attribute("proxy_id", &proxy_id)
        .add_attribute("proposal_id", &proposal_id)
        .add_attribute("target_chain", &proxy.target_chain))
}

10.4 用例四:跨链代币桥监控 Agent

场景:AI Agent 监控跨链桥通道的健康状况,在异常时自动触发保护措施。

// ============================================================
// 桥监控 Agent (Rust 核心逻辑)
// ============================================================

pub struct BridgeMonitorAgent;

impl BridgeMonitorAgent {
    /// 检查桥通道健康状况
    pub fn check_bridge_health(
        metrics: &ChannelMetrics,
        config: &RateLimitConfig,
    ) -> Vec<BridgeAlert> {
        let mut alerts = Vec::new();

        // 1. 速率限制接近阈值
        let egress_usage = config.current_egress as f64 / config.max_egress as f64;
        if egress_usage > 0.9 {
            alerts.push(BridgeAlert {
                severity: AlertSeverity::Warning,
                message: format!("Egress rate at {:.1}%", egress_usage * 100.0),
                channel_id: config.channel_id.clone(),
                timestamp: 0,
            });
        }

        // 2. 失败率异常
        if metrics.failure_rate > 0.1 {
            alerts.push(BridgeAlert {
                severity: AlertSeverity::Critical,
                message: format!("Failure rate {:.2}% exceeds threshold", metrics.failure_rate * 100.0),
                channel_id: metrics.channel_id.clone(),
                timestamp: 0,
            });
        }

        // 3. 长时间无活动
        if metrics.total_packets == 0 {
            alerts.push(BridgeAlert {
                severity: AlertSeverity::Info,
                message: "No packets transferred yet".to_string(),
                channel_id: metrics.channel_id.clone(),
                timestamp: 0,
            });
        }

        alerts
    }

    /// 执行自动保护动作
    pub fn execute_protection(
        alerts: &[BridgeAlert],
        env: &Env,
    ) -> Vec<IbcMsg> {
        let mut msgs = Vec::new();

        for alert in alerts {
            match alert.severity {
                AlertSeverity::Critical => {
                    // 暂停通道
                    msgs.push(IbcMsg::CloseChannel {
                        channel_id: alert.channel_id.clone(),
                    });
                }
                AlertSeverity::Warning => {
                    // 发送 A2A 通知给维护 Agent
                }
                AlertSeverity::Info => {
                    // 仅记录日志
                }
            }
        }

        msgs
    }
}

#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct BridgeAlert {
    pub severity: AlertSeverity,
    pub message: String,
    pub channel_id: String,
    pub timestamp: u64,
}

#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
pub enum AlertSeverity {
    Info,
    Warning,
    Critical,
}

11. 总结与边界

11.1 MSG Chain IBC 能力矩阵总结

能力 状态 生产可用 本文章节
ICS-20 代币转账 ✅ implemented ✅ 基础指南
ICS-29 Fee Middleware ✅ implemented ✅ 第 2 章
Rate Limit Middleware ✅ implemented ✅ 第 2 章
自定义 CosmWasm IBC 中间件 ✅ contract_runtime ✅ 第 2 章
ICS-721 NFT 跨链 开发中 ❌ 第 3 章
ICS-27 Interchain Accounts 开发中 ❌ 第 4 章
ICS-24 Interchain Queries 开发中 ❌ 第 5 章
IBC 通道升级 开发中 ❌ 第 7 章
genesis_registry_v1 ✅ implemented ✅ 第 3 章引用

11.2 按 MSG Chain 开发者能力矩阵的边界声明

根据 developer_capability_matrix.json:

能力表面 production_supported 在本文中的处理方式
contract_runtime ✅ CosmWasm 合约 IBC 入口点为生产就绪
registry_resolution ✅ canonical key 解析为生产就绪
rpc_gateway ✅ RPC 查询与广播为生产就绪
chain_config_pack ✅ 链配置为生产就绪
core_contract_reference_pack ❌ 合约 reference 为参考级别,不视为生产 ABI
formal_api_schema_pack ❌ API schema 为参考级别
agent_query_and_guarded_write ❌ Agent 写路径仍为受保护模式
sdk_surface ❌ SDK 为 alpha 候选
public_sandbox_strategy ❌ 公共沙箱为 fail-closed

11.3 关键边界

  1. ICS-27 / ICS-721 / ICQ 当前状态:本文提供的合约代码为设计参考和开发中实现,并非 MSG Chain 主网已部署的生产合约。在生产环境中使用前,必须验证对应模块的实际部署状态。

  2. Stub 端点不可用:本文不引用任何标记为 Stub 的 API 端点。Agent 查询面和受保护写路径(agent_query_and_guarded_write)中的 Stub 路径不可用于生产。

  3. 中间件组合依赖于链配置:Fee Middleware 和 Rate Limit Middleware 的实际可用性取决于 MSG Chain 的 IBC 模块编译配置。部署前应通过链上查询确认。

  4. 通道升级需要链升级:IBC 通道升级(ICS-27 升级特性)需要 Cosmos SDK v0.47+ 的支持。MSG Chain 的升级时间线应参考链官方文档。

  5. ICA 控制的安全性:Interchain Accounts 赋予 AI Agent 在目标链上的直接控制权。开发者应严格限制 ICA 的权限范围,并使用 multisig 或时间锁机制保护关键操作。

  6. ICQ 证明验证的依赖:Interchain Queries 的安全性依赖于轻客户端证明验证。如果 Relayer 不可信但证明验证得到正确执行,ICQ 仍然是安全的。

11.4 路线图参考

以下功能在 MSG Chain 路线图中:

功能 预期阶段 依赖
ICS-721 主网上线 开发中 CW-721 兼容性
ICS-27 ICA 主网上线 开发中 SDK 版本
ICQ 查询面 开发中 Relayer 基础设施
通道升级 规划中 SDK v0.50+
Agent 写路径生产化 规划中 风控、密钥、权限系统
SDK 正式发布 规划中 稳定 API 契约

11.5 延伸阅读


文档版本:v1.0 · 2026-07-08
适用链:msg-chain-1 · Bech32 前缀:msg
维护:本文档由 MSG Chain 技术团队维护,随链升级同步更新。