├── .gitignore ├── README.md ├── contracts ├── .DS_Store ├── PancakeFlashSwap.sol ├── interfaces │ ├── IERC20.sol │ ├── IUniswapV2Factory.sol │ ├── IUniswapV2Pair.sol │ ├── IUniswapV2Router01.sol │ └── IUniswapV2Router02.sol └── libraries │ ├── Address.sol │ ├── SafeERC20.sol │ ├── SafeMath.sol │ └── UniswapV2Library.sol ├── hardhat.config.js ├── package-lock.json ├── package.json ├── scripts └── deploy.js ├── test └── tester.js └── utils └── utilities.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .env 3 | coverage 4 | coverage.json 5 | typechain 6 | 7 | #Hardhat files 8 | cache 9 | artifacts 10 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # MEV TRIANGULAR ARBITRAGE BOT - [Smart contract part] 2 | 3 | To run this project 4 | 5 | 1. git clone ```https://github.com/NorVirae/mev-triangular-arbitrage-bot-contract.git``` 6 | 2. run ```npm install``` 7 | 3. run ```npx hardhat test```, to see project execute on a fork of mainnet 8 | 4. run ```npx hardhat run scripts/deploy.js --network [network_name]```, Network Name from "hardhat.config" 9 | 5. you can hook up the smart contract interface with a nodejs, python django/flask server(bot), to call the interfaces to perform arbitrage on block change. 10 | -------------------------------------------------------------------------------- /contracts/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NorVirae/mev-triangular-arbitrage-bot-contract/62c85341dedf0815c6e54da937e7c12623577ffd/contracts/.DS_Store -------------------------------------------------------------------------------- /contracts/PancakeFlashSwap.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.6.6; 3 | import "hardhat/console.sol"; 4 | 5 | // import libraries and interfaces 6 | import "./libraries/SafeERC20.sol"; 7 | import "./libraries/SafeMath.sol"; 8 | 9 | import "./libraries/UniswapV2Library.sol"; 10 | import "./interfaces/IERC20.sol"; 11 | import "./interfaces/IUniswapV2Factory.sol"; 12 | import "./interfaces/IUniswapV2Pair.sol"; 13 | import "./interfaces/IUniswapV2Router01.sol"; 14 | import "./interfaces/IUniswapV2Router02.sol"; 15 | 16 | contract PancakeFlashSwap { 17 | using SafeERC20 for IERC20; 18 | using SafeMath for uint256; 19 | // factory and router contract addresses 20 | address private constant PANCAKEV2_FACTORY = 21 | 0xcA143Ce32Fe78f1f7019d7d551a6402fC5350c73; 22 | address private constant PANCAKEV2_ROUTER = 23 | 0x10ED43C718714eb63d5aA57B78B54704E256024E; 24 | 25 | // set Deadline 26 | uint256 private deadline = block.timestamp + 20 minutes; 27 | uint256 private constant MAX_INT = 28 | 115792089237316195423570985008687907853269984665640564039457584007913129639935; 29 | 30 | // Fund smart contract 31 | // function allows smart contract to be funded 32 | 33 | function fundFlashContract( 34 | address _owner, 35 | address _token, 36 | uint256 _amount 37 | ) public { 38 | IERC20(_token).transferFrom(_owner, address(this), _amount); 39 | } 40 | 41 | // get balance of token on contract 42 | function getFlashContractBalance(address _token) 43 | public 44 | view 45 | returns (uint256) 46 | { 47 | return IERC20(_token).balanceOf(address(this)); 48 | } 49 | 50 | // place trade function 51 | function placeTrade( 52 | address _fromToken, 53 | address _toToken, 54 | uint256 _amountIn 55 | ) private returns (uint256) { 56 | // make sure pair exist so to not waste gas 57 | address pair = IUniswapV2Factory(PANCAKEV2_FACTORY).getPair( 58 | _fromToken, 59 | _toToken 60 | ); 61 | require(pair != address(0), "There is no Liquidity"); 62 | address[] memory path = new address[](2); 63 | path[0] = _fromToken; 64 | path[1] = _toToken; 65 | uint256 amountsOutMin = IUniswapV2Router01(PANCAKEV2_ROUTER) 66 | .getAmountsOut(_amountIn, path)[1]; 67 | console.log("amounts required %s", amountsOutMin); 68 | 69 | uint256 amountsReceived = IUniswapV2Router01(PANCAKEV2_ROUTER) 70 | .swapExactTokensForTokens( 71 | _amountIn, 72 | amountsOutMin, 73 | path, 74 | address(this), 75 | deadline 76 | )[1]; 77 | console.log("amounts received %s", amountsReceived); 78 | require(amountsReceived > 0, "Aborted Tx: Trade returned 0"); 79 | return amountsReceived; 80 | } 81 | 82 | // Get pair balance 83 | function getPairBalance(address _token0, address _token1) 84 | public 85 | view 86 | returns ( 87 | uint256, 88 | uint256, 89 | uint256, 90 | uint256 91 | ) 92 | { 93 | address pair = IUniswapV2Factory(PANCAKEV2_FACTORY).getPair(_token0, _token1); 94 | 95 | uint256 balance0 = IERC20(_token0).balanceOf( 96 | IUniswapV2Pair(pair).token0() 97 | ); 98 | uint256 balance1 = IERC20(_token1).balanceOf( 99 | IUniswapV2Pair(pair).token1() 100 | ); 101 | uint256 balance2 = IERC20(_token0).balanceOf(address(pair)); 102 | uint256 balance3 = IERC20(_token0).balanceOf(address(this)); 103 | // console.log("balance0: %s, balance1: %s, balance2: %s, balance3: %s", balance0, balance1, balance2, balance3); 104 | return (balance0, balance1, balance2, balance3); 105 | } 106 | 107 | // checks trade profitabilty in order of token provided 108 | function checkTriangularTradeProfitabilityOnBlockCall( 109 | address _tradeToken1, 110 | address _tradeToken2, 111 | address _tradeToken3, 112 | uint256 _amount 113 | ) external view returns (bool) { 114 | address[] memory tPath1 = new address[](2); 115 | address[] memory tPath2 = new address[](2); 116 | address[] memory tPath3 = new address[](2); 117 | bool startTrade = false; 118 | 119 | // Trade 1 path 120 | tPath1[0] = _tradeToken1; 121 | tPath1[1] = _tradeToken2; 122 | 123 | // Trade 2 path 124 | tPath2[0] = _tradeToken2; 125 | tPath2[1] = _tradeToken3; 126 | 127 | // Trade 3 path 128 | tPath3[0] = _tradeToken3; 129 | tPath3[1] = _tradeToken1; 130 | 131 | // get possible trade result of first swap 132 | uint256 trade1PossibleOutcomeAmount = IUniswapV2Router01( 133 | PANCAKEV2_ROUTER 134 | ).getAmountsOut(_amount, tPath1)[1]; 135 | 136 | // get posssible result of second swap 137 | uint256 trade2PossibleOutcomeAmount = IUniswapV2Router01( 138 | PANCAKEV2_ROUTER 139 | ).getAmountsOut(trade1PossibleOutcomeAmount, tPath2)[1]; 140 | 141 | // get possible resultant amount of 3rd swap 142 | uint256 trade3PossibleOutcomeAmount = IUniswapV2Router01( 143 | PANCAKEV2_ROUTER 144 | ).getAmountsOut(trade2PossibleOutcomeAmount, tPath3)[1]; 145 | 146 | // fee to pay back for loan 147 | uint256 fee = ((_amount * 3) / 997) + 1; 148 | 149 | // amount to repay 150 | uint256 amountToRepay = _amount.add(fee); 151 | 152 | // check resultant amount is greater than payback amount 153 | if (trade3PossibleOutcomeAmount > amountToRepay) { 154 | startTrade = true; 155 | } 156 | return startTrade; 157 | } 158 | 159 | // Check profitablity 160 | function checkProfitability(uint256 input, uint256 output) 161 | public 162 | pure 163 | returns (bool) 164 | { 165 | bool isOutputBigger = output > input ? true : false; 166 | return isOutputBigger; 167 | } 168 | 169 | // get flashloan from contract 170 | function startLoan( 171 | address _tokenBorrow, 172 | address _tokenBaseBorrow, //probably WBNB 173 | address _trade1TOken, 174 | address _trade2Token, 175 | uint256 _amount 176 | ) external { 177 | IERC20(_trade1TOken).safeApprove(address(PANCAKEV2_ROUTER), MAX_INT); 178 | IERC20(_trade2Token).safeApprove(address(PANCAKEV2_ROUTER), MAX_INT); 179 | IERC20(_tokenBorrow).safeApprove(address(PANCAKEV2_ROUTER), MAX_INT); 180 | 181 | // get the factory pair address for combined 182 | address pair = IUniswapV2Factory(PANCAKEV2_FACTORY).getPair( 183 | _tokenBorrow, 184 | _tokenBaseBorrow 185 | ); 186 | 187 | require(pair != address(0), "pool doesn't exist"); 188 | 189 | // get pair 190 | address token0 = IUniswapV2Pair(pair).token0(); 191 | address token1 = IUniswapV2Pair(pair).token1(); 192 | uint256 amountOut0 = _tokenBorrow == token0 ? _amount : 0; 193 | uint256 amountOut1 = _tokenBorrow == token1 ? _amount : 0; 194 | 195 | // encode data 196 | bytes memory data = abi.encode( 197 | _tokenBorrow, 198 | _amount, 199 | msg.sender, 200 | _trade1TOken, 201 | _trade2Token 202 | ); 203 | 204 | // call swap 205 | IUniswapV2Pair(pair).swap(amountOut0, amountOut1, address(this), data); 206 | } 207 | 208 | function pancakeCall( 209 | address _sender, 210 | uint256 _amount0, 211 | uint256 _amount1, 212 | bytes calldata _data 213 | ) external { 214 | address token0 = IUniswapV2Pair(msg.sender).token0(); 215 | address token1 = IUniswapV2Pair(msg.sender).token1(); 216 | 217 | address pair = IUniswapV2Factory(PANCAKEV2_FACTORY).getPair( 218 | token0, 219 | token1 220 | ); 221 | require(pair == msg.sender, "pool does not exist"); 222 | require( 223 | _sender == address(this), 224 | "Swap call was not called by this contract" 225 | ); 226 | 227 | // decode data 228 | ( 229 | address _tokenBorrow, 230 | uint256 _amount, 231 | address myAddress, 232 | address _tradeToken1, 233 | address _tradeToken2 234 | ) = abi.decode( 235 | _data, 236 | (address, uint256, address, address, address) 237 | ); 238 | 239 | // calculate amount to repay 240 | uint256 fee = ((_amount * 3) / 997) + 1; 241 | uint256 amountToRepay = _amount.add(fee); 242 | 243 | // Perform arbitrage 244 | // get Trade amount 245 | uint256 tradeAmount = _amount0 > 0 ? _amount0 : _amount1; 246 | 247 | // placeTrade 248 | uint256 receivedAmountTrade1 = placeTrade(_tokenBorrow, _tradeToken1, tradeAmount); 249 | uint256 receivedAmountTrade2 = placeTrade( 250 | _tradeToken1, 251 | _tradeToken2, 252 | receivedAmountTrade1 253 | ); 254 | uint256 receivedAmountTrade3 = placeTrade( 255 | _tradeToken2, 256 | _tokenBorrow, 257 | receivedAmountTrade2 258 | ); 259 | 260 | // check trade profitablity 261 | bool isOutputBigger = checkProfitability( 262 | amountToRepay, 263 | receivedAmountTrade3 264 | ); 265 | require(isOutputBigger, "Trade not profitable"); 266 | 267 | // Pay yourself 268 | if (isOutputBigger) { 269 | IERC20 otherToken = IERC20(_tokenBorrow); 270 | otherToken.transfer(myAddress, receivedAmountTrade3 - amountToRepay); 271 | } 272 | 273 | // Pay back loan 274 | IERC20(_tokenBorrow).safeTransfer(pair, amountToRepay); 275 | } 276 | } 277 | -------------------------------------------------------------------------------- /contracts/interfaces/IERC20.sol: -------------------------------------------------------------------------------- 1 | //SPDX-License-Identifier: MIT 2 | pragma solidity >=0.5.0; 3 | 4 | interface IERC20 { 5 | event Approval( 6 | address indexed owner, 7 | address indexed spender, 8 | uint256 value 9 | ); 10 | event Transfer(address indexed from, address indexed to, uint256 value); 11 | 12 | function name() external view returns (string memory); 13 | 14 | function symbol() external view returns (string memory); 15 | 16 | function decimals() external view returns (uint8); 17 | 18 | function totalSupply() external view returns (uint256); 19 | 20 | function balanceOf(address owner) external view returns (uint256); 21 | 22 | function allowance(address owner, address spender) 23 | external 24 | view 25 | returns (uint256); 26 | 27 | function approve(address spender, uint256 value) external returns (bool); 28 | 29 | function transfer(address to, uint256 value) external returns (bool); 30 | 31 | function transferFrom( 32 | address from, 33 | address to, 34 | uint256 value 35 | ) external returns (bool); 36 | } 37 | -------------------------------------------------------------------------------- /contracts/interfaces/IUniswapV2Factory.sol: -------------------------------------------------------------------------------- 1 | //SPDX-License-Identifier: MIT 2 | pragma solidity >=0.5.0; 3 | 4 | interface IUniswapV2Factory { 5 | event PairCreated( 6 | address indexed token0, 7 | address indexed token1, 8 | address pair, 9 | uint256 10 | ); 11 | 12 | function getPair(address tokenA, address tokenB) 13 | external 14 | view 15 | returns (address pair); 16 | 17 | function allPairs(uint256) external view returns (address pair); 18 | 19 | function allPairsLength() external view returns (uint256); 20 | 21 | function feeTo() external view returns (address); 22 | 23 | function feeToSetter() external view returns (address); 24 | 25 | function createPair(address tokenA, address tokenB) 26 | external 27 | returns (address pair); 28 | } 29 | -------------------------------------------------------------------------------- /contracts/interfaces/IUniswapV2Pair.sol: -------------------------------------------------------------------------------- 1 | //SPDX-License-Identifier: MIT 2 | pragma solidity >=0.5.0; 3 | 4 | interface IUniswapV2Pair { 5 | event Approval( 6 | address indexed owner, 7 | address indexed spender, 8 | uint256 value 9 | ); 10 | event Transfer(address indexed from, address indexed to, uint256 value); 11 | 12 | function name() external pure returns (string memory); 13 | 14 | function symbol() external pure returns (string memory); 15 | 16 | function decimals() external pure returns (uint8); 17 | 18 | function totalSupply() external view returns (uint256); 19 | 20 | function balanceOf(address owner) external view returns (uint256); 21 | 22 | function allowance(address owner, address spender) 23 | external 24 | view 25 | returns (uint256); 26 | 27 | function approve(address spender, uint256 value) external returns (bool); 28 | 29 | function transfer(address to, uint256 value) external returns (bool); 30 | 31 | function transferFrom( 32 | address from, 33 | address to, 34 | uint256 value 35 | ) external returns (bool); 36 | 37 | function DOMAIN_SEPARATOR() external view returns (bytes32); 38 | 39 | function PERMIT_TYPEHASH() external pure returns (bytes32); 40 | 41 | function nonces(address owner) external view returns (uint256); 42 | 43 | function permit( 44 | address owner, 45 | address spender, 46 | uint256 value, 47 | uint256 deadline, 48 | uint8 v, 49 | bytes32 r, 50 | bytes32 s 51 | ) external; 52 | 53 | event Mint(address indexed sender, uint256 amount0, uint256 amount1); 54 | event Burn( 55 | address indexed sender, 56 | uint256 amount0, 57 | uint256 amount1, 58 | address indexed to 59 | ); 60 | event Swap( 61 | address indexed sender, 62 | uint256 amount0In, 63 | uint256 amount1In, 64 | uint256 amount0Out, 65 | uint256 amount1Out, 66 | address indexed to 67 | ); 68 | event Sync(uint112 reserve0, uint112 reserve1); 69 | 70 | function MINIMUM_LIQUIDITY() external pure returns (uint256); 71 | 72 | function factory() external view returns (address); 73 | 74 | function token0() external view returns (address); 75 | 76 | function token1() external view returns (address); 77 | 78 | function getReserves() 79 | external 80 | view 81 | returns ( 82 | uint112 reserve0, 83 | uint112 reserve1, 84 | uint32 blockTimestampLast 85 | ); 86 | 87 | function price0CumulativeLast() external view returns (uint256); 88 | 89 | function price1CumulativeLast() external view returns (uint256); 90 | 91 | function kLast() external view returns (uint256); 92 | 93 | function mint(address to) external returns (uint256 liquidity); 94 | 95 | function burn(address to) 96 | external 97 | returns (uint256 amount0, uint256 amount1); 98 | 99 | function swap( 100 | uint256 amount0Out, 101 | uint256 amount1Out, 102 | address to, 103 | bytes calldata data 104 | ) external; 105 | 106 | function skim(address to) external; 107 | 108 | function sync() external; 109 | 110 | function initialize(address, address) external; 111 | } 112 | -------------------------------------------------------------------------------- /contracts/interfaces/IUniswapV2Router01.sol: -------------------------------------------------------------------------------- 1 | //SPDX-License-Identifier: MIT 2 | pragma solidity >=0.6.2; 3 | 4 | interface IUniswapV2Router01 { 5 | function factory() external pure returns (address); 6 | 7 | function WETH() external pure returns (address); 8 | 9 | function addLiquidity( 10 | address tokenA, 11 | address tokenB, 12 | uint256 amountADesired, 13 | uint256 amountBDesired, 14 | uint256 amountAMin, 15 | uint256 amountBMin, 16 | address to, 17 | uint256 deadline 18 | ) 19 | external 20 | returns ( 21 | uint256 amountA, 22 | uint256 amountB, 23 | uint256 liquidity 24 | ); 25 | 26 | function addLiquidityETH( 27 | address token, 28 | uint256 amountTokenDesired, 29 | uint256 amountTokenMin, 30 | uint256 amountETHMin, 31 | address to, 32 | uint256 deadline 33 | ) 34 | external 35 | payable 36 | returns ( 37 | uint256 amountToken, 38 | uint256 amountETH, 39 | uint256 liquidity 40 | ); 41 | 42 | function removeLiquidity( 43 | address tokenA, 44 | address tokenB, 45 | uint256 liquidity, 46 | uint256 amountAMin, 47 | uint256 amountBMin, 48 | address to, 49 | uint256 deadline 50 | ) external returns (uint256 amountA, uint256 amountB); 51 | 52 | function removeLiquidityETH( 53 | address token, 54 | uint256 liquidity, 55 | uint256 amountTokenMin, 56 | uint256 amountETHMin, 57 | address to, 58 | uint256 deadline 59 | ) external returns (uint256 amountToken, uint256 amountETH); 60 | 61 | function removeLiquidityWithPermit( 62 | address tokenA, 63 | address tokenB, 64 | uint256 liquidity, 65 | uint256 amountAMin, 66 | uint256 amountBMin, 67 | address to, 68 | uint256 deadline, 69 | bool approveMax, 70 | uint8 v, 71 | bytes32 r, 72 | bytes32 s 73 | ) external returns (uint256 amountA, uint256 amountB); 74 | 75 | function removeLiquidityETHWithPermit( 76 | address token, 77 | uint256 liquidity, 78 | uint256 amountTokenMin, 79 | uint256 amountETHMin, 80 | address to, 81 | uint256 deadline, 82 | bool approveMax, 83 | uint8 v, 84 | bytes32 r, 85 | bytes32 s 86 | ) external returns (uint256 amountToken, uint256 amountETH); 87 | 88 | function swapExactTokensForTokens( 89 | uint256 amountIn, 90 | uint256 amountOutMin, 91 | address[] calldata path, 92 | address to, 93 | uint256 deadline 94 | ) external returns (uint256[] memory amounts); 95 | 96 | function swapTokensForExactTokens( 97 | uint256 amountOut, 98 | uint256 amountInMax, 99 | address[] calldata path, 100 | address to, 101 | uint256 deadline 102 | ) external returns (uint256[] memory amounts); 103 | 104 | function swapExactETHForTokens( 105 | uint256 amountOutMin, 106 | address[] calldata path, 107 | address to, 108 | uint256 deadline 109 | ) external payable returns (uint256[] memory amounts); 110 | 111 | function swapTokensForExactETH( 112 | uint256 amountOut, 113 | uint256 amountInMax, 114 | address[] calldata path, 115 | address to, 116 | uint256 deadline 117 | ) external returns (uint256[] memory amounts); 118 | 119 | function swapExactTokensForETH( 120 | uint256 amountIn, 121 | uint256 amountOutMin, 122 | address[] calldata path, 123 | address to, 124 | uint256 deadline 125 | ) external returns (uint256[] memory amounts); 126 | 127 | function swapETHForExactTokens( 128 | uint256 amountOut, 129 | address[] calldata path, 130 | address to, 131 | uint256 deadline 132 | ) external payable returns (uint256[] memory amounts); 133 | 134 | function quote( 135 | uint256 amountA, 136 | uint256 reserveA, 137 | uint256 reserveB 138 | ) external pure returns (uint256 amountB); 139 | 140 | function getAmountOut( 141 | uint256 amountIn, 142 | uint256 reserveIn, 143 | uint256 reserveOut 144 | ) external pure returns (uint256 amountOut); 145 | 146 | function getAmountIn( 147 | uint256 amountOut, 148 | uint256 reserveIn, 149 | uint256 reserveOut 150 | ) external pure returns (uint256 amountIn); 151 | 152 | function getAmountsOut(uint256 amountIn, address[] calldata path) 153 | external 154 | view 155 | returns (uint256[] memory amounts); 156 | 157 | function getAmountsIn(uint256 amountOut, address[] calldata path) 158 | external 159 | view 160 | returns (uint256[] memory amounts); 161 | } 162 | -------------------------------------------------------------------------------- /contracts/interfaces/IUniswapV2Router02.sol: -------------------------------------------------------------------------------- 1 | //SPDX-License-Identifier: MIT 2 | pragma solidity >=0.6.2; 3 | 4 | import "./IUniswapV2Router01.sol"; 5 | 6 | interface IUniswapV2Router02 is IUniswapV2Router01 { 7 | function removeLiquidityETHSupportingFeeOnTransferTokens( 8 | address token, 9 | uint256 liquidity, 10 | uint256 amountTokenMin, 11 | uint256 amountETHMin, 12 | address to, 13 | uint256 deadline 14 | ) external returns (uint256 amountETH); 15 | 16 | function removeLiquidityETHWithPermitSupportingFeeOnTransferTokens( 17 | address token, 18 | uint256 liquidity, 19 | uint256 amountTokenMin, 20 | uint256 amountETHMin, 21 | address to, 22 | uint256 deadline, 23 | bool approveMax, 24 | uint8 v, 25 | bytes32 r, 26 | bytes32 s 27 | ) external returns (uint256 amountETH); 28 | 29 | function swapExactTokensForTokensSupportingFeeOnTransferTokens( 30 | uint256 amountIn, 31 | uint256 amountOutMin, 32 | address[] calldata path, 33 | address to, 34 | uint256 deadline 35 | ) external; 36 | 37 | function swapExactETHForTokensSupportingFeeOnTransferTokens( 38 | uint256 amountOutMin, 39 | address[] calldata path, 40 | address to, 41 | uint256 deadline 42 | ) external payable; 43 | 44 | function swapExactTokensForETHSupportingFeeOnTransferTokens( 45 | uint256 amountIn, 46 | uint256 amountOutMin, 47 | address[] calldata path, 48 | address to, 49 | uint256 deadline 50 | ) external; 51 | } 52 | -------------------------------------------------------------------------------- /contracts/libraries/Address.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity >=0.6.6; 4 | 5 | /** 6 | * @dev Collection of functions related to the address type 7 | */ 8 | library Address { 9 | /** 10 | * @dev Returns true if `account` is a contract. 11 | * 12 | * [IMPORTANT] 13 | * ==== 14 | * It is unsafe to assume that an address for which this function returns 15 | * false is an externally-owned account (EOA) and not a contract. 16 | * 17 | * Among others, `isContract` will return false for the following 18 | * types of addresses: 19 | * 20 | * - an externally-owned account 21 | * - a contract in construction 22 | * - an address where a contract will be created 23 | * - an address where a contract lived, but was destroyed 24 | * ==== 25 | */ 26 | function isContract(address account) internal view returns (bool) { 27 | // This method relies on extcodesize, which returns 0 for contracts in 28 | // construction, since the code is only stored at the end of the 29 | // constructor execution. 30 | 31 | uint256 size; 32 | // solhint-disable-next-line no-inline-assembly 33 | assembly { 34 | size := extcodesize(account) 35 | } 36 | return size > 0; 37 | } 38 | 39 | /** 40 | * @dev Replacement for Solidity's `transfer`: sends `amount` wei to 41 | * `recipient`, forwarding all available gas and reverting on errors. 42 | * 43 | * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost 44 | * of certain opcodes, possibly making contracts go over the 2300 gas limit 45 | * imposed by `transfer`, making them unable to receive funds via 46 | * `transfer`. {sendValue} removes this limitation. 47 | * 48 | * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more]. 49 | * 50 | * IMPORTANT: because control is transferred to `recipient`, care must be 51 | * taken to not create reentrancy vulnerabilities. Consider using 52 | * {ReentrancyGuard} or the 53 | * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. 54 | */ 55 | function sendValue(address payable recipient, uint256 amount) internal { 56 | require( 57 | address(this).balance >= amount, 58 | "Address: insufficient balance" 59 | ); 60 | 61 | // solhint-disable-next-line avoid-low-level-calls, avoid-call-value 62 | (bool success, ) = recipient.call{value: amount}(""); 63 | require( 64 | success, 65 | "Address: unable to send value, recipient may have reverted" 66 | ); 67 | } 68 | 69 | /** 70 | * @dev Performs a Solidity function call using a low level `call`. A 71 | * plain`call` is an unsafe replacement for a function call: use this 72 | * function instead. 73 | * 74 | * If `target` reverts with a revert reason, it is bubbled up by this 75 | * function (like regular Solidity function calls). 76 | * 77 | * Returns the raw returned data. To convert to the expected return value, 78 | * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`]. 79 | * 80 | * Requirements: 81 | * 82 | * - `target` must be a contract. 83 | * - calling `target` with `data` must not revert. 84 | * 85 | * _Available since v3.1._ 86 | */ 87 | function functionCall(address target, bytes memory data) 88 | internal 89 | returns (bytes memory) 90 | { 91 | return functionCall(target, data, "Address: low-level call failed"); 92 | } 93 | 94 | /** 95 | * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with 96 | * `errorMessage` as a fallback revert reason when `target` reverts. 97 | * 98 | * _Available since v3.1._ 99 | */ 100 | function functionCall( 101 | address target, 102 | bytes memory data, 103 | string memory errorMessage 104 | ) internal returns (bytes memory) { 105 | return functionCallWithValue(target, data, 0, errorMessage); 106 | } 107 | 108 | /** 109 | * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], 110 | * but also transferring `value` wei to `target`. 111 | * 112 | * Requirements: 113 | * 114 | * - the calling contract must have an ETH balance of at least `value`. 115 | * - the called Solidity function must be `payable`. 116 | * 117 | * _Available since v3.1._ 118 | */ 119 | function functionCallWithValue( 120 | address target, 121 | bytes memory data, 122 | uint256 value 123 | ) internal returns (bytes memory) { 124 | return 125 | functionCallWithValue( 126 | target, 127 | data, 128 | value, 129 | "Address: low-level call with value failed" 130 | ); 131 | } 132 | 133 | /** 134 | * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but 135 | * with `errorMessage` as a fallback revert reason when `target` reverts. 136 | * 137 | * _Available since v3.1._ 138 | */ 139 | function functionCallWithValue( 140 | address target, 141 | bytes memory data, 142 | uint256 value, 143 | string memory errorMessage 144 | ) internal returns (bytes memory) { 145 | require( 146 | address(this).balance >= value, 147 | "Address: insufficient balance for call" 148 | ); 149 | require(isContract(target), "Address: call to non-contract"); 150 | 151 | // solhint-disable-next-line avoid-low-level-calls 152 | (bool success, bytes memory returndata) = target.call{value: value}( 153 | data 154 | ); 155 | return _verifyCallResult(success, returndata, errorMessage); 156 | } 157 | 158 | /** 159 | * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], 160 | * but performing a static call. 161 | * 162 | * _Available since v3.3._ 163 | */ 164 | function functionStaticCall(address target, bytes memory data) 165 | internal 166 | view 167 | returns (bytes memory) 168 | { 169 | return 170 | functionStaticCall( 171 | target, 172 | data, 173 | "Address: low-level static call failed" 174 | ); 175 | } 176 | 177 | /** 178 | * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], 179 | * but performing a static call. 180 | * 181 | * _Available since v3.3._ 182 | */ 183 | function functionStaticCall( 184 | address target, 185 | bytes memory data, 186 | string memory errorMessage 187 | ) internal view returns (bytes memory) { 188 | require(isContract(target), "Address: static call to non-contract"); 189 | 190 | // solhint-disable-next-line avoid-low-level-calls 191 | (bool success, bytes memory returndata) = target.staticcall(data); 192 | return _verifyCallResult(success, returndata, errorMessage); 193 | } 194 | 195 | /** 196 | * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], 197 | * but performing a delegate call. 198 | * 199 | * _Available since v3.4._ 200 | */ 201 | function functionDelegateCall(address target, bytes memory data) 202 | internal 203 | returns (bytes memory) 204 | { 205 | return 206 | functionDelegateCall( 207 | target, 208 | data, 209 | "Address: low-level delegate call failed" 210 | ); 211 | } 212 | 213 | /** 214 | * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], 215 | * but performing a delegate call. 216 | * 217 | * _Available since v3.4._ 218 | */ 219 | function functionDelegateCall( 220 | address target, 221 | bytes memory data, 222 | string memory errorMessage 223 | ) internal returns (bytes memory) { 224 | require(isContract(target), "Address: delegate call to non-contract"); 225 | 226 | // solhint-disable-next-line avoid-low-level-calls 227 | (bool success, bytes memory returndata) = target.delegatecall(data); 228 | return _verifyCallResult(success, returndata, errorMessage); 229 | } 230 | 231 | function _verifyCallResult( 232 | bool success, 233 | bytes memory returndata, 234 | string memory errorMessage 235 | ) private pure returns (bytes memory) { 236 | if (success) { 237 | return returndata; 238 | } else { 239 | // Look for revert reason and bubble it up if present 240 | if (returndata.length > 0) { 241 | // The easiest way to bubble the revert reason is using memory via assembly 242 | 243 | // solhint-disable-next-line no-inline-assembly 244 | assembly { 245 | let returndata_size := mload(returndata) 246 | revert(add(32, returndata), returndata_size) 247 | } 248 | } else { 249 | revert(errorMessage); 250 | } 251 | } 252 | } 253 | } 254 | -------------------------------------------------------------------------------- /contracts/libraries/SafeERC20.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity >=0.6.0; 4 | 5 | import "../interfaces/IERC20.sol"; 6 | import "./SafeMath.sol"; 7 | import "./Address.sol"; 8 | 9 | /** 10 | * @title SafeERC20 11 | * @dev Wrappers around ERC20 operations that throw on failure (when the token 12 | * contract returns false). Tokens that return no value (and instead revert or 13 | * throw on failure) are also supported, non-reverting calls are assumed to be 14 | * successful. 15 | * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract, 16 | * which allows you to call the safe operations as `token.safeTransfer(...)`, etc. 17 | */ 18 | library SafeERC20 { 19 | using SafeMath for uint256; 20 | using Address for address; 21 | 22 | function safeTransfer( 23 | IERC20 token, 24 | address to, 25 | uint256 value 26 | ) internal { 27 | _callOptionalReturn( 28 | token, 29 | abi.encodeWithSelector(token.transfer.selector, to, value) 30 | ); 31 | } 32 | 33 | function safeTransferFrom( 34 | IERC20 token, 35 | address from, 36 | address to, 37 | uint256 value 38 | ) internal { 39 | _callOptionalReturn( 40 | token, 41 | abi.encodeWithSelector(token.transferFrom.selector, from, to, value) 42 | ); 43 | } 44 | 45 | /** 46 | * @dev Deprecated. This function has issues similar to the ones found in 47 | * {IERC20-approve}, and its usage is discouraged. 48 | * 49 | * Whenever possible, use {safeIncreaseAllowance} and 50 | * {safeDecreaseAllowance} instead. 51 | */ 52 | function safeApprove( 53 | IERC20 token, 54 | address spender, 55 | uint256 value 56 | ) internal { 57 | // safeApprove should only be called when setting an initial allowance, 58 | // or when resetting it to zero. To increase and decrease it, use 59 | // 'safeIncreaseAllowance' and 'safeDecreaseAllowance' 60 | // solhint-disable-next-line max-line-length 61 | require( 62 | (value == 0) || (token.allowance(address(this), spender) == 0), 63 | "SafeERC20: approve from non-zero to non-zero allowance" 64 | ); 65 | _callOptionalReturn( 66 | token, 67 | abi.encodeWithSelector(token.approve.selector, spender, value) 68 | ); 69 | } 70 | 71 | function safeIncreaseAllowance( 72 | IERC20 token, 73 | address spender, 74 | uint256 value 75 | ) internal { 76 | uint256 newAllowance = token.allowance(address(this), spender).add( 77 | value 78 | ); 79 | _callOptionalReturn( 80 | token, 81 | abi.encodeWithSelector( 82 | token.approve.selector, 83 | spender, 84 | newAllowance 85 | ) 86 | ); 87 | } 88 | 89 | function safeDecreaseAllowance( 90 | IERC20 token, 91 | address spender, 92 | uint256 value 93 | ) internal { 94 | uint256 newAllowance = token.allowance(address(this), spender).sub( 95 | value, 96 | "SafeERC20: decreased allowance below zero" 97 | ); 98 | _callOptionalReturn( 99 | token, 100 | abi.encodeWithSelector( 101 | token.approve.selector, 102 | spender, 103 | newAllowance 104 | ) 105 | ); 106 | } 107 | 108 | /** 109 | * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement 110 | * on the return value: the return value is optional (but if data is returned, it must not be false). 111 | * @param token The token targeted by the call. 112 | * @param data The call data (encoded using abi.encode or one of its variants). 113 | */ 114 | function _callOptionalReturn(IERC20 token, bytes memory data) private { 115 | // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since 116 | // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that 117 | // the target address contains contract code and also asserts for success in the low-level call. 118 | 119 | bytes memory returndata = address(token).functionCall( 120 | data, 121 | "SafeERC20: low-level call failed" 122 | ); 123 | if (returndata.length > 0) { 124 | // Return data is optional 125 | // solhint-disable-next-line max-line-length 126 | require( 127 | abi.decode(returndata, (bool)), 128 | "SafeERC20: ERC20 operation did not succeed" 129 | ); 130 | } 131 | } 132 | } 133 | -------------------------------------------------------------------------------- /contracts/libraries/SafeMath.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity >=0.6.6; 4 | 5 | /** 6 | * @dev Wrappers over Solidity's arithmetic operations with added overflow 7 | * checks. 8 | * 9 | * Arithmetic operations in Solidity wrap on overflow. This can easily result 10 | * in bugs, because programmers usually assume that an overflow raises an 11 | * error, which is the standard behavior in high level programming languages. 12 | * `SafeMath` restores this intuition by reverting the transaction when an 13 | * operation overflows. 14 | * 15 | * Using this library instead of the unchecked operations eliminates an entire 16 | * class of bugs, so it's recommended to use it always. 17 | */ 18 | library SafeMath { 19 | /** 20 | * @dev Returns the addition of two unsigned integers, with an overflow flag. 21 | * 22 | * _Available since v3.4._ 23 | */ 24 | function tryAdd(uint256 a, uint256 b) 25 | internal 26 | pure 27 | returns (bool, uint256) 28 | { 29 | uint256 c = a + b; 30 | if (c < a) return (false, 0); 31 | return (true, c); 32 | } 33 | 34 | /** 35 | * @dev Returns the substraction of two unsigned integers, with an overflow flag. 36 | * 37 | * _Available since v3.4._ 38 | */ 39 | function trySub(uint256 a, uint256 b) 40 | internal 41 | pure 42 | returns (bool, uint256) 43 | { 44 | if (b > a) return (false, 0); 45 | return (true, a - b); 46 | } 47 | 48 | /** 49 | * @dev Returns the multiplication of two unsigned integers, with an overflow flag. 50 | * 51 | * _Available since v3.4._ 52 | */ 53 | function tryMul(uint256 a, uint256 b) 54 | internal 55 | pure 56 | returns (bool, uint256) 57 | { 58 | // Gas optimization: this is cheaper than requiring 'a' not being zero, but the 59 | // benefit is lost if 'b' is also tested. 60 | // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522 61 | if (a == 0) return (true, 0); 62 | uint256 c = a * b; 63 | if (c / a != b) return (false, 0); 64 | return (true, c); 65 | } 66 | 67 | /** 68 | * @dev Returns the division of two unsigned integers, with a division by zero flag. 69 | * 70 | * _Available since v3.4._ 71 | */ 72 | function tryDiv(uint256 a, uint256 b) 73 | internal 74 | pure 75 | returns (bool, uint256) 76 | { 77 | if (b == 0) return (false, 0); 78 | return (true, a / b); 79 | } 80 | 81 | /** 82 | * @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag. 83 | * 84 | * _Available since v3.4._ 85 | */ 86 | function tryMod(uint256 a, uint256 b) 87 | internal 88 | pure 89 | returns (bool, uint256) 90 | { 91 | if (b == 0) return (false, 0); 92 | return (true, a % b); 93 | } 94 | 95 | /** 96 | * @dev Returns the addition of two unsigned integers, reverting on 97 | * overflow. 98 | * 99 | * Counterpart to Solidity's `+` operator. 100 | * 101 | * Requirements: 102 | * 103 | * - Addition cannot overflow. 104 | */ 105 | function add(uint256 a, uint256 b) internal pure returns (uint256) { 106 | uint256 c = a + b; 107 | require(c >= a, "SafeMath: addition overflow"); 108 | return c; 109 | } 110 | 111 | /** 112 | * @dev Returns the subtraction of two unsigned integers, reverting on 113 | * overflow (when the result is negative). 114 | * 115 | * Counterpart to Solidity's `-` operator. 116 | * 117 | * Requirements: 118 | * 119 | * - Subtraction cannot overflow. 120 | */ 121 | function sub(uint256 a, uint256 b) internal pure returns (uint256) { 122 | require(b <= a, "SafeMath: subtraction overflow"); 123 | return a - b; 124 | } 125 | 126 | /** 127 | * @dev Returns the multiplication of two unsigned integers, reverting on 128 | * overflow. 129 | * 130 | * Counterpart to Solidity's `*` operator. 131 | * 132 | * Requirements: 133 | * 134 | * - Multiplication cannot overflow. 135 | */ 136 | function mul(uint256 a, uint256 b) internal pure returns (uint256) { 137 | if (a == 0) return 0; 138 | uint256 c = a * b; 139 | require(c / a == b, "SafeMath: multiplication overflow"); 140 | return c; 141 | } 142 | 143 | /** 144 | * @dev Returns the integer division of two unsigned integers, reverting on 145 | * division by zero. The result is rounded towards zero. 146 | * 147 | * Counterpart to Solidity's `/` operator. Note: this function uses a 148 | * `revert` opcode (which leaves remaining gas untouched) while Solidity 149 | * uses an invalid opcode to revert (consuming all remaining gas). 150 | * 151 | * Requirements: 152 | * 153 | * - The divisor cannot be zero. 154 | */ 155 | function div(uint256 a, uint256 b) internal pure returns (uint256) { 156 | require(b > 0, "SafeMath: division by zero"); 157 | return a / b; 158 | } 159 | 160 | /** 161 | * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), 162 | * reverting when dividing by zero. 163 | * 164 | * Counterpart to Solidity's `%` operator. This function uses a `revert` 165 | * opcode (which leaves remaining gas untouched) while Solidity uses an 166 | * invalid opcode to revert (consuming all remaining gas). 167 | * 168 | * Requirements: 169 | * 170 | * - The divisor cannot be zero. 171 | */ 172 | function mod(uint256 a, uint256 b) internal pure returns (uint256) { 173 | require(b > 0, "SafeMath: modulo by zero"); 174 | return a % b; 175 | } 176 | 177 | /** 178 | * @dev Returns the subtraction of two unsigned integers, reverting with custom message on 179 | * overflow (when the result is negative). 180 | * 181 | * CAUTION: This function is deprecated because it requires allocating memory for the error 182 | * message unnecessarily. For custom revert reasons use {trySub}. 183 | * 184 | * Counterpart to Solidity's `-` operator. 185 | * 186 | * Requirements: 187 | * 188 | * - Subtraction cannot overflow. 189 | */ 190 | function sub( 191 | uint256 a, 192 | uint256 b, 193 | string memory errorMessage 194 | ) internal pure returns (uint256) { 195 | require(b <= a, errorMessage); 196 | return a - b; 197 | } 198 | 199 | /** 200 | * @dev Returns the integer division of two unsigned integers, reverting with custom message on 201 | * division by zero. The result is rounded towards zero. 202 | * 203 | * CAUTION: This function is deprecated because it requires allocating memory for the error 204 | * message unnecessarily. For custom revert reasons use {tryDiv}. 205 | * 206 | * Counterpart to Solidity's `/` operator. Note: this function uses a 207 | * `revert` opcode (which leaves remaining gas untouched) while Solidity 208 | * uses an invalid opcode to revert (consuming all remaining gas). 209 | * 210 | * Requirements: 211 | * 212 | * - The divisor cannot be zero. 213 | */ 214 | function div( 215 | uint256 a, 216 | uint256 b, 217 | string memory errorMessage 218 | ) internal pure returns (uint256) { 219 | require(b > 0, errorMessage); 220 | return a / b; 221 | } 222 | 223 | /** 224 | * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), 225 | * reverting with custom message when dividing by zero. 226 | * 227 | * CAUTION: This function is deprecated because it requires allocating memory for the error 228 | * message unnecessarily. For custom revert reasons use {tryMod}. 229 | * 230 | * Counterpart to Solidity's `%` operator. This function uses a `revert` 231 | * opcode (which leaves remaining gas untouched) while Solidity uses an 232 | * invalid opcode to revert (consuming all remaining gas). 233 | * 234 | * Requirements: 235 | * 236 | * - The divisor cannot be zero. 237 | */ 238 | function mod( 239 | uint256 a, 240 | uint256 b, 241 | string memory errorMessage 242 | ) internal pure returns (uint256) { 243 | require(b > 0, errorMessage); 244 | return a % b; 245 | } 246 | } 247 | -------------------------------------------------------------------------------- /contracts/libraries/UniswapV2Library.sol: -------------------------------------------------------------------------------- 1 | //SPDX-License-Identifier: MIT 2 | pragma solidity =0.6.6; 3 | 4 | import "../interfaces/IUniswapV2Pair.sol"; 5 | 6 | import "./SafeMath.sol"; 7 | 8 | library UniswapV2Library { 9 | using SafeMath for uint256; 10 | 11 | // returns sorted token addresses, used to handle return values from pairs sorted in this order 12 | function sortTokens(address tokenA, address tokenB) 13 | internal 14 | pure 15 | returns (address token0, address token1) 16 | { 17 | require(tokenA != tokenB, "UniswapV2Library: IDENTICAL_ADDRESSES"); 18 | (token0, token1) = tokenA < tokenB 19 | ? (tokenA, tokenB) 20 | : (tokenB, tokenA); 21 | require(token0 != address(0), "UniswapV2Library: ZERO_ADDRESS"); 22 | } 23 | 24 | // calculates the CREATE2 address for a pair without making any external calls 25 | function pairFor( 26 | address factory, 27 | address tokenA, 28 | address tokenB 29 | ) internal pure returns (address pair) { 30 | (address token0, address token1) = sortTokens(tokenA, tokenB); 31 | pair = address( 32 | uint256( 33 | keccak256( 34 | abi.encodePacked( 35 | hex"ff", 36 | factory, 37 | keccak256(abi.encodePacked(token0, token1)), 38 | hex"96e8ac4277198ff8b6f785478aa9a39f403cb768dd02cbee326c3e7da348845f" // init code hash 39 | ) 40 | ) 41 | ) 42 | ); 43 | } 44 | 45 | // fetches and sorts the reserves for a pair 46 | function getReserves( 47 | address factory, 48 | address tokenA, 49 | address tokenB 50 | ) internal view returns (uint256 reserveA, uint256 reserveB) { 51 | (address token0, ) = sortTokens(tokenA, tokenB); 52 | (uint256 reserve0, uint256 reserve1, ) = IUniswapV2Pair( 53 | pairFor(factory, tokenA, tokenB) 54 | ).getReserves(); 55 | (reserveA, reserveB) = tokenA == token0 56 | ? (reserve0, reserve1) 57 | : (reserve1, reserve0); 58 | } 59 | 60 | // given some amount of an asset and pair reserves, returns an equivalent amount of the other asset 61 | function quote( 62 | uint256 amountA, 63 | uint256 reserveA, 64 | uint256 reserveB 65 | ) internal pure returns (uint256 amountB) { 66 | require(amountA > 0, "UniswapV2Library: INSUFFICIENT_AMOUNT"); 67 | require( 68 | reserveA > 0 && reserveB > 0, 69 | "UniswapV2Library: INSUFFICIENT_LIQUIDITY" 70 | ); 71 | amountB = amountA.mul(reserveB) / reserveA; 72 | } 73 | 74 | // given an input amount of an asset and pair reserves, returns the maximum output amount of the other asset 75 | function getAmountOut( 76 | uint256 amountIn, 77 | uint256 reserveIn, 78 | uint256 reserveOut 79 | ) internal pure returns (uint256 amountOut) { 80 | require(amountIn > 0, "UniswapV2Library: INSUFFICIENT_INPUT_AMOUNT"); 81 | require( 82 | reserveIn > 0 && reserveOut > 0, 83 | "UniswapV2Library: INSUFFICIENT_LIQUIDITY" 84 | ); 85 | uint256 amountInWithFee = amountIn.mul(997); 86 | uint256 numerator = amountInWithFee.mul(reserveOut); 87 | uint256 denominator = reserveIn.mul(1000).add(amountInWithFee); 88 | amountOut = numerator / denominator; 89 | } 90 | 91 | // given an output amount of an asset and pair reserves, returns a required input amount of the other asset 92 | function getAmountIn( 93 | uint256 amountOut, 94 | uint256 reserveIn, 95 | uint256 reserveOut 96 | ) internal pure returns (uint256 amountIn) { 97 | require(amountOut > 0, "UniswapV2Library: INSUFFICIENT_OUTPUT_AMOUNT"); 98 | require( 99 | reserveIn > 0 && reserveOut > 0, 100 | "UniswapV2Library: INSUFFICIENT_LIQUIDITY" 101 | ); 102 | uint256 numerator = reserveIn.mul(amountOut).mul(1000); 103 | uint256 denominator = reserveOut.sub(amountOut).mul(997); 104 | amountIn = (numerator / denominator).add(1); 105 | } 106 | 107 | // performs chained getAmountOut calculations on any number of pairs 108 | function getAmountsOut( 109 | address factory, 110 | uint256 amountIn, 111 | address[] memory path 112 | ) internal view returns (uint256[] memory amounts) { 113 | require(path.length >= 2, "UniswapV2Library: INVALID_PATH"); 114 | amounts = new uint256[](path.length); 115 | amounts[0] = amountIn; 116 | for (uint256 i; i < path.length - 1; i++) { 117 | (uint256 reserveIn, uint256 reserveOut) = getReserves( 118 | factory, 119 | path[i], 120 | path[i + 1] 121 | ); 122 | amounts[i + 1] = getAmountOut(amounts[i], reserveIn, reserveOut); 123 | } 124 | } 125 | 126 | // performs chained getAmountIn calculations on any number of pairs 127 | function getAmountsIn( 128 | address factory, 129 | uint256 amountOut, 130 | address[] memory path 131 | ) internal view returns (uint256[] memory amounts) { 132 | require(path.length >= 2, "UniswapV2Library: INVALID_PATH"); 133 | amounts = new uint256[](path.length); 134 | amounts[amounts.length - 1] = amountOut; 135 | for (uint256 i = path.length - 1; i > 0; i--) { 136 | (uint256 reserveIn, uint256 reserveOut) = getReserves( 137 | factory, 138 | path[i - 1], 139 | path[i] 140 | ); 141 | amounts[i - 1] = getAmountIn(amounts[i], reserveIn, reserveOut); 142 | } 143 | } 144 | } 145 | -------------------------------------------------------------------------------- /hardhat.config.js: -------------------------------------------------------------------------------- 1 | require("@nomiclabs/hardhat-waffle"); 2 | 3 | // This is a sample Hardhat task. To learn how to create your own go to 4 | // https://hardhat.org/guides/create-task.html 5 | task("accounts", "Prints the list of accounts", async (taskArgs, hre) => { 6 | const accounts = await hre.ethers.getSigners(); 7 | 8 | for (const account of accounts) { 9 | console.log(account.address); 10 | } 11 | }); 12 | 13 | // You need to export an object to set up your config 14 | // Go to https://hardhat.org/config/ to learn more 15 | 16 | /** 17 | * @type import('hardhat/config').HardhatUserConfig 18 | */ 19 | module.exports = { 20 | solidity: { 21 | compilers: [ 22 | { version: "0.5.5" }, 23 | { version: "0.6.6" }, 24 | { version: "0.8.8" }, 25 | ], 26 | }, 27 | networks: { 28 | hardhat: { 29 | forking: { 30 | url: "https://bsc-dataseed.binance.org/", 31 | }, 32 | }, 33 | testnet: { 34 | url: "https://data-seed-prebsc-1-s1.binance.org:8545/", 35 | chainId: 97, 36 | accounts: [ 37 | "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80", 38 | ], 39 | }, 40 | mainnet: { 41 | url: "https://bsc-dataseed.binance.org/", 42 | chainId: 56, 43 | }, 44 | }, 45 | }; 46 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "scripts":{ 3 | "test": "npx hardhat test && --openssl-legacy-provider" 4 | }, 5 | "devDependencies": { 6 | "@nomiclabs/hardhat-ethers": "^2.0.5", 7 | "@nomiclabs/hardhat-waffle": "^2.0.3", 8 | "chai": "^4.3.6", 9 | "ethereum-waffle": "^3.4.4", 10 | "ethers": "^5.6.4", 11 | "hardhat": "^2.9.3" 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /scripts/deploy.js: -------------------------------------------------------------------------------- 1 | const { ethers } = require("hardhat"); 2 | 3 | async function main() { 4 | const [deployer] = await ethers.getSigners(); 5 | 6 | console.log("Deploying contracts with the account:", deployer.address); 7 | 8 | console.log("Account balance:", (await deployer.getBalance()).toString()); 9 | 10 | const Token = await ethers.getContractFactory("PancakeFlashSwap"); 11 | const token = await Token.deploy(); 12 | 13 | console.log("Token address:", token.address); 14 | } 15 | 16 | main() 17 | .then(() => process.exit(0)) 18 | .catch((error) => { 19 | console.error(error); 20 | process.exit(1); 21 | }); 22 | -------------------------------------------------------------------------------- /test/tester.js: -------------------------------------------------------------------------------- 1 | const { expect, assert } = require("chai"); 2 | const { ethers } = require("hardhat"); 3 | const { impersonateFundErc20 } = require("../utils/utilities"); 4 | 5 | const { 6 | abi, 7 | } = require("../artifacts/contracts/interfaces/IERC20.sol/IERC20.json"); 8 | 9 | const provider = waffle.provider; 10 | 11 | describe("FlashSwap Contract", () => { 12 | let FLASHSWAP, 13 | BORROW_AMOUNT, 14 | FUND_AMOUNT, 15 | initialFundingHuman, 16 | txArbitrage, 17 | gasUsedUSD; 18 | 19 | const DECIMALS = 18; 20 | 21 | const BUSD_WHALE = "0x8894e0a0c962cb723c1976a4421c95949be2d4e3"; 22 | const WBNB = "0xbb4cdb9cbd36b01bd1cbaebf2de08d9173bc095c"; 23 | const BUSD = "0xe9e7CEA3DedcA5984780Bafc599bD69ADd087D56"; 24 | const CAKE = "0x0e09fabb73bd3ade0a17ecc321fd13a19e81ce82"; 25 | const DOT = "0x55d398326f99059ff775485246999027b3197955"; 26 | 27 | const BASE_TOKEN_ADDRESS = BUSD; 28 | 29 | const tokenBase = new ethers.Contract(BASE_TOKEN_ADDRESS, abi, provider); 30 | 31 | beforeEach(async () => { 32 | // Ensure that the WHALE has a balance 33 | const whale_balance = await provider.getBalance(BUSD_WHALE); 34 | expect(whale_balance).not.equal("0"); 35 | 36 | const amountToBorrowInHuman = "1"; 37 | BORROW_AMOUNT = ethers.utils.parseUnits(amountToBorrowInHuman, DECIMALS); 38 | initialFundingHuman = "10"; 39 | FUND_AMOUNT = ethers.utils.parseUnits(initialFundingHuman, DECIMALS); 40 | const flashSwapFactory = await ethers.getContractFactory( 41 | "PancakeFlashSwap" 42 | ); 43 | FLASHSWAP = await flashSwapFactory.deploy(); 44 | await FLASHSWAP.deployed(); 45 | 46 | await impersonateFundErc20( 47 | tokenBase, 48 | BUSD_WHALE, 49 | FLASHSWAP.address, 50 | initialFundingHuman 51 | ); 52 | }); 53 | 54 | describe("Arbitrage Execution", () => { 55 | it("ensures contract is funded", async () => { 56 | const tokenBalance = await FLASHSWAP.getFlashContractBalance( 57 | BASE_TOKEN_ADDRESS 58 | ); 59 | 60 | const tokenBalances = await FLASHSWAP.getPairBalance(WBNB, BUSD); 61 | console.log( 62 | "CHECK THIS ", 63 | ethers.utils.formatUnits(tokenBalances[0], DECIMALS), 64 | ethers.utils.formatUnits(tokenBalances[1], DECIMALS), 65 | ethers.utils.formatUnits(tokenBalances[2], DECIMALS), 66 | ethers.utils.formatUnits(tokenBalances[3], DECIMALS), 67 | tokenBalances[3].toString(), 68 | 69 | " CHECK THIS" 70 | ); 71 | const tokenBalanceInHuman = ethers.utils.formatUnits( 72 | tokenBalance, 73 | DECIMALS 74 | ); 75 | expect(Number(tokenBalanceInHuman)).equal(Number(initialFundingHuman)); 76 | }); 77 | 78 | it("excutes an arbitrage", async () => { 79 | txArbitrage = await FLASHSWAP.startLoan( 80 | BUSD, 81 | WBNB, 82 | CAKE, 83 | DOT, 84 | BORROW_AMOUNT 85 | ); 86 | 87 | const balanceAfterArbitrage = await FLASHSWAP.getFlashContractBalance( 88 | BASE_TOKEN_ADDRESS 89 | ); 90 | const formattedAmount = ethers.utils.formatUnits( 91 | balanceAfterArbitrage, 92 | DECIMALS 93 | ); 94 | 95 | const currentBalance = await FLASHSWAP.getFlashContractBalance( 96 | BASE_TOKEN_ADDRESS 97 | ); 98 | 99 | const currentBalanceCAKE = await FLASHSWAP.getFlashContractBalance(CAKE); 100 | 101 | console.log("BUSD: ", ethers.utils.formatUnits(currentBalance, DECIMALS)); 102 | console.log( 103 | "CAKE: ", 104 | ethers.utils.formatUnits(currentBalanceCAKE, DECIMALS) 105 | ); 106 | 107 | assert(txArbitrage); 108 | }); 109 | 110 | it("checks if a trade is profitable", async () => { 111 | let startTrade = 112 | await FLASHSWAP.checkTriangularTradeProfitabilityOnBlockCall( 113 | BUSD, 114 | CAKE, 115 | DOT, 116 | BORROW_AMOUNT 117 | ); 118 | console.log("Start trade status", startTrade); 119 | expect(startTrade).not(undefined); 120 | }); 121 | }); 122 | }); 123 | -------------------------------------------------------------------------------- /utils/utilities.js: -------------------------------------------------------------------------------- 1 | const { network, ethers } = require("hardhat"); 2 | 3 | const fundErc20 = async (contract, sender, recepient, amount) => { 4 | const FUND_AMOUNT = ethers.utils.parseUnits(amount, 18); 5 | // fund erc20 token to the contract 6 | const whale = await ethers.getSigner(sender); 7 | 8 | const contractSigner = contract.connect(whale); 9 | await contractSigner.transfer(recepient, FUND_AMOUNT); 10 | }; 11 | 12 | const impersonateFundErc20 = async (contract, sender, recepient, amount) => { 13 | await network.provider.request({ 14 | method: "hardhat_impersonateAccount", 15 | params: [sender], 16 | }); 17 | 18 | // fund baseToken to the contract 19 | await fundErc20(contract, sender, recepient, amount); 20 | await network.provider.request({ 21 | method: "hardhat_stopImpersonatingAccount", 22 | params: [sender], 23 | }); 24 | }; 25 | 26 | module.exports = { 27 | impersonateFundErc20: impersonateFundErc20, 28 | }; 29 | --------------------------------------------------------------------------------