ETH Price: $1,965.24 (-3.83%)
 

Overview

ETH Balance

0 ETH

Eth Value

$0.00

More Info

Private Name Tags

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
0x9f9032fb239473812025-12-05 14:36:4788 days ago1764945407IN
0x04d68ce0...D91E2B75e
0 ETH0.000046761.54384994
0x9f9032fb239473802025-12-05 14:36:3588 days ago1764945395IN
0x04d68ce0...D91E2B75e
0 ETH0.000045811.54463049
Update Registry239463322025-12-05 11:03:3588 days ago1764932615IN
0x04d68ce0...D91E2B75e
0 ETH0.000046421.52369587
Submit Proof239427862025-12-04 23:02:1188 days ago1764889331IN
0x04d68ce0...D91E2B75e
0 ETH0.000463421.5231191
Submit Proof239427852025-12-04 23:01:5988 days ago1764889319IN
0x04d68ce0...D91E2B75e
0 ETH0.000463691.52407217
Submit Proof239427842025-12-04 23:01:4788 days ago1764889307IN
0x04d68ce0...D91E2B75e
0 ETH0.000464031.52506784
Submit Proof239427832025-12-04 23:01:3588 days ago1764889295IN
0x04d68ce0...D91E2B75e
0 ETH0.000464151.52562749
Submit Proof239427822025-12-04 23:01:2388 days ago1764889283IN
0x04d68ce0...D91E2B75e
0 ETH0.000464571.52695749
Submit Proof239427812025-12-04 23:01:1188 days ago1764889271IN
0x04d68ce0...D91E2B75e
0 ETH0.000463921.52498938
Submit Proof239427802025-12-04 23:00:5988 days ago1764889259IN
0x04d68ce0...D91E2B75e
0 ETH0.000464041.52520411
Submit Proof239427792025-12-04 23:00:4788 days ago1764889247IN
0x04d68ce0...D91E2B75e
0 ETH0.000464311.52596671
Submit Proof239427782025-12-04 23:00:3588 days ago1764889235IN
0x04d68ce0...D91E2B75e
0 ETH0.000463461.52330104
Submit Proof239427772025-12-04 23:00:2388 days ago1764889223IN
0x04d68ce0...D91E2B75e
0 ETH0.000463691.52419868
Register Asset W...239422542025-12-04 21:14:4789 days ago1764882887IN
0x04d68ce0...D91E2B75e
0 ETH0.004407761.55015633
Register Asset239421982025-12-04 21:03:2389 days ago1764882203IN
0x04d68ce0...D91E2B75e
0 ETH0.000499861.55139161
Register Asset W...239420812025-12-04 20:38:3589 days ago1764880715IN
0x04d68ce0...D91E2B75e
0 ETH0.005032321.54580992
Register Asset W...239420032025-12-04 20:22:4789 days ago1764879767IN
0x04d68ce0...D91E2B75e
0 ETH0.004295121.54788036
Register Asset W...239415682025-12-04 18:53:4789 days ago1764874427IN
0x04d68ce0...D91E2B75e
0 ETH0.004230421.55017447
Register Asset W...239414392025-12-04 18:27:4789 days ago1764872867IN
0x04d68ce0...D91E2B75e
0 ETH0.004428071.54527678
Register Asset W...239412192025-12-04 17:42:3589 days ago1764870155IN
0x04d68ce0...D91E2B75e
0 ETH0.004584791.54962607
Register Asset W...239409432025-12-04 16:45:1189 days ago1764866711IN
0x04d68ce0...D91E2B75e
0 ETH0.004245811.55582756
Register Asset239409132025-12-04 16:38:5989 days ago1764866339IN
0x04d68ce0...D91E2B75e
0 ETH0.000426191.54159613
Register Asset W...239403742025-12-04 14:48:5989 days ago1764859739IN
0x04d68ce0...D91E2B75e
0 ETH0.004332451.54880539
Register Field S...239403732025-12-04 14:48:4789 days ago1764859727IN
0x04d68ce0...D91E2B75e
0 ETH0.000394961.54646745
Register Field S...239403712025-12-04 14:48:2389 days ago1764859703IN
0x04d68ce0...D91E2B75e
0 ETH0.000428041.55203041
View all transactions

View more zero value Internal Transactions in Advanced View mode

Advanced mode:
Loading...
Loading
Loading...
Loading
Cross-Chain Transactions

Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits
Loading...
Loading

Similar Match Source Code
This contract matches the deployed Bytecode of the Source Code for Contract 0x09F6654b...7F9E8bE7F
The constructor portion of the code might be different and could alter the actual behaviour of the contract

Contract Name:
QXMPDynamicProofOfReserve

Compiler Version
v0.8.17+commit.8df45f5f

Optimization Enabled:
Yes with 200 runs

Other Settings:
default evmVersion

Contract Source Code (Solidity Standard Json-Input format)

// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.17;

import "@redstone-finance/evm-connector/contracts/data-services/MainDemoConsumerBase.sol";
import "./QXMPDynamicRegistry.sol";

/**
 * @title QXMPDynamicProofOfReserve
 * @notice Oracle-verified proof-of-reserves for dynamic assets with RedStone integration
 * @dev Integrates with QXMPDynamicRegistry and RedStone oracle for value verification
 * 
 * Key Features:
 * - RedStone MainDemoConsumerBase integration
 * - QXMP oracle signer authorization (0xaD00eb5dC02E56d628d68AbD144B8c223A6Cf1Ef)
 * - Dynamic asset registration (ANY asset type)
 * - Oracle-verified proof submissions
 * - Proof history tracking
 * - Automatic registry value updates
 * 
 * Oracle Flow:
 * 1. Asset registered in registry
 * 2. Backend wraps submitProof() with RedStone WrapperBuilder
 * 3. Oracle data injected into transaction calldata
 * 4. Contract verifies signature and extracts value
 * 5. Proof stored and registry updated
 */
contract QXMPDynamicProofOfReserve is MainDemoConsumerBase {
    
    // ═══════════════════════════════════════════════════════════════════════
    // STATE VARIABLES
    // ═══════════════════════════════════════════════════════════════════════
    
    QXMPDynamicRegistry public registry;
    address public owner;
    
    // QXMP Oracle signer address (deployer wallet)
    // This wallet signs all oracle data
    address public constant QXMP_ORACLE_SIGNER = 0xaD00eb5dC02E56d628d68AbD144B8c223A6Cf1Ef;
    
    // ═══════════════════════════════════════════════════════════════════════
    // STRUCTS
    // ═══════════════════════════════════════════════════════════════════════
    
    /**
     * @dev Proof-of-reserve record
     * Each proof represents an oracle-verified asset valuation
     */
    struct Proof {
        bytes32 assetCode;          // Asset identifier
        uint256 value;              // Oracle-verified USD value (18 decimals)
        uint256 timestamp;          // Block timestamp when proof was submitted
        bytes32 dataFeedId;         // RedStone data feed ID (e.g., QXMP:GOLDMINE-NI43-ZA)
        bool verified;              // Always true (only verified proofs are stored)
    }
    
    // ═══════════════════════════════════════════════════════════════════════
    // STORAGE
    // ═══════════════════════════════════════════════════════════════════════
    
    // All proofs for an asset (complete history)
    mapping(bytes32 => Proof[]) public proofs;
    
    // Latest proof for an asset (for quick access)
    mapping(bytes32 => Proof) public latestProofs;
    
    // ═══════════════════════════════════════════════════════════════════════
    // EVENTS
    // ═══════════════════════════════════════════════════════════════════════
    
    event AssetRegisteredWithRegistry(
        bytes32 indexed assetCode,
        string assetName,
        string assetType,
        uint256 primaryValueUsd
    );
    
    event ProofSubmitted(
        bytes32 indexed assetCode,
        uint256 value,
        uint256 timestamp,
        bytes32 dataFeedId
    );
    
    event RegistryUpdated(address indexed oldRegistry, address indexed newRegistry);
    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
    
    // ═══════════════════════════════════════════════════════════════════════
    // MODIFIERS
    // ═══════════════════════════════════════════════════════════════════════
    
    modifier onlyOwner() {
        require(msg.sender == owner, "Only owner can call this function");
        _;
    }
    
    // ═══════════════════════════════════════════════════════════════════════
    // CONSTRUCTOR
    // ═══════════════════════════════════════════════════════════════════════
    
    /**
     * @param _registryAddress Address of QXMPDynamicRegistry contract
     * @dev CRITICAL: After deployment, Registry ownership MUST be transferred to this contract
     *      Otherwise all registry function calls will revert.
     *      Deployment sequence:
     *      1. Deploy Registry
     *      2. Deploy ProofOfReserve(registryAddress)
     *      3. Call registry.transferOwnership(proofOfReserveAddress)
     */
    constructor(address _registryAddress) {
        require(_registryAddress != address(0), "Invalid registry address");
        registry = QXMPDynamicRegistry(_registryAddress);
        owner = msg.sender;
    }
    
    // ═══════════════════════════════════════════════════════════════════════
    // REDSTONE ORACLE AUTHORIZATION
    // ═══════════════════════════════════════════════════════════════════════
    
    /**
     * @notice Override to authorize QXMP oracle signer
     * @dev RedStone calls this to verify signer is authorized
     * @param signerAddress Address to check authorization for
     * @return uint8 Index of the authorized signer (0 for QXMP oracle)
     */
    function getAuthorisedSignerIndex(
        address signerAddress
    ) public view virtual override returns (uint8) {
        if (signerAddress == QXMP_ORACLE_SIGNER) {
            return 0; // First (and only) authorized signer
        }
        revert("Signer not authorized");
    }
    
    /**
     * @notice Override to require only 1 signer (QXMP oracle)
     * @dev RedStone calls this to determine how many signers are required
     * @return uint8 Number of unique signers required (1 for QXMP)
     */
    function getUniqueSignersThreshold() public view virtual override returns (uint8) {
        return 1;
    }
    
    // ═══════════════════════════════════════════════════════════════════════
    // ASSET REGISTRATION
    // ═══════════════════════════════════════════════════════════════════════
    
    /**
     * @notice Register asset in dynamic registry
     * @dev Called by owner after AI extraction and admin approval
     * @param assetCode Unique asset identifier (keccak256 of human-readable code)
     * @param assetName Human-readable asset name
     * @param assetType Dynamic type (e.g., "Gold Mine", "Land Parcel", "Fine Art")
     * @param jurisdiction Location/country code
     * @param primaryValueUsd Main USD value (18 decimals)
     * @param reportHash SHA-256 hash of source PDF
     * @param holder Asset holder wallet address
     * @param ipfsCid IPFS CID for full metadata backup
     */
    function registerAsset(
        bytes32 assetCode,
        string memory assetName,
        string memory assetType,
        string memory jurisdiction,
        uint256 primaryValueUsd,
        bytes32 reportHash,
        address holder,
        string memory ipfsCid
    ) external onlyOwner {
        // Forward to dynamic registry
        registry.registerAssetCore(
            assetCode,
            assetName,
            assetType,
            jurisdiction,
            primaryValueUsd,
            reportHash,
            holder,
            ipfsCid
        );
        
        emit AssetRegisteredWithRegistry(assetCode, assetName, assetType, primaryValueUsd);
    }
    
    /**
     * @notice Register asset with fields in single transaction
     * @dev Convenience function for registering asset + fields together
     */
    function registerAssetWithFields(
        bytes32 assetCode,
        string memory assetName,
        string memory assetType,
        string memory jurisdiction,
        uint256 primaryValueUsd,
        bytes32 reportHash,
        address holder,
        string memory ipfsCid,
        bytes32[] memory schemaIds,
        bytes[] memory values,
        string[] memory displayValues,
        uint8[] memory confidences,
        bytes32[] memory sourceHashes
    ) external onlyOwner {
        // Register core asset
        registry.registerAssetCore(
            assetCode,
            assetName,
            assetType,
            jurisdiction,
            primaryValueUsd,
            reportHash,
            holder,
            ipfsCid
        );
        
        // Add fields in batch
        registry.addFields(
            assetCode,
            schemaIds,
            values,
            displayValues,
            confidences,
            sourceHashes
        );
        
        emit AssetRegisteredWithRegistry(assetCode, assetName, assetType, primaryValueUsd);
    }
    
    // ═══════════════════════════════════════════════════════════════════════
    // REDSTONE PROOF SUBMISSION
    // ═══════════════════════════════════════════════════════════════════════
    
    /**
     * @notice Submit proof-of-reserve with RedStone oracle data
     * @dev Backend wraps this call with WrapperBuilder to inject oracle data
     * 
     * Flow:
     * 1. Backend calls: WrapperBuilder.wrap(contract).submitProof(...)
     * 2. RedStone injects signed oracle data into transaction calldata
     * 3. Contract reads oracle value with getOracleNumericValueFromTxMsg()
     * 4. RedStone verifies signature matches QXMP_ORACLE_SIGNER
     * 5. Proof stored and registry updated
     * 
     * @param assetCode Asset identifier
     * @param dataFeedId RedStone feed ID (format: QXMP:ASSET-STANDARD-JURISDICTION)
     */
    function submitProof(bytes32 assetCode, bytes32 dataFeedId) external onlyOwner {
        // Read oracle value from transaction calldata
        // RedStone wrapper automatically injects signed data
        // This function verifies the signature and extracts the value
        uint256 oracleValue = getOracleNumericValueFromTxMsg(dataFeedId);
        
        require(oracleValue > 0, "Invalid oracle value");
        
        // Create proof record
        Proof memory proof = Proof({
            assetCode: assetCode,
            value: oracleValue,
            timestamp: block.timestamp,
            dataFeedId: dataFeedId,
            verified: true
        });
        
        // Store in history
        proofs[assetCode].push(proof);
        
        // Update latest proof
        latestProofs[assetCode] = proof;
        
        // Update registry with oracle-verified value
        registry.updatePrimaryValue(assetCode, oracleValue);
        
        emit ProofSubmitted(assetCode, oracleValue, block.timestamp, dataFeedId);
    }
    
    // ═══════════════════════════════════════════════════════════════════════
    // PROOF QUERIES
    // ═══════════════════════════════════════════════════════════════════════
    
    /**
     * @notice Get latest proof for an asset
     * @param assetCode Asset identifier
     * @return Proof struct with value, timestamp, and dataFeedId
     */
    function getLatestProof(bytes32 assetCode) external view returns (Proof memory) {
        return latestProofs[assetCode];
    }
    
    /**
     * @notice Get all proofs for an asset (complete history)
     * @param assetCode Asset identifier
     * @return Proof[] array of all proofs
     */
    function getProofs(bytes32 assetCode) external view returns (Proof[] memory) {
        return proofs[assetCode];
    }
    
    /**
     * @notice Get proof count for an asset
     * @param assetCode Asset identifier
     * @return uint256 Number of proofs
     */
    function getProofCount(bytes32 assetCode) external view returns (uint256) {
        return proofs[assetCode].length;
    }
    
    /**
     * @notice Get specific proof by index
     * @param assetCode Asset identifier
     * @param index Proof index in history
     * @return Proof struct
     */
    function getProofByIndex(bytes32 assetCode, uint256 index) 
        external 
        view 
        returns (Proof memory) 
    {
        require(index < proofs[assetCode].length, "Index out of bounds");
        return proofs[assetCode][index];
    }
    
    /**
     * @notice Check if asset has any proofs
     * @param assetCode Asset identifier
     * @return bool True if asset has at least one proof
     */
    function hasProof(bytes32 assetCode) external view returns (bool) {
        return proofs[assetCode].length > 0;
    }
    
    // ═══════════════════════════════════════════════════════════════════════
    // ORACLE VALUE READING (FOR TESTING)
    // ═══════════════════════════════════════════════════════════════════════
    
    /**
     * @notice Get oracle value for a specific data feed
     * @dev This is a view function that reads from RedStone oracle data in tx
     * @dev For testing purposes only - actual proof submission uses submitProof()
     * @param dataFeedId RedStone data feed identifier
     * @return uint256 Oracle value
     */
    function getOracleValue(bytes32 dataFeedId) external view returns (uint256) {
        return getOracleNumericValueFromTxMsg(dataFeedId);
    }
    
    // ═══════════════════════════════════════════════════════════════════════
    // ADMIN FUNCTIONS
    // ═══════════════════════════════════════════════════════════════════════
    
    /**
     * @notice Update registry contract address
     * @param newRegistry New registry contract address
     */
    function updateRegistry(address newRegistry) external onlyOwner {
        require(newRegistry != address(0), "Invalid registry address");
        address oldRegistry = address(registry);
        registry = QXMPDynamicRegistry(newRegistry);
        emit RegistryUpdated(oldRegistry, newRegistry);
    }
    
    /**
     * @notice Transfer ownership to a new address
     * @param newOwner New owner address
     */
    function transferOwnership(address newOwner) external onlyOwner {
        require(newOwner != address(0), "Invalid new owner address");
        address previousOwner = owner;
        owner = newOwner;
        emit OwnershipTransferred(previousOwner, newOwner);
    }
    
    // ═══════════════════════════════════════════════════════════════════════
    // FIELD SCHEMA MANAGEMENT (Convenience wrappers)
    // ═══════════════════════════════════════════════════════════════════════
    
    /**
     * @notice Register field schema in registry
     * @dev Convenience wrapper for registry.registerFieldSchema()
     */
    function registerFieldSchema(
        string memory fieldName,
        QXMPDynamicRegistry.DataType dataType,
        string memory unit,
        string memory category,
        string memory description,
        bool isNumeric
    ) external onlyOwner returns (bytes32 schemaId) {
        return registry.registerFieldSchema(
            fieldName,
            dataType,
            unit,
            category,
            description,
            isNumeric
        );
    }
    
    /**
     * @notice Add field to asset
     * @dev Convenience wrapper for registry.addField()
     */
    function addField(
        bytes32 assetCode,
        bytes32 schemaId,
        bytes memory value,
        string memory displayValue,
        uint8 confidence,
        bytes32 sourceHash
    ) external onlyOwner {
        registry.addField(
            assetCode,
            schemaId,
            value,
            displayValue,
            confidence,
            sourceHash
        );
    }
    
    /**
     * @notice Add multiple fields to asset
     * @dev Convenience wrapper for registry.addFields()
     */
    function addFields(
        bytes32 assetCode,
        bytes32[] memory schemaIds,
        bytes[] memory values,
        string[] memory displayValues,
        uint8[] memory confidences,
        bytes32[] memory sourceHashes
    ) external onlyOwner {
        registry.addFields(
            assetCode,
            schemaIds,
            values,
            displayValues,
            confidences,
            sourceHashes
        );
    }
}

// SPDX-License-Identifier: BUSL-1.1

pragma solidity ^0.8.4;

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 11 : RedstoneConstants.sol
// SPDX-License-Identifier: BUSL-1.1

pragma solidity ^0.8.4;

/**
 * @title The base contract with helpful constants
 * @author The Redstone Oracles team
 * @dev It mainly contains redstone-related values, which improve readability
 * of other contracts (e.g. CalldataExtractor and RedstoneConsumerBase)
 */
contract RedstoneConstants {
  // === Abbreviations ===
  // BS - Bytes size
  // PTR - Pointer (memory location)
  // SIG - Signature

  // Solidity and YUL constants
  uint256 internal constant STANDARD_SLOT_BS = 32;
  uint256 internal constant FREE_MEMORY_PTR = 0x40;
  uint256 internal constant BYTES_ARR_LEN_VAR_BS = 32;
  uint256 internal constant FUNCTION_SIGNATURE_BS = 4;
  uint256 internal constant REVERT_MSG_OFFSET = 68; // Revert message structure described here: https://ethereum.stackexchange.com/a/66173/106364
  uint256 internal constant STRING_ERR_MESSAGE_MASK = 0x08c379a000000000000000000000000000000000000000000000000000000000;

  // RedStone protocol consts
  uint256 internal constant SIG_BS = 65;
  uint256 internal constant TIMESTAMP_BS = 6;
  uint256 internal constant DATA_PACKAGES_COUNT_BS = 2;
  uint256 internal constant DATA_POINTS_COUNT_BS = 3;
  uint256 internal constant DATA_POINT_VALUE_BYTE_SIZE_BS = 4;
  uint256 internal constant DATA_POINT_SYMBOL_BS = 32;
  uint256 internal constant DEFAULT_DATA_POINT_VALUE_BS = 32;
  uint256 internal constant UNSIGNED_METADATA_BYTE_SIZE_BS = 3;
  uint256 internal constant REDSTONE_MARKER_BS = 9; // byte size of 0x000002ed57011e0000
  uint256 internal constant REDSTONE_MARKER_MASK = 0x0000000000000000000000000000000000000000000000000002ed57011e0000;

  // Derived values (based on consts)
  uint256 internal constant TIMESTAMP_NEGATIVE_OFFSET_IN_DATA_PACKAGE_WITH_STANDARD_SLOT_BS = 104; // SIG_BS + DATA_POINTS_COUNT_BS + DATA_POINT_VALUE_BYTE_SIZE_BS + STANDARD_SLOT_BS
  uint256 internal constant DATA_PACKAGE_WITHOUT_DATA_POINTS_BS = 78; // DATA_POINT_VALUE_BYTE_SIZE_BS + TIMESTAMP_BS + DATA_POINTS_COUNT_BS + SIG_BS
  uint256 internal constant DATA_PACKAGE_WITHOUT_DATA_POINTS_AND_SIG_BS = 13; // DATA_POINT_VALUE_BYTE_SIZE_BS + TIMESTAMP_BS + DATA_POINTS_COUNT_BS
  uint256 internal constant REDSTONE_MARKER_BS_PLUS_STANDARD_SLOT_BS = 41; // REDSTONE_MARKER_BS + STANDARD_SLOT_BS

  // Error messages
  error CalldataOverOrUnderFlow();
  error IncorrectUnsignedMetadataSize();
  error InsufficientNumberOfUniqueSigners(uint256 receivedSignersCount, uint256 requiredSignersCount);
  error EachSignerMustProvideTheSameValue();
  error EmptyCalldataPointersArr();
  error InvalidCalldataPointer();
  error CalldataMustHaveValidPayload();
  error SignerNotAuthorised(address receivedSigner);
  error DataTimestampCannotBeZero();
  error TimestampsMustBeEqual();
}

// SPDX-License-Identifier: BUSL-1.1

pragma solidity ^0.8.4;

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

  /**
   * @dev This function may be overridden by the child consumer contract.
   * It should aggregate values from different signers to a single uint value.
   * By default, it calculates the median value
   * @param values An array of uint256 values from different signers
   * @return Result of the aggregation in the form of a single number
   */
  function aggregateValues(uint256[] memory values) public view virtual returns (uint256) {
    return RedstoneDefaultsLib.aggregateValues(values);
  }

  /* ========== FUNCTIONS WITH IMPLEMENTATION (CAN NOT BE OVERRIDDEN) ========== */

  /**
   * @dev This is an internal helpful function for secure extraction oracle values
   * from the tx calldata. Security is achieved by signatures verification, timestamp
   * validation, and aggregating values from different authorised signers into a
   * single numeric value. If any of the required conditions (e.g. 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 && dataPackagesTimestamp != 0) {
        revert TimestampsMustBeEqual();
      }

      dataPackagesTimestamp = dataPackageTimestamp;

      // Shifting memory pointer back to the "safe" value
      assembly {
        mstore(FREE_MEMORY_PTR, freeMemPtr)
      }
      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;
  }
}

// SPDX-License-Identifier: BUSL-1.1

pragma solidity ^0.8.4;

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;
  }
}

// SPDX-License-Identifier: BUSL-1.1

pragma solidity ^0.8.4;

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);
  }
}

// SPDX-License-Identifier: BUSL-1.1

pragma solidity ^0.8.4;

import "../core/RedstoneConsumerNumericBase.sol";

contract MainDemoConsumerBase is RedstoneConsumerNumericBase {
  function getDataServiceId() public view virtual override returns (string memory) {
    return "redstone-main-demo";
  }

  function getUniqueSignersThreshold() public view virtual override returns (uint8) {
    return 1;
  }

  function getAuthorisedSignerIndex(address signerAddress)
    public
    view
    virtual
    override
    returns (uint8)
  {
    if (signerAddress == 0x0C39486f770B26F5527BBBf942726537986Cd7eb) {
      return 0;
    } else {
      revert SignerNotAuthorised(signerAddress);
    }
  }
}

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.4;

library BitmapLib {
  function setBitInBitmap(uint256 bitmap, uint256 bitIndex) internal pure returns (uint256) {
    return bitmap | (1 << bitIndex);
  }

  function getBitFromBitmap(uint256 bitmap, uint256 bitIndex) internal pure returns (bool) {
    uint256 bitAtIndex = bitmap & (1 << bitIndex);
    return bitAtIndex > 0;
  }
}

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.4;

library NumericArrayLib {
  // This function sort array in memory using bubble sort algorithm,
  // which performs even better than quick sort for small arrays

  uint256 constant BYTES_ARR_LEN_VAR_BS = 32;
  uint256 constant UINT256_VALUE_BS = 32;

  error CanNotPickMedianOfEmptyArray();

  // This function modifies the array
  function pickMedian(uint256[] memory arr) internal pure returns (uint256) {
    if (arr.length == 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)
          }
        }
      }
    }
  }
}

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.4;

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

  function recoverSignerAddress(bytes32 signedHash, uint256 signatureCalldataNegativeOffset)
    internal
    pure
    returns (address)
  {
    bytes32 r;
    bytes32 s;
    uint8 v;
    assembly {
      let signatureCalldataStartPos := sub(calldatasize(), signatureCalldataNegativeOffset)
      r := calldataload(signatureCalldataStartPos)
      signatureCalldataStartPos := add(signatureCalldataStartPos, ECDSA_SIG_R_BS)
      s := calldataload(signatureCalldataStartPos)
      signatureCalldataStartPos := add(signatureCalldataStartPos, ECDSA_SIG_S_BS)
      v := byte(0, calldataload(signatureCalldataStartPos)) // last byte of the signature memory array
    }
    return ecrecover(signedHash, v, r, s);
  }
}

// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.17;

/**
 * @title QXMPDynamicRegistry
 * @notice Universal asset registry with schema-less dynamic fields
 * @dev Supports ANY asset type with ANY number of fields
 * 
 * Key Features:
 * - Global field schema registry (define field types once, reuse forever)
 * - Per-asset dynamic field storage (0 to 100+ fields per asset)
 * - Field-level confidence tracking (AI extraction confidence)
 * - Admin verification system (manual verification flag per field)
 * - IPFS backup integration (full metadata stored off-chain)
 * - Gas optimized (only store fields that exist)
 * 
 * Asset Types Supported: Gold, Diamond, Land, Art, Vehicles, IP, ANY future type
 */
contract QXMPDynamicRegistry {
    
    // ═══════════════════════════════════════════════════════════════════════
    // ENUMS
    // ═══════════════════════════════════════════════════════════════════════
    
    /**
     * @dev Data types for field values
     * Each type determines how the bytes value is encoded/decoded
     */
    enum DataType {
        String,     // 0: Text data (name, description, location)
        Uint256,    // 1: Unsigned integer (quantities, amounts)
        Int256,     // 2: Signed integer (coordinates, deltas)
        Address,    // 3: Ethereum address (holder, certifier)
        Bytes32,    // 4: Hash or identifier
        Bool        // 5: Boolean flag
    }
    
    // ═══════════════════════════════════════════════════════════════════════
    // STRUCTS
    // ═══════════════════════════════════════════════════════════════════════
    
    /**
     * @dev Field schema definition
     * Registered once globally, reused for all assets that have this field
     */
    struct FieldSchema {
        bytes32 schemaId;           // keccak256(fieldName) - unique identifier
        string fieldName;           // Human-readable name (e.g., "Gold Ounces In Situ")
        DataType dataType;          // How to interpret the bytes value
        string unit;                // Unit of measurement ("oz", "m²", "USD", "")
        string category;            // Grouping ("Resource", "Location", "Financial", "Legal")
        string description;         // Human-readable description for UI
        bool isNumeric;             // Can be used in oracle price feeds
        bool isRequired;            // Required for certain asset types (optional enforcement)
        uint256 registeredAt;       // Block timestamp when schema was created
        bool exists;                // Schema exists flag
    }
    
    /**
     * @dev Core asset record (minimal, universal)
     * Same fields for ALL asset types
     */
    struct AssetCore {
        bytes32 assetCode;          // Unique identifier (keccak256 of human-readable code)
        string assetName;           // Human-readable asset name
        string assetType;           // Dynamic type: "Gold Mine", "Land Parcel", "Fine Art", etc.
        string jurisdiction;        // Location / country code
        uint256 primaryValueUsd;    // Main USD value (18 decimals) - oracle verified
        bytes32 reportHash;         // SHA-256 hash of source PDF document
        uint256 registeredAt;       // Block timestamp of registration
        uint256 lastUpdated;        // Last update timestamp (field changes, oracle updates)
        address holder;             // Asset holder wallet address
        bool isActive;              // Active status (can be deactivated)
        string ipfsCid;             // IPFS CID for full metadata backup
    }
    
    /**
     * @dev Dynamic field value with metadata
     * Each asset can have 0 to N of these
     */
    struct AssetField {
        bytes32 schemaId;           // References FieldSchema.schemaId
        bytes value;                // ABI-encoded value (decode based on schema.dataType)
        string displayValue;        // Human-readable string for UI display
        uint8 confidence;           // AI extraction confidence (0-100)
        bool isVerified;            // Admin manual verification flag
        uint256 updatedAt;          // Last update timestamp
        bytes32 sourceHash;         // Hash of PDF page/section where extracted (optional)
    }
    
    // ═══════════════════════════════════════════════════════════════════════
    // STATE VARIABLES
    // ═══════════════════════════════════════════════════════════════════════
    
    // Owner address (QXMP Labs or ProofOfReserve contract)
    address public owner;
    
    // Global field schema registry
    mapping(bytes32 => FieldSchema) public fieldSchemas;
    bytes32[] public fieldSchemaIds;
    
    // Asset core data
    mapping(bytes32 => AssetCore) public assets;
    bytes32[] public assetCodes;
    
    // Asset fields (assetCode => array of fields)
    mapping(bytes32 => AssetField[]) private assetFields;
    
    // Field index for O(1) lookups (assetCode => schemaId => index in array)
    mapping(bytes32 => mapping(bytes32 => uint256)) private fieldIndex;
    mapping(bytes32 => mapping(bytes32 => bool)) private hasField;
    
    // ═══════════════════════════════════════════════════════════════════════
    // EVENTS
    // ═══════════════════════════════════════════════════════════════════════
    
    event FieldSchemaRegistered(
        bytes32 indexed schemaId,
        string fieldName,
        DataType dataType,
        string category
    );
    
    event AssetRegistered(
        bytes32 indexed assetCode,
        string assetName,
        string assetType,
        uint256 primaryValueUsd,
        uint256 fieldCount
    );
    
    event FieldAdded(
        bytes32 indexed assetCode,
        bytes32 indexed schemaId,
        string displayValue
    );
    
    event FieldUpdated(
        bytes32 indexed assetCode,
        bytes32 indexed schemaId,
        string newDisplayValue
    );
    
    event FieldVerified(
        bytes32 indexed assetCode,
        bytes32 indexed schemaId,
        address verifier
    );
    
    event PrimaryValueUpdated(
        bytes32 indexed assetCode,
        uint256 oldValue,
        uint256 newValue
    );
    
    event AssetDeactivated(bytes32 indexed assetCode);
    event AssetReactivated(bytes32 indexed assetCode);
    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
    
    // ═══════════════════════════════════════════════════════════════════════
    // MODIFIERS
    // ═══════════════════════════════════════════════════════════════════════
    
    modifier onlyOwner() {
        require(msg.sender == owner, "Only owner can call this function");
        _;
    }
    
    modifier assetExists(bytes32 assetCode) {
        require(assets[assetCode].isActive, "Asset does not exist or is inactive");
        _;
    }
    
    modifier schemaExists(bytes32 schemaId) {
        require(fieldSchemas[schemaId].exists, "Field schema does not exist");
        _;
    }
    
    // ═══════════════════════════════════════════════════════════════════════
    // CONSTRUCTOR
    // ═══════════════════════════════════════════════════════════════════════
    
    constructor() {
        owner = msg.sender;
    }
    
    // ═══════════════════════════════════════════════════════════════════════
    // FIELD SCHEMA MANAGEMENT
    // ═══════════════════════════════════════════════════════════════════════
    
    /**
     * @notice Register a new field schema
     * @dev Can be called by owner or automatically when new field type is discovered
     * @param fieldName Human-readable field name
     * @param dataType Data type enum value
     * @param unit Unit of measurement (empty string if none)
     * @param category Grouping category
     * @param description Human-readable description
     * @param isNumeric Whether field can be used in oracle feeds
     * @return schemaId Generated schema identifier
     */
    function registerFieldSchema(
        string memory fieldName,
        DataType dataType,
        string memory unit,
        string memory category,
        string memory description,
        bool isNumeric
    ) external onlyOwner returns (bytes32 schemaId) {
        // Generate schema ID from field name
        schemaId = keccak256(abi.encodePacked(fieldName));
        
        require(!fieldSchemas[schemaId].exists, "Field schema already exists");
        require(bytes(fieldName).length > 0, "Field name cannot be empty");
        
        fieldSchemas[schemaId] = FieldSchema({
            schemaId: schemaId,
            fieldName: fieldName,
            dataType: dataType,
            unit: unit,
            category: category,
            description: description,
            isNumeric: isNumeric,
            isRequired: false,
            registeredAt: block.timestamp,
            exists: true
        });
        
        fieldSchemaIds.push(schemaId);
        
        emit FieldSchemaRegistered(schemaId, fieldName, dataType, category);
        
        return schemaId;
    }
    
    /**
     * @notice Get field schema by ID
     */
    function getFieldSchema(bytes32 schemaId) 
        external 
        view 
        schemaExists(schemaId) 
        returns (FieldSchema memory) 
    {
        return fieldSchemas[schemaId];
    }
    
    /**
     * @notice Get total number of registered field schemas
     */
    function getFieldSchemaCount() external view returns (uint256) {
        return fieldSchemaIds.length;
    }
    
    /**
     * @notice Check if field schema exists
     */
    function fieldSchemaExists(bytes32 schemaId) external view returns (bool) {
        return fieldSchemas[schemaId].exists;
    }
    
    // ═══════════════════════════════════════════════════════════════════════
    // ASSET CORE MANAGEMENT
    // ═══════════════════════════════════════════════════════════════════════
    
    /**
     * @notice Register asset core data (called by ProofOfReserve contract)
     * @dev Fields are added separately with addField() or in batch with addFields()
     */
    function registerAssetCore(
        bytes32 assetCode,
        string memory assetName,
        string memory assetType,
        string memory jurisdiction,
        uint256 primaryValueUsd,
        bytes32 reportHash,
        address holder,
        string memory ipfsCid
    ) external onlyOwner {
        require(assets[assetCode].registeredAt == 0, "Asset code already used");
        require(primaryValueUsd > 0, "Primary value must be greater than 0");
        require(holder != address(0), "Invalid holder address");
        require(bytes(assetName).length > 0, "Asset name cannot be empty");
        
        assets[assetCode] = AssetCore({
            assetCode: assetCode,
            assetName: assetName,
            assetType: assetType,
            jurisdiction: jurisdiction,
            primaryValueUsd: primaryValueUsd,
            reportHash: reportHash,
            registeredAt: block.timestamp,
            lastUpdated: block.timestamp,
            holder: holder,
            isActive: true,
            ipfsCid: ipfsCid
        });
        
        assetCodes.push(assetCode);
        
        emit AssetRegistered(assetCode, assetName, assetType, primaryValueUsd, 0);
    }
    
    /**
     * @notice Update primary value (called by ProofOfReserve after oracle verification)
     */
    function updatePrimaryValue(bytes32 assetCode, uint256 newValueUsd) 
        external 
        onlyOwner 
        assetExists(assetCode) 
    {
        require(newValueUsd > 0, "Value must be greater than 0");
        
        uint256 oldValue = assets[assetCode].primaryValueUsd;
        assets[assetCode].primaryValueUsd = newValueUsd;
        assets[assetCode].lastUpdated = block.timestamp;
        
        emit PrimaryValueUpdated(assetCode, oldValue, newValueUsd);
    }
    
    /**
     * @notice Deactivate an asset
     */
    function deactivateAsset(bytes32 assetCode) 
        external 
        onlyOwner 
        assetExists(assetCode) 
    {
        assets[assetCode].isActive = false;
        emit AssetDeactivated(assetCode);
    }
    
    /**
     * @notice Reactivate a deactivated asset
     */
    function reactivateAsset(bytes32 assetCode) external onlyOwner {
        require(assets[assetCode].registeredAt > 0, "Asset never existed");
        require(!assets[assetCode].isActive, "Asset already active");
        assets[assetCode].isActive = true;
        assets[assetCode].lastUpdated = block.timestamp;
        emit AssetReactivated(assetCode);
    }
    
    /**
     * @notice Get asset core data
     */
    function getAsset(bytes32 assetCode) 
        external 
        view 
        assetExists(assetCode) 
        returns (AssetCore memory) 
    {
        return assets[assetCode];
    }
    
    /**
     * @notice Get total number of registered assets
     */
    function getAssetCount() external view returns (uint256) {
        return assetCodes.length;
    }
    
    /**
     * @notice Get asset code by index
     */
    function getAssetCodeByIndex(uint256 index) external view returns (bytes32) {
        require(index < assetCodes.length, "Index out of bounds");
        return assetCodes[index];
    }
    
    // ═══════════════════════════════════════════════════════════════════════
    // DYNAMIC FIELD MANAGEMENT
    // ═══════════════════════════════════════════════════════════════════════
    
    /**
     * @notice Add a field to an existing asset
     * @dev Schema must exist before calling this
     */
    function addField(
        bytes32 assetCode,
        bytes32 schemaId,
        bytes memory value,
        string memory displayValue,
        uint8 confidence,
        bytes32 sourceHash
    ) external onlyOwner assetExists(assetCode) schemaExists(schemaId) {
        require(!hasField[assetCode][schemaId], "Field already exists, use updateField");
        require(confidence <= 100, "Confidence must be 0-100");
        
        AssetField memory field = AssetField({
            schemaId: schemaId,
            value: value,
            displayValue: displayValue,
            confidence: confidence,
            isVerified: false,
            updatedAt: block.timestamp,
            sourceHash: sourceHash
        });
        
        assetFields[assetCode].push(field);
        fieldIndex[assetCode][schemaId] = assetFields[assetCode].length - 1;
        hasField[assetCode][schemaId] = true;
        
        assets[assetCode].lastUpdated = block.timestamp;
        
        emit FieldAdded(assetCode, schemaId, displayValue);
    }
    
    /**
     * @notice Add multiple fields in batch (gas efficient)
     */
    function addFields(
        bytes32 assetCode,
        bytes32[] memory schemaIds,
        bytes[] memory values,
        string[] memory displayValues,
        uint8[] memory confidences,
        bytes32[] memory sourceHashes
    ) external onlyOwner assetExists(assetCode) {
        require(schemaIds.length > 0, "Cannot add zero fields");
        require(schemaIds.length <= 50, "Max 50 fields per batch to prevent out-of-gas");
        require(schemaIds.length == values.length, "Array length mismatch");
        require(schemaIds.length == displayValues.length, "Array length mismatch");
        require(schemaIds.length == confidences.length, "Array length mismatch");
        require(schemaIds.length == sourceHashes.length, "Array length mismatch");
        
        for (uint i = 0; i < schemaIds.length; i++) {
            bytes32 schemaId = schemaIds[i];
            
            require(fieldSchemas[schemaId].exists, "Field schema does not exist");
            require(!hasField[assetCode][schemaId], "Field already exists");
            require(confidences[i] <= 100, "Confidence must be 0-100");
            
            AssetField memory field = AssetField({
                schemaId: schemaId,
                value: values[i],
                displayValue: displayValues[i],
                confidence: confidences[i],
                isVerified: false,
                updatedAt: block.timestamp,
                sourceHash: sourceHashes[i]
            });
            
            assetFields[assetCode].push(field);
            fieldIndex[assetCode][schemaId] = assetFields[assetCode].length - 1;
            hasField[assetCode][schemaId] = true;
            
            emit FieldAdded(assetCode, schemaId, displayValues[i]);
        }
        
        assets[assetCode].lastUpdated = block.timestamp;
    }
    
    /**
     * @notice Update an existing field value
     */
    function updateField(
        bytes32 assetCode,
        bytes32 schemaId,
        bytes memory newValue,
        string memory newDisplayValue
    ) external onlyOwner assetExists(assetCode) {
        require(hasField[assetCode][schemaId], "Field does not exist");
        
        uint256 idx = fieldIndex[assetCode][schemaId];
        assetFields[assetCode][idx].value = newValue;
        assetFields[assetCode][idx].displayValue = newDisplayValue;
        assetFields[assetCode][idx].updatedAt = block.timestamp;
        assetFields[assetCode][idx].isVerified = false; // Reset verification
        
        assets[assetCode].lastUpdated = block.timestamp;
        
        emit FieldUpdated(assetCode, schemaId, newDisplayValue);
    }
    
    /**
     * @notice Verify a field (admin approval)
     */
    function verifyField(bytes32 assetCode, bytes32 schemaId) 
        external 
        onlyOwner 
        assetExists(assetCode) 
    {
        require(hasField[assetCode][schemaId], "Field does not exist");
        
        uint256 idx = fieldIndex[assetCode][schemaId];
        assetFields[assetCode][idx].isVerified = true;
        assetFields[assetCode][idx].updatedAt = block.timestamp;
        
        emit FieldVerified(assetCode, schemaId, msg.sender);
    }
    
    /**
     * @notice Get all fields for an asset
     */
    function getAssetFields(bytes32 assetCode) 
        external 
        view 
        assetExists(assetCode) 
        returns (AssetField[] memory) 
    {
        return assetFields[assetCode];
    }
    
    /**
     * @notice Get specific field value
     */
    function getFieldValue(bytes32 assetCode, bytes32 schemaId) 
        external 
        view 
        assetExists(assetCode) 
        returns (
            bytes memory value,
            string memory displayValue,
            uint8 confidence,
            bool isVerified
        ) 
    {
        require(hasField[assetCode][schemaId], "Field does not exist");
        
        uint256 idx = fieldIndex[assetCode][schemaId];
        AssetField memory field = assetFields[assetCode][idx];
        
        return (field.value, field.displayValue, field.confidence, field.isVerified);
    }
    
    /**
     * @notice Get field count for an asset
     */
    function getFieldCount(bytes32 assetCode) 
        external 
        view 
        assetExists(assetCode) 
        returns (uint256) 
    {
        return assetFields[assetCode].length;
    }
    
    /**
     * @notice Check if asset has specific field
     */
    function assetHasField(bytes32 assetCode, bytes32 schemaId) 
        external 
        view 
        returns (bool) 
    {
        return hasField[assetCode][schemaId];
    }
    
    // ═══════════════════════════════════════════════════════════════════════
    // ADMIN FUNCTIONS
    // ═══════════════════════════════════════════════════════════════════════
    
    /**
     * @notice Transfer ownership
     */
    function transferOwnership(address newOwner) external onlyOwner {
        require(newOwner != address(0), "Invalid new owner address");
        address previousOwner = owner;
        owner = newOwner;
        emit OwnershipTransferred(previousOwner, newOwner);
    }
}

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

Contract Security Audit

Contract ABI

API
[{"inputs":[{"internalType":"address","name":"_registryAddress","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"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":[],"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":[],"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":true,"internalType":"bytes32","name":"assetCode","type":"bytes32"},{"indexed":false,"internalType":"string","name":"assetName","type":"string"},{"indexed":false,"internalType":"string","name":"assetType","type":"string"},{"indexed":false,"internalType":"uint256","name":"primaryValueUsd","type":"uint256"}],"name":"AssetRegisteredWithRegistry","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"assetCode","type":"bytes32"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"timestamp","type":"uint256"},{"indexed":false,"internalType":"bytes32","name":"dataFeedId","type":"bytes32"}],"name":"ProofSubmitted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"oldRegistry","type":"address"},{"indexed":true,"internalType":"address","name":"newRegistry","type":"address"}],"name":"RegistryUpdated","type":"event"},{"inputs":[],"name":"QXMP_ORACLE_SIGNER","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"assetCode","type":"bytes32"},{"internalType":"bytes32","name":"schemaId","type":"bytes32"},{"internalType":"bytes","name":"value","type":"bytes"},{"internalType":"string","name":"displayValue","type":"string"},{"internalType":"uint8","name":"confidence","type":"uint8"},{"internalType":"bytes32","name":"sourceHash","type":"bytes32"}],"name":"addField","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"assetCode","type":"bytes32"},{"internalType":"bytes32[]","name":"schemaIds","type":"bytes32[]"},{"internalType":"bytes[]","name":"values","type":"bytes[]"},{"internalType":"string[]","name":"displayValues","type":"string[]"},{"internalType":"uint8[]","name":"confidences","type":"uint8[]"},{"internalType":"bytes32[]","name":"sourceHashes","type":"bytes32[]"}],"name":"addFields","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"values","type":"uint256[]"}],"name":"aggregateValues","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":[],"name":"getDataServiceId","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"assetCode","type":"bytes32"}],"name":"getLatestProof","outputs":[{"components":[{"internalType":"bytes32","name":"assetCode","type":"bytes32"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"uint256","name":"timestamp","type":"uint256"},{"internalType":"bytes32","name":"dataFeedId","type":"bytes32"},{"internalType":"bool","name":"verified","type":"bool"}],"internalType":"struct QXMPDynamicProofOfReserve.Proof","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"dataFeedId","type":"bytes32"}],"name":"getOracleValue","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"assetCode","type":"bytes32"},{"internalType":"uint256","name":"index","type":"uint256"}],"name":"getProofByIndex","outputs":[{"components":[{"internalType":"bytes32","name":"assetCode","type":"bytes32"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"uint256","name":"timestamp","type":"uint256"},{"internalType":"bytes32","name":"dataFeedId","type":"bytes32"},{"internalType":"bool","name":"verified","type":"bool"}],"internalType":"struct QXMPDynamicProofOfReserve.Proof","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"assetCode","type":"bytes32"}],"name":"getProofCount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"assetCode","type":"bytes32"}],"name":"getProofs","outputs":[{"components":[{"internalType":"bytes32","name":"assetCode","type":"bytes32"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"uint256","name":"timestamp","type":"uint256"},{"internalType":"bytes32","name":"dataFeedId","type":"bytes32"},{"internalType":"bool","name":"verified","type":"bool"}],"internalType":"struct QXMPDynamicProofOfReserve.Proof[]","name":"","type":"tuple[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getUniqueSignersThreshold","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"assetCode","type":"bytes32"}],"name":"hasProof","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"latestProofs","outputs":[{"internalType":"bytes32","name":"assetCode","type":"bytes32"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"uint256","name":"timestamp","type":"uint256"},{"internalType":"bytes32","name":"dataFeedId","type":"bytes32"},{"internalType":"bool","name":"verified","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"proofs","outputs":[{"internalType":"bytes32","name":"assetCode","type":"bytes32"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"uint256","name":"timestamp","type":"uint256"},{"internalType":"bytes32","name":"dataFeedId","type":"bytes32"},{"internalType":"bool","name":"verified","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"assetCode","type":"bytes32"},{"internalType":"string","name":"assetName","type":"string"},{"internalType":"string","name":"assetType","type":"string"},{"internalType":"string","name":"jurisdiction","type":"string"},{"internalType":"uint256","name":"primaryValueUsd","type":"uint256"},{"internalType":"bytes32","name":"reportHash","type":"bytes32"},{"internalType":"address","name":"holder","type":"address"},{"internalType":"string","name":"ipfsCid","type":"string"}],"name":"registerAsset","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"assetCode","type":"bytes32"},{"internalType":"string","name":"assetName","type":"string"},{"internalType":"string","name":"assetType","type":"string"},{"internalType":"string","name":"jurisdiction","type":"string"},{"internalType":"uint256","name":"primaryValueUsd","type":"uint256"},{"internalType":"bytes32","name":"reportHash","type":"bytes32"},{"internalType":"address","name":"holder","type":"address"},{"internalType":"string","name":"ipfsCid","type":"string"},{"internalType":"bytes32[]","name":"schemaIds","type":"bytes32[]"},{"internalType":"bytes[]","name":"values","type":"bytes[]"},{"internalType":"string[]","name":"displayValues","type":"string[]"},{"internalType":"uint8[]","name":"confidences","type":"uint8[]"},{"internalType":"bytes32[]","name":"sourceHashes","type":"bytes32[]"}],"name":"registerAssetWithFields","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"fieldName","type":"string"},{"internalType":"enum QXMPDynamicRegistry.DataType","name":"dataType","type":"uint8"},{"internalType":"string","name":"unit","type":"string"},{"internalType":"string","name":"category","type":"string"},{"internalType":"string","name":"description","type":"string"},{"internalType":"bool","name":"isNumeric","type":"bool"}],"name":"registerFieldSchema","outputs":[{"internalType":"bytes32","name":"schemaId","type":"bytes32"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"registry","outputs":[{"internalType":"contract QXMPDynamicRegistry","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"assetCode","type":"bytes32"},{"internalType":"bytes32","name":"dataFeedId","type":"bytes32"}],"name":"submitProof","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newRegistry","type":"address"}],"name":"updateRegistry","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"receivedTimestampMilliseconds","type":"uint256"}],"name":"validateTimestamp","outputs":[],"stateMutability":"view","type":"function"}]

0x6080346100c757601f6124c338819003918201601f19168301916001600160401b038311848410176100cc578084926020946040528339810103126100c757516001600160a01b038116908190036100c75780156100825760018060a01b031990816000541617600055339060015416176001556040516123e090816100e38239f35b60405162461bcd60e51b815260206004820152601860248201527f496e76616c6964207265676973747279206164647265737300000000000000006044820152606490fd5b600080fd5b634e487b7160e01b600052604160045260246000fdfe60806040526004361015610013575b600080fd5b60003560e01c806301d918d71461020757806303f34181146101fe5780631a5da6c8146101f55780632093c15b146101ec578063241d3827146101e3578063265ce501146101da5780633ce142f5146101d15780633facfafb146101c857806355a547d5146101bf5780637b103999146101b65780637fb58537146101ad5780638da5cb5b146101a4578063968e72bd1461019b578063a1c69d0214610192578063b24ebfcc14610189578063c274583a14610180578063cd13efb614610177578063e3d1e6d61461016e578063f2aef13b14610165578063f2fde38b1461015c578063f50b2efe14610153578063f5bcb8d61461014a578063f90c492414610141578063f9cd9080146101385763fd769d631461013057600080fd5b61000e611274565b5061000e6111d3565b5061000e6111b6565b5061000e61118f565b5061000e6110f8565b5061000e611040565b5061000e610fd2565b5061000e610fa3565b5061000e610f73565b5061000e610f1f565b5061000e610e55565b5061000e610de9565b5061000e610c4a565b5061000e610c20565b5061000e610ba3565b5061000e610b07565b5061000e610a25565b5061000e61096f565b5061000e610922565b5061000e6108b4565b5061000e6107df565b5061000e61075f565b5061000e610657565b5061000e6105bc565b5061000e6104a9565b50634e487b7160e01b600052604160045260246000fd5b6001600160401b03811161023a57604052565b610242610210565b604052565b60a081019081106001600160401b0382111761023a57604052565b604081019081106001600160401b0382111761023a57604052565b90601f801991011681019081106001600160401b0382111761023a57604052565b604051906102ab82610247565b565b81601f8201121561000e578035906001600160401b038211610304575b604051926102e2601f8401601f19166020018561027d565b8284526020838301011161000e57816000926020809301838601378301015290565b61030c610210565b6102ca565b60c435906001600160a01b038216820361000e57565b600435906001600160a01b038216820361000e57565b6020906001600160401b038111610356575b60051b0190565b61035e610210565b61034f565b81601f8201121561000e5780359161037a8361033d565b92610388604051948561027d565b808452602092838086019260051b82010192831161000e578301905b8282106103b2575050505090565b813581529083019083016103a4565b9080601f8301121561000e578135906103d98261033d565b926103e7604051948561027d565b828452602092838086019160051b8301019280841161000e57848301915b8483106104155750505050505090565b82356001600160401b03811161000e578691610436848480948901016102ad565b815201920191610405565b81601f8201121561000e578035916104588361033d565b92610466604051948561027d565b808452602092838086019260051b82010192831161000e578301905b828210610490575050505090565b813560ff8116810361000e578152908301908301610482565b503461000e576101a036600319011261000e576001600160401b0360243581811161000e576104dd600491369083016102ad565b60443583811161000e576104f490369084016102ad565b9160643584811161000e5761050c90369083016102ad565b91610515610311565b60e43586811161000e5761052c90369085016102ad565b6101043587811161000e576105449036908601610363565b916101243588811161000e5761055d90369087016103c1565b936101443589811161000e5761057690369088016103c1565b95610164358a811161000e5761058f9036908301610441565b97610184359a8b1161000e576105ab6105ba9b36908401610363565b9960a4359360843593356118a9565b005b503461000e5760c036600319011261000e576001600160401b0360243581811161000e576105ef60049136908301610363565b60443583811161000e5761060690369084016103c1565b60643584811161000e5761061d90369085016103c1565b9060843585811161000e576106359036908601610441565b9260a43595861161000e576106506105ba9636908701610363565b9435612337565b503461000e57602036600319011261000e57610671610327565b6001546001600160a01b0391829161068c90831633146116f5565b169081156106d957600080546001600160a01b031981166001600160a01b03851617909155167f482b97c53e48ffa324a976e2738053e9aff6eee04d8aac63b10e19411d869b82600080a3005b60405162461bcd60e51b815260206004820152601860248201527f496e76616c6964207265676973747279206164647265737300000000000000006044820152606490fd5b50634e487b7160e01b600052603260045260246000fd5b8054821015610752575b6000526005602060002091020190600090565b61075a61071e565b61073f565b503461000e57604036600319011261000e576024356004356000526002602052604060002090815481101561000e5761079791610735565b50805460018201546002830154600384015460049094015460408051948552602085019390935291830152606082019290925260ff9091161515608082015260a090f35b0390f35b503461000e5761010036600319011261000e576001600160401b0360243581811161000e576108129036906004016102ad565b60443582811161000e5761082a9036906004016102ad565b9060643583811161000e576108439036906004016102ad565b9161084c610311565b9160e43594851161000e576108686105ba9536906004016102ad565b9360a435926084359260043561174b565b6102ab9092919260a0810193608080918051845260208101516020850152604081015160408501526060810151606085015201511515910152565b503461000e57602036600319011261000e576108ce61210a565b50600435600052600360205260a06108e96040600020612135565b6109206040518092608080918051845260208101516020850152604081015160408501526060810151606085015201511515910152565bf35b503461000e57602036600319011261000e576020610946610941610327565b611690565b60ff60405191168152f35b60243590600682101561000e57565b60a43590811515820361000e57565b503461000e5760c036600319011261000e576001600160401b0360043581811161000e576109a19036906004016102ad565b906109aa610951565b9160443582811161000e576109c39036906004016102ad565b9260643583811161000e576109dc9036906004016102ad565b60843593841161000e576107db946109fb610a0a9536906004016102ad565b92610a04610960565b94612176565b6040519081529081906020820190565b600091031261000e57565b503461000e57600036600319011261000e57600061ffff610a4c610a476113bd565b611455565b9116908115610af5579190815b818110610a6b57604051838152602090f35b9091610a768461137a565b9065ffffffffffff610a90610a8a876112d4565b3661135a565b3516908115610ae35780610abc5750610ab391610aad919561133e565b916112b8565b92919092610a59565b80959103610ad157610ab391610aad9161133e565b60405163d9d1f46560e01b8152600490fd5b604051630336dc9d60e41b8152600490fd5b604051632154bfcf60e21b8152600490fd5b503461000e57600036600319011261000e576000546040516001600160a01b039091168152602090f35b6020908160408183019282815285518094520193019160005b828110610b58575050505090565b909192938260a082610b976001948951608080918051845260208101516020850152604081015160408501526060810151606085015201511515910152565b01950193929101610b4a565b503461000e5760208060031936011261000e576004356000526002815260406000208054610bd08161033d565b91610bde604051938461027d565b8183526000908152838120938084015b838310610c0357604051806107db8782610b31565b600582600192610c1289612135565b815201960192019194610bee565b503461000e57600036600319011261000e576001546040516001600160a01b039091168152602090f35b503461000e57604036600319011261000e5760043560243590610c7860018060a01b036001541633146116f5565b610c8182611c33565b610c8c811515611b64565b610d25610c9761029e565b838152826020820152426040820152846060820152610cb96080820160019052565b610cd681610cd1866000526002602052604060002090565b611ba7565b610cea846000526003602052604060002090565b60046080918351815560208401516001820155604084015160028201556060840151600382015501910151151560ff80198354169116179055565b60008054909390610d46906001600160a01b03165b6001600160a01b031690565b91823b15610de557604051631136e0db60e21b815260048101859052602481018290527f75353ef654b6c12a8349e2389e2de66319a30d57b06b1ee0863fdd58f7bbac549386908290604490829084905af18015610dd8575b610dbf575b5060408051918252426020830152810191909152606090a280f35b80610dcc610dd292610227565b80610a1a565b38610da4565b610de061186e565b610d9f565b8480fd5b503461000e5760c036600319011261000e576001600160401b0360443581811161000e57610e1b9036906004016102ad565b9060643590811161000e57610e349036906004016102ad565b9060843560ff8116810361000e576105ba9260a43592602435600435612289565b503461000e5760208060031936011261000e576004356001600160401b03811161000e573660238201121561000e57806004013590610e938261033d565b91610ea1604051938461027d565b80835260248484019160051b8301019136831161000e57602401905b828210610ed0576107db610a0a856115af565b81358152908401908401610ebd565b919082519283825260005b848110610f0b575050826000602080949584010152601f8019910116010190565b602081830181015184830182015201610eea565b503461000e57600036600319011261000e576107db604051610f4081610262565b601281527172656473746f6e652d6d61696e2d64656d6f60701b6020820152604051918291602083526020830190610edf565b503461000e57600036600319011261000e57602060405173ad00eb5dc02e56d628d68abd144b8c223a6cf1ef8152f35b503461000e57602036600319011261000e57600435600052600260205260206040600020541515604051908152f35b503461000e57602036600319011261000e576004356000526003602052604060002080546107db60018301549260028101549060ff6004600383015492015416916040519586958693909594919260809360a086019786526020860152604085015260608401521515910152565b503461000e57602036600319011261000e5761105a610327565b600154906001600160a01b03808316916110753384146116f5565b169182156110b3576001600160a01b03191682176001557f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0600080a3005b60405162461bcd60e51b815260206004820152601960248201527f496e76616c6964206e6577206f776e65722061646472657373000000000000006044820152606490fd5b503461000e57602036600319011261000e576103e86004350480421060001461114e57603c611127428361135a565b1161112e57005b60405163b6b0916d60e01b81526004810191909152426024820152604490fd5b60b4814203428111611182575b1161116257005b604051630321d0b560e01b81526004810191909152426024820152604490fd5b61118a6112a1565b61115b565b503461000e57602036600319011261000e5760206111ae600435611c33565b604051908152f35b503461000e57600036600319011261000e57602060405160018152f35b503461000e57604036600319011261000e576004356024356111f361210a565b508160005260026020526040600020548110156112395761122761122d916107db9360005260026020526040600020610735565b50612135565b60405191829182610879565b60405162461bcd60e51b8152602060048201526013602482015272496e646578206f7574206f6620626f756e647360681b6044820152606490fd5b503461000e57602036600319011261000e5760043560005260026020526020604060002054604051908152f35b50634e487b7160e01b600052601160045260246000fd5b60019060001981146112c8570190565b6112d06112a1565b0190565b90606882018092116112e257565b6102ab6112a1565b90600982018092116112e257565b90600282018092116112e257565b90602082018092116112e257565b90600d82018092116112e257565b90604182018092116112e257565b90604e82018092116112e257565b919082018092116112e257565b6000198101919082116112e257565b919082039182116112e257565b818102929181159184041417156112e257565b6113866113979161149a565b60200190816020116113b057611367565b604e81018091116113a55790565b6113ad6112a1565b90565b6113b86112a1565b611367565b6602ed57011dffff1936601f1901356602ed57011e0000160161144357366029116114315761140062ffffff6028193601351660038101809111611424576112ea565b3661140a826112f8565b116114125790565b60405163c30a7bd760e01b8152600490fd5b61142c6112a1565b6112ea565b604051632bcb7bc560e11b8152600490fd5b6040516373bb264f60e11b8152600490fd5b906020820180831161148d575b3610611431576002820191828111611480575b3603601f1901359190565b6114886112a1565b611475565b6114956112a1565b611462565b60418101808211611506575b60618201106114f9575b36039063ffffffff62ffffff6060198401933685116114ec575b60631901938085116114df575b351692351690565b6114e76112a1565b6114d7565b6114f46112a1565b6114ca565b6115016112a1565b6114b0565b61150e6112a1565b6114a6565b6103e8900480421060001461154457603c428203828111611537575b1161112e5750565b61153f6112a1565b61152f565b60b4814203428111611559575b116111625750565b6115616112a1565b611551565b602090805115611574570190565b6112d061071e565b604090805160011015611574570190565b60209181518110156115a2575b60051b010190565b6115aa61071e565b61159a565b600281511461162557805115611613576115c88161163f565b8051600181811c9116611606576113ad916115f9826115f26115ec6116009561134b565b8461158d565b519261158d565b519061133e565b60011c90565b61160f9161158d565b5190565b604051639e198af960e01b8152600490fd5b611600816115f96116386113ad94611566565b519161157c565b8051602082818094019260051b010190805b82811061165e5750505050565b83825b82811061166f575001611651565b82518151808210611685575b5050018490611661565b84528152388061167b565b6001600160a01b031673ad00eb5dc02e56d628d68abd144b8c223a6cf1ef146116f05760405162461bcd60e51b815260206004820152601560248201527414da59db995c881b9bdd08185d5d1a1bdc9a5e9959605a1b6044820152606490fd5b600090565b156116fc57565b60405162461bcd60e51b815260206004820152602160248201527f4f6e6c79206f776e65722063616e2063616c6c20746869732066756e6374696f6044820152603760f91b6064820152608490fd5b9694929093919460018060a01b03611768816001541633146116f5565b6000541696873b1561000e577f710f92a97c7717747dba03755cfebc972b83eefd3c3c821fa005203325108e1b978660008b6117c382968c966117e09b8b6040519b8c9a8b998a98635a0413d360e01b8a5260048a01611801565b03925af180156117f4575b6117e5575b506040519384938461187b565b0390a2565b6117ee90610227565b386117d3565b6117fc61186e565b6117ce565b949693906113ad989693611829611845946118379389526101008060208b0152890190610edf565b908782036040890152610edf565b908582036060870152610edf565b608084019590955260a08301526001600160a01b031660c082015280830360e090910152610edf565b506040513d6000823e3d90fd5b9392916118a490611896604093606088526060880190610edf565b908682036020880152610edf565b930152565b9a949098959c9b929199969793976118cc60018060a01b036001541633146116f5565b60008054909e8f9390916118e8906001600160a01b0316610d3a565b91823b15610de5578e61191a8f958f94938f9489956040519b8c9a8b998a98635a0413d360e01b8a5260048a01611801565b03925af180156119e4575b6119d1575b508a5461193f906001600160a01b0316610d3a565b92833b156119cd57918b8a6119977f710f92a97c7717747dba03755cfebc972b83eefd3c3c821fa005203325108e1b9c9d9e96946117e09a999896604051998a98899788966303f3418160e01b885260048801611ab1565b03925af180156119c0575b6119b357506040519384938461187b565b80610dcc6117ee92610227565b6119c861186e565b6119a2565b8b80fd5b80610dcc6119de92610227565b3861192a565b6119ec61186e565b611925565b90815180825260208080930193019160005b828110611a11575050505090565b835185529381019392810192600101611a03565b90815180825260208092019182818360051b85019501936000915b848310611a505750505050505090565b9091929394958480611a6a83856001950387528a51610edf565b9801930193019194939290611a40565b90815180825260208080930193019160005b828110611a9a575050505090565b835160ff1685529381019392810192600101611a8c565b949296959391908552611ad060209160c08388015260c08701906119f1565b9085820360408701528751908183528083019281808460051b8301019a01936000915b848310611b365750505050505090611b1a86611b2893866113ad9899036060880152611a25565b908482036080860152611a7a565b9160a08184039101526119f1565b90919293949a8480611b546001938f601f1987830301885251610edf565b9d01930193019194939290611af3565b15611b6b57565b60405162461bcd60e51b8152602060048201526014602482015273496e76616c6964206f7261636c652076616c756560601b6044820152606490fd5b8054611bca9168010000000000000000821015611c26575b600182018155610735565b919091611c10576102ab9160046080918351815560208401516001820155604084015160028201556060840151600382015501910151151560ff80198354169116179055565b634e487b7160e01b600052600060045260246000fd5b611c2e610210565b611bbf565b906040918251611c4281610262565b600191828252602082016020368237825115611d6d575b52600091611c678251611d94565b91611c728151611d94565b90611c7d8151611dc6565b9685845b611d35575b509061ffff939291611c99610a476113bd565b95169281519288965b858810611ccd575050505050505050611cc2611cc89161160f9495612038565b91611513565b611566565b611ce1908c848b859d9798999a9b9d611e29565b90938115611d2457808214159081611d1a575b50611d09578585529887019695949392611ca2565b845163265e23a160e11b8152600490fd5b9050151538611cf4565b855163dfb25a7960e01b8152600490fd5b84908351811015611d67578190611d4a611d7a565b611d54828d61158d565b52611d5f818c61158d565b500190611c81565b50611c86565b611d7561071e565b611c59565b60405190611d8782610262565b6001825260203681840137565b90611d9e8261033d565b611dab604051918261027d565b8281528092611dbc601f199161033d565b0190602036910137565b90611dd08261033d565b611ddd604051918261027d565b8281528092611dee601f199161033d565b019060005b828110611dff57505050565b806060602080938501015201611df3565b9060405191818352602082840101604052602083013790565b9091949394611e378661149a565b919094611e55611e5087611e4a86611306565b90611367565b611314565b97611eb7611eb1611eab61094165ffffffffffff611e75610a8a876112d4565b9d6020611e9682611e91610a8a82611e8c8d611322565b61133e565b611e10565b01209d35169c611ea586611322565b906120c0565b60ff1690565b91611330565b9660009687955b828710611ed2575050505050505050509190565b929598611eec84611ef1929d94979a9d9c9396999c61133e565b611306565b611efb8482611fe9565b96909c805b8d51811015611fd3578e611f158f839061158d565b5114611f2357600101611f00565b9095939c9a9896949d50896001939c929a98611f3f838c61158d565b51858a1b91828216158781611fbf575b50611f6b575b50505050505b0196919097969594939297611ebe565b611f8d611f7b86611fb49661158d565b51611f86878c61158d565b519061158d565b52611fa1611f9b858a61158d565b516112b8565b611fab858a61158d565b5217918a61158d565b523887818080611f55565b9050611fcb868b61158d565b511038611f4f565b5094929b999795939c5097955098600190611f5b565b91909136039136831161202b575b823592602182101561201257602001359060200360031b1c90565b6040516360007e2160e11b815260048101839052602490fd5b6120336112a1565b611ff7565b91906120448351611d94565b9060005b84518110156120b95760018061205e838561158d565b511061209157508061207c61207661208c938861158d565b516115af565b612086828661158d565b526112b8565b612048565b61209d6044928461158d565b519060405191632b13aef560e01b835260048301526024820152fd5b5090925050565b60806000916020809436036040519283526040810135851a82840152803560408401520135606082015282805260015afa156120fd575b60005190565b61210561186e565b6120f7565b6040519061211782610247565b60006080838281528260208201528260408201528260608201520152565b9060405161214281610247565b608060ff60048395805485526001810154602086015260028101546040860152600381015460608601520154161515910152565b9493909392919260018060a01b03612193816001541633146116f5565b60005416926121ba60405197633facfafb60e01b895260c060048a015260c4890190610edf565b916006871015612264576122108896879561220187956121f160009860209d60248a015260031995868a83030160448b0152610edf565b9084888303016064890152610edf565b91858303016084860152610edf565b90151560a483015203925af1908115612257575b60009161222f575090565b6113ad915060203d8111612250575b612248818361027d565b81019061227a565b503d61223e565b61225f61186e565b612224565b634e487b7160e01b600052602160045260246000fd5b9081602091031261000e575190565b919294939460018060a01b036122a4816001541633146116f5565b6000541692833b1561000e576122ed9660009660ff6122ff89956040519b8c9a8b998a986350e34e8160e11b8a5260048a0152602489015260c0604489015260c4880190610edf565b86810360031901606488015290610edf565b9216608484015260a483015203925af1801561232a575b61231d5750565b80610dcc6102ab92610227565b61233261186e565b612316565b91929460018060a01b03612350816001541633146116f5565b6000541692833b1561000e57612382600096928793604051998a98899788966303f3418160e01b885260048801611ab1565b03925af1801561239d575b6123945750565b6102ab90610227565b6123a561186e565b61238d56fea264697066735822122037f173854de5d66444f3f5a3bace4adb4a1e0e11cc109d371da4f527933851f564736f6c6343000811003300000000000000000000000012df1496f2c2f57967d21dc7ecc700c28d748ae5

Deployed Bytecode

0x60806040526004361015610013575b600080fd5b60003560e01c806301d918d71461020757806303f34181146101fe5780631a5da6c8146101f55780632093c15b146101ec578063241d3827146101e3578063265ce501146101da5780633ce142f5146101d15780633facfafb146101c857806355a547d5146101bf5780637b103999146101b65780637fb58537146101ad5780638da5cb5b146101a4578063968e72bd1461019b578063a1c69d0214610192578063b24ebfcc14610189578063c274583a14610180578063cd13efb614610177578063e3d1e6d61461016e578063f2aef13b14610165578063f2fde38b1461015c578063f50b2efe14610153578063f5bcb8d61461014a578063f90c492414610141578063f9cd9080146101385763fd769d631461013057600080fd5b61000e611274565b5061000e6111d3565b5061000e6111b6565b5061000e61118f565b5061000e6110f8565b5061000e611040565b5061000e610fd2565b5061000e610fa3565b5061000e610f73565b5061000e610f1f565b5061000e610e55565b5061000e610de9565b5061000e610c4a565b5061000e610c20565b5061000e610ba3565b5061000e610b07565b5061000e610a25565b5061000e61096f565b5061000e610922565b5061000e6108b4565b5061000e6107df565b5061000e61075f565b5061000e610657565b5061000e6105bc565b5061000e6104a9565b50634e487b7160e01b600052604160045260246000fd5b6001600160401b03811161023a57604052565b610242610210565b604052565b60a081019081106001600160401b0382111761023a57604052565b604081019081106001600160401b0382111761023a57604052565b90601f801991011681019081106001600160401b0382111761023a57604052565b604051906102ab82610247565b565b81601f8201121561000e578035906001600160401b038211610304575b604051926102e2601f8401601f19166020018561027d565b8284526020838301011161000e57816000926020809301838601378301015290565b61030c610210565b6102ca565b60c435906001600160a01b038216820361000e57565b600435906001600160a01b038216820361000e57565b6020906001600160401b038111610356575b60051b0190565b61035e610210565b61034f565b81601f8201121561000e5780359161037a8361033d565b92610388604051948561027d565b808452602092838086019260051b82010192831161000e578301905b8282106103b2575050505090565b813581529083019083016103a4565b9080601f8301121561000e578135906103d98261033d565b926103e7604051948561027d565b828452602092838086019160051b8301019280841161000e57848301915b8483106104155750505050505090565b82356001600160401b03811161000e578691610436848480948901016102ad565b815201920191610405565b81601f8201121561000e578035916104588361033d565b92610466604051948561027d565b808452602092838086019260051b82010192831161000e578301905b828210610490575050505090565b813560ff8116810361000e578152908301908301610482565b503461000e576101a036600319011261000e576001600160401b0360243581811161000e576104dd600491369083016102ad565b60443583811161000e576104f490369084016102ad565b9160643584811161000e5761050c90369083016102ad565b91610515610311565b60e43586811161000e5761052c90369085016102ad565b6101043587811161000e576105449036908601610363565b916101243588811161000e5761055d90369087016103c1565b936101443589811161000e5761057690369088016103c1565b95610164358a811161000e5761058f9036908301610441565b97610184359a8b1161000e576105ab6105ba9b36908401610363565b9960a4359360843593356118a9565b005b503461000e5760c036600319011261000e576001600160401b0360243581811161000e576105ef60049136908301610363565b60443583811161000e5761060690369084016103c1565b60643584811161000e5761061d90369085016103c1565b9060843585811161000e576106359036908601610441565b9260a43595861161000e576106506105ba9636908701610363565b9435612337565b503461000e57602036600319011261000e57610671610327565b6001546001600160a01b0391829161068c90831633146116f5565b169081156106d957600080546001600160a01b031981166001600160a01b03851617909155167f482b97c53e48ffa324a976e2738053e9aff6eee04d8aac63b10e19411d869b82600080a3005b60405162461bcd60e51b815260206004820152601860248201527f496e76616c6964207265676973747279206164647265737300000000000000006044820152606490fd5b50634e487b7160e01b600052603260045260246000fd5b8054821015610752575b6000526005602060002091020190600090565b61075a61071e565b61073f565b503461000e57604036600319011261000e576024356004356000526002602052604060002090815481101561000e5761079791610735565b50805460018201546002830154600384015460049094015460408051948552602085019390935291830152606082019290925260ff9091161515608082015260a090f35b0390f35b503461000e5761010036600319011261000e576001600160401b0360243581811161000e576108129036906004016102ad565b60443582811161000e5761082a9036906004016102ad565b9060643583811161000e576108439036906004016102ad565b9161084c610311565b9160e43594851161000e576108686105ba9536906004016102ad565b9360a435926084359260043561174b565b6102ab9092919260a0810193608080918051845260208101516020850152604081015160408501526060810151606085015201511515910152565b503461000e57602036600319011261000e576108ce61210a565b50600435600052600360205260a06108e96040600020612135565b6109206040518092608080918051845260208101516020850152604081015160408501526060810151606085015201511515910152565bf35b503461000e57602036600319011261000e576020610946610941610327565b611690565b60ff60405191168152f35b60243590600682101561000e57565b60a43590811515820361000e57565b503461000e5760c036600319011261000e576001600160401b0360043581811161000e576109a19036906004016102ad565b906109aa610951565b9160443582811161000e576109c39036906004016102ad565b9260643583811161000e576109dc9036906004016102ad565b60843593841161000e576107db946109fb610a0a9536906004016102ad565b92610a04610960565b94612176565b6040519081529081906020820190565b600091031261000e57565b503461000e57600036600319011261000e57600061ffff610a4c610a476113bd565b611455565b9116908115610af5579190815b818110610a6b57604051838152602090f35b9091610a768461137a565b9065ffffffffffff610a90610a8a876112d4565b3661135a565b3516908115610ae35780610abc5750610ab391610aad919561133e565b916112b8565b92919092610a59565b80959103610ad157610ab391610aad9161133e565b60405163d9d1f46560e01b8152600490fd5b604051630336dc9d60e41b8152600490fd5b604051632154bfcf60e21b8152600490fd5b503461000e57600036600319011261000e576000546040516001600160a01b039091168152602090f35b6020908160408183019282815285518094520193019160005b828110610b58575050505090565b909192938260a082610b976001948951608080918051845260208101516020850152604081015160408501526060810151606085015201511515910152565b01950193929101610b4a565b503461000e5760208060031936011261000e576004356000526002815260406000208054610bd08161033d565b91610bde604051938461027d565b8183526000908152838120938084015b838310610c0357604051806107db8782610b31565b600582600192610c1289612135565b815201960192019194610bee565b503461000e57600036600319011261000e576001546040516001600160a01b039091168152602090f35b503461000e57604036600319011261000e5760043560243590610c7860018060a01b036001541633146116f5565b610c8182611c33565b610c8c811515611b64565b610d25610c9761029e565b838152826020820152426040820152846060820152610cb96080820160019052565b610cd681610cd1866000526002602052604060002090565b611ba7565b610cea846000526003602052604060002090565b60046080918351815560208401516001820155604084015160028201556060840151600382015501910151151560ff80198354169116179055565b60008054909390610d46906001600160a01b03165b6001600160a01b031690565b91823b15610de557604051631136e0db60e21b815260048101859052602481018290527f75353ef654b6c12a8349e2389e2de66319a30d57b06b1ee0863fdd58f7bbac549386908290604490829084905af18015610dd8575b610dbf575b5060408051918252426020830152810191909152606090a280f35b80610dcc610dd292610227565b80610a1a565b38610da4565b610de061186e565b610d9f565b8480fd5b503461000e5760c036600319011261000e576001600160401b0360443581811161000e57610e1b9036906004016102ad565b9060643590811161000e57610e349036906004016102ad565b9060843560ff8116810361000e576105ba9260a43592602435600435612289565b503461000e5760208060031936011261000e576004356001600160401b03811161000e573660238201121561000e57806004013590610e938261033d565b91610ea1604051938461027d565b80835260248484019160051b8301019136831161000e57602401905b828210610ed0576107db610a0a856115af565b81358152908401908401610ebd565b919082519283825260005b848110610f0b575050826000602080949584010152601f8019910116010190565b602081830181015184830182015201610eea565b503461000e57600036600319011261000e576107db604051610f4081610262565b601281527172656473746f6e652d6d61696e2d64656d6f60701b6020820152604051918291602083526020830190610edf565b503461000e57600036600319011261000e57602060405173ad00eb5dc02e56d628d68abd144b8c223a6cf1ef8152f35b503461000e57602036600319011261000e57600435600052600260205260206040600020541515604051908152f35b503461000e57602036600319011261000e576004356000526003602052604060002080546107db60018301549260028101549060ff6004600383015492015416916040519586958693909594919260809360a086019786526020860152604085015260608401521515910152565b503461000e57602036600319011261000e5761105a610327565b600154906001600160a01b03808316916110753384146116f5565b169182156110b3576001600160a01b03191682176001557f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0600080a3005b60405162461bcd60e51b815260206004820152601960248201527f496e76616c6964206e6577206f776e65722061646472657373000000000000006044820152606490fd5b503461000e57602036600319011261000e576103e86004350480421060001461114e57603c611127428361135a565b1161112e57005b60405163b6b0916d60e01b81526004810191909152426024820152604490fd5b60b4814203428111611182575b1161116257005b604051630321d0b560e01b81526004810191909152426024820152604490fd5b61118a6112a1565b61115b565b503461000e57602036600319011261000e5760206111ae600435611c33565b604051908152f35b503461000e57600036600319011261000e57602060405160018152f35b503461000e57604036600319011261000e576004356024356111f361210a565b508160005260026020526040600020548110156112395761122761122d916107db9360005260026020526040600020610735565b50612135565b60405191829182610879565b60405162461bcd60e51b8152602060048201526013602482015272496e646578206f7574206f6620626f756e647360681b6044820152606490fd5b503461000e57602036600319011261000e5760043560005260026020526020604060002054604051908152f35b50634e487b7160e01b600052601160045260246000fd5b60019060001981146112c8570190565b6112d06112a1565b0190565b90606882018092116112e257565b6102ab6112a1565b90600982018092116112e257565b90600282018092116112e257565b90602082018092116112e257565b90600d82018092116112e257565b90604182018092116112e257565b90604e82018092116112e257565b919082018092116112e257565b6000198101919082116112e257565b919082039182116112e257565b818102929181159184041417156112e257565b6113866113979161149a565b60200190816020116113b057611367565b604e81018091116113a55790565b6113ad6112a1565b90565b6113b86112a1565b611367565b6602ed57011dffff1936601f1901356602ed57011e0000160161144357366029116114315761140062ffffff6028193601351660038101809111611424576112ea565b3661140a826112f8565b116114125790565b60405163c30a7bd760e01b8152600490fd5b61142c6112a1565b6112ea565b604051632bcb7bc560e11b8152600490fd5b6040516373bb264f60e11b8152600490fd5b906020820180831161148d575b3610611431576002820191828111611480575b3603601f1901359190565b6114886112a1565b611475565b6114956112a1565b611462565b60418101808211611506575b60618201106114f9575b36039063ffffffff62ffffff6060198401933685116114ec575b60631901938085116114df575b351692351690565b6114e76112a1565b6114d7565b6114f46112a1565b6114ca565b6115016112a1565b6114b0565b61150e6112a1565b6114a6565b6103e8900480421060001461154457603c428203828111611537575b1161112e5750565b61153f6112a1565b61152f565b60b4814203428111611559575b116111625750565b6115616112a1565b611551565b602090805115611574570190565b6112d061071e565b604090805160011015611574570190565b60209181518110156115a2575b60051b010190565b6115aa61071e565b61159a565b600281511461162557805115611613576115c88161163f565b8051600181811c9116611606576113ad916115f9826115f26115ec6116009561134b565b8461158d565b519261158d565b519061133e565b60011c90565b61160f9161158d565b5190565b604051639e198af960e01b8152600490fd5b611600816115f96116386113ad94611566565b519161157c565b8051602082818094019260051b010190805b82811061165e5750505050565b83825b82811061166f575001611651565b82518151808210611685575b5050018490611661565b84528152388061167b565b6001600160a01b031673ad00eb5dc02e56d628d68abd144b8c223a6cf1ef146116f05760405162461bcd60e51b815260206004820152601560248201527414da59db995c881b9bdd08185d5d1a1bdc9a5e9959605a1b6044820152606490fd5b600090565b156116fc57565b60405162461bcd60e51b815260206004820152602160248201527f4f6e6c79206f776e65722063616e2063616c6c20746869732066756e6374696f6044820152603760f91b6064820152608490fd5b9694929093919460018060a01b03611768816001541633146116f5565b6000541696873b1561000e577f710f92a97c7717747dba03755cfebc972b83eefd3c3c821fa005203325108e1b978660008b6117c382968c966117e09b8b6040519b8c9a8b998a98635a0413d360e01b8a5260048a01611801565b03925af180156117f4575b6117e5575b506040519384938461187b565b0390a2565b6117ee90610227565b386117d3565b6117fc61186e565b6117ce565b949693906113ad989693611829611845946118379389526101008060208b0152890190610edf565b908782036040890152610edf565b908582036060870152610edf565b608084019590955260a08301526001600160a01b031660c082015280830360e090910152610edf565b506040513d6000823e3d90fd5b9392916118a490611896604093606088526060880190610edf565b908682036020880152610edf565b930152565b9a949098959c9b929199969793976118cc60018060a01b036001541633146116f5565b60008054909e8f9390916118e8906001600160a01b0316610d3a565b91823b15610de5578e61191a8f958f94938f9489956040519b8c9a8b998a98635a0413d360e01b8a5260048a01611801565b03925af180156119e4575b6119d1575b508a5461193f906001600160a01b0316610d3a565b92833b156119cd57918b8a6119977f710f92a97c7717747dba03755cfebc972b83eefd3c3c821fa005203325108e1b9c9d9e96946117e09a999896604051998a98899788966303f3418160e01b885260048801611ab1565b03925af180156119c0575b6119b357506040519384938461187b565b80610dcc6117ee92610227565b6119c861186e565b6119a2565b8b80fd5b80610dcc6119de92610227565b3861192a565b6119ec61186e565b611925565b90815180825260208080930193019160005b828110611a11575050505090565b835185529381019392810192600101611a03565b90815180825260208092019182818360051b85019501936000915b848310611a505750505050505090565b9091929394958480611a6a83856001950387528a51610edf565b9801930193019194939290611a40565b90815180825260208080930193019160005b828110611a9a575050505090565b835160ff1685529381019392810192600101611a8c565b949296959391908552611ad060209160c08388015260c08701906119f1565b9085820360408701528751908183528083019281808460051b8301019a01936000915b848310611b365750505050505090611b1a86611b2893866113ad9899036060880152611a25565b908482036080860152611a7a565b9160a08184039101526119f1565b90919293949a8480611b546001938f601f1987830301885251610edf565b9d01930193019194939290611af3565b15611b6b57565b60405162461bcd60e51b8152602060048201526014602482015273496e76616c6964206f7261636c652076616c756560601b6044820152606490fd5b8054611bca9168010000000000000000821015611c26575b600182018155610735565b919091611c10576102ab9160046080918351815560208401516001820155604084015160028201556060840151600382015501910151151560ff80198354169116179055565b634e487b7160e01b600052600060045260246000fd5b611c2e610210565b611bbf565b906040918251611c4281610262565b600191828252602082016020368237825115611d6d575b52600091611c678251611d94565b91611c728151611d94565b90611c7d8151611dc6565b9685845b611d35575b509061ffff939291611c99610a476113bd565b95169281519288965b858810611ccd575050505050505050611cc2611cc89161160f9495612038565b91611513565b611566565b611ce1908c848b859d9798999a9b9d611e29565b90938115611d2457808214159081611d1a575b50611d09578585529887019695949392611ca2565b845163265e23a160e11b8152600490fd5b9050151538611cf4565b855163dfb25a7960e01b8152600490fd5b84908351811015611d67578190611d4a611d7a565b611d54828d61158d565b52611d5f818c61158d565b500190611c81565b50611c86565b611d7561071e565b611c59565b60405190611d8782610262565b6001825260203681840137565b90611d9e8261033d565b611dab604051918261027d565b8281528092611dbc601f199161033d565b0190602036910137565b90611dd08261033d565b611ddd604051918261027d565b8281528092611dee601f199161033d565b019060005b828110611dff57505050565b806060602080938501015201611df3565b9060405191818352602082840101604052602083013790565b9091949394611e378661149a565b919094611e55611e5087611e4a86611306565b90611367565b611314565b97611eb7611eb1611eab61094165ffffffffffff611e75610a8a876112d4565b9d6020611e9682611e91610a8a82611e8c8d611322565b61133e565b611e10565b01209d35169c611ea586611322565b906120c0565b60ff1690565b91611330565b9660009687955b828710611ed2575050505050505050509190565b929598611eec84611ef1929d94979a9d9c9396999c61133e565b611306565b611efb8482611fe9565b96909c805b8d51811015611fd3578e611f158f839061158d565b5114611f2357600101611f00565b9095939c9a9896949d50896001939c929a98611f3f838c61158d565b51858a1b91828216158781611fbf575b50611f6b575b50505050505b0196919097969594939297611ebe565b611f8d611f7b86611fb49661158d565b51611f86878c61158d565b519061158d565b52611fa1611f9b858a61158d565b516112b8565b611fab858a61158d565b5217918a61158d565b523887818080611f55565b9050611fcb868b61158d565b511038611f4f565b5094929b999795939c5097955098600190611f5b565b91909136039136831161202b575b823592602182101561201257602001359060200360031b1c90565b6040516360007e2160e11b815260048101839052602490fd5b6120336112a1565b611ff7565b91906120448351611d94565b9060005b84518110156120b95760018061205e838561158d565b511061209157508061207c61207661208c938861158d565b516115af565b612086828661158d565b526112b8565b612048565b61209d6044928461158d565b519060405191632b13aef560e01b835260048301526024820152fd5b5090925050565b60806000916020809436036040519283526040810135851a82840152803560408401520135606082015282805260015afa156120fd575b60005190565b61210561186e565b6120f7565b6040519061211782610247565b60006080838281528260208201528260408201528260608201520152565b9060405161214281610247565b608060ff60048395805485526001810154602086015260028101546040860152600381015460608601520154161515910152565b9493909392919260018060a01b03612193816001541633146116f5565b60005416926121ba60405197633facfafb60e01b895260c060048a015260c4890190610edf565b916006871015612264576122108896879561220187956121f160009860209d60248a015260031995868a83030160448b0152610edf565b9084888303016064890152610edf565b91858303016084860152610edf565b90151560a483015203925af1908115612257575b60009161222f575090565b6113ad915060203d8111612250575b612248818361027d565b81019061227a565b503d61223e565b61225f61186e565b612224565b634e487b7160e01b600052602160045260246000fd5b9081602091031261000e575190565b919294939460018060a01b036122a4816001541633146116f5565b6000541692833b1561000e576122ed9660009660ff6122ff89956040519b8c9a8b998a986350e34e8160e11b8a5260048a0152602489015260c0604489015260c4880190610edf565b86810360031901606488015290610edf565b9216608484015260a483015203925af1801561232a575b61231d5750565b80610dcc6102ab92610227565b61233261186e565b612316565b91929460018060a01b03612350816001541633146116f5565b6000541692833b1561000e57612382600096928793604051998a98899788966303f3418160e01b885260048801611ab1565b03925af1801561239d575b6123945750565b6102ab90610227565b6123a561186e565b61238d56fea264697066735822122037f173854de5d66444f3f5a3bace4adb4a1e0e11cc109d371da4f527933851f564736f6c63430008110033

Block Uncle Number Difficulty Gas Used Reward
View All Uncles
Loading...
Loading
Loading...
Loading
Loading...
Loading
[ Download: CSV Export  ]

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.