FRAX Price: $0.97 (-4.76%)

Contract

0x9De2f3926b636D26b4187a9beA11B18ab17b5441

Overview

FRAX Balance | FXTL Balance

0 FRAX | 12 FXTL

FRAX Value

$0.00

Token Holdings

More Info

Private Name Tags

Multichain Info

No addresses found
Transaction Hash
Block
From
To

There are no matching entries

1 Token Transfer found.

View more zero value Internal Transactions in Advanced View mode

Advanced mode:

Cross-Chain Transactions
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
CurveFacet

Compiler Version
v0.8.25+commit.b61c2a91

Optimization Enabled:
Yes with 200 runs

Other Settings:
cancun EvmVersion
// SPDX-License-Identifier: UNLICENSED
// Copyright (c) Eywa.Fi, 2021-2025 - all rights reserved
pragma solidity 0.8.25;

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

import {OpsCurveAMM, Ops} from "./Ops.sol";
import {ICurveFacet, ICoreFacet} from "./interfaces/ICurveFacet.sol";
import {IAccessControlFacet} from "./interfaces/IAccessControlFacet.sol";
import {IUnifiedPoolAdapter} from "../interfaces/IUnifiedPoolAdapter.sol";
import {CoreFacetStorage} from "./libraries/CoreFacetStorage.sol";
import {CurveFacetStorage} from "./libraries/CurveFacetStorage.sol";
import {TypecastLib} from "../utils/TypecastLib.sol";


contract CurveFacet is ICurveFacet, OpsCurveAMM, Ops {
    using CoreFacetStorage for CoreFacetStorage.DS;
    using CurveFacetStorage for CurveFacetStorage.DS;

    /// @notice Role identifier for operator permissions.
    /// @dev Accounts with this role can perform operational tasks like setting pool adapters.
    bytes32 public constant OPERATOR_ROLE = keccak256("OPERATOR_ROLE");

    /// @dev Modifier that checks that the caller has a specific role.
    /// @param role The role identifier to check.
    modifier onlyRole(bytes32 role) {
        require(IAccessControlFacet(address(this)).hasRole(role, msg.sender), "CurveFacet: missing role");
        _;
    }

    /// @inheritdoc ICurveFacet
    function setPoolAdapter(
        address pool_,
        address poolAdapter_
    ) external onlyRole(OPERATOR_ROLE) {
        require(pool_ != address(0), "CurveFacet: zero address");
        CurveFacetStorage.ds().poolAdapter[pool_] = poolAdapter_;
        emit PoolAdapterSet(pool_, poolAdapter_);
    }

    /// @inheritdoc ICurveFacet
    function executeCurveAMMOp(
        bool /*isOpHalfDone*/,
        bytes32 op,
        bytes32 nextOp,
        bytes memory params,
        ICoreFacet.MaskedParams memory prevMaskedParams
    ) 
        external 
        payable 
        onlyDiamond
        returns (
            uint64 chainIdTo, 
            bytes memory updatedParams, 
            ICoreFacet.MaskedParams memory maskedParams,
            ICoreFacet.ExecutionResult result
        )
    {
        if (ADD_CODE == op) {
            ICurveFacet.AddParams memory p = abi.decode(params, (ICurveFacet.AddParams));
            address adapter = _getPoolAdapter(TypecastLib.castToAddress(p.pool));
            (p.amountIn, p.from, p.emergencyTo) = checkMaskedParams(p.amountIn, p.from, p.emergencyTo, prevMaskedParams);
            p.to = checkTo(p.to, p.emergencyTo, uint64(block.chainid), op, nextOp);

            _transferToAdapter(TypecastLib.castToAddress(p.tokenIn), TypecastLib.castToAddress(p.from), adapter, p.amountIn);

            maskedParams.amountOut = IUnifiedPoolAdapter(adapter).addLiquidity(
                TypecastLib.castToAddress(p.tokenIn),
                p.amountIn,
                TypecastLib.castToAddress(p.to),
                TypecastLib.castToAddress(p.pool),
                p.minAmountOut,
                p.i,
                TypecastLib.castToAddress(p.emergencyTo)
            );
            maskedParams.to = p.to;
            maskedParams.emergencyTo = p.emergencyTo;
            result = _checkResult(maskedParams.amountOut);
        } else if (REMOVE_CODE == op) {
            ICurveFacet.RemoveParams memory p = abi.decode(params, (ICurveFacet.RemoveParams));
            address adapter = _getPoolAdapter(TypecastLib.castToAddress(p.pool));
            (p.amountIn, p.from, p.emergencyTo) = checkMaskedParams(p.amountIn, p.from, p.emergencyTo, prevMaskedParams);
            p.to = checkTo(p.to, p.emergencyTo, uint64(block.chainid), op, nextOp);

            _transferToAdapter(TypecastLib.castToAddress(p.tokenIn), TypecastLib.castToAddress(p.from), adapter, p.amountIn);

            maskedParams.amountOut = IUnifiedPoolAdapter(adapter).removeLiquidity(
                TypecastLib.castToAddress(p.tokenIn),
                p.amountIn,
                TypecastLib.castToAddress(p.to),
                TypecastLib.castToAddress(p.pool),
                p.minAmountOut,
                p.j,
                TypecastLib.castToAddress(p.emergencyTo)
            );
            maskedParams.to = p.to;
            maskedParams.emergencyTo = p.emergencyTo;
            result = _checkResult(maskedParams.amountOut);
        } else if (SWAP_CODE == op) {
            ICurveFacet.SwapParams memory p = abi.decode(params, (ICurveFacet.SwapParams));
            address adapter = _getPoolAdapter(TypecastLib.castToAddress(p.pool));
            (p.amountIn, p.from, p.emergencyTo) = checkMaskedParams(p.amountIn, p.from, p.emergencyTo, prevMaskedParams);
            p.to = checkTo(p.to, p.emergencyTo, uint64(block.chainid), op, nextOp);

            _transferToAdapter(TypecastLib.castToAddress(p.tokenIn), TypecastLib.castToAddress(p.from), adapter, p.amountIn);

            maskedParams.amountOut = IUnifiedPoolAdapter(adapter).swap(
                TypecastLib.castToAddress(p.tokenIn),
                p.amountIn,
                TypecastLib.castToAddress(p.to),
                TypecastLib.castToAddress(p.pool),
                p.minAmountOut,
                p.i,
                p.j,
                TypecastLib.castToAddress(p.emergencyTo)
            );
            maskedParams.to = p.to;
            maskedParams.emergencyTo = p.emergencyTo;
            result = _checkResult(maskedParams.amountOut);
        } else {
            result = ICoreFacet.ExecutionResult.Failed;
        }
        chainIdTo = 0;
        updatedParams = bytes("");
    }

    /// @inheritdoc ICurveFacet
    function poolAdapter(address pool) external view returns (address) {
        return CurveFacetStorage.ds().poolAdapter[pool];
    }

    /// @notice Transfers tokens from `from` to `adapter`, depending on ownership.
    /// @param tokenIn Address of the token.
    /// @param from Address of sender.
    /// @param adapter Address of the target adapter.
    /// @param amountIn Amount to transfer.
    function _transferToAdapter(address tokenIn, address from, address adapter, uint256 amountIn) private {
        if (from == address(this)) {
            SafeERC20.safeTransfer(IERC20(tokenIn), adapter, amountIn);
        } else {
            SafeERC20.safeTransferFrom(IERC20(tokenIn), from, adapter, amountIn);
        }
    }

    /// @notice Returns the adapter address for a given liquidity pool.
    /// @param pool The liquidity pool address.
    /// @return adapter The corresponding pool adapter address.
    /// @dev Reverts if adapter is not registered.
    function _getPoolAdapter(address pool) private view returns (address adapter) {
        adapter = CurveFacetStorage.ds().poolAdapter[pool];
        require(adapter != address(0), "CurveFacet: pool adapter not set");
    }

    /// @notice Returns the execution result based on the output amount.
    /// @param amountOut The amount received after operation.
    /// @return The corresponding ExecutionResult enum value.
    function _checkResult(uint256 amountOut) private view returns (ICoreFacet.ExecutionResult) {
        if (amountOut == 0) {
            if (CoreFacetStorage.isOriginNetwork()) {
                revert("CurveFacet: slippage");
            }
            return ICoreFacet.ExecutionResult.Interrupted;
        }
        return ICoreFacet.ExecutionResult.Succeeded;
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.4) (token/ERC20/extensions/IERC20Permit.sol)

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
 * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
 *
 * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
 * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't
 * need to send a transaction, and thus is not required to hold Ether at all.
 *
 * ==== Security Considerations
 *
 * There are two important considerations concerning the use of `permit`. The first is that a valid permit signature
 * expresses an allowance, and it should not be assumed to convey additional meaning. In particular, it should not be
 * considered as an intention to spend the allowance in any specific way. The second is that because permits have
 * built-in replay protection and can be submitted by anyone, they can be frontrun. A protocol that uses permits should
 * take this into consideration and allow a `permit` call to fail. Combining these two aspects, a pattern that may be
 * generally recommended is:
 *
 * ```solidity
 * function doThingWithPermit(..., uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) public {
 *     try token.permit(msg.sender, address(this), value, deadline, v, r, s) {} catch {}
 *     doThing(..., value);
 * }
 *
 * function doThing(..., uint256 value) public {
 *     token.safeTransferFrom(msg.sender, address(this), value);
 *     ...
 * }
 * ```
 *
 * Observe that: 1) `msg.sender` is used as the owner, leaving no ambiguity as to the signer intent, and 2) the use of
 * `try/catch` allows the permit to fail and makes the code tolerant to frontrunning. (See also
 * {SafeERC20-safeTransferFrom}).
 *
 * Additionally, note that smart contract wallets (such as Argent or Safe) are not able to produce permit signatures, so
 * contracts should have entry points that don't rely on permit.
 */
interface IERC20Permit {
    /**
     * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,
     * given ``owner``'s signed approval.
     *
     * IMPORTANT: The same issues {IERC20-approve} has related to transaction
     * ordering also apply here.
     *
     * Emits an {Approval} event.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     * - `deadline` must be a timestamp in the future.
     * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
     * over the EIP712-formatted function arguments.
     * - the signature must use ``owner``'s current nonce (see {nonces}).
     *
     * For more information on the signature format, see the
     * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
     * section].
     *
     * CAUTION: See Security Considerations above.
     */
    function permit(
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external;

    /**
     * @dev Returns the current nonce for `owner`. This value must be
     * included whenever a signature is generated for {permit}.
     *
     * Every successful call to {permit} increases ``owner``'s nonce by one. This
     * prevents a signature from being used multiple times.
     */
    function nonces(address owner) external view returns (uint256);

    /**
     * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.
     */
    // solhint-disable-next-line func-name-mixedcase
    function DOMAIN_SEPARATOR() external view returns (bytes32);
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/IERC20.sol)

pragma solidity ^0.8.0;

/**
 * @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 amount of tokens in existence.
     */
    function totalSupply() external view returns (uint256);

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

    /**
     * @dev Moves `amount` 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 amount) 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 `amount` 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 amount) external returns (bool);

    /**
     * @dev Moves `amount` tokens from `from` to `to` using the
     * allowance mechanism. `amount` 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 amount) external returns (bool);
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.3) (token/ERC20/utils/SafeERC20.sol)

pragma solidity ^0.8.0;

import "../IERC20.sol";
import "../extensions/IERC20Permit.sol";
import "../../../utils/Address.sol";

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

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

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

    /**
     * @dev Deprecated. This function has issues similar to the ones found in
     * {IERC20-approve}, and its usage is discouraged.
     *
     * Whenever possible, use {safeIncreaseAllowance} and
     * {safeDecreaseAllowance} instead.
     */
    function safeApprove(IERC20 token, address spender, uint256 value) internal {
        // safeApprove should only be called when setting an initial allowance,
        // or when resetting it to zero. To increase and decrease it, use
        // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
        require(
            (value == 0) || (token.allowance(address(this), spender) == 0),
            "SafeERC20: approve from non-zero to non-zero allowance"
        );
        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
    }

    /**
     * @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
     * non-reverting calls are assumed to be successful.
     */
    function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
        uint256 oldAllowance = token.allowance(address(this), spender);
        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance + value));
    }

    /**
     * @dev Decrease the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
     * non-reverting calls are assumed to be successful.
     */
    function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal {
        unchecked {
            uint256 oldAllowance = token.allowance(address(this), spender);
            require(oldAllowance >= value, "SafeERC20: decreased allowance below zero");
            _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance - value));
        }
    }

    /**
     * @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value,
     * non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval
     * to be set to zero before setting it to a non-zero value, such as USDT.
     */
    function forceApprove(IERC20 token, address spender, uint256 value) internal {
        bytes memory approvalCall = abi.encodeWithSelector(token.approve.selector, spender, value);

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

    /**
     * @dev Use a ERC-2612 signature to set the `owner` approval toward `spender` on `token`.
     * Revert on invalid signature.
     */
    function safePermit(
        IERC20Permit token,
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) internal {
        uint256 nonceBefore = token.nonces(owner);
        token.permit(owner, spender, value, deadline, v, r, s);
        uint256 nonceAfter = token.nonces(owner);
        require(nonceAfter == nonceBefore + 1, "SafeERC20: permit did not succeed");
    }

    /**
     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
     * on the return value: the return value is optional (but if data is returned, it must not be false).
     * @param token The token targeted by the call.
     * @param data The call data (encoded using abi.encode or one of its variants).
     */
    function _callOptionalReturn(IERC20 token, bytes memory data) private {
        // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
        // we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that
        // the target address contains contract code and also asserts for success in the low-level call.

        bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
        require(returndata.length == 0 || abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
    }

    /**
     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
     * on the return value: the return value is optional (but if data is returned, it must not be false).
     * @param token The token targeted by the call.
     * @param data The call data (encoded using abi.encode or one of its variants).
     *
     * This is a variant of {_callOptionalReturn} that silents catches all reverts and returns a bool instead.
     */
    function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
        // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
        // we're implementing it ourselves. We cannot use {Address-functionCall} here since this should return false
        // and not revert is the subcall reverts.

        (bool success, bytes memory returndata) = address(token).call(data);
        return
            success && (returndata.length == 0 || abi.decode(returndata, (bool))) && Address.isContract(address(token));
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/Address.sol)

pragma solidity ^0.8.1;

/**
 * @dev Collection of functions related to the address type
 */
library Address {
    /**
     * @dev Returns true if `account` is a contract.
     *
     * [IMPORTANT]
     * ====
     * It is unsafe to assume that an address for which this function returns
     * false is an externally-owned account (EOA) and not a contract.
     *
     * Among others, `isContract` will return false for the following
     * types of addresses:
     *
     *  - an externally-owned account
     *  - a contract in construction
     *  - an address where a contract will be created
     *  - an address where a contract lived, but was destroyed
     *
     * Furthermore, `isContract` will also return true if the target contract within
     * the same transaction is already scheduled for destruction by `SELFDESTRUCT`,
     * which only has an effect at the end of a transaction.
     * ====
     *
     * [IMPORTANT]
     * ====
     * You shouldn't rely on `isContract` to protect against flash loan attacks!
     *
     * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
     * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
     * constructor.
     * ====
     */
    function isContract(address account) internal view returns (bool) {
        // This method relies on extcodesize/address.code.length, which returns 0
        // for contracts in construction, since the code is only stored at the end
        // of the constructor execution.

        return account.code.length > 0;
    }

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

        (bool success, ) = recipient.call{value: amount}("");
        require(success, "Address: unable to send value, recipient may have reverted");
    }

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

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
     * `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal returns (bytes memory) {
        return functionCallWithValue(target, data, 0, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but also transferring `value` wei to `target`.
     *
     * Requirements:
     *
     * - the calling contract must have an ETH balance of at least `value`.
     * - the called Solidity function must be `payable`.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
        return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
    }

    /**
     * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
     * with `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(
        address target,
        bytes memory data,
        uint256 value,
        string memory errorMessage
    ) internal returns (bytes memory) {
        require(address(this).balance >= value, "Address: insufficient balance for call");
        (bool success, bytes memory returndata) = target.call{value: value}(data);
        return verifyCallResultFromTarget(target, success, returndata, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
        return functionStaticCall(target, data, "Address: low-level static call failed");
    }

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

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a delegate call.
     *
     * _Available since v3.4._
     */
    function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
        return functionDelegateCall(target, data, "Address: low-level delegate call failed");
    }

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

    /**
     * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling
     * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.
     *
     * _Available since v4.8._
     */
    function verifyCallResultFromTarget(
        address target,
        bool success,
        bytes memory returndata,
        string memory errorMessage
    ) internal view returns (bytes memory) {
        if (success) {
            if (returndata.length == 0) {
                // only check isContract if the call was successful and the return data is empty
                // otherwise we already know that it was a contract
                require(isContract(target), "Address: call to non-contract");
            }
            return returndata;
        } else {
            _revert(returndata, errorMessage);
        }
    }

    /**
     * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the
     * revert reason or using the provided one.
     *
     * _Available since v4.3._
     */
    function verifyCallResult(
        bool success,
        bytes memory returndata,
        string memory errorMessage
    ) internal pure returns (bytes memory) {
        if (success) {
            return returndata;
        } else {
            _revert(returndata, errorMessage);
        }
    }

    function _revert(bytes memory returndata, string memory errorMessage) private pure {
        // Look for revert reason and bubble it up if present
        if (returndata.length > 0) {
            // The easiest way to bubble the revert reason is using memory via assembly
            /// @solidity memory-safe-assembly
            assembly {
                let returndata_size := mload(returndata)
                revert(add(32, returndata), returndata_size)
            }
        } else {
            revert(errorMessage);
        }
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/Counters.sol)

pragma solidity ^0.8.0;

/**
 * @title Counters
 * @author Matt Condon (@shrugs)
 * @dev Provides counters that can only be incremented, decremented or reset. This can be used e.g. to track the number
 * of elements in a mapping, issuing ERC721 ids, or counting request ids.
 *
 * Include with `using Counters for Counters.Counter;`
 */
library Counters {
    struct Counter {
        // This variable should never be directly accessed by users of the library: interactions must be restricted to
        // the library's function. As of Solidity v0.5.2, this cannot be enforced, though there is a proposal to add
        // this feature: see https://github.com/ethereum/solidity/issues/4637
        uint256 _value; // default: 0
    }

    function current(Counter storage counter) internal view returns (uint256) {
        return counter._value;
    }

    function increment(Counter storage counter) internal {
        unchecked {
            counter._value += 1;
        }
    }

    function decrement(Counter storage counter) internal {
        uint256 value = counter._value;
        require(value > 0, "Counter: decrement overflow");
        unchecked {
            counter._value = value - 1;
        }
    }

    function reset(Counter storage counter) internal {
        counter._value = 0;
    }
}

// SPDX-License-Identifier: UNLICENSED
// Copyright (c) Eywa.Fi, 2021-2025 - all rights reserved
pragma solidity ^0.8.17;


interface IAddressBook {
    /// @dev returns portal by given chainId
    function portal(uint64 chainId) external view returns (address);

    /// @dev returns synthesis by given chainId
    function synthesis(uint64 chainId) external view returns (address);

    /// @dev returns router by given chainId
    function router(uint64 chainId) external view returns (address);

    /// @dev returns portal by given chainId
    function portalV3(uint64 chainId) external view returns (bytes32);

    /// @dev returns synthesis by given chainId
    function synthesisV3(uint64 chainId) external view returns (bytes32);

    /// @dev returns router by given chainId
    function routerV3(uint64 chainId) external view returns (bytes32);

    /// @dev returns whitelist
    function whitelist() external view returns (address);

    /// @dev returns treasury
    function treasury() external view returns (address);

    /// @dev returns gateKeeper
    function gateKeeper() external view returns (address);

    /// @dev returns receiver
    function receiver() external view returns (address);

    /// @dev returns wrapped native asset (WETH)
    function WETH() external view returns (address);
}

// SPDX-License-Identifier: UNLICENSED
// Copyright (c) Eywa.Fi, 2021-2023 - all rights reserved
pragma solidity ^0.8.17;


interface IUnifiedPoolAdapter {

    function addLiquidity(
        address tokenIn,
        uint256 amountIn,
        address to,
        address pool,
        uint256 minAmountOut,
        uint8 i,
        address emergencyTo
    ) external returns (uint256 amountOut);

    function swap(
        address tokenIn,
        uint256 amountIn,
        address to,
        address pool,
        uint256 minAmountOut,
        uint8 i,
        uint8 j,
        address emergencyTo
    ) external returns (uint256 amountOut);

    function removeLiquidity(
        address tokenIn,
        uint256 amountIn,
        address to,
        address pool,
        uint256 minAmountOut,
        uint8 j,
        address emergencyTo
    ) external returns (uint256 amountOut);
}

// SPDX-License-Identifier: UNLICENSED
// Copyright (c) Eywa.Fi, 2021-2025 - all rights reserved
pragma solidity 0.8.25;


interface IAccessControlFacet {
    /// @notice Emitted when a role's admin role is changed.
    /// @param role The role identifier whose admin role was changed.
    /// @param previousAdminRole The previous admin role.
    /// @param newAdminRole The new admin role.
    event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole);

    /// @notice Emitted when a role is granted to an account.
    /// @param role The role identifier being granted.
    /// @param account The address receiving the role.
    /// @param sender The address that performed the role grant.
    event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);

    /// @notice Emitted when a role is revoked from an account.
    /// @param role The role identifier being revoked.
    /// @param account The address losing the role.
    /// @param sender The address that performed the role revocation.
    event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);

    /// @notice Grants a role to an account.
    /// @dev Only callable by accounts that have the admin role of the given role.
    /// @param role The role identifier to grant.
    /// @param account The address to grant the role to.
    function grantRole(bytes32 role, address account) external;

    /// @notice Revokes a role from an account.
    /// @dev Only callable by accounts that have the admin role of the given role.
    /// @param role The role identifier to revoke.
    /// @param account The address to revoke the role from.
    function revokeRole(bytes32 role, address account) external;

    /// @notice Allows a caller to renounce a role for themselves.
    /// @dev Caller must be the same as the account being modified.
    /// @param role The role identifier to renounce.
    /// @param account The address renouncing the role (must be msg.sender).
    function renounceRole(bytes32 role, address account) external;

    /// @notice Checks whether an account has a specific role.
    /// @param role The role identifier to check.
    /// @param account The address to check for the role.
    /// @return True if the account has the role, false otherwise.
    function hasRole(bytes32 role, address account) external view returns (bool);

    /// @notice Returns the admin role that controls a specific role.
    /// @param role The role identifier to query.
    /// @return The admin role associated with the given role.
    function getRoleAdmin(bytes32 role) external view returns (bytes32);
}

// SPDX-License-Identifier: UNLICENSED
// Copyright (c) Eywa.Fi, 2021-2025 - all rights reserved
pragma solidity 0.8.25;

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

interface IBreakFacet {
    /// @notice Break operation attempted as the first step of a resumed tail.
    /// @dev Used by {resumeBr} to forbid placing `br` at index 0 of the resumed pipeline.
    error BrFirstStepForbidden();

    /// @notice Previous step output was not routed back into the router.
    /// @dev Thrown when `prevMaskedParams.to` is not equal to the router address during `br`,
    ///      meaning the router does not currently custody the funds to be escrowed.
    error BrPrevToNotRouter();

    /// @notice Break with this requestId already exists.
    /// @dev Signals that a new `br` tried to reuse a `requestId` whose status is not `None`.
    error BrAlreadyExists();

    /// @notice Nothing to stash for this break.
    /// @dev Thrown when the previous step produced `amountOut == 0`, so there is no value
    ///      to put under break escrow.
    error BrNothingToStash();

    /// @notice Invalid break state.
    /// @dev Used as a generic internal-safety error, for example when `requestId` is zero
    ///      or other invariant assumptions do not hold.
    error BrInternalState();

    /// @notice Break metadata not found.
    /// @dev Indicates that no record exists in storage for the specified `requestId`.
    error BrNotFound();

    /// @notice Break is not in the Pending state.
    /// @dev Thrown when an action that requires `Pending` (cancel or resume) is attempted
    ///      on a break with a different status.
    error BrNotPending();

    /// @notice Wrong chain.
    /// @dev Raised when `resumeBr` is called on a chain whose `block.chainid` does not match
    ///      the chain recorded in the break metadata.
    error BrWrongChain();

    /// @notice Wrong cursor position.
    /// @dev Thrown if the `cPos` argument passed into `resumeBr` does not match the cursor
    ///      that was persisted at break time.
    error BrWrongCPos();

    /// @notice Tail mismatch.
    /// @dev Used when:
    ///      - the provided tail length differs from the stored `tailLen`, or
    ///      - `keccak256(abi.encode(opsTail, paramsTail))` does not match the stored `tailHash`.
    error BrWrongTail();

    /// @notice Break resume already started.
    /// @dev Signaled when `resumeBr` is invoked again for a break where `resumeStarted`
    ///      is already true.
    error BrAlreadyStarted();

    /// @notice Caller is not authorized.
    /// @dev Thrown when a function restricted to `initiator` or `emergencyTo` is called
    ///      by a different address.
    error BrNotAuthorized();

    /// @notice Break funds have already been consumed.
    /// @dev Raised when `resumeBr` is called but the stored reserved balance for this break
    ///      is already zero, meaning the funds were processed earlier.
    error BrAlreadyProcessed();

    /// @notice Non-zero msg.value where zero was expected.
    /// @dev Used by `resumeBr` and other break-related entry points that must never accept
    ///      direct ETH.
    error BrMsgValueNotAllowed();

    /// @notice Router is paused.
    /// @dev Thrown when break flows are invoked while the router is paused via the pausable
    ///      facet.
    error BrPaused();

    /// @notice Insufficient native balance to escrow.
    /// @dev Signals that the router’s native balance is lower than the amount that must
    ///      be placed into break escrow for the current operation.
    error BrInsufficientNative();

    /// @notice Insufficient ERC20 balance to escrow.
    /// @dev Signals that the router’s ERC20 balance for the given asset is lower than the
    ///      amount that must be placed into break escrow.
    error BrInsufficientToken();

    /// @notice Break escrow address is not configured.
    /// @dev Used when a break operation requires the escrow contract but the stored escrow
    ///      address is zero.
    error BrEscrowNotSet();

    /// @notice Caller is not the configured break escrow contract.
    /// @dev Enforces that the router-side callback for native returns can only be invoked
    ///      by the current BreakEscrow instance.
    error BrOnlyEscrow();
    
    /// @notice Stage context was not found for this break.
    /// @dev Thrown when `executeBreakOp` is invoked but no staged asset and amount
    ///      were recorded in `BreakFacetStorage` by the preceding step (e.g. Rubic/Bungee/Runner),
    ///      typically meaning `BREAK` was not placed directly after a staging operation.
    error BrMissingStageContext();

    /// @notice Staged amount does not match previous step output.
    /// @dev Used when the amount recorded in `BreakFacetStorage` differs from
    ///      `prevMaskedParams.amountOut` inside `executeBreakOp`, indicating a corrupted
    ///      or misconfigured pipeline for the current break.
    error BrInconsistentAmount();

    /// @notice Emitted when a new break (`br`) is created and funds are escrowed.
    /// @dev Records the full metadata snapshot for a newly created break:
    ///      - `requestId` uniquely identifies the break,
    ///      - `initiator` is the address that triggered `br`,
    ///      - `emergencyTo` is the account that may later cancel or resume,
    ///      - `chainId` is the chain on which the break must be resumed,
    ///      - `cPos` is the cursor position where execution halted,
    ///      - `tailHash` is `keccak256(abi.encode(opsTail, paramsTail))`,
    ///      - `tailLen` is the number of operations in the saved tail.
    /// @param requestId Unique break identifier.
    /// @param initiator Address that initiated the break.
    /// @param emergencyTo Emergency recipient that may cancel or resume.
    /// @param chainId Chain ID where this break must later be resumed.
    /// @param cPos Cursor position within the pipeline where execution stopped.
    /// @param tailHash Hash of the recorded tail ops and params.
    /// @param tailLen Number of operations stored in the tail.
    event BreakCreated(
        bytes32 indexed requestId,
        address indexed initiator,
        address indexed emergencyTo,
        uint64 chainId,
        uint8 cPos,
        bytes32 tailHash,
        uint16 tailLen
    );

    /// @notice Emitted when a break is successfully resumed and its funds consumed.
    /// @dev Emitted at the end of a successful `resumeBr` flow, after the reserved balances
    ///      for this break have been cleared and the status set to `Consumed`.
    /// @param requestId Break identifier that was resumed and consumed.
    event BreakConsumed(bytes32 indexed requestId);

    /// @notice Emitted when a pending break is cancelled with `!br`.
    /// @dev Emitted after a break is marked `Cancelled` and its reserved funds are released
    ///      to the configured emergency recipient.
    /// @param requestId Break identifier that was cancelled.
    /// @param to Recipient of released funds (usually `emergencyTo`).
    event BreakCancelled(bytes32 indexed requestId, address indexed to);

    /// @notice Configures the BreakEscrow contract used to hold break funds.
    /// @dev Must be called by a privileged router role before any `br` / `!br` / `resumeBr`
    ///      flows are executed. Reverts with {BrEscrowNotSet} if `escrow` is the zero address.
    /// @param escrow Address of the BreakEscrow contract that will custody break funds.
    function setBreakEscrow(address escrow) external;

    /// @notice Creates a new break (`br`) inside the router pipeline and interrupts execution.
    /// @dev Intended to be invoked only by the router’s core execution loop via delegatecall.
    ///      The function:
    ///      - validates that `prevMaskedParams.to` points back to the router,
    ///      - derives the amount to be escrowed from `prevMaskedParams.amountOut`,
    ///      - moves native or ERC20 funds into the configured BreakEscrow,
    ///      - records break metadata keyed by `requestId`,
    ///      - returns `ExecutionResult.Interrupted` so the router stops processing further ops.
    ///      After a successful call the break status becomes `Pending`.
    /// @param isResumeStart Reserved flag propagated by the router. Always false for `br`.
    /// @param currentOp Operation code for this step. Expected to equal `BREAK_CODE`.
    /// @param nextOp Next operation code in the pipeline. Expected to be zero, since `br` is terminal.
    /// @param rawParams ABI-encoded break parameters:
    ///        - `requestId`: unique ID for this break,
    ///        - `asset`: token being escrowed (zero address = native),
    ///        - `cPos`: cursor position of the break,
    ///        - `tailHash`: keccak256 hash of `(opsTail, paramsTail)`,
    ///        - `tailLen`: number of ops in the tail.
    /// @param prevMaskedParams Masked context from the previous step, including `amountOut`,
    ///        `to`, and `emergencyTo`. Used to:
    ///        - determine how much value to escrow,
    ///        - assert that the router currently holds the funds,
    ///        - persist the authorized `emergencyTo`.
    /// @return chainIdTo Always 0. No cross-chain hop is initiated by `br`.
    /// @return updatedParams Always empty. The break does not forward param patches.
    /// @return newMaskedParams Masked params that will be carried forward unchanged.
    /// @return result Always `ExecutionResult.Interrupted`. Signals the pipeline to stop.
    function executeBreakOp(
        bool isResumeStart,
        bytes32 currentOp,
        bytes32 nextOp,
        bytes calldata rawParams,
        ICoreFacet.MaskedParams calldata prevMaskedParams
    )
        external
        returns (
            uint64 chainIdTo,
            bytes memory updatedParams,
            ICoreFacet.MaskedParams memory newMaskedParams,
            ICoreFacet.ExecutionResult result
        );

    /// @notice Cancels a pending break (`!br`) and releases the reserved funds to the emergency recipient.
    /// @dev Intended to be invoked only by the router’s core execution loop via delegatecall.
    ///      The function:
    ///      - requires that the break status is `Pending`,
    ///      - requires the caller to be either `initiator` or `emergencyTo`,
    ///      - requests the BreakEscrow to transfer reserved funds to `emergencyTo`,
    ///      - clears the logical reserved balance in break storage,
    ///      - marks the break as `Cancelled`,
    ///      - returns `ExecutionResult.Succeeded`.
    ///      After a successful cancellation the break cannot be resumed anymore.
    /// @param isResumeStart Reserved flag propagated by the router. Unused for `!br`.
    /// @param currentOp Operation code. Expected to equal `CANCEL_BREAK_CODE`.
    /// @param nextOp Next operation code. Expected to be zero, since `!br` is terminal.
    /// @param rawParams ABI-encoded single field:
    ///        - `requestId`: identifier of the break to cancel.
    /// @param prevMaskedParams Masked context from the previous step. Forwarded unchanged.
    /// @return chainIdTo Always 0. No cross-chain hop is initiated by `!br`.
    /// @return updatedParams Always empty. No param patching is forwarded.
    /// @return newMaskedParams Same masked params as `prevMaskedParams`.
    /// @return result Always `ExecutionResult.Succeeded`. Indicates success to the router.
    function executeCancelBreakOp(
        bool isResumeStart,
        bytes32 currentOp,
        bytes32 nextOp,
        bytes calldata rawParams,
        ICoreFacet.MaskedParams calldata prevMaskedParams
    )
        external
        returns (
            uint64 chainIdTo,
            bytes memory updatedParams,
            ICoreFacet.MaskedParams memory newMaskedParams,
            ICoreFacet.ExecutionResult result
        );

    /// @notice Resumes a pending break by executing the stored tail of operations.
    /// @dev Called directly by `initiator` or `emergencyTo` outside of the normal pipeline.
    ///      The function:
    ///      - requires `msg.value == 0`,
    ///      - checks that the router is not paused,
    ///      - verifies that the break exists and is `Pending`,
    ///      - validates caller authorization against `initiator` / `emergencyTo`,
    ///      - enforces that `chainId`, `cPos`, `tailLen` and `tailHash` match the stored metadata,
    ///      - reads the reserved balance from break storage and reverts if it is already zero,
    ///      - pulls the reserved funds from BreakEscrow back to the router,
    ///      - records resume context via `setResumeMask` in the break-core facet,
    ///      - ensures the first step of the tail is not another `br`,
    ///      - executes the tail through `runTailFrom`,
    ///      - after success, clears reserved balances and marks the break as `Consumed`.
    ///      If a step in the tail triggers a cross-chain hop, the helper facet handles
    ///      dispatch and local execution stops at that point.
    /// @param requestId Unique break identifier to resume.
    /// @param cPos Cursor position saved at break time. Must match the stored cursor.
    /// @param opsTail Array of operation names (string identifiers) representing the saved
    ///        tail of the pipeline after the break.
    /// @param paramsTail ABI-encoded params for each op in `opsTail`. Must match `opsTail`
    ///        one-to-one.
    /// @param bridgeOptions Encoded bridge configuration stack used if any resumed step
    ///        performs a cross-chain hop.
    function resumeBr(
        bytes32 requestId,
        uint8 cPos,
        string[] calldata opsTail,
        bytes[] calldata paramsTail,
        bytes calldata bridgeOptions
    )
        external
        payable;

    /// @notice Callback used by the BreakEscrow contract to return native funds to the router.
    /// @dev Called by BreakEscrow when native funds are withdrawn back to the router during
    ///      a resume flow. The function:
    ///      - requires `msg.sender` to be the configured escrow address,
    ///      - checks that `requestId` is non-zero,
    ///      - accepts `msg.value` as router balance without mutating break storage.
    ///      Logical reserved balances remain tracked inside `BreakFacetStorage`.
    /// @param requestId Break identifier whose native funds are being returned to the router.
    function receiveNativeFromEscrow(bytes32 requestId) external payable;

    /// @notice Returns internal metadata for a break.
    /// @dev Provides a read-only view into the stored break record:
    ///      - `initiator`: address that called `br`,
    ///      - `emergencyTo`: address allowed to cancel or resume,
    ///      - `chainId`: chain where the break is anchored,
    ///      - `cPos`: cursor position where execution paused,
    ///      - `tailHash`: keccak256(abi.encode(opsTail, paramsTail)) captured at break time,
    ///      - `tailLen`: number of ops in the recorded tail,
    ///      - `status`: encoded status enum (None / Pending / Consumed / Cancelled),
    ///      - `resumeStarted`: whether `resumeBr` has already been invoked.
    ///      The underlying asset address is stored in the internal struct but is not part
    ///      of this return set.
    /// @param requestId Break identifier to inspect.
    /// @return initiator Address that initiated the break.
    /// @return emergencyTo Authorized emergency recipient for this break.
    /// @return chainId Chain ID where the break must be resumed.
    /// @return cPos Saved cursor position for resume.
    /// @return tailHash Hash of the recorded tail.
    /// @return tailLen Tail length recorded during `br`.
    /// @return status Encoded status enum value for this break.
    /// @return resumeStarted True if `resumeBr` has already been started.
    function getBreakMeta(
        bytes32 requestId
    )
        external
        view
        returns (
            address initiator,
            address emergencyTo,
            uint64 chainId,
            uint8 cPos,
            bytes32 tailHash,
            uint16 tailLen,
            uint8 status,
            bool resumeStarted
        );

    /// @notice Returns the reserved ERC20 balance for a given break and token.
    /// @dev Reads the logical reserved amount tracked in break storage for (`requestId`, `token`).
    ///      The physical custody of these funds is held by BreakEscrow until a resume or cancel
    ///      operation is executed.
    /// @param requestId Break identifier.
    /// @param token ERC20 token address.
    /// @return balance Amount of `token` currently reserved for this break.
    function getTokenBalance(bytes32 requestId, address token) external view returns (uint256 balance);

    /// @notice Returns the reserved native balance for a given break.
    /// @dev Reads the logical reserved native amount tracked in break storage for `requestId`.
    ///      The actual ETH is held in BreakEscrow until resume or cancel flows are executed.
    /// @param requestId Break identifier.
    /// @return balance Amount of native currency currently reserved for this break.
    function getNativeBalance(bytes32 requestId) external view returns (uint256 balance);
}

// SPDX-License-Identifier: UNLICENSED
// Copyright (c) Eywa.Fi, 2021-2025 - all rights reserved
pragma solidity 0.8.25;

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

interface IBungeeFacet {
    /// @notice ERC20 approval data for a Bungee "manual route" call built off-chain.
    /// @dev
    /// If `approvalTokenAddress` is non-zero, the facet will:
    /// - compute how much of that token it is about to send into the downstream call,
    /// - make sure allowance for `allowanceTarget` is at least `minimumApprovalAmount`
    ///   (or that computed amount, whichever is larger),
    /// - then later reset that allowance to zero.
    struct ManualApprovalData {
        /// @notice Address of the ERC20 that needs approval.
        /// @dev Zero address means "no ERC20 approval needed / expect native-only".
        address approvalTokenAddress;
        /// @notice Spender to approve.
        /// @dev Must be allowlisted via {setBungeeSpender}.
        address allowanceTarget;
        /// @notice Minimum allowance that must exist before calling the downstream target.
        /// @dev The facet will approve `max(minimumApprovalAmount, totalInputAmountOfToken)`.
        uint256 minimumApprovalAmount;
    }

    /// @notice Off-chain constructed call description for a manual Bungee route.
    /// @dev
    /// The idea of "manual" is: integrator / backend calls Bungee (Socket) API,
    /// gets a ready-to-execute tx (target, data, value), and passes it here.
    /// The facet will:
    /// - forward the summed native input + `value` + `nativeValue` from params,
    /// - optionally set ERC20 allowance using `approvalData`,
    /// - perform the external call,
    /// - zero the allowance again.
    struct ManualBuildTx {
        /// @notice Target contract that actually executes the Bungee route.
        /// @dev Must be allowlisted via {setBungeeTarget}.
        address payable txTarget;
        /// @notice Calldata to send to `txTarget`.
        bytes txData;
        /// @notice Extra ETH to attach specifically to this call,
        /// @dev in addition to any ETH aggregated from user inputs and `nativeValue`.
        uint256 value;
        /// @notice Optional ERC20 approval info.
        /// @dev If `approvalTokenAddress` is zero, no ERC20 approval path is executed.
        ManualApprovalData approvalData;
    }

    /// @notice Parameters for starting a manual Bungee operation.
    /// @dev
    /// High-level flow:
    /// - We collect inputs (native and/or ERC20s) from `from`.
    /// - We may approve an allowlisted spender.
    /// - We call `buildTx.txTarget` with `buildTx.txData` and the computed ETH value.
    /// - We reset approvals back to zero.
    /// - We measure how much of `stageAsset` ended up staged on the router after that call.
    ///
    /// `tokens` / `amounts`:
    /// - `tokens[i]` is the input token encoded as bytes32; `address(0)` (cast from bytes32) means native token.
    /// - `amounts[i]` is how much of that token to use.
    /// - If `amounts[i] == type(uint256).max`, we substitute `prevMaskedParams.amountOut` for that entry.
    ///
    /// `from`:
    /// - bytes32-encoded address we should pull ERC20 funds from (using safeTransferFrom).
    /// - If `from == 0`, we fallback to `prevMaskedParams.to`. (Meaning: use output of previous pipeline step.)
    /// - If `from != 0`, then on the origin call it MUST equal `msg.sender`.
    ///
    /// `emergencyTo`:
    /// - bytes32-encoded address for emergency withdrawal fallback.
    /// - If `emergencyTo == 0`, we fallback to `prevMaskedParams.emergencyTo`.
    /// - If `emergencyTo != 0`, then on the origin call it MUST equal `msg.sender`.
    ///
    /// `nativeValue`:
    /// - Extra ETH we must include in the downstream call in addition to any ETH amounts coming from `tokens[i]`.
    ///
    /// Staging (`stageAsset`, `minStageAmount`) and BREAK:
    /// - `stageAsset` is the token (as bytes32 address; `0` = native ETH) we EXPECT to remain sitting on the router
    ///   after Bungee completes.
    /// - We snapshot router balance of that asset before and after the Bungee call.
    ///   For ETH, we adjust the "before" snapshot by subtracting exactly the ETH we are about to forward
    ///   so that forwarded call value is not counted as staged output.
    /// - The delta is "stagedDelta".
    /// - If the next op is BREAK (`nextOp == BreakOps.BREAK_CODE`), we REQUIRE `stagedDelta >= minStageAmount`.
    ///   Then we surface that staged amount to the BREAK via `maskedParams`.
    /// - If the next op is terminal (`nextOp == 0`), we do not enforce staging and return zeroed amountOut/to.
    struct ManualStartParams {
        /// @notice Input asset addresses encoded as bytes32 (`0` = native token).
        bytes32[] tokens;
        /// @notice Amounts corresponding to `tokens`.
        /// @dev `type(uint256).max` means "use prevMaskedParams.amountOut" for that entry.
        uint256[] amounts;
        /// @notice bytes32-encoded source of funds.
        /// @dev If zero, defaults to `prevMaskedParams.to`. Otherwise must match `msg.sender` on origin.
        bytes32 from;
        /// @notice Extra ETH to add on top of summed native inputs.
        /// @dev Used for fees / integrator-required msg.value top-ups.
        uint256 nativeValue;
        /// @notice bytes32-encoded emergency recipient.
        /// @dev If zero, defaults to `prevMaskedParams.emergencyTo`. Otherwise must match `msg.sender` on origin.
        bytes32 emergencyTo;
        /// @notice Asset we expect to remain staged on the router if we BREAK next.
        /// @dev bytes32-encoded token address. `0` means native ETH.
        bytes32 stageAsset;
        /// @notice Minimal acceptable amount of `stageAsset` that must be staged on the router post-call.
        /// @dev Only enforced if `nextOp == BreakOps.BREAK_CODE`.
        uint256 minStageAmount;
        /// @notice Off-chain prebuilt call definition for the Bungee step.
        ManualBuildTx buildTx;
    }

    /// @notice ERC20 approval data for an "auto route" Bungee call.
    /// @dev
    /// "Auto" = we already know the target, calldata, and required approval on-chain
    /// (for example, a pre-integrated Socket/Bungee router).
    ///
    /// If `tokenAddress` is non-zero, the facet:
    /// - computes total ERC20 amount it's about to send,
    /// - grants allowance to `spenderAddress` for `max(amount, totalInputAmount)`,
    /// - executes the call,
    /// - resets allowance back to zero.
    struct AutoApprovalData {
        /// @notice ERC20 that must be approved for spending.
        /// @dev Zero means "no ERC20 approval path / expect native-only".
        address tokenAddress;
        /// @notice Spender that will pull the tokens.
        /// @dev Must be allowlisted via {setBungeeSpender}.
        address spenderAddress;
        /// @notice Minimum allowance that must be guaranteed before the call.
        /// @dev The facet will approve `max(amount, totalInputAmount)`.
        uint256 amount;
    }

    /// @notice On-chain call specification for an auto Bungee route.
    /// @dev
    /// The facet will call `to` directly with `data`, attach the required ETH,
    /// handle approvals according to `approval`, and then revoke approvals.
    struct AutoTxData {
        /// @notice Destination contract to invoke.
        /// @dev Must be allowlisted via {setBungeeTarget}.
        address payable to;
        /// @notice Calldata for the Bungee route call.
        bytes data;
        /// @notice Extra ETH to attach to the call in addition to aggregated native inputs.
        uint256 value;
        /// @notice Optional ERC20 approval config.
        AutoApprovalData approval;
    }

    /// @notice Parameters for starting an auto Bungee operation.
    /// @dev
    /// Semantics match {ManualStartParams} but instead of `ManualBuildTx` we have fixed `autoTx`.
    ///
    /// `tokens`, `amounts`, `from`, `emergencyTo`, `nativeValue`:
    /// - behave exactly like in {ManualStartParams}.
    ///
    /// Staging / BREAK semantics are also identical:
    /// - We snapshot balance of `stageAsset` before and after the `autoTx` call (with ETH "before" adjusted
    ///   by subtracting the ETH we're about to forward).
    /// - Compute stagedDelta.
    /// - If `nextOp == BreakOps.BREAK_CODE`, require `stagedDelta >= minStageAmount`
    ///   and surface it to BREAK in `maskedParams`.
    struct AutoStartParams {
        /// @notice Input asset addresses (bytes32, `0` = native).
        bytes32[] tokens;
        /// @notice Input amounts matching `tokens`.
        /// @dev `type(uint256).max` means "use prevMaskedParams.amountOut" for that index".
        uint256[] amounts;
        /// @notice bytes32-encoded address from which ERC20 funds should be sourced.
        /// @dev Zero means "use prevMaskedParams.to". Otherwise must equal `msg.sender` on origin.
        bytes32 from;
        /// @notice Extra ETH to include on top of summed native inputs.
        uint256 nativeValue;
        /// @notice bytes32-encoded emergency recipient for fallback withdrawal.
        /// @dev Zero means "use prevMaskedParams.emergencyTo". Otherwise must equal `msg.sender` on origin.
        bytes32 emergencyTo;
        /// @notice Asset whose balance increase on the router we treat as staged output if we BREAK next.
        /// @dev bytes32-encoded token address, or zero for native ETH.
        bytes32 stageAsset;
        /// @notice Minimal acceptable staged amount of `stageAsset` after the call.
        /// @dev Only enforced if `nextOp == BreakOps.BREAK_CODE`.
        uint256 minStageAmount;
        /// @notice On-chain call data for the auto route.
        AutoTxData autoTx;
    }

    /// @notice One refund transfer instruction to be executed by {executeBungeeRefundOp}.
    /// @dev
    /// `token` encoding:
    /// - `token == bytes32(0)` means native ETH.
    /// - otherwise `token` is a bytes32-encoded ERC20 token address.
    ///
    /// `unwrapWETH`:
    /// - If `unwrapWETH == true`, then:
    ///   - `token` MUST be WETH (as configured in Core storage),
    ///   - the facet unwraps WETH into native ETH and sends ETH to the recipient.
    /// - If `token == 0` (native ETH), `unwrapWETH` MUST be false.
    struct RefundItem {
        /// @notice Asset identifier (bytes32-encoded token address; `0` = native ETH).
        bytes32 token;
        /// @notice Amount of the asset to refund.
        uint256 amount;
        /// @notice Whether to unwrap WETH into native ETH before sending.
        bool unwrapWETH;
    }

    /// @notice Parameters for a single refund payout executed by {executeBungeeRefundOp}.
    /// @dev
    /// This op is intended for backend-triggered refunds on the origin chain, paid from the router’s
    /// existing balance (funds must already be on the router at the time of execution).
    ///
    /// Constraints expected by the implementation:
    /// - `to` MUST decode to a non-zero address.
    /// - `refundId` MUST be non-zero and is used for idempotency / replay protection:
    ///   the facet marks `refundId` as used and reverts if the same `refundId` is submitted again.
    /// - `item.amount` MUST be non-zero.
    /// - `item.token` and `item.unwrapWETH` semantics are described in {RefundItem}.
    struct RefundParams {
        /// @notice bytes32-encoded recipient address.
        bytes32 to;
        /// @notice Mandatory idempotency / replay-protection key.
        bytes32 refundId;
        /// @notice Refund transfer instruction.
        RefundItem item;
    }

    /// @notice Scratch space for intermediate values in `executeBungeeManualOp` / `executeBungeeAutoOp`.
    /// @dev
    /// This struct exists only to avoid "stack too deep" during execution of the Bungee ops.
    /// Instead of keeping many separate local variables on the stack, the facet creates a single
    /// `LocalVars memory v;` and stores all working state there.
    ///
    /// High-level meaning:
    /// - We resolve who provides funds (`fromAddr`), pull ERC20s if needed, and possibly approve a spender.
    /// - We aggregate native (ETH) input and compute how much ETH must be forwarded into the external Bungee call.
    /// - We snapshot router balances of the expected "staging" asset before and after the Bungee call to determine
    ///   how much actually remained on the router (`stagedDelta`).
    /// - If the pipeline is about to BREAK, `stagedDelta` is enforced against `minStageAmount` and then surfaced
    ///   back through `maskedParams`.
    struct LocalVars {
        /// @notice Final resolved address whose funds are being used for this step.
        /// @dev
        /// Comes from `p.from` (or `prevMaskedParams.to` if `p.from == 0`) via `checkMaskedParams`.
        /// On origin calls, if `p.from` was non-zero it MUST equal `msg.sender`.
        address fromAddr;
        /// @notice ERC20 token address actually being spent for this Bungee call, if any.
        /// @dev
        /// `_collectInputs` enforces that all non-native inputs are the same ERC20;
        /// if there are no ERC20 inputs this will remain the zero address.
        address erc20TokenIn;
        /// @notice Asset we are tracking as "staged" on the router after the Bungee call.
        /// @dev
        /// This is `p.stageAsset` decoded to an `address`. `address(0)` means native ETH.
        /// We snapshot this asset’s balance on the router before and after the external call
        /// to measure how much value actually stayed here for the BREAK step.
        address stageAssetAddr;
        /// @notice Masked emergency withdrawal recipient that will be propagated downstream.
        /// @dev
        /// Obtained from `checkMaskedParams`. If a non-zero emergency recipient was provided
        /// on origin, it must equal `msg.sender`; otherwise we fall back to `prevMaskedParams.emergencyTo`.
        bytes32 emergencyTo;
        /// @notice Total native amount requested from inputs.
        /// @dev
        /// Sum of all `p.amounts[i]` where the corresponding `p.tokens[i] == address(0)`.
        /// Represents user-supplied ETH that should go into the Bungee call.
        uint256 nativeRequired;
        /// @notice Total ERC20 amount being provided across inputs for `erc20TokenIn`.
        /// @dev
        /// `_collectInputs` accumulates this. If `fromAddr` is not the router itself,
        /// we `safeTransferFrom` this amount to the router before approval/call.
        uint256 erc20TotalIn;
        /// @notice The ERC20 allowance amount we grant to the downstream spender.
        /// @dev
        /// For manual mode: `max(erc20TotalIn, minimumApprovalAmount)`.
        /// For auto mode:   `max(erc20TotalIn, autoTx.approval.amount)`.
        /// After the call we always reset this allowance back to zero.
        uint256 approveAmount;
        /// @notice Router balance of `stageAssetAddr` before performing the external Bungee call.
        /// @dev
        /// For ETH, this is `address(this).balance`.
        /// For ERC20, this is `IERC20(stageAssetAddr).balanceOf(address(this))`.
        uint256 balBefore;
        /// @notice Exact ETH value that will be forwarded to the external Bungee call.
        /// @dev
        /// Computed as `nativeRequired + p.nativeValue + buildTx.value` (manual)
        /// or `nativeRequired + p.nativeValue + autoTx.value` (auto).
        /// `_checkMsgValue` enforces:
        ///  - On origin call: `msg.value` MUST equal this.
        ///  - On resume call: `msg.value` MUST be 0 and the router balance MUST already cover it.
        uint256 valueToSend;
        /// @notice Router balance of `stageAssetAddr` after the external Bungee call returns (and after approvals are cleared).
        /// @dev
        /// Measured the same way as `balBefore`.
        uint256 balAfter;
        /// @notice Adjusted "before" balance used to measure the actually staged output.
        /// @dev
        /// For ETH, we conceptually subtract `valueToSend` from `balBefore` so we do not count
        /// ETH that we intentionally forwarded into the external call as if it remained staged.
        /// For ERC20, this is simply `balBefore`.
        uint256 effectiveBefore;
        /// @notice Net amount of `stageAssetAddr` that truly ended up staged on the router.
        /// @dev
        /// Computed as `balAfter - effectiveBefore`.
        /// If `nextOp == BreakOps.BREAK_CODE`, this must be `>= p.minStageAmount`, otherwise we revert.
        /// On success in BREAK mode, this becomes `maskedParams.amountOut` and we mark
        /// `maskedParams.to = address(this)` so the BREAK step knows funds are held by the router.
        uint256 stagedDelta;
    }

    /// @notice Emitted once per refunded item when {executeBungeeRefundOp} performs a payout.
    /// @dev
    /// - `refundId` is the idempotency key provided in {RefundParams}. May be zero.
    /// - `token` reflects the item token (native ETH is represented as `address(0)`).
    /// - If `unwrapped == true`, the item source token was WETH and the user received native ETH.
    ///   (I.e., transfer asset is ETH, while the source token is WETH.)
    /// @param refundId The refund id used for replay protection (may be zero).
    /// @param to The recipient of the refund.
    /// @param token The asset identifier: `address(0)` for native ETH, otherwise ERC20 token address.
    /// @param amount The amount refunded.
    /// @param unwrapped True if WETH was unwrapped into native ETH before sending.
    event RefundExecuted(
        bytes32 indexed refundId,
        address indexed to,
        address indexed token,
        uint256 amount,
        bool unwrapped
    );

    /// @notice Executes the manual Bungee op.
    /// @dev
    /// Two routing modes depending on `nextOp`:
    ///
    /// 1. Terminal mode:
    ///    - `nextOp == bytes32(0)`.
    ///    - We:
    ///        * pull/aggregate inputs (ERC20 and/or native),
    ///        * optionally approve `approvalData.allowanceTarget`,
    ///        * call `buildTx.txTarget` with the composed ETH value,
    ///        * reset approval to zero.
    ///    - We DO NOT require or expose staged output.
    ///    - Return convention:
    ///        * `chainIdTo = 0`
    ///        * `updatedParams = ""`
    ///        * `maskedParams.amountOut = 0`
    ///        * `maskedParams.to = 0`
    ///        * `maskedParams.emergencyTo = propagated emergencyTo`
    ///        * `result = ICoreFacet.ExecutionResult.Succeeded`
    ///
    /// 2. Pre-break mode:
    ///    - `nextOp == BreakOps.BREAK_CODE`.
    ///    - After the external call succeeds, we snapshot how much of `p.stageAsset`
    ///      actually ended up sitting on the router ("stagedDelta"), where for ETH we
    ///      adjust the "before" snapshot by subtracting the ETH we were about to forward.
    ///    - We REQUIRE `stagedDelta >= p.minStageAmount`, otherwise revert.
    ///    - Return convention:
    ///        * `chainIdTo = 0`
    ///        * `updatedParams = ""`
    ///        * `maskedParams.amountOut = stagedDelta`
    ///        * `maskedParams.to = address(this)` (router now holds staged funds)
    ///        * `maskedParams.emergencyTo = propagated emergencyTo`
    ///        * `result = ICoreFacet.ExecutionResult.Succeeded`
    ///
    /// msg.value rules (enforced internally via `_checkMsgValue`):
    /// - On origin call (`CoreFacetStorage.currentRequestId() == 0`), `msg.value` MUST equal
    ///   the ETH we are about to forward downstream.
    /// - On resume call, `msg.value` MUST be zero, and the router's own ETH balance MUST
    ///   already cover the required call value.
    ///
    /// @param isOpHalfDone Reserved flag propagated by the router, ignored by this facet.
    /// @param op Operation code; must equal `BUNGEE_MANUAL_START_CODE`.
    /// @param nextOp Next pipeline step. Must be either:
    ///        - `bytes32(0)` for terminal mode, or
    ///        - `BreakOps.BREAK_CODE` if we intend to pause after staging funds.
    /// @param params ABI-encoded {ManualStartParams} describing inputs, approvals, call target, etc.
    /// @param prevMaskedParams Masked params from the previous pipeline step. Supplies fallback
    ///        `from`, `emergencyTo`, and previous `amountOut` for sentinel `type(uint256).max`.
    /// @return chainIdTo Always 0. This facet does not itself schedule a cross-chain hop.
    /// @return updatedParams Always empty bytes (`""`). No param mutation for downstream ops.
    /// @return maskedParams
    ///         - Terminal mode: {amountOut=0, to=0, emergencyTo=propagated}
    ///         - Pre-break mode: {amountOut=stagedDelta, to=router, emergencyTo=propagated}
    /// @return result Always `ICoreFacet.ExecutionResult.Succeeded` if we didn't revert.
    function executeBungeeManualOp(
        bool isOpHalfDone,
        bytes32 op,
        bytes32 nextOp,
        bytes calldata params,
        ICoreFacet.MaskedParams calldata prevMaskedParams
    )
        external
        payable
        returns (
            uint64 chainIdTo,
            bytes memory updatedParams,
            ICoreFacet.MaskedParams memory maskedParams,
            ICoreFacet.ExecutionResult result
        );

    /// @notice Executes the auto Bungee op.
    /// @dev
    /// Same behavioral contract as {executeBungeeManualOp}, except that:
    /// - Instead of `ManualBuildTx`, we use `AutoTxData` (which is expected to be known/curated on-chain).
    /// - All approvals / target contracts must still pass allowlist checks.
    ///
    /// Modes:
    ///
    /// 1. Terminal mode (`nextOp == 0`):
    ///    - Pull inputs (ERC20/native), approve if needed, call `autoTx.to` with `autoTx.data`
    ///      and the computed ETH value, then revoke approvals.
    ///    - Return masked params with `amountOut=0`, `to=0`, `emergencyTo` propagated.
    ///
    /// 2. Pre-break mode (`nextOp == BreakOps.BREAK_CODE`):
    ///    - Same call flow, but we also:
    ///        * snapshot router balance of `p.stageAsset` before/after (ETH "before" is adjusted
    ///          by subtracting the ETH we're about to forward),
    ///        * compute `stagedDelta`,
    ///        * require `stagedDelta >= p.minStageAmount`,
    ///        * return `amountOut=stagedDelta`, `to=router`, `emergencyTo` propagated.
    ///
    /// msg.value rules:
    /// - On origin call, `msg.value` MUST equal the ETH to be forwarded.
    /// - On resume call, `msg.value` MUST be 0 and the router must already hold enough ETH.
    ///
    /// @param isOpHalfDone Reserved pipeline flag, ignored here.
    /// @param op Operation code; must equal `BUNGEE_AUTO_START_CODE`.
    /// @param nextOp Must be either zero (terminal) or `BreakOps.BREAK_CODE` (break/stage).
    /// @param params ABI-encoded {AutoStartParams}.
    /// @param prevMaskedParams Previous step's masked params for fallback `from`, `emergencyTo`,
    ///        and substitution for `type(uint256).max` amounts.
    /// @return chainIdTo Always 0. Auto route itself doesn't hop chains here.
    /// @return updatedParams Always empty (`""`). No param forwarding mutation.
    /// @return maskedParams
    ///         - Terminal mode: {amountOut=0, to=0, emergencyTo=propagated}
    ///         - Pre-break mode: {amountOut=stagedDelta, to=router, emergencyTo=propagated}
    /// @return result Always `ICoreFacet.ExecutionResult.Succeeded` if successful.
    function executeBungeeAutoOp(
        bool isOpHalfDone,
        bytes32 op,
        bytes32 nextOp,
        bytes calldata params,
        ICoreFacet.MaskedParams calldata prevMaskedParams
    )
        external
        payable
        returns (
            uint64 chainIdTo,
            bytes memory updatedParams,
            ICoreFacet.MaskedParams memory maskedParams,
            ICoreFacet.ExecutionResult result
        );
    
    /// @notice Executes a backend-triggered refund from the router’s balance on the origin network.
    /// @dev
    /// Purpose:
    /// - Some Bungee/Socket routes may produce delayed refunds (funds arrive to the router later).
    /// - This op allows an operator to transfer already-received funds out to the user.
    ///
    /// Execution constraints (enforced by implementation):
    /// - `op` MUST equal `BungeeOps.BUNGEE_REFUND_CODE`.
    /// - `nextOp` MUST be `bytes32(0)` (refund is always terminal).
    /// - Must run on the origin network only: `CoreFacetStorage.isOriginNetwork() == true`.
    /// - Caller must be the diamond (`onlyDiamond` in the facet implementation); access control
    ///   may additionally be enforced at the router/diamond level.
    ///
    /// Idempotency / replay protection:
    /// - `RefundParams.refundId` MUST be non-zero.
    /// - The facet records `refundId` as consumed and reverts if it was already used.
    ///
    /// Refund item semantics:
    /// - If `item.token == 0`: sends native ETH; `item.unwrapWETH` MUST be false.
    /// - If `item.token != 0 && item.unwrapWETH == false`: transfers ERC20 token via `safeTransfer`.
    /// - If `item.unwrapWETH == true`: `item.token` MUST equal WETH configured in Core storage;
    ///   the facet unwraps WETH into ETH and sends native ETH.
    ///
    /// Events:
    /// - Emits {RefundExecuted} once for this payout.
    ///
    /// @param isOpHalfDone Reserved pipeline flag propagated by the router; ignored by this op.
    /// @param op Operation code; must equal `BUNGEE_REFUND_CODE`.
    /// @param nextOp Must be `bytes32(0)` (terminal).
    /// @param params ABI-encoded {RefundParams}.
    /// @param prevMaskedParams Unused for refunds (present for unified op interface).
    /// @return chainIdTo Always 0 (no cross-chain hop is scheduled by this op).
    /// @return updatedParams Always empty bytes (`""`).
    /// @return maskedParams Always zeroed (refund op does not produce staged output).
    /// @return result Always {ICoreFacet.ExecutionResult.Succeeded} if the op does not revert.
    function executeBungeeRefundOp(
        bool isOpHalfDone,
        bytes32 op,
        bytes32 nextOp,
        bytes calldata params,
        ICoreFacet.MaskedParams calldata prevMaskedParams
    )
        external
        returns (
            uint64 chainIdTo,
            bytes memory updatedParams,
            ICoreFacet.MaskedParams memory maskedParams,
            ICoreFacet.ExecutionResult result
        );

    /// @notice Adds or removes a downstream call target from the allowlist.
    /// @dev
    /// - `target` cannot be the zero address.
    /// - Only callable by an account with `OPERATOR_ROLE` on the diamond.
    /// - Calls to Bungee (Socket) routers and helpers MUST go through allowlisted targets.
    ///
    /// @param target The external contract that this facet is allowed to call.
    /// @param allowed Whether that contract address is considered allowed.
    function setBungeeTarget(address target, bool allowed) external;

    /// @notice Adds or removes an address from the spender allowlist.
    /// @dev
    /// - `spender` cannot be the zero address.
    /// - Only callable by an account with `OPERATOR_ROLE` on the diamond.
    /// - Before giving any ERC20 allowance, the facet checks that the spender is allowlisted.
    ///
    /// @param spender The spender (router / proxy / aggregator) to add or remove.
    /// @param allowed Whether this spender address is allowed to receive approvals.
    function setBungeeSpender(address spender, bool allowed) external;

    /// @notice Returns whether a given `target` is currently allowlisted as a valid Bungee call target.
    /// @param target The contract address to check.
    /// @return isAllowed True if `target` is allowed, false otherwise.
    function isBungeeTargetAllowed(address target) external view returns (bool isAllowed);

    /// @notice Returns whether a given `spender` is allowlisted for ERC20 approvals.
    /// @param spender The spender address to check.
    /// @return isAllowed True if `spender` is allowed, false otherwise.
    function isBungeeSpenderAllowed(address spender) external view returns (bool isAllowed);
}

// SPDX-License-Identifier: UNLICENSED
// Copyright (c) Eywa.Fi, 2021-2025 - all rights reserved
pragma solidity 0.8.25;

import {ProcessedOps} from "../libraries/CoreFacetStorage.sol";

interface ICoreFacet {
    /// @notice Result of an execution step.
    enum ExecutionResult {
        /// @dev The operation failed.
        Failed,
        /// @dev The operation succeeded.
        Succeeded,
        /// @dev The operation was interrupted and should resume on the next chain.
        Interrupted
    }

    /// @notice State of a cross-chain operation tracked by requestId.
    enum CrossChainOpState {
        /// @dev Operation has not yet been processed.
        Unknown,
        /// @dev Operation successfully completed.
        Succeeded,
        /// @dev Operation was reverted (e.g. emergency cancel).
        Reverted
    }

    /// @notice Signed invoice data for execution payment.
    struct Invoice {
        uint256 executionPrice;
        uint256 deadline;
        uint8 v;
        bytes32 r;
        bytes32 s;
    }

        /// @notice Signed invoice data for execution payment.
    struct InvoiceV4 {
        Invoice invoice;
        uint256 feeShare;
        address feeShareRecipient;
        address feeToken;
    }

    /// @notice Holds result values between cross-chain operation steps.
    struct MaskedParams {
        uint256 amountOut;
        bytes32 to;
        bytes32 emergencyTo;
    }

    /// @notice Execution context passed between operation steps.
    struct ExecutionContext {
        MaskedParams maskedParams;
        bytes updatedParams;
    }

    /// @notice Emitted when a cross-chain operation completes or transfers to another chain.
    /// @param currentChainId The chain ID where the event occurred.
    /// @param currentRequestId The current request ID being processed.
    /// @param nextChainId Target chain ID if continuation is needed.
    /// @param nextRequestId New request ID for the next chain.
    /// @param result Result of the execution.
    /// @param lastOp Index of the last successfully executed operation.
    event ComplexOpProcessed(
        uint64 indexed currentChainId,
        bytes32 indexed currentRequestId,
        uint64 indexed nextChainId,
        bytes32 nextRequestId,
        ExecutionResult result,
        uint8 lastOp
    );

    /// @notice Emitted when execution fees are paid.
    /// @param payer The address that submitted the request and paid the fee.
    /// @param token The token in which fee was paid.
    /// @param accountant The address that received the fee.
    /// @param executionPrice Amount of ETH paid for the execution.
    event FeePaid(address indexed payer, address token, address accountant, uint256 executionPrice);

    /// @notice Sets the address book contract address.
    /// @param addressBook_ The address of the address book contract.
    /// @dev Can only be called by an account with DEFAULT_ADMIN_ROLE.
    function setAddressBook(address addressBook_) external;

    /// @notice Caches WETH from address book.
    /// @dev Can only be called by an account with DEFAULT_ADMIN_ROLE.
    function resetWETH() external;

    /// @notice Caches Treasury from address book.
    /// @dev Can only be called by an account with DEFAULT_ADMIN_ROLE.
    function resetTreasury() external;

    /// @notice Verifies that the caller is the trusted router from the source chain and correct selector is passed.
    /// @param selector The function selector, expected to be `ICoreFacet.resume.selector`.
    /// @param from The sender address from the source chain.
    /// @param chainIdFrom The chain ID from which the call originated.
    /// @return True if validation passed, otherwise reverts.
    function receiveValidatedData(
        bytes4 selector,
        bytes32 from,
        uint64 chainIdFrom
    ) external returns (bool);

    /// @notice Starts the execution of a cross-chain operation on the origin network.
    /// @param operations List of operation names to execute.
    /// @param params Encoded parameters for each operation.
    /// @param receipt Signed receipt (invoice) for fee validation.
    /// @param bridgeOptions Encoded options for bridging to other chains.
    function start(
        string[] calldata operations,
        bytes[] calldata params,
        InvoiceV4 calldata receipt,
        bytes memory bridgeOptions
    ) external payable;

    /// @notice Resumes the execution of a cross-chain operation on the destination network.
    /// @param requestId The request ID of the cross-chain operation.
    /// @param cPos The index of the operation to resume from.
    /// @param operations List of operation names.
    /// @param params Parameters for each operation.
    /// @param bridgeOptions Encoded options for continued bridging.
    /// @dev Can only be called by the configured receiver and requires valid requestId context.
    function resume(
        bytes32 requestId,
        uint8 cPos,
        string[] calldata operations,
        bytes[] memory params,
        bytes memory bridgeOptions
    ) external;

    /// @notice Current nonce counter for a given address.
    /// @param account User address whose nonce is requested.
    /// @return Present value of `RouterStorage.DS.nonces[account]`.
    function nonces(address account) external view returns (uint256);

    /// @notice Parameters hash with which a cross-chain request was started.
    /// @param requestId Identifier of the cross-chain request.
    /// @return keccak256 hash stored in `RouterStorage.DS.startedOps[requestId]`.
    function startedOps(bytes32 requestId) external view returns (bytes32);

    /// @notice Processing state of a cross-chain request.
    /// @param requestId Identifier of the cross-chain request.
    /// @return The structure with information about cross-chain transaction delivery (see defination of ProcessedOps).
    function processedOps(bytes32 requestId) external view returns (ProcessedOps memory);

    /// @notice Cached AddressBook contract address.
    /// @return Address stored in `RouterStorage.DS.AddressBook`.
    function addressBook() external view returns (address);

    /// @notice Cached Treasury contract address.
    /// @return Address stored in `RouterStorage.DS.treasury`.
    function treasury() external view returns (address);

    /// @notice Returns address of wrapped ETH contract.
    /// @return The address of wrapped ETH contract.
    function WETH() external view returns (address);
}

// SPDX-License-Identifier: UNLICENSED
// Copyright (c) Eywa.Fi, 2021-2025 - all rights reserved
pragma solidity 0.8.25;

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


interface ICrosschainFacet {
    /// @notice Parameters for ERC20 `permit` signature-based approval.
    struct PermitParams {
        bytes32 token;
        bytes32 owner;
        uint256 amount;
        uint256 deadline;
        uint8 v;
        bytes32 r;
        bytes32 s;
    }

    /// @notice Parameters for synthetic token operations (mint/burn/lock/unlock).
    struct SynthParams {
        bytes32 tokenIn;
        uint256 amountIn;
        bytes32 from;
        bytes32 to;
        uint64 chainIdTo;
        uint64 tokenInChainIdFrom;
        bytes32 emergencyTo;
    }

    /// @notice Parameters for wrapping and unwrapping tokens (e.g. ETH <-> WETH).
    struct WrapParams {
        bytes32 tokenIn;
        uint256 amountIn;
        bytes32 from;
        bytes32 to;
    }

    /// @notice Parameters for emergency cancellation of a cross-chain request.
    struct CancelParams {
        bytes32 requestId;
        bytes32 hashSynthParams;
        uint64 chainIdTo;
        SynthParams emergencyParams;
    }

    /// @notice Handles main operational logic for core operation types (permit, mint, lock, etc.).
    /// @param isOpHalfDone True if operation is continuing from another chain.
    /// @param op Current operation code.
    /// @param nextOp Next operation code.
    /// @param params Parameters for the operation.
    /// @param prevMaskedParams Previous masked parameters to inherit state.
    /// @return chainIdTo Target chain ID (if any).
    /// @return updatedParams Updated params to pass for cross-chain.
    /// @return maskedParams Resulting masked params.
    /// @return result Execution result (Succeeded, Failed, or Interrupted).
    function executeCrosschainOp(
        bool isOpHalfDone,
        bytes32 op,
        bytes32 nextOp,
        bytes memory params,
        ICoreFacet.MaskedParams memory prevMaskedParams
    ) 
        external
        payable
        returns (
            uint64 chainIdTo,
            bytes memory updatedParams,
            ICoreFacet.MaskedParams memory maskedParams,
            ICoreFacet.ExecutionResult result
        );
}

// SPDX-License-Identifier: UNLICENSED
// Copyright (c) Eywa.Fi, 2021-2025 - all rights reserved
pragma solidity 0.8.25;

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


interface ICurveFacet {
    /// @notice Parameters for adding liquidity to a pool.
    struct AddParams {
        bytes32 tokenIn;
        uint256 amountIn;
        bytes32 from;
        bytes32 to;
        bytes32 pool;
        uint256 minAmountOut;
        uint8 i;
        bytes32 emergencyTo;
    }

    /// @notice Parameters for removing liquidity from a pool.
    struct RemoveParams {
        bytes32 tokenIn;
        uint256 amountIn;
        bytes32 from;
        bytes32 to;
        bytes32 pool;
        uint256 minAmountOut;
        uint8 j;
        bytes32 emergencyTo;
    }

    /// @notice Parameters for performing a token swap.
    struct SwapParams {
        bytes32 tokenIn;
        uint256 amountIn;
        bytes32 from;
        bytes32 to;
        bytes32 pool;
        uint256 minAmountOut;
        uint8 i;
        uint8 j;
        bytes32 emergencyTo;
    }

    /// @notice Emitted when a new adapter is set for a liquidity pool.
    /// @param pool The address of the liquidity pool.
    /// @param adapter The address of the adapter associated with the pool.
    event PoolAdapterSet(address pool, address adapter);

    /// @notice Sets the adapter for a specific liquidity pool.
    /// @param pool_ The address of the liquidity pool.
    /// @param poolAdapter_ The address of the corresponding pool adapter.
    /// @dev Can only be called by an account with OPERATOR_ROLE.
    /// Reverts if the pool address is zero.
    function setPoolAdapter(
        address pool_,
        address poolAdapter_
    ) external;

    /// @notice Handles core (permit, mint, lock, etc.) and pool-related operations (add, remove, swap).
    /// @param isOpHalfDone True if operation is resuming.
    /// @param op Operation code.
    /// @param nextOp Next operation code.
    /// @param params Parameters for the operation.
    /// @param prevMaskedParams Previously accumulated masked params.
    /// @return chainIdTo Target chain ID.
    /// @return updatedParams Updated parameters (for cross-chain execution).
    /// @return maskedParams Updated masked state.
    /// @return result Execution result.
    function executeCurveAMMOp(
        bool isOpHalfDone,
        bytes32 op,
        bytes32 nextOp,
        bytes memory params,
        ICoreFacet.MaskedParams memory prevMaskedParams
    ) 
        external 
        payable 
        returns (
            uint64 chainIdTo, 
            bytes memory updatedParams, 
            ICoreFacet.MaskedParams memory maskedParams,
            ICoreFacet.ExecutionResult result
        );

    /// @notice Adapter contract that the router uses for a specific liquidity pool.
    /// @param pool Address of the liquidity pool.
    /// @return Address stored in `RouterStorage.DS.poolAdapter[pool]`.
    function poolAdapter(address pool) external view returns (address);
}

// SPDX-License-Identifier: UNLICENSED
// Copyright (c) Eywa.Fi, 2021-2025 - all rights reserved
pragma solidity 0.8.25;

import {ICoreFacet} from "./ICoreFacet.sol";
import {ResumedOps} from "../libraries/OFTFacetStorage.sol";

interface IOFTFacet {
    /// @notice State of a lzCompose operation tracked by guid.
    enum OftOpState {
        /// @dev Operation has not yet been processed.
        Unknown,
        /// @dev Operation successfully completed.
        Succeeded,
        /// @dev Operation was reverted (e.g. emergency cancel).
        Reverted,
        /// @dev Operation was corrected.
        Pushed
    }

    /// @notice Patterns of interaction between the OFT contract and the token.
    enum OftType {
        /// @dev The token itself supports the OFT standard.
        Token,
        /// @dev The OFT contract is the owner of the token.
        Owner,
        /// @dev The OFT contract is an adapter for the token.
        Adapter
    }

    /// @notice Parameters for send OFT token operations to dist chain.
    struct OftParams {
        bytes32 oft;
        uint256 amountIn;
        bytes32 from;
        bytes32 to;
        uint64 chainIdTo;
        uint32 dstEid;
        bytes extraOptions;
        uint256 nativeFeeLimit;
        OftType oftTypeSource;
        OftType oftTypeDist;
        bytes32 emergencyTo;
    }

    /// @notice Parameters for execute oft operation.
    /// @param cPos Current operation number.
    /// @param operationsCode Array of operation codes.
    /// @param operations Array of operation names to execute.
    /// @param params Array of encoded parameters for each operation.
    /// @param bridgeOptions Encoded bridge-related options for further continuation.
    /// @param prevMaskedParams Previous masked parameters to inherit state.
    struct ExecuteOftParams {
        uint256 cPos;
        bytes32[] operationsCode;
        string[] operations;
        bytes[] params;
        bytes bridgeOptions;
        ICoreFacet.MaskedParams prevMaskedParams;
    }

    /// @notice Parameters for emergency oft operations.
    struct EmergencyOftParams {
        bytes32 guid;
        OftParams oftParams;
    }

    /// @notice Parameters for push OFT token operations in dist chain.
    struct pushOftParams {
        bytes32 guid;
    }

    /// @notice Emitted when the new value of endPoint is set.
    /// @param endPoint The endPoint address.
    event SetEndPoint(address endPoint);

    /// @notice Emitted when a cross-chain operation completes or transfers to another chain.
    /// @param guid The current ID being processed.
    /// @param nextChainId Target chain ID if continuation is needed.
    /// @param oftOpState The state of OFT operation.
    event OFTProcessed(
        bytes32 indexed guid,
        uint64 indexed nextChainId,
        OftOpState indexed oftOpState
    );

    /// @notice Handles main operational logic for send OFT tokens operation.
    /// @param ep Parameters for execute oft operation
    /// @return chainIdTo Id of the chain to which OFT tokens were sent.
    /// @return updatedParams Id of operation in Layer Zero protocol.
    /// @return maskedParams Updated masked parameters to inherit state.
    /// @return result xecution result (Succeeded, Failed, or Interrupted).
    function executeOftOp(
        ExecuteOftParams memory ep
    ) 
        external
        payable
        returns (
            uint64 chainIdTo,
            bytes memory updatedParams,
            ICoreFacet.MaskedParams memory maskedParams,
            ICoreFacet.ExecutionResult result
    );

    /// @notice Processing a message sent with OFT tokens.
    /// @param from OFT token address.
    /// @param guid Id of operation in Layer Zero protocol.
    /// @param message Message with parameters for further processing.
    /// @param sender Executor address.
    /// @param extraData Options for sending a message.
    function lzCompose(
        address from,
        bytes32 guid,
        bytes calldata message,
        address sender,
        bytes calldata extraData
    ) external payable;

    /// @notice Sets the EndPoint contract address.
    /// @param endPoint The address of the EndPoint contract.
    /// @dev Can only be called by an account with DEFAULT_ADMIN_ROLE.
    function setEndPoint(address endPoint) external;

    /// @notice Retrun EndPoint contract address.
    /// @return Address stored in `OFTFacetStorage.DS.endPoint`.
    function endPoint() external view returns(address);

    /// @notice struct whith hashs of the BMo operation and subsequent operations obtained in the lzCompose() function
    /// @param guid The id of LZ operation.
    /// @return keccak256 hash stored in `OFTFacetStorage.DS.resumedOps[guid]`.
    function resumedOps(bytes32 guid) external view returns (ResumedOps memory);

    /// @notice Processing state of a lzCompose request.
    /// @param guid The id of LZ operation.
    /// @return 0 – Unknown, 1 – Succeeded, 2 – Reverted (see `CrossChainOpState`).
    function processedOftOps(bytes32 guid) external view returns (uint8);
}

// SPDX-License-Identifier: UNLICENSED
// Copyright (c) Eywa.Fi, 2021-2025 - all rights reserved
pragma solidity 0.8.25;

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

interface IRubicFacet {
    /// @notice Parameters to start a Rubic route from the router.
    /// @dev
    /// - `tokens[i]` is the input asset address encoded as bytes32. `address(0)` (cast from bytes32) means native token (ETH).
    /// - `amounts[i]` is the amount for `tokens[i]`. If `amounts[i] == type(uint256).max`, the facet will substitute
    ///   `prevMaskedParams.amountOut` for that entry.
    ///
    /// - `from` is a bytes32-encoded address indicating whose funds to pull:
    ///     * If `from == 0`, the facet will fallback to `prevMaskedParams.to`
    ///       (i.e. use the output holder from the previous op in the pipeline).
    ///     * If `from != 0`, then on the origin call it MUST match `msg.sender`.
    ///
    /// - `emergencyTo` is the emergency withdrawal recipient:
    ///     * If `emergencyTo == 0`, we fallback to `prevMaskedParams.emergencyTo`.
    ///     * If `emergencyTo != 0`, then on the origin call it MUST match `msg.sender`.
    ///
    /// - `nativeValue` is extra ETH that must be forwarded to Rubic in addition to any ETH that appears
    ///   in `tokens[i] == address(0)`. The facet enforces msg.value rules so that:
    ///     * on origin call, `msg.value` must exactly equal the ETH we're going to forward;
    ///     * on resume call, `msg.value` must be 0 and the router must already hold enough ETH.
    ///
    /// - `stageAsset` / `minStageAmount` are only enforced if the router is about to `break`
    ///   (i.e. `nextOp == BreakOps.BREAK_CODE`):
    ///     * `stageAsset` is the asset we expect to remain "staged" (left behind) on the router after Rubic finishes.
    ///       It is encoded as bytes32; `address(0)` means native ETH.
    ///     * The facet snapshots the router's balance of `stageAsset` before and after the Rubic call.
    ///       For ETH, the "before" snapshot is adjusted by subtracting the ETH we are about to forward,
    ///       so we don't count forwarded call value as staged profit.
    ///     * The delta is the actually staged amount. The facet requires `delta >= minStageAmount`.
    ///     * If that check passes, the facet will report this staged amount through `maskedParams.amountOut`
    ///       and will set `maskedParams.to = address(this)` so the next step (the break) knows the funds
    ///       are already sitting on the router.
    struct RubicStartParams {
        /// @notice Input asset addresses as bytes32; `0` means native token.
        bytes32[] tokens;
        /// @notice Amounts for each input asset; can be `type(uint256).max` to reuse `prevMaskedParams.amountOut`.
        uint256[] amounts;
        /// @notice Opaque calldata blob for Rubic's proxy (`startViaRubic`).
        bytes facetCallData;
        /// @notice Source of funds (bytes32-encoded address). `0` means "use prevMaskedParams.to".
        bytes32 from;
        /// @notice Extra ETH that must be sent into Rubic on top of summed native inputs.
        uint256 nativeValue;
        /// @notice Emergency recipient. `0` means "use prevMaskedParams.emergencyTo".
        bytes32 emergencyTo;
        /// @notice Asset we require to stay staged on the router if the next op is BREAK.
        /// @dev bytes32-encoded token address; `0` means native ETH.
        bytes32 stageAsset;
        /// @notice Minimal acceptable staged amount of `stageAsset` after the Rubic call,
        /// @dev enforced only when `nextOp == BreakOps.BREAK_CODE`.
        uint256 minStageAmount;
    }

    /// @notice Scratch container for intermediate values in `executeRubicOp`.
    /// @dev
    /// This struct exists purely to avoid "stack too deep" in `executeRubicOp`.
    /// Instead of keeping many independent local variables (which quickly exceeds the
    /// stack slot limit), we group them under a single in-memory struct `LocalVars v`.
    ///
    /// High-level meaning of the fields:
    /// - We resolve who is the actual payer of ERC20 inputs (`fromAddr`), and we may pull
    ///   their tokens into the router.
    /// - We accumulate total native input required by the Rubic route (`nativeRequired`)
    ///   and compute the final ETH that must be forwarded to Rubic (`valueToSend`).
    /// - We snapshot router balances of the expected "staging" asset before and after
    ///   calling Rubic (`balBefore`, `balAfter`), then compute how much actually remained
    ///   on the router (`stagedDelta`). This is enforced against `minStageAmount` when
    ///   `nextOp == BreakOps.BREAK_CODE`.
    /// - We also keep the Rubic proxy address (`proxy`), the chosen staging asset
    ///   (`stageAssetAddr`), and the deduplicated ERC20 count (`idx`).
    /// - We carry forward the masked emergency recipient (`emergencyTo`) to propagate it
    ///   into `maskedParams.emergencyTo`.
    struct LocalVars {
        /// @notice Final resolved address that provides ERC20 inputs for this Rubic step.
        /// @dev
        /// Derived from `p.from` and `prevMaskedParams.to` using `checkMaskedParams`.
        /// If `p.from` was zero, this will typically be the previous op's output holder.
        /// Otherwise, on origin calls, it must match `msg.sender`.
        address fromAddr;
        /// @notice Address of the Rubic ERC20 proxy we will call (`startViaRubic`).
        /// @dev
        /// Loaded from facet storage (`RubicFacetStorage.ds().erc20Proxy`). Must be non-zero
        /// or we revert. Used both for approvals and for the external call.
        address proxy;
        /// @notice Asset we are tracking as "staged" on the router after Rubic finishes.
        /// @dev
        /// This is `p.stageAsset` decoded to `address`. `address(0)` means native ETH.
        /// We snapshot this asset's balance on the router before/after the Rubic call
        /// to measure how much value actually remained here to be consumed by BREAK.
        address stageAssetAddr;
        /// @notice Total native amount requested by the route inputs.
        /// @dev
        /// This is the sum of all `p.amounts[i]` where `p.tokens[i] == address(0)`.
        /// It reflects the ETH that is part of the user-supplied swap/bridge input.
        uint256 nativeRequired;
        /// @notice The exact ETH value that must be forwarded to the Rubic proxy.
        /// @dev
        /// Computed as `nativeRequired + p.nativeValue`.
        /// Enforced via `_checkMsgValue`:
        ///  - On origin calls, `msg.value` must equal this.
        ///  - On resume calls, `msg.value` must be zero and the router must already
        ///    hold at least this much ETH.
        uint256 valueToSend;
        /// @notice Router balance of `stageAssetAddr` before calling Rubic.
        /// @dev
        /// For ETH, this is `address(this).balance`.
        /// For ERC20, this is `IERC20(stageAssetAddr).balanceOf(address(this))`.
        /// Captured to later compute how much actually stayed on the router.
        uint256 balBefore;
        /// @notice Router balance of `stageAssetAddr` after calling Rubic.
        /// @dev
        /// Same measurement logic as `balBefore`, but taken after `startViaRubic`
        /// returns and after we reset approvals.
        uint256 balAfter;
        /// @notice Adjusted "before" balance used to measure staged value.
        /// @dev
        /// For ETH, we conceptually subtract the ETH we intentionally forwarded
        /// (`valueToSend`) so we do not count that outgoing ETH as if it had
        /// "remained on the router". For ERC20, this is just `balBefore`.
        uint256 effectiveBefore;
        /// @notice Net amount of `stageAssetAddr` that actually ended up staged on the router.
        /// @dev
        /// Computed as `balAfter - effectiveBefore`.
        /// If `nextOp == BreakOps.BREAK_CODE`, we require `stagedDelta >= p.minStageAmount`.
        /// When that holds, we propagate `stagedDelta` via `maskedParams.amountOut`
        /// and mark `maskedParams.to = address(this)` so BREAK knows funds are on the router.
        uint256 stagedDelta;
        /// @notice Number of distinct ERC20 tokens that we aggregated for this Rubic call.
        /// @dev
        /// While iterating inputs we merge same-token amounts. `idx` is how many unique
        /// tokens we ended up with. We then truncate the temporary `erc20Tokens` /
        /// `erc20Amounts` arrays to length `idx` before calling `startViaRubic`.
        uint256 idx;
        /// @notice Masked emergency withdrawal recipient to forward in `maskedParams.emergencyTo`.
        /// @dev
        /// Comes from `checkMaskedParams`, which enforces (on origin) that if a non-zero
        /// emergency address is provided it must equal `msg.sender`, or otherwise falls
        /// back to `prevMaskedParams.emergencyTo`.
        bytes32 emergencyTo;
    }

    /// @notice Executes the Rubic step in the pipeline.
    /// @dev
    /// Flow types:
    ///
    /// 1. Terminal flow (no break next):
    ///    - `nextOp == bytes32(0)`.
    ///    - Facet pulls/approves inputs, forwards assets into Rubic, calls Rubic proxy,
   ///      and then clears approvals.
    ///    - The pipeline stops here.
    ///    - Return convention:
    ///        * `chainIdTo = 0`
    ///        * `updatedParams = ""`
    ///        * `maskedParams.amountOut = 0`
    ///        * `maskedParams.to = 0`
    ///        * `maskedParams.emergencyTo = propagated emergencyTo`
    ///        * `result = ICoreFacet.ExecutionResult.Succeeded`
    ///
    /// 2. Pre-break flow:
    ///    - `nextOp == BreakOps.BREAK_CODE`.
    ///    - Facet does the same Rubic interaction (pull assets, approve, forward ETH, call proxy, reset approvals),
    ///      but additionally:
    ///        * It snapshots the router's balance of `p.stageAsset` before and after the call.
    ///          For ETH, it subtracts the ETH we are about to forward so we don't "double count" the call value.
    ///        * It computes the staged delta = (adjusted afterBalance - adjusted beforeBalance).
    ///        * Requires `stagedDelta >= p.minStageAmount`, otherwise revert.
    ///        * It returns:
    ///            - `maskedParams.amountOut = stagedDelta`
    ///            - `maskedParams.to = address(this)` (router holds the staged funds)
    ///            - `maskedParams.emergencyTo = propagated emergencyTo`
    ///            - `chainIdTo = 0`
    ///            - `updatedParams = ""`
    ///            - `result = ICoreFacet.ExecutionResult.Succeeded`
    ///
    /// Common rules for both flows:
    /// - ERC20 inputs: we aggregate all requested ERC20 amounts per token, optionally pull them
    ///   from `from` (or `prevMaskedParams.to` if `from == 0`), approve the Rubic proxy for those amounts,
    ///   and later reset approvals to zero.
    /// - Native inputs: we sum all native amounts (tokens[i] == address(0)) plus `nativeValue`.
    /// - `msg.value` policy:
    ///     * On origin call (`CoreFacetStorage.currentRequestId() == 0`), `msg.value` MUST equal the ETH
    ///       we are about to forward.
    ///     * On resume call, `msg.value` MUST be 0, and the router's ETH balance MUST be large enough to fund the call.
    ///
    /// @param isOpHalfDone Reserved flag propagated by the router. Not used by Rubic logic.
    /// @param op Operation code; must be the Rubic start code (`RUBIC_START_CODE`).
    /// @param nextOp Next operation code. Must be either:
    ///        - `bytes32(0)` for terminal flow, or
    ///        - `BreakOps.BREAK_CODE` if we're staging funds for a BREAK next.
    /// @param params ABI-encoded `RubicStartParams` specifying inputs, routing data, staging expectations, etc.
    /// @param prevMaskedParams Masked params from the previous pipeline step. Supplies default `from`,
    ///        `emergencyTo`, and `amountOut` substitutions.
    /// @return chainIdTo Always 0. RubicFacet itself does not directly schedule a cross-chain hop.
    /// @return updatedParams Always empty (`""`). RubicFacet does not mutate downstream op params.
    /// @return maskedParams
    ///         Terminal flow (`nextOp == 0`):
    ///            - amountOut = 0
    ///            - to        = 0
    ///            - emergencyTo = propagated
    ///         Pre-break flow (`nextOp == BreakOps.BREAK_CODE`):
    ///            - amountOut   = stagedDelta (measured staged amount)
    ///            - to          = address(this) (router now holds the staged funds)
    ///            - emergencyTo = propagated
    /// @return result Always `ICoreFacet.ExecutionResult.Succeeded` if we didn't revert.
    function executeRubicOp(
        bool isOpHalfDone,
        bytes32 op,
        bytes32 nextOp,
        bytes calldata params,
        ICoreFacet.MaskedParams calldata prevMaskedParams
    )
        external
        payable
        returns (
            uint64 chainIdTo,
            bytes memory updatedParams,
            ICoreFacet.MaskedParams memory maskedParams,
            ICoreFacet.ExecutionResult result
        );

    /// @notice Sets the Rubic ERC20 proxy address for the current chain.
    /// @dev
    /// - Restricted to `OPERATOR_ROLE` enforced in the facet implementation via `onlyRole`.
    /// - Must not be the zero address.
    /// @param erc20Proxy Address of Rubic's ERC20 proxy contract on this chain.
    function setRubicProxy(address erc20Proxy) external;

    /// @notice Returns the Rubic ERC20 proxy address configured for this chain.
    /// @return The address of the Rubic ERC20 proxy contract currently stored in facet storage.
    function rubicProxy() external view returns (address);
}

// SPDX-License-Identifier: UNLICENSED
// Copyright (c) Eywa.Fi, 2021-2025 - all rights reserved
pragma solidity 0.8.25;

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

interface IRunnerFacet {
    /// @notice Immutable configuration parameters embedded into a UniversalRunner clone.
    /// @dev These values are encoded as immutable args in the clone bytecode (no storage used).
    ///      They define who can pull funds, on which chain, until when, and under which logical request.
    /// @param router Router (diamond) address that is allowed to call collect() on the runner.
    /// @param factory UniversalRunnerFactory that deployed this runner via CREATE2.
    /// @param beneficiary Recipient of emergencyWithdraw() after deadline expiry.
    /// @param token Asset expected on the runner:
    ///        - non-zero: ERC20 token to be collected,
    ///        - zero: native asset (ETH) to be collected.
    /// @param deadline Last valid timestamp (inclusive) when collect() is allowed to succeed.
    /// @param chainId Chain id on which this runner is valid; must equal block.chainid.
    /// @param requestId Logical request identifier associated with this runner instance.
    struct RunnerImmutableArgs {
        address router;
        address factory;
        address beneficiary;
        address token;
        uint64 deadline;
        uint64 chainId;
        bytes32 requestId;
    }

    /// @notice Parameters for the RUNNER_COLLECT pipeline operation.
    /// @dev Encoded into `params` when calling executeRunnerCollectOp via the router.
    /// @param runnerArgs Immutable configuration that will be embedded into the UniversalRunner clone.
    /// @param salt CREATE2 salt used to deterministically derive the runner address.
    /// @param amountIn Desired amount to pull from the runner (or type(uint256).max when using masking).
    /// @param emergencyTo Emergency recipient for the overall pipeline; propagated into masked params.
    struct RunnerCollectParams {
        RunnerImmutableArgs runnerArgs;
        bytes32 salt;
        uint256 amountIn;
        bytes32 emergencyTo;
    }

    /// @notice Scratch space for executeRunnerCollectOp to avoid "stack too deep" errors.
    /// @dev
    /// Groups together intermediate values used during RUNNER_COLLECT execution so they
    /// do not each occupy a separate stack slot. This struct is not stored on-chain and
    /// is only used inside RunnerFacet.executeRunnerCollectOp.
    /// @param amountIn Resolved amount to collect from the runner after applying masking rules.
    /// @param emergencyTo Resolved emergency recipient encoded as bytes32 (address-packed).
    /// @param factory UniversalRunnerFactory address loaded from RunnerFacetStorage.
    /// @param runner Deployed UniversalRunner clone address used for the collect() call.
    /// @param collected Actual amount returned by runner.collect(), compared against amountIn.
    /// @param emergencyToAddr Emergency recipient decoded as an address, used to validate beneficiary.
    struct LocalVars {
        uint256 amountIn;
        bytes32 emergencyTo;
        address factory;
        address runner;
        uint256 collected;
        address emergencyToAddr;
    }

    /// @notice Thrown when the caller lacks the required role to perform an admin-only action.
    /// @dev Used by {setUniversalRunnerFactory}.
    error RunnerFacetMissingRole();

    /// @notice Thrown when a zero factory address is provided to an admin setter.
    /// @dev Protects configuration from being set to address(0).
    error RunnerFacetFactoryZero();

    /// @notice Thrown when the universal runner factory is not configured.
    /// @dev Raised in executeRunnerCollectOp when no factory address is set in storage.
    error RunnerFacetFactoryNotSet();

    /// @notice Thrown when the factory in params does not match the configured factory.
    /// @dev Ensures that the runner is deployed only through the trusted UniversalRunnerFactory.
    error RunnerFacetFactoryMismatch();

    /// @notice Thrown when executeRunnerCollectOp is called with an unsupported op code.
    /// @dev `op` must equal RUNNER_COLLECT_CODE for this facet.
    error RunnerFacetWrongOp();

    /// @notice Thrown when the router in RunnerImmutableArgs does not match the current diamond.
    /// @dev Protects against misconfigured or spoofed runner args.
    error RunnerFacetWrongRouter();

    /// @notice Thrown when the runner's configured chainId does not match the current block.chainid.
    /// @dev Prevents re-use of runner configs across chains.
    error RunnerFacetChainIdMismatch();

    /// @notice Thrown when the runner deadline has already passed at the time of execution.
    /// @dev Used to prevent collect() from being used after expiration.
    error RunnerFacetDeadlineExpired();

    /// @notice Thrown when the resolved amount to collect is zero.
    /// @dev Applies both when amountIn is zero directly or after masking resolution.
    error RunnerFacetAmountZero();

    /// @notice Thrown when the runner returns less funds than requested.
    /// @dev Indicates that the actual collected amount is strictly less than `amountIn`.
    error RunnerFacetInsufficientCollected();

    /// @notice Thrown when executeRunnerCollectOp is called in a "half-done" (resumed) mode.
    /// @dev RUNNER_COLLECT is a one-step operation and does not support isOpHalfDone == true.
    error RunnerFacetHalfDoneNotSupported();

    /// @notice Thrown when the resolved emergency recipient address is zero.
    /// @dev Ensures that RUNNER_COLLECT always has a non-zero emergencyTo in masked params.
    error RunnerFacetEmergencyToZero();

    /// @notice Thrown when the runner's configured beneficiary does not match the resolved emergencyTo.
    /// @dev Binds UniversalRunner.beneficiary to the pipeline initiator by enforcing beneficiary == emergencyTo.
    error RunnerFacetWrongBeneficiary();

    /// @notice Sets the UniversalRunnerFactory used to deploy runner clones.
    /// @dev Admin-gated. Reverts if `factory` is the zero address.
    /// @param factory The UniversalRunnerFactory contract address.
    function setUniversalRunnerFactory(address factory) external;

    /// @notice Executes the RUNNER_COLLECT operation as part of the router pipeline.
    /// @dev
    /// High-level behavior:
    ///  - Validates that `op` equals RUNNER_COLLECT_CODE and that `isOpHalfDone` is false.
    ///  - Requires `msg.value == 0` (no native value is accepted at this stage).
    ///  - Decodes RunnerCollectParams from `params`.
    ///  - Ensures:
    ///       * factory is configured and matches `runnerArgs.factory`,
    ///       * `runnerArgs.router == address(this)` (the current diamond),
    ///       * `runnerArgs.chainId == block.chainid`,
    ///       * `runnerArgs.deadline >= block.timestamp`.
    ///  - Resolves `amountIn` and `emergencyTo` using `prevMaskedParams` and masking rules.
    ///  - Deploys a UniversalRunner clone via factory if it does not exist yet.
    ///  - Calls `collect(runnerArgs.token, address(this), amountIn)` on the runner.
    ///  - Requires that the collected amount is at least `amountIn`.
    ///  - Returns updated masked params with:
    ///       * `amountOut = collected`,
    ///       * `to = address(this)`,
    ///       * `emergencyTo` propagated from the resolved value.
    ///
    /// This operation does not schedule a cross-chain hop and does not mutate params for next ops.
    ///
    /// @param isOpHalfDone Indicates whether the router is resuming a half-executed op; must be false.
    /// @param op Operation code for this step; must equal RUNNER_COLLECT_CODE.
    /// @param nextOp Operation code for the next step in the pipeline; ignored by this facet.
    /// @param params ABI-encoded RunnerCollectParams structure.
    /// @param prevMaskedParams Masked params from the previous step in the pipeline.
    /// @return chainIdTo Always 0, since RUNNER_COLLECT does not initiate a cross-chain operation.
    /// @return updatedParams Always empty bytes, as this op does not mutate subsequent params.
    /// @return maskedParams Updated masked params with the collected amount and router as `to`.
    /// @return result Execution status; always ExecutionResult.Succeeded on success, otherwise reverts.
    function executeRunnerCollectOp(
        bool isOpHalfDone,
        bytes32 op,
        bytes32 nextOp,
        bytes calldata params,
        ICoreFacet.MaskedParams calldata prevMaskedParams
    )
        external
        payable
        returns (
            uint64 chainIdTo,
            bytes memory updatedParams,
            ICoreFacet.MaskedParams memory maskedParams,
            ICoreFacet.ExecutionResult result
        );
}

// SPDX-License-Identifier: UNLICENSED
// Copyright (c) Eywa.Fi, 2021-2025 - all rights reserved
pragma solidity 0.8.25;

import "@openzeppelin/contracts/utils/Counters.sol";

/// @notice Structure with information about cross-chain transaction delivery.
/// @param hashSynthParams hash from the parameters of a successfully delivered cross-chain transaction.
/// @param crossChainOpState cross-chain transaction delivery status.
struct ProcessedOps {
    bytes32 hashSynthParams;
    uint8 crossChainOpState;
}

library CoreFacetStorage {
    /// @notice Fixed storage slot for the CoreFacet data structure.
    bytes32 private constant POSITION = keccak256("crosscurve.core.facet.storage");

    /// @notice Slot for the currentRequestId.
    /// @dev keccak256("crosscurve.core.facet.transient.currentRequestId");
    bytes32 private constant CURRENT_REQUEST_ID_POS = 0x4c0fc00f7060fb51f491b90bbb038205c36b6fbc6a8aed52d27b18d2967b53f4;

    /// @notice Slot for the currentChainIdFrom.
    /// @dev keccak256("crosscurve.core.facet.transient.currentChainIdFrom");
    bytes32 private constant CURRENT_CHAIN_ID_FROM_POS = 0x67b6f0fb6bca8398a49a7966c252819542f4d36560c9c6961937957fdf20f9f4;

    /// @notice Slot for the isOriginNetwork.
    /// @dev keccak256("crosscurve.core.facet.transient.isOriginNetwork");
    bytes32 private constant IS_ORIGIN_NETWORK_POS = 0xb13bd13498ce9409b85a4287d16ff0fcdaca732822a47a97d2bdada3325aa451;

    /// @notice Slot for the isDiamondCall.
    /// @dev keccak256("crosscurve.core.facet.transient.isDiamondCall");
    bytes32 private constant IS_DIAMOND_CALL = 0x2e445143a211ecbb689de5812742d02a96369c482aec76832fc56490cf3cc6f2;

    /// @notice Slot for the isReentrancyLocked.
    /// @dev keccak256("crosscurve.core.facet.transient.isReentrancyLocked");
    bytes32 private constant IS_REENTRANCY_LOCKED = 0x1e0578bb15c7ba996e367f5cb2606a2c0e49f47e3ec9ce25487e41761d562498;

    /// @notice Slot for msgValueLeft budget (origin start only).
    /// @dev keccak256("crosscurve.core.facet.transient.msgValueLeft");
    bytes32 private constant MSG_VALUE_LEFT_POS = 0x7f3e5ed8ce4a8806c618eaa07afc27516c5c690793b80b8262a28aa8675561fc;

    /// @notice Thrown when an op tries to spend more ETH than was provided in msg.value for the whole start().
    error MsgValueBudgetExceeded(uint256 requested, uint256 left);

    /// @notice Layout for all persistent CoreFacet state.  
    /// @dev Every variable needed by CoreFacet logic is grouped here and kept
    /// at the single slot defined in `POSITION`.
    struct DS {
        mapping(address => Counters.Counter) nonces;
        mapping(bytes32 => bytes32) startedOps;
        mapping(bytes32 => ProcessedOps) processedOps;
        address addressBook;
        address treasury;
        address WETH;
    }

    /// @notice Accessor to the CoreFacet storage struct.
    /// @dev Assembly ties the returned reference to the predefined slot so the
    /// compiler understands where the struct actually lives in storage.
    /// @return s Pointer to the `DS` struct residing at `POSITION`.
    function ds() internal pure returns (DS storage s) {
        bytes32 pos = POSITION;
        assembly {
            s.slot := pos
        }
    }

    function setCurrentRequestId(bytes32 currentRequestId_) internal {
        assembly ("memory-safe") {
            tstore(CURRENT_REQUEST_ID_POS, currentRequestId_)
        }
    }

    function currentRequestId() internal view returns (bytes32 currentRequestId_) {
        assembly ("memory-safe") {
            currentRequestId_ := tload(CURRENT_REQUEST_ID_POS)
        }
    }

    function setCurrentChainIdFrom(uint64 currentChainIdFrom_) internal {
        assembly ("memory-safe") {
            tstore(CURRENT_CHAIN_ID_FROM_POS, currentChainIdFrom_)
        }
    }

    function currentChainIdFrom() internal view returns (uint64 currentChainIdFrom_) {
        assembly ("memory-safe") {
            currentChainIdFrom_ := tload(CURRENT_CHAIN_ID_FROM_POS)
        }
    }

    function setOriginNetwork(bool isOriginNetwork_) internal {
        assembly ("memory-safe") {
            tstore(IS_ORIGIN_NETWORK_POS, isOriginNetwork_)
        }
    }

    function isOriginNetwork() internal view returns (bool isOriginNetwork_) {
        assembly ("memory-safe") {
            isOriginNetwork_ := tload(IS_ORIGIN_NETWORK_POS)
        }
    }

    function setDiamondCall(bool isDiamondCall_) internal {
        assembly ("memory-safe") {
            tstore(IS_DIAMOND_CALL, isDiamondCall_)
        }
    }

    function isDiamondCall() internal view returns (bool isDiamondCall_) {
        assembly ("memory-safe") {
            isDiamondCall_ := tload(IS_DIAMOND_CALL)
        }
    }

    function setReentrancyLock(bool isLocked_) internal {
        assembly ("memory-safe") {
            tstore(IS_REENTRANCY_LOCKED, isLocked_)
        }
    }

    function isReentrancyLocked() internal view returns (bool isLocked_) {
        assembly ("memory-safe") {
            isLocked_ := tload(IS_REENTRANCY_LOCKED)
        }
    }

    function setMsgValueLeft(uint256 msgValueLeft_) internal {
        assembly ("memory-safe") {
            tstore(MSG_VALUE_LEFT_POS, msgValueLeft_)
        }
    }

    function msgValueLeft() internal view returns (uint256 msgValueLeft_) {
        assembly ("memory-safe") {
            msgValueLeft_ := tload(MSG_VALUE_LEFT_POS)
        }
    }

    function consumeMsgValue(uint256 amount) internal {
        uint256 left;
        assembly ("memory-safe") {
            left := tload(MSG_VALUE_LEFT_POS)
        }
        if (amount > left) {
            revert MsgValueBudgetExceeded(amount, left);
        }
        unchecked {
            left -= amount;
        }
        assembly ("memory-safe") {
            tstore(MSG_VALUE_LEFT_POS, left)
        }
    }
}

File 19 of 30 : CurveFacetStorage.sol
// SPDX-License-Identifier: UNLICENSED
// Copyright (c) Eywa.Fi, 2021-2025 - all rights reserved
pragma solidity 0.8.25;


library CurveFacetStorage {
    /// @notice Fixed storage slot for the CurveFacet data structure.
    bytes32 private constant POSITION = keccak256("crosscurve.curve.facet.storage");

    /// @notice Layout for all persistent CurveFacet state.  
    /// @dev Every variable needed by CurveFacet logic is grouped here and kept
    /// at the single slot defined in `POSITION`.
    struct DS {
        mapping (address => address) poolAdapter;
    }

    /// @notice Accessor to the CurveFacet storage struct.
    /// @dev Assembly ties the returned reference to the predefined slot so the
    /// compiler understands where the struct actually lives in storage.
    /// @return s Pointer to the `DS` struct residing at `POSITION`.
    function ds() internal pure returns (DS storage s) {
        bytes32 pos = POSITION;
        assembly {
            s.slot := pos
        }
    }
}

// SPDX-License-Identifier: UNLICENSED
// Copyright (c) Eywa.Fi, 2021-2025 - all rights reserved
pragma solidity 0.8.25;


/// @param bmoOpHash Hash from the parameters of the BMo operation, which was reverted
/// @param opsHash Hash from the sequence of operation parameters that was reverted
struct ResumedOps{
    bytes32 bmoOpHash;
    bytes32 opsHash;
}

library OFTFacetStorage {

    /// @notice Fixed storage slot for the CoreFacet data structure.
    bytes32 private constant POSITION = keccak256("crosscurve.oft.facet.storage");

    /// @notice Slot for the lzComposeIsProcessed.
    /// @dev keccak256("crosscurve.oft.facet.transient.lzComposeIsProcessed");
    bytes32 private constant LZ_COMPOSE_IS_PROCESSED = 0x30273a0c1aadada7e550a5e2c5e4d1944f67a058b948828ccf2622923a9aae4b;

    /// @notice Layout for all persistent CoreFacet state.  
    /// @dev Every variable needed by CoreFacet logic is grouped here and kept
    /// at the single slot defined in `POSITION`.
    struct DS {
        mapping(bytes32 => ResumedOps) resumedOps;
        mapping(bytes32 => uint8) processedOps;
        mapping (bytes32 => bool) guids;
        address endPoint;
    }

    /// @notice Accessor to the CoreFacet storage struct.
    /// @dev Assembly ties the returned reference to the predefined slot so the
    /// compiler understands where the struct actually lives in storage.
    /// @return s Pointer to the `DS` struct residing at `POSITION`.
    function ds() internal pure returns (DS storage s) {
        bytes32 pos = POSITION;
        assembly {
            s.slot := pos
        }
    }

    function setLzComposeIsProcessed(bool lzComposeIsProcessed_) internal {
        assembly ("memory-safe") {
            tstore(LZ_COMPOSE_IS_PROCESSED, lzComposeIsProcessed_)
        }
    }

    function lzComposeIsProcessed() internal view returns (bool lzComposeIsProcessed_) {
        assembly ("memory-safe") {
            lzComposeIsProcessed_ := tload(LZ_COMPOSE_IS_PROCESSED)
        }
    }
}

File 21 of 30 : BreakOps.sol
// SPDX-License-Identifier: UNLICENSED
// Copyright (c) Eywa.Fi, 2021-2025 - all rights reserved
pragma solidity 0.8.25;

library BreakOps {
    /// @notice Operation code for starting a break ("br").
    /// @dev The router must map this code to `executeBreakOp`, which:
    ///      - records break metadata and pipeline tail,
    ///      - stashes the produced funds,
    ///      - returns `ExecutionResult.Interrupted`.
    bytes32 public constant BREAK_CODE = keccak256(abi.encodePacked("br"));

    /// @notice Operation code for cancelling a break ("!br").
    /// @dev The router must map this code to `executeCancelBreakOp`, which:
    ///      - requires the break to still be pending,
    ///      - transfers escrowed funds to `emergencyTo`,
    ///      - marks the break as cancelled.
    bytes32 public constant CANCEL_BREAK_CODE = keccak256(abi.encodePacked("!br"));
}

File 22 of 30 : BungeeOps.sol
// SPDX-License-Identifier: UNLICENSED
// Copyright (c) Eywa.Fi, 2021-2025 - all rights reserved
pragma solidity 0.8.25;

library BungeeOps {
    /// @notice Operation code for the Bungee "manual" start (off-chain built tx).
    bytes32 public constant BUNGEE_MANUAL_START_CODE = keccak256(abi.encodePacked("BMAN"));

    /// @notice Operation code for the Bungee "auto" start (on-chain tx data).
    bytes32 public constant BUNGEE_AUTO_START_CODE = keccak256(abi.encodePacked("BAUTO"));

    /// @notice Operation code for backend-triggered refunds from router balance.
    bytes32 public constant BUNGEE_REFUND_CODE = keccak256(abi.encodePacked("BRFD"));
}

File 23 of 30 : CrosschainOps.sol
// SPDX-License-Identifier: UNLICENSED
// Copyright (c) Eywa.Fi, 2021-2025 - all rights reserved
pragma solidity 0.8.25;


library CrosschainOps {
    /// @notice Operation code for lock and mint operations.
    bytes32 public constant LOCK_MINT_CODE = keccak256(abi.encodePacked("LM"));

    /// @notice Operation code for burn and unlock operations.
    bytes32 public constant BURN_UNLOCK_CODE = keccak256(abi.encodePacked("BU"));

    /// @notice Operation code for burn and mint operations.
    bytes32 public constant BURN_MINT_CODE = keccak256(abi.encodePacked("BM"));

    /// @notice Operation code for emergency unlock (cancel lock) operations.
    bytes32 public constant EMERGENCY_UNLOCK_CODE = keccak256(abi.encodePacked("!M"));

    /// @notice Operation code for emergency mint (cancel burn) operations.
    bytes32 public constant EMERGENCY_MINT_CODE = keccak256(abi.encodePacked("!U"));
}

File 24 of 30 : CurveAMMOps.sol
// SPDX-License-Identifier: UNLICENSED
// Copyright (c) Eywa.Fi, 2021-2025 - all rights reserved
pragma solidity 0.8.25;


library CurveAMMOps {
    /// @notice Operation code for adding liquidity or assets.
    bytes32 public constant ADD_CODE = keccak256(abi.encodePacked("A"));

    /// @notice Operation code for removing liquidity or assets.
    bytes32 public constant REMOVE_CODE = keccak256(abi.encodePacked("R"));

    /// @notice Operation code for token or asset swap.
    bytes32 public constant SWAP_CODE = keccak256(abi.encodePacked("S"));
}

File 25 of 30 : OftOps.sol
// SPDX-License-Identifier: UNLICENSED
// Copyright (c) Eywa.Fi, 2021-2025 - all rights reserved
pragma solidity 0.8.25;


library OftOps {
    /// @notice Operation code for OFT burn and mint operations.
    bytes32 public constant BURN_MINT_OFT_CODE = keccak256(abi.encodePacked("BMo"));

    /// @notice Operation code for emergency OFT mint operations.
    bytes32 public constant EMERGENCY_MINT_OFT_CODE = keccak256(abi.encodePacked("!Mo"));

    /// @notice Operation code for push OFT operations in dist chain.
    bytes32 public constant PUSH_MINT_OFT_CODE = keccak256(abi.encodePacked("PMo"));
}

File 26 of 30 : RubicOps.sol
// SPDX-License-Identifier: UNLICENSED
// Copyright (c) Eywa.Fi, 2021-2025 - all rights reserved
pragma solidity 0.8.25;

library RubicOps {
    /// @notice Operation code for Rubic start (terminal step).
    bytes32 public constant RUBIC_START_CODE = keccak256(abi.encodePacked("RS"));
}

File 27 of 30 : RunnerOps.sol
// SPDX-License-Identifier: UNLICENSED
// Copyright (c) Eywa.Fi, 2021-2025 - all rights reserved
pragma solidity 0.8.25;

library RunnerOps {
    /// @notice Operation code for the "collect from UniversalRunner" operation.
    /// @dev Routed to {IRunnerFacet.executeRunnerCollectOp} when present
    ///      as the first operation in the pipeline.
    bytes32 public constant RUNNER_COLLECT_CODE = keccak256("RUNNER_COLLECT");
}

File 28 of 30 : UtilsOps.sol
// SPDX-License-Identifier: UNLICENSED
// Copyright (c) Eywa.Fi, 2021-2025 - all rights reserved
pragma solidity 0.8.25;


library UtilsOps {
    /// @notice Operation code for `permit` functionality.
    bytes32 public constant PERMIT_CODE = keccak256(abi.encodePacked("P"));

    /// @notice Operation code for token wrapping.
    bytes32 public constant WRAP_CODE = keccak256(abi.encodePacked("W"));

    /// @notice Operation code for token unwrapping.
    bytes32 public constant UNWRAP_CODE = keccak256(abi.encodePacked("Uw"));
}

// SPDX-License-Identifier: UNLICENSED
// Copyright (c) Eywa.Fi, 2021-2025 - all rights reserved
pragma solidity 0.8.25;

import {ICoreFacet} from "./interfaces/ICoreFacet.sol";
import {ICrosschainFacet} from "./interfaces/ICrosschainFacet.sol";
import {ICurveFacet} from "./interfaces/ICurveFacet.sol";
import {IOFTFacet} from "./interfaces/IOFTFacet.sol";
import {IRubicFacet} from "./interfaces/IRubicFacet.sol";
import {IBungeeFacet} from "./interfaces/IBungeeFacet.sol";
import {IBreakFacet} from "./interfaces/IBreakFacet.sol";
import {IRunnerFacet} from "./interfaces/IRunnerFacet.sol";
import {IAddressBook} from "../interfaces/IAddressBook.sol";
import {CoreFacetStorage} from "./libraries/CoreFacetStorage.sol";
import {CrosschainOps} from "./libraries/ops/CrosschainOps.sol";
import {CurveAMMOps} from "./libraries/ops/CurveAMMOps.sol";
import {OftOps} from "./libraries/ops/OftOps.sol";
import {RubicOps} from "./libraries/ops/RubicOps.sol";
import {BungeeOps} from "./libraries/ops/BungeeOps.sol";
import {BreakOps} from "./libraries/ops/BreakOps.sol";
import {RunnerOps} from "./libraries/ops/RunnerOps.sol";
import {UtilsOps} from "./libraries/ops/UtilsOps.sol";
import {TypecastLib} from "../utils/TypecastLib.sol";

abstract contract OpsCrosschain {
    /// @notice Operation code for lock and mint operations.
    bytes32 public constant LOCK_MINT_CODE = CrosschainOps.LOCK_MINT_CODE;

    /// @notice Operation code for burn and unlock operations.
    bytes32 public constant BURN_UNLOCK_CODE = CrosschainOps.BURN_UNLOCK_CODE;

    /// @notice Operation code for burn and mint operations.
    bytes32 public constant BURN_MINT_CODE = CrosschainOps.BURN_MINT_CODE;

    /// @notice Operation code for emergency unlock (cancel lock) operations.
    bytes32 public constant EMERGENCY_UNLOCK_CODE = CrosschainOps.EMERGENCY_UNLOCK_CODE;

    /// @notice Operation code for emergency mint (cancel burn) operations.
    bytes32 public constant EMERGENCY_MINT_CODE = CrosschainOps.EMERGENCY_MINT_CODE;

    /// @notice Operation code for `permit` functionality.
    bytes32 public constant PERMIT_CODE = UtilsOps.PERMIT_CODE;

    /// @notice Operation code for token wrapping.
    bytes32 public constant WRAP_CODE = UtilsOps.WRAP_CODE;

    /// @notice Operation code for token unwrapping.
    bytes32 public constant UNWRAP_CODE = UtilsOps.UNWRAP_CODE;
}

abstract contract OpsCurveAMM {
    /// @notice Operation code for adding liquidity or assets.
    bytes32 public constant ADD_CODE = CurveAMMOps.ADD_CODE;

    /// @notice Operation code for removing liquidity or assets.
    bytes32 public constant REMOVE_CODE = CurveAMMOps.REMOVE_CODE;

    /// @notice Operation code for token or asset swap.
    bytes32 public constant SWAP_CODE = CurveAMMOps.SWAP_CODE;
}

abstract contract OpsOFT {
    /// @notice Operation code for OFT burn and mint operations.
    bytes32 public constant BURN_MINT_OFT_CODE = OftOps.BURN_MINT_OFT_CODE;

    /// @notice Operation code for emergency OFT mint operations.
    bytes32 public constant EMERGENCY_MINT_OFT_CODE = OftOps.EMERGENCY_MINT_OFT_CODE;

    /// @notice Operation code for push OFT operations in dist chain.
    bytes32 public constant PUSH_MINT_OFT_CODE = OftOps.PUSH_MINT_OFT_CODE;
}

abstract contract OpsRubic {
    bytes32 public constant RUBIC_START_CODE = RubicOps.RUBIC_START_CODE;
}

abstract contract OpsBungee {
    bytes32 public constant BUNGEE_MANUAL_START_CODE = BungeeOps.BUNGEE_MANUAL_START_CODE;
    bytes32 public constant BUNGEE_AUTO_START_CODE = BungeeOps.BUNGEE_AUTO_START_CODE;
    bytes32 public constant BUNGEE_REFUND_CODE = BungeeOps.BUNGEE_REFUND_CODE;
}

abstract contract Ops {
    using CoreFacetStorage for CoreFacetStorage.DS;

    /// @notice The modifier checks that the current operation is being on CoreFacet.
    modifier onlyDiamond() {
        require(CoreFacetStorage.isDiamondCall(), "Router: isn't Diamond call");
        require(!CoreFacetStorage.isReentrancyLocked(), "Router: reentrancy");
        CoreFacetStorage.setReentrancyLock(true);
        _;
        CoreFacetStorage.setReentrancyLock(false);
    }

    function getOpSelector(bytes32 op) internal pure returns (bytes4 selector) {
        if (
            op == UtilsOps.PERMIT_CODE
            || op == UtilsOps.WRAP_CODE
            || op == UtilsOps.UNWRAP_CODE
        ) {
            return ICrosschainFacet.executeCrosschainOp.selector;
        } else if (
            op == CrosschainOps.LOCK_MINT_CODE
            || op == CrosschainOps.BURN_UNLOCK_CODE
            || op == CrosschainOps.BURN_MINT_CODE
            || op == CrosschainOps.EMERGENCY_UNLOCK_CODE
            || op == CrosschainOps.EMERGENCY_MINT_CODE
        ) {
            return ICrosschainFacet.executeCrosschainOp.selector;
        } else if (
            op == CurveAMMOps.ADD_CODE
            || op == CurveAMMOps.REMOVE_CODE
            || op == CurveAMMOps.SWAP_CODE
        ) {
            return ICurveFacet.executeCurveAMMOp.selector;
        } else if (
            op == OftOps.BURN_MINT_OFT_CODE
            || op == OftOps.EMERGENCY_MINT_OFT_CODE
            || op == OftOps.PUSH_MINT_OFT_CODE
        ) {
            return IOFTFacet.executeOftOp.selector;
        } else if (op == RubicOps.RUBIC_START_CODE) {
            return IRubicFacet.executeRubicOp.selector;
        } else if (op == BungeeOps.BUNGEE_MANUAL_START_CODE) {
            return IBungeeFacet.executeBungeeManualOp.selector;
        } else if (op == BungeeOps.BUNGEE_AUTO_START_CODE) {
            return IBungeeFacet.executeBungeeAutoOp.selector;
        } else if (op == BungeeOps.BUNGEE_REFUND_CODE) {
            return IBungeeFacet.executeBungeeRefundOp.selector;
        } else if (op == BreakOps.BREAK_CODE) {
            return IBreakFacet.executeBreakOp.selector;
        } else if (op == BreakOps.CANCEL_BREAK_CODE) {
            return IBreakFacet.executeCancelBreakOp.selector;
        } else if (op == RunnerOps.RUNNER_COLLECT_CODE) {
            return IRunnerFacet.executeRunnerCollectOp.selector;
        }
        revert("Ops: op is not supported");
    }

    /// @notice Returns the final values for amountIn, from, and emergencyTo based on masking logic.
    /// @param currentAmountIn The current input amount, can be type(uint256).max for masking.
    /// @param currentFrom The current sender address (as bytes32), may be 0.
    /// @param currentEmergencyTo The current emergency address (as bytes32), may be 0.
    /// @param prevMaskedParams Previously masked values used to fill in missing data.
    /// @return amountIn Final resolved input amount.
    /// @return from Final resolved sender address.
    /// @return emergencyTo Final resolved emergency address.
    function checkMaskedParams(
        uint256 currentAmountIn,
        bytes32 currentFrom,
        bytes32 currentEmergencyTo,
        ICoreFacet.MaskedParams memory prevMaskedParams
    ) internal view returns (uint256 amountIn, bytes32 from, bytes32 emergencyTo) {
        amountIn = currentAmountIn == type(uint256).max ? prevMaskedParams.amountOut : currentAmountIn;
        if (currentFrom != 0) {
            require(TypecastLib.castToAddress(currentFrom) == msg.sender, "Router: wrong sender");
            from = currentFrom;
        } else {
            from = prevMaskedParams.to;
        }
        if (CoreFacetStorage.currentRequestId() == 0 && currentEmergencyTo != 0) {
            require(TypecastLib.castToAddress(currentEmergencyTo) == msg.sender, "Router: wrong emergencyTo");
            emergencyTo = currentEmergencyTo;
        } else {
            emergencyTo = prevMaskedParams.emergencyTo;
        }
    }    

    /// @notice Validates and resolves the correct `to` address for an operation sequence.
    /// @dev Calls `coreOpCheckTo` on the current contract (as ICrosschainFacet), and if it returns zero,
    /// falls back to calling `poolOpCheckTo` on the current contract (as ICurveFacet).
    /// @param to The initially proposed recipient address (encoded as bytes32).
    /// @param emergencyTo The emergency fallback address (encoded as bytes32).
    /// @param chainId The chain ID for which the address resolution is taking place.
    /// @param currentOp The identifier of the current operation (hashed name).
    /// @param nextOp The identifier of the next operation in sequence (hashed name).
    /// @return correctTo The final resolved recipient address (encoded as bytes32). If invalid, may revert.
    function checkTo(bytes32 to, bytes32 emergencyTo, uint64 chainId, bytes32 currentOp, bytes32 nextOp) internal view returns (bytes32 correctTo) {
        CoreFacetStorage.DS storage ds = CoreFacetStorage.ds();
        require(
            nextOp == 0 && to != 0 || nextOp != 0 && to == 0, 
            "Router: wrong to"
        );
        if (CoreFacetStorage.currentRequestId() == 0 && currentOp != UtilsOps.WRAP_CODE && currentOp != UtilsOps.UNWRAP_CODE) {
            require(TypecastLib.castToAddress(emergencyTo) == msg.sender, "Router: emergencyTo is not equal the sender");
        }
        if (nextOp == bytes32(0)) {
            correctTo = to;
        } else if (nextOp == CrosschainOps.LOCK_MINT_CODE) {
            correctTo = IAddressBook(ds.addressBook).portalV3(chainId);
        } else if (nextOp == CrosschainOps.BURN_UNLOCK_CODE || nextOp == CrosschainOps.BURN_MINT_CODE) {
            correctTo = IAddressBook(ds.addressBook).synthesisV3(chainId);
        } else if (UtilsOps.WRAP_CODE == nextOp || UtilsOps.UNWRAP_CODE == nextOp) {
            correctTo = IAddressBook(ds.addressBook).routerV3(chainId);
        } else if (nextOp == CurveAMMOps.ADD_CODE || nextOp == CurveAMMOps.REMOVE_CODE || nextOp == CurveAMMOps.SWAP_CODE) {
            correctTo = IAddressBook(ds.addressBook).routerV3(chainId);
        } else if (nextOp == OftOps.BURN_MINT_OFT_CODE || nextOp == OftOps.EMERGENCY_MINT_OFT_CODE || nextOp == OftOps.PUSH_MINT_OFT_CODE) {
            correctTo = IAddressBook(ds.addressBook).routerV3(chainId);
        } else if (nextOp == RubicOps.RUBIC_START_CODE) {
            correctTo = IAddressBook(ds.addressBook).routerV3(chainId);
        } else if (nextOp == BreakOps.BREAK_CODE) {
            correctTo = IAddressBook(ds.addressBook).routerV3(chainId);
        } else if (
            nextOp == BungeeOps.BUNGEE_MANUAL_START_CODE ||
            nextOp == BungeeOps.BUNGEE_AUTO_START_CODE
        ) {
            correctTo = IAddressBook(ds.addressBook).routerV3(chainId);
        } else if (nextOp == RunnerOps.RUNNER_COLLECT_CODE) {
            correctTo = IAddressBook(ds.addressBook).routerV3(chainId);
        }
    }

    /// @notice Validates a sequence of operation codes starting from a given position.
    /// @param cPos Starting index in the operation sequence.
    /// @param operationsCode Array of operation codes to validate.
    /// @return true if the sequence is valid, false otherwise.
    function checkOperations(uint256 cPos, bytes32[] memory operationsCode) internal view returns (bool) {
        for (uint256 i = cPos; i < operationsCode.length - 1; i++) {
            bytes32 operationCode = operationsCode[i];
            if (operationCode == BreakOps.CANCEL_BREAK_CODE) {
                if (operationsCode.length != 2) {
                    return false;
                }
                if (i != 0) {
                    return false;
                }
                if (i != operationsCode.length - 2) {
                    return false;
                }
                if (CoreFacetStorage.currentRequestId() != 0) {
                    return false;
                }
                continue;
            }
            if (
                CoreFacetStorage.currentRequestId() != 0 &&
                i == cPos &&
                !(
                    operationCode == CrosschainOps.LOCK_MINT_CODE ||
                    operationCode == CrosschainOps.BURN_UNLOCK_CODE ||
                    operationCode == CrosschainOps.BURN_MINT_CODE ||
                    operationCode == CrosschainOps.EMERGENCY_UNLOCK_CODE ||
                    operationCode == CrosschainOps.EMERGENCY_MINT_CODE ||
                    operationCode == OftOps.BURN_MINT_OFT_CODE
                )
            ) {
                return false;
            }
            if (
                (
                    operationCode == UtilsOps.PERMIT_CODE ||
                    operationCode == UtilsOps.WRAP_CODE ||
                    operationCode == OftOps.EMERGENCY_MINT_OFT_CODE ||
                    operationCode == OftOps.PUSH_MINT_OFT_CODE
                ) && i != 0
            ) {
                return false;
            }
            if (operationCode == UtilsOps.UNWRAP_CODE && i != operationsCode.length - 2) {
                return false;
            }
            if (
                operationCode == CrosschainOps.LOCK_MINT_CODE &&
                operationsCode[i + 1] == CrosschainOps.LOCK_MINT_CODE
            ) {
                return false;
            }
            if (
                operationCode == CrosschainOps.BURN_UNLOCK_CODE &&
                operationsCode[i + 1] == CrosschainOps.BURN_UNLOCK_CODE
            ) {
                return false;
            }
            if (
                operationCode == CrosschainOps.BURN_MINT_CODE &&
                operationsCode[i + 1] == CrosschainOps.BURN_MINT_CODE
            ) {
                return false;
            }
            if (
                (
                    operationCode == CrosschainOps.EMERGENCY_UNLOCK_CODE ||
                    operationCode == CrosschainOps.EMERGENCY_MINT_CODE ||
                    operationCode == OftOps.EMERGENCY_MINT_OFT_CODE
                ) &&
                operationsCode.length > 2
            ) {
                return false;
            }
            if (operationCode == BreakOps.BREAK_CODE) {
                if (i == 0) {
                    return false;
                }
                if (i != operationsCode.length - 2) {
                    return false;
                }
                if (CoreFacetStorage.currentRequestId() != 0) {
                    return false;
                }
                bytes32 prev = operationsCode[i - 1];
                if (
                    prev != RubicOps.RUBIC_START_CODE &&
                    prev != BungeeOps.BUNGEE_MANUAL_START_CODE &&
                    prev != BungeeOps.BUNGEE_AUTO_START_CODE &&
                    prev != RunnerOps.RUNNER_COLLECT_CODE
                ) {
                    return false;
                }
                continue;
            }
            if (operationCode == RubicOps.RUBIC_START_CODE) {
                bool isTerminal = (i == operationsCode.length - 2);
                bool followedByBr = (operationsCode[i + 1] == BreakOps.BREAK_CODE);
                if (!isTerminal && !followedByBr) {
                    return false;
                }
                continue;
            }
            if (
                operationCode == BungeeOps.BUNGEE_MANUAL_START_CODE ||
                operationCode == BungeeOps.BUNGEE_AUTO_START_CODE
            ) {
                bool isTerminal = (i == operationsCode.length - 2);
                bool followedByBr = (operationsCode[i + 1] == BreakOps.BREAK_CODE);
                if (!isTerminal && !followedByBr) {
                    return false;
                }
                continue;
            }
            if (operationCode == BungeeOps.BUNGEE_REFUND_CODE) {
                if (i != 0) {
                    return false;
                }
                if (i != operationsCode.length - 2) {
                    return false;
                }
                if (CoreFacetStorage.currentRequestId() != 0) {
                    return false;
                }
                if (!CoreFacetStorage.isOriginNetwork()) {
                    return false;
                }
                continue;
            }
            if (operationCode == RunnerOps.RUNNER_COLLECT_CODE) {
                if (i != 0) {
                    return false;
                }
                if (CoreFacetStorage.currentRequestId() != 0) {
                    return false;
                }
                if (!CoreFacetStorage.isOriginNetwork()) {
                    return false;
                }
                continue;
            }
        }
        return true;
    }
}

// SPDX-License-Identifier: UNLICENSED
// Copyright (c) Eywa.Fi, 2021-2025 - all rights reserved
pragma solidity ^0.8.17;


library TypecastLib {
    function castToAddress(bytes32 x) internal pure returns (address) {
        return address(uint160(uint256(x)));
    }

    function castToBytes32(address a) internal pure returns (bytes32) {
        return bytes32(uint256(uint160(a)));
    }
}

Settings
{
  "optimizer": {
    "enabled": true,
    "runs": 200
  },
  "evmVersion": "cancun",
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  }
}

Contract Security Audit

Contract ABI

API
[{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"pool","type":"address"},{"indexed":false,"internalType":"address","name":"adapter","type":"address"}],"name":"PoolAdapterSet","type":"event"},{"inputs":[],"name":"ADD_CODE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"OPERATOR_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"REMOVE_CODE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"SWAP_CODE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"bytes32","name":"op","type":"bytes32"},{"internalType":"bytes32","name":"nextOp","type":"bytes32"},{"internalType":"bytes","name":"params","type":"bytes"},{"components":[{"internalType":"uint256","name":"amountOut","type":"uint256"},{"internalType":"bytes32","name":"to","type":"bytes32"},{"internalType":"bytes32","name":"emergencyTo","type":"bytes32"}],"internalType":"struct ICoreFacet.MaskedParams","name":"prevMaskedParams","type":"tuple"}],"name":"executeCurveAMMOp","outputs":[{"internalType":"uint64","name":"chainIdTo","type":"uint64"},{"internalType":"bytes","name":"updatedParams","type":"bytes"},{"components":[{"internalType":"uint256","name":"amountOut","type":"uint256"},{"internalType":"bytes32","name":"to","type":"bytes32"},{"internalType":"bytes32","name":"emergencyTo","type":"bytes32"}],"internalType":"struct ICoreFacet.MaskedParams","name":"maskedParams","type":"tuple"},{"internalType":"enum ICoreFacet.ExecutionResult","name":"result","type":"uint8"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"pool","type":"address"}],"name":"poolAdapter","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"pool_","type":"address"},{"internalType":"address","name":"poolAdapter_","type":"address"}],"name":"setPoolAdapter","outputs":[],"stateMutability":"nonpayable","type":"function"}]

6080604052348015600e575f80fd5b506119cc8061001c5f395ff3fe60806040526004361061006e575f3560e01c8063bae633611161004c578063bae633611461011b578063bcf4f0a61461013e578063beadbe321461015f578063f5b541a614610173575f80fd5b80632d07ae691461007257806395ad709a146100995780639ba520ad14610107575b5f80fd5b34801561007d575f80fd5b506100866101a6565b6040519081526020015b60405180910390f35b3480156100a4575f80fd5b506100ef6100b3366004611576565b6001600160a01b039081165f9081527ff7ba6083e63c71062a85498a64e063b755730ac3a92d494147a56ecca6d95b4260205260409020541690565b6040516001600160a01b039091168152602001610090565b348015610112575f80fd5b506100866101d0565b61012e61012936600461166c565b6101e5565b604051610090949392919061175e565b348015610149575f80fd5b5061015d6101583660046117c7565b610710565b005b34801561016a575f80fd5b506100866108c4565b34801561017e575f80fd5b506100867f97667070c54ef182b0f5858b034beac1b6f3089aa2d3188bb1e8929f4fa9b92981565b604051602960f91b60208201526021015b6040516020818303038152906040528051906020012081565b604051604160f81b60208201526021016101b7565b60408051606081810183525f80835260208301819052928201839052905f7f2e445143a211ecbb689de5812742d02a96369c482aec76832fc56490cf3cc6f25c6102765760405162461bcd60e51b815260206004820152601a60248201527f526f757465723a2069736e2774204469616d6f6e642063616c6c00000000000060448201526064015b60405180910390fd5b7f1e0578bb15c7ba996e367f5cb2606a2c0e49f47e3ec9ce25487e41761d5624985c156102da5760405162461bcd60e51b8152602060048201526012602482015271526f757465723a207265656e7472616e637960701b604482015260640161026d565b6102e460016108d9565b604051604160f81b602082015288906021016040516020818303038152906040528051906020012003610488575f86806020019051810190610326919061189c565b90505f61033c610337836080015190565b6108ff565b9050610356826020015183604001518460e001518a61098a565b60e0850181905260408501919091526020840191909152606083015161037e91468d8d610aa8565b6060830152815161039a905b60408401518385602001516111b3565b81516001600160a01b0382169063dc64ef45905b60208501516060860151608087015160a088015160c089015160e0808b01516040519189901b6001600160e01b03191682526001600160a01b039788166004830152602482019690965293861660448501529185166064840152608483015260ff1660a4820152911660c482015260e4016020604051808303815f875af115801561043b573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061045f91906118b7565b8085526060830151602086015260e0830151604086015261047f906111e5565b925050506106e7565b604051602960f91b602082015288906021016040516020818303038152906040528051906020012003610545575f868060200190518101906104ca919061189c565b90505f6104db610337836080015190565b90506104f5826020015183604001518460e001518a61098a565b60e0850181905260408501919091526020840191909152606083015161051d91468d8d610aa8565b6060830152815161052d9061038a565b81516001600160a01b0382169063cd7bfd58906103ae565b604051605360f81b6020820152889060210160405160208183030381529060405280519060200120036106e4575f8680602001905181019061058791906118ce565b90505f610598610337836080015190565b90506105b3826020015183604001518461010001518a61098a565b61010085018190526040850191909152602084019190915260608301516105dc91468d8d610aa8565b606083015281516105ec9061038a565b81516001600160a01b03821690636469c44f9060208501516060860151608087015160a088015160c089015160e08a01516101008b015160405160e08a901b6001600160e01b03191681526001600160a01b039889166004820152602481019790975294871660448701529286166064860152608485019190915260ff90811660a48501521660c483015290911660e4820152610104016020604051808303815f875af115801561069f573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906106c391906118b7565b80855260608301516020860152610100830151604086015261047f906111e5565b505f5b5f935060405180602001604052805f81525092506107045f6108d9565b95509550955095915050565b604051632474521560e21b81527f97667070c54ef182b0f5858b034beac1b6f3089aa2d3188bb1e8929f4fa9b929600482018190523360248301529030906391d1485490604401602060405180830381865afa158015610772573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906107969190611953565b6107e25760405162461bcd60e51b815260206004820152601860248201527f437572766546616365743a206d697373696e6720726f6c650000000000000000604482015260640161026d565b6001600160a01b0383166108385760405162461bcd60e51b815260206004820152601860248201527f437572766546616365743a207a65726f20616464726573730000000000000000604482015260640161026d565b6001600160a01b038381165f8181527ff7ba6083e63c71062a85498a64e063b755730ac3a92d494147a56ecca6d95b42602090815260409182902080546001600160a01b0319169487169485179055815192835282019290925281517fec3d3c5fd0e9bfa5a7de49c176f7699b00e2fe558782f174a9d510c72218d69a929181900390910190a1505050565b604051605360f81b60208201526021016101b7565b807f1e0578bb15c7ba996e367f5cb2606a2c0e49f47e3ec9ce25487e41761d5624985d50565b6001600160a01b038181165f9081527ff7ba6083e63c71062a85498a64e063b755730ac3a92d494147a56ecca6d95b42602052604090205416806109855760405162461bcd60e51b815260206004820181905260248201527f437572766546616365743a20706f6f6c2061646170746572206e6f7420736574604482015260640161026d565b919050565b5f805f5f19871461099b578661099e565b83515b925085156109fd5733866001600160a01b0316146109f55760405162461bcd60e51b81526020600482015260146024820152732937baba32b91d103bb937b7339039b2b73232b960611b604482015260640161026d565b859150610a05565b836020015191505b7f4c0fc00f7060fb51f491b90bbb038205c36b6fbc6a8aed52d27b18d2967b53f45c158015610a3357508415155b15610a975733856001600160a01b031614610a905760405162461bcd60e51b815260206004820152601960248201527f526f757465723a2077726f6e6720656d657267656e6379546f00000000000000604482015260640161026d565b5083610a9e565b5060408301515b9450945094915050565b5f7f1985103ae1175f43d643c2baccb8e4caab00e02289861e71bc8dd662b8d8101f82158015610ad757508615155b80610aea57508215801590610aea575086155b610b295760405162461bcd60e51b815260206004820152601060248201526f526f757465723a2077726f6e6720746f60801b604482015260640161026d565b7f4c0fc00f7060fb51f491b90bbb038205c36b6fbc6a8aed52d27b18d2967b53f45c158015610b7d5750604051605760f81b6020820152602101604051602081830303815290604052805190602001208414155b8015610baf575060405161557760f01b6020820152602201604051602081830303815290604052805190602001208414155b15610c205733866001600160a01b031614610c205760405162461bcd60e51b815260206004820152602b60248201527f526f757465723a20656d657267656e6379546f206973206e6f7420657175616c60448201526a103a34329039b2b73232b960a91b606482015260840161026d565b82610c2d578691506111a9565b604051614c4d60f01b6020820152602201604051602081830303815290604052805190602001208303610cd757600381015460405163535cfbb760e11b81526001600160401b03871660048201526001600160a01b039091169063a6b9f76e906024015b602060405180830381865afa158015610cac573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610cd091906118b7565b91506111a9565b60405161425560f01b602082015260220160405160208183030381529060405280519060200120831480610d30575060405161424d60f01b60208201526022016040516020818303038152906040528051906020012083145b15610d705760038101546040516374a748ff60e11b81526001600160401b03871660048201526001600160a01b039091169063e94e91fe90602401610c91565b604051605760f81b60208201528390602101604051602081830303815290604052805190602001201480610dca575060405161557760f01b6020820152839060220160405160208183030381529060405280519060200120145b15610e0a5760038101546040516397c2cd9160e01b81526001600160401b03871660048201526001600160a01b03909116906397c2cd9190602401610c91565b604051604160f81b602082015260210160405160208183030381529060405280519060200120831480610e615750604051602960f91b60208201526021016040516020818303038152906040528051906020012083145b80610e905750604051605360f81b60208201526021016040516020818303038152906040528051906020012083145b15610ed05760038101546040516397c2cd9160e01b81526001600160401b03871660048201526001600160a01b03909116906397c2cd9190602401610c91565b60405162424d6f60e81b602082015260230160405160208183030381529060405280519060200120831480610f2b575060405162214d6f60e81b60208201526023016040516020818303038152906040528051906020012083145b80610f5c575060405162504d6f60e81b60208201526023016040516020818303038152906040528051906020012083145b15610f9c5760038101546040516397c2cd9160e01b81526001600160401b03871660048201526001600160a01b03909116906397c2cd9190602401610c91565b60405161525360f01b60208201526022016040516020818303038152906040528051906020012083036110045760038101546040516397c2cd9160e01b81526001600160401b03871660048201526001600160a01b03909116906397c2cd9190602401610c91565b60405161313960f11b602082015260220160405160208183030381529060405280519060200120830361106c5760038101546040516397c2cd9160e01b81526001600160401b03871660048201526001600160a01b03909116906397c2cd9190602401610c91565b604051632126a0a760e11b6020820152602401604051602081830303815290604052805190602001208314806110ca575060405164424155544f60d81b60208201526025016040516020818303038152906040528051906020012083145b1561110a5760038101546040516397c2cd9160e01b81526001600160401b03871660048201526001600160a01b03909116906397c2cd9190602401610c91565b7f33c4137323016cc703f06d36ae2c7f43f1a70fa030334bd76934bba665c80d6a83036111a95760038101546040516397c2cd9160e01b81526001600160401b03871660048201526001600160a01b03909116906397c2cd9190602401602060405180830381865afa158015611182573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906111a691906118b7565b91505b5095945050505050565b306001600160a01b038416036111d3576111ce848383611263565b6111df565b6111df848484846112cb565b50505050565b5f815f0361125b577fb13bd13498ce9409b85a4287d16ff0fcdaca732822a47a97d2bdada3325aa4515c156112535760405162461bcd60e51b8152602060048201526014602482015273437572766546616365743a20736c69707061676560601b604482015260640161026d565b506002919050565b506001919050565b6040516001600160a01b0383166024820152604481018290526112c690849063a9059cbb60e01b906064015b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b031990931692909217909152611303565b505050565b6040516001600160a01b03808516602483015283166044820152606481018290526111df9085906323b872dd60e01b9060840161128f565b5f611357826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b03166113d69092919063ffffffff16565b905080515f14806113775750808060200190518101906113779190611953565b6112c65760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b606482015260840161026d565b60606113e484845f856113ec565b949350505050565b60608247101561144d5760405162461bcd60e51b815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f6044820152651c8818d85b1b60d21b606482015260840161026d565b5f80866001600160a01b03168587604051611468919061196e565b5f6040518083038185875af1925050503d805f81146114a2576040519150601f19603f3d011682016040523d82523d5f602084013e6114a7565b606091505b50915091506114b8878383876114c3565b979650505050505050565b606083156115315782515f0361152a576001600160a01b0385163b61152a5760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e7472616374000000604482015260640161026d565b50816113e4565b6113e483838151156115465781518083602001fd5b8060405162461bcd60e51b815260040161026d9190611984565b80356001600160a01b0381168114610985575f80fd5b5f60208284031215611586575f80fd5b61158f82611560565b9392505050565b80151581146115a3575f80fd5b50565b634e487b7160e01b5f52604160045260245ffd5b60405161012081016001600160401b03811182821017156115dd576115dd6115a6565b60405290565b604051601f8201601f191681016001600160401b038111828210171561160b5761160b6115a6565b604052919050565b5f60608284031215611623575f80fd5b604051606081018181106001600160401b0382111715611645576116456115a6565b80604052508091508235815260208301356020820152604083013560408201525092915050565b5f805f805f60e08688031215611680575f80fd5b853561168b81611596565b945060208681013594506040870135935060608701356001600160401b03808211156116b5575f80fd5b818901915089601f8301126116c8575f80fd5b8135818111156116da576116da6115a6565b6116ec601f8201601f191685016115e3565b91508082528a84828501011115611701575f80fd5b80848401858401375f848284010152508094505050506117248760808801611613565b90509295509295909350565b5f81518084528060208401602086015e5f602082860101526020601f19601f83011685010191505092915050565b6001600160401b038516815260c060208201525f61177f60c0830186611730565b9050835160408301526020840151606083015260408401516080830152600383106117b857634e487b7160e01b5f52602160045260245ffd5b8260a083015295945050505050565b5f80604083850312156117d8575f80fd5b6117e183611560565b91506117ef60208401611560565b90509250929050565b805160ff81168114610985575f80fd5b5f61010080838503121561181a575f80fd5b604051908101906001600160401b038211818310171561183c5761183c6115a6565b81604052809250835181526020840151602082015260408401516040820152606084015160608201526080840151608082015260a084015160a082015261188560c085016117f8565b60c082015260e084015160e0820152505092915050565b5f61010082840312156118ad575f80fd5b61158f8383611808565b5f602082840312156118c7575f80fd5b5051919050565b5f61012082840312156118df575f80fd5b6118e76115ba565b825181526020830151602082015260408301516040820152606083015160608201526080830151608082015260a083015160a082015261192960c084016117f8565b60c082015261193a60e084016117f8565b60e0820152610100928301519281019290925250919050565b5f60208284031215611963575f80fd5b815161158f81611596565b5f82518060208501845e5f920191825250919050565b602081525f61158f602083018461173056fea2646970667358221220aa8464f9eeaffe565b89c00354f372312f40dc52786d7dc92939bd00e447a83b64736f6c63430008190033

Deployed Bytecode

0x60806040526004361061006e575f3560e01c8063bae633611161004c578063bae633611461011b578063bcf4f0a61461013e578063beadbe321461015f578063f5b541a614610173575f80fd5b80632d07ae691461007257806395ad709a146100995780639ba520ad14610107575b5f80fd5b34801561007d575f80fd5b506100866101a6565b6040519081526020015b60405180910390f35b3480156100a4575f80fd5b506100ef6100b3366004611576565b6001600160a01b039081165f9081527ff7ba6083e63c71062a85498a64e063b755730ac3a92d494147a56ecca6d95b4260205260409020541690565b6040516001600160a01b039091168152602001610090565b348015610112575f80fd5b506100866101d0565b61012e61012936600461166c565b6101e5565b604051610090949392919061175e565b348015610149575f80fd5b5061015d6101583660046117c7565b610710565b005b34801561016a575f80fd5b506100866108c4565b34801561017e575f80fd5b506100867f97667070c54ef182b0f5858b034beac1b6f3089aa2d3188bb1e8929f4fa9b92981565b604051602960f91b60208201526021015b6040516020818303038152906040528051906020012081565b604051604160f81b60208201526021016101b7565b60408051606081810183525f80835260208301819052928201839052905f7f2e445143a211ecbb689de5812742d02a96369c482aec76832fc56490cf3cc6f25c6102765760405162461bcd60e51b815260206004820152601a60248201527f526f757465723a2069736e2774204469616d6f6e642063616c6c00000000000060448201526064015b60405180910390fd5b7f1e0578bb15c7ba996e367f5cb2606a2c0e49f47e3ec9ce25487e41761d5624985c156102da5760405162461bcd60e51b8152602060048201526012602482015271526f757465723a207265656e7472616e637960701b604482015260640161026d565b6102e460016108d9565b604051604160f81b602082015288906021016040516020818303038152906040528051906020012003610488575f86806020019051810190610326919061189c565b90505f61033c610337836080015190565b6108ff565b9050610356826020015183604001518460e001518a61098a565b60e0850181905260408501919091526020840191909152606083015161037e91468d8d610aa8565b6060830152815161039a905b60408401518385602001516111b3565b81516001600160a01b0382169063dc64ef45905b60208501516060860151608087015160a088015160c089015160e0808b01516040519189901b6001600160e01b03191682526001600160a01b039788166004830152602482019690965293861660448501529185166064840152608483015260ff1660a4820152911660c482015260e4016020604051808303815f875af115801561043b573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061045f91906118b7565b8085526060830151602086015260e0830151604086015261047f906111e5565b925050506106e7565b604051602960f91b602082015288906021016040516020818303038152906040528051906020012003610545575f868060200190518101906104ca919061189c565b90505f6104db610337836080015190565b90506104f5826020015183604001518460e001518a61098a565b60e0850181905260408501919091526020840191909152606083015161051d91468d8d610aa8565b6060830152815161052d9061038a565b81516001600160a01b0382169063cd7bfd58906103ae565b604051605360f81b6020820152889060210160405160208183030381529060405280519060200120036106e4575f8680602001905181019061058791906118ce565b90505f610598610337836080015190565b90506105b3826020015183604001518461010001518a61098a565b61010085018190526040850191909152602084019190915260608301516105dc91468d8d610aa8565b606083015281516105ec9061038a565b81516001600160a01b03821690636469c44f9060208501516060860151608087015160a088015160c089015160e08a01516101008b015160405160e08a901b6001600160e01b03191681526001600160a01b039889166004820152602481019790975294871660448701529286166064860152608485019190915260ff90811660a48501521660c483015290911660e4820152610104016020604051808303815f875af115801561069f573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906106c391906118b7565b80855260608301516020860152610100830151604086015261047f906111e5565b505f5b5f935060405180602001604052805f81525092506107045f6108d9565b95509550955095915050565b604051632474521560e21b81527f97667070c54ef182b0f5858b034beac1b6f3089aa2d3188bb1e8929f4fa9b929600482018190523360248301529030906391d1485490604401602060405180830381865afa158015610772573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906107969190611953565b6107e25760405162461bcd60e51b815260206004820152601860248201527f437572766546616365743a206d697373696e6720726f6c650000000000000000604482015260640161026d565b6001600160a01b0383166108385760405162461bcd60e51b815260206004820152601860248201527f437572766546616365743a207a65726f20616464726573730000000000000000604482015260640161026d565b6001600160a01b038381165f8181527ff7ba6083e63c71062a85498a64e063b755730ac3a92d494147a56ecca6d95b42602090815260409182902080546001600160a01b0319169487169485179055815192835282019290925281517fec3d3c5fd0e9bfa5a7de49c176f7699b00e2fe558782f174a9d510c72218d69a929181900390910190a1505050565b604051605360f81b60208201526021016101b7565b807f1e0578bb15c7ba996e367f5cb2606a2c0e49f47e3ec9ce25487e41761d5624985d50565b6001600160a01b038181165f9081527ff7ba6083e63c71062a85498a64e063b755730ac3a92d494147a56ecca6d95b42602052604090205416806109855760405162461bcd60e51b815260206004820181905260248201527f437572766546616365743a20706f6f6c2061646170746572206e6f7420736574604482015260640161026d565b919050565b5f805f5f19871461099b578661099e565b83515b925085156109fd5733866001600160a01b0316146109f55760405162461bcd60e51b81526020600482015260146024820152732937baba32b91d103bb937b7339039b2b73232b960611b604482015260640161026d565b859150610a05565b836020015191505b7f4c0fc00f7060fb51f491b90bbb038205c36b6fbc6a8aed52d27b18d2967b53f45c158015610a3357508415155b15610a975733856001600160a01b031614610a905760405162461bcd60e51b815260206004820152601960248201527f526f757465723a2077726f6e6720656d657267656e6379546f00000000000000604482015260640161026d565b5083610a9e565b5060408301515b9450945094915050565b5f7f1985103ae1175f43d643c2baccb8e4caab00e02289861e71bc8dd662b8d8101f82158015610ad757508615155b80610aea57508215801590610aea575086155b610b295760405162461bcd60e51b815260206004820152601060248201526f526f757465723a2077726f6e6720746f60801b604482015260640161026d565b7f4c0fc00f7060fb51f491b90bbb038205c36b6fbc6a8aed52d27b18d2967b53f45c158015610b7d5750604051605760f81b6020820152602101604051602081830303815290604052805190602001208414155b8015610baf575060405161557760f01b6020820152602201604051602081830303815290604052805190602001208414155b15610c205733866001600160a01b031614610c205760405162461bcd60e51b815260206004820152602b60248201527f526f757465723a20656d657267656e6379546f206973206e6f7420657175616c60448201526a103a34329039b2b73232b960a91b606482015260840161026d565b82610c2d578691506111a9565b604051614c4d60f01b6020820152602201604051602081830303815290604052805190602001208303610cd757600381015460405163535cfbb760e11b81526001600160401b03871660048201526001600160a01b039091169063a6b9f76e906024015b602060405180830381865afa158015610cac573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610cd091906118b7565b91506111a9565b60405161425560f01b602082015260220160405160208183030381529060405280519060200120831480610d30575060405161424d60f01b60208201526022016040516020818303038152906040528051906020012083145b15610d705760038101546040516374a748ff60e11b81526001600160401b03871660048201526001600160a01b039091169063e94e91fe90602401610c91565b604051605760f81b60208201528390602101604051602081830303815290604052805190602001201480610dca575060405161557760f01b6020820152839060220160405160208183030381529060405280519060200120145b15610e0a5760038101546040516397c2cd9160e01b81526001600160401b03871660048201526001600160a01b03909116906397c2cd9190602401610c91565b604051604160f81b602082015260210160405160208183030381529060405280519060200120831480610e615750604051602960f91b60208201526021016040516020818303038152906040528051906020012083145b80610e905750604051605360f81b60208201526021016040516020818303038152906040528051906020012083145b15610ed05760038101546040516397c2cd9160e01b81526001600160401b03871660048201526001600160a01b03909116906397c2cd9190602401610c91565b60405162424d6f60e81b602082015260230160405160208183030381529060405280519060200120831480610f2b575060405162214d6f60e81b60208201526023016040516020818303038152906040528051906020012083145b80610f5c575060405162504d6f60e81b60208201526023016040516020818303038152906040528051906020012083145b15610f9c5760038101546040516397c2cd9160e01b81526001600160401b03871660048201526001600160a01b03909116906397c2cd9190602401610c91565b60405161525360f01b60208201526022016040516020818303038152906040528051906020012083036110045760038101546040516397c2cd9160e01b81526001600160401b03871660048201526001600160a01b03909116906397c2cd9190602401610c91565b60405161313960f11b602082015260220160405160208183030381529060405280519060200120830361106c5760038101546040516397c2cd9160e01b81526001600160401b03871660048201526001600160a01b03909116906397c2cd9190602401610c91565b604051632126a0a760e11b6020820152602401604051602081830303815290604052805190602001208314806110ca575060405164424155544f60d81b60208201526025016040516020818303038152906040528051906020012083145b1561110a5760038101546040516397c2cd9160e01b81526001600160401b03871660048201526001600160a01b03909116906397c2cd9190602401610c91565b7f33c4137323016cc703f06d36ae2c7f43f1a70fa030334bd76934bba665c80d6a83036111a95760038101546040516397c2cd9160e01b81526001600160401b03871660048201526001600160a01b03909116906397c2cd9190602401602060405180830381865afa158015611182573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906111a691906118b7565b91505b5095945050505050565b306001600160a01b038416036111d3576111ce848383611263565b6111df565b6111df848484846112cb565b50505050565b5f815f0361125b577fb13bd13498ce9409b85a4287d16ff0fcdaca732822a47a97d2bdada3325aa4515c156112535760405162461bcd60e51b8152602060048201526014602482015273437572766546616365743a20736c69707061676560601b604482015260640161026d565b506002919050565b506001919050565b6040516001600160a01b0383166024820152604481018290526112c690849063a9059cbb60e01b906064015b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b031990931692909217909152611303565b505050565b6040516001600160a01b03808516602483015283166044820152606481018290526111df9085906323b872dd60e01b9060840161128f565b5f611357826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b03166113d69092919063ffffffff16565b905080515f14806113775750808060200190518101906113779190611953565b6112c65760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b606482015260840161026d565b60606113e484845f856113ec565b949350505050565b60608247101561144d5760405162461bcd60e51b815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f6044820152651c8818d85b1b60d21b606482015260840161026d565b5f80866001600160a01b03168587604051611468919061196e565b5f6040518083038185875af1925050503d805f81146114a2576040519150601f19603f3d011682016040523d82523d5f602084013e6114a7565b606091505b50915091506114b8878383876114c3565b979650505050505050565b606083156115315782515f0361152a576001600160a01b0385163b61152a5760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e7472616374000000604482015260640161026d565b50816113e4565b6113e483838151156115465781518083602001fd5b8060405162461bcd60e51b815260040161026d9190611984565b80356001600160a01b0381168114610985575f80fd5b5f60208284031215611586575f80fd5b61158f82611560565b9392505050565b80151581146115a3575f80fd5b50565b634e487b7160e01b5f52604160045260245ffd5b60405161012081016001600160401b03811182821017156115dd576115dd6115a6565b60405290565b604051601f8201601f191681016001600160401b038111828210171561160b5761160b6115a6565b604052919050565b5f60608284031215611623575f80fd5b604051606081018181106001600160401b0382111715611645576116456115a6565b80604052508091508235815260208301356020820152604083013560408201525092915050565b5f805f805f60e08688031215611680575f80fd5b853561168b81611596565b945060208681013594506040870135935060608701356001600160401b03808211156116b5575f80fd5b818901915089601f8301126116c8575f80fd5b8135818111156116da576116da6115a6565b6116ec601f8201601f191685016115e3565b91508082528a84828501011115611701575f80fd5b80848401858401375f848284010152508094505050506117248760808801611613565b90509295509295909350565b5f81518084528060208401602086015e5f602082860101526020601f19601f83011685010191505092915050565b6001600160401b038516815260c060208201525f61177f60c0830186611730565b9050835160408301526020840151606083015260408401516080830152600383106117b857634e487b7160e01b5f52602160045260245ffd5b8260a083015295945050505050565b5f80604083850312156117d8575f80fd5b6117e183611560565b91506117ef60208401611560565b90509250929050565b805160ff81168114610985575f80fd5b5f61010080838503121561181a575f80fd5b604051908101906001600160401b038211818310171561183c5761183c6115a6565b81604052809250835181526020840151602082015260408401516040820152606084015160608201526080840151608082015260a084015160a082015261188560c085016117f8565b60c082015260e084015160e0820152505092915050565b5f61010082840312156118ad575f80fd5b61158f8383611808565b5f602082840312156118c7575f80fd5b5051919050565b5f61012082840312156118df575f80fd5b6118e76115ba565b825181526020830151602082015260408301516040820152606083015160608201526080830151608082015260a083015160a082015261192960c084016117f8565b60c082015261193a60e084016117f8565b60e0820152610100928301519281019290925250919050565b5f60208284031215611963575f80fd5b815161158f81611596565b5f82518060208501845e5f920191825250919050565b602081525f61158f602083018461173056fea2646970667358221220aa8464f9eeaffe565b89c00354f372312f40dc52786d7dc92939bd00e447a83b64736f6c63430008190033

Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

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

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits
Loading...
Loading

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.