FeeAdapterV2

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

import {Ownable, Ownable2Step} from '@openzeppelin/contracts/access/Ownable2Step.sol';
import {IERC20} from '@openzeppelin/contracts/token/ERC20/IERC20.sol';
import {SafeERC20} from '@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol';
import {Address} from '@openzeppelin/contracts/utils/Address.sol';
import {ECDSA} from '@openzeppelin/contracts/utils/cryptography/ECDSA.sol';
import {MessageHashUtils} from '@openzeppelin/contracts/utils/cryptography/MessageHashUtils.sol';

import {TypeCasts} from 'contracts/common/TypeCasts.sol';

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

import {IPermit2} from 'interfaces/common/IPermit2.sol';
import {IEverclearSpokeV6} from 'interfaces/intent/IEverclearSpokeV6.sol';
import {IFeeAdapterV2} from 'interfaces/intent/IFeeAdapterV2.sol';

contract FeeAdapterV2 is IFeeAdapterV2, Ownable2Step {
  ////////////////////
  //// Libraries /////
  ////////////////////
  using SafeERC20 for IERC20;
  using TypeCasts for address;
  using TypeCasts for bytes32;

  ////////////////////
  ///// Storage //////
  ////////////////////

  /// @inheritdoc IFeeAdapterV2
  IPermit2 public constant PERMIT2 = IPermit2(0x000000000022D473030F116dDEE9F6B43aC78BA3);

  /// @inheritdoc IFeeAdapterV2
  IEverclearSpokeV6 public immutable spoke;

  // @inheritdoc IFeeAdapterV2
  address public immutable xerc20Module;

  /// @inheritdoc IFeeAdapterV2
  address public feeRecipient;

  /// @inheritdoc IFeeAdapterV2
  address public feeSigner;

  /// @inheritdoc IFeeAdapterV2
  mapping(bytes32 => bool) public txExists;

  ////////////////////
  /// Constructor ////
  ////////////////////
  constructor(
    address _spoke,
    address _feeRecipient,
    address _feeSigner,
    address _xerc20Module,
    address _owner
  ) Ownable(_owner) {
    spoke = IEverclearSpokeV6(_spoke);
    xerc20Module = _xerc20Module;
    _updateFeeRecipient(_feeRecipient);
    _updateFeeSigner(_feeSigner);
  }

  ////////////////////
  ////// Admin ///////
  ////////////////////

  /// @inheritdoc IFeeAdapterV2
  function updateFeeRecipient(
    address _feeRecipient
  ) external onlyOwner {
    _updateFeeRecipient(_feeRecipient);
  }

  /// @inheritdoc IFeeAdapterV2
  function updateFeeSigner(
    address _feeSigner
  ) external onlyOwner {
    _updateFeeSigner(_feeSigner);
  }

  /// @inheritdoc IFeeAdapterV2
  function returnUnsupportedIntent(
    address _asset,
    uint256 _amount,
    address _recipient
  ) external onlyOwner {
    spoke.withdraw(_asset, _amount);
    _pushTokens(_recipient, _asset, _amount);
  }

  ////////////////////
  ///// External /////
  ////////////////////

  /// @inheritdoc IFeeAdapterV2
  function newIntent(
    uint32[] memory _destinations,
    bytes32 _receiver,
    address _inputAsset,
    bytes32 _outputAsset,
    uint256 _amount,
    uint256 _amountOutMin,
    uint48 _ttl,
    bytes calldata _data,
    IFeeAdapterV2.FeeParams calldata _feeParams
  ) external payable returns (bytes32 _intentId, IEverclearV2.Intent memory _intent) {
    // Transfer from caller
    _pullTokens(msg.sender, _inputAsset, _amount + _feeParams.fee);

    // Create intent
    (_intentId, _intent) =
      _newIntent(_destinations, _receiver, _inputAsset, _outputAsset, _amount, _amountOutMin, _ttl, _data, _feeParams);
  }

  /// @inheritdoc IFeeAdapterV2
  function newIntent(
    uint32[] memory _destinations,
    address _receiver,
    address _inputAsset,
    address _outputAsset,
    uint256 _amount,
    uint256 _amountOutMin,
    uint48 _ttl,
    bytes calldata _data,
    IFeeAdapterV2.FeeParams calldata _feeParams
  ) external payable returns (bytes32 _intentId, IEverclearV2.Intent memory _intent) {
    // Transfer from caller
    _pullTokens(msg.sender, _inputAsset, _amount + _feeParams.fee);

    // Create intent
    (_intentId, _intent) =
      _newIntent(_destinations, _receiver, _inputAsset, _outputAsset, _amount, _amountOutMin, _ttl, _data, _feeParams);
  }

  /// @inheritdoc IFeeAdapterV2
  function newIntent(
    uint32[] memory _destinations,
    address _receiver,
    address _inputAsset,
    address _outputAsset,
    uint256 _amount,
    uint256 _amountOutMin,
    uint48 _ttl,
    bytes calldata _data,
    IEverclearSpokeV6.Permit2Params calldata _permit2Params,
    IFeeAdapterV2.FeeParams calldata _feeParams
  ) external payable returns (bytes32 _intentId, IEverclearV2.Intent memory _intent) {
    // Transfer from caller using permit2
    _pullWithPermit2(_inputAsset, _amount + _feeParams.fee, _permit2Params);

    // Call internal helper to create intent
    (_intentId, _intent) =
      _newIntent(_destinations, _receiver, _inputAsset, _outputAsset, _amount, _amountOutMin, _ttl, _data, _feeParams);
  }

  /// @inheritdoc IFeeAdapterV2
  function newOrderSplitEvenly(
    uint32 _numIntents,
    uint256 _fee,
    uint256 _deadline,
    bytes calldata _sig,
    OrderParameters memory _params
  ) external payable returns (bytes32 _orderId, bytes32[] memory _intentIds) {
    // Transfer once from the user
    _pullTokens(msg.sender, _params.inputAsset, _params.amount + _fee);

    // Send fees to recipient
    bytes32 _sigData = keccak256(abi.encode(msg.value, _numIntents, _params, _fee, _deadline));
    _verifySignature(_sigData, _sig);
    _handleFees(_fee, msg.value, _params.inputAsset, _deadline);

    // Approve the spoke contract if needed
    _approveSpokeIfNeeded(_params.inputAsset, _params.amount);

    // Create `_numIntents` intents with the same params and `_amount` divided
    // equally across all created intents.
    uint256 _toSend = _params.amount / _numIntents;

    // Initialising array length
    _intentIds = new bytes32[](_numIntents);

    for (uint256 i; i < _numIntents - 1; i++) {
      // Create new intent
      (bytes32 _intentId,) = spoke.newIntent(
        _params.destinations,
        _params.receiver,
        _params.inputAsset,
        _params.outputAsset,
        _toSend,
        _params.amountOutMin,
        _params.ttl,
        _params.data
      );
      _intentIds[i] = _intentId;
    }

    // Create a final intent here with the remainder of balance
    _toSend = _toSend * (_numIntents - 1);
    (bytes32 _intentId,) = spoke.newIntent(
      _params.destinations,
      _params.receiver,
      _params.inputAsset,
      _params.outputAsset,
      _params.amount - _toSend, // handles remainder gracefully
      _params.amountOutMin,
      _params.ttl,
      _params.data
    );

    // Add to array
    _intentIds[_numIntents - 1] = _intentId;

    // Calculate order id
    _orderId = keccak256(abi.encode(_intentIds));

    // Emit order information event
    emit OrderCreated(_orderId, msg.sender.toBytes32(), _intentIds, _fee, msg.value);
  }

  /// @inheritdoc IFeeAdapterV2
  function newOrder(
    uint256 _fee,
    uint256 _deadline,
    bytes calldata _sig,
    OrderParameters[] memory _params
  ) external payable returns (bytes32 _orderId, bytes32[] memory _intentIds) {
    uint256 _numIntents = _params.length;

    {
      // Get the asset
      address _inputAsset = _params[0].inputAsset;

      // Get the sum of the order amounts
      uint256 _orderSum;
      for (uint256 i; i < _numIntents; i++) {
        _orderSum += _params[i].amount;
        if (_params[i].inputAsset != _inputAsset) {
          revert MultipleOrderAssets();
        }
      }

      // Transfer once from the user
      _pullTokens(msg.sender, _inputAsset, _orderSum + _fee);

      // Approve the spoke contract if needed
      _approveSpokeIfNeeded(_inputAsset, _orderSum);

      // Send fees to recipient
      bytes32 _sigData = keccak256(abi.encode(msg.value, _params, _fee, _deadline));
      _verifySignature(_sigData, _sig);
      _handleFees(_fee, msg.value, _inputAsset, _deadline);
    }

    // Initialising array length
    _intentIds = new bytes32[](_numIntents);
    for (uint256 i; i < _numIntents; i++) {
      // Create new intent
      (bytes32 _intentId,) = spoke.newIntent(
        _params[i].destinations,
        _params[i].receiver,
        _params[i].inputAsset,
        _params[i].outputAsset,
        _params[i].amount,
        _params[i].amountOutMin,
        _params[i].ttl,
        _params[i].data
      );
      _intentIds[i] = _intentId;
    }

    // Calculate order id
    _orderId = keccak256(abi.encode(_intentIds));

    // Emit order event
    emit OrderCreated(_orderId, msg.sender.toBytes32(), _intentIds, _fee, msg.value);
  }

  ////////////////////
  ///// Internal /////
  ////////////////////

  /**
   * @notice Internal function to create a new intent
   * @param _destinations Array of destination chain IDs
   * @param _receiver Address of the receiver on the destination chain
   * @param _inputAsset Address of the input asset
   * @param _outputAsset Address of the output asset
   * @param _amount Amount of input asset to transfer
   * @param _amountOutMin Maximum fee in basis points that can be charged
   * @param _ttl Time-to-live for the intent
   * @param _data Additional data for the intent
   * @param _feeParams Fee parameters including fee amount, deadline, and signature
   * @return _intentId The ID of the created intent
   * @return _intent The created intent object
   */
  function _newIntent(
    uint32[] memory _destinations,
    bytes32 _receiver,
    address _inputAsset,
    bytes32 _outputAsset,
    uint256 _amount,
    uint256 _amountOutMin,
    uint48 _ttl,
    bytes calldata _data,
    IFeeAdapterV2.FeeParams calldata _feeParams
  ) internal returns (bytes32 _intentId, IEverclearV2.Intent memory _intent) {
    // Send fees to recipient
    bytes32 _sigData = keccak256(
      abi.encode(
        msg.value,
        _destinations,
        _inputAsset,
        _outputAsset,
        _amount,
        _amountOutMin,
        _ttl,
        _data,
        _feeParams.fee,
        _feeParams.deadline
      )
    );
    _verifySignature(_sigData, _feeParams.sig);
    _handleFees(_feeParams.fee, msg.value, _inputAsset, _feeParams.deadline);

    // Approve the spoke contract if needed
    _approveSpokeIfNeeded(_inputAsset, _amount);

    // Create new intent
    (_intentId, _intent) =
      spoke.newIntent(_destinations, _receiver, _inputAsset, _outputAsset, _amount, _amountOutMin, _ttl, _data);

    // Emit event
    emit IntentWithFeesAdded(_intentId, msg.sender.toBytes32(), _feeParams.fee, msg.value);
    return (_intentId, _intent);
  }

  /**
   * @notice Internal function to create a new intent
   * @param _destinations Array of destination chain IDs
   * @param _receiver Address of the receiver on the destination chain
   * @param _inputAsset Address of the input asset
   * @param _outputAsset Address of the output asset
   * @param _amount Amount of input asset to transfer
   * @param _amountOutMin Maximum fee in basis points that can be charged
   * @param _ttl Time-to-live for the intent
   * @param _data Additional data for the intent
   * @param _feeParams Fee parameters including fee amount, deadline, and signature
   * @return _intentId The ID of the created intent
   * @return _intent The created intent object
   */
  function _newIntent(
    uint32[] memory _destinations,
    address _receiver,
    address _inputAsset,
    address _outputAsset,
    uint256 _amount,
    uint256 _amountOutMin,
    uint48 _ttl,
    bytes calldata _data,
    IFeeAdapterV2.FeeParams calldata _feeParams
  ) internal returns (bytes32 _intentId, IEverclearV2.Intent memory _intent) {
    // Send fees to recipient
    bytes32 _sigData = keccak256(
      abi.encode(
        msg.value,
        _destinations,
        _inputAsset,
        _outputAsset,
        _amount,
        _amountOutMin,
        _ttl,
        _data,
        _feeParams.fee,
        _feeParams.deadline
      )
    );
    _verifySignature(_sigData, _feeParams.sig);
    _handleFees(_feeParams.fee, msg.value, _inputAsset, _feeParams.deadline);

    // Approve the spoke contract if needed
    _approveSpokeIfNeeded(_inputAsset, _amount);

    // Create new intent
    (_intentId, _intent) =
      spoke.newIntent(_destinations, _receiver, _inputAsset, _outputAsset, _amount, _amountOutMin, _ttl, _data);

    // Emit event
    emit IntentWithFeesAdded(_intentId, msg.sender.toBytes32(), _feeParams.fee, msg.value);
    return (_intentId, _intent);
  }

  /**
   * @notice Updates the fee recipient
   * @param _feeRecipient New recipient
   */
  function _updateFeeRecipient(
    address _feeRecipient
  ) internal {
    emit FeeRecipientUpdated(_feeRecipient, feeRecipient);
    feeRecipient = _feeRecipient;
  }

  /**
   * @notice Updates the fee signer
   * @param _feeSigner New signer
   */
  function _updateFeeSigner(
    address _feeSigner
  ) internal {
    emit FeeSignerUpdated(_feeSigner, feeSigner);
    feeSigner = _feeSigner;
  }

  /**
   * @notice Verifies a signature
   * @param _dataHash The data of the message
   * @param _signature The signature of the message
   */
  function _verifySignature(
    bytes32 _dataHash,
    bytes calldata _signature
  ) internal {
    bytes32 _hash = keccak256(abi.encode(_dataHash, msg.sender, address(this), block.chainid));

    if (txExists[_hash]) revert FeeAdapter_SignatureAlreadyUsed();
    txExists[_hash] = true;

    address _recoveredSigner = ECDSA.recover(MessageHashUtils.toEthSignedMessageHash(_hash), _signature);
    if (_recoveredSigner != feeSigner) {
      revert FeeAdapter_InvalidSignature();
    }
  }

  /**
   * @notice Sends fees to recipient
   * @param _tokenFee Amount in transacting asset to send to recipient
   * @param _nativeFee Amount in native asset to send to recipient
   */
  function _handleFees(
    uint256 _tokenFee,
    uint256 _nativeFee,
    address _inputAsset,
    uint256 _deadline
  ) internal {
    // Verify the ttl is valid
    if (block.timestamp > _deadline) {
      revert FeeAdapter_InvalidDeadline();
    }

    // Handle token fees if exist
    if (_tokenFee > 0) {
      _pushTokens(feeRecipient, _inputAsset, _tokenFee);
    }

    // Handle native tokens
    if (_nativeFee > 0) {
      Address.sendValue(payable(feeRecipient), _nativeFee);
    }
  }

  /**
   * @notice Approves the maximum uint value to the gateway.
   * @dev Approving the max reduces gas for following intents.
   * @param _asset Asset to approve to spoke.
   * @param _minimum Minimum required approval budget.
   */
  function _approveSpokeIfNeeded(
    address _asset,
    uint256 _minimum
  ) internal {
    // Checking if the strategy is default or not
    address spender;
    IEverclearV2.Strategy _strategy = spoke.strategies(_asset);
    if (_strategy == IEverclearV2.Strategy.DEFAULT) spender = address(spoke);
    else spender = xerc20Module;

    // Approve the spoke contract if needed
    IERC20 _token = IERC20(_asset);
    uint256 _current = _token.allowance(address(this), spender);
    if (_current >= _minimum) {
      return;
    }

    // Approve to 0
    if (_current != 0) {
      _token.safeDecreaseAllowance(spender, _current);
    }

    // Approve to max
    _token.safeIncreaseAllowance(spender, type(uint256).max);
  }

  /**
   * @notice Transfers tokens from the caller to this contract using Permit2
   * @dev Uses the Permit2 contract to transfer tokens with a signature
   * @param _asset The token to transfer
   * @param _amount The amount to transfer
   * @param _permit2Params The permit2 parameters including nonce, deadline, and signature
   */
  function _pullWithPermit2(
    address _asset,
    uint256 _amount,
    IEverclearSpokeV6.Permit2Params calldata _permit2Params
  ) internal {
    // Transfer from caller using permit2
    PERMIT2.permitTransferFrom(
      IPermit2.PermitTransferFrom({
        permitted: IPermit2.TokenPermissions({token: IERC20(_asset), amount: _amount}),
        nonce: _permit2Params.nonce,
        deadline: _permit2Params.deadline
      }),
      IPermit2.SignatureTransferDetails({to: address(this), requestedAmount: _amount}),
      msg.sender,
      _permit2Params.signature
    );
  }

  /**
   * @notice Pull tokens from the sender to the 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 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);
  }
}
"
    },
    "../../node_modules/@openzeppelin/contracts/access/Ownable2Step.sol": {
      "content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable2Step.sol)

pragma solidity ^0.8.20;

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

/**
 * @dev Contract module which provides access control mechanism, where
 * there is an account (an owner) that can be granted exclusive access to
 * specific functions.
 *
 * The initial owner is specified at deployment time in the constructor for `Ownable`. This
 * can later be changed with {transferOwnership} and {acceptOwnership}.
 *
 * This module is used through inheritance. It will make available all functions
 * from parent (Ownable).
 */
abstract contract Ownable2Step is Ownable {
    address private _pendingOwner;

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

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

    /**
     * @dev Starts the ownership transfer of the contract to a new account. Replaces the pending transfer if there is one.
     * Can only be called by the current owner.
     */
    function transferOwnership(address newOwner) public virtual override onlyOwner {
        _pendingOwner = newOwner;
        emit OwnershipTransferStarted(owner(), newOwner);
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`) and deletes any pending owner.
     * Internal function without access restriction.
     */
    function _transferOwnership(address newOwner) internal virtual override {
        delete _pendingOwner;
        super._transferOwnership(newOwner);
    }

    /**
     * @dev The new owner accepts the ownership transfer.
     */
    function acceptOwnership() public virtual {
        address sender = _msgSender();
        if (pendingOwner() != sender) {
            revert OwnableUnauthorizedAccount(sender);
        }
        _transferOwnership(sender);
    }
}
"
    },
    "../../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/Address.sol": {
      "content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/Address.sol)

pragma solidity ^0.8.20;

/**
 * @dev Collection of functions related to the address type
 */
library Address {
    /**
     * @dev The ETH balance of the account is not enough to perform the operation.
     */
    error AddressInsufficientBalance(address account);

    /**
     * @dev There's no code at `target` (it is not a contract).
     */
    error AddressEmptyCode(address target);

    /**
     * @dev A call to an address target failed. The target may have reverted.
     */
    error FailedInnerCall();

    /**
     * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
     * `recipient`, forwarding all available gas and reverting on errors.
     *
     * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
     * of certain opcodes, possibly making contracts go over the 2300 gas limit
     * imposed by `transfer`, making them unable to receive funds via
     * `transfer`. {sendValue} removes this limitation.
     *
     * https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more].
     *
     * IMPORTANT: because control is transferred to `recipient`, care must be
     * taken to not create reentrancy vulnerabilities. Consider using
     * {ReentrancyGuard} or the
     * https://solidity.readthedocs.io/en/v0.8.20/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
     */
    function sendValue(address payable recipient, uint256 amount) internal {
        if (address(this).balance < amount) {
            revert AddressInsufficientBalance(address(this));
        }

        (bool success, ) = recipient.call{value: amount}("");
        if (!success) {
            revert FailedInnerCall();
        }
    }

    /**
     * @dev Performs a Solidity function call using a low level `call`. A
     * plain `call` is an unsafe replacement for a function call: use this
     * function instead.
     *
     * If `target` reverts with a revert reason or custom error, it is bubbled
     * up by this function (like regular Solidity function calls). However, if
     * the call reverted with no returned reason, this function reverts with a
     * {FailedInnerCall} error.
     *
     * Returns the raw returned data. To convert to the expected return value,
     * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
     *
     * Requirements:
     *
     * - `target` must be a contract.
     * - calling `target` with `data` must not revert.
     */
    function functionCall(address target, bytes memory data) internal returns (bytes memory) {
        return functionCallWithValue(target, data, 0);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but also transferring `value` wei to `target`.
     *
     * Requirements:
     *
     * - the calling contract must have an ETH balance of at least `value`.
     * - the called Solidity function must be `payable`.
     */
    function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
        if (address(this).balance < value) {
            revert AddressInsufficientBalance(address(this));
        }
        (bool success, bytes memory returndata) = target.call{value: value}(data);
        return verifyCallResultFromTarget(target, success, returndata);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a static call.
     */
    function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
        (bool success, bytes memory returndata) = target.staticcall(data);
        return verifyCallResultFromTarget(target, success, returndata);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a delegate call.
     */
    function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
        (bool success, bytes memory returndata) = target.delegatecall(data);
        return verifyCallResultFromTarget(target, success, returndata);
    }

    /**
     * @dev Tool to verify that a low level call to smart-contract was successful, and reverts if the target
     * was not a contract or bubbling up the revert reason (falling back to {FailedInnerCall}) in case of an
     * unsuccessful call.
     */
    function verifyCallResultFromTarget(
        address target,
        bool success,
        bytes memory returndata
    ) internal view returns (bytes memory) {
        if (!success) {
            _revert(returndata);
        } else {
            // only check if target is a contract if the call was successful and the return data is empty
            // otherwise we already know that it was a contract
            if (returndata.length == 0 && target.code.length == 0) {
                revert AddressEmptyCode(target);
            }
            return returndata;
        }
    }

    /**
     * @dev Tool to verify that a low level call was successful, and reverts if it wasn't, either by bubbling the
     * revert reason or with a default {FailedInnerCall} error.
     */
    function verifyCallResult(bool success, bytes memory returndata) internal pure returns (bytes memory) {
        if (!success) {
            _revert(returndata);
        } else {
            return returndata;
        }
    }

    /**
     * @dev Reverts with returndata if present. Otherwise reverts with {FailedInnerCall}.
     */
    function _revert(bytes memory returndata) private pure {
        // Look for revert reason and bubble it up if present
        if (returndata.length > 0) {
            // The easiest way to bubble the revert reason is using memory via assembly
            /// @solidity memory-safe-assembly
            assembly {
                let returndata_size := mload(returndata)
                revert(add(32, returndata), returndata_size)
            }
        } else {
            revert FailedInnerCall();
        }
    }
}
"
    },
    "../../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-toEthSignedMessageHash} on it.
     */
    function recover(bytes32 hash, bytes memory signature) internal pure returns (address) {
        (address recovered, RecoverError error, bytes32 errorArg) = tryRecover(hash, signature);
        _throwError(error, errorArg);
        return recovered;
    }

    /**
     * @dev Overload of {ECDSA-tryRecover} that receives the `r` and `vs` short-signature fields separately.
     *
     * See https://eips.ethereum.org/EIPS/eip-2098[EIP-2098 short signatures]
     */
    function tryRecover(bytes32 hash, bytes32 r, bytes32 vs) internal pure returns (address, RecoverError, bytes32) {
        unchecked {
            bytes32 s = vs & bytes32(0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff);
            // We do not check for an overflow here since the shift operation results in 0 or 1.
            uint8 v = uint8((uint256(vs) >> 255) + 27);
            return tryRecover(hash, v, r, s);
        }
    }

    /**
     * @dev Overload of {ECDSA-recover} that receives the `r and `vs` short-signature fields separately.
     */
    function recover(bytes32 hash, bytes32 r, bytes32 vs) internal pure returns (address) {
        (address recovered, RecoverError error, bytes32 errorArg) = tryRecover(hash, r, vs);
        _throwError(error, errorArg);
        return recovered;
    }

    /**
     * @dev Overload of {ECDSA-tryRecover} that receives the `v`,
     * `r` and `s` signature fields separately.
     */
    function tryRecover(
        bytes32 hash,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) internal pure returns (address, RecoverError, bytes32) {
        // EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature
        // unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines
        // the valid range for s in (301): 0 < s < secp256k1n ÷ 2 + 1, and for v in (302): v ∈ {27, 28}. Most
        // signatures from current libraries generate a unique signature with an s-value in the lower half order.
        //
        // If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value
        // with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or
        // vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept
        // these malleable signatures as well.
        if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) {
            return (address(0), RecoverError.InvalidSignatureS, s);
        }

        // If the signature is valid (and not malleable), return the signer address
        address signer = ecrecover(hash, v, r, s);
        if (signer == address(0)) {
            return (address(0), RecoverError.InvalidSignature, bytes32(0));
        }

        return (signer, RecoverError.NoError, bytes32(0));
    }

    /**
     * @dev Overload of {ECDSA-recover} that receives the `v`,
     * `r` and `s` signature fields separately.
     */
    function recover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) internal pure returns (address) {
        (address recovered, RecoverError error, bytes32 errorArg) = tryRecover(hash, v, r, s);
        _throwError(error, errorArg);
        return recovered;
    }

    /**
     * @dev Optionally reverts with the corresponding custom error according to the `error` argument provided.
     */
    function _throwError(RecoverError error, bytes32 errorArg) private pure {
        if (error == RecoverError.NoError) {
            return; // no error: do nothing
        } else if (error == RecoverError.InvalidSignature) {
            revert ECDSAInvalidSignature();
        } else if (error == RecoverError.InvalidSignatureLength) {
            revert ECDSAInvalidSignatureLength(uint256(errorArg));
        } else if (error == RecoverError.InvalidSignatureS) {
            revert ECDSAInvalidSignatureS(errorArg);
        }
    }
}
"
    },
    "../../node_modules/@openzeppelin/contracts/utils/cryptography/MessageHashUtils.sol": {
      "content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/cryptography/MessageHashUtils.sol)

pragma solidity ^0.8.20;

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

/**
 * @dev Signature message hash utilities for producing digests to be consumed by {ECDSA} recovery or signing.
 *
 * The library provides methods for generating a hash of a message that conforms to the
 * https://eips.ethereum.org/EIPS/eip-191[EIP 191] and https://eips.ethereum.org/EIPS/eip-712[EIP 712]
 * specifications.
 */
library MessageHashUtils {
    /**
     * @dev Returns the keccak256 digest of an EIP-191 signed data with version
     * `0x45` (`personal_sign` messages).
     *
     * The digest is calculated by prefixing a bytes32 `messageHash` with
     * `"\x19Ethereum Signed Message:\
32"` and hashing the result. It corresponds with the
     * hash signed when using the https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`] JSON-RPC method.
     *
     * NOTE: The `messageHash` parameter is intended to be the result of hashing a raw message with
     * keccak256, although any bytes32 value can be safely used because the final digest will
     * be re-hashed.
     *
     * See {ECDSA-recover}.
     */
    function toEthSignedMessageHash(bytes32 messageHash) internal pure returns (bytes32 digest) {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x00, "\x19Ethereum Signed Message:\
32") // 32 is the bytes-length of messageHash
            mstore(0x1c, messageHash) // 0x1c (28) is the length of the prefix
            digest := keccak256(0x00, 0x3c) // 0x3c is the length of the prefix (0x1c) + messageHash (0x20)
        }
    }

    /**
     * @dev Returns the keccak256 digest of an EIP-191 signed data with version
     * `0x45` (`personal_sign` messages).
     *
     * The digest is calculated by prefixing an arbitrary `message` with
     * `"\x19Ethereum Signed Message:\
" + len(message)` and hashing the result. It corresponds with the
     * hash signed when using the https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`] JSON-RPC method.
     *
     * See {ECDSA-recover}.
     */
    function toEthSignedMessageHash(bytes memory message) internal pure returns (bytes32) {
        return
            keccak256(bytes.concat("\x19Ethereum Signed Message:\
", bytes(Strings.toString(message.length)), message));
    }

    /**
     * @dev Returns the keccak256 digest of an EIP-191 signed data with version
     * `0x00` (data with intended validator).
     *
     * The digest is calculated by prefixing an arbitrary `data` with `"\x19\x00"` and the intended
     * `validator` address. Then hashing the result.
     *
     * See {ECDSA-recover}.
     */
    function toDataWithIntendedValidatorHash(address validator, bytes memory data) internal pure returns (bytes32) {
        return keccak256(abi.encodePacked(hex"19_00", validator, data));
    }

    /**
     * @dev Returns the keccak256 digest of an EIP-712 typed data (EIP-191 version `0x01`).
     *
     * The digest is calculated from a `domainSeparator` and a `structHash`, by prefixing them with
     * `\x19\x01` and hashing the result. It corresponds to the hash signed by the
     * https://eips.ethereum.org/EIPS/eip-712[`eth_signTypedData`] JSON-RPC method as part of EIP-712.
     *
     * See {ECDSA-recover}.
     */
    function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32 digest) {
        /// @solidity memory-safe-assembly
        assembly {
            let ptr := mload(0x40)
            mstore(ptr, hex"19_01")
            mstore(add(ptr, 0x02), domainSeparator)
            mstore(add(ptr, 0x22), structHash)
            digest := keccak256(ptr, 0x42)
        }
    }
}
"
    },
    "src/contracts/common/TypeCasts.sol": {
      "content": "// SPDX-License-Identifier: MIT OR Apache-2.0
pragma solidity 0.8.25;

/**
 * @title TypeCasts
 * @notice Library for type casts
 */
library TypeCasts {
  // alignment preserving cast
  /**
   * @notice Cast an address to a bytes32
   * @param _addr The address to cast
   */
  function toBytes32(
    address _addr
  ) internal pure returns (bytes32) {
    return bytes32(uint256(uint160(_addr)));
  }

  // alignment preserving cast
  /**
   * @notice Cast a bytes32 to an address
   * @param _buf The bytes32 to cast
   */
  function toAddress(
    bytes32 _buf
  ) internal pure returns (address) {
    return address(uint160(uint256(_buf)));
  }
}
"
    },
    "src/interfaces/common/IEverclearV2.sol": {
      "content": "// SPDX-License-Identifier: MIT
pragma solidity 0.8.25;

/**
 * @title IEverclear
 * @notice Common interface for EverclearHub and EverclearSpoke
 */
interface IEverclearV2 {
  /*//////////////////////////////////////////////////////////////
                                ENUMS
    //////////////////////////////////////////////////////////////*/
  /**
   * @notice Enum representing statuses of an intent
   */
  enum IntentStatus {
    NONE, // 0
    ADDED, // 1
    DEPOSIT_PROCESSED, // 2
    FILLED, // 3
    ADDED_AND_FILLED, // 4
    INVOICED, // 5
    SETTLED, // 6
    SETTLED_AND_MANUALLY_EXECUTED, // 7
    UNSUPPORTED, // 8
    UNSUPPORTED_RETURNED // 9
  }

  /**
   * @notice Enum representing asset strategies
   */
  enum Strategy {
    DEFAULT,
    XERC20
  }

  /*///////////////////////////////////////////////////////////////
                            STRUCTS
  //////////////////////////////////////////////////////////////*/

  /**
   * @notice The structure of an intent
   * @param initiator The address of the intent initiator
   * @param receiver The address of the intent receiver
   * @param inputAsset The address of the intent asset on origin
   * @param outputAsset The address of the intent asset on destination
   * @param origin The origin chain of the intent
   * @param destinations The possible destination chains of the intent
   * @param nonce The nonce of the intent
   * @param timestamp The timestamp of the intent
   * @param ttl The time to live of the intent
   * @param amount The amount of the intent asset normalized to 18 decimals
   * @param amountOutMin The minimum amount of the output asset that the intent solver should return
   * @param data The data of the intent
   */
  struct Intent {
    bytes32 initiator;
    bytes32 receiver;
    bytes32 inputAsset;
    bytes32 outputAsset;
    uint32 origin;
    uint64 nonce;
    uint48 timestamp;
    uint48 ttl;
    uint256 amount;
    uint256 amountOutMin;
    uint32[] destinations;
    bytes data;
  }

  /**
   * @notice The structure of a fill message
   * @param intentId The ID of the intent
   * @param solver The address of the intent solver in bytes32 format
   * @param initiator The address of the intent initiator
   * @param fee The total fee of the expressed in dbps, represents the solver fee plus the sum of protocol fees for the token
   * @param executionTimestamp The execution timestamp of the intent
   */
  struct DeprecatedFillMessage {
    bytes32 intentId;
    bytes32 solver;
    bytes32 initiator;
    uint24 fee;
    uint48 executionTimestamp;
  }

  /**
   * @notice The structure of a fill message
   * @param intentId The ID of the intent
   * @param receiver The address of the intent receiver in bytes32 format
   * @param intentInputAsset The input asset of the intent (i.e. asset the solver will be repaid in)
   * @param intentOrigin The origin chain of the intent
   * @param amountOut The amount being sent to the user by the solver
   * @param destinations The settlement destinations for the fill
   * @param executionTimestamp The execution timestamp of the intent
   */
  struct FillMessage {
    bytes32 intentId;
    bytes32 receiver;
    bytes32 intentInputAsset;
    uint32 intentOrigin;
    uint256 amountOut;
    uint32[] destinations;
    uint48 executionTimestamp;
  }

  /**
   * @notice The structure of a settlement
   * @param intentId The ID of the intent
   * @param amount The amount of the asset
   * @param asset The address of the asset
   * @param recipient The address of the recipient
   * @param updateVirtualBalance If set to true, the settlement will not be transferred to the recipient in spoke domain and the virtual balance will be increased
   */
  struct Settlement {
    bytes32 intentId;
    uint256 amount;
    bytes32 asset;
    bytes32 recipient;
    bool updateVirtualBalance;
  }
}
"
    },
    "src/interfaces/common/IPermit2.sol": {
      "content": "// SPDX-License-Identifier: MIT
pragma solidity 0.8.25;

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

/**
 * @title IPermit2
 * @notice Interface for permit2
 */
interface IPermit2 {
  /*///////////////////////////////////////////////////////////////
                                STRUCTS
  //////////////////////////////////////////////////////////////*/

  /**
   * @notice Struct for token and amount in a permit message
   * @param token The token to transfer
   * @param amount The amount to transfer
   */
  struct TokenPermissions {
    IERC20 token;
    uint256 amount;
  }

  /**
   * @notice Struct for the permit2 message
   * @param permitted The permitted token and amount
   * @param nonce The unique identifier for this permit
   * @param deadline The expiration for this permit
   */
  struct PermitTransferFrom {
    TokenPermissions permitted;
    uint256 nonce;
    uint256 deadline;
  }

  /**
   * @notice Struct for the transfer details for permitTransferFrom()
   * @param to The recipient of the tokens
   * @param requestedAmount The amount to transfer
   */
  struct SignatureTransferDetails {
    address to;
    uint256 requestedAmount;
  }

  /*///////////////////////////////////////////////////////////////
                                LOGIC
  //////////////////////////////////////////////////////////////*/

  /**
   * @notice Consume a permit2 message and transfer tokens
   * @param permit The permit message
   * @param transferDetails The transfer details
   * @param owner The owner of the tokens
   * @param signature The signature of the permit
   */
  function permitTransferFrom(
    PermitTransferFrom calldata permit,
    SignatureTransferDetails calldata transferDetails,
    address owner,
    bytes calldata signature
  ) external;
}
"
    },
    "src/interfaces/intent/IEverclearSpokeV6.sol": {
      "content": "// SPDX-License-Identifier: MIT
pragma solidity 0.8.25;

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

import {ISpokeStorageV6} from './ISpokeStorageV6.sol';

/**
 * @title IEverclearSpoke
 * @notice Interface for the EverclearSpoke contract
 */
interface IEverclearSpokeV6 is ISpokeStorageV6 {
  /*///////////////////////////////////////////////////////////////
                              STRUCTS
  //////////////////////////////////////////////////////////////*/

  /**
   * @notice Parameters needed to execute a permit2
   * @param nonce The nonce of the permit
   * @param deadline The deadline of the permit
   * @param signature The signature of the permit
   */
  struct Permit2Params {
    uint256 nonce;
    uint256 deadline;
    bytes signature;
  }
  /*///////////////////////////////////////////////////////////////
                              EVENTS
  //////////////////////////////////////////////////////////////*/

  /**
   * @notice emitted when a new intent is added on origin
   * @param _intentId The ID of the intent
   * @param _queueIdx The index of the intent in the IntentQueue
   * @param _intent The intent object
   */
  event IntentAdded(bytes32 indexed _intentId, uint256 _queueIdx, Intent _intent);

  /**
   * @notice emitted when an intent is filled on destination
   * @param _intentId The ID of the intent
   * @param _solver The address of the intent solver
   * @param _receiver The address of the intent receiver
   * @param _amountOut The total amount the user has been transferred
   * @param _queueIdx The index of the FillMessage in the FillQueue
   * @param _intent The full intent object
   */
  event IntentFilled(
    bytes32 indexed _intentId,
    address indexed _solver,
    bytes32 indexed _receiver,
    uint256 _amountOut,
    uint256 _queueIdx,
    Intent _intent
  );

  /**
   * @notice emitted when solver (or anyone) deposits an asset in the EverclearSpoke
   * @param _depositant The address of the depositant
   * @param _asset The address of the deposited asset
   * @param _amount The amount of the deposited asset
   */
  event Deposited(address indexed _depositant, address indexed _asset, uint256 _amount);

  /**
   * @notice emitted when solver (or anyone) withdraws an asset from the EverclearSpoke
   * @param _withdrawer The address of the withdrawer
   * @param _asset The address of the withdrawn asset
   * @param _amount The amount of the withdrawn asset
   */
  event Withdrawn(address indexed _withdrawer, address indexed _asset, uint256 _amount);

  /**
   * @notice Emitted when the intent queue is processed
   * @param _messageId The ID of the message
   * @param _firstIdx The first index of the queue to be processed
   * @param _lastIdx The last index of the queue to be processed
   * @param _quote The quote amount
   */
  event IntentQueueProcessed(bytes32 indexed _messageId, uint256 _firstIdx, uint256 _lastIdx, uint256 _quote);

  /**
   * @notice Emitted when the fill queue is processed
   * @param _messageId The ID of the message
   * @param _firstIdx The first index of the queue to be processed
   * @param _lastIdx The last index of the queue to be processed
   * @param _quote The quote amount
   */
  event FillQueueProcessed(bytes32 indexed _messageId, uint256 _firstIdx, uint256 _lastIdx, uint256 _quote);

  /**
   * @notice Emitted when an external call is executed
   * @param _intentId The ID of the intent
   * @param _returnData The return data of the call
   */
  event ExternalCalldataExecuted(bytes32 indexed _intentId, bytes _returnData);

  /**
   * @notice Emitted when feeAdapter is updated
   * @param _newFeeAdapter The new fee adapter
   */
  event FeeAdapterUpdated(address _newFeeAdapter);

  /**
   * @notice Emitted when fill signer is updated
   * @param _oldFillSigner The old fill signer
   * @param _newFillSigner The new fill signer
   */
  event FillSignerUpdated(address _oldFillSigner, address _newFillSigner);

  /*///////////////////////////////////////////////////////////////
                              ERRORS
  //////////////////////////////////////////////////////////////*/

  /**
   * @notice Thrown when the intent is already filled
   * @param _intentId The id of the intent which is being tried to fill
   */
  error EverclearSpoke_FillIntent_InvalidStatus(bytes32 _intentId);

  /**
   * @notice Thrown when trying to fill an expired intent
   * @param _intentId The id of the intent which is being tried to fill
   */
  error EverclearSpoke_FillIntent_IntentExpired(bytes32 _intentId);

  /**
   * @notice Thrown when calling newIntent with invalid intent parameters
   */
  error EverclearSpoke_NewIntent_InvalidIntent();

  /**
   * @notice Thrown when the ttl is non-zero and outputAsset is null
   */
  error EverclearSpoke_NewIntent_OutputAssetNull();

  /**
   * @notice Thrown when the destination array > 1 and outputAsset is not null
   */
  error EverclearSpoke_NewIntent_OutputAssetNotNull();

  /**
   * @notice Thrown when the maxFee is exceeded
   * @param _amountOut The amount sent by the solver
   * @param _amountOutMin The min amount out
   */
  error EverclearSpoke_FillIntent_AmountOutInvalid(uint256 _amountOut, uint256 _amountOutMin);

  /**
   * @notice Thrown when the intent amount is zero
   */
  error EverclearSpoke_NewIntent_ZeroAmount();

  /**
   * @notice Thrown when the solver doesnt have sufficient funds to fill an intent
   * @param _requested The amount of tokens needed to fill the intent
   * @param _available The amount of tokens the solver has deposited in the `EverclearSpoke`
   */
  error EverclearSpoke_FillIntent_InsufficientFunds(uint256 _requested, uint256 _available);

  /**
   * @notice Thrown when the destination array is empty
   */
  error EverclearSpoke_FillIntent_InvalidDestinationArray();

  /**
   * @notice Thrown when the intent calldata exceeds the limit
   */
  error EverclearSpoke_NewIntent_CalldataExceedsLimit();

  /**
   * @notice Thrown when a signature signer does not match the expected address
   */
  error EverclearSpoke_InvalidSignature();

  /**
   * @notice Thrown when the domain does not match the expected domain
   */
  error EverclearSpoke_ProcessFillViaRelayer_WrongDomain();

  /**
   * @notice Thrown when the relayer address does not match the msg.sender
   */
  error EverclearSpoke_ProcessFillViaRelayer_NotRelayer();

  /**
   * @notice Thrown when the TTL of the message has expired
   */
  error EverclearSpoke_ProcessFillViaRelayer_TTLExpired();

  /**
   * @notice Thrown when processing the intent queue and the intent is not found in the position specified in the parameter
   * @param _intentId The id of the intent being processed
   * @param _position The position specified by the queue processor
   */
  error EverclearSpoke_ProcessIntentQueue_NotFound(bytes32 _intentId, uint256 _position);

  /**
   * @notice Thrown when trying to execute the calldata of an intent with invalid status
   * @param _intentId The id of the intent whose calldata is trying to be executed
   */
  error EverclearSpoke_ExecuteIntentCalldata_InvalidStatus(bytes32 _intentId);

  /**
   * @notice Thrown when the external call failed on executeIntentCalldata
   */
  error EverclearSpoke_ExecuteIntentCalldata_ExternalCallFailed();

  /**
   * @notice Thrown when the queues are non-empty
   */
  error EverclearSpoke_Initialize_IntentQueueNotEmpty();

  /**
   * @notice Thrown when the queues are non-empty
   */
  error EverclearSpoke_Initialize_FillQueueNotEmpty();

  /**
   * @notice Thrown when the array length invalid in a batch fill
   */
  error EverclearSpoke_FillIntent_InvalidArrayLengths();

  /**
   * @notice Thrown when the fill signature is invalid
   */
  error EverclearSpoke_InvalidFillSignature();

  /**
   * @notice Thrown when the dynamic gas limit exceeds the maximum gas limit
   * @param _dynamicGasLimit The dynamic gas limit being provided
   */
  error EverclearSpoke_ProcessQueue_ExceedsGasLimit(uint256 _dynamicGasLimit);

  /*///////////////////////////////////////////////////////////////
                              LOGIC
  //////////////////////////////////////////////////////////////*/

  /**
   * @notice Pauses the contract
   * @dev only the lighthouse and watchtower can pause the contract
   */
  function pause() external;

  /**
   * @notice Unpauses the contract
   * @dev only the lighthouse and watchtower can unpause the contract
   */
  function unpause() external;

  /**
   * @notice Sets a minting / burning strategy for an asset
   * @param _asset The asset address
   * @param _strategy The strategy id (see `enum Strategy`)
   */
  function setStrategyForAsset(
    address _asset,
    IEverclearV2.Strategy _strategy
  ) external;

  /**
   * @notice Sets a module for a strategy
   * @param _strategy The strategy id (see `enum Strategy`)
   * @param _module The module contract
   */
  function setModuleForStrategy(
    IEverclearV2.Strategy _strategy,
    ISettlementModule _module
  ) external;

  /**
   * @notice Updates the security module
   * @param _newSecurityModule The address of the new security module
   */
  function updateSecurityModule(
    address _newSecurityModule
  ) external;

  /**
   * @notice Update the fee adapter
   * @param _newFeeAdapter The address of the new fee adapter
   */
  function updateFeeAdapter(
    address _newFeeAdapter
  ) external;

  /**
   * @notice Initialize the EverclearSpoke contract
   */
  function initialize(
    address _feeAdapter,
    address _messageReceiver,
    address _fillSigner
  ) external;

  /**
   * @notice Creates a new intent
   * @param _destinations The possible 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 the solver should return
   * @param _ttl The time to live of the intent
   * @param _data The data of the intent
   * @return _intentId The ID of the intent
   * @return _intent The intent object
   */
  function newIntent(
    uint32[] memory _destinations,
    bytes32 _receiver,
    address _inputAsset,
    bytes32 _outputAsset,
    uint256 _amount,
    uint256 _amountOutMin,
    uint48 _ttl,
    bytes calldata _data
  ) external returns (bytes32 _intentId, Intent memory _intent);

  /**
   * @notice Creates a new intent
   * @param _destinations The possible 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 the solver should return
   * @param _ttl The time to live of the intent
   * @param _data The data of the intent
   * @return _intentId The ID of the intent
   * @return _intent The intent object
   */
  function newIntent(
    uint32[] memory _destinations,
    address _receiver,
    address _inputAsset,
    addr

Tags:
ERC20, Multisig, Pausable, Upgradeable, Multi-Signature, Factory|addr:0x20ff5ea948881d18f7d64b64410ec2b81f8797f4|verified:true|block:23683657|tx:0x9ab38728bfadf2e1d65a45165039252828131d0297950cc26390ef6252bef4f2|first_check:1761751931

Submitted on: 2025-10-29 16:32:11

Comments

Log in to comment.

No comments yet.