├── test └── .gitkeep ├── migrations ├── 1_initial_migration.js └── 2_deploy_mdxswap.js ├── bscContracts ├── blockHole.sol └── oracle.sol ├── .gitignore ├── config └── config.js ├── contracts ├── interface │ ├── IMdx.sol │ ├── IERC20.sol │ ├── IMdexFactory.sol │ └── IMdexPair.sol ├── Migrations.sol ├── assets │ ├── BlackHole.sol │ ├── Repurchase.sol │ ├── Airdrop.sol │ └── AirdropMDX.sol ├── mainnet │ ├── MdxToken.sol │ └── CoinChef.sol ├── timeLock │ └── TeamTimeLock.sol ├── library │ └── SafeMath.sol ├── governance │ ├── Timelock.sol │ └── GovernorAlpha.sol ├── oracle │ └── Oracle.sol └── heco │ ├── MdxTokenHeco.sol │ ├── SwapMining.sol │ ├── HecoPool.sol │ ├── Factory.sol │ └── Router.sol ├── package.json ├── README.md └── truffle-config.js /test/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /migrations/1_initial_migration.js: -------------------------------------------------------------------------------- 1 | const Migrations = artifacts.require("Migrations"); 2 | 3 | module.exports = function (deployer) { 4 | deployer.deploy(Migrations); 5 | }; 6 | -------------------------------------------------------------------------------- /bscContracts/blockHole.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.6.12; 3 | 4 | // This is a black hole contract address 5 | // no one can transfer MDX from the contract 6 | contract BlackHole {} 7 | 8 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | logs/ 2 | npm-debug.log 3 | yarn-error.log 4 | node_modules/ 5 | package-lock.json 6 | yarn.lock 7 | .idea/ 8 | run/ 9 | .DS_Store 10 | *.sw* 11 | *.un~ 12 | typings/ 13 | .nyc_output/ 14 | build/ 15 | file/ -------------------------------------------------------------------------------- /config/config.js: -------------------------------------------------------------------------------- 1 | const mdxswap = { 2 | dev_address : "0xf8E7855E5406f1BED8c61568bee311ea23ED416F", 3 | mdx_perblock : "500000000000000000000", 4 | start_block : "7785700" 5 | } 6 | 7 | module.exports = { 8 | mdxswap 9 | } -------------------------------------------------------------------------------- /contracts/interface/IMdx.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.6.0; 3 | 4 | import {IERC20 as SIERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; 5 | 6 | interface IMdx is SIERC20 { 7 | function mint(address to, uint256 amount) external returns (bool); 8 | } 9 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "htswap", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "truffle-config.js", 6 | "directories": { 7 | "test": "test" 8 | }, 9 | "dependencies": { 10 | "@openzeppelin/contracts": "^3.3.0", 11 | "@truffle/hdwallet-provider": "^1.2.1" 12 | }, 13 | "devDependencies": {}, 14 | "scripts": { 15 | "test": "echo \"Error: no test specified\" && exit 1" 16 | }, 17 | "author": "Mdxswap", 18 | "license": "ISC" 19 | } 20 | -------------------------------------------------------------------------------- /contracts/Migrations.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.4.22 <0.9.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_mdxswap.js: -------------------------------------------------------------------------------- 1 | const MdxToken = artifacts.require("MdxToken"); 2 | const CoinChef = artifacts.require("CoinChef"); 3 | const MdxConfig = require('../config/config'); 4 | 5 | module.exports = function (deployer) { 6 | deployer.deploy(MdxToken).then(function() { 7 | return deployer.deploy( 8 | CoinChef, 9 | MdxToken.address, 10 | MdxConfig.mdxswap.dev_address, 11 | MdxConfig.mdxswap.mdx_perblock, 12 | MdxConfig.mdxswap.start_block 13 | ); 14 | }); 15 | }; -------------------------------------------------------------------------------- /contracts/assets/BlackHole.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.6.12; 3 | 4 | // This is a black hole contract address 5 | // no one can transfer MDX from the contract 6 | contract BlackHole {} 7 | 8 | /* 9 | Mdex is an automatic market-making decentralized exchange 10 | 11 | based on the concept of fund pools.It is similar in function 12 | 13 | to some DEXs on the market,but on this basis,it proposes and 14 | 15 | implements a dual-chain DEX model based on the Huobi Eco Chain 16 | 17 | and Ethereum .It combines the advantages of the low transaction 18 | 19 | fees of the Huobi Eco Chain and the prosperity of the Ethereum ecosystem, 20 | 21 | and supports the dual mining mechanism of liquidity and transactions. 22 | 23 | */ 24 | -------------------------------------------------------------------------------- /contracts/mainnet/MdxToken.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.6.0; 3 | 4 | import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; 5 | 6 | contract MdxToken is ERC20("MDX Token", "MDX"){ 7 | address public minter; 8 | 9 | // mint with max supply 10 | function mint(address _to, uint256 _amount) public onlyMinter returns (bool) { 11 | _mint(_to, _amount); 12 | return true; 13 | } 14 | 15 | function setMinter(address _newMinter) external { 16 | require(minter == address(0), "has set up"); 17 | require(_newMinter != address(0), "is zero address"); 18 | minter = _newMinter; 19 | } 20 | // modifier for mint function 21 | modifier onlyMinter() { 22 | require(msg.sender == minter, "caller is not the minter"); 23 | _; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /contracts/interface/IERC20.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >=0.5.0 <0.8.0; 2 | 3 | interface IERC20 { 4 | event Approval(address indexed owner, address indexed spender, uint value); 5 | event Transfer(address indexed from, address indexed to, uint value); 6 | 7 | function name() external view returns (string memory); 8 | 9 | function symbol() external view returns (string memory); 10 | 11 | function decimals() external view returns (uint8); 12 | 13 | function totalSupply() external view returns (uint); 14 | 15 | function balanceOf(address owner) external view returns (uint); 16 | 17 | function allowance(address owner, address spender) external view returns (uint); 18 | 19 | function approve(address spender, uint value) external returns (bool); 20 | 21 | function transfer(address to, uint value) external returns (bool); 22 | 23 | function transferFrom(address from, address to, uint value) external returns (bool); 24 | } 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ### MDEX Token Ecosystem on Heco Chain 2 | 3 | **Core Infrastructure** 4 | - Factory Contract: 0xb0b670fc1F7724119963018DB0BfA86aDb22d941 5 | - Router Contract: 0xED7d5F38C79115ca12fe6C0041abb22F0A06C300 6 | - Contract Creation Hash: 0x2ad889f82040abccb2649ea6a874796c1601fb67f91a747a80e08860c73ddf24 7 | 8 | **Token & Core Components** 9 | - MDX Token: 0x25D2e80cB6B86881Fd7e07dd263Fb79f4AbE033c 10 | - Liquidity Pool: 0xFB03e11D93632D97a8981158A632Dd5986F5E909 11 | - Swap Mining: 0x7373c42502874C88954bDd6D50b53061F018422e 12 | 13 | **Vesting & Distribution** 14 | - Team Timelock: 0xa3FD9758323C8A86292B55702F631c81283c9B79 15 | - Investor Timelock: 0xa6FE654241140469d1757A5bB8Ee844325059569 16 | - Brand Treasury: 0x465D246233Ba20e7cfc95743B5d073BE8A7746B0 17 | - Airdrop Distribution: 0x9197d717a4F45B672aCacaB4CC0C6e09222f8695 18 | 19 | **Economic Mechanisms** 20 | - Token Buyback: 0x46900C0c18ace98bAAB81561B9906Dc93287910C 21 | - Burn Address: 0xF9852C6588b70ad3c26daE47120f174527e03a25 22 | 23 | --- 24 | 25 | ### Development Setup 26 | 27 | ```bash 28 | npm install 29 | truffle migrate --network rinkeby 30 | ``` 31 | 32 | The MDEX ecosystem features a comprehensive DeFi infrastructure with built-in tokenomics including timed vesting schedules, automated liquidity mechanisms, and deflationary burn functionality, all deployed on the Heco blockchain network. 33 | -------------------------------------------------------------------------------- /contracts/interface/IMdexFactory.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >=0.5.0 <0.8.0; 2 | 3 | interface IMdexFactory { 4 | event PairCreated(address indexed token0, address indexed token1, address pair, uint); 5 | 6 | function feeTo() external view returns (address); 7 | 8 | function feeToSetter() external view returns (address); 9 | 10 | function feeToRate() external view returns (uint256); 11 | 12 | function getPair(address tokenA, address tokenB) external view returns (address pair); 13 | 14 | function allPairs(uint) external view returns (address pair); 15 | 16 | function allPairsLength() external view returns (uint); 17 | 18 | function createPair(address tokenA, address tokenB) external returns (address pair); 19 | 20 | function setFeeTo(address) external; 21 | 22 | function setFeeToSetter(address) external; 23 | 24 | function setFeeToRate(uint256) external; 25 | 26 | function sortTokens(address tokenA, address tokenB) external pure returns (address token0, address token1); 27 | 28 | function pairFor(address tokenA, address tokenB) external view returns (address pair); 29 | 30 | function getReserves(address tokenA, address tokenB) external view returns (uint256 reserveA, uint256 reserveB); 31 | 32 | function quote(uint256 amountA, uint256 reserveA, uint256 reserveB) external pure returns (uint256 amountB); 33 | 34 | function getAmountOut(uint256 amountIn, uint256 reserveIn, uint256 reserveOut) external view returns (uint256 amountOut); 35 | 36 | function getAmountIn(uint256 amountOut, uint256 reserveIn, uint256 reserveOut) external view returns (uint256 amountIn); 37 | 38 | function getAmountsOut(uint256 amountIn, address[] calldata path) external view returns (uint256[] memory amounts); 39 | 40 | function getAmountsIn(uint256 amountOut, address[] calldata path) external view returns (uint256[] memory amounts); 41 | } 42 | -------------------------------------------------------------------------------- /contracts/timeLock/TeamTimeLock.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.6.0; 2 | 3 | import '@openzeppelin/contracts/math/SafeMath.sol'; 4 | import '@openzeppelin/contracts/token/ERC20/IERC20.sol'; 5 | import '@openzeppelin/contracts/token/ERC20/SafeERC20.sol'; 6 | 7 | 8 | contract TeamTimeLock { 9 | using SafeMath for uint; 10 | using SafeERC20 for IERC20; 11 | 12 | IERC20 public token; 13 | uint constant public PERIOD = 30 days; 14 | uint constant public CYCLE_TIMES = 24; 15 | uint public fixedQuantity; // Monthly rewards are fixed 16 | uint public startTime; 17 | uint public delay; 18 | uint public cycle; // cycle already received 19 | uint public hasReward; // Rewards already withdrawn 20 | address public beneficiary; 21 | string public introduce; 22 | 23 | event WithDraw(address indexed operator, address indexed to, uint amount); 24 | 25 | constructor( 26 | address _beneficiary, 27 | address _token, 28 | uint _fixedQuantity, 29 | uint _startTime, 30 | uint _delay, 31 | string memory _introduce 32 | ) public { 33 | require(_beneficiary != address(0) && _token != address(0), "TimeLock: zero address"); 34 | require(_fixedQuantity > 0, "TimeLock: fixedQuantity is zero"); 35 | beneficiary = _beneficiary; 36 | token = IERC20(_token); 37 | fixedQuantity = _fixedQuantity; 38 | delay = _delay; 39 | startTime = _startTime.add(_delay); 40 | introduce = _introduce; 41 | } 42 | 43 | 44 | function getBalance() public view returns (uint) { 45 | return token.balanceOf(address(this)); 46 | } 47 | 48 | function getReward() public view returns (uint) { 49 | // Has ended or not started 50 | if (cycle >= CYCLE_TIMES || block.timestamp <= startTime) { 51 | return 0; 52 | } 53 | uint pCycle = (block.timestamp.sub(startTime)).div(PERIOD); 54 | if (pCycle >= CYCLE_TIMES) { 55 | return token.balanceOf(address(this)); 56 | } 57 | return pCycle.sub(cycle).mul(fixedQuantity); 58 | } 59 | 60 | function withDraw() external { 61 | uint reward = getReward(); 62 | require(reward > 0, "TimeLock: no reward"); 63 | uint pCycle = (block.timestamp.sub(startTime)).div(PERIOD); 64 | cycle = pCycle >= CYCLE_TIMES ? CYCLE_TIMES : pCycle; 65 | hasReward = hasReward.add(reward); 66 | token.safeTransfer(beneficiary, reward); 67 | emit WithDraw(msg.sender, beneficiary, reward); 68 | } 69 | 70 | // Update beneficiary address by the previous beneficiary. 71 | function setBeneficiary(address _newBeneficiary) public { 72 | require(msg.sender == beneficiary, "Not beneficiary"); 73 | beneficiary = _newBeneficiary; 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /contracts/interface/IMdexPair.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >=0.5.0 <0.8.0; 2 | 3 | interface IMdexPair { 4 | event Approval(address indexed owner, address indexed spender, uint value); 5 | event Transfer(address indexed from, address indexed to, uint value); 6 | 7 | function name() external pure returns (string memory); 8 | 9 | function symbol() external pure returns (string memory); 10 | 11 | function decimals() external pure returns (uint8); 12 | 13 | function totalSupply() external view returns (uint); 14 | 15 | function balanceOf(address owner) external view returns (uint); 16 | 17 | function allowance(address owner, address spender) external view returns (uint); 18 | 19 | function approve(address spender, uint value) external returns (bool); 20 | 21 | function transfer(address to, uint value) external returns (bool); 22 | 23 | function transferFrom(address from, address to, uint value) external returns (bool); 24 | 25 | function DOMAIN_SEPARATOR() external view returns (bytes32); 26 | 27 | function PERMIT_TYPEHASH() external pure returns (bytes32); 28 | 29 | function nonces(address owner) external view returns (uint); 30 | 31 | function permit(address owner, address spender, uint value, uint deadline, uint8 v, bytes32 r, bytes32 s) external; 32 | 33 | event Mint(address indexed sender, uint amount0, uint amount1); 34 | event Burn(address indexed sender, uint amount0, uint amount1, address indexed to); 35 | event Swap( 36 | address indexed sender, 37 | uint amount0In, 38 | uint amount1In, 39 | uint amount0Out, 40 | uint amount1Out, 41 | address indexed to 42 | ); 43 | event Sync(uint112 reserve0, uint112 reserve1); 44 | 45 | function MINIMUM_LIQUIDITY() external pure returns (uint); 46 | 47 | function factory() external view returns (address); 48 | 49 | function token0() external view returns (address); 50 | 51 | function token1() external view returns (address); 52 | 53 | function getReserves() external view returns (uint112 reserve0, uint112 reserve1, uint32 blockTimestampLast); 54 | 55 | function price0CumulativeLast() external view returns (uint); 56 | 57 | function price1CumulativeLast() external view returns (uint); 58 | 59 | function kLast() external view returns (uint); 60 | 61 | function mint(address to) external returns (uint liquidity); 62 | 63 | function burn(address to) external returns (uint amount0, uint amount1); 64 | 65 | function swap(uint amount0Out, uint amount1Out, address to, bytes calldata data) external; 66 | 67 | function skim(address to) external; 68 | 69 | function sync() external; 70 | 71 | function price(address token, uint256 baseDecimal) external view returns (uint256); 72 | 73 | function initialize(address, address) external; 74 | } 75 | -------------------------------------------------------------------------------- /contracts/assets/Repurchase.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.6.12; 3 | 4 | import "@openzeppelin/contracts/access/Ownable.sol"; 5 | import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; 6 | import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol"; 7 | import "@openzeppelin/contracts/math/SafeMath.sol"; 8 | import "@openzeppelin/contracts/utils/EnumerableSet.sol"; 9 | import "../interface/IMdexPair.sol"; 10 | 11 | contract Repurchase is Ownable { 12 | using SafeMath for uint256; 13 | using SafeERC20 for IERC20; 14 | 15 | using EnumerableSet for EnumerableSet.AddressSet; 16 | EnumerableSet.AddressSet private _caller; 17 | 18 | address public constant USDT = 0xa71EdC38d189767582C38A3145b5873052c3e47a; 19 | address public constant MDX = 0x25D2e80cB6B86881Fd7e07dd263Fb79f4AbE033c; 20 | address public constant MDX_USDT = 0x615E6285c5944540fd8bd921c9c8c56739Fd1E13; 21 | address public constant destroyAddress = 0xF9852C6588b70ad3c26daE47120f174527e03a25; 22 | address public emergencyAddress; 23 | uint256 public amountIn; 24 | 25 | constructor (uint256 _amount, address _emergencyAddress) public { 26 | require(_amount > 0, "Amount must be greater than zero"); 27 | require(_emergencyAddress != address(0), "Is zero address"); 28 | amountIn = _amount; 29 | emergencyAddress = _emergencyAddress; 30 | } 31 | 32 | function setAmountIn(uint256 _newIn) public onlyOwner { 33 | amountIn = _newIn; 34 | } 35 | 36 | function setEmergencyAddress(address _newAddress) public onlyOwner { 37 | require(_newAddress != address(0), "Is zero address"); 38 | emergencyAddress = _newAddress; 39 | } 40 | 41 | function addCaller(address _newCaller) public onlyOwner returns (bool) { 42 | require(_newCaller != address(0), "NewCaller is the zero address"); 43 | return EnumerableSet.add(_caller, _newCaller); 44 | } 45 | 46 | function delCaller(address _delCaller) public onlyOwner returns (bool) { 47 | require(_delCaller != address(0), "DelCaller is the zero address"); 48 | return EnumerableSet.remove(_caller, _delCaller); 49 | } 50 | 51 | function getCallerLength() public view returns (uint256) { 52 | return EnumerableSet.length(_caller); 53 | } 54 | 55 | function isCaller(address _call) public view returns (bool) { 56 | return EnumerableSet.contains(_caller, _call); 57 | } 58 | 59 | function getCaller(uint256 _index) public view returns (address){ 60 | require(_index <= getCallerLength() - 1, "index out of bounds"); 61 | return EnumerableSet.at(_caller, _index); 62 | } 63 | 64 | function swap() external onlyCaller returns (uint256 amountOut){ 65 | require(IERC20(USDT).balanceOf(address(this)) >= amountIn, "Insufficient contract balance"); 66 | (uint256 reserve0, uint256 reserve1,) = IMdexPair(MDX_USDT).getReserves(); 67 | uint256 amountInWithFee = amountIn.mul(997); 68 | amountOut = amountIn.mul(997).mul(reserve0) / reserve1.mul(1000).add(amountInWithFee); 69 | IERC20(USDT).safeTransfer(MDX_USDT, amountIn); 70 | IMdexPair(MDX_USDT).swap(amountOut, 0, destroyAddress, new bytes(0)); 71 | } 72 | 73 | modifier onlyCaller() { 74 | require(isCaller(msg.sender), "Not the caller"); 75 | _; 76 | } 77 | 78 | function emergencyWithdraw(address _token) public onlyOwner { 79 | require(IERC20(_token).balanceOf(address(this)) > 0, "Insufficient contract balance"); 80 | IERC20(_token).transfer(emergencyAddress, IERC20(_token).balanceOf(address(this))); 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /contracts/library/SafeMath.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >=0.5.0 <0.8.0; 2 | 3 | library SafeMath { 4 | uint256 constant WAD = 10 ** 18; 5 | uint256 constant RAY = 10 ** 27; 6 | 7 | function wad() public pure returns (uint256) { 8 | return WAD; 9 | } 10 | 11 | function ray() public pure returns (uint256) { 12 | return RAY; 13 | } 14 | 15 | function add(uint256 a, uint256 b) internal pure returns (uint256) { 16 | uint256 c = a + b; 17 | require(c >= a, "SafeMath: addition overflow"); 18 | 19 | return c; 20 | } 21 | 22 | function sub(uint256 a, uint256 b) internal pure returns (uint256) { 23 | return sub(a, b, "SafeMath: subtraction overflow"); 24 | } 25 | 26 | function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { 27 | require(b <= a, errorMessage); 28 | uint256 c = a - b; 29 | 30 | return c; 31 | } 32 | 33 | function mul(uint256 a, uint256 b) internal pure returns (uint256) { 34 | // Gas optimization: this is cheaper than requiring 'a' not being zero, but the 35 | // benefit is lost if 'b' is also tested. 36 | // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522 37 | if (a == 0) { 38 | return 0; 39 | } 40 | 41 | uint256 c = a * b; 42 | require(c / a == b, "SafeMath: multiplication overflow"); 43 | 44 | return c; 45 | } 46 | 47 | function div(uint256 a, uint256 b) internal pure returns (uint256) { 48 | return div(a, b, "SafeMath: division by zero"); 49 | } 50 | 51 | function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { 52 | // Solidity only automatically asserts when dividing by 0 53 | require(b > 0, errorMessage); 54 | uint256 c = a / b; 55 | // assert(a == b * c + a % b); // There is no case in which this doesn't hold 56 | 57 | return c; 58 | } 59 | 60 | function mod(uint256 a, uint256 b) internal pure returns (uint256) { 61 | return mod(a, b, "SafeMath: modulo by zero"); 62 | } 63 | 64 | function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { 65 | require(b != 0, errorMessage); 66 | return a % b; 67 | } 68 | 69 | function min(uint256 a, uint256 b) internal pure returns (uint256) { 70 | return a <= b ? a : b; 71 | } 72 | 73 | function max(uint256 a, uint256 b) internal pure returns (uint256) { 74 | return a >= b ? a : b; 75 | } 76 | 77 | function sqrt(uint256 a) internal pure returns (uint256 b) { 78 | if (a > 3) { 79 | b = a; 80 | uint256 x = a / 2 + 1; 81 | while (x < b) { 82 | b = x; 83 | x = (a / x + x) / 2; 84 | } 85 | } else if (a != 0) { 86 | b = 1; 87 | } 88 | } 89 | 90 | function wmul(uint256 a, uint256 b) internal pure returns (uint256) { 91 | return mul(a, b) / WAD; 92 | } 93 | 94 | function wmulRound(uint256 a, uint256 b) internal pure returns (uint256) { 95 | return add(mul(a, b), WAD / 2) / WAD; 96 | } 97 | 98 | function rmul(uint256 a, uint256 b) internal pure returns (uint256) { 99 | return mul(a, b) / RAY; 100 | } 101 | 102 | function rmulRound(uint256 a, uint256 b) internal pure returns (uint256) { 103 | return add(mul(a, b), RAY / 2) / RAY; 104 | } 105 | 106 | function wdiv(uint256 a, uint256 b) internal pure returns (uint256) { 107 | return div(mul(a, WAD), b); 108 | } 109 | 110 | function wdivRound(uint256 a, uint256 b) internal pure returns (uint256) { 111 | return add(mul(a, WAD), b / 2) / b; 112 | } 113 | 114 | function rdiv(uint256 a, uint256 b) internal pure returns (uint256) { 115 | return div(mul(a, RAY), b); 116 | } 117 | 118 | function rdivRound(uint256 a, uint256 b) internal pure returns (uint256) { 119 | return add(mul(a, RAY), b / 2) / b; 120 | } 121 | 122 | function wpow(uint256 x, uint256 n) internal pure returns (uint256) { 123 | uint256 result = WAD; 124 | while (n > 0) { 125 | if (n % 2 != 0) { 126 | result = wmul(result, x); 127 | } 128 | x = wmul(x, x); 129 | n /= 2; 130 | } 131 | return result; 132 | } 133 | 134 | function rpow(uint256 x, uint256 n) internal pure returns (uint256) { 135 | uint256 result = RAY; 136 | while (n > 0) { 137 | if (n % 2 != 0) { 138 | result = rmul(result, x); 139 | } 140 | x = rmul(x, x); 141 | n /= 2; 142 | } 143 | return result; 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /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 = ""; 23 | 24 | const fs = require('fs'); 25 | const mnemonic = fs.readFileSync("file/.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 | rinkeby: { 70 | provider: () => new HDWalletProvider(mnemonic, `https://rinkeby.infura.io/v3/${infuraKey}`), 71 | network_id: 4, // Rinkeby's id 72 | gas: 5500000, // Rinkeby has a lower block limit than mainnet 73 | confirmations: 2, // # of confs to wait between deployments. (default: 0) 74 | timeoutBlocks: 200, // # of blocks before a deployment times out (minimum/default: 50) 75 | skipDryRun: true // Skip dry run before migrations? (default: false for public nets ) 76 | }, 77 | // Useful for private networks 78 | // private: { 79 | // provider: () => new HDWalletProvider(mnemonic, `https://network.io`), 80 | // network_id: 2111, // This network is yours, in the cloud. 81 | // production: true // Treats this network as if it was a public net. (default: false) 82 | // } 83 | }, 84 | 85 | // Set default mocha options here, use special reporters etc. 86 | mocha: { 87 | // timeout: 100000 88 | }, 89 | 90 | // Configure your compilers 91 | compilers: { 92 | solc: { 93 | version: "0.6.12", // Fetch exact version from solc-bin (default: truffle's version) 94 | docker: false, // Use "0.5.1" you've installed locally with docker (default: false) 95 | settings: { // See the solidity docs for advice about optimization and evmVersion 96 | optimizer: { 97 | enabled: true, 98 | runs: 200 99 | }, 100 | evmVersion: "istanbul" 101 | } 102 | } 103 | } 104 | }; 105 | -------------------------------------------------------------------------------- /contracts/governance/Timelock.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.6.12; 2 | 3 | import "@openzeppelin/contracts/math/SafeMath.sol"; 4 | 5 | contract Timelock { 6 | using SafeMath for uint; 7 | 8 | event NewAdmin(address indexed newAdmin); 9 | event NewPendingAdmin(address indexed newPendingAdmin); 10 | event NewDelay(uint indexed newDelay); 11 | event CancelTransaction(bytes32 indexed txHash, address indexed target, uint value, string signature, bytes data, uint eta); 12 | event ExecuteTransaction(bytes32 indexed txHash, address indexed target, uint value, string signature, bytes data, uint eta); 13 | event QueueTransaction(bytes32 indexed txHash, address indexed target, uint value, string signature, bytes data, uint eta); 14 | 15 | uint public constant GRACE_PERIOD = 14 days; 16 | uint public constant MINIMUM_DELAY = 2 days; 17 | uint public constant MAXIMUM_DELAY = 30 days; 18 | 19 | address public admin; 20 | address public pendingAdmin; 21 | uint public delay; 22 | 23 | mapping(bytes32 => bool) public queuedTransactions; 24 | 25 | 26 | constructor(address admin_, uint delay_) public { 27 | require(delay_ >= MINIMUM_DELAY, "Timelock::constructor: Delay must exceed minimum delay."); 28 | require(delay_ <= MAXIMUM_DELAY, "Timelock::setDelay: Delay must not exceed maximum delay."); 29 | 30 | admin = admin_; 31 | delay = delay_; 32 | } 33 | 34 | receive() external payable {} 35 | 36 | function setDelay(uint delay_) public { 37 | require(msg.sender == address(this), "Timelock::setDelay: Call must come from Timelock."); 38 | require(delay_ >= MINIMUM_DELAY, "Timelock::setDelay: Delay must exceed minimum delay."); 39 | require(delay_ <= MAXIMUM_DELAY, "Timelock::setDelay: Delay must not exceed maximum delay."); 40 | delay = delay_; 41 | 42 | emit NewDelay(delay); 43 | } 44 | 45 | function acceptAdmin() public { 46 | require(msg.sender == pendingAdmin, "Timelock::acceptAdmin: Call must come from pendingAdmin."); 47 | admin = msg.sender; 48 | pendingAdmin = address(0); 49 | 50 | emit NewAdmin(admin); 51 | } 52 | 53 | function setPendingAdmin(address pendingAdmin_) public { 54 | require(msg.sender == address(this), "Timelock::setPendingAdmin: Call must come from Timelock."); 55 | pendingAdmin = pendingAdmin_; 56 | 57 | emit NewPendingAdmin(pendingAdmin); 58 | } 59 | 60 | function queueTransaction(address target, uint value, string memory signature, bytes memory data, uint eta) public returns (bytes32) { 61 | require(msg.sender == admin, "Timelock::queueTransaction: Call must come from admin."); 62 | require(eta >= getBlockTimestamp().add(delay), "Timelock::queueTransaction: Estimated execution block must satisfy delay."); 63 | 64 | bytes32 txHash = keccak256(abi.encode(target, value, signature, data, eta)); 65 | queuedTransactions[txHash] = true; 66 | 67 | emit QueueTransaction(txHash, target, value, signature, data, eta); 68 | return txHash; 69 | } 70 | 71 | function cancelTransaction(address target, uint value, string memory signature, bytes memory data, uint eta) public { 72 | require(msg.sender == admin, "Timelock::cancelTransaction: Call must come from admin."); 73 | 74 | bytes32 txHash = keccak256(abi.encode(target, value, signature, data, eta)); 75 | queuedTransactions[txHash] = false; 76 | 77 | emit CancelTransaction(txHash, target, value, signature, data, eta); 78 | } 79 | 80 | function executeTransaction(address target, uint value, string memory signature, bytes memory data, uint eta) public payable returns (bytes memory) { 81 | require(msg.sender == admin, "Timelock::executeTransaction: Call must come from admin."); 82 | 83 | bytes32 txHash = keccak256(abi.encode(target, value, signature, data, eta)); 84 | require(queuedTransactions[txHash], "Timelock::executeTransaction: Transaction hasn't been queued."); 85 | require(getBlockTimestamp() >= eta, "Timelock::executeTransaction: Transaction hasn't surpassed time lock."); 86 | require(getBlockTimestamp() <= eta.add(GRACE_PERIOD), "Timelock::executeTransaction: Transaction is stale."); 87 | 88 | queuedTransactions[txHash] = false; 89 | 90 | bytes memory callData; 91 | 92 | if (bytes(signature).length == 0) { 93 | callData = data; 94 | } else { 95 | callData = abi.encodePacked(bytes4(keccak256(bytes(signature))), data); 96 | } 97 | 98 | // solium-disable-next-line security/no-call-value 99 | (bool success, bytes memory returnData) = target.call{value : value}(callData); 100 | require(success, "Timelock::executeTransaction: Transaction execution reverted."); 101 | 102 | emit ExecuteTransaction(txHash, target, value, signature, data, eta); 103 | 104 | return returnData; 105 | } 106 | 107 | function getBlockTimestamp() internal view returns (uint) { 108 | // solium-disable-next-line security/no-block-members 109 | return block.timestamp; 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /contracts/oracle/Oracle.sol: -------------------------------------------------------------------------------- 1 | pragma solidity =0.6.6; 2 | 3 | import "../interface/IMdexFactory.sol"; 4 | import "../interface/IMdexPair.sol"; 5 | 6 | library SafeMath { 7 | function add(uint x, uint y) internal pure returns (uint z) { 8 | require((z = x + y) >= x, 'ds-math-add-overflow'); 9 | } 10 | 11 | function sub(uint x, uint y) internal pure returns (uint z) { 12 | require((z = x - y) <= x, 'ds-math-sub-underflow'); 13 | } 14 | 15 | function mul(uint x, uint y) internal pure returns (uint z) { 16 | require(y == 0 || (z = x * y) / y == x, 'ds-math-mul-overflow'); 17 | } 18 | } 19 | 20 | library FixedPoint { 21 | // range: [0, 2**112 - 1] 22 | // resolution: 1 / 2**112 23 | struct uq112x112 { 24 | uint224 _x; 25 | } 26 | 27 | // range: [0, 2**144 - 1] 28 | // resolution: 1 / 2**112 29 | struct uq144x112 { 30 | uint _x; 31 | } 32 | 33 | uint8 private constant RESOLUTION = 112; 34 | 35 | // encode a uint112 as a UQ112x112 36 | function encode(uint112 x) internal pure returns (uq112x112 memory) { 37 | return uq112x112(uint224(x) << RESOLUTION); 38 | } 39 | 40 | // encodes a uint144 as a UQ144x112 41 | function encode144(uint144 x) internal pure returns (uq144x112 memory) { 42 | return uq144x112(uint256(x) << RESOLUTION); 43 | } 44 | 45 | // divide a UQ112x112 by a uint112, returning a UQ112x112 46 | function div(uq112x112 memory self, uint112 x) internal pure returns (uq112x112 memory) { 47 | require(x != 0, 'FixedPoint: DIV_BY_ZERO'); 48 | return uq112x112(self._x / uint224(x)); 49 | } 50 | 51 | // multiply a UQ112x112 by a uint, returning a UQ144x112 52 | // reverts on overflow 53 | function mul(uq112x112 memory self, uint y) internal pure returns (uq144x112 memory) { 54 | uint z; 55 | require(y == 0 || (z = uint(self._x) * y) / y == uint(self._x), "FixedPoint: MULTIPLICATION_OVERFLOW"); 56 | return uq144x112(z); 57 | } 58 | 59 | // returns a UQ112x112 which represents the ratio of the numerator to the denominator 60 | // equivalent to encode(numerator).div(denominator) 61 | function fraction(uint112 numerator, uint112 denominator) internal pure returns (uq112x112 memory) { 62 | require(denominator > 0, "FixedPoint: DIV_BY_ZERO"); 63 | return uq112x112((uint224(numerator) << RESOLUTION) / denominator); 64 | } 65 | 66 | // decode a UQ112x112 into a uint112 by truncating after the radix point 67 | function decode(uq112x112 memory self) internal pure returns (uint112) { 68 | return uint112(self._x >> RESOLUTION); 69 | } 70 | 71 | // decode a UQ144x112 into a uint144 by truncating after the radix point 72 | function decode144(uq144x112 memory self) internal pure returns (uint144) { 73 | return uint144(self._x >> RESOLUTION); 74 | } 75 | } 76 | 77 | library MdexOracleLibrary { 78 | using FixedPoint for *; 79 | 80 | // helper function that returns the current block timestamp within the range of uint32, i.e. [0, 2**32 - 1] 81 | function currentBlockTimestamp() internal view returns (uint32) { 82 | return uint32(block.timestamp % 2 ** 32); 83 | } 84 | 85 | // produces the cumulative price using counterfactuals to save gas and avoid a call to sync. 86 | function currentCumulativePrices( 87 | address pair 88 | ) internal view returns (uint price0Cumulative, uint price1Cumulative, uint32 blockTimestamp) { 89 | blockTimestamp = currentBlockTimestamp(); 90 | price0Cumulative = IMdexPair(pair).price0CumulativeLast(); 91 | price1Cumulative = IMdexPair(pair).price1CumulativeLast(); 92 | 93 | // if time has elapsed since the last update on the pair, mock the accumulated price values 94 | (uint112 reserve0, uint112 reserve1, uint32 blockTimestampLast) = IMdexPair(pair).getReserves(); 95 | if (blockTimestampLast != blockTimestamp) { 96 | // subtraction overflow is desired 97 | uint32 timeElapsed = blockTimestamp - blockTimestampLast; 98 | // addition overflow is desired 99 | // counterfactual 100 | price0Cumulative += uint(FixedPoint.fraction(reserve1, reserve0)._x) * timeElapsed; 101 | // counterfactual 102 | price1Cumulative += uint(FixedPoint.fraction(reserve0, reserve1)._x) * timeElapsed; 103 | } 104 | } 105 | } 106 | 107 | contract Oracle { 108 | using FixedPoint for *; 109 | using SafeMath for uint; 110 | 111 | struct Observation { 112 | uint timestamp; 113 | uint price0Cumulative; 114 | uint price1Cumulative; 115 | } 116 | 117 | address public immutable factory; 118 | uint public constant CYCLE = 30 minutes; 119 | 120 | // mapping from pair address to a list of price observations of that pair 121 | mapping(address => Observation) public pairObservations; 122 | 123 | constructor(address factory_) public { 124 | factory = factory_; 125 | } 126 | 127 | 128 | function update(address tokenA, address tokenB) external { 129 | address pair = IMdexFactory(factory).pairFor(tokenA, tokenB); 130 | 131 | Observation storage observation = pairObservations[pair]; 132 | uint timeElapsed = block.timestamp - observation.timestamp; 133 | require(timeElapsed >= CYCLE, 'MDEXOracle: PERIOD_NOT_ELAPSED'); 134 | (uint price0Cumulative, uint price1Cumulative,) = MdexOracleLibrary.currentCumulativePrices(pair); 135 | observation.timestamp = block.timestamp; 136 | observation.price0Cumulative = price0Cumulative; 137 | observation.price1Cumulative = price1Cumulative; 138 | } 139 | 140 | 141 | function computeAmountOut( 142 | uint priceCumulativeStart, uint priceCumulativeEnd, 143 | uint timeElapsed, uint amountIn 144 | ) private pure returns (uint amountOut) { 145 | // overflow is desired. 146 | FixedPoint.uq112x112 memory priceAverage = FixedPoint.uq112x112( 147 | uint224((priceCumulativeEnd - priceCumulativeStart) / timeElapsed) 148 | ); 149 | amountOut = priceAverage.mul(amountIn).decode144(); 150 | } 151 | 152 | 153 | function consult(address tokenIn, uint amountIn, address tokenOut) external view returns (uint amountOut) { 154 | address pair = IMdexFactory(factory).pairFor(tokenIn, tokenOut); 155 | Observation storage observation = pairObservations[pair]; 156 | uint timeElapsed = block.timestamp - observation.timestamp; 157 | (uint price0Cumulative, uint price1Cumulative,) = MdexOracleLibrary.currentCumulativePrices(pair); 158 | (address token0,) = IMdexFactory(factory).sortTokens(tokenIn, tokenOut); 159 | 160 | if (token0 == tokenIn) { 161 | return computeAmountOut(observation.price0Cumulative, price0Cumulative, timeElapsed, amountIn); 162 | } else { 163 | return computeAmountOut(observation.price1Cumulative, price1Cumulative, timeElapsed, amountIn); 164 | } 165 | } 166 | } 167 | -------------------------------------------------------------------------------- /contracts/assets/Airdrop.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.6.0; 3 | 4 | import "@openzeppelin/contracts/access/Ownable.sol"; 5 | import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; 6 | import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol"; 7 | import "@openzeppelin/contracts/math/SafeMath.sol"; 8 | 9 | library TransferHelper { 10 | function safeApprove(address token, address to, uint value) internal { 11 | // bytes4(keccak256(bytes('approve(address,uint256)'))); 12 | (bool success, bytes memory data) = token.call(abi.encodeWithSelector(0x095ea7b3, to, value)); 13 | require(success && (data.length == 0 || abi.decode(data, (bool))), 'TransferHelper: APPROVE_FAILED'); 14 | } 15 | 16 | function safeTransfer(address token, address to, uint value) internal { 17 | // bytes4(keccak256(bytes('transfer(address,uint256)'))); 18 | (bool success, bytes memory data) = token.call(abi.encodeWithSelector(0xa9059cbb, to, value)); 19 | require(success && (data.length == 0 || abi.decode(data, (bool))), 'TransferHelper: TRANSFER_FAILED'); 20 | } 21 | 22 | function safeTransferFrom(address token, address from, address to, uint value) internal { 23 | // bytes4(keccak256(bytes('transferFrom(address,address,uint256)'))); 24 | (bool success, bytes memory data) = token.call(abi.encodeWithSelector(0x23b872dd, from, to, value)); 25 | require(success && (data.length == 0 || abi.decode(data, (bool))), 'TransferHelper: TRANSFER_FROM_FAILED'); 26 | } 27 | } 28 | 29 | interface IWHT { 30 | function balanceOf(address owner) external view returns (uint); 31 | 32 | function transfer(address to, uint value) external returns (bool); 33 | } 34 | 35 | contract Airdrop is Ownable { 36 | using SafeMath for uint256; 37 | using SafeERC20 for IERC20; 38 | 39 | struct UserInfo { 40 | uint256 amount; 41 | uint256 rewardDebt; 42 | } 43 | 44 | struct PoolInfo { 45 | IERC20 lpToken; 46 | uint256 allocPoint; 47 | uint256 lastRewardBlock; 48 | uint256 accWhtPerShare; 49 | } 50 | 51 | IWHT public wht; 52 | // Airdrop tokens for per block. 53 | uint256 public whtPerBlock; 54 | // Info of each pool. 55 | PoolInfo[] public poolInfo; 56 | // Info of each user that stakes LP tokens. 57 | mapping(uint256 => mapping(address => UserInfo)) public userInfo; 58 | // Total allocation points. Must be the sum of all allocation points in all pools. 59 | uint256 public totalAllocPoint = 0; 60 | // The block number when wht mining starts. 61 | uint256 public startBlock; 62 | // The block number when wht mining end; 63 | uint256 public endBlock; 64 | // Airdrop cycle default 1day 65 | uint256 public cycle; 66 | 67 | event Deposit(address indexed user, uint256 indexed pid, uint256 amount); 68 | event Withdraw(address indexed user, uint256 indexed pid, uint256 amount); 69 | event EmergencyWithdraw(address indexed user, uint256 indexed pid, uint256 amount); 70 | 71 | constructor( 72 | IWHT _wht, 73 | uint256 _cycle 74 | ) public { 75 | wht = _wht; 76 | cycle = _cycle; 77 | } 78 | 79 | function poolLength() external view returns (uint256) { 80 | return poolInfo.length; 81 | } 82 | 83 | function newAirdrop(uint256 _whtAmount, uint256 _newPerBlock, uint256 _startBlock) public onlyOwner { 84 | require(block.number > endBlock && _startBlock >= endBlock, "Not finished"); 85 | massUpdatePools(); 86 | uint256 beforeAmount = IWHT(wht).balanceOf(address(this)); 87 | TransferHelper.safeTransferFrom(address(wht), msg.sender, address(this), _whtAmount); 88 | uint256 afterAmount = IWHT(wht).balanceOf(address(this)); 89 | uint256 balance = afterAmount.sub(beforeAmount); 90 | require(balance == _whtAmount, "Error balance"); 91 | require(balance > 0 && (cycle * _newPerBlock) <= balance, "Balance not enough"); 92 | whtPerBlock = _newPerBlock; 93 | startBlock = _startBlock; 94 | endBlock = _startBlock.add(cycle); 95 | updatePoolLastRewardBlock(_startBlock); 96 | } 97 | 98 | function updatePoolLastRewardBlock(uint256 _lastRewardBlock) private { 99 | uint256 length = poolInfo.length; 100 | for (uint256 pid = 0; pid < length; ++pid) { 101 | PoolInfo storage pool = poolInfo[pid]; 102 | pool.lastRewardBlock = _lastRewardBlock; 103 | } 104 | } 105 | 106 | function setCycle(uint256 _newCycle) public onlyOwner { 107 | cycle = _newCycle; 108 | } 109 | 110 | // Add a new lp to the pool. Can only be called by the owner. 111 | // XXX DO NOT add the same LP token more than once. Rewards will be messed up if you do. 112 | function add(uint256 _allocPoint, IERC20 _lpToken, bool _withUpdate) public onlyOwner { 113 | require(address(_lpToken) != address(0), "lpToken is the zero address"); 114 | if (_withUpdate) { 115 | massUpdatePools(); 116 | } 117 | uint256 lastRewardBlock = block.number > startBlock ? block.number : startBlock; 118 | totalAllocPoint = totalAllocPoint.add(_allocPoint); 119 | poolInfo.push(PoolInfo({ 120 | lpToken : _lpToken, 121 | allocPoint : _allocPoint, 122 | lastRewardBlock : lastRewardBlock, 123 | accWhtPerShare : 0 124 | })); 125 | } 126 | 127 | // Update the given pool's wht allocation point. Can only be called by the owner. 128 | function set(uint256 _pid, uint256 _allocPoint, bool _withUpdate) public onlyOwner { 129 | if (_withUpdate) { 130 | massUpdatePools(); 131 | } 132 | totalAllocPoint = totalAllocPoint.sub(poolInfo[_pid].allocPoint).add(_allocPoint); 133 | poolInfo[_pid].allocPoint = _allocPoint; 134 | } 135 | 136 | // Update reward variables for all pools. Be careful of gas spending! 137 | function massUpdatePools() public { 138 | uint256 length = poolInfo.length; 139 | for (uint256 pid = 0; pid < length; ++pid) { 140 | updatePool(pid); 141 | } 142 | } 143 | 144 | // Update reward variables of the given pool to be up-to-date. 145 | function updatePool(uint256 _pid) public { 146 | PoolInfo storage pool = poolInfo[_pid]; 147 | uint256 number = block.number > endBlock ? endBlock : block.number; 148 | if (number <= pool.lastRewardBlock) { 149 | return; 150 | } 151 | uint256 lpSupply = pool.lpToken.balanceOf(address(this)); 152 | if (lpSupply == 0) { 153 | pool.lastRewardBlock = number; 154 | return; 155 | } 156 | uint256 multiplier = number.sub(pool.lastRewardBlock); 157 | uint256 whtReward = multiplier.mul(whtPerBlock).mul(pool.allocPoint).div(totalAllocPoint); 158 | pool.accWhtPerShare = pool.accWhtPerShare.add(whtReward.mul(1e12).div(lpSupply)); 159 | pool.lastRewardBlock = number; 160 | } 161 | 162 | 163 | function pending(uint256 _pid, address _user) external view returns (uint256) { 164 | PoolInfo storage pool = poolInfo[_pid]; 165 | UserInfo storage user = userInfo[_pid][_user]; 166 | uint256 accWhtPerShare = pool.accWhtPerShare; 167 | uint256 lpSupply = pool.lpToken.balanceOf(address(this)); 168 | uint256 number = block.number > endBlock ? endBlock : block.number; 169 | if (number > pool.lastRewardBlock && lpSupply != 0) { 170 | uint256 multiplier = number.sub(pool.lastRewardBlock); 171 | uint256 whtReward = multiplier.mul(whtPerBlock).mul(pool.allocPoint).div(totalAllocPoint); 172 | accWhtPerShare = accWhtPerShare.add(whtReward.mul(1e12).div(lpSupply)); 173 | } 174 | return user.amount.mul(accWhtPerShare).div(1e12).sub(user.rewardDebt); 175 | } 176 | 177 | 178 | // Deposit LP tokens dividends WHT; 179 | function deposit(uint256 _pid, uint256 _amount) public { 180 | PoolInfo storage pool = poolInfo[_pid]; 181 | UserInfo storage user = userInfo[_pid][msg.sender]; 182 | updatePool(_pid); 183 | if (user.amount > 0) { 184 | uint256 pendingAmount = user.amount.mul(pool.accWhtPerShare).div(1e12).sub(user.rewardDebt); 185 | if (pendingAmount > 0) { 186 | safeWhtTransfer(msg.sender, pendingAmount); 187 | } 188 | } 189 | if (_amount > 0) { 190 | pool.lpToken.safeTransferFrom(address(msg.sender), address(this), _amount); 191 | user.amount = user.amount.add(_amount); 192 | } 193 | user.rewardDebt = user.amount.mul(pool.accWhtPerShare).div(1e12); 194 | emit Deposit(msg.sender, _pid, _amount); 195 | } 196 | 197 | // Withdraw LP tokens. 198 | function withdraw(uint256 _pid, uint256 _amount) public { 199 | PoolInfo storage pool = poolInfo[_pid]; 200 | UserInfo storage user = userInfo[_pid][msg.sender]; 201 | require(user.amount >= _amount, "withdraw: not good"); 202 | updatePool(_pid); 203 | uint256 pendingAmount = user.amount.mul(pool.accWhtPerShare).div(1e12).sub(user.rewardDebt); 204 | if (pendingAmount > 0) { 205 | safeWhtTransfer(msg.sender, pendingAmount); 206 | } 207 | if (_amount > 0) { 208 | user.amount = user.amount.sub(_amount); 209 | pool.lpToken.safeTransfer(address(msg.sender), _amount); 210 | } 211 | user.rewardDebt = user.amount.mul(pool.accWhtPerShare).div(1e12); 212 | emit Withdraw(msg.sender, _pid, _amount); 213 | } 214 | 215 | // Withdraw without caring about rewards. EMERGENCY ONLY. 216 | function emergencyWithdraw(uint256 _pid) public { 217 | PoolInfo storage pool = poolInfo[_pid]; 218 | UserInfo storage user = userInfo[_pid][msg.sender]; 219 | uint256 amount = user.amount; 220 | user.amount = 0; 221 | user.rewardDebt = 0; 222 | pool.lpToken.safeTransfer(address(msg.sender), amount); 223 | emit EmergencyWithdraw(msg.sender, _pid, amount); 224 | } 225 | 226 | // Safe wht transfer function, just in case if rounding error causes pool to not have enough whts. 227 | function safeWhtTransfer(address _to, uint256 _amount) internal { 228 | uint256 whtBal = IWHT(wht).balanceOf(address(this)); 229 | if (_amount > whtBal) { 230 | wht.transfer(_to, whtBal); 231 | } else { 232 | wht.transfer(_to, _amount); 233 | } 234 | } 235 | } 236 | -------------------------------------------------------------------------------- /contracts/assets/AirdropMDX.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.6.0; 3 | 4 | import "@openzeppelin/contracts/access/Ownable.sol"; 5 | import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; 6 | import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol"; 7 | import "@openzeppelin/contracts/math/SafeMath.sol"; 8 | 9 | 10 | library TransferHelper { 11 | function safeApprove(address token, address to, uint value) internal { 12 | // bytes4(keccak256(bytes('approve(address,uint256)'))); 13 | (bool success, bytes memory data) = token.call(abi.encodeWithSelector(0x095ea7b3, to, value)); 14 | require(success && (data.length == 0 || abi.decode(data, (bool))), 'TransferHelper: APPROVE_FAILED'); 15 | } 16 | 17 | function safeTransfer(address token, address to, uint value) internal { 18 | // bytes4(keccak256(bytes('transfer(address,uint256)'))); 19 | (bool success, bytes memory data) = token.call(abi.encodeWithSelector(0xa9059cbb, to, value)); 20 | require(success && (data.length == 0 || abi.decode(data, (bool))), 'TransferHelper: TRANSFER_FAILED'); 21 | } 22 | 23 | function safeTransferFrom(address token, address from, address to, uint value) internal { 24 | // bytes4(keccak256(bytes('transferFrom(address,address,uint256)'))); 25 | (bool success, bytes memory data) = token.call(abi.encodeWithSelector(0x23b872dd, from, to, value)); 26 | require(success && (data.length == 0 || abi.decode(data, (bool))), 'TransferHelper: TRANSFER_FROM_FAILED'); 27 | } 28 | } 29 | 30 | contract AirdropMDX is Ownable { 31 | using SafeMath for uint256; 32 | using SafeERC20 for IERC20; 33 | 34 | struct UserInfo { 35 | uint256 amount; 36 | uint256 rewardDebt; 37 | } 38 | 39 | struct PoolInfo { 40 | IERC20 lpToken; 41 | uint256 allocPoint; 42 | uint256 lastRewardBlock; 43 | uint256 accMDXPerShare; 44 | uint256 mdxAmount; 45 | } 46 | 47 | address public mdx; 48 | // Airdrop tokens for per block. 49 | uint256 public mdxPerBlock; 50 | // Info of each pool. 51 | PoolInfo[] public poolInfo; 52 | // Info of each user that stakes LP tokens. 53 | mapping(uint256 => mapping(address => UserInfo)) public userInfo; 54 | // Total allocation points. Must be the sum of all allocation points in all pools. 55 | uint256 public totalAllocPoint = 0; 56 | // The block number when mdx mining starts. 57 | uint256 public startBlock; 58 | // The block number when mdx mining end; 59 | uint256 public endBlock; 60 | // Airdrop cycle default 1day 61 | uint256 public cycle; 62 | 63 | event Deposit(address indexed user, uint256 indexed pid, uint256 amount); 64 | event Withdraw(address indexed user, uint256 indexed pid, uint256 amount); 65 | event EmergencyWithdraw(address indexed user, uint256 indexed pid, uint256 amount); 66 | 67 | constructor( 68 | address _mdx, 69 | uint256 _cycle 70 | ) public { 71 | mdx = _mdx; 72 | cycle = _cycle; 73 | } 74 | 75 | function poolLength() external view returns (uint256) { 76 | return poolInfo.length; 77 | } 78 | 79 | function newAirdrop(uint256 _mdxAmount, uint256 _newPerBlock, uint256 _startBlock) public onlyOwner { 80 | require(block.number > endBlock && _startBlock >= endBlock, "Not finished"); 81 | massUpdatePools(); 82 | uint256 beforeAmount = IERC20(mdx).balanceOf(address(this)); 83 | TransferHelper.safeTransferFrom(mdx, msg.sender, address(this), _mdxAmount); 84 | uint256 afterAmount = IERC20(mdx).balanceOf(address(this)); 85 | uint256 balance = afterAmount.sub(beforeAmount); 86 | require(balance == _mdxAmount, "Error balance"); 87 | require(balance > 0 && (cycle * _newPerBlock) <= balance, "Balance not enough"); 88 | mdxPerBlock = _newPerBlock; 89 | startBlock = _startBlock; 90 | endBlock = _startBlock.add(cycle); 91 | updatePoolLastRewardBlock(_startBlock); 92 | } 93 | 94 | function updatePoolLastRewardBlock(uint256 _lastRewardBlock) private { 95 | uint256 length = poolInfo.length; 96 | for (uint256 pid = 0; pid < length; ++pid) { 97 | PoolInfo storage pool = poolInfo[pid]; 98 | pool.lastRewardBlock = _lastRewardBlock; 99 | } 100 | } 101 | 102 | function setCycle(uint256 _newCycle) public onlyOwner { 103 | cycle = _newCycle; 104 | } 105 | 106 | // Add a new lp to the pool. Can only be called by the owner. 107 | // XXX DO NOT add the same LP token more than once. Rewards will be messed up if you do. 108 | function add(uint256 _allocPoint, IERC20 _lpToken, bool _withUpdate) public onlyOwner { 109 | require(address(_lpToken) != address(0), "lpToken is the zero address"); 110 | if (_withUpdate) { 111 | massUpdatePools(); 112 | } 113 | uint256 lastRewardBlock = block.number > startBlock ? block.number : startBlock; 114 | totalAllocPoint = totalAllocPoint.add(_allocPoint); 115 | poolInfo.push(PoolInfo({ 116 | lpToken : _lpToken, 117 | allocPoint : _allocPoint, 118 | lastRewardBlock : lastRewardBlock, 119 | accMDXPerShare : 0, 120 | mdxAmount : 0 121 | })); 122 | } 123 | 124 | // Update the given pool's wht allocation point. Can only be called by the owner. 125 | function set(uint256 _pid, uint256 _allocPoint, bool _withUpdate) public onlyOwner { 126 | if (_withUpdate) { 127 | massUpdatePools(); 128 | } 129 | totalAllocPoint = totalAllocPoint.sub(poolInfo[_pid].allocPoint).add(_allocPoint); 130 | poolInfo[_pid].allocPoint = _allocPoint; 131 | } 132 | 133 | // Update reward variables for all pools. Be careful of gas spending! 134 | function massUpdatePools() public { 135 | uint256 length = poolInfo.length; 136 | for (uint256 pid = 0; pid < length; ++pid) { 137 | updatePool(pid); 138 | } 139 | } 140 | 141 | // Update reward variables of the given pool to be up-to-date. 142 | function updatePool(uint256 _pid) public { 143 | PoolInfo storage pool = poolInfo[_pid]; 144 | uint256 number = block.number > endBlock ? endBlock : block.number; 145 | if (number <= pool.lastRewardBlock) { 146 | return; 147 | } 148 | uint256 lpSupply; 149 | if (address(pool.lpToken) == mdx) { 150 | lpSupply = pool.mdxAmount; 151 | } else { 152 | lpSupply = pool.lpToken.balanceOf(address(this)); 153 | } 154 | if (lpSupply == 0) { 155 | pool.lastRewardBlock = number; 156 | return; 157 | } 158 | uint256 multiplier = number.sub(pool.lastRewardBlock); 159 | uint256 mdxReward = multiplier.mul(mdxPerBlock).mul(pool.allocPoint).div(totalAllocPoint); 160 | pool.accMDXPerShare = pool.accMDXPerShare.add(mdxReward.mul(1e12).div(lpSupply)); 161 | pool.lastRewardBlock = number; 162 | } 163 | 164 | 165 | function pending(uint256 _pid, address _user) external view returns (uint256) { 166 | PoolInfo storage pool = poolInfo[_pid]; 167 | UserInfo storage user = userInfo[_pid][_user]; 168 | uint256 accMDXPerShare = pool.accMDXPerShare; 169 | uint256 lpSupply; 170 | if (address(pool.lpToken) == mdx) { 171 | lpSupply = pool.mdxAmount; 172 | } else { 173 | lpSupply = pool.lpToken.balanceOf(address(this)); 174 | } 175 | uint256 number = block.number > endBlock ? endBlock : block.number; 176 | if (number > pool.lastRewardBlock && lpSupply != 0) { 177 | uint256 multiplier = number.sub(pool.lastRewardBlock); 178 | uint256 mdxReward = multiplier.mul(mdxPerBlock).mul(pool.allocPoint).div(totalAllocPoint); 179 | accMDXPerShare = accMDXPerShare.add(mdxReward.mul(1e12).div(lpSupply)); 180 | } 181 | return user.amount.mul(accMDXPerShare).div(1e12).sub(user.rewardDebt); 182 | } 183 | 184 | 185 | // Deposit LP tokens dividends WHT; 186 | function deposit(uint256 _pid, uint256 _amount) public { 187 | PoolInfo storage pool = poolInfo[_pid]; 188 | UserInfo storage user = userInfo[_pid][msg.sender]; 189 | updatePool(_pid); 190 | if (user.amount > 0) { 191 | uint256 pendingAmount = user.amount.mul(pool.accMDXPerShare).div(1e12).sub(user.rewardDebt); 192 | if (pendingAmount > 0) { 193 | safeMDXTransfer(msg.sender, pendingAmount, pool.mdxAmount); 194 | } 195 | } 196 | if (_amount > 0) { 197 | pool.lpToken.safeTransferFrom(address(msg.sender), address(this), _amount); 198 | user.amount = user.amount.add(_amount); 199 | if (address(pool.lpToken) == mdx) { 200 | pool.mdxAmount = pool.mdxAmount.add(_amount); 201 | } 202 | } 203 | user.rewardDebt = user.amount.mul(pool.accMDXPerShare).div(1e12); 204 | emit Deposit(msg.sender, _pid, _amount); 205 | } 206 | 207 | // Withdraw LP tokens. 208 | function withdraw(uint256 _pid, uint256 _amount) public { 209 | PoolInfo storage pool = poolInfo[_pid]; 210 | UserInfo storage user = userInfo[_pid][msg.sender]; 211 | require(user.amount >= _amount, "withdraw: not good"); 212 | updatePool(_pid); 213 | uint256 pendingAmount = user.amount.mul(pool.accMDXPerShare).div(1e12).sub(user.rewardDebt); 214 | if (pendingAmount > 0) { 215 | safeMDXTransfer(msg.sender, pendingAmount, pool.mdxAmount); 216 | } 217 | if (_amount > 0) { 218 | user.amount = user.amount.sub(_amount); 219 | if (address(pool.lpToken) == mdx) { 220 | pool.mdxAmount = pool.mdxAmount.sub(_amount); 221 | } 222 | pool.lpToken.safeTransfer(address(msg.sender), _amount); 223 | } 224 | user.rewardDebt = user.amount.mul(pool.accMDXPerShare).div(1e12); 225 | emit Withdraw(msg.sender, _pid, _amount); 226 | } 227 | 228 | // Withdraw without caring about rewards. EMERGENCY ONLY. 229 | function emergencyWithdraw(uint256 _pid) public { 230 | PoolInfo storage pool = poolInfo[_pid]; 231 | UserInfo storage user = userInfo[_pid][msg.sender]; 232 | uint256 amount = user.amount; 233 | user.amount = 0; 234 | user.rewardDebt = 0; 235 | if (address(pool.lpToken) == mdx) { 236 | pool.mdxAmount = pool.mdxAmount.sub(amount); 237 | } 238 | pool.lpToken.safeTransfer(address(msg.sender), amount); 239 | emit EmergencyWithdraw(msg.sender, _pid, amount); 240 | } 241 | 242 | // Safe mdx transfer function, just in case if rounding error causes pool to not have enough mdxs. 243 | function safeMDXTransfer(address _to, uint256 _amount, uint256 _poolMDXAmount) internal { 244 | uint256 mdxBalance = IERC20(mdx).balanceOf(address(this)); 245 | mdxBalance = mdxBalance.sub(_poolMDXAmount); 246 | if (_amount > mdxBalance) { 247 | IERC20(mdx).transfer(_to, mdxBalance); 248 | } else { 249 | IERC20(mdx).transfer(_to, _amount); 250 | } 251 | } 252 | } 253 | -------------------------------------------------------------------------------- /contracts/heco/MdxTokenHeco.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.6.0; 3 | pragma experimental ABIEncoderV2; 4 | 5 | import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; 6 | import "@openzeppelin/contracts/access/Ownable.sol"; 7 | import "@openzeppelin/contracts/utils/EnumerableSet.sol"; 8 | 9 | abstract contract DelegateERC20 is ERC20 { 10 | /// @notice A record of each accounts delegate 11 | mapping (address => address) internal _delegates; 12 | 13 | /// @notice A checkpoint for marking number of votes from a given block 14 | struct Checkpoint { 15 | uint32 fromBlock; 16 | uint256 votes; 17 | } 18 | 19 | /// @notice A record of votes checkpoints for each account, by index 20 | mapping (address => mapping (uint32 => Checkpoint)) public checkpoints; 21 | 22 | /// @notice The number of checkpoints for each account 23 | mapping (address => uint32) public numCheckpoints; 24 | 25 | /// @notice The EIP-712 typehash for the contract's domain 26 | bytes32 public constant DOMAIN_TYPEHASH = keccak256("EIP712Domain(string name,uint256 chainId,address verifyingContract)"); 27 | 28 | /// @notice The EIP-712 typehash for the delegation struct used by the contract 29 | bytes32 public constant DELEGATION_TYPEHASH = keccak256("Delegation(address delegatee,uint256 nonce,uint256 expiry)"); 30 | 31 | /// @notice A record of states for signing / validating signatures 32 | mapping (address => uint) public nonces; 33 | 34 | 35 | // support delegates mint 36 | function _mint(address account, uint256 amount) internal override virtual { 37 | super._mint(account, amount); 38 | 39 | // add delegates to the minter 40 | _moveDelegates(address(0), _delegates[account], amount); 41 | } 42 | 43 | 44 | function _transfer(address sender, address recipient, uint256 amount) internal override virtual { 45 | super._transfer(sender, recipient, amount); 46 | _moveDelegates(_delegates[sender], _delegates[recipient], amount); 47 | } 48 | 49 | 50 | /** 51 | * @notice Delegate votes from `msg.sender` to `delegatee` 52 | * @param delegatee The address to delegate votes to 53 | */ 54 | function delegate(address delegatee) external { 55 | return _delegate(msg.sender, delegatee); 56 | } 57 | 58 | /** 59 | * @notice Delegates votes from signatory to `delegatee` 60 | * @param delegatee The address to delegate votes to 61 | * @param nonce The contract state required to match the signature 62 | * @param expiry The time at which to expire the signature 63 | * @param v The recovery byte of the signature 64 | * @param r Half of the ECDSA signature pair 65 | * @param s Half of the ECDSA signature pair 66 | */ 67 | function delegateBySig( 68 | address delegatee, 69 | uint nonce, 70 | uint expiry, 71 | uint8 v, 72 | bytes32 r, 73 | bytes32 s 74 | ) 75 | external 76 | { 77 | bytes32 domainSeparator = keccak256( 78 | abi.encode( 79 | DOMAIN_TYPEHASH, 80 | keccak256(bytes(name())), 81 | getChainId(), 82 | address(this) 83 | ) 84 | ); 85 | 86 | bytes32 structHash = keccak256( 87 | abi.encode( 88 | DELEGATION_TYPEHASH, 89 | delegatee, 90 | nonce, 91 | expiry 92 | ) 93 | ); 94 | 95 | bytes32 digest = keccak256( 96 | abi.encodePacked( 97 | "\x19\x01", 98 | domainSeparator, 99 | structHash 100 | ) 101 | ); 102 | 103 | address signatory = ecrecover(digest, v, r, s); 104 | require(signatory != address(0), "MdxToken::delegateBySig: invalid signature"); 105 | require(nonce == nonces[signatory]++, "MdxToken::delegateBySig: invalid nonce"); 106 | require(now <= expiry, "MdxToken::delegateBySig: signature expired"); 107 | return _delegate(signatory, delegatee); 108 | } 109 | 110 | /** 111 | * @notice Gets the current votes balance for `account` 112 | * @param account The address to get votes balance 113 | * @return The number of current votes for `account` 114 | */ 115 | function getCurrentVotes(address account) 116 | external 117 | view 118 | returns (uint256) 119 | { 120 | uint32 nCheckpoints = numCheckpoints[account]; 121 | return nCheckpoints > 0 ? checkpoints[account][nCheckpoints - 1].votes : 0; 122 | } 123 | 124 | /** 125 | * @notice Determine the prior number of votes for an account as of a block number 126 | * @dev Block number must be a finalized block or else this function will revert to prevent misinformation. 127 | * @param account The address of the account to check 128 | * @param blockNumber The block number to get the vote balance at 129 | * @return The number of votes the account had as of the given block 130 | */ 131 | function getPriorVotes(address account, uint blockNumber) 132 | external 133 | view 134 | returns (uint256) 135 | { 136 | require(blockNumber < block.number, "MdxToken::getPriorVotes: not yet determined"); 137 | 138 | uint32 nCheckpoints = numCheckpoints[account]; 139 | if (nCheckpoints == 0) { 140 | return 0; 141 | } 142 | 143 | // First check most recent balance 144 | if (checkpoints[account][nCheckpoints - 1].fromBlock <= blockNumber) { 145 | return checkpoints[account][nCheckpoints - 1].votes; 146 | } 147 | 148 | // Next check implicit zero balance 149 | if (checkpoints[account][0].fromBlock > blockNumber) { 150 | return 0; 151 | } 152 | 153 | uint32 lower = 0; 154 | uint32 upper = nCheckpoints - 1; 155 | while (upper > lower) { 156 | uint32 center = upper - (upper - lower) / 2; // ceil, avoiding overflow 157 | Checkpoint memory cp = checkpoints[account][center]; 158 | if (cp.fromBlock == blockNumber) { 159 | return cp.votes; 160 | } else if (cp.fromBlock < blockNumber) { 161 | lower = center; 162 | } else { 163 | upper = center - 1; 164 | } 165 | } 166 | return checkpoints[account][lower].votes; 167 | } 168 | 169 | function _delegate(address delegator, address delegatee) 170 | internal 171 | { 172 | address currentDelegate = _delegates[delegator]; 173 | uint256 delegatorBalance = balanceOf(delegator); // balance of underlying balances (not scaled); 174 | _delegates[delegator] = delegatee; 175 | 176 | _moveDelegates(currentDelegate, delegatee, delegatorBalance); 177 | 178 | emit DelegateChanged(delegator, currentDelegate, delegatee); 179 | } 180 | 181 | function _moveDelegates(address srcRep, address dstRep, uint256 amount) internal { 182 | if (srcRep != dstRep && amount > 0) { 183 | if (srcRep != address(0)) { 184 | // decrease old representative 185 | uint32 srcRepNum = numCheckpoints[srcRep]; 186 | uint256 srcRepOld = srcRepNum > 0 ? checkpoints[srcRep][srcRepNum - 1].votes : 0; 187 | uint256 srcRepNew = srcRepOld.sub(amount); 188 | _writeCheckpoint(srcRep, srcRepNum, srcRepOld, srcRepNew); 189 | } 190 | 191 | if (dstRep != address(0)) { 192 | // increase new representative 193 | uint32 dstRepNum = numCheckpoints[dstRep]; 194 | uint256 dstRepOld = dstRepNum > 0 ? checkpoints[dstRep][dstRepNum - 1].votes : 0; 195 | uint256 dstRepNew = dstRepOld.add(amount); 196 | _writeCheckpoint(dstRep, dstRepNum, dstRepOld, dstRepNew); 197 | } 198 | } 199 | } 200 | 201 | function _writeCheckpoint( 202 | address delegatee, 203 | uint32 nCheckpoints, 204 | uint256 oldVotes, 205 | uint256 newVotes 206 | ) 207 | internal 208 | { 209 | uint32 blockNumber = safe32(block.number, "MdxToken::_writeCheckpoint: block number exceeds 32 bits"); 210 | 211 | if (nCheckpoints > 0 && checkpoints[delegatee][nCheckpoints - 1].fromBlock == blockNumber) { 212 | checkpoints[delegatee][nCheckpoints - 1].votes = newVotes; 213 | } else { 214 | checkpoints[delegatee][nCheckpoints] = Checkpoint(blockNumber, newVotes); 215 | numCheckpoints[delegatee] = nCheckpoints + 1; 216 | } 217 | 218 | emit DelegateVotesChanged(delegatee, oldVotes, newVotes); 219 | } 220 | 221 | function safe32(uint n, string memory errorMessage) internal pure returns (uint32) { 222 | require(n < 2**32, errorMessage); 223 | return uint32(n); 224 | } 225 | 226 | function getChainId() internal pure returns (uint) { 227 | uint256 chainId; 228 | assembly { chainId := chainid() } 229 | 230 | return chainId; 231 | } 232 | 233 | /// @notice An event thats emitted when an account changes its delegate 234 | event DelegateChanged(address indexed delegator, address indexed fromDelegate, address indexed toDelegate); 235 | 236 | /// @notice An event thats emitted when a delegate account's vote balance changes 237 | event DelegateVotesChanged(address indexed delegate, uint previousBalance, uint newBalance); 238 | 239 | } 240 | 241 | contract MdxToken is DelegateERC20, Ownable { 242 | uint256 private constant preMineSupply = 200000000 * 1e18; 243 | uint256 private constant maxSupply = 1000000000 * 1e18; // the total supply 244 | 245 | using EnumerableSet for EnumerableSet.AddressSet; 246 | EnumerableSet.AddressSet private _minters; 247 | 248 | constructor() public ERC20("MDX Token", "MDX"){ 249 | _mint(msg.sender, preMineSupply); 250 | } 251 | 252 | // mint with max supply 253 | function mint(address _to, uint256 _amount) public onlyMinter returns (bool) { 254 | if (_amount.add(totalSupply()) > maxSupply) { 255 | return false; 256 | } 257 | _mint(_to, _amount); 258 | return true; 259 | } 260 | 261 | function addMinter(address _addMinter) public onlyOwner returns (bool) { 262 | require(_addMinter != address(0), "MdxToken: _addMinter is the zero address"); 263 | return EnumerableSet.add(_minters, _addMinter); 264 | } 265 | 266 | function delMinter(address _delMinter) public onlyOwner returns (bool) { 267 | require(_delMinter != address(0), "MdxToken: _delMinter is the zero address"); 268 | return EnumerableSet.remove(_minters, _delMinter); 269 | } 270 | 271 | function getMinterLength() public view returns (uint256) { 272 | return EnumerableSet.length(_minters); 273 | } 274 | 275 | function isMinter(address account) public view returns (bool) { 276 | return EnumerableSet.contains(_minters, account); 277 | } 278 | 279 | function getMinter(uint256 _index) public view onlyOwner returns (address){ 280 | require(_index <= getMinterLength() - 1, "MdxToken: index out of bounds"); 281 | return EnumerableSet.at(_minters, _index); 282 | } 283 | 284 | // modifier for mint function 285 | modifier onlyMinter() { 286 | require(isMinter(msg.sender), "caller is not the minter"); 287 | _; 288 | } 289 | 290 | } 291 | -------------------------------------------------------------------------------- /bscContracts/oracle.sol: -------------------------------------------------------------------------------- 1 | pragma solidity =0.6.6; 2 | 3 | library SafeMath { 4 | function add(uint x, uint y) internal pure returns (uint z) { 5 | require((z = x + y) >= x, 'ds-math-add-overflow'); 6 | } 7 | 8 | function sub(uint x, uint y) internal pure returns (uint z) { 9 | require((z = x - y) <= x, 'ds-math-sub-underflow'); 10 | } 11 | 12 | function mul(uint x, uint y) internal pure returns (uint z) { 13 | require(y == 0 || (z = x * y) / y == x, 'ds-math-mul-overflow'); 14 | } 15 | } 16 | 17 | library FixedPoint { 18 | // range: [0, 2**112 - 1] 19 | // resolution: 1 / 2**112 20 | struct uq112x112 { 21 | uint224 _x; 22 | } 23 | 24 | // range: [0, 2**144 - 1] 25 | // resolution: 1 / 2**112 26 | struct uq144x112 { 27 | uint _x; 28 | } 29 | 30 | uint8 private constant RESOLUTION = 112; 31 | 32 | // encode a uint112 as a UQ112x112 33 | function encode(uint112 x) internal pure returns (uq112x112 memory) { 34 | return uq112x112(uint224(x) << RESOLUTION); 35 | } 36 | 37 | // encodes a uint144 as a UQ144x112 38 | function encode144(uint144 x) internal pure returns (uq144x112 memory) { 39 | return uq144x112(uint256(x) << RESOLUTION); 40 | } 41 | 42 | // divide a UQ112x112 by a uint112, returning a UQ112x112 43 | function div(uq112x112 memory self, uint112 x) internal pure returns (uq112x112 memory) { 44 | require(x != 0, 'FixedPoint: DIV_BY_ZERO'); 45 | return uq112x112(self._x / uint224(x)); 46 | } 47 | 48 | // multiply a UQ112x112 by a uint, returning a UQ144x112 49 | // reverts on overflow 50 | function mul(uq112x112 memory self, uint y) internal pure returns (uq144x112 memory) { 51 | uint z; 52 | require(y == 0 || (z = uint(self._x) * y) / y == uint(self._x), "FixedPoint: MULTIPLICATION_OVERFLOW"); 53 | return uq144x112(z); 54 | } 55 | 56 | // returns a UQ112x112 which represents the ratio of the numerator to the denominator 57 | // equivalent to encode(numerator).div(denominator) 58 | function fraction(uint112 numerator, uint112 denominator) internal pure returns (uq112x112 memory) { 59 | require(denominator > 0, "FixedPoint: DIV_BY_ZERO"); 60 | return uq112x112((uint224(numerator) << RESOLUTION) / denominator); 61 | } 62 | 63 | // decode a UQ112x112 into a uint112 by truncating after the radix point 64 | function decode(uq112x112 memory self) internal pure returns (uint112) { 65 | return uint112(self._x >> RESOLUTION); 66 | } 67 | 68 | // decode a UQ144x112 into a uint144 by truncating after the radix point 69 | function decode144(uq144x112 memory self) internal pure returns (uint144) { 70 | return uint144(self._x >> RESOLUTION); 71 | } 72 | } 73 | 74 | interface IMdexPair { 75 | event Approval(address indexed owner, address indexed spender, uint value); 76 | event Transfer(address indexed from, address indexed to, uint value); 77 | 78 | function name() external pure returns (string memory); 79 | 80 | function symbol() external pure returns (string memory); 81 | 82 | function decimals() external pure returns (uint8); 83 | 84 | function totalSupply() external view returns (uint); 85 | 86 | function balanceOf(address owner) external view returns (uint); 87 | 88 | function allowance(address owner, address spender) external view returns (uint); 89 | 90 | function approve(address spender, uint value) external returns (bool); 91 | 92 | function transfer(address to, uint value) external returns (bool); 93 | 94 | function transferFrom(address from, address to, uint value) external returns (bool); 95 | 96 | function DOMAIN_SEPARATOR() external view returns (bytes32); 97 | 98 | function PERMIT_TYPEHASH() external pure returns (bytes32); 99 | 100 | function nonces(address owner) external view returns (uint); 101 | 102 | function permit(address owner, address spender, uint value, uint deadline, uint8 v, bytes32 r, bytes32 s) external; 103 | 104 | event Mint(address indexed sender, uint amount0, uint amount1); 105 | event Burn(address indexed sender, uint amount0, uint amount1, address indexed to); 106 | event Swap( 107 | address indexed sender, 108 | uint amount0In, 109 | uint amount1In, 110 | uint amount0Out, 111 | uint amount1Out, 112 | address indexed to 113 | ); 114 | event Sync(uint112 reserve0, uint112 reserve1); 115 | 116 | function MINIMUM_LIQUIDITY() external pure returns (uint); 117 | 118 | function factory() external view returns (address); 119 | 120 | function token0() external view returns (address); 121 | 122 | function token1() external view returns (address); 123 | 124 | function getReserves() external view returns (uint112 reserve0, uint112 reserve1, uint32 blockTimestampLast); 125 | 126 | function price0CumulativeLast() external view returns (uint); 127 | 128 | function price1CumulativeLast() external view returns (uint); 129 | 130 | function kLast() external view returns (uint); 131 | 132 | function mint(address to) external returns (uint liquidity); 133 | 134 | function burn(address to) external returns (uint amount0, uint amount1); 135 | 136 | function swap(uint amount0Out, uint amount1Out, address to, bytes calldata data) external; 137 | 138 | function skim(address to) external; 139 | 140 | function sync() external; 141 | 142 | function price(address token, uint256 baseDecimal) external view returns (uint256); 143 | 144 | function initialize(address, address) external; 145 | } 146 | 147 | library MdexOracleLibrary { 148 | using FixedPoint for *; 149 | 150 | // helper function that returns the current block timestamp within the range of uint32, i.e. [0, 2**32 - 1] 151 | function currentBlockTimestamp() internal view returns (uint32) { 152 | return uint32(block.timestamp % 2 ** 32); 153 | } 154 | 155 | // produces the cumulative price using counterfactuals to save gas and avoid a call to sync. 156 | function currentCumulativePrices( 157 | address pair 158 | ) internal view returns (uint price0Cumulative, uint price1Cumulative, uint32 blockTimestamp) { 159 | blockTimestamp = currentBlockTimestamp(); 160 | price0Cumulative = IMdexPair(pair).price0CumulativeLast(); 161 | price1Cumulative = IMdexPair(pair).price1CumulativeLast(); 162 | 163 | // if time has elapsed since the last update on the pair, mock the accumulated price values 164 | (uint112 reserve0, uint112 reserve1, uint32 blockTimestampLast) = IMdexPair(pair).getReserves(); 165 | if (blockTimestampLast != blockTimestamp) { 166 | // subtraction overflow is desired 167 | uint32 timeElapsed = blockTimestamp - blockTimestampLast; 168 | // addition overflow is desired 169 | // counterfactual 170 | price0Cumulative += uint(FixedPoint.fraction(reserve1, reserve0)._x) * timeElapsed; 171 | // counterfactual 172 | price1Cumulative += uint(FixedPoint.fraction(reserve0, reserve1)._x) * timeElapsed; 173 | } 174 | } 175 | } 176 | 177 | interface IMdexFactory { 178 | event PairCreated(address indexed token0, address indexed token1, address pair, uint); 179 | 180 | function feeTo() external view returns (address); 181 | 182 | function feeToSetter() external view returns (address); 183 | 184 | function feeToRate() external view returns (uint256); 185 | 186 | function initCodeHash() external view returns (bytes32); 187 | 188 | function getPair(address tokenA, address tokenB) external view returns (address pair); 189 | 190 | function allPairs(uint) external view returns (address pair); 191 | 192 | function allPairsLength() external view returns (uint); 193 | 194 | function createPair(address tokenA, address tokenB) external returns (address pair); 195 | 196 | function setFeeTo(address) external; 197 | 198 | function setFeeToSetter(address) external; 199 | 200 | function setFeeToRate(uint256) external; 201 | 202 | function setInitCodeHash(bytes32) external; 203 | 204 | function sortTokens(address tokenA, address tokenB) external pure returns (address token0, address token1); 205 | 206 | function pairFor(address tokenA, address tokenB) external view returns (address pair); 207 | 208 | function getReserves(address tokenA, address tokenB) external view returns (uint256 reserveA, uint256 reserveB); 209 | 210 | function quote(uint256 amountA, uint256 reserveA, uint256 reserveB) external pure returns (uint256 amountB); 211 | 212 | function getAmountOut(uint256 amountIn, uint256 reserveIn, uint256 reserveOut) external view returns (uint256 amountOut); 213 | 214 | function getAmountIn(uint256 amountOut, uint256 reserveIn, uint256 reserveOut) external view returns (uint256 amountIn); 215 | 216 | function getAmountsOut(uint256 amountIn, address[] calldata path) external view returns (uint256[] memory amounts); 217 | 218 | function getAmountsIn(uint256 amountOut, address[] calldata path) external view returns (uint256[] memory amounts); 219 | } 220 | 221 | 222 | contract Oracle { 223 | using FixedPoint for *; 224 | using SafeMath for uint; 225 | 226 | struct Observation { 227 | uint timestamp; 228 | uint price0Cumulative; 229 | uint price1Cumulative; 230 | } 231 | 232 | address public immutable factory; 233 | uint public constant CYCLE = 15 minutes; 234 | 235 | // mapping from pair address to a list of price observations of that pair 236 | mapping(address => Observation) public pairObservations; 237 | 238 | constructor(address factory_) public { 239 | factory = factory_; 240 | } 241 | 242 | 243 | function update(address tokenA, address tokenB) external { 244 | address pair = IMdexFactory(factory).pairFor(tokenA, tokenB); 245 | 246 | Observation storage observation = pairObservations[pair]; 247 | uint timeElapsed = block.timestamp - observation.timestamp; 248 | require(timeElapsed >= CYCLE, 'MDEXOracle: PERIOD_NOT_ELAPSED'); 249 | (uint price0Cumulative, uint price1Cumulative,) = MdexOracleLibrary.currentCumulativePrices(pair); 250 | observation.timestamp = block.timestamp; 251 | observation.price0Cumulative = price0Cumulative; 252 | observation.price1Cumulative = price1Cumulative; 253 | } 254 | 255 | 256 | function computeAmountOut( 257 | uint priceCumulativeStart, uint priceCumulativeEnd, 258 | uint timeElapsed, uint amountIn 259 | ) private pure returns (uint amountOut) { 260 | // overflow is desired. 261 | FixedPoint.uq112x112 memory priceAverage = FixedPoint.uq112x112( 262 | uint224((priceCumulativeEnd - priceCumulativeStart) / timeElapsed) 263 | ); 264 | amountOut = priceAverage.mul(amountIn).decode144(); 265 | } 266 | 267 | 268 | function consult(address tokenIn, uint amountIn, address tokenOut) external view returns (uint amountOut) { 269 | address pair = IMdexFactory(factory).pairFor(tokenIn, tokenOut); 270 | Observation storage observation = pairObservations[pair]; 271 | uint timeElapsed = block.timestamp - observation.timestamp; 272 | (uint price0Cumulative, uint price1Cumulative,) = MdexOracleLibrary.currentCumulativePrices(pair); 273 | (address token0,) = IMdexFactory(factory).sortTokens(tokenIn, tokenOut); 274 | 275 | if (token0 == tokenIn) { 276 | return computeAmountOut(observation.price0Cumulative, price0Cumulative, timeElapsed, amountIn); 277 | } else { 278 | return computeAmountOut(observation.price1Cumulative, price1Cumulative, timeElapsed, amountIn); 279 | } 280 | } 281 | } 282 | 283 | -------------------------------------------------------------------------------- /contracts/heco/SwapMining.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity >=0.6.0 <0.8.0; 4 | 5 | import "@openzeppelin/contracts/access/Ownable.sol"; 6 | import "@openzeppelin/contracts/utils/EnumerableSet.sol"; 7 | import "../interface/IERC20.sol"; 8 | import "../library/SafeMath.sol"; 9 | import "../interface/IMdexFactory.sol"; 10 | import "../interface/IMdexPair.sol"; 11 | import "../interface/IMdx.sol"; 12 | 13 | interface IOracle { 14 | function update(address tokenA, address tokenB) external; 15 | 16 | function consult(address tokenIn, uint amountIn, address tokenOut) external view returns (uint amountOut); 17 | } 18 | 19 | contract SwapMining is Ownable { 20 | using SafeMath for uint256; 21 | using EnumerableSet for EnumerableSet.AddressSet; 22 | EnumerableSet.AddressSet private _whitelist; 23 | 24 | // MDX tokens created per block 25 | uint256 public mdxPerBlock; 26 | // The block number when MDX mining starts. 27 | uint256 public startBlock; 28 | // How many blocks are halved 29 | uint256 public halvingPeriod = 5256000; 30 | // Total allocation points 31 | uint256 public totalAllocPoint = 0; 32 | IOracle public oracle; 33 | // router address 34 | address public router; 35 | // factory address 36 | IMdexFactory public factory; 37 | // mdx token address 38 | IMdx public mdx; 39 | // Calculate price based on HUSD 40 | address public targetToken; 41 | // pair corresponding pid 42 | mapping(address => uint256) public pairOfPid; 43 | 44 | constructor( 45 | IMdx _mdx, 46 | IMdexFactory _factory, 47 | IOracle _oracle, 48 | address _router, 49 | address _targetToken, 50 | uint256 _mdxPerBlock, 51 | uint256 _startBlock 52 | ) public { 53 | mdx = _mdx; 54 | factory = _factory; 55 | oracle = _oracle; 56 | router = _router; 57 | targetToken = _targetToken; 58 | mdxPerBlock = _mdxPerBlock; 59 | startBlock = _startBlock; 60 | } 61 | 62 | struct UserInfo { 63 | uint256 quantity; // How many LP tokens the user has provided 64 | uint256 blockNumber; // Last transaction block 65 | } 66 | 67 | struct PoolInfo { 68 | address pair; // Trading pairs that can be mined 69 | uint256 quantity; // Current amount of LPs 70 | uint256 totalQuantity; // All quantity 71 | uint256 allocPoint; // How many allocation points assigned to this pool 72 | uint256 allocMdxAmount; // How many MDXs 73 | uint256 lastRewardBlock;// Last transaction block 74 | } 75 | 76 | PoolInfo[] public poolInfo; 77 | mapping(uint256 => mapping(address => UserInfo)) public userInfo; 78 | 79 | 80 | function poolLength() public view returns (uint256) { 81 | return poolInfo.length; 82 | } 83 | 84 | 85 | function addPair(uint256 _allocPoint, address _pair, bool _withUpdate) public onlyOwner { 86 | require(_pair != address(0), "_pair is the zero address"); 87 | if (_withUpdate) { 88 | massMintPools(); 89 | } 90 | uint256 lastRewardBlock = block.number > startBlock ? block.number : startBlock; 91 | totalAllocPoint = totalAllocPoint.add(_allocPoint); 92 | poolInfo.push(PoolInfo({ 93 | pair : _pair, 94 | quantity : 0, 95 | totalQuantity : 0, 96 | allocPoint : _allocPoint, 97 | allocMdxAmount : 0, 98 | lastRewardBlock : lastRewardBlock 99 | })); 100 | pairOfPid[_pair] = poolLength() - 1; 101 | } 102 | 103 | // Update the allocPoint of the pool 104 | function setPair(uint256 _pid, uint256 _allocPoint, bool _withUpdate) public onlyOwner { 105 | if (_withUpdate) { 106 | massMintPools(); 107 | } 108 | totalAllocPoint = totalAllocPoint.sub(poolInfo[_pid].allocPoint).add(_allocPoint); 109 | poolInfo[_pid].allocPoint = _allocPoint; 110 | } 111 | 112 | // Set the number of mdx produced by each block 113 | function setMdxPerBlock(uint256 _newPerBlock) public onlyOwner { 114 | massMintPools(); 115 | mdxPerBlock = _newPerBlock; 116 | } 117 | 118 | // Only tokens in the whitelist can be mined MDX 119 | function addWhitelist(address _addToken) public onlyOwner returns (bool) { 120 | require(_addToken != address(0), "SwapMining: token is the zero address"); 121 | return EnumerableSet.add(_whitelist, _addToken); 122 | } 123 | 124 | function delWhitelist(address _delToken) public onlyOwner returns (bool) { 125 | require(_delToken != address(0), "SwapMining: token is the zero address"); 126 | return EnumerableSet.remove(_whitelist, _delToken); 127 | } 128 | 129 | function getWhitelistLength() public view returns (uint256) { 130 | return EnumerableSet.length(_whitelist); 131 | } 132 | 133 | function isWhitelist(address _token) public view returns (bool) { 134 | return EnumerableSet.contains(_whitelist, _token); 135 | } 136 | 137 | function getWhitelist(uint256 _index) public view returns (address){ 138 | require(_index <= getWhitelistLength() - 1, "SwapMining: index out of bounds"); 139 | return EnumerableSet.at(_whitelist, _index); 140 | } 141 | 142 | function setHalvingPeriod(uint256 _block) public onlyOwner { 143 | halvingPeriod = _block; 144 | } 145 | 146 | function setRouter(address newRouter) public onlyOwner { 147 | require(newRouter != address(0), "SwapMining: new router is the zero address"); 148 | router = newRouter; 149 | } 150 | 151 | function setOracle(IOracle _oracle) public onlyOwner { 152 | require(address(_oracle) != address(0), "SwapMining: new oracle is the zero address"); 153 | oracle = _oracle; 154 | } 155 | 156 | // At what phase 157 | function phase(uint256 blockNumber) public view returns (uint256) { 158 | if (halvingPeriod == 0) { 159 | return 0; 160 | } 161 | if (blockNumber > startBlock) { 162 | return (blockNumber.sub(startBlock).sub(1)).div(halvingPeriod); 163 | } 164 | return 0; 165 | } 166 | 167 | function phase() public view returns (uint256) { 168 | return phase(block.number); 169 | } 170 | 171 | function reward(uint256 blockNumber) public view returns (uint256) { 172 | uint256 _phase = phase(blockNumber); 173 | return mdxPerBlock.div(2 ** _phase); 174 | } 175 | 176 | function reward() public view returns (uint256) { 177 | return reward(block.number); 178 | } 179 | 180 | // Rewards for the current block 181 | function getMdxReward(uint256 _lastRewardBlock) public view returns (uint256) { 182 | require(_lastRewardBlock <= block.number, "SwapMining: must little than the current block number"); 183 | uint256 blockReward = 0; 184 | uint256 n = phase(_lastRewardBlock); 185 | uint256 m = phase(block.number); 186 | // If it crosses the cycle 187 | while (n < m) { 188 | n++; 189 | // Get the last block of the previous cycle 190 | uint256 r = n.mul(halvingPeriod).add(startBlock); 191 | // Get rewards from previous periods 192 | blockReward = blockReward.add((r.sub(_lastRewardBlock)).mul(reward(r))); 193 | _lastRewardBlock = r; 194 | } 195 | blockReward = blockReward.add((block.number.sub(_lastRewardBlock)).mul(reward(block.number))); 196 | return blockReward; 197 | } 198 | 199 | // Update all pools Called when updating allocPoint and setting new blocks 200 | function massMintPools() public { 201 | uint256 length = poolInfo.length; 202 | for (uint256 pid = 0; pid < length; ++pid) { 203 | mint(pid); 204 | } 205 | } 206 | 207 | function mint(uint256 _pid) public returns (bool) { 208 | PoolInfo storage pool = poolInfo[_pid]; 209 | if (block.number <= pool.lastRewardBlock) { 210 | return false; 211 | } 212 | uint256 blockReward = getMdxReward(pool.lastRewardBlock); 213 | if (blockReward <= 0) { 214 | return false; 215 | } 216 | // Calculate the rewards obtained by the pool based on the allocPoint 217 | uint256 mdxReward = blockReward.mul(pool.allocPoint).div(totalAllocPoint); 218 | mdx.mint(address(this), mdxReward); 219 | // Increase the number of tokens in the current pool 220 | pool.allocMdxAmount = pool.allocMdxAmount.add(mdxReward); 221 | pool.lastRewardBlock = block.number; 222 | return true; 223 | } 224 | 225 | // swapMining only router 226 | function swap(address account, address input, address output, uint256 amount) public onlyRouter returns (bool) { 227 | require(account != address(0), "SwapMining: taker swap account is the zero address"); 228 | require(input != address(0), "SwapMining: taker swap input is the zero address"); 229 | require(output != address(0), "SwapMining: taker swap output is the zero address"); 230 | 231 | if (poolLength() <= 0) { 232 | return false; 233 | } 234 | 235 | if (!isWhitelist(input) || !isWhitelist(output)) { 236 | return false; 237 | } 238 | 239 | address pair = IMdexFactory(factory).pairFor(input, output); 240 | 241 | PoolInfo storage pool = poolInfo[pairOfPid[pair]]; 242 | // If it does not exist or the allocPoint is 0 then return 243 | if (pool.pair != pair || pool.allocPoint <= 0) { 244 | return false; 245 | } 246 | 247 | uint256 quantity = getQuantity(output, amount, targetToken); 248 | if (quantity <= 0) { 249 | return false; 250 | } 251 | 252 | mint(pairOfPid[pair]); 253 | 254 | pool.quantity = pool.quantity.add(quantity); 255 | pool.totalQuantity = pool.totalQuantity.add(quantity); 256 | UserInfo storage user = userInfo[pairOfPid[pair]][account]; 257 | user.quantity = user.quantity.add(quantity); 258 | user.blockNumber = block.number; 259 | return true; 260 | } 261 | 262 | // The user withdraws all the transaction rewards of the pool 263 | function takerWithdraw() public { 264 | uint256 userSub; 265 | uint256 length = poolInfo.length; 266 | for (uint256 pid = 0; pid < length; ++pid) { 267 | PoolInfo storage pool = poolInfo[pid]; 268 | UserInfo storage user = userInfo[pid][msg.sender]; 269 | if (user.quantity > 0) { 270 | mint(pid); 271 | // The reward held by the user in this pool 272 | uint256 userReward = pool.allocMdxAmount.mul(user.quantity).div(pool.quantity); 273 | pool.quantity = pool.quantity.sub(user.quantity); 274 | pool.allocMdxAmount = pool.allocMdxAmount.sub(userReward); 275 | user.quantity = 0; 276 | user.blockNumber = block.number; 277 | userSub = userSub.add(userReward); 278 | } 279 | } 280 | if (userSub <= 0) { 281 | return; 282 | } 283 | mdx.transfer(msg.sender, userSub); 284 | } 285 | 286 | // Get rewards from users in the current pool 287 | function getUserReward(uint256 _pid) public view returns (uint256, uint256){ 288 | require(_pid <= poolInfo.length - 1, "SwapMining: Not find this pool"); 289 | uint256 userSub; 290 | PoolInfo memory pool = poolInfo[_pid]; 291 | UserInfo memory user = userInfo[_pid][msg.sender]; 292 | if (user.quantity > 0) { 293 | uint256 blockReward = getMdxReward(pool.lastRewardBlock); 294 | uint256 mdxReward = blockReward.mul(pool.allocPoint).div(totalAllocPoint); 295 | userSub = userSub.add((pool.allocMdxAmount.add(mdxReward)).mul(user.quantity).div(pool.quantity)); 296 | } 297 | //Mdx available to users, User transaction amount 298 | return (userSub, user.quantity); 299 | } 300 | 301 | // Get details of the pool 302 | function getPoolInfo(uint256 _pid) public view returns (address, address, uint256, uint256, uint256, uint256){ 303 | require(_pid <= poolInfo.length - 1, "SwapMining: Not find this pool"); 304 | PoolInfo memory pool = poolInfo[_pid]; 305 | address token0 = IMdexPair(pool.pair).token0(); 306 | address token1 = IMdexPair(pool.pair).token1(); 307 | uint256 mdxAmount = pool.allocMdxAmount; 308 | uint256 blockReward = getMdxReward(pool.lastRewardBlock); 309 | uint256 mdxReward = blockReward.mul(pool.allocPoint).div(totalAllocPoint); 310 | mdxAmount = mdxAmount.add(mdxReward); 311 | //token0,token1,Pool remaining reward,Total /Current transaction volume of the pool 312 | return (token0, token1, mdxAmount, pool.totalQuantity, pool.quantity, pool.allocPoint); 313 | } 314 | 315 | modifier onlyRouter() { 316 | require(msg.sender == router, "SwapMining: caller is not the router"); 317 | _; 318 | } 319 | 320 | function getQuantity(address outputToken, uint256 outputAmount, address anchorToken) public view returns (uint256) { 321 | uint256 quantity = 0; 322 | if (outputToken == anchorToken) { 323 | quantity = outputAmount; 324 | } else if (IMdexFactory(factory).getPair(outputToken, anchorToken) != address(0)) { 325 | quantity = IOracle(oracle).consult(outputToken, outputAmount, anchorToken); 326 | } else { 327 | uint256 length = getWhitelistLength(); 328 | for (uint256 index = 0; index < length; index++) { 329 | address intermediate = getWhitelist(index); 330 | if (IMdexFactory(factory).getPair(outputToken, intermediate) != address(0) && IMdexFactory(factory).getPair(intermediate, anchorToken) != address(0)) { 331 | uint256 interQuantity = IOracle(oracle).consult(outputToken, outputAmount, intermediate); 332 | quantity = IOracle(oracle).consult(intermediate, interQuantity, anchorToken); 333 | break; 334 | } 335 | } 336 | } 337 | return quantity; 338 | } 339 | 340 | } 341 | -------------------------------------------------------------------------------- /contracts/governance/GovernorAlpha.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.6.12; 2 | pragma experimental ABIEncoderV2; 3 | 4 | import "../heco/MdxTokenHeco.sol"; 5 | 6 | contract GovernorAlpha { 7 | /// @notice The name of this contract 8 | string public constant name = "MDX Governor Alpha"; 9 | 10 | /// @notice The number of votes in support of a proposal required in order for a quorum to be reached and for a vote to succeed 11 | function quorumVotes() public view returns (uint) {return mdx.totalSupply() / 25;} // 400,000 = 4% of MDX 12 | 13 | /// @notice The number of votes required in order for a voter to become a proposer 14 | function proposalThreshold() public view returns (uint) {return mdx.totalSupply() / 100;} // 100,000 = 1% of MDX 15 | 16 | /// @notice The maximum number of actions that can be included in a proposal 17 | function proposalMaxOperations() public pure returns (uint) {return 10;} // 10 actions 18 | 19 | /// @notice The delay before voting on a proposal may take place, once proposed 20 | function votingDelay() public pure returns (uint) {return 1;} // 1 block 21 | 22 | /// @notice The duration of voting on a proposal, in blocks 23 | function votingPeriod() public pure returns (uint) {return 86400;} // ~3 days in blocks (assuming 3s blocks) 24 | 25 | /// @notice The address of the MDX Protocol Timelock 26 | TimelockInterface public timelock; 27 | 28 | /// @notice The address of the MDX governance token 29 | MdxToken public mdx; 30 | 31 | /// @notice The address of the Governor Guardian 32 | address public guardian; 33 | 34 | /// @notice The total number of proposals 35 | uint public proposalCount; 36 | 37 | struct Proposal { 38 | /// @notice Unique id for looking up a proposal 39 | uint id; 40 | 41 | /// @notice Creator of the proposal 42 | address proposer; 43 | 44 | /// @notice The timestamp that the proposal will be available for execution, set once the vote succeeds 45 | uint eta; 46 | 47 | /// @notice the ordered list of target addresses for calls to be made 48 | address[] targets; 49 | 50 | /// @notice The ordered list of values (i.e. msg.value) to be passed to the calls to be made 51 | uint[] values; 52 | 53 | /// @notice The ordered list of function signatures to be called 54 | string[] signatures; 55 | 56 | /// @notice The ordered list of calldata to be passed to each call 57 | bytes[] calldatas; 58 | 59 | /// @notice The block at which voting begins: holders must delegate their votes prior to this block 60 | uint startBlock; 61 | 62 | /// @notice The block at which voting ends: votes must be cast prior to this block 63 | uint endBlock; 64 | 65 | /// @notice Current number of votes in favor of this proposal 66 | uint forVotes; 67 | 68 | /// @notice Current number of votes in opposition to this proposal 69 | uint againstVotes; 70 | 71 | /// @notice Flag marking whether the proposal has been canceled 72 | bool canceled; 73 | 74 | /// @notice Flag marking whether the proposal has been executed 75 | bool executed; 76 | 77 | /// @notice Receipts of ballots for the entire set of voters 78 | mapping(address => Receipt) receipts; 79 | } 80 | 81 | /// @notice Ballot receipt record for a voter 82 | struct Receipt { 83 | /// @notice Whether or not a vote has been cast 84 | bool hasVoted; 85 | 86 | /// @notice Whether or not the voter supports the proposal 87 | bool support; 88 | 89 | /// @notice The number of votes the voter had, which were cast 90 | uint256 votes; 91 | } 92 | 93 | /// @notice Possible states that a proposal may be in 94 | enum ProposalState { 95 | Pending, 96 | Active, 97 | Canceled, 98 | Defeated, 99 | Succeeded, 100 | Queued, 101 | Expired, 102 | Executed 103 | } 104 | 105 | /// @notice The official record of all proposals ever proposed 106 | mapping(uint => Proposal) public proposals; 107 | 108 | /// @notice The latest proposal for each proposer 109 | mapping(address => uint) public latestProposalIds; 110 | 111 | /// @notice The EIP-712 typehash for the contract's domain 112 | bytes32 public constant DOMAIN_TYPEHASH = keccak256("EIP712Domain(string name,uint256 chainId,address verifyingContract)"); 113 | 114 | /// @notice The EIP-712 typehash for the ballot struct used by the contract 115 | bytes32 public constant BALLOT_TYPEHASH = keccak256("Ballot(uint256 proposalId,bool support)"); 116 | 117 | /// @notice An event emitted when a new proposal is created 118 | event ProposalCreated(uint id, address proposer, address[] targets, uint[] values, string[] signatures, bytes[] calldatas, uint startBlock, uint endBlock, string description); 119 | 120 | /// @notice An event emitted when a vote has been cast on a proposal 121 | event VoteCast(address voter, uint proposalId, bool support, uint votes); 122 | 123 | /// @notice An event emitted when a proposal has been canceled 124 | event ProposalCanceled(uint id); 125 | 126 | /// @notice An event emitted when a proposal has been queued in the Timelock 127 | event ProposalQueued(uint id, uint eta); 128 | 129 | /// @notice An event emitted when a proposal has been executed in the Timelock 130 | event ProposalExecuted(uint id); 131 | 132 | constructor(address timelock_, address mdx_, address guardian_) public { 133 | timelock = TimelockInterface(timelock_); 134 | mdx = MdxToken(mdx_); 135 | guardian = guardian_; 136 | } 137 | 138 | function propose(address[] memory targets, uint[] memory values, string[] memory signatures, bytes[] memory calldatas, string memory description) public returns (uint) { 139 | require(mdx.getPriorVotes(msg.sender, sub256(block.number, 1)) > proposalThreshold(), "GovernorAlpha::propose: proposer votes below proposal threshold"); 140 | require(targets.length == values.length && targets.length == signatures.length && targets.length == calldatas.length, "GovernorAlpha::propose: proposal function information arity mismatch"); 141 | require(targets.length != 0, "GovernorAlpha::propose: must provide actions"); 142 | require(targets.length <= proposalMaxOperations(), "GovernorAlpha::propose: too many actions"); 143 | 144 | uint latestProposalId = latestProposalIds[msg.sender]; 145 | if (latestProposalId != 0) { 146 | ProposalState proposersLatestProposalState = state(latestProposalId); 147 | require(proposersLatestProposalState != ProposalState.Active, "GovernorAlpha::propose: one live proposal per proposer, found an already active proposal"); 148 | require(proposersLatestProposalState != ProposalState.Pending, "GovernorAlpha::propose: one live proposal per proposer, found an already pending proposal"); 149 | } 150 | 151 | uint startBlock = add256(block.number, votingDelay()); 152 | uint endBlock = add256(startBlock, votingPeriod()); 153 | 154 | proposalCount++; 155 | Proposal memory newProposal = Proposal({ 156 | id : proposalCount, 157 | proposer : msg.sender, 158 | eta : 0, 159 | targets : targets, 160 | values : values, 161 | signatures : signatures, 162 | calldatas : calldatas, 163 | startBlock : startBlock, 164 | endBlock : endBlock, 165 | forVotes : 0, 166 | againstVotes : 0, 167 | canceled : false, 168 | executed : false 169 | }); 170 | 171 | proposals[newProposal.id] = newProposal; 172 | latestProposalIds[newProposal.proposer] = newProposal.id; 173 | 174 | emit ProposalCreated(newProposal.id, msg.sender, targets, values, signatures, calldatas, startBlock, endBlock, description); 175 | return newProposal.id; 176 | } 177 | 178 | function queue(uint proposalId) public { 179 | require(state(proposalId) == ProposalState.Succeeded, "GovernorAlpha::queue: proposal can only be queued if it is succeeded"); 180 | Proposal storage proposal = proposals[proposalId]; 181 | uint eta = add256(block.timestamp, timelock.delay()); 182 | for (uint i = 0; i < proposal.targets.length; i++) { 183 | _queueOrRevert(proposal.targets[i], proposal.values[i], proposal.signatures[i], proposal.calldatas[i], eta); 184 | } 185 | proposal.eta = eta; 186 | emit ProposalQueued(proposalId, eta); 187 | } 188 | 189 | function _queueOrRevert(address target, uint value, string memory signature, bytes memory data, uint eta) internal { 190 | require(!timelock.queuedTransactions(keccak256(abi.encode(target, value, signature, data, eta))), "GovernorAlpha::_queueOrRevert: proposal action already queued at eta"); 191 | timelock.queueTransaction(target, value, signature, data, eta); 192 | } 193 | 194 | function execute(uint proposalId) public payable { 195 | require(state(proposalId) == ProposalState.Queued, "GovernorAlpha::execute: proposal can only be executed if it is queued"); 196 | Proposal storage proposal = proposals[proposalId]; 197 | proposal.executed = true; 198 | for (uint i = 0; i < proposal.targets.length; i++) { 199 | timelock.executeTransaction{value : proposal.values[i]}(proposal.targets[i], proposal.values[i], proposal.signatures[i], proposal.calldatas[i], proposal.eta); 200 | } 201 | emit ProposalExecuted(proposalId); 202 | } 203 | 204 | function cancel(uint proposalId) public { 205 | ProposalState state = state(proposalId); 206 | require(state != ProposalState.Executed, "GovernorAlpha::cancel: cannot cancel executed proposal"); 207 | 208 | Proposal storage proposal = proposals[proposalId]; 209 | require(msg.sender == guardian || mdx.getPriorVotes(proposal.proposer, sub256(block.number, 1)) < proposalThreshold(), "GovernorAlpha::cancel: proposer above threshold"); 210 | 211 | proposal.canceled = true; 212 | for (uint i = 0; i < proposal.targets.length; i++) { 213 | timelock.cancelTransaction(proposal.targets[i], proposal.values[i], proposal.signatures[i], proposal.calldatas[i], proposal.eta); 214 | } 215 | 216 | emit ProposalCanceled(proposalId); 217 | } 218 | 219 | function getActions(uint proposalId) public view returns (address[] memory targets, uint[] memory values, string[] memory signatures, bytes[] memory calldatas) { 220 | Proposal storage p = proposals[proposalId]; 221 | return (p.targets, p.values, p.signatures, p.calldatas); 222 | } 223 | 224 | function getReceipt(uint proposalId, address voter) public view returns (Receipt memory) { 225 | return proposals[proposalId].receipts[voter]; 226 | } 227 | 228 | function state(uint proposalId) public view returns (ProposalState) { 229 | require(proposalCount >= proposalId && proposalId > 0, "GovernorAlpha::state: invalid proposal id"); 230 | Proposal storage proposal = proposals[proposalId]; 231 | if (proposal.canceled) { 232 | return ProposalState.Canceled; 233 | } else if (block.number <= proposal.startBlock) { 234 | return ProposalState.Pending; 235 | } else if (block.number <= proposal.endBlock) { 236 | return ProposalState.Active; 237 | } else if (proposal.forVotes <= proposal.againstVotes || proposal.forVotes < quorumVotes()) { 238 | return ProposalState.Defeated; 239 | } else if (proposal.eta == 0) { 240 | return ProposalState.Succeeded; 241 | } else if (proposal.executed) { 242 | return ProposalState.Executed; 243 | } else if (block.timestamp >= add256(proposal.eta, timelock.GRACE_PERIOD())) { 244 | return ProposalState.Expired; 245 | } else { 246 | return ProposalState.Queued; 247 | } 248 | } 249 | 250 | function castVote(uint proposalId, bool support) public { 251 | return _castVote(msg.sender, proposalId, support); 252 | } 253 | 254 | function castVoteBySig(uint proposalId, bool support, uint8 v, bytes32 r, bytes32 s) public { 255 | bytes32 domainSeparator = keccak256(abi.encode(DOMAIN_TYPEHASH, keccak256(bytes(name)), getChainId(), address(this))); 256 | bytes32 structHash = keccak256(abi.encode(BALLOT_TYPEHASH, proposalId, support)); 257 | bytes32 digest = keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash)); 258 | address signatory = ecrecover(digest, v, r, s); 259 | require(signatory != address(0), "GovernorAlpha::castVoteBySig: invalid signature"); 260 | return _castVote(signatory, proposalId, support); 261 | } 262 | 263 | function _castVote(address voter, uint proposalId, bool support) internal { 264 | require(state(proposalId) == ProposalState.Active, "GovernorAlpha::_castVote: voting is closed"); 265 | Proposal storage proposal = proposals[proposalId]; 266 | Receipt storage receipt = proposal.receipts[voter]; 267 | require(receipt.hasVoted == false, "GovernorAlpha::_castVote: voter already voted"); 268 | uint256 votes = mdx.getPriorVotes(voter, proposal.startBlock); 269 | 270 | if (support) { 271 | proposal.forVotes = add256(proposal.forVotes, votes); 272 | } else { 273 | proposal.againstVotes = add256(proposal.againstVotes, votes); 274 | } 275 | 276 | receipt.hasVoted = true; 277 | receipt.support = support; 278 | receipt.votes = votes; 279 | 280 | emit VoteCast(voter, proposalId, support, votes); 281 | } 282 | 283 | function __acceptAdmin() public { 284 | require(msg.sender == guardian, "GovernorAlpha::__acceptAdmin: sender must be gov guardian"); 285 | timelock.acceptAdmin(); 286 | } 287 | 288 | function __abdicate() public { 289 | require(msg.sender == guardian, "GovernorAlpha::__abdicate: sender must be gov guardian"); 290 | guardian = address(0); 291 | } 292 | 293 | function __queueSetTimelockPendingAdmin(address newPendingAdmin, uint eta) public { 294 | require(msg.sender == guardian, "GovernorAlpha::__queueSetTimelockPendingAdmin: sender must be gov guardian"); 295 | timelock.queueTransaction(address(timelock), 0, "setPendingAdmin(address)", abi.encode(newPendingAdmin), eta); 296 | } 297 | 298 | function __executeSetTimelockPendingAdmin(address newPendingAdmin, uint eta) public { 299 | require(msg.sender == guardian, "GovernorAlpha::__executeSetTimelockPendingAdmin: sender must be gov guardian"); 300 | timelock.executeTransaction(address(timelock), 0, "setPendingAdmin(address)", abi.encode(newPendingAdmin), eta); 301 | } 302 | 303 | function add256(uint256 a, uint256 b) internal pure returns (uint) { 304 | uint c = a + b; 305 | require(c >= a, "addition overflow"); 306 | return c; 307 | } 308 | 309 | function sub256(uint256 a, uint256 b) internal pure returns (uint) { 310 | require(b <= a, "subtraction underflow"); 311 | return a - b; 312 | } 313 | 314 | function getChainId() internal pure returns (uint) { 315 | uint chainId; 316 | assembly {chainId := chainid()} 317 | return chainId; 318 | } 319 | } 320 | 321 | interface TimelockInterface { 322 | function delay() external view returns (uint); 323 | 324 | function GRACE_PERIOD() external view returns (uint); 325 | 326 | function acceptAdmin() external; 327 | 328 | function queuedTransactions(bytes32 hash) external view returns (bool); 329 | 330 | function queueTransaction(address target, uint value, string calldata signature, bytes calldata data, uint eta) external returns (bytes32); 331 | 332 | function cancelTransaction(address target, uint value, string calldata signature, bytes calldata data, uint eta) external; 333 | 334 | function executeTransaction(address target, uint value, string calldata signature, bytes calldata data, uint eta) external payable returns (bytes memory); 335 | } 336 | -------------------------------------------------------------------------------- /contracts/mainnet/CoinChef.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.6.0; 3 | 4 | import "@openzeppelin/contracts/access/Ownable.sol"; 5 | import "@openzeppelin/contracts/utils/EnumerableSet.sol"; 6 | import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; 7 | import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol"; 8 | import "@openzeppelin/contracts/math/SafeMath.sol"; 9 | import "../interface/IMdx.sol"; 10 | 11 | interface IMasterChef { 12 | function pendingSushi(uint256 pid, address user) external view returns (uint256); 13 | 14 | function deposit(uint256 pid, uint256 amount) external; 15 | 16 | function withdraw(uint256 pid, uint256 amount) external; 17 | 18 | function emergencyWithdraw(uint256 pid) external; 19 | } 20 | 21 | contract CoinChef is Ownable { 22 | using SafeMath for uint256; 23 | using SafeERC20 for IERC20; 24 | 25 | using EnumerableSet for EnumerableSet.AddressSet; 26 | EnumerableSet.AddressSet private _sushiLP; 27 | 28 | // Info of each user. 29 | struct UserInfo { 30 | uint256 amount; // How many LP tokens the user has provided. 31 | uint256 rewardDebt; // Reward debt. 32 | uint256 sushiRewardDebt; //sushi Reward debt. 33 | } 34 | 35 | // Info of each pool. 36 | struct PoolInfo { 37 | IERC20 lpToken; // Address of LP token contract. 38 | uint256 allocPoint; // How many allocation points assigned to this pool. MDXs to distribute per block. 39 | uint256 lastRewardBlock; // Last block number that MDXs distribution occurs. 40 | uint256 accMdxPerShare; // Accumulated MDXs per share, times 1e12. 41 | uint256 totalAmount; // Total amount of current pool deposit. 42 | uint256 accSushiPerShare; //Accumulated SuSHIs per share 43 | } 44 | 45 | // The MDX TOKEN! 46 | IMdx public mdx; 47 | // MDX tokens created per block. 48 | uint256 public constant mdxPerBlock = 100 ** 1e18; 49 | // Info of each pool. 50 | PoolInfo[] public poolInfo; 51 | // Info of each user that stakes LP tokens. 52 | mapping(uint256 => mapping(address => UserInfo)) public userInfo; 53 | // Corresponding to the pid of the sushi pool 54 | mapping(uint256 => uint256) public poolCorrespond; 55 | // Total allocation points. Must be the sum of all allocation points in all pools. 56 | uint256 public totalAllocPoint = 0; 57 | // The block number when MDX mining starts. 58 | uint256 public startBlock; 59 | // The block number when MDX mining end; 60 | uint256 public endBlock; 61 | // SUSHI MasterChef 0xc2EdaD668740f1aA35E4D8f227fB8E17dcA888Cd 62 | address public constant sushiChef = 0xc2EdaD668740f1aA35E4D8f227fB8E17dcA888Cd; 63 | // SUSHI Token 0x6B3595068778DD592e39A122f4f5a5cF09C90fE2 64 | address public constant sushiToken = 0x6B3595068778DD592e39A122f4f5a5cF09C90fE2; 65 | 66 | event Deposit(address indexed user, uint256 indexed pid, uint256 amount); 67 | event Withdraw(address indexed user, uint256 indexed pid, uint256 amount); 68 | event EmergencyWithdraw(address indexed user, uint256 indexed pid, uint256 amount); 69 | 70 | constructor( 71 | IMdx _mdx, 72 | uint256 _startBlock 73 | ) public { 74 | mdx = _mdx; 75 | startBlock = _startBlock; 76 | endBlock = _startBlock.add(200000); 77 | } 78 | 79 | function poolLength() public view returns (uint256) { 80 | return poolInfo.length; 81 | } 82 | 83 | function addSushiLP(address _addLP) public onlyOwner returns (bool) { 84 | require(_addLP != address(0), "LP is the zero address"); 85 | IERC20(_addLP).approve(sushiChef, uint256(- 1)); 86 | return EnumerableSet.add(_sushiLP, _addLP); 87 | } 88 | 89 | function isSushiLP(address _LP) public view returns (bool) { 90 | return EnumerableSet.contains(_sushiLP, _LP); 91 | } 92 | 93 | function getSushiLPLength() public view returns (uint256) { 94 | return EnumerableSet.length(_sushiLP); 95 | } 96 | 97 | function getSushiLPAddress(uint256 _pid) public view returns (address){ 98 | require(_pid <= getSushiLPLength() - 1, "not find this SushiLP"); 99 | return EnumerableSet.at(_sushiLP, _pid); 100 | } 101 | 102 | // Add a new lp to the pool. Can only be called by the owner. 103 | // XXX DO NOT add the same LP token more than once. Rewards will be messed up if you do. 104 | function add(uint256 _allocPoint, IERC20 _lpToken, bool _withUpdate) public onlyOwner { 105 | require(address(_lpToken) != address(0), "lpToken is the zero address"); 106 | require(block.number < endBlock, "All token mining completed"); 107 | if (_withUpdate) { 108 | massUpdatePools(); 109 | } 110 | uint256 lastRewardBlock = block.number > startBlock ? block.number : startBlock; 111 | totalAllocPoint = totalAllocPoint.add(_allocPoint); 112 | poolInfo.push(PoolInfo({ 113 | lpToken : _lpToken, 114 | allocPoint : _allocPoint, 115 | lastRewardBlock : lastRewardBlock, 116 | accMdxPerShare : 0, 117 | totalAmount : 0, 118 | accSushiPerShare : 0 119 | })); 120 | } 121 | 122 | // Update the given pool's MDX allocation point. Can only be called by the owner. 123 | function set(uint256 _pid, uint256 _allocPoint, bool _withUpdate) public onlyOwner { 124 | if (_withUpdate) { 125 | massUpdatePools(); 126 | } 127 | totalAllocPoint = totalAllocPoint.sub(poolInfo[_pid].allocPoint).add(_allocPoint); 128 | poolInfo[_pid].allocPoint = _allocPoint; 129 | } 130 | 131 | // The current pool corresponds to the pid of the sushi pool 132 | function setPoolCorr(uint256 _pid, uint256 _sid) public onlyOwner { 133 | require(_pid <= poolLength() - 1, "not find this pool"); 134 | poolCorrespond[_pid] = _sid; 135 | } 136 | 137 | // Update reward variables for all pools. Be careful of gas spending! 138 | function massUpdatePools() public { 139 | uint256 length = poolInfo.length; 140 | for (uint256 pid = 0; pid < length; ++pid) { 141 | updatePool(pid); 142 | } 143 | } 144 | 145 | // Update reward variables of the given pool to be up-to-date. 146 | function updatePool(uint256 _pid) public { 147 | PoolInfo storage pool = poolInfo[_pid]; 148 | uint256 number = block.number > endBlock ? endBlock : block.number; 149 | if (number <= pool.lastRewardBlock) { 150 | return; 151 | } 152 | uint256 lpSupply; 153 | if (isSushiLP(address(pool.lpToken))) { 154 | if (pool.totalAmount == 0) { 155 | pool.lastRewardBlock = number; 156 | return; 157 | } 158 | lpSupply = pool.totalAmount; 159 | } else { 160 | lpSupply = pool.lpToken.balanceOf(address(this)); 161 | if (lpSupply == 0) { 162 | pool.lastRewardBlock = number; 163 | return; 164 | } 165 | } 166 | 167 | uint256 multiplier = number.sub(pool.lastRewardBlock); 168 | uint256 mdxReward = multiplier.mul(mdxPerBlock).mul(pool.allocPoint).div(totalAllocPoint); 169 | bool minRet = mdx.mint(address(this), mdxReward); 170 | if (minRet) { 171 | pool.accMdxPerShare = pool.accMdxPerShare.add(mdxReward.mul(1e12).div(lpSupply)); 172 | } 173 | pool.lastRewardBlock = number; 174 | } 175 | 176 | // View function to see pending MDXs on frontend. 177 | function pending(uint256 _pid, address _user) external view returns (uint256, uint256){ 178 | PoolInfo storage pool = poolInfo[_pid]; 179 | if (isSushiLP(address(pool.lpToken))) { 180 | (uint256 mdxAmount, uint256 sushiAmount) = pendingMdxAndSushi(_pid, _user); 181 | return (mdxAmount, sushiAmount); 182 | } else { 183 | uint256 mdxAmount = pendingMdx(_pid, _user); 184 | return (mdxAmount, 0); 185 | } 186 | } 187 | 188 | function pendingMdxAndSushi(uint256 _pid, address _user) private view returns (uint256, uint256){ 189 | PoolInfo storage pool = poolInfo[_pid]; 190 | UserInfo storage user = userInfo[_pid][_user]; 191 | uint256 accMdxPerShare = pool.accMdxPerShare; 192 | uint256 accSushiPerShare = pool.accSushiPerShare; 193 | uint256 number = block.number > endBlock ? endBlock : block.number; 194 | if (user.amount > 0) { 195 | uint256 sushiPending = IMasterChef(sushiChef).pendingSushi(poolCorrespond[_pid], address(this)); 196 | accSushiPerShare = accSushiPerShare.add(sushiPending.mul(1e12).div(pool.totalAmount)); 197 | uint256 userPending = user.amount.mul(accSushiPerShare).div(1e12).sub(user.sushiRewardDebt); 198 | if (number > pool.lastRewardBlock) { 199 | uint256 multiplier = number.sub(pool.lastRewardBlock); 200 | uint256 mdxReward = multiplier.mul(mdxPerBlock).mul(pool.allocPoint).div(totalAllocPoint); 201 | accMdxPerShare = accMdxPerShare.add(mdxReward.mul(1e12).div(pool.totalAmount)); 202 | return (user.amount.mul(accMdxPerShare).div(1e12).sub(user.rewardDebt), userPending); 203 | } 204 | if (number == pool.lastRewardBlock) { 205 | return (user.amount.mul(accMdxPerShare).div(1e12).sub(user.rewardDebt), userPending); 206 | } 207 | } 208 | return (0, 0); 209 | } 210 | 211 | function pendingMdx(uint256 _pid, address _user) private view returns (uint256){ 212 | PoolInfo storage pool = poolInfo[_pid]; 213 | UserInfo storage user = userInfo[_pid][_user]; 214 | uint256 accMdxPerShare = pool.accMdxPerShare; 215 | uint256 lpSupply = pool.lpToken.balanceOf(address(this)); 216 | uint256 number = block.number > endBlock ? endBlock : block.number; 217 | if (user.amount > 0) { 218 | if (number > pool.lastRewardBlock) { 219 | uint256 multiplier = block.number.sub(pool.lastRewardBlock); 220 | uint256 mdxReward = multiplier.mul(mdxPerBlock).mul(pool.allocPoint).div(totalAllocPoint); 221 | accMdxPerShare = accMdxPerShare.add(mdxReward.mul(1e12).div(lpSupply)); 222 | return user.amount.mul(accMdxPerShare).div(1e12).sub(user.rewardDebt); 223 | } 224 | if (number == pool.lastRewardBlock) { 225 | return user.amount.mul(accMdxPerShare).div(1e12).sub(user.rewardDebt); 226 | } 227 | } 228 | return 0; 229 | } 230 | 231 | // Deposit LP tokens to CoinChef for MDX allocation. 232 | function deposit(uint256 _pid, uint256 _amount) public { 233 | PoolInfo storage pool = poolInfo[_pid]; 234 | if (isSushiLP(address(pool.lpToken))) { 235 | depositMdxAndSushi(_pid, _amount, msg.sender); 236 | } else { 237 | depositMdx(_pid, _amount, msg.sender); 238 | } 239 | } 240 | 241 | function depositMdxAndSushi(uint256 _pid, uint256 _amount, address _user) private { 242 | PoolInfo storage pool = poolInfo[_pid]; 243 | UserInfo storage user = userInfo[_pid][_user]; 244 | updatePool(_pid); 245 | if (user.amount > 0) { 246 | uint256 pendingAmount = user.amount.mul(pool.accMdxPerShare).div(1e12).sub(user.rewardDebt); 247 | if (pendingAmount > 0) { 248 | safeMdxTransfer(_user, pendingAmount); 249 | } 250 | uint256 beforeSushi = IERC20(sushiToken).balanceOf(address(this)); 251 | IMasterChef(sushiChef).deposit(poolCorrespond[_pid], 0); 252 | uint256 afterSushi = IERC20(sushiToken).balanceOf(address(this)); 253 | pool.accSushiPerShare = pool.accSushiPerShare.add(afterSushi.sub(beforeSushi).mul(1e12).div(pool.totalAmount)); 254 | uint256 sushiPending = user.amount.mul(pool.accSushiPerShare).div(1e12).sub(user.sushiRewardDebt); 255 | if (sushiPending > 0) { 256 | IERC20(sushiToken).safeTransfer(_user, sushiPending); 257 | } 258 | } 259 | if (_amount > 0) { 260 | pool.lpToken.safeTransferFrom(_user, address(this), _amount); 261 | if (pool.totalAmount == 0) { 262 | IMasterChef(sushiChef).deposit(poolCorrespond[_pid], _amount); 263 | pool.totalAmount = pool.totalAmount.add(_amount); 264 | user.amount = user.amount.add(_amount); 265 | } else { 266 | uint256 beforeSushi = IERC20(sushiToken).balanceOf(address(this)); 267 | IMasterChef(sushiChef).deposit(poolCorrespond[_pid], _amount); 268 | uint256 afterSushi = IERC20(sushiToken).balanceOf(address(this)); 269 | pool.accSushiPerShare = pool.accSushiPerShare.add(afterSushi.sub(beforeSushi).mul(1e12).div(pool.totalAmount)); 270 | pool.totalAmount = pool.totalAmount.add(_amount); 271 | user.amount = user.amount.add(_amount); 272 | } 273 | } 274 | user.rewardDebt = user.amount.mul(pool.accMdxPerShare).div(1e12); 275 | user.sushiRewardDebt = user.amount.mul(pool.accSushiPerShare).div(1e12); 276 | emit Deposit(_user, _pid, _amount); 277 | } 278 | 279 | function depositMdx(uint256 _pid, uint256 _amount, address _user) private { 280 | PoolInfo storage pool = poolInfo[_pid]; 281 | UserInfo storage user = userInfo[_pid][_user]; 282 | updatePool(_pid); 283 | if (user.amount > 0) { 284 | uint256 pendingAmount = user.amount.mul(pool.accMdxPerShare).div(1e12).sub(user.rewardDebt); 285 | if (pendingAmount > 0) { 286 | safeMdxTransfer(_user, pendingAmount); 287 | } 288 | } 289 | if (_amount > 0) { 290 | pool.lpToken.safeTransferFrom(_user, address(this), _amount); 291 | user.amount = user.amount.add(_amount); 292 | pool.totalAmount = pool.totalAmount.add(_amount); 293 | } 294 | user.rewardDebt = user.amount.mul(pool.accMdxPerShare).div(1e12); 295 | emit Deposit(_user, _pid, _amount); 296 | } 297 | 298 | // Withdraw LP tokens from CoinChef. 299 | function withdraw(uint256 _pid, uint256 _amount) public { 300 | PoolInfo storage pool = poolInfo[_pid]; 301 | if (isSushiLP(address(pool.lpToken))) { 302 | withdrawMdxAndSushi(_pid, _amount, msg.sender); 303 | } else { 304 | withdrawMdx(_pid, _amount, msg.sender); 305 | } 306 | } 307 | 308 | function withdrawMdxAndSushi(uint256 _pid, uint256 _amount, address _user) private { 309 | PoolInfo storage pool = poolInfo[_pid]; 310 | UserInfo storage user = userInfo[_pid][_user]; 311 | require(user.amount >= _amount, "withdrawMdxAndSushi: not good"); 312 | updatePool(_pid); 313 | uint256 pendingAmount = user.amount.mul(pool.accMdxPerShare).div(1e12).sub(user.rewardDebt); 314 | if (pendingAmount > 0) { 315 | safeMdxTransfer(_user, pendingAmount); 316 | } 317 | if (_amount > 0) { 318 | uint256 beforeSushi = IERC20(sushiToken).balanceOf(address(this)); 319 | IMasterChef(sushiChef).withdraw(poolCorrespond[_pid], _amount); 320 | uint256 afterSushi = IERC20(sushiToken).balanceOf(address(this)); 321 | pool.accSushiPerShare = pool.accSushiPerShare.add(afterSushi.sub(beforeSushi).mul(1e12).div(pool.totalAmount)); 322 | uint256 sushiPending = user.amount.mul(pool.accSushiPerShare).div(1e12).sub(user.sushiRewardDebt); 323 | if (sushiPending > 0) { 324 | IERC20(sushiToken).safeTransfer(_user, sushiPending); 325 | } 326 | user.amount = user.amount.sub(_amount); 327 | pool.totalAmount = pool.totalAmount.sub(_amount); 328 | pool.lpToken.safeTransfer(_user, _amount); 329 | } 330 | user.rewardDebt = user.amount.mul(pool.accMdxPerShare).div(1e12); 331 | user.sushiRewardDebt = user.amount.mul(pool.accSushiPerShare).div(1e12); 332 | emit Withdraw(_user, _pid, _amount); 333 | } 334 | 335 | function withdrawMdx(uint256 _pid, uint256 _amount, address _user) private { 336 | PoolInfo storage pool = poolInfo[_pid]; 337 | UserInfo storage user = userInfo[_pid][_user]; 338 | require(user.amount >= _amount, "withdrawMdx: not good"); 339 | updatePool(_pid); 340 | uint256 pendingAmount = user.amount.mul(pool.accMdxPerShare).div(1e12).sub(user.rewardDebt); 341 | if (pendingAmount > 0) { 342 | safeMdxTransfer(_user, pendingAmount); 343 | } 344 | if (_amount > 0) { 345 | user.amount = user.amount.sub(_amount); 346 | pool.totalAmount = pool.totalAmount.sub(_amount); 347 | pool.lpToken.safeTransfer(_user, _amount); 348 | } 349 | user.rewardDebt = user.amount.mul(pool.accMdxPerShare).div(1e12); 350 | emit Withdraw(_user, _pid, _amount); 351 | } 352 | 353 | // Withdraw without caring about rewards. EMERGENCY ONLY. 354 | function emergencyWithdraw(uint256 _pid) public { 355 | PoolInfo storage pool = poolInfo[_pid]; 356 | if (isSushiLP(address(pool.lpToken))) { 357 | emergencyWithdrawMdxAndSushi(_pid, msg.sender); 358 | } else { 359 | emergencyWithdrawMdx(_pid, msg.sender); 360 | } 361 | } 362 | 363 | function emergencyWithdrawMdxAndSushi(uint256 _pid, address _user) private { 364 | PoolInfo storage pool = poolInfo[_pid]; 365 | UserInfo storage user = userInfo[_pid][_user]; 366 | uint256 amount = user.amount; 367 | uint256 beforeSushi = IERC20(sushiToken).balanceOf(address(this)); 368 | IMasterChef(sushiChef).withdraw(poolCorrespond[_pid], amount); 369 | uint256 afterSushi = IERC20(sushiToken).balanceOf(address(this)); 370 | pool.accSushiPerShare = pool.accSushiPerShare.add(afterSushi.sub(beforeSushi).mul(1e12).div(pool.totalAmount)); 371 | user.amount = 0; 372 | user.rewardDebt = 0; 373 | pool.lpToken.safeTransfer(_user, amount); 374 | pool.totalAmount = pool.totalAmount.sub(amount); 375 | emit EmergencyWithdraw(_user, _pid, amount); 376 | } 377 | 378 | function emergencyWithdrawMdx(uint256 _pid, address _user) private { 379 | PoolInfo storage pool = poolInfo[_pid]; 380 | UserInfo storage user = userInfo[_pid][_user]; 381 | uint256 amount = user.amount; 382 | user.amount = 0; 383 | user.rewardDebt = 0; 384 | pool.lpToken.safeTransfer(_user, amount); 385 | pool.totalAmount = pool.totalAmount.sub(amount); 386 | emit EmergencyWithdraw(_user, _pid, amount); 387 | } 388 | 389 | // Safe MDX transfer function, just in case if rounding error causes pool to not have enough MDXs. 390 | function safeMdxTransfer(address _to, uint256 _amount) internal { 391 | uint256 mdxBal = mdx.balanceOf(address(this)); 392 | if (_amount > mdxBal) { 393 | mdx.transfer(_to, mdxBal); 394 | } else { 395 | mdx.transfer(_to, _amount); 396 | } 397 | } 398 | 399 | } 400 | -------------------------------------------------------------------------------- /contracts/heco/HecoPool.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.6.0; 3 | 4 | import "@openzeppelin/contracts/access/Ownable.sol"; 5 | import "@openzeppelin/contracts/utils/EnumerableSet.sol"; 6 | import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; 7 | import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol"; 8 | import "@openzeppelin/contracts/math/SafeMath.sol"; 9 | import "../interface/IMdx.sol"; 10 | 11 | interface IMasterChefHeco { 12 | function pending(uint256 pid, address user) external view returns (uint256); 13 | 14 | function deposit(uint256 pid, uint256 amount) external; 15 | 16 | function withdraw(uint256 pid, uint256 amount) external; 17 | 18 | function emergencyWithdraw(uint256 pid) external; 19 | } 20 | 21 | contract HecoPool is Ownable { 22 | using SafeMath for uint256; 23 | using SafeERC20 for IERC20; 24 | 25 | using EnumerableSet for EnumerableSet.AddressSet; 26 | EnumerableSet.AddressSet private _multLP; 27 | 28 | // Info of each user. 29 | struct UserInfo { 30 | uint256 amount; // How many LP tokens the user has provided. 31 | uint256 rewardDebt; // Reward debt. 32 | uint256 multLpRewardDebt; //multLp Reward debt. 33 | } 34 | 35 | // Info of each pool. 36 | struct PoolInfo { 37 | IERC20 lpToken; // Address of LP token contract. 38 | uint256 allocPoint; // How many allocation points assigned to this pool. MDXs to distribute per block. 39 | uint256 lastRewardBlock; // Last block number that MDXs distribution occurs. 40 | uint256 accMdxPerShare; // Accumulated MDXs per share, times 1e12. 41 | uint256 accMultLpPerShare; //Accumulated multLp per share 42 | uint256 totalAmount; // Total amount of current pool deposit. 43 | } 44 | 45 | // The MDX Token! 46 | IMdx public mdx; 47 | // MDX tokens created per block. 48 | uint256 public mdxPerBlock; 49 | // Info of each pool. 50 | PoolInfo[] public poolInfo; 51 | // Info of each user that stakes LP tokens. 52 | mapping(uint256 => mapping(address => UserInfo)) public userInfo; 53 | // Corresponding to the pid of the multLP pool 54 | mapping(uint256 => uint256) public poolCorrespond; 55 | // pid corresponding address 56 | mapping(address => uint256) public LpOfPid; 57 | // Control mining 58 | bool public paused = false; 59 | // Total allocation points. Must be the sum of all allocation points in all pools. 60 | uint256 public totalAllocPoint = 0; 61 | // The block number when MDX mining starts. 62 | uint256 public startBlock; 63 | // multLP MasterChef 64 | address public multLpChef; 65 | // multLP Token 66 | address public multLpToken; 67 | // How many blocks are halved 68 | uint256 public halvingPeriod = 5256000; 69 | 70 | event Deposit(address indexed user, uint256 indexed pid, uint256 amount); 71 | event Withdraw(address indexed user, uint256 indexed pid, uint256 amount); 72 | event EmergencyWithdraw(address indexed user, uint256 indexed pid, uint256 amount); 73 | 74 | constructor( 75 | IMdx _mdx, 76 | uint256 _mdxPerBlock, 77 | uint256 _startBlock 78 | ) public { 79 | mdx = _mdx; 80 | mdxPerBlock = _mdxPerBlock; 81 | startBlock = _startBlock; 82 | } 83 | 84 | function setHalvingPeriod(uint256 _block) public onlyOwner { 85 | halvingPeriod = _block; 86 | } 87 | 88 | // Set the number of mdx produced by each block 89 | function setMdxPerBlock(uint256 _newPerBlock) public onlyOwner { 90 | massUpdatePools(); 91 | mdxPerBlock = _newPerBlock; 92 | } 93 | 94 | function poolLength() public view returns (uint256) { 95 | return poolInfo.length; 96 | } 97 | 98 | function addMultLP(address _addLP) public onlyOwner returns (bool) { 99 | require(_addLP != address(0), "LP is the zero address"); 100 | IERC20(_addLP).approve(multLpChef, uint256(- 1)); 101 | return EnumerableSet.add(_multLP, _addLP); 102 | } 103 | 104 | function isMultLP(address _LP) public view returns (bool) { 105 | return EnumerableSet.contains(_multLP, _LP); 106 | } 107 | 108 | function getMultLPLength() public view returns (uint256) { 109 | return EnumerableSet.length(_multLP); 110 | } 111 | 112 | function getMultLPAddress(uint256 _pid) public view returns (address){ 113 | require(_pid <= getMultLPLength() - 1, "not find this multLP"); 114 | return EnumerableSet.at(_multLP, _pid); 115 | } 116 | 117 | function setPause() public onlyOwner { 118 | paused = !paused; 119 | } 120 | 121 | function setMultLP(address _multLpToken, address _multLpChef) public onlyOwner { 122 | require(_multLpToken != address(0) && _multLpChef != address(0), "is the zero address"); 123 | multLpToken = _multLpToken; 124 | multLpChef = _multLpChef; 125 | } 126 | 127 | function replaceMultLP(address _multLpToken, address _multLpChef) public onlyOwner { 128 | require(_multLpToken != address(0) && _multLpChef != address(0), "is the zero address"); 129 | require(paused == true, "No mining suspension"); 130 | multLpToken = _multLpToken; 131 | multLpChef = _multLpChef; 132 | uint256 length = getMultLPLength(); 133 | while (length > 0) { 134 | address dAddress = EnumerableSet.at(_multLP, 0); 135 | uint256 pid = LpOfPid[dAddress]; 136 | IMasterChefHeco(multLpChef).emergencyWithdraw(poolCorrespond[pid]); 137 | EnumerableSet.remove(_multLP, dAddress); 138 | length--; 139 | } 140 | } 141 | 142 | // Add a new lp to the pool. Can only be called by the owner. 143 | // XXX DO NOT add the same LP token more than once. Rewards will be messed up if you do. 144 | function add(uint256 _allocPoint, IERC20 _lpToken, bool _withUpdate) public onlyOwner { 145 | require(address(_lpToken) != address(0), "_lpToken is the zero address"); 146 | if (_withUpdate) { 147 | massUpdatePools(); 148 | } 149 | uint256 lastRewardBlock = block.number > startBlock ? block.number : startBlock; 150 | totalAllocPoint = totalAllocPoint.add(_allocPoint); 151 | poolInfo.push(PoolInfo({ 152 | lpToken : _lpToken, 153 | allocPoint : _allocPoint, 154 | lastRewardBlock : lastRewardBlock, 155 | accMdxPerShare : 0, 156 | accMultLpPerShare : 0, 157 | totalAmount : 0 158 | })); 159 | LpOfPid[address(_lpToken)] = poolLength() - 1; 160 | } 161 | 162 | // Update the given pool's MDX allocation point. Can only be called by the owner. 163 | function set(uint256 _pid, uint256 _allocPoint, bool _withUpdate) public onlyOwner { 164 | if (_withUpdate) { 165 | massUpdatePools(); 166 | } 167 | totalAllocPoint = totalAllocPoint.sub(poolInfo[_pid].allocPoint).add(_allocPoint); 168 | poolInfo[_pid].allocPoint = _allocPoint; 169 | } 170 | 171 | // The current pool corresponds to the pid of the multLP pool 172 | function setPoolCorr(uint256 _pid, uint256 _sid) public onlyOwner { 173 | require(_pid <= poolLength() - 1, "not find this pool"); 174 | poolCorrespond[_pid] = _sid; 175 | } 176 | 177 | function phase(uint256 blockNumber) public view returns (uint256) { 178 | if (halvingPeriod == 0) { 179 | return 0; 180 | } 181 | if (blockNumber > startBlock) { 182 | return (blockNumber.sub(startBlock).sub(1)).div(halvingPeriod); 183 | } 184 | return 0; 185 | } 186 | 187 | function reward(uint256 blockNumber) public view returns (uint256) { 188 | uint256 _phase = phase(blockNumber); 189 | return mdxPerBlock.div(2 ** _phase); 190 | } 191 | 192 | function getMdxBlockReward(uint256 _lastRewardBlock) public view returns (uint256) { 193 | uint256 blockReward = 0; 194 | uint256 n = phase(_lastRewardBlock); 195 | uint256 m = phase(block.number); 196 | while (n < m) { 197 | n++; 198 | uint256 r = n.mul(halvingPeriod).add(startBlock); 199 | blockReward = blockReward.add((r.sub(_lastRewardBlock)).mul(reward(r))); 200 | _lastRewardBlock = r; 201 | } 202 | blockReward = blockReward.add((block.number.sub(_lastRewardBlock)).mul(reward(block.number))); 203 | return blockReward; 204 | } 205 | 206 | // Update reward variables for all pools. Be careful of gas spending! 207 | function massUpdatePools() public { 208 | uint256 length = poolInfo.length; 209 | for (uint256 pid = 0; pid < length; ++pid) { 210 | updatePool(pid); 211 | } 212 | } 213 | 214 | // Update reward variables of the given pool to be up-to-date. 215 | function updatePool(uint256 _pid) public { 216 | PoolInfo storage pool = poolInfo[_pid]; 217 | if (block.number <= pool.lastRewardBlock) { 218 | return; 219 | } 220 | uint256 lpSupply; 221 | if (isMultLP(address(pool.lpToken))) { 222 | if (pool.totalAmount == 0) { 223 | pool.lastRewardBlock = block.number; 224 | return; 225 | } 226 | lpSupply = pool.totalAmount; 227 | } else { 228 | lpSupply = pool.lpToken.balanceOf(address(this)); 229 | if (lpSupply == 0) { 230 | pool.lastRewardBlock = block.number; 231 | return; 232 | } 233 | } 234 | uint256 blockReward = getMdxBlockReward(pool.lastRewardBlock); 235 | if (blockReward <= 0) { 236 | return; 237 | } 238 | uint256 mdxReward = blockReward.mul(pool.allocPoint).div(totalAllocPoint); 239 | bool minRet = mdx.mint(address(this), mdxReward); 240 | if (minRet) { 241 | pool.accMdxPerShare = pool.accMdxPerShare.add(mdxReward.mul(1e12).div(lpSupply)); 242 | } 243 | pool.lastRewardBlock = block.number; 244 | } 245 | 246 | // View function to see pending MDXs on frontend. 247 | function pending(uint256 _pid, address _user) external view returns (uint256, uint256){ 248 | PoolInfo storage pool = poolInfo[_pid]; 249 | if (isMultLP(address(pool.lpToken))) { 250 | (uint256 mdxAmount, uint256 tokenAmount) = pendingMdxAndToken(_pid, _user); 251 | return (mdxAmount, tokenAmount); 252 | } else { 253 | uint256 mdxAmount = pendingMdx(_pid, _user); 254 | return (mdxAmount, 0); 255 | } 256 | } 257 | 258 | function pendingMdxAndToken(uint256 _pid, address _user) private view returns (uint256, uint256){ 259 | PoolInfo storage pool = poolInfo[_pid]; 260 | UserInfo storage user = userInfo[_pid][_user]; 261 | uint256 accMdxPerShare = pool.accMdxPerShare; 262 | uint256 accMultLpPerShare = pool.accMultLpPerShare; 263 | if (user.amount > 0) { 264 | uint256 TokenPending = IMasterChefHeco(multLpChef).pending(poolCorrespond[_pid], address(this)); 265 | accMultLpPerShare = accMultLpPerShare.add(TokenPending.mul(1e12).div(pool.totalAmount)); 266 | uint256 userPending = user.amount.mul(accMultLpPerShare).div(1e12).sub(user.multLpRewardDebt); 267 | if (block.number > pool.lastRewardBlock) { 268 | uint256 blockReward = getMdxBlockReward(pool.lastRewardBlock); 269 | uint256 mdxReward = blockReward.mul(pool.allocPoint).div(totalAllocPoint); 270 | accMdxPerShare = accMdxPerShare.add(mdxReward.mul(1e12).div(pool.totalAmount)); 271 | return (user.amount.mul(accMdxPerShare).div(1e12).sub(user.rewardDebt), userPending); 272 | } 273 | if (block.number == pool.lastRewardBlock) { 274 | return (user.amount.mul(accMdxPerShare).div(1e12).sub(user.rewardDebt), userPending); 275 | } 276 | } 277 | return (0, 0); 278 | } 279 | 280 | function pendingMdx(uint256 _pid, address _user) private view returns (uint256){ 281 | PoolInfo storage pool = poolInfo[_pid]; 282 | UserInfo storage user = userInfo[_pid][_user]; 283 | uint256 accMdxPerShare = pool.accMdxPerShare; 284 | uint256 lpSupply = pool.lpToken.balanceOf(address(this)); 285 | if (user.amount > 0) { 286 | if (block.number > pool.lastRewardBlock) { 287 | uint256 blockReward = getMdxBlockReward(pool.lastRewardBlock); 288 | uint256 mdxReward = blockReward.mul(pool.allocPoint).div(totalAllocPoint); 289 | accMdxPerShare = accMdxPerShare.add(mdxReward.mul(1e12).div(lpSupply)); 290 | return user.amount.mul(accMdxPerShare).div(1e12).sub(user.rewardDebt); 291 | } 292 | if (block.number == pool.lastRewardBlock) { 293 | return user.amount.mul(accMdxPerShare).div(1e12).sub(user.rewardDebt); 294 | } 295 | } 296 | return 0; 297 | } 298 | 299 | // Deposit LP tokens to HecoPool for MDX allocation. 300 | function deposit(uint256 _pid, uint256 _amount) public notPause { 301 | PoolInfo storage pool = poolInfo[_pid]; 302 | if (isMultLP(address(pool.lpToken))) { 303 | depositMdxAndToken(_pid, _amount, msg.sender); 304 | } else { 305 | depositMdx(_pid, _amount, msg.sender); 306 | } 307 | } 308 | 309 | function depositMdxAndToken(uint256 _pid, uint256 _amount, address _user) private { 310 | PoolInfo storage pool = poolInfo[_pid]; 311 | UserInfo storage user = userInfo[_pid][_user]; 312 | updatePool(_pid); 313 | if (user.amount > 0) { 314 | uint256 pendingAmount = user.amount.mul(pool.accMdxPerShare).div(1e12).sub(user.rewardDebt); 315 | if (pendingAmount > 0) { 316 | safeMdxTransfer(_user, pendingAmount); 317 | } 318 | uint256 beforeToken = IERC20(multLpToken).balanceOf(address(this)); 319 | IMasterChefHeco(multLpChef).deposit(poolCorrespond[_pid], 0); 320 | uint256 afterToken = IERC20(multLpToken).balanceOf(address(this)); 321 | pool.accMultLpPerShare = pool.accMultLpPerShare.add(afterToken.sub(beforeToken).mul(1e12).div(pool.totalAmount)); 322 | uint256 tokenPending = user.amount.mul(pool.accMultLpPerShare).div(1e12).sub(user.multLpRewardDebt); 323 | if (tokenPending > 0) { 324 | IERC20(multLpToken).safeTransfer(_user, tokenPending); 325 | } 326 | } 327 | if (_amount > 0) { 328 | pool.lpToken.safeTransferFrom(_user, address(this), _amount); 329 | if (pool.totalAmount == 0) { 330 | IMasterChefHeco(multLpChef).deposit(poolCorrespond[_pid], _amount); 331 | user.amount = user.amount.add(_amount); 332 | pool.totalAmount = pool.totalAmount.add(_amount); 333 | } else { 334 | uint256 beforeToken = IERC20(multLpToken).balanceOf(address(this)); 335 | IMasterChefHeco(multLpChef).deposit(poolCorrespond[_pid], _amount); 336 | uint256 afterToken = IERC20(multLpToken).balanceOf(address(this)); 337 | pool.accMultLpPerShare = pool.accMultLpPerShare.add(afterToken.sub(beforeToken).mul(1e12).div(pool.totalAmount)); 338 | user.amount = user.amount.add(_amount); 339 | pool.totalAmount = pool.totalAmount.add(_amount); 340 | } 341 | } 342 | user.rewardDebt = user.amount.mul(pool.accMdxPerShare).div(1e12); 343 | user.multLpRewardDebt = user.amount.mul(pool.accMultLpPerShare).div(1e12); 344 | emit Deposit(_user, _pid, _amount); 345 | } 346 | 347 | function depositMdx(uint256 _pid, uint256 _amount, address _user) private { 348 | PoolInfo storage pool = poolInfo[_pid]; 349 | UserInfo storage user = userInfo[_pid][_user]; 350 | updatePool(_pid); 351 | if (user.amount > 0) { 352 | uint256 pendingAmount = user.amount.mul(pool.accMdxPerShare).div(1e12).sub(user.rewardDebt); 353 | if (pendingAmount > 0) { 354 | safeMdxTransfer(_user, pendingAmount); 355 | } 356 | } 357 | if (_amount > 0) { 358 | pool.lpToken.safeTransferFrom(_user, address(this), _amount); 359 | user.amount = user.amount.add(_amount); 360 | pool.totalAmount = pool.totalAmount.add(_amount); 361 | } 362 | user.rewardDebt = user.amount.mul(pool.accMdxPerShare).div(1e12); 363 | emit Deposit(_user, _pid, _amount); 364 | } 365 | 366 | // Withdraw LP tokens from HecoPool. 367 | function withdraw(uint256 _pid, uint256 _amount) public notPause { 368 | PoolInfo storage pool = poolInfo[_pid]; 369 | if (isMultLP(address(pool.lpToken))) { 370 | withdrawMdxAndToken(_pid, _amount, msg.sender); 371 | } else { 372 | withdrawMdx(_pid, _amount, msg.sender); 373 | } 374 | } 375 | 376 | function withdrawMdxAndToken(uint256 _pid, uint256 _amount, address _user) private { 377 | PoolInfo storage pool = poolInfo[_pid]; 378 | UserInfo storage user = userInfo[_pid][_user]; 379 | require(user.amount >= _amount, "withdrawMdxAndToken: not good"); 380 | updatePool(_pid); 381 | uint256 pendingAmount = user.amount.mul(pool.accMdxPerShare).div(1e12).sub(user.rewardDebt); 382 | if (pendingAmount > 0) { 383 | safeMdxTransfer(_user, pendingAmount); 384 | } 385 | if (_amount > 0) { 386 | uint256 beforeToken = IERC20(multLpToken).balanceOf(address(this)); 387 | IMasterChefHeco(multLpChef).withdraw(poolCorrespond[_pid], _amount); 388 | uint256 afterToken = IERC20(multLpToken).balanceOf(address(this)); 389 | pool.accMultLpPerShare = pool.accMultLpPerShare.add(afterToken.sub(beforeToken).mul(1e12).div(pool.totalAmount)); 390 | uint256 tokenPending = user.amount.mul(pool.accMultLpPerShare).div(1e12).sub(user.multLpRewardDebt); 391 | if (tokenPending > 0) { 392 | IERC20(multLpToken).safeTransfer(_user, tokenPending); 393 | } 394 | user.amount = user.amount.sub(_amount); 395 | pool.totalAmount = pool.totalAmount.sub(_amount); 396 | pool.lpToken.safeTransfer(_user, _amount); 397 | } 398 | user.rewardDebt = user.amount.mul(pool.accMdxPerShare).div(1e12); 399 | user.multLpRewardDebt = user.amount.mul(pool.accMultLpPerShare).div(1e12); 400 | emit Withdraw(_user, _pid, _amount); 401 | } 402 | 403 | function withdrawMdx(uint256 _pid, uint256 _amount, address _user) private { 404 | PoolInfo storage pool = poolInfo[_pid]; 405 | UserInfo storage user = userInfo[_pid][_user]; 406 | require(user.amount >= _amount, "withdrawMdx: not good"); 407 | updatePool(_pid); 408 | uint256 pendingAmount = user.amount.mul(pool.accMdxPerShare).div(1e12).sub(user.rewardDebt); 409 | if (pendingAmount > 0) { 410 | safeMdxTransfer(_user, pendingAmount); 411 | } 412 | if (_amount > 0) { 413 | user.amount = user.amount.sub(_amount); 414 | pool.totalAmount = pool.totalAmount.sub(_amount); 415 | pool.lpToken.safeTransfer(_user, _amount); 416 | } 417 | user.rewardDebt = user.amount.mul(pool.accMdxPerShare).div(1e12); 418 | emit Withdraw(_user, _pid, _amount); 419 | } 420 | 421 | // Withdraw without caring about rewards. EMERGENCY ONLY. 422 | function emergencyWithdraw(uint256 _pid) public notPause { 423 | PoolInfo storage pool = poolInfo[_pid]; 424 | if (isMultLP(address(pool.lpToken))) { 425 | emergencyWithdrawMdxAndToken(_pid, msg.sender); 426 | } else { 427 | emergencyWithdrawMdx(_pid, msg.sender); 428 | } 429 | } 430 | 431 | function emergencyWithdrawMdxAndToken(uint256 _pid, address _user) private { 432 | PoolInfo storage pool = poolInfo[_pid]; 433 | UserInfo storage user = userInfo[_pid][_user]; 434 | uint256 amount = user.amount; 435 | uint256 beforeToken = IERC20(multLpToken).balanceOf(address(this)); 436 | IMasterChefHeco(multLpChef).withdraw(poolCorrespond[_pid], amount); 437 | uint256 afterToken = IERC20(multLpToken).balanceOf(address(this)); 438 | pool.accMultLpPerShare = pool.accMultLpPerShare.add(afterToken.sub(beforeToken).mul(1e12).div(pool.totalAmount)); 439 | user.amount = 0; 440 | user.rewardDebt = 0; 441 | pool.lpToken.safeTransfer(_user, amount); 442 | pool.totalAmount = pool.totalAmount.sub(amount); 443 | emit EmergencyWithdraw(_user, _pid, amount); 444 | } 445 | 446 | function emergencyWithdrawMdx(uint256 _pid, address _user) private { 447 | PoolInfo storage pool = poolInfo[_pid]; 448 | UserInfo storage user = userInfo[_pid][_user]; 449 | uint256 amount = user.amount; 450 | user.amount = 0; 451 | user.rewardDebt = 0; 452 | pool.lpToken.safeTransfer(_user, amount); 453 | pool.totalAmount = pool.totalAmount.sub(amount); 454 | emit EmergencyWithdraw(_user, _pid, amount); 455 | } 456 | 457 | // Safe MDX transfer function, just in case if rounding error causes pool to not have enough MDXs. 458 | function safeMdxTransfer(address _to, uint256 _amount) internal { 459 | uint256 mdxBal = mdx.balanceOf(address(this)); 460 | if (_amount > mdxBal) { 461 | mdx.transfer(_to, mdxBal); 462 | } else { 463 | mdx.transfer(_to, _amount); 464 | } 465 | } 466 | 467 | modifier notPause() { 468 | require(paused == false, "Mining has been suspended"); 469 | _; 470 | } 471 | } 472 | -------------------------------------------------------------------------------- /contracts/heco/Factory.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >=0.5.0 <0.8.0; 2 | 3 | import "../library/SafeMath.sol"; 4 | import "../interface/IERC20.sol"; 5 | import "../interface/IMdexFactory.sol"; 6 | import "../interface/IMdexPair.sol"; 7 | 8 | interface IHswapV2Callee { 9 | function hswapV2Call(address sender, uint amount0, uint amount1, bytes calldata data) external; 10 | } 11 | 12 | interface IMdexERC20 { 13 | event Approval(address indexed owner, address indexed spender, uint value); 14 | event Transfer(address indexed from, address indexed to, uint value); 15 | 16 | function name() external pure returns (string memory); 17 | 18 | function symbol() external pure returns (string memory); 19 | 20 | function decimals() external pure returns (uint8); 21 | 22 | function totalSupply() external view returns (uint); 23 | 24 | function balanceOf(address owner) external view returns (uint); 25 | 26 | function allowance(address owner, address spender) external view returns (uint); 27 | 28 | function approve(address spender, uint value) external returns (bool); 29 | 30 | function transfer(address to, uint value) external returns (bool); 31 | 32 | function transferFrom(address from, address to, uint value) external returns (bool); 33 | 34 | function DOMAIN_SEPARATOR() external view returns (bytes32); 35 | 36 | function PERMIT_TYPEHASH() external pure returns (bytes32); 37 | 38 | function nonces(address owner) external view returns (uint); 39 | 40 | function permit(address owner, address spender, uint value, uint deadline, uint8 v, bytes32 r, bytes32 s) external; 41 | } 42 | 43 | contract MdexERC20 is IMdexERC20 { 44 | using SafeMath for uint; 45 | 46 | string public constant name = 'HSwap LP Token'; 47 | string public constant symbol = 'HMDX'; 48 | uint8 public constant decimals = 18; 49 | uint public totalSupply; 50 | mapping(address => uint) public balanceOf; 51 | mapping(address => mapping(address => uint)) public allowance; 52 | 53 | bytes32 public DOMAIN_SEPARATOR; 54 | // keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)"); 55 | bytes32 public constant PERMIT_TYPEHASH = 0x6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9; 56 | mapping(address => uint) public nonces; 57 | 58 | event Approval(address indexed owner, address indexed spender, uint value); 59 | event Transfer(address indexed from, address indexed to, uint value); 60 | 61 | constructor() public { 62 | uint chainId; 63 | assembly { 64 | chainId := chainid 65 | } 66 | DOMAIN_SEPARATOR = keccak256( 67 | abi.encode( 68 | keccak256('EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)'), 69 | keccak256(bytes(name)), 70 | keccak256(bytes('1')), 71 | chainId, 72 | address(this) 73 | ) 74 | ); 75 | } 76 | 77 | function _mint(address to, uint value) internal { 78 | totalSupply = totalSupply.add(value); 79 | balanceOf[to] = balanceOf[to].add(value); 80 | emit Transfer(address(0), to, value); 81 | } 82 | 83 | function _burn(address from, uint value) internal { 84 | balanceOf[from] = balanceOf[from].sub(value); 85 | totalSupply = totalSupply.sub(value); 86 | emit Transfer(from, address(0), value); 87 | } 88 | 89 | function _approve(address owner, address spender, uint value) private { 90 | allowance[owner][spender] = value; 91 | emit Approval(owner, spender, value); 92 | } 93 | 94 | function _transfer(address from, address to, uint value) private { 95 | balanceOf[from] = balanceOf[from].sub(value); 96 | balanceOf[to] = balanceOf[to].add(value); 97 | emit Transfer(from, to, value); 98 | } 99 | 100 | function approve(address spender, uint value) external returns (bool) { 101 | _approve(msg.sender, spender, value); 102 | return true; 103 | } 104 | 105 | function transfer(address to, uint value) external returns (bool) { 106 | _transfer(msg.sender, to, value); 107 | return true; 108 | } 109 | 110 | function transferFrom(address from, address to, uint value) external returns (bool) { 111 | if (allowance[from][msg.sender] != uint(- 1)) { 112 | allowance[from][msg.sender] = allowance[from][msg.sender].sub(value); 113 | } 114 | _transfer(from, to, value); 115 | return true; 116 | } 117 | 118 | function permit(address owner, address spender, uint value, uint deadline, uint8 v, bytes32 r, bytes32 s) external { 119 | require(deadline >= block.timestamp, 'MdexSwap: EXPIRED'); 120 | bytes32 digest = keccak256( 121 | abi.encodePacked( 122 | '\x19\x01', 123 | DOMAIN_SEPARATOR, 124 | keccak256(abi.encode(PERMIT_TYPEHASH, owner, spender, value, nonces[owner]++, deadline)) 125 | ) 126 | ); 127 | address recoveredAddress = ecrecover(digest, v, r, s); 128 | require(recoveredAddress != address(0) && recoveredAddress == owner, 'MdexSwap: INVALID_SIGNATURE'); 129 | _approve(owner, spender, value); 130 | } 131 | } 132 | 133 | contract MdexPair is IMdexPair, MdexERC20 { 134 | using SafeMath for uint; 135 | using UQ112x112 for uint224; 136 | 137 | uint public constant MINIMUM_LIQUIDITY = 10 ** 3; 138 | bytes4 private constant SELECTOR = bytes4(keccak256(bytes('transfer(address,uint256)'))); 139 | 140 | address public factory; 141 | address public token0; 142 | address public token1; 143 | 144 | uint112 private reserve0; // uses single storage slot, accessible via getReserves 145 | uint112 private reserve1; // uses single storage slot, accessible via getReserves 146 | uint32 private blockTimestampLast; // uses single storage slot, accessible via getReserves 147 | 148 | uint public price0CumulativeLast; 149 | uint public price1CumulativeLast; 150 | uint public kLast; // reserve0 * reserve1, as of immediately after the most recent liquidity event 151 | 152 | uint private unlocked = 1; 153 | modifier lock() { 154 | require(unlocked == 1, 'MdexSwap: LOCKED'); 155 | unlocked = 0; 156 | _; 157 | unlocked = 1; 158 | } 159 | 160 | function getReserves() public view returns (uint112 _reserve0, uint112 _reserve1, uint32 _blockTimestampLast) { 161 | _reserve0 = reserve0; 162 | _reserve1 = reserve1; 163 | _blockTimestampLast = blockTimestampLast; 164 | } 165 | 166 | function _safeTransfer(address token, address to, uint value) private { 167 | (bool success, bytes memory data) = token.call(abi.encodeWithSelector(SELECTOR, to, value)); 168 | require(success && (data.length == 0 || abi.decode(data, (bool))), 'MdexSwap: TRANSFER_FAILED'); 169 | } 170 | 171 | event Mint(address indexed sender, uint amount0, uint amount1); 172 | event Burn(address indexed sender, uint amount0, uint amount1, address indexed to); 173 | event Swap( 174 | address indexed sender, 175 | uint amount0In, 176 | uint amount1In, 177 | uint amount0Out, 178 | uint amount1Out, 179 | address indexed to 180 | ); 181 | event Sync(uint112 reserve0, uint112 reserve1); 182 | 183 | constructor() public { 184 | factory = msg.sender; 185 | } 186 | 187 | // called once by the factory at time of deployment 188 | function initialize(address _token0, address _token1) external { 189 | require(msg.sender == factory, 'MdexSwap: FORBIDDEN'); 190 | // sufficient check 191 | token0 = _token0; 192 | token1 = _token1; 193 | } 194 | 195 | // update reserves and, on the first call per block, price accumulators 196 | function _update(uint balance0, uint balance1, uint112 _reserve0, uint112 _reserve1) private { 197 | require(balance0 <= uint112(- 1) && balance1 <= uint112(- 1), 'MdexSwap: OVERFLOW'); 198 | uint32 blockTimestamp = uint32(block.timestamp % 2 ** 32); 199 | uint32 timeElapsed = blockTimestamp - blockTimestampLast; 200 | // overflow is desired 201 | if (timeElapsed > 0 && _reserve0 != 0 && _reserve1 != 0) { 202 | // * never overflows, and + overflow is desired 203 | price0CumulativeLast += uint(UQ112x112.encode(_reserve1).uqdiv(_reserve0)) * timeElapsed; 204 | price1CumulativeLast += uint(UQ112x112.encode(_reserve0).uqdiv(_reserve1)) * timeElapsed; 205 | } 206 | reserve0 = uint112(balance0); 207 | reserve1 = uint112(balance1); 208 | blockTimestampLast = blockTimestamp; 209 | emit Sync(reserve0, reserve1); 210 | } 211 | 212 | // if fee is on, mint liquidity equivalent to 1/6th of the growth in sqrt(k) 213 | function _mintFee(uint112 _reserve0, uint112 _reserve1) private returns (bool feeOn) { 214 | address feeTo = IMdexFactory(factory).feeTo(); 215 | feeOn = feeTo != address(0); 216 | uint _kLast = kLast; 217 | // gas savings 218 | if (feeOn) { 219 | if (_kLast != 0) { 220 | uint rootK = SafeMath.sqrt(uint(_reserve0).mul(_reserve1)); 221 | uint rootKLast = SafeMath.sqrt(_kLast); 222 | if (rootK > rootKLast) { 223 | uint numerator = totalSupply.mul(rootK.sub(rootKLast)); 224 | uint denominator = rootK.mul(IMdexFactory(factory).feeToRate()).add(rootKLast); 225 | uint liquidity = numerator / denominator; 226 | if (liquidity > 0) _mint(feeTo, liquidity); 227 | } 228 | } 229 | } else if (_kLast != 0) { 230 | kLast = 0; 231 | } 232 | } 233 | 234 | // this low-level function should be called from a contract which performs important safety checks 235 | function mint(address to) external lock returns (uint liquidity) { 236 | (uint112 _reserve0, uint112 _reserve1,) = getReserves(); 237 | // gas savings 238 | uint balance0 = IERC20(token0).balanceOf(address(this)); 239 | uint balance1 = IERC20(token1).balanceOf(address(this)); 240 | uint amount0 = balance0.sub(_reserve0); 241 | uint amount1 = balance1.sub(_reserve1); 242 | 243 | bool feeOn = _mintFee(_reserve0, _reserve1); 244 | uint _totalSupply = totalSupply; 245 | // gas savings, must be defined here since totalSupply can update in _mintFee 246 | if (_totalSupply == 0) { 247 | liquidity = SafeMath.sqrt(amount0.mul(amount1)).sub(MINIMUM_LIQUIDITY); 248 | _mint(address(0), MINIMUM_LIQUIDITY); 249 | // permanently lock the first MINIMUM_LIQUIDITY tokens 250 | } else { 251 | liquidity = SafeMath.min(amount0.mul(_totalSupply) / _reserve0, amount1.mul(_totalSupply) / _reserve1); 252 | } 253 | require(liquidity > 0, 'MdexSwap: INSUFFICIENT_LIQUIDITY_MINTED'); 254 | _mint(to, liquidity); 255 | 256 | _update(balance0, balance1, _reserve0, _reserve1); 257 | if (feeOn) kLast = uint(reserve0).mul(reserve1); 258 | // reserve0 and reserve1 are up-to-date 259 | emit Mint(msg.sender, amount0, amount1); 260 | } 261 | 262 | // this low-level function should be called from a contract which performs important safety checks 263 | function burn(address to) external lock returns (uint amount0, uint amount1) { 264 | (uint112 _reserve0, uint112 _reserve1,) = getReserves(); 265 | // gas savings 266 | address _token0 = token0; 267 | // gas savings 268 | address _token1 = token1; 269 | // gas savings 270 | uint balance0 = IERC20(_token0).balanceOf(address(this)); 271 | uint balance1 = IERC20(_token1).balanceOf(address(this)); 272 | uint liquidity = balanceOf[address(this)]; 273 | 274 | bool feeOn = _mintFee(_reserve0, _reserve1); 275 | uint _totalSupply = totalSupply; 276 | // gas savings, must be defined here since totalSupply can update in _mintFee 277 | amount0 = liquidity.mul(balance0) / _totalSupply; 278 | // using balances ensures pro-rata distribution 279 | amount1 = liquidity.mul(balance1) / _totalSupply; 280 | // using balances ensures pro-rata distribution 281 | require(amount0 > 0 && amount1 > 0, 'MdexSwap: INSUFFICIENT_LIQUIDITY_BURNED'); 282 | _burn(address(this), liquidity); 283 | _safeTransfer(_token0, to, amount0); 284 | _safeTransfer(_token1, to, amount1); 285 | balance0 = IERC20(_token0).balanceOf(address(this)); 286 | balance1 = IERC20(_token1).balanceOf(address(this)); 287 | 288 | _update(balance0, balance1, _reserve0, _reserve1); 289 | if (feeOn) kLast = uint(reserve0).mul(reserve1); 290 | // reserve0 and reserve1 are up-to-date 291 | emit Burn(msg.sender, amount0, amount1, to); 292 | } 293 | 294 | // this low-level function should be called from a contract which performs important safety checks 295 | function swap(uint amount0Out, uint amount1Out, address to, bytes calldata data) external lock { 296 | require(amount0Out > 0 || amount1Out > 0, 'MdexSwap: INSUFFICIENT_OUTPUT_AMOUNT'); 297 | (uint112 _reserve0, uint112 _reserve1,) = getReserves(); 298 | // gas savings 299 | require(amount0Out < _reserve0 && amount1Out < _reserve1, 'MdexSwap: INSUFFICIENT_LIQUIDITY'); 300 | 301 | uint balance0; 302 | uint balance1; 303 | {// scope for _token{0,1}, avoids stack too deep errors 304 | address _token0 = token0; 305 | address _token1 = token1; 306 | require(to != _token0 && to != _token1, 'MdexSwap: INVALID_TO'); 307 | if (amount0Out > 0) _safeTransfer(_token0, to, amount0Out); 308 | // optimistically transfer tokens 309 | if (amount1Out > 0) _safeTransfer(_token1, to, amount1Out); 310 | // optimistically transfer tokens 311 | if (data.length > 0) IHswapV2Callee(to).hswapV2Call(msg.sender, amount0Out, amount1Out, data); 312 | balance0 = IERC20(_token0).balanceOf(address(this)); 313 | balance1 = IERC20(_token1).balanceOf(address(this)); 314 | } 315 | uint amount0In = balance0 > _reserve0 - amount0Out ? balance0 - (_reserve0 - amount0Out) : 0; 316 | uint amount1In = balance1 > _reserve1 - amount1Out ? balance1 - (_reserve1 - amount1Out) : 0; 317 | require(amount0In > 0 || amount1In > 0, 'MdexSwap: INSUFFICIENT_INPUT_AMOUNT'); 318 | {// scope for reserve{0,1}Adjusted, avoids stack too deep errors 319 | uint balance0Adjusted = balance0.mul(1000).sub(amount0In.mul(3)); 320 | uint balance1Adjusted = balance1.mul(1000).sub(amount1In.mul(3)); 321 | require(balance0Adjusted.mul(balance1Adjusted) >= uint(_reserve0).mul(_reserve1).mul(1000 ** 2), 'MdexSwap: K'); 322 | } 323 | 324 | _update(balance0, balance1, _reserve0, _reserve1); 325 | emit Swap(msg.sender, amount0In, amount1In, amount0Out, amount1Out, to); 326 | } 327 | 328 | // force balances to match reserves 329 | function skim(address to) external lock { 330 | address _token0 = token0; 331 | // gas savings 332 | address _token1 = token1; 333 | // gas savings 334 | _safeTransfer(_token0, to, IERC20(_token0).balanceOf(address(this)).sub(reserve0)); 335 | _safeTransfer(_token1, to, IERC20(_token1).balanceOf(address(this)).sub(reserve1)); 336 | } 337 | 338 | // force reserves to match balances 339 | function sync() external lock { 340 | _update(IERC20(token0).balanceOf(address(this)), IERC20(token1).balanceOf(address(this)), reserve0, reserve1); 341 | } 342 | 343 | function price(address token, uint256 baseDecimal) public view returns (uint256) { 344 | if ((token0 != token && token1 != token) || 0 == reserve0 || 0 == reserve1) { 345 | return 0; 346 | } 347 | if (token0 == token) { 348 | return uint256(reserve1).mul(baseDecimal).div(uint256(reserve0)); 349 | } else { 350 | return uint256(reserve0).mul(baseDecimal).div(uint256(reserve1)); 351 | } 352 | } 353 | 354 | } 355 | 356 | contract MdexFactory is IMdexFactory { 357 | using SafeMath for uint256; 358 | address public feeTo; 359 | address public feeToSetter; 360 | uint256 public feeToRate; 361 | bytes32 public initCodeHash; 362 | 363 | mapping(address => mapping(address => address)) public getPair; 364 | address[] public allPairs; 365 | 366 | event PairCreated(address indexed token0, address indexed token1, address pair, uint); 367 | 368 | constructor(address _feeToSetter) public { 369 | feeToSetter = _feeToSetter; 370 | initCodeHash = keccak256(abi.encodePacked(type(MdexPair).creationCode)); 371 | } 372 | 373 | function allPairsLength() external view returns (uint) { 374 | return allPairs.length; 375 | } 376 | 377 | function createPair(address tokenA, address tokenB) external returns (address pair) { 378 | require(tokenA != tokenB, 'MdexSwapFactory: IDENTICAL_ADDRESSES'); 379 | (address token0, address token1) = tokenA < tokenB ? (tokenA, tokenB) : (tokenB, tokenA); 380 | require(token0 != address(0), 'MdexSwapFactory: ZERO_ADDRESS'); 381 | require(getPair[token0][token1] == address(0), 'MdexSwapFactory: PAIR_EXISTS'); 382 | // single check is sufficient 383 | bytes memory bytecode = type(MdexPair).creationCode; 384 | bytes32 salt = keccak256(abi.encodePacked(token0, token1)); 385 | assembly { 386 | pair := create2(0, add(bytecode, 32), mload(bytecode), salt) 387 | } 388 | IMdexPair(pair).initialize(token0, token1); 389 | getPair[token0][token1] = pair; 390 | getPair[token1][token0] = pair; 391 | // populate mapping in the reverse direction 392 | allPairs.push(pair); 393 | emit PairCreated(token0, token1, pair, allPairs.length); 394 | } 395 | 396 | function setFeeTo(address _feeTo) external { 397 | require(msg.sender == feeToSetter, 'MdexSwapFactory: FORBIDDEN'); 398 | feeTo = _feeTo; 399 | } 400 | 401 | function setFeeToSetter(address _feeToSetter) external { 402 | require(msg.sender == feeToSetter, 'MdexSwapFactory: FORBIDDEN'); 403 | require(_feeToSetter != address(0), "MdexSwapFactory: FeeToSetter is zero address"); 404 | feeToSetter = _feeToSetter; 405 | } 406 | 407 | function setFeeToRate(uint256 _rate) external { 408 | require(msg.sender == feeToSetter, 'MdexSwapFactory: FORBIDDEN'); 409 | require(_rate > 0, "MdexSwapFactory: FEE_TO_RATE_OVERFLOW"); 410 | feeToRate = _rate.sub(1); 411 | } 412 | 413 | // returns sorted token addresses, used to handle return values from pairs sorted in this order 414 | function sortTokens(address tokenA, address tokenB) public pure returns (address token0, address token1) { 415 | require(tokenA != tokenB, 'MdexSwapFactory: IDENTICAL_ADDRESSES'); 416 | (token0, token1) = tokenA < tokenB ? (tokenA, tokenB) : (tokenB, tokenA); 417 | require(token0 != address(0), 'MdexSwapFactory: ZERO_ADDRESS'); 418 | } 419 | 420 | // calculates the CREATE2 address for a pair without making any external calls 421 | function pairFor(address tokenA, address tokenB) public view returns (address pair) { 422 | (address token0, address token1) = sortTokens(tokenA, tokenB); 423 | pair = address(uint(keccak256(abi.encodePacked( 424 | hex'ff', 425 | address(this), 426 | keccak256(abi.encodePacked(token0, token1)), 427 | initCodeHash 428 | )))); 429 | } 430 | // fetches and sorts the reserves for a pair 431 | function getReserves(address tokenA, address tokenB) public view returns (uint reserveA, uint reserveB) { 432 | (address token0,) = sortTokens(tokenA, tokenB); 433 | (uint reserve0, uint reserve1,) = IMdexPair(pairFor(tokenA, tokenB)).getReserves(); 434 | (reserveA, reserveB) = tokenA == token0 ? (reserve0, reserve1) : (reserve1, reserve0); 435 | } 436 | 437 | // given some amount of an asset and pair reserves, returns an equivalent amount of the other asset 438 | function quote(uint amountA, uint reserveA, uint reserveB) public pure returns (uint amountB) { 439 | require(amountA > 0, 'MdexSwapFactory: INSUFFICIENT_AMOUNT'); 440 | require(reserveA > 0 && reserveB > 0, 'MdexSwapFactory: INSUFFICIENT_LIQUIDITY'); 441 | amountB = amountA.mul(reserveB) / reserveA; 442 | } 443 | 444 | // given an input amount of an asset and pair reserves, returns the maximum output amount of the other asset 445 | function getAmountOut(uint amountIn, uint reserveIn, uint reserveOut) public view returns (uint amountOut) { 446 | require(amountIn > 0, 'MdexSwapFactory: INSUFFICIENT_INPUT_AMOUNT'); 447 | require(reserveIn > 0 && reserveOut > 0, 'MdexSwapFactory: INSUFFICIENT_LIQUIDITY'); 448 | uint amountInWithFee = amountIn.mul(997); 449 | uint numerator = amountInWithFee.mul(reserveOut); 450 | uint denominator = reserveIn.mul(1000).add(amountInWithFee); 451 | amountOut = numerator / denominator; 452 | } 453 | 454 | // given an output amount of an asset and pair reserves, returns a required input amount of the other asset 455 | function getAmountIn(uint amountOut, uint reserveIn, uint reserveOut) public view returns (uint amountIn) { 456 | require(amountOut > 0, 'MdexSwapFactory: INSUFFICIENT_OUTPUT_AMOUNT'); 457 | require(reserveIn > 0 && reserveOut > 0, 'MdexSwapFactory: INSUFFICIENT_LIQUIDITY'); 458 | uint numerator = reserveIn.mul(amountOut).mul(1000); 459 | uint denominator = reserveOut.sub(amountOut).mul(997); 460 | amountIn = (numerator / denominator).add(1); 461 | } 462 | 463 | // performs chained getAmountOut calculations on any number of pairs 464 | function getAmountsOut(uint amountIn, address[] memory path) public view returns (uint[] memory amounts) { 465 | require(path.length >= 2, 'MdexSwapFactory: INVALID_PATH'); 466 | amounts = new uint[](path.length); 467 | amounts[0] = amountIn; 468 | for (uint i; i < path.length - 1; i++) { 469 | (uint reserveIn, uint reserveOut) = getReserves(path[i], path[i + 1]); 470 | amounts[i + 1] = getAmountOut(amounts[i], reserveIn, reserveOut); 471 | } 472 | } 473 | 474 | // performs chained getAmountIn calculations on any number of pairs 475 | function getAmountsIn(uint amountOut, address[] memory path) public view returns (uint[] memory amounts) { 476 | require(path.length >= 2, 'MdexSwapFactory: INVALID_PATH'); 477 | amounts = new uint[](path.length); 478 | amounts[amounts.length - 1] = amountOut; 479 | for (uint i = path.length - 1; i > 0; i--) { 480 | (uint reserveIn, uint reserveOut) = getReserves(path[i - 1], path[i]); 481 | amounts[i - 1] = getAmountIn(amounts[i], reserveIn, reserveOut); 482 | } 483 | } 484 | } 485 | 486 | library UQ112x112 { 487 | uint224 constant Q112 = 2 ** 112; 488 | 489 | // encode a uint112 as a UQ112x112 490 | function encode(uint112 y) internal pure returns (uint224 z) { 491 | z = uint224(y) * Q112; 492 | // never overflows 493 | } 494 | 495 | // divide a UQ112x112 by a uint112, returning a UQ112x112 496 | function uqdiv(uint224 x, uint112 y) internal pure returns (uint224 z) { 497 | z = x / uint224(y); 498 | } 499 | } 500 | -------------------------------------------------------------------------------- /contracts/heco/Router.sol: -------------------------------------------------------------------------------- 1 | pragma solidity =0.6.6; 2 | 3 | import "@openzeppelin/contracts/access/Ownable.sol"; 4 | import "../library/SafeMath.sol"; 5 | import "../interface/IERC20.sol"; 6 | import "../interface/IMdexFactory.sol"; 7 | import "../interface/IMdexPair.sol"; 8 | 9 | interface IMdexRouter { 10 | function factory() external pure returns (address); 11 | 12 | function WHT() external pure returns (address); 13 | 14 | function swapMining() external pure returns (address); 15 | 16 | function addLiquidity( 17 | address tokenA, 18 | address tokenB, 19 | uint amountADesired, 20 | uint amountBDesired, 21 | uint amountAMin, 22 | uint amountBMin, 23 | address to, 24 | uint deadline 25 | ) external returns (uint amountA, uint amountB, uint liquidity); 26 | 27 | function addLiquidityETH( 28 | address token, 29 | uint amountTokenDesired, 30 | uint amountTokenMin, 31 | uint amountETHMin, 32 | address to, 33 | uint deadline 34 | ) external payable returns (uint amountToken, uint amountETH, uint liquidity); 35 | 36 | function removeLiquidity( 37 | address tokenA, 38 | address tokenB, 39 | uint liquidity, 40 | uint amountAMin, 41 | uint amountBMin, 42 | address to, 43 | uint deadline 44 | ) external returns (uint amountA, uint amountB); 45 | 46 | function removeLiquidityETH( 47 | address token, 48 | uint liquidity, 49 | uint amountTokenMin, 50 | uint amountETHMin, 51 | address to, 52 | uint deadline 53 | ) external returns (uint amountToken, uint amountETH); 54 | 55 | function removeLiquidityWithPermit( 56 | address tokenA, 57 | address tokenB, 58 | uint liquidity, 59 | uint amountAMin, 60 | uint amountBMin, 61 | address to, 62 | uint deadline, 63 | bool approveMax, uint8 v, bytes32 r, bytes32 s 64 | ) external returns (uint amountA, uint amountB); 65 | 66 | function removeLiquidityETHWithPermit( 67 | address token, 68 | uint liquidity, 69 | uint amountTokenMin, 70 | uint amountETHMin, 71 | address to, 72 | uint deadline, 73 | bool approveMax, uint8 v, bytes32 r, bytes32 s 74 | ) external returns (uint amountToken, uint amountETH); 75 | 76 | function swapExactTokensForTokens( 77 | uint amountIn, 78 | uint amountOutMin, 79 | address[] calldata path, 80 | address to, 81 | uint deadline 82 | ) external returns (uint[] memory amounts); 83 | 84 | function swapTokensForExactTokens( 85 | uint amountOut, 86 | uint amountInMax, 87 | address[] calldata path, 88 | address to, 89 | uint deadline 90 | ) external returns (uint[] memory amounts); 91 | 92 | function swapExactETHForTokens(uint amountOutMin, address[] calldata path, address to, uint deadline) 93 | external 94 | payable 95 | returns (uint[] memory amounts); 96 | 97 | function swapTokensForExactETH(uint amountOut, uint amountInMax, address[] calldata path, address to, uint deadline) 98 | external 99 | returns (uint[] memory amounts); 100 | 101 | function swapExactTokensForETH(uint amountIn, uint amountOutMin, address[] calldata path, address to, uint deadline) 102 | external 103 | returns (uint[] memory amounts); 104 | 105 | function swapETHForExactTokens(uint amountOut, address[] calldata path, address to, uint deadline) 106 | external 107 | payable 108 | returns (uint[] memory amounts); 109 | 110 | function quote(uint256 amountA, uint256 reserveA, uint256 reserveB) external view returns (uint256 amountB); 111 | 112 | function getAmountOut(uint256 amountIn, uint256 reserveIn, uint256 reserveOut) external view returns (uint256 amountOut); 113 | 114 | function getAmountIn(uint256 amountOut, uint256 reserveIn, uint256 reserveOut) external view returns (uint256 amountIn); 115 | 116 | function getAmountsOut(uint256 amountIn, address[] calldata path) external view returns (uint256[] memory amounts); 117 | 118 | function getAmountsIn(uint256 amountOut, address[] calldata path) external view returns (uint256[] memory amounts); 119 | 120 | function removeLiquidityETHSupportingFeeOnTransferTokens( 121 | address token, 122 | uint liquidity, 123 | uint amountTokenMin, 124 | uint amountETHMin, 125 | address to, 126 | uint deadline 127 | ) external returns (uint amountETH); 128 | 129 | function removeLiquidityETHWithPermitSupportingFeeOnTransferTokens( 130 | address token, 131 | uint liquidity, 132 | uint amountTokenMin, 133 | uint amountETHMin, 134 | address to, 135 | uint deadline, 136 | bool approveMax, uint8 v, bytes32 r, bytes32 s 137 | ) external returns (uint amountETH); 138 | 139 | function swapExactTokensForTokensSupportingFeeOnTransferTokens( 140 | uint amountIn, 141 | uint amountOutMin, 142 | address[] calldata path, 143 | address to, 144 | uint deadline 145 | ) external; 146 | 147 | function swapExactETHForTokensSupportingFeeOnTransferTokens( 148 | uint amountOutMin, 149 | address[] calldata path, 150 | address to, 151 | uint deadline 152 | ) external payable; 153 | 154 | function swapExactTokensForETHSupportingFeeOnTransferTokens( 155 | uint amountIn, 156 | uint amountOutMin, 157 | address[] calldata path, 158 | address to, 159 | uint deadline 160 | ) external; 161 | } 162 | 163 | interface ISwapMining { 164 | function swap(address account, address input, address output, uint256 amount) external returns (bool); 165 | } 166 | 167 | interface IWHT { 168 | function deposit() external payable; 169 | 170 | function transfer(address to, uint value) external returns (bool); 171 | 172 | function withdraw(uint) external; 173 | } 174 | 175 | contract MdexRouter is IMdexRouter, Ownable { 176 | using SafeMath for uint256; 177 | 178 | address public immutable override factory; 179 | address public immutable override WHT; 180 | address public override swapMining; 181 | 182 | modifier ensure(uint deadline) { 183 | require(deadline >= block.timestamp, 'MdexRouter: EXPIRED'); 184 | _; 185 | } 186 | 187 | constructor(address _factory, address _WHT) public { 188 | factory = _factory; 189 | WHT = _WHT; 190 | } 191 | 192 | receive() external payable { 193 | assert(msg.sender == WHT); 194 | // only accept HT via fallback from the WHT contract 195 | } 196 | 197 | function pairFor(address tokenA, address tokenB) public view returns (address pair){ 198 | pair = IMdexFactory(factory).pairFor(tokenA, tokenB); 199 | } 200 | 201 | function setSwapMining(address _swapMininng) public onlyOwner { 202 | swapMining = _swapMininng; 203 | } 204 | 205 | // **** ADD LIQUIDITY **** 206 | function _addLiquidity( 207 | address tokenA, 208 | address tokenB, 209 | uint amountADesired, 210 | uint amountBDesired, 211 | uint amountAMin, 212 | uint amountBMin 213 | ) internal virtual returns (uint amountA, uint amountB) { 214 | // create the pair if it doesn't exist yet 215 | if (IMdexFactory(factory).getPair(tokenA, tokenB) == address(0)) { 216 | IMdexFactory(factory).createPair(tokenA, tokenB); 217 | } 218 | (uint reserveA, uint reserveB) = IMdexFactory(factory).getReserves(tokenA, tokenB); 219 | if (reserveA == 0 && reserveB == 0) { 220 | (amountA, amountB) = (amountADesired, amountBDesired); 221 | } else { 222 | uint amountBOptimal = IMdexFactory(factory).quote(amountADesired, reserveA, reserveB); 223 | if (amountBOptimal <= amountBDesired) { 224 | require(amountBOptimal >= amountBMin, 'MdexRouter: INSUFFICIENT_B_AMOUNT'); 225 | (amountA, amountB) = (amountADesired, amountBOptimal); 226 | } else { 227 | uint amountAOptimal = IMdexFactory(factory).quote(amountBDesired, reserveB, reserveA); 228 | assert(amountAOptimal <= amountADesired); 229 | require(amountAOptimal >= amountAMin, 'MdexRouter: INSUFFICIENT_A_AMOUNT'); 230 | (amountA, amountB) = (amountAOptimal, amountBDesired); 231 | } 232 | } 233 | } 234 | 235 | function addLiquidity( 236 | address tokenA, 237 | address tokenB, 238 | uint amountADesired, 239 | uint amountBDesired, 240 | uint amountAMin, 241 | uint amountBMin, 242 | address to, 243 | uint deadline 244 | ) external virtual override ensure(deadline) returns (uint amountA, uint amountB, uint liquidity) { 245 | (amountA, amountB) = _addLiquidity(tokenA, tokenB, amountADesired, amountBDesired, amountAMin, amountBMin); 246 | address pair = pairFor(tokenA, tokenB); 247 | TransferHelper.safeTransferFrom(tokenA, msg.sender, pair, amountA); 248 | TransferHelper.safeTransferFrom(tokenB, msg.sender, pair, amountB); 249 | liquidity = IMdexPair(pair).mint(to); 250 | } 251 | 252 | function addLiquidityETH( 253 | address token, 254 | uint amountTokenDesired, 255 | uint amountTokenMin, 256 | uint amountETHMin, 257 | address to, 258 | uint deadline 259 | ) external virtual override payable ensure(deadline) returns (uint amountToken, uint amountETH, uint liquidity) { 260 | (amountToken, amountETH) = _addLiquidity( 261 | token, 262 | WHT, 263 | amountTokenDesired, 264 | msg.value, 265 | amountTokenMin, 266 | amountETHMin 267 | ); 268 | address pair = pairFor(token, WHT); 269 | TransferHelper.safeTransferFrom(token, msg.sender, pair, amountToken); 270 | IWHT(WHT).deposit{value : amountETH}(); 271 | assert(IWHT(WHT).transfer(pair, amountETH)); 272 | liquidity = IMdexPair(pair).mint(to); 273 | // refund dust eth, if any 274 | if (msg.value > amountETH) TransferHelper.safeTransferETH(msg.sender, msg.value - amountETH); 275 | } 276 | 277 | // **** REMOVE LIQUIDITY **** 278 | function removeLiquidity( 279 | address tokenA, 280 | address tokenB, 281 | uint liquidity, 282 | uint amountAMin, 283 | uint amountBMin, 284 | address to, 285 | uint deadline 286 | ) public virtual override ensure(deadline) returns (uint amountA, uint amountB) { 287 | address pair = pairFor(tokenA, tokenB); 288 | IMdexPair(pair).transferFrom(msg.sender, pair, liquidity); 289 | // send liquidity to pair 290 | (uint amount0, uint amount1) = IMdexPair(pair).burn(to); 291 | (address token0,) = IMdexFactory(factory).sortTokens(tokenA, tokenB); 292 | (amountA, amountB) = tokenA == token0 ? (amount0, amount1) : (amount1, amount0); 293 | require(amountA >= amountAMin, 'MdexRouter: INSUFFICIENT_A_AMOUNT'); 294 | require(amountB >= amountBMin, 'MdexRouter: INSUFFICIENT_B_AMOUNT'); 295 | } 296 | 297 | function removeLiquidityETH( 298 | address token, 299 | uint liquidity, 300 | uint amountTokenMin, 301 | uint amountETHMin, 302 | address to, 303 | uint deadline 304 | ) public virtual override ensure(deadline) returns (uint amountToken, uint amountETH) { 305 | (amountToken, amountETH) = removeLiquidity( 306 | token, 307 | WHT, 308 | liquidity, 309 | amountTokenMin, 310 | amountETHMin, 311 | address(this), 312 | deadline 313 | ); 314 | TransferHelper.safeTransfer(token, to, amountToken); 315 | IWHT(WHT).withdraw(amountETH); 316 | TransferHelper.safeTransferETH(to, amountETH); 317 | } 318 | 319 | function removeLiquidityWithPermit( 320 | address tokenA, 321 | address tokenB, 322 | uint liquidity, 323 | uint amountAMin, 324 | uint amountBMin, 325 | address to, 326 | uint deadline, 327 | bool approveMax, uint8 v, bytes32 r, bytes32 s 328 | ) external virtual override returns (uint amountA, uint amountB) { 329 | address pair = pairFor(tokenA, tokenB); 330 | uint value = approveMax ? uint(- 1) : liquidity; 331 | IMdexPair(pair).permit(msg.sender, address(this), value, deadline, v, r, s); 332 | (amountA, amountB) = removeLiquidity(tokenA, tokenB, liquidity, amountAMin, amountBMin, to, deadline); 333 | } 334 | 335 | function removeLiquidityETHWithPermit( 336 | address token, 337 | uint liquidity, 338 | uint amountTokenMin, 339 | uint amountETHMin, 340 | address to, 341 | uint deadline, 342 | bool approveMax, uint8 v, bytes32 r, bytes32 s 343 | ) external virtual override returns (uint amountToken, uint amountETH) { 344 | address pair = pairFor(token, WHT); 345 | uint value = approveMax ? uint(- 1) : liquidity; 346 | IMdexPair(pair).permit(msg.sender, address(this), value, deadline, v, r, s); 347 | (amountToken, amountETH) = removeLiquidityETH(token, liquidity, amountTokenMin, amountETHMin, to, deadline); 348 | } 349 | 350 | // **** REMOVE LIQUIDITY (supporting fee-on-transfer tokens) **** 351 | function removeLiquidityETHSupportingFeeOnTransferTokens( 352 | address token, 353 | uint liquidity, 354 | uint amountTokenMin, 355 | uint amountETHMin, 356 | address to, 357 | uint deadline 358 | ) public virtual override ensure(deadline) returns (uint amountETH) { 359 | (, amountETH) = removeLiquidity( 360 | token, 361 | WHT, 362 | liquidity, 363 | amountTokenMin, 364 | amountETHMin, 365 | address(this), 366 | deadline 367 | ); 368 | TransferHelper.safeTransfer(token, to, IERC20(token).balanceOf(address(this))); 369 | IWHT(WHT).withdraw(amountETH); 370 | TransferHelper.safeTransferETH(to, amountETH); 371 | } 372 | 373 | function removeLiquidityETHWithPermitSupportingFeeOnTransferTokens( 374 | address token, 375 | uint liquidity, 376 | uint amountTokenMin, 377 | uint amountETHMin, 378 | address to, 379 | uint deadline, 380 | bool approveMax, uint8 v, bytes32 r, bytes32 s 381 | ) external virtual override returns (uint amountETH) { 382 | address pair = pairFor(token, WHT); 383 | uint value = approveMax ? uint(- 1) : liquidity; 384 | IMdexPair(pair).permit(msg.sender, address(this), value, deadline, v, r, s); 385 | amountETH = removeLiquidityETHSupportingFeeOnTransferTokens( 386 | token, liquidity, amountTokenMin, amountETHMin, to, deadline 387 | ); 388 | } 389 | 390 | // **** SWAP **** 391 | // requires the initial amount to have already been sent to the first pair 392 | function _swap(uint[] memory amounts, address[] memory path, address _to) internal virtual { 393 | for (uint i; i < path.length - 1; i++) { 394 | (address input, address output) = (path[i], path[i + 1]); 395 | (address token0,) = IMdexFactory(factory).sortTokens(input, output); 396 | uint amountOut = amounts[i + 1]; 397 | if (swapMining != address(0)) { 398 | ISwapMining(swapMining).swap(msg.sender, input, output, amountOut); 399 | } 400 | (uint amount0Out, uint amount1Out) = input == token0 ? (uint(0), amountOut) : (amountOut, uint(0)); 401 | address to = i < path.length - 2 ? pairFor(output, path[i + 2]) : _to; 402 | IMdexPair(pairFor(input, output)).swap( 403 | amount0Out, amount1Out, to, new bytes(0) 404 | ); 405 | } 406 | } 407 | 408 | function swapExactTokensForTokens( 409 | uint amountIn, 410 | uint amountOutMin, 411 | address[] calldata path, 412 | address to, 413 | uint deadline 414 | ) external virtual override ensure(deadline) returns (uint[] memory amounts) { 415 | amounts = IMdexFactory(factory).getAmountsOut(amountIn, path); 416 | require(amounts[amounts.length - 1] >= amountOutMin, 'MdexRouter: INSUFFICIENT_OUTPUT_AMOUNT'); 417 | TransferHelper.safeTransferFrom( 418 | path[0], msg.sender, pairFor(path[0], path[1]), amounts[0] 419 | ); 420 | _swap(amounts, path, to); 421 | } 422 | 423 | function swapTokensForExactTokens( 424 | uint amountOut, 425 | uint amountInMax, 426 | address[] calldata path, 427 | address to, 428 | uint deadline 429 | ) external virtual override ensure(deadline) returns (uint[] memory amounts) { 430 | amounts = IMdexFactory(factory).getAmountsIn(amountOut, path); 431 | require(amounts[0] <= amountInMax, 'MdexRouter: EXCESSIVE_INPUT_AMOUNT'); 432 | TransferHelper.safeTransferFrom( 433 | path[0], msg.sender, pairFor(path[0], path[1]), amounts[0] 434 | ); 435 | _swap(amounts, path, to); 436 | } 437 | 438 | function swapExactETHForTokens(uint amountOutMin, address[] calldata path, address to, uint deadline) 439 | external 440 | virtual 441 | override 442 | payable 443 | ensure(deadline) 444 | returns (uint[] memory amounts) 445 | { 446 | require(path[0] == WHT, 'MdexRouter: INVALID_PATH'); 447 | amounts = IMdexFactory(factory).getAmountsOut(msg.value, path); 448 | require(amounts[amounts.length - 1] >= amountOutMin, 'MdexRouter: INSUFFICIENT_OUTPUT_AMOUNT'); 449 | IWHT(WHT).deposit{value : amounts[0]}(); 450 | assert(IWHT(WHT).transfer(pairFor(path[0], path[1]), amounts[0])); 451 | _swap(amounts, path, to); 452 | } 453 | 454 | function swapTokensForExactETH(uint amountOut, uint amountInMax, address[] calldata path, address to, uint deadline) 455 | external 456 | virtual 457 | override 458 | ensure(deadline) 459 | returns (uint[] memory amounts) 460 | { 461 | require(path[path.length - 1] == WHT, 'MdexRouter: INVALID_PATH'); 462 | amounts = IMdexFactory(factory).getAmountsIn(amountOut, path); 463 | require(amounts[0] <= amountInMax, 'MdexRouter: EXCESSIVE_INPUT_AMOUNT'); 464 | TransferHelper.safeTransferFrom( 465 | path[0], msg.sender, pairFor(path[0], path[1]), amounts[0] 466 | ); 467 | _swap(amounts, path, address(this)); 468 | IWHT(WHT).withdraw(amounts[amounts.length - 1]); 469 | TransferHelper.safeTransferETH(to, amounts[amounts.length - 1]); 470 | } 471 | 472 | function swapExactTokensForETH(uint amountIn, uint amountOutMin, address[] calldata path, address to, uint deadline) 473 | external 474 | virtual 475 | override 476 | ensure(deadline) 477 | returns (uint[] memory amounts) 478 | { 479 | require(path[path.length - 1] == WHT, 'MdexRouter: INVALID_PATH'); 480 | amounts = IMdexFactory(factory).getAmountsOut(amountIn, path); 481 | require(amounts[amounts.length - 1] >= amountOutMin, 'MdexRouter: INSUFFICIENT_OUTPUT_AMOUNT'); 482 | TransferHelper.safeTransferFrom( 483 | path[0], msg.sender, pairFor(path[0], path[1]), amounts[0] 484 | ); 485 | _swap(amounts, path, address(this)); 486 | IWHT(WHT).withdraw(amounts[amounts.length - 1]); 487 | TransferHelper.safeTransferETH(to, amounts[amounts.length - 1]); 488 | } 489 | 490 | function swapETHForExactTokens(uint amountOut, address[] calldata path, address to, uint deadline) 491 | external 492 | virtual 493 | override 494 | payable 495 | ensure(deadline) 496 | returns (uint[] memory amounts) 497 | { 498 | require(path[0] == WHT, 'MdexRouter: INVALID_PATH'); 499 | amounts = IMdexFactory(factory).getAmountsIn(amountOut, path); 500 | require(amounts[0] <= msg.value, 'MdexRouter: EXCESSIVE_INPUT_AMOUNT'); 501 | IWHT(WHT).deposit{value : amounts[0]}(); 502 | assert(IWHT(WHT).transfer(pairFor(path[0], path[1]), amounts[0])); 503 | _swap(amounts, path, to); 504 | // refund dust eth, if any 505 | if (msg.value > amounts[0]) TransferHelper.safeTransferETH(msg.sender, msg.value - amounts[0]); 506 | } 507 | 508 | // **** SWAP (supporting fee-on-transfer tokens) **** 509 | // requires the initial amount to have already been sent to the first pair 510 | function _swapSupportingFeeOnTransferTokens(address[] memory path, address _to) internal virtual { 511 | for (uint i; i < path.length - 1; i++) { 512 | (address input, address output) = (path[i], path[i + 1]); 513 | (address token0,) = IMdexFactory(factory).sortTokens(input, output); 514 | IMdexPair pair = IMdexPair(pairFor(input, output)); 515 | uint amountInput; 516 | uint amountOutput; 517 | {// scope to avoid stack too deep errors 518 | (uint reserve0, uint reserve1,) = pair.getReserves(); 519 | (uint reserveInput, uint reserveOutput) = input == token0 ? (reserve0, reserve1) : (reserve1, reserve0); 520 | amountInput = IERC20(input).balanceOf(address(pair)).sub(reserveInput); 521 | amountOutput = IMdexFactory(factory).getAmountOut(amountInput, reserveInput, reserveOutput); 522 | } 523 | if (swapMining != address(0)) { 524 | ISwapMining(swapMining).swap(msg.sender, input, output, amountOutput); 525 | } 526 | (uint amount0Out, uint amount1Out) = input == token0 ? (uint(0), amountOutput) : (amountOutput, uint(0)); 527 | address to = i < path.length - 2 ? pairFor(output, path[i + 2]) : _to; 528 | pair.swap(amount0Out, amount1Out, to, new bytes(0)); 529 | } 530 | } 531 | 532 | function swapExactTokensForTokensSupportingFeeOnTransferTokens( 533 | uint amountIn, 534 | uint amountOutMin, 535 | address[] calldata path, 536 | address to, 537 | uint deadline 538 | ) external virtual override ensure(deadline) { 539 | TransferHelper.safeTransferFrom( 540 | path[0], msg.sender, pairFor(path[0], path[1]), amountIn 541 | ); 542 | uint balanceBefore = IERC20(path[path.length - 1]).balanceOf(to); 543 | _swapSupportingFeeOnTransferTokens(path, to); 544 | require( 545 | IERC20(path[path.length - 1]).balanceOf(to).sub(balanceBefore) >= amountOutMin, 546 | 'MdexRouter: INSUFFICIENT_OUTPUT_AMOUNT' 547 | ); 548 | } 549 | 550 | function swapExactETHForTokensSupportingFeeOnTransferTokens( 551 | uint amountOutMin, 552 | address[] calldata path, 553 | address to, 554 | uint deadline 555 | ) 556 | external 557 | virtual 558 | override 559 | payable 560 | ensure(deadline) 561 | { 562 | require(path[0] == WHT, 'MdexRouter: INVALID_PATH'); 563 | uint amountIn = msg.value; 564 | IWHT(WHT).deposit{value : amountIn}(); 565 | assert(IWHT(WHT).transfer(pairFor(path[0], path[1]), amountIn)); 566 | uint balanceBefore = IERC20(path[path.length - 1]).balanceOf(to); 567 | _swapSupportingFeeOnTransferTokens(path, to); 568 | require( 569 | IERC20(path[path.length - 1]).balanceOf(to).sub(balanceBefore) >= amountOutMin, 570 | 'MdexRouter: INSUFFICIENT_OUTPUT_AMOUNT' 571 | ); 572 | } 573 | 574 | function swapExactTokensForETHSupportingFeeOnTransferTokens( 575 | uint amountIn, 576 | uint amountOutMin, 577 | address[] calldata path, 578 | address to, 579 | uint deadline 580 | ) 581 | external 582 | virtual 583 | override 584 | ensure(deadline) 585 | { 586 | require(path[path.length - 1] == WHT, 'MdexRouter: INVALID_PATH'); 587 | TransferHelper.safeTransferFrom( 588 | path[0], msg.sender, pairFor(path[0], path[1]), amountIn 589 | ); 590 | _swapSupportingFeeOnTransferTokens(path, address(this)); 591 | uint amountOut = IERC20(WHT).balanceOf(address(this)); 592 | require(amountOut >= amountOutMin, 'MdexRouter: INSUFFICIENT_OUTPUT_AMOUNT'); 593 | IWHT(WHT).withdraw(amountOut); 594 | TransferHelper.safeTransferETH(to, amountOut); 595 | } 596 | 597 | // **** LIBRARY FUNCTIONS **** 598 | function quote(uint256 amountA, uint256 reserveA, uint256 reserveB) public view override returns (uint256 amountB) { 599 | return IMdexFactory(factory).quote(amountA, reserveA, reserveB); 600 | } 601 | 602 | function getAmountOut(uint256 amountIn, uint256 reserveIn, uint256 reserveOut) public view override returns (uint256 amountOut){ 603 | return IMdexFactory(factory).getAmountOut(amountIn, reserveIn, reserveOut); 604 | } 605 | 606 | function getAmountIn(uint256 amountOut, uint256 reserveIn, uint256 reserveOut) public view override returns (uint256 amountIn){ 607 | return IMdexFactory(factory).getAmountIn(amountOut, reserveIn, reserveOut); 608 | } 609 | 610 | function getAmountsOut(uint256 amountIn, address[] memory path) public view override returns (uint256[] memory amounts){ 611 | return IMdexFactory(factory).getAmountsOut(amountIn, path); 612 | } 613 | 614 | function getAmountsIn(uint256 amountOut, address[] memory path) public view override returns (uint256[] memory amounts){ 615 | return IMdexFactory(factory).getAmountsIn(amountOut, path); 616 | } 617 | 618 | } 619 | 620 | 621 | // helper methods for interacting with ERC20 tokens and sending ETH that do not consistently return true/false 622 | library TransferHelper { 623 | function safeApprove(address token, address to, uint value) internal { 624 | // bytes4(keccak256(bytes('approve(address,uint256)'))); 625 | (bool success, bytes memory data) = token.call(abi.encodeWithSelector(0x095ea7b3, to, value)); 626 | require(success && (data.length == 0 || abi.decode(data, (bool))), 'TransferHelper: APPROVE_FAILED'); 627 | } 628 | 629 | function safeTransfer(address token, address to, uint value) internal { 630 | // bytes4(keccak256(bytes('transfer(address,uint256)'))); 631 | (bool success, bytes memory data) = token.call(abi.encodeWithSelector(0xa9059cbb, to, value)); 632 | require(success && (data.length == 0 || abi.decode(data, (bool))), 'TransferHelper: TRANSFER_FAILED'); 633 | } 634 | 635 | function safeTransferFrom(address token, address from, address to, uint value) internal { 636 | // bytes4(keccak256(bytes('transferFrom(address,address,uint256)'))); 637 | (bool success, bytes memory data) = token.call(abi.encodeWithSelector(0x23b872dd, from, to, value)); 638 | require(success && (data.length == 0 || abi.decode(data, (bool))), 'TransferHelper: TRANSFER_FROM_FAILED'); 639 | } 640 | 641 | function safeTransferETH(address to, uint value) internal { 642 | (bool success,) = to.call{value : value}(new bytes(0)); 643 | require(success, 'TransferHelper: ETH_TRANSFER_FAILED'); 644 | } 645 | } 646 | --------------------------------------------------------------------------------