ETH Price: $3,862.15 (+5.77%)

Contract Diff Checker

Contract Name:
NumeP2P

Contract Source Code:

// SPDX-License-Identifier: GNU GPLv3
pragma solidity 0.8.20;

/******************************************************************************\
* https://github.com/mudgen/diamond-2-hardhat/blob/main/contracts/interfaces/IDiamondCut.sol
* Author: Nick Mudge <[email protected]> (https://twitter.com/mudgen)
* EIP-2535 Diamonds: https://eips.ethereum.org/EIPS/eip-2535
/******************************************************************************/

interface IDiamondCut {
    enum FacetCutAction {
        Add,
        Replace,
        Remove
    }
    // Add=0, Replace=1, Remove=2

    struct FacetCut {
        address facetAddress;
        FacetCutAction action;
        bytes4[] functionSelectors;
    }

    /// @notice Add/replace/remove any number of functions and optionally execute
    ///         a function with delegatecall
    /// @param _diamondCut Contains the facet addresses and function selectors
    /// @param _init The address of the contract or facet to execute _calldata
    /// @param _calldata A function call, including function selector and arguments
    ///                  _calldata is executed with delegatecall on _init
    function diamondCut(
        FacetCut[] calldata _diamondCut,
        address _init,
        bytes calldata _calldata
    ) external;

    event DiamondCut(FacetCut[] _diamondCut, address _init, bytes _calldata);
}

// SPDX-License-Identifier: GNU GPLv3
pragma solidity 0.8.20;

library AppStorage {
    bytes32 constant CONFIG_STORAGE_POSITION =
        keccak256("nume.protocol.config.storage");

    bytes32 constant USER_STORAGE_POSITION =
        keccak256("nume.protocol.user.storage");

    bytes32 constant QUEUE_STORAGE_POSITION =
        keccak256("nume.protocol.queue.storage");

    bytes32 constant NUME_STORAGE_POSITION =
        keccak256("nume.protocol.request.storage");

    struct ConfigStorage {
        bool isInExodusMode;
        uint256 totalTokens;
        uint256 numeBlockNumber;
        address verificationAddress;
        address numeOwner;
        address governorContract;
        uint256 depositsLimit;
        uint256 nftDepositsLimit;
        uint256 currDepositsQueueIndex;
        uint256 currNFTDepositsQueueIndex;
        uint256 currWithdrawalsQueueIndex;
        uint256 currNFTWithdrawalsQueueIndex;
        uint256 currCancelSubsciptionsQueueIndex;
        uint256 lastFinalizedDepositsQueueIndex;
        uint256 lastFinalizedNFTDepositsQueueIndex;
        uint256 lastFinalizedWithdrawalsQueueIndex;
        uint256 lastFinalizedNFTWithdrawalsQueueIndex;
        uint256 lastFinalizedCancelSubsciptionsQueueIndex;
        uint256 WITHDRAWAL_STAKE;
        uint256 WITHDRAWAL_REQUEST_TIMEOUT;
        uint256 SETTLEMENT_TIMEOUT;
        uint256 LAST_SETTLEMENT_TIMESTAMP;
        bytes32 settlementsRoot;
        bytes32 nftCollectionsRoot;
        mapping(address => bool) supportedTokens;
        mapping(address => uint256) tokenDepositLimit;
        bytes32[] SettlementsRootQueue;
        bytes32[] NftCollectionsRootQueue;
    }

    struct UserStorage {
        mapping(uint256 => address) depositSenderAddress;
        mapping(uint256 => address) nftDepositSenderAddress;
        mapping(uint256 => address) contractWithdrawalSenderAddress;
        mapping(uint256 => address) contractNftWithdrawalSenderAddress;
        mapping(address => uint256) userDepositCount; // userAddress => depositCount
        mapping(address => uint256) userDepositTimestamp; // userAddress => depositTimestamp
        mapping(address => uint256) userNftDepositCount; // userAddress => nft depositCount
        mapping(address => uint256) userNftDepositTimestamp; // userAddress => nft depositTimestamp
        mapping(address => mapping(address => bool)) userExodusWithdrawals; // userAddress => tokenAddress => isWithdrawn
        mapping(address => mapping(bytes32 => bool)) userExodusNFTWithdrawals; // userAddress => keccak256(NFTContractAddress, tokenID) => isWithdrawn
        mapping(address => bool) isAddressBlacklisted; // userAddress => isBlacklisted
        mapping(address => mapping(address => uint256)) userWithdrawalBalance; // userAddress => tokenAddress => withdrawal amount
        mapping(address => bytes[]) userBackendNftWithdrawalRequests; // userAddress => nftWithdrawalRequestHashes
    }

    struct QueueStorage {
        bytes32[] depositsQueue;
        bytes32[] NFTDepositsQueue;
        bytes32[] withdrawalsQueue;
        bytes32[] NFTWithdrawalsQueue;
        bytes32[] cancelSubsciptionsQueue;
    }

    struct NumeStorage {
        mapping(address => mapping(address => bool)) withdrawalRequests; // user adddress => tokenID => isWithdrawalRequested
        mapping(address => mapping(bytes32 => bool)) NFTWithdrawalRequests; // user adddress => keccak256(NFTContractAddress, tokenID) => isWithdrawalRequested
        mapping(uint256 => uint256) withdrawalRequestTimestamps; // withdrawalQueueIndex => timestamp
        mapping(uint256 => uint256) NFTWithdrawalRequestTimestamps; // nft withdrawalQueueIndex => timestamp
        mapping(address => bool) cancelSubscriptionRequests; // userAddress => isCancelRequested
        mapping(uint256 => uint256) cancelSubscriptionRequestTimestamps; // cancel subscription queue index => timestamp
    }

    function configStorage() internal pure returns (ConfigStorage storage cs) {
        bytes32 position = CONFIG_STORAGE_POSITION;
        assembly {
            cs.slot := position
        }
    }

    function queueStorage() internal pure returns (QueueStorage storage qs) {
        bytes32 position = QUEUE_STORAGE_POSITION;
        assembly {
            qs.slot := position
        }
    }

    function userStorage() internal pure returns (UserStorage storage us) {
        bytes32 position = USER_STORAGE_POSITION;
        assembly {
            us.slot := position
        }
    }

    function numeStorage() internal pure returns (NumeStorage storage ns) {
        bytes32 position = NUME_STORAGE_POSITION;
        assembly {
            ns.slot := position
        }
    }

    function enforceIsNumeOwner() internal view {
        require(
            msg.sender == configStorage().numeOwner,
            "Nume: Only Nume owner can call this function"
        );
    }

    function enforceIsGovernor() internal view {
        require(
            msg.sender == configStorage().governorContract,
            "Nume: Only governor contract can call this function"
        );
    }

    function notBlacklisted(address user) internal view {
        require(
            !userStorage().isAddressBlacklisted[user],
            "Nume: User is blacklisted"
        );
    }

    function enforceNotExodusMode() internal view {
        require(
            !configStorage().isInExodusMode,
            "Nume: Protocol is in exodus mode"
        );
    }

    error FacetNotExist();
    error ExceededMaximumDailyCalls(address user);
    error InvalidTokenAddress(address tokenAddress);
    error InvalidAmount();
    error TransactionFailed();
    error NotInExodusMode();
    error AddressBlacklisted(address user);
    error InvalidAddress();
}

// SPDX-License-Identifier: GNU GPLv3
pragma solidity 0.8.20;

/******************************************************************************\
* Author: Nick Mudge <[email protected]> (https://twitter.com/mudgen)
* EIP-2535 Diamonds: https://eips.ethereum.org/EIPS/eip-2535
/******************************************************************************/
import {IDiamondCut} from "../interfaces/IDiamondCut.sol";

// Remember to add the loupe functions from DiamondLoupeFacet to the diamond.
// The loupe functions are required by the EIP2535 Diamonds standard

error InitializationFunctionReverted(
    address _initializationContractAddress,
    bytes _calldata
);

library LibDiamond {
    bytes32 constant DIAMOND_STORAGE_POSITION =
        keccak256("diamond.standard.diamond.storage");

    struct DiamondStorage {
        // maps function selectors to the facets that execute the functions.
        // and maps the selectors to their position in the selectorSlots array.
        // func selector => address facet, selector position
        mapping(bytes4 => bytes32) facets;
        // array of slots of function selectors.
        // each slot holds 8 function selectors.
        mapping(uint256 => bytes32) selectorSlots;
        // The number of function selectors in selectorSlots
        uint16 selectorCount;
        // Used to query if a contract implements an interface.
        // Used to implement ERC-165.
        mapping(bytes4 => bool) supportedInterfaces;
        // owner of the contract
        address diamondOwner;
    }

    function diamondStorage()
        internal
        pure
        returns (DiamondStorage storage ds)
    {
        bytes32 position = DIAMOND_STORAGE_POSITION;
        assembly {
            ds.slot := position
        }
    }

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

    function setDiamondOwner(address _newOwner) internal {
        DiamondStorage storage ds = diamondStorage();
        address previousOwner = ds.diamondOwner;
        ds.diamondOwner = _newOwner;
        emit OwnershipTransferred(previousOwner, _newOwner);
    }

    function getDiamondOwner() internal view returns (address contractOwner_) {
        contractOwner_ = diamondStorage().diamondOwner;
    }

    function enforceIsDiamondOwner() internal view {
        require(
            msg.sender == diamondStorage().diamondOwner,
            "LibDiamond: Must be diamond owner"
        );
    }

    event DiamondCut(
        IDiamondCut.FacetCut[] _diamondCut,
        address _init,
        bytes _calldata
    );

    bytes32 constant CLEAR_ADDRESS_MASK =
        bytes32(uint256(0xffffffffffffffffffffffff));
    bytes32 constant CLEAR_SELECTOR_MASK = bytes32(uint256(0xffffffff << 224));

    // Internal function version of diamondCut
    // This code is almost the same as the external diamondCut,
    // except it is using 'Facet[] memory _diamondCut' instead of
    // 'Facet[] calldata _diamondCut'.
    // The code is duplicated to prevent copying calldata to memory which
    // causes an error for a two dimensional array.
    function diamondCut(
        IDiamondCut.FacetCut[] memory _diamondCut,
        address _init,
        bytes memory _calldata
    ) internal {
        DiamondStorage storage ds = diamondStorage();
        uint256 originalSelectorCount = ds.selectorCount;
        uint256 selectorCount = originalSelectorCount;
        bytes32 selectorSlot;
        // Check if last selector slot is not full
        // "selectorCount & 7" is a gas efficient modulo by eight "selectorCount % 8"
        if (selectorCount & 7 > 0) {
            // get last selectorSlot
            // "selectorSlot >> 3" is a gas efficient division by 8 "selectorSlot / 8"
            selectorSlot = ds.selectorSlots[selectorCount >> 3];
        }
        // loop through diamond cut
        for (uint256 facetIndex; facetIndex < _diamondCut.length; ) {
            (selectorCount, selectorSlot) = addReplaceRemoveFacetSelectors(
                selectorCount,
                selectorSlot,
                _diamondCut[facetIndex].facetAddress,
                _diamondCut[facetIndex].action,
                _diamondCut[facetIndex].functionSelectors
            );

            unchecked {
                facetIndex++;
            }
        }
        if (selectorCount != originalSelectorCount) {
            ds.selectorCount = uint16(selectorCount);
        }
        // If last selector slot is not full
        // "selectorCount & 7" is a gas efficient modulo by eight "selectorCount % 8"
        if (selectorCount & 7 > 0) {
            // "selectorSlot >> 3" is a gas efficient division by 8 "selectorSlot / 8"
            ds.selectorSlots[selectorCount >> 3] = selectorSlot;
        }
        emit DiamondCut(_diamondCut, _init, _calldata);
        initializeDiamondCut(_init, _calldata);
    }

    function addReplaceRemoveFacetSelectors(
        uint256 _selectorCount,
        bytes32 _selectorSlot,
        address _newFacetAddress,
        IDiamondCut.FacetCutAction _action,
        bytes4[] memory _selectors
    ) internal returns (uint256, bytes32) {
        DiamondStorage storage ds = diamondStorage();
        require(
            _selectors.length > 0,
            "LibDiamondCut: No selectors in facet to cut"
        );
        if (_action == IDiamondCut.FacetCutAction.Add) {
            enforceHasContractCode(
                _newFacetAddress,
                "LibDiamondCut: Add facet has no code"
            );
            for (uint256 selectorIndex; selectorIndex < _selectors.length; ) {
                bytes4 selector = _selectors[selectorIndex];
                bytes32 oldFacet = ds.facets[selector];
                require(
                    address(bytes20(oldFacet)) == address(0),
                    "LibDiamondCut: Can't add function that already exists"
                );
                // add facet for selector
                ds.facets[selector] =
                    bytes20(_newFacetAddress) |
                    bytes32(_selectorCount);
                // "_selectorCount & 7" is a gas efficient modulo by eight "_selectorCount % 8"
                // " << 5 is the same as multiplying by 32 ( * 32)
                uint256 selectorInSlotPosition = (_selectorCount & 7) << 5;
                // clear selector position in slot and add selector
                _selectorSlot =
                    (_selectorSlot &
                        ~(CLEAR_SELECTOR_MASK >> selectorInSlotPosition)) |
                    (bytes32(selector) >> selectorInSlotPosition);
                // if slot is full then write it to storage
                if (selectorInSlotPosition == 224) {
                    // "_selectorSlot >> 3" is a gas efficient division by 8 "_selectorSlot / 8"
                    ds.selectorSlots[_selectorCount >> 3] = _selectorSlot;
                    _selectorSlot = 0;
                }
                _selectorCount++;

                unchecked {
                    selectorIndex++;
                }
            }
        } else if (_action == IDiamondCut.FacetCutAction.Replace) {
            enforceHasContractCode(
                _newFacetAddress,
                "LibDiamondCut: Replace facet has no code"
            );
            for (uint256 selectorIndex; selectorIndex < _selectors.length; ) {
                bytes4 selector = _selectors[selectorIndex];
                bytes32 oldFacet = ds.facets[selector];
                address oldFacetAddress = address(bytes20(oldFacet));
                // only useful if immutable functions exist
                require(
                    oldFacetAddress != address(this),
                    "LibDiamondCut: Can't replace immutable function"
                );
                require(
                    oldFacetAddress != _newFacetAddress,
                    "LibDiamondCut: Can't replace function with same function"
                );
                require(
                    oldFacetAddress != address(0),
                    "LibDiamondCut: Can't replace function that doesn't exist"
                );
                // replace old facet address
                ds.facets[selector] =
                    (oldFacet & CLEAR_ADDRESS_MASK) |
                    bytes20(_newFacetAddress);

                unchecked {
                    selectorIndex++;
                }
            }
        } else if (_action == IDiamondCut.FacetCutAction.Remove) {
            require(
                _newFacetAddress == address(0),
                "LibDiamondCut: Remove facet address must be address(0)"
            );
            // "_selectorCount >> 3" is a gas efficient division by 8 "_selectorCount / 8"
            uint256 selectorSlotCount = _selectorCount >> 3;
            // "_selectorCount & 7" is a gas efficient modulo by eight "_selectorCount % 8"
            uint256 selectorInSlotIndex = _selectorCount & 7;
            for (uint256 selectorIndex; selectorIndex < _selectors.length; ) {
                if (_selectorSlot == 0) {
                    // get last selectorSlot
                    selectorSlotCount--;
                    _selectorSlot = ds.selectorSlots[selectorSlotCount];
                    selectorInSlotIndex = 7;
                } else {
                    selectorInSlotIndex--;
                }
                bytes4 lastSelector;
                uint256 oldSelectorsSlotCount;
                uint256 oldSelectorInSlotPosition;
                // adding a block here prevents stack too deep error
                {
                    bytes4 selector = _selectors[selectorIndex];
                    bytes32 oldFacet = ds.facets[selector];
                    require(
                        address(bytes20(oldFacet)) != address(0),
                        "LibDiamondCut: Can't remove function that doesn't exist"
                    );
                    // only useful if immutable functions exist
                    require(
                        address(bytes20(oldFacet)) != address(this),
                        "LibDiamondCut: Can't remove immutable function"
                    );
                    // replace selector with last selector in ds.facets
                    // gets the last selector
                    // " << 5 is the same as multiplying by 32 ( * 32)
                    lastSelector = bytes4(
                        _selectorSlot << (selectorInSlotIndex << 5)
                    );
                    if (lastSelector != selector) {
                        // update last selector slot position info
                        ds.facets[lastSelector] =
                            (oldFacet & CLEAR_ADDRESS_MASK) |
                            bytes20(ds.facets[lastSelector]);
                    }
                    delete ds.facets[selector];
                    uint256 oldSelectorCount = uint16(uint256(oldFacet));
                    // "oldSelectorCount >> 3" is a gas efficient division by 8 "oldSelectorCount / 8"
                    oldSelectorsSlotCount = oldSelectorCount >> 3;
                    // "oldSelectorCount & 7" is a gas efficient modulo by eight "oldSelectorCount % 8"
                    // " << 5 is the same as multiplying by 32 ( * 32)
                    oldSelectorInSlotPosition = (oldSelectorCount & 7) << 5;
                }
                if (oldSelectorsSlotCount != selectorSlotCount) {
                    bytes32 oldSelectorSlot = ds.selectorSlots[
                        oldSelectorsSlotCount
                    ];
                    // clears the selector we are deleting and puts the last selector in its place.
                    oldSelectorSlot =
                        (oldSelectorSlot &
                            ~(CLEAR_SELECTOR_MASK >>
                                oldSelectorInSlotPosition)) |
                        (bytes32(lastSelector) >> oldSelectorInSlotPosition);
                    // update storage with the modified slot
                    ds.selectorSlots[oldSelectorsSlotCount] = oldSelectorSlot;
                } else {
                    // clears the selector we are deleting and puts the last selector in its place.
                    _selectorSlot =
                        (_selectorSlot &
                            ~(CLEAR_SELECTOR_MASK >>
                                oldSelectorInSlotPosition)) |
                        (bytes32(lastSelector) >> oldSelectorInSlotPosition);
                }
                if (selectorInSlotIndex == 0) {
                    delete ds.selectorSlots[selectorSlotCount];
                    _selectorSlot = 0;
                }

                unchecked {
                    selectorIndex++;
                }
            }
            _selectorCount = selectorSlotCount * 8 + selectorInSlotIndex;
        } else {
            revert("LibDiamondCut: Incorrect FacetCutAction");
        }
        return (_selectorCount, _selectorSlot);
    }

    function initializeDiamondCut(
        address _init,
        bytes memory _calldata
    ) internal {
        if (_init == address(0)) {
            return;
        }
        enforceHasContractCode(
            _init,
            "LibDiamondCut: _init address has no code"
        );
        (bool success, bytes memory error) = _init.delegatecall(_calldata);
        if (!success) {
            if (error.length > 0) {
                // bubble up error
                /// @solidity memory-safe-assembly
                assembly {
                    let returndata_size := mload(error)
                    revert(add(32, error), returndata_size)
                }
            } else {
                revert InitializationFunctionReverted(_init, _calldata);
            }
        }
    }

    function enforceHasContractCode(
        address _contract,
        string memory _errorMessage
    ) internal view {
        uint256 contractSize;
        assembly {
            contractSize := extcodesize(_contract)
        }
        require(contractSize > 0, _errorMessage);
    }
}

// SPDX-License-Identifier: GNU GPLv3
pragma solidity 0.8.20;

/******************************************************************************\
* This file is a modification of the diamond-2 implementation by Nick Mudge.
* https://github.com/mudgen/diamond-2-hardhat/blob/main/contracts/Diamond.sol
* Implementation of a diamond. Referred from Nick Mudge's diamond-2 implementation.
/******************************************************************************/

import {LibDiamond} from "./libraries/LibDiamond.sol";
import {AppStorage} from "./libraries/AppStorage.sol";
import {IDiamondCut} from "./interfaces/IDiamondCut.sol";

contract NumeP2P {
    constructor(
        address _contractOwner,
        address _numeOwner,
        address _governorContract,
        address _diamondCutFacet,
        bytes memory _data
    ) payable {
        LibDiamond.setDiamondOwner(_contractOwner);
        AppStorage.configStorage().numeOwner = _numeOwner;
        AppStorage.configStorage().governorContract = _governorContract;

        // Add the diamondCut external function from the diamondCutFacet
        IDiamondCut.FacetCut[] memory cut = new IDiamondCut.FacetCut[](1);
        bytes4[] memory functionSelectors = new bytes4[](1);
        functionSelectors[0] = IDiamondCut.diamondCut.selector;
        cut[0] = IDiamondCut.FacetCut({
            facetAddress: _diamondCutFacet,
            action: IDiamondCut.FacetCutAction.Add,
            functionSelectors: functionSelectors
        });
        LibDiamond.diamondCut(cut, address(0), "");

        (bytes32 _initSettlementsRoot, bytes32 _initNFTCollectionsRoot) = abi
            .decode(_data, (bytes32, bytes32));

        AppStorage.ConfigStorage storage cs = AppStorage.configStorage();

        ++cs.totalTokens;
        cs.supportedTokens[0x1111111111111111111111111111111111111111] = true;
        cs.settlementsRoot = _initSettlementsRoot;
        cs.nftCollectionsRoot = _initNFTCollectionsRoot;
        cs.WITHDRAWAL_STAKE = 0.01 ether;
        cs.WITHDRAWAL_REQUEST_TIMEOUT = 14 days;
        cs.SETTLEMENT_TIMEOUT = 10 minutes;
    }

    // Find facet for function that is called and execute the
    // function if a facet is found and return any value.
    fallback() external payable {
        LibDiamond.DiamondStorage storage ds;
        bytes32 position = LibDiamond.DIAMOND_STORAGE_POSITION;
        // get diamond storage
        assembly {
            ds.slot := position
        }
        // get facet from function selector
        address facet = address(bytes20(ds.facets[msg.sig]));
        if (facet == address(0)) {
            revert AppStorage.FacetNotExist();
        }
        // Execute external function from facet using delegatecall and return any value.
        assembly {
            // copy function selector and any arguments
            calldatacopy(0, 0, calldatasize())
            // execute function call using the facet
            let result := delegatecall(gas(), facet, 0, calldatasize(), 0, 0)
            // get any return value
            returndatacopy(0, 0, returndatasize())
            // return any return value or error back to the caller
            switch result
            case 0 {
                revert(0, returndatasize())
            }
            default {
                return(0, returndatasize())
            }
        }
    }
}

Please enter a contract address above to load the contract details and source code.

Context size (optional):