WitPriceFeedsDataLib

Description:

Proxy contract enabling upgradeable smart contract patterns. Delegates calls to an implementation contract.

Blockchain: Ethereum

Source Code: View Code On The Blockchain

Solidity Source Code:

{{
  "language": "Solidity",
  "sources": {
    "/contracts/data/WitPriceFeedsDataLib.sol": {
      "content": "// SPDX-License-Identifier: MIT\r
\r
pragma solidity >=0.8.0 <0.9.0;\r
\r
import "ado-contracts/contracts/interfaces/IERC2362.sol";\r
\r
import "../libs/Witnet.sol";\r
\r
import "../interfaces/IWitOracle.sol";\r
import "../interfaces/IWitOracleRadonRegistry.sol";\r
import "../interfaces/IWitPriceFeeds.sol";\r
import "../interfaces/IWitPriceFeedsAdmin.sol";\r
import "../interfaces/IWitPriceFeedsMappingSolver.sol";\r
\r
import "../libs/Slices.sol";\r
\r
/// @title WitPriceFeeds data model.\r
/// @author The Witnet Foundation.\r
library WitPriceFeedsDataLib {\r
\r
    using Slices for string;\r
    using Slices for Slices.Slice;\r
\r
    using Witnet for Witnet.DataResult;\r
    using Witnet for Witnet.QueryId;\r
    using Witnet for Witnet.RadonHash;\r
    using Witnet for Witnet.ResultStatus;\r
    using Witnet for Witnet.Timestamp;\r
\r
    using WitPriceFeedsDataLib for IWitPriceFeeds.ID4;\r
    using WitPriceFeedsDataLib for PriceFeed;\r
    \r
    bytes32 private constant _WIT_FEEDS_DATA_SLOTHASH =\r
        /* keccak256("io.witnet.feeds.data.v3") & ~bytes32(uint256(0xff) */\r
        0xc5354469a5d32189a18f5e79f9508d828fa089087c317bc89792b1c8dba53900;\r
\r
    struct Storage {\r
        IWitPyth.ID[] ids;\r
        mapping (IWitPriceFeeds.ID4 => PriceFeed) records;\r
        mapping (IWitPriceFeeds.ID4 => IWitPriceFeeds.ID4[]) reverseDeps;\r
        mapping (Witnet.RadonHash => IWitPriceFeeds.ID4) reverseIds;\r
        IWitPriceFeeds.UpdateConditions defaultUpdateConditions;\r
        address consumer;\r
        bytes4  footprint;\r
    }\r
\r
    struct PriceData {\r
        /// @dev Exponentially moving average proportional to actual time since previous update.\r
        uint64 emaPrice;\r
        \r
        /// @dev Price attested on the Witnet blockchain.\r
        uint64 price;\r
        \r
        /// @dev How much the price varied since previous update.\r
        int56 deltaPrice;\r
        \r
        /// @dev Base-10 exponent to compute actual price.\r
        int8 exponent;\r
\r
        /// @dev Timestamp at which the price was attested on the Witnet blockchain.\r
        Witnet.Timestamp timestamp;\r
\r
        /// @dev Auditory trail: price witnessing act on the Witnet blockchain.\r
        Witnet.TransactionHash trail;\r
    }\r
\r
    // struct Price {\r
    //     /// @dev Price data point to be read is just one single SLOAD.\r
    //     PriceData data;\r
        \r
    //     /// @dev Auditory trail: price witnessing act on the Witnet blockchain.\r
    //     Witnet.TransactionHash trail;\r
    // }\r
\r
    struct PriceFeed {\r
        /// @dev Human-readable symbol for this price feed.\r
        string symbol;\r
        \r
        /// @dev (Required for removing price-feed settlements from storage)\r
        uint32 index;\r
\r
        /// @dev Base-10 exponent to compute actual price.\r
        int8 exponent;\r
\r
        /// @dev (40-bit reserve)\r
        int40 _reserved;\r
        \r
        /// @dev Price feed's mapping algorithm, if any.        \r
        IWitPriceFeeds.Mappers mapper;\r
\r
        /// @dev Price feed's aggregator oracle type, if other than the Wit/Oracle.\r
        IWitPriceFeeds.Oracles oracle;\r
\r
        /// @dev Price feed's aggregator oracle address, if other than the Wit/Oracle.\r
        address oracleAddress;\r
\r
        /// @dev Unique ID identifying actual data sources and off-chain computations performed by \r
        /// the selected oracle when retrieving a fresh update for this price feed \r
        /// (e.g. Radon Request hash if oracle is the Wit/Oracle).\r
        bytes32 oracleSources;\r
\r
        /// @dev 256-bit flag containing references up to 8x existing price feeds.\r
        bytes32 mapperDeps;\r
\r
        /// @dev Price-feed specific update conditions, if other than defaults.\r
        IWitPriceFeeds.UpdateConditions updateConditions;\r
\r
        /// @dev Last valid update data retrieved from the Wit/Oracle, if any.\r
        PriceData lastUpdate;\r
    }\r
\r
\r
    // ================================================================================================================\r
    // --- Public methods ---------------------------------------------------------------------------------------------\r
\r
    function fetchLastUpdate(PriceFeed storage self, IWitPriceFeeds.ID4 id4, uint24 heartbeat)\r
        public view \r
        returns (PriceData memory _lastUpdate)\r
    {\r
        IWitPriceFeeds.Mappers _mapper = self.mapper;\r
        if (_mapper == IWitPriceFeeds.Mappers.None) {\r
            \r
            IWitPriceFeeds.Oracles _oracle = self.oracle;\r
            if (_oracle == IWitPriceFeeds.Oracles.Witnet) {\r
                if (self.oracleAddress == address(0) || self.oracleAddress == address(this)) {\r
                    return self.lastUpdate;\r
                \r
                } else {\r
                    return (\r
                        self.updateConditions.computeEma\r
                            ? _intoEmaPriceData(IWitPriceFeeds(self.oracleAddress).getPriceNotOlderThan(\r
                                IWitPriceFeeds.ID4.wrap(bytes4(self.oracleSources)), \r
                                heartbeat\r
                            ))\r
                            : _intoPriceData(IWitPriceFeeds(self.oracleAddress).getPriceNotOlderThan(\r
                                IWitPriceFeeds.ID4.wrap(bytes4(self.oracleSources)), \r
                                heartbeat\r
                            ))\r
                    );\r
                }\r
\r
            } else if (_oracle == IWitPriceFeeds.Oracles.ERC2362) {\r
                (int _value, uint _timestamp,) = IERC2362(self.oracleAddress).valueFor(self.oracleSources);\r
                _lastUpdate.price = uint64(int64(_value));\r
                _lastUpdate.timestamp = Witnet.Timestamp.wrap(uint64(_timestamp));\r
                _lastUpdate.exponent = self.exponent;\r
\r
            } else if (_oracle == IWitPriceFeeds.Oracles.Chainlink) {\r
                (, int _value,, uint _timestamp,) = IChainlinkAggregatorV3(self.oracleAddress).latestRoundData();\r
                _lastUpdate.price = uint64(int64(_value));\r
                _lastUpdate.timestamp = Witnet.Timestamp.wrap(uint64(_timestamp));\r
                _lastUpdate.exponent = self.exponent;\r
            \r
            } else if (_oracle == IWitPriceFeeds.Oracles.Pyth) {\r
                IWitPyth.PythPrice memory _price;\r
                if (self.updateConditions.computeEma) {\r
                    _price = IWitPyth(self.oracleAddress).getEmaPriceUnsafe(IWitPyth.ID.wrap(self.oracleSources));\r
                    _lastUpdate.emaPrice = uint64(_price.price);\r
                } else {\r
                    _price = IWitPyth(self.oracleAddress).getPriceUnsafe(IWitPyth.ID.wrap(self.oracleSources));\r
                    _lastUpdate.price = uint64(_price.price);\r
                }\r
                _lastUpdate.timestamp = Witnet.Timestamp.wrap(uint64(_price.publishTime));\r
                _lastUpdate.exponent = int8(_price.expo);\r
\r
            } else {\r
                revert("unsupported oracle");\r
            }\r
        \r
        } else {    \r
            if (\r
                _mapper == IWitPriceFeeds.Mappers.Product \r
                    || _mapper == IWitPriceFeeds.Mappers.Inverse\r
            ) {\r
                return fetchLastUpdateFromProduct(\r
                    id4, \r
                    heartbeat, \r
                    self.exponent, \r
                    _mapper == IWitPriceFeeds.Mappers.Inverse\r
                );\r
\r
            } else if (_mapper == IWitPriceFeeds.Mappers.Hottest) {\r
                return fetchLastUpdateFromHottest(\r
                    id4, \r
                    heartbeat,\r
                    self.exponent\r
                );\r
            \r
            } else if (_mapper == IWitPriceFeeds.Mappers.Fallback) {\r
                return fetchLastUpdateFromFallback(\r
                    id4, \r
                    heartbeat,\r
                    self.exponent\r
                );\r
\r
            } else {\r
                revert("unsupported mapper");\r
            }\r
        }\r
    }\r
\r
    function getPrice(IWitPriceFeeds.ID4 id4)\r
        public view \r
        returns (IWitPriceFeeds.Price memory)\r
    {\r
        PriceFeed storage __record = seekPriceFeed(id4);\r
        IWitPriceFeeds.UpdateConditions memory _conditions = coalesce(__record.updateConditions);\r
\r
        PriceData memory _lastUpdate = fetchLastUpdate(__record, id4, _conditions.heartbeatSecs);\r
        \r
        require(\r
            !_lastUpdate.timestamp.isZero(), \r
            IWitPythErrors.PriceFeedNotFound()\r
        );\r
        \r
        require(\r
            _conditions.heartbeatSecs == 0\r
                || block.timestamp <= Witnet.Timestamp.unwrap(_lastUpdate.timestamp) + _conditions.heartbeatSecs,\r
            IWitPythErrors.StalePrice()\r
        );\r
\r
        return IWitPriceFeeds.Price({\r
            exponent: _lastUpdate.exponent,\r
            deltaPrice: _lastUpdate.deltaPrice,\r
            price: _lastUpdate.emaPrice > 0 ? _lastUpdate.emaPrice : _lastUpdate.price,\r
            timestamp: _lastUpdate.timestamp,\r
            trail: _lastUpdate.trail\r
        });\r
    }\r
\r
    function getPriceNotOlderThan(IWitPriceFeeds.ID4 id4, uint24 age)\r
        public view \r
        returns (IWitPriceFeeds.Price memory)\r
    {\r
        PriceFeed storage __record = seekPriceFeed(id4);\r
        PriceData memory _lastUpdate = fetchLastUpdate(__record, id4, age);\r
\r
        require(\r
            !_lastUpdate.timestamp.isZero(), \r
            IWitPythErrors.PriceFeedNotFound()\r
        );\r
\r
        require(\r
            block.timestamp <= Witnet.Timestamp.unwrap(_lastUpdate.timestamp) + age,\r
            IWitPythErrors.StalePrice()\r
        );\r
\r
        return IWitPriceFeeds.Price({\r
            exponent: _lastUpdate.exponent,\r
            deltaPrice: _lastUpdate.deltaPrice,\r
            price: _lastUpdate.emaPrice > 0 ? _lastUpdate.emaPrice : _lastUpdate.price,\r
            timestamp: _lastUpdate.timestamp,\r
            trail: _lastUpdate.trail\r
        });\r
    }\r
\r
    function getPriceUnsafe(IWitPriceFeeds.ID4 id4)\r
        public view \r
        returns (IWitPriceFeeds.Price memory)\r
    {\r
        PriceFeed storage __record = seekPriceFeed(id4);\r
        PriceData memory _lastUpdate = fetchLastUpdate(__record, id4, 0);\r
\r
        return IWitPriceFeeds.Price({\r
            exponent: _lastUpdate.exponent,\r
            deltaPrice: _lastUpdate.deltaPrice,\r
            price: _lastUpdate.emaPrice > 0 ? _lastUpdate.emaPrice : _lastUpdate.price,\r
            timestamp: _lastUpdate.timestamp,\r
            trail: _lastUpdate.trail\r
        });\r
    }\r
\r
    \r
    // ================================================================================================================\r
    // --- Price-feed admin methods -----------------------------------------------------------------------------------\r
\r
    function removePriceFeed(IWitPriceFeeds.ID4 id4, bool recursively) public {\r
        PriceFeed storage self = seekPriceFeed(id4);\r
        if (self.settled()) {\r
            IWitPriceFeeds.ID4[] memory _reverseDeps = data().reverseDeps[id4];\r
            require(\r
                recursively\r
                    || _reverseDeps.length == 0,\r
                "cannot remove if mapped from others"  \r
            );\r
\r
            // recursively remove reverse dependencies, if any\r
            // (i.e. other price feeds that rely directly or indirectly on this one)\r
            for (uint _ix; _ix < _reverseDeps.length; ++ _ix) {\r
                removePriceFeed(_reverseDeps[_ix], recursively);\r
            }\r
            delete data().reverseDeps[id4];\r
\r
            // remove from array of supported price feeds\r
            uint _popIndex = data().ids.length - 1;\r
            if (self.index < _popIndex) {\r
                IWitPyth.ID _popID = data().ids[_popIndex];\r
                PriceFeed storage __last = seekPriceFeed(_intoID4(_popID));\r
                __last.index = self.index;\r
                data().ids[self.index] = _popID;\r
            }\r
            data().ids.pop();\r
\r
            // delete all metadata, but the update conditions\r
            Witnet.RadonHash _radonHash = Witnet.RadonHash.wrap(self.oracleSources);       \r
            if (!_radonHash.isZero()) {\r
                if (id4.equals(data().reverseIds[_radonHash])) {\r
                    data().reverseIds[_radonHash] = IWitPriceFeeds.ID4.wrap(0);\r
                }\r
            }\r
            delete data().records[id4].lastUpdate;\r
            delete data().records[id4];\r
        }\r
    }\r
\r
    function settlePriceFeedFootprint() public returns (bytes4 _footprint) {\r
        _footprint = _computePriceFeedsFootprint();\r
        data().footprint = _footprint;\r
    }\r
\r
    function settlePriceFeedMapper(\r
            string calldata symbol,\r
            int8 exponent,\r
            IWitPriceFeeds.Mappers mapper,\r
            string[] calldata mapperDeps\r
        )\r
        public\r
        returns (bytes4)\r
    {\r
        require(\r
            uint8(mapper) > uint8(IWitPriceFeeds.Mappers.None)\r
                && uint8(mapper) <= uint8(IWitPriceFeeds.Mappers.Inverse), \r
            "invalid mapper"\r
        );\r
        IWitPriceFeeds.ID4 id4 = _settlePriceFeedSymbol(symbol);\r
        PriceFeed storage __record = seekPriceFeed(id4);\r
        require(!__record.settled(), "already settled");\r
        bytes32 _mapperDeps;\r
        for (uint _ix; _ix < mapperDeps.length; _ix ++) {\r
            bytes4 _id4 = bytes4(hash(mapperDeps[_ix]));\r
            PriceFeed storage __depsFeed = seekPriceFeed(IWitPriceFeeds.ID4.wrap(_id4));\r
            require(__depsFeed.settled(), string(abi.encodePacked(\r
                "unsupported dependency: ",\r
                mapperDeps[_ix]\r
            )));\r
            _mapperDeps |= (bytes32(_id4) >> (32 * _ix));\r
            data().reverseDeps[IWitPriceFeeds.ID4.wrap(_id4)].push(id4);\r
        }\r
        __record.settleMapper(\r
            exponent, \r
            mapper, \r
            _mapperDeps\r
        );\r
        \r
        // smoke test: force the transaction to revert, should there be any dependency loopback:\r
        getPriceUnsafe(id4);\r
\r
        // recompute and return the new price feeds footprint:\r
        return settlePriceFeedFootprint();\r
    }\r
\r
    function settlePriceFeedOracle(\r
            string calldata symbol,\r
            int8 exponent,\r
            IWitPriceFeeds.Oracles oracle,\r
            address oracleAddress,\r
            bytes32 oracleSources\r
        )\r
        public\r
        returns (bytes4)\r
    {\r
        require(\r
            uint8(oracle) >= uint8(IWitPriceFeeds.Oracles.Witnet)\r
                && uint8(oracle) <= uint8(IWitPriceFeeds.Oracles.Pyth), \r
            "invalid oracle"\r
        );\r
        IWitPriceFeeds.ID4 id4 = _settlePriceFeedSymbol(symbol);\r
        PriceFeed storage __record = seekPriceFeed(id4);\r
        require(!__record.settled(), "already settled");\r
        require(oracleAddress.code.length > 0, "inexistent oracle");\r
        __record.settleOracle(\r
            exponent,\r
            oracle,\r
            oracleAddress,\r
            oracleSources\r
        );\r
\r
        // smoke test: force the transaction to revert, if providing bad sources or target address\r
        getPriceUnsafe(id4);\r
\r
        // recompute and return the new price feeds footprint:\r
        return settlePriceFeedFootprint();\r
    }\r
\r
    function settlePriceFeedRadonBytecode(\r
            string calldata symbol,\r
            bytes calldata radonBytecode,\r
            int8 exponent,\r
            IWitOracleRadonRegistry registry\r
        )\r
        public\r
        returns (bytes4 _footprint, Witnet.RadonHash _radonHash)\r
    {\r
        IWitPriceFeeds.ID4 id4 = _settlePriceFeedSymbol(symbol);\r
        PriceFeed storage __record = seekPriceFeed(id4);\r
        require(address(registry) != address(0), "no radon registry");\r
        _radonHash = registry.hashOf(radonBytecode);\r
        __record.settleOracle(\r
            exponent, \r
            IWitPriceFeeds.Oracles.Witnet,\r
            address(this),\r
            Witnet.RadonHash.unwrap(_radonHash)\r
        );\r
        _footprint = settlePriceFeedFootprint();\r
    }\r
\r
    function settlePriceFeedRadonHash(\r
            string calldata symbol,\r
            Witnet.RadonHash radonHash,\r
            int8 exponent,\r
            IWitOracleRadonRegistry registry\r
        )\r
        public\r
        returns (bytes4)\r
    {\r
        IWitPriceFeeds.ID4 id4 = _settlePriceFeedSymbol(symbol);\r
        PriceFeed storage __record = seekPriceFeed(id4);\r
        require(address(registry) != address(0), "no radon registry");\r
        require(registry.isVerifiedRadonRequest(radonHash), "unverified sources");\r
        __record.settleOracle(\r
            exponent, \r
            IWitPriceFeeds.Oracles.Witnet,\r
            address(this),\r
            Witnet.RadonHash.unwrap(radonHash)\r
        );\r
        return settlePriceFeedFootprint();\r
    }\r
\r
    function toString(IWitPriceFeeds.Mappers mapper) public pure returns (string memory) {\r
        if (mapper == IWitPriceFeeds.Mappers.None) {\r
            return "None";\r
        } else if (mapper == IWitPriceFeeds.Mappers.Product) {\r
            return "Product";\r
        } else if (mapper == IWitPriceFeeds.Mappers.Fallback) {\r
            return "Fallback";\r
        } else if (mapper == IWitPriceFeeds.Mappers.Hottest) {\r
            return "Hottest";\r
        } \r
        revert("unsupported mapper");\r
    }\r
\r
    function toString(IWitPriceFeeds.Oracles oracle) public pure returns (string memory) {\r
        if (oracle == IWitPriceFeeds.Oracles.Witnet) {\r
            return "Wit/Oracle";\r
        } else if (oracle == IWitPriceFeeds.Oracles.ERC2362) {\r
            return "ADO/ERC2362";\r
        } else if (oracle == IWitPriceFeeds.Oracles.Chainlink) {\r
            return "ChainlinkAggregatorV3";\r
        } else if (oracle == IWitPriceFeeds.Oracles.Pyth) {\r
            return "IPyth";\r
        }\r
        revert("unsupported oracle");\r
    }\r
\r
    \r
    // ================================================================================================================\r
    // --- Internal methods -------------------------------------------------------------------------------------------\r
\r
    function coalesce(IWitPriceFeeds.UpdateConditions storage self) \r
        internal view \r
        returns (IWitPriceFeeds.UpdateConditions memory)\r
    {\r
        IWitPriceFeeds.UpdateConditions storage __default = data().defaultUpdateConditions;\r
        return IWitPriceFeeds.UpdateConditions({\r
            callbackGas: self.callbackGas == 0 ? __default.callbackGas : self.callbackGas,\r
            computeEma: self.computeEma || __default.computeEma,\r
            cooldownSecs:  self.cooldownSecs == 0 ? __default.cooldownSecs : self.cooldownSecs,\r
            heartbeatSecs: self.heartbeatSecs == 0 ? __default.heartbeatSecs : self.heartbeatSecs,\r
            maxDeviation1000: self.maxDeviation1000 == 0 ? __default.maxDeviation1000 : self.maxDeviation1000,\r
            minWitnesses: self.minWitnesses == 0 ? __default.minWitnesses : self.minWitnesses                \r
        });\r
    }\r
\r
    function coalesce(\r
            IWitPriceFeeds.UpdateConditions storage self, \r
            IWitPriceFeeds.UpdateConditions memory _default\r
        ) \r
        internal pure\r
        returns (IWitPriceFeeds.UpdateConditions memory)\r
    {\r
        IWitPriceFeeds.UpdateConditions memory _self = self;\r
        return IWitPriceFeeds.UpdateConditions({\r
            callbackGas: _self.callbackGas == 0 ? _default.callbackGas : _self.callbackGas,\r
            computeEma: _self.computeEma || _default.computeEma,\r
            cooldownSecs:  _self.cooldownSecs == 0 ? _default.cooldownSecs : _self.cooldownSecs,\r
            heartbeatSecs: _self.heartbeatSecs == 0 ? _default.heartbeatSecs : _self.heartbeatSecs,\r
            maxDeviation1000: _self.maxDeviation1000 == 0 ? _default.maxDeviation1000 : _self.maxDeviation1000,\r
            minWitnesses: _self.minWitnesses == 0 ? _default.minWitnesses : _self.minWitnesses\r
        });\r
    }\r
\r
    /// @notice Returns storage pointer to where Storage data is located. \r
    function data() internal pure returns (Storage storage _ptr) {\r
        assembly {\r
            _ptr.slot := _WIT_FEEDS_DATA_SLOTHASH\r
        }\r
    }\r
\r
    /// @notice Returns array of price feed ids from which given feed's value depends.\r
    /// @dev Returns empty array on either unsupported or not-routed feeds.\r
    /// @dev The maximum number of dependencies is hard-limited to 8, as to limit number\r
    /// @dev of SSTORE operations (`__storage().records[feedId].solverDepsFlag`), \r
    /// @dev no matter the actual number of depending feeds involved.\r
    function deps(IWitPriceFeeds.ID4 self) internal view returns (IWitPriceFeeds.ID4[] memory _deps) {\r
        bytes32 _solverDepsFlag = data().records[self].mapperDeps;\r
        _deps = new IWitPriceFeeds.ID4[](8);\r
        uint _len;\r
        for (_len; _len < 8; ++ _len) {\r
            bytes4 _id4 = bytes4(_solverDepsFlag);\r
            if (_id4 == 0) {\r
                break;\r
            } else {\r
                _deps[_len] = IWitPriceFeeds.ID4.wrap(_id4);\r
                _solverDepsFlag <<= 32;\r
            }\r
        }\r
        assembly {\r
            // reset length to actual number of dependencies:\r
            mstore(_deps, _len)\r
        }\r
    }\r
\r
    function equals(IWitPriceFeeds.ID4 a, IWitPriceFeeds.ID4 b) internal pure returns (bool) {\r
        return (\r
            IWitPriceFeeds.ID4.unwrap(a)\r
                == IWitPriceFeeds.ID4.unwrap(b)\r
        );\r
    }\r
\r
    function fetchLastUpdateFromProduct(\r
            IWitPriceFeeds.ID4 id4, \r
            uint24 heartbeat, \r
            int8 exponent, \r
            bool inverse\r
        )\r
        internal view \r
        returns (PriceData memory _lastUpdate)\r
    {\r
        IWitPriceFeeds.ID4[] memory _deps = deps(id4);\r
        int[3] memory _regs;\r
        // _regs[0] -> _lastPrice\r
        // _regs[1] -> _lastEmaPrice\r
        // _regs[2] -> _exponent\r
        unchecked {\r
            for (uint _ix; _ix < _deps.length; ++ _ix) {\r
                PriceData memory _depLastUpdate = fetchLastUpdate(seekPriceFeed(_deps[_ix]), _deps[_ix], heartbeat);\r
                if (_ix == 0) {\r
                    if (_depLastUpdate.emaPrice > 0) {\r
                        _regs[1] = int64(_depLastUpdate.emaPrice);\r
                    } else {\r
                        _regs[0] = int64(_depLastUpdate.price);\r
                    }\r
                    _lastUpdate.timestamp = _depLastUpdate.timestamp;\r
                    _lastUpdate.trail = _depLastUpdate.trail;\r
                    \r
                } else {\r
                    if (_regs[1] > 0) {\r
                        _regs[1] *= int64(_depLastUpdate.emaPrice);\r
                    } else {\r
                        _regs[0] *= int64(_depLastUpdate.price);\r
                    }\r
                    if (_lastUpdate.timestamp.gt(_depLastUpdate.timestamp)) {\r
                        // on Product: timestamp belong to oldest of all deps\r
                        _lastUpdate.timestamp = _depLastUpdate.timestamp;\r
                        _lastUpdate.trail = _depLastUpdate.trail;\r
                    }\r
                }\r
                _regs[2] += inverse ? _depLastUpdate.exponent : - _depLastUpdate.exponent;\r
            }\r
        }\r
        _regs[2] += exponent;\r
        if (_regs[2] <= 0) {\r
            if (inverse) {\r
                uint _factor = 10 ** uint(-_regs[2]);\r
                if (_regs[1] > 0) {\r
                    _lastUpdate.emaPrice = uint64(_factor / uint(_regs[1]));\r
                } else if (_regs[0] > 0) {\r
                    _lastUpdate.price = uint64(_factor / uint(_regs[0]));\r
                } else {\r
                    _lastUpdate.price = 0; // avoid unhandled reverts\r
                }\r
            } else {\r
                uint _divisor = 10 ** uint(-_regs[2]);\r
                if (_regs[1] > 0) {\r
                    _lastUpdate.emaPrice = uint64(uint(_regs[1]) / _divisor);\r
                } else {\r
                    _lastUpdate.price = uint64(uint(_regs[0]) / _divisor);\r
                }\r
            }\r
        } else {\r
            uint _factor = 10 ** uint(_regs[2]);\r
            if (_regs[1] > 0) {\r
                _lastUpdate.emaPrice = uint64(uint(_regs[1]) * _factor);\r
            } else {\r
                _lastUpdate.price = uint64(uint(_regs[0]) * _factor);\r
            }\r
        }\r
        _lastUpdate.exponent = exponent;\r
    }\r
\r
    function fetchLastUpdateFromHottest(IWitPriceFeeds.ID4 id4, uint24 heartbeat, int8 exponent)\r
        internal view \r
        returns (PriceData memory _lastUpdate)\r
    {\r
        IWitPriceFeeds.ID4[] memory _deps = deps(id4);\r
        for (uint _ix; _ix < _deps.length; ++ _ix) {\r
            PriceData memory _depLastUpdate = fetchLastUpdate(seekPriceFeed(_deps[_ix]),  _deps[_ix], heartbeat);\r
            if (\r
                _ix == 0\r
                    || _depLastUpdate.timestamp.gt(_lastUpdate.timestamp)\r
            ) {\r
                _lastUpdate = _depLastUpdate;\r
            }\r
        }\r
        if (exponent < _lastUpdate.exponent) {\r
            _lastUpdate.price *= uint64(10 ** uint8(_lastUpdate.exponent - exponent));\r
            _lastUpdate.exponent = exponent;\r
        } else if (exponent > _lastUpdate.exponent) {\r
            _lastUpdate.price /= uint64(10 ** uint8(exponent - _lastUpdate.exponent));\r
            _lastUpdate.exponent = exponent;\r
        }\r
    }\r
\r
    function fetchLastUpdateFromFallback(IWitPriceFeeds.ID4 id4, uint24 heartbeat, int8 exponent)\r
        internal view \r
        returns (PriceData memory _lastUpdate)\r
    {\r
        IWitPriceFeeds.ID4[] memory _deps = deps(id4);\r
        for (uint _ix; _ix < _deps.length; ++ _ix) {\r
            PriceData memory _depLastUpdate = fetchLastUpdate(seekPriceFeed(_deps[_ix]), _deps[_ix], heartbeat);\r
            if (\r
                heartbeat == 0\r
                    || Witnet.Timestamp.unwrap(_depLastUpdate.timestamp) > uint64(block.timestamp - heartbeat)\r
            ) {\r
                return _depLastUpdate;\r
            }\r
        }\r
        if (exponent < _lastUpdate.exponent) {\r
            _lastUpdate.price *= uint64(10 ** uint8(_lastUpdate.exponent - exponent));\r
            _lastUpdate.exponent = exponent;\r
        } else if (exponent > _lastUpdate.exponent) {\r
            _lastUpdate.price /= uint64(10 ** uint8(exponent - _lastUpdate.exponent));\r
            _lastUpdate.exponent = exponent;\r
        }\r
    }\r
\r
    function hash(string memory symbol) internal pure returns (bytes32) {\r
        return keccak256(abi.encode(symbol));\r
    }\r
\r
    function isZero(IWitPriceFeeds.ID4 id4) internal pure returns (bool) {\r
        return IWitPriceFeeds.ID4.unwrap(id4) == 0;\r
    }\r
\r
    // function pushDataResult(\r
    //         Witnet.DataResult memory result,\r
    //         IWitPriceFeeds.UpdateConditions memory defaultUpdateConditions,\r
    //         IWitPriceFeeds.ID4 id4\r
    //     )\r
    //     internal\r
    // {\r
    //     PriceFeed storage __record = seekPriceFeed(id4);\r
    //     PriceData memory _lastUpdate = __record.lastUpdate.data;\r
    //     IWitPriceFeeds.UpdateConditions memory _updateConditions = coalesce(\r
    //         __record.updateConditions, \r
    //         defaultUpdateConditions\r
    //     );\r
        \r
    //     // consider updating price-feed's last update only if reported value is more recent:\r
    //     if (\r
    //         Witnet.Timestamp.unwrap(_lastUpdate.timestamp) + _updateConditions.cooldownSecs\r
    //             < Witnet.Timestamp.unwrap(result.timestamp)\r
    //     ) {\r
    //         // revert if any of the allegedly fresh updates actually contains \r
    //         // no integer value:\r
    //         require(\r
    //             result.dataType == Witnet.RadonDataTypes.Integer\r
    //                 && result.status == Witnet.ResultStatus.NoErrors,\r
    //             IWitPythErrors.InvalidUpdateData()\r
    //         );\r
\r
    //         // compute next data point based on `_result` and `_lastUpdate`\r
    //         __record.lastUpdate = _computeNextPrice(\r
    //             result, \r
    //             _lastUpdate, \r
    //             _updateConditions\r
    //         );\r
    //     }\r
    // }\r
\r
    function seekPriceFeed(IWitPriceFeeds.ID4 id4) internal view returns (PriceFeed storage) {\r
        return data().records[id4];\r
    }\r
\r
    function settled(PriceFeed storage self) internal view returns (bool) {\r
        return(\r
            self.oracleSources != bytes32(0)\r
                || uint8(self.mapper) != 0\r
                || self.oracleAddress != address(0)\r
        );\r
    }\r
\r
    function settleMapper(\r
            PriceFeed storage self, \r
            int8 exponent,\r
            IWitPriceFeeds.Mappers mapper, \r
            bytes32 mapperDeps\r
        )\r
        internal\r
    {\r
        require(!self.settled(), "already settled");\r
        self.exponent = exponent;\r
        self.mapper = mapper;\r
        self.mapperDeps = mapperDeps;\r
    }\r
\r
    function settleOracle(\r
            PriceFeed storage self,\r
            int8 exponent,\r
            IWitPriceFeeds.Oracles oracle,\r
            address oracleAddress,\r
            bytes32 oracleSources\r
        )\r
        internal\r
    {\r
        require(!self.settled(), "already settled");\r
        self.exponent = exponent;\r
        self.oracle = oracle;\r
        self.oracleAddress = oracleAddress;\r
        self.oracleSources = oracleSources;\r
    }\r
\r
    // function settleWitOracle(\r
    //         PriceFeed storage self, \r
    //         int8 exponent,\r
    //         Witnet.RadonHash radonHash\r
    //     )\r
    //     internal\r
    // {\r
    //     require(!self.settled(), "already settled");\r
    //     self.exponent = exponent;\r
    //     self.oracle = IWitPriceFeeds.Oracles.Witnet;\r
    //     self.oracleAddress = address(this);\r
    //     self.oracleSources = Witnet.RadonHash.unwrap(radonHash);\r
    //     self.lastUpdate.data.exponent = exponent;\r
    // }\r
\r
    function toERC165Id(IWitPriceFeeds.Oracles oracle) public pure returns (bytes4) {\r
        if (oracle == IWitPriceFeeds.Oracles.Witnet) {\r
            return type(IWitOracle).interfaceId;\r
        \r
        } else if (oracle == IWitPriceFeeds.Oracles.ERC2362) {\r
            return type(IERC2362).interfaceId;\r
        \r
        } else if (oracle == IWitPriceFeeds.Oracles.Chainlink) {\r
            return type(IChainlinkAggregatorV3).interfaceId;\r
        \r
        } else if (oracle == IWitPriceFeeds.Oracles.Pyth) {\r
            return type(IWitPyth).interfaceId;\r
        \r
        } else {\r
            return 0x0;\r
        }\r
    }\r
\r
\r
    // ================================================================================================================\r
    // --- Private methods --------------------------------------------------------------------------------------------\r
\r
    function _computePriceFeedsFootprint() private view returns (bytes4 _footprint) {\r
        uint _totalIds = data().ids.length;\r
        if (_totalIds > 0) {\r
            _footprint = _footprintOf(_intoID4(data().ids[0]));\r
            for (uint _ix = 1; _ix < _totalIds; ++ _ix) {\r
                _footprint ^= _footprintOf(_intoID4(data().ids[_ix]));\r
            }\r
        }\r
    }\r
\r
    function _footprintOf(IWitPriceFeeds.ID4 id4) private view returns (bytes4) {\r
        WitPriceFeedsDataLib.PriceFeed storage self = seekPriceFeed(id4);\r
        if (self.oracleSources == bytes32(0)) {\r
            return self.mapper != IWitPriceFeeds.Mappers.None ? (\r
                bytes4(keccak256(abi.encodePacked(\r
                    IWitPriceFeeds.ID4.unwrap(id4), \r
                    self.mapperDeps\r
                )))\r
            ) : (\r
                bytes4(keccak256(abi.encodePacked(\r
                    IWitPriceFeeds.ID4.unwrap(id4), \r
                    self.oracleAddress\r
                )))\r
            );\r
\r
        } else {\r
            return bytes4(keccak256(abi.encodePacked(\r
                IWitPriceFeeds.ID4.unwrap(id4), \r
                self.oracleSources\r
            )));\r
        }\r
    }\r
\r
    function _intoID4(IWitPyth.ID id) private pure returns (IWitPriceFeeds.ID4) {\r
        return IWitPriceFeeds.ID4.wrap(bytes4(IWitPyth.ID.unwrap(id)));\r
    }\r
\r
    function _intoWitPythID(IWitPriceFeeds.ID4 id4) private view returns (IWitPyth.ID) {\r
        return data().ids[data().records[id4].index];\r
    }\r
\r
        function _intoPriceData(IWitPriceFeeds.Price memory _price) internal pure returns (PriceData memory) {\r
        return PriceData({\r
            emaPrice: 0,\r
            price: _price.price,\r
            deltaPrice: _price.deltaPrice,\r
            timestamp: _price.timestamp,\r
            trail: _price.trail,\r
            exponent: _price.exponent\r
        });\r
    }\r
\r
    function _intoEmaPriceData(IWitPriceFeeds.Price memory _price) internal pure returns (PriceData memory) {\r
        return PriceData({\r
            emaPrice: _price.price,\r
            price: 0,\r
            deltaPrice: _price.deltaPrice,\r
            timestamp: _price.timestamp,\r
            trail: _price.trail,\r
            exponent: _price.exponent\r
        });\r
    }\r
\r
    // function _intoWitPythPriceFeed(\r
    //         IWitPriceFeeds.ID priceId,\r
    //         Witnet.DataResult memory result,\r
    //         PriceData memory prevData,\r
    //         IWitPriceFeeds.UpdateConditions memory updateConditions\r
    //     )\r
    //     private pure\r
    //     returns (IWitPyth.PriceFeed memory)\r
    // {\r
    //     Price memory _next = _computeNextPrice(result, prevData, updateConditions);\r
    //     uint64 _nextDeviation1000 = uint64(\r
    //         _next.data.deltaPrice >= 0 \r
    //             ? int64(_next.data.deltaPrice) \r
    //             : int64(-_next.data.deltaPrice)\r
    //     );\r
    //     return IWitPyth.PriceFeed({\r
    //         id: priceId,\r
    //         price: IWitPyth.Price({\r
    //             price: _next.data.price,\r
    //             conf: _nextDeviation1000,\r
    //             expo: _next.data.exponent,\r
    //             publishTime: _next.data.timestamp,\r
    //             track: _next.trail\r
    //         }),\r
    //         emaPrice: IWitPyth.Price({\r
    //             price: _next.data.emaPrice,\r
    //             conf: _nextDeviation1000,\r
    //             expo: _next.data.exponent,\r
    //             publishTime: _next.data.timestamp,\r
    //             track: _next.trail\r
    //         })\r
    //     });\r
    // }\r
\r
    function _settlePriceFeedSymbol(string calldata symbol) private returns (IWitPriceFeeds.ID4 id4) {\r
        bytes32 _id = hash(symbol);\r
        id4 = IWitPriceFeeds.ID4.wrap(bytes4(_id));\r
        PriceFeed storage __record = seekPriceFeed(id4);\r
        if (\r
            keccak256(abi.encode(symbol))\r
                != keccak256(abi.encode(__record.symbol))\r
        ) {\r
            require(\r
                data().reverseDeps[id4].length == 0,\r
                "cannot refactor exisitng symbol if mapped by others"\r
            );\r
            __record.symbol = symbol;\r
            __record.index = uint32(data().ids.length);\r
            delete __record.lastUpdate;\r
        }\r
        if (!__record.settled()) {\r
            // add id to the list of supported price feeds, if not currently settled\r
            data().ids.push(IWitPyth.ID.wrap(_id));\r
        }\r
    }\r
}\r
"
    },
    "/contracts/libs/WitnetCBOR.sol": {
      "content": "// SPDX-License-Identifier: MIT\r
\r
pragma solidity >=0.8.0 <0.9.0;\r
\r
import "./WitnetBuffer.sol";\r
\r
/// @title A minimalistic implementation of “RFC 7049 Concise Binary Object Representation”\r
/// @notice This library leverages a buffer-like structure for step-by-step decoding of bytes so as to minimize\r
/// the gas cost of decoding them into a useful native type.\r
/// @dev Most of the logic has been borrowed from Patrick Gansterer’s cbor.js library: https://github.com/paroga/cbor-js\r
/// @author The Witnet Foundation.\r
\r
library WitnetCBOR {\r
\r
  using WitnetBuffer for WitnetBuffer.Buffer;\r
  using WitnetCBOR for WitnetCBOR.CBOR;\r
\r
  /// Data struct following the RFC-7049 standard: Concise Binary Object Representation.\r
  struct CBOR {\r
      WitnetBuffer.Buffer buffer;\r
      uint8 initialByte;\r
      uint8 majorType;\r
      uint8 additionalInformation;\r
      uint64 len;\r
      uint64 tag;\r
  }\r
\r
  uint8 internal constant MAJOR_TYPE_INT = 0;\r
  uint8 internal constant MAJOR_TYPE_NEGATIVE_INT = 1;\r
  uint8 internal constant MAJOR_TYPE_BYTES = 2;\r
  uint8 internal constant MAJOR_TYPE_STRING = 3;\r
  uint8 internal constant MAJOR_TYPE_ARRAY = 4;\r
  uint8 internal constant MAJOR_TYPE_MAP = 5;\r
  uint8 internal constant MAJOR_TYPE_TAG = 6;\r
  uint8 internal constant MAJOR_TYPE_CONTENT_FREE = 7;\r
\r
  uint32 internal constant UINT32_MAX = type(uint32).max;\r
  uint64 internal constant UINT64_MAX = type(uint64).max;\r
  \r
  error EmptyArray();\r
  error InvalidLengthEncoding(uint length);\r
  error UnexpectedMajorType(uint read, uint expected);\r
  error UnsupportedPrimitive(uint primitive);\r
  error UnsupportedMajorType(uint unexpected);  \r
\r
  modifier isMajorType(\r
      WitnetCBOR.CBOR memory cbor,\r
      uint8 expected\r
  ) {\r
    if (cbor.majorType != expected) {\r
      revert UnexpectedMajorType(cbor.majorType, expected);\r
    }\r
    _;\r
  }\r
\r
  modifier notEmpty(WitnetBuffer.Buffer memory buffer) {\r
    if (buffer.data.length == 0) {\r
      revert WitnetBuffer.EmptyBuffer();\r
    }\r
    _;\r
  }\r
\r
  function eof(CBOR memory cbor)\r
    internal pure\r
    returns (bool)\r
  {\r
    return cbor.buffer.cursor >= cbor.buffer.data.length;\r
  }\r
\r
  /// @notice Decode a CBOR structure from raw bytes.\r
  /// @dev This is the main factory for CBOR instances, which can be later decoded into native EVM types.\r
  /// @param bytecode Raw bytes representing a CBOR-encoded value.\r
  /// @return A `CBOR` instance containing a partially decoded value.\r
  function fromBytes(bytes memory bytecode)\r
    internal pure\r
    returns (CBOR memory)\r
  {\r
    WitnetBuffer.Buffer memory buffer = WitnetBuffer.Buffer(bytecode, 0);\r
    return fromBuffer(buffer);\r
  }\r
\r
  /// @notice Decode a CBOR structure from raw bytes.\r
  /// @dev This is an alternate factory for CBOR instances, which can be later decoded into native EVM types.\r
  /// @param buffer A Buffer structure representing a CBOR-encoded value.\r
  /// @return A `CBOR` instance containing a partially decoded value.\r
  function fromBuffer(WitnetBuffer.Buffer memory buffer)\r
    internal pure\r
    notEmpty(buffer)\r
    returns (CBOR memory)\r
  {\r
    uint8 initialByte;\r
    uint8 majorType = 255;\r
    uint8 additionalInformation;\r
    uint64 tag = UINT64_MAX;\r
    uint256 len;\r
    bool isTagged = true;\r
    while (isTagged) {\r
      // Extract basic CBOR properties from input bytes\r
      initialByte = buffer.readUint8();\r
      len ++;\r
      majorType = initialByte >> 5;\r
      additionalInformation = initialByte & 0x1f;\r
      // Early CBOR tag parsing.\r
      if (majorType == MAJOR_TYPE_TAG) {\r
        uint _cursor = buffer.cursor;\r
        tag = readLength(buffer, additionalInformation);\r
        len += buffer.cursor - _cursor;\r
      } else {\r
        isTagged = false;\r
      }\r
    }\r
    if (majorType > MAJOR_TYPE_CONTENT_FREE) {\r
      revert UnsupportedMajorType(majorType);\r
    }\r
    return CBOR(\r
      buffer,\r
      initialByte,\r
      majorType,\r
      additionalInformation,\r
      uint64(len),\r
      tag\r
    );\r
  }\r
\r
  function fork(WitnetCBOR.CBOR memory self)\r
    internal pure\r
    returns (WitnetCBOR.CBOR memory)\r
  {\r
    return CBOR({\r
      buffer: self.buffer.fork(),\r
      initialByte: self.initialByte,\r
      majorType: self.majorType,\r
      additionalInformation: self.additionalInformation,\r
      len: self.len,\r
      tag: self.tag\r
    });\r
  }\r
\r
  function settle(CBOR memory self)\r
      internal pure\r
      returns (WitnetCBOR.CBOR memory)\r
  {\r
    if (!self.eof()) {\r
      return fromBuffer(self.buffer);\r
    } else {\r
      return self;\r
    }\r
  }\r
\r
  function skip(CBOR memory self)\r
      internal pure\r
      returns (WitnetCBOR.CBOR memory)\r
  {\r
    if (\r
      self.majorType == MAJOR_TYPE_INT\r
        || self.majorType == MAJOR_TYPE_NEGATIVE_INT\r
        || (\r
          self.majorType == MAJOR_TYPE_CONTENT_FREE \r
            && self.additionalInformation >= 25\r
            && self.additionalInformation <= 27\r
        )\r
    ) {\r
      self.buffer.cursor += self.peekLength();\r
    } else if (\r
        self.majorType == MAJOR_TYPE_STRING\r
          || self.majorType == MAJOR_TYPE_BYTES\r
    ) {\r
      uint64 len = readLength(self.buffer, self.additionalInformation);\r
      self.buffer.cursor += len;\r
    } else if (\r
      self.majorType == MAJOR_TYPE_ARRAY\r
        || self.majorType == MAJOR_TYPE_MAP\r
    ) { \r
      self.len = readLength(self.buffer, self.additionalInformation);      \r
    } else if (\r
       self.majorType != MAJOR_TYPE_CONTENT_FREE\r
        || (\r
          self.additionalInformation != 20\r
            && self.additionalInformation != 21\r
        )\r
    ) {\r
      revert("WitnetCBOR.skip: unsupported major type");\r
    }\r
    return self;\r
  }\r
\r
  function peekLength(CBOR memory self)\r
    internal pure\r
    returns (uint64)\r
  {\r
    if (self.additionalInformation < 24) {\r
      return 0;\r
    } else if (self.additionalInformation < 28) {\r
      return uint64(1 << (self.additionalInformation - 24));\r
    } else {\r
      revert InvalidLengthEncoding(self.additionalInformation);\r
    }\r
  }\r
\r
  function readArray(CBOR memory self)\r
    internal pure\r
    isMajorType(self, MAJOR_TYPE_ARRAY)\r
    returns (CBOR[] memory items)\r
  {\r
    // read array's length and move self cursor forward to the first array element:\r
    uint64 len = readLength(self.buffer, self.additionalInformation);\r
    items = new CBOR[](len + 1);\r
    for (uint ix = 0; ix < len; ix ++) {\r
      // settle next element in the array:\r
      self = self.settle();\r
      // fork it and added to the list of items to be returned:\r
      items[ix] = self.fork();\r
      if (self.majorType == MAJOR_TYPE_ARRAY) {\r
        CBOR[] memory _subitems = self.readArray();\r
        // move forward to the first element after inner array:\r
        self = _subitems[_subitems.length - 1];\r
      } else if (self.majorType == MAJOR_TYPE_MAP) {\r
        CBOR[] memory _subitems = self.readMap();\r
        // move forward to the first element after inner map:\r
        self = _subitems[_subitems.length - 1];\r
      } else {\r
        // move forward to the next element:\r
        self.skip();\r
      }\r
    }\r
    // return self cursor as extra item at the end of the list,\r
    // as to optimize recursion when jumping over nested arrays:\r
    items[len] = self;\r
  }\r
\r
  function readMap(CBOR memory self)\r
    internal pure\r
    isMajorType(self, MAJOR_TYPE_MAP)\r
    returns (CBOR[] memory items)\r
  {\r
    // read number of items within the map and move self cursor forward to the first inner element:\r
    uint64 len = readLength(self.buffer, self.additionalInformation) * 2;\r
    items = new CBOR[](len + 1);\r
    for (uint ix = 0; ix < len; ix ++) {\r
      // settle next element in the array:\r
      self = self.settle();\r
      // fork it and added to the list of items to be returned:\r
      items[ix] = self.fork();\r
      if (ix % 2 == 0 && self.majorType != MAJOR_TYPE_STRING) {\r
        revert UnexpectedMajorType(self.majorType, MAJOR_TYPE_STRING);\r
      } else if (self.majorType == MAJOR_TYPE_ARRAY || self.majorType == MAJOR_TYPE_MAP) {\r
        CBOR[] memory _subitems = (self.majorType == MAJOR_TYPE_ARRAY\r
            ? self.readArray()\r
            : self.readMap()\r
        );\r
        // move forward to the first element after inner array or map:\r
        self = _subitems[_subitems.length - 1];\r
      } else {\r
        // move forward to the next element:\r
        self.skip();\r
      }\r
    }\r
    // return self cursor as extra item at the end of the list,\r
    // as to optimize recursion when jumping over nested arrays:\r
    items[len] = self;\r
  }\r
\r
  /// Reads the length of the settle CBOR item from a buffer, consuming a different number of bytes depending on the\r
  /// value of the `additionalInformation` argument.\r
  function readLength(\r
      WitnetBuffer.Buffer memory buffer,\r
      uint8 additionalInformation\r
    ) \r
    internal pure\r
    returns (uint64)\r
  {\r
    if (additionalInformation < 24) {\r
      return additionalInformation;\r
    }\r
    if (additionalInformation == 24) {\r
      return buffer.readUint8();\r
    }\r
    if (additionalInformation == 25) {\r
      return buffer.readUint16();\r
    }\r
    if (additionalInformation == 26) {\r
      return buffer.readUint32();\r
    }\r
    if (additionalInformation == 27) {\r
      return buffer.readUint64();\r
    }\r
    if (additionalInformation == 31) {\r
      return UINT64_MAX;\r
    }\r
    revert InvalidLengthEncoding(additionalInformation);\r
  }\r
\r
  /// @notice Read a `CBOR` structure into a native `bool` value.\r
  /// @param cbor An instance of `CBOR`.\r
  /// @return The value represented by the input, as a `bool` value.\r
  function readBool(CBOR memory cbor)\r
    internal pure\r
    isMajorType(cbor, MAJOR_TYPE_CONTENT_FREE)\r
    returns (bool)\r
  {\r
    if (cbor.additionalInformation == 20) {\r
      return false;\r
    } else if (cbor.additionalInformation == 21) {\r
      return true;\r
    } else {\r
      revert UnsupportedPrimitive(cbor.additionalInformation);\r
    }\r
  }\r
\r
  /// @notice Decode a `CBOR` structure into a native `bytes` value.\r
  /// @param cbor An instance of `CBOR`.\r
  /// @return output The value represented by the input, as a `bytes` value.   \r
  function readBytes(CBOR memory cbor)\r
    internal pure\r
    isMajorType(cbor, MAJOR_TYPE_BYTES)\r
    returns (bytes memory output)\r
  {\r
    cbor.len = readLength(\r
      cbor.buffer,\r
      cbor.additionalInformation\r
    );\r
    if (cbor.len == UINT32_MAX) {\r
      // These checks look repetitive but the equivalent loop would be more expensive.\r
      uint32 length = uint32(_readIndefiniteStringLength(\r
        cbor.buffer,\r
        cbor.majorType\r
      ));\r
      if (length < UINT32_MAX) {\r
        output = abi.encodePacked(cbor.buffer.read(length));\r
        length = uint32(_readIndefiniteStringLength(\r
          cbor.buffer,\r
          cbor.majorType\r
        ));\r
        if (length < UINT32_MAX) {\r
          output = abi.encodePacked(\r
            output,\r
            cbor.buffer.read(length)\r
          );\r
        }\r
      }\r
    } else {\r
      return cbor.buffer.read(uint32(cbor.len));\r
    }\r
  }\r
\r
  /// @notice Decode a `CBOR` structure into a `fixed16` value.\r
  /// @dev Due to the lack of support for floating or fixed point arithmetic in the EVM, this method offsets all values\r
  /// by 5 decimal orders so as to get a fixed precision of 5 decimal positions, which should be OK for most `fixed16`\r
  /// use cases. In other words, the output of this method is 10,000 times the actual value, encoded into an `int32`.\r
  /// @param cbor An instance of `CBOR`.\r
  /// @return The value represented by the input, as an `int128` value.\r
  function readFloat16(CBOR memory cbor)\r
    internal pure\r
    isMajorType(cbor, MAJOR_TYPE_CONTENT_FREE)\r
    returns (int32)\r
  {\r
    if (cbor.additionalInformation == 25) {\r
      return cbor.buffer.readFloat16();\r
    } else {\r
      revert UnsupportedPrimitive(cbor.additionalInformation);\r
    }\r
  }\r
\r
  /// @notice Decode a `CBOR` structure into a `fixed32` value.\r
  /// @dev Due to the lack of support for floating or fixed point arithmetic in the EVM, this method offsets all values\r
  /// by 9 decimal orders so as to get a fixed precision of 9 decimal positions, which should be OK for most `fixed64`\r
  /// use cases. In other words, the output of this method is 10^9 times the actual value, encoded into an `int`.\r
  /// @param cbor An instance of `CBOR`.\r
  /// @return The value represented by the input, as an `int` value.\r
  function readFloat32(CBOR memory cbor)\r
    internal pure\r
    isMajorType(cbor, MAJOR_TYPE_CONTENT_FREE)\r
    returns (int)\r
  {\r
    if (cbor.additionalInformation == 26) {\r
      return cbor.buffer.readFloat32();\r
    } else {\r
      revert UnsupportedPrimitive(cbor.additionalInformation);\r
    }\r
  }\r
\r
  /// @notice Decode a `CBOR` structure into a `fixed64` value.\r
  /// @dev Due to the lack of support for floating or fixed point arithmetic in the EVM, this method offsets all values\r
  /// by 15 decimal orders so as to get a fixed precision of 15 decimal positions, which should be OK for most `fixed64`\r
  /// use cases. In other words, the output of this method is 10^15 times the actual value, encoded into an `int`.\r
  /// @param cbor An instance of `CBOR`.\r
  /// @return The value represented by the input, as an `int` value.\r
  function readFloat64(CBOR memory cbor)\r
    internal pure\r
    isMajorType(cbor, MAJOR_TYPE_CONTENT_FREE)\r
    returns (int)\r
  {\r
    if (cbor.additionalInformation == 27) {\r
      return cbor.buffer.readFloat64();\r
    } else {\r
      revert UnsupportedPrimitive(cbor.additionalInformation);\r
    }\r
  }\r
\r
  /// @notice Decode a `CBOR` structure into a native `int128[]` value whose inner values follow the same convention \r
  /// @notice as explained in `decodeFixed16`.\r
  /// @param cbor An instance of `CBOR`.\r
  function readFloat16Array(CBOR memory cbor)\r
    internal pure\r
    isMajorType(cbor, MAJOR_TYPE_ARRAY)\r
    returns (int32[] memory values)\r
  {\r
    uint64 length = readLength(cbor.buffer, cbor.additionalInformation);\r
    if (length < UINT64_MAX) {\r
      values = new int32[](length);\r
      for (uint64 i = 0; i < length; ) {\r
        CBOR memory item = fromBuffer(cbor.buffer);\r
        values[i] = readFloat16(item);\r
        unchecked {\r
          i ++;\r
        }\r
      }\r
    } else {\r
      revert InvalidLengthEncoding(length);\r
    }\r
  }\r
\r
  /// @notice Decode a `CBOR` structure into a native `int128` value.\r
  /// @param cbor An instance of `CBOR`.\r
  /// @return The value represented by the input, as an `int128` value.\r
  function readInt(CBOR memory cbor)\r
    internal pure\r
    returns (int64)\r
  {\r
    if (cbor.majorType == 1) {\r
      uint64 _value = readLength(\r
        cbor.buffer,\r
        cbor.additionalInformation\r
      );\r
      return int64(-1) - int64(uint64(_value));\r
    } else if (cbor.majorType == 0) {\r
      // Any `uint64` can be safely casted to `int128`, so this method supports majorType 1 as well so as to have offer\r
      // a uniform API for positive and negative numbers\r
      return int64(readUint(cbor));\r
    }\r
    else {\r
      revert UnexpectedMajorType(cbor.majorType, 1);\r
    }\r
  }\r
\r
  /// @notice Decode a `CBOR` structure into a native `int[]` value.\r
  /// @param cbor instance of `CBOR`.\r
  /// @return array The value represented by the input, as an `int[]` value.\r
  function readIntArray(CBOR memory cbor)\r
    internal pure\r
    isMajorType(cbor, MAJOR_TYPE_ARRAY)\r
    returns (int64[] memory array)\r
  {\r
    uint64 length = readLength(cbor.buffer, cbor.additionalInformation);\r
    if (length < UINT64_MAX) {\r
      array = new int64[](length);\r
      for (uint i = 0; i < length; ) {\r
        CBOR memory item = fromBuffer(cbor.buffer);\r
        array[i] = readInt(item);\r
        unchecked {\r
          i ++;\r
        }\r
      }\r
    } else {\r
      revert InvalidLengthEncoding(length);\r
    }\r
  }\r
\r
  /// @notice Decode a `CBOR` structure into a native `string` value.\r
  /// @param cbor An instance of `CBOR`.\r
  /// @return text The value represented by the input, as a `string` value.\r
  function readString(CBOR memory cbor)\r
    internal pure\r
    isMajorType(cbor, MAJOR_TYPE_STRING)\r
    returns (string memory text)\r
  {\r
    cbor.len = readLength(cbor.buffer, cbor.additionalInformation);\r
    if (cbor.len == UINT64_MAX) {\r
      bool _done;\r
      while (!_done) {\r
        uint64 length = _readIndefiniteStringLength(\r
          cbor.buffer,\r
          cbor.majorType\r
        );\r
        if (length < UINT64_MAX) {\r
          text = string(abi.encodePacked(\r
            text,\r
            cbor.buffer.readText(length / 4)\r
          ));\r
        } else {\r
          _done = true;\r
        }\r
      }\r
    } else {\r
      return string(cbor.buffer.readText(cbor.len));\r
    }\r
  }\r
\r
  /// @notice Decode a `CBOR` structure into a native `string[]` value.\r
  /// @param cbor An instance of `CBOR`.\r
  /// @return strings The value represented by the input, as an `string[]` value.\r
  function readStringArray(CBOR memory cbor)\r
    internal pure\r
    isMajorType(cbor, MAJOR_TYPE_ARRAY)\r
    returns (string[] memory strings)\r
  {\r
    uint length = readLength(cbor.buffer, cbor.additionalInformation);\r
    if (length < UINT64_MAX) {\r
      strings = new string[](length);\r
      for (uint i = 0; i < length; ) {\r
        CBOR memory item = fromBuffer(cbor.buffer);\r
        strings[i] = readString(item);\r
        unchecked {\r
          i ++;\r
        }\r
      }\r
    } else {\r
      revert InvalidLengthEncoding(length);\r
    }\r
  }\r
\r
  /// @notice Decode a `CBOR` structure into a native `uint64` value.\r
  /// @param cbor An instance of `CBOR`.\r
  /// @return The value represented by the input, as an `uint64` value.\r
  function readUint(CBOR memory cbor)\r
    internal pure\r
    isMajorType(cbor, MAJOR_TYPE_INT)\r
    returns (uint64)\r
  {\r
    return readLength(\r
      cbor.buffer,\r
      cbor.additionalInformation\r
    );\r
  }\r
\r
  /// @notice Decode a `CBOR` structure into a native `uint64[]` value.\r
  /// @param cbor An instance of `CBOR`.\r
  /// @return values The value represented by the input, as an `uint64[]` value.\r
  function readUintArray(CBOR memory cbor)\r
    internal pure\r
    isMajorType(cbor, MAJOR_TYPE_ARRAY)\r
    returns (uint64[] memory values)\r
  {\r
    uint64 length = readLength(cbor.buffer, cbor.additionalInformation);\r
    if (length < UINT64_MAX) {\r
      values = new uint64[](length);\r
      for (uint ix = 0; ix < length; ) {\r
        CBOR memory item = fromBuffer(cbor.buffer);\r
        values[ix] = readUint(item);\r
        unchecked {\r
          ix ++;\r
        }\r
      }\r
    } else {\r
      revert InvalidLengthEncoding(length);\r
    }\r
  }  \r
\r
  /// Read the length of a CBOR indifinite-length item (arrays, maps, byte strings and text) from a buffer, consuming\r
  /// as many bytes as specified by the first byte.\r
  function _readIndefiniteStringLength(\r
      WitnetBuffer.Buffer memory buffer,\r
      uint8 majorType\r
    )\r
    private pure\r
    returns (uint64 len)\r
  {\r
    uint8 initialByte = buffer.readUint8();\r
    if (initialByte == 0xff) {\r
      return UINT64_MAX;\r
    }\r
    len = readLength(\r
      buffer,\r
      initialByte & 0x1f\r
    );\r
    if (len >= UINT64_MAX) {\r
      revert InvalidLengthEncoding(len);\r
    } else if (majorType != (initialByte >> 5)) {\r
      revert UnexpectedMajorType((initialByte >> 5), majorType);\r
    }\r
  }\r
 \r
}"
    },
    "/contracts/libs/WitnetBuffer.sol": {
      "content": "// SPDX-License-Identifier: MIT\r
\r
pragma solidity >=0.8.0 <0.9.0;\r
\r
/// @title A convenient wrapper around the `bytes memory` type that exposes a buffer-like interface\r
/// @notice The buffer has an inner cursor that tracks the final offset of every read, i.e. any subsequent read will\r
/// start with the byte that goes right after the last one in the previous read.\r
/// @dev `uint32` is used here for `cursor` because `uint16` would only enable seeking up to 8KB, which could in some\r
/// theoretical use cases be exceeded. Conversely, `uint32` supports up to 512MB, which cannot credibly be exceeded.\r
/// @author The Witnet Foundation.\r
library WitnetBuffer {\r
\r
  error EmptyBuffer();\r
  error IndexOutOfBounds(uint index, uint range);\r
  error MissingArgs(uint expected, uint given);\r
\r
  /// Iterable bytes buffer.\r
  struct Buffer {\r
      bytes data;\r
      uint cursor;\r
  }\r
\r
  // Ensures we access an existing index in an array\r
  modifier withinRange(uint index, uint _range) {\r
    if (index > _range) {\r
      revert IndexOutOfBounds(index, _range);\r
    }\r
    _;\r
  }\r
\r
  /// @notice Concatenate undefinite number of bytes chunks.\r
  /// @dev Faster than looping on `abi.encodePacked(output, _buffs[ix])`.\r
  function concat(bytes[] memory _buffs)\r
    internal pure\r
    returns (bytes memory output)\r
  {\r
    unchecked {\r
      uint destinationPointer;\r
      uint destinationLength;\r
      assembly {\r
        // get safe scratch location\r
        output := mload(0x40)\r
        // set starting destination pointer\r
        destinationPointer := add(output, 32)\r
      }      \r
      for (uint ix = 1; ix <= _buffs.length; ix ++) {  \r
        uint source;\r
        uint sourceLength;\r
        uint sourcePointer;        \r
        assembly {\r
          // load source length pointer\r
          source := mload(add(_buffs, mul(ix, 32)))\r
          // load source length\r
          sourceLength := mload(source)\r
          // sets source memory pointer\r
          sourcePointer := add(source, 32)\r
        }\r
        memcpy(\r
          destinationPointer,\r
          sourcePointer,\r
          sourceLength\r
        );\r
        assembly {          \r
          // increase total destination length\r
          destinationLength := add(destinationLength, sourceLength)\r
          // sets destination memory pointer\r
          destinationPointer := add(destinationPointer, sourceLength)\r
        }\r
      }\r
      assembly {\r
        // protect output bytes\r
        mstore(output, destinationLength)\r
        // set final output length\r
        mstore(0x40, add(mload(0x40), add(destinationLength, 32)))\r
      }\r
    }\r
  }\r
\r
  function fork(WitnetBuffer.Buffer memory buffer)\r
    internal pure\r
    returns (WitnetBuffer.Buffer memory)\r
  {\r
    return Buffer(\r
      buffer.data,\r
      buffer.cursor\r
    );\r
  }\r
\r
  function mutate(\r
      WitnetBuffer.Buffer memory buffer,\r
      uint length,\r
      bytes memory pokes\r
    )\r
    internal pure\r
    withinRange(length, buffer.data.length - buffer.cursor + 1)\r
  {\r
    bytes[] memory parts = new bytes[](3);\r
    parts[0] = peek(\r
      buffer,\r
      0,\r
      buffer.cursor\r
    );\r
    parts[1] = pokes;\r
    parts[2] = peek(\r
      buffer,\r
      buffer.cursor + length,\r
      buffer.data.length - buffer.cursor - length\r
    );\r
    buffer.data = concat(parts);\r
  }\r
\r
  /// @notice Read and consume the next byte from the buffer.\r
  /// @param buffer An instance of `Buffer`.\r
  /// @return The next byte in the buffer counting from the cursor position.\r
  function next(Buffer memory buffer)\r
    internal pure\r
    withinRange(buffer.cursor, buffer.data.length)\r
    returns (bytes1)\r
  {\r
    // Return the byte at the position marked by the cursor and advance the cursor all at once\r
    return buffer.data[buffer.cursor ++];\r
  }\r
\r
  function peek(\r
      WitnetBuffer.Buffer memory buffer,\r
      uint offset,\r
      uint length\r
    )\r
    internal pure\r
    withinRange(offset + length, buffer.data.length)\r
    returns (bytes memory)\r
  {\r
    bytes memory data = buffer.data;\r
    bytes memory peeks = new bytes(length);\r
    uint destinationPointer;\r
    uint sourcePointer;\r
    assembly {\r
      destinationPointer := add(peeks, 32)\r
      sourcePointer := add(add(data, 32), offset)\r
    }\r
    memcpy(\r
      destinationPointer,\r
      sourcePointer,\r
      length\r
    );\r
    return peeks;\r
  }\r
\r
  // @notice Extract bytes array from buffer starting from current cursor.\r
  /// @param buffer An instance of `Buffer`.\r
  /// @param length How many bytes to peek from the Buffer.\r
  // solium-disable-next-line security/no-assign-params\r
  function peek(\r
      WitnetBuffer.Buffer memory buffer,\r
      uint length\r
    )\r
    internal pure\r
    withinRange(length, buffer.data.length - buffer.cursor)\r
    returns (bytes memory)\r
  {\r
    return peek(\r
      buffer,\r
      buffer.cursor,\r
      length\r
    );\r
  }\r
\r
  /// @notice Read and consume a certain amount of bytes from the buffer.\r
  /// @param buffer An instance of `Buffer`.\r
  /// @param length How many bytes to read and consume from the buffer.\r
  /// @return output A `bytes memory` containing the first `length` bytes from the buffer, counting from the cursor position.\r
  function read(Buffer memory buffer, uint length)\r
    internal pure\r
    withinRange(buffer.cursor + length, buffer.data.length)\r
    returns (bytes memory output)\r
  {\r
    // Create a new `bytes memory destination` value\r
    output = new bytes(length);\r
    // Early return in case that bytes length is 0\r
    if (length > 0) {\r
      bytes memory input = buffer.data;\r
      uint offset = buffer.cursor;\r
      // Get raw pointers for source and destination\r
      uint sourcePointer;\r
      uint destinationPointer;\r
      assembly {\r
        sourcePointer := add(add(input, 32), offset)\r
        destinationPointer := add(output, 32)\r
      }\r
      // Copy `length` bytes from source to destination\r
      memcpy(\r
        destinationPointer,\r
        sourcePointer,\r
        length\r
      );\r
      // Move the cursor forward by `length` bytes\r
      seek(\r
        buffer,\r
        length,\r
        true\r
      );\r
    }\r
  }\r
  \r
  /// @notice Read and consume the next 2 bytes from the buffer as an IEEE 754-2008 floating point number enclosed in an\r
  /// `int32`.\r
  /// @dev Due to the lack of support for floating or fixed point arithmetic in the EVM, this method offsets all values\r
  /// by 5 decimal orders so as to get a fixed precision of 5 decimal positions, which should be OK for most `float16`\r
  /// use cases. In other words, the integer output of this method 

Tags:
Proxy, Upgradeable, Factory, Oracle|addr:0x98b7d3282aa217148afc94dfb8a6d0ad1a91fba7|verified:true|block:23575424|tx:0x887df374c98494add4177f33afadc7ce5a7f2fae0dc6959065fc1f456e437595|first_check:1760442266

Submitted on: 2025-10-14 13:44:26

Comments

Log in to comment.

No comments yet.