ETH Price: $2,914.56 (+2.96%)
 

Overview

ETH Balance

0 ETH

ETH Value

$0.00

Token Holdings

More Info

Private Name Tags

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
Register1118879502023-11-07 16:31:17772 days ago1699374677IN
0x00000000...47Bc225d0
0.003700824861122 ETH0.0002646586040.01831021
Register1118877762023-11-07 16:25:29772 days ago1699374329IN
0x00000000...47Bc225d0
0.003700824861122 ETH0.0002980263990.01097506
Register1118858822023-11-07 15:22:21772 days ago1699370541IN
0x00000000...47Bc225d0
0.003700824861122 ETH0.0002875836490.02800437
Register1118857462023-11-07 15:17:49772 days ago1699370269IN
0x00000000...47Bc225d0
0.003700824861122 ETH0.0002269908520.01583979
Register1118856792023-11-07 15:15:35772 days ago1699370135IN
0x00000000...47Bc225d0
0.003700824861122 ETH0.0002204382780.01171306
Register1118851772023-11-07 14:58:51772 days ago1699369131IN
0x00000000...47Bc225d0
0.003700824861122 ETH0.0003014516180.0137611
Register1118846662023-11-07 14:41:49772 days ago1699368109IN
0x00000000...47Bc225d0
0.003700824861122 ETH0.0002695607760.0181592
Register1118844672023-11-07 14:35:11772 days ago1699367711IN
0x00000000...47Bc225d0
0.003700824861122 ETH0.0002109034320.01698824
Register1118842222023-11-07 14:27:01772 days ago1699367221IN
0x00000000...47Bc225d0
0.003700824861122 ETH0.0002554219360.05479645
Register1118835772023-11-07 14:05:31772 days ago1699365931IN
0x00000000...47Bc225d0
0.003700824861122 ETH0.0002167229380.02006229
Register1118835502023-11-07 14:04:37772 days ago1699365877IN
0x00000000...47Bc225d0
0.003700824861122 ETH0.0002274278480.02100488
Register1118830312023-11-07 13:47:19772 days ago1699364839IN
0x00000000...47Bc225d0
0.003700824861122 ETH0.0003276021920.01220216
Register1118826582023-11-07 13:34:53772 days ago1699364093IN
0x00000000...47Bc225d0
0.003700824861122 ETH0.0002816021520.01398908
Register1118822242023-11-07 13:20:25772 days ago1699363225IN
0x00000000...47Bc225d0
0.003700824861122 ETH0.0002014917750.01887884
Register1118822182023-11-07 13:20:13772 days ago1699363213IN
0x00000000...47Bc225d0
0.003700824861122 ETH0.000205674980.01893509
Register1118809272023-11-07 12:37:11772 days ago1699360631IN
0x00000000...47Bc225d0
0.003700824861122 ETH0.0001694931480.01198481
Register1118804032023-11-07 12:19:43772 days ago1699359583IN
0x00000000...47Bc225d0
0.003700824861122 ETH0.0001617012130.01215129
Register1118795282023-11-07 11:50:33772 days ago1699357833IN
0x00000000...47Bc225d0
0.003700824861122 ETH0.0001916357350.01117158
Register1118794652023-11-07 11:48:27772 days ago1699357707IN
0x00000000...47Bc225d0
0.003700824861122 ETH0.0001763913210.01132304
Register1118790202023-11-07 11:33:37772 days ago1699356817IN
0x00000000...47Bc225d0
0.003700824861122 ETH0.000180965990.01191703
Register1118789072023-11-07 11:29:51772 days ago1699356591IN
0x00000000...47Bc225d0
0.003700824861122 ETH0.000165437790.0110371
Register1118786252023-11-07 11:20:27772 days ago1699356027IN
0x00000000...47Bc225d0
0.003700824861122 ETH0.0001571881690.01438758
Register1118782982023-11-07 11:09:33772 days ago1699355373IN
0x00000000...47Bc225d0
0.003700824861122 ETH0.0001999704990.01208874
Register1118781562023-11-07 11:04:49772 days ago1699355089IN
0x00000000...47Bc225d0
0.003700824861122 ETH0.0001740599190.01150382
Register1118781372023-11-07 11:04:11772 days ago1699355051IN
0x00000000...47Bc225d0
0.003700824861122 ETH0.0001809164660.01177432
View all transactions

Latest 25 internal transactions (View All)

Advanced mode:
Parent Transaction Hash Block From To
1118879502023-11-07 16:31:17772 days ago1699374677
0x00000000...47Bc225d0
0.003700824861122 ETH
1118877762023-11-07 16:25:29772 days ago1699374329
0x00000000...47Bc225d0
0.003700824861122 ETH
1118858822023-11-07 15:22:21772 days ago1699370541
0x00000000...47Bc225d0
0.003700824861122 ETH
1118857462023-11-07 15:17:49772 days ago1699370269
0x00000000...47Bc225d0
0.003700824861122 ETH
1118856792023-11-07 15:15:35772 days ago1699370135
0x00000000...47Bc225d0
0.003700824861122 ETH
1118851772023-11-07 14:58:51772 days ago1699369131
0x00000000...47Bc225d0
0.003700824861122 ETH
1118846662023-11-07 14:41:49772 days ago1699368109
0x00000000...47Bc225d0
0.003700824861122 ETH
1118844672023-11-07 14:35:11772 days ago1699367711
0x00000000...47Bc225d0
0.003700824861122 ETH
1118842222023-11-07 14:27:01772 days ago1699367221
0x00000000...47Bc225d0
0.003700824861122 ETH
1118835772023-11-07 14:05:31772 days ago1699365931
0x00000000...47Bc225d0
0.003700824861122 ETH
1118835502023-11-07 14:04:37772 days ago1699365877
0x00000000...47Bc225d0
0.003700824861122 ETH
1118830312023-11-07 13:47:19772 days ago1699364839
0x00000000...47Bc225d0
0.003700824861122 ETH
1118826582023-11-07 13:34:53772 days ago1699364093
0x00000000...47Bc225d0
0.003700824861122 ETH
1118822242023-11-07 13:20:25772 days ago1699363225
0x00000000...47Bc225d0
0.003700824861122 ETH
1118822182023-11-07 13:20:13772 days ago1699363213
0x00000000...47Bc225d0
0.003700824861122 ETH
1118809272023-11-07 12:37:11772 days ago1699360631
0x00000000...47Bc225d0
0.003700824861122 ETH
1118804032023-11-07 12:19:43772 days ago1699359583
0x00000000...47Bc225d0
0.003700824861122 ETH
1118795282023-11-07 11:50:33772 days ago1699357833
0x00000000...47Bc225d0
0.003700824861122 ETH
1118794652023-11-07 11:48:27772 days ago1699357707
0x00000000...47Bc225d0
0.003700824861122 ETH
1118790202023-11-07 11:33:37772 days ago1699356817
0x00000000...47Bc225d0
0.003700824861122 ETH
1118789072023-11-07 11:29:51772 days ago1699356591
0x00000000...47Bc225d0
0.003700824861122 ETH
1118786252023-11-07 11:20:27772 days ago1699356027
0x00000000...47Bc225d0
0.003700824861122 ETH
1118782982023-11-07 11:09:33772 days ago1699355373
0x00000000...47Bc225d0
0.003700824861122 ETH
1118781562023-11-07 11:04:49772 days ago1699355089
0x00000000...47Bc225d0
0.003700824861122 ETH
1118781372023-11-07 11:04:11772 days ago1699355051
0x00000000...47Bc225d0
0.003700824861122 ETH
View All Internal Transactions

Cross-Chain Transactions
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
Bundler

Compiler Version
v0.8.21+commit.d9974bed

Optimization Enabled:
Yes with 100000 runs

Other Settings:
paris EvmVersion, MIT license
// SPDX-License-Identifier: MIT
pragma solidity 0.8.21;

import {IdRegistry} from "./IdRegistry.sol";
import {StorageRegistry} from "./StorageRegistry.sol";
import {KeyRegistry} from "./KeyRegistry.sol";
import {IBundler} from "./interfaces/IBundler.sol";
import {TrustedCaller} from "./lib/TrustedCaller.sol";
import {TransferHelper} from "./lib/TransferHelper.sol";

/**
 * @title Farcaster Bundler
 *
 * @notice See https://github.com/farcasterxyz/contracts/blob/v3.0.0/docs/docs.md for an overview.
 *
 * @custom:security-contact [email protected]
 */
contract Bundler is IBundler, TrustedCaller {
    using TransferHelper for address;

    /*//////////////////////////////////////////////////////////////
                                 ERRORS
    //////////////////////////////////////////////////////////////*/

    /// @dev Revert if the caller does not have the authority to perform the action.
    error Unauthorized();

    /// @dev Revert if the caller attempts to rent zero storage units.
    error InvalidAmount();

    /*//////////////////////////////////////////////////////////////
                                CONSTANTS
    //////////////////////////////////////////////////////////////*/

    /**
     * @dev Contract version specified using Farcaster protocol version scheme.
     */
    string public constant VERSION = "2023.08.23";

    /*//////////////////////////////////////////////////////////////
                                IMMUTABLES
    //////////////////////////////////////////////////////////////*/

    /**
     * @dev Address of the IdRegistry contract
     */
    IdRegistry public immutable idRegistry;

    /**
     * @dev Address of the StorageRegistry contract
     */
    StorageRegistry public immutable storageRegistry;

    /**
     * @dev Address of the KeyRegistry contract
     */
    KeyRegistry public immutable keyRegistry;

    /*//////////////////////////////////////////////////////////////
                               CONSTRUCTOR
    //////////////////////////////////////////////////////////////*/

    /**
     * @notice Configure the addresses of the Registry contracts and the trusted caller, which is
     *         allowed to register users during the bootstrap phase.
     *
     * @param _idRegistry      Address of the IdRegistry contract
     * @param _storageRegistry Address of the StorageRegistry contract
     * @param _keyRegistry     Address of the KeyRegistry contract
     * @param _trustedCaller   Address that can call trustedRegister and trustedBatchRegister
     * @param _initialOwner    Address that can set the trusted caller
     */
    constructor(
        address _idRegistry,
        address _storageRegistry,
        address _keyRegistry,
        address _trustedCaller,
        address _initialOwner
    ) TrustedCaller(_initialOwner) {
        idRegistry = IdRegistry(_idRegistry);
        storageRegistry = StorageRegistry(_storageRegistry);
        keyRegistry = KeyRegistry(_keyRegistry);
        _setTrustedCaller(_trustedCaller);
    }

    /**
     * @notice Register an fid, multiple signers, and rent storage to an address in a single transaction.
     *
     * @param registration Struct containing registration parameters: to, recovery, deadline, and signature.
     * @param signers      Array of structs containing signer parameters: keyType, key, metadataType,
     *                        metadata, deadline, and signature.
     * @param storageUnits Number of storage units to rent
     *
     */
    function register(
        RegistrationParams calldata registration,
        SignerParams[] calldata signers,
        uint256 storageUnits
    ) external payable {
        if (storageUnits == 0) revert InvalidAmount();
        uint256 fid =
            idRegistry.registerFor(registration.to, registration.recovery, registration.deadline, registration.sig);

        uint256 signersLen = signers.length;
        for (uint256 i; i < signersLen;) {
            SignerParams calldata signer = signers[i];
            keyRegistry.addFor(
                registration.to,
                signer.keyType,
                signer.key,
                signer.metadataType,
                signer.metadata,
                signer.deadline,
                signer.sig
            );

            // We know this will not overflow because it's less than the length of the array, which is a `uint256`.
            unchecked {
                ++i;
            }
        }

        uint256 overpayment = storageRegistry.rent{value: msg.value}(fid, storageUnits);

        if (overpayment > 0) {
            msg.sender.sendNative(overpayment);
        }
    }

    /**
     * @notice Register an fid, add a signer, and credit storage to an address in a single transaction. Can only
     *         be called by the trustedCaller during the Seedable phase.
     *
     * @param user UserData struct including to/recovery address, key params, and number of storage units.
     */
    function trustedRegister(UserData calldata user) external onlyTrustedCaller {
        // Will revert unless IdRegistry is in the Seedable phase
        uint256 fid = idRegistry.trustedRegister(user.to, user.recovery);
        uint256 signersLen = user.signers.length;
        for (uint256 i; i < signersLen;) {
            SignerData calldata signer = user.signers[i];
            keyRegistry.trustedAdd(user.to, signer.keyType, signer.key, signer.metadataType, signer.metadata);
            unchecked {
                ++i;
            }
        }
        storageRegistry.credit(fid, user.units);
    }

    /**
     * @notice Register fids, keys, and credit storage for multiple users in a single transaction. Can
     *         only be called by the trustedCaller during the Seedable phase. Will be used when
     *         migrating across Ethereum networks to bootstrap a new contract with existing data.
     *
     * @param users  Array of UserData structs to register
     */
    function trustedBatchRegister(UserData[] calldata users) external onlyTrustedCaller {
        // Safety: calls inside a loop are safe since caller is trusted
        uint256 usersLen = users.length;
        for (uint256 i; i < usersLen;) {
            UserData calldata user = users[i];
            uint256 fid = idRegistry.trustedRegister(user.to, user.recovery);
            uint256 signersLen = user.signers.length;

            for (uint256 j; j < signersLen;) {
                SignerData calldata signer = user.signers[j];
                keyRegistry.trustedAdd(user.to, signer.keyType, signer.key, signer.metadataType, signer.metadata);
                unchecked {
                    ++j;
                }
            }

            storageRegistry.credit(fid, user.units);

            // We know this will not overflow because it's less than the length of the array, which is a `uint256`.
            unchecked {
                ++i;
            }
        }
    }

    // solhint-disable-next-line no-empty-blocks
    receive() external payable {
        if (msg.sender != address(storageRegistry)) revert Unauthorized();
    }
}

// SPDX-License-Identifier: MIT
pragma solidity 0.8.21;

import {SignatureChecker} from "openzeppelin/contracts/utils/cryptography/SignatureChecker.sol";
import {Nonces} from "openzeppelin-latest/contracts/utils/Nonces.sol";
import {Pausable} from "openzeppelin/contracts/security/Pausable.sol";

import {IIdRegistry} from "./interfaces/IIdRegistry.sol";
import {EIP712} from "./lib/EIP712.sol";
import {Signatures} from "./lib/Signatures.sol";
import {TrustedCaller} from "./lib/TrustedCaller.sol";

/**
 * @title Farcaster IdRegistry
 *
 * @notice See https://github.com/farcasterxyz/contracts/blob/v3.0.0/docs/docs.md for an overview.
 *
 * @custom:security-contact [email protected]
 */
contract IdRegistry is IIdRegistry, TrustedCaller, Signatures, Pausable, EIP712, Nonces {
    /*//////////////////////////////////////////////////////////////
                                 ERRORS
    //////////////////////////////////////////////////////////////*/

    /// @dev Revert when the caller does not have the authority to perform the action.
    error Unauthorized();

    /// @dev Revert when the caller must have an fid but does not have one.
    error HasNoId();

    /// @dev Revert when the destination must be empty but has an fid.
    error HasId();

    /*//////////////////////////////////////////////////////////////
                                 EVENTS
    //////////////////////////////////////////////////////////////*/

    /**
     * @dev Emit an event when a new Farcaster ID is registered.
     *
     *      Hubs listen for this and update their address-to-fid mapping by adding `to` as the
     *      current owner of `id`. Hubs assume the invariants:
     *
     *      1. Two Register events can never emit with the same `id`
     *
     *      2. Two Register(alice, ..., ...) cannot emit unless a Transfer(alice, bob, ...) emits
     *          in between, where bob != alice.
     *
     * @param to       The custody address that owns the fid
     * @param id       The fid that was registered.
     * @param recovery The address that can initiate a recovery request for the fid.
     */
    event Register(address indexed to, uint256 indexed id, address recovery);

    /**
     * @dev Emit an event when an fid is transferred to a new custody address.
     *
     *      Hubs listen to this event and atomically change the current owner of `id`
     *      from `from` to `to` in their address-to-fid mapping. Hubs assume the invariants:
     *
     *      1. A Transfer(..., alice, ...) cannot emit if the most recent event for alice is
     *         Register (alice, ..., ...)
     *
     *      2. A Transfer(alice, ..., id) cannot emit unless the most recent event with that id is
     *         Transfer(..., alice, id) or Register(alice, id, ...)
     *
     * @param from The custody address that previously owned the fid.
     * @param to   The custody address that now owns the fid.
     * @param id   The fid that was transferred.
     */
    event Transfer(address indexed from, address indexed to, uint256 indexed id);

    /**
     * @dev Emit an event when an fid is recovered.
     *
     * @param from The custody address that previously owned the fid.
     * @param to   The custody address that now owns the fid.
     * @param id   The fid that was recovered.
     */
    event Recover(address indexed from, address indexed to, uint256 indexed id);

    /**
     * @dev Emit an event when a Farcaster ID's recovery address changes. It is possible for this
     *      event to emit multiple times in a row with the same recovery address.
     *
     * @param id       The fid whose recovery address was changed.
     * @param recovery The new recovery address.
     */
    event ChangeRecoveryAddress(uint256 indexed id, address indexed recovery);

    /*//////////////////////////////////////////////////////////////
                              CONSTANTS
    //////////////////////////////////////////////////////////////*/

    /**
     * @inheritdoc IIdRegistry
     */
    string public constant name = "Farcaster FID";

    /**
     * @inheritdoc IIdRegistry
     */
    string public constant VERSION = "2023.08.23";

    /**
     * @inheritdoc IIdRegistry
     */
    bytes32 public constant REGISTER_TYPEHASH =
        keccak256("Register(address to,address recovery,uint256 nonce,uint256 deadline)");

    /**
     * @inheritdoc IIdRegistry
     */
    bytes32 public constant TRANSFER_TYPEHASH =
        keccak256("Transfer(uint256 fid,address to,uint256 nonce,uint256 deadline)");

    /**
     * @inheritdoc IIdRegistry
     */
    bytes32 public constant CHANGE_RECOVERY_ADDRESS_TYPEHASH =
        keccak256("ChangeRecoveryAddress(uint256 fid,address recovery,uint256 nonce,uint256 deadline)");

    /*//////////////////////////////////////////////////////////////
                                 STORAGE
    //////////////////////////////////////////////////////////////*/

    /**
     * @inheritdoc IIdRegistry
     */
    uint256 public idCounter;

    /**
     * @inheritdoc IIdRegistry
     */
    mapping(address owner => uint256 fid) public idOf;

    /**
     * @inheritdoc IIdRegistry
     */
    mapping(uint256 fid => address recovery) public recoveryOf;

    /*//////////////////////////////////////////////////////////////
                               CONSTRUCTOR
    //////////////////////////////////////////////////////////////*/

    /**
     * @notice Set the owner of the contract to the provided _owner.
     *
     * @param _initialOwner Initial owner address.
     *
     */
    // solhint-disable-next-line no-empty-blocks
    constructor(address _initialOwner) TrustedCaller(_initialOwner) EIP712("Farcaster IdRegistry", "1") {}

    /*//////////////////////////////////////////////////////////////
                             REGISTRATION LOGIC
    //////////////////////////////////////////////////////////////*/

    /**
     * @inheritdoc IIdRegistry
     */
    function register(address recovery) external returns (uint256 fid) {
        return _register(msg.sender, recovery);
    }

    /**
     * @inheritdoc IIdRegistry
     */
    function registerFor(
        address to,
        address recovery,
        uint256 deadline,
        bytes calldata sig
    ) external returns (uint256 fid) {
        /* Revert if signature is invalid */
        _verifyRegisterSig({to: to, recovery: recovery, deadline: deadline, sig: sig});
        return _register(to, recovery);
    }

    /**
     * @inheritdoc IIdRegistry
     */
    function trustedRegister(address to, address recovery) external onlyTrustedCaller returns (uint256 fid) {
        fid = _unsafeRegister(to, recovery);
        emit Register(to, idCounter, recovery);
    }

    /**
     * @dev Registers an fid and sets up a recovery address for a target. The contract must not be
     *      in the Seedable (trustedOnly = 1) state and target must not have an fid.
     */
    function _register(address to, address recovery) internal whenNotTrusted returns (uint256 fid) {
        fid = _unsafeRegister(to, recovery);
        emit Register(to, idCounter, recovery);
    }

    /**
     * @dev Registers an fid and sets up a recovery address for a target. Does not check all
     *      invariants or emit events.
     */
    function _unsafeRegister(address to, address recovery) internal whenNotPaused returns (uint256 fid) {
        /* Revert if the target(to) has an fid */
        if (idOf[to] != 0) revert HasId();

        /* Safety: idCounter won't realistically overflow. */
        unchecked {
            /* Incrementing before assignment ensures that no one gets the 0 fid. */
            fid = ++idCounter;
        }

        idOf[to] = fid;
        recoveryOf[fid] = recovery;
    }

    /*//////////////////////////////////////////////////////////////
                             TRANSFER LOGIC
    //////////////////////////////////////////////////////////////*/

    /**
     * @inheritdoc IIdRegistry
     */
    function transfer(address to, uint256 deadline, bytes calldata sig) external {
        address from = msg.sender;
        uint256 fromId = idOf[from];

        /* Revert if the sender has no id */
        if (fromId == 0) revert HasNoId();
        /* Revert if recipient has an id */
        if (idOf[to] != 0) revert HasId();

        /* Revert if signature is invalid */
        _verifyTransferSig({fid: fromId, to: to, deadline: deadline, signer: to, sig: sig});

        _unsafeTransfer(fromId, from, to);
    }

    /**
     * @inheritdoc IIdRegistry
     */
    function transferFor(
        address from,
        address to,
        uint256 fromDeadline,
        bytes calldata fromSig,
        uint256 toDeadline,
        bytes calldata toSig
    ) external {
        uint256 fromId = idOf[from];

        /* Revert if the sender has no id */
        if (fromId == 0) revert HasNoId();
        /* Revert if recipient has an id */
        if (idOf[to] != 0) revert HasId();

        /* Revert if either signature is invalid */
        _verifyTransferSig({fid: fromId, to: to, deadline: fromDeadline, signer: from, sig: fromSig});
        _verifyTransferSig({fid: fromId, to: to, deadline: toDeadline, signer: to, sig: toSig});

        _unsafeTransfer(fromId, from, to);
    }

    /**
     * @dev Transfer the fid to another address without checking invariants.
     */
    function _unsafeTransfer(uint256 id, address from, address to) internal whenNotPaused {
        idOf[to] = id;
        delete idOf[from];

        emit Transfer(from, to, id);
    }

    /*//////////////////////////////////////////////////////////////
                             RECOVERY LOGIC
    //////////////////////////////////////////////////////////////*/

    /**
     * @inheritdoc IIdRegistry
     */
    function changeRecoveryAddress(address recovery) external whenNotPaused {
        /* Revert if the caller does not own an fid */
        uint256 ownerId = idOf[msg.sender];
        if (ownerId == 0) revert HasNoId();

        /* Change the recovery address */
        recoveryOf[ownerId] = recovery;

        emit ChangeRecoveryAddress(ownerId, recovery);
    }

    /**
     * @inheritdoc IIdRegistry
     */
    function changeRecoveryAddressFor(
        address owner,
        address recovery,
        uint256 deadline,
        bytes calldata sig
    ) external whenNotPaused {
        /* Revert if the caller does not own an fid */
        uint256 ownerId = idOf[owner];
        if (ownerId == 0) revert HasNoId();

        _verifyChangeRecoveryAddressSig({fid: ownerId, recovery: recovery, deadline: deadline, signer: owner, sig: sig});

        /* Change the recovery address */
        recoveryOf[ownerId] = recovery;

        emit ChangeRecoveryAddress(ownerId, recovery);
    }

    /**
     * @inheritdoc IIdRegistry
     */
    function recover(address from, address to, uint256 deadline, bytes calldata sig) external {
        /* Revert if from does not own an fid */
        uint256 fromId = idOf[from];
        if (fromId == 0) revert HasNoId();

        /* Revert if the caller is not the recovery address */
        address caller = msg.sender;
        if (recoveryOf[fromId] != caller) revert Unauthorized();

        /* Revert if destination(to) already has an fid */
        if (idOf[to] != 0) revert HasId();

        /* Revert if signature is invalid */
        _verifyTransferSig({fid: fromId, to: to, deadline: deadline, signer: to, sig: sig});

        emit Recover(from, to, fromId);
        _unsafeTransfer(fromId, from, to);
    }

    /**
     * @inheritdoc IIdRegistry
     */
    function recoverFor(
        address from,
        address to,
        uint256 recoveryDeadline,
        bytes calldata recoverySig,
        uint256 toDeadline,
        bytes calldata toSig
    ) external {
        /* Revert if from does not own an fid */
        uint256 fromId = idOf[from];
        if (fromId == 0) revert HasNoId();

        /* Revert if destination(to) already has an fid */
        if (idOf[to] != 0) revert HasId();

        /* Revert if either signature is invalid */
        _verifyTransferSig({
            fid: fromId,
            to: to,
            deadline: recoveryDeadline,
            signer: recoveryOf[fromId],
            sig: recoverySig
        });
        _verifyTransferSig({fid: fromId, to: to, deadline: toDeadline, signer: to, sig: toSig});

        emit Recover(from, to, fromId);
        _unsafeTransfer(fromId, from, to);
    }

    /*//////////////////////////////////////////////////////////////
                         PERMISSIONED ACTIONS
    //////////////////////////////////////////////////////////////*/

    /**
     * @inheritdoc IIdRegistry
     */
    function pause() external onlyOwner {
        _pause();
    }

    /**
     * @inheritdoc IIdRegistry
     */
    function unpause() external onlyOwner {
        _unpause();
    }

    /*//////////////////////////////////////////////////////////////
                                  VIEWS
    //////////////////////////////////////////////////////////////*/

    /**
     * @inheritdoc IIdRegistry
     */
    function verifyFidSignature(
        address custodyAddress,
        uint256 fid,
        bytes32 digest,
        bytes calldata sig
    ) external view returns (bool isValid) {
        isValid = idOf[custodyAddress] == fid && SignatureChecker.isValidSignatureNow(custodyAddress, digest, sig);
    }

    /*//////////////////////////////////////////////////////////////
                     SIGNATURE VERIFICATION HELPERS
    //////////////////////////////////////////////////////////////*/

    function _verifyRegisterSig(address to, address recovery, uint256 deadline, bytes memory sig) internal {
        _verifySig(
            _hashTypedDataV4(keccak256(abi.encode(REGISTER_TYPEHASH, to, recovery, _useNonce(to), deadline))),
            to,
            deadline,
            sig
        );
    }

    function _verifyTransferSig(uint256 fid, address to, uint256 deadline, address signer, bytes memory sig) internal {
        _verifySig(
            _hashTypedDataV4(keccak256(abi.encode(TRANSFER_TYPEHASH, fid, to, _useNonce(signer), deadline))),
            signer,
            deadline,
            sig
        );
    }

    function _verifyChangeRecoveryAddressSig(
        uint256 fid,
        address recovery,
        uint256 deadline,
        address signer,
        bytes memory sig
    ) internal {
        _verifySig(
            _hashTypedDataV4(
                keccak256(abi.encode(CHANGE_RECOVERY_ADDRESS_TYPEHASH, fid, recovery, _useNonce(signer), deadline))
            ),
            signer,
            deadline,
            sig
        );
    }
}

// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity 0.8.21;

import {AggregatorV3Interface} from "chainlink/v0.8/interfaces/AggregatorV3Interface.sol";
import {AccessControlEnumerable} from "openzeppelin/contracts/access/AccessControlEnumerable.sol";
import {Pausable} from "openzeppelin/contracts/security/Pausable.sol";
import {FixedPointMathLib} from "solmate/src/utils/FixedPointMathLib.sol";

import {IStorageRegistry} from "./interfaces/IStorageRegistry.sol";
import {TransferHelper} from "./lib/TransferHelper.sol";

/**
 * @title Farcaster StorageRegistry
 *
 * @notice See https://github.com/farcasterxyz/contracts/blob/v3.0.0/docs/docs.md for an overview.
 *
 * @custom:security-contact [email protected]
 */
contract StorageRegistry is IStorageRegistry, AccessControlEnumerable, Pausable {
    using FixedPointMathLib for uint256;
    using TransferHelper for address;

    /*//////////////////////////////////////////////////////////////
                                 ERRORS
    //////////////////////////////////////////////////////////////*/

    /// @dev Revert if the caller attempts to rent storage after the contract is deprecated.
    error ContractDeprecated();

    /// @dev Revert if the caller attempts to rent more storage than is available.
    error ExceedsCapacity();

    /// @dev Revert if the caller attempts to rent zero units.
    error InvalidAmount();

    /// @dev Revert if the caller attempts a batch rent with mismatched input array lengths or an empty array.
    error InvalidBatchInput();

    /// @dev Revert if the caller provides the wrong payment amount.
    error InvalidPayment();

    /// @dev Revert if the price feed returns a stale answer.
    error StaleAnswer();

    /// @dev Revert if any data feed round is incomplete and has not yet generated an answer.
    error IncompleteRound();

    /// @dev Revert if any data feed returns a timestamp in the future.
    error InvalidRoundTimestamp();

    /// @dev Revert if the price feed returns a value greater than the min/max bound.
    error PriceOutOfBounds();

    /// @dev Revert if the price feed returns a zero or negative price.
    error InvalidPrice();

    /// @dev Revert if the sequencer uptime feed detects that the L2 sequencer is unavailable.
    error SequencerDown();

    /// @dev Revert if the L2 sequencer restarted less than L2_DOWNTIME_GRACE_PERIOD seconds ago.
    error GracePeriodNotOver();

    /// @dev Revert if the deprecation timestamp parameter is in the past.
    error InvalidDeprecationTimestamp();

    /// @dev Revert if the priceFeedMinAnswer parameter is greater than or equal to priceFeedMaxAnswer.
    error InvalidMinAnswer();

    /// @dev Revert if the priceFeedMaxAnswer parameter is less than or equal to priceFeedMinAnswer.
    error InvalidMaxAnswer();

    /// @dev Revert if the fixedEthUsdPrice is outside the configured price bounds.
    error InvalidFixedPrice();

    /// @dev Revert if the caller is not an owner.
    error NotOwner();

    /// @dev Revert if the caller is not an operator.
    error NotOperator();

    /// @dev Revert if the caller is not a treasurer.
    error NotTreasurer();

    /// @dev Revert if the caller does not have an authorized role.
    error Unauthorized();

    /// @dev Revert if transferred to the zero address.
    error InvalidAddress();

    /// @dev Revert if the caller attempts a continuous credit withan invalid range.
    error InvalidRangeInput();

    /*//////////////////////////////////////////////////////////////
                                 EVENTS
    //////////////////////////////////////////////////////////////*/

    /**
     * @dev Emit an event when a caller pays rent for an fid's storage.
     *
     *      Hubs increment the units assigned to the fid for exactly 395 days (1 year + grace) from
     *      the event timestamp. Hubs track this for unregistered fids and will assign units when
     *      the fid is registered. Storage credited to fid 0 is a no-op.
     *
     * @param payer     Address of the account paying the storage rent.
     * @param fid       The fid that will receive the storage units.
     * @param units     The number of storage units being rented.
     */
    event Rent(address indexed payer, uint256 indexed fid, uint256 units);

    /**
     * @dev Emit an event when an owner changes the price feed address.
     *
     * @param oldFeed The previous price feed address.
     * @param newFeed The new price feed address.
     */
    event SetPriceFeed(address oldFeed, address newFeed);

    /**
     * @dev Emit an event when an owner changes the uptime feed address.
     *
     * @param oldFeed The previous uptime feed address.
     * @param newFeed The new uptime feed address.
     */
    event SetUptimeFeed(address oldFeed, address newFeed);

    /**
     * @dev Emit an event when an owner changes the price of storage units.
     *
     * @param oldPrice The previous unit price in USD. Fixed point value with 8 decimals.
     * @param newPrice The new unit price in USD. Fixed point value with 8 decimals.
     */
    event SetPrice(uint256 oldPrice, uint256 newPrice);

    /**
     * @dev Emit an event when an owner changes the fixed ETH/USD price.
     *      Setting this value to zero means the fixed price is disabled.
     *
     * @param oldPrice The previous ETH price in USD. Fixed point value with 8 decimals.
     * @param newPrice The new ETH price in USD. Fixed point value with 8 decimals.
     */
    event SetFixedEthUsdPrice(uint256 oldPrice, uint256 newPrice);

    /**
     * @dev Emit an event when an owner changes the maximum supply of storage units.
     *
     *      Hub operators should be made aware of changes to storage requirements before they occur
     *      since it may cause Hubs to fail if they do not allocate sufficient storage.
     *
     * @param oldMax The previous maximum amount.
     * @param newMax The new maximum amount.
     */
    event SetMaxUnits(uint256 oldMax, uint256 newMax);

    /**
     * @dev Emit an event when an owner changes the deprecationTimestamp.
     *
     *      Hubs will stop listening to events after the deprecationTimestamp. This can be used
     *      when cutting over to a new contract. Hubs assume the following invariants:
     *
     *      1. SetDeprecationTimestamp() is only emitted once.
     *
     * @param oldTimestamp The previous deprecationTimestamp.
     * @param newTimestamp The new deprecationTimestamp.
     */
    event SetDeprecationTimestamp(uint256 oldTimestamp, uint256 newTimestamp);

    /**
     * @dev Emit an event when an owner changes the priceFeedCacheDuration.
     *
     * @param oldDuration The previous priceFeedCacheDuration.
     * @param newDuration The new priceFeedCacheDuration.
     */
    event SetCacheDuration(uint256 oldDuration, uint256 newDuration);

    /**
     * @dev Emit an event when an owner changes the priceFeedMaxAge.
     *
     * @param oldAge The previous priceFeedMaxAge.
     * @param newAge The new priceFeedMaxAge.
     */
    event SetMaxAge(uint256 oldAge, uint256 newAge);

    /**
     * @dev Emit an event when an owner changes the priceFeedMinAnswer.
     *
     * @param oldPrice The previous priceFeedMinAnswer.
     * @param newPrice The new priceFeedMaxAnswer.
     */
    event SetMinAnswer(uint256 oldPrice, uint256 newPrice);

    /**
     * @dev Emit an event when an owner changes the priceFeedMaxAnswer.
     *
     * @param oldPrice The previous priceFeedMaxAnswer.
     * @param newPrice The new priceFeedMaxAnswer.
     */
    event SetMaxAnswer(uint256 oldPrice, uint256 newPrice);

    /**
     * @dev Emit an event when an owner changes the uptimeFeedGracePeriod.
     *
     * @param oldPeriod The previous uptimeFeedGracePeriod.
     * @param newPeriod The new uptimeFeedGracePeriod.
     */
    event SetGracePeriod(uint256 oldPeriod, uint256 newPeriod);

    /**
     * @dev Emit an event when an owner changes the vault.
     *
     * @param oldVault The previous vault.
     * @param newVault The new vault.
     */
    event SetVault(address oldVault, address newVault);

    /**
     * @dev Emit an event when a treasurer withdraws any contract balance to the vault.
     *
     * @param to     Address of recipient.
     * @param amount The amount of ether withdrawn.
     */
    event Withdraw(address indexed to, uint256 amount);

    /*//////////////////////////////////////////////////////////////
                                CONSTANTS
    //////////////////////////////////////////////////////////////*/

    /**
     * @inheritdoc IStorageRegistry
     */
    string public constant VERSION = "2023.08.23";

    bytes32 internal constant OWNER_ROLE = keccak256("OWNER_ROLE");
    bytes32 internal constant OPERATOR_ROLE = keccak256("OPERATOR_ROLE");
    bytes32 internal constant TREASURER_ROLE = keccak256("TREASURER_ROLE");

    /*//////////////////////////////////////////////////////////////
                              PARAMETERS
    //////////////////////////////////////////////////////////////*/

    /**
     * @inheritdoc IStorageRegistry
     */
    AggregatorV3Interface public priceFeed;

    /**
     * @inheritdoc IStorageRegistry
     */
    AggregatorV3Interface public uptimeFeed;

    /**
     * @inheritdoc IStorageRegistry
     */
    uint256 public deprecationTimestamp;

    /**
     * @inheritdoc IStorageRegistry
     */
    uint256 public usdUnitPrice;

    /**
     * @inheritdoc IStorageRegistry
     */
    uint256 public fixedEthUsdPrice;

    /**
     * @inheritdoc IStorageRegistry
     */
    uint256 public maxUnits;

    /**
     * @inheritdoc IStorageRegistry
     */
    uint256 public priceFeedCacheDuration;

    /**
     * @inheritdoc IStorageRegistry
     */
    uint256 public priceFeedMaxAge;

    /**
     * @inheritdoc IStorageRegistry
     */
    uint256 public priceFeedMinAnswer;

    /**
     * @inheritdoc IStorageRegistry
     */
    uint256 public priceFeedMaxAnswer;

    /**
     * @inheritdoc IStorageRegistry
     */
    uint256 public uptimeFeedGracePeriod;

    /**
     * @inheritdoc IStorageRegistry
     */
    address public vault;

    /*//////////////////////////////////////////////////////////////
                                STORAGE
    //////////////////////////////////////////////////////////////*/

    /**
     * @inheritdoc IStorageRegistry
     */
    uint256 public rentedUnits;

    /**
     * @inheritdoc IStorageRegistry
     */
    uint256 public ethUsdPrice;

    /**
     * @inheritdoc IStorageRegistry
     */
    uint256 public prevEthUsdPrice;

    /**
     * @inheritdoc IStorageRegistry
     */
    uint256 public lastPriceFeedUpdateTime;

    /**
     * @inheritdoc IStorageRegistry
     */
    uint256 public lastPriceFeedUpdateBlock;

    /*//////////////////////////////////////////////////////////////
                               CONSTRUCTOR
    //////////////////////////////////////////////////////////////*/

    /**
     * @notice Set the price feed, uptime feed, and initial parameters.
     *
     * @param _priceFeed                     Chainlink ETH/USD price feed.
     * @param _uptimeFeed                    Chainlink L2 sequencer uptime feed.
     * @param _initialUsdUnitPrice           Initial unit price in USD. Fixed point 8 decimal value.
     * @param _initialMaxUnits               Initial maximum capacity in storage units.
     * @param _initialVault                  Initial vault address.
     * @param _initialRoleAdmin              Initial role admin address.
     * @param _initialOwner                  Initial owner address.
     * @param _initialOperator               Initial operator address.
     * @param _initialTreasurer              Initial treasurer address.
     */
    constructor(
        AggregatorV3Interface _priceFeed,
        AggregatorV3Interface _uptimeFeed,
        uint256 _initialUsdUnitPrice,
        uint256 _initialMaxUnits,
        address _initialVault,
        address _initialRoleAdmin,
        address _initialOwner,
        address _initialOperator,
        address _initialTreasurer
    ) {
        priceFeed = _priceFeed;
        emit SetPriceFeed(address(0), address(_priceFeed));

        uptimeFeed = _uptimeFeed;
        emit SetUptimeFeed(address(0), address(_uptimeFeed));

        deprecationTimestamp = block.timestamp + 365 days;
        emit SetDeprecationTimestamp(0, deprecationTimestamp);

        usdUnitPrice = _initialUsdUnitPrice;
        emit SetPrice(0, _initialUsdUnitPrice);

        maxUnits = _initialMaxUnits;
        emit SetMaxUnits(0, _initialMaxUnits);

        priceFeedCacheDuration = 1 days;
        emit SetCacheDuration(0, 1 days);

        priceFeedMaxAge = 2 hours;
        emit SetMaxAge(0, 2 hours);

        uptimeFeedGracePeriod = 1 hours;
        emit SetGracePeriod(0, 1 hours);

        priceFeedMinAnswer = 100e8; // 100 USD / ETH
        emit SetMinAnswer(0, 100e8);

        priceFeedMaxAnswer = 10_000e8; // 10_000 USD / ETH
        emit SetMaxAnswer(0, 10_000e8);

        vault = _initialVault;
        emit SetVault(address(0), _initialVault);

        _grantRole(DEFAULT_ADMIN_ROLE, _initialRoleAdmin);
        _grantRole(OWNER_ROLE, _initialOwner);
        _grantRole(OPERATOR_ROLE, _initialOperator);
        _grantRole(TREASURER_ROLE, _initialTreasurer);

        _refreshPrice();
    }

    /*//////////////////////////////////////////////////////////////
                               MODIFIERS
    //////////////////////////////////////////////////////////////*/

    modifier whenNotDeprecated() {
        if (block.timestamp >= deprecationTimestamp) {
            revert ContractDeprecated();
        }
        _;
    }

    modifier onlyOwner() {
        if (!hasRole(OWNER_ROLE, msg.sender)) revert NotOwner();
        _;
    }

    modifier onlyOperator() {
        if (!hasRole(OPERATOR_ROLE, msg.sender)) revert NotOperator();
        _;
    }

    modifier onlyTreasurer() {
        if (!hasRole(TREASURER_ROLE, msg.sender)) revert NotTreasurer();
        _;
    }

    /*//////////////////////////////////////////////////////////////
                        STORAGE RENTAL LOGIC
    //////////////////////////////////////////////////////////////*/

    /**
     * @inheritdoc IStorageRegistry
     */
    function rent(
        uint256 fid,
        uint256 units
    ) external payable whenNotDeprecated whenNotPaused returns (uint256 overpayment) {
        // Checks
        if (units == 0) revert InvalidAmount();
        if (rentedUnits + units > maxUnits) revert ExceedsCapacity();
        uint256 totalPrice = _price(units);
        if (msg.value < totalPrice) revert InvalidPayment();

        // Effects
        rentedUnits += units;
        emit Rent(msg.sender, fid, units);

        // Interactions
        // Safety: overpayment is guaranteed to be >=0 because of checks
        overpayment = msg.value - totalPrice;
        if (overpayment > 0) {
            msg.sender.sendNative(overpayment);
        }
    }

    /**
     * @inheritdoc IStorageRegistry
     */
    function batchRent(
        uint256[] calldata fids,
        uint256[] calldata units
    ) external payable whenNotDeprecated whenNotPaused {
        // Pre-checks
        if (fids.length == 0 || units.length == 0) revert InvalidBatchInput();
        if (fids.length != units.length) revert InvalidBatchInput();

        // Effects
        uint256 _usdPrice = usdUnitPrice;
        uint256 _ethPrice = _ethUsdPrice();

        uint256 totalQty;
        for (uint256 i; i < fids.length; ++i) {
            uint256 qty = units[i];
            if (qty == 0) continue;
            totalQty += qty;
            emit Rent(msg.sender, fids[i], qty);
        }
        uint256 totalPrice = _price(totalQty, _usdPrice, _ethPrice);

        // Post-checks
        if (msg.value < totalPrice) revert InvalidPayment();
        if (rentedUnits + totalQty > maxUnits) revert ExceedsCapacity();

        // Effects
        rentedUnits += totalQty;

        // Interactions
        if (msg.value > totalPrice) {
            msg.sender.sendNative(msg.value - totalPrice);
        }
    }

    /*//////////////////////////////////////////////////////////////
                              PRICE VIEWS
    //////////////////////////////////////////////////////////////*/

    /**
     * @inheritdoc IStorageRegistry
     */
    function unitPrice() external view returns (uint256) {
        return price(1);
    }

    /**
     * @inheritdoc IStorageRegistry
     */
    function price(uint256 units) public view returns (uint256) {
        uint256 ethPrice;
        if (fixedEthUsdPrice != 0) {
            ethPrice = fixedEthUsdPrice;

            /**
             *  Slither flags the following line as a dangerous strict equality, but we want to
             *  make an exact comparison here and are not using this value in the context
             *  this detector rule describes.
             */

            // slither-disable-next-line incorrect-equality
        } else if (lastPriceFeedUpdateBlock == block.number) {
            ethPrice = prevEthUsdPrice;
        } else {
            ethPrice = ethUsdPrice;
        }
        return _price(units, usdUnitPrice, ethPrice);
    }

    /**
     * @dev Return the fixed price if present and the cached ethUsdPrice if it is not. If cached
     *      price is no longer valid, refresh the cache from the price feed but return the cached
     *      price for the rest of this block to avoid unexpected price changes.
     */
    function _ethUsdPrice() internal returns (uint256) {
        /**
         *  If a fixed ETH/USD price is set, use it. This disables external calls
         *  to the price feed in case of emergency.
         */
        if (fixedEthUsdPrice != 0) return fixedEthUsdPrice;

        /**
         *  If cache duration has expired, get the latest price from the price feed.
         *  This updates prevEthUsdPrice, ethUsdPrice, lastPriceFeedUpdateTime, and
         *  lastPriceFeedUpdateBlock.
         */
        if (block.timestamp - lastPriceFeedUpdateTime > priceFeedCacheDuration) {
            _refreshPrice();
        }

        /**
         *  We want price changes to take effect in the first block after the price
         *  refresh, rather than immediately, to keep the price from changing intra
         *  block. If we update the price in this block, use the previous price
         *  until the next block. Otherwise, use the latest price.
         *
         *  Slither flags this line as a dangerous strict equality, but we want to
         *  make an exact comparison here and are not using this value in the context
         *  this detector rule describes.
         */

        // slither-disable-next-line incorrect-equality
        return (lastPriceFeedUpdateBlock == block.number) ? prevEthUsdPrice : ethUsdPrice;
    }

    /**
     * @dev Get the latest ETH/USD price from the price feed and update the cached price.
     */
    function _refreshPrice() internal {
        /**
         *  Get and validate the L2 sequencer status.
         *  We ignore the deprecated answeredInRound value.
         */

        // slither-disable-next-line unused-return
        (uint80 uptimeRoundId, int256 sequencerUp, uint256 uptimeStartedAt, uint256 uptimeUpdatedAt,) =
            uptimeFeed.latestRoundData();
        if (sequencerUp != 0) revert SequencerDown();
        if (uptimeRoundId == 0) revert IncompleteRound();
        if (uptimeUpdatedAt == 0) revert IncompleteRound();
        if (uptimeUpdatedAt > block.timestamp) revert InvalidRoundTimestamp();

        /* If the L2 sequencer recently restarted, ensure the grace period has elapsed. */
        uint256 timeSinceUp = block.timestamp - uptimeStartedAt;
        if (timeSinceUp < uptimeFeedGracePeriod) revert GracePeriodNotOver();

        /**
         *  Get and validate the Chainlink ETH/USD price. Validate that the answer is a positive
         *  value, the round is complete, and the answer is not stale by round.
         *
         *  Ignore the deprecated answeredInRound value.
         *
         *  Ignore the price feed startedAt value, which isn't used in validations, since the
         *  priceUpdatedAt timestamp is more meaningful.
         *
         *  Slither flags this as an unused return value error, but this is safe since
         *  we use priceUpdatedAt and are interested in the latest value.
         */

        // slither-disable-next-line unused-return
        (uint80 priceRoundId, int256 answer,, uint256 priceUpdatedAt,) = priceFeed.latestRoundData();
        if (answer <= 0) revert InvalidPrice();
        if (priceRoundId == 0) revert IncompleteRound();
        if (priceUpdatedAt == 0) revert IncompleteRound();
        if (priceUpdatedAt > block.timestamp) revert InvalidRoundTimestamp();
        if (block.timestamp - priceUpdatedAt > priceFeedMaxAge) {
            revert StaleAnswer();
        }
        if (uint256(answer) < priceFeedMinAnswer || uint256(answer) > priceFeedMaxAnswer) revert PriceOutOfBounds();

        /* Set the last update timestamp and block. */
        lastPriceFeedUpdateTime = block.timestamp;
        lastPriceFeedUpdateBlock = block.number;

        if (prevEthUsdPrice == 0 && ethUsdPrice == 0) {
            /* If this is the very first price update, set previous equal to latest. */
            prevEthUsdPrice = ethUsdPrice = uint256(answer);
        } else {
            prevEthUsdPrice = ethUsdPrice;
            ethUsdPrice = uint256(answer);
        }
    }

    /**
     * @dev Calculate the cost in wei to rent storage units.
     */
    function _price(uint256 units) internal returns (uint256) {
        return _price(units, usdUnitPrice, _ethUsdPrice());
    }

    /**
     * @dev Calculate the cost in wei to rent storage units.
     *
     * @param units      Number of storage units. Integer, no decimals.
     * @param usdPerUnit Unit price in USD. Fixed point with 8 decimals.
     * @param usdPerEth  ETH/USD price. Fixed point with 8 decimals.
     *
     * @return uint256 price in wei, i.e. 18 decimals.
     */
    function _price(uint256 units, uint256 usdPerUnit, uint256 usdPerEth) internal pure returns (uint256) {
        return (units * usdPerUnit).divWadUp(usdPerEth);
    }

    /*//////////////////////////////////////////////////////////////
                         PERMISSIONED ACTIONS
    //////////////////////////////////////////////////////////////*/

    /**
     * @inheritdoc IStorageRegistry
     */
    function credit(uint256 fid, uint256 units) external onlyOperator whenNotDeprecated whenNotPaused {
        if (units == 0) revert InvalidAmount();
        if (rentedUnits + units > maxUnits) revert ExceedsCapacity();

        rentedUnits += units;
        emit Rent(msg.sender, fid, units);
    }

    /**
     * @inheritdoc IStorageRegistry
     */
    function batchCredit(
        uint256[] calldata fids,
        uint256 units
    ) external onlyOperator whenNotDeprecated whenNotPaused {
        if (units == 0) revert InvalidAmount();
        uint256 totalUnits = fids.length * units;
        if (rentedUnits + totalUnits > maxUnits) revert ExceedsCapacity();
        rentedUnits += totalUnits;
        for (uint256 i; i < fids.length; ++i) {
            emit Rent(msg.sender, fids[i], units);
        }
    }

    /**
     * @inheritdoc IStorageRegistry
     */
    function continuousCredit(
        uint256 start,
        uint256 end,
        uint256 units
    ) external onlyOperator whenNotDeprecated whenNotPaused {
        if (units == 0) revert InvalidAmount();
        if (start >= end) revert InvalidRangeInput();

        uint256 len = end - start + 1;
        uint256 totalUnits = len * units;
        if (rentedUnits + totalUnits > maxUnits) revert ExceedsCapacity();
        rentedUnits += totalUnits;
        for (uint256 i; i < len; ++i) {
            emit Rent(msg.sender, start + i, units);
        }
    }

    /**
     * @inheritdoc IStorageRegistry
     */
    function refreshPrice() external {
        if (!hasRole(OWNER_ROLE, msg.sender) && !hasRole(TREASURER_ROLE, msg.sender)) revert Unauthorized();
        _refreshPrice();
    }

    /**
     * @inheritdoc IStorageRegistry
     */
    function setPriceFeed(AggregatorV3Interface feed) external onlyOwner {
        emit SetPriceFeed(address(priceFeed), address(feed));
        priceFeed = feed;
    }

    /**
     * @inheritdoc IStorageRegistry
     */
    function setUptimeFeed(AggregatorV3Interface feed) external onlyOwner {
        emit SetUptimeFeed(address(uptimeFeed), address(feed));
        uptimeFeed = feed;
    }

    /**
     * @inheritdoc IStorageRegistry
     */
    function setPrice(uint256 usdPrice) external onlyOwner {
        emit SetPrice(usdUnitPrice, usdPrice);
        usdUnitPrice = usdPrice;
    }

    /**
     * @inheritdoc IStorageRegistry
     */
    function setFixedEthUsdPrice(uint256 fixedPrice) external onlyOwner {
        if (fixedPrice != 0) {
            if (fixedPrice < priceFeedMinAnswer || fixedPrice > priceFeedMaxAnswer) revert InvalidFixedPrice();
        }
        emit SetFixedEthUsdPrice(fixedEthUsdPrice, fixedPrice);
        fixedEthUsdPrice = fixedPrice;
    }

    /**
     * @inheritdoc IStorageRegistry
     */
    function setMaxUnits(uint256 max) external onlyOwner {
        emit SetMaxUnits(maxUnits, max);
        maxUnits = max;
    }

    /**
     * @inheritdoc IStorageRegistry
     */
    function setDeprecationTimestamp(uint256 timestamp) external onlyOwner {
        if (timestamp < block.timestamp) revert InvalidDeprecationTimestamp();
        emit SetDeprecationTimestamp(deprecationTimestamp, timestamp);
        deprecationTimestamp = timestamp;
    }

    /**
     * @inheritdoc IStorageRegistry
     */
    function setCacheDuration(uint256 duration) external onlyOwner {
        emit SetCacheDuration(priceFeedCacheDuration, duration);
        priceFeedCacheDuration = duration;
    }

    /**
     * @inheritdoc IStorageRegistry
     */
    function setMaxAge(uint256 age) external onlyOwner {
        emit SetMaxAge(priceFeedMaxAge, age);
        priceFeedMaxAge = age;
    }

    /**
     * @inheritdoc IStorageRegistry
     */
    function setMinAnswer(uint256 minPrice) external onlyOwner {
        if (minPrice >= priceFeedMaxAnswer) revert InvalidMinAnswer();
        emit SetMinAnswer(priceFeedMinAnswer, minPrice);
        priceFeedMinAnswer = minPrice;
    }

    /**
     * @inheritdoc IStorageRegistry
     */
    function setMaxAnswer(uint256 maxPrice) external onlyOwner {
        if (maxPrice <= priceFeedMinAnswer) revert InvalidMaxAnswer();
        emit SetMaxAnswer(priceFeedMaxAnswer, maxPrice);
        priceFeedMaxAnswer = maxPrice;
    }

    /**
     * @inheritdoc IStorageRegistry
     */
    function setGracePeriod(uint256 period) external onlyOwner {
        emit SetGracePeriod(uptimeFeedGracePeriod, period);
        uptimeFeedGracePeriod = period;
    }

    /**
     * @inheritdoc IStorageRegistry
     */
    function setVault(address vaultAddr) external onlyOwner {
        if (vaultAddr == address(0)) revert InvalidAddress();
        emit SetVault(vault, vaultAddr);
        vault = vaultAddr;
    }

    /**
     * @inheritdoc IStorageRegistry
     */
    function withdraw(uint256 amount) external onlyTreasurer {
        emit Withdraw(vault, amount);
        vault.sendNative(amount);
    }

    /**
     * @inheritdoc IStorageRegistry
     */
    function pause() external onlyOwner {
        _pause();
    }

    /**
     * @inheritdoc IStorageRegistry
     */
    function unpause() external onlyOwner {
        _unpause();
    }
}

// SPDX-License-Identifier: MIT
pragma solidity 0.8.21;

import {Nonces} from "openzeppelin-latest/contracts/utils/Nonces.sol";
import {Pausable} from "openzeppelin/contracts/security/Pausable.sol";

import {IdRegistry} from "./IdRegistry.sol";
import {IdRegistryLike} from "./interfaces/IdRegistryLike.sol";
import {IKeyRegistry} from "./interfaces/IKeyRegistry.sol";
import {IMetadataValidator} from "./interfaces/IMetadataValidator.sol";
import {EIP712} from "./lib/EIP712.sol";
import {Signatures} from "./lib/Signatures.sol";
import {TrustedCaller} from "./lib/TrustedCaller.sol";

/**
 * @title Farcaster KeyRegistry
 *
 * @notice See https://github.com/farcasterxyz/contracts/blob/v3.0.0/docs/docs.md for an overview.
 *
 * @custom:security-contact [email protected]
 */
contract KeyRegistry is IKeyRegistry, TrustedCaller, Signatures, Pausable, EIP712, Nonces {
    /*//////////////////////////////////////////////////////////////
                                 ERRORS
    //////////////////////////////////////////////////////////////*/

    /// @dev Revert if a key violates KeyState transition rules.
    error InvalidState();

    /// @dev Revert if a validator has not been registered for this keyType and metadataType.
    error ValidatorNotFound(uint32 keyType, uint8 metadataType);

    /// @dev Revert if metadata validation failed.
    error InvalidMetadata();

    /// @dev Revert if the admin sets a validator for keyType 0.
    error InvalidKeyType();

    /// @dev Revert if the admin sets a validator for metadataType 0.
    error InvalidMetadataType();

    /// @dev Revert if the caller does not have the authority to perform the action.
    error Unauthorized();

    /// @dev Revert if the owner calls migrateKeys more than once.
    error AlreadyMigrated();

    /*//////////////////////////////////////////////////////////////
                                 EVENTS
    //////////////////////////////////////////////////////////////*/

    /**
     * @dev Emit an event when an admin or fid adds a new key.
     *
     *      Hubs listen for this, validate that keyBytes is an EdDSA pub key and keyType == 1 and
     *      add keyBytes to its SignerStore. Messages signed by keyBytes with `fid` are now valid
     *      and accepted over gossip, sync and client apis. Hubs assume the invariants:
     *
     *      1. Add(fid, ..., key, keyBytes, ...) cannot emit if there is an earlier emit with
     *         Add(fid, ..., key, keyBytes, ...) and no AdminReset(fid, key, keyBytes) inbetween.
     *
     *      2. Add(fid, ..., key, keyBytes, ...) cannot emit if there is an earlier emit with
     *         Remove(fid, key, keyBytes).
     *
     *      3. For all Add(..., ..., key, keyBytes, ...), key = keccak(keyBytes)
     *
     * @param fid          The fid associated with the key.
     * @param keyType      The type of the key.
     * @param key          The key being registered. (indexed as hash)
     * @param keyBytes     The bytes of the key being registered.
     * @param metadataType The type of the metadata.
     * @param metadata     Metadata about the key.
     */
    event Add(
        uint256 indexed fid,
        uint32 indexed keyType,
        bytes indexed key,
        bytes keyBytes,
        uint8 metadataType,
        bytes metadata
    );

    /**
     * @dev Emit an event when an fid removes an added key.
     *
     *      Hubs listen for this, validate that keyType == 1 and keyBytes exists in its SignerStore.
     *      keyBytes is marked as removed, messages signed by keyBytes with `fid` are invalid,
     *      dropped immediately and no longer accepted. Hubs assume the invariants:
     *
     *      1. Remove(fid, key, keyBytes) cannot emit if there is no earlier emit with
     *         Add(fid, ..., key, keyBytes, ...)
     *
     *      2. Remove(fid, key, keyBytes) cannot emit if there is an earlier emit with
     *         Remove(fid, key, keyBytes)
     *
     *      3. For all Remove(..., key, keyBytes), key = keccak(keyBytes)
     *
     * @param fid       The fid associated with the key.
     * @param key       The key being registered. (indexed as hash)
     * @param keyBytes  The bytes of the key being registered.
     */
    event Remove(uint256 indexed fid, bytes indexed key, bytes keyBytes);

    /**
     * @dev Emit an event when an admin resets an added key.
     *
     *      Hubs listen for this, validate that keyType == 1 and that keyBytes exists in its SignerStore.
     *      keyBytes is no longer tracked, messages signed by keyBytes with `fid` are invalid, dropped
     *      immediately and not accepted. Hubs assume the following invariants:
     *
     *      1. AdminReset(fid, key, keyBytes) cannot emit unless the most recent event for the fid
     *         was Add(fid, ..., key, keyBytes, ...).
     *
     *      2. For all AdminReset(..., key, keyBytes), key = keccak(keyBytes).
     *
     *      3. AdminReset() cannot emit after Migrated().
     *
     * @param fid       The fid associated with the key.
     * @param key       The key being reset. (indexed as hash)
     * @param keyBytes  The bytes of the key being registered.
     */
    event AdminReset(uint256 indexed fid, bytes indexed key, bytes keyBytes);

    /**
     * @dev Emit an event when the admin calls migrateKeys. Used to migrate Hubs from using
     *      offchain signers to onchain signers.
     *
     *      Hubs listen for this and:
     *      1. Stop accepting Farcaster Signer messages with a timestamp >= keysMigratedAt.
     *      2. After the grace period (24 hours), stop accepting all Farcaster Signer messages.
     *      3. Drop any messages created by off-chain Farcaster Signers whose pub key was
     *         not emitted as an Add event.
     *
     *      If SignerMessages are not correctly migrated by an admin during the migration,
     *      there is a chance that there is some data loss, which is considered an acceptable
     *      risk for this migration.
     *
     *      If this event is emitted incorrectly ahead of schedule, new users cannot not post
     *      and existing users cannot add new apps. A protocol upgrade will be necessary
     *      which could take up to 6 weeks to roll out correctly.
     *
     * @param keysMigratedAt  The timestamp at which the migration occurred.
     */
    event Migrated(uint256 indexed keysMigratedAt);

    /**
     * @dev Emit an event when the admin sets a metadata validator contract for a given
     *      keyType and metadataType.
     *
     * @param keyType      The numeric keyType associated with this validator.
     * @param metadataType The metadataType associated with this validator.
     * @param oldValidator The previous validator contract address.
     * @param newValidator The new validator contract address.
     */
    event SetValidator(uint32 keyType, uint8 metadataType, address oldValidator, address newValidator);

    /**
     * @dev Emit an event when the admin sets a new IdRegistry contract address.
     *
     * @param oldIdRegistry The previous IdRegistry address.
     * @param newIdRegistry The ne IdRegistry address.
     */
    event SetIdRegistry(address oldIdRegistry, address newIdRegistry);

    /*//////////////////////////////////////////////////////////////
                              CONSTANTS
    //////////////////////////////////////////////////////////////*/

    /**
     * @inheritdoc IKeyRegistry
     */
    string public constant VERSION = "2023.08.23";

    /**
     * @inheritdoc IKeyRegistry
     */
    uint24 public constant gracePeriod = uint24(24 hours);

    /**
     * @inheritdoc IKeyRegistry
     */
    bytes32 public constant ADD_TYPEHASH = keccak256(
        "Add(address owner,uint32 keyType,bytes key,uint8 metadataType,bytes metadata,uint256 nonce,uint256 deadline)"
    );

    /**
     * @inheritdoc IKeyRegistry
     */
    bytes32 public constant REMOVE_TYPEHASH =
        keccak256("Remove(address owner,bytes key,uint256 nonce,uint256 deadline)");

    /*//////////////////////////////////////////////////////////////
                                STORAGE
    //////////////////////////////////////////////////////////////*/

    /**
     * @inheritdoc IKeyRegistry
     */
    IdRegistryLike public idRegistry;

    /**
     * @inheritdoc IKeyRegistry
     */
    uint40 public keysMigratedAt;

    /**
     * @dev Mapping of fid to a key to the key's data.
     *
     * @custom:param fid       The fid associated with the key.
     * @custom:param key       Bytes of the key.
     * @custom:param data      Struct with the state and key type. In the initial migration
     *                         all keys will have data.keyType == 1.
     */
    mapping(uint256 fid => mapping(bytes key => KeyData data)) public keys;

    /**
     * @dev Mapping of keyType to metadataType to validator contract.
     *
     * @custom:param keyType      Numeric keyType.
     * @custom:param metadataType Metadata metadataType.
     * @custom:param validator    Validator contract implementing IMetadataValidator.
     */
    mapping(uint32 keyType => mapping(uint8 metadataType => IMetadataValidator validator)) public validators;

    /*//////////////////////////////////////////////////////////////
                               CONSTRUCTOR
    //////////////////////////////////////////////////////////////*/

    /**
     * @notice Set the IdRegistry and owner.
     *
     * @param _idRegistry   IdRegistry contract address.
     * @param _initialOwner Initial contract owner address.
     */
    constructor(
        address _idRegistry,
        address _initialOwner
    ) TrustedCaller(_initialOwner) EIP712("Farcaster KeyRegistry", "1") {
        idRegistry = IdRegistryLike(_idRegistry);
    }

    /*//////////////////////////////////////////////////////////////
                                  VIEWS
    //////////////////////////////////////////////////////////////*/

    /**
     * @inheritdoc IKeyRegistry
     */
    function keyDataOf(uint256 fid, bytes calldata key) external view returns (KeyData memory) {
        return keys[fid][key];
    }

    /**
     * @inheritdoc IKeyRegistry
     */
    function isMigrated() public view returns (bool) {
        return keysMigratedAt != 0;
    }

    /*//////////////////////////////////////////////////////////////
                              REGISTRATION
    //////////////////////////////////////////////////////////////*/

    /**
     * @inheritdoc IKeyRegistry
     */
    function add(uint32 keyType, bytes calldata key, uint8 metadataType, bytes calldata metadata) external {
        _add(_fidOf(msg.sender), keyType, key, metadataType, metadata);
    }

    /**
     * @inheritdoc IKeyRegistry
     */
    function addFor(
        address fidOwner,
        uint32 keyType,
        bytes calldata key,
        uint8 metadataType,
        bytes calldata metadata,
        uint256 deadline,
        bytes calldata sig
    ) external {
        _verifyAddSig(fidOwner, keyType, key, metadataType, metadata, deadline, sig);
        _add(_fidOf(fidOwner), keyType, key, metadataType, metadata);
    }

    /**
     * @inheritdoc IKeyRegistry
     */
    function trustedAdd(
        address fidOwner,
        uint32 keyType,
        bytes calldata key,
        uint8 metadataType,
        bytes calldata metadata
    ) external onlyTrustedCaller {
        _add(_fidOf(fidOwner), keyType, key, metadataType, metadata);
    }

    /**
     * @inheritdoc IKeyRegistry
     */
    function remove(bytes calldata key) external {
        _remove(_fidOf(msg.sender), key);
    }

    /**
     * @inheritdoc IKeyRegistry
     */
    function removeFor(address fidOwner, bytes calldata key, uint256 deadline, bytes calldata sig) external {
        _verifyRemoveSig(fidOwner, key, deadline, sig);
        _remove(_fidOf(fidOwner), key);
    }

    /*//////////////////////////////////////////////////////////////
                                MIGRATION
    //////////////////////////////////////////////////////////////*/

    /**
     * @inheritdoc IKeyRegistry
     */
    function migrateKeys() external onlyOwner {
        if (isMigrated()) revert AlreadyMigrated();
        keysMigratedAt = uint40(block.timestamp);
        emit Migrated(keysMigratedAt);
    }

    /**
     * @inheritdoc IKeyRegistry
     */
    function bulkAddKeysForMigration(BulkAddData[] calldata items) external onlyOwner {
        if (isMigrated() && block.timestamp > keysMigratedAt + gracePeriod) {
            revert Unauthorized();
        }

        // Safety: i and j can be incremented unchecked since they are bound by items.length and
        // items[i].keys.length respectively.
        unchecked {
            for (uint256 i = 0; i < items.length; i++) {
                BulkAddData calldata item = items[i];
                for (uint256 j = 0; j < item.keys.length; j++) {
                    // TODO: add note about griefing during migration
                    _add(item.fid, 1, item.keys[j].key, 1, item.keys[j].metadata);
                }
            }
        }
    }

    /**
     * @inheritdoc IKeyRegistry
     */
    function bulkResetKeysForMigration(BulkResetData[] calldata items) external onlyOwner {
        if (isMigrated() && block.timestamp > keysMigratedAt + gracePeriod) {
            revert Unauthorized();
        }

        // Safety: i and j can be incremented unchecked since they are bound by items.length and
        // items[i].keys.length respectively.
        unchecked {
            for (uint256 i = 0; i < items.length; i++) {
                BulkResetData calldata item = items[i];
                for (uint256 j = 0; j < item.keys.length; j++) {
                    // TODO: add note about griefing during migration
                    _reset(item.fid, item.keys[j]);
                }
            }
        }
    }

    /*//////////////////////////////////////////////////////////////
                                 ADMIN
    //////////////////////////////////////////////////////////////*/

    /**
     * @inheritdoc IKeyRegistry
     */
    function setValidator(uint32 keyType, uint8 metadataType, IMetadataValidator validator) external onlyOwner {
        if (keyType == 0) revert InvalidKeyType();
        if (metadataType == 0) revert InvalidMetadataType();
        emit SetValidator(keyType, metadataType, address(validators[keyType][metadataType]), address(validator));
        validators[keyType][metadataType] = validator;
    }

    /**
     * @inheritdoc IKeyRegistry
     */
    function setIdRegistry(address _idRegistry) external onlyOwner {
        emit SetIdRegistry(address(idRegistry), _idRegistry);
        idRegistry = IdRegistryLike(_idRegistry);
    }

    /**
     * @inheritdoc IKeyRegistry
     */
    function pause() external onlyOwner {
        _pause();
    }

    /**
     * @inheritdoc IKeyRegistry
     */
    function unpause() external onlyOwner {
        _unpause();
    }

    /*//////////////////////////////////////////////////////////////
                                 HELPERS
    //////////////////////////////////////////////////////////////*/

    function _add(
        uint256 fid,
        uint32 keyType,
        bytes calldata key,
        uint8 metadataType,
        bytes calldata metadata
    ) internal whenNotPaused {
        KeyData storage keyData = keys[fid][key];
        if (keyData.state != KeyState.NULL) revert InvalidState();

        IMetadataValidator validator = validators[keyType][metadataType];
        if (validator == IMetadataValidator(address(0))) {
            revert ValidatorNotFound(keyType, metadataType);
        }
        bool isValid = validator.validate(fid, key, metadata);
        if (!isValid) revert InvalidMetadata();

        keyData.state = KeyState.ADDED;
        keyData.keyType = keyType;
        emit Add(fid, keyType, key, key, metadataType, metadata);
    }

    function _remove(uint256 fid, bytes calldata key) internal whenNotPaused {
        KeyData storage keyData = keys[fid][key];
        if (keyData.state != KeyState.ADDED) revert InvalidState();

        keyData.state = KeyState.REMOVED;
        emit Remove(fid, key, key);
    }

    function _reset(uint256 fid, bytes calldata key) internal whenNotPaused {
        KeyData storage keyData = keys[fid][key];
        if (keyData.state != KeyState.ADDED) revert InvalidState();

        keyData.state = KeyState.NULL;
        delete keyData.keyType;
        emit AdminReset(fid, key, key);
    }

    /*//////////////////////////////////////////////////////////////
                     SIGNATURE VERIFICATION HELPERS
    //////////////////////////////////////////////////////////////*/

    function _verifyAddSig(
        address fidOwner,
        uint32 keyType,
        bytes memory key,
        uint8 metadataType,
        bytes memory metadata,
        uint256 deadline,
        bytes memory sig
    ) internal {
        _verifySig(
            _hashTypedDataV4(
                keccak256(
                    abi.encode(
                        ADD_TYPEHASH,
                        fidOwner,
                        keyType,
                        keccak256(key),
                        metadataType,
                        keccak256(metadata),
                        _useNonce(fidOwner),
                        deadline
                    )
                )
            ),
            fidOwner,
            deadline,
            sig
        );
    }

    function _verifyRemoveSig(address fidOwner, bytes memory key, uint256 deadline, bytes memory sig) internal {
        _verifySig(
            _hashTypedDataV4(
                keccak256(abi.encode(REMOVE_TYPEHASH, fidOwner, keccak256(key), _useNonce(fidOwner), deadline))
            ),
            fidOwner,
            deadline,
            sig
        );
    }

    /*//////////////////////////////////////////////////////////////
                           FID HELPERS 
    //////////////////////////////////////////////////////////////*/

    function _fidOf(address fidOwner) internal view returns (uint256 fid) {
        fid = idRegistry.idOf(fidOwner);
        if (fid == 0) revert Unauthorized();
    }
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.21;

interface IBundler {
    /*//////////////////////////////////////////////////////////////
                                 STRUCTS
    //////////////////////////////////////////////////////////////*/

    /// @notice Data needed to trusted register a user with the fid and storage contracts.
    struct UserData {
        address to;
        address recovery;
        SignerData[] signers;
        uint256 units;
    }

    /// @notice Data needed to trusted register a signer with the key registry
    struct SignerData {
        uint32 keyType;
        bytes key;
        uint8 metadataType;
        bytes metadata;
    }

    /// @notice Data needed to register an fid with signature.
    struct RegistrationParams {
        address to;
        address recovery;
        uint256 deadline;
        bytes sig;
    }

    /// @notice Data needed to add a signer with signature.
    struct SignerParams {
        uint32 keyType;
        bytes key;
        uint8 metadataType;
        bytes metadata;
        uint256 deadline;
        bytes sig;
    }

    /*//////////////////////////////////////////////////////////////
                                CONSTANTS
    //////////////////////////////////////////////////////////////*/

    /**
     * @notice Contract version specified in the Farcaster protocol version scheme.
     */
    function VERSION() external view returns (string memory);

    /*//////////////////////////////////////////////////////////////
                                 REGISTRATION
    //////////////////////////////////////////////////////////////*/

    /**
     * @notice Register an fid, multiple signers, and rent storage to an address in a single transaction.
     *
     * @param registration Struct containing registration parameters: to, recovery, deadline, and signature.
     * @param signers      Array of structs containing signer parameters: keyType, key, metadataType, metadata, deadline, and signature.
     * @param storageUnits Number of storage units to rent
     *
     */
    function register(
        RegistrationParams calldata registration,
        SignerParams[] calldata signers,
        uint256 storageUnits
    ) external payable;

    /*//////////////////////////////////////////////////////////////
                         PERMISSIONED ACTIONS
    //////////////////////////////////////////////////////////////*/

    /**
     * @notice Register an fid, add a signer, and credit storage to an address in a single transaction. Can only
     *         be called by the trustedCaller during the Seedable phase.
     *
     * @param user UserData struct including to/recovery address, key params, and number of storage units.
     */
    function trustedRegister(UserData calldata user) external;

    /**
     * @notice Register fids, keys, and credit storage for multiple users in a single transaction. Can
     *         only be called by the trustedCaller during the Seedable phase. Will be used when
     *         migrating across Ethereum networks to bootstrap a new contract with existing data.
     *
     * @param users  Array of UserData structs to register
     */
    function trustedBatchRegister(UserData[] calldata users) external;
}

// SPDX-License-Identifier: MIT
pragma solidity 0.8.21;

import {Ownable2Step} from "openzeppelin/contracts/access/Ownable2Step.sol";

abstract contract TrustedCaller is Ownable2Step {
    /*//////////////////////////////////////////////////////////////
                                 ERRORS
    //////////////////////////////////////////////////////////////*/

    /// @dev Revert when an unauthorized caller calls a trusted function.
    error OnlyTrustedCaller();

    /// @dev Revert if trustedRegister is invoked after trustedCallerOnly is disabled.
    error Registrable();

    /// @dev Revert if register is invoked before trustedCallerOnly is disabled.
    error Seedable();

    /// @dev Revert when an invalid address is provided as input.
    error InvalidAddress();

    /*//////////////////////////////////////////////////////////////
                                 EVENTS
    //////////////////////////////////////////////////////////////*/

    /**
     * @dev Emit an event when the trusted caller is modified.
     *
     * @param oldCaller The address of the old trusted caller.
     * @param newCaller The address of the new trusted caller.
     * @param owner     The address of the owner setting the new caller.
     */
    event SetTrustedCaller(address indexed oldCaller, address indexed newCaller, address owner);

    /**
     * @dev Emit an event when the trustedOnly state is disabled.
     */
    event DisableTrustedOnly();

    /*//////////////////////////////////////////////////////////////
                              STORAGE
    //////////////////////////////////////////////////////////////*/

    /**
     * @dev The privileged address that is allowed to call trusted functions.
     */
    address public trustedCaller;

    /**
     * @dev Allows calling trusted functions when set 1, and disables trusted
     *      functions when set to 0. The value is set to 1 and can be changed to 0,
     *      but never back to 1.
     */
    uint256 public trustedOnly = 1;

    /*//////////////////////////////////////////////////////////////
                               MODIFIERS
    //////////////////////////////////////////////////////////////*/

    /**
     * @dev Allow only the trusted caller to call the modified function.
     */
    modifier onlyTrustedCaller() {
        if (trustedOnly == 0) revert Registrable();
        if (msg.sender != trustedCaller) revert OnlyTrustedCaller();
        _;
    }

    /**
     * @dev Prevent calling the modified function in trustedOnly mode.
     */
    modifier whenNotTrusted() {
        if (trustedOnly == 1) revert Seedable();
        _;
    }

    /*//////////////////////////////////////////////////////////////
                               CONSTRUCTOR
    //////////////////////////////////////////////////////////////*/

    /**
     * @param _initialOwner Initial contract owner address.
     */
    constructor(address _initialOwner) {
        _transferOwnership(_initialOwner);
    }

    /*//////////////////////////////////////////////////////////////
                         PERMISSIONED ACTIONS
    //////////////////////////////////////////////////////////////*/

    /**
     * @notice Change the trusted caller by calling this from the contract's owner.
     *
     * @param _trustedCaller The address of the new trusted caller
     */
    function setTrustedCaller(address _trustedCaller) public onlyOwner {
        _setTrustedCaller(_trustedCaller);
    }

    /**
     * @notice Disable trustedOnly mode. Must be called by the contract's owner.
     */
    function disableTrustedOnly() external onlyOwner {
        delete trustedOnly;
        emit DisableTrustedOnly();
    }

    /*//////////////////////////////////////////////////////////////
                         INTERNAL FUNCTIONS
    //////////////////////////////////////////////////////////////*/

    /**
     * @dev Internal helper to set trusted caller. Can be used internally
     *      to set the trusted caller at construction time.
     */
    function _setTrustedCaller(address _trustedCaller) internal {
        if (_trustedCaller == address(0)) revert InvalidAddress();

        emit SetTrustedCaller(trustedCaller, _trustedCaller, msg.sender);
        trustedCaller = _trustedCaller;
    }
}

// SPDX-License-Identifier: MIT
pragma solidity 0.8.21;

library TransferHelper {
    /// @dev Revert when a native token transfer fails.
    error CallFailed();

    /**
     * @dev Native token transfer helper.
     */
    function sendNative(address to, uint256 amount) internal {
        bool success;

        // solhint-disable-next-line no-inline-assembly
        assembly {
            // Transfer the native token and store if it succeeded or not.
            success := call(gas(), to, amount, 0, 0, 0, 0)
        }

        if (!success) revert CallFailed();
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/cryptography/SignatureChecker.sol)

pragma solidity ^0.8.0;

import "./ECDSA.sol";
import "../../interfaces/IERC1271.sol";

/**
 * @dev Signature verification helper that can be used instead of `ECDSA.recover` to seamlessly support both ECDSA
 * signatures from externally owned accounts (EOAs) as well as ERC1271 signatures from smart contract wallets like
 * Argent and Gnosis Safe.
 *
 * _Available since v4.1._
 */
library SignatureChecker {
    /**
     * @dev Checks if a signature is valid for a given signer and data hash. If the signer is a smart contract, the
     * signature is validated against that smart contract using ERC1271, otherwise it's validated using `ECDSA.recover`.
     *
     * NOTE: Unlike ECDSA signatures, contract signatures are revocable, and the outcome of this function can thus
     * change through time. It could return true at block N and false at block N+1 (or the opposite).
     */
    function isValidSignatureNow(address signer, bytes32 hash, bytes memory signature) internal view returns (bool) {
        (address recovered, ECDSA.RecoverError error) = ECDSA.tryRecover(hash, signature);
        return
            (error == ECDSA.RecoverError.NoError && recovered == signer) ||
            isValidERC1271SignatureNow(signer, hash, signature);
    }

    /**
     * @dev Checks if a signature is valid for a given signer and data hash. The signature is validated
     * against the signer smart contract using ERC1271.
     *
     * NOTE: Unlike ECDSA signatures, contract signatures are revocable, and the outcome of this function can thus
     * change through time. It could return true at block N and false at block N+1 (or the opposite).
     */
    function isValidERC1271SignatureNow(
        address signer,
        bytes32 hash,
        bytes memory signature
    ) internal view returns (bool) {
        (bool success, bytes memory result) = signer.staticcall(
            abi.encodeWithSelector(IERC1271.isValidSignature.selector, hash, signature)
        );
        return (success &&
            result.length >= 32 &&
            abi.decode(result, (bytes32)) == bytes32(IERC1271.isValidSignature.selector));
    }
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

/**
 * @dev Provides tracking nonces for addresses. Nonces will only increment.
 */
abstract contract Nonces {
    /**
     * @dev The nonce used for an `account` is not the expected current nonce.
     */
    error InvalidAccountNonce(address account, uint256 currentNonce);

    mapping(address => uint256) private _nonces;

    /**
     * @dev Returns an the next unused nonce for an address.
     */
    function nonces(address owner) public view virtual returns (uint256) {
        return _nonces[owner];
    }

    /**
     * @dev Consumes a nonce.
     *
     * Returns the current value and increments nonce.
     */
    function _useNonce(address owner) internal virtual returns (uint256) {
        // For each account, the nonce has an initial value of 0, can only be incremented by one, and cannot be
        // decremented or reset. This guarantees that the nonce never overflows.
        unchecked {
            // It is important to do x++ and not ++x here.
            return _nonces[owner]++;
        }
    }

    /**
     * @dev Same as {_useNonce} but checking that `nonce` is the next valid for `owner`.
     */
    function _useCheckedNonce(address owner, uint256 nonce) internal virtual returns (uint256) {
        uint256 current = _useNonce(owner);
        if (nonce != current) {
            revert InvalidAccountNonce(owner, current);
        }
        return current;
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (security/Pausable.sol)

pragma solidity ^0.8.0;

import "../utils/Context.sol";

/**
 * @dev Contract module which allows children to implement an emergency stop
 * mechanism that can be triggered by an authorized account.
 *
 * This module is used through inheritance. It will make available the
 * modifiers `whenNotPaused` and `whenPaused`, which can be applied to
 * the functions of your contract. Note that they will not be pausable by
 * simply including this module, only once the modifiers are put in place.
 */
abstract contract Pausable is Context {
    /**
     * @dev Emitted when the pause is triggered by `account`.
     */
    event Paused(address account);

    /**
     * @dev Emitted when the pause is lifted by `account`.
     */
    event Unpaused(address account);

    bool private _paused;

    /**
     * @dev Initializes the contract in unpaused state.
     */
    constructor() {
        _paused = false;
    }

    /**
     * @dev Modifier to make a function callable only when the contract is not paused.
     *
     * Requirements:
     *
     * - The contract must not be paused.
     */
    modifier whenNotPaused() {
        _requireNotPaused();
        _;
    }

    /**
     * @dev Modifier to make a function callable only when the contract is paused.
     *
     * Requirements:
     *
     * - The contract must be paused.
     */
    modifier whenPaused() {
        _requirePaused();
        _;
    }

    /**
     * @dev Returns true if the contract is paused, and false otherwise.
     */
    function paused() public view virtual returns (bool) {
        return _paused;
    }

    /**
     * @dev Throws if the contract is paused.
     */
    function _requireNotPaused() internal view virtual {
        require(!paused(), "Pausable: paused");
    }

    /**
     * @dev Throws if the contract is not paused.
     */
    function _requirePaused() internal view virtual {
        require(paused(), "Pausable: not paused");
    }

    /**
     * @dev Triggers stopped state.
     *
     * Requirements:
     *
     * - The contract must not be paused.
     */
    function _pause() internal virtual whenNotPaused {
        _paused = true;
        emit Paused(_msgSender());
    }

    /**
     * @dev Returns to normal state.
     *
     * Requirements:
     *
     * - The contract must be paused.
     */
    function _unpause() internal virtual whenPaused {
        _paused = false;
        emit Unpaused(_msgSender());
    }
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.21;

interface IIdRegistry {
    /*//////////////////////////////////////////////////////////////
                              CONSTANTS
    //////////////////////////////////////////////////////////////*/

    /**
     * @notice Defined for compatibility with tools like Etherscan that detect fid
     *         transfers as token transfers. This is intentionally lowercased.
     */
    function name() external view returns (string memory);

    /**
     * @notice Contract version specified in the Farcaster protocol version scheme.
     */
    function VERSION() external view returns (string memory);

    /**
     * @notice EIP-712 typehash for Register signatures.
     */
    function REGISTER_TYPEHASH() external view returns (bytes32);

    /**
     * @notice EIP-712 typehash for Transfer signatures.
     */
    function TRANSFER_TYPEHASH() external view returns (bytes32);

    /**
     * @notice EIP-712 typehash for ChangeRecoveryAddress signatures.
     */
    function CHANGE_RECOVERY_ADDRESS_TYPEHASH() external view returns (bytes32);

    /*//////////////////////////////////////////////////////////////
                                 STORAGE
    //////////////////////////////////////////////////////////////*/

    /**
     * @notice The last Farcaster id that was issued.
     */
    function idCounter() external view returns (uint256);

    /**
     * @notice Maps each address to an fid, or zero if it does not own an fid.
     */
    function idOf(address owner) external view returns (uint256 fid);

    /**
     * @notice Maps each fid to an address that can initiate a recovery.
     */
    function recoveryOf(uint256 fid) external view returns (address recovery);

    /*//////////////////////////////////////////////////////////////
                             REGISTRATION LOGIC
    //////////////////////////////////////////////////////////////*/

    /**
     * @notice Register a new Farcaster ID (fid) to the caller. The caller must not have an fid.
     *         The contract must not be in the Registrable (trustedOnly = 0) state.
     *
     * @param recovery Address which can recover the fid. Set to zero to disable recovery.
     *
     * @return fid registered FID.
     */
    function register(address recovery) external returns (uint256 fid);

    /**
     * @notice Register a new Farcaster ID (fid) to any address. A signed message from the address
     *         must be provided which approves both the to and the recovery. The address must not
     *         have an fid. The contract must be in the Registrable (trustedOnly = 0) state.
     *
     * @param to       Address which will own the fid.
     * @param recovery Address which can recover the fid. Set to zero to disable recovery.
     * @param deadline Expiration timestamp of the signature.
     * @param sig      EIP-712 Register signature signed by the to address.
     *
     * @return fid registered FID.
     */
    function registerFor(
        address to,
        address recovery,
        uint256 deadline,
        bytes calldata sig
    ) external returns (uint256 fid);

    /*//////////////////////////////////////////////////////////////
                             TRANSFER LOGIC
    //////////////////////////////////////////////////////////////*/

    /**
     * @notice Transfer the fid owned by this address to another address that does not have an fid.
     *         A signed Transfer message from the destination address must be provided.
     *
     * @param to       The address to transfer the fid to.
     * @param deadline Expiration timestamp of the signature.
     * @param sig      EIP-712 Transfer signature signed by the to address.
     */
    function transfer(address to, uint256 deadline, bytes calldata sig) external;

    /**
     * @notice Transfer the fid owned by the from address to another address that does not
     *         have an fid. Caller must provide two signed Transfer messages: one signed by
     *         the from address and one signed by the to address.
     *
     * @param from         The owner address of the fid to transfer.
     * @param to           The address to transfer the fid to.
     * @param fromDeadline Expiration timestamp of the from signature.
     * @param fromSig      EIP-712 Transfer signature signed by the from address.
     * @param toDeadline   Expiration timestamp of the to signature.
     * @param toSig        EIP-712 Transfer signature signed by the to address.
     */
    function transferFor(
        address from,
        address to,
        uint256 fromDeadline,
        bytes calldata fromSig,
        uint256 toDeadline,
        bytes calldata toSig
    ) external;

    /*//////////////////////////////////////////////////////////////
                             RECOVERY LOGIC
    //////////////////////////////////////////////////////////////*/

    /**
     * @notice Change the recovery address of the fid owned by the caller.
     *
     * @param recovery The address which can recover the fid. Set to 0x0 to disable recovery.
     */
    function changeRecoveryAddress(address recovery) external;

    /**
     * @notice Change the recovery address of fid owned by the owner. Caller must provide an
     *         EIP-712 ChangeRecoveryAddress message signed by the owner.
     *
     * @param owner    Custody address of the fid whose recovery address will be changed.
     * @param recovery The address which can recover the fid. Set to 0x0 to disable recovery.
     * @param deadline Expiration timestamp of the ChangeRecoveryAddress signature.
     * @param sig      EIP-712 ChangeRecoveryAddress message signed by the owner address.
     */
    function changeRecoveryAddressFor(address owner, address recovery, uint256 deadline, bytes calldata sig) external;

    /**
     * @notice Transfer the fid from the from address to the to address. Must be called by the
     *         recovery address. A signed message from the to address must be provided.
     *
     * @param from     The address that currently owns the fid.
     * @param to       The address to transfer the fid to.
     * @param deadline Expiration timestamp of the signature.
     * @param sig      EIP-712 Transfer signature signed by the to address.
     */
    function recover(address from, address to, uint256 deadline, bytes calldata sig) external;

    /**
     * @notice Transfer the fid owned by the from address to another address that does not
     *         have an fid. Caller must provide two signed Transfer messages: one signed by
     *         the recovery address and one signed by the to address.
     *
     * @param from             The owner address of the fid to transfer.
     * @param to               The address to transfer the fid to.
     * @param recoveryDeadline Expiration timestamp of the recovery signature.
     * @param recoverySig      EIP-712 Transfer signature signed by the recovery address.
     * @param toDeadline       Expiration timestamp of the to signature.
     * @param toSig            EIP-712 Transfer signature signed by the to address.
     */
    function recoverFor(
        address from,
        address to,
        uint256 recoveryDeadline,
        bytes calldata recoverySig,
        uint256 toDeadline,
        bytes calldata toSig
    ) external;

    /*//////////////////////////////////////////////////////////////
                                 VIEWS
    //////////////////////////////////////////////////////////////*/

    /**
     * @notice Verify that a signature was produced by the custody address that owns an fid.
     *
     * @param custodyAddress   The address to check the signature of.
     * @param fid              The fid to check the signature of.
     * @param digest           The digest that was signed.
     * @param sig              The signature to check.
     *
     * @return isValid Whether provided signature is valid.
     */
    function verifyFidSignature(
        address custodyAddress,
        uint256 fid,
        bytes32 digest,
        bytes calldata sig
    ) external view returns (bool isValid);

    /*//////////////////////////////////////////////////////////////
                         PERMISSIONED ACTIONS
    //////////////////////////////////////////////////////////////*/

    /**
     * @notice Register a new Farcaster ID (fid) to any address. The address must not have an fid.
     *         The contract must be in the Seedable (trustedOnly = 1) state.
     *         Can only be called by the trustedCaller.
     *
     * @param to       The address which will own the fid.
     * @param recovery The address which can recover the fid.
     *
     * @return fid registered FID.
     */
    function trustedRegister(address to, address recovery) external returns (uint256 fid);

    /**
     * @notice Pause registration, transfer, and recovery.
     *         Must be called by the owner.
     */
    function pause() external;

    /**
     * @notice Unpause registration, transfer, and recovery.
     *         Must be called by the owner.
     */
    function unpause() external;
}

// SPDX-License-Identifier: MIT
pragma solidity 0.8.21;

import {EIP712 as EIP712Base} from "openzeppelin/contracts/utils/cryptography/EIP712.sol";

abstract contract EIP712 is EIP712Base {
    constructor(string memory name, string memory version) EIP712Base(name, version) {}

    /*//////////////////////////////////////////////////////////////
                           EIP-712 HELPERS
    //////////////////////////////////////////////////////////////*/

    /**
     * @notice Helper view to read EIP-712 domain separator.
     *
     * @return bytes32 domain separator hash.
     */
    function domainSeparatorV4() external view returns (bytes32) {
        return _domainSeparatorV4();
    }

    /**
     * @notice Helper view to hash EIP-712 typed data onchain.
     *
     * @param structHash EIP-712 typed data hash.
     *
     * @return bytes32 EIP-712 message digest.
     */
    function hashTypedDataV4(bytes32 structHash) external view returns (bytes32) {
        return _hashTypedDataV4(structHash);
    }
}

File 13 of 38 : Signatures.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.21;

import {SignatureChecker} from "openzeppelin/contracts/utils/cryptography/SignatureChecker.sol";

abstract contract Signatures {
    /*//////////////////////////////////////////////////////////////
                                 ERRORS
    //////////////////////////////////////////////////////////////*/

    /// @dev Revert when the signature provided is invalid.
    error InvalidSignature();

    /// @dev Revert when the block.timestamp is ahead of the signature deadline.
    error SignatureExpired();

    /*//////////////////////////////////////////////////////////////
                     SIGNATURE VERIFICATION HELPERS
    //////////////////////////////////////////////////////////////*/

    function _verifySig(bytes32 digest, address signer, uint256 deadline, bytes memory sig) internal view {
        if (block.timestamp >= deadline) revert SignatureExpired();
        if (!SignatureChecker.isValidSignatureNow(signer, digest, sig)) {
            revert InvalidSignature();
        }
    }
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

interface AggregatorV3Interface {

  function decimals()
    external
    view
    returns (
      uint8
    );

  function description()
    external
    view
    returns (
      string memory
    );

  function version()
    external
    view
    returns (
      uint256
    );

  // getRoundData and latestRoundData should both raise "No data present"
  // if they do not have data to report, instead of returning unset values
  // which could be misinterpreted as actual reported values.
  function getRoundData(
    uint80 _roundId
  )
    external
    view
    returns (
      uint80 roundId,
      int256 answer,
      uint256 startedAt,
      uint256 updatedAt,
      uint80 answeredInRound
    );

  function latestRoundData()
    external
    view
    returns (
      uint80 roundId,
      int256 answer,
      uint256 startedAt,
      uint256 updatedAt,
      uint80 answeredInRound
    );

}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.0) (access/AccessControlEnumerable.sol)

pragma solidity ^0.8.0;

import "./IAccessControlEnumerable.sol";
import "./AccessControl.sol";
import "../utils/structs/EnumerableSet.sol";

/**
 * @dev Extension of {AccessControl} that allows enumerating the members of each role.
 */
abstract contract AccessControlEnumerable is IAccessControlEnumerable, AccessControl {
    using EnumerableSet for EnumerableSet.AddressSet;

    mapping(bytes32 => EnumerableSet.AddressSet) private _roleMembers;

    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
        return interfaceId == type(IAccessControlEnumerable).interfaceId || super.supportsInterface(interfaceId);
    }

    /**
     * @dev Returns one of the accounts that have `role`. `index` must be a
     * value between 0 and {getRoleMemberCount}, non-inclusive.
     *
     * Role bearers are not sorted in any particular way, and their ordering may
     * change at any point.
     *
     * WARNING: When using {getRoleMember} and {getRoleMemberCount}, make sure
     * you perform all queries on the same block. See the following
     * https://forum.openzeppelin.com/t/iterating-over-elements-on-enumerableset-in-openzeppelin-contracts/2296[forum post]
     * for more information.
     */
    function getRoleMember(bytes32 role, uint256 index) public view virtual override returns (address) {
        return _roleMembers[role].at(index);
    }

    /**
     * @dev Returns the number of accounts that have `role`. Can be used
     * together with {getRoleMember} to enumerate all bearers of a role.
     */
    function getRoleMemberCount(bytes32 role) public view virtual override returns (uint256) {
        return _roleMembers[role].length();
    }

    /**
     * @dev Overload {_grantRole} to track enumerable memberships
     */
    function _grantRole(bytes32 role, address account) internal virtual override {
        super._grantRole(role, account);
        _roleMembers[role].add(account);
    }

    /**
     * @dev Overload {_revokeRole} to track enumerable memberships
     */
    function _revokeRole(bytes32 role, address account) internal virtual override {
        super._revokeRole(role, account);
        _roleMembers[role].remove(account);
    }
}

// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;

/// @notice Arithmetic library with operations for fixed-point numbers.
/// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/utils/FixedPointMathLib.sol)
library FixedPointMathLib {
    /*//////////////////////////////////////////////////////////////
                    SIMPLIFIED FIXED POINT OPERATIONS
    //////////////////////////////////////////////////////////////*/

    uint256 internal constant WAD = 1e18; // The scalar of ETH and most ERC20s.

    function mulWadDown(uint256 x, uint256 y) internal pure returns (uint256) {
        return mulDivDown(x, y, WAD); // Equivalent to (x * y) / WAD rounded down.
    }

    function mulWadUp(uint256 x, uint256 y) internal pure returns (uint256) {
        return mulDivUp(x, y, WAD); // Equivalent to (x * y) / WAD rounded up.
    }

    function divWadDown(uint256 x, uint256 y) internal pure returns (uint256) {
        return mulDivDown(x, WAD, y); // Equivalent to (x * WAD) / y rounded down.
    }

    function divWadUp(uint256 x, uint256 y) internal pure returns (uint256) {
        return mulDivUp(x, WAD, y); // Equivalent to (x * WAD) / y rounded up.
    }

    function powWad(int256 x, int256 y) internal pure returns (int256) {
        // Equivalent to x to the power of y because x ** y = (e ** ln(x)) ** y = e ** (ln(x) * y)
        return expWad((lnWad(x) * y) / int256(WAD)); // Using ln(x) means x must be greater than 0.
    }

    function expWad(int256 x) internal pure returns (int256 r) {
        unchecked {
            // When the result is < 0.5 we return zero. This happens when
            // x <= floor(log(0.5e18) * 1e18) ~ -42e18
            if (x <= -42139678854452767551) return 0;

            // When the result is > (2**255 - 1) / 1e18 we can not represent it as an
            // int. This happens when x >= floor(log((2**255 - 1) / 1e18) * 1e18) ~ 135.
            if (x >= 135305999368893231589) revert("EXP_OVERFLOW");

            // x is now in the range (-42, 136) * 1e18. Convert to (-42, 136) * 2**96
            // for more intermediate precision and a binary basis. This base conversion
            // is a multiplication by 1e18 / 2**96 = 5**18 / 2**78.
            x = (x << 78) / 5**18;

            // Reduce range of x to (-½ ln 2, ½ ln 2) * 2**96 by factoring out powers
            // of two such that exp(x) = exp(x') * 2**k, where k is an integer.
            // Solving this gives k = round(x / log(2)) and x' = x - k * log(2).
            int256 k = ((x << 96) / 54916777467707473351141471128 + 2**95) >> 96;
            x = x - k * 54916777467707473351141471128;

            // k is in the range [-61, 195].

            // Evaluate using a (6, 7)-term rational approximation.
            // p is made monic, we'll multiply by a scale factor later.
            int256 y = x + 1346386616545796478920950773328;
            y = ((y * x) >> 96) + 57155421227552351082224309758442;
            int256 p = y + x - 94201549194550492254356042504812;
            p = ((p * y) >> 96) + 28719021644029726153956944680412240;
            p = p * x + (4385272521454847904659076985693276 << 96);

            // We leave p in 2**192 basis so we don't need to scale it back up for the division.
            int256 q = x - 2855989394907223263936484059900;
            q = ((q * x) >> 96) + 50020603652535783019961831881945;
            q = ((q * x) >> 96) - 533845033583426703283633433725380;
            q = ((q * x) >> 96) + 3604857256930695427073651918091429;
            q = ((q * x) >> 96) - 14423608567350463180887372962807573;
            q = ((q * x) >> 96) + 26449188498355588339934803723976023;

            assembly {
                // Div in assembly because solidity adds a zero check despite the unchecked.
                // The q polynomial won't have zeros in the domain as all its roots are complex.
                // No scaling is necessary because p is already 2**96 too large.
                r := sdiv(p, q)
            }

            // r should be in the range (0.09, 0.25) * 2**96.

            // We now need to multiply r by:
            // * the scale factor s = ~6.031367120.
            // * the 2**k factor from the range reduction.
            // * the 1e18 / 2**96 factor for base conversion.
            // We do this all at once, with an intermediate result in 2**213
            // basis, so the final right shift is always by a positive amount.
            r = int256((uint256(r) * 3822833074963236453042738258902158003155416615667) >> uint256(195 - k));
        }
    }

    function lnWad(int256 x) internal pure returns (int256 r) {
        unchecked {
            require(x > 0, "UNDEFINED");

            // We want to convert x from 10**18 fixed point to 2**96 fixed point.
            // We do this by multiplying by 2**96 / 10**18. But since
            // ln(x * C) = ln(x) + ln(C), we can simply do nothing here
            // and add ln(2**96 / 10**18) at the end.

            // Reduce range of x to (1, 2) * 2**96
            // ln(2^k * x) = k * ln(2) + ln(x)
            int256 k = int256(log2(uint256(x))) - 96;
            x <<= uint256(159 - k);
            x = int256(uint256(x) >> 159);

            // Evaluate using a (8, 8)-term rational approximation.
            // p is made monic, we will multiply by a scale factor later.
            int256 p = x + 3273285459638523848632254066296;
            p = ((p * x) >> 96) + 24828157081833163892658089445524;
            p = ((p * x) >> 96) + 43456485725739037958740375743393;
            p = ((p * x) >> 96) - 11111509109440967052023855526967;
            p = ((p * x) >> 96) - 45023709667254063763336534515857;
            p = ((p * x) >> 96) - 14706773417378608786704636184526;
            p = p * x - (795164235651350426258249787498 << 96);

            // We leave p in 2**192 basis so we don't need to scale it back up for the division.
            // q is monic by convention.
            int256 q = x + 5573035233440673466300451813936;
            q = ((q * x) >> 96) + 71694874799317883764090561454958;
            q = ((q * x) >> 96) + 283447036172924575727196451306956;
            q = ((q * x) >> 96) + 401686690394027663651624208769553;
            q = ((q * x) >> 96) + 204048457590392012362485061816622;
            q = ((q * x) >> 96) + 31853899698501571402653359427138;
            q = ((q * x) >> 96) + 909429971244387300277376558375;
            assembly {
                // Div in assembly because solidity adds a zero check despite the unchecked.
                // The q polynomial is known not to have zeros in the domain.
                // No scaling required because p is already 2**96 too large.
                r := sdiv(p, q)
            }

            // r is in the range (0, 0.125) * 2**96

            // Finalization, we need to:
            // * multiply by the scale factor s = 5.549…
            // * add ln(2**96 / 10**18)
            // * add k * ln(2)
            // * multiply by 10**18 / 2**96 = 5**18 >> 78

            // mul s * 5e18 * 2**96, base is now 5**18 * 2**192
            r *= 1677202110996718588342820967067443963516166;
            // add ln(2) * k * 5e18 * 2**192
            r += 16597577552685614221487285958193947469193820559219878177908093499208371 * k;
            // add ln(2**96 / 10**18) * 5e18 * 2**192
            r += 600920179829731861736702779321621459595472258049074101567377883020018308;
            // base conversion: mul 2**18 / 2**192
            r >>= 174;
        }
    }

    /*//////////////////////////////////////////////////////////////
                    LOW LEVEL FIXED POINT OPERATIONS
    //////////////////////////////////////////////////////////////*/

    function mulDivDown(
        uint256 x,
        uint256 y,
        uint256 denominator
    ) internal pure returns (uint256 z) {
        assembly {
            // Store x * y in z for now.
            z := mul(x, y)

            // Equivalent to require(denominator != 0 && (x == 0 || (x * y) / x == y))
            if iszero(and(iszero(iszero(denominator)), or(iszero(x), eq(div(z, x), y)))) {
                revert(0, 0)
            }

            // Divide z by the denominator.
            z := div(z, denominator)
        }
    }

    function mulDivUp(
        uint256 x,
        uint256 y,
        uint256 denominator
    ) internal pure returns (uint256 z) {
        assembly {
            // Store x * y in z for now.
            z := mul(x, y)

            // Equivalent to require(denominator != 0 && (x == 0 || (x * y) / x == y))
            if iszero(and(iszero(iszero(denominator)), or(iszero(x), eq(div(z, x), y)))) {
                revert(0, 0)
            }

            // First, divide z - 1 by the denominator and add 1.
            // We allow z - 1 to underflow if z is 0, because we multiply the
            // end result by 0 if z is zero, ensuring we return 0 if z is zero.
            z := mul(iszero(iszero(z)), add(div(sub(z, 1), denominator), 1))
        }
    }

    function rpow(
        uint256 x,
        uint256 n,
        uint256 scalar
    ) internal pure returns (uint256 z) {
        assembly {
            switch x
            case 0 {
                switch n
                case 0 {
                    // 0 ** 0 = 1
                    z := scalar
                }
                default {
                    // 0 ** n = 0
                    z := 0
                }
            }
            default {
                switch mod(n, 2)
                case 0 {
                    // If n is even, store scalar in z for now.
                    z := scalar
                }
                default {
                    // If n is odd, store x in z for now.
                    z := x
                }

                // Shifting right by 1 is like dividing by 2.
                let half := shr(1, scalar)

                for {
                    // Shift n right by 1 before looping to halve it.
                    n := shr(1, n)
                } n {
                    // Shift n right by 1 each iteration to halve it.
                    n := shr(1, n)
                } {
                    // Revert immediately if x ** 2 would overflow.
                    // Equivalent to iszero(eq(div(xx, x), x)) here.
                    if shr(128, x) {
                        revert(0, 0)
                    }

                    // Store x squared.
                    let xx := mul(x, x)

                    // Round to the nearest number.
                    let xxRound := add(xx, half)

                    // Revert if xx + half overflowed.
                    if lt(xxRound, xx) {
                        revert(0, 0)
                    }

                    // Set x to scaled xxRound.
                    x := div(xxRound, scalar)

                    // If n is even:
                    if mod(n, 2) {
                        // Compute z * x.
                        let zx := mul(z, x)

                        // If z * x overflowed:
                        if iszero(eq(div(zx, x), z)) {
                            // Revert if x is non-zero.
                            if iszero(iszero(x)) {
                                revert(0, 0)
                            }
                        }

                        // Round to the nearest number.
                        let zxRound := add(zx, half)

                        // Revert if zx + half overflowed.
                        if lt(zxRound, zx) {
                            revert(0, 0)
                        }

                        // Return properly scaled zxRound.
                        z := div(zxRound, scalar)
                    }
                }
            }
        }
    }

    /*//////////////////////////////////////////////////////////////
                        GENERAL NUMBER UTILITIES
    //////////////////////////////////////////////////////////////*/

    function sqrt(uint256 x) internal pure returns (uint256 z) {
        assembly {
            let y := x // We start y at x, which will help us make our initial estimate.

            z := 181 // The "correct" value is 1, but this saves a multiplication later.

            // This segment is to get a reasonable initial estimate for the Babylonian method. With a bad
            // start, the correct # of bits increases ~linearly each iteration instead of ~quadratically.

            // We check y >= 2^(k + 8) but shift right by k bits
            // each branch to ensure that if x >= 256, then y >= 256.
            if iszero(lt(y, 0x10000000000000000000000000000000000)) {
                y := shr(128, y)
                z := shl(64, z)
            }
            if iszero(lt(y, 0x1000000000000000000)) {
                y := shr(64, y)
                z := shl(32, z)
            }
            if iszero(lt(y, 0x10000000000)) {
                y := shr(32, y)
                z := shl(16, z)
            }
            if iszero(lt(y, 0x1000000)) {
                y := shr(16, y)
                z := shl(8, z)
            }

            // Goal was to get z*z*y within a small factor of x. More iterations could
            // get y in a tighter range. Currently, we will have y in [256, 256*2^16).
            // We ensured y >= 256 so that the relative difference between y and y+1 is small.
            // That's not possible if x < 256 but we can just verify those cases exhaustively.

            // Now, z*z*y <= x < z*z*(y+1), and y <= 2^(16+8), and either y >= 256, or x < 256.
            // Correctness can be checked exhaustively for x < 256, so we assume y >= 256.
            // Then z*sqrt(y) is within sqrt(257)/sqrt(256) of sqrt(x), or about 20bps.

            // For s in the range [1/256, 256], the estimate f(s) = (181/1024) * (s+1) is in the range
            // (1/2.84 * sqrt(s), 2.84 * sqrt(s)), with largest error when s = 1 and when s = 256 or 1/256.

            // Since y is in [256, 256*2^16), let a = y/65536, so that a is in [1/256, 256). Then we can estimate
            // sqrt(y) using sqrt(65536) * 181/1024 * (a + 1) = 181/4 * (y + 65536)/65536 = 181 * (y + 65536)/2^18.

            // There is no overflow risk here since y < 2^136 after the first branch above.
            z := shr(18, mul(z, add(y, 65536))) // A mul() is saved from starting z at 181.

            // Given the worst case multiplicative error of 2.84 above, 7 iterations should be enough.
            z := shr(1, add(z, div(x, z)))
            z := shr(1, add(z, div(x, z)))
            z := shr(1, add(z, div(x, z)))
            z := shr(1, add(z, div(x, z)))
            z := shr(1, add(z, div(x, z)))
            z := shr(1, add(z, div(x, z)))
            z := shr(1, add(z, div(x, z)))

            // If x+1 is a perfect square, the Babylonian method cycles between
            // floor(sqrt(x)) and ceil(sqrt(x)). This statement ensures we return floor.
            // See: https://en.wikipedia.org/wiki/Integer_square_root#Using_only_integer_division
            // Since the ceil is rare, we save gas on the assignment and repeat division in the rare case.
            // If you don't care whether the floor or ceil square root is returned, you can remove this statement.
            z := sub(z, lt(div(x, z), z))
        }
    }

    function log2(uint256 x) internal pure returns (uint256 r) {
        require(x > 0, "UNDEFINED");

        assembly {
            r := shl(7, lt(0xffffffffffffffffffffffffffffffff, x))
            r := or(r, shl(6, lt(0xffffffffffffffff, shr(r, x))))
            r := or(r, shl(5, lt(0xffffffff, shr(r, x))))
            r := or(r, shl(4, lt(0xffff, shr(r, x))))
            r := or(r, shl(3, lt(0xff, shr(r, x))))
            r := or(r, shl(2, lt(0xf, shr(r, x))))
            r := or(r, shl(1, lt(0x3, shr(r, x))))
            r := or(r, lt(0x1, shr(r, x)))
        }
    }
}

File 17 of 38 : IStorageRegistry.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.21;

import {AggregatorV3Interface} from "chainlink/v0.8/interfaces/AggregatorV3Interface.sol";

interface IStorageRegistry {
    /*//////////////////////////////////////////////////////////////
                                CONSTANTS
    //////////////////////////////////////////////////////////////*/

    /**
     * @notice Contract version specified in the Farcaster protocol version scheme.
     */
    function VERSION() external view returns (string memory);

    /*//////////////////////////////////////////////////////////////
                              PARAMETERS
    //////////////////////////////////////////////////////////////*/

    /**
     * @notice Chainlink ETH/USD price feed.
     */
    function priceFeed() external view returns (AggregatorV3Interface);

    /**
     * @notice Chainlink L2 sequencer uptime feed.
     */
    function uptimeFeed() external view returns (AggregatorV3Interface);

    /**
     * @notice Block timestamp at which this contract will no longer accept storage rent payments.
     *         Changeable by owner.
     */
    function deprecationTimestamp() external view returns (uint256);

    /**
     * @notice Price per storage unit in USD. Fixed point value with 8 decimals, e.g. 5e8 = $5 USD.
     *         Changeable by owner.
     */
    function usdUnitPrice() external view returns (uint256);

    /**
     * @notice A fixed ETH/USD price which overrides the Chainlink feed. If this value is nonzero,
     *         we disable external calls to the price feed and use this price. Changeable by owner.
     *         To be used in the event of a price feed failure.
     */
    function fixedEthUsdPrice() external view returns (uint256);

    /**
     * @notice Total capacity of storage units. Changeable by owner.
     */
    function maxUnits() external view returns (uint256);

    /**
     * @notice Duration to cache ethUsdPrice before updating from the price feed. Changeable by owner.
     */
    function priceFeedCacheDuration() external view returns (uint256);

    /**
     * @notice Max age of a price feed answer before it is considered stale. Changeable by owner.
     */
    function priceFeedMaxAge() external view returns (uint256);

    /**
     * @notice Lower bound on acceptable price feed answer. Changeable by owner.
     */
    function priceFeedMinAnswer() external view returns (uint256);

    /**
     * @notice Upper bound on acceptable price feed answer. Changeable by owner.
     */
    function priceFeedMaxAnswer() external view returns (uint256);

    /**
     * @notice Period in seconds to wait after the L2 sequencer restarts before resuming rentals.
     *         See: https://docs.chain.link/data-feeds/l2-sequencer-feeds. Changeable by owner.
     */
    function uptimeFeedGracePeriod() external view returns (uint256);

    /**
     * @notice Address to which the treasurer role can withdraw funds. Changeable by owner.
     */
    function vault() external view returns (address);

    /**
     * @notice Total number of storage units that have been rented.
     */
    function rentedUnits() external view returns (uint256);

    /**
     * @notice Cached Chainlink ETH/USD price.
     */
    function ethUsdPrice() external view returns (uint256);

    /**
     * @notice Previously cached Chainlink ETH/USD price.
     */
    function prevEthUsdPrice() external view returns (uint256);

    /**
     * @notice Timestamp of the last update to ethUsdPrice.
     */
    function lastPriceFeedUpdateTime() external view returns (uint256);

    /**
     * @notice Block number of the last update to ethUsdPrice.
     */
    function lastPriceFeedUpdateBlock() external view returns (uint256);

    /*//////////////////////////////////////////////////////////////
                        STORAGE RENTAL LOGIC
    //////////////////////////////////////////////////////////////*/

    /**
     * @notice Rent storage for a given fid for a year. The caller must provide at least
     *         price(units) wei of payment. Any excess payment will be refunded to the caller. Hubs
     *         will issue storage for 365 days + 30 day grace period after which it expires.
     *
     *         RentedUnits is never decremented on the contract even as the assigned storage expires
     *         on the hubs. This is done to keep the contract simple since we expect to launch a new
     *         storage contract within the year and deprecate this one. Even if that does not occur,
     *         the existing maxUnits parameter can be tweaked to account for expired units.
     *
     * @param fid   The fid that will receive the storage units.
     * @param units Number of storage units to rent.
     */
    function rent(uint256 fid, uint256 units) external payable returns (uint256 overpayment);

    /**
     * @notice Rent storage for multiple fids for a year. The caller must provide at least
     *         price(units) wei of payment where units is the sum of storage units requested across
     *         the fids. See comments on rent() for additional details.
     *
     * @param fids  An array of fids.
     * @param units An array of storage unit quantities. Must be the same length as the fids array.
     */
    function batchRent(uint256[] calldata fids, uint256[] calldata units) external payable;

    /*//////////////////////////////////////////////////////////////
                              PRICE VIEWS
    //////////////////////////////////////////////////////////////*/

    /**
     * @notice Calculate the cost in wei to rent one storage unit.
     *
     * @return uint256 cost in wei.
     */
    function unitPrice() external view returns (uint256);

    /**
     * @notice Calculate the cost in wei to rent the given number of storage units.
     *
     * @param units Number of storage units.
     * @return uint256 cost in wei.
     */
    function price(uint256 units) external view returns (uint256);

    /*//////////////////////////////////////////////////////////////
                         PERMISSIONED ACTIONS
    //////////////////////////////////////////////////////////////*/

    /**
     * @notice Credit a single fid with free storage units. Only callable by operator.
     *
     * @param fid   The fid that will receive the credit.
     * @param units Number of storage units to credit.
     */
    function credit(uint256 fid, uint256 units) external;

    /**
     * @notice Credit multiple fids with free storage units. Only callable by operator.
     *
     * @param fids  An array of fids.
     * @param units Number of storage units per fid.
     */
    function batchCredit(uint256[] calldata fids, uint256 units) external;

    /**
     * @notice Credit a continuous sequence of fids with free storage units. Only callable by operator.
     *
     * @param start Lowest fid in sequence (inclusive).
     * @param end   Highest fid in sequence (inclusive).
     * @param units Number of storage units per fid.
     */
    function continuousCredit(uint256 start, uint256 end, uint256 units) external;

    /**
     * @notice Force refresh the cached Chainlink ETH/USD price. Callable by owner and treasurer.
     */
    function refreshPrice() external;

    /**
     * @notice Change the price feed addresss. Callable by owner.
     *
     * @param feed The new price feed.
     */
    function setPriceFeed(AggregatorV3Interface feed) external;

    /**
     * @notice Change the uptime feed addresss. Callable by owner.
     *
     * @param feed The new uptime feed.
     */
    function setUptimeFeed(AggregatorV3Interface feed) external;

    /**
     * @notice Change the USD price per storage unit. Callable by owner.
     *
     * @param usdPrice The new unit price in USD. Fixed point value with 8 decimals.
     */
    function setPrice(uint256 usdPrice) external;

    /**
     * @notice Set the fixed ETH/USD price, disabling the price feed if the value is
     *         nonzero. This is an emergency fallback in case of a price feed failure.
     *         Only callable by owner.
     *
     * @param fixedPrice The new fixed ETH/USD price. Fixed point value with 8 decimals.
     *                   Setting this value back to zero from a nonzero value will
     *                   re-enable the price feed.
     */
    function setFixedEthUsdPrice(uint256 fixedPrice) external;

    /**
     * @notice Change the maximum supply of storage units. Only callable by owner.
     *
     * @param max The new maximum supply of storage units.
     */
    function setMaxUnits(uint256 max) external;

    /**
     * @notice Change the deprecationTimestamp. Only callable by owner.
     *
     * @param timestamp The new deprecationTimestamp. Must be at least equal to block.timestamp.
     */
    function setDeprecationTimestamp(uint256 timestamp) external;

    /**
     * @notice Change the priceFeedCacheDuration. Only callable by owner.
     *
     * @param duration The new priceFeedCacheDuration.
     */
    function setCacheDuration(uint256 duration) external;

    /**
     * @notice Change the priceFeedMaxAge. Only callable by owner.
     *
     * @param age The new priceFeedMaxAge.
     */
    function setMaxAge(uint256 age) external;

    /**
     * @notice Change the priceFeedMinAnswer. Only callable by owner.
     *
     * @param minPrice The new priceFeedMinAnswer. Must be less than current priceFeedMaxAnswer.
     */
    function setMinAnswer(uint256 minPrice) external;

    /**
     * @notice Change the priceFeedMaxAnswer. Only callable by owner.
     *
     * @param maxPrice The new priceFeedMaxAnswer. Must be greater than current priceFeedMinAnswer.
     */
    function setMaxAnswer(uint256 maxPrice) external;

    /**
     * @notice Change the uptimeFeedGracePeriod. Only callable by owner.
     *
     * @param period The new uptimeFeedGracePeriod.
     */
    function setGracePeriod(uint256 period) external;

    /**
     * @notice Change the vault address that can receive funds from this contract.
     *         Only callable by owner.
     *
     * @param vaultAddr The new vault address.
     */
    function setVault(address vaultAddr) external;

    /**
     * @notice Withdraw a specified amount of ether from the contract balance to the vault.
     *         Only callable by treasurer.
     *
     * @param amount The amount of ether to withdraw.
     */
    function withdraw(uint256 amount) external;

    /**
     * @notice Pause, disabling rentals and credits.
     *         Only callable by owner.
     */
    function pause() external;

    /**
     * @notice Unpause, enabling rentals and credits.
     *         Only callable by owner.
     */
    function unpause() external;
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.21;

interface IdRegistryLike {
    function idOf(address fidOwner) external view returns (uint256);

    function verifyFidSignature(
        address custodyAddress,
        uint256 fid,
        bytes32 digest,
        bytes calldata sig
    ) external view returns (bool isValid);
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.21;

import {IMetadataValidator} from "./IMetadataValidator.sol";
import {IdRegistryLike} from "./IdRegistryLike.sol";

interface IKeyRegistry {
    /*//////////////////////////////////////////////////////////////
                                 STRUCTS
    //////////////////////////////////////////////////////////////*/

    /**
     *  @notice State enumeration for a key in the registry. During migration, an admin can change
     *          the state of any fids key from NULL to ADDED or ADDED to NULL. After migration, an
     *          fid can change the state of a key from NULL to ADDED or ADDED to REMOVED only.
     *
     *          - NULL: The key is not in the registry.
     *          - ADDED: The key has been added to the registry.
     *          - REMOVED: The key was added to the registry but is now removed.
     */
    enum KeyState {
        NULL,
        ADDED,
        REMOVED
    }

    /**
     *  @notice Data about a key.
     *
     *  @param state   The current state of the key.
     *  @param keyType Numeric ID representing the manner in which the key should be used.
     */
    struct KeyData {
        KeyState state;
        uint32 keyType;
    }

    /**
     * @dev Struct argument for bulk add function, representing an FID
     *      and its associated keys.
     *
     * @param fid  Fid associated with provided keys to add.
     * @param keys Array of BulkAddKey structs, including key and metadata.
     */
    struct BulkAddData {
        uint256 fid;
        BulkAddKey[] keys;
    }

    /**
     * @dev Struct argument for bulk add function, representing a key
     *      and its associated metadata.
     *
     * @param key  Bytes of the signer key.
     * @param keys Metadata metadata of the signer key.
     */
    struct BulkAddKey {
        bytes key;
        bytes metadata;
    }

    /**
     * @dev Struct argument for bulk reset function, representing an FID
     *      and its associated keys.
     *
     * @param fid  Fid associated with provided keys to reset.
     * @param keys Array of keys to reset.
     */
    struct BulkResetData {
        uint256 fid;
        bytes[] keys;
    }

    /*//////////////////////////////////////////////////////////////
                              CONSTANTS
    //////////////////////////////////////////////////////////////*/

    /**
     * @notice Contract version specified in the Farcaster protocol version scheme.
     */
    function VERSION() external view returns (string memory);

    /**
     * @notice Period in seconds after migration during which admin can bulk add/reset keys.
     *         Admins can make corrections to the migrated data during the grace period if necessary,
     *         but cannot make changes after it expires.
     */
    function gracePeriod() external view returns (uint24);

    /**
     * @notice EIP-712 typehash for Add signatures.
     */
    function ADD_TYPEHASH() external view returns (bytes32);

    /**
     * @notice EIP-712 typehash for Remove signatures.
     */
    function REMOVE_TYPEHASH() external view returns (bytes32);

    /*//////////////////////////////////////////////////////////////
                                STORAGE
    //////////////////////////////////////////////////////////////*/

    /**
     * @notice The IdRegistry contract.
     */
    function idRegistry() external view returns (IdRegistryLike);

    /**
     * @notice Timestamp at which keys migrated. Hubs will cut over to use this KeyRegistry as their
     *         source of truth after this timestamp.
     */
    function keysMigratedAt() external view returns (uint40);

    /*//////////////////////////////////////////////////////////////
                                  VIEWS
    //////////////////////////////////////////////////////////////*/

    /**
     * @notice Retrieve state and type data for a given key.
     *
     * @param fid   The fid associated with the key.
     * @param key   Bytes of the key.
     *
     * @return KeyData struct that contains the state and keyType.
     */
    function keyDataOf(uint256 fid, bytes calldata key) external view returns (KeyData memory);

    /**
     * @notice Check if the contract has been migrated.
     *
     * @return true if the contract has been migrated, false otherwise.
     */
    function isMigrated() external view returns (bool);

    /*//////////////////////////////////////////////////////////////
                              REGISTRATION
    //////////////////////////////////////////////////////////////*/

    /**
     * @notice Add a key to the caller's fid, setting the key state to ADDED.
     *
     * @param keyType      The key's numeric keyType.
     * @param key          Bytes of the key to add.
     * @param metadataType Metadata type ID.
     * @param metadata     Metadata about the key, which is not stored and only emitted in an event.
     */
    function add(uint32 keyType, bytes calldata key, uint8 metadataType, bytes calldata metadata) external;

    /**
     * @notice Add a key on behalf of another fid owner, setting the key state to ADDED.
     *         caller must supply a valid EIP-712 Add signature from the fid owner.
     *
     * @param fidOwner     The fid owner address.
     * @param keyType      The key's numeric keyType.
     * @param key          Bytes of the key to add.
     * @param metadataType Metadata type ID.
     * @param metadata     Metadata about the key, which is not stored and only emitted in an event.
     * @param deadline     Deadline after which the signature expires.
     * @param sig          EIP-712 Add signature generated by fid owner.
     */
    function addFor(
        address fidOwner,
        uint32 keyType,
        bytes calldata key,
        uint8 metadataType,
        bytes calldata metadata,
        uint256 deadline,
        bytes calldata sig
    ) external;

    /**
     * @notice Remove a key associated with the caller's fid, setting the key state to REMOVED.
     *         The key must be in the ADDED state.
     *
     * @param key   Bytes of the key to remove.
     */
    function remove(bytes calldata key) external;

    /**
     * @notice Remove a key on behalf of another fid owner, setting the key state to REMOVED.
     *         caller must supply a valid EIP-712 Remove signature from the fid owner.
     *
     * @param fidOwner The fid owner address.
     * @param key      Bytes of the key to remove.
     * @param deadline Deadline after which the signature expires.
     * @param sig      EIP-712 Remove signature generated by fid owner.
     */
    function removeFor(address fidOwner, bytes calldata key, uint256 deadline, bytes calldata sig) external;

    /*//////////////////////////////////////////////////////////////
                         PERMISSIONED ACTIONS
    //////////////////////////////////////////////////////////////*/

    /**
     * @notice Add a key on behalf of another fid owner, setting the key state to ADDED.
     *         Can only be called by the trustedCaller.
     *
     * @param fidOwner     The fid owner address.
     * @param keyType      The key's numeric keyType.
     * @param key          Bytes of the key to add.
     * @param metadataType Metadata type ID.
     * @param metadata     Metadata about the key, which is not stored and only emitted in an event.
     */
    function trustedAdd(
        address fidOwner,
        uint32 keyType,
        bytes calldata key,
        uint8 metadataType,
        bytes calldata metadata
    ) external;

    /**
     * @notice Set the time of the key migration and emit an event. Hubs will watch this event and
     *         cut over to use the onchain registry as their source of truth after this timestamp.
     *         Only callable by the contract owner.
     */
    function migrateKeys() external;

    /**
     * @notice Add multiple keys as part of the initial migration. Only callable by the contract owner.
     *
     * @param items An array of BulkAddData structs including fid and array of BulkAddKey structs.
     */
    function bulkAddKeysForMigration(BulkAddData[] calldata items) external;

    /**
     * @notice Reset multiple keys as part of the initial migration. Only callable by the contract owner.
     *         Reset is not the same as removal: this function sets the key state back to NULL,
     *         rather than REMOVED. This allows the owner to correct any errors in the initial migration until
     *         the grace period expires.
     *
     * @param items    A list of BulkResetData structs including an fid and array of keys.
     */
    function bulkResetKeysForMigration(BulkResetData[] calldata items) external;

    /**
     * @notice Set a metadata validator contract for the given keyType and metadataType. Only callable by owner.
     *
     * @param keyType      The numeric key type ID associated with this validator.
     * @param metadataType The numeric metadata type ID associated with this validator.
     * @param validator    Contract implementing IMetadataValidator.
     */
    function setValidator(uint32 keyType, uint8 metadataType, IMetadataValidator validator) external;

    /**
     * @notice Set the IdRegistry contract address. Only callable by owner.
     *
     * @param _idRegistry The new IdRegistry address.
     */
    function setIdRegistry(address _idRegistry) external;

    /**
     * @notice Pause add, remove, and reset.
     *         Must be called by the owner.
     */
    function pause() external;

    /**
     * @notice Unpause add, remove, and reset.
     *         Must be called by the owner.
     */
    function unpause() external;
}

// SPDX-License-Identifier: MIT
pragma solidity 0.8.21;

interface IMetadataValidator {
    function validate(uint256 userFid, bytes memory key, bytes memory metadata) external returns (bool);
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (access/Ownable2Step.sol)

pragma solidity ^0.8.0;

import "./Ownable.sol";

/**
 * @dev Contract module which provides access control mechanism, where
 * there is an account (an owner) that can be granted exclusive access to
 * specific functions.
 *
 * By default, the owner account will be the one that deploys the contract. This
 * can later be changed with {transferOwnership} and {acceptOwnership}.
 *
 * This module is used through inheritance. It will make available all functions
 * from parent (Ownable).
 */
abstract contract Ownable2Step is Ownable {
    address private _pendingOwner;

    event OwnershipTransferStarted(address indexed previousOwner, address indexed newOwner);

    /**
     * @dev Returns the address of the pending owner.
     */
    function pendingOwner() public view virtual returns (address) {
        return _pendingOwner;
    }

    /**
     * @dev Starts the ownership transfer of the contract to a new account. Replaces the pending transfer if there is one.
     * Can only be called by the current owner.
     */
    function transferOwnership(address newOwner) public virtual override onlyOwner {
        _pendingOwner = newOwner;
        emit OwnershipTransferStarted(owner(), newOwner);
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`) and deletes any pending owner.
     * Internal function without access restriction.
     */
    function _transferOwnership(address newOwner) internal virtual override {
        delete _pendingOwner;
        super._transferOwnership(newOwner);
    }

    /**
     * @dev The new owner accepts the ownership transfer.
     */
    function acceptOwnership() public virtual {
        address sender = _msgSender();
        require(pendingOwner() == sender, "Ownable2Step: caller is not the new owner");
        _transferOwnership(sender);
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.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 // Deprecated in v4.8
    }

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

    /**
     * @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) {
        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.
            /// @solidity memory-safe-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 {
            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 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 message) {
        // 32 is the length in bytes of hash,
        // enforced by the type signature above
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x00, "\x19Ethereum Signed Message:\n32")
            mstore(0x1c, hash)
            message := keccak256(0x00, 0x3c)
        }
    }

    /**
     * @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 data) {
        /// @solidity memory-safe-assembly
        assembly {
            let ptr := mload(0x40)
            mstore(ptr, "\x19\x01")
            mstore(add(ptr, 0x02), domainSeparator)
            mstore(add(ptr, 0x22), structHash)
            data := keccak256(ptr, 0x42)
        }
    }

    /**
     * @dev Returns an Ethereum Signed Data with intended validator, created from a
     * `validator` and `data` according to the version 0 of EIP-191.
     *
     * See {recover}.
     */
    function toDataWithIntendedValidatorHash(address validator, bytes memory data) internal pure returns (bytes32) {
        return keccak256(abi.encodePacked("\x19\x00", validator, data));
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (interfaces/IERC1271.sol)

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC1271 standard signature validation method for
 * contracts as defined in https://eips.ethereum.org/EIPS/eip-1271[ERC-1271].
 *
 * _Available since v4.1._
 */
interface IERC1271 {
    /**
     * @dev Should return whether the signature provided is valid for the provided data
     * @param hash      Hash of the data to be signed
     * @param signature Signature byte array associated with _data
     */
    function isValidSignature(bytes32 hash, bytes memory signature) external view returns (bytes4 magicValue);
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)

pragma solidity ^0.8.0;

/**
 * @dev Provides information about the current execution context, including the
 * sender of the transaction and its data. While these are generally available
 * via msg.sender and msg.data, they should not be accessed in such a direct
 * manner, since when dealing with meta-transactions the account sending and
 * paying for execution may not be the actual sender (as far as an application
 * is concerned).
 *
 * This contract is only required for intermediate, library-like contracts.
 */
abstract contract Context {
    function _msgSender() internal view virtual returns (address) {
        return msg.sender;
    }

    function _msgData() internal view virtual returns (bytes calldata) {
        return msg.data;
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/cryptography/EIP712.sol)

pragma solidity ^0.8.8;

import "./ECDSA.sol";
import "../ShortStrings.sol";
import "../../interfaces/IERC5267.sol";

/**
 * @dev https://eips.ethereum.org/EIPS/eip-712[EIP 712] is a standard for hashing and signing of typed structured data.
 *
 * The encoding specified in the EIP is very generic, and such a generic implementation in Solidity is not feasible,
 * thus this contract does not implement the encoding itself. Protocols need to implement the type-specific encoding
 * they need in their contracts using a combination of `abi.encode` and `keccak256`.
 *
 * This contract implements the EIP 712 domain separator ({_domainSeparatorV4}) that is used as part of the encoding
 * scheme, and the final step of the encoding to obtain the message digest that is then signed via ECDSA
 * ({_hashTypedDataV4}).
 *
 * The implementation of the domain separator was designed to be as efficient as possible while still properly updating
 * the chain id to protect against replay attacks on an eventual fork of the chain.
 *
 * NOTE: This contract implements the version of the encoding known as "v4", as implemented by the JSON RPC method
 * https://docs.metamask.io/guide/signing-data.html[`eth_signTypedDataV4` in MetaMask].
 *
 * NOTE: In the upgradeable version of this contract, the cached values will correspond to the address, and the domain
 * separator of the implementation contract. This will cause the `_domainSeparatorV4` function to always rebuild the
 * separator from the immutable values, which is cheaper than accessing a cached version in cold storage.
 *
 * _Available since v3.4._
 *
 * @custom:oz-upgrades-unsafe-allow state-variable-immutable state-variable-assignment
 */
abstract contract EIP712 is IERC5267 {
    using ShortStrings for *;

    bytes32 private constant _TYPE_HASH =
        keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)");

    // Cache the domain separator as an immutable value, but also store the chain id that it corresponds to, in order to
    // invalidate the cached domain separator if the chain id changes.
    bytes32 private immutable _cachedDomainSeparator;
    uint256 private immutable _cachedChainId;
    address private immutable _cachedThis;

    bytes32 private immutable _hashedName;
    bytes32 private immutable _hashedVersion;

    ShortString private immutable _name;
    ShortString private immutable _version;
    string private _nameFallback;
    string private _versionFallback;

    /**
     * @dev Initializes the domain separator and parameter caches.
     *
     * The meaning of `name` and `version` is specified in
     * https://eips.ethereum.org/EIPS/eip-712#definition-of-domainseparator[EIP 712]:
     *
     * - `name`: the user readable name of the signing domain, i.e. the name of the DApp or the protocol.
     * - `version`: the current major version of the signing domain.
     *
     * NOTE: These parameters cannot be changed except through a xref:learn::upgrading-smart-contracts.adoc[smart
     * contract upgrade].
     */
    constructor(string memory name, string memory version) {
        _name = name.toShortStringWithFallback(_nameFallback);
        _version = version.toShortStringWithFallback(_versionFallback);
        _hashedName = keccak256(bytes(name));
        _hashedVersion = keccak256(bytes(version));

        _cachedChainId = block.chainid;
        _cachedDomainSeparator = _buildDomainSeparator();
        _cachedThis = address(this);
    }

    /**
     * @dev Returns the domain separator for the current chain.
     */
    function _domainSeparatorV4() internal view returns (bytes32) {
        if (address(this) == _cachedThis && block.chainid == _cachedChainId) {
            return _cachedDomainSeparator;
        } else {
            return _buildDomainSeparator();
        }
    }

    function _buildDomainSeparator() private view returns (bytes32) {
        return keccak256(abi.encode(_TYPE_HASH, _hashedName, _hashedVersion, block.chainid, address(this)));
    }

    /**
     * @dev Given an already https://eips.ethereum.org/EIPS/eip-712#definition-of-hashstruct[hashed struct], this
     * function returns the hash of the fully encoded EIP712 message for this domain.
     *
     * This hash can be used together with {ECDSA-recover} to obtain the signer of a message. For example:
     *
     * ```solidity
     * bytes32 digest = _hashTypedDataV4(keccak256(abi.encode(
     *     keccak256("Mail(address to,string contents)"),
     *     mailTo,
     *     keccak256(bytes(mailContents))
     * )));
     * address signer = ECDSA.recover(digest, signature);
     * ```
     */
    function _hashTypedDataV4(bytes32 structHash) internal view virtual returns (bytes32) {
        return ECDSA.toTypedDataHash(_domainSeparatorV4(), structHash);
    }

    /**
     * @dev See {EIP-5267}.
     *
     * _Available since v4.9._
     */
    function eip712Domain()
        public
        view
        virtual
        override
        returns (
            bytes1 fields,
            string memory name,
            string memory version,
            uint256 chainId,
            address verifyingContract,
            bytes32 salt,
            uint256[] memory extensions
        )
    {
        return (
            hex"0f", // 01111
            _name.toStringWithFallback(_nameFallback),
            _version.toStringWithFallback(_versionFallback),
            block.chainid,
            address(this),
            bytes32(0),
            new uint256[](0)
        );
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (access/IAccessControlEnumerable.sol)

pragma solidity ^0.8.0;

import "./IAccessControl.sol";

/**
 * @dev External interface of AccessControlEnumerable declared to support ERC165 detection.
 */
interface IAccessControlEnumerable is IAccessControl {
    /**
     * @dev Returns one of the accounts that have `role`. `index` must be a
     * value between 0 and {getRoleMemberCount}, non-inclusive.
     *
     * Role bearers are not sorted in any particular way, and their ordering may
     * change at any point.
     *
     * WARNING: When using {getRoleMember} and {getRoleMemberCount}, make sure
     * you perform all queries on the same block. See the following
     * https://forum.openzeppelin.com/t/iterating-over-elements-on-enumerableset-in-openzeppelin-contracts/2296[forum post]
     * for more information.
     */
    function getRoleMember(bytes32 role, uint256 index) external view returns (address);

    /**
     * @dev Returns the number of accounts that have `role`. Can be used
     * together with {getRoleMember} to enumerate all bearers of a role.
     */
    function getRoleMemberCount(bytes32 role) external view returns (uint256);
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (access/AccessControl.sol)

pragma solidity ^0.8.0;

import "./IAccessControl.sol";
import "../utils/Context.sol";
import "../utils/Strings.sol";
import "../utils/introspection/ERC165.sol";

/**
 * @dev Contract module that allows children to implement role-based access
 * control mechanisms. This is a lightweight version that doesn't allow enumerating role
 * members except through off-chain means by accessing the contract event logs. Some
 * applications may benefit from on-chain enumerability, for those cases see
 * {AccessControlEnumerable}.
 *
 * Roles are referred to by their `bytes32` identifier. These should be exposed
 * in the external API and be unique. The best way to achieve this is by
 * using `public constant` hash digests:
 *
 * ```solidity
 * bytes32 public constant MY_ROLE = keccak256("MY_ROLE");
 * ```
 *
 * Roles can be used to represent a set of permissions. To restrict access to a
 * function call, use {hasRole}:
 *
 * ```solidity
 * function foo() public {
 *     require(hasRole(MY_ROLE, msg.sender));
 *     ...
 * }
 * ```
 *
 * Roles can be granted and revoked dynamically via the {grantRole} and
 * {revokeRole} functions. Each role has an associated admin role, and only
 * accounts that have a role's admin role can call {grantRole} and {revokeRole}.
 *
 * By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means
 * that only accounts with this role will be able to grant or revoke other
 * roles. More complex role relationships can be created by using
 * {_setRoleAdmin}.
 *
 * WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to
 * grant and revoke this role. Extra precautions should be taken to secure
 * accounts that have been granted it. We recommend using {AccessControlDefaultAdminRules}
 * to enforce additional security measures for this role.
 */
abstract contract AccessControl is Context, IAccessControl, ERC165 {
    struct RoleData {
        mapping(address => bool) members;
        bytes32 adminRole;
    }

    mapping(bytes32 => RoleData) private _roles;

    bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;

    /**
     * @dev Modifier that checks that an account has a specific role. Reverts
     * with a standardized message including the required role.
     *
     * The format of the revert reason is given by the following regular expression:
     *
     *  /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/
     *
     * _Available since v4.1._
     */
    modifier onlyRole(bytes32 role) {
        _checkRole(role);
        _;
    }

    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
        return interfaceId == type(IAccessControl).interfaceId || super.supportsInterface(interfaceId);
    }

    /**
     * @dev Returns `true` if `account` has been granted `role`.
     */
    function hasRole(bytes32 role, address account) public view virtual override returns (bool) {
        return _roles[role].members[account];
    }

    /**
     * @dev Revert with a standard message if `_msgSender()` is missing `role`.
     * Overriding this function changes the behavior of the {onlyRole} modifier.
     *
     * Format of the revert message is described in {_checkRole}.
     *
     * _Available since v4.6._
     */
    function _checkRole(bytes32 role) internal view virtual {
        _checkRole(role, _msgSender());
    }

    /**
     * @dev Revert with a standard message if `account` is missing `role`.
     *
     * The format of the revert reason is given by the following regular expression:
     *
     *  /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/
     */
    function _checkRole(bytes32 role, address account) internal view virtual {
        if (!hasRole(role, account)) {
            revert(
                string(
                    abi.encodePacked(
                        "AccessControl: account ",
                        Strings.toHexString(account),
                        " is missing role ",
                        Strings.toHexString(uint256(role), 32)
                    )
                )
            );
        }
    }

    /**
     * @dev Returns the admin role that controls `role`. See {grantRole} and
     * {revokeRole}.
     *
     * To change a role's admin, use {_setRoleAdmin}.
     */
    function getRoleAdmin(bytes32 role) public view virtual override returns (bytes32) {
        return _roles[role].adminRole;
    }

    /**
     * @dev Grants `role` to `account`.
     *
     * If `account` had not been already granted `role`, emits a {RoleGranted}
     * event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     *
     * May emit a {RoleGranted} event.
     */
    function grantRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) {
        _grantRole(role, account);
    }

    /**
     * @dev Revokes `role` from `account`.
     *
     * If `account` had been granted `role`, emits a {RoleRevoked} event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     *
     * May emit a {RoleRevoked} event.
     */
    function revokeRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) {
        _revokeRole(role, account);
    }

    /**
     * @dev Revokes `role` from the calling account.
     *
     * Roles are often managed via {grantRole} and {revokeRole}: this function's
     * purpose is to provide a mechanism for accounts to lose their privileges
     * if they are compromised (such as when a trusted device is misplaced).
     *
     * If the calling account had been revoked `role`, emits a {RoleRevoked}
     * event.
     *
     * Requirements:
     *
     * - the caller must be `account`.
     *
     * May emit a {RoleRevoked} event.
     */
    function renounceRole(bytes32 role, address account) public virtual override {
        require(account == _msgSender(), "AccessControl: can only renounce roles for self");

        _revokeRole(role, account);
    }

    /**
     * @dev Grants `role` to `account`.
     *
     * If `account` had not been already granted `role`, emits a {RoleGranted}
     * event. Note that unlike {grantRole}, this function doesn't perform any
     * checks on the calling account.
     *
     * May emit a {RoleGranted} event.
     *
     * [WARNING]
     * ====
     * This function should only be called from the constructor when setting
     * up the initial roles for the system.
     *
     * Using this function in any other way is effectively circumventing the admin
     * system imposed by {AccessControl}.
     * ====
     *
     * NOTE: This function is deprecated in favor of {_grantRole}.
     */
    function _setupRole(bytes32 role, address account) internal virtual {
        _grantRole(role, account);
    }

    /**
     * @dev Sets `adminRole` as ``role``'s admin role.
     *
     * Emits a {RoleAdminChanged} event.
     */
    function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual {
        bytes32 previousAdminRole = getRoleAdmin(role);
        _roles[role].adminRole = adminRole;
        emit RoleAdminChanged(role, previousAdminRole, adminRole);
    }

    /**
     * @dev Grants `role` to `account`.
     *
     * Internal function without access restriction.
     *
     * May emit a {RoleGranted} event.
     */
    function _grantRole(bytes32 role, address account) internal virtual {
        if (!hasRole(role, account)) {
            _roles[role].members[account] = true;
            emit RoleGranted(role, account, _msgSender());
        }
    }

    /**
     * @dev Revokes `role` from `account`.
     *
     * Internal function without access restriction.
     *
     * May emit a {RoleRevoked} event.
     */
    function _revokeRole(bytes32 role, address account) internal virtual {
        if (hasRole(role, account)) {
            _roles[role].members[account] = false;
            emit RoleRevoked(role, account, _msgSender());
        }
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/structs/EnumerableSet.sol)
// This file was procedurally generated from scripts/generate/templates/EnumerableSet.js.

pragma solidity ^0.8.0;

/**
 * @dev Library for managing
 * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive
 * types.
 *
 * Sets have the following properties:
 *
 * - Elements are added, removed, and checked for existence in constant time
 * (O(1)).
 * - Elements are enumerated in O(n). No guarantees are made on the ordering.
 *
 * ```solidity
 * contract Example {
 *     // Add the library methods
 *     using EnumerableSet for EnumerableSet.AddressSet;
 *
 *     // Declare a set state variable
 *     EnumerableSet.AddressSet private mySet;
 * }
 * ```
 *
 * As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`)
 * and `uint256` (`UintSet`) are supported.
 *
 * [WARNING]
 * ====
 * Trying to delete such a structure from storage will likely result in data corruption, rendering the structure
 * unusable.
 * See https://github.com/ethereum/solidity/pull/11843[ethereum/solidity#11843] for more info.
 *
 * In order to clean an EnumerableSet, you can either remove all elements one by one or create a fresh instance using an
 * array of EnumerableSet.
 * ====
 */
library EnumerableSet {
    // To implement this library for multiple types with as little code
    // repetition as possible, we write it in terms of a generic Set type with
    // bytes32 values.
    // The Set implementation uses private functions, and user-facing
    // implementations (such as AddressSet) are just wrappers around the
    // underlying Set.
    // This means that we can only create new EnumerableSets for types that fit
    // in bytes32.

    struct Set {
        // Storage of set values
        bytes32[] _values;
        // Position of the value in the `values` array, plus 1 because index 0
        // means a value is not in the set.
        mapping(bytes32 => uint256) _indexes;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function _add(Set storage set, bytes32 value) private returns (bool) {
        if (!_contains(set, value)) {
            set._values.push(value);
            // The value is stored at length-1, but we add 1 to all indexes
            // and use 0 as a sentinel value
            set._indexes[value] = set._values.length;
            return true;
        } else {
            return false;
        }
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function _remove(Set storage set, bytes32 value) private returns (bool) {
        // We read and store the value's index to prevent multiple reads from the same storage slot
        uint256 valueIndex = set._indexes[value];

        if (valueIndex != 0) {
            // Equivalent to contains(set, value)
            // To delete an element from the _values array in O(1), we swap the element to delete with the last one in
            // the array, and then remove the last element (sometimes called as 'swap and pop').
            // This modifies the order of the array, as noted in {at}.

            uint256 toDeleteIndex = valueIndex - 1;
            uint256 lastIndex = set._values.length - 1;

            if (lastIndex != toDeleteIndex) {
                bytes32 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[value];

            return true;
        } else {
            return false;
        }
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function _contains(Set storage set, bytes32 value) private view returns (bool) {
        return set._indexes[value] != 0;
    }

    /**
     * @dev Returns the number of values on the set. O(1).
     */
    function _length(Set storage set) private view returns (uint256) {
        return set._values.length;
    }

    /**
     * @dev Returns the value stored at position `index` in the set. O(1).
     *
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function _at(Set storage set, uint256 index) private view returns (bytes32) {
        return set._values[index];
    }

    /**
     * @dev Return the entire set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function _values(Set storage set) private view returns (bytes32[] memory) {
        return set._values;
    }

    // Bytes32Set

    struct Bytes32Set {
        Set _inner;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {
        return _add(set._inner, value);
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) {
        return _remove(set._inner, value);
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {
        return _contains(set._inner, value);
    }

    /**
     * @dev Returns the number of values in the set. O(1).
     */
    function length(Bytes32Set storage set) internal view returns (uint256) {
        return _length(set._inner);
    }

    /**
     * @dev Returns the value stored at position `index` in the set. O(1).
     *
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {
        return _at(set._inner, index);
    }

    /**
     * @dev Return the entire set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function values(Bytes32Set storage set) internal view returns (bytes32[] memory) {
        bytes32[] memory store = _values(set._inner);
        bytes32[] memory result;

        /// @solidity memory-safe-assembly
        assembly {
            result := store
        }

        return result;
    }

    // AddressSet

    struct AddressSet {
        Set _inner;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function add(AddressSet storage set, address value) internal returns (bool) {
        return _add(set._inner, bytes32(uint256(uint160(value))));
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function remove(AddressSet storage set, address value) internal returns (bool) {
        return _remove(set._inner, bytes32(uint256(uint160(value))));
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function contains(AddressSet storage set, address value) internal view returns (bool) {
        return _contains(set._inner, bytes32(uint256(uint160(value))));
    }

    /**
     * @dev Returns the number of values in the set. O(1).
     */
    function length(AddressSet storage set) internal view returns (uint256) {
        return _length(set._inner);
    }

    /**
     * @dev Returns the value stored at position `index` in the set. O(1).
     *
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function at(AddressSet storage set, uint256 index) internal view returns (address) {
        return address(uint160(uint256(_at(set._inner, index))));
    }

    /**
     * @dev Return the entire set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function values(AddressSet storage set) internal view returns (address[] memory) {
        bytes32[] memory store = _values(set._inner);
        address[] memory result;

        /// @solidity memory-safe-assembly
        assembly {
            result := store
        }

        return result;
    }

    // UintSet

    struct UintSet {
        Set _inner;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function add(UintSet storage set, uint256 value) internal returns (bool) {
        return _add(set._inner, bytes32(value));
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function remove(UintSet storage set, uint256 value) internal returns (bool) {
        return _remove(set._inner, bytes32(value));
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function contains(UintSet storage set, uint256 value) internal view returns (bool) {
        return _contains(set._inner, bytes32(value));
    }

    /**
     * @dev Returns the number of values in the set. O(1).
     */
    function length(UintSet storage set) internal view returns (uint256) {
        return _length(set._inner);
    }

    /**
     * @dev Returns the value stored at position `index` in the set. O(1).
     *
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function at(UintSet storage set, uint256 index) internal view returns (uint256) {
        return uint256(_at(set._inner, index));
    }

    /**
     * @dev Return the entire set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function values(UintSet storage set) internal view returns (uint256[] memory) {
        bytes32[] memory store = _values(set._inner);
        uint256[] memory result;

        /// @solidity memory-safe-assembly
        assembly {
            result := store
        }

        return result;
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (access/Ownable.sol)

pragma solidity ^0.8.0;

import "../utils/Context.sol";

/**
 * @dev Contract module which provides a basic access control mechanism, where
 * there is an account (an owner) that can be granted exclusive access to
 * specific functions.
 *
 * By default, the owner account will be the one that deploys the contract. This
 * can later be changed with {transferOwnership}.
 *
 * This module is used through inheritance. It will make available the modifier
 * `onlyOwner`, which can be applied to your functions to restrict their use to
 * the owner.
 */
abstract contract Ownable is Context {
    address private _owner;

    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);

    /**
     * @dev Initializes the contract setting the deployer as the initial owner.
     */
    constructor() {
        _transferOwnership(_msgSender());
    }

    /**
     * @dev Throws if called by any account other than the owner.
     */
    modifier onlyOwner() {
        _checkOwner();
        _;
    }

    /**
     * @dev Returns the address of the current owner.
     */
    function owner() public view virtual returns (address) {
        return _owner;
    }

    /**
     * @dev Throws if the sender is not the owner.
     */
    function _checkOwner() internal view virtual {
        require(owner() == _msgSender(), "Ownable: caller is not the owner");
    }

    /**
     * @dev Leaves the contract without owner. It will not be possible to call
     * `onlyOwner` functions. Can only be called by the current owner.
     *
     * NOTE: Renouncing ownership will leave the contract without an owner,
     * thereby disabling any functionality that is only available to the owner.
     */
    function renounceOwnership() public virtual onlyOwner {
        _transferOwnership(address(0));
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Can only be called by the current owner.
     */
    function transferOwnership(address newOwner) public virtual onlyOwner {
        require(newOwner != address(0), "Ownable: new owner is the zero address");
        _transferOwnership(newOwner);
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Internal function without access restriction.
     */
    function _transferOwnership(address newOwner) internal virtual {
        address oldOwner = _owner;
        _owner = newOwner;
        emit OwnershipTransferred(oldOwner, newOwner);
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/Strings.sol)

pragma solidity ^0.8.0;

import "./math/Math.sol";
import "./math/SignedMath.sol";

/**
 * @dev String operations.
 */
library Strings {
    bytes16 private constant _SYMBOLS = "0123456789abcdef";
    uint8 private constant _ADDRESS_LENGTH = 20;

    /**
     * @dev Converts a `uint256` to its ASCII `string` decimal representation.
     */
    function toString(uint256 value) internal pure returns (string memory) {
        unchecked {
            uint256 length = Math.log10(value) + 1;
            string memory buffer = new string(length);
            uint256 ptr;
            /// @solidity memory-safe-assembly
            assembly {
                ptr := add(buffer, add(32, length))
            }
            while (true) {
                ptr--;
                /// @solidity memory-safe-assembly
                assembly {
                    mstore8(ptr, byte(mod(value, 10), _SYMBOLS))
                }
                value /= 10;
                if (value == 0) break;
            }
            return buffer;
        }
    }

    /**
     * @dev Converts a `int256` to its ASCII `string` decimal representation.
     */
    function toString(int256 value) internal pure returns (string memory) {
        return string(abi.encodePacked(value < 0 ? "-" : "", toString(SignedMath.abs(value))));
    }

    /**
     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
     */
    function toHexString(uint256 value) internal pure returns (string memory) {
        unchecked {
            return toHexString(value, Math.log256(value) + 1);
        }
    }

    /**
     * @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] = _SYMBOLS[value & 0xf];
            value >>= 4;
        }
        require(value == 0, "Strings: hex length insufficient");
        return string(buffer);
    }

    /**
     * @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation.
     */
    function toHexString(address addr) internal pure returns (string memory) {
        return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH);
    }

    /**
     * @dev Returns true if the two strings are equal.
     */
    function equal(string memory a, string memory b) internal pure returns (bool) {
        return keccak256(bytes(a)) == keccak256(bytes(b));
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/ShortStrings.sol)

pragma solidity ^0.8.8;

import "./StorageSlot.sol";

// | string  | 0xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA   |
// | length  | 0x                                                              BB |
type ShortString is bytes32;

/**
 * @dev This library provides functions to convert short memory strings
 * into a `ShortString` type that can be used as an immutable variable.
 *
 * Strings of arbitrary length can be optimized using this library if
 * they are short enough (up to 31 bytes) by packing them with their
 * length (1 byte) in a single EVM word (32 bytes). Additionally, a
 * fallback mechanism can be used for every other case.
 *
 * Usage example:
 *
 * ```solidity
 * contract Named {
 *     using ShortStrings for *;
 *
 *     ShortString private immutable _name;
 *     string private _nameFallback;
 *
 *     constructor(string memory contractName) {
 *         _name = contractName.toShortStringWithFallback(_nameFallback);
 *     }
 *
 *     function name() external view returns (string memory) {
 *         return _name.toStringWithFallback(_nameFallback);
 *     }
 * }
 * ```
 */
library ShortStrings {
    // Used as an identifier for strings longer than 31 bytes.
    bytes32 private constant _FALLBACK_SENTINEL = 0x00000000000000000000000000000000000000000000000000000000000000FF;

    error StringTooLong(string str);
    error InvalidShortString();

    /**
     * @dev Encode a string of at most 31 chars into a `ShortString`.
     *
     * This will trigger a `StringTooLong` error is the input string is too long.
     */
    function toShortString(string memory str) internal pure returns (ShortString) {
        bytes memory bstr = bytes(str);
        if (bstr.length > 31) {
            revert StringTooLong(str);
        }
        return ShortString.wrap(bytes32(uint256(bytes32(bstr)) | bstr.length));
    }

    /**
     * @dev Decode a `ShortString` back to a "normal" string.
     */
    function toString(ShortString sstr) internal pure returns (string memory) {
        uint256 len = byteLength(sstr);
        // using `new string(len)` would work locally but is not memory safe.
        string memory str = new string(32);
        /// @solidity memory-safe-assembly
        assembly {
            mstore(str, len)
            mstore(add(str, 0x20), sstr)
        }
        return str;
    }

    /**
     * @dev Return the length of a `ShortString`.
     */
    function byteLength(ShortString sstr) internal pure returns (uint256) {
        uint256 result = uint256(ShortString.unwrap(sstr)) & 0xFF;
        if (result > 31) {
            revert InvalidShortString();
        }
        return result;
    }

    /**
     * @dev Encode a string into a `ShortString`, or write it to storage if it is too long.
     */
    function toShortStringWithFallback(string memory value, string storage store) internal returns (ShortString) {
        if (bytes(value).length < 32) {
            return toShortString(value);
        } else {
            StorageSlot.getStringSlot(store).value = value;
            return ShortString.wrap(_FALLBACK_SENTINEL);
        }
    }

    /**
     * @dev Decode a string that was encoded to `ShortString` or written to storage using {setWithFallback}.
     */
    function toStringWithFallback(ShortString value, string storage store) internal pure returns (string memory) {
        if (ShortString.unwrap(value) != _FALLBACK_SENTINEL) {
            return toString(value);
        } else {
            return store;
        }
    }

    /**
     * @dev Return the length of a string that was encoded to `ShortString` or written to storage using {setWithFallback}.
     *
     * WARNING: This will return the "byte length" of the string. This may not reflect the actual length in terms of
     * actual characters as the UTF-8 encoding of a single character can span over multiple bytes.
     */
    function byteLengthWithFallback(ShortString value, string storage store) internal view returns (uint256) {
        if (ShortString.unwrap(value) != _FALLBACK_SENTINEL) {
            return byteLength(value);
        } else {
            return bytes(store).length;
        }
    }
}

File 32 of 38 : IERC5267.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (interfaces/IERC5267.sol)

pragma solidity ^0.8.0;

interface IERC5267 {
    /**
     * @dev MAY be emitted to signal that the domain could have changed.
     */
    event EIP712DomainChanged();

    /**
     * @dev returns the fields and values that describe the domain separator used by this contract for EIP-712
     * signature.
     */
    function eip712Domain()
        external
        view
        returns (
            bytes1 fields,
            string memory name,
            string memory version,
            uint256 chainId,
            address verifyingContract,
            bytes32 salt,
            uint256[] memory extensions
        );
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (access/IAccessControl.sol)

pragma solidity ^0.8.0;

/**
 * @dev External interface of AccessControl declared to support ERC165 detection.
 */
interface IAccessControl {
    /**
     * @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole`
     *
     * `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite
     * {RoleAdminChanged} not being emitted signaling this.
     *
     * _Available since v3.1._
     */
    event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole);

    /**
     * @dev Emitted when `account` is granted `role`.
     *
     * `sender` is the account that originated the contract call, an admin role
     * bearer except when using {AccessControl-_setupRole}.
     */
    event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);

    /**
     * @dev Emitted when `account` is revoked `role`.
     *
     * `sender` is the account that originated the contract call:
     *   - if using `revokeRole`, it is the admin role bearer
     *   - if using `renounceRole`, it is the role bearer (i.e. `account`)
     */
    event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);

    /**
     * @dev Returns `true` if `account` has been granted `role`.
     */
    function hasRole(bytes32 role, address account) external view returns (bool);

    /**
     * @dev Returns the admin role that controls `role`. See {grantRole} and
     * {revokeRole}.
     *
     * To change a role's admin, use {AccessControl-_setRoleAdmin}.
     */
    function getRoleAdmin(bytes32 role) external view returns (bytes32);

    /**
     * @dev Grants `role` to `account`.
     *
     * If `account` had not been already granted `role`, emits a {RoleGranted}
     * event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     */
    function grantRole(bytes32 role, address account) external;

    /**
     * @dev Revokes `role` from `account`.
     *
     * If `account` had been granted `role`, emits a {RoleRevoked} event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     */
    function revokeRole(bytes32 role, address account) external;

    /**
     * @dev Revokes `role` from the calling account.
     *
     * Roles are often managed via {grantRole} and {revokeRole}: this function's
     * purpose is to provide a mechanism for accounts to lose their privileges
     * if they are compromised (such as when a trusted device is misplaced).
     *
     * If the calling account had been granted `role`, emits a {RoleRevoked}
     * event.
     *
     * Requirements:
     *
     * - the caller must be `account`.
     */
    function renounceRole(bytes32 role, address account) external;
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol)

pragma solidity ^0.8.0;

import "./IERC165.sol";

/**
 * @dev Implementation of the {IERC165} interface.
 *
 * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check
 * for the additional interface id that will be supported. For example:
 *
 * ```solidity
 * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
 *     return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
 * }
 * ```
 *
 * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation.
 */
abstract contract ERC165 is IERC165 {
    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
        return interfaceId == type(IERC165).interfaceId;
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/math/Math.sol)

pragma solidity ^0.8.0;

/**
 * @dev Standard math utilities missing in the Solidity language.
 */
library Math {
    enum Rounding {
        Down, // Toward negative infinity
        Up, // Toward infinity
        Zero // Toward zero
    }

    /**
     * @dev Returns the largest of two numbers.
     */
    function max(uint256 a, uint256 b) internal pure returns (uint256) {
        return a > b ? a : b;
    }

    /**
     * @dev Returns the smallest of two numbers.
     */
    function min(uint256 a, uint256 b) internal pure returns (uint256) {
        return a < b ? a : b;
    }

    /**
     * @dev Returns the average of two numbers. The result is rounded towards
     * zero.
     */
    function average(uint256 a, uint256 b) internal pure returns (uint256) {
        // (a + b) / 2 can overflow.
        return (a & b) + (a ^ b) / 2;
    }

    /**
     * @dev Returns the ceiling of the division of two numbers.
     *
     * This differs from standard division with `/` in that it rounds up instead
     * of rounding down.
     */
    function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
        // (a + b - 1) / b can overflow on addition, so we distribute.
        return a == 0 ? 0 : (a - 1) / b + 1;
    }

    /**
     * @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or denominator == 0
     * @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv)
     * with further edits by Uniswap Labs also under MIT license.
     */
    function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) {
        unchecked {
            // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
            // use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
            // variables such that product = prod1 * 2^256 + prod0.
            uint256 prod0; // Least significant 256 bits of the product
            uint256 prod1; // Most significant 256 bits of the product
            assembly {
                let mm := mulmod(x, y, not(0))
                prod0 := mul(x, y)
                prod1 := sub(sub(mm, prod0), lt(mm, prod0))
            }

            // Handle non-overflow cases, 256 by 256 division.
            if (prod1 == 0) {
                // Solidity will revert if denominator == 0, unlike the div opcode on its own.
                // The surrounding unchecked block does not change this fact.
                // See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic.
                return prod0 / denominator;
            }

            // Make sure the result is less than 2^256. Also prevents denominator == 0.
            require(denominator > prod1, "Math: mulDiv overflow");

            ///////////////////////////////////////////////
            // 512 by 256 division.
            ///////////////////////////////////////////////

            // Make division exact by subtracting the remainder from [prod1 prod0].
            uint256 remainder;
            assembly {
                // Compute remainder using mulmod.
                remainder := mulmod(x, y, denominator)

                // Subtract 256 bit number from 512 bit number.
                prod1 := sub(prod1, gt(remainder, prod0))
                prod0 := sub(prod0, remainder)
            }

            // Factor powers of two out of denominator and compute largest power of two divisor of denominator. Always >= 1.
            // See https://cs.stackexchange.com/q/138556/92363.

            // Does not overflow because the denominator cannot be zero at this stage in the function.
            uint256 twos = denominator & (~denominator + 1);
            assembly {
                // Divide denominator by twos.
                denominator := div(denominator, twos)

                // Divide [prod1 prod0] by twos.
                prod0 := div(prod0, twos)

                // Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.
                twos := add(div(sub(0, twos), twos), 1)
            }

            // Shift in bits from prod1 into prod0.
            prod0 |= prod1 * twos;

            // Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such
            // that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for
            // four bits. That is, denominator * inv = 1 mod 2^4.
            uint256 inverse = (3 * denominator) ^ 2;

            // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also works
            // in modular arithmetic, doubling the correct bits in each step.
            inverse *= 2 - denominator * inverse; // inverse mod 2^8
            inverse *= 2 - denominator * inverse; // inverse mod 2^16
            inverse *= 2 - denominator * inverse; // inverse mod 2^32
            inverse *= 2 - denominator * inverse; // inverse mod 2^64
            inverse *= 2 - denominator * inverse; // inverse mod 2^128
            inverse *= 2 - denominator * inverse; // inverse mod 2^256

            // Because the division is now exact we can divide by multiplying with the modular inverse of denominator.
            // This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is
            // less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1
            // is no longer required.
            result = prod0 * inverse;
            return result;
        }
    }

    /**
     * @notice Calculates x * y / denominator with full precision, following the selected rounding direction.
     */
    function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) {
        uint256 result = mulDiv(x, y, denominator);
        if (rounding == Rounding.Up && mulmod(x, y, denominator) > 0) {
            result += 1;
        }
        return result;
    }

    /**
     * @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded down.
     *
     * Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11).
     */
    function sqrt(uint256 a) internal pure returns (uint256) {
        if (a == 0) {
            return 0;
        }

        // For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.
        //
        // We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have
        // `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.
        //
        // This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`
        // → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`
        // → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`
        //
        // Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.
        uint256 result = 1 << (log2(a) >> 1);

        // At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,
        // since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at
        // every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision
        // into the expected uint128 result.
        unchecked {
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            return min(result, a / result);
        }
    }

    /**
     * @notice Calculates sqrt(a), following the selected rounding direction.
     */
    function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = sqrt(a);
            return result + (rounding == Rounding.Up && result * result < a ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 2, rounded down, of a positive value.
     * Returns 0 if given 0.
     */
    function log2(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >> 128 > 0) {
                value >>= 128;
                result += 128;
            }
            if (value >> 64 > 0) {
                value >>= 64;
                result += 64;
            }
            if (value >> 32 > 0) {
                value >>= 32;
                result += 32;
            }
            if (value >> 16 > 0) {
                value >>= 16;
                result += 16;
            }
            if (value >> 8 > 0) {
                value >>= 8;
                result += 8;
            }
            if (value >> 4 > 0) {
                value >>= 4;
                result += 4;
            }
            if (value >> 2 > 0) {
                value >>= 2;
                result += 2;
            }
            if (value >> 1 > 0) {
                result += 1;
            }
        }
        return result;
    }

    /**
     * @dev Return the log in base 2, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log2(value);
            return result + (rounding == Rounding.Up && 1 << result < value ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 10, rounded down, of a positive value.
     * Returns 0 if given 0.
     */
    function log10(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >= 10 ** 64) {
                value /= 10 ** 64;
                result += 64;
            }
            if (value >= 10 ** 32) {
                value /= 10 ** 32;
                result += 32;
            }
            if (value >= 10 ** 16) {
                value /= 10 ** 16;
                result += 16;
            }
            if (value >= 10 ** 8) {
                value /= 10 ** 8;
                result += 8;
            }
            if (value >= 10 ** 4) {
                value /= 10 ** 4;
                result += 4;
            }
            if (value >= 10 ** 2) {
                value /= 10 ** 2;
                result += 2;
            }
            if (value >= 10 ** 1) {
                result += 1;
            }
        }
        return result;
    }

    /**
     * @dev Return the log in base 10, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log10(value);
            return result + (rounding == Rounding.Up && 10 ** result < value ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 256, rounded down, of a positive value.
     * Returns 0 if given 0.
     *
     * Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
     */
    function log256(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >> 128 > 0) {
                value >>= 128;
                result += 16;
            }
            if (value >> 64 > 0) {
                value >>= 64;
                result += 8;
            }
            if (value >> 32 > 0) {
                value >>= 32;
                result += 4;
            }
            if (value >> 16 > 0) {
                value >>= 16;
                result += 2;
            }
            if (value >> 8 > 0) {
                result += 1;
            }
        }
        return result;
    }

    /**
     * @dev Return the log in base 256, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log256(value);
            return result + (rounding == Rounding.Up && 1 << (result << 3) < value ? 1 : 0);
        }
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (utils/math/SignedMath.sol)

pragma solidity ^0.8.0;

/**
 * @dev Standard signed math utilities missing in the Solidity language.
 */
library SignedMath {
    /**
     * @dev Returns the largest of two signed numbers.
     */
    function max(int256 a, int256 b) internal pure returns (int256) {
        return a > b ? a : b;
    }

    /**
     * @dev Returns the smallest of two signed numbers.
     */
    function min(int256 a, int256 b) internal pure returns (int256) {
        return a < b ? a : b;
    }

    /**
     * @dev Returns the average of two signed numbers without overflow.
     * The result is rounded towards zero.
     */
    function average(int256 a, int256 b) internal pure returns (int256) {
        // Formula from the book "Hacker's Delight"
        int256 x = (a & b) + ((a ^ b) >> 1);
        return x + (int256(uint256(x) >> 255) & (a ^ b));
    }

    /**
     * @dev Returns the absolute unsigned value of a signed value.
     */
    function abs(int256 n) internal pure returns (uint256) {
        unchecked {
            // must be unchecked in order to support `n = type(int256).min`
            return uint256(n >= 0 ? n : -n);
        }
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/StorageSlot.sol)
// This file was procedurally generated from scripts/generate/templates/StorageSlot.js.

pragma solidity ^0.8.0;

/**
 * @dev Library for reading and writing primitive types to specific storage slots.
 *
 * Storage slots are often used to avoid storage conflict when dealing with upgradeable contracts.
 * This library helps with reading and writing to such slots without the need for inline assembly.
 *
 * The functions in this library return Slot structs that contain a `value` member that can be used to read or write.
 *
 * Example usage to set ERC1967 implementation slot:
 * ```solidity
 * contract ERC1967 {
 *     bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
 *
 *     function _getImplementation() internal view returns (address) {
 *         return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;
 *     }
 *
 *     function _setImplementation(address newImplementation) internal {
 *         require(Address.isContract(newImplementation), "ERC1967: new implementation is not a contract");
 *         StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
 *     }
 * }
 * ```
 *
 * _Available since v4.1 for `address`, `bool`, `bytes32`, `uint256`._
 * _Available since v4.9 for `string`, `bytes`._
 */
library StorageSlot {
    struct AddressSlot {
        address value;
    }

    struct BooleanSlot {
        bool value;
    }

    struct Bytes32Slot {
        bytes32 value;
    }

    struct Uint256Slot {
        uint256 value;
    }

    struct StringSlot {
        string value;
    }

    struct BytesSlot {
        bytes value;
    }

    /**
     * @dev Returns an `AddressSlot` with member `value` located at `slot`.
     */
    function getAddressSlot(bytes32 slot) internal pure returns (AddressSlot storage r) {
        /// @solidity memory-safe-assembly
        assembly {
            r.slot := slot
        }
    }

    /**
     * @dev Returns an `BooleanSlot` with member `value` located at `slot`.
     */
    function getBooleanSlot(bytes32 slot) internal pure returns (BooleanSlot storage r) {
        /// @solidity memory-safe-assembly
        assembly {
            r.slot := slot
        }
    }

    /**
     * @dev Returns an `Bytes32Slot` with member `value` located at `slot`.
     */
    function getBytes32Slot(bytes32 slot) internal pure returns (Bytes32Slot storage r) {
        /// @solidity memory-safe-assembly
        assembly {
            r.slot := slot
        }
    }

    /**
     * @dev Returns an `Uint256Slot` with member `value` located at `slot`.
     */
    function getUint256Slot(bytes32 slot) internal pure returns (Uint256Slot storage r) {
        /// @solidity memory-safe-assembly
        assembly {
            r.slot := slot
        }
    }

    /**
     * @dev Returns an `StringSlot` with member `value` located at `slot`.
     */
    function getStringSlot(bytes32 slot) internal pure returns (StringSlot storage r) {
        /// @solidity memory-safe-assembly
        assembly {
            r.slot := slot
        }
    }

    /**
     * @dev Returns an `StringSlot` representation of the string storage pointer `store`.
     */
    function getStringSlot(string storage store) internal pure returns (StringSlot storage r) {
        /// @solidity memory-safe-assembly
        assembly {
            r.slot := store.slot
        }
    }

    /**
     * @dev Returns an `BytesSlot` with member `value` located at `slot`.
     */
    function getBytesSlot(bytes32 slot) internal pure returns (BytesSlot storage r) {
        /// @solidity memory-safe-assembly
        assembly {
            r.slot := slot
        }
    }

    /**
     * @dev Returns an `BytesSlot` representation of the bytes storage pointer `store`.
     */
    function getBytesSlot(bytes storage store) internal pure returns (BytesSlot storage r) {
        /// @solidity memory-safe-assembly
        assembly {
            r.slot := store.slot
        }
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC165 standard, as defined in the
 * https://eips.ethereum.org/EIPS/eip-165[EIP].
 *
 * Implementers can declare support of contract interfaces, which can then be
 * queried by others ({ERC165Checker}).
 *
 * For an implementation, see {ERC165}.
 */
interface IERC165 {
    /**
     * @dev Returns true if this contract implements the interface defined by
     * `interfaceId`. See the corresponding
     * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
     * to learn more about how these ids are created.
     *
     * This function call must use less than 30 000 gas.
     */
    function supportsInterface(bytes4 interfaceId) external view returns (bool);
}

Settings
{
  "remappings": [
    "solmate/=lib/solmate/",
    "openzeppelin/=lib/openzeppelin-contracts/",
    "openzeppelin-latest/=lib/openzeppelin-latest/",
    "chainlink/=lib/chainlink-brownie-contracts/contracts/src/",
    "chainlink-brownie-contracts/=lib/chainlink-brownie-contracts/",
    "ds-test/=lib/forge-std/lib/ds-test/src/",
    "erc4626-tests/=lib/openzeppelin-latest/lib/erc4626-tests/",
    "forge-std/=lib/forge-std/src/",
    "openzeppelin-contracts/=lib/openzeppelin-contracts/"
  ],
  "optimizer": {
    "enabled": true,
    "runs": 100000
  },
  "metadata": {
    "useLiteralContent": false,
    "bytecodeHash": "none",
    "appendCBOR": true
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "abi"
      ]
    }
  },
  "evmVersion": "paris",
  "libraries": {}
}

Contract Security Audit

Contract ABI

API
[{"inputs":[{"internalType":"address","name":"_idRegistry","type":"address"},{"internalType":"address","name":"_storageRegistry","type":"address"},{"internalType":"address","name":"_keyRegistry","type":"address"},{"internalType":"address","name":"_trustedCaller","type":"address"},{"internalType":"address","name":"_initialOwner","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"CallFailed","type":"error"},{"inputs":[],"name":"InvalidAddress","type":"error"},{"inputs":[],"name":"InvalidAmount","type":"error"},{"inputs":[],"name":"OnlyTrustedCaller","type":"error"},{"inputs":[],"name":"Registrable","type":"error"},{"inputs":[],"name":"Seedable","type":"error"},{"inputs":[],"name":"Unauthorized","type":"error"},{"anonymous":false,"inputs":[],"name":"DisableTrustedOnly","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferStarted","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":"address","name":"oldCaller","type":"address"},{"indexed":true,"internalType":"address","name":"newCaller","type":"address"},{"indexed":false,"internalType":"address","name":"owner","type":"address"}],"name":"SetTrustedCaller","type":"event"},{"inputs":[],"name":"VERSION","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"acceptOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"disableTrustedOnly","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"idRegistry","outputs":[{"internalType":"contract IdRegistry","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"keyRegistry","outputs":[{"internalType":"contract KeyRegistry","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pendingOwner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"to","type":"address"},{"internalType":"address","name":"recovery","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"bytes","name":"sig","type":"bytes"}],"internalType":"struct IBundler.RegistrationParams","name":"registration","type":"tuple"},{"components":[{"internalType":"uint32","name":"keyType","type":"uint32"},{"internalType":"bytes","name":"key","type":"bytes"},{"internalType":"uint8","name":"metadataType","type":"uint8"},{"internalType":"bytes","name":"metadata","type":"bytes"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"bytes","name":"sig","type":"bytes"}],"internalType":"struct IBundler.SignerParams[]","name":"signers","type":"tuple[]"},{"internalType":"uint256","name":"storageUnits","type":"uint256"}],"name":"register","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_trustedCaller","type":"address"}],"name":"setTrustedCaller","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"storageRegistry","outputs":[{"internalType":"contract StorageRegistry","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"to","type":"address"},{"internalType":"address","name":"recovery","type":"address"},{"components":[{"internalType":"uint32","name":"keyType","type":"uint32"},{"internalType":"bytes","name":"key","type":"bytes"},{"internalType":"uint8","name":"metadataType","type":"uint8"},{"internalType":"bytes","name":"metadata","type":"bytes"}],"internalType":"struct IBundler.SignerData[]","name":"signers","type":"tuple[]"},{"internalType":"uint256","name":"units","type":"uint256"}],"internalType":"struct IBundler.UserData[]","name":"users","type":"tuple[]"}],"name":"trustedBatchRegister","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"trustedCaller","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"trustedOnly","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"to","type":"address"},{"internalType":"address","name":"recovery","type":"address"},{"components":[{"internalType":"uint32","name":"keyType","type":"uint32"},{"internalType":"bytes","name":"key","type":"bytes"},{"internalType":"uint8","name":"metadataType","type":"uint8"},{"internalType":"bytes","name":"metadata","type":"bytes"}],"internalType":"struct IBundler.SignerData[]","name":"signers","type":"tuple[]"},{"internalType":"uint256","name":"units","type":"uint256"}],"internalType":"struct IBundler.UserData","name":"user","type":"tuple"}],"name":"trustedRegister","outputs":[],"stateMutability":"nonpayable","type":"function"},{"stateMutability":"payable","type":"receive"}]

60e060405260016003553480156200001657600080fd5b5060405162001a0038038062001a00833981016040819052620000399162000199565b80620000453362000080565b620000508162000080565b506001600160a01b0380861660805284811660a052831660c05262000075826200009e565b505050505062000209565b600180546001600160a01b03191690556200009b816200012c565b50565b6001600160a01b038116620000c65760405163e6c4247b60e01b815260040160405180910390fd5b6002546040513381526001600160a01b038381169216907ff49aa3de84c4496d3a942511e6d2319eb43aff3afa8072a0cb18f9cc55a0359f9060200160405180910390a3600280546001600160a01b0319166001600160a01b0392909216919091179055565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b80516001600160a01b03811681146200019457600080fd5b919050565b600080600080600060a08688031215620001b257600080fd5b620001bd866200017c565b9450620001cd602087016200017c565b9350620001dd604087016200017c565b9250620001ed606087016200017c565b9150620001fd608087016200017c565b90509295509295909350565b60805160a05160c0516117816200027f6000396000818161018401528181610621015281816109900152610d65015260008181610114015281816102830152818161072401528181610a930152610e920152600081816101e2015281816104e00152818161084f0152610c6201526117816000f3fe6080604052600436106100f75760003560e01c8063715018a61161008a578063e30c397811610059578063e30c397814610346578063ea40ae1414610371578063f2fde38b14610391578063ffa1ad74146103b157600080fd5b8063715018a6146102de57806379ba5097146102f35780638da5cb5b14610308578063a44c9ce71461033357600080fd5b80634121e73a116100c65780634121e73a146102515780634ec77b45146102715780636b2ddd4e146102a55780636e9bde49146102c957600080fd5b8063086b5198146101725780630aa13b8c146101d0578063268f0760146102045780632a79713f1461023157600080fd5b3661016d573373ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000161461016b576040517f82b4290000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b005b600080fd5b34801561017e57600080fd5b506101a67f000000000000000000000000000000000000000000000000000000000000000081565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020015b60405180910390f35b3480156101dc57600080fd5b506101a67f000000000000000000000000000000000000000000000000000000000000000081565b34801561021057600080fd5b506002546101a69073ffffffffffffffffffffffffffffffffffffffff1681565b34801561023d57600080fd5b5061016b61024c366004611276565b610407565b34801561025d57600080fd5b5061016b61026c3660046112d0565b6107a9565b34801561027d57600080fd5b506101a67f000000000000000000000000000000000000000000000000000000000000000081565b3480156102b157600080fd5b506102bb60035481565b6040519081526020016101c7565b3480156102d557600080fd5b5061016b610b09565b3480156102ea57600080fd5b5061016b610b41565b3480156102ff57600080fd5b5061016b610b55565b34801561031457600080fd5b5060005473ffffffffffffffffffffffffffffffffffffffff166101a6565b61016b61034136600461130d565b610c0f565b34801561035257600080fd5b5060015473ffffffffffffffffffffffffffffffffffffffff166101a6565b34801561037d57600080fd5b5061016b61038c36600461137d565b610f13565b34801561039d57600080fd5b5061016b6103ac36600461137d565b610f24565b3480156103bd57600080fd5b506103fa6040518060400160405280600a81526020017f323032332e30382e32330000000000000000000000000000000000000000000081525081565b6040516101c791906113ba565b600354600003610443576040517f7cb40a3900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60025473ffffffffffffffffffffffffffffffffffffffff163314610494576040517f7033971c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8060005b818110156107a357368484838181106104b3576104b3611426565b90506020028101906104c59190611455565b9050600073ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001663b12da9aa610512602085018561137d565b610522604086016020870161137d565b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e085901b16815273ffffffffffffffffffffffffffffffffffffffff9283166004820152911660248201526044016020604051808303816000875af1158015610594573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105b89190611493565b905060006105c960408401846114ac565b9050905060005b818110156106ea57366105e660408601866114ac565b838181106105f6576105f6611426565b90506020028101906106089190611455565b905073ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016632cd840f0610653602088018861137d565b6106606020850185611514565b61066d602086018661153a565b61067d606088016040890161159f565b61068a606089018961153a565b6040518863ffffffff1660e01b81526004016106ac979695949392919061160b565b600060405180830381600087803b1580156106c657600080fd5b505af11580156106da573d6000803e3d6000fd5b50505050816001019150506105d0565b506040517fab7ccc1c00000000000000000000000000000000000000000000000000000000815260048101839052606084013560248201527f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff169063ab7ccc1c90604401600060405180830381600087803b15801561077d57600080fd5b505af1158015610791573d6000803e3d6000fd5b50505050836001019350505050610498565b50505050565b6003546000036107e5576040517f7cb40a3900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60025473ffffffffffffffffffffffffffffffffffffffff163314610836576040517f7033971c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001663b12da9aa610881602085018561137d565b610891604086016020870161137d565b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e085901b16815273ffffffffffffffffffffffffffffffffffffffff9283166004820152911660248201526044016020604051808303816000875af1158015610903573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109279190611493565b9050600061093860408401846114ac565b9050905060005b81811015610a59573661095560408601866114ac565b8381811061096557610965611426565b90506020028101906109779190611455565b905073ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016632cd840f06109c2602088018861137d565b6109cf6020850185611514565b6109dc602086018661153a565b6109ec606088016040890161159f565b6109f9606089018961153a565b6040518863ffffffff1660e01b8152600401610a1b979695949392919061160b565b600060405180830381600087803b158015610a3557600080fd5b505af1158015610a49573d6000803e3d6000fd5b505050508160010191505061093f565b506040517fab7ccc1c00000000000000000000000000000000000000000000000000000000815260048101839052606084013560248201527f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff169063ab7ccc1c90604401600060405180830381600087803b158015610aec57600080fd5b505af1158015610b00573d6000803e3d6000fd5b50505050505050565b610b11610fd4565b600060038190556040517f03732e5295a5bd18e6ef95b03b41aa8bcadae292a7ef40468144c7a727dfa8b59190a1565b610b49610fd4565b610b536000611055565b565b600154339073ffffffffffffffffffffffffffffffffffffffff168114610c03576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602960248201527f4f776e61626c6532537465703a2063616c6c6572206973206e6f74207468652060448201527f6e6577206f776e6572000000000000000000000000000000000000000000000060648201526084015b60405180910390fd5b610c0c81611055565b50565b80600003610c49576040517f2c5211c600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016633efa0b02610c94602088018861137d565b610ca46040890160208a0161137d565b6040890135610cb660608b018b61153a565b6040518663ffffffff1660e01b8152600401610cd6959493929190611671565b6020604051808303816000875af1158015610cf5573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d199190611493565b90508260005b81811015610e435736868683818110610d3a57610d3a611426565b9050602002810190610d4c91906116bc565b905073ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001663a005d3d2610d9760208b018b61137d565b610da46020850185611514565b610db1602086018661153a565b610dc1606088016040890161159f565b610dce606089018961153a565b60808a0135610de060a08c018c61153a565b6040518b63ffffffff1660e01b8152600401610e059a999897969594939291906116f0565b600060405180830381600087803b158015610e1f57600080fd5b505af1158015610e33573d6000803e3d6000fd5b5050505081600101915050610d1f565b506040517f783a112b000000000000000000000000000000000000000000000000000000008152600481018390526024810184905260009073ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000169063783a112b90349060440160206040518083038185885af1158015610edc573d6000803e3d6000fd5b50505050506040513d601f19601f82011682018060405250810190610f019190611493565b90508015610b0057610b003382611086565b610f1b610fd4565b610c0c816110d0565b610f2c610fd4565b6001805473ffffffffffffffffffffffffffffffffffffffff83167fffffffffffffffffffffffff00000000000000000000000000000000000000009091168117909155610f8f60005473ffffffffffffffffffffffffffffffffffffffff1690565b73ffffffffffffffffffffffffffffffffffffffff167f38d16b8cac22d99fc7c124b9cd0de2d3fa1faef420bfe791d8c362d765e2270060405160405180910390a350565b60005473ffffffffffffffffffffffffffffffffffffffff163314610b53576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152606401610bfa565b600180547fffffffffffffffffffffffff0000000000000000000000000000000000000000169055610c0c816111b5565b600080600080600085875af19050806110cb576040517f3204506f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b505050565b73ffffffffffffffffffffffffffffffffffffffff811661111d576040517fe6c4247b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60025460405133815273ffffffffffffffffffffffffffffffffffffffff8381169216907ff49aa3de84c4496d3a942511e6d2319eb43aff3afa8072a0cb18f9cc55a0359f9060200160405180910390a3600280547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff92909216919091179055565b6000805473ffffffffffffffffffffffffffffffffffffffff8381167fffffffffffffffffffffffff0000000000000000000000000000000000000000831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b60008083601f84011261123c57600080fd5b50813567ffffffffffffffff81111561125457600080fd5b6020830191508360208260051b850101111561126f57600080fd5b9250929050565b6000806020838503121561128957600080fd5b823567ffffffffffffffff8111156112a057600080fd5b6112ac8582860161122a565b90969095509350505050565b6000608082840312156112ca57600080fd5b50919050565b6000602082840312156112e257600080fd5b813567ffffffffffffffff8111156112f957600080fd5b611305848285016112b8565b949350505050565b6000806000806060858703121561132357600080fd5b843567ffffffffffffffff8082111561133b57600080fd5b611347888389016112b8565b9550602087013591508082111561135d57600080fd5b5061136a8782880161122a565b9598909750949560400135949350505050565b60006020828403121561138f57600080fd5b813573ffffffffffffffffffffffffffffffffffffffff811681146113b357600080fd5b9392505050565b600060208083528351808285015260005b818110156113e7578581018301518582016040015282016113cb565b5060006040828601015260407fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8301168501019250505092915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b600082357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8183360301811261148957600080fd5b9190910192915050565b6000602082840312156114a557600080fd5b5051919050565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18436030181126114e157600080fd5b83018035915067ffffffffffffffff8211156114fc57600080fd5b6020019150600581901b360382131561126f57600080fd5b60006020828403121561152657600080fd5b813563ffffffff811681146113b357600080fd5b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe184360301811261156f57600080fd5b83018035915067ffffffffffffffff82111561158a57600080fd5b60200191503681900382131561126f57600080fd5b6000602082840312156115b157600080fd5b813560ff811681146113b357600080fd5b8183528181602085013750600060208284010152600060207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f840116840101905092915050565b73ffffffffffffffffffffffffffffffffffffffff8816815263ffffffff8716602082015260a06040820152600061164760a0830187896115c2565b60ff8616606084015282810360808401526116638185876115c2565b9a9950505050505050505050565b600073ffffffffffffffffffffffffffffffffffffffff8088168352808716602084015250846040830152608060608301526116b16080830184866115c2565b979650505050505050565b600082357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff4183360301811261148957600080fd5b73ffffffffffffffffffffffffffffffffffffffff8b16815263ffffffff8a16602082015260e06040820152600061172c60e083018a8c6115c2565b60ff89166060840152828103608084015261174881888a6115c2565b90508560a084015282810360c08401526117638185876115c2565b9d9c5050505050505050505050505056fea164736f6c6343000815000a00000000000000000000000000000000fcaf86937e41ba038b4fa40baa4b780a00000000000000000000000000000000fcce7f938e7ae6d3c335bd6a1a7c593d00000000000000000000000000000000fc9e66f1c6d86d750b4af47ff0cc343d0000000000000000000000002d93c2f74b2c4697f9ea85d0450148aa45d4d5a200000000000000000000000053c6da835c777ad11159198fbe11f95e5ee6b692

Deployed Bytecode

0x6080604052600436106100f75760003560e01c8063715018a61161008a578063e30c397811610059578063e30c397814610346578063ea40ae1414610371578063f2fde38b14610391578063ffa1ad74146103b157600080fd5b8063715018a6146102de57806379ba5097146102f35780638da5cb5b14610308578063a44c9ce71461033357600080fd5b80634121e73a116100c65780634121e73a146102515780634ec77b45146102715780636b2ddd4e146102a55780636e9bde49146102c957600080fd5b8063086b5198146101725780630aa13b8c146101d0578063268f0760146102045780632a79713f1461023157600080fd5b3661016d573373ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000fcce7f938e7ae6d3c335bd6a1a7c593d161461016b576040517f82b4290000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b005b600080fd5b34801561017e57600080fd5b506101a67f00000000000000000000000000000000fc9e66f1c6d86d750b4af47ff0cc343d81565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020015b60405180910390f35b3480156101dc57600080fd5b506101a67f00000000000000000000000000000000fcaf86937e41ba038b4fa40baa4b780a81565b34801561021057600080fd5b506002546101a69073ffffffffffffffffffffffffffffffffffffffff1681565b34801561023d57600080fd5b5061016b61024c366004611276565b610407565b34801561025d57600080fd5b5061016b61026c3660046112d0565b6107a9565b34801561027d57600080fd5b506101a67f00000000000000000000000000000000fcce7f938e7ae6d3c335bd6a1a7c593d81565b3480156102b157600080fd5b506102bb60035481565b6040519081526020016101c7565b3480156102d557600080fd5b5061016b610b09565b3480156102ea57600080fd5b5061016b610b41565b3480156102ff57600080fd5b5061016b610b55565b34801561031457600080fd5b5060005473ffffffffffffffffffffffffffffffffffffffff166101a6565b61016b61034136600461130d565b610c0f565b34801561035257600080fd5b5060015473ffffffffffffffffffffffffffffffffffffffff166101a6565b34801561037d57600080fd5b5061016b61038c36600461137d565b610f13565b34801561039d57600080fd5b5061016b6103ac36600461137d565b610f24565b3480156103bd57600080fd5b506103fa6040518060400160405280600a81526020017f323032332e30382e32330000000000000000000000000000000000000000000081525081565b6040516101c791906113ba565b600354600003610443576040517f7cb40a3900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60025473ffffffffffffffffffffffffffffffffffffffff163314610494576040517f7033971c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8060005b818110156107a357368484838181106104b3576104b3611426565b90506020028101906104c59190611455565b9050600073ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000fcaf86937e41ba038b4fa40baa4b780a1663b12da9aa610512602085018561137d565b610522604086016020870161137d565b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e085901b16815273ffffffffffffffffffffffffffffffffffffffff9283166004820152911660248201526044016020604051808303816000875af1158015610594573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105b89190611493565b905060006105c960408401846114ac565b9050905060005b818110156106ea57366105e660408601866114ac565b838181106105f6576105f6611426565b90506020028101906106089190611455565b905073ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000fc9e66f1c6d86d750b4af47ff0cc343d16632cd840f0610653602088018861137d565b6106606020850185611514565b61066d602086018661153a565b61067d606088016040890161159f565b61068a606089018961153a565b6040518863ffffffff1660e01b81526004016106ac979695949392919061160b565b600060405180830381600087803b1580156106c657600080fd5b505af11580156106da573d6000803e3d6000fd5b50505050816001019150506105d0565b506040517fab7ccc1c00000000000000000000000000000000000000000000000000000000815260048101839052606084013560248201527f00000000000000000000000000000000fcce7f938e7ae6d3c335bd6a1a7c593d73ffffffffffffffffffffffffffffffffffffffff169063ab7ccc1c90604401600060405180830381600087803b15801561077d57600080fd5b505af1158015610791573d6000803e3d6000fd5b50505050836001019350505050610498565b50505050565b6003546000036107e5576040517f7cb40a3900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60025473ffffffffffffffffffffffffffffffffffffffff163314610836576040517f7033971c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000fcaf86937e41ba038b4fa40baa4b780a1663b12da9aa610881602085018561137d565b610891604086016020870161137d565b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e085901b16815273ffffffffffffffffffffffffffffffffffffffff9283166004820152911660248201526044016020604051808303816000875af1158015610903573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109279190611493565b9050600061093860408401846114ac565b9050905060005b81811015610a59573661095560408601866114ac565b8381811061096557610965611426565b90506020028101906109779190611455565b905073ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000fc9e66f1c6d86d750b4af47ff0cc343d16632cd840f06109c2602088018861137d565b6109cf6020850185611514565b6109dc602086018661153a565b6109ec606088016040890161159f565b6109f9606089018961153a565b6040518863ffffffff1660e01b8152600401610a1b979695949392919061160b565b600060405180830381600087803b158015610a3557600080fd5b505af1158015610a49573d6000803e3d6000fd5b505050508160010191505061093f565b506040517fab7ccc1c00000000000000000000000000000000000000000000000000000000815260048101839052606084013560248201527f00000000000000000000000000000000fcce7f938e7ae6d3c335bd6a1a7c593d73ffffffffffffffffffffffffffffffffffffffff169063ab7ccc1c90604401600060405180830381600087803b158015610aec57600080fd5b505af1158015610b00573d6000803e3d6000fd5b50505050505050565b610b11610fd4565b600060038190556040517f03732e5295a5bd18e6ef95b03b41aa8bcadae292a7ef40468144c7a727dfa8b59190a1565b610b49610fd4565b610b536000611055565b565b600154339073ffffffffffffffffffffffffffffffffffffffff168114610c03576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602960248201527f4f776e61626c6532537465703a2063616c6c6572206973206e6f74207468652060448201527f6e6577206f776e6572000000000000000000000000000000000000000000000060648201526084015b60405180910390fd5b610c0c81611055565b50565b80600003610c49576040517f2c5211c600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000fcaf86937e41ba038b4fa40baa4b780a16633efa0b02610c94602088018861137d565b610ca46040890160208a0161137d565b6040890135610cb660608b018b61153a565b6040518663ffffffff1660e01b8152600401610cd6959493929190611671565b6020604051808303816000875af1158015610cf5573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d199190611493565b90508260005b81811015610e435736868683818110610d3a57610d3a611426565b9050602002810190610d4c91906116bc565b905073ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000fc9e66f1c6d86d750b4af47ff0cc343d1663a005d3d2610d9760208b018b61137d565b610da46020850185611514565b610db1602086018661153a565b610dc1606088016040890161159f565b610dce606089018961153a565b60808a0135610de060a08c018c61153a565b6040518b63ffffffff1660e01b8152600401610e059a999897969594939291906116f0565b600060405180830381600087803b158015610e1f57600080fd5b505af1158015610e33573d6000803e3d6000fd5b5050505081600101915050610d1f565b506040517f783a112b000000000000000000000000000000000000000000000000000000008152600481018390526024810184905260009073ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000fcce7f938e7ae6d3c335bd6a1a7c593d169063783a112b90349060440160206040518083038185885af1158015610edc573d6000803e3d6000fd5b50505050506040513d601f19601f82011682018060405250810190610f019190611493565b90508015610b0057610b003382611086565b610f1b610fd4565b610c0c816110d0565b610f2c610fd4565b6001805473ffffffffffffffffffffffffffffffffffffffff83167fffffffffffffffffffffffff00000000000000000000000000000000000000009091168117909155610f8f60005473ffffffffffffffffffffffffffffffffffffffff1690565b73ffffffffffffffffffffffffffffffffffffffff167f38d16b8cac22d99fc7c124b9cd0de2d3fa1faef420bfe791d8c362d765e2270060405160405180910390a350565b60005473ffffffffffffffffffffffffffffffffffffffff163314610b53576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152606401610bfa565b600180547fffffffffffffffffffffffff0000000000000000000000000000000000000000169055610c0c816111b5565b600080600080600085875af19050806110cb576040517f3204506f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b505050565b73ffffffffffffffffffffffffffffffffffffffff811661111d576040517fe6c4247b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60025460405133815273ffffffffffffffffffffffffffffffffffffffff8381169216907ff49aa3de84c4496d3a942511e6d2319eb43aff3afa8072a0cb18f9cc55a0359f9060200160405180910390a3600280547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff92909216919091179055565b6000805473ffffffffffffffffffffffffffffffffffffffff8381167fffffffffffffffffffffffff0000000000000000000000000000000000000000831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b60008083601f84011261123c57600080fd5b50813567ffffffffffffffff81111561125457600080fd5b6020830191508360208260051b850101111561126f57600080fd5b9250929050565b6000806020838503121561128957600080fd5b823567ffffffffffffffff8111156112a057600080fd5b6112ac8582860161122a565b90969095509350505050565b6000608082840312156112ca57600080fd5b50919050565b6000602082840312156112e257600080fd5b813567ffffffffffffffff8111156112f957600080fd5b611305848285016112b8565b949350505050565b6000806000806060858703121561132357600080fd5b843567ffffffffffffffff8082111561133b57600080fd5b611347888389016112b8565b9550602087013591508082111561135d57600080fd5b5061136a8782880161122a565b9598909750949560400135949350505050565b60006020828403121561138f57600080fd5b813573ffffffffffffffffffffffffffffffffffffffff811681146113b357600080fd5b9392505050565b600060208083528351808285015260005b818110156113e7578581018301518582016040015282016113cb565b5060006040828601015260407fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8301168501019250505092915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b600082357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8183360301811261148957600080fd5b9190910192915050565b6000602082840312156114a557600080fd5b5051919050565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18436030181126114e157600080fd5b83018035915067ffffffffffffffff8211156114fc57600080fd5b6020019150600581901b360382131561126f57600080fd5b60006020828403121561152657600080fd5b813563ffffffff811681146113b357600080fd5b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe184360301811261156f57600080fd5b83018035915067ffffffffffffffff82111561158a57600080fd5b60200191503681900382131561126f57600080fd5b6000602082840312156115b157600080fd5b813560ff811681146113b357600080fd5b8183528181602085013750600060208284010152600060207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f840116840101905092915050565b73ffffffffffffffffffffffffffffffffffffffff8816815263ffffffff8716602082015260a06040820152600061164760a0830187896115c2565b60ff8616606084015282810360808401526116638185876115c2565b9a9950505050505050505050565b600073ffffffffffffffffffffffffffffffffffffffff8088168352808716602084015250846040830152608060608301526116b16080830184866115c2565b979650505050505050565b600082357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff4183360301811261148957600080fd5b73ffffffffffffffffffffffffffffffffffffffff8b16815263ffffffff8a16602082015260e06040820152600061172c60e083018a8c6115c2565b60ff89166060840152828103608084015261174881888a6115c2565b90508560a084015282810360c08401526117638185876115c2565b9d9c5050505050505050505050505056fea164736f6c6343000815000a

Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)

00000000000000000000000000000000fcaf86937e41ba038b4fa40baa4b780a00000000000000000000000000000000fcce7f938e7ae6d3c335bd6a1a7c593d00000000000000000000000000000000fc9e66f1c6d86d750b4af47ff0cc343d0000000000000000000000002d93c2f74b2c4697f9ea85d0450148aa45d4d5a200000000000000000000000053c6da835c777ad11159198fbe11f95e5ee6b692

-----Decoded View---------------
Arg [0] : _idRegistry (address): 0x00000000FcAf86937e41bA038B4fA40BAA4B780A
Arg [1] : _storageRegistry (address): 0x00000000fcCe7f938e7aE6D3c335bD6a1a7c593D
Arg [2] : _keyRegistry (address): 0x00000000fC9e66f1c6d86D750B4af47fF0Cc343d
Arg [3] : _trustedCaller (address): 0x2D93c2F74b2C4697f9ea85D0450148AA45D4D5a2
Arg [4] : _initialOwner (address): 0x53c6dA835c777AD11159198FBe11f95E5eE6B692

-----Encoded View---------------
5 Constructor Arguments found :
Arg [0] : 00000000000000000000000000000000fcaf86937e41ba038b4fa40baa4b780a
Arg [1] : 00000000000000000000000000000000fcce7f938e7ae6d3c335bd6a1a7c593d
Arg [2] : 00000000000000000000000000000000fc9e66f1c6d86d750b4af47ff0cc343d
Arg [3] : 0000000000000000000000002d93c2f74b2c4697f9ea85d0450148aa45d4d5a2
Arg [4] : 00000000000000000000000053c6da835c777ad11159198fbe11f95e5ee6b692


Deployed Bytecode Sourcemap

561:6552:24:-:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;7043:10;:38;7065:15;7043:38;;7039:65;;7090:14;;;;;;;;;;;;;;7039:65;561:6552;;;;;1846:40;;;;;;;;;;;;;;;;;;210:42:38;198:55;;;180:74;;168:2;153:18;1846:40:24;;;;;;;;1614:38;;;;;;;;;;;;;;;1734:28:37;;;;;;;;;;-1:-1:-1;1734:28:37;;;;;;;;5973:974:24;;;;;;;;;;-1:-1:-1;5973:974:24;;;;;:::i;:::-;;:::i;4985:603::-;;;;;;;;;;-1:-1:-1;4985:603:24;;;;;:::i;:::-;;:::i;1727:48::-;;;;;;;;;;;;;;;1980:30:37;;;;;;;;;;;;;;;;;;;2548:25:38;;;2536:2;2521:18;1980:30:37;2402:177:38;3574:119:37;;;;;;;;;;;;;:::i;1824:101:5:-;;;;;;;;;;;;;:::i;1734:212:6:-;;;;;;;;;;;;;:::i;1201:85:5:-;;;;;;;;;;-1:-1:-1;1247:7:5;1273:6;;;1201:85;;3520:1143:24;;;;;;:::i;:::-;;:::i;847:99:6:-;;;;;;;;;;-1:-1:-1;926:13:6;;;;847:99;;3354:117:37;;;;;;;;;;-1:-1:-1;3354:117:37;;;;;:::i;:::-;;:::i;1139:178:6:-;;;;;;;;;;-1:-1:-1;1139:178:6;;;;;:::i;:::-;;:::i;1317:45:24:-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;5973:974::-;2329:11:37;;2344:1;2329:16;2325:42;;2354:13;;;;;;;;;;;;;;2325:42;2395:13;;;;2381:10;:27;2377:59;;2417:19;;;;;;;;;;;;;;2377:59;6158:5:24;6139:16:::1;6180:761;6200:8;6196:1;:12;6180:761;;;6225:22;6250:5;;6256:1;6250:8;;;;;;;:::i;:::-;;;;;;;;;;;;:::i;:::-;6225:33:::0;-1:-1:-1;6272:11:24::1;6286:26;:10;:26;;6313:7;;::::0;::::1;6225:33:::0;6313:7:::1;:::i;:::-;6322:13;::::0;;;::::1;::::0;::::1;;:::i;:::-;6286:50;::::0;;::::1;::::0;;;;;;5098:42:38;5167:15;;;6286:50:24::1;::::0;::::1;5149:34:38::0;5219:15;;5199:18;;;5192:43;5061:18;;6286:50:24::1;;;;;;;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;6272:64:::0;-1:-1:-1;6350:18:24::1;6371:12;;::::0;::::1;:4:::0;:12:::1;:::i;:::-;:19;;6350:40;;6410:9;6405:296;6425:10;6421:1;:14;6405:296;;;6456:26;6485:12;;::::0;::::1;:4:::0;:12:::1;:::i;:::-;6498:1;6485:15;;;;;;;:::i;:::-;;;;;;;;;;;;:::i;:::-;6456:44:::0;-1:-1:-1;6518:22:24::1;:11;:22;;6541:7;;::::0;::::1;:4:::0;:7:::1;:::i;:::-;6550:14;;::::0;::::1;:6:::0;:14:::1;:::i;:::-;6566:10;;::::0;::::1;:6:::0;:10:::1;:::i;:::-;6578:19;::::0;;;::::1;::::0;::::1;;:::i;:::-;6599:15;;::::0;::::1;:6:::0;:15:::1;:::i;:::-;6518:97;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;::::0;::::1;;;;;;;;;6665:3;;;;;6438:263;6405:296;;;-1:-1:-1::0;6715:39:24::1;::::0;;;;::::1;::::0;::::1;8832:25:38::0;;;6743:10:24::1;::::0;::::1;;8873:18:38::0;;;8866:34;6715:15:24::1;:22;;::::0;::::1;::::0;8805:18:38;;6715:39:24::1;;;;;;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;::::0;::::1;;;;;;;;;6913:3;;;;;6211:730;;;6180:761;;;;6057:890;5973:974:::0;;:::o;4985:603::-;2329:11:37;;2344:1;2329:16;2325:42;;2354:13;;;;;;;;;;;;;;2325:42;2395:13;;;;2381:10;:27;2377:59;;2417:19;;;;;;;;;;;;;;2377:59;5137:11:24::1;5151:26;:10;:26;;5178:7;;::::0;::::1;:4:::0;:7:::1;:::i;:::-;5187:13;::::0;;;::::1;::::0;::::1;;:::i;:::-;5151:50;::::0;;::::1;::::0;;;;;;5098:42:38;5167:15;;;5151:50:24::1;::::0;::::1;5149:34:38::0;5219:15;;5199:18;;;5192:43;5061:18;;5151:50:24::1;;;;;;;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;5137:64:::0;-1:-1:-1;5211:18:24::1;5232:12;;::::0;::::1;:4:::0;:12:::1;:::i;:::-;:19;;5211:40;;5266:9;5261:272;5281:10;5277:1;:14;5261:272;;;5308:26;5337:12;;::::0;::::1;:4:::0;:12:::1;:::i;:::-;5350:1;5337:15;;;;;;;:::i;:::-;;;;;;;;;;;;:::i;:::-;5308:44:::0;-1:-1:-1;5366:22:24::1;:11;:22;;5389:7;;::::0;::::1;:4:::0;:7:::1;:::i;:::-;5398:14;;::::0;::::1;:6:::0;:14:::1;:::i;:::-;5414:10;;::::0;::::1;:6:::0;:10:::1;:::i;:::-;5426:19;::::0;;;::::1;::::0;::::1;;:::i;:::-;5447:15;;::::0;::::1;:6:::0;:15:::1;:::i;:::-;5366:97;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;::::0;::::1;;;;;;;;;5505:3;;;;;5294:239;5261:272;;;-1:-1:-1::0;5542:39:24::1;::::0;;;;::::1;::::0;::::1;8832:25:38::0;;;5570:10:24::1;::::0;::::1;;8873:18:38::0;;;8866:34;5542:15:24::1;:22;;::::0;::::1;::::0;8805:18:38;;5542:39:24::1;;;;;;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;::::0;::::1;;;;;;;;;5061:527;;4985:603:::0;:::o;3574:119:37:-;1094:13:5;:11;:13::i;:::-;3633:18:37::1;3640:11;3633:18:::0;;;3666:20:::1;::::0;::::1;::::0;3633:18;3666:20:::1;3574:119::o:0;1824:101:5:-;1094:13;:11;:13::i;:::-;1888:30:::1;1915:1;1888:18;:30::i;:::-;1824:101::o:0;1734:212:6:-;926:13;;719:10:10;;1833:24:6;926:13;1833:24;;1825:78;;;;;;;9113:2:38;1825:78:6;;;9095:21:38;9152:2;9132:18;;;9125:30;9191:34;9171:18;;;9164:62;9262:11;9242:18;;;9235:39;9291:19;;1825:78:6;;;;;;;;;1913:26;1932:6;1913:18;:26::i;:::-;1776:170;1734:212::o;3520:1143:24:-;3696:12;3712:1;3696:17;3692:45;;3722:15;;;;;;;;;;;;;;3692:45;3747:11;3773:22;:10;:22;;3796:15;;;;:12;:15;:::i;:::-;3813:21;;;;;;;;:::i;:::-;3836;;;;3859:16;;;;3836:12;3859:16;:::i;:::-;3773:103;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;3747:129;-1:-1:-1;3908:7:24;3887:18;3932:545;3952:10;3948:1;:14;3932:545;;;3979:28;4010:7;;4018:1;4010:10;;;;;;;:::i;:::-;;;;;;;;;;;;:::i;:::-;3979:41;-1:-1:-1;4034:18:24;:11;:18;;4070:15;;;;:12;:15;:::i;:::-;4103:14;;;;:6;:14;:::i;:::-;4135:10;;;;:6;:10;:::i;:::-;4163:19;;;;;;;;:::i;:::-;4200:15;;;;:6;:15;:::i;:::-;4233;;;;4266:10;;;;4233:6;4266:10;:::i;:::-;4034:256;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;4449:3;;;;;3965:512;3932:545;;;-1:-1:-1;4509:57:24;;;;;;;;8832:25:38;;;8873:18;;;8866:34;;;4487:19:24;;4509:20;:15;:20;;;;4537:9;;8805:18:38;;4509:57:24;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;4487:79;-1:-1:-1;4581:15:24;;4577:80;;4612:34;:10;4634:11;4612:21;:34::i;3354:117:37:-;1094:13:5;:11;:13::i;:::-;3431:33:37::1;3449:14;3431:17;:33::i;1139:178:6:-:0;1094:13:5;:11;:13::i;:::-;1228::6::1;:24:::0;;::::1;::::0;::::1;::::0;;;::::1;::::0;::::1;::::0;;;1292:7:::1;1247::5::0;1273:6;;;;1201:85;1292:7:6::1;1267:43;;;;;;;;;;;;1139:178:::0;:::o;1359:130:5:-;1247:7;1273:6;1422:23;1273:6;719:10:10;1422:23:5;1414:68;;;;;;;11442:2:38;1414:68:5;;;11424:21:38;;;11461:18;;;11454:30;11520:34;11500:18;;;11493:62;11572:18;;1414:68:5;11240:356:38;1501:153:6;1590:13;1583:20;;;;;;1613:34;1638:8;1613:24;:34::i;225:350:36:-;292:12;513:1;510;507;504;496:6;492:2;485:5;480:35;469:46;;540:7;535:33;;556:12;;;;;;;;;;;;;;535:33;282:293;225:350;;:::o;4032:249:37:-;4106:28;;;4102:57;;4143:16;;;;;;;;;;;;;;4102:57;4192:13;;4175:59;;4223:10;180:74:38;;4175:59:37;;;;;4192:13;;4175:59;;168:2:38;153:18;4175:59:37;;;;;;;4244:13;:30;;;;;;;;;;;;;;;4032:249::o;2426:187:5:-;2499:16;2518:6;;;2534:17;;;;;;;;;;2566:40;;2518:6;;;;;;;2566:40;;2499:16;2566:40;2489:124;2426:187;:::o;746:384:38:-;826:8;836:6;890:3;883:4;875:6;871:17;867:27;857:55;;908:1;905;898:12;857:55;-1:-1:-1;931:20:38;;974:18;963:30;;960:50;;;1006:1;1003;996:12;960:50;1043:4;1035:6;1031:17;1019:29;;1103:3;1096:4;1086:6;1083:1;1079:14;1071:6;1067:27;1063:38;1060:47;1057:67;;;1120:1;1117;1110:12;1057:67;746:384;;;;;:::o;1135:482::-;1249:6;1257;1310:2;1298:9;1289:7;1285:23;1281:32;1278:52;;;1326:1;1323;1316:12;1278:52;1366:9;1353:23;1399:18;1391:6;1388:30;1385:50;;;1431:1;1428;1421:12;1385:50;1470:87;1549:7;1540:6;1529:9;1525:22;1470:87;:::i;:::-;1576:8;;1444:113;;-1:-1:-1;1135:482:38;-1:-1:-1;;;;1135:482:38:o;1622:157::-;1683:5;1728:3;1719:6;1714:3;1710:16;1706:26;1703:46;;;1745:1;1742;1735:12;1703:46;-1:-1:-1;1767:6:38;1622:157;-1:-1:-1;1622:157:38:o;1784:358::-;1871:6;1924:2;1912:9;1903:7;1899:23;1895:32;1892:52;;;1940:1;1937;1930:12;1892:52;1980:9;1967:23;2013:18;2005:6;2002:30;1999:50;;;2045:1;2042;2035:12;1999:50;2068:68;2128:7;2119:6;2108:9;2104:22;2068:68;:::i;:::-;2058:78;1784:358;-1:-1:-1;;;;1784:358:38:o;2584:821::-;2758:6;2766;2774;2782;2835:2;2823:9;2814:7;2810:23;2806:32;2803:52;;;2851:1;2848;2841:12;2803:52;2891:9;2878:23;2920:18;2961:2;2953:6;2950:14;2947:34;;;2977:1;2974;2967:12;2947:34;3000:68;3060:7;3051:6;3040:9;3036:22;3000:68;:::i;:::-;2990:78;;3121:2;3110:9;3106:18;3093:32;3077:48;;3150:2;3140:8;3137:16;3134:36;;;3166:1;3163;3156:12;3134:36;;3205:89;3286:7;3275:8;3264:9;3260:24;3205:89;:::i;:::-;2584:821;;3313:8;;-1:-1:-1;3179:115:38;;3395:2;3380:18;3367:32;;2584:821;-1:-1:-1;;;;2584:821:38:o;3410:309::-;3469:6;3522:2;3510:9;3501:7;3497:23;3493:32;3490:52;;;3538:1;3535;3528:12;3490:52;3577:9;3564:23;3627:42;3620:5;3616:54;3609:5;3606:65;3596:93;;3685:1;3682;3675:12;3596:93;3708:5;3410:309;-1:-1:-1;;;3410:309:38:o;3724:607::-;3836:4;3865:2;3894;3883:9;3876:21;3926:6;3920:13;3969:6;3964:2;3953:9;3949:18;3942:34;3994:1;4004:140;4018:6;4015:1;4012:13;4004:140;;;4113:14;;;4109:23;;4103:30;4079:17;;;4098:2;4075:26;4068:66;4033:10;;4004:140;;;4008:3;4193:1;4188:2;4179:6;4168:9;4164:22;4160:31;4153:42;4322:2;4252:66;4247:2;4239:6;4235:15;4231:88;4220:9;4216:104;4212:113;4204:121;;;;3724:607;;;;:::o;4336:184::-;4388:77;4385:1;4378:88;4485:4;4482:1;4475:15;4509:4;4506:1;4499:15;4525:384;4619:4;4677:11;4664:25;4767:66;4756:8;4740:14;4736:29;4732:102;4712:18;4708:127;4698:155;;4849:1;4846;4839:12;4698:155;4870:33;;;;;4525:384;-1:-1:-1;;4525:384:38:o;5246:184::-;5316:6;5369:2;5357:9;5348:7;5344:23;5340:32;5337:52;;;5385:1;5382;5375:12;5337:52;-1:-1:-1;5408:16:38;;5246:184;-1:-1:-1;5246:184:38:o;5435:634::-;5558:4;5564:6;5624:11;5611:25;5714:66;5703:8;5687:14;5683:29;5679:102;5659:18;5655:127;5645:155;;5796:1;5793;5786:12;5645:155;5823:33;;5875:20;;;-1:-1:-1;5918:18:38;5907:30;;5904:50;;;5950:1;5947;5940:12;5904:50;5983:4;5971:17;;-1:-1:-1;6034:1:38;6030:14;;;6014;6010:35;6000:46;;5997:66;;;6059:1;6056;6049:12;6465:276;6523:6;6576:2;6564:9;6555:7;6551:23;6547:32;6544:52;;;6592:1;6589;6582:12;6544:52;6631:9;6618:23;6681:10;6674:5;6670:22;6663:5;6660:33;6650:61;;6707:1;6704;6697:12;6746:580;6823:4;6829:6;6889:11;6876:25;6979:66;6968:8;6952:14;6948:29;6944:102;6924:18;6920:127;6910:155;;7061:1;7058;7051:12;6910:155;7088:33;;7140:20;;;-1:-1:-1;7183:18:38;7172:30;;7169:50;;;7215:1;7212;7205:12;7169:50;7248:4;7236:17;;-1:-1:-1;7279:14:38;7275:27;;;7265:38;;7262:58;;;7316:1;7313;7306:12;7331:269;7388:6;7441:2;7429:9;7420:7;7416:23;7412:32;7409:52;;;7457:1;7454;7447:12;7409:52;7496:9;7483:23;7546:4;7539:5;7535:16;7528:5;7525:27;7515:55;;7566:1;7563;7556:12;7605:325;7693:6;7688:3;7681:19;7745:6;7738:5;7731:4;7726:3;7722:14;7709:43;;7797:1;7790:4;7781:6;7776:3;7772:16;7768:27;7761:38;7663:3;7919:4;7849:66;7844:2;7836:6;7832:15;7828:88;7823:3;7819:98;7815:109;7808:116;;7605:325;;;;:::o;7935:718::-;8238:42;8230:6;8226:55;8215:9;8208:74;8330:10;8322:6;8318:23;8313:2;8302:9;8298:18;8291:51;8378:3;8373:2;8362:9;8358:18;8351:31;8189:4;8405:62;8462:3;8451:9;8447:19;8439:6;8431;8405:62;:::i;:::-;8515:4;8507:6;8503:17;8498:2;8487:9;8483:18;8476:45;8570:9;8562:6;8558:22;8552:3;8541:9;8537:19;8530:51;8598:49;8640:6;8632;8624;8598:49;:::i;:::-;8590:57;7935:718;-1:-1:-1;;;;;;;;;;7935:718:38:o;9321:538::-;9525:4;9554:42;9635:2;9627:6;9623:15;9612:9;9605:34;9687:2;9679:6;9675:15;9670:2;9659:9;9655:18;9648:43;;9727:6;9722:2;9711:9;9707:18;9700:34;9770:3;9765:2;9754:9;9750:18;9743:31;9791:62;9848:3;9837:9;9833:19;9825:6;9817;9791:62;:::i;:::-;9783:70;9321:538;-1:-1:-1;;;;;;;9321:538:38:o;9864:388::-;9962:4;10020:11;10007:25;10110:66;10099:8;10083:14;10079:29;10075:102;10055:18;10051:127;10041:155;;10192:1;10189;10182:12;10257:978;10644:42;10636:6;10632:55;10621:9;10614:74;10736:10;10728:6;10724:23;10719:2;10708:9;10704:18;10697:51;10784:3;10779:2;10768:9;10764:18;10757:31;10595:4;10811:62;10868:3;10857:9;10853:19;10845:6;10837;10811:62;:::i;:::-;10921:4;10913:6;10909:17;10904:2;10893:9;10889:18;10882:45;10976:9;10968:6;10964:22;10958:3;10947:9;10943:19;10936:51;11010:49;11052:6;11044;11036;11010:49;:::i;:::-;10996:63;;11096:6;11090:3;11079:9;11075:19;11068:35;11152:9;11144:6;11140:22;11134:3;11123:9;11119:19;11112:51;11180:49;11222:6;11214;11206;11180:49;:::i;:::-;11172:57;10257:978;-1:-1:-1;;;;;;;;;;;;;10257:978:38:o

Swarm Source

none://164736f6c6343000815000a

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

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits
Loading...
Loading
[ Download: CSV Export  ]
[ 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.