├── .gitignore ├── Procfile ├── README.md ├── contracts ├── BitPredict.sol ├── BitpredictFactory.sol ├── Ownable.sol ├── SafeMath.sol └── provableAPI.sol ├── docs └── dev.md ├── index.js ├── package.json ├── src └── web │ ├── css │ ├── index.css │ └── w3.css │ ├── icons │ ├── favicon.ico │ ├── icon-192.png │ ├── icon-512.png │ ├── icon.svg │ └── texture.png │ ├── index.html │ ├── js │ ├── BitEth.js │ ├── BitPredict.js │ ├── BitPredictFactory.js │ ├── abis.js │ └── index.js │ └── svelte │ ├── App.svelte │ ├── BetRow.svelte │ ├── BitPredictHelper.svelte │ ├── ContractCountdown.svelte │ ├── ContractFactory.svelte │ ├── ContractRow.svelte │ ├── ContractSubscribers.svelte │ ├── ContractWinners.svelte │ ├── ContractsFactory.svelte │ ├── ModalBetTransaction.svelte │ ├── ModalFactoryContract.svelte │ ├── ModalUserContract.svelte │ ├── NavigationBar.svelte │ ├── UserContractBet.svelte │ ├── UserContracts.svelte │ ├── Web3Warn.svelte │ └── WinnerRow.svelte └── webpack.config.js /.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules/ 2 | /public/build/ 3 | 4 | .DS_Store 5 | /.idea/*.gitignore 6 | /.idea 7 | /public 8 | /package-lock.json 9 | -------------------------------------------------------------------------------- /Procfile: -------------------------------------------------------------------------------- 1 | web: npm run start -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # BitPredict 2 | **Crypto Currency Betting Platform** 3 | ```bash 4 | Powered By Ethereum Smart Contracts! 5 | ``` 6 | ## What is BitPredict? 7 | It is about Crypto Price Betting. 8 | - Participants competes to predict upcoming crypto price. 9 | - The winner(s) are who predicted the nearest future price. 10 | - The Reward is the Contract Jackpot minus the beneficiary fees. 11 | 12 | ## Who can create the contract? 13 | BitPredict a Smart Contract Betting Factory. 14 | - The protocol is living on the blockchain 15 | - Anyone can freely interact 16 | - You can create your own flavor Betting Contract 17 | - Share the participation link with your friends/connections. 18 | 19 | ## BitPredict Contract? 20 | Creating the contract consists of 5 inputs: 21 | - **Contract Ticker:** the bet prediction target e.g. BTC-USD 22 | - **Ticket Price:** the participation price 23 | - **Closure Delay:** until when people can still participate 24 | - **Execution Delay:** the execution time, winner(s) pick and rewards 25 | - **Beneficiary Fees:** your % fees on the jackpot total 26 | 27 | ## How It Works? 28 | Let us Say your Created a **BitPredict** contract as follows: 29 | 30 | Input|Value 31 | -------|-------- 32 | Ticker|BTC-USD 33 | Price|0.2 ETH 34 | Closure|2 days 35 | Execution|3 days 36 | Beneficiary Fees| 5% 37 | 38 | Your beneficiary is your wallet address: 39 | ***0xd04d90aC0E87ad156A5De6Faaa5595f9b4E45386*** 40 | 41 | **So the happy flow is** 42 | 43 | 1. Within 2 days having 5 participants: 44 | 45 | Address|Prediction| 46 | -------|--------| 47 | 0x369fF36DeF150A8fB4C1623142aba6DBc26C0D39|9580| 48 | 0xFb8E981d0DF292382541cc18db7421AdE58A0cDe|7500| 49 | 0xF3A6EE2c5CA4450071E4Cba69075a313b00f2dC1|10125| 50 | 0xcD7f213f001E982E4d6244B4186809014fE116CE|10500| 51 | 0xe0742a310cBB73c6348B2d27dB4C4c8c22cf23E9|9100| 52 | 53 | 2. Jackpot is 5*0.2 = 1 ETH 54 | 3. After 3 days the bitcoin price is got from CoinBase api 55 | 4. Let us say the BTC-USD ticker is 10210 USD 56 | 5. The closest participant prediction is 10125 57 | 7. The beneficiary fees 5%: 0.05 ETH are deduced 58 | 6. The participant reward: 1.9 ETH is automatically distributed to its wallet address 0xF3A6EE2c5CA4450071E4Cba69075a313b00f2dC1 59 | 60 | ## ON-CHAIN Integration 61 | You can integrate our **BitPredictFactory** and create programmatically your **BitPredict** 62 | Here you can find BitPredict solidity interfaces, compile generate and use their ABI as well! 63 | e.g. 64 | ````solidity 65 | import 'IBitPredictFactory.sol'; 66 | 67 | contract Test { 68 | 69 | constructor() public { 70 | IBitPredictFactory factory = IBitPredict(0x4e8f1ccf050e59ec0939a34417ae9e175f862943); 71 | uint contractPriceWei = factory.contractPrice(); 72 | // ... 73 | } 74 | } 75 | ```` 76 | **IBitPredictFactory.sol:** 77 | ````solidity 78 | pragma solidity ^0.5.17; 79 | 80 | /// @dev use this interface to integrate with BitPredictFactory 81 | interface IBitPredictFactory { 82 | 83 | /// @dev returns the BitPredict contract price in Wei 84 | function contractPrice() external returns (uint); 85 | 86 | /// @dev returns the supported tickers e.g. ['BTC-USD', 'ETH-USD'] 87 | function tickersList() external view returns (bytes9[] memory); 88 | 89 | /// @dev event fired on BitPredict contract creation 90 | /// @notice you can watch this event on your pending transaction 91 | event PredictionContractCreated( 92 | address indexed creator, // beneficiary address 93 | address bitPredict, // BitPredict smarct contract address 94 | bytes9 ticker, // BitPredict Ticker e.g. BTC-USD 95 | uint weiTicket, // 2e17 WEI : 0.2 ETH ticket price 96 | uint closureDelay, // 2*24*3600 : 2 days closure delay 97 | uint executionDelay, // 3*24*3600 : 3 days execution delay 98 | uint fees, // 5 : beneficiary fees 5% 99 | uint created // creation time (now) in solidity 100 | ); 101 | 102 | /// @dev create your BitPredict Contract 103 | /// @notice payable send contractPrice in wei with the arguments 104 | function createPredictionContract ( 105 | bytes9 ticker, // BTC-USD 106 | uint weiTicket, // 2e17 WEI : 0.2 ETH ticket price 107 | uint closureDelay, // 2*24*3600 : 2 days closure delay 108 | uint executionDelay, // 3*24*3600 : 3 days execution delay 109 | uint fees // 5 : beneficiary fees 5% 110 | ) external payable; 111 | } 112 | ```` 113 | 114 | If you want to interact with your BitPredict Contract you can do it via the IBitPredict Interface: 115 | e.g. 116 | ````solidity 117 | import 'IBitPredict.sol'; 118 | 119 | contract Test { 120 | constructor() public { 121 | IBitPredict mContract = IBitPredict(0x70Ee5F70C3eECc4d951Ac87b7107fCcc33713469); 122 | uint subscriptionPriceWei = mContract.ticketPriceWei(); 123 | } 124 | } 125 | ```` 126 | **IBitPredict.sol:** 127 | ````solidity 128 | pragma solidity >= 0.5.0 < 0.6.0; 129 | 130 | 131 | interface IBitPredict { 132 | 133 | /// @dev returns (now) contract deployment time 134 | function created() external returns (uint); 135 | 136 | /// @dev subscription available until (created + closureDelay) 137 | function closureDelay() external returns (uint); 138 | 139 | /// @dev price calculation and winners pick at (created + executionDelay) 140 | function executionDelay() external returns (uint); 141 | 142 | /// @dev ticket price in wei to subscribe 143 | function ticketPriceWei() external returns (uint); 144 | 145 | /// @dev contract ticker e.g. 'BTC-USD' 146 | function ticker() external returns (bytes9); 147 | 148 | /// @dev contract status: 0 open, 1 closed, 2 executed, 3 reward distributed 149 | function status() external returns (uint); 150 | 151 | /// @dev return the count of subscribers and winners 152 | function counters() external view returns (uint subs, uint wins); 153 | 154 | /// @dev execution actual ticker price e.g. 10400 for 'BTC-USD' 155 | function winSolution() external returns (uint); 156 | 157 | /// @dev winner closest prediction e.g. 10350 for 'BTC-USD' 158 | function winGuess() external returns(uint); 159 | 160 | /// @dev winner reward in WEI 161 | function winReward() external returns (uint); 162 | 163 | /// @dev emitted on a new bet participant 164 | event PriceBet(address user, uint bet, uint time); 165 | 166 | /// @dev emitted on price execution 167 | event RoundExecuted(uint solution); 168 | 169 | /// @dev emitted foreach winner reward 170 | event WinnerPayout(address winner, uint solution, uint guess, uint reward); 171 | 172 | // accept ether subscription payments 173 | // only one time per address 174 | // msg.value = ticketPriceWei + expected future price format in wei 175 | // Example 176 | // 1. ticketPrice is 0.01 Ether 177 | // 2. Your future price prediction is 9588.25 178 | // 3. You send 0.1 + 0.0000000000958825 179 | // 4. The contract registers your prediction of 958825 180 | // 5. The contract pays you back get back 0.0000000000958825 Ether 181 | function() payable external; 182 | 183 | /// @dev rollback subscribers ether if the execution did not happen after 24 hours 184 | /// @notice only the beneficiary msg.sender can withdraw total amount same share equally with the subscribers 185 | function rollback() external; 186 | } 187 | ```` -------------------------------------------------------------------------------- /contracts/BitPredict.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >= 0.5.0 < 0.6.0; 2 | 3 | /**f.aro*/ 4 | 5 | import "SafeMath.sol"; 6 | 7 | contract BitPredict { 8 | 9 | using SafeMath for uint256; 10 | 11 | uint public created; 12 | uint public executionDelay; 13 | uint public closureDelay; 14 | uint public ticketPriceWei; 15 | 16 | bytes9 public ticker; 17 | string public resolver; 18 | 19 | uint public status = 0; 20 | 21 | address payable [] public subscribers; 22 | mapping(address => uint) public futures; 23 | 24 | uint public winSolution; 25 | uint public winGuess; 26 | uint public winReward; 27 | uint public winShare; 28 | 29 | address[] public winners; 30 | address payable beneficiary; 31 | 32 | event RoundClose(); 33 | 34 | event PriceBet(address user, uint bet, uint time); 35 | 36 | event RoundExecuted(uint solution); 37 | 38 | event WinnerPayout(address winner, uint solution, uint guess, uint reward); 39 | 40 | address payable factory; 41 | 42 | // deploy and schedule the contract 43 | // payable to cover provable oracle fees 44 | // After seconds no one can subscribe 45 | // After seconds contract gets the bitcoin price and announce the winner(s) 46 | // winPer is the percentage on total jackpot remaing beneficiary fees 47 | constructor(address payable creator, uint winPer, uint weiTicket, uint closeDelay, uint executeDelay, bytes9 pair, string memory apiResolver) public payable { 48 | require(closeDelay < executeDelay, 'timing errors'); 49 | require(winPer <= 100 && winPer > 0, 'profit error'); 50 | 51 | created = now; 52 | ticker = pair; 53 | resolver = apiResolver; 54 | beneficiary = creator; 55 | executionDelay = executeDelay; 56 | closureDelay = closeDelay; 57 | ticketPriceWei = weiTicket; 58 | winShare = winPer; 59 | 60 | factory = msg.sender; 61 | } 62 | 63 | // accept ether subscription payments 64 | // only one time per address 65 | // msg.value = ticketPriceWei + expected future price format in wei 66 | // Example 67 | // 1. ticketPrice is 0.01 Ether 68 | // 2. Your future price prediction is 9588.25 69 | // 3. You send 0.1 + 0.0000000000958825 70 | // 4. The contract registers your prediction of 958825 71 | // 5. The contract pays you back get back 0.0000000000958825 Ether 72 | function() payable external { 73 | require(status == 0, 'closed'); 74 | require(msg.value > ticketPriceWei && msg.value < ticketPriceWei + 10000000, 'invalid ticket price'); 75 | require(futures[msg.sender] == 0, 'already in'); 76 | uint future = msg.value - ticketPriceWei; 77 | futures[msg.sender] = future; 78 | subscribers.push(msg.sender); 79 | msg.sender.transfer(future); 80 | emit PriceBet(msg.sender, future, now); 81 | } 82 | 83 | // return the count of subscribers and winners 84 | function counters() public view returns (uint subs, uint wins) { 85 | return (subscribers.length, winners.length); 86 | } 87 | 88 | // return absolute value: | x - y | 89 | function priceDistance(uint x, uint y) private pure returns (uint) { 90 | return (x > y ? x.sub(y) : y.sub(x)); 91 | } 92 | 93 | // close subscription doors 94 | function closeSubscriptions() public { 95 | require(msg.sender == factory, 'only factory'); 96 | require(status == 0, 'wrong transition'); 97 | status = 1; 98 | // in case of one+ subscribers 99 | // lunch the next solution timer 100 | if(subscribers.length == 0) { 101 | status = 2; 102 | distributeRewards(); 103 | } 104 | emit RoundClose(); 105 | } 106 | 107 | // if the execution did not happen after 24 hours 108 | // the beneficiary can withdraw total amount same share 109 | // equally with the subscribers 110 | function rollback() public { 111 | 112 | // should be not distributed 113 | require(status < 3, 'already distributed'); 114 | 115 | // only the beneficiary can do it 116 | require(msg.sender == beneficiary, 'only beneficiary'); 117 | 118 | // at least 24H passed without execution 119 | uint unlockTime = created.add(executionDelay).add(24*3600); 120 | require(now >= unlockTime, 'still early'); 121 | 122 | // the balance is normally distributed 123 | uint perShareWei = address(this).balance.div(subscribers.length); 124 | for(uint i = 0; i < subscribers.length; i++) { 125 | subscribers[i].transfer(perShareWei); 126 | } 127 | } 128 | 129 | // set the solution and alarm the distribution 130 | // pay the factory and tell about the gas Limit 131 | function executeResult(uint solution) public returns (uint gasLimit) { 132 | require(msg.sender == factory, 'only factory'); 133 | require(status == 1, 'wrong transition'); 134 | status = 2; 135 | winSolution = solution; 136 | emit RoundExecuted(winSolution); 137 | 138 | // default provable 20 GWei per Gas and 200 000 GAS 139 | uint PROVABLE_GAS_PRICE = 20e9 wei; 140 | 141 | // estimation for the factory 142 | uint estGas = subscribers.length*7000 + 300000; 143 | uint estWei = estGas.mul(PROVABLE_GAS_PRICE); 144 | 145 | // check up the available cost 146 | if(address(this).balance >= estWei) { 147 | // defaulting extras back to factory 148 | factory.transfer((estGas.sub(200000)).mul(PROVABLE_GAS_PRICE)); 149 | return estGas; 150 | } else { 151 | // The factory covers DEFAULT PROVABLE GAS COST: 20GWEI * 200 000 GAS 152 | return 200000; 153 | } 154 | } 155 | 156 | // calculate winners and distribution 157 | function distributeRewards() public { 158 | require(status == 2, 'wrong transition'); 159 | require(msg.sender == beneficiary || msg.sender == factory, 'not authorized'); 160 | status = 3; 161 | if (subscribers.length > 0) { 162 | uint distance; 163 | uint minDistance = 10 ** 11; 164 | uint windex = 0; 165 | address payable[] memory wins = new address payable[](subscribers.length); 166 | uint i; 167 | // GetWinners 168 | for (i = 0; i < subscribers.length; i++) { 169 | distance = priceDistance(futures[subscribers[i]] * 10000, winSolution); 170 | if (distance < minDistance) { 171 | windex = 0; 172 | wins[windex] = subscribers[i]; 173 | minDistance = distance; 174 | winGuess = futures[subscribers[i]]; 175 | } else if (distance == minDistance) { 176 | wins[++windex] = subscribers[i]; 177 | } 178 | } 179 | // WinnerPayout 180 | windex++; 181 | 182 | // total balance adjust 183 | uint total = ticketPriceWei * subscribers.length; 184 | if(address(this).balance < total) 185 | total = address(this).balance; 186 | 187 | winReward = (total.mul(winShare)).div(windex.mul(100)); 188 | for (i = 0; i < windex; i++) { 189 | winners.push(wins[i]); 190 | wins[i].transfer(winReward); 191 | emit WinnerPayout(wins[i], winSolution, futures[wins[i]], winReward); 192 | } 193 | 194 | // BeneficiaryPayout 195 | uint ONE_HUNDRED = 100; 196 | beneficiary.transfer((total.mul(ONE_HUNDRED.sub(winShare))).div(100)); 197 | } 198 | 199 | // Factory mint back 200 | if(address(this).balance > 0) { 201 | factory.transfer(address(this).balance); 202 | } 203 | } 204 | } 205 | -------------------------------------------------------------------------------- /contracts/BitpredictFactory.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >= 0.5.0 < 0.6.0; 2 | 3 | /**f.aro*/ 4 | 5 | import "Ownable.sol"; 6 | import "provableAPI.sol"; 7 | import "BitPredict.sol"; 8 | import "SafeMath.sol"; 9 | 10 | contract BitPredictFactory is Ownable, usingProvable { 11 | 12 | using SafeMath for uint256; 13 | 14 | // default contractPrice in wei 15 | uint public contractPrice = 12e15 wei; 16 | 17 | // registered tickers 18 | bytes9[] public tickers; 19 | mapping(bytes9 => string) public tickersResolvers; 20 | 21 | // all bet contracts 22 | address[] public bitPredicts; 23 | 24 | // provable callbacks 25 | mapping(bytes32 => address payable) callbax; 26 | 27 | event PredictionContractCreated(address indexed creator, address bitPredict, bytes9 ticker, uint weiTicket, uint closureDelay, uint executionDelay, uint fees, uint created); 28 | 29 | constructor () public { 30 | // beneficiary owner 31 | owner = msg.sender; 32 | // initial supported tickers 33 | // bitcoin USD ticker 34 | tickers.push('BTC-USD'); 35 | tickersResolvers['BTC-USD'] = "json(https://api.pro.coinbase.com/products/BTC-USD/ticker).price"; 36 | // ethereum USD ticker 37 | tickers.push('ETH-USD'); 38 | tickersResolvers['ETH-USD'] = "json(https://api.pro.coinbase.com/products/ETH-USD/ticker).price"; 39 | } 40 | 41 | function createPredictionContract(bytes9 ticker, uint weiTicket, uint closureDelay, uint executionDelay, uint fees) public payable { 42 | 43 | // should cover provable 44 | require(msg.value >= contractPrice, 'low price'); 45 | require(address(this).balance > provable_getPrice("URL"), 'provable off'); 46 | 47 | // mandatory ticker is registered 48 | string memory resolver = tickersResolvers[ticker]; 49 | require(bytes(resolver).length > 0); 50 | 51 | // create and log the smart contract 52 | require(fees >= 0 && fees <= 100, 'fees error'); 53 | BitPredict bitPredict = new BitPredict(msg.sender, 100 - fees, weiTicket, closureDelay, executionDelay, ticker, resolver); 54 | bitPredicts.push(address(bitPredict)); 55 | emit PredictionContractCreated(msg.sender, address(bitPredict), ticker, weiTicket, closureDelay, executionDelay, fees, now); 56 | 57 | // provable covered in contractPrice 58 | bytes32 uid = provable_query(closureDelay, "URL", ""); 59 | callbax[uid] = address(bitPredict); 60 | } 61 | 62 | function counter() public view returns (uint) { 63 | return bitPredicts.length; 64 | } 65 | 66 | function tickersList() public view returns (bytes9[] memory) { 67 | return tickers; 68 | } 69 | 70 | function setContractPrice(uint priceWei) public onlyOwner { 71 | // provable defaulting at 200 000 gas 72 | // at default price or 20 GWei 73 | // => 2e5*20e9 = 4e15 74 | require(priceWei > 4e15, 'should cover provable'); 75 | contractPrice = priceWei; 76 | } 77 | 78 | function setTickerResolver(bytes9 ticker, string memory resolver) public onlyOwner { 79 | // existing ticker 80 | for (uint i = 0; i < tickers.length; i++) { 81 | if (tickers[i] == ticker) { 82 | tickersResolvers[ticker] = resolver; 83 | return; 84 | } 85 | } 86 | // new ticker 87 | tickers.push(ticker); 88 | tickersResolvers[ticker] = resolver; 89 | } 90 | 91 | function removeTicker(bytes9 ticker) public onlyOwner { 92 | // delete ticker 93 | // remove resolver 94 | for (uint i = 0; i < tickers.length; i++) { 95 | if (tickers[i] == ticker) { 96 | delete tickersResolvers[ticker]; 97 | for (; i < tickers.length - 1; i++) { 98 | tickers[i] = tickers[i + 1]; 99 | } 100 | tickers.length--; 101 | break; 102 | } 103 | } 104 | } 105 | 106 | // BitPredictFactory payable fallback 107 | function() payable external { 108 | 109 | } 110 | 111 | // BitPredictFactory fees withdraw 112 | function withdraw() public onlyOwner { 113 | owner.transfer(address(this).balance); 114 | } 115 | 116 | function __callback(bytes32 _myid, string memory _result, bytes memory _proof) public { 117 | // only provable callback 118 | require(msg.sender == provable_cbAddress()); 119 | 120 | address payable callback = callbax[_myid]; 121 | require(callback != address(0), 'invalid callback'); 122 | 123 | // get the predictor instance 124 | BitPredict predictor = BitPredict(callbax[_myid]); 125 | 126 | // get the predictor status 127 | uint status = predictor.status(); 128 | 129 | // when status is executed await the distribution 130 | if(status == 2) 131 | predictor.distributeRewards(); 132 | 133 | // when status is closed checkout the result 134 | else if(status == 1) { 135 | uint solution = parseInt(_result, 6); 136 | // get paid, check gas limit and schedule 137 | uint gasLimit = predictor.executeResult(solution); 138 | bytes32 uid = provable_query(1, "URL", "", gasLimit); 139 | // update the callback 140 | callbax[uid] = callback; 141 | } 142 | // when status is open close subscription => status = 1 143 | else if (status == 0) { 144 | predictor.closeSubscriptions(); 145 | // when no subscribers than status will switch to 3 146 | if (predictor.status() < 3) { 147 | uint delay = predictor.executionDelay().sub(predictor.closureDelay()); 148 | bytes32 uid = provable_query(delay, "URL", predictor.resolver()); 149 | callbax[uid] = callback; 150 | } 151 | } 152 | _proof; 153 | } 154 | 155 | } -------------------------------------------------------------------------------- /contracts/Ownable.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >= 0.5.0 < 0.6.0; 2 | 3 | contract Ownable { 4 | 5 | address payable public owner; 6 | 7 | event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); 8 | 9 | /** 10 | * @dev The Ownable constructor sets the original `owner` of the contract to the sender 11 | * account. 12 | */ 13 | constructor() public { 14 | owner = msg.sender; 15 | } 16 | 17 | /** 18 | * @dev Throws if called by any account other than the owner. 19 | */ 20 | modifier onlyOwner() { 21 | require(msg.sender == owner); 22 | _; 23 | } 24 | 25 | /** 26 | * @dev Allows the current owner to transfer control of the contract to a newOwner. 27 | * @param newOwner The address to transfer ownership to. 28 | */ 29 | function transferOwnership(address payable newOwner) public onlyOwner { 30 | require(newOwner != address(0)); 31 | emit OwnershipTransferred(owner, newOwner); 32 | owner = newOwner; 33 | } 34 | } -------------------------------------------------------------------------------- /contracts/SafeMath.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >= 0.5.0 < 0.6.0; 2 | 3 | library SafeMath { 4 | 5 | /** 6 | * @dev Multiplies two numbers, throws on overflow. 7 | */ 8 | function mul(uint256 a, uint256 b) internal pure returns (uint256) { 9 | if (a == 0) { 10 | return 0; 11 | } 12 | uint256 c = a * b; 13 | assert(c / a == b); 14 | return c; 15 | } 16 | 17 | /** 18 | * @dev Integer division of two numbers, truncating the quotient. 19 | */ 20 | function div(uint256 a, uint256 b) internal pure returns (uint256) { 21 | // assert(b > 0); // Solidity automatically throws when dividing by 0 22 | uint256 c = a / b; 23 | // assert(a == b * c + a % b); // There is no case in which this doesn't hold 24 | return c; 25 | } 26 | 27 | /** 28 | * @dev Substracts two numbers, throws on overflow (i.e. if subtrahend is greater than minuend). 29 | */ 30 | function sub(uint256 a, uint256 b) internal pure returns (uint256) { 31 | assert(b <= a); 32 | return a - b; 33 | } 34 | 35 | /** 36 | * @dev Adds two numbers, throws on overflow. 37 | */ 38 | function add(uint256 a, uint256 b) internal pure returns (uint256) { 39 | uint256 c = a + b; 40 | assert(c >= a); 41 | return c; 42 | } 43 | } -------------------------------------------------------------------------------- /contracts/provableAPI.sol: -------------------------------------------------------------------------------- 1 | // 2 | /* 3 | Copyright (c) 2015-2016 Oraclize SRL 4 | Copyright (c) 2016-2019 Oraclize LTD 5 | Copyright (c) 2019 Provable Things Limited 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | */ 22 | pragma solidity >= 0.5.0 < 0.6.0; // Incompatible compiler version - please select a compiler within the stated pragma range, or use a different version of the provableAPI! 23 | 24 | // Dummy contract only used to emit to end-user they are using wrong solc 25 | contract solcChecker { 26 | /* INCOMPATIBLE SOLC: import the following instead: "github.com/oraclize/ethereum-api/oraclizeAPI_0.4.sol" */ function f(bytes calldata x) external; 27 | } 28 | 29 | contract ProvableI { 30 | 31 | address public cbAddress; 32 | 33 | function setProofType(byte _proofType) external; 34 | function setCustomGasPrice(uint _gasPrice) external; 35 | function getPrice(string memory _datasource) public returns (uint _dsprice); 36 | function randomDS_getSessionPubKeyHash() external view returns (bytes32 _sessionKeyHash); 37 | function getPrice(string memory _datasource, uint _gasLimit) public returns (uint _dsprice); 38 | function queryN(uint _timestamp, string memory _datasource, bytes memory _argN) public payable returns (bytes32 _id); 39 | function query(uint _timestamp, string calldata _datasource, string calldata _arg) external payable returns (bytes32 _id); 40 | function query2(uint _timestamp, string memory _datasource, string memory _arg1, string memory _arg2) public payable returns (bytes32 _id); 41 | function query_withGasLimit(uint _timestamp, string calldata _datasource, string calldata _arg, uint _gasLimit) external payable returns (bytes32 _id); 42 | function queryN_withGasLimit(uint _timestamp, string calldata _datasource, bytes calldata _argN, uint _gasLimit) external payable returns (bytes32 _id); 43 | function query2_withGasLimit(uint _timestamp, string calldata _datasource, string calldata _arg1, string calldata _arg2, uint _gasLimit) external payable returns (bytes32 _id); 44 | } 45 | 46 | contract OracleAddrResolverI { 47 | function getAddress() public returns (address _address); 48 | } 49 | /* 50 | Begin solidity-cborutils 51 | https://github.com/smartcontractkit/solidity-cborutils 52 | MIT License 53 | Copyright (c) 2018 SmartContract ChainLink, Ltd. 54 | Permission is hereby granted, free of charge, to any person obtaining a copy 55 | of this software and associated documentation files (the "Software"), to deal 56 | in the Software without restriction, including without limitation the rights 57 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 58 | copies of the Software, and to permit persons to whom the Software is 59 | furnished to do so, subject to the following conditions: 60 | The above copyright notice and this permission notice shall be included in all 61 | copies or substantial portions of the Software. 62 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 63 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 64 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 65 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 66 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 67 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 68 | SOFTWARE. 69 | */ 70 | library Buffer { 71 | 72 | struct buffer { 73 | bytes buf; 74 | uint capacity; 75 | } 76 | 77 | function init(buffer memory _buf, uint _capacity) internal pure { 78 | uint capacity = _capacity; 79 | if (capacity % 32 != 0) { 80 | capacity += 32 - (capacity % 32); 81 | } 82 | _buf.capacity = capacity; // Allocate space for the buffer data 83 | assembly { 84 | let ptr := mload(0x40) 85 | mstore(_buf, ptr) 86 | mstore(ptr, 0) 87 | mstore(0x40, add(ptr, capacity)) 88 | } 89 | } 90 | 91 | function resize(buffer memory _buf, uint _capacity) private pure { 92 | bytes memory oldbuf = _buf.buf; 93 | init(_buf, _capacity); 94 | append(_buf, oldbuf); 95 | } 96 | 97 | function max(uint _a, uint _b) private pure returns (uint _max) { 98 | if (_a > _b) { 99 | return _a; 100 | } 101 | return _b; 102 | } 103 | /** 104 | * @dev Appends a byte array to the end of the buffer. Resizes if doing so 105 | * would exceed the capacity of the buffer. 106 | * @param _buf The buffer to append to. 107 | * @param _data The data to append. 108 | * @return The original buffer. 109 | * 110 | */ 111 | function append(buffer memory _buf, bytes memory _data) internal pure returns (buffer memory _buffer) { 112 | if (_data.length + _buf.buf.length > _buf.capacity) { 113 | resize(_buf, max(_buf.capacity, _data.length) * 2); 114 | } 115 | uint dest; 116 | uint src; 117 | uint len = _data.length; 118 | assembly { 119 | let bufptr := mload(_buf) // Memory address of the buffer data 120 | let buflen := mload(bufptr) // Length of existing buffer data 121 | dest := add(add(bufptr, buflen), 32) // Start address = buffer address + buffer length + sizeof(buffer length) 122 | mstore(bufptr, add(buflen, mload(_data))) // Update buffer length 123 | src := add(_data, 32) 124 | } 125 | for(; len >= 32; len -= 32) { // Copy word-length chunks while possible 126 | assembly { 127 | mstore(dest, mload(src)) 128 | } 129 | dest += 32; 130 | src += 32; 131 | } 132 | uint mask = 256 ** (32 - len) - 1; // Copy remaining bytes 133 | assembly { 134 | let srcpart := and(mload(src), not(mask)) 135 | let destpart := and(mload(dest), mask) 136 | mstore(dest, or(destpart, srcpart)) 137 | } 138 | return _buf; 139 | } 140 | /** 141 | * 142 | * @dev Appends a byte to the end of the buffer. Resizes if doing so would 143 | * exceed the capacity of the buffer. 144 | * @param _buf The buffer to append to. 145 | * @param _data The data to append. 146 | * @return The original buffer. 147 | * 148 | */ 149 | function append(buffer memory _buf, uint8 _data) internal pure { 150 | if (_buf.buf.length + 1 > _buf.capacity) { 151 | resize(_buf, _buf.capacity * 2); 152 | } 153 | assembly { 154 | let bufptr := mload(_buf) // Memory address of the buffer data 155 | let buflen := mload(bufptr) // Length of existing buffer data 156 | let dest := add(add(bufptr, buflen), 32) // Address = buffer address + buffer length + sizeof(buffer length) 157 | mstore8(dest, _data) 158 | mstore(bufptr, add(buflen, 1)) // Update buffer length 159 | } 160 | } 161 | /** 162 | * 163 | * @dev Appends a byte to the end of the buffer. Resizes if doing so would 164 | * exceed the capacity of the buffer. 165 | * @param _buf The buffer to append to. 166 | * @param _data The data to append. 167 | * @return The original buffer. 168 | * 169 | */ 170 | function appendInt(buffer memory _buf, uint _data, uint _len) internal pure returns (buffer memory _buffer) { 171 | if (_len + _buf.buf.length > _buf.capacity) { 172 | resize(_buf, max(_buf.capacity, _len) * 2); 173 | } 174 | uint mask = 256 ** _len - 1; 175 | assembly { 176 | let bufptr := mload(_buf) // Memory address of the buffer data 177 | let buflen := mload(bufptr) // Length of existing buffer data 178 | let dest := add(add(bufptr, buflen), _len) // Address = buffer address + buffer length + sizeof(buffer length) + len 179 | mstore(dest, or(and(mload(dest), not(mask)), _data)) 180 | mstore(bufptr, add(buflen, _len)) // Update buffer length 181 | } 182 | return _buf; 183 | } 184 | } 185 | 186 | library CBOR { 187 | 188 | using Buffer for Buffer.buffer; 189 | 190 | uint8 private constant MAJOR_TYPE_INT = 0; 191 | uint8 private constant MAJOR_TYPE_MAP = 5; 192 | uint8 private constant MAJOR_TYPE_BYTES = 2; 193 | uint8 private constant MAJOR_TYPE_ARRAY = 4; 194 | uint8 private constant MAJOR_TYPE_STRING = 3; 195 | uint8 private constant MAJOR_TYPE_NEGATIVE_INT = 1; 196 | uint8 private constant MAJOR_TYPE_CONTENT_FREE = 7; 197 | 198 | function encodeType(Buffer.buffer memory _buf, uint8 _major, uint _value) private pure { 199 | if (_value <= 23) { 200 | _buf.append(uint8((_major << 5) | _value)); 201 | } else if (_value <= 0xFF) { 202 | _buf.append(uint8((_major << 5) | 24)); 203 | _buf.appendInt(_value, 1); 204 | } else if (_value <= 0xFFFF) { 205 | _buf.append(uint8((_major << 5) | 25)); 206 | _buf.appendInt(_value, 2); 207 | } else if (_value <= 0xFFFFFFFF) { 208 | _buf.append(uint8((_major << 5) | 26)); 209 | _buf.appendInt(_value, 4); 210 | } else if (_value <= 0xFFFFFFFFFFFFFFFF) { 211 | _buf.append(uint8((_major << 5) | 27)); 212 | _buf.appendInt(_value, 8); 213 | } 214 | } 215 | 216 | function encodeIndefiniteLengthType(Buffer.buffer memory _buf, uint8 _major) private pure { 217 | _buf.append(uint8((_major << 5) | 31)); 218 | } 219 | 220 | function encodeUInt(Buffer.buffer memory _buf, uint _value) internal pure { 221 | encodeType(_buf, MAJOR_TYPE_INT, _value); 222 | } 223 | 224 | function encodeInt(Buffer.buffer memory _buf, int _value) internal pure { 225 | if (_value >= 0) { 226 | encodeType(_buf, MAJOR_TYPE_INT, uint(_value)); 227 | } else { 228 | encodeType(_buf, MAJOR_TYPE_NEGATIVE_INT, uint(-1 - _value)); 229 | } 230 | } 231 | 232 | function encodeBytes(Buffer.buffer memory _buf, bytes memory _value) internal pure { 233 | encodeType(_buf, MAJOR_TYPE_BYTES, _value.length); 234 | _buf.append(_value); 235 | } 236 | 237 | function encodeString(Buffer.buffer memory _buf, string memory _value) internal pure { 238 | encodeType(_buf, MAJOR_TYPE_STRING, bytes(_value).length); 239 | _buf.append(bytes(_value)); 240 | } 241 | 242 | function startArray(Buffer.buffer memory _buf) internal pure { 243 | encodeIndefiniteLengthType(_buf, MAJOR_TYPE_ARRAY); 244 | } 245 | 246 | function startMap(Buffer.buffer memory _buf) internal pure { 247 | encodeIndefiniteLengthType(_buf, MAJOR_TYPE_MAP); 248 | } 249 | 250 | function endSequence(Buffer.buffer memory _buf) internal pure { 251 | encodeIndefiniteLengthType(_buf, MAJOR_TYPE_CONTENT_FREE); 252 | } 253 | } 254 | /* 255 | End solidity-cborutils 256 | */ 257 | contract usingProvable { 258 | 259 | using CBOR for Buffer.buffer; 260 | 261 | ProvableI provable; 262 | OracleAddrResolverI OAR; 263 | 264 | uint constant day = 60 * 60 * 24; 265 | uint constant week = 60 * 60 * 24 * 7; 266 | uint constant month = 60 * 60 * 24 * 30; 267 | 268 | byte constant proofType_NONE = 0x00; 269 | byte constant proofType_Ledger = 0x30; 270 | byte constant proofType_Native = 0xF0; 271 | byte constant proofStorage_IPFS = 0x01; 272 | byte constant proofType_Android = 0x40; 273 | byte constant proofType_TLSNotary = 0x10; 274 | 275 | string provable_network_name; 276 | uint8 constant networkID_auto = 0; 277 | uint8 constant networkID_morden = 2; 278 | uint8 constant networkID_mainnet = 1; 279 | uint8 constant networkID_testnet = 2; 280 | uint8 constant networkID_consensys = 161; 281 | 282 | mapping(bytes32 => bytes32) provable_randomDS_args; 283 | mapping(bytes32 => bool) provable_randomDS_sessionKeysHashVerified; 284 | 285 | modifier provableAPI { 286 | if ((address(OAR) == address(0)) || (getCodeSize(address(OAR)) == 0)) { 287 | provable_setNetwork(networkID_auto); 288 | } 289 | if (address(provable) != OAR.getAddress()) { 290 | provable = ProvableI(OAR.getAddress()); 291 | } 292 | _; 293 | } 294 | 295 | modifier provable_randomDS_proofVerify(bytes32 _queryId, string memory _result, bytes memory _proof) { 296 | // RandomDS Proof Step 1: The prefix has to match 'LP\x01' (Ledger Proof version 1) 297 | require((_proof[0] == "L") && (_proof[1] == "P") && (uint8(_proof[2]) == uint8(1))); 298 | bool proofVerified = provable_randomDS_proofVerify__main(_proof, _queryId, bytes(_result), provable_getNetworkName()); 299 | require(proofVerified); 300 | _; 301 | } 302 | 303 | function provable_setNetwork(uint8 _networkID) internal returns (bool _networkSet) { 304 | _networkID; // NOTE: Silence the warning and remain backwards compatible 305 | return provable_setNetwork(); 306 | } 307 | 308 | function provable_setNetworkName(string memory _network_name) internal { 309 | provable_network_name = _network_name; 310 | } 311 | 312 | function provable_getNetworkName() internal view returns (string memory _networkName) { 313 | return provable_network_name; 314 | } 315 | 316 | function provable_setNetwork() internal returns (bool _networkSet) { 317 | if (getCodeSize(0x1d3B2638a7cC9f2CB3D298A3DA7a90B67E5506ed) > 0) { //mainnet 318 | OAR = OracleAddrResolverI(0x1d3B2638a7cC9f2CB3D298A3DA7a90B67E5506ed); 319 | provable_setNetworkName("eth_mainnet"); 320 | return true; 321 | } 322 | if (getCodeSize(0xc03A2615D5efaf5F49F60B7BB6583eaec212fdf1) > 0) { //ropsten testnet 323 | OAR = OracleAddrResolverI(0xc03A2615D5efaf5F49F60B7BB6583eaec212fdf1); 324 | provable_setNetworkName("eth_ropsten3"); 325 | return true; 326 | } 327 | if (getCodeSize(0xB7A07BcF2Ba2f2703b24C0691b5278999C59AC7e) > 0) { //kovan testnet 328 | OAR = OracleAddrResolverI(0xB7A07BcF2Ba2f2703b24C0691b5278999C59AC7e); 329 | provable_setNetworkName("eth_kovan"); 330 | return true; 331 | } 332 | if (getCodeSize(0x146500cfd35B22E4A392Fe0aDc06De1a1368Ed48) > 0) { //rinkeby testnet 333 | OAR = OracleAddrResolverI(0x146500cfd35B22E4A392Fe0aDc06De1a1368Ed48); 334 | provable_setNetworkName("eth_rinkeby"); 335 | return true; 336 | } 337 | if (getCodeSize(0xa2998EFD205FB9D4B4963aFb70778D6354ad3A41) > 0) { //goerli testnet 338 | OAR = OracleAddrResolverI(0xa2998EFD205FB9D4B4963aFb70778D6354ad3A41); 339 | provable_setNetworkName("eth_goerli"); 340 | return true; 341 | } 342 | if (getCodeSize(0x6f485C8BF6fc43eA212E93BBF8ce046C7f1cb475) > 0) { //ethereum-bridge 343 | OAR = OracleAddrResolverI(0x6f485C8BF6fc43eA212E93BBF8ce046C7f1cb475); 344 | return true; 345 | } 346 | if (getCodeSize(0x20e12A1F859B3FeaE5Fb2A0A32C18F5a65555bBF) > 0) { //ether.camp ide 347 | OAR = OracleAddrResolverI(0x20e12A1F859B3FeaE5Fb2A0A32C18F5a65555bBF); 348 | return true; 349 | } 350 | if (getCodeSize(0x51efaF4c8B3C9AfBD5aB9F4bbC82784Ab6ef8fAA) > 0) { //browser-solidity 351 | OAR = OracleAddrResolverI(0x51efaF4c8B3C9AfBD5aB9F4bbC82784Ab6ef8fAA); 352 | return true; 353 | } 354 | return false; 355 | } 356 | /** 357 | * @dev The following `__callback` functions are just placeholders ideally 358 | * meant to be defined in child contract when proofs are used. 359 | * The function bodies simply silence compiler warnings. 360 | */ 361 | function __callback(bytes32 _myid, string memory _result) public { 362 | __callback(_myid, _result, new bytes(0)); 363 | } 364 | 365 | function __callback(bytes32 _myid, string memory _result, bytes memory _proof) public { 366 | _myid; _result; _proof; 367 | provable_randomDS_args[bytes32(0)] = bytes32(0); 368 | } 369 | 370 | function provable_getPrice(string memory _datasource) provableAPI internal returns (uint _queryPrice) { 371 | return provable.getPrice(_datasource); 372 | } 373 | 374 | function provable_getPrice(string memory _datasource, uint _gasLimit) provableAPI internal returns (uint _queryPrice) { 375 | return provable.getPrice(_datasource, _gasLimit); 376 | } 377 | 378 | function provable_query(string memory _datasource, string memory _arg) provableAPI internal returns (bytes32 _id) { 379 | uint price = provable.getPrice(_datasource); 380 | if (price > 1 ether + tx.gasprice * 200000) { 381 | return 0; // Unexpectedly high price 382 | } 383 | return provable.query.value(price)(0, _datasource, _arg); 384 | } 385 | 386 | function provable_query(uint _timestamp, string memory _datasource, string memory _arg) provableAPI internal returns (bytes32 _id) { 387 | uint price = provable.getPrice(_datasource); 388 | if (price > 1 ether + tx.gasprice * 200000) { 389 | return 0; // Unexpectedly high price 390 | } 391 | return provable.query.value(price)(_timestamp, _datasource, _arg); 392 | } 393 | 394 | function provable_query(uint _timestamp, string memory _datasource, string memory _arg, uint _gasLimit) provableAPI internal returns (bytes32 _id) { 395 | uint price = provable.getPrice(_datasource,_gasLimit); 396 | if (price > 1 ether + tx.gasprice * _gasLimit) { 397 | return 0; // Unexpectedly high price 398 | } 399 | return provable.query_withGasLimit.value(price)(_timestamp, _datasource, _arg, _gasLimit); 400 | } 401 | 402 | function provable_query(string memory _datasource, string memory _arg, uint _gasLimit) provableAPI internal returns (bytes32 _id) { 403 | uint price = provable.getPrice(_datasource, _gasLimit); 404 | if (price > 1 ether + tx.gasprice * _gasLimit) { 405 | return 0; // Unexpectedly high price 406 | } 407 | return provable.query_withGasLimit.value(price)(0, _datasource, _arg, _gasLimit); 408 | } 409 | 410 | function provable_query(string memory _datasource, string memory _arg1, string memory _arg2) provableAPI internal returns (bytes32 _id) { 411 | uint price = provable.getPrice(_datasource); 412 | if (price > 1 ether + tx.gasprice * 200000) { 413 | return 0; // Unexpectedly high price 414 | } 415 | return provable.query2.value(price)(0, _datasource, _arg1, _arg2); 416 | } 417 | 418 | function provable_query(uint _timestamp, string memory _datasource, string memory _arg1, string memory _arg2) provableAPI internal returns (bytes32 _id) { 419 | uint price = provable.getPrice(_datasource); 420 | if (price > 1 ether + tx.gasprice * 200000) { 421 | return 0; // Unexpectedly high price 422 | } 423 | return provable.query2.value(price)(_timestamp, _datasource, _arg1, _arg2); 424 | } 425 | 426 | function provable_query(uint _timestamp, string memory _datasource, string memory _arg1, string memory _arg2, uint _gasLimit) provableAPI internal returns (bytes32 _id) { 427 | uint price = provable.getPrice(_datasource, _gasLimit); 428 | if (price > 1 ether + tx.gasprice * _gasLimit) { 429 | return 0; // Unexpectedly high price 430 | } 431 | return provable.query2_withGasLimit.value(price)(_timestamp, _datasource, _arg1, _arg2, _gasLimit); 432 | } 433 | 434 | function provable_query(string memory _datasource, string memory _arg1, string memory _arg2, uint _gasLimit) provableAPI internal returns (bytes32 _id) { 435 | uint price = provable.getPrice(_datasource, _gasLimit); 436 | if (price > 1 ether + tx.gasprice * _gasLimit) { 437 | return 0; // Unexpectedly high price 438 | } 439 | return provable.query2_withGasLimit.value(price)(0, _datasource, _arg1, _arg2, _gasLimit); 440 | } 441 | 442 | function provable_query(string memory _datasource, string[] memory _argN) provableAPI internal returns (bytes32 _id) { 443 | uint price = provable.getPrice(_datasource); 444 | if (price > 1 ether + tx.gasprice * 200000) { 445 | return 0; // Unexpectedly high price 446 | } 447 | bytes memory args = stra2cbor(_argN); 448 | return provable.queryN.value(price)(0, _datasource, args); 449 | } 450 | 451 | function provable_query(uint _timestamp, string memory _datasource, string[] memory _argN) provableAPI internal returns (bytes32 _id) { 452 | uint price = provable.getPrice(_datasource); 453 | if (price > 1 ether + tx.gasprice * 200000) { 454 | return 0; // Unexpectedly high price 455 | } 456 | bytes memory args = stra2cbor(_argN); 457 | return provable.queryN.value(price)(_timestamp, _datasource, args); 458 | } 459 | 460 | function provable_query(uint _timestamp, string memory _datasource, string[] memory _argN, uint _gasLimit) provableAPI internal returns (bytes32 _id) { 461 | uint price = provable.getPrice(_datasource, _gasLimit); 462 | if (price > 1 ether + tx.gasprice * _gasLimit) { 463 | return 0; // Unexpectedly high price 464 | } 465 | bytes memory args = stra2cbor(_argN); 466 | return provable.queryN_withGasLimit.value(price)(_timestamp, _datasource, args, _gasLimit); 467 | } 468 | 469 | function provable_query(string memory _datasource, string[] memory _argN, uint _gasLimit) provableAPI internal returns (bytes32 _id) { 470 | uint price = provable.getPrice(_datasource, _gasLimit); 471 | if (price > 1 ether + tx.gasprice * _gasLimit) { 472 | return 0; // Unexpectedly high price 473 | } 474 | bytes memory args = stra2cbor(_argN); 475 | return provable.queryN_withGasLimit.value(price)(0, _datasource, args, _gasLimit); 476 | } 477 | 478 | function provable_query(string memory _datasource, string[1] memory _args) provableAPI internal returns (bytes32 _id) { 479 | string[] memory dynargs = new string[](1); 480 | dynargs[0] = _args[0]; 481 | return provable_query(_datasource, dynargs); 482 | } 483 | 484 | function provable_query(uint _timestamp, string memory _datasource, string[1] memory _args) provableAPI internal returns (bytes32 _id) { 485 | string[] memory dynargs = new string[](1); 486 | dynargs[0] = _args[0]; 487 | return provable_query(_timestamp, _datasource, dynargs); 488 | } 489 | 490 | function provable_query(uint _timestamp, string memory _datasource, string[1] memory _args, uint _gasLimit) provableAPI internal returns (bytes32 _id) { 491 | string[] memory dynargs = new string[](1); 492 | dynargs[0] = _args[0]; 493 | return provable_query(_timestamp, _datasource, dynargs, _gasLimit); 494 | } 495 | 496 | function provable_query(string memory _datasource, string[1] memory _args, uint _gasLimit) provableAPI internal returns (bytes32 _id) { 497 | string[] memory dynargs = new string[](1); 498 | dynargs[0] = _args[0]; 499 | return provable_query(_datasource, dynargs, _gasLimit); 500 | } 501 | 502 | function provable_query(string memory _datasource, string[2] memory _args) provableAPI internal returns (bytes32 _id) { 503 | string[] memory dynargs = new string[](2); 504 | dynargs[0] = _args[0]; 505 | dynargs[1] = _args[1]; 506 | return provable_query(_datasource, dynargs); 507 | } 508 | 509 | function provable_query(uint _timestamp, string memory _datasource, string[2] memory _args) provableAPI internal returns (bytes32 _id) { 510 | string[] memory dynargs = new string[](2); 511 | dynargs[0] = _args[0]; 512 | dynargs[1] = _args[1]; 513 | return provable_query(_timestamp, _datasource, dynargs); 514 | } 515 | 516 | function provable_query(uint _timestamp, string memory _datasource, string[2] memory _args, uint _gasLimit) provableAPI internal returns (bytes32 _id) { 517 | string[] memory dynargs = new string[](2); 518 | dynargs[0] = _args[0]; 519 | dynargs[1] = _args[1]; 520 | return provable_query(_timestamp, _datasource, dynargs, _gasLimit); 521 | } 522 | 523 | function provable_query(string memory _datasource, string[2] memory _args, uint _gasLimit) provableAPI internal returns (bytes32 _id) { 524 | string[] memory dynargs = new string[](2); 525 | dynargs[0] = _args[0]; 526 | dynargs[1] = _args[1]; 527 | return provable_query(_datasource, dynargs, _gasLimit); 528 | } 529 | 530 | function provable_query(string memory _datasource, string[3] memory _args) provableAPI internal returns (bytes32 _id) { 531 | string[] memory dynargs = new string[](3); 532 | dynargs[0] = _args[0]; 533 | dynargs[1] = _args[1]; 534 | dynargs[2] = _args[2]; 535 | return provable_query(_datasource, dynargs); 536 | } 537 | 538 | function provable_query(uint _timestamp, string memory _datasource, string[3] memory _args) provableAPI internal returns (bytes32 _id) { 539 | string[] memory dynargs = new string[](3); 540 | dynargs[0] = _args[0]; 541 | dynargs[1] = _args[1]; 542 | dynargs[2] = _args[2]; 543 | return provable_query(_timestamp, _datasource, dynargs); 544 | } 545 | 546 | function provable_query(uint _timestamp, string memory _datasource, string[3] memory _args, uint _gasLimit) provableAPI internal returns (bytes32 _id) { 547 | string[] memory dynargs = new string[](3); 548 | dynargs[0] = _args[0]; 549 | dynargs[1] = _args[1]; 550 | dynargs[2] = _args[2]; 551 | return provable_query(_timestamp, _datasource, dynargs, _gasLimit); 552 | } 553 | 554 | function provable_query(string memory _datasource, string[3] memory _args, uint _gasLimit) provableAPI internal returns (bytes32 _id) { 555 | string[] memory dynargs = new string[](3); 556 | dynargs[0] = _args[0]; 557 | dynargs[1] = _args[1]; 558 | dynargs[2] = _args[2]; 559 | return provable_query(_datasource, dynargs, _gasLimit); 560 | } 561 | 562 | function provable_query(string memory _datasource, string[4] memory _args) provableAPI internal returns (bytes32 _id) { 563 | string[] memory dynargs = new string[](4); 564 | dynargs[0] = _args[0]; 565 | dynargs[1] = _args[1]; 566 | dynargs[2] = _args[2]; 567 | dynargs[3] = _args[3]; 568 | return provable_query(_datasource, dynargs); 569 | } 570 | 571 | function provable_query(uint _timestamp, string memory _datasource, string[4] memory _args) provableAPI internal returns (bytes32 _id) { 572 | string[] memory dynargs = new string[](4); 573 | dynargs[0] = _args[0]; 574 | dynargs[1] = _args[1]; 575 | dynargs[2] = _args[2]; 576 | dynargs[3] = _args[3]; 577 | return provable_query(_timestamp, _datasource, dynargs); 578 | } 579 | 580 | function provable_query(uint _timestamp, string memory _datasource, string[4] memory _args, uint _gasLimit) provableAPI internal returns (bytes32 _id) { 581 | string[] memory dynargs = new string[](4); 582 | dynargs[0] = _args[0]; 583 | dynargs[1] = _args[1]; 584 | dynargs[2] = _args[2]; 585 | dynargs[3] = _args[3]; 586 | return provable_query(_timestamp, _datasource, dynargs, _gasLimit); 587 | } 588 | 589 | function provable_query(string memory _datasource, string[4] memory _args, uint _gasLimit) provableAPI internal returns (bytes32 _id) { 590 | string[] memory dynargs = new string[](4); 591 | dynargs[0] = _args[0]; 592 | dynargs[1] = _args[1]; 593 | dynargs[2] = _args[2]; 594 | dynargs[3] = _args[3]; 595 | return provable_query(_datasource, dynargs, _gasLimit); 596 | } 597 | 598 | function provable_query(string memory _datasource, string[5] memory _args) provableAPI internal returns (bytes32 _id) { 599 | string[] memory dynargs = new string[](5); 600 | dynargs[0] = _args[0]; 601 | dynargs[1] = _args[1]; 602 | dynargs[2] = _args[2]; 603 | dynargs[3] = _args[3]; 604 | dynargs[4] = _args[4]; 605 | return provable_query(_datasource, dynargs); 606 | } 607 | 608 | function provable_query(uint _timestamp, string memory _datasource, string[5] memory _args) provableAPI internal returns (bytes32 _id) { 609 | string[] memory dynargs = new string[](5); 610 | dynargs[0] = _args[0]; 611 | dynargs[1] = _args[1]; 612 | dynargs[2] = _args[2]; 613 | dynargs[3] = _args[3]; 614 | dynargs[4] = _args[4]; 615 | return provable_query(_timestamp, _datasource, dynargs); 616 | } 617 | 618 | function provable_query(uint _timestamp, string memory _datasource, string[5] memory _args, uint _gasLimit) provableAPI internal returns (bytes32 _id) { 619 | string[] memory dynargs = new string[](5); 620 | dynargs[0] = _args[0]; 621 | dynargs[1] = _args[1]; 622 | dynargs[2] = _args[2]; 623 | dynargs[3] = _args[3]; 624 | dynargs[4] = _args[4]; 625 | return provable_query(_timestamp, _datasource, dynargs, _gasLimit); 626 | } 627 | 628 | function provable_query(string memory _datasource, string[5] memory _args, uint _gasLimit) provableAPI internal returns (bytes32 _id) { 629 | string[] memory dynargs = new string[](5); 630 | dynargs[0] = _args[0]; 631 | dynargs[1] = _args[1]; 632 | dynargs[2] = _args[2]; 633 | dynargs[3] = _args[3]; 634 | dynargs[4] = _args[4]; 635 | return provable_query(_datasource, dynargs, _gasLimit); 636 | } 637 | 638 | function provable_query(string memory _datasource, bytes[] memory _argN) provableAPI internal returns (bytes32 _id) { 639 | uint price = provable.getPrice(_datasource); 640 | if (price > 1 ether + tx.gasprice * 200000) { 641 | return 0; // Unexpectedly high price 642 | } 643 | bytes memory args = ba2cbor(_argN); 644 | return provable.queryN.value(price)(0, _datasource, args); 645 | } 646 | 647 | function provable_query(uint _timestamp, string memory _datasource, bytes[] memory _argN) provableAPI internal returns (bytes32 _id) { 648 | uint price = provable.getPrice(_datasource); 649 | if (price > 1 ether + tx.gasprice * 200000) { 650 | return 0; // Unexpectedly high price 651 | } 652 | bytes memory args = ba2cbor(_argN); 653 | return provable.queryN.value(price)(_timestamp, _datasource, args); 654 | } 655 | 656 | function provable_query(uint _timestamp, string memory _datasource, bytes[] memory _argN, uint _gasLimit) provableAPI internal returns (bytes32 _id) { 657 | uint price = provable.getPrice(_datasource, _gasLimit); 658 | if (price > 1 ether + tx.gasprice * _gasLimit) { 659 | return 0; // Unexpectedly high price 660 | } 661 | bytes memory args = ba2cbor(_argN); 662 | return provable.queryN_withGasLimit.value(price)(_timestamp, _datasource, args, _gasLimit); 663 | } 664 | 665 | function provable_query(string memory _datasource, bytes[] memory _argN, uint _gasLimit) provableAPI internal returns (bytes32 _id) { 666 | uint price = provable.getPrice(_datasource, _gasLimit); 667 | if (price > 1 ether + tx.gasprice * _gasLimit) { 668 | return 0; // Unexpectedly high price 669 | } 670 | bytes memory args = ba2cbor(_argN); 671 | return provable.queryN_withGasLimit.value(price)(0, _datasource, args, _gasLimit); 672 | } 673 | 674 | function provable_query(string memory _datasource, bytes[1] memory _args) provableAPI internal returns (bytes32 _id) { 675 | bytes[] memory dynargs = new bytes[](1); 676 | dynargs[0] = _args[0]; 677 | return provable_query(_datasource, dynargs); 678 | } 679 | 680 | function provable_query(uint _timestamp, string memory _datasource, bytes[1] memory _args) provableAPI internal returns (bytes32 _id) { 681 | bytes[] memory dynargs = new bytes[](1); 682 | dynargs[0] = _args[0]; 683 | return provable_query(_timestamp, _datasource, dynargs); 684 | } 685 | 686 | function provable_query(uint _timestamp, string memory _datasource, bytes[1] memory _args, uint _gasLimit) provableAPI internal returns (bytes32 _id) { 687 | bytes[] memory dynargs = new bytes[](1); 688 | dynargs[0] = _args[0]; 689 | return provable_query(_timestamp, _datasource, dynargs, _gasLimit); 690 | } 691 | 692 | function provable_query(string memory _datasource, bytes[1] memory _args, uint _gasLimit) provableAPI internal returns (bytes32 _id) { 693 | bytes[] memory dynargs = new bytes[](1); 694 | dynargs[0] = _args[0]; 695 | return provable_query(_datasource, dynargs, _gasLimit); 696 | } 697 | 698 | function provable_query(string memory _datasource, bytes[2] memory _args) provableAPI internal returns (bytes32 _id) { 699 | bytes[] memory dynargs = new bytes[](2); 700 | dynargs[0] = _args[0]; 701 | dynargs[1] = _args[1]; 702 | return provable_query(_datasource, dynargs); 703 | } 704 | 705 | function provable_query(uint _timestamp, string memory _datasource, bytes[2] memory _args) provableAPI internal returns (bytes32 _id) { 706 | bytes[] memory dynargs = new bytes[](2); 707 | dynargs[0] = _args[0]; 708 | dynargs[1] = _args[1]; 709 | return provable_query(_timestamp, _datasource, dynargs); 710 | } 711 | 712 | function provable_query(uint _timestamp, string memory _datasource, bytes[2] memory _args, uint _gasLimit) provableAPI internal returns (bytes32 _id) { 713 | bytes[] memory dynargs = new bytes[](2); 714 | dynargs[0] = _args[0]; 715 | dynargs[1] = _args[1]; 716 | return provable_query(_timestamp, _datasource, dynargs, _gasLimit); 717 | } 718 | 719 | function provable_query(string memory _datasource, bytes[2] memory _args, uint _gasLimit) provableAPI internal returns (bytes32 _id) { 720 | bytes[] memory dynargs = new bytes[](2); 721 | dynargs[0] = _args[0]; 722 | dynargs[1] = _args[1]; 723 | return provable_query(_datasource, dynargs, _gasLimit); 724 | } 725 | 726 | function provable_query(string memory _datasource, bytes[3] memory _args) provableAPI internal returns (bytes32 _id) { 727 | bytes[] memory dynargs = new bytes[](3); 728 | dynargs[0] = _args[0]; 729 | dynargs[1] = _args[1]; 730 | dynargs[2] = _args[2]; 731 | return provable_query(_datasource, dynargs); 732 | } 733 | 734 | function provable_query(uint _timestamp, string memory _datasource, bytes[3] memory _args) provableAPI internal returns (bytes32 _id) { 735 | bytes[] memory dynargs = new bytes[](3); 736 | dynargs[0] = _args[0]; 737 | dynargs[1] = _args[1]; 738 | dynargs[2] = _args[2]; 739 | return provable_query(_timestamp, _datasource, dynargs); 740 | } 741 | 742 | function provable_query(uint _timestamp, string memory _datasource, bytes[3] memory _args, uint _gasLimit) provableAPI internal returns (bytes32 _id) { 743 | bytes[] memory dynargs = new bytes[](3); 744 | dynargs[0] = _args[0]; 745 | dynargs[1] = _args[1]; 746 | dynargs[2] = _args[2]; 747 | return provable_query(_timestamp, _datasource, dynargs, _gasLimit); 748 | } 749 | 750 | function provable_query(string memory _datasource, bytes[3] memory _args, uint _gasLimit) provableAPI internal returns (bytes32 _id) { 751 | bytes[] memory dynargs = new bytes[](3); 752 | dynargs[0] = _args[0]; 753 | dynargs[1] = _args[1]; 754 | dynargs[2] = _args[2]; 755 | return provable_query(_datasource, dynargs, _gasLimit); 756 | } 757 | 758 | function provable_query(string memory _datasource, bytes[4] memory _args) provableAPI internal returns (bytes32 _id) { 759 | bytes[] memory dynargs = new bytes[](4); 760 | dynargs[0] = _args[0]; 761 | dynargs[1] = _args[1]; 762 | dynargs[2] = _args[2]; 763 | dynargs[3] = _args[3]; 764 | return provable_query(_datasource, dynargs); 765 | } 766 | 767 | function provable_query(uint _timestamp, string memory _datasource, bytes[4] memory _args) provableAPI internal returns (bytes32 _id) { 768 | bytes[] memory dynargs = new bytes[](4); 769 | dynargs[0] = _args[0]; 770 | dynargs[1] = _args[1]; 771 | dynargs[2] = _args[2]; 772 | dynargs[3] = _args[3]; 773 | return provable_query(_timestamp, _datasource, dynargs); 774 | } 775 | 776 | function provable_query(uint _timestamp, string memory _datasource, bytes[4] memory _args, uint _gasLimit) provableAPI internal returns (bytes32 _id) { 777 | bytes[] memory dynargs = new bytes[](4); 778 | dynargs[0] = _args[0]; 779 | dynargs[1] = _args[1]; 780 | dynargs[2] = _args[2]; 781 | dynargs[3] = _args[3]; 782 | return provable_query(_timestamp, _datasource, dynargs, _gasLimit); 783 | } 784 | 785 | function provable_query(string memory _datasource, bytes[4] memory _args, uint _gasLimit) provableAPI internal returns (bytes32 _id) { 786 | bytes[] memory dynargs = new bytes[](4); 787 | dynargs[0] = _args[0]; 788 | dynargs[1] = _args[1]; 789 | dynargs[2] = _args[2]; 790 | dynargs[3] = _args[3]; 791 | return provable_query(_datasource, dynargs, _gasLimit); 792 | } 793 | 794 | function provable_query(string memory _datasource, bytes[5] memory _args) provableAPI internal returns (bytes32 _id) { 795 | bytes[] memory dynargs = new bytes[](5); 796 | dynargs[0] = _args[0]; 797 | dynargs[1] = _args[1]; 798 | dynargs[2] = _args[2]; 799 | dynargs[3] = _args[3]; 800 | dynargs[4] = _args[4]; 801 | return provable_query(_datasource, dynargs); 802 | } 803 | 804 | function provable_query(uint _timestamp, string memory _datasource, bytes[5] memory _args) provableAPI internal returns (bytes32 _id) { 805 | bytes[] memory dynargs = new bytes[](5); 806 | dynargs[0] = _args[0]; 807 | dynargs[1] = _args[1]; 808 | dynargs[2] = _args[2]; 809 | dynargs[3] = _args[3]; 810 | dynargs[4] = _args[4]; 811 | return provable_query(_timestamp, _datasource, dynargs); 812 | } 813 | 814 | function provable_query(uint _timestamp, string memory _datasource, bytes[5] memory _args, uint _gasLimit) provableAPI internal returns (bytes32 _id) { 815 | bytes[] memory dynargs = new bytes[](5); 816 | dynargs[0] = _args[0]; 817 | dynargs[1] = _args[1]; 818 | dynargs[2] = _args[2]; 819 | dynargs[3] = _args[3]; 820 | dynargs[4] = _args[4]; 821 | return provable_query(_timestamp, _datasource, dynargs, _gasLimit); 822 | } 823 | 824 | function provable_query(string memory _datasource, bytes[5] memory _args, uint _gasLimit) provableAPI internal returns (bytes32 _id) { 825 | bytes[] memory dynargs = new bytes[](5); 826 | dynargs[0] = _args[0]; 827 | dynargs[1] = _args[1]; 828 | dynargs[2] = _args[2]; 829 | dynargs[3] = _args[3]; 830 | dynargs[4] = _args[4]; 831 | return provable_query(_datasource, dynargs, _gasLimit); 832 | } 833 | 834 | function provable_setProof(byte _proofP) provableAPI internal { 835 | return provable.setProofType(_proofP); 836 | } 837 | 838 | 839 | function provable_cbAddress() provableAPI internal returns (address _callbackAddress) { 840 | return provable.cbAddress(); 841 | } 842 | 843 | function getCodeSize(address _addr) view internal returns (uint _size) { 844 | assembly { 845 | _size := extcodesize(_addr) 846 | } 847 | } 848 | 849 | function provable_setCustomGasPrice(uint _gasPrice) provableAPI internal { 850 | return provable.setCustomGasPrice(_gasPrice); 851 | } 852 | 853 | function provable_randomDS_getSessionPubKeyHash() provableAPI internal returns (bytes32 _sessionKeyHash) { 854 | return provable.randomDS_getSessionPubKeyHash(); 855 | } 856 | 857 | function parseAddr(string memory _a) internal pure returns (address _parsedAddress) { 858 | bytes memory tmp = bytes(_a); 859 | uint160 iaddr = 0; 860 | uint160 b1; 861 | uint160 b2; 862 | for (uint i = 2; i < 2 + 2 * 20; i += 2) { 863 | iaddr *= 256; 864 | b1 = uint160(uint8(tmp[i])); 865 | b2 = uint160(uint8(tmp[i + 1])); 866 | if ((b1 >= 97) && (b1 <= 102)) { 867 | b1 -= 87; 868 | } else if ((b1 >= 65) && (b1 <= 70)) { 869 | b1 -= 55; 870 | } else if ((b1 >= 48) && (b1 <= 57)) { 871 | b1 -= 48; 872 | } 873 | if ((b2 >= 97) && (b2 <= 102)) { 874 | b2 -= 87; 875 | } else if ((b2 >= 65) && (b2 <= 70)) { 876 | b2 -= 55; 877 | } else if ((b2 >= 48) && (b2 <= 57)) { 878 | b2 -= 48; 879 | } 880 | iaddr += (b1 * 16 + b2); 881 | } 882 | return address(iaddr); 883 | } 884 | 885 | function strCompare(string memory _a, string memory _b) internal pure returns (int _returnCode) { 886 | bytes memory a = bytes(_a); 887 | bytes memory b = bytes(_b); 888 | uint minLength = a.length; 889 | if (b.length < minLength) { 890 | minLength = b.length; 891 | } 892 | for (uint i = 0; i < minLength; i ++) { 893 | if (a[i] < b[i]) { 894 | return -1; 895 | } else if (a[i] > b[i]) { 896 | return 1; 897 | } 898 | } 899 | if (a.length < b.length) { 900 | return -1; 901 | } else if (a.length > b.length) { 902 | return 1; 903 | } else { 904 | return 0; 905 | } 906 | } 907 | 908 | function indexOf(string memory _haystack, string memory _needle) internal pure returns (int _returnCode) { 909 | bytes memory h = bytes(_haystack); 910 | bytes memory n = bytes(_needle); 911 | if (h.length < 1 || n.length < 1 || (n.length > h.length)) { 912 | return -1; 913 | } else if (h.length > (2 ** 128 - 1)) { 914 | return -1; 915 | } else { 916 | uint subindex = 0; 917 | for (uint i = 0; i < h.length; i++) { 918 | if (h[i] == n[0]) { 919 | subindex = 1; 920 | while(subindex < n.length && (i + subindex) < h.length && h[i + subindex] == n[subindex]) { 921 | subindex++; 922 | } 923 | if (subindex == n.length) { 924 | return int(i); 925 | } 926 | } 927 | } 928 | return -1; 929 | } 930 | } 931 | 932 | function strConcat(string memory _a, string memory _b) internal pure returns (string memory _concatenatedString) { 933 | return strConcat(_a, _b, "", "", ""); 934 | } 935 | 936 | function strConcat(string memory _a, string memory _b, string memory _c) internal pure returns (string memory _concatenatedString) { 937 | return strConcat(_a, _b, _c, "", ""); 938 | } 939 | 940 | function strConcat(string memory _a, string memory _b, string memory _c, string memory _d) internal pure returns (string memory _concatenatedString) { 941 | return strConcat(_a, _b, _c, _d, ""); 942 | } 943 | 944 | function strConcat(string memory _a, string memory _b, string memory _c, string memory _d, string memory _e) internal pure returns (string memory _concatenatedString) { 945 | bytes memory _ba = bytes(_a); 946 | bytes memory _bb = bytes(_b); 947 | bytes memory _bc = bytes(_c); 948 | bytes memory _bd = bytes(_d); 949 | bytes memory _be = bytes(_e); 950 | string memory abcde = new string(_ba.length + _bb.length + _bc.length + _bd.length + _be.length); 951 | bytes memory babcde = bytes(abcde); 952 | uint k = 0; 953 | uint i = 0; 954 | for (i = 0; i < _ba.length; i++) { 955 | babcde[k++] = _ba[i]; 956 | } 957 | for (i = 0; i < _bb.length; i++) { 958 | babcde[k++] = _bb[i]; 959 | } 960 | for (i = 0; i < _bc.length; i++) { 961 | babcde[k++] = _bc[i]; 962 | } 963 | for (i = 0; i < _bd.length; i++) { 964 | babcde[k++] = _bd[i]; 965 | } 966 | for (i = 0; i < _be.length; i++) { 967 | babcde[k++] = _be[i]; 968 | } 969 | return string(babcde); 970 | } 971 | 972 | function safeParseInt(string memory _a) internal pure returns (uint _parsedInt) { 973 | return safeParseInt(_a, 0); 974 | } 975 | 976 | function safeParseInt(string memory _a, uint _b) internal pure returns (uint _parsedInt) { 977 | bytes memory bresult = bytes(_a); 978 | uint mint = 0; 979 | bool decimals = false; 980 | for (uint i = 0; i < bresult.length; i++) { 981 | if ((uint(uint8(bresult[i])) >= 48) && (uint(uint8(bresult[i])) <= 57)) { 982 | if (decimals) { 983 | if (_b == 0) break; 984 | else _b--; 985 | } 986 | mint *= 10; 987 | mint += uint(uint8(bresult[i])) - 48; 988 | } else if (uint(uint8(bresult[i])) == 46) { 989 | require(!decimals, 'More than one decimal encountered in string!'); 990 | decimals = true; 991 | } else { 992 | revert("Non-numeral character encountered in string!"); 993 | } 994 | } 995 | if (_b > 0) { 996 | mint *= 10 ** _b; 997 | } 998 | return mint; 999 | } 1000 | 1001 | function parseInt(string memory _a) internal pure returns (uint _parsedInt) { 1002 | return parseInt(_a, 0); 1003 | } 1004 | 1005 | function parseInt(string memory _a, uint _b) internal pure returns (uint _parsedInt) { 1006 | bytes memory bresult = bytes(_a); 1007 | uint mint = 0; 1008 | bool decimals = false; 1009 | for (uint i = 0; i < bresult.length; i++) { 1010 | if ((uint(uint8(bresult[i])) >= 48) && (uint(uint8(bresult[i])) <= 57)) { 1011 | if (decimals) { 1012 | if (_b == 0) { 1013 | break; 1014 | } else { 1015 | _b--; 1016 | } 1017 | } 1018 | mint *= 10; 1019 | mint += uint(uint8(bresult[i])) - 48; 1020 | } else if (uint(uint8(bresult[i])) == 46) { 1021 | decimals = true; 1022 | } 1023 | } 1024 | if (_b > 0) { 1025 | mint *= 10 ** _b; 1026 | } 1027 | return mint; 1028 | } 1029 | 1030 | function uint2str(uint _i) internal pure returns (string memory _uintAsString) { 1031 | if (_i == 0) { 1032 | return "0"; 1033 | } 1034 | uint j = _i; 1035 | uint len; 1036 | while (j != 0) { 1037 | len++; 1038 | j /= 10; 1039 | } 1040 | bytes memory bstr = new bytes(len); 1041 | uint k = len - 1; 1042 | while (_i != 0) { 1043 | bstr[k--] = byte(uint8(48 + _i % 10)); 1044 | _i /= 10; 1045 | } 1046 | return string(bstr); 1047 | } 1048 | 1049 | function stra2cbor(string[] memory _arr) internal pure returns (bytes memory _cborEncoding) { 1050 | safeMemoryCleaner(); 1051 | Buffer.buffer memory buf; 1052 | Buffer.init(buf, 1024); 1053 | buf.startArray(); 1054 | for (uint i = 0; i < _arr.length; i++) { 1055 | buf.encodeString(_arr[i]); 1056 | } 1057 | buf.endSequence(); 1058 | return buf.buf; 1059 | } 1060 | 1061 | function ba2cbor(bytes[] memory _arr) internal pure returns (bytes memory _cborEncoding) { 1062 | safeMemoryCleaner(); 1063 | Buffer.buffer memory buf; 1064 | Buffer.init(buf, 1024); 1065 | buf.startArray(); 1066 | for (uint i = 0; i < _arr.length; i++) { 1067 | buf.encodeBytes(_arr[i]); 1068 | } 1069 | buf.endSequence(); 1070 | return buf.buf; 1071 | } 1072 | 1073 | function provable_newRandomDSQuery(uint _delay, uint _nbytes, uint _customGasLimit) internal returns (bytes32 _queryId) { 1074 | require((_nbytes > 0) && (_nbytes <= 32)); 1075 | _delay *= 10; // Convert from seconds to ledger timer ticks 1076 | bytes memory nbytes = new bytes(1); 1077 | nbytes[0] = byte(uint8(_nbytes)); 1078 | bytes memory unonce = new bytes(32); 1079 | bytes memory sessionKeyHash = new bytes(32); 1080 | bytes32 sessionKeyHash_bytes32 = provable_randomDS_getSessionPubKeyHash(); 1081 | assembly { 1082 | mstore(unonce, 0x20) 1083 | /* 1084 | The following variables can be relaxed. 1085 | Check the relaxed random contract at https://github.com/oraclize/ethereum-examples 1086 | for an idea on how to override and replace commit hash variables. 1087 | */ 1088 | mstore(add(unonce, 0x20), xor(blockhash(sub(number, 1)), xor(coinbase, timestamp))) 1089 | mstore(sessionKeyHash, 0x20) 1090 | mstore(add(sessionKeyHash, 0x20), sessionKeyHash_bytes32) 1091 | } 1092 | bytes memory delay = new bytes(32); 1093 | assembly { 1094 | mstore(add(delay, 0x20), _delay) 1095 | } 1096 | bytes memory delay_bytes8 = new bytes(8); 1097 | copyBytes(delay, 24, 8, delay_bytes8, 0); 1098 | bytes[4] memory args = [unonce, nbytes, sessionKeyHash, delay]; 1099 | bytes32 queryId = provable_query("random", args, _customGasLimit); 1100 | bytes memory delay_bytes8_left = new bytes(8); 1101 | assembly { 1102 | let x := mload(add(delay_bytes8, 0x20)) 1103 | mstore8(add(delay_bytes8_left, 0x27), div(x, 0x100000000000000000000000000000000000000000000000000000000000000)) 1104 | mstore8(add(delay_bytes8_left, 0x26), div(x, 0x1000000000000000000000000000000000000000000000000000000000000)) 1105 | mstore8(add(delay_bytes8_left, 0x25), div(x, 0x10000000000000000000000000000000000000000000000000000000000)) 1106 | mstore8(add(delay_bytes8_left, 0x24), div(x, 0x100000000000000000000000000000000000000000000000000000000)) 1107 | mstore8(add(delay_bytes8_left, 0x23), div(x, 0x1000000000000000000000000000000000000000000000000000000)) 1108 | mstore8(add(delay_bytes8_left, 0x22), div(x, 0x10000000000000000000000000000000000000000000000000000)) 1109 | mstore8(add(delay_bytes8_left, 0x21), div(x, 0x100000000000000000000000000000000000000000000000000)) 1110 | mstore8(add(delay_bytes8_left, 0x20), div(x, 0x1000000000000000000000000000000000000000000000000)) 1111 | } 1112 | provable_randomDS_setCommitment(queryId, keccak256(abi.encodePacked(delay_bytes8_left, args[1], sha256(args[0]), args[2]))); 1113 | return queryId; 1114 | } 1115 | 1116 | function provable_randomDS_setCommitment(bytes32 _queryId, bytes32 _commitment) internal { 1117 | provable_randomDS_args[_queryId] = _commitment; 1118 | } 1119 | 1120 | function verifySig(bytes32 _tosignh, bytes memory _dersig, bytes memory _pubkey) internal returns (bool _sigVerified) { 1121 | bool sigok; 1122 | address signer; 1123 | bytes32 sigr; 1124 | bytes32 sigs; 1125 | bytes memory sigr_ = new bytes(32); 1126 | uint offset = 4 + (uint(uint8(_dersig[3])) - 0x20); 1127 | sigr_ = copyBytes(_dersig, offset, 32, sigr_, 0); 1128 | bytes memory sigs_ = new bytes(32); 1129 | offset += 32 + 2; 1130 | sigs_ = copyBytes(_dersig, offset + (uint(uint8(_dersig[offset - 1])) - 0x20), 32, sigs_, 0); 1131 | assembly { 1132 | sigr := mload(add(sigr_, 32)) 1133 | sigs := mload(add(sigs_, 32)) 1134 | } 1135 | (sigok, signer) = safer_ecrecover(_tosignh, 27, sigr, sigs); 1136 | if (address(uint160(uint256(keccak256(_pubkey)))) == signer) { 1137 | return true; 1138 | } else { 1139 | (sigok, signer) = safer_ecrecover(_tosignh, 28, sigr, sigs); 1140 | return (address(uint160(uint256(keccak256(_pubkey)))) == signer); 1141 | } 1142 | } 1143 | 1144 | function provable_randomDS_proofVerify__sessionKeyValidity(bytes memory _proof, uint _sig2offset) internal returns (bool _proofVerified) { 1145 | bool sigok; 1146 | // Random DS Proof Step 6: Verify the attestation signature, APPKEY1 must sign the sessionKey from the correct ledger app (CODEHASH) 1147 | bytes memory sig2 = new bytes(uint(uint8(_proof[_sig2offset + 1])) + 2); 1148 | copyBytes(_proof, _sig2offset, sig2.length, sig2, 0); 1149 | bytes memory appkey1_pubkey = new bytes(64); 1150 | copyBytes(_proof, 3 + 1, 64, appkey1_pubkey, 0); 1151 | bytes memory tosign2 = new bytes(1 + 65 + 32); 1152 | tosign2[0] = byte(uint8(1)); //role 1153 | copyBytes(_proof, _sig2offset - 65, 65, tosign2, 1); 1154 | bytes memory CODEHASH = hex"fd94fa71bc0ba10d39d464d0d8f465efeef0a2764e3887fcc9df41ded20f505c"; 1155 | copyBytes(CODEHASH, 0, 32, tosign2, 1 + 65); 1156 | sigok = verifySig(sha256(tosign2), sig2, appkey1_pubkey); 1157 | if (!sigok) { 1158 | return false; 1159 | } 1160 | // Random DS Proof Step 7: Verify the APPKEY1 provenance (must be signed by Ledger) 1161 | bytes memory LEDGERKEY = hex"7fb956469c5c9b89840d55b43537e66a98dd4811ea0a27224272c2e5622911e8537a2f8e86a46baec82864e98dd01e9ccc2f8bc5dfc9cbe5a91a290498dd96e4"; 1162 | bytes memory tosign3 = new bytes(1 + 65); 1163 | tosign3[0] = 0xFE; 1164 | copyBytes(_proof, 3, 65, tosign3, 1); 1165 | bytes memory sig3 = new bytes(uint(uint8(_proof[3 + 65 + 1])) + 2); 1166 | copyBytes(_proof, 3 + 65, sig3.length, sig3, 0); 1167 | sigok = verifySig(sha256(tosign3), sig3, LEDGERKEY); 1168 | return sigok; 1169 | } 1170 | 1171 | function provable_randomDS_proofVerify__returnCode(bytes32 _queryId, string memory _result, bytes memory _proof) internal returns (uint8 _returnCode) { 1172 | // Random DS Proof Step 1: The prefix has to match 'LP\x01' (Ledger Proof version 1) 1173 | if ((_proof[0] != "L") || (_proof[1] != "P") || (uint8(_proof[2]) != uint8(1))) { 1174 | return 1; 1175 | } 1176 | bool proofVerified = provable_randomDS_proofVerify__main(_proof, _queryId, bytes(_result), provable_getNetworkName()); 1177 | if (!proofVerified) { 1178 | return 2; 1179 | } 1180 | return 0; 1181 | } 1182 | 1183 | function matchBytes32Prefix(bytes32 _content, bytes memory _prefix, uint _nRandomBytes) internal pure returns (bool _matchesPrefix) { 1184 | bool match_ = true; 1185 | require(_prefix.length == _nRandomBytes); 1186 | for (uint256 i = 0; i< _nRandomBytes; i++) { 1187 | if (_content[i] != _prefix[i]) { 1188 | match_ = false; 1189 | } 1190 | } 1191 | return match_; 1192 | } 1193 | 1194 | function provable_randomDS_proofVerify__main(bytes memory _proof, bytes32 _queryId, bytes memory _result, string memory _contextName) internal returns (bool _proofVerified) { 1195 | // Random DS Proof Step 2: The unique keyhash has to match with the sha256 of (context name + _queryId) 1196 | uint ledgerProofLength = 3 + 65 + (uint(uint8(_proof[3 + 65 + 1])) + 2) + 32; 1197 | bytes memory keyhash = new bytes(32); 1198 | copyBytes(_proof, ledgerProofLength, 32, keyhash, 0); 1199 | if (!(keccak256(keyhash) == keccak256(abi.encodePacked(sha256(abi.encodePacked(_contextName, _queryId)))))) { 1200 | return false; 1201 | } 1202 | bytes memory sig1 = new bytes(uint(uint8(_proof[ledgerProofLength + (32 + 8 + 1 + 32) + 1])) + 2); 1203 | copyBytes(_proof, ledgerProofLength + (32 + 8 + 1 + 32), sig1.length, sig1, 0); 1204 | // Random DS Proof Step 3: We assume sig1 is valid (it will be verified during step 5) and we verify if '_result' is the _prefix of sha256(sig1) 1205 | if (!matchBytes32Prefix(sha256(sig1), _result, uint(uint8(_proof[ledgerProofLength + 32 + 8])))) { 1206 | return false; 1207 | } 1208 | // Random DS Proof Step 4: Commitment match verification, keccak256(delay, nbytes, unonce, sessionKeyHash) == commitment in storage. 1209 | // This is to verify that the computed args match with the ones specified in the query. 1210 | bytes memory commitmentSlice1 = new bytes(8 + 1 + 32); 1211 | copyBytes(_proof, ledgerProofLength + 32, 8 + 1 + 32, commitmentSlice1, 0); 1212 | bytes memory sessionPubkey = new bytes(64); 1213 | uint sig2offset = ledgerProofLength + 32 + (8 + 1 + 32) + sig1.length + 65; 1214 | copyBytes(_proof, sig2offset - 64, 64, sessionPubkey, 0); 1215 | bytes32 sessionPubkeyHash = sha256(sessionPubkey); 1216 | if (provable_randomDS_args[_queryId] == keccak256(abi.encodePacked(commitmentSlice1, sessionPubkeyHash))) { //unonce, nbytes and sessionKeyHash match 1217 | delete provable_randomDS_args[_queryId]; 1218 | } else return false; 1219 | // Random DS Proof Step 5: Validity verification for sig1 (keyhash and args signed with the sessionKey) 1220 | bytes memory tosign1 = new bytes(32 + 8 + 1 + 32); 1221 | copyBytes(_proof, ledgerProofLength, 32 + 8 + 1 + 32, tosign1, 0); 1222 | if (!verifySig(sha256(tosign1), sig1, sessionPubkey)) { 1223 | return false; 1224 | } 1225 | // Verify if sessionPubkeyHash was verified already, if not.. let's do it! 1226 | if (!provable_randomDS_sessionKeysHashVerified[sessionPubkeyHash]) { 1227 | provable_randomDS_sessionKeysHashVerified[sessionPubkeyHash] = provable_randomDS_proofVerify__sessionKeyValidity(_proof, sig2offset); 1228 | } 1229 | return provable_randomDS_sessionKeysHashVerified[sessionPubkeyHash]; 1230 | } 1231 | /* 1232 | The following function has been written by Alex Beregszaszi (@axic), use it under the terms of the MIT license 1233 | */ 1234 | function copyBytes(bytes memory _from, uint _fromOffset, uint _length, bytes memory _to, uint _toOffset) internal pure returns (bytes memory _copiedBytes) { 1235 | uint minLength = _length + _toOffset; 1236 | require(_to.length >= minLength); // Buffer too small. Should be a better way? 1237 | uint i = 32 + _fromOffset; // NOTE: the offset 32 is added to skip the `size` field of both bytes variables 1238 | uint j = 32 + _toOffset; 1239 | while (i < (32 + _fromOffset + _length)) { 1240 | assembly { 1241 | let tmp := mload(add(_from, i)) 1242 | mstore(add(_to, j), tmp) 1243 | } 1244 | i += 32; 1245 | j += 32; 1246 | } 1247 | return _to; 1248 | } 1249 | /* 1250 | The following function has been written by Alex Beregszaszi (@axic), use it under the terms of the MIT license 1251 | Duplicate Solidity's ecrecover, but catching the CALL return value 1252 | */ 1253 | function safer_ecrecover(bytes32 _hash, uint8 _v, bytes32 _r, bytes32 _s) internal returns (bool _success, address _recoveredAddress) { 1254 | /* 1255 | We do our own memory management here. Solidity uses memory offset 1256 | 0x40 to store the current end of memory. We write past it (as 1257 | writes are memory extensions), but don't update the offset so 1258 | Solidity will reuse it. The memory used here is only needed for 1259 | this context. 1260 | FIXME: inline assembly can't access return values 1261 | */ 1262 | bool ret; 1263 | address addr; 1264 | assembly { 1265 | let size := mload(0x40) 1266 | mstore(size, _hash) 1267 | mstore(add(size, 32), _v) 1268 | mstore(add(size, 64), _r) 1269 | mstore(add(size, 96), _s) 1270 | ret := call(3000, 1, 0, size, 128, size, 32) // NOTE: we can reuse the request memory because we deal with the return code. 1271 | addr := mload(size) 1272 | } 1273 | return (ret, addr); 1274 | } 1275 | /* 1276 | The following function has been written by Alex Beregszaszi (@axic), use it under the terms of the MIT license 1277 | */ 1278 | function ecrecovery(bytes32 _hash, bytes memory _sig) internal returns (bool _success, address _recoveredAddress) { 1279 | bytes32 r; 1280 | bytes32 s; 1281 | uint8 v; 1282 | if (_sig.length != 65) { 1283 | return (false, address(0)); 1284 | } 1285 | /* 1286 | The signature format is a compact form of: 1287 | {bytes32 r}{bytes32 s}{uint8 v} 1288 | Compact means, uint8 is not padded to 32 bytes. 1289 | */ 1290 | assembly { 1291 | r := mload(add(_sig, 32)) 1292 | s := mload(add(_sig, 64)) 1293 | /* 1294 | Here we are loading the last 32 bytes. We exploit the fact that 1295 | 'mload' will pad with zeroes if we overread. 1296 | There is no 'mload8' to do this, but that would be nicer. 1297 | */ 1298 | v := byte(0, mload(add(_sig, 96))) 1299 | /* 1300 | Alternative solution: 1301 | 'byte' is not working due to the Solidity parser, so lets 1302 | use the second best option, 'and' 1303 | v := and(mload(add(_sig, 65)), 255) 1304 | */ 1305 | } 1306 | /* 1307 | albeit non-transactional signatures are not specified by the YP, one would expect it 1308 | to match the YP range of [27, 28] 1309 | geth uses [0, 1] and some clients have followed. This might change, see: 1310 | https://github.com/ethereum/go-ethereum/issues/2053 1311 | */ 1312 | if (v < 27) { 1313 | v += 27; 1314 | } 1315 | if (v != 27 && v != 28) { 1316 | return (false, address(0)); 1317 | } 1318 | return safer_ecrecover(_hash, v, r, s); 1319 | } 1320 | 1321 | function safeMemoryCleaner() internal pure { 1322 | assembly { 1323 | let fmem := mload(0x40) 1324 | codecopy(fmem, codesize, sub(msize, fmem)) 1325 | } 1326 | } 1327 | } 1328 | // -------------------------------------------------------------------------------- /docs/dev.md: -------------------------------------------------------------------------------- 1 | ## Local Install 2 | make sure you have nodejs and npm on your environment, than: 3 | 4 | `npm install` 5 | 6 | ### Build Svelte UI & Serve 7 | ``` 8 | webpack 9 | npm start 10 | ``` 11 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | const path = require('path'); 3 | const http = require('http'); 4 | 5 | const port = process.env.PORT || 5000; 6 | 7 | express() 8 | .use(express.static(path.join(__dirname, 'public'))) 9 | .get('/', (req, res) => res.sendFile('index.html', {root: __dirname})) 10 | .listen(port, () => console.log(`Listening on ${port}`)); 11 | 12 | // keep alive ping 13 | if (process.env.WAKE_URL && process.env.WAKE_MS) { 14 | const wakeUp = () => http.get(process.env.WAKE_URL); 15 | setInterval(wakeUp, process.env.WAKE_MS); 16 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "bitpredict", 3 | "version": "1.0.1", 4 | "description": "Cryptocurrency Prediction", 5 | "main": "index.js", 6 | "scripts": { 7 | "build": "webpack", 8 | "start": "node index.js", 9 | "test": "echo \"Error: no test specified\" && exit 1" 10 | }, 11 | "dependencies": { 12 | "express": "^4.17.1", 13 | "web3": "^1.3.6" 14 | }, 15 | "devDependencies": { 16 | "chance": "^1.1.4", 17 | "copy-webpack-plugin": "^5.1.1", 18 | "cross-fetch": "latest", 19 | "css-loader": "^3.2.0", 20 | "cssnano": "latest", 21 | "file-loader": "^6.0.0", 22 | "html-webpack-plugin": "^3.2.0", 23 | "mini-css-extract-plugin": "^0.8.0", 24 | "optimize-css-assets-webpack-plugin": "^5.0.3", 25 | "page": "^1.11.5", 26 | "qrcode": "latest", 27 | "request": "^2.88.0", 28 | "sass": "^1.35.1", 29 | "sass-loader": "^8.0.0", 30 | "style-loader": "^1.0.0", 31 | "svelte": "^3.9.1", 32 | "svelte-loader": "^2.13.6", 33 | "svelte-select": "^3.7.0", 34 | "tape": "^4.10.1", 35 | "url-loader": "^4.1.0", 36 | "web3": "^1.3.6", 37 | "webpack": "^4.39.3", 38 | "webpack-cli": "^3.3.10", 39 | "webpack-livereload-plugin": "^2.2.0" 40 | }, 41 | "repository": { 42 | "type": "git", 43 | "url": "https://github.com/bitspent/bitpredict.git" 44 | }, 45 | "keywords": [ 46 | "crypto", 47 | "bitcoin", 48 | "ethereum", 49 | "betting", 50 | "blockchain" 51 | ], 52 | "author": "Fady Aro ", 53 | "license": "ARO" 54 | } 55 | -------------------------------------------------------------------------------- /src/web/css/index.css: -------------------------------------------------------------------------------- 1 | .slider { 2 | -webkit-appearance: none; 3 | width: 100%; 4 | height: 25px; 5 | outline: none; 6 | opacity: 0.7; 7 | -webkit-transition: .2s; 8 | transition: opacity .2s; 9 | } 10 | 11 | .slider:hover { 12 | opacity: 1; 13 | } 14 | 15 | .slider::-webkit-slider-thumb { 16 | -webkit-appearance: none; 17 | appearance: none; 18 | width: 25px; 19 | height: 25px; 20 | background: orange; 21 | cursor: pointer; 22 | } 23 | 24 | .slider::-moz-range-thumb { 25 | width: 25px; 26 | height: 25px; 27 | background: orange; 28 | cursor: pointer; 29 | } 30 | 31 | * { 32 | -khtml-user-select: none; 33 | -webkit-user-select: none; 34 | -ms-user-select: none; 35 | user-select: none; 36 | font-family: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; 37 | -moz-user-select: none; 38 | -o-user-select: none; 39 | -webkit-user-drag: none; 40 | } 41 | 42 | .w3-container, .w3-panel { 43 | padding: 0 16px !important; 44 | } 45 | 46 | html, body { 47 | margin: 0; 48 | padding: 0; 49 | } 50 | 51 | .m-background { 52 | position: fixed; 53 | top: 0; 54 | left: 0; 55 | width: 100%; 56 | height: 100%; 57 | background: url(../icons/texture.png); 58 | opacity: 1; 59 | z-index: -1; 60 | } 61 | 62 | .loader, 63 | .loader:before, 64 | .loader:after { 65 | background: #ffffff; 66 | -webkit-animation: load1 1s infinite ease-in-out; 67 | animation: load1 1s infinite ease-in-out; 68 | width: 1em; 69 | height: 1em; 70 | 71 | } 72 | 73 | .loader { 74 | color: #ffffff; 75 | text-indent: -9999em; 76 | font-size: 4px; 77 | margin-top: 2px; 78 | -webkit-transform: translateZ(0); 79 | -ms-transform: translateZ(0); 80 | transform: translateZ(0); 81 | -webkit-animation-delay: -0.16s; 82 | animation-delay: -0.16s; 83 | } 84 | 85 | .loader:before, 86 | .loader:after { 87 | position: absolute; 88 | top: 0; 89 | content: ''; 90 | } 91 | 92 | .loader:before { 93 | left: -1.5em; 94 | -webkit-animation-delay: -0.32s; 95 | animation-delay: -0.32s; 96 | } 97 | 98 | .loader:after { 99 | left: 1.5em; 100 | } 101 | 102 | @-webkit-keyframes load1 { 103 | 0%, 104 | 80%, 105 | 100% { 106 | box-shadow: 0 0; 107 | height: 4em; 108 | } 109 | 40% { 110 | box-shadow: 0 -2em; 111 | height: 5em; 112 | } 113 | } 114 | 115 | @keyframes load1 { 116 | 0%, 117 | 80%, 118 | 100% { 119 | box-shadow: 0 0; 120 | height: 4em; 121 | } 122 | 40% { 123 | box-shadow: 0 -2em; 124 | height: 5em; 125 | } 126 | } 127 | 128 | ::-webkit-scrollbar { 129 | width: 0.5em; 130 | height: 0.5em; 131 | } 132 | 133 | ::-webkit-scrollbar-thumb { 134 | background: #060d75; 135 | } 136 | 137 | ::-webkit-scrollbar-track { 138 | background: #9000ff; 139 | } 140 | 141 | body { 142 | scrollbar-face-color: #060d75; 143 | scrollbar-track-color: #9000ff; 144 | } 145 | 146 | input[type=number]::-webkit-inner-spin-button, 147 | input[type=number]::-webkit-outer-spin-button { 148 | -webkit-appearance: none; 149 | margin: 0; 150 | } 151 | 152 | input[type=number] { 153 | -moz-appearance: textfield; 154 | } -------------------------------------------------------------------------------- /src/web/css/w3.css: -------------------------------------------------------------------------------- 1 | /* W3.CSS 4.13 June 2019 by Jan Egil and Borge Refsnes */ 2 | html{box-sizing:border-box}*,*:before,*:after{box-sizing:inherit} 3 | /* Extract from normalize.css by Nicolas Gallagher and Jonathan Neal git.io/normalize */ 4 | html{-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}body{margin:0} 5 | article,aside,details,figcaption,figure,footer,header,main,menu,nav,section{display:block}summary{display:list-item} 6 | audio,canvas,progress,video{display:inline-block}progress{vertical-align:baseline} 7 | audio:not([controls]){display:none;height:0}[hidden],template{display:none} 8 | a{background-color:transparent}a:active,a:hover{outline-width:0} 9 | abbr[title]{border-bottom:none;text-decoration:underline;text-decoration:underline dotted} 10 | b,strong{font-weight:bolder}dfn{font-style:italic}mark{background:#ff0;color:#000} 11 | small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline} 12 | sub{bottom:-0.25em}sup{top:-0.5em}figure{margin:1em 40px}img{border-style:none} 13 | code,kbd,pre,samp{font-family:monospace,monospace;font-size:1em}hr{box-sizing:content-box;height:0;overflow:visible} 14 | button,input,select,textarea,optgroup{font:inherit;margin:0}optgroup{font-weight:bold} 15 | button,input{overflow:visible}button,select{text-transform:none} 16 | button,[type=button],[type=reset],[type=submit]{-webkit-appearance:button} 17 | button::-moz-focus-inner,[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner{border-style:none;padding:0} 18 | button:-moz-focusring,[type=button]:-moz-focusring,[type=reset]:-moz-focusring,[type=submit]:-moz-focusring{outline:1px dotted ButtonText} 19 | fieldset{border:1px solid #c0c0c0;margin:0 2px;padding:.35em .625em .75em} 20 | legend{color:inherit;display:table;max-width:100%;padding:0;white-space:normal}textarea{overflow:auto} 21 | [type=checkbox],[type=radio]{padding:0} 22 | [type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto} 23 | [type=search]{-webkit-appearance:textfield;outline-offset:-2px} 24 | [type=search]::-webkit-search-decoration{-webkit-appearance:none} 25 | ::-webkit-file-upload-button{-webkit-appearance:button;font:inherit} 26 | /* End extract */ 27 | html,body{font-family:Verdana,sans-serif;font-size:15px;line-height:1.5}html{overflow-x:hidden} 28 | h1{font-size:36px}h2{font-size:30px}h3{font-size:24px}h4{font-size:20px}h5{font-size:18px}h6{font-size:16px}.w3-serif{font-family:serif} 29 | h1,h2,h3,h4,h5,h6{font-family:"Segoe UI",Arial,sans-serif;font-weight:400;margin:10px 0}.w3-wide{letter-spacing:4px} 30 | hr{border:0;border-top:1px solid #eee;margin:20px 0} 31 | .w3-image{max-width:100%;height:auto}img{vertical-align:middle}a{color:inherit} 32 | .w3-table,.w3-table-all{border-collapse:collapse;border-spacing:0;width:100%;display:table}.w3-table-all{border:1px solid #ccc} 33 | .w3-bordered tr,.w3-table-all tr{border-bottom:1px solid #ddd}.w3-striped tbody tr:nth-child(even){background-color:#f1f1f1} 34 | .w3-table-all tr:nth-child(odd){background-color:#fff}.w3-table-all tr:nth-child(even){background-color:#f1f1f1} 35 | .w3-hoverable tbody tr:hover,.w3-ul.w3-hoverable li:hover{background-color:#ccc}.w3-centered tr th,.w3-centered tr td{text-align:center} 36 | .w3-table td,.w3-table th,.w3-table-all td,.w3-table-all th{padding:8px 8px;display:table-cell;text-align:left;vertical-align:top} 37 | .w3-table th:first-child,.w3-table td:first-child,.w3-table-all th:first-child,.w3-table-all td:first-child{padding-left:16px} 38 | .w3-btn,.w3-button{border:none;display:inline-block;padding:8px 16px;vertical-align:middle;overflow:hidden;text-decoration:none;color:inherit;background-color:inherit;text-align:center;cursor:pointer;white-space:nowrap} 39 | .w3-btn:hover{box-shadow:0 8px 16px 0 rgba(0,0,0,0.2),0 6px 20px 0 rgba(0,0,0,0.19)} 40 | .w3-btn,.w3-button{-webkit-touch-callout:none;-webkit-user-select:none;-khtml-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none} 41 | .w3-disabled,.w3-btn:disabled,.w3-button:disabled{cursor:not-allowed;opacity:0.3}.w3-disabled *,:disabled *{pointer-events:none} 42 | .w3-btn.w3-disabled:hover,.w3-btn:disabled:hover{box-shadow:none} 43 | .w3-badge,.w3-tag{background-color:#000;color:#fff;display:inline-block;padding-left:8px;padding-right:8px;text-align:center}.w3-badge{border-radius:50%} 44 | .w3-ul{list-style-type:none;padding:0;margin:0}.w3-ul li{padding:8px 16px;border-bottom:1px solid #ddd}.w3-ul li:last-child{border-bottom:none} 45 | .w3-tooltip,.w3-display-container{position:relative}.w3-tooltip .w3-text{display:none}.w3-tooltip:hover .w3-text{display:inline-block} 46 | .w3-ripple:active{opacity:0.5}.w3-ripple{transition:opacity 0s} 47 | .w3-input{padding:8px;display:block;border:none;border-bottom:1px solid #ccc;width:100%} 48 | .w3-select{padding:9px 0;width:100%;border:none;border-bottom:1px solid #ccc} 49 | .w3-dropdown-click,.w3-dropdown-hover{position:relative;display:inline-block;cursor:pointer} 50 | .w3-dropdown-hover:hover .w3-dropdown-content{display:block} 51 | .w3-dropdown-hover:first-child,.w3-dropdown-click:hover{background-color:#ccc;color:#000} 52 | .w3-dropdown-hover:hover > .w3-button:first-child,.w3-dropdown-click:hover > .w3-button:first-child{background-color:#ccc;color:#000} 53 | .w3-dropdown-content{cursor:auto;color:#000;background-color:#fff;display:none;position:absolute;min-width:160px;margin:0;padding:0;z-index:1} 54 | .w3-check,.w3-radio{width:24px;height:24px;position:relative;top:6px} 55 | .w3-sidebar{height:100%;width:200px;background-color:#fff;position:fixed!important;z-index:1;overflow:auto} 56 | .w3-bar-block .w3-dropdown-hover,.w3-bar-block .w3-dropdown-click{width:100%} 57 | .w3-bar-block .w3-dropdown-hover .w3-dropdown-content,.w3-bar-block .w3-dropdown-click .w3-dropdown-content{min-width:100%} 58 | .w3-bar-block .w3-dropdown-hover .w3-button,.w3-bar-block .w3-dropdown-click .w3-button{width:100%;text-align:left;padding:8px 16px} 59 | .w3-main,#main{transition:margin-left .4s} 60 | .w3-modal{z-index:3;display:none;padding-top:100px;position:fixed;left:0;top:0;width:100%;height:100%;overflow:auto;background-color:rgb(0,0,0);background-color:rgba(0,0,0,0.4)} 61 | .w3-modal-content{margin:auto;background-color:#fff;position:relative;padding:0;outline:0;width:600px} 62 | .w3-bar{width:100%;overflow:hidden}.w3-center .w3-bar{display:inline-block;width:auto} 63 | .w3-bar .w3-bar-item{padding:8px 16px;float:left;width:auto;border:none;display:block;outline:0} 64 | .w3-bar .w3-dropdown-hover,.w3-bar .w3-dropdown-click{position:static;float:left} 65 | .w3-bar .w3-button{white-space:normal} 66 | .w3-bar-block .w3-bar-item{width:100%;display:block;padding:8px 16px;text-align:left;border:none;white-space:normal;float:none;outline:0} 67 | .w3-bar-block.w3-center .w3-bar-item{text-align:center}.w3-block{display:block;width:100%} 68 | .w3-responsive{display:block;overflow-x:auto} 69 | .w3-container:after,.w3-container:before,.w3-panel:after,.w3-panel:before,.w3-row:after,.w3-row:before,.w3-row-padding:after,.w3-row-padding:before, 70 | .w3-cell-row:before,.w3-cell-row:after,.w3-clear:after,.w3-clear:before,.w3-bar:before,.w3-bar:after{content:"";display:table;clear:both} 71 | .w3-col,.w3-half,.w3-third,.w3-twothird,.w3-threequarter,.w3-quarter{float:left;width:100%} 72 | .w3-col.s1{width:8.33333%}.w3-col.s2{width:16.66666%}.w3-col.s3{width:24.99999%}.w3-col.s4{width:33.33333%} 73 | .w3-col.s5{width:41.66666%}.w3-col.s6{width:49.99999%}.w3-col.s7{width:58.33333%}.w3-col.s8{width:66.66666%} 74 | .w3-col.s9{width:74.99999%}.w3-col.s10{width:83.33333%}.w3-col.s11{width:91.66666%}.w3-col.s12{width:99.99999%} 75 | @media (min-width:601px){.w3-col.m1{width:8.33333%}.w3-col.m2{width:16.66666%}.w3-col.m3,.w3-quarter{width:24.99999%}.w3-col.m4,.w3-third{width:33.33333%} 76 | .w3-col.m5{width:41.66666%}.w3-col.m6,.w3-half{width:49.99999%}.w3-col.m7{width:58.33333%}.w3-col.m8,.w3-twothird{width:66.66666%} 77 | .w3-col.m9,.w3-threequarter{width:74.99999%}.w3-col.m10{width:83.33333%}.w3-col.m11{width:91.66666%}.w3-col.m12{width:99.99999%}} 78 | @media (min-width:993px){.w3-col.l1{width:8.33333%}.w3-col.l2{width:16.66666%}.w3-col.l3{width:24.99999%}.w3-col.l4{width:33.33333%} 79 | .w3-col.l5{width:41.66666%}.w3-col.l6{width:49.99999%}.w3-col.l7{width:58.33333%}.w3-col.l8{width:66.66666%} 80 | .w3-col.l9{width:74.99999%}.w3-col.l10{width:83.33333%}.w3-col.l11{width:91.66666%}.w3-col.l12{width:99.99999%}} 81 | .w3-rest{overflow:hidden}.w3-stretch{margin-left:-16px;margin-right:-16px} 82 | .w3-content,.w3-auto{margin-left:auto;margin-right:auto}.w3-content{max-width:980px}.w3-auto{max-width:1140px} 83 | .w3-cell-row{display:table;width:100%}.w3-cell{display:table-cell} 84 | .w3-cell-top{vertical-align:top}.w3-cell-middle{vertical-align:middle}.w3-cell-bottom{vertical-align:bottom} 85 | .w3-hide{display:none!important}.w3-show-block,.w3-show{display:block!important}.w3-show-inline-block{display:inline-block!important} 86 | @media (max-width:1205px){.w3-auto{max-width:95%}} 87 | @media (max-width:600px){.w3-modal-content{margin:0 10px;width:auto!important}.w3-modal{padding-top:30px} 88 | .w3-dropdown-hover.w3-mobile .w3-dropdown-content,.w3-dropdown-click.w3-mobile .w3-dropdown-content{position:relative} 89 | .w3-hide-small{display:none!important}.w3-mobile{display:block;width:100%!important}.w3-bar-item.w3-mobile,.w3-dropdown-hover.w3-mobile,.w3-dropdown-click.w3-mobile{text-align:center} 90 | .w3-dropdown-hover.w3-mobile,.w3-dropdown-hover.w3-mobile .w3-btn,.w3-dropdown-hover.w3-mobile .w3-button,.w3-dropdown-click.w3-mobile,.w3-dropdown-click.w3-mobile .w3-btn,.w3-dropdown-click.w3-mobile .w3-button{width:100%}} 91 | @media (max-width:768px){.w3-modal-content{width:500px}.w3-modal{padding-top:50px}} 92 | @media (min-width:993px){.w3-modal-content{width:900px}.w3-hide-large{display:none!important}.w3-sidebar.w3-collapse{display:block!important}} 93 | @media (max-width:992px) and (min-width:601px){.w3-hide-medium{display:none!important}} 94 | @media (max-width:992px){.w3-sidebar.w3-collapse{display:none}.w3-main{margin-left:0!important;margin-right:0!important}.w3-auto{max-width:100%}} 95 | .w3-top,.w3-bottom{position:fixed;width:100%;z-index:1}.w3-top{top:0}.w3-bottom{bottom:0} 96 | .w3-overlay{position:fixed;display:none;width:100%;height:100%;top:0;left:0;right:0;bottom:0;background-color:rgba(0,0,0,0.5);z-index:2} 97 | .w3-display-topleft{position:absolute;left:0;top:0}.w3-display-topright{position:absolute;right:0;top:0} 98 | .w3-display-bottomleft{position:absolute;left:0;bottom:0}.w3-display-bottomright{position:absolute;right:0;bottom:0} 99 | .w3-display-middle{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);-ms-transform:translate(-50%,-50%)} 100 | .w3-display-left{position:absolute;top:50%;left:0%;transform:translate(0%,-50%);-ms-transform:translate(-0%,-50%)} 101 | .w3-display-right{position:absolute;top:50%;right:0%;transform:translate(0%,-50%);-ms-transform:translate(0%,-50%)} 102 | .w3-display-topmiddle{position:absolute;left:50%;top:0;transform:translate(-50%,0%);-ms-transform:translate(-50%,0%)} 103 | .w3-display-bottommiddle{position:absolute;left:50%;bottom:0;transform:translate(-50%,0%);-ms-transform:translate(-50%,0%)} 104 | .w3-display-container:hover .w3-display-hover{display:block}.w3-display-container:hover span.w3-display-hover{display:inline-block}.w3-display-hover{display:none} 105 | .w3-display-position{position:absolute} 106 | .w3-circle{border-radius:50%} 107 | .w3-round-small{border-radius:2px}.w3-round,.w3-round-medium{border-radius:4px}.w3-round-large{border-radius:8px}.w3-round-xlarge{border-radius:16px}.w3-round-xxlarge{border-radius:32px} 108 | .w3-row-padding,.w3-row-padding>.w3-half,.w3-row-padding>.w3-third,.w3-row-padding>.w3-twothird,.w3-row-padding>.w3-threequarter,.w3-row-padding>.w3-quarter,.w3-row-padding>.w3-col{padding:0 8px} 109 | .w3-container,.w3-panel{padding:0.01em 16px}.w3-panel{margin-top:16px;margin-bottom:16px} 110 | .w3-code,.w3-codespan{font-family:Consolas,"courier new";font-size:16px} 111 | .w3-code{width:auto;background-color:#fff;padding:8px 12px;border-left:4px solid #4CAF50;word-wrap:break-word} 112 | .w3-codespan{color:crimson;background-color:#f1f1f1;padding-left:4px;padding-right:4px;font-size:110%} 113 | .w3-card,.w3-card-2{box-shadow:0 2px 5px 0 rgba(0,0,0,0.16),0 2px 10px 0 rgba(0,0,0,0.12)} 114 | .w3-card-4,.w3-hover-shadow:hover{box-shadow:0 4px 10px 0 rgba(0,0,0,0.2),0 4px 20px 0 rgba(0,0,0,0.19)} 115 | .w3-spin{animation:w3-spin 2s infinite linear}@keyframes w3-spin{0%{transform:rotate(0deg)}100%{transform:rotate(359deg)}} 116 | .w3-animate-fading{animation:fading 10s infinite}@keyframes fading{0%{opacity:0}50%{opacity:1}100%{opacity:0}} 117 | .w3-animate-opacity{animation:opac 0.8s}@keyframes opac{from{opacity:0} to{opacity:1}} 118 | .w3-animate-top{position:relative;animation:animatetop 0.4s}@keyframes animatetop{from{top:-300px;opacity:0} to{top:0;opacity:1}} 119 | .w3-animate-left{position:relative;animation:animateleft 0.4s}@keyframes animateleft{from{left:-300px;opacity:0} to{left:0;opacity:1}} 120 | .w3-animate-right{position:relative;animation:animateright 0.4s}@keyframes animateright{from{right:-300px;opacity:0} to{right:0;opacity:1}} 121 | .w3-animate-bottom{position:relative;animation:animatebottom 0.4s}@keyframes animatebottom{from{bottom:-300px;opacity:0} to{bottom:0;opacity:1}} 122 | .w3-animate-zoom {animation:animatezoom 0.6s}@keyframes animatezoom{from{transform:scale(0)} to{transform:scale(1)}} 123 | .w3-animate-input{transition:width 0.4s ease-in-out}.w3-animate-input:focus{width:100%!important} 124 | .w3-opacity,.w3-hover-opacity:hover{opacity:0.60}.w3-opacity-off,.w3-hover-opacity-off:hover{opacity:1} 125 | .w3-opacity-max{opacity:0.25}.w3-opacity-min{opacity:0.75} 126 | .w3-greyscale-max,.w3-grayscale-max,.w3-hover-greyscale:hover,.w3-hover-grayscale:hover{filter:grayscale(100%)} 127 | .w3-greyscale,.w3-grayscale{filter:grayscale(75%)}.w3-greyscale-min,.w3-grayscale-min{filter:grayscale(50%)} 128 | .w3-sepia{filter:sepia(75%)}.w3-sepia-max,.w3-hover-sepia:hover{filter:sepia(100%)}.w3-sepia-min{filter:sepia(50%)} 129 | .w3-tiny{font-size:10px!important}.w3-small{font-size:12px!important}.w3-medium{font-size:15px!important}.w3-large{font-size:18px!important} 130 | .w3-xlarge{font-size:24px!important}.w3-xxlarge{font-size:36px!important}.w3-xxxlarge{font-size:48px!important}.w3-jumbo{font-size:64px!important} 131 | .w3-left-align{text-align:left!important}.w3-right-align{text-align:right!important}.w3-justify{text-align:justify!important}.w3-center{text-align:center!important} 132 | .w3-border-0{border:0!important}.w3-border{border:1px solid #ccc!important} 133 | .w3-border-top{border-top:1px solid #ccc!important}.w3-border-bottom{border-bottom:1px solid #ccc!important} 134 | .w3-border-left{border-left:1px solid #ccc!important}.w3-border-right{border-right:1px solid #ccc!important} 135 | .w3-topbar{border-top:6px solid #ccc!important}.w3-bottombar{border-bottom:6px solid #ccc!important} 136 | .w3-leftbar{border-left:6px solid #ccc!important}.w3-rightbar{border-right:6px solid #ccc!important} 137 | .w3-section,.w3-code{margin-top:16px!important;margin-bottom:16px!important} 138 | .w3-margin{margin:16px!important}.w3-margin-top{margin-top:16px!important}.w3-margin-bottom{margin-bottom:16px!important} 139 | .w3-margin-left{margin-left:16px!important}.w3-margin-right{margin-right:16px!important} 140 | .w3-padding-small{padding:4px 8px!important}.w3-padding{padding:8px 16px!important}.w3-padding-large{padding:12px 24px!important} 141 | .w3-padding-16{padding-top:16px!important;padding-bottom:16px!important}.w3-padding-24{padding-top:24px!important;padding-bottom:24px!important} 142 | .w3-padding-32{padding-top:32px!important;padding-bottom:32px!important}.w3-padding-48{padding-top:48px!important;padding-bottom:48px!important} 143 | .w3-padding-64{padding-top:64px!important;padding-bottom:64px!important} 144 | .w3-left{float:left!important}.w3-right{float:right!important} 145 | .w3-button:hover{color:#000!important;background-color:#ccc!important} 146 | .w3-transparent,.w3-hover-none:hover{background-color:transparent!important} 147 | .w3-hover-none:hover{box-shadow:none!important} 148 | /* Colors */ 149 | .w3-amber,.w3-hover-amber:hover{color:#000!important;background-color:#ffc107!important} 150 | .w3-aqua,.w3-hover-aqua:hover{color:#000!important;background-color:#00ffff!important} 151 | .w3-blue,.w3-hover-blue:hover{color:#fff!important;background-color:#2196F3!important} 152 | .w3-light-blue,.w3-hover-light-blue:hover{color:#000!important;background-color:#87CEEB!important} 153 | .w3-brown,.w3-hover-brown:hover{color:#fff!important;background-color:#795548!important} 154 | .w3-cyan,.w3-hover-cyan:hover{color:#000!important;background-color:#00bcd4!important} 155 | .w3-blue-grey,.w3-hover-blue-grey:hover,.w3-blue-gray,.w3-hover-blue-gray:hover{color:#fff!important;background-color:#607d8b!important} 156 | .w3-green,.w3-hover-green:hover{color:#fff!important;background-color:#4CAF50!important} 157 | .w3-light-green,.w3-hover-light-green:hover{color:#000!important;background-color:#8bc34a!important} 158 | .w3-indigo,.w3-hover-indigo:hover{color:#fff!important;background-color:#3f51b5!important} 159 | .w3-khaki,.w3-hover-khaki:hover{color:#000!important;background-color:#f0e68c!important} 160 | .w3-lime,.w3-hover-lime:hover{color:#000!important;background-color:#cddc39!important} 161 | .w3-orange,.w3-hover-orange:hover{color:#000!important;background-color:#ff9800!important} 162 | .w3-deep-orange,.w3-hover-deep-orange:hover{color:#fff!important;background-color:#ff5722!important} 163 | .w3-pink,.w3-hover-pink:hover{color:#fff!important;background-color:#e91e63!important} 164 | .w3-purple,.w3-hover-purple:hover{color:#fff!important;background-color:#9c27b0!important} 165 | .w3-deep-purple,.w3-hover-deep-purple:hover{color:#fff!important;background-color:#673ab7!important} 166 | .w3-red,.w3-hover-red:hover{color:#fff!important;background-color:#f44336!important} 167 | .w3-sand,.w3-hover-sand:hover{color:#000!important;background-color:#fdf5e6!important} 168 | .w3-teal,.w3-hover-teal:hover{color:#fff!important;background-color:#009688!important} 169 | .w3-yellow,.w3-hover-yellow:hover{color:#000!important;background-color:#ffeb3b!important} 170 | .w3-white,.w3-hover-white:hover{color:#000!important;background-color:#fff!important} 171 | .w3-black,.w3-hover-black:hover{color:#fff!important;background-color:#000!important} 172 | .w3-grey,.w3-hover-grey:hover,.w3-gray,.w3-hover-gray:hover{color:#000!important;background-color:#9e9e9e!important} 173 | .w3-light-grey,.w3-hover-light-grey:hover,.w3-light-gray,.w3-hover-light-gray:hover{color:#000!important;background-color:#f1f1f1!important} 174 | .w3-dark-grey,.w3-hover-dark-grey:hover,.w3-dark-gray,.w3-hover-dark-gray:hover{color:#fff!important;background-color:#616161!important} 175 | .w3-pale-red,.w3-hover-pale-red:hover{color:#000!important;background-color:#ffdddd!important} 176 | .w3-pale-green,.w3-hover-pale-green:hover{color:#000!important;background-color:#ddffdd!important} 177 | .w3-pale-yellow,.w3-hover-pale-yellow:hover{color:#000!important;background-color:#ffffcc!important} 178 | .w3-pale-blue,.w3-hover-pale-blue:hover{color:#000!important;background-color:#ddffff!important} 179 | .w3-text-amber,.w3-hover-text-amber:hover{color:#ffc107!important} 180 | .w3-text-aqua,.w3-hover-text-aqua:hover{color:#00ffff!important} 181 | .w3-text-blue,.w3-hover-text-blue:hover{color:#2196F3!important} 182 | .w3-text-light-blue,.w3-hover-text-light-blue:hover{color:#87CEEB!important} 183 | .w3-text-brown,.w3-hover-text-brown:hover{color:#795548!important} 184 | .w3-text-cyan,.w3-hover-text-cyan:hover{color:#00bcd4!important} 185 | .w3-text-blue-grey,.w3-hover-text-blue-grey:hover,.w3-text-blue-gray,.w3-hover-text-blue-gray:hover{color:#607d8b!important} 186 | .w3-text-green,.w3-hover-text-green:hover{color:#4CAF50!important} 187 | .w3-text-light-green,.w3-hover-text-light-green:hover{color:#8bc34a!important} 188 | .w3-text-indigo,.w3-hover-text-indigo:hover{color:#3f51b5!important} 189 | .w3-text-khaki,.w3-hover-text-khaki:hover{color:#b4aa50!important} 190 | .w3-text-lime,.w3-hover-text-lime:hover{color:#cddc39!important} 191 | .w3-text-orange,.w3-hover-text-orange:hover{color:#ff9800!important} 192 | .w3-text-deep-orange,.w3-hover-text-deep-orange:hover{color:#ff5722!important} 193 | .w3-text-pink,.w3-hover-text-pink:hover{color:#e91e63!important} 194 | .w3-text-purple,.w3-hover-text-purple:hover{color:#9c27b0!important} 195 | .w3-text-deep-purple,.w3-hover-text-deep-purple:hover{color:#673ab7!important} 196 | .w3-text-red,.w3-hover-text-red:hover{color:#f44336!important} 197 | .w3-text-sand,.w3-hover-text-sand:hover{color:#fdf5e6!important} 198 | .w3-text-teal,.w3-hover-text-teal:hover{color:#009688!important} 199 | .w3-text-yellow,.w3-hover-text-yellow:hover{color:#d2be0e!important} 200 | .w3-text-white,.w3-hover-text-white:hover{color:#fff!important} 201 | .w3-text-black,.w3-hover-text-black:hover{color:#000!important} 202 | .w3-text-grey,.w3-hover-text-grey:hover,.w3-text-gray,.w3-hover-text-gray:hover{color:#757575!important} 203 | .w3-text-light-grey,.w3-hover-text-light-grey:hover,.w3-text-light-gray,.w3-hover-text-light-gray:hover{color:#f1f1f1!important} 204 | .w3-text-dark-grey,.w3-hover-text-dark-grey:hover,.w3-text-dark-gray,.w3-hover-text-dark-gray:hover{color:#3a3a3a!important} 205 | .w3-border-amber,.w3-hover-border-amber:hover{border-color:#ffc107!important} 206 | .w3-border-aqua,.w3-hover-border-aqua:hover{border-color:#00ffff!important} 207 | .w3-border-blue,.w3-hover-border-blue:hover{border-color:#2196F3!important} 208 | .w3-border-light-blue,.w3-hover-border-light-blue:hover{border-color:#87CEEB!important} 209 | .w3-border-brown,.w3-hover-border-brown:hover{border-color:#795548!important} 210 | .w3-border-cyan,.w3-hover-border-cyan:hover{border-color:#00bcd4!important} 211 | .w3-border-blue-grey,.w3-hover-border-blue-grey:hover,.w3-border-blue-gray,.w3-hover-border-blue-gray:hover{border-color:#607d8b!important} 212 | .w3-border-green,.w3-hover-border-green:hover{border-color:#4CAF50!important} 213 | .w3-border-light-green,.w3-hover-border-light-green:hover{border-color:#8bc34a!important} 214 | .w3-border-indigo,.w3-hover-border-indigo:hover{border-color:#3f51b5!important} 215 | .w3-border-khaki,.w3-hover-border-khaki:hover{border-color:#f0e68c!important} 216 | .w3-border-lime,.w3-hover-border-lime:hover{border-color:#cddc39!important} 217 | .w3-border-orange,.w3-hover-border-orange:hover{border-color:#ff9800!important} 218 | .w3-border-deep-orange,.w3-hover-border-deep-orange:hover{border-color:#ff5722!important} 219 | .w3-border-pink,.w3-hover-border-pink:hover{border-color:#e91e63!important} 220 | .w3-border-purple,.w3-hover-border-purple:hover{border-color:#9c27b0!important} 221 | .w3-border-deep-purple,.w3-hover-border-deep-purple:hover{border-color:#673ab7!important} 222 | .w3-border-red,.w3-hover-border-red:hover{border-color:#f44336!important} 223 | .w3-border-sand,.w3-hover-border-sand:hover{border-color:#fdf5e6!important} 224 | .w3-border-teal,.w3-hover-border-teal:hover{border-color:#009688!important} 225 | .w3-border-yellow,.w3-hover-border-yellow:hover{border-color:#ffeb3b!important} 226 | .w3-border-white,.w3-hover-border-white:hover{border-color:#fff!important} 227 | .w3-border-black,.w3-hover-border-black:hover{border-color:#000!important} 228 | .w3-border-grey,.w3-hover-border-grey:hover,.w3-border-gray,.w3-hover-border-gray:hover{border-color:#9e9e9e!important} 229 | .w3-border-light-grey,.w3-hover-border-light-grey:hover,.w3-border-light-gray,.w3-hover-border-light-gray:hover{border-color:#f1f1f1!important} 230 | .w3-border-dark-grey,.w3-hover-border-dark-grey:hover,.w3-border-dark-gray,.w3-hover-border-dark-gray:hover{border-color:#616161!important} 231 | .w3-border-pale-red,.w3-hover-border-pale-red:hover{border-color:#ffe7e7!important}.w3-border-pale-green,.w3-hover-border-pale-green:hover{border-color:#e7ffe7!important} 232 | .w3-border-pale-yellow,.w3-hover-border-pale-yellow:hover{border-color:#ffffcc!important}.w3-border-pale-blue,.w3-hover-border-pale-blue:hover{border-color:#e7ffff!important} -------------------------------------------------------------------------------- /src/web/icons/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitspent/bitpredict/c63d749fbfec659e4e446ff59411cb363cc67023/src/web/icons/favicon.ico -------------------------------------------------------------------------------- /src/web/icons/icon-192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitspent/bitpredict/c63d749fbfec659e4e446ff59411cb363cc67023/src/web/icons/icon-192.png -------------------------------------------------------------------------------- /src/web/icons/icon-512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitspent/bitpredict/c63d749fbfec659e4e446ff59411cb363cc67023/src/web/icons/icon-512.png -------------------------------------------------------------------------------- /src/web/icons/icon.svg: -------------------------------------------------------------------------------- 1 | 3 | 4 | 6 | 8 | 11 | 13 | 14 | -------------------------------------------------------------------------------- /src/web/icons/texture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitspent/bitpredict/c63d749fbfec659e4e446ff59411cb363cc67023/src/web/icons/texture.png -------------------------------------------------------------------------------- /src/web/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | BitPredict 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /src/web/js/BitEth.js: -------------------------------------------------------------------------------- 1 | let BitEth = { 2 | /* 3 | * Get Default Account[0] 4 | */ 5 | getAddressAsync() { 6 | return new Promise(async (res, rej) => { 7 | if (typeof window.web3 !== 'undefined' && typeof window.web3.currentProvider !== 'undefined') { 8 | const accounts = await ethereum.request({method: 'eth_accounts'}); 9 | if (accounts && accounts.length === 0) { 10 | await ethereum.enable(); 11 | window.location.reload(); 12 | } else { 13 | res(accounts[0]); 14 | } 15 | } 16 | }); 17 | }, 18 | cutAddress(address) { 19 | return address.substring(0, 8) + '...' + address.substring(36); 20 | } 21 | }; 22 | 23 | export default BitEth; -------------------------------------------------------------------------------- /src/web/js/BitPredict.js: -------------------------------------------------------------------------------- 1 | import abis from './abis'; 2 | import _Web3 from "web3"; 3 | 4 | /* 5 | * Predict Client 6 | */ 7 | let BitPredict = { 8 | 9 | abi: abis.bitPredict, 10 | 11 | address: '', 12 | 13 | setAddress: function (address) { 14 | BitPredict.address = address; 15 | }, 16 | 17 | instance() { 18 | const web3NoAccount = new _Web3(web3.currentProvider); 19 | return new web3NoAccount.eth.Contract(BitPredict.abi, BitPredict.address, {}); 20 | }, 21 | 22 | instanceAt(address) { 23 | const web3NoAccount = new _Web3(web3.currentProvider); 24 | let contract = new web3NoAccount.eth.Contract(BitPredict.abi, address, {}); 25 | return { 26 | callAsync: async function () { 27 | let method = contract.methods[arguments[0]]; 28 | let args = Array.prototype.slice.apply(arguments).splice(1); 29 | let func = args.length > 0 ? method.call(this, args[0]) : method.call(); 30 | return await func.call(); 31 | } 32 | } 33 | }, 34 | 35 | async callContractAsync() { 36 | let method = BitPredict.instance().methods[arguments[0]]; 37 | let args = Array.prototype.slice.apply(arguments).splice(1); 38 | let func = args.length > 0 ? method.call(this, args[0]) : method.call(); 39 | return await func.call(); 40 | }, 41 | 42 | placeBetAsync(address, userPrediction, ticketEth) { 43 | return new Promise((res, rej) => { 44 | let price = Number(userPrediction).toFixed(2) * 100; 45 | let wei = ticketEth * 1e18 + price; 46 | const OP_COST = 150000; 47 | web3.eth.sendTransaction({ 48 | from: address, 49 | to: BitPredict.address, 50 | value: wei, 51 | gas: OP_COST 52 | }, (e, r) => e && rej(e) || res(r) 53 | ); 54 | }); 55 | }, 56 | 57 | async callAsync() { 58 | let method = BitPredict.instance().methods[arguments[0]]; 59 | let args = Array.prototype.slice.apply(arguments).splice(1); 60 | let func = args.length > 0 ? method.call(this, args[0]) : method.call(); 61 | return await func.call(); 62 | }, 63 | 64 | watchContractBets(cb) { 65 | BitPredict 66 | .instance() 67 | .events['PriceBet']({filter: {}, fromBlock: 0}, function (error, event) { 68 | if (error) { 69 | console.log(`error while watching factory events: ${error}`); 70 | } 71 | }).on('data', function (event) { 72 | console.log(event); 73 | cb && cb(event) 74 | }).on('changed', function (event) { 75 | 76 | }).on('error', console.error); 77 | }, 78 | 79 | watchContractWins(cb) { 80 | BitPredict 81 | .instance() 82 | .events['WinnerPayout']({filter: {}, fromBlock: 0}, function (error, event) { 83 | if (error) { 84 | console.log(`error while watching factory events: ${error}`); 85 | } 86 | }).on('data', function (event) { 87 | console.log(event); 88 | cb && cb(event) 89 | }).on('changed', function (event) { 90 | 91 | }).on('error', console.error); 92 | } 93 | } 94 | ; 95 | 96 | export default BitPredict; -------------------------------------------------------------------------------- /src/web/js/BitPredictFactory.js: -------------------------------------------------------------------------------- 1 | import abis from './abis'; 2 | import _Web3 from 'web3'; 3 | 4 | let BitPredictFactory = { 5 | 6 | abi: abis.bitPredictFactory, 7 | 8 | // test net: 0x0221bD8DC990512F7c7FF3417d5436BB52C32a56 9 | // main net: 0x4e8f1ccf050e59ec0939a34417ae9e175f862943 10 | address: '0x0221bD8DC990512F7c7FF3417d5436BB52C32a56', 11 | 12 | instance() { 13 | const web3NoAccount = new _Web3(web3.currentProvider); 14 | return new web3NoAccount.eth.Contract(BitPredictFactory.abi, BitPredictFactory.address, {}); 15 | }, 16 | 17 | createPredictionContractAsync(address, bytes9Ticker, uintWeiTicket, uintClosureDelay, uintExecutionDelay, uintFees) { 18 | return new Promise(async (resolve, reject) => { 19 | const PROVABLE_COST = 0.012e18; 20 | let contractPrice = await BitPredictFactory.callAsync('contractPrice'); 21 | BitPredictFactory 22 | .instance() 23 | .methods 24 | .createPredictionContract(bytes9Ticker, uintWeiTicket, uintClosureDelay, uintExecutionDelay, uintFees) 25 | .send({ 26 | from: address, 27 | value: PROVABLE_COST 28 | }, (err, res) => err && reject(err) || resolve(res)); 29 | }); 30 | }, 31 | 32 | async callAsync() { 33 | let method = BitPredictFactory.instance().methods[arguments[0]]; 34 | let args = Array.prototype.slice.apply(arguments).splice(1); 35 | let func = method.call(args); 36 | return await func.call(); 37 | }, 38 | 39 | watchFactory(cb) { 40 | // noinspection JSUnresolvedFunction 41 | BitPredictFactory 42 | .instance() 43 | .events['PredictionContractCreated']({ 44 | filter: {}, // Using an array means OR: e.g. 20 or 23 45 | fromBlock: 0 46 | }, function (error, event) { 47 | if (error) { 48 | console.log(`error while watching factory events: ${error}`); 49 | } 50 | }).on('data', function (event) { 51 | console.log(event); 52 | cb && cb(event) 53 | }).on('changed', function (event) { 54 | 55 | }).on('error', console.error); 56 | } 57 | } 58 | ; 59 | 60 | export default BitPredictFactory; -------------------------------------------------------------------------------- /src/web/js/abis.js: -------------------------------------------------------------------------------- 1 | let abis = { 2 | 3 | bitPredict: [ 4 | { 5 | "inputs": [ 6 | { 7 | "internalType": "address payable", 8 | "name": "creator", 9 | "type": "address" 10 | }, 11 | { 12 | "internalType": "uint256", 13 | "name": "winPer", 14 | "type": "uint256" 15 | }, 16 | { 17 | "internalType": "uint256", 18 | "name": "weiTicket", 19 | "type": "uint256" 20 | }, 21 | { 22 | "internalType": "uint256", 23 | "name": "closeDelay", 24 | "type": "uint256" 25 | }, 26 | { 27 | "internalType": "uint256", 28 | "name": "executeDelay", 29 | "type": "uint256" 30 | }, 31 | { 32 | "internalType": "bytes9", 33 | "name": "pair", 34 | "type": "bytes9" 35 | }, 36 | { 37 | "internalType": "string", 38 | "name": "apiResolver", 39 | "type": "string" 40 | } 41 | ], 42 | "payable": true, 43 | "stateMutability": "payable", 44 | "type": "constructor" 45 | }, 46 | { 47 | "anonymous": false, 48 | "inputs": [ 49 | { 50 | "indexed": false, 51 | "internalType": "address", 52 | "name": "user", 53 | "type": "address" 54 | }, 55 | { 56 | "indexed": false, 57 | "internalType": "uint256", 58 | "name": "bet", 59 | "type": "uint256" 60 | }, 61 | { 62 | "indexed": false, 63 | "internalType": "uint256", 64 | "name": "time", 65 | "type": "uint256" 66 | } 67 | ], 68 | "name": "PriceBet", 69 | "type": "event" 70 | }, 71 | { 72 | "anonymous": false, 73 | "inputs": [], 74 | "name": "RoundClose", 75 | "type": "event" 76 | }, 77 | { 78 | "anonymous": false, 79 | "inputs": [ 80 | { 81 | "indexed": false, 82 | "internalType": "uint256", 83 | "name": "solution", 84 | "type": "uint256" 85 | } 86 | ], 87 | "name": "RoundExecuted", 88 | "type": "event" 89 | }, 90 | { 91 | "anonymous": false, 92 | "inputs": [ 93 | { 94 | "indexed": false, 95 | "internalType": "address", 96 | "name": "winner", 97 | "type": "address" 98 | }, 99 | { 100 | "indexed": false, 101 | "internalType": "uint256", 102 | "name": "solution", 103 | "type": "uint256" 104 | }, 105 | { 106 | "indexed": false, 107 | "internalType": "uint256", 108 | "name": "guess", 109 | "type": "uint256" 110 | }, 111 | { 112 | "indexed": false, 113 | "internalType": "uint256", 114 | "name": "reward", 115 | "type": "uint256" 116 | } 117 | ], 118 | "name": "WinnerPayout", 119 | "type": "event" 120 | }, 121 | { 122 | "payable": true, 123 | "stateMutability": "payable", 124 | "type": "fallback" 125 | }, 126 | { 127 | "constant": false, 128 | "inputs": [], 129 | "name": "closeSubscriptions", 130 | "outputs": [], 131 | "payable": false, 132 | "stateMutability": "nonpayable", 133 | "type": "function" 134 | }, 135 | { 136 | "constant": true, 137 | "inputs": [], 138 | "name": "closureDelay", 139 | "outputs": [ 140 | { 141 | "internalType": "uint256", 142 | "name": "", 143 | "type": "uint256" 144 | } 145 | ], 146 | "payable": false, 147 | "stateMutability": "view", 148 | "type": "function" 149 | }, 150 | { 151 | "constant": true, 152 | "inputs": [], 153 | "name": "counters", 154 | "outputs": [ 155 | { 156 | "internalType": "uint256", 157 | "name": "subs", 158 | "type": "uint256" 159 | }, 160 | { 161 | "internalType": "uint256", 162 | "name": "wins", 163 | "type": "uint256" 164 | } 165 | ], 166 | "payable": false, 167 | "stateMutability": "view", 168 | "type": "function" 169 | }, 170 | { 171 | "constant": true, 172 | "inputs": [], 173 | "name": "created", 174 | "outputs": [ 175 | { 176 | "internalType": "uint256", 177 | "name": "", 178 | "type": "uint256" 179 | } 180 | ], 181 | "payable": false, 182 | "stateMutability": "view", 183 | "type": "function" 184 | }, 185 | { 186 | "constant": false, 187 | "inputs": [], 188 | "name": "distributeRewards", 189 | "outputs": [], 190 | "payable": false, 191 | "stateMutability": "nonpayable", 192 | "type": "function" 193 | }, 194 | { 195 | "constant": false, 196 | "inputs": [ 197 | { 198 | "internalType": "uint256", 199 | "name": "solution", 200 | "type": "uint256" 201 | } 202 | ], 203 | "name": "executeResult", 204 | "outputs": [ 205 | { 206 | "internalType": "uint256", 207 | "name": "gasLimit", 208 | "type": "uint256" 209 | } 210 | ], 211 | "payable": false, 212 | "stateMutability": "nonpayable", 213 | "type": "function" 214 | }, 215 | { 216 | "constant": true, 217 | "inputs": [], 218 | "name": "executionDelay", 219 | "outputs": [ 220 | { 221 | "internalType": "uint256", 222 | "name": "", 223 | "type": "uint256" 224 | } 225 | ], 226 | "payable": false, 227 | "stateMutability": "view", 228 | "type": "function" 229 | }, 230 | { 231 | "constant": true, 232 | "inputs": [ 233 | { 234 | "internalType": "address", 235 | "name": "", 236 | "type": "address" 237 | } 238 | ], 239 | "name": "futures", 240 | "outputs": [ 241 | { 242 | "internalType": "uint256", 243 | "name": "", 244 | "type": "uint256" 245 | } 246 | ], 247 | "payable": false, 248 | "stateMutability": "view", 249 | "type": "function" 250 | }, 251 | { 252 | "constant": true, 253 | "inputs": [], 254 | "name": "resolver", 255 | "outputs": [ 256 | { 257 | "internalType": "string", 258 | "name": "", 259 | "type": "string" 260 | } 261 | ], 262 | "payable": false, 263 | "stateMutability": "view", 264 | "type": "function" 265 | }, 266 | { 267 | "constant": false, 268 | "inputs": [], 269 | "name": "rollback", 270 | "outputs": [], 271 | "payable": false, 272 | "stateMutability": "nonpayable", 273 | "type": "function" 274 | }, 275 | { 276 | "constant": true, 277 | "inputs": [], 278 | "name": "status", 279 | "outputs": [ 280 | { 281 | "internalType": "uint256", 282 | "name": "", 283 | "type": "uint256" 284 | } 285 | ], 286 | "payable": false, 287 | "stateMutability": "view", 288 | "type": "function" 289 | }, 290 | { 291 | "constant": true, 292 | "inputs": [ 293 | { 294 | "internalType": "uint256", 295 | "name": "", 296 | "type": "uint256" 297 | } 298 | ], 299 | "name": "subscribers", 300 | "outputs": [ 301 | { 302 | "internalType": "address payable", 303 | "name": "", 304 | "type": "address" 305 | } 306 | ], 307 | "payable": false, 308 | "stateMutability": "view", 309 | "type": "function" 310 | }, 311 | { 312 | "constant": true, 313 | "inputs": [], 314 | "name": "ticker", 315 | "outputs": [ 316 | { 317 | "internalType": "bytes9", 318 | "name": "", 319 | "type": "bytes9" 320 | } 321 | ], 322 | "payable": false, 323 | "stateMutability": "view", 324 | "type": "function" 325 | }, 326 | { 327 | "constant": true, 328 | "inputs": [], 329 | "name": "ticketPriceWei", 330 | "outputs": [ 331 | { 332 | "internalType": "uint256", 333 | "name": "", 334 | "type": "uint256" 335 | } 336 | ], 337 | "payable": false, 338 | "stateMutability": "view", 339 | "type": "function" 340 | }, 341 | { 342 | "constant": true, 343 | "inputs": [], 344 | "name": "winGuess", 345 | "outputs": [ 346 | { 347 | "internalType": "uint256", 348 | "name": "", 349 | "type": "uint256" 350 | } 351 | ], 352 | "payable": false, 353 | "stateMutability": "view", 354 | "type": "function" 355 | }, 356 | { 357 | "constant": true, 358 | "inputs": [], 359 | "name": "winReward", 360 | "outputs": [ 361 | { 362 | "internalType": "uint256", 363 | "name": "", 364 | "type": "uint256" 365 | } 366 | ], 367 | "payable": false, 368 | "stateMutability": "view", 369 | "type": "function" 370 | }, 371 | { 372 | "constant": true, 373 | "inputs": [], 374 | "name": "winShare", 375 | "outputs": [ 376 | { 377 | "internalType": "uint256", 378 | "name": "", 379 | "type": "uint256" 380 | } 381 | ], 382 | "payable": false, 383 | "stateMutability": "view", 384 | "type": "function" 385 | }, 386 | { 387 | "constant": true, 388 | "inputs": [], 389 | "name": "winSolution", 390 | "outputs": [ 391 | { 392 | "internalType": "uint256", 393 | "name": "", 394 | "type": "uint256" 395 | } 396 | ], 397 | "payable": false, 398 | "stateMutability": "view", 399 | "type": "function" 400 | }, 401 | { 402 | "constant": true, 403 | "inputs": [ 404 | { 405 | "internalType": "uint256", 406 | "name": "", 407 | "type": "uint256" 408 | } 409 | ], 410 | "name": "winners", 411 | "outputs": [ 412 | { 413 | "internalType": "address", 414 | "name": "", 415 | "type": "address" 416 | } 417 | ], 418 | "payable": false, 419 | "stateMutability": "view", 420 | "type": "function" 421 | } 422 | ], 423 | 424 | bitPredictFactory: [ 425 | { 426 | "constant": false, 427 | "inputs": [ 428 | { 429 | "internalType": "bytes32", 430 | "name": "_myid", 431 | "type": "bytes32" 432 | }, 433 | { 434 | "internalType": "string", 435 | "name": "_result", 436 | "type": "string" 437 | } 438 | ], 439 | "name": "__callback", 440 | "outputs": [], 441 | "payable": false, 442 | "stateMutability": "nonpayable", 443 | "type": "function" 444 | }, 445 | { 446 | "constant": false, 447 | "inputs": [ 448 | { 449 | "internalType": "bytes32", 450 | "name": "_myid", 451 | "type": "bytes32" 452 | }, 453 | { 454 | "internalType": "string", 455 | "name": "_result", 456 | "type": "string" 457 | }, 458 | { 459 | "internalType": "bytes", 460 | "name": "_proof", 461 | "type": "bytes" 462 | } 463 | ], 464 | "name": "__callback", 465 | "outputs": [], 466 | "payable": false, 467 | "stateMutability": "nonpayable", 468 | "type": "function" 469 | }, 470 | { 471 | "constant": false, 472 | "inputs": [ 473 | { 474 | "internalType": "bytes9", 475 | "name": "ticker", 476 | "type": "bytes9" 477 | }, 478 | { 479 | "internalType": "uint256", 480 | "name": "weiTicket", 481 | "type": "uint256" 482 | }, 483 | { 484 | "internalType": "uint256", 485 | "name": "closureDelay", 486 | "type": "uint256" 487 | }, 488 | { 489 | "internalType": "uint256", 490 | "name": "executionDelay", 491 | "type": "uint256" 492 | }, 493 | { 494 | "internalType": "uint256", 495 | "name": "fees", 496 | "type": "uint256" 497 | } 498 | ], 499 | "name": "createPredictionContract", 500 | "outputs": [], 501 | "payable": true, 502 | "stateMutability": "payable", 503 | "type": "function" 504 | }, 505 | { 506 | "inputs": [], 507 | "payable": false, 508 | "stateMutability": "nonpayable", 509 | "type": "constructor" 510 | }, 511 | { 512 | "anonymous": false, 513 | "inputs": [ 514 | { 515 | "indexed": true, 516 | "internalType": "address", 517 | "name": "previousOwner", 518 | "type": "address" 519 | }, 520 | { 521 | "indexed": true, 522 | "internalType": "address", 523 | "name": "newOwner", 524 | "type": "address" 525 | } 526 | ], 527 | "name": "OwnershipTransferred", 528 | "type": "event" 529 | }, 530 | { 531 | "anonymous": false, 532 | "inputs": [ 533 | { 534 | "indexed": true, 535 | "internalType": "address", 536 | "name": "creator", 537 | "type": "address" 538 | }, 539 | { 540 | "indexed": false, 541 | "internalType": "address", 542 | "name": "bitPredict", 543 | "type": "address" 544 | }, 545 | { 546 | "indexed": false, 547 | "internalType": "bytes9", 548 | "name": "ticker", 549 | "type": "bytes9" 550 | }, 551 | { 552 | "indexed": false, 553 | "internalType": "uint256", 554 | "name": "weiTicket", 555 | "type": "uint256" 556 | }, 557 | { 558 | "indexed": false, 559 | "internalType": "uint256", 560 | "name": "closureDelay", 561 | "type": "uint256" 562 | }, 563 | { 564 | "indexed": false, 565 | "internalType": "uint256", 566 | "name": "executionDelay", 567 | "type": "uint256" 568 | }, 569 | { 570 | "indexed": false, 571 | "internalType": "uint256", 572 | "name": "fees", 573 | "type": "uint256" 574 | }, 575 | { 576 | "indexed": false, 577 | "internalType": "uint256", 578 | "name": "created", 579 | "type": "uint256" 580 | } 581 | ], 582 | "name": "PredictionContractCreated", 583 | "type": "event" 584 | }, 585 | { 586 | "constant": false, 587 | "inputs": [ 588 | { 589 | "internalType": "bytes9", 590 | "name": "ticker", 591 | "type": "bytes9" 592 | } 593 | ], 594 | "name": "removeTicker", 595 | "outputs": [], 596 | "payable": false, 597 | "stateMutability": "nonpayable", 598 | "type": "function" 599 | }, 600 | { 601 | "constant": false, 602 | "inputs": [ 603 | { 604 | "internalType": "uint256", 605 | "name": "priceWei", 606 | "type": "uint256" 607 | } 608 | ], 609 | "name": "setContractPrice", 610 | "outputs": [], 611 | "payable": false, 612 | "stateMutability": "nonpayable", 613 | "type": "function" 614 | }, 615 | { 616 | "constant": false, 617 | "inputs": [ 618 | { 619 | "internalType": "bytes9", 620 | "name": "ticker", 621 | "type": "bytes9" 622 | }, 623 | { 624 | "internalType": "string", 625 | "name": "resolver", 626 | "type": "string" 627 | } 628 | ], 629 | "name": "setTickerResolver", 630 | "outputs": [], 631 | "payable": false, 632 | "stateMutability": "nonpayable", 633 | "type": "function" 634 | }, 635 | { 636 | "constant": false, 637 | "inputs": [ 638 | { 639 | "internalType": "address payable", 640 | "name": "newOwner", 641 | "type": "address" 642 | } 643 | ], 644 | "name": "transferOwnership", 645 | "outputs": [], 646 | "payable": false, 647 | "stateMutability": "nonpayable", 648 | "type": "function" 649 | }, 650 | { 651 | "payable": true, 652 | "stateMutability": "payable", 653 | "type": "fallback" 654 | }, 655 | { 656 | "constant": false, 657 | "inputs": [], 658 | "name": "withdraw", 659 | "outputs": [], 660 | "payable": false, 661 | "stateMutability": "nonpayable", 662 | "type": "function" 663 | }, 664 | { 665 | "constant": true, 666 | "inputs": [ 667 | { 668 | "internalType": "uint256", 669 | "name": "", 670 | "type": "uint256" 671 | } 672 | ], 673 | "name": "bitPredicts", 674 | "outputs": [ 675 | { 676 | "internalType": "address", 677 | "name": "", 678 | "type": "address" 679 | } 680 | ], 681 | "payable": false, 682 | "stateMutability": "view", 683 | "type": "function" 684 | }, 685 | { 686 | "constant": true, 687 | "inputs": [], 688 | "name": "contractPrice", 689 | "outputs": [ 690 | { 691 | "internalType": "uint256", 692 | "name": "", 693 | "type": "uint256" 694 | } 695 | ], 696 | "payable": false, 697 | "stateMutability": "view", 698 | "type": "function" 699 | }, 700 | { 701 | "constant": true, 702 | "inputs": [], 703 | "name": "counter", 704 | "outputs": [ 705 | { 706 | "internalType": "uint256", 707 | "name": "", 708 | "type": "uint256" 709 | } 710 | ], 711 | "payable": false, 712 | "stateMutability": "view", 713 | "type": "function" 714 | }, 715 | { 716 | "constant": true, 717 | "inputs": [], 718 | "name": "owner", 719 | "outputs": [ 720 | { 721 | "internalType": "address payable", 722 | "name": "", 723 | "type": "address" 724 | } 725 | ], 726 | "payable": false, 727 | "stateMutability": "view", 728 | "type": "function" 729 | }, 730 | { 731 | "constant": true, 732 | "inputs": [ 733 | { 734 | "internalType": "uint256", 735 | "name": "", 736 | "type": "uint256" 737 | } 738 | ], 739 | "name": "tickers", 740 | "outputs": [ 741 | { 742 | "internalType": "bytes9", 743 | "name": "", 744 | "type": "bytes9" 745 | } 746 | ], 747 | "payable": false, 748 | "stateMutability": "view", 749 | "type": "function" 750 | }, 751 | { 752 | "constant": true, 753 | "inputs": [], 754 | "name": "tickersList", 755 | "outputs": [ 756 | { 757 | "internalType": "bytes9[]", 758 | "name": "", 759 | "type": "bytes9[]" 760 | } 761 | ], 762 | "payable": false, 763 | "stateMutability": "view", 764 | "type": "function" 765 | }, 766 | { 767 | "constant": true, 768 | "inputs": [ 769 | { 770 | "internalType": "bytes9", 771 | "name": "", 772 | "type": "bytes9" 773 | } 774 | ], 775 | "name": "tickersResolvers", 776 | "outputs": [ 777 | { 778 | "internalType": "string", 779 | "name": "", 780 | "type": "string" 781 | } 782 | ], 783 | "payable": false, 784 | "stateMutability": "view", 785 | "type": "function" 786 | } 787 | ] 788 | 789 | }; 790 | 791 | export default abis; -------------------------------------------------------------------------------- /src/web/js/index.js: -------------------------------------------------------------------------------- 1 | import '../css/w3.css'; 2 | import '../css/index.css'; 3 | import App from '../svelte/App.svelte'; 4 | 5 | function onlyHttps() { 6 | window.location.protocol !== "https:" 7 | && window.location.href.indexOf('localhost') < 0 8 | && (window.location.href = window.location.href.replace('http:', 'https:')); 9 | } 10 | 11 | new App({target: document.getElementsByTagName('app')[0]}); 12 | 13 | // onlyHttps(); 14 | -------------------------------------------------------------------------------- /src/web/svelte/App.svelte: -------------------------------------------------------------------------------- 1 |
2 | 3 | {#if web3Warn} 4 | 5 | {:else if (!queryUser && !queryContract)} 6 | 7 | 8 |
9 |
10 | 11 |
12 |
13 | 14 |
15 |
16 | {:else if (queryUser)} 17 | 18 |
19 |
20 | Contracts Created by Wallet {BitEth.cutAddress(queryUser)} 21 |
22 |
23 |
24 |
25 |
26 | 27 |
28 |
29 | {:else if (queryContract)} 30 | 31 |
32 |
33 | Contract: {BitEth.cutAddress(queryContract)} 34 |

35 |
36 | 37 |
38 |
39 |
40 | {#if userOpen} 41 | 44 | {:else if userExecuted} 45 |
46 | 47 |
48 |
49 | 50 |
51 | {:else} 52 |
53 | 54 |
55 | {/if} 56 |
57 | {/if} 58 |

59 | 70 |
71 | -------------------------------------------------------------------------------- /src/web/svelte/BetRow.svelte: -------------------------------------------------------------------------------- 1 | 6 | {#if user.toLowerCase() === event.user.toLowerCase()} 7 | 8 | YOU 9 | {event.bet/100} 10 | {new Date(event.time*1000).toLocaleString()} 11 | 12 | {:else} 13 | 14 | {BitEth.cutAddress(event.user)} 15 | {event.bet/100} 16 | {new Date(event.time*1000).toLocaleString()} 17 | 18 | {/if} -------------------------------------------------------------------------------- /src/web/svelte/BitPredictHelper.svelte: -------------------------------------------------------------------------------- 1 |
2 |
3 |
helperHidden = !helperHidden} 4 | class="w3-button w3-blue w3-hover-black w3-left-align"> 5 |
Crypto Price Betting {@html helperHidden ? '⊽' : 'x'}
6 |
7 |
8 |

Create your crypto pricing betting contract and challenge your friends. 9 | Provably Fair Smart Contracts Powered By 10 | Provable 11 | Oracles. 12 |

13 |
14 |
15 |
How it works?
16 |

17 | Once deployed, share the participation link.
18 | • Anyone can join before the Closure Date.
19 | • Winner(s) are announced on the Execution Date. 20 |

21 |
22 |
23 |
Jackpot Winner?
24 |

25 | When execution date is reached, the Jackpot is shared equally with the winners. 26 |
• The closest price prediction(s) to the Ticker on the execution date is the 27 | winning result 28 |
• Only one participation (prediction) per wallet (address) 29 |

30 |
31 |
32 |
33 |
34 |
35 | -------------------------------------------------------------------------------- /src/web/svelte/ContractCountdown.svelte: -------------------------------------------------------------------------------- 1 | 117 | {@html status} -------------------------------------------------------------------------------- /src/web/svelte/ContractFactory.svelte: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 | 5 | ⓘ Create Round 7 | Make Your Own Round 9 | 10 |
11 |
12 | 14 | Explore ⬥ 15 | 16 |
17 |
18 |
19 | 33 |
34 |
35 | 43 | 53 | 63 | 71 | 75 |
76 |
77 | -------------------------------------------------------------------------------- /src/web/svelte/ContractRow.svelte: -------------------------------------------------------------------------------- 1 | 12 | 13 | 14 | {#if event.open} 15 | 18 | Bet 19 | 20 | {:else} 21 | 24 | 🔍 25 | 26 | {/if} 27 | 28 | {event.ticker} 29 | {event.ticket} 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /src/web/svelte/ContractSubscribers.svelte: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |
Users Bets
5 |
6 | 7 | 8 | 9 | 10 | 11 | 12 | {#each items as event} 13 | 14 | {/each} 15 |
AddressPrice BetTime
16 |
17 |
18 | -------------------------------------------------------------------------------- /src/web/svelte/ContractWinners.svelte: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |
Winner(s)
5 |
6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | {#each items as event} 14 | 15 | {/each} 16 |
AddressPrice BetSolutionWon
17 |
18 |
19 | 20 | -------------------------------------------------------------------------------- /src/web/svelte/ContractsFactory.svelte: -------------------------------------------------------------------------------- 1 |
2 |
3 |
Recent Contracts
4 | My Contracts 6 |
7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | {#each items as event} 15 | 16 | {/each} 17 |
PairTicketStatus
18 |
19 | 20 | -------------------------------------------------------------------------------- /src/web/svelte/ModalBetTransaction.svelte: -------------------------------------------------------------------------------- 1 | {#if showPending} 2 |
3 |
4 |
5 | {#if showClose} 6 | showPending = false} style="z-index: 1" 7 | class="w3-button w3-xlarge w3-text-black w3-display-topright">× 8 |
9 |
Your Price Bet on {userTicker}: {userPrediction}
10 |
11 | {:else} 12 |
13 |
14 |
15 | { pendingTx ? 'Broadcasting Bet Transaction' : 'Loading Contract From Ethereum' } 16 | ({counter} sec) 17 |
18 | {#if pendingTx != null} 19 | 22 | {/if} 23 |
24 | {/if} 25 |
26 |
27 |
28 | {/if} 29 | 30 | -------------------------------------------------------------------------------- /src/web/svelte/ModalFactoryContract.svelte: -------------------------------------------------------------------------------- 1 | {#if showPending} 2 |
3 |
4 |
5 | {#if showClose} 6 | showPending = false} style="z-index: 1" 7 | class="w3-button w3-text-black w3-xlarge w3-display-topright">× 8 |
9 |
Your Contract Is Deployed
10 |

Copy & Share the Contract Link:

11 | 15 | Share via Whatsapp 18 |
19 | {:else} 20 | {#if pendingTx === null} 21 |
22 |
23 |
24 | Loading Contract From Ethereum ({counter} sec) 25 |
26 |
27 | {:else} 28 |
29 |
30 |
31 | Pushing Contract Tx to Ethereum ({counter} sec) 32 |
33 | 37 |
38 | {/if} 39 | {/if} 40 |
41 |
42 |
43 | {/if} 44 | -------------------------------------------------------------------------------- /src/web/svelte/ModalUserContract.svelte: -------------------------------------------------------------------------------- 1 | {#if showPending} 2 |
3 |
4 |
5 | {#if pendingTx === null} 6 |
7 |
8 |
9 | Loading Contracts From Ethereum ({counter} sec) 10 |
11 |
12 | {/if} 13 |
14 |
15 |
16 | {/if} 17 | -------------------------------------------------------------------------------- /src/web/svelte/NavigationBar.svelte: -------------------------------------------------------------------------------- 1 |
2 | Ξ BitPredict 3 |
contractAddress ? window.open('?contract='+contractAddress, '_blank'): ''} 5 | class="w3-bar-item w3-button w3-small w3-black w3-right">Go 6 |
7 | 11 |
12 | -------------------------------------------------------------------------------- /src/web/svelte/UserContractBet.svelte: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |
Ticker Prediction
5 |
{userTicker} / {tickerPrice}
6 |
7 |
8 | 18 |
19 | House Fees: {userFees}% 20 | Jackpot: {userJackpot.toFixed(2)} ETH 21 |
22 |
23 |
24 | 34 | {#if qrURIData} 35 |
36 | OR Just Scan 37 |
38 | QRCode 39 |
40 | For Supported Wallets 41 |
42 | {/if} 43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
Users Bets
51 |
52 | 53 | 54 | 55 | 56 | 57 | 58 | {#each items as event} 59 | 60 | {/each} 61 |
AddressPrice BetTime
62 |
63 |
64 | -------------------------------------------------------------------------------- /src/web/svelte/UserContracts.svelte: -------------------------------------------------------------------------------- 1 |
2 |
3 |
My Recent Contracts
4 |
5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | {#each items as event} 13 | {#if event.creator === queryUser} 14 | 15 | {/if} 16 | {/each} 17 |
#PairTicketStatus
18 |
19 | 20 | -------------------------------------------------------------------------------- /src/web/svelte/Web3Warn.svelte: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |
5 |

Warning:

6 |

7 | Your Browser Is Not Web3 Gen! 8 | Install Metamask 9 | and retry, or switch to another browser. 10 |
Or install Web3 Ethereum Wallets and browser our DApp: 11 |

12 | 18 |
19 |
20 |
21 |
-------------------------------------------------------------------------------- /src/web/svelte/WinnerRow.svelte: -------------------------------------------------------------------------------- 1 | 6 | {#if user === event.winner} 7 | 8 | YOU 9 | {event.guess/100} 10 | {event.solution/1e6} 11 | {event.reward/1e18} ETH 12 | 13 | {:else} 14 | 15 | {BitEth.cutAddress(event.winner)} 16 | {event.guess/100} 17 | {event.solution/1e6} 18 | {event.reward/1e18} ETH 19 | 20 | {/if} -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const MiniCssExtractPlugin = require('mini-css-extract-plugin'); 3 | const OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin'); 4 | const CopyPlugin = require('copy-webpack-plugin'); 5 | const LiveReloadPlugin = require('webpack-livereload-plugin'); 6 | const mode = process.env.NODE_ENV || 'development'; 7 | 8 | console.log("========================"); 9 | console.log("webpack mode", mode); 10 | console.log("========================"); 11 | 12 | const cssnano = require('cssnano'); 13 | 14 | const cssnanoProcess = cssnano.process; 15 | 16 | cssnano.process = function(source, processOpts, pluginOpts) { 17 | return cssnanoProcess.call(this, source, processOpts, Object.assign(pluginOpts || {}, { 18 | preset: [ 'default', { calc: false } ] 19 | })); 20 | }; 21 | 22 | module.exports = { 23 | mode: mode, 24 | entry: { 25 | bundle: path.resolve(__dirname, './src/web/js/index.js') 26 | }, 27 | output: { 28 | path: path.resolve(__dirname, 'public'), 29 | filename: '[name].js' 30 | }, 31 | plugins: [ 32 | new LiveReloadPlugin({appendScriptTag: true}), 33 | new CopyPlugin([ 34 | {from: './src/web/index.html', to: 'index.html', toType: 'file'}, 35 | {from: './src/web/icons', to: 'icons/'} 36 | ]), 37 | new MiniCssExtractPlugin({ 38 | filename: '[name].css', 39 | chunkFilename: '[name].[id].css' 40 | }), 41 | new OptimizeCssAssetsPlugin({ 42 | assetNameRegExp: /\.css$/g, 43 | cssProcessor: cssnanoProcess, 44 | cssProcessorPluginOptions: { 45 | preset: ['default', {discardComments: {removeAll: true}}] 46 | }, 47 | canPrint: true 48 | }) 49 | ], 50 | module: { 51 | rules: [ 52 | { 53 | test: /\.(html|svelte)$/, 54 | use: 'svelte-loader' 55 | }, 56 | { 57 | test: /\.(png|jpg|gif|svg)$/i, 58 | use: [ 59 | { 60 | loader: 'url-loader', 61 | options: { 62 | limit: 8192, 63 | }, 64 | }, 65 | ], 66 | }, 67 | { 68 | test: /\.(sa|sc|c)ss$/, 69 | use: [ 70 | 'style-loader', 71 | MiniCssExtractPlugin.loader, 72 | 'css-loader', 73 | { 74 | loader: 'sass-loader', 75 | options: { 76 | sassOptions: { 77 | includePaths: [ 78 | './theme', 79 | './node_modules' 80 | ] 81 | } 82 | } 83 | } 84 | ] 85 | } 86 | ] 87 | } 88 | }; 89 | --------------------------------------------------------------------------------