{"content":{"title":"深入浅出的分析一下dodo闪电贷","body":"最近两三年，闪电贷比较流行，也有不少的分析文章。最近深入的研究了一下dodo闪电贷，分享给大家。\r\n话不多说，先上源码。\r\n\r\n```js\r\n// This is a file copied from https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/IERC20.sol\r\n// SPDX-License-Identifier: MIT\r\npragma solidity 0.6.12;\r\npragma experimental ABIEncoderV2;\r\n//导入IERC20接口，这个就不用讲解了。\r\ninterface IERC20 {\r\n    function totalSupply() external view returns (uint256);\r\n    function decimals() external view returns (uint8);\r\n    function name() external view returns (string memory);\r\n    function symbol() external view returns (string memory);\r\n    function balanceOf(address account) external view returns (uint256);\r\n    function transfer(address recipient, uint256 amount) external returns (bool);\r\n    function allowance(address owner, address spender) external view returns (uint256);\r\n    function approve(address spender, uint256 amount) external returns (bool);\r\n    function transferFrom(\r\n        address sender,\r\n        address recipient,\r\n        uint256 amount\r\n    ) external returns (bool);\r\n}\r\n//这里导入币安的路由接口，方便举例子。\r\ninterface IPancakeRouter01 {\r\n    function factory() external pure returns (address);\r\n    function WETH() external pure returns (address);\r\n    function addLiquidity(\r\n        address tokenA,\r\n        address tokenB,\r\n        uint amountADesired,\r\n        uint amountBDesired,\r\n        uint amountAMin,\r\n        uint amountBMin,\r\n        address to,\r\n        uint deadline\r\n    ) external returns (uint amountA, uint amountB, uint liquidity);\r\n    function addLiquidityETH(\r\n        address token,\r\n        uint amountTokenDesired,\r\n        uint amountTokenMin,\r\n        uint amountETHMin,\r\n        address to,\r\n        uint deadline\r\n    ) external payable returns (uint amountToken, uint amountETH, uint liquidity);\r\n    function removeLiquidity(\r\n        address tokenA,\r\n        address tokenB,\r\n        uint liquidity,\r\n        uint amountAMin,\r\n        uint amountBMin,\r\n        address to,\r\n        uint deadline\r\n    ) external returns (uint amountA, uint amountB);\r\n    function removeLiquidityETH(\r\n        address token,\r\n        uint liquidity,\r\n        uint amountTokenMin,\r\n        uint amountETHMin,\r\n        address to,\r\n        uint deadline\r\n    ) external returns (uint amountToken, uint amountETH);\r\n    function removeLiquidityWithPermit(\r\n        address tokenA,\r\n        address tokenB,\r\n        uint liquidity,\r\n        uint amountAMin,\r\n        uint amountBMin,\r\n        address to,\r\n        uint deadline,\r\n        bool approveMax, uint8 v, bytes32 r, bytes32 s\r\n    ) external returns (uint amountA, uint amountB);\r\n    function removeLiquidityETHWithPermit(\r\n        address token,\r\n        uint liquidity,\r\n        uint amountTokenMin,\r\n        uint amountETHMin,\r\n        address to,\r\n        uint deadline,\r\n        bool approveMax, uint8 v, bytes32 r, bytes32 s\r\n    ) external returns (uint amountToken, uint amountETH);\r\n    function swapExactTokensForTokens(\r\n        uint amountIn,\r\n        uint amountOutMin,\r\n        address[] calldata path,\r\n        address to,\r\n        uint deadline\r\n    ) external returns (uint[] memory amounts);\r\n    function swapTokensForExactTokens(\r\n        uint amountOut,\r\n        uint amountInMax,\r\n        address[] calldata path,\r\n        address to,\r\n        uint deadline\r\n    ) external returns (uint[] memory amounts);\r\n    function swapExactETHForTokens(uint amountOutMin, address[] calldata path, address to, uint deadline)\r\n        external\r\n        payable\r\n        returns (uint[] memory amounts);\r\n    function swapTokensForExactETH(uint amountOut, uint amountInMax, address[] calldata path, address to, uint deadline)\r\n        external\r\n        returns (uint[] memory amounts);\r\n    function swapExactTokensForETH(uint amountIn, uint amountOutMin, address[] calldata path, address to, uint deadline)\r\n        external\r\n        returns (uint[] memory amounts);\r\n    function swapETHForExactTokens(uint amountOut, address[] calldata path, address to, uint deadline)\r\n        external\r\n        payable\r\n        returns (uint[] memory amounts);\r\n\r\n    function quote(uint amountA, uint reserveA, uint reserveB) external pure returns (uint amountB);\r\n    function getAmountOut(uint amountIn, uint reserveIn, uint reserveOut) external pure returns (uint amountOut);\r\n    function getAmountIn(uint amountOut, uint reserveIn, uint reserveOut) external pure returns (uint amountIn);\r\n    function getAmountsOut(uint amountIn, address[] calldata path) external view returns (uint[] memory amounts);\r\n    function getAmountsIn(uint amountOut, address[] calldata path) external view returns (uint[] memory amounts);\r\n}\r\n\r\n// File: contracts\\interfaces\\IPancakeRouter02.sol\r\n\r\npragma solidity >=0.6.2;\r\n\r\ninterface IPancakeRouter02 is IPancakeRouter01 {\r\n    function removeLiquidityETHSupportingFeeOnTransferTokens(\r\n        address token,\r\n        uint liquidity,\r\n        uint amountTokenMin,\r\n        uint amountETHMin,\r\n        address to,\r\n        uint deadline\r\n    ) external returns (uint amountETH);\r\n    function removeLiquidityETHWithPermitSupportingFeeOnTransferTokens(\r\n        address token,\r\n        uint liquidity,\r\n        uint amountTokenMin,\r\n        uint amountETHMin,\r\n        address to,\r\n        uint deadline,\r\n        bool approveMax, uint8 v, bytes32 r, bytes32 s\r\n    ) external returns (uint amountETH);\r\n\r\n    function swapExactTokensForTokensSupportingFeeOnTransferTokens(\r\n        uint amountIn,\r\n        uint amountOutMin,\r\n        address[] calldata path,\r\n        address to,\r\n        uint deadline\r\n    ) external;\r\n    function swapExactETHForTokensSupportingFeeOnTransferTokens(\r\n        uint amountOutMin,\r\n        address[] calldata path,\r\n        address to,\r\n        uint deadline\r\n    ) external payable;\r\n    function swapExactTokensForETHSupportingFeeOnTransferTokens(\r\n        uint amountIn,\r\n        uint amountOutMin,\r\n        address[] calldata path,\r\n        address to,\r\n        uint deadline\r\n    ) external;\r\n}\r\ninterface IDODO {\r\n    function flashLoan(\r\n        uint256 baseAmount,\r\n        uint256 quoteAmount,\r\n        address assetTo,\r\n        bytes calldata data\r\n    ) external;\r\n\r\n    function _BASE_TOKEN_() external view returns (address);\r\n}\r\n\r\ncontract DODOFlashloan {\r\n    \r\n    //flashLoanPool:0x0fe261aeE0d1C4DFdDee4102E82Dd425999065F4\r\n    //flashLoanPool:0x6ddcc16c80a08de35866a5e173a25502d4a940da\r\n    //flashLoanPool:0xbdc7df7bb398e40125d7f82176a7d2f23e64ad06\r\n    //loanToken:0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c\r\n    address wbnb=0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c;\r\n    address usdt=0x55d398326f99059fF775485246999027B3197955;\r\n    address router=0x10ED43C718714eb63d5aA57B78B54704E256024E;\r\n    //借贷的主函数。这个函数不用动，基本上就是照抄就可以了。\r\n     //1、flashloanpool如何查找，看后面的说明。\r\n    //2、loanAmount怎么确认？\r\n    //3、loantoken在哪里查找？\r\n    function dodoFlashLoan(\r\n        address flashLoanPool, //You will make a flashloan from this DODOV2 pool\r\n        uint256 loanAmount, \r\n        address loanToken\r\n    ) external  {\r\n        bytes memory data = abi.encode(flashLoanPool, loanToken, loanAmount);\r\n        address flashLoanBase = IDODO(flashLoanPool)._BASE_TOKEN_();\r\n        //这个函数说明一下。_BASE_TOKEN是POOL池中，会有两个币对，那个币做为主要币种的。比如，wbnb/usdt,那么，_BASE_TOKEN就是wbnb\r\n        if(flashLoanBase == loanToken) {\r\n        //下面这个判断，就是看我们要借贷那一种币，如果是_BASE_TOKEN,那么，合约就会把loanAmount数量的币转到我们的合约地址，0是另一种币。\r\n            IDODO(flashLoanPool).flashLoan(loanAmount, 0, address(this), data);\r\n        } else {\r\n            IDODO(flashLoanPool).flashLoan(0, loanAmount, address(this), data);\r\n        }\r\n    }\r\n  \r\n    function DVMFlashLoanCall(address sender, uint256 baseAmount, uint256 quoteAmount,bytes calldata data) external {\r\n    //我这里只是写了一个简单的兑换，就是将借来的wbnb兑换成usdt,这里就会有一个比较有意思的问题。如果我们部署了合约，直接调用的话，就会出错，主要问题就是我们把借来的wbnb兑换成usdt了，没有足够的币还给借贷池，执行就出错了，所以，在执行借贷之前，需要先向合约里转大于我们借贷值的wbnb.\r\n       address[] memory path1 = new address[](2);\r\n        IERC20(wbnb).approve(router,2**256-1);\r\n        path1[0] = wbnb;\r\n\r\n        path1[1] = usdt;\r\n        //这个就是一个简单的池子兑换函数\r\n        IPancakeRouter02(router).swapExactTokensForTokens(\r\n        baseAmount,\r\n        0,\r\n        path1,\r\n        address(this),\r\n        block.timestamp+12000\r\n         );\r\n    \r\n        //过程执行完成 ，将借来的wbnb原数返还。\r\n        _flashLoanCallBack(sender,baseAmount,quoteAmount,data);\r\n        //下面两句，比较重要，一定要将我们换回的币和多余的wbnb转回我们自己的钱包或者合约，否则，这些币就会死在合约中。当然了，你加一个提现函数也是可以的，不过要手工调用几次，比较麻烦。\r\n        IERC20(usdt).transfer(tx.origin, IERC20(usdt).balanceOf(address(this)));\r\n        IERC20(wbnb).transfer(tx.origin, IERC20(wbnb).balanceOf(address(this)));\r\n    }\r\n    event test(uint,address[]);\r\n  \r\n    function DPPFlashLoanCall(address sender, uint256 baseAmount, uint256 quoteAmount, bytes calldata data)  external {\r\n        \r\n        _flashLoanCallBack(sender,baseAmount,quoteAmount,data);\r\n   \r\n    }\r\n\r\n    //Note: CallBack function executed by DODOV2(DSP) flashLoan pool\r\n    function DSPFlashLoanCall(address sender, uint256 baseAmount, uint256 quoteAmount, bytes calldata data) external {\r\n        _flashLoanCallBack(sender,baseAmount,quoteAmount,data);\r\n   \r\n    }\r\n\r\n    function _flashLoanCallBack(address sender, uint256, uint256, bytes calldata data) internal {\r\n        (address flashLoanPool, address loanToken, uint256 loanAmount) = abi.decode(data, (address, address, uint256));\r\n        \r\n        require(sender == address(this) && msg.sender == flashLoanPool, \"HANDLE_FLASH_NENIED\");\r\n\r\n        IERC20(loanToken).transfer(flashLoanPool, loanAmount);\r\n    }\r\n}\r\n```\r\n1、flashloanpool如何查找，看后面的说明。\r\n打开https://app.dodoex.io/pool?network=bsc-mainnet，然后点左边的流动池，选择您要使用的链，选择您要选择的币种。\r\n\r\n![image.png](https://img.learnblockchain.cn/attachments/2023/09/W5yS0dS365010136c1c00.png)\r\n点击复制池子的address.然后进入币安链查询。\r\n\r\n\r\n![image.png](https://img.learnblockchain.cn/attachments/2023/09/dQGqOX3b650101bba4fdc.png)\r\n首先看一下，是DVM，DPP还是DSP，那么这里就确定着你的主要逻辑，是写在哪个函数中。\r\n\r\n2、loanAmount怎么确认？\r\n![image.png](https://img.learnblockchain.cn/attachments/2023/09/rCID3yPf6501020d330f9.png)\r\n函数4，表示我们可以借多少个amount,_Base_TOKEN，上面已经讲过了。\r\n\r\n![image.png](https://img.learnblockchain.cn/attachments/2023/09/Oculc2YK650102906094d.png)\r\n这里可以看到我们每个币种可以借贷的最大数量。\r\n3、loantoken在哪里查找？\r\n用getpari函数，查询这个lp里面的币对组成，就可以查找需要的币种了，当然，你可以直接看到，有经验用更好的方式 。\r\n\r\n![image.png](https://img.learnblockchain.cn/attachments/2023/09/m85QPGGe6501031cbe83b.png)\r\n有经验的直接在这里就找到了。"},"author":{"user":"https://learnblockchain.cn/people/16751","address":null},"history":null,"timestamp":1694565188,"version":1}