├── README.md ├── contracts ├── BTCParam.sol ├── BTCParamV2.sol ├── LpStaking.sol ├── Migrations.sol ├── MockERC20.sol ├── POWERC20.sol ├── POWToken.sol ├── POWTokenProxy.sol ├── Pausable.sol ├── ReentrancyGuard.sol ├── Staking.sol ├── TokenDistribute.sol ├── interfaces │ ├── IBTCParam.sol │ ├── IERC20Detail.sol │ ├── ILpStaking.sol │ ├── IPOWERC20.sol │ ├── IPOWToken.sol │ ├── IStaking.sol │ └── IUniswapV2ERC20.sol └── uniswapv2 │ ├── FixedPoint.sol │ ├── IUniswapV2Pair.sol │ └── UniswapV2OracleLibrary.sol ├── migrations └── 1_initial_migration.js ├── package.json ├── test └── .gitkeep └── truffle-config.js /README.md: -------------------------------------------------------------------------------- 1 | # MARS Project 2 | 3 | https://mars.poolin.fi. 4 | 5 | Mars Project combined standardized Bitcoin hashrate product and liquidity mining together, packing POW mining into an on-chain protocol. 6 | It provides a consistent Bitcoin mining output which could be acutely calculated in the very beginning, offering an edge for users on mining stability. 7 | No more complicated variables on daily mining. On the other hand, MARS token would be distributed to users in this project. 8 | Daily liquidity providing (LP) reward, shortened the distance of DeFi to Bitcoin community. 9 | 10 | The protocol consists of pBTC35A tokens and MARS token. Each pBTC35A token represents 1TH/s hashrate with pre-determined power ratio, mining rigs would be in Poolin Superhashrate’s custody during life cycle. 11 | While net profit on wBTC would be distributed per block. In the first batch, Poolin Superhashrate would provide 50,000 pBTC35A (approximately 50PH/s) tokens for Bitcoin (output with wBTC) mining in this protocol and lock up more than 50PH/s machines phisically. 12 | Two ways to obtain pBTC35A tokens, inhouse store or Uniswap. ETH and other POW mining tokens coming soon but no firm schedule yet. 13 | 14 | ## Deployed Contracts 15 | 16 | - BTCParamV2 - https://etherscan.io/address/0x260f6bAB7680019d2447Bf62e9dbBc80dD94b897 17 | - pBTC35A - https://etherscan.io/address/0xA8b12Cc90AbF65191532a12bb5394A714A46d358 18 | - TokenDistribute - https://etherscan.io/address/0x52Fe73fa78D4b85437A33a7DcbdA16aADa07E7B7 19 | - Staking - https://etherscan.io/address/0x5CBaDe4D03Ea436F792E9f939E70908524949efD 20 | - LpStaking - https://etherscan.io/address/0xaE26170200ec3aE66B8AfAa87f2FA49C1E0A02B9 21 | 22 | - MARS - https://etherscan.io/address/0x66c0dded8433c9ea86c8cf91237b14e10b4d70b7 23 | - USDTMARSLPTOKENPool - https://etherscan.io/address/0x9b9c2D202F9a0ed9Be9DaF9Df50CC0e327A809dd 24 | 25 | ## License 26 | 27 | MIT License -------------------------------------------------------------------------------- /contracts/BTCParam.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >=0.5.0; 2 | 3 | import "@openzeppelin/contracts/math/SafeMath.sol"; 4 | import './interfaces/IPOWToken.sol'; 5 | 6 | contract BTCParam { 7 | using SafeMath for uint256; 8 | 9 | bool internal initialized; 10 | address public owner; 11 | address public paramSetter; 12 | 13 | uint256 public btcBlockRewardInWei; 14 | uint256 public btcNetDiff; 15 | uint256 public btcPrice; 16 | uint256 public btcTxFeeRewardPerTPerSecInWei; 17 | 18 | address[] public paramListeners; 19 | 20 | function initialize(address newOwner, address _paramSetter, uint256 _btcNetDiff, uint256 _btcBlockRewardInWei, uint256 _btcPrice) public { 21 | require(!initialized, "already initialized"); 22 | require(newOwner != address(0), "new owner is the zero address"); 23 | initialized = true; 24 | owner = newOwner; 25 | paramSetter= _paramSetter; 26 | btcPrice = _btcPrice; 27 | btcBlockRewardInWei = _btcBlockRewardInWei; 28 | btcNetDiff = _btcNetDiff; 29 | } 30 | 31 | function transferOwnership(address newOwner) external onlyOwner { 32 | require(newOwner != address(0), "new owner is the zero address"); 33 | emit OwnershipTransferred(owner, newOwner); 34 | owner = newOwner; 35 | } 36 | 37 | function setParamSetter(address _paramSetter) external onlyOwner { 38 | require(_paramSetter != address(0), "param setter is the zero address"); 39 | emit ParamSetterChanged(paramSetter, _paramSetter); 40 | paramSetter = _paramSetter; 41 | } 42 | 43 | function setBtcNetDiff(uint256 _btcNetDiff) external onlyParamSetter { 44 | btcNetDiff = _btcNetDiff; 45 | notifyListeners(); 46 | } 47 | 48 | function setBtcBlockReward(uint256 _btcBlockRewardInWei) external onlyParamSetter { 49 | btcBlockRewardInWei = _btcBlockRewardInWei; 50 | notifyListeners(); 51 | } 52 | 53 | function setBtcPrice(uint256 _btcPrice) external onlyParamSetter { 54 | btcPrice = _btcPrice; 55 | notifyListeners(); 56 | } 57 | 58 | function setBtcTxFeeRewardRate(uint256 _btcTxFeeRewardPerTPerSecInWei) external onlyParamSetter { 59 | btcTxFeeRewardPerTPerSecInWei = _btcTxFeeRewardPerTPerSecInWei; 60 | notifyListeners(); 61 | } 62 | 63 | function addListener(address _listener) external onlyParamSetter { 64 | for (uint i=0; i=0.5.0; 2 | 3 | import "@openzeppelin/contracts/math/SafeMath.sol"; 4 | import './interfaces/IPOWToken.sol'; 5 | import "./uniswapv2/UniswapV2OracleLibrary.sol"; 6 | 7 | contract BTCParamV2 { 8 | using SafeMath for uint256; 9 | 10 | bool internal initialized; 11 | address public owner; 12 | address public paramSetter; 13 | 14 | uint256 public btcBlockRewardInWei; 15 | uint256 public btcNetDiff; 16 | uint256 public btcTxFeeRewardPerTPerSecInWei; 17 | 18 | address public uniPairAddress; 19 | bool public usePrice0; 20 | uint32 public lastPriceUpdateTime; 21 | uint256 public lastCumulativePrice; 22 | uint256 public lastAveragePrice; 23 | 24 | address[] public paramListeners; 25 | 26 | function initialize(address newOwner, address _paramSetter, uint256 _btcNetDiff, uint256 _btcBlockRewardInWei, address _uniPairAddress, bool _usePrice0) public { 27 | require(!initialized, "already initialized"); 28 | require(newOwner != address(0), "new owner is the zero address"); 29 | initialized = true; 30 | owner = newOwner; 31 | paramSetter= _paramSetter; 32 | btcBlockRewardInWei = _btcBlockRewardInWei; 33 | btcNetDiff = _btcNetDiff; 34 | 35 | uniPairAddress = _uniPairAddress; 36 | usePrice0 = _usePrice0; 37 | (uint256 price0Cumulative, uint256 price1Cumulative, uint32 currentBlockTimestamp) = 38 | UniswapV2OracleLibrary.currentCumulativePrices(_uniPairAddress); 39 | 40 | lastPriceUpdateTime = currentBlockTimestamp; 41 | lastCumulativePrice = _usePrice0?price0Cumulative:price1Cumulative; 42 | } 43 | 44 | function transferOwnership(address newOwner) external onlyOwner { 45 | require(newOwner != address(0), "new owner is the zero address"); 46 | emit OwnershipTransferred(owner, newOwner); 47 | owner = newOwner; 48 | } 49 | 50 | function setParamSetter(address _paramSetter) external onlyOwner { 51 | require(_paramSetter != address(0), "param setter is the zero address"); 52 | emit ParamSetterChanged(paramSetter, _paramSetter); 53 | paramSetter = _paramSetter; 54 | } 55 | 56 | function setBtcNetDiff(uint256 _btcNetDiff) external onlyParamSetter { 57 | btcNetDiff = _btcNetDiff; 58 | notifyListeners(); 59 | } 60 | 61 | function setBtcBlockReward(uint256 _btcBlockRewardInWei) external onlyParamSetter { 62 | btcBlockRewardInWei = _btcBlockRewardInWei; 63 | notifyListeners(); 64 | } 65 | 66 | function updateBtcPrice() external onlyParamSetter { 67 | _updateBtcPrice(); 68 | notifyListeners(); 69 | } 70 | 71 | function _updateBtcPrice() internal { 72 | (uint256 price0Cumulative, uint256 price1Cumulative, uint32 currentBlockTimestamp) = 73 | UniswapV2OracleLibrary.currentCumulativePrices(uniPairAddress); 74 | uint256 currentPrice = usePrice0?price0Cumulative:price1Cumulative; 75 | 76 | uint256 timeElapsed = currentBlockTimestamp - lastPriceUpdateTime; // overflow is desired 77 | if (timeElapsed > 0) { 78 | lastAveragePrice = currentPrice.sub(lastCumulativePrice).div(timeElapsed); 79 | lastPriceUpdateTime = currentBlockTimestamp; 80 | lastCumulativePrice = currentPrice; 81 | } 82 | } 83 | 84 | function setBtcTxFeeRewardRate(uint256 _btcTxFeeRewardPerTPerSecInWei) external onlyParamSetter { 85 | btcTxFeeRewardPerTPerSecInWei = _btcTxFeeRewardPerTPerSecInWei; 86 | notifyListeners(); 87 | } 88 | 89 | function setBtcTxFeeRewardRateAndUpdateBtcPrice(uint256 _btcTxFeeRewardPerTPerSecInWei) external onlyParamSetter{ 90 | btcTxFeeRewardPerTPerSecInWei = _btcTxFeeRewardPerTPerSecInWei; 91 | _updateBtcPrice(); 92 | notifyListeners(); 93 | } 94 | 95 | function addListener(address _listener) external onlyParamSetter { 96 | for (uint i=0; i=0.5.0; 2 | 3 | import '@openzeppelin/contracts/token/ERC20/IERC20.sol'; 4 | import '@openzeppelin/contracts/token/ERC20/SafeERC20.sol'; 5 | import "@openzeppelin/contracts/math/Math.sol"; 6 | import "@openzeppelin/contracts/math/SafeMath.sol"; 7 | import "./ReentrancyGuard.sol"; 8 | import './interfaces/IPOWToken.sol'; 9 | import './interfaces/IUniswapV2ERC20.sol'; 10 | import './interfaces/IStaking.sol'; 11 | 12 | contract LpStaking is ReentrancyGuard{ 13 | using SafeMath for uint256; 14 | using SafeERC20 for IERC20; 15 | 16 | bool internal initialized; 17 | address public owner; 18 | address public stakingPool; 19 | bool public emergencyStop; 20 | address public hashRateToken; 21 | address public stakingLpToken; 22 | 23 | uint256 public incomeLastUpdateTime; 24 | uint256 public incomePerTokenStored; 25 | mapping(address => uint256) public userIncomePerTokenPaid; 26 | mapping(address => uint256) public incomes; 27 | mapping(address => uint256) public accumulatedIncomes; 28 | 29 | uint256 public rewardLastUpdateTime; 30 | uint256 public rewardPerTokenStored; 31 | mapping(address => uint256) public userRewardPerTokenPaid; 32 | mapping(address => uint256) public rewards; 33 | 34 | mapping(address => uint256) public balances; 35 | uint256 public totalSupply; 36 | 37 | function initialize(address newOwner, address _hashRateToken, address _stakingPool, address _stakingLpToken) public { 38 | require(!initialized, "already initialized"); 39 | require(newOwner != address(0), "new owner is the zero address"); 40 | super.initialize(); 41 | initialized = true; 42 | owner = newOwner; 43 | stakingLpToken = _stakingLpToken; 44 | hashRateToken = _hashRateToken; 45 | stakingPool = _stakingPool; 46 | } 47 | 48 | function transferOwnership(address newOwner) external onlyOwner { 49 | require(newOwner != address(0), "new owner is the zero address"); 50 | emit OwnershipTransferred(owner, newOwner); 51 | owner = newOwner; 52 | } 53 | 54 | function setEmergencyStop(bool _emergencyStop) external onlyOwner { 55 | emergencyStop = _emergencyStop; 56 | } 57 | 58 | function calculateLpStakingIncomeRate(uint256 _incomeRate) internal view returns(uint256) { 59 | if (totalSupply == 0 || _incomeRate == 0) { 60 | //special case. 61 | return 0; 62 | } 63 | uint256 _totolSupply = IERC20(hashRateToken).totalSupply(); 64 | uint256 stakingSupply = IStaking(stakingPool).totalSupply(); 65 | return _incomeRate.mul(_totolSupply.sub(stakingSupply)).div(totalSupply); 66 | } 67 | 68 | function weiToSatoshi(uint256 amount) public pure returns (uint256) { 69 | return amount.div(10**10); 70 | } 71 | 72 | function stakeWithPermit(uint256 amount, uint deadline, uint8 v, bytes32 r, bytes32 s) external nonReentrant updateIncome(msg.sender) updateReward(msg.sender) { 73 | require(amount > 0, "Cannot stake 0"); 74 | 75 | balances[msg.sender] = balances[msg.sender].add(amount); 76 | totalSupply = totalSupply.add(amount); 77 | 78 | // permit 79 | IUniswapV2ERC20(stakingLpToken).permit(msg.sender, address(this), amount, deadline, v, r, s); 80 | IERC20(stakingLpToken).safeTransferFrom(msg.sender, address(this), amount); 81 | emit Staked(msg.sender, amount); 82 | } 83 | 84 | function stake(uint256 amount) external nonReentrant updateIncome(msg.sender) updateReward(msg.sender) { 85 | require(amount > 0, "Cannot stake 0"); 86 | 87 | balances[msg.sender] = balances[msg.sender].add(amount); 88 | totalSupply = totalSupply.add(amount); 89 | IERC20(stakingLpToken).safeTransferFrom(msg.sender, address(this), amount); 90 | emit Staked(msg.sender, amount); 91 | } 92 | 93 | function withdraw(uint256 amount) public nonReentrant updateIncome(msg.sender) updateReward(msg.sender) { 94 | require(amount > 0, "Cannot withdraw 0"); 95 | 96 | totalSupply = totalSupply.sub(amount); 97 | balances[msg.sender] = balances[msg.sender].sub(amount); 98 | IERC20(stakingLpToken).safeTransfer(msg.sender, amount); 99 | emit Withdrawn(msg.sender, amount); 100 | } 101 | 102 | function exit() external { 103 | withdraw(balances[msg.sender]); 104 | getIncome(); 105 | getReward(); 106 | } 107 | 108 | function lpIncomeRateChanged() external updateIncome(address(0)) { 109 | 110 | } 111 | 112 | function getCurIncomeRate() public view returns (uint256) { 113 | uint startMiningTime = IPOWToken(hashRateToken).startMiningTime(); 114 | //not start mining yet. 115 | if (block.timestamp <= startMiningTime) { 116 | return 0; 117 | } 118 | 119 | uint256 incomeRate = IPOWToken(hashRateToken).incomeRate(); 120 | incomeRate = calculateLpStakingIncomeRate(incomeRate); 121 | return incomeRate; 122 | } 123 | 124 | function incomePerToken() public view returns (uint256) { 125 | uint startMiningTime = IPOWToken(hashRateToken).startMiningTime(); 126 | //not start mining yet. 127 | if (block.timestamp <= startMiningTime) { 128 | return 0; 129 | } 130 | 131 | uint256 _incomeLastUpdateTime = incomeLastUpdateTime; 132 | if (_incomeLastUpdateTime == 0) { 133 | _incomeLastUpdateTime = startMiningTime; 134 | } 135 | 136 | uint256 incomeRate = getCurIncomeRate(); 137 | uint256 normalIncome = block.timestamp.sub(_incomeLastUpdateTime).mul(incomeRate); 138 | return incomePerTokenStored.add(normalIncome); 139 | } 140 | 141 | function incomeEarned(address account) external view returns (uint256) { 142 | uint256 income = _incomeEarned(account); 143 | return weiToSatoshi(income); 144 | } 145 | 146 | function _incomeEarned(address account) internal view returns (uint256) { 147 | return balances[account].mul(incomePerToken().sub(userIncomePerTokenPaid[account])).div(1e18).add(incomes[account]); 148 | } 149 | 150 | function getUserAccumulatedWithdrawIncome(address account) public view returns (uint256) { 151 | uint256 amount = accumulatedIncomes[account]; 152 | return weiToSatoshi(amount); 153 | } 154 | 155 | function getIncome() public nonReentrant updateIncome(msg.sender) nonEmergencyStop { 156 | uint256 income = incomes[msg.sender]; 157 | if (income > 0) { 158 | accumulatedIncomes[msg.sender] = accumulatedIncomes[msg.sender].add(income); 159 | incomes[msg.sender] = 0; 160 | uint256 amount = weiToSatoshi(income); 161 | IPOWToken(hashRateToken).claimIncome(msg.sender, amount); 162 | emit IncomePaid(msg.sender, income); 163 | } 164 | } 165 | 166 | function lastTimeRewardApplicable() public view returns (uint256) { 167 | uint256 periodFinish = IPOWToken(hashRateToken).rewardPeriodFinish(); 168 | return Math.min(block.timestamp, periodFinish); 169 | } 170 | 171 | function lpRewardRateChanged() external updateReward(address(0)) { 172 | rewardLastUpdateTime = block.timestamp; 173 | } 174 | 175 | function rewardPerToken() public view returns (uint256) { 176 | if (totalSupply == 0) { 177 | return rewardPerTokenStored; 178 | } 179 | uint256 rewardRate = IPOWToken(hashRateToken).lpStakingRewardRate(); 180 | return 181 | rewardPerTokenStored.add( 182 | lastTimeRewardApplicable().sub(rewardLastUpdateTime).mul(rewardRate).mul(1e18).div(totalSupply) 183 | ); 184 | } 185 | 186 | function rewardEarned(address account) public view returns (uint256) { 187 | return balances[account].mul(rewardPerToken().sub(userRewardPerTokenPaid[account])).div(1e18).add(rewards[account]); 188 | } 189 | 190 | function getReward() public nonReentrant updateReward(msg.sender) nonEmergencyStop { 191 | uint256 reward = rewards[msg.sender]; 192 | if (reward > 0) { 193 | rewards[msg.sender] = 0; 194 | IPOWToken(hashRateToken).claimReward(msg.sender, reward); 195 | emit RewardPaid(msg.sender, reward); 196 | } 197 | } 198 | 199 | function inCaseTokensGetStuck(address _token, uint256 _amount) external onlyOwner { 200 | require(_token != address(stakingLpToken), 'stakingToken cannot transfer.'); 201 | IERC20(_token).safeTransfer(msg.sender, _amount); 202 | } 203 | 204 | /* ========== MODIFIERS ========== */ 205 | 206 | modifier updateIncome(address account) { 207 | uint startMiningTime = IPOWToken(hashRateToken).startMiningTime(); 208 | if (block.timestamp > startMiningTime) { 209 | incomePerTokenStored = incomePerToken(); 210 | incomeLastUpdateTime = block.timestamp; 211 | if (account != address(0)) { 212 | incomes[account] = _incomeEarned(account); 213 | userIncomePerTokenPaid[account] = incomePerTokenStored; 214 | } 215 | } 216 | _; 217 | } 218 | 219 | modifier updateReward(address account) { 220 | rewardPerTokenStored = rewardPerToken(); 221 | rewardLastUpdateTime = lastTimeRewardApplicable(); 222 | if (account != address(0)) { 223 | rewards[account] = rewardEarned(account); 224 | userRewardPerTokenPaid[account] = rewardPerTokenStored; 225 | } 226 | _; 227 | } 228 | 229 | modifier nonEmergencyStop() { 230 | require(emergencyStop == false, "emergency stop"); 231 | _; 232 | } 233 | 234 | modifier onlyOwner() { 235 | require(msg.sender == owner, "!owner"); 236 | _; 237 | } 238 | 239 | /* ========== EVENTS ========== */ 240 | 241 | event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); 242 | event ParamSetterChanged(address indexed previousSetter, address indexed newSetter); 243 | event Staked(address indexed user, uint256 amount); 244 | event Withdrawn(address indexed user, uint256 amount); 245 | event IncomePaid(address indexed user, uint256 income); 246 | event RewardPaid(address indexed user, uint256 reward); 247 | } -------------------------------------------------------------------------------- /contracts/Migrations.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.4.22 <0.8.0; 3 | 4 | contract Migrations { 5 | address public owner = msg.sender; 6 | uint public last_completed_migration; 7 | 8 | modifier restricted() { 9 | require( 10 | msg.sender == owner, 11 | "This function is restricted to the contract's owner" 12 | ); 13 | _; 14 | } 15 | 16 | function setCompleted(uint completed) public restricted { 17 | last_completed_migration = completed; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /contracts/MockERC20.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >=0.5.0; 2 | 3 | import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; 4 | import "@openzeppelin/contracts/token/ERC20/ERC20Detailed.sol"; 5 | 6 | 7 | contract MockERC20 is ERC20, ERC20Detailed { 8 | constructor( 9 | string memory name, 10 | string memory symbol, 11 | uint8 decimals, 12 | uint256 supply 13 | ) public ERC20Detailed(name, symbol, decimals) { 14 | _mint(msg.sender, supply); 15 | } 16 | } -------------------------------------------------------------------------------- /contracts/POWERC20.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >=0.5.0; 2 | 3 | import "@openzeppelin/contracts/math/SafeMath.sol"; 4 | import './Pausable.sol'; 5 | 6 | contract POWERC20 is Pausable{ 7 | using SafeMath for uint; 8 | 9 | string public name; 10 | string public symbol; 11 | uint8 public constant decimals = 18; 12 | uint public totalSupply; 13 | mapping(address => uint) public balanceOf; 14 | mapping(address => mapping(address => uint)) public allowance; 15 | 16 | bytes32 public DOMAIN_SEPARATOR; 17 | // keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)"); 18 | bytes32 public constant PERMIT_TYPEHASH = 0x6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9; 19 | mapping(address => uint) public nonces; 20 | 21 | event Approval(address indexed owner, address indexed spender, uint value); 22 | event Transfer(address indexed from, address indexed to, uint value); 23 | 24 | function initializeToken(string memory tokenName, string memory tokenSymbol) internal { 25 | name = tokenName; 26 | symbol = tokenSymbol; 27 | 28 | uint chainId; 29 | assembly { 30 | chainId := chainid 31 | } 32 | DOMAIN_SEPARATOR = keccak256( 33 | abi.encode( 34 | keccak256('EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)'), 35 | keccak256(bytes(name)), 36 | keccak256(bytes('1')), 37 | chainId, 38 | address(this) 39 | ) 40 | ); 41 | } 42 | 43 | function _mint(address to, uint value) internal { 44 | totalSupply = totalSupply.add(value); 45 | balanceOf[to] = balanceOf[to].add(value); 46 | emit Transfer(address(0), to, value); 47 | } 48 | 49 | function _burn(address from, uint value) internal { 50 | balanceOf[from] = balanceOf[from].sub(value); 51 | totalSupply = totalSupply.sub(value); 52 | emit Transfer(from, address(0), value); 53 | } 54 | 55 | function _approve(address owner, address spender, uint value) private { 56 | allowance[owner][spender] = value; 57 | emit Approval(owner, spender, value); 58 | } 59 | 60 | function _transfer(address from, address to, uint value) private { 61 | balanceOf[from] = balanceOf[from].sub(value); 62 | balanceOf[to] = balanceOf[to].add(value); 63 | emit Transfer(from, to, value); 64 | } 65 | 66 | function approve(address spender, uint value) external returns (bool) { 67 | _approve(msg.sender, spender, value); 68 | return true; 69 | } 70 | 71 | function transfer(address to, uint value) external whenNotPaused returns (bool) { 72 | _transfer(msg.sender, to, value); 73 | return true; 74 | } 75 | 76 | function transferFrom(address from, address to, uint value) whenNotPaused external returns (bool) { 77 | if (allowance[from][msg.sender] != uint(-1)) { 78 | allowance[from][msg.sender] = allowance[from][msg.sender].sub(value); 79 | } 80 | _transfer(from, to, value); 81 | return true; 82 | } 83 | 84 | function permit(address owner, address spender, uint value, uint deadline, uint8 v, bytes32 r, bytes32 s) external { 85 | require(deadline >= block.timestamp, 'HashRateERC20: EXPIRED'); 86 | bytes32 digest = keccak256( 87 | abi.encodePacked( 88 | '\x19\x01', 89 | DOMAIN_SEPARATOR, 90 | keccak256(abi.encode(PERMIT_TYPEHASH, owner, spender, value, nonces[owner]++, deadline)) 91 | ) 92 | ); 93 | address recoveredAddress = ecrecover(digest, v, r, s); 94 | require(recoveredAddress != address(0) && recoveredAddress == owner, 'HashRateERC20: INVALID_SIGNATURE'); 95 | _approve(owner, spender, value); 96 | } 97 | } -------------------------------------------------------------------------------- /contracts/POWToken.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >=0.5.0; 2 | 3 | import '@openzeppelin/contracts/token/ERC20/IERC20.sol'; 4 | import '@openzeppelin/contracts/token/ERC20/SafeERC20.sol'; 5 | import "@openzeppelin/contracts/math/SafeMath.sol"; 6 | import './interfaces/IStaking.sol'; 7 | import './interfaces/IBTCParam.sol'; 8 | import './interfaces/ILpStaking.sol'; 9 | import './POWERC20.sol'; 10 | 11 | contract POWToken is POWERC20 { 12 | using SafeMath for uint256; 13 | using SafeERC20 for IERC20; 14 | 15 | bool internal initialized; 16 | address public owner; 17 | address public paramSetter; 18 | address public minter; 19 | address public stakingPool; 20 | address public lpStakingPool; 21 | address public btcParam; 22 | 23 | uint256 public elecPowerPerTHSec; 24 | uint256 public startMiningTime; 25 | 26 | uint256 public electricCharge; 27 | uint256 public minerPoolFeeNumerator; 28 | uint256 public depreciationNumerator; 29 | uint256 public workingRateNumerator; 30 | uint256 public workingHashRate; 31 | uint256 public totalHashRate; 32 | uint256 public workerNumLastUpdateTime; 33 | 34 | IERC20 public incomeToken; 35 | uint256 public incomeRate; 36 | IERC20 public rewardsToken; 37 | uint256 public rewardRate; 38 | uint256 public rewardsDuration; 39 | uint256 public rewardPeriodFinish; 40 | uint256 public stakingRewardRatio; 41 | 42 | function initialize(string memory name, string memory symbol, address newOwner, address _paramSetter, address _stakingPool, address _lpStakingPool, address _minter, address _btcParam, address _incomeToken, address _rewardsToken, uint256 _elecPowerPerTHSec, uint256 _startMiningTime, uint256 _electricCharge, uint256 _minerPoolFeeNumerator, uint256 _totalHashRate) public { 43 | require(!initialized, "Token already initialized"); 44 | require(newOwner != address(0), "new owner is the zero address"); 45 | require(_paramSetter != address(0), "_paramSetter is the zero address"); 46 | require(_startMiningTime > block.timestamp, "nonlegal startMiningTime."); 47 | require(_minerPoolFeeNumerator < 1000000, "nonlegal minerPoolFeeNumerator."); 48 | 49 | initialized = true; 50 | initializeToken(name, symbol); 51 | 52 | owner = newOwner; 53 | paramSetter = _paramSetter; 54 | stakingPool = _stakingPool; 55 | lpStakingPool = _lpStakingPool; 56 | minter = _minter; 57 | btcParam = _btcParam; 58 | incomeToken = IERC20(_incomeToken); 59 | rewardsToken = IERC20(_rewardsToken); 60 | elecPowerPerTHSec = _elecPowerPerTHSec; 61 | startMiningTime = _startMiningTime; 62 | electricCharge = _electricCharge; 63 | minerPoolFeeNumerator = _minerPoolFeeNumerator; 64 | totalHashRate = _totalHashRate; 65 | 66 | rewardsDuration = 30 days; 67 | stakingRewardRatio = 20; 68 | depreciationNumerator = 1000000; 69 | workingHashRate = _totalHashRate; 70 | workerNumLastUpdateTime = startMiningTime; 71 | updateIncomeRate(); 72 | } 73 | 74 | function setStakingRewardRatio(uint256 _stakingRewardRatio) external onlyOwner { 75 | require(_stakingRewardRatio <= 100, "illegal _stakingRewardRatio"); 76 | 77 | updateStakingPoolReward(); 78 | updateLpStakingPoolReward(); 79 | stakingRewardRatio = _stakingRewardRatio; 80 | } 81 | 82 | function transferOwnership(address newOwner) external onlyOwner { 83 | require(newOwner != address(0), "new owner is the zero address"); 84 | emit OwnershipTransferred(owner, newOwner); 85 | owner = newOwner; 86 | } 87 | 88 | function setParamSetter(address _paramSetter) external onlyOwner { 89 | require(_paramSetter != address(0), "param setter is the zero address"); 90 | emit ParamSetterChanged(paramSetter, _paramSetter); 91 | paramSetter = _paramSetter; 92 | } 93 | 94 | function pause() onlyOwner external { 95 | _pause(); 96 | } 97 | 98 | function unpause() onlyOwner external { 99 | _unpause(); 100 | } 101 | 102 | function remainingAmount() public view returns(uint256) { 103 | return totalHashRate.mul(1e18).sub(totalSupply); 104 | } 105 | 106 | function mint(address to, uint value) external whenNotPaused { 107 | require(msg.sender == minter, "!minter"); 108 | require(value <= remainingAmount(), "not sufficient supply."); 109 | _mint(to, value); 110 | updateLpStakingPoolIncome(); 111 | } 112 | 113 | function addHashRate(uint256 hashRate) external onlyParamSetter { 114 | require(hashRate > 0, "hashRate cannot be 0"); 115 | 116 | // should keep current workingRate and incomeRate unchanged. 117 | totalHashRate = totalHashRate.add(hashRate.mul(totalHashRate).div(workingHashRate)); 118 | workingHashRate = workingHashRate.add(hashRate); 119 | } 120 | 121 | function setBtcParam(address _btcParam) external onlyParamSetter { 122 | require(btcParam != _btcParam, "same btcParam."); 123 | btcParam = _btcParam; 124 | updateIncomeRate(); 125 | } 126 | 127 | function setStartMiningTime(uint256 _startMiningTime) external onlyParamSetter { 128 | require(startMiningTime != _startMiningTime, "same startMiningTime."); 129 | require(startMiningTime > block.timestamp, "already start mining."); 130 | require(_startMiningTime > block.timestamp, "nonlegal startMiningTime."); 131 | startMiningTime = _startMiningTime; 132 | workerNumLastUpdateTime = _startMiningTime; 133 | } 134 | 135 | function setElectricCharge(uint256 _electricCharge) external onlyParamSetter { 136 | require(electricCharge != _electricCharge, "same electricCharge."); 137 | electricCharge = _electricCharge; 138 | updateIncomeRate(); 139 | } 140 | 141 | function setMinerPoolFeeNumerator(uint256 _minerPoolFeeNumerator) external onlyParamSetter { 142 | require(minerPoolFeeNumerator != _minerPoolFeeNumerator, "same minerPoolFee."); 143 | require(_minerPoolFeeNumerator < 1000000, "nonlegal minerPoolFee."); 144 | minerPoolFeeNumerator = _minerPoolFeeNumerator; 145 | updateIncomeRate(); 146 | } 147 | 148 | function setDepreciationNumerator(uint256 _depreciationNumerator) external onlyParamSetter { 149 | require(depreciationNumerator != _depreciationNumerator, "same depreciationNumerator."); 150 | require(_depreciationNumerator <= 1000000, "nonlegal depreciation."); 151 | depreciationNumerator = _depreciationNumerator; 152 | updateIncomeRate(); 153 | } 154 | 155 | function setWorkingHashRate(uint256 _workingHashRate) external onlyParamSetter { 156 | require(workingHashRate != _workingHashRate, "same workingHashRate."); 157 | //require(totalHashRate >= _workingHashRate, "param workingHashRate not legal."); 158 | 159 | if (block.timestamp > startMiningTime) { 160 | workingRateNumerator = getHistoryWorkingRate(); 161 | workerNumLastUpdateTime = block.timestamp; 162 | } 163 | 164 | workingHashRate = _workingHashRate; 165 | updateIncomeRate(); 166 | } 167 | 168 | function getHistoryWorkingRate() public view returns (uint256) { 169 | if (block.timestamp > startMiningTime) { 170 | uint256 time_interval = block.timestamp.sub(workerNumLastUpdateTime); 171 | uint256 totalRate = workerNumLastUpdateTime.sub(startMiningTime).mul(workingRateNumerator).add(time_interval.mul(getCurWorkingRate())); 172 | uint256 totalTime = block.timestamp.sub(startMiningTime); 173 | 174 | return totalRate.div(totalTime); 175 | } 176 | 177 | return 0; 178 | } 179 | 180 | function getCurWorkingRate() public view returns (uint256) { 181 | return 1000000 * workingHashRate / totalHashRate; 182 | } 183 | 184 | function getPowerConsumptionBTCInWeiPerSec() public view returns(uint256){ 185 | uint256 btcPrice = IBTCParam(btcParam).btcPrice(); 186 | if (btcPrice != 0) { 187 | uint256 Base = 1e18; 188 | uint256 elecPowerPerTHSecAmplifier = 1000; 189 | uint256 powerConsumptionPerHour = elecPowerPerTHSec.mul(Base).div(elecPowerPerTHSecAmplifier).div(1000); 190 | uint256 powerConsumptionBTCInWeiPerHour = powerConsumptionPerHour.mul(electricCharge).div(1000000).div(btcPrice); 191 | return powerConsumptionBTCInWeiPerHour.div(3600); 192 | } 193 | return 0; 194 | } 195 | 196 | function getIncomeBTCInWeiPerSec() public view returns(uint256){ 197 | uint256 paramDenominator = 1000000; 198 | uint256 afterMinerPoolFee = 0; 199 | { 200 | uint256 btcIncomePerTPerSecInWei = IBTCParam(btcParam).btcIncomePerTPerSecInWei(); 201 | afterMinerPoolFee = btcIncomePerTPerSecInWei.mul(paramDenominator.sub(minerPoolFeeNumerator)).div(paramDenominator); 202 | } 203 | 204 | uint256 afterDepreciation = 0; 205 | { 206 | afterDepreciation = afterMinerPoolFee.mul(depreciationNumerator).div(paramDenominator); 207 | } 208 | 209 | return afterDepreciation; 210 | } 211 | 212 | function updateIncomeRate() public { 213 | //not start mining yet. 214 | if (block.timestamp > startMiningTime) { 215 | // update income first. 216 | updateStakingPoolIncome(); 217 | updateLpStakingPoolIncome(); 218 | } 219 | 220 | uint256 oldValue = incomeRate; 221 | 222 | //compute electric charge. 223 | uint256 powerConsumptionBTCInWeiPerSec = getPowerConsumptionBTCInWeiPerSec(); 224 | 225 | //compute btc income 226 | uint256 incomeBTCInWeiPerSec = getIncomeBTCInWeiPerSec(); 227 | 228 | if (incomeBTCInWeiPerSec > powerConsumptionBTCInWeiPerSec) { 229 | uint256 targetRate = incomeBTCInWeiPerSec.sub(powerConsumptionBTCInWeiPerSec); 230 | incomeRate = targetRate.mul(workingHashRate).div(totalHashRate); 231 | } 232 | //miner close down. 233 | else { 234 | incomeRate = 0; 235 | } 236 | 237 | emit IncomeRateChanged(oldValue, incomeRate); 238 | } 239 | 240 | function updateStakingPoolIncome() internal { 241 | if (stakingPool != address(0)) { 242 | IStaking(stakingPool).incomeRateChanged(); 243 | } 244 | } 245 | 246 | function updateLpStakingPoolIncome() internal { 247 | if (lpStakingPool != address(0)) { 248 | ILpStaking(lpStakingPool).lpIncomeRateChanged(); 249 | } 250 | } 251 | 252 | function updateStakingPoolReward() internal { 253 | if (stakingPool != address(0)) { 254 | IStaking(stakingPool).rewardRateChanged(); 255 | } 256 | } 257 | 258 | function updateLpStakingPoolReward() internal { 259 | if (lpStakingPool != address(0)) { 260 | ILpStaking(lpStakingPool).lpRewardRateChanged(); 261 | } 262 | } 263 | 264 | function stakingRewardRate() public view returns(uint256) { 265 | return rewardRate.mul(stakingRewardRatio).div(100); 266 | } 267 | function lpStakingRewardRate() external view returns(uint256) { 268 | uint256 _stakingRewardRate = stakingRewardRate(); 269 | return rewardRate.sub(_stakingRewardRate); 270 | } 271 | 272 | function notifyRewardAmount(uint256 reward) external onlyOwner { 273 | updateStakingPoolReward(); 274 | updateLpStakingPoolReward(); 275 | 276 | if (block.timestamp >= rewardPeriodFinish) { 277 | rewardRate = reward.div(rewardsDuration); 278 | } else { 279 | uint256 remaining = rewardPeriodFinish.sub(block.timestamp); 280 | uint256 leftover = remaining.mul(rewardRate); 281 | rewardRate = reward.add(leftover).div(rewardsDuration); 282 | } 283 | 284 | // Ensure the provided reward amount is not more than the balance in the contract. 285 | // This keeps the reward rate in the right range, preventing overflows due to 286 | // very high values of rewardRate in the earned and rewardsPerToken functions; 287 | // Reward + leftover must be less than 2^256 / 10^18 to avoid overflow. 288 | uint balance = rewardsToken.balanceOf(address(this)); 289 | require(rewardRate <= balance.div(rewardsDuration), "Provided reward too high"); 290 | 291 | rewardPeriodFinish = block.timestamp.add(rewardsDuration); 292 | emit RewardAdded(reward); 293 | } 294 | 295 | function claimIncome(address to, uint256 amount) external { 296 | require(to != address(0), "to is the zero address"); 297 | require(msg.sender == stakingPool || msg.sender == lpStakingPool, "No permissions"); 298 | incomeToken.safeTransfer(to, amount); 299 | } 300 | 301 | function claimReward(address to, uint256 amount) external { 302 | require(to != address(0), "to is the zero address"); 303 | require(msg.sender == stakingPool || msg.sender == lpStakingPool, "No permissions"); 304 | rewardsToken.safeTransfer(to, amount); 305 | } 306 | 307 | function inCaseTokensGetStuck(address _token, uint256 _amount) external onlyOwner { 308 | IERC20(_token).safeTransfer(msg.sender, _amount); 309 | } 310 | 311 | modifier onlyOwner() { 312 | require(msg.sender == owner, "!owner"); 313 | _; 314 | } 315 | 316 | modifier onlyParamSetter() { 317 | require(msg.sender == paramSetter, "!paramSetter"); 318 | _; 319 | } 320 | 321 | event IncomeRateChanged(uint256 oldValue, uint256 newValue); 322 | event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); 323 | event ParamSetterChanged(address indexed previousSetter, address indexed newSetter); 324 | event RewardAdded(uint256 reward); 325 | } -------------------------------------------------------------------------------- /contracts/POWTokenProxy.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >=0.5.0; 2 | 3 | import "zos-lib/contracts/upgradeability/AdminUpgradeabilityProxy.sol"; 4 | 5 | contract POWTokenProxy is AdminUpgradeabilityProxy { 6 | constructor(address _implementation, address _admin) public AdminUpgradeabilityProxy(_implementation, _admin, new bytes(0)) { 7 | } 8 | } -------------------------------------------------------------------------------- /contracts/Pausable.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.0; 2 | 3 | contract Pausable { 4 | 5 | event Paused(); 6 | 7 | event Unpaused(); 8 | 9 | bool private _paused; 10 | 11 | /** 12 | * @dev Returns true if the contract is paused, and false otherwise. 13 | */ 14 | function paused() public view returns (bool) { 15 | return _paused; 16 | } 17 | 18 | /** 19 | * @dev Modifier to make a function callable only when the contract is not paused. 20 | */ 21 | modifier whenNotPaused() { 22 | require(!_paused, "Pausable: paused"); 23 | _; 24 | } 25 | 26 | /** 27 | * @dev Modifier to make a function callable only when the contract is paused. 28 | */ 29 | modifier whenPaused() { 30 | require(_paused, "Pausable: not paused"); 31 | _; 32 | } 33 | 34 | /** 35 | * @dev Called by a pauser to pause, triggers stopped state. 36 | */ 37 | function _pause() internal { 38 | _paused = true; 39 | emit Paused(); 40 | } 41 | 42 | /** 43 | * @dev Called by a pauser to unpause, returns to normal state. 44 | */ 45 | function _unpause() internal { 46 | _paused = false; 47 | emit Unpaused(); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /contracts/ReentrancyGuard.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.0; 2 | 3 | /** 4 | * @dev Contract module that helps prevent reentrant calls to a function. 5 | * 6 | * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier 7 | * available, which can be applied to functions to make sure there are no nested 8 | * (reentrant) calls to them. 9 | * 10 | * Note that because there is a single `nonReentrant` guard, functions marked as 11 | * `nonReentrant` may not call one another. This can be worked around by making 12 | * those functions `private`, and then adding `external` `nonReentrant` entry 13 | * points to them. 14 | * 15 | * TIP: If you would like to learn more about reentrancy and alternative ways 16 | * to protect against it, check out our blog post 17 | * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul]. 18 | * 19 | * _Since v2.5.0:_ this module is now much more gas efficient, given net gas 20 | * metering changes introduced in the Istanbul hardfork. 21 | */ 22 | contract ReentrancyGuard { 23 | bool private _notEntered; 24 | 25 | function initialize() internal { 26 | // Storing an initial non-zero value makes deployment a bit more 27 | // expensive, but in exchange the refund on every call to nonReentrant 28 | // will be lower in amount. Since refunds are capped to a percetange of 29 | // the total transaction's gas, it is best to keep them low in cases 30 | // like this one, to increase the likelihood of the full refund coming 31 | // into effect. 32 | _notEntered = true; 33 | } 34 | 35 | /** 36 | * @dev Prevents a contract from calling itself, directly or indirectly. 37 | * Calling a `nonReentrant` function from another `nonReentrant` 38 | * function is not supported. It is possible to prevent this from happening 39 | * by making the `nonReentrant` function external, and make it call a 40 | * `private` function that does the actual work. 41 | */ 42 | modifier nonReentrant() { 43 | // On the first call to nonReentrant, _notEntered will be true 44 | require(_notEntered, "ReentrancyGuard: reentrant call"); 45 | 46 | // Any calls to nonReentrant after this point will fail 47 | _notEntered = false; 48 | 49 | _; 50 | 51 | // By storing the original value once again, a refund is triggered (see 52 | // https://eips.ethereum.org/EIPS/eip-2200) 53 | _notEntered = true; 54 | } 55 | } -------------------------------------------------------------------------------- /contracts/Staking.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >=0.5.0; 2 | 3 | import '@openzeppelin/contracts/token/ERC20/IERC20.sol'; 4 | import '@openzeppelin/contracts/token/ERC20/SafeERC20.sol'; 5 | import "@openzeppelin/contracts/math/Math.sol"; 6 | import "@openzeppelin/contracts/math/SafeMath.sol"; 7 | import "./ReentrancyGuard.sol"; 8 | import './interfaces/IPOWToken.sol'; 9 | import './interfaces/IPOWERC20.sol'; 10 | import './interfaces/ILpStaking.sol'; 11 | 12 | contract Staking is ReentrancyGuard{ 13 | using SafeMath for uint256; 14 | using SafeERC20 for IERC20; 15 | 16 | bool internal initialized; 17 | address public owner; 18 | address public lpStaking; 19 | bool public emergencyStop; 20 | address public hashRateToken; 21 | 22 | uint256 public incomeLastUpdateTime; 23 | uint256 public incomePerTokenStored; 24 | mapping(address => uint256) public userIncomePerTokenPaid; 25 | mapping(address => uint256) public incomes; 26 | mapping(address => uint256) public accumulatedIncomes; 27 | 28 | uint256 public rewardLastUpdateTime; 29 | uint256 public rewardPerTokenStored; 30 | mapping(address => uint256) public userRewardPerTokenPaid; 31 | mapping(address => uint256) public rewards; 32 | 33 | mapping(address => uint256) public balances; 34 | uint256 public totalSupply; 35 | 36 | function initialize(address newOwner, address _hashRateToken, address _lpStaking) public { 37 | require(!initialized, "already initialized"); 38 | require(newOwner != address(0), "new owner is the zero address"); 39 | super.initialize(); 40 | initialized = true; 41 | owner = newOwner; 42 | hashRateToken = _hashRateToken; 43 | lpStaking = _lpStaking; 44 | } 45 | 46 | function transferOwnership(address newOwner) external onlyOwner { 47 | require(newOwner != address(0), "new owner is the zero address"); 48 | emit OwnershipTransferred(owner, newOwner); 49 | owner = newOwner; 50 | } 51 | 52 | function setEmergencyStop(bool _emergencyStop) external onlyOwner { 53 | emergencyStop = _emergencyStop; 54 | } 55 | 56 | function weiToSatoshi(uint256 amount) public pure returns (uint256) { 57 | return amount.div(10**10); 58 | } 59 | 60 | function stakeWithPermit(uint256 amount, uint deadline, uint8 v, bytes32 r, bytes32 s) external nonReentrant updateIncome(msg.sender) updateReward(msg.sender) { 61 | require(amount > 0, "Cannot stake 0"); 62 | notifyLpStakingUpdateIncome(); 63 | 64 | balances[msg.sender] = balances[msg.sender].add(amount); 65 | totalSupply = totalSupply.add(amount); 66 | 67 | // permit 68 | IPOWERC20(hashRateToken).permit(msg.sender, address(this), amount, deadline, v, r, s); 69 | IERC20(hashRateToken).safeTransferFrom(msg.sender, address(this), amount); 70 | emit Staked(msg.sender, amount); 71 | } 72 | 73 | function stake(uint256 amount) external nonReentrant updateIncome(msg.sender) updateReward(msg.sender) { 74 | require(amount > 0, "Cannot stake 0"); 75 | notifyLpStakingUpdateIncome(); 76 | 77 | balances[msg.sender] = balances[msg.sender].add(amount); 78 | totalSupply = totalSupply.add(amount); 79 | IERC20(hashRateToken).safeTransferFrom(msg.sender, address(this), amount); 80 | emit Staked(msg.sender, amount); 81 | } 82 | 83 | function withdraw(uint256 amount) public nonReentrant updateIncome(msg.sender) updateReward(msg.sender) { 84 | require(amount > 0, "Cannot withdraw 0"); 85 | notifyLpStakingUpdateIncome(); 86 | 87 | totalSupply = totalSupply.sub(amount); 88 | balances[msg.sender] = balances[msg.sender].sub(amount); 89 | IERC20(hashRateToken).safeTransfer(msg.sender, amount); 90 | emit Withdrawn(msg.sender, amount); 91 | } 92 | 93 | function exit() external { 94 | withdraw(balances[msg.sender]); 95 | getIncome(); 96 | getReward(); 97 | } 98 | 99 | function incomeRateChanged() external updateIncome(address(0)) { 100 | 101 | } 102 | 103 | function getCurIncomeRate() public view returns (uint256) { 104 | uint startMiningTime = IPOWToken(hashRateToken).startMiningTime(); 105 | //not start mining yet. 106 | if (block.timestamp <= startMiningTime) { 107 | return 0; 108 | } 109 | 110 | uint256 incomeRate = IPOWToken(hashRateToken).incomeRate(); 111 | return incomeRate; 112 | } 113 | 114 | function incomePerToken() public view returns (uint256) { 115 | uint startMiningTime = IPOWToken(hashRateToken).startMiningTime(); 116 | //not start mining yet. 117 | if (block.timestamp <= startMiningTime) { 118 | return 0; 119 | } 120 | 121 | uint256 _incomeLastUpdateTime = incomeLastUpdateTime; 122 | if (_incomeLastUpdateTime == 0) { 123 | _incomeLastUpdateTime = startMiningTime; 124 | } 125 | 126 | uint256 incomeRate = getCurIncomeRate(); 127 | uint256 normalIncome = block.timestamp.sub(_incomeLastUpdateTime).mul(incomeRate); 128 | return incomePerTokenStored.add(normalIncome); 129 | } 130 | 131 | function incomeEarned(address account) external view returns (uint256) { 132 | uint256 income = _incomeEarned(account); 133 | return weiToSatoshi(income); 134 | } 135 | 136 | function _incomeEarned(address account) internal view returns (uint256) { 137 | return balances[account].mul(incomePerToken().sub(userIncomePerTokenPaid[account])).div(1e18).add(incomes[account]); 138 | } 139 | 140 | function getUserAccumulatedWithdrawIncome(address account) public view returns (uint256) { 141 | uint256 amount = accumulatedIncomes[account]; 142 | return weiToSatoshi(amount); 143 | } 144 | 145 | function getIncome() public nonReentrant updateIncome(msg.sender) nonEmergencyStop { 146 | uint256 income = incomes[msg.sender]; 147 | if (income > 0) { 148 | accumulatedIncomes[msg.sender] = accumulatedIncomes[msg.sender].add(income); 149 | incomes[msg.sender] = 0; 150 | uint256 amount = weiToSatoshi(income); 151 | IPOWToken(hashRateToken).claimIncome(msg.sender, amount); 152 | emit IncomePaid(msg.sender, income); 153 | } 154 | } 155 | 156 | function notifyLpStakingUpdateIncome() internal { 157 | if (lpStaking != address(0)) { 158 | ILpStaking(lpStaking).lpIncomeRateChanged(); 159 | } 160 | } 161 | 162 | function lastTimeRewardApplicable() public view returns (uint256) { 163 | uint256 periodFinish = IPOWToken(hashRateToken).rewardPeriodFinish(); 164 | return Math.min(block.timestamp, periodFinish); 165 | } 166 | 167 | function rewardRateChanged() external updateReward(address(0)) { 168 | rewardLastUpdateTime = block.timestamp; 169 | } 170 | 171 | function rewardPerToken() public view returns (uint256) { 172 | if (totalSupply == 0) { 173 | return rewardPerTokenStored; 174 | } 175 | uint256 rewardRate = IPOWToken(hashRateToken).stakingRewardRate(); 176 | return 177 | rewardPerTokenStored.add( 178 | lastTimeRewardApplicable().sub(rewardLastUpdateTime).mul(rewardRate).mul(1e18).div(totalSupply) 179 | ); 180 | } 181 | 182 | function rewardEarned(address account) public view returns (uint256) { 183 | return balances[account].mul(rewardPerToken().sub(userRewardPerTokenPaid[account])).div(1e18).add(rewards[account]); 184 | } 185 | 186 | function getReward() public nonReentrant updateReward(msg.sender) nonEmergencyStop { 187 | uint256 reward = rewards[msg.sender]; 188 | if (reward > 0) { 189 | rewards[msg.sender] = 0; 190 | IPOWToken(hashRateToken).claimReward(msg.sender, reward); 191 | emit RewardPaid(msg.sender, reward); 192 | } 193 | } 194 | 195 | function inCaseTokensGetStuck(address _token, uint256 _amount) external onlyOwner { 196 | require(_token != hashRateToken, 'hashRateToken cannot transfer.'); 197 | IERC20(_token).safeTransfer(msg.sender, _amount); 198 | } 199 | 200 | /* ========== MODIFIERS ========== */ 201 | 202 | modifier updateIncome(address account) { 203 | uint startMiningTime = IPOWToken(hashRateToken).startMiningTime(); 204 | if (block.timestamp > startMiningTime) { 205 | incomePerTokenStored = incomePerToken(); 206 | incomeLastUpdateTime = block.timestamp; 207 | if (account != address(0)) { 208 | incomes[account] = _incomeEarned(account); 209 | userIncomePerTokenPaid[account] = incomePerTokenStored; 210 | } 211 | } 212 | _; 213 | } 214 | 215 | modifier updateReward(address account) { 216 | rewardPerTokenStored = rewardPerToken(); 217 | rewardLastUpdateTime = lastTimeRewardApplicable(); 218 | if (account != address(0)) { 219 | rewards[account] = rewardEarned(account); 220 | userRewardPerTokenPaid[account] = rewardPerTokenStored; 221 | } 222 | _; 223 | } 224 | 225 | modifier nonEmergencyStop() { 226 | require(emergencyStop == false, "emergency stop"); 227 | _; 228 | } 229 | 230 | modifier onlyOwner() { 231 | require(msg.sender == owner, "!owner"); 232 | _; 233 | } 234 | 235 | /* ========== EVENTS ========== */ 236 | 237 | event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); 238 | event ParamSetterChanged(address indexed previousSetter, address indexed newSetter); 239 | event Staked(address indexed user, uint256 amount); 240 | event Withdrawn(address indexed user, uint256 amount); 241 | event IncomePaid(address indexed user, uint256 income); 242 | event RewardPaid(address indexed user, uint256 reward); 243 | } -------------------------------------------------------------------------------- /contracts/TokenDistribute.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >=0.5.0; 2 | 3 | import '@openzeppelin/contracts/token/ERC20/IERC20.sol'; 4 | import "@openzeppelin/contracts/math/SafeMath.sol"; 5 | import '@openzeppelin/contracts/token/ERC20/SafeERC20.sol'; 6 | import "./interfaces/IPOWToken.sol"; 7 | import "./interfaces/IERC20Detail.sol"; 8 | import "./ReentrancyGuard.sol"; 9 | 10 | contract TokenDistribute is ReentrancyGuard{ 11 | using SafeMath for uint256; 12 | using SafeERC20 for IERC20; 13 | 14 | bool internal initialized; 15 | address public owner; 16 | address public paramSetter; 17 | address public hashRateToken; 18 | address[] public exchangeTokens; 19 | uint256[] public exchangeRates; 20 | 21 | mapping (address => bool) public isWhiteListed; 22 | 23 | function initialize(address newOwner, address _paramSetter, address _hashRateToken) public { 24 | require(!initialized, "Token already initialized"); 25 | require(newOwner != address(0), "new owner is the zero address"); 26 | super.initialize(); 27 | initialized = true; 28 | 29 | owner = newOwner; 30 | paramSetter = _paramSetter; 31 | hashRateToken = _hashRateToken; 32 | } 33 | 34 | function transferOwnership(address newOwner) external onlyOwner { 35 | require(newOwner != address(0), "new owner is the zero address"); 36 | emit OwnershipTransferred(owner, newOwner); 37 | owner = newOwner; 38 | } 39 | 40 | function setParamSetter(address _paramSetter) external onlyOwner { 41 | require(_paramSetter != address(0), "param setter is the zero address"); 42 | emit ParamSetterChanged(paramSetter, _paramSetter); 43 | paramSetter = _paramSetter; 44 | } 45 | 46 | function addWhiteLists (address[] calldata _users) external onlyParamSetter { 47 | for (uint i=0; i<_users.length; i++){ 48 | address _user = _users[i]; 49 | _addWhiteList(_user); 50 | } 51 | } 52 | 53 | function addWhiteList (address _user) external onlyParamSetter { 54 | _addWhiteList(_user); 55 | } 56 | 57 | function _addWhiteList (address _user) internal { 58 | isWhiteListed[_user] = true; 59 | emit AddedWhiteList(_user); 60 | } 61 | 62 | function removeWhiteList (address _user) external onlyParamSetter { 63 | delete isWhiteListed[_user]; 64 | emit RemovedWhiteList(_user); 65 | } 66 | 67 | function getWhiteListStatus(address _user) public view returns (bool) { 68 | return isWhiteListed[_user]; 69 | } 70 | 71 | function addExchangeToken(address _exchangeToken, uint256 _exchangeRate) external onlyParamSetter { 72 | exchangeTokens.push(_exchangeToken); 73 | exchangeRates.push(_exchangeRate); 74 | } 75 | 76 | function updateExchangeRate(uint256 _tokenId, uint256 _exchangeRate) external onlyParamSetter checkTokenId(_tokenId) { 77 | exchangeRates[_tokenId] = _exchangeRate; 78 | } 79 | 80 | function remainingAmount() public view returns(uint256) { 81 | return IPOWToken(hashRateToken).remainingAmount(); 82 | } 83 | 84 | function exchange(uint256 _tokenId, uint256 amount, address to) checkTokenId(_tokenId) external nonReentrant { 85 | require(amount > 0, "Cannot exchange 0"); 86 | require(amount <= remainingAmount(), "not sufficient supply"); 87 | require(getWhiteListStatus(to), "to is not in whitelist"); 88 | 89 | uint256 exchangeRateAmplifier = 1000; 90 | uint256 hashRateTokenAmplifier; 91 | uint256 exchangeTokenAmplifier; 92 | { 93 | address exchangeToken = exchangeTokens[_tokenId]; 94 | uint256 hashRateTokenDecimal = IERC20Detail(hashRateToken).decimals(); 95 | uint256 exchangeTokenDecimal = IERC20Detail(exchangeToken).decimals(); 96 | hashRateTokenAmplifier = 10**hashRateTokenDecimal; 97 | exchangeTokenAmplifier = 10**exchangeTokenDecimal; 98 | } 99 | 100 | uint256 token_amount = amount.mul(exchangeRates[_tokenId]).mul(exchangeTokenAmplifier).div(hashRateTokenAmplifier).div(exchangeRateAmplifier); 101 | 102 | { 103 | address exchangeToken = exchangeTokens[_tokenId]; 104 | IERC20(exchangeToken).safeTransferFrom(msg.sender, address(this), token_amount); 105 | IPOWToken(hashRateToken).mint(to, amount); 106 | } 107 | 108 | emit Exchanged(msg.sender, amount, _tokenId, token_amount); 109 | } 110 | 111 | function ownerMint(uint256 amount)external onlyOwner { 112 | IPOWToken(hashRateToken).mint(owner, amount); 113 | } 114 | 115 | function ownerWithdraw(address _token, uint256 _amount) external onlyOwner { 116 | IERC20(_token).safeTransfer(owner, _amount); 117 | } 118 | 119 | modifier checkTokenId(uint256 _tokenId) { 120 | require(exchangeTokens[_tokenId] != address(0) && exchangeRates[_tokenId] != 0, "wrong _tokenId"); 121 | _; 122 | } 123 | 124 | modifier onlyOwner() { 125 | require(msg.sender == owner, "!owner"); 126 | _; 127 | } 128 | 129 | modifier onlyParamSetter() { 130 | require(msg.sender == paramSetter, "!param setter"); 131 | _; 132 | } 133 | 134 | event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); 135 | event ParamSetterChanged(address indexed previousSetter, address indexed newSetter); 136 | event Exchanged(address indexed user, uint256 amount, uint256 tokenId, uint256 token_amount); 137 | event AddedWhiteList(address _user); 138 | event RemovedWhiteList(address _user); 139 | } -------------------------------------------------------------------------------- /contracts/interfaces/IBTCParam.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >=0.5.0; 2 | 3 | interface IBTCParam { 4 | function btcPrice() external view returns (uint256); 5 | function btcIncomePerTPerSecInWei() external view returns(uint256); 6 | } -------------------------------------------------------------------------------- /contracts/interfaces/IERC20Detail.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >=0.5.0; 2 | 3 | interface IERC20Detail { 4 | function name() external pure returns (string memory); 5 | function symbol() external pure returns (string memory); 6 | function decimals() external pure returns (uint8); 7 | } -------------------------------------------------------------------------------- /contracts/interfaces/ILpStaking.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >=0.5.0; 2 | 3 | interface ILpStaking { 4 | function lpIncomeRateChanged() external; 5 | function lpRewardRateChanged() external; 6 | } -------------------------------------------------------------------------------- /contracts/interfaces/IPOWERC20.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >=0.5.0; 2 | 3 | interface IPOWERC20 { 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 | function symbol() external pure returns (string memory); 9 | function decimals() external pure returns (uint8); 10 | function totalSupply() external view returns (uint); 11 | function balanceOf(address owner) external view returns (uint); 12 | function allowance(address owner, address spender) external view returns (uint); 13 | 14 | function approve(address spender, uint value) external returns (bool); 15 | function transfer(address to, uint value) external returns (bool); 16 | function transferFrom(address from, address to, uint value) external returns (bool); 17 | 18 | function DOMAIN_SEPARATOR() external view returns (bytes32); 19 | function PERMIT_TYPEHASH() external pure returns (bytes32); 20 | function nonces(address owner) external view returns (uint); 21 | 22 | function permit(address owner, address spender, uint value, uint deadline, uint8 v, bytes32 r, bytes32 s) external; 23 | } -------------------------------------------------------------------------------- /contracts/interfaces/IPOWToken.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >=0.5.0; 2 | 3 | interface IPOWToken { 4 | function updateIncomeRate() external; 5 | function incomeToken() external view returns(uint256); 6 | function incomeRate() external view returns(uint256); 7 | function startMiningTime() external view returns (uint256); 8 | function mint(address to, uint value) external; 9 | function remainingAmount() external view returns(uint256); 10 | function rewardToken() external view returns(uint256); 11 | function stakingRewardRate() external view returns(uint256); 12 | function lpStakingRewardRate() external view returns(uint256); 13 | function rewardPeriodFinish() external view returns(uint256); 14 | function claimIncome(address to, uint256 amount) external; 15 | function claimReward(address to, uint256 amount) external; 16 | } -------------------------------------------------------------------------------- /contracts/interfaces/IStaking.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >=0.5.0; 2 | 3 | interface IStaking { 4 | function incomeRateChanged() external; 5 | function rewardRateChanged() external; 6 | function totalSupply() external view returns(uint256); 7 | } -------------------------------------------------------------------------------- /contracts/interfaces/IUniswapV2ERC20.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >=0.5.0; 2 | 3 | interface IUniswapV2ERC20 { 4 | function permit(address owner, address spender, uint value, uint deadline, uint8 v, bytes32 r, bytes32 s) external; 5 | } -------------------------------------------------------------------------------- /contracts/uniswapv2/FixedPoint.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >=0.4.0; 2 | 3 | // a library for handling binary fixed point numbers (https://en.wikipedia.org/wiki/Q_(number_format)) 4 | library FixedPoint { 5 | // range: [0, 2**112 - 1] 6 | // resolution: 1 / 2**112 7 | struct uq112x112 { 8 | uint224 _x; 9 | } 10 | 11 | // range: [0, 2**144 - 1] 12 | // resolution: 1 / 2**112 13 | struct uq144x112 { 14 | uint _x; 15 | } 16 | 17 | uint8 private constant RESOLUTION = 112; 18 | 19 | // encode a uint112 as a UQ112x112 20 | function encode(uint112 x) internal pure returns (uq112x112 memory) { 21 | return uq112x112(uint224(x) << RESOLUTION); 22 | } 23 | 24 | // encodes a uint144 as a UQ144x112 25 | function encode144(uint144 x) internal pure returns (uq144x112 memory) { 26 | return uq144x112(uint256(x) << RESOLUTION); 27 | } 28 | 29 | // divide a UQ112x112 by a uint112, returning a UQ112x112 30 | function div(uq112x112 memory self, uint112 x) internal pure returns (uq112x112 memory) { 31 | require(x != 0, 'FixedPoint: DIV_BY_ZERO'); 32 | return uq112x112(self._x / uint224(x)); 33 | } 34 | 35 | // multiply a UQ112x112 by a uint, returning a UQ144x112 36 | // reverts on overflow 37 | function mul(uq112x112 memory self, uint y) internal pure returns (uq144x112 memory) { 38 | uint z; 39 | require(y == 0 || (z = uint(self._x) * y) / y == uint(self._x), "FixedPoint: MULTIPLICATION_OVERFLOW"); 40 | return uq144x112(z); 41 | } 42 | 43 | // returns a UQ112x112 which represents the ratio of the numerator to the denominator 44 | // equivalent to encode(numerator).div(denominator) 45 | function fraction(uint112 numerator, uint112 denominator) internal pure returns (uq112x112 memory) { 46 | require(denominator > 0, "FixedPoint: DIV_BY_ZERO"); 47 | return uq112x112((uint224(numerator) << RESOLUTION) / denominator); 48 | } 49 | 50 | // decode a UQ112x112 into a uint112 by truncating after the radix point 51 | function decode(uq112x112 memory self) internal pure returns (uint112) { 52 | return uint112(self._x >> RESOLUTION); 53 | } 54 | 55 | // decode a UQ144x112 into a uint144 by truncating after the radix point 56 | function decode144(uq144x112 memory self) internal pure returns (uint144) { 57 | return uint144(self._x >> RESOLUTION); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /contracts/uniswapv2/IUniswapV2Pair.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >=0.5.0; 2 | 3 | interface IUniswapV2Pair { 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 | function symbol() external pure returns (string memory); 9 | function decimals() external pure returns (uint8); 10 | function totalSupply() external view returns (uint); 11 | function balanceOf(address owner) external view returns (uint); 12 | function allowance(address owner, address spender) external view returns (uint); 13 | 14 | function approve(address spender, uint value) external returns (bool); 15 | function transfer(address to, uint value) external returns (bool); 16 | function transferFrom(address from, address to, uint value) external returns (bool); 17 | 18 | function DOMAIN_SEPARATOR() external view returns (bytes32); 19 | function PERMIT_TYPEHASH() external pure returns (bytes32); 20 | function nonces(address owner) external view returns (uint); 21 | 22 | function permit(address owner, address spender, uint value, uint deadline, uint8 v, bytes32 r, bytes32 s) external; 23 | 24 | event Mint(address indexed sender, uint amount0, uint amount1); 25 | event Burn(address indexed sender, uint amount0, uint amount1, address indexed to); 26 | event Swap( 27 | address indexed sender, 28 | uint amount0In, 29 | uint amount1In, 30 | uint amount0Out, 31 | uint amount1Out, 32 | address indexed to 33 | ); 34 | event Sync(uint112 reserve0, uint112 reserve1); 35 | 36 | function MINIMUM_LIQUIDITY() external pure returns (uint); 37 | function factory() external view returns (address); 38 | function token0() external view returns (address); 39 | function token1() external view returns (address); 40 | function getReserves() external view returns (uint112 reserve0, uint112 reserve1, uint32 blockTimestampLast); 41 | function price0CumulativeLast() external view returns (uint); 42 | function price1CumulativeLast() external view returns (uint); 43 | function kLast() external view returns (uint); 44 | 45 | function mint(address to) external returns (uint liquidity); 46 | function burn(address to) external returns (uint amount0, uint amount1); 47 | function swap(uint amount0Out, uint amount1Out, address to, bytes calldata data) external; 48 | function skim(address to) external; 49 | function sync() external; 50 | 51 | function initialize(address, address) external; 52 | } -------------------------------------------------------------------------------- /contracts/uniswapv2/UniswapV2OracleLibrary.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >=0.5.0; 2 | 3 | import './IUniswapV2Pair.sol'; 4 | import './FixedPoint.sol'; 5 | 6 | // library with helper methods for oracles that are concerned with computing average prices 7 | library UniswapV2OracleLibrary { 8 | using FixedPoint for *; 9 | 10 | // helper function that returns the current block timestamp within the range of uint32, i.e. [0, 2**32 - 1] 11 | function currentBlockTimestamp() internal view returns (uint32) { 12 | return uint32(block.timestamp % 2 ** 32); 13 | } 14 | 15 | // produces the cumulative price using counterfactuals to save gas and avoid a call to sync. 16 | function currentCumulativePrices( 17 | address pair 18 | ) internal view returns (uint price0Cumulative, uint price1Cumulative, uint32 blockTimestamp) { 19 | blockTimestamp = currentBlockTimestamp(); 20 | price0Cumulative = IUniswapV2Pair(pair).price0CumulativeLast(); 21 | price1Cumulative = IUniswapV2Pair(pair).price1CumulativeLast(); 22 | 23 | // if time has elapsed since the last update on the pair, mock the accumulated price values 24 | (uint112 reserve0, uint112 reserve1, uint32 blockTimestampLast) = IUniswapV2Pair(pair).getReserves(); 25 | if (blockTimestampLast != blockTimestamp) { 26 | // subtraction overflow is desired 27 | uint32 timeElapsed = blockTimestamp - blockTimestampLast; 28 | // addition overflow is desired 29 | // counterfactual 30 | price0Cumulative += uint(FixedPoint.fraction(reserve1, reserve0)._x) * timeElapsed; 31 | // counterfactual 32 | price1Cumulative += uint(FixedPoint.fraction(reserve0, reserve1)._x) * timeElapsed; 33 | } 34 | } 35 | } -------------------------------------------------------------------------------- /migrations/1_initial_migration.js: -------------------------------------------------------------------------------- 1 | const Migrations = artifacts.require("Migrations"); 2 | 3 | module.exports = function (deployer) { 4 | deployer.deploy(Migrations); 5 | }; 6 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "POWToken", 3 | "version": "1.0.0", 4 | "main": "index.js", 5 | "license": "MIT", 6 | "dependencies": { 7 | "@openzeppelin/contracts": "^2.5.0", 8 | "@openzeppelin/test-helpers": "^0.5.6", 9 | "zos-lib": "^2.4.3" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /test/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MarsFi/POWToken/9a7cb362e7144b2d525031ddcb365aa74ab224db/test/.gitkeep -------------------------------------------------------------------------------- /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 | * truffleframework.com/docs/advanced/configuration 10 | * 11 | * To deploy via Infura you'll need a wallet provider (like @truffle/hdwallet-provider) 12 | * to sign your transactions before they're sent to a remote public node. Infura accounts 13 | * are available for free at: infura.io/register. 14 | * 15 | * You'll also need a mnemonic - the twelve word phrase the wallet uses to generate 16 | * public/private key pairs. If you're publishing your code to GitHub make sure you load this 17 | * phrase from a file you've .gitignored so it doesn't accidentally become public. 18 | * 19 | */ 20 | 21 | // const HDWalletProvider = require('@truffle/hdwallet-provider'); 22 | // const infuraKey = "fj4jll3k....."; 23 | // 24 | // const fs = require('fs'); 25 | // const mnemonic = fs.readFileSync(".secret").toString().trim(); 26 | 27 | module.exports = { 28 | /** 29 | * Networks define how you connect to your ethereum client and let you set the 30 | * defaults web3 uses to send transactions. If you don't specify one truffle 31 | * will spin up a development blockchain for you on port 9545 when you 32 | * run `develop` or `test`. You can ask a truffle command to use a specific 33 | * network from the command line, e.g 34 | * 35 | * $ truffle test --network 36 | */ 37 | 38 | networks: { 39 | // Useful for testing. The `development` name is special - truffle uses it by default 40 | // if it's defined here and no other network is specified at the command line. 41 | // You should run a client (like ganache-cli, geth or parity) in a separate terminal 42 | // tab if you use this network and you must also set the `host`, `port` and `network_id` 43 | // options below to some value. 44 | // 45 | // development: { 46 | // host: "127.0.0.1", // Localhost (default: none) 47 | // port: 8545, // Standard Ethereum port (default: none) 48 | // network_id: "*", // Any network (default: none) 49 | // }, 50 | // Another network with more advanced options... 51 | // advanced: { 52 | // port: 8777, // Custom port 53 | // network_id: 1342, // Custom network 54 | // gas: 8500000, // Gas sent with each transaction (default: ~6700000) 55 | // gasPrice: 20000000000, // 20 gwei (in wei) (default: 100 gwei) 56 | // from:
, // Account to send txs from (default: accounts[0]) 57 | // websockets: true // Enable EventEmitter interface for web3 (default: false) 58 | // }, 59 | // Useful for deploying to a public network. 60 | // NB: It's important to wrap the provider as a function. 61 | // ropsten: { 62 | // provider: () => new HDWalletProvider(mnemonic, `https://ropsten.infura.io/v3/YOUR-PROJECT-ID`), 63 | // network_id: 3, // Ropsten's id 64 | // gas: 5500000, // Ropsten has a lower block limit than mainnet 65 | // confirmations: 2, // # of confs to wait between deployments. (default: 0) 66 | // timeoutBlocks: 200, // # of blocks before a deployment times out (minimum/default: 50) 67 | // skipDryRun: true // Skip dry run before migrations? (default: false for public nets ) 68 | // }, 69 | // Useful for private networks 70 | // private: { 71 | // provider: () => new HDWalletProvider(mnemonic, `https://network.io`), 72 | // network_id: 2111, // This network is yours, in the cloud. 73 | // production: true // Treats this network as if it was a public net. (default: false) 74 | // } 75 | }, 76 | 77 | // Set default mocha options here, use special reporters etc. 78 | mocha: { 79 | // timeout: 100000 80 | }, 81 | 82 | // Configure your compilers 83 | compilers: { 84 | solc: { 85 | // version: "0.5.1", // Fetch exact version from solc-bin (default: truffle's version) 86 | // docker: true, // Use "0.5.1" you've installed locally with docker (default: false) 87 | // settings: { // See the solidity docs for advice about optimization and evmVersion 88 | // optimizer: { 89 | // enabled: false, 90 | // runs: 200 91 | // }, 92 | // evmVersion: "byzantium" 93 | // } 94 | }, 95 | }, 96 | }; 97 | --------------------------------------------------------------------------------