WitOracleRadonEncodingLib

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/libs/WitOracleRadonEncodingLib.sol": {
      "content": "// SPDX-License-Identifier: MIT\r
\r
pragma solidity >=0.8.0 <0.9.0;\r
\r
import "./Witnet.sol";\r
\r
/// @title A library for encoding Witnet Data Requests.\r
/// @author The Witnet Foundation.\r
library WitOracleRadonEncodingLib {\r
\r
    using WitnetBuffer for WitnetBuffer.Buffer;\r
    using WitnetCBOR for WitnetCBOR.CBOR;\r
    using WitnetCBOR for WitnetCBOR.CBOR[];\r
\r
    bytes internal constant WITNET_RADON_OPCODES_RESULT_TYPES =\r
        hex"10ffffffffffffffffffffffffffffff040100010203050406071311ff0101ff07ff02ffffffffffffffffffffffffff070304ff04ffffffffffffff03ffffff0405070202ff0404040403ffffffffff05070402040205050505ff04ff04ffff07010203050406070101ff06ffff06ff0203050404000106060707070701ffff";\r
            // 10ffffffffffffffffffffffffffffff\r
            // 040100001203050406070100ff0101ff\r
            // 07ff02ffffffffffffffffffffffffff\r
            // 070304ff04ffffffffffffff03ffffff\r
            // 0405070202ff0404040403ffffffffff\r
            // 05070402040205050505ff04ff04ffff\r
            // 07010203050406070101ff06ffff06ff\r
            // 0203050404000106060707070701ffff\r
\r
    error UnsupportedDataRequestMethod(uint8 method, string schema, string body, string[2][] headers);\r
    error UnsupportedRadonDataType(uint8 datatype, uint256 maxlength);\r
    error UnsupportedRadonFilterOpcode(uint8 opcode);\r
    error UnsupportedRadonFilterArgs(uint8 opcode, bytes args);\r
    error UnsupportedRadonReducerOpcode(uint8 opcode);\r
    error UnsupportedRadonReducerScript(uint8 opcode, bytes script, uint256 offset);\r
    error UnsupportedRadonScript(bytes script, uint256 offset);\r
    error UnsupportedRadonScriptOpcode(bytes script, uint256 cursor, uint8 opcode);\r
    error UnsupportedRadonTallyScript(bytes32 hash);\r
\r
    /// ===============================================================================================================\r
    /// --- WitOracleRadonEncodingLib internal methods --------------------------------------------------------------------------------\r
\r
    function size(Witnet.RadonDataTypes _type) internal pure returns (uint16) {\r
        if (_type == Witnet.RadonDataTypes.Integer\r
            || _type == Witnet.RadonDataTypes.Float\r
        ) {\r
            return 9;\r
        } else if (_type == Witnet.RadonDataTypes.Bool) {\r
            return 1;\r
        } else {\r
            // undetermined\r
            return 0; \r
        }\r
    }\r
\r
\r
    /// ===============================================================================================================\r
    /// --- WitOracleRadonEncodingLib public methods (if used library will have to linked to calling contracts) -----------------------\r
\r
    /// @notice Encode bytes array into given major type (UTF-8 not yet supported)\r
    /// @param buf Bytes array\r
    /// @return Marshaled bytes\r
    function encode(bytes memory buf, uint majorType)\r
        public pure\r
        returns (bytes memory)\r
    {\r
        uint len = buf.length;\r
        if (len < 23) {\r
            return abi.encodePacked(\r
                uint8((majorType << 5) | uint8(len)),\r
                buf\r
            );\r
        } else {\r
            uint8 buf0 = uint8((majorType << 5));\r
            bytes memory buf1;\r
            if (len <= 0xff) {\r
                buf0 |= 24;\r
                buf1 = abi.encodePacked(uint8(len));                \r
            } else if (len <= 0xffff) {\r
                buf0 |= 25;\r
                buf1 = abi.encodePacked(uint16(len));\r
            } else if (len <= 0xffffffff) {\r
                buf0 |= 26;\r
                buf1 = abi.encodePacked(uint32(len));\r
            } else {\r
                buf0 |= 27;\r
                buf1 = abi.encodePacked(uint64(len));\r
            }\r
            return abi.encodePacked(\r
                buf0,\r
                buf1,\r
                buf\r
            );\r
        }\r
    }\r
\r
    /// @notice Encode bytes array.\r
    /// @param buf Bytes array\r
    /// @return Mashaled bytes\r
    function encode(bytes memory buf)\r
        public pure\r
        returns (bytes memory)\r
    {\r
        return encode(buf, WitnetCBOR.MAJOR_TYPE_BYTES);\r
    } \r
\r
    /// @notice Encode string array (UTF-8 not yet supported).\r
    /// @param str String bytes.\r
    /// @return Mashaled bytes\r
    function encode(string memory str)\r
        public pure\r
        returns (bytes memory)\r
    {\r
        return encode(bytes(str), WitnetCBOR.MAJOR_TYPE_STRING);\r
    }\r
\r
    /// @dev Encode uint64 into tagged varint.\r
    /// @dev See https://developers.google.com/protocol-buffers/docs/encoding#varints.\r
    /// @param n Number\r
    /// @param t Tag\r
    /// @return buf Marshaled bytes\r
    function encode(uint64 n, bytes1 t)\r
        public pure\r
        returns (bytes memory buf)\r
    {\r
        unchecked {\r
            // Count the number of groups of 7 bits\r
            // We need this pre-processing step since Solidity doesn't allow dynamic memory resizing\r
            uint64 tmp = n;\r
            uint64 numBytes = 2;\r
            while (tmp > 0x7F) {\r
                tmp = tmp >> 7;\r
                numBytes += 1;\r
            }\r
            buf = new bytes(numBytes);\r
            tmp = n;\r
            buf[0] = t;\r
            for (uint64 i = 1; i < numBytes; i++) {\r
                // Set the first bit in the byte for each group of 7 bits\r
                buf[i] = bytes1(0x80 | uint8(tmp & 0x7F));\r
                tmp = tmp >> 7;\r
            }\r
            // Unset the first bit of the last byte\r
            buf[numBytes - 1] &= 0x7F;\r
        }\r
    }   \r
\r
    function encode(Witnet.RadonRetrieval memory source)\r
        public pure\r
        returns (bytes memory)\r
    {\r
        bytes memory _encodedMethod = encode(uint64(source.method), bytes1(0x08));\r
        bytes memory _encodedUrl;\r
        if (bytes(source.url).length > 0) {\r
            _encodedUrl = abi.encodePacked(\r
                encode(uint64(bytes(source.url).length), bytes1(0x12)),\r
                bytes(source.url)\r
            );\r
        }\r
        bytes memory _encodedScript;\r
        if (source.radonScript.length > 0) {\r
            _encodedScript = abi.encodePacked(\r
                encode(uint64(source.radonScript.length), bytes1(0x1a)),\r
                source.radonScript\r
            );\r
        }\r
        bytes memory _encodedBody;\r
        if (bytes(source.body).length > 0) {\r
            _encodedBody = abi.encodePacked(\r
                encode(uint64(bytes(source.body).length), bytes1(0x22)),\r
                bytes(source.body)\r
            );\r
        }\r
        bytes memory _encodedHeaders;\r
        if (source.headers.length > 0) {\r
            for (uint _ix = 0; _ix < source.headers.length; _ix ++) {\r
                bytes memory _headers = abi.encodePacked(\r
                    encode(uint64(bytes(source.headers[_ix][0]).length), bytes1(0x0a)),\r
                    bytes(source.headers[_ix][0]),\r
                    encode(uint64(bytes(source.headers[_ix][1]).length), bytes1(0x12)),\r
                    bytes(source.headers[_ix][1])\r
                );\r
                _encodedHeaders = abi.encodePacked(\r
                    _encodedHeaders,\r
                    encode(uint64(_headers.length), bytes1(0x2a)),\r
                    _headers\r
                );\r
            }\r
        }\r
        uint _innerSize = (\r
            _encodedMethod.length\r
                + _encodedUrl.length\r
                + _encodedScript.length\r
                + _encodedBody.length\r
                + _encodedHeaders.length\r
        );\r
        return abi.encodePacked(\r
            encode(uint64(_innerSize), bytes1(0x12)),\r
            _encodedMethod,\r
            _encodedUrl,\r
            _encodedScript,\r
            _encodedBody,\r
            _encodedHeaders\r
        );\r
    }\r
\r
    function encode(\r
            Witnet.RadonRetrieval[] memory retrievals,\r
            string[] calldata args,\r
            bytes memory aggregatorInnerBytecode,\r
            bytes memory tallyInnerBytecode\r
        )\r
        public pure\r
        returns (bytes memory)\r
    {\r
        bytes[] memory encodedSources = new bytes[](retrievals.length);\r
        for (uint ix; ix < retrievals.length; ++ ix) {\r
            replaceWildcards(retrievals[ix], args);\r
            encodedSources[ix] = encode(retrievals[ix]);\r
        }\r
        return abi.encodePacked(\r
            WitnetBuffer.concat(encodedSources),\r
            encode(uint64(aggregatorInnerBytecode.length), bytes1(0x1a)),\r
            aggregatorInnerBytecode,\r
            encode(uint64(tallyInnerBytecode.length), bytes1(0x22)),\r
            tallyInnerBytecode\r
        );\r
    }\r
    \r
    function encode(\r
            Witnet.RadonRetrieval[] memory sources,\r
            string[][] memory args,\r
            bytes memory aggregatorInnerBytecode,\r
            bytes memory tallyInnerBytecode,\r
            uint16\r
        )\r
        public pure\r
        returns (bytes memory)\r
    {\r
        bytes[] memory encodedSources = new bytes[](sources.length);\r
        for (uint ix = 0; ix < sources.length; ix ++) {\r
            replaceWildcards(sources[ix], args[ix]);\r
            encodedSources[ix] = encode(sources[ix]);\r
        }\r
        return abi.encodePacked(\r
            WitnetBuffer.concat(encodedSources),\r
            encode(uint64(aggregatorInnerBytecode.length), bytes1(0x1a)),\r
            aggregatorInnerBytecode,\r
            encode(uint64(tallyInnerBytecode.length), bytes1(0x22)),\r
            tallyInnerBytecode\r
        );\r
    }\r
\r
    function encode(Witnet.RadonReducer memory reducer)\r
        public pure\r
        returns (bytes memory bytecode)\r
    {\r
        // if (reducer.script.length == 0) {\r
            for (uint ix = 0; ix < reducer.filters.length; ix ++) {\r
                bytecode = abi.encodePacked(\r
                    bytecode,\r
                    encode(reducer.filters[ix])\r
                );\r
            }\r
            bytecode = abi.encodePacked(\r
                bytecode,\r
                encode(reducer.opcode)\r
            );\r
        // } else {\r
        //     return abi.encodePacked(\r
        //         encode(uint64(reducer.script.length), bytes1(0x18)),\r
        //         reducer.script\r
        //     );\r
        // }\r
    }\r
\r
    function encode(Witnet.RadonFilter memory filter)\r
        public pure\r
        returns (bytes memory bytecode)\r
    {        \r
        bytecode = abi.encodePacked(\r
            encode(uint64(filter.opcode), bytes1(0x08)),\r
            filter.cborArgs.length > 0\r
                ? abi.encodePacked(\r
                    encode(uint64(filter.cborArgs.length), bytes1(0x12)),\r
                    filter.cborArgs\r
                ) : bytes("")\r
        );\r
        return abi.encodePacked(\r
            encode(uint64(bytecode.length), bytes1(0x0a)),\r
            bytecode\r
        );\r
    }\r
\r
    function encode(Witnet.RadonReduceOpcodes opcode)\r
        public pure\r
        returns (bytes memory)\r
    {\r
        \r
        return encode(uint64(opcode), bytes1(0x10));\r
    }\r
\r
    function encode(Witnet.RadonSLAv1 memory sla)\r
        public pure\r
        returns (bytes memory)\r
    {\r
        return abi.encodePacked(\r
            encode(uint64(sla.witnessReward), bytes1(0x10)),\r
            encode(uint64(sla.numWitnesses), bytes1(0x18)),\r
            encode(uint64(sla.minerCommitRevealFee), bytes1(0x20)),\r
            encode(uint64(sla.minConsensusPercentage), bytes1(0x28)),\r
            encode(uint64(sla.witnessCollateral), bytes1(0x30))\r
        );\r
    }\r
\r
    function replaceCborStringsFromBytes(\r
            bytes memory data,\r
            uint8 argIndex,\r
            string memory argValue\r
        )\r
        public pure\r
        returns (bytes memory)\r
    {\r
        WitnetCBOR.CBOR memory cbor = WitnetCBOR.fromBytes(data);\r
        while (!cbor.eof()) {\r
            if (cbor.majorType == WitnetCBOR.MAJOR_TYPE_STRING) {\r
                _replaceCborWildcard(cbor, argIndex, argValue);\r
                cbor = cbor.settle();\r
            } else {\r
                cbor = cbor.skip().settle();\r
            }\r
        }\r
        return cbor.buffer.data;\r
    }\r
\r
    function replaceCborStringsFromBytes(\r
            bytes memory data,\r
            string[] memory args\r
        )\r
        public pure\r
        returns (bytes memory)\r
    {\r
        WitnetCBOR.CBOR memory cbor = WitnetCBOR.fromBytes(data);\r
        while (!cbor.eof()) {\r
            if (cbor.majorType == WitnetCBOR.MAJOR_TYPE_STRING) {\r
                _replaceCborWildcards(cbor, args);\r
                cbor = cbor.settle();\r
            } else {\r
                cbor = cbor.skip().settle();\r
            }\r
        }\r
        return cbor.buffer.data;\r
    }\r
\r
    function replaceWildcards(Witnet.RadonRetrieval memory self, uint8 argIndex, string memory argValue)\r
        public pure\r
        returns (Witnet.RadonRetrieval memory)\r
    {\r
        self.url = WitnetBuffer.replace(self.url, argIndex, argValue);\r
        self.body = WitnetBuffer.replace(self.body, argIndex, argValue);\r
        self.radonScript = replaceCborStringsFromBytes(self.radonScript, argIndex, argValue);\r
        for (uint _ix = 0 ; _ix < self.headers.length; _ix ++) {\r
            self.headers[_ix][0] = WitnetBuffer.replace(self.headers[_ix][0], argIndex, argValue);\r
            self.headers[_ix][1] = WitnetBuffer.replace(self.headers[_ix][1], argIndex, argValue);\r
        }\r
        return self;\r
    }\r
\r
    function replaceWildcards(Witnet.RadonRetrieval memory self, string[] memory args)\r
        public pure\r
        returns (Witnet.RadonRetrieval memory)\r
    {\r
        self.url = WitnetBuffer.replace(self.url, args);\r
        self.body = WitnetBuffer.replace(self.body, args);\r
        self.radonScript = replaceCborStringsFromBytes(self.radonScript, args);\r
        for (uint _ix = 0 ; _ix < self.headers.length; _ix ++) {\r
            self.headers[_ix][0] = WitnetBuffer.replace(self.headers[_ix][0], args);\r
            self.headers[_ix][1] = WitnetBuffer.replace(self.headers[_ix][1], args);\r
        }\r
        return self;\r
    }\r
\r
    function validate(\r
            Witnet.RadonRetrievalMethods method,\r
            string memory url,\r
            string memory body,\r
            string[2][] memory headers,\r
            bytes memory script\r
        )\r
        public pure\r
        returns (bytes32)\r
    {\r
        if (!(\r
            method == Witnet.RadonRetrievalMethods.HttpPost\r
            || (method == Witnet.RadonRetrievalMethods.HttpGet && bytes(body).length == 0)\r
            || (method == Witnet.RadonRetrievalMethods.HttpHead && bytes(body).length == 0)\r
            || (method == Witnet.RadonRetrievalMethods.RNG\r
                && bytes(url).length == 0\r
                && bytes(body).length == 0\r
                && headers.length == 0\r
                && script.length >= 1\r
            )\r
        )) {\r
            revert UnsupportedDataRequestMethod(\r
                uint8(method),\r
                url,\r
                body,\r
                headers\r
            );\r
        }\r
        return keccak256(abi.encode(method, url, body, headers, script));\r
    }\r
    \r
    function validate(\r
            Witnet.RadonDataTypes dataType,\r
            uint16 maxDataSize\r
        )\r
        public pure\r
        returns (uint16)\r
    {\r
        if (\r
            dataType == Witnet.RadonDataTypes.Any\r
                || dataType == Witnet.RadonDataTypes.String\r
                || dataType == Witnet.RadonDataTypes.Bytes\r
                || dataType == Witnet.RadonDataTypes.Array\r
                || dataType == Witnet.RadonDataTypes.Map\r
        ) {\r
            if (maxDataSize == 0) {\r
                revert UnsupportedRadonDataType(\r
                    uint8(dataType),\r
                    maxDataSize\r
                );\r
            }\r
            return maxDataSize + 3; // todo?: determine CBOR-encoding length overhead??\r
        \r
        } else if (\r
            dataType == Witnet.RadonDataTypes.Integer\r
                || dataType == Witnet.RadonDataTypes.Float\r
                || dataType == Witnet.RadonDataTypes.Bool\r
        ) {\r
            return 9; \r
        \r
        } else {\r
            revert UnsupportedRadonDataType(\r
                uint8(dataType),\r
                size(dataType)\r
            );\r
        }\r
    }\r
\r
    function validate(Witnet.RadonFilter memory filter)\r
        public pure\r
    {\r
        if (\r
            filter.opcode == Witnet.RadonFilterOpcodes.StandardDeviation\r
        ) {\r
            // check filters that require arguments\r
            if (filter.cborArgs.length == 0) {\r
                revert UnsupportedRadonFilterArgs(uint8(filter.opcode), filter.cborArgs);\r
            }\r
        } else if (\r
            filter.opcode == Witnet.RadonFilterOpcodes.Mode\r
        ) {\r
            // check filters that don't require any arguments\r
            if (filter.cborArgs.length > 0) {\r
                revert UnsupportedRadonFilterArgs(uint8(filter.opcode), filter.cborArgs);\r
            }\r
        } else {\r
            // reject unsupported opcodes\r
            revert UnsupportedRadonFilterOpcode(uint8(filter.opcode));\r
        }\r
    }\r
\r
    function validate(Witnet.RadonReducer memory reducer)\r
        public pure\r
    {\r
        // if (reducer.script.length == 0) {\r
            if (!(\r
                reducer.opcode == Witnet.RadonReduceOpcodes.AverageMean \r
                    || reducer.opcode == Witnet.RadonReduceOpcodes.StandardDeviation\r
                    || reducer.opcode == Witnet.RadonReduceOpcodes.Mode\r
                    || reducer.opcode == Witnet.RadonReduceOpcodes.ConcatenateAndHash\r
                    || reducer.opcode == Witnet.RadonReduceOpcodes.AverageMedian\r
            )) {\r
                revert UnsupportedRadonReducerOpcode(uint8(reducer.opcode));\r
            }\r
            for (uint ix = 0; ix < reducer.filters.length; ix ++) {\r
                validate(reducer.filters[ix]);\r
            }\r
        // } else {\r
        //     if (uint8(reducer.opcode) != 0xff || reducer.filters.length > 0) {\r
        //         revert UnsupportedRadonReducerScript(\r
        //             uint8(reducer.opcode),\r
        //             reducer.script,\r
        //             0\r
        //         );\r
        //     }\r
        // }\r
    }\r
\r
    function validate(Witnet.RadonSLAv1 memory sla)\r
        public pure\r
    {\r
        if (sla.witnessReward == 0) {\r
            revert("WitOracleRadonEncodingLib: invalid SLA: no reward");\r
        }\r
        if (sla.numWitnesses == 0) {\r
            revert("WitOracleRadonEncodingLib: invalid SLA: no witnesses");\r
        } else if (sla.numWitnesses > 127) {\r
            revert("WitOracleRadonEncodingLib: invalid SLA: too many witnesses (>127)");\r
        }\r
        if (\r
            sla.minConsensusPercentage < 51 \r
                || sla.minConsensusPercentage > 99\r
        ) {\r
            revert("WitOracleRadonEncodingLib: invalid SLA: consensus percentage out of range");\r
        }\r
        if (sla.witnessCollateral > 0) {\r
            revert("WitOracleRadonEncodingLib: invalid SLA: no collateral");\r
        }\r
        if (sla.witnessCollateral / sla.witnessReward > 127) {\r
            revert("WitOracleRadonEncodingLib: invalid SLA: collateral/reward ratio too high (>127)");\r
        }\r
    }\r
\r
    function verifyRadonScriptResultDataType(bytes memory script)\r
        public pure\r
        returns (Witnet.RadonDataTypes)\r
    {\r
        return _verifyRadonScriptResultDataType(\r
            WitnetCBOR.fromBytes(script),\r
            false\r
        );\r
    }\r
\r
\r
    /// ===============================================================================================================\r
    /// --- WitOracleRadonEncodingLib private methods ---------------------------------------------------------------------------------\r
\r
    function _replaceCborWildcard(\r
            WitnetCBOR.CBOR memory self,\r
            uint8 argIndex,\r
            string memory argValue\r
        ) private pure\r
    {\r
        uint _rewind = self.len;\r
        uint _start = self.buffer.cursor;\r
        bytes memory _peeks = bytes(self.readString());\r
        (bytes memory _pokes, uint _replacements) = WitnetBuffer.replace(_peeks, argIndex, argValue);\r
        if (_replacements > 0) {\r
            bytes memory _encodedPokes = encode(string(_pokes));\r
            self.buffer.cursor = _start - _rewind;\r
            self.buffer.mutate(\r
                _peeks.length + _rewind,\r
                _encodedPokes\r
            );\r
            self.buffer.cursor += _encodedPokes.length;\r
        }\r
    }\r
\r
    function _replaceCborWildcards(\r
            WitnetCBOR.CBOR memory self,\r
            string[] memory args\r
        ) private pure\r
    {\r
        uint _rewind = self.len;\r
        uint _start = self.buffer.cursor;\r
        bytes memory _peeks = bytes(self.readString());\r
        (bytes memory _pokes, uint _replacements) = WitnetBuffer.replace(_peeks, args);\r
        if (_replacements > 0) {\r
            bytes memory _encodedPokes = encode(string(_pokes));\r
            self.buffer.cursor = _start - _rewind;\r
            self.buffer.mutate(\r
                _peeks.length + _rewind,\r
                _encodedPokes\r
            );\r
            self.buffer.cursor += _encodedPokes.length;\r
        }\r
    }\r
    \r
    function _verifyRadonScriptResultDataType(WitnetCBOR.CBOR memory self, bool flip)\r
        private pure\r
        returns (Witnet.RadonDataTypes)\r
    {\r
        if (self.majorType == WitnetCBOR.MAJOR_TYPE_ARRAY) {\r
            WitnetCBOR.CBOR[] memory items = self.readArray();\r
            if (items.length > 1) {\r
                return flip\r
                    ? _verifyRadonScriptResultDataType(items[0], false)\r
                    : _verifyRadonScriptResultDataType(items[items.length - 2], true)\r
                ;\r
            } else {\r
                return Witnet.RadonDataTypes.Any;\r
            }\r
        } else if (self.majorType == WitnetCBOR.MAJOR_TYPE_INT) {            \r
            uint cursor = self.buffer.cursor;\r
            uint opcode = self.readUint();\r
            uint8 dataType = (opcode > WITNET_RADON_OPCODES_RESULT_TYPES.length\r
                ? 0xff\r
                : uint8(WITNET_RADON_OPCODES_RESULT_TYPES[opcode])\r
            );\r
            if (dataType > uint8(type(Witnet.RadonDataTypes).max)) {\r
                revert UnsupportedRadonScriptOpcode(\r
                    self.buffer.data,\r
                    cursor,\r
                    uint8(opcode)\r
                );\r
            }\r
            return Witnet.RadonDataTypes(dataType);\r
        } else {\r
            revert WitnetCBOR.UnexpectedMajorType(\r
                WitnetCBOR.MAJOR_TYPE_INT,\r
                self.majorType\r
            );\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 is 10,000 times the actual value. The input bytes are\r
  /// expected to follow the 16-bit base-2 format (a.k.a. `binary16`) in the IEEE 754-2008 standard.\r
  /// @param buffer An instance of `Buffer`.\r
  /// @return result The `int32` value of the next 4 bytes in the buffer counting from the cursor position.\r
  function readFloat16(Buffer memory buffer)\r
    internal pure\r
    returns (int32 result)\r
  {\r
    uint32 value = readUint16(buffer);\r
    // Get bit at position 0\r
    uint32 sign = value & 0x8000;\r
    // Get bits 1 to 5, then normalize to the [-15, 16] range so as to counterweight the IEEE 754 exponent bias\r
    int32 exponent = (int32(value & 0x7c00) >> 10) - 15;\r
    // Get bits 6 to 15\r
    int32 fraction = int32(value & 0x03ff);\r
    // Add 2^10 to the fraction if exponent is not -15\r
    if (exponent != -15) {\r
      fraction |= 0x400;\r
    } else if (exponent == 16) {\r
      revert(\r
        string(abi.encodePacked(\r
          "WitnetBuffer.readFloat16: ",\r
          sign != 0 ? "negative" : hex"",\r
          " infinity"\r
        ))\r
      );\r
    }\r
    // Compute `2 ^ exponent · (1 + fraction / 1024)`\r
    if (exponent >= 0) {\r
      result = int32(int(\r
        int(1 << uint256(int256(exponent)))\r
          * 10000\r
          * fraction\r
      ) >> 10);\r
    } else {\r
      result = int32(int(\r
        int(fraction)\r
          * 10000\r
          / int(1 << uint(int(- exponent)))\r
      ) >> 10);\r
    }\r
    // Make the result negative if the sign bit is not 0\r
    if (sign != 0) {\r
      result *= -1;\r
    }\r
  }\r
\r
  /// @notice Consume the next 4 bytes from the buffer as an IEEE 754-2008 floating point number enclosed into an `int`.\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 `float32`\r
  /// use cases. In other words, the integer output of this method is 10^9 times the actual value. The input bytes are\r
  /// expected to follow the 64-bit base-2 format (a.k.a. `binary32`) in the IEEE 754-2008 standard.\r
  /// @param buffer An instance of `Buffer`.\r
  /// @return result The `int` value of the next 8 bytes in the buffer counting from the cursor position.\r
  function readFloat32(Buffer memory buffer)\r
    internal pure\r
    returns (int result)\r
  {\r
    uint value = readUint32(buffer);\r
    // Get bit at position 0\r
    uint sign = value & 0x80000000;\r
    // Get bits 1 to 8, then normalize to the [-127, 128] range so as to counterweight the IEEE 754 exponent bias\r
    int exponent = (int(value & 0x7f800000) >> 23) - 127;\r
    // Get bits 9 to 31\r
    int fraction = int(value & 0x007fffff);\r
    // Add 2^23 to the fraction if exponent is not -127\r
    if (exponent != -127) {\r
      fraction |= 0x800000;\r
    } else if (exponent == 128) {\r
      revert(\r
        string(abi.encodePacked(\r
          "WitnetBuffer.readFloat32: ",\r
          sign != 0 ? "negative" : hex"",\r
          " infinity"\r
        ))\r
      );\r
    }\r
    // Compute `2 ^ exponent · (1 + fraction / 2^23)`\r
    if (exponent >= 0) {\r
      result = (\r
        int(1 << uint(exponent))\r
          * (10 ** 9)\r
          * fraction\r
      ) >> 23;\r
    } else {\r
      result = (\r
        fraction \r
          * (10 ** 9)\r
          / int(1 << uint(-exponent)) \r
      ) >> 23;\r
    }\r
    // Make the result negative if the sign bit is not 0\r
    if (sign != 0) {\r
      result *= -1;\r
    }\r
  }\r
\r
  /// @notice Consume the next 8 bytes from the buffer as an IEEE 754-2008 floating point number enclosed into an `int`.\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 `float64`\r
  /// use cases. In other words, the integer output of this method is 10^15 times the actual value. The input bytes are\r
  /// expected to follow the 64-bit base-2 format (a.k.a. `binary64`) in the IEEE 754-2008 standard.\r
  /// @param buffer An instance of `Buffer`.\r
  /// @return result The `int` value of the next 8 bytes in the buffer counting from the cursor position.\r
  function readFloat64(Buffer memory buffer)\r
    internal pure\r
    returns (int result)\r
  {\r
    uint value = readUint64(buffer);\r
    // Get bit at position 0\r
    uint sign = value & 0x8000000000000000;\r
    // Get bits 1 to 12, then normalize to the [-1023, 1024] range so as to counterweight the IEEE 754 exponent bias\r
    int exponent = (int(value & 0x7ff0000000000000) >> 52) - 1023;\r
    // Get bits 6 to 15\r
    int fraction = int(value & 0x000fffffffffffff);\r
    // Add 2^52 to the fraction if exponent is not -1023\r
    if (exponent != -1023) {\r
      fraction |= 0x10000000000000;\r
    } else if (exponent == 1024) {\r
      revert(\r
        string(abi.encodePacked(\r
          "WitnetBuffer.readFloat64: ",\r
          sign != 0 ? "negative" : hex"",\r
          " infinity"\r
        ))\r
      );\r
    }\r
    // Compute `2 ^ exponent · (1 + fraction / 1024)`\r
    if (exponent >= 0) {\r
      result = (\r
        int(1 << uint(exponent))\r
          * (10 ** 15)\r
          * fraction\r
      ) >> 52;\r
    } else {\r
      result = (\r
        fraction \r
          * (10 ** 15)\r
          / int(1 << uint(-exponent)) \r
      ) >> 52;\r
    }\r
    // Make the result negative if the sign bit is not 0\r
    if (sign != 0) {\r
      result *= -1;\r
    }\r
  }\r
\r
  // Read a text string of a given length from a buffer. Returns a `bytes memory` value for the sake of genericness,\r
  /// but it can be easily casted into a string with `string(result)`.\r
  // solium-disable-next-line security/no-assign-params\r
  function readText(\r
      WitnetBuffer.Buffer memory buffer,\r
      uint64 length\r
    )\r
    internal pure\r
    returns (bytes memory text)\r
  {\r
    text = new bytes(length);\r
    unchecked {\r
      for (uint64 index = 0; index < length; index ++) {\r
        uint8 char = readUint8(buffer);\r
        if (char & 0x80 != 0) {\r
          if (char < 0xe0) {\r
            char = (char & 0x1f) << 6\r
              | (readUint8(buffer) & 0x3f);\r
            length -= 1;\r
          } else if (char < 0xf0) {\r
            char  = (char & 0x0f) << 12\r
              | (readUint8(buffer) & 0x3f) << 6\r
              | (readUint8(buffer) & 0x3f);\r
            length -= 2;\r
          } else {\r
            char = (char & 0x0f) << 18\r
              | (readUint8(buffer) & 0x3f) << 12\r
              | (readUint8(buffer) & 0x3f) << 6  \r
              | (readUint8(buffer) & 0x3f);\r
            length -= 3;\r
          }\r
        }\r
        text[index] = bytes1(char);\r
      }\r
      // Adjust text to actual length:\r
      assembly {\r
        mstore(text, length)\r
      }\r
    }\r
  }\r
\r
  /// @notice Read and consume the next byte from the buffer as an `uint8`.\r
  /// @param buffer An instance of `Buffer`.\r
  /// @return value The `uint8` value of the next byte in the buffer counting from the cursor position.\r
  function readUint8(Buffer memory buffer)\r
    internal pure\r
    withinRange(buffer.cursor, buffer.data.length)\r
    returns (uint8 value)\r
  {\r
    bytes memory data = buffer.data;\r
    uint offset = buffer.cursor;\r
    assembly {\r
      value := mload(add(add(data, 1), offset))\r
    }\r
    buffer.cursor ++;\r
  }\r
\r
  /// @notice Read and consume the next 2 bytes from the buffer as an `uint16`.\r
  /// @param buffer An instance of `Buffer`.\r
  /// @return value The `uint16` value of the next 2 bytes in the buffer counting from the cursor position.\r
  function readUint16(Buffer memory buffer)\r
    internal pure\r
    withinRange(buffer.cursor + 2, buffer.data.length)\r
    returns (uint16 value)\r
  {\r
    bytes memory data = buffer.data;\r
    uint offset = buffer.cursor;\r
    assembly {\r
      value := mload(add(add(data, 2), offset))\r
    }\r
    buffer.cursor += 2;\r
  }\r
\r
  /// @notice Read and consume the next 4 bytes from the buffer as an `uint32`.\r
  /// @param buffer An instance of `Buffer`.\r
  /// @return value The `uint32` value of the next 4 bytes in the buffer counting from the cursor position.\r
  function readUint32(Buffer memory buffer)\r
    internal pure\r
    withinRange(buffer.cursor + 4, buffer.data.length)\r
    returns (uint32 value)\r
  {\r
    bytes memory data = buffer.data;\r
    uint offset = buffer.cursor;\r
    assembly {\r
      value := mload(add(add(data, 4), offset))\r
    }\r
    buffer.cursor += 4;\r
  }\r
\r
  /// @notice Read and consume the next 8 bytes from the buffer as an `uint64`.\r
  /// @param buffer An instance of `Buffer`.\r
  /// @return value The `uint64` value of the next 8 bytes in the buffer counting from the cursor position.\r
  function readUint64(Buffer memory buffer)\r
    internal pure\r
    withinRange(buffer.cursor + 8, buffer.data.length)\r
    returns (uint64 value)\r
  {\r
    bytes memory data = buffer.data;\r
    uint offset = buffer.cursor;\r
    assembly {\r
      value := mload(add(add(data, 8), offset))\r
    }\r
    buffer.cursor += 8;\r
  }\r
\r
  /// @notice Read and consume the next 16 bytes from the buffer as an `uint128`.\r
  /// @param buffer An instance of `Buffer`.\r
  /// @return value The `uint128` value of the next 16 bytes in the buffer counting from the cursor position.\r
  function readUint128(Buffer memory buffer)\r
    internal pure\r
    withinRange(buffer.cursor + 16, buffer.data.length)\r
    returns (uint128 value)\r
  {\r
    bytes memory data = buffer.data;\r
    uint offset = buffer.cursor;\r
    assembly {\r
      value := mload(add(add(data, 16), offset))\r
    }\r
    buffer.cursor += 16;\r
  }\r
\r
  /// @notice Read and consume the next 32 bytes from the buffer as an `uint256`.\r
  /// @param buffer An instance of `Buffer`.\r
  /// @return value The `uint256` value of the next 32 bytes in the buffer counting from the cursor position.\r
  function readUint256(Buffer memory buffer)\r
    internal pure\r
    withinRange(buffer.cursor + 32, buffer.data.length)\r
    returns (uint256 value)\r
  {\r
    bytes memory data = buffer.data;\r
    uint offset = buffer.cursor;\r
    assembly {\r
      value := mload(add(add(data, 32), offset))\r
    }\r
    buffer.cursor += 32;\r
  }\r
\r
  /// @notice Count number of required parameters for given bytes arrays\r
  /// @dev Wildcard format: "\#\", with # in ["0".."9"].\r
  /// @param input Bytes array containing strings.\r
  /// @param count Highest wildcard index found, plus 1.\r
  function argsCountOf(bytes memory input)\r
    internal pure\r
    returns (uint8 count)\r
  {\r
    if (input.length < 3) {\r
      return 0;\r
    }\r
    unchecked {\r
      uint ix = 0; \r
      uint length = input.length - 2;\r
      for (; ix < length; ) {\r
        if (\r
          input[ix] == bytes1("\\")\r
            && input[ix + 2] == bytes1("\\")\r
            && input[ix + 1] >= bytes1("0")\r
            && input[ix + 1] <= bytes1("9")\r
        ) {\r
          uint8 ax = uint8(uint8(input[ix + 1]) - uint8(bytes1("0")) + 1);\r
          if (ax > count) {\r
            count = ax;\r
          }\r
          ix += 3;\r
        } else {\r
          ix ++;\r
        }\r
      }\r
    }\r
  }\r
\r
  /// @notice Replace indexed bytes-wildcards by correspondent substrings.\r
  /// @dev Wildcard format: "\#\", with # in ["0".."9"].\r
  /// @param input Bytes array containing strings.\r
  /// @param args Array of substring values for replacing indexed wildcards.\r
  /// @return output Resulting bytes array after replacing all wildcards.\r
  /// @return hits Total number of replaced wildcards.\r
  function replace(bytes memory input, string[] memory args)\r
    internal pure\r
    returns (bytes memory output, uint hits)\r
  {\r
    uint ix = 0; uint lix = 0;\r
    uint inputLength;\r
    uint inputPointer;\r
    uint outputLength;\r
    uint outputPointer;    \r
    uint source;\r
    uint sourceLength;\r
    uint sourcePointer;\r
\r
    if (input.length < 3) {\r
      return (input, 0);\r
    }\r
    \r
    assembly {\r
      // set starting input pointer\r
      inputPointer := add(input, 32)\r
      // get safe output location\r
      output := mload(0x40)\r
      // set starting output pointer\r
      outputPointer := add(output, 32)\r
    }         \r
\r
    unchecked {\r
      uint length = input.length - 2;\r
      for (; ix < length; ) {\r
        if (\r
          input[ix] == bytes1("\\")\r
            && input[ix + 2] == bytes1("\\")\r
            && input[ix + 1] >= bytes1("0")\r
            && input[ix + 1] <= bytes1("9")\r
        ) {\r
          inputLength = (ix - lix);\r
          if (ix > lix) {\r
            memcpy(\r
              outp

Tags:
Proxy, Upgradeable, Factory, Oracle|addr:0x5a13430a283701be24ce7e1712b38f31d4b9ac58|verified:true|block:23575460|tx:0x9a20641be8571534e34432a94687372242cae95322a2c4cb808f008347c49de8|first_check:1760442687

Submitted on: 2025-10-14 13:51:28

Comments

Log in to comment.

No comments yet.