Contract Overview
Balance:
0 CELO
CELO Value:
$0.00
My Name Tag:
Not Available, login to update
Txn Hash | Method |
Block
|
From
|
To
|
Value | [Txn Fee] | |||
---|---|---|---|---|---|---|---|---|---|
0xf84b52371e4e84860a11c0f54661565043da89ef4791079b78882903054146ee | 0x60806040 | 18257593 | 11 days 15 hrs ago | 0xbf841a8229f4995c080703098fb73455c4e7215b | IN | Create: CustomUrlsConsumerBase | 0 CELO | 0.0041423325 |
[ Download CSV Export ]
Contract Source Code Verified (Exact Match)
Contract Name:
CustomUrlsConsumerBase
Compiler Version
v0.8.17+commit.8df45f5f
Contract Source Code (Solidity)
/** *Submitted for verification at celoscan.io on 2023-03-16 */ // SPDX-License-Identifier: BUSL-1.1 // File: https://github.com/redstone-finance/redstone-oracles-monorepo/blob/4470072aab0e5a64ded46dda53a456f7d91f5d90/packages/evm-connector/contracts/libs/SignatureLib.sol pragma solidity ^0.8.4; library SignatureLib { uint256 constant ECDSA_SIG_R_BS = 32; uint256 constant ECDSA_SIG_S_BS = 32; function recoverSignerAddress(bytes32 signedHash, uint256 signatureCalldataNegativeOffset) internal pure returns (address) { bytes32 r; bytes32 s; uint8 v; assembly { let signatureCalldataStartPos := sub(calldatasize(), signatureCalldataNegativeOffset) r := calldataload(signatureCalldataStartPos) signatureCalldataStartPos := add(signatureCalldataStartPos, ECDSA_SIG_R_BS) s := calldataload(signatureCalldataStartPos) signatureCalldataStartPos := add(signatureCalldataStartPos, ECDSA_SIG_S_BS) v := byte(0, calldataload(signatureCalldataStartPos)) // last byte of the signature memory array } return ecrecover(signedHash, v, r, s); } } // File: https://github.com/redstone-finance/redstone-oracles-monorepo/blob/4470072aab0e5a64ded46dda53a456f7d91f5d90/packages/evm-connector/contracts/libs/BitmapLib.sol pragma solidity ^0.8.4; library BitmapLib { function setBitInBitmap(uint256 bitmap, uint256 bitIndex) internal pure returns (uint256) { return bitmap | (1 << bitIndex); } function getBitFromBitmap(uint256 bitmap, uint256 bitIndex) internal pure returns (bool) { uint256 bitAtIndex = bitmap & (1 << bitIndex); return bitAtIndex > 0; } } // File: https://github.com/redstone-finance/redstone-oracles-monorepo/blob/4470072aab0e5a64ded46dda53a456f7d91f5d90/packages/evm-connector/contracts/core/RedstoneConstants.sol pragma solidity ^0.8.4; /** * @title The base contract with helpful constants * @author The Redstone Oracles team * @dev It mainly contains redstone-related values, which improve readability * of other contracts (e.g. CalldataExtractor and RedstoneConsumerBase) */ contract RedstoneConstants { // === Abbreviations === // BS - Bytes size // PTR - Pointer (memory location) // SIG - Signature // Solidity and YUL constants uint256 internal constant STANDARD_SLOT_BS = 32; uint256 internal constant FREE_MEMORY_PTR = 0x40; uint256 internal constant BYTES_ARR_LEN_VAR_BS = 32; uint256 internal constant FUNCTION_SIGNATURE_BS = 4; uint256 internal constant REVERT_MSG_OFFSET = 68; // Revert message structure described here: https://ethereum.stackexchange.com/a/66173/106364 uint256 internal constant STRING_ERR_MESSAGE_MASK = 0x08c379a000000000000000000000000000000000000000000000000000000000; // RedStone protocol consts uint256 internal constant SIG_BS = 65; uint256 internal constant TIMESTAMP_BS = 6; uint256 internal constant DATA_PACKAGES_COUNT_BS = 2; uint256 internal constant DATA_POINTS_COUNT_BS = 3; uint256 internal constant DATA_POINT_VALUE_BYTE_SIZE_BS = 4; uint256 internal constant DATA_POINT_SYMBOL_BS = 32; uint256 internal constant DEFAULT_DATA_POINT_VALUE_BS = 32; uint256 internal constant UNSIGNED_METADATA_BYTE_SIZE_BS = 3; uint256 internal constant REDSTONE_MARKER_BS = 9; // byte size of 0x000002ed57011e0000 uint256 internal constant REDSTONE_MARKER_MASK = 0x0000000000000000000000000000000000000000000000000002ed57011e0000; // Derived values (based on consts) uint256 internal constant TIMESTAMP_NEGATIVE_OFFSET_IN_DATA_PACKAGE_WITH_STANDARD_SLOT_BS = 104; // SIG_BS + DATA_POINTS_COUNT_BS + DATA_POINT_VALUE_BYTE_SIZE_BS + STANDARD_SLOT_BS uint256 internal constant DATA_PACKAGE_WITHOUT_DATA_POINTS_BS = 78; // DATA_POINT_VALUE_BYTE_SIZE_BS + TIMESTAMP_BS + DATA_POINTS_COUNT_BS + SIG_BS uint256 internal constant DATA_PACKAGE_WITHOUT_DATA_POINTS_AND_SIG_BS = 13; // DATA_POINT_VALUE_BYTE_SIZE_BS + TIMESTAMP_BS + DATA_POINTS_COUNT_BS uint256 internal constant REDSTONE_MARKER_BS_PLUS_STANDARD_SLOT_BS = 41; // REDSTONE_MARKER_BS + STANDARD_SLOT_BS // Error messages error CalldataOverOrUnderFlow(); error IncorrectUnsignedMetadataSize(); error InsufficientNumberOfUniqueSigners(uint256 receivedSignersCount, uint256 requiredSignersCount); error EachSignerMustProvideTheSameValue(); error EmptyCalldataPointersArr(); error InvalidCalldataPointer(); error CalldataMustHaveValidPayload(); error SignerNotAuthorised(address receivedSigner); } // File: @openzeppelin/contracts/utils/math/SafeMath.sol // OpenZeppelin Contracts (last updated v4.6.0) (utils/math/SafeMath.sol) pragma solidity ^0.8.0; // CAUTION // This version of SafeMath should only be used with Solidity 0.8 or later, // because it relies on the compiler's built in overflow checks. /** * @dev Wrappers over Solidity's arithmetic operations. * * NOTE: `SafeMath` is generally not needed starting with Solidity 0.8, since the compiler * now has built in overflow checking. */ library SafeMath { /** * @dev Returns the addition of two unsigned integers, with an overflow flag. * * _Available since v3.4._ */ function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) { unchecked { uint256 c = a + b; if (c < a) return (false, 0); return (true, c); } } /** * @dev Returns the subtraction of two unsigned integers, with an overflow flag. * * _Available since v3.4._ */ function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) { unchecked { if (b > a) return (false, 0); return (true, a - b); } } /** * @dev Returns the multiplication of two unsigned integers, with an overflow flag. * * _Available since v3.4._ */ function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) { unchecked { // Gas optimization: this is cheaper than requiring 'a' not being zero, but the // benefit is lost if 'b' is also tested. // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522 if (a == 0) return (true, 0); uint256 c = a * b; if (c / a != b) return (false, 0); return (true, c); } } /** * @dev Returns the division of two unsigned integers, with a division by zero flag. * * _Available since v3.4._ */ function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) { unchecked { if (b == 0) return (false, 0); return (true, a / b); } } /** * @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag. * * _Available since v3.4._ */ function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) { unchecked { if (b == 0) return (false, 0); return (true, a % b); } } /** * @dev Returns the addition of two unsigned integers, reverting on * overflow. * * Counterpart to Solidity's `+` operator. * * Requirements: * * - Addition cannot overflow. */ function add(uint256 a, uint256 b) internal pure returns (uint256) { return a + b; } /** * @dev Returns the subtraction of two unsigned integers, reverting on * overflow (when the result is negative). * * Counterpart to Solidity's `-` operator. * * Requirements: * * - Subtraction cannot overflow. */ function sub(uint256 a, uint256 b) internal pure returns (uint256) { return a - b; } /** * @dev Returns the multiplication of two unsigned integers, reverting on * overflow. * * Counterpart to Solidity's `*` operator. * * Requirements: * * - Multiplication cannot overflow. */ function mul(uint256 a, uint256 b) internal pure returns (uint256) { return a * b; } /** * @dev Returns the integer division of two unsigned integers, reverting on * division by zero. The result is rounded towards zero. * * Counterpart to Solidity's `/` operator. * * Requirements: * * - The divisor cannot be zero. */ function div(uint256 a, uint256 b) internal pure returns (uint256) { return a / b; } /** * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), * reverting when dividing by zero. * * Counterpart to Solidity's `%` operator. This function uses a `revert` * opcode (which leaves remaining gas untouched) while Solidity uses an * invalid opcode to revert (consuming all remaining gas). * * Requirements: * * - The divisor cannot be zero. */ function mod(uint256 a, uint256 b) internal pure returns (uint256) { return a % b; } /** * @dev Returns the subtraction of two unsigned integers, reverting with custom message on * overflow (when the result is negative). * * CAUTION: This function is deprecated because it requires allocating memory for the error * message unnecessarily. For custom revert reasons use {trySub}. * * Counterpart to Solidity's `-` operator. * * Requirements: * * - Subtraction cannot overflow. */ function sub( uint256 a, uint256 b, string memory errorMessage ) internal pure returns (uint256) { unchecked { require(b <= a, errorMessage); return a - b; } } /** * @dev Returns the integer division of two unsigned integers, reverting with custom message on * division by zero. The result is rounded towards zero. * * Counterpart to Solidity's `/` operator. Note: this function uses a * `revert` opcode (which leaves remaining gas untouched) while Solidity * uses an invalid opcode to revert (consuming all remaining gas). * * Requirements: * * - The divisor cannot be zero. */ function div( uint256 a, uint256 b, string memory errorMessage ) internal pure returns (uint256) { unchecked { require(b > 0, errorMessage); return a / b; } } /** * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), * reverting with custom message when dividing by zero. * * CAUTION: This function is deprecated because it requires allocating memory for the error * message unnecessarily. For custom revert reasons use {tryMod}. * * Counterpart to Solidity's `%` operator. This function uses a `revert` * opcode (which leaves remaining gas untouched) while Solidity uses an * invalid opcode to revert (consuming all remaining gas). * * Requirements: * * - The divisor cannot be zero. */ function mod( uint256 a, uint256 b, string memory errorMessage ) internal pure returns (uint256) { unchecked { require(b > 0, errorMessage); return a % b; } } } // File: https://github.com/redstone-finance/redstone-oracles-monorepo/blob/4470072aab0e5a64ded46dda53a456f7d91f5d90/packages/evm-connector/contracts/core/CalldataExtractor.sol pragma solidity ^0.8.4; /** * @title The base contract with the main logic of data extraction from calldata * @author The Redstone Oracles team * @dev This contract was created to reuse the same logic in the RedstoneConsumerBase * and the ProxyConnector contracts */ contract CalldataExtractor is RedstoneConstants { using SafeMath for uint256; function _extractByteSizeOfUnsignedMetadata() internal pure returns (uint256) { // Checking if the calldata ends with the RedStone marker bool hasValidRedstoneMarker; assembly { let calldataLast32Bytes := calldataload(sub(calldatasize(), STANDARD_SLOT_BS)) hasValidRedstoneMarker := eq( REDSTONE_MARKER_MASK, and(calldataLast32Bytes, REDSTONE_MARKER_MASK) ) } if (!hasValidRedstoneMarker) { revert CalldataMustHaveValidPayload(); } // Using uint24, because unsigned metadata byte size number has 3 bytes uint24 unsignedMetadataByteSize; if (REDSTONE_MARKER_BS_PLUS_STANDARD_SLOT_BS > msg.data.length) { revert CalldataOverOrUnderFlow(); } assembly { unsignedMetadataByteSize := calldataload( sub(calldatasize(), REDSTONE_MARKER_BS_PLUS_STANDARD_SLOT_BS) ) } uint256 calldataNegativeOffset = unsignedMetadataByteSize + UNSIGNED_METADATA_BYTE_SIZE_BS + REDSTONE_MARKER_BS; if (calldataNegativeOffset + DATA_PACKAGES_COUNT_BS > msg.data.length) { revert IncorrectUnsignedMetadataSize(); } return calldataNegativeOffset; } // We return uint16, because unsigned metadata byte size number has 2 bytes function _extractDataPackagesCountFromCalldata(uint256 calldataNegativeOffset) internal pure returns (uint16 dataPackagesCount) { uint256 calldataNegativeOffsetWithStandardSlot = calldataNegativeOffset + STANDARD_SLOT_BS; if (calldataNegativeOffsetWithStandardSlot > msg.data.length) { revert CalldataOverOrUnderFlow(); } assembly { dataPackagesCount := calldataload( sub(calldatasize(), calldataNegativeOffsetWithStandardSlot) ) } return dataPackagesCount; } function _extractDataPointValueAndDataFeedId( uint256 calldataNegativeOffsetForDataPackage, uint256 defaultDataPointValueByteSize, uint256 dataPointIndex ) internal pure virtual returns (bytes32 dataPointDataFeedId, uint256 dataPointValue) { uint256 negativeOffsetToDataPoints = calldataNegativeOffsetForDataPackage + DATA_PACKAGE_WITHOUT_DATA_POINTS_BS; uint256 dataPointNegativeOffset = negativeOffsetToDataPoints.add( (1 + dataPointIndex).mul((defaultDataPointValueByteSize + DATA_POINT_SYMBOL_BS)) ); uint256 dataPointCalldataOffset = msg.data.length.sub(dataPointNegativeOffset); assembly { dataPointDataFeedId := calldataload(dataPointCalldataOffset) dataPointValue := calldataload(add(dataPointCalldataOffset, DATA_POINT_SYMBOL_BS)) } } function _extractDataPointsDetailsForDataPackage(uint256 calldataNegativeOffsetForDataPackage) internal pure returns (uint256 dataPointsCount, uint256 eachDataPointValueByteSize) { // Using uint24, because data points count byte size number has 3 bytes uint24 dataPointsCount_; // Using uint32, because data point value byte size has 4 bytes uint32 eachDataPointValueByteSize_; // Extract data points count uint256 negativeCalldataOffset = calldataNegativeOffsetForDataPackage + SIG_BS; uint256 calldataOffset = msg.data.length.sub(negativeCalldataOffset + STANDARD_SLOT_BS); assembly { dataPointsCount_ := calldataload(calldataOffset) } // Extract each data point value size calldataOffset = calldataOffset.sub(DATA_POINTS_COUNT_BS); assembly { eachDataPointValueByteSize_ := calldataload(calldataOffset) } // Prepare returned values dataPointsCount = dataPointsCount_; eachDataPointValueByteSize = eachDataPointValueByteSize_; } } // File: https://github.com/redstone-finance/redstone-oracles-monorepo/blob/4470072aab0e5a64ded46dda53a456f7d91f5d90/packages/evm-connector/contracts/libs/NumericArrayLib.sol pragma solidity ^0.8.4; library NumericArrayLib { // This function sort array in memory using bubble sort algorithm, // which performs even better than quick sort for small arrays uint256 constant BYTES_ARR_LEN_VAR_BS = 32; uint256 constant UINT256_VALUE_BS = 32; error CanNotPickMedianOfEmptyArray(); // This function modifies the array function pickMedian(uint256[] memory arr) internal pure returns (uint256) { if (arr.length == 0) { revert CanNotPickMedianOfEmptyArray(); } sort(arr); uint256 middleIndex = arr.length / 2; if (arr.length % 2 == 0) { uint256 sum = SafeMath.add(arr[middleIndex - 1], arr[middleIndex]); return sum / 2; } else { return arr[middleIndex]; } } function sort(uint256[] memory arr) internal pure { assembly { let arrLength := mload(arr) let valuesPtr := add(arr, BYTES_ARR_LEN_VAR_BS) let endPtr := add(valuesPtr, mul(arrLength, UINT256_VALUE_BS)) for { let arrIPtr := valuesPtr } lt(arrIPtr, endPtr) { arrIPtr := add(arrIPtr, UINT256_VALUE_BS) // arrIPtr += 32 } { for { let arrJPtr := valuesPtr } lt(arrJPtr, arrIPtr) { arrJPtr := add(arrJPtr, UINT256_VALUE_BS) // arrJPtr += 32 } { let arrI := mload(arrIPtr) let arrJ := mload(arrJPtr) if lt(arrI, arrJ) { mstore(arrIPtr, arrJ) mstore(arrJPtr, arrI) } } } } } } // File: https://github.com/redstone-finance/redstone-oracles-monorepo/blob/4470072aab0e5a64ded46dda53a456f7d91f5d90/packages/evm-connector/contracts/core/RedstoneDefaultsLib.sol pragma solidity ^0.8.4; /** * @title Default implementations of virtual redstone consumer base functions * @author The Redstone Oracles team */ library RedstoneDefaultsLib { uint256 constant DEFAULT_MAX_DATA_TIMESTAMP_DELAY_SECONDS = 3 minutes; uint256 constant DEFAULT_MAX_DATA_TIMESTAMP_AHEAD_SECONDS = 1 minutes; error TimestampFromTooLongFuture(uint256 receivedTimestampSeconds, uint256 blockTimestamp); error TimestampIsTooOld(uint256 receivedTimestampSeconds, uint256 blockTimestamp); function validateTimestamp(uint256 receivedTimestampMilliseconds) internal view { // Getting data timestamp from future seems quite unlikely // But we've already spent too much time with different cases // Where block.timestamp was less than dataPackage.timestamp. // Some blockchains may case this problem as well. // That's why we add MAX_BLOCK_TIMESTAMP_DELAY // and allow data "from future" but with a small delay uint256 receivedTimestampSeconds = receivedTimestampMilliseconds / 1000; if (block.timestamp < receivedTimestampSeconds) { if ((receivedTimestampSeconds - block.timestamp) > DEFAULT_MAX_DATA_TIMESTAMP_AHEAD_SECONDS) { revert TimestampFromTooLongFuture(receivedTimestampSeconds, block.timestamp); } } else if ((block.timestamp - receivedTimestampSeconds) > DEFAULT_MAX_DATA_TIMESTAMP_DELAY_SECONDS) { revert TimestampIsTooOld(receivedTimestampSeconds, block.timestamp); } } function aggregateValues(uint256[] memory values) internal pure returns (uint256) { return NumericArrayLib.pickMedian(values); } } // File: github/redstone-finance/redstone-oracles-monorepo/packages/evm-connector/contracts/core/RedstoneConsumerBase.sol pragma solidity ^0.8.4; /** * @title The base contract with the main Redstone logic * @author The Redstone Oracles team * @dev Do not use this contract directly in consumer contracts, take a * look at `RedstoneConsumerNumericBase` and `RedstoneConsumerBytesBase` instead */ abstract contract RedstoneConsumerBase is CalldataExtractor { using SafeMath for uint256; /* ========== VIRTUAL FUNCTIONS (MAY BE OVERRIDDEN IN CHILD CONTRACTS) ========== */ /** * @dev This function must be implemented by the child consumer contract. * It should return a unique index for a given signer address if the signer * is authorised, otherwise it should revert * @param receivedSigner The address of a signer, recovered from ECDSA signature * @return Unique index for a signer in the range [0..255] */ function getAuthorisedSignerIndex(address receivedSigner) public view virtual returns (uint8); /** * @dev This function may be overridden by the child consumer contract. * It should validate the timestamp against the current time (block.timestamp) * It should revert with a helpful message if the timestamp is not valid * @param receivedTimestampMilliseconds Timestamp extracted from calldata */ function validateTimestamp(uint256 receivedTimestampMilliseconds) public view virtual { RedstoneDefaultsLib.validateTimestamp(receivedTimestampMilliseconds); } /** * @dev This function should be overridden by the child consumer contract. * @return The minimum required value of unique authorised signers */ function getUniqueSignersThreshold() public view virtual returns (uint8) { return 1; } /** * @dev This function may be overridden by the child consumer contract. * It should aggregate values from different signers to a single uint value. * By default, it calculates the median value * @param values An array of uint256 values from different signers * @return Result of the aggregation in the form of a single number */ function aggregateValues(uint256[] memory values) public view virtual returns (uint256) { return RedstoneDefaultsLib.aggregateValues(values); } /* ========== FUNCTIONS WITH IMPLEMENTATION (CAN NOT BE OVERRIDDEN) ========== */ /** * @dev This is an internal helpful function for secure extraction oracle values * from the tx calldata. Security is achieved by signatures verification, timestamp * validation, and aggregating values from different authorised signers into a * single numeric value. If any of the required conditions (e.g. too old timestamp or * insufficient number of authorised signers) do not match, the function will revert. * * Note! You should not call this function in a consumer contract. You can use * `getOracleNumericValuesFromTxMsg` or `getOracleNumericValueFromTxMsg` instead. * * @param dataFeedIds An array of unique data feed identifiers * @return An array of the extracted and verified oracle values in the same order * as they are requested in dataFeedIds array */ function _securelyExtractOracleValuesFromTxMsg(bytes32[] memory dataFeedIds) internal view returns (uint256[] memory) { // Initializing helpful variables and allocating memory uint256[] memory uniqueSignerCountForDataFeedIds = new uint256[](dataFeedIds.length); uint256[] memory signersBitmapForDataFeedIds = new uint256[](dataFeedIds.length); uint256[][] memory valuesForDataFeeds = new uint256[][](dataFeedIds.length); for (uint256 i = 0; i < dataFeedIds.length; i++) { // The line below is commented because newly allocated arrays are filled with zeros // But we left it for better readability // signersBitmapForDataFeedIds[i] = 0; // <- setting to an empty bitmap valuesForDataFeeds[i] = new uint256[](getUniqueSignersThreshold()); } // Extracting the number of data packages from calldata uint256 calldataNegativeOffset = _extractByteSizeOfUnsignedMetadata(); uint256 dataPackagesCount = _extractDataPackagesCountFromCalldata(calldataNegativeOffset); calldataNegativeOffset += DATA_PACKAGES_COUNT_BS; // Saving current free memory pointer uint256 freeMemPtr; assembly { freeMemPtr := mload(FREE_MEMORY_PTR) } // Data packages extraction in a loop for (uint256 dataPackageIndex = 0; dataPackageIndex < dataPackagesCount; dataPackageIndex++) { // Extract data package details and update calldata offset uint256 dataPackageByteSize = _extractDataPackage( dataFeedIds, uniqueSignerCountForDataFeedIds, signersBitmapForDataFeedIds, valuesForDataFeeds, calldataNegativeOffset ); calldataNegativeOffset += dataPackageByteSize; // Shifting memory pointer back to the "safe" value assembly { mstore(FREE_MEMORY_PTR, freeMemPtr) } } // Validating numbers of unique signers and calculating aggregated values for each dataFeedId return _getAggregatedValues(valuesForDataFeeds, uniqueSignerCountForDataFeedIds); } /** * @dev This is a private helpful function, which extracts data for a data package based * on the given negative calldata offset, verifies them, and in the case of successful * verification updates the corresponding data package values in memory * * @param dataFeedIds an array of unique data feed identifiers * @param uniqueSignerCountForDataFeedIds an array with the numbers of unique signers * for each data feed * @param signersBitmapForDataFeedIds an array of signer bitmaps for data feeds * @param valuesForDataFeeds 2-dimensional array, valuesForDataFeeds[i][j] contains * j-th value for the i-th data feed * @param calldataNegativeOffset negative calldata offset for the given data package * * @return An array of the aggregated values */ function _extractDataPackage( bytes32[] memory dataFeedIds, uint256[] memory uniqueSignerCountForDataFeedIds, uint256[] memory signersBitmapForDataFeedIds, uint256[][] memory valuesForDataFeeds, uint256 calldataNegativeOffset ) private view returns (uint256) { uint256 signerIndex; ( uint256 dataPointsCount, uint256 eachDataPointValueByteSize ) = _extractDataPointsDetailsForDataPackage(calldataNegativeOffset); // We use scopes to resolve problem with too deep stack { uint48 extractedTimestamp; address signerAddress; bytes32 signedHash; bytes memory signedMessage; uint256 signedMessageBytesCount; signedMessageBytesCount = dataPointsCount.mul(eachDataPointValueByteSize + DATA_POINT_SYMBOL_BS) + DATA_PACKAGE_WITHOUT_DATA_POINTS_AND_SIG_BS; //DATA_POINT_VALUE_BYTE_SIZE_BS + TIMESTAMP_BS + DATA_POINTS_COUNT_BS uint256 timestampCalldataOffset = msg.data.length.sub( calldataNegativeOffset + TIMESTAMP_NEGATIVE_OFFSET_IN_DATA_PACKAGE_WITH_STANDARD_SLOT_BS); uint256 signedMessageCalldataOffset = msg.data.length.sub( calldataNegativeOffset + SIG_BS + signedMessageBytesCount); assembly { // Extracting the signed message signedMessage := extractBytesFromCalldata( signedMessageCalldataOffset, signedMessageBytesCount ) // Hashing the signed message signedHash := keccak256(add(signedMessage, BYTES_ARR_LEN_VAR_BS), signedMessageBytesCount) // Extracting timestamp extractedTimestamp := calldataload(timestampCalldataOffset) function initByteArray(bytesCount) -> ptr { ptr := mload(FREE_MEMORY_PTR) mstore(ptr, bytesCount) ptr := add(ptr, BYTES_ARR_LEN_VAR_BS) mstore(FREE_MEMORY_PTR, add(ptr, bytesCount)) } function extractBytesFromCalldata(offset, bytesCount) -> extractedBytes { let extractedBytesStartPtr := initByteArray(bytesCount) calldatacopy( extractedBytesStartPtr, offset, bytesCount ) extractedBytes := sub(extractedBytesStartPtr, BYTES_ARR_LEN_VAR_BS) } } // Validating timestamp validateTimestamp(extractedTimestamp); // Verifying the off-chain signature against on-chain hashed data signerAddress = SignatureLib.recoverSignerAddress( signedHash, calldataNegativeOffset + SIG_BS ); signerIndex = getAuthorisedSignerIndex(signerAddress); } // Updating helpful arrays { bytes32 dataPointDataFeedId; uint256 dataPointValue; for (uint256 dataPointIndex = 0; dataPointIndex < dataPointsCount; dataPointIndex++) { // Extracting data feed id and value for the current data point (dataPointDataFeedId, dataPointValue) = _extractDataPointValueAndDataFeedId( calldataNegativeOffset, eachDataPointValueByteSize, dataPointIndex ); for ( uint256 dataFeedIdIndex = 0; dataFeedIdIndex < dataFeedIds.length; dataFeedIdIndex++ ) { if (dataPointDataFeedId == dataFeedIds[dataFeedIdIndex]) { uint256 bitmapSignersForDataFeedId = signersBitmapForDataFeedIds[dataFeedIdIndex]; if ( !BitmapLib.getBitFromBitmap(bitmapSignersForDataFeedId, signerIndex) && /* current signer was not counted for current dataFeedId */ uniqueSignerCountForDataFeedIds[dataFeedIdIndex] < getUniqueSignersThreshold() ) { // Increase unique signer counter uniqueSignerCountForDataFeedIds[dataFeedIdIndex]++; // Add new value valuesForDataFeeds[dataFeedIdIndex][ uniqueSignerCountForDataFeedIds[dataFeedIdIndex] - 1 ] = dataPointValue; // Update signers bitmap signersBitmapForDataFeedIds[dataFeedIdIndex] = BitmapLib.setBitInBitmap( bitmapSignersForDataFeedId, signerIndex ); } // Breaking, as there couldn't be several indexes for the same feed ID break; } } } } // Return total data package byte size return DATA_PACKAGE_WITHOUT_DATA_POINTS_BS + (eachDataPointValueByteSize + DATA_POINT_SYMBOL_BS) * dataPointsCount; } /** * @dev This is a private helpful function, which aggregates values from different * authorised signers for the given arrays of values for each data feed * * @param valuesForDataFeeds 2-dimensional array, valuesForDataFeeds[i][j] contains * j-th value for the i-th data feed * @param uniqueSignerCountForDataFeedIds an array with the numbers of unique signers * for each data feed * * @return An array of the aggregated values */ function _getAggregatedValues( uint256[][] memory valuesForDataFeeds, uint256[] memory uniqueSignerCountForDataFeedIds ) private view returns (uint256[] memory) { uint256[] memory aggregatedValues = new uint256[](valuesForDataFeeds.length); uint256 uniqueSignersThreshold = getUniqueSignersThreshold(); for (uint256 dataFeedIndex = 0; dataFeedIndex < valuesForDataFeeds.length; dataFeedIndex++) { if (uniqueSignerCountForDataFeedIds[dataFeedIndex] < uniqueSignersThreshold) { revert InsufficientNumberOfUniqueSigners( uniqueSignerCountForDataFeedIds[dataFeedIndex], uniqueSignersThreshold); } uint256 aggregatedValueForDataFeedId = aggregateValues(valuesForDataFeeds[dataFeedIndex]); aggregatedValues[dataFeedIndex] = aggregatedValueForDataFeedId; } return aggregatedValues; } } // File: github/redstone-finance/redstone-oracles-monorepo/packages/evm-connector/contracts/core/RedstoneConsumerNumericBase.sol pragma solidity ^0.8.4; /** * @title The base contract for Redstone consumers' contracts that allows to * securely calculate numeric redstone oracle values * @author The Redstone Oracles team * @dev This contract can extend other contracts to allow them * securely fetch Redstone oracle data from transactions calldata */ abstract contract RedstoneConsumerNumericBase is RedstoneConsumerBase { /** * @dev This function can be used in a consumer contract to securely extract an * oracle value for a given data feed id. Security is achieved by * signatures verification, timestamp validation, and aggregating values * from different authorised signers into a single numeric value. If any of the * required conditions do not match, the function will revert. * Note! This function expects that tx calldata contains redstone payload in the end * Learn more about redstone payload here: https://github.com/redstone-finance/redstone-oracles-monorepo/tree/main/packages/evm-connector#readme * @param dataFeedId bytes32 value that uniquely identifies the data feed * @return Extracted and verified numeric oracle value for the given data feed id */ function getOracleNumericValueFromTxMsg(bytes32 dataFeedId) internal view virtual returns (uint256) { bytes32[] memory dataFeedIds = new bytes32[](1); dataFeedIds[0] = dataFeedId; return getOracleNumericValuesFromTxMsg(dataFeedIds)[0]; } /** * @dev This function can be used in a consumer contract to securely extract several * numeric oracle values for a given array of data feed ids. Security is achieved by * signatures verification, timestamp validation, and aggregating values * from different authorised signers into a single numeric value. If any of the * required conditions do not match, the function will revert. * Note! This function expects that tx calldata contains redstone payload in the end * Learn more about redstone payload here: https://github.com/redstone-finance/redstone-oracles-monorepo/tree/main/packages/evm-connector#readme * @param dataFeedIds An array of unique data feed identifiers * @return An array of the extracted and verified oracle values in the same order * as they are requested in the dataFeedIds array */ function getOracleNumericValuesFromTxMsg(bytes32[] memory dataFeedIds) internal view virtual returns (uint256[] memory) { return _securelyExtractOracleValuesFromTxMsg(dataFeedIds); } /** * @dev This function works similarly to the `getOracleNumericValuesFromTxMsg` with the * only difference that it allows to request oracle data for an array of data feeds * that may contain duplicates * * @param dataFeedIdsWithDuplicates An array of data feed identifiers (duplicates are allowed) * @return An array of the extracted and verified oracle values in the same order * as they are requested in the dataFeedIdsWithDuplicates array */ function getOracleNumericValuesWithDuplicatesFromTxMsg(bytes32[] memory dataFeedIdsWithDuplicates) internal view returns (uint256[] memory) { // Building an array without duplicates bytes32[] memory dataFeedIdsWithoutDuplicates = new bytes32[](dataFeedIdsWithDuplicates.length); bool alreadyIncluded; uint256 uniqueDataFeedIdsCount = 0; for (uint256 indexWithDup = 0; indexWithDup < dataFeedIdsWithDuplicates.length; indexWithDup++) { // Checking if current element is already included in `dataFeedIdsWithoutDuplicates` alreadyIncluded = false; for (uint256 indexWithoutDup = 0; indexWithoutDup < uniqueDataFeedIdsCount; indexWithoutDup++) { if (dataFeedIdsWithoutDuplicates[indexWithoutDup] == dataFeedIdsWithDuplicates[indexWithDup]) { alreadyIncluded = true; break; } } // Adding if not included if (!alreadyIncluded) { dataFeedIdsWithoutDuplicates[uniqueDataFeedIdsCount] = dataFeedIdsWithDuplicates[indexWithDup]; uniqueDataFeedIdsCount++; } } // Overriding dataFeedIdsWithoutDuplicates.length // Equivalent to: dataFeedIdsWithoutDuplicates.length = uniqueDataFeedIdsCount; assembly { mstore(dataFeedIdsWithoutDuplicates, uniqueDataFeedIdsCount) } // Requesting oracle values (without duplicates) uint256[] memory valuesWithoutDuplicates = getOracleNumericValuesFromTxMsg(dataFeedIdsWithoutDuplicates); // Preparing result values array uint256[] memory valuesWithDuplicates = new uint256[](dataFeedIdsWithDuplicates.length); for (uint256 indexWithDup = 0; indexWithDup < dataFeedIdsWithDuplicates.length; indexWithDup++) { for (uint256 indexWithoutDup = 0; indexWithoutDup < dataFeedIdsWithoutDuplicates.length; indexWithoutDup++) { if (dataFeedIdsWithDuplicates[indexWithDup] == dataFeedIdsWithoutDuplicates[indexWithoutDup]) { valuesWithDuplicates[indexWithDup] = valuesWithoutDuplicates[indexWithoutDup]; break; } } } return valuesWithDuplicates; } } // File: github/redstone-finance/redstone-oracles-monorepo/packages/evm-connector/contracts/data-services/CustomUrlsConsumerBase.sol pragma solidity ^0.8.4; contract CustomUrlsConsumerBase is RedstoneConsumerNumericBase { function getUniqueSignersThreshold() public view virtual override returns (uint8) { return 2; } function getAuthorisedSignerIndex(address signerAddress) public view virtual override returns (uint8) { if (signerAddress == 0x11fFFc9970c41B9bFB9Aa35Be838d39bce918CfF) { return 0; } else if (signerAddress == 0xdBcC2C6c892C8d3e3Fe4D325fEc810B7376A5Ed6) { return 1; } else { revert SignerNotAuthorised(signerAddress); } } }
[{"inputs":[],"name":"CalldataMustHaveValidPayload","type":"error"},{"inputs":[],"name":"CalldataOverOrUnderFlow","type":"error"},{"inputs":[],"name":"CanNotPickMedianOfEmptyArray","type":"error"},{"inputs":[],"name":"EachSignerMustProvideTheSameValue","type":"error"},{"inputs":[],"name":"EmptyCalldataPointersArr","type":"error"},{"inputs":[],"name":"IncorrectUnsignedMetadataSize","type":"error"},{"inputs":[{"internalType":"uint256","name":"receivedSignersCount","type":"uint256"},{"internalType":"uint256","name":"requiredSignersCount","type":"uint256"}],"name":"InsufficientNumberOfUniqueSigners","type":"error"},{"inputs":[],"name":"InvalidCalldataPointer","type":"error"},{"inputs":[{"internalType":"address","name":"receivedSigner","type":"address"}],"name":"SignerNotAuthorised","type":"error"},{"inputs":[{"internalType":"uint256","name":"receivedTimestampSeconds","type":"uint256"},{"internalType":"uint256","name":"blockTimestamp","type":"uint256"}],"name":"TimestampFromTooLongFuture","type":"error"},{"inputs":[{"internalType":"uint256","name":"receivedTimestampSeconds","type":"uint256"},{"internalType":"uint256","name":"blockTimestamp","type":"uint256"}],"name":"TimestampIsTooOld","type":"error"},{"inputs":[{"internalType":"uint256[]","name":"values","type":"uint256[]"}],"name":"aggregateValues","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"signerAddress","type":"address"}],"name":"getAuthorisedSignerIndex","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getUniqueSignersThreshold","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"receivedTimestampMilliseconds","type":"uint256"}],"name":"validateTimestamp","outputs":[],"stateMutability":"view","type":"function"}]
Contract Creation Code
608060405234801561001057600080fd5b50610914806100206000396000f3fe608060405234801561001057600080fd5b506004361061004c5760003560e01c80633ce142f514610051578063b24ebfcc14610081578063f50b2efe146100b1578063f90c4924146100cd575b600080fd5b61006b600480360381019061006691906104a1565b6100eb565b60405161007891906104ea565b60405180910390f35b61009b60048036038101906100969190610694565b6101cf565b6040516100a891906106ec565b60405180910390f35b6100cb60048036038101906100c69190610707565b6101e1565b005b6100d56101ed565b6040516100e291906104ea565b60405180910390f35b60007311fffc9970c41b9bfb9aa35be838d39bce918cff73ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff160361013d57600090506101ca565b73dbcc2c6c892c8d3e3fe4d325fec810b7376a5ed673ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff160361018d57600190506101ca565b816040517fec459bc00000000000000000000000000000000000000000000000000000000081526004016101c19190610743565b60405180910390fd5b919050565b60006101da826101f6565b9050919050565b6101ea81610208565b50565b60006002905090565b6000610201826102d2565b9050919050565b60006103e88261021891906107bc565b90508042101561027a57603c428261023091906107ed565b11156102755780426040517fb6b0916d00000000000000000000000000000000000000000000000000000000815260040161026c929190610821565b60405180910390fd5b6102ce565b60b4814261028891906107ed565b11156102cd5780426040517f0321d0b50000000000000000000000000000000000000000000000000000000081526004016102c4929190610821565b60405180910390fd5b5b5050565b60008082510361030e576040517f9e198af900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610317826103c6565b60006002835161032791906107bc565b9050600060028451610339919061084a565b036103a257600061038a8460018461035191906107ed565b815181106103625761036161087b565b5b602002602001015185848151811061037d5761037c61087b565b5b6020026020010151610419565b905060028161039991906107bc565b925050506103c1565b8281815181106103b5576103b461087b565b5b60200260200101519150505b919050565b805160208201602082028101815b8181101561041257825b818110156104065781518151808210156103f9578084528183525b50506020810190506103de565b506020810190506103d4565b5050505050565b6000818361042791906108aa565b905092915050565b6000604051905090565b600080fd5b600080fd5b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b600061046e82610443565b9050919050565b61047e81610463565b811461048957600080fd5b50565b60008135905061049b81610475565b92915050565b6000602082840312156104b7576104b6610439565b5b60006104c58482850161048c565b91505092915050565b600060ff82169050919050565b6104e4816104ce565b82525050565b60006020820190506104ff60008301846104db565b92915050565b600080fd5b6000601f19601f8301169050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6105538261050a565b810181811067ffffffffffffffff821117156105725761057161051b565b5b80604052505050565b600061058561042f565b9050610591828261054a565b919050565b600067ffffffffffffffff8211156105b1576105b061051b565b5b602082029050602081019050919050565b600080fd5b6000819050919050565b6105da816105c7565b81146105e557600080fd5b50565b6000813590506105f7816105d1565b92915050565b600061061061060b84610596565b61057b565b90508083825260208201905060208402830185811115610633576106326105c2565b5b835b8181101561065c578061064888826105e8565b845260208401935050602081019050610635565b5050509392505050565b600082601f83011261067b5761067a610505565b5b813561068b8482602086016105fd565b91505092915050565b6000602082840312156106aa576106a9610439565b5b600082013567ffffffffffffffff8111156106c8576106c761043e565b5b6106d484828501610666565b91505092915050565b6106e6816105c7565b82525050565b600060208201905061070160008301846106dd565b92915050565b60006020828403121561071d5761071c610439565b5b600061072b848285016105e8565b91505092915050565b61073d81610463565b82525050565b60006020820190506107586000830184610734565b92915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60006107c7826105c7565b91506107d2836105c7565b9250826107e2576107e161075e565b5b828204905092915050565b60006107f8826105c7565b9150610803836105c7565b925082820390508181111561081b5761081a61078d565b5b92915050565b600060408201905061083660008301856106dd565b61084360208301846106dd565b9392505050565b6000610855826105c7565b9150610860836105c7565b9250826108705761086f61075e565b5b828206905092915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b60006108b5826105c7565b91506108c0836105c7565b92508282019050808211156108d8576108d761078d565b5b9291505056fea2646970667358221220abfe6073cdf86426d575aefa3b42f9ace7976f08c32603f37884852fc0c5b7bd64736f6c63430008110033
Deployed ByteCode Sourcemap
37008:572:0:-:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;37185:392;;;;;;;;;;;;;:::i;:::-;;:::i;:::-;;;;;;;:::i;:::-;;;;;;;;21600:151;;;;;;;;;;;;;:::i;:::-;;:::i;:::-;;;;;;;:::i;:::-;;;;;;;;20806:167;;;;;;;;;;;;;:::i;:::-;;:::i;:::-;;37076:103;;;:::i;:::-;;;;;;;:::i;:::-;;;;;;;;37185:392;37305:5;37343:42;37326:59;;:13;:59;;;37322:250;;37403:1;37396:8;;;;37322:250;37439:42;37422:59;;:13;:59;;;37418:154;;37499:1;37492:8;;;;37418:154;37550:13;37530:34;;;;;;;;;;;:::i;:::-;;;;;;;;37185:392;;;;:::o;21600:151::-;21679:7;21702:43;21738:6;21702:35;:43::i;:::-;21695:50;;21600:151;;;:::o;20806:167::-;20899:68;20937:29;20899:37;:68::i;:::-;20806:167;:::o;37076:103::-;37151:5;37172:1;37165:8;;37076:103;:::o;19258:136::-;19331:7;19354:34;19381:6;19354:26;:34::i;:::-;19347:41;;19258:136;;;:::o;18277:975::-;18730:32;18797:4;18765:29;:36;;;;:::i;:::-;18730:71;;18832:24;18814:15;:42;18810:437;;;18078:9;18899:15;18872:24;:42;;;;:::i;:::-;18871:87;18867:190;;;19005:24;19031:15;18978:69;;;;;;;;;;;;:::i;:::-;;;;;;;;18867:190;18810:437;;;18004:9;19093:24;19075:15;:42;;;;:::i;:::-;19074:87;19070:177;;;19197:24;19223:15;19179:60;;;;;;;;;;;;:::i;:::-;;;;;;;;19070:177;18810:437;18357:895;18277:975;:::o;16379:403::-;16444:7;16478:1;16464:3;:10;:15;16460:75;;16497:30;;;;;;;;;;;;;;16460:75;16541:9;16546:3;16541:4;:9::i;:::-;16557:19;16592:1;16579:3;:10;:14;;;;:::i;:::-;16557:36;;16622:1;16617;16604:3;:10;:14;;;;:::i;:::-;:19;16600:177;;16634:11;16648:52;16661:3;16679:1;16665:11;:15;;;;:::i;:::-;16661:20;;;;;;;;:::i;:::-;;;;;;;;16683:3;16687:11;16683:16;;;;;;;;:::i;:::-;;;;;;;;16648:12;:52::i;:::-;16634:66;;16722:1;16716:3;:7;;;;:::i;:::-;16709:14;;;;;;16600:177;16753:3;16757:11;16753:16;;;;;;;;:::i;:::-;;;;;;;;16746:23;;;16379:403;;;;:::o;16788:774::-;16886:3;16880:10;16924:20;16919:3;16915:30;16997:16;16986:9;16982:32;16971:9;16967:48;17053:9;17023:527;17084:6;17075:7;17072:19;17023:527;;;17214:9;17182:359;17247:7;17238;17235:20;17182:359;;;17370:7;17364:14;17408:7;17402:14;17440:4;17434;17431:14;17428:102;;;17477:4;17468:7;17461:21;17512:4;17503:7;17496:21;17428:102;17339:202;;17293:16;17284:7;17280:30;17269:41;;17182:359;;;17186:48;17127:16;17118:7;17114:30;17103:41;;17023:527;;;17027:44;16854:703;;;16788:774;:::o;7395:98::-;7453:7;7484:1;7480;:5;;;;:::i;:::-;7473:12;;7395:98;;;;:::o;7:75:1:-;40:6;73:2;67:9;57:19;;7:75;:::o;88:117::-;197:1;194;187:12;211:117;320:1;317;310:12;334:126;371:7;411:42;404:5;400:54;389:65;;334:126;;;:::o;466:96::-;503:7;532:24;550:5;532:24;:::i;:::-;521:35;;466:96;;;:::o;568:122::-;641:24;659:5;641:24;:::i;:::-;634:5;631:35;621:63;;680:1;677;670:12;621:63;568:122;:::o;696:139::-;742:5;780:6;767:20;758:29;;796:33;823:5;796:33;:::i;:::-;696:139;;;;:::o;841:329::-;900:6;949:2;937:9;928:7;924:23;920:32;917:119;;;955:79;;:::i;:::-;917:119;1075:1;1100:53;1145:7;1136:6;1125:9;1121:22;1100:53;:::i;:::-;1090:63;;1046:117;841:329;;;;:::o;1176:86::-;1211:7;1251:4;1244:5;1240:16;1229:27;;1176:86;;;:::o;1268:112::-;1351:22;1367:5;1351:22;:::i;:::-;1346:3;1339:35;1268:112;;:::o;1386:214::-;1475:4;1513:2;1502:9;1498:18;1490:26;;1526:67;1590:1;1579:9;1575:17;1566:6;1526:67;:::i;:::-;1386:214;;;;:::o;1606:117::-;1715:1;1712;1705:12;1729:102;1770:6;1821:2;1817:7;1812:2;1805:5;1801:14;1797:28;1787:38;;1729:102;;;:::o;1837:180::-;1885:77;1882:1;1875:88;1982:4;1979:1;1972:15;2006:4;2003:1;1996:15;2023:281;2106:27;2128:4;2106:27;:::i;:::-;2098:6;2094:40;2236:6;2224:10;2221:22;2200:18;2188:10;2185:34;2182:62;2179:88;;;2247:18;;:::i;:::-;2179:88;2287:10;2283:2;2276:22;2066:238;2023:281;;:::o;2310:129::-;2344:6;2371:20;;:::i;:::-;2361:30;;2400:33;2428:4;2420:6;2400:33;:::i;:::-;2310:129;;;:::o;2445:311::-;2522:4;2612:18;2604:6;2601:30;2598:56;;;2634:18;;:::i;:::-;2598:56;2684:4;2676:6;2672:17;2664:25;;2744:4;2738;2734:15;2726:23;;2445:311;;;:::o;2762:117::-;2871:1;2868;2861:12;2885:77;2922:7;2951:5;2940:16;;2885:77;;;:::o;2968:122::-;3041:24;3059:5;3041:24;:::i;:::-;3034:5;3031:35;3021:63;;3080:1;3077;3070:12;3021:63;2968:122;:::o;3096:139::-;3142:5;3180:6;3167:20;3158:29;;3196:33;3223:5;3196:33;:::i;:::-;3096:139;;;;:::o;3258:710::-;3354:5;3379:81;3395:64;3452:6;3395:64;:::i;:::-;3379:81;:::i;:::-;3370:90;;3480:5;3509:6;3502:5;3495:21;3543:4;3536:5;3532:16;3525:23;;3596:4;3588:6;3584:17;3576:6;3572:30;3625:3;3617:6;3614:15;3611:122;;;3644:79;;:::i;:::-;3611:122;3759:6;3742:220;3776:6;3771:3;3768:15;3742:220;;;3851:3;3880:37;3913:3;3901:10;3880:37;:::i;:::-;3875:3;3868:50;3947:4;3942:3;3938:14;3931:21;;3818:144;3802:4;3797:3;3793:14;3786:21;;3742:220;;;3746:21;3360:608;;3258:710;;;;;:::o;3991:370::-;4062:5;4111:3;4104:4;4096:6;4092:17;4088:27;4078:122;;4119:79;;:::i;:::-;4078:122;4236:6;4223:20;4261:94;4351:3;4343:6;4336:4;4328:6;4324:17;4261:94;:::i;:::-;4252:103;;4068:293;3991:370;;;;:::o;4367:539::-;4451:6;4500:2;4488:9;4479:7;4475:23;4471:32;4468:119;;;4506:79;;:::i;:::-;4468:119;4654:1;4643:9;4639:17;4626:31;4684:18;4676:6;4673:30;4670:117;;;4706:79;;:::i;:::-;4670:117;4811:78;4881:7;4872:6;4861:9;4857:22;4811:78;:::i;:::-;4801:88;;4597:302;4367:539;;;;:::o;4912:118::-;4999:24;5017:5;4999:24;:::i;:::-;4994:3;4987:37;4912:118;;:::o;5036:222::-;5129:4;5167:2;5156:9;5152:18;5144:26;;5180:71;5248:1;5237:9;5233:17;5224:6;5180:71;:::i;:::-;5036:222;;;;:::o;5264:329::-;5323:6;5372:2;5360:9;5351:7;5347:23;5343:32;5340:119;;;5378:79;;:::i;:::-;5340:119;5498:1;5523:53;5568:7;5559:6;5548:9;5544:22;5523:53;:::i;:::-;5513:63;;5469:117;5264:329;;;;:::o;5599:118::-;5686:24;5704:5;5686:24;:::i;:::-;5681:3;5674:37;5599:118;;:::o;5723:222::-;5816:4;5854:2;5843:9;5839:18;5831:26;;5867:71;5935:1;5924:9;5920:17;5911:6;5867:71;:::i;:::-;5723:222;;;;:::o;5951:180::-;5999:77;5996:1;5989:88;6096:4;6093:1;6086:15;6120:4;6117:1;6110:15;6137:180;6185:77;6182:1;6175:88;6282:4;6279:1;6272:15;6306:4;6303:1;6296:15;6323:185;6363:1;6380:20;6398:1;6380:20;:::i;:::-;6375:25;;6414:20;6432:1;6414:20;:::i;:::-;6409:25;;6453:1;6443:35;;6458:18;;:::i;:::-;6443:35;6500:1;6497;6493:9;6488:14;;6323:185;;;;:::o;6514:194::-;6554:4;6574:20;6592:1;6574:20;:::i;:::-;6569:25;;6608:20;6626:1;6608:20;:::i;:::-;6603:25;;6652:1;6649;6645:9;6637:17;;6676:1;6670:4;6667:11;6664:37;;;6681:18;;:::i;:::-;6664:37;6514:194;;;;:::o;6714:332::-;6835:4;6873:2;6862:9;6858:18;6850:26;;6886:71;6954:1;6943:9;6939:17;6930:6;6886:71;:::i;:::-;6967:72;7035:2;7024:9;7020:18;7011:6;6967:72;:::i;:::-;6714:332;;;;;:::o;7052:176::-;7084:1;7101:20;7119:1;7101:20;:::i;:::-;7096:25;;7135:20;7153:1;7135:20;:::i;:::-;7130:25;;7174:1;7164:35;;7179:18;;:::i;:::-;7164:35;7220:1;7217;7213:9;7208:14;;7052:176;;;;:::o;7234:180::-;7282:77;7279:1;7272:88;7379:4;7376:1;7369:15;7403:4;7400:1;7393:15;7420:191;7460:3;7479:20;7497:1;7479:20;:::i;:::-;7474:25;;7513:20;7531:1;7513:20;:::i;:::-;7508:25;;7556:1;7553;7549:9;7542:16;;7577:3;7574:1;7571:10;7568:36;;;7584:18;;:::i;:::-;7568:36;7420:191;;;;:::o
Swarm Source
ipfs://abfe6073cdf86426d575aefa3b42f9ace7976f08c32603f37884852fc0c5b7bd
Age | Block | Fee Address | BC Fee Address | Voting Power | Jailed | Incoming |
---|
Make sure to use the "Vote Down" button for any spammy posts, and the "Vote Up" for interesting conversations.