{"content":{"title":"BMW DAO 套利分析和实现","body":"# BMW DAO 套利分析和实现\r\n\r\n# 1 前言\r\n\r\n其实也不算漏洞，只是简单的自动化了，你手动在网页上操作也是可行的，在项目方的官网私募（私募没有锁仓），然后拿去卖掉。\r\n\r\n本文会逐步分析 BMW [DAO](https:\/\/learnblockchain.cn\/article\/4640) 合约的套利漏洞，并且实现，涉及到的源代码获取方式（**代码已开源**）\r\n\r\n- 确保你已经熟练使用Hardhat，将会用到Hardhat fork主网进行测试。\r\n\r\n- 确保你已经熟练编写Contract和Js，可能会有其他诈骗分子引用文章并修改源码让你受骗（**代码已开源**）。\r\n\r\n- **文章作者没有全部套利完**，如果你看懂了此篇文章，可以自己尝试去利用此漏洞套利（**代码已开源**），有疑问可以关注并私信（**不能从其他途径获取源码，以防受骗**），作者的推特[@TokenKnow](https:\/\/twitter.com\/TokenKnow)。\r\n\r\n# 2 分析官方网站获取合约ABI\r\n\r\n官网地址：[http:\/\/bmwdao.co\/](http:\/\/bmwdao.co\/)\r\n\r\n可以审查元素中发现Confirm按钮的点击事件指向了donateClick()方法，如图：\r\n\r\n![hack03.png](https:\/\/img.learnblockchain.cn\/attachments\/2022\/09\/aQxPeoPD63325d0d4dbbc.png)\r\n\r\n查看donateClick()方法，先是判断了是否连接了钱包，如果连接了钱包就会调用donate()方法，如图：\r\n\r\n![hack04.png](https:\/\/img.learnblockchain.cn\/attachments\/2022\/09\/4op2hQCN63325d26aca7f.png)\r\n\r\n查看donate()方法，先是判断了传入的bnb必须小于等于1，如果小于等于1就会调用合约的donate()方法，如图：\r\n\r\n![hack05.png](https:\/\/img.learnblockchain.cn\/attachments\/2022\/09\/BEmsM8GI63325d34369ea.png)\r\n\r\n我测试过，如果前端调用合约的donate()方法时，手动传入大于1bnb，也会报错，那就证明合约内也有校验。\r\n\r\n到此为止，我们就知道了是调用了合约的donate()方法来进行公开私募（哪怕合约没开源，其他的没开源项目也可以分析他的网站来获取一些有用的东西）。\r\n\r\n# 3 获取合约地址、Pair地址、Router地址\r\n\r\n从官方添加流动性的交易中（0xe1bc05176f96e43986e342b73420a81287539dd2dd536a028b64320618846c4b）可得合约地址、Pair地址、Router地址，如图所示：\r\n\r\n合约地址：0xffd7581fbad3713b049a64db4316e34614970777\r\n\r\nPair地址：0x45139fe54b18a419dc4a2e78ee25ccd7c72c8626\r\n\r\nRouter地址：0x10ed43c718714eb63d5aa57b78b54704e256024e\r\n\r\n# 4 构建Hardhat项目Fork主网测试\r\n\r\n本文重点不是Hardhat的使用，可参考其他Hardhat教程去熟练使用。\r\n\r\n# 5 编写合约\r\n\r\n### 5.1 定义路由接口\r\n\r\n```\r\ninterface IUniswapV2Router {\r\n    function swapExactETHForTokens(\r\n        uint256 amountOutMin, \r\n        address[] calldata path, \r\n        address to, \r\n        uint256 deadline) external;\r\n    function swapExactTokensForETH(\r\n        uint256 amountIn, \r\n        uint256 amountOutMin, \r\n        address[] calldata path, \r\n        address to, \r\n        uint256 deadline) external;\r\n}\r\n```\r\n\r\nswapExactETHForTokens：通过ETH换取代币\r\n\r\nswapExactTokensForETH：通过代币换取ETH（本文主要用到了这个）\r\n\r\n### 5.2 定义交易对接口\r\n\r\n```\r\ninterface IUniswapV2Pair {\r\n    function getReserves() external view returns (uint112 reserve0, uint112 reserve1, uint32 blockTimestampLast);\r\n}\r\n```\r\n\r\ngetReserves：获取交易对的流动性（主要用来计算价格）\r\n\r\n### 5.3 定义BmwDao合约接口\r\n\r\n```\r\ninterface IBmwDaoERC20 {\r\n    function balanceOf(address account) external view returns (uint256);\r\n    function donation() external payable returns(uint);\r\n    function approve(address spender, uint256 value) external returns (bool);\r\n}\r\n```\r\n\r\nbalanceOf：获取代币余额\r\n\r\ndonation：公开私募\r\n\r\napprove：授权（用于卖出代币时的授权）\r\n\r\n### 5.4 定义主合约和主要属性\r\n\r\n```\r\ncontract BmwDaoHack {\r\n\r\n    address private owner = 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266;\r\n\r\n    address private bnb = 0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c;\r\n    address private bmwdao = 0xFfd7581fbad3713b049A64db4316E34614970777;\r\n    address private pair = 0x45139fe54B18a419dc4A2E78eE25Ccd7c72c8626;\r\n    address private router = 0x10ED43C718714eb63d5aA57B78B54704E256024E;\r\n    IUniswapV2Pair private bmwdaoPair = IUniswapV2Pair(pair);\r\n    IUniswapV2Router private bmwdaoRouter = IUniswapV2Router(router);\r\n    IBmwDaoERC20 private bmwdaoERC20 = IBmwDaoERC20(bmwdao);\r\n\r\n}\r\n```\r\n\r\n### 5.5 定义获取公开私募的方法\r\n\r\n```\r\nfunction get() public payable {\r\n    console.log(\"==========msg.value==========\");\r\n    console.log(msg.value);\r\n    require(msg.sender == owner, \"No permission.\");\r\n    bmwdaoERC20.donation{value: msg.value}();\r\n    console.log(\"==========getContractBalance==========\");\r\n    console.log(getContractBalance());\r\n    console.log(\"==========getContractBmwdaoBalance==========\");\r\n    console.log(getContractBmwdaoBalance());\r\n}\r\n```\r\n\r\n### 5.6 定义卖出代币的方法\r\n\r\n```\r\nfunction sell() public {\r\n    bmwdaoERC20.approve(router, 1000000000000);\r\n    uint curTime = block.timestamp;\r\n    curTime = curTime + 1200;\r\n    (uint112 reserve0, uint112 reserve1, uint32 blockTimestampLast) = bmwdaoPair.getReserves();\r\n    uint amountIn = getContractBmwdaoBalance();\r\n    console.log(amountIn);\r\n\r\n    uint left = reserve0 \/ reserve1;\r\n    uint amountOutMin = left * amountIn;\r\n    amountOutMin = amountIn * 997 * reserve0 \/ (reserve1 * 1000 + amountIn * 997);\r\n\r\n    address[] memory path = new address[](2);\r\n    path[0] = bmwdao;\r\n    path[1] = bnb;\r\n\r\n    address to = address(this);\r\n\r\n    bmwdaoRouter.swapExactTokensForETH(amountIn, amountOutMin, path, to, curTime);\r\n    console.log(\"==========succeed==========\");\r\n    console.log(\"==========getContractBalance==========\");\r\n    console.log(getContractBalance());\r\n    console.log(\"==========getContractBmwdaoBalance==========\");\r\n    console.log(getContractBmwdaoBalance());\r\n}\r\n```\r\n\r\n# 6 分开调用的原因\r\n\r\n市场上有很多复制机器人，分步操作可防止复制机器人复制你的交易并提高gas在你之前打包交易上链，套利空间就会减少。\r\n\r\n# 7 使用Hardhat测试合约\r\n\r\n```\r\nconst { expect } = require(\"chai\");\r\nconst { ethers } = require(\"hardhat\");\r\n\r\ndescribe(\"BmwDaoHack\", function() {\r\n    it(\"init params\", async function () {\r\n        [deployer, ...users] = await ethers.getSigners();\r\n    });\r\n    it(\"BmwDaoHack Test\", async function() {\r\n        const BmwDaoHack = await ethers.getContractFactory(\"BmwDaoHack\");\r\n        const bmwDaoHack = await BmwDaoHack.deploy();\r\n        \r\n        await bmwDaoHack.deployed();\r\n\r\n        await bmwDaoHack.connect(deployer).getAirdrop({ value: ethers.utils.parseEther(\"0.01\") });\r\n        await bmwDaoHack.connect(deployer).sell();\r\n    });\r\n});\r\n```\r\n\r\n# 8 执行结果\r\n\r\n用**1BNB**私募了**1000BMW**代币\r\n\r\n因流动池中**1BMW**可换**0.00146513BNB**\r\n\r\n将私募的**1000BMW**换成**1.46513BNB**\r\n\r\n完成套利！！！\r\n\r\n按照官网中[http:\/\/bmwdao.co\/](http:\/\/bmwdao.co\/)所说：每个钱包最多只能私募1BNB的代币。\r\n\r\n可以做个循环用多个钱包来进行套利。\r\n\r\n# 9 后语\r\n\r\n本文的作者对一切行为不负责，只秉承着技术交流分享。\r\n\r\n文章发出前已联系项目方，你在操作上的盈利、亏损等由自己或项目方承担。\r\n\r\n关注推特（[@TokenKnow](https:\/\/twitter.com\/TokenKnow)），交流更多。"},"author":{"user":"https:\/\/learnblockchain.cn\/people\/11769","address":null},"history":"QmZrhP8pqEoWcuoXj1dGsibsdaS5sbQF89NenE6ndGaK1v","timestamp":1664437464,"version":1}