S Price: $0.85637 (+0.70%)

Contract

0xdA1459bbF299c4834F549FC7B147de2B2E28fdC4

Overview

S Balance

Sonic LogoSonic LogoSonic Logo0 S

S Value

$0.00

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To

There are no matching entries

1 Internal Transaction found.

Latest 1 internal transaction

Parent Transaction Hash Block From To
83161682025-02-17 18:13:526 days ago1739816032  Contract Creation0 S
Loading...
Loading

Similar Match Source Code
This contract matches the deployed Bytecode of the Source Code for Contract 0xdbeDB4Db...465513266
The constructor portion of the code might be different and could alter the actual behaviour of the contract

Contract Name:
AssetTokenCCIPCompatible

Compiler Version
v0.8.28+commit.7893614a

Optimization Enabled:
Yes with 200 runs

Other Settings:
paris EvmVersion
File 1 of 10 : AssetTokenCCIPCompatible.sol
// SPDX-License-Identifier: GPL-3.0-only
pragma solidity 0.8.28;

import { Ownable } from "solady/src/auth/Ownable.sol";
import { EnumerableRoles } from "solady/src/auth/EnumerableRoles.sol";
import { AssetToken } from "../assetToken/AssetToken.sol";

/**
 * @title AssetTokenCCIPCompatible
 * @notice This abstract contract extends the AssetToken functionality by implementing an annual fee mechanism.
 * @dev Tracks the time since the last operation and calculates fees to be minted periodically based on the annual fee rate.
 * Fees are minted to the fee receiver as defined in the BundleStorage contract.
 * @author Swarm
 */
contract AssetTokenCCIPCompatible is AssetToken, Ownable, EnumerableRoles {
    uint256 public constant MINTER_ROLE = uint256(keccak256("MINTER_ROLE"));

    /// @notice Constructor: sets the state variables and provide proper checks to deploy
    /// @param _assetTokenData the asset token data contract address
    /// @param _statePercent the state percent to check the safeguard convertion
    /// @param _kya verification link
    /// @param _minimumRedemptionAmount less than this value is not allowed
    /// @param _name of the token
    /// @param _symbol of the token
    constructor(
        address _assetTokenData,
        address _owner,
        uint256 _statePercent,
        string memory _kya,
        uint256 _minimumRedemptionAmount,
        string memory _name,
        string memory _symbol
    ) AssetToken(_assetTokenData, _statePercent, _kya, _minimumRedemptionAmount, _name, _symbol) {
        _initializeOwner(_owner);
    }

    /**
     * @notice Mints a specified amount of tokens to a given address.
     * @dev Only accounts with the MINTER role can call this function.
     *
     * @param account The address that will receive the minted tokens.
     * @param amount The amount of tokens to mint.
     */
    function mint(address account, uint256 amount) external onlyRole(MINTER_ROLE) {
        _mint(account, amount);
    }
}

File 2 of 10 : AccessManager.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;

import { Ownable } from "solady/src/auth/Ownable.sol";
import { EnumerableRoles } from "solady/src/auth/EnumerableRoles.sol";
import { IAuthorizationContract } from "../interfaces/IAuthorizationContract.sol";

/// @author Swarm Markets
/// @title Access Manager for AssetToken Contract
/// @notice Contract to manage the Asset Token contracts
abstract contract AccessManager is EnumerableRoles, Ownable {
    error ZeroAddressPassed();
    error NewAgentEqOldAgent(address agent);
    error ListNotOwned(address oldAgent, address sender);

    enum RoleErrorType {
        OnlyIssuerOrAdmin,
        OnlyGuardianOrAdmin,
        OnlyIssuer,
        OnlyAgent,
        OnlyIssuerOrAgent,
        OnlyIssuerOnActive,
        OnlyAgentOrIssuerOnActive,
        OnlyGuardianOnSafeguard
    }
    error RolesError(address caller, RoleErrorType role);

    enum BlacklistErrorType {
        Blacklisted,
        NotBlacklisted
    }
    error BlacklistError(address account, BlacklistErrorType errorType);

    enum AgentErrorType {
        NotExists,
        Exists,
        HasContractsAssigned
    }
    error AgentError(address token, address agent, AgentErrorType errorType);

    enum TokenErrorType {
        NotStored,
        Registered,
        NotFrozen,
        Frozen,
        NotActiveOnSafeguard
    }
    error TokenError(address token, TokenErrorType errorType);

    enum ContractErrorType {
        NotAContract,
        NotFound,
        NotManagedByCaller,
        BelongsToAgent
    }
    error ContractError(address token, address contractAddress, ContractErrorType errorType);

    enum ValidationErrorType {
        ZeroAddressesPassed,
        AuthListEmpty,
        IndexNotExists,
        RemovingFromAuthFailed,
        AuthListOwnershipTransferFailed,
        AgentWithoutContracts
    }
    error ValidationError(ValidationErrorType errorType);

    /// @notice Emitted when changed max quantity
    event ChangedMaxQtyOfAuthorizationLists(address indexed changedBy, uint newQty);

    /// @notice Emitted when Issuer is transferred
    event IssuerTransferred(address indexed _tokenAddress, address indexed _caller, address indexed _newIssuer);
    /// @notice Emitted when Guardian is transferred
    event GuardianTransferred(address indexed _tokenAddress, address indexed _caller, address indexed _newGuardian);

    /// @notice Emitted when Agent is added to the contract
    event AgentAdded(address indexed _tokenAddress, address indexed _caller, address indexed _newAgent);
    /// @notice Emitted when Agent is removed from the contract
    event AgentRemoved(address indexed _tokenAddress, address indexed _caller, address indexed _agent);

    /// @notice Emitted when an Agent list is transferred to another Agent
    event AgentAuthorizationListTransferred(
        address indexed _tokenAddress,
        address _caller,
        address indexed _newAgent,
        address indexed _oldAgent
    );

    /// @notice Emitted when an account is added to the Asset Token Blacklist
    event AddedToBlacklist(address indexed _tokenAddress, address indexed _account, address indexed _from);
    /// @notice Emitted when an account is removed from the Asset Token Blacklist
    event RemovedFromBlacklist(address indexed _tokenAddress, address indexed _account, address indexed _from);

    /// @notice Emitted when a contract is added to the Asset Token Authorization list
    event AddedToAuthorizationContracts(
        address indexed _tokenAddress,
        address indexed _contractAddress,
        address indexed _from
    );
    /// @notice Emitted when a contract is removed from the Asset Token Authorization list
    event RemovedFromAuthorizationContracts(
        address indexed _tokenAddress,
        address indexed _contractAddress,
        address indexed _from
    );

    /// @notice Emitted when an account is granted with the right to transfer on safeguard state
    event AddedTransferOnSafeguardAccount(address indexed _tokenAddress, address indexed _account);
    /// @notice Emitted when an account is revoked the right to transfer on safeguard state
    event RemovedTransferOnSafeguardAccount(address indexed _tokenAddress, address indexed _account);

    /// @notice Emitted when a new Asset Token is deployed and registered
    event TokenRegistered(address indexed _tokenAddress, address _caller);
    /// @notice Emitted when an  Asset Token is deleted
    event TokenDeleted(address indexed _tokenAddress, address _caller);

    /// @notice Emitted when the contract changes to safeguard mode
    event ChangedToSafeGuard(address indexed _tokenAddress);

    /// @notice Emitted when the contract gets frozen
    event FrozenContract(address indexed _tokenAddress);
    /// @notice Emitted when the contract gets unfrozen
    event UnfrozenContract(address indexed _tokenAddress);

    /// @notice Admin role
    uint256 public constant DEFAULT_ADMIN_ROLE = uint256(keccak256("DEFAULT_ADMIN_ROLE"));
    /// @notice Role to be able to deploy an Asset Token
    uint256 public constant ASSET_DEPLOYER_ROLE = uint256(keccak256("ASSET_DEPLOYER_ROLE"));

    /// @dev This is a WAD on DSMATH representing 1
    /// @dev This is a proportion of 1 representing 100%, equal to a WAD
    uint256 public constant DECIMALS = 10 ** 18;

    /// @notice Structure to hold the Token Data
    /// @notice guardian and issuer of the contract
    /// @notice isFrozen: boolean to store if the contract is frozen
    /// @notice isOnSafeguard: state of the contract: false is ACTIVE // true is SAFEGUARD
    /// @notice positiveInterest: if the interest will be a positvie or negative one
    /// @notice interestRate: the interest rate set in AssetTokenData.setInterestRate() (percent per seconds)
    /// @notice rate: the interest determined by the formula. Default is 10**18
    /// @notice lastUpdate: last block where the update function was called
    /// @notice blacklist: account => bool (if bool = true, account is blacklisted)
    /// @notice agents: agents => bool(true or false) (enabled/disabled agent)
    /// @notice safeguardTransferAllow: allow certain addresses to transfer even on safeguard
    /// @notice authorizationsPerAgent: list of contracts of each agent to authorize a user
    /// @notice array of addresses. Each one is a contract with the isTxAuthorized function
    struct TokenData {
        bool isFrozen;
        bool isOnSafeguard;
        bool positiveInterest;
        uint256 interestRate;
        uint256 rate;
        uint256 lastUpdate;
        address issuer;
        address guardian;
        address[] authorizationContracts;
        mapping(address => bool) blacklist;
        mapping(address => bool) agents;
        mapping(address => bool) safeguardTransferAllow;
        mapping(address => address) authorizationsPerAgent;
    }
    /// @notice mapping of TokenData, entered by token Address
    mapping(address => TokenData) public tokensData;

    /// @dev this is just to have an estimation of qty and prevent innecesary looping
    uint256 public maxQtyOfAuthorizationLists;

    modifier requireNonEmptyAddress(address _address) {
        require(_address != address(0), ZeroAddressPassed());
        _;
    }

    /// @notice Check if the token is valid
    /// @param tokenAddress address of the current token being managed
    modifier onlyStoredToken(address tokenAddress) {
        require(tokensData[tokenAddress].issuer != address(0), TokenError(tokenAddress, TokenErrorType.NotStored));
        _;
    }

    /// @notice Check if sender is an AGENT
    /// @param tokenAddress address of the current token being managed
    /// @param functionCaller the caller of the function where this is used
    modifier onlyAgent(address tokenAddress, address functionCaller) {
        require(tokensData[tokenAddress].agents[functionCaller], RolesError(functionCaller, RoleErrorType.OnlyAgent));
        _;
    }

    /// @notice Allow TRANSFER on Safeguard
    /// @param _tokenAddress address of the current token being managed
    /// @param _account the account to grant the right to transfer on safeguard state
    function allowTransferOnSafeguard(address _tokenAddress, address _account) external onlyStoredToken(_tokenAddress) {
        onlyIssuerOrGuardian(_tokenAddress, msg.sender);
        tokensData[_tokenAddress].safeguardTransferAllow[_account] = true;
        emit AddedTransferOnSafeguardAccount(_tokenAddress, _account);
    }

    /// @notice Removed TRANSFER on Safeguard
    /// @param _tokenAddress address of the current token being managed
    /// @param _account the account to be revoked from the right to transfer on safeguard state
    function preventTransferOnSafeguard(
        address _tokenAddress,
        address _account
    ) external onlyStoredToken(_tokenAddress) {
        onlyIssuerOrGuardian(_tokenAddress, msg.sender);
        tokensData[_tokenAddress].safeguardTransferAllow[_account] = false;
        emit RemovedTransferOnSafeguardAccount(_tokenAddress, _account);
    }

    function changeMaxQtyOfAuthorizationLists(uint newMaxQty) public onlyRole(DEFAULT_ADMIN_ROLE) {
        maxQtyOfAuthorizationLists = newMaxQty;
        emit ChangedMaxQtyOfAuthorizationLists(msg.sender, newMaxQty);
    }

    /**
     * @notice Checks if the user is authorized by the agent
     * @dev This function verifies if the `_from` and `_to` addresses are authorized to perform a given `_amount`
     * transaction on the asset token contract `_tokenAddress`.
     * @param _tokenAddress The address of the current token being managed
     * @param _from The address to be checked if it's authorized
     * @param _to The address to be checked if it's authorized
     * @param _amount The amount of the operation to be made
     * @return bool Returns true if `_from` and `_to` are authorized to perform the transaction
     */
    function mustBeAuthorizedHolders(
        address _tokenAddress,
        address _from,
        address _to,
        uint256 _amount
    ) external onlyStoredToken(_tokenAddress) returns (bool) {
        require(msg.sender == _tokenAddress, ContractError(_tokenAddress, msg.sender, ContractErrorType.NotAContract));
        // This line below should never happen. A registered asset token shouldn't call
        // to this function with both addresses (from - to) in ZERO
        require(_from != address(0) || _to != address(0), ValidationError(ValidationErrorType.ZeroAddressesPassed));

        address[2] memory addresses = [_from, _to];
        uint256 response = 0;
        uint256 arrayLength = addresses.length;
        TokenData storage token = tokensData[_tokenAddress];
        for (uint256 i = 0; i < arrayLength; ++i) {
            if (addresses[i] != address(0)) {
                require(!token.blacklist[addresses[i]], BlacklistError(addresses[i], BlacklistErrorType.Blacklisted));

                /// @dev the caller (the asset token contract) is an authorized holder
                if (addresses[i] == _tokenAddress && addresses[i] == msg.sender) {
                    response++;
                    // this is a resource to avoid validating this contract in other system
                    addresses[i] = address(0);
                }
                if (!token.isOnSafeguard) {
                    /// @dev on active state, issuer and agents are authorized holder
                    if (addresses[i] == token.issuer || token.agents[addresses[i]]) {
                        response++;
                        // this is a resource to avoid validating agent/issuer in other system
                        addresses[i] = address(0);
                    }
                } else {
                    /// @dev on safeguard state, guardian is authorized holder
                    if (addresses[i] == token.guardian) {
                        response++;
                        // this is a resource to avoid validating guardian in other system
                        addresses[i] = address(0);
                    }
                }

                /// each of these if statements are mutually exclusive, so response cannot be more than 2
            }
        }

        /// if response is more than 0 none of the address are:
        /// the asset token contract itself, agents, issuer or guardian
        /// if response is 1 there is one address which is one of the above
        /// if response is 2 both addresses are one of the above, no need to iterate in external list
        if (response < 2) {
            uint256 length = token.authorizationContracts.length;
            require(length > 0, ValidationError(ValidationErrorType.AuthListEmpty));
            for (uint256 i = 0; i < length; ++i) {
                if (
                    IAuthorizationContract(token.authorizationContracts[i]).isTxAuthorized(
                        _tokenAddress,
                        addresses[0],
                        addresses[1],
                        _amount
                    )
                ) {
                    return true;
                }
            }
        } else {
            return true;
        }
        return false;
    }

    /// @notice Changes the ISSUER
    /// @param _tokenAddress address of the current token being managed
    /// @param _newIssuer to be assigned in the contract
    function transferIssuer(address _tokenAddress, address _newIssuer) external onlyStoredToken(_tokenAddress) {
        TokenData storage tokenData = tokensData[_tokenAddress];
        require(
            msg.sender == tokenData.issuer || hasRole(msg.sender, DEFAULT_ADMIN_ROLE),
            RolesError(msg.sender, RoleErrorType.OnlyIssuerOrAdmin)
        );
        tokenData.issuer = _newIssuer;
        emit IssuerTransferred(_tokenAddress, msg.sender, _newIssuer);
    }

    /// @notice Changes the GUARDIAN
    /// @param _tokenAddress address of the current token being managed
    /// @param _newGuardian to be assigned in the contract
    function transferGuardian(address _tokenAddress, address _newGuardian) external onlyStoredToken(_tokenAddress) {
        TokenData storage tokenData = tokensData[_tokenAddress];
        require(
            msg.sender == tokenData.guardian || hasRole(msg.sender, DEFAULT_ADMIN_ROLE),
            RolesError(msg.sender, RoleErrorType.OnlyGuardianOrAdmin)
        );
        tokenData.guardian = _newGuardian;
        emit GuardianTransferred(_tokenAddress, msg.sender, _newGuardian);
    }

    /// @notice Adds an AGENT
    /// @param _tokenAddress address of the current token being managed
    /// @param _newAgent to be added
    function addAgent(address _tokenAddress, address _newAgent) external onlyStoredToken(_tokenAddress) {
        onlyIssuerOrGuardian(_tokenAddress, msg.sender);
        TokenData storage tokenData = tokensData[_tokenAddress];
        require(!tokenData.agents[_newAgent], AgentError(_tokenAddress, _newAgent, AgentErrorType.Exists));
        tokenData.agents[_newAgent] = true;
        emit AgentAdded(_tokenAddress, msg.sender, _newAgent);
    }

    /// @notice Deletes an AGENT
    /// @param _tokenAddress address of the current token being managed
    /// @param _agent to be removed
    function removeAgent(address _tokenAddress, address _agent) external onlyStoredToken(_tokenAddress) {
        onlyIssuerOrGuardian(_tokenAddress, msg.sender);
        TokenData storage tokenData = tokensData[_tokenAddress];
        require(tokenData.agents[_agent], AgentError(_tokenAddress, _agent, AgentErrorType.NotExists));

        require(
            !_agentHasContractsAssigned(_tokenAddress, _agent),
            AgentError(_tokenAddress, _agent, AgentErrorType.HasContractsAssigned)
        );

        delete tokenData.agents[_agent];
        emit AgentRemoved(_tokenAddress, msg.sender, _agent);
    }

    /// @notice Transfers the authorization contracts to a new Agent
    /// @param _tokenAddress address of the current token being managed
    /// @param _newAgent to link the authorization list
    /// @param _oldAgent to unlink the authrization list
    function transferAgentList(
        address _tokenAddress,
        address _newAgent,
        address _oldAgent
    ) external onlyStoredToken(_tokenAddress) {
        TokenData storage tokenData = tokensData[_tokenAddress];

        if (!tokenData.isOnSafeguard) {
            require(
                msg.sender == tokenData.issuer || tokenData.agents[msg.sender],
                RolesError(msg.sender, RoleErrorType.OnlyAgentOrIssuerOnActive)
            );
        } else {
            require(msg.sender == tokenData.guardian, RolesError(msg.sender, RoleErrorType.OnlyGuardianOnSafeguard));
        }
        require(tokenData.authorizationContracts.length > 0, ValidationError(ValidationErrorType.AuthListEmpty));
        require(_newAgent != _oldAgent, NewAgentEqOldAgent(_newAgent));
        require(tokenData.agents[_oldAgent], AgentError(_tokenAddress, _oldAgent, AgentErrorType.NotExists));

        if (msg.sender != tokenData.issuer && msg.sender != tokenData.guardian) {
            require(_oldAgent == msg.sender, ListNotOwned(_oldAgent, msg.sender));
        }
        require(tokenData.agents[_newAgent], AgentError(_tokenAddress, _newAgent, AgentErrorType.NotExists));

        (bool executionOk, bool changed) = _changeAuthorizationOwnership(_tokenAddress, _newAgent, _oldAgent);
        // this 2 lines below should never happen. The change list owner should always be successfull
        // because of the requires validating the information before calling _changeAuthorizationOwnership
        require(executionOk, ValidationError(ValidationErrorType.AuthListOwnershipTransferFailed));
        require(changed, ValidationError(ValidationErrorType.AgentWithoutContracts));
        emit AgentAuthorizationListTransferred(_tokenAddress, msg.sender, _newAgent, _oldAgent);
    }

    /// @notice Adds an address to the authorization list
    /// @param _tokenAddress address of the current token being managed
    /// @param _contractAddress the address to be added
    function addToAuthorizationList(
        address _tokenAddress,
        address _contractAddress
    ) external onlyStoredToken(_tokenAddress) onlyAgent(_tokenAddress, msg.sender) {
        require(
            _isContract(_contractAddress),
            ContractError(_tokenAddress, _contractAddress, ContractErrorType.NotAContract)
        );
        TokenData storage tokenData = tokensData[_tokenAddress];
        require(
            tokenData.authorizationsPerAgent[_contractAddress] == address(0),
            ContractError(_tokenAddress, _contractAddress, ContractErrorType.BelongsToAgent)
        );
        tokenData.authorizationContracts.push(_contractAddress);
        tokenData.authorizationsPerAgent[_contractAddress] = msg.sender;
        emit AddedToAuthorizationContracts(_tokenAddress, _contractAddress, msg.sender);
    }

    /// @notice Removes an address from the authorization list
    /// @param _tokenAddress address of the current token being managed
    /// @param _contractAddress the address to be removed
    function removeFromAuthorizationList(
        address _tokenAddress,
        address _contractAddress
    ) external onlyStoredToken(_tokenAddress) onlyAgent(_tokenAddress, msg.sender) {
        require(
            _isContract(_contractAddress),
            ContractError(_tokenAddress, _contractAddress, ContractErrorType.NotAContract)
        );
        TokenData storage tokenData = tokensData[_tokenAddress];
        require(
            tokenData.authorizationsPerAgent[_contractAddress] != address(0),
            ContractError(_tokenAddress, _contractAddress, ContractErrorType.NotFound)
        );
        require(
            tokenData.authorizationsPerAgent[_contractAddress] == msg.sender,
            ContractError(_tokenAddress, _contractAddress, ContractErrorType.NotManagedByCaller)
        );

        // this line below should never happen. The removal should always be successfull
        // because of the require validating the caller before _removeFromAuthorizationArray
        require(
            _removeFromAuthorizationArray(_tokenAddress, _contractAddress),
            ValidationError(ValidationErrorType.RemovingFromAuthFailed)
        );

        delete tokenData.authorizationsPerAgent[_contractAddress];
        emit RemovedFromAuthorizationContracts(_tokenAddress, _contractAddress, msg.sender);
    }

    /// @notice Adds an address to the blacklist
    /// @param _tokenAddress address of the current token being managed
    /// @param _account the address to be blacklisted
    function addMemberToBlacklist(address _tokenAddress, address _account) external onlyStoredToken(_tokenAddress) {
        onlyIssuerOrGuardian(_tokenAddress, msg.sender);
        TokenData storage tokenData = tokensData[_tokenAddress];
        require(!tokenData.blacklist[_account], BlacklistError(_account, BlacklistErrorType.Blacklisted));
        tokenData.blacklist[_account] = true;
        emit AddedToBlacklist(_tokenAddress, _account, msg.sender);
    }

    /// @notice Removes an address from the blacklist
    /// @param _tokenAddress address of the current token being managed
    /// @param _account the address to be removed from the blacklisted
    function removeMemberFromBlacklist(
        address _tokenAddress,
        address _account
    ) external onlyStoredToken(_tokenAddress) {
        onlyIssuerOrGuardian(_tokenAddress, msg.sender);
        TokenData storage tokenData = tokensData[_tokenAddress];
        require(tokenData.blacklist[_account], BlacklistError(_account, BlacklistErrorType.NotBlacklisted));
        delete tokenData.blacklist[_account];
        emit RemovedFromBlacklist(_tokenAddress, _account, msg.sender);
    }

    /// @notice Register the asset tokens and its rates in this contract
    /// @param _tokenAddress address of the current token being managed
    /// @param _issuer address of the contract issuer
    /// @param _guardian address of the contract guardian
    /// @return bool true if operation was successful
    function registerAssetToken(
        address _tokenAddress,
        address _issuer,
        address _guardian
    )
        external
        onlyRole(ASSET_DEPLOYER_ROLE)
        requireNonEmptyAddress(_tokenAddress)
        requireNonEmptyAddress(_issuer)
        requireNonEmptyAddress(_guardian)
        returns (bool)
    {
        TokenData storage tokenData = tokensData[_tokenAddress];
        require(tokenData.issuer == address(0), TokenError(_tokenAddress, TokenErrorType.Registered));
        require(
            _isContract(_tokenAddress),
            ContractError(_tokenAddress, address(this), ContractErrorType.NotAContract)
        );

        tokenData.issuer = _issuer;
        tokenData.guardian = _guardian;
        tokenData.rate = DECIMALS;
        tokenData.lastUpdate = block.timestamp;

        emit TokenRegistered(_tokenAddress, msg.sender);
        return true;
    }

    /// @notice Deletes the asset token from this contract
    /// @notice It has no real use (I think should be removed)
    /// @param _tokenAddress address of the current token being managed
    function deleteAssetToken(address _tokenAddress) external onlyStoredToken(_tokenAddress) {
        onlyUnfrozenContract(_tokenAddress);
        onlyIssuerOrGuardian(_tokenAddress, msg.sender);
        delete tokensData[_tokenAddress];
        emit TokenDeleted(_tokenAddress, msg.sender);
    }

    /// @notice Set the contract into Safeguard)
    /// @param _tokenAddress address of the current token being managed
    /// @return bool true if operation was successful
    function setContractToSafeguard(address _tokenAddress) external onlyStoredToken(_tokenAddress) returns (bool) {
        onlyUnfrozenContract(_tokenAddress);
        onlyActiveContract(_tokenAddress);
        require(msg.sender == _tokenAddress, ContractError(_tokenAddress, msg.sender, ContractErrorType.NotAContract));
        tokensData[_tokenAddress].isOnSafeguard = true;
        emit ChangedToSafeGuard(_tokenAddress);
        return true;
    }

    /// @notice Freeze the contract
    /// @param _tokenAddress address of the current token being managed
    /// @return bool true if operation was successful
    function freezeContract(address _tokenAddress) external onlyStoredToken(_tokenAddress) returns (bool) {
        require(msg.sender == _tokenAddress, ContractError(_tokenAddress, msg.sender, ContractErrorType.NotAContract));
        TokenData storage tokenData = tokensData[_tokenAddress];
        require(!tokenData.isFrozen, TokenError(_tokenAddress, TokenErrorType.Frozen));

        tokenData.isFrozen = true;
        emit FrozenContract(_tokenAddress);
        return true;
    }

    /// @notice Unfreeze the contract
    /// @param _tokenAddress address of the current token being managed
    /// @return bool true if operation was successful
    function unfreezeContract(address _tokenAddress) external onlyStoredToken(_tokenAddress) returns (bool) {
        require(msg.sender == _tokenAddress, ContractError(_tokenAddress, msg.sender, ContractErrorType.NotAContract));
        TokenData storage tokenData = tokensData[_tokenAddress];
        require(tokenData.isFrozen, TokenError(_tokenAddress, TokenErrorType.NotFrozen));

        tokenData.isFrozen = false;
        emit UnfrozenContract(_tokenAddress);
        return true;
    }

    /// @notice Check if the token contract is Active
    /// @param _tokenAddress address of the current token being managed
    function onlyActiveContract(address _tokenAddress) public view {
        require(
            !tokensData[_tokenAddress].isOnSafeguard,
            TokenError(_tokenAddress, TokenErrorType.NotActiveOnSafeguard)
        );
    }

    /// @notice Check if the token contract is Not frozen
    /// @param tokenAddress address of the current token being managed
    function onlyUnfrozenContract(address tokenAddress) public view {
        require(!tokensData[tokenAddress].isFrozen, TokenError(tokenAddress, TokenErrorType.Frozen));
    }

    /// @notice Check if sender is the ISSUER
    /// @param _tokenAddress address of the current token being managed
    /// @param _functionCaller the caller of the function where this is used
    function onlyIssuer(address _tokenAddress, address _functionCaller) external view {
        require(
            _functionCaller == tokensData[_tokenAddress].issuer,
            RolesError(_functionCaller, RoleErrorType.OnlyIssuer)
        );
    }

    /// @notice Check if sender is AGENT_or ISSUER
    /// @param _tokenAddress address of the current token being managed
    /// @param _functionCaller the caller of the function where this is used
    function onlyIssuerOrAgent(address _tokenAddress, address _functionCaller) external view {
        TokenData storage data = tokensData[_tokenAddress];
        require(
            _functionCaller == data.issuer || data.agents[_functionCaller],
            RolesError(_functionCaller, RoleErrorType.OnlyIssuerOrAgent)
        );
    }

    /// @notice Check if sender is GUARDIAN or ISSUER
    /// @param _tokenAddress address of the current token being managed
    /// @param _functionCaller the caller of the function where this is used
    function onlyIssuerOrGuardian(address _tokenAddress, address _functionCaller) public view {
        TokenData storage data = tokensData[_tokenAddress];
        if (data.isOnSafeguard) {
            require(
                _functionCaller == data.guardian,
                RolesError(_functionCaller, RoleErrorType.OnlyGuardianOnSafeguard)
            );
        } else {
            require(_functionCaller == data.issuer, RolesError(_functionCaller, RoleErrorType.OnlyIssuerOnActive));
        }
    }

    /// @notice Return if the account can transfer on safeguard
    /// @param _tokenAddress address of the current token being managed
    /// @param _account the account to get info from
    /// @return isAllowed bool true or false
    function isAllowedTransferOnSafeguard(
        address _tokenAddress,
        address _account
    ) external view onlyStoredToken(_tokenAddress) returns (bool isAllowed) {
        isAllowed = tokensData[_tokenAddress].safeguardTransferAllow[_account];
    }

    /// @notice Get if the contract is on SafeGuard or not
    /// @param _tokenAddress address of the current token being managed
    /// @return bool true if the contract is on SafeGuard
    function isOnSafeguard(address _tokenAddress) external view onlyStoredToken(_tokenAddress) returns (bool) {
        return tokensData[_tokenAddress].isOnSafeguard;
    }

    /// @notice Get if the contract is frozen or not
    /// @param _tokenAddress address of the current token being managed
    /// @return isFrozen bool true if the contract is frozen
    function isContractFrozen(
        address _tokenAddress
    ) external view onlyStoredToken(_tokenAddress) returns (bool isFrozen) {
        isFrozen = tokensData[_tokenAddress].isFrozen;
    }

    /// @notice Get the issuer of the asset token
    /// @param _tokenAddress address of the current token being managed
    /// @return address the issuer address
    function getIssuer(address _tokenAddress) external view onlyStoredToken(_tokenAddress) returns (address) {
        return tokensData[_tokenAddress].issuer;
    }

    /// @notice Get the guardian of the asset token
    /// @param _tokenAddress address of the current token being managed
    /// @return address the guardian address
    function getGuardian(address _tokenAddress) external view onlyStoredToken(_tokenAddress) returns (address) {
        return tokensData[_tokenAddress].guardian;
    }

    /// @notice Get if the account is blacklisted for the asset token
    /// @param _tokenAddress address of the current token being managed
    /// @return blacklisted bool true if the account is blacklisted
    function isBlacklisted(
        address _tokenAddress,
        address _account
    ) external view onlyStoredToken(_tokenAddress) returns (bool blacklisted) {
        blacklisted = tokensData[_tokenAddress].blacklist[_account];
    }

    /// @notice Get if the account is an agent of the asset token
    /// @param _tokenAddress address of the current token being managed
    /// @return _agent bool true if account is an agent
    function isAgent(
        address _tokenAddress,
        address _agentAddress
    ) external view onlyStoredToken(_tokenAddress) returns (bool _agent) {
        _agent = tokensData[_tokenAddress].agents[_agentAddress];
    }

    /// @notice Get the agent address who was responsable of the validation contract (_contractAddress)
    /// @param _tokenAddress address of the current token being managed
    /// @return addedBy address of the agent
    function authorizationContractAddedBy(
        address _tokenAddress,
        address _contractAddress
    ) external view onlyStoredToken(_tokenAddress) returns (address addedBy) {
        addedBy = tokensData[_tokenAddress].authorizationsPerAgent[_contractAddress];
    }

    /// @notice Get the position (index) in the authorizationContracts array of the authorization contract
    /// @param _tokenAddress address of the current token being managed
    /// @return uint256 the index of the array
    function getIndexByAuthorizationAddress(
        address _tokenAddress,
        address _authorizationContractAddress
    ) external view onlyStoredToken(_tokenAddress) returns (uint256) {
        TokenData storage token = tokensData[_tokenAddress];
        uint256 length = token.authorizationContracts.length;
        for (uint256 i = 0; i < length; ++i) {
            if (token.authorizationContracts[i] == _authorizationContractAddress) {
                return i;
            }
        }
        /// @dev returning this when address is not found
        return maxQtyOfAuthorizationLists + 1;
    }

    /// @notice Get the authorization contract address given an index in authorizationContracts array
    /// @param _tokenAddress address of the current token being managed
    /// @return addressByIndex address the address of the authorization contract
    function getAuthorizationAddressByIndex(
        address _tokenAddress,
        uint256 _index
    ) external view returns (address addressByIndex) {
        TokenData storage token = tokensData[_tokenAddress];
        require(_index < token.authorizationContracts.length, ValidationError(ValidationErrorType.IndexNotExists));
        addressByIndex = token.authorizationContracts[_index];
    }

    /* *
     * [IMPORTANT]
     * ====
     * It is unsafe to assume that an address for which this function returns
     * false is an externally-owned account (EOA) and not a contract.
     *
     * Among others, `isContract` will return false for the following
     * types of addresses:
     *
     *  - an externally-owned account
     *  - a contract in construction
     *  - an address where a contract will be created
     *  - an address where a contract lived, but was destroyed
     * ====
     */

    /// @notice Returns true if `account` is a contract
    /// @param _contractAddress the address to be ckecked
    /// @return bool if `account` is a contract
    function _isContract(address _contractAddress) internal view returns (bool) {
        // This method relies on extcodesize, which returns 0 for contracts in
        // construction, since the code is only stored at the end of the
        // constructor execution.

        uint256 size;
        // solhint-disable-next-line no-inline-assembly
        assembly {
            size := extcodesize(_contractAddress)
        }
        return size > 0;
    }

    /// @notice checks if the agent has a contract from the array list assigned
    /// @param _tokenAddress address of the current token being managed
    /// @param _agent agent to check
    /// @return bool if the agent has any contract assigned
    function _agentHasContractsAssigned(address _tokenAddress, address _agent) internal view returns (bool) {
        TokenData storage token = tokensData[_tokenAddress];
        uint256 length = token.authorizationContracts.length;
        for (uint256 i = 0; i < length; ++i) {
            if (token.authorizationsPerAgent[token.authorizationContracts[i]] == _agent) {
                return true;
            }
        }
        return false;
    }

    /// @notice changes the owner of the contracts auth array
    /// @param _tokenAddress address of the current token being managed
    /// @param _newAgent target agent to link the contracts to
    /// @param _oldAgent source agent to unlink the contracts from
    /// @return bool true if there was no error
    /// @return bool true if authorization ownership has occurred
    function _changeAuthorizationOwnership(
        address _tokenAddress,
        address _newAgent,
        address _oldAgent
    ) internal returns (bool, bool) {
        bool changed = false;
        TokenData storage token = tokensData[_tokenAddress];
        uint256 length = token.authorizationContracts.length;
        for (uint256 i = 0; i < length; ++i) {
            if (token.authorizationsPerAgent[token.authorizationContracts[i]] == _oldAgent) {
                token.authorizationsPerAgent[token.authorizationContracts[i]] = _newAgent;
                changed = true;
            }
        }
        return (true, changed);
    }

    /// @notice removes contract from auth array
    /// @param _tokenAddress address of the current token being managed
    /// @param _contractAddress to be removed
    /// @return bool if address was removed
    function _removeFromAuthorizationArray(address _tokenAddress, address _contractAddress) internal returns (bool) {
        TokenData storage token = tokensData[_tokenAddress];
        uint256 length = token.authorizationContracts.length;
        for (uint256 i = 0; i < length; ++i) {
            if (token.authorizationContracts[i] == _contractAddress) {
                token.authorizationContracts[i] = token.authorizationContracts[length - 1];
                token.authorizationContracts.pop();
                return true;
            }
        }
        // This line below should never happen. Before calling this function,
        // it is known that the address exists in the array
        return false;
    }
}

File 3 of 10 : AssetToken.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;

import { ERC20 } from "solady/src/tokens/ERC20.sol";
import { ReentrancyGuard } from "solady/src/utils/ReentrancyGuard.sol";
import { AssetTokenData } from "./AssetTokenData.sol";

/// @author Swarm Markets
/// @title AssetToken
/// @notice Main Asset Token Contract
contract AssetToken is ERC20, ReentrancyGuard {
    error ZeroAddressPassed();
    error WrongKYA(string kya);

    enum RequestErrorType {
        NotExists,
        Completed,
        Cancelled,
        UnstakeRequested
    }
    error RequestError(uint256 requestID, RequestErrorType errorType);

    enum AmountErrorType {
        ZeroAmount,
        NotEnoughFunds,
        MaxStatePercentReached,
        AmountExceedsStaked,
        MinRedemptionAmountNotReached
    }
    error AmountError(uint256 value1, uint256 value2, AmountErrorType errorType);

    enum ContractErrorType {
        NotAuthorizedOnActive,
        NotAllowedOnSafeguard,
        FreezingError,
        UnfreezingError,
        SafeguardChangeError,
        ContractIsActiveNotOnSafeguard
    }
    error ContractError(address contractAddress, ContractErrorType errorType);

    /// @notice Emitted when the address of the asset token data is set
    event AssetTokenDataChanged(address indexed oldAddress, address indexed newAddress, address indexed caller);

    /// @notice Emitted when kya string is set
    event KyaChanged(string kya, address indexed caller);

    /// @notice Emitted when minimumRedemptionAmount is set
    event MinimumRedemptionAmountChanged(uint256 newAmount, address indexed caller);

    /// @notice Emitted when a mint request is requested
    event MintRequested(
        uint256 indexed mintRequestID,
        address indexed destination,
        uint256 amount,
        address indexed caller
    );

    /// @notice Emitted when a mint request gets approved
    event MintApproved(
        uint256 indexed mintRequestID,
        address indexed destination,
        uint256 amountMinted,
        address indexed caller
    );

    /// @notice Emitted when a redemption request is requested
    event RedemptionRequested(
        uint256 indexed redemptionRequestID,
        uint256 assetTokenAmount,
        uint256 underlyingAssetAmount,
        bool fromStake,
        address indexed caller
    );

    /// @notice Emitted when a redemption request is cancelled
    event RedemptionCanceled(
        uint256 indexed redemptionRequestID,
        address indexed requestReceiver,
        string motive,
        address indexed caller
    );

    /// @notice Emitted when a redemption request is approved
    event RedemptionApproved(
        uint256 indexed redemptionRequestID,
        uint256 assetTokenAmount,
        uint256 underlyingAssetAmount,
        address indexed requestReceiver,
        address indexed caller
    );

    /// @notice Emitted when the token gets bruned
    event TokenBurned(uint256 amount, address indexed caller);

    /// @notice Emitted when the contract change to safeguard
    event SafeguardUnstaked(uint256 amount, address indexed caller);

    /// @dev This is a WAD on DSMATH representing 1
    /// @dev This is a proportion of 1 representing 100%, equal to a WAD
    uint256 public constant DECIMALS_HUNDRED_PERCENT = 10 ** 18;

    /// @dev Used to check access to functions as a kindof modifiers
    uint256 private constant ACTIVE_CONTRACT = 1 << 0;
    uint256 private constant UNFROZEN_CONTRACT = 1 << 1;
    uint256 private constant ONLY_ISSUER = 1 << 2;
    uint256 private constant ONLY_ISSUER_OR_GUARDIAN = 1 << 3;
    uint256 private constant ONLY_ISSUER_OR_AGENT = 1 << 4;

    string private constant AUTOMATIC_REDEMPTION_APPROVAL = "AutomaticRedemptionApproval";

    string private NAME;
    string private SYMBOL;

    /// @notice AssetTokenData Address
    address public assetTokenDataAddress;

    /// @notice Structure to hold the Mint Requests
    struct MintRequest {
        address destination;
        uint256 amount;
        string referenceTo;
        bool completed;
    }
    /// @notice Mint Requests mapping and last ID
    mapping(uint256 => MintRequest) public mintRequests;
    uint256 public mintRequestID;

    /// @notice Structure to hold the Redemption Requests
    struct RedemptionRequest {
        address sender;
        string receipt;
        uint256 assetTokenAmount;
        uint256 underlyingAssetAmount;
        bool completed;
        bool fromStake;
        string approveTxID;
        address canceledBy;
    }
    /// @notice Redemption Requests mapping and last ID
    mapping(uint256 => RedemptionRequest) public redemptionRequests;
    uint256 public redemptionRequestID;

    /// @notice stakedRedemptionRequests is map from requester to request ID
    /// @notice exists to detect that sender already has request from stake function
    mapping(address => uint256) public stakedRedemptionRequests;

    /// @notice mapping to hold each user safeguardStake amoun
    mapping(address => uint256) public safeguardStakes;

    /// @notice sum of the total stakes amounts
    uint256 public totalStakes;

    /// @notice the percetage (on 18 digits)
    /// @notice if this gets overgrown the contract change state
    uint256 public statePercent;

    /// @notice know your asset string
    string public kya;

    /// @notice minimum Redemption Amount (in Asset token value)
    uint256 public minimumRedemptionAmount;

    modifier requireNonEmptyAddress(address _address) {
        require(_address != address(0), ZeroAddressPassed());
        _;
    }

    /// @notice Constructor: sets the state variables and provide proper checks to deploy
    /// @param _assetTokenData the asset token data contract address
    /// @param _statePercent the state percent to check the safeguard convertion
    /// @param _kya verification link
    /// @param _minimumRedemptionAmount less than this value is not allowed
    /// @param _name of the token
    /// @param _symbol of the token
    constructor(
        address _assetTokenData,
        uint256 _statePercent,
        string memory _kya,
        uint256 _minimumRedemptionAmount,
        string memory _name,
        string memory _symbol
    ) requireNonEmptyAddress(_assetTokenData) {
        require(_statePercent > 0, AmountError(_statePercent, 0, AmountErrorType.ZeroAmount));
        require(
            _statePercent <= DECIMALS_HUNDRED_PERCENT,
            AmountError(_statePercent, DECIMALS_HUNDRED_PERCENT, AmountErrorType.MaxStatePercentReached)
        );
        require(bytes(_kya).length > 3, WrongKYA(_kya));

        NAME = _name;
        SYMBOL = _symbol;
        // IT IS THE WAD EQUIVALENT USED IN DSMATH
        assetTokenDataAddress = _assetTokenData;
        statePercent = _statePercent;
        kya = _kya;
        minimumRedemptionAmount = _minimumRedemptionAmount;
    }

    /// @notice Approves the Mint Request
    /// @param _mintRequestID the ID to be referenced in the mapping
    /// @param _referenceTo reference comment for the issuer
    function approveMint(uint256 _mintRequestID, string memory _referenceTo) public nonReentrant {
        _checkAccessToFunction(ACTIVE_CONTRACT | ONLY_ISSUER);

        MintRequest storage s_req = mintRequests[_mintRequestID];
        MintRequest memory m_req = s_req;
        require(m_req.destination != address(0), RequestError(_mintRequestID, RequestErrorType.NotExists));
        require(!m_req.completed, RequestError(_mintRequestID, RequestErrorType.Completed));

        s_req.completed = true;
        s_req.referenceTo = _referenceTo;

        uint256 currentRate = AssetTokenData(assetTokenDataAddress).update(address(this));
        uint256 amountToMint = (m_req.amount * DECIMALS_HUNDRED_PERCENT) / currentRate;

        _mint(m_req.destination, amountToMint);
        emit MintApproved(_mintRequestID, m_req.destination, amountToMint, msg.sender);
    }

    /// @notice Approves the Redemption Requests
    /// @param _redemptionRequestID redemption request ID to be referenced in the mapping
    /// @param _approveTxID the transaction ID
    function approveRedemption(uint256 _redemptionRequestID, string memory _approveTxID) public {
        _checkAccessToFunction(ONLY_ISSUER_OR_GUARDIAN);
        RedemptionRequest storage s_req = redemptionRequests[_redemptionRequestID];
        RedemptionRequest storage m_req = s_req;

        require(m_req.canceledBy == address(0), RequestError(_redemptionRequestID, RequestErrorType.Cancelled));
        require(m_req.sender != address(0), RequestError(_redemptionRequestID, RequestErrorType.NotExists));
        require(!m_req.completed, RequestError(_redemptionRequestID, RequestErrorType.Completed));
        if (m_req.fromStake)
            require(
                AssetTokenData(assetTokenDataAddress).isOnSafeguard(address(this)),
                ContractError(address(this), ContractErrorType.ContractIsActiveNotOnSafeguard)
            );

        s_req.completed = true;
        s_req.approveTxID = _approveTxID;
        _burn(address(this), m_req.assetTokenAmount);

        emit RedemptionApproved(
            _redemptionRequestID,
            m_req.assetTokenAmount,
            m_req.underlyingAssetAmount,
            m_req.sender,
            msg.sender
        );
    }

    /// @notice Requests an amount of assetToken Redemption
    /// @param _assetTokenAmount the amount of Asset Token to be redeemed
    /// @param _destination the off chain hash of the redemption transaction
    /// @return reqId uint256 redemptionRequest ID to be referenced in the mapping
    function requestRedemption(
        uint256 _assetTokenAmount,
        string calldata _destination
    ) external nonReentrant returns (uint256 reqId) {
        require(_assetTokenAmount > 0, AmountError(_assetTokenAmount, 0, AmountErrorType.ZeroAmount));
        uint256 balance = balanceOf(msg.sender);
        require(balance >= _assetTokenAmount, AmountError(_assetTokenAmount, balance, AmountErrorType.NotEnoughFunds));

        AssetTokenData assetTknDtaContract = AssetTokenData(assetTokenDataAddress);
        address issuer = assetTknDtaContract.getIssuer(address(this));
        address guardian = assetTknDtaContract.getGuardian(address(this));
        bool isOnSafeguard = assetTknDtaContract.isOnSafeguard(address(this));

        if ((!isOnSafeguard && msg.sender != issuer) || (isOnSafeguard && msg.sender != guardian)) {
            uint256 _minimumRedemptionAmount = minimumRedemptionAmount;
            require(
                _assetTokenAmount >= _minimumRedemptionAmount,
                AmountError(_assetTokenAmount, _minimumRedemptionAmount, AmountErrorType.MinRedemptionAmountNotReached)
            );
        }

        uint256 rate = assetTknDtaContract.update(address(this));
        uint256 underlyingAssetAmount = (_assetTokenAmount * rate) / DECIMALS_HUNDRED_PERCENT;

        reqId = redemptionRequestID + 1;
        redemptionRequestID = reqId;

        redemptionRequests[reqId] = RedemptionRequest(
            msg.sender,
            _destination,
            _assetTokenAmount,
            underlyingAssetAmount,
            false,
            false,
            "",
            address(0)
        );

        /// @dev make the transfer to the contract for the amount requested (18 digits)
        _transfer(msg.sender, address(this), _assetTokenAmount);

        /// @dev approve instantly when called by issuer or guardian
        if ((!isOnSafeguard && msg.sender == issuer) || (isOnSafeguard && msg.sender == guardian)) {
            approveRedemption(reqId, AUTOMATIC_REDEMPTION_APPROVAL);
        }

        emit RedemptionRequested(reqId, _assetTokenAmount, underlyingAssetAmount, false, msg.sender);
    }

    /// @notice Performs the Safeguard Stake
    /// @param _amount the assetToken amount to be staked
    /// @param _receipt the off chain hash of the redemption transaction
    function safeguardStake(uint256 _amount, string calldata _receipt) external nonReentrant {
        _checkAccessToFunction(ACTIVE_CONTRACT);
        uint256 balance = balanceOf(msg.sender);
        require(balance >= _amount, AmountError(_amount, balance, AmountErrorType.NotEnoughFunds));

        uint256 _totalStakes = totalStakes + _amount;

        if ((_totalStakes * DECIMALS_HUNDRED_PERCENT) / totalSupply() >= statePercent) {
            require(
                AssetTokenData(assetTokenDataAddress).setContractToSafeguard(address(this)),
                ContractError(address(this), ContractErrorType.SafeguardChangeError)
            );
            /// @dev now the contract is on safeguard
        }

        uint256 _requestID = stakedRedemptionRequests[msg.sender];
        if (_requestID == 0) {
            /// @dev zero means that it's new request
            uint256 reqId = redemptionRequestID + 1;
            _requestID = reqId;

            redemptionRequestID = reqId;
            redemptionRequests[reqId] = RedemptionRequest(
                msg.sender,
                _receipt,
                _amount,
                0,
                false,
                true,
                "",
                address(0)
            );
            stakedRedemptionRequests[msg.sender] = reqId;
        } else {
            /// @dev non zero means the request already exist and need only add amount
            redemptionRequests[_requestID].assetTokenAmount += _amount;
        }

        safeguardStakes[msg.sender] += _amount;
        totalStakes = _totalStakes;

        _transfer(msg.sender, address(this), _amount);

        emit RedemptionRequested(
            _requestID,
            redemptionRequests[_requestID].assetTokenAmount,
            redemptionRequests[_requestID].underlyingAssetAmount,
            true,
            msg.sender
        );
    }

    /// @notice Sets Asset Token Data Address
    /// @param _newAddress value to be set
    function setAssetTokenData(address _newAddress) external requireNonEmptyAddress(_newAddress) {
        _checkAccessToFunction(UNFROZEN_CONTRACT | ONLY_ISSUER_OR_GUARDIAN);
        address oldAddress = assetTokenDataAddress;
        assetTokenDataAddress = _newAddress;
        emit AssetTokenDataChanged(oldAddress, _newAddress, msg.sender);
    }

    /// @notice Requests a mint to the caller
    /// @param _amount the amount to mint in asset token format
    /// @return uint256 request ID to be referenced in the mapping
    function requestMint(uint256 _amount) external returns (uint256) {
        return _requestMint(_amount, msg.sender);
    }

    /// @notice Requests a mint to the _destination address
    /// @param _amount the amount to mint in asset token format
    /// @param _destination the receiver of the tokens
    /// @return uint256 request ID to be referenced in the mapping
    function requestMint(uint256 _amount, address _destination) external returns (uint256) {
        return _requestMint(_amount, _destination);
    }

    /// @notice Sets the verification link
    /// @param _kya value to be set
    function setKya(string calldata _kya) external {
        _checkAccessToFunction(ONLY_ISSUER_OR_GUARDIAN | UNFROZEN_CONTRACT);
        require(bytes(_kya).length > 3, WrongKYA(_kya));
        kya = _kya;
        emit KyaChanged(_kya, msg.sender);
    }

    /// @notice Sets the _minimumRedemptionAmount
    /// @param _minimumRedemptionAmount value to be set
    function setMinimumRedemptionAmount(uint256 _minimumRedemptionAmount) external {
        _checkAccessToFunction(ONLY_ISSUER_OR_GUARDIAN | UNFROZEN_CONTRACT);
        minimumRedemptionAmount = _minimumRedemptionAmount;
        emit MinimumRedemptionAmountChanged(_minimumRedemptionAmount, msg.sender);
    }

    /// @notice Freeze the contract
    function freezeContract() external {
        _checkAccessToFunction(ONLY_ISSUER_OR_GUARDIAN);
        require(
            AssetTokenData(assetTokenDataAddress).freezeContract(address(this)),
            ContractError(address(this), ContractErrorType.FreezingError)
        );
    }

    /// @notice unfreeze the contract
    function unfreezeContract() external {
        _checkAccessToFunction(ONLY_ISSUER_OR_GUARDIAN);
        require(
            AssetTokenData(assetTokenDataAddress).unfreezeContract(address(this)),
            ContractError(address(this), ContractErrorType.UnfreezingError)
        );
    }

    /// @notice Burns a certain amount of tokens
    /// @param _amount qty of assetTokens to be burned
    function burn(uint256 _amount) external {
        _burn(msg.sender, _amount);
        emit TokenBurned(_amount, msg.sender);
    }

    /// @notice Approves the Redemption Requests
    /// @param _redemptionRequestID redemption request ID to be referenced in the mapping
    /// @param _motive motive of the cancelation
    function cancelRedemptionRequest(uint256 _redemptionRequestID, string calldata _motive) external {
        RedemptionRequest storage s_req = redemptionRequests[_redemptionRequestID];
        RedemptionRequest memory m_req = s_req;
        require(m_req.sender != address(0), RequestError(_redemptionRequestID, RequestErrorType.NotExists));
        require(m_req.canceledBy == address(0), RequestError(_redemptionRequestID, RequestErrorType.Cancelled));
        require(!m_req.completed, RequestError(_redemptionRequestID, RequestErrorType.Completed));
        require(!m_req.fromStake, RequestError(_redemptionRequestID, RequestErrorType.UnstakeRequested));

        if (msg.sender != m_req.sender) {
            // not owner of the redemption so guardian or issuer should be the caller
            AssetTokenData(assetTokenDataAddress).onlyIssuerOrGuardian(address(this), msg.sender);
        }

        s_req.assetTokenAmount = 0;
        s_req.underlyingAssetAmount = 0;
        s_req.canceledBy = msg.sender;

        _transfer(address(this), m_req.sender, m_req.assetTokenAmount);

        emit RedemptionCanceled(_redemptionRequestID, m_req.sender, _motive, msg.sender);
    }

    /// @notice Calls to UnStake all the funds
    function safeguardUnstake() external {
        _safeguardUnstake(safeguardStakes[msg.sender]);
    }

    /// @notice Calls to UnStake with a certain amount
    /// @param _amount to be unStaked in asset token
    function safeguardUnstake(uint256 _amount) external {
        _safeguardUnstake(_amount);
    }

    /// @dev Returns the name of the token.
    function name() public view override returns (string memory) {
        return NAME;
    }

    /// @dev Returns the symbol of the token.
    function symbol() public view override returns (string memory) {
        return SYMBOL;
    }

    /// @notice Performs the Mint Request to the destination address
    /// @param _amount entered in the external functions
    /// @param _destination the receiver of the tokens
    /// @return reqId uint256 request ID to be referenced in the mapping
    function _requestMint(uint256 _amount, address _destination) private returns (uint256 reqId) {
        _checkAccessToFunction(ACTIVE_CONTRACT | UNFROZEN_CONTRACT | ONLY_ISSUER_OR_AGENT);
        require(_amount > 0, AmountError(_amount, 0, AmountErrorType.ZeroAmount));

        reqId = mintRequestID + 1;
        mintRequestID = reqId;

        mintRequests[reqId] = MintRequest(_destination, _amount, "", false);

        if (msg.sender == AssetTokenData(assetTokenDataAddress).getIssuer(address(this))) {
            approveMint(reqId, "IssuerMint");
        }

        emit MintRequested(reqId, _destination, _amount, msg.sender);
    }

    /// @notice Hook to be executed before every transfer and mint
    /// @notice This overrides the ERC20 defined function
    /// @param _from the sender
    /// @param _to the receipent
    /// @param _amount the amount (it is not used  but needed to be defined to override)
    function _beforeTokenTransfer(address _from, address _to, uint256 _amount) internal override {
        //  on safeguard the only available transfers are from allowed addresses and guardian
        //  or from an authorized user to this contract
        //  address(this) is added as the _from for approving redemption (burn)
        //  address(this) is added as the _to for requesting redemption (transfer to this contract)
        //  address(0) is added to the condition to allow burn on safeguard
        _checkAccessToFunction(UNFROZEN_CONTRACT);
        AssetTokenData assetTknDtaContract = AssetTokenData(assetTokenDataAddress);

        bool mbah = assetTknDtaContract.mustBeAuthorizedHolders(address(this), _from, _to, _amount);
        if (assetTknDtaContract.isOnSafeguard(address(this))) {
            /// @dev  State is SAFEGUARD
            if (
                // receiver is NOT this contract AND sender is NOT this contract AND sender is NOT guardian
                _to != address(this) &&
                _from != address(this) &&
                _from != assetTknDtaContract.getGuardian(address(this))
            ) {
                require(
                    assetTknDtaContract.isAllowedTransferOnSafeguard(address(this), _from),
                    ContractError(address(this), ContractErrorType.NotAllowedOnSafeguard)
                );
            } else {
                require(mbah, ContractError(address(this), ContractErrorType.NotAuthorizedOnActive));
            }
        } else {
            /// @dev State is ACTIVE
            // this is mint or transfer
            // mint signature: ==> _beforeTokenTransfer(address(0), account, amount);
            // burn signature: ==> _beforeTokenTransfer(account, address(0), amount);
            require(mbah, ContractError(address(this), ContractErrorType.NotAuthorizedOnActive));
        }

        super._beforeTokenTransfer(_from, _to, _amount);
    }

    /// @notice Performs the UnStake with a certain amount
    /// @param _amount to be unStaked in asset token
    function _safeguardUnstake(uint256 _amount) private {
        _checkAccessToFunction(ACTIVE_CONTRACT | UNFROZEN_CONTRACT);
        require(_amount > 0, AmountError(_amount, 0, AmountErrorType.ZeroAmount));
        uint256 _safeguardStakes = safeguardStakes[msg.sender];
        require(
            _safeguardStakes >= _amount,
            AmountError(_amount, _safeguardStakes, AmountErrorType.AmountExceedsStaked)
        );

        safeguardStakes[msg.sender] = _safeguardStakes - _amount;
        totalStakes -= _amount;
        redemptionRequests[stakedRedemptionRequests[msg.sender]].assetTokenAmount -= _amount;

        _transfer(address(this), msg.sender, _amount);

        emit SafeguardUnstaked(_amount, msg.sender);
    }

    /// @notice kindof modifier to frist-check data on functions
    /// @param modifiers an array containing the modifiers to check (the enums)
    function _checkAccessToFunction(uint256 modifiers) private view {
        bool found;
        AssetTokenData assetTknDtaContract = AssetTokenData(assetTokenDataAddress);
        if (modifiers & ACTIVE_CONTRACT != 0) {
            assetTknDtaContract.onlyActiveContract(address(this));
            found = true;
        }
        if (modifiers & UNFROZEN_CONTRACT != 0) {
            assetTknDtaContract.onlyUnfrozenContract(address(this));
            found = true;
        }
        if (modifiers & ONLY_ISSUER != 0) {
            assetTknDtaContract.onlyIssuer(address(this), msg.sender);
            found = true;
        }
        if (modifiers & ONLY_ISSUER_OR_GUARDIAN != 0) {
            assetTknDtaContract.onlyIssuerOrGuardian(address(this), msg.sender);
            found = true;
        }
        if (modifiers & ONLY_ISSUER_OR_AGENT != 0) {
            assetTknDtaContract.onlyIssuerOrAgent(address(this), msg.sender);
            found = true;
        }
        require(found, "AssetToken: access not found");
    }
}

File 4 of 10 : AssetTokenData.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;

import { FixedPointMathLib } from "solady/src/utils/FixedPointMathLib.sol";
import { AccessManager } from "./AccessManager.sol";

/// @author Swarm Markets
/// @title Asset Token Data for Asset Token Contract
/// @notice Manages interest rate calculations for Asset Tokens
contract AssetTokenData is AccessManager {
    using FixedPointMathLib for uint256;

    error MaxQtyOfAuthorizationListsError(uint256 maxQtyOfAuthorizationLists);
    error InterestRateError(uint256 interestRate, uint256 HUNDRED_PERCENT_ANNUAL);

    /// @notice Emitted when the interest rate is set
    event InterestRateStored(
        address indexed token,
        address indexed caller,
        uint256 interestRate,
        bool positiveInterest
    );

    /// @notice Emitted when the rate gets updated
    event RateUpdated(address indexed token, address indexed caller, uint256 newRate, bool positiveInterest);

    uint256 public constant HUNDRED_PERCENT_ANNUAL = 21979553151;

    /// @notice Constructor
    /// @param _maxQtyOfAuthorizationLists max qty for addresses to be added in the authorization list
    constructor(uint256 _maxQtyOfAuthorizationLists) {
        require(
            _maxQtyOfAuthorizationLists > 0 && _maxQtyOfAuthorizationLists < 100,
            MaxQtyOfAuthorizationListsError(_maxQtyOfAuthorizationLists)
        );

        maxQtyOfAuthorizationLists = _maxQtyOfAuthorizationLists;
        _initializeOwner(msg.sender);
        _setRole(msg.sender, DEFAULT_ADMIN_ROLE, true);
    }

    /// @notice Gets the interest rate and positive/negative interest value
    /// @param token address of the current token being managed
    /// @return rate uint256 the interest rate
    /// @return isPositive bool true if it is positive interest, false if it is not
    function getInterestRate(
        address token
    ) external view onlyStoredToken(token) returns (uint256 rate, bool isPositive) {
        TokenData storage data = tokensData[token];
        return (data.interestRate, data.positiveInterest);
    }

    /// @notice Gets the current rate
    /// @param token address of the current token being managed
    /// @return rate uint256 the rate
    function getCurrentRate(address token) external view onlyStoredToken(token) returns (uint256) {
        return tokensData[token].rate;
    }

    /// @notice Gets the timestamp of the last update
    /// @param token address of the current token being managed
    /// @return lastUpd uint256 the last update in block.timestamp format
    function getLastUpdate(address token) external view onlyStoredToken(token) returns (uint256) {
        return tokensData[token].lastUpdate;
    }

    /// @notice Sets the new intereset rate
    /// @param token address of the current token being managed
    /// @param interestRate the value to be set (the value is in percent per seconds)
    /// @param positiveInterest if it's a negative or positive interest
    function setInterestRate(
        address token,
        uint256 interestRate,
        bool positiveInterest
    ) external onlyStoredToken(token) {
        onlyIssuerOrGuardian(token, msg.sender);
        require(interestRate <= HUNDRED_PERCENT_ANNUAL, InterestRateError(interestRate, HUNDRED_PERCENT_ANNUAL));

        update(token);

        TokenData storage data = tokensData[token];
        data.interestRate = interestRate;
        data.positiveInterest = positiveInterest;

        emit InterestRateStored(token, msg.sender, interestRate, positiveInterest);
    }

    /// @notice Update the Structure counting the blocks since the last update and calculating the rate
    /// @param token address of the current token being managed
    function update(address token) public onlyStoredToken(token) returns (uint256 newRate) {
        TokenData storage data = tokensData[token];

        uint256 multiplier = data.positiveInterest ? DECIMALS + data.interestRate : DECIMALS - data.interestRate;
        newRate = (data.rate * multiplier.rpow(block.timestamp - data.lastUpdate, DECIMALS)) / DECIMALS;

        data.rate = newRate;
        data.lastUpdate = block.timestamp;

        emit RateUpdated(token, msg.sender, newRate, data.positiveInterest);
    }
}

File 5 of 10 : IAuthorizationContract.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

/// @author Swarm Markets
/// @title IAuthorizationContracts
/// @notice Provided interface to interact with any contract to check
/// @notice authorization to a certain transaction
interface IAuthorizationContract {
    function isAccountAuthorized(address _user) external view returns (bool);

    function isTxAuthorized(address _tokenAddress, address _from, address _to, uint256 _amount) external returns (bool);
}

File 6 of 10 : EnumerableRoles.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

/// @notice Enumerable multiroles authorization mixin.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/auth/EnumerableRoles.sol)
///
/// @dev Note:
/// This implementation is agnostic to the Ownable that the contract inherits from.
/// It performs a self-staticcall to the `owner()` function to determine the owner.
/// This is useful for situations where the contract inherits from
/// OpenZeppelin's Ownable, such as in LayerZero's OApp contracts.
///
/// This implementation performs a self-staticcall to `MAX_ROLE()` to determine
/// the maximum role that can be set/unset. If the inheriting contract does not
/// have `MAX_ROLE()`, then any role can be set/unset.
///
/// This implementation allows for any uint256 role,
/// it does NOT take in a bitmask of roles.
/// This is to accommodate teams that are allergic to bitwise flags.
///
/// By default, the `owner()` is the only account that is authorized to set roles.
/// This behavior can be changed via overrides.
///
/// This implementation is compatible with any Ownable.
/// This implementation is NOT compatible with OwnableRoles.
abstract contract EnumerableRoles {
    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                           EVENTS                           */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev The status of `role` for `holder` has been set to `active`.
    event RoleSet(address indexed holder, uint256 indexed role, bool indexed active);

    /// @dev `keccak256(bytes("RoleSet(address,uint256,bool)"))`.
    uint256 private constant _ROLE_SET_EVENT_SIGNATURE =
        0xaddc47d7e02c95c00ec667676636d772a589ffbf0663cfd7cd4dd3d4758201b8;

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                       CUSTOM ERRORS                        */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev The index is out of bounds of the role holders array.
    error RoleHoldersIndexOutOfBounds();

    /// @dev Cannot set the role of the zero address.
    error RoleHolderIsZeroAddress();

    /// @dev The role has exceeded the maximum role.
    error InvalidRole();

    /// @dev Unauthorized to perform the action.
    error EnumerableRolesUnauthorized();

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                          STORAGE                           */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev The storage layout of the holders enumerable mapping is given by:
    /// ```
    ///     mstore(0x18, holder)
    ///     mstore(0x04, _ENUMERABLE_ROLES_SLOT_SEED)
    ///     mstore(0x00, role)
    ///     let rootSlot := keccak256(0x00, 0x24)
    ///     let positionSlot := keccak256(0x00, 0x38)
    ///     let holderSlot := add(rootSlot, sload(positionSlot))
    ///     let holderInStorage := shr(96, sload(holderSlot))
    ///     let length := shr(160, shl(160, sload(rootSlot)))
    /// ```
    uint256 private constant _ENUMERABLE_ROLES_SLOT_SEED = 0xee9853bb;

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                  PUBLIC UPDATE FUNCTIONS                   */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Sets the status of `role` of `holder` to `active`.
    function setRole(address holder, uint256 role, bool active) public payable virtual {
        _authorizeSetRole(holder, role, active);
        _setRole(holder, role, active);
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                   PUBLIC READ FUNCTIONS                    */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Returns if `holder` has active `role`.
    function hasRole(address holder, uint256 role) public view virtual returns (bool result) {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x18, holder)
            mstore(0x04, _ENUMERABLE_ROLES_SLOT_SEED)
            mstore(0x00, role)
            result := iszero(iszero(sload(keccak256(0x00, 0x38))))
        }
    }

    /// @dev Returns an array of the holders of `role`.
    function roleHolders(uint256 role) public view virtual returns (address[] memory result) {
        /// @solidity memory-safe-assembly
        assembly {
            result := mload(0x40)
            mstore(0x04, _ENUMERABLE_ROLES_SLOT_SEED)
            mstore(0x00, role)
            let rootSlot := keccak256(0x00, 0x24)
            let rootPacked := sload(rootSlot)
            let n := shr(160, shl(160, rootPacked))
            let o := add(0x20, result)
            mstore(o, shr(96, rootPacked))
            for { let i := 1 } lt(i, n) { i := add(i, 1) } {
                mstore(add(o, shl(5, i)), shr(96, sload(add(rootSlot, i))))
            }
            mstore(result, n)
            mstore(0x40, add(o, shl(5, n)))
        }
    }

    /// @dev Returns the total number of holders of `role`.
    function roleHolderCount(uint256 role) public view virtual returns (uint256 result) {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x04, _ENUMERABLE_ROLES_SLOT_SEED)
            mstore(0x00, role)
            result := shr(160, shl(160, sload(keccak256(0x00, 0x24))))
        }
    }

    /// @dev Returns the holder of `role` at the index `i`.
    function roleHolderAt(uint256 role, uint256 i) public view virtual returns (address result) {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x04, _ENUMERABLE_ROLES_SLOT_SEED)
            mstore(0x00, role)
            let rootSlot := keccak256(0x00, 0x24)
            let rootPacked := sload(rootSlot)
            if iszero(lt(i, shr(160, shl(160, rootPacked)))) {
                mstore(0x00, 0x5694da8e) // `RoleHoldersIndexOutOfBounds()`.
                revert(0x1c, 0x04)
            }
            result := shr(96, rootPacked)
            if i { result := shr(96, sload(add(rootSlot, i))) }
        }
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                     INTERNAL FUNCTIONS                     */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Set the role for holder directly without authorization guard.
    function _setRole(address holder, uint256 role, bool active) internal virtual {
        _validateRole(role);
        /// @solidity memory-safe-assembly
        assembly {
            let holder_ := shl(96, holder)
            if iszero(holder_) {
                mstore(0x00, 0x82550143) // `RoleHolderIsZeroAddress()`.
                revert(0x1c, 0x04)
            }
            mstore(0x18, holder)
            mstore(0x04, _ENUMERABLE_ROLES_SLOT_SEED)
            mstore(0x00, role)
            let rootSlot := keccak256(0x00, 0x24)
            let n := shr(160, shl(160, sload(rootSlot)))
            let positionSlot := keccak256(0x00, 0x38)
            let position := sload(positionSlot)
            for {} 1 {} {
                if iszero(active) {
                    if iszero(position) { break }
                    let nSub := sub(n, 1)
                    if iszero(eq(sub(position, 1), nSub)) {
                        let lastHolder_ := shl(96, shr(96, sload(add(rootSlot, nSub))))
                        sstore(add(rootSlot, sub(position, 1)), lastHolder_)
                        sstore(add(rootSlot, nSub), 0)
                        mstore(0x24, lastHolder_)
                        sstore(keccak256(0x00, 0x38), position)
                    }
                    sstore(rootSlot, or(shl(96, shr(96, sload(rootSlot))), nSub))
                    sstore(positionSlot, 0)
                    break
                }
                if iszero(position) {
                    sstore(add(rootSlot, n), holder_)
                    sstore(positionSlot, add(n, 1))
                    sstore(rootSlot, add(sload(rootSlot), 1))
                }
                break
            }
            // forgefmt: disable-next-item
            log4(0x00, 0x00, _ROLE_SET_EVENT_SIGNATURE, shr(96, holder_), role, iszero(iszero(active)))
        }
    }

    /// @dev Requires the role is not greater than `MAX_ROLE()`.
    /// If `MAX_ROLE()` is not implemented, this is an no-op.
    function _validateRole(uint256 role) internal view virtual {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x00, 0xd24f19d5) // `MAX_ROLE()`.
            if and(
                and(gt(role, mload(0x00)), gt(returndatasize(), 0x1f)),
                staticcall(gas(), address(), 0x1c, 0x04, 0x00, 0x20)
            ) {
                mstore(0x00, 0xd954416a) // `InvalidRole()`.
                revert(0x1c, 0x04)
            }
        }
    }

    /// @dev Checks that the caller is authorized to set the role.
    function _authorizeSetRole(address holder, uint256 role, bool active) internal virtual {
        if (!_enumerableRolesSenderIsContractOwner()) _revertEnumerableRolesUnauthorized();
        // Silence compiler warning on unused variables.
        (holder, role, active) = (holder, role, active);
    }

    /// @dev Returns if `holder` has any roles in `encodedRoles`.
    /// `encodedRoles` is `abi.encode(SAMPLE_ROLE_0, SAMPLE_ROLE_1, ...)`.
    function _hasAnyRoles(address holder, bytes memory encodedRoles)
        internal
        view
        virtual
        returns (bool result)
    {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x18, holder)
            mstore(0x04, _ENUMERABLE_ROLES_SLOT_SEED)
            let end := add(encodedRoles, shl(5, shr(5, mload(encodedRoles))))
            for {} lt(result, lt(encodedRoles, end)) {} {
                encodedRoles := add(0x20, encodedRoles)
                mstore(0x00, mload(encodedRoles))
                result := sload(keccak256(0x00, 0x38))
            }
            result := iszero(iszero(result))
        }
    }

    /// @dev Reverts if `msg.sender` does not have `role`.
    function _checkRole(uint256 role) internal view virtual {
        if (!hasRole(msg.sender, role)) _revertEnumerableRolesUnauthorized();
    }

    /// @dev Reverts if `msg.sender` does not have any role in `encodedRoles`.
    function _checkRoles(bytes memory encodedRoles) internal view virtual {
        if (!_hasAnyRoles(msg.sender, encodedRoles)) _revertEnumerableRolesUnauthorized();
    }

    /// @dev Reverts if `msg.sender` is not the contract owner and does not have `role`.
    function _checkOwnerOrRole(uint256 role) internal view virtual {
        if (!_enumerableRolesSenderIsContractOwner()) _checkRole(role);
    }

    /// @dev Reverts if `msg.sender` is not the contract owner and
    /// does not have any role in `encodedRoles`.
    function _checkOwnerOrRoles(bytes memory encodedRoles) internal view virtual {
        if (!_enumerableRolesSenderIsContractOwner()) _checkRoles(encodedRoles);
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                         MODIFIERS                          */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Marks a function as only callable by an account with `role`.
    modifier onlyRole(uint256 role) virtual {
        _checkRole(role);
        _;
    }

    /// @dev Marks a function as only callable by an account with any role in `encodedRoles`.
    /// `encodedRoles` is `abi.encode(SAMPLE_ROLE_0, SAMPLE_ROLE_1, ...)`.
    modifier onlyRoles(bytes memory encodedRoles) virtual {
        _checkRoles(encodedRoles);
        _;
    }

    /// @dev Marks a function as only callable by the owner or by an account with `role`.
    modifier onlyOwnerOrRole(uint256 role) virtual {
        _checkOwnerOrRole(role);
        _;
    }

    /// @dev Marks a function as only callable by the owner or
    /// by an account with any role in `encodedRoles`.
    /// Checks for ownership first, then checks for roles.
    /// `encodedRoles` is `abi.encode(SAMPLE_ROLE_0, SAMPLE_ROLE_1, ...)`.
    modifier onlyOwnerOrRoles(bytes memory encodedRoles) virtual {
        _checkOwnerOrRoles(encodedRoles);
        _;
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                      PRIVATE HELPERS                       */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Returns if the `msg.sender` is equal to `owner()` on this contract.
    /// If the contract does not have `owner()` implemented, returns false.
    function _enumerableRolesSenderIsContractOwner() private view returns (bool result) {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x00, 0x8da5cb5b) // `owner()`.
            result :=
                and(
                    and(eq(caller(), mload(0x00)), gt(returndatasize(), 0x1f)),
                    staticcall(gas(), address(), 0x1c, 0x04, 0x00, 0x20)
                )
        }
    }

    /// @dev Reverts with `EnumerableRolesUnauthorized()`.
    function _revertEnumerableRolesUnauthorized() private pure {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x00, 0x99152cca) // `EnumerableRolesUnauthorized()`.
            revert(0x1c, 0x04)
        }
    }
}

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

/// @notice Simple single owner authorization mixin.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/auth/Ownable.sol)
///
/// @dev Note:
/// This implementation does NOT auto-initialize the owner to `msg.sender`.
/// You MUST call the `_initializeOwner` in the constructor / initializer.
///
/// While the ownable portion follows
/// [EIP-173](https://eips.ethereum.org/EIPS/eip-173) for compatibility,
/// the nomenclature for the 2-step ownership handover may be unique to this codebase.
abstract contract Ownable {
    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                       CUSTOM ERRORS                        */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev The caller is not authorized to call the function.
    error Unauthorized();

    /// @dev The `newOwner` cannot be the zero address.
    error NewOwnerIsZeroAddress();

    /// @dev The `pendingOwner` does not have a valid handover request.
    error NoHandoverRequest();

    /// @dev Cannot double-initialize.
    error AlreadyInitialized();

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                           EVENTS                           */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev The ownership is transferred from `oldOwner` to `newOwner`.
    /// This event is intentionally kept the same as OpenZeppelin's Ownable to be
    /// compatible with indexers and [EIP-173](https://eips.ethereum.org/EIPS/eip-173),
    /// despite it not being as lightweight as a single argument event.
    event OwnershipTransferred(address indexed oldOwner, address indexed newOwner);

    /// @dev An ownership handover to `pendingOwner` has been requested.
    event OwnershipHandoverRequested(address indexed pendingOwner);

    /// @dev The ownership handover to `pendingOwner` has been canceled.
    event OwnershipHandoverCanceled(address indexed pendingOwner);

    /// @dev `keccak256(bytes("OwnershipTransferred(address,address)"))`.
    uint256 private constant _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE =
        0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0;

    /// @dev `keccak256(bytes("OwnershipHandoverRequested(address)"))`.
    uint256 private constant _OWNERSHIP_HANDOVER_REQUESTED_EVENT_SIGNATURE =
        0xdbf36a107da19e49527a7176a1babf963b4b0ff8cde35ee35d6cd8f1f9ac7e1d;

    /// @dev `keccak256(bytes("OwnershipHandoverCanceled(address)"))`.
    uint256 private constant _OWNERSHIP_HANDOVER_CANCELED_EVENT_SIGNATURE =
        0xfa7b8eab7da67f412cc9575ed43464468f9bfbae89d1675917346ca6d8fe3c92;

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                          STORAGE                           */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev The owner slot is given by:
    /// `bytes32(~uint256(uint32(bytes4(keccak256("_OWNER_SLOT_NOT")))))`.
    /// It is intentionally chosen to be a high value
    /// to avoid collision with lower slots.
    /// The choice of manual storage layout is to enable compatibility
    /// with both regular and upgradeable contracts.
    bytes32 internal constant _OWNER_SLOT =
        0xffffffffffffffffffffffffffffffffffffffffffffffffffffffff74873927;

    /// The ownership handover slot of `newOwner` is given by:
    /// ```
    ///     mstore(0x00, or(shl(96, user), _HANDOVER_SLOT_SEED))
    ///     let handoverSlot := keccak256(0x00, 0x20)
    /// ```
    /// It stores the expiry timestamp of the two-step ownership handover.
    uint256 private constant _HANDOVER_SLOT_SEED = 0x389a75e1;

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                     INTERNAL FUNCTIONS                     */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Override to return true to make `_initializeOwner` prevent double-initialization.
    function _guardInitializeOwner() internal pure virtual returns (bool guard) {}

    /// @dev Initializes the owner directly without authorization guard.
    /// This function must be called upon initialization,
    /// regardless of whether the contract is upgradeable or not.
    /// This is to enable generalization to both regular and upgradeable contracts,
    /// and to save gas in case the initial owner is not the caller.
    /// For performance reasons, this function will not check if there
    /// is an existing owner.
    function _initializeOwner(address newOwner) internal virtual {
        if (_guardInitializeOwner()) {
            /// @solidity memory-safe-assembly
            assembly {
                let ownerSlot := _OWNER_SLOT
                if sload(ownerSlot) {
                    mstore(0x00, 0x0dc149f0) // `AlreadyInitialized()`.
                    revert(0x1c, 0x04)
                }
                // Clean the upper 96 bits.
                newOwner := shr(96, shl(96, newOwner))
                // Store the new value.
                sstore(ownerSlot, or(newOwner, shl(255, iszero(newOwner))))
                // Emit the {OwnershipTransferred} event.
                log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, 0, newOwner)
            }
        } else {
            /// @solidity memory-safe-assembly
            assembly {
                // Clean the upper 96 bits.
                newOwner := shr(96, shl(96, newOwner))
                // Store the new value.
                sstore(_OWNER_SLOT, newOwner)
                // Emit the {OwnershipTransferred} event.
                log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, 0, newOwner)
            }
        }
    }

    /// @dev Sets the owner directly without authorization guard.
    function _setOwner(address newOwner) internal virtual {
        if (_guardInitializeOwner()) {
            /// @solidity memory-safe-assembly
            assembly {
                let ownerSlot := _OWNER_SLOT
                // Clean the upper 96 bits.
                newOwner := shr(96, shl(96, newOwner))
                // Emit the {OwnershipTransferred} event.
                log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, sload(ownerSlot), newOwner)
                // Store the new value.
                sstore(ownerSlot, or(newOwner, shl(255, iszero(newOwner))))
            }
        } else {
            /// @solidity memory-safe-assembly
            assembly {
                let ownerSlot := _OWNER_SLOT
                // Clean the upper 96 bits.
                newOwner := shr(96, shl(96, newOwner))
                // Emit the {OwnershipTransferred} event.
                log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, sload(ownerSlot), newOwner)
                // Store the new value.
                sstore(ownerSlot, newOwner)
            }
        }
    }

    /// @dev Throws if the sender is not the owner.
    function _checkOwner() internal view virtual {
        /// @solidity memory-safe-assembly
        assembly {
            // If the caller is not the stored owner, revert.
            if iszero(eq(caller(), sload(_OWNER_SLOT))) {
                mstore(0x00, 0x82b42900) // `Unauthorized()`.
                revert(0x1c, 0x04)
            }
        }
    }

    /// @dev Returns how long a two-step ownership handover is valid for in seconds.
    /// Override to return a different value if needed.
    /// Made internal to conserve bytecode. Wrap it in a public function if needed.
    function _ownershipHandoverValidFor() internal view virtual returns (uint64) {
        return 48 * 3600;
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                  PUBLIC UPDATE FUNCTIONS                   */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Allows the owner to transfer the ownership to `newOwner`.
    function transferOwnership(address newOwner) public payable virtual onlyOwner {
        /// @solidity memory-safe-assembly
        assembly {
            if iszero(shl(96, newOwner)) {
                mstore(0x00, 0x7448fbae) // `NewOwnerIsZeroAddress()`.
                revert(0x1c, 0x04)
            }
        }
        _setOwner(newOwner);
    }

    /// @dev Allows the owner to renounce their ownership.
    function renounceOwnership() public payable virtual onlyOwner {
        _setOwner(address(0));
    }

    /// @dev Request a two-step ownership handover to the caller.
    /// The request will automatically expire in 48 hours (172800 seconds) by default.
    function requestOwnershipHandover() public payable virtual {
        unchecked {
            uint256 expires = block.timestamp + _ownershipHandoverValidFor();
            /// @solidity memory-safe-assembly
            assembly {
                // Compute and set the handover slot to `expires`.
                mstore(0x0c, _HANDOVER_SLOT_SEED)
                mstore(0x00, caller())
                sstore(keccak256(0x0c, 0x20), expires)
                // Emit the {OwnershipHandoverRequested} event.
                log2(0, 0, _OWNERSHIP_HANDOVER_REQUESTED_EVENT_SIGNATURE, caller())
            }
        }
    }

    /// @dev Cancels the two-step ownership handover to the caller, if any.
    function cancelOwnershipHandover() public payable virtual {
        /// @solidity memory-safe-assembly
        assembly {
            // Compute and set the handover slot to 0.
            mstore(0x0c, _HANDOVER_SLOT_SEED)
            mstore(0x00, caller())
            sstore(keccak256(0x0c, 0x20), 0)
            // Emit the {OwnershipHandoverCanceled} event.
            log2(0, 0, _OWNERSHIP_HANDOVER_CANCELED_EVENT_SIGNATURE, caller())
        }
    }

    /// @dev Allows the owner to complete the two-step ownership handover to `pendingOwner`.
    /// Reverts if there is no existing ownership handover requested by `pendingOwner`.
    function completeOwnershipHandover(address pendingOwner) public payable virtual onlyOwner {
        /// @solidity memory-safe-assembly
        assembly {
            // Compute and set the handover slot to 0.
            mstore(0x0c, _HANDOVER_SLOT_SEED)
            mstore(0x00, pendingOwner)
            let handoverSlot := keccak256(0x0c, 0x20)
            // If the handover does not exist, or has expired.
            if gt(timestamp(), sload(handoverSlot)) {
                mstore(0x00, 0x6f5e8818) // `NoHandoverRequest()`.
                revert(0x1c, 0x04)
            }
            // Set the handover slot to 0.
            sstore(handoverSlot, 0)
        }
        _setOwner(pendingOwner);
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                   PUBLIC READ FUNCTIONS                    */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Returns the owner of the contract.
    function owner() public view virtual returns (address result) {
        /// @solidity memory-safe-assembly
        assembly {
            result := sload(_OWNER_SLOT)
        }
    }

    /// @dev Returns the expiry timestamp for the two-step ownership handover to `pendingOwner`.
    function ownershipHandoverExpiresAt(address pendingOwner)
        public
        view
        virtual
        returns (uint256 result)
    {
        /// @solidity memory-safe-assembly
        assembly {
            // Compute the handover slot.
            mstore(0x0c, _HANDOVER_SLOT_SEED)
            mstore(0x00, pendingOwner)
            // Load the handover slot.
            result := sload(keccak256(0x0c, 0x20))
        }
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                         MODIFIERS                          */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Marks a function as only callable by the owner.
    modifier onlyOwner() virtual {
        _checkOwner();
        _;
    }
}

File 8 of 10 : ERC20.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

/// @notice Simple ERC20 + EIP-2612 implementation.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/tokens/ERC20.sol)
/// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC20.sol)
/// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/ERC20.sol)
///
/// @dev Note:
/// - The ERC20 standard allows minting and transferring to and from the zero address,
///   minting and transferring zero tokens, as well as self-approvals.
///   For performance, this implementation WILL NOT revert for such actions.
///   Please add any checks with overrides if desired.
/// - The `permit` function uses the ecrecover precompile (0x1).
///
/// If you are overriding:
/// - NEVER violate the ERC20 invariant:
///   the total sum of all balances must be equal to `totalSupply()`.
/// - Check that the overridden function is actually used in the function you want to
///   change the behavior of. Much of the code has been manually inlined for performance.
abstract contract ERC20 {
    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                       CUSTOM ERRORS                        */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev The total supply has overflowed.
    error TotalSupplyOverflow();

    /// @dev The allowance has overflowed.
    error AllowanceOverflow();

    /// @dev The allowance has underflowed.
    error AllowanceUnderflow();

    /// @dev Insufficient balance.
    error InsufficientBalance();

    /// @dev Insufficient allowance.
    error InsufficientAllowance();

    /// @dev The permit is invalid.
    error InvalidPermit();

    /// @dev The permit has expired.
    error PermitExpired();

    /// @dev The allowance of Permit2 is fixed at infinity.
    error Permit2AllowanceIsFixedAtInfinity();

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                           EVENTS                           */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Emitted when `amount` tokens is transferred from `from` to `to`.
    event Transfer(address indexed from, address indexed to, uint256 amount);

    /// @dev Emitted when `amount` tokens is approved by `owner` to be used by `spender`.
    event Approval(address indexed owner, address indexed spender, uint256 amount);

    /// @dev `keccak256(bytes("Transfer(address,address,uint256)"))`.
    uint256 private constant _TRANSFER_EVENT_SIGNATURE =
        0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef;

    /// @dev `keccak256(bytes("Approval(address,address,uint256)"))`.
    uint256 private constant _APPROVAL_EVENT_SIGNATURE =
        0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925;

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                          STORAGE                           */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev The storage slot for the total supply.
    uint256 private constant _TOTAL_SUPPLY_SLOT = 0x05345cdf77eb68f44c;

    /// @dev The balance slot of `owner` is given by:
    /// ```
    ///     mstore(0x0c, _BALANCE_SLOT_SEED)
    ///     mstore(0x00, owner)
    ///     let balanceSlot := keccak256(0x0c, 0x20)
    /// ```
    uint256 private constant _BALANCE_SLOT_SEED = 0x87a211a2;

    /// @dev The allowance slot of (`owner`, `spender`) is given by:
    /// ```
    ///     mstore(0x20, spender)
    ///     mstore(0x0c, _ALLOWANCE_SLOT_SEED)
    ///     mstore(0x00, owner)
    ///     let allowanceSlot := keccak256(0x0c, 0x34)
    /// ```
    uint256 private constant _ALLOWANCE_SLOT_SEED = 0x7f5e9f20;

    /// @dev The nonce slot of `owner` is given by:
    /// ```
    ///     mstore(0x0c, _NONCES_SLOT_SEED)
    ///     mstore(0x00, owner)
    ///     let nonceSlot := keccak256(0x0c, 0x20)
    /// ```
    uint256 private constant _NONCES_SLOT_SEED = 0x38377508;

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                         CONSTANTS                          */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev `(_NONCES_SLOT_SEED << 16) | 0x1901`.
    uint256 private constant _NONCES_SLOT_SEED_WITH_SIGNATURE_PREFIX = 0x383775081901;

    /// @dev `keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)")`.
    bytes32 private constant _DOMAIN_TYPEHASH =
        0x8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f;

    /// @dev `keccak256("1")`.
    /// If you need to use a different version, override `_versionHash`.
    bytes32 private constant _DEFAULT_VERSION_HASH =
        0xc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc6;

    /// @dev `keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)")`.
    bytes32 private constant _PERMIT_TYPEHASH =
        0x6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9;

    /// @dev The canonical Permit2 address.
    /// For signature-based allowance granting for single transaction ERC20 `transferFrom`.
    /// To enable, override `_givePermit2InfiniteAllowance()`.
    /// [Github](https://github.com/Uniswap/permit2)
    /// [Etherscan](https://etherscan.io/address/0x000000000022D473030F116dDEE9F6B43aC78BA3)
    address internal constant _PERMIT2 = 0x000000000022D473030F116dDEE9F6B43aC78BA3;

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                       ERC20 METADATA                       */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Returns the name of the token.
    function name() public view virtual returns (string memory);

    /// @dev Returns the symbol of the token.
    function symbol() public view virtual returns (string memory);

    /// @dev Returns the decimals places of the token.
    function decimals() public view virtual returns (uint8) {
        return 18;
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                           ERC20                            */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Returns the amount of tokens in existence.
    function totalSupply() public view virtual returns (uint256 result) {
        /// @solidity memory-safe-assembly
        assembly {
            result := sload(_TOTAL_SUPPLY_SLOT)
        }
    }

    /// @dev Returns the amount of tokens owned by `owner`.
    function balanceOf(address owner) public view virtual returns (uint256 result) {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x0c, _BALANCE_SLOT_SEED)
            mstore(0x00, owner)
            result := sload(keccak256(0x0c, 0x20))
        }
    }

    /// @dev Returns the amount of tokens that `spender` can spend on behalf of `owner`.
    function allowance(address owner, address spender)
        public
        view
        virtual
        returns (uint256 result)
    {
        if (_givePermit2InfiniteAllowance()) {
            if (spender == _PERMIT2) return type(uint256).max;
        }
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x20, spender)
            mstore(0x0c, _ALLOWANCE_SLOT_SEED)
            mstore(0x00, owner)
            result := sload(keccak256(0x0c, 0x34))
        }
    }

    /// @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
    ///
    /// Emits a {Approval} event.
    function approve(address spender, uint256 amount) public virtual returns (bool) {
        if (_givePermit2InfiniteAllowance()) {
            /// @solidity memory-safe-assembly
            assembly {
                // If `spender == _PERMIT2 && amount != type(uint256).max`.
                if iszero(or(xor(shr(96, shl(96, spender)), _PERMIT2), iszero(not(amount)))) {
                    mstore(0x00, 0x3f68539a) // `Permit2AllowanceIsFixedAtInfinity()`.
                    revert(0x1c, 0x04)
                }
            }
        }
        /// @solidity memory-safe-assembly
        assembly {
            // Compute the allowance slot and store the amount.
            mstore(0x20, spender)
            mstore(0x0c, _ALLOWANCE_SLOT_SEED)
            mstore(0x00, caller())
            sstore(keccak256(0x0c, 0x34), amount)
            // Emit the {Approval} event.
            mstore(0x00, amount)
            log3(0x00, 0x20, _APPROVAL_EVENT_SIGNATURE, caller(), shr(96, mload(0x2c)))
        }
        return true;
    }

    /// @dev Transfer `amount` tokens from the caller to `to`.
    ///
    /// Requirements:
    /// - `from` must at least have `amount`.
    ///
    /// Emits a {Transfer} event.
    function transfer(address to, uint256 amount) public virtual returns (bool) {
        _beforeTokenTransfer(msg.sender, to, amount);
        /// @solidity memory-safe-assembly
        assembly {
            // Compute the balance slot and load its value.
            mstore(0x0c, _BALANCE_SLOT_SEED)
            mstore(0x00, caller())
            let fromBalanceSlot := keccak256(0x0c, 0x20)
            let fromBalance := sload(fromBalanceSlot)
            // Revert if insufficient balance.
            if gt(amount, fromBalance) {
                mstore(0x00, 0xf4d678b8) // `InsufficientBalance()`.
                revert(0x1c, 0x04)
            }
            // Subtract and store the updated balance.
            sstore(fromBalanceSlot, sub(fromBalance, amount))
            // Compute the balance slot of `to`.
            mstore(0x00, to)
            let toBalanceSlot := keccak256(0x0c, 0x20)
            // Add and store the updated balance of `to`.
            // Will not overflow because the sum of all user balances
            // cannot exceed the maximum uint256 value.
            sstore(toBalanceSlot, add(sload(toBalanceSlot), amount))
            // Emit the {Transfer} event.
            mstore(0x20, amount)
            log3(0x20, 0x20, _TRANSFER_EVENT_SIGNATURE, caller(), shr(96, mload(0x0c)))
        }
        _afterTokenTransfer(msg.sender, to, amount);
        return true;
    }

    /// @dev Transfers `amount` tokens from `from` to `to`.
    ///
    /// Note: Does not update the allowance if it is the maximum uint256 value.
    ///
    /// Requirements:
    /// - `from` must at least have `amount`.
    /// - The caller must have at least `amount` of allowance to transfer the tokens of `from`.
    ///
    /// Emits a {Transfer} event.
    function transferFrom(address from, address to, uint256 amount) public virtual returns (bool) {
        _beforeTokenTransfer(from, to, amount);
        // Code duplication is for zero-cost abstraction if possible.
        if (_givePermit2InfiniteAllowance()) {
            /// @solidity memory-safe-assembly
            assembly {
                let from_ := shl(96, from)
                if iszero(eq(caller(), _PERMIT2)) {
                    // Compute the allowance slot and load its value.
                    mstore(0x20, caller())
                    mstore(0x0c, or(from_, _ALLOWANCE_SLOT_SEED))
                    let allowanceSlot := keccak256(0x0c, 0x34)
                    let allowance_ := sload(allowanceSlot)
                    // If the allowance is not the maximum uint256 value.
                    if not(allowance_) {
                        // Revert if the amount to be transferred exceeds the allowance.
                        if gt(amount, allowance_) {
                            mstore(0x00, 0x13be252b) // `InsufficientAllowance()`.
                            revert(0x1c, 0x04)
                        }
                        // Subtract and store the updated allowance.
                        sstore(allowanceSlot, sub(allowance_, amount))
                    }
                }
                // Compute the balance slot and load its value.
                mstore(0x0c, or(from_, _BALANCE_SLOT_SEED))
                let fromBalanceSlot := keccak256(0x0c, 0x20)
                let fromBalance := sload(fromBalanceSlot)
                // Revert if insufficient balance.
                if gt(amount, fromBalance) {
                    mstore(0x00, 0xf4d678b8) // `InsufficientBalance()`.
                    revert(0x1c, 0x04)
                }
                // Subtract and store the updated balance.
                sstore(fromBalanceSlot, sub(fromBalance, amount))
                // Compute the balance slot of `to`.
                mstore(0x00, to)
                let toBalanceSlot := keccak256(0x0c, 0x20)
                // Add and store the updated balance of `to`.
                // Will not overflow because the sum of all user balances
                // cannot exceed the maximum uint256 value.
                sstore(toBalanceSlot, add(sload(toBalanceSlot), amount))
                // Emit the {Transfer} event.
                mstore(0x20, amount)
                log3(0x20, 0x20, _TRANSFER_EVENT_SIGNATURE, shr(96, from_), shr(96, mload(0x0c)))
            }
        } else {
            /// @solidity memory-safe-assembly
            assembly {
                let from_ := shl(96, from)
                // Compute the allowance slot and load its value.
                mstore(0x20, caller())
                mstore(0x0c, or(from_, _ALLOWANCE_SLOT_SEED))
                let allowanceSlot := keccak256(0x0c, 0x34)
                let allowance_ := sload(allowanceSlot)
                // If the allowance is not the maximum uint256 value.
                if not(allowance_) {
                    // Revert if the amount to be transferred exceeds the allowance.
                    if gt(amount, allowance_) {
                        mstore(0x00, 0x13be252b) // `InsufficientAllowance()`.
                        revert(0x1c, 0x04)
                    }
                    // Subtract and store the updated allowance.
                    sstore(allowanceSlot, sub(allowance_, amount))
                }
                // Compute the balance slot and load its value.
                mstore(0x0c, or(from_, _BALANCE_SLOT_SEED))
                let fromBalanceSlot := keccak256(0x0c, 0x20)
                let fromBalance := sload(fromBalanceSlot)
                // Revert if insufficient balance.
                if gt(amount, fromBalance) {
                    mstore(0x00, 0xf4d678b8) // `InsufficientBalance()`.
                    revert(0x1c, 0x04)
                }
                // Subtract and store the updated balance.
                sstore(fromBalanceSlot, sub(fromBalance, amount))
                // Compute the balance slot of `to`.
                mstore(0x00, to)
                let toBalanceSlot := keccak256(0x0c, 0x20)
                // Add and store the updated balance of `to`.
                // Will not overflow because the sum of all user balances
                // cannot exceed the maximum uint256 value.
                sstore(toBalanceSlot, add(sload(toBalanceSlot), amount))
                // Emit the {Transfer} event.
                mstore(0x20, amount)
                log3(0x20, 0x20, _TRANSFER_EVENT_SIGNATURE, shr(96, from_), shr(96, mload(0x0c)))
            }
        }
        _afterTokenTransfer(from, to, amount);
        return true;
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                          EIP-2612                          */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev For more performance, override to return the constant value
    /// of `keccak256(bytes(name()))` if `name()` will never change.
    function _constantNameHash() internal view virtual returns (bytes32 result) {}

    /// @dev If you need a different value, override this function.
    function _versionHash() internal view virtual returns (bytes32 result) {
        result = _DEFAULT_VERSION_HASH;
    }

    /// @dev For inheriting contracts to increment the nonce.
    function _incrementNonce(address owner) internal virtual {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x0c, _NONCES_SLOT_SEED)
            mstore(0x00, owner)
            let nonceSlot := keccak256(0x0c, 0x20)
            sstore(nonceSlot, add(1, sload(nonceSlot)))
        }
    }

    /// @dev Returns the current nonce for `owner`.
    /// This value is used to compute the signature for EIP-2612 permit.
    function nonces(address owner) public view virtual returns (uint256 result) {
        /// @solidity memory-safe-assembly
        assembly {
            // Compute the nonce slot and load its value.
            mstore(0x0c, _NONCES_SLOT_SEED)
            mstore(0x00, owner)
            result := sload(keccak256(0x0c, 0x20))
        }
    }

    /// @dev Sets `value` as the allowance of `spender` over the tokens of `owner`,
    /// authorized by a signed approval by `owner`.
    ///
    /// Emits a {Approval} event.
    function permit(
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) public virtual {
        if (_givePermit2InfiniteAllowance()) {
            /// @solidity memory-safe-assembly
            assembly {
                // If `spender == _PERMIT2 && value != type(uint256).max`.
                if iszero(or(xor(shr(96, shl(96, spender)), _PERMIT2), iszero(not(value)))) {
                    mstore(0x00, 0x3f68539a) // `Permit2AllowanceIsFixedAtInfinity()`.
                    revert(0x1c, 0x04)
                }
            }
        }
        bytes32 nameHash = _constantNameHash();
        //  We simply calculate it on-the-fly to allow for cases where the `name` may change.
        if (nameHash == bytes32(0)) nameHash = keccak256(bytes(name()));
        bytes32 versionHash = _versionHash();
        /// @solidity memory-safe-assembly
        assembly {
            // Revert if the block timestamp is greater than `deadline`.
            if gt(timestamp(), deadline) {
                mstore(0x00, 0x1a15a3cc) // `PermitExpired()`.
                revert(0x1c, 0x04)
            }
            let m := mload(0x40) // Grab the free memory pointer.
            // Clean the upper 96 bits.
            owner := shr(96, shl(96, owner))
            spender := shr(96, shl(96, spender))
            // Compute the nonce slot and load its value.
            mstore(0x0e, _NONCES_SLOT_SEED_WITH_SIGNATURE_PREFIX)
            mstore(0x00, owner)
            let nonceSlot := keccak256(0x0c, 0x20)
            let nonceValue := sload(nonceSlot)
            // Prepare the domain separator.
            mstore(m, _DOMAIN_TYPEHASH)
            mstore(add(m, 0x20), nameHash)
            mstore(add(m, 0x40), versionHash)
            mstore(add(m, 0x60), chainid())
            mstore(add(m, 0x80), address())
            mstore(0x2e, keccak256(m, 0xa0))
            // Prepare the struct hash.
            mstore(m, _PERMIT_TYPEHASH)
            mstore(add(m, 0x20), owner)
            mstore(add(m, 0x40), spender)
            mstore(add(m, 0x60), value)
            mstore(add(m, 0x80), nonceValue)
            mstore(add(m, 0xa0), deadline)
            mstore(0x4e, keccak256(m, 0xc0))
            // Prepare the ecrecover calldata.
            mstore(0x00, keccak256(0x2c, 0x42))
            mstore(0x20, and(0xff, v))
            mstore(0x40, r)
            mstore(0x60, s)
            let t := staticcall(gas(), 1, 0x00, 0x80, 0x20, 0x20)
            // If the ecrecover fails, the returndatasize will be 0x00,
            // `owner` will be checked if it equals the hash at 0x00,
            // which evaluates to false (i.e. 0), and we will revert.
            // If the ecrecover succeeds, the returndatasize will be 0x20,
            // `owner` will be compared against the returned address at 0x20.
            if iszero(eq(mload(returndatasize()), owner)) {
                mstore(0x00, 0xddafbaef) // `InvalidPermit()`.
                revert(0x1c, 0x04)
            }
            // Increment and store the updated nonce.
            sstore(nonceSlot, add(nonceValue, t)) // `t` is 1 if ecrecover succeeds.
            // Compute the allowance slot and store the value.
            // The `owner` is already at slot 0x20.
            mstore(0x40, or(shl(160, _ALLOWANCE_SLOT_SEED), spender))
            sstore(keccak256(0x2c, 0x34), value)
            // Emit the {Approval} event.
            log3(add(m, 0x60), 0x20, _APPROVAL_EVENT_SIGNATURE, owner, spender)
            mstore(0x40, m) // Restore the free memory pointer.
            mstore(0x60, 0) // Restore the zero pointer.
        }
    }

    /// @dev Returns the EIP-712 domain separator for the EIP-2612 permit.
    function DOMAIN_SEPARATOR() public view virtual returns (bytes32 result) {
        bytes32 nameHash = _constantNameHash();
        //  We simply calculate it on-the-fly to allow for cases where the `name` may change.
        if (nameHash == bytes32(0)) nameHash = keccak256(bytes(name()));
        bytes32 versionHash = _versionHash();
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40) // Grab the free memory pointer.
            mstore(m, _DOMAIN_TYPEHASH)
            mstore(add(m, 0x20), nameHash)
            mstore(add(m, 0x40), versionHash)
            mstore(add(m, 0x60), chainid())
            mstore(add(m, 0x80), address())
            result := keccak256(m, 0xa0)
        }
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                  INTERNAL MINT FUNCTIONS                   */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Mints `amount` tokens to `to`, increasing the total supply.
    ///
    /// Emits a {Transfer} event.
    function _mint(address to, uint256 amount) internal virtual {
        _beforeTokenTransfer(address(0), to, amount);
        /// @solidity memory-safe-assembly
        assembly {
            let totalSupplyBefore := sload(_TOTAL_SUPPLY_SLOT)
            let totalSupplyAfter := add(totalSupplyBefore, amount)
            // Revert if the total supply overflows.
            if lt(totalSupplyAfter, totalSupplyBefore) {
                mstore(0x00, 0xe5cfe957) // `TotalSupplyOverflow()`.
                revert(0x1c, 0x04)
            }
            // Store the updated total supply.
            sstore(_TOTAL_SUPPLY_SLOT, totalSupplyAfter)
            // Compute the balance slot and load its value.
            mstore(0x0c, _BALANCE_SLOT_SEED)
            mstore(0x00, to)
            let toBalanceSlot := keccak256(0x0c, 0x20)
            // Add and store the updated balance.
            sstore(toBalanceSlot, add(sload(toBalanceSlot), amount))
            // Emit the {Transfer} event.
            mstore(0x20, amount)
            log3(0x20, 0x20, _TRANSFER_EVENT_SIGNATURE, 0, shr(96, mload(0x0c)))
        }
        _afterTokenTransfer(address(0), to, amount);
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                  INTERNAL BURN FUNCTIONS                   */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Burns `amount` tokens from `from`, reducing the total supply.
    ///
    /// Emits a {Transfer} event.
    function _burn(address from, uint256 amount) internal virtual {
        _beforeTokenTransfer(from, address(0), amount);
        /// @solidity memory-safe-assembly
        assembly {
            // Compute the balance slot and load its value.
            mstore(0x0c, _BALANCE_SLOT_SEED)
            mstore(0x00, from)
            let fromBalanceSlot := keccak256(0x0c, 0x20)
            let fromBalance := sload(fromBalanceSlot)
            // Revert if insufficient balance.
            if gt(amount, fromBalance) {
                mstore(0x00, 0xf4d678b8) // `InsufficientBalance()`.
                revert(0x1c, 0x04)
            }
            // Subtract and store the updated balance.
            sstore(fromBalanceSlot, sub(fromBalance, amount))
            // Subtract and store the updated total supply.
            sstore(_TOTAL_SUPPLY_SLOT, sub(sload(_TOTAL_SUPPLY_SLOT), amount))
            // Emit the {Transfer} event.
            mstore(0x00, amount)
            log3(0x00, 0x20, _TRANSFER_EVENT_SIGNATURE, shr(96, shl(96, from)), 0)
        }
        _afterTokenTransfer(from, address(0), amount);
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                INTERNAL TRANSFER FUNCTIONS                 */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Moves `amount` of tokens from `from` to `to`.
    function _transfer(address from, address to, uint256 amount) internal virtual {
        _beforeTokenTransfer(from, to, amount);
        /// @solidity memory-safe-assembly
        assembly {
            let from_ := shl(96, from)
            // Compute the balance slot and load its value.
            mstore(0x0c, or(from_, _BALANCE_SLOT_SEED))
            let fromBalanceSlot := keccak256(0x0c, 0x20)
            let fromBalance := sload(fromBalanceSlot)
            // Revert if insufficient balance.
            if gt(amount, fromBalance) {
                mstore(0x00, 0xf4d678b8) // `InsufficientBalance()`.
                revert(0x1c, 0x04)
            }
            // Subtract and store the updated balance.
            sstore(fromBalanceSlot, sub(fromBalance, amount))
            // Compute the balance slot of `to`.
            mstore(0x00, to)
            let toBalanceSlot := keccak256(0x0c, 0x20)
            // Add and store the updated balance of `to`.
            // Will not overflow because the sum of all user balances
            // cannot exceed the maximum uint256 value.
            sstore(toBalanceSlot, add(sload(toBalanceSlot), amount))
            // Emit the {Transfer} event.
            mstore(0x20, amount)
            log3(0x20, 0x20, _TRANSFER_EVENT_SIGNATURE, shr(96, from_), shr(96, mload(0x0c)))
        }
        _afterTokenTransfer(from, to, amount);
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                INTERNAL ALLOWANCE FUNCTIONS                */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Updates the allowance of `owner` for `spender` based on spent `amount`.
    function _spendAllowance(address owner, address spender, uint256 amount) internal virtual {
        if (_givePermit2InfiniteAllowance()) {
            if (spender == _PERMIT2) return; // Do nothing, as allowance is infinite.
        }
        /// @solidity memory-safe-assembly
        assembly {
            // Compute the allowance slot and load its value.
            mstore(0x20, spender)
            mstore(0x0c, _ALLOWANCE_SLOT_SEED)
            mstore(0x00, owner)
            let allowanceSlot := keccak256(0x0c, 0x34)
            let allowance_ := sload(allowanceSlot)
            // If the allowance is not the maximum uint256 value.
            if not(allowance_) {
                // Revert if the amount to be transferred exceeds the allowance.
                if gt(amount, allowance_) {
                    mstore(0x00, 0x13be252b) // `InsufficientAllowance()`.
                    revert(0x1c, 0x04)
                }
                // Subtract and store the updated allowance.
                sstore(allowanceSlot, sub(allowance_, amount))
            }
        }
    }

    /// @dev Sets `amount` as the allowance of `spender` over the tokens of `owner`.
    ///
    /// Emits a {Approval} event.
    function _approve(address owner, address spender, uint256 amount) internal virtual {
        if (_givePermit2InfiniteAllowance()) {
            /// @solidity memory-safe-assembly
            assembly {
                // If `spender == _PERMIT2 && amount != type(uint256).max`.
                if iszero(or(xor(shr(96, shl(96, spender)), _PERMIT2), iszero(not(amount)))) {
                    mstore(0x00, 0x3f68539a) // `Permit2AllowanceIsFixedAtInfinity()`.
                    revert(0x1c, 0x04)
                }
            }
        }
        /// @solidity memory-safe-assembly
        assembly {
            let owner_ := shl(96, owner)
            // Compute the allowance slot and store the amount.
            mstore(0x20, spender)
            mstore(0x0c, or(owner_, _ALLOWANCE_SLOT_SEED))
            sstore(keccak256(0x0c, 0x34), amount)
            // Emit the {Approval} event.
            mstore(0x00, amount)
            log3(0x00, 0x20, _APPROVAL_EVENT_SIGNATURE, shr(96, owner_), shr(96, mload(0x2c)))
        }
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                     HOOKS TO OVERRIDE                      */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Hook that is called before any transfer of tokens.
    /// This includes minting and burning.
    function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual {}

    /// @dev Hook that is called after any transfer of tokens.
    /// This includes minting and burning.
    function _afterTokenTransfer(address from, address to, uint256 amount) internal virtual {}

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                          PERMIT2                           */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Returns whether to fix the Permit2 contract's allowance at infinity.
    ///
    /// This value should be kept constant after contract initialization,
    /// or else the actual allowance values may not match with the {Approval} events.
    /// For best performance, return a compile-time constant for zero-cost abstraction.
    function _givePermit2InfiniteAllowance() internal view virtual returns (bool) {
        return false;
    }
}

File 9 of 10 : FixedPointMathLib.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

/// @notice Arithmetic library with operations for fixed-point numbers.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/FixedPointMathLib.sol)
/// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/FixedPointMathLib.sol)
library FixedPointMathLib {
    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                       CUSTOM ERRORS                        */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev The operation failed, as the output exceeds the maximum value of uint256.
    error ExpOverflow();

    /// @dev The operation failed, as the output exceeds the maximum value of uint256.
    error FactorialOverflow();

    /// @dev The operation failed, due to an overflow.
    error RPowOverflow();

    /// @dev The mantissa is too big to fit.
    error MantissaOverflow();

    /// @dev The operation failed, due to an multiplication overflow.
    error MulWadFailed();

    /// @dev The operation failed, due to an multiplication overflow.
    error SMulWadFailed();

    /// @dev The operation failed, either due to a multiplication overflow, or a division by a zero.
    error DivWadFailed();

    /// @dev The operation failed, either due to a multiplication overflow, or a division by a zero.
    error SDivWadFailed();

    /// @dev The operation failed, either due to a multiplication overflow, or a division by a zero.
    error MulDivFailed();

    /// @dev The division failed, as the denominator is zero.
    error DivFailed();

    /// @dev The full precision multiply-divide operation failed, either due
    /// to the result being larger than 256 bits, or a division by a zero.
    error FullMulDivFailed();

    /// @dev The output is undefined, as the input is less-than-or-equal to zero.
    error LnWadUndefined();

    /// @dev The input outside the acceptable domain.
    error OutOfDomain();

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                         CONSTANTS                          */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

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

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*              SIMPLIFIED FIXED POINT OPERATIONS             */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Equivalent to `(x * y) / WAD` rounded down.
    function mulWad(uint256 x, uint256 y) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            // Equivalent to `require(y == 0 || x <= type(uint256).max / y)`.
            if gt(x, div(not(0), y)) {
                if y {
                    mstore(0x00, 0xbac65e5b) // `MulWadFailed()`.
                    revert(0x1c, 0x04)
                }
            }
            z := div(mul(x, y), WAD)
        }
    }

    /// @dev Equivalent to `(x * y) / WAD` rounded down.
    function sMulWad(int256 x, int256 y) internal pure returns (int256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := mul(x, y)
            // Equivalent to `require((x == 0 || z / x == y) && !(x == -1 && y == type(int256).min))`.
            if iszero(gt(or(iszero(x), eq(sdiv(z, x), y)), lt(not(x), eq(y, shl(255, 1))))) {
                mstore(0x00, 0xedcd4dd4) // `SMulWadFailed()`.
                revert(0x1c, 0x04)
            }
            z := sdiv(z, WAD)
        }
    }

    /// @dev Equivalent to `(x * y) / WAD` rounded down, but without overflow checks.
    function rawMulWad(uint256 x, uint256 y) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := div(mul(x, y), WAD)
        }
    }

    /// @dev Equivalent to `(x * y) / WAD` rounded down, but without overflow checks.
    function rawSMulWad(int256 x, int256 y) internal pure returns (int256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := sdiv(mul(x, y), WAD)
        }
    }

    /// @dev Equivalent to `(x * y) / WAD` rounded up.
    function mulWadUp(uint256 x, uint256 y) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := mul(x, y)
            // Equivalent to `require(y == 0 || x <= type(uint256).max / y)`.
            if iszero(eq(div(z, y), x)) {
                if y {
                    mstore(0x00, 0xbac65e5b) // `MulWadFailed()`.
                    revert(0x1c, 0x04)
                }
            }
            z := add(iszero(iszero(mod(z, WAD))), div(z, WAD))
        }
    }

    /// @dev Equivalent to `(x * y) / WAD` rounded up, but without overflow checks.
    function rawMulWadUp(uint256 x, uint256 y) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := add(iszero(iszero(mod(mul(x, y), WAD))), div(mul(x, y), WAD))
        }
    }

    /// @dev Equivalent to `(x * WAD) / y` rounded down.
    function divWad(uint256 x, uint256 y) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            // Equivalent to `require(y != 0 && x <= type(uint256).max / WAD)`.
            if iszero(mul(y, lt(x, add(1, div(not(0), WAD))))) {
                mstore(0x00, 0x7c5f487d) // `DivWadFailed()`.
                revert(0x1c, 0x04)
            }
            z := div(mul(x, WAD), y)
        }
    }

    /// @dev Equivalent to `(x * WAD) / y` rounded down.
    function sDivWad(int256 x, int256 y) internal pure returns (int256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := mul(x, WAD)
            // Equivalent to `require(y != 0 && ((x * WAD) / WAD == x))`.
            if iszero(mul(y, eq(sdiv(z, WAD), x))) {
                mstore(0x00, 0x5c43740d) // `SDivWadFailed()`.
                revert(0x1c, 0x04)
            }
            z := sdiv(z, y)
        }
    }

    /// @dev Equivalent to `(x * WAD) / y` rounded down, but without overflow and divide by zero checks.
    function rawDivWad(uint256 x, uint256 y) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := div(mul(x, WAD), y)
        }
    }

    /// @dev Equivalent to `(x * WAD) / y` rounded down, but without overflow and divide by zero checks.
    function rawSDivWad(int256 x, int256 y) internal pure returns (int256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := sdiv(mul(x, WAD), y)
        }
    }

    /// @dev Equivalent to `(x * WAD) / y` rounded up.
    function divWadUp(uint256 x, uint256 y) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            // Equivalent to `require(y != 0 && x <= type(uint256).max / WAD)`.
            if iszero(mul(y, lt(x, add(1, div(not(0), WAD))))) {
                mstore(0x00, 0x7c5f487d) // `DivWadFailed()`.
                revert(0x1c, 0x04)
            }
            z := add(iszero(iszero(mod(mul(x, WAD), y))), div(mul(x, WAD), y))
        }
    }

    /// @dev Equivalent to `(x * WAD) / y` rounded up, but without overflow and divide by zero checks.
    function rawDivWadUp(uint256 x, uint256 y) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := add(iszero(iszero(mod(mul(x, WAD), y))), div(mul(x, WAD), y))
        }
    }

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

    /// @dev Returns `exp(x)`, denominated in `WAD`.
    /// Credit to Remco Bloemen under MIT license: https://2π.com/22/exp-ln
    /// Note: This function is an approximation. Monotonically increasing.
    function expWad(int256 x) internal pure returns (int256 r) {
        unchecked {
            // When the result is less than 0.5 we return zero.
            // This happens when `x <= (log(1e-18) * 1e18) ~ -4.15e19`.
            if (x <= -41446531673892822313) return r;

            /// @solidity memory-safe-assembly
            assembly {
                // When the result is greater than `(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 iszero(slt(x, 135305999368893231589)) {
                    mstore(0x00, 0xa37bfec9) // `ExpOverflow()`.
                    revert(0x1c, 0x04)
                }
            }

            // `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;

            /// @solidity memory-safe-assembly
            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)
            );
        }
    }

    /// @dev Returns `ln(x)`, denominated in `WAD`.
    /// Credit to Remco Bloemen under MIT license: https://2π.com/22/exp-ln
    /// Note: This function is an approximation. Monotonically increasing.
    function lnWad(int256 x) internal pure returns (int256 r) {
        /// @solidity memory-safe-assembly
        assembly {
            // 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.

            // Compute `k = log2(x) - 96`, `r = 159 - k = 255 - log2(x) = 255 ^ log2(x)`.
            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))))
            // We place the check here for more optimal stack operations.
            if iszero(sgt(x, 0)) {
                mstore(0x00, 0x1615e638) // `LnWadUndefined()`.
                revert(0x1c, 0x04)
            }
            // forgefmt: disable-next-item
            r := xor(r, byte(and(0x1f, shr(shr(r, x), 0x8421084210842108cc6318c6db6d54be)),
                0xf8f9f9faf9fdfafbf9fdfcfdfafbfcfef9fafdfafcfcfbfefafafcfbffffffff))

            // Reduce range of x to (1, 2) * 2**96
            // ln(2^k * x) = k * ln(2) + ln(x)
            x := shr(159, shl(r, x))

            // Evaluate using a (8, 8)-term rational approximation.
            // `p` is made monic, we will multiply by a scale factor later.
            // forgefmt: disable-next-item
            let p := sub( // This heavily nested expression is to avoid stack-too-deep for via-ir.
                sar(96, mul(add(43456485725739037958740375743393,
                sar(96, mul(add(24828157081833163892658089445524,
                sar(96, mul(add(3273285459638523848632254066296,
                    x), x))), x))), x)), 11111509109440967052023855526967)
            p := sub(sar(96, mul(p, x)), 45023709667254063763336534515857)
            p := sub(sar(96, mul(p, x)), 14706773417378608786704636184526)
            p := sub(mul(p, x), shl(96, 795164235651350426258249787498))
            // 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.
            let q := add(5573035233440673466300451813936, x)
            q := add(71694874799317883764090561454958, sar(96, mul(x, q)))
            q := add(283447036172924575727196451306956, sar(96, mul(x, q)))
            q := add(401686690394027663651624208769553, sar(96, mul(x, q)))
            q := add(204048457590392012362485061816622, sar(96, mul(x, q)))
            q := add(31853899698501571402653359427138, sar(96, mul(x, q)))
            q := add(909429971244387300277376558375, sar(96, mul(x, q)))

            // `p / q` 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`.

            // The q polynomial is known not to have zeros in the domain.
            // No scaling required because p is already `2**96` too large.
            p := sdiv(p, q)
            // Multiply by the scaling factor: `s * 5**18 * 2**96`, base is now `5**18 * 2**192`.
            p := mul(1677202110996718588342820967067443963516166, p)
            // Add `ln(2) * k * 5**18 * 2**192`.
            // forgefmt: disable-next-item
            p := add(mul(16597577552685614221487285958193947469193820559219878177908093499208371, sub(159, r)), p)
            // Add `ln(2**96 / 10**18) * 5**18 * 2**192`.
            p := add(600920179829731861736702779321621459595472258049074101567377883020018308, p)
            // Base conversion: mul `2**18 / 2**192`.
            r := sar(174, p)
        }
    }

    /// @dev Returns `W_0(x)`, denominated in `WAD`.
    /// See: https://en.wikipedia.org/wiki/Lambert_W_function
    /// a.k.a. Product log function. This is an approximation of the principal branch.
    /// Note: This function is an approximation. Monotonically increasing.
    function lambertW0Wad(int256 x) internal pure returns (int256 w) {
        // forgefmt: disable-next-item
        unchecked {
            if ((w = x) <= -367879441171442322) revert OutOfDomain(); // `x` less than `-1/e`.
            (int256 wad, int256 p) = (int256(WAD), x);
            uint256 c; // Whether we need to avoid catastrophic cancellation.
            uint256 i = 4; // Number of iterations.
            if (w <= 0x1ffffffffffff) {
                if (-0x4000000000000 <= w) {
                    i = 1; // Inputs near zero only take one step to converge.
                } else if (w <= -0x3ffffffffffffff) {
                    i = 32; // Inputs near `-1/e` take very long to converge.
                }
            } else if (uint256(w >> 63) == uint256(0)) {
                /// @solidity memory-safe-assembly
                assembly {
                    // Inline log2 for more performance, since the range is small.
                    let v := shr(49, w)
                    let l := shl(3, lt(0xff, v))
                    l := add(or(l, byte(and(0x1f, shr(shr(l, v), 0x8421084210842108cc6318c6db6d54be)),
                        0x0706060506020504060203020504030106050205030304010505030400000000)), 49)
                    w := sdiv(shl(l, 7), byte(sub(l, 31), 0x0303030303030303040506080c13))
                    c := gt(l, 60)
                    i := add(2, add(gt(l, 53), c))
                }
            } else {
                int256 ll = lnWad(w = lnWad(w));
                /// @solidity memory-safe-assembly
                assembly {
                    // `w = ln(x) - ln(ln(x)) + b * ln(ln(x)) / ln(x)`.
                    w := add(sdiv(mul(ll, 1023715080943847266), w), sub(w, ll))
                    i := add(3, iszero(shr(68, x)))
                    c := iszero(shr(143, x))
                }
                if (c == uint256(0)) {
                    do { // If `x` is big, use Newton's so that intermediate values won't overflow.
                        int256 e = expWad(w);
                        /// @solidity memory-safe-assembly
                        assembly {
                            let t := mul(w, div(e, wad))
                            w := sub(w, sdiv(sub(t, x), div(add(e, t), wad)))
                        }
                        if (p <= w) break;
                        p = w;
                    } while (--i != uint256(0));
                    /// @solidity memory-safe-assembly
                    assembly {
                        w := sub(w, sgt(w, 2))
                    }
                    return w;
                }
            }
            do { // Otherwise, use Halley's for faster convergence.
                int256 e = expWad(w);
                /// @solidity memory-safe-assembly
                assembly {
                    let t := add(w, wad)
                    let s := sub(mul(w, e), mul(x, wad))
                    w := sub(w, sdiv(mul(s, wad), sub(mul(e, t), sdiv(mul(add(t, wad), s), add(t, t)))))
                }
                if (p <= w) break;
                p = w;
            } while (--i != c);
            /// @solidity memory-safe-assembly
            assembly {
                w := sub(w, sgt(w, 2))
            }
            // For certain ranges of `x`, we'll use the quadratic-rate recursive formula of
            // R. Iacono and J.P. Boyd for the last iteration, to avoid catastrophic cancellation.
            if (c == uint256(0)) return w;
            int256 t = w | 1;
            /// @solidity memory-safe-assembly
            assembly {
                x := sdiv(mul(x, wad), t)
            }
            x = (t * (wad + lnWad(x)));
            /// @solidity memory-safe-assembly
            assembly {
                w := sdiv(x, add(wad, t))
            }
        }
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                  GENERAL NUMBER UTILITIES                  */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Returns `a * b == x * y`, with full precision.
    function fullMulEq(uint256 a, uint256 b, uint256 x, uint256 y)
        internal
        pure
        returns (bool result)
    {
        /// @solidity memory-safe-assembly
        assembly {
            result := and(eq(mul(a, b), mul(x, y)), eq(mulmod(x, y, not(0)), mulmod(a, b, not(0))))
        }
    }

    /// @dev Calculates `floor(x * y / d)` with full precision.
    /// Throws if result overflows a uint256 or when `d` is zero.
    /// Credit to Remco Bloemen under MIT license: https://2π.com/21/muldiv
    function fullMulDiv(uint256 x, uint256 y, uint256 d) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            // 512-bit multiply `[p1 p0] = x * y`.
            // Compute the product mod `2**256` and mod `2**256 - 1`
            // then use the Chinese Remainder Theorem to reconstruct
            // the 512 bit result. The result is stored in two 256
            // variables such that `product = p1 * 2**256 + p0`.

            // Temporarily use `z` as `p0` to save gas.
            z := mul(x, y) // Lower 256 bits of `x * y`.
            for {} 1 {} {
                // If overflows.
                if iszero(mul(or(iszero(x), eq(div(z, x), y)), d)) {
                    let mm := mulmod(x, y, not(0))
                    let p1 := sub(mm, add(z, lt(mm, z))) // Upper 256 bits of `x * y`.

                    /*------------------- 512 by 256 division --------------------*/

                    // Make division exact by subtracting the remainder from `[p1 p0]`.
                    let r := mulmod(x, y, d) // Compute remainder using mulmod.
                    let t := and(d, sub(0, d)) // The least significant bit of `d`. `t >= 1`.
                    // Make sure `z` is less than `2**256`. Also prevents `d == 0`.
                    // Placing the check here seems to give more optimal stack operations.
                    if iszero(gt(d, p1)) {
                        mstore(0x00, 0xae47f702) // `FullMulDivFailed()`.
                        revert(0x1c, 0x04)
                    }
                    d := div(d, t) // Divide `d` by `t`, which is a power of two.
                    // Invert `d mod 2**256`
                    // Now that `d` is an odd number, it has an inverse
                    // modulo `2**256` such that `d * inv = 1 mod 2**256`.
                    // Compute the inverse by starting with a seed that is correct
                    // correct for four bits. That is, `d * inv = 1 mod 2**4`.
                    let inv := xor(2, mul(3, d))
                    // Now use 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.
                    inv := mul(inv, sub(2, mul(d, inv))) // inverse mod 2**8
                    inv := mul(inv, sub(2, mul(d, inv))) // inverse mod 2**16
                    inv := mul(inv, sub(2, mul(d, inv))) // inverse mod 2**32
                    inv := mul(inv, sub(2, mul(d, inv))) // inverse mod 2**64
                    inv := mul(inv, sub(2, mul(d, inv))) // inverse mod 2**128
                    z :=
                        mul(
                            // Divide [p1 p0] by the factors of two.
                            // Shift in bits from `p1` into `p0`. For this we need
                            // to flip `t` such that it is `2**256 / t`.
                            or(mul(sub(p1, gt(r, z)), add(div(sub(0, t), t), 1)), div(sub(z, r), t)),
                            mul(sub(2, mul(d, inv)), inv) // inverse mod 2**256
                        )
                    break
                }
                z := div(z, d)
                break
            }
        }
    }

    /// @dev Calculates `floor(x * y / d)` with full precision.
    /// Behavior is undefined if `d` is zero or the final result cannot fit in 256 bits.
    /// Performs the full 512 bit calculation regardless.
    function fullMulDivUnchecked(uint256 x, uint256 y, uint256 d)
        internal
        pure
        returns (uint256 z)
    {
        /// @solidity memory-safe-assembly
        assembly {
            z := mul(x, y)
            let mm := mulmod(x, y, not(0))
            let p1 := sub(mm, add(z, lt(mm, z)))
            let t := and(d, sub(0, d))
            let r := mulmod(x, y, d)
            d := div(d, t)
            let inv := xor(2, mul(3, d))
            inv := mul(inv, sub(2, mul(d, inv)))
            inv := mul(inv, sub(2, mul(d, inv)))
            inv := mul(inv, sub(2, mul(d, inv)))
            inv := mul(inv, sub(2, mul(d, inv)))
            inv := mul(inv, sub(2, mul(d, inv)))
            z :=
                mul(
                    or(mul(sub(p1, gt(r, z)), add(div(sub(0, t), t), 1)), div(sub(z, r), t)),
                    mul(sub(2, mul(d, inv)), inv)
                )
        }
    }

    /// @dev Calculates `floor(x * y / d)` with full precision, rounded up.
    /// Throws if result overflows a uint256 or when `d` is zero.
    /// Credit to Uniswap-v3-core under MIT license:
    /// https://github.com/Uniswap/v3-core/blob/main/contracts/libraries/FullMath.sol
    function fullMulDivUp(uint256 x, uint256 y, uint256 d) internal pure returns (uint256 z) {
        z = fullMulDiv(x, y, d);
        /// @solidity memory-safe-assembly
        assembly {
            if mulmod(x, y, d) {
                z := add(z, 1)
                if iszero(z) {
                    mstore(0x00, 0xae47f702) // `FullMulDivFailed()`.
                    revert(0x1c, 0x04)
                }
            }
        }
    }

    /// @dev Calculates `floor(x * y / 2 ** n)` with full precision.
    /// Throws if result overflows a uint256.
    /// Credit to Philogy under MIT license:
    /// https://github.com/SorellaLabs/angstrom/blob/main/contracts/src/libraries/X128MathLib.sol
    function fullMulDivN(uint256 x, uint256 y, uint8 n) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            // Temporarily use `z` as `p0` to save gas.
            z := mul(x, y) // Lower 256 bits of `x * y`. We'll call this `z`.
            for {} 1 {} {
                if iszero(or(iszero(x), eq(div(z, x), y))) {
                    let k := and(n, 0xff) // `n`, cleaned.
                    let mm := mulmod(x, y, not(0))
                    let p1 := sub(mm, add(z, lt(mm, z))) // Upper 256 bits of `x * y`.
                    //         |      p1     |      z     |
                    // Before: | p1_0 ¦ p1_1 | z_0  ¦ z_1 |
                    // Final:  |   0  ¦ p1_0 | p1_1 ¦ z_0 |
                    // Check that final `z` doesn't overflow by checking that p1_0 = 0.
                    if iszero(shr(k, p1)) {
                        z := add(shl(sub(256, k), p1), shr(k, z))
                        break
                    }
                    mstore(0x00, 0xae47f702) // `FullMulDivFailed()`.
                    revert(0x1c, 0x04)
                }
                z := shr(and(n, 0xff), z)
                break
            }
        }
    }

    /// @dev Returns `floor(x * y / d)`.
    /// Reverts if `x * y` overflows, or `d` is zero.
    function mulDiv(uint256 x, uint256 y, uint256 d) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := mul(x, y)
            // Equivalent to `require(d != 0 && (y == 0 || x <= type(uint256).max / y))`.
            if iszero(mul(or(iszero(x), eq(div(z, x), y)), d)) {
                mstore(0x00, 0xad251c27) // `MulDivFailed()`.
                revert(0x1c, 0x04)
            }
            z := div(z, d)
        }
    }

    /// @dev Returns `ceil(x * y / d)`.
    /// Reverts if `x * y` overflows, or `d` is zero.
    function mulDivUp(uint256 x, uint256 y, uint256 d) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := mul(x, y)
            // Equivalent to `require(d != 0 && (y == 0 || x <= type(uint256).max / y))`.
            if iszero(mul(or(iszero(x), eq(div(z, x), y)), d)) {
                mstore(0x00, 0xad251c27) // `MulDivFailed()`.
                revert(0x1c, 0x04)
            }
            z := add(iszero(iszero(mod(z, d))), div(z, d))
        }
    }

    /// @dev Returns `x`, the modular multiplicative inverse of `a`, such that `(a * x) % n == 1`.
    function invMod(uint256 a, uint256 n) internal pure returns (uint256 x) {
        /// @solidity memory-safe-assembly
        assembly {
            let g := n
            let r := mod(a, n)
            for { let y := 1 } 1 {} {
                let q := div(g, r)
                let t := g
                g := r
                r := sub(t, mul(r, q))
                let u := x
                x := y
                y := sub(u, mul(y, q))
                if iszero(r) { break }
            }
            x := mul(eq(g, 1), add(x, mul(slt(x, 0), n)))
        }
    }

    /// @dev Returns `ceil(x / d)`.
    /// Reverts if `d` is zero.
    function divUp(uint256 x, uint256 d) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            if iszero(d) {
                mstore(0x00, 0x65244e4e) // `DivFailed()`.
                revert(0x1c, 0x04)
            }
            z := add(iszero(iszero(mod(x, d))), div(x, d))
        }
    }

    /// @dev Returns `max(0, x - y)`.
    function zeroFloorSub(uint256 x, uint256 y) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := mul(gt(x, y), sub(x, y))
        }
    }

    /// @dev Returns `condition ? x : y`, without branching.
    function ternary(bool condition, uint256 x, uint256 y) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := xor(x, mul(xor(x, y), iszero(condition)))
        }
    }

    /// @dev Returns `condition ? x : y`, without branching.
    function ternary(bool condition, bytes32 x, bytes32 y) internal pure returns (bytes32 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := xor(x, mul(xor(x, y), iszero(condition)))
        }
    }

    /// @dev Returns `condition ? x : y`, without branching.
    function ternary(bool condition, address x, address y) internal pure returns (address z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := xor(x, mul(xor(x, y), iszero(condition)))
        }
    }

    /// @dev Exponentiate `x` to `y` by squaring, denominated in base `b`.
    /// Reverts if the computation overflows.
    function rpow(uint256 x, uint256 y, uint256 b) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := mul(b, iszero(y)) // `0 ** 0 = 1`. Otherwise, `0 ** n = 0`.
            if x {
                z := xor(b, mul(xor(b, x), and(y, 1))) // `z = isEven(y) ? scale : x`
                let half := shr(1, b) // Divide `b` by 2.
                // Divide `y` by 2 every iteration.
                for { y := shr(1, y) } y { y := shr(1, y) } {
                    let xx := mul(x, x) // Store x squared.
                    let xxRound := add(xx, half) // Round to the nearest number.
                    // Revert if `xx + half` overflowed, or if `x ** 2` overflows.
                    if or(lt(xxRound, xx), shr(128, x)) {
                        mstore(0x00, 0x49f7642b) // `RPowOverflow()`.
                        revert(0x1c, 0x04)
                    }
                    x := div(xxRound, b) // Set `x` to scaled `xxRound`.
                    // If `y` is odd:
                    if and(y, 1) {
                        let zx := mul(z, x) // Compute `z * x`.
                        let zxRound := add(zx, half) // Round to the nearest number.
                        // If `z * x` overflowed or `zx + half` overflowed:
                        if or(xor(div(zx, x), z), lt(zxRound, zx)) {
                            // Revert if `x` is non-zero.
                            if x {
                                mstore(0x00, 0x49f7642b) // `RPowOverflow()`.
                                revert(0x1c, 0x04)
                            }
                        }
                        z := div(zxRound, b) // Return properly scaled `zxRound`.
                    }
                }
            }
        }
    }

    /// @dev Returns the square root of `x`, rounded down.
    function sqrt(uint256 x) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            // `floor(sqrt(2**15)) = 181`. `sqrt(2**15) - 181 = 2.84`.
            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.

            // Let `y = x / 2**r`. We check `y >= 2**(k + 8)`
            // but shift right by `k` bits to ensure that if `x >= 256`, then `y >= 256`.
            let r := shl(7, lt(0xffffffffffffffffffffffffffffffffff, x))
            r := or(r, shl(6, lt(0xffffffffffffffffff, shr(r, x))))
            r := or(r, shl(5, lt(0xffffffffff, shr(r, x))))
            r := or(r, shl(4, lt(0xffffff, shr(r, x))))
            z := shl(shr(1, r), 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(shr(r, x), 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
            z := sub(z, lt(div(x, z), z))
        }
    }

    /// @dev Returns the cube root of `x`, rounded down.
    /// Credit to bout3fiddy and pcaversaccio under AGPLv3 license:
    /// https://github.com/pcaversaccio/snekmate/blob/main/src/utils/Math.vy
    /// Formally verified by xuwinnie:
    /// https://github.com/vectorized/solady/blob/main/audits/xuwinnie-solady-cbrt-proof.pdf
    function cbrt(uint256 x) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            let 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))))
            // Makeshift lookup table to nudge the approximate log2 result.
            z := div(shl(div(r, 3), shl(lt(0xf, shr(r, x)), 0xf)), xor(7, mod(r, 3)))
            // Newton-Raphson's.
            z := div(add(add(div(x, mul(z, z)), z), z), 3)
            z := div(add(add(div(x, mul(z, z)), z), z), 3)
            z := div(add(add(div(x, mul(z, z)), z), z), 3)
            z := div(add(add(div(x, mul(z, z)), z), z), 3)
            z := div(add(add(div(x, mul(z, z)), z), z), 3)
            z := div(add(add(div(x, mul(z, z)), z), z), 3)
            z := div(add(add(div(x, mul(z, z)), z), z), 3)
            // Round down.
            z := sub(z, lt(div(x, mul(z, z)), z))
        }
    }

    /// @dev Returns the square root of `x`, denominated in `WAD`, rounded down.
    function sqrtWad(uint256 x) internal pure returns (uint256 z) {
        unchecked {
            if (x <= type(uint256).max / 10 ** 18) return sqrt(x * 10 ** 18);
            z = (1 + sqrt(x)) * 10 ** 9;
            z = (fullMulDivUnchecked(x, 10 ** 18, z) + z) >> 1;
        }
        /// @solidity memory-safe-assembly
        assembly {
            z := sub(z, gt(999999999999999999, sub(mulmod(z, z, x), 1))) // Round down.
        }
    }

    /// @dev Returns the cube root of `x`, denominated in `WAD`, rounded down.
    /// Formally verified by xuwinnie:
    /// https://github.com/vectorized/solady/blob/main/audits/xuwinnie-solady-cbrt-proof.pdf
    function cbrtWad(uint256 x) internal pure returns (uint256 z) {
        unchecked {
            if (x <= type(uint256).max / 10 ** 36) return cbrt(x * 10 ** 36);
            z = (1 + cbrt(x)) * 10 ** 12;
            z = (fullMulDivUnchecked(x, 10 ** 36, z * z) + z + z) / 3;
        }
        /// @solidity memory-safe-assembly
        assembly {
            let p := x
            for {} 1 {} {
                if iszero(shr(229, p)) {
                    if iszero(shr(199, p)) {
                        p := mul(p, 100000000000000000) // 10 ** 17.
                        break
                    }
                    p := mul(p, 100000000) // 10 ** 8.
                    break
                }
                if iszero(shr(249, p)) { p := mul(p, 100) }
                break
            }
            let t := mulmod(mul(z, z), z, p)
            z := sub(z, gt(lt(t, shr(1, p)), iszero(t))) // Round down.
        }
    }

    /// @dev Returns the factorial of `x`.
    function factorial(uint256 x) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := 1
            if iszero(lt(x, 58)) {
                mstore(0x00, 0xaba0f2a2) // `FactorialOverflow()`.
                revert(0x1c, 0x04)
            }
            for {} x { x := sub(x, 1) } { z := mul(z, x) }
        }
    }

    /// @dev Returns the log2 of `x`.
    /// Equivalent to computing the index of the most significant bit (MSB) of `x`.
    /// Returns 0 if `x` is zero.
    function log2(uint256 x) internal pure returns (uint256 r) {
        /// @solidity memory-safe-assembly
        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))))
            // forgefmt: disable-next-item
            r := or(r, byte(and(0x1f, shr(shr(r, x), 0x8421084210842108cc6318c6db6d54be)),
                0x0706060506020504060203020504030106050205030304010505030400000000))
        }
    }

    /// @dev Returns the log2 of `x`, rounded up.
    /// Returns 0 if `x` is zero.
    function log2Up(uint256 x) internal pure returns (uint256 r) {
        r = log2(x);
        /// @solidity memory-safe-assembly
        assembly {
            r := add(r, lt(shl(r, 1), x))
        }
    }

    /// @dev Returns the log10 of `x`.
    /// Returns 0 if `x` is zero.
    function log10(uint256 x) internal pure returns (uint256 r) {
        /// @solidity memory-safe-assembly
        assembly {
            if iszero(lt(x, 100000000000000000000000000000000000000)) {
                x := div(x, 100000000000000000000000000000000000000)
                r := 38
            }
            if iszero(lt(x, 100000000000000000000)) {
                x := div(x, 100000000000000000000)
                r := add(r, 20)
            }
            if iszero(lt(x, 10000000000)) {
                x := div(x, 10000000000)
                r := add(r, 10)
            }
            if iszero(lt(x, 100000)) {
                x := div(x, 100000)
                r := add(r, 5)
            }
            r := add(r, add(gt(x, 9), add(gt(x, 99), add(gt(x, 999), gt(x, 9999)))))
        }
    }

    /// @dev Returns the log10 of `x`, rounded up.
    /// Returns 0 if `x` is zero.
    function log10Up(uint256 x) internal pure returns (uint256 r) {
        r = log10(x);
        /// @solidity memory-safe-assembly
        assembly {
            r := add(r, lt(exp(10, r), x))
        }
    }

    /// @dev Returns the log256 of `x`.
    /// Returns 0 if `x` is zero.
    function log256(uint256 x) internal pure returns (uint256 r) {
        /// @solidity memory-safe-assembly
        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(shr(3, r), lt(0xff, shr(r, x)))
        }
    }

    /// @dev Returns the log256 of `x`, rounded up.
    /// Returns 0 if `x` is zero.
    function log256Up(uint256 x) internal pure returns (uint256 r) {
        r = log256(x);
        /// @solidity memory-safe-assembly
        assembly {
            r := add(r, lt(shl(shl(3, r), 1), x))
        }
    }

    /// @dev Returns the scientific notation format `mantissa * 10 ** exponent` of `x`.
    /// Useful for compressing prices (e.g. using 25 bit mantissa and 7 bit exponent).
    function sci(uint256 x) internal pure returns (uint256 mantissa, uint256 exponent) {
        /// @solidity memory-safe-assembly
        assembly {
            mantissa := x
            if mantissa {
                if iszero(mod(mantissa, 1000000000000000000000000000000000)) {
                    mantissa := div(mantissa, 1000000000000000000000000000000000)
                    exponent := 33
                }
                if iszero(mod(mantissa, 10000000000000000000)) {
                    mantissa := div(mantissa, 10000000000000000000)
                    exponent := add(exponent, 19)
                }
                if iszero(mod(mantissa, 1000000000000)) {
                    mantissa := div(mantissa, 1000000000000)
                    exponent := add(exponent, 12)
                }
                if iszero(mod(mantissa, 1000000)) {
                    mantissa := div(mantissa, 1000000)
                    exponent := add(exponent, 6)
                }
                if iszero(mod(mantissa, 10000)) {
                    mantissa := div(mantissa, 10000)
                    exponent := add(exponent, 4)
                }
                if iszero(mod(mantissa, 100)) {
                    mantissa := div(mantissa, 100)
                    exponent := add(exponent, 2)
                }
                if iszero(mod(mantissa, 10)) {
                    mantissa := div(mantissa, 10)
                    exponent := add(exponent, 1)
                }
            }
        }
    }

    /// @dev Convenience function for packing `x` into a smaller number using `sci`.
    /// The `mantissa` will be in bits [7..255] (the upper 249 bits).
    /// The `exponent` will be in bits [0..6] (the lower 7 bits).
    /// Use `SafeCastLib` to safely ensure that the `packed` number is small
    /// enough to fit in the desired unsigned integer type:
    /// ```
    ///     uint32 packed = SafeCastLib.toUint32(FixedPointMathLib.packSci(777 ether));
    /// ```
    function packSci(uint256 x) internal pure returns (uint256 packed) {
        (x, packed) = sci(x); // Reuse for `mantissa` and `exponent`.
        /// @solidity memory-safe-assembly
        assembly {
            if shr(249, x) {
                mstore(0x00, 0xce30380c) // `MantissaOverflow()`.
                revert(0x1c, 0x04)
            }
            packed := or(shl(7, x), packed)
        }
    }

    /// @dev Convenience function for unpacking a packed number from `packSci`.
    function unpackSci(uint256 packed) internal pure returns (uint256 unpacked) {
        unchecked {
            unpacked = (packed >> 7) * 10 ** (packed & 0x7f);
        }
    }

    /// @dev Returns the average of `x` and `y`. Rounds towards zero.
    function avg(uint256 x, uint256 y) internal pure returns (uint256 z) {
        unchecked {
            z = (x & y) + ((x ^ y) >> 1);
        }
    }

    /// @dev Returns the average of `x` and `y`. Rounds towards negative infinity.
    function avg(int256 x, int256 y) internal pure returns (int256 z) {
        unchecked {
            z = (x >> 1) + (y >> 1) + (x & y & 1);
        }
    }

    /// @dev Returns the absolute value of `x`.
    function abs(int256 x) internal pure returns (uint256 z) {
        unchecked {
            z = (uint256(x) + uint256(x >> 255)) ^ uint256(x >> 255);
        }
    }

    /// @dev Returns the absolute distance between `x` and `y`.
    function dist(uint256 x, uint256 y) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := add(xor(sub(0, gt(x, y)), sub(y, x)), gt(x, y))
        }
    }

    /// @dev Returns the absolute distance between `x` and `y`.
    function dist(int256 x, int256 y) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := add(xor(sub(0, sgt(x, y)), sub(y, x)), sgt(x, y))
        }
    }

    /// @dev Returns the minimum of `x` and `y`.
    function min(uint256 x, uint256 y) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := xor(x, mul(xor(x, y), lt(y, x)))
        }
    }

    /// @dev Returns the minimum of `x` and `y`.
    function min(int256 x, int256 y) internal pure returns (int256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := xor(x, mul(xor(x, y), slt(y, x)))
        }
    }

    /// @dev Returns the maximum of `x` and `y`.
    function max(uint256 x, uint256 y) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := xor(x, mul(xor(x, y), gt(y, x)))
        }
    }

    /// @dev Returns the maximum of `x` and `y`.
    function max(int256 x, int256 y) internal pure returns (int256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := xor(x, mul(xor(x, y), sgt(y, x)))
        }
    }

    /// @dev Returns `x`, bounded to `minValue` and `maxValue`.
    function clamp(uint256 x, uint256 minValue, uint256 maxValue)
        internal
        pure
        returns (uint256 z)
    {
        /// @solidity memory-safe-assembly
        assembly {
            z := xor(x, mul(xor(x, minValue), gt(minValue, x)))
            z := xor(z, mul(xor(z, maxValue), lt(maxValue, z)))
        }
    }

    /// @dev Returns `x`, bounded to `minValue` and `maxValue`.
    function clamp(int256 x, int256 minValue, int256 maxValue) internal pure returns (int256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := xor(x, mul(xor(x, minValue), sgt(minValue, x)))
            z := xor(z, mul(xor(z, maxValue), slt(maxValue, z)))
        }
    }

    /// @dev Returns greatest common divisor of `x` and `y`.
    function gcd(uint256 x, uint256 y) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            for { z := x } y {} {
                let t := y
                y := mod(z, y)
                z := t
            }
        }
    }

    /// @dev Returns `a + (b - a) * (t - begin) / (end - begin)`,
    /// with `t` clamped between `begin` and `end` (inclusive).
    /// Agnostic to the order of (`a`, `b`) and (`end`, `begin`).
    /// If `begins == end`, returns `t <= begin ? a : b`.
    function lerp(uint256 a, uint256 b, uint256 t, uint256 begin, uint256 end)
        internal
        pure
        returns (uint256)
    {
        if (begin > end) (t, begin, end) = (~t, ~begin, ~end);
        if (t <= begin) return a;
        if (t >= end) return b;
        unchecked {
            if (b >= a) return a + fullMulDiv(b - a, t - begin, end - begin);
            return a - fullMulDiv(a - b, t - begin, end - begin);
        }
    }

    /// @dev Returns `a + (b - a) * (t - begin) / (end - begin)`.
    /// with `t` clamped between `begin` and `end` (inclusive).
    /// Agnostic to the order of (`a`, `b`) and (`end`, `begin`).
    /// If `begins == end`, returns `t <= begin ? a : b`.
    function lerp(int256 a, int256 b, int256 t, int256 begin, int256 end)
        internal
        pure
        returns (int256)
    {
        if (begin > end) (t, begin, end) = (~t, ~begin, ~end);
        if (t <= begin) return a;
        if (t >= end) return b;
        // forgefmt: disable-next-item
        unchecked {
            if (b >= a) return int256(uint256(a) + fullMulDiv(uint256(b - a),
                uint256(t - begin), uint256(end - begin)));
            return int256(uint256(a) - fullMulDiv(uint256(a - b),
                uint256(t - begin), uint256(end - begin)));
        }
    }

    /// @dev Returns if `x` is an even number. Some people may need this.
    function isEven(uint256 x) internal pure returns (bool) {
        return x & uint256(1) == uint256(0);
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                   RAW NUMBER OPERATIONS                    */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Returns `x + y`, without checking for overflow.
    function rawAdd(uint256 x, uint256 y) internal pure returns (uint256 z) {
        unchecked {
            z = x + y;
        }
    }

    /// @dev Returns `x + y`, without checking for overflow.
    function rawAdd(int256 x, int256 y) internal pure returns (int256 z) {
        unchecked {
            z = x + y;
        }
    }

    /// @dev Returns `x - y`, without checking for underflow.
    function rawSub(uint256 x, uint256 y) internal pure returns (uint256 z) {
        unchecked {
            z = x - y;
        }
    }

    /// @dev Returns `x - y`, without checking for underflow.
    function rawSub(int256 x, int256 y) internal pure returns (int256 z) {
        unchecked {
            z = x - y;
        }
    }

    /// @dev Returns `x * y`, without checking for overflow.
    function rawMul(uint256 x, uint256 y) internal pure returns (uint256 z) {
        unchecked {
            z = x * y;
        }
    }

    /// @dev Returns `x * y`, without checking for overflow.
    function rawMul(int256 x, int256 y) internal pure returns (int256 z) {
        unchecked {
            z = x * y;
        }
    }

    /// @dev Returns `x / y`, returning 0 if `y` is zero.
    function rawDiv(uint256 x, uint256 y) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := div(x, y)
        }
    }

    /// @dev Returns `x / y`, returning 0 if `y` is zero.
    function rawSDiv(int256 x, int256 y) internal pure returns (int256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := sdiv(x, y)
        }
    }

    /// @dev Returns `x % y`, returning 0 if `y` is zero.
    function rawMod(uint256 x, uint256 y) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := mod(x, y)
        }
    }

    /// @dev Returns `x % y`, returning 0 if `y` is zero.
    function rawSMod(int256 x, int256 y) internal pure returns (int256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := smod(x, y)
        }
    }

    /// @dev Returns `(x + y) % d`, return 0 if `d` if zero.
    function rawAddMod(uint256 x, uint256 y, uint256 d) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := addmod(x, y, d)
        }
    }

    /// @dev Returns `(x * y) % d`, return 0 if `d` if zero.
    function rawMulMod(uint256 x, uint256 y, uint256 d) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := mulmod(x, y, d)
        }
    }
}

File 10 of 10 : ReentrancyGuard.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

/// @notice Reentrancy guard mixin.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/ReentrancyGuard.sol)
abstract contract ReentrancyGuard {
    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                       CUSTOM ERRORS                        */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Unauthorized reentrant call.
    error Reentrancy();

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                          STORAGE                           */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Equivalent to: `uint72(bytes9(keccak256("_REENTRANCY_GUARD_SLOT")))`.
    /// 9 bytes is large enough to avoid collisions with lower slots,
    /// but not too large to result in excessive bytecode bloat.
    uint256 private constant _REENTRANCY_GUARD_SLOT = 0x929eee149b4bd21268;

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                      REENTRANCY GUARD                      */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Guards a function from reentrancy.
    modifier nonReentrant() virtual {
        /// @solidity memory-safe-assembly
        assembly {
            if eq(sload(_REENTRANCY_GUARD_SLOT), address()) {
                mstore(0x00, 0xab143c06) // `Reentrancy()`.
                revert(0x1c, 0x04)
            }
            sstore(_REENTRANCY_GUARD_SLOT, address())
        }
        _;
        /// @solidity memory-safe-assembly
        assembly {
            sstore(_REENTRANCY_GUARD_SLOT, codesize())
        }
    }

    /// @dev Guards a view function from read-only reentrancy.
    modifier nonReadReentrant() virtual {
        /// @solidity memory-safe-assembly
        assembly {
            if eq(sload(_REENTRANCY_GUARD_SLOT), address()) {
                mstore(0x00, 0xab143c06) // `Reentrancy()`.
                revert(0x1c, 0x04)
            }
        }
        _;
    }
}

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

Contract Security Audit

Contract ABI

[{"inputs":[{"internalType":"address","name":"_assetTokenData","type":"address"},{"internalType":"address","name":"_owner","type":"address"},{"internalType":"uint256","name":"_statePercent","type":"uint256"},{"internalType":"string","name":"_kya","type":"string"},{"internalType":"uint256","name":"_minimumRedemptionAmount","type":"uint256"},{"internalType":"string","name":"_name","type":"string"},{"internalType":"string","name":"_symbol","type":"string"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"AllowanceOverflow","type":"error"},{"inputs":[],"name":"AllowanceUnderflow","type":"error"},{"inputs":[],"name":"AlreadyInitialized","type":"error"},{"inputs":[{"internalType":"uint256","name":"value1","type":"uint256"},{"internalType":"uint256","name":"value2","type":"uint256"},{"internalType":"enum AssetToken.AmountErrorType","name":"errorType","type":"uint8"}],"name":"AmountError","type":"error"},{"inputs":[{"internalType":"address","name":"contractAddress","type":"address"},{"internalType":"enum AssetToken.ContractErrorType","name":"errorType","type":"uint8"}],"name":"ContractError","type":"error"},{"inputs":[],"name":"EnumerableRolesUnauthorized","type":"error"},{"inputs":[],"name":"InsufficientAllowance","type":"error"},{"inputs":[],"name":"InsufficientBalance","type":"error"},{"inputs":[],"name":"InvalidPermit","type":"error"},{"inputs":[],"name":"InvalidRole","type":"error"},{"inputs":[],"name":"NewOwnerIsZeroAddress","type":"error"},{"inputs":[],"name":"NoHandoverRequest","type":"error"},{"inputs":[],"name":"Permit2AllowanceIsFixedAtInfinity","type":"error"},{"inputs":[],"name":"PermitExpired","type":"error"},{"inputs":[],"name":"Reentrancy","type":"error"},{"inputs":[{"internalType":"uint256","name":"requestID","type":"uint256"},{"internalType":"enum AssetToken.RequestErrorType","name":"errorType","type":"uint8"}],"name":"RequestError","type":"error"},{"inputs":[],"name":"RoleHolderIsZeroAddress","type":"error"},{"inputs":[],"name":"RoleHoldersIndexOutOfBounds","type":"error"},{"inputs":[],"name":"TotalSupplyOverflow","type":"error"},{"inputs":[],"name":"Unauthorized","type":"error"},{"inputs":[{"internalType":"string","name":"kya","type":"string"}],"name":"WrongKYA","type":"error"},{"inputs":[],"name":"ZeroAddressPassed","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"oldAddress","type":"address"},{"indexed":true,"internalType":"address","name":"newAddress","type":"address"},{"indexed":true,"internalType":"address","name":"caller","type":"address"}],"name":"AssetTokenDataChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"string","name":"kya","type":"string"},{"indexed":true,"internalType":"address","name":"caller","type":"address"}],"name":"KyaChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"newAmount","type":"uint256"},{"indexed":true,"internalType":"address","name":"caller","type":"address"}],"name":"MinimumRedemptionAmountChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"mintRequestID","type":"uint256"},{"indexed":true,"internalType":"address","name":"destination","type":"address"},{"indexed":false,"internalType":"uint256","name":"amountMinted","type":"uint256"},{"indexed":true,"internalType":"address","name":"caller","type":"address"}],"name":"MintApproved","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"mintRequestID","type":"uint256"},{"indexed":true,"internalType":"address","name":"destination","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":true,"internalType":"address","name":"caller","type":"address"}],"name":"MintRequested","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"pendingOwner","type":"address"}],"name":"OwnershipHandoverCanceled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"pendingOwner","type":"address"}],"name":"OwnershipHandoverRequested","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"oldOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"redemptionRequestID","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"assetTokenAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"underlyingAssetAmount","type":"uint256"},{"indexed":true,"internalType":"address","name":"requestReceiver","type":"address"},{"indexed":true,"internalType":"address","name":"caller","type":"address"}],"name":"RedemptionApproved","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"redemptionRequestID","type":"uint256"},{"indexed":true,"internalType":"address","name":"requestReceiver","type":"address"},{"indexed":false,"internalType":"string","name":"motive","type":"string"},{"indexed":true,"internalType":"address","name":"caller","type":"address"}],"name":"RedemptionCanceled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"redemptionRequestID","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"assetTokenAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"underlyingAssetAmount","type":"uint256"},{"indexed":false,"internalType":"bool","name":"fromStake","type":"bool"},{"indexed":true,"internalType":"address","name":"caller","type":"address"}],"name":"RedemptionRequested","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"holder","type":"address"},{"indexed":true,"internalType":"uint256","name":"role","type":"uint256"},{"indexed":true,"internalType":"bool","name":"active","type":"bool"}],"name":"RoleSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":true,"internalType":"address","name":"caller","type":"address"}],"name":"SafeguardUnstaked","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":true,"internalType":"address","name":"caller","type":"address"}],"name":"TokenBurned","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Transfer","type":"event"},{"inputs":[],"name":"DECIMALS_HUNDRED_PERCENT","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"DOMAIN_SEPARATOR","outputs":[{"internalType":"bytes32","name":"result","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MINTER_ROLE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"result","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_mintRequestID","type":"uint256"},{"internalType":"string","name":"_referenceTo","type":"string"}],"name":"approveMint","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_redemptionRequestID","type":"uint256"},{"internalType":"string","name":"_approveTxID","type":"string"}],"name":"approveRedemption","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"assetTokenDataAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"result","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"burn","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"cancelOwnershipHandover","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_redemptionRequestID","type":"uint256"},{"internalType":"string","name":"_motive","type":"string"}],"name":"cancelRedemptionRequest","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"pendingOwner","type":"address"}],"name":"completeOwnershipHandover","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"freezeContract","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"holder","type":"address"},{"internalType":"uint256","name":"role","type":"uint256"}],"name":"hasRole","outputs":[{"internalType":"bool","name":"result","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"kya","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"minimumRedemptionAmount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"mint","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"mintRequestID","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"mintRequests","outputs":[{"internalType":"address","name":"destination","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"string","name":"referenceTo","type":"string"},{"internalType":"bool","name":"completed","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"nonces","outputs":[{"internalType":"uint256","name":"result","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"result","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"pendingOwner","type":"address"}],"name":"ownershipHandoverExpiresAt","outputs":[{"internalType":"uint256","name":"result","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"permit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"redemptionRequestID","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"redemptionRequests","outputs":[{"internalType":"address","name":"sender","type":"address"},{"internalType":"string","name":"receipt","type":"string"},{"internalType":"uint256","name":"assetTokenAmount","type":"uint256"},{"internalType":"uint256","name":"underlyingAssetAmount","type":"uint256"},{"internalType":"bool","name":"completed","type":"bool"},{"internalType":"bool","name":"fromStake","type":"bool"},{"internalType":"string","name":"approveTxID","type":"string"},{"internalType":"address","name":"canceledBy","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"requestMint","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amount","type":"uint256"},{"internalType":"address","name":"_destination","type":"address"}],"name":"requestMint","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"requestOwnershipHandover","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_assetTokenAmount","type":"uint256"},{"internalType":"string","name":"_destination","type":"string"}],"name":"requestRedemption","outputs":[{"internalType":"uint256","name":"reqId","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"role","type":"uint256"},{"internalType":"uint256","name":"i","type":"uint256"}],"name":"roleHolderAt","outputs":[{"internalType":"address","name":"result","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"role","type":"uint256"}],"name":"roleHolderCount","outputs":[{"internalType":"uint256","name":"result","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"role","type":"uint256"}],"name":"roleHolders","outputs":[{"internalType":"address[]","name":"result","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amount","type":"uint256"},{"internalType":"string","name":"_receipt","type":"string"}],"name":"safeguardStake","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"safeguardStakes","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"safeguardUnstake","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"safeguardUnstake","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_newAddress","type":"address"}],"name":"setAssetTokenData","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"_kya","type":"string"}],"name":"setKya","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_minimumRedemptionAmount","type":"uint256"}],"name":"setMinimumRedemptionAmount","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"holder","type":"address"},{"internalType":"uint256","name":"role","type":"uint256"},{"internalType":"bool","name":"active","type":"bool"}],"name":"setRole","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"stakedRedemptionRequests","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"statePercent","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalStakes","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"result","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"unfreezeContract","outputs":[],"stateMutability":"nonpayable","type":"function"}]

Deployed Bytecode

0x60806040526004361061031a5760003560e01c806384cc10c5116101ab578063dd62ed3e116100f7578063f15965d911610095578063f634d4e81161006f578063f634d4e81461094c578063fe3f41e414610979578063fee81cf414610995578063ff322ba1146109c857600080fd5b8063f15965d914610904578063f2fde38b14610924578063f4917dd31461093757600080fd5b8063e3b3ac43116100d1578063e3b3ac431461089b578063ea4a1d5c146108bb578063ee761189146108d1578063f04e283e146108f157600080fd5b8063dd62ed3e1461081b578063e0f486ef14610851578063e1a0cc6f1461086757600080fd5b8063a584a9b511610164578063bf9befb11161013e578063bf9befb114610791578063d505accf146107a7578063d5391393146107c7578063dc174960146107fb57600080fd5b8063a584a9b514610746578063a9059cbb1461075b578063bad9d0e41461077b57600080fd5b806384cc10c51461067357806386511595146106a05780638660970c146106c05780638da5cb5b146106f857806395d89b411461071157806398d9423e1461072657600080fd5b8063424e65751161026a5780635605a9981161022357806370a08231116101fd57806370a08231146105e5578063715018a614610618578063793f255c146106205780637ecebe001461064057600080fd5b80635605a9981461057a5780635978cd291461059a5780635c97f4a2146105ad57600080fd5b8063424e6575146104a957806342966c68146104d9578063492ba875146104f957806349733d04146105325780634ef1ccd11461055257806354d1f13d1461057257600080fd5b806323b872dd116102d75780633644e515116102b15780633644e51514610449578063377a1a621461045e5780633fc7293a1461047457806340c10f191461048957600080fd5b806323b872dd146104055780632569296214610425578063313ce5671461042d57600080fd5b806306fdde031461031f578063095ea7b31461034a5780630ac7f4e61461037a57806318160ddd1461039c578063189d1e0b146103c35780631d0b56af146103d8575b600080fd5b34801561032b57600080fd5b506103346109e8565b60405161034191906131da565b60405180910390f35b34801561035657600080fd5b5061036a610365366004613202565b610a7a565b6040519015158152602001610341565b34801561038657600080fd5b5061039a61039536600461322e565b610ace565b005b3480156103a857600080fd5b506805345cdf77eb68f44c545b604051908152602001610341565b3480156103cf57600080fd5b5061039a610b54565b3480156103e457600080fd5b506103b56103f336600461322e565b60086020526000908152604090205481565b34801561041157600080fd5b5061036a61042036600461324b565b610b6f565b61039a610c24565b34801561043957600080fd5b5060405160128152602001610341565b34801561045557600080fd5b506103b5610c74565b34801561046a57600080fd5b506103b560045481565b34801561048057600080fd5b50610334610cf1565b34801561049557600080fd5b5061039a6104a4366004613202565b610d7f565b3480156104b557600080fd5b506104c96104c436600461328c565b610db8565b60405161034194939291906132a5565b3480156104e557600080fd5b5061039a6104f436600461328c565b610e76565b34801561050557600080fd5b506103b561051436600461328c565b63ee9853bb600452600090815260249020546001600160601b031690565b34801561053e57600080fd5b506103b561054d36600461328c565b610eb9565b34801561055e57600080fd5b5061039a61056d36600461328c565b610ec5565b61039a610f06565b34801561058657600080fd5b5061039a61059536600461328c565b610f42565b61039a6105a83660046132ed565b610f4e565b3480156105b957600080fd5b5061036a6105c8366004613202565b60189190915263ee9853bb60045260009081526038902054151590565b3480156105f157600080fd5b506103b561060036600461322e565b6387a211a2600c908152600091909152602090205490565b61039a610f64565b34801561062c57600080fd5b5061039a61063b366004613378565b610f76565b34801561064c57600080fd5b506103b561065b36600461322e565b6338377508600c908152600091909152602090205490565b34801561067f57600080fd5b5061069361068e36600461328c565b61131d565b60405161034191906133c4565b3480156106ac57600080fd5b506103b56106bb366004613378565b611383565b3480156106cc57600080fd5b506002546106e0906001600160a01b031681565b6040516001600160a01b039091168152602001610341565b34801561070457600080fd5b50638b78c6d819546106e0565b34801561071d57600080fd5b50610334611895565b34801561073257600080fd5b5061039a610741366004613410565b6118a4565b34801561075257600080fd5b5061039a61192b565b34801561076757600080fd5b5061036a610776366004613202565b6119cb565b34801561078757600080fd5b506103b5600a5481565b34801561079d57600080fd5b506103b560095481565b3480156107b357600080fd5b5061039a6107c2366004613452565b611a3f565b3480156107d357600080fd5b506103b57f9f2df0fed2c77648de5860a4cc508cd0818c85b8b8a1ab4ceeef8d981c8956a681565b34801561080757600080fd5b5061039a6108163660046134df565b611bca565b34801561082757600080fd5b506103b56108363660046135a2565b602052637f5e9f20600c908152600091909152603490205490565b34801561085d57600080fd5b506103b5600c5481565b34801561087357600080fd5b5061088761088236600461328c565b611e59565b6040516103419897969594939291906135db565b3480156108a757600080fd5b506106e06108b636600461364c565b611fc7565b3480156108c757600080fd5b506103b560065481565b3480156108dd57600080fd5b5061039a6108ec366004613378565b612014565b61039a6108ff36600461322e565b612373565b34801561091057600080fd5b5061039a61091f3660046134df565b6123b0565b61039a61093236600461322e565b61258a565b34801561094357600080fd5b5061039a6125b1565b34801561095857600080fd5b506103b561096736600461322e565b60076020526000908152604090205481565b34801561098557600080fd5b506103b5670de0b6b3a764000081565b3480156109a157600080fd5b506103b56109b036600461322e565b63389a75e1600c908152600091909152602090205490565b3480156109d457600080fd5b506103b56109e336600461366e565b61264d565b6060600080546109f790613693565b80601f0160208091040260200160405190810160405280929190818152602001828054610a2390613693565b8015610a705780601f10610a4557610100808354040283529160200191610a70565b820191906000526020600020905b815481529060010190602001808311610a5357829003601f168201915b5050505050905090565b600082602052637f5e9f20600c5233600052816034600c205581600052602c5160601c337f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92560206000a35060015b92915050565b806001600160a01b038116610af65760405163dcfd2eb960e01b815260040160405180910390fd5b610b00600a612660565b600280546001600160a01b038481166001600160a01b031983168117909355604051911691339183907f1bd2f11f284e6391695ec5dc02786a2f817560fab3a4c6ecebf1f483204f966890600090a4505050565b33600090815260086020526040902054610b6d906128c7565b565b6000610b7c8484846129dd565b8360601b33602052637f5e9f208117600c526034600c208054801915610bb85780851115610bb2576313be252b6000526004601cfd5b84810382555b50506387a211a28117600c526020600c20805480851115610be15763f4d678b86000526004601cfd5b84810382555050836000526020600c208381540181555082602052600c5160601c8160601c600080516020613a1c833981519152602080a3505060019392505050565b60006202a30067ffffffffffffffff164201905063389a75e1600c5233600052806020600c2055337fdbf36a107da19e49527a7176a1babf963b4b0ff8cde35ee35d6cd8f1f9ac7e1d600080a250565b600080610c7f6109e8565b805190602001209050604080517f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f815260208101929092527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc69082015246606082015230608082015260a09020919050565b600b8054610cfe90613693565b80601f0160208091040260200160405190810160405280929190818152602001828054610d2a90613693565b8015610d775780601f10610d4c57610100808354040283529160200191610d77565b820191906000526020600020905b815481529060010190602001808311610d5a57829003601f168201915b505050505081565b7f9f2df0fed2c77648de5860a4cc508cd0818c85b8b8a1ab4ceeef8d981c8956a6610da981612c78565b610db38383612c9a565b505050565b6003602052600090815260409020805460018201546002830180546001600160a01b03909316939192610dea90613693565b80601f0160208091040260200160405190810160405280929190818152602001828054610e1690613693565b8015610e635780601f10610e3857610100808354040283529160200191610e63565b820191906000526020600020905b815481529060010190602001808311610e4657829003601f168201915b5050506003909301549192505060ff1684565b610e803382612d13565b60405181815233907f33631bcd0a4d34a7e2c240ab0753d5adfb7284d8ac89dab6876ec785c0cfa0e6906020015b60405180910390a250565b6000610ac88233612d84565b610ecf600a612660565b600c81905560405181815233907fc30bbd22b0c6378065c37f72e8e5e352368ad2c72d3d107ec28e11c61d16a05e90602001610eae565b63389a75e1600c523360005260006020600c2055337ffa7b8eab7da67f412cc9575ed43464468f9bfbae89d1675917346ca6d8fe3c92600080a2565b610f4b816128c7565b50565b610f59838383612f57565b610db3838383612f6b565b610f6c613063565b610b6d600061307e565b3068929eee149b4bd212685403610f955763ab143c066000526004601cfd5b3068929eee149b4bd2126855610fab6001612660565b6387a211a2600c9081523360005260209020548381600182821015610fef576040516349b6096f60e01b8152600401610fe6939291906136f7565b60405180910390fd5b5050506000846009546110029190613730565b9050600a546110186805345cdf77eb68f44c5490565b61102a670de0b6b3a764000084613743565b611034919061375a565b106110ce57600254604051632c0edf6560e11b81523060048201526001600160a01b039091169063581dbeca906024016020604051808303816000875af1158015611083573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110a7919061377c565b30600490916110cb57604051633cdd624d60e11b8152600401610fe6929190613799565b50505b336000908152600760205260408120549081900361124f57600060065460016110f79190613730565b6006819055604080516101008101825233815281516020601f8a0181900481028201810190935288815292945084935091818301918990899081908401838280828437600092018290525093855250505060208083018b90526040808401839052606084018390526001608085018190528151808401835284815260a086015260c090940183905285835260058252909120835181546001600160a01b0319166001600160a01b039091161781559083015190918201906111b8908261380d565b506040820151600282015560608201516003820155608082015160048201805460a085015115156101000261ff00199315159390931661ffff199091161791909117905560c08201516005820190611210908261380d565b5060e09190910151600690910180546001600160a01b0319166001600160a01b0390921691909117905533600090815260076020526040902055611276565b60008181526005602052604081206002018054889290611270908490613730565b90915550505b3360009081526008602052604081208054889290611295908490613730565b909155505060098290556112aa3330886130bc565b60008181526005602090815260409182902060028101546003909101548351918252918101919091526001818301529051339183917fa8cb3abff9d33baf50c27997f56858a870eece3044d19c6645d25719ee4eb5859181900360600190a35050503868929eee149b4bd2126855505050565b60405163ee9853bb6004526000828152602490208054606081901c602084019081526001916001600160601b0316905b8183101561136e578284015460601c8360051b82015260018301925061134d565b8185528160051b810160405250505050919050565b60003068929eee149b4bd2126854036113a45763ab143c066000526004601cfd5b3068929eee149b4bd212685583600080826113d5576040516349b6096f60e01b8152600401610fe6939291906136f7565b50505060006113f6336387a211a2600c908152600091909152602090205490565b90508481600182821015611420576040516349b6096f60e01b8152600401610fe6939291906136f7565b505060025460405163fef03da360e01b81523060048201526001600160a01b039091169150600090829063fef03da390602401602060405180830381865afa158015611470573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061149491906138cc565b604051635980ec8160e11b81523060048201529091506000906001600160a01b0384169063b301d90290602401602060405180830381865afa1580156114de573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061150291906138cc565b604051636306be8560e01b81523060048201529091506000906001600160a01b03851690636306be8590602401602060405180830381865afa15801561154c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611570919061377c565b9050801580156115895750336001600160a01b03841614155b806115a557508080156115a55750336001600160a01b03831614155b156115da57600c5489816004818310156115d5576040516349b6096f60e01b8152600401610fe6939291906136f7565b505050505b604051630e0dc3b960e11b81523060048201526000906001600160a01b03861690631c1b8772906024016020604051808303816000875af1158015611623573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061164791906138e9565b90506000670de0b6b3a764000061165e838d613743565b611668919061375a565b905060065460016116799190613730565b6006819055604080516101008101825233815281516020601f8e018190048102820181019093528c8152929a5091818301918d908d9081908401838280828437600092018290525093855250505060208083018f9052604080840186905260608401839052608084018390528051808301825283815260a085015260c09093018290528b825260058152919020825181546001600160a01b0319166001600160a01b03909116178155908201516001820190611735908261380d565b506040820151600282015560608201516003820155608082015160048201805460a085015115156101000261ff00199315159390931661ffff199091161791909117905560c0820151600582019061178d908261380d565b5060e09190910151600690910180546001600160a01b0319166001600160a01b039092169190911790556117c233308d6130bc565b821580156117d85750336001600160a01b038616145b806117f357508280156117f35750336001600160a01b038516145b1561183757611837886040518060400160405280601b81526020017f4175746f6d61746963526564656d7074696f6e417070726f76616c00000000008152506123b0565b604080518c815260208101839052600081830152905133918a917fa8cb3abff9d33baf50c27997f56858a870eece3044d19c6645d25719ee4eb5859181900360600190a3505050505050503868929eee149b4bd21268559392505050565b6060600180546109f790613693565b6118ae600a612660565b8181600381116118d357604051634c1603d760e11b8152600401610fe6929190613902565b50600b90506118e3828483613931565b50336001600160a01b03167fa968454fe42ca93a4780be08eb42cedc78e3f1dd75d23e0daf15c615e2594d67838360405161191f929190613902565b60405180910390a25050565b6119356008612660565b600254604051631bef0dc360e01b81523060048201526001600160a01b0390911690631bef0dc3906024016020604051808303816000875af115801561197f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906119a3919061377c565b30600290916119c757604051633cdd624d60e11b8152600401610fe6929190613799565b5050565b60006119d83384846129dd565b6387a211a2600c52336000526020600c20805480841115611a015763f4d678b86000526004601cfd5b83810382555050826000526020600c208281540181555081602052600c5160601c33600080516020613a1c833981519152602080a350600192915050565b6000611a496109e8565b8051906020012090507fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc642861015611a8957631a15a3cc6000526004601cfd5b6040518960601b60601c99508860601b60601c985065383775081901600e52896000526020600c2080547f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f835284602084015283604084015246606084015230608084015260a08320602e527f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c983528b60208401528a60408401528960608401528060808401528860a084015260c08320604e526042602c206000528760ff1660205286604052856060526020806080600060015afa8c3d5114611b755763ddafbaef6000526004601cfd5b0190556303faf4f960a51b89176040526034602c20889055888a7f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925602060608501a36040525050600060605250505050505050565b3068929eee149b4bd212685403611be95763ab143c066000526004601cfd5b3068929eee149b4bd2126855611bff6005612660565b6000828152600360209081526040808320815160808101835281546001600160a01b031681526001820154938101939093526002810180549194939285929084019190611c4b90613693565b80601f0160208091040260200160405190810160405280929190818152602001828054611c7790613693565b8015611cc45780601f10611c9957610100808354040283529160200191611cc4565b820191906000526020600020905b815481529060010190602001808311611ca757829003601f168201915b50505091835250506003919091015460ff161515602090910152805190915084906000906001600160a01b0316611d105760405163b522703d60e01b8152600401610fe69291906139f1565b50508060600151158460019091611d3c5760405163b522703d60e01b8152600401610fe69291906139f1565b505060038201805460ff1916600117905560028201611d5b848261380d565b50600254604051630e0dc3b960e11b81523060048201526000916001600160a01b031690631c1b8772906024016020604051808303816000875af1158015611da7573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611dcb91906138e9565b9050600081670de0b6b3a76400008460200151611de89190613743565b611df2919061375a565b9050611e02836000015182612c9a565b825160405182815233916001600160a01b03169088907f293a53949964e4a793f245e9ef19f0b1134ea644f51d51df86331137bb9232c69060200160405180910390a4505050503868929eee149b4bd21268555050565b600560205260009081526040902080546001820180546001600160a01b039092169291611e8590613693565b80601f0160208091040260200160405190810160405280929190818152602001828054611eb190613693565b8015611efe5780601f10611ed357610100808354040283529160200191611efe565b820191906000526020600020905b815481529060010190602001808311611ee157829003601f168201915b505050506002830154600384015460048501546005860180549596939592945060ff808316946101009093041692611f3590613693565b80601f0160208091040260200160405190810160405280929190818152602001828054611f6190613693565b8015611fae5780601f10611f8357610100808354040283529160200191611fae565b820191906000526020600020905b815481529060010190602001808311611f9157829003601f168201915b505050600690930154919250506001600160a01b031688565b63ee9853bb60045260008281526024812080546001600160601b0381168410611ff857635694da8e6000526004601cfd5b60601c9150821561200d578281015460601c91505b5092915050565b60008381526005602090815260408083208151610100810190925280546001600160a01b031682526001810180549194938592908401919061205590613693565b80601f016020809104026020016040519081016040528092919081815260200182805461208190613693565b80156120ce5780601f106120a3576101008083540402835291602001916120ce565b820191906000526020600020905b8154815290600101906020018083116120b157829003601f168201915b50505091835250506002820154602082015260038201546040820152600482015460ff80821615156060840152610100909104161515608082015260058201805460a09092019161211e90613693565b80601f016020809104026020016040519081016040528092919081815260200182805461214a90613693565b80156121975780601f1061216c57610100808354040283529160200191612197565b820191906000526020600020905b81548152906001019060200180831161217a57829003601f168201915b5050509183525050600691909101546001600160a01b0390811660209092019190915281519192508691600091166121e45760405163b522703d60e01b8152600401610fe69291906139f1565b505060e081015185906002906001600160a01b0316156122195760405163b522703d60e01b8152600401610fe69291906139f1565b505080608001511585600190916122455760405163b522703d60e01b8152600401610fe69291906139f1565b50508060a001511585600390916122715760405163b522703d60e01b8152600401610fe69291906139f1565b505080516001600160a01b031633146122e6576002546040516362d1cb6760e01b81523060048201523360248201526001600160a01b03909116906362d1cb679060440160006040518083038186803b1580156122cd57600080fd5b505afa1580156122e1573d6000803e3d6000fd5b505050505b60006002830181905560038301556006820180546001600160a01b031916331790558051604082015161231a9130916130bc565b336001600160a01b031681600001516001600160a01b0316867f31916b1311adb3f7a4a9092ec7c3d79cc1eab54bddaa4e1db9f7e1097797f9298787604051612364929190613902565b60405180910390a45050505050565b61237b613063565b63389a75e1600c52806000526020600c2080544211156123a357636f5e88186000526004601cfd5b60009055610f4b8161307e565b6123ba6008612660565b60008281526005602052604090206006810154819084906002906001600160a01b0316156123fd5760405163b522703d60e01b8152600401610fe69291906139f1565b5050805484906000906001600160a01b031661242e5760405163b522703d60e01b8152600401610fe69291906139f1565b50506004810154849060019060ff161561245d5760405163b522703d60e01b8152600401610fe69291906139f1565b50506004810154610100900460ff161561250457600254604051636306be8560e01b81523060048201526001600160a01b0390911690636306be8590602401602060405180830381865afa1580156124b9573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906124dd919061377c565b306005909161250157604051633cdd624d60e11b8152600401610fe6929190613799565b50505b60048201805460ff1916600117905560058201612521848261380d565b50612530308260020154612d13565b80546002820154600383015460408051928352602083019190915233926001600160a01b03169187917f77ac421ef93134bdf8b76efd922614ddca35f3f14e97c693dd88b4a3aa160636910160405180910390a450505050565b612592613063565b8060601b6125a857637448fbae6000526004601cfd5b610f4b8161307e565b6125bb6008612660565b6002546040516370e6513b60e11b81523060048201526001600160a01b039091169063e1cca276906024016020604051808303816000875af1158015612605573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612629919061377c565b30600390916119c757604051633cdd624d60e11b8152600401610fe6929190613799565b60006126598383612d84565b9392505050565b6002546000906001600160a01b031660018316156126d4576040516348f261b960e01b81523060048201526001600160a01b038216906348f261b99060240160006040518083038186803b1580156126b757600080fd5b505afa1580156126cb573d6000803e3d6000fd5b50505050600191505b6002831615612739576040516331e11d4160e01b81523060048201526001600160a01b038216906331e11d419060240160006040518083038186803b15801561271c57600080fd5b505afa158015612730573d6000803e3d6000fd5b50505050600191505b60048316156127a45760405163d9253c7d60e01b81523060048201523360248201526001600160a01b0382169063d9253c7d9060440160006040518083038186803b15801561278757600080fd5b505afa15801561279b573d6000803e3d6000fd5b50505050600191505b600883161561280f576040516362d1cb6760e01b81523060048201523360248201526001600160a01b038216906362d1cb679060440160006040518083038186803b1580156127f257600080fd5b505afa158015612806573d6000803e3d6000fd5b50505050600191505b601083161561287a5760405163b14cfbb960e01b81523060048201523360248201526001600160a01b0382169063b14cfbb99060440160006040518083038186803b15801561285d57600080fd5b505afa158015612871573d6000803e3d6000fd5b50505050600191505b81610db35760405162461bcd60e51b815260206004820152601c60248201527f4173736574546f6b656e3a20616363657373206e6f7420666f756e64000000006044820152606401610fe6565b6128d16003612660565b80600080826128f6576040516349b6096f60e01b8152600401610fe6939291906136f7565b50503360009081526008602052604090205490508181600382821015612932576040516349b6096f60e01b8152600401610fe6939291906136f7565b50505081816129419190613a08565b3360009081526008602052604081209190915560098054849290612966908490613a08565b909155505033600090815260076020908152604080832054835260059091528120600201805484929061299a908490613a08565b909155506129ab90503033846130bc565b60405182815233907f59696ca7031c7df7a856426d8eb521fa3033d4257b16cd455de5f30590591c7e9060200161191f565b6129e76002612660565b600254604051632325e69f60e21b81523060048201526001600160a01b038581166024830152848116604483015260648201849052909116906000908290638c979a7c906084016020604051808303816000875af1158015612a4d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612a71919061377c565b604051636306be8560e01b81523060048201529091506001600160a01b03831690636306be8590602401602060405180830381865afa158015612ab8573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612adc919061377c565b15612c4b576001600160a01b0384163014801590612b0357506001600160a01b0385163014155b8015612b885750604051635980ec8160e11b81523060048201526001600160a01b0383169063b301d90290602401602060405180830381865afa158015612b4e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612b7291906138cc565b6001600160a01b0316856001600160a01b031614155b15612c285760405163db5f725760e01b81523060048201526001600160a01b03868116602483015283169063db5f725790604401602060405180830381865afa158015612bd9573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612bfd919061377c565b3060019091612c2157604051633cdd624d60e11b8152600401610fe6929190613799565b5050612c71565b30600082612c2157604051633cdd624d60e11b8152600401610fe6929190613799565b30600082612c6e57604051633cdd624d60e11b8152600401610fe6929190613799565b50505b5050505050565b3360185263ee9853bb60045260008181526038902054610f4b57610f4b613130565b612ca6600083836129dd565b6805345cdf77eb68f44c5481810181811015612cca5763e5cfe9576000526004601cfd5b806805345cdf77eb68f44c5550506387a211a2600c52816000526020600c208181540181555080602052600c5160601c6000600080516020613a1c833981519152602080a35050565b612d1f826000836129dd565b6387a211a2600c52816000526020600c20805480831115612d485763f4d678b86000526004601cfd5b82900390556805345cdf77eb68f44c8054829003905560008181526001600160a01b038316600080516020613a1c833981519152602083a35050565b6000612d906013612660565b8260008082612db5576040516349b6096f60e01b8152600401610fe6939291906136f7565b5050600454612dc691506001613730565b6004819055604080516080810182526001600160a01b038581168252602080830188815284518083018652600080825285870191825260608601819052878152600390935294909120835181546001600160a01b03191693169290921782555160018201559151929350916002820190612e40908261380d565b50606091909101516003909101805460ff191691151591909117905560025460405163fef03da360e01b81523060048201526001600160a01b039091169063fef03da390602401602060405180830381865afa158015612ea4573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612ec891906138cc565b6001600160a01b03163303612f0357612f03816040518060400160405280600a815260200169125cdcdd595c935a5b9d60b21b815250611bca565b336001600160a01b0316826001600160a01b0316827fd44ca9091dc2db8840a4a2459864b4bb02cd1f06f285f304ec45aa15ec28e0ea86604051612f4991815260200190565b60405180910390a492915050565b612f5f61313e565b610db357610db3613130565b612f7482613163565b8260601b80612f8b5763825501436000526004601cfd5b8360185263ee9853bb600452826000526024600020805460a01b60a01c603860002080548561301457801561302c5760018303806001830314612ff75784810180546bffffffffffffffffffffffff191683870160001901819055600091829055602452603890208290555b84546bffffffffffffffffffffffff19161784556000825561302c565b8061302c578483850155600183018255600184540184555b50505050811515838260601c7faddc47d7e02c95c00ec667676636d772a589ffbf0663cfd7cd4dd3d4758201b8600080a450505050565b638b78c6d819543314610b6d576382b429006000526004601cfd5b638b78c6d81980546001600160a01b039092169182907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0600080a355565b6130c78383836129dd565b8260601b6387a211a28117600c526020600c208054808411156130f25763f4d678b86000526004601cfd5b83810382555050826000526020600c208281540181555081602052600c5160601c8160601c600080516020613a1c833981519152602080a350505050565b6399152cca6000526004601cfd5b6000638da5cb5b600052602060006004601c305afa601f3d1160005133141616905090565b63d24f19d5600052602060006004601c305afa601f3d116000518311161615610f4b5763d954416a6000526004601cfd5b6000815180845260005b818110156131ba5760208185018101518683018201520161319e565b506000602082860101526020601f19601f83011685010191505092915050565b6020815260006126596020830184613194565b6001600160a01b0381168114610f4b57600080fd5b6000806040838503121561321557600080fd5b8235613220816131ed565b946020939093013593505050565b60006020828403121561324057600080fd5b8135612659816131ed565b60008060006060848603121561326057600080fd5b833561326b816131ed565b9250602084013561327b816131ed565b929592945050506040919091013590565b60006020828403121561329e57600080fd5b5035919050565b60018060a01b03851681528360208201526080604082015260006132cc6080830185613194565b9050821515606083015295945050505050565b8015158114610f4b57600080fd5b60008060006060848603121561330257600080fd5b833561330d816131ed565b9250602084013591506040840135613324816132df565b809150509250925092565b60008083601f84011261334157600080fd5b50813567ffffffffffffffff81111561335957600080fd5b60208301915083602082850101111561337157600080fd5b9250929050565b60008060006040848603121561338d57600080fd5b83359250602084013567ffffffffffffffff8111156133ab57600080fd5b6133b78682870161332f565b9497909650939450505050565b602080825282518282018190526000918401906040840190835b818110156134055783516001600160a01b03168352602093840193909201916001016133de565b509095945050505050565b6000806020838503121561342357600080fd5b823567ffffffffffffffff81111561343a57600080fd5b6134468582860161332f565b90969095509350505050565b600080600080600080600060e0888a03121561346d57600080fd5b8735613478816131ed565b96506020880135613488816131ed565b95506040880135945060608801359350608088013560ff811681146134ac57600080fd5b9699959850939692959460a0840135945060c09093013592915050565b634e487b7160e01b600052604160045260246000fd5b600080604083850312156134f257600080fd5b82359150602083013567ffffffffffffffff81111561351057600080fd5b8301601f8101851361352157600080fd5b803567ffffffffffffffff81111561353b5761353b6134c9565b604051601f8201601f19908116603f0116810167ffffffffffffffff8111828210171561356a5761356a6134c9565b60405281815282820160200187101561358257600080fd5b816020840160208301376000602083830101528093505050509250929050565b600080604083850312156135b557600080fd5b82356135c0816131ed565b915060208301356135d0816131ed565b809150509250929050565b6001600160a01b0389168152610100602082018190526000906136009083018a613194565b886040840152876060840152861515608084015285151560a084015282810360c084015261362e8186613194565b91505060018060a01b03831660e08301529998505050505050505050565b6000806040838503121561365f57600080fd5b50508035926020909101359150565b6000806040838503121561368157600080fd5b8235915060208301356135d0816131ed565b600181811c908216806136a757607f821691505b6020821081036136c757634e487b7160e01b600052602260045260246000fd5b50919050565b634e487b7160e01b600052602160045260246000fd5b600581106136f3576136f36136cd565b9052565b838152602081018390526060810161371260408301846136e3565b949350505050565b634e487b7160e01b600052601160045260246000fd5b80820180821115610ac857610ac861371a565b8082028115828204841417610ac857610ac861371a565b60008261377757634e487b7160e01b600052601260045260246000fd5b500490565b60006020828403121561378e57600080fd5b8151612659816132df565b6001600160a01b038316815260408101600683106137b9576137b96136cd565b8260208301529392505050565b601f821115610db357806000526020600020601f840160051c810160208510156137ed5750805b601f840160051c820191505b81811015612c7157600081556001016137f9565b815167ffffffffffffffff811115613827576138276134c9565b61383b816138358454613693565b846137c6565b6020601f82116001811461386f57600083156138575750848201515b600019600385901b1c1916600184901b178455612c71565b600084815260208120601f198516915b8281101561389f578785015182556020948501946001909201910161387f565b50848210156138bd5786840151600019600387901b60f8161c191681555b50505050600190811b01905550565b6000602082840312156138de57600080fd5b8151612659816131ed565b6000602082840312156138fb57600080fd5b5051919050565b60208152816020820152818360408301376000818301604090810191909152601f909201601f19160101919050565b67ffffffffffffffff831115613949576139496134c9565b61395d836139578354613693565b836137c6565b6000601f84116001811461399157600085156139795750838201355b600019600387901b1c1916600186901b178355612c71565b600083815260209020601f19861690835b828110156139c257868501358255602094850194600190920191016139a2565b50868210156139df5760001960f88860031b161c19848701351681555b505060018560011b0183555050505050565b82815260408101600483106137b9576137b96136cd565b81810381811115610ac857610ac861371a56feddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3efa2646970667358221220bfaf73b4cdb3e1345a858398e0cb20a0de3c0644c6beb13446717f65d66e571664736f6c634300081c0033

Block Transaction 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
[ 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.