├── .babelrc ├── .gitattributes ├── .gitignore ├── .npmignore ├── .solcover.js ├── README.md ├── contracts ├── DataFeedOracles │ ├── DataFeedOracleBase.sol │ ├── DataFeedOraclePrimary.sol │ ├── IDataFeedOracle.sol │ ├── MedianDataFeedOracle.sol │ └── TypedOracle.sol ├── Mocks │ └── OracleConsumerMock.sol ├── Oracles │ ├── BasicOracle.sol │ ├── IOracle.sol │ ├── MultiOracle.sol │ ├── OracleBase.sol │ ├── PaidMultiOracle.sol │ ├── PaidOracle.sol │ ├── SignedMultiOracle.sol │ └── SignedOracle.sol └── PushOracles │ ├── BasicPushOracle.sol │ ├── IOracleConsumer.sol │ ├── PushOracleBase.sol │ └── SignedPushOracle.sol ├── docs ├── .gitignore ├── README.md ├── docs │ ├── api_Mocks_OracleConsumerMock.md │ ├── api_Oracles_BasicOracle.md │ ├── api_Oracles_IOracle.md │ ├── api_Oracles_OracleBase.md │ ├── api_Oracles_PaidOracle.md │ ├── api_Oracles_SignedOracle.md │ ├── api_PushOracles_BasicPushOracle.md │ ├── api_PushOracles_IOracleConsumer.md │ ├── api_PushOracles_PushOracleBase.md │ ├── api_PushOracles_SignedPushOracle.md │ ├── api_es_openzeppelin-solidity_contracts_ECRecovery.md │ ├── api_es_openzeppelin-solidity_contracts_math_Math.md │ └── api_es_openzeppelin-solidity_contracts_math_SafeMath.md └── website │ ├── blog │ ├── 2016-03-11-blog-post.md │ ├── 2017-04-10-blog-post-two.md │ ├── 2017-09-25-testing-rss.md │ ├── 2017-09-26-adding-rss.md │ └── 2017-10-24-new-version-1.0.0.md │ ├── core │ └── Footer.js │ ├── package-lock.json │ ├── package.json │ ├── pages │ └── en │ │ ├── help.js │ │ ├── index.js │ │ └── users.js │ ├── sidebars.json │ ├── siteConfig.js │ └── static │ ├── css │ └── custom.css │ └── img │ ├── favicon.png │ └── favicon │ └── favicon.ico ├── migrations └── .gitkeep ├── package-lock.json ├── package.json ├── password_kovan.txt ├── scripts ├── build.js ├── buildDocs.sh ├── runGanacheCLI.sh └── test.sh ├── test ├── DataFeedOracles │ ├── DataFeedOracleBase.test.js │ ├── MedianDataFeedOracle.test.js │ └── Utils.test.js ├── Mocks │ └── MockToken.sol ├── Oracles │ ├── BasicOracle.test.js │ ├── MultiOracle.test.js │ ├── OracleBase.test.js │ ├── PaidMutliOracle.test.js │ ├── PaidOracle.test.js │ ├── SignedMultiOracle.test.js │ └── SignedOracle.test.js └── PushOracles │ ├── BasicPushOracle.test.js │ ├── PushOracleBase.test.js │ └── SignedPushOracle.test.js ├── truffle.js └── zos.json /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["es2015", "stage-0"], 3 | "env": { 4 | "commonjs": { 5 | "plugins": [ 6 | ["transform-es2015-modules-commonjs"] 7 | ] 8 | } 9 | } 10 | } -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | *.sol linguist-language=Solidity 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | build 3 | *.log 4 | lib 5 | secrets.json 6 | coverage 7 | coverage.json 8 | allFiredEvents 9 | scTopics 10 | coverageEnv 11 | 12 | .zos.session 13 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | contracts 2 | migrations 3 | node_modules 4 | package-lock.json 5 | scripts 6 | test 7 | -------------------------------------------------------------------------------- /.solcover.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | port: 8546, 3 | copyPackages: ['zeppelin-solidity'] 4 | } 5 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Tidbit 2 | 3 | A library for oracles on Ethereum. 4 | 5 | ### Contracts 6 | 7 | For more information on each contract checkout the [documentation](https://github.com/levelkdev/tidbit/blob/master/docs/docs/api_Oracles_OracleBase.md). 8 | 9 | ##### Inheritance Tree 10 | 11 | ``` 12 | IOracle 13 | | 14 | v 15 | OracleBase --------> BasicOracle ------> SignedOracle 16 | | | | 17 | v v v 18 | PushOracleBase --> BasicPushOracle --> SignedPushOracle 19 | ``` 20 | 21 | ### Setup 22 | 23 | Then run `npm install` 24 | 25 | `chmod +x ./scripts/**` to grant execute permissions on the scripts directory 26 | 27 | ### Compile 28 | 29 | Recompile contracts and build artifacts. 30 | 31 | ``` 32 | $ npm run compile 33 | ``` 34 | 35 | ### Test 36 | 37 | Run `npm run compile` before first test run, and after any changes to the `.sol` files 38 | 39 | ``` 40 | $ npm test 41 | ``` 42 | 43 | Run `npm run test:coverage` to run with coverage reporting 44 | -------------------------------------------------------------------------------- /contracts/DataFeedOracles/DataFeedOracleBase.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.0; 2 | 3 | import "./IDataFeedOracle.sol"; 4 | import "zos-lib/contracts/Initializable.sol"; 5 | 6 | /** 7 | * @title DataFeedOracleBase 8 | * @dev Allows a data source address to set bytes32 result values by date. Result values 9 | * can be publicly read by date and by index. 10 | */ 11 | contract DataFeedOracleBase is Initializable, IDataFeedOracle { 12 | 13 | uint256[] dates; 14 | 15 | mapping(uint256 => bytes32) resultsByDate; 16 | 17 | mapping(uint256 => uint256) indicesByDate; 18 | 19 | address public dataSource; 20 | 21 | event ResultSet(bytes32 _result, uint256 _date, uint256 _index, address _sender); 22 | 23 | /** 24 | * @dev Throws if _date is not current or past. 25 | * @param _date Date to check against the `now` value. 26 | */ 27 | modifier onlyBefore(uint256 _date) { 28 | require(_date <= now, "Date cannot be in the future"); 29 | _; 30 | } 31 | 32 | /** 33 | * @dev Throws if the data source is not the caller. 34 | */ 35 | modifier onlyDataSource() { 36 | require(msg.sender == dataSource, "The caller is not the data source"); 37 | _; 38 | } 39 | 40 | /** 41 | * @dev Initializes DataFeedOracleBase. 42 | * @param _dataSource The address that is allowed to set results. 43 | */ 44 | function initialize(address _dataSource) public initializer { 45 | require(_dataSource != address(0), "_dataSource cannot be address(0)"); 46 | dataSource = _dataSource; 47 | dates.push(0); // The first valid result index starts at 1 48 | } 49 | 50 | /** 51 | * @dev Sets a bytes32 result for the given date. 52 | * @param _result The result being set. 53 | * @param _date The date for the result. 54 | * @return The index of the result. 55 | */ 56 | function setResult(bytes32 _result, uint256 _date) 57 | public 58 | onlyDataSource() 59 | onlyBefore(_date) 60 | returns (uint256 index) 61 | { 62 | if (dates.length > 0) { 63 | require(_date > dates[totalResults()]); 64 | } 65 | _setResult(_result, _date); 66 | return totalResults(); 67 | } 68 | 69 | /** 70 | * @return The total number of results that have been set. 71 | */ 72 | function totalResults() public view returns (uint256) { 73 | return dates.length - 1; 74 | } 75 | 76 | /** 77 | * @dev Returns a result and a date, given an index. Throws if no result exists for 78 | * the given index. 79 | * @param _index The index of the result. 80 | * @return The result value and the date of the result. 81 | */ 82 | function resultByIndex(uint256 _index) public view returns (bytes32, uint256) { 83 | require(indexHasResult(_index), "No result set for _index"); 84 | return (resultsByDate[dates[_index]], dates[_index]); 85 | } 86 | 87 | /** 88 | * @dev Returns a result and an index, given a date. Throws if no result exists for 89 | * the given date. 90 | * @param _date The date of the result. 91 | * @return The result value and the index of the result. 92 | */ 93 | function resultByDate(uint256 _date) public view returns (bytes32, uint256) { 94 | require(dateHasResult(_date), "No result set for _date"); 95 | return (resultsByDate[_date], indicesByDate[_date]); 96 | } 97 | 98 | /** 99 | * @notice Throws if no results have been set. 100 | * @return The date of the last result that was set. 101 | */ 102 | function latestResultDate() 103 | public view 104 | returns (uint256) 105 | { 106 | return (dates[totalResults()]); 107 | } 108 | 109 | /** 110 | * @notice Throws if no results have been set. 111 | * @return The last result that was set. 112 | */ 113 | function latestResult() 114 | public view 115 | returns (bytes32) 116 | { 117 | return resultsByDate[dates[totalResults()]]; 118 | } 119 | 120 | /** 121 | * @param _index The index of a result. 122 | * @return `true` if a result for the given index exists. 123 | */ 124 | function indexHasResult(uint256 _index) public view returns (bool) { 125 | require(_index > 0, "_index must be greater than 0"); 126 | return dates.length > _index; 127 | } 128 | 129 | /** 130 | * @param _date The date of the data feed 131 | * @return `true` if a result has been set for the given date. 132 | */ 133 | function dateHasResult(uint256 _date) public view returns (bool) { 134 | return indicesByDate[_date] > 0; 135 | } 136 | 137 | /** 138 | * @dev Sets a bytes32 result value and a date for the result. 139 | * @param _result The result to set. 140 | * @param _date The date of the result. 141 | */ 142 | function _setResult(bytes32 _result, uint256 _date) internal { 143 | resultsByDate[_date] = _result; 144 | dates.push(_date); 145 | indicesByDate[_date] = totalResults(); 146 | 147 | _resultWasSet(_result, _date); 148 | 149 | emit ResultSet(_result, _date, totalResults(), msg.sender); 150 | } 151 | 152 | /** 153 | * @dev Unimplemented function meant to be overidden in subclasses. 154 | */ 155 | function _resultWasSet(bytes32 /*_result*/, uint256 /*_date*/) internal { 156 | // optional override 157 | } 158 | 159 | } 160 | -------------------------------------------------------------------------------- /contracts/DataFeedOracles/DataFeedOraclePrimary.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.0; 2 | 3 | // Optional, DataFeedOraclePrimary must implement DataFeedOracle 4 | interface DataFeedOraclePrimary { 5 | event ResultSet(bytes32 _result, uint256 _date, uint256 _index); 6 | } 7 | -------------------------------------------------------------------------------- /contracts/DataFeedOracles/IDataFeedOracle.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.0; 2 | 3 | /** 4 | * @title DataFeedOracle interface 5 | */ 6 | interface IDataFeedOracle { 7 | function totalResults() external view returns (uint256); 8 | function indexHasResult(uint256 index) external view returns (bool); 9 | function dateHasResult(uint256 date) external view returns (bool); 10 | function resultByIndex(uint256 index) external view returns (bytes32, uint256); 11 | function resultByDate(uint256 date) external view returns (bytes32, uint256); 12 | function latestResultDate() external view returns (uint256); 13 | function latestResult() external view returns (bytes32); 14 | } 15 | -------------------------------------------------------------------------------- /contracts/DataFeedOracles/MedianDataFeedOracle.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.0; 2 | 3 | import "./DataFeedOracleBase.sol"; 4 | import "openzeppelin-solidity/contracts/math/SafeMath.sol"; 5 | import "zos-lib/contracts/Initializable.sol"; 6 | 7 | contract MedianDataFeedOracle is Initializable, DataFeedOracleBase { 8 | 9 | mapping(address => bool) public approvedDataFeeds; 10 | uint public approvedDataFeedsLength; 11 | 12 | event AddedDataFeed(address dataFeed); 13 | event RemovedDataFeed(address dataFeed); 14 | 15 | /** 16 | * @dev MedianDataFeedOracle constructor 17 | * @param _dataFeedSources Valid datafeeds to update price. 18 | * @param _dataSource The address that is able to set the result 19 | */ 20 | function initialize(address[] memory _dataFeedSources, address _dataSource) public initializer { 21 | require(_dataFeedSources.length > 0, "Cannot initialize MedianDataFeedOracle without data feeds"); 22 | for (uint i = 0; i < _dataFeedSources.length; i++) { 23 | _addDataFeed(_dataFeedSources[i]); 24 | } 25 | DataFeedOracleBase.initialize(_dataSource); 26 | } 27 | 28 | /** 29 | * @dev setResult with sorted dataFeeds 30 | * @param _dataFeeds Valid datafeeds to update price. 31 | * The assumption here is that the dataFeeds initialized here are already sorted. 32 | * It could be achieved cheaper off-chain than on-chain. 33 | */ 34 | function setResult(DataFeedOracleBase[] calldata _dataFeeds) external { 35 | require(_dataFeeds.length == approvedDataFeedsLength, "Must include every approved data feed without duplicates"); 36 | 37 | for (uint i = 0; i < _dataFeeds.length; i++) { 38 | require(approvedDataFeeds[address(_dataFeeds[i].dataSource)], "Unauthorized data feed."); 39 | require(_dataFeeds[i].latestResultDate() > latestResultDate(), "Stale data."); 40 | 41 | for (uint j = i + 1; j < _dataFeeds.length; j++) { 42 | require(_dataFeeds[i] != _dataFeeds[j], "Duplicate data feeds prohibited"); 43 | } 44 | 45 | if(i != _dataFeeds.length - 1) { 46 | require(uint256(_dataFeeds[i].latestResult()) <= uint256(_dataFeeds[i+1].latestResult()), "The dataFeeds are not sorted."); 47 | } 48 | } 49 | 50 | bytes32 medianValue; 51 | if(_dataFeeds.length % 2 == 0) { 52 | uint256 one = uint256(_dataFeeds[(_dataFeeds.length / 2) - 1].latestResult()); 53 | uint256 two = uint256(_dataFeeds[(_dataFeeds.length / 2)].latestResult()); 54 | medianValue = bytes32((one + two) / 2); 55 | } else { 56 | medianValue = _dataFeeds[_dataFeeds.length / 2].latestResult(); 57 | } 58 | uint256 now = uint256(block.timestamp); 59 | super.setResult(medianValue, now); 60 | } 61 | 62 | /** 63 | * @dev add new dataFeed to be medianized for each result 64 | * @param dataFeed dataFeedOracle to add to approvedDataFeeds 65 | */ 66 | function addDataFeed(address dataFeed) onlyDataSource() public { 67 | _addDataFeed(dataFeed); 68 | } 69 | 70 | /** 71 | * @dev remove existing dataFeed to be medianized for each result 72 | * @param dataFeed dataFeedOracle to remove from approvedDataFeeds 73 | */ 74 | function removeDataFeed(address dataFeed) onlyDataSource() public { 75 | _removeDataFeed(dataFeed); 76 | } 77 | 78 | 79 | // Internal Functions 80 | 81 | function _addDataFeed(address dataFeed) internal { 82 | require(!approvedDataFeeds[dataFeed]); 83 | approvedDataFeeds[dataFeed] = true; 84 | approvedDataFeedsLength++; 85 | emit AddedDataFeed(dataFeed); 86 | } 87 | 88 | function _removeDataFeed(address dataFeed) internal { 89 | require(approvedDataFeeds[dataFeed]); 90 | approvedDataFeeds[dataFeed] = false; 91 | approvedDataFeedsLength--; 92 | emit RemovedDataFeed(dataFeed); 93 | } 94 | 95 | } 96 | -------------------------------------------------------------------------------- /contracts/DataFeedOracles/TypedOracle.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.0; 2 | 3 | interface TypedOracle { 4 | function dataType(uint256 _id) external view returns (uint); 5 | } 6 | -------------------------------------------------------------------------------- /contracts/Mocks/OracleConsumerMock.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.0; 2 | 3 | import "../PushOracles/IOracleConsumer.sol"; 4 | 5 | contract OracleConsumerMock is IOracleConsumer { 6 | 7 | bytes32 public result; 8 | 9 | function receiveResult(bytes32 /*id*/, bytes32 _result) external { 10 | result = _result; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /contracts/Oracles/BasicOracle.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.0; 2 | 3 | import "./OracleBase.sol"; 4 | import "zos-lib/contracts/Initializable.sol"; 5 | 6 | /** 7 | * @title BasicOracle 8 | * @dev Extends OracleBase to allow the result to be set by a single data source 9 | */ 10 | contract BasicOracle is Initializable, OracleBase { 11 | 12 | // The address that is able to set the result 13 | address public dataSource; 14 | 15 | /** 16 | * @dev BasicOracle initializer 17 | * @param _dataSource The address that is able to set the result 18 | */ 19 | function initialize(address _dataSource) public initializer { 20 | require(_dataSource != address(0), "Require a non-null dataSource"); 21 | dataSource = _dataSource; 22 | } 23 | 24 | /** 25 | * @dev Sets the result of the oracle 26 | * @param _result The result being set 27 | */ 28 | function setResult(bytes32 _result) public { 29 | require(msg.sender == dataSource, "The caller is not the data source."); 30 | _setResult(_result); 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /contracts/Oracles/IOracle.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.0; 2 | 3 | interface IOracle { 4 | function resultFor(bytes32 id) external view returns (bytes32); 5 | function isResultSet(bytes32 id) external view returns (bool); 6 | } 7 | -------------------------------------------------------------------------------- /contracts/Oracles/MultiOracle.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.0; 2 | 3 | import "./IOracle.sol"; 4 | import "openzeppelin-solidity/contracts/ownership/Ownable.sol"; 5 | 6 | contract MultiOracle is IOracle, Ownable { 7 | 8 | struct OracleData { 9 | address dataSource; 10 | bytes32 result; 11 | bool resultIsSet; 12 | } 13 | 14 | mapping (bytes32 => OracleData) results; // id to result map 15 | 16 | event ResultSet(bytes32 _id, bytes32 _result, address _sender); 17 | 18 | /** 19 | * @dev Throws if operator is not dataSource. 20 | * @param _operator address 21 | */ 22 | modifier onlyIfValidAddress(address _operator) 23 | { 24 | require(_operator != address(0), "Invalid dataSource."); 25 | _; 26 | } 27 | 28 | /* 29 | * Public functions 30 | */ 31 | 32 | /** 33 | * @dev Sets the result of the oracle 34 | * @param id The id being set 35 | * @param result The result being set 36 | */ 37 | function setResult( 38 | bytes32 id, 39 | bytes32 result 40 | ) 41 | public 42 | { 43 | require(msg.sender == results[id].dataSource, "The caller is not the valid data source with given id."); 44 | _setResult(id, result); 45 | } 46 | 47 | /** 48 | * @dev Returns the result or reverts if it hasn't been set 49 | * @param id The id to identify the oracle 50 | * @return The result or the oracle's single event 51 | */ 52 | function resultFor(bytes32 id) 53 | external 54 | view 55 | returns 56 | (bytes32) 57 | { 58 | require(isResultSet(id), "The result has not been set."); 59 | return results[id].result; 60 | } 61 | 62 | /** 63 | * @dev Checks if the result has been set 64 | * @param id The id to identify oracle 65 | * @return True if the result has been set 66 | */ 67 | function isResultSet(bytes32 id) 68 | public 69 | view 70 | returns 71 | (bool) 72 | { 73 | return results[id].resultIsSet; 74 | } 75 | 76 | /** 77 | * @dev Checks if the oracle has been set 78 | * @param id The id to identify the oracle 79 | * @return True if the oracle has been set 80 | */ 81 | function isOracleSet(bytes32 id) 82 | public 83 | view 84 | returns 85 | (bool) 86 | { 87 | return results[id].dataSource != address(0); 88 | } 89 | 90 | /** 91 | * @dev Only owner could add a new oracle with id and dataSource information 92 | */ 93 | function newOracle( 94 | bytes32 _id, 95 | address _dataSource 96 | ) 97 | public 98 | onlyOwner 99 | onlyIfValidAddress(_dataSource) 100 | { 101 | require(!isOracleSet(_id), "Oracle with the given id has already been set."); 102 | require(!isResultSet(_id), "Result has already been set."); 103 | results[_id].dataSource = _dataSource; 104 | } 105 | 106 | /* 107 | * Internal functions 108 | */ 109 | 110 | /** 111 | * @dev Set's the result, emits ResultSet, and calls the _resultWasSet() 112 | * overridable function 113 | * @param _result The result of the oracle's single event. 114 | * @param _id The id to identify single oracle. 115 | */ 116 | function _setResult( 117 | bytes32 _id, 118 | bytes32 _result 119 | ) 120 | internal 121 | { 122 | require(!isResultSet(_id), "Result has already been set."); 123 | results[_id].result = _result; 124 | results[_id].resultIsSet = true; 125 | emit ResultSet(_id, _result, msg.sender); 126 | _resultWasSet(_id, _result); 127 | } 128 | 129 | /** 130 | * @dev Empty function meant to be overidden in subclasses 131 | */ 132 | function _resultWasSet(bytes32 /*_id*/, bytes32 /*_result*/) 133 | internal 134 | { 135 | // optional override 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /contracts/Oracles/OracleBase.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.0; 2 | 3 | import "./IOracle.sol"; 4 | 5 | /** 6 | * @title OracleBase 7 | * @dev Lays out generic single-event oracle functionality. It implements 8 | * an internal function to set the result that can be called by public 9 | * functions that extend OracleBase. 10 | */ 11 | 12 | contract OracleBase is IOracle { 13 | 14 | bytes32 result; 15 | bool resultIsSet; 16 | 17 | event ResultSet(bytes32 _result, address _sender); 18 | 19 | /** 20 | * Public functions 21 | */ 22 | 23 | /** 24 | * @dev Returns the result or reverts if it hasn't been set 25 | * @param id This is not used in single-event oracles and should be 0 26 | * @return The result or the oracle's single event 27 | */ 28 | function resultFor(bytes32 id) external view returns (bytes32) { 29 | require(id == bytes32(0), "This oracle does not support ids."); 30 | require(isResultSet(id), "The result has not been set."); 31 | return result; 32 | } 33 | 34 | /** 35 | * @dev Checks if the result has been set 36 | * @return True if the result has been set 37 | */ 38 | function isResultSet(bytes32 /*id*/) public view returns (bool) { 39 | return resultIsSet; 40 | } 41 | 42 | /** 43 | * Internal functions 44 | */ 45 | 46 | /** 47 | * @dev Set's the result, emits ResultSet, and calls the _resultWasSet() 48 | * overridable function 49 | * @param _result The result of the oracle's single event. 50 | */ 51 | function _setResult(bytes32 _result) internal { 52 | require(!resultIsSet, "Result has already been set."); 53 | result = _result; 54 | resultIsSet = true; 55 | emit ResultSet(_result, msg.sender); 56 | _resultWasSet(_result); 57 | } 58 | 59 | /** 60 | * @dev Empty function meant to be overidden in subclasses 61 | */ 62 | function _resultWasSet(bytes32 /*_result*/) internal { 63 | // optional override 64 | } 65 | 66 | } 67 | -------------------------------------------------------------------------------- /contracts/Oracles/PaidMultiOracle.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.0; 2 | 3 | import "./MultiOracle.sol"; 4 | import "openzeppelin-solidity/contracts/token/ERC20/IERC20.sol"; 5 | import "openzeppelin-solidity/contracts/math/Math.sol"; 6 | import "openzeppelin-solidity/contracts/math/SafeMath.sol"; 7 | import "zos-lib/contracts/Initializable.sol"; 8 | 9 | /** 10 | * @title PaidMultiOracle 11 | * @dev Extends MultiOracle to include rewards for dataSources 12 | */ 13 | 14 | contract PaidMultiOracle is Initializable, MultiOracle { 15 | 16 | uint256 public reward; 17 | IERC20 public token; 18 | 19 | function initialize(IERC20 _token, uint256 _reward) public initializer { 20 | token = _token; 21 | reward = _reward; 22 | } 23 | 24 | /** 25 | * @dev Returns the oracle reward or the contract's balance if it's less than the reward 26 | */ 27 | function getReward() public view returns (uint256) { 28 | return Math.min(reward, token.balanceOf(address(this))); 29 | } 30 | 31 | /* 32 | * Internal functions 33 | */ 34 | 35 | function _resultWasSet(bytes32 _id, bytes32 /*_result*/) 36 | internal 37 | { 38 | require(results[_id].resultIsSet, "Result hasn't been set yet."); 39 | require(results[_id].dataSource != address(0), "Invalid dataSource"); 40 | token.transfer(results[_id].dataSource, getReward()); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /contracts/Oracles/PaidOracle.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.0; 2 | 3 | import "./BasicOracle.sol"; 4 | import "openzeppelin-solidity/contracts/token/ERC20/IERC20.sol"; 5 | import "openzeppelin-solidity/contracts/math/Math.sol"; 6 | import "openzeppelin-solidity/contracts/math/SafeMath.sol"; 7 | import "zos-lib/contracts/Initializable.sol"; 8 | 9 | /** 10 | * @title PaidOracle 11 | * @dev BasicOracle with reward set in initializer. The reward is transfered to 12 | * dataSource when the result is successfully set. 13 | */ 14 | 15 | contract PaidOracle is Initializable, BasicOracle { 16 | 17 | uint256 public reward; 18 | IERC20 public token; 19 | 20 | function initialize( 21 | IERC20 _token, 22 | address _dataSource, 23 | uint256 _reward 24 | ) 25 | public 26 | initializer 27 | { 28 | BasicOracle.initialize(_dataSource); 29 | token = _token; 30 | reward = _reward; 31 | } 32 | 33 | /** 34 | * @dev Returns the oracle reward or the contract's balance if it's less than the reward 35 | */ 36 | function getReward() 37 | public 38 | view 39 | returns 40 | (uint256) 41 | { 42 | return Math.min(reward, token.balanceOf(address(this))); 43 | } 44 | 45 | /* 46 | * Internal functions 47 | */ 48 | function _resultWasSet(bytes32 /*_result*/) 49 | internal 50 | { 51 | require(resultIsSet, "Result hasn't been set yet."); 52 | require(dataSource != address(0)); 53 | token.transfer(dataSource, getReward()); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /contracts/Oracles/SignedMultiOracle.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.0; 2 | 3 | import "./MultiOracle.sol"; 4 | import "openzeppelin-solidity/contracts/cryptography/ECDSA.sol"; 5 | 6 | /** 7 | * @title SignedMultiOracle 8 | * @dev Extends MultiOracle to use signed messages 9 | */ 10 | contract SignedMultiOracle is MultiOracle { 11 | using ECDSA for bytes32; 12 | 13 | /** 14 | * @dev Sets the result of the oracle with a signed message. 15 | * To resolve the issue that truffle not supporting function overrides, 16 | * rename `setResult` method to be `setResultWithSignature`. 17 | * @param _id The id being set 18 | * @param _result The result being set 19 | * @param _signature The hash of the result signed by the data source 20 | */ 21 | function setResultWithSignature( 22 | bytes32 _id, 23 | bytes32 _result, 24 | bytes memory _signature 25 | ) 26 | public 27 | { 28 | // Generate message hash 29 | bytes32 messageHash = keccak256(abi.encodePacked(_id, _result, address(this))); 30 | 31 | // Recover signer from the signature with messageSigned 32 | address signer = messageHash.toEthSignedMessageHash().recover(_signature); 33 | // Check that the signer is the dataSource 34 | require(signer == results[_id].dataSource, "Invalid signature"); 35 | _setResult(_id, _result); 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /contracts/Oracles/SignedOracle.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.0; 2 | 3 | import "./BasicOracle.sol"; 4 | import "openzeppelin-solidity/contracts/cryptography/ECDSA.sol"; 5 | import "zos-lib/contracts/Initializable.sol"; 6 | 7 | /** 8 | * @title SignedOracle 9 | * @dev Extends BasicOracle to allow any address to resolve the oracle with a 10 | * signed message from the data source 11 | */ 12 | contract SignedOracle is Initializable, BasicOracle { 13 | using ECDSA for bytes32; 14 | 15 | /** 16 | * @dev SignedOracle initializer 17 | * @param _dataSource The address that is able to set the result 18 | */ 19 | function initialize( 20 | address _dataSource 21 | ) 22 | public 23 | initializer 24 | { 25 | BasicOracle.initialize(_dataSource); 26 | } 27 | 28 | /** 29 | * @dev Sets the result of the oracle with a signed message 30 | * @param _result The result being set 31 | * @param _signature The hash of the result signed by the data source 32 | */ 33 | function setResult(bytes32 _result, bytes memory _signature) public { 34 | // Generate message hash 35 | bytes32 messageHash = keccak256(abi.encodePacked(_result, address(this))); 36 | 37 | // Recover signer from the signature 38 | address signer = messageHash.toEthSignedMessageHash().recover(_signature); 39 | 40 | // Check that the signer is the dataSource 41 | require(signer == dataSource, "Invalid signature"); 42 | 43 | _setResult(_result); 44 | } 45 | 46 | } 47 | -------------------------------------------------------------------------------- /contracts/PushOracles/BasicPushOracle.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.0; 2 | 3 | import "./PushOracleBase.sol"; 4 | import "../Oracles/BasicOracle.sol"; 5 | import "zos-lib/contracts/Initializable.sol"; 6 | 7 | /** 8 | * @title BasicPushOracle 9 | * @dev Combines BasicOracle and PushOracleBase to create a push style BasicOracle 10 | */ 11 | contract BasicPushOracle is Initializable, BasicOracle, PushOracleBase { 12 | 13 | /** 14 | * @dev BasicPushOracle initializer 15 | * @param _dataSource The address that is able to set the result 16 | * @param _consumer A contract that implements IOracleConsumer and is called when 17 | * the result has been set. 18 | */ 19 | function initialize ( 20 | address _dataSource, 21 | IOracleConsumer _consumer 22 | ) 23 | public 24 | initializer 25 | { 26 | BasicOracle.initialize(_dataSource); 27 | PushOracleBase.initialize(_consumer, 0); 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /contracts/PushOracles/IOracleConsumer.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.0; 2 | 3 | interface IOracleConsumer { 4 | function receiveResult(bytes32 id, bytes32 result) external; 5 | } 6 | -------------------------------------------------------------------------------- /contracts/PushOracles/PushOracleBase.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.0; 2 | 3 | import "./IOracleConsumer.sol"; 4 | import "../Oracles/OracleBase.sol"; 5 | import "zos-lib/contracts/Initializable.sol"; 6 | 7 | /** 8 | * @title PushOracleBase 9 | * @dev Extends OracleBase to be a push type oracle by calling an oracle consumer 10 | * when the result is set 11 | */ 12 | contract PushOracleBase is Initializable, OracleBase { 13 | 14 | IOracleConsumer public consumer; 15 | 16 | /** 17 | * @dev PushOracleBase initializer 18 | * @param _consumer A contract that implements IOracleConsumer and is called when 19 | * the result has been set. 20 | */ 21 | function initialize(IOracleConsumer _consumer, uint unusedParam) initializer public { 22 | consumer = _consumer; 23 | } 24 | 25 | /** 26 | * Internal functions 27 | */ 28 | 29 | /** 30 | * @dev Calls receiveResult(bytes32, bytes) on the oracle consumer when the 31 | * result is set 32 | * @dev Called by _setResult(bytes32) in OracleBase 33 | * @param _result The result being set in _setResult(bytes32) 34 | */ 35 | function _resultWasSet(bytes32 _result) internal { 36 | super._resultWasSet(_result); 37 | consumer.receiveResult(0, _result); 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /contracts/PushOracles/SignedPushOracle.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.0; 2 | 3 | import "./PushOracleBase.sol"; 4 | import "../Oracles/SignedOracle.sol"; 5 | import "zos-lib/contracts/Initializable.sol"; 6 | 7 | /** 8 | * @title SignedPushOracle 9 | * @dev Combines SignedOracle and PushOracleBase to create a push style SignedOracle 10 | */ 11 | contract SignedPushOracle is Initializable, SignedOracle, PushOracleBase { 12 | 13 | /** 14 | * @dev SignedPushOracle initializer 15 | * @param _dataSource The address that is able to set the result 16 | * @param _consumer A contract that implements IOracleConsumer and is called when 17 | * the result has been set. 18 | */ 19 | function initialize( 20 | address _dataSource, 21 | IOracleConsumer _consumer 22 | ) 23 | public 24 | initializer 25 | { 26 | SignedOracle.initialize(_dataSource); 27 | PushOracleBase.initialize(_consumer, 0); 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /docs/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | 3 | node_modules 4 | 5 | lib/core/metadata.js 6 | lib/core/MetadataBlog.js 7 | 8 | website/translated_docs 9 | website/build/ 10 | website/yarn.lock 11 | website/node_modules 12 | website/i18n/* 13 | -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | # Tidbit Documentation 2 | 3 | Tidbit documentation generated by [`solidity-docgen`](https://github.com/OpenZeppelin/solidity-docgen) and [`docusaurus`](https://docusaurus.io/) 4 | 5 | #### Update documentation on Github pages 6 | 7 | First, generate documentation by running `npm run build-docs` in the `tidbit` repository. 8 | 9 | Then, to deploy the docs to Github pages, navigate to the `docs/website/` directory and run: 10 | 11 | ```bash 12 | GIT_USER= \ 13 | USE_SSH=true \ 14 | npm run publish-gh-pages 15 | # USE_SSH environment var is optional 16 | ``` 17 | (must have write permissions) 18 | -------------------------------------------------------------------------------- /docs/docs/api_Mocks_OracleConsumerMock.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: Mocks_OracleConsumerMock 3 | title: OracleConsumerMock 4 | --- 5 | 6 |

contract OracleConsumerMock

is IOracleConsumer

Reference

Functions

  • receiveResult

    function receiveResult(bytes32 , bytes _result) external 
    Parameters:
    - bytes32
    _result - bytes
7 | -------------------------------------------------------------------------------- /docs/docs/api_Oracles_BasicOracle.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: Oracles_BasicOracle 3 | title: BasicOracle 4 | --- 5 | 6 |

contract BasicOracle

is OracleBase

Extends OracleBase to allow the result to be set by a single data source.

Reference

Functions

  • fallback

    function (address _dataSource) public 

    BasicOracle constructor.

    Parameters:
    _dataSource - The address that is able to set the result
  • setResult

    function setResult(bytes _result) public 

    Sets the result of the oracle.

    Parameters:
    _result - The result being set
7 | -------------------------------------------------------------------------------- /docs/docs/api_Oracles_IOracle.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: Oracles_IOracle 3 | title: IOracle 4 | --- 5 | 6 |

interface IOracle

Reference

Functions

  • isResultSet

    abstract function isResultSet(bytes32 id) external view returns  (bool) 
    Parameters:
    id - bytes32
    Returns:
    bool
  • resultFor

    abstract function resultFor(bytes32 id) external view returns  (bytes) 
    Parameters:
    id - bytes32
    Returns:
    bytes
7 | -------------------------------------------------------------------------------- /docs/docs/api_Oracles_OracleBase.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: Oracles_OracleBase 3 | title: OracleBase 4 | --- 5 | 6 |

contract OracleBase

is IOracle

Lays out generic single-event oracle functionality but doesn't implement a method to set the result.

Reference

Events

  • ResultSet

    event ResultSet(bytes _result, address _sender) 
    Parameters:
    _result - bytes
    _sender - address

Functions

  • _resultWasSet

    function _resultWasSet(bytes ) internal 

    Empty function meant to be overidden in subclasses.

    Parameters:
    - bytes
  • _setResult

    function _setResult(bytes _result) internal 

    Set's the result, emits ResultSet, and calls the _resultWasSet() overridable function.

    Parameters:
    _result - The result of the oracle's single event.
  • isResultSet

    function isResultSet(bytes32 ) public view returns  (bool) 

    Checks if the result has been set.

    Parameters:
    - bytes32
    Returns:
    True if the result has been set
  • resultFor

    function resultFor(bytes32 id) external view returns  (bytes) 

    Returns the result or reverts if it hasn't been set.

    Parameters:
    id - This is not used in single-event orcles and should be 0 The result or the oracle's single event
    Returns:
    bytes
7 | -------------------------------------------------------------------------------- /docs/docs/api_Oracles_PaidOracle.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: Oracles_PaidOracle 3 | title: PaidOracle 4 | --- 5 | 6 |

contract PaidOracle

is BasicOracle

Reference

Functions

  • _resultWasSet

    function _resultWasSet(bytes ) internal 
    Parameters:
    - bytes
  • fallback

    function (address _dataSource, uint256 _reward) public payable 
    Modifiers:
    Parameters:
    _dataSource - address
    _reward - uint256
  • getReward

    function getReward() public view returns  (uint256) 

    Returns the oracle reward or the contract's balance if it's less than the reward.

    Returns:
    uint256
7 | -------------------------------------------------------------------------------- /docs/docs/api_Oracles_SignedOracle.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: Oracles_SignedOracle 3 | title: SignedOracle 4 | --- 5 | 6 |

contract SignedOracle

is BasicOracle

Extends BasicOracle to allow any address to resolve the oracle with a signed message from the data source.

Reference

Functions

  • fallback

    function (address _dataSource) public 

    SignedOracle constructor.

    Modifiers:
    Parameters:
    _dataSource - The address that is able to set the result
  • setResult

    function setResult(bytes _result, bytes _signature) public 

    Sets the result of the oracle with a signed message.

    Parameters:
    _result - The result being set
    _signature - The hash of the result signed by the data source
7 | -------------------------------------------------------------------------------- /docs/docs/api_PushOracles_BasicPushOracle.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: PushOracles_BasicPushOracle 3 | title: BasicPushOracle 4 | --- 5 | 6 |

contract BasicPushOracle

is BasicOraclePushOracleBase

Combines BasicOracle and PushOracleBase to create a push style BasicOracle.

Reference

Functions

  • fallback

    function (address _dataSource, IOracleConsumer _consumer) public 

    BasicPushOracle constructor.

    Modifiers:
    Parameters:
    _dataSource - The address that is able to set the result
    _consumer - A contract that implements IOracleConsumer and is called when the result has been set.
7 | -------------------------------------------------------------------------------- /docs/docs/api_PushOracles_IOracleConsumer.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: PushOracles_IOracleConsumer 3 | title: IOracleConsumer 4 | --- 5 | 6 |

interface IOracleConsumer

Reference

Functions

  • receiveResult

    abstract function receiveResult(bytes32 id, bytes result) external 
    Parameters:
    id - bytes32
    result - bytes
7 | -------------------------------------------------------------------------------- /docs/docs/api_PushOracles_PushOracleBase.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: PushOracles_PushOracleBase 3 | title: PushOracleBase 4 | --- 5 | 6 |

contract PushOracleBase

is OracleBase

Extends OracleBase to be a push type oracle by calling an oracle consumer when the result is set.

Reference

Functions

  • _resultWasSet

    function _resultWasSet(bytes _result) internal 

    Called by _setResult(bytes) in OracleBase.

    Parameters:
    _result - The result being set in _setResult(bytes)
  • fallback

    function (IOracleConsumer _consumer) public 

    PushOracleBase constructor.

    Parameters:
    _consumer - A contract that implements IOracleConsumer and is called when the result has been set.
7 | -------------------------------------------------------------------------------- /docs/docs/api_PushOracles_SignedPushOracle.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: PushOracles_SignedPushOracle 3 | title: SignedPushOracle 4 | --- 5 | 6 |

contract SignedPushOracle

is SignedOraclePushOracleBase

Combines SignedOracle and PushOracleBase to create a push style SignedOracle.

Reference

Functions

  • fallback

    function (address _dataSource, IOracleConsumer _consumer) public 

    SignedPushOracle constructor.

    Modifiers:
    Parameters:
    _dataSource - The address that is able to set the result
    _consumer - A contract that implements IOracleConsumer and is called when the result has been set.
7 | -------------------------------------------------------------------------------- /docs/docs/api_es_openzeppelin-solidity_contracts_ECRecovery.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: es_openzeppelin-solidity_contracts_ECRecovery 3 | title: ECRecovery 4 | --- 5 | 6 |

library ECRecovery

Based on https://gist.github.com/axic/5b33912c6f61ae6fd96d6c4a47afde6d TODO Remove this library once solidity supports passing a signature to ecrecover. See https://github.com/ethereum/solidity/issues/864.

Reference

Functions

  • recover

    function recover(bytes32 _hash, bytes _sig) internal pure returns  (address) 

    Recover signer address from a message by using their signature.

    Parameters:
    _hash - bytes32 message, the hash is the signed message. What is recovered is the signer address.
    _sig - bytes signature, the signature is generated using web3.eth.sign()
    Returns:
    address
  • toEthSignedMessageHash

    function toEthSignedMessageHash(bytes32 _hash) internal pure returns  (bytes32) 

    ToEthSignedMessageHash, prefix a bytes32 value with "\x19Ethereum Signed Message:" and hash the result.

    Parameters:
    _hash - bytes32
    Returns:
    bytes32
7 | -------------------------------------------------------------------------------- /docs/docs/api_es_openzeppelin-solidity_contracts_math_Math.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: es_openzeppelin-solidity_contracts_math_Math 3 | title: Math 4 | --- 5 | 6 |

library Math

Assorted math operations.

Reference

Functions

  • max256

    function max256(uint256 _a, uint256 _b) internal pure returns  (uint256) 
    Parameters:
    _a - uint256
    _b - uint256
    Returns:
    uint256
  • max64

    function max64(uint64 _a, uint64 _b) internal pure returns  (uint64) 
    Parameters:
    _a - uint64
    _b - uint64
    Returns:
    uint64
  • min256

    function min256(uint256 _a, uint256 _b) internal pure returns  (uint256) 
    Parameters:
    _a - uint256
    _b - uint256
    Returns:
    uint256
  • min64

    function min64(uint64 _a, uint64 _b) internal pure returns  (uint64) 
    Parameters:
    _a - uint64
    _b - uint64
    Returns:
    uint64
7 | -------------------------------------------------------------------------------- /docs/docs/api_es_openzeppelin-solidity_contracts_math_SafeMath.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: es_openzeppelin-solidity_contracts_math_SafeMath 3 | title: SafeMath 4 | --- 5 | 6 |

library SafeMath

Math operations with safety checks that throw on error.

Reference

Functions

  • add

    function add(uint256 _a, uint256 _b) internal pure returns  (uint256) 

    Adds two numbers, throws on overflow.

    Parameters:
    _a - uint256
    _b - uint256
    Returns:
    uint256
  • div

    function div(uint256 _a, uint256 _b) internal pure returns  (uint256) 

    Integer division of two numbers, truncating the quotient.

    Parameters:
    _a - uint256
    _b - uint256
    Returns:
    uint256
  • mul

    function mul(uint256 _a, uint256 _b) internal pure returns  (uint256) 

    Multiplies two numbers, throws on overflow.

    Parameters:
    _a - uint256
    _b - uint256
    Returns:
    uint256
  • sub

    function sub(uint256 _a, uint256 _b) internal pure returns  (uint256) 

    Subtracts two numbers, throws on overflow (i.e. if subtrahend is greater than minuend).

    Parameters:
    _a - uint256
    _b - uint256
    Returns:
    uint256
7 | -------------------------------------------------------------------------------- /docs/website/blog/2016-03-11-blog-post.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Blog Title 3 | author: Blog Author 4 | authorURL: http://twitter.com/ 5 | authorFBID: 100002976521003 6 | --- 7 | 8 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus elementum massa eget nulla aliquet sagittis. Proin odio tortor, vulputate ut odio in, ultrices ultricies augue. Cras ornare ultrices lorem malesuada iaculis. Etiam sit amet libero tempor, pulvinar mauris sed, sollicitudin sapien. 9 | 10 | 11 | 12 | Mauris vestibulum ullamcorper nibh, ut semper purus pulvinar ut. Donec volutpat orci sit amet mauris malesuada, non pulvinar augue aliquam. Vestibulum ultricies at urna ut suscipit. Morbi iaculis, erat at imperdiet semper, ipsum nulla sodales erat, eget tincidunt justo dui quis justo. Pellentesque dictum bibendum diam at aliquet. Sed pulvinar, dolor quis finibus ornare, eros odio facilisis erat, eu rhoncus nunc dui sed ex. Nunc gravida dui massa, sed ornare arcu tincidunt sit amet. Maecenas efficitur sapien neque, a laoreet libero feugiat ut. 13 | 14 | Nulla facilisi. Maecenas sodales nec purus eget posuere. Sed sapien quam, pretium a risus in, porttitor dapibus erat. Sed sit amet fringilla ipsum, eget iaculis augue. Integer sollicitudin tortor quis ultricies aliquam. Suspendisse fringilla nunc in tellus cursus, at placerat tellus scelerisque. Sed tempus elit a sollicitudin rhoncus. Nulla facilisi. Morbi nec dolor dolor. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Cras et aliquet lectus. Pellentesque sit amet eros nisi. Quisque ac sapien in sapien congue accumsan. Nullam in posuere ante. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Proin lacinia leo a nibh fringilla pharetra. 15 | 16 | Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Proin venenatis lectus dui, vel ultrices ante bibendum hendrerit. Aenean egestas feugiat dui id hendrerit. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Curabitur in tellus laoreet, eleifend nunc id, viverra leo. Proin vulputate non dolor vel vulputate. Curabitur pretium lobortis felis, sit amet finibus lorem suscipit ut. Sed non mollis risus. Duis sagittis, mi in euismod tincidunt, nunc mauris vestibulum urna, at euismod est elit quis erat. Phasellus accumsan vitae neque eu placerat. In elementum arcu nec tellus imperdiet, eget maximus nulla sodales. Curabitur eu sapien eget nisl sodales fermentum. 17 | 18 | Phasellus pulvinar ex id commodo imperdiet. Praesent odio nibh, sollicitudin sit amet faucibus id, placerat at metus. Donec vitae eros vitae tortor hendrerit finibus. Interdum et malesuada fames ac ante ipsum primis in faucibus. Quisque vitae purus dolor. Duis suscipit ac nulla et finibus. Phasellus ac sem sed dui dictum gravida. Phasellus eleifend vestibulum facilisis. Integer pharetra nec enim vitae mattis. Duis auctor, lectus quis condimentum bibendum, nunc dolor aliquam massa, id bibendum orci velit quis magna. Ut volutpat nulla nunc, sed interdum magna condimentum non. Sed urna metus, scelerisque vitae consectetur a, feugiat quis magna. Donec dignissim ornare nisl, eget tempor risus malesuada quis. 19 | -------------------------------------------------------------------------------- /docs/website/blog/2017-04-10-blog-post-two.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: New Blog Post 3 | author: Blog Author 4 | authorURL: http://twitter.com/ 5 | authorFBID: 100002976521003 6 | --- 7 | 8 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus elementum massa eget nulla aliquet sagittis. Proin odio tortor, vulputate ut odio in, ultrices ultricies augue. Cras ornare ultrices lorem malesuada iaculis. Etiam sit amet libero tempor, pulvinar mauris sed, sollicitudin sapien. 9 | 10 | 11 | 12 | Mauris vestibulum ullamcorper nibh, ut semper purus pulvinar ut. Donec volutpat orci sit amet mauris malesuada, non pulvinar augue aliquam. Vestibulum ultricies at urna ut suscipit. Morbi iaculis, erat at imperdiet semper, ipsum nulla sodales erat, eget tincidunt justo dui quis justo. Pellentesque dictum bibendum diam at aliquet. Sed pulvinar, dolor quis finibus ornare, eros odio facilisis erat, eu rhoncus nunc dui sed ex. Nunc gravida dui massa, sed ornare arcu tincidunt sit amet. Maecenas efficitur sapien neque, a laoreet libero feugiat ut. 13 | 14 | Nulla facilisi. Maecenas sodales nec purus eget posuere. Sed sapien quam, pretium a risus in, porttitor dapibus erat. Sed sit amet fringilla ipsum, eget iaculis augue. Integer sollicitudin tortor quis ultricies aliquam. Suspendisse fringilla nunc in tellus cursus, at placerat tellus scelerisque. Sed tempus elit a sollicitudin rhoncus. Nulla facilisi. Morbi nec dolor dolor. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Cras et aliquet lectus. Pellentesque sit amet eros nisi. Quisque ac sapien in sapien congue accumsan. Nullam in posuere ante. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Proin lacinia leo a nibh fringilla pharetra. 15 | 16 | Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Proin venenatis lectus dui, vel ultrices ante bibendum hendrerit. Aenean egestas feugiat dui id hendrerit. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Curabitur in tellus laoreet, eleifend nunc id, viverra leo. Proin vulputate non dolor vel vulputate. Curabitur pretium lobortis felis, sit amet finibus lorem suscipit ut. Sed non mollis risus. Duis sagittis, mi in euismod tincidunt, nunc mauris vestibulum urna, at euismod est elit quis erat. Phasellus accumsan vitae neque eu placerat. In elementum arcu nec tellus imperdiet, eget maximus nulla sodales. Curabitur eu sapien eget nisl sodales fermentum. 17 | 18 | Phasellus pulvinar ex id commodo imperdiet. Praesent odio nibh, sollicitudin sit amet faucibus id, placerat at metus. Donec vitae eros vitae tortor hendrerit finibus. Interdum et malesuada fames ac ante ipsum primis in faucibus. Quisque vitae purus dolor. Duis suscipit ac nulla et finibus. Phasellus ac sem sed dui dictum gravida. Phasellus eleifend vestibulum facilisis. Integer pharetra nec enim vitae mattis. Duis auctor, lectus quis condimentum bibendum, nunc dolor aliquam massa, id bibendum orci velit quis magna. Ut volutpat nulla nunc, sed interdum magna condimentum non. Sed urna metus, scelerisque vitae consectetur a, feugiat quis magna. Donec dignissim ornare nisl, eget tempor risus malesuada quis. 19 | -------------------------------------------------------------------------------- /docs/website/blog/2017-09-25-testing-rss.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Adding RSS Support - RSS Truncation Test 3 | author: Eric Nakagawa 4 | authorURL: http://twitter.com/ericnakagawa 5 | authorFBID: 661277173 6 | --- 7 | 1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890 8 | 9 | This should be truncated. 10 | 11 | This line should never render in XML. 12 | -------------------------------------------------------------------------------- /docs/website/blog/2017-09-26-adding-rss.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Adding RSS Support 3 | author: Eric Nakagawa 4 | authorURL: http://twitter.com/ericnakagawa 5 | authorFBID: 661277173 6 | --- 7 | 8 | This is a test post. 9 | 10 | A whole bunch of other information. 11 | -------------------------------------------------------------------------------- /docs/website/blog/2017-10-24-new-version-1.0.0.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: New Version 1.0.0 3 | author: Eric Nakagawa 4 | authorURL: http://twitter.com/ericnakagawa 5 | authorFBID: 661277173 6 | --- 7 | 8 | This blog post will test file name parsing issues when periods are present. 9 | -------------------------------------------------------------------------------- /docs/website/core/Footer.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2017-present, Facebook, Inc. 3 | * 4 | * This source code is licensed under the MIT license found in the 5 | * LICENSE file in the root directory of this source tree. 6 | */ 7 | 8 | const React = require('react'); 9 | 10 | class Footer extends React.Component { 11 | docUrl(doc, language) { 12 | const baseUrl = this.props.config.baseUrl; 13 | return `${baseUrl}docs/${language ? `${language}/` : ''}${doc}`; 14 | } 15 | 16 | pageUrl(doc, language) { 17 | const baseUrl = this.props.config.baseUrl; 18 | return baseUrl + (language ? `${language}/` : '') + doc; 19 | } 20 | 21 | render() { 22 | return ( 23 | 26 | ); 27 | } 28 | } 29 | 30 | module.exports = Footer; 31 | -------------------------------------------------------------------------------- /docs/website/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "scripts": { 3 | "examples": "docusaurus-examples", 4 | "start": "docusaurus-start", 5 | "build": "docusaurus-build", 6 | "publish-gh-pages": "docusaurus-publish", 7 | "write-translations": "docusaurus-write-translations", 8 | "version": "docusaurus-version", 9 | "rename-version": "docusaurus-rename-version" 10 | }, 11 | "devDependencies": { 12 | "docusaurus": "^1.3.3" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /docs/website/pages/en/help.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2017-present, Facebook, Inc. 3 | * 4 | * This source code is licensed under the MIT license found in the 5 | * LICENSE file in the root directory of this source tree. 6 | */ 7 | 8 | const React = require('react'); 9 | 10 | const CompLibrary = require('../../core/CompLibrary.js'); 11 | 12 | const Container = CompLibrary.Container; 13 | const GridBlock = CompLibrary.GridBlock; 14 | 15 | const siteConfig = require(`${process.cwd()}/siteConfig.js`); 16 | 17 | function docUrl(doc, language) { 18 | return `${siteConfig.baseUrl}docs/${language ? `${language}/` : ''}${doc}`; 19 | } 20 | 21 | class Help extends React.Component { 22 | render() { 23 | const language = this.props.language || ''; 24 | const supportLinks = [ 25 | { 26 | content: `Learn more using the [documentation on this site.](${docUrl( 27 | 'doc1.html', 28 | language 29 | )})`, 30 | title: 'Browse Docs', 31 | }, 32 | { 33 | content: 'Ask questions about the documentation and project', 34 | title: 'Join the community', 35 | }, 36 | { 37 | content: "Find out what's new with this project", 38 | title: 'Stay up to date', 39 | }, 40 | ]; 41 | 42 | return ( 43 |
44 | 45 |
46 |
47 |

Need help?

48 |
49 |

This project is maintained by a dedicated group of people.

50 | 51 |
52 |
53 |
54 | ); 55 | } 56 | } 57 | 58 | module.exports = Help; 59 | -------------------------------------------------------------------------------- /docs/website/pages/en/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2017-present, Facebook, Inc. 3 | * 4 | * This source code is licensed under the MIT license found in the 5 | * LICENSE file in the root directory of this source tree. 6 | */ 7 | 8 | const React = require('react'); 9 | 10 | const CompLibrary = require('../../core/CompLibrary.js'); 11 | 12 | const MarkdownBlock = CompLibrary.MarkdownBlock; /* Used to read markdown */ 13 | const Container = CompLibrary.Container; 14 | const GridBlock = CompLibrary.GridBlock; 15 | 16 | const siteConfig = require(`${process.cwd()}/siteConfig.js`); 17 | 18 | function imgUrl(img) { 19 | return `${siteConfig.baseUrl}img/${img}`; 20 | } 21 | 22 | function docUrl(doc, language) { 23 | return `${siteConfig.baseUrl}docs/${language ? `${language}/` : ''}${doc}`; 24 | } 25 | 26 | function pageUrl(page, language) { 27 | return siteConfig.baseUrl + (language ? `${language}/` : '') + page; 28 | } 29 | 30 | class Button extends React.Component { 31 | render() { 32 | return ( 33 |
34 | 35 | {this.props.children} 36 | 37 |
38 | ); 39 | } 40 | } 41 | 42 | Button.defaultProps = { 43 | target: '_self', 44 | }; 45 | 46 | const SplashContainer = props => ( 47 |
48 |
49 |
{props.children}
50 |
51 |
52 | ); 53 | 54 | const Logo = props => ( 55 |
56 | Project Logo 57 |
58 | ); 59 | 60 | const ProjectTitle = () => ( 61 |

62 | {siteConfig.title} 63 | {siteConfig.tagline} 64 |

65 | ); 66 | 67 | const PromoSection = props => ( 68 |
69 |
70 |
{props.children}
71 |
72 |
73 | ); 74 | 75 | class HomeSplash extends React.Component { 76 | render() { 77 | const language = this.props.language || ''; 78 | return ( 79 | 80 |
81 | 82 | 83 | 84 | 85 |
86 |
87 | ); 88 | } 89 | } 90 | 91 | const Block = props => ( 92 | 96 | 97 | 98 | ); 99 | 100 | const Features = () => ( 101 | 102 | {[ 103 | { 104 | content: 'This is the content of my feature', 105 | image: imgUrl('docusaurus.svg'), 106 | imageAlign: 'top', 107 | title: 'Feature One', 108 | }, 109 | { 110 | content: 'The content of my second feature', 111 | image: imgUrl('docusaurus.svg'), 112 | imageAlign: 'top', 113 | title: 'Feature Two', 114 | }, 115 | ]} 116 | 117 | ); 118 | 119 | const FeatureCallout = () => ( 120 |
123 |

Feature Callout

124 | These are features of this project 125 |
126 | ); 127 | 128 | const LearnHow = () => ( 129 | 130 | {[ 131 | { 132 | content: 'Talk about learning how to use this', 133 | image: imgUrl('docusaurus.svg'), 134 | imageAlign: 'right', 135 | title: 'Learn How', 136 | }, 137 | ]} 138 | 139 | ); 140 | 141 | const TryOut = () => ( 142 | 143 | {[ 144 | { 145 | content: 'Talk about trying this out', 146 | image: imgUrl('docusaurus.svg'), 147 | imageAlign: 'left', 148 | title: 'Try it Out', 149 | }, 150 | ]} 151 | 152 | ); 153 | 154 | const Description = () => ( 155 | 156 | {[ 157 | { 158 | content: 'This is another description of how this project is useful', 159 | image: imgUrl('docusaurus.svg'), 160 | imageAlign: 'right', 161 | title: 'Description', 162 | }, 163 | ]} 164 | 165 | ); 166 | 167 | const Showcase = props => { 168 | if ((siteConfig.users || []).length === 0) { 169 | return null; 170 | } 171 | 172 | const showcase = siteConfig.users.filter(user => user.pinned).map(user => ( 173 | 174 | {user.caption} 175 | 176 | )); 177 | 178 | return ( 179 |
180 |

Who is Using This?

181 |

This project is used by all these people

182 |
{showcase}
183 |
184 | 185 | More {siteConfig.title} Users 186 | 187 |
188 |
189 | ); 190 | }; 191 | 192 | class Index extends React.Component { 193 | render() { 194 | const language = this.props.language || ''; 195 | 196 | return ( 197 |
198 | 199 |
200 | ); 201 | } 202 | } 203 | 204 | module.exports = Index; 205 | -------------------------------------------------------------------------------- /docs/website/pages/en/users.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2017-present, Facebook, Inc. 3 | * 4 | * This source code is licensed under the MIT license found in the 5 | * LICENSE file in the root directory of this source tree. 6 | */ 7 | 8 | const React = require('react'); 9 | 10 | const CompLibrary = require('../../core/CompLibrary.js'); 11 | 12 | const Container = CompLibrary.Container; 13 | 14 | const siteConfig = require(`${process.cwd()}/siteConfig.js`); 15 | 16 | class Users extends React.Component { 17 | render() { 18 | if ((siteConfig.users || []).length === 0) { 19 | return null; 20 | } 21 | 22 | const editUrl = `${siteConfig.repoUrl}/edit/master/website/siteConfig.js`; 23 | const showcase = siteConfig.users.map(user => ( 24 | 25 | {user.caption} 26 | 27 | )); 28 | 29 | return ( 30 |
31 | 32 |
33 |
34 |

Who is Using This?

35 |

This project is used by many folks

36 |
37 |
{showcase}
38 |

Are you using this project?

39 | 40 | Add your company 41 | 42 |
43 |
44 |
45 | ); 46 | } 47 | } 48 | 49 | module.exports = Users; 50 | -------------------------------------------------------------------------------- /docs/website/sidebars.json: -------------------------------------------------------------------------------- 1 | { 2 | "docs-api": { 3 | "ORACLES": [ 4 | "Oracles_BasicOracle", 5 | "Oracles_IOracle", 6 | "Oracles_OracleBase", 7 | "Oracles_PaidOracle", 8 | "Oracles_SignedOracle" 9 | ], 10 | "PUSHORACLES": [ 11 | "PushOracles_BasicPushOracle", 12 | "PushOracles_IOracleConsumer", 13 | "PushOracles_PushOracleBase", 14 | "PushOracles_SignedPushOracle" 15 | ] 16 | } 17 | } -------------------------------------------------------------------------------- /docs/website/siteConfig.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2017-present, Facebook, Inc. 3 | * 4 | * This source code is licensed under the MIT license found in the 5 | * LICENSE file in the root directory of this source tree. 6 | */ 7 | 8 | const siteConfig = { 9 | title: 'Tidbit', 10 | tagline: 'A library for oracles on Ethereum.', 11 | url: 'https://tidbit.org', 12 | baseUrl: '/tidbit/', 13 | organizationName: 'levelkdev', 14 | projectName: 'tidbit', 15 | headerLinks: [ 16 | { 17 | doc: 'Oracles_IOracle', 18 | label: 'Docs', 19 | }, 20 | { 21 | href: 'https://github.com/levelkdev/tidbit', 22 | label: 'Github', 23 | }, 24 | ], 25 | //headerIcon: 'img/image.png', 26 | //footerIcon: 'img/image.png', 27 | //favicon: 'img/favicon.png', 28 | colors: { 29 | primaryColor: '#1d2d3c', 30 | secondaryColor: 'white', 31 | }, 32 | copyright: 'Copyright © 2018 Level K, Inc.', 33 | highlight: { 34 | theme: 'default', 35 | }, 36 | scripts: ['https://buttons.github.io/buttons.js'], 37 | stylesheets: [ 38 | 'https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css', 39 | 'https://fonts.googleapis.com/css?family=Lato:100,200,300,400,500,700,400italic,700italic', 40 | ], 41 | repoUrl: 'https://github.com/levelkdev/tidbit', 42 | }; 43 | 44 | module.exports = siteConfig; 45 | -------------------------------------------------------------------------------- /docs/website/static/css/custom.css: -------------------------------------------------------------------------------- 1 | /* your custom css */ 2 | 3 | a { 4 | color: #4E82C2 5 | } 6 | 7 | @media only screen and (min-device-width: 360px) and (max-device-width: 736px) { 8 | } 9 | 10 | @media only screen and (min-width: 1024px) { 11 | } 12 | 13 | @media only screen and (max-width: 1023px) { 14 | } 15 | 16 | @media only screen and (min-width: 1400px) { 17 | } 18 | 19 | @media only screen and (min-width: 1500px) { 20 | } 21 | -------------------------------------------------------------------------------- /docs/website/static/img/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cwhinfrey/tidbit/9574e7952c960806e11427d39ecbd4b98cdd12bf/docs/website/static/img/favicon.png -------------------------------------------------------------------------------- /docs/website/static/img/favicon/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cwhinfrey/tidbit/9574e7952c960806e11427d39ecbd4b98cdd12bf/docs/website/static/img/favicon/favicon.ico -------------------------------------------------------------------------------- /migrations/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cwhinfrey/tidbit/9574e7952c960806e11427d39ecbd4b98cdd12bf/migrations/.gitkeep -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "tidbit", 3 | "version": "0.1.0", 4 | "license": "MIT", 5 | "main": "lib/index.js", 6 | "files": [ 7 | "build", 8 | "contracts", 9 | "test" 10 | ], 11 | "repository": { 12 | "type": "git", 13 | "url": "https://github.com/levelkdev/tidbit.git" 14 | }, 15 | "dependencies": { 16 | "bn-chai": "^1.0.1", 17 | "moment": "^2.22.0", 18 | "openzeppelin-solidity": "^2.1.2", 19 | "solc": "^0.4.24", 20 | "truffle-blockchain-utils": "^0.0.3", 21 | "truffle-hdwallet-provider": "^0.0.3", 22 | "zos-lib": "2.1.2" 23 | }, 24 | "devDependencies": { 25 | "babel-cli": "^6.24.1", 26 | "babel-plugin-transform-es2015-modules-commonjs": "^6.24.1", 27 | "babel-plugin-transform-runtime": "^6.23.0", 28 | "babel-polyfill": "^6.26.0", 29 | "babel-preset-es2015": "^6.24.1", 30 | "babel-preset-stage-0": "^6.24.1", 31 | "babel-register": "^6.26.0", 32 | "chai": "^4.1.2", 33 | "cross-env": "^5.1.4", 34 | "ganache-cli": "^6.1.6", 35 | "lk-test-helpers": "^0.1.3", 36 | "openzeppelin-test-helpers": "0.1.2", 37 | "snazzy": "^7.1.1", 38 | "solidity-coverage": "^0.5.5", 39 | "solidity-docgen": "^0.1.0", 40 | "standard": "^10.0.3", 41 | "truffle": "^5.0.3", 42 | "web3": "1.0.0-beta.35" 43 | }, 44 | "scripts": { 45 | "build": "node scripts/build.js", 46 | "build-docs": "scripts/buildDocs.sh", 47 | "cleanup": "rm -rf build/ && mkdir build && rm -rf lib/ && mkdir lib", 48 | "compile": "npm run cleanup && truffle compile --all && npm run build", 49 | "deploy": "npm run cleanup && truffle migrate --reset && npm run build", 50 | "deploy-rinkeby": "npm run cleanup && truffle migrate --reset --network rinkeby && npm run build", 51 | "test": "npm run build && scripts/test.sh", 52 | "test:truffle": "truffle test", 53 | "test:coverage": "npm run build && npm run solidity-coverage", 54 | "ganache-cli": "scripts/runGanacheCLI.sh", 55 | "lint": "standard --verbose | snazzy" 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /password_kovan.txt: -------------------------------------------------------------------------------- 1 | password 2 | -------------------------------------------------------------------------------- /scripts/build.js: -------------------------------------------------------------------------------- 1 | var fs = require('fs') 2 | 3 | var contractsPath = 'build/contracts' 4 | var bundleFilePath = 'lib/index.js' 5 | 6 | fs.readdir(contractsPath, function (err, items) { 7 | if (err) { 8 | return console.log(err) 9 | } 10 | 11 | var bundle = 'module.exports = {' 12 | 13 | for (var i = 0; i < items.length; i++) { 14 | var fileName = items[i] 15 | var contractName = fileName.substr(0, fileName.length - 5) 16 | var data = fs.readFileSync(`${contractsPath}/${fileName}`).toString() 17 | bundle += `"${contractName}": ${data},` 18 | } 19 | 20 | bundle = bundle.substr(0, bundle.length - 1) 21 | 22 | bundle += '}' 23 | 24 | fs.writeFile(bundleFilePath, bundle, function (err) { 25 | if (err) { 26 | return console.log(err) 27 | } 28 | }) 29 | }) 30 | -------------------------------------------------------------------------------- /scripts/buildDocs.sh: -------------------------------------------------------------------------------- 1 | rm -rf docs/docs 2 | mkdir docs/docs 3 | SOLC_ARGS='openzeppelin-solidity=$PWD/node_modules/openzeppelin-solidity' \ 4 | npx solidity-docgen ./ ./contracts --exclude ./Mocks ./docs 5 | -------------------------------------------------------------------------------- /scripts/runGanacheCLI.sh: -------------------------------------------------------------------------------- 1 | ganache-cli \ 2 | --account="0x2bdd21761a483f71054e14f5b827213567971c676928d9a1808cbfa4b7501200,1000000000000000000000000" \ 3 | --account="0x2bdd21761a483f71054e14f5b827213567971c676928d9a1808cbfa4b7501201,1000000000000000000000000" \ 4 | --account="0x2bdd21761a483f71054e14f5b827213567971c676928d9a1808cbfa4b7501202,1000000000000000000000000" \ 5 | --account="0x2bdd21761a483f71054e14f5b827213567971c676928d9a1808cbfa4b7501203,1000000000000000000000000" \ 6 | --account="0x2bdd21761a483f71054e14f5b827213567971c676928d9a1808cbfa4b7501204,1000000000000000000000000" \ 7 | --account="0x2bdd21761a483f71054e14f5b827213567971c676928d9a1808cbfa4b7501205,1000000000000000000000000" \ 8 | --account="0x2bdd21761a483f71054e14f5b827213567971c676928d9a1808cbfa4b7501206,1000000000000000000000000" \ 9 | --account="0x2bdd21761a483f71054e14f5b827213567971c676928d9a1808cbfa4b7501207,1000000000000000000000000" \ 10 | --account="0x2bdd21761a483f71054e14f5b827213567971c676928d9a1808cbfa4b7501208,1000000000000000000000000" \ 11 | --account="0x2bdd21761a483f71054e14f5b827213567971c676928d9a1808cbfa4b7501209,1000000000000000000000000" \ 12 | --port=8546 13 | -------------------------------------------------------------------------------- /scripts/test.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | # really would like to modify testrpc to add: 4 | # - account params from a JSON file 5 | # - `testrpc start` and `testrpc stop`, so these custom scripts don't 6 | # have to be added to every project 7 | 8 | output=$(nc -z localhost 8546; echo $?) 9 | [ $output -eq "0" ] && trpc_running=true 10 | if [ ! $trpc_running ]; then 11 | echo "Starting our own ganache-cli node instance" 12 | # we give each account 1M ether, needed for high-value tests 13 | ganache-cli \ 14 | --account="0x2bdd21761a483f71054e14f5b827213567971c676928d9a1808cbfa4b7501200,1000000000000000000000000" \ 15 | --account="0x2bdd21761a483f71054e14f5b827213567971c676928d9a1808cbfa4b7501201,1000000000000000000000000" \ 16 | --account="0x2bdd21761a483f71054e14f5b827213567971c676928d9a1808cbfa4b7501202,1000000000000000000000000" \ 17 | --account="0x2bdd21761a483f71054e14f5b827213567971c676928d9a1808cbfa4b7501203,1000000000000000000000000" \ 18 | --account="0x2bdd21761a483f71054e14f5b827213567971c676928d9a1808cbfa4b7501204,1000000000000000000000000" \ 19 | --account="0x2bdd21761a483f71054e14f5b827213567971c676928d9a1808cbfa4b7501205,1000000000000000000000000" \ 20 | --account="0x2bdd21761a483f71054e14f5b827213567971c676928d9a1808cbfa4b7501206,1000000000000000000000000" \ 21 | --account="0x2bdd21761a483f71054e14f5b827213567971c676928d9a1808cbfa4b7501207,1000000000000000000000000" \ 22 | --account="0x2bdd21761a483f71054e14f5b827213567971c676928d9a1808cbfa4b7501208,1000000000000000000000000" \ 23 | --account="0x2bdd21761a483f71054e14f5b827213567971c676928d9a1808cbfa4b7501209,1000000000000000000000000" \ 24 | --port=8546 \ 25 | > /dev/null & 26 | trpc_pid=$! 27 | fi 28 | npm run test:truffle 29 | kill -9 $trpc_pid 30 | -------------------------------------------------------------------------------- /test/DataFeedOracles/DataFeedOracleBase.test.js: -------------------------------------------------------------------------------- 1 | import { expectEvent, shouldFail } from 'openzeppelin-test-helpers' 2 | const { soliditySha3, toAscii, fromAscii, padRight, BN } = web3.utils 3 | 4 | const DataFeedOracleBase = artifacts.require('DataFeedOracleBase') 5 | 6 | require('chai').should() 7 | 8 | const now = Math.round((new Date()).getTime() / 1000) 9 | 10 | const ORACLE_INDEX_0 = 0 11 | const ORACLE_INDEX_1 = 1 12 | const ORACLE_INDEX_5 = 5 13 | const ORACLE_INDEX_7 = 7 14 | const ORACLE_INDEX_1_DATE = now - 4 * 60 * 60 | 0 15 | const ORACLE_INDEX_2_DATE = now - 3 * 60 * 60 | 0 16 | const ORACLE_INDEX_3_DATE = now - 2 * 60 * 60 | 0 17 | const ORACLE_INDEX_4_DATE = now - 1 * 60 * 60 | 0 18 | const ORACLE_INDEX_5_DATE = now | 0 19 | const HALF_AN_HOUR_AGO = now - 0.5 * 60 * 60 | 0 20 | const HALF_AN_HOUR_LATER = now + 0.5 * 60 * 60 | 0 21 | const ORACLE_INDEX_1_RESULT = soliditySha3('4 hours ago') 22 | const ORACLE_INDEX_2_RESULT = soliditySha3('3 hours ago') 23 | const ORACLE_INDEX_3_RESULT = soliditySha3('2 hours ago') 24 | const ORACLE_INDEX_4_RESULT = soliditySha3('1 hour ago') 25 | const ORACLE_INDEX_5_RESULT = soliditySha3('now') 26 | const HALF_AN_HOUR_LATER_RESULT = soliditySha3('0.5 hours later') 27 | 28 | const RESULTS_DATA = new Map([ 29 | [ORACLE_INDEX_1_DATE, ORACLE_INDEX_1_RESULT], 30 | [ORACLE_INDEX_2_DATE, ORACLE_INDEX_2_RESULT], 31 | [ORACLE_INDEX_3_DATE, ORACLE_INDEX_3_RESULT], 32 | [ORACLE_INDEX_4_DATE, ORACLE_INDEX_4_RESULT], 33 | [ORACLE_INDEX_5_DATE, ORACLE_INDEX_5_RESULT] // index 5 34 | ]) 35 | 36 | contract('DataFeedOracleBase', (accounts) => { 37 | const dataSource = accounts[1] 38 | 39 | let oracle 40 | beforeEach(async ()=> { 41 | oracle = await DataFeedOracleBase.new() 42 | }) 43 | 44 | it('cannot be initialized with data source as the zero address', async () => { 45 | await shouldFail(oracle.initialize(0)) 46 | }) 47 | 48 | it('is initialized with the correct state', async () => { 49 | await oracle.initialize(dataSource) 50 | 51 | await shouldFail(oracle.resultByIndex(ORACLE_INDEX_1)) 52 | await shouldFail(oracle.resultByIndex(ORACLE_INDEX_0)) 53 | await shouldFail(oracle.resultByDate(now)) 54 | 55 | const dateHasResult = await oracle.dateHasResult(now) 56 | dateHasResult.should.equal(false) 57 | 58 | const indexHasResult = await oracle.indexHasResult(ORACLE_INDEX_1) 59 | indexHasResult.should.equal(false) 60 | }) 61 | 62 | it('can set result by data source', async () => { 63 | await oracle.initialize(dataSource) 64 | 65 | for(var [key, value] of RESULTS_DATA) { 66 | await oracle.setResult(value, key, { from: dataSource }); 67 | } 68 | 69 | const isResultSet = await oracle.dateHasResult(ORACLE_INDEX_5_DATE) 70 | isResultSet.should.equal(true) 71 | const indexHasResult = await oracle.indexHasResult(ORACLE_INDEX_5) 72 | indexHasResult.should.equal(true) 73 | 74 | const [resultByIndex, date] = Object.values(await oracle.resultByIndex(ORACLE_INDEX_5)) 75 | resultByIndex.should.equal(ORACLE_INDEX_5_RESULT) 76 | date.should.eq.BN(ORACLE_INDEX_5_DATE) 77 | const [resultByDate, index] = Object.values(await oracle.resultByDate(ORACLE_INDEX_5_DATE)) 78 | resultByDate.should.equal(ORACLE_INDEX_5_RESULT) 79 | index.should.eq.BN(ORACLE_INDEX_5) 80 | 81 | const latestResultDate = await oracle.latestResultDate() 82 | latestResultDate.should.eq.BN(ORACLE_INDEX_5_DATE) 83 | 84 | const latestResult = await oracle.latestResult() 85 | latestResult.should.equal(ORACLE_INDEX_5_RESULT) 86 | }) 87 | 88 | it('cannot be set by a different data source', async () => { 89 | await oracle.initialize(dataSource) 90 | 91 | await shouldFail(oracle.setResult(ORACLE_INDEX_5_RESULT, ORACLE_INDEX_5_DATE, { from: accounts[2] })) 92 | 93 | const isResultSet = await oracle.dateHasResult(ORACLE_INDEX_5_DATE) 94 | isResultSet.should.equal(false) 95 | const indexHasResult = await oracle.indexHasResult(ORACLE_INDEX_1) 96 | indexHasResult.should.equal(false) 97 | 98 | await shouldFail(oracle.resultByIndex(ORACLE_INDEX_1)) 99 | await shouldFail(oracle.resultByDate(ORACLE_INDEX_5_DATE)) 100 | }) 101 | 102 | it('cannot be set twice', async () => { 103 | await oracle.initialize(dataSource) 104 | await oracle.setResult(ORACLE_INDEX_5_RESULT, ORACLE_INDEX_5_DATE, { from: dataSource }) 105 | await shouldFail(oracle.setResult(ORACLE_INDEX_5_RESULT, ORACLE_INDEX_5_DATE, { from: dataSource })) 106 | }) 107 | 108 | it('cannot set date earlier than last added data feed', async () => { 109 | await oracle.initialize(dataSource) 110 | await oracle.setResult(ORACLE_INDEX_5_RESULT, ORACLE_INDEX_5_DATE, { from: dataSource }) 111 | await shouldFail(oracle.setResult(ORACLE_INDEX_4_RESULT, ORACLE_INDEX_4_DATE, { from: dataSource })) 112 | }) 113 | 114 | it('cannot set a date later than the `now` value', async () => { 115 | await oracle.initialize(dataSource) 116 | await shouldFail( 117 | oracle.setResult(HALF_AN_HOUR_LATER_RESULT, HALF_AN_HOUR_LATER, { from: dataSource }) 118 | ) 119 | }) 120 | 121 | it('cannot fetch result with invalid index or date', async () => { 122 | await oracle.initialize(dataSource) 123 | for( var [key, value] of RESULTS_DATA ){ 124 | await oracle.setResult(value, key, { from: dataSource }); 125 | } 126 | const indexHasResult = await oracle.indexHasResult(ORACLE_INDEX_7) 127 | indexHasResult.should.equal(false) 128 | await shouldFail(oracle.resultByIndex(ORACLE_INDEX_7)) 129 | 130 | let isResultSet = await oracle.dateHasResult(HALF_AN_HOUR_AGO) 131 | isResultSet.should.equal(false) 132 | await shouldFail(oracle.resultByDate(HALF_AN_HOUR_AGO)) 133 | 134 | isResultSet = await oracle.dateHasResult(HALF_AN_HOUR_LATER) 135 | isResultSet.should.equal(false) 136 | await shouldFail(oracle.resultByDate(HALF_AN_HOUR_LATER)) 137 | }) 138 | 139 | it('should emit ResultSet event', async () => { 140 | await oracle.initialize(dataSource) 141 | const { logs } = await oracle.setResult(ORACLE_INDEX_1_RESULT, ORACLE_INDEX_1_DATE, { from: dataSource}) 142 | const dateResult = new BN(ORACLE_INDEX_1_DATE) 143 | const indexResult = new BN(ORACLE_INDEX_1) 144 | await expectEvent.inLogs( 145 | logs, 146 | 'ResultSet', 147 | { _result: ORACLE_INDEX_1_RESULT, 148 | _date: dateResult, 149 | _index: indexResult, 150 | _sender: dataSource 151 | } 152 | ) 153 | }) 154 | }) 155 | -------------------------------------------------------------------------------- /test/DataFeedOracles/MedianDataFeedOracle.test.js: -------------------------------------------------------------------------------- 1 | import { expectEvent, shouldFail } from 'openzeppelin-test-helpers' 2 | const { bytes32ToNumString, getMedian, numberToBytes32 } = require('./Utils.test') 3 | 4 | const MedianDataFeedOracle = artifacts.require('MedianDataFeedOracle') 5 | const DataFeedOracleBase = artifacts.require('DataFeedOracleBase') 6 | 7 | require('chai').should() 8 | 9 | const now = () => (new Date()).getTime() / 1000 10 | 11 | const ORACLE_INDEX_1_DATE = now() - 1 * 60 * 60 | 0 12 | const ORACLE_INDEX_2_DATE = now() | 0 13 | 14 | const DATES = [ORACLE_INDEX_1_DATE, ORACLE_INDEX_2_DATE] 15 | const PRICES = [[0, 1.00], [0, 1.36], [0, 1.49], [0, 1.88]] 16 | const ORACLE_RESULT = PRICES.map( 17 | y => y.map( 18 | z => [ DATES[y.indexOf(z)], numberToBytes32(z * Math.pow(10, 18) )] 19 | )) 20 | 21 | contract('MedianDataFeedOracle', (accounts) => { 22 | let dataFeedOracle, oracles 23 | const dataFeedOracleDataSource = accounts[0] 24 | const approvedDataFeeds = [accounts[1], accounts[2], accounts[3], accounts[4]] 25 | 26 | before(async () => { 27 | oracles = [] 28 | for (var i = 0; i < approvedDataFeeds.length; i++) { 29 | let oracle = await DataFeedOracleBase.new() 30 | await oracle.initialize(approvedDataFeeds[i]) 31 | 32 | for(var j = 0; j < ORACLE_RESULT[i].length; j++ ){ 33 | await oracle.setResult(ORACLE_RESULT[i][j][1], ORACLE_RESULT[i][j][0], { from: approvedDataFeeds[i]}); 34 | } 35 | oracles.push(oracle.address) 36 | } 37 | }) 38 | 39 | describe('initialize()', () => { 40 | beforeEach(async () => { 41 | dataFeedOracle = await MedianDataFeedOracle.new() 42 | }) 43 | 44 | it('cannot initilize medianDataFeedOracle with empty oracle array.', async () => { 45 | await shouldFail(dataFeedOracle.initialize([])) 46 | }) 47 | 48 | it('cannot be initialized twice', async () => { 49 | await dataFeedOracle.initialize(oracles, dataFeedOracleDataSource) 50 | await shouldFail(dataFeedOracle.initialize(oracles, dataFeedOracleDataSource)) 51 | }) 52 | 53 | it('sets the correct approvedDataFeedsLength', async () => { 54 | await dataFeedOracle.initialize(oracles, dataFeedOracleDataSource) 55 | expect((await dataFeedOracle.approvedDataFeedsLength()).toNumber()).to.equal(oracles.length) 56 | }) 57 | }) 58 | 59 | describe('setResult()', () => { 60 | beforeEach(async () => { 61 | dataFeedOracle = await MedianDataFeedOracle.new() 62 | await dataFeedOracle.initialize(oracles, dataFeedOracleDataSource) 63 | }) 64 | 65 | it('sets the correct median', async () => { 66 | await dataFeedOracle.setResult(oracles, { from: dataFeedOracleDataSource}) 67 | const latestResult = await dataFeedOracle.latestResult() 68 | const median = getMedian(PRICES.map(x => x[1] * Math.pow(10, 18))) 69 | bytes32ToNumString(latestResult).should.equal(median.toString()) 70 | const latestResultDate = await dataFeedOracle.latestResultDate() 71 | expect(latestResultDate.toNumber()).to.be.within(Math.floor(now()), Math.floor(now() + 1)) 72 | }) 73 | 74 | it('reverts if not all approved data feeds are included', async () => { 75 | await shouldFail(dataFeedOracle.setResult(oracles.slice(1,5), { from: dataFeedOracleDataSource})) 76 | }) 77 | 78 | it('reverts if there are any duplicated data feeds included', async () => { 79 | let duplicateOracles = [oracles[0], oracles[1], oracles[0], oracles[3]] 80 | await shouldFail(dataFeedOracle.setResult(duplicateOracles, { from: dataFeedOracleDataSource})) 81 | }) 82 | 83 | it('reverts if data feeds are not properly sorted', async () => { 84 | await shouldFail(dataFeedOracle.setResult([oracles[0], oracles[2], oracles[1], oracles[3]])) 85 | }) 86 | }) 87 | 88 | describe('addDataFeed()', () => { 89 | let dataFeedOracle 90 | 91 | beforeEach(async () => { 92 | dataFeedOracle = await MedianDataFeedOracle.new() 93 | await dataFeedOracle.initialize(oracles, dataFeedOracleDataSource) 94 | }) 95 | 96 | it('adds new dataFeed to approvedDataFeeds', async () => { 97 | expect(await dataFeedOracle.approvedDataFeeds(accounts[6])).to.equal(false) 98 | await dataFeedOracle.addDataFeed(accounts[6]) 99 | expect(await dataFeedOracle.approvedDataFeeds(accounts[6])).to.equal(true) 100 | }) 101 | 102 | it('updates the approvedDataFeedsLength', async () => { 103 | expect((await dataFeedOracle.approvedDataFeedsLength()).toNumber()).to.equal(4) 104 | await dataFeedOracle.addDataFeed(accounts[6]) 105 | expect((await dataFeedOracle.approvedDataFeedsLength()).toNumber()).to.equal(5) 106 | }) 107 | 108 | it('reverts if dataFeed is already a dataSource', async () => { 109 | expect(await dataFeedOracle.approvedDataFeeds(oracles[0])).to.equal(true) 110 | await shouldFail(dataFeedOracle.addDataFeed(oracles[0])) 111 | }) 112 | 113 | it('emits an AddedDataFeed event', async () => { 114 | const { logs } = await dataFeedOracle.addDataFeed(accounts[6]) 115 | expect(logs[0].event).to.equal('AddedDataFeed') 116 | expect(logs[0].args.dataFeed).to.equal(accounts[6]) 117 | }) 118 | }) 119 | 120 | describe('removeDataFeed()', () => { 121 | let dataFeedOracle 122 | 123 | beforeEach(async () => { 124 | dataFeedOracle = await MedianDataFeedOracle.new() 125 | await dataFeedOracle.initialize(oracles, dataFeedOracleDataSource) 126 | }) 127 | 128 | it('removes existing dataFeed from approvedDataFeeds', async () => { 129 | expect(await dataFeedOracle.approvedDataFeeds(oracles[0])).to.equal(true) 130 | await dataFeedOracle.removeDataFeed(oracles[0]) 131 | expect(await dataFeedOracle.approvedDataFeeds(oracles[0])).to.equal(false) 132 | }) 133 | 134 | it('updates the approvedDataFeedsLength', async () => { 135 | expect((await dataFeedOracle.approvedDataFeedsLength()).toNumber()).to.equal(4) 136 | await dataFeedOracle.removeDataFeed(oracles[0]) 137 | expect((await dataFeedOracle.approvedDataFeedsLength()).toNumber()).to.equal(3) 138 | }) 139 | 140 | it('reverts if dataFeed is not an existing dataSource', async () => { 141 | expect(await dataFeedOracle.approvedDataFeeds(accounts[6])).to.equal(false) 142 | await shouldFail(dataFeedOracle.removeDataFeed(accounts[6])) 143 | }) 144 | 145 | it('emits a RemoveDataFeed event', async () => { 146 | const { logs } = await dataFeedOracle.removeDataFeed(oracles[0]) 147 | expect(logs[0].event).to.equal('RemovedDataFeed') 148 | expect(logs[0].args.dataFeed).to.equal(oracles[0]) 149 | }) 150 | }) 151 | }) 152 | -------------------------------------------------------------------------------- /test/DataFeedOracles/Utils.test.js: -------------------------------------------------------------------------------- 1 | const { padLeft, BN} = web3.utils 2 | 3 | function numberToBytes32(number) { 4 | return padLeft(number, 64) 5 | } 6 | 7 | function bytes32ToNumString(bytes32str) { 8 | bytes32str = bytes32str.replace(/^0x/, ''); 9 | var bn = new BN(bytes32str, 16).fromTwos(256); 10 | return bn.toString(); 11 | } 12 | 13 | function getMedian(array) { 14 | const len = array.length; 15 | return len % 2 == 0 ? (array[len/2] + array[len/2 - 1]) / 2 : array[len/2] 16 | } 17 | 18 | module.exports = { 19 | bytes32ToNumString, 20 | getMedian, 21 | numberToBytes32 22 | } 23 | -------------------------------------------------------------------------------- /test/Mocks/MockToken.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.0; 2 | 3 | import "openzeppelin-solidity/contracts/token/ERC20/ERC20Mintable.sol"; 4 | 5 | contract MockToken is ERC20Mintable {} 6 | -------------------------------------------------------------------------------- /test/Oracles/BasicOracle.test.js: -------------------------------------------------------------------------------- 1 | import { expectEvent, shouldFail } from 'openzeppelin-test-helpers' 2 | const { soliditySha3, toAscii, fromAscii, padRight } = web3.utils 3 | 4 | const BasicOracle = artifacts.require('BasicOracle') 5 | 6 | require('chai').should() 7 | 8 | const RESULT = soliditySha3('hello oracle') 9 | 10 | contract('BasicOracle', (accounts) => { 11 | const dataSource = accounts[1] 12 | 13 | let oracle 14 | beforeEach(async ()=> { 15 | oracle = await BasicOracle.new() 16 | await oracle.initialize(dataSource) 17 | }) 18 | 19 | it('can set result by owner', async () => { 20 | await oracle.setResult(RESULT, { from: dataSource }) 21 | 22 | const result = await oracle.resultFor('0x0') 23 | result.should.equal(RESULT) 24 | 25 | const isResultSet = await oracle.isResultSet('0x0') 26 | isResultSet.should.equal(true) 27 | }) 28 | 29 | it('cannot be set by a different data source', async () => { 30 | await shouldFail(oracle.setResult(RESULT, { from: accounts[2] })) 31 | 32 | const isResultSet = await oracle.isResultSet('0x0') 33 | isResultSet.should.equal(false) 34 | }) 35 | 36 | it('cannot be set twice', async () => { 37 | await oracle.setResult(RESULT, { from: dataSource }) 38 | await shouldFail(oracle.setResult(RESULT, { from: dataSource })) 39 | }) 40 | 41 | it('should emit ResultSet event', async () => { 42 | const { logs } = await oracle.setResult(RESULT, { from: dataSource }) 43 | await expectEvent.inLogs( 44 | logs, 45 | 'ResultSet', 46 | { _result: RESULT, _sender: dataSource } 47 | ) 48 | }) 49 | }) 50 | -------------------------------------------------------------------------------- /test/Oracles/MultiOracle.test.js: -------------------------------------------------------------------------------- 1 | import { expectEvent, shouldFail } from 'openzeppelin-test-helpers' 2 | const { toAscii, fromAscii, soliditySha3 } = web3.utils 3 | 4 | const MultiOracle = artifacts.require('MultiOracle') 5 | 6 | const RESULT = soliditySha3('hello oracle1') 7 | const RESULT2 = soliditySha3('hello oracle2') 8 | const ZERO_ADDRESS = '0x0000000000000000000000000000000000000000' 9 | const ORACLE_ID_0 = '0x0' 10 | const ORACLE_ID_1 = '0x1' 11 | 12 | contract('MultiOracle', (accounts) => { 13 | const dataSource1 = accounts[1] 14 | const dataSource2 = accounts[2] 15 | 16 | let oracle 17 | beforeEach(async ()=> { 18 | oracle = await MultiOracle.new() 19 | }) 20 | 21 | it('requires a non-null dataSource', async () => { 22 | await shouldFail(oracle.newOracle(ORACLE_ID_0, ZERO_ADDRESS)) 23 | }) 24 | 25 | it('is initialized with the correct state with unset results', async () => { 26 | await shouldFail(oracle.resultFor(ORACLE_ID_1)) 27 | const isResultSet = await oracle.isResultSet(ORACLE_ID_1) 28 | isResultSet.should.equal(false) 29 | }) 30 | 31 | it('is initialized with the correct state with unset oracles', async () => { 32 | const isOracleSet0 = await oracle.isOracleSet(ORACLE_ID_0) 33 | isOracleSet0.should.equal(false) 34 | 35 | const isOracleSet1 = await oracle.isOracleSet(ORACLE_ID_1) 36 | isOracleSet1.should.equal(false) 37 | }) 38 | 39 | it('cannot set result for the same id twice', async () => { 40 | await oracle.newOracle(ORACLE_ID_0, dataSource1) 41 | await oracle.setResult(ORACLE_ID_0, RESULT, { from: dataSource1 }) 42 | await shouldFail(oracle.setResult(ORACLE_ID_0, RESULT2, { from: dataSource1 })) 43 | }) 44 | 45 | it('cannot set oracle for the same id twice', async () => { 46 | await oracle.newOracle(ORACLE_ID_0, dataSource1) 47 | await shouldFail(oracle.newOracle(ORACLE_ID_0, dataSource2)) 48 | }) 49 | 50 | it('can set result only by added dataSource', async () => { 51 | await oracle.newOracle(ORACLE_ID_1, dataSource1) 52 | await shouldFail(oracle.setResult(ORACLE_ID_1, RESULT, {from : dataSource2})) 53 | const isResultSet = await oracle.isResultSet(ORACLE_ID_1) 54 | isResultSet.should.equal(false) 55 | 56 | await oracle.setResult(ORACLE_ID_1, RESULT, {from : dataSource1}) 57 | const result = await oracle.resultFor(ORACLE_ID_1) 58 | result.should.equal(RESULT) 59 | 60 | const isResultSetSuccess = await oracle.isResultSet(ORACLE_ID_1) 61 | isResultSetSuccess.should.equal(true) 62 | }) 63 | 64 | it('should emit ResultSet event', async () => { 65 | await oracle.newOracle(ORACLE_ID_1, dataSource2) 66 | const { logs } = await oracle.setResult(ORACLE_ID_1, RESULT, { from: dataSource2 }) 67 | await expectEvent.inLogs( 68 | logs, 69 | 'ResultSet', 70 | { _result: RESULT, _sender: dataSource2 } 71 | ) 72 | }) 73 | 74 | }) 75 | -------------------------------------------------------------------------------- /test/Oracles/OracleBase.test.js: -------------------------------------------------------------------------------- 1 | import { shouldFail } from 'openzeppelin-test-helpers' 2 | 3 | const OracleBase = artifacts.require('OracleBase') 4 | 5 | require('chai').should() 6 | 7 | const ORACLE_ID = '0x0' 8 | 9 | contract('OracleBase', (accounts) => { 10 | 11 | it('is initialized with the correct state', async () => { 12 | const oracle = await OracleBase.new() 13 | await shouldFail(oracle.resultFor(ORACLE_ID)) 14 | const isResultSet = await oracle.isResultSet(ORACLE_ID) 15 | isResultSet.should.equal(false) 16 | }) 17 | 18 | }) 19 | -------------------------------------------------------------------------------- /test/Oracles/PaidMutliOracle.test.js: -------------------------------------------------------------------------------- 1 | import { shouldFail } from 'openzeppelin-test-helpers' 2 | import bnChai from 'bn-chai' 3 | import chai from 'chai' 4 | chai 5 | .use(bnChai(web3.utils.BN)) 6 | .should() 7 | 8 | const { toWei, soliditySha3, BN } = web3.utils 9 | 10 | const Token = artifacts.require('MockToken') 11 | const PaidMultiOracle = artifacts.require('PaidMultiOracle') 12 | 13 | const ORACLE_ID_0 = '0x0' 14 | const ORACLE_ID_1 = '0x1' 15 | const RESULT = soliditySha3('hello oracle') 16 | const RESULT2 = soliditySha3('hello again oracle') 17 | const ZERO_ADDRESS = '0x0000000000000000000000000000000000000000' 18 | 19 | contract('PaidMultiOracle', (accounts) => { 20 | const dataSource1 = accounts[1] 21 | const dataSource2 = accounts[2] 22 | const reward = web3.utils.toWei('10', 'ether') 23 | const contractBalance = toWei('100', 'ether') 24 | const contractBalance2 = toWei('5', 'ether') 25 | 26 | let multiOracle, token 27 | beforeEach(async ()=> { 28 | multiOracle = await PaidMultiOracle.new() 29 | token = await Token.new() 30 | await multiOracle.initialize(token.address, reward) 31 | await token.mint(multiOracle.address, contractBalance) 32 | }) 33 | 34 | it('getReward should return the reward if the contractBalance is greater than the reward', async () => { 35 | const oracleReward = await multiOracle.getReward() 36 | oracleReward.should.eq.BN(reward) 37 | }) 38 | 39 | it('getReward should return the contractBalance if the contractBalance is less than the reward', async () => { 40 | const multiOracle2 = await PaidMultiOracle.new() 41 | await multiOracle2.initialize(token.address, reward) 42 | await token.mint(multiOracle2.address, contractBalance2) 43 | const contractBalance = await token.balanceOf(multiOracle2.address) 44 | const oracleReward = await multiOracle2.getReward() 45 | oracleReward.should.eq.BN(contractBalance) 46 | }) 47 | 48 | it('requires a non-null dataSource', async () => { 49 | await shouldFail( 50 | multiOracle.newOracle(ORACLE_ID_0, ZERO_ADDRESS) 51 | ) 52 | }) 53 | 54 | it('should pay out reward', async () => { 55 | await multiOracle.newOracle(ORACLE_ID_0, dataSource1) 56 | await multiOracle.setResult(ORACLE_ID_0, RESULT, {from: dataSource1 }) 57 | await multiOracle.newOracle(ORACLE_ID_1, dataSource2) 58 | await multiOracle.setResult(ORACLE_ID_1, RESULT2, {from: dataSource2}) 59 | 60 | const dataSourceBalance1 = new BN(await token.balanceOf(dataSource1)) 61 | const dataSourceBalance2 = new BN(await token.balanceOf(dataSource2)) 62 | 63 | dataSourceBalance1.should.be.eq.BN(reward) 64 | dataSourceBalance2.should.be.eq.BN(reward) 65 | }) 66 | 67 | it('cannot pay out reward when the result was set twice', async () => { 68 | await multiOracle.newOracle(ORACLE_ID_1, dataSource2) 69 | await multiOracle.setResult(ORACLE_ID_1, RESULT, {from: dataSource2 }) 70 | await shouldFail ( 71 | multiOracle.setResult(ORACLE_ID_1, RESULT2, { from: dataSource2 }) 72 | ) 73 | }) 74 | 75 | it('isResultSet bool should be flipped after result was set', async () => { 76 | await multiOracle.newOracle(ORACLE_ID_1, dataSource1) 77 | let isResultSet = await multiOracle.isResultSet(ORACLE_ID_1) 78 | isResultSet.should.equal(false) 79 | 80 | await multiOracle.setResult(ORACLE_ID_1, RESULT, {from: dataSource1 }) 81 | isResultSet = await multiOracle.isResultSet(ORACLE_ID_1) 82 | isResultSet.should.equal(true) 83 | }) 84 | }) 85 | -------------------------------------------------------------------------------- /test/Oracles/PaidOracle.test.js: -------------------------------------------------------------------------------- 1 | import { shouldFail } from 'openzeppelin-test-helpers' 2 | import { encodeCall } from 'zos-lib' 3 | import bnChai from 'bn-chai' 4 | import chai from 'chai' 5 | chai 6 | .use(bnChai(web3.utils.BN)) 7 | .should() 8 | 9 | const { BN, soliditySha3 } = web3.utils 10 | 11 | const Token = artifacts.require('MockToken') 12 | const PaidOracle = artifacts.require('PaidOracle') 13 | 14 | const RESULT = soliditySha3('hello oracle') 15 | 16 | contract('PaidOracle', (accounts) => { 17 | const dataSource = accounts[2] 18 | const ZERO_ADDRESS = '0x0000000000000000000000000000000000000000' 19 | const reward = web3.utils.toWei('10', 'ether') 20 | const contractBalance = web3.utils.toWei('100', 'ether') 21 | 22 | let oracle, initializeData, token 23 | beforeEach(async ()=> { 24 | oracle = await PaidOracle.new() 25 | token = await Token.new() 26 | 27 | initializeData = encodeCall( 28 | "initialize", 29 | ['address', 'address', 'uint256'], 30 | [token.address, dataSource, reward] 31 | ) 32 | await web3.eth.sendTransaction({data: initializeData, to: oracle.address, from: accounts[0], gasLimit: 500000}) 33 | await token.mint(oracle.address, contractBalance) 34 | }) 35 | 36 | it('requires a non-null dataSource', async () => { 37 | const paidOracle = await PaidOracle.new() 38 | initializeData = encodeCall( 39 | "initialize", 40 | ['address', 'address', 'uint256'], 41 | [token.address, ZERO_ADDRESS, reward] 42 | ) 43 | await shouldFail( 44 | paidOracle.sendTransaction({data: initializeData}) 45 | ) 46 | }) 47 | 48 | it('reward should be the contract balance if its less than the reward, otherwise return reward itself.', async () => { 49 | const contractBalance = new BN(await token.balanceOf(oracle.address)) 50 | const oracleReward = await oracle.getReward() 51 | let amount = contractBalance.lt(reward) ? contractBalance : reward 52 | oracleReward.should.eq.BN(amount) 53 | }) 54 | 55 | it('should pay out reward', async () => { 56 | await oracle.setResult(RESULT, {from: dataSource }) 57 | const dataSourceBalance = new BN(await token.balanceOf(dataSource)) 58 | dataSourceBalance.should.be.eq.BN(reward) 59 | }) 60 | 61 | it('cannot pay out reward when the result was set twice', async () => { 62 | await oracle.setResult(RESULT, {from: dataSource }) 63 | await shouldFail ( 64 | oracle.setResult(RESULT, { from: dataSource }) 65 | ) 66 | }) 67 | 68 | it('isResultSet should be flipped after result was set', async () => { 69 | let isResultSet = await oracle.isResultSet('0x0') 70 | isResultSet.should.equal(false) 71 | 72 | await oracle.setResult(RESULT, {from: dataSource }) 73 | isResultSet = await oracle.isResultSet('0x0') 74 | isResultSet.should.equal(true) 75 | }) 76 | }) 77 | -------------------------------------------------------------------------------- /test/Oracles/SignedMultiOracle.test.js: -------------------------------------------------------------------------------- 1 | import { expectEvent, shouldFail } from 'openzeppelin-test-helpers' 2 | const { soliditySha3 } = web3.utils 3 | const { sign } = web3.eth 4 | 5 | const SignedMultiOracle = artifacts.require('SignedMultiOracle') 6 | 7 | require('chai').should() 8 | 9 | const RESULT1 = soliditySha3('hello SignedMultiOracle1') 10 | 11 | const RESULT2 = soliditySha3('hello SignedMultiOracle2') 12 | 13 | contract('SignedMultiOracle', (accounts) => { 14 | const signer0 = accounts[0] 15 | const signer1 = accounts[1] 16 | const signer2 = accounts[2] 17 | const thirdParty = accounts[2] 18 | const id1 = soliditySha3(1) 19 | const id2 = soliditySha3(2) 20 | 21 | let oracle, messageHash1, messageHash2 22 | beforeEach(async () => { 23 | oracle = await SignedMultiOracle.new() 24 | await oracle.newOracle(id1, signer1) 25 | await oracle.newOracle(id2, signer2) 26 | 27 | messageHash1 = soliditySha3(id1, RESULT1, oracle.address) 28 | messageHash2 = soliditySha3(id2, RESULT2, oracle.address) 29 | }) 30 | 31 | it('can set result with signature by data source', async () => { 32 | const signature1 = await sign(messageHash1, signer1) 33 | await oracle.setResultWithSignature(id1, RESULT1, signature1, { from: thirdParty }) 34 | const result1 = await oracle.resultFor(id1) 35 | result1.should.equal(RESULT1) 36 | const isResultSet = await oracle.isResultSet(id1) 37 | isResultSet.should.equal(true) 38 | 39 | const signature2 = await web3.eth.sign(messageHash2, signer2) 40 | await oracle.setResultWithSignature(id2, RESULT2, signature2, { from: thirdParty }) 41 | const result2 = await oracle.resultFor(id2) 42 | result2.should.equal(RESULT2) 43 | const isResultSet2 = await oracle.isResultSet(id2) 44 | isResultSet2.should.equal(true) 45 | }) 46 | 47 | it('cannot be set by a different signer', async () => { 48 | const signature = await web3.eth.sign(RESULT1, signer0) 49 | await shouldFail(oracle.setResultWithSignature(id1, RESULT1, signature, { from: thirdParty})) 50 | }) 51 | 52 | it('cannot be set with the same id twice', async () => { 53 | let signature = await web3.eth.sign(messageHash1, signer1) 54 | await oracle.setResultWithSignature(id1, RESULT1, signature, { from: signer1 }) 55 | await shouldFail(oracle.setResultWithSignature(id1, RESULT1, signature, { from: thirdParty })) 56 | 57 | const secondHash = web3.utils.soliditySha3('another result'); 58 | signature = await web3.eth.sign(secondHash, signer1) 59 | await shouldFail(oracle.setResultWithSignature(id1, RESULT1, signature, { from: thirdParty })) 60 | }) 61 | 62 | it('should emit ResultSet event', async () => { 63 | // hard-coded here since web3.utils.fromAscii won't return 32 bytes hex string 64 | const signature = await web3.eth.sign(messageHash1, signer1) 65 | const { logs } = await oracle.setResultWithSignature(id1, RESULT1, signature, { from: thirdParty }) 66 | await expectEvent.inLogs( 67 | logs, 68 | 'ResultSet', 69 | {_id: id1, _result: RESULT1, _sender: thirdParty } 70 | ) 71 | }) 72 | }) 73 | -------------------------------------------------------------------------------- /test/Oracles/SignedOracle.test.js: -------------------------------------------------------------------------------- 1 | import { expectEvent, shouldFail } from 'openzeppelin-test-helpers' 2 | const { soliditySha3 } = web3.utils 3 | const { sign } = web3.eth 4 | 5 | const SignedOracle = artifacts.require('SignedOracle') 6 | 7 | require('chai').should() 8 | 9 | const RESULT = soliditySha3('hello oracle') 10 | 11 | contract('SignedOracle', (accounts) => { 12 | const dataSource = accounts[1] 13 | 14 | let oracle, messageHash 15 | beforeEach(async ()=> { 16 | oracle = await SignedOracle.new() 17 | await oracle.initialize(dataSource) 18 | messageHash = soliditySha3(RESULT, oracle.address); 19 | }) 20 | 21 | it('can set result by data source', async () => { 22 | const signature = await web3.eth.sign(messageHash, dataSource) 23 | 24 | await oracle.setResult(RESULT, signature) 25 | const result = await oracle.resultFor('0x0') 26 | result.should.equal(RESULT) 27 | }) 28 | 29 | it('cannot be set by a different data source', async () => { 30 | const signature = await web3.eth.sign(messageHash, accounts[0]) 31 | await shouldFail(oracle.setResult(RESULT, signature)) 32 | }) 33 | 34 | it('cannot be set twice', async () => { 35 | let signature = await web3.eth.sign(messageHash, dataSource) 36 | await oracle.setResult(RESULT, signature) 37 | 38 | const secondHash = web3.utils.soliditySha3('another result'); 39 | signature = await web3.eth.sign(secondHash, dataSource) 40 | await shouldFail(oracle.setResult(RESULT, signature)) 41 | }) 42 | 43 | it('should emit ResultSet event', async () => { 44 | const signature = await web3.eth.sign(messageHash, dataSource) 45 | const { logs } = await oracle.setResult(RESULT, signature) 46 | await expectEvent.inLogs( 47 | logs, 48 | 'ResultSet', 49 | { _result: RESULT, _sender: accounts[0] } 50 | ) 51 | }) 52 | }) 53 | -------------------------------------------------------------------------------- /test/PushOracles/BasicPushOracle.test.js: -------------------------------------------------------------------------------- 1 | import { toAscii } from 'web3-utils' 2 | import { encodeCall } from 'zos-lib' 3 | const { soliditySha3 } = web3.utils 4 | 5 | const BasicPushOracle = artifacts.require('BasicPushOracle') 6 | const OracleConsumerMock = artifacts.require('OracleConsumerMock') 7 | 8 | require('chai').should() 9 | 10 | const RESULT = soliditySha3('hello oracle') 11 | 12 | contract('BasicPushOracle', (accounts) => { 13 | const dataSource = accounts[1] 14 | 15 | it('calls receiveResult() on OracleConsumer', async () => { 16 | const oracleConsumer = await OracleConsumerMock.new() 17 | const oracle = await BasicPushOracle.new() 18 | const initializeData = encodeCall("initialize", ['address', 'address'], [dataSource, oracleConsumer.address]) 19 | // https://forum.zeppelin.solutions/t/revert-when-calling-initialize-manually/314 20 | // await oracle.sendTransaction({data: initializeData, from: accounts[0]}) 21 | await web3.eth.sendTransaction({data: initializeData, to: oracle.address, from: accounts[0], gasLimit: 500000}) 22 | const source = await oracle.dataSource() 23 | await oracle.setResult(RESULT, { from: dataSource }) 24 | const result = await oracleConsumer.result() 25 | result.should.equal(RESULT) 26 | }) 27 | }) 28 | -------------------------------------------------------------------------------- /test/PushOracles/PushOracleBase.test.js: -------------------------------------------------------------------------------- 1 | const PushOracleBase = artifacts.require('PushOracleBase') 2 | 3 | require('chai').should() 4 | 5 | contract('PushOracleBase', (accounts) => { 6 | const consumer = accounts[2] 7 | 8 | it('is initialized with the correct state', async () => { 9 | const oracle = await PushOracleBase.new() 10 | await oracle.initialize(consumer, 0) 11 | const consumerOnChain = await oracle.consumer() 12 | consumer.should.equal(consumerOnChain) 13 | }) 14 | 15 | }) 16 | -------------------------------------------------------------------------------- /test/PushOracles/SignedPushOracle.test.js: -------------------------------------------------------------------------------- 1 | import { encodeCall } from 'zos-lib' 2 | const { soliditySha3 } = web3.utils 3 | const { sign } = web3.eth 4 | 5 | const SignedPushOracle = artifacts.require('SignedPushOracle') 6 | const OracleConsumerMock = artifacts.require('OracleConsumerMock') 7 | 8 | require('chai').should() 9 | 10 | const RESULT = soliditySha3('hello oracle') 11 | 12 | contract('SignedPushOracle', (accounts) => { 13 | const signer = accounts[1] 14 | 15 | it('calls receiveResult() on OracleConsumer', async () => { 16 | // Deploy contracts 17 | const oracleConsumer = await OracleConsumerMock.new() 18 | const oracle = await SignedPushOracle.new() 19 | const initializeData = encodeCall("initialize", ['address', 'address'], [signer, oracleConsumer.address]) 20 | // https://forum.zeppelin.solutions/t/revert-when-calling-initialize-manually/314 21 | // await oracle.sendTransaction({data: initializeData, from: accounts[0]}) 22 | await web3.eth.sendTransaction({data: initializeData, to: oracle.address, from: accounts[0], gasLimit: 500000}) 23 | 24 | // Sign and set result hash 25 | const messageHash = soliditySha3(RESULT, oracle.address) 26 | let signature = await sign(messageHash, signer) 27 | await oracle.setResult(RESULT, signature) 28 | 29 | // Get result from oracle consumer 30 | const result = await oracleConsumer.result() 31 | result.should.equal(RESULT) 32 | }) 33 | }) 34 | -------------------------------------------------------------------------------- /truffle.js: -------------------------------------------------------------------------------- 1 | require('babel-register') 2 | require('babel-polyfill') 3 | 4 | module.exports = { 5 | networks: { 6 | development: { 7 | host: 'localhost', 8 | port: 8546, 9 | network_id: '*', // Match any network id 10 | gas: 6000000 11 | }, 12 | coverage: { 13 | host: 'localhost', 14 | network_id: '*', 15 | port: 8546, 16 | gas: 0xfffffffffff, 17 | gasPrice: 0x01 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /zos.json: -------------------------------------------------------------------------------- 1 | { 2 | "zosversion": "2", 3 | "name": "tidbit", 4 | "version": "0.1.0", 5 | "contracts": {} 6 | } --------------------------------------------------------------------------------