EverclearSpokeV6

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/contracts/intent/EverclearSpokeV6.sol": {
      "content": "// SPDX-License-Identifier: MIT
pragma solidity 0.8.25;

/*

Coded for Everclear with ♥ by

░██╗░░░░░░░██╗░█████╗░███╗░░██╗██████╗░███████╗██████╗░██╗░░░░░░█████╗░███╗░░██╗██████╗░
░██║░░██╗░░██║██╔══██╗████╗░██║██╔══██╗██╔════╝██╔══██╗██║░░░░░██╔══██╗████╗░██║██╔══██╗
░╚██╗████╗██╔╝██║░░██║██╔██╗██║██║░░██║█████╗░░██████╔╝██║░░░░░███████║██╔██╗██║██║░░██║
░░████╔═████║░██║░░██║██║╚████║██║░░██║██╔══╝░░██╔══██╗██║░░░░░██╔══██║██║╚████║██║░░██║
░░╚██╔╝░╚██╔╝░╚█████╔╝██║░╚███║██████╔╝███████╗██║░░██║███████╗██║░░██║██║░╚███║██████╔╝
░░░╚═╝░░░╚═╝░░░╚════╝░╚═╝░░╚══╝╚═════╝░╚══════╝╚═╝░░╚═╝╚══════╝╚═╝░░╚═╝╚═╝░░╚══╝╚═════╝░

https://defi.sucks

*/

import {OwnableUpgradeable} from '@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol';
import {UUPSUpgradeable} from '@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol';
import {NoncesUpgradeable} from '@openzeppelin/contracts-upgradeable/utils/NoncesUpgradeable.sol';

import {ERC20} from '@openzeppelin/contracts/token/ERC20/ERC20.sol';
import {IERC20} from '@openzeppelin/contracts/token/ERC20/IERC20.sol';
import {SafeERC20} from '@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol';

import {ECDSA} from '@openzeppelin/contracts/utils/cryptography/ECDSA.sol';
import {MessageHashUtils} from '@openzeppelin/contracts/utils/cryptography/MessageHashUtils.sol';

import {AssetUtils} from 'contracts/common/AssetUtils.sol';
import {Constants as Common} from 'contracts/common/Constants.sol';
import {MessageLibV2} from 'contracts/common/MessageLibV2.sol';
import {QueueLibV2} from 'contracts/common/QueueLibV2.sol';
import {TypeCasts} from 'contracts/common/TypeCasts.sol';

import {Constants} from 'contracts/intent/lib/Constants.sol';

import {IEverclearV2} from 'interfaces/common/IEverclearV2.sol';

import {IMessageReceiver} from 'interfaces/common/IMessageReceiver.sol';
import {IPermit2} from 'interfaces/common/IPermit2.sol';
import {ISettlementModule} from 'interfaces/common/ISettlementModule.sol';
import {IEverclearSpokeV6} from 'interfaces/intent/IEverclearSpokeV6.sol';
import {ISpokeGateway} from 'interfaces/intent/ISpokeGateway.sol';

import {SpokeStorageV6} from 'contracts/intent/SpokeStorageV6.sol';

/**
 * @title EverclearSpoke
 * @notice Spoke contract for Everclear
 */
contract EverclearSpokeV6 is
  SpokeStorageV6,
  UUPSUpgradeable,
  OwnableUpgradeable,
  NoncesUpgradeable,
  IEverclearSpokeV6,
  IMessageReceiver
{
  using QueueLibV2 for QueueLibV2.IntentQueue;
  using QueueLibV2 for QueueLibV2.FillQueue;
  using SafeERC20 for IERC20;
  using TypeCasts for address;
  using TypeCasts for bytes32;

  constructor() {
    _disableInitializers();
  }

  /*///////////////////////////////////////////////////////////////
                       EXTERNAL FUNCTIONS
  //////////////////////////////////////////////////////////////*/

  /// @inheritdoc IEverclearSpokeV6
  function pause() external hasPauseAccess {
    paused = true;
    emit Paused();
  }

  /// @inheritdoc IEverclearSpokeV6
  function unpause() external hasPauseAccess {
    paused = false;
    emit Unpaused();
  }

  /// @inheritdoc IEverclearSpokeV6
  function setStrategyForAsset(
    address _asset,
    IEverclearV2.Strategy _strategy
  ) external onlyOwner {
    strategies[_asset] = _strategy;
    emit StrategySetForAsset(_asset, _strategy);
  }

  /// @inheritdoc IEverclearSpokeV6
  function setModuleForStrategy(
    IEverclearV2.Strategy _strategy,
    ISettlementModule _module
  ) external onlyOwner {
    modules[_strategy] = _module;
    emit ModuleSetForStrategy(_strategy, _module);
  }

  /// @inheritdoc IEverclearSpokeV6
  function updateSecurityModule(
    address _newSecurityModule
  ) external onlyOwner {
    gateway.updateSecurityModule(_newSecurityModule);
  }

  /// @inheritdoc IEverclearSpokeV6
  function updateFeeAdapter(
    address _feeAdapter
  ) external onlyOwner {
    feeAdapter = _feeAdapter;
    emit FeeAdapterUpdated(_feeAdapter);
  }

  /// @inheritdoc IMessageReceiver
  function receiveMessage(
    bytes calldata
  ) external {
    _delegate(messageReceiver);
  }

  /// @inheritdoc IEverclearSpokeV6
  function newIntent(
    uint32[] memory _destinations,
    bytes32 _receiver,
    address _inputAsset,
    bytes32 _outputAsset,
    uint256 _amount,
    uint256 _amountOutMin,
    uint48 _ttl,
    bytes calldata _data
  ) external whenNotPaused onlyFeeAdapter returns (bytes32 _intentId, Intent memory _intent) {
    if (_destinations.length > 10) revert EverclearSpoke_NewIntent_InvalidIntent();
    (_intentId, _intent) = _newIntent({
      _destinations: _destinations,
      _receiver: _receiver,
      _inputAsset: _inputAsset,
      _outputAsset: _outputAsset,
      _amount: _amount,
      _amountOutMin: _amountOutMin,
      _ttl: _ttl,
      _data: _data,
      _usesPermit2: false
    });
  }

  /// @inheritdoc IEverclearSpokeV6
  function newIntent(
    uint32[] memory _destinations,
    address _receiver,
    address _inputAsset,
    address _outputAsset,
    uint256 _amount,
    uint256 _amountOutMin,
    uint48 _ttl,
    bytes calldata _data
  ) external whenNotPaused onlyFeeAdapter returns (bytes32 _intentId, Intent memory _intent) {
    if (_destinations.length > 10) revert EverclearSpoke_NewIntent_InvalidIntent();
    (_intentId, _intent) = _newIntent({
      _destinations: _destinations,
      _receiver: _receiver.toBytes32(),
      _inputAsset: _inputAsset,
      _outputAsset: _outputAsset.toBytes32(),
      _amount: _amount,
      _amountOutMin: _amountOutMin,
      _ttl: _ttl,
      _data: _data,
      _usesPermit2: false
    });
  }

  /// @inheritdoc IEverclearSpokeV6
  function batchFillIntent(
    Intent[] calldata _intents,
    uint256[] calldata _amountOut,
    bytes32[] calldata _receivers,
    uint32[][] calldata _destinations,
    bytes calldata _signature,
    bool _pullFunds
  ) external whenNotPaused returns (FillMessage[] memory _fillMessages) {
    if (
      _intents.length != _amountOut.length || _intents.length != _receivers.length
        || _intents.length != _destinations.length
    ) {
      revert EverclearSpoke_FillIntent_InvalidArrayLengths();
    }

    bytes memory _data = abi.encode(
      BATCH_FILL_INTENT_TYPEHASH,
      keccak256(abi.encode(block.chainid, address(this))),
      msg.sender,
      _intents,
      _amountOut,
      _receivers,
      _destinations
    );
    _verifySignature(fillSigner, _data, _signature);

    _fillMessages = new FillMessage[](_intents.length);
    for (uint256 i; i < _intents.length; i++) {
      _fillMessages[i] =
        _fillIntent(_intents[i], msg.sender, _receivers[i], _amountOut[i], _destinations[i], _pullFunds);
    }
  }

  /// @inheritdoc IEverclearSpokeV6
  function fillIntent(
    Intent calldata _intent,
    uint256 _amountOut,
    bytes32 _receiver,
    uint32[] memory _destinations,
    bytes calldata _signature,
    bool _pullFunds
  ) external whenNotPaused returns (FillMessage memory _fillMessage) {
    bytes32 _domain = keccak256(abi.encode(block.chainid, address(this)));
    bytes memory _data =
      abi.encode(FILL_INTENT_TYPEHASH, _domain, msg.sender, _intent, _amountOut, _receiver, _destinations);
    _verifySignature(fillSigner, _data, _signature);

    _fillMessage = _fillIntent(_intent, msg.sender, _receiver, _amountOut, _destinations, _pullFunds);
  }

  /// @inheritdoc IEverclearSpokeV6
  function processIntentQueue(
    Intent[] calldata _intents,
    uint256 _dynamicGasLimit
  ) external payable whenNotPaused {
    _checkDynamicGasLimit(_dynamicGasLimit);
    (bytes memory _batchIntentmessage, uint256 _firstIdx) = _processIntentQueue(_intents);

    (bytes32 _messageId, uint256 _feeSpent) =
      gateway.sendMessage{value: msg.value}(EVERCLEAR, _batchIntentmessage, _dynamicGasLimit);

    emit IntentQueueProcessed(_messageId, _firstIdx, _firstIdx + _intents.length, _feeSpent);
  }

  /// @inheritdoc IEverclearSpokeV6
  function processFillQueue(
    uint32 _amount,
    uint256 _dynamicGasLimit
  ) external payable whenNotPaused {
    _checkDynamicGasLimit(_dynamicGasLimit);
    (bytes memory _batchFillMessage, uint256 _firstIdx) = _processFillQueue(_amount);

    (bytes32 _messageId, uint256 _feeSpent) =
      gateway.sendMessage{value: msg.value}(EVERCLEAR, _batchFillMessage, _dynamicGasLimit);

    emit FillQueueProcessed(_messageId, _firstIdx, _firstIdx + _amount, _feeSpent);
  }

  /// @inheritdoc IEverclearSpokeV6
  function processIntentQueueViaRelayer(
    uint32 _domain,
    Intent[] calldata _intents,
    address _relayer,
    uint256 _ttl,
    uint256 _nonce,
    uint256 _dynamicGasLimit,
    bytes calldata _signature
  ) external whenNotPaused {
    _checkDynamicGasLimit(_dynamicGasLimit);

    uint32 _amount = uint32(_intents.length);
    bytes memory _data =
      abi.encode(PROCESS_INTENT_QUEUE_VIA_RELAYER_TYPEHASH, _domain, _amount, _relayer, _ttl, _nonce, _dynamicGasLimit);
    _verifySignature(lighthouse, _data, _nonce, _signature);
    _processQueueChecks(_domain, _relayer, _ttl);

    (bytes memory _batchIntentmessage, uint256 _firstIdx) = _processIntentQueue(_intents);

    uint256 _fee = gateway.quoteMessage(EVERCLEAR, _batchIntentmessage, _dynamicGasLimit);

    (bytes32 _messageId, uint256 _feeSpent) =
      gateway.sendMessage(EVERCLEAR, _batchIntentmessage, _fee, _dynamicGasLimit);

    emit IntentQueueProcessed(_messageId, _firstIdx, _firstIdx + _amount, _feeSpent);
  }

  /// @inheritdoc IEverclearSpokeV6
  function processFillQueueViaRelayer(
    uint32 _domain,
    uint32 _amount,
    address _relayer,
    uint256 _ttl,
    uint256 _nonce,
    uint256 _dynamicGasLimit,
    bytes calldata _signature
  ) external whenNotPaused {
    _checkDynamicGasLimit(_dynamicGasLimit);

    bytes memory _data =
      abi.encode(PROCESS_FILL_QUEUE_VIA_RELAYER_TYPEHASH, _domain, _amount, _relayer, _ttl, _nonce, _dynamicGasLimit);
    _verifySignature(lighthouse, _data, _nonce, _signature);
    _processQueueChecks(_domain, _relayer, _ttl);

    (bytes memory _batchFillMessage, uint256 _firstIdx) = _processFillQueue(_amount);

    uint256 _fee = gateway.quoteMessage(EVERCLEAR, _batchFillMessage, _dynamicGasLimit);

    (bytes32 _messageId, uint256 _feeSpent) = gateway.sendMessage(EVERCLEAR, _batchFillMessage, _fee, _dynamicGasLimit);

    emit FillQueueProcessed(_messageId, _firstIdx, _firstIdx + _amount, _feeSpent);
  }

  /// @inheritdoc IEverclearSpokeV6
  function deposit(
    address _asset,
    uint256 _amount
  ) external whenNotPaused {
    _pullTokens(msg.sender, _asset, _amount);
    balances[_asset.toBytes32()][msg.sender.toBytes32()] += _amount;

    emit Deposited(msg.sender, _asset, _amount);
  }

  /// @inheritdoc IEverclearSpokeV6
  function withdraw(
    address _asset,
    uint256 _amount
  ) external whenNotPaused {
    balances[_asset.toBytes32()][msg.sender.toBytes32()] -= _amount;

    _pushTokens(msg.sender, _asset, _amount);
    emit Withdrawn(msg.sender, _asset, _amount);
  }

  /// @inheritdoc IEverclearSpokeV6
  function updateGateway(
    address _newGateway
  ) external onlyOwner {
    address _oldGateway = address(gateway);
    gateway = ISpokeGateway(_newGateway);

    emit GatewayUpdated(_oldGateway, _newGateway);
  }

  /// @inheritdoc IEverclearSpokeV6
  function updateMessageReceiver(
    address _newMessageReceiver
  ) external onlyOwner {
    address _oldMessageReceiver = messageReceiver;
    messageReceiver = _newMessageReceiver;
    emit MessageReceiverUpdated(_oldMessageReceiver, _newMessageReceiver);
  }

  /// @inheritdoc IEverclearSpokeV6
  function updateMessageGasLimit(
    uint256 _newGasLimit
  ) external onlyOwner {
    uint256 _oldGasLimit = messageGasLimit;
    messageGasLimit = _newGasLimit;
    emit MessageGasLimitUpdated(_oldGasLimit, _newGasLimit);
  }
  /// @inheritdoc IEverclearSpokeV6

  function updateFillSigner(
    address _feeSigner
  ) external onlyOwner {
    address _oldFillSigner = fillSigner;
    fillSigner = _feeSigner;
    emit FillSignerUpdated(_oldFillSigner, _feeSigner);
  }

  /// @inheritdoc IEverclearSpokeV6
  function executeIntentCalldata(
    Intent calldata _intent
  ) external whenNotPaused validDestination(_intent) {
    bytes32 _intentId = keccak256(abi.encode(_intent));

    if (status[_intentId] != IntentStatus.SETTLED) {
      revert EverclearSpoke_ExecuteIntentCalldata_InvalidStatus(_intentId);
    }

    // internal method will revert if it fails
    _executeCalldata(_intentId, _intent.data);

    status[_intentId] = IntentStatus.SETTLED_AND_MANUALLY_EXECUTED;
  }

  /*///////////////////////////////////////////////////////////////
                           INITIALIZER
  //////////////////////////////////////////////////////////////*/

  /// @inheritdoc IEverclearSpokeV6
  function initialize(
    address _feeAdapter,
    address _messageReceiver,
    address _fillSigner
  ) public reinitializer(4) {
    if (!_isEmpty(deprecated_intentQueue.first, deprecated_intentQueue.last)) {
      revert EverclearSpoke_Initialize_IntentQueueNotEmpty();
    }
    if (!_isEmpty(deprecated_fillQueue.first, deprecated_fillQueue.last)) {
      revert EverclearSpoke_Initialize_FillQueueNotEmpty();
    }

    // Intialize the queues for new storage vars
    intentQueue.first = 1;
    fillQueue.first = 1;

    // Updating to feeAdapterV2 and messageReceiverV2
    feeAdapter = _feeAdapter;
    messageReceiver = _messageReceiver;
    fillSigner = _fillSigner;
  }

  /*///////////////////////////////////////////////////////////////
                       INTERNAL FUNCTIONS
  //////////////////////////////////////////////////////////////*/

  /**
   * @notice Creates a new intent
   * @param _destinations The destination chains of the intent
   * @param _receiver The destination address of the intent
   * @param _inputAsset The asset address on origin
   * @param _outputAsset The asset address on destination
   * @param _amount The amount of the asset
   * @param _amountOutMin The minimum amount out expected from the solver
   * @param _ttl The time to live of the intent
   * @param _data The data of the intent
   * @param _usesPermit2 If the intent uses permit2
   * @return _intentId The ID of the intent
   * @return _intent The intent structure
   */
  function _newIntent(
    uint32[] memory _destinations,
    bytes32 _receiver,
    address _inputAsset,
    bytes32 _outputAsset,
    uint256 _amount,
    uint256 _amountOutMin,
    uint48 _ttl,
    bytes calldata _data,
    bool _usesPermit2
  ) internal returns (bytes32 _intentId, Intent memory _intent) {
    if (_destinations.length == 1) {
      // output asset should not be null if the intent has a single destination and ttl != 0
      if (_ttl != 0 && _outputAsset == 0) revert EverclearSpoke_NewIntent_OutputAssetNull();
    } else {
      // output asset should be null if the intent has multiple destinations
      // ttl should be 0 if the intent has multiple destinations
      if (_ttl != 0 || _outputAsset != 0) revert EverclearSpoke_NewIntent_OutputAssetNotNull();
    }

    if (_data.length > Common.MAX_CALLDATA_SIZE) {
      revert EverclearSpoke_NewIntent_CalldataExceedsLimit();
    }

    uint256 _normalizedAmount =
      AssetUtils.normalizeDecimals(ERC20(_inputAsset).decimals(), Common.DEFAULT_NORMALIZED_DECIMALS, _amount);

    // check normalized amount before pulling tokens
    if (_normalizedAmount == 0) {
      revert EverclearSpoke_NewIntent_ZeroAmount();
    }

    if (!_usesPermit2) {
      Strategy _strategy = strategies[_inputAsset];
      if (_strategy == Strategy.DEFAULT) {
        _pullTokens(msg.sender, _inputAsset, _amount);
      } else {
        ISettlementModule _module = modules[_strategy];
        _module.handleBurnStrategy(_inputAsset, msg.sender, _amount, '');
      }
    }

    _intent = Intent({
      initiator: msg.sender.toBytes32(),
      receiver: _receiver,
      inputAsset: _inputAsset.toBytes32(),
      outputAsset: _outputAsset,
      origin: DOMAIN,
      nonce: ++nonce,
      timestamp: uint48(block.timestamp),
      ttl: _ttl,
      amount: _normalizedAmount,
      amountOutMin: _amountOutMin,
      destinations: _destinations,
      data: _data
    });

    _intentId = keccak256(abi.encode(_intent));

    intentQueue.enqueueIntent(_intentId);

    status[_intentId] = IntentStatus.ADDED;

    emit IntentAdded(_intentId, intentQueue.last, _intent);
  }

  /**
   * @notice Fills an intent
   * @param _intent The intent structure
   * @param _solver The solver address
   * @param _amountOut The amount out being sent
   * @param _pull Should pull from wallet or use deposited balance
   * @return _fillMessage The fill message
   */
  function _fillIntent(
    Intent calldata _intent,
    address _solver,
    bytes32 _receiver,
    uint256 _amountOut,
    uint32[] memory _destinations,
    bool _pull
  ) internal validDestination(_intent) returns (FillMessage memory _fillMessage) {
    bytes32 _intentId = keccak256(abi.encode(_intent));
    if (block.timestamp >= _intent.timestamp + _intent.ttl) {
      revert EverclearSpoke_FillIntent_IntentExpired(_intentId);
    }

    if (_amountOut < _intent.amountOutMin) {
      revert EverclearSpoke_FillIntent_AmountOutInvalid(_amountOut, _intent.amountOutMin);
    }

    if (_destinations.length == 0 || _destinations.length > 10) {
      revert EverclearSpoke_FillIntent_InvalidDestinationArray();
    }

    if (status[_intentId] != IntentStatus.NONE && status[_intentId] != IntentStatus.ADDED) {
      revert EverclearSpoke_FillIntent_InvalidStatus(_intentId);
    }

    if (!_pull) {
      if (balances[_intent.outputAsset][_solver.toBytes32()] < _amountOut) {
        revert EverclearSpoke_FillIntent_InsufficientFunds(
          _amountOut, balances[_intent.outputAsset][_solver.toBytes32()]
        );
      }

      balances[_intent.outputAsset][_solver.toBytes32()] -= _amountOut;
    }

    status[_intentId] = IntentStatus.FILLED;

    if (_intent.receiver != 0 && _intent.outputAsset != 0 && _amountOut != 0) {
      _pull
        ? IERC20(_intent.outputAsset.toAddress()).safeTransferFrom(_solver, _intent.receiver.toAddress(), _amountOut)
        : _pushTokens(_intent.receiver.toAddress(), _intent.outputAsset.toAddress(), _amountOut);
    }

    if (keccak256(_intent.data) != Constants.EMPTY_HASH) {
      _executeCalldata(_intentId, _intent.data);
    }

    _fillMessage = FillMessage({
      intentId: _intentId,
      receiver: _receiver,
      intentInputAsset: _intent.inputAsset,
      intentOrigin: _intent.origin,
      amountOut: _amountOut,
      destinations: _destinations,
      executionTimestamp: uint48(block.timestamp)
    });

    fillQueue.enqueueFill(_fillMessage);

    emit IntentFilled(_intentId, _solver, _receiver, _amountOut, fillQueue.last, _intent);
  }

  /**
   * @notice Verifies a signature
   * @param _signer The signer of the message
   * @param _data The data of the message
   * @param _nonce The nonce of the message
   * @param _signature The signature of the message
   */
  function _verifySignature(
    address _signer,
    bytes memory _data,
    uint256 _nonce,
    bytes calldata _signature
  ) internal {
    bytes32 _hash = keccak256(_data);
    address _recoveredSigner = ECDSA.recover(MessageHashUtils.toEthSignedMessageHash(_hash), _signature);
    if (_recoveredSigner != _signer) {
      revert EverclearSpoke_InvalidSignature();
    }

    _useCheckedNonce(_recoveredSigner, _nonce);
  }

  /**
   * @notice Verifies a signature
   * @param _signer The signer of the message
   * @param _data The data of the message
   * @param _signature The signature of the message
   */
  function _verifySignature(
    address _signer,
    bytes memory _data,
    bytes calldata _signature
  ) internal {
    bytes32 _hash = keccak256(_data);
    address _recoveredSigner = ECDSA.recover(MessageHashUtils.toEthSignedMessageHash(_hash), _signature);
    if (_recoveredSigner != _signer) {
      revert EverclearSpoke_InvalidFillSignature();
    }
  }

  /**
   * @notice Process the intent queue messages to send a batching message to the transport layer
   * @param _intents The intents to process, the order of the intents must match the order in the queue
   * @return _batchIntentmessage The batched intent message
   * @return _firstIdx The first index of the intents processed
   */
  function _processIntentQueue(
    Intent[] calldata _intents
  )
    internal
    validQueueAmount(intentQueue.first, intentQueue.last, _intents.length)
    returns (bytes memory _batchIntentmessage, uint256 _firstIdx)
  {
    _firstIdx = intentQueue.first;

    for (uint32 _i; _i < _intents.length; _i++) {
      bytes32 _queueIntentId = intentQueue.dequeueIntent();
      bytes32 _intentHash = keccak256(abi.encode(_intents[_i]));
      // verify the intent and its position in the queue
      if (_queueIntentId != _intentHash) {
        revert EverclearSpoke_ProcessIntentQueue_NotFound(_intentHash, _i);
      }
    }

    _batchIntentmessage = MessageLibV2.formatIntentMessageBatch(_intents);
  }

  /**
   * @notice Process the fill queue messages to send a batching message to the transport layer
   * @param _amount The amount of messages to process
   * @return _batchFillMessage The batched fill message
   * @return _firstIdx The first index of the fills processed
   */
  function _processFillQueue(
    uint32 _amount
  )
    internal
    validQueueAmount(fillQueue.first, fillQueue.last, _amount)
    returns (bytes memory _batchFillMessage, uint256 _firstIdx)
  {
    _firstIdx = fillQueue.first;

    FillMessage[] memory _fillMessages = new FillMessage[](_amount);
    for (uint32 _i; _i < _amount; _i++) {
      _fillMessages[_i] = fillQueue.dequeueFill();
    }

    _batchFillMessage = MessageLibV2.formatFillMessageBatch(_fillMessages);
  }

  /**
   * @notice Executes the calldata of an intent
   * @param _intentId The intent ID
   * @param _data The calldata of the intent
   */
  function _executeCalldata(
    bytes32 _intentId,
    bytes memory _data
  ) internal {
    (address _target, bytes memory _calldata) = abi.decode(_data, (address, bytes));

    (bool _success, bytes memory _returnData) = callExecutor.excessivelySafeCall(
      _target, gasleft() - Constants.EXECUTE_CALLDATA_RESERVE_GAS, 0, Constants.DEFAULT_COPY_BYTES, _calldata
    );

    if (_success) {
      emit ExternalCalldataExecuted(_intentId, _returnData);
    } else {
      revert EverclearSpoke_ExecuteIntentCalldata_ExternalCallFailed();
    }
  }

  /**
   * @notice Pull tokens from the sender to the spoke contract
   * @param _sender The address of the sender
   * @param _asset The address of the asset
   * @param _amount The amount of the asset
   */
  function _pullTokens(
    address _sender,
    address _asset,
    uint256 _amount
  ) internal {
    IERC20(_asset).safeTransferFrom(_sender, address(this), _amount);
  }

  /**
   * @notice Push tokens from the spoke contract to the recipient
   * @param _recipient The address of the recipient
   * @param _asset The address of the asset
   * @param _amount The amount of the asset
   */
  function _pushTokens(
    address _recipient,
    address _asset,
    uint256 _amount
  ) internal {
    IERC20(_asset).safeTransfer(_recipient, _amount);
  }

  /**
   * @notice Perform a `delegatcall`
   * @param _delegatee The address of the delegatee
   */
  function _delegate(
    address _delegatee
  ) internal {
    assembly {
      // Copy msg.data. We take full control of memory in this inline assembly
      // block because it will not return to Solidity code. We overwrite the
      // Solidity scratch pad at memory position 0.
      calldatacopy(0, 0, calldatasize())

      // Call the implementation.
      // out and outsize are 0 because we don't know the size yet.
      let result := delegatecall(gas(), _delegatee, 0, calldatasize(), 0, 0)

      // Copy the returned data.
      returndatacopy(0, 0, returndatasize())

      switch result
      // delegatecall returns 0 on error.
      case 0 { revert(0, returndatasize()) }
      default { return(0, returndatasize()) }
    }
  }

  /**
   * @notice Checks that the upgrade function is called by the owner
   */
  function _authorizeUpgrade(
    address
  ) internal override onlyOwner {}

  /**
   * @notice Process queue checks (applied when a relayer tries to process a queue)
   * @param _domain The domain of the queue
   * @param _relayer The relayer address
   * @param _ttl The time to live of the message
   */
  function _processQueueChecks(
    uint32 _domain,
    address _relayer,
    uint256 _ttl
  ) internal view {
    if (_domain != DOMAIN) {
      revert EverclearSpoke_ProcessFillViaRelayer_WrongDomain();
    }

    if (_relayer != msg.sender) {
      revert EverclearSpoke_ProcessFillViaRelayer_NotRelayer();
    }

    if (block.timestamp > _ttl) {
      revert EverclearSpoke_ProcessFillViaRelayer_TTLExpired();
    }
  }

  function _isEmpty(
    uint256 first,
    uint256 last
  ) internal pure returns (bool) {
    return (last < first) || (first == 0 && last == 0);
  }

  /**
   * @notice Checks the dynamic gas limit does not exceed the maxGasLimit (messageGasLimit) when processing queues
   * @param _dynamicGasLimit The dynamic gas limit to check
   */
  function _checkDynamicGasLimit(
    uint256 _dynamicGasLimit
  ) internal view {
    if (_dynamicGasLimit > messageGasLimit) {
      revert EverclearSpoke_ProcessQueue_ExceedsGasLimit(_dynamicGasLimit);
    }
  }
}
"
    },
    "../../node_modules/@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol": {
      "content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable.sol)

pragma solidity ^0.8.20;

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

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

    // keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.Ownable")) - 1)) & ~bytes32(uint256(0xff))
    bytes32 private constant OwnableStorageLocation = 0x9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c199300;

    function _getOwnableStorage() private pure returns (OwnableStorage storage $) {
        assembly {
            $.slot := OwnableStorageLocation
        }
    }

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

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

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

    /**
     * @dev Initializes the contract setting the address provided by the deployer as the initial owner.
     */
    function __Ownable_init(address initialOwner) internal onlyInitializing {
        __Ownable_init_unchained(initialOwner);
    }

    function __Ownable_init_unchained(address initialOwner) internal onlyInitializing {
        if (initialOwner == address(0)) {
            revert OwnableInvalidOwner(address(0));
        }
        _transferOwnership(initialOwner);
    }

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

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

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

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

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

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Internal function without access restriction.
     */
    function _transferOwnership(address newOwner) internal virtual {
        OwnableStorage storage $ = _getOwnableStorage();
        address oldOwner = $._owner;
        $._owner = newOwner;
        emit OwnershipTransferred(oldOwner, newOwner);
    }
}
"
    },
    "../../node_modules/@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol": {
      "content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (proxy/utils/UUPSUpgradeable.sol)

pragma solidity ^0.8.20;

import {IERC1822Proxiable} from "@openzeppelin/contracts/interfaces/draft-IERC1822.sol";
import {ERC1967Utils} from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Utils.sol";
import {Initializable} from "./Initializable.sol";

/**
 * @dev An upgradeability mechanism designed for UUPS proxies. The functions included here can perform an upgrade of an
 * {ERC1967Proxy}, when this contract is set as the implementation behind such a proxy.
 *
 * A security mechanism ensures that an upgrade does not turn off upgradeability accidentally, although this risk is
 * reinstated if the upgrade retains upgradeability but removes the security mechanism, e.g. by replacing
 * `UUPSUpgradeable` with a custom implementation of upgrades.
 *
 * The {_authorizeUpgrade} function must be overridden to include access restriction to the upgrade mechanism.
 */
abstract contract UUPSUpgradeable is Initializable, IERC1822Proxiable {
    /// @custom:oz-upgrades-unsafe-allow state-variable-immutable
    address private immutable __self = address(this);

    /**
     * @dev The version of the upgrade interface of the contract. If this getter is missing, both `upgradeTo(address)`
     * and `upgradeToAndCall(address,bytes)` are present, and `upgradeTo` must be used if no function should be called,
     * while `upgradeToAndCall` will invoke the `receive` function if the second argument is the empty byte string.
     * If the getter returns `"5.0.0"`, only `upgradeToAndCall(address,bytes)` is present, and the second argument must
     * be the empty byte string if no function should be called, making it impossible to invoke the `receive` function
     * during an upgrade.
     */
    string public constant UPGRADE_INTERFACE_VERSION = "5.0.0";

    /**
     * @dev The call is from an unauthorized context.
     */
    error UUPSUnauthorizedCallContext();

    /**
     * @dev The storage `slot` is unsupported as a UUID.
     */
    error UUPSUnsupportedProxiableUUID(bytes32 slot);

    /**
     * @dev Check that the execution is being performed through a delegatecall call and that the execution context is
     * a proxy contract with an implementation (as defined in ERC1967) pointing to self. This should only be the case
     * for UUPS and transparent proxies that are using the current contract as their implementation. Execution of a
     * function through ERC1167 minimal proxies (clones) would not normally pass this test, but is not guaranteed to
     * fail.
     */
    modifier onlyProxy() {
        _checkProxy();
        _;
    }

    /**
     * @dev Check that the execution is not being performed through a delegate call. This allows a function to be
     * callable on the implementing contract but not through proxies.
     */
    modifier notDelegated() {
        _checkNotDelegated();
        _;
    }

    function __UUPSUpgradeable_init() internal onlyInitializing {
    }

    function __UUPSUpgradeable_init_unchained() internal onlyInitializing {
    }
    /**
     * @dev Implementation of the ERC1822 {proxiableUUID} function. This returns the storage slot used by the
     * implementation. It is used to validate the implementation's compatibility when performing an upgrade.
     *
     * IMPORTANT: A proxy pointing at a proxiable contract should not be considered proxiable itself, because this risks
     * bricking a proxy that upgrades to it, by delegating to itself until out of gas. Thus it is critical that this
     * function revert if invoked through a proxy. This is guaranteed by the `notDelegated` modifier.
     */
    function proxiableUUID() external view virtual notDelegated returns (bytes32) {
        return ERC1967Utils.IMPLEMENTATION_SLOT;
    }

    /**
     * @dev Upgrade the implementation of the proxy to `newImplementation`, and subsequently execute the function call
     * encoded in `data`.
     *
     * Calls {_authorizeUpgrade}.
     *
     * Emits an {Upgraded} event.
     *
     * @custom:oz-upgrades-unsafe-allow-reachable delegatecall
     */
    function upgradeToAndCall(address newImplementation, bytes memory data) public payable virtual onlyProxy {
        _authorizeUpgrade(newImplementation);
        _upgradeToAndCallUUPS(newImplementation, data);
    }

    /**
     * @dev Reverts if the execution is not performed via delegatecall or the execution
     * context is not of a proxy with an ERC1967-compliant implementation pointing to self.
     * See {_onlyProxy}.
     */
    function _checkProxy() internal view virtual {
        if (
            address(this) == __self || // Must be called through delegatecall
            ERC1967Utils.getImplementation() != __self // Must be called through an active proxy
        ) {
            revert UUPSUnauthorizedCallContext();
        }
    }

    /**
     * @dev Reverts if the execution is performed via delegatecall.
     * See {notDelegated}.
     */
    function _checkNotDelegated() internal view virtual {
        if (address(this) != __self) {
            // Must not be called through delegatecall
            revert UUPSUnauthorizedCallContext();
        }
    }

    /**
     * @dev Function that should revert when `msg.sender` is not authorized to upgrade the contract. Called by
     * {upgradeToAndCall}.
     *
     * Normally, this function will use an xref:access.adoc[access control] modifier such as {Ownable-onlyOwner}.
     *
     * ```solidity
     * function _authorizeUpgrade(address) internal onlyOwner {}
     * ```
     */
    function _authorizeUpgrade(address newImplementation) internal virtual;

    /**
     * @dev Performs an implementation upgrade with a security check for UUPS proxies, and additional setup call.
     *
     * As a security check, {proxiableUUID} is invoked in the new implementation, and the return value
     * is expected to be the implementation slot in ERC1967.
     *
     * Emits an {IERC1967-Upgraded} event.
     */
    function _upgradeToAndCallUUPS(address newImplementation, bytes memory data) private {
        try IERC1822Proxiable(newImplementation).proxiableUUID() returns (bytes32 slot) {
            if (slot != ERC1967Utils.IMPLEMENTATION_SLOT) {
                revert UUPSUnsupportedProxiableUUID(slot);
            }
            ERC1967Utils.upgradeToAndCall(newImplementation, data);
        } catch {
            // The implementation is not UUPS
            revert ERC1967Utils.ERC1967InvalidImplementation(newImplementation);
        }
    }
}
"
    },
    "../../node_modules/@openzeppelin/contracts-upgradeable/utils/NoncesUpgradeable.sol": {
      "content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/Nonces.sol)
pragma solidity ^0.8.20;
import {Initializable} from "../proxy/utils/Initializable.sol";

/**
 * @dev Provides tracking nonces for addresses. Nonces will only increment.
 */
abstract contract NoncesUpgradeable is Initializable {
    /**
     * @dev The nonce used for an `account` is not the expected current nonce.
     */
    error InvalidAccountNonce(address account, uint256 currentNonce);

    /// @custom:storage-location erc7201:openzeppelin.storage.Nonces
    struct NoncesStorage {
        mapping(address account => uint256) _nonces;
    }

    // keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.Nonces")) - 1)) & ~bytes32(uint256(0xff))
    bytes32 private constant NoncesStorageLocation = 0x5ab42ced628888259c08ac98db1eb0cf702fc1501344311d8b100cd1bfe4bb00;

    function _getNoncesStorage() private pure returns (NoncesStorage storage $) {
        assembly {
            $.slot := NoncesStorageLocation
        }
    }

    function __Nonces_init() internal onlyInitializing {
    }

    function __Nonces_init_unchained() internal onlyInitializing {
    }
    /**
     * @dev Returns the next unused nonce for an address.
     */
    function nonces(address owner) public view virtual returns (uint256) {
        NoncesStorage storage $ = _getNoncesStorage();
        return $._nonces[owner];
    }

    /**
     * @dev Consumes a nonce.
     *
     * Returns the current value and increments nonce.
     */
    function _useNonce(address owner) internal virtual returns (uint256) {
        NoncesStorage storage $ = _getNoncesStorage();
        // For each account, the nonce has an initial value of 0, can only be incremented by one, and cannot be
        // decremented or reset. This guarantees that the nonce never overflows.
        unchecked {
            // It is important to do x++ and not ++x here.
            return $._nonces[owner]++;
        }
    }

    /**
     * @dev Same as {_useNonce} but checking that `nonce` is the next valid for `owner`.
     */
    function _useCheckedNonce(address owner, uint256 nonce) internal virtual {
        uint256 current = _useNonce(owner);
        if (nonce != current) {
            revert InvalidAccountNonce(owner, current);
        }
    }
}
"
    },
    "../../node_modules/@openzeppelin/contracts/token/ERC20/ERC20.sol": {
      "content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/ERC20.sol)

pragma solidity ^0.8.20;

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

/**
 * @dev Implementation of the {IERC20} interface.
 *
 * This implementation is agnostic to the way tokens are created. This means
 * that a supply mechanism has to be added in a derived contract using {_mint}.
 *
 * TIP: For a detailed writeup see our guide
 * https://forum.openzeppelin.com/t/how-to-implement-erc20-supply-mechanisms/226[How
 * to implement supply mechanisms].
 *
 * The default value of {decimals} is 18. To change this, you should override
 * this function so it returns a different value.
 *
 * We have followed general OpenZeppelin Contracts guidelines: functions revert
 * instead returning `false` on failure. This behavior is nonetheless
 * conventional and does not conflict with the expectations of ERC20
 * applications.
 *
 * Additionally, an {Approval} event is emitted on calls to {transferFrom}.
 * This allows applications to reconstruct the allowance for all accounts just
 * by listening to said events. Other implementations of the EIP may not emit
 * these events, as it isn't required by the specification.
 */
abstract contract ERC20 is Context, IERC20, IERC20Metadata, IERC20Errors {
    mapping(address account => uint256) private _balances;

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

    uint256 private _totalSupply;

    string private _name;
    string private _symbol;

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

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

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

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

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

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

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

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

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

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

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

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

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

        emit Transfer(from, to, value);
    }

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

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

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

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

    /**
     * @dev Updates `owner` s allowance for `spender` based on spent `value`.
     *
     * Does not update the allowance value in case of infinite allowance.
     * Revert if not enough allowance is available.
     *
     * Does not emit an {Approval} event.
     */
    function _spendAllowance(address owner, address spender, uint256 value) internal virtual {
        uint256 currentAllowance = allowance(owner, spender);
        if (currentAllowance != type(uint256).max) {
            if (currentAllowance < value) {
                revert ERC20InsufficientAllowance(spender, currentAllowance, value);
            }
            unchecked {
                _approve(owner, spender, currentAllowance - value, false);
            }
        }
    }
}
"
    },
    "../../node_modules/@openzeppelin/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);
}
"
    },
    "../../node_modules/@openzeppelin/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;
    }
}
"
    },
    "../../node_modules/@openzeppelin/contracts/utils/cryptography/ECDSA.sol": {
      "content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/cryptography/ECDSA.sol)

pragma solidity ^0.8.20;

/**
 * @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations.
 *
 * These functions can be used to verify that a message was signed by the holder
 * of the private keys of a given address.
 */
library ECDSA {
    enum RecoverError {
        NoError,
        InvalidSignature,
        InvalidSignatureLength,
        InvalidSignatureS
    }

    /**
     * @dev The signature derives the `address(0)`.
     */
    error ECDSAInvalidSignature();

    /**
     * @dev The signature has an invalid length.
     */
    error ECDSAInvalidSignatureLength(uint256 length);

    /**
     * @dev The signature has an S value that is in the upper half order.
     */
    error ECDSAInvalidSignatureS(bytes32 s);

    /**
     * @dev Returns the address that signed a hashed message (`hash`) with `signature` or an error. This will not
     * return address(0) without also returning an error description. Errors are documented using an enum (error type)
     * and a bytes32 providing additional information about the error.
     *
     * If no error is returned, then the address can be used for verification purposes.
     *
     * The `ecrecover` EVM precompile allows for malleable (non-unique) signatures:
     * this function rejects them by requiring the `s` value to be in the lower
     * half order, and the `v` value to be either 27 or 28.
     *
     * IMPORTANT: `hash` _must_ be the result of a hash operation for the
     * verification to be secure: it is possible to craft signatures that
     * recover to arbitrary addresses for non-hashed data. A safe way to ensure
     * this is by receiving a hash of the original message (which may otherwise
     * be too long), and then calling {MessageHashUtils-toEthSignedMessageHash} on it.
     *
     * Documentation for signature generation:
     * - with https://web3js.readthedocs.io/en/v1.3.4/web3-eth-accounts.html#sign[Web3.js]
     * - with https://docs.ethers.io/v5/api/signer/#Signer-signMessage[ethers]
     */
    function tryRecover(bytes32 hash, bytes memory signature) internal pure returns (address, RecoverError, bytes32) {
        if (signature.length == 65) {
            bytes32 r;
            bytes32 s;
            uint8 v;
            // ecrecover takes the signature parameters, and the only way to get them
            // currently is to use assembly.
            /// @solidity memory-safe-assembly
            assembly {
                r := mload(add(signature, 0x20))
                s := mload(add(signature, 0x40))
                v := byte(0, mload(add(signature, 0x60)))
            }
            return tryRecover(hash, v, r, s);
        } else {
            return (address(0), RecoverError.InvalidSignatureLength, bytes32(signature.length));
        }
    }

    /**
     * @dev Returns the address that signed a hashed message (`hash`) with
     * `signature`. This address can then be used for verification purposes.
     *
     * The `ecrecover` EVM precompile allows for malleable (non-unique) signatures:
     * this function rejects them by requiring the `s` value to be in the lower
     * half order, and the `v` value to be either 27 or 28.
     *
     * IMPORTANT: `hash` _must_ be the result of a hash operation for the
     * verification to be secure: it is possible to craft signatures that
     * recover to arbitrary addresses for non-hashed data. A safe way to ensure
     * this is by receiving a hash of the original message (which may otherwise
     * be too long), and then calling {MessageHashUtils-toEthSignedMessag

Tags:
ERC20, Multisig, Pausable, Upgradeable, Multi-Signature, Factory|addr:0xe6b85d36bbabaca3e900fc887a13522345bd024e|verified:true|block:23683657|tx:0xa368365e242d26b62e05c18e0abf470463169ffd2552a2fec76519b98065bc16|first_check:1761751912

Submitted on: 2025-10-29 16:31:52

Comments

Log in to comment.

No comments yet.