如何实现自有NFT市场中用户BVC Token支付的钱包授权?
如何获取用户BVC Token权限完成NFT购买
首先,你当前合约里的授权逻辑完全搞反了:ERC20的授权是用户(Token持有者)主动授权给市场合约,允许合约从用户地址划转指定数量的Token,而不是合约给用户授权。下面是正确的实现方案:
一、核心流程说明
用户购买NFT时的正确流程是:
- 前端检查用户对市场合约的BVC Token授权额度是否≥NFT售价
- 如果授权额度不足,调用BVC Token合约的
approve(address spender, uint256 amount)方法,让用户签名完成授权 - 授权完成后,调用市场合约的购买函数,由合约从用户地址划转Token完成交易
二、合约代码修正
你当前的n2DMarketSale和approveCRI函数存在严重逻辑错误,以下是修正后的关键代码:
1. 移除错误的授权函数
删除approveCRI和allowenceCRI函数,因为这两个函数的逻辑完全错误(合约不能给自己授权用户的Token),授权操作应该由前端直接调用BVC Token合约完成。
2. 修正购买函数n2DMarketSale
function n2DMarketSale(address nftContract, uint256 itemId, uint256 _pid) public nonReentrant { TokenInfo storage tokens = AllowedCrypto[_pid]; IERC20 paytoken = tokens.paytoken; uint256 listingFee = tokens.listingFee; uint256 price = idToVaultItem[itemId].price; // 从VaultItem取对应NFT的价格,而非全局固定价格 uint256 tokenId = idToVaultItem[itemId].tokenId; VaultItem storage item = idToVaultItem[itemId]; require(!item.sold, "该NFT已售出"); require(msg.sender != item.seller, "不能购买自己的NFT"); // 检查用户授权额度是否足够覆盖售价 require(paytoken.allowance(msg.sender, address(this)) >= price, "授权额度不足"); // 检查用户Token余额是否足够 require(paytoken.balanceOf(msg.sender) >= price, "Token余额不足"); // 划转Token:用户 -> 卖家 require(paytoken.transferFrom(msg.sender, item.seller, price), "向卖家转账失败"); // 划转平台手续费:用户 -> 平台持有者 require(paytoken.transferFrom(msg.sender, holder, listingFee), "手续费转账失败"); // 转移NFT所有权 IERC721(nftContract).transferFrom(address(this), msg.sender, tokenId); item.holder = payable(msg.sender); item.sold = true; _itemsSold.increment(); }
3. 修正createVaultItem函数(优化建议)
当前函数固定使用TokenInfo里的价格,建议改成由卖家自定义价格,更符合NFT市场逻辑:
function createVaultItem(address nftContract, uint256 tokenId, uint256 _pid, uint256 customPrice) public nonReentrant { TokenInfo storage tokens = AllowedCrypto[_pid]; IERC20 paytoken = tokens.paytoken; uint256 listingFee = tokens.listingFee; uint256 price = customPrice; require(price > 0, "价格不能为0"); // 若listingFee收取BVC Token,替换msg.value逻辑为Token转账: // require(paytoken.allowance(msg.sender, address(this)) >= listingFee, "手续费授权不足"); // require(paytoken.transferFrom(msg.sender, holder, listingFee), "手续费转账失败"); _itemIds.increment(); uint256 itemId = _itemIds.current(); idToVaultItem[itemId] = VaultItem(itemId, nftContract, tokenId, payable(msg.sender), payable(address(0)), price, false); IERC721(nftContract).transferFrom(msg.sender, address(this), tokenId); emit VaultItemCreated(itemId, nftContract, tokenId, msg.sender, address(0), price, false); }
4. 修正withdraw函数
原函数逻辑错误,调整为仅允许owner提取合约内的Token:
function withdraw(uint256 _pid) public onlyOwner { TokenInfo storage tokens = AllowedCrypto[_pid]; IERC20 paytoken = tokens.paytoken; uint256 balance = paytoken.balanceOf(address(this)); require(balance > 0, "没有可提取的Token"); require(paytoken.transfer(msg.sender, balance), "提取失败"); }
三、前端实现步骤
用户点击购买按钮时,前端需执行以下操作:
- 获取目标NFT的售价
price、对应BVC Token合约地址及市场合约地址 - 调用BVC Token合约的
allowance(userAddress, marketContractAddress)方法,检查当前授权额度 - 若授权额度 < 售价,调用BVC Token合约的
approve(marketContractAddress, price)方法,触发用户钱包签名授权 - 等待授权交易上链确认后,调用市场合约的
n2DMarketSale方法完成购买
示例前端伪代码:
async function buyNFT(nftContract, itemId, pid, price) { const bvcToken = new ethers.Contract(bvcAddress, bvcAbi, signer); const marketContract = new ethers.Contract(marketAddress, marketAbi, signer); // 检查授权额度 const allowance = await bvcToken.allowance(signer.address, marketAddress); if (allowance.lt(price)) { // 发起授权请求 const approveTx = await bvcToken.approve(marketAddress, price); await approveTx.wait(); } // 执行NFT购买 const buyTx = await marketContract.n2DMarketSale(nftContract, itemId, pid); await buyTx.wait(); alert("NFT购买成功!"); }
四、注意事项
- 确保你的BVC Token合约符合标准ERC20规范,已实现
approve和transferFrom方法 - 若平台手续费收取ETH,保留原
msg.value逻辑;若收取BVC Token,需替换为Token转账逻辑 - 合约中所有涉及Token操作的地方,需添加转账结果检查(
require(transferResult, "转账失败")),避免异常情况
内容的提问来源于stack exchange,提问作者CRYPTO
相关产品推荐
相关产品推荐

