S Price: $0.418205 (-2.07%)

Contract

0xe4bfE4071D363E1d4274fB8F79Cb510B75913020

Overview

S Balance

Sonic LogoSonic LogoSonic Logo0 S

S Value

$0.00

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To

There are no matching entries

Please try again later

Parent Transaction Hash Block From To
View All Internal Transactions
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
SonicMultiFeedAdapterWithoutRoundsV1

Compiler Version
v0.8.17+commit.8df45f5f

Optimization Enabled:
Yes with 10000 runs

Other Settings:
default evmVersion
File 1 of 15 : SonicMultiFeedAdapterWithoutRoundsV1.sol
// SPDX-License-Identifier: BUSL-1.1

pragma solidity ^0.8.17;

import {OldGelatoAddress, GelatoAddress} from "../__addresses/Addresses.sol";
import {MultiFeedAdapterWithoutRoundsPrimaryProd} from "@redstone-finance/on-chain-relayer/contracts/price-feeds/data-services/MultiFeedAdapterWithoutRoundsPrimaryProd.sol";

contract SonicMultiFeedAdapterWithoutRoundsV1 is MultiFeedAdapterWithoutRoundsPrimaryProd {

  address internal constant MAIN_UPDATER_ADDRESS = 0xF0547b3E44b904FeE2569ACf5107769dD28a17C3;
  address internal constant FALLBACK_UPDATER_ADDRESS = 0x3f11Cb12D6E69f66124ea6ca45D3C5ffDc4b1704;
  address internal constant MANUAL_UPDATER_ADDRESS = 0x4622DEcEfa557d7cFFd9aa00B93c5F9a7bbCe15f;

 function _validateBlockTimestamp(uint256 lastBlockTimestamp) internal view virtual override returns (bool) {  
    if (
      msg.sender == MAIN_UPDATER_ADDRESS ||
      msg.sender == FALLBACK_UPDATER_ADDRESS ||
      msg.sender == MANUAL_UPDATER_ADDRESS ||
      msg.sender == GelatoAddress.ADDR ||
      msg.sender == OldGelatoAddress.ADDR
    ) {  
      // For whitelisted addresses we only require a newer block  
      return block.timestamp > lastBlockTimestamp;  
    } else {  
      // For non-whitelisted addresses we require some time to pass after the latest update  
      return block.timestamp > lastBlockTimestamp + 40 seconds;  
    }  
  }
}

File 2 of 15 : CalldataExtractor.sol
// SPDX-License-Identifier: BUSL-1.1

pragma solidity ^0.8.17;

import "./RedstoneConstants.sol";

/**
 * @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 {

  error DataPackageTimestampMustNotBeZero();
  error DataPackageTimestampsMustBeEqual();
  error RedstonePayloadMustHaveAtLeastOneDataPackage();
  error TooLargeValueByteSize(uint256 valueByteSize);

  function extractTimestampsAndAssertAllAreEqual() public pure returns (uint256 extractedTimestamp) {
    uint256 calldataNegativeOffset = _extractByteSizeOfUnsignedMetadata();
    uint256 dataPackagesCount;
    (dataPackagesCount, calldataNegativeOffset) = _extractDataPackagesCountFromCalldata(calldataNegativeOffset);

    if (dataPackagesCount == 0) {
      revert RedstonePayloadMustHaveAtLeastOneDataPackage();
    }

    for (uint256 dataPackageIndex = 0; dataPackageIndex < dataPackagesCount; dataPackageIndex++) {
      uint256 dataPackageByteSize = _getDataPackageByteSize(calldataNegativeOffset);

      // Extracting timestamp for the current data package
      uint48 dataPackageTimestamp; // uint48, because timestamp uses 6 bytes
      uint256 timestampNegativeOffset = (calldataNegativeOffset + TIMESTAMP_NEGATIVE_OFFSET_IN_DATA_PACKAGE_WITH_STANDARD_SLOT_BS);
      uint256 timestampOffset = msg.data.length - timestampNegativeOffset;
      assembly {
        dataPackageTimestamp := calldataload(timestampOffset)
      }

      if (dataPackageTimestamp == 0) {
        revert DataPackageTimestampMustNotBeZero();
      }

      if (extractedTimestamp == 0) {
        extractedTimestamp = dataPackageTimestamp;
      } else if (dataPackageTimestamp != extractedTimestamp) {
        revert DataPackageTimestampsMustBeEqual();
      }

      calldataNegativeOffset += dataPackageByteSize;
    }
  }

  function _getDataPackageByteSize(uint256 calldataNegativeOffset) internal pure returns (uint256) {
    (
      uint256 dataPointsCount,
      uint256 eachDataPointValueByteSize
    ) = _extractDataPointsDetailsForDataPackage(calldataNegativeOffset);

    return
      dataPointsCount *
      (DATA_POINT_SYMBOL_BS + eachDataPointValueByteSize) +
      DATA_PACKAGE_WITHOUT_DATA_POINTS_BS;
  }

  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 nextCalldataNegativeOffset)
  {
    uint256 calldataNegativeOffsetWithStandardSlot = calldataNegativeOffset + STANDARD_SLOT_BS;
    if (calldataNegativeOffsetWithStandardSlot > msg.data.length) {
      revert CalldataOverOrUnderFlow();
    }
    assembly {
      dataPackagesCount := calldataload(
        sub(calldatasize(), calldataNegativeOffsetWithStandardSlot)
      )
    }
    return (dataPackagesCount, calldataNegativeOffset + DATA_PACKAGES_COUNT_BS);
  }

  function _extractDataPointValueAndDataFeedId(
    uint256 dataPointNegativeOffset,
    uint256 dataPointValueByteSize
  ) internal pure virtual returns (bytes32 dataPointDataFeedId, uint256 dataPointValue) {
    uint256 dataPointCalldataOffset = msg.data.length - dataPointNegativeOffset;
    assembly {
      dataPointDataFeedId := calldataload(dataPointCalldataOffset)
      dataPointValue := calldataload(add(dataPointCalldataOffset, DATA_POINT_SYMBOL_BS))
    }
    if (dataPointValueByteSize >= 33) {
      revert TooLargeValueByteSize(dataPointValueByteSize);
    }
    unchecked {
      dataPointValue = dataPointValue >> (32 - dataPointValueByteSize) * 8; 
    }
  }

  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 calldataOffset = msg.data.length - (calldataNegativeOffsetForDataPackage + SIG_BS + STANDARD_SLOT_BS);
    assembly {
      dataPointsCount_ := calldataload(calldataOffset)
    }

    // Extract each data point value size
    calldataOffset = calldataOffset - DATA_POINTS_COUNT_BS;
    assembly {
      eachDataPointValueByteSize_ := calldataload(calldataOffset)
    }

    // Prepare returned values
    dataPointsCount = dataPointsCount_;
    eachDataPointValueByteSize = eachDataPointValueByteSize_;
  }
}

File 3 of 15 : RedstoneConstants.sol
// SPDX-License-Identifier: BUSL-1.1

pragma solidity ^0.8.17;

/**
 * @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 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);
  error DataTimestampCannotBeZero();
  error TimestampsMustBeEqual();
}

File 4 of 15 : RedstoneConsumerBase.sol
// SPDX-License-Identifier: BUSL-1.1

pragma solidity ^0.8.17;

import "./RedstoneConstants.sol";
import "./RedstoneDefaultsLib.sol";
import "./CalldataExtractor.sol";
import "../libs/BitmapLib.sol";
import "../libs/SignatureLib.sol";

/**
 * @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 {

  error GetDataServiceIdNotImplemented();

  /* ========== VIRTUAL FUNCTIONS (MAY BE OVERRIDDEN IN CHILD CONTRACTS) ========== */

  /**
   * @dev This function must be implemented by the child consumer contract.
   * It should return dataServiceId which DataServiceWrapper will use if not provided explicitly .
   * If not overridden, value will always have to be provided explicitly in DataServiceWrapper.
   * @return dataServiceId being consumed by contract
   */
  function getDataServiceId() public view virtual returns (string memory) {
    revert GetDataServiceIdNotImplemented();
  }

  /**
   * @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 must be implemented by the child consumer contract.
   * @return The minimum required value of unique authorised signers
   */
  function getUniqueSignersThreshold() public view virtual returns (uint8);

  /**
   * @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. packages with different 
   * timestamps 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
   * @return dataPackagesTimestamp timestamp equal for all data packages
   */
  function _securelyExtractOracleValuesAndTimestampFromTxMsg(bytes32[] memory dataFeedIds)
    internal
    view
    returns (uint256[] memory, uint256 dataPackagesTimestamp)
  {
    // 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;) {
      // 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());
      unchecked {
        i++;
      }
    }

    // Extracting the number of data packages from calldata
    uint256 calldataNegativeOffset = _extractByteSizeOfUnsignedMetadata();
    uint256 dataPackagesCount;
    (dataPackagesCount, calldataNegativeOffset) = _extractDataPackagesCountFromCalldata(calldataNegativeOffset);

    // 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;) {
      // Extract data package details and update calldata offset
      uint256 dataPackageTimestamp;
      (calldataNegativeOffset, dataPackageTimestamp) = _extractDataPackage(
        dataFeedIds,
        uniqueSignerCountForDataFeedIds,
        signersBitmapForDataFeedIds,
        valuesForDataFeeds,
        calldataNegativeOffset
      );

      if (dataPackageTimestamp == 0) {
        revert DataTimestampCannotBeZero();
      }

      if (dataPackageTimestamp != dataPackagesTimestamp) {
        if (dataPackagesTimestamp == 0) {
          // Setting dataPackagesTimestamp first time
          dataPackagesTimestamp = dataPackageTimestamp;    
        } else {
          revert TimestampsMustBeEqual();
        }
      }

      // Resetting the memory pointer to the initial "safe" value
      // We add STANDARD_SLOT_BS (32 bytes) to account for potential allocation
      // of the dataPackageIndex variable, which may or may not be stored in memory
      assembly {
        mstore(FREE_MEMORY_PTR, add(freeMemPtr, STANDARD_SLOT_BS))
      }
      unchecked {
        dataPackageIndex++;
      }
    }

    // Validating numbers of unique signers and calculating aggregated values for each dataFeedId
    return (_getAggregatedValues(valuesForDataFeeds, uniqueSignerCountForDataFeedIds), dataPackagesTimestamp);
  }

  /**
   * @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 nextCalldataNegativeOffset negative calldata offset for the next data package
   * @return dataPackageTimestamp data package timestamp
   */
  function _extractDataPackage(
    bytes32[] memory dataFeedIds,
    uint256[] memory uniqueSignerCountForDataFeedIds,
    uint256[] memory signersBitmapForDataFeedIds,
    uint256[][] memory valuesForDataFeeds,
    uint256 calldataNegativeOffset
  ) private view returns (uint256 nextCalldataNegativeOffset, uint256 dataPackageTimestamp) {
    uint256 signerIndex;

    (
      uint256 dataPointsCount,
      uint256 eachDataPointValueByteSize
    ) = _extractDataPointsDetailsForDataPackage(calldataNegativeOffset);

    // We use scopes to resolve problem with too deep stack
    {
      address signerAddress;
      bytes32 signedHash;
      bytes memory signedMessage;
      uint256 signedMessageBytesCount;
      uint48 extractedTimestamp;

      signedMessageBytesCount = dataPointsCount * (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 - 
        (calldataNegativeOffset + TIMESTAMP_NEGATIVE_OFFSET_IN_DATA_PACKAGE_WITH_STANDARD_SLOT_BS);

      uint256 signedMessageCalldataOffset = msg.data.length - 
        (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)
        }
      }

      dataPackageTimestamp = extractedTimestamp;

      // Verifying the off-chain signature against on-chain hashed data
      signerAddress = SignatureLib.recoverSignerAddress(
        signedHash,
        calldataNegativeOffset + SIG_BS
      );
      signerIndex = getAuthorisedSignerIndex(signerAddress);
    }

    // Updating helpful arrays
    {
      calldataNegativeOffset = calldataNegativeOffset + DATA_PACKAGE_WITHOUT_DATA_POINTS_BS;
      bytes32 dataPointDataFeedId;
      uint256 dataPointValue;
      for (uint256 dataPointIndex = 0; dataPointIndex < dataPointsCount;) {
        calldataNegativeOffset = calldataNegativeOffset + eachDataPointValueByteSize + DATA_POINT_SYMBOL_BS;
        // Extracting data feed id and value for the current data point
        (dataPointDataFeedId, dataPointValue) = _extractDataPointValueAndDataFeedId(
          calldataNegativeOffset,
          eachDataPointValueByteSize
        );

        for (
          uint256 dataFeedIdIndex = 0;
          dataFeedIdIndex < dataFeedIds.length;
        ) {
          if (dataPointDataFeedId == dataFeedIds[dataFeedIdIndex]) {
            uint256 bitmapSignersForDataFeedId = signersBitmapForDataFeedIds[dataFeedIdIndex];

            if (
              !BitmapLib.getBitFromBitmap(bitmapSignersForDataFeedId, signerIndex) && /* current signer was not counted for current dataFeedId */
              uniqueSignerCountForDataFeedIds[dataFeedIdIndex] < getUniqueSignersThreshold()
            ) {
              // Add new value
              valuesForDataFeeds[dataFeedIdIndex][uniqueSignerCountForDataFeedIds[dataFeedIdIndex]] = dataPointValue;

              // Increase unique signer counter
              uniqueSignerCountForDataFeedIds[dataFeedIdIndex]++;

              // Update signers bitmap
              signersBitmapForDataFeedIds[dataFeedIdIndex] = BitmapLib.setBitInBitmap(
                bitmapSignersForDataFeedId,
                signerIndex
              );
            }

            // Breaking, as there couldn't be several indexes for the same feed ID
            break;
          }
          unchecked {
            dataFeedIdIndex++;
          }
        }
        unchecked {
           dataPointIndex++;
        }
      }
    }

    return (calldataNegativeOffset, dataPackageTimestamp);
  }

  /**
   * @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 5 of 15 : RedstoneConsumerNumericBase.sol
// SPDX-License-Identifier: BUSL-1.1

pragma solidity ^0.8.17;

import "./RedstoneConsumerBase.sol";

/**
 * @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)
  {
    (uint256[] memory values, uint256 timestamp) = _securelyExtractOracleValuesAndTimestampFromTxMsg(dataFeedIds);
    validateTimestamp(timestamp);
    return values;
  }

  /**
   * @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 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 returns the timestamp of the packages (it requires it to be 
   * the same for all), but does not validate this timestamp.
   * 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 and data packages timestamp
   */
   function getOracleNumericValuesAndTimestampFromTxMsg(bytes32[] memory dataFeedIds)
    internal
    view
    virtual
    returns (uint256[] memory, uint256)
  {
    return _securelyExtractOracleValuesAndTimestampFromTxMsg(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, uint256 timestamp) = _securelyExtractOracleValuesAndTimestampFromTxMsg(dataFeedIdsWithoutDuplicates);
    validateTimestamp(timestamp);

    // 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 6 of 15 : RedstoneDefaultsLib.sol
// SPDX-License-Identifier: BUSL-1.1

pragma solidity ^0.8.17;

import "../libs/NumericArrayLib.sol";

/**
 * @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 7 of 15 : BitmapLib.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.17;

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 8 of 15 : NumericArrayLib.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.17;

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 == 2) {
      return (arr[0] + arr[1]) / 2;
    }
    if (arr.length == 0) {
      revert CanNotPickMedianOfEmptyArray();
    }
    sort(arr);
    uint256 middleIndex = arr.length / 2;
    if (arr.length % 2 == 0) {
      uint256 sum = 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 9 of 15 : SignatureLib.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.17;

library SignatureLib {
  uint256 constant ECDSA_SIG_R_BS = 32;
  uint256 constant ECDSA_SIG_S_BS = 32;

  error InvalidSignature(bytes32 signedHash);

  function recoverSignerAddress(bytes32 signedHash, uint256 signatureCalldataNegativeOffset)
    internal
    pure
    returns (address signerAddress)
  {
    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
    }
    signerAddress = ecrecover(signedHash, v, r, s);
    if (signerAddress == address(0)) {
      revert InvalidSignature(signedHash);
    }
  }
}

File 10 of 15 : ILToken.sol
// SPDX-License-Identifier: BUSL-1.1

pragma solidity ^0.8.17;

interface ILToken {
  function underlying() external view returns (address);
}

File 11 of 15 : IPriceCalculator.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.17;

interface IPriceCalculator {
  struct ReferenceData {
    uint256 lastData;
    uint256 lastUpdated;
  }

  function priceOf(address asset) external view returns (uint256);

  function pricesOf(
    address[] memory assets
  ) external view returns (uint256[] memory);

  function priceOfETH() external view returns (uint256);

  function getUnderlyingPrice(address gToken) external view returns (uint256);

  function getUnderlyingPrices(
    address[] memory gTokens
  ) external view returns (uint256[] memory);
}

File 12 of 15 : MultiFeedAdapterWithoutRoundsPrimaryProd.sol
// SPDX-License-Identifier: BUSL-1.1

pragma solidity ^0.8.17;

import {MultiFeedAdapterWithoutRounds} from "../without-rounds/MultiFeedAdapterWithoutRounds.sol";

abstract contract MultiFeedAdapterWithoutRoundsPrimaryProd is MultiFeedAdapterWithoutRounds {
  function getUniqueSignersThreshold() public view virtual override returns (uint8) {
    return 3;
  }

  function getAuthorisedSignerIndex(
    address signerAddress
  ) public view virtual override returns (uint8) {
    if (signerAddress == 0x8BB8F32Df04c8b654987DAaeD53D6B6091e3B774) { return 0; }
    else if (signerAddress == 0xdEB22f54738d54976C4c0fe5ce6d408E40d88499) { return 1; }
    else if (signerAddress == 0x51Ce04Be4b3E32572C4Ec9135221d0691Ba7d202) { return 2; }
    else if (signerAddress == 0xDD682daEC5A90dD295d14DA4b0bec9281017b5bE) { return 3; }
    else if (signerAddress == 0x9c5AE89C4Af6aA32cE58588DBaF90d18a855B6de) { return 4; }
    else {
      revert SignerNotAuthorised(signerAddress);
    }
  }
}

File 13 of 15 : IMultiFeedAdapter.sol
// SPDX-License-Identifier: BUSL-1.1

pragma solidity ^0.8.17;

interface IMultiFeedAdapter {
  function updateDataFeedsValuesPartial(bytes32[] memory dataFeedsIds) external;

  function getLastUpdateDetails(bytes32 dataFeedId) external view returns (uint256 lastDataTimestamp, uint256 lastBlockTimestamp, uint256 lastValue);

  function getLastUpdateDetailsUnsafe(bytes32 dataFeedId) external view returns (uint256 lastDataTimestamp, uint256 lastBlockTimestamp, uint256 lastValue);

  function getValuesForDataFeeds(bytes32[] memory requestedDataFeedIds) external view returns (uint256[] memory values);

  function getValueForDataFeed(bytes32 dataFeedId) external view returns (uint256 dataFeedValue);

  function getDataTimestampFromLatestUpdate(bytes32 dataFeedId) external view returns (uint256 lastDataTimestamp);

  function getBlockTimestampFromLatestUpdate(bytes32 dataFeedId) external view returns (uint256 blockTimestamp);
}

File 14 of 15 : MultiFeedAdapterWithoutRounds.sol
// SPDX-License-Identifier: BUSL-1.1

pragma solidity ^0.8.17;

import {RedstoneConsumerNumericBase, RedstoneDefaultsLib} from "@redstone-finance/evm-connector/contracts/core/RedstoneConsumerNumericBase.sol";
import {IMultiFeedAdapter} from "../interfaces/IMultiFeedAdapter.sol";
import {IPriceCalculator} from "../../custom-integrations/layerbank/IPriceCalculator.sol";
import {ILToken} from "../../custom-integrations/layerbank/ILToken.sol";

/**
 * @title MultiFeedAdapterWithoutRounds
 * @author The Redstone Oracles team
 * @dev This abstract contract serves as an adapter for multiple data feeds, facilitating
 * the updating and retrieval of oracle data values independently.
 *
 * Key details about the contract:
 * - Values for data feeds can be updated using the `updateDataFeedsValuesPartial` function
 * - Unlike the previous version (RedstoneAdapterBase), this adapter allows updating any set of data feeds,
 *   with each update being made independently.
 * - Updates are highly independent. Each data feed update is attempted separately, ensuring maximum possible
 *   updates without reverting the entire transaction if some of them fail. Both successful value updates and
 *   update skips due to failed validation are represented in corresponding events.
 * - Efficient storage usage: Related timestamps and values are packed into a single 32-byte slot when possible.
 *   If a value exceeds the slot capacity, it is stored in the next slot, with one bool prop (isValueBigger) indicating the storage method used.
 * - All data packages in the Redstone payload must have the same timestamp. Invalid timestamps (too old or too new) will cause transaction reversion.
 * - The contract includes a built-in IPriceCalculator interface used by LayerBank and other projects
 */
abstract contract MultiFeedAdapterWithoutRounds is RedstoneConsumerNumericBase, IMultiFeedAdapter, IPriceCalculator {
  bytes32 internal constant DATA_FEEDS_STORAGE_LOCATION = 0x5e9fb4cb0eb3c2583734d3394f30bb14b241acb9b3a034f7e7ba1a62db4370f1; // keccak256("RedStone.MultiFeedAdapterWithoutRounds.dataFeeds");
  bytes32 internal constant ETH_DATA_FEED_ID = bytes32("ETH");
  uint256 internal constant MAX_DATA_STALENESS = 30 hours;
  uint256 internal constant DEFAULT_DECIMAL_SCALER_LAYERBANK = 1e10;

  error DataTimestampTooLarge(uint256 dataTimestamp);
  error BlockTimestampTooLarge(uint256 blockTimestamp);
  error InvalidLastUpdateDetails(bytes32 dataFeedId, uint256 lastDataTimestamp, uint256 lastBlockTimestamp, uint256 lastValue);

  event ValueUpdate(uint256 value, bytes32 dataFeedId, uint256 updatedAt);
  event UpdateSkipDueToBlockTimestamp(bytes32 dataFeedId);
  event UpdateSkipDueToDataTimestamp(bytes32 dataFeedId);
  event UpdateSkipDueToInvalidValue(bytes32 dataFeedId);

  // This struct uses exactly one storage slot (32 bytes)
  struct DataFeedDetails {
    uint48 dataTimestamp;
    uint48 blockTimestamp;
    uint152 value;
    bool isValueBigger;
  }

  struct DataFeedDetailsWithOptionalBigValue {
    DataFeedDetails details;
    uint256 biggerValue;
  }

  // This struct is used only for returning values
  struct LastUpdateDetails {
    uint256 dataTimestamp;
    uint256 blockTimestamp;
    uint256 value;
  }

  struct DataFeedsStorage {
    mapping(bytes32 => DataFeedDetailsWithOptionalBigValue) _dataFeeds;
  }

  /// This function allows to update any set of data feeds
  function updateDataFeedsValuesPartial(bytes32[] memory dataFeedsIds) public {
    (uint256[] memory oracleValues, uint256 extractedDataTimestamp) = getOracleNumericValuesAndTimestampFromTxMsg(dataFeedsIds);

    // Revert if data timestamp doesn't fit within the allowed block timestamp window
    validateTimestamp(extractedDataTimestamp);

    // Revert if data or block timestamp do not fit into 48 bits reserved in storage for timestamps
    if (extractedDataTimestamp > type(uint48).max) {
      revert DataTimestampTooLarge(extractedDataTimestamp);
    }
    if (block.timestamp > type(uint48).max) {
      revert BlockTimestampTooLarge(block.timestamp);
    }

    // The logic below can fail only in the case when gas limit reached
    for (uint256 i = 0; i < dataFeedsIds.length;) {
      // Note, each update is independent. It means that we are trying to update everything we can.
      // And skip the rest (emitting corresponding events)
      _tryToUpdateDataFeed(dataFeedsIds[i], oracleValues[i], extractedDataTimestamp);
      unchecked { i++; } // reduces gas costs
    }
  }

  function _tryToUpdateDataFeed(bytes32 dataFeedId, uint256 value, uint256 dataTimestamp) internal virtual {
    (uint256 lastDataTimestamp, uint256 lastBlockTimestamp, uint256 lastValue) = getLastUpdateDetailsUnsafe(dataFeedId);

    if (!_validateBlockTimestamp(lastBlockTimestamp)) {
      emit UpdateSkipDueToBlockTimestamp(dataFeedId);
      return;
    }

    if (!_validateDataTimestamp(dataTimestamp, lastDataTimestamp)) {
      emit UpdateSkipDueToDataTimestamp(dataFeedId);
      return;
    }

    if (!_validateValueBeforeSave(dataFeedId, value, lastValue)) {
      emit UpdateSkipDueToInvalidValue(dataFeedId);
      return;
    }

    _saveNewUpdateDetails(dataFeedId, value, dataTimestamp);
    _emitEventAfterValueUpdate(dataFeedId, value);
  }

  function _saveNewUpdateDetails(bytes32 dataFeedId, uint256 newValue, uint256 dataTimestamp) internal {
    DataFeedDetailsWithOptionalBigValue storage dataFeed = _getDataFeedsStorage()._dataFeeds[dataFeedId];

    bool isValueBigger = newValue > type(uint152).max;

    // We can safely cast timestamps here, because we checked timestamp values in the `updateDataFeedsValuesPartial` function
    dataFeed.details = DataFeedDetails({
      dataTimestamp: uint48(dataTimestamp),
      blockTimestamp: uint48(block.timestamp),
      value: uint152(newValue), // we can store anything here is isValueBigger == true, but it's slightly cheaper to always store the same value
      isValueBigger: isValueBigger
    });

    if (isValueBigger) {
      dataFeed.biggerValue = newValue;
    }
  }

  function getLastUpdateDetails(bytes32 dataFeedId) public view virtual returns (uint256 lastDataTimestamp, uint256 lastBlockTimestamp, uint256 lastValue) {
    (lastDataTimestamp, lastBlockTimestamp, lastValue) = getLastUpdateDetailsUnsafe(dataFeedId);
    if (!_validateLastUpdateDetailsOnRead(dataFeedId, lastDataTimestamp, lastBlockTimestamp, lastValue)) {
      revert InvalidLastUpdateDetails(dataFeedId, lastDataTimestamp, lastBlockTimestamp, lastValue);
    }
  }

  function getLastUpdateDetailsUnsafe(bytes32 dataFeedId) public view virtual returns (uint256 lastDataTimestamp, uint256 lastBlockTimestamp, uint256 lastValue) {
    DataFeedDetailsWithOptionalBigValue storage dataFeed = _getDataFeedsStorage()._dataFeeds[dataFeedId];

    lastDataTimestamp = dataFeed.details.dataTimestamp;
    lastBlockTimestamp = dataFeed.details.blockTimestamp;

    if (dataFeed.details.isValueBigger) {
      lastValue = dataFeed.biggerValue;
    } else {
      lastValue = dataFeed.details.value;
    }
  }

  function _getDataFeedsStorage() private pure returns (DataFeedsStorage storage $) {
    assembly {
      $.slot := DATA_FEEDS_STORAGE_LOCATION
    }
  }

  /// This function can be used to implement time-based whitelisting (e.g. whitelisting for only X seconds after the latest update)
  /// Important! This function should not revert, it should only return bool result of the validation
  function _validateBlockTimestamp(uint256 lastBlockTimestamp) internal view virtual returns (bool) {
    // In the default implementation we just check if the block number is higher
    // To ensure max 1 update for a given data feed in a block
    return block.timestamp > lastBlockTimestamp;
  }

  /// Important! This function should not revert, it should only return bool result of the validation
  function _validateDataTimestamp(uint256 proposedDataTimestamp, uint256 lastDataTimestamp) internal view virtual returns (bool) {
    return proposedDataTimestamp > lastDataTimestamp;
  }

  /// Important! This function should not revert, it should only return bool result of the validation
  /// It can be overridden to handle more specific logic in future
  function _validateValueBeforeSave(bytes32 /* dataFeedId */, uint256 proposedValue, uint256 /* lastValue */) internal view virtual returns (bool) {
    return proposedValue > 0;
  }

  /// This function can be overridden (e.g. value validation and staleness check)
  /// We've added dataFeedId for being able to implement custom validation per feed
  function _validateLastUpdateDetailsOnRead(bytes32 /* dataFeedId */, uint256 /* lastDataTimestamp */, uint256 lastBlockTimestamp, uint256 lastValue) internal view virtual returns (bool) {
    return lastValue > 0 && lastBlockTimestamp + MAX_DATA_STALENESS > block.timestamp;
  }

  /// Important! This function should not revert, it should only emit an event
  /// It is a separate function, so that we can specify custom events for specific data feeds
  function _emitEventAfterValueUpdate(bytes32 dataFeedId, uint256 newValue) internal virtual {
    emit ValueUpdate(newValue, dataFeedId, block.timestamp);
  }


  ////////////////////////////////////////////////////
  /////////// Functions for relayers below ///////////
  ////////////////////////////////////////////////////

  function getLastUpdateDetailsUnsafeForMany(bytes32[] memory dataFeedIds) external view returns (LastUpdateDetails[] memory detailsForFeeds) {
    detailsForFeeds = new LastUpdateDetails[](dataFeedIds.length);
    for (uint256 i = 0; i < dataFeedIds.length;) {
      (detailsForFeeds[i].dataTimestamp, detailsForFeeds[i].blockTimestamp, detailsForFeeds[i].value) = getLastUpdateDetailsUnsafe(dataFeedIds[i]);
      unchecked { i++; } // reduces gas costs
    }
  }

  function getValuesForDataFeeds(bytes32[] memory requestedDataFeedIds) external view returns (uint256[] memory values) {
    values = new uint256[](requestedDataFeedIds.length);
    for (uint256 i = 0; i < requestedDataFeedIds.length;) {
      values[i] = getValueForDataFeed(requestedDataFeedIds[i]);
      unchecked { i++; } // reduces gas costs
    }
  }

  function getValueForDataFeed(bytes32 dataFeedId) public view virtual returns (uint256 dataFeedValue) {
    (,, dataFeedValue) = getLastUpdateDetails(dataFeedId);
  }

  function getDataTimestampFromLatestUpdate(bytes32 dataFeedId) external view virtual returns (uint256 lastDataTimestamp) {
    (lastDataTimestamp, ,) = getLastUpdateDetails(dataFeedId);
  }

  function getBlockTimestampFromLatestUpdate(bytes32 dataFeedId) external view virtual returns (uint256 blockTimestamp) {
    (, blockTimestamp, ) = getLastUpdateDetails(dataFeedId);
  }

  ///////////////////////////////////////////////////////
  //////////// LayerBank interface functions ////////////
  ///////////////////////////////////////////////////////

  /// We can connect manager contract here or implement it directly here
  /// By default, users will be able to use data feed identifiers (casted to addresses) in layerbank functions
  function getDataFeedIdForAsset(address asset) public view virtual returns(bytes32) {
    return bytes32(uint256(uint160(asset)));
  }

  function convertDecimals(bytes32 /* dataFeedId */, uint256 valueFromRedstonePayload) public view virtual returns (uint256) {
    return valueFromRedstonePayload * DEFAULT_DECIMAL_SCALER_LAYERBANK;
  }

  function getUnderlyingAsset(address gToken) public view virtual returns(address) {
    return ILToken(gToken).underlying();
  }

  function priceOf(address asset) public view virtual returns (uint256) {
    bytes32 dataFeedId = getDataFeedIdForAsset(asset);
    uint256 latestValue = getValueForDataFeed(dataFeedId);
    return convertDecimals(dataFeedId, latestValue);
  }

  function priceOfETH() public view virtual returns (uint256) {
    return convertDecimals(ETH_DATA_FEED_ID, getValueForDataFeed(ETH_DATA_FEED_ID));
  }

  function pricesOf(
    address[] memory assets
  ) external view returns (uint256[] memory values) {
    values = new uint256[](assets.length);
    for (uint256 i = 0; i < assets.length;) {
      values[i] = priceOf(assets[i]);
      unchecked { i++; } // reduces gas costs
    }
  }

  function getUnderlyingPrice(address gToken) public view returns (uint256) {
    return priceOf(getUnderlyingAsset(gToken));
  }

  function getUnderlyingPrices(
    address[] memory gTokens
  ) public view returns (uint256[] memory values) {
    values = new uint256[](gTokens.length);
    for (uint256 i = 0; i < gTokens.length;) {
      values[i] = getUnderlyingPrice(gTokens[i]);
      unchecked { i++; } // reduces gas costs
    }
  }
}

File 15 of 15 : Addresses.sol
// SPDX-License-Identifier: BUSL-1.1

pragma solidity ^0.8.4;

library OldGelatoAddress {
  address constant public ADDR = 0xc4D1AE5E796E6d7561cdc8335F85e6B57a36e097;
}

library GelatoAddress {
  address constant public ADDR = 0xCD6BfDA4D95d5C0f3f2882dC221D792392c99714;
}

Settings
{
  "optimizer": {
    "enabled": true,
    "runs": 10000
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "libraries": {}
}

Contract Security Audit

Contract ABI

API
[{"inputs":[{"internalType":"uint256","name":"blockTimestamp","type":"uint256"}],"name":"BlockTimestampTooLarge","type":"error"},{"inputs":[],"name":"CalldataMustHaveValidPayload","type":"error"},{"inputs":[],"name":"CalldataOverOrUnderFlow","type":"error"},{"inputs":[],"name":"CanNotPickMedianOfEmptyArray","type":"error"},{"inputs":[],"name":"DataPackageTimestampMustNotBeZero","type":"error"},{"inputs":[],"name":"DataPackageTimestampsMustBeEqual","type":"error"},{"inputs":[],"name":"DataTimestampCannotBeZero","type":"error"},{"inputs":[{"internalType":"uint256","name":"dataTimestamp","type":"uint256"}],"name":"DataTimestampTooLarge","type":"error"},{"inputs":[],"name":"EachSignerMustProvideTheSameValue","type":"error"},{"inputs":[],"name":"EmptyCalldataPointersArr","type":"error"},{"inputs":[],"name":"GetDataServiceIdNotImplemented","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":"bytes32","name":"dataFeedId","type":"bytes32"},{"internalType":"uint256","name":"lastDataTimestamp","type":"uint256"},{"internalType":"uint256","name":"lastBlockTimestamp","type":"uint256"},{"internalType":"uint256","name":"lastValue","type":"uint256"}],"name":"InvalidLastUpdateDetails","type":"error"},{"inputs":[{"internalType":"bytes32","name":"signedHash","type":"bytes32"}],"name":"InvalidSignature","type":"error"},{"inputs":[],"name":"RedstonePayloadMustHaveAtLeastOneDataPackage","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":[],"name":"TimestampsMustBeEqual","type":"error"},{"inputs":[{"internalType":"uint256","name":"valueByteSize","type":"uint256"}],"name":"TooLargeValueByteSize","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bytes32","name":"dataFeedId","type":"bytes32"}],"name":"UpdateSkipDueToBlockTimestamp","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bytes32","name":"dataFeedId","type":"bytes32"}],"name":"UpdateSkipDueToDataTimestamp","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bytes32","name":"dataFeedId","type":"bytes32"}],"name":"UpdateSkipDueToInvalidValue","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"},{"indexed":false,"internalType":"bytes32","name":"dataFeedId","type":"bytes32"},{"indexed":false,"internalType":"uint256","name":"updatedAt","type":"uint256"}],"name":"ValueUpdate","type":"event"},{"inputs":[{"internalType":"uint256[]","name":"values","type":"uint256[]"}],"name":"aggregateValues","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"},{"internalType":"uint256","name":"valueFromRedstonePayload","type":"uint256"}],"name":"convertDecimals","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"extractTimestampsAndAssertAllAreEqual","outputs":[{"internalType":"uint256","name":"extractedTimestamp","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"signerAddress","type":"address"}],"name":"getAuthorisedSignerIndex","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"dataFeedId","type":"bytes32"}],"name":"getBlockTimestampFromLatestUpdate","outputs":[{"internalType":"uint256","name":"blockTimestamp","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"asset","type":"address"}],"name":"getDataFeedIdForAsset","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getDataServiceId","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"dataFeedId","type":"bytes32"}],"name":"getDataTimestampFromLatestUpdate","outputs":[{"internalType":"uint256","name":"lastDataTimestamp","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"dataFeedId","type":"bytes32"}],"name":"getLastUpdateDetails","outputs":[{"internalType":"uint256","name":"lastDataTimestamp","type":"uint256"},{"internalType":"uint256","name":"lastBlockTimestamp","type":"uint256"},{"internalType":"uint256","name":"lastValue","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"dataFeedId","type":"bytes32"}],"name":"getLastUpdateDetailsUnsafe","outputs":[{"internalType":"uint256","name":"lastDataTimestamp","type":"uint256"},{"internalType":"uint256","name":"lastBlockTimestamp","type":"uint256"},{"internalType":"uint256","name":"lastValue","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32[]","name":"dataFeedIds","type":"bytes32[]"}],"name":"getLastUpdateDetailsUnsafeForMany","outputs":[{"components":[{"internalType":"uint256","name":"dataTimestamp","type":"uint256"},{"internalType":"uint256","name":"blockTimestamp","type":"uint256"},{"internalType":"uint256","name":"value","type":"uint256"}],"internalType":"struct MultiFeedAdapterWithoutRounds.LastUpdateDetails[]","name":"detailsForFeeds","type":"tuple[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"gToken","type":"address"}],"name":"getUnderlyingAsset","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"gToken","type":"address"}],"name":"getUnderlyingPrice","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address[]","name":"gTokens","type":"address[]"}],"name":"getUnderlyingPrices","outputs":[{"internalType":"uint256[]","name":"values","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getUniqueSignersThreshold","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"dataFeedId","type":"bytes32"}],"name":"getValueForDataFeed","outputs":[{"internalType":"uint256","name":"dataFeedValue","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32[]","name":"requestedDataFeedIds","type":"bytes32[]"}],"name":"getValuesForDataFeeds","outputs":[{"internalType":"uint256[]","name":"values","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"asset","type":"address"}],"name":"priceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"priceOfETH","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address[]","name":"assets","type":"address[]"}],"name":"pricesOf","outputs":[{"internalType":"uint256[]","name":"values","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32[]","name":"dataFeedsIds","type":"bytes32[]"}],"name":"updateDataFeedsValuesPartial","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"receivedTimestampMilliseconds","type":"uint256"}],"name":"validateTimestamp","outputs":[],"stateMutability":"view","type":"function"}]

608060405234801561001057600080fd5b506120cd806100206000396000f3fe608060405234801561001057600080fd5b50600436106101825760003560e01c80639037ecae116100d8578063c274583a1161008c578063f90c492411610066578063f90c492414610371578063fb2eeb3b14610378578063fc57d4df1461038b57600080fd5b8063c274583a14610311578063e8689daa14610326578063f50b2efe1461035e57600080fd5b8063b24ebfcc116100bd578063b24ebfcc146102d6578063b7a16251146102e9578063b95ed06f146102fe57600080fd5b80639037ecae146102b0578063971b9c03146102c357600080fd5b806355a547d51161013a5780637de92016116101145780637de9201614610275578063858b033d1461028857806386a8a7221461029057600080fd5b806355a547d51461022c578063612f56bb14610234578063729bfda41461024757600080fd5b806343ba8cad1161016b57806343ba8cad146101e657806344e02982146101f957806348a1371b1461020c57600080fd5b806302b9c828146101875780633ce142f5146101c1575b600080fd5b6101ae610195366004611c28565b73ffffffffffffffffffffffffffffffffffffffff1690565b6040519081526020015b60405180910390f35b6101d46101cf366004611c28565b61039e565b60405160ff90911681526020016101b8565b6101ae6101f4366004611c45565b61050c565b6101ae610207366004611c45565b61051f565b61021f61021a366004611d00565b610532565b6040516101b89190611d9f565b6101ae6105d3565b6101ae610242366004611de3565b61072c565b61025a610255366004611c45565b610746565b604080519384526020840192909252908201526060016101b8565b61021f610283366004611d00565b6107f9565b6101ae610894565b6102a361029e366004611e05565b6108e8565b6040516101b89190611e8b565b6101ae6102be366004611c45565b6109f4565b61021f6102d1366004611e05565b610a08565b6101ae6102e4366004611e05565b610aa3565b6102fc6102f7366004611e05565b610aae565b005b6101ae61030c366004611c28565b610ba3565b610319610bd2565b6040516101b89190611ee4565b610339610334366004611c28565b610c06565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020016101b8565b6102fc61036c366004611c45565b610c77565b60036101d4565b61025a610386366004611c45565b610c83565b6101ae610399366004611c28565b610cf9565b600073ffffffffffffffffffffffffffffffffffffffff8216738bb8f32df04c8b654987daaed53d6b6091e3b774036103d957506000919050565b73ffffffffffffffffffffffffffffffffffffffff821673deb22f54738d54976c4c0fe5ce6d408e40d884990361041257506001919050565b73ffffffffffffffffffffffffffffffffffffffff82167351ce04be4b3e32572c4ec9135221d0691ba7d2020361044b57506002919050565b73ffffffffffffffffffffffffffffffffffffffff821673dd682daec5a90dd295d14da4b0bec9281017b5be0361048457506003919050565b73ffffffffffffffffffffffffffffffffffffffff8216739c5ae89c4af6aa32ce58588dbaf90d18a855b6de036104bd57506004919050565b6040517fec459bc000000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff831660048201526024015b60405180910390fd5b600061051782610c83565b509392505050565b600061052a82610c83565b949350505050565b6060815167ffffffffffffffff81111561054e5761054e611c5e565b604051908082528060200260200182016040528015610577578160200160208202803683370190505b50905060005b82518110156105cd576105a883828151811061059b5761059b611f50565b6020026020010151610cf9565b8282815181106105ba576105ba611f50565b602090810291909101015260010161057d565b50919050565b6000806105de610d07565b905060006105eb82610e3c565b925061ffff169050600081900361062e576040517f8552ff3c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60005b8181101561072657600061064484610ea4565b9050600080610654606887611fae565b905060006106628236611fc1565b9050803592508265ffffffffffff166000036106aa576040517f336dc9d000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b876000036106c2578265ffffffffffff169750610703565b878365ffffffffffff1614610703576040517fd9d1f46500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61070d8488611fae565b965050505050808061071e90611fd4565b915050610631565b50505090565b600061073d6402540be4008361200c565b90505b92915050565b60008181527f5e9fb4cb0eb3c2583734d3394f30bb14b241acb9b3a034f7e7ba1a62db4370f160205260408120805465ffffffffffff80821693660100000000000083049091169290917f0100000000000000000000000000000000000000000000000000000000000000900460ff16156107c757806001015491506107f1565b80546c01000000000000000000000000900472ffffffffffffffffffffffffffffffffffffff1691505b509193909250565b6060815167ffffffffffffffff81111561081557610815611c5e565b60405190808252806020026020018201604052801561083e578160200160208202803683370190505b50905060005b82518110156105cd5761086f83828151811061086257610862611f50565b6020026020010151610ba3565b82828151811061088157610881611f50565b6020908102919091010152600101610844565b60006108e37f45544800000000000000000000000000000000000000000000000000000000006102427f455448000000000000000000000000000000000000000000000000000000000061051f565b905090565b6060815167ffffffffffffffff81111561090457610904611c5e565b60405190808252806020026020018201604052801561095957816020015b61094660405180606001604052806000815260200160008152602001600081525090565b8152602001906001900390816109225790505b50905060005b82518110156105cd5761098a83828151811061097d5761097d611f50565b6020026020010151610746565b84848151811061099c5761099c611f50565b60200260200101516000018585815181106109b9576109b9611f50565b60200260200101516020018686815181106109d6576109d6611f50565b6020908102919091010151604001929092529190525260010161095f565b60006109ff82610c83565b50909392505050565b6060815167ffffffffffffffff811115610a2457610a24611c5e565b604051908082528060200260200182016040528015610a4d578160200160208202803683370190505b50905060005b82518110156105cd57610a7e838281518110610a7157610a71611f50565b602002602001015161051f565b828281518110610a9057610a90611f50565b6020908102919091010152600101610a53565b600061074082610ed8565b600080610aba83610ee3565b91509150610ac781610c77565b65ffffffffffff811115610b0a576040517f48f06f0800000000000000000000000000000000000000000000000000000000815260048101829052602401610503565b65ffffffffffff421115610b4c576040517f5cf61159000000000000000000000000000000000000000000000000000000008152426004820152602401610503565b60005b8351811015610b9d57610b95848281518110610b6d57610b6d611f50565b6020026020010151848381518110610b8757610b87611f50565b602002602001015184610ef9565b600101610b4f565b50505050565b600073ffffffffffffffffffffffffffffffffffffffff821681610bc68261051f565b905061052a828261072c565b60606040517f608b530700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008173ffffffffffffffffffffffffffffffffffffffff16636f307dc36040518163ffffffff1660e01b8152600401602060405180830381865afa158015610c53573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107409190612023565b610c8081610fdf565b50565b6000806000610c9184610746565b91945092509050610ca484848484611095565b610cf2576040517f4f319ffe00000000000000000000000000000000000000000000000000000000815260048101859052602481018490526044810183905260648101829052608401610503565b9193909250565b600061074061030c83610c06565b60006602ed57011e00007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe036013581161480610d6f576040517fe7764c9e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60003660291115610dac576040517f5796f78a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd736013560006009610de5600362ffffff8516611fae565b610def9190611fae565b905036610dfd600283611fae565b1115610e35576040517fc30a7bd700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b9392505050565b60008080610e4b602085611fae565b905036811115610e87576040517f5796f78a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b3681900335925082610e9a600286611fae565b9250925050915091565b6000806000610eb2846110ba565b9092509050604e610ec4826020611fae565b610ece908461200c565b61052a9190611fae565b60006107408261110d565b60606000610ef08361124a565b91509150915091565b6000806000610f0786610746565b925092509250610f1682611470565b610f56576040518681527fd2fed36e637cb43fd01b51ddf3b0ca9d9966df112183894d5699cd9a16f85eb5906020015b60405180910390a1505050505050565b838310610f8d576040518681527ff34a55619bad9bd385340bece69770212d10a808ac72a95e6454acf05462ac1b90602001610f46565b84610fc2576040518681527fad6ebdc3909f212583bf7e8a0f31338c8c92f403f2fd37d23014fb3fd34e310290602001610f46565b610fcd86868661151f565b610fd7868661164d565b505050505050565b6000610fed6103e88361206f565b90508042101561104857603c6110034283611fc1565b1115611044576040517fb6b0916d00000000000000000000000000000000000000000000000000000000815260048101829052426024820152604401610503565b5050565b60b46110548242611fc1565b1115611044576040517f0321d0b500000000000000000000000000000000000000000000000000000000815260048101829052426024820152604401610503565b600080821180156110b15750426110af6201a5e085611fae565b115b95945050505050565b60008080808060206110cd604188611fae565b6110d79190611fae565b6110e19036611fc1565b8035935090506110f2600382611fc1565b62ffffff9390931696923563ffffffff169550919350505050565b600081516002036111645760028260018151811061112d5761112d611f50565b60200260200101518360008151811061114857611148611f50565b602002602001015161115a9190611fae565b610740919061206f565b815160000361119f576040517f9e198af900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6111a882611690565b6000600283516111b8919061206f565b9050600283516111c89190612083565b6000036112285760008382815181106111e3576111e3611f50565b6020026020010151846001846111f99190611fc1565b8151811061120957611209611f50565b602002602001015161121b9190611fae565b905061052a60028261206f565b82818151811061123a5761123a611f50565b6020026020010151915050919050565b6060600080835167ffffffffffffffff81111561126957611269611c5e565b604051908082528060200260200182016040528015611292578160200160208202803683370190505b5090506000845167ffffffffffffffff8111156112b1576112b1611c5e565b6040519080825280602002602001820160405280156112da578160200160208202803683370190505b5090506000855167ffffffffffffffff8111156112f9576112f9611c5e565b60405190808252806020026020018201604052801561132c57816020015b60608152602001906001900390816113175790505b50905060005b865181101561137f576040805160038082526080820190925290602082016060803683370190505082828151811061136c5761136c611f50565b6020908102919091010152600101611332565b50600061138a610d07565b9050600061139782610e3c565b60405190935061ffff909116915060005b828110156114585760006113bf8b898989896116d8565b909550905060008190036113ff576040517fdfb25a7900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b888114611448578860000361141657809850611448565b6040517f4cbc474200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b50602082016040526001016113a8565b506114638487611981565b9750505050505050915091565b60003373f0547b3e44b904fee2569acf5107769dd28a17c314806114a7575033733f11cb12d6e69f66124ea6ca45d3c5ffdc4b1704145b806114c5575033734622decefa557d7cffd9aa00b93c5f9a7bbce15f145b806114e357503373cd6bfda4d95d5c0f3f2882dc221d792392c99714145b8061150157503373c4d1ae5e796e6d7561cdc8335f85e6b57a36e097145b1561150c5750421190565b611517826028611fae565b421192915050565b60007f5e9fb4cb0eb3c2583734d3394f30bb14b241acb9b3a034f7e7ba1a62db4370f160008581526020918252604090819020815160808101835265ffffffffffff8681168083524290911694820185905272ffffffffffffffffffffffffffffffffffffff8881169483018590528811606090920182905282547fffffffffffffffffffffffffffffffffffffffff00000000000000000000000016176601000000000000909402939093176bffffffffffffffffffffffff166c010000000000000000000000009092027effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff16919091177f010000000000000000000000000000000000000000000000000000000000000083021781559150801561164657600182018490555b5050505050565b6040805182815260208101849052428183015290517ff36866d965ee70c8632ff558f5cf8d41ee9ca1d0d0bc7700786e57be607473909181900360600190a15050565b8051602082016020820281019150805b82811015610b9d57815b818110156116cf5781518151808210156116c5578084528183525b50506020016116aa565b506020016116a0565b60008060008060006116e9866110ba565b909250905060008060608180600d611702602088611fae565b61170c908961200c565b6117169190611fae565b9150600061172560688d611fae565b61172f9036611fc1565b905060008361173f60418f611fae565b6117499190611fae565b6117539036611fc1565b905061175f8482611772565b94508360208601209550813592506117b4565b604080518381526020818501810190925260009101838382377fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0019392505050565b8265ffffffffffff169a506117d58660418f6117d09190611fae565b611ab3565b96506117e08761039e565b60ff16995050505050505050604e866117f99190611fae565b955060008060005b8481101561196e576020611815858b611fae565b61181f9190611fae565b985061182b8985611b9a565b909350915060005b8d51811015611965578d818151811061184e5761184e611f50565b6020026020010151840361195d5760008c828151811061187057611870611f50565b6020026020010151905061188981896001901b16151590565b1580156118b25750600360ff168e83815181106118a8576118a8611f50565b6020026020010151105b1561195757838c83815181106118ca576118ca611f50565b60200260200101518f84815181106118e4576118e4611f50565b6020026020010151815181106118fc576118fc611f50565b6020026020010181815250508d828151811061191a5761191a611f50565b60200260200101805180919061192f90611fd4565b9052506001881b81178d838151811061194a5761194a611f50565b6020026020010181815250505b50611965565b600101611833565b50600101611801565b5050508594505050509550959350505050565b60606000835167ffffffffffffffff81111561199f5761199f611c5e565b6040519080825280602002602001820160405280156119c8578160200160208202803683370190505b509050600360005b8551811015611aa957818582815181106119ec576119ec611f50565b60200260200101511015611a5257848181518110611a0c57611a0c611f50565b6020026020010151826040517f2b13aef5000000000000000000000000000000000000000000000000000000008152600401610503929190918252602082015260400190565b6000611a76878381518110611a6957611a69611f50565b6020026020010151610aa3565b905080848381518110611a8b57611a8b611f50565b60209081029190910101525080611aa181611fd4565b9150506119d0565b5090949350505050565b60408051600080825260208083018085528690523685900380850135831a948401859052803560608501819052910135608084018190529193909260019060a0016020604051602081039080840390855afa158015611b16573d6000803e3d6000fd5b50506040517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0015194505073ffffffffffffffffffffffffffffffffffffffff8416611b91576040517f666b7cba00000000000000000000000000000000000000000000000000000000815260048101879052602401610503565b50505092915050565b60008080611ba88536611fc1565b9050803592506020810135915060218410611bf2576040517fc000fc4200000000000000000000000000000000000000000000000000000000815260048101859052602401610503565b8360200360080282901c9150509250929050565b73ffffffffffffffffffffffffffffffffffffffff81168114610c8057600080fd5b600060208284031215611c3a57600080fd5b8135610e3581611c06565b600060208284031215611c5757600080fd5b5035919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff81118282101715611cd457611cd4611c5e565b604052919050565b600067ffffffffffffffff821115611cf657611cf6611c5e565b5060051b60200190565b60006020808385031215611d1357600080fd5b823567ffffffffffffffff811115611d2a57600080fd5b8301601f81018513611d3b57600080fd5b8035611d4e611d4982611cdc565b611c8d565b81815260059190911b82018301908381019087831115611d6d57600080fd5b928401925b82841015611d94578335611d8581611c06565b82529284019290840190611d72565b979650505050505050565b6020808252825182820181905260009190848201906040850190845b81811015611dd757835183529284019291840191600101611dbb565b50909695505050505050565b60008060408385031215611df657600080fd5b50508035926020909101359150565b60006020808385031215611e1857600080fd5b823567ffffffffffffffff811115611e2f57600080fd5b8301601f81018513611e4057600080fd5b8035611e4e611d4982611cdc565b81815260059190911b82018301908381019087831115611e6d57600080fd5b928401925b82841015611d9457833582529284019290840190611e72565b602080825282518282018190526000919060409081850190868401855b82811015611ed75781518051855286810151878601528501518585015260609093019290850190600101611ea8565b5091979650505050505050565b600060208083528351808285015260005b81811015611f1157858101830151858201604001528201611ef5565b5060006040828601015260407fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8301168501019250505092915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b8082018082111561074057610740611f7f565b8181038181111561074057610740611f7f565b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff820361200557612005611f7f565b5060010190565b808202811582820484141761074057610740611f7f565b60006020828403121561203557600080fd5b8151610e3581611c06565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b60008261207e5761207e612040565b500490565b60008261209257612092612040565b50069056fea2646970667358221220826d92ed14652aeb23db118117b6fa0a3219d69d198f1ab40585aec1d71f854e64736f6c63430008110033

Deployed Bytecode

0x608060405234801561001057600080fd5b50600436106101825760003560e01c80639037ecae116100d8578063c274583a1161008c578063f90c492411610066578063f90c492414610371578063fb2eeb3b14610378578063fc57d4df1461038b57600080fd5b8063c274583a14610311578063e8689daa14610326578063f50b2efe1461035e57600080fd5b8063b24ebfcc116100bd578063b24ebfcc146102d6578063b7a16251146102e9578063b95ed06f146102fe57600080fd5b80639037ecae146102b0578063971b9c03146102c357600080fd5b806355a547d51161013a5780637de92016116101145780637de9201614610275578063858b033d1461028857806386a8a7221461029057600080fd5b806355a547d51461022c578063612f56bb14610234578063729bfda41461024757600080fd5b806343ba8cad1161016b57806343ba8cad146101e657806344e02982146101f957806348a1371b1461020c57600080fd5b806302b9c828146101875780633ce142f5146101c1575b600080fd5b6101ae610195366004611c28565b73ffffffffffffffffffffffffffffffffffffffff1690565b6040519081526020015b60405180910390f35b6101d46101cf366004611c28565b61039e565b60405160ff90911681526020016101b8565b6101ae6101f4366004611c45565b61050c565b6101ae610207366004611c45565b61051f565b61021f61021a366004611d00565b610532565b6040516101b89190611d9f565b6101ae6105d3565b6101ae610242366004611de3565b61072c565b61025a610255366004611c45565b610746565b604080519384526020840192909252908201526060016101b8565b61021f610283366004611d00565b6107f9565b6101ae610894565b6102a361029e366004611e05565b6108e8565b6040516101b89190611e8b565b6101ae6102be366004611c45565b6109f4565b61021f6102d1366004611e05565b610a08565b6101ae6102e4366004611e05565b610aa3565b6102fc6102f7366004611e05565b610aae565b005b6101ae61030c366004611c28565b610ba3565b610319610bd2565b6040516101b89190611ee4565b610339610334366004611c28565b610c06565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020016101b8565b6102fc61036c366004611c45565b610c77565b60036101d4565b61025a610386366004611c45565b610c83565b6101ae610399366004611c28565b610cf9565b600073ffffffffffffffffffffffffffffffffffffffff8216738bb8f32df04c8b654987daaed53d6b6091e3b774036103d957506000919050565b73ffffffffffffffffffffffffffffffffffffffff821673deb22f54738d54976c4c0fe5ce6d408e40d884990361041257506001919050565b73ffffffffffffffffffffffffffffffffffffffff82167351ce04be4b3e32572c4ec9135221d0691ba7d2020361044b57506002919050565b73ffffffffffffffffffffffffffffffffffffffff821673dd682daec5a90dd295d14da4b0bec9281017b5be0361048457506003919050565b73ffffffffffffffffffffffffffffffffffffffff8216739c5ae89c4af6aa32ce58588dbaf90d18a855b6de036104bd57506004919050565b6040517fec459bc000000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff831660048201526024015b60405180910390fd5b600061051782610c83565b509392505050565b600061052a82610c83565b949350505050565b6060815167ffffffffffffffff81111561054e5761054e611c5e565b604051908082528060200260200182016040528015610577578160200160208202803683370190505b50905060005b82518110156105cd576105a883828151811061059b5761059b611f50565b6020026020010151610cf9565b8282815181106105ba576105ba611f50565b602090810291909101015260010161057d565b50919050565b6000806105de610d07565b905060006105eb82610e3c565b925061ffff169050600081900361062e576040517f8552ff3c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60005b8181101561072657600061064484610ea4565b9050600080610654606887611fae565b905060006106628236611fc1565b9050803592508265ffffffffffff166000036106aa576040517f336dc9d000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b876000036106c2578265ffffffffffff169750610703565b878365ffffffffffff1614610703576040517fd9d1f46500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61070d8488611fae565b965050505050808061071e90611fd4565b915050610631565b50505090565b600061073d6402540be4008361200c565b90505b92915050565b60008181527f5e9fb4cb0eb3c2583734d3394f30bb14b241acb9b3a034f7e7ba1a62db4370f160205260408120805465ffffffffffff80821693660100000000000083049091169290917f0100000000000000000000000000000000000000000000000000000000000000900460ff16156107c757806001015491506107f1565b80546c01000000000000000000000000900472ffffffffffffffffffffffffffffffffffffff1691505b509193909250565b6060815167ffffffffffffffff81111561081557610815611c5e565b60405190808252806020026020018201604052801561083e578160200160208202803683370190505b50905060005b82518110156105cd5761086f83828151811061086257610862611f50565b6020026020010151610ba3565b82828151811061088157610881611f50565b6020908102919091010152600101610844565b60006108e37f45544800000000000000000000000000000000000000000000000000000000006102427f455448000000000000000000000000000000000000000000000000000000000061051f565b905090565b6060815167ffffffffffffffff81111561090457610904611c5e565b60405190808252806020026020018201604052801561095957816020015b61094660405180606001604052806000815260200160008152602001600081525090565b8152602001906001900390816109225790505b50905060005b82518110156105cd5761098a83828151811061097d5761097d611f50565b6020026020010151610746565b84848151811061099c5761099c611f50565b60200260200101516000018585815181106109b9576109b9611f50565b60200260200101516020018686815181106109d6576109d6611f50565b6020908102919091010151604001929092529190525260010161095f565b60006109ff82610c83565b50909392505050565b6060815167ffffffffffffffff811115610a2457610a24611c5e565b604051908082528060200260200182016040528015610a4d578160200160208202803683370190505b50905060005b82518110156105cd57610a7e838281518110610a7157610a71611f50565b602002602001015161051f565b828281518110610a9057610a90611f50565b6020908102919091010152600101610a53565b600061074082610ed8565b600080610aba83610ee3565b91509150610ac781610c77565b65ffffffffffff811115610b0a576040517f48f06f0800000000000000000000000000000000000000000000000000000000815260048101829052602401610503565b65ffffffffffff421115610b4c576040517f5cf61159000000000000000000000000000000000000000000000000000000008152426004820152602401610503565b60005b8351811015610b9d57610b95848281518110610b6d57610b6d611f50565b6020026020010151848381518110610b8757610b87611f50565b602002602001015184610ef9565b600101610b4f565b50505050565b600073ffffffffffffffffffffffffffffffffffffffff821681610bc68261051f565b905061052a828261072c565b60606040517f608b530700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008173ffffffffffffffffffffffffffffffffffffffff16636f307dc36040518163ffffffff1660e01b8152600401602060405180830381865afa158015610c53573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107409190612023565b610c8081610fdf565b50565b6000806000610c9184610746565b91945092509050610ca484848484611095565b610cf2576040517f4f319ffe00000000000000000000000000000000000000000000000000000000815260048101859052602481018490526044810183905260648101829052608401610503565b9193909250565b600061074061030c83610c06565b60006602ed57011e00007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe036013581161480610d6f576040517fe7764c9e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60003660291115610dac576040517f5796f78a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd736013560006009610de5600362ffffff8516611fae565b610def9190611fae565b905036610dfd600283611fae565b1115610e35576040517fc30a7bd700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b9392505050565b60008080610e4b602085611fae565b905036811115610e87576040517f5796f78a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b3681900335925082610e9a600286611fae565b9250925050915091565b6000806000610eb2846110ba565b9092509050604e610ec4826020611fae565b610ece908461200c565b61052a9190611fae565b60006107408261110d565b60606000610ef08361124a565b91509150915091565b6000806000610f0786610746565b925092509250610f1682611470565b610f56576040518681527fd2fed36e637cb43fd01b51ddf3b0ca9d9966df112183894d5699cd9a16f85eb5906020015b60405180910390a1505050505050565b838310610f8d576040518681527ff34a55619bad9bd385340bece69770212d10a808ac72a95e6454acf05462ac1b90602001610f46565b84610fc2576040518681527fad6ebdc3909f212583bf7e8a0f31338c8c92f403f2fd37d23014fb3fd34e310290602001610f46565b610fcd86868661151f565b610fd7868661164d565b505050505050565b6000610fed6103e88361206f565b90508042101561104857603c6110034283611fc1565b1115611044576040517fb6b0916d00000000000000000000000000000000000000000000000000000000815260048101829052426024820152604401610503565b5050565b60b46110548242611fc1565b1115611044576040517f0321d0b500000000000000000000000000000000000000000000000000000000815260048101829052426024820152604401610503565b600080821180156110b15750426110af6201a5e085611fae565b115b95945050505050565b60008080808060206110cd604188611fae565b6110d79190611fae565b6110e19036611fc1565b8035935090506110f2600382611fc1565b62ffffff9390931696923563ffffffff169550919350505050565b600081516002036111645760028260018151811061112d5761112d611f50565b60200260200101518360008151811061114857611148611f50565b602002602001015161115a9190611fae565b610740919061206f565b815160000361119f576040517f9e198af900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6111a882611690565b6000600283516111b8919061206f565b9050600283516111c89190612083565b6000036112285760008382815181106111e3576111e3611f50565b6020026020010151846001846111f99190611fc1565b8151811061120957611209611f50565b602002602001015161121b9190611fae565b905061052a60028261206f565b82818151811061123a5761123a611f50565b6020026020010151915050919050565b6060600080835167ffffffffffffffff81111561126957611269611c5e565b604051908082528060200260200182016040528015611292578160200160208202803683370190505b5090506000845167ffffffffffffffff8111156112b1576112b1611c5e565b6040519080825280602002602001820160405280156112da578160200160208202803683370190505b5090506000855167ffffffffffffffff8111156112f9576112f9611c5e565b60405190808252806020026020018201604052801561132c57816020015b60608152602001906001900390816113175790505b50905060005b865181101561137f576040805160038082526080820190925290602082016060803683370190505082828151811061136c5761136c611f50565b6020908102919091010152600101611332565b50600061138a610d07565b9050600061139782610e3c565b60405190935061ffff909116915060005b828110156114585760006113bf8b898989896116d8565b909550905060008190036113ff576040517fdfb25a7900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b888114611448578860000361141657809850611448565b6040517f4cbc474200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b50602082016040526001016113a8565b506114638487611981565b9750505050505050915091565b60003373f0547b3e44b904fee2569acf5107769dd28a17c314806114a7575033733f11cb12d6e69f66124ea6ca45d3c5ffdc4b1704145b806114c5575033734622decefa557d7cffd9aa00b93c5f9a7bbce15f145b806114e357503373cd6bfda4d95d5c0f3f2882dc221d792392c99714145b8061150157503373c4d1ae5e796e6d7561cdc8335f85e6b57a36e097145b1561150c5750421190565b611517826028611fae565b421192915050565b60007f5e9fb4cb0eb3c2583734d3394f30bb14b241acb9b3a034f7e7ba1a62db4370f160008581526020918252604090819020815160808101835265ffffffffffff8681168083524290911694820185905272ffffffffffffffffffffffffffffffffffffff8881169483018590528811606090920182905282547fffffffffffffffffffffffffffffffffffffffff00000000000000000000000016176601000000000000909402939093176bffffffffffffffffffffffff166c010000000000000000000000009092027effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff16919091177f010000000000000000000000000000000000000000000000000000000000000083021781559150801561164657600182018490555b5050505050565b6040805182815260208101849052428183015290517ff36866d965ee70c8632ff558f5cf8d41ee9ca1d0d0bc7700786e57be607473909181900360600190a15050565b8051602082016020820281019150805b82811015610b9d57815b818110156116cf5781518151808210156116c5578084528183525b50506020016116aa565b506020016116a0565b60008060008060006116e9866110ba565b909250905060008060608180600d611702602088611fae565b61170c908961200c565b6117169190611fae565b9150600061172560688d611fae565b61172f9036611fc1565b905060008361173f60418f611fae565b6117499190611fae565b6117539036611fc1565b905061175f8482611772565b94508360208601209550813592506117b4565b604080518381526020818501810190925260009101838382377fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0019392505050565b8265ffffffffffff169a506117d58660418f6117d09190611fae565b611ab3565b96506117e08761039e565b60ff16995050505050505050604e866117f99190611fae565b955060008060005b8481101561196e576020611815858b611fae565b61181f9190611fae565b985061182b8985611b9a565b909350915060005b8d51811015611965578d818151811061184e5761184e611f50565b6020026020010151840361195d5760008c828151811061187057611870611f50565b6020026020010151905061188981896001901b16151590565b1580156118b25750600360ff168e83815181106118a8576118a8611f50565b6020026020010151105b1561195757838c83815181106118ca576118ca611f50565b60200260200101518f84815181106118e4576118e4611f50565b6020026020010151815181106118fc576118fc611f50565b6020026020010181815250508d828151811061191a5761191a611f50565b60200260200101805180919061192f90611fd4565b9052506001881b81178d838151811061194a5761194a611f50565b6020026020010181815250505b50611965565b600101611833565b50600101611801565b5050508594505050509550959350505050565b60606000835167ffffffffffffffff81111561199f5761199f611c5e565b6040519080825280602002602001820160405280156119c8578160200160208202803683370190505b509050600360005b8551811015611aa957818582815181106119ec576119ec611f50565b60200260200101511015611a5257848181518110611a0c57611a0c611f50565b6020026020010151826040517f2b13aef5000000000000000000000000000000000000000000000000000000008152600401610503929190918252602082015260400190565b6000611a76878381518110611a6957611a69611f50565b6020026020010151610aa3565b905080848381518110611a8b57611a8b611f50565b60209081029190910101525080611aa181611fd4565b9150506119d0565b5090949350505050565b60408051600080825260208083018085528690523685900380850135831a948401859052803560608501819052910135608084018190529193909260019060a0016020604051602081039080840390855afa158015611b16573d6000803e3d6000fd5b50506040517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0015194505073ffffffffffffffffffffffffffffffffffffffff8416611b91576040517f666b7cba00000000000000000000000000000000000000000000000000000000815260048101879052602401610503565b50505092915050565b60008080611ba88536611fc1565b9050803592506020810135915060218410611bf2576040517fc000fc4200000000000000000000000000000000000000000000000000000000815260048101859052602401610503565b8360200360080282901c9150509250929050565b73ffffffffffffffffffffffffffffffffffffffff81168114610c8057600080fd5b600060208284031215611c3a57600080fd5b8135610e3581611c06565b600060208284031215611c5757600080fd5b5035919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff81118282101715611cd457611cd4611c5e565b604052919050565b600067ffffffffffffffff821115611cf657611cf6611c5e565b5060051b60200190565b60006020808385031215611d1357600080fd5b823567ffffffffffffffff811115611d2a57600080fd5b8301601f81018513611d3b57600080fd5b8035611d4e611d4982611cdc565b611c8d565b81815260059190911b82018301908381019087831115611d6d57600080fd5b928401925b82841015611d94578335611d8581611c06565b82529284019290840190611d72565b979650505050505050565b6020808252825182820181905260009190848201906040850190845b81811015611dd757835183529284019291840191600101611dbb565b50909695505050505050565b60008060408385031215611df657600080fd5b50508035926020909101359150565b60006020808385031215611e1857600080fd5b823567ffffffffffffffff811115611e2f57600080fd5b8301601f81018513611e4057600080fd5b8035611e4e611d4982611cdc565b81815260059190911b82018301908381019087831115611e6d57600080fd5b928401925b82841015611d9457833582529284019290840190611e72565b602080825282518282018190526000919060409081850190868401855b82811015611ed75781518051855286810151878601528501518585015260609093019290850190600101611ea8565b5091979650505050505050565b600060208083528351808285015260005b81811015611f1157858101830151858201604001528201611ef5565b5060006040828601015260407fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8301168501019250505092915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b8082018082111561074057610740611f7f565b8181038181111561074057610740611f7f565b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff820361200557612005611f7f565b5060010190565b808202811582820484141761074057610740611f7f565b60006020828403121561203557600080fd5b8151610e3581611c06565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b60008261207e5761207e612040565b500490565b60008261209257612092612040565b50069056fea2646970667358221220826d92ed14652aeb23db118117b6fa0a3219d69d198f1ab40585aec1d71f854e64736f6c63430008110033

Block Transaction Gas Used Reward
view all blocks ##produced##

Block Uncle Number Difficulty Gas Used Reward
View All Uncles
Loading...
Loading
Loading...
Loading

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits

A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.