├── test └── .gitkeep ├── .gitignore ├── contracts ├── compound │ ├── CToken.sol │ ├── Comptroller.sol │ ├── CEther.sol │ ├── InterestRateModel.sol │ ├── ComptrollerInterface.sol │ └── CTokenInterfaces.sol ├── oracle │ └── ChainlinkAdaptor.sol ├── FlashLoanReceiverBase.sol ├── WETH.sol ├── Migrations.sol ├── IFlashLoanReceiver.sol ├── Governable.sol ├── FlashLoanStorage.sol ├── test │ └── TestFlashLoan.sol ├── IFlashLoan.sol ├── FlashLoan.sol └── dependency.sol ├── migrations ├── 1_initial_migration.js ├── 2_deploy_FlashLoan.js └── 3_deploy_test.js ├── package.json ├── truffle-config.js └── README.md /test/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | build 3 | .DS_Store 4 | .secret 5 | package-lock.json 6 | -------------------------------------------------------------------------------- /contracts/compound/CToken.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.0; 2 | 3 | import "./CTokenInterfaces.sol"; 4 | 5 | contract CToken is CTokenInterface, CErc20Interface { 6 | } 7 | -------------------------------------------------------------------------------- /migrations/1_initial_migration.js: -------------------------------------------------------------------------------- 1 | const Migrations = artifacts.require("Migrations"); 2 | 3 | module.exports = function (deployer) { 4 | deployer.deploy(Migrations); 5 | }; 6 | -------------------------------------------------------------------------------- /contracts/oracle/ChainlinkAdaptor.sol: -------------------------------------------------------------------------------- 1 | 2 | pragma solidity ^0.5.0; 3 | 4 | import "../compound/CToken.sol"; 5 | 6 | contract ChainlinkAdaptor { 7 | function getUnderlyingPrice(CToken cToken) external view returns (uint); 8 | } 9 | -------------------------------------------------------------------------------- /contracts/compound/Comptroller.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.0; 2 | 3 | import "./ComptrollerInterface.sol"; 4 | import "./CToken.sol"; 5 | 6 | contract Comptroller is ComptrollerInterface { 7 | function checkMembership(address account, CToken cToken) external view returns (bool); 8 | 9 | function getAccountLiquidity(address account) external view returns (uint, uint, uint); 10 | } 11 | -------------------------------------------------------------------------------- /contracts/FlashLoanReceiverBase.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.5.0; 3 | 4 | import './IFlashLoan.sol'; 5 | import './IFlashLoanReceiver.sol'; 6 | 7 | contract FlashLoanReceiverBase is IFlashLoanReceiver { 8 | 9 | IFlashLoan public FLASHLOAN_POOL; 10 | 11 | constructor(IFlashLoan _flashLoan) public { 12 | FLASHLOAN_POOL = IFlashLoan(_flashLoan); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "flashloan", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "truffle-config.js", 6 | "directories": { 7 | "test": "test" 8 | }, 9 | "scripts": { 10 | "test": "echo \"Error: no test specified\" && exit 1" 11 | }, 12 | "author": "filda.io", 13 | "license": "MIT", 14 | "dependencies": { 15 | "@truffle/hdwallet-provider": "^1.2.6" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /contracts/compound/CEther.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.0; 2 | 3 | import "./CTokenInterfaces.sol"; 4 | 5 | contract CEther is CTokenInterface { 6 | function liquidateBorrow(address borrower, address cTokenCollateral) external payable; 7 | function repayBorrowBehalf(address borrower) external payable; 8 | function repayBorrow() external payable; 9 | function borrow(uint borrowAmount) external returns (uint); 10 | } 11 | -------------------------------------------------------------------------------- /contracts/WETH.sol: -------------------------------------------------------------------------------- 1 | 2 | // SPDX-License-Identifier: MIT 3 | pragma solidity ^0.5.0; 4 | 5 | contract WETH { 6 | function deposit() public payable; 7 | function withdraw(uint wad) public; 8 | function totalSupply() public view returns (uint); 9 | function approve(address guy, uint wad) public returns (bool); 10 | function transfer(address dst, uint wad) public returns (bool); 11 | function transferFrom(address src, address dst, uint wad) public returns (bool); 12 | } 13 | -------------------------------------------------------------------------------- /contracts/Migrations.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.4.22 <0.8.0; 3 | 4 | contract Migrations { 5 | address public owner = msg.sender; 6 | uint public last_completed_migration; 7 | 8 | modifier restricted() { 9 | require( 10 | msg.sender == owner, 11 | "This function is restricted to the contract's owner" 12 | ); 13 | _; 14 | } 15 | 16 | function setCompleted(uint completed) public restricted { 17 | last_completed_migration = completed; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /migrations/2_deploy_FlashLoan.js: -------------------------------------------------------------------------------- 1 | const FlashLoan = artifacts.require("FlashLoan"); 2 | 3 | module.exports = async function (deployer, network, accounts) { 4 | await deployer.deploy(FlashLoan, 5 | '0x', // _governance 6 | '0x', // comptroller 7 | '0x', // oracle 8 | '0x', // fHUSD 9 | '0x' // WHT 10 | ); 11 | 12 | console.log("***********************************************"); 13 | console.log("FlashLoan address:", FlashLoan.address); 14 | console.log("***********************************************"); 15 | }; 16 | -------------------------------------------------------------------------------- /migrations/3_deploy_test.js: -------------------------------------------------------------------------------- 1 | const TestFlashLoan = artifacts.require("TestFlashLoan"); 2 | const FlashLoan = artifacts.require("FlashLoan"); 3 | 4 | module.exports = async function (deployer, network, accounts) { 5 | const flashloan = await FlashLoan.deployed(); 6 | await deployer.deploy(TestFlashLoan, flashloan.address, 7 | '0x', // _governance 8 | '0x' // WHT 9 | ); 10 | 11 | console.log("***********************************************"); 12 | console.log("TestFlashLoan address:", TestFlashLoan.address); 13 | console.log("***********************************************"); 14 | }; 15 | -------------------------------------------------------------------------------- /contracts/IFlashLoanReceiver.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.5.0; 3 | 4 | import './IFlashLoan.sol'; 5 | 6 | /** 7 | * @title IFlashLoanReceiver interface 8 | * @notice Interface for the Aave fee IFlashLoanReceiver. 9 | * @author Aave 10 | * @dev implement this interface to develop a flashloan-compatible flashLoanReceiver contract 11 | **/ 12 | interface IFlashLoanReceiver { 13 | function executeOperation( 14 | address[] calldata assets, 15 | uint256[] calldata amounts, 16 | uint256[] calldata premiums, 17 | address initiator, 18 | bytes calldata params 19 | ) external returns (bool); 20 | 21 | function FLASHLOAN_POOL() external view returns (IFlashLoan); 22 | } 23 | -------------------------------------------------------------------------------- /contracts/Governable.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.0; 2 | 3 | contract Governable { 4 | 5 | address public governance; 6 | 7 | constructor(address _governance) public { 8 | require(_governance != address(0), "governance shouldn't be empty"); 9 | governance = _governance; 10 | } 11 | 12 | modifier onlyGovernance() { 13 | require(isGovernance(msg.sender), "Not governance"); 14 | _; 15 | } 16 | 17 | function setGovernance(address _governance) public onlyGovernance { 18 | require(_governance != address(0), "new governance shouldn't be empty"); 19 | governance = _governance; 20 | } 21 | 22 | 23 | function isGovernance(address account) public view returns (bool) { 24 | return account == governance; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /contracts/FlashLoanStorage.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.5.0; 3 | 4 | import "./compound/Comptroller.sol"; 5 | import "./oracle/ChainlinkAdaptor.sol"; 6 | import "./WETH.sol"; 7 | 8 | contract FlashLoanStorage { 9 | 10 | struct ReserveData { 11 | //tokens addresses 12 | address fTokenAddress; 13 | address tokenAddress; 14 | //the id of the reserve. Represents the position in the list of the active reserves 15 | uint8 id; 16 | } 17 | 18 | struct WhiteListData { 19 | bool isInWhiteList; 20 | uint256 premium; 21 | } 22 | 23 | mapping(address => ReserveData) internal _reserves; 24 | 25 | // the list of the available reserves, structured as a mapping for gas savings reasons 26 | mapping(uint256 => address) internal _reservesList; 27 | 28 | uint256 internal _reservesCount; 29 | 30 | bool internal _paused; 31 | 32 | uint256 internal _flashLoanPremiumTotal; 33 | 34 | uint256 internal _maxNumberOfReserves; 35 | 36 | Comptroller internal _comptroller; 37 | 38 | ChainlinkAdaptor internal _oracle; 39 | 40 | address internal _fHUSD; 41 | 42 | WETH internal _WETH; 43 | 44 | mapping (address => WhiteListData) internal _whitelist; 45 | } 46 | -------------------------------------------------------------------------------- /contracts/compound/InterestRateModel.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.0; 2 | 3 | /** 4 | * @title Compound's InterestRateModel Interface 5 | * @author Compound 6 | */ 7 | contract InterestRateModel { 8 | /// @notice Indicator that this is an InterestRateModel contract (for inspection) 9 | bool public constant isInterestRateModel = true; 10 | 11 | /** 12 | * @notice Calculates the current borrow interest rate per block 13 | * @param cash The total amount of cash the market has 14 | * @param borrows The total amount of borrows the market has outstanding 15 | * @param reserves The total amount of reserves the market has 16 | * @return The borrow rate per block (as a percentage, and scaled by 1e18) 17 | */ 18 | function getBorrowRate(uint cash, uint borrows, uint reserves) external view returns (uint); 19 | 20 | /** 21 | * @notice Calculates the current supply interest rate per block 22 | * @param cash The total amount of cash the market has 23 | * @param borrows The total amount of borrows the market has outstanding 24 | * @param reserves The total amount of reserves the market has 25 | * @param reserveFactorMantissa The current reserve factor the market has 26 | * @return The supply rate per block (as a percentage, and scaled by 1e18) 27 | */ 28 | function getSupplyRate(uint cash, uint borrows, uint reserves, uint reserveFactorMantissa) external view returns (uint); 29 | 30 | } 31 | -------------------------------------------------------------------------------- /contracts/test/TestFlashLoan.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: agpl-3.0 2 | pragma solidity ^0.5.0; 3 | 4 | import "../FlashLoanReceiverBase.sol"; 5 | import "../dependency.sol"; 6 | import "../WETH.sol"; 7 | 8 | contract TestFlashLoan is FlashLoanReceiverBase { 9 | using SafeMath for uint256; 10 | using SafeERC20 for IERC20; 11 | 12 | address public governance; 13 | WETH public _WETH; 14 | 15 | constructor(IFlashLoan _flashLoan, address _governance, address weth) public FlashLoanReceiverBase(_flashLoan) { 16 | governance = _governance; 17 | _WETH = WETH(weth); 18 | } 19 | 20 | function executeOperation( 21 | address[] calldata assets, 22 | uint256[] calldata amounts, 23 | uint256[] calldata premiums, 24 | address initiator, 25 | bytes calldata params 26 | ) external returns (bool) { 27 | params; 28 | require(initiator == governance, "initiator is not governance."); 29 | require(msg.sender == address(FLASHLOAN_POOL), "caller is not flash loan contract"); 30 | 31 | for (uint i = 0; i < assets.length; i++) { 32 | uint256 repay = amounts[i].add(premiums[i]); 33 | if (assets[i] == address(_WETH)) { 34 | _WETH.withdraw(amounts[i]); 35 | _WETH.deposit.value(repay)(); 36 | } 37 | 38 | IERC20(assets[i]).safeApprove(address(FLASHLOAN_POOL), repay); 39 | } 40 | 41 | return true; 42 | } 43 | 44 | function withdrawERC20(address _token, address _account, uint256 amount) public returns (uint256) { 45 | require(msg.sender == governance, "only governance."); 46 | IERC20 token = IERC20(_token); 47 | if (amount > token.balanceOf(address(this))) { 48 | amount = token.balanceOf(address(this)); 49 | } 50 | token.safeTransfer(_account, amount); 51 | return amount; 52 | } 53 | 54 | function withdraw(address payable _account, uint256 amount) public { 55 | require(msg.sender == governance, "only governance."); 56 | if (amount > address(this).balance) { 57 | amount = address(this).balance; 58 | } 59 | _account.transfer(amount); 60 | } 61 | 62 | function () external payable {} 63 | } 64 | -------------------------------------------------------------------------------- /contracts/compound/ComptrollerInterface.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.0; 2 | 3 | contract ComptrollerInterface { 4 | /// @notice Indicator that this is a Comptroller contract (for inspection) 5 | bool public constant isComptroller = true; 6 | 7 | /*** Assets You Are In ***/ 8 | 9 | function enterMarkets(address[] calldata cTokens) external returns (uint[] memory); 10 | function exitMarket(address cToken) external returns (uint); 11 | 12 | /*** Policy Hooks ***/ 13 | 14 | function mintAllowed(address cToken, address minter, uint mintAmount) external returns (uint); 15 | function mintVerify(address cToken, address minter, uint mintAmount, uint mintTokens) external; 16 | 17 | function redeemAllowed(address cToken, address redeemer, uint redeemTokens) external returns (uint); 18 | function redeemVerify(address cToken, address redeemer, uint redeemAmount, uint redeemTokens) external; 19 | 20 | function borrowAllowed(address cToken, address borrower, uint borrowAmount) external returns (uint); 21 | function borrowVerify(address cToken, address borrower, uint borrowAmount) external; 22 | 23 | function repayBorrowAllowed( 24 | address cToken, 25 | address payer, 26 | address borrower, 27 | uint repayAmount) external returns (uint); 28 | function repayBorrowVerify( 29 | address cToken, 30 | address payer, 31 | address borrower, 32 | uint repayAmount, 33 | uint borrowerIndex) external; 34 | 35 | function liquidateBorrowAllowed( 36 | address cTokenBorrowed, 37 | address cTokenCollateral, 38 | address liquidator, 39 | address borrower, 40 | uint repayAmount) external returns (uint); 41 | function liquidateBorrowVerify( 42 | address cTokenBorrowed, 43 | address cTokenCollateral, 44 | address liquidator, 45 | address borrower, 46 | uint repayAmount, 47 | uint seizeTokens) external; 48 | 49 | function seizeAllowed( 50 | address cTokenCollateral, 51 | address cTokenBorrowed, 52 | address liquidator, 53 | address borrower, 54 | uint seizeTokens) external returns (uint); 55 | function seizeVerify( 56 | address cTokenCollateral, 57 | address cTokenBorrowed, 58 | address liquidator, 59 | address borrower, 60 | uint seizeTokens) external; 61 | 62 | function transferAllowed(address cToken, address src, address dst, uint transferTokens) external returns (uint); 63 | function transferVerify(address cToken, address src, address dst, uint transferTokens) external; 64 | 65 | /*** Liquidity/Liquidation Calculations ***/ 66 | 67 | function liquidateCalculateSeizeTokens( 68 | address cTokenBorrowed, 69 | address cTokenCollateral, 70 | uint repayAmount) external view returns (uint, uint); 71 | } 72 | -------------------------------------------------------------------------------- /contracts/IFlashLoan.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.5.0; 3 | 4 | interface IFlashLoan { 5 | /** 6 | * @dev Emitted when the pause is triggered. 7 | */ 8 | event Paused(); 9 | 10 | /** 11 | * @dev Emitted when the pause is lifted. 12 | */ 13 | event Unpaused(); 14 | 15 | /** 16 | * @dev Emitted when the oracle contract address is changed. 17 | */ 18 | event OracleChanged( 19 | address indexed from, 20 | address indexed to 21 | ); 22 | 23 | /** 24 | * @dev Emitted when the whitelist added or premium changed. 25 | */ 26 | event WhitelistChanged( 27 | address[] list, 28 | uint256[] premiums 29 | ); 30 | 31 | /** 32 | * @dev Emitted when the target removed form whitelist . 33 | */ 34 | event WhitelistRemoved( 35 | address indexed target 36 | ); 37 | 38 | /** 39 | * @dev Emitted on flashLoan() 40 | * @param target The address of the flash loan receiver contract 41 | * @param initiator The address initiating the flash loan 42 | * @param asset The address of the asset being flash borrowed 43 | * @param amount The amount flash borrowed 44 | * @param premium The fee flash borrowed 45 | **/ 46 | event FlashLoan( 47 | address indexed target, 48 | address indexed initiator, 49 | address indexed asset, 50 | uint256 amount, 51 | uint256 premium 52 | ); 53 | 54 | /** 55 | * @dev Initializes a reserve, activating it, assigning a fToken and underlying tokens 56 | * @param assets The address list of the underlying asset of the reserve 57 | * @param fTokenAddresses The address list of the fToken 58 | **/ 59 | function initReserve( 60 | address[] calldata assets, 61 | address[] calldata fTokenAddresses 62 | ) external; 63 | 64 | /** 65 | * @dev Returns the state and configuration of the reserve 66 | * @param asset The address of the underlying asset of the reserve 67 | * @return The state of the reserve 68 | **/ 69 | function getReserveData(address asset) 70 | external 71 | view 72 | returns (address, uint8); 73 | 74 | /** 75 | * @dev Allows smartcontracts to access the liquidity of the pool within one transaction, 76 | * as long as the amount taken plus a fee is returned. 77 | * IMPORTANT There are security concerns for developers of flashloan receiver contracts that must be kept into consideration. 78 | * For further details please visit https://developers.aave.com 79 | * @param receiverAddress The address of the contract receiving the funds, implementing the IFlashLoanReceiver interface 80 | * @param assets The addresses of the assets being flash-borrowed 81 | * @param amounts The amounts amounts being flash-borrowed 82 | * @param params Variadic packed params to pass to the receiver as extra information 83 | * 0 if the action is executed directly by the user, without any middle-man 84 | **/ 85 | function flashLoan( 86 | address receiverAddress, 87 | address[] calldata assets, 88 | uint256[] calldata amounts, 89 | bytes calldata params 90 | ) external; 91 | 92 | function getReservesList() external view returns (address[] memory); 93 | 94 | function setPause(bool val) external; 95 | 96 | function paused() external view returns (bool); 97 | 98 | function setFlashLoanPremium(uint256 flashLoanPremium) external; 99 | 100 | function getComptroller() external view returns (address); 101 | 102 | function setComptroller(address comptroller) external; 103 | 104 | function getLiquidity() external view returns (uint256); 105 | 106 | function getMaxTokenAmount(address asset) external view returns (uint256); 107 | 108 | function getOracle() external view returns (address); 109 | 110 | function setOracle(address oracle) external; 111 | 112 | function WETH() external view returns (address); 113 | 114 | function addToWhitelist(address[] calldata _target, uint256[] calldata premiums) external; 115 | 116 | function removeFromWhitelist(address _target) external; 117 | 118 | function isInWhitelist(address _target) external view returns (bool); 119 | 120 | function getPremium(address _target) external view returns (uint256); 121 | } 122 | -------------------------------------------------------------------------------- /truffle-config.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Use this file to configure your truffle project. It's seeded with some 3 | * common settings for different networks and features like migrations, 4 | * compilation and testing. Uncomment the ones you need or modify 5 | * them to suit your project as necessary. 6 | * 7 | * More information about configuration can be found at: 8 | * 9 | * trufflesuite.com/docs/advanced/configuration 10 | * 11 | * To deploy via Infura you'll need a wallet provider (like @truffle/hdwallet-provider) 12 | * to sign your transactions before they're sent to a remote public node. Infura accounts 13 | * are available for free at: infura.io/register. 14 | * 15 | * You'll also need a mnemonic - the twelve word phrase the wallet uses to generate 16 | * public/private key pairs. If you're publishing your code to GitHub make sure you load this 17 | * phrase from a file you've .gitignored so it doesn't accidentally become public. 18 | * 19 | */ 20 | 21 | const HDWalletProvider = require('@truffle/hdwallet-provider'); 22 | // const infuraKey = "fj4jll3k....."; 23 | // 24 | const fs = require('fs'); 25 | const mnemonicStr = fs.readFileSync(".secret").toString().trim(); 26 | 27 | module.exports = { 28 | /** 29 | * Networks define how you connect to your ethereum client and let you set the 30 | * defaults web3 uses to send transactions. If you don't specify one truffle 31 | * will spin up a development blockchain for you on port 9545 when you 32 | * run `develop` or `test`. You can ask a truffle command to use a specific 33 | * network from the command line, e.g 34 | * 35 | * $ truffle test --network 36 | */ 37 | 38 | networks: { 39 | // Useful for testing. The `development` name is special - truffle uses it by default 40 | // if it's defined here and no other network is specified at the command line. 41 | // You should run a client (like ganache-cli, geth or parity) in a separate terminal 42 | // tab if you use this network and you must also set the `host`, `port` and `network_id` 43 | // options below to some value. 44 | // 45 | // development: { 46 | // host: "127.0.0.1", // Localhost (default: none) 47 | // port: 8545, // Standard Ethereum port (default: none) 48 | // network_id: "*", // Any network (default: none) 49 | // }, 50 | // Another network with more advanced options... 51 | // advanced: { 52 | // port: 8777, // Custom port 53 | // network_id: 1342, // Custom network 54 | // gas: 8500000, // Gas sent with each transaction (default: ~6700000) 55 | // gasPrice: 20000000000, // 20 gwei (in wei) (default: 100 gwei) 56 | // from:
, // Account to send txs from (default: accounts[0]) 57 | // websockets: true // Enable EventEmitter interface for web3 (default: false) 58 | // }, 59 | // Useful for deploying to a public network. 60 | // NB: It's important to wrap the provider as a function. 61 | // ropsten: { 62 | // provider: () => new HDWalletProvider(mnemonic, `https://ropsten.infura.io/v3/YOUR-PROJECT-ID`), 63 | // network_id: 3, // Ropsten's id 64 | // gas: 5500000, // Ropsten has a lower block limit than mainnet 65 | // confirmations: 2, // # of confs to wait between deployments. (default: 0) 66 | // timeoutBlocks: 200, // # of blocks before a deployment times out (minimum/default: 50) 67 | // skipDryRun: true // Skip dry run before migrations? (default: false for public nets ) 68 | // }, 69 | // Useful for private networks 70 | // private: { 71 | // provider: () => new HDWalletProvider(mnemonic, `https://network.io`), 72 | // network_id: 2111, // This network is yours, in the cloud. 73 | // production: true // Treats this network as if it was a public net. (default: false) 74 | // } 75 | ela: { 76 | provider: () => new HDWalletProvider({mnemonic: mnemonicStr, providerOrUrl:`https://api.elastos.io/eth`}), 77 | network_id: 20, // This network is yours, in the cloud. 78 | //production: true, // Treats this network as if it was a public net. (default: false) 79 | timeoutBlocks: 200 80 | }, 81 | elatest: { 82 | provider: () => new HDWalletProvider({mnemonic: mnemonicStr, providerOrUrl:`https://api-testnet.elastos.io/eth`}), 83 | network_id: 0x15, // This network is yours, in the cloud. 84 | //production: true, // Treats this network as if it was a public net. (default: false) 85 | timeoutBlocks: 200 86 | }, 87 | hecotest: { 88 | provider: () => new HDWalletProvider({mnemonic: mnemonicStr, providerOrUrl:`https://http-testnet.hecochain.com`}), 89 | network_id: 256, // This network is yours, in the cloud. 90 | //production: true, // Treats this network as if it was a public net. (default: false) 91 | timeoutBlocks: 200 92 | }, 93 | heco: { 94 | // https://http-mainnet-node.huobichain.com 95 | // https://heconode.ifoobar.com 96 | // https://heconode.filda.io 97 | provider: () => new HDWalletProvider({mnemonic: mnemonicStr, providerOrUrl:'https://heconode.filda.io'}), 98 | network_id: 0x80, // This network is yours, in the cloud. 99 | //production: true, // Treats this network as if it was a public net. (default: false) 100 | timeoutBlocks: 200, 101 | confirmations: 2, 102 | gasPrice: 2000000000, 103 | skipDryRun: true, 104 | networkCheckTimeout: 100000000, 105 | websockets: true 106 | }, 107 | }, 108 | 109 | // Set default mocha options here, use special reporters etc. 110 | mocha: { 111 | // timeout: 100000 112 | }, 113 | 114 | // Configure your compilers 115 | compilers: { 116 | solc: { 117 | version: "0.5.16", // Fetch exact version from solc-bin (default: truffle's version) 118 | // docker: true, // Use "0.5.1" you've installed locally with docker (default: false) 119 | settings: { // See the solidity docs for advice about optimization and evmVersion 120 | optimizer: { 121 | enabled: true, 122 | runs: 200 123 | }, 124 | // evmVersion: "byzantium" 125 | } 126 | } 127 | } 128 | }; 129 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # FlashLoan 2 | 3 | Flash Loans are special uncollateralised loans that allow the borrowing of an asset, as long as the borrowed amount (and a fee) is returned before the end of the transaction. 4 | 5 | ## Overview 6 | 7 | For developers, a helpful mental model to consider when developing your solution: 8 | 9 | 1. Your contract calls the **FlashLoan** contract, requesting a Flash Loan of a certain amounts of reserves using flashLoan(). 10 | 2. After some sanity checks, the **FlashLoan** transfers the requested amounts of the reserves to your contract, then calls executeOperation() on your contract (or another contract that you specify as the _receiver). 11 | 3. Your contract, now holding the flash loaned amounts, executes any arbitrary operation in its code. 12 | - when your code has finished, you approve the flash loaned amounts of reserves to the **FlashLoan**. 13 | - The **FlashLoan** contract pulls the flash loaned amount + fee. 14 | - If the amount owing is not available (due to a lack of balance or approval), then the transaction is reverted. 15 | 4. All of the above happens in 1 transaction (hence in a single ethereum block). 16 | 17 | ## Step by step 18 | 19 | ### 1. Setting up 20 | 21 | Your contract that receives the flash loaned amounts must conform to the **IFlashLoanReceiver** interface by implementing the relevant executeOperation() function. In the example below, we inherit from **FlashLoanReceiverBase**, which conforms to the **IFlashLoanReceiver**. 22 | 23 | Also note that since the owed amounts will be pulled from your contract, your contract must give allowance to the **FlashLoan** contract to pull those funds to pay back the flash loan debts + premiums. 24 | 25 | ``` 26 | pragma solidity ^0.5.0; 27 | 28 | import "./FlashLoanReceiverBase.sol"; 29 | import "./SafeMath.sol"; 30 | import "./SafeERC20.sol"; 31 | import "./IERC20.sol"; 32 | 33 | 34 | /** 35 | !!! 36 | Never keep funds permanently on your FlashLoanReceiverBase contract as they could be 37 | exposed to a 'griefing' attack, where the stored funds are used by an attacker. 38 | !!! 39 | */ 40 | 41 | contract MyFlashLoan is FlashLoanReceiverBase { 42 | using SafeMath for uint256; 43 | using SafeERC20 for IERC20; 44 | 45 | 46 | constructor(IFlashLoan _flashLoan) public FlashLoanReceiverBase(_flashLoan) {} 47 | 48 | /** 49 | This function is called after your contract has received the flash loaned amount 50 | */ 51 | function executeOperation( 52 | address[] calldata assets, 53 | uint256[] calldata amounts, 54 | uint256[] calldata premiums, 55 | address initiator, 56 | bytes calldata params 57 | ) external returns (bool) { 58 | // 59 | // This contract now has the funds requested. 60 | // Your logic goes here. 61 | // 62 | 63 | // At the end of your logic above, this contract owes 64 | // the flashloaned amounts + premiums. 65 | // Therefore ensure your contract has enough to repay 66 | // these amounts. 67 | 68 | 69 | require(msg.sender == address(FLASHLOAN_POOL), "caller is not flash loan contract"); 70 | 71 | for (uint i = 0; i < assets.length; i++) { 72 | uint256 repay = amounts[i].add(premiums[i]); 73 | IERC20(assets[i]).safeApprove(address(FLASHLOAN_POOL), repay); 74 | } 75 | 76 | return true; 77 | } 78 | } 79 | ``` 80 | 81 | ### 2. Calling flashLoan() 82 | 83 | To call flashloan() on the **FlashLoan** contract, we need to pass in the relevant parameters. There are 3 ways you can do this. 84 | 85 | **From normal heco account** 86 | 87 | To use an EOA, send a transaction to the **FlashLoan** contract calling the flashLoan() function. See the flashLoan() function documentation for parameter details, ensuring you use your contract address from step 1 for the receiverAddress. 88 | 89 | **From a different contract** 90 | 91 | Similar to sending a transaction as above, ensure the receiverAddress is your contract address from step 1. 92 | 93 | 94 | **From the same contract** 95 | 96 | If you want to use the same contract as in step 1, use address(this) for the receiverAddress parameter in the flashLoan function. 97 | The example below shows this third case, where the executeOperation() is in the same contract calling flashLoan() on the **FlashLoan** contract. 98 | 99 | > [!WARNING] 100 | > Never keep funds permanently on your FlashLoanReceiverBase contract as they could be exposed to a 'griefing' attack, where the stored funds are used by an attacker. 101 | 102 | ``` 103 | pragma solidity ^0.5.0; 104 | 105 | import "./FlashLoanReceiverBase.sol"; 106 | import "./SafeMath.sol"; 107 | import "./SafeERC20.sol"; 108 | import "./IERC20.sol"; 109 | 110 | 111 | /** 112 | !!! 113 | Never keep funds permanently on your FlashLoanReceiverBase contract as they could be 114 | exposed to a 'griefing' attack, where the stored funds are used by an attacker. 115 | !!! 116 | */ 117 | 118 | contract MyFlashLoan is FlashLoanReceiverBase { 119 | using SafeMath for uint256; 120 | using SafeERC20 for IERC20; 121 | 122 | 123 | constructor(IFlashLoan _flashLoan) public FlashLoanReceiverBase(_flashLoan) {} 124 | 125 | /** 126 | This function is called after your contract has received the flash loaned amount 127 | */ 128 | function executeOperation( 129 | address[] calldata assets, 130 | uint256[] calldata amounts, 131 | uint256[] calldata premiums, 132 | address initiator, 133 | bytes calldata params 134 | ) external returns (bool) { 135 | // 136 | // This contract now has the funds requested. 137 | // Your logic goes here. 138 | // 139 | 140 | // At the end of your logic above, this contract owes 141 | // the flashloaned amounts + premiums. 142 | // Therefore ensure your contract has enough to repay 143 | // these amounts. 144 | 145 | 146 | require(msg.sender == address(FLASHLOAN_POOL), "caller is not flash loan contract"); 147 | 148 | for (uint i = 0; i < assets.length; i++) { 149 | uint256 repay = amounts[i].add(premiums[i]); 150 | IERC20(assets[i]).safeApprove(address(FLASHLOAN_POOL), repay); 151 | } 152 | 153 | return true; 154 | } 155 | 156 | function myFlashLoanCall() public { 157 | address receiverAddress = address(this); 158 | 159 | address[] memory assets = new address[](2); 160 | assets[0] = address(INSERT_ASSET_ONE_ADDRESS); 161 | assets[1] = address(INSERT_ASSET_TWO_ADDRESS); 162 | 163 | uint256[] memory amounts = new uint256[](2); 164 | amounts[0] = INSERT_ASSET_ONE_AMOUNT; 165 | amounts[1] = INSERT_ASSET_TWO_AMOUNT; 166 | 167 | bytes memory params = ""; 168 | 169 | FLASHLOAN_POOL.flashLoan( 170 | receiverAddress, 171 | assets, 172 | amounts, 173 | params 174 | ); 175 | } 176 | } 177 | ``` 178 | 179 | ### 3. Completing the flash loan 180 | 181 | Once you have performed your logic with the flash loaned assets (in your executeOperation() function), you will need to pay back the flash loaned amounts. 182 | 183 | Ensure your contract has the relevant amount + premium to payback the loaned asset. You can calculate this by taking the sum of the relevant entry in the amounts and premiums array passed into the executeOperation() function. 184 | 185 | You do not need to transfer the owed amount back to the **FlashLoan** contract. The funds will be automatically pulled at the conclusion of your operation. 186 | 187 | 188 | ## Encoding and Decoding Parameters 189 | 190 | If you would like to pass parameters into your flash loan function, you will first need to encode them, then decode them in your executeOperation(). 191 | 192 | ### Encoding 193 | 194 | If you're encoding in solidity, you can use the in-built abi.encode(): 195 | ``` 196 | // Encoding an address and a uint 197 | bytes memory params = abi.encode(address(this), 1234); 198 | ``` 199 | 200 | If you're encoding off-chain, then you can use a package like web3.js which has an abi.encodeParameters(): 201 | ``` 202 | const params = web3.eth.abi.encodeParameters( 203 | ["bytes32", "address"], 204 | [ 205 | web3.utils.utf8ToHex("some_value"), 206 | "0x0298c2b32eae4da002a15f36fdf7615bea3da047" 207 | ] 208 | ) 209 | ``` 210 | 211 | ### Decoding 212 | 213 | When decoding in your executeOperation(), you will need to use the in-build abi.decode(): 214 | 215 | `(bytes32 someValue, address addr) = abi.decode(params, (bytes32, address));` 216 | 217 | 218 | -------------------------------------------------------------------------------- /contracts/compound/CTokenInterfaces.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.0; 2 | 3 | import "./ComptrollerInterface.sol"; 4 | import "./InterestRateModel.sol"; 5 | 6 | contract CTokenStorage { 7 | /** 8 | * @dev Guard variable for re-entrancy checks 9 | */ 10 | bool internal _notEntered; 11 | 12 | /** 13 | * @notice EIP-20 token name for this token 14 | */ 15 | string public name; 16 | 17 | /** 18 | * @notice EIP-20 token symbol for this token 19 | */ 20 | string public symbol; 21 | 22 | /** 23 | * @notice EIP-20 token decimals for this token 24 | */ 25 | uint8 public decimals; 26 | 27 | /** 28 | * @notice Maximum borrow rate that can ever be applied (.0005% / block) 29 | */ 30 | 31 | uint internal constant borrowRateMaxMantissa = 0.0005e16; 32 | 33 | /** 34 | * @notice Maximum fraction of interest that can be set aside for reserves 35 | */ 36 | uint internal constant reserveFactorMaxMantissa = 1e18; 37 | 38 | /** 39 | * @notice Administrator for this contract 40 | */ 41 | address payable public admin; 42 | 43 | /** 44 | * @notice Pending administrator for this contract 45 | */ 46 | address payable public pendingAdmin; 47 | 48 | /** 49 | * @notice Contract which oversees inter-cToken operations 50 | */ 51 | ComptrollerInterface public comptroller; 52 | 53 | /** 54 | * @notice Model which tells what the current interest rate should be 55 | */ 56 | InterestRateModel public interestRateModel; 57 | 58 | /** 59 | * @notice Initial exchange rate used when minting the first CTokens (used when totalSupply = 0) 60 | */ 61 | uint internal initialExchangeRateMantissa; 62 | 63 | /** 64 | * @notice Fraction of interest currently set aside for reserves 65 | */ 66 | uint public reserveFactorMantissa; 67 | 68 | /** 69 | * @notice Block number that interest was last accrued at 70 | */ 71 | uint public accrualBlockNumber; 72 | 73 | /** 74 | * @notice Accumulator of the total earned interest rate since the opening of the market 75 | */ 76 | uint public borrowIndex; 77 | 78 | /** 79 | * @notice Total amount of outstanding borrows of the underlying in this market 80 | */ 81 | uint public totalBorrows; 82 | 83 | /** 84 | * @notice Total amount of reserves of the underlying held in this market 85 | */ 86 | uint public totalReserves; 87 | 88 | /** 89 | * @notice Total number of tokens in circulation 90 | */ 91 | uint public totalSupply; 92 | 93 | /** 94 | * @notice Official record of token balances for each account 95 | */ 96 | mapping (address => uint) internal accountTokens; 97 | 98 | /** 99 | * @notice Approved token transfer amounts on behalf of others 100 | */ 101 | mapping (address => mapping (address => uint)) internal transferAllowances; 102 | 103 | /** 104 | * @notice Container for borrow balance information 105 | * @member principal Total balance (with accrued interest), after applying the most recent balance-changing action 106 | * @member interestIndex Global borrowIndex as of the most recent balance-changing action 107 | */ 108 | struct BorrowSnapshot { 109 | uint principal; 110 | uint interestIndex; 111 | } 112 | 113 | /** 114 | * @notice Mapping of account addresses to outstanding borrow balances 115 | */ 116 | mapping(address => BorrowSnapshot) internal accountBorrows; 117 | } 118 | 119 | contract CTokenInterface is CTokenStorage { 120 | /** 121 | * @notice Indicator that this is a CToken contract (for inspection) 122 | */ 123 | bool public constant isCToken = true; 124 | 125 | 126 | /*** Market Events ***/ 127 | 128 | /** 129 | * @notice Event emitted when interest is accrued 130 | */ 131 | event AccrueInterest(uint cashPrior, uint interestAccumulated, uint borrowIndex, uint totalBorrows); 132 | 133 | /** 134 | * @notice Event emitted when tokens are minted 135 | */ 136 | event Mint(address minter, uint mintAmount, uint mintTokens); 137 | 138 | /** 139 | * @notice Event emitted when tokens are redeemed 140 | */ 141 | event Redeem(address redeemer, uint redeemAmount, uint redeemTokens); 142 | 143 | /** 144 | * @notice Event emitted when underlying is borrowed 145 | */ 146 | event Borrow(address borrower, uint borrowAmount, uint accountBorrows, uint totalBorrows); 147 | 148 | /** 149 | * @notice Event emitted when a borrow is repaid 150 | */ 151 | event RepayBorrow(address payer, address borrower, uint repayAmount, uint accountBorrows, uint totalBorrows); 152 | 153 | /** 154 | * @notice Event emitted when a borrow is liquidated 155 | */ 156 | event LiquidateBorrow(address liquidator, address borrower, uint repayAmount, address cTokenCollateral, uint seizeTokens); 157 | 158 | 159 | /*** Admin Events ***/ 160 | 161 | /** 162 | * @notice Event emitted when pendingAdmin is changed 163 | */ 164 | event NewPendingAdmin(address oldPendingAdmin, address newPendingAdmin); 165 | 166 | /** 167 | * @notice Event emitted when pendingAdmin is accepted, which means admin is updated 168 | */ 169 | event NewAdmin(address oldAdmin, address newAdmin); 170 | 171 | /** 172 | * @notice Event emitted when comptroller is changed 173 | */ 174 | event NewComptroller(ComptrollerInterface oldComptroller, ComptrollerInterface newComptroller); 175 | 176 | /** 177 | * @notice Event emitted when interestRateModel is changed 178 | */ 179 | event NewMarketInterestRateModel(InterestRateModel oldInterestRateModel, InterestRateModel newInterestRateModel); 180 | 181 | /** 182 | * @notice Event emitted when the reserve factor is changed 183 | */ 184 | event NewReserveFactor(uint oldReserveFactorMantissa, uint newReserveFactorMantissa); 185 | 186 | /** 187 | * @notice Event emitted when the reserves are added 188 | */ 189 | event ReservesAdded(address benefactor, uint addAmount, uint newTotalReserves); 190 | 191 | /** 192 | * @notice Event emitted when the reserves are reduced 193 | */ 194 | event ReservesReduced(address admin, uint reduceAmount, uint newTotalReserves); 195 | 196 | /** 197 | * @notice EIP20 Transfer event 198 | */ 199 | event Transfer(address indexed from, address indexed to, uint amount); 200 | 201 | /** 202 | * @notice EIP20 Approval event 203 | */ 204 | event Approval(address indexed owner, address indexed spender, uint amount); 205 | 206 | /** 207 | * @notice Failure event 208 | */ 209 | event Failure(uint error, uint info, uint detail); 210 | 211 | 212 | /*** User Interface ***/ 213 | 214 | function transfer(address dst, uint amount) external returns (bool); 215 | function transferFrom(address src, address dst, uint amount) external returns (bool); 216 | function approve(address spender, uint amount) external returns (bool); 217 | function allowance(address owner, address spender) external view returns (uint); 218 | function balanceOf(address owner) external view returns (uint); 219 | function balanceOfUnderlying(address owner) external returns (uint); 220 | function getAccountSnapshot(address account) external view returns (uint, uint, uint, uint); 221 | function borrowRatePerBlock() external view returns (uint); 222 | function supplyRatePerBlock() external view returns (uint); 223 | function totalBorrowsCurrent() external returns (uint); 224 | function borrowBalanceCurrent(address account) external returns (uint); 225 | function borrowBalanceStored(address account) public view returns (uint); 226 | function exchangeRateCurrent() public returns (uint); 227 | function exchangeRateStored() public view returns (uint); 228 | function getCash() external view returns (uint); 229 | function accrueInterest() public returns (uint); 230 | function seize(address liquidator, address borrower, uint seizeTokens) external returns (uint); 231 | 232 | 233 | /*** Admin Functions ***/ 234 | 235 | function _setPendingAdmin(address payable newPendingAdmin) external returns (uint); 236 | function _acceptAdmin() external returns (uint); 237 | function _setComptroller(ComptrollerInterface newComptroller) public returns (uint); 238 | function _setReserveFactor(uint newReserveFactorMantissa) external returns (uint); 239 | function _reduceReserves(uint reduceAmount) external returns (uint); 240 | function _setInterestRateModel(InterestRateModel newInterestRateModel) public returns (uint); 241 | } 242 | 243 | contract CErc20Storage { 244 | /** 245 | * @notice Underlying asset for this CToken 246 | */ 247 | address public underlying; 248 | } 249 | 250 | contract CErc20Interface is CErc20Storage { 251 | 252 | /*** User Interface ***/ 253 | 254 | function mint(uint mintAmount) external returns (uint); 255 | function redeem(uint redeemTokens) external returns (uint); 256 | function redeemUnderlying(uint redeemAmount) external returns (uint); 257 | function borrow(uint borrowAmount) external returns (uint); 258 | function repayBorrow(uint repayAmount) external returns (uint); 259 | function repayBorrowBehalf(address borrower, uint repayAmount) external returns (uint); 260 | function liquidateBorrow(address borrower, uint repayAmount, CTokenInterface cTokenCollateral) external returns (uint); 261 | 262 | 263 | /*** Admin Functions ***/ 264 | 265 | function _addReserves(uint addAmount) external returns (uint); 266 | } 267 | 268 | contract CDelegationStorage { 269 | /** 270 | * @notice Implementation address for this contract 271 | */ 272 | address public implementation; 273 | } 274 | 275 | contract CDelegatorInterface is CDelegationStorage { 276 | /** 277 | * @notice Emitted when implementation is changed 278 | */ 279 | event NewImplementation(address oldImplementation, address newImplementation); 280 | 281 | /** 282 | * @notice Called by the admin to update the implementation of the delegator 283 | * @param implementation_ The address of the new implementation for delegation 284 | * @param allowResign Flag to indicate whether to call _resignImplementation on the old implementation 285 | * @param becomeImplementationData The encoded bytes data to be passed to _becomeImplementation 286 | */ 287 | function _setImplementation(address implementation_, bool allowResign, bytes memory becomeImplementationData) public; 288 | } 289 | 290 | contract CDelegateInterface is CDelegationStorage { 291 | /** 292 | * @notice Called by the delegator on a delegate to initialize it for duty 293 | * @dev Should revert if any issues arise which make it unfit for delegation 294 | * @param data The encoded bytes data for any initialization 295 | */ 296 | function _becomeImplementation(bytes memory data) public; 297 | 298 | /** 299 | * @notice Called by the delegator on a delegate to forfeit its responsibility 300 | */ 301 | function _resignImplementation() public; 302 | } 303 | -------------------------------------------------------------------------------- /contracts/FlashLoan.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.5.0; 3 | 4 | import './Governable.sol'; 5 | import './FlashLoanStorage.sol'; 6 | import './IFlashLoan.sol'; 7 | import './IFlashLoanReceiver.sol'; 8 | import './compound/CTokenInterfaces.sol'; 9 | import './compound/Comptroller.sol'; 10 | import './compound/CEther.sol'; 11 | import './dependency.sol'; 12 | import './oracle/ChainlinkAdaptor.sol'; 13 | 14 | contract IERC20Extented is IERC20 { 15 | function decimals() public view returns (uint8); 16 | } 17 | 18 | contract FlashLoan is IFlashLoan, FlashLoanStorage, Governable, Ownable { 19 | using SafeMath for uint256; 20 | using SafeERC20 for IERC20; 21 | using SafeERC20 for WETH; 22 | 23 | modifier whenNotPaused() { 24 | _whenNotPaused(); 25 | _; 26 | } 27 | 28 | function _whenNotPaused() internal view { 29 | require(!_paused, "FlashLoan: flash loan is paused!"); 30 | } 31 | 32 | constructor(address _governance, address comptroller, address oracle, address fHUSD, address weth) public Governable(_governance) { 33 | _comptroller = Comptroller(comptroller); 34 | _oracle = ChainlinkAdaptor(oracle); 35 | _fHUSD = fHUSD; 36 | _WETH = WETH(weth); 37 | _flashLoanPremiumTotal = 10; 38 | _maxNumberOfReserves = 128; 39 | } 40 | 41 | struct FlashLoanLocalVars { 42 | IFlashLoanReceiver receiver; 43 | uint256 i; 44 | address currentAsset; 45 | address currentFTokenAddress; 46 | uint256 currentAmount; 47 | uint256 currentPremium; 48 | uint256 currentAmountPlusPremium; 49 | } 50 | 51 | /** 52 | * @dev Allows smartcontracts to access the liquidity of the pool within one transaction, 53 | * as long as the amount taken plus a fee is returned. 54 | * IMPORTANT There are security concerns for developers of flashloan receiver contracts that must be kept into consideration. 55 | * For further details please visit https://developers.aave.com 56 | * @param receiverAddress The address of the contract receiving the funds, implementing the IFlashLoanReceiver interface 57 | * @param assets The addresses of the assets being flash-borrowed 58 | * @param amounts The amounts amounts being flash-borrowed 59 | * @param params Variadic packed params to pass to the receiver as extra information 60 | * 0 if the action is executed directly by the user, without any middle-man 61 | **/ 62 | function flashLoan( 63 | address receiverAddress, 64 | address[] calldata assets, 65 | uint256[] calldata amounts, 66 | bytes calldata params 67 | ) external whenNotPaused { 68 | FlashLoanLocalVars memory vars; 69 | uint err = 0; 70 | 71 | require(assets.length == amounts.length, "FlashLoan: invalid flash loan parameter."); 72 | 73 | uint256 liquidity = _getLiquidity(); 74 | 75 | address[] memory fTokenAddresses = new address[](assets.length); 76 | uint256[] memory premiums = new uint256[](assets.length); 77 | 78 | vars.receiver = IFlashLoanReceiver(receiverAddress); 79 | 80 | for (vars.i = 0; vars.i < assets.length; vars.i++) { 81 | uint256 maxTokenAmount = getMaxTokenAmount(assets[vars.i]); 82 | require(amounts[vars.i] <= maxTokenAmount, "FlashLoan: Insufficient liquidity"); 83 | 84 | fTokenAddresses[vars.i] = _reserves[assets[vars.i]].fTokenAddress; 85 | 86 | if (assets[vars.i] == address(_WETH)) { 87 | borrowETH(fTokenAddresses[vars.i], amounts[vars.i]); 88 | } else { 89 | err = CToken(fTokenAddresses[vars.i]).borrow(amounts[vars.i]); 90 | require(err == 0, "FlashLoan: borrow error"); 91 | } 92 | 93 | uint256 premiumTotal = isInWhitelist(receiverAddress) ? _whitelist[receiverAddress].premium : _flashLoanPremiumTotal; 94 | 95 | premiums[vars.i] = amounts[vars.i].mul(premiumTotal).div(10000); 96 | 97 | IERC20(assets[vars.i]).safeTransfer(receiverAddress, amounts[vars.i]); 98 | } 99 | 100 | require( 101 | vars.receiver.executeOperation(assets, amounts, premiums, msg.sender, params), 102 | "FlashLoan: invalid flash loan executor return!" 103 | ); 104 | 105 | for (vars.i = 0; vars.i < assets.length; vars.i++) { 106 | vars.currentAsset = assets[vars.i]; 107 | vars.currentAmount = amounts[vars.i]; 108 | vars.currentPremium = premiums[vars.i]; 109 | vars.currentFTokenAddress = fTokenAddresses[vars.i]; 110 | vars.currentAmountPlusPremium = vars.currentAmount.add(vars.currentPremium); 111 | 112 | IERC20(vars.currentAsset).safeTransferFrom( 113 | receiverAddress, 114 | address(this), 115 | vars.currentAmountPlusPremium 116 | ); 117 | 118 | uint256 borrowBalance = CToken(vars.currentFTokenAddress).borrowBalanceCurrent(address(this)); 119 | if (vars.currentAsset == address(_WETH)) { 120 | _WETH.withdraw(vars.currentAmountPlusPremium); 121 | 122 | // repay loan. 123 | CEther(vars.currentFTokenAddress).repayBorrow.value(borrowBalance)(); 124 | 125 | if (vars.currentPremium > 0) { 126 | // send premium to owner. 127 | address(uint160(owner())).transfer(vars.currentAmountPlusPremium.sub(borrowBalance)); 128 | } 129 | } else { 130 | IERC20(vars.currentAsset).safeApprove(vars.currentFTokenAddress, 0); 131 | IERC20(vars.currentAsset).safeApprove(vars.currentFTokenAddress, borrowBalance); 132 | 133 | // repay loan. 134 | err = CToken(vars.currentFTokenAddress).repayBorrow(borrowBalance); 135 | require(err == 0, "FlashLoan: repayBorrow error"); 136 | 137 | if (vars.currentPremium > 0) { 138 | // send premium to owner. 139 | IERC20(vars.currentAsset).safeTransfer(owner(), vars.currentAmountPlusPremium.sub(borrowBalance)); 140 | } 141 | } 142 | 143 | emit FlashLoan( 144 | receiverAddress, 145 | msg.sender, 146 | vars.currentAsset, 147 | vars.currentAmount, 148 | vars.currentPremium 149 | ); 150 | } 151 | 152 | require(liquidity <= _getLiquidity(), "FlashLoan: liquidity decreased!"); 153 | } 154 | 155 | function borrowETH(address cetherAddr, uint256 amount) internal { 156 | uint err = CEther(cetherAddr).borrow(amount); 157 | require(err == 0, "FlashLoan: borrow error"); 158 | 159 | _WETH.deposit.value(amount)(); 160 | } 161 | 162 | /** 163 | * @dev Returns the list of the initialized reserves 164 | **/ 165 | function getReservesList() external view returns (address[] memory) { 166 | address[] memory _activeReserves = new address[](_reservesCount); 167 | 168 | for (uint256 i = 0; i < _reservesCount; i++) { 169 | _activeReserves[i] = _reservesList[i]; 170 | } 171 | return _activeReserves; 172 | } 173 | 174 | /** 175 | * @dev Returns the fee on flash loans 176 | */ 177 | function FLASHLOAN_PREMIUM_TOTAL() public view returns (uint256) { 178 | return _flashLoanPremiumTotal; 179 | } 180 | 181 | /** 182 | * @dev Returns the maximum number of reserves supported to be listed in this LendingPool 183 | */ 184 | function MAX_NUMBER_RESERVES() public view returns (uint256) { 185 | return _maxNumberOfReserves; 186 | } 187 | 188 | /** 189 | * @dev Set the _pause state of a reserve 190 | * - Only callable by the LendingPoolConfigurator contract 191 | * @param val `true` to pause the reserve, `false` to un-pause it 192 | */ 193 | function setPause(bool val) external onlyGovernance { 194 | _paused = val; 195 | if (_paused) { 196 | emit Paused(); 197 | } else { 198 | emit Unpaused(); 199 | } 200 | } 201 | 202 | function paused() external view returns (bool) { 203 | return _paused; 204 | } 205 | 206 | function setFlashLoanPremium(uint256 flashLoanPremium) external onlyGovernance { 207 | _flashLoanPremiumTotal = flashLoanPremium; 208 | } 209 | 210 | /** 211 | * @dev Initializes a reserve, activating it, assigning a fToken and underlying tokens 212 | * @param assets The address list of the underlying asset of the reserve 213 | * @param fTokenAddresses The address list of the fToken 214 | **/ 215 | function initReserve( 216 | address[] calldata assets, 217 | address[] calldata fTokenAddresses 218 | ) external onlyGovernance { 219 | for (uint8 i = 0; i < assets.length; i++) { 220 | address asset = assets[i]; 221 | require(Address.isContract(asset), "FlashLoan: asset address is not contract"); 222 | require(_reserves[asset].fTokenAddress == address(0), "FlashLoan: reserve already initialized."); 223 | 224 | enterMarket(fTokenAddresses[i]); 225 | 226 | _reserves[asset].fTokenAddress = fTokenAddresses[i]; 227 | _reserves[asset].tokenAddress = asset; 228 | 229 | _addReserveToList(asset); 230 | } 231 | } 232 | 233 | /** 234 | * @dev Returns the state and configuration of the reserve 235 | * @param asset The address of the underlying asset of the reserve 236 | * @return The state of the reserve 237 | **/ 238 | function getReserveData(address asset) 239 | external 240 | view 241 | returns (address, uint8) 242 | { 243 | return (_reserves[asset].fTokenAddress, _reserves[asset].id); 244 | } 245 | 246 | 247 | function _addReserveToList(address asset) internal { 248 | uint256 reservesCount = _reservesCount; 249 | 250 | require(reservesCount < _maxNumberOfReserves, "FlashLoan: no more reserves allowed!"); 251 | 252 | bool reserveAlreadyAdded = _reserves[asset].id != 0 || _reservesList[0] == asset; 253 | 254 | if (!reserveAlreadyAdded) { 255 | _reserves[asset].id = uint8(reservesCount); 256 | _reservesList[reservesCount] = asset; 257 | 258 | _reservesCount = reservesCount + 1; 259 | } 260 | } 261 | 262 | function withdrawERC20(address _token, address _account, uint256 amount) public onlyOwner { 263 | require(_token != address(0) && _account != address(0) && amount > 0, "FlashLoan: Invalid parameter"); 264 | IERC20 token = IERC20(_token); 265 | if (amount > token.balanceOf(address(this))) { 266 | amount = token.balanceOf(address(this)); 267 | } 268 | token.safeTransfer(_account, amount); 269 | } 270 | 271 | function withdraw(address payable _account, uint256 amount) public onlyOwner { 272 | require(_account != address(0) && amount > 0, "FlashLoan: Invalid parameter"); 273 | if (amount > address(this).balance) { 274 | amount = address(this).balance; 275 | } 276 | _account.transfer(amount); 277 | } 278 | 279 | function getComptroller() external view returns (address) { 280 | return address(_comptroller); 281 | } 282 | 283 | function setComptroller(address comptroller) external onlyGovernance { 284 | require(comptroller != address(0), "FlashLoan: comptroller address can not be zero"); 285 | _comptroller = Comptroller(comptroller); 286 | } 287 | 288 | function enterMarket(address ftoken) internal { 289 | if (_comptroller.checkMembership(address(this), CToken(ftoken))) { 290 | return; 291 | } 292 | 293 | address[] memory tokens = new address[](1); 294 | tokens[0] = ftoken; 295 | uint[] memory errs = _comptroller.enterMarkets(tokens); 296 | require(errs[0] == 0, "FlashLoan: enter market error"); 297 | } 298 | 299 | function exitMarket(address ftoken) internal { 300 | if (!_comptroller.checkMembership(address(this), CToken(ftoken))) { 301 | return; 302 | } 303 | 304 | uint err = _comptroller.exitMarket(ftoken); 305 | require(err == 0, "FlashLoan: exit market error"); 306 | } 307 | 308 | function _getLiquidity() private view returns (uint256) { 309 | (uint error, uint256 liquidity, uint shortfall) = _comptroller.getAccountLiquidity(address(this)); 310 | if (error != 0 || shortfall != 0) { 311 | return 0; 312 | } 313 | 314 | return liquidity; 315 | } 316 | 317 | function getLiquidity() public view returns (uint256) { 318 | return _getLiquidity().mul(getHtPrice()).div(1e18); 319 | } 320 | 321 | function getOracle() external view returns (address) { 322 | return address(_oracle); 323 | } 324 | 325 | function setOracle(address oracle) external onlyGovernance { 326 | require(Address.isContract(oracle), "FlashLoan: oracle address is not contract"); 327 | 328 | address from = address(_oracle); 329 | _oracle = ChainlinkAdaptor(oracle); 330 | 331 | emit OracleChanged(from, oracle); 332 | } 333 | 334 | function getMaxTokenAmount(address asset) public view returns (uint256) { 335 | require(Address.isContract(asset), "FlashLoan: asset address is not contract"); 336 | require(_reserves[asset].fTokenAddress != address(0), "FlashLoan: this asset is not surpported"); 337 | 338 | if (asset == address(_WETH)) { 339 | return _getLiquidity(); 340 | } 341 | 342 | uint256 liquidity = getLiquidity(); 343 | if (liquidity == 0) { 344 | return 0; 345 | } 346 | 347 | uint256 husdHTPrice = _oracle.getUnderlyingPrice(CToken(_fHUSD)); 348 | uint256 tokenHTPrice = _oracle.getUnderlyingPrice(CToken(_reserves[asset].fTokenAddress)); 349 | 350 | uint256 decimals = IERC20Extented(asset).decimals(); 351 | uint256 tokenPrice = tokenHTPrice.mul(10**decimals).div(husdHTPrice); 352 | 353 | return liquidity.mul(10**decimals).div(tokenPrice); 354 | } 355 | 356 | function getHtPrice() private view returns (uint256) { 357 | return uint256(1e36).div(_oracle.getUnderlyingPrice(CToken(_fHUSD))); 358 | } 359 | 360 | function WETH() external view returns (address) { 361 | return address(_WETH); 362 | } 363 | 364 | function () external payable {} 365 | 366 | function addToWhitelist(address[] calldata _targets, uint256[] calldata premiums) external onlyGovernance { 367 | require(_targets.length > 0 && _targets.length == premiums.length, "FlashLoan: invalid argument"); 368 | for (uint i = 0; i < _targets.length; i++) { 369 | require(_targets[i] != address(0), "FlashLoan: whitelist can not be zero address"); 370 | _whitelist[_targets[i]].isInWhiteList = true; 371 | _whitelist[_targets[i]].premium = premiums[i]; 372 | } 373 | 374 | emit WhitelistChanged(_targets, premiums); 375 | } 376 | 377 | function removeFromWhitelist(address _target) external onlyGovernance { 378 | require(_target != address(0), "FlashLoan: whitelist can not be zero address"); 379 | _whitelist[_target].isInWhiteList = false; 380 | 381 | emit WhitelistRemoved(_target); 382 | } 383 | 384 | function isInWhitelist(address _target) public view returns (bool) { 385 | return _whitelist[_target].isInWhiteList; 386 | } 387 | 388 | function getPremium(address _target) external view returns (uint256) { 389 | if (isInWhitelist(_target)) { 390 | return _whitelist[_target].premium; 391 | } 392 | 393 | return FLASHLOAN_PREMIUM_TOTAL(); 394 | } 395 | } 396 | -------------------------------------------------------------------------------- /contracts/dependency.sol: -------------------------------------------------------------------------------- 1 | // File: @openzeppelin/contracts/math/Math.sol 2 | 3 | pragma solidity ^0.5.0; 4 | 5 | /** 6 | * @dev Standard math utilities missing in the Solidity language. 7 | */ 8 | library Math { 9 | /** 10 | * @dev Returns the largest of two numbers. 11 | */ 12 | function max(uint256 a, uint256 b) internal pure returns (uint256) { 13 | return a >= b ? a : b; 14 | } 15 | 16 | /** 17 | * @dev Returns the smallest of two numbers. 18 | */ 19 | function min(uint256 a, uint256 b) internal pure returns (uint256) { 20 | return a < b ? a : b; 21 | } 22 | 23 | /** 24 | * @dev Returns the average of two numbers. The result is rounded towards 25 | * zero. 26 | */ 27 | function average(uint256 a, uint256 b) internal pure returns (uint256) { 28 | // (a + b) / 2 can overflow, so we distribute 29 | return (a / 2) + (b / 2) + ((a % 2 + b % 2) / 2); 30 | } 31 | } 32 | 33 | 34 | // File: @openzeppelin/contracts/GSN/Context.sol 35 | 36 | pragma solidity ^0.5.0; 37 | 38 | /* 39 | * @dev Provides information about the current execution context, including the 40 | * sender of the transaction and its data. While these are generally available 41 | * via msg.sender and msg.data, they should not be accessed in such a direct 42 | * manner, since when dealing with GSN meta-transactions the account sending and 43 | * paying for execution may not be the actual sender (as far as an application 44 | * is concerned). 45 | * 46 | * This contract is only required for intermediate, library-like contracts. 47 | */ 48 | contract Context { 49 | // Empty internal constructor, to prevent people from mistakenly deploying 50 | // an instance of this contract, which should be used via inheritance. 51 | constructor () internal { } 52 | // solhint-disable-previous-line no-empty-blocks 53 | 54 | function _msgSender() internal view returns (address payable) { 55 | return msg.sender; 56 | } 57 | 58 | function _msgData() internal view returns (bytes memory) { 59 | this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691 60 | return msg.data; 61 | } 62 | } 63 | 64 | // File: @openzeppelin/contracts/ownership/Ownable.sol 65 | 66 | pragma solidity ^0.5.0; 67 | 68 | /** 69 | * @dev Contract module which provides a basic access control mechanism, where 70 | * there is an account (an owner) that can be granted exclusive access to 71 | * specific functions. 72 | * 73 | * This module is used through inheritance. It will make available the modifier 74 | * `onlyOwner`, which can be applied to your functions to restrict their use to 75 | * the owner. 76 | */ 77 | contract Ownable is Context { 78 | address private _owner; 79 | 80 | event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); 81 | 82 | /** 83 | * @dev Initializes the contract setting the deployer as the initial owner. 84 | */ 85 | constructor () internal { 86 | _owner = _msgSender(); 87 | emit OwnershipTransferred(address(0), _owner); 88 | } 89 | 90 | /** 91 | * @dev Returns the address of the current owner. 92 | */ 93 | function owner() public view returns (address) { 94 | return _owner; 95 | } 96 | 97 | /** 98 | * @dev Throws if called by any account other than the owner. 99 | */ 100 | modifier onlyOwner() { 101 | require(isOwner(), "Ownable: caller is not the owner"); 102 | _; 103 | } 104 | 105 | /** 106 | * @dev Returns true if the caller is the current owner. 107 | */ 108 | function isOwner() public view returns (bool) { 109 | return _msgSender() == _owner; 110 | } 111 | 112 | /** 113 | * @dev Leaves the contract without owner. It will not be possible to call 114 | * `onlyOwner` functions anymore. Can only be called by the current owner. 115 | * 116 | * NOTE: Renouncing ownership will leave the contract without an owner, 117 | * thereby removing any functionality that is only available to the owner. 118 | */ 119 | function renounceOwnership() public onlyOwner { 120 | emit OwnershipTransferred(_owner, address(0)); 121 | _owner = address(0); 122 | } 123 | 124 | /** 125 | * @dev Transfers ownership of the contract to a new account (`newOwner`). 126 | * Can only be called by the current owner. 127 | */ 128 | function transferOwnership(address newOwner) public onlyOwner { 129 | _transferOwnership(newOwner); 130 | } 131 | 132 | /** 133 | * @dev Transfers ownership of the contract to a new account (`newOwner`). 134 | */ 135 | function _transferOwnership(address newOwner) internal { 136 | require(newOwner != address(0), "Ownable: new owner is the zero address"); 137 | emit OwnershipTransferred(_owner, newOwner); 138 | _owner = newOwner; 139 | } 140 | } 141 | 142 | 143 | // File: @openzeppelin/contracts/math/SafeMath.sol 144 | 145 | pragma solidity ^0.5.0; 146 | 147 | /** 148 | * @dev Wrappers over Solidity's arithmetic operations with added overflow 149 | * checks. 150 | * 151 | * Arithmetic operations in Solidity wrap on overflow. This can easily result 152 | * in bugs, because programmers usually assume that an overflow raises an 153 | * error, which is the standard behavior in high level programming languages. 154 | * `SafeMath` restores this intuition by reverting the transaction when an 155 | * operation overflows. 156 | * 157 | * Using this library instead of the unchecked operations eliminates an entire 158 | * class of bugs, so it's recommended to use it always. 159 | */ 160 | library SafeMath { 161 | /** 162 | * @dev Returns the addition of two unsigned integers, reverting on 163 | * overflow. 164 | * 165 | * Counterpart to Solidity's `+` operator. 166 | * 167 | * Requirements: 168 | * - Addition cannot overflow. 169 | */ 170 | function add(uint256 a, uint256 b) internal pure returns (uint256) { 171 | uint256 c = a + b; 172 | require(c >= a, "SafeMath: addition overflow"); 173 | 174 | return c; 175 | } 176 | 177 | /** 178 | * @dev Returns the subtraction of two unsigned integers, reverting on 179 | * overflow (when the result is negative). 180 | * 181 | * Counterpart to Solidity's `-` operator. 182 | * 183 | * Requirements: 184 | * - Subtraction cannot overflow. 185 | */ 186 | function sub(uint256 a, uint256 b) internal pure returns (uint256) { 187 | return sub(a, b, "SafeMath: subtraction overflow"); 188 | } 189 | 190 | /** 191 | * @dev Returns the subtraction of two unsigned integers, reverting with custom message on 192 | * overflow (when the result is negative). 193 | * 194 | * Counterpart to Solidity's `-` operator. 195 | * 196 | * Requirements: 197 | * - Subtraction cannot overflow. 198 | * 199 | * _Available since v2.4.0._ 200 | */ 201 | function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { 202 | require(b <= a, errorMessage); 203 | uint256 c = a - b; 204 | 205 | return c; 206 | } 207 | 208 | /** 209 | * @dev Returns the multiplication of two unsigned integers, reverting on 210 | * overflow. 211 | * 212 | * Counterpart to Solidity's `*` operator. 213 | * 214 | * Requirements: 215 | * - Multiplication cannot overflow. 216 | */ 217 | function mul(uint256 a, uint256 b) internal pure returns (uint256) { 218 | // Gas optimization: this is cheaper than requiring 'a' not being zero, but the 219 | // benefit is lost if 'b' is also tested. 220 | // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522 221 | if (a == 0) { 222 | return 0; 223 | } 224 | 225 | uint256 c = a * b; 226 | require(c / a == b, "SafeMath: multiplication overflow"); 227 | 228 | return c; 229 | } 230 | 231 | /** 232 | * @dev Returns the integer division of two unsigned integers. Reverts on 233 | * division by zero. The result is rounded towards zero. 234 | * 235 | * Counterpart to Solidity's `/` operator. Note: this function uses a 236 | * `revert` opcode (which leaves remaining gas untouched) while Solidity 237 | * uses an invalid opcode to revert (consuming all remaining gas). 238 | * 239 | * Requirements: 240 | * - The divisor cannot be zero. 241 | */ 242 | function div(uint256 a, uint256 b) internal pure returns (uint256) { 243 | return div(a, b, "SafeMath: division by zero"); 244 | } 245 | 246 | /** 247 | * @dev Returns the integer division of two unsigned integers. Reverts with custom message on 248 | * division by zero. The result is rounded towards zero. 249 | * 250 | * Counterpart to Solidity's `/` operator. Note: this function uses a 251 | * `revert` opcode (which leaves remaining gas untouched) while Solidity 252 | * uses an invalid opcode to revert (consuming all remaining gas). 253 | * 254 | * Requirements: 255 | * - The divisor cannot be zero. 256 | * 257 | * _Available since v2.4.0._ 258 | */ 259 | function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { 260 | // Solidity only automatically asserts when dividing by 0 261 | require(b > 0, errorMessage); 262 | uint256 c = a / b; 263 | // assert(a == b * c + a % b); // There is no case in which this doesn't hold 264 | 265 | return c; 266 | } 267 | 268 | /** 269 | * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), 270 | * Reverts when dividing by zero. 271 | * 272 | * Counterpart to Solidity's `%` operator. This function uses a `revert` 273 | * opcode (which leaves remaining gas untouched) while Solidity uses an 274 | * invalid opcode to revert (consuming all remaining gas). 275 | * 276 | * Requirements: 277 | * - The divisor cannot be zero. 278 | */ 279 | function mod(uint256 a, uint256 b) internal pure returns (uint256) { 280 | return mod(a, b, "SafeMath: modulo by zero"); 281 | } 282 | 283 | /** 284 | * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), 285 | * Reverts with custom message when dividing by zero. 286 | * 287 | * Counterpart to Solidity's `%` operator. This function uses a `revert` 288 | * opcode (which leaves remaining gas untouched) while Solidity uses an 289 | * invalid opcode to revert (consuming all remaining gas). 290 | * 291 | * Requirements: 292 | * - The divisor cannot be zero. 293 | * 294 | * _Available since v2.4.0._ 295 | */ 296 | function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { 297 | require(b != 0, errorMessage); 298 | return a % b; 299 | } 300 | } 301 | 302 | // File: @openzeppelin/contracts/token/ERC20/IERC20.sol 303 | 304 | pragma solidity ^0.5.0; 305 | 306 | /** 307 | * @dev Interface of the ERC20 standard as defined in the EIP. Does not include 308 | * the optional functions; to access them see {ERC20Detailed}. 309 | */ 310 | interface IERC20 { 311 | /** 312 | * @dev Returns the amount of tokens in existence. 313 | */ 314 | function totalSupply() external view returns (uint256); 315 | 316 | /** 317 | * @dev Returns the amount of tokens owned by `account`. 318 | */ 319 | function balanceOf(address account) external view returns (uint256); 320 | 321 | /** 322 | * @dev Moves `amount` tokens from the caller's account to `recipient`. 323 | * 324 | * Returns a boolean value indicating whether the operation succeeded. 325 | * 326 | * Emits a {Transfer} event. 327 | */ 328 | function transfer(address recipient, uint256 amount) external returns (bool); 329 | 330 | /** 331 | * @dev Returns the remaining number of tokens that `spender` will be 332 | * allowed to spend on behalf of `owner` through {transferFrom}. This is 333 | * zero by default. 334 | * 335 | * This value changes when {approve} or {transferFrom} are called. 336 | */ 337 | function allowance(address owner, address spender) external view returns (uint256); 338 | 339 | /** 340 | * @dev Sets `amount` as the allowance of `spender` over the caller's tokens. 341 | * 342 | * Returns a boolean value indicating whether the operation succeeded. 343 | * 344 | * IMPORTANT: Beware that changing an allowance with this method brings the risk 345 | * that someone may use both the old and the new allowance by unfortunate 346 | * transaction ordering. One possible solution to mitigate this race 347 | * condition is to first reduce the spender's allowance to 0 and set the 348 | * desired value afterwards: 349 | * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 350 | * 351 | * Emits an {Approval} event. 352 | */ 353 | function approve(address spender, uint256 amount) external returns (bool); 354 | 355 | /** 356 | * @dev Moves `amount` tokens from `sender` to `recipient` using the 357 | * allowance mechanism. `amount` is then deducted from the caller's 358 | * allowance. 359 | * 360 | * Returns a boolean value indicating whether the operation succeeded. 361 | * 362 | * Emits a {Transfer} event. 363 | */ 364 | function transferFrom(address sender, address recipient, uint256 amount) external returns (bool); 365 | 366 | /** 367 | * @dev Emitted when `value` tokens are moved from one account (`from`) to 368 | * another (`to`). 369 | * 370 | * Note that `value` may be zero. 371 | */ 372 | event Transfer(address indexed from, address indexed to, uint256 value); 373 | 374 | /** 375 | * @dev Emitted when the allowance of a `spender` for an `owner` is set by 376 | * a call to {approve}. `value` is the new allowance. 377 | */ 378 | event Approval(address indexed owner, address indexed spender, uint256 value); 379 | } 380 | 381 | 382 | // File: @openzeppelin/contracts/utils/Address.sol 383 | 384 | pragma solidity ^0.5.0; 385 | 386 | /** 387 | * @dev Collection of functions related to the address type 388 | */ 389 | library Address { 390 | /** 391 | * @dev Returns true if `account` is a contract. 392 | * 393 | * This test is non-exhaustive, and there may be false-negatives: during the 394 | * execution of a contract's constructor, its address will be reported as 395 | * not containing a contract. 396 | * 397 | * IMPORTANT: It is unsafe to assume that an address for which this 398 | * function returns false is an externally-owned account (EOA) and not a 399 | * contract. 400 | */ 401 | function isContract(address account) internal view returns (bool) { 402 | // This method relies in extcodesize, which returns 0 for contracts in 403 | // construction, since the code is only stored at the end of the 404 | // constructor execution. 405 | 406 | // According to EIP-1052, 0x0 is the value returned for not-yet created accounts 407 | // and 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470 is returned 408 | // for accounts without code, i.e. `keccak256('')` 409 | bytes32 codehash; 410 | bytes32 accountHash = 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470; 411 | // solhint-disable-next-line no-inline-assembly 412 | assembly { codehash := extcodehash(account) } 413 | return (codehash != 0x0 && codehash != accountHash); 414 | } 415 | 416 | /** 417 | * @dev Converts an `address` into `address payable`. Note that this is 418 | * simply a type cast: the actual underlying value is not changed. 419 | * 420 | * _Available since v2.4.0._ 421 | */ 422 | function toPayable(address account) internal pure returns (address payable) { 423 | return address(uint160(account)); 424 | } 425 | 426 | /** 427 | * @dev Replacement for Solidity's `transfer`: sends `amount` wei to 428 | * `recipient`, forwarding all available gas and reverting on errors. 429 | * 430 | * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost 431 | * of certain opcodes, possibly making contracts go over the 2300 gas limit 432 | * imposed by `transfer`, making them unable to receive funds via 433 | * `transfer`. {sendValue} removes this limitation. 434 | * 435 | * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more]. 436 | * 437 | * IMPORTANT: because control is transferred to `recipient`, care must be 438 | * taken to not create reentrancy vulnerabilities. Consider using 439 | * {ReentrancyGuard} or the 440 | * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. 441 | * 442 | * _Available since v2.4.0._ 443 | */ 444 | function sendValue(address payable recipient, uint256 amount) internal { 445 | require(address(this).balance >= amount, "Address: insufficient balance"); 446 | 447 | // solhint-disable-next-line avoid-call-value 448 | (bool success, ) = recipient.call.value(amount)(""); 449 | require(success, "Address: unable to send value, recipient may have reverted"); 450 | } 451 | } 452 | 453 | 454 | 455 | // File: @openzeppelin/contracts/token/ERC20/SafeERC20.sol 456 | 457 | pragma solidity ^0.5.0; 458 | 459 | /** 460 | * @title SafeERC20 461 | * @dev Wrappers around ERC20 operations that throw on failure (when the token 462 | * contract returns false). Tokens that return no value (and instead revert or 463 | * throw on failure) are also supported, non-reverting calls are assumed to be 464 | * successful. 465 | * To use this library you can add a `using SafeERC20 for ERC20;` statement to your contract, 466 | * which allows you to call the safe operations as `token.safeTransfer(...)`, etc. 467 | */ 468 | library SafeERC20 { 469 | using SafeMath for uint256; 470 | using Address for address; 471 | 472 | function safeTransfer(IERC20 token, address to, uint256 value) internal { 473 | callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value)); 474 | } 475 | 476 | function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal { 477 | callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value)); 478 | } 479 | 480 | function safeApprove(IERC20 token, address spender, uint256 value) internal { 481 | // safeApprove should only be called when setting an initial allowance, 482 | // or when resetting it to zero. To increase and decrease it, use 483 | // 'safeIncreaseAllowance' and 'safeDecreaseAllowance' 484 | // solhint-disable-next-line max-line-length 485 | require((value == 0) || (token.allowance(address(this), spender) == 0), 486 | "SafeERC20: approve from non-zero to non-zero allowance" 487 | ); 488 | callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value)); 489 | } 490 | 491 | function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal { 492 | uint256 newAllowance = token.allowance(address(this), spender).add(value); 493 | callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); 494 | } 495 | 496 | function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal { 497 | uint256 newAllowance = token.allowance(address(this), spender).sub(value, "SafeERC20: decreased allowance below zero"); 498 | callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); 499 | } 500 | 501 | /** 502 | * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement 503 | * on the return value: the return value is optional (but if data is returned, it must not be false). 504 | * @param token The token targeted by the call. 505 | * @param data The call data (encoded using abi.encode or one of its variants). 506 | */ 507 | function callOptionalReturn(IERC20 token, bytes memory data) private { 508 | // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since 509 | // we're implementing it ourselves. 510 | 511 | // A Solidity high level call has three parts: 512 | // 1. The target address is checked to verify it contains contract code 513 | // 2. The call itself is made, and success asserted 514 | // 3. The return value is decoded, which in turn checks the size of the returned data. 515 | // solhint-disable-next-line max-line-length 516 | require(address(token).isContract(), "SafeERC20: call to non-contract"); 517 | 518 | // solhint-disable-next-line avoid-low-level-calls 519 | (bool success, bytes memory returndata) = address(token).call(data); 520 | require(success, "SafeERC20: low-level call failed"); 521 | 522 | if (returndata.length > 0) { // Return data is optional 523 | // solhint-disable-next-line max-line-length 524 | require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed"); 525 | } 526 | } 527 | } 528 | 529 | // File: @openzeppelin/contracts/utils/Counters.sol 530 | 531 | pragma solidity ^0.5.0; 532 | 533 | /** 534 | * @title Counters 535 | * @author Matt Condon (@shrugs) 536 | * @dev Provides counters that can only be incremented or decremented by one. This can be used e.g. to track the number 537 | * of elements in a mapping, issuing ERC721 ids, or counting request ids. 538 | * 539 | * Include with `using Counters for Counters.Counter;` 540 | * Since it is not possible to overflow a 256 bit integer with increments of one, `increment` can skip the {SafeMath} 541 | * overflow check, thereby saving gas. This does assume however correct usage, in that the underlying `_value` is never 542 | * directly accessed. 543 | */ 544 | library Counters { 545 | using SafeMath for uint256; 546 | 547 | struct Counter { 548 | // This variable should never be directly accessed by users of the library: interactions must be restricted to 549 | // the library's function. As of Solidity v0.5.2, this cannot be enforced, though there is a proposal to add 550 | // this feature: see https://github.com/ethereum/solidity/issues/4637 551 | uint256 _value; // default: 0 552 | } 553 | 554 | function current(Counter storage counter) internal view returns (uint256) { 555 | return counter._value; 556 | } 557 | 558 | function increment(Counter storage counter) internal { 559 | // The {SafeMath} overflow check can be skipped here, see the comment at the top 560 | counter._value += 1; 561 | } 562 | 563 | function decrement(Counter storage counter) internal { 564 | counter._value = counter._value.sub(1); 565 | } 566 | } 567 | 568 | // File: @openzeppelin/contracts/utils/Arrays.sol 569 | 570 | pragma solidity ^0.5.0; 571 | 572 | /** 573 | * @dev Collection of functions related to array types. 574 | */ 575 | library Arrays { 576 | /** 577 | * @dev Searches a sorted `array` and returns the first index that contains 578 | * a value greater or equal to `element`. If no such index exists (i.e. all 579 | * values in the array are strictly less than `element`), the array length is 580 | * returned. Time complexity O(log n). 581 | * 582 | * `array` is expected to be sorted in ascending order, and to contain no 583 | * repeated elements. 584 | */ 585 | function findUpperBound(uint256[] storage array, uint256 element) internal view returns (uint256) { 586 | if (array.length == 0) { 587 | return 0; 588 | } 589 | 590 | uint256 low = 0; 591 | uint256 high = array.length; 592 | 593 | while (low < high) { 594 | uint256 mid = Math.average(low, high); 595 | 596 | // Note that mid will always be strictly less than high (i.e. it will be a valid array index) 597 | // because Math.average rounds down (it does integer division with truncation). 598 | if (array[mid] > element) { 599 | high = mid; 600 | } else { 601 | low = mid + 1; 602 | } 603 | } 604 | 605 | // At this point `low` is the exclusive upper bound. We will return the inclusive upper bound. 606 | if (low > 0 && array[low - 1] == element) { 607 | return low - 1; 608 | } else { 609 | return low; 610 | } 611 | } 612 | } 613 | --------------------------------------------------------------------------------