# meme币的彩票模型和流动性自动添加模型 **Published by:** [berwinYes](https://paragraph.com/@coolberwin/) **Published on:** 2022-07-04 **URL:** https://paragraph.com/@coolberwin/meme ## Content 这篇文章从合约代买的角度带大家了解一下 去年土狗风靡一时的彩票模型和自动流动性添加模型。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 addressUnsupported embed; 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 ); } ## Publication Information - [berwinYes](https://paragraph.com/@coolberwin/): Publication homepage - [All Posts](https://paragraph.com/@coolberwin/): More posts from this publication - [RSS Feed](https://api.paragraph.com/blogs/rss/@coolberwin): Subscribe to updates - [Twitter](https://twitter.com/0xcoolberwin): Follow on Twitter