Skip to content

Instantly share code, notes, and snippets.

@luchenqun
Created June 9, 2025 14:09
Show Gist options
  • Select an option

  • Save luchenqun/6a86c1de08086ea6d1ee938ba8e57e42 to your computer and use it in GitHub Desktop.

Select an option

Save luchenqun/6a86c1de08086ea6d1ee938ba8e57e42 to your computer and use it in GitHub Desktop.
Token
// 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