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

作为合约管理员,如何代用户执行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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.22 05:37:44