ETH Price: $2,420.25 (+0.04%)

Contract

0x357AAFeF834E9078203a96E9Afb188b5f16fB412
 

Overview

ETH Balance

0 ETH

Eth Value

$0.00

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
Register MKR For187420502023-12-08 14:11:11302 days ago1702044671IN
0x357AAFeF...5f16fB412
0 ETH0.0049597863.44218724
Register MKR For187420302023-12-08 14:07:11302 days ago1702044431IN
0x357AAFeF...5f16fB412
0 ETH0.0055639770.4398494
Register MKR For187419482023-12-08 13:50:47302 days ago1702043447IN
0x357AAFeF...5f16fB412
0 ETH0.0035992547.06083736
Register MKR For187419472023-12-08 13:50:35302 days ago1702043435IN
0x357AAFeF...5f16fB412
0 ETH0.0036291446.94396557
Register MKR For185366802023-11-09 20:08:59331 days ago1699560539IN
0x357AAFeF...5f16fB412
0 ETH0.0035725245.68335134
Register MKR For185366492023-11-09 20:02:35331 days ago1699560155IN
0x357AAFeF...5f16fB412
0 ETH0.0036239545.76627732
Register MKR For185366342023-11-09 19:59:35331 days ago1699559975IN
0x357AAFeF...5f16fB412
0 ETH0.0035464445.68803318
Register MKR For185366072023-11-09 19:54:11331 days ago1699559651IN
0x357AAFeF...5f16fB412
0 ETH0.0029625237.49986779
Register MKR For185364972023-11-09 19:32:11331 days ago1699558331IN
0x357AAFeF...5f16fB412
0 ETH0.0037508349.44027768

Latest 1 internal transaction

Advanced mode:
Parent Transaction Hash Block From To
185245092023-11-08 3:18:23333 days ago1699413503  Contract Creation0 ETH
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
MKRVerifier

Compiler Version
v0.8.21+commit.d9974bed

Optimization Enabled:
Yes with 1000000 runs

Other Settings:
shanghai EvmVersion
File 1 of 7 : MKRVerifier.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

// import { console2 } from "forge-std/Test.sol"; // comment out before deploy
import { IHatsEligibility } from "hats-protocol/Interfaces/IHatsEligibility.sol";
import { IHats } from "hats-protocol/Interfaces/IHats.sol";
import { SignatureCheckerLib } from "solady/utils/SignatureCheckerLib.sol";

interface ERC20Like {
  function balanceOf(address account) external view returns (uint256);
}

/**
 * @title MKRVerifier
 * @author spengrah
 * @notice This contract is used to verify that a MakerDAO ecosystem actor has a balance of at least the amount of MKR
 * they claim to have.
 * It also serves as an eligibility module for Hats Protocol, and as such be used to determine
 * whether an ecosystem actor is eligible to hold a particular role within the MakerDAO ecosystem.
 */
contract MKRVerifier is IHatsEligibility {
  /*//////////////////////////////////////////////////////////////
                            CUSTOM ERRORS
  //////////////////////////////////////////////////////////////*/

  /// @dev Thrown when an ecosystem actor tries to register more MKR than they have
  error InsufficientMKR();

  /// @dev Thrown when a non-facilitator tries to register MKR for an ecosystem actor
  error Unauthorized();

  /// @dev Thrown when a signature is invalid
  error InvalidSignature();

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

  /**
   * @notice Emitted when MKR is registered for an ecosystem actor via this contract
   * @param ecosystemActor The ecosystem actor whose MKR was registered
   * @param amount The amount of MKR that was registered
   * @param message Raw string representation of the recognition submission message required by
   * [MIP113-5.2.1.2.3](https://mips.makerdao.com/mips/details/MIP113#5-2-1-2-3).
   */
  event MKRRegistered(address ecosystemActor, uint256 amount, string message);

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

  /// @notice The MKR token contract
  ERC20Like public immutable MKR;

  /// @notice The Hats Protocol contract
  IHats public constant HATS = IHats(0x3bc1A0Ad72417f2d411118085256fC53CBdDd137); // v1.hatsprotocol.eth

  /*//////////////////////////////////////////////////////////////
                            MUTABLE STATE
  //////////////////////////////////////////////////////////////*/

  /// @notice The hat ID of the facilitator role
  uint256 public immutable FACILITATOR_HAT;

  /// @notice The amount of MKR an ecosystem actor has registered with this contract
  mapping(address ecosystemActor => uint256 registeredAmount) public registeredMKR;

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

  /**
   * @notice Create a new MKRVerifier contract
   * @param _facilitatorHat The hat ID of the facilitator role
   */
  constructor(address _MKR, uint256 _facilitatorHat) {
    MKR = ERC20Like(_MKR);
    FACILITATOR_HAT = _facilitatorHat;
  }

  /*//////////////////////////////////////////////////////////////
                      HATS ELIGIBILITY FUNCTION
  //////////////////////////////////////////////////////////////*/

  /// @inheritdoc IHatsEligibility
  function getWearerStatus(address _wearer, uint256 /*_hatId */ )
    public
    view
    virtual
    override
    returns (bool eligible, bool standing)
  {
    /// @dev this module doesn't deal with standing, so we default it to true
    standing = true;

    /**
     * @dev wearers are eligible if they have at least some verified MKR, ie...
     *    1) they have registered some MKR with this contract, and
     *    2) their present MKR balance is greater than or equal to their amount registered
     */
    eligible = getVerifiedMKR(_wearer) > 0;
  }

  /*//////////////////////////////////////////////////////////////
                          PUBLIC FUNCTIONS
  //////////////////////////////////////////////////////////////*/

  /**
   * @notice Register an amount of MKR to be used for eligibility verification. The caller must have at least as much
   * MKR as they are trying to register.
   * This function can also be used by an ecosystem actor to update their registered amount.
   * @param _amount The amount of MKR to register
   * @param _message Raw string representation of the recognition submission message required by
   * [MIP113-5.2.1.2.3](https://mips.makerdao.com/mips/details/MIP113#5-2-1-2-3).
   */
  function registerMKR(uint256 _amount, string calldata _message) public {
    _registerMKR(msg.sender, _amount, _message);
  }

  /*//////////////////////////////////////////////////////////////
                          facilitator FUNCTIONS
  //////////////////////////////////////////////////////////////*/

  /**
   * @notice Register an amount of MKR to be used for eligibility verification for an `_ecosystemActor`. The
   * `_ecosystemActor` must have at least as much MKR as the registration `_amount`. Can only be called by a wearer of
   * the facilitator hat.
   * @param _ecosystemActor The ecosystem actor to register MKR for.
   * @param _amount The amount of MKR to register. Must be <= the ecosystem actor's MKR balance.
   * @param _message Raw string representation of the recognition submission message required by
   * [MIP113-5.2.1.2.3](https://mips.makerdao.com/mips/details/MIP113#5-2-1-2-3).
   * @param _sig An EIP-191-compatible signature by `_ecosystemActor` of the EIP-191-compatible hash of `_message`.
   * @custom:version Next version should use EIP712 signatures
   */
  function registerMKRFor(address _ecosystemActor, uint256 _amount, string calldata _message, bytes calldata _sig)
    public
  {
    // only the facilitator can register MKR for an ecosystem actor
    if (!HATS.isWearerOfHat(msg.sender, FACILITATOR_HAT)) revert Unauthorized();

    // verify the signature
    if (!_verifySig(_ecosystemActor, _message, _sig)) revert InvalidSignature();

    // check balance and register the MKR
    _registerMKR(_ecosystemActor, _amount, _message);
  }

  /*//////////////////////////////////////////////////////////////
                          VIEW FUNCTIONS
  //////////////////////////////////////////////////////////////*/

  /**
   * @notice Get the amount of verified MKR an ecosystem actor. An ecosystem actor's verified MKR is the amount of MKR
   * they have registered with this contract, as long as their present MKR balance is greater than or equal to that
   * amount. If their balance is lower than their registered amount, their verified amount is 0.
   * @param _ecosystemActor The ecosystem actor to check.
   * @return verifiedAmount The amount of verified MKR the ecosystem actor has.
   */
  function getVerifiedMKR(address _ecosystemActor) public view returns (uint256 verifiedAmount) {
    uint256 registeredAmount = registeredMKR[_ecosystemActor];

    // set verified amount to registered amount if their balance covers the registered amount
    // otherwise, verified amount remains 0 (as initialized)
    if (MKR.balanceOf(_ecosystemActor) >= registeredAmount) verifiedAmount = registeredAmount;
  }

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

  /**
   * @dev Check an `ecosystemActor`'s MKR balance and register the MKR if they have enough
   * @param _ecosystemActor The ecosystem actor to register MKR for.
   * @param _amount The amount of MKR to register. Must be <= the ecosystem actor's MKR balance.
   */
  function _registerMKR(address _ecosystemActor, uint256 _amount, string calldata _message) internal {
    // the ecosystem actor must have at least as much MKR as they are trying to register
    if (MKR.balanceOf(_ecosystemActor) < _amount) revert InsufficientMKR();

    // set the ecosystem actor's registered amount
    registeredMKR[_ecosystemActor] = _amount;

    // log the registration
    emit MKRRegistered(_ecosystemActor, _amount, _message);
  }

  /**
   * @dev Verify whether `_sig` is a valid EIP-191 signature of `_message` by `_ecosystemActor`. First converts the
   * `_message` to an EIP-191-compatible message hash, then checks the signature against that hash.
   * @param _ecosystemActor The ecosystem actor who signed the message.
   * @param _message A raw string message.
   * @param _sig An EIP-191-compatible signature by `_ecosystemActor` of the EIP-191-compatible hash of `_message`.
   */
  function _verifySig(address _ecosystemActor, string calldata _message, bytes calldata _sig)
    internal
    view
    returns (bool)
  {
    return SignatureCheckerLib.isValidSignatureNowCalldata(
      _ecosystemActor, SignatureCheckerLib.toEthSignedMessageHash(abi.encodePacked(_message)), _sig
    );
  }
}

File 2 of 7 : IHatsEligibility.sol
// SPDX-License-Identifier: AGPL-3.0
// Copyright (C) 2023 Haberdasher Labs
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <https://www.gnu.org/licenses/>.

pragma solidity >=0.8.13;

interface IHatsEligibility {
    /// @notice Returns the status of a wearer for a given hat
    /// @dev If standing is false, eligibility MUST also be false
    /// @param _wearer The address of the current or prospective Hat wearer
    /// @param _hatId The id of the hat in question
    /// @return eligible Whether the _wearer is eligible to wear the hat
    /// @return standing Whether the _wearer is in goog standing
    function getWearerStatus(address _wearer, uint256 _hatId) external view returns (bool eligible, bool standing);
}

File 3 of 7 : IHats.sol
// SPDX-License-Identifier: AGPL-3.0
// Copyright (C) 2023 Haberdasher Labs
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <https://www.gnu.org/licenses/>.

pragma solidity >=0.8.13;

import "./IHatsIdUtilities.sol";
import "./HatsErrors.sol";
import "./HatsEvents.sol";

interface IHats is IHatsIdUtilities, HatsErrors, HatsEvents {
    function mintTopHat(address _target, string memory _details, string memory _imageURI)
        external
        returns (uint256 topHatId);

    function createHat(
        uint256 _admin,
        string calldata _details,
        uint32 _maxSupply,
        address _eligibility,
        address _toggle,
        bool _mutable,
        string calldata _imageURI
    ) external returns (uint256 newHatId);

    function batchCreateHats(
        uint256[] calldata _admins,
        string[] calldata _details,
        uint32[] calldata _maxSupplies,
        address[] memory _eligibilityModules,
        address[] memory _toggleModules,
        bool[] calldata _mutables,
        string[] calldata _imageURIs
    ) external returns (bool success);

    function getNextId(uint256 _admin) external view returns (uint256 nextId);

    function mintHat(uint256 _hatId, address _wearer) external returns (bool success);

    function batchMintHats(uint256[] calldata _hatIds, address[] calldata _wearers) external returns (bool success);

    function setHatStatus(uint256 _hatId, bool _newStatus) external returns (bool toggled);

    function checkHatStatus(uint256 _hatId) external returns (bool toggled);

    function setHatWearerStatus(uint256 _hatId, address _wearer, bool _eligible, bool _standing)
        external
        returns (bool updated);

    function checkHatWearerStatus(uint256 _hatId, address _wearer) external returns (bool updated);

    function renounceHat(uint256 _hatId) external;

    function transferHat(uint256 _hatId, address _from, address _to) external;

    /*//////////////////////////////////////////////////////////////
                              HATS ADMIN FUNCTIONS
    //////////////////////////////////////////////////////////////*/

    function makeHatImmutable(uint256 _hatId) external;

    function changeHatDetails(uint256 _hatId, string memory _newDetails) external;

    function changeHatEligibility(uint256 _hatId, address _newEligibility) external;

    function changeHatToggle(uint256 _hatId, address _newToggle) external;

    function changeHatImageURI(uint256 _hatId, string memory _newImageURI) external;

    function changeHatMaxSupply(uint256 _hatId, uint32 _newMaxSupply) external;

    function requestLinkTopHatToTree(uint32 _topHatId, uint256 _newAdminHat) external;

    function approveLinkTopHatToTree(
        uint32 _topHatId,
        uint256 _newAdminHat,
        address _eligibility,
        address _toggle,
        string calldata _details,
        string calldata _imageURI
    ) external;

    function unlinkTopHatFromTree(uint32 _topHatId, address _wearer) external;

    function relinkTopHatWithinTree(
        uint32 _topHatDomain,
        uint256 _newAdminHat,
        address _eligibility,
        address _toggle,
        string calldata _details,
        string calldata _imageURI
    ) external;

    /*//////////////////////////////////////////////////////////////
                              VIEW FUNCTIONS
    //////////////////////////////////////////////////////////////*/

    function viewHat(uint256 _hatId)
        external
        view
        returns (
            string memory details,
            uint32 maxSupply,
            uint32 supply,
            address eligibility,
            address toggle,
            string memory imageURI,
            uint16 lastHatId,
            bool mutable_,
            bool active
        );

    function isWearerOfHat(address _user, uint256 _hatId) external view returns (bool isWearer);

    function isAdminOfHat(address _user, uint256 _hatId) external view returns (bool isAdmin);

    function isInGoodStanding(address _wearer, uint256 _hatId) external view returns (bool standing);

    function isEligible(address _wearer, uint256 _hatId) external view returns (bool eligible);

    function getHatEligibilityModule(uint256 _hatId) external view returns (address eligibility);

    function getHatToggleModule(uint256 _hatId) external view returns (address toggle);

    function getHatMaxSupply(uint256 _hatId) external view returns (uint32 maxSupply);

    function hatSupply(uint256 _hatId) external view returns (uint32 supply);

    function getImageURIForHat(uint256 _hatId) external view returns (string memory _uri);

    function balanceOf(address wearer, uint256 hatId) external view returns (uint256 balance);

    function balanceOfBatch(address[] calldata _wearers, uint256[] calldata _hatIds)
        external
        view
        returns (uint256[] memory);

    function uri(uint256 id) external view returns (string memory _uri);
}

File 4 of 7 : SignatureCheckerLib.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

/// @notice Signature verification helper that supports both ECDSA signatures from EOAs
/// and ERC1271 signatures from smart contract wallets like Argent and Gnosis safe.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/SignatureCheckerLib.sol)
/// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/cryptography/SignatureChecker.sol)
///
/// @dev Note:
/// - The signature checking functions use the ecrecover precompile (0x1).
/// - The `bytes memory signature` variants use the identity precompile (0x4)
///   to copy memory internally.
/// - Unlike ECDSA signatures, contract signatures are revocable.
///
/// WARNING! Do NOT use signatures as unique identifiers.
/// Please use EIP712 with a nonce included in the digest to prevent replay attacks.
/// This implementation does NOT check if a signature is non-malleable.
library SignatureCheckerLib {
    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*               SIGNATURE CHECKING OPERATIONS                */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Returns whether `signature` is valid for `signer` and `hash`.
    /// If `signer` is a smart contract, the signature is validated with ERC1271.
    /// Otherwise, the signature is validated with `ECDSA.recover`.
    function isValidSignatureNow(address signer, bytes32 hash, bytes memory signature)
        internal
        view
        returns (bool isValid)
    {
        /// @solidity memory-safe-assembly
        assembly {
            // Clean the upper 96 bits of `signer` in case they are dirty.
            for { signer := shr(96, shl(96, signer)) } signer {} {
                let m := mload(0x40)
                if eq(mload(signature), 65) {
                    mstore(0x00, hash)
                    mstore(0x20, byte(0, mload(add(signature, 0x60)))) // `v`.
                    mstore(0x40, mload(add(signature, 0x20))) // `r`.
                    mstore(0x60, mload(add(signature, 0x40))) // `s`.
                    let t :=
                        staticcall(
                            gas(), // Amount of gas left for the transaction.
                            1, // Address of `ecrecover`.
                            0x00, // Start of input.
                            0x80, // Size of input.
                            0x01, // Start of output.
                            0x20 // Size of output.
                        )
                    // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise.
                    if iszero(or(iszero(returndatasize()), xor(signer, mload(t)))) {
                        isValid := 1
                        mstore(0x60, 0) // Restore the zero slot.
                        mstore(0x40, m) // Restore the free memory pointer.
                        break
                    }
                }
                mstore(0x60, 0) // Restore the zero slot.
                mstore(0x40, m) // Restore the free memory pointer.

                let f := shl(224, 0x1626ba7e)
                mstore(m, f) // `bytes4(keccak256("isValidSignature(bytes32,bytes)"))`.
                mstore(add(m, 0x04), hash)
                let d := add(m, 0x24)
                mstore(d, 0x40) // The offset of the `signature` in the calldata.
                // Copy the `signature` over.
                let n := add(0x20, mload(signature))
                pop(staticcall(gas(), 4, signature, n, add(m, 0x44), n))
                // forgefmt: disable-next-item
                isValid := and(
                    // Whether the returndata is the magic value `0x1626ba7e` (left-aligned).
                    eq(mload(d), f),
                    // Whether the staticcall does not revert.
                    // This must be placed at the end of the `and` clause,
                    // as the arguments are evaluated from right to left.
                    staticcall(
                        gas(), // Remaining gas.
                        signer, // The `signer` address.
                        m, // Offset of calldata in memory.
                        add(returndatasize(), 0x44), // Length of calldata in memory.
                        d, // Offset of returndata.
                        0x20 // Length of returndata to write.
                    )
                )
                break
            }
        }
    }

    /// @dev Returns whether `signature` is valid for `signer` and `hash`.
    /// If `signer` is a smart contract, the signature is validated with ERC1271.
    /// Otherwise, the signature is validated with `ECDSA.recover`.
    function isValidSignatureNowCalldata(address signer, bytes32 hash, bytes calldata signature)
        internal
        view
        returns (bool isValid)
    {
        /// @solidity memory-safe-assembly
        assembly {
            // Clean the upper 96 bits of `signer` in case they are dirty.
            for { signer := shr(96, shl(96, signer)) } signer {} {
                let m := mload(0x40)
                if eq(signature.length, 65) {
                    mstore(0x00, hash)
                    mstore(0x20, byte(0, calldataload(add(signature.offset, 0x40)))) // `v`.
                    calldatacopy(0x40, signature.offset, 0x40) // `r`, `s`.
                    let t :=
                        staticcall(
                            gas(), // Amount of gas left for the transaction.
                            1, // Address of `ecrecover`.
                            0x00, // Start of input.
                            0x80, // Size of input.
                            0x01, // Start of output.
                            0x20 // Size of output.
                        )
                    // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise.
                    if iszero(or(iszero(returndatasize()), xor(signer, mload(t)))) {
                        isValid := 1
                        mstore(0x60, 0) // Restore the zero slot.
                        mstore(0x40, m) // Restore the free memory pointer.
                        break
                    }
                }
                mstore(0x60, 0) // Restore the zero slot.
                mstore(0x40, m) // Restore the free memory pointer.

                let f := shl(224, 0x1626ba7e)
                mstore(m, f) // `bytes4(keccak256("isValidSignature(bytes32,bytes)"))`.
                mstore(add(m, 0x04), hash)
                let d := add(m, 0x24)
                mstore(d, 0x40) // The offset of the `signature` in the calldata.
                mstore(add(m, 0x44), signature.length)
                // Copy the `signature` over.
                calldatacopy(add(m, 0x64), signature.offset, signature.length)
                // forgefmt: disable-next-item
                isValid := and(
                    // Whether the returndata is the magic value `0x1626ba7e` (left-aligned).
                    eq(mload(d), f),
                    // Whether the staticcall does not revert.
                    // This must be placed at the end of the `and` clause,
                    // as the arguments are evaluated from right to left.
                    staticcall(
                        gas(), // Remaining gas.
                        signer, // The `signer` address.
                        m, // Offset of calldata in memory.
                        add(signature.length, 0x64), // Length of calldata in memory.
                        d, // Offset of returndata.
                        0x20 // Length of returndata to write.
                    )
                )
                break
            }
        }
    }

    /// @dev Returns whether the signature (`r`, `vs`) is valid for `signer` and `hash`.
    /// If `signer` is a smart contract, the signature is validated with ERC1271.
    /// Otherwise, the signature is validated with `ECDSA.recover`.
    function isValidSignatureNow(address signer, bytes32 hash, bytes32 r, bytes32 vs)
        internal
        view
        returns (bool isValid)
    {
        /// @solidity memory-safe-assembly
        assembly {
            // Clean the upper 96 bits of `signer` in case they are dirty.
            for { signer := shr(96, shl(96, signer)) } signer {} {
                let m := mload(0x40)
                mstore(0x00, hash)
                mstore(0x20, add(shr(255, vs), 27)) // `v`.
                mstore(0x40, r) // `r`.
                mstore(0x60, shr(1, shl(1, vs))) // `s`.
                let t :=
                    staticcall(
                        gas(), // Amount of gas left for the transaction.
                        1, // Address of `ecrecover`.
                        0x00, // Start of input.
                        0x80, // Size of input.
                        0x01, // Start of output.
                        0x20 // Size of output.
                    )
                // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise.
                if iszero(or(iszero(returndatasize()), xor(signer, mload(t)))) {
                    isValid := 1
                    mstore(0x60, 0) // Restore the zero slot.
                    mstore(0x40, m) // Restore the free memory pointer.
                    break
                }

                let f := shl(224, 0x1626ba7e)
                mstore(m, f) // `bytes4(keccak256("isValidSignature(bytes32,bytes)"))`.
                mstore(add(m, 0x04), hash)
                let d := add(m, 0x24)
                mstore(d, 0x40) // The offset of the `signature` in the calldata.
                mstore(add(m, 0x44), 65) // Length of the signature.
                mstore(add(m, 0x64), r) // `r`.
                mstore(add(m, 0x84), mload(0x60)) // `s`.
                mstore8(add(m, 0xa4), mload(0x20)) // `v`.
                // forgefmt: disable-next-item
                isValid := and(
                    // Whether the returndata is the magic value `0x1626ba7e` (left-aligned).
                    eq(mload(d), f),
                    // Whether the staticcall does not revert.
                    // This must be placed at the end of the `and` clause,
                    // as the arguments are evaluated from right to left.
                    staticcall(
                        gas(), // Remaining gas.
                        signer, // The `signer` address.
                        m, // Offset of calldata in memory.
                        0xa5, // Length of calldata in memory.
                        d, // Offset of returndata.
                        0x20 // Length of returndata to write.
                    )
                )
                mstore(0x60, 0) // Restore the zero slot.
                mstore(0x40, m) // Restore the free memory pointer.
                break
            }
        }
    }

    /// @dev Returns whether the signature (`v`, `r`, `s`) is valid for `signer` and `hash`.
    /// If `signer` is a smart contract, the signature is validated with ERC1271.
    /// Otherwise, the signature is validated with `ECDSA.recover`.
    function isValidSignatureNow(address signer, bytes32 hash, uint8 v, bytes32 r, bytes32 s)
        internal
        view
        returns (bool isValid)
    {
        /// @solidity memory-safe-assembly
        assembly {
            // Clean the upper 96 bits of `signer` in case they are dirty.
            for { signer := shr(96, shl(96, signer)) } signer {} {
                let m := mload(0x40)
                mstore(0x00, hash)
                mstore(0x20, and(v, 0xff)) // `v`.
                mstore(0x40, r) // `r`.
                mstore(0x60, s) // `s`.
                let t :=
                    staticcall(
                        gas(), // Amount of gas left for the transaction.
                        1, // Address of `ecrecover`.
                        0x00, // Start of input.
                        0x80, // Size of input.
                        0x01, // Start of output.
                        0x20 // Size of output.
                    )
                // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise.
                if iszero(or(iszero(returndatasize()), xor(signer, mload(t)))) {
                    isValid := 1
                    mstore(0x60, 0) // Restore the zero slot.
                    mstore(0x40, m) // Restore the free memory pointer.
                    break
                }

                let f := shl(224, 0x1626ba7e)
                mstore(m, f) // `bytes4(keccak256("isValidSignature(bytes32,bytes)"))`.
                mstore(add(m, 0x04), hash)
                let d := add(m, 0x24)
                mstore(d, 0x40) // The offset of the `signature` in the calldata.
                mstore(add(m, 0x44), 65) // Length of the signature.
                mstore(add(m, 0x64), r) // `r`.
                mstore(add(m, 0x84), s) // `s`.
                mstore8(add(m, 0xa4), v) // `v`.
                // forgefmt: disable-next-item
                isValid := and(
                    // Whether the returndata is the magic value `0x1626ba7e` (left-aligned).
                    eq(mload(d), f),
                    // Whether the staticcall does not revert.
                    // This must be placed at the end of the `and` clause,
                    // as the arguments are evaluated from right to left.
                    staticcall(
                        gas(), // Remaining gas.
                        signer, // The `signer` address.
                        m, // Offset of calldata in memory.
                        0xa5, // Length of calldata in memory.
                        d, // Offset of returndata.
                        0x20 // Length of returndata to write.
                    )
                )
                mstore(0x60, 0) // Restore the zero slot.
                mstore(0x40, m) // Restore the free memory pointer.
                break
            }
        }
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                     ERC1271 OPERATIONS                     */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Returns whether `signature` is valid for `hash`
    /// for an ERC1271 `signer` contract.
    function isValidERC1271SignatureNow(address signer, bytes32 hash, bytes memory signature)
        internal
        view
        returns (bool isValid)
    {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40)
            let f := shl(224, 0x1626ba7e)
            mstore(m, f) // `bytes4(keccak256("isValidSignature(bytes32,bytes)"))`.
            mstore(add(m, 0x04), hash)
            let d := add(m, 0x24)
            mstore(d, 0x40) // The offset of the `signature` in the calldata.
            // Copy the `signature` over.
            let n := add(0x20, mload(signature))
            pop(staticcall(gas(), 4, signature, n, add(m, 0x44), n))
            // forgefmt: disable-next-item
            isValid := and(
                // Whether the returndata is the magic value `0x1626ba7e` (left-aligned).
                eq(mload(d), f),
                // Whether the staticcall does not revert.
                // This must be placed at the end of the `and` clause,
                // as the arguments are evaluated from right to left.
                staticcall(
                    gas(), // Remaining gas.
                    signer, // The `signer` address.
                    m, // Offset of calldata in memory.
                    add(returndatasize(), 0x44), // Length of calldata in memory.
                    d, // Offset of returndata.
                    0x20 // Length of returndata to write.
                )
            )
        }
    }

    /// @dev Returns whether `signature` is valid for `hash`
    /// for an ERC1271 `signer` contract.
    function isValidERC1271SignatureNowCalldata(
        address signer,
        bytes32 hash,
        bytes calldata signature
    ) internal view returns (bool isValid) {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40)
            let f := shl(224, 0x1626ba7e)
            mstore(m, f) // `bytes4(keccak256("isValidSignature(bytes32,bytes)"))`.
            mstore(add(m, 0x04), hash)
            let d := add(m, 0x24)
            mstore(d, 0x40) // The offset of the `signature` in the calldata.
            mstore(add(m, 0x44), signature.length)
            // Copy the `signature` over.
            calldatacopy(add(m, 0x64), signature.offset, signature.length)
            // forgefmt: disable-next-item
            isValid := and(
                // Whether the returndata is the magic value `0x1626ba7e` (left-aligned).
                eq(mload(d), f),
                // Whether the staticcall does not revert.
                // This must be placed at the end of the `and` clause,
                // as the arguments are evaluated from right to left.
                staticcall(
                    gas(), // Remaining gas.
                    signer, // The `signer` address.
                    m, // Offset of calldata in memory.
                    add(signature.length, 0x64), // Length of calldata in memory.
                    d, // Offset of returndata.
                    0x20 // Length of returndata to write.
                )
            )
        }
    }

    /// @dev Returns whether the signature (`r`, `vs`) is valid for `hash`
    /// for an ERC1271 `signer` contract.
    function isValidERC1271SignatureNow(address signer, bytes32 hash, bytes32 r, bytes32 vs)
        internal
        view
        returns (bool isValid)
    {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40)
            let f := shl(224, 0x1626ba7e)
            mstore(m, f) // `bytes4(keccak256("isValidSignature(bytes32,bytes)"))`.
            mstore(add(m, 0x04), hash)
            let d := add(m, 0x24)
            mstore(d, 0x40) // The offset of the `signature` in the calldata.
            mstore(add(m, 0x44), 65) // Length of the signature.
            mstore(add(m, 0x64), r) // `r`.
            mstore(add(m, 0x84), shr(1, shl(1, vs))) // `s`.
            mstore8(add(m, 0xa4), add(shr(255, vs), 27)) // `v`.
            // forgefmt: disable-next-item
            isValid := and(
                // Whether the returndata is the magic value `0x1626ba7e` (left-aligned).
                eq(mload(d), f),
                // Whether the staticcall does not revert.
                // This must be placed at the end of the `and` clause,
                // as the arguments are evaluated from right to left.
                staticcall(
                    gas(), // Remaining gas.
                    signer, // The `signer` address.
                    m, // Offset of calldata in memory.
                    0xa5, // Length of calldata in memory.
                    d, // Offset of returndata.
                    0x20 // Length of returndata to write.
                )
            )
        }
    }

    /// @dev Returns whether the signature (`v`, `r`, `s`) is valid for `hash`
    /// for an ERC1271 `signer` contract.
    function isValidERC1271SignatureNow(address signer, bytes32 hash, uint8 v, bytes32 r, bytes32 s)
        internal
        view
        returns (bool isValid)
    {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40)
            let f := shl(224, 0x1626ba7e)
            mstore(m, f) // `bytes4(keccak256("isValidSignature(bytes32,bytes)"))`.
            mstore(add(m, 0x04), hash)
            let d := add(m, 0x24)
            mstore(d, 0x40) // The offset of the `signature` in the calldata.
            mstore(add(m, 0x44), 65) // Length of the signature.
            mstore(add(m, 0x64), r) // `r`.
            mstore(add(m, 0x84), s) // `s`.
            mstore8(add(m, 0xa4), v) // `v`.
            // forgefmt: disable-next-item
            isValid := and(
                // Whether the returndata is the magic value `0x1626ba7e` (left-aligned).
                eq(mload(d), f),
                // Whether the staticcall does not revert.
                // This must be placed at the end of the `and` clause,
                // as the arguments are evaluated from right to left.
                staticcall(
                    gas(), // Remaining gas.
                    signer, // The `signer` address.
                    m, // Offset of calldata in memory.
                    0xa5, // Length of calldata in memory.
                    d, // Offset of returndata.
                    0x20 // Length of returndata to write.
                )
            )
        }
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                     HASHING OPERATIONS                     */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Returns an Ethereum Signed Message, created from a `hash`.
    /// This produces a hash corresponding to the one signed with the
    /// [`eth_sign`](https://eth.wiki/json-rpc/API#eth_sign)
    /// JSON-RPC method as part of EIP-191.
    function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32 result) {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x20, hash) // Store into scratch space for keccak256.
            mstore(0x00, "\x00\x00\x00\x00\x19Ethereum Signed Message:\n32") // 28 bytes.
            result := keccak256(0x04, 0x3c) // `32 * 2 - (32 - 28) = 60 = 0x3c`.
        }
    }

    /// @dev Returns an Ethereum Signed Message, created from `s`.
    /// This produces a hash corresponding to the one signed with the
    /// [`eth_sign`](https://eth.wiki/json-rpc/API#eth_sign)
    /// JSON-RPC method as part of EIP-191.
    /// Note: Supports lengths of `s` up to 999999 bytes.
    function toEthSignedMessageHash(bytes memory s) internal pure returns (bytes32 result) {
        /// @solidity memory-safe-assembly
        assembly {
            let sLength := mload(s)
            let o := 0x20
            mstore(o, "\x19Ethereum Signed Message:\n") // 26 bytes, zero-right-padded.
            mstore(0x00, 0x00)
            // Convert the `s.length` to ASCII decimal representation: `base10(s.length)`.
            for { let temp := sLength } 1 {} {
                o := sub(o, 1)
                mstore8(o, add(48, mod(temp, 10)))
                temp := div(temp, 10)
                if iszero(temp) { break }
            }
            let n := sub(0x3a, o) // Header length: `26 + 32 - o`.
            // Throw an out-of-offset error (consumes all gas) if the header exceeds 32 bytes.
            returndatacopy(returndatasize(), returndatasize(), gt(n, 0x20))
            mstore(s, or(mload(0x00), mload(n))) // Temporarily store the header.
            result := keccak256(add(s, sub(0x20, n)), add(n, sLength))
            mstore(s, sLength) // Restore the length.
        }
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                   EMPTY CALLDATA HELPERS                   */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Returns an empty calldata bytes.
    function emptySignature() internal pure returns (bytes calldata signature) {
        /// @solidity memory-safe-assembly
        assembly {
            signature.length := 0
        }
    }
}

File 5 of 7 : IHatsIdUtilities.sol
// SPDX-License-Identifier: AGPL-3.0
// Copyright (C) 2023 Haberdasher Labs
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <https://www.gnu.org/licenses/>.

pragma solidity >=0.8.13;

interface IHatsIdUtilities {
    function buildHatId(uint256 _admin, uint16 _newHat) external pure returns (uint256 id);

    function getHatLevel(uint256 _hatId) external view returns (uint32 level);

    function getLocalHatLevel(uint256 _hatId) external pure returns (uint32 level);

    function isTopHat(uint256 _hatId) external view returns (bool _topHat);

    function isLocalTopHat(uint256 _hatId) external pure returns (bool _localTopHat);

    function isValidHatId(uint256 _hatId) external view returns (bool validHatId);

    function getAdminAtLevel(uint256 _hatId, uint32 _level) external view returns (uint256 admin);

    function getAdminAtLocalLevel(uint256 _hatId, uint32 _level) external pure returns (uint256 admin);

    function getTopHatDomain(uint256 _hatId) external view returns (uint32 domain);

    function getTippyTopHatDomain(uint32 _topHatDomain) external view returns (uint32 domain);

    function noCircularLinkage(uint32 _topHatDomain, uint256 _linkedAdmin) external view returns (bool notCircular);

    function sameTippyTopHatDomain(uint32 _topHatDomain, uint256 _newAdminHat)
        external
        view
        returns (bool sameDomain);
}

File 6 of 7 : HatsErrors.sol
// SPDX-License-Identifier: AGPL-3.0
// Copyright (C) 2023 Haberdasher Labs
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <https://www.gnu.org/licenses/>.

pragma solidity >=0.8.13;

interface HatsErrors {
    /// @notice Emitted when `user` is attempting to perform an action on `hatId` but is not wearing one of `hatId`'s admin hats
    /// @dev Can be equivalent to `NotHatWearer(buildHatId(hatId))`, such as when emitted by `approveLinkTopHatToTree` or `relinkTopHatToTree`
    error NotAdmin(address user, uint256 hatId);

    /// @notice Emitted when attempting to perform an action as or for an account that is not a wearer of a given hat
    error NotHatWearer();

    /// @notice Emitted when attempting to perform an action that requires being either an admin or wearer of a given hat
    error NotAdminOrWearer();

    /// @notice Emitted when attempting to mint `hatId` but `hatId`'s maxSupply has been reached
    error AllHatsWorn(uint256 hatId);

    /// @notice Emitted when attempting to create a hat with a level 14 hat as its admin
    error MaxLevelsReached();

    /// @notice Emitted when an attempted hat id has empty intermediate level(s)
    error InvalidHatId();

    /// @notice Emitted when attempting to mint `hatId` to a `wearer` who is already wearing the hat
    error AlreadyWearingHat(address wearer, uint256 hatId);

    /// @notice Emitted when attempting to mint a non-existant hat
    error HatDoesNotExist(uint256 hatId);

    /// @notice Emmitted when attempting to mint or transfer a hat that is not active
    error HatNotActive();

    /// @notice Emitted when attempting to mint or transfer a hat to an ineligible wearer
    error NotEligible();

    /// @notice Emitted when attempting to check or set a hat's status from an account that is not that hat's toggle module
    error NotHatsToggle();

    /// @notice Emitted when attempting to check or set a hat wearer's status from an account that is not that hat's eligibility module
    error NotHatsEligibility();

    /// @notice Emitted when array arguments to a batch function have mismatching lengths
    error BatchArrayLengthMismatch();

    /// @notice Emitted when attempting to mutate or transfer an immutable hat
    error Immutable();

    /// @notice Emitted when attempting to change a hat's maxSupply to a value lower than its current supply
    error NewMaxSupplyTooLow();

    /// @notice Emitted when attempting to link a tophat to a new admin for which the tophat serves as an admin
    error CircularLinkage();

    /// @notice Emitted when attempting to link or relink a tophat to a separate tree
    error CrossTreeLinkage();

    /// @notice Emitted when attempting to link a tophat without a request
    error LinkageNotRequested();

    /// @notice Emitted when attempting to unlink a tophat that does not have a wearer
    /// @dev This ensures that unlinking never results in a bricked tophat
    error InvalidUnlink();

    /// @notice Emmited when attempting to change a hat's eligibility or toggle module to the zero address
    error ZeroAddress();

    /// @notice Emmitted when attempting to change a hat's details or imageURI to a string with over 7000 bytes (~characters)
    /// @dev This protects against a DOS attack where an admin iteratively extend's a hat's details or imageURI
    ///      to be so long that reading it exceeds the block gas limit, breaking `uri()` and `viewHat()`
    error StringTooLong();
}

File 7 of 7 : HatsEvents.sol
// SPDX-License-Identifier: AGPL-3.0
// Copyright (C) 2023 Haberdasher Labs
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <https://www.gnu.org/licenses/>.

pragma solidity >=0.8.13;

interface HatsEvents {
    /// @notice Emitted when a new hat is created
    /// @param id The id for the new hat
    /// @param details A description of the Hat
    /// @param maxSupply The total instances of the Hat that can be worn at once
    /// @param eligibility The address that can report on the Hat wearer's status
    /// @param toggle The address that can deactivate the Hat
    /// @param mutable_ Whether the hat's properties are changeable after creation
    /// @param imageURI The image uri for this hat and the fallback for its
    event HatCreated(
        uint256 id,
        string details,
        uint32 maxSupply,
        address eligibility,
        address toggle,
        bool mutable_,
        string imageURI
    );

    /// @notice Emitted when a hat wearer's standing is updated
    /// @dev Eligibility is excluded since the source of truth for eligibility is the eligibility module and may change without a transaction
    /// @param hatId The id of the wearer's hat
    /// @param wearer The wearer's address
    /// @param wearerStanding Whether the wearer is in good standing for the hat
    event WearerStandingChanged(uint256 hatId, address wearer, bool wearerStanding);

    /// @notice Emitted when a hat's status is updated
    /// @param hatId The id of the hat
    /// @param newStatus Whether the hat is active
    event HatStatusChanged(uint256 hatId, bool newStatus);

    /// @notice Emitted when a hat's details are updated
    /// @param hatId The id of the hat
    /// @param newDetails The updated details
    event HatDetailsChanged(uint256 hatId, string newDetails);

    /// @notice Emitted when a hat's eligibility module is updated
    /// @param hatId The id of the hat
    /// @param newEligibility The updated eligibiliy module
    event HatEligibilityChanged(uint256 hatId, address newEligibility);

    /// @notice Emitted when a hat's toggle module is updated
    /// @param hatId The id of the hat
    /// @param newToggle The updated toggle module
    event HatToggleChanged(uint256 hatId, address newToggle);

    /// @notice Emitted when a hat's mutability is updated
    /// @param hatId The id of the hat
    event HatMutabilityChanged(uint256 hatId);

    /// @notice Emitted when a hat's maximum supply is updated
    /// @param hatId The id of the hat
    /// @param newMaxSupply The updated max supply
    event HatMaxSupplyChanged(uint256 hatId, uint32 newMaxSupply);

    /// @notice Emitted when a hat's image URI is updated
    /// @param hatId The id of the hat
    /// @param newImageURI The updated image URI
    event HatImageURIChanged(uint256 hatId, string newImageURI);

    /// @notice Emitted when a tophat linkage is requested by its admin
    /// @param domain The domain of the tree tophat to link
    /// @param newAdmin The tophat's would-be admin in the parent tree
    event TopHatLinkRequested(uint32 domain, uint256 newAdmin);

    /// @notice Emitted when a tophat is linked to a another tree
    /// @param domain The domain of the newly-linked tophat
    /// @param newAdmin The tophat's new admin in the parent tree
    event TopHatLinked(uint32 domain, uint256 newAdmin);
}

Settings
{
  "remappings": [
    "ds-test/=lib/forge-std/lib/ds-test/src/",
    "forge-std/=lib/forge-std/src/",
    "hats-protocol/=lib/hats-protocol/src/",
    "solady/=lib/solady/src/",
    "ERC1155/=lib/hats-protocol/lib/ERC1155/",
    "solbase/=lib/hats-protocol/lib/solbase/src/",
    "utils/=lib/hats-protocol/lib/utils/"
  ],
  "optimizer": {
    "enabled": true,
    "runs": 1000000
  },
  "metadata": {
    "useLiteralContent": false,
    "bytecodeHash": "none",
    "appendCBOR": true
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "evmVersion": "shanghai",
  "libraries": {}
}

Contract Security Audit

Contract ABI

[{"inputs":[{"internalType":"address","name":"_MKR","type":"address"},{"internalType":"uint256","name":"_facilitatorHat","type":"uint256"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"InsufficientMKR","type":"error"},{"inputs":[],"name":"InvalidSignature","type":"error"},{"inputs":[],"name":"Unauthorized","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"ecosystemActor","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"string","name":"message","type":"string"}],"name":"MKRRegistered","type":"event"},{"inputs":[],"name":"FACILITATOR_HAT","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"HATS","outputs":[{"internalType":"contract IHats","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MKR","outputs":[{"internalType":"contract ERC20Like","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_ecosystemActor","type":"address"}],"name":"getVerifiedMKR","outputs":[{"internalType":"uint256","name":"verifiedAmount","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_wearer","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"getWearerStatus","outputs":[{"internalType":"bool","name":"eligible","type":"bool"},{"internalType":"bool","name":"standing","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amount","type":"uint256"},{"internalType":"string","name":"_message","type":"string"}],"name":"registerMKR","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_ecosystemActor","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"},{"internalType":"string","name":"_message","type":"string"},{"internalType":"bytes","name":"_sig","type":"bytes"}],"name":"registerMKRFor","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"ecosystemActor","type":"address"}],"name":"registeredMKR","outputs":[{"internalType":"uint256","name":"registeredAmount","type":"uint256"}],"stateMutability":"view","type":"function"}]

60c060405234801561000f575f80fd5b5060405161098838038061098883398101604081905261002e91610044565b6001600160a01b0390911660805260a05261007b565b5f8060408385031215610055575f80fd5b82516001600160a01b038116811461006b575f80fd5b6020939093015192949293505050565b60805160a0516108d86100b05f395f818161018c01526101ed01525f818160a301528181610358015261042e01526108d85ff3fe608060405234801561000f575f80fd5b5060043610610085575f3560e01c80637150ef88116100585780637150ef881461012f578063739586aa1461014a578063bd6838721461015d578063bef59a0c14610187575f80fd5b8063102d282e146100895780633d31a4af1461009e57806341a49356146100ef57806358c655be1461011c575b5f80fd5b61009c6100973660046106da565b6101ae565b005b6100c57f000000000000000000000000000000000000000000000000000000000000000081565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020015b60405180910390f35b61010e6100fd36600461074a565b5f6020819052908152604090205481565b6040519081526020016100e6565b61009c61012a36600461076a565b6101bf565b6100c5733bc1a0ad72417f2d411118085256fc53cbddd13781565b61010e61015836600461074a565b6102fc565b61017061016b3660046107ee565b6103d0565b6040805192151583529015156020830152016100e6565b61010e7f000000000000000000000000000000000000000000000000000000000000000081565b6101ba338484846103e7565b505050565b6040517f4352409a0000000000000000000000000000000000000000000000000000000081523360048201527f00000000000000000000000000000000000000000000000000000000000000006024820152733bc1a0ad72417f2d411118085256fc53cbddd13790634352409a90604401602060405180830381865afa15801561024b573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061026f9190610816565b6102a5576040517f82b4290000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6102b2868585858561053b565b6102e8576040517f8baa579f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6102f4868686866103e7565b505050505050565b73ffffffffffffffffffffffffffffffffffffffff8181165f818152602081905260408082205490517f70a0823100000000000000000000000000000000000000000000000000000000815260048101939093529092909182917f000000000000000000000000000000000000000000000000000000000000000016906370a0823190602401602060405180830381865afa15801561039d573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906103c19190610835565b106103ca578091505b50919050565b5f6001816103dd856102fc565b1191509250929050565b6040517f70a0823100000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff858116600483015284917f0000000000000000000000000000000000000000000000000000000000000000909116906370a0823190602401602060405180830381865afa158015610475573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906104999190610835565b10156104d1576040517f62adb94f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff84165f9081526020819052604090819020849055517ea36578399109fd77650bd7a46592880be61181e7fc62cce6211c9c12d9aa449061052d90869086908690869061084c565b60405180910390a150505050565b5f6105718661056a87876040516020016105569291906108bc565b60405160208183030381529060405261057b565b85856105e4565b9695505050505050565b5f815160207f19457468657265756d205369676e6564204d6573736167653a0a00000000000081525f8052815b600182039150600a81066030018253600a9004806105a85750603a03602081113d3d3e80515f5117845281810160209190910384012092525090565b73ffffffffffffffffffffffffffffffffffffffff909316925f841561068d576040516041830361064a57845f5260408401355f1a6020526040846040376020600160805f60015afa805187183d151761064857505f60605260405250600161068d565b505b5f60605280604052631626ba7e60e01b80825285600483015260248201604081528460448401528486606485013760208160648701858b5afa9051909114169150505b949350505050565b5f8083601f8401126106a5575f80fd5b50813567ffffffffffffffff8111156106bc575f80fd5b6020830191508360208285010111156106d3575f80fd5b9250929050565b5f805f604084860312156106ec575f80fd5b83359250602084013567ffffffffffffffff811115610709575f80fd5b61071586828701610695565b9497909650939450505050565b803573ffffffffffffffffffffffffffffffffffffffff81168114610745575f80fd5b919050565b5f6020828403121561075a575f80fd5b61076382610722565b9392505050565b5f805f805f806080878903121561077f575f80fd5b61078887610722565b955060208701359450604087013567ffffffffffffffff808211156107ab575f80fd5b6107b78a838b01610695565b909650945060608901359150808211156107cf575f80fd5b506107dc89828a01610695565b979a9699509497509295939492505050565b5f80604083850312156107ff575f80fd5b61080883610722565b946020939093013593505050565b5f60208284031215610826575f80fd5b81518015158114610763575f80fd5b5f60208284031215610845575f80fd5b5051919050565b73ffffffffffffffffffffffffffffffffffffffff8516815283602082015260606040820152816060820152818360808301375f818301608090810191909152601f9092017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01601019392505050565b818382375f910190815291905056fea164736f6c6343000815000a0000000000000000000000009f8f72aa9304c8b593d555f12ef6589cc3a579a20000000c00010002000100000000000000000000000000000000000000000000

Deployed Bytecode

0x608060405234801561000f575f80fd5b5060043610610085575f3560e01c80637150ef88116100585780637150ef881461012f578063739586aa1461014a578063bd6838721461015d578063bef59a0c14610187575f80fd5b8063102d282e146100895780633d31a4af1461009e57806341a49356146100ef57806358c655be1461011c575b5f80fd5b61009c6100973660046106da565b6101ae565b005b6100c57f0000000000000000000000009f8f72aa9304c8b593d555f12ef6589cc3a579a281565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020015b60405180910390f35b61010e6100fd36600461074a565b5f6020819052908152604090205481565b6040519081526020016100e6565b61009c61012a36600461076a565b6101bf565b6100c5733bc1a0ad72417f2d411118085256fc53cbddd13781565b61010e61015836600461074a565b6102fc565b61017061016b3660046107ee565b6103d0565b6040805192151583529015156020830152016100e6565b61010e7f0000000c0001000200010000000000000000000000000000000000000000000081565b6101ba338484846103e7565b505050565b6040517f4352409a0000000000000000000000000000000000000000000000000000000081523360048201527f0000000c000100020001000000000000000000000000000000000000000000006024820152733bc1a0ad72417f2d411118085256fc53cbddd13790634352409a90604401602060405180830381865afa15801561024b573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061026f9190610816565b6102a5576040517f82b4290000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6102b2868585858561053b565b6102e8576040517f8baa579f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6102f4868686866103e7565b505050505050565b73ffffffffffffffffffffffffffffffffffffffff8181165f818152602081905260408082205490517f70a0823100000000000000000000000000000000000000000000000000000000815260048101939093529092909182917f0000000000000000000000009f8f72aa9304c8b593d555f12ef6589cc3a579a216906370a0823190602401602060405180830381865afa15801561039d573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906103c19190610835565b106103ca578091505b50919050565b5f6001816103dd856102fc565b1191509250929050565b6040517f70a0823100000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff858116600483015284917f0000000000000000000000009f8f72aa9304c8b593d555f12ef6589cc3a579a2909116906370a0823190602401602060405180830381865afa158015610475573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906104999190610835565b10156104d1576040517f62adb94f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff84165f9081526020819052604090819020849055517ea36578399109fd77650bd7a46592880be61181e7fc62cce6211c9c12d9aa449061052d90869086908690869061084c565b60405180910390a150505050565b5f6105718661056a87876040516020016105569291906108bc565b60405160208183030381529060405261057b565b85856105e4565b9695505050505050565b5f815160207f19457468657265756d205369676e6564204d6573736167653a0a00000000000081525f8052815b600182039150600a81066030018253600a9004806105a85750603a03602081113d3d3e80515f5117845281810160209190910384012092525090565b73ffffffffffffffffffffffffffffffffffffffff909316925f841561068d576040516041830361064a57845f5260408401355f1a6020526040846040376020600160805f60015afa805187183d151761064857505f60605260405250600161068d565b505b5f60605280604052631626ba7e60e01b80825285600483015260248201604081528460448401528486606485013760208160648701858b5afa9051909114169150505b949350505050565b5f8083601f8401126106a5575f80fd5b50813567ffffffffffffffff8111156106bc575f80fd5b6020830191508360208285010111156106d3575f80fd5b9250929050565b5f805f604084860312156106ec575f80fd5b83359250602084013567ffffffffffffffff811115610709575f80fd5b61071586828701610695565b9497909650939450505050565b803573ffffffffffffffffffffffffffffffffffffffff81168114610745575f80fd5b919050565b5f6020828403121561075a575f80fd5b61076382610722565b9392505050565b5f805f805f806080878903121561077f575f80fd5b61078887610722565b955060208701359450604087013567ffffffffffffffff808211156107ab575f80fd5b6107b78a838b01610695565b909650945060608901359150808211156107cf575f80fd5b506107dc89828a01610695565b979a9699509497509295939492505050565b5f80604083850312156107ff575f80fd5b61080883610722565b946020939093013593505050565b5f60208284031215610826575f80fd5b81518015158114610763575f80fd5b5f60208284031215610845575f80fd5b5051919050565b73ffffffffffffffffffffffffffffffffffffffff8516815283602082015260606040820152816060820152818360808301375f818301608090810191909152601f9092017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01601019392505050565b818382375f910190815291905056fea164736f6c6343000815000a

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

0000000000000000000000009f8f72aa9304c8b593d555f12ef6589cc3a579a20000000c00010002000100000000000000000000000000000000000000000000

-----Decoded View---------------
Arg [0] : _MKR (address): 0x9f8F72aA9304c8B593d555F12eF6589cC3A579A2
Arg [1] : _facilitatorHat (uint256): 323519771394501307089259385432256433664376508480981965228332365643776

-----Encoded View---------------
2 Constructor Arguments found :
Arg [0] : 0000000000000000000000009f8f72aa9304c8b593d555f12ef6589cc3a579a2
Arg [1] : 0000000c00010002000100000000000000000000000000000000000000000000


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.