IS21Engine

Description:

Multi-signature wallet contract requiring multiple confirmations for transaction execution.

Blockchain: Ethereum

Source Code: View Code On The Blockchain

Solidity Source Code:

{{
  "language": "Solidity",
  "sources": {
    "src/IS21Engine.sol": {
      "content": "// SPDX-License-Identifier: MIT

pragma solidity 0.8.24;

import {ReentrancyGuard} from "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {ERC20Pausable} from "@openzeppelin/contracts/token/ERC20/extensions/ERC20Pausable.sol";
import {ERC20Permit} from "@openzeppelin/contracts/token/ERC20/extensions/ERC20Permit.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
import {EnumerableSet} from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";

/// @notice Public struct for fiat reserve metadata
struct FiatReserves {
    bytes32 currency; // The currency code (e.g., "USD", "EUR", "ZAR").
    uint256 amount; // The amount of fiat in the smallest unit (e.g., cents).
}

/**
 * @title IS21Engine
 * @author BlueAsset Technology Team
 *
 * @notice The IS21Engine contract is the foundation of the InterShare21 reserve currency system.
 * It governs the issuance and management of InterShare21 (IS21) tokens, a decentralized,
 * exogenously collateralized, fiat-backed currency. It is the official token contract for IS21 and
 * for the InterShare Loan Engine (ISLoanEngine).
 *
 * @dev Key Principles:
 * - Exogenously Collateralized: Backed by external fiat reserves stored in trusted institutions.
 * - Fiat Backed: Each IS21 token corresponds to reserves of major global fiat currencies.
 * - Managed Supply: IS21 tokens are minted or burned in response to verified reserve changes.
 * - Transparent Verification: Approved auditors publish proof-of-reserve reports (e.g., via IPFS/Arweave),
 *   and fund managers ensure reserves remain adequate.
 *
 * @dev Contract Capabilities:
 * - Minting and burning of IS21 tokens by approved fund managers.
 * - Approval and revocation of auditors and fund managers.
 * - Recording and publishing of off-chain reserve proof hashes.
 * - Fiat reserve tracking across multiple currencies.
 * - Pausable contract functionality for added security.
 *
 * @notice IS21 is designed as a global reserve currency system to provide stability,
 * transparency, and decentralization in financial ecosystems.
 */

contract IS21Engine is
    ERC20,
    ERC20Permit,
    ReentrancyGuard,
    ERC20Pausable,
    Ownable
{
    using SafeERC20 for IERC20;
    using EnumerableSet for EnumerableSet.AddressSet;

    ///////////////
    // Errors    //
    ///////////////
    error IS21Engine__AmountMustBeMoreThanZero();
    error IS21Engine__BurnAmountExceedsBalance();
    error IS21Engine__NotZeroAddress();
    error IS21Engine__OnlyFundManagerCanExecute();
    error IS21Engine__OnlyAuditorCanExecute();
    error IS21Engine__ETHNotAccepted();
    error IS21Engine__CannotSendToContract();

    /////////////////////
    // State Variables //
    /////////////////////
    string public constant IS21_VERSION = "1.0.0"; // Semantic versioning for the IS21Engine contract
    mapping(bytes32 => uint256) private sCurrencyToReserve;
    EnumerableSet.AddressSet private sFundManagers;
    EnumerableSet.AddressSet private sAuditors;
    string private sLatestReserveProofHash;

    ////////////
    // Events //
    ////////////
    event IS21Minted(address indexed to, uint256 amount);
    event IS21Burned(address indexed from, uint256 amount);
    event AuditorApproved(address indexed auditor, uint256 timestamp);
    event AuditorRevoked(address indexed auditor, uint256 timestamp);
    event FundManagerApproved(address indexed fundManager, uint256 timestamp);
    event FundManagerRevoked(address indexed fundManager, uint256 timestamp);
    event ContractPaused(address indexed caller, uint256 timestamp);
    event ContractUnpaused(address indexed caller, uint256 timestamp);
    event ReserveVerified(
        address indexed auditor,
        uint256 timestamp,
        string message
    );
    event FiatReserveUpdated(
        address indexed fundManager,
        bytes32 indexed currency,
        uint256 amount,
        uint256 timestamp
    );
    event ReserveProofHashUpdated(
        address indexed auditor,
        string oldHash,
        string newHash,
        uint256 timestamp
    );

    ///////////////
    // Modifiers //
    ///////////////
    modifier moreThanZero(uint256 amount) {
        if (amount <= 0) {
            revert IS21Engine__AmountMustBeMoreThanZero();
        }
        _;
    }

    modifier nonZeroAddress(address to) {
        if (to == address(0)) {
            revert IS21Engine__NotZeroAddress();
        }
        _;
    }

    modifier onlyFundManager() {
        if (!sFundManagers.contains(msg.sender)) {
            revert IS21Engine__OnlyFundManagerCanExecute();
        }
        _;
    }

    modifier onlyAuditor() {
        if (!sAuditors.contains(msg.sender)) {
            revert IS21Engine__OnlyAuditorCanExecute();
        }
        _;
    }

    modifier cannotSendToContract(address to) {
        if (to == address(this)) {
            revert IS21Engine__CannotSendToContract();
        }
        _;
    }

    /////////////////
    // Constructor //
    /////////////////

    /** 
        @param ownerAddress The address of the owner of the contract.
        @notice The owner address cannot be zero.
        @notice The contract is initialized with the name "InterShare21" and symbol "IS21".
        @dev The contract uses OpenZeppelin's ERC20 implementation for token functionality.
        @dev The constructor initializes the contract with the owner's address.
    */
    constructor(
        address ownerAddress
    )
        ERC20("InterShare21", "IS21")
        ERC20Permit("InterShare21")
        Ownable(ownerAddress)
    {
        if (ownerAddress == address(0)) {
            revert IS21Engine__NotZeroAddress();
        }
    }

    ///////////////////////////////////
    //  External/Public Functions    //
    ///////////////////////////////////

    /**
        @return The number of decimals used to get its user representation.
        @dev This function overrides the default decimals function in the ERC20 contract.
        @dev IS21 uses 18 decimals, similar to Ether and many other ERC20 tokens.
    */
    function decimals() public pure override returns (uint8) {
        return 18;
    }

    /**
        @return The current version of the IS21Engine contract as a string.
        @dev This function returns a constant string defined in the contract.
     */
    function getVersion() external pure returns (string memory) {
        return IS21_VERSION;
    }

    /**
        This function allows the owner to rescue ERC20 tokens from the contract.
        @param token The address of the ERC20 token contract.
        @param amount The amount of tokens to rescue.
        @param to The address to which the rescued tokens will be sent.
        @notice This function can only be called by the owner and is protected against reentrancy attacks.
        @dev It uses the transfer function from the IERC20 interface to send the tokens.
    */
    function rescueErc20(
        address token,
        uint256 amount,
        address to
    ) external onlyOwner nonZeroAddress(to) nonZeroAddress(token) nonReentrant {
        IERC20(token).safeTransfer(to, amount);
    }

    /**
        Returns the latest reserve proof hash published by an auditor.
        @return The string hash (e.g., IPFS CID) pointing to the proof document.
    */
    function getLatestReserveProofHash() external view returns (string memory) {
        return sLatestReserveProofHash;
    }

    /**
        Allows an approved auditor to update the off-chain proof-of-reserve hash.
        @param newHash The IPFS or Arweave hash of the signed reserve audit report.
        @notice This function can only be called by an approved auditor.
        @dev Emits an event to log and trace reserve proof updates.
    */
    function setReserveProofHash(
        string calldata newHash
    ) external onlyAuditor nonReentrant {
        string memory oldHash = sLatestReserveProofHash;
        sLatestReserveProofHash = newHash;
        emit ReserveProofHashUpdated(
            msg.sender,
            oldHash,
            newHash,
            block.timestamp
        );
    }

    /**
        This function allows the owner to approve a fund manager.
        @param fundManager The address of the fund manager to be approved.
        @notice This function can only be called by the current owner and is protected against reentrancy attacks.
        @dev It updates the mapping of approved fund managers and emits an event for tracking purposes.
    */
    function approveFundManager(
        address fundManager
    ) external onlyOwner nonZeroAddress(fundManager) nonReentrant {
        if (sFundManagers.add(fundManager)) {
            emit FundManagerApproved(fundManager, block.timestamp);
        }
    }

    /** 
        This function allows the owner to revoke a fund manager's approval.
        @param fundManager The address of the fund manager to be revoked.
        @notice This function can only be called by the current owner and is protected against reentrancy attacks.
        @dev It updates the mapping of approved fund managers and emits an event for tracking purposes.
    */
    function revokeFundManager(
        address fundManager
    ) external onlyOwner nonZeroAddress(fundManager) nonReentrant {
        if (sFundManagers.remove(fundManager)) {
            emit FundManagerRevoked(fundManager, block.timestamp);
        }
    }

    /**
        This function retrieves the list of all approved fund managers.
        @return An array of addresses representing the approved fund managers.
        @notice This function is view-only and does not modify the state of the contract.
        @dev It can be used to get a list of all fund managers for administrative or informational purposes.
    */
    function getFundManagers() external view returns (address[] memory) {
        return sFundManagers.values();
    }

    /**
        This function checks if an address is an approved fund manager.
        @param account The address to check for fund manager approval.
        @return A boolean indicating whether the address is an approved fund manager.
        @notice This function is view-only and does not modify the state of the contract.
        @dev It can be used to verify if an address has fund management privileges.
    */
    function isFundManager(address account) external view returns (bool) {
        return sFundManagers.contains(account);
    }

    /** 
        This function allows the owner to approve an auditor.
        @param auditor The address of the auditor to be approved.
        @notice This function can only be called by the owner and is protected against reentrancy attacks.
        @dev It updates the mapping of approved auditors and emits an event for tracking purposes.
    */
    function approveAuditor(
        address auditor
    ) external onlyOwner nonZeroAddress(auditor) nonReentrant {
        if (sAuditors.add(auditor)) {
            emit AuditorApproved(auditor, block.timestamp);
        }
    }

    /** 
        This function allows the owner to revoke an auditor's approval.
        @param auditor The address of the auditor to be revoked.
        @notice This function can only be called by the owner and is protected against reentrancy attacks.
        @dev It updates the mapping of approved auditors and emits an event for tracking purposes.
    */
    function revokeAuditor(
        address auditor
    ) external onlyOwner nonZeroAddress(auditor) nonReentrant {
        if (sAuditors.remove(auditor)) {
            emit AuditorRevoked(auditor, block.timestamp);
        }
    }

    /**
        This function retrieves the list of all approved auditors.
        @return An array of addresses representing the approved auditors.
        @notice This function is view-only and does not modify the state of the contract.
        @notice Some may currently be revoked; check isAuditor() to confirm active status.
        @dev It can be used to get a list of all auditors for administrative or informational purposes.
    */
    function getAuditors() external view returns (address[] memory) {
        return sAuditors.values();
    }

    /**
        This function checks if an address is an approved auditor.
        @param account The address to check for auditor approval.
        @return A boolean indicating whether the address is an approved auditor.
        @notice This function is view-only and does not modify the state of the contract.
        @dev It can be used to verify if an address has auditor privileges.
    */
    function isAuditor(address account) external view returns (bool) {
        return sAuditors.contains(account);
    }

    /**        
        This function allows an auditor to verify the reserves of the contract.
        @param message A string message that can be used to provide context or details about the verification.
        @notice This function can only be called by an approved auditor and is protected against reentrancy attacks.
        @dev It emits a ReserveVerified event with the auditor's address, current timestamp, and the provided message.
        @dev The auditor's address must be approved by the owner before calling this function.
    */
    function verifyReserves(
        string calldata message
    ) external onlyAuditor nonReentrant {
        emit ReserveVerified(msg.sender, block.timestamp, message);
    }

    /**
        This function allows the fund manager to update a single fiat reserve for a specific currency.
        @param currency The currency code (e.g., "USD", "EUR", "ZAR") for which the reserve is being updated.
        @param amount The amount of fiat in the smallest unit (e.g., cents) to be set as the reserve for the specified currency.
        @notice This function can only be called by the fund manager and is protected against reentrancy attacks.
        @dev It updates the mapping of currency to reserve and emits an event for tracking purposes.
    */
    function updateFiatReserve(
        bytes32 currency,
        uint256 amount
    ) external onlyFundManager nonReentrant {
        sCurrencyToReserve[currency] = amount;
        emit FiatReserveUpdated(msg.sender, currency, amount, block.timestamp);
    }

    /** This function allows the fund manager to update multiple fiat reserves at once.
        @param reserves An array of FiatReserves structs, each containing a currency code and the corresponding amount to be set as the reserve.
        @notice This function can only be called by the fund manager and is protected against reentrancy attacks.
        @dev It iterates through the array of reserves, updating the mapping of currency to reserve for each entry and emitting an event for each update.
    */
    function updateFiatReserves(
        FiatReserves[] calldata reserves
    ) external onlyFundManager nonReentrant {
        for (uint256 i = 0; i < reserves.length; ) {
            sCurrencyToReserve[reserves[i].currency] = reserves[i].amount;
            emit FiatReserveUpdated(
                msg.sender,
                reserves[i].currency,
                reserves[i].amount,
                block.timestamp
            );
            unchecked {
                ++i;
            }
        }
    }

    /**
        This function allows anyone to retrieve the fiat reserve for a specific currency.
        @param currency The currency code (e.g., "USD", "EUR", "ZAR") for which the reserve is being queried.
        @return The amount of fiat in the smallest unit (e.g., cents) that is reserved for the specified currency.
        @notice This function is view-only and does not modify the state of the contract.
    */
    function getFiatReserve(bytes32 currency) external view returns (uint256) {
        return sCurrencyToReserve[currency];
    }

    /**
        This function allows anyone to retrieve the fiat reserves for multiple currencies.
        @param currencies An array of currency codes (e.g., ["USD", "EUR", "ZAR"]) for which the reserves are being queried.
        @return An array of amounts, where each amount corresponds to the reserve for the specified currency in the same index.
        @notice This function is view-only and does not modify the state of the contract.
    */
    function getFiatReserves(
        bytes32[] calldata currencies
    ) external view returns (uint256[] memory) {
        uint256[] memory reserves = new uint256[](currencies.length);
        for (uint256 i = 0; i < currencies.length; ) {
            reserves[i] = sCurrencyToReserve[currencies[i]];
            unchecked {
                ++i;
            }
        }
        return reserves;
    }

    /**
        This function allows fund managers to mint IS21 tokens directly to their own address.
        @param amount The amount of IS21 tokens to mint.
        @notice This function can only be called by an approved fund manager, when not paused, and with a positive amount.
        @dev Equivalent to mintIs21To(msg.sender, amount).
     */
    function mintIs21(uint256 amount) external {
        mintIs21To(msg.sender, amount);
    }

    /**
        @notice Allows an approved fund manager to mint IS21 tokens directly to a specified address.
        @param to The address that will receive the minted tokens.
        @param amount The amount of IS21 tokens to mint.
        @dev Only callable by an approved fund manager, when not paused, and with a positive amount.
    **/
    function mintIs21To(
        address to,
        uint256 amount
    )
        public
        onlyFundManager
        nonZeroAddress(to)
        moreThanZero(amount)
        nonReentrant
    {
        _mintInterShare21(to, amount);
        emit IS21Minted(to, amount);
    }

    /**
        This function allows fund managers to burn IS21 tokens.
        @param amount The amount of IS21 tokens to burn.
        @notice This function can only be called by the fund manager and must be more than zero.
        @dev It checks if the sender has enough balance before burning the tokens.
        @dev It uses the _burnInterShare21 function to burn the tokens and emits an InterShare21Burned event to log the burning action.
        @dev It is protected against reentrancy attacks and can only be executed when the contract is not paused.
    */
    function burnIs21(
        uint256 amount
    ) external onlyFundManager moreThanZero(amount) nonReentrant {
        if (balanceOf(msg.sender) < amount) {
            revert IS21Engine__BurnAmountExceedsBalance();
        }

        _burnInterShare21(msg.sender, amount);
        emit IS21Burned(msg.sender, amount);
    }

    /**
        This function allows fund managers to burn IS21 tokens from a specified account.
        @param account The address from which the IS21 tokens will be burned.
        @param amount The amount of IS21 tokens to burn.
        @notice This function can only be called by the fund manager and must be more than zero.
        @dev It checks if the account has enough balance before burning the tokens.
        @dev It uses the _spendAllowance function to check and reduce the allowance, and the _burn function to burn the tokens.
        @dev It emits an InterShare21Burned event to log the burning action.
        @dev It is protected against reentrancy attacks and can only be executed when the contract is not paused.
    */
    function burnIs21From(
        address account,
        uint256 amount
    )
        external
        onlyFundManager
        moreThanZero(amount)
        nonZeroAddress(account)
        nonReentrant
    {
        _spendAllowance(account, msg.sender, amount);
        _burnInterShare21(account, amount);
        emit IS21Burned(account, amount);
    }

    /**
        This function allows the owner to pause the contract.
        @notice This function can only be called by the owner.
        @dev It uses the _pause function from the Pausable contract to pause the contract.
        @dev It emits a ContractPaused event to log the pausing action.
    */
    function pause() external onlyOwner {
        _pause();
        emit ContractPaused(msg.sender, block.timestamp);
    }

    /**
        This function allows the owner to unpause the contract.
        @notice This function can only be called by the owner.
        @dev It uses the _unpause function from the Pausable contract to unpause the contract.
        @dev It emits a ContractUnpaused event to log the unpausing action.
    */
    function unpause() external onlyOwner {
        _unpause();
        emit ContractUnpaused(msg.sender, block.timestamp);
    }

    /**
        This function specifies that the contract does not accept ETH.
        @notice It reverts any incoming ETH transactions with a custom error.
        @dev This function is used to prevent accidental ETH transfers to the contract.
    */
    receive() external payable {
        revert IS21Engine__ETHNotAccepted();
    }

    /**
        This function specifies that the contract does not accept ETH.
        @notice It reverts any incoming ETH transactions with a custom error.
        @dev This function is used to prevent accidental ETH transfers to the contract.
    */
    fallback() external payable {
        revert IS21Engine__ETHNotAccepted();
    }

    ////////////////////////////////////
    //  Internal/Private Functions    //
    ////////////////////////////////////

    /**
        This function burns InterShare21 tokens from the sender's balance.
        @param amount The amount of InterShare21 tokens to burn.
        @notice This function is private and can only be called internally.
        @dev It checks if the sender has enough balance before burning the tokens.
    */
    function _burnInterShare21(address from, uint256 amount) private {
        _burn(from, amount);
    }

    /**
        This function mints InterShare21 tokens to a specified address.
        @param to The address to which the InterShare21 tokens will be minted.
        @param amount The amount of InterShare21 tokens to mint.
        @notice This function is private and can only be called internally.
        @dev It uses the _mint function from the ERC20 contract to mint the tokens.
        @dev It includes a check to prevent minting tokens to the contract itself.
    */
    function _mintInterShare21(
        address to,
        uint256 amount
    ) private cannotSendToContract(to) {
        _mint(to, amount);
    }

    /**
        This function overrides the _update function from the ERC20 and ERC20Pausable contracts.
        It is called during token transfers, mints, and burns to ensure that the contract is not paused.
        @param from The address from which tokens are being transferred.
        @param to The address to which tokens are being transferred.
        @param value The amount of tokens being transferred.
        @dev It calls the super implementation of the _update function to perform the actual token transfer logic.
        @dev It adds a check to prevent transfers to the token contract itself.
    */
    function _update(
        address from,
        address to,
        uint256 value
    ) internal override(ERC20, ERC20Pausable) cannotSendToContract(to) {
        super._update(from, to, value);
    }
}
"
    },
    "lib/openzeppelin-contracts/contracts/utils/ReentrancyGuard.sol": {
      "content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/ReentrancyGuard.sol)

pragma solidity ^0.8.20;

/**
 * @dev Contract module that helps prevent reentrant calls to a function.
 *
 * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
 * available, which can be applied to functions to make sure there are no nested
 * (reentrant) calls to them.
 *
 * Note that because there is a single `nonReentrant` guard, functions marked as
 * `nonReentrant` may not call one another. This can be worked around by making
 * those functions `private`, and then adding `external` `nonReentrant` entry
 * points to them.
 *
 * TIP: If EIP-1153 (transient storage) is available on the chain you're deploying at,
 * consider using {ReentrancyGuardTransient} instead.
 *
 * TIP: If you would like to learn more about reentrancy and alternative ways
 * to protect against it, check out our blog post
 * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
 */
abstract contract ReentrancyGuard {
    // Booleans are more expensive than uint256 or any type that takes up a full
    // word because each write operation emits an extra SLOAD to first read the
    // slot's contents, replace the bits taken up by the boolean, and then write
    // back. This is the compiler's defense against contract upgrades and
    // pointer aliasing, and it cannot be disabled.

    // The values being non-zero value makes deployment a bit more expensive,
    // but in exchange the refund on every call to nonReentrant will be lower in
    // amount. Since refunds are capped to a percentage of the total
    // transaction's gas, it is best to keep them low in cases like this one, to
    // increase the likelihood of the full refund coming into effect.
    uint256 private constant NOT_ENTERED = 1;
    uint256 private constant ENTERED = 2;

    uint256 private _status;

    /**
     * @dev Unauthorized reentrant call.
     */
    error ReentrancyGuardReentrantCall();

    constructor() {
        _status = NOT_ENTERED;
    }

    /**
     * @dev Prevents a contract from calling itself, directly or indirectly.
     * Calling a `nonReentrant` function from another `nonReentrant`
     * function is not supported. It is possible to prevent this from happening
     * by making the `nonReentrant` function external, and making it call a
     * `private` function that does the actual work.
     */
    modifier nonReentrant() {
        _nonReentrantBefore();
        _;
        _nonReentrantAfter();
    }

    function _nonReentrantBefore() private {
        // On the first call to nonReentrant, _status will be NOT_ENTERED
        if (_status == ENTERED) {
            revert ReentrancyGuardReentrantCall();
        }

        // Any calls to nonReentrant after this point will fail
        _status = ENTERED;
    }

    function _nonReentrantAfter() private {
        // By storing the original value once again, a refund is triggered (see
        // https://eips.ethereum.org/EIPS/eip-2200)
        _status = NOT_ENTERED;
    }

    /**
     * @dev Returns true if the reentrancy guard is currently set to "entered", which indicates there is a
     * `nonReentrant` function in the call stack.
     */
    function _reentrancyGuardEntered() internal view returns (bool) {
        return _status == ENTERED;
    }
}
"
    },
    "lib/openzeppelin-contracts/contracts/token/ERC20/ERC20.sol": {
      "content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.3.0) (token/ERC20/ERC20.sol)

pragma solidity ^0.8.20;

import {IERC20} from "./IERC20.sol";
import {IERC20Metadata} from "./extensions/IERC20Metadata.sol";
import {Context} from "../../utils/Context.sol";
import {IERC20Errors} from "../../interfaces/draft-IERC6093.sol";

/**
 * @dev Implementation of the {IERC20} interface.
 *
 * This implementation is agnostic to the way tokens are created. This means
 * that a supply mechanism has to be added in a derived contract using {_mint}.
 *
 * TIP: For a detailed writeup see our guide
 * https://forum.openzeppelin.com/t/how-to-implement-erc20-supply-mechanisms/226[How
 * to implement supply mechanisms].
 *
 * The default value of {decimals} is 18. To change this, you should override
 * this function so it returns a different value.
 *
 * We have followed general OpenZeppelin Contracts guidelines: functions revert
 * instead returning `false` on failure. This behavior is nonetheless
 * conventional and does not conflict with the expectations of ERC-20
 * applications.
 */
abstract contract ERC20 is Context, IERC20, IERC20Metadata, IERC20Errors {
    mapping(address account => uint256) private _balances;

    mapping(address account => mapping(address spender => uint256)) private _allowances;

    uint256 private _totalSupply;

    string private _name;
    string private _symbol;

    /**
     * @dev Sets the values for {name} and {symbol}.
     *
     * Both values are immutable: they can only be set once during construction.
     */
    constructor(string memory name_, string memory symbol_) {
        _name = name_;
        _symbol = symbol_;
    }

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

    /**
     * @dev Returns the symbol of the token, usually a shorter version of the
     * name.
     */
    function symbol() public view virtual returns (string memory) {
        return _symbol;
    }

    /**
     * @dev Returns the number of decimals used to get its user representation.
     * For example, if `decimals` equals `2`, a balance of `505` tokens should
     * be displayed to a user as `5.05` (`505 / 10 ** 2`).
     *
     * Tokens usually opt for a value of 18, imitating the relationship between
     * Ether and Wei. This is the default value returned by this function, unless
     * it's overridden.
     *
     * NOTE: This information is only used for _display_ purposes: it in
     * no way affects any of the arithmetic of the contract, including
     * {IERC20-balanceOf} and {IERC20-transfer}.
     */
    function decimals() public view virtual returns (uint8) {
        return 18;
    }

    /**
     * @dev See {IERC20-totalSupply}.
     */
    function totalSupply() public view virtual returns (uint256) {
        return _totalSupply;
    }

    /**
     * @dev See {IERC20-balanceOf}.
     */
    function balanceOf(address account) public view virtual returns (uint256) {
        return _balances[account];
    }

    /**
     * @dev See {IERC20-transfer}.
     *
     * Requirements:
     *
     * - `to` cannot be the zero address.
     * - the caller must have a balance of at least `value`.
     */
    function transfer(address to, uint256 value) public virtual returns (bool) {
        address owner = _msgSender();
        _transfer(owner, to, value);
        return true;
    }

    /**
     * @dev See {IERC20-allowance}.
     */
    function allowance(address owner, address spender) public view virtual returns (uint256) {
        return _allowances[owner][spender];
    }

    /**
     * @dev See {IERC20-approve}.
     *
     * NOTE: If `value` is the maximum `uint256`, the allowance is not updated on
     * `transferFrom`. This is semantically equivalent to an infinite approval.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     */
    function approve(address spender, uint256 value) public virtual returns (bool) {
        address owner = _msgSender();
        _approve(owner, spender, value);
        return true;
    }

    /**
     * @dev See {IERC20-transferFrom}.
     *
     * Skips emitting an {Approval} event indicating an allowance update. This is not
     * required by the ERC. See {xref-ERC20-_approve-address-address-uint256-bool-}[_approve].
     *
     * NOTE: Does not update the allowance if the current allowance
     * is the maximum `uint256`.
     *
     * Requirements:
     *
     * - `from` and `to` cannot be the zero address.
     * - `from` must have a balance of at least `value`.
     * - the caller must have allowance for ``from``'s tokens of at least
     * `value`.
     */
    function transferFrom(address from, address to, uint256 value) public virtual returns (bool) {
        address spender = _msgSender();
        _spendAllowance(from, spender, value);
        _transfer(from, to, value);
        return true;
    }

    /**
     * @dev Moves a `value` amount of tokens from `from` to `to`.
     *
     * This internal function is equivalent to {transfer}, and can be used to
     * e.g. implement automatic token fees, slashing mechanisms, etc.
     *
     * Emits a {Transfer} event.
     *
     * NOTE: This function is not virtual, {_update} should be overridden instead.
     */
    function _transfer(address from, address to, uint256 value) internal {
        if (from == address(0)) {
            revert ERC20InvalidSender(address(0));
        }
        if (to == address(0)) {
            revert ERC20InvalidReceiver(address(0));
        }
        _update(from, to, value);
    }

    /**
     * @dev Transfers a `value` amount of tokens from `from` to `to`, or alternatively mints (or burns) if `from`
     * (or `to`) is the zero address. All customizations to transfers, mints, and burns should be done by overriding
     * this function.
     *
     * Emits a {Transfer} event.
     */
    function _update(address from, address to, uint256 value) internal virtual {
        if (from == address(0)) {
            // Overflow check required: The rest of the code assumes that totalSupply never overflows
            _totalSupply += value;
        } else {
            uint256 fromBalance = _balances[from];
            if (fromBalance < value) {
                revert ERC20InsufficientBalance(from, fromBalance, value);
            }
            unchecked {
                // Overflow not possible: value <= fromBalance <= totalSupply.
                _balances[from] = fromBalance - value;
            }
        }

        if (to == address(0)) {
            unchecked {
                // Overflow not possible: value <= totalSupply or value <= fromBalance <= totalSupply.
                _totalSupply -= value;
            }
        } else {
            unchecked {
                // Overflow not possible: balance + value is at most totalSupply, which we know fits into a uint256.
                _balances[to] += value;
            }
        }

        emit Transfer(from, to, value);
    }

    /**
     * @dev Creates a `value` amount of tokens and assigns them to `account`, by transferring it from address(0).
     * Relies on the `_update` mechanism
     *
     * Emits a {Transfer} event with `from` set to the zero address.
     *
     * NOTE: This function is not virtual, {_update} should be overridden instead.
     */
    function _mint(address account, uint256 value) internal {
        if (account == address(0)) {
            revert ERC20InvalidReceiver(address(0));
        }
        _update(address(0), account, value);
    }

    /**
     * @dev Destroys a `value` amount of tokens from `account`, lowering the total supply.
     * Relies on the `_update` mechanism.
     *
     * Emits a {Transfer} event with `to` set to the zero address.
     *
     * NOTE: This function is not virtual, {_update} should be overridden instead
     */
    function _burn(address account, uint256 value) internal {
        if (account == address(0)) {
            revert ERC20InvalidSender(address(0));
        }
        _update(account, address(0), value);
    }

    /**
     * @dev Sets `value` as the allowance of `spender` over the `owner`'s tokens.
     *
     * This internal function is equivalent to `approve`, and can be used to
     * e.g. set automatic allowances for certain subsystems, etc.
     *
     * Emits an {Approval} event.
     *
     * Requirements:
     *
     * - `owner` cannot be the zero address.
     * - `spender` cannot be the zero address.
     *
     * Overrides to this logic should be done to the variant with an additional `bool emitEvent` argument.
     */
    function _approve(address owner, address spender, uint256 value) internal {
        _approve(owner, spender, value, true);
    }

    /**
     * @dev Variant of {_approve} with an optional flag to enable or disable the {Approval} event.
     *
     * By default (when calling {_approve}) the flag is set to true. On the other hand, approval changes made by
     * `_spendAllowance` during the `transferFrom` operation set the flag to false. This saves gas by not emitting any
     * `Approval` event during `transferFrom` operations.
     *
     * Anyone who wishes to continue emitting `Approval` events on the`transferFrom` operation can force the flag to
     * true using the following override:
     *
     * ```solidity
     * function _approve(address owner, address spender, uint256 value, bool) internal virtual override {
     *     super._approve(owner, spender, value, true);
     * }
     * ```
     *
     * Requirements are the same as {_approve}.
     */
    function _approve(address owner, address spender, uint256 value, bool emitEvent) internal virtual {
        if (owner == address(0)) {
            revert ERC20InvalidApprover(address(0));
        }
        if (spender == address(0)) {
            revert ERC20InvalidSpender(address(0));
        }
        _allowances[owner][spender] = value;
        if (emitEvent) {
            emit Approval(owner, spender, value);
        }
    }

    /**
     * @dev Updates `owner`'s allowance for `spender` based on spent `value`.
     *
     * Does not update the allowance value in case of infinite allowance.
     * Revert if not enough allowance is available.
     *
     * Does not emit an {Approval} event.
     */
    function _spendAllowance(address owner, address spender, uint256 value) internal virtual {
        uint256 currentAllowance = allowance(owner, spender);
        if (currentAllowance < type(uint256).max) {
            if (currentAllowance < value) {
                revert ERC20InsufficientAllowance(spender, currentAllowance, value);
            }
            unchecked {
                _approve(owner, spender, currentAllowance - value, false);
            }
        }
    }
}
"
    },
    "lib/openzeppelin-contracts/contracts/token/ERC20/IERC20.sol": {
      "content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (token/ERC20/IERC20.sol)

pragma solidity ^0.8.20;

/**
 * @dev Interface of the ERC-20 standard as defined in the ERC.
 */
interface IERC20 {
    /**
     * @dev Emitted when `value` tokens are moved from one account (`from`) to
     * another (`to`).
     *
     * Note that `value` may be zero.
     */
    event Transfer(address indexed from, address indexed to, uint256 value);

    /**
     * @dev Emitted when the allowance of a `spender` for an `owner` is set by
     * a call to {approve}. `value` is the new allowance.
     */
    event Approval(address indexed owner, address indexed spender, uint256 value);

    /**
     * @dev Returns the value of tokens in existence.
     */
    function totalSupply() external view returns (uint256);

    /**
     * @dev Returns the value of tokens owned by `account`.
     */
    function balanceOf(address account) external view returns (uint256);

    /**
     * @dev Moves a `value` amount of tokens from the caller's account to `to`.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transfer(address to, uint256 value) external returns (bool);

    /**
     * @dev Returns the remaining number of tokens that `spender` will be
     * allowed to spend on behalf of `owner` through {transferFrom}. This is
     * zero by default.
     *
     * This value changes when {approve} or {transferFrom} are called.
     */
    function allowance(address owner, address spender) external view returns (uint256);

    /**
     * @dev Sets a `value` amount of tokens as the allowance of `spender` over the
     * caller's tokens.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * IMPORTANT: Beware that changing an allowance with this method brings the risk
     * that someone may use both the old and the new allowance by unfortunate
     * transaction ordering. One possible solution to mitigate this race
     * condition is to first reduce the spender's allowance to 0 and set the
     * desired value afterwards:
     * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
     *
     * Emits an {Approval} event.
     */
    function approve(address spender, uint256 value) external returns (bool);

    /**
     * @dev Moves a `value` amount of tokens from `from` to `to` using the
     * allowance mechanism. `value` is then deducted from the caller's
     * allowance.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transferFrom(address from, address to, uint256 value) external returns (bool);
}
"
    },
    "lib/openzeppelin-contracts/contracts/token/ERC20/extensions/ERC20Pausable.sol": {
      "content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (token/ERC20/extensions/ERC20Pausable.sol)

pragma solidity ^0.8.20;

import {ERC20} from "../ERC20.sol";
import {Pausable} from "../../../utils/Pausable.sol";

/**
 * @dev ERC-20 token with pausable token transfers, minting and burning.
 *
 * Useful for scenarios such as preventing trades until the end of an evaluation
 * period, or having an emergency switch for freezing all token transfers in the
 * event of a large bug.
 *
 * IMPORTANT: This contract does not include public pause and unpause functions. In
 * addition to inheriting this contract, you must define both functions, invoking the
 * {Pausable-_pause} and {Pausable-_unpause} internal functions, with appropriate
 * access control, e.g. using {AccessControl} or {Ownable}. Not doing so will
 * make the contract pause mechanism of the contract unreachable, and thus unusable.
 */
abstract contract ERC20Pausable is ERC20, Pausable {
    /**
     * @dev See {ERC20-_update}.
     *
     * Requirements:
     *
     * - the contract must not be paused.
     */
    function _update(address from, address to, uint256 value) internal virtual override whenNotPaused {
        super._update(from, to, value);
    }
}
"
    },
    "lib/openzeppelin-contracts/contracts/token/ERC20/extensions/ERC20Permit.sol": {
      "content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (token/ERC20/extensions/ERC20Permit.sol)

pragma solidity ^0.8.20;

import {IERC20Permit} from "./IERC20Permit.sol";
import {ERC20} from "../ERC20.sol";
import {ECDSA} from "../../../utils/cryptography/ECDSA.sol";
import {EIP712} from "../../../utils/cryptography/EIP712.sol";
import {Nonces} from "../../../utils/Nonces.sol";

/**
 * @dev Implementation of the ERC-20 Permit extension allowing approvals to be made via signatures, as defined in
 * https://eips.ethereum.org/EIPS/eip-2612[ERC-2612].
 *
 * Adds the {permit} method, which can be used to change an account's ERC-20 allowance (see {IERC20-allowance}) by
 * presenting a message signed by the account. By not relying on `{IERC20-approve}`, the token holder account doesn't
 * need to send a transaction, and thus is not required to hold Ether at all.
 */
abstract contract ERC20Permit is ERC20, IERC20Permit, EIP712, Nonces {
    bytes32 private constant PERMIT_TYPEHASH =
        keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)");

    /**
     * @dev Permit deadline has expired.
     */
    error ERC2612ExpiredSignature(uint256 deadline);

    /**
     * @dev Mismatched signature.
     */
    error ERC2612InvalidSigner(address signer, address owner);

    /**
     * @dev Initializes the {EIP712} domain separator using the `name` parameter, and setting `version` to `"1"`.
     *
     * It's a good idea to use the same `name` that is defined as the ERC-20 token name.
     */
    constructor(string memory name) EIP712(name, "1") {}

    /**
     * @inheritdoc IERC20Permit
     */
    function permit(
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) public virtual {
        if (block.timestamp > deadline) {
            revert ERC2612ExpiredSignature(deadline);
        }

        bytes32 structHash = keccak256(abi.encode(PERMIT_TYPEHASH, owner, spender, value, _useNonce(owner), deadline));

        bytes32 hash = _hashTypedDataV4(structHash);

        address signer = ECDSA.recover(hash, v, r, s);
        if (signer != owner) {
            revert ERC2612InvalidSigner(signer, owner);
        }

        _approve(owner, spender, value);
    }

    /**
     * @inheritdoc IERC20Permit
     */
    function nonces(address owner) public view virtual override(IERC20Permit, Nonces) returns (uint256) {
        return super.nonces(owner);
    }

    /**
     * @inheritdoc IERC20Permit
     */
    // solhint-disable-next-line func-name-mixedcase
    function DOMAIN_SEPARATOR() external view virtual returns (bytes32) {
        return _domainSeparatorV4();
    }
}
"
    },
    "lib/openzeppelin-contracts/contracts/token/ERC20/utils/SafeERC20.sol": {
      "content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.3.0) (token/ERC20/utils/SafeERC20.sol)

pragma solidity ^0.8.20;

import {IERC20} from "../IERC20.sol";
import {IERC1363} from "../../../interfaces/IERC1363.sol";

/**
 * @title SafeERC20
 * @dev Wrappers around ERC-20 operations that throw on failure (when the token
 * contract returns false). Tokens that return no value (and instead revert or
 * throw on failure) are also supported, non-reverting calls are assumed to be
 * successful.
 * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
 * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
 */
library SafeERC20 {
    /**
     * @dev An operation with an ERC-20 token failed.
     */
    error SafeERC20FailedOperation(address token);

    /**
     * @dev Indicates a failed `decreaseAllowance` request.
     */
    error SafeERC20FailedDecreaseAllowance(address spender, uint256 currentAllowance, uint256 requestedDecrease);

    /**
     * @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value,
     * non-reverting calls are assumed to be successful.
     */
    function safeTransfer(IERC20 token, address to, uint256 value) internal {
        _callOptionalReturn(token, abi.encodeCall(token.transfer, (to, value)));
    }

    /**
     * @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the
     * calling contract. If `token` returns no value, non-reverting calls are assumed to be successful.
     */
    function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
        _callOptionalReturn(token, abi.encodeCall(token.transferFrom, (from, to, value)));
    }

    /**
     * @dev Variant of {safeTransfer} that returns a bool instead of reverting if the operation is not successful.
     */
    function trySafeTransfer(IERC20 token, address to, uint256 value) internal returns (bool) {
        return _callOptionalReturnBool(token, abi.encodeCall(token.transfer, (to, value)));
    }

    /**
     * @dev Variant of {safeTransferFrom} that returns a bool instead of reverting if the operation is not successful.
     */
    function trySafeTransferFrom(IERC20 token, address from, address to, uint256 value) internal returns (bool) {
        return _callOptionalReturnBool(token, abi.encodeCall(token.transferFrom, (from, to, value)));
    }

    /**
     * @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
     * non-reverting calls are assumed to be successful.
     *
     * IMPORTANT: If the token implements ERC-7674 (ERC-20 with temporary allowance), and if the "client"
     * smart contract uses ERC-7674 to set temporary allowances, then the "client" smart contract should avoid using
     * this function. Performing a {safeIncreaseAllowance} or {safeDecreaseAllowance} operation on a token contract
     * that has a non-zero temporary allowance (for that particular owner-spender) will result in unexpected behavior.
     */
    function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
        uint256 oldAllowance = token.allowance(address(this), spender);
        forceApprove(token, spender, oldAllowance + value);
    }

    /**
     * @dev Decrease the calling contract's allowance toward `spender` by `requestedDecrease`. If `token` returns no
     * value, non-reverting calls are assumed to be successful.
     *
     * IMPORTANT: If the token implements ERC-7674 (ERC-20 with temporary allowance), and if the "client"
     * smart contract uses ERC-7674 to set temporary allowances, then the "client" smart contract should avoid using
     * this function. Performing a {safeIncreaseAllowance} or {safeDecreaseAllowance} operation on a token contract
     * that has a non-zero temporary allowance (for that particular owner-spender) will result in unexpected behavior.
     */
    function safeDecreaseAllowance(IERC20 token, address spender, uint256 requestedDecrease) internal {
        unchecked {
            uint256 currentAllowance = token.allowance(address(this), spender);
            if (currentAllowance < requestedDecrease) {
                revert SafeERC20FailedDecreaseAllowance(spender, currentAllowance, requestedDecrease);
            }
            forceApprove(token, spender, currentAllowance - requestedDecrease);
        }
    }

    /**
     * @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value,
     * non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval
     * to be set to zero before setting it to a non-zero value, such as USDT.
     *
     * NOTE: If the token implements ERC-7674, this function will not modify any temporary allowance. This function
     * only sets the "standard" allowance. Any temporary allowance will remain active, in addition to the value being
     * set here.
     */
    function forceApprove(IERC20 token, address spender, uint256 value) internal {
        bytes memory approvalCall = abi.encodeCall(token.approve, (spender, value));

        if (!_callOptionalReturnBool(token, approvalCall)) {
            _callOptionalReturn(token, abi.encodeCall(token.approve, (spender, 0)));
            _callOptionalReturn(token, approvalCall);
        }
    }

    /**
     * @dev Performs an {ERC1363} transferAndCall, with a fallback to the simple {ERC20} transfer if the target has no
     * code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when
     * targeting contracts.
     *
     * Reverts if the returned value is other than `true`.
     */
    function transferAndCallRelaxed(IERC1363 token, address to, uint256 value, bytes memory data) internal {
        if (to.code.length == 0) {
            safeTransfer(token, to, value);
        } else if (!token.transferAndCall(to, value, data)) {
            revert SafeERC20FailedOperation(address(token));
        }
    }

    /**
     * @dev Performs an {ERC1363} transferFromAndCall, with a fallback to the simple {ERC20} transferFrom if the target
     * has no code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when
     * targeting contracts.
     *
     * Reverts if the returned value is other than `true`.
     */
    function transferFromAndCallRelaxed(
        IERC1363 token,
        address from,
        address to,
        uint256 value,
        bytes memory data
    ) internal {
        if (to.code.length == 0) {
            safeTransferFrom(token, from, to, value);
        } else if (!token.transferFromAndCall(from, to, value, data)) {
            revert SafeERC20FailedOperation(address(token));
        }
    }

    /**
     * @dev Performs an {ERC1363} approveAndCall, with a fallback to the simple {ERC20} approve if the target has no
     * code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when
     * targeting contracts.
     *
     * NOTE: When the recipient address (`to`) has no code (i.e. is an EOA), this function behaves as {forceApprove}.
     * Opposedly, when the recipient address (`to`) has code, this function only attempts to call {ERC1363-approveAndCall}
     * once without retrying, and relies on the returned value to be true.
     *
     * Reverts if the returned value is other than `true`.
     */
    function approveAndCallRelaxed(IERC1363 token, address to, uint256 value, bytes memory data) internal {
        if (to.code.length == 0) {
            forceApprove(token, to, value);
        } else if (!token.approveAndCall(to, value, data)) {
            revert SafeERC20FailedOperation(address(token));
        }
    }

    /**
     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
     * on the return value: the return value is optional (but if data is returned, it must not be false).
     * @param token The token targeted by the call.
     * @param data The call data (encoded using abi.encode or one of its variants).
     *
     * This is a variant of {_callOptionalReturnBool} that reverts if call fails to meet the requirements.
     */
    function _callOptionalReturn(IERC20 token, bytes memory data) private {
        uint256 returnSize;
        uint256 returnValue;
        assembly ("memory-safe") {
            let success := call(gas(), token, 0, add(data, 0x20), mload(data), 0, 0x20)
            // bubble errors
            if iszero(success) {
                let ptr := mload(0x40)
                returndatacopy(ptr, 0, returndatasize())
                revert(ptr, returndatasize())
            }
            returnSize := returndatasize()
            returnValue := mload(0)
        }

        if (returnSize == 0 ? address(token).code.length == 0 : returnValue != 1) {
            revert SafeERC20FailedOperation(address(token));
        }
    }

    /**
     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
     * on the return value: the return value is optional (but if data is returned, it must not be false).
     * @param token The token targeted by the call.
     * @param data The call data (encoded using abi.encode or one of its variants).
     *
     * This is a variant of {_callOptionalReturn} that silently catches all reverts and returns a bool instead.
     */
    function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
        bool success;
        uint256 returnSize;
        uint256 returnValue;
        assembly ("memory-safe") {
            success := call(gas(), token, 0, add(data, 0x20), mload(data), 0, 0x20)
            returnSize := returndatasize()
            returnValue := mload(0)
        }
        return success && (returnSize == 0 ? address(token).code.length > 0 : returnValue == 1);
    }
}
"
    },
    "lib/openzeppelin-contracts/contracts/access/Ownable.sol": {
      "content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable.sol)

pragma solidity ^0.8.20;

import {Context} from "../utils/Context.sol";

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

    /**
     * @dev The caller account is not authorized to perform an operation.
     */
    error OwnableUnauthorizedAccount(address account);

    /**
     * @dev The owner is not a valid owner account. (eg. `address(0)`)
     */
    error OwnableInvalidOwner(address owner);

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

    /**
     * @dev Initializes the contract setting the address provided by the deployer as the initial owner.
     */
    constructor(address initialOwner) {
        if (initialOwner == address(0)) {
            revert OwnableInvalidOwner(address(0));
        }
        _transferOwnership(initialOwner);
    }

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

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

    /**
     * @dev Throws if the sender is not the owner.
     */
    function _checkOwner() internal view virtual {
        if (owner() != _msgSender()) {
            revert OwnableUnauthorizedAccount(_msgSender());
        }
    }

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

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Can only be called by the current owner.
     */
    function transferOwnership(address newOwner) public virtual onlyOwner {
        if (newOwner == address(0)) {
            revert OwnableInvalidOwner(address(0));
        }
        _transferOwnership(newOwner);
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Internal function without access restriction.
     */
    function _transferOwnership(address newOwner) internal virtual {
        address oldOwner = _owner;
        _owner = newOwner;
        emit OwnershipTransferred(oldOwner, newOwner);
    }
}
"
    },
    "lib/openzeppelin-contracts/contracts/utils/structs/EnumerableSet.sol": {
      "content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.3.0) (utils/structs/EnumerableSet.sol)
// This file was procedurally generated from scripts/generate/templates/EnumerableSet.js.

pragma solidity ^0.8.20;

import {Arrays} from "../Arrays.sol";

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

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

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

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

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

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

            if (valueIndex != lastIndex) {
                bytes32 lastValue = set._values[lastIndex];

                // Move the lastValue to the index where the value to delete is
                set._values[valueIndex] = lastValue;
                // Update the tracked position of the lastValue (that was just moved)
                set._positions[lastValue] = position;
            }

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

            // Delete the tracked position for the deleted slot
            delete set._positions[value];

            return true;
        } else {
            return false;
        }
    }

    /**
     * @dev Removes all the values from a set. O(n).
     *
     * WARNING: Developers should keep in mind that this function has an unbounded cost and using it may render the
     * function uncallable if the set grows to the point where clearing it consumes too much gas to fit in a block.
     */
    function _clear(Set storage set) private {
        uint256 len = _length(set);
        for (uint256 i = 0; i < len; ++i) {
            delete set._positions[set._values[i]];
        }
        Arrays.unsafeSetLength(set._values, 0);
    }

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

    /**
     * @dev Returns the number of values on the set. O(1).
     */
    function _length(Set storage set) private v

Tags:
ERC20, ERC165, Multisig, Pausable, Swap, Upgradeable, Multi-Signature, Factory, Oracle|addr:0x3619a9103397121b6157859504637689b5c67c3a|verified:true|block:23689851|tx:0x287d88cb796f2935aa16041654dcce970a906a140a0bcf815f8fec47c208c30e|first_check:1761830747

Submitted on: 2025-10-30 14:25:50

Comments

Log in to comment.

No comments yet.