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
相关产品推荐
相关产品推荐

