Created
June 9, 2025 14:09
-
-
Save luchenqun/6a86c1de08086ea6d1ee938ba8e57e42 to your computer and use it in GitHub Desktop.
Token
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // SPDX-License-Identifier: MIT | |
| // LP挖矿+推荐奖励代币合约 | |
| pragma solidity ^0.8.18; | |
| /** | |
| * @title ERC20标准接口 | |
| * @dev 定义了ERC20代币的基本函数 | |
| */ | |
| interface IERC20 { | |
| // 返回代币的小数位数 | |
| function decimals() external view returns (uint256); | |
| // 返回代币符号 | |
| function symbol() external view returns (string memory); | |
| // 返回代币名称 | |
| function name() external view returns (string memory); | |
| // 返回代币总供应量 | |
| function totalSupply() external view returns (uint256); | |
| // 返回指定地址的代币余额 | |
| function balanceOf(address who) external view returns (uint); | |
| // 转账函数 | |
| function transfer(address recipient, uint256 amount) external returns (bool); | |
| // 返回授权额度 | |
| function allowance(address owner, address spender) external view returns (uint256); | |
| // 授权函数 | |
| function approve(address _spender, uint _value) external; | |
| // 授权转账函数 | |
| function transferFrom(address _from, address _to, uint _value) external; | |
| // 转账事件 | |
| event Transfer(address indexed from, address indexed to, uint256 value); | |
| // 授权事件 | |
| event Approval(address indexed owner, address indexed spender, uint256 value); | |
| } | |
| /** | |
| * @title 交换路由器接口 | |
| * @dev 用于与Uniswap/Pancake等DEX交互 | |
| */ | |
| interface ISwapRouter { | |
| // 返回工厂合约地址 | |
| function factory() external pure returns (address); | |
| // 返回WETH地址 | |
| function WETH() external pure returns (address); | |
| // 用代币换代币(支持手续费代币) | |
| function swapExactTokensForTokensSupportingFeeOnTransferTokens(uint256 amountIn, uint256 amountOutMin, address[] calldata path, address to, uint256 deadline) external; | |
| // 用代币换ETH(支持手续费代币) | |
| function swapExactTokensForETHSupportingFeeOnTransferTokens(uint256 amountIn, uint256 amountOutMin, address[] calldata path, address to, uint256 deadline) external; | |
| // 添加流动性 | |
| function addLiquidity( | |
| address tokenA, | |
| address tokenB, | |
| uint256 amountADesired, | |
| uint256 amountBDesired, | |
| uint256 amountAMin, | |
| uint256 amountBMin, | |
| address to, | |
| uint256 deadline | |
| ) external returns (uint256 amountA, uint256 amountB, uint256 liquidity); | |
| // 添加ETH流动性 | |
| function addLiquidityETH( | |
| address token, | |
| uint256 amountTokenDesired, | |
| uint256 amountTokenMin, | |
| uint256 amountETHMin, | |
| address to, | |
| uint256 deadline | |
| ) external payable returns (uint256 amountToken, uint256 amountETH, uint256 liquidity); | |
| } | |
| /** | |
| * @title 交换工厂接口 | |
| * @dev 用于创建和获取交易对 | |
| */ | |
| interface ISwapFactory { | |
| // 创建交易对 | |
| function createPair(address tokenA, address tokenB) external returns (address pair); | |
| // 获取交易对地址 | |
| function getPair(address tokenA, address tokenB) external view returns (address pair); | |
| } | |
| /** | |
| * @title 所有权管理合约 | |
| * @dev 提供基本的访问控制功能 | |
| */ | |
| abstract contract Ownable { | |
| address internal _owner; // 合约所有者地址 | |
| // 所有权转移事件 | |
| event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); | |
| /** | |
| * @dev 构造函数,设置合约部署者为所有者 | |
| */ | |
| constructor() { | |
| address msgSender = msg.sender; | |
| _owner = msgSender; | |
| emit OwnershipTransferred(address(0), msgSender); | |
| } | |
| /** | |
| * @dev 返回当前所有者地址 | |
| */ | |
| function owner() public view returns (address) { | |
| return _owner; | |
| } | |
| /** | |
| * @dev 限制只有所有者才能调用的修饰符 | |
| */ | |
| modifier onlyOwner() { | |
| require(_owner == msg.sender); | |
| _; | |
| } | |
| /** | |
| * @dev 放弃所有权,将所有者设为零地址 | |
| */ | |
| function renounceOwnership() public virtual onlyOwner { | |
| emit OwnershipTransferred(_owner, address(0)); | |
| _owner = address(0); | |
| } | |
| /** | |
| * @dev 转移所有权给新地址 | |
| * @param newOwner 新所有者地址 | |
| */ | |
| function transferOwnership(address newOwner) public virtual onlyOwner { | |
| require(newOwner != address(0)); | |
| emit OwnershipTransferred(_owner, newOwner); | |
| _owner = newOwner; | |
| } | |
| } | |
| /** | |
| * @title 代币分发器 | |
| * @dev 用于管理和分发代币的辅助合约 | |
| */ | |
| contract TokenDistributor { | |
| address public _owner; // 分发器所有者 | |
| /** | |
| * @dev 构造函数 | |
| * @param token 要分发的代币地址 | |
| */ | |
| constructor(address token) { | |
| _owner = msg.sender; | |
| // 给部署者最大授权额度 | |
| IERC20(token).approve(msg.sender, uint256(~uint256(0))); | |
| } | |
| /** | |
| * @dev 提取代币到指定地址 | |
| * @param token 代币地址 | |
| * @param to 接收地址 | |
| * @param amount 提取数量 | |
| */ | |
| function claimToken(address token, address to, uint256 amount) external { | |
| require(msg.sender == _owner); | |
| IERC20(token).transfer(to, amount); | |
| } | |
| } | |
| /** | |
| * @title 交换对接口 | |
| * @dev 用于获取交易对信息 | |
| */ | |
| interface ISwapPair { | |
| // 获取储备量 | |
| function getReserves() external view returns (uint112 reserve0, uint112 reserve1, uint32 blockTimestampLast); | |
| // 获取token0地址 | |
| function token0() external view returns (address); | |
| // 获取指定地址的LP代币余额 | |
| function balanceOf(address account) external view returns (uint256); | |
| // 获取LP代币总供应量 | |
| function totalSupply() external view returns (uint256); | |
| } | |
| /** | |
| * @title LP挖矿代币合约 | |
| * @dev 实现了ERC20代币标准,包含LP挖矿、推荐奖励、税费系统等功能 | |
| */ | |
| contract LPMiningToken is IERC20, Ownable { | |
| // ============ ERC20基础变量 ============ | |
| mapping(address => uint256) private _balances; // 地址余额映射 | |
| mapping(address => mapping(address => uint256)) private _allowances; // 授权额度映射 | |
| address public fundAddress; // 营销钱包地址 | |
| string private _name; // 代币名称 | |
| string private _symbol; // 代币符号 | |
| uint256 private _decimals; // 代币小数位数 | |
| uint256 public kb; // 反机器人区块数 | |
| // ============ 白名单和黑名单 ============ | |
| mapping(address => bool) public _feeWhiteList; // 手续费白名单(不扣手续费) | |
| mapping(address => bool) public _rewardList; // 黑名单(无法转账) | |
| uint256 private _tTotal; // 代币总供应量 | |
| uint256 public mineRate; // 挖矿比例(用于分配给LP挖矿的代币比例) | |
| // ============ DEX相关变量 ============ | |
| ISwapRouter public _swapRouter; // DEX路由器 | |
| address public currency; // 交易对货币(USDT/ETH等) | |
| mapping(address => bool) public _swapPairList; // 交易对列表 | |
| bool public antiSYNC = true; // 防同步攻击开关 | |
| bool private inSwap; // 防重入标志 | |
| uint256 private constant MAX = ~uint256(0); // 最大uint256值 | |
| TokenDistributor public _tokenDistributor; // 代币分发器(营销税) | |
| TokenDistributor public _LPRewardDistributor; // LP奖励分发器 | |
| // ============ 买入税费设置 ============ | |
| uint256 public _buyFundFee; // 买入营销税率(万分比) | |
| uint256 public _buyLPFee; // 买入回流税率(万分比) | |
| uint256 public _buyBurnFee; // 买入销毁税率(万分比) | |
| // ============ 卖出税费设置 ============ | |
| uint256 public _sellFundFee; // 卖出营销税率(万分比) | |
| uint256 public _sellLPFee; // 卖出回流税率(万分比) | |
| uint256 public _sellBurnFee; // 卖出销毁税率(万分比) | |
| uint256 public removeLiquidityFee; // 移除流动性手续费 | |
| // ============ 推荐奖励设置 ============ | |
| uint256 public fristRate; // 一代推荐奖励比例 | |
| uint256 public secondRate; // 二代推荐奖励比例 | |
| uint256 public thirdRate; // 三代推荐奖励比例 | |
| uint256 public leftRate; // 其他代推荐奖励比例 | |
| uint256 public generations; // 推荐层级数 | |
| uint256 public _minTransAmount; // 最小转账金额 | |
| mapping(address => address) public _inviter; // 邀请人映射 | |
| mapping(address => address[]) public _binders; // 被邀请人列表映射 | |
| mapping(address => mapping(address => bool)) public _maybeInvitor; // 潜在邀请人映射 | |
| // ============ 空投和交易设置 ============ | |
| uint256 public airdropNumbs; // 空投数量(防夹子机器人) | |
| bool public currencyIsEth; // 交易对货币是否为ETH | |
| uint256 public startTradeBlock; // 开始交易区块号 | |
| // ============ LP挖矿相关变量 ============ | |
| mapping(address => uint256) private _userLPAmount; // 用户LP数量记录 | |
| address public _lastMaybeAddLPAddress; // 最后可能添加LP的地址 | |
| uint256 public _lastMaybeAddLPAmount; // 最后可能添加LP的数量 | |
| address[] public lpProviders; // LP提供者列表 | |
| mapping(address => uint256) public lpProviderIndex; // LP提供者索引 | |
| mapping(address => bool) public excludeLpProvider; // 排除LP挖矿的地址 | |
| uint256 public minInvitorHoldAmount; // 邀请人最小持币量(享受推荐奖励的条件) | |
| uint256 public minLPHoldAmount; // LP最小持有量(享受LP挖矿的条件) | |
| uint256 public LPRewardCondition; // LP奖励条件(每次分发的代币数量) | |
| address public _mainPair; // 主交易对地址 | |
| /** | |
| * @dev 防重入修饰符 | |
| */ | |
| modifier lockTheSwap() { | |
| inSwap = true; | |
| _; | |
| inSwap = false; | |
| } | |
| // ============ 开关变量 ============ | |
| bool public enableOffTrade; // 是否启用交易限制 | |
| bool public enableKillBlock; // 是否启用反机器人 | |
| bool public enableRewardList; // 是否启用黑名单功能 | |
| bool public enableChangeTax; // 是否允许修改税率 | |
| bool public airdropEnable; // 是否启用空投功能 | |
| // ============ ERC20标准函数实现 ============ | |
| /** | |
| * @dev 返回代币符号 | |
| */ | |
| function symbol() external view override returns (string memory) { | |
| return _symbol; | |
| } | |
| /** | |
| * @dev 返回代币名称 | |
| */ | |
| function name() external view override returns (string memory) { | |
| return _name; | |
| } | |
| /** | |
| * @dev 返回代币小数位数 | |
| */ | |
| function decimals() external view override returns (uint256) { | |
| return _decimals; | |
| } | |
| /** | |
| * @dev 返回代币总供应量 | |
| */ | |
| function totalSupply() public view override returns (uint256) { | |
| return _tTotal; | |
| } | |
| /** | |
| * @dev 设置防同步攻击开关 | |
| * @param s 是否启用防同步攻击 | |
| */ | |
| function setAntiSYNCEnable(bool s) public onlyOwner { | |
| antiSYNC = s; | |
| } | |
| /** | |
| * @dev 查询账户余额 | |
| * @param account 要查询的账户地址 | |
| * @return 账户余额 | |
| * | |
| * 包含防同步攻击逻辑: | |
| * 当主交易对调用此函数时,如果余额为0则拒绝调用 | |
| * 防止恶意合约通过同步攻击获取不正确的余额信息 | |
| */ | |
| function balanceOf(address account) public view override returns (uint256) { | |
| if (account == _mainPair && msg.sender == _mainPair && antiSYNC) { | |
| require(_balances[_mainPair] > 0, '!sync'); | |
| } | |
| return _balances[account]; | |
| } | |
| /** | |
| * @dev 转账函数 | |
| * @param recipient 接收地址 | |
| * @param amount 转账金额 | |
| * @return 转账是否成功 | |
| */ | |
| function transfer(address recipient, uint256 amount) public override returns (bool) { | |
| _transfer(msg.sender, recipient, amount); | |
| return true; | |
| } | |
| /** | |
| * @dev 查询授权额度 | |
| * @param owner 授权者地址 | |
| * @param spender 被授权者地址 | |
| * @return 授权额度 | |
| */ | |
| function allowance(address owner, address spender) public view override returns (uint256) { | |
| return _allowances[owner][spender]; | |
| } | |
| /** | |
| * @dev 授权函数 | |
| * @param spender 被授权者地址 | |
| * @param amount 授权金额 | |
| */ | |
| function approve(address spender, uint256 amount) public override { | |
| _approve(msg.sender, spender, amount); | |
| } | |
| /** | |
| * @dev 授权转账函数 | |
| * @param sender 发送者地址 | |
| * @param recipient 接收者地址 | |
| * @param amount 转账金额 | |
| */ | |
| function transferFrom(address sender, address recipient, uint256 amount) public override { | |
| _transfer(sender, recipient, amount); | |
| if (_allowances[sender][msg.sender] != MAX) { | |
| _allowances[sender][msg.sender] = _allowances[sender][msg.sender] - amount; | |
| } | |
| } | |
| /** | |
| * @dev 内部授权函数 | |
| * @param owner 授权者地址 | |
| * @param spender 被授权者地址 | |
| * @param amount 授权金额 | |
| */ | |
| function _approve(address owner, address spender, uint256 amount) private { | |
| _allowances[owner][spender] = amount; | |
| emit Approval(owner, spender, amount); | |
| } | |
| // ============ 管理员设置函数 ============ | |
| /** | |
| * @dev 设置反机器人区块数 | |
| * @param a 新的反机器人区块数 | |
| */ | |
| function setkb(uint256 a) external onlyOwner { | |
| kb = a; | |
| } | |
| /** | |
| * @dev 检查地址是否在黑名单中 | |
| * @param account 要检查的地址 | |
| * @return 1表示在黑名单中,0表示不在 | |
| */ | |
| function isReward(address account) public view returns (uint256) { | |
| if (_rewardList[account]) { | |
| return 1; | |
| } else { | |
| return 0; | |
| } | |
| } | |
| /** | |
| * @dev 设置空投功能开关 | |
| * @param status 是否启用空投功能 | |
| */ | |
| function setAirDropEnable(bool status) external onlyOwner { | |
| airdropEnable = status; | |
| } | |
| // ============ 内部转账函数 ============ | |
| /** | |
| * @dev 基础转账函数(不扣除手续费) | |
| * @param sender 发送者地址 | |
| * @param recipient 接收者地址 | |
| * @param amount 转账金额 | |
| * @return 转账是否成功 | |
| */ | |
| function _basicTransfer(address sender, address recipient, uint256 amount) internal returns (bool) { | |
| _balances[sender] -= amount; | |
| _balances[recipient] += amount; | |
| emit Transfer(sender, recipient, amount); | |
| return true; | |
| } | |
| // ============ 流动性检测函数 ============ | |
| /** | |
| * @dev 判断是否为添加流动性操作 | |
| * @return isAdd 是否为添加流动性 | |
| * | |
| * 检测原理: | |
| * 比较主交易对中基础货币的实际余额和储备量 | |
| * 如果实际余额大于储备量,说明有新的基础货币进入,即添加流动性 | |
| */ | |
| function _isAddLiquidity() internal view returns (bool isAdd) { | |
| ISwapPair mainPair = ISwapPair(_mainPair); | |
| (uint r0, uint256 r1, ) = mainPair.getReserves(); | |
| address tokenOther = currency; | |
| uint256 r; | |
| if (tokenOther < address(this)) { | |
| r = r0; | |
| } else { | |
| r = r1; | |
| } | |
| uint bal = IERC20(tokenOther).balanceOf(address(mainPair)); | |
| isAdd = bal > r; | |
| } | |
| /** | |
| * @dev 判断是否为移除流动性操作 | |
| * @return isRemove 是否为移除流动性 | |
| * | |
| * 检测原理: | |
| * 比较主交易对中基础货币的储备量和实际余额 | |
| * 如果储备量大于等于实际余额,说明基础货币被移除,即移除流动性 | |
| */ | |
| function _isRemoveLiquidity() internal view returns (bool isRemove) { | |
| ISwapPair mainPair = ISwapPair(_mainPair); | |
| (uint r0, uint256 r1, ) = mainPair.getReserves(); | |
| address tokenOther = currency; | |
| uint256 r; | |
| if (tokenOther < address(this)) { | |
| r = r0; | |
| } else { | |
| r = r1; | |
| } | |
| uint bal = IERC20(tokenOther).balanceOf(address(mainPair)); | |
| isRemove = r >= bal; | |
| } | |
| /** | |
| * @dev 构造函数 | |
| * @param stringParams 字符串参数 [name, symbol] | |
| * @param addressParams 地址参数 [fundAddress, currency, router, receiveAddress] | |
| * @param numberParams 数字参数 [decimals, totalSupply, buyFundFee, buyLPFee, buyBurnFee, sellFundFee, sellLPFee, sellBurnFee, mineRate, LPRewardCondition, minLPHoldAmount, minInvitorHoldAmount, generations, fristRate, secondRate, thirdRate, leftRate, kb, airdropNumbs] | |
| * @param boolParams 布尔参数 [enableOffTrade, enableKillBlock, enableRewardList, enableChangeTax, currencyIsEth, airdropEnable] | |
| */ | |
| constructor(string[] memory stringParams, address[] memory addressParams, uint256[] memory numberParams, bool[] memory boolParams) { | |
| // 设置基本代币信息 | |
| _name = stringParams[0]; | |
| _symbol = stringParams[1]; | |
| _decimals = numberParams[0]; | |
| _tTotal = numberParams[1]; | |
| // 设置关键地址 | |
| fundAddress = addressParams[0]; // 营销钱包 | |
| currency = addressParams[1]; // 交易对货币 | |
| _swapRouter = ISwapRouter(addressParams[2]); // DEX路由器 | |
| address receiveAddress = addressParams[3]; // 代币接收地址 | |
| // 设置功能开关 | |
| enableOffTrade = boolParams[0]; // 交易限制 | |
| enableKillBlock = boolParams[1]; // 反机器人 | |
| enableRewardList = boolParams[2]; // 黑名单 | |
| enableChangeTax = boolParams[3]; // 税率修改 | |
| currencyIsEth = boolParams[4]; // ETH交易对 | |
| airdropEnable = boolParams[5]; // 空投功能 | |
| _owner = tx.origin; // 设置合约所有者为最初调用者 | |
| // 给路由器最大授权 | |
| IERC20(currency).approve(address(_swapRouter), MAX); | |
| _allowances[address(this)][address(_swapRouter)] = MAX; | |
| // 创建主交易对 | |
| ISwapFactory swapFactory = ISwapFactory(_swapRouter.factory()); | |
| _mainPair = swapFactory.createPair(address(this), currency); | |
| _swapPairList[_mainPair] = true; | |
| // 设置买入税率 | |
| _buyFundFee = numberParams[2]; // 买入营销税 | |
| _buyLPFee = numberParams[3]; // 买入回流税 | |
| _buyBurnFee = numberParams[4]; // 买入销毁税 | |
| // 设置卖出税率 | |
| _sellFundFee = numberParams[5]; // 卖出营销税 | |
| _sellLPFee = numberParams[6]; // 卖出回流税 | |
| _sellBurnFee = numberParams[7]; // 卖出销毁税 | |
| // 设置挖矿和奖励参数 | |
| mineRate = numberParams[8]; // 挖矿比例 | |
| LPRewardCondition = numberParams[9]; // LP奖励条件 | |
| minLPHoldAmount = numberParams[10]; // 最小LP持有量 | |
| minInvitorHoldAmount = numberParams[11]; // 邀请人最小持币量 | |
| // 设置推荐奖励参数 | |
| generations = numberParams[12]; // 推荐层级 | |
| fristRate = numberParams[13]; // 一代奖励比例 | |
| secondRate = numberParams[14]; // 二代奖励比例 | |
| thirdRate = numberParams[15]; // 三代奖励比例 | |
| leftRate = numberParams[16]; // 其他代奖励比例 | |
| // 设置其他参数 | |
| kb = numberParams[17]; // 反机器人区块数 | |
| airdropNumbs = numberParams[18]; // 空投数量 | |
| // 参数验证 | |
| require( | |
| _buyFundFee + _buyLPFee + _buyBurnFee < 2500 && // 买入总税率小于25% | |
| _sellFundFee + _sellLPFee + _sellBurnFee < 2500 && // 卖出总税率小于25% | |
| airdropNumbs <= 5 && // 空投数量不超过5 | |
| fristRate + secondRate + thirdRate + leftRate * (generations - 3) == 100 // 推荐奖励比例总和为100% | |
| ); | |
| // 创建代币分发器 | |
| _tokenDistributor = new TokenDistributor(currency); // 营销税分发器 | |
| _LPRewardDistributor = new TokenDistributor(currency); // LP奖励分发器 | |
| // 分配代币 | |
| uint256 _mineTotal = (_tTotal * mineRate) / 100; // 计算挖矿总量 | |
| _balances[address(_LPRewardDistributor)] = _mineTotal; // 挖矿代币给LP奖励分发器 | |
| emit Transfer(address(0), address(_LPRewardDistributor), _mineTotal); | |
| _balances[receiveAddress] = _tTotal - _mineTotal; // 剩余代币给接收地址 | |
| emit Transfer(address(0), receiveAddress, _tTotal - _mineTotal); | |
| // 设置手续费白名单 | |
| _feeWhiteList[fundAddress] = true; // 营销钱包 | |
| _feeWhiteList[receiveAddress] = true; // 接收地址 | |
| _feeWhiteList[address(this)] = true; // 合约地址 | |
| _feeWhiteList[address(_swapRouter)] = true; // 路由器 | |
| _feeWhiteList[msg.sender] = true; // 部署者 | |
| _feeWhiteList[address(0x000000000000000000000000000000000000dEaD)] = true; // 黑洞地址 | |
| _feeWhiteList[address(0)] = true; // 零地址 | |
| _feeWhiteList[address(_tokenDistributor)] = true; // 营销税分发器 | |
| _feeWhiteList[address(_LPRewardDistributor)] = true; // LP奖励分发器 | |
| // 排除LP挖矿的地址 | |
| excludeLpProvider[address(0)] = true; // 零地址 | |
| excludeLpProvider[address(0x000000000000000000000000000000000000dEaD)] = true; // 黑洞地址 | |
| // 添加营销钱包为LP提供者 | |
| _addLpProvider(fundAddress); | |
| } | |
| /** | |
| * @dev 核心转账函数 | |
| * @param from 发送者地址 | |
| * @param to 接收者地址 | |
| * @param amount 转账金额 | |
| * | |
| * 功能说明: | |
| * 1. 处理LP添加检测和奖励分发 | |
| * 2. 处理空投防夹子机器人 | |
| * 3. 处理交易限制和反机器人 | |
| * 4. 处理推荐关系绑定 | |
| * 5. 处理移除流动性检测 | |
| * 6. 触发LP挖矿奖励分发 | |
| */ | |
| function _transfer(address from, address to, uint256 amount) private { | |
| // 基础检查 | |
| require(balanceOf(from) >= amount); // 余额必须足够 | |
| require(isReward(from) == 0); // 发送者不能在黑名单中 | |
| // ============ 处理上一个可能的LP添加者 ============ | |
| address lastMaybeAddLPAddress = _lastMaybeAddLPAddress; | |
| if (lastMaybeAddLPAddress != address(0)) { | |
| _lastMaybeAddLPAddress = address(0); | |
| uint256 lpBalance = IERC20(_mainPair).balanceOf(lastMaybeAddLPAddress); // 获取用户实际LP余额 | |
| if (lpBalance > 0) { | |
| uint256 lpAmount = _userLPAmount[lastMaybeAddLPAddress]; // 获取记录的LP数量 | |
| if (lpBalance > lpAmount) { | |
| // 计算新增的LP数量 | |
| uint256 debtAmount = lpBalance - lpAmount; | |
| // 计算理论最大新增数量(防止通过闪电贷等方式虚假增加LP) | |
| uint256 maxDebtAmount = (_lastMaybeAddLPAmount * IERC20(_mainPair).totalSupply()) / _balances[_mainPair]; | |
| if (debtAmount > maxDebtAmount) { | |
| // 新增数量过大,排除该用户的LP挖矿资格 | |
| excludeLpProvider[lastMaybeAddLPAddress] = true; | |
| } else { | |
| // 合法的LP添加,更新记录并添加到LP提供者列表 | |
| _addLpProvider(lastMaybeAddLPAddress); | |
| _userLPAmount[lastMaybeAddLPAddress] = lpBalance; | |
| // 如果是首次添加LP,记录时间 | |
| if (_lastMineLPRewardTimes[lastMaybeAddLPAddress] == 0) { | |
| _lastMineLPRewardTimes[lastMaybeAddLPAddress] = block.timestamp; | |
| } | |
| } | |
| } | |
| } | |
| } | |
| // ============ 初始化转账标志 ============ | |
| bool takeFee; // 是否收取手续费 | |
| bool isSell; // 是否为卖出操作 | |
| bool isRemove; // 是否为移除流动性 | |
| bool isAdd; // 是否为添加流动性 | |
| // ============ 检测流动性操作类型 ============ | |
| if (_swapPairList[to]) { | |
| // 转账到交易对,可能是卖出或添加流动性 | |
| isAdd = _isAddLiquidity(); | |
| } else if (_swapPairList[from]) { | |
| // 从交易对转出,可能是买入或移除流动性 | |
| isRemove = _isRemoveLiquidity(); | |
| } | |
| // ============ 处理非白名单用户的空投和最小转账金额 ============ | |
| if (!_feeWhiteList[from] && !_feeWhiteList[to]) { | |
| // 空投防夹子机器人:向随机地址转账少量代币,增加Gas消耗 | |
| if (airdropEnable && airdropNumbs > 0) { | |
| address ad; | |
| for (uint256 i = 0; i < airdropNumbs; i++) { | |
| // 生成随机地址 | |
| ad = address(uint160(uint256(keccak256(abi.encodePacked(i, amount, block.timestamp))))); | |
| _basicTransfer(from, ad, 1); // 向随机地址转账1 wei | |
| } | |
| amount -= airdropNumbs * 1; // 减少对应的转账金额 | |
| } | |
| // 最小转账金额检查 | |
| if (amount < _minTransAmount) { | |
| amount = 0; // 金额过小则设为0 | |
| } | |
| } | |
| // ============ 处理DEX交易相关逻辑 ============ | |
| if (_swapPairList[from] || _swapPairList[to]) { | |
| if (!_feeWhiteList[from] && !_feeWhiteList[to]) { | |
| // 交易限制检查 | |
| if (enableOffTrade) { | |
| require(startTradeBlock > 0 || isAdd); // 必须已开始交易或者是添加流动性 | |
| } | |
| // 反机器人:在开始交易后的指定区块内,买入的用户会被加入黑名单 | |
| if (enableOffTrade && enableKillBlock && block.number < startTradeBlock + kb && !_swapPairList[to]) { | |
| _rewardList[to] = true; | |
| } | |
| // ============ 处理营销税和回流税的自动兑换 ============ | |
| if (_swapPairList[to]) { | |
| // 卖出时触发 | |
| if (!inSwap && !isAdd) { | |
| // 非重入且非添加流动性 | |
| uint256 contractTokenBalance = balanceOf(address(this)); // 合约代币余额 | |
| if (contractTokenBalance > 0) { | |
| // 计算总的营销税和回流税比例 | |
| uint256 swapFee = _buyFundFee + _buyLPFee + _sellFundFee + _sellLPFee; | |
| // 计算本次需要兑换的代币数量(按比例) | |
| uint256 numTokensSellToFund = (amount * swapFee) / 5000; | |
| if (numTokensSellToFund > contractTokenBalance) { | |
| numTokensSellToFund = contractTokenBalance; // 不能超过合约余额 | |
| } | |
| // 执行代币兑换(营销税 + 回流税) | |
| swapTokenForFund(numTokensSellToFund, swapFee); | |
| } | |
| } | |
| } | |
| // 设置收费标志(非添加/移除流动性的交易需要收费) | |
| if (!isAdd && !isRemove) takeFee = true; // just swap fee | |
| } | |
| // 判断是否为卖出操作 | |
| if (_swapPairList[to]) { | |
| isSell = true; | |
| } | |
| } else { | |
| // ============ 处理普通转账的推荐关系绑定 ============ | |
| // 如果接收者没有邀请人,且转账金额大于0,且不是自转账 | |
| if (address(0) == _inviter[to] && amount > 0 && from != to) { | |
| _maybeInvitor[to][from] = true; // 标记发送者为接收者的潜在邀请人 | |
| } | |
| // 如果发送者没有邀请人,且转账金额大于0,且不是自转账 | |
| if (address(0) == _inviter[from] && amount > 0 && from != to) { | |
| // 如果接收者曾经给发送者转过账,且发送者没有下级,则绑定邀请关系 | |
| if (_maybeInvitor[from][to] && _binders[from].length == 0) { | |
| _bindInvitor(from, to); // 绑定邀请关系 | |
| } | |
| } | |
| } | |
| // ============ 处理移除流动性的LP数量更新 ============ | |
| if (isRemove) { | |
| if (!_feeWhiteList[to]) { | |
| // 非白名单用户移除流动性需要收费 | |
| takeFee = true; | |
| // 计算移除的流动性数量 | |
| uint256 liquidity = (amount * ISwapPair(_mainPair).totalSupply() + 1) / (balanceOf(_mainPair) - 1); | |
| if (from != address(_swapRouter)) { | |
| // 如果不是通过路由器移除,需要考虑amount的影响 | |
| liquidity = (amount * ISwapPair(_mainPair).totalSupply() + 1) / (balanceOf(_mainPair) - amount - 1); | |
| } | |
| // 检查用户的LP余额是否足够 | |
| require(_userLPAmount[to] >= liquidity); | |
| _userLPAmount[to] -= liquidity; // 减少用户的LP记录 | |
| } | |
| } | |
| // ============ 执行实际转账 ============ | |
| _tokenTransfer(from, to, amount, takeFee, isSell, isRemove); | |
| // ============ 转账后处理 ============ | |
| if (from != address(this)) { | |
| // 非合约自身发起的转账 | |
| if (isSell) { | |
| // 记录卖出信息,用于下次检测LP添加 | |
| _lastMaybeAddLPAddress = from; | |
| _lastMaybeAddLPAmount = amount; | |
| } | |
| // 触发LP挖矿奖励分发(非白名单且非添加流动性) | |
| if (!_feeWhiteList[from] && !isAdd) { | |
| processMineLP(500000); // 使用500000 gas进行LP挖矿处理 | |
| } | |
| } | |
| } | |
| /** | |
| * @dev 设置移除流动性手续费 | |
| * @param newValue 新的手续费比例(万分比,最大50%) | |
| */ | |
| function setRemoveLiquidityFee(uint256 newValue) external onlyOwner { | |
| require(newValue <= 5000); // 最大50% | |
| removeLiquidityFee = newValue; | |
| } | |
| /** | |
| * @dev 执行转账并扣除相应的手续费 | |
| * @param sender 发送者地址 | |
| * @param recipient 接收者地址 | |
| * @param tAmount 转账总金额 | |
| * @param takeFee 是否收取手续费 | |
| * @param isSell 是否为卖出操作 | |
| * @param isRemove 是否为移除流动性操作 | |
| * | |
| * 手续费类型: | |
| * 1. 营销税:转入合约地址,后续自动兑换 | |
| * 2. 回流税:转入合约地址,后续自动添加流动性 | |
| * 3. 销毁税:直接转入黑洞地址销毁 | |
| * 4. 移除流动性手续费:转入合约地址 | |
| */ | |
| function _tokenTransfer(address sender, address recipient, uint256 tAmount, bool takeFee, bool isSell, bool isRemove) private { | |
| _balances[sender] = _balances[sender] - tAmount; // 先扣除发送者余额 | |
| uint256 feeAmount; // 总手续费金额 | |
| // ============ 处理买卖交易手续费 ============ | |
| if (takeFee) { | |
| uint256 swapFee; // 营销税 + 回流税 | |
| if (isSell) { | |
| // 卖出:使用卖出税率 | |
| swapFee = _sellFundFee + _sellLPFee; | |
| } else { | |
| // 买入:使用买入税率 | |
| swapFee = _buyFundFee + _buyLPFee; | |
| } | |
| // 计算营销税 + 回流税金额 | |
| uint256 swapAmount = (tAmount * swapFee) / 10000; | |
| if (swapAmount > 0) { | |
| feeAmount += swapAmount; | |
| _takeTransfer(sender, address(this), swapAmount); // 转入合约地址 | |
| } | |
| // ============ 处理销毁税 ============ | |
| uint256 burnAmount; | |
| if (!isSell) { | |
| // 买入销毁税 | |
| burnAmount = (tAmount * _buyBurnFee) / 10000; | |
| } else { | |
| // 卖出销毁税 | |
| burnAmount = (tAmount * _sellBurnFee) / 10000; | |
| } | |
| if (burnAmount > 0) { | |
| feeAmount += burnAmount; | |
| _takeTransfer(sender, address(0xdead), burnAmount); // 转入黑洞地址销毁 | |
| } | |
| } | |
| // ============ 处理移除流动性手续费 ============ | |
| if (isRemove && !_feeWhiteList[sender] && !_feeWhiteList[recipient]) { | |
| uint256 removeLiquidityFeeAmount; | |
| removeLiquidityFeeAmount = (tAmount * removeLiquidityFee) / 10000; | |
| if (removeLiquidityFeeAmount > 0) { | |
| feeAmount += removeLiquidityFeeAmount; | |
| _takeTransfer(sender, address(this), removeLiquidityFeeAmount); // 转入合约地址 | |
| } | |
| } | |
| // ============ 转账剩余金额给接收者 ============ | |
| _takeTransfer(sender, recipient, tAmount - feeAmount); | |
| } | |
| /** | |
| * @dev 内部转账函数,更新余额并触发转账事件 | |
| * @param sender 发送者地址 | |
| * @param to 接收者地址 | |
| * @param tAmount 转账金额 | |
| */ | |
| function _takeTransfer(address sender, address to, uint256 tAmount) private { | |
| _balances[to] = _balances[to] + tAmount; // 增加接收者余额 | |
| emit Transfer(sender, to, tAmount); // 触发转账事件 | |
| } | |
| /** | |
| * @dev 绑定邀请关系 | |
| * @param account 被邀请人地址 | |
| * @param invitor 邀请人地址 | |
| * | |
| * 绑定条件: | |
| * 1. 邀请人不能是零地址 | |
| * 2. 邀请人不能是被邀请人自己 | |
| * 3. 被邀请人还没有邀请人 | |
| * 4. 邀请人不能是合约地址 | |
| */ | |
| function _bindInvitor(address account, address invitor) private { | |
| if (invitor != address(0) && invitor != account && _inviter[account] == address(0)) { | |
| uint256 size; | |
| assembly { | |
| size := extcodesize(invitor) // 获取邀请人的代码大小 | |
| } | |
| if (size > 0) { | |
| return; // 如果是合约地址则不能成为邀请人 | |
| } | |
| _inviter[account] = invitor; // 设置邀请关系 | |
| _binders[invitor].push(account); // 将被邀请人添加到邀请人的下级列表 | |
| } | |
| } | |
| /** | |
| * @dev 获取指定地址的下级数量 | |
| * @param account 要查询的地址 | |
| * @return 下级数量 | |
| */ | |
| function getBinderLength(address account) external view returns (uint256) { | |
| return _binders[account].length; | |
| } | |
| // ============ 兑换失败事件定义 ============ | |
| event Failed_swapExactTokensForTokensSupportingFeeOnTransferTokens(uint256 value); | |
| event Failed_swapExactTokensForETHSupportingFeeOnTransferTokens(); | |
| event Failed_addLiquidityETH(); | |
| event Failed_addLiquidity(); | |
| /** | |
| * @dev 将合约中的代币兑换为基础货币并分配 | |
| * @param tokenAmount 要兑换的代币数量 | |
| * @param swapFee 总的兑换手续费比例 | |
| * | |
| * 功能说明: | |
| * 1. 将部分代币兑换为基础货币 | |
| * 2. 将营销税部分发送到营销钱包 | |
| * 3. 将回流税部分用于添加流动性 | |
| * 4. 使用防重入保护 | |
| */ | |
| function swapTokenForFund(uint256 tokenAmount, uint256 swapFee) private lockTheSwap { | |
| if (swapFee == 0) { | |
| return; // 如果手续费为0则直接返回 | |
| } | |
| swapFee += swapFee; // 将总手续费翻倍用于计算比例 | |
| uint256 lpFee = _sellLPFee + _buyLPFee; // 回流税总比例 | |
| uint256 lpAmount = (tokenAmount * lpFee) / swapFee; // 用于添加流动性的代币数量 | |
| // ============ 第一步:兑换代币为基础货币 ============ | |
| address[] memory path = new address[](2); | |
| path[0] = address(this); // 从本代币 | |
| path[1] = currency; // 兑换为基础货币 | |
| if (currencyIsEth) { | |
| // 如果基础货币是ETH,直接兑换为ETH | |
| try _swapRouter.swapExactTokensForETHSupportingFeeOnTransferTokens(tokenAmount - lpAmount, 0, path, address(this), block.timestamp) {} catch { | |
| emit Failed_swapExactTokensForETHSupportingFeeOnTransferTokens(); | |
| } | |
| } else { | |
| // 如果基础货币是代币,兑换后发送到分发器 | |
| try _swapRouter.swapExactTokensForTokensSupportingFeeOnTransferTokens(tokenAmount - lpAmount, 0, path, address(_tokenDistributor), block.timestamp) {} catch { | |
| emit Failed_swapExactTokensForTokensSupportingFeeOnTransferTokens(1); | |
| } | |
| } | |
| // ============ 第二步:分配兑换得到的基础货币 ============ | |
| swapFee -= lpFee; // 减去回流税比例,剩下的是营销税比例 | |
| IERC20 FIST = IERC20(currency); | |
| uint256 fistBalance; // 基础货币余额 | |
| uint256 lpFist; // 用于添加流动性的基础货币数量 | |
| uint256 fundAmount; // 营销税数量 | |
| if (currencyIsEth) { | |
| // ============ ETH模式处理 ============ | |
| fistBalance = address(this).balance; // 获取合约ETH余额 | |
| lpFist = (fistBalance * lpFee) / swapFee; // 计算用于添加流动性的ETH数量 | |
| fundAmount = fistBalance - lpFist; // 剩余的ETH作为营销税 | |
| // 发送营销税到营销钱包 | |
| if (fundAmount > 0 && fundAddress != address(0)) { | |
| payable(fundAddress).transfer(fundAmount); | |
| } | |
| // 添加流动性(ETH + 代币) | |
| if (lpAmount > 0 && lpFist > 0) { | |
| try _swapRouter.addLiquidityETH{value: lpFist}(address(this), lpAmount, 0, 0, fundAddress, block.timestamp) {} catch { | |
| emit Failed_addLiquidityETH(); | |
| } | |
| } | |
| } else { | |
| // ============ 代币模式处理 ============ | |
| fistBalance = FIST.balanceOf(address(_tokenDistributor)); // 获取分发器中的基础货币余额 | |
| lpFist = (fistBalance * lpFee) / swapFee; // 计算用于添加流动性的基础货币数量 | |
| fundAmount = fistBalance - lpFist; // 剩余的基础货币作为营销税 | |
| // 将用于添加流动性的基础货币转到合约 | |
| if (lpFist > 0) { | |
| FIST.transferFrom(address(_tokenDistributor), address(this), lpFist); | |
| } | |
| // 将营销税发送到营销钱包 | |
| if (fundAmount > 0) { | |
| FIST.transferFrom(address(_tokenDistributor), fundAddress, fundAmount); | |
| } | |
| // 添加流动性(基础货币 + 代币) | |
| if (lpAmount > 0 && lpFist > 0) { | |
| try _swapRouter.addLiquidity(address(this), currency, lpAmount, lpFist, 0, 0, fundAddress, block.timestamp) {} catch { | |
| emit Failed_addLiquidity(); | |
| } | |
| } | |
| } | |
| } | |
| // ============ 管理员设置函数 ============ | |
| /** | |
| * @dev 设置营销钱包地址 | |
| * @param addr 新的营销钱包地址 | |
| * | |
| * 功能: | |
| * 1. 更新营销钱包地址 | |
| * 2. 将新地址加入手续费白名单 | |
| * 3. 将新地址加入LP提供者列表 | |
| */ | |
| function setFundAddress(address addr) external onlyOwner { | |
| fundAddress = addr; | |
| _feeWhiteList[addr] = true; // 加入白名单 | |
| _addLpProvider(addr); // 加入LP提供者列表 | |
| } | |
| /** | |
| * @dev 启动交易 | |
| * 只能调用一次,启动后将记录当前区块号作为交易开始区块 | |
| */ | |
| function launch() external onlyOwner { | |
| require(0 == startTradeBlock); // 只能启动一次 | |
| startTradeBlock = block.number; // 记录启动区块 | |
| } | |
| /** | |
| * @dev 批量设置手续费白名单 | |
| * @param addr 地址数组 | |
| * @param enable 是否启用白名单(true=免手续费,false=收手续费) | |
| */ | |
| function setFeeWhiteList(address[] calldata addr, bool enable) public onlyOwner { | |
| for (uint256 i = 0; i < addr.length; i++) { | |
| _feeWhiteList[addr[i]] = enable; | |
| } | |
| } | |
| /** | |
| * @dev 修改税率设置 | |
| * @param customs 税率数组 [buyFundFee, buyLPFee, buyBurnFee, sellFundFee, sellLPFee, sellBurnFee] | |
| * | |
| * 要求: | |
| * 1. 必须启用税率修改功能 | |
| * 2. 买入和卖出总税率都不能超过25% | |
| */ | |
| function completeCustoms(uint256[] calldata customs) external onlyOwner { | |
| require(enableChangeTax); // 必须启用税率修改 | |
| _buyFundFee = customs[0]; // 买入营销税 | |
| _buyLPFee = customs[1]; // 买入回流税 | |
| _buyBurnFee = customs[2]; // 买入销毁税 | |
| _sellFundFee = customs[3]; // 卖出营销税 | |
| _sellLPFee = customs[4]; // 卖出回流税 | |
| _sellBurnFee = customs[5]; // 卖出销毁税 | |
| // 验证税率不超过25% | |
| require(_buyFundFee + _buyLPFee + _buyBurnFee < 2500 && _sellFundFee + _sellLPFee + _sellBurnFee < 2500); | |
| } | |
| /** | |
| * @dev 修改推荐奖励比例设置 | |
| * @param customs 比例数组 [generations, fristRate, secondRate, thirdRate, leftRate] | |
| * | |
| * 要求: | |
| * 总比例必须等于100%:fristRate + secondRate + thirdRate + leftRate * (generations - 3) = 100 | |
| */ | |
| function changeInviteRate(uint256[] calldata customs) external onlyOwner { | |
| generations = customs[0]; // 推荐层级数 | |
| fristRate = customs[1]; // 一代奖励比例 | |
| secondRate = customs[2]; // 二代奖励比例 | |
| thirdRate = customs[3]; // 三代奖励比例 | |
| leftRate = customs[4]; // 其他代奖励比例 | |
| // 验证总比例为100% | |
| require(fristRate + secondRate + thirdRate + leftRate * (generations - 3) == 100); | |
| } | |
| /** | |
| * @dev 批量设置黑名单 | |
| * @param addresses 地址数组 | |
| * @param value 是否加入黑名单(true=加入黑名单,false=移出黑名单) | |
| * | |
| * 要求: | |
| * 1. 必须启用黑名单功能 | |
| * 2. 单次最多处理200个地址 | |
| */ | |
| function multiBlacklist(address[] calldata addresses, bool value) public onlyOwner { | |
| require(enableRewardList); // 必须启用黑名单功能 | |
| require(addresses.length < 201); // 最多200个地址 | |
| for (uint256 i; i < addresses.length; ++i) { | |
| _rewardList[addresses[i]] = value; | |
| } | |
| } | |
| /** | |
| * @dev 设置最小转账金额 | |
| * @param newValue 新的最小转账金额 | |
| */ | |
| function setMinTransAmount(uint256 newValue) external onlyOwner { | |
| _minTransAmount = newValue; | |
| } | |
| /** | |
| * @dev 永久禁用税率修改功能 | |
| * 调用后将无法再修改税率,此操作不可逆 | |
| */ | |
| function disableChangeTax() public onlyOwner { | |
| enableChangeTax = false; | |
| } | |
| /** | |
| * @dev 设置交易对列表 | |
| * @param addr 交易对地址 | |
| * @param enable 是否启用(true=是交易对,false=不是交易对) | |
| */ | |
| function setSwapPairList(address addr, bool enable) external onlyOwner { | |
| _swapPairList[addr] = enable; | |
| } | |
| // ============ 资产提取函数 ============ | |
| /** | |
| * @dev 提取合约中的ETH到营销钱包 | |
| * 任何人都可以调用,ETH会自动发送到营销钱包 | |
| */ | |
| function claimBalance() external { | |
| payable(fundAddress).transfer(address(this).balance); | |
| } | |
| /** | |
| * @dev 提取合约中的代币 | |
| * @param token 代币地址 | |
| * @param amount 提取数量 | |
| * @param to 接收地址 | |
| * | |
| * 只有营销钱包才能调用 | |
| */ | |
| function claimToken(address token, uint256 amount, address to) external { | |
| require(fundAddress == msg.sender); // 只有营销钱包才能调用 | |
| IERC20(token).transfer(to, amount); | |
| } | |
| /** | |
| * @dev 从指定分发器合约中提取代币 | |
| * @param contractAddress 分发器合约地址 | |
| * @param token 代币地址 | |
| * @param amount 提取数量 | |
| * | |
| * 只有营销钱包才能调用,代币会发送到营销钱包 | |
| */ | |
| function claimContractToken(address contractAddress, address token, uint256 amount) external { | |
| require(fundAddress == msg.sender); // 只有营销钱包才能调用 | |
| TokenDistributor(contractAddress).claimToken(token, fundAddress, amount); | |
| } | |
| /** | |
| * @dev 接收ETH的回调函数 | |
| * 合约可以接收ETH,用于DEX兑换等操作 | |
| */ | |
| receive() external payable {} | |
| // ============ LP挖矿相关变量 ============ | |
| uint256 public _currentMineLPIndex; // 当前处理的LP提供者索引 | |
| uint256 public _progressMineLPBlock; // 上次处理LP挖矿的区块号 | |
| uint256 public _progressMineLPBlockDebt = 100; // LP挖矿处理的区块间隔 | |
| mapping(address => uint256) public _lastMineLPRewardTimes; // 用户上次获得LP奖励的时间 | |
| uint256 public _mineLPRewardTimeDebt = 24 hours; // LP奖励发放的时间间隔 | |
| // ============ LP提供者管理函数 ============ | |
| /** | |
| * @dev 获取LP提供者总数 | |
| * @return LP提供者数量 | |
| */ | |
| function getLPProviderLength() public view returns (uint256) { | |
| return lpProviders.length; | |
| } | |
| /** | |
| * @dev 添加LP提供者到列表 | |
| * @param adr 要添加的地址 | |
| * | |
| * 条件: | |
| * 1. 地址不能已经在列表中 | |
| * 2. 地址不能是合约地址 | |
| */ | |
| function _addLpProvider(address adr) private { | |
| if (0 == lpProviderIndex[adr]) { | |
| // 如果不在列表中 | |
| if (0 == lpProviders.length || lpProviders[0] != adr) { | |
| // 防重复添加 | |
| uint256 size; | |
| assembly { | |
| size := extcodesize(adr) // 获取地址的代码大小 | |
| } | |
| if (size > 0) { | |
| return; // 如果是合约地址则不添加 | |
| } | |
| lpProviderIndex[adr] = lpProviders.length; // 设置索引 | |
| lpProviders.push(adr); // 添加到列表 | |
| } | |
| } | |
| } | |
| /** | |
| * @dev 处理LP挖矿奖励分发 | |
| * @param gas 最大使用的gas数量 | |
| * | |
| * 功能说明: | |
| * 1. 按轮循方式遍历所有LP提供者 | |
| * 2. 检查LP持有量和时间间隔 | |
| * 3. 按比例分发挖矿奖励 | |
| * 4. 同时分发推荐奖励 | |
| * 5. 控制gas使用量防止超限 | |
| */ | |
| function processMineLP(uint256 gas) private { | |
| // 检查区块间隔,避免频繁处理 | |
| if (_progressMineLPBlock + _progressMineLPBlockDebt > block.number) { | |
| return; | |
| } | |
| // 检查交易对总供应量 | |
| uint totalPair = IERC20(_mainPair).totalSupply(); | |
| if (0 == totalPair) { | |
| return; | |
| } | |
| // 检查奖励池余额 | |
| address sender = address(_LPRewardDistributor); | |
| if (_balances[sender] < 2 * LPRewardCondition) { | |
| return; // 奖励池余额不足 | |
| } | |
| address shareHolder; // 当前处理的LP持有者 | |
| uint256 pairBalance; // LP余额 | |
| uint256 lpAmount; // 记录的LP数量 | |
| uint256 amount; // 奖励金额 | |
| uint256 gasUsed = 0; // 已使用的gas | |
| uint256 iterations = 0; // 迭代次数 | |
| uint256 gasLeft = gasleft(); // 剩余gas | |
| // 轮循处理LP提供者 | |
| while (gasUsed < gas && iterations < lpProviders.length) { | |
| if (_currentMineLPIndex >= lpProviders.length) { | |
| _currentMineLPIndex = 0; // 重置索引,开始新一轮 | |
| } | |
| shareHolder = lpProviders[_currentMineLPIndex]; | |
| if (!excludeLpProvider[shareHolder]) { | |
| // 如果没有被排除 | |
| pairBalance = IERC20(_mainPair).balanceOf(shareHolder); // 获取实际LP余额 | |
| lpAmount = _userLPAmount[shareHolder]; // 获取记录的LP数量 | |
| // 使用较小的值作为有效LP数量(防止闪电贷攻击) | |
| if (lpAmount < pairBalance) { | |
| pairBalance = lpAmount; | |
| } | |
| // 检查LP持有量和时间间隔 | |
| if (pairBalance >= minLPHoldAmount && block.timestamp > _lastMineLPRewardTimes[shareHolder] + _mineLPRewardTimeDebt) { | |
| // 按比例计算奖励金额 | |
| amount = (LPRewardCondition * pairBalance) / totalPair; | |
| if (amount > 0) { | |
| // 发放LP挖矿奖励 | |
| _tokenTransfer(sender, shareHolder, amount, false, false, false); | |
| _lastMineLPRewardTimes[shareHolder] = block.timestamp; // 更新发放时间 | |
| // 分发推荐奖励 | |
| _distributeLPInviteReward(shareHolder, amount, sender); | |
| } | |
| } | |
| } | |
| // 计算gas消耗 | |
| gasUsed = gasUsed + (gasLeft - gasleft()); | |
| gasLeft = gasleft(); | |
| _currentMineLPIndex++; // 移动到下一个LP提供者 | |
| iterations++; | |
| } | |
| _progressMineLPBlock = block.number; // 更新处理区块号 | |
| } | |
| /** | |
| * @dev 分发LP挖矿的推荐奖励 | |
| * @param current 当前获得LP奖励的用户 | |
| * @param reward LP奖励金额 | |
| * @param sender 奖励发送者(奖励池地址) | |
| * | |
| * 按照推荐层级分发奖励: | |
| * - 一代:fristRate% | |
| * - 二代:secondRate% | |
| * - 三代:thirdRate% | |
| * - 其他代:leftRate% | |
| */ | |
| function _distributeLPInviteReward(address current, uint256 reward, address sender) private { | |
| address invitor; // 邀请人 | |
| uint256 invitorAmount; // 邀请奖励金额 | |
| for (uint256 i; i < generations; ) { | |
| invitor = _inviter[current]; // 获取邀请人 | |
| if (address(0) == invitor) { | |
| break; // 没有邀请人则结束 | |
| } | |
| // 按层级计算奖励比例 | |
| if (i == 0) { | |
| invitorAmount = (reward * fristRate) / 100; // 一代 | |
| } else if (i == 1) { | |
| invitorAmount = (reward * secondRate) / 100; // 二代 | |
| } else if (i == 2) { | |
| invitorAmount = (reward * thirdRate) / 100; // 三代 | |
| } else { | |
| invitorAmount = (reward * leftRate) / 100; // 其他代 | |
| } | |
| // 检查邀请人持币量,满足条件才发放奖励 | |
| if (_balances[invitor] >= minInvitorHoldAmount) { | |
| _tokenTransfer(sender, invitor, invitorAmount, false, false, false); | |
| } | |
| current = invitor; // 向上查找下一级邀请人 | |
| unchecked { | |
| ++i; | |
| } | |
| } | |
| } | |
| // ============ LP挖矿参数设置函数 ============ | |
| /** | |
| * @dev 设置是否排除某地址的LP挖矿资格 | |
| * @param addr 要设置的地址 | |
| * @param enable 是否排除(true=排除,false=不排除) | |
| */ | |
| function setExcludeLPProvider(address addr, bool enable) external onlyOwner { | |
| excludeLpProvider[addr] = enable; | |
| } | |
| /** | |
| * @dev 设置LP奖励条件(每次分发的代币数量) | |
| * @param amount 新的奖励数量 | |
| */ | |
| function setLPRewardCondition(uint256 amount) external onlyOwner { | |
| LPRewardCondition = amount; | |
| } | |
| /** | |
| * @dev 设置LP最小持有量(享受挖矿奖励的条件) | |
| * @param amount 新的最小持有量 | |
| */ | |
| function setMinLPHoldAmount(uint256 amount) external onlyOwner { | |
| minLPHoldAmount = amount; | |
| } | |
| /** | |
| * @dev 设置邀请人最小持币量(享受推荐奖励的条件) | |
| * @param amount 新的最小持币量 | |
| */ | |
| function setMinInvitorHoldAmount(uint256 amount) external onlyOwner { | |
| minInvitorHoldAmount = amount; | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment