AlephVaultSettlement

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/modules/AlephVaultSettlement.sol": {
      "content": "// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.27;
/*
  ______   __                      __       
 /      \ /  |                    /  |      
/$$$$$$  |$$ |  ______    ______  $$ |____  
$$ |__$$ |$$ | /      \  /      \ $$      \ 
$$    $$ |$$ |/$$$$$$  |/$$$$$$  |$$$$$$$  |
$$$$$$$$ |$$ |$$    $$ |$$ |  $$ |$$ |  $$ |
$$ |  $$ |$$ |$$$$$$$$/ $$ |__$$ |$$ |  $$ |
$$ |  $$ |$$ |$$       |$$    $$/ $$ |  $$ |
$$/   $$/ $$/  $$$$$$$/ $$$$$$$/  $$/   $$/ 
                        $$ |                
                        $$ |                
                        $$/                 
*/

import {EnumerableSet} from "openzeppelin-contracts/contracts/utils/structs/EnumerableSet.sol";
import {IERC20} from "openzeppelin-contracts/contracts/token/ERC20/IERC20.sol";
import {SafeERC20} from "openzeppelin-contracts/contracts/token/ERC20/utils/SafeERC20.sol";
import {IAlephVault} from "@aleph-vault/interfaces/IAlephVault.sol";
import {IAlephVaultSettlement} from "@aleph-vault/interfaces/IAlephVaultSettlement.sol";
import {IFeeManager} from "@aleph-vault/interfaces/IFeeManager.sol";
import {AuthLibrary} from "@aleph-vault/libraries/AuthLibrary.sol";
import {ERC4626Math} from "@aleph-vault/libraries/ERC4626Math.sol";
import {ModulesLibrary} from "@aleph-vault/libraries/ModulesLibrary.sol";
import {SeriesAccounting} from "@aleph-vault/libraries/SeriesAccounting.sol";
import {AlephVaultBase} from "@aleph-vault/AlephVaultBase.sol";
import {AlephVaultStorageData} from "@aleph-vault/AlephVaultStorage.sol";

/**
 * @author Othentic Labs LTD.
 * @notice Terms of Service: https://aleph.finance/terms-of-service
 */
contract AlephVaultSettlement is IAlephVaultSettlement, AlephVaultBase {
    using SafeERC20 for IERC20;
    using EnumerableSet for EnumerableSet.AddressSet;
    using SeriesAccounting for IAlephVault.ShareClass;

    /*//////////////////////////////////////////////////////////////
                            CONSTRUCTOR
    //////////////////////////////////////////////////////////////*/
    /**
     * @notice Constructor for AlephVaultSettlement module
     * @param _batchDuration The duration of each batch cycle in seconds
     */
    constructor(uint48 _batchDuration) AlephVaultBase(_batchDuration) {}

    /*//////////////////////////////////////////////////////////////
                            SETTLEMENT FUNCTIONS
    //////////////////////////////////////////////////////////////*/
    /// @inheritdoc IAlephVaultSettlement
    function settleDeposit(SettlementParams calldata _settlementParams) external nonReentrant {
        _settleDeposit(_getStorage(), _settlementParams);
    }

    /// @inheritdoc IAlephVaultSettlement
    function settleRedeem(SettlementParams calldata _settlementParams) external nonReentrant {
        _settleRedeem(_getStorage(), _settlementParams);
    }

    /// @inheritdoc IAlephVaultSettlement
    function forceRedeem(address _user) external nonReentrant {
        _forceRedeem(_getStorage(), _user);
    }

    /*//////////////////////////////////////////////////////////////
                            INTERNAL FUNCTIONS
    //////////////////////////////////////////////////////////////*/
    /**
     * @dev Internal function to settle all deposits for batches up to the current batch.
     * @param _sd The storage struct.
     * @param _settlementParams The parameters for the settlement.
     */
    function _settleDeposit(AlephVaultStorageData storage _sd, SettlementParams calldata _settlementParams) internal {
        // verify all conditions are satisfied to settle deposits
        if (_settlementParams.toBatchId > _currentBatch(_sd)) {
            revert InvalidToBatchId();
        }
        IAlephVault.ShareClass storage _shareClass = _sd.shareClasses[_settlementParams.classId];
        uint48 _depositSettleId = _shareClass.depositSettleId;
        if (_settlementParams.toBatchId <= _depositSettleId) {
            revert NoDepositsToSettle();
        }
        uint32 _lastConsolidatedSeriesId = _shareClass.lastConsolidatedSeriesId;
        _validateNewTotalAssets(_shareClass.shareSeriesId, _lastConsolidatedSeriesId, _settlementParams.newTotalAssets);
        if (_sd.isSettlementAuthEnabled) {
            AuthLibrary.verifySettlementAuthSignature(
                AuthLibrary.SETTLE_DEPOSIT,
                _settlementParams.classId,
                _settlementParams.toBatchId,
                _sd.manager,
                _settlementParams.newTotalAssets,
                _sd.authSigner,
                _settlementParams.authSignature
            );
        }
        // accumalate fees if applicable
        _accumulateFees(
            _shareClass,
            _settlementParams.classId,
            _lastConsolidatedSeriesId,
            _settlementParams.toBatchId,
            _settlementParams.newTotalAssets
        );
        uint32 _settlementSeriesId = _handleSeriesAccounting(
            _shareClass, _settlementParams.classId, _lastConsolidatedSeriesId, _settlementParams.toBatchId
        );
        IAlephVault.ShareSeries storage _shareSeries = _shareClass.shareSeries[_settlementSeriesId];
        SettleDepositDetails memory _settleDepositDetails = SettleDepositDetails({
            // check if a new series needs to be created
            createSeries: _settlementSeriesId > 0,
            classId: _settlementParams.classId,
            seriesId: _settlementSeriesId,
            batchId: _depositSettleId,
            toBatchId: _settlementParams.toBatchId,
            totalAssets: _shareSeries.totalAssets,
            totalShares: _shareSeries.totalShares
        });
        uint256 _amountToSettle;
        for (
            _settleDepositDetails.batchId;
            _settleDepositDetails.batchId < _settlementParams.toBatchId;
            _settleDepositDetails.batchId++
        ) {
            // settle deposits for each unsettled batch
            (uint256 _amount, uint256 _sharesToMint) = _settleDepositForBatch(_shareClass, _settleDepositDetails);
            _amountToSettle += _amount;
            _settleDepositDetails.totalAssets += _amount;
            _settleDepositDetails.totalShares += _sharesToMint;
        }
        _shareClass.depositSettleId = _settlementParams.toBatchId;
        _shareSeries.totalAssets = _settleDepositDetails.totalAssets;
        _shareSeries.totalShares = _settleDepositDetails.totalShares;
        _sd.totalAmountToDeposit -= _amountToSettle;
        uint256 _requiredVaultBalance = _amountToSettle + _sd.totalAmountToDeposit + _sd.totalAmountToWithdraw;
        if (IERC20(_sd.underlyingToken).balanceOf(address(this)) < _requiredVaultBalance) {
            revert InsufficientAssetsToSettle(_requiredVaultBalance);
        }
        if (_amountToSettle > 0) {
            IERC20(_sd.underlyingToken).safeTransfer(_sd.custodian, _amountToSettle);
        }
        emit SettleDeposit(
            _settlementParams.classId,
            _settleDepositDetails.seriesId,
            _depositSettleId,
            _settlementParams.toBatchId,
            _amountToSettle,
            _settleDepositDetails.totalAssets,
            _settleDepositDetails.totalShares
        );
    }

    /**
     * @dev Internal function to settle deposits for a specific batch.
     * @param _shareClass The share class to settle.
     * @param _settleDepositDetails The parameters for the settlement.
     * @return The total amount settled for the batch.
     * @return The total shares minted for the batch.
     */
    function _settleDepositForBatch(
        IAlephVault.ShareClass storage _shareClass,
        SettleDepositDetails memory _settleDepositDetails
    ) internal returns (uint256, uint256) {
        IAlephVault.DepositRequests storage _depositRequests =
            _shareClass.depositRequests[_settleDepositDetails.batchId];
        uint256 _totalAmountToDeposit = _depositRequests.totalAmountToDeposit;
        // if there are no deposits to settle, return 0
        if (_totalAmountToDeposit == 0) {
            return (0, 0);
        }
        // create a new series only if there are deposits to settle (and createSeries flag is true)
        if (_settleDepositDetails.createSeries) {
            _shareClass.createNewSeries(_settleDepositDetails.classId, _settleDepositDetails.toBatchId);
            _settleDepositDetails.createSeries = false;
        }
        uint256 _totalSharesToMint;
        uint256 _len = _depositRequests.usersToDeposit.length();
        // iterate through all requests in batch (one user can only make one request per batch)
        for (uint256 _i; _i < _len; _i++) {
            DepositRequestDetails memory _depositRequestDetails;
            _depositRequestDetails.user = _depositRequests.usersToDeposit.at(_i);
            _depositRequestDetails.amount = _depositRequests.depositRequest[_depositRequestDetails.user];
            _depositRequestDetails.sharesToMint = ERC4626Math.previewDeposit(
                _depositRequestDetails.amount, _settleDepositDetails.totalShares, _settleDepositDetails.totalAssets
            );
            _totalSharesToMint += _depositRequestDetails.sharesToMint;
            _shareClass.shareSeries[_settleDepositDetails.seriesId].sharesOf[_depositRequestDetails.user] +=
                _depositRequestDetails.sharesToMint;
            // add user into settlement series if they don't already exist there
            if (!_shareClass.shareSeries[_settleDepositDetails.seriesId].users.contains(_depositRequestDetails.user)) {
                _shareClass.shareSeries[_settleDepositDetails.seriesId].users.add(_depositRequestDetails.user);
            }
            // delete user deposit request
            delete _depositRequests.depositRequest[_depositRequestDetails.user];
            emit IAlephVaultSettlement.DepositRequestSettled(
                _settleDepositDetails.classId,
                _settleDepositDetails.seriesId,
                _settleDepositDetails.batchId,
                _depositRequestDetails.user,
                _depositRequestDetails.amount,
                _depositRequestDetails.sharesToMint
            );
        }
        // delete deposit requests
        _depositRequests.usersToDeposit.clear();
        delete _shareClass.depositRequests[_settleDepositDetails.batchId];
        emit SettleDepositBatch(
            _settleDepositDetails.classId,
            _settleDepositDetails.seriesId,
            _settleDepositDetails.batchId,
            _totalAmountToDeposit,
            _totalSharesToMint
        );
        return (_totalAmountToDeposit, _totalSharesToMint);
    }

    /**
     * @dev Internal function to settle all redeems for batches up to the current batch.
     * @param _sd The storage struct.
     * @param _settlementParams The parameters for the settlement.
     */
    function _settleRedeem(AlephVaultStorageData storage _sd, SettlementParams calldata _settlementParams) internal {
        // verify all conditions are satisfied to settle redeems
        IAlephVault.ShareClass storage _shareClass = _sd.shareClasses[_settlementParams.classId];
        uint48 _currentBatchId = _currentBatch(_sd);
        uint48 _noticePeriod = _shareClass.shareClassParams.noticePeriod;
        if (_settlementParams.toBatchId > _currentBatchId || _settlementParams.toBatchId < _noticePeriod) {
            revert InvalidToBatchId();
        }
        uint48 _redeemSettleId = _shareClass.redeemSettleId;
        uint48 _settleUptoBatchId = _settlementParams.toBatchId - _noticePeriod;
        if (_settleUptoBatchId <= _redeemSettleId) {
            revert NoRedeemsToSettle();
        }
        uint32 _lastConsolidatedSeriesId = _shareClass.lastConsolidatedSeriesId;
        _validateNewTotalAssets(_shareClass.shareSeriesId, _lastConsolidatedSeriesId, _settlementParams.newTotalAssets);
        if (_sd.isSettlementAuthEnabled) {
            AuthLibrary.verifySettlementAuthSignature(
                AuthLibrary.SETTLE_REDEEM,
                _settlementParams.classId,
                _settlementParams.toBatchId,
                _sd.manager,
                _settlementParams.newTotalAssets,
                _sd.authSigner,
                _settlementParams.authSignature
            );
        }
        // accumalate fees if applicable
        _accumulateFees(
            _shareClass,
            _settlementParams.classId,
            _lastConsolidatedSeriesId,
            _settlementParams.toBatchId,
            _settlementParams.newTotalAssets
        );
        // consolidate series if required
        _handleSeriesAccounting(
            _shareClass, _settlementParams.classId, _lastConsolidatedSeriesId, _settlementParams.toBatchId
        );
        // settle redeems for each batch
        uint256 _totalAmountToRedeem;
        for (uint48 _batchId = _redeemSettleId; _batchId < _settleUptoBatchId; _batchId++) {
            _totalAmountToRedeem += _settleRedeemForBatch(_sd, _shareClass, _settlementParams.classId, _batchId);
        }
        // revert if manager didnt fund the vault before settling redeems
        uint256 _requiredVaultBalance = _totalAmountToRedeem + _sd.totalAmountToDeposit + _sd.totalAmountToWithdraw;
        if (IERC20(_sd.underlyingToken).balanceOf(address(this)) < _requiredVaultBalance) {
            revert InsufficientAssetsToSettle(_requiredVaultBalance);
        }
        _shareClass.redeemSettleId = _settleUptoBatchId;
        _sd.totalAmountToWithdraw += _totalAmountToRedeem;
        emit SettleRedeem(_settlementParams.classId, _redeemSettleId, _settlementParams.toBatchId);
    }

    /**
     * @dev Internal function to settle redeems for a specific batch.
     * @param _sd The storage struct.
     * @param _shareClass The share class storage reference.
     * @param _classId The id of the class.
     * @param _batchId The id of the batch.
     * @return _totalAmountToRedeem The total amount to redeem in this batch.
     */
    function _settleRedeemForBatch(
        AlephVaultStorageData storage _sd,
        IAlephVault.ShareClass storage _shareClass,
        uint8 _classId,
        uint48 _batchId
    ) internal returns (uint256 _totalAmountToRedeem) {
        IAlephVault.RedeemRequests storage _redeemRequests = _shareClass.redeemRequests[_batchId];
        uint256 _len = _redeemRequests.usersToRedeem.length();
        if (_len == 0) {
            return 0;
        }
        // iterate through all requests in batch (one user can only make one request per batch)
        for (uint256 _i; _i < _len; _i++) {
            address _user = _redeemRequests.usersToRedeem.at(_i);
            // calculate amount to redeem for the user
            // redeem request value is the proportional amount user requested to redeem
            // this amount can now be different from the original amount requested as the price per share
            // in this cycle may have changed since the request was made due to pnl of the vault and fees
            uint256 _amount = ERC4626Math.previewMintUnits(
                _redeemRequests.redeemRequest[_user], _assetsPerClassOf(_shareClass, _classId, _user)
            );
            _shareClass.settleRedeemForUser(_classId, _batchId, _user, _amount);
            _totalAmountToRedeem += _amount;
            _sd.redeemableAmount[_user] += _amount;
            // delete redeem request
            delete _redeemRequests.redeemRequest[_user];
            emit RedeemRequestSettled(_classId, _batchId, _user, _amount);
        }
        // delete redeem requests
        _redeemRequests.usersToRedeem.clear();
        delete _shareClass.redeemRequests[_batchId];
        emit SettleRedeemBatch(_classId, _batchId, _totalAmountToRedeem);
    }

    /**
     * @dev Internal function to force a redeem for a user.
     * @param _sd The storage struct.
     * @param _user The user to force a redeem for.
     */
    function _forceRedeem(AlephVaultStorageData storage _sd, address _user) internal {
        uint8 _shareClasses = _sd.shareClassesId;
        uint48 _currentBatchId = _currentBatch(_sd);
        uint256 _totalUserAssets;
        uint256 _newDepositsToRedeem;
        for (uint8 _classId = 1; _classId <= _shareClasses; _classId++) {
            IAlephVault.ShareClass storage _shareClass = _sd.shareClasses[_classId];
            uint48 _depositSettleId = _shareClass.depositSettleId;
            uint48 _redeemSettleId = _shareClass.redeemSettleId;
            for (
                uint48 _batchId = _depositSettleId > _redeemSettleId ? _redeemSettleId : _depositSettleId;
                _batchId <= _currentBatchId;
                _batchId++
            ) {
                if (_batchId >= _depositSettleId) {
                    IAlephVault.DepositRequests storage _depositRequest = _shareClass.depositRequests[_batchId];
                    uint256 _amount = _depositRequest.depositRequest[_user];
                    _newDepositsToRedeem += _amount;
                    _depositRequest.totalAmountToDeposit -= _amount;
                    _depositRequest.usersToDeposit.remove(_user);
                    delete _depositRequest.depositRequest[_user];
                }
                if (_batchId >= _redeemSettleId) {
                    IAlephVault.RedeemRequests storage _redeemRequest = _shareClass.redeemRequests[_batchId];
                    _redeemRequest.usersToRedeem.remove(_user);
                    delete _redeemRequest.redeemRequest[_user];
                }
            }
            uint256 _userAssets = _assetsPerClassOf(_shareClass, _classId, _user);
            _shareClass.settleRedeemForUser(_classId, _currentBatchId, _user, _userAssets);
            _totalUserAssets += _userAssets;
        }
        uint256 _totalAssetsToSettle = _totalUserAssets + _newDepositsToRedeem;
        _sd.totalAmountToWithdraw += _totalAssetsToSettle;
        _sd.totalAmountToDeposit -= _newDepositsToRedeem;
        _sd.redeemableAmount[_user] += _totalAssetsToSettle;
        uint256 _requiredVaultBalance = _sd.totalAmountToWithdraw + _sd.totalAmountToDeposit;
        if (IERC20(_sd.underlyingToken).balanceOf(address(this)) < _requiredVaultBalance) {
            revert InsufficientAssetsToSettle(_requiredVaultBalance);
        }
        emit ForceRedeem(_currentBatchId, _user, _totalAssetsToSettle);
    }

    /**
     * @dev Internal function to handle the series accounting.
     * @param _shareClass The share class.
     * @param _classId The id of the class.
     * @param _lastConsolidatedSeriesId The id of the last consolidated series.
     * @param _toBatchId The batch id in which to consolidate/create new series.
     * @return _seriesId The series id in which to settle pending deposits.
     * @dev this function is called before settling deposits/redeems to handle the series accounting.
     * it consolidates outstanding series into the lead series if required. The return param is only
     * used in settle deposits to get the series id in which to settle pending deposits.
     * for redeems, this function is called only to handle consolidation if required.
     */
    function _handleSeriesAccounting(
        IAlephVault.ShareClass storage _shareClass,
        uint8 _classId,
        uint32 _lastConsolidatedSeriesId,
        uint48 _toBatchId
    ) internal returns (uint32 _seriesId) {
        // for non-incentive classes, all settlements take place in the lead series
        if (_shareClass.shareClassParams.performanceFee > 0) {
            uint32 _shareSeriesId = _shareClass.shareSeriesId;
            // if new lead series highwatermark is not reached, deposit settlements must take place in a new series
            // if a new highwater mark is reached in this cycle, it will be updated in _accumalateFees function
            // hence, after fee accumalation process, the lead highwater mark is either greater than or equal to
            // the lead price per share
            if (
                _shareClass.shareSeries[SeriesAccounting.LEAD_SERIES_ID].highWaterMark
                    > _leadPricePerShare(_shareClass, _classId)
            ) {
                // we don't create a new series just yet because there might not be any deposit request to settle
                // in this cycle
                _seriesId = _shareSeriesId + 1;
            } else if (_shareSeriesId > _lastConsolidatedSeriesId) {
                // if new lead series highwatermark was reached in this cycle and their exists outstanding series,
                // consolidate them into lead series
                _shareClass.consolidateSeries(_classId, _shareSeriesId, _lastConsolidatedSeriesId, _toBatchId);
            }
        }
    }

    /**
     * @dev Internal function to validate the new total assets.
     * @param _shareSeriesId The id of the share series.
     * @param _lastConsolidatedSeriesId The id of the last consolidated series.
     * @param _newTotalAssets The new total assets after settlement.
     */
    function _validateNewTotalAssets(
        uint32 _shareSeriesId,
        uint32 _lastConsolidatedSeriesId,
        uint256[] calldata _newTotalAssets
    ) internal pure {
        if (_newTotalAssets.length != _shareSeriesId - _lastConsolidatedSeriesId + 1) {
            revert InvalidNewTotalAssets();
        }
    }

    /**
     * @dev Internal function to accumulate fees.
     * @param _shareClass The share class to accumulate fees for.
     * @param _classId The id of the class.
     * @param _lastConsolidatedSeriesId The id of the last consolidated series.
     * @param _toBatchId The batch id to settle deposits up to.
     * @param _newTotalAssets The new total assets after settlement.
     */
    function _accumulateFees(
        IAlephVault.ShareClass storage _shareClass,
        uint8 _classId,
        uint32 _lastConsolidatedSeriesId,
        uint48 _toBatchId,
        uint256[] calldata _newTotalAssets
    ) internal {
        uint48 _lastFeePaidId = _shareClass.lastFeePaidId;
        if (_toBatchId > _lastFeePaidId) {
            for (uint32 _i = 0; _i < _newTotalAssets.length; _i++) {
                uint32 _seriesId = _i > SeriesAccounting.LEAD_SERIES_ID
                    ? _lastConsolidatedSeriesId + _i
                    : SeriesAccounting.LEAD_SERIES_ID;
                // update the series total assets and shares
                _shareClass.shareSeries[_seriesId].totalAssets = _newTotalAssets[_i];
                _shareClass.shareSeries[_seriesId].totalShares += _accumulateFeeShares(
                    _newTotalAssets[_i],
                    _shareClass.shareSeries[_seriesId].totalShares,
                    _toBatchId,
                    _lastFeePaidId,
                    _classId,
                    _seriesId
                );
            }
            _shareClass.lastFeePaidId = _toBatchId;
        }
    }

    /**
     * @dev Internal function to get the accumulated fee shares.
     * @param _newTotalAssets The new total assets after settlement.
     * @param _totalShares The total shares in the vault.
     * @param _toBatchId The batch id to settle deposits up to.
     * @param _lastFeePaidId The last fee paid id.
     * @param _classId The id of the class.
     * @param _seriesId The id of the series.
     * @return The accumulated fee shares to mint.
     */
    function _accumulateFeeShares(
        uint256 _newTotalAssets,
        uint256 _totalShares,
        uint48 _toBatchId,
        uint48 _lastFeePaidId,
        uint8 _classId,
        uint32 _seriesId
    ) internal returns (uint256) {
        if (_newTotalAssets == 0) {
            return 0;
        }
        (bool _success, bytes memory _data) = _getStorage().moduleImplementations[ModulesLibrary.FEE_MANAGER]
            .delegatecall(
            abi.encodeCall(
                IFeeManager.accumulateFees,
                (_classId, _seriesId, _toBatchId, _lastFeePaidId, _newTotalAssets, _totalShares)
            )
        );
        if (!_success) {
            revert DelegateCallFailed(_data);
        }
        return abi.decode(_data, (uint256));
    }
}
"
    },
    "lib/openzeppelin-contracts/contracts/utils/structs/EnumerableSet.sol": {
      "content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.3.0) (utils/structs/EnumerableSet.sol)
// This file was procedurally generated from scripts/generate/templates/EnumerableSet.js.

pragma solidity ^0.8.20;

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

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

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

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

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

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

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

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

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

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

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

            return true;
        } else {
            return false;
        }
    }

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

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

    /**
     * @dev Returns the number of values on the set. O(1).
     */
    function _length(Set storage set) private view returns (uint256) {
        return set._values.length;
    }

    /**
     * @dev Returns the value stored at position `index` in the set. O(1).
     *
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function _at(Set storage set, uint256 index) private view returns (bytes32) {
        return set._values[index];
    }

    /**
     * @dev Return the entire set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function _values(Set storage set) private view returns (bytes32[] memory) {
        return set._values;
    }

    /**
     * @dev Return a slice of the set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function _values(Set storage set, uint256 start, uint256 end) private view returns (bytes32[] memory) {
        unchecked {
            end = Math.min(end, _length(set));
            start = Math.min(start, end);

            uint256 len = end - start;
            bytes32[] memory result = new bytes32[](len);
            for (uint256 i = 0; i < len; ++i) {
                result[i] = Arrays.unsafeAccess(set._values, start + i).value;
            }
            return result;
        }
    }

    // Bytes32Set

    struct Bytes32Set {
        Set _inner;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {
        return _add(set._inner, value);
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) {
        return _remove(set._inner, value);
    }

    /**
     * @dev Removes all the values from a set. O(n).
     *
     * WARNING: Developers should keep in mind that this function has an unbounded cost and using it may render the
     * function uncallable if the set grows to the point where clearing it consumes too much gas to fit in a block.
     */
    function clear(Bytes32Set storage set) internal {
        _clear(set._inner);
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {
        return _contains(set._inner, value);
    }

    /**
     * @dev Returns the number of values in the set. O(1).
     */
    function length(Bytes32Set storage set) internal view returns (uint256) {
        return _length(set._inner);
    }

    /**
     * @dev Returns the value stored at position `index` in the set. O(1).
     *
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {
        return _at(set._inner, index);
    }

    /**
     * @dev Return the entire set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function values(Bytes32Set storage set) internal view returns (bytes32[] memory) {
        bytes32[] memory store = _values(set._inner);
        bytes32[] memory result;

        assembly ("memory-safe") {
            result := store
        }

        return result;
    }

    /**
     * @dev Return a slice of the set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function values(Bytes32Set storage set, uint256 start, uint256 end) internal view returns (bytes32[] memory) {
        bytes32[] memory store = _values(set._inner, start, end);
        bytes32[] memory result;

        assembly ("memory-safe") {
            result := store
        }

        return result;
    }

    // AddressSet

    struct AddressSet {
        Set _inner;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function add(AddressSet storage set, address value) internal returns (bool) {
        return _add(set._inner, bytes32(uint256(uint160(value))));
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function remove(AddressSet storage set, address value) internal returns (bool) {
        return _remove(set._inner, bytes32(uint256(uint160(value))));
    }

    /**
     * @dev Removes all the values from a set. O(n).
     *
     * WARNING: Developers should keep in mind that this function has an unbounded cost and using it may render the
     * function uncallable if the set grows to the point where clearing it consumes too much gas to fit in a block.
     */
    function clear(AddressSet storage set) internal {
        _clear(set._inner);
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function contains(AddressSet storage set, address value) internal view returns (bool) {
        return _contains(set._inner, bytes32(uint256(uint160(value))));
    }

    /**
     * @dev Returns the number of values in the set. O(1).
     */
    function length(AddressSet storage set) internal view returns (uint256) {
        return _length(set._inner);
    }

    /**
     * @dev Returns the value stored at position `index` in the set. O(1).
     *
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function at(AddressSet storage set, uint256 index) internal view returns (address) {
        return address(uint160(uint256(_at(set._inner, index))));
    }

    /**
     * @dev Return the entire set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function values(AddressSet storage set) internal view returns (address[] memory) {
        bytes32[] memory store = _values(set._inner);
        address[] memory result;

        assembly ("memory-safe") {
            result := store
        }

        return result;
    }

    /**
     * @dev Return a slice of the set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function values(AddressSet storage set, uint256 start, uint256 end) internal view returns (address[] memory) {
        bytes32[] memory store = _values(set._inner, start, end);
        address[] memory result;

        assembly ("memory-safe") {
            result := store
        }

        return result;
    }

    // UintSet

    struct UintSet {
        Set _inner;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function add(UintSet storage set, uint256 value) internal returns (bool) {
        return _add(set._inner, bytes32(value));
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function remove(UintSet storage set, uint256 value) internal returns (bool) {
        return _remove(set._inner, bytes32(value));
    }

    /**
     * @dev Removes all the values from a set. O(n).
     *
     * WARNING: Developers should keep in mind that this function has an unbounded cost and using it may render the
     * function uncallable if the set grows to the point where clearing it consumes too much gas to fit in a block.
     */
    function clear(UintSet storage set) internal {
        _clear(set._inner);
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function contains(UintSet storage set, uint256 value) internal view returns (bool) {
        return _contains(set._inner, bytes32(value));
    }

    /**
     * @dev Returns the number of values in the set. O(1).
     */
    function length(UintSet storage set) internal view returns (uint256) {
        return _length(set._inner);
    }

    /**
     * @dev Returns the value stored at position `index` in the set. O(1).
     *
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function at(UintSet storage set, uint256 index) internal view returns (uint256) {
        return uint256(_at(set._inner, index));
    }

    /**
     * @dev Return the entire set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function values(UintSet storage set) internal view returns (uint256[] memory) {
        bytes32[] memory store = _values(set._inner);
        uint256[] memory result;

        assembly ("memory-safe") {
            result := store
        }

        return result;
    }

    /**
     * @dev Return a slice of the set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function values(UintSet storage set, uint256 start, uint256 end) internal view returns (uint256[] memory) {
        bytes32[] memory store = _values(set._inner, start, end);
        uint256[] memory result;

        assembly ("memory-safe") {
            result := store
        }

        return result;
    }

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

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

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

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

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

            if (valueIndex != lastIndex) {
                string memory lastValue = self._values[lastIndex];

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

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

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

            return true;
        } else {
            return false;
        }
    }

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

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function contains(StringSet storage self, string memory value) internal view returns (bool) {
        return self._positions[value] != 0;
    }

    /**
     * @dev Returns the number of values on the set. O(1).
     */
    function length(StringSet storage self) internal view returns (uint256) {
        return self._values.length;
    }

    /**
     * @dev Returns the value stored at position `index` in the set. O(1).
     *
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function at(StringSet storage self, uint256 index) internal view returns (string memory) {
        return self._values[index];
    }

    /**
     * @dev Return the entire set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function values(StringSet storage self) internal view returns (string[] memory) {
        return self._values;
    }

    /**
     * @dev Return a slice of the set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function values(StringSet storage set, uint256 start, uint256 end) internal view returns (string[] memory) {
        unchecked {
            end = Math.min(end, length(set));
            start = Math.min(start, end);

            uint256 len = end - start;
            string[] memory result = new string[](len);
            for (uint256 i = 0; i < len; ++i) {
                result[i] = Arrays.unsafeAccess(set._values, start + i).value;
            }
            return result;
        }
    }

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

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

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

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

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

            if (valueIndex != lastIndex) {
                bytes memory lastValue = self._values[lastIndex];

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

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

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

            return true;
        } else {
            return false;
        }
    }

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

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function contains(BytesSet storage self, bytes memory value) internal view returns (bool) {
        return self._positions[value] != 0;
    }

    /**
     * @dev Returns the number of values on the set. O(1).
     */
    function length(BytesSet storage self) internal view returns (uint256) {
        return self._values.length;
    }

    /**
     * @dev Returns the value stored at position `index` in the set. O(1).
     *
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function at(BytesSet storage self, uint256 index) internal view returns (bytes memory) {
        return self._values[index];
    }

    /**
     * @dev Return the entire set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function values(BytesSet storage self) internal view returns (bytes[] memory) {
        return self._values;
    }

    /**
     * @dev Return a slice of the set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function values(BytesSet storage set, uint256 start, uint256 end) internal view returns (bytes[] memory) {
        unchecked {
            end = Math.min(end, length(set));
            start = Math.min(start, end);

            uint256 len = end - start;
            bytes[] memory result = new bytes[](len);
            for (uint256 i = 0; i < len; ++i) {
                result[i] = Arrays.unsafeAccess(set._values, start + i).value;
            }
            return result;
        }
    }
}
"
    },
    "lib/openzeppelin-contracts/contracts/token/ERC20/IERC20.sol": {
      "content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (token/ERC20/IERC20.sol)

pragma solidity >=0.4.16;

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

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

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

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

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

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

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

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

pragma solidity ^0.8.20;

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

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

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

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

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

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

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

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

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

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

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

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

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

    /**
     * @dev Performs an {ERC1363} approveAndCall, with a fallback to the simple {ERC20} approve if the target has no
     * code. This can be used to implement an {ERC721}-like safe transfer that

Tags:
ERC20, ERC165, Multisig, Swap, Upgradeable, Multi-Signature, Factory, Oracle|addr:0xe979eaa1525eb1d261d6455985dc687336fd28c4|verified:true|block:23578259|tx:0x023a0e81e65a3c2bed883373db2e1d21cbbc227157b09884691ff5358d36cc53|first_check:1760512859

Submitted on: 2025-10-15 09:21:00

Comments

Log in to comment.

No comments yet.