Source Code
Overview
ETH Balance
0 ETH
Eth Value
$0.00Latest 25 from a total of 35 transactions
| Transaction Hash |
Method
|
Block
|
From
|
|
To
|
||||
|---|---|---|---|---|---|---|---|---|---|
| 0x9f9032fb | 23947381 | 88 days ago | IN | 0 ETH | 0.00004676 | ||||
| 0x9f9032fb | 23947380 | 88 days ago | IN | 0 ETH | 0.00004581 | ||||
| Update Registry | 23946332 | 88 days ago | IN | 0 ETH | 0.00004642 | ||||
| Submit Proof | 23942786 | 88 days ago | IN | 0 ETH | 0.00046342 | ||||
| Submit Proof | 23942785 | 88 days ago | IN | 0 ETH | 0.00046369 | ||||
| Submit Proof | 23942784 | 88 days ago | IN | 0 ETH | 0.00046403 | ||||
| Submit Proof | 23942783 | 88 days ago | IN | 0 ETH | 0.00046415 | ||||
| Submit Proof | 23942782 | 88 days ago | IN | 0 ETH | 0.00046457 | ||||
| Submit Proof | 23942781 | 88 days ago | IN | 0 ETH | 0.00046392 | ||||
| Submit Proof | 23942780 | 88 days ago | IN | 0 ETH | 0.00046404 | ||||
| Submit Proof | 23942779 | 88 days ago | IN | 0 ETH | 0.00046431 | ||||
| Submit Proof | 23942778 | 88 days ago | IN | 0 ETH | 0.00046346 | ||||
| Submit Proof | 23942777 | 88 days ago | IN | 0 ETH | 0.00046369 | ||||
| Register Asset W... | 23942254 | 89 days ago | IN | 0 ETH | 0.00440776 | ||||
| Register Asset | 23942198 | 89 days ago | IN | 0 ETH | 0.00049986 | ||||
| Register Asset W... | 23942081 | 89 days ago | IN | 0 ETH | 0.00503232 | ||||
| Register Asset W... | 23942003 | 89 days ago | IN | 0 ETH | 0.00429512 | ||||
| Register Asset W... | 23941568 | 89 days ago | IN | 0 ETH | 0.00423042 | ||||
| Register Asset W... | 23941439 | 89 days ago | IN | 0 ETH | 0.00442807 | ||||
| Register Asset W... | 23941219 | 89 days ago | IN | 0 ETH | 0.00458479 | ||||
| Register Asset W... | 23940943 | 89 days ago | IN | 0 ETH | 0.00424581 | ||||
| Register Asset | 23940913 | 89 days ago | IN | 0 ETH | 0.00042619 | ||||
| Register Asset W... | 23940374 | 89 days ago | IN | 0 ETH | 0.00433245 | ||||
| Register Field S... | 23940373 | 89 days ago | IN | 0 ETH | 0.00039496 | ||||
| Register Field S... | 23940371 | 89 days ago | IN | 0 ETH | 0.00042804 |
View more zero value Internal Transactions in Advanced View mode
Advanced mode:
Loading...
Loading
Loading...
Loading
Cross-Chain Transactions
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_;
}
}// 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);
}
}{
"optimizer": {
"enabled": true,
"runs": 200
},
"viaIR": true,
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
}
}Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
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"}]Contract Creation Code
0x6080346100c757601f6124c338819003918201601f19168301916001600160401b038311848410176100cc578084926020946040528339810103126100c757516001600160a01b038116908190036100c75780156100825760018060a01b031990816000541617600055339060015416176001556040516123e090816100e38239f35b60405162461bcd60e51b815260206004820152601860248201527f496e76616c6964207265676973747279206164647265737300000000000000006044820152606490fd5b600080fd5b634e487b7160e01b600052604160045260246000fdfe60806040526004361015610013575b600080fd5b60003560e01c806301d918d71461020757806303f34181146101fe5780631a5da6c8146101f55780632093c15b146101ec578063241d3827146101e3578063265ce501146101da5780633ce142f5146101d15780633facfafb146101c857806355a547d5146101bf5780637b103999146101b65780637fb58537146101ad5780638da5cb5b146101a4578063968e72bd1461019b578063a1c69d0214610192578063b24ebfcc14610189578063c274583a14610180578063cd13efb614610177578063e3d1e6d61461016e578063f2aef13b14610165578063f2fde38b1461015c578063f50b2efe14610153578063f5bcb8d61461014a578063f90c492414610141578063f9cd9080146101385763fd769d631461013057600080fd5b61000e611274565b5061000e6111d3565b5061000e6111b6565b5061000e61118f565b5061000e6110f8565b5061000e611040565b5061000e610fd2565b5061000e610fa3565b5061000e610f73565b5061000e610f1f565b5061000e610e55565b5061000e610de9565b5061000e610c4a565b5061000e610c20565b5061000e610ba3565b5061000e610b07565b5061000e610a25565b5061000e61096f565b5061000e610922565b5061000e6108b4565b5061000e6107df565b5061000e61075f565b5061000e610657565b5061000e6105bc565b5061000e6104a9565b50634e487b7160e01b600052604160045260246000fd5b6001600160401b03811161023a57604052565b610242610210565b604052565b60a081019081106001600160401b0382111761023a57604052565b604081019081106001600160401b0382111761023a57604052565b90601f801991011681019081106001600160401b0382111761023a57604052565b604051906102ab82610247565b565b81601f8201121561000e578035906001600160401b038211610304575b604051926102e2601f8401601f19166020018561027d565b8284526020838301011161000e57816000926020809301838601378301015290565b61030c610210565b6102ca565b60c435906001600160a01b038216820361000e57565b600435906001600160a01b038216820361000e57565b6020906001600160401b038111610356575b60051b0190565b61035e610210565b61034f565b81601f8201121561000e5780359161037a8361033d565b92610388604051948561027d565b808452602092838086019260051b82010192831161000e578301905b8282106103b2575050505090565b813581529083019083016103a4565b9080601f8301121561000e578135906103d98261033d565b926103e7604051948561027d565b828452602092838086019160051b8301019280841161000e57848301915b8483106104155750505050505090565b82356001600160401b03811161000e578691610436848480948901016102ad565b815201920191610405565b81601f8201121561000e578035916104588361033d565b92610466604051948561027d565b808452602092838086019260051b82010192831161000e578301905b828210610490575050505090565b813560ff8116810361000e578152908301908301610482565b503461000e576101a036600319011261000e576001600160401b0360243581811161000e576104dd600491369083016102ad565b60443583811161000e576104f490369084016102ad565b9160643584811161000e5761050c90369083016102ad565b91610515610311565b60e43586811161000e5761052c90369085016102ad565b6101043587811161000e576105449036908601610363565b916101243588811161000e5761055d90369087016103c1565b936101443589811161000e5761057690369088016103c1565b95610164358a811161000e5761058f9036908301610441565b97610184359a8b1161000e576105ab6105ba9b36908401610363565b9960a4359360843593356118a9565b005b503461000e5760c036600319011261000e576001600160401b0360243581811161000e576105ef60049136908301610363565b60443583811161000e5761060690369084016103c1565b60643584811161000e5761061d90369085016103c1565b9060843585811161000e576106359036908601610441565b9260a43595861161000e576106506105ba9636908701610363565b9435612337565b503461000e57602036600319011261000e57610671610327565b6001546001600160a01b0391829161068c90831633146116f5565b169081156106d957600080546001600160a01b031981166001600160a01b03851617909155167f482b97c53e48ffa324a976e2738053e9aff6eee04d8aac63b10e19411d869b82600080a3005b60405162461bcd60e51b815260206004820152601860248201527f496e76616c6964207265676973747279206164647265737300000000000000006044820152606490fd5b50634e487b7160e01b600052603260045260246000fd5b8054821015610752575b6000526005602060002091020190600090565b61075a61071e565b61073f565b503461000e57604036600319011261000e576024356004356000526002602052604060002090815481101561000e5761079791610735565b50805460018201546002830154600384015460049094015460408051948552602085019390935291830152606082019290925260ff9091161515608082015260a090f35b0390f35b503461000e5761010036600319011261000e576001600160401b0360243581811161000e576108129036906004016102ad565b60443582811161000e5761082a9036906004016102ad565b9060643583811161000e576108439036906004016102ad565b9161084c610311565b9160e43594851161000e576108686105ba9536906004016102ad565b9360a435926084359260043561174b565b6102ab9092919260a0810193608080918051845260208101516020850152604081015160408501526060810151606085015201511515910152565b503461000e57602036600319011261000e576108ce61210a565b50600435600052600360205260a06108e96040600020612135565b6109206040518092608080918051845260208101516020850152604081015160408501526060810151606085015201511515910152565bf35b503461000e57602036600319011261000e576020610946610941610327565b611690565b60ff60405191168152f35b60243590600682101561000e57565b60a43590811515820361000e57565b503461000e5760c036600319011261000e576001600160401b0360043581811161000e576109a19036906004016102ad565b906109aa610951565b9160443582811161000e576109c39036906004016102ad565b9260643583811161000e576109dc9036906004016102ad565b60843593841161000e576107db946109fb610a0a9536906004016102ad565b92610a04610960565b94612176565b6040519081529081906020820190565b600091031261000e57565b503461000e57600036600319011261000e57600061ffff610a4c610a476113bd565b611455565b9116908115610af5579190815b818110610a6b57604051838152602090f35b9091610a768461137a565b9065ffffffffffff610a90610a8a876112d4565b3661135a565b3516908115610ae35780610abc5750610ab391610aad919561133e565b916112b8565b92919092610a59565b80959103610ad157610ab391610aad9161133e565b60405163d9d1f46560e01b8152600490fd5b604051630336dc9d60e41b8152600490fd5b604051632154bfcf60e21b8152600490fd5b503461000e57600036600319011261000e576000546040516001600160a01b039091168152602090f35b6020908160408183019282815285518094520193019160005b828110610b58575050505090565b909192938260a082610b976001948951608080918051845260208101516020850152604081015160408501526060810151606085015201511515910152565b01950193929101610b4a565b503461000e5760208060031936011261000e576004356000526002815260406000208054610bd08161033d565b91610bde604051938461027d565b8183526000908152838120938084015b838310610c0357604051806107db8782610b31565b600582600192610c1289612135565b815201960192019194610bee565b503461000e57600036600319011261000e576001546040516001600160a01b039091168152602090f35b503461000e57604036600319011261000e5760043560243590610c7860018060a01b036001541633146116f5565b610c8182611c33565b610c8c811515611b64565b610d25610c9761029e565b838152826020820152426040820152846060820152610cb96080820160019052565b610cd681610cd1866000526002602052604060002090565b611ba7565b610cea846000526003602052604060002090565b60046080918351815560208401516001820155604084015160028201556060840151600382015501910151151560ff80198354169116179055565b60008054909390610d46906001600160a01b03165b6001600160a01b031690565b91823b15610de557604051631136e0db60e21b815260048101859052602481018290527f75353ef654b6c12a8349e2389e2de66319a30d57b06b1ee0863fdd58f7bbac549386908290604490829084905af18015610dd8575b610dbf575b5060408051918252426020830152810191909152606090a280f35b80610dcc610dd292610227565b80610a1a565b38610da4565b610de061186e565b610d9f565b8480fd5b503461000e5760c036600319011261000e576001600160401b0360443581811161000e57610e1b9036906004016102ad565b9060643590811161000e57610e349036906004016102ad565b9060843560ff8116810361000e576105ba9260a43592602435600435612289565b503461000e5760208060031936011261000e576004356001600160401b03811161000e573660238201121561000e57806004013590610e938261033d565b91610ea1604051938461027d565b80835260248484019160051b8301019136831161000e57602401905b828210610ed0576107db610a0a856115af565b81358152908401908401610ebd565b919082519283825260005b848110610f0b575050826000602080949584010152601f8019910116010190565b602081830181015184830182015201610eea565b503461000e57600036600319011261000e576107db604051610f4081610262565b601281527172656473746f6e652d6d61696e2d64656d6f60701b6020820152604051918291602083526020830190610edf565b503461000e57600036600319011261000e57602060405173ad00eb5dc02e56d628d68abd144b8c223a6cf1ef8152f35b503461000e57602036600319011261000e57600435600052600260205260206040600020541515604051908152f35b503461000e57602036600319011261000e576004356000526003602052604060002080546107db60018301549260028101549060ff6004600383015492015416916040519586958693909594919260809360a086019786526020860152604085015260608401521515910152565b503461000e57602036600319011261000e5761105a610327565b600154906001600160a01b03808316916110753384146116f5565b169182156110b3576001600160a01b03191682176001557f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0600080a3005b60405162461bcd60e51b815260206004820152601960248201527f496e76616c6964206e6577206f776e65722061646472657373000000000000006044820152606490fd5b503461000e57602036600319011261000e576103e86004350480421060001461114e57603c611127428361135a565b1161112e57005b60405163b6b0916d60e01b81526004810191909152426024820152604490fd5b60b4814203428111611182575b1161116257005b604051630321d0b560e01b81526004810191909152426024820152604490fd5b61118a6112a1565b61115b565b503461000e57602036600319011261000e5760206111ae600435611c33565b604051908152f35b503461000e57600036600319011261000e57602060405160018152f35b503461000e57604036600319011261000e576004356024356111f361210a565b508160005260026020526040600020548110156112395761122761122d916107db9360005260026020526040600020610735565b50612135565b60405191829182610879565b60405162461bcd60e51b8152602060048201526013602482015272496e646578206f7574206f6620626f756e647360681b6044820152606490fd5b503461000e57602036600319011261000e5760043560005260026020526020604060002054604051908152f35b50634e487b7160e01b600052601160045260246000fd5b60019060001981146112c8570190565b6112d06112a1565b0190565b90606882018092116112e257565b6102ab6112a1565b90600982018092116112e257565b90600282018092116112e257565b90602082018092116112e257565b90600d82018092116112e257565b90604182018092116112e257565b90604e82018092116112e257565b919082018092116112e257565b6000198101919082116112e257565b919082039182116112e257565b818102929181159184041417156112e257565b6113866113979161149a565b60200190816020116113b057611367565b604e81018091116113a55790565b6113ad6112a1565b90565b6113b86112a1565b611367565b6602ed57011dffff1936601f1901356602ed57011e0000160161144357366029116114315761140062ffffff6028193601351660038101809111611424576112ea565b3661140a826112f8565b116114125790565b60405163c30a7bd760e01b8152600490fd5b61142c6112a1565b6112ea565b604051632bcb7bc560e11b8152600490fd5b6040516373bb264f60e11b8152600490fd5b906020820180831161148d575b3610611431576002820191828111611480575b3603601f1901359190565b6114886112a1565b611475565b6114956112a1565b611462565b60418101808211611506575b60618201106114f9575b36039063ffffffff62ffffff6060198401933685116114ec575b60631901938085116114df575b351692351690565b6114e76112a1565b6114d7565b6114f46112a1565b6114ca565b6115016112a1565b6114b0565b61150e6112a1565b6114a6565b6103e8900480421060001461154457603c428203828111611537575b1161112e5750565b61153f6112a1565b61152f565b60b4814203428111611559575b116111625750565b6115616112a1565b611551565b602090805115611574570190565b6112d061071e565b604090805160011015611574570190565b60209181518110156115a2575b60051b010190565b6115aa61071e565b61159a565b600281511461162557805115611613576115c88161163f565b8051600181811c9116611606576113ad916115f9826115f26115ec6116009561134b565b8461158d565b519261158d565b519061133e565b60011c90565b61160f9161158d565b5190565b604051639e198af960e01b8152600490fd5b611600816115f96116386113ad94611566565b519161157c565b8051602082818094019260051b010190805b82811061165e5750505050565b83825b82811061166f575001611651565b82518151808210611685575b5050018490611661565b84528152388061167b565b6001600160a01b031673ad00eb5dc02e56d628d68abd144b8c223a6cf1ef146116f05760405162461bcd60e51b815260206004820152601560248201527414da59db995c881b9bdd08185d5d1a1bdc9a5e9959605a1b6044820152606490fd5b600090565b156116fc57565b60405162461bcd60e51b815260206004820152602160248201527f4f6e6c79206f776e65722063616e2063616c6c20746869732066756e6374696f6044820152603760f91b6064820152608490fd5b9694929093919460018060a01b03611768816001541633146116f5565b6000541696873b1561000e577f710f92a97c7717747dba03755cfebc972b83eefd3c3c821fa005203325108e1b978660008b6117c382968c966117e09b8b6040519b8c9a8b998a98635a0413d360e01b8a5260048a01611801565b03925af180156117f4575b6117e5575b506040519384938461187b565b0390a2565b6117ee90610227565b386117d3565b6117fc61186e565b6117ce565b949693906113ad989693611829611845946118379389526101008060208b0152890190610edf565b908782036040890152610edf565b908582036060870152610edf565b608084019590955260a08301526001600160a01b031660c082015280830360e090910152610edf565b506040513d6000823e3d90fd5b9392916118a490611896604093606088526060880190610edf565b908682036020880152610edf565b930152565b9a949098959c9b929199969793976118cc60018060a01b036001541633146116f5565b60008054909e8f9390916118e8906001600160a01b0316610d3a565b91823b15610de5578e61191a8f958f94938f9489956040519b8c9a8b998a98635a0413d360e01b8a5260048a01611801565b03925af180156119e4575b6119d1575b508a5461193f906001600160a01b0316610d3a565b92833b156119cd57918b8a6119977f710f92a97c7717747dba03755cfebc972b83eefd3c3c821fa005203325108e1b9c9d9e96946117e09a999896604051998a98899788966303f3418160e01b885260048801611ab1565b03925af180156119c0575b6119b357506040519384938461187b565b80610dcc6117ee92610227565b6119c861186e565b6119a2565b8b80fd5b80610dcc6119de92610227565b3861192a565b6119ec61186e565b611925565b90815180825260208080930193019160005b828110611a11575050505090565b835185529381019392810192600101611a03565b90815180825260208092019182818360051b85019501936000915b848310611a505750505050505090565b9091929394958480611a6a83856001950387528a51610edf565b9801930193019194939290611a40565b90815180825260208080930193019160005b828110611a9a575050505090565b835160ff1685529381019392810192600101611a8c565b949296959391908552611ad060209160c08388015260c08701906119f1565b9085820360408701528751908183528083019281808460051b8301019a01936000915b848310611b365750505050505090611b1a86611b2893866113ad9899036060880152611a25565b908482036080860152611a7a565b9160a08184039101526119f1565b90919293949a8480611b546001938f601f1987830301885251610edf565b9d01930193019194939290611af3565b15611b6b57565b60405162461bcd60e51b8152602060048201526014602482015273496e76616c6964206f7261636c652076616c756560601b6044820152606490fd5b8054611bca9168010000000000000000821015611c26575b600182018155610735565b919091611c10576102ab9160046080918351815560208401516001820155604084015160028201556060840151600382015501910151151560ff80198354169116179055565b634e487b7160e01b600052600060045260246000fd5b611c2e610210565b611bbf565b906040918251611c4281610262565b600191828252602082016020368237825115611d6d575b52600091611c678251611d94565b91611c728151611d94565b90611c7d8151611dc6565b9685845b611d35575b509061ffff939291611c99610a476113bd565b95169281519288965b858810611ccd575050505050505050611cc2611cc89161160f9495612038565b91611513565b611566565b611ce1908c848b859d9798999a9b9d611e29565b90938115611d2457808214159081611d1a575b50611d09578585529887019695949392611ca2565b845163265e23a160e11b8152600490fd5b9050151538611cf4565b855163dfb25a7960e01b8152600490fd5b84908351811015611d67578190611d4a611d7a565b611d54828d61158d565b52611d5f818c61158d565b500190611c81565b50611c86565b611d7561071e565b611c59565b60405190611d8782610262565b6001825260203681840137565b90611d9e8261033d565b611dab604051918261027d565b8281528092611dbc601f199161033d565b0190602036910137565b90611dd08261033d565b611ddd604051918261027d565b8281528092611dee601f199161033d565b019060005b828110611dff57505050565b806060602080938501015201611df3565b9060405191818352602082840101604052602083013790565b9091949394611e378661149a565b919094611e55611e5087611e4a86611306565b90611367565b611314565b97611eb7611eb1611eab61094165ffffffffffff611e75610a8a876112d4565b9d6020611e9682611e91610a8a82611e8c8d611322565b61133e565b611e10565b01209d35169c611ea586611322565b906120c0565b60ff1690565b91611330565b9660009687955b828710611ed2575050505050505050509190565b929598611eec84611ef1929d94979a9d9c9396999c61133e565b611306565b611efb8482611fe9565b96909c805b8d51811015611fd3578e611f158f839061158d565b5114611f2357600101611f00565b9095939c9a9896949d50896001939c929a98611f3f838c61158d565b51858a1b91828216158781611fbf575b50611f6b575b50505050505b0196919097969594939297611ebe565b611f8d611f7b86611fb49661158d565b51611f86878c61158d565b519061158d565b52611fa1611f9b858a61158d565b516112b8565b611fab858a61158d565b5217918a61158d565b523887818080611f55565b9050611fcb868b61158d565b511038611f4f565b5094929b999795939c5097955098600190611f5b565b91909136039136831161202b575b823592602182101561201257602001359060200360031b1c90565b6040516360007e2160e11b815260048101839052602490fd5b6120336112a1565b611ff7565b91906120448351611d94565b9060005b84518110156120b95760018061205e838561158d565b511061209157508061207c61207661208c938861158d565b516115af565b612086828661158d565b526112b8565b612048565b61209d6044928461158d565b519060405191632b13aef560e01b835260048301526024820152fd5b5090925050565b60806000916020809436036040519283526040810135851a82840152803560408401520135606082015282805260015afa156120fd575b60005190565b61210561186e565b6120f7565b6040519061211782610247565b60006080838281528260208201528260408201528260608201520152565b9060405161214281610247565b608060ff60048395805485526001810154602086015260028101546040860152600381015460608601520154161515910152565b9493909392919260018060a01b03612193816001541633146116f5565b60005416926121ba60405197633facfafb60e01b895260c060048a015260c4890190610edf565b916006871015612264576122108896879561220187956121f160009860209d60248a015260031995868a83030160448b0152610edf565b9084888303016064890152610edf565b91858303016084860152610edf565b90151560a483015203925af1908115612257575b60009161222f575090565b6113ad915060203d8111612250575b612248818361027d565b81019061227a565b503d61223e565b61225f61186e565b612224565b634e487b7160e01b600052602160045260246000fd5b9081602091031261000e575190565b919294939460018060a01b036122a4816001541633146116f5565b6000541692833b1561000e576122ed9660009660ff6122ff89956040519b8c9a8b998a986350e34e8160e11b8a5260048a0152602489015260c0604489015260c4880190610edf565b86810360031901606488015290610edf565b9216608484015260a483015203925af1801561232a575b61231d5750565b80610dcc6102ab92610227565b61233261186e565b612316565b91929460018060a01b03612350816001541633146116f5565b6000541692833b1561000e57612382600096928793604051998a98899788966303f3418160e01b885260048801611ab1565b03925af1801561239d575b6123945750565b6102ab90610227565b6123a561186e565b61238d56fea264697066735822122037f173854de5d66444f3f5a3bace4adb4a1e0e11cc109d371da4f527933851f564736f6c6343000811003300000000000000000000000012df1496f2c2f57967d21dc7ecc700c28d748ae5
Deployed Bytecode
0x60806040526004361015610013575b600080fd5b60003560e01c806301d918d71461020757806303f34181146101fe5780631a5da6c8146101f55780632093c15b146101ec578063241d3827146101e3578063265ce501146101da5780633ce142f5146101d15780633facfafb146101c857806355a547d5146101bf5780637b103999146101b65780637fb58537146101ad5780638da5cb5b146101a4578063968e72bd1461019b578063a1c69d0214610192578063b24ebfcc14610189578063c274583a14610180578063cd13efb614610177578063e3d1e6d61461016e578063f2aef13b14610165578063f2fde38b1461015c578063f50b2efe14610153578063f5bcb8d61461014a578063f90c492414610141578063f9cd9080146101385763fd769d631461013057600080fd5b61000e611274565b5061000e6111d3565b5061000e6111b6565b5061000e61118f565b5061000e6110f8565b5061000e611040565b5061000e610fd2565b5061000e610fa3565b5061000e610f73565b5061000e610f1f565b5061000e610e55565b5061000e610de9565b5061000e610c4a565b5061000e610c20565b5061000e610ba3565b5061000e610b07565b5061000e610a25565b5061000e61096f565b5061000e610922565b5061000e6108b4565b5061000e6107df565b5061000e61075f565b5061000e610657565b5061000e6105bc565b5061000e6104a9565b50634e487b7160e01b600052604160045260246000fd5b6001600160401b03811161023a57604052565b610242610210565b604052565b60a081019081106001600160401b0382111761023a57604052565b604081019081106001600160401b0382111761023a57604052565b90601f801991011681019081106001600160401b0382111761023a57604052565b604051906102ab82610247565b565b81601f8201121561000e578035906001600160401b038211610304575b604051926102e2601f8401601f19166020018561027d565b8284526020838301011161000e57816000926020809301838601378301015290565b61030c610210565b6102ca565b60c435906001600160a01b038216820361000e57565b600435906001600160a01b038216820361000e57565b6020906001600160401b038111610356575b60051b0190565b61035e610210565b61034f565b81601f8201121561000e5780359161037a8361033d565b92610388604051948561027d565b808452602092838086019260051b82010192831161000e578301905b8282106103b2575050505090565b813581529083019083016103a4565b9080601f8301121561000e578135906103d98261033d565b926103e7604051948561027d565b828452602092838086019160051b8301019280841161000e57848301915b8483106104155750505050505090565b82356001600160401b03811161000e578691610436848480948901016102ad565b815201920191610405565b81601f8201121561000e578035916104588361033d565b92610466604051948561027d565b808452602092838086019260051b82010192831161000e578301905b828210610490575050505090565b813560ff8116810361000e578152908301908301610482565b503461000e576101a036600319011261000e576001600160401b0360243581811161000e576104dd600491369083016102ad565b60443583811161000e576104f490369084016102ad565b9160643584811161000e5761050c90369083016102ad565b91610515610311565b60e43586811161000e5761052c90369085016102ad565b6101043587811161000e576105449036908601610363565b916101243588811161000e5761055d90369087016103c1565b936101443589811161000e5761057690369088016103c1565b95610164358a811161000e5761058f9036908301610441565b97610184359a8b1161000e576105ab6105ba9b36908401610363565b9960a4359360843593356118a9565b005b503461000e5760c036600319011261000e576001600160401b0360243581811161000e576105ef60049136908301610363565b60443583811161000e5761060690369084016103c1565b60643584811161000e5761061d90369085016103c1565b9060843585811161000e576106359036908601610441565b9260a43595861161000e576106506105ba9636908701610363565b9435612337565b503461000e57602036600319011261000e57610671610327565b6001546001600160a01b0391829161068c90831633146116f5565b169081156106d957600080546001600160a01b031981166001600160a01b03851617909155167f482b97c53e48ffa324a976e2738053e9aff6eee04d8aac63b10e19411d869b82600080a3005b60405162461bcd60e51b815260206004820152601860248201527f496e76616c6964207265676973747279206164647265737300000000000000006044820152606490fd5b50634e487b7160e01b600052603260045260246000fd5b8054821015610752575b6000526005602060002091020190600090565b61075a61071e565b61073f565b503461000e57604036600319011261000e576024356004356000526002602052604060002090815481101561000e5761079791610735565b50805460018201546002830154600384015460049094015460408051948552602085019390935291830152606082019290925260ff9091161515608082015260a090f35b0390f35b503461000e5761010036600319011261000e576001600160401b0360243581811161000e576108129036906004016102ad565b60443582811161000e5761082a9036906004016102ad565b9060643583811161000e576108439036906004016102ad565b9161084c610311565b9160e43594851161000e576108686105ba9536906004016102ad565b9360a435926084359260043561174b565b6102ab9092919260a0810193608080918051845260208101516020850152604081015160408501526060810151606085015201511515910152565b503461000e57602036600319011261000e576108ce61210a565b50600435600052600360205260a06108e96040600020612135565b6109206040518092608080918051845260208101516020850152604081015160408501526060810151606085015201511515910152565bf35b503461000e57602036600319011261000e576020610946610941610327565b611690565b60ff60405191168152f35b60243590600682101561000e57565b60a43590811515820361000e57565b503461000e5760c036600319011261000e576001600160401b0360043581811161000e576109a19036906004016102ad565b906109aa610951565b9160443582811161000e576109c39036906004016102ad565b9260643583811161000e576109dc9036906004016102ad565b60843593841161000e576107db946109fb610a0a9536906004016102ad565b92610a04610960565b94612176565b6040519081529081906020820190565b600091031261000e57565b503461000e57600036600319011261000e57600061ffff610a4c610a476113bd565b611455565b9116908115610af5579190815b818110610a6b57604051838152602090f35b9091610a768461137a565b9065ffffffffffff610a90610a8a876112d4565b3661135a565b3516908115610ae35780610abc5750610ab391610aad919561133e565b916112b8565b92919092610a59565b80959103610ad157610ab391610aad9161133e565b60405163d9d1f46560e01b8152600490fd5b604051630336dc9d60e41b8152600490fd5b604051632154bfcf60e21b8152600490fd5b503461000e57600036600319011261000e576000546040516001600160a01b039091168152602090f35b6020908160408183019282815285518094520193019160005b828110610b58575050505090565b909192938260a082610b976001948951608080918051845260208101516020850152604081015160408501526060810151606085015201511515910152565b01950193929101610b4a565b503461000e5760208060031936011261000e576004356000526002815260406000208054610bd08161033d565b91610bde604051938461027d565b8183526000908152838120938084015b838310610c0357604051806107db8782610b31565b600582600192610c1289612135565b815201960192019194610bee565b503461000e57600036600319011261000e576001546040516001600160a01b039091168152602090f35b503461000e57604036600319011261000e5760043560243590610c7860018060a01b036001541633146116f5565b610c8182611c33565b610c8c811515611b64565b610d25610c9761029e565b838152826020820152426040820152846060820152610cb96080820160019052565b610cd681610cd1866000526002602052604060002090565b611ba7565b610cea846000526003602052604060002090565b60046080918351815560208401516001820155604084015160028201556060840151600382015501910151151560ff80198354169116179055565b60008054909390610d46906001600160a01b03165b6001600160a01b031690565b91823b15610de557604051631136e0db60e21b815260048101859052602481018290527f75353ef654b6c12a8349e2389e2de66319a30d57b06b1ee0863fdd58f7bbac549386908290604490829084905af18015610dd8575b610dbf575b5060408051918252426020830152810191909152606090a280f35b80610dcc610dd292610227565b80610a1a565b38610da4565b610de061186e565b610d9f565b8480fd5b503461000e5760c036600319011261000e576001600160401b0360443581811161000e57610e1b9036906004016102ad565b9060643590811161000e57610e349036906004016102ad565b9060843560ff8116810361000e576105ba9260a43592602435600435612289565b503461000e5760208060031936011261000e576004356001600160401b03811161000e573660238201121561000e57806004013590610e938261033d565b91610ea1604051938461027d565b80835260248484019160051b8301019136831161000e57602401905b828210610ed0576107db610a0a856115af565b81358152908401908401610ebd565b919082519283825260005b848110610f0b575050826000602080949584010152601f8019910116010190565b602081830181015184830182015201610eea565b503461000e57600036600319011261000e576107db604051610f4081610262565b601281527172656473746f6e652d6d61696e2d64656d6f60701b6020820152604051918291602083526020830190610edf565b503461000e57600036600319011261000e57602060405173ad00eb5dc02e56d628d68abd144b8c223a6cf1ef8152f35b503461000e57602036600319011261000e57600435600052600260205260206040600020541515604051908152f35b503461000e57602036600319011261000e576004356000526003602052604060002080546107db60018301549260028101549060ff6004600383015492015416916040519586958693909594919260809360a086019786526020860152604085015260608401521515910152565b503461000e57602036600319011261000e5761105a610327565b600154906001600160a01b03808316916110753384146116f5565b169182156110b3576001600160a01b03191682176001557f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0600080a3005b60405162461bcd60e51b815260206004820152601960248201527f496e76616c6964206e6577206f776e65722061646472657373000000000000006044820152606490fd5b503461000e57602036600319011261000e576103e86004350480421060001461114e57603c611127428361135a565b1161112e57005b60405163b6b0916d60e01b81526004810191909152426024820152604490fd5b60b4814203428111611182575b1161116257005b604051630321d0b560e01b81526004810191909152426024820152604490fd5b61118a6112a1565b61115b565b503461000e57602036600319011261000e5760206111ae600435611c33565b604051908152f35b503461000e57600036600319011261000e57602060405160018152f35b503461000e57604036600319011261000e576004356024356111f361210a565b508160005260026020526040600020548110156112395761122761122d916107db9360005260026020526040600020610735565b50612135565b60405191829182610879565b60405162461bcd60e51b8152602060048201526013602482015272496e646578206f7574206f6620626f756e647360681b6044820152606490fd5b503461000e57602036600319011261000e5760043560005260026020526020604060002054604051908152f35b50634e487b7160e01b600052601160045260246000fd5b60019060001981146112c8570190565b6112d06112a1565b0190565b90606882018092116112e257565b6102ab6112a1565b90600982018092116112e257565b90600282018092116112e257565b90602082018092116112e257565b90600d82018092116112e257565b90604182018092116112e257565b90604e82018092116112e257565b919082018092116112e257565b6000198101919082116112e257565b919082039182116112e257565b818102929181159184041417156112e257565b6113866113979161149a565b60200190816020116113b057611367565b604e81018091116113a55790565b6113ad6112a1565b90565b6113b86112a1565b611367565b6602ed57011dffff1936601f1901356602ed57011e0000160161144357366029116114315761140062ffffff6028193601351660038101809111611424576112ea565b3661140a826112f8565b116114125790565b60405163c30a7bd760e01b8152600490fd5b61142c6112a1565b6112ea565b604051632bcb7bc560e11b8152600490fd5b6040516373bb264f60e11b8152600490fd5b906020820180831161148d575b3610611431576002820191828111611480575b3603601f1901359190565b6114886112a1565b611475565b6114956112a1565b611462565b60418101808211611506575b60618201106114f9575b36039063ffffffff62ffffff6060198401933685116114ec575b60631901938085116114df575b351692351690565b6114e76112a1565b6114d7565b6114f46112a1565b6114ca565b6115016112a1565b6114b0565b61150e6112a1565b6114a6565b6103e8900480421060001461154457603c428203828111611537575b1161112e5750565b61153f6112a1565b61152f565b60b4814203428111611559575b116111625750565b6115616112a1565b611551565b602090805115611574570190565b6112d061071e565b604090805160011015611574570190565b60209181518110156115a2575b60051b010190565b6115aa61071e565b61159a565b600281511461162557805115611613576115c88161163f565b8051600181811c9116611606576113ad916115f9826115f26115ec6116009561134b565b8461158d565b519261158d565b519061133e565b60011c90565b61160f9161158d565b5190565b604051639e198af960e01b8152600490fd5b611600816115f96116386113ad94611566565b519161157c565b8051602082818094019260051b010190805b82811061165e5750505050565b83825b82811061166f575001611651565b82518151808210611685575b5050018490611661565b84528152388061167b565b6001600160a01b031673ad00eb5dc02e56d628d68abd144b8c223a6cf1ef146116f05760405162461bcd60e51b815260206004820152601560248201527414da59db995c881b9bdd08185d5d1a1bdc9a5e9959605a1b6044820152606490fd5b600090565b156116fc57565b60405162461bcd60e51b815260206004820152602160248201527f4f6e6c79206f776e65722063616e2063616c6c20746869732066756e6374696f6044820152603760f91b6064820152608490fd5b9694929093919460018060a01b03611768816001541633146116f5565b6000541696873b1561000e577f710f92a97c7717747dba03755cfebc972b83eefd3c3c821fa005203325108e1b978660008b6117c382968c966117e09b8b6040519b8c9a8b998a98635a0413d360e01b8a5260048a01611801565b03925af180156117f4575b6117e5575b506040519384938461187b565b0390a2565b6117ee90610227565b386117d3565b6117fc61186e565b6117ce565b949693906113ad989693611829611845946118379389526101008060208b0152890190610edf565b908782036040890152610edf565b908582036060870152610edf565b608084019590955260a08301526001600160a01b031660c082015280830360e090910152610edf565b506040513d6000823e3d90fd5b9392916118a490611896604093606088526060880190610edf565b908682036020880152610edf565b930152565b9a949098959c9b929199969793976118cc60018060a01b036001541633146116f5565b60008054909e8f9390916118e8906001600160a01b0316610d3a565b91823b15610de5578e61191a8f958f94938f9489956040519b8c9a8b998a98635a0413d360e01b8a5260048a01611801565b03925af180156119e4575b6119d1575b508a5461193f906001600160a01b0316610d3a565b92833b156119cd57918b8a6119977f710f92a97c7717747dba03755cfebc972b83eefd3c3c821fa005203325108e1b9c9d9e96946117e09a999896604051998a98899788966303f3418160e01b885260048801611ab1565b03925af180156119c0575b6119b357506040519384938461187b565b80610dcc6117ee92610227565b6119c861186e565b6119a2565b8b80fd5b80610dcc6119de92610227565b3861192a565b6119ec61186e565b611925565b90815180825260208080930193019160005b828110611a11575050505090565b835185529381019392810192600101611a03565b90815180825260208092019182818360051b85019501936000915b848310611a505750505050505090565b9091929394958480611a6a83856001950387528a51610edf565b9801930193019194939290611a40565b90815180825260208080930193019160005b828110611a9a575050505090565b835160ff1685529381019392810192600101611a8c565b949296959391908552611ad060209160c08388015260c08701906119f1565b9085820360408701528751908183528083019281808460051b8301019a01936000915b848310611b365750505050505090611b1a86611b2893866113ad9899036060880152611a25565b908482036080860152611a7a565b9160a08184039101526119f1565b90919293949a8480611b546001938f601f1987830301885251610edf565b9d01930193019194939290611af3565b15611b6b57565b60405162461bcd60e51b8152602060048201526014602482015273496e76616c6964206f7261636c652076616c756560601b6044820152606490fd5b8054611bca9168010000000000000000821015611c26575b600182018155610735565b919091611c10576102ab9160046080918351815560208401516001820155604084015160028201556060840151600382015501910151151560ff80198354169116179055565b634e487b7160e01b600052600060045260246000fd5b611c2e610210565b611bbf565b906040918251611c4281610262565b600191828252602082016020368237825115611d6d575b52600091611c678251611d94565b91611c728151611d94565b90611c7d8151611dc6565b9685845b611d35575b509061ffff939291611c99610a476113bd565b95169281519288965b858810611ccd575050505050505050611cc2611cc89161160f9495612038565b91611513565b611566565b611ce1908c848b859d9798999a9b9d611e29565b90938115611d2457808214159081611d1a575b50611d09578585529887019695949392611ca2565b845163265e23a160e11b8152600490fd5b9050151538611cf4565b855163dfb25a7960e01b8152600490fd5b84908351811015611d67578190611d4a611d7a565b611d54828d61158d565b52611d5f818c61158d565b500190611c81565b50611c86565b611d7561071e565b611c59565b60405190611d8782610262565b6001825260203681840137565b90611d9e8261033d565b611dab604051918261027d565b8281528092611dbc601f199161033d565b0190602036910137565b90611dd08261033d565b611ddd604051918261027d565b8281528092611dee601f199161033d565b019060005b828110611dff57505050565b806060602080938501015201611df3565b9060405191818352602082840101604052602083013790565b9091949394611e378661149a565b919094611e55611e5087611e4a86611306565b90611367565b611314565b97611eb7611eb1611eab61094165ffffffffffff611e75610a8a876112d4565b9d6020611e9682611e91610a8a82611e8c8d611322565b61133e565b611e10565b01209d35169c611ea586611322565b906120c0565b60ff1690565b91611330565b9660009687955b828710611ed2575050505050505050509190565b929598611eec84611ef1929d94979a9d9c9396999c61133e565b611306565b611efb8482611fe9565b96909c805b8d51811015611fd3578e611f158f839061158d565b5114611f2357600101611f00565b9095939c9a9896949d50896001939c929a98611f3f838c61158d565b51858a1b91828216158781611fbf575b50611f6b575b50505050505b0196919097969594939297611ebe565b611f8d611f7b86611fb49661158d565b51611f86878c61158d565b519061158d565b52611fa1611f9b858a61158d565b516112b8565b611fab858a61158d565b5217918a61158d565b523887818080611f55565b9050611fcb868b61158d565b511038611f4f565b5094929b999795939c5097955098600190611f5b565b91909136039136831161202b575b823592602182101561201257602001359060200360031b1c90565b6040516360007e2160e11b815260048101839052602490fd5b6120336112a1565b611ff7565b91906120448351611d94565b9060005b84518110156120b95760018061205e838561158d565b511061209157508061207c61207661208c938861158d565b516115af565b612086828661158d565b526112b8565b612048565b61209d6044928461158d565b519060405191632b13aef560e01b835260048301526024820152fd5b5090925050565b60806000916020809436036040519283526040810135851a82840152803560408401520135606082015282805260015afa156120fd575b60005190565b61210561186e565b6120f7565b6040519061211782610247565b60006080838281528260208201528260408201528260608201520152565b9060405161214281610247565b608060ff60048395805485526001810154602086015260028101546040860152600381015460608601520154161515910152565b9493909392919260018060a01b03612193816001541633146116f5565b60005416926121ba60405197633facfafb60e01b895260c060048a015260c4890190610edf565b916006871015612264576122108896879561220187956121f160009860209d60248a015260031995868a83030160448b0152610edf565b9084888303016064890152610edf565b91858303016084860152610edf565b90151560a483015203925af1908115612257575b60009161222f575090565b6113ad915060203d8111612250575b612248818361027d565b81019061227a565b503d61223e565b61225f61186e565b612224565b634e487b7160e01b600052602160045260246000fd5b9081602091031261000e575190565b919294939460018060a01b036122a4816001541633146116f5565b6000541692833b1561000e576122ed9660009660ff6122ff89956040519b8c9a8b998a986350e34e8160e11b8a5260048a0152602489015260c0604489015260c4880190610edf565b86810360031901606488015290610edf565b9216608484015260a483015203925af1801561232a575b61231d5750565b80610dcc6102ab92610227565b61233261186e565b612316565b91929460018060a01b03612350816001541633146116f5565b6000541692833b1561000e57612382600096928793604051998a98899788966303f3418160e01b885260048801611ab1565b03925af1801561239d575b6123945750565b6102ab90610227565b6123a561186e565b61238d56fea264697066735822122037f173854de5d66444f3f5a3bace4adb4a1e0e11cc109d371da4f527933851f564736f6c63430008110033
Loading...
Loading
Loading...
Loading
Net Worth in USD
$0.00
Net Worth in ETH
0
Multichain Portfolio | 33 Chains
| Chain | Token | Portfolio % | Price | Amount | Value |
|---|
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.