├── .env_example ├── .eslintrc ├── .github └── workflows │ ├── nodejs.yml │ └── npm-publish.yml ├── .gitignore ├── .solhint.json ├── .solhintignore ├── .travis.yml ├── LICENSE ├── README.md ├── contracts ├── WitnetFeeds.sol ├── WitnetOracle.sol ├── WitnetPriceFeeds.sol ├── WitnetRandomness.sol ├── WitnetRequest.sol ├── WitnetRequestBytecodes.sol ├── WitnetRequestFactory.sol ├── WitnetRequestTemplate.sol ├── apps │ ├── UsingWitnet.sol │ ├── UsingWitnetRandomness.sol │ ├── UsingWitnetRequest.sol │ ├── UsingWitnetRequestTemplate.sol │ ├── WitnetConsumer.sol │ ├── WitnetPriceSolverBase.sol │ ├── WitnetRandomnessRequestConsumer.sol │ ├── WitnetRandomnessV2.sol │ ├── WitnetRequestConsumer.sol │ └── WitnetRequestTemplateConsumer.sol ├── core │ ├── WitnetDeployer.sol │ ├── WitnetProxy.sol │ ├── WitnetUpgradableBase.sol │ ├── customs │ │ ├── WitnetDeployerCfxCore.sol │ │ ├── WitnetDeployerMeter.sol │ │ ├── WitnetOracleTrustableObscuro.sol │ │ ├── WitnetOracleTrustableOvm2.sol │ │ ├── WitnetOracleTrustableReef.sol │ │ ├── WitnetRequestBytecodesNoSha256.sol │ │ └── WitnetRequestFactoryCfxCore.sol │ └── defaults │ │ ├── WitnetOracleTrustableBase.sol │ │ ├── WitnetOracleTrustableDefault.sol │ │ ├── WitnetPriceFeedsDefault.sol │ │ ├── WitnetRequestBytecodesDefault.sol │ │ └── WitnetRequestFactoryDefault.sol ├── data │ ├── WitnetOracleDataLib.sol │ ├── WitnetPriceFeedsData.sol │ ├── WitnetRequestBytecodesData.sol │ └── WitnetRequestFactoryData.sol ├── interfaces │ ├── IFeeds.sol │ ├── IWitnetConsumer.sol │ ├── IWitnetFeeds.sol │ ├── IWitnetFeedsAdmin.sol │ ├── IWitnetOracle.sol │ ├── IWitnetOracleEvents.sol │ ├── IWitnetOracleReporter.sol │ ├── IWitnetPriceFeeds.sol │ ├── IWitnetPriceSolver.sol │ ├── IWitnetPriceSolverDeployer.sol │ ├── IWitnetRandomness.sol │ ├── IWitnetRandomnessAdmin.sol │ ├── IWitnetRandomnessEvents.sol │ ├── IWitnetRequestBoardAdmin.sol │ ├── IWitnetRequestBoardAdminACLs.sol │ ├── IWitnetRequestBytecodes.sol │ └── IWitnetRequestFactory.sol ├── libs │ ├── Create3.sol │ ├── Slices.sol │ ├── Witnet.sol │ ├── WitnetBuffer.sol │ ├── WitnetCBOR.sol │ ├── WitnetEncodingLib.sol │ ├── WitnetErrorsLib.sol │ ├── WitnetPriceFeedsLib.sol │ └── WitnetV2.sol ├── mocks │ ├── WitnetMockedOracle.sol │ ├── WitnetMockedPriceFeeds.sol │ ├── WitnetMockedRandomness.sol │ ├── WitnetMockedRequestBytecodes.sol │ └── WitnetMockedRequestFactory.sol └── patterns │ ├── Clonable.sol │ ├── Initializable.sol │ ├── Ownable.sol │ ├── Ownable2Step.sol │ ├── Payable.sol │ ├── Proxiable.sol │ ├── ReentrancyGuard.sol │ └── Upgradeable.sol ├── hardhat.config.js ├── migrations ├── addresses.json ├── constructorArgs.json ├── salts │ └── .gitkeep └── scripts │ ├── 1_deployer.js │ ├── 2_libs.js │ ├── 3_core.js │ ├── 4_proxies.js │ └── 5_apps.js ├── package.json ├── pnpm-lock.yaml ├── scripts ├── addresses.js ├── clean.js ├── eth-create2.js ├── eth-create3.js ├── flatten.js ├── migrate.js ├── networks.js ├── prepare.js ├── vanity2gen.js ├── vanity3gen.js ├── verify-apps.js ├── verify-core.js ├── verify-impls.js └── verify-libs.js ├── settings ├── artifacts.js ├── index.js ├── networks.js ├── solidity.js └── specs.js ├── src ├── index.js └── utils.js ├── test ├── TestWitnetBuffer.sol ├── TestWitnetCBOR.sol ├── TestWitnetEncodingLib.sol ├── TestWitnetV2.sol └── witnet_bytecodes.test.js └── truffle-config.js /.env_example: -------------------------------------------------------------------------------- 1 | WITNET_EVM_REALM=default 2 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["standard"], 3 | "env": { 4 | "browser": false, 5 | "node": true, 6 | "jest": true, 7 | "mocha": true, 8 | "es2021": true 9 | }, 10 | "globals" : { 11 | "artifacts": false, 12 | "contract": false, 13 | "assert": false, 14 | "web3": false 15 | }, 16 | "parserOptions": { 17 | "ecmaVersion": 12, 18 | "sourceType": "module" 19 | }, 20 | "rules": { 21 | "comma-dangle": ["error", { 22 | "arrays": "always-multiline", 23 | "objects": "always-multiline", 24 | "imports": "always-multiline", 25 | "exports": "always-multiline", 26 | "functions": "ignore" 27 | }], 28 | "indent": ["error", 2, {"SwitchCase": 1 }], 29 | "jsx-quotes": [2, "prefer-double"], 30 | "prefer-promise-reject-errors": 0, 31 | "quotes": [2, "double"], 32 | "import/export": 0, 33 | "no-useless-constructor": 1, 34 | "handle-callback-err": 1, 35 | "max-len" : ["error", { "code": 130 }] 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /.github/workflows/nodejs.yml: -------------------------------------------------------------------------------- 1 | name: On push 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | build: 7 | 8 | runs-on: ubuntu-latest 9 | 10 | env: 11 | CI: true 12 | 13 | strategy: 14 | matrix: 15 | node-version: [20.x] 16 | 17 | steps: 18 | - uses: actions/checkout@v1 19 | - name: Use Node.js ${{ matrix.node-version }} 20 | uses: actions/setup-node@v1 21 | with: 22 | node-version: ${{ matrix.node-version }} 23 | - uses: pnpm/action-setup@v3 24 | with: 25 | version: 8 26 | - name: Install dependencies 27 | run: | 28 | pnpm install 29 | - name: Check format 30 | run: | 31 | pnpm run fmt 32 | - name: Compile solidity contracts 33 | run: | 34 | pnpm run compile 35 | - name: Run tests 36 | run: | 37 | pnpm run test 38 | -------------------------------------------------------------------------------- /.github/workflows/npm-publish.yml: -------------------------------------------------------------------------------- 1 | # This workflow will run tests using node and then publish a package to GitHub Packages when a release is created 2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/publishing-nodejs-packages 3 | 4 | name: Publish NPM package 5 | 6 | on: 7 | push: 8 | tags: 9 | - '*' 10 | 11 | jobs: 12 | Publish: 13 | environment: Release 14 | runs-on: ubuntu-latest 15 | steps: 16 | - name: Checkout 17 | uses: actions/checkout@v4 18 | 19 | - name: Setup Node.js 20 | uses: actions/setup-node@v4 21 | with: 22 | node-version: 20 23 | registry-url: https://registry.npmjs.org/ 24 | 25 | - name: Setup pnpm 26 | uses: pnpm/action-setup@v3 27 | with: 28 | version: 8 29 | 30 | - name: Install Dependencies 31 | run: pnpm install 32 | 33 | - name: Publish 34 | run: pnpm publish 35 | env: 36 | NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | artifacts/ 3 | build/ 4 | cache/ 5 | flattened/ 6 | node_modules/ 7 | 8 | .env 9 | .idea 10 | .vscode 11 | 12 | *.tmp 13 | .old 14 | -------------------------------------------------------------------------------- /.solhint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "solhint:recommended", 3 | "rules": { 4 | "compiler-version": [ 5 | "error", 6 | ">=0.6.0 <0.9.0" 7 | ], 8 | "constructor-syntax": "warn", 9 | "custom-errors": "off", 10 | "explicit-types": "off", 11 | "func-param-name-mixedcase": "warn", 12 | "func-visibility": [ 13 | "warn", 14 | { 15 | "ignoreConstructors": true 16 | } 17 | ], 18 | "immutable-vars-naming": "off", 19 | "modifier-name-mixedcase": "warn", 20 | "no-empty-blocks": "off", 21 | "no-inline-assembly": "off", 22 | "no-global-import": "off", 23 | "var-name-mixedcase": "off", 24 | "reason-string": [ 25 | "warn", 26 | { 27 | "maxLength": 128 28 | } 29 | ] 30 | } 31 | } -------------------------------------------------------------------------------- /.solhintignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | dist: trusty 2 | sudo: false 3 | group: beta 4 | language: node_js 5 | node_js: 6 | - "14" 7 | - "16" 8 | 9 | cache: 10 | directories: 11 | - node_modules 12 | 13 | jobs: 14 | fast_finish: true 15 | include: 16 | - stage: tests 17 | name: "Linter" 18 | script: npm run fmt 19 | 20 | - stage: tests 21 | name: "Unit tests" 22 | script: npm run test 23 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Witnet Project 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # witnet-solidity-bridge 2 | 3 | Solidity source code of the smart contracts composing the **Witnet EVM Bridge** framework. This framework enables smart contracts operating in a long range of EVM-compatible chains to interact with the [Witnet Oracle Blockchain](https://witnet.io) for retrieving and aggregating offchain public data, or as an entropy source for randomness generation. 4 | 5 | ## Install the package 6 | 7 | `$ pnpm install` 8 | 9 | ## Deploying the Witnet EVM Bridge on a new chain 10 | 11 | ### Pre-assessment 12 | 13 | Should any artifact require customized contract implementations: 14 | 15 | - Please add source files accordingly to `contracts/core/customs`. 16 | 17 | - Set up new artifact names, and eventual new construction parameters, if required, to `settings/artifacts` and `settings/specs`, respectively. 18 | 19 | - Run regression tests: `$ pnpm run test` 20 | 21 | 22 | ### Prepare the environment 23 | 24 | - Add a new network configuration to `settings/networks`. The network name should follow the pattern `:`. 25 | 26 | - Make sure you run an ETH/RPC provider for the specified `host` and `port`, capable of intercepting `eth_sendTransaction` calls (e.g. [web3-ethrpc-gateway](https://github.io/witnet/web3-jsonrpc-gateway)). 27 | 28 | ### Run the script 29 | 30 | `$ pnpm run migrate :` 31 | 32 | ## Upgrding the Witnet EVM Bridge on an existing chain 33 | 34 | When modifying the existing source code, or the contents of `settings/artifacts` or `settings/specs`, you may need to upgrade some of the artifacts on certain networks. Just add the `--artifacts` parameter and a comma-separated list of the artifacts you need to upgrade. For instance: 35 | 36 | `$ pnpm run migrate : WitnetErrorsLib,WitnetPriceFeeds` 37 | 38 | When specifying deployable library artifacts, the depending contracts will be attempted to be upgraded as well. 39 | 40 | With respect to deployable contracts, you shall be asked to confirm manually before actually performing a contract upgrade. You can automate all potentially involved upgrades by adding the parameter `--upgrade-all`. 41 | 42 | Reasons for an upgrade to fail: 43 | - You have no credentials. 44 | - You're attempting to upgrade a contract with the same implementation logic as it currently has. 45 | - The parameters passed to the upgrade call, as specified in `settings/specs` are not accepted for some reason (see actual revert message for further info). 46 | 47 | ## Package exported modules 48 | 49 | ### `require("witnet-solidity-bridge")` 50 | Javacript methods and resources: 51 | 52 | - List of supported EVM ecosystems: 53 | - `supportedEcosystems()` 54 | - List of supported EVM chains: 55 | - `supportedNetworks()` 56 | - WEB addresses at a given chain: 57 | - `getAddresses(network)` 58 | - WEB artifacts: 59 | - `assets.WitnetOracle` 60 | - `assets.WitnetPriceFeeds` 61 | - `assets.WitnetPriceRouteSolver` 62 | - `assets.WitnetRequest` 63 | - `assets.WitnetRequestBytecodes` 64 | - `assets.WitnetRequestFactory` 65 | - `assets.WitnetRequestTemplate` 66 | - `assets.WitnetUpgrableBase` 67 | 68 | ### `require("witnet-solidity-bridge/utils")` 69 | 70 | Javascript utils methods: 71 | 72 | - `fromAscii(str)` 73 | - `getRealmNetworkFromArgs()` 74 | - `getRealmNetworkFromString()` 75 | - `getWitnetArtifactsFromArgs()` 76 | - `getWitnetRequestMethodString(method)` 77 | - `isDryRun(network)` 78 | - `isNullAddress(addr)` 79 | - `padLeft(str, char, size)` 80 | - `prompt(text)` 81 | - `readJsonFromFile(filename)` 82 | - `overwriteJsonFile(filname, extra)` 83 | - `traceHeader(header)` 84 | - `traceTx(tx)` 85 | - `traceVerify(network, verifyArgs)` 86 | 87 | -------------------------------------------------------------------------------- /contracts/WitnetFeeds.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity >=0.7.0 <0.9.0; 4 | 5 | import "./interfaces/IFeeds.sol"; 6 | import "./interfaces/IWitnetFeeds.sol"; 7 | import "./interfaces/IWitnetFeedsAdmin.sol"; 8 | 9 | import "ado-contracts/contracts/interfaces/IERC2362.sol"; 10 | 11 | abstract contract WitnetFeeds 12 | is 13 | IERC2362, 14 | IFeeds, 15 | IWitnetFeeds, 16 | IWitnetFeedsAdmin, 17 | IWitnetOracleEvents 18 | { 19 | Witnet.RadonDataTypes immutable public override dataType; 20 | 21 | function class() virtual external view returns (string memory); 22 | function specs() virtual external view returns (bytes4); 23 | function witnet() virtual external view returns (WitnetOracle); 24 | 25 | constructor( 26 | Witnet.RadonDataTypes _dataType, 27 | string memory _prefix 28 | ) 29 | { 30 | dataType = _dataType; 31 | __prefix = Witnet.toBytes32(bytes(_prefix)); 32 | } 33 | 34 | bytes32 immutable internal __prefix; 35 | 36 | function prefix() override public view returns (string memory) { 37 | return Witnet.toString(__prefix); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /contracts/WitnetOracle.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity >=0.7.0 <0.9.0; 4 | 5 | import "./WitnetRequestBytecodes.sol"; 6 | import "./WitnetRequestFactory.sol"; 7 | import "./interfaces/IWitnetOracle.sol"; 8 | import "./interfaces/IWitnetOracleEvents.sol"; 9 | 10 | /// @title Witnet Request Board functionality base contract. 11 | /// @author The Witnet Foundation. 12 | abstract contract WitnetOracle 13 | is 14 | IWitnetOracle, 15 | IWitnetOracleEvents 16 | { 17 | function class() virtual external view returns (string memory) { 18 | return type(WitnetOracle).name; 19 | } 20 | function channel() virtual external view returns (bytes4); 21 | function factory() virtual external view returns (WitnetRequestFactory); 22 | function registry() virtual external view returns (WitnetRequestBytecodes); 23 | function specs() virtual external view returns (bytes4); 24 | } -------------------------------------------------------------------------------- /contracts/WitnetPriceFeeds.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity >=0.7.0 <0.9.0; 4 | 5 | import "./WitnetFeeds.sol"; 6 | 7 | import "./interfaces/IWitnetPriceFeeds.sol"; 8 | import "./interfaces/IWitnetPriceSolverDeployer.sol"; 9 | 10 | /// @title WitnetPriceFeeds: Price Feeds live repository reliant on the Witnet Oracle blockchain. 11 | /// @author The Witnet Foundation. 12 | abstract contract WitnetPriceFeeds 13 | is 14 | WitnetFeeds, 15 | IWitnetPriceFeeds, 16 | IWitnetPriceSolverDeployer 17 | { 18 | constructor() 19 | WitnetFeeds(Witnet.RadonDataTypes.Integer, "Price-") 20 | {} 21 | 22 | } 23 | -------------------------------------------------------------------------------- /contracts/WitnetRandomness.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity >=0.8.0 <0.9.0; 4 | 5 | import "./interfaces/IWitnetOracleEvents.sol"; 6 | import "./interfaces/IWitnetRandomness.sol"; 7 | import "./interfaces/IWitnetRandomnessEvents.sol"; 8 | 9 | abstract contract WitnetRandomness 10 | is 11 | IWitnetOracleEvents, 12 | IWitnetRandomness, 13 | IWitnetRandomnessEvents 14 | { 15 | function class() virtual external view returns (string memory); 16 | function specs() virtual external view returns (bytes4); 17 | } 18 | -------------------------------------------------------------------------------- /contracts/WitnetRequest.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity >=0.7.0 <0.9.0; 4 | pragma experimental ABIEncoderV2; 5 | 6 | import "./WitnetRequestTemplate.sol"; 7 | 8 | abstract contract WitnetRequest 9 | is 10 | WitnetRequestTemplate 11 | { 12 | /// introspection methods 13 | function template() virtual external view returns (WitnetRequestTemplate); 14 | 15 | /// request-exclusive fields 16 | function args() virtual external view returns (string[][] memory); 17 | function bytecode() virtual external view returns (bytes memory); 18 | function radHash() virtual external view returns (bytes32); 19 | } -------------------------------------------------------------------------------- /contracts/WitnetRequestBytecodes.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity >=0.7.0 <0.9.0; 4 | pragma experimental ABIEncoderV2; 5 | 6 | import "./interfaces/IWitnetRequestBytecodes.sol"; 7 | 8 | abstract contract WitnetRequestBytecodes 9 | is 10 | IWitnetRequestBytecodes 11 | { 12 | function class() virtual external view returns (string memory) { 13 | return type(WitnetRequestBytecodes).name; 14 | } 15 | function specs() virtual external view returns (bytes4); 16 | } -------------------------------------------------------------------------------- /contracts/WitnetRequestFactory.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity >=0.7.0 <0.9.0; 4 | pragma experimental ABIEncoderV2; 5 | 6 | import "./WitnetRequestBytecodes.sol"; 7 | import "./WitnetOracle.sol"; 8 | import "./interfaces/IWitnetRequestFactory.sol"; 9 | 10 | abstract contract WitnetRequestFactory 11 | is 12 | IWitnetRequestFactory 13 | { 14 | function class() virtual external view returns (string memory); 15 | function registry() virtual external view returns (WitnetRequestBytecodes); 16 | function specs() virtual external view returns (bytes4); 17 | function witnet() virtual external view returns (WitnetOracle); 18 | } -------------------------------------------------------------------------------- /contracts/WitnetRequestTemplate.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity >=0.7.0 <0.9.0; 4 | pragma experimental ABIEncoderV2; 5 | 6 | import "./WitnetRequestBytecodes.sol"; 7 | import "./WitnetOracle.sol"; 8 | import "./WitnetRequestFactory.sol"; 9 | 10 | abstract contract WitnetRequestTemplate 11 | { 12 | event WitnetRequestBuilt(address indexed request, bytes32 indexed radHash, string[][] args); 13 | 14 | function class() virtual external view returns (string memory); 15 | function factory() virtual external view returns (WitnetRequestFactory); 16 | function registry() virtual external view returns (WitnetRequestBytecodes); 17 | function specs() virtual external view returns (bytes4); 18 | function version() virtual external view returns (string memory); 19 | function witnet() virtual external view returns (WitnetOracle); 20 | 21 | function aggregator() virtual external view returns (bytes32); 22 | function parameterized() virtual external view returns (bool); 23 | function resultDataMaxSize() virtual external view returns (uint16); 24 | function resultDataType() virtual external view returns (Witnet.RadonDataTypes); 25 | function retrievals() virtual external view returns (bytes32[] memory); 26 | function tally() virtual external view returns (bytes32); 27 | 28 | function getRadonAggregator() virtual external view returns (Witnet.RadonReducer memory); 29 | function getRadonRetrievalByIndex(uint256) virtual external view returns (Witnet.RadonRetrieval memory); 30 | function getRadonRetrievalsCount() virtual external view returns (uint256); 31 | function getRadonTally() virtual external view returns (Witnet.RadonReducer memory); 32 | 33 | function buildRequest(string[][] calldata args) virtual external returns (address); 34 | function verifyRadonRequest(string[][] calldata args) virtual external returns (bytes32); 35 | } -------------------------------------------------------------------------------- /contracts/apps/UsingWitnet.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity >=0.7.0 <0.9.0; 4 | pragma experimental ABIEncoderV2; 5 | 6 | import "../WitnetOracle.sol"; 7 | 8 | /// @title The UsingWitnet contract 9 | /// @dev Witnet-aware contracts can inherit from this contract in order to interact with Witnet. 10 | /// @author The Witnet Foundation. 11 | abstract contract UsingWitnet 12 | is 13 | IWitnetOracleEvents 14 | { 15 | /// @dev Immutable reference to the Witnet Request Board contract. 16 | WitnetOracle internal immutable __witnet; 17 | 18 | /// @dev Default Security-Level Agreement parameters to be fulfilled by the Witnet blockchain 19 | /// @dev when solving a data request. 20 | WitnetV2.RadonSLA internal __witnetDefaultSLA; 21 | 22 | /// @dev Percentage over base fee to pay on every data request, 23 | /// @dev as to deal with volatility of evmGasPrice and evmWitPrice during the live time of 24 | /// @dev a data request (since being posted until a result gets reported back), at both the EVM and 25 | /// @dev the Witnet blockchain levels, respectivelly. 26 | uint16 internal __witnetBaseFeeOverheadPercentage; 27 | 28 | /// @param _wrb Address of the WitnetOracle contract. 29 | constructor(WitnetOracle _wrb) { 30 | require( 31 | _wrb.specs() == type(IWitnetOracle).interfaceId, 32 | "UsingWitnet: uncompliant WitnetOracle" 33 | ); 34 | __witnet = _wrb; 35 | __witnetDefaultSLA = WitnetV2.RadonSLA({ 36 | // Number of nodes in the Witnet blockchain that will take part in solving the data request: 37 | committeeSize: 10, 38 | // Fee in $nanoWIT paid to every node in the Witnet blockchain involved in solving the data request: 39 | witnessingFeeNanoWit: 2 * 10 ** 8 // defaults to 0.2 $WIT 40 | }); 41 | 42 | __witnetBaseFeeOverheadPercentage = 33; // defaults to 33% 43 | } 44 | 45 | /// @dev Provides a convenient way for client contracts extending this to block the execution of the main logic of the 46 | /// @dev contract until a particular request has been successfully solved and reported by Witnet, 47 | /// @dev either with an error or successfully. 48 | modifier witnetQuerySolved(uint256 _witnetQueryId) { 49 | require(_witnetCheckQueryResultAvailability(_witnetQueryId), "UsingWitnet: unsolved query"); 50 | _; 51 | } 52 | 53 | function witnet() virtual public view returns (WitnetOracle) { 54 | return __witnet; 55 | } 56 | 57 | /// @notice Check if given query was already reported back from the Witnet oracle. 58 | /// @param _id The unique identifier of a previously posted data request. 59 | function _witnetCheckQueryResultAvailability(uint256 _id) 60 | internal view 61 | returns (bool) 62 | { 63 | return __witnet.getQueryStatus(_id) == WitnetV2.QueryStatus.Reported; 64 | } 65 | 66 | /// @notice Estimate the minimum reward required for posting a data request, using `tx.gasprice` as a reference. 67 | /// @dev Underestimates if the size of returned data is greater than `_resultMaxSize`. 68 | /// @param _resultMaxSize Maximum expected size of returned data (in bytes). 69 | function _witnetEstimateEvmReward(uint16 _resultMaxSize) 70 | virtual internal view 71 | returns (uint256) 72 | { 73 | return ( 74 | (100 + __witnetBaseFeeOverheadPercentage) 75 | * __witnet.estimateBaseFee(tx.gasprice, _resultMaxSize) 76 | ) / 100; 77 | } 78 | 79 | function _witnetCheckQueryResponseStatus(uint256 _witnetQueryId) 80 | internal view 81 | returns (WitnetV2.ResponseStatus) 82 | { 83 | return __witnet.getQueryResponseStatus(_witnetQueryId); 84 | } 85 | 86 | function _witnetCheckQueryResultError(uint256 _witnetQueryId) 87 | internal view 88 | returns (Witnet.ResultError memory) 89 | { 90 | return __witnet.getQueryResultError(_witnetQueryId); 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /contracts/apps/UsingWitnetRandomness.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity >=0.7.0 <0.9.0; 4 | pragma experimental ABIEncoderV2; 5 | 6 | import "../WitnetRandomness.sol"; 7 | 8 | /// @title The UsingWitnetRandomness contract 9 | /// @dev Contracts willing to interact with WitnetRandomness appliance should just inherit from this contract. 10 | /// @author The Witnet Foundation. 11 | abstract contract UsingWitnetRandomness 12 | is 13 | IWitnetOracleEvents, 14 | IWitnetRandomnessEvents 15 | { 16 | WitnetOracle immutable public witnet; 17 | WitnetRandomness immutable public __RNG; 18 | 19 | constructor(WitnetRandomness _witnetRandomness) { 20 | require( 21 | address(_witnetRandomness).code.length > 0 22 | && _witnetRandomness.specs() == type(WitnetRandomness).interfaceId, 23 | "UsingWitnetRandomness: uncompliant WitnetRandomness appliance" 24 | ); 25 | __RNG = _witnetRandomness; 26 | witnet = __RNG.witnet(); 27 | } 28 | 29 | } 30 | 31 | -------------------------------------------------------------------------------- /contracts/apps/UsingWitnetRequest.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.0; 3 | 4 | import "./UsingWitnet.sol"; 5 | import "../WitnetRequest.sol"; 6 | 7 | abstract contract UsingWitnetRequest 8 | is UsingWitnet 9 | { 10 | WitnetRequest immutable public dataRequest; 11 | 12 | bytes32 immutable internal __witnetRequestRadHash; 13 | uint16 immutable internal __witnetQueryResultMaxSize; 14 | 15 | /// @param _witnetRequest Address of the WitnetRequest contract containing the actual data request. 16 | /// @param _baseFeeOverheadPercentage Percentage over base fee to pay as on every data request. 17 | constructor ( 18 | WitnetRequest _witnetRequest, 19 | uint16 _baseFeeOverheadPercentage 20 | ) 21 | UsingWitnet(_witnetRequest.witnet()) 22 | { 23 | require( 24 | _witnetRequest.specs() == type(WitnetRequest).interfaceId, 25 | "UsingWitnetRequest: uncompliant WitnetRequest" 26 | ); 27 | dataRequest = _witnetRequest; 28 | __witnetQueryResultMaxSize = _witnetRequest.resultDataMaxSize(); 29 | __witnetRequestRadHash = _witnetRequest.radHash(); 30 | __witnetBaseFeeOverheadPercentage = _baseFeeOverheadPercentage; 31 | } 32 | 33 | function _witnetEstimateEvmReward() 34 | virtual internal view 35 | returns (uint256) 36 | { 37 | return _witnetEstimateEvmReward(__witnetQueryResultMaxSize); 38 | } 39 | 40 | function __witnetRequestData(uint256 _witnetEvmReward) 41 | virtual internal returns (uint256) 42 | { 43 | return __witnetRequestData(_witnetEvmReward, __witnetDefaultSLA); 44 | } 45 | 46 | function __witnetRequestData( 47 | uint256 _witnetEvmReward, 48 | WitnetV2.RadonSLA memory _witnetQuerySLA 49 | ) 50 | virtual internal returns (uint256) 51 | { 52 | return __witnet.postRequest{value: _witnetEvmReward}( 53 | __witnetRequestRadHash, 54 | _witnetQuerySLA 55 | ); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /contracts/apps/UsingWitnetRequestTemplate.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.0; 3 | 4 | import "./UsingWitnet.sol"; 5 | import "../WitnetRequest.sol"; 6 | 7 | abstract contract UsingWitnetRequestTemplate 8 | is UsingWitnet 9 | { 10 | WitnetRequestTemplate immutable public dataRequestTemplate; 11 | 12 | uint16 immutable internal __witnetQueryResultMaxSize; 13 | 14 | /// @param _witnetRequestTemplate Address of the WitnetRequestTemplate from which actual data requests will get built. 15 | /// @param _baseFeeOverheadPercentage Percentage over base fee to pay as on every data request. 16 | constructor ( 17 | WitnetRequestTemplate _witnetRequestTemplate, 18 | uint16 _baseFeeOverheadPercentage 19 | ) 20 | UsingWitnet(_witnetRequestTemplate.witnet()) 21 | { 22 | require( 23 | _witnetRequestTemplate.specs() == type(WitnetRequestTemplate).interfaceId, 24 | "UsingWitnetRequestTemplate: uncompliant WitnetRequestTemplate" 25 | ); 26 | dataRequestTemplate = _witnetRequestTemplate; 27 | __witnetQueryResultMaxSize = _witnetRequestTemplate.resultDataMaxSize(); 28 | __witnetBaseFeeOverheadPercentage = _baseFeeOverheadPercentage; 29 | } 30 | 31 | function _witnetBuildRadHash(string[][] memory _witnetRequestArgs) 32 | internal returns (bytes32) 33 | { 34 | return dataRequestTemplate.verifyRadonRequest(_witnetRequestArgs); 35 | } 36 | 37 | function _witnetBuildRequest(string[][] memory _witnetRequestArgs) 38 | internal returns (WitnetRequest) 39 | { 40 | return WitnetRequest(dataRequestTemplate.buildRequest(_witnetRequestArgs)); 41 | } 42 | 43 | function _witnetEstimateEvmReward() 44 | virtual internal view 45 | returns (uint256) 46 | { 47 | return _witnetEstimateEvmReward(__witnetQueryResultMaxSize); 48 | } 49 | 50 | function __witnetRequestData( 51 | uint256 _witnetEvmReward, 52 | string[][] memory _witnetRequestArgs 53 | ) 54 | virtual internal returns (uint256) 55 | { 56 | return __witnetRequestData(_witnetEvmReward, _witnetRequestArgs, __witnetDefaultSLA); 57 | } 58 | 59 | function __witnetRequestData( 60 | uint256 _witnetEvmReward, 61 | string[][] memory _witnetRequestArgs, 62 | WitnetV2.RadonSLA memory _witnetQuerySLA 63 | ) 64 | virtual internal returns (uint256) 65 | { 66 | return __witnet.postRequest{value: _witnetEvmReward}( 67 | _witnetBuildRadHash(_witnetRequestArgs), 68 | _witnetQuerySLA 69 | ); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /contracts/apps/WitnetConsumer.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.0; 3 | 4 | import "./UsingWitnet.sol"; 5 | import "../interfaces/IWitnetConsumer.sol"; 6 | 7 | abstract contract WitnetConsumer 8 | is 9 | IWitnetConsumer, 10 | UsingWitnet 11 | { 12 | /// @dev Maximum gas to be spent by the IWitnetConsumer's callback methods. 13 | uint24 internal immutable __witnetCallbackGasLimit; 14 | 15 | modifier onlyFromWitnet { 16 | require(msg.sender == address(__witnet), "WitnetConsumer: unauthorized"); 17 | _; 18 | } 19 | 20 | /// @param _callbackGasLimit Maximum gas to be spent by the IWitnetConsumer's callback methods. 21 | constructor (uint24 _callbackGasLimit) { 22 | __witnetCallbackGasLimit = _callbackGasLimit; 23 | } 24 | 25 | 26 | /// =============================================================================================================== 27 | /// --- Base implementation of IWitnetConsumer -------------------------------------------------------------------- 28 | 29 | function reportableFrom(address _from) virtual override external view returns (bool) { 30 | return _from == address(__witnet); 31 | } 32 | 33 | 34 | /// =============================================================================================================== 35 | /// --- WitnetConsumer virtual methods ---------------------------------------------------------------------------- 36 | 37 | function _witnetEstimateEvmReward() virtual internal view returns (uint256) { 38 | return ( 39 | (100 + __witnetBaseFeeOverheadPercentage) 40 | * __witnet.estimateBaseFeeWithCallback( 41 | tx.gasprice, 42 | __witnetCallbackGasLimit 43 | ) 44 | ) / 100; 45 | } 46 | 47 | function _witnetEstimateEvmReward(uint16) 48 | virtual override internal view 49 | returns (uint256) 50 | { 51 | return _witnetEstimateEvmReward(); 52 | } 53 | 54 | 55 | /// @notice Estimate the minimum reward required for posting a data request, using `tx.gasprice` as a reference. 56 | /// @dev Underestimates if the size of returned data is greater than `_resultMaxSize`. 57 | /// @param _callbackGasLimit Maximum gas to be spent when reporting the data request result. 58 | function _witnetEstimateEvmRewardWithCallback(uint24 _callbackGasLimit) 59 | virtual internal view 60 | returns (uint256) 61 | { 62 | return ( 63 | (100 + __witnetBaseFeeOverheadPercentage) 64 | * __witnet.estimateBaseFeeWithCallback( 65 | tx.gasprice, 66 | _callbackGasLimit 67 | ) 68 | ) / 100; 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /contracts/apps/WitnetPriceSolverBase.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity >=0.8.0 <0.9.0; 4 | 5 | import "../data/WitnetPriceFeedsData.sol"; 6 | import "../interfaces/IWitnetPriceFeeds.sol"; 7 | 8 | abstract contract WitnetPriceSolverBase 9 | is 10 | IWitnetPriceSolver, 11 | WitnetPriceFeedsData 12 | { 13 | address public immutable override delegator; 14 | 15 | modifier onlyDelegator { 16 | require( 17 | address(this) == delegator, 18 | "WitnetPriceSolverBase: not the delegator" 19 | ); 20 | _; 21 | } 22 | 23 | constructor() { 24 | delegator = msg.sender; 25 | } 26 | 27 | function specs() external pure returns (bytes4) { 28 | return type(IWitnetPriceSolver).interfaceId; 29 | } 30 | 31 | function validate(bytes4 feedId, string[] calldata deps) virtual override external { 32 | bytes32 _depsFlag; 33 | uint256 _innerDecimals; 34 | require( 35 | deps.length <= 8, 36 | "WitnetPriceSolverBase: too many dependencies" 37 | ); 38 | for (uint _ix = 0; _ix < deps.length; _ix ++) { 39 | bytes4 _depsId4 = bytes4(keccak256(bytes(deps[_ix]))); 40 | Record storage __depsFeed = __records_(_depsId4); 41 | require( 42 | __depsFeed.index > 0, 43 | string(abi.encodePacked( 44 | "WitnetPriceSolverBase: unsupported ", 45 | deps[_ix] 46 | )) 47 | ); 48 | require( 49 | _depsId4 != feedId, 50 | string(abi.encodePacked( 51 | "WitnetPriceSolverBase: loop on ", 52 | deps[_ix] 53 | )) 54 | ); 55 | _depsFlag |= (bytes32(_depsId4) >> (32 * _ix)); 56 | _innerDecimals += __depsFeed.decimals; 57 | } 58 | Record storage __feed = __records_(feedId); 59 | __feed.solverReductor = int(uint(__feed.decimals)) - int(_innerDecimals); 60 | __feed.solverDepsFlag = _depsFlag; 61 | } 62 | } -------------------------------------------------------------------------------- /contracts/apps/WitnetRandomnessRequestConsumer.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity >=0.7.0 <0.9.0; 4 | pragma experimental ABIEncoderV2; 5 | 6 | import "./WitnetConsumer.sol"; 7 | import "../WitnetRequest.sol"; 8 | 9 | abstract contract WitnetRandomnessRequestConsumer 10 | is 11 | WitnetConsumer 12 | { 13 | using Witnet for bytes; 14 | using WitnetCBOR for WitnetCBOR.CBOR; 15 | using WitnetV2 for bytes32; 16 | using WitnetV2 for WitnetV2.RadonSLA; 17 | 18 | bytes32 internal immutable __witnetRandomnessRadHash; 19 | 20 | /// @param _wrb Address of the WitnetOracle contract. 21 | /// @param _baseFeeOverheadPercentage Percentage over base fee to pay as on every data request. 22 | /// @param _callbackGasLimit Maximum gas to be spent by the IWitnetConsumer's callback methods. 23 | constructor( 24 | WitnetOracle _wrb, 25 | uint16 _baseFeeOverheadPercentage, 26 | uint24 _callbackGasLimit 27 | ) 28 | UsingWitnet(_wrb) 29 | WitnetConsumer(_callbackGasLimit) 30 | { 31 | // On-chain building of the Witnet Randomness Request: 32 | { 33 | WitnetRequestBytecodes _registry = witnet().registry(); 34 | // Build own Witnet Randomness Request: 35 | bytes32[] memory _retrievals = new bytes32[](1); 36 | _retrievals[0] = _registry.verifyRadonRetrieval( 37 | Witnet.RadonDataRequestMethods.RNG, 38 | "", // no url 39 | "", // no body 40 | new string[2][](0), // no headers 41 | hex"80" // no retrieval script 42 | ); 43 | Witnet.RadonFilter[] memory _filters; 44 | bytes32 _aggregator = _registry.verifyRadonReducer(Witnet.RadonReducer({ 45 | opcode: Witnet.RadonReducerOpcodes.Mode, 46 | filters: _filters // no filters 47 | })); 48 | bytes32 _tally = _registry.verifyRadonReducer(Witnet.RadonReducer({ 49 | opcode: Witnet.RadonReducerOpcodes.ConcatenateAndHash, 50 | filters: _filters // no filters 51 | })); 52 | __witnetRandomnessRadHash = _registry.verifyRadonRequest( 53 | _retrievals, 54 | _aggregator, 55 | _tally, 56 | 32, // 256 bits of pure entropy ;-) 57 | new string[][](_retrievals.length) 58 | ); 59 | } 60 | __witnetBaseFeeOverheadPercentage = _baseFeeOverheadPercentage; 61 | } 62 | 63 | function _witnetEstimateEvmReward() virtual override internal view returns (uint256) { 64 | return _witnetEstimateEvmReward(32); 65 | } 66 | 67 | function _witnetRandomUniformUint32(uint32 _range, uint256 _nonce, bytes32 _seed) internal pure returns (uint32) { 68 | uint256 _number = uint256( 69 | keccak256( 70 | abi.encode(_seed, _nonce) 71 | ) 72 | ) & uint256(2 ** 224 - 1); 73 | return uint32((_number * _range) >> 224); 74 | } 75 | 76 | function _witnetReadRandomizeFromResultValue(WitnetCBOR.CBOR calldata cborValue) internal pure returns (bytes32) { 77 | return cborValue.readBytes().toBytes32(); 78 | } 79 | 80 | function __witnetRandomize(uint256 _witnetEvmReward) virtual internal returns (uint256) { 81 | return __witnetRandomize(_witnetEvmReward, __witnetDefaultSLA); 82 | } 83 | 84 | function __witnetRandomize( 85 | uint256 _witnetEvmReward, 86 | WitnetV2.RadonSLA memory _witnetQuerySLA 87 | ) 88 | virtual internal 89 | returns (uint256 _randomizeId) 90 | { 91 | return __witnet.postRequestWithCallback{ 92 | value: _witnetEvmReward 93 | }( 94 | __witnetRandomnessRadHash, 95 | _witnetQuerySLA, 96 | __witnetCallbackGasLimit 97 | ); 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /contracts/apps/WitnetRequestConsumer.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.0; 3 | 4 | import "./UsingWitnetRequest.sol"; 5 | import "./WitnetConsumer.sol"; 6 | 7 | abstract contract WitnetRequestConsumer 8 | is 9 | UsingWitnetRequest, 10 | WitnetConsumer 11 | { 12 | using WitnetCBOR for WitnetCBOR.CBOR; 13 | using WitnetCBOR for WitnetCBOR.CBOR[]; 14 | 15 | /// @param _witnetRequest Address of the WitnetRequest contract containing the actual data request. 16 | /// @param _baseFeeOverheadPercentage Percentage over base fee to pay as on every data request. 17 | /// @param _callbackGasLimit Maximum gas to be spent by the IWitnetConsumer's callback methods. 18 | constructor( 19 | WitnetRequest _witnetRequest, 20 | uint16 _baseFeeOverheadPercentage, 21 | uint24 _callbackGasLimit 22 | ) 23 | UsingWitnetRequest(_witnetRequest, _baseFeeOverheadPercentage) 24 | WitnetConsumer(_callbackGasLimit) 25 | {} 26 | 27 | function _witnetEstimateEvmReward() 28 | virtual override(UsingWitnetRequest, WitnetConsumer) 29 | internal view 30 | returns (uint256) 31 | { 32 | return WitnetConsumer._witnetEstimateEvmReward(); 33 | } 34 | 35 | function _witnetEstimateEvmReward(uint16) 36 | virtual override(UsingWitnet, WitnetConsumer) 37 | internal view 38 | returns (uint256) 39 | { 40 | return WitnetConsumer._witnetEstimateEvmReward(); 41 | } 42 | 43 | function __witnetRequestData( 44 | uint256 _witnetEvmReward, 45 | WitnetV2.RadonSLA memory _witnetQuerySLA 46 | ) 47 | virtual override 48 | internal returns (uint256) 49 | { 50 | return __witnet.postRequestWithCallback{ 51 | value: _witnetEvmReward 52 | }( 53 | __witnetRequestRadHash, 54 | _witnetQuerySLA, 55 | __witnetCallbackGasLimit 56 | ); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /contracts/apps/WitnetRequestTemplateConsumer.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.0; 3 | 4 | import "./UsingWitnetRequestTemplate.sol"; 5 | import "./WitnetConsumer.sol"; 6 | 7 | abstract contract WitnetRequestTemplateConsumer 8 | is 9 | UsingWitnetRequestTemplate, 10 | WitnetConsumer 11 | { 12 | using WitnetCBOR for WitnetCBOR.CBOR; 13 | using WitnetCBOR for WitnetCBOR.CBOR[]; 14 | 15 | /// @param _witnetRequestTemplate Address of the WitnetRequestTemplate from which actual data requests will get built. 16 | /// @param _baseFeeOverheadPercentage Percentage over base fee to pay as on every data request. 17 | /// @param _callbackGasLimit Maximum gas to be spent by the IWitnetConsumer's callback methods. 18 | constructor( 19 | WitnetRequestTemplate _witnetRequestTemplate, 20 | uint16 _baseFeeOverheadPercentage, 21 | uint24 _callbackGasLimit 22 | ) 23 | UsingWitnetRequestTemplate(_witnetRequestTemplate, _baseFeeOverheadPercentage) 24 | WitnetConsumer(_callbackGasLimit) 25 | {} 26 | 27 | function _witnetEstimateEvmReward() 28 | virtual override(UsingWitnetRequestTemplate, WitnetConsumer) 29 | internal view 30 | returns (uint256) 31 | { 32 | return WitnetConsumer._witnetEstimateEvmReward(__witnetQueryResultMaxSize); 33 | } 34 | 35 | function _witnetEstimateEvmReward(uint16) 36 | virtual override(UsingWitnet, WitnetConsumer) 37 | internal view 38 | returns (uint256) 39 | { 40 | return WitnetConsumer._witnetEstimateEvmReward(); 41 | } 42 | 43 | function __witnetRequestData( 44 | uint256 _witnetEvmReward, 45 | string[][] memory _witnetRequestArgs, 46 | WitnetV2.RadonSLA memory _witnetQuerySLA 47 | ) 48 | virtual override 49 | internal returns (uint256) 50 | { 51 | return __witnet.postRequestWithCallback{ 52 | value: _witnetEvmReward 53 | }( 54 | _witnetBuildRadHash(_witnetRequestArgs), 55 | _witnetQuerySLA, 56 | __witnetCallbackGasLimit 57 | ); 58 | } 59 | 60 | } 61 | -------------------------------------------------------------------------------- /contracts/core/WitnetDeployer.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity >=0.8.0 <0.9.0; 4 | 5 | import "./WitnetProxy.sol"; 6 | import "../libs/Create3.sol"; 7 | 8 | /// @notice WitnetDeployer contract used both as CREATE2 (EIP-1014) factory for Witnet artifacts, 9 | /// @notice and CREATE3 (EIP-3171) factory for Witnet proxies. 10 | /// @author Guillermo Díaz 11 | 12 | contract WitnetDeployer { 13 | 14 | /// @notice Use given `_initCode` and `_salt` to deploy a contract into a deterministic address. 15 | /// @dev The address of deployed address will be determined by both the `_initCode` and the `_salt`, but not the address 16 | /// @dev nor the nonce of the caller (i.e. see EIP-1014). 17 | /// @param _initCode Creation code, including construction logic and input parameters. 18 | /// @param _salt Arbitrary value to modify resulting address. 19 | /// @return _deployed Just deployed contract address. 20 | function deploy(bytes memory _initCode, bytes32 _salt) 21 | external 22 | returns (address _deployed) 23 | { 24 | _deployed = determineAddr(_initCode, _salt); 25 | if (_deployed.code.length == 0) { 26 | assembly { 27 | _deployed := create2(0, add(_initCode, 0x20), mload(_initCode), _salt) 28 | } 29 | require(_deployed != address(0), "WitnetDeployer: deployment failed"); 30 | } 31 | } 32 | 33 | /// @notice Determine counter-factual address of the contract that would be deployed by the given `_initCode` and a `_salt`. 34 | /// @param _initCode Creation code, including construction logic and input parameters. 35 | /// @param _salt Arbitrary value to modify resulting address. 36 | /// @return Deterministic contract address. 37 | function determineAddr(bytes memory _initCode, bytes32 _salt) 38 | public view 39 | returns (address) 40 | { 41 | return address( 42 | uint160(uint(keccak256( 43 | abi.encodePacked( 44 | bytes1(0xff), 45 | address(this), 46 | _salt, 47 | keccak256(_initCode) 48 | ) 49 | ))) 50 | ); 51 | } 52 | 53 | function determineProxyAddr(bytes32 _salt) 54 | public view 55 | returns (address) 56 | { 57 | return Create3.determineAddr(_salt); 58 | } 59 | 60 | function proxify(bytes32 _proxySalt, address _firstImplementation, bytes memory _initData) 61 | external 62 | returns (WitnetProxy) 63 | { 64 | address _proxyAddr = determineProxyAddr(_proxySalt); 65 | if (_proxyAddr.code.length == 0) { 66 | // deploy the WitnetProxy 67 | Create3.deploy(_proxySalt, type(WitnetProxy).creationCode); 68 | // settle first implementation address, 69 | WitnetProxy(payable(_proxyAddr)).upgradeTo( 70 | _firstImplementation, 71 | // and initialize it, providing 72 | abi.encode( 73 | // the owner (i.e. the caller of this function) 74 | msg.sender, 75 | // and some (optional) initialization data 76 | _initData 77 | ) 78 | ); 79 | return WitnetProxy(payable(_proxyAddr)); 80 | } else { 81 | revert("WitnetDeployer: already proxified"); 82 | } 83 | } 84 | 85 | } -------------------------------------------------------------------------------- /contracts/core/WitnetProxy.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity >=0.7.0 <0.9.0; 4 | pragma experimental ABIEncoderV2; 5 | 6 | import "../patterns/Upgradeable.sol"; 7 | 8 | /// @title WitnetProxy: upgradable delegate-proxy contract. 9 | /// @author Guillermo Díaz 10 | contract WitnetProxy { 11 | 12 | /// Event emitted every time the implementation gets updated. 13 | event Upgraded(address indexed implementation); 14 | 15 | /// Constructor with no params as to ease eventual support of Singleton pattern (i.e. ERC-2470). 16 | constructor () {} 17 | 18 | receive() virtual external payable {} 19 | 20 | /// Payable fallback accepts delegating calls to payable functions. 21 | fallback() external payable { /* solhint-disable no-complex-fallback */ 22 | address _implementation = implementation(); 23 | assembly { /* solhint-disable avoid-low-level-calls */ 24 | // Gas optimized delegate call to 'implementation' contract. 25 | // Note: `msg.data`, `msg.sender` and `msg.value` will be passed over 26 | // to actual implementation of `msg.sig` within `implementation` contract. 27 | let ptr := mload(0x40) 28 | calldatacopy(ptr, 0, calldatasize()) 29 | let result := delegatecall(gas(), _implementation, ptr, calldatasize(), 0, 0) 30 | let size := returndatasize() 31 | returndatacopy(ptr, 0, size) 32 | switch result 33 | case 0 { 34 | // pass back revert message: 35 | revert(ptr, size) 36 | } 37 | default { 38 | // pass back same data as returned by 'implementation' contract: 39 | return(ptr, size) 40 | } 41 | } 42 | } 43 | 44 | /// Returns proxy's current implementation address. 45 | function implementation() public view returns (address) { 46 | return __proxySlot().implementation; 47 | } 48 | 49 | /// Upgrades the `implementation` address. 50 | /// @param _newImplementation New implementation address. 51 | /// @param _initData Raw data with which new implementation will be initialized. 52 | /// @return Returns whether new implementation would be further upgradable, or not. 53 | function upgradeTo(address _newImplementation, bytes memory _initData) 54 | public returns (bool) 55 | { 56 | // New implementation cannot be null: 57 | require(_newImplementation != address(0), "WitnetProxy: null implementation"); 58 | 59 | address _oldImplementation = implementation(); 60 | if (_oldImplementation != address(0)) { 61 | // New implementation address must differ from current one: 62 | require(_newImplementation != _oldImplementation, "WitnetProxy: nothing to upgrade"); 63 | 64 | // Assert whether current implementation is intrinsically upgradable: 65 | try Upgradeable(_oldImplementation).isUpgradable() returns (bool _isUpgradable) { 66 | require(_isUpgradable, "WitnetProxy: not upgradable"); 67 | } catch { 68 | revert("WitnetProxy: unable to check upgradability"); 69 | } 70 | 71 | // Assert whether current implementation allows `msg.sender` to upgrade the proxy: 72 | (bool _wasCalled, bytes memory _result) = _oldImplementation.delegatecall( 73 | abi.encodeWithSignature( 74 | "isUpgradableFrom(address)", 75 | msg.sender 76 | ) 77 | ); 78 | require(_wasCalled, "WitnetProxy: uncompliant implementation"); 79 | require(abi.decode(_result, (bool)), "WitnetProxy: not authorized"); 80 | require( 81 | Upgradeable(_oldImplementation).proxiableUUID() == Upgradeable(_newImplementation).proxiableUUID(), 82 | "WitnetProxy: proxiableUUIDs mismatch" 83 | ); 84 | } 85 | 86 | // Initialize new implementation within proxy-context storage: 87 | (bool _wasInitialized, bytes memory _returnData) = _newImplementation.delegatecall( 88 | abi.encodeWithSignature( 89 | "initialize(bytes)", 90 | _initData 91 | ) 92 | ); 93 | if (!_wasInitialized) { 94 | if (_returnData.length < 68) { 95 | revert("WitnetProxy: initialization failed"); 96 | } else { 97 | assembly { 98 | _returnData := add(_returnData, 0x04) 99 | } 100 | revert(abi.decode(_returnData, (string))); 101 | } 102 | } 103 | 104 | // If all checks and initialization pass, update implementation address: 105 | __proxySlot().implementation = _newImplementation; 106 | 107 | emit Upgraded(_newImplementation); 108 | 109 | // Asserts new implementation complies w/ minimal implementation of Upgradeable interface: 110 | try Upgradeable(_newImplementation).isUpgradable() returns (bool _isUpgradable) { 111 | return _isUpgradable; 112 | } 113 | catch { 114 | revert ("WitnetProxy: uncompliant implementation"); 115 | } 116 | } 117 | 118 | /// @dev Complying with EIP-1967, retrieves storage struct containing proxy's current implementation address. 119 | function __proxySlot() private pure returns (Proxiable.ProxiableSlot storage _slot) { 120 | assembly { 121 | // bytes32(uint256(keccak256('eip1967.proxy.implementation')) - 1) 122 | _slot.slot := 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc 123 | } 124 | } 125 | 126 | } 127 | -------------------------------------------------------------------------------- /contracts/core/WitnetUpgradableBase.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | // solhint-disable var-name-mixedcase 3 | // solhint-disable payable-fallback 4 | 5 | pragma solidity >=0.8.0 <0.9.0; 6 | 7 | import "../patterns/Ownable2Step.sol"; 8 | import "../patterns/ReentrancyGuard.sol"; 9 | import "../patterns/Upgradeable.sol"; 10 | 11 | import "./WitnetProxy.sol"; 12 | 13 | /// @title Witnet Request Board base contract, with an Upgradeable (and Destructible) touch. 14 | /// @author Guillermo Díaz 15 | abstract contract WitnetUpgradableBase 16 | is 17 | Ownable2Step, 18 | Upgradeable, 19 | ReentrancyGuard 20 | { 21 | bytes32 internal immutable _WITNET_UPGRADABLE_VERSION; 22 | 23 | address public immutable deployer = msg.sender; 24 | 25 | constructor( 26 | bool _upgradable, 27 | bytes32 _versionTag, 28 | string memory _proxiableUUID 29 | ) 30 | Upgradeable(_upgradable) 31 | { 32 | _WITNET_UPGRADABLE_VERSION = _versionTag; 33 | proxiableUUID = keccak256(bytes(_proxiableUUID)); 34 | } 35 | 36 | /// @dev Reverts if proxy delegatecalls to unexistent method. 37 | fallback() virtual external { 38 | _revert("not implemented"); 39 | } 40 | 41 | 42 | function class() virtual public view returns (string memory) { 43 | return type(WitnetUpgradableBase).name; 44 | } 45 | 46 | // ================================================================================================================ 47 | // --- Overrides 'Proxiable' -------------------------------------------------------------------------------------- 48 | 49 | /// @dev Gets immutable "heritage blood line" (ie. genotype) as a Proxiable, and eventually Upgradeable, contract. 50 | /// If implemented as an Upgradeable touch, upgrading this contract to another one with a different 51 | /// `proxiableUUID()` value should fail. 52 | bytes32 public immutable override proxiableUUID; 53 | 54 | 55 | // ================================================================================================================ 56 | // --- Overrides 'Upgradeable' -------------------------------------------------------------------------------------- 57 | 58 | /// Retrieves human-readable version tag of current implementation. 59 | function version() public view virtual override returns (string memory) { 60 | return _toString(_WITNET_UPGRADABLE_VERSION); 61 | } 62 | 63 | 64 | // ================================================================================================================ 65 | // --- Internal methods ------------------------------------------------------------------------------------------- 66 | 67 | function _require( 68 | bool _condition, 69 | string memory _message 70 | ) 71 | internal view 72 | { 73 | if (!_condition) { 74 | _revert(_message); 75 | } 76 | } 77 | 78 | function _revert(string memory _message) 79 | internal view 80 | { 81 | revert( 82 | string(abi.encodePacked( 83 | class(), 84 | ": ", 85 | _message 86 | )) 87 | ); 88 | } 89 | 90 | /// Converts bytes32 into string. 91 | function _toString(bytes32 _bytes32) 92 | internal pure 93 | returns (string memory) 94 | { 95 | bytes memory _bytes = new bytes(_toStringLength(_bytes32)); 96 | for (uint _i = 0; _i < _bytes.length;) { 97 | _bytes[_i] = _bytes32[_i]; 98 | unchecked { 99 | _i ++; 100 | } 101 | } 102 | return string(_bytes); 103 | } 104 | 105 | // Calculate length of string-equivalent to given bytes32. 106 | function _toStringLength(bytes32 _bytes32) 107 | internal pure 108 | returns (uint _length) 109 | { 110 | for (; _length < 32; ) { 111 | if (_bytes32[_length] == 0) { 112 | break; 113 | } 114 | unchecked { 115 | _length ++; 116 | } 117 | } 118 | } 119 | 120 | } -------------------------------------------------------------------------------- /contracts/core/customs/WitnetDeployerCfxCore.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity >=0.8.0 <0.9.0; 4 | 5 | import "../WitnetProxy.sol"; 6 | 7 | /// @notice WitnetDeployer contract used both as CREATE2 factory (EIP-1014) for Witnet artifacts, 8 | /// @notice and CREATE3 factory (EIP-3171) for Witnet proxies, on the Conflux Core Ecosystem. 9 | /// @author Guillermo Díaz 10 | 11 | contract WitnetDeployerCfxCore { 12 | 13 | /// @notice Use given `_initCode` and `_salt` to deploy a contract into a deterministic address. 14 | /// @dev The address of deployed address will be determined by both the `_initCode` and the `_salt`, but not the address 15 | /// @dev nor the nonce of the caller (i.e. see EIP-1014). 16 | /// @param _initCode Creation code, including construction logic and input parameters. 17 | /// @param _salt Arbitrary value to modify resulting address. 18 | /// @return _deployed Just deployed contract address. 19 | function deploy(bytes memory _initCode, bytes32 _salt) 20 | public 21 | returns (address _deployed) 22 | { 23 | _deployed = determineAddr(_initCode, _salt); 24 | if (_deployed.code.length == 0) { 25 | assembly { 26 | _deployed := create2(0, add(_initCode, 0x20), mload(_initCode), _salt) 27 | } 28 | require(_deployed != address(0), "WitnetDeployer: deployment failed"); 29 | } 30 | } 31 | 32 | /// @notice Determine counter-factual address of the contract that would be deployed by the given `_initCode` and a `_salt`. 33 | /// @param _initCode Creation code, including construction logic and input parameters. 34 | /// @param _salt Arbitrary value to modify resulting address. 35 | /// @return Deterministic contract address. 36 | function determineAddr(bytes memory _initCode, bytes32 _salt) 37 | public view 38 | returns (address) 39 | { 40 | return address( 41 | (uint160(uint(keccak256( 42 | abi.encodePacked( 43 | bytes1(0xff), 44 | address(this), 45 | _salt, 46 | keccak256(_initCode) 47 | ) 48 | ))) & uint160(0x0fffFFFFFfFfffFfFfFFffFffFffFFfffFfFFFFf) 49 | ) | uint160(0x8000000000000000000000000000000000000000) 50 | ); 51 | } 52 | 53 | function determineProxyAddr(bytes32 _salt) 54 | public view 55 | returns (address) 56 | { 57 | return determineAddr(type(WitnetProxy).creationCode, _salt); 58 | } 59 | 60 | function proxify(bytes32 _proxySalt, address _firstImplementation, bytes memory _initData) 61 | external 62 | returns (WitnetProxy) 63 | { 64 | address _proxyAddr = determineProxyAddr(_proxySalt); 65 | if (_proxyAddr.code.length == 0) { 66 | // deploy the WitnetProxy 67 | deploy(type(WitnetProxy).creationCode, _proxySalt); 68 | // settle first implementation address, 69 | WitnetProxy(payable(_proxyAddr)).upgradeTo( 70 | _firstImplementation, 71 | // and initialize it, providing 72 | abi.encode( 73 | // the owner (i.e. the caller of this function) 74 | msg.sender, 75 | // and some (optional) initialization data 76 | _initData 77 | ) 78 | ); 79 | return WitnetProxy(payable(_proxyAddr)); 80 | } else { 81 | revert("WitnetDeployer: already proxified"); 82 | } 83 | } 84 | 85 | } -------------------------------------------------------------------------------- /contracts/core/customs/WitnetDeployerMeter.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity >=0.8.0 <0.9.0; 4 | 5 | import "../WitnetProxy.sol"; 6 | 7 | /// @notice WitnetDeployer contract used both as CREATE2 factory (EIP-1014) for Witnet artifacts, 8 | /// @notice and CREATE3 factory (EIP-3171) for Witnet proxies, on the Meter Ecosystem. 9 | /// @author Guillermo Díaz 10 | 11 | contract WitnetDeployerMeter { 12 | 13 | /// @notice Use given `_initCode` and `_salt` to deploy a contract into a deterministic address. 14 | /// @dev The address of deployed address will be determined by both the `_initCode` and the `_salt`, but not the address 15 | /// @dev nor the nonce of the caller (i.e. see EIP-1014). 16 | /// @param _initCode Creation code, including construction logic and input parameters. 17 | /// @param _salt Arbitrary value to modify resulting address. 18 | /// @return _deployed Just deployed contract address. 19 | function deploy(bytes memory _initCode, bytes32 _salt) 20 | public 21 | returns (address _deployed) 22 | { 23 | _deployed = determineAddr(_initCode, _salt); 24 | if (_deployed.code.length == 0) { 25 | assembly { 26 | _deployed := create2(0, add(_initCode, 0x20), mload(_initCode), _salt) 27 | } 28 | require(_deployed != address(0), "WitnetDeployerMeter: deployment failed"); 29 | } 30 | } 31 | 32 | /// @notice Determine counter-factual address of the contract that would be deployed by the given `_initCode` and a `_salt`. 33 | /// @param _initCode Creation code, including construction logic and input parameters. 34 | /// @param _salt Arbitrary value to modify resulting address. 35 | /// @return Deterministic contract address. 36 | function determineAddr(bytes memory _initCode, bytes32 _salt) 37 | public view 38 | returns (address) 39 | { 40 | return address( 41 | uint160(uint(keccak256( 42 | abi.encodePacked( 43 | bytes1(0xff), 44 | address(this), 45 | _salt, 46 | keccak256(_initCode) 47 | ) 48 | ))) 49 | ); 50 | } 51 | 52 | function determineProxyAddr(bytes32 _salt) 53 | public view 54 | returns (address) 55 | { 56 | return determineAddr(type(WitnetProxy).creationCode, _salt); 57 | } 58 | 59 | function proxify(bytes32 _proxySalt, address _firstImplementation, bytes memory _initData) 60 | external 61 | returns (WitnetProxy) 62 | { 63 | address _proxyAddr = determineProxyAddr(_proxySalt); 64 | if (_proxyAddr.code.length == 0) { 65 | // deploy the WitnetProxy 66 | deploy(type(WitnetProxy).creationCode, _proxySalt); 67 | // settle first implementation address, 68 | WitnetProxy(payable(_proxyAddr)).upgradeTo( 69 | _firstImplementation, 70 | // and initialize it, providing 71 | abi.encode( 72 | // the owner (i.e. the caller of this function) 73 | msg.sender, 74 | // and some (optional) initialization data 75 | _initData 76 | ) 77 | ); 78 | return WitnetProxy(payable(_proxyAddr)); 79 | } else { 80 | revert("WitnetDeployerMeter: already proxified"); 81 | } 82 | } 83 | 84 | } -------------------------------------------------------------------------------- /contracts/core/customs/WitnetOracleTrustableObscuro.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | /* solhint-disable var-name-mixedcase */ 4 | 5 | pragma solidity >=0.7.0 <0.9.0; 6 | pragma experimental ABIEncoderV2; 7 | 8 | import "../defaults/WitnetOracleTrustableDefault.sol"; 9 | 10 | /// @title Witnet Request Board "trustable" implementation contract. 11 | /// @notice Contract to bridge requests to Witnet Decentralized Oracle Network. 12 | /// @dev This contract enables posting requests that Witnet bridges will insert into the Witnet network. 13 | /// The result of the requests will be posted back to this contract by the bridge nodes too. 14 | /// @author The Witnet Foundation 15 | contract WitnetOracleTrustableObscuro 16 | is 17 | WitnetOracleTrustableDefault 18 | { 19 | function class() virtual override public view returns (string memory) { 20 | return type(WitnetOracleTrustableObscuro).name; 21 | } 22 | 23 | constructor( 24 | WitnetRequestFactory _factory, 25 | WitnetRequestBytecodes _registry, 26 | bool _upgradable, 27 | bytes32 _versionTag, 28 | uint256 _reportResultGasBase, 29 | uint256 _reportResultWithCallbackGasBase, 30 | uint256 _reportResultWithCallbackRevertGasBase, 31 | uint256 _sstoreFromZeroGas 32 | ) 33 | WitnetOracleTrustableDefault( 34 | _factory, 35 | _registry, 36 | _upgradable, 37 | _versionTag, 38 | _reportResultGasBase, 39 | _reportResultWithCallbackGasBase, 40 | _reportResultWithCallbackRevertGasBase, 41 | _sstoreFromZeroGas 42 | ) 43 | {} 44 | 45 | 46 | // ================================================================================================================ 47 | // --- Overrides implementation of 'IWitnetOracleView' ------------------------------------------------------ 48 | 49 | /// @notice Gets the whole Query data contents, if any, no matter its current status. 50 | /// @dev Fails if or if `msg.sender` is not the actual requester. 51 | function getQuery(uint256 _queryId) 52 | public view 53 | virtual override 54 | onlyRequester(_queryId) 55 | returns (WitnetV2.Query memory) 56 | { 57 | return WitnetOracleTrustableBase.getQuery(_queryId); 58 | } 59 | 60 | /// @notice Retrieves the whole `Witnet.Response` record referred to a previously posted Witnet Data Request. 61 | /// @dev Fails if the `_queryId` is not in 'Reported' status, or if `msg.sender` is not the actual requester. 62 | /// @param _queryId The unique query identifier 63 | function getQueryResponse(uint256 _queryId) 64 | public view 65 | virtual override 66 | onlyRequester(_queryId) 67 | returns (WitnetV2.Response memory _response) 68 | { 69 | return WitnetOracleTrustableBase.getQueryResponse(_queryId); 70 | } 71 | 72 | /// @notice Gets error code identifying some possible failure on the resolution of the given query. 73 | /// @param _queryId The unique query identifier. 74 | function getQueryResultError(uint256 _queryId) 75 | public view 76 | virtual override 77 | onlyRequester(_queryId) 78 | returns (Witnet.ResultError memory) 79 | { 80 | return WitnetOracleTrustableBase.getQueryResultError(_queryId); 81 | } 82 | 83 | } 84 | -------------------------------------------------------------------------------- /contracts/core/customs/WitnetOracleTrustableOvm2.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | /* solhint-disable var-name-mixedcase */ 4 | 5 | pragma solidity >=0.7.0 <0.9.0; 6 | pragma experimental ABIEncoderV2; 7 | 8 | import "../defaults/WitnetOracleTrustableDefault.sol"; 9 | 10 | // solhint-disable-next-line 11 | interface OVM_GasPriceOracle { 12 | function getL1Fee(bytes calldata _data) external view returns (uint256); 13 | } 14 | 15 | /// @title Witnet Request Board "trustable" implementation contract. 16 | /// @notice Contract to bridge requests to Witnet Decentralized Oracle Network. 17 | /// @dev This contract enables posting requests that Witnet bridges will insert into the Witnet network. 18 | /// The result of the requests will be posted back to this contract by the bridge nodes too. 19 | /// @author The Witnet Foundation 20 | contract WitnetOracleTrustableOvm2 21 | is 22 | WitnetOracleTrustableDefault 23 | { 24 | using WitnetV2 for WitnetV2.RadonSLA; 25 | 26 | function class() virtual override public view returns (string memory) { 27 | return type(WitnetOracleTrustableOvm2).name; 28 | } 29 | 30 | constructor( 31 | WitnetRequestFactory _factory, 32 | WitnetRequestBytecodes _registry, 33 | bool _upgradable, 34 | bytes32 _versionTag, 35 | uint256 _reportResultGasBase, 36 | uint256 _reportResultWithCallbackGasBase, 37 | uint256 _reportResultWithCallbackRevertGasBase, 38 | uint256 _sstoreFromZeroGas 39 | ) 40 | WitnetOracleTrustableDefault( 41 | _factory, 42 | _registry, 43 | _upgradable, 44 | _versionTag, 45 | _reportResultGasBase, 46 | _reportResultWithCallbackGasBase, 47 | _reportResultWithCallbackRevertGasBase, 48 | _sstoreFromZeroGas 49 | ) 50 | { 51 | __gasPriceOracleL1 = OVM_GasPriceOracle(0x420000000000000000000000000000000000000F); 52 | } 53 | 54 | OVM_GasPriceOracle immutable internal __gasPriceOracleL1; 55 | 56 | function _getCurrentL1Fee(uint16 _resultMaxSize) virtual internal view returns (uint256) { 57 | return __gasPriceOracleL1.getL1Fee( 58 | abi.encodePacked( 59 | hex"06eb2c42000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000ffffffffff00000000000000000000000000000000000000000000000000000000fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000ff", 60 | _resultMaxBuffer(_resultMaxSize) 61 | ) 62 | ); 63 | } 64 | 65 | function _resultMaxBuffer(uint16 _resultMaxSize) private pure returns (bytes memory) { 66 | unchecked { 67 | uint256[] memory _buffer = new uint256[](_resultMaxSize / 32); 68 | for (uint _ix = 0; _ix < _buffer.length; _ix ++) { 69 | _buffer[_ix] = type(uint256).max; 70 | } 71 | return abi.encodePacked( 72 | _buffer, 73 | uint256((1 << (_resultMaxSize % 32)) - 1) 74 | ); 75 | } 76 | } 77 | 78 | // ================================================================================================================ 79 | // --- Overrides 'IWitnetOracle' ---------------------------------------------------------------------------- 80 | 81 | /// @notice Estimate the minimum reward required for posting a data request. 82 | /// @dev Underestimates if the size of returned data is greater than `_resultMaxSize`. 83 | /// @param _gasPrice Expected gas price to pay upon posting the data request. 84 | /// @param _resultMaxSize Maximum expected size of returned data (in bytes). 85 | function estimateBaseFee(uint256 _gasPrice, uint16 _resultMaxSize) 86 | public view 87 | virtual override 88 | returns (uint256) 89 | { 90 | return _getCurrentL1Fee(_resultMaxSize) + WitnetOracleTrustableDefault.estimateBaseFee(_gasPrice, _resultMaxSize); 91 | } 92 | 93 | /// @notice Estimate the minimum reward required for posting a data request with a callback. 94 | /// @param _gasPrice Expected gas price to pay upon posting the data request. 95 | /// @param _callbackGasLimit Maximum gas to be spent when reporting the data request result. 96 | function estimateBaseFeeWithCallback(uint256 _gasPrice, uint24 _callbackGasLimit) 97 | public view 98 | virtual override 99 | returns (uint256) 100 | { 101 | return _getCurrentL1Fee(32) + WitnetOracleTrustableDefault.estimateBaseFeeWithCallback(_gasPrice, _callbackGasLimit); 102 | } 103 | 104 | // ================================================================================================================ 105 | // --- Overrides 'IWitnetOracleReporter' -------------------------------------------------------------------------- 106 | 107 | /// @notice Estimates the actual earnings (or loss), in WEI, that a reporter would get by reporting result to given query, 108 | /// @notice based on the gas price of the calling transaction. Data requesters should consider upgrading the reward on 109 | /// @notice queries providing no actual earnings. 110 | function estimateReportEarnings( 111 | uint256[] calldata _witnetQueryIds, 112 | bytes calldata _reportTxMsgData, 113 | uint256 _reportTxGasPrice, 114 | uint256 _nanoWitPrice 115 | ) 116 | external view 117 | virtual override 118 | returns (uint256 _revenues, uint256 _expenses) 119 | { 120 | for (uint _ix = 0; _ix < _witnetQueryIds.length; _ix ++) { 121 | if (WitnetOracleDataLib.seekQueryStatus(_witnetQueryIds[_ix]) == WitnetV2.QueryStatus.Posted) { 122 | WitnetV2.Request storage __request = WitnetOracleDataLib.seekQueryRequest(_witnetQueryIds[_ix]); 123 | if (__request.gasCallback > 0) { 124 | _expenses += WitnetOracleTrustableDefault.estimateBaseFeeWithCallback( 125 | _reportTxGasPrice, 126 | __request.gasCallback 127 | ); 128 | } else { 129 | if (__request.witnetRAD != bytes32(0)) { 130 | _expenses += WitnetOracleTrustableDefault.estimateBaseFee( 131 | _reportTxGasPrice, 132 | registry.lookupRadonRequestResultMaxSize(__request.witnetRAD) 133 | ); 134 | } else { 135 | // todo: improve profit estimation accuracy if reporting on deleted query 136 | _expenses += WitnetOracleTrustableDefault.estimateBaseFee( 137 | _reportTxGasPrice, 138 | uint16(0) 139 | ); 140 | } 141 | } 142 | _expenses += __request.witnetSLA.nanoWitTotalFee() * _nanoWitPrice; 143 | _revenues += __request.evmReward; 144 | } 145 | } 146 | _expenses += __gasPriceOracleL1.getL1Fee(_reportTxMsgData); 147 | } 148 | } 149 | -------------------------------------------------------------------------------- /contracts/core/customs/WitnetOracleTrustableReef.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | /* solhint-disable var-name-mixedcase */ 4 | 5 | pragma solidity >=0.7.0 <0.9.0; 6 | pragma experimental ABIEncoderV2; 7 | 8 | // Inherits from: 9 | import "../defaults/WitnetOracleTrustableDefault.sol"; 10 | 11 | /// @title Witnet Request Board OVM-compatible (Optimism) "trustable" implementation. 12 | /// @notice Contract to bridge requests to Witnet Decentralized Oracle Network. 13 | /// @dev This contract enables posting requests that Witnet bridges will insert into the Witnet network. 14 | /// The result of the requests will be posted back to this contract by the bridge nodes too. 15 | /// @author The Witnet Foundation 16 | contract WitnetOracleTrustableReef 17 | is 18 | WitnetOracleTrustableDefault 19 | { 20 | function class() virtual override public view returns (string memory) { 21 | return type(WitnetOracleTrustableReef).name; 22 | } 23 | 24 | constructor( 25 | WitnetRequestFactory _factory, 26 | WitnetRequestBytecodes _registry, 27 | bool _upgradable, 28 | bytes32 _versionTag, 29 | uint256 _reportResultGasBase, 30 | uint256 _reportResultWithCallbackGasBase, 31 | uint256 _reportResultWithCallbackRevertGasBase, 32 | uint256 _sstoreFromZeroGas 33 | ) 34 | WitnetOracleTrustableDefault( 35 | _factory, 36 | _registry, 37 | _upgradable, 38 | _versionTag, 39 | _reportResultGasBase, 40 | _reportResultWithCallbackGasBase, 41 | _reportResultWithCallbackRevertGasBase, 42 | _sstoreFromZeroGas 43 | ) 44 | {} 45 | 46 | // ================================================================================================================ 47 | // --- Overrides 'IWitnetOracle' ---------------------------------------------------------------------------- 48 | 49 | /// @notice Estimate the minimum reward required for posting a data request. 50 | /// @dev Underestimates if the size of returned data is greater than `_resultMaxSize`. 51 | /// @param _resultMaxSize Maximum expected size of returned data (in bytes). 52 | function estimateBaseFee(uint256, uint16 _resultMaxSize) 53 | public view 54 | virtual override 55 | returns (uint256) 56 | { 57 | return WitnetOracleTrustableDefault.estimateBaseFee(1, _resultMaxSize); 58 | } 59 | 60 | /// @notice Estimate the minimum reward required for posting a data request with a callback. 61 | /// @param _callbackGasLimit Maximum gas to be spent when reporting the data request result. 62 | function estimateBaseFeeWithCallback(uint256, uint24 _callbackGasLimit) 63 | public view 64 | virtual override 65 | returns (uint256) 66 | { 67 | return WitnetOracleTrustableDefault.estimateBaseFeeWithCallback(1, _callbackGasLimit); 68 | } 69 | 70 | 71 | // ================================================================================================================ 72 | // --- Overrides 'Payable' ---------------------------------------------------------------------------------------- 73 | 74 | /// Gets current transaction price. 75 | function _getGasPrice() 76 | internal pure 77 | virtual override 78 | returns (uint256) 79 | { 80 | return 1; 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /contracts/core/customs/WitnetRequestBytecodesNoSha256.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity >=0.7.0 <0.9.0; 4 | pragma experimental ABIEncoderV2; 5 | 6 | import "../defaults/WitnetRequestBytecodesDefault.sol"; 7 | 8 | contract WitnetRequestBytecodesNoSha256 9 | is 10 | WitnetRequestBytecodesDefault 11 | { 12 | function class() virtual override public view returns (string memory) { 13 | return type(WitnetRequestBytecodesNoSha256).name; 14 | } 15 | 16 | constructor(bool _upgradable, bytes32 _versionTag) 17 | WitnetRequestBytecodesDefault(_upgradable, _versionTag) 18 | {} 19 | 20 | function _witnetHash(bytes memory chunk) virtual override internal pure returns (bytes32) { 21 | return keccak256(chunk); 22 | } 23 | } -------------------------------------------------------------------------------- /contracts/core/customs/WitnetRequestFactoryCfxCore.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity >=0.7.0 <0.9.0; 4 | pragma experimental ABIEncoderV2; 5 | 6 | import "../defaults/WitnetRequestFactoryDefault.sol"; 7 | 8 | contract WitnetRequestFactoryCfxCore 9 | is 10 | WitnetRequestFactoryDefault 11 | { 12 | constructor( 13 | WitnetOracle _witnet, 14 | WitnetRequestBytecodes _registry, 15 | bool _upgradable, 16 | bytes32 _versionTag 17 | ) 18 | WitnetRequestFactoryDefault(_witnet, _registry, _upgradable, _versionTag) 19 | {} 20 | 21 | function _cloneDeterministic(bytes32 _salt) 22 | override internal 23 | returns (address _instance) 24 | { 25 | bytes memory ptr = _cloneBytecodePtr(); 26 | assembly { 27 | // CREATE2 new instance: 28 | _instance := create2(0, ptr, 0x37, _salt) 29 | } 30 | emit Cloned(msg.sender, self(), _instance); 31 | } 32 | } -------------------------------------------------------------------------------- /contracts/core/defaults/WitnetOracleTrustableDefault.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | /* solhint-disable var-name-mixedcase */ 4 | 5 | pragma solidity >=0.7.0 <0.9.0; 6 | pragma experimental ABIEncoderV2; 7 | 8 | import "./WitnetOracleTrustableBase.sol"; 9 | 10 | /// @title Witnet Request Board "trustable" implementation contract. 11 | /// @notice Contract to bridge requests to Witnet Decentralized Oracle Network. 12 | /// @dev This contract enables posting requests that Witnet bridges will insert into the Witnet network. 13 | /// The result of the requests will be posted back to this contract by the bridge nodes too. 14 | /// @author The Witnet Foundation 15 | contract WitnetOracleTrustableDefault 16 | is 17 | WitnetOracleTrustableBase 18 | { 19 | function class() virtual override public view returns (string memory) { 20 | return type(WitnetOracleTrustableDefault).name; 21 | } 22 | 23 | uint256 internal immutable __reportResultGasBase; 24 | uint256 internal immutable __reportResultWithCallbackGasBase; 25 | uint256 internal immutable __reportResultWithCallbackRevertGasBase; 26 | uint256 internal immutable __sstoreFromZeroGas; 27 | 28 | constructor( 29 | WitnetRequestFactory _factory, 30 | WitnetRequestBytecodes _registry, 31 | bool _upgradable, 32 | bytes32 _versionTag, 33 | uint256 _reportResultGasBase, 34 | uint256 _reportResultWithCallbackGasBase, 35 | uint256 _reportResultWithCallbackRevertGasBase, 36 | uint256 _sstoreFromZeroGas 37 | ) 38 | WitnetOracleTrustableBase( 39 | _factory, 40 | _registry, 41 | _upgradable, 42 | _versionTag, 43 | address(0) 44 | ) 45 | { 46 | __reportResultGasBase = _reportResultGasBase; 47 | __reportResultWithCallbackGasBase = _reportResultWithCallbackGasBase; 48 | __reportResultWithCallbackRevertGasBase = _reportResultWithCallbackRevertGasBase; 49 | __sstoreFromZeroGas = _sstoreFromZeroGas; 50 | } 51 | 52 | 53 | // ================================================================================================================ 54 | // --- Overrides 'IWitnetOracle' ---------------------------------------------------------------------------- 55 | 56 | /// @notice Estimate the minimum reward required for posting a data request. 57 | /// @dev Underestimates if the size of returned data is greater than `_resultMaxSize`. 58 | /// @param _gasPrice Expected gas price to pay upon posting the data request. 59 | /// @param _resultMaxSize Maximum expected size of returned data (in bytes). 60 | function estimateBaseFee(uint256 _gasPrice, uint16 _resultMaxSize) 61 | public view 62 | virtual override 63 | returns (uint256) 64 | { 65 | return _gasPrice * ( 66 | __reportResultGasBase 67 | + __sstoreFromZeroGas * ( 68 | 4 + (_resultMaxSize == 0 ? 0 : _resultMaxSize - 1) / 32 69 | ) 70 | ); 71 | } 72 | 73 | /// @notice Estimate the minimum reward required for posting a data request with a callback. 74 | /// @param _gasPrice Expected gas price to pay upon posting the data request. 75 | /// @param _callbackGasLimit Maximum gas to be spent when reporting the data request result. 76 | function estimateBaseFeeWithCallback(uint256 _gasPrice, uint24 _callbackGasLimit) 77 | public view 78 | virtual override 79 | returns (uint256) 80 | { 81 | uint _reportResultWithCallbackGasThreshold = ( 82 | __reportResultWithCallbackRevertGasBase 83 | + 3 * __sstoreFromZeroGas 84 | ); 85 | if ( 86 | _callbackGasLimit < _reportResultWithCallbackGasThreshold 87 | || __reportResultWithCallbackGasBase + _callbackGasLimit < _reportResultWithCallbackGasThreshold 88 | ) { 89 | return ( 90 | _gasPrice 91 | * _reportResultWithCallbackGasThreshold 92 | ); 93 | } else { 94 | return ( 95 | _gasPrice 96 | * ( 97 | __reportResultWithCallbackGasBase 98 | + _callbackGasLimit 99 | ) 100 | ); 101 | } 102 | } 103 | 104 | 105 | // ================================================================================================================ 106 | // --- Overrides 'Payable' ---------------------------------------------------------------------------------------- 107 | 108 | /// Gets current transaction price. 109 | function _getGasPrice() 110 | internal view 111 | virtual override 112 | returns (uint256) 113 | { 114 | return tx.gasprice; 115 | } 116 | 117 | /// Gets current payment value. 118 | function _getMsgValue() 119 | internal view 120 | virtual override 121 | returns (uint256) 122 | { 123 | return msg.value; 124 | } 125 | 126 | /// Transfers ETHs to given address. 127 | /// @param _to Recipient address. 128 | /// @param _amount Amount of ETHs to transfer. 129 | function __safeTransferTo(address payable _to, uint256 _amount) 130 | internal 131 | virtual override 132 | { 133 | payable(_to).transfer(_amount); 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /contracts/data/WitnetOracleDataLib.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity >=0.7.0 <0.9.0; 4 | 5 | import "../WitnetRequestBytecodes.sol"; 6 | import "../libs/WitnetV2.sol"; 7 | 8 | /// @title Witnet Request Board base data model library 9 | /// @author The Witnet Foundation. 10 | library WitnetOracleDataLib { 11 | 12 | using WitnetV2 for WitnetV2.Request; 13 | 14 | bytes32 internal constant _WITNET_ORACLE_DATA_SLOTHASH = 15 | /* keccak256("io.witnet.boards.data") */ 16 | 0xf595240b351bc8f951c2f53b26f4e78c32cb62122cf76c19b7fdda7d4968e183; 17 | 18 | struct Storage { 19 | uint256 nonce; 20 | mapping (uint => WitnetV2.Query) queries; 21 | mapping (address => bool) reporters; 22 | } 23 | 24 | // ================================================================================================================ 25 | // --- Internal functions ----------------------------------------------------------------------------------------- 26 | 27 | /// Returns storage pointer to contents of 'WitnetBoardState' struct. 28 | function data() internal pure returns (Storage storage _ptr) 29 | { 30 | assembly { 31 | _ptr.slot := _WITNET_ORACLE_DATA_SLOTHASH 32 | } 33 | } 34 | 35 | function isReporter(address addr) internal view returns (bool) { 36 | return data().reporters[addr]; 37 | } 38 | 39 | /// Gets query storage by query id. 40 | function seekQuery(uint256 _queryId) internal view returns (WitnetV2.Query storage) { 41 | return data().queries[_queryId]; 42 | } 43 | 44 | /// Gets the Witnet.Request part of a given query. 45 | function seekQueryRequest(uint256 _queryId) internal view returns (WitnetV2.Request storage) { 46 | return data().queries[_queryId].request; 47 | } 48 | 49 | /// Gets the Witnet.Result part of a given query. 50 | function seekQueryResponse(uint256 _queryId) internal view returns (WitnetV2.Response storage) { 51 | return data().queries[_queryId].response; 52 | } 53 | 54 | function seekQueryStatus(uint256 queryId) internal view returns (WitnetV2.QueryStatus) { 55 | WitnetV2.Query storage __query = data().queries[queryId]; 56 | if (__query.response.resultTimestamp != 0) { 57 | if (block.number >= __query.response.finality) { 58 | return WitnetV2.QueryStatus.Finalized; 59 | } else { 60 | return WitnetV2.QueryStatus.Reported; 61 | } 62 | } else if (__query.request.requester != address(0)) { 63 | return WitnetV2.QueryStatus.Posted; 64 | } else { 65 | return WitnetV2.QueryStatus.Unknown; 66 | } 67 | } 68 | 69 | function seekQueryResponseStatus(uint256 queryId) internal view returns (WitnetV2.ResponseStatus) { 70 | WitnetV2.QueryStatus _queryStatus = seekQueryStatus(queryId); 71 | if (_queryStatus == WitnetV2.QueryStatus.Finalized) { 72 | bytes storage __cborValues = data().queries[queryId].response.resultCborBytes; 73 | if (__cborValues.length > 0) { 74 | // determine whether stored result is an error by peeking the first byte 75 | return (__cborValues[0] == bytes1(0xd8) 76 | ? WitnetV2.ResponseStatus.Error 77 | : WitnetV2.ResponseStatus.Ready 78 | ); 79 | } else { 80 | // the result is final but delivered to the requesting address 81 | return WitnetV2.ResponseStatus.Delivered; 82 | } 83 | } else if (_queryStatus == WitnetV2.QueryStatus.Posted) { 84 | return WitnetV2.ResponseStatus.Awaiting; 85 | } else if (_queryStatus == WitnetV2.QueryStatus.Reported) { 86 | return WitnetV2.ResponseStatus.Finalizing; 87 | } else { 88 | return WitnetV2.ResponseStatus.Void; 89 | } 90 | } 91 | 92 | // ================================================================================================================ 93 | // --- Public functions ------------------------------------------------------------------------------------------- 94 | 95 | function extractWitnetDataRequests(WitnetRequestBytecodes registry, uint256[] calldata queryIds) 96 | public view 97 | returns (bytes[] memory bytecodes) 98 | { 99 | bytecodes = new bytes[](queryIds.length); 100 | for (uint _ix = 0; _ix < queryIds.length; _ix ++) { 101 | if (seekQueryStatus(queryIds[_ix]) != WitnetV2.QueryStatus.Unknown) { 102 | WitnetV2.Request storage __request = data().queries[queryIds[_ix]].request; 103 | if (__request.witnetRAD != bytes32(0)) { 104 | bytecodes[_ix] = registry.bytecodeOf( 105 | __request.witnetRAD, 106 | __request.witnetSLA 107 | ); 108 | } else { 109 | bytecodes[_ix] = registry.bytecodeOf( 110 | __request.witnetBytecode, 111 | __request.witnetSLA 112 | ); 113 | } 114 | } 115 | } 116 | } 117 | 118 | function notInStatusRevertMessage(WitnetV2.QueryStatus self) public pure returns (string memory) { 119 | if (self == WitnetV2.QueryStatus.Posted) { 120 | return "query not in Posted status"; 121 | } else if (self == WitnetV2.QueryStatus.Reported) { 122 | return "query not in Reported status"; 123 | } else if (self == WitnetV2.QueryStatus.Finalized) { 124 | return "query not in Finalized status"; 125 | } else { 126 | return "bad mood"; 127 | } 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /contracts/data/WitnetPriceFeedsData.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity >=0.8.0 <0.9.0; 4 | 5 | /// @title WitnetFeeds data model. 6 | /// @author The Witnet Foundation. 7 | abstract contract WitnetPriceFeedsData { 8 | 9 | bytes32 private constant _WITNET_FEEDS_DATA_SLOTHASH = 10 | /* keccak256("io.witnet.feeds.data") */ 11 | 0xe36ea87c48340f2c23c9e1c9f72f5c5165184e75683a4d2a19148e5964c1d1ff; 12 | 13 | struct Storage { 14 | bytes32 reserved; 15 | bytes4[] ids; 16 | mapping (bytes4 => Record) records; 17 | } 18 | 19 | struct Record { 20 | string caption; 21 | uint8 decimals; 22 | uint256 index; 23 | uint256 lastValidQueryId; 24 | uint256 latestUpdateQueryId; 25 | bytes32 radHash; 26 | address solver; // logic contract address for reducing values on routed feeds. 27 | int256 solverReductor; // as to reduce resulting number of decimals on routed feeds. 28 | bytes32 solverDepsFlag; // as to store ids of up to 8 depending feeds. 29 | } 30 | 31 | // ================================================================================================ 32 | // --- Internal functions ------------------------------------------------------------------------- 33 | 34 | /// @notice Returns storage pointer to where Storage data is located. 35 | function __storage() 36 | internal pure 37 | returns (Storage storage _ptr) 38 | { 39 | assembly { 40 | _ptr.slot := _WITNET_FEEDS_DATA_SLOTHASH 41 | } 42 | } 43 | 44 | /// @notice Returns storage pointer to where Record for given feedId is located. 45 | function __records_(bytes4 feedId) internal view returns (Record storage) { 46 | return __storage().records[feedId]; 47 | } 48 | 49 | /// @notice Returns array of feed ids from which given feed's value depends. 50 | /// @dev Returns empty array on either unsupported or not-routed feeds. 51 | /// @dev The maximum number of dependencies is hard-limited to 8, as to limit number 52 | /// @dev of SSTORE operations (`__storage().records[feedId].solverDepsFlag`), 53 | /// @dev no matter the actual number of depending feeds involved. 54 | function _depsOf(bytes4 feedId) internal view returns (bytes4[] memory _deps) { 55 | bytes32 _solverDepsFlag = __storage().records[feedId].solverDepsFlag; 56 | _deps = new bytes4[](8); 57 | uint _len; 58 | for (_len = 0; _len < 8; _len ++) { 59 | _deps[_len] = bytes4(_solverDepsFlag); 60 | if (_deps[_len] == 0) { 61 | break; 62 | } else { 63 | _solverDepsFlag <<= 32; 64 | } 65 | } 66 | assembly { 67 | // reset length to actual number of dependencies: 68 | mstore(_deps, _len) 69 | } 70 | } 71 | } -------------------------------------------------------------------------------- /contracts/data/WitnetRequestBytecodesData.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity >=0.8.0 <0.9.0; 4 | 5 | import "../libs/WitnetV2.sol"; 6 | 7 | /// @title Witnet Request Board base data model. 8 | /// @author The Witnet Foundation. 9 | abstract contract WitnetRequestBytecodesData { 10 | 11 | bytes32 private constant _WITNET_BYTECODES_DATA_SLOTHASH = 12 | /* keccak256("io.witnet.bytecodes.data") */ 13 | 0x673359bdfd0124f9962355e7aed2d07d989b0d4bc4cbe2c94c295e0f81427dec; 14 | 15 | struct Storage { 16 | address base; 17 | address owner; 18 | address pendingOwner; 19 | 20 | Database db; 21 | uint256 totalDataProviders; 22 | // ... 23 | } 24 | 25 | struct DataProvider { 26 | string authority; 27 | uint256 totalEndpoints; 28 | mapping (uint256 => bytes32) endpoints; 29 | } 30 | 31 | struct DataRequest { 32 | string[][] args; 33 | bytes32 aggregator; 34 | bytes32 radHash; 35 | Witnet.RadonDataTypes resultDataType; 36 | uint16 resultMaxSize; 37 | bytes32[] retrievals; 38 | bytes32 tally; 39 | } 40 | 41 | struct Database { 42 | mapping (uint256 => DataProvider) providers; 43 | mapping (bytes32 => uint256) providersIndex; 44 | 45 | mapping (bytes32 => Witnet.RadonReducer) reducers; 46 | mapping (bytes32 => Witnet.RadonRetrieval) retrievals; 47 | mapping (bytes32 => DataRequest) requests; 48 | mapping (bytes32 => bytes32) rads; 49 | mapping (bytes32 => bytes) radsBytecode; 50 | mapping (bytes32 => bytes) _slasBytecode; 51 | } 52 | 53 | constructor() { 54 | // auto-initialize upon deployment 55 | __bytecodes().base = address(this); 56 | } 57 | 58 | 59 | // ================================================================================================================ 60 | // --- Internal state-modifying functions ------------------------------------------------------------------------- 61 | 62 | /// @dev Returns storage pointer to contents of 'Storage' struct. 63 | function __bytecodes() 64 | internal pure 65 | returns (Storage storage _ptr) 66 | { 67 | assembly { 68 | _ptr.slot := _WITNET_BYTECODES_DATA_SLOTHASH 69 | } 70 | } 71 | 72 | /// @dev Returns storage pointer to contents of 'Database' struct. 73 | function __database() 74 | internal view 75 | returns (Database storage _ptr) 76 | { 77 | return __bytecodes().db; 78 | } 79 | 80 | function __requests(bytes32 _radHash) 81 | internal view 82 | returns (DataRequest storage _ptr) 83 | { 84 | return __database().requests[_radHash]; 85 | } 86 | } -------------------------------------------------------------------------------- /contracts/data/WitnetRequestFactoryData.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity >=0.7.0 <0.9.0; 4 | pragma experimental ABIEncoderV2; 5 | 6 | import "../WitnetRequest.sol"; 7 | 8 | contract WitnetRequestFactoryData { 9 | 10 | bytes32 internal constant _WITNET_REQUEST_SLOTHASH = 11 | /* keccak256("io.witnet.data.request") */ 12 | 0xbf9e297db5f64cdb81cd821e7ad085f56008e0c6100f4ebf5e41ef6649322034; 13 | 14 | bytes32 internal constant _WITNET_REQUEST_FACTORY_SLOTHASH = 15 | /* keccak256("io.witnet.data.request.factory") */ 16 | 0xfaf45a8ecd300851b566566df52ca7611b7a56d24a3449b86f4e21c71638e642; 17 | 18 | bytes32 internal constant _WITNET_REQUEST_TEMPLATE_SLOTHASH = 19 | /* keccak256("io.witnet.data.request.template") */ 20 | 0x50402db987be01ecf619cd3fb022cf52f861d188e7b779dd032a62d082276afb; 21 | 22 | struct Slot { 23 | address owner; 24 | address pendingOwner; 25 | } 26 | 27 | struct WitnetRequestSlot { 28 | /// Array of string arguments passed upon initialization. 29 | string[][] args; 30 | /// Radon RAD hash. 31 | bytes32 radHash; 32 | /// Parent WitnetRequestTemplate contract. 33 | WitnetRequestTemplate template; 34 | } 35 | 36 | struct WitnetRequestTemplateSlot { 37 | /// @notice Aggregator reducer hash. 38 | bytes32 aggregator; 39 | /// @notice Parent IWitnetRequestFactory from which this template was built. 40 | WitnetRequestFactory factory; 41 | /// Whether any of the sources is parameterized. 42 | bool parameterized; 43 | /// @notice Tally reducer hash. 44 | bytes32 tally; 45 | /// @notice Array of retrievals hashes passed upon construction. 46 | bytes32[] retrievals; 47 | /// @notice Result data type. 48 | Witnet.RadonDataTypes resultDataType; 49 | /// @notice Result max size or rank (if variable type). 50 | uint16 resultDataMaxSize; 51 | } 52 | 53 | function __witnetRequestFactory() 54 | internal pure 55 | returns (Slot storage ptr) 56 | { 57 | assembly { 58 | ptr.slot := _WITNET_REQUEST_FACTORY_SLOTHASH 59 | } 60 | } 61 | 62 | function __witnetRequest() 63 | internal pure 64 | returns (WitnetRequestSlot storage ptr) 65 | { 66 | assembly { 67 | ptr.slot := _WITNET_REQUEST_SLOTHASH 68 | } 69 | } 70 | 71 | function __witnetRequestTemplate() 72 | internal pure 73 | returns (WitnetRequestTemplateSlot storage ptr) 74 | { 75 | assembly { 76 | ptr.slot := _WITNET_REQUEST_TEMPLATE_SLOTHASH 77 | } 78 | } 79 | } -------------------------------------------------------------------------------- /contracts/interfaces/IFeeds.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity >=0.8.0 <0.9.0; 4 | 5 | interface IFeeds { 6 | function footprint() external view returns (bytes4); 7 | function hash(string calldata caption) external pure returns (bytes4); 8 | function lookupCaption(bytes4) external view returns (string memory); 9 | function supportedFeeds() external view returns (bytes4[] memory, string[] memory, bytes32[] memory); 10 | function supportsCaption(string calldata) external view returns (bool); 11 | function totalFeeds() external view returns (uint256); 12 | } -------------------------------------------------------------------------------- /contracts/interfaces/IWitnetConsumer.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.0; 3 | 4 | import "../libs/Witnet.sol"; 5 | 6 | interface IWitnetConsumer { 7 | 8 | /// @notice Method to be called from the WitnetOracle contract as soon as the given Witnet `queryId` 9 | /// @notice gets reported, if reported with no errors. 10 | /// @dev It should revert if called from any other address different to the WitnetOracle being used 11 | /// @dev by the WitnetConsumer contract. 12 | /// @param witnetQueryId The unique identifier of the Witnet query being reported. 13 | /// @param witnetResultTallyHash Hash of the commit/reveal witnessing act that took place in the Witnet blockahin. 14 | /// @param witnetResultTimestamp Timestamp at which the reported value was captured by the Witnet blockchain. 15 | /// @param witnetEvmFinalityBlock EVM block at which the provided data can be considered to be final. 16 | /// @param witnetResultCborValue The CBOR-encoded resulting value of the Witnet query being reported. 17 | function reportWitnetQueryResult( 18 | uint256 witnetQueryId, 19 | uint64 witnetResultTimestamp, 20 | bytes32 witnetResultTallyHash, 21 | uint256 witnetEvmFinalityBlock, 22 | WitnetCBOR.CBOR calldata witnetResultCborValue 23 | ) external; 24 | 25 | /// @notice Method to be called from the WitnetOracle contract as soon as the given Witnet `queryId` 26 | /// @notice gets reported, if reported WITH errors. 27 | /// @dev It should revert if called from any other address different to the WitnetOracle being used 28 | /// @dev by the WitnetConsumer contract. 29 | /// @param witnetQueryId The unique identifier of the Witnet query being reported. 30 | /// @param witnetResultTallyHash Hash of the commit/reveal witnessing act that took place in the Witnet blockahin. 31 | /// @param witnetResultTimestamp Timestamp at which the reported value was captured by the Witnet blockchain. 32 | /// @param witnetEvmFinalityBlock EVM block at which the provided data can be considered to be final. 33 | /// @param errorCode The error code enum identifying the error produced during resolution on the Witnet blockchain. 34 | /// @param errorArgs Error arguments, if any. An empty buffer is to be passed if no error arguments apply. 35 | function reportWitnetQueryError( 36 | uint256 witnetQueryId, 37 | uint64 witnetResultTimestamp, 38 | bytes32 witnetResultTallyHash, 39 | uint256 witnetEvmFinalityBlock, 40 | Witnet.ResultErrorCodes errorCode, 41 | WitnetCBOR.CBOR calldata errorArgs 42 | ) external; 43 | 44 | /// @notice Determines if Witnet queries can be reported from given address. 45 | /// @dev In practice, must only be true on the WitnetOracle address that's being used by 46 | /// @dev the WitnetConsumer to post queries. 47 | function reportableFrom(address) external view returns (bool); 48 | } -------------------------------------------------------------------------------- /contracts/interfaces/IWitnetFeeds.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity >=0.8.0 <0.9.0; 4 | 5 | import "../WitnetOracle.sol"; 6 | import "../WitnetRequestBytecodes.sol"; 7 | 8 | interface IWitnetFeeds { 9 | 10 | event WitnetFeedDeleted(bytes4 feedId); 11 | event WitnetFeedSettled(bytes4 feedId, bytes32 radHash); 12 | event WitnetFeedSolverSettled(bytes4 feedId, address solver); 13 | event WitnetRadonSLA(WitnetV2.RadonSLA sla); 14 | 15 | event WitnetFeedUpdateRequested( 16 | address indexed origin, 17 | bytes4 indexed feedId, 18 | uint256 witnetQueryId, 19 | uint256 witnetQueryEvmReward, 20 | WitnetV2.RadonSLA witnetQuerySLA 21 | ); 22 | 23 | event WitnetFeedUpdateRequested( 24 | address indexed origin, 25 | bytes4 indexed feedId, 26 | uint256 witnetQueryId, 27 | uint256 witnetQueryReward 28 | ); 29 | 30 | function dataType() external view returns (Witnet.RadonDataTypes); 31 | function prefix() external view returns (string memory); 32 | function registry() external view returns (WitnetRequestBytecodes); 33 | function witnet() external view returns (WitnetOracle); 34 | 35 | function defaultRadonSLA() external view returns (Witnet.RadonSLA memory); 36 | function estimateUpdateBaseFee(uint256 evmGasPrice) external view returns (uint); 37 | 38 | function lastValidQueryId(bytes4 feedId) external view returns (uint256); 39 | function lastValidResponse(bytes4 feedId) external view returns (WitnetV2.Response memory); 40 | 41 | function latestUpdateQueryId(bytes4 feedId) external view returns (uint256); 42 | function latestUpdateRequest(bytes4 feedId) external view returns (WitnetV2.Request memory); 43 | function latestUpdateResponse(bytes4 feedId) external view returns (WitnetV2.Response memory); 44 | function latestUpdateResponseStatus(bytes4 feedId) external view returns (WitnetV2.ResponseStatus); 45 | function latestUpdateResultError(bytes4 feedId) external view returns (Witnet.ResultError memory); 46 | 47 | function lookupWitnetBytecode(bytes4 feedId) external view returns (bytes memory); 48 | function lookupWitnetRadHash(bytes4 feedId) external view returns (bytes32); 49 | function lookupWitnetRetrievals(bytes4 feedId) external view returns (Witnet.RadonRetrieval[] memory); 50 | 51 | function requestUpdate(bytes4 feedId) external payable returns (uint256 usedFunds); 52 | function requestUpdate(bytes4 feedId, WitnetV2.RadonSLA calldata updateSLA) external payable returns (uint256 usedFunds); 53 | } -------------------------------------------------------------------------------- /contracts/interfaces/IWitnetFeedsAdmin.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity >=0.8.0 <0.9.0; 4 | 5 | import "../libs/WitnetV2.sol"; 6 | import "../WitnetRequest.sol"; 7 | 8 | interface IWitnetFeedsAdmin { 9 | function acceptOwnership() external; 10 | function baseFeeOverheadPercentage() external view returns (uint16); 11 | function deleteFeed(string calldata caption) external; 12 | function deleteFeeds() external; 13 | function owner() external view returns (address); 14 | function pendingOwner() external returns (address); 15 | function settleBaseFeeOverheadPercentage(uint16) external; 16 | function settleDefaultRadonSLA(WitnetV2.RadonSLA calldata) external; 17 | function settleFeedRequest(string calldata caption, bytes32 radHash) external; 18 | function settleFeedRequest(string calldata caption, WitnetRequest request) external; 19 | function settleFeedRequest(string calldata caption, WitnetRequestTemplate template, string[][] calldata) external; 20 | function settleFeedSolver (string calldata caption, address solver, string[] calldata deps) external; 21 | function transferOwnership(address) external; 22 | } -------------------------------------------------------------------------------- /contracts/interfaces/IWitnetOracleEvents.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.7.0 <0.9.0; 3 | 4 | import "../libs/WitnetV2.sol"; 5 | 6 | interface IWitnetOracleEvents { 7 | 8 | /// Emitted every time a new query containing some verified data request is posted to the WRB. 9 | event WitnetQuery( 10 | uint256 id, 11 | uint256 evmReward, 12 | WitnetV2.RadonSLA witnetSLA 13 | ); 14 | 15 | /// Emitted when a query with no callback gets reported into the WRB. 16 | event WitnetQueryResponse( 17 | uint256 id, 18 | uint256 evmGasPrice 19 | ); 20 | 21 | /// Emitted when a query with a callback gets successfully reported into the WRB. 22 | event WitnetQueryResponseDelivered( 23 | uint256 id, 24 | uint256 evmGasPrice, 25 | uint256 evmCallbackGas 26 | ); 27 | 28 | /// Emitted when a query with a callback cannot get reported into the WRB. 29 | event WitnetQueryResponseDeliveryFailed( 30 | uint256 id, 31 | bytes resultCborBytes, 32 | uint256 evmGasPrice, 33 | uint256 evmCallbackActualGas, 34 | string evmCallbackRevertReason 35 | ); 36 | 37 | /// Emitted when the reward of some not-yet reported query is upgraded. 38 | event WitnetQueryRewardUpgraded( 39 | uint256 id, 40 | uint256 evmReward 41 | ); 42 | 43 | } 44 | -------------------------------------------------------------------------------- /contracts/interfaces/IWitnetOracleReporter.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity >=0.7.0 <0.9.0; 4 | 5 | import "../libs/WitnetV2.sol"; 6 | 7 | /// @title The Witnet Request Board Reporter interface. 8 | /// @author The Witnet Foundation. 9 | interface IWitnetOracleReporter { 10 | 11 | /// @notice Estimates the actual earnings in WEI, that a reporter would get by reporting result to given query, 12 | /// @notice based on the gas price of the calling transaction. Data requesters should consider upgrading the reward on 13 | /// @notice queries providing no actual earnings. 14 | function estimateReportEarnings( 15 | uint256[] calldata witnetQueryIds, 16 | bytes calldata reportTxMsgData, 17 | uint256 reportTxGasPrice, 18 | uint256 nanoWitPrice 19 | ) external view returns (uint256, uint256); 20 | 21 | /// @notice Retrieves the Witnet Data Request bytecodes and SLAs of previously posted queries. 22 | /// @dev Returns empty buffer if the query does not exist. 23 | /// @param queryIds Query identifiers. 24 | function extractWitnetDataRequests(uint256[] calldata queryIds) 25 | external view returns (bytes[] memory drBytecodes); 26 | 27 | /// @notice Reports the Witnet-provided result to a previously posted request. 28 | /// @dev Will assume `block.timestamp` as the timestamp at which the request was solved. 29 | /// @dev Fails if: 30 | /// @dev - the `_witnetQueryId` is not in 'Posted' status. 31 | /// @dev - provided `_tallyHash` is zero; 32 | /// @dev - length of provided `_result` is zero. 33 | /// @param witnetQueryId The unique identifier of the data request. 34 | /// @param witnetQueryResultTallyHash The hash of the corresponding data request transaction in Witnet. 35 | /// @param witnetQueryResultCborBytes The result itself as bytes. 36 | function reportResult( 37 | uint256 witnetQueryId, 38 | bytes32 witnetQueryResultTallyHash, 39 | bytes calldata witnetQueryResultCborBytes 40 | ) external returns (uint256); 41 | 42 | /// @notice Reports the Witnet-provided result to a previously posted request. 43 | /// @dev Fails if: 44 | /// @dev - called from unauthorized address; 45 | /// @dev - the `_witnetQueryId` is not in 'Posted' status. 46 | /// @dev - provided `_tallyHash` is zero; 47 | /// @dev - length of provided `_result` is zero. 48 | /// @param witnetQueryId The unique query identifier 49 | /// @param witnetQueryResultTimestamp The timestamp of the solving tally transaction in Witnet. 50 | /// @param witnetQueryResultTallyHash The hash of the corresponding data request transaction in Witnet. 51 | /// @param witnetQueryResultCborBytes The result itself as bytes. 52 | function reportResult( 53 | uint256 witnetQueryId, 54 | uint32 witnetQueryResultTimestamp, 55 | bytes32 witnetQueryResultTallyHash, 56 | bytes calldata witnetQueryResultCborBytes 57 | ) external returns (uint256); 58 | 59 | /// @notice Reports Witnet-provided results to multiple requests within a single EVM tx. 60 | /// @notice Emits either a WitnetQueryResponse* or a BatchReportError event per batched report. 61 | /// @dev Fails only if called from unauthorized address. 62 | /// @param _batchResults Array of BatchResult structs, every one containing: 63 | /// - unique query identifier; 64 | /// - timestamp of the solving tally txs in Witnet. If zero is provided, EVM-timestamp will be used instead; 65 | /// - hash of the corresponding data request tx at the Witnet side-chain level; 66 | /// - data request result in raw bytes. 67 | function reportResultBatch(BatchResult[] calldata _batchResults) external returns (uint256); 68 | 69 | struct BatchResult { 70 | uint256 queryId; 71 | uint32 queryResultTimestamp; 72 | bytes32 queryResultTallyHash; 73 | bytes queryResultCborBytes; 74 | } 75 | 76 | event BatchReportError(uint256 queryId, string reason); 77 | } 78 | -------------------------------------------------------------------------------- /contracts/interfaces/IWitnetPriceFeeds.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity >=0.8.0 <0.9.0; 4 | 5 | import "./IWitnetPriceSolver.sol"; 6 | 7 | interface IWitnetPriceFeeds { 8 | /// ====================================================================================================== 9 | /// --- IFeeds extension --------------------------------------------------------------------------------- 10 | 11 | function lookupDecimals(bytes4 feedId) external view returns (uint8); 12 | function lookupPriceSolver(bytes4 feedId) external view returns ( 13 | IWitnetPriceSolver solverAddress, 14 | string[] memory solverDeps 15 | ); 16 | 17 | /// ====================================================================================================== 18 | /// --- IWitnetFeeds extension --------------------------------------------------------------------------- 19 | 20 | function latestPrice(bytes4 feedId) external view returns (IWitnetPriceSolver.Price memory); 21 | function latestPrices(bytes4[] calldata feedIds) external view returns (IWitnetPriceSolver.Price[] memory); 22 | } -------------------------------------------------------------------------------- /contracts/interfaces/IWitnetPriceSolver.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity >=0.8.0 <0.9.0; 4 | 5 | import "../libs/WitnetV2.sol"; 6 | 7 | interface IWitnetPriceSolver { 8 | struct Price { 9 | uint value; 10 | uint timestamp; 11 | bytes32 tallyHash; 12 | WitnetV2.ResponseStatus status; 13 | } 14 | function class() external pure returns (string memory); 15 | function delegator() external view returns (address); 16 | function solve(bytes4 feedId) external view returns (Price memory); 17 | function specs() external pure returns (bytes4); 18 | function validate(bytes4 feedId, string[] calldata initdata) external; 19 | } -------------------------------------------------------------------------------- /contracts/interfaces/IWitnetPriceSolverDeployer.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity >=0.8.0 <0.9.0; 4 | 5 | interface IWitnetPriceSolverDeployer { 6 | event WitnetPriceSolverDeployed(address solver, bytes32 codehash, bytes constructorParams); 7 | function deployPriceSolver(bytes calldata initcode, bytes calldata additionalParams) external returns (address); 8 | function determinePriceSolverAddress(bytes calldata initcode, bytes calldata additionalParams) external view returns (address); 9 | } -------------------------------------------------------------------------------- /contracts/interfaces/IWitnetRandomnessAdmin.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity >=0.8.0 <0.9.0; 4 | 5 | import "../libs/WitnetV2.sol"; 6 | 7 | interface IWitnetRandomnessAdmin { 8 | function acceptOwnership() external; 9 | function baseFeeOverheadPercentage() external view returns (uint16); 10 | function owner() external view returns (address); 11 | function pendingOwner() external returns (address); 12 | function transferOwnership(address) external; 13 | function settleBaseFeeOverheadPercentage(uint16) external; 14 | function settleWitnetQuerySLA(WitnetV2.RadonSLA calldata) external; 15 | } -------------------------------------------------------------------------------- /contracts/interfaces/IWitnetRandomnessEvents.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity >=0.7.0 <0.9.0; 4 | pragma experimental ABIEncoderV2; 5 | 6 | import "../libs/WitnetV2.sol"; 7 | 8 | /// @title The Witnet Randomness generator interface. 9 | /// @author Witnet Foundation. 10 | interface IWitnetRandomnessEvents { 11 | event Randomizing( 12 | uint256 blockNumber, 13 | uint256 evmTxGasPrice, 14 | uint256 evmRandomizeFee, 15 | uint256 witnetQueryId, 16 | WitnetV2.RadonSLA witnetQuerySLA 17 | ); 18 | } 19 | -------------------------------------------------------------------------------- /contracts/interfaces/IWitnetRequestBoardAdmin.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity >=0.7.0 <0.9.0; 4 | 5 | /// @title Witnet Request Board basic administration interface. 6 | /// @author The Witnet Foundation. 7 | interface IWitnetRequestBoardAdmin { 8 | 9 | event OwnershipTransferred(address indexed from, address indexed to); 10 | 11 | /// Gets admin/owner address. 12 | function owner() external view returns (address); 13 | 14 | /// Transfers ownership. 15 | function transferOwnership(address) external; 16 | 17 | /// Accepts ownership. 18 | function acceptOwnership() external; 19 | } 20 | -------------------------------------------------------------------------------- /contracts/interfaces/IWitnetRequestBoardAdminACLs.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity >=0.7.0 <0.9.0; 4 | 5 | /// @title Witnet Request Board ACLs administration interface. 6 | /// @author The Witnet Foundation. 7 | interface IWitnetRequestBoardAdminACLs { 8 | 9 | event ReportersSet(address[] reporters); 10 | event ReportersUnset(address[] reporters); 11 | 12 | /// Tells whether given address is included in the active reporters control list. 13 | function isReporter(address) external view returns (bool); 14 | 15 | /// Adds given addresses to the active reporters control list. 16 | /// @dev Can only be called from the owner address. 17 | /// @dev Emits the `ReportersSet` event. 18 | function setReporters(address[] calldata reporters) external; 19 | 20 | /// Removes given addresses from the active reporters control list. 21 | /// @dev Can only be called from the owner address. 22 | /// @dev Emits the `ReportersUnset` event. 23 | function unsetReporters(address[] calldata reporters) external; 24 | } 25 | -------------------------------------------------------------------------------- /contracts/interfaces/IWitnetRequestBytecodes.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.8.0 <0.9.0; 3 | 4 | import "../libs/WitnetV2.sol"; 5 | 6 | interface IWitnetRequestBytecodes { 7 | 8 | error UnknownRadonRetrieval(bytes32 hash); 9 | error UnknownRadonReducer(bytes32 hash); 10 | error UnknownRadonRequest(bytes32 hash); 11 | 12 | event NewDataProvider(uint256 index); 13 | event NewRadonRetrievalHash(bytes32 hash); 14 | event NewRadonReducerHash(bytes32 hash); 15 | event NewRadHash(bytes32 hash); 16 | 17 | function bytecodeOf(bytes32 radHash) external view returns (bytes memory); 18 | function bytecodeOf(bytes32 radHash, WitnetV2.RadonSLA calldata sla) external view returns (bytes memory); 19 | function bytecodeOf(bytes calldata radBytecode, WitnetV2.RadonSLA calldata sla) external view returns (bytes memory); 20 | 21 | function hashOf(bytes calldata) external view returns (bytes32); 22 | 23 | function lookupDataProvider(uint256 index) external view returns (string memory, uint); 24 | function lookupDataProviderIndex(string calldata authority) external view returns (uint); 25 | function lookupDataProviderSources(uint256 index, uint256 offset, uint256 length) external view returns (bytes32[] memory); 26 | 27 | function lookupRadonReducer(bytes32 hash) external view returns (Witnet.RadonReducer memory); 28 | 29 | function lookupRadonRetrieval(bytes32 hash) external view returns (Witnet.RadonRetrieval memory); 30 | function lookupRadonRetrievalArgsCount(bytes32 hash) external view returns (uint8); 31 | function lookupRadonRetrievalResultDataType(bytes32 hash) external view returns (Witnet.RadonDataTypes); 32 | 33 | function lookupRadonRequestAggregator(bytes32 radHash) external view returns (Witnet.RadonReducer memory); 34 | function lookupRadonRequestResultMaxSize(bytes32 radHash) external view returns (uint16); 35 | function lookupRadonRequestResultDataType(bytes32 radHash) external view returns (Witnet.RadonDataTypes); 36 | function lookupRadonRequestSources(bytes32 radHash) external view returns (bytes32[] memory); 37 | function lookupRadonRequestSourcesCount(bytes32 radHash) external view returns (uint); 38 | function lookupRadonRequestTally(bytes32 radHash) external view returns (Witnet.RadonReducer memory); 39 | 40 | function verifyRadonRetrieval( 41 | Witnet.RadonDataRequestMethods requestMethod, 42 | string calldata requestURL, 43 | string calldata requestBody, 44 | string[2][] calldata requestHeaders, 45 | bytes calldata requestRadonScript 46 | ) external returns (bytes32 hash); 47 | 48 | function verifyRadonReducer(Witnet.RadonReducer calldata reducer) 49 | external returns (bytes32 hash); 50 | 51 | function verifyRadonRequest( 52 | bytes32[] calldata sources, 53 | bytes32 aggregator, 54 | bytes32 tally, 55 | uint16 resultMaxSize, 56 | string[][] calldata args 57 | ) external returns (bytes32 radHash); 58 | 59 | function totalDataProviders() external view returns (uint); 60 | } 61 | -------------------------------------------------------------------------------- /contracts/interfaces/IWitnetRequestFactory.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity >=0.7.0 <0.9.0; 4 | 5 | interface IWitnetRequestFactory { 6 | 7 | event WitnetRequestTemplateBuilt(address template, bool parameterized); 8 | 9 | function buildRequestTemplate( 10 | bytes32[] memory sourcesIds, 11 | bytes32 aggregatorId, 12 | bytes32 tallyId, 13 | uint16 resultDataMaxSize 14 | ) external returns (address template); 15 | 16 | } -------------------------------------------------------------------------------- /contracts/libs/Create3.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0-only 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | /// @notice Deploy to deterministic addresses without an initcode factor. 6 | /// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/CREATE3.sol) 7 | /// @author 0xSequence (https://github.com/0xSequence/create3/blob/master/contracts/Create3.sol) 8 | 9 | library Create3 { 10 | 11 | //--------------------------------------------------------------------------------// 12 | // Opcode | Opcode + Arguments | Description | Stack View // 13 | //--------------------------------------------------------------------------------// 14 | // 0x36 | 0x36 | CALLDATASIZE | size // 15 | // 0x3d | 0x3d | RETURNDATASIZE | 0 size // 16 | // 0x3d | 0x3d | RETURNDATASIZE | 0 0 size // 17 | // 0x37 | 0x37 | CALLDATACOPY | // 18 | // 0x36 | 0x36 | CALLDATASIZE | size // 19 | // 0x3d | 0x3d | RETURNDATASIZE | 0 size // 20 | // 0x34 | 0x34 | CALLVALUE | value 0 size // 21 | // 0xf0 | 0xf0 | CREATE | newContract // 22 | //--------------------------------------------------------------------------------// 23 | // Opcode | Opcode + Arguments | Description | Stack View // 24 | //--------------------------------------------------------------------------------// 25 | // 0x67 | 0x67XXXXXXXXXXXXXXXX | PUSH8 bytecode | bytecode // 26 | // 0x3d | 0x3d | RETURNDATASIZE | 0 bytecode // 27 | // 0x52 | 0x52 | MSTORE | // 28 | // 0x60 | 0x6008 | PUSH1 08 | 8 // 29 | // 0x60 | 0x6018 | PUSH1 18 | 24 8 // 30 | // 0xf3 | 0xf3 | RETURN | // 31 | //--------------------------------------------------------------------------------// 32 | 33 | bytes internal constant CREATE3_FACTORY_BYTECODE = hex"67_36_3d_3d_37_36_3d_34_f0_3d_52_60_08_60_18_f3"; 34 | bytes32 internal constant CREATE3_FACTORY_CODEHASH = keccak256(CREATE3_FACTORY_BYTECODE); 35 | 36 | /// @notice Creates a new contract with given `_creationCode` and `_salt` 37 | /// @param _salt Salt of the contract creation, resulting address will be derivated from this value only 38 | /// @param _creationCode Creation code (constructor) of the contract to be deployed, this value doesn't affect the resulting address 39 | /// @return addr of the deployed contract, reverts on error 40 | function deploy(bytes32 _salt, bytes memory _creationCode) 41 | internal 42 | returns (address) 43 | { 44 | return deploy(_salt, _creationCode, 0); 45 | } 46 | 47 | /// @notice Creates a new contract with given `_creationCode`, `_salt` and `_value`. 48 | /// @param _salt Salt of the contract creation, resulting address will be derivated from this value only 49 | /// @param _creationCode Creation code (constructor) of the contract to be deployed, this value doesn't affect the resulting address 50 | /// @param _value In WEI of ETH to be forwarded to child contract 51 | /// @return _deployed The address of the deployed contract. 52 | function deploy(bytes32 _salt, bytes memory _creationCode, uint256 _value) 53 | internal 54 | returns (address _deployed) 55 | { 56 | // Get target final address 57 | _deployed = determineAddr(_salt); 58 | if (_deployed.code.length != 0) revert("Create3: target already exists"); 59 | 60 | // Create factory 61 | address _factory; 62 | bytes memory _factoryBytecode = CREATE3_FACTORY_BYTECODE; 63 | /// @solidity memory-safe-assembly 64 | assembly { 65 | // Deploy a factory contract with our pre-made bytecode via CREATE2. 66 | // We start 32 bytes into the code to avoid copying the byte length. 67 | _factory := create2(0, add(_factoryBytecode, 32), mload(_factoryBytecode), _salt) 68 | } 69 | require(_factory != address(0), "Create3: error creating factory"); 70 | 71 | // Use factory to deploy target 72 | (bool _success, ) = _factory.call{value: _value}(_creationCode); 73 | require(_success && _deployed.code.length != 0, "Create3: error creating target"); 74 | } 75 | 76 | /// @notice Computes the resulting address of a contract deployed using address(this) and the given `_salt` 77 | /// @param _salt Salt of the contract creation, resulting address will be derivated from this value only 78 | /// @return addr of the deployed contract, reverts on error 79 | /// @dev The address creation formula is: keccak256(rlp([keccak256(0xff ++ address(this) ++ _salt ++ keccak256(childBytecode))[12:], 0x01])) 80 | function determineAddr(bytes32 _salt) internal view returns (address) { 81 | address _factory = address( 82 | uint160( 83 | uint256( 84 | keccak256( 85 | abi.encodePacked( 86 | hex'ff', 87 | address(this), 88 | _salt, 89 | CREATE3_FACTORY_CODEHASH 90 | ) 91 | ) 92 | ) 93 | ) 94 | ); 95 | return address( 96 | uint160( 97 | uint256( 98 | keccak256( 99 | abi.encodePacked( 100 | // 0xd6 = 0xc0 (short RLP prefix) + 0x16 (length of: 0x94 ++ _factory ++ 0x01) 101 | // 0x94 = 0x80 + 0x14 (0x14 = the length of an address, 20 bytes, in hex) 102 | hex"d6_94", 103 | _factory, 104 | // _factory's nonce on which the target is created: 0x1 105 | hex"01" 106 | ) 107 | ) 108 | ) 109 | ) 110 | ); 111 | } 112 | 113 | } -------------------------------------------------------------------------------- /contracts/libs/WitnetPriceFeedsLib.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity >=0.7.0 <0.9.0; 4 | pragma experimental ABIEncoderV2; 5 | 6 | import "../interfaces/IWitnetPriceSolver.sol"; 7 | import "../interfaces/IWitnetPriceSolverDeployer.sol"; 8 | 9 | import "../libs/Slices.sol"; 10 | 11 | /// @title Ancillary deployable library for WitnetPriceFeeds. 12 | /// @dev Features: 13 | /// @dev - deployment of counter-factual IWitnetPriceSolver instances. 14 | /// @dev - validation of feed caption strings. 15 | /// @author The Witnet Foundation. 16 | library WitnetPriceFeedsLib { 17 | 18 | using Slices for string; 19 | using Slices for Slices.Slice; 20 | 21 | function deployPriceSolver( 22 | bytes calldata initcode, 23 | bytes calldata constructorParams 24 | ) 25 | external 26 | returns (address _solver) 27 | { 28 | _solver = determinePriceSolverAddress(initcode, constructorParams); 29 | if (_solver.code.length == 0) { 30 | bytes memory _bytecode = _completeInitCode(initcode, constructorParams); 31 | address _createdContract; 32 | assembly { 33 | _createdContract := create2( 34 | 0, 35 | add(_bytecode, 0x20), 36 | mload(_bytecode), 37 | 0 38 | ) 39 | } 40 | // assert(_solver == _createdContract); // fails on TEN chains 41 | _solver = _createdContract; 42 | require( 43 | IWitnetPriceSolver(_solver).specs() == type(IWitnetPriceSolver).interfaceId, 44 | "WitnetPriceFeedsLib: uncompliant solver implementation" 45 | ); 46 | } 47 | } 48 | 49 | function determinePriceSolverAddress( 50 | bytes calldata initcode, 51 | bytes calldata constructorParams 52 | ) 53 | public view 54 | returns (address) 55 | { 56 | return address( 57 | uint160(uint(keccak256( 58 | abi.encodePacked( 59 | bytes1(0xff), 60 | address(this), 61 | bytes32(0), 62 | keccak256(_completeInitCode(initcode, constructorParams)) 63 | ) 64 | ))) 65 | ); 66 | } 67 | 68 | function validateCaption(bytes32 prefix, string calldata caption) 69 | external pure 70 | returns (uint8) 71 | { 72 | require( 73 | bytes6(bytes(caption)) == bytes6(prefix), 74 | "WitnetPriceFeedsLib: bad caption prefix" 75 | ); 76 | Slices.Slice memory _caption = caption.toSlice(); 77 | Slices.Slice memory _delim = string("-").toSlice(); 78 | string[] memory _parts = new string[](_caption.count(_delim) + 1); 79 | for (uint _ix = 0; _ix < _parts.length; _ix ++) { 80 | _parts[_ix] = _caption.split(_delim).toString(); 81 | } 82 | (uint _decimals, bool _success) = Witnet.tryUint(_parts[_parts.length - 1]); 83 | require(_success, "WitnetPriceFeedsLib: bad decimals"); 84 | return uint8(_decimals); 85 | } 86 | 87 | function _completeInitCode(bytes calldata initcode, bytes calldata constructorParams) 88 | private pure 89 | returns (bytes memory) 90 | { 91 | return abi.encodePacked( 92 | initcode, 93 | constructorParams 94 | ); 95 | } 96 | 97 | } 98 | -------------------------------------------------------------------------------- /contracts/libs/WitnetV2.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity >=0.8.0 <0.9.0; 4 | 5 | import "./Witnet.sol"; 6 | 7 | library WitnetV2 { 8 | 9 | /// Struct containing both request and response data related to every query posted to the Witnet Request Board 10 | struct Query { 11 | Request request; 12 | Response response; 13 | } 14 | 15 | /// Possible status of a Witnet query. 16 | enum QueryStatus { 17 | Unknown, 18 | Posted, 19 | Reported, 20 | Finalized 21 | } 22 | 23 | /// Data kept in EVM-storage for every Request posted to the Witnet Request Board. 24 | struct Request { 25 | address requester; // EVM address from which the request was posted. 26 | uint24 gasCallback; // Max callback gas limit upon response, if a callback is required. 27 | uint72 evmReward; // EVM amount in wei eventually to be paid to the legit result reporter. 28 | bytes witnetBytecode; // Optional: Witnet Data Request bytecode to be solved by the Witnet blockchain. 29 | bytes32 witnetRAD; // Optional: Previously verified hash of the Witnet Data Request to be solved. 30 | WitnetV2.RadonSLA witnetSLA; // Minimum Service-Level parameters to be committed by the Witnet blockchain. 31 | } 32 | 33 | /// Response metadata and result as resolved by the Witnet blockchain. 34 | struct Response { 35 | address reporter; // EVM address from which the Data Request result was reported. 36 | uint64 finality; // EVM block number at which the reported data will be considered to be finalized. 37 | uint32 resultTimestamp; // Unix timestamp (seconds) at which the data request was resolved in the Witnet blockchain. 38 | bytes32 resultTallyHash; // Unique hash of the commit/reveal act in the Witnet blockchain that resolved the data request. 39 | bytes resultCborBytes; // CBOR-encode result to the request, as resolved in the Witnet blockchain. 40 | } 41 | 42 | /// Response status from a requester's point of view. 43 | enum ResponseStatus { 44 | Void, 45 | Awaiting, 46 | Ready, 47 | Error, 48 | Finalizing, 49 | Delivered 50 | } 51 | 52 | struct RadonSLA { 53 | /// @notice Number of nodes in the Witnet blockchain that will take part in solving the data request. 54 | uint8 committeeSize; 55 | 56 | /// @notice Fee in $nanoWIT paid to every node in the Witnet blockchain involved in solving the data request. 57 | /// @dev Witnet nodes participating as witnesses will have to stake as collateral 100x this amount. 58 | uint64 witnessingFeeNanoWit; 59 | } 60 | 61 | 62 | /// =============================================================================================================== 63 | /// --- 'WitnetV2.RadonSLA' helper methods ------------------------------------------------------------------------ 64 | 65 | function equalOrGreaterThan(RadonSLA memory a, RadonSLA memory b) 66 | internal pure returns (bool) 67 | { 68 | return (a.committeeSize >= b.committeeSize); 69 | } 70 | 71 | function isValid(RadonSLA calldata sla) internal pure returns (bool) { 72 | return ( 73 | sla.witnessingFeeNanoWit > 0 74 | && sla.committeeSize > 0 && sla.committeeSize <= 127 75 | ); 76 | } 77 | 78 | function toV1(RadonSLA memory self) internal pure returns (Witnet.RadonSLA memory) { 79 | return Witnet.RadonSLA({ 80 | numWitnesses: self.committeeSize, 81 | minConsensusPercentage: 51, 82 | witnessReward: self.witnessingFeeNanoWit, 83 | witnessCollateral: self.witnessingFeeNanoWit * 100, 84 | minerCommitRevealFee: self.witnessingFeeNanoWit / self.committeeSize 85 | }); 86 | } 87 | 88 | function nanoWitTotalFee(RadonSLA storage self) internal view returns (uint64) { 89 | return self.witnessingFeeNanoWit * (self.committeeSize + 3); 90 | } 91 | 92 | 93 | /// =============================================================================================================== 94 | /// --- P-RNG generators ------------------------------------------------------------------------------------------ 95 | 96 | /// Generates a pseudo-random uint32 number uniformly distributed within the range `[0 .. range)`, based on 97 | /// the given `nonce` and `seed` values. 98 | function randomUniformUint32(uint32 range, uint256 nonce, bytes32 seed) 99 | internal pure 100 | returns (uint32) 101 | { 102 | uint256 _number = uint256( 103 | keccak256( 104 | abi.encode(seed, nonce) 105 | ) 106 | ) & uint256(2 ** 224 - 1); 107 | return uint32((_number * range) >> 224); 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /contracts/mocks/WitnetMockedOracle.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity >=0.7.0 <0.9.0; 4 | pragma experimental ABIEncoderV2; 5 | 6 | import "./WitnetMockedRequestBytecodes.sol"; 7 | import "./WitnetMockedRequestFactory.sol"; 8 | import "../core/defaults/WitnetOracleTrustableDefault.sol"; 9 | 10 | import "./WitnetMockedPriceFeeds.sol"; 11 | import "./WitnetMockedRandomness.sol"; 12 | 13 | /// @title Mocked implementation of `WitnetOracle`. 14 | /// @dev TO BE USED ONLY ON DEVELOPMENT ENVIRONMENTS. 15 | /// @dev ON SUPPORTED TESTNETS AND MAINNETS, PLEASE USE 16 | /// @dev THE `WitnetOracle` CONTRACT ADDRESS PROVIDED 17 | /// @dev BY THE WITNET FOUNDATION. 18 | contract WitnetMockedOracle 19 | is 20 | WitnetOracleTrustableDefault 21 | { 22 | WitnetRequestFactory private __factory; 23 | 24 | constructor(WitnetMockedRequestBytecodes _registry) 25 | WitnetOracleTrustableDefault( 26 | WitnetRequestFactory(address(0)), 27 | WitnetRequestBytecodes(address(_registry)), 28 | false, 29 | bytes32("mocked"), 30 | 60000, 65000, 70000, 20000 31 | ) 32 | { 33 | address[] memory _reporters = new address[](1); 34 | _reporters[0] = msg.sender; 35 | __setReporters(_reporters); 36 | } 37 | 38 | function factory() override public view returns (WitnetRequestFactory) { 39 | return __factory; 40 | } 41 | 42 | function setFactory(WitnetMockedRequestFactory _factory) external onlyOwner { 43 | __factory = WitnetRequestFactory(address(_factory)); 44 | } 45 | } -------------------------------------------------------------------------------- /contracts/mocks/WitnetMockedPriceFeeds.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity >=0.7.0 <0.9.0; 4 | pragma experimental ABIEncoderV2; 5 | 6 | import "./WitnetMockedOracle.sol"; 7 | import "../core/defaults/WitnetPriceFeedsDefault.sol"; 8 | 9 | /// @title Mocked implementation of `WitnetPriceFeeds`. 10 | /// @dev TO BE USED ONLY ON DEVELOPMENT ENVIRONMENTS. 11 | /// @dev ON SUPPORTED TESTNETS AND MAINNETS, PLEASE USE 12 | /// @dev THE `WitnetPriceFeeds` CONTRACT ADDRESS PROVIDED 13 | /// @dev BY THE WITNET FOUNDATION. 14 | contract WitnetMockedPriceFeeds is WitnetPriceFeedsDefault { 15 | constructor(WitnetMockedOracle _wrb) 16 | WitnetPriceFeedsDefault( 17 | _wrb, 18 | false, 19 | bytes32("mocked") 20 | ) 21 | {} 22 | } 23 | -------------------------------------------------------------------------------- /contracts/mocks/WitnetMockedRandomness.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity >=0.7.0 <0.9.0; 4 | pragma experimental ABIEncoderV2; 5 | 6 | import "./WitnetMockedOracle.sol"; 7 | import "../apps/WitnetRandomnessV2.sol"; 8 | 9 | /// @title Mocked implementation of `WitnetRandomness`. 10 | /// @dev TO BE USED ONLY ON DEVELOPMENT ENVIRONMENTS. 11 | /// @dev ON SUPPORTED TESTNETS AND MAINNETS, PLEASE USE 12 | /// @dev THE `WitnetRandomness` CONTRACT ADDRESS PROVIDED 13 | /// @dev BY THE WITNET FOUNDATION. 14 | contract WitnetMockedRandomness is WitnetRandomnessV2 { 15 | constructor(WitnetMockedOracle _wrb) 16 | WitnetRandomnessV2(_wrb, msg.sender) 17 | {} 18 | } 19 | -------------------------------------------------------------------------------- /contracts/mocks/WitnetMockedRequestBytecodes.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity >=0.7.0 <0.9.0; 4 | pragma experimental ABIEncoderV2; 5 | 6 | import "../core/defaults/WitnetRequestBytecodesDefault.sol"; 7 | 8 | /// @title Mocked implementation of `WitnetRequestBytecodes`. 9 | /// @dev TO BE USED ONLY ON DEVELOPMENT ENVIRONMENTS. 10 | /// @dev ON SUPPORTED TESTNETS AND MAINNETS, PLEASE USE 11 | /// @dev THE `WitnetRequestBytecodes` CONTRACT ADDRESS PROVIDED 12 | /// @dev BY THE WITNET FOUNDATION. 13 | contract WitnetMockedRequestBytecodes is WitnetRequestBytecodesDefault { 14 | constructor() 15 | WitnetRequestBytecodesDefault( 16 | false, 17 | bytes32("mocked") 18 | ) 19 | {} 20 | } 21 | -------------------------------------------------------------------------------- /contracts/mocks/WitnetMockedRequestFactory.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity >=0.7.0 <0.9.0; 4 | pragma experimental ABIEncoderV2; 5 | 6 | import "./WitnetMockedOracle.sol"; 7 | import "../core/defaults/WitnetRequestFactoryDefault.sol"; 8 | 9 | /// @title Mocked implementation of `WitnetRequestFactory`. 10 | /// @dev TO BE USED ONLY ON DEVELOPMENT ENVIRONMENTS. 11 | /// @dev ON SUPPORTED TESTNETS AND MAINNETS, PLEASE USE 12 | /// @dev THE `WitnetRequestFactory` CONTRACT ADDRESS PROVIDED 13 | /// @dev BY THE WITNET FOUNDATION. 14 | contract WitnetMockedRequestFactory 15 | is 16 | WitnetRequestFactoryDefault 17 | { 18 | constructor (WitnetMockedOracle _wrb) 19 | WitnetRequestFactoryDefault( 20 | WitnetOracle(address(_wrb)), 21 | WitnetRequestBytecodes(_wrb.registry()), 22 | false, 23 | bytes32("mocked") 24 | ) 25 | {} 26 | } -------------------------------------------------------------------------------- /contracts/patterns/Clonable.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity >=0.6.0 <0.9.0; 4 | 5 | import "./Initializable.sol"; 6 | 7 | abstract contract Clonable 8 | is 9 | Initializable 10 | { 11 | address immutable internal _SELF = address(this); 12 | 13 | event Cloned(address indexed by, address indexed self, address indexed clone); 14 | 15 | modifier onlyDelegateCalls virtual { 16 | require(address(this) != _SELF, "Clonable: not a delegate call"); 17 | _; 18 | } 19 | 20 | modifier wasInitialized { 21 | require(initialized(), "Clonable: not initialized"); 22 | _; 23 | } 24 | 25 | /// @notice Tells whether this contract is a clone of `self()` 26 | function cloned() 27 | public view 28 | returns (bool) 29 | { 30 | return ( 31 | address(this) != self() 32 | ); 33 | } 34 | 35 | /// @notice Tells whether this instance has been initialized. 36 | function initialized() virtual public view returns (bool); 37 | 38 | /// @notice Contract address to which clones will be re-directed. 39 | function self() virtual public view returns (address) { 40 | return _SELF; 41 | } 42 | 43 | /// Deploys and returns the address of a minimal proxy clone that replicates contract 44 | /// behaviour while using its own EVM storage. 45 | /// @dev This function should always provide a new address, no matter how many times 46 | /// @dev is actually called from the same `msg.sender`. 47 | /// @dev See https://eips.ethereum.org/EIPS/eip-1167. 48 | /// @dev See https://blog.openzeppelin.com/deep-dive-into-the-minimal-proxy-contract/. 49 | function _clone() 50 | internal 51 | returns (address _instance) 52 | { 53 | bytes memory ptr = _cloneBytecodePtr(); 54 | assembly { 55 | // CREATE new instance: 56 | _instance := create(0, ptr, 0x37) 57 | } 58 | require(_instance != address(0), "Clonable: CREATE failed"); 59 | emit Cloned(msg.sender, self(), _instance); 60 | } 61 | 62 | /// @notice Returns minimal proxy's deploy bytecode. 63 | function _cloneBytecode() 64 | virtual internal view 65 | returns (bytes memory) 66 | { 67 | return abi.encodePacked( 68 | hex"3d602d80600a3d3981f3363d3d373d3d3d363d73", 69 | bytes20(self()), 70 | hex"5af43d82803e903d91602b57fd5bf3" 71 | ); 72 | } 73 | 74 | /// @notice Returns mem pointer to minimal proxy's deploy bytecode. 75 | function _cloneBytecodePtr() 76 | virtual internal view 77 | returns (bytes memory ptr) 78 | { 79 | address _base = self(); 80 | assembly { 81 | // ptr to free mem: 82 | ptr := mload(0x40) 83 | // begin minimal proxy construction bytecode: 84 | mstore(ptr, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000000000000000000000) 85 | // make minimal proxy delegate all calls to `self()`: 86 | mstore(add(ptr, 0x14), shl(0x60, _base)) 87 | // end minimal proxy construction bytecode: 88 | mstore(add(ptr, 0x28), 0x5af43d82803e903d91602b57fd5bf30000000000000000000000000000000000) 89 | } 90 | } 91 | 92 | /// Deploys and returns the address of a minimal proxy clone that replicates contract 93 | /// behaviour while using its own EVM storage. 94 | /// @dev This function uses the CREATE2 opcode and a `_salt` to deterministically deploy 95 | /// @dev the clone. Using the same `_salt` multiple times will revert, since 96 | /// @dev no contract can be deployed more than once at the same address. 97 | /// @dev See https://eips.ethereum.org/EIPS/eip-1167. 98 | /// @dev See https://blog.openzeppelin.com/deep-dive-into-the-minimal-proxy-contract/. 99 | function _cloneDeterministic(bytes32 _salt) 100 | virtual internal 101 | returns (address _instance) 102 | { 103 | bytes memory ptr = _cloneBytecodePtr(); 104 | assembly { 105 | // CREATE2 new instance: 106 | _instance := create2(0, ptr, 0x37, _salt) 107 | } 108 | require(_instance != address(0), "Clonable: CREATE2 failed"); 109 | emit Cloned(msg.sender, self(), _instance); 110 | } 111 | } -------------------------------------------------------------------------------- /contracts/patterns/Initializable.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.8.0 <0.9.0; 3 | import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; -------------------------------------------------------------------------------- /contracts/patterns/Ownable.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.0; 3 | import "@openzeppelin/contracts/access/Ownable.sol"; 4 | -------------------------------------------------------------------------------- /contracts/patterns/Ownable2Step.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.0; 3 | 4 | import "./Ownable.sol"; 5 | 6 | /** 7 | * @dev Contract module which provides access control mechanism, where 8 | * there is an account (an owner) that can be granted exclusive access to 9 | * specific functions. 10 | * 11 | * The initial owner is specified at deployment time in the constructor for `Ownable`. This 12 | * can later be changed with {transferOwnership} and {acceptOwnership}. 13 | * 14 | * This module is used through inheritance. It will make available all functions 15 | * from parent (Ownable). 16 | */ 17 | abstract contract Ownable2Step is Ownable { 18 | address private _pendingOwner; 19 | 20 | event OwnershipTransferStarted(address indexed previousOwner, address indexed newOwner); 21 | 22 | /** 23 | * @dev Returns the address of the pending owner. 24 | */ 25 | function pendingOwner() public view virtual returns (address) { 26 | return _pendingOwner; 27 | } 28 | 29 | /** 30 | * @dev Starts the ownership transfer of the contract to a new account. Replaces the pending transfer if there is one. 31 | * Can only be called by the current owner. 32 | */ 33 | function transferOwnership(address newOwner) public virtual override onlyOwner { 34 | _pendingOwner = newOwner; 35 | emit OwnershipTransferStarted(owner(), newOwner); 36 | } 37 | 38 | /** 39 | * @dev Transfers ownership of the contract to a new account (`newOwner`) and deletes any pending owner. 40 | * Internal function without access restriction. 41 | */ 42 | function _transferOwnership(address newOwner) internal virtual override { 43 | delete _pendingOwner; 44 | super._transferOwnership(newOwner); 45 | } 46 | 47 | /** 48 | * @dev The new owner accepts the ownership transfer. 49 | */ 50 | function acceptOwnership() public virtual { 51 | address sender = _msgSender(); 52 | if (pendingOwner() != sender) { 53 | revert("Ownable2Step: caller is not the new owner"); 54 | } 55 | _transferOwnership(sender); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /contracts/patterns/Payable.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.6.0 <0.9.0; 3 | 4 | import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; 5 | 6 | abstract contract Payable { 7 | IERC20 public immutable currency; 8 | 9 | event Received(address from, uint256 value); 10 | event Transfer(address to, uint256 value); 11 | 12 | constructor(address _currency) { 13 | currency = IERC20(_currency); 14 | } 15 | 16 | /// Gets current transaction price. 17 | function _getGasPrice() internal view virtual returns (uint256); 18 | 19 | /// Gets current payment value. 20 | function _getMsgValue() internal view virtual returns (uint256); 21 | 22 | /// Perform safe transfer or whatever token is used for paying rewards. 23 | function __safeTransferTo(address payable, uint256) internal virtual; 24 | } 25 | -------------------------------------------------------------------------------- /contracts/patterns/Proxiable.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity >=0.6.0 <0.9.0; 4 | 5 | abstract contract Proxiable { 6 | /// @dev Complying with EIP-1822: Universal Upgradeable Proxy Standard (UUPS) 7 | /// @dev See https://eips.ethereum.org/EIPS/eip-1822. 8 | function proxiableUUID() virtual external view returns (bytes32); 9 | 10 | struct ProxiableSlot { 11 | address implementation; 12 | address proxy; 13 | bytes32 codehash; 14 | } 15 | 16 | function __implementation() internal view returns (address) { 17 | return __proxiable().implementation; 18 | } 19 | 20 | function __proxy() internal view returns (address) { 21 | return __proxiable().proxy; 22 | } 23 | 24 | function __proxiable() internal pure returns (ProxiableSlot storage proxiable) { 25 | assembly { 26 | // bytes32(uint256(keccak256('eip1967.proxy.implementation')) - 1) 27 | proxiable.slot := 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /contracts/patterns/ReentrancyGuard.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.0; 3 | import "@openzeppelin/contracts/utils/ReentrancyGuard.sol"; 4 | -------------------------------------------------------------------------------- /contracts/patterns/Upgradeable.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | /* solhint-disable var-name-mixedcase */ 4 | 5 | pragma solidity >=0.6.0 <0.9.0; 6 | 7 | import "@openzeppelin/contracts/utils/introspection/ERC165.sol"; 8 | 9 | import "./Initializable.sol"; 10 | import "./Proxiable.sol"; 11 | 12 | abstract contract Upgradeable is Initializable, Proxiable { 13 | 14 | address internal immutable _BASE; 15 | bytes32 internal immutable _CODEHASH; 16 | bool internal immutable _UPGRADABLE; 17 | 18 | modifier onlyDelegateCalls virtual { 19 | require( 20 | address(this) != _BASE, 21 | "Upgradeable: not a delegate call" 22 | ); 23 | _; 24 | } 25 | 26 | /// Emitted every time the contract gets upgraded. 27 | /// @param from The address who ordered the upgrading. Namely, the WRB operator in "trustable" implementations. 28 | /// @param baseAddr The address of the new implementation contract. 29 | /// @param baseCodehash The EVM-codehash of the new implementation contract. 30 | /// @param versionTag Ascii-encoded version literal with which the implementation deployer decided to tag it. 31 | event Upgraded( 32 | address indexed from, 33 | address indexed baseAddr, 34 | bytes32 indexed baseCodehash, 35 | string versionTag 36 | ); 37 | 38 | constructor (bool _isUpgradable) { 39 | address _base = address(this); 40 | _BASE = _base; 41 | _UPGRADABLE = _isUpgradable; 42 | } 43 | 44 | /// @dev Retrieves base contract. Differs from address(this) when called via delegate-proxy pattern. 45 | function base() public view returns (address) { 46 | return _BASE; 47 | } 48 | 49 | /// @dev Retrieves the immutable codehash of this contract, even if invoked as delegatecall. 50 | function codehash() public view returns (bytes32 _codehash) { 51 | address _base = _BASE; 52 | assembly { 53 | _codehash := extcodehash(_base) 54 | } 55 | } 56 | 57 | /// @dev Determines whether the logic of this contract is potentially upgradable. 58 | function isUpgradable() public view returns (bool) { 59 | return _UPGRADABLE; 60 | } 61 | 62 | /// @dev Tells whether provided address could eventually upgrade the contract. 63 | function isUpgradableFrom(address from) virtual external view returns (bool); 64 | 65 | /// @notice Re-initialize contract's storage context upon a new upgrade from a proxy. 66 | /// @dev Must fail when trying to upgrade to same logic contract more than once. 67 | function initialize(bytes memory) virtual external; 68 | 69 | /// @dev Retrieves human-redable named version of current implementation. 70 | function version() virtual public view returns (string memory); 71 | } -------------------------------------------------------------------------------- /hardhat.config.js: -------------------------------------------------------------------------------- 1 | require("@nomicfoundation/hardhat-verify") 2 | 3 | const settings = require("./settings") 4 | const utils = require("./src/utils") 5 | const [, target] = utils.getRealmNetworkFromArgs() 6 | 7 | module.exports = { 8 | paths: { 9 | sources: "./contracts", 10 | }, 11 | networks: Object.fromEntries( 12 | Object.entries(settings.getNetworks()) 13 | .map(([network, config]) => { 14 | return [network, { 15 | chainId: config.network_id, 16 | gas: config?.gas, 17 | gasPrice: config?.gasPrice, 18 | url: `http://${config?.host || "localhost"}:${config?.port || 8545}`, 19 | }] 20 | }) 21 | ), 22 | solidity: settings.getCompilers(target), 23 | sourcify: { 24 | enabled: true, 25 | apiUrl: "https://sourcify.dev/server", 26 | browserUrl: "https://repo.sourcify.dev", 27 | }, 28 | etherscan: { 29 | apiKey: Object.fromEntries( 30 | Object.entries(settings.getNetworks()) 31 | .filter(([, config]) => config?.verify !== undefined) 32 | .map(([network, config]) => { 33 | const [ecosystem] = utils.getRealmNetworkFromString(network) 34 | const envar = `ETHERSCAN_${ecosystem.toUpperCase()}_API_KEY` 35 | return [network, 36 | config?.verify?.apiKey || process.env[envar] || process.env.ETHERSCAN_API_KEY || "MY_API_KEY", 37 | ] 38 | }), 39 | ), 40 | customChains: Object.entries(settings.getNetworks()) 41 | .filter(([, config]) => config?.verify !== undefined) 42 | .map(([network, config]) => { 43 | return { 44 | network, 45 | chainId: config.network_id, 46 | urls: { 47 | apiURL: config?.apiUrl, 48 | browserURL: config?.browserUrl, 49 | }, 50 | } 51 | }), 52 | }, 53 | } 54 | -------------------------------------------------------------------------------- /migrations/salts/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/witnet/witnet-solidity-bridge/b9115449d4dafb362216da4e4fe1113e2785ce6f/migrations/salts/.gitkeep -------------------------------------------------------------------------------- /migrations/scripts/1_deployer.js: -------------------------------------------------------------------------------- 1 | const settings = require("../../settings") 2 | const utils = require("../../src/utils") 3 | 4 | const WitnetDeployer = artifacts.require("WitnetDeployer") 5 | const WitnetProxy = artifacts.require("WitnetProxy") 6 | 7 | module.exports = async function (deployer, network, [,,, master]) { 8 | const addresses = await utils.readJsonFromFile("./migrations/addresses.json") 9 | if (!addresses[network]) addresses[network] = {} 10 | 11 | const factoryAddr = addresses[network]?.WitnetDeployer || addresses?.default?.WitnetDeployer || "" 12 | if ( 13 | utils.isNullAddress(factoryAddr) || 14 | (await web3.eth.getCode(factoryAddr)).length < 3 15 | ) { 16 | const WitnetDeployerImpl = artifacts.require(settings.getArtifacts(network).WitnetDeployer) 17 | await deployer.deploy(WitnetDeployerImpl, { 18 | from: settings.getSpecs(network)?.WitnetDeployer?.from || web3.utils.toChecksumAddress(master), 19 | }) 20 | const factory = await WitnetDeployerImpl.deployed() 21 | if (factory.address !== addresses?.default?.WitnetDeployer) { 22 | addresses[network].WitnetDeployer = factory.address 23 | if (!utils.isDryRun(network)) { 24 | await utils.overwriteJsonFile("./migrations/addresses.json", addresses) 25 | } 26 | } 27 | WitnetDeployer.address = factory.address 28 | } else { 29 | const factory = await WitnetDeployer.at(factoryAddr) 30 | WitnetDeployer.address = factory.address 31 | utils.traceHeader("Skipped 'WitnetDeployer'") 32 | console.info(" > Contract address:", factory.address) 33 | console.info() 34 | } 35 | 36 | const proxyAddr = addresses[network]?.WitnetProxy || addresses?.default?.WitnetProxy || "" 37 | if ( 38 | utils.isNullAddress(proxyAddr) || 39 | (await web3.eth.getCode(proxyAddr)).length < 3 40 | ) { 41 | await deployer.deploy(WitnetProxy, { 42 | from: settings.getSpecs(network)?.WitnetDeployer?.from || master, 43 | }) 44 | if (WitnetProxy.address !== addresses?.default?.WitnetProxy) { 45 | addresses[network].WitnetProxy = WitnetProxy.address 46 | if (!utils.isDryRun(network)) { 47 | await utils.overwriteJsonFile("./migrations/addresses.json", addresses) 48 | } 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /migrations/scripts/2_libs.js: -------------------------------------------------------------------------------- 1 | const settings = require("../../settings") 2 | const utils = require("../../src/utils") 3 | 4 | const WitnetDeployer = artifacts.require("WitnetDeployer") 5 | 6 | module.exports = async function (_, network, [, from]) { 7 | const addresses = await utils.readJsonFromFile("./migrations/addresses.json") 8 | if (!addresses[network]) addresses[network] = {} 9 | 10 | const targets = settings.getArtifacts(network) 11 | const libs = [ 12 | targets.WitnetErrorsLib, 13 | targets.WitnetEncodingLib, 14 | targets.WitnetPriceFeedsLib, 15 | targets.WitnetOracleDataLib, 16 | ] 17 | 18 | const selection = utils.getWitnetArtifactsFromArgs() 19 | 20 | const deployer = await WitnetDeployer.deployed() 21 | for (const index in libs) { 22 | const key = libs[index] 23 | const artifact = artifacts.require(key) 24 | if ( 25 | utils.isNullAddress(addresses[network][key]) || 26 | (await web3.eth.getCode(addresses[network][key])).length < 3 || 27 | selection.includes(key) 28 | ) { 29 | utils.traceHeader(`Deploying '${key}'...`) 30 | const libInitCode = artifact.toJSON().bytecode 31 | const libAddr = await deployer.determineAddr.call(libInitCode, "0x0", { from }) 32 | console.info(" ", "> account: ", from) 33 | console.info(" ", "> balance: ", web3.utils.fromWei(await web3.eth.getBalance(from), "ether"), "ETH") 34 | const tx = await deployer.deploy(libInitCode, "0x0", { from }) 35 | utils.traceTx(tx) 36 | if ((await web3.eth.getCode(libAddr)).length > 3) { 37 | addresses[network][key] = libAddr 38 | } else { 39 | console.info(`Error: Library was not deployed on expected address: ${libAddr}`) 40 | process.exit(1) 41 | } 42 | if (!utils.isDryRun(network)) { 43 | await utils.overwriteJsonFile("./migrations/addresses.json", addresses) 44 | } 45 | } else { 46 | utils.traceHeader(`Skipped '${key}'`) 47 | } 48 | artifact.address = addresses[network][key] 49 | console.info(" ", "> library address: ", artifact.address) 50 | console.info(" ", "> library codehash: ", web3.utils.soliditySha3(await web3.eth.getCode(artifact.address))) 51 | console.info() 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /migrations/scripts/4_proxies.js: -------------------------------------------------------------------------------- 1 | const ethUtils = require("ethereumjs-util") 2 | const merge = require("lodash.merge") 3 | const settings = require("../../settings") 4 | const utils = require("../../src/utils") 5 | 6 | const version = `${ 7 | require("../../package").version 8 | }-${ 9 | require("child_process").execSync("git rev-parse HEAD").toString().trim().substring(0, 7) 10 | }` 11 | 12 | const WitnetDeployer = artifacts.require("WitnetDeployer") 13 | const WitnetProxy = artifacts.require("WitnetProxy") 14 | 15 | module.exports = async function (_, network, [, from, reporter]) { 16 | const targets = settings.getArtifacts(network) 17 | const specs = settings.getSpecs(network) 18 | 19 | const singletons = [ 20 | "WitnetRequestBytecodes", 21 | "WitnetRequestFactory", 22 | "WitnetOracle", 23 | "WitnetPriceFeeds", 24 | ] 25 | 26 | // inject `reporter` within array of addresses as first initialization args 27 | specs.WitnetOracle.mutables = merge({ 28 | types: ["address[]"], 29 | values: [[reporter]], 30 | }, specs.WitnetOracle.mutables 31 | ) 32 | 33 | // Deploy/upgrade singleton proxies, if required 34 | for (const index in singletons) { 35 | const key = singletons[index] 36 | await deploy({ 37 | network, 38 | specs, 39 | targets, 40 | key, 41 | from: utils.isDryRun(network) ? from : specs[key].from || from, 42 | }) 43 | } 44 | } 45 | 46 | async function deploy (target) { 47 | const { from, key, network, specs, targets } = target 48 | 49 | const addresses = await utils.readJsonFromFile("./migrations/addresses.json") 50 | if (!addresses[network]) addresses[network] = {} 51 | 52 | const mutables = specs[key].mutables 53 | const proxy = artifacts.require(key) 54 | const proxySalt = specs[key].vanity 55 | ? "0x" + ethUtils.setLengthLeft(ethUtils.toBuffer(specs[key].vanity), 32).toString("hex") 56 | : "0x0" 57 | 58 | let proxyAddr = addresses[network][key] || addresses?.default[key] || "" 59 | if (utils.isNullAddress(proxyAddr) || (await web3.eth.getCode(proxyAddr)).length < 3) { 60 | utils.traceHeader(`Deploying '${key}'...`) 61 | console.info(" ", "> account: ", from) 62 | console.info(" ", "> balance: ", web3.utils.fromWei(await web3.eth.getBalance(from), "ether"), "ETH") 63 | const deployer = await WitnetDeployer.deployed() 64 | const impl = await artifacts.require(targets[key]).deployed() 65 | proxyAddr = await deployer.determineProxyAddr.call(proxySalt, { from }) 66 | if ((await web3.eth.getCode(proxyAddr)).length < 3) { 67 | const initdata = mutables ? web3.eth.abi.encodeParameters(mutables.types, mutables.values) : "0x" 68 | if (initdata.length > 2) { 69 | console.info(" ", "> initialize types: ", mutables.types) 70 | console.info(" ", "> initialize params:", mutables.values) 71 | } 72 | const tx = await deployer.proxify(proxySalt, impl.address, initdata, { from }) 73 | utils.traceTx(tx) 74 | } else { 75 | try { 76 | const oldImplAddr = await getProxyImplementation(from, proxyAddr) 77 | const oldImpl = await artifacts.require(targets[key]).at(oldImplAddr) 78 | const oldClass = await oldImpl.class.call({ from }) 79 | const newClass = await impl.class.call({ from }) 80 | if (oldClass !== newClass) { 81 | console.info(`Error: proxy address already taken ("${oldClass}" != "${newClass}")`) 82 | process.exit(1) 83 | } else { 84 | console.info(" ", `> recovered proxy address on class "${oldClass}" ;-)`) 85 | } 86 | } catch (ex) { 87 | console.info("Error: cannot check proxy recoverability:", ex) 88 | } 89 | } 90 | if ((await web3.eth.getCode(proxyAddr)).length > 3) { 91 | if (proxyAddr !== addresses?.default[key]) { 92 | addresses[network][key] = proxyAddr 93 | if (!utils.isDryRun(network)) { 94 | await utils.overwriteJsonFile("./migrations/addresses.json", addresses) 95 | } 96 | } 97 | } else { 98 | console.info(`Error: Contract was not deployed on expected address: ${proxyAddr}`) 99 | process.exit(1) 100 | } 101 | } else { 102 | const oldAddr = await getProxyImplementation(from, proxyAddr) 103 | const newImpl = await artifacts.require(targets[key]).deployed() 104 | if (oldAddr !== newImpl.address) { 105 | utils.traceHeader(`Upgrading '${key}'...`) 106 | const newVersion = await newImpl.version.call({ from }) 107 | const color = newVersion === version ? "\x1b[1;97m" : "\x1b[93m" 108 | if ( 109 | (process.argv.length >= 3 && process.argv[2].includes("--upgrade-all")) || ( 110 | ["y", "yes"].includes((await 111 | utils.prompt(` > Upgrade to ${color}${targets[key]} v${newVersion}\x1b[0m? (y/N) `) 112 | ).toLowerCase().trim()) 113 | ) 114 | ) { 115 | const initdata = mutables ? web3.eth.abi.encodeParameters(mutables.types, mutables.values) : "0x" 116 | if (initdata.length > 2) { 117 | console.info(" ", "> initialize types: ", mutables.types) 118 | console.info(" ", "> initialize params:", mutables.values) 119 | } 120 | try { 121 | const tx = await upgradeProxyTo(from, proxyAddr, newImpl.address, initdata) 122 | utils.traceTx(tx) 123 | } catch (ex) { 124 | console.error(" ", "> Exception:\n", ex) 125 | } 126 | } 127 | } else { 128 | utils.traceHeader(`Skipped '${key}'`) 129 | } 130 | } 131 | proxy.address = proxyAddr 132 | const impl = await artifacts.require(targets[key]).at(proxy.address) 133 | console.info(" ", "> proxy address: ", impl.address) 134 | console.info(" ", "> proxy codehash: ", web3.utils.soliditySha3(await web3.eth.getCode(impl.address))) 135 | console.info(" ", "> proxy operator: ", await impl.owner.call({ from })) 136 | console.info(" ", "> impl. address: ", await getProxyImplementation(from, proxy.address)) 137 | console.info(" ", "> impl. class: ", await impl.class.call({ from })) 138 | console.info(" ", "> impl. version: ", await impl.version.call({ from })) 139 | console.info() 140 | return proxy 141 | } 142 | 143 | async function getProxyImplementation (from, proxyAddr) { 144 | const proxy = await WitnetProxy.at(proxyAddr) 145 | return await proxy.implementation.call({ from }) 146 | } 147 | 148 | async function upgradeProxyTo (from, proxyAddr, implAddr, initData) { 149 | const proxyContract = await WitnetProxy.at(proxyAddr) 150 | return await proxyContract.upgradeTo(implAddr, initData, { from }) 151 | } 152 | -------------------------------------------------------------------------------- /migrations/scripts/5_apps.js: -------------------------------------------------------------------------------- 1 | const ethUtils = require("ethereumjs-util") 2 | const settings = require("../../settings") 3 | const utils = require("../../src/utils") 4 | 5 | const WitnetDeployer = artifacts.require("WitnetDeployer") 6 | 7 | module.exports = async function (_, network, [, from]) { 8 | const specs = settings.getSpecs(network) 9 | const targets = settings.getArtifacts(network) 10 | 11 | // Community appliances built on top of the Witnet Oracle are meant to be immutable, 12 | // and therefore not upgradable. Appliances can only be deployed 13 | // once all core Witnet Oracle artifacts get deployed and initialized. 14 | 15 | // ========================================================================== 16 | // --- WitnetRandomnessV2 -------------------------------------------------- 17 | 18 | if (!process.argv.includes("--no-randomness")) { 19 | await deploy({ 20 | network, 21 | targets, 22 | from: utils.isDryRun(network) ? from : specs.WitnetRandomness.from || from, 23 | key: "WitnetRandomness", 24 | specs: specs.WitnetRandomness, 25 | intrinsics: { 26 | types: ["address", "address"], 27 | values: [ 28 | /* _witnetOracle */ await determineProxyAddr(from, specs.WitnetOracle?.vanity || 3), 29 | /* _witnetOperator */ utils.isDryRun(network) ? from : specs?.WitnetRandomness?.from || from, 30 | ], 31 | }, 32 | }) 33 | } 34 | } 35 | 36 | async function deploy (target) { 37 | const { from, key, intrinsics, network, specs, targets } = target 38 | const { libs, immutables, vanity } = specs 39 | const salt = vanity ? "0x" + utils.padLeft(vanity.toString(16), "0", 64) : "0x0" 40 | 41 | const addresses = await utils.readJsonFromFile("./migrations/addresses.json") 42 | if (!addresses[network]) addresses[network] = {} 43 | 44 | const selection = utils.getWitnetArtifactsFromArgs() 45 | const artifact = artifacts.require(key) 46 | const contract = artifacts.require(targets[key]) 47 | if ( 48 | (!utils.isNullAddress(addresses[network][targets[key]] || addresses?.default[targets[key]]) && 49 | (await web3.eth.getCode(addresses[network][targets[key]] || addresses?.default[targets[key]])).length < 3) || 50 | addresses[network][targets[key]] === "" || 51 | selection.includes(targets[key]) 52 | ) { 53 | utils.traceHeader(`Deploying '${key}'...`) 54 | console.info(" ", "> account: ", from) 55 | console.info(" ", "> balance: ", web3.utils.fromWei(await web3.eth.getBalance(from), "ether"), "ETH") 56 | const deployer = await WitnetDeployer.deployed() 57 | let { types, values } = intrinsics 58 | if (immutables?.types) types = [...types, ...immutables.types] 59 | if (immutables?.values) values = [...values, ...immutables.values] 60 | const constructorArgs = web3.eth.abi.encodeParameters(types, values) 61 | if (constructorArgs.length > 2) { 62 | console.info(" ", "> constructor types:", JSON.stringify(types)) 63 | console.info(" ", "> constructor args: ", constructorArgs.slice(2)) 64 | } 65 | const bytecode = link(contract.toJSON().bytecode, libs, targets) 66 | if (bytecode.indexOf("__") > -1) { 67 | console.info(bytecode) 68 | console.info("Error: Cannot deploy due to some missing libs") 69 | process.exit(1) 70 | } 71 | const initCode = bytecode + constructorArgs.slice(2) 72 | const addr = await deployer.determineAddr.call(initCode, salt, { from }) 73 | const tx = await deployer.deploy(initCode, salt || "0x0", { from }) 74 | utils.traceTx(tx) 75 | if ((await web3.eth.getCode(addr)).length > 3) { 76 | if (addr !== addresses?.default[targets[key]]) { 77 | addresses[network][targets[key]] = addr 78 | // save addresses file if required 79 | if (!utils.isDryRun(network)) { 80 | await utils.overwriteJsonFile("./migrations/addresses.json", addresses) 81 | const args = await utils.readJsonFromFile("./migrations/constructorArgs.json") 82 | if (!args?.default[targets[key]] || constructorArgs.slice(2) !== args.default[targets[key]]) { 83 | if (!args[network]) args[network] = {} 84 | args[network][targets[key]] = constructorArgs.slice(2) 85 | await utils.overwriteJsonFile("./migrations/constructorArgs.json", args) 86 | } 87 | } 88 | } 89 | } else { 90 | console.info(`Error: Contract was not deployed on expected address: ${addr}`) 91 | process.exit(1) 92 | } 93 | } else { 94 | utils.traceHeader(`Skipped '${key}'`) 95 | } 96 | if (!utils.isNullAddress(addresses[network][targets[key]]) || addresses?.default[targets[key]]) { 97 | artifact.address = addresses[network][targets[key]] || addresses?.default[targets[key]] 98 | contract.address = addresses[network][targets[key]] || addresses?.default[targets[key]] 99 | for (const index in libs) { 100 | const libname = libs[index] 101 | const lib = artifacts.require(libname) 102 | contract.link(lib) 103 | console.info(" ", "> external library: ", `${libname}@${lib.address}`) 104 | }; 105 | const appliance = await artifact.deployed() 106 | console.info(" ", "> appliance address: ", appliance.address) 107 | console.info(" ", "> appliance class: ", await appliance.class({ from })) 108 | console.info(" ", "> appliance codehash:", web3.utils.soliditySha3(await web3.eth.getCode(appliance.address))) 109 | console.info(" ", "> appliance specs: ", await appliance.specs({ from })) 110 | console.info() 111 | } 112 | return contract 113 | } 114 | 115 | async function determineProxyAddr (from, nonce) { 116 | const salt = nonce ? "0x" + ethUtils.setLengthLeft(ethUtils.toBuffer(nonce), 32).toString("hex") : "0x0" 117 | const deployer = await WitnetDeployer.deployed() 118 | return await deployer.determineProxyAddr.call(salt, { from }) 119 | } 120 | 121 | function link (bytecode, libs, targets) { 122 | if (libs && Array.isArray(libs) && libs.length > 0) { 123 | for (const index in libs) { 124 | const key = targets[libs[index]] 125 | const lib = artifacts.require(key) 126 | bytecode = bytecode.replaceAll(`__${key}${"_".repeat(38 - key.length)}`, lib.address.slice(2)) 127 | console.info(" ", `> linked library: ${key} => ${lib.address}`) 128 | } 129 | } 130 | return bytecode 131 | } 132 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "witnet-solidity-bridge", 3 | "version": "2.0.18", 4 | "description": "Witnet Solidity Bridge contracts for EVM-compatible chains", 5 | "author": "Witnet Foundation ", 6 | "license": "MIT", 7 | "repository": { 8 | "type": "git", 9 | "url": "https://github.com/witnet/witnet-solidity-bridge/tree/2.0.x" 10 | }, 11 | "type": "commonjs", 12 | "keywords": [ 13 | "bridge", 14 | "witnet", 15 | "ethereum" 16 | ], 17 | "exports": { 18 | ".": "./src/index.js", 19 | "./assets": "./src/index.js", 20 | "./utils": "./src/utils.js" 21 | }, 22 | "files": [ 23 | "artifacts", 24 | "build/contracts", 25 | "contracts", 26 | "migrations/*.json", 27 | "settings", 28 | "src" 29 | ], 30 | "scripts": { 31 | "addresses": "node ./scripts/addresses.js 2>&1", 32 | "clean": "pnpm run clean:artifacts && pnpm run clean:build && pnpm run clean:flattened", 33 | "clean:artifacts": "node ./scripts/clean.js artifacts", 34 | "clean:build": "node ./scripts/clean.js build", 35 | "clean:flattened": "node ./scripts/clean.js flattened", 36 | "compile": "npx truffle compile --all", 37 | "console": "npx truffle console", 38 | "coverage": "solidity-coverage", 39 | "flatten": "node ./scripts/flatten.js 2>&1", 40 | "flatten:all": "pnpm run clean && pnpm run flatten:core && pnpm run flatten:apps && pnpm run flatten:libs && pnpm run flatten:proxy", 41 | "flatten:apps": "pnpm run flatten contracts/apps/", 42 | "flatten:core": "pnpm run flatten contracts/core/", 43 | "flatten:libs": "pnpm run flatten contracts/libs/WitnetErrorsLib.sol && pnpm run flatten contracts/libs/WitnetEncodingLib.sol && pnpm run flatten contracts/libs/WitnetPriceFeedsLib.sol", 44 | "flatten:proxy": "pnpm run flatten contracts/core/WitnetProxy.sol", 45 | "fmt:js": "eslint \"**/*.js\"", 46 | "fmt:sol": "solhint --max-warnings 0 \"contracts/**/*.sol\" && solhint \"test/**/*.sol\" && solhint \"flattened/**/*.sol\"", 47 | "fmt!:js": "eslint \"**/*.js\" --fix", 48 | "fmt!:sol": "solhint --max-warnings 0 \"contracts/**/*.sol\" --fix && solhint \"test/**/*.sol\" --fix && solhint \"flattened/**/*.sol\"", 49 | "fmt!": "pnpm run fmt!:js && pnpm run fmt!:sol", 50 | "fmt": "pnpm run fmt:js && pnpm run fmt:sol", 51 | "migrate": "npx truffle migrate --network", 52 | "networks": "node ./scripts/networks.js 2>&1", 53 | "ops:rng:sla": "npx truffle migrate --migrations_directory ./migrations/ops/rng/sla --network", 54 | "prepare": "npx truffle compile --all && npx hardhat compile --force && node ./scripts/prepare.js", 55 | "test": "pnpm run clean && npx truffle test", 56 | "verify:apps": "node ./scripts/verify-apps.js 2>&1", 57 | "verify:core": "node ./scripts/verify-core.js 2>&1", 58 | "verify:libs": "node ./scripts/verify-libs.js 2>&1", 59 | "verify:impls": "node ./scripts/verify-impls.js 2>&1" 60 | }, 61 | "dependencies": { 62 | "dotenv": "^16.4.4", 63 | "lodash.merge": "^4.6.2", 64 | "proper-lockfile": "^4.1.2" 65 | }, 66 | "devDependencies": { 67 | "@nomicfoundation/hardhat-verify": "^2.0.4", 68 | "@openzeppelin/contracts": "^5.0.1", 69 | "@openzeppelin/contracts-upgradeable": "^5.0.1", 70 | "@openzeppelin/test-helpers": "~0.5.16", 71 | "ado-contracts": "1.0.0", 72 | "bn.js": "^4.11.0", 73 | "chai": "^5.1.0", 74 | "custom-error-test-helper": "^1.0.6", 75 | "eslint": "^8.56.0", 76 | "eslint-config-standard": "^17.1.0", 77 | "eslint-plugin-import": "^2.29.1", 78 | "eslint-plugin-n": "^16.6.2", 79 | "eslint-plugin-promise": "^6.1.1", 80 | "eth-gas-reporter": "^0.2.27", 81 | "eth-helpers": "^1.3.0", 82 | "hardhat": "^2.22.2", 83 | "nanoassert": "^2.0.0", 84 | "sha3-wasm": "^1.0.0", 85 | "solhint": "^4.1.1", 86 | "truffle": "^5.11.5", 87 | "truffle-assertions": "^0.9.2", 88 | "truffle-flattener": "^1.6.0", 89 | "witnet-toolkit": "^2.0.1", 90 | "truffle-plugin-verify": "^0.6.7" 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /scripts/addresses.js: -------------------------------------------------------------------------------- 1 | let addresses = require("../migrations/addresses") 2 | let realm, network 3 | if (process.argv.length >= 3) { 4 | network = process.argv[2].toLowerCase() 5 | realm = network.split(".")[0].toLowerCase() 6 | if (realm === "ethereum") realm = "default" 7 | if (!addresses[realm]) { 8 | console.log("Unknown realm:", realm) 9 | process.exit(1) 10 | } 11 | if (network.split(".")[1]) { 12 | if (!addresses[realm][network]) { 13 | console.log("Realm:", realm) 14 | console.log("Unknown network:", network) 15 | process.exit(1) 16 | } 17 | addresses = addresses[realm][network] 18 | } else { 19 | addresses = addresses[realm] 20 | } 21 | } 22 | console.log(addresses) 23 | -------------------------------------------------------------------------------- /scripts/clean.js: -------------------------------------------------------------------------------- 1 | const exec = require("child_process").execSync 2 | const os = require("os") 3 | const fs = require("fs") 4 | 5 | if (process.argv.length < 3) { 6 | console.log(`Usage: ${0} ${1} /path/to/be/cleaned`) 7 | process.exit(0) 8 | } 9 | 10 | let target = process.argv[2] 11 | if (fs.existsSync(target)) { 12 | if (os.type() === "Windows_NT") { 13 | target = target.replace(/\//g, "\\") 14 | exec(`del ${target}\\ /f /q /s`) 15 | exec(`rmdir ${target}\\ /q /s`) 16 | } else { 17 | target = target.replace(/\\/g, "/") 18 | exec(`rm -rf ${target}`) 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /scripts/eth-create2.js: -------------------------------------------------------------------------------- 1 | const assert = require("nanoassert") 2 | const { utils } = require("eth-helpers") 3 | const keccak = require("sha3-wasm").keccak256 4 | 5 | const prefix = Buffer.from([0xff]) 6 | 7 | /** 8 | * Generate Ethereum CREATE2 address 9 | * 10 | * @param {string|Buffer} address Ethereum address of creator contract as 11 | * string or as a 20 byte `Buffer` 12 | * @param {string|Buffer} salt 256 bit salt as either string or 32 byte `Buffer` 13 | * @param {string|Buffer} initCode init_code as string or Buffer 14 | * @returns {string} result address as hex encoded string. Not 15 | * checksum'ed. This can be done with 16 | * `eth-checksum` or similar modules 17 | */ 18 | module.exports = function create2 (address, salt, initCode) { 19 | if (typeof address === "string") address = utils.parse.address(address) 20 | if (typeof salt === "string") salt = utils.parse.uint256(salt) 21 | if (typeof initCode === "string") initCode = utils.parse.bytes(initCode) 22 | 23 | assert(address.byteLength === 20, "address must be 20 bytes") 24 | assert(salt.byteLength === 32, "salt must be 32 bytes") 25 | assert(initCode.byteLength != null, "initCode must be Buffer") 26 | 27 | const codeHash = keccak().update(initCode).digest() 28 | 29 | return utils.format.address(keccak() 30 | .update(prefix) 31 | .update(address) 32 | .update(salt) 33 | .update(codeHash) 34 | .digest() 35 | .slice(-20)) 36 | } 37 | -------------------------------------------------------------------------------- /scripts/eth-create3.js: -------------------------------------------------------------------------------- 1 | const assert = require("nanoassert") 2 | const { utils } = require("eth-helpers") 3 | const keccak = require("sha3-wasm").keccak256 4 | 5 | const factoryPrefix = Buffer.from([0xff]) 6 | 7 | /** 8 | * Generate Ethereum CREATE3-library address 9 | * 10 | * @param {string|Buffer} address Ethereum address of creator contract as 11 | * string or as a 20 byte `Buffer` 12 | * @param {string|Buffer} salt 256 bit salt as either string or 32 byte `Buffer` 13 | * @returns {string} result address as hex encoded string. Not 14 | * checksum'ed. This can be done with 15 | * `eth-checksum` or similar modules 16 | */ 17 | module.exports = function create3 (address, salt) { 18 | if (typeof address === "string") address = utils.parse.address(address) 19 | if (typeof salt === "string") salt = utils.parse.uint256(salt) 20 | 21 | assert(address.byteLength === 20, "address must be 20 bytes") 22 | assert(salt.byteLength === 32, "salt must be 32 bytes") 23 | 24 | const factoryHash = utils.parse.uint256("0x21c35dbe1b344a2488cf3321d6ce542f8e9f305544ff09e4993a62319a497c1f") 25 | const factoryAddr = keccak() 26 | .update(factoryPrefix) 27 | .update(address) 28 | .update(salt) 29 | .update(factoryHash) 30 | .digest() 31 | .slice(-20) 32 | 33 | assert(factoryAddr.byteLength === 20, "address must be 20 bytes") 34 | 35 | return utils.format.address(keccak() 36 | .update(Buffer.from([0xd6, 0x94])) 37 | .update(factoryAddr) 38 | .update(Buffer.from([0x01])) 39 | .digest() 40 | .slice(-20) 41 | ) 42 | } 43 | -------------------------------------------------------------------------------- /scripts/flatten.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-multi-str */ 2 | 3 | const exec = require("child_process").exec 4 | const fs = require("fs") 5 | const os = require("os") 6 | const path = require("path") 7 | 8 | if (process.argv.length < 3) { 9 | console.log("\n\ 10 | Usage: yarn flatten \n\ 11 | or: npm run flatten \n\n\ 12 | ") 13 | process.exit(0) 14 | } 15 | 16 | try { 17 | createFolder("./flattened") 18 | const stats = fs.lstatSync(process.argv[2]) 19 | if (stats.isFile()) { 20 | flatten(path.parse(process.argv[2]).dir, process.argv[2]) 21 | } else if (stats.isDirectory()) { 22 | const basedir = path.normalize(process.argv[2]).replace(/\\/g, "/") 23 | const files = fs.readdirSync(basedir).filter(filename => filename.endsWith(".sol")) 24 | files.forEach(filename => { 25 | flatten(basedir, filename) 26 | }) 27 | console.log(`\nProcessed ${files.length} Solidity files.`) 28 | } 29 | } catch (e) { 30 | console.error("Fatal:", e) 31 | process.exit(1) 32 | } 33 | 34 | /// //////////////////////////////////////////////////////////////////////////// 35 | 36 | function createFolder (folder) { 37 | if (!fs.existsSync(folder)) { 38 | if (os.type() === "Windows_NT") { 39 | folder = folder.replace(/\//g, "\\") 40 | exec(`mkdir ${folder}`) 41 | } else { 42 | exec(`mkdir -p ${folder}`) 43 | } 44 | } 45 | } 46 | 47 | function flatten (basedir, filepath) { 48 | const filename = path.parse(filepath).base 49 | const basename = path.parse(filepath).name 50 | const flattened = `flattened/${basename}/Flattened${basename}.sol` 51 | createFolder(`flattened/${basename}/`) 52 | if (fs.existsSync(flattened)) { 53 | console.log(`Skipping ${filename}: already flattened as '${flattened}'...`) 54 | } else { 55 | console.log(`Flattening ${filename} into '${flattened}'...`) 56 | exec(`npx truffle-flattener ${basedir}/${filename} > ${flattened}`) 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /scripts/migrate.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable camelcase */ 2 | /* eslint-disable new-cap */ 3 | /* eslint-disable no-multi-str */ 4 | /* eslint-disable no-template-curly-in-string */ 5 | 6 | require("dotenv").config() 7 | 8 | const settings = require("../migrations/witnet.settings") 9 | const utils = require("./utils") 10 | 11 | if (process.argv.length < 3) { 12 | console.log() 13 | console.log("\n\ 14 | Usage: yarn migrate <[Realm.]Network>\n\ 15 | or: npm run migrate <[Realm.]Network>\n\n\ 16 | ") 17 | process.exit(0) 18 | } 19 | 20 | const rn = utils.getRealmNetworkFromString(process.argv[2]) 21 | const realm = rn[0]; const network = rn[1] 22 | 23 | if (!settings.networks[realm] || !settings.networks[realm][network]) { 24 | console.error(`\n!!! Network "${network}" not found.\n`) 25 | if (settings.networks[realm]) { 26 | console.error(`> Available networks in realm "${realm}":`) 27 | console.error(settings.networks[realm]) 28 | } else { 29 | console.error("> Available networks:") 30 | console.error(settings.networks) 31 | } 32 | process.exit(1) 33 | } 34 | 35 | migrate(network) 36 | 37 | /// /////////////////////////////////////////////////////////////////////////////// 38 | 39 | async function migrate (network) { 40 | console.log( 41 | `> Migrating into "${realm}:${network}"...` 42 | ) 43 | await new Promise((resolve) => { 44 | const subprocess = require("child_process").spawn( 45 | "npx truffle", 46 | [ 47 | "migrate", 48 | "--reset", 49 | "--network", 50 | network, 51 | ], 52 | { 53 | shell: true, 54 | stdin: "inherit", 55 | } 56 | ) 57 | process.stdin.pipe(subprocess.stdin) 58 | subprocess.stdout.pipe(process.stdout) 59 | subprocess.stderr.pipe(process.stderr) 60 | subprocess.on("close", (code) => { 61 | if (code !== 0) { 62 | process.exit(code) 63 | } 64 | resolve(subprocess.stdout) 65 | }) 66 | }) 67 | } 68 | -------------------------------------------------------------------------------- /scripts/networks.js: -------------------------------------------------------------------------------- 1 | let networks = require("../migrations/witnet.settings").networks 2 | let realm, network 3 | if (process.argv.length >= 3) { 4 | network = process.argv[2].toLowerCase() 5 | realm = network.split(".")[0].toLowerCase() 6 | if (realm === "ethereum") realm = "default" 7 | if (!networks[realm]) { 8 | console.log("Unknown realm:", realm) 9 | process.exit(1) 10 | } 11 | if (network.split(".")[1]) { 12 | if (!networks[realm][network]) { 13 | console.log("Realm:", realm) 14 | console.log("Unknown network:", network) 15 | process.exit(1) 16 | } 17 | networks = networks[realm][network] 18 | } else { 19 | networks = networks[realm] 20 | } 21 | } 22 | console.log(networks) 23 | -------------------------------------------------------------------------------- /scripts/prepare.js: -------------------------------------------------------------------------------- 1 | const exec = require("child_process").execSync 2 | const os = require("os") 3 | const fs = require("fs") 4 | 5 | if (fs.existsSync("./artifacts")) { 6 | if (os.type() === "Windows_NT") { 7 | exec("del /s /q artifacts\\*.dbg.json") 8 | } else { 9 | exec("find ./artifacts -name \"*.dbg.json\" -exec rm -r {} \\;") 10 | } 11 | } 12 | 13 | if (fs.existsSync("./build/contracts")) { 14 | exec("sed -i -- \"/\bsourcePath\b/d\" build/contracts/*.json") 15 | } 16 | -------------------------------------------------------------------------------- /scripts/vanity2gen.js: -------------------------------------------------------------------------------- 1 | const create2 = require("./eth-create2") 2 | const fs = require("fs") 3 | const utils = require("../src/utils") 4 | 5 | const addresses = require("../migrations/addresses") 6 | 7 | module.exports = async function () { 8 | let artifact 9 | let count = 0 10 | let from 11 | let hits = 10 12 | let offset = 0 13 | let network = "default" 14 | let prefix = "0x00" 15 | let suffix = "00" 16 | let hexArgs = "" 17 | process.argv.map((argv, index, args) => { 18 | if (argv === "--offset") { 19 | offset = parseInt(args[index + 1]) 20 | } else if (argv === "--artifact") { 21 | artifact = artifacts.require(args[index + 1]) 22 | } else if (argv === "--prefix") { 23 | prefix = args[index + 1].toLowerCase() 24 | if (!web3.utils.isHexStrict(prefix)) { 25 | throw new Error("--prefix: invalid hex string") 26 | } 27 | } else if (argv === "--suffix") { 28 | suffix = args[index + 1].toLowerCase() 29 | if (!web3.utils.isHexStrict(suffix)) { 30 | throw new Error("--suffix: invalid hex string") 31 | } 32 | } else if (argv === "--hits") { 33 | hits = parseInt(args[index + 1]) 34 | } else if (argv === "--network") { 35 | [ecosystem, network] = utils.getRealmNetworkFromString(args[index + 1].toLowerCase()) 36 | } else if (argv === "--hexArgs") { 37 | hexArgs = args[index + 1].toLowerCase() 38 | if (hexArgs.startsWith("0x")) hexArgs = hexArgs.slice(2) 39 | if (!web3.utils.isHexStrict("0x" + hexArgs)) { 40 | throw new Error("--hexArgs: invalid hex string") 41 | } 42 | } else if (argv === "--from") { 43 | from = args[index + 1] 44 | } 45 | return argv 46 | }) 47 | try { 48 | from = from || addresses[network]?.WitnetDeployer || addresses.default.WitnetDeployer 49 | } catch { 50 | console.error(`WitnetDeployer must have been previously deployed on network '${network}'.\n`) 51 | console.info("Usage:\n") 52 | console.info(" --artifact => Truffle artifact name (mandatory)") 53 | console.info(" --hexArgs => Hexified constructor arguments") 54 | console.info(" --hits => Number of vanity hits to look for (default: 10)") 55 | console.info(" --network => Network name") 56 | console.info(" --offset => Salt starting value minus 1 (default: 0)") 57 | console.info(" --prefix => Prefix hex string to look for (default: 0x00)") 58 | console.info(" --suffix => suffix hex string to look for (default: 0x00)") 59 | process.exit(1) 60 | } 61 | if (!artifact) { 62 | console.error("No --artifact was set!") 63 | process.exit(1) 64 | } 65 | const initCode = artifact.toJSON().bytecode + hexArgs 66 | console.log("Init code: ", initCode) 67 | console.log("Artifact: ", artifact?.contractName) 68 | console.log("From: ", from) 69 | console.log("Hits: ", hits) 70 | console.log("Offset: ", offset) 71 | console.log("Prefix: ", prefix) 72 | console.log("Suffix: ", suffix) 73 | console.log("=".repeat(55)) 74 | suffix = suffix.slice(2) 75 | while (count < hits) { 76 | const salt = "0x" + utils.padLeft(offset.toString(16), "0", 32) 77 | const addr = create2(from, salt, initCode).toLowerCase() 78 | if (addr.startsWith(prefix) && addr.endsWith(suffix)) { 79 | const found = `${offset} => ${web3.utils.toChecksumAddress(addr)}` 80 | console.log(found) 81 | fs.appendFileSync(`./migrations/salts/${artifact?.contractName}$${from.toLowerCase()}.tmp`, found + "\n") 82 | count++ 83 | } 84 | offset++ 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /scripts/vanity3gen.js: -------------------------------------------------------------------------------- 1 | const create3 = require("./eth-create3") 2 | const fs = require("fs") 3 | const utils = require("../src/utils") 4 | 5 | const addresses = require("../migrations/addresses") 6 | 7 | module.exports = async function () { 8 | let count = 0 9 | let from 10 | let hits = 10 11 | let offset = 0 12 | let network = "default" 13 | let prefix = "0x00" 14 | let suffix = "0x00" 15 | process.argv.map((argv, index, args) => { 16 | if (argv === "--offset") { 17 | offset = parseInt(args[index + 1]) 18 | } else if (argv === "--prefix") { 19 | prefix = args[index + 1].toLowerCase() 20 | if (!web3.utils.isHexStrict(prefix)) { 21 | throw Error("--prefix: invalid hex string") 22 | } 23 | } else if (argv === "--suffix") { 24 | suffix = args[index + 1].toLowerCase() 25 | if (!web3.utils.isHexStrict(suffix)) { 26 | throw Error("--suffix: invalid hex string") 27 | } 28 | } else if (argv === "--hits") { 29 | hits = parseInt(args[index + 1]) 30 | } else if (argv === "--network") { 31 | [, network] = utils.getRealmNetworkFromString(args[index + 1].toLowerCase()) 32 | } else if (argv === "--from") { 33 | from = args[index + 1] 34 | } 35 | return argv 36 | }) 37 | try { 38 | from = from || addresses[network]?.WitnetDeployer || addresses.default.WitnetDeployer 39 | } catch { 40 | console.error(`WitnetDeployer must have been previously deployed on network '${network}'.\n`) 41 | console.info("Usage:\n") 42 | console.info(" --hits => Number of vanity hits to look for (default: 10)") 43 | console.info(" --network => Network name") 44 | console.info(" --offset => Salt starting value minus 1 (default: 0)") 45 | console.info(" --prefix => Prefix hex string to look for (default: 0x00)") 46 | console.info(" --suffix => suffix hex string to look for (default: 0x00)") 47 | process.exit(1) 48 | } 49 | console.log("From: ", from) 50 | console.log("Hits: ", hits) 51 | console.log("Offset: ", offset) 52 | console.log("Prefix: ", prefix) 53 | console.log("Suffix: ", suffix) 54 | console.log("=".repeat(55)) 55 | suffix = suffix.slice(2) 56 | while (count < hits) { 57 | const salt = "0x" + utils.padLeft(offset.toString(16), "0", 32) 58 | const addr = create3(from, salt).toLowerCase() 59 | if (addr.startsWith(prefix) && addr.endsWith(suffix)) { 60 | const found = `${offset} => ${web3.utils.toChecksumAddress(addr)}` 61 | console.log(found) 62 | fs.appendFileSync(`./migrations/salts/create3$${from.toLowerCase()}.tmp`, found + "\n") 63 | count++ 64 | } 65 | offset++ 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /scripts/verify-apps.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | const settings = require("../settings") 4 | const utils = require("../src/utils") 5 | 6 | if (process.argv.length < 3) { 7 | console.error("\nUsage:\n\n$ node ./scripts/verify-apps.js : ...OPTIONAL_ARGS\n") 8 | process.exit(0) 9 | } 10 | 11 | const network = process.argv[2].toLowerCase().replaceAll(".", ":") 12 | 13 | const header = network.toUpperCase() + " APPS" 14 | console.info() 15 | console.info(header) 16 | console.info("=".repeat(header.length)) 17 | console.info() 18 | 19 | const artifacts = settings.getArtifacts(network) 20 | const apps = [ 21 | artifacts.WitnetRandomness, 22 | ] 23 | const constructorArgs = require("../migrations/constructorArgs.json") 24 | if (!constructorArgs[network]) constructorArgs[network] = {} 25 | for (const index in apps) { 26 | utils.traceVerify(network, `${apps[index]} --forceConstructorArgs string:${ 27 | constructorArgs[network][apps[index]] || constructorArgs?.default[apps[index]] 28 | }`) 29 | } 30 | -------------------------------------------------------------------------------- /scripts/verify-core.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | const settings = require("../settings") 4 | const utils = require("../src/utils") 5 | 6 | if (process.argv.length < 3) { 7 | console.error("\nUsage:\n\n$ node ./scripts/verify-core.js : ...OPTIONAL_ARGS\n") 8 | process.exit(0) 9 | } 10 | 11 | const network = process.argv[2].toLowerCase().replaceAll(".", ":") 12 | 13 | const header = network.toUpperCase() + " CORE" 14 | console.info() 15 | console.info(header) 16 | console.info("=".repeat(header.length)) 17 | console.info() 18 | 19 | utils.traceVerify(network, settings.getArtifacts(network).WitnetDeployer) 20 | utils.traceVerify(network, "WitnetProxy") 21 | 22 | const addresses = require("../migrations/addresses.json") 23 | const singletons = [ 24 | "WitnetOracle", 25 | "WitnetPriceFeeds", 26 | "WitnetRequestBytecodes", 27 | "WitnetRequestFactory", 28 | ] 29 | for (const index in singletons) { 30 | utils.traceVerify(network, `WitnetProxy@${ 31 | addresses[network][singletons[index]] || addresses.default[singletons[index]] 32 | } --custom-proxy WitnetProxy`) 33 | } 34 | -------------------------------------------------------------------------------- /scripts/verify-impls.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | const settings = require("../settings") 4 | const utils = require("../src/utils") 5 | 6 | if (process.argv.length < 3) { 7 | console.error("\nUsage:\n\n$ node ./scripts/verify-impls.js : ...OPTIONAL_ARGS\n") 8 | process.exit(0) 9 | } 10 | 11 | const network = process.argv[2].toLowerCase().replaceAll(".", ":") 12 | 13 | const header = network.toUpperCase() 14 | console.info() 15 | console.info(header) 16 | console.info("=".repeat(header.length)) 17 | console.info() 18 | 19 | const artifacts = settings.getArtifacts(network) 20 | const impls = [ 21 | artifacts.WitnetOracle, 22 | artifacts.WitnetPriceFeeds, 23 | artifacts.WitnetRequestBytecodes, 24 | artifacts.WitnetRequestFactory, 25 | ] 26 | const constructorArgs = require("../migrations/constructorArgs.json") 27 | if (!constructorArgs[network]) constructorArgs[network] = {} 28 | for (const index in impls) { 29 | utils.traceVerify(network, `${impls[index]} --forceConstructorArgs string:${ 30 | constructorArgs[network][impls[index]] || constructorArgs?.default[impls[index]] 31 | } --verifiers etherscan,sourcify`) 32 | } 33 | -------------------------------------------------------------------------------- /scripts/verify-libs.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | const settings = require("../settings") 4 | const utils = require("../src/utils") 5 | 6 | if (process.argv.length < 3) { 7 | console.error("\nUsage:\n\n$ node ./scripts/verify-libs.js : ...OPTIONAL_ARGS\n") 8 | process.exit(0) 9 | } 10 | 11 | const network = process.argv[2].toLowerCase().replaceAll(".", ":") 12 | 13 | const header = network.toUpperCase() + " LIBS" 14 | console.info() 15 | console.info(header) 16 | console.info("=".repeat(header.length)) 17 | console.info() 18 | 19 | const addresses = require("../migrations/addresses.json") 20 | const artifacts = settings.getArtifacts(network) 21 | const libs = [ 22 | artifacts.WitnetEncodingLib, 23 | artifacts.WitnetErrorsLib, 24 | artifacts.WitnetOracleDataLib, 25 | artifacts.WitnetPriceFeedsLib, 26 | ] 27 | for (const index in libs) { 28 | utils.traceVerify(network, `${libs[index]}@${addresses[network][libs[index]]}`) 29 | } 30 | -------------------------------------------------------------------------------- /settings/artifacts.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | default: { 3 | WitnetDeployer: "WitnetDeployer", 4 | WitnetOracle: "WitnetOracleTrustableDefault", 5 | WitnetPriceFeeds: "WitnetPriceFeedsDefault", 6 | WitnetRandomness: "WitnetRandomnessV2", 7 | WitnetRequestBytecodes: "WitnetRequestBytecodesDefault", 8 | WitnetRequestFactory: "WitnetRequestFactoryDefault", 9 | WitnetEncodingLib: "WitnetEncodingLib", 10 | WitnetErrorsLib: "WitnetErrorsLib", 11 | WitnetPriceFeedsLib: "WitnetPriceFeedsLib", 12 | WitnetOracleDataLib: "WitnetOracleDataLib", 13 | }, 14 | base: { 15 | WitnetOracle: "WitnetOracleTrustableOvm2", 16 | }, 17 | boba: { 18 | WitnetOracle: "WitnetOracleTrustableOvm2", 19 | }, 20 | "conflux:core:testnet": { 21 | WitnetDeployer: "WitnetDeployerCfxCore", 22 | WitnetRequestFactory: "WitnetRequestFactoryCfxCore", 23 | }, 24 | "conflux:core:mainnet": { 25 | WitnetDeployer: "WitnetDeployerCfxCore", 26 | WitnetRequestFactory: "WitnetRequestFactoryCfxCore", 27 | }, 28 | mantle: { 29 | WitnetOracle: "WitnetOracleTrustableOvm2", 30 | }, 31 | meter: { 32 | WitnetDeployer: "WitnetDeployerMeter", 33 | WitnetRequestFactory: "WitnetRequestFactoryCfxCore", 34 | }, 35 | optimism: { 36 | WitnetOracle: "WitnetOracleTrustableOvm2", 37 | }, 38 | "okx:x1:mainnet": { 39 | WitnetRequestBytecodes: "WitnetRequestBytecodesNoSha256", 40 | }, 41 | "okx:xlayer:sepolia": { 42 | WitnetRequestBytecodes: "WitnetRequestBytecodesNoSha256", 43 | }, 44 | "polygon:zkevm:goerli": { 45 | WitnetRequestBytecodes: "WitnetRequestBytecodesNoSha256", 46 | }, 47 | "polygon:zkevm:mainnet": { 48 | WitnetRequestBytecodes: "WitnetRequestBytecodesNoSha256", 49 | }, 50 | scroll: { 51 | WitnetRequestBytecodes: "WitnetRequestBytecodesNoSha256", 52 | }, 53 | "syscoin:rollux:mainnet": { 54 | WitnetOracle: "WitnetOracleTrustableOvm2", 55 | }, 56 | ten: { 57 | WitnetOracle: "WitnetOracleTrustableObscuro", 58 | }, 59 | worldchain: { 60 | WitnetOracle: "WitnetOracleTrustableOvm2", 61 | }, 62 | } 63 | -------------------------------------------------------------------------------- /settings/index.js: -------------------------------------------------------------------------------- 1 | const artifacts = require("./artifacts") 2 | const merge = require("lodash.merge") 3 | const networks = require("./networks") 4 | const specs = require("./specs") 5 | const solidity = require("./solidity") 6 | const utils = require("../src/utils") 7 | 8 | module.exports = { 9 | getArtifacts: (network) => { 10 | const [eco, net] = utils.getRealmNetworkFromString(network) 11 | return merge( 12 | artifacts.default, 13 | artifacts[eco], 14 | artifacts[net] 15 | ) 16 | }, 17 | getCompilers: (network) => { 18 | const [eco, net] = utils.getRealmNetworkFromString(network) 19 | return merge( 20 | solidity.default, 21 | solidity[eco], 22 | solidity[net], 23 | ) 24 | }, 25 | getNetworks: () => { 26 | return Object.fromEntries(Object.entries(networks) 27 | .filter(entry => entry[0].indexOf(":") > -1) 28 | .map(entry => { 29 | const [ecosystem, network] = utils.getRealmNetworkFromString(entry[0]) 30 | return [ 31 | network.toLowerCase(), merge( 32 | { ...networks.default }, 33 | networks[ecosystem], 34 | networks[entry[0]] 35 | ), 36 | ] 37 | }) 38 | ) 39 | }, 40 | getSpecs: (network) => { 41 | const [eco, net] = utils.getRealmNetworkFromString(network) 42 | return merge( 43 | specs.default, 44 | specs[eco], 45 | specs[net] 46 | ) 47 | }, 48 | artifacts, 49 | solidity, 50 | specs, 51 | } 52 | -------------------------------------------------------------------------------- /settings/solidity.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | default: { 3 | version: "0.8.25", 4 | settings: { 5 | optimizer: { 6 | enabled: true, 7 | runs: 200, 8 | }, 9 | evmVersion: "paris", 10 | }, 11 | }, 12 | } 13 | -------------------------------------------------------------------------------- /settings/specs.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | default: { 3 | WitnetOracle: { 4 | immutables: { 5 | types: ["uint256", "uint256", "uint256", "uint256"], 6 | values: [ 7 | /* _reportResultGasBase */ 58282, 8 | /* _reportResultWithCallbackGasBase */ 65273, 9 | /* _reportResultWithCallbackRevertGasBase */ 69546, 10 | /* _sstoreFromZeroGas */ 20000, 11 | ], 12 | }, 13 | libs: ["WitnetErrorsLib", "WitnetOracleDataLib"], 14 | vanity: 13710368043, // 0x77703aE126B971c9946d562F41Dd47071dA00777 15 | }, 16 | WitnetPriceFeeds: { 17 | from: "0xF121b71715E71DDeD592F1125a06D4ED06F0694D", 18 | libs: ["WitnetPriceFeedsLib"], 19 | vanity: 1865150170, // 0x1111AbA2164AcdC6D291b08DfB374280035E1111 20 | }, 21 | WitnetRandomness: { 22 | from: "0xF121b71715E71DDeD592F1125a06D4ED06F0694D", 23 | vanity: 1060132513, // 0xC0FFEE98AD1434aCbDB894BbB752e138c1006fAB 24 | }, 25 | WitnetRequestBytecodes: { 26 | libs: ["WitnetEncodingLib"], 27 | vanity: 6765579443, // 0x000B61Fe075F545fd37767f40391658275900000 28 | }, 29 | WitnetRequestFactory: { 30 | vanity: 1240014136, // 0x000DB36997AF1F02209A6F995883B9B699900000 31 | }, 32 | }, 33 | "conflux:core:mainnet": { 34 | WitnetDeployer: { 35 | from: "0x1169bf81ecf738d02fd8d3824dfe02153b334ef7", 36 | }, 37 | WitnetOracle: { 38 | vanity: 3, 39 | }, 40 | WitnetPriceFeeds: { 41 | from: "0x1169bf81ecf738d02fd8d3824dfe02153b334ef7", 42 | vanity: 4, 43 | }, 44 | WitnetRandomness: { 45 | from: "0x1169bf81ecf738d02fd8d3824dfe02153b334ef7", 46 | vanity: 5, 47 | }, 48 | WitnetRequestBytecodes: { 49 | vanity: 1, 50 | }, 51 | WitnetRequestFactory: { 52 | vanity: 2, 53 | }, 54 | }, 55 | "conflux:core:testnet": { 56 | WitnetDeployer: { 57 | from: "0x1169Bf81ecf738d02fd8d3824dfe02153B334eF7", 58 | }, 59 | WitnetOracle: { 60 | vanity: 3, 61 | }, 62 | WitnetPriceFeeds: { 63 | from: "0x1169Bf81ecf738d02fd8d3824dfe02153B334eF7", 64 | vanity: 4, 65 | }, 66 | WitnetRandomness: { 67 | from: "0x1169Bf81ecf738d02fd8d3824dfe02153B334eF7", 68 | vanity: 5, 69 | }, 70 | WitnetRequestBytecodes: { 71 | vanity: 1, 72 | }, 73 | WitnetRequestFactory: { 74 | vanity: 2, 75 | }, 76 | }, 77 | meter: { 78 | WitnetDeployer: { 79 | from: "0xE169Bf81Ecf738d02fD8d3824DFe02153b334eF7", 80 | }, 81 | WitnetPriceFeeds: { 82 | from: "0xE169Bf81Ecf738d02fD8d3824DFe02153b334eF7", 83 | }, 84 | WitnetRandomness: { 85 | from: "0xE169Bf81Ecf738d02fD8d3824DFe02153b334eF7", 86 | }, 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | const addresses = require("../migrations/addresses.json") 2 | const merge = require("lodash.merge") 3 | const utils = require("./utils") 4 | module.exports = { 5 | getAddresses: (network) => { 6 | const [eco, net] = utils.getRealmNetworkFromString(network) 7 | if (addresses[net]) { 8 | const merged = merge( 9 | addresses.default, 10 | addresses[eco], 11 | addresses[net], 12 | ) 13 | return { 14 | WitnetOracle: merged?.WitnetOracle, 15 | WitnetPriceFeeds: merged?.WitnetPriceFeeds, 16 | WitnetRandomnessV2: merged?.WitnetRandomnessV2, 17 | } 18 | } else { 19 | return {} 20 | } 21 | }, 22 | supportedEcosystems: () => { 23 | const ecosystems = [] 24 | supportedNetworks().forEach(network => { 25 | const [ecosystem] = utils.getRealmNetworkFromString(network) 26 | if (!ecosystems.includes(ecosystem)) { 27 | ecosystems.push(ecosystem) 28 | } 29 | }) 30 | return ecosystems 31 | }, 32 | supportedNetworks, 33 | artifacts: { 34 | WitnetOracle: require("../artifacts/contracts/WitnetOracle.sol/WitnetOracle.json"), 35 | WitnetPriceFeeds: require("../artifacts/contracts/WitnetPriceFeeds.sol/WitnetPriceFeeds.json"), 36 | WitnetPriceRouteSolver: require("../artifacts/contracts/interfaces/IWitnetPriceSolver.sol/IWitnetPriceSolver.json"), 37 | WitnetRandomness: require("../artifacts/contracts/WitnetRandomness.sol/WitnetRandomness.json"), 38 | WitnetRequest: require("../artifacts/contracts/WitnetRequest.sol/WitnetRequest.json"), 39 | WitnetRequestBytecodes: require("../artifacts/contracts/WitnetRequestBytecodes.sol/WitnetRequestBytecodes.json"), 40 | WitnetRequestFactory: require("../artifacts/contracts/WitnetRequestFactory.sol/WitnetRequestFactory.json"), 41 | WitnetRequestTemplate: require("../artifacts/contracts/WitnetRequestTemplate.sol/WitnetRequestTemplate.json"), 42 | WitnetUpgradableBase: require("../artifacts/contracts/core/WitnetUpgradableBase.sol/WitnetUpgradableBase.json"), 43 | }, 44 | settings: require("../settings"), 45 | utils, 46 | } 47 | 48 | function supportedNetworks (ecosystem) { 49 | return Object 50 | .entries(addresses) 51 | .filter(value => value[0].indexOf(":") > -1 && (!ecosystem || value[0].startsWith(ecosystem))) 52 | .map(value => value[0]) 53 | .sort() 54 | } 55 | -------------------------------------------------------------------------------- /src/utils.js: -------------------------------------------------------------------------------- 1 | const execSync = require("child_process").execSync 2 | const fs = require("fs") 3 | require("dotenv").config() 4 | const lockfile = require("proper-lockfile") 5 | const readline = require("readline") 6 | 7 | module.exports = { 8 | fromAscii, 9 | getRealmNetworkFromArgs, 10 | getRealmNetworkFromString, 11 | getWitnetArtifactsFromArgs, 12 | getWitnetRequestMethodString, 13 | isDryRun, 14 | isNullAddress, 15 | padLeft, 16 | prompt, 17 | readJsonFromFile, 18 | overwriteJsonFile, 19 | traceHeader, 20 | traceTx, 21 | traceVerify, 22 | } 23 | 24 | function fromAscii (str) { 25 | const arr1 = [] 26 | for (let n = 0, l = str.length; n < l; n++) { 27 | const hex = Number(str.charCodeAt(n)).toString(16) 28 | arr1.push(hex) 29 | } 30 | return "0x" + arr1.join("") 31 | } 32 | 33 | function getRealmNetworkFromArgs () { 34 | let networkString = process.env.WSB_DEFAULT_CHAIN || process.argv.includes("test") ? "test" : "development" 35 | // If a `--network` argument is provided, use that instead 36 | const args = process.argv.join("=").split("=") 37 | const networkIndex = args.indexOf("--network") 38 | if (networkIndex >= 0) { 39 | networkString = args[networkIndex + 1] 40 | } 41 | return getRealmNetworkFromString(networkString) 42 | } 43 | 44 | function getRealmNetworkFromString (network) { 45 | network = network ? network.toLowerCase() : "development" 46 | if (network.indexOf(":") > -1) { 47 | return [network.split(":")[0], network] 48 | } else { 49 | return [null, network] 50 | } 51 | } 52 | 53 | function getWitnetRequestMethodString (method) { 54 | if (!method) { 55 | return "HTTP-GET" 56 | } else { 57 | const strings = { 58 | 0: "UNKNOWN", 59 | 1: "HTTP-GET", 60 | 2: "RNG", 61 | 3: "HTTP-POST", 62 | 4: "HTTP-HEAD", 63 | } 64 | return strings[method] || method.toString() 65 | } 66 | } 67 | 68 | function getWitnetArtifactsFromArgs () { 69 | let selection = [] 70 | process.argv.map((argv, index, args) => { 71 | if (argv === "--artifacts") { 72 | selection = args[index + 1].split(",") 73 | } 74 | return argv 75 | }) 76 | return selection 77 | }; 78 | 79 | function isDryRun (network) { 80 | return network === "test" || network.split("-")[1] === "fork" || network.split("-")[0] === "develop" 81 | } 82 | 83 | function isNullAddress (addr) { 84 | return !addr || 85 | addr === "" || 86 | addr === "0x0000000000000000000000000000000000000000" 87 | } 88 | 89 | function padLeft (str, char, size) { 90 | if (str.length < size) { 91 | return char.repeat((size - str.length) / char.length) + str 92 | } else { 93 | return str 94 | } 95 | } 96 | 97 | async function prompt (text) { 98 | const rl = readline.createInterface({ 99 | input: process.stdin, 100 | output: process.stdout, 101 | }) 102 | let answer 103 | await new Promise((resolve) => { 104 | rl.question( 105 | text, 106 | function (input) { 107 | answer = input 108 | rl.close() 109 | }) 110 | rl.on("close", function () { 111 | resolve() 112 | }) 113 | }) 114 | return answer 115 | } 116 | 117 | async function readJsonFromFile (filename) { 118 | lockfile.lockSync(filename) 119 | const json = JSON.parse(await fs.readFileSync(filename)) 120 | lockfile.unlockSync(filename) 121 | return json || {} 122 | } 123 | 124 | async function overwriteJsonFile (filename, extra) { 125 | lockfile.lockSync(filename) 126 | const json = { ...JSON.parse(fs.readFileSync(filename)), ...extra } 127 | fs.writeFileSync(filename, JSON.stringify(json, null, 4), { flag: "w+" }) 128 | lockfile.unlockSync(filename) 129 | } 130 | 131 | function traceHeader (header) { 132 | console.log("") 133 | console.log(" ", header) 134 | console.log(" ", `${"-".repeat(header.length)}`) 135 | } 136 | 137 | function traceTx (tx) { 138 | console.info(" ", "> transaction hash: ", tx.receipt.transactionHash) 139 | console.info(" ", "> gas used: ", tx.receipt.gasUsed.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",")) 140 | if (tx.receipt?.effectiveGasPrice) { 141 | console.info(" ", "> gas price: ", tx.receipt.effectiveGasPrice / 10 ** 9, "gwei") 142 | console.info(" ", "> total cost: ", parseFloat( 143 | BigInt(tx.receipt.gasUsed) * 144 | BigInt(tx.receipt.effectiveGasPrice) / 145 | BigInt(10 ** 18) 146 | ).toString(), 147 | "ETH" 148 | ) 149 | } 150 | } 151 | 152 | function traceVerify (network, verifyArgs) { 153 | console.log( 154 | execSync( 155 | `npx truffle run verify --network ${network} ${verifyArgs} ${process.argv.slice(3)}`, 156 | { stdout: "inherit" } 157 | ).toString().split("\n") 158 | .join("\n") 159 | ) 160 | } 161 | -------------------------------------------------------------------------------- /test/TestWitnetEncodingLib.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity >=0.7.0 <0.9.0; 4 | pragma experimental ABIEncoderV2; 5 | 6 | import "truffle/Assert.sol"; 7 | import "../contracts/libs/WitnetEncodingLib.sol"; 8 | 9 | contract TestWitnetEncodingLib { 10 | 11 | using WitnetEncodingLib for string; 12 | 13 | // event Log(bytes data); 14 | // event Log(bytes data, uint256 length); 15 | 16 | function testEncodeTaggedVarint() external { 17 | bytes memory bytecode = WitnetEncodingLib.encode(10 ** 6, bytes1(0x10)); 18 | Assert.equal( 19 | keccak256(bytecode), 20 | keccak256(hex"10c0843d"), 21 | "bad encode(uint64,bytes1)" 22 | ); 23 | } 24 | 25 | function testEncodeString() external { 26 | bytes memory bytecode = WitnetEncodingLib.encode(string("witnet")); 27 | Assert.equal( 28 | keccak256(bytecode), 29 | keccak256(hex"667769746E6574"), 30 | "bad encode(string)" 31 | ); 32 | } 33 | 34 | function testEncodeBytes() external { 35 | bytes memory bytecode = WitnetEncodingLib.encode(bytes("witnet")); 36 | Assert.equal( 37 | keccak256(bytecode), 38 | keccak256(hex"467769746E6574"), 39 | "bad encode(bytes)" 40 | ); 41 | } 42 | 43 | function testEncodeRadonReducerOpcodes() external { 44 | bytes memory bytecode = WitnetEncodingLib.encode( 45 | Witnet.RadonReducerOpcodes.StandardDeviation 46 | ); 47 | Assert.equal( 48 | keccak256(bytecode), 49 | keccak256(hex"1007"), 50 | "bad encode(Witnet.RadonReducerOpcodes)" 51 | ); 52 | } 53 | 54 | function testEncodeRadonSLA() external { 55 | bytes memory bytecode = WitnetEncodingLib.encode( 56 | Witnet.RadonSLA({ 57 | numWitnesses: 10, 58 | minConsensusPercentage: 51, 59 | minerCommitRevealFee: 1000000, 60 | witnessCollateral: 5000000, 61 | witnessReward: 1000000 62 | }) 63 | ); 64 | // emit Log(bytecode); 65 | Assert.equal( 66 | keccak256(bytecode), 67 | keccak256(hex"10c0843d180a20c0843d283330c096b102"), 68 | "bad encode(Witnet.RadonSLA)" 69 | ); 70 | } 71 | 72 | function testEncodeRadonReducer1Filter() external { 73 | Witnet.RadonReducer memory reducer; 74 | reducer.opcode = Witnet.RadonReducerOpcodes.Mode; 75 | reducer.filters = new Witnet.RadonFilter[](1); 76 | reducer.filters[0].opcode = Witnet.RadonFilterOpcodes.StandardDeviation; 77 | reducer.filters[0].args = hex"fa40200000"; 78 | bytes memory bytecode = WitnetEncodingLib.encode(reducer); 79 | // emit Log(bytecode); 80 | Assert.equal( 81 | keccak256(bytecode), 82 | keccak256(hex"0a0908051205fa402000001002"), 83 | "bad encode(Witnet.RadonReducer)" 84 | ); 85 | } 86 | 87 | function testEncodeRadonRetrievalUrlOnly() external { 88 | Witnet.RadonRetrieval memory source; 89 | source.method = Witnet.RadonDataRequestMethods.HttpGet; 90 | source.url = "https://data.messar.io/api/v1/assets/\\0\\/metrics/market-data?fields=market_data/price_\\1\\"; 91 | source.script = hex"861877821866646461746182186664706f6f6c8218646b746f6b656e3150726963658218571a000f4240185b"; 92 | bytes memory bytecode = WitnetEncodingLib.encode(source); 93 | // emit Log(bytecode); 94 | Assert.equal( 95 | keccak256(bytecode), 96 | keccak256(hex"128b010801125968747470733a2f2f646174612e6d65737361722e696f2f6170692f76312f6173736574732f5c305c2f6d6574726963732f6d61726b65742d646174613f6669656c64733d6d61726b65745f646174612f70726963655f5c315c1a2c861877821866646461746182186664706f6f6c8218646b746f6b656e3150726963658218571a000f4240185b"), 97 | "bad encode(Witnet.RadonRetrieval)" 98 | ); 99 | } 100 | 101 | function testEncodeRadonRetrievalUrlBodyHeaders() external { 102 | Witnet.RadonRetrieval memory source; 103 | source.method = Witnet.RadonDataRequestMethods.HttpPost; 104 | source.url = "https://api.thegraph.com/subgraphs/name/uniswap/uniswap-v3"; 105 | source.body = "{\"query\":\"{pool(id:\\\"0xc2a856c3aff2110c1171b8f942256d40e980c726\\\"){token1Price}}\"}"; 106 | source.headers = new string[2][](2); 107 | source.headers[0] = [ "user-agent", "witnet-rust" ]; 108 | source.headers[1] = [ "content-type", "text/html; charset=utf-8" ]; 109 | source.script = hex"861877821866646461746182186664706f6f6c8218646b746f6b656e3150726963658218571a000f4240185b"; 110 | bytes memory bytecode = WitnetEncodingLib.encode(source); 111 | // emit Log(bytecode); 112 | Assert.equal( 113 | keccak256(bytecode), 114 | keccak256( 115 | hex"1285020803123a68747470733a2f2f6170692e74686567726170682e636f6d2f7375626772617068732f6e616d652f756e69737761702f756e69737761702d76331a2c861877821866646461746182186664706f6f6c8218646b746f6b656e3150726963658218571a000f4240185b22527b227175657279223a227b706f6f6c2869643a5c223078633261383536633361666632313130633131373162386639343232353664343065393830633732365c22297b746f6b656e3150726963657d7d227d2a190a0a757365722d6167656e74120b7769746e65742d727573742a280a0c636f6e74656e742d747970651218746578742f68746d6c3b20636861727365743d7574662d38"), 116 | "bad encode(Witnet.RadonRetrieval)" 117 | ); 118 | } 119 | 120 | function testReplaceCborStringsFromBytes() external { 121 | bytes memory radon = hex"861877821866646461746182186664706F6F6C821864635C305C8218571A000F4240185B"; 122 | // emit Log(radon, radon.length); 123 | string[] memory args = new string[](1); 124 | args[0] = "token1Price"; 125 | bytes memory newradon = WitnetEncodingLib.replaceCborStringsFromBytes(radon, args); 126 | // emit Log(newradon, newradon.length); 127 | Assert.equal( 128 | keccak256(newradon), 129 | keccak256(hex'861877821866646461746182186664706F6F6C8218646B746F6B656E3150726963658218571A000F4240185B'), 130 | "not good :/" 131 | ); 132 | } 133 | 134 | function testVerifyRadonScriptOk1() external { 135 | Assert.equal( 136 | uint(WitnetEncodingLib.verifyRadonScriptResultDataType(hex"861877821866646461746182186664706f6f6c8218646b746f6b656e3150726963658218571a000f4240185b")), 137 | uint(Witnet.RadonDataTypes.Integer), 138 | "unexpected result data type" 139 | ); 140 | } 141 | 142 | function testVerifyRadonScriptOk2() external { 143 | Assert.equal( 144 | uint(WitnetEncodingLib.verifyRadonScriptResultDataType(hex"80")), 145 | uint(Witnet.RadonDataTypes.Any), 146 | "unexpected result data type" 147 | ); 148 | } 149 | 150 | function testVerifyRadonScriptOk3() external { 151 | Assert.equal( 152 | uint(WitnetEncodingLib.verifyRadonScriptResultDataType(hex"8218778218676445746167")), 153 | uint(Witnet.RadonDataTypes.String), 154 | "unexpected result data type" 155 | ); 156 | } 157 | 158 | function testVerifyRadonScriptOk4() external { 159 | Assert.equal( 160 | uint(WitnetEncodingLib.verifyRadonScriptResultDataType(hex"880B821866646461746182186165706169727382118282186762696483187582635C305CF5F4821818F48218646D746F6B656E5C315C50726963658218571A000F4240185B")), 161 | uint(Witnet.RadonDataTypes.Integer), 162 | "unexpected result data type" 163 | ); 164 | } 165 | 166 | function testVerifyRadonScriptOk5() external { 167 | Assert.equal( 168 | uint(WitnetEncodingLib.verifyRadonScriptResultDataType(hex"851876821182821867657469746c65831875a1635c315cf5f4821183821867696d65726765645f617418748218430082181800821867657374617465")), 169 | uint(Witnet.RadonDataTypes.String), 170 | "unexpected result data type" 171 | ); 172 | } 173 | } -------------------------------------------------------------------------------- /test/TestWitnetV2.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity >=0.7.0 <0.9.0; 4 | pragma experimental ABIEncoderV2; 5 | 6 | import "truffle/Assert.sol"; 7 | import "../contracts/libs/WitnetV2.sol"; 8 | 9 | contract TestWitnetV2 { 10 | using WitnetV2 for *; 11 | 12 | 13 | WitnetV2.Request internal __request; 14 | WitnetV2.Response internal __response; 15 | 16 | uint256 internal __finalityBlock; 17 | 18 | function testNOP() external {} 19 | 20 | function testSSTORE() external { 21 | __finalityBlock = block.number; 22 | } 23 | 24 | function testWitnetRequestPackingWithBytecode() external { 25 | __request = WitnetV2.Request({ 26 | requester: msg.sender, 27 | gasCallback: 500000, 28 | evmReward: 10 ** 18, 29 | witnetBytecode: hex"0aab0412520801123268747470733a2f2f6170692e62696e616e63652e55532f6170692f76332f7469636b65723f73796d626f6c3d4254435553441a1a841877821864696c61737450726963658218571a000f4240185b124d0801122c68747470733a2f2f6170692e62697466696e65782e636f6d2f76312f7075627469636b65722f6274637573641a1b8418778218646a6c6173745f70726963658218571a000f4240185b12480801122d68747470733a2f2f7777772e6269747374616d702e6e65742f6170692f76322f7469636b65722f6274637573641a15841877821864646c6173748218571a000f4240185b12550801123168747470733a2f2f6170692e626974747265782e636f6d2f76332f6d61726b6574732f4254432d5553442f7469636b65721a1e8418778218646d6c6173745472616465526174658218571a000f4240185b12620801123768747470733a2f2f6170692e636f696e626173652e636f6d2f76322f65786368616e67652d72617465733f63757272656e63793d4254431a258618778218666464617461821866657261746573821864635553448218571a000f4240185b12630801123268747470733a2f2f6170692e6b72616b656e2e636f6d2f302f7075626c69632f5469636b65723f706169723d4254435553441a2b87187782186666726573756c7482186668585842545a55534482186161618216008218571a000f4240185b1a0d0a0908051205fa3fc000001003220d0a0908051205fa4020000010031080a3c347180a2080ade20428333080acc7f037", 30 | witnetRAD: bytes32(0), 31 | witnetSLA: WitnetV2.RadonSLA({ 32 | committeeSize: 7, 33 | witnessingFeeNanoWit: 10 ** 9 34 | }) 35 | }); 36 | } 37 | 38 | function testWitnetRequestUnpackingWithBytecode() external returns (WitnetV2.Request memory) { 39 | return __request; 40 | } 41 | 42 | function testWitnetRequestPackingWithRadHash() external { 43 | __request = WitnetV2.Request({ 44 | requester: msg.sender, 45 | gasCallback: 500000, 46 | evmReward: 10 ** 18, 47 | witnetBytecode: hex"", 48 | witnetRAD: bytes32(bytes2(0x1234)), 49 | witnetSLA: WitnetV2.RadonSLA({ 50 | committeeSize: 7, 51 | witnessingFeeNanoWit: 10 ** 9 52 | }) 53 | }); 54 | } 55 | 56 | function testWitnetRequestUnpackingWithRadHash() external returns (WitnetV2.Request memory) { 57 | return __request; 58 | } 59 | 60 | function testWitnetResponsePacking() external { 61 | __response = WitnetV2.Response({ 62 | reporter: msg.sender, 63 | finality: uint64(block.number), 64 | resultTimestamp: uint32(block.timestamp), 65 | resultTallyHash: blockhash(block.number - 1), 66 | resultCborBytes: hex"010203040506" 67 | }); 68 | } 69 | 70 | function testWitnetResponseUnpacking() external returns (WitnetV2.Response memory) { 71 | return __response; 72 | } 73 | 74 | } 75 | -------------------------------------------------------------------------------- /truffle-config.js: -------------------------------------------------------------------------------- 1 | const settings = require("./settings") 2 | const utils = require("./src/utils") 3 | 4 | const { ecosystem, network } = utils.getRealmNetworkFromArgs() 5 | if (ecosystem) { 6 | const header = console.info(`${ecosystem.toUpperCase()}`) 7 | console.info(header) 8 | console.info("=".repeat(header.length)) 9 | } 10 | 11 | module.exports = { 12 | build_directory: "./build/", 13 | contracts_directory: "./contracts/", 14 | migrations_directory: "./migrations/scripts/", 15 | networks: settings.getNetworks(network), 16 | compilers: { 17 | solc: settings.getCompilers(network), 18 | }, 19 | mocha: { 20 | reporter: "eth-gas-reporter", 21 | reporterOptions: { 22 | coinmarketcap: process.env.COINMARKETCAP_API_KEY, 23 | currency: "USD", 24 | gasPrice: 100, 25 | excludeContracts: ["Migrations"], 26 | src: "contracts", 27 | }, 28 | timeout: 300000, 29 | useColors: true, 30 | }, 31 | plugins: [ 32 | "truffle-plugin-verify", 33 | ], 34 | api_keys: { 35 | arbiscan: process.env.ETHERSCAN_ARBISCAN_API_KEY, 36 | basescan: process.env.ETHERSCAN_BASESCAN_API_KEY, 37 | bobascan: process.env.BOBASCAN_API_KEY, 38 | celoscan: process.env.ETHERSCAN_CELO_API_KEY, 39 | cronos: process.env.ETHERSCAN_CRONOS_API_KEY, 40 | cronoscan: process.env.ETHERSCAN_CRONOSCAN_API_KEY, 41 | elastos: process.env.ETHERSCAN_ELASTOS_API_KEY, 42 | etherscan: process.env.ETHERSCAN_API_KEY, 43 | gnosisscan: process.env.ETHERSCAN_GNOSIS_API_KEY, 44 | kcc: process.env.ETHERSCAN_KCC_API_KEY, 45 | mantle: process.env.ETHERSCAN_MANTLE_API_KEY, 46 | moonscan: process.env.ETHERSCAN_MOONBEAM_API_KEY, 47 | oklink: process.env.ETHERSCAN_OKLINK_API_KEY, 48 | okx: process.env.ETHERSCAN_OKLINK_API_KEY, 49 | optimistic_etherscan: process.env.ETHERSCAN_OPTIMISM_API_KEY, 50 | polygonscan: process.env.ETHERSCAN_POLYGON_API_KEY, 51 | routescan: process.env.ETHERSCAN_ROUTESCAN_API_KEY, 52 | scrollscan: process.env.ETHERSCAN_SCROLL_API_KEY, 53 | uniscan: process.env.ETHERSCAN_UNISCAN_API_KEY, 54 | }, 55 | } 56 | --------------------------------------------------------------------------------