SwapperEngine

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/swapperEngine/SwapperEngine.sol": {
      "content": "// SPDX-License-Identifier: Apache-2.0

pragma solidity 0.8.20;

import {IERC20} from "openzeppelin-contracts/token/ERC20/IERC20.sol";
import {IERC20Metadata} from "openzeppelin-contracts/token/ERC20/extensions/IERC20Metadata.sol";
import {IERC20Permit} from "openzeppelin-contracts/token/ERC20/extensions/IERC20Permit.sol";
import {PausableUpgradeable} from "openzeppelin-contracts-upgradeable/utils/PausableUpgradeable.sol";
import {SafeERC20} from "openzeppelin-contracts/token/ERC20/utils/SafeERC20.sol";
import {Initializable} from "openzeppelin-contracts-upgradeable/proxy/utils/Initializable.sol";
import {ReentrancyGuardUpgradeable} from
    "openzeppelin-contracts-upgradeable/utils/ReentrancyGuardUpgradeable.sol";

import {IRegistryContract} from "src/interfaces/registry/IRegistryContract.sol";

import {IRegistryAccess} from "src/interfaces/registry/IRegistryAccess.sol";
import {ISwapperEngine} from "src/interfaces/swapperEngine/ISwapperEngine.sol";
import {IEur0} from "src/interfaces/token/IEur0.sol";

import {IOracle} from "src/interfaces/oracles/IOracle.sol";

import {Normalize} from "src/utils/normalize.sol";
import {CheckAccessControl} from "src/utils/CheckAccessControl.sol";
import {
    ONE_EURC,
    DEFAULT_ADMIN_ROLE,
    PAUSING_CONTRACTS_ROLE,
    UNPAUSING_CONTRACTS_ROLE,
    CONTRACT_REGISTRY_ACCESS,
    CONTRACT_ORACLE,
    EURC,
    CONTRACT_EUR0,
    MINIMUM_EURC_PROVIDED
} from "src/constants.sol";

import {
    InsufficientEUR0Balance,
    OrderNotActive,
    NoOrdersIdsProvided,
    NotRecipient,
    AmountTooLow,
    AmountIsZero,
    NotAuthorized,
    NullContract,
    NullAddress
} from "src/errors.sol";

/// @title SwapperEngine
/// @notice A contract for swapping EURC tokens for EUR0 tokens using order matching.
/// @dev This contract allows users to deposit EURC tokens, create orders, and match those orders
///      against EUR0 tokens provided by other users.
///      Order matching works by allowing users to create individual orders specifying the amount
///      of EURC tokens they wish to swap. Other users can then provide EUR0 tokens to match against
///      these orders, directly swapping their EUR0 tokens for the EURC tokens in the orders.
///      Tradeoffs:
///      + Direct swaps: Users can directly swap their tokens with each other, without the need for
///        intermediary liquidity pools.
///      + Low Slippage: The price of the swap is determined by the eurc oracle price at that block height irrespective of liquidity depth.
///      - Liquidity: Order matching relies on the presence of active orders to facilitate swaps.
///        If there are no matching orders available, users may need to wait for new orders to be created.
///      - Match to Market: The price of the swaps is determined by the individual orders when they are executed not when they are placed (offset by low price volatility of stables)
/// @custom:mechanism Effectively this facilitates RWA --> EUR0 --> EURC --> EUR0 --> RWA ... limited only by EURC orderbook depth
/// @author  Usual Tech team
contract SwapperEngine is
    Initializable,
    ReentrancyGuardUpgradeable,
    PausableUpgradeable,
    ISwapperEngine
{
    using SafeERC20 for IERC20;
    using SafeERC20 for IEur0;
    using Normalize for uint256;
    using CheckAccessControl for IRegistryAccess;

    struct SwapperEngineStorageV0 {
        IRegistryAccess registryAccess;
        IRegistryContract registryContract;
        IOracle oracle;
        IERC20 eurcToken;
        IEur0 eur0;
        mapping(uint256 => EurcOrder) orders;
        uint256 nextOrderId;
        uint256 minimumEURCAmountProvided;
    }

    // keccak256(abi.encode(uint256(keccak256("swapperengine.storage.v0")) - 1)) & ~bytes32(uint256(0xff))
    // solhint-disable-next-line
    bytes32 public constant SwapperEngineStorageV0Location =
        0x6c3529a15b63e79e1691946ad3dcd9eb824ac76513a1aed382fd5661938dea00;

    /// @notice Returns the storage struct of the contract.
    /// @return $ .
    function _swapperEngineStorageV0() internal pure returns (SwapperEngineStorageV0 storage $) {
        bytes32 position = SwapperEngineStorageV0Location;
        // solhint-disable-next-line no-inline-assembly
        assembly {
            $.slot := position
        }
    }

    /*//////////////////////////////////////////////////////////////
                             Constructor
    //////////////////////////////////////////////////////////////*/

    /// @custom:oz-upgrades-unsafe-allow constructor
    constructor() {
        _disableInitializers();
    }

    /// @notice Function for initializing the contract.
    /// @dev This function is used to set the initial state of the contract.
    /// @param registryContract The registry contract address.
    function initialize(address registryContract) public initializer {
        if (registryContract == address(0)) {
            revert NullContract();
        }
        __Pausable_init_unchained();
        __ReentrancyGuard_init_unchained();
        SwapperEngineStorageV0 storage $ = _swapperEngineStorageV0();
        $.registryContract = IRegistryContract(registryContract);
        $.registryAccess = IRegistryAccess($.registryContract.getContract(CONTRACT_REGISTRY_ACCESS));
        $.eurcToken = IERC20(EURC);
        $.eur0 = IEur0($.registryContract.getContract(CONTRACT_EUR0));
        $.nextOrderId = 1;
        $.oracle = IOracle($.registryContract.getContract(CONTRACT_ORACLE));
        $.minimumEURCAmountProvided = MINIMUM_EURC_PROVIDED;
    }

    /*//////////////////////////////////////////////////////////////
                           Restricted Functions
    //////////////////////////////////////////////////////////////*/

    /// @inheritdoc ISwapperEngine
    function updateMinimumEURCAmountProvided(uint256 minimumEURCAmount) external {
        if (minimumEURCAmount < ONE_EURC) {
            // Minimum amount must be at least 1 EURC
            revert AmountTooLow();
        }
        SwapperEngineStorageV0 storage $ = _swapperEngineStorageV0();
        $.registryAccess.onlyMatchingRole(DEFAULT_ADMIN_ROLE);
        $.minimumEURCAmountProvided = minimumEURCAmount;
    }

    /// @inheritdoc ISwapperEngine
    function pause() external {
        SwapperEngineStorageV0 storage $ = _swapperEngineStorageV0();
        $.registryAccess.onlyMatchingRole(PAUSING_CONTRACTS_ROLE);
        _pause();
    }

    /// @inheritdoc ISwapperEngine
    function unpause() external {
        SwapperEngineStorageV0 storage $ = _swapperEngineStorageV0();
        $.registryAccess.onlyMatchingRole(UNPAUSING_CONTRACTS_ROLE);
        _unpause();
    }

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

    ///@notice Retrieves the current price of EURC in WAD format (18 decimals).
    ///@dev This function fetches the price of EURC from the oracle contract and returns it in WAD format.
    ///@return eurcWadPrice The current price of EURC in WAD format.
    function _getEurcWadPrice() private view returns (uint256 eurcWadPrice) {
        SwapperEngineStorageV0 storage $ = _swapperEngineStorageV0();
        eurcWadPrice = uint256($.oracle.getPrice(address($.eurcToken)));
    }

    ///@notice Calculates the EUR0 equivalent amount in WAD format for a given EURC token amount.
    ///@dev This function converts the EURC token amount from its native decimal representation to WAD format (18 decimals),
    ///     and then calculates the equivalent EUR0 amount based on the provided EURC price in WAD format.
    ///@param eurcTokenAmountInNativeDecimals The amount of EURC tokens in their native decimal representation (6 decimals).
    ///@param eurcWadPrice The price of EURC in WAD format.
    ///@return eur0WadEquivalent The equivalent amount of EUR0 in WAD format.
    function _getEur0WadEquivalent(uint256 eurcTokenAmountInNativeDecimals, uint256 eurcWadPrice)
        private
        view
        returns (uint256 eur0WadEquivalent)
    {
        SwapperEngineStorageV0 storage $ = _swapperEngineStorageV0();
        uint8 decimals = IERC20Metadata(address($.eurcToken)).decimals();
        uint256 eurcWad = eurcTokenAmountInNativeDecimals.tokenAmountToWad(decimals);
        eur0WadEquivalent = eurcWad.wadAmountByPrice(eurcWadPrice);
    }

    /// @notice Calculates the EURC token amount in native decimals for a given EUR0 amount in WAD format.
    /// @dev This function calculates the expected EURC amount to receive based on the provided eur0WadAmount and EURC price in WAD format.
    /// @param eur0WadAmount The amount of EUR0 in WAD format.
    /// @param eurcWadPrice The price of EURC in WAD format.
    /// @return eurcTokenAmountInNativeDecimals The equivalent amount of EURC tokens in their native decimal representation.
    function _getEurcAmountFromEur0WadEquivalent(uint256 eur0WadAmount, uint256 eurcWadPrice)
        private
        view
        returns (uint256 eurcTokenAmountInNativeDecimals)
    {
        SwapperEngineStorageV0 storage $ = _swapperEngineStorageV0();
        uint8 decimals = IERC20Metadata(address($.eurcToken)).decimals();
        eurcTokenAmountInNativeDecimals =
            eur0WadAmount.wadTokenAmountForPrice(eurcWadPrice, decimals);
    }

    function _depositEURC(
        SwapperEngineStorageV0 storage $,
        uint256 amountToDeposit,
        address sender,
        address recipient
    ) internal {
        if (amountToDeposit < $.minimumEURCAmountProvided) {
            // amountToDeposit must be equal or greater than the minimumEURCAmountProvided storage value
            revert AmountTooLow();
        }
        if (recipient == address(0)) {
            revert NullAddress();
        }
        if ($.eur0.isBlacklisted(sender) || $.eur0.isBlacklisted(recipient)) {
            revert NotAuthorized();
        }

        uint256 orderId = $.nextOrderId++;
        $.orders[orderId] =
            EurcOrder({recipient: recipient, tokenAmount: amountToDeposit, active: true});

        // Transfer EURC tokens to this contract
        $.eurcToken.safeTransferFrom(sender, address(this), amountToDeposit);

        emit Deposit(sender, recipient, orderId, amountToDeposit);
    }

    /// @notice Allows a user to provide EUR0 tokens and receive EURC tokens by matching against existing orders.
    /// @dev This function allows users to specify an amount of EURC tokens they want, calculating the corresponding
    ///      EUR0 tokens they need and exchanging it against active orders.
    /// @param recipient The address to receive the EUR0 tokens.
    /// @param amountEurcToTakeInNativeDecimals The amount of EURC tokens to take, in the token's native decimal representation.
    /// @param orderIdsToTake An array of order IDs to match against.
    /// @param partialMatchingAllowed A flag indicating whether partial matching is allowed.
    /// @return unmatchedEurcAmount The amount of EURC tokens that were not matched.
    /// @return totalEur0Provided The total amount of EUR0 tokens provided.
    function _provideEur0ReceiveEURC( // solhint-disable-line
        address recipient,
        uint256 amountEurcToTakeInNativeDecimals,
        uint256[] memory orderIdsToTake,
        bool partialMatchingAllowed,
        uint256 eurcWadPrice
    ) internal returns (uint256 unmatchedEurcAmount, uint256 totalEur0Provided) {
        if (amountEurcToTakeInNativeDecimals == 0) {
            // Amount must be greater than 0
            revert AmountIsZero();
        }
        if (orderIdsToTake.length == 0) {
            revert NoOrdersIdsProvided();
        }

        SwapperEngineStorageV0 storage $ = _swapperEngineStorageV0();

        uint256 totalEurcTaken = 0;

        for (
            uint256 i;
            i < orderIdsToTake.length && totalEurcTaken < amountEurcToTakeInNativeDecimals;
        ) {
            uint256 orderId = orderIdsToTake[i];
            EurcOrder storage order = $.orders[orderId];

            if (order.active) {
                uint256 remainingAmountToTake = amountEurcToTakeInNativeDecimals - totalEurcTaken;
                // if the eurcOrder tokenAmount > remainingAmountToTake only take the remaining else take the whole order
                uint256 amountOfEurcFromOrder = order.tokenAmount > remainingAmountToTake
                    ? remainingAmountToTake
                    : order.tokenAmount;

                // @NOTE oracle price check & calculation of nominal EURC TokenAmount to EUR in 18 decimals.
                // EUR0 has 18 decimals and we are considering it with a static EUR value of 1.
                // EURC has 6 decimals, needs to be normalized to 18 decimals.

                order.tokenAmount -= amountOfEurcFromOrder;
                totalEurcTaken += amountOfEurcFromOrder;

                if (order.tokenAmount == 0) {
                    order.active = false;
                }

                uint256 eur0Amount = _getEur0WadEquivalent(amountOfEurcFromOrder, eurcWadPrice);
                totalEur0Provided += eur0Amount;
                // Transfer EUR0 from sender to order recipient
                $.eur0.safeTransferFrom(msg.sender, order.recipient, eur0Amount);

                emit OrderMatched(order.recipient, msg.sender, orderId, amountOfEurcFromOrder);
            }

            unchecked {
                ++i;
            }
        }

        // Transfer EURC from this contract to the recipient
        $.eurcToken.safeTransfer(recipient, totalEurcTaken);
        // Revert if partial matching is not allowed and we haven't taken all of the EUR0
        if (
            !partialMatchingAllowed && totalEurcTaken != amountEurcToTakeInNativeDecimals
                || totalEurcTaken == 0
        ) {
            revert AmountTooLow();
        }

        return ((amountEurcToTakeInNativeDecimals - totalEurcTaken), totalEur0Provided);
    }

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

    /// @inheritdoc ISwapperEngine
    function depositEURC(uint256 amountToDeposit) external nonReentrant whenNotPaused {
        SwapperEngineStorageV0 storage $ = _swapperEngineStorageV0();
        _depositEURC($, amountToDeposit, msg.sender, msg.sender);
    }

    /// @inheritdoc ISwapperEngine
    function depositEURCOnBehalf(uint256 amountToDeposit, address recipient)
        external
        nonReentrant
        whenNotPaused
    {
        SwapperEngineStorageV0 storage $ = _swapperEngineStorageV0();
        _depositEURC($, amountToDeposit, msg.sender, recipient);
    }

    /// @inheritdoc ISwapperEngine
    function depositEURCWithPermit(
        uint256 amountToDeposit,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external nonReentrant whenNotPaused {
        SwapperEngineStorageV0 storage $ = _swapperEngineStorageV0();
        try IERC20Permit(address($.eurcToken)).permit(
            msg.sender, address(this), amountToDeposit, deadline, v, r, s
        ) {} catch {} // solhint-disable-line no-empty-blocks
        _depositEURC($, amountToDeposit, msg.sender, msg.sender);
    }

    /// @inheritdoc ISwapperEngine
    function withdrawEURC(uint256 orderToCancel) external nonReentrant whenNotPaused {
        SwapperEngineStorageV0 storage $ = _swapperEngineStorageV0();
        EurcOrder storage order = $.orders[orderToCancel];

        if (!order.active) {
            // Order not active or does not exist
            revert OrderNotActive();
        }
        if (order.recipient != msg.sender) {
            // Only the recipient can cancel their order
            revert NotRecipient();
        }

        uint256 amountToWithdraw = order.tokenAmount;
        order.active = false; // Deactivate the order
        order.tokenAmount = 0; // Set the amount to zero

        // Transfer EURC back to the recipient
        $.eurcToken.safeTransfer(order.recipient, amountToWithdraw);

        emit Withdraw(order.recipient, orderToCancel, amountToWithdraw);
    }

    /// @inheritdoc ISwapperEngine
    function provideEur0ReceiveEURC(
        address recipient,
        uint256 amountEurcToTakeInNativeDecimals,
        uint256[] memory orderIdsToTake,
        bool partialMatchingAllowed
    ) external nonReentrant whenNotPaused returns (uint256) {
        SwapperEngineStorageV0 storage $ = _swapperEngineStorageV0();
        uint256 eurcWadPrice = _getEurcWadPrice();
        uint256 requiredEur0Amount =
            _getEur0WadEquivalent(amountEurcToTakeInNativeDecimals, eurcWadPrice);
        if ($.eur0.balanceOf(msg.sender) < requiredEur0Amount) {
            revert InsufficientEUR0Balance();
        }
        (uint256 unmatchedEurcAmount,) = _provideEur0ReceiveEURC(
            recipient,
            amountEurcToTakeInNativeDecimals,
            orderIdsToTake,
            partialMatchingAllowed,
            eurcWadPrice
        );
        return unmatchedEurcAmount;
    }

    /// @inheritdoc ISwapperEngine
    function provideEur0ReceiveEURCWithPermit(
        address recipient,
        uint256 amountEurcToTakeInNativeDecimals,
        uint256[] memory orderIdsToTake,
        bool partialMatchingAllowed,
        uint256 eur0ToPermit,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external nonReentrant whenNotPaused returns (uint256) {
        SwapperEngineStorageV0 storage $ = _swapperEngineStorageV0();
        uint256 eurcWadPrice = _getEurcWadPrice();
        uint256 requiredEur0Amount =
            _getEur0WadEquivalent(amountEurcToTakeInNativeDecimals, eurcWadPrice);
        // Authorization transfer
        if ($.eur0.balanceOf(msg.sender) < requiredEur0Amount || eur0ToPermit < requiredEur0Amount)
        {
            revert InsufficientEUR0Balance();
        }
        try IERC20Permit(address($.eur0)).permit(
            msg.sender, address(this), eur0ToPermit, deadline, v, r, s
        ) {} catch {} // solhint-disable-line no-empty-blocks
        (uint256 unmatchedEurcAmount,) = _provideEur0ReceiveEURC(
            recipient,
            amountEurcToTakeInNativeDecimals,
            orderIdsToTake,
            partialMatchingAllowed,
            eurcWadPrice
        );
        return unmatchedEurcAmount;
    }

    /// @inheritdoc ISwapperEngine
    function swapEur0(
        address recipient,
        uint256 amountEur0ToProvideInWad,
        uint256[] memory orderIdsToTake,
        bool partialMatchingAllowed
    ) external nonReentrant whenNotPaused returns (uint256) {
        uint256 eurcWadPrice = _getEurcWadPrice();

        (, uint256 totalEur0Provided) = _provideEur0ReceiveEURC(
            recipient,
            _getEurcAmountFromEur0WadEquivalent(amountEur0ToProvideInWad, eurcWadPrice),
            orderIdsToTake,
            partialMatchingAllowed,
            eurcWadPrice
        );

        return amountEur0ToProvideInWad - totalEur0Provided;
    }

    /*//////////////////////////////////////////////////////////////
                            Getter Functions
    //////////////////////////////////////////////////////////////*/

    /// @inheritdoc ISwapperEngine
    function minimumEURCAmountProvided() public view returns (uint256 minimumEURCAmount) {
        SwapperEngineStorageV0 storage $ = _swapperEngineStorageV0();
        minimumEURCAmount = $.minimumEURCAmountProvided;
    }

    /// @inheritdoc ISwapperEngine
    function getOrder(uint256 orderId) public view returns (bool active, uint256 tokenAmount) {
        SwapperEngineStorageV0 storage $ = _swapperEngineStorageV0();
        EurcOrder memory order = $.orders[orderId];
        active = order.active;
        tokenAmount = order.tokenAmount;
    }

    /// @inheritdoc ISwapperEngine
    function getNextOrderId() external view override returns (uint256) {
        SwapperEngineStorageV0 storage $ = _swapperEngineStorageV0();
        return $.nextOrderId;
    }
}
"
    },
    "lib/openzeppelin-contracts/contracts/token/ERC20/IERC20.sol": {
      "content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/IERC20.sol)

pragma solidity ^0.8.20;

/**
 * @dev Interface of the ERC20 standard as defined in the EIP.
 */
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/IERC20Metadata.sol": {
      "content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/extensions/IERC20Metadata.sol)

pragma solidity ^0.8.20;

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

/**
 * @dev Interface for the optional metadata functions from the ERC20 standard.
 */
interface IERC20Metadata is IERC20 {
    /**
     * @dev Returns the name of the token.
     */
    function name() external view returns (string memory);

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

    /**
     * @dev Returns the decimals places of the token.
     */
    function decimals() external view returns (uint8);
}
"
    },
    "lib/openzeppelin-contracts/contracts/token/ERC20/extensions/IERC20Permit.sol": {
      "content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/extensions/IERC20Permit.sol)

pragma solidity ^0.8.20;

/**
 * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
 * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
 *
 * Adds the {permit} method, which can be used to change an account's ERC20 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.
 *
 * ==== Security Considerations
 *
 * There are two important considerations concerning the use of `permit`. The first is that a valid permit signature
 * expresses an allowance, and it should not be assumed to convey additional meaning. In particular, it should not be
 * considered as an intention to spend the allowance in any specific way. The second is that because permits have
 * built-in replay protection and can be submitted by anyone, they can be frontrun. A protocol that uses permits should
 * take this into consideration and allow a `permit` call to fail. Combining these two aspects, a pattern that may be
 * generally recommended is:
 *
 * ```solidity
 * function doThingWithPermit(..., uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) public {
 *     try token.permit(msg.sender, address(this), value, deadline, v, r, s) {} catch {}
 *     doThing(..., value);
 * }
 *
 * function doThing(..., uint256 value) public {
 *     token.safeTransferFrom(msg.sender, address(this), value);
 *     ...
 * }
 * ```
 *
 * Observe that: 1) `msg.sender` is used as the owner, leaving no ambiguity as to the signer intent, and 2) the use of
 * `try/catch` allows the permit to fail and makes the code tolerant to frontrunning. (See also
 * {SafeERC20-safeTransferFrom}).
 *
 * Additionally, note that smart contract wallets (such as Argent or Safe) are not able to produce permit signatures, so
 * contracts should have entry points that don't rely on permit.
 */
interface IERC20Permit {
    /**
     * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,
     * given ``owner``'s signed approval.
     *
     * IMPORTANT: The same issues {IERC20-approve} has related to transaction
     * ordering also apply here.
     *
     * Emits an {Approval} event.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     * - `deadline` must be a timestamp in the future.
     * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
     * over the EIP712-formatted function arguments.
     * - the signature must use ``owner``'s current nonce (see {nonces}).
     *
     * For more information on the signature format, see the
     * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
     * section].
     *
     * CAUTION: See Security Considerations above.
     */
    function permit(
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external;

    /**
     * @dev Returns the current nonce for `owner`. This value must be
     * included whenever a signature is generated for {permit}.
     *
     * Every successful call to {permit} increases ``owner``'s nonce by one. This
     * prevents a signature from being used multiple times.
     */
    function nonces(address owner) external view returns (uint256);

    /**
     * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.
     */
    // solhint-disable-next-line func-name-mixedcase
    function DOMAIN_SEPARATOR() external view returns (bytes32);
}
"
    },
    "lib/openzeppelin-contracts-upgradeable/contracts/utils/PausableUpgradeable.sol": {
      "content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/Pausable.sol)

pragma solidity ^0.8.20;

import {ContextUpgradeable} from "../utils/ContextUpgradeable.sol";
import {Initializable} from "../proxy/utils/Initializable.sol";

/**
 * @dev Contract module which allows children to implement an emergency stop
 * mechanism that can be triggered by an authorized account.
 *
 * This module is used through inheritance. It will make available the
 * modifiers `whenNotPaused` and `whenPaused`, which can be applied to
 * the functions of your contract. Note that they will not be pausable by
 * simply including this module, only once the modifiers are put in place.
 */
abstract contract PausableUpgradeable is Initializable, ContextUpgradeable {
    /// @custom:storage-location erc7201:openzeppelin.storage.Pausable
    struct PausableStorage {
        bool _paused;
    }

    // keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.Pausable")) - 1)) & ~bytes32(uint256(0xff))
    bytes32 private constant PausableStorageLocation = 0xcd5ed15c6e187e77e9aee88184c21f4f2182ab5827cb3b7e07fbedcd63f03300;

    function _getPausableStorage() private pure returns (PausableStorage storage $) {
        assembly {
            $.slot := PausableStorageLocation
        }
    }

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

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

    /**
     * @dev The operation failed because the contract is paused.
     */
    error EnforcedPause();

    /**
     * @dev The operation failed because the contract is not paused.
     */
    error ExpectedPause();

    /**
     * @dev Initializes the contract in unpaused state.
     */
    function __Pausable_init() internal onlyInitializing {
        __Pausable_init_unchained();
    }

    function __Pausable_init_unchained() internal onlyInitializing {
        PausableStorage storage $ = _getPausableStorage();
        $._paused = false;
    }

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

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

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

    /**
     * @dev Throws if the contract is paused.
     */
    function _requireNotPaused() internal view virtual {
        if (paused()) {
            revert EnforcedPause();
        }
    }

    /**
     * @dev Throws if the contract is not paused.
     */
    function _requirePaused() internal view virtual {
        if (!paused()) {
            revert ExpectedPause();
        }
    }

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

    /**
     * @dev Returns to normal state.
     *
     * Requirements:
     *
     * - The contract must be paused.
     */
    function _unpause() internal virtual whenPaused {
        PausableStorage storage $ = _getPausableStorage();
        $._paused = false;
        emit Unpaused(_msgSender());
    }
}
"
    },
    "lib/openzeppelin-contracts/contracts/token/ERC20/utils/SafeERC20.sol": {
      "content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/utils/SafeERC20.sol)

pragma solidity ^0.8.20;

import {IERC20} from "../IERC20.sol";
import {IERC20Permit} from "../extensions/IERC20Permit.sol";
import {Address} from "../../../utils/Address.sol";

/**
 * @title SafeERC20
 * @dev Wrappers around ERC20 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 {
    using Address for address;

    /**
     * @dev An operation with an ERC20 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 Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
     * non-reverting calls are assumed to be successful.
     */
    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.
     */
    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.
     */
    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 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).
     */
    function _callOptionalReturn(IERC20 token, bytes memory data) private {
        // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
        // we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that
        // the target address contains contract code and also asserts for success in the low-level call.

        bytes memory returndata = address(token).functionCall(data);
        if (returndata.length != 0 && !abi.decode(returndata, (bool))) {
            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 silents catches all reverts and returns a bool instead.
     */
    function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
        // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
        // we're implementing it ourselves. We cannot use {Address-functionCall} here since this should return false
        // and not revert is the subcall reverts.

        (bool success, bytes memory returndata) = address(token).call(data);
        return success && (returndata.length == 0 || abi.decode(returndata, (bool))) && address(token).code.length > 0;
    }
}
"
    },
    "lib/openzeppelin-contracts-upgradeable/contracts/proxy/utils/Initializable.sol": {
      "content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (proxy/utils/Initializable.sol)

pragma solidity ^0.8.20;

/**
 * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed
 * behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an
 * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer
 * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.
 *
 * The initialization functions use a version number. Once a version number is used, it is consumed and cannot be
 * reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in
 * case an upgrade adds a module that needs to be initialized.
 *
 * For example:
 *
 * [.hljs-theme-light.nopadding]
 * ```solidity
 * contract MyToken is ERC20Upgradeable {
 *     function initialize() initializer public {
 *         __ERC20_init("MyToken", "MTK");
 *     }
 * }
 *
 * contract MyTokenV2 is MyToken, ERC20PermitUpgradeable {
 *     function initializeV2() reinitializer(2) public {
 *         __ERC20Permit_init("MyToken");
 *     }
 * }
 * ```
 *
 * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as
 * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.
 *
 * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure
 * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.
 *
 * [CAUTION]
 * ====
 * Avoid leaving a contract uninitialized.
 *
 * An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation
 * contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke
 * the {_disableInitializers} function in the constructor to automatically lock it when it is deployed:
 *
 * [.hljs-theme-light.nopadding]
 * ```
 * /// @custom:oz-upgrades-unsafe-allow constructor
 * constructor() {
 *     _disableInitializers();
 * }
 * ```
 * ====
 */
abstract contract Initializable {
    /**
     * @dev Storage of the initializable contract.
     *
     * It's implemented on a custom ERC-7201 namespace to reduce the risk of storage collisions
     * when using with upgradeable contracts.
     *
     * @custom:storage-location erc7201:openzeppelin.storage.Initializable
     */
    struct InitializableStorage {
        /**
         * @dev Indicates that the contract has been initialized.
         */
        uint64 _initialized;
        /**
         * @dev Indicates that the contract is in the process of being initialized.
         */
        bool _initializing;
    }

    // keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.Initializable")) - 1)) & ~bytes32(uint256(0xff))
    bytes32 private constant INITIALIZABLE_STORAGE = 0xf0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00;

    /**
     * @dev The contract is already initialized.
     */
    error InvalidInitialization();

    /**
     * @dev The contract is not initializing.
     */
    error NotInitializing();

    /**
     * @dev Triggered when the contract has been initialized or reinitialized.
     */
    event Initialized(uint64 version);

    /**
     * @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope,
     * `onlyInitializing` functions can be used to initialize parent contracts.
     *
     * Similar to `reinitializer(1)`, except that in the context of a constructor an `initializer` may be invoked any
     * number of times. This behavior in the constructor can be useful during testing and is not expected to be used in
     * production.
     *
     * Emits an {Initialized} event.
     */
    modifier initializer() {
        // solhint-disable-next-line var-name-mixedcase
        InitializableStorage storage $ = _getInitializableStorage();

        // Cache values to avoid duplicated sloads
        bool isTopLevelCall = !$._initializing;
        uint64 initialized = $._initialized;

        // Allowed calls:
        // - initialSetup: the contract is not in the initializing state and no previous version was
        //                 initialized
        // - construction: the contract is initialized at version 1 (no reininitialization) and the
        //                 current contract is just being deployed
        bool initialSetup = initialized == 0 && isTopLevelCall;
        bool construction = initialized == 1 && address(this).code.length == 0;

        if (!initialSetup && !construction) {
            revert InvalidInitialization();
        }
        $._initialized = 1;
        if (isTopLevelCall) {
            $._initializing = true;
        }
        _;
        if (isTopLevelCall) {
            $._initializing = false;
            emit Initialized(1);
        }
    }

    /**
     * @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the
     * contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be
     * used to initialize parent contracts.
     *
     * A reinitializer may be used after the original initialization step. This is essential to configure modules that
     * are added through upgrades and that require initialization.
     *
     * When `version` is 1, this modifier is similar to `initializer`, except that functions marked with `reinitializer`
     * cannot be nested. If one is invoked in the context of another, execution will revert.
     *
     * Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in
     * a contract, executing them in the right order is up to the developer or operator.
     *
     * WARNING: Setting the version to 2**64 - 1 will prevent any future reinitialization.
     *
     * Emits an {Initialized} event.
     */
    modifier reinitializer(uint64 version) {
        // solhint-disable-next-line var-name-mixedcase
        InitializableStorage storage $ = _getInitializableStorage();

        if ($._initializing || $._initialized >= version) {
            revert InvalidInitialization();
        }
        $._initialized = version;
        $._initializing = true;
        _;
        $._initializing = false;
        emit Initialized(version);
    }

    /**
     * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the
     * {initializer} and {reinitializer} modifiers, directly or indirectly.
     */
    modifier onlyInitializing() {
        _checkInitializing();
        _;
    }

    /**
     * @dev Reverts if the contract is not in an initializing state. See {onlyInitializing}.
     */
    function _checkInitializing() internal view virtual {
        if (!_isInitializing()) {
            revert NotInitializing();
        }
    }

    /**
     * @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call.
     * Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized
     * to any version. It is recommended to use this to lock implementation contracts that are designed to be called
     * through proxies.
     *
     * Emits an {Initialized} event the first time it is successfully executed.
     */
    function _disableInitializers() internal virtual {
        // solhint-disable-next-line var-name-mixedcase
        InitializableStorage storage $ = _getInitializableStorage();

        if ($._initializing) {
            revert InvalidInitialization();
        }
        if ($._initialized != type(uint64).max) {
            $._initialized = type(uint64).max;
            emit Initialized(type(uint64).max);
        }
    }

    /**
     * @dev Returns the highest version that has been initialized. See {reinitializer}.
     */
    function _getInitializedVersion() internal view returns (uint64) {
        return _getInitializableStorage()._initialized;
    }

    /**
     * @dev Returns `true` if the contract is currently initializing. See {onlyInitializing}.
     */
    function _isInitializing() internal view returns (bool) {
        return _getInitializableStorage()._initializing;
    }

    /**
     * @dev Returns a pointer to the storage namespace.
     */
    // solhint-disable-next-line var-name-mixedcase
    function _getInitializableStorage() private pure returns (InitializableStorage storage $) {
        assembly {
            $.slot := INITIALIZABLE_STORAGE
        }
    }
}
"
    },
    "lib/openzeppelin-contracts-upgradeable/contracts/utils/ReentrancyGuardUpgradeable.sol": {
      "content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/ReentrancyGuard.sol)

pragma solidity ^0.8.20;
import {Initializable} from "../proxy/utils/Initializable.sol";

/**
 * @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 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 ReentrancyGuardUpgradeable is Initializable {
    // 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;

    /// @custom:storage-location erc7201:openzeppelin.storage.ReentrancyGuard
    struct ReentrancyGuardStorage {
        uint256 _status;
    }

    // keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.ReentrancyGuard")) - 1)) & ~bytes32(uint256(0xff))
    bytes32 private constant ReentrancyGuardStorageLocation = 0x9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f00;

    function _getReentrancyGuardStorage() private pure returns (ReentrancyGuardStorage storage $) {
        assembly {
            $.slot := ReentrancyGuardStorageLocation
        }
    }

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

    function __ReentrancyGuard_init() internal onlyInitializing {
        __ReentrancyGuard_init_unchained();
    }

    function __ReentrancyGuard_init_unchained() internal onlyInitializing {
        ReentrancyGuardStorage storage $ = _getReentrancyGuardStorage();
        $._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 {
        ReentrancyGuardStorage storage $ = _getReentrancyGuardStorage();
        // 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 {
        ReentrancyGuardStorage storage $ = _getReentrancyGuardStorage();
        // 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) {
        ReentrancyGuardStorage storage $ = _getReentrancyGuardStorage();
        return $._status == ENTERED;
    }
}
"
    },
    "src/interfaces/registry/IRegistryContract.sol": {
      "content": "// SPDX-License-Identifier: Apache-2.0

pragma solidity 0.8.20;

interface IRegistryContract {
    /*//////////////////////////////////////////////////////////////
                                Events
    //////////////////////////////////////////////////////////////*/

    /// @notice This event is emitted when the address of the contract is set
    /// @param name The name of the contract in bytes32
    /// @param contractAddress The address of the contract
    event SetContract(bytes32 indexed name, address indexed contractAddress);

    /*//////////////////////////////////////////////////////////////
                                Functions
    //////////////////////////////////////////////////////////////*/

    /// @notice Set the address of the contract
    /// @param name The name of the contract in bytes32
    /// @param contractAddress The address of the contract
    function setContract(bytes32 name, address contractAddress) external;

    /// @notice Get the address of the contract
    /// @param name The name of the contract in bytes32
    /// @return contractAddress The address of the contract
    function getContract(bytes32 name) external view returns (address);
}
"
    },
    "src/interfaces/registry/IRegistryAccess.sol": {
      "content": "// SPDX-License-Identifier: Apache-2.0

pragma solidity 0.8.20;

import {IAccessControlDefaultAdminRules} from
    "openzeppelin-contracts/access/extensions/IAccessControlDefaultAdminRules.sol";

interface IRegistryAccess is IAccessControlDefaultAdminRules {
    /*//////////////////////////////////////////////////////////////
                                Functions
    //////////////////////////////////////////////////////////////*/

    /// @notice Set the admin role for a specific role
    /// @param role The role to set the admin for
    /// @param adminRole The admin role to set
    function setRoleAdmin(bytes32 role, bytes32 adminRole) external;
}
"
    },
    "src/interfaces/swapperEngine/ISwapperEngine.sol": {
      "content": "// SPDX-License-Identifier: Apache-2.0

pragma solidity 0.8.20;

interface ISwapperEngine {
    /*//////////////////////////////////////////////////////////////
                                Structs
    //////////////////////////////////////////////////////////////*/

    /// @notice Struct representing a EURC order.
    struct EurcOrder {
        /// @dev The amount of EURC tokens associated with the order.
        uint256 tokenAmount;
        /// @dev The address of the recipient.
        address recipient;
        /// @dev A boolean indicating whether the order is active or not.
        bool active;
    }

    /*//////////////////////////////////////////////////////////////
                                Events
    //////////////////////////////////////////////////////////////*/

    /// @notice Event emitted when an order is deposited.
    /// @param requester The address of the requester.
    /// @param recipient The address of the recipient.
    /// @param orderId The ID of the order.
    /// @param amount The amount of EURC tokens deposited.
    event Deposit(
        address indexed requester,
        address indexed recipient,
        uint256 indexed orderId,
        uint256 amount
    );

    /// @notice Event emitted when an order is withdrawn.
    /// @param recipient The address of the recipient.
    /// @param orderId The ID of the order.
    /// @param amount The amount of EURC tokens withdrawn.
    event Withdraw(address indexed recipient, uint256 indexed orderId, uint256 amount);

    /// @notice Event emitted when an order is matched.
    /// @param eurcProviderAddr The address of the EURC provider.
    /// @param eur0Provider The address of the EUR0 provider.
    /// @param orderId The ID of the order.
    /// @param amount The amount of EURC tokens matched.
    event OrderMatched(
        address indexed eurcProviderAddr,
        address indexed eur0Provider,
        uint256 indexed orderId,
        uint256 amount
    );

    /*//////////////////////////////////////////////////////////////
                                Functions
    //////////////////////////////////////////////////////////////*/

    /// @notice Returns the minimum EURC amount required for providing liquidity.
    /// @return minimumEURCAmount The minimum EURC amount required for providing liquidity.
    function minimumEURCAmountProvided() external view returns (uint256);

    /// @notice Retrieves the details of a specific EURC order.
    /// @dev This function returns the active status and token amount of the specified order.
    /// @param orderId The unique identifier of the order.
    /// @return active A boolean indicating whether the order is active or not.
    /// @return tokenAmount The amount of EURC tokens associated with the order.
    function getOrder(uint256 orderId) external view returns (bool active, uint256 tokenAmount);

    /// @notice Updates the minimum amount of EURC that must be provided in a deposit.
    /// @dev This function can only be called by an administrator.
    /// @param minimumEURCAmount The new minimum amount of EURC to deposit
    ///        The new minimumEURCAmount must be greater than 1 EURC (1e6)
    function updateMinimumEURCAmountProvided(uint256 minimumEURCAmount) external;

    /// @notice Pauses the contract.
    /// @dev This function can only be called by a pauser.
    function pause() external;

    /// @notice Unpauses the contract.
    /// @dev This function can only be called by an unpauser.
    function unpause() external;

    /// @notice Allows a user to deposit EURC tokens and create a new order.
    /// @dev This function transfers the specified amount of EURC tokens from the caller to the contract
    ///      and creates a new order with the deposited amount and the caller as the requester.
    /// @param amountToDeposit The amount of EURC tokens to deposit.
    function depositEURC(uint256 amountToDeposit) external;

    /// @notice Allows a user to deposit EURC tokens and create a new order on behalf of another address.
    /// @dev This function transfers the specified amount of EURC tokens from the caller to the contract
    ///      and creates a new order with the deposited amount and the caller as the requester.
    /// @param amountToDeposit The amount of EURC tokens to deposit.
    /// @param recipient The address to receive the EURC tokens.
    function depositEURCOnBehalf(uint256 amountToDeposit, address recipient) external;

    /// @notice Allows a user to deposit EURC tokens with permit and create a new order.
    /// @dev This function transfers the specified amount of EURC tokens from the caller to the contract
    ///      and creates a new order with the deposited amount and the caller as the requester.
    /// @param amountToDeposit The amount of EURC tokens to deposit.
    /// @param deadline The deadline for the permit
    /// @param v The v value for the permit
    /// @param r The r value for the permit
    /// @param s The s value for the permit
    function depositEURCWithPermit(
        uint256 amountToDeposit,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external;

    /// @notice Allows the recipient of an order to withdraw their deposited EURC tokens and cancel the order.
    /// @dev This function deactivates the specified order, sets its token amount to zero, and transfers
    ///      the deposited EURC tokens back to the recipient.
    /// @param orderToCancel The ID of the order to cancel and withdraw from.
    function withdrawEURC(uint256 orderToCancel) external;

    /// @notice Allows a user to provide EUR0 tokens and receive EURC tokens by matching against existing orders.
    /// @dev This function allows users to specify an amount of EURC tokens they want, calculating the corresponding
    ///      EUR0 tokens they need and exchanging it against active orders.
    /// @param recipient The address to receive the EURC tokens.
    /// @param amountEurcToTakeInNativeDecimals The amount of EURC tokens to take, in the token's native decimal representation.
    /// @param orderIdsToTake An array of order IDs to match against.
    /// @param partialMatchingAllowed A flag indicating whether partial matching is allowed.
    /// @return The unmatched amount of EURC tokens.
    function provideEur0ReceiveEURC(
        address recipient,
        uint256 amountEurcToTakeInNativeDecimals,
        uint256[] memory orderIdsToTake,
        bool partialMatchingAllowed
    ) external returns (uint256);

    /// @notice provideEur0ReceiveEURCWithPermit method with permit
    /// @dev This function allows users to to swap their EUR0 for EURC with permit
    /// @param recipient The address to receive the EURC tokens.
    /// @param amountEurcToTakeInNativeDecimals The amount of EURC tokens to take, in the token's native decimal representation.
    /// @param orderIdsToTake An array of order IDs to match against.
    /// @param partialMatchingAllowed A flag indicating whether partial matching is allowed.
    /// @param deadline The deadline for the permit
    /// @param eur0ToPermit The amount of EUR0 tokens to permit, must be greater than the equivalent amount of EURC tokens to take.
    /// @param v The v value for the permit
    /// @param r The r value for the permit
    /// @param s The s value for the permit
    /// @return The unmatched amount of EURC tokens.
    function provideEur0ReceiveEURCWithPermit(
        address recipient,
        uint256 amountEurcToTakeInNativeDecimals,
        uint256[] memory orderIdsToTake,
        bool partialMatchingAllowed,
        uint256 eur0ToPermit,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external returns (uint256);

    /// @notice Allows users to specify an amount of EUR0 tokens to swap and receive EURC tokens by matching against existing orders.
    /// @dev This function handles the precision differences between EUR0 and EURC taking dust into account
    ///      to ensure accurate conversion. It returns the unmatched EUR0 amount in WAD format, including the dust.
    /// @param recipient The address to receive the EURC tokens.
    /// @param amountEur0ToProvideInWad The amount of EUR0 to provide in WAD format.
    /// @param orderIdsToTake An array of order IDs to match against.
    /// @param partialMatchingAllowed A flag indicating whether partial matching is allowed.
    /// @return The unmatched amount of EUR0 tokens in WAD format.
    function swapEur0(
        address recipient,
        uint256 amountEur0ToProvideInWad,
        uint256[] memory orderIdsToTake,
        bool partialMatchingAllowed
    ) external returns (uint256);

    /// @notice Get the next order ID.
    /// @dev This function returns the next order ID, which is the total number of orders created.
    /// @return The next order ID.
    function getNextOrderId() external view returns (uint256);
}
"
    },
    "src/interfaces/token/IEur0.sol": {
      "content": "// SPDX-License-Identifier: Apache-2.0

pragma solidity 0.8.20;

import {IERC20Metadata} from "openzeppelin-contracts/token/ERC20/extensions/IERC20Metadata.sol";

interface IEur0 is IERC20Metadata {
    /*//////////////////////////////////////////////////////////////
                                Events
    //////////////////////////////////////////////////////////////*/

    /// @notice Event emitted when an address is blacklisted.
    /// @param account The address that was blacklisted.
    event Blacklist(address account);
    /// @notice Event emitted when an address is removed from blacklist.
    /// @param account The address that was removed from blacklist.
    event UnBlacklist(address account);

    /*//////////////////////////////////////////////////////////////
                                Functions
    //////////////////////////////////////////////////////////////*/

    /// @notice Pauses all token transfers.
    /// @dev Can only be called by an account with the PAUSING_CONTRACTS_ROLE
    function pause() external;

    /// @notice Unpauses all token transfers.
    /// @dev Can only be called by an account with the UNPAUSING_CONTRACTS_ROLE
    function unpause() external;

    /// @notice mint Eur0 token
    /// @dev Can only be called by EUR0_MINT role;
    /// @dev Can only mint if enough EUR backing is available.
    /// @param to address of the account who want to mint their token
    /// @param amount the amount of tokens to mint
    function mint(address to, uint256 amount) external;

    /// @notice burnFrom Eur0 token
    /// @dev Can only be called by EUR0_BURN role
    /// @param account address of 

Tags:
ERC20, Multisig, Mintable, Burnable, Pausable, Swap, Liquidity, Upgradeable, Multi-Signature, Factory, Oracle|addr:0xca6305b22f8e8886b54d12b5d9d66ea08d02d466|verified:true|block:23496102|tx:0x6543538462ef0782436f7ff4678ac38de3a87fec7cd92805920b15830ba04783|first_check:1759484010

Submitted on: 2025-10-03 11:33:32

Comments

Log in to comment.

No comments yet.