APE Price: $0.74 (+3.52%)

Contract

0x4e2bbbFb10058E0D248a78fe2F469562f4eDbe66

Overview

APE Balance

Apechain LogoApechain LogoApechain Logo0 APE

APE Value

$0.00
Transaction Hash
Method
Block
From
To

There are no matching entries

Please try again later

Parent Transaction Hash Block From To
View All Internal Transactions

Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
SpokeMessageReceiver

Compiler Version
v0.8.23+commit.f704f362

Optimization Enabled:
Yes with 10000 runs

Other Settings:
shanghai EvmVersion
File 1 of 26 : SpokeMessageReceiver.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.23;

import {OwnableUpgradeable} from '@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol';
import {ERC20} from '@openzeppelin/contracts/token/ERC20/ERC20.sol';

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

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

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

contract SpokeMessageReceiver is SpokeStorage, OwnableUpgradeable, IMessageReceiver {
  using TypeCasts for bytes32;

  /**
   * @notice Checks that the function is called by the gateway
   */
  modifier onlyAuthorized() {
    if (msg.sender != owner() && (msg.sender != address(gateway) || paused)) {
      revert EverclearSpoke_Unauthorized();
    }
    _;
  }

  /// @inheritdoc IMessageReceiver
  function receiveMessage(
    bytes memory _message
  ) external onlyAuthorized {
    (MessageLib.MessageType _messageType, bytes memory _data) = MessageLib.parseMessage(_message);

    if (_messageType == MessageLib.MessageType.SETTLEMENT) {
      _handleBatchSettlement(_data);
    } else if (_messageType == MessageLib.MessageType.VAR_UPDATE) {
      (bytes32 _updateVariable, bytes memory _updateData) = MessageLib.parseVarUpdateMessage(_data);
      _handleVarUpdate(_updateVariable, _updateData);
    } else {
      revert EverclearSpoke_InvalidMessageType();
    }
  }

  /**
   * @notice handle a variable update message
   * @param _updateVariable The hash of the variable being updated
   * @param _updateData The data of the update
   */
  function _handleVarUpdate(bytes32 _updateVariable, bytes memory _updateData) internal {
    if (_updateVariable == Common.GATEWAY_HASH) {
      address _newGateway = MessageLib.parseAddressUpdateMessage(_updateData).toAddress();
      _updateGateway(_newGateway);
    } else if (_updateVariable == Common.MAILBOX_HASH) {
      address _newMailbox = MessageLib.parseAddressUpdateMessage(_updateData).toAddress();
      _updateMailbox(_newMailbox);
    } else if (_updateVariable == Common.LIGHTHOUSE_HASH) {
      address _newLighthouse = MessageLib.parseAddressUpdateMessage(_updateData).toAddress();
      _updateLighthouse(_newLighthouse);
    } else if (_updateVariable == Common.WATCHTOWER_HASH) {
      address _newWatchtower = MessageLib.parseAddressUpdateMessage(_updateData).toAddress();
      _updateWatchtower(_newWatchtower);
    } else {
      revert EverclearSpoke_InvalidVarUpdate();
    }
  }

  /**
   * @notice Handles a batch of settlement messages
   * @param _data The batch of settlement messages
   */
  function _handleBatchSettlement(
    bytes memory _data
  ) internal {
    Settlement[] memory _settlementMessage = MessageLib.parseSettlementBatch(_data);
    for (uint256 _i; _i < _settlementMessage.length; _i++) {
      Settlement memory _message = _settlementMessage[_i];
      _handleSettlement(_message);
    }
  }

  /**
   * @notice Handles a settlement message
   * @param _message The settlement message
   */
  function _handleSettlement(
    Settlement memory _message
  ) internal {
    address _asset = _message.asset.toAddress();
    address _recipient = _message.recipient.toAddress();

    IntentStatus _intentStatus = status[_message.intentId];
    // if already settled, ignore (shouldn't happen)
    if (_intentStatus == IntentStatus.SETTLED || _intentStatus == IntentStatus.SETTLED_AND_MANUALLY_EXECUTED) {
      return;
    }
    status[_message.intentId] = IntentStatus.SETTLED;

    uint256 _amount =
      AssetUtils.normalizeDecimals(Common.DEFAULT_NORMALIZED_DECIMALS, ERC20(_asset).decimals(), _message.amount);

    // after decimals normalization, the _amount can be 0 as result of loss of precision, check if it's > 0
    if (_amount > 0) {
      // if amount is > 0 proceed with the settlement according to the strategy
      // fetch strategy for asset
      Strategy _strategy = strategies[_asset];

      if (_strategy == Strategy.DEFAULT) {
        // default strategy
        if (_message.updateVirtualBalance) {
          balances[_message.asset][_message.recipient] += _amount;
        } else {
          // if transfer fails (eg. blacklisted recipient), increase virtual balance instead
          bytes memory _transferData = abi.encodeWithSignature('transfer(address,uint256)', _recipient, _amount);
          (bool _success, bytes memory _res) = _asset.call(_transferData);

          // doing the transfer as a low-level call to avoid reverting the whole batch if the transfer calls revert
          // applying the same checks as `SafeERC20` for the `transfer` as it can't be wrapped in a `try/catch` block
          if (!_success || (_res.length != 0 && !abi.decode(_res, (bool)))) {
            balances[_message.asset][_message.recipient] += _amount;
            emit AssetTransferFailed(_asset, _recipient, _amount);
          }
        }
      } else {
        // dedicated strategy
        ISettlementModule _module = modules[_strategy];
        address _mintRecipient = _message.updateVirtualBalance ? address(this) : _recipient;
        bool _success = _module.handleMintStrategy(_asset, _mintRecipient, _recipient, _amount, '');

        if (_success && _message.updateVirtualBalance) {
          balances[_message.asset][_message.recipient] += _amount;
        } else if (!_success) {
          emit AssetMintFailed(_asset, _recipient, _amount, _strategy);
        }
      }
    }
    emit Settled(_message.intentId, _recipient, _asset, _amount);
  }

  /**
   * @notice Update the gateway
   * @param _newGateway The new gateway address
   */
  function _updateGateway(
    address _newGateway
  ) internal validAddress(_newGateway) {
    address _oldGateway = address(gateway);
    gateway = ISpokeGateway(_newGateway);
    emit GatewayUpdated(_oldGateway, _newGateway);
  }

  /**
   * @notice Update the local mailbox address
   * @param _newMailbox The new mailbox address
   */
  function _updateMailbox(
    address _newMailbox
  ) internal {
    gateway.updateMailbox(_newMailbox);
  }

  /**
   * @notice Update the interchain security module address
   * @param _newSecurityModule The new security module address
   */
  function _updateSecurityModule(
    address _newSecurityModule
  ) internal {
    gateway.updateSecurityModule(_newSecurityModule);
  }

  /**
   * @notice Update the lighthouse address
   * @param _newLighthouse The new lighthouse address
   */
  function _updateLighthouse(
    address _newLighthouse
  ) internal validAddress(_newLighthouse) {
    address _oldLighthouse = lighthouse;
    lighthouse = _newLighthouse;
    emit LighthouseUpdated(_oldLighthouse, _newLighthouse);
  }

  /**
   * @notice Update the watchtower address
   * @param _newWatchtower The new watchtower address
   */
  function _updateWatchtower(
    address _newWatchtower
  ) internal validAddress(_newWatchtower) {
    address _oldWatchtower = watchtower;
    watchtower = _newWatchtower;

    emit WatchtowerUpdated(_oldWatchtower, _newWatchtower);
  }
}

File 2 of 26 : OwnableUpgradeable.sol
// 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);
    }
}

File 3 of 26 : ERC20.sol
// 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);
            }
        }
    }
}

File 4 of 26 : AssetUtils.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.23;

/**
 * @title AssetUtils
 * @notice Library for asset utility functions
 */
library AssetUtils {
  /**
   * @notice This function translates the _amount in _in decimals
   * to _out decimals
   *
   * @param _in The decimals of the asset in / amount in
   * @param _out The decimals of the target asset
   * @param _amount The value to normalize to the `_out` decimals
   * @return _normalized Normalized decimals.
   */
  function normalizeDecimals(uint8 _in, uint8 _out, uint256 _amount) internal pure returns (uint256 _normalized) {
    if (_in == _out) {
      return _amount;
    }
    // Convert this value to the same decimals as _out
    if (_in < _out) {
      _normalized = _amount * (10 ** (_out - _in));
    } else {
      _normalized = _amount / (10 ** (_in - _out));
    }
  }

  /**
   * @notice Get the hash of an asset
   * @param _asset The address of the asset
   * @param _domain The domain of the asset
   * @return _assetHash The hash of the asset
   */
  function getAssetHash(bytes32 _asset, uint32 _domain) internal pure returns (bytes32 _assetHash) {
    return keccak256(abi.encode(_asset, _domain));
  }
}

File 5 of 26 : Constants.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.23;

library Constants {
  // Default normalized decimals for tokens
  uint8 public constant DEFAULT_NORMALIZED_DECIMALS = 18;
  // 1/10 of a basis point denominator
  uint24 public constant DBPS_DENOMINATOR = 100_000;

  // Precomputed hashes (reduce gas costs)
  bytes32 public constant GATEWAY_HASH = keccak256(abi.encode('GATEWAY'));
  bytes32 public constant MAILBOX_HASH = keccak256(abi.encode('MAILBOX'));
  bytes32 public constant LIGHTHOUSE_HASH = keccak256(abi.encode('LIGHTHOUSE'));
  bytes32 public constant WATCHTOWER_HASH = keccak256(abi.encode('WATCHTOWER'));
  bytes32 public constant MAX_FEE_HASH = keccak256(abi.encode('MAX_FEE'));
  bytes32 public constant INTENT_TTL_HASH = keccak256(abi.encode('INTENT_TTL'));

  // Default gas limit for external calls
  uint256 public constant DEFAULT_GAS_LIMIT = 50_000;
  // Maximum calldata size for external calls
  uint256 public constant MAX_CALLDATA_SIZE = 50_000;
}

File 6 of 26 : MessageLib.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.23;

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

library MessageLib {
  /*//////////////////////////////////////////////////////////////
                            ENUMS
  //////////////////////////////////////////////////////////////*/

  /**
   * @dev Enum for message types
   * INTENT: Intent message type
   * FILL: Fill message type
   * SETTLEMENT: Settlement message type
   * VAR_UPDATE: Variable update message type
   */
  enum MessageType {
    INTENT,
    FILL,
    SETTLEMENT,
    VAR_UPDATE
  }

  /*//////////////////////////////////////////////////////////////
                      GENERAL PURPOSE FUNCTIONS
  //////////////////////////////////////////////////////////////*/

  /**
   * @dev Formats a message with a message type and data
   * @param _messageType The message type
   * @param _data The data to send in the message
   * @return _message The formatted message
   */
  function formatMessage(MessageType _messageType, bytes memory _data) internal pure returns (bytes memory _message) {
    _message = abi.encode(uint8(_messageType), _data);
  }

  /**
   * @dev Parses a message into its message type and data
   * @param _message The message to parse
   * @return _messageType The message type
   * @return _data The data in the message
   */
  function parseMessage(
    bytes memory _message
  ) internal pure returns (MessageType _messageType, bytes memory _data) {
    uint8 _msgTypeNumber;
    (_msgTypeNumber, _data) = abi.decode(_message, (uint8, bytes));
    _messageType = MessageType(_msgTypeNumber);
  }

  /*//////////////////////////////////////////////////////////////
                        MESSAGE FORMATTING
  //////////////////////////////////////////////////////////////*/

  /**
   * @dev Formats an intent message
   * @param _intents Array of intents
   * @return _message The formatted intent message
   */
  function formatIntentMessageBatch(
    IEverclear.Intent[] memory _intents
  ) internal pure returns (bytes memory _message) {
    _message = formatMessage(MessageType.INTENT, abi.encode(_intents));
  }

  /**
   * @dev Formats a fill message
   * @param _fillMessages Array of fill messages
   * @return _message The formatted fill message
   */
  function formatFillMessageBatch(
    IEverclear.FillMessage[] memory _fillMessages
  ) internal pure returns (bytes memory _message) {
    _message = formatMessage(MessageType.FILL, abi.encode(_fillMessages));
  }

  /**
   * @dev Formats a settlement message
   * @param _settlementMessages Array of settlement messages
   * @return _message The formatted settlement message
   */
  function formatSettlementBatch(
    IEverclear.Settlement[] memory _settlementMessages
  ) internal pure returns (bytes memory _message) {
    _message = formatMessage(MessageType.SETTLEMENT, abi.encode(_settlementMessages));
  }

  /**
   * @dev Formats a var update message
   * @param _data The data (encoded variable)
   * @return _message The formatted var update message
   */
  function formatVarUpdateMessage(
    bytes memory _data
  ) internal pure returns (bytes memory _message) {
    _message = formatMessage(MessageType.VAR_UPDATE, _data);
  }

  /**
   * @dev Formats an address updating message (Mailbox, SecurityModule, Gateway)
   * @param _updateVariable the name of the variable being updated
   * @param _address The new address
   * @return _message The formatted address update message
   */
  function formatAddressUpdateMessage(
    bytes32 _updateVariable,
    bytes32 _address
  ) internal pure returns (bytes memory _message) {
    _message = formatVarUpdateMessage(abi.encode(_updateVariable, abi.encode(_address)));
  }

  /**
   * @dev Formats a uint updating message (MaxRoutersFee)
   * @param _updateVariable the hashed name of the variable being updated
   * @param _value The new value
   * @return _message The formatted uint update message
   */
  function formatUintUpdateMessage(
    bytes32 _updateVariable,
    uint256 _value
  ) internal pure returns (bytes memory _message) {
    _message = formatVarUpdateMessage(abi.encode(_updateVariable, abi.encode(_value)));
  }

  /*//////////////////////////////////////////////////////////////
                          MESSAGE PARSING
  //////////////////////////////////////////////////////////////*/

  /**
   * @dev Parses an intent message
   * @param _data The intent message data
   * @return _intents Array of decoded intents
   */
  function parseIntentMessageBatch(
    bytes memory _data
  ) internal pure returns (IEverclear.Intent[] memory _intents) {
    (_intents) = abi.decode(_data, (IEverclear.Intent[]));
  }

  /**
   * @dev Parses a fill message
   * @param _data The packed fill message data
   * @return _fillMessages Array of fill messages
   */
  function parseFillMessageBatch(
    bytes memory _data
  ) internal pure returns (IEverclear.FillMessage[] memory _fillMessages) {
    (_fillMessages) = abi.decode(_data, (IEverclear.FillMessage[]));
  }

  /**
   * @dev Parses a settlement message
   * @param _data The packed settlement message data
   * @return _settlementMessages Array of settlement messages
   */
  function parseSettlementBatch(
    bytes memory _data
  ) internal pure returns (IEverclear.Settlement[] memory _settlementMessages) {
    (_settlementMessages) = abi.decode(_data, (IEverclear.Settlement[]));
  }

  /**
   * @dev Parses a var update message
   * @param _data The abi encoded variable
   * @return _updateVariable The hashed name of the variable being updated
   * @return _varData The encoded variable data
   */
  function parseVarUpdateMessage(
    bytes memory _data
  ) internal pure returns (bytes32 _updateVariable, bytes memory _varData) {
    (_updateVariable, _varData) = abi.decode(_data, (bytes32, bytes));
  }

  /**
   * @dev Parses an address update message
   * @param _data The abi encoded address
   * @return _address The decoded address
   */
  function parseAddressUpdateMessage(
    bytes memory _data
  ) internal pure returns (bytes32 _address) {
    _address = abi.decode(_data, (bytes32));
  }

  /**
   * @dev Parses a uint update message
   * @param _data The abi encoded uint
   * @return _value The decoded uint
   */
  function parseUintUpdateMessage(
    bytes memory _data
  ) internal pure returns (uint256 _value) {
    _value = abi.decode(_data, (uint256));
  }
}

File 7 of 26 : TypeCasts.sol
// SPDX-License-Identifier: MIT OR Apache-2.0
pragma solidity 0.8.23;

/**
 * @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)));
  }
}

File 8 of 26 : IMessageReceiver.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.23;

/**
 * @title IMessageReceiver
 * @notice Interface for the transport layer communication with the message receiver
 */
interface IMessageReceiver {
  /*///////////////////////////////////////////////////////////////
                              LOGIC
  //////////////////////////////////////////////////////////////*/

  /**
   * @notice Receive a message from the transport layer
   * @param _message The message to receive encoded as bytes
   * @dev This function should be called by the the gateway contract
   */
  function receiveMessage(
    bytes calldata _message
  ) external;
}

File 9 of 26 : ISettlementModule.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.23;

/**
 * @title ISettlementModule
 * @notice Interface for the base settlement module
 */
interface ISettlementModule {
  /*///////////////////////////////////////////////////////////////
                              LOGIC
  //////////////////////////////////////////////////////////////*/

  /**
   * @notice Handle a mint action for a specific strategy
   * @param _asset The address of the asset to mint
   * @param _recipient The recipient of the minted assets
   * @param _fallbackRecipient The fallback recipient of the minted assets (in case of failure)
   * @param _amount The amount to mint
   * @param _data Extra data needed by some modules
   * @return _success The outcome of the minting strategy
   * @dev In case of failure, the parent module will handle the operation accordingly
   */
  function handleMintStrategy(
    address _asset,
    address _recipient,
    address _fallbackRecipient,
    uint256 _amount,
    bytes calldata _data
  ) external returns (bool _success);

  /**
   * @notice Handle a burn action for a specific strategy
   * @param _asset The address of the asset to burn
   * @param _user The user whose assets are being burned
   * @param _amount The amount to burn
   * @param _data Extra data needed by some modules
   * @dev In case of failure, the `newIntent` flow will revert
   */
  function handleBurnStrategy(address _asset, address _user, uint256 _amount, bytes calldata _data) external;
}

File 10 of 26 : ISpokeGateway.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.23;

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

/**
 * @title ISpokeGateway
 * @notice Interface for the SpokeGateway contract, sends and receives messages to and from the transport layer
 */
interface ISpokeGateway is IGateway {
  /*///////////////////////////////////////////////////////////////
                              LOGIC
  //////////////////////////////////////////////////////////////*/

  /**
   * @notice Initialize Gateway variables
   * @param _owner The address of the owner
   * @param _mailbox The address of the local mailbox
   * @param _receiver The address of the local message receiver (EverclearSpoke)
   * @param _interchainSecurityModule The address of the chosen interchain security module
   * @param _everclearId The id of the Everclear domain
   * @param _hubGateway The bytes32 representation of the Hub gateway
   * @dev Only called once on initialization
   */
  function initialize(
    address _owner,
    address _mailbox,
    address _receiver,
    address _interchainSecurityModule,
    uint32 _everclearId,
    bytes32 _hubGateway
  ) external;

  /*///////////////////////////////////////////////////////////////
                              VIEWS
  //////////////////////////////////////////////////////////////*/

  /**
   * @notice Returns the Everclear hub chain id
   * @return _hubChainId The Everclear chain id
   */
  function EVERCLEAR_ID() external view returns (uint32 _hubChainId);

  /**
   * @notice Returns the `HubGateway` gateway address
   * @return _hubGateway The `HubGateway` address
   */
  function EVERCLEAR_GATEWAY() external view returns (bytes32 _hubGateway);
}

File 11 of 26 : SpokeStorage.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.23;

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

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

import {ISettlementModule} from 'interfaces/common/ISettlementModule.sol';
import {ICallExecutor} from 'interfaces/intent/ICallExecutor.sol';
import {ISpokeGateway} from 'interfaces/intent/ISpokeGateway.sol';
import {ISpokeStorage} from 'interfaces/intent/ISpokeStorage.sol';

/**
 * @title SpokeStorage
 * @notice Storage layout and modifiers for the `EverclearSpoke`
 */
abstract contract SpokeStorage is ISpokeStorage {
  /// @inheritdoc ISpokeStorage
  bytes32 public constant FILL_INTENT_FOR_SOLVER_TYPEHASH = keccak256(
    'function fillIntentForSolver(address _solver, Intent calldata _intent, uint256 _nonce, uint24 _fee, bytes memory _signature)'
  );

  /// @inheritdoc ISpokeStorage
  bytes32 public constant PROCESS_INTENT_QUEUE_VIA_RELAYER_TYPEHASH = keccak256(
    'function processIntentQueueViaRelayer(uint32 _domain, Intent[] memory _intents, address _relayer, uint256 _ttl, uint256 _nonce, uint256 _bufferDBPS, bytes memory _signature)'
  );

  /// @inheritdoc ISpokeStorage
  bytes32 public constant PROCESS_FILL_QUEUE_VIA_RELAYER_TYPEHASH = keccak256(
    'function processFillQueueViaRelayer(uint32 _domain, uint32 _amount, address _relayer, uint256 _ttl, uint256 _nonce, uint256 _bufferDBPS, bytes memory _signature)'
  );

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

  /// @inheritdoc ISpokeStorage
  uint32 public EVERCLEAR;

  /// @inheritdoc ISpokeStorage
  uint32 public DOMAIN;

  /// @inheritdoc ISpokeStorage
  address public lighthouse;

  /// @inheritdoc ISpokeStorage
  address public watchtower;

  /// @inheritdoc ISpokeStorage
  address public messageReceiver;

  /// @inheritdoc ISpokeStorage
  ISpokeGateway public gateway;

  /// @inheritdoc ISpokeStorage
  ICallExecutor public callExecutor;

  /// @inheritdoc ISpokeStorage
  bool public paused;

  /// @inheritdoc ISpokeStorage
  uint64 public nonce;

  /// @inheritdoc ISpokeStorage
  uint256 public messageGasLimit;

  /// @inheritdoc ISpokeStorage
  mapping(bytes32 _asset => mapping(bytes32 _user => uint256 _amount)) public balances;

  /// @inheritdoc ISpokeStorage
  mapping(bytes32 _intentId => IntentStatus status) public status;

  /// @inheritdoc ISpokeStorage
  mapping(address _asset => Strategy _strategy) public strategies;

  /// @inheritdoc ISpokeStorage
  mapping(Strategy _strategy => ISettlementModule _module) public modules;

  /**
   * @notice The intent queue
   */
  QueueLib.IntentQueue public intentQueue;

  /**
   * @notice The fill queue
   */
  QueueLib.FillQueue public fillQueue;

  /**
   * @notice Checks that the address is valid
   */
  modifier validAddress(
    address _address
  ) {
    if (_address == address(0)) {
      revert EverclearSpoke_ZeroAddress();
    }
    _;
  }

  /**
   * @notice Checks that the local domain is included in the destinations
   * @param _intent The intent to check
   */
  modifier validDestination(
    Intent calldata _intent
  ) {
    // when it's an xcall executable, destinations.length is always 1
    if (_intent.destinations[0] != DOMAIN) {
      revert EverclearSpoke_WrongDestination();
    }
    _;
  }

  /**
   * @notice Checks when processing a queue that the amount is valid for the queue being processed
   * @param _first The first index of the queue
   * @param _last The last index of the queue
   * @param _amount The amount to process
   */
  modifier validQueueAmount(uint256 _first, uint256 _last, uint256 _amount) {
    if (_amount == 0) {
      revert EverclearSpoke_ProcessQueue_ZeroAmount();
    }

    if (_first + _amount - 1 > _last) {
      revert EverclearSpoke_ProcessQueue_InvalidAmount(_first, _last, _amount);
    }

    _;
  }

  /**
   * @notice Checks that the contract is not paused
   */
  modifier whenNotPaused() {
    if (paused) {
      revert EverclearSpoke_Paused();
    }
    _;
  }

  /**
   * @notice Checks that the caller has access to pause the contract
   */
  modifier hasPauseAccess() {
    if (msg.sender != lighthouse && msg.sender != watchtower) {
      revert EverclearSpoke_Pause_NotAuthorized();
    }
    _;
  }
}

File 12 of 26 : ContextUpgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol)

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

/**
 * @dev Provides information about the current execution context, including the
 * sender of the transaction and its data. While these are generally available
 * via msg.sender and msg.data, they should not be accessed in such a direct
 * manner, since when dealing with meta-transactions the account sending and
 * paying for execution may not be the actual sender (as far as an application
 * is concerned).
 *
 * This contract is only required for intermediate, library-like contracts.
 */
abstract contract ContextUpgradeable is Initializable {
    function __Context_init() internal onlyInitializing {
    }

    function __Context_init_unchained() internal onlyInitializing {
    }
    function _msgSender() internal view virtual returns (address) {
        return msg.sender;
    }

    function _msgData() internal view virtual returns (bytes calldata) {
        return msg.data;
    }

    function _contextSuffixLength() internal view virtual returns (uint256) {
        return 0;
    }
}

File 13 of 26 : Initializable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (proxy/utils/Initializable.sol)

pragma solidity ^0.8.20;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    /**
     * @dev Returns a pointer to the storage namespace.
     */
    // solhint-disable-next-line var-name-mixedcase
    function _getInitializableStorage() private pure returns (InitializableStorage storage $) {
        assembly {
            $.slot := INITIALIZABLE_STORAGE
        }
    }
}

File 14 of 26 : IERC20.sol
// 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);
}

File 15 of 26 : IERC20Metadata.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/extensions/IERC20Metadata.sol)

pragma solidity ^0.8.20;

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

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

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

    /**
     * @dev Returns the decimals places of the token.
     */
    function decimals() external view returns (uint8);
}

File 16 of 26 : Context.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol)

pragma solidity ^0.8.20;

/**
 * @dev Provides information about the current execution context, including the
 * sender of the transaction and its data. While these are generally available
 * via msg.sender and msg.data, they should not be accessed in such a direct
 * manner, since when dealing with meta-transactions the account sending and
 * paying for execution may not be the actual sender (as far as an application
 * is concerned).
 *
 * This contract is only required for intermediate, library-like contracts.
 */
abstract contract Context {
    function _msgSender() internal view virtual returns (address) {
        return msg.sender;
    }

    function _msgData() internal view virtual returns (bytes calldata) {
        return msg.data;
    }

    function _contextSuffixLength() internal view virtual returns (uint256) {
        return 0;
    }
}

File 17 of 26 : draft-IERC6093.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/draft-IERC6093.sol)
pragma solidity ^0.8.20;

/**
 * @dev Standard ERC20 Errors
 * Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC20 tokens.
 */
interface IERC20Errors {
    /**
     * @dev Indicates an error related to the current `balance` of a `sender`. Used in transfers.
     * @param sender Address whose tokens are being transferred.
     * @param balance Current balance for the interacting account.
     * @param needed Minimum amount required to perform a transfer.
     */
    error ERC20InsufficientBalance(address sender, uint256 balance, uint256 needed);

    /**
     * @dev Indicates a failure with the token `sender`. Used in transfers.
     * @param sender Address whose tokens are being transferred.
     */
    error ERC20InvalidSender(address sender);

    /**
     * @dev Indicates a failure with the token `receiver`. Used in transfers.
     * @param receiver Address to which tokens are being transferred.
     */
    error ERC20InvalidReceiver(address receiver);

    /**
     * @dev Indicates a failure with the `spender`’s `allowance`. Used in transfers.
     * @param spender Address that may be allowed to operate on tokens without being their owner.
     * @param allowance Amount of tokens a `spender` is allowed to operate with.
     * @param needed Minimum amount required to perform a transfer.
     */
    error ERC20InsufficientAllowance(address spender, uint256 allowance, uint256 needed);

    /**
     * @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
     * @param approver Address initiating an approval operation.
     */
    error ERC20InvalidApprover(address approver);

    /**
     * @dev Indicates a failure with the `spender` to be approved. Used in approvals.
     * @param spender Address that may be allowed to operate on tokens without being their owner.
     */
    error ERC20InvalidSpender(address spender);
}

/**
 * @dev Standard ERC721 Errors
 * Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC721 tokens.
 */
interface IERC721Errors {
    /**
     * @dev Indicates that an address can't be an owner. For example, `address(0)` is a forbidden owner in EIP-20.
     * Used in balance queries.
     * @param owner Address of the current owner of a token.
     */
    error ERC721InvalidOwner(address owner);

    /**
     * @dev Indicates a `tokenId` whose `owner` is the zero address.
     * @param tokenId Identifier number of a token.
     */
    error ERC721NonexistentToken(uint256 tokenId);

    /**
     * @dev Indicates an error related to the ownership over a particular token. Used in transfers.
     * @param sender Address whose tokens are being transferred.
     * @param tokenId Identifier number of a token.
     * @param owner Address of the current owner of a token.
     */
    error ERC721IncorrectOwner(address sender, uint256 tokenId, address owner);

    /**
     * @dev Indicates a failure with the token `sender`. Used in transfers.
     * @param sender Address whose tokens are being transferred.
     */
    error ERC721InvalidSender(address sender);

    /**
     * @dev Indicates a failure with the token `receiver`. Used in transfers.
     * @param receiver Address to which tokens are being transferred.
     */
    error ERC721InvalidReceiver(address receiver);

    /**
     * @dev Indicates a failure with the `operator`’s approval. Used in transfers.
     * @param operator Address that may be allowed to operate on tokens without being their owner.
     * @param tokenId Identifier number of a token.
     */
    error ERC721InsufficientApproval(address operator, uint256 tokenId);

    /**
     * @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
     * @param approver Address initiating an approval operation.
     */
    error ERC721InvalidApprover(address approver);

    /**
     * @dev Indicates a failure with the `operator` to be approved. Used in approvals.
     * @param operator Address that may be allowed to operate on tokens without being their owner.
     */
    error ERC721InvalidOperator(address operator);
}

/**
 * @dev Standard ERC1155 Errors
 * Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC1155 tokens.
 */
interface IERC1155Errors {
    /**
     * @dev Indicates an error related to the current `balance` of a `sender`. Used in transfers.
     * @param sender Address whose tokens are being transferred.
     * @param balance Current balance for the interacting account.
     * @param needed Minimum amount required to perform a transfer.
     * @param tokenId Identifier number of a token.
     */
    error ERC1155InsufficientBalance(address sender, uint256 balance, uint256 needed, uint256 tokenId);

    /**
     * @dev Indicates a failure with the token `sender`. Used in transfers.
     * @param sender Address whose tokens are being transferred.
     */
    error ERC1155InvalidSender(address sender);

    /**
     * @dev Indicates a failure with the token `receiver`. Used in transfers.
     * @param receiver Address to which tokens are being transferred.
     */
    error ERC1155InvalidReceiver(address receiver);

    /**
     * @dev Indicates a failure with the `operator`’s approval. Used in transfers.
     * @param operator Address that may be allowed to operate on tokens without being their owner.
     * @param owner Address of the current owner of a token.
     */
    error ERC1155MissingApprovalForAll(address operator, address owner);

    /**
     * @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
     * @param approver Address initiating an approval operation.
     */
    error ERC1155InvalidApprover(address approver);

    /**
     * @dev Indicates a failure with the `operator` to be approved. Used in approvals.
     * @param operator Address that may be allowed to operate on tokens without being their owner.
     */
    error ERC1155InvalidOperator(address operator);

    /**
     * @dev Indicates an array length mismatch between ids and values in a safeBatchTransferFrom operation.
     * Used in batch transfers.
     * @param idsLength Length of the array of token identifiers
     * @param valuesLength Length of the array of token amounts
     */
    error ERC1155InvalidArrayLength(uint256 idsLength, uint256 valuesLength);
}

File 18 of 26 : IEverclear.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.23;

/**
 * @title IEverclear
 * @notice Common interface for EverclearHub and EverclearSpoke
 */
interface IEverclear {
  /*//////////////////////////////////////////////////////////////
                                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 maxFee The maximum fee that can be taken by solvers
   * @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 data The data of the intent
   */
  struct Intent {
    bytes32 initiator;
    bytes32 receiver;
    bytes32 inputAsset;
    bytes32 outputAsset;
    uint24 maxFee;
    uint32 origin;
    uint64 nonce;
    uint48 timestamp;
    uint48 ttl;
    uint256 amount;
    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 FillMessage {
    bytes32 intentId;
    bytes32 solver;
    bytes32 initiator;
    uint24 fee;
    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;
  }
}

File 19 of 26 : IGateway.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

import {IMailbox} from '@hyperlane/interfaces/IMailbox.sol';

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

interface IGateway {
  /*///////////////////////////////////////////////////////////////
                              EVENTS
  //////////////////////////////////////////////////////////////*/

  /**
   * @notice Emitted when the mailbox is updated
   * @param _oldMailbox The old mailbox address
   * @param _newMailbox The new mailbox address
   */
  event MailboxUpdated(address _oldMailbox, address _newMailbox);

  /**
   * @notice Emitted when the security module is updated
   * @param _oldSecurityModule The old security module address
   * @param _newSecurityModule The new security module address
   */
  event SecurityModuleUpdated(address _oldSecurityModule, address _newSecurityModule);

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

  /**
   * @notice Thrown when the message origin is invalid
   */
  error Gateway_Handle_InvalidOriginDomain();

  /**
   * @notice Thrown when the sender is not the appropriate remote Gateway
   */
  error Gateway_Handle_InvalidSender();

  /**
   * @notice Thrown when the caller is not the local mailbox
   */
  error Gateway_Handle_NotCalledByMailbox();

  /**
   * @notice Thrown when the GasTank does not have enough native asset to cover the fee
   */
  error Gateway_SendMessage_InsufficientBalance();

  /**
   * @notice Thrown when the message dispatcher is not the local receiver
   */
  error Gateway_SendMessage_UnauthorizedCaller();

  /**
   * @notice Thrown when the call returning the unused fee fails
   */
  error Gateway_SendMessage_UnsuccessfulRebate();

  /**
   * @notice Thrown when an address equals the address zero
   */
  error Gateway_ZeroAddress();

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

  /**
   * @notice Send a message to the transport layer using the gas tank
   * @param _chainId The id of the destination chain
   * @param _message The message to send
   * @param _fee The fee to send the message
   * @param _gasLimit The gas limit to use on destination
   * @return _messageId The id message of the transport layer
   * @return _feeSpent The fee spent to send the message
   * @dev only called by the spoke contract
   */
  function sendMessage(
    uint32 _chainId,
    bytes memory _message,
    uint256 _fee,
    uint256 _gasLimit
  ) external returns (bytes32 _messageId, uint256 _feeSpent);

  /**
   * @notice Send a message to the transport layer
   * @param _chainId The id of the destination chain
   * @param _message The message to send
   * @param _gasLimit The gas limit to use on destination
   * @return _messageId The id message of the transport layer
   * @return _feeSpent The fee spent to send the message
   * @dev only called by the spoke contract
   */
  function sendMessage(
    uint32 _chainId,
    bytes memory _message,
    uint256 _gasLimit
  ) external payable returns (bytes32 _messageId, uint256 _feeSpent);

  /**
   * @notice Updates the mailbox
   * @param _mailbox The new mailbox address
   * @dev only called by the `receiver`
   */
  function updateMailbox(
    address _mailbox
  ) external;

  /**
   * @notice Updates the gateway security module
   * @param _securityModule The address of the new security module
   * @dev only called by the `receiver`
   */
  function updateSecurityModule(
    address _securityModule
  ) external;

  /*///////////////////////////////////////////////////////////////
                              VIEWS
  //////////////////////////////////////////////////////////////*/

  /**
   * @notice Returns the transport layer message routing smart contract
   * @dev this is independent of the transport layer used, adopting mailbox name because its descriptive enough
   *      using address instead of specific interface to be independent from HL or any other TL
   * @return _mailbox The mailbox contract
   */
  function mailbox() external view returns (IMailbox _mailbox);

  /**
   * @notice Returns the message receiver for this Gateway (EverclearHub / EverclearSpoke)
   * @return _receiver The message receiver
   */
  function receiver() external view returns (IMessageReceiver _receiver);

  /**
   * @notice Quotes cost of sending a message to the transport layer
   * @param _chainId The id of the destination chain
   * @param _message The message to send
   * @param _gasLimit The gas limit for delivering the message
   * @return _fee The fee to send the message
   */
  function quoteMessage(uint32 _chainId, bytes memory _message, uint256 _gasLimit) external view returns (uint256 _fee);
}

File 20 of 26 : QueueLib.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.23;

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

/**
 * @title QueueLib
 * @notice Library for managing queues
 */
library QueueLib {
  /**
   * @notice Structure for the IntentQueue
   * @dev first should always be initialized to 1
   * @param first The first position in the queue
   * @param last The last position in the queue
   * @param queue The queue of intent ids
   */
  struct IntentQueue {
    uint256 first;
    uint256 last;
    mapping(uint256 _position => bytes32 _intentId) queue;
  }

  /**
   * @notice Structure for the FillQueue
   * @dev Member first should always be initialized to 1
   * @param first The first position in the queue
   * @param last The last position in the queue
   * @param queue The queue of fill messages
   */
  struct FillQueue {
    uint256 first;
    uint256 last;
    mapping(uint256 _position => IEverclear.FillMessage _fillMessage) queue;
  }

  /**
   * @notice Thrown when the queue is empty
   */
  error Queue_EmptyQueue();

  /**
   * @notice Enqueue an intent id to the IntentQueue
   * @param _queue The IntentQueue
   * @param _intentId The intent id to enqueue
   */
  function enqueueIntent(IntentQueue storage _queue, bytes32 _intentId) internal {
    _queue.last += 1;
    _queue.queue[_queue.last] = _intentId;
  }

  /**
   * @notice Enqueue a fill message to the FillQueue
   * @param _queue The FillQueue
   * @param _fillMessage The fill message to enqueue
   */
  function enqueueFill(FillQueue storage _queue, IEverclear.FillMessage memory _fillMessage) internal {
    _queue.last += 1;
    _queue.queue[_queue.last] = _fillMessage;
  }

  /**
   * @notice Dequeue an intent id from the IntentQueue
   * @param _queue The IntentQueue
   * @return _intentId The dequeued intent id
   */
  function dequeueIntent(
    IntentQueue storage _queue
  ) internal returns (bytes32 _intentId) {
    // non-empty queue check
    if (_queue.last < _queue.first) revert Queue_EmptyQueue();

    _intentId = _queue.queue[_queue.first];

    delete _queue.queue[_queue.first];
    _queue.first += 1;
  }

  /**
   * @notice Dequeue a fill message from the FillQueue
   * @param _queue The FillQueue
   * @return _fillMessage The dequeued fill message
   */
  function dequeueFill(
    FillQueue storage _queue
  ) internal returns (IEverclear.FillMessage memory _fillMessage) {
    // non-empty queue
    if (_queue.last < _queue.first) revert Queue_EmptyQueue();

    _fillMessage = _queue.queue[_queue.first];

    delete _queue.queue[_queue.first];
    _queue.first += 1;
  }
}

File 21 of 26 : IPermit2.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.23;

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;
}

File 22 of 26 : ICallExecutor.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.23;

/**
 * @title ICallExecutor
 * @notice Interface for the CallExecutor contract, executes calls to external contracts
 */
interface ICallExecutor {
  /**
   * @notice Safely call a target contract, use when you _really_ really _really_ don't trust the called
   * contract. This prevents the called contract from causing reversion of the caller in as many ways as we can.
   * @param _target The address to call
   * @param _gas The amount of gas to forward to the remote contract
   * @param _value The value in wei to send to the remote contract
   * @param _maxCopy The maximum number of bytes of returndata to copy to memory
   * @param _calldata The data to send to the remote contract
   * @return _success Whether the call was successful
   * @return _returnData Returndata as `.call()`. Returndata is capped to `_maxCopy` bytes.
   */
  function excessivelySafeCall(
    address _target,
    uint256 _gas,
    uint256 _value,
    uint16 _maxCopy,
    bytes memory _calldata
  ) external returns (bool _success, bytes memory _returnData);
}

File 23 of 26 : ISpokeStorage.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.23;

import {IEverclear} from 'interfaces/common/IEverclear.sol';
import {IPermit2} from 'interfaces/common/IPermit2.sol';

import {ISettlementModule} from 'interfaces/common/ISettlementModule.sol';
import {ICallExecutor} from 'interfaces/intent/ICallExecutor.sol';
import {ISpokeGateway} from 'interfaces/intent/ISpokeGateway.sol';

/**
 * @title ISpokeStorage
 * @notice Interface for the SpokeStorage contract
 */
interface ISpokeStorage is IEverclear {
  /*///////////////////////////////////////////////////////////////
                              STRUCTS
  //////////////////////////////////////////////////////////////*/

  /**
   * @notice Parameters needed to initiliaze `EverclearSpoke`
   * @param gateway The local `SpokeGateway`
   * @param callExecutor The local `CallExecutor`
   * @param messageReceiver The address for the `SpokeMessageReceiver` module
   * @param lighthouse The address for the Lighthouse agent
   * @param watchtower The address for the Watchtower agent
   * @param hubDomain The chain id for the Everclear domain
   * @param owner The initial owner of the contract
   */
  struct SpokeInitializationParams {
    ISpokeGateway gateway;
    ICallExecutor callExecutor;
    address messageReceiver;
    address lighthouse;
    address watchtower;
    uint32 hubDomain;
    address owner;
  }

  /*///////////////////////////////////////////////////////////////
                              EVENTS
  //////////////////////////////////////////////////////////////*/

  /**
   * @notice emitted when the Gateway address is updated
   * @param _oldGateway The address of the old gateway
   * @param _newGateway The address of the new gateway
   */
  event GatewayUpdated(address _oldGateway, address _newGateway);

  /**
   * @notice emitted when the Lighthouse address is updated
   * @param _oldLightHouse The address of the old lighthouse
   * @param _newLightHouse The address of the new lighthouse
   */
  event LighthouseUpdated(address _oldLightHouse, address _newLightHouse);

  /**
   * @notice emitted when the Watchtower address is updated
   * @param _oldWatchtower The address of the old watchtower
   * @param _newWatchtower The address of the new watchtower
   */
  event WatchtowerUpdated(address _oldWatchtower, address _newWatchtower);

  /**
   * @notice emitted when the MessageReceiver address is updated
   * @param _oldMessageReceiver The address of the old message receiver
   * @param _newMessageReceiver The address of the new message receiver
   */
  event MessageReceiverUpdated(address _oldMessageReceiver, address _newMessageReceiver);

  /**
   * @notice emitted when messageGasLimit is updated
   * @param _oldGasLimit The old gas limit
   * @param _newGasLimit The new gas limit
   */
  event MessageGasLimitUpdated(uint256 _oldGasLimit, uint256 _newGasLimit);

  /**
   * @notice emitted when the protocol is paused (domain-level)
   */
  event Paused();

  /**
   * @notice emitted when the protocol is paused (domain-level)
   */
  event Unpaused();

  /**
   * @notice emitted when a strategy is set for an asset
   * @param _asset The address of the asset being configured
   * @param _strategy The id for the strategy (see `enum Strategy`)
   */
  event StrategySetForAsset(address _asset, IEverclear.Strategy _strategy);

  /**
   * @notice emitted when the module is set for a strategy
   * @param _strategy The id for the strategy (see `enum Strategy`)
   * @param _module The settlement module
   */
  event ModuleSetForStrategy(IEverclear.Strategy _strategy, ISettlementModule _module);

  /**
   * @notice emitted when the EverclearSpoke processes a settlement
   * @param _intentId The ID of the intent
   * @param _account The address of the account
   * @param _asset The address of the asset
   * @param _amount The amount of the asset
   */
  event Settled(bytes32 indexed _intentId, address _account, address _asset, uint256 _amount);

  /**
   * @notice emitted when `_handleSettlement` fails to transfer tokens to a user (eg. blacklisted recipient)
   * @param _asset The address of the asset
   * @param _recipient The address of the recipient
   * @param _amount The amount of the asset
   */
  event AssetTransferFailed(address indexed _asset, address indexed _recipient, uint256 _amount);

  /**
   * @notice emitted when `_handleSettlement` fails to mint the non-default stategy asset
   * @param _asset The address of the asset
   * @param _recipient The address of the recipient
   * @param _amount The amount of the asset
   * @param _strategy The strategy used for the asset
   */
  event AssetMintFailed(address indexed _asset, address indexed _recipient, uint256 _amount, Strategy _strategy);

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

  /**
   * @notice Thrown when the spoke is receiving a message from an address that is not the authorized gateway, admin or owner
   */
  error EverclearSpoke_Unauthorized();

  /**
   * @notice Thrown when a message is not a valid message type
   */
  error EverclearSpoke_InvalidMessageType();

  /**
   * @notice Thrown when the destination is wrong
   */
  error EverclearSpoke_WrongDestination();

  /**
   * @notice Thrown when a variable update is invalid
   */
  error EverclearSpoke_InvalidVarUpdate();

  /**
   * @notice Thrown when calling to a processQueue method with a zero amount
   */
  error EverclearSpoke_ProcessQueue_ZeroAmount();

  /**
   * @notice Thrown when calling to a processQueue method with an invalid amount
   * @param _first The index of the first element of the queue
   * @param _last The index of the last element of the queue
   * @param _amount The amount of items being tried to process
   */
  error EverclearSpoke_ProcessQueue_InvalidAmount(uint256 _first, uint256 _last, uint256 _amount);

  /**
   * @notice Thrown when calling a function with the zero address
   */
  error EverclearSpoke_ZeroAddress();

  /**
   * @notice Thrown when a function is called when the spoke is paused
   */
  error EverclearSpoke_Paused();

  /**
   * @notice Thrown when the caller is not authorized to pause the spoke
   */
  error EverclearSpoke_Pause_NotAuthorized();

  /*///////////////////////////////////////////////////////////////
                              VIEWS
  //////////////////////////////////////////////////////////////*/

  /**
   * @notice returns the typehash for `fillIntentForSolver`
   * @return _typeHash The `fillIntentForSolver` type hash
   */
  function FILL_INTENT_FOR_SOLVER_TYPEHASH() external view returns (bytes32 _typeHash);

  /**
   * @notice returns the typehash for `processIntentQueueViaRelayer`
   * @return _typeHash The `processIntentQueueViaRelayer` type hash
   */
  function PROCESS_INTENT_QUEUE_VIA_RELAYER_TYPEHASH() external view returns (bytes32 _typeHash);

  /**
   * @notice returns the typehash for `processFillQueueViaRelayer`
   * @return _typeHash The `processFillQueueViaRelayer` type hash
   */
  function PROCESS_FILL_QUEUE_VIA_RELAYER_TYPEHASH() external view returns (bytes32 _typeHash);

  /**
   * @notice returns the permit2 contract
   * @return _permit2 The Permit2 singleton address
   */
  function PERMIT2() external view returns (IPermit2 _permit2);

  /**
   * @notice returns the domain id for the Everclear rollup
   * @return _domain The id of the Everclear domain
   */
  function EVERCLEAR() external view returns (uint32 _domain);

  /**
   * @notice returns the current domain
   * @return _domain The id of the current domain
   */
  function DOMAIN() external view returns (uint32 _domain);

  /**
   * @notice returns the lighthouse address
   * @return _lighthouse The address of the Lighthouse agent
   */
  function lighthouse() external view returns (address _lighthouse);

  /**
   * @notice returns the watchtower address
   * @return _watchtower The address of the Watchtower agent
   */
  function watchtower() external view returns (address _watchtower);

  /**
   * @notice returns the message receiver address
   * @return _messageReceiver The address of the `SpokeMessageReceiver`
   */
  function messageReceiver() external view returns (address _messageReceiver);

  /**
   * @notice returns the gateway
   * @return _gateway The local `SpokeGateway`
   */
  function gateway() external view returns (ISpokeGateway _gateway);

  /**
   * @notice returns the call executor
   * @return _callExecutor The local `CallExecutor`
   */
  function callExecutor() external view returns (ICallExecutor _callExecutor);

  /**
   * @notice returns the paused status of the spoke
   * @return _paused The boolean indicating if the contract is paused
   */
  function paused() external view returns (bool _paused);

  /**
   * @notice returns the current intent nonce
   * @return _nonce The current nonce
   */
  function nonce() external view returns (uint64 _nonce);

  /**
   * @notice returns the gas limit used for outgoing messages
   * @return _messageGasLimit the max gas limit
   */
  function messageGasLimit() external view returns (uint256 _messageGasLimit);

  /**
   * @notice returns the balance of an asset for a user
   * @param _asset The address of the asset
   * @param _user The address of the user
   * @return _amount The amount of assets locked in the contract
   */
  function balances(bytes32 _asset, bytes32 _user) external view returns (uint256 _amount);

  /**
   * @notice returns the status of an intent
   * @param _intentId The ID of the intent
   * @return _status The status of the intent
   */
  function status(
    bytes32 _intentId
  ) external view returns (IntentStatus _status);

  /**
   * @notice returns the configured strategy id for an asset
   * @param _asset The address of the asset
   * @return _strategy The strategy for the asset
   */
  function strategies(
    address _asset
  ) external view returns (IEverclear.Strategy _strategy);

  /**
   * @notice returns the module address for a strategy
   * @param _strategy The strategy id
   * @return _module The strategy module
   */
  function modules(
    IEverclear.Strategy _strategy
  ) external view returns (ISettlementModule _module);
}

File 24 of 26 : IMailbox.sol
// SPDX-License-Identifier: MIT OR Apache-2.0
pragma solidity >=0.8.0;

import {IInterchainSecurityModule} from "./IInterchainSecurityModule.sol";
import {IPostDispatchHook} from "./hooks/IPostDispatchHook.sol";

interface IMailbox {
    // ============ Events ============
    /**
     * @notice Emitted when a new message is dispatched via Hyperlane
     * @param sender The address that dispatched the message
     * @param destination The destination domain of the message
     * @param recipient The message recipient address on `destination`
     * @param message Raw bytes of message
     */
    event Dispatch(
        address indexed sender,
        uint32 indexed destination,
        bytes32 indexed recipient,
        bytes message
    );

    /**
     * @notice Emitted when a new message is dispatched via Hyperlane
     * @param messageId The unique message identifier
     */
    event DispatchId(bytes32 indexed messageId);

    /**
     * @notice Emitted when a Hyperlane message is processed
     * @param messageId The unique message identifier
     */
    event ProcessId(bytes32 indexed messageId);

    /**
     * @notice Emitted when a Hyperlane message is delivered
     * @param origin The origin domain of the message
     * @param sender The message sender address on `origin`
     * @param recipient The address that handled the message
     */
    event Process(
        uint32 indexed origin,
        bytes32 indexed sender,
        address indexed recipient
    );

    function localDomain() external view returns (uint32);

    function delivered(bytes32 messageId) external view returns (bool);

    function defaultIsm() external view returns (IInterchainSecurityModule);

    function defaultHook() external view returns (IPostDispatchHook);

    function requiredHook() external view returns (IPostDispatchHook);

    function latestDispatchedId() external view returns (bytes32);

    function dispatch(
        uint32 destinationDomain,
        bytes32 recipientAddress,
        bytes calldata messageBody
    ) external payable returns (bytes32 messageId);

    function quoteDispatch(
        uint32 destinationDomain,
        bytes32 recipientAddress,
        bytes calldata messageBody
    ) external view returns (uint256 fee);

    function dispatch(
        uint32 destinationDomain,
        bytes32 recipientAddress,
        bytes calldata body,
        bytes calldata defaultHookMetadata
    ) external payable returns (bytes32 messageId);

    function quoteDispatch(
        uint32 destinationDomain,
        bytes32 recipientAddress,
        bytes calldata messageBody,
        bytes calldata defaultHookMetadata
    ) external view returns (uint256 fee);

    function dispatch(
        uint32 destinationDomain,
        bytes32 recipientAddress,
        bytes calldata body,
        bytes calldata customHookMetadata,
        IPostDispatchHook customHook
    ) external payable returns (bytes32 messageId);

    function quoteDispatch(
        uint32 destinationDomain,
        bytes32 recipientAddress,
        bytes calldata messageBody,
        bytes calldata customHookMetadata,
        IPostDispatchHook customHook
    ) external view returns (uint256 fee);

    function process(
        bytes calldata metadata,
        bytes calldata message
    ) external payable;

    function recipientIsm(
        address recipient
    ) external view returns (IInterchainSecurityModule module);
}

File 25 of 26 : IInterchainSecurityModule.sol
// SPDX-License-Identifier: MIT OR Apache-2.0
pragma solidity >=0.6.11;

interface IInterchainSecurityModule {
    enum Types {
        UNUSED,
        ROUTING,
        AGGREGATION,
        LEGACY_MULTISIG,
        MERKLE_ROOT_MULTISIG,
        MESSAGE_ID_MULTISIG,
        NULL, // used with relayer carrying no metadata
        CCIP_READ
    }

    /**
     * @notice Returns an enum that represents the type of security model
     * encoded by this ISM.
     * @dev Relayers infer how to fetch and format metadata.
     */
    function moduleType() external view returns (uint8);

    /**
     * @notice Defines a security model responsible for verifying interchain
     * messages based on the provided metadata.
     * @param _metadata Off-chain metadata provided by a relayer, specific to
     * the security model encoded by the module (e.g. validator signatures)
     * @param _message Hyperlane encoded interchain message
     * @return True if the message was verified
     */
    function verify(
        bytes calldata _metadata,
        bytes calldata _message
    ) external returns (bool);
}

interface ISpecifiesInterchainSecurityModule {
    function interchainSecurityModule()
        external
        view
        returns (IInterchainSecurityModule);
}

File 26 of 26 : IPostDispatchHook.sol
// SPDX-License-Identifier: MIT OR Apache-2.0
pragma solidity >=0.8.0;

/*@@@@@@@       @@@@@@@@@
 @@@@@@@@@       @@@@@@@@@
  @@@@@@@@@       @@@@@@@@@
   @@@@@@@@@       @@@@@@@@@
    @@@@@@@@@@@@@@@@@@@@@@@@@
     @@@@@  HYPERLANE  @@@@@@@
    @@@@@@@@@@@@@@@@@@@@@@@@@
   @@@@@@@@@       @@@@@@@@@
  @@@@@@@@@       @@@@@@@@@
 @@@@@@@@@       @@@@@@@@@
@@@@@@@@@       @@@@@@@@*/

interface IPostDispatchHook {
    enum Types {
        UNUSED,
        ROUTING,
        AGGREGATION,
        MERKLE_TREE,
        INTERCHAIN_GAS_PAYMASTER,
        FALLBACK_ROUTING,
        ID_AUTH_ISM,
        PAUSABLE,
        PROTOCOL_FEE,
        LAYER_ZERO_V1
    }

    /**
     * @notice Returns an enum that represents the type of hook
     */
    function hookType() external view returns (uint8);

    /**
     * @notice Returns whether the hook supports metadata
     * @param metadata metadata
     * @return Whether the hook supports metadata
     */
    function supportsMetadata(
        bytes calldata metadata
    ) external view returns (bool);

    /**
     * @notice Post action after a message is dispatched via the Mailbox
     * @param metadata The metadata required for the hook
     * @param message The message passed from the Mailbox.dispatch() call
     */
    function postDispatch(
        bytes calldata metadata,
        bytes calldata message
    ) external payable;

    /**
     * @notice Compute the payment required by the postDispatch call
     * @param metadata The metadata required for the hook
     * @param message The message passed from the Mailbox.dispatch() call
     * @return Quoted payment for the postDispatch call
     */
    function quoteDispatch(
        bytes calldata metadata,
        bytes calldata message
    ) external view returns (uint256);
}

Settings
{
  "remappings": [
    "ds-test/=../../node_modules/ds-test/src/",
    "forge-std/=../../node_modules/forge-std/src/",
    "isolmate/=../../node_modules/isolmate/src/",
    "@hyperlane/=../../node_modules/@hyperlane-xyz/core/contracts/",
    "@openzeppelin/=../../node_modules/@openzeppelin/",
    "@upgrades/=lib/openzeppelin-foundry-upgrades/src/",
    "xerc20/=lib/xerc20/solidity/contracts/",
    "contracts/=src/contracts/",
    "interfaces/=src/interfaces/",
    "utils/=script/utils/",
    "@eth-optimism/=../../node_modules/@eth-optimism/",
    "@hyperlane-xyz/=../../node_modules/@hyperlane-xyz/",
    "@layerzerolabs/=../../node_modules/@layerzerolabs/",
    "erc4626-tests/=lib/xerc20/lib/openzeppelin-contracts/lib/erc4626-tests/",
    "forge-gas-snapshot/=lib/xerc20/lib/permit2/lib/forge-gas-snapshot/src/",
    "fx-portal/=../../node_modules/fx-portal/",
    "openzeppelin-contracts/=lib/xerc20/lib/openzeppelin-contracts/",
    "openzeppelin-foundry-upgrades/=lib/openzeppelin-foundry-upgrades/src/",
    "openzeppelin/=lib/xerc20/lib/openzeppelin-contracts/contracts/",
    "permit2/=lib/xerc20/lib/permit2/",
    "prb-test/=lib/xerc20/lib/prb-test/src/",
    "prb/test/=lib/xerc20/lib/prb-test/src/",
    "solidity-stringutils/=lib/openzeppelin-foundry-upgrades/lib/solidity-stringutils/",
    "solmate/=lib/xerc20/lib/permit2/lib/solmate/"
  ],
  "optimizer": {
    "enabled": true,
    "runs": 10000
  },
  "metadata": {
    "useLiteralContent": false,
    "bytecodeHash": "ipfs",
    "appendCBOR": true
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "evmVersion": "shanghai",
  "viaIR": false,
  "libraries": {}
}

Contract Security Audit

Contract ABI

[{"inputs":[],"name":"EverclearSpoke_InvalidMessageType","type":"error"},{"inputs":[],"name":"EverclearSpoke_InvalidVarUpdate","type":"error"},{"inputs":[],"name":"EverclearSpoke_Pause_NotAuthorized","type":"error"},{"inputs":[],"name":"EverclearSpoke_Paused","type":"error"},{"inputs":[{"internalType":"uint256","name":"_first","type":"uint256"},{"internalType":"uint256","name":"_last","type":"uint256"},{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"EverclearSpoke_ProcessQueue_InvalidAmount","type":"error"},{"inputs":[],"name":"EverclearSpoke_ProcessQueue_ZeroAmount","type":"error"},{"inputs":[],"name":"EverclearSpoke_Unauthorized","type":"error"},{"inputs":[],"name":"EverclearSpoke_WrongDestination","type":"error"},{"inputs":[],"name":"EverclearSpoke_ZeroAddress","type":"error"},{"inputs":[],"name":"InvalidInitialization","type":"error"},{"inputs":[],"name":"NotInitializing","type":"error"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"OwnableInvalidOwner","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"OwnableUnauthorizedAccount","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_asset","type":"address"},{"indexed":true,"internalType":"address","name":"_recipient","type":"address"},{"indexed":false,"internalType":"uint256","name":"_amount","type":"uint256"},{"indexed":false,"internalType":"enum IEverclear.Strategy","name":"_strategy","type":"uint8"}],"name":"AssetMintFailed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_asset","type":"address"},{"indexed":true,"internalType":"address","name":"_recipient","type":"address"},{"indexed":false,"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"AssetTransferFailed","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"_oldGateway","type":"address"},{"indexed":false,"internalType":"address","name":"_newGateway","type":"address"}],"name":"GatewayUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint64","name":"version","type":"uint64"}],"name":"Initialized","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"_oldLightHouse","type":"address"},{"indexed":false,"internalType":"address","name":"_newLightHouse","type":"address"}],"name":"LighthouseUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"_oldGasLimit","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_newGasLimit","type":"uint256"}],"name":"MessageGasLimitUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"_oldMessageReceiver","type":"address"},{"indexed":false,"internalType":"address","name":"_newMessageReceiver","type":"address"}],"name":"MessageReceiverUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"enum IEverclear.Strategy","name":"_strategy","type":"uint8"},{"indexed":false,"internalType":"contract ISettlementModule","name":"_module","type":"address"}],"name":"ModuleSetForStrategy","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[],"name":"Paused","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"_intentId","type":"bytes32"},{"indexed":false,"internalType":"address","name":"_account","type":"address"},{"indexed":false,"internalType":"address","name":"_asset","type":"address"},{"indexed":false,"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"Settled","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"_asset","type":"address"},{"indexed":false,"internalType":"enum IEverclear.Strategy","name":"_strategy","type":"uint8"}],"name":"StrategySetForAsset","type":"event"},{"anonymous":false,"inputs":[],"name":"Unpaused","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"_oldWatchtower","type":"address"},{"indexed":false,"internalType":"address","name":"_newWatchtower","type":"address"}],"name":"WatchtowerUpdated","type":"event"},{"inputs":[],"name":"DOMAIN","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"EVERCLEAR","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"FILL_INTENT_FOR_SOLVER_TYPEHASH","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"PERMIT2","outputs":[{"internalType":"contract IPermit2","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"PROCESS_FILL_QUEUE_VIA_RELAYER_TYPEHASH","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"PROCESS_INTENT_QUEUE_VIA_RELAYER_TYPEHASH","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"_asset","type":"bytes32"},{"internalType":"bytes32","name":"_user","type":"bytes32"}],"name":"balances","outputs":[{"internalType":"uint256","name":"_amount","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"callExecutor","outputs":[{"internalType":"contract ICallExecutor","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"fillQueue","outputs":[{"internalType":"uint256","name":"first","type":"uint256"},{"internalType":"uint256","name":"last","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"gateway","outputs":[{"internalType":"contract ISpokeGateway","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"intentQueue","outputs":[{"internalType":"uint256","name":"first","type":"uint256"},{"internalType":"uint256","name":"last","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lighthouse","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"messageGasLimit","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"messageReceiver","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"enum IEverclear.Strategy","name":"_strategy","type":"uint8"}],"name":"modules","outputs":[{"internalType":"contract ISettlementModule","name":"_module","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"nonce","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"paused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes","name":"_message","type":"bytes"}],"name":"receiveMessage","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"_intentId","type":"bytes32"}],"name":"status","outputs":[{"internalType":"enum IEverclear.IntentStatus","name":"status","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_asset","type":"address"}],"name":"strategies","outputs":[{"internalType":"enum IEverclear.Strategy","name":"_strategy","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"watchtower","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"}]

608060405234801561000f575f80fd5b50611b2a8061001d5f395ff3fe608060405234801561000f575f80fd5b506004361061018f575f3560e01c80638da5cb5b116100dd578063d6979ef611610088578063f2fde38b11610063578063f2fde38b146104e6578063f37c0a2e146104f9578063f953cec714610508575f80fd5b8063d6979ef614610491578063df7a76011461049f578063e2cae9f5146104c6575f80fd5b8063affed0e0116100b8578063affed0e01461040d578063c2aeb7bd14610453578063cab0071e14610488575f80fd5b80638da5cb5b1461038d5780638fd68940146103ca578063990969f6146103ed575f80fd5b80635c975abb1161013d578063715018a611610118578063715018a61461033c57806373e554f6146103465780637f7673f91461036d575f80fd5b80635c975abb146102c65780636301fd63146102fb5780636afdd85014610326575f80fd5b8063522dbaa51161016d578063522dbaa51461024457806352a9674b1461026b57806352ad0d5e14610297575f80fd5b8063043dca0414610193578063116191b6146101d057806339ebf82314610215575b5f80fd5b6101bd6101a1366004611435565b600660209081525f928352604080842090915290825290205481565b6040519081526020015b60405180910390f35b6003546101f09073ffffffffffffffffffffffffffffffffffffffff1681565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020016101c7565b610237610223366004611455565b60086020525f908152604090205460ff1681565b6040516101c791906114c9565b6101bd7f0afae807991f914b71165fd92589f1dc28648cb9fb1f8558f3a6c7507d56deff81565b5f5461028290640100000000900463ffffffff1681565b60405163ffffffff90911681526020016101c7565b6102b96102a53660046114d7565b60076020525f908152604090205460ff1681565b6040516101c791906114ee565b6004546102eb9074010000000000000000000000000000000000000000900460ff1681565b60405190151581526020016101c7565b5f546101f09068010000000000000000900473ffffffffffffffffffffffffffffffffffffffff1681565b6101f06e22d473030f116ddee9f6b43ac78ba381565b61034461051b565b005b6101bd7f8104c8a42e1531612796e696e327ea52a475d9583ee6d64ffdefcafad22c0b2481565b6001546101f09073ffffffffffffffffffffffffffffffffffffffff1681565b7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c1993005473ffffffffffffffffffffffffffffffffffffffff166101f0565b600a54600b546103d8919082565b604080519283526020830191909152016101c7565b6004546101f09073ffffffffffffffffffffffffffffffffffffffff1681565b60045461043a907501000000000000000000000000000000000000000000900467ffffffffffffffff1681565b60405167ffffffffffffffff90911681526020016101c7565b6101f0610461366004611508565b60096020525f908152604090205473ffffffffffffffffffffffffffffffffffffffff1681565b6101bd60055481565b600d54600e546103d8919082565b6101bd7fce47b2c080dbcd8e420dc92bcf58f25f73f2e008a3b34ac41b9d468d1fb45d5a81565b6002546101f09073ffffffffffffffffffffffffffffffffffffffff1681565b6103446104f4366004611455565b61052e565b5f546102829063ffffffff1681565b610344610516366004611610565b610596565b6105236106f2565b61052c5f610780565b565b6105366106f2565b73ffffffffffffffffffffffffffffffffffffffff811661058a576040517f1e4fbdf70000000000000000000000000000000000000000000000000000000081525f60048201526024015b60405180910390fd5b61059381610780565b50565b7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c1993005473ffffffffffffffffffffffffffffffffffffffff163314801590610619575060035473ffffffffffffffffffffffffffffffffffffffff1633141580610619575060045474010000000000000000000000000000000000000000900460ff165b15610650576040517fa756e44800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f8061065b83610815565b9092509050600282600381111561067457610674611488565b03610687576106828161084e565b505050565b600382600381111561069b5761069b611488565b036106c0575f806106ab83610894565b915091506106b982826108b5565b5050505050565b6040517f0a88c33900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b336107317f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c1993005473ffffffffffffffffffffffffffffffffffffffff1690565b73ffffffffffffffffffffffffffffffffffffffff161461052c576040517f118cdaa7000000000000000000000000000000000000000000000000000000008152336004820152602401610581565b7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c19930080547fffffffffffffffffffffffff0000000000000000000000000000000000000000811673ffffffffffffffffffffffffffffffffffffffff848116918217845560405192169182907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0905f90a3505050565b5f60605f8380602001905181019061082d919061170b565b9250905060ff8116600381111561084657610846611488565b925050915091565b5f61085882610abe565b90505f5b8151811015610682575f82828151811061087857610878611756565b6020026020010151905061088b81610ada565b5060010161085c565b5f6060828060200190518101906108ab9190611783565b9094909350915050565b6040516020016108f69060208082526007908201527f4741544557415900000000000000000000000000000000000000000000000000604082015260600190565b60405160208183030381529060405280519060200120820361092d575f61092261091f836110b3565b90565b9050610682816110c8565b60405160200161096e9060208082526007908201527f4d41494c424f5800000000000000000000000000000000000000000000000000604082015260600190565b6040516020818303038152906040528051906020012082036109a2575f61099761091f836110b3565b90506106828161119e565b6040516020016109e3906020808252600a908201527f4c49474854484f55534500000000000000000000000000000000000000000000604082015260600190565b604051602081830303815290604052805190602001208203610a17575f610a0c61091f836110b3565b90506106828161121a565b604051602001610a58906020808252600a908201527f5741544348544f57455200000000000000000000000000000000000000000000604082015260600190565b604051602081830303815290604052805190602001208203610a8c575f610a8161091f836110b3565b9050610682816112f6565b6040517f12109f2800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b606081806020019051810190610ad491906117c0565b92915050565b5f610ae6826040015190565b90505f610af4836060015190565b83515f9081526007602052604090205490915060ff166006816009811115610b1e57610b1e611488565b1480610b3b57506007816009811115610b3957610b39611488565b145b15610b465750505050565b83515f9081526007602052604090208054600691907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660018302179055505f610c0260128573ffffffffffffffffffffffffffffffffffffffff1663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa158015610bd4573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610bf891906118a8565b87602001516113c3565b905080156110525773ffffffffffffffffffffffffffffffffffffffff84165f9081526008602052604081205460ff1690816001811115610c4557610c45611488565b03610e7357856080015115610c8f576040808701515f9081526006602090815282822060608a0151835290529081208054849290610c849084906118ee565b909155506110509050565b60405173ffffffffffffffffffffffffffffffffffffffff85166024820152604481018390525f90606401604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fa9059cbb00000000000000000000000000000000000000000000000000000000179052519091505f90819073ffffffffffffffffffffffffffffffffffffffff891690610d5b908590611901565b5f604051808303815f865af19150503d805f8114610d94576040519150601f19603f3d011682016040523d82523d5f602084013e610d99565b606091505b5091509150811580610dc75750805115801590610dc7575080806020019051810190610dc5919061191c565b155b15610e6b576040808a01515f9081526006602090815282822060608d0151835290529081208054879290610dfc9084906118ee565b925050819055508673ffffffffffffffffffffffffffffffffffffffff168873ffffffffffffffffffffffffffffffffffffffff167f4bb241f3b40fc832b91e1567c9d919078fe98ec3808ffaf1970b75b68248794287604051610e6291815260200190565b60405180910390a35b505050611050565b5f60095f836001811115610e8957610e89611488565b6001811115610e9a57610e9a611488565b815260208101919091526040015f90812054608089015173ffffffffffffffffffffffffffffffffffffffff9091169250610ed55785610ed7565b305b6040517f55389fb000000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8981166004830152808316602483015288811660448301526064820187905260a060848301525f60a483018190529293508416906355389fb09060c4016020604051808303815f875af1158015610f6b573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610f8f919061191c565b9050808015610f9f575088608001515b15610fdf576040808a01515f9081526006602090815282822060608d0151835290529081208054879290610fd49084906118ee565b9091555061104c9050565b8061104c578673ffffffffffffffffffffffffffffffffffffffff168873ffffffffffffffffffffffffffffffffffffffff167f8557f7d07a398ea2e78b2bc72cc07a853cf05a78160fa3626be99b5613e171498787604051611043929190611935565b60405180910390a35b5050505b505b84516040805173ffffffffffffffffffffffffffffffffffffffff8087168252871660208201529081018390527f4190759d37d5cfe7a1a70e06ec7508a05d12fd9cb76f353da1c9e028e5a48dcf9060600160405180910390a25050505050565b5f81806020019051810190610ad49190611949565b8073ffffffffffffffffffffffffffffffffffffffff8116611116576040517fe713a76300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6003805473ffffffffffffffffffffffffffffffffffffffff8481167fffffffffffffffffffffffff000000000000000000000000000000000000000083168117909355604080519190921680825260208201939093527f68e84423772dadc3e4047f8b5bd221ddb02dc67796e7852533fd976947d86c5191015b60405180910390a1505050565b6003546040517f8a901b9f00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff838116600483015290911690638a901b9f906024015f604051808303815f87803b158015611208575f80fd5b505af11580156106b9573d5f803e3d5ffd5b8073ffffffffffffffffffffffffffffffffffffffff8116611268576040517fe713a76300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f805473ffffffffffffffffffffffffffffffffffffffff848116680100000000000000008181027fffffffff0000000000000000000000000000000000000000ffffffffffffffff85161790945560408051949093049091168084526020840191909152917f0be32d823eb66f9bc102ffe567930c4a8f19e2d4d5aedb1cb8b97c2c640aa3159101611191565b8073ffffffffffffffffffffffffffffffffffffffff8116611344576040517fe713a76300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001805473ffffffffffffffffffffffffffffffffffffffff8481167fffffffffffffffffffffffff000000000000000000000000000000000000000083168117909355604080519190921680825260208201939093527f68bcfd6b375bdfac05ba92dc0447413c6f7d61309f75f08e297e245571cf1f339101611191565b5f8260ff168460ff16036113d857508061142e565b8260ff168460ff16101561140c576113f08484611960565b6113fb90600a611a97565b6114059083611aa5565b905061142e565b6114168385611960565b61142190600a611a97565b61142b9083611abc565b90505b9392505050565b5f8060408385031215611446575f80fd5b50508035926020909101359150565b5f60208284031215611465575f80fd5b813573ffffffffffffffffffffffffffffffffffffffff8116811461142e575f80fd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52602160045260245ffd5b600281106114c5576114c5611488565b9052565b60208101610ad482846114b5565b5f602082840312156114e7575f80fd5b5035919050565b60208101600a831061150257611502611488565b91905290565b5f60208284031215611518575f80fd5b81356002811061142e575f80fd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b60405160a0810167ffffffffffffffff8111828210171561157657611576611526565b60405290565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff811182821017156115c3576115c3611526565b604052919050565b5f67ffffffffffffffff8211156115e4576115e4611526565b50601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01660200190565b5f60208284031215611620575f80fd5b813567ffffffffffffffff811115611636575f80fd5b8201601f81018413611646575f80fd5b8035611659611654826115cb565b61157c565b81815285602083850101111561166d575f80fd5b816020840160208301375f91810160200191909152949350505050565b805160ff8116811461169a575f80fd5b919050565b5f5b838110156116b95781810151838201526020016116a1565b50505f910152565b5f82601f8301126116d0575f80fd5b81516116de611654826115cb565b8181528460208386010111156116f2575f80fd5b61170382602083016020870161169f565b949350505050565b5f806040838503121561171c575f80fd5b6117258361168a565b9150602083015167ffffffffffffffff811115611740575f80fd5b61174c858286016116c1565b9150509250929050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b5f8060408385031215611794575f80fd5b82519150602083015167ffffffffffffffff811115611740575f80fd5b8051801515811461169a575f80fd5b5f60208083850312156117d1575f80fd5b825167ffffffffffffffff808211156117e8575f80fd5b818501915085601f8301126117fb575f80fd5b81518181111561180d5761180d611526565b61181b848260051b0161157c565b818152848101925060a0918202840185019188831115611839575f80fd5b938501935b8285101561189c5780858a031215611854575f80fd5b61185c611553565b855181528686015187820152604080870151908201526060808701519082015260806118898188016117b1565b908201528452938401939285019261183e565b50979650505050505050565b5f602082840312156118b8575f80fd5b61142e8261168a565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b80820180821115610ad457610ad46118c1565b5f825161191281846020870161169f565b9190910192915050565b5f6020828403121561192c575f80fd5b61142e826117b1565b8281526040810161142e60208301846114b5565b5f60208284031215611959575f80fd5b5051919050565b60ff8281168282160390811115610ad457610ad46118c1565b600181815b808511156119d257817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff048211156119b8576119b86118c1565b808516156119c557918102915b93841c939080029061197e565b509250929050565b5f826119e857506001610ad4565b816119f457505f610ad4565b8160018114611a0a5760028114611a1457611a30565b6001915050610ad4565b60ff841115611a2557611a256118c1565b50506001821b610ad4565b5060208310610133831016604e8410600b8410161715611a53575081810a610ad4565b611a5d8383611979565b807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff04821115611a8f57611a8f6118c1565b029392505050565b5f61142e60ff8416836119da565b8082028115828204841417610ad457610ad46118c1565b5f82611aef577f4e487b71000000000000000000000000000000000000000000000000000000005f52601260045260245ffd5b50049056fea2646970667358221220732fc6355dd2e082c969ea5cfd3bd625b35a7bb6b93b1116300f98ea9fa727bf64736f6c63430008170033

Deployed Bytecode

0x608060405234801561000f575f80fd5b506004361061018f575f3560e01c80638da5cb5b116100dd578063d6979ef611610088578063f2fde38b11610063578063f2fde38b146104e6578063f37c0a2e146104f9578063f953cec714610508575f80fd5b8063d6979ef614610491578063df7a76011461049f578063e2cae9f5146104c6575f80fd5b8063affed0e0116100b8578063affed0e01461040d578063c2aeb7bd14610453578063cab0071e14610488575f80fd5b80638da5cb5b1461038d5780638fd68940146103ca578063990969f6146103ed575f80fd5b80635c975abb1161013d578063715018a611610118578063715018a61461033c57806373e554f6146103465780637f7673f91461036d575f80fd5b80635c975abb146102c65780636301fd63146102fb5780636afdd85014610326575f80fd5b8063522dbaa51161016d578063522dbaa51461024457806352a9674b1461026b57806352ad0d5e14610297575f80fd5b8063043dca0414610193578063116191b6146101d057806339ebf82314610215575b5f80fd5b6101bd6101a1366004611435565b600660209081525f928352604080842090915290825290205481565b6040519081526020015b60405180910390f35b6003546101f09073ffffffffffffffffffffffffffffffffffffffff1681565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020016101c7565b610237610223366004611455565b60086020525f908152604090205460ff1681565b6040516101c791906114c9565b6101bd7f0afae807991f914b71165fd92589f1dc28648cb9fb1f8558f3a6c7507d56deff81565b5f5461028290640100000000900463ffffffff1681565b60405163ffffffff90911681526020016101c7565b6102b96102a53660046114d7565b60076020525f908152604090205460ff1681565b6040516101c791906114ee565b6004546102eb9074010000000000000000000000000000000000000000900460ff1681565b60405190151581526020016101c7565b5f546101f09068010000000000000000900473ffffffffffffffffffffffffffffffffffffffff1681565b6101f06e22d473030f116ddee9f6b43ac78ba381565b61034461051b565b005b6101bd7f8104c8a42e1531612796e696e327ea52a475d9583ee6d64ffdefcafad22c0b2481565b6001546101f09073ffffffffffffffffffffffffffffffffffffffff1681565b7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c1993005473ffffffffffffffffffffffffffffffffffffffff166101f0565b600a54600b546103d8919082565b604080519283526020830191909152016101c7565b6004546101f09073ffffffffffffffffffffffffffffffffffffffff1681565b60045461043a907501000000000000000000000000000000000000000000900467ffffffffffffffff1681565b60405167ffffffffffffffff90911681526020016101c7565b6101f0610461366004611508565b60096020525f908152604090205473ffffffffffffffffffffffffffffffffffffffff1681565b6101bd60055481565b600d54600e546103d8919082565b6101bd7fce47b2c080dbcd8e420dc92bcf58f25f73f2e008a3b34ac41b9d468d1fb45d5a81565b6002546101f09073ffffffffffffffffffffffffffffffffffffffff1681565b6103446104f4366004611455565b61052e565b5f546102829063ffffffff1681565b610344610516366004611610565b610596565b6105236106f2565b61052c5f610780565b565b6105366106f2565b73ffffffffffffffffffffffffffffffffffffffff811661058a576040517f1e4fbdf70000000000000000000000000000000000000000000000000000000081525f60048201526024015b60405180910390fd5b61059381610780565b50565b7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c1993005473ffffffffffffffffffffffffffffffffffffffff163314801590610619575060035473ffffffffffffffffffffffffffffffffffffffff1633141580610619575060045474010000000000000000000000000000000000000000900460ff165b15610650576040517fa756e44800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f8061065b83610815565b9092509050600282600381111561067457610674611488565b03610687576106828161084e565b505050565b600382600381111561069b5761069b611488565b036106c0575f806106ab83610894565b915091506106b982826108b5565b5050505050565b6040517f0a88c33900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b336107317f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c1993005473ffffffffffffffffffffffffffffffffffffffff1690565b73ffffffffffffffffffffffffffffffffffffffff161461052c576040517f118cdaa7000000000000000000000000000000000000000000000000000000008152336004820152602401610581565b7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c19930080547fffffffffffffffffffffffff0000000000000000000000000000000000000000811673ffffffffffffffffffffffffffffffffffffffff848116918217845560405192169182907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0905f90a3505050565b5f60605f8380602001905181019061082d919061170b565b9250905060ff8116600381111561084657610846611488565b925050915091565b5f61085882610abe565b90505f5b8151811015610682575f82828151811061087857610878611756565b6020026020010151905061088b81610ada565b5060010161085c565b5f6060828060200190518101906108ab9190611783565b9094909350915050565b6040516020016108f69060208082526007908201527f4741544557415900000000000000000000000000000000000000000000000000604082015260600190565b60405160208183030381529060405280519060200120820361092d575f61092261091f836110b3565b90565b9050610682816110c8565b60405160200161096e9060208082526007908201527f4d41494c424f5800000000000000000000000000000000000000000000000000604082015260600190565b6040516020818303038152906040528051906020012082036109a2575f61099761091f836110b3565b90506106828161119e565b6040516020016109e3906020808252600a908201527f4c49474854484f55534500000000000000000000000000000000000000000000604082015260600190565b604051602081830303815290604052805190602001208203610a17575f610a0c61091f836110b3565b90506106828161121a565b604051602001610a58906020808252600a908201527f5741544348544f57455200000000000000000000000000000000000000000000604082015260600190565b604051602081830303815290604052805190602001208203610a8c575f610a8161091f836110b3565b9050610682816112f6565b6040517f12109f2800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b606081806020019051810190610ad491906117c0565b92915050565b5f610ae6826040015190565b90505f610af4836060015190565b83515f9081526007602052604090205490915060ff166006816009811115610b1e57610b1e611488565b1480610b3b57506007816009811115610b3957610b39611488565b145b15610b465750505050565b83515f9081526007602052604090208054600691907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660018302179055505f610c0260128573ffffffffffffffffffffffffffffffffffffffff1663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa158015610bd4573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610bf891906118a8565b87602001516113c3565b905080156110525773ffffffffffffffffffffffffffffffffffffffff84165f9081526008602052604081205460ff1690816001811115610c4557610c45611488565b03610e7357856080015115610c8f576040808701515f9081526006602090815282822060608a0151835290529081208054849290610c849084906118ee565b909155506110509050565b60405173ffffffffffffffffffffffffffffffffffffffff85166024820152604481018390525f90606401604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fa9059cbb00000000000000000000000000000000000000000000000000000000179052519091505f90819073ffffffffffffffffffffffffffffffffffffffff891690610d5b908590611901565b5f604051808303815f865af19150503d805f8114610d94576040519150601f19603f3d011682016040523d82523d5f602084013e610d99565b606091505b5091509150811580610dc75750805115801590610dc7575080806020019051810190610dc5919061191c565b155b15610e6b576040808a01515f9081526006602090815282822060608d0151835290529081208054879290610dfc9084906118ee565b925050819055508673ffffffffffffffffffffffffffffffffffffffff168873ffffffffffffffffffffffffffffffffffffffff167f4bb241f3b40fc832b91e1567c9d919078fe98ec3808ffaf1970b75b68248794287604051610e6291815260200190565b60405180910390a35b505050611050565b5f60095f836001811115610e8957610e89611488565b6001811115610e9a57610e9a611488565b815260208101919091526040015f90812054608089015173ffffffffffffffffffffffffffffffffffffffff9091169250610ed55785610ed7565b305b6040517f55389fb000000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8981166004830152808316602483015288811660448301526064820187905260a060848301525f60a483018190529293508416906355389fb09060c4016020604051808303815f875af1158015610f6b573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610f8f919061191c565b9050808015610f9f575088608001515b15610fdf576040808a01515f9081526006602090815282822060608d0151835290529081208054879290610fd49084906118ee565b9091555061104c9050565b8061104c578673ffffffffffffffffffffffffffffffffffffffff168873ffffffffffffffffffffffffffffffffffffffff167f8557f7d07a398ea2e78b2bc72cc07a853cf05a78160fa3626be99b5613e171498787604051611043929190611935565b60405180910390a35b5050505b505b84516040805173ffffffffffffffffffffffffffffffffffffffff8087168252871660208201529081018390527f4190759d37d5cfe7a1a70e06ec7508a05d12fd9cb76f353da1c9e028e5a48dcf9060600160405180910390a25050505050565b5f81806020019051810190610ad49190611949565b8073ffffffffffffffffffffffffffffffffffffffff8116611116576040517fe713a76300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6003805473ffffffffffffffffffffffffffffffffffffffff8481167fffffffffffffffffffffffff000000000000000000000000000000000000000083168117909355604080519190921680825260208201939093527f68e84423772dadc3e4047f8b5bd221ddb02dc67796e7852533fd976947d86c5191015b60405180910390a1505050565b6003546040517f8a901b9f00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff838116600483015290911690638a901b9f906024015f604051808303815f87803b158015611208575f80fd5b505af11580156106b9573d5f803e3d5ffd5b8073ffffffffffffffffffffffffffffffffffffffff8116611268576040517fe713a76300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f805473ffffffffffffffffffffffffffffffffffffffff848116680100000000000000008181027fffffffff0000000000000000000000000000000000000000ffffffffffffffff85161790945560408051949093049091168084526020840191909152917f0be32d823eb66f9bc102ffe567930c4a8f19e2d4d5aedb1cb8b97c2c640aa3159101611191565b8073ffffffffffffffffffffffffffffffffffffffff8116611344576040517fe713a76300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001805473ffffffffffffffffffffffffffffffffffffffff8481167fffffffffffffffffffffffff000000000000000000000000000000000000000083168117909355604080519190921680825260208201939093527f68bcfd6b375bdfac05ba92dc0447413c6f7d61309f75f08e297e245571cf1f339101611191565b5f8260ff168460ff16036113d857508061142e565b8260ff168460ff16101561140c576113f08484611960565b6113fb90600a611a97565b6114059083611aa5565b905061142e565b6114168385611960565b61142190600a611a97565b61142b9083611abc565b90505b9392505050565b5f8060408385031215611446575f80fd5b50508035926020909101359150565b5f60208284031215611465575f80fd5b813573ffffffffffffffffffffffffffffffffffffffff8116811461142e575f80fd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52602160045260245ffd5b600281106114c5576114c5611488565b9052565b60208101610ad482846114b5565b5f602082840312156114e7575f80fd5b5035919050565b60208101600a831061150257611502611488565b91905290565b5f60208284031215611518575f80fd5b81356002811061142e575f80fd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b60405160a0810167ffffffffffffffff8111828210171561157657611576611526565b60405290565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff811182821017156115c3576115c3611526565b604052919050565b5f67ffffffffffffffff8211156115e4576115e4611526565b50601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01660200190565b5f60208284031215611620575f80fd5b813567ffffffffffffffff811115611636575f80fd5b8201601f81018413611646575f80fd5b8035611659611654826115cb565b61157c565b81815285602083850101111561166d575f80fd5b816020840160208301375f91810160200191909152949350505050565b805160ff8116811461169a575f80fd5b919050565b5f5b838110156116b95781810151838201526020016116a1565b50505f910152565b5f82601f8301126116d0575f80fd5b81516116de611654826115cb565b8181528460208386010111156116f2575f80fd5b61170382602083016020870161169f565b949350505050565b5f806040838503121561171c575f80fd5b6117258361168a565b9150602083015167ffffffffffffffff811115611740575f80fd5b61174c858286016116c1565b9150509250929050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b5f8060408385031215611794575f80fd5b82519150602083015167ffffffffffffffff811115611740575f80fd5b8051801515811461169a575f80fd5b5f60208083850312156117d1575f80fd5b825167ffffffffffffffff808211156117e8575f80fd5b818501915085601f8301126117fb575f80fd5b81518181111561180d5761180d611526565b61181b848260051b0161157c565b818152848101925060a0918202840185019188831115611839575f80fd5b938501935b8285101561189c5780858a031215611854575f80fd5b61185c611553565b855181528686015187820152604080870151908201526060808701519082015260806118898188016117b1565b908201528452938401939285019261183e565b50979650505050505050565b5f602082840312156118b8575f80fd5b61142e8261168a565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b80820180821115610ad457610ad46118c1565b5f825161191281846020870161169f565b9190910192915050565b5f6020828403121561192c575f80fd5b61142e826117b1565b8281526040810161142e60208301846114b5565b5f60208284031215611959575f80fd5b5051919050565b60ff8281168282160390811115610ad457610ad46118c1565b600181815b808511156119d257817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff048211156119b8576119b86118c1565b808516156119c557918102915b93841c939080029061197e565b509250929050565b5f826119e857506001610ad4565b816119f457505f610ad4565b8160018114611a0a5760028114611a1457611a30565b6001915050610ad4565b60ff841115611a2557611a256118c1565b50506001821b610ad4565b5060208310610133831016604e8410600b8410161715611a53575081810a610ad4565b611a5d8383611979565b807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff04821115611a8f57611a8f6118c1565b029392505050565b5f61142e60ff8416836119da565b8082028115828204841417610ad457610ad46118c1565b5f82611aef577f4e487b71000000000000000000000000000000000000000000000000000000005f52601260045260245ffd5b50049056fea2646970667358221220732fc6355dd2e082c969ea5cfd3bd625b35a7bb6b93b1116300f98ea9fa727bf64736f6c63430008170033

Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

Block Uncle Number Difficulty Gas Used Reward
View All Uncles
Loading...
Loading
Loading...
Loading

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits

A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.