MATIC Price: $1.03 (-2.06%)
Gas: 85 GWei
 

Overview

MATIC Balance

Polygon PoS Chain LogoPolygon PoS Chain LogoPolygon PoS Chain Logo0 MATIC

MATIC Value

$0.00

Sponsored

Transaction Hash
Method
Block
From
To
Value
0x60806040359767262022-11-23 18:27:30463 days 20 hrs ago1669228050IN
 Create: Vehicle
0 MATIC0.051619532.71297759

Parent Txn Hash Block From To Value
View All Internal Transactions
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
Vehicle

Compiler Version
v0.8.13+commit.abaa5c0e

Optimization Enabled:
Yes with 200 runs

Other Settings:
default evmVersion

Contract Source Code (Solidity Standard Json-Input format)

File 1 of 18 : Vehicle.sol
//SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.13;

import "../../interfaces/INFT.sol";
import "../../Eip712/Eip712CheckerInternal.sol";
import "../../libraries/NodesStorage.sol";
import "../../libraries/nodes/ManufacturerStorage.sol";
import "../../libraries/nodes/VehicleStorage.sol";

import {DEFAULT_ADMIN_ROLE} from "../../shared/Roles.sol";
import {AttributeInfoPair} from "../../shared/Types.sol";

import "@solidstate/contracts/access/access_control/AccessControlInternal.sol";

/// @title Vehicle
/// @notice Contract that represents the Vehicle node
contract Vehicle is AccessControlInternal {
    bytes32 private constant MINT_TYPEHASH =
        keccak256(
            "MintVehicleSign(uint256 manufacturerNode,address owner,string[] attributes,string[] infos)"
        );

    event VehicleIdProxySet(address indexed proxy);
    event VehicleAttributeAdded(string attribute);
    event VehicleAttributeSet(uint256 tokenId, string attribute, string info);
    event VehicleNodeMinted(uint256 tokenId, address owner);

    // ***** Admin management ***** //

    /// @notice Sets the NFT proxy associated with the Vehicle node
    /// @dev Only an admin can set the address
    /// @param addr The address of the proxy
    function setVehicleIdProxyAddress(address addr)
        external
        onlyRole(DEFAULT_ADMIN_ROLE)
    {
        require(addr != address(0), "Non zero address");
        VehicleStorage.getStorage().idProxyAddress = addr;

        emit VehicleIdProxySet(addr);
    }

    /// @notice Adds an attribute to the whielist
    /// @dev Only an admin can add a new attribute
    /// @param attribute The attribute to be added
    function addVehicleAttribute(string calldata attribute)
        external
        onlyRole(DEFAULT_ADMIN_ROLE)
    {
        require(
            AttributeSet.add(
                VehicleStorage.getStorage().whitelistedAttributes,
                attribute
            ),
            "Attribute already exists"
        );

        emit VehicleAttributeAdded(attribute);
    }

    // ***** Interaction with nodes *****//

    /// @notice Mints a vehicle
    /// @dev Caller must have the admin role
    /// @param manufacturerNode Parent manufacturer node id
    /// @param owner The address of the new owner
    /// @param attrInfo List of attribute-info pairs to be added
    function mintVehicle(
        uint256 manufacturerNode,
        address owner,
        AttributeInfoPair[] calldata attrInfo
    ) external onlyRole(DEFAULT_ADMIN_ROLE) {
        address vehicleIdProxyAddress = VehicleStorage
            .getStorage()
            .idProxyAddress;

        require(
            INFT(ManufacturerStorage.getStorage().idProxyAddress).exists(
                manufacturerNode
            ),
            "Invalid parent node"
        );

        uint256 newTokenId = INFT(vehicleIdProxyAddress).safeMint(owner);

        NodesStorage
        .getStorage()
        .nodes[vehicleIdProxyAddress][newTokenId].parentNode = manufacturerNode;

        _setInfos(newTokenId, attrInfo);

        emit VehicleNodeMinted(newTokenId, owner);
    }

    /// @notice Mints a vehicle through a metatransaction
    /// The vehicle owner signs a typed structured (EIP-712) message in advance and submits to be verified
    /// @dev Caller must have the admin role
    /// @param manufacturerNode Parent manufacturer node id
    /// @param owner The address of the new owner
    /// @param attrInfo List of attribute-info pairs to be added
    /// @param signature User's signature hash
    function mintVehicleSign(
        uint256 manufacturerNode,
        address owner,
        AttributeInfoPair[] calldata attrInfo,
        bytes calldata signature
    ) external onlyRole(DEFAULT_ADMIN_ROLE) {
        NodesStorage.Storage storage ns = NodesStorage.getStorage();
        address vehicleIdProxyAddress = VehicleStorage
            .getStorage()
            .idProxyAddress;

        require(
            INFT(ManufacturerStorage.getStorage().idProxyAddress).exists(
                manufacturerNode
            ),
            "Invalid parent node"
        );

        uint256 newTokenId = INFT(vehicleIdProxyAddress).safeMint(owner);

        ns
        .nodes[vehicleIdProxyAddress][newTokenId].parentNode = manufacturerNode;

        (bytes32 attributesHash, bytes32 infosHash) = _setInfosHash(
            newTokenId,
            attrInfo
        );

        bytes32 message = keccak256(
            abi.encode(
                MINT_TYPEHASH,
                manufacturerNode,
                owner,
                attributesHash,
                infosHash
            )
        );

        require(
            Eip712CheckerInternal._verifySignature(owner, message, signature),
            "Invalid signature"
        );

        emit VehicleNodeMinted(newTokenId, owner);
    }

    /// @notice Add infos to node
    /// @dev attributes must be whitelisted
    /// @dev Caller must have the admin role
    /// @param tokenId Node where the info will be added
    /// @param attrInfo List of attribute-info pairs to be added
    function setVehicleInfo(
        uint256 tokenId,
        AttributeInfoPair[] calldata attrInfo
    ) external onlyRole(DEFAULT_ADMIN_ROLE) {
        require(
            INFT(VehicleStorage.getStorage().idProxyAddress).exists(tokenId),
            "Invalid vehicle node"
        );
        _setInfos(tokenId, attrInfo);
    }

    // ***** PRIVATE FUNCTIONS ***** //

    /// @dev Internal function to add infos to node
    /// @dev attributes must be whitelisted
    /// @param tokenId Node where the info will be added
    /// @param attrInfo List of attribute-info pairs to be added
    function _setInfos(uint256 tokenId, AttributeInfoPair[] calldata attrInfo)
        private
    {
        NodesStorage.Storage storage ns = NodesStorage.getStorage();
        VehicleStorage.Storage storage s = VehicleStorage.getStorage();
        address idProxyAddress = s.idProxyAddress;

        for (uint256 i = 0; i < attrInfo.length; i++) {
            require(
                AttributeSet.exists(
                    s.whitelistedAttributes,
                    attrInfo[i].attribute
                ),
                "Not whitelisted"
            );
            ns.nodes[idProxyAddress][tokenId].info[
                attrInfo[i].attribute
            ] = attrInfo[i].info;

            emit VehicleAttributeSet(
                tokenId,
                attrInfo[i].attribute,
                attrInfo[i].info
            );
        }
    }

    /// @dev Internal function to add infos to node and calculate attribute and info hashes
    /// @dev attributes must be whitelisted
    /// @param tokenId Node where the info will be added
    /// @param attrInfo List of attribute-info pairs to be added
    /// @return keccak256 of the list of attributes and infos
    function _setInfosHash(
        uint256 tokenId,
        AttributeInfoPair[] calldata attrInfo
    ) private returns (bytes32, bytes32) {
        NodesStorage.Storage storage ns = NodesStorage.getStorage();
        VehicleStorage.Storage storage s = VehicleStorage.getStorage();
        address idProxyAddress = s.idProxyAddress;

        bytes32[] memory attributeHashes = new bytes32[](attrInfo.length);
        bytes32[] memory infoHashes = new bytes32[](attrInfo.length);

        for (uint256 i = 0; i < attrInfo.length; i++) {
            require(
                AttributeSet.exists(
                    s.whitelistedAttributes,
                    attrInfo[i].attribute
                ),
                "Not whitelisted"
            );

            attributeHashes[i] = keccak256(bytes(attrInfo[i].attribute));
            infoHashes[i] = keccak256(bytes(attrInfo[i].info));

            ns.nodes[idProxyAddress][tokenId].info[
                attrInfo[i].attribute
            ] = attrInfo[i].info;

            emit VehicleAttributeSet(
                tokenId,
                attrInfo[i].attribute,
                attrInfo[i].info
            );
        }

        return (
            keccak256(abi.encodePacked(attributeHashes)),
            keccak256(abi.encodePacked(infoHashes))
        );
    }

    /// @dev Internal function to update a single attribute
    /// @dev attribute must be whitelisted
    /// @param tokenId Node where the info will be added
    /// @param attribute Attribute to be updated
    /// @param info Info to be set
    function _setAttributeInfo(
        uint256 tokenId,
        string calldata attribute,
        string calldata info
    ) private {
        NodesStorage.Storage storage ns = NodesStorage.getStorage();
        VehicleStorage.Storage storage s = VehicleStorage.getStorage();

        require(
            AttributeSet.exists(s.whitelistedAttributes, attribute),
            "Not whitelisted"
        );

        address idProxyAddress = s.idProxyAddress;

        ns.nodes[idProxyAddress][tokenId].info[attribute] = info;

        emit VehicleAttributeSet(tokenId, attribute, info);
    }
}

File 2 of 18 : INFT.sol
//SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.13;

/// @title INFT
/// @notice Interface of a generic NFT
interface INFT {
    function safeMint(address to) external returns (uint256);

    function safeTransferByRegistry(
        address from,
        address to,
        uint256 tokenId
    ) external;

    function safeTransferFrom(
        address from,
        address to,
        uint256 tokenId
    ) external;

    function setApprovalForAll(address operator, bool _approved) external;

    function safeTransferFrom(
        address from,
        address to,
        uint256 tokenId,
        bytes calldata data
    ) external;

    function ownerOf(uint256 tokenId) external view returns (address owner);

    function exists(uint256 tokenId) external view returns (bool);

    function isApprovedForAll(address owner, address operator)
        external
        view
        returns (bool);
}

File 3 of 18 : Eip712CheckerInternal.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.13;

import "./Eip712CheckerStorage.sol";

import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";

/// @title Eip712CheckerInternal
/// @notice Contract with internal functions to assist in verifying signatures
/// @dev Based on the EIP-712 https://eips.ethereum.org/EIPS/eip-712
library Eip712CheckerInternal {
    bytes32 private constant EIP712_DOMAIN_TYPEHASH =
        keccak256(
            "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"
        );

    /// @dev Returns the EIP-712 domain separator
    function _eip712Domain() internal view returns (bytes32) {
        Eip712CheckerStorage.Storage storage s = Eip712CheckerStorage
            .getStorage();

        return
            keccak256(
                abi.encode(
                    EIP712_DOMAIN_TYPEHASH,
                    s.name,
                    s.version,
                    block.chainid,
                    address(this)
                )
            );
    }

    /// @dev Recovers message signer and verifies if metches signatory
    /// @param signatory The signer to be verified
    /// @param message Hashed data payload
    /// @param signature Signed data payload
    function _verifySignature(
        address signatory,
        bytes32 message,
        bytes calldata signature
    ) internal view returns (bool success) {
        require(signatory != address(0), "ECDSA: zero signatory address");

        bytes32 msgHash = keccak256(
            abi.encodePacked("\x19\x01", _eip712Domain(), message)
        );

        return signatory == ECDSA.recover(msgHash, signature);
    }

    /// @dev Recovers message signer and verifies if metches signatory
    /// @param signatory The signer to be verified
    /// @param message Hashed data payload
    /// @param v Signature "v" value
    /// @param r Signature "r" value
    /// @param s Signature "s" value
    function _verifySignature(
        address signatory,
        bytes32 message,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) internal view returns (bool success) {
        require(signatory != address(0), "ECDSA: zero signatory address");

        bytes32 msgHash = keccak256(
            abi.encodePacked("\x19\x01", _eip712Domain(), message)
        );

        return signatory == ECDSA.recover(msgHash, v, r, s);
    }
}

File 4 of 18 : NodesStorage.sol
//SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.13;

/// @title NodesStorage
/// @notice Storage of the Nodes contract
library NodesStorage {
    bytes32 internal constant NODES_STORAGE_SLOT =
        keccak256("DIMORegistry.nodes.storage");

    struct Node {
        uint256 parentNode;
        mapping(string => string) info;
    }

    struct Storage {
        mapping(address => mapping(uint256 => Node)) nodes;
    }

    /* solhint-disable no-inline-assembly */
    function getStorage() internal pure returns (Storage storage s) {
        bytes32 slot = NODES_STORAGE_SLOT;
        assembly {
            s.slot := slot
        }
    }
    /* solhint-enable no-inline-assembly */
}

File 5 of 18 : ManufacturerStorage.sol
//SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.13;

import "../AttributeSet.sol";

/// @title ManufacturerStorage
/// @notice Storage of the Manufacturer contract
library ManufacturerStorage {
    using AttributeSet for AttributeSet.Set;

    bytes32 private constant MANUFACTURER_STORAGE_SLOT =
        keccak256("DIMORegistry.Manufacturer.storage");

    struct Controller {
        bool isController;
        bool manufacturerMinted;
    }

    struct Storage {
        address idProxyAddress;
        // [Controller address] => is controller, has minted manufacturer
        mapping(address => Controller) controllers;
        // Allowed node attribute
        AttributeSet.Set whitelistedAttributes;
        // Manufacturer name => Manufacturer Id
        mapping(string => uint256) manufacturerNameToNodeId;
        // Manufacturer Id => Manufacturer name
        mapping(uint256 => string) nodeIdToManufacturerName;
    }

    /* solhint-disable no-inline-assembly */
    function getStorage() internal pure returns (Storage storage s) {
        bytes32 slot = MANUFACTURER_STORAGE_SLOT;
        assembly {
            s.slot := slot
        }
    }
    /* solhint-enable no-inline-assembly */
}

File 6 of 18 : VehicleStorage.sol
//SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.13;

import "../AttributeSet.sol";

/// @title VehicleStorage
/// @notice Storage of the Vehicle contract
library VehicleStorage {
    using AttributeSet for AttributeSet.Set;

    bytes32 private constant VEHICLE_STORAGE_SLOT =
        keccak256("DIMORegistry.vehicle.storage");

    struct Storage {
        address idProxyAddress;
        // Allowed node attribute
        AttributeSet.Set whitelistedAttributes;
    }

    /* solhint-disable no-inline-assembly */
    function getStorage() internal pure returns (Storage storage s) {
        bytes32 slot = VEHICLE_STORAGE_SLOT;
        assembly {
            s.slot := slot
        }
    }
    /* solhint-enable no-inline-assembly */
}

File 7 of 18 : Roles.sol
//SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.13;

bytes32 constant DEFAULT_ADMIN_ROLE = 0x00;
bytes32 constant MINTER_ROLE = keccak256("Minter");
bytes32 constant MANUFACTURER_ROLE = keccak256("Manufacturer");

File 8 of 18 : Types.sol
//SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.13;

/// @notice File to store shared structs

struct AttributeInfoPair {
    string attribute;
    string info;
}

struct AftermarketDeviceInfos {
    address addr;
    AttributeInfoPair[] attrInfoPairs;
}

struct AftermarketDeviceOwnerPair {
    uint256 aftermarketDeviceNodeId;
    address owner;
}

File 9 of 18 : AccessControlInternal.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import { EnumerableSet } from '../../data/EnumerableSet.sol';
import { AddressUtils } from '../../utils/AddressUtils.sol';
import { UintUtils } from '../../utils/UintUtils.sol';
import { IAccessControlInternal } from './IAccessControlInternal.sol';
import { AccessControlStorage } from './AccessControlStorage.sol';

/**
 * @title Role-based access control system
 * @dev derived from https://github.com/OpenZeppelin/openzeppelin-contracts (MIT license)
 */
abstract contract AccessControlInternal is IAccessControlInternal {
    using AddressUtils for address;
    using EnumerableSet for EnumerableSet.AddressSet;
    using UintUtils for uint256;

    modifier onlyRole(bytes32 role) {
        _checkRole(role);
        _;
    }

    /*
     * @notice query whether role is assigned to account
     * @param role role to query
     * @param account account to query
     * @return whether role is assigned to account
     */
    function _hasRole(bytes32 role, address account)
        internal
        view
        virtual
        returns (bool)
    {
        return
            AccessControlStorage.layout().roles[role].members.contains(account);
    }

    /**
     * @notice revert if sender does not have given role
     * @param role role to query
     */
    function _checkRole(bytes32 role) internal view virtual {
        _checkRole(role, msg.sender);
    }

    /**
     * @notice revert if given account does not have given role
     * @param role role to query
     * @param account to query
     */
    function _checkRole(bytes32 role, address account) internal view virtual {
        if (!_hasRole(role, account)) {
            revert(
                string(
                    abi.encodePacked(
                        'AccessControl: account ',
                        account.toString(),
                        ' is missing role ',
                        uint256(role).toHexString(32)
                    )
                )
            );
        }
    }

    /*
     * @notice query admin role for given role
     * @param role role to query
     * @return admin role
     */
    function _getRoleAdmin(bytes32 role)
        internal
        view
        virtual
        returns (bytes32)
    {
        return AccessControlStorage.layout().roles[role].adminRole;
    }

    /**
     * @notice set role as admin role
     * @param role role to set
     * @param adminRole admin role to set
     */
    function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual {
        bytes32 previousAdminRole = _getRoleAdmin(role);
        AccessControlStorage.layout().roles[role].adminRole = adminRole;
        emit RoleAdminChanged(role, previousAdminRole, adminRole);
    }

    /*
     * @notice assign role to given account
     * @param role role to assign
     * @param account recipient of role assignment
     */
    function _grantRole(bytes32 role, address account) internal virtual {
        AccessControlStorage.layout().roles[role].members.add(account);
        emit RoleGranted(role, account, msg.sender);
    }

    /*
     * @notice unassign role from given account
     * @param role role to unassign
     * @parm account
     */
    function _revokeRole(bytes32 role, address account) internal virtual {
        AccessControlStorage.layout().roles[role].members.remove(account);
        emit RoleRevoked(role, account, msg.sender);
    }

    /**
     * @notice relinquish role
     * @param role role to relinquish
     */
    function _renounceRole(bytes32 role) internal virtual {
        _revokeRole(role, msg.sender);
    }
}

File 10 of 18 : Eip712CheckerStorage.sol
//SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.13;

/// @title Eip712CheckerStorage
/// @notice Storage of the Eip712Checker contract
library Eip712CheckerStorage {
    bytes32 internal constant EIP712_CHECKER_STORAGE_SLOT =
        keccak256("DIMORegistry.eip712Checker.storage");

    struct Storage {
        bytes32 name;
        bytes32 version;
    }

    /* solhint-disable no-inline-assembly */
    function getStorage() internal pure returns (Storage storage s) {
        bytes32 slot = EIP712_CHECKER_STORAGE_SLOT;
        assembly {
            s.slot := slot
        }
    }
    /* solhint-enable no-inline-assembly */
}

File 11 of 18 : ECDSA.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.0) (utils/cryptography/ECDSA.sol)

pragma solidity ^0.8.0;

import "../Strings.sol";

/**
 * @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations.
 *
 * These functions can be used to verify that a message was signed by the holder
 * of the private keys of a given address.
 */
library ECDSA {
    enum RecoverError {
        NoError,
        InvalidSignature,
        InvalidSignatureLength,
        InvalidSignatureS,
        InvalidSignatureV
    }

    function _throwError(RecoverError error) private pure {
        if (error == RecoverError.NoError) {
            return; // no error: do nothing
        } else if (error == RecoverError.InvalidSignature) {
            revert("ECDSA: invalid signature");
        } else if (error == RecoverError.InvalidSignatureLength) {
            revert("ECDSA: invalid signature length");
        } else if (error == RecoverError.InvalidSignatureS) {
            revert("ECDSA: invalid signature 's' value");
        } else if (error == RecoverError.InvalidSignatureV) {
            revert("ECDSA: invalid signature 'v' value");
        }
    }

    /**
     * @dev Returns the address that signed a hashed message (`hash`) with
     * `signature` or error string. This address can then be used for verification purposes.
     *
     * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
     * this function rejects them by requiring the `s` value to be in the lower
     * half order, and the `v` value to be either 27 or 28.
     *
     * IMPORTANT: `hash` _must_ be the result of a hash operation for the
     * verification to be secure: it is possible to craft signatures that
     * recover to arbitrary addresses for non-hashed data. A safe way to ensure
     * this is by receiving a hash of the original message (which may otherwise
     * be too long), and then calling {toEthSignedMessageHash} on it.
     *
     * Documentation for signature generation:
     * - with https://web3js.readthedocs.io/en/v1.3.4/web3-eth-accounts.html#sign[Web3.js]
     * - with https://docs.ethers.io/v5/api/signer/#Signer-signMessage[ethers]
     *
     * _Available since v4.3._
     */
    function tryRecover(bytes32 hash, bytes memory signature) internal pure returns (address, RecoverError) {
        // Check the signature length
        // - case 65: r,s,v signature (standard)
        // - case 64: r,vs signature (cf https://eips.ethereum.org/EIPS/eip-2098) _Available since v4.1._
        if (signature.length == 65) {
            bytes32 r;
            bytes32 s;
            uint8 v;
            // ecrecover takes the signature parameters, and the only way to get them
            // currently is to use assembly.
            assembly {
                r := mload(add(signature, 0x20))
                s := mload(add(signature, 0x40))
                v := byte(0, mload(add(signature, 0x60)))
            }
            return tryRecover(hash, v, r, s);
        } else if (signature.length == 64) {
            bytes32 r;
            bytes32 vs;
            // ecrecover takes the signature parameters, and the only way to get them
            // currently is to use assembly.
            assembly {
                r := mload(add(signature, 0x20))
                vs := mload(add(signature, 0x40))
            }
            return tryRecover(hash, r, vs);
        } else {
            return (address(0), RecoverError.InvalidSignatureLength);
        }
    }

    /**
     * @dev Returns the address that signed a hashed message (`hash`) with
     * `signature`. This address can then be used for verification purposes.
     *
     * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
     * this function rejects them by requiring the `s` value to be in the lower
     * half order, and the `v` value to be either 27 or 28.
     *
     * IMPORTANT: `hash` _must_ be the result of a hash operation for the
     * verification to be secure: it is possible to craft signatures that
     * recover to arbitrary addresses for non-hashed data. A safe way to ensure
     * this is by receiving a hash of the original message (which may otherwise
     * be too long), and then calling {toEthSignedMessageHash} on it.
     */
    function recover(bytes32 hash, bytes memory signature) internal pure returns (address) {
        (address recovered, RecoverError error) = tryRecover(hash, signature);
        _throwError(error);
        return recovered;
    }

    /**
     * @dev Overload of {ECDSA-tryRecover} that receives the `r` and `vs` short-signature fields separately.
     *
     * See https://eips.ethereum.org/EIPS/eip-2098[EIP-2098 short signatures]
     *
     * _Available since v4.3._
     */
    function tryRecover(
        bytes32 hash,
        bytes32 r,
        bytes32 vs
    ) internal pure returns (address, RecoverError) {
        bytes32 s = vs & bytes32(0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff);
        uint8 v = uint8((uint256(vs) >> 255) + 27);
        return tryRecover(hash, v, r, s);
    }

    /**
     * @dev Overload of {ECDSA-recover} that receives the `r and `vs` short-signature fields separately.
     *
     * _Available since v4.2._
     */
    function recover(
        bytes32 hash,
        bytes32 r,
        bytes32 vs
    ) internal pure returns (address) {
        (address recovered, RecoverError error) = tryRecover(hash, r, vs);
        _throwError(error);
        return recovered;
    }

    /**
     * @dev Overload of {ECDSA-tryRecover} that receives the `v`,
     * `r` and `s` signature fields separately.
     *
     * _Available since v4.3._
     */
    function tryRecover(
        bytes32 hash,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) internal pure returns (address, RecoverError) {
        // EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature
        // unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines
        // the valid range for s in (301): 0 < s < secp256k1n ÷ 2 + 1, and for v in (302): v ∈ {27, 28}. Most
        // signatures from current libraries generate a unique signature with an s-value in the lower half order.
        //
        // If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value
        // with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or
        // vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept
        // these malleable signatures as well.
        if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) {
            return (address(0), RecoverError.InvalidSignatureS);
        }
        if (v != 27 && v != 28) {
            return (address(0), RecoverError.InvalidSignatureV);
        }

        // If the signature is valid (and not malleable), return the signer address
        address signer = ecrecover(hash, v, r, s);
        if (signer == address(0)) {
            return (address(0), RecoverError.InvalidSignature);
        }

        return (signer, RecoverError.NoError);
    }

    /**
     * @dev Overload of {ECDSA-recover} that receives the `v`,
     * `r` and `s` signature fields separately.
     */
    function recover(
        bytes32 hash,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) internal pure returns (address) {
        (address recovered, RecoverError error) = tryRecover(hash, v, r, s);
        _throwError(error);
        return recovered;
    }

    /**
     * @dev Returns an Ethereum Signed Message, created from a `hash`. This
     * produces hash corresponding to the one signed with the
     * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]
     * JSON-RPC method as part of EIP-191.
     *
     * See {recover}.
     */
    function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32) {
        // 32 is the length in bytes of hash,
        // enforced by the type signature above
        return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", hash));
    }

    /**
     * @dev Returns an Ethereum Signed Message, created from `s`. This
     * produces hash corresponding to the one signed with the
     * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]
     * JSON-RPC method as part of EIP-191.
     *
     * See {recover}.
     */
    function toEthSignedMessageHash(bytes memory s) internal pure returns (bytes32) {
        return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n", Strings.toString(s.length), s));
    }

    /**
     * @dev Returns an Ethereum Signed Typed Data, created from a
     * `domainSeparator` and a `structHash`. This produces hash corresponding
     * to the one signed with the
     * https://eips.ethereum.org/EIPS/eip-712[`eth_signTypedData`]
     * JSON-RPC method as part of EIP-712.
     *
     * See {recover}.
     */
    function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32) {
        return keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash));
    }
}

File 12 of 18 : Strings.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/Strings.sol)

pragma solidity ^0.8.0;

/**
 * @dev String operations.
 */
library Strings {
    bytes16 private constant _HEX_SYMBOLS = "0123456789abcdef";

    /**
     * @dev Converts a `uint256` to its ASCII `string` decimal representation.
     */
    function toString(uint256 value) internal pure returns (string memory) {
        // Inspired by OraclizeAPI's implementation - MIT licence
        // https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol

        if (value == 0) {
            return "0";
        }
        uint256 temp = value;
        uint256 digits;
        while (temp != 0) {
            digits++;
            temp /= 10;
        }
        bytes memory buffer = new bytes(digits);
        while (value != 0) {
            digits -= 1;
            buffer[digits] = bytes1(uint8(48 + uint256(value % 10)));
            value /= 10;
        }
        return string(buffer);
    }

    /**
     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
     */
    function toHexString(uint256 value) internal pure returns (string memory) {
        if (value == 0) {
            return "0x00";
        }
        uint256 temp = value;
        uint256 length = 0;
        while (temp != 0) {
            length++;
            temp >>= 8;
        }
        return toHexString(value, length);
    }

    /**
     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
     */
    function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
        bytes memory buffer = new bytes(2 * length + 2);
        buffer[0] = "0";
        buffer[1] = "x";
        for (uint256 i = 2 * length + 1; i > 1; --i) {
            buffer[i] = _HEX_SYMBOLS[value & 0xf];
            value >>= 4;
        }
        require(value == 0, "Strings: hex length insufficient");
        return string(buffer);
    }
}

File 13 of 18 : AttributeSet.sol
//SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.13;

/// @dev derived from https://github.com/OpenZeppelin/openzeppelin-contracts (MIT license)

library AttributeSet {
    struct Set {
        string[] _values;
        mapping(string => uint256) _indexes;
    }

    function add(Set storage set, string calldata key) internal returns (bool) {
        if (!exists(set, key)) {
            set._values.push(key);
            set._indexes[key] = set._values.length;
            return true;
        } else {
            return false;
        }
    }

    function remove(Set storage set, string calldata key)
        internal
        returns (bool)
    {
        uint256 valueIndex = set._indexes[key];

        if (valueIndex != 0) {
            uint256 toDeleteIndex = valueIndex - 1;
            uint256 lastIndex = set._values.length - 1;

            if (lastIndex != toDeleteIndex) {
                string memory lastvalue = set._values[lastIndex];

                // Move the last value to the index where the value to delete is
                set._values[toDeleteIndex] = lastvalue;
                // Update the index for the moved value
                set._indexes[lastvalue] = valueIndex; // Replace lastvalue's index to valueIndex
            }

            // Delete the slot where the moved value was stored
            set._values.pop();

            // Delete the index for the deleted slot
            delete set._indexes[key];

            return true;
        } else {
            return false;
        }
    }

    function count(Set storage set) internal view returns (uint256) {
        return (set._values.length);
    }

    function exists(Set storage set, string calldata key)
        internal
        view
        returns (bool)
    {
        return set._indexes[key] != 0;
    }
}

File 14 of 18 : EnumerableSet.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.8;

/**
 * @title Set implementation with enumeration functions
 * @dev derived from https://github.com/OpenZeppelin/openzeppelin-contracts (MIT license)
 */
library EnumerableSet {
    error EnumerableSet__IndexOutOfBounds();

    struct Set {
        bytes32[] _values;
        // 1-indexed to allow 0 to signify nonexistence
        mapping(bytes32 => uint256) _indexes;
    }

    struct Bytes32Set {
        Set _inner;
    }

    struct AddressSet {
        Set _inner;
    }

    struct UintSet {
        Set _inner;
    }

    function at(Bytes32Set storage set, uint256 index)
        internal
        view
        returns (bytes32)
    {
        return _at(set._inner, index);
    }

    function at(AddressSet storage set, uint256 index)
        internal
        view
        returns (address)
    {
        return address(uint160(uint256(_at(set._inner, index))));
    }

    function at(UintSet storage set, uint256 index)
        internal
        view
        returns (uint256)
    {
        return uint256(_at(set._inner, index));
    }

    function contains(Bytes32Set storage set, bytes32 value)
        internal
        view
        returns (bool)
    {
        return _contains(set._inner, value);
    }

    function contains(AddressSet storage set, address value)
        internal
        view
        returns (bool)
    {
        return _contains(set._inner, bytes32(uint256(uint160(value))));
    }

    function contains(UintSet storage set, uint256 value)
        internal
        view
        returns (bool)
    {
        return _contains(set._inner, bytes32(value));
    }

    function indexOf(Bytes32Set storage set, bytes32 value)
        internal
        view
        returns (uint256)
    {
        return _indexOf(set._inner, value);
    }

    function indexOf(AddressSet storage set, address value)
        internal
        view
        returns (uint256)
    {
        return _indexOf(set._inner, bytes32(uint256(uint160(value))));
    }

    function indexOf(UintSet storage set, uint256 value)
        internal
        view
        returns (uint256)
    {
        return _indexOf(set._inner, bytes32(value));
    }

    function length(Bytes32Set storage set) internal view returns (uint256) {
        return _length(set._inner);
    }

    function length(AddressSet storage set) internal view returns (uint256) {
        return _length(set._inner);
    }

    function length(UintSet storage set) internal view returns (uint256) {
        return _length(set._inner);
    }

    function add(Bytes32Set storage set, bytes32 value)
        internal
        returns (bool)
    {
        return _add(set._inner, value);
    }

    function add(AddressSet storage set, address value)
        internal
        returns (bool)
    {
        return _add(set._inner, bytes32(uint256(uint160(value))));
    }

    function add(UintSet storage set, uint256 value) internal returns (bool) {
        return _add(set._inner, bytes32(value));
    }

    function remove(Bytes32Set storage set, bytes32 value)
        internal
        returns (bool)
    {
        return _remove(set._inner, value);
    }

    function remove(AddressSet storage set, address value)
        internal
        returns (bool)
    {
        return _remove(set._inner, bytes32(uint256(uint160(value))));
    }

    function remove(UintSet storage set, uint256 value)
        internal
        returns (bool)
    {
        return _remove(set._inner, bytes32(value));
    }

    function toArray(Bytes32Set storage set)
        internal
        view
        returns (bytes32[] memory)
    {
        uint256 len = _length(set._inner);
        bytes32[] memory arr = new bytes32[](len);

        unchecked {
            for (uint256 index; index < len; ++index) {
                arr[index] = at(set, index);
            }
        }

        return arr;
    }

    function toArray(AddressSet storage set)
        internal
        view
        returns (address[] memory)
    {
        uint256 len = _length(set._inner);
        address[] memory arr = new address[](len);

        unchecked {
            for (uint256 index; index < len; ++index) {
                arr[index] = at(set, index);
            }
        }

        return arr;
    }

    function toArray(UintSet storage set)
        internal
        view
        returns (uint256[] memory)
    {
        uint256 len = _length(set._inner);
        uint256[] memory arr = new uint256[](len);

        unchecked {
            for (uint256 index; index < len; ++index) {
                arr[index] = at(set, index);
            }
        }

        return arr;
    }

    function _at(Set storage set, uint256 index)
        private
        view
        returns (bytes32)
    {
        if (index >= set._values.length)
            revert EnumerableSet__IndexOutOfBounds();
        return set._values[index];
    }

    function _contains(Set storage set, bytes32 value)
        private
        view
        returns (bool)
    {
        return set._indexes[value] != 0;
    }

    function _indexOf(Set storage set, bytes32 value)
        private
        view
        returns (uint256)
    {
        unchecked {
            return set._indexes[value] - 1;
        }
    }

    function _length(Set storage set) private view returns (uint256) {
        return set._values.length;
    }

    function _add(Set storage set, bytes32 value) private returns (bool) {
        if (!_contains(set, value)) {
            set._values.push(value);
            set._indexes[value] = set._values.length;
            return true;
        } else {
            return false;
        }
    }

    function _remove(Set storage set, bytes32 value) private returns (bool) {
        uint256 valueIndex = set._indexes[value];

        if (valueIndex != 0) {
            unchecked {
                bytes32 last = set._values[set._values.length - 1];

                // move last value to now-vacant index

                set._values[valueIndex - 1] = last;
                set._indexes[last] = valueIndex;
            }
            // clear last index

            set._values.pop();
            delete set._indexes[value];

            return true;
        } else {
            return false;
        }
    }
}

File 15 of 18 : AddressUtils.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.8;

import { UintUtils } from './UintUtils.sol';

library AddressUtils {
    using UintUtils for uint256;

    error AddressUtils__InsufficientBalance();
    error AddressUtils__NotContract();
    error AddressUtils__SendValueFailed();

    function toString(address account) internal pure returns (string memory) {
        return uint256(uint160(account)).toHexString(20);
    }

    function isContract(address account) internal view returns (bool) {
        uint256 size;
        assembly {
            size := extcodesize(account)
        }
        return size > 0;
    }

    function sendValue(address payable account, uint256 amount) internal {
        (bool success, ) = account.call{ value: amount }('');
        if (!success) revert AddressUtils__SendValueFailed();
    }

    function functionCall(address target, bytes memory data)
        internal
        returns (bytes memory)
    {
        return
            functionCall(target, data, 'AddressUtils: failed low-level call');
    }

    function functionCall(
        address target,
        bytes memory data,
        string memory error
    ) internal returns (bytes memory) {
        return _functionCallWithValue(target, data, 0, error);
    }

    function functionCallWithValue(
        address target,
        bytes memory data,
        uint256 value
    ) internal returns (bytes memory) {
        return
            functionCallWithValue(
                target,
                data,
                value,
                'AddressUtils: failed low-level call with value'
            );
    }

    function functionCallWithValue(
        address target,
        bytes memory data,
        uint256 value,
        string memory error
    ) internal returns (bytes memory) {
        if (value > address(this).balance)
            revert AddressUtils__InsufficientBalance();
        return _functionCallWithValue(target, data, value, error);
    }

    function _functionCallWithValue(
        address target,
        bytes memory data,
        uint256 value,
        string memory error
    ) private returns (bytes memory) {
        if (!isContract(target)) revert AddressUtils__NotContract();

        (bool success, bytes memory returnData) = target.call{ value: value }(
            data
        );

        if (success) {
            return returnData;
        } else if (returnData.length > 0) {
            assembly {
                let returnData_size := mload(returnData)
                revert(add(32, returnData), returnData_size)
            }
        } else {
            revert(error);
        }
    }
}

File 16 of 18 : UintUtils.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.8;

/**
 * @title utility functions for uint256 operations
 * @dev derived from https://github.com/OpenZeppelin/openzeppelin-contracts/ (MIT license)
 */
library UintUtils {
    error UintUtils__InsufficientHexLength();

    bytes16 private constant HEX_SYMBOLS = '0123456789abcdef';

    function add(uint256 a, int256 b) internal pure returns (uint256) {
        return b < 0 ? sub(a, -b) : a + uint256(b);
    }

    function sub(uint256 a, int256 b) internal pure returns (uint256) {
        return b < 0 ? add(a, -b) : a - uint256(b);
    }

    function toString(uint256 value) internal pure returns (string memory) {
        if (value == 0) {
            return '0';
        }

        uint256 temp = value;
        uint256 digits;

        while (temp != 0) {
            digits++;
            temp /= 10;
        }

        bytes memory buffer = new bytes(digits);

        while (value != 0) {
            digits -= 1;
            buffer[digits] = bytes1(uint8(48 + uint256(value % 10)));
            value /= 10;
        }

        return string(buffer);
    }

    function toHexString(uint256 value) internal pure returns (string memory) {
        if (value == 0) {
            return '0x00';
        }

        uint256 length = 0;

        for (uint256 temp = value; temp != 0; temp >>= 8) {
            unchecked {
                length++;
            }
        }

        return toHexString(value, length);
    }

    function toHexString(uint256 value, uint256 length)
        internal
        pure
        returns (string memory)
    {
        bytes memory buffer = new bytes(2 * length + 2);
        buffer[0] = '0';
        buffer[1] = 'x';

        unchecked {
            for (uint256 i = 2 * length + 1; i > 1; --i) {
                buffer[i] = HEX_SYMBOLS[value & 0xf];
                value >>= 4;
            }
        }

        if (value != 0) revert UintUtils__InsufficientHexLength();

        return string(buffer);
    }
}

File 17 of 18 : IAccessControlInternal.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

/**
 * @title Partial AccessControl interface needed by internal functions
 */
interface IAccessControlInternal {
    event RoleAdminChanged(
        bytes32 indexed role,
        bytes32 indexed previousAdminRole,
        bytes32 indexed newAdminRole
    );

    event RoleGranted(
        bytes32 indexed role,
        address indexed account,
        address indexed sender
    );

    event RoleRevoked(
        bytes32 indexed role,
        address indexed account,
        address indexed sender
    );
}

File 18 of 18 : AccessControlStorage.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import { EnumerableSet } from '../../data/EnumerableSet.sol';

library AccessControlStorage {
    struct RoleData {
        EnumerableSet.AddressSet members;
        bytes32 adminRole;
    }

    struct Layout {
        mapping(bytes32 => RoleData) roles;
    }

    bytes32 internal constant DEFAULT_ADMIN_ROLE = 0x00;

    bytes32 internal constant STORAGE_SLOT =
        keccak256('solidstate.contracts.storage.AccessControl');

    function layout() internal pure returns (Layout storage l) {
        bytes32 slot = STORAGE_SLOT;
        assembly {
            l.slot := slot
        }
    }
}

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

Contract Security Audit

Contract ABI

[{"inputs":[],"name":"UintUtils__InsufficientHexLength","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"previousAdminRole","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"newAdminRole","type":"bytes32"}],"name":"RoleAdminChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleGranted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleRevoked","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"string","name":"attribute","type":"string"}],"name":"VehicleAttributeAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"tokenId","type":"uint256"},{"indexed":false,"internalType":"string","name":"attribute","type":"string"},{"indexed":false,"internalType":"string","name":"info","type":"string"}],"name":"VehicleAttributeSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"proxy","type":"address"}],"name":"VehicleIdProxySet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"tokenId","type":"uint256"},{"indexed":false,"internalType":"address","name":"owner","type":"address"}],"name":"VehicleNodeMinted","type":"event"},{"inputs":[{"internalType":"string","name":"attribute","type":"string"}],"name":"addVehicleAttribute","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"manufacturerNode","type":"uint256"},{"internalType":"address","name":"owner","type":"address"},{"components":[{"internalType":"string","name":"attribute","type":"string"},{"internalType":"string","name":"info","type":"string"}],"internalType":"struct AttributeInfoPair[]","name":"attrInfo","type":"tuple[]"}],"name":"mintVehicle","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"manufacturerNode","type":"uint256"},{"internalType":"address","name":"owner","type":"address"},{"components":[{"internalType":"string","name":"attribute","type":"string"},{"internalType":"string","name":"info","type":"string"}],"internalType":"struct AttributeInfoPair[]","name":"attrInfo","type":"tuple[]"},{"internalType":"bytes","name":"signature","type":"bytes"}],"name":"mintVehicleSign","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"addr","type":"address"}],"name":"setVehicleIdProxyAddress","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"},{"components":[{"internalType":"string","name":"attribute","type":"string"},{"internalType":"string","name":"info","type":"string"}],"internalType":"struct AttributeInfoPair[]","name":"attrInfo","type":"tuple[]"}],"name":"setVehicleInfo","outputs":[],"stateMutability":"nonpayable","type":"function"}]

608060405234801561001057600080fd5b50611b96806100206000396000f3fe608060405234801561001057600080fd5b50600436106100575760003560e01c80631b1a82c81461005c5780633da44e56146100715780639bfae6da14610084578063d9c3ae6114610097578063f0d1a557146100aa575b600080fd5b61006f61006a366004611677565b6100bd565b005b61006f61007f366004611701565b61037a565b61006f61009236600461175b565b610568565b61006f6100a5366004611776565b610614565b61006f6100b83660046117c2565b6106ed565b60006100c8816107ad565b6000600080516020611b2183398151915290506000600080516020611b41833981519152546001600160a01b031690507fb3aebca153c1c49c2de51244227bd8a78635de6ee19593288cf651404b08e5dc54604051634f558e7960e01b8152600481018b90526001600160a01b0390911690634f558e7990602401602060405180830381865afa158015610160573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906101849190611804565b6101cb5760405162461bcd60e51b8152602060048201526013602482015272496e76616c696420706172656e74206e6f646560681b60448201526064015b60405180910390fd5b6040516340d097c360e01b81526001600160a01b038981166004830152600091908316906340d097c3906024016020604051808303816000875af1158015610217573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061023b9190611826565b6001600160a01b03831660009081526020858152604080832084845290915281208c90559091508061026e838b8b6107ba565b604080517fcfd18b359f1adef30e06ee2a373b6d895b82c4db3f547c5dd574c6d272b7912160208201529081018f90526001600160a01b038e1660608201526080810183905260a08101829052919350915060009060c0016040516020818303038152906040528051906020012090506102ea8c828b8b610b93565b61032a5760405162461bcd60e51b8152602060048201526011602482015270496e76616c6964207369676e617475726560781b60448201526064016101c2565b604080518581526001600160a01b038e1660208201527f09ec7fe5281be92443463e1061ce315afc1142b6c31c98a90b711012a54cc32f910160405180910390a150505050505050505050505050565b6000610385816107ad565b6000600080516020611b41833981519152546001600160a01b031690507fb3aebca153c1c49c2de51244227bd8a78635de6ee19593288cf651404b08e5dc54604051634f558e7960e01b8152600481018890526001600160a01b0390911690634f558e7990602401602060405180830381865afa15801561040a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061042e9190611804565b6104705760405162461bcd60e51b8152602060048201526013602482015272496e76616c696420706172656e74206e6f646560681b60448201526064016101c2565b6040516340d097c360e01b81526001600160a01b038681166004830152600091908316906340d097c3906024016020604051808303816000875af11580156104bc573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906104e09190611826565b905086600080516020611b218339815191526001600160a01b038416600090815260209182526040808220858352909252205561051e818686610d16565b604080518281526001600160a01b03881660208201527f09ec7fe5281be92443463e1061ce315afc1142b6c31c98a90b711012a54cc32f910160405180910390a150505050505050565b6000610573816107ad565b6001600160a01b0382166105bc5760405162461bcd60e51b815260206004820152601060248201526f4e6f6e207a65726f206164647265737360801b60448201526064016101c2565b600080516020611b4183398151915280546001600160a01b0319166001600160a01b0384169081179091556040517f3e7484c4e57f7d92e9f02eba6cd805d89112e48db8c21aeb8485fcf0020e479d90600090a25050565b600061061f816107ad565b600080516020611b4183398151915254604051634f558e7960e01b8152600481018690526001600160a01b0390911690634f558e7990602401602060405180830381865afa158015610675573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106999190611804565b6106dc5760405162461bcd60e51b8152602060048201526014602482015273496e76616c69642076656869636c65206e6f646560601b60448201526064016101c2565b6106e7848484610d16565b50505050565b60006106f8816107ad565b6107237fec68e743e11f3509477f41b0ec121b040a15d46b2c24a1935aa31ef46eca17ae8484610f06565b61076f5760405162461bcd60e51b815260206004820152601860248201527f41747472696275746520616c726561647920657869737473000000000000000060448201526064016101c2565b7f2b7d41dc33ffd58029f53ebfc3232e4f343480b078458bc17c527583e0172c1a83836040516107a0929190611868565b60405180910390a1505050565b6107b78133610f6f565b50565b600080516020611b4183398151915280546000918291600080516020611b2183398151915291906001600160a01b0316838667ffffffffffffffff81111561080457610804611884565b60405190808252806020026020018201604052801561082d578160200160208202803683370190505b50905060008767ffffffffffffffff81111561084b5761084b611884565b604051908082528060200260200182016040528015610874578160200160208202803683370190505b50905060005b88811015610b33576108bc856001018b8b8481811061089b5761089b61189a565b90506020028101906108ad91906118b0565b6108b790806118d0565b610fd5565b6108fa5760405162461bcd60e51b815260206004820152600f60248201526e139bdd081dda1a5d195b1a5cdd1959608a1b60448201526064016101c2565b89898281811061090c5761090c61189a565b905060200281019061091e91906118b0565b61092890806118d0565b604051610936929190611917565b60405180910390208382815181106109505761095061189a565b60200260200101818152505089898281811061096e5761096e61189a565b905060200281019061098091906118b0565b61098e9060208101906118d0565b60405161099c929190611917565b60405180910390208282815181106109b6576109b661189a565b6020026020010181815250508989828181106109d4576109d461189a565b90506020028101906109e691906118b0565b6109f49060208101906118d0565b876000016000876001600160a01b03166001600160a01b0316815260200190815260200160002060008e81526020019081526020016000206001018c8c85818110610a4157610a4161189a565b9050602002810190610a5391906118b0565b610a5d90806118d0565b604051610a6b929190611917565b908152604051908190036020019020610a8592909161153b565b507f3a259e5d4c53f11c343582a8291a82a8cc0b36ec211d5ab48c2f29ebb068e5fb8b8b8b84818110610aba57610aba61189a565b9050602002810190610acc91906118b0565b610ad690806118d0565b8d8d86818110610ae857610ae861189a565b9050602002810190610afa91906118b0565b610b089060208101906118d0565b604051610b19959493929190611927565b60405180910390a180610b2b81611976565b91505061087a565b5081604051602001610b45919061198f565b6040516020818303038152906040528051906020012081604051602001610b6c919061198f565b60405160208183030381529060405280519060200120965096505050505050935093915050565b60006001600160a01b038516610beb5760405162461bcd60e51b815260206004820152601d60248201527f45434453413a207a65726f207369676e61746f7279206164647265737300000060448201526064016101c2565b6000610c7f6000807f1caba0f9c68661100ca506c628d69817ec17e818730121b067a9c57efc89b27280546001820154604080517f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f60208201529081019290925260608201524660808201523060a082015290915060c0016040516020818303038152906040528051906020012091505090565b60405161190160f01b6020820152602281019190915260428101869052606201604051602081830303815290604052805190602001209050610cf78185858080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061100592505050565b6001600160a01b0316866001600160a01b031614915050949350505050565b600080516020611b418339815191528054600080516020611b2183398151915291906001600160a01b031660005b84811015610efd57610d658360010187878481811061089b5761089b61189a565b610da35760405162461bcd60e51b815260206004820152600f60248201526e139bdd081dda1a5d195b1a5cdd1959608a1b60448201526064016101c2565b858582818110610db557610db561189a565b9050602002810190610dc791906118b0565b610dd59060208101906118d0565b6001600160a01b0384166000908152602087815260408083208c84529091529020600101888885818110610e0b57610e0b61189a565b9050602002810190610e1d91906118b0565b610e2790806118d0565b604051610e35929190611917565b908152604051908190036020019020610e4f92909161153b565b507f3a259e5d4c53f11c343582a8291a82a8cc0b36ec211d5ab48c2f29ebb068e5fb87878784818110610e8457610e8461189a565b9050602002810190610e9691906118b0565b610ea090806118d0565b898986818110610eb257610eb261189a565b9050602002810190610ec491906118b0565b610ed29060208101906118d0565b604051610ee3959493929190611927565b60405180910390a180610ef581611976565b915050610d44565b50505050505050565b6000610f13848484610fd5565b610f64578354600181018555600085815260209020610f349101848461153b565b5083546040516001860190610f4c9086908690611917565b90815260405190819003602001902055506001610f68565b5060005b9392505050565b610f798282611029565b610fd157610f8f816001600160a01b0316611060565b610f9a83602061107c565b604051602001610fab9291906119f1565b60408051601f198184030181529082905262461bcd60e51b82526101c291600401611a66565b5050565b6000836001018383604051610feb929190611917565b908152604051908190036020019020541515949350505050565b600080600061101485856111cf565b915091506110218161123d565b509392505050565b60008281527fd3889cc5458b268d0544e5534672df1296288ca3f93cbd39bd6f497a5c62281160205260408120610f6890836113f3565b60606110766001600160a01b038316601461107c565b92915050565b6060600061108b836002611a99565b611096906002611ab8565b67ffffffffffffffff8111156110ae576110ae611884565b6040519080825280601f01601f1916602001820160405280156110d8576020820181803683370190505b509050600360fc1b816000815181106110f3576110f361189a565b60200101906001600160f81b031916908160001a905350600f60fb1b816001815181106111225761112261189a565b60200101906001600160f81b031916908160001a905350600160028402015b60018111156111af576f181899199a1a9b1b9c1cb0b131b232b360811b85600f16601081106111725761117261189a565b1a60f81b8282815181106111885761118861189a565b60200101906001600160f81b031916908160001a90535060049490941c9360001901611141565b508315610f685760405163c913478560e01b815260040160405180910390fd5b60008082516041036112055760208301516040840151606085015160001a6111f987828585611415565b94509450505050611236565b825160400361122e5760208301516040840151611223868383611502565b935093505050611236565b506000905060025b9250929050565b600081600481111561125157611251611ad0565b036112595750565b600181600481111561126d5761126d611ad0565b036112ba5760405162461bcd60e51b815260206004820152601860248201527f45434453413a20696e76616c6964207369676e6174757265000000000000000060448201526064016101c2565b60028160048111156112ce576112ce611ad0565b0361131b5760405162461bcd60e51b815260206004820152601f60248201527f45434453413a20696e76616c6964207369676e6174757265206c656e6774680060448201526064016101c2565b600381600481111561132f5761132f611ad0565b036113875760405162461bcd60e51b815260206004820152602260248201527f45434453413a20696e76616c6964207369676e6174757265202773272076616c604482015261756560f01b60648201526084016101c2565b600481600481111561139b5761139b611ad0565b036107b75760405162461bcd60e51b815260206004820152602260248201527f45434453413a20696e76616c6964207369676e6174757265202776272076616c604482015261756560f01b60648201526084016101c2565b6001600160a01b03811660009081526001830160205260408120541515610f68565b6000807f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a083111561144c57506000905060036114f9565b8460ff16601b1415801561146457508460ff16601c14155b1561147557506000905060046114f9565b6040805160008082526020820180845289905260ff881692820192909252606081018690526080810185905260019060a0016020604051602081039080840390855afa1580156114c9573d6000803e3d6000fd5b5050604051601f1901519150506001600160a01b0381166114f2576000600192509250506114f9565b9150600090505b94509492505050565b6000806001600160ff1b0383168161151f60ff86901c601b611ab8565b905061152d87828885611415565b935093505050935093915050565b82805461154790611ae6565b90600052602060002090601f01602090048101928261156957600085556115af565b82601f106115825782800160ff198235161785556115af565b828001600101855582156115af579182015b828111156115af578235825591602001919060010190611594565b506115bb9291506115bf565b5090565b5b808211156115bb57600081556001016115c0565b80356001600160a01b03811681146115eb57600080fd5b919050565b60008083601f84011261160257600080fd5b50813567ffffffffffffffff81111561161a57600080fd5b6020830191508360208260051b850101111561123657600080fd5b60008083601f84011261164757600080fd5b50813567ffffffffffffffff81111561165f57600080fd5b60208301915083602082850101111561123657600080fd5b6000806000806000806080878903121561169057600080fd5b863595506116a0602088016115d4565b9450604087013567ffffffffffffffff808211156116bd57600080fd5b6116c98a838b016115f0565b909650945060608901359150808211156116e257600080fd5b506116ef89828a01611635565b979a9699509497509295939492505050565b6000806000806060858703121561171757600080fd5b84359350611727602086016115d4565b9250604085013567ffffffffffffffff81111561174357600080fd5b61174f878288016115f0565b95989497509550505050565b60006020828403121561176d57600080fd5b610f68826115d4565b60008060006040848603121561178b57600080fd5b83359250602084013567ffffffffffffffff8111156117a957600080fd5b6117b5868287016115f0565b9497909650939450505050565b600080602083850312156117d557600080fd5b823567ffffffffffffffff8111156117ec57600080fd5b6117f885828601611635565b90969095509350505050565b60006020828403121561181657600080fd5b81518015158114610f6857600080fd5b60006020828403121561183857600080fd5b5051919050565b81835281816020850137506000828201602090810191909152601f909101601f19169091010190565b60208152600061187c60208301848661183f565b949350505050565b634e487b7160e01b600052604160045260246000fd5b634e487b7160e01b600052603260045260246000fd5b60008235603e198336030181126118c657600080fd5b9190910192915050565b6000808335601e198436030181126118e757600080fd5b83018035915067ffffffffffffffff82111561190257600080fd5b60200191503681900382131561123657600080fd5b8183823760009101908152919050565b85815260606020820152600061194160608301868861183f565b828103604084015261195481858761183f565b98975050505050505050565b634e487b7160e01b600052601160045260246000fd5b60006001820161198857611988611960565b5060010190565b815160009082906020808601845b838110156119b95781518552938201939082019060010161199d565b50929695505050505050565b60005b838110156119e05781810151838201526020016119c8565b838111156106e75750506000910152565b7f416363657373436f6e74726f6c3a206163636f756e7420000000000000000000815260008351611a298160178501602088016119c5565b7001034b99036b4b9b9b4b733903937b6329607d1b6017918401918201528351611a5a8160288401602088016119c5565b01602801949350505050565b6020815260008251806020840152611a858160408501602087016119c5565b601f01601f19169190910160400192915050565b6000816000190483118215151615611ab357611ab3611960565b500290565b60008219821115611acb57611acb611960565b500190565b634e487b7160e01b600052602160045260246000fd5b600181811c90821680611afa57607f821691505b602082108103611b1a57634e487b7160e01b600052602260045260246000fd5b5091905056fe9ecf8425a6266511746b403af66b69eb2b277b7b757807fa21eac9474b45ab3fec68e743e11f3509477f41b0ec121b040a15d46b2c24a1935aa31ef46eca17ada2646970667358221220ffbc87219bdfc99084fb32de4a8a340471216b94f9525f8f4e778b778d3e587e64736f6c634300080d0033

Deployed Bytecode

0x608060405234801561001057600080fd5b50600436106100575760003560e01c80631b1a82c81461005c5780633da44e56146100715780639bfae6da14610084578063d9c3ae6114610097578063f0d1a557146100aa575b600080fd5b61006f61006a366004611677565b6100bd565b005b61006f61007f366004611701565b61037a565b61006f61009236600461175b565b610568565b61006f6100a5366004611776565b610614565b61006f6100b83660046117c2565b6106ed565b60006100c8816107ad565b6000600080516020611b2183398151915290506000600080516020611b41833981519152546001600160a01b031690507fb3aebca153c1c49c2de51244227bd8a78635de6ee19593288cf651404b08e5dc54604051634f558e7960e01b8152600481018b90526001600160a01b0390911690634f558e7990602401602060405180830381865afa158015610160573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906101849190611804565b6101cb5760405162461bcd60e51b8152602060048201526013602482015272496e76616c696420706172656e74206e6f646560681b60448201526064015b60405180910390fd5b6040516340d097c360e01b81526001600160a01b038981166004830152600091908316906340d097c3906024016020604051808303816000875af1158015610217573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061023b9190611826565b6001600160a01b03831660009081526020858152604080832084845290915281208c90559091508061026e838b8b6107ba565b604080517fcfd18b359f1adef30e06ee2a373b6d895b82c4db3f547c5dd574c6d272b7912160208201529081018f90526001600160a01b038e1660608201526080810183905260a08101829052919350915060009060c0016040516020818303038152906040528051906020012090506102ea8c828b8b610b93565b61032a5760405162461bcd60e51b8152602060048201526011602482015270496e76616c6964207369676e617475726560781b60448201526064016101c2565b604080518581526001600160a01b038e1660208201527f09ec7fe5281be92443463e1061ce315afc1142b6c31c98a90b711012a54cc32f910160405180910390a150505050505050505050505050565b6000610385816107ad565b6000600080516020611b41833981519152546001600160a01b031690507fb3aebca153c1c49c2de51244227bd8a78635de6ee19593288cf651404b08e5dc54604051634f558e7960e01b8152600481018890526001600160a01b0390911690634f558e7990602401602060405180830381865afa15801561040a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061042e9190611804565b6104705760405162461bcd60e51b8152602060048201526013602482015272496e76616c696420706172656e74206e6f646560681b60448201526064016101c2565b6040516340d097c360e01b81526001600160a01b038681166004830152600091908316906340d097c3906024016020604051808303816000875af11580156104bc573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906104e09190611826565b905086600080516020611b218339815191526001600160a01b038416600090815260209182526040808220858352909252205561051e818686610d16565b604080518281526001600160a01b03881660208201527f09ec7fe5281be92443463e1061ce315afc1142b6c31c98a90b711012a54cc32f910160405180910390a150505050505050565b6000610573816107ad565b6001600160a01b0382166105bc5760405162461bcd60e51b815260206004820152601060248201526f4e6f6e207a65726f206164647265737360801b60448201526064016101c2565b600080516020611b4183398151915280546001600160a01b0319166001600160a01b0384169081179091556040517f3e7484c4e57f7d92e9f02eba6cd805d89112e48db8c21aeb8485fcf0020e479d90600090a25050565b600061061f816107ad565b600080516020611b4183398151915254604051634f558e7960e01b8152600481018690526001600160a01b0390911690634f558e7990602401602060405180830381865afa158015610675573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106999190611804565b6106dc5760405162461bcd60e51b8152602060048201526014602482015273496e76616c69642076656869636c65206e6f646560601b60448201526064016101c2565b6106e7848484610d16565b50505050565b60006106f8816107ad565b6107237fec68e743e11f3509477f41b0ec121b040a15d46b2c24a1935aa31ef46eca17ae8484610f06565b61076f5760405162461bcd60e51b815260206004820152601860248201527f41747472696275746520616c726561647920657869737473000000000000000060448201526064016101c2565b7f2b7d41dc33ffd58029f53ebfc3232e4f343480b078458bc17c527583e0172c1a83836040516107a0929190611868565b60405180910390a1505050565b6107b78133610f6f565b50565b600080516020611b4183398151915280546000918291600080516020611b2183398151915291906001600160a01b0316838667ffffffffffffffff81111561080457610804611884565b60405190808252806020026020018201604052801561082d578160200160208202803683370190505b50905060008767ffffffffffffffff81111561084b5761084b611884565b604051908082528060200260200182016040528015610874578160200160208202803683370190505b50905060005b88811015610b33576108bc856001018b8b8481811061089b5761089b61189a565b90506020028101906108ad91906118b0565b6108b790806118d0565b610fd5565b6108fa5760405162461bcd60e51b815260206004820152600f60248201526e139bdd081dda1a5d195b1a5cdd1959608a1b60448201526064016101c2565b89898281811061090c5761090c61189a565b905060200281019061091e91906118b0565b61092890806118d0565b604051610936929190611917565b60405180910390208382815181106109505761095061189a565b60200260200101818152505089898281811061096e5761096e61189a565b905060200281019061098091906118b0565b61098e9060208101906118d0565b60405161099c929190611917565b60405180910390208282815181106109b6576109b661189a565b6020026020010181815250508989828181106109d4576109d461189a565b90506020028101906109e691906118b0565b6109f49060208101906118d0565b876000016000876001600160a01b03166001600160a01b0316815260200190815260200160002060008e81526020019081526020016000206001018c8c85818110610a4157610a4161189a565b9050602002810190610a5391906118b0565b610a5d90806118d0565b604051610a6b929190611917565b908152604051908190036020019020610a8592909161153b565b507f3a259e5d4c53f11c343582a8291a82a8cc0b36ec211d5ab48c2f29ebb068e5fb8b8b8b84818110610aba57610aba61189a565b9050602002810190610acc91906118b0565b610ad690806118d0565b8d8d86818110610ae857610ae861189a565b9050602002810190610afa91906118b0565b610b089060208101906118d0565b604051610b19959493929190611927565b60405180910390a180610b2b81611976565b91505061087a565b5081604051602001610b45919061198f565b6040516020818303038152906040528051906020012081604051602001610b6c919061198f565b60405160208183030381529060405280519060200120965096505050505050935093915050565b60006001600160a01b038516610beb5760405162461bcd60e51b815260206004820152601d60248201527f45434453413a207a65726f207369676e61746f7279206164647265737300000060448201526064016101c2565b6000610c7f6000807f1caba0f9c68661100ca506c628d69817ec17e818730121b067a9c57efc89b27280546001820154604080517f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f60208201529081019290925260608201524660808201523060a082015290915060c0016040516020818303038152906040528051906020012091505090565b60405161190160f01b6020820152602281019190915260428101869052606201604051602081830303815290604052805190602001209050610cf78185858080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061100592505050565b6001600160a01b0316866001600160a01b031614915050949350505050565b600080516020611b418339815191528054600080516020611b2183398151915291906001600160a01b031660005b84811015610efd57610d658360010187878481811061089b5761089b61189a565b610da35760405162461bcd60e51b815260206004820152600f60248201526e139bdd081dda1a5d195b1a5cdd1959608a1b60448201526064016101c2565b858582818110610db557610db561189a565b9050602002810190610dc791906118b0565b610dd59060208101906118d0565b6001600160a01b0384166000908152602087815260408083208c84529091529020600101888885818110610e0b57610e0b61189a565b9050602002810190610e1d91906118b0565b610e2790806118d0565b604051610e35929190611917565b908152604051908190036020019020610e4f92909161153b565b507f3a259e5d4c53f11c343582a8291a82a8cc0b36ec211d5ab48c2f29ebb068e5fb87878784818110610e8457610e8461189a565b9050602002810190610e9691906118b0565b610ea090806118d0565b898986818110610eb257610eb261189a565b9050602002810190610ec491906118b0565b610ed29060208101906118d0565b604051610ee3959493929190611927565b60405180910390a180610ef581611976565b915050610d44565b50505050505050565b6000610f13848484610fd5565b610f64578354600181018555600085815260209020610f349101848461153b565b5083546040516001860190610f4c9086908690611917565b90815260405190819003602001902055506001610f68565b5060005b9392505050565b610f798282611029565b610fd157610f8f816001600160a01b0316611060565b610f9a83602061107c565b604051602001610fab9291906119f1565b60408051601f198184030181529082905262461bcd60e51b82526101c291600401611a66565b5050565b6000836001018383604051610feb929190611917565b908152604051908190036020019020541515949350505050565b600080600061101485856111cf565b915091506110218161123d565b509392505050565b60008281527fd3889cc5458b268d0544e5534672df1296288ca3f93cbd39bd6f497a5c62281160205260408120610f6890836113f3565b60606110766001600160a01b038316601461107c565b92915050565b6060600061108b836002611a99565b611096906002611ab8565b67ffffffffffffffff8111156110ae576110ae611884565b6040519080825280601f01601f1916602001820160405280156110d8576020820181803683370190505b509050600360fc1b816000815181106110f3576110f361189a565b60200101906001600160f81b031916908160001a905350600f60fb1b816001815181106111225761112261189a565b60200101906001600160f81b031916908160001a905350600160028402015b60018111156111af576f181899199a1a9b1b9c1cb0b131b232b360811b85600f16601081106111725761117261189a565b1a60f81b8282815181106111885761118861189a565b60200101906001600160f81b031916908160001a90535060049490941c9360001901611141565b508315610f685760405163c913478560e01b815260040160405180910390fd5b60008082516041036112055760208301516040840151606085015160001a6111f987828585611415565b94509450505050611236565b825160400361122e5760208301516040840151611223868383611502565b935093505050611236565b506000905060025b9250929050565b600081600481111561125157611251611ad0565b036112595750565b600181600481111561126d5761126d611ad0565b036112ba5760405162461bcd60e51b815260206004820152601860248201527f45434453413a20696e76616c6964207369676e6174757265000000000000000060448201526064016101c2565b60028160048111156112ce576112ce611ad0565b0361131b5760405162461bcd60e51b815260206004820152601f60248201527f45434453413a20696e76616c6964207369676e6174757265206c656e6774680060448201526064016101c2565b600381600481111561132f5761132f611ad0565b036113875760405162461bcd60e51b815260206004820152602260248201527f45434453413a20696e76616c6964207369676e6174757265202773272076616c604482015261756560f01b60648201526084016101c2565b600481600481111561139b5761139b611ad0565b036107b75760405162461bcd60e51b815260206004820152602260248201527f45434453413a20696e76616c6964207369676e6174757265202776272076616c604482015261756560f01b60648201526084016101c2565b6001600160a01b03811660009081526001830160205260408120541515610f68565b6000807f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a083111561144c57506000905060036114f9565b8460ff16601b1415801561146457508460ff16601c14155b1561147557506000905060046114f9565b6040805160008082526020820180845289905260ff881692820192909252606081018690526080810185905260019060a0016020604051602081039080840390855afa1580156114c9573d6000803e3d6000fd5b5050604051601f1901519150506001600160a01b0381166114f2576000600192509250506114f9565b9150600090505b94509492505050565b6000806001600160ff1b0383168161151f60ff86901c601b611ab8565b905061152d87828885611415565b935093505050935093915050565b82805461154790611ae6565b90600052602060002090601f01602090048101928261156957600085556115af565b82601f106115825782800160ff198235161785556115af565b828001600101855582156115af579182015b828111156115af578235825591602001919060010190611594565b506115bb9291506115bf565b5090565b5b808211156115bb57600081556001016115c0565b80356001600160a01b03811681146115eb57600080fd5b919050565b60008083601f84011261160257600080fd5b50813567ffffffffffffffff81111561161a57600080fd5b6020830191508360208260051b850101111561123657600080fd5b60008083601f84011261164757600080fd5b50813567ffffffffffffffff81111561165f57600080fd5b60208301915083602082850101111561123657600080fd5b6000806000806000806080878903121561169057600080fd5b863595506116a0602088016115d4565b9450604087013567ffffffffffffffff808211156116bd57600080fd5b6116c98a838b016115f0565b909650945060608901359150808211156116e257600080fd5b506116ef89828a01611635565b979a9699509497509295939492505050565b6000806000806060858703121561171757600080fd5b84359350611727602086016115d4565b9250604085013567ffffffffffffffff81111561174357600080fd5b61174f878288016115f0565b95989497509550505050565b60006020828403121561176d57600080fd5b610f68826115d4565b60008060006040848603121561178b57600080fd5b83359250602084013567ffffffffffffffff8111156117a957600080fd5b6117b5868287016115f0565b9497909650939450505050565b600080602083850312156117d557600080fd5b823567ffffffffffffffff8111156117ec57600080fd5b6117f885828601611635565b90969095509350505050565b60006020828403121561181657600080fd5b81518015158114610f6857600080fd5b60006020828403121561183857600080fd5b5051919050565b81835281816020850137506000828201602090810191909152601f909101601f19169091010190565b60208152600061187c60208301848661183f565b949350505050565b634e487b7160e01b600052604160045260246000fd5b634e487b7160e01b600052603260045260246000fd5b60008235603e198336030181126118c657600080fd5b9190910192915050565b6000808335601e198436030181126118e757600080fd5b83018035915067ffffffffffffffff82111561190257600080fd5b60200191503681900382131561123657600080fd5b8183823760009101908152919050565b85815260606020820152600061194160608301868861183f565b828103604084015261195481858761183f565b98975050505050505050565b634e487b7160e01b600052601160045260246000fd5b60006001820161198857611988611960565b5060010190565b815160009082906020808601845b838110156119b95781518552938201939082019060010161199d565b50929695505050505050565b60005b838110156119e05781810151838201526020016119c8565b838111156106e75750506000910152565b7f416363657373436f6e74726f6c3a206163636f756e7420000000000000000000815260008351611a298160178501602088016119c5565b7001034b99036b4b9b9b4b733903937b6329607d1b6017918401918201528351611a5a8160288401602088016119c5565b01602801949350505050565b6020815260008251806020840152611a858160408501602087016119c5565b601f01601f19169190910160400192915050565b6000816000190483118215151615611ab357611ab3611960565b500290565b60008219821115611acb57611acb611960565b500190565b634e487b7160e01b600052602160045260246000fd5b600181811c90821680611afa57607f821691505b602082108103611b1a57634e487b7160e01b600052602260045260246000fd5b5091905056fe9ecf8425a6266511746b403af66b69eb2b277b7b757807fa21eac9474b45ab3fec68e743e11f3509477f41b0ec121b040a15d46b2c24a1935aa31ef46eca17ada2646970667358221220ffbc87219bdfc99084fb32de4a8a340471216b94f9525f8f4e778b778d3e587e64736f6c634300080d0033

Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

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

Validator Index Block Amount
View All Withdrawals

Txn Hash Block Value Eth2 PubKey Valid
View All Deposits
[ 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.