You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

CosmWASM开发的SEI链NFT置换合约无法向所有者发放新NFT

SEI测试网CosmWASM NFT置换合约执行异常问题

我在SEI区块链上基于CosmWASM开发NFT置换合约,核心逻辑为:用户发送旧系列NFT至合约,合约销毁旧NFT并向用户发放新系列NFT。该合约在本地测试环境运行正常,但部署到SEI测试网后,合约能接收旧NFT,却未执行后续销毁旧NFT、发放新NFT的操作,且无任何报错信息。

测试流程

  • 部署旧NFT集合(cw721标准)
  • 铸造旧集合内1枚NFT(例:代币#1)
  • 部署新NFT集合(cw721标准)
  • 将新集合全部NFT铸造至置换合约地址(合约成为新NFT所有者)
  • 通过Cosmos SDK的send_nft函数,将旧NFT #1发送至置换合约
  • 置换合约收到旧NFT,但未触发销毁旧NFT、转移新NFT给原所有者的预期操作

核心代码

use std::marker::PhantomData;
use cosmwasm_std::{from_json, Addr, Empty, MessageInfo, Response};
use cw721::Cw721ReceiveMsg;
use cw2981_royalties::ExecuteMsg as Cw2981ExecuteMsg;
use cw721_base::helpers::Cw721Contract;

use crate::{msg::InnerMsg, ContractError};

pub fn receive_nft(info: MessageInfo, receive_msg: Cw721ReceiveMsg) -> Result<Response, ContractError> {
    let token_id = receive_msg.token_id.as_str();
    let recipient = receive_msg.sender.as_str();
    let inner_msg: InnerMsg = from_json(&receive_msg.msg)?;
    let new_collection_addr = inner_msg.new_collection_addr;

    let old_collection_env_addr = info.sender.as_str();
    let old_collection_addr = Addr::unchecked("sei1exm3fjundhdzf6wng3xcny4nhjlawwmztxd286f35zcvx4mav4jqcnese6");
    if old_collection_addr != old_collection_env_addr {
        return Err(ContractError::Unauthorized {});
    }

    Cw721Contract::<Empty, Empty>(
        old_collection_addr.clone(),
        PhantomData,
        PhantomData
    ).call(Cw2981ExecuteMsg::Burn { token_id: token_id.to_string() }).unwrap();

    Cw721Contract::<Empty, Empty>(
        new_collection_addr.clone(),
        PhantomData,
        PhantomData
    ).call(Cw2981ExecuteMsg::TransferNft { recipient: recipient.to_string(), token_id: token_id.to_string() }).unwrap();

    let response = Response::new();
    Ok(
        response
            .add_attribute("action", "burn_to_mint")
            .add_attribute("old_collection", old_collection_addr.to_string())
            .add_attribute("old_collection_env", old_collection_env_addr.to_string())
            .add_attribute("new_collection", new_collection_addr.to_string())
            .add_attribute("recipient", recipient.to_string())
            .add_attribute("token_id", token_id.to_string())
    )
}

预期执行的核心逻辑

Cw721Contract::<Empty, Empty>(
        old_collection_addr.clone(),
        PhantomData,
        PhantomData
    ).call(Cw2981ExecuteMsg::Burn { token_id: token_id.to_string() }).unwrap();

Cw721Contract::<Empty, Empty>(
        new_collection_addr.clone(),
        PhantomData,
        PhantomData
    ).call(Cw2981ExecuteMsg::TransferNft { recipient: recipient.to_string(), token_id: token_id.to_string() }).unwrap();

问题分析与修复方案

1. 跨合约调用未添加至响应消息

CosmWASM中,Cw721Contract::call()仅构建调用消息,不会自动执行链上操作,必须将消息添加到Response的子消息列表中才会触发执行。原代码未执行此步骤,导致调用逻辑无实际效果。

2. 错误处理不当

使用.unwrap()会直接触发panic,测试网环境下panic会导致整个交易回滚,且不会暴露具体错误信息。应改用?将错误返回为ContractError,便于在链上日志中排查问题。

3. 修正后的代码示例

use std::marker::PhantomData;
use cosmwasm_std::{from_json, Addr, Empty, MessageInfo, Response};
use cw721::Cw721ReceiveMsg;
use cw2981_royalties::ExecuteMsg as Cw2981ExecuteMsg;
use cw721_base::helpers::Cw721Contract;

use crate::{msg::InnerMsg, ContractError};

pub fn receive_nft(info: MessageInfo, receive_msg: Cw721ReceiveMsg) -> Result<Response, ContractError> {
    let token_id = receive_msg.token_id.as_str();
    let recipient = receive_msg.sender.as_str();
    let inner_msg: InnerMsg = from_json(&receive_msg.msg)?;
    let new_collection_addr = inner_msg.new_collection_addr;

    let old_collection_env_addr = info.sender.as_str();
    let old_collection_addr = Addr::unchecked("sei1exm3fjundhdzf6wng3xcny4nhjlawwmztxd286f35zcvx4mav4jqcnese6");
    if old_collection_addr != old_collection_env_addr {
        return Err(ContractError::Unauthorized {});
    }

    // 构建销毁旧NFT的消息并添加至响应
    let burn_msg = Cw721Contract::<Empty, Empty>(
        old_collection_addr.clone(),
        PhantomData,
        PhantomData
    ).call(Cw2981ExecuteMsg::Burn { token_id: token_id.to_string() })?;

    // 构建转移新NFT的消息并添加至响应
    let transfer_msg = Cw721Contract::<Empty, Empty>(
        new_collection_addr.clone(),
        PhantomData,
        PhantomData
    ).call(Cw2981ExecuteMsg::TransferNft { recipient: recipient.to_string(), token_id: token_id.to_string() })?;

    Ok(Response::new()
        .add_message(burn_msg)
        .add_message(transfer_msg)
        .add_attribute("action", "burn_to_mint")
        .add_attribute("old_collection", old_collection_addr.to_string())
        .add_attribute("old_collection_env", old_collection_env_addr.to_string())
        .add_attribute("new_collection", new_collection_addr.to_string())
        .add_attribute("recipient", recipient.to_string())
        .add_attribute("token_id", token_id.to_string())
    )
}

额外检查点

  • 查看测试网交易的链上日志,确认是否存在隐藏错误
  • 验证置换合约是否确实持有目标新NFT,可通过调用新NFT集合的owner_of接口确认
  • 确认旧NFT集合的Burn权限已开放给置换合约

内容的提问来源于stack exchange,提问作者Nico Serrano

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.29 08:52:02