如何检测ERC20代币转入合约并兑换游戏能量?
实现ERC20代币自动兑换能量的合约方案
首先明确:ERC20代币的转账不会像ETH那样触发合约的receive()或fallback()函数,因为ERC20转账是调用代币合约的transfer/transferFrom方法,而非直接向合约地址转原生代币。你的示例代码存在两个核心问题:一是sentToken变量未定义,二是没有实际处理代币的转账逻辑,会导致合约未收到代币就给用户增加能量,存在严重漏洞。
下面是最通用的ERC20实现方案:
完整合约代码
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; // 导入ERC20标准接口 interface IERC20 { function transferFrom(address from, address to, uint256 amount) external returns (bool); function balanceOf(address account) external view returns (uint256); } contract GameEnergySystem { // 自研X代币的合约地址 address public immutable X_TOKEN_ADDRESS; // 兑换比例:1000 X = 1 能量 uint256 public constant EXCHANGE_RATIO = 1000; // 记录每个用户累计转入的X代币数量 mapping(address => uint256) public xTokenInfo; // 记录每个用户的能量余额 mapping(address => uint256) public energyBalance; // 构造函数初始化X代币地址 constructor(address _xTokenAddress) { X_TOKEN_ADDRESS = _xTokenAddress; } /** * 用户调用此函数兑换能量 * 前提:用户已调用X代币合约的approve方法,授权本合约转走指定数量的X代币 */ function exchangeEnergy(uint256 _xAmount) external { // 检查转入的代币数量是否符合兑换门槛(至少1000枚) require(_xAmount >= EXCHANGE_RATIO, "X amount too low for energy"); // 从用户地址转X代币到合约 bool success = IERC20(X_TOKEN_ADDRESS).transferFrom(msg.sender, address(this), _xAmount); require(success, "Token transfer failed"); // 更新用户累计转入的X代币数量 xTokenInfo[msg.sender] += _xAmount; // 计算可兑换的能量并更新余额(整数除法,余数忽略,若要保留余数可额外记录) uint256 addedEnergy = _xAmount / EXCHANGE_RATIO; energyBalance[msg.sender] += addedEnergy; } // 可选:查询用户当前可兑换的能量(基于已授权但未兑换的X代币) function getAvailableEnergy(address _user) external view returns (uint256) { uint256 allowedAmount = IERC20(X_TOKEN_ADDRESS).allowance(_user, address(this)); return allowedAmount / EXCHANGE_RATIO; } }
关键逻辑说明
- ERC20授权机制:用户必须先调用X代币合约的
approve(address spender, uint256 amount),授权本合约可以转走指定数量的X代币,否则transferFrom会失败。 - 代币转账验证:通过
transferFrom完成代币的实际转移,并检查返回值确保转账成功,避免出现用户未转币却获得能量的漏洞。 - 数据记录与能量计算:累计用户转入的X代币数量到
xTokenInfo,按比例计算能量并累加(原代码用=会覆盖原有余额,正确写法是+=)。 - 兑换门槛控制:通过
require确保用户转入的X代币至少满足1点能量的兑换要求,避免无效操作。
替代方案(ERC777钩子)
如果你的X代币采用ERC777标准,可以利用tokensReceived钩子实现类似"自动到账兑换"的效果,无需用户主动调用兑换函数:
// 仅展示核心钩子函数,需结合ERC777接口实现 function tokensReceived( address operator, address from, address to, uint256 amount, bytes calldata userData, bytes calldata operatorData ) external { require(msg.sender == X_TOKEN_ADDRESS, "Only X token allowed"); xTokenInfo[from] += amount; energyBalance[from] += amount / EXCHANGE_RATIO; }
这种方案只要用户向合约转ERC777代币,就会自动触发兑换逻辑,但前提是X代币必须实现ERC777标准。
内容的提问来源于stack exchange,提问作者hertac
相关产品推荐
相关产品推荐

