├── .DS_Store ├── .gitignore ├── README.md ├── contracts ├── .DS_Store ├── FlashTri.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 ├── robots.txt ├── scripts ├── deploy.js └── trial.js ├── test └── tester.js └── utils └── utilities.js /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CryptoWizardsNet/triangular_arbitrage_flashswap/b2146f2012080ed930bc7e61b3b98b6412a4f492/.DS_Store -------------------------------------------------------------------------------- /.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 | # Basic Sample Hardhat Project 2 | 3 | Forks mainnet for testing arbitrage opportunities. 4 | 5 | 1. UPDATE YOUR TESTNET or Mainnet PRIVATE KEY 6 | hardhat.config.js 7 | 8 | 2. npm install 9 | 10 | 3. npx hardhat test 11 | 12 | You will likely see a "Arbitrage not profitable" error come up, if the arbitrage is not profitable. 13 | -------------------------------------------------------------------------------- /contracts/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CryptoWizardsNet/triangular_arbitrage_flashswap/b2146f2012080ed930bc7e61b3b98b6412a4f492/contracts/.DS_Store -------------------------------------------------------------------------------- /contracts/FlashTri.sol: -------------------------------------------------------------------------------- 1 | //SPDX-License-Identifier: MIT 2 | pragma solidity >=0.6.6; 3 | 4 | import "hardhat/console.sol"; 5 | 6 | // import "./interfaces/Uniswap.sol"; 7 | import "./libraries/UniswapV2Library.sol"; 8 | import "./interfaces/IERC20.sol"; 9 | import "./interfaces/IUniswapV2Router01.sol"; 10 | import "./interfaces/IUniswapV2Router02.sol"; 11 | import "./interfaces/IUniswapV2Pair.sol"; 12 | import "./interfaces/IUniswapV2Factory.sol"; 13 | import "./interfaces/IERC20.sol"; 14 | import "./libraries/SafeERC20.sol"; 15 | 16 | contract ContractFlashTri { 17 | using SafeERC20 for IERC20; 18 | 19 | // Trade Variables 20 | address private constant WBNB = 0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c; 21 | address private constant BUSD = 0xe9e7CEA3DedcA5984780Bafc599bD69ADd087D56; 22 | address private constant USDC = 0x8AC76a51cc950d9822D68b83fE1Ad97B32Cd580d; 23 | address private constant CAKE = 0x0E09FaBB73Bd3Ade0a17ECC321fD13a19e81cE82; 24 | uint256 private constant MAX_INT = 25 | 115792089237316195423570985008687907853269984665640564039457584007913129639935; 26 | 27 | // Trade Struct 28 | struct TradeDetails { 29 | address factoryT1; 30 | address factoryT2; 31 | address factoryT3; 32 | address routerT1; 33 | address routerT2; 34 | address routerT3; 35 | address tokenA; 36 | address tokenB; 37 | address tokenC; 38 | } 39 | 40 | // Trade Mapping 41 | mapping(address => TradeDetails) public tradeDetails; 42 | 43 | // FUND SWAP CONTRACT 44 | // Provides a runction to allow contract to be funded 45 | function fundFlashSwapContract( 46 | address _owner, 47 | address _token, 48 | uint256 _amount 49 | ) public { 50 | IERC20(_token).transferFrom(_owner, address(this), _amount); 51 | } 52 | 53 | // GET CONTRACT BALANCE 54 | // Allows public view of balance for contract 55 | function getBalanceOfToken(address _address) public view returns (uint256) { 56 | return IERC20(_address).balanceOf(address(this)); 57 | } 58 | 59 | // PLACE A TRADE 60 | // Executes placing a trade 61 | function placeTrade( 62 | address _factory, 63 | address _router, 64 | address _fromToken, 65 | address _toToken, 66 | uint256 _amountIn 67 | ) private returns (uint256) { 68 | address pair = IUniswapV2Factory(_factory).getPair( 69 | _fromToken, 70 | _toToken 71 | ); 72 | require(pair != address(0), "Pool does not exist"); 73 | 74 | // Perform Arbitrage - Swap for another token on Uniswap 75 | address[] memory path = new address[](2); 76 | path[0] = _fromToken; 77 | path[1] = _toToken; 78 | 79 | uint256 amountRequired = IUniswapV2Router01(_router).getAmountsOut( 80 | _amountIn, 81 | path 82 | )[1]; 83 | 84 | uint256 deadline = block.timestamp + 30 minutes; 85 | 86 | uint256 amountReceived = IUniswapV2Router01(_router) 87 | .swapExactTokensForTokens( 88 | _amountIn, // amountIn 89 | amountRequired, // amountOutMin 90 | path, // contract addresses 91 | address(this), // address to 92 | deadline // block deadline 93 | )[1]; 94 | 95 | // Return output 96 | require(amountReceived > 0, "Aborted Tx: Trade returned zero"); 97 | return amountReceived; 98 | } 99 | 100 | // CHECK PROFITABILITY 101 | // Checks whether output > input 102 | function checkProfitability(uint256 _input, uint256 _output) 103 | private 104 | pure 105 | returns (bool) 106 | { 107 | return _output > _input; 108 | } 109 | 110 | // INITIATE ARBITRAGE 111 | // Begins the arbitrage for receiving a Flash Loan 112 | function triangularArbitrage( 113 | address[3] calldata _factories, 114 | address[3] calldata _routers, 115 | address[3] calldata _tokens, 116 | uint256 _amountBorrow 117 | ) external { 118 | // Approve contract to make transactions 119 | if (_routers[0] == _routers[1] && _routers[1] == _routers[2]) { 120 | IERC20(_tokens[0]).approve(address(_routers[0]), MAX_INT); 121 | IERC20(_tokens[1]).approve(address(_routers[0]), MAX_INT); 122 | IERC20(_tokens[2]).approve(address(_routers[0]), MAX_INT); 123 | } else if (_routers[0] == _routers[1] && _routers[1] != _routers[2]) { 124 | IERC20(_tokens[0]).approve(address(_routers[0]), MAX_INT); 125 | IERC20(_tokens[1]).approve(address(_routers[0]), MAX_INT); 126 | IERC20(_tokens[2]).approve(address(_routers[0]), MAX_INT); 127 | IERC20(_tokens[1]).approve(address(_routers[2]), MAX_INT); 128 | IERC20(_tokens[2]).approve(address(_routers[2]), MAX_INT); 129 | } else if (_routers[0] != _routers[1] && _routers[1] == _routers[2]) { 130 | IERC20(_tokens[0]).approve(address(_routers[0]), MAX_INT); 131 | IERC20(_tokens[1]).approve(address(_routers[0]), MAX_INT); 132 | IERC20(_tokens[0]).approve(address(_routers[1]), MAX_INT); 133 | IERC20(_tokens[1]).approve(address(_routers[1]), MAX_INT); 134 | IERC20(_tokens[2]).approve(address(_routers[1]), MAX_INT); 135 | } else if (_routers[0] != _routers[1] && _routers[1] != _routers[2]) { 136 | IERC20(_tokens[0]).approve(address(_routers[0]), MAX_INT); 137 | IERC20(_tokens[1]).approve(address(_routers[0]), MAX_INT); 138 | IERC20(_tokens[2]).approve(address(_routers[0]), MAX_INT); 139 | IERC20(_tokens[1]).approve(address(_routers[1]), MAX_INT); 140 | IERC20(_tokens[2]).approve(address(_routers[1]), MAX_INT); 141 | } 142 | 143 | // Assign dummy token change if needed 144 | address dummyToken; 145 | if (_tokens[0] != WBNB && _tokens[1] != WBNB && _tokens[2] != WBNB) { 146 | dummyToken = WBNB; 147 | } else if ( 148 | _tokens[0] != BUSD && _tokens[1] != BUSD && _tokens[2] != BUSD 149 | ) { 150 | dummyToken = BUSD; 151 | } else if ( 152 | _tokens[0] != CAKE && _tokens[1] != CAKE && _tokens[2] != CAKE 153 | ) { 154 | dummyToken = CAKE; 155 | } else { 156 | dummyToken = USDC; 157 | } 158 | 159 | // Get Factory pair address for combined tokens 160 | address pair = IUniswapV2Factory(_factories[0]).getPair( 161 | _tokens[0], 162 | dummyToken 163 | ); 164 | require(pair != address(0), "Pool does not exist"); 165 | 166 | // Figure out which token (0 or 1) has the amount and assign 167 | // Assumes borrowing tokenA 168 | address token0 = IUniswapV2Pair(pair).token0(); 169 | address token1 = IUniswapV2Pair(pair).token1(); 170 | uint256 amount0Out = _tokens[0] == token0 ? _amountBorrow : 0; 171 | uint256 amount1Out = _tokens[0] == token1 ? _amountBorrow : 0; 172 | 173 | // Passing data triggers pancakeCall as this is what constitutes a loan 174 | // TokenA is the token being borrowed 175 | bytes memory data = abi.encode(_tokens[0], _amountBorrow, msg.sender); 176 | 177 | // Save trade data to tradeDetails mapping 178 | tradeDetails[msg.sender] = TradeDetails( 179 | _factories[0], 180 | _factories[1], 181 | _factories[2], 182 | _routers[0], 183 | _routers[1], 184 | _routers[2], 185 | _tokens[0], 186 | _tokens[1], 187 | _tokens[2] 188 | ); 189 | 190 | // Execute the initial swap with the loan 191 | IUniswapV2Pair(pair).swap(amount0Out, amount1Out, address(this), data); 192 | } 193 | 194 | // RECEIVE LOAN AND EXECUTE TRADES 195 | // This function is called from the .swap in startArbitrage if there is byte data 196 | function pancakeCall( 197 | address _sender, 198 | uint256 _amount0, 199 | uint256 _amount1, 200 | bytes calldata _data 201 | ) external { 202 | // Decode data for calculating repayment 203 | (address tokenA, uint256 amountBorrow, address sender) = abi.decode( 204 | _data, 205 | (address, uint256, address) 206 | ); 207 | 208 | // Ensure this request came from the contract 209 | address token0 = IUniswapV2Pair(msg.sender).token0(); 210 | address token1 = IUniswapV2Pair(msg.sender).token1(); 211 | address pair = IUniswapV2Factory(tradeDetails[sender].factoryT1) 212 | .getPair(token0, token1); 213 | require(msg.sender == pair, "The sender needs to match the pair"); 214 | require(_sender == address(this), "Sender should match this contract"); 215 | 216 | // Calculate amount to repay at the end 217 | uint256 fee = ((amountBorrow * 3) / 997) + 1; 218 | uint256 amountToRepay = amountBorrow + fee; 219 | 220 | // Extract amount of acquired token going into next trade 221 | uint256 loanAmount = _amount0 > 0 ? _amount0 : _amount1; 222 | 223 | // Trade 1 224 | uint256 trade1AcquiredCoin = placeTrade( 225 | tradeDetails[sender].factoryT1, 226 | tradeDetails[sender].routerT1, 227 | tradeDetails[sender].tokenA, 228 | tradeDetails[sender].tokenB, 229 | loanAmount 230 | ); 231 | 232 | // Trade 2 233 | uint256 trade2AcquiredCoin = placeTrade( 234 | tradeDetails[sender].factoryT2, 235 | tradeDetails[sender].routerT2, 236 | tradeDetails[sender].tokenB, 237 | tradeDetails[sender].tokenC, 238 | trade1AcquiredCoin 239 | ); 240 | 241 | // Trade 3 242 | uint256 trade3AcquiredCoin = placeTrade( 243 | tradeDetails[sender].factoryT3, 244 | tradeDetails[sender].routerT3, 245 | tradeDetails[sender].tokenC, 246 | tradeDetails[sender].tokenA, 247 | trade2AcquiredCoin 248 | ); 249 | 250 | // Profit check 251 | bool profCheck = checkProfitability(loanAmount, trade3AcquiredCoin); 252 | require(profCheck, "Arbitrage not profitable"); 253 | 254 | // Pay yourself back first 255 | IERC20 otherToken = IERC20(tradeDetails[sender].tokenA); 256 | otherToken.transfer(sender, trade3AcquiredCoin - amountToRepay); 257 | 258 | // Pay loan back 259 | // TokenA as borrowed token 260 | IERC20(tokenA).transfer(pair, amountToRepay); 261 | } 262 | } 263 | -------------------------------------------------------------------------------- /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 | { 23 | version: "0.5.5", 24 | }, 25 | { 26 | version: "0.6.6", 27 | }, 28 | { 29 | version: "0.8.0", 30 | }, 31 | ], 32 | }, 33 | networks: { 34 | hardhat: { 35 | forking: { 36 | url: "https://bsc-dataseed.binance.org/", 37 | }, 38 | }, 39 | testnet: { 40 | url: "https://data-seed-prebsc-1-s1.binance.org:8545", 41 | chainId: 97, 42 | accounts: ["<0x..ENTER YOUR PRIVATE KEY>"], 43 | }, 44 | mainnet: { 45 | url: "https://bsc-dataseed.binance.org/", 46 | chainId: 56, 47 | accounts: ["<0x..ENTER YOUR PRIVATE KEY>"], 48 | }, 49 | }, 50 | }; 51 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "devDependencies": { 3 | "@nomiclabs/hardhat-ethers": "^2.0.5", 4 | "@nomiclabs/hardhat-waffle": "^2.0.3", 5 | "chai": "^4.3.6", 6 | "ethereum-waffle": "^3.4.4", 7 | "ethers": "^5.6.5", 8 | "hardhat": "^2.9.3" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /robots.txt: -------------------------------------------------------------------------------- 1 | User-agent: * 2 | Disallow: / 3 | -------------------------------------------------------------------------------- /scripts/deploy.js: -------------------------------------------------------------------------------- 1 | async function main() { 2 | const [deployer] = await ethers.getSigners(); 3 | 4 | console.log("Deploying contracts with the account:", deployer.address); 5 | 6 | console.log("Account balance:", (await deployer.getBalance()).toString()); 7 | 8 | const Token = await ethers.getContractFactory("ContractFlashTri"); 9 | const token = await Token.deploy(); 10 | 11 | console.log("Token address:", token.address); 12 | } 13 | 14 | main() 15 | .then(() => process.exit(0)) 16 | .catch((error) => { 17 | console.error(error); 18 | process.exit(1); 19 | }); 20 | -------------------------------------------------------------------------------- /scripts/trial.js: -------------------------------------------------------------------------------- 1 | const { ethers } = require("ethers"); 2 | 3 | const { 4 | abi: ERC20ABI, 5 | } = require("../artifacts/contracts/interfaces/IERC20.sol/IERC20.json"); 6 | 7 | const { 8 | abi: FlashABI, 9 | } = require("../artifacts/contracts/FlashTri.sol/ContractFlashTri.json"); 10 | 11 | // Addresses 12 | const deployedAddress = "0x5B7ff4E29697ebed11eE4c10B04307cCEe7f6742"; // DEPLOYED CONTRACT 13 | const factoryPancake = "0xcA143Ce32Fe78f1f7019d7d551a6402fC5350c73"; 14 | const routerPancake = "0x10ED43C718714eb63d5aA57B78B54704E256024E"; 15 | const tokenA = "0xe9e7CEA3DedcA5984780Bafc599bD69ADd087D56"; 16 | const tokenB = "0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c"; 17 | const tokenC = "0x89675DcCFE0c19bca178A0E0384Bd8E273a45cbA"; 18 | 19 | // Inputs 20 | const factories = [factoryPancake, factoryPancake, factoryPancake]; 21 | const routers = [routerPancake, routerPancake, routerPancake]; 22 | const tokens = [tokenA, tokenB, tokenC]; 23 | const borrowAmount = ethers.utils.parseUnits("2", 18); 24 | console.log(borrowAmount.toString()); 25 | 26 | // Provider (MAINNET) 27 | const provider = new ethers.providers.JsonRpcProvider( 28 | "https://bsc-dataseed.binance.org/" 29 | ); 30 | 31 | // Wallet Signer (MAINNET) 32 | const privateKey = 33 | "0xc3a03c9f54d1e455a95cbabe61fc1344d0c0c8fd8df8c3564d3d94756b377c35"; 34 | const walletSigner = new ethers.Wallet(privateKey, provider); 35 | 36 | // Contract (MAINNET) 37 | const contractFlashSwap = new ethers.Contract( 38 | deployedAddress, 39 | FlashABI, 40 | walletSigner 41 | ); 42 | 43 | // Call Arbitrage 44 | async function getArbitrage() { 45 | const arbTx = await contractFlashSwap.triangularArbitrage( 46 | factories, 47 | routers, 48 | tokens, 49 | borrowAmount, 50 | { 51 | gasLimit: 6000000, 52 | gasPrice: ethers.utils.parseUnits("5.5", "gwei"), 53 | } 54 | ); 55 | console.log(arbTx); 56 | } 57 | 58 | // 0.4519 59 | getArbitrage(); 60 | 61 | // txHash = "0x402dcce770515ec0aea2193d6a6dbe85e954ce885207db8b683de0dfacd4740a"; 62 | -------------------------------------------------------------------------------- /test/tester.js: -------------------------------------------------------------------------------- 1 | // IMPORTS 2 | const { expect, assert } = require("chai"); 3 | const { ethers } = require("hardhat"); 4 | const { impersonateFundErc20 } = require("../utils/utilities"); 5 | 6 | const { 7 | abi, 8 | } = require("../artifacts/contracts/interfaces/IERC20.sol/IERC20.json"); 9 | 10 | const provider = waffle.provider; 11 | 12 | describe("Token contract", () => { 13 | let FLASHSWAP, 14 | BORROW_AMOUNT, 15 | FUND_AMOUNT, 16 | initialFundingHuman, 17 | txArbitrage, 18 | gasUsedUSD; 19 | 20 | const DECIMALS = 18; 21 | 22 | const FACTORY_PANCAKE = "0xcA143Ce32Fe78f1f7019d7d551a6402fC5350c73"; 23 | const FACTORY_APESWAP = "0x0841BD0B734E4F5853f0dD8d7Ea041c241fb0Da6"; 24 | const ROUTER_PANCAKE = "0x10ED43C718714eb63d5aA57B78B54704E256024E"; 25 | const ROUTER_APESWAP = "0xcF0feBd3f17CEf5b47b0cD257aCf6025c5BFf3b7"; 26 | 27 | const BUSD_WHALE = "0xf977814e90da44bfa03b6295a0616a897441acec"; 28 | const TOKEN_A = "0xe9e7CEA3DedcA5984780Bafc599bD69ADd087D56"; // BUSD 29 | const TOKEN_B = "0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c"; // WBNB 30 | const TOKEN_C = "0x37dfACfaeDA801437Ff648A1559d73f4C40aAcb7"; // APYS 31 | 32 | // Assume borrowing Token A 33 | const tokenBase = new ethers.Contract(TOKEN_A, abi, provider); 34 | 35 | beforeEach(async () => { 36 | // Get owner as signer 37 | [owner] = await ethers.getSigners(); 38 | 39 | // Ensure Whale has balance 40 | const whale_balance = await provider.getBalance(BUSD_WHALE); 41 | expect(whale_balance).not.equal("0"); 42 | 43 | // Deploy smart contract 44 | const FlashSwap = await ethers.getContractFactory("ContractFlashTri"); 45 | FLASHSWAP = await FlashSwap.deploy(); 46 | await FLASHSWAP.deployed(); 47 | 48 | // Configure Borrowing 49 | const borrowAmountHuman = "1"; // borrow anything, even 1m 50 | BORROW_AMOUNT = ethers.utils.parseUnits(borrowAmountHuman, DECIMALS); 51 | 52 | // Configure Funding 53 | initialFundingHuman = "100"; // 100 assigned just to pass payback of loan whilst testing 54 | FUND_AMOUNT = ethers.utils.parseUnits(initialFundingHuman, DECIMALS); 55 | 56 | await impersonateFundErc20( 57 | tokenBase, 58 | BUSD_WHALE, 59 | FLASHSWAP.address, 60 | initialFundingHuman 61 | ); 62 | }); 63 | 64 | describe("Arbitrage execution", () => { 65 | it("ensures contract is funded", async () => { 66 | const flashSwapBalance = await FLASHSWAP.getBalanceOfToken(TOKEN_A); 67 | 68 | const flashSwapBalanceHuman = ethers.utils.formatUnits( 69 | flashSwapBalance, 70 | DECIMALS 71 | ); 72 | expect(Number(flashSwapBalanceHuman)).equal(Number(initialFundingHuman)); 73 | }); 74 | 75 | it("executes the arbitrage", async () => { 76 | // console.log(ethers.utils.formatUnits("8211184147365292123", 18)); 77 | 78 | txArbitrage = await FLASHSWAP.triangularArbitrage( 79 | [FACTORY_PANCAKE, FACTORY_PANCAKE, FACTORY_PANCAKE], 80 | [ROUTER_PANCAKE, ROUTER_PANCAKE, ROUTER_PANCAKE], 81 | [TOKEN_A, TOKEN_B, TOKEN_C], 82 | BORROW_AMOUNT 83 | ); 84 | 85 | assert(txArbitrage); 86 | 87 | // Print balances 88 | const contractBalanceTOKENA = await FLASHSWAP.getBalanceOfToken(TOKEN_A); 89 | 90 | const formattedBalTOKENA = Number( 91 | ethers.utils.formatUnits(contractBalanceTOKENA, 18) 92 | ); 93 | const contractBalanceTOKENB = await FLASHSWAP.getBalanceOfToken(TOKEN_B); 94 | 95 | console.log("Balance Of TOKEN A: " + formattedBalTOKENA); 96 | 97 | console.log( 98 | "Balance Of TOKEN B: " + 99 | ethers.utils.formatUnits(contractBalanceTOKENB, 18) 100 | ); 101 | }); 102 | 103 | it("provides GAS output", async () => { 104 | const txReceipt = await provider.getTransactionReceipt(txArbitrage.hash); 105 | 106 | const effGasPrice = txReceipt.effectiveGasPrice; 107 | const txGasUsed = txReceipt.gasUsed; 108 | const gasUsedBNB = effGasPrice * txGasUsed; 109 | gasUsedUSD = ethers.utils.formatUnits(gasUsedBNB, 18) * 395; // USD to BNB price today 110 | 111 | console.log("Total Gas USD: " + gasUsedUSD); 112 | 113 | expect(gasUsedUSD).gte(0.1); 114 | }); 115 | }); 116 | }); 117 | -------------------------------------------------------------------------------- /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 | --------------------------------------------------------------------------------