这篇文章从合约代买的角度带大家了解一下 去年土狗风靡一时的彩票模型和自动流动性添加模型。
1、彩票模型:
核心每次交易的部分手续费进入彩票池,当彩票池中金额达到开奖标准,则随机在持有者中抽取一个钱包地址,转入奖金,当然,持有者必须符合持有一定代币数量,否则将本期奖金打入开发者钱包。
找了个之前买的meme币 catgirl 0x79eBC9A2ce02277A4b5b3A768b1C0A4ed75Bd936 来解读一下彩票的代码:
1.1 定义变量
mapping (address => uint256) private _rOwned; // address=> 持币数量 映射
address[] private _addressList; // 存所有地址
address payable private _devWallet; // 开发者钱包
address private _lottoPotAddress; //抽奖地址
address private _lottoWalletAddress; // 抽奖钱包地址
uint256 public _lastLottoWinnerAmount; // 最后赢家奖金金额
uint256 public _totalLottoPrize; // 总奖金金额
uint public _lottoDrawCount = 0; // 计数
uint256 public lotteryThreshold = 10 * 10**12 * 10**9; // 开奖阈值
bool inSwapAndLiquify; // 是否已经进入流动性添加环节标志 默认为false
bool inLotteryDraw; // 是否已经进入开奖环节标志 默认为false
bool public swapAndLiquifyEnabled = true; // 是否开启 自动加入流动性方法 标志
bool public lottoEnabled = true; // 是否开启 彩票抽奖方法 标志
bool public _shouldSwapToBNB = false;
1.2 方法调用
合约 _transfer()方法中出现
function _transfer(
address from,
address to,
uint256 amount
) private {
.......
// 查看 彩票奖池里的代币余额
uint256 lottoBalance = balanceOf(_lottoPotAddress);
// 彩票奖池中余额是否大于开奖阈值
bool overMinLottoBalance = lottoBalance >= lotteryThreshold;
if (
overMinLottoBalance && !inSwapAndLiquify && !inLotteryDraw && lottoEnabled
) {
drawLotto(lottoBalance);
}
......
}
开奖程序是否启动设置函数
function setLottoEnabled(bool enabled) public onlyOwner() {
lottoEnabled = enabled;
}
彩票抽奖方法主方法:
event DrawLotto(uint256 amount, uint _lottoDrawCount);
// 函数修改器 修改进入流程标志
modifier lockTheLottery {
inLotteryDraw = true;
_;
inLotteryDraw = false;
}
function lotterize() private view returns(address) {
uint256 randomNumber = random().mod(_addressList.length);
uint256 ownedAmount = _rOwned[_addressList[randomNumber]];
// 如果随机的address 持有代币数量不足 直接打入开发者钱包
if (ownedAmount >= _minLottoBalance) {
return _addressList[randomNumber];
}
return _devWallet;
}
function drawLotto(uint256 amount) private lockTheLottery {
_lottoWalletAddress = lotterize();
// 此时将amount 转入 中奖者 地址
// 并且由于函数修改器 将inLotteryDraw = true 故再次转账时跳过抽奖环节
_transfer(_lottoPotAddress, _lottoWalletAddress, amount);
_lastLottoWinnerAmount = amount;
// 总发出奖金
_totalLottoPrize = _totalLottoPrize.add(amount);
// 总开奖次数
++_lottoDrawCount;
// 链上上报事件
emit DrawLotto(amount, _lottoDrawCount);
}
2、流动性自动添加模型
流动性自动添加模型就是在每笔交易中收取一部分手续费,将其累计,当到达一定的金额,自动拿出积累的一半token换入 eth 组成[eth, token]交易对,注入流动池。这样会保证池子一直充盈,不会被大户交易吸干。
如果看懂了上面的彩票模型,那么流动性自动添加也是一个套路
event SwapAndLiquify(
uint256 tokensSwapped,
uint256 ethReceived,
uint256 tokensIntoLiqudity
);
function setSwapAndLiquifyEnabled(bool _enabled) public onlyOwner {
swapAndLiquifyEnabled = _enabled;
emit SwapAndLiquifyEnabledUpdated(_enabled);
}
每次调用_transfer()方法,进行一次流动性添加判断
function _transfer(
address from,
address to,
uint256 amount
) private {
....
// 查看 合约地址内的代币余额 每笔交易的手续费中一部分存在address(this) 中作为流动性储备
uint256 contractTokenBalance = balanceOf(address(this));
// 查看 是否触发最大转账金额 默认_maxTxAmount 为代币对外总数量 10000 * 10**12 *10*9
if(contractTokenBalance >= _maxTxAmount)
{
contractTokenBalance = _maxTxAmount;
}
// 持有数量是否 超过转移数量就加入流动性阈值
// numTokensSellToAddToLiquidity 初始为 50 * 10**12 *10**9
bool overMinTokenBalance = contractTokenBalance >= numTokensSellToAddToLiquidity;
if (
overMinTokenBalance && !inSwapAndLiquify && from != uniswapV2Pair && swapAndLiquifyEnabled
) {
contractTokenBalance = numTokensSellToAddToLiquidity;
//add liquidity
// 流动性添加
swapAndLiquify(contractTokenBalance);
}
....
}
进入swapAndLiquify() 方法
// 函数修改器 修改 流动性添加是否在进行的标志
modifier lockTheSwap {
inSwapAndLiquify = true;
_;
inSwapAndLiquify = false;
}
function swapAndLiquify(uint256 contractTokenBalance) private lockTheSwap {
// split the contract balance into halves
// contractTokenBalance 的一半换成 eth
uint256 half = contractTokenBalance.div(2);
uint256 otherHalf = contractTokenBalance.sub(half);
// capture the contract's current ETH balance.
// this is so that we can capture exactly the amount of ETH that the
// swap creates, and not make the liquidity event include any ETH that
// has been manually sent to the contract
// 查看初始时 address(this)中的eth数量
uint256 initialBalance = address(this).balance;
// swap tokens for ETH
// 换eth操作
swapTokensForEth(half); // <- this breaks the ETH -> HATE swap when swap+liquify is triggered
// how much ETH did we just swap into?
// 换到了多少eth
uint256 newBalance = address(this).balance.sub(initialBalance);
// add liquidity to uniswap
// 一半contractTokenBalance 和 一半contractTokenBalance换的eth 添加进流动性池
addLiquidity(otherHalf, newBalance);
// 报链上事件
emit SwapAndLiquify(half, newBalance, otherHalf);
}
function swapTokensForEth(uint256 tokenAmount) private {
// generate the uniswap pair path of token -> weth
address[] memory path = new address[](2);
path[0] = address(this);
path[1] = uniswapV2Router.WETH();
_approve(address(this), address(uniswapV2Router), tokenAmount);
// make the swap
uniswapV2Router.swapExactTokensForETHSupportingFeeOnTransferTokens(
tokenAmount,
0, // accept any amount of ETH
path,
address(this),
block.timestamp
);
}
function addLiquidity(uint256 tokenAmount, uint256 ethAmount) private {
// approve token transfer to cover all possible scenarios
_approve(address(this), address(uniswapV2Router), tokenAmount);
// add the liquidity
uniswapV2Router.addLiquidityETH{value: ethAmount}(
address(this),
tokenAmount,
0, // slippage is unavoidable
0, // slippage is unavoidable
owner(),
block.timestamp
);
}