如何通过EVM交易或HSCS智能合约关联/解除关联HTS代币?
解决HTS代币关联/解除关联的EVM实现问题
问题根源
你在合约里调用HederaTokenService.associateToken(address(msg.sender), token)失败的核心原因是:关联代币是需要账户主动授权的操作,合约无法直接替外部账户执行关联,除非该账户预先给合约授予了关联权限。Hedera的代币关联操作要求交易由账户本身发起,或者账户通过签名授权第三方(比如合约)代为执行。
可行解决方案
方案1:让用户先预授权,再由合约执行关联
用户需要先通过EVM签名给合约授予关联指定代币的权限,之后合约才能成功执行关联操作。
步骤1:用户前端生成授权签名(示例用ethers.js)
用户需要对授权内容签名,构造授权数据后完成签名:
import { ethers } from "ethers"; const tokenAddress = "0x..."; // HTS代币的EVM地址 const contractAddress = "0x..."; // 你的合约地址 // 构造授权数据:授权合约关联指定代币 const approveAssociateData = ethers.utils.defaultAbiCoder.encode( ["address", "address"], [tokenAddress, contractAddress] ); // 用户签名 const signature = await signer.signMessage(ethers.utils.arrayify(approveAssociateData));
步骤2:合约中验证签名并执行关联
修改合约代码,先验证用户的授权签名,再执行关联:
import "@hashgraph/hedera-solidity-library/contracts/HederaTokenService.sol"; import "@hashgraph/hedera-solidity-library/contracts/HederaResponseCodes.sol"; import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; contract YourNFTContract { using ECDSA for bytes32; function mintNft( address token, bytes[] memory metadata, bytes memory signature ) public payable returns(int64){ // 验证用户的授权签名 bytes32 messageHash = keccak256(abi.encode(token, address(this))); bytes32 ethSignedMessageHash = messageHash.toEthSignedMessageHash(); address signer = ethSignedMessageHash.recover(signature); require(signer == msg.sender, "Invalid authorization signature"); // 铸造NFT (int response, , int64[] memory serial) = HederaTokenService.mintToken(token, 0, metadata); if(response != HederaResponseCodes.SUCCESS){ revert("Failed to mint non-fungible token"); } // 执行关联 int res = HederaTokenService.associateToken( address(msg.sender), token ); if(res != HederaResponseCodes.SUCCESS){ revert("Failed to associate non-fungible token"); } return serial[0]; } }
方案2:让用户自行通过EVM交易完成关联
如果不想处理授权逻辑,可让用户在调用合约mintNft之前,直接调用Hedera Token Service的关联方法完成操作:
import { ethers } from "ethers"; const tokenAddress = "0x..."; // HTS代币的EVM地址 // HTS合约固定地址:0x1670010000000000000000000000000000000000 const htsContractABI = [ "function associateToken(address account, address token) external returns(int)" ]; const htsContract = new ethers.Contract( "0x1670010000000000000000000000000000000000", htsContractABI, signer ); // 发送关联交易 const tx = await htsContract.associateToken(signer.address, tokenAddress); await tx.wait();
用户完成关联后,再调用合约的mintNft方法就不会报错了。
方案3:绕开关联,先铸到合约再转用户
如果用户不想提前关联,可先将NFT铸到合约地址(合约需预先关联目标代币),再从合约转账给用户:
import "@hashgraph/hedera-solidity-library/contracts/HederaTokenService.sol"; import "@hashgraph/hedera-solidity-library/contracts/HederaResponseCodes.sol"; contract YourNFTContract { function mintNft( address token, bytes[] memory metadata ) public payable returns(int64){ // 铸造NFT到合约地址 (int response, , int64[] memory serial) = HederaTokenService.mintToken(token, 0, metadata); if(response != HederaResponseCodes.SUCCESS){ revert("Failed to mint non-fungible token"); } // 从合约转账NFT给用户 int transferRes = HederaTokenService.transferNFT( token, address(this), address(msg.sender), serial[0] ); if(transferRes != HederaResponseCodes.SUCCESS){ revert("Failed to transfer NFT to user"); } return serial[0]; } }
注意:需要确保合约已经关联了目标代币,可在合约部署时添加关联逻辑,或由管理员提前给合约完成关联。
解除关联的实现逻辑
解除关联和关联逻辑一致:
- 合约中调用
HederaTokenService.disassociateToken(address account, address token),前提是用户完成授权 - 或者用户直接调用HTS合约的
disassociateToken方法自行操作
内容的提问来源于stack exchange,提问作者Alfredo Gutierrez
相关产品推荐
相关产品推荐

