作为合约管理员,如何代用户执行BSC测试网存提交易?
解决方案:管理员代玩家执行BSC测试网合约存提操作
核心问题分析
你遇到的错误根源是:尝试用玩家地址作为交易发起方,但你没有玩家的私钥,MetaMask无法授权一个不在你钱包里的地址。要实现管理员代执行,必须让玩家提前完成代币授权+操作签名,同时优化合约的权限验证逻辑。
一、合约层优化(必须步骤)
当前存提函数没有验证操作是否经过玩家同意,任何人都可以调用,存在风险。建议添加签名验证,确保只有持有玩家有效签名的管理员才能执行操作:
// 签名验证工具函数 function verifySignature(address _sender, uint256 _amount, bytes memory _signature) public view returns (bool) { // 构造签名消息(包含链ID防止跨链重放) bytes32 messageHash = keccak256(abi.encodePacked(_sender, _amount, address(this), block.chainid)); bytes32 ethSignedMessageHash = keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", messageHash)); // 恢复签名者地址 address signer = ecrecover(ethSignedMessageHash, uint8(_signature[64]), bytes32(_signature[0:32]), bytes32(_signature[32:64])); return signer == _sender; } // 优化后的存币函数 function deposit(uint256 _amount, address _sender, bytes memory _signature) external { // 验证签名为玩家本人签署 require(verifySignature(_sender, _amount, _signature), "签名无效"); // 验证玩家已授权足够额度 require(tfmToken.allowance(_sender, address(this)) >= _amount, "授权额度不足"); tfmToken.transferFrom(_sender, address(this), _amount); emit Deposit(_sender, _amount); deposits[_sender] += _amount; } // 优化后的提币函数 function withdraw(uint256 _amount, address _sender, bytes memory _signature) external { require(verifySignature(_sender, _amount, _signature), "签名无效"); require(deposits[_sender] >= _amount, "存币余额不足"); tfmToken.transfer(_sender, _amount); emit Withdraw(_sender, _amount); withdraws[_sender] += _amount; deposits[_sender] -= _amount; }
二、前端完整流程实现(Next.js)
1. 玩家端提交存提请求时
玩家需要完成两个关键操作,然后把参数提交给管理员后台:
// 1. 玩家授权Game合约转走指定数量的tfmToken const tfmTokenContract = new web3.eth.Contract(TfmTokenABI, tfmTokenAddress); await tfmTokenContract.methods.approve(gameContractAddress, weiamount).send({ from: playerAddress }); // 2. 玩家对存提信息签名(证明是本人发起的请求) const messageHash = web3.utils.soliditySha3( playerAddress, weiamount, gameContractAddress, 97 // BSC测试网链ID,固定值 ); const signature = await web3.eth.personal.sign(messageHash, playerAddress); // 3. 把参数提交给后端/管理员后台 await fetch('/api/submit-request', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ playerAddress, weiamount, signature, type: 'deposit' // 标记是存还是提 }) });
2. 管理员后台执行交易
管理员用自己的钱包发起交易,调用优化后的合约函数:
// 连接管理员的MetaMask钱包 const web3 = new Web3(window.ethereum); await window.ethereum.enable(); const adminAddress = (await web3.eth.getAccounts())[0]; // 从后端获取玩家提交的参数:weiamount, playerAddress, signature const gameContract = new web3.eth.Contract(GameContractABI, gameContractAddress); // 执行存币交易(提币同理调用withdraw) await gameContract.methods.deposit(weiamount, playerAddress, signature).send({ from: adminAddress });
三、常见问题修复
- 公共RPC节点未知地址错误:公共节点不允许用未授权地址发起交易,必须用管理员自己的钱包签名后发送,要么连接MetaMask,要么服务器端用管理员私钥签名交易(不建议前端存私钥)。
- 去掉.send()无效:
.send()才是发起链上交易的方法,.call()只是模拟执行,不会改变链上状态,必须保留。
内容的提问来源于stack exchange,提问作者Fraddle9
相关产品推荐
相关产品推荐

