DaoCollateral

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

pragma solidity 0.8.20;

import {SafeERC20} from "openzeppelin-contracts/token/ERC20/utils/SafeERC20.sol";
import {EIP712Upgradeable} from
    "openzeppelin-contracts-upgradeable/utils/cryptography/EIP712Upgradeable.sol";
import {IERC20Permit} from "openzeppelin-contracts/token/ERC20/extensions/IERC20Permit.sol";
import {IERC20} from "openzeppelin-contracts/token/ERC20/IERC20.sol";
import {SignatureChecker} from "openzeppelin-contracts/utils/cryptography/SignatureChecker.sol";

import {IERC20Metadata} from "openzeppelin-contracts/token/ERC20/extensions/IERC20Metadata.sol";
import {Math} from "openzeppelin-contracts/utils/math/Math.sol";
import {ReentrancyGuardUpgradeable} from
    "openzeppelin-contracts-upgradeable/utils/ReentrancyGuardUpgradeable.sol";
import {PausableUpgradeable} from "openzeppelin-contracts-upgradeable/utils/PausableUpgradeable.sol";
import {NoncesUpgradeable} from "src/utils/NoncesUpgradeable.sol";

import {IRegistryAccess} from "src/interfaces/registry/IRegistryAccess.sol";
import {IEur0} from "src/interfaces/token/IEur0.sol";
import {IRegistryContract} from "src/interfaces/registry/IRegistryContract.sol";
import {ITokenMapping} from "src/interfaces/tokenManager/ITokenMapping.sol";
import {IOracle} from "src/interfaces/oracles/IOracle.sol";
import {IDaoCollateral, Approval, Intent} from "src/interfaces/IDaoCollateral.sol";
import {ISwapperEngine} from "src/interfaces/swapperEngine/ISwapperEngine.sol";
import {CheckAccessControl} from "src/utils/CheckAccessControl.sol";
import {Normalize} from "src/utils/normalize.sol";
import {
    SCALAR_ONE,
    MIN_REDEEM_AMOUNT_IN_EUR0,
    DEFAULT_ADMIN_ROLE,
    MAX_REDEEM_FEE,
    BASIS_POINT_BASE,
    CONTRACT_YIELD_TREASURY,
    PAUSING_CONTRACTS_ROLE,
    DAO_REDEMPTION_ROLE,
    UNPAUSING_CONTRACTS_ROLE,
    CONTRACT_REGISTRY_ACCESS,
    CONTRACT_TREASURY,
    CONTRACT_TOKEN_MAPPING,
    CONTRACT_EUR0,
    CONTRACT_SWAPPER_ENGINE,
    CONTRACT_ORACLE,
    NONCE_THRESHOLD_SETTER_ROLE,
    INTENT_MATCHING_ROLE,
    INTENT_TYPE_HASH,
    MIN_REDEEM_AMOUNT_SET_ROLE
} from "src/constants.sol";

import {
    InvalidToken,
    AmountIsZero,
    AmountTooLow,
    AmountTooBig,
    ApprovalFailed,
    RedeemMustNotBePaused,
    RedeemMustBePaused,
    SwapMustNotBePaused,
    SwapMustBePaused,
    SameValue,
    CBRIsTooHigh,
    CBRIsNull,
    RedeemFeeTooBig,
    RedeemFeeCannotBeZero,
    NullContract,
    InvalidSigner,
    InvalidDeadline,
    ExpiredSignature,
    NoOrdersIdsProvided,
    InvalidOrderAmount
} from "src/errors.sol";

/// @title   DaoCollateral Contract
/// @notice  Manages the swapping of Euro collateral tokens for EUR0, with functionalities for swap (direct mint) and redeeming tokens
/// @dev     Provides mechanisms for token swap operations, fee management, called Dao Collateral for historical reasons
/// @author  Usual Tech team
contract DaoCollateral is
    ReentrancyGuardUpgradeable,
    PausableUpgradeable,
    NoncesUpgradeable,
    EIP712Upgradeable,
    IDaoCollateral
{
    using SafeERC20 for IERC20Metadata;
    using CheckAccessControl for IRegistryAccess;
    using Normalize for uint256;

    struct DaoCollateralStorageV0 {
        /// @notice Indicates if the redeem functionality is paused.
        bool _redeemPaused;
        /// @notice Indicates if the swap functionality is paused.
        bool _swapPaused;
        /// @notice Indicates if the Counter Bank Run (CBR) functionality is active.
        bool isCBROn;
        /// @notice The fee for redeeming tokens, in basis points.
        uint256 redeemFee;
        /// @notice The coefficient for calculating the returned collateralToken amount when CBR is active.
        uint256 cbrCoef;
        /// @notice The RegistryAccess contract instance for role checks.
        IRegistryAccess registryAccess;
        /// @notice The TokenMapping contract instance for managing token mappings.
        ITokenMapping tokenMapping;
        /// @notice The EUR0 token contract instance.
        IEur0 eur0;
        /// @notice The Oracle contract instance for price feeds.
        IOracle oracle;
        /// @notice The address of treasury holding collateral tokens.
        address treasury;
        /// @notice The SwapperEngine contract instance for managing token swaps.
        ISwapperEngine swapperEngine;
        /// @notice the threshold for intents to be considered used in EUR0 swapCollateralTokenToEurcIntent
        uint256 nonceThreshold;
        /// @notice The mapping of the amount of the order taken that matches up to current nonce for each account
        mapping(address account => uint256) _orderAmountTaken;
        /// @notice The address of treasury holding fee tokens.
        address treasuryYield;
        /// @notice The minimum amount of EUR0 that can be redeemed.
        uint256 minimumRedeemAmount;
    }

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

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

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

    /// @notice Initializes the DaoCollateral contract with registry information and initial configuration.
    /// @param _registryContract The address of the registry contract.
    /// @param _redeemFee The initial redeem fee, in basis points.
    function initialize(address _registryContract, uint256 _redeemFee) public initializer {
        // can't have a redeem fee greater than 25%
        if (_redeemFee > MAX_REDEEM_FEE) {
            revert RedeemFeeTooBig();
        }
        if (_redeemFee == 0) {
            revert RedeemFeeCannotBeZero();
        }

        if (_registryContract == address(0)) {
            revert NullContract();
        }

        __ReentrancyGuard_init_unchained();
        __Pausable_init_unchained();
        __EIP712_init_unchained("DaoCollateral", "1");
        DaoCollateralStorageV0 storage $ = _daoCollateralStorageV0();
        $.redeemFee = _redeemFee;
        emit RedeemFeeUpdated(_redeemFee);
        IRegistryContract registryContract = IRegistryContract(_registryContract);
        $.registryAccess = IRegistryAccess(registryContract.getContract(CONTRACT_REGISTRY_ACCESS));
        $.treasury = address(registryContract.getContract(CONTRACT_TREASURY));
        $.tokenMapping = ITokenMapping(registryContract.getContract(CONTRACT_TOKEN_MAPPING));
        $.eur0 = IEur0(registryContract.getContract(CONTRACT_EUR0));
        $.oracle = IOracle(registryContract.getContract(CONTRACT_ORACLE));
        $.swapperEngine = ISwapperEngine(registryContract.getContract(CONTRACT_SWAPPER_ENGINE));
        $.minimumRedeemAmount = MIN_REDEEM_AMOUNT_IN_EUR0;
        emit MinimumRedeemAmountUpdated(MIN_REDEEM_AMOUNT_IN_EUR0);
        $.treasuryYield = registryContract.getContract(CONTRACT_YIELD_TREASURY);
    }

    /// @notice Returns the storage struct of the contract.
    /// @return $ The pointer to the storage struct of the contract.
    function _daoCollateralStorageV0() internal pure returns (DaoCollateralStorageV0 storage $) {
        bytes32 position = DaoCollateralStorageV0Location;
        // solhint-disable-next-line no-inline-assembly
        assembly {
            $.slot := position
        }
    }

    /*//////////////////////////////////////////////////////////////
                               Setters
    //////////////////////////////////////////////////////////////*/

    /// @inheritdoc IDaoCollateral
    function activateCBR(uint256 coefficient) external {
        // we should revert if the coef is greater than 1
        if (coefficient > SCALAR_ONE) {
            revert CBRIsTooHigh();
        } else if (coefficient == 0) {
            revert CBRIsNull();
        }
        DaoCollateralStorageV0 storage $ = _daoCollateralStorageV0();
        $.registryAccess.onlyMatchingRole(DEFAULT_ADMIN_ROLE);
        $.isCBROn = true;
        $._swapPaused = true;
        $.cbrCoef = coefficient;
        emit CBRActivated($.cbrCoef);
        emit SwapPaused();
    }

    /// @inheritdoc IDaoCollateral
    function deactivateCBR() external {
        DaoCollateralStorageV0 storage $ = _daoCollateralStorageV0();
        $.registryAccess.onlyMatchingRole(DEFAULT_ADMIN_ROLE);
        if ($.isCBROn == false) revert SameValue();
        $.isCBROn = false;
        $.cbrCoef = 0;
        emit CBRDeactivated();
    }

    /// @inheritdoc IDaoCollateral
    function setMinimumRedeemAmount(uint256 _minimumRedeemAmount) external {
        DaoCollateralStorageV0 storage $ = _daoCollateralStorageV0();
        $.registryAccess.onlyMatchingRole(MIN_REDEEM_AMOUNT_SET_ROLE);
        if (_minimumRedeemAmount == 0) revert AmountTooLow();
        if ($.minimumRedeemAmount == _minimumRedeemAmount) revert SameValue();
        $.minimumRedeemAmount = _minimumRedeemAmount;
        emit MinimumRedeemAmountUpdated(_minimumRedeemAmount);
    }

    /// @inheritdoc IDaoCollateral
    function setRedeemFee(uint256 _redeemFee) external {
        if (_redeemFee > MAX_REDEEM_FEE) revert RedeemFeeTooBig();
        if (_redeemFee == 0) revert RedeemFeeCannotBeZero();
        DaoCollateralStorageV0 storage $ = _daoCollateralStorageV0();
        $.registryAccess.onlyMatchingRole(DEFAULT_ADMIN_ROLE);
        if ($.redeemFee == _redeemFee) revert SameValue();
        $.redeemFee = _redeemFee;
        emit RedeemFeeUpdated(_redeemFee);
    }

    /// @inheritdoc IDaoCollateral
    function setNonceThreshold(uint256 threshold) external {
        DaoCollateralStorageV0 storage $ = _daoCollateralStorageV0();
        $.registryAccess.onlyMatchingRole(NONCE_THRESHOLD_SETTER_ROLE);
        $.nonceThreshold = threshold;
        emit NonceThresholdSet(threshold);
    }

    /// @inheritdoc IDaoCollateral
    function pauseRedeem() external {
        DaoCollateralStorageV0 storage $ = _daoCollateralStorageV0();
        $.registryAccess.onlyMatchingRole(PAUSING_CONTRACTS_ROLE);
        if ($._redeemPaused) {
            revert RedeemMustNotBePaused();
        }
        $._redeemPaused = true;
        emit RedeemPaused();
    }

    /// @inheritdoc IDaoCollateral
    function unpauseRedeem() external {
        DaoCollateralStorageV0 storage $ = _daoCollateralStorageV0();
        $.registryAccess.onlyMatchingRole(UNPAUSING_CONTRACTS_ROLE);
        if (!$._redeemPaused) {
            revert RedeemMustBePaused();
        }
        $._redeemPaused = false;
        emit RedeemUnPaused();
    }

    /// @inheritdoc IDaoCollateral
    function pauseSwap() external {
        DaoCollateralStorageV0 storage $ = _daoCollateralStorageV0();
        $.registryAccess.onlyMatchingRole(PAUSING_CONTRACTS_ROLE);
        if ($._swapPaused) {
            revert SwapMustNotBePaused();
        }
        $._swapPaused = true;
        emit SwapPaused();
    }

    /// @inheritdoc IDaoCollateral
    function unpauseSwap() external {
        DaoCollateralStorageV0 storage $ = _daoCollateralStorageV0();
        $.registryAccess.onlyMatchingRole(UNPAUSING_CONTRACTS_ROLE);
        if (!$._swapPaused) {
            revert SwapMustBePaused();
        }
        $._swapPaused = false;
        emit SwapUnPaused();
    }

    /// @inheritdoc IDaoCollateral
    function pause() external {
        DaoCollateralStorageV0 storage $ = _daoCollateralStorageV0();
        $.registryAccess.onlyMatchingRole(PAUSING_CONTRACTS_ROLE);
        _pause();
    }

    /// @inheritdoc IDaoCollateral
    function unpause() external {
        DaoCollateralStorageV0 storage $ = _daoCollateralStorageV0();
        $.registryAccess.onlyMatchingRole(UNPAUSING_CONTRACTS_ROLE);
        _unpause();
    }

    /*//////////////////////////////////////////////////////////////
                               Internal
    //////////////////////////////////////////////////////////////*/

    /// @notice  _swapCheckAndGetEURQuote method will check if the token is a EUR0-supported collateral token and if the amount is not 0
    /// @dev     Function that do sanity check on the inputs
    /// @dev      and return the normalized EUR quoted price of collateral tokens for the given amount
    /// @param   collateralToken  address of the token to swap MUST be a collateral token.
    /// @param   amountInToken  amount of collateral token to swap.
    /// @return  wadQuoteInEUR The quoted amount in EUR with 18 decimals for the specified token and amount.
    function _swapCheckAndGetEURQuote(address collateralToken, uint256 amountInToken)
        internal
        view
        returns (uint256 wadQuoteInEUR)
    {
        if (amountInToken == 0) {
            revert AmountIsZero();
        }

        // Amount can't be greater than uint128
        if (amountInToken > type(uint128).max) {
            revert AmountTooBig();
        }

        DaoCollateralStorageV0 storage $ = _daoCollateralStorageV0();
        if (!$.tokenMapping.isEur0Collateral(collateralToken)) {
            revert InvalidToken();
        }
        wadQuoteInEUR = _getQuoteInEUR(amountInToken, collateralToken);
        //slither-disable-next-line incorrect-equality
        if (wadQuoteInEUR == 0) {
            revert AmountTooLow();
        }
    }

    /// @notice  transfers Collateral Token And Mint EUR0
    /// @dev     will transfer the collateral to the treasury and mints the corresponding stableAmount in EUR0
    /// @param   collateralToken  address of the token to swap MUST be a collateral token.
    /// @param   amount  amount of collateral token to swap.
    /// @param   wadAmountInEUR0 amount of EUR0 to mint.
    function _transferCollateralTokenAndMintEur0(
        address collateralToken,
        uint256 amount,
        uint256 wadAmountInEUR0
    ) internal {
        DaoCollateralStorageV0 storage $ = _daoCollateralStorageV0();
        // Should revert if balance is insufficient
        IERC20Metadata(address(collateralToken)).safeTransferFrom(msg.sender, $.treasury, amount);
        // Mint some EUR0
        $.eur0.mint(msg.sender, wadAmountInEUR0);
    }

    /// @dev call the oracle to get the price in EUR
    /// @param collateralToken the collateral token address
    /// @return wadPriceInEUR the price in EUR with 18 decimals
    /// @return decimals number of decimals of the token
    function _getPriceAndDecimals(address collateralToken)
        internal
        view
        returns (uint256 wadPriceInEUR, uint8 decimals)
    {
        DaoCollateralStorageV0 storage $ = _daoCollateralStorageV0();
        wadPriceInEUR = uint256($.oracle.getPrice(collateralToken));
        decimals = uint8(IERC20Metadata(collateralToken).decimals());
    }

    /// @notice  get the price in EUR of an `tokenAmount` of `collateralToken`
    /// @dev call the oracle to get the price in EUR of `tokenAmount` of token with 18 decimals
    /// @param tokenAmount the amount of token to convert in EUR with 18 decimals
    /// @param collateralToken the collateral token address
    /// @return wadAmountInEUR the amount in EUR with 18 decimals
    function _getQuoteInEUR(uint256 tokenAmount, address collateralToken)
        internal
        view
        returns (uint256 wadAmountInEUR)
    {
        (uint256 wadPriceInEUR, uint8 decimals) = _getPriceAndDecimals(collateralToken);
        uint256 wadAmount = tokenAmount.tokenAmountToWad(decimals);
        wadAmountInEUR = Math.mulDiv(wadAmount, wadPriceInEUR, SCALAR_ONE, Math.Rounding.Floor);
    }

    /// @notice  get the amount of token for an amount of EUR
    /// @dev call the oracle to get the price in EUR of `amount` of token with 18 decimals
    /// @param wadStableAmount the amount of EUR with 18 decimals
    /// @param collateralToken the collateral token address
    /// @return amountInToken the amount in token corresponding to the amount of EUR
    function _getQuoteInTokenFromEUR(uint256 wadStableAmount, address collateralToken)
        internal
        view
        returns (uint256 amountInToken)
    {
        (uint256 wadPriceInEUR, uint8 decimals) = _getPriceAndDecimals(collateralToken);
        // will result in an amount with the same 'decimals' as the token
        amountInToken = wadStableAmount.wadTokenAmountForPrice(wadPriceInEUR, decimals);
    }

    /// @notice Calculates the returned amount of collateralToken give an amount of EUR
    /// @dev return the amountInToken of token for `wadStableAmount` of EUR at the current price
    /// @param wadStableAmount the amount of EUR
    /// @param collateralToken the collateral token address
    /// @return amountInToken the amount of token that is worth `wadStableAmount` of EUR with 18 decimals
    function _getTokenAmountForAmountInEUR(uint256 wadStableAmount, address collateralToken)
        internal
        view
        returns (uint256 amountInToken)
    {
        DaoCollateralStorageV0 storage $ = _daoCollateralStorageV0();
        amountInToken = _getQuoteInTokenFromEUR(wadStableAmount, collateralToken);
        // if cbr is on we need to apply the coef to the collateral price
        // cbrCoef should be less than 1e18
        if ($.isCBROn) {
            amountInToken = Math.mulDiv(amountInToken, $.cbrCoef, SCALAR_ONE, Math.Rounding.Floor);
        }
    }

    /// @notice  _calculateFee method will calculate the EUR0 redeem fee
    /// @dev     Function that transfer the fee to the treasury
    /// @dev     The fee is calculated as a percentage of the amount of EUR0 to redeem
    /// @dev     The fee is minted to avoid transfer and allowance as the whole EUR0 amount is burnt afterwards
    /// @param   eur0Amount  Amount of EUR0 to transfer to treasury.
    /// @param   collateralToken  address of the collateral token to swap should be a collateral token.
    /// @return stableFee The amount of EUR0 minted as fees for the treasury.
    function _calculateFee(uint256 eur0Amount, address collateralToken)
        internal
        view
        returns (uint256 stableFee)
    {
        DaoCollateralStorageV0 storage $ = _daoCollateralStorageV0();
        stableFee = Math.mulDiv(eur0Amount, $.redeemFee, BASIS_POINT_BASE, Math.Rounding.Floor);
        uint8 tokenDecimals = IERC20Metadata(collateralToken).decimals();
        // if the token has less decimals than EUR0 we need to normalize the fee
        if (tokenDecimals < 18) {
            // we scale down the fee to the token decimals
            // and we scale it up to 18 decimals
            stableFee = Normalize.tokenAmountToWad(
                Normalize.wadAmountToDecimals(stableFee, tokenDecimals), tokenDecimals
            );
        }
    }

    /// @notice  _burnEur0TokenAndTransferCollateral method will burn the EUR0 token and transfer the collateral token
    /// @param   collateralToken  address of the token to swap should be a collateral token.
    /// @param   eur0Amount  amount of EUR0 to swap.
    /// @param   stableFee  amount of fee in EUR0.
    /// @return returnedCollateral The amount of collateral token returned.
    function _burnEur0TokenAndTransferCollateral(
        address collateralToken,
        uint256 eur0Amount,
        uint256 stableFee
    ) internal returns (uint256 returnedCollateral) {
        DaoCollateralStorageV0 storage $ = _daoCollateralStorageV0();
        // we burn the remaining EUR0 token
        uint256 burnedEur0 = eur0Amount - stableFee;
        // we burn all the EUR0 token
        $.eur0.burnFrom(msg.sender, eur0Amount);

        // get the amount of collateral token for the amount of EUR0 burned by calling the oracle
        returnedCollateral = _getTokenAmountForAmountInEUR(burnedEur0, collateralToken);
        if (returnedCollateral == 0) {
            revert AmountTooLow();
        }

        // we distribute the collateral token from the treasury to the user
        // slither-disable-next-line arbitrary-send-erc20
        IERC20Metadata(collateralToken).safeTransferFrom($.treasury, msg.sender, returnedCollateral);

        // If the CBR is on, the fees are forfeited from the yield treasury to favor the collateralization ratio
        if (stableFee > 0 && !$.isCBROn) {
            $.eur0.mint($.treasuryYield, stableFee);
        }
    }

    /// @notice Partially or fully consumes an amount of an intent
    /// @dev Function should be called internally after verifying the intent is valid
    /// @param amount The amount to consume from the remaining reusable intent
    /// @param intent Intent data and signature of data
    function _useIntentAmount(uint256 amount, Intent memory intent) internal virtual {
        DaoCollateralStorageV0 storage $ = _daoCollateralStorageV0();
        // check that the amount they want to use is less than the remaining amount unmatched in the smart order
        uint256 remainingAmountUnmatched =
            (intent.amountInTokenDecimals - $._orderAmountTaken[intent.recipient]);

        if (amount > remainingAmountUnmatched) {
            revert InvalidOrderAmount(intent.recipient, amount);
        } else if ((remainingAmountUnmatched - amount) <= $.nonceThreshold) {
            emit IntentConsumed(
                intent.recipient,
                nonces(intent.recipient),
                intent.rwaToken,
                intent.amountInTokenDecimals
            );
            _useNonce(intent.recipient);
            //reset the intent amount taken for the next nonce
            $._orderAmountTaken[intent.recipient] = 0;
        } else {
            $._orderAmountTaken[intent.recipient] += amount;
        }
    }

    /// @notice Checks if an Intent is valid by verifying its signature and returns the remaining amount unmatched
    /// @dev Function should be called internally before any fields from intent are used in contract logic
    /// @param intent Intent data and signature of data
    /// @return remainingAmountUnmatched Amount left in the current intent for swap
    /// @return nonce The current nonce for this reusable intent
    function _isValidIntent(Intent calldata intent)
        internal
        virtual
        returns (uint256 remainingAmountUnmatched, uint256 nonce)
    {
        DaoCollateralStorageV0 storage $ = _daoCollateralStorageV0();
        //if we increment the nonce this wont match so the user can cancel the order essentially
        uint256 currentOrderNonce = nonces(intent.recipient);
        // check the signature w/ nonce
        bytes32 structHash = keccak256(
            abi.encode(
                INTENT_TYPE_HASH,
                intent.recipient,
                intent.rwaToken,
                intent.amountInTokenDecimals,
                currentOrderNonce,
                intent.deadline
            )
        );

        bytes32 hash = _hashTypedDataV4(structHash);

        if (!SignatureChecker.isValidSignatureNow(intent.recipient, hash, intent.signature)) {
            revert InvalidSigner(intent.recipient);
        }

        if ($._orderAmountTaken[intent.recipient] > intent.amountInTokenDecimals) {
            revert InvalidOrderAmount(intent.recipient, intent.amountInTokenDecimals);
        }

        // return the amount left to be filled
        remainingAmountUnmatched =
            (intent.amountInTokenDecimals - $._orderAmountTaken[intent.recipient]);
        return (remainingAmountUnmatched, currentOrderNonce);
    }

    /// @notice Swap Collateral Token for EURC through offers on the SwapperContract
    /// @dev Takes Collateral Token, mints EUR0 and provides it to the Swapper Contract directly
    /// Sends EUR0 to the offer's creator and sends EURC to the recipient
    /// @dev the recipient Address to receive the EURC is msg.sender
    /// @param caller Address of the caller (msg.sender or intent recipient)
    /// @param collateralToken Address of the Collateral Token to swap for EURC
    /// @param amountInTokenDecimals Amount of the Collateral Token to swap for EURC
    /// @param partialMatching flag to allow partial matching
    /// @param orderIdsToTake orderIds to be taken
    /// @param approval ERC20Permit approval data and signature of data
    /// @return matchedAmountInTokenDecimals The amount of collateral tokens which have been matched.
    /// @return matchedAmountInEUR           The net amount of EUR0 tokens minted.
    // solhint-disable-next-line code-complexity
    function _swapCollateralTokenToEurc(
        address caller,
        address collateralToken,
        uint256 amountInTokenDecimals,
        bool partialMatching,
        uint256[] calldata orderIdsToTake,
        Approval calldata approval
    ) internal returns (uint256 matchedAmountInTokenDecimals, uint256 matchedAmountInEUR) {
        if (amountInTokenDecimals == 0) {
            revert AmountIsZero();
        }
        if (amountInTokenDecimals > type(uint128).max) {
            revert AmountTooBig();
        }
        if (orderIdsToTake.length == 0) {
            revert NoOrdersIdsProvided();
        }
        DaoCollateralStorageV0 storage $ = _daoCollateralStorageV0();
        if (!$.tokenMapping.isEur0Collateral(collateralToken)) {
            revert InvalidToken();
        }

        // Check if the approval isn't null, if it isn't, use it for the permit
        if (approval.deadline != 0 && approval.v != 0 && approval.r != 0 && approval.s != 0) {
            // Authorization transfer
            try IERC20Permit(collateralToken).permit( //NOTE: this will fail if permit already used but that's ok as long as there is enough allowance
                caller,
                address(this),
                type(uint256).max,
                approval.deadline,
                approval.v,
                approval.r,
                approval.s
            ) {} catch {} // solhint-disable-line no-empty-blocks
        }

        // Take the collateral token from the recipient
        IERC20Metadata(collateralToken).safeTransferFrom(caller, $.treasury, amountInTokenDecimals);
        // Get the price quote of the collateral token to mint EUR0
        uint256 wadCollateralQuoteInEUR = _getQuoteInEUR(amountInTokenDecimals, collateralToken);
        // Mint the corresponding amount of EUR0 stablecoin
        $.eur0.mint(address(this), wadCollateralQuoteInEUR);
        if (!IERC20($.eur0).approve(address($.swapperEngine), wadCollateralQuoteInEUR)) {
            revert ApprovalFailed();
        }
        // Provide the EUR0 to the SwapperEngine and receive EURC for the caller
        uint256 wadCollateralNotTakenInEUR = $.swapperEngine.swapEur0(
            caller, wadCollateralQuoteInEUR, orderIdsToTake, partialMatching
        );

        // Burn any unmatched EUR0 and return the collateral token
        if (wadCollateralNotTakenInEUR > 0) {
            if (!IERC20($.eur0).approve(address($.swapperEngine), 0)) {
                revert ApprovalFailed();
            }
            $.eur0.burnFrom(address(this), wadCollateralNotTakenInEUR);

            // Get amount of collateral token for the wadCollateralNotTakenInEUR pricing
            uint256 collateralTokensToReturn =
                _getQuoteInTokenFromEUR(wadCollateralNotTakenInEUR, collateralToken);

            // Transfer back the remaining collateral tokens to the recipient
            // slither-disable-next-line arbitrary-send-erc20-permit
            IERC20Metadata(collateralToken).safeTransferFrom(
                $.treasury, caller, collateralTokensToReturn
            );

            matchedAmountInTokenDecimals = amountInTokenDecimals - collateralTokensToReturn;
        } else {
            matchedAmountInTokenDecimals = amountInTokenDecimals;
        }

        matchedAmountInEUR = wadCollateralQuoteInEUR - wadCollateralNotTakenInEUR;
        emit Swap(caller, collateralToken, matchedAmountInTokenDecimals, matchedAmountInEUR);

        return (matchedAmountInTokenDecimals, matchedAmountInEUR);
    }

    /*//////////////////////////////////////////////////////////////
                               External
    //////////////////////////////////////////////////////////////*/

    /// @inheritdoc IDaoCollateral
    function swap(address collateralToken, uint256 amount, uint256 minAmountOut)
        public
        nonReentrant
        whenNotPaused
    {
        DaoCollateralStorageV0 storage $ = _daoCollateralStorageV0();
        if ($._swapPaused) {
            revert SwapMustNotBePaused();
        }
        uint256 wadQuoteInEUR = _swapCheckAndGetEURQuote(collateralToken, amount);
        // Check if the amount is greater than the minAmountOut
        if (wadQuoteInEUR < minAmountOut) {
            revert AmountTooLow();
        }

        _transferCollateralTokenAndMintEur0(collateralToken, amount, wadQuoteInEUR);
        emit Swap(msg.sender, collateralToken, amount, wadQuoteInEUR);
    }

    /// @inheritdoc IDaoCollateral
    function swapWithPermit(
        address collateralToken,
        uint256 amount,
        uint256 minAmountOut,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external {
        // solhint-disable-next-line no-empty-blocks
        try IERC20Permit(collateralToken).permit(
            msg.sender, address(this), amount, deadline, v, r, s
        ) {} catch {} // solhint-disable-line no-empty-blocks
        swap(collateralToken, amount, minAmountOut);
    }

    /// @inheritdoc IDaoCollateral
    function redeem(address collateralToken, uint256 amount, uint256 minAmountOut)
        external
        nonReentrant
        whenNotPaused
    {
        DaoCollateralStorageV0 storage $ = _daoCollateralStorageV0();
        // Amount can't be less than the minimum redeem amount
        if (amount < $.minimumRedeemAmount) {
            revert AmountTooLow();
        }

        if ($._redeemPaused) {
            revert RedeemMustNotBePaused();
        }

        // check that collateralToken is a collateral token
        if (!$.tokenMapping.isEur0Collateral(collateralToken)) {
            revert InvalidToken();
        }
        uint256 stableFee = _calculateFee(amount, collateralToken);
        if (stableFee == 0) revert RedeemFeeCannotBeZero();

        uint256 returnedCollateral =
            _burnEur0TokenAndTransferCollateral(collateralToken, amount, stableFee);
        // Check if the amount is greater than the minAmountOut
        if (returnedCollateral < minAmountOut) {
            revert AmountTooLow();
        }
        emit Redeem(msg.sender, collateralToken, amount, returnedCollateral, stableFee);
    }

    /// @inheritdoc IDaoCollateral
    function redeemDao(address collateralToken, uint256 amount)
        external
        nonReentrant
        whenNotPaused
    {
        // Amount can't be 0
        if (amount == 0) {
            revert AmountIsZero();
        }

        DaoCollateralStorageV0 storage $ = _daoCollateralStorageV0();
        $.registryAccess.onlyMatchingRole(DAO_REDEMPTION_ROLE);
        // check that collateralToken is a collateral token
        if (!$.tokenMapping.isEur0Collateral(collateralToken)) {
            revert InvalidToken();
        }
        uint256 returnedCollateral = _burnEur0TokenAndTransferCollateral(collateralToken, amount, 0);
        emit Redeem(msg.sender, collateralToken, amount, returnedCollateral, 0);
    }

    /// @inheritdoc IDaoCollateral
    function swapCollateralTokenToEurc(
        address collateralToken,
        uint256 amountInTokenDecimals,
        bool partialMatching,
        uint256[] calldata orderIdsToTake,
        Approval calldata approval
    ) external nonReentrant whenNotPaused {
        DaoCollateralStorageV0 storage $ = _daoCollateralStorageV0();
        if ($._swapPaused) {
            revert SwapMustNotBePaused();
        }
        _swapCollateralTokenToEurc(
            msg.sender,
            collateralToken,
            amountInTokenDecimals,
            partialMatching,
            orderIdsToTake,
            approval
        );
    }

    /// @inheritdoc IDaoCollateral
    function invalidateNonce() external {
        uint256 nonceUsed = _useNonce(msg.sender);
        _daoCollateralStorageV0()._orderAmountTaken[msg.sender] = 0;
        emit NonceInvalidated(msg.sender, nonceUsed);
    }

    /// @inheritdoc IDaoCollateral
    function invalidateUpToNonce(uint256 newNonce) external {
        _invalidateUpToNonce(msg.sender, newNonce);
        _daoCollateralStorageV0()._orderAmountTaken[msg.sender] = 0;
        emit NonceInvalidated(msg.sender, newNonce - 1);
    }

    /// @inheritdoc IDaoCollateral
    function swapCollateralTokenToEurcIntent(
        uint256[] calldata orderIdsToTake,
        Approval calldata approval,
        Intent calldata intent,
        bool partialMatching
    ) external nonReentrant whenNotPaused {
        if (block.timestamp > intent.deadline) {
            revert ExpiredSignature(intent.deadline);
        }
        if (approval.deadline != intent.deadline) {
            revert InvalidDeadline(approval.deadline, intent.deadline);
        }

        DaoCollateralStorageV0 storage $ = _daoCollateralStorageV0();
        $.registryAccess.onlyMatchingRole(INTENT_MATCHING_ROLE);
        if ($._swapPaused) {
            revert SwapMustNotBePaused();
        }

        (uint256 remainingAmountUnmatched, uint256 nonce) = _isValidIntent(intent);
        //NOTE: if its a full match then we increment the nonce
        if (!partialMatching) {
            emit IntentConsumed(
                intent.recipient, nonce, intent.rwaToken, intent.amountInTokenDecimals
            );
            _useNonce(intent.recipient);
            $._orderAmountTaken[intent.recipient] = 0;
        }

        (uint256 matchedAmountInTokenDecimals, uint256 matchedAmountInEUR) =
        _swapCollateralTokenToEurc(
            intent.recipient,
            intent.rwaToken,
            remainingAmountUnmatched,
            partialMatching,
            orderIdsToTake,
            approval
        );
        //NOTE: if it is a partial match then we deduct the matched amount from the remaining unmatched, if the intent is used up then increment the nonce
        if (partialMatching) {
            _useIntentAmount(matchedAmountInTokenDecimals, intent);
        }

        emit IntentMatched(
            intent.recipient,
            nonce,
            intent.rwaToken,
            matchedAmountInTokenDecimals,
            matchedAmountInEUR
        );
    }

    /*//////////////////////////////////////////////////////////////
                                Getters
    //////////////////////////////////////////////////////////////*/

    // solhint-disable-next-line func-name-mixedcase
    function DOMAIN_SEPARATOR() external view virtual returns (bytes32) {
        return _domainSeparatorV4();
    }

    /// @inheritdoc IDaoCollateral
    function orderAmountTakenCurrentNonce(address owner) public view virtual returns (uint256) {
        DaoCollateralStorageV0 storage $ = _daoCollateralStorageV0();
        return $._orderAmountTaken[owner];
    }

    /// @inheritdoc IDaoCollateral
    function nonceThreshold() external view returns (uint256) {
        DaoCollateralStorageV0 storage $ = _daoCollateralStorageV0();
        return $.nonceThreshold;
    }

    /// @inheritdoc IDaoCollateral
    function isCBROn() external view returns (bool) {
        DaoCollateralStorageV0 storage $ = _daoCollateralStorageV0();
        return $.isCBROn;
    }

    /// @inheritdoc IDaoCollateral
    function cbrCoef() public view returns (uint256) {
        DaoCollateralStorageV0 storage $ = _daoCollateralStorageV0();
        return $.cbrCoef;
    }

    /// @inheritdoc IDaoCollateral
    function minimumRedeemAmount() public view returns (uint256) {
        DaoCollateralStorageV0 storage $ = _daoCollateralStorageV0();
        return $.minimumRedeemAmount;
    }

    /// @inheritdoc IDaoCollateral
    function redeemFee() public view returns (uint256) {
        DaoCollateralStorageV0 storage $ = _daoCollateralStorageV0();
        return $.redeemFee;
    }

    /// @inheritdoc IDaoCollateral
    function isRedeemPaused() public view returns (bool) {
        DaoCollateralStorageV0 storage $ = _daoCollateralStorageV0();
        return $._redeemPaused;
    }

    /// @inheritdoc IDaoCollateral
    function isSwapPaused() public view returns (bool) {
        DaoCollateralStorageV0 storage $ = _daoCollateralStorageV0();
        return $._swapPaused;
    }
}
"
    },
    "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/utils/cryptography/EIP712Upgradeable.sol": {
      "content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/cryptography/EIP712.sol)

pragma solidity ^0.8.20;

import {MessageHashUtils} from "@openzeppelin/contracts/utils/cryptography/MessageHashUtils.sol";
import {IERC5267} from "@openzeppelin/contracts/interfaces/IERC5267.sol";
import {Initializable} from "../../proxy/utils/Initializable.sol";

/**
 * @dev https://eips.ethereum.org/EIPS/eip-712[EIP 712] is a standard for hashing and signing of typed structured data.
 *
 * The encoding scheme specified in the EIP requires a domain separator and a hash of the typed structured data, whose
 * encoding is very generic and therefore its implementation in Solidity is not feasible, thus this contract
 * does not implement the encoding itself. Protocols need to implement the type-specific encoding they need in order to
 * produce the hash of their typed data using a combination of `abi.encode` and `keccak256`.
 *
 * This contract implements the EIP 712 domain separator ({_domainSeparatorV4}) that is used as part of the encoding
 * scheme, and the final step of the encoding to obtain the message digest that is then signed via ECDSA
 * ({_hashTypedDataV4}).
 *
 * The implementation of the domain separator was designed to be as efficient as possible while still properly updating
 * the chain id to protect against replay attacks on an eventual fork of the chain.
 *
 * NOTE: This contract implements the version of the encoding known as "v4", as implemented by the JSON RPC method
 * https://docs.metamask.io/guide/signing-data.html[`eth_signTypedDataV4` in MetaMask].
 *
 * NOTE: In the upgradeable version of this contract, the cached values will correspond to the address, and the domain
 * separator of the implementation contract. This will cause the {_domainSeparatorV4} function to always rebuild the
 * separator from the immutable values, which is cheaper than accessing a cached version in cold storage.
 */
abstract contract EIP712Upgradeable is Initializable, IERC5267 {
    bytes32 private constant TYPE_HASH =
        keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)");

    /// @custom:storage-location erc7201:openzeppelin.storage.EIP712
    struct EIP712Storage {
        /// @custom:oz-renamed-from _HASHED_NAME
        bytes32 _hashedName;
        /// @custom:oz-renamed-from _HASHED_VERSION
        bytes32 _hashedVersion;

        string _name;
        string _version;
    }

    // keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.EIP712")) - 1)) & ~bytes32(uint256(0xff))
    bytes32 private constant EIP712StorageLocation = 0xa16a46d94261c7517cc8ff89f61c0ce93598e3c849801011dee649a6a557d100;

    function _getEIP712Storage() private pure returns (EIP712Storage storage $) {
        assembly {
            $.slot := EIP712StorageLocation
        }
    }

    /**
     * @dev Initializes the domain separator and parameter caches.
     *
     * The meaning of `name` and `version` is specified in
     * https://eips.ethereum.org/EIPS/eip-712#definition-of-domainseparator[EIP 712]:
     *
     * - `name`: the user readable name of the signing domain, i.e. the name of the DApp or the protocol.
     * - `version`: the current major version of the signing domain.
     *
     * NOTE: These parameters cannot be changed except through a xref:learn::upgrading-smart-contracts.adoc[smart
     * contract upgrade].
     */
    function __EIP712_init(string memory name, string memory version) internal onlyInitializing {
        __EIP712_init_unchained(name, version);
    }

    function __EIP712_init_unchained(string memory name, string memory version) internal onlyInitializing {
        EIP712Storage storage $ = _getEIP712Storage();
        $._name = name;
        $._version = version;

        // Reset prior values in storage if upgrading
        $._hashedName = 0;
        $._hashedVersion = 0;
    }

    /**
     * @dev Returns the domain separator for the current chain.
     */
    function _domainSeparatorV4() internal view returns (bytes32) {
        return _buildDomainSeparator();
    }

    function _buildDomainSeparator() private view returns (bytes32) {
        return keccak256(abi.encode(TYPE_HASH, _EIP712NameHash(), _EIP712VersionHash(), block.chainid, address(this)));
    }

    /**
     * @dev Given an already https://eips.ethereum.org/EIPS/eip-712#definition-of-hashstruct[hashed struct], this
     * function returns the hash of the fully encoded EIP712 message for this domain.
     *
     * This hash can be used together with {ECDSA-recover} to obtain the signer of a message. For example:
     *
     * ```solidity
     * bytes32 digest = _hashTypedDataV4(keccak256(abi.encode(
     *     keccak256("Mail(address to,string contents)"),
     *     mailTo,
     *     keccak256(bytes(mailContents))
     * )));
     * address signer = ECDSA.recover(digest, signature);
     * ```
     */
    function _hashTypedDataV4(bytes32 structHash) internal view virtual returns (bytes32) {
        return MessageHashUtils.toTypedDataHash(_domainSeparatorV4(), structHash);
    }

    /**
     * @dev See {IERC-5267}.
     */
    function eip712Domain()
        public
        view
        virtual
        returns (
            bytes1 fields,
            string memory name,
            string memory version,
            uint256 chainId,
            address verifyingContract,
            bytes32 salt,
            uint256[] memory extensions
        )
    {
        EIP712Storage storage $ = _getEIP712Storage();
        // If the hashed name and version in storage are non-zero, the contract hasn't been properly initialized
        // and the EIP712 domain is not reliable, as it will be missing name and version.
        require($._hashedName == 0 && $._hashedVersion == 0, "EIP712: Uninitialized");

        return (
            hex"0f", // 01111
            _EIP712Name(),
            _EIP712Version(),
            block.chainid,
            address(this),
            bytes32(0),
            new uint256[](0)
        );
    }

    /**
     * @dev The name parameter for the EIP712 domain.
     *
     * NOTE: This function reads from storage by default, but can be redefined to return a constant value if gas costs
     * are a concern.
     */
    function _EIP712Name() internal view virtual returns (string memory) {
        EIP712Storage storage $ = _getEIP712Storage();
        return $._name;
    }

    /**
     * @dev The version parameter for the EIP712 domain.
     *
     * NOTE: This function reads from storage by default, but can be redefined to return a constant value if gas costs
     * are a concern.
     */
    function _EIP712Version() internal view virtual returns (string memory) {
        EIP712Storage storage $ = _getEIP712Storage();
        return $._version;
    }

    /**
     * @dev The hash of the name parameter for the EIP712 domain.
     *
     * NOTE: In previous versions this function was virtual. In this version you should override `_EIP712Name` instead.
     */
    function _EIP712NameHash() internal view returns (bytes32) {
        EIP712Storage storage $ = _getEIP712Storage();
        string memory name = _EIP712Name();
        if (bytes(name).length > 0) {
            return keccak256(bytes(name));
        } else {
            // If the name is empty, the contract may have been upgraded without initializing the new storage.
            // We return the name hash in storage if non-zero, otherwise we assume the name is empty by design.
            bytes32 hashedName = $._hashedName;
            if (hashedName != 0) {
                return hashedName;
            } else {
                return keccak256("");
            }
        }
    }

    /**
     * @dev The hash of the version parameter for the EIP712 domain.
     *
     * NOTE: In previous versions this function was virtual. In this version you should override `_EIP712Version` instead.
     */
    function _EIP712VersionHash() internal view returns (bytes32) {
        EIP712Storage storage $ = _getEIP712Storage();
        string memory version = _EIP712Version();
        if (bytes(version).length > 0) {
            return keccak256(bytes(version));
        } else {
            // If the version is empty, the contract may have been upgraded without initializing the new storage.
            // We return the version hash in storage if non-zero, otherwise we assume the version is empty by design.
            bytes32 hashedVersion = $._hashedVersion;
            if (hashedVersion != 0) {
                return hashedVersion;
            } else {
                return keccak256("");
            }
        }
    }
}
"
    },
    "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/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/utils/cryptography/SignatureChecker.sol": {
      "content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/cryptography/SignatureChecker.sol)

pragma solidity ^0.8.20;

import {ECDSA} from "./ECDSA.sol";
import {IERC1271} from "../../interfaces/IERC1271.sol";

/**
 * @dev Signature verification helper that can be used instead of `ECDSA.recover` to seamlessly support both ECDSA
 * signatures from externally owned accounts (EOAs) as well as ERC1271 signatures from smart contract wallets like
 * Argent and Safe Wallet (previously Gnosis Safe).
 */
library SignatureChecker {
    /**
     * @dev Checks if a signature is valid for a given signer and data hash. If the signer is a smart contract, the
     * signature is validated against that smart contract using ERC1271, otherwise it's validated using `ECDSA.recover`.
     *
     * NOTE: Unlike ECDSA signatures, contract signatures are revocable, and the outcome of this function can thus
     * change through time. It could return true at block N and false at block N+1 (or the opposite).
     */
    function isValidSignatureNow(address signer, bytes32 hash, bytes memory signature) internal view returns (bool) {
        (address recovered, ECDSA.RecoverError error, ) = ECDSA.tryRecover(hash, signature);
        return
            (error == ECDSA.RecoverError.NoError && recovered == signer) ||
            isValidERC1271SignatureNow(signer, hash, signature);
    }

    /**
     * @dev Checks if a signature is valid for a given signer and data hash. The signature is validated
     * against the signer smart contract using ERC1271.
     *
     * NOTE: Unlike ECDSA signatures, contract signatures are revocable, and the outcome of this function can thus
     * change through time. It could return true at block N and false at block N+1 (or the opposite).
     */
    function isValidERC1271SignatureNow(
        address signer,
        bytes32 hash,
        bytes memory signature
    ) internal view returns (bool) {
        (bool success, bytes memory result) = signer.staticcall(
            abi.encodeCall(IERC1271.isValidSignature, (hash, signature))
        );
        return (success &&
            result.length >= 32 &&
            abi.decode(result, (bytes32)) == bytes32(IERC1271.isValidSignature.selector));
    }
}
"
    },
    "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/utils/math/Math.sol": {
      "content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/math/Math.sol)

pragma solidity ^0.8.20;

/**
 * @dev Standard math utilities missing in the Solidity language.
 */
library Math {
    /**
     * @dev Muldiv operation overflow.
     */
    error MathOverflowedMulDiv();

    enum Rounding {
        Floor, // Toward negative infinity
        Ceil, // Toward positive infinity
        Trunc, // Toward zero
        Expand // Away from zero
    }

    /**
     * @dev Returns the addition of two unsigned integers, with an overflow flag.
     */
    function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            uint256 c = a + b;
            if (c < a) return (false, 0);
            return (true, c);
        }
    }

    /**
     * @dev Returns the subtraction of two unsigned integers, with an overflow flag.
     */
    function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            if (b > a) return (false, 0);
            return (true, a - b);
        }
    }

    /**
     * @dev Returns the multiplication of two unsigned integers, with an overflow flag.
     */
    function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
            // benefit is lost if 'b' is also tested.
            // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
            if (a == 0) return (true, 0);
            uint256 c = a * b;
            if (c / a != b) return (false, 0);
            return (true, c);
        }
    }

    /**
     * @dev Returns the division of two unsigned integers, with a division by zero flag.
     */
    function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            if (b == 0) return (false, 0);
            return (true, a / b);
        }
    }

    /**
     * @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag.
     */
    function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            if (b == 0) return (false, 0);
            return (true, a % b);
        }
    }

    /**
     * @dev Returns the largest of two numbers.
     */
    function max(uint256 a, uint256 b) internal pure retur

Tags:
ERC20, Multisig, Mintable, Burnable, Pausable, Swap, Liquidity, Yield, Upgradeable, Multi-Signature, Factory, Oracle|addr:0x2a8115fa4c48b476f7f4902a309365bc3b7a9cd9|verified:true|block:23496109|tx:0xc1f6c833c094de3db0921b4a7fdbd3660daf9eabae638cc22a1c97136e53b41d|first_check:1759484014

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

Comments

Log in to comment.

No comments yet.