ETH Price: $1,803.75 (-5.38%)
 

Overview

ETH Balance

0 ETH

Eth Value

$0.00

More Info

Private Name Tags

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To

There are no matching entries

1 Internal Transaction found.

Latest 1 internal transaction

Advanced mode:
Parent Transaction Hash Method Block
From
To
0x60a06040226219792025-06-03 5:14:47266 days ago1748927687  Contract Creation0 ETH
Loading...
Loading
Loading...
Loading
Cross-Chain Transactions

Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

Validator Index Block Amount
View All Withdrawals

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

Contract Source Code Verified (Exact Match)

Contract Name:
FunRouter

Compiler Version
v0.8.27+commit.40a35a09

Optimization Enabled:
Yes with 10000 runs

Other Settings:
cancun EvmVersion
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.27;

import {SwapAndAction} from "@fils/Composers/SwapAndAction.sol";
import {CreateAndForward} from "@fils/Composers/CreateAndForward.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";

/// @title FunRouter
/// @notice Smart contract that routes FILS operations and manages token swaps, action execution, and emergency functions
/// @dev Inherits from SwapAndAction and CreateAndForward to handle various protocol operations
/// @dev Includes operator management and emergency rescue functions
contract FunRouter is SwapAndAction {
    using SafeERC20 for IERC20;

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

    /// @notice Thrown when trying to transfer more tokens than available
    /// @param balance Current balance available
    /// @param amount Amount attempting to transfer
    error InsufficientBalance(uint256 balance, uint256 amount);

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

    /// @notice Emitted when an operator is removed
    /// @param operator The address of the removed operator
    event OperatorInvalidated(address operator);

    /// @notice Emitted when an operator is added
    /// @param operator The address of the added operator
    event OperatorAdded(address operator);

    /// @notice Emitted when native ETH tokens are rescued from the contract
    /// @dev Triggered during sweep or die operations when ETH is transferred to the guardian
    /// @param balance Amount of native ETH rescued
    event NativeRescued(uint256 balance);

    /// @notice Emitted when ERC20 tokens are rescued from the contract
    /// @dev Triggered during sweep or die operations when tokens are transferred to the guardian
    /// @param token Address of the rescued token
    /// @param balance Amount of tokens rescued
    event ERC20Rescued(address token, uint256 balance);
    /*//////////////////////////////////////////////////////////////
                               CONSTRUCTOR
    //////////////////////////////////////////////////////////////*/

    /// @notice Parameters for sweeping tokens from the contract
    /// @dev Use address(0) in tokens array to represent native ETH
    /// @dev Use type(uint256).max in amounts array to sweep the entire balance
    /// @custom:member tokens Array of token addresses to sweep (address(0) for native ETH)
    /// @custom:member amounts Array of amounts to sweep (type(uint256).max for entire balance)
    struct SweepParams {
        address[] tokens;
        uint256[] amounts;
    }

    /// @notice Creates a new FunRouter contract
    /// @dev Initializes the contract with a list of operators
    /// @param operators Array of addresses to be set as initial operators
    constructor(
        address[] memory operators
    ) SwapAndAction(operators) {}

    /// @notice Adds a new operator
    /// @dev Can only be called by the guardian
    /// @dev Emits an OperatorAdded event
    /// @param operator Address to add as an operator
    function addOperator(address operator) external onlyGuardian {
        isOperator[operator] = true;
        OPERATORS.push(operator);
        emit OperatorAdded(operator);
    }

    /// @notice Removes an operator
    /// @dev Can only be called by the guardian
    /// @dev Uses swap-and-pop pattern for gas efficiency when removing from array
    /// @dev Emits an OperatorInvalidated event
    /// @param operator Address to remove from operators
    function invalidateOperator(address operator) external onlyGuardian {
        isOperator[operator] = false;
        for (uint256 i = 0; i < OPERATORS.length; i++) {
            if (OPERATORS[i] == operator) {
                OPERATORS[i] = OPERATORS[OPERATORS.length - 1];
                OPERATORS.pop();
            }
        }
        emit OperatorInvalidated(operator);
    }

    /// @notice Emergency function to rescue ERC20 tokens stuck in the contract
    /// @dev Can only be called by the guardian
    /// @dev Uses safeTransfer to handle non-standard tokens
    /// @param token The ERC20 token address to rescue
    /// @param amount The amount of tokens to rescue
    function rescueERC20(address token, uint256 amount) internal onlyGuardian {
        IERC20(token).safeTransfer(GUARDIAN, amount);
    }

    /// @notice Emergency function to rescue native tokens stuck in the contract
    /// @dev Can only be called by the guardian
    /// @dev Reverts if attempting to rescue more than the available balance
    /// @dev Emits a NativeRescued event upon success
    /// @param amount The amount of native tokens to rescue
    function rescueNative(uint256 amount) internal onlyGuardian {
        uint256 balance = address(this).balance;
        if (balance < amount) revert InsufficientBalance(balance, amount);
        payable(GUARDIAN).transfer(amount);
        balance -= address(this).balance;
        emit NativeRescued(balance);
    }

    /// @notice Sweeps specified tokens and ETH from the contract to the guardian
    /// @dev Can only be called by the guardian
    /// @dev For each token, specifies either an exact amount or the entire balance
    /// @dev Any remaining ETH will also be rescued
    /// @dev Emits ERC20Rescued and NativeRescued events
    /// @param params SweepParams struct containing tokens and amounts to sweep
    function sweep(SweepParams calldata params) public onlyGuardian {
        for (uint256 i = 0; i < params.tokens.length; i++) {
            if (params.tokens[i] == address(0)) {
                rescueNative(
                    params.amounts[i] == type(uint256).max
                        ? address(this).balance
                        : params.amounts[i]
                );
            } else {
                rescueERC20(
                    params.tokens[i],
                    params.amounts[i] == type(uint256).max
                        ? IERC20(params.tokens[i]).balanceOf(address(this))
                        : params.amounts[i]
                );
            }
        }
        rescueNative(address(this).balance);
    }

    /// @notice Emergency function to sweep tokens and pause the contract
    /// @dev Can only be called by the guardian
    /// @dev First sweeps all tokens via the sweep function, then pauses the contract
    /// @dev Used in emergency situations to prevent further interactions
    /// @dev This function is designed as a kill switch for the contract
    /// @param params SweepParams struct containing tokens and amounts to sweep
    function die(SweepParams calldata params) external onlyGuardian {
        sweep(params);
        isPaused = true;
    }
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.27;

import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {ReentrancyGuard} from "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
import {ActionsRouter} from "@fils/Composers/utils/ActionsRouter.sol";
import {ActionParams} from "@fils/Actions/IRoutePreference.sol";

/// @title SwapAndAction
/// @notice Smart contract that handles swap and post-swap action operations.
/// @dev Uses SafeERC20 for all token operations to handle non-standard tokens.
/// @dev Only supports Base and Arbitrum networks.
/// @dev Implements pause functionality for emergency situations.
abstract contract SwapAndAction is ReentrancyGuard, ActionsRouter {
    using SafeERC20 for IERC20;

    /// @notice The guardian address that can pause/unpause the contract and perform emergency operations
    /// @dev Immutable to prevent unauthorized changes to the guardian address
    address public immutable GUARDIAN =
        address(0x2A187bE85c3E6ecAC4C1cdcE40843bdd3476677D);

    /// @notice Array of all operator addresses
    /// @dev Used for tracking all operators and iteration
    address[] public OPERATORS;

    /// @notice Mapping of addresses to their operator status
    /// @dev Quick lookup for checking if an address is an operator
    mapping(address => bool) public isOperator;
    /*//////////////////////////////////////////////////////////////
                                 EVENTS
    //////////////////////////////////////////////////////////////*/

    /// @notice Emitted when tokens are swapped.
    /// @param fromToken The token being swapped from.
    /// @param toToken The token being swapped to.
    /// @param fromAmount The amount of fromToken being swapped.
    /// @param toAmount The amount of toToken received.
    event TokenSwapped(
        address indexed fromToken,
        address indexed toToken,
        uint256 fromAmount,
        uint256 toAmount
    );

    /// @notice Emitted when a swap and action operation is completed.
    /// @param fromToken The token that was swapped from.
    /// @param fromAmount The amount that was swapped.
    /// @param toToken The token that was used in the action.
    /// @param toAmount The amount that was used in the action.
    event SwapAndActionCompleted(
        address indexed fromToken,
        uint256 fromAmount,
        address indexed toToken,
        uint256 indexed toAmount
    );

    /// @notice Emitted when a swap is completed.
    /// @param fromToken The token that was swapped from.
    /// @param fromAmount The amount that was swapped.
    /// @param toToken The token that was swapped to.
    /// @param toAmount The amount that was swapped to.
    event SwapCompleted(
        address indexed fromToken,
        uint256 fromAmount,
        address indexed toToken,
        uint256 toAmount
    );

    /// @notice Emitted when the contract is paused.
    event Paused();

    /// @notice Emitted when the contract is unpaused.
    event Unpaused();

    /// @notice Thrown when a non-operator calls a function restricted to operators
    error NotOperator();

    /// @notice Thrown when a caller is neither an operator nor the guardian
    error NotOperatorOrGuardian(
        address caller,
        address guardian,
        address[] operators
    );

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

    /// @notice Thrown when a swap operation fails.
    error SwapFailed();

    /// @notice Thrown when the caller is not the guardian.
    error NotGuardian();

    /// @notice Thrown when swap output is below minimum amount.
    /// @param minimum The minimum amount expected.
    /// @param actual The actual amount received.
    error SlippageExceeded(uint256 minimum, uint256 actual);

    /// @notice Thrown when the balance after a swap is less than the balance before the swap.
    /// @param balanceBefore The balance before the swap.
    /// @param balanceAfter The balance after the swap.
    error BalanceAfterSwapReduced(uint256 balanceBefore, uint256 balanceAfter);

    /// @notice Thrown when the contract is paused.
    error ContractPaused();

    /// @notice Flag to indicate if the contract is paused
    /// @dev When true, all core functionality is disabled
    bool public isPaused;

    /// @notice Mapping to track used deposit addresses
    /// @dev Prevents double spending bridge transactions by ensuring deposit addresses are only used once
    mapping(address => bool) public usedDepositAddresses;

    /// @notice Thrown when a deposit address has already been used
    error DepositAddressAlreadyUsed(address depositAddress);

    /// @notice Modifier to ensure the contract is not paused
    /// @dev Reverts with ContractPaused if the contract is paused
    modifier onlyWhenNotPaused() {
        if (isPaused) revert ContractPaused();
        _;
    }

    /// @notice Modifier to ensure only the guardian can call a function
    /// @dev Reverts with NotGuardian if the caller is not the guardian
    modifier onlyGuardian() {
        if (msg.sender != GUARDIAN) revert NotGuardian();
        _;
    }

    /// @notice Restricts function calls to operators only
    /// @dev Reverts with NotOperator if the caller is not an operator
    modifier onlyOperator() {
        if (!isOperator[msg.sender]) revert NotOperator();
        _;
    }

    /// @notice Restricts function calls to operators or the guardian
    /// @dev Reverts with NotOperatorOrGuardian if the caller is neither
    modifier onlyOperatorOrGuardian() {
        if (!isOperator[msg.sender] && msg.sender != GUARDIAN)
            revert NotOperatorOrGuardian(msg.sender, GUARDIAN, OPERATORS);
        _;
    }
    /*//////////////////////////////////////////////////////////////
                               CONSTRUCTOR
    //////////////////////////////////////////////////////////////*/

    /// @notice Creates a new SwapAndAction contract.
    constructor(address[] memory operators) ActionsRouter() {
        OPERATORS = operators;
        for (uint256 i = 0; i < OPERATORS.length; i++) {
            isOperator[OPERATORS[i]] = true;
        }
    }

    /*//////////////////////////////////////////////////////////////
                               CORE LOGIC
    //////////////////////////////////////////////////////////////*/

    /// @notice Executes a swap and post-swap action operation using direct token transfer
    /// @dev Handles token transfers, swaps, and post-swap actions
    /// @dev Checks for slippage and ensures swap success
    /// @param params The action parameters containing swap and action details
    /// @return uint256 The amount of tokens used in the action
    function swapAndAction(
        ActionParams calldata params
    )
        external
        nonReentrant
        onlyWhenNotPaused
        onlyOperatorOrGuardian
        returns (uint256)
    {
        // Check if deposit address has already been used
        address depositAddress = params.depositAddress;
        if (depositAddress != address(0)) {
            if (usedDepositAddresses[depositAddress]) {
                revert DepositAddressAlreadyUsed(depositAddress);
            }
        }

        // Transfer tokens directly from the caller to this contract
        IERC20(params.tokenIn).safeTransferFrom(
            msg.sender,
            address(this),
            params.fromAmount
        );

        uint256 balanceBefore = IERC20(params.tokenOut).balanceOf(
            address(this)
        );
        uint256 swapOutput;

        // @dev if there is a swap target, we need to approve the swap target
        // and then execute the swap
        if (params.swapTarget != address(0)) {
            // Approve the swap target if necessary
            approveIfBelow(
                params.tokenIn,
                params.swapTarget,
                params.fromAmount
            );

            // Execute swap
            (bool swapSuccess, ) = params.swapTarget.call(params.swapCallData);
            if (!swapSuccess) revert SwapFailed();

            // Check swap output
            // if the diff is less than the minimum amount out, revert
            // this accounts for tokens that are moving in the same block
            uint256 balanceAfter = IERC20(params.tokenOut).balanceOf(
                address(this)
            );

            // ensure we wont overflow on the subtraction and revert
            // if the balance after is less than the balance before
            // or if the balance after is equal to the balance before
            // meaning the swap failed somehow
            if (balanceAfter <= balanceBefore)
                revert BalanceAfterSwapReduced(balanceBefore, balanceAfter);

            swapOutput = balanceAfter - balanceBefore;

            // ensure the swap output is greater than the minimum amount out
            if (swapOutput < params.minimumAmountOut)
                revert SlippageExceeded(params.minimumAmountOut, swapOutput);

            emit SwapCompleted(
                params.tokenIn,
                params.fromAmount,
                params.tokenOut,
                swapOutput
            );
        } else {
            // if the swap target is the 0 address, we dont need to approve or swap
            // so we just use the from amount to perform the action directly
            swapOutput = params.fromAmount;
        }

        // Ensure we have enough tokens from the swap to fulfill the toAmount
        if (swapOutput < params.toAmount)
            revert SlippageExceeded(params.toAmount, swapOutput);

        // Execute action through ActionsRouter
        (bool actionSuccess, uint256 amountUsed) = handleAction(
            params,
            params.toAmount
        );
        emit ActionExecuted(params.routePreference, actionSuccess);

        emit SwapAndActionCompleted(
            params.tokenIn,
            params.fromAmount,
            params.tokenOut,
            amountUsed
        );

        // Mark deposit address as used after successful transaction
        if (params.depositAddress != address(0)) {
            usedDepositAddresses[params.depositAddress] = true;
        }

        return amountUsed;
    }

    /// @notice Approves a spender to spend tokens if the current allowance is insufficient
    /// @dev Uses type(uint256).max for approval to save gas on future approvals
    /// @param token The token to approve
    /// @param spender The address to approve
    /// @param amount The minimum amount needed for approval
    function approveIfBelow(
        address token,
        address spender,
        uint256 amount
    ) internal {
        uint256 allowance = IERC20(token).allowance(address(this), spender);
        if (allowance < amount) {
            IERC20(token).approve(spender, amount);
        }
    }

    /// @notice Pauses the contract
    /// @dev Can only be called by the guardian
    /// @dev Emits a Paused event
    function pause() external onlyGuardian {
        isPaused = true;
        emit Paused();
    }

    /// @notice Unpauses the contract
    /// @dev Can only be called by the guardian
    /// @dev Emits an Unpaused event
    function unpause() external onlyGuardian {
        isPaused = false;
        emit Unpaused();
    }
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.27;

import {ICheckoutPool, CheckoutState} from "@fils/Composers/utils/Interfaces/ICheckoutPool.sol";
import {ICreate2ForwarderFactory, ICreate2Forwarder} from "@fils/Composers/utils/Interfaces/ICreate2ForwarderFactory.sol";

contract CheckoutPoolConfig {
    ICheckoutPool public checkoutPool =
        ICheckoutPool(0x1929347E025D4F5F8D6B2Bd2261e2f4EfcAcd215);
    ICreate2ForwarderFactory public forwarderFactory =
        ICreate2ForwarderFactory(0x8629Bcd3b8a90fD7247F0c0E0e1434C69DADCCA7);
}

contract CreateAndForward is CheckoutPoolConfig {
    function createAndForward(
        CheckoutState memory checkout,
        bytes32 salt
    ) external returns (ICreate2Forwarder) {
        ICreate2Forwarder forwarder = forwarderFactory.createAndForward(
            checkout,
            salt
        );
        return forwarder;
    }

    function create(
        CheckoutState memory checkout,
        bytes32 salt
    ) external returns (ICreate2Forwarder) {
        ICreate2Forwarder forwarder = forwarderFactory.create(checkout, salt);
        return forwarder;
    }

    function forward(
        ICreate2Forwarder forwarder
    ) external {
        forwarder.forward();
    }
}

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

pragma solidity ^0.8.20;

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

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

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

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

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

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

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

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

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

pragma solidity ^0.8.20;

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

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

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

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

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

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

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

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

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

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

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

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

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

    /**
     * @dev Performs an {ERC1363} approveAndCall, with a fallback to the simple {ERC20} approve if the target has no
     * code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when
     * targeting contracts.
     *
     * NOTE: When the recipient address (`to`) has no code (i.e. is an EOA), this function behaves as {forceApprove}.
     * Opposedly, when the recipient address (`to`) has code, this function only attempts to call {ERC1363-approveAndCall}
     * once without retrying, and relies on the returned value to be true.
     *
     * Reverts if the returned value is other than `true`.
     */
    function approveAndCallRelaxed(IERC1363 token, address to, uint256 value, bytes memory data) internal {
        if (to.code.length == 0) {
            forceApprove(token, to, value);
        } else if (!token.approveAndCall(to, value, data)) {
            revert SafeERC20FailedOperation(address(token));
        }
    }

    /**
     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
     * on the return value: the return value is optional (but if data is returned, it must not be false).
     * @param token The token targeted by the call.
     * @param data The call data (encoded using abi.encode or one of its variants).
     *
     * This is a variant of {_callOptionalReturnBool} that reverts if call fails to meet the requirements.
     */
    function _callOptionalReturn(IERC20 token, bytes memory data) private {
        uint256 returnSize;
        uint256 returnValue;
        assembly ("memory-safe") {
            let success := call(gas(), token, 0, add(data, 0x20), mload(data), 0, 0x20)
            // bubble errors
            if iszero(success) {
                let ptr := mload(0x40)
                returndatacopy(ptr, 0, returndatasize())
                revert(ptr, returndatasize())
            }
            returnSize := returndatasize()
            returnValue := mload(0)
        }

        if (returnSize == 0 ? address(token).code.length == 0 : returnValue != 1) {
            revert SafeERC20FailedOperation(address(token));
        }
    }

    /**
     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
     * on the return value: the return value is optional (but if data is returned, it must not be false).
     * @param token The token targeted by the call.
     * @param data The call data (encoded using abi.encode or one of its variants).
     *
     * This is a variant of {_callOptionalReturn} that silently catches all reverts and returns a bool instead.
     */
    function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
        bool success;
        uint256 returnSize;
        uint256 returnValue;
        assembly ("memory-safe") {
            success := call(gas(), token, 0, add(data, 0x20), mload(data), 0, 0x20)
            returnSize := returndatasize()
            returnValue := mload(0)
        }
        return success && (returnSize == 0 ? address(token).code.length > 0 : returnValue == 1);
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/ReentrancyGuard.sol)

pragma solidity ^0.8.20;

/**
 * @dev Contract module that helps prevent reentrant calls to a function.
 *
 * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
 * available, which can be applied to functions to make sure there are no nested
 * (reentrant) calls to them.
 *
 * Note that because there is a single `nonReentrant` guard, functions marked as
 * `nonReentrant` may not call one another. This can be worked around by making
 * those functions `private`, and then adding `external` `nonReentrant` entry
 * points to them.
 *
 * TIP: If EIP-1153 (transient storage) is available on the chain you're deploying at,
 * consider using {ReentrancyGuardTransient} instead.
 *
 * TIP: If you would like to learn more about reentrancy and alternative ways
 * to protect against it, check out our blog post
 * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
 */
abstract contract ReentrancyGuard {
    // Booleans are more expensive than uint256 or any type that takes up a full
    // word because each write operation emits an extra SLOAD to first read the
    // slot's contents, replace the bits taken up by the boolean, and then write
    // back. This is the compiler's defense against contract upgrades and
    // pointer aliasing, and it cannot be disabled.

    // The values being non-zero value makes deployment a bit more expensive,
    // but in exchange the refund on every call to nonReentrant will be lower in
    // amount. Since refunds are capped to a percentage of the total
    // transaction's gas, it is best to keep them low in cases like this one, to
    // increase the likelihood of the full refund coming into effect.
    uint256 private constant NOT_ENTERED = 1;
    uint256 private constant ENTERED = 2;

    uint256 private _status;

    /**
     * @dev Unauthorized reentrant call.
     */
    error ReentrancyGuardReentrantCall();

    constructor() {
        _status = NOT_ENTERED;
    }

    /**
     * @dev Prevents a contract from calling itself, directly or indirectly.
     * Calling a `nonReentrant` function from another `nonReentrant`
     * function is not supported. It is possible to prevent this from happening
     * by making the `nonReentrant` function external, and making it call a
     * `private` function that does the actual work.
     */
    modifier nonReentrant() {
        _nonReentrantBefore();
        _;
        _nonReentrantAfter();
    }

    function _nonReentrantBefore() private {
        // On the first call to nonReentrant, _status will be NOT_ENTERED
        if (_status == ENTERED) {
            revert ReentrancyGuardReentrantCall();
        }

        // Any calls to nonReentrant after this point will fail
        _status = ENTERED;
    }

    function _nonReentrantAfter() private {
        // By storing the original value once again, a refund is triggered (see
        // https://eips.ethereum.org/EIPS/eip-2200)
        _status = NOT_ENTERED;
    }

    /**
     * @dev Returns true if the reentrancy guard is currently set to "entered", which indicates there is a
     * `nonReentrant` function in the call stack.
     */
    function _reentrancyGuardEntered() internal view returns (bool) {
        return _status == ENTERED;
    }
}

File 7 of 18 : ActionsRouter.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.27;

import {VertexUtils} from "@fils/Actions/Vertex/VertexUtils.sol";
import {RoutePreference, ActionParams} from "@fils/Actions/IRoutePreference.sol";
import {TransferUtils} from "@fils/Actions/utils/TransferUtils.sol";
/// @notice Router contract that handles all different actions.
/// @dev Inherited by SwapAndAction to handle post-swap actions.
abstract contract ActionsRouter is VertexUtils, TransferUtils {
    /*//////////////////////////////////////////////////////////////
                                 EVENTS
    //////////////////////////////////////////////////////////////*/

    /// @notice Emitted when a post-swap action is executed.
    /// @param routePreference The route preference used for the action.
    /// @param success Whether the action was successful.
    event ActionExecuted(uint8 indexed routePreference, bool success);

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

    /// @notice Thrown when an invalid route preference is specified.
    error InvalidRoutePreference(uint8 invalidPreference);

    /*//////////////////////////////////////////////////////////////
                               CORE LOGIC
    //////////////////////////////////////////////////////////////*/

    /// @notice Handles the action based on the route preference.
    /// @param actionParams The action parameters.
    /// @return success Whether the action was successful.
    function handleAction(
        ActionParams calldata actionParams,
        uint256 inputAmount
    ) internal returns (bool success, uint256 actionPerformedWithAmount) {
        if (actionParams.routePreference == uint8(RoutePreference.VERTEX)) {
            (success, actionPerformedWithAmount) = handleVertexDeposit(
                actionParams,
                inputAmount
            );
        } else if (
            actionParams.routePreference ==
            uint8(RoutePreference.DIRECT_TRANSFER) ||
            actionParams.routePreference == uint8(RoutePreference.OSTIUM) ||
            actionParams.routePreference == uint8(RoutePreference.POLYMARKET) ||
            actionParams.routePreference == uint8(RoutePreference.BANKR) ||
            actionParams.routePreference == uint8(RoutePreference.SUSHISWAP) ||
            actionParams.routePreference == uint8(RoutePreference.QUICKSWAP) ||
            actionParams.routePreference == uint8(RoutePreference.ETHERFI)
        ) {
            // just send the tokens to the recipient
            (success, actionPerformedWithAmount) = handleDirectTransfer(
                actionParams,
                inputAmount
            );
        } else {
            revert InvalidRoutePreference(actionParams.routePreference);
        }
        emit ActionExecuted(actionParams.routePreference, success);
    }
}

File 8 of 18 : IRoutePreference.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.27;

/// @notice Enum defining the available route preferences for actions.
enum RoutePreference {
    NONE,
    DIRECT_TRANSFER,
    POLYMARKET,
    OSTIUM,
    VERTEX,
    BANKR,
    BSX,
    SUSHISWAP,
    QUICKSWAP,
    ETHERFI
}

error UnsupportedChain(RoutePreference routePreference, uint256 chainId);

/// @notice Parameters for direct token transfer and actions.
struct ActionParams {
    address tokenIn;
    address tokenOut;
    uint256 fromAmount;
    uint256 minimumAmountOut;
    uint256 toAmount;
    address swapTarget;
    bytes swapCallData;
    uint8 routePreference;
    bytes actionCallData;
    address depositAddress;
}

// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.27;

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

/// @dev Immutable parameters of a checkout account.
struct CheckoutParams {
    bytes32 userOpHash;
    bytes32 targetAsset;
    uint96 targetChainId;
    uint128 targetAmount;
    uint128 expiration;
    bytes32 recipient;
}

/// @dev State of a checkout account.
struct CheckoutState {
    CheckoutParams params;
    IERC20 heldAsset;
    uint256 heldAmount;
}

struct SwapParams {
    address target;
    address spender;
    bytes callData;
    address receivedAsset;
    bool isETHSwap;
}

struct BridgeParams {
    address target;
    address spender;
    bytes callData;
    IERC20 bridgeReceivedAsset;
    uint256 minBridgeReceivedAmount;
}

interface ICheckoutPool {
    function deposit(CheckoutState calldata checkoutState) external;

    function swap(address depositAddress, SwapParams calldata swapParams) external;

    function bridge(address depositAddress, BridgeParams calldata bridgeParams) external;

    function checkoutExists(address depositAddress) external view returns (bool);

    function getCheckout(address depositAddress) external view returns (CheckoutState memory);

    function getCheckoutOrZero(address depositAddress) external view returns (CheckoutState memory);

    function forwardFund(address depositAddress) external;
}

// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;

import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { CheckoutState } from "@fils/Composers/utils/Interfaces/ICheckoutPool.sol";

interface ICreate2ForwarderFactory {
    error AlreadyForwarded();
    error ForwardError(bytes errorData);
    error Underfunded(uint256 actualHeldAmount, uint256 minSourceAmount);

    function createAndForward(CheckoutState calldata checkout, bytes32 salt) external returns (ICreate2Forwarder);

    function create(CheckoutState calldata checkout, bytes32 salt) external returns (ICreate2Forwarder);

    function getProxyCreationCode() external pure returns (bytes memory);

    function getAddressForChain(
        CheckoutState calldata checkout,
        bytes32 salt,
        uint256 chainId
    )
        external
        view
        returns (address payable);
}

interface ICreate2Forwarder {
    function forward() external;
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (interfaces/IERC1363.sol)

pragma solidity ^0.8.20;

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

/**
 * @title IERC1363
 * @dev Interface of the ERC-1363 standard as defined in the https://eips.ethereum.org/EIPS/eip-1363[ERC-1363].
 *
 * Defines an extension interface for ERC-20 tokens that supports executing code on a recipient contract
 * after `transfer` or `transferFrom`, or code on a spender contract after `approve`, in a single transaction.
 */
interface IERC1363 is IERC20, IERC165 {
    /*
     * Note: the ERC-165 identifier for this interface is 0xb0202a11.
     * 0xb0202a11 ===
     *   bytes4(keccak256('transferAndCall(address,uint256)')) ^
     *   bytes4(keccak256('transferAndCall(address,uint256,bytes)')) ^
     *   bytes4(keccak256('transferFromAndCall(address,address,uint256)')) ^
     *   bytes4(keccak256('transferFromAndCall(address,address,uint256,bytes)')) ^
     *   bytes4(keccak256('approveAndCall(address,uint256)')) ^
     *   bytes4(keccak256('approveAndCall(address,uint256,bytes)'))
     */

    /**
     * @dev Moves a `value` amount of tokens from the caller's account to `to`
     * and then calls {IERC1363Receiver-onTransferReceived} on `to`.
     * @param to The address which you want to transfer to.
     * @param value The amount of tokens to be transferred.
     * @return A boolean value indicating whether the operation succeeded unless throwing.
     */
    function transferAndCall(address to, uint256 value) external returns (bool);

    /**
     * @dev Moves a `value` amount of tokens from the caller's account to `to`
     * and then calls {IERC1363Receiver-onTransferReceived} on `to`.
     * @param to The address which you want to transfer to.
     * @param value The amount of tokens to be transferred.
     * @param data Additional data with no specified format, sent in call to `to`.
     * @return A boolean value indicating whether the operation succeeded unless throwing.
     */
    function transferAndCall(address to, uint256 value, bytes calldata data) external returns (bool);

    /**
     * @dev Moves a `value` amount of tokens from `from` to `to` using the allowance mechanism
     * and then calls {IERC1363Receiver-onTransferReceived} on `to`.
     * @param from The address which you want to send tokens from.
     * @param to The address which you want to transfer to.
     * @param value The amount of tokens to be transferred.
     * @return A boolean value indicating whether the operation succeeded unless throwing.
     */
    function transferFromAndCall(address from, address to, uint256 value) external returns (bool);

    /**
     * @dev Moves a `value` amount of tokens from `from` to `to` using the allowance mechanism
     * and then calls {IERC1363Receiver-onTransferReceived} on `to`.
     * @param from The address which you want to send tokens from.
     * @param to The address which you want to transfer to.
     * @param value The amount of tokens to be transferred.
     * @param data Additional data with no specified format, sent in call to `to`.
     * @return A boolean value indicating whether the operation succeeded unless throwing.
     */
    function transferFromAndCall(address from, address to, uint256 value, bytes calldata data) external returns (bool);

    /**
     * @dev Sets a `value` amount of tokens as the allowance of `spender` over the
     * caller's tokens and then calls {IERC1363Spender-onApprovalReceived} on `spender`.
     * @param spender The address which will spend the funds.
     * @param value The amount of tokens to be spent.
     * @return A boolean value indicating whether the operation succeeded unless throwing.
     */
    function approveAndCall(address spender, uint256 value) external returns (bool);

    /**
     * @dev Sets a `value` amount of tokens as the allowance of `spender` over the
     * caller's tokens and then calls {IERC1363Spender-onApprovalReceived} on `spender`.
     * @param spender The address which will spend the funds.
     * @param value The amount of tokens to be spent.
     * @param data Additional data with no specified format, sent in call to `spender`.
     * @return A boolean value indicating whether the operation succeeded unless throwing.
     */
    function approveAndCall(address spender, uint256 value, bytes calldata data) external returns (bool);
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.27;

import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {IVertexEndpoint} from "@fils/Actions/Vertex/Interfaces/IVertexEndpoint.sol";
import {IVertex} from "@fils/Actions/Vertex/Interfaces/IVertexRouter.sol";
import {IClearinghouse, IVertexEndpoint} from "@fils/Actions/Vertex/Interfaces/IVertexEndpoint.sol";
import {RoutePreference, UnsupportedChain} from "@fils/Actions/IRoutePreference.sol";
import {ReentrancyGuard} from "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
import {ActionParams} from "@fils/Actions/IRoutePreference.sol";

contract VertexConfig {
    /// @notice The Vertex Endpoint contract for Base.
    IVertexEndpoint public constant BASE_ENDPOINT =
        IVertexEndpoint(0x92C2201D48481e2d42772Da02485084A4407Bbe2);

    /// @notice The Vertex Endpoint contract for Arbitrum.
    IVertexEndpoint public constant ARBITRUM_ENDPOINT =
        IVertexEndpoint(0xbbEE07B3e8121227AfCFe1E2B82772246226128e);

    /// @notice The Vertex Clearinghouse for Base.
    IClearinghouse public constant BASE_CLEARINGHOUSE =
        IClearinghouse(0xE46Cb729F92D287F6459bDA6899434E22eCC48AE);

    /// @notice The Vertex Clearinghouse for Arbitrum.
    IClearinghouse public constant ARBITRUM_CLEARINGHOUSE =
        IClearinghouse(0xAE1ec28d6225dCE2ff787dcb8CE11cF6D3AE064f);

    /// @notice Gets the Vertex Endpoint for the current chain.
    /// @return endpoint The Vertex Endpoint contract.
    function getEndpoint() public view returns (IVertexEndpoint endpoint) {
        uint256 chainId = block.chainid;
        if (chainId == 8453) return BASE_ENDPOINT;
        if (chainId == 42161) return ARBITRUM_ENDPOINT;
        revert UnsupportedChain(RoutePreference.VERTEX, chainId);
    }
}
/// @notice Utility contract for Vertex-specific actions.
/// @dev Handles deposits into Vertex subaccounts.

contract VertexUtils is IVertex, VertexConfig {
    using SafeERC20 for IERC20;

    // token addresses on Base
    address private constant BASE_WETH =
        0x4200000000000000000000000000000000000006;
    address private constant BASE_BENJI =
        0xBC45647eA894030a4E9801Ec03479739FA2485F0;

    // Token addresses on Arbitrum
    address private constant ARBITRUM_WETH =
        0x82aF49447D8a07e3bd95BD0d56f35241523fBab1;
    address private constant ARBITRUM_WBTC =
        0x2f2a2543B76A4166549F7aaB2e75Bef0aefC5B0f;
    address private constant ARBITRUM_WSTETH =
        0x5979D7b546E38E414F7E9822514be443A4800529;
    address private constant ARBITRUM_VRTX =
        0x95146881b86B3ee99e63705eC87AfE29Fcc044D9;
    address private constant ARBITRUM_USDT =
        0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9;
    address private constant ARBITRUM_ARB =
        0x912CE59144191C1204E64559FE8253a0e49E6548;

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

    /// @notice Thrown when the Vertex approval fails.
    error VertexApprovalFailed(
        address depositAsset,
        address spender,
        address endpoint,
        uint256 amount
    );

    /// @notice Thrown when the balance after deposit is higher than before deposit.
    error BalanceAfterDepositIncreased(
        uint256 balanceBefore,
        uint256 balanceAfter
    );

    /// @notice Emitted when a Vertex deposit is completed.
    event VertexDepositCompleted(bytes32 indexed subaccount, uint256 amount);

    /*//////////////////////////////////////////////////////////////
                               CORE LOGIC
    //////////////////////////////////////////////////////////////*/

    /// @notice Decodes the action calldata into DepositParams.
    /// @param actionCallData The calldata to decode.
    /// @return params The decoded deposit parameters.
    function decodeDepositParams(
        bytes calldata actionCallData
    ) public pure returns (DepositParams memory params) {
        return abi.decode(actionCallData, (DepositParams));
    }

    function handleVertexDeposit(
        ActionParams calldata actionParams,
        uint256 inputAmount
    ) internal returns (bool success, uint256 amountOut) {
        DepositParams memory params = abi.decode(
            actionParams.actionCallData,
            (DepositParams)
        );

        // Validate parameters
        if (params.subaccount == bytes32(0)) revert ZeroSubaccount();
        IVertexEndpoint endpoint = getEndpoint();

        uint256 balanceBefore = IERC20(actionParams.tokenOut).balanceOf(
            address(this)
        );

        uint256 approval = IERC20(actionParams.tokenOut).allowance(
            address(this),
            address(endpoint)
        );
        if (approval < inputAmount) {
            IERC20(actionParams.tokenOut).approve(
                address(endpoint),
                type(uint256).max
            );
        }

        // Deposit into subaccount with referral code
        try
            endpoint.depositCollateralWithReferral(
                params.subaccount,
                params.productId,
                uint128(inputAmount),
                params.referralCode
            )
        {
            success = true;
            emit VertexDepositCompleted(params.subaccount, inputAmount);
        } catch {
            revert DepositFailed();
        }

        uint256 balanceAfter = IERC20(actionParams.tokenOut).balanceOf(
            address(this)
        );
        if (balanceAfter > balanceBefore) {
            revert BalanceAfterDepositIncreased(balanceBefore, balanceAfter);
        }

        amountOut = balanceBefore - balanceAfter;

        return (success, amountOut);
    }

    /// @notice Converts an owner address and subaccount name to a Vertex subaccount ID
    /// @param subaccountOwner The owner address of the subaccount
    /// @param subaccountName The name of the subaccount (max 12 bytes)
    /// @return The subaccount ID as bytes32
    function createSubaccountId(
        address subaccountOwner,
        string memory subaccountName
    ) public pure returns (bytes32) {
        bytes memory subaccountOwnerBytes = abi.encodePacked(subaccountOwner);
        bytes memory subaccountNameBytes = bytes(subaccountName);

        require(
            subaccountOwnerBytes.length == 20,
            "Invalid owner address length"
        );
        require(
            subaccountNameBytes.length <= 12,
            "Subaccount name too long (max 12 bytes)"
        );

        bytes32 result;

        // Copy owner address (20 bytes) to the beginning of result
        assembly {
            // First copy the owner address (20 bytes) to the start of the result
            let ownerBytes := mload(add(subaccountOwnerBytes, 32)) // Load 32 bytes, starting with length
            // Shift right by (256 - 160) bits to get only the address bytes
            ownerBytes := shr(96, ownerBytes)
            // Place the owner address in the most significant bytes of the result
            result := shl(96, ownerBytes)
        }

        // Copy subaccount name bytes to the remaining part of the result
        for (uint256 i = 0; i < subaccountNameBytes.length; i++) {
            result = bytes32(
                uint256(result) |
                    (uint256(uint8(subaccountNameBytes[i])) << (8 * (19 - i)))
            );
        }

        return result;
    }
}

pragma solidity ^0.8.27;

import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {ActionParams} from "@fils/Actions/IRoutePreference.sol";

contract TransferUtils {
    error TransferFailed();
    error TransferInvalidToken();
    error TransferInvalidRecipient();

    struct TransferParams {
        address token;
        address recipient;
    }

    function decodeTransferParams(
        bytes memory data
    ) internal pure returns (TransferParams memory params) {
        (address token, address recipient) = abi.decode(
            data,
            (address, address)
        );

        if (token == address(0)) {
            revert TransferInvalidToken();
        }
        if (recipient == address(0)) {
            revert TransferInvalidRecipient();
        }
        return TransferParams({token: token, recipient: recipient});
    }

    function handleDirectTransfer(
        ActionParams calldata actionParams,
        uint256 inputAmount
    ) internal returns (bool success, uint256 actionPerformedWithAmount) {
        TransferParams memory params = decodeTransferParams(
            actionParams.actionCallData
        );

        success = IERC20(params.token).transfer(params.recipient, inputAmount);
        if (!success) revert TransferFailed();

        actionPerformedWithAmount = inputAmount;
    }
}

File 14 of 18 : IERC20.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC20.sol)

pragma solidity ^0.8.20;

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

File 15 of 18 : IERC165.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC165.sol)

pragma solidity ^0.8.20;

import {IERC165} from "../utils/introspection/IERC165.sol";

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.27;
// solhint-disable

/// @notice Interface for Vertex Endpoint contract.
/// @dev Handles collateral deposits with referral codes.
interface IVertexEndpoint {
    /// @notice Deposits collateral to a subaccount with a referral code.
    /// @param subaccount The subaccount to deposit to.
    /// @param productId The product ID to deposit for.
    /// @param amount The amount of collateral to deposit.
    /// @param referralCode The referral code to use.
    function depositCollateralWithReferral(
        bytes32 subaccount,
        uint32 productId,
        uint128 amount,
        string calldata referralCode
    )
        external;

    /// @notice Returns the clearinghouse address.
    /// @return clearinghouse The clearinghouse address.
    function clearinghouse() external view returns (address);

    /// @notice Returns the implementation address
    function implementation() external view returns (address);
}

interface IClearinghouse {
    /// @notice Retrieve quote ERC20 address
    function getQuote() external view returns (address);

    /// @notice Returns the registered engine address by type
    function getEngineByType(IProductEngine.EngineType engineType) external view returns (address);

    /// @notice Returns the engine associated with a product ID
    function getEngineByProduct(uint32 productId) external view returns (address);

    /// @notice Returns the implementation address
    function implementation() external view returns (address);
}

interface IProductEngine {
    enum EngineType {
        SPOT,
        PERP
    }
    /// @notice return productIds associated with engine
    //solhint-disable-next-line

    function getProductIds() external view returns (uint32[] memory);
}

interface ISpotEngine is IProductEngine {
    /// @notice Balance struct for the spot engine.
    struct Balance {
        int128 amount;
        int128 lastCumulativeMultiplierX18;
    }

    function getBalance(uint32 productId, bytes32 subaccount) external view returns (Balance memory);

    function getToken(uint32 productId) external view returns (address);
}

File 17 of 18 : IVertexRouter.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.27;

import {ActionParams} from "@fils/Actions/IRoutePreference.sol";
/// @notice Interface for Vertex-specific actions.
/// @dev Handles deposits into Vertex subaccounts.

interface IVertex {
    /// @notice Parameters for depositing into a Vertex subaccount.
    struct DepositParams {
        bytes32 subaccount;
        uint32 productId;
        uint128 amount;
        string referralCode;
    }

    /// @notice Thrown when the deposit amount is zero.
    error ZeroDepositAmount();

    /// @notice Thrown when the subaccount is zero.
    error ZeroSubaccount();

    /// @notice Thrown when the deposit fails.
    error DepositFailed();

    /// @notice Decodes the action calldata into DepositParams.
    function decodeDepositParams(
        bytes calldata actionCallData
    ) external pure returns (DepositParams memory);
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/introspection/IERC165.sol)

pragma solidity ^0.8.20;

/**
 * @dev Interface of the ERC-165 standard, as defined in the
 * https://eips.ethereum.org/EIPS/eip-165[ERC].
 *
 * Implementers can declare support of contract interfaces, which can then be
 * queried by others ({ERC165Checker}).
 *
 * For an implementation, see {ERC165}.
 */
interface IERC165 {
    /**
     * @dev Returns true if this contract implements the interface defined by
     * `interfaceId`. See the corresponding
     * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[ERC section]
     * to learn more about how these ids are created.
     *
     * This function call must use less than 30 000 gas.
     */
    function supportsInterface(bytes4 interfaceId) external view returns (bool);
}

Settings
{
  "remappings": [
    "@create3/=dependencies/zeframlou-create3-factory-567d6ec78cd0545f2fb18135dcb68298a5a1ef09/src/",
    "@fils-test/=test/",
    "@fils/=src/",
    "@openzeppelin/contracts/=dependencies/@openzeppelin-contracts-5.3.0/",
    "@uniswap/permit2/=dependencies/uniswap-permit2-unlocked-1.0.0/src/",
    "ds-test/=dependencies/uniswap-permit2-unlocked-1.0.0/lib/solmate/lib/ds-test/src/",
    "forge-gas-snapshot/=dependencies/uniswap-permit2-unlocked-1.0.0/lib/forge-gas-snapshot/src/",
    "forge-std/=dependencies/forge-std-1.9.7/src/",
    "openzeppelin-contracts/=dependencies/uniswap-permit2-unlocked-1.0.0/lib/openzeppelin-contracts/",
    "solmate/=dependencies/uniswap-permit2-unlocked-1.0.0/lib/solmate/src/",
    "@openzeppelin-contracts-5.3.0/=dependencies/@openzeppelin-contracts-5.3.0/",
    "forge-std-1.9.7/=dependencies/forge-std-1.9.7/src/",
    "zeframlou-create3-factory-567d6ec78cd0545f2fb18135dcb68298a5a1ef09/=dependencies/zeframlou-create3-factory-567d6ec78cd0545f2fb18135dcb68298a5a1ef09/src/"
  ],
  "optimizer": {
    "enabled": true,
    "runs": 10000
  },
  "metadata": {
    "useLiteralContent": false,
    "bytecodeHash": "ipfs",
    "appendCBOR": true
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "evmVersion": "cancun",
  "viaIR": true,
  "libraries": {}
}

Contract Security Audit

Contract ABI

API
[{"inputs":[{"internalType":"address[]","name":"operators","type":"address[]"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"uint256","name":"balanceBefore","type":"uint256"},{"internalType":"uint256","name":"balanceAfter","type":"uint256"}],"name":"BalanceAfterDepositIncreased","type":"error"},{"inputs":[{"internalType":"uint256","name":"balanceBefore","type":"uint256"},{"internalType":"uint256","name":"balanceAfter","type":"uint256"}],"name":"BalanceAfterSwapReduced","type":"error"},{"inputs":[],"name":"ContractPaused","type":"error"},{"inputs":[{"internalType":"address","name":"depositAddress","type":"address"}],"name":"DepositAddressAlreadyUsed","type":"error"},{"inputs":[],"name":"DepositFailed","type":"error"},{"inputs":[{"internalType":"uint256","name":"balance","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"InsufficientBalance","type":"error"},{"inputs":[{"internalType":"uint8","name":"invalidPreference","type":"uint8"}],"name":"InvalidRoutePreference","type":"error"},{"inputs":[],"name":"NotGuardian","type":"error"},{"inputs":[],"name":"NotOperator","type":"error"},{"inputs":[{"internalType":"address","name":"caller","type":"address"},{"internalType":"address","name":"guardian","type":"address"},{"internalType":"address[]","name":"operators","type":"address[]"}],"name":"NotOperatorOrGuardian","type":"error"},{"inputs":[],"name":"ReentrancyGuardReentrantCall","type":"error"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"SafeERC20FailedOperation","type":"error"},{"inputs":[{"internalType":"uint256","name":"minimum","type":"uint256"},{"internalType":"uint256","name":"actual","type":"uint256"}],"name":"SlippageExceeded","type":"error"},{"inputs":[],"name":"SwapFailed","type":"error"},{"inputs":[],"name":"TransferFailed","type":"error"},{"inputs":[],"name":"TransferInvalidRecipient","type":"error"},{"inputs":[],"name":"TransferInvalidToken","type":"error"},{"inputs":[{"internalType":"enum RoutePreference","name":"routePreference","type":"uint8"},{"internalType":"uint256","name":"chainId","type":"uint256"}],"name":"UnsupportedChain","type":"error"},{"inputs":[{"internalType":"address","name":"depositAsset","type":"address"},{"internalType":"address","name":"spender","type":"address"},{"internalType":"address","name":"endpoint","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"VertexApprovalFailed","type":"error"},{"inputs":[],"name":"ZeroDepositAmount","type":"error"},{"inputs":[],"name":"ZeroSubaccount","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint8","name":"routePreference","type":"uint8"},{"indexed":false,"internalType":"bool","name":"success","type":"bool"}],"name":"ActionExecuted","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"balance","type":"uint256"}],"name":"ERC20Rescued","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"balance","type":"uint256"}],"name":"NativeRescued","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"operator","type":"address"}],"name":"OperatorAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"operator","type":"address"}],"name":"OperatorInvalidated","type":"event"},{"anonymous":false,"inputs":[],"name":"Paused","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"fromToken","type":"address"},{"indexed":false,"internalType":"uint256","name":"fromAmount","type":"uint256"},{"indexed":true,"internalType":"address","name":"toToken","type":"address"},{"indexed":true,"internalType":"uint256","name":"toAmount","type":"uint256"}],"name":"SwapAndActionCompleted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"fromToken","type":"address"},{"indexed":false,"internalType":"uint256","name":"fromAmount","type":"uint256"},{"indexed":true,"internalType":"address","name":"toToken","type":"address"},{"indexed":false,"internalType":"uint256","name":"toAmount","type":"uint256"}],"name":"SwapCompleted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"fromToken","type":"address"},{"indexed":true,"internalType":"address","name":"toToken","type":"address"},{"indexed":false,"internalType":"uint256","name":"fromAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"toAmount","type":"uint256"}],"name":"TokenSwapped","type":"event"},{"anonymous":false,"inputs":[],"name":"Unpaused","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"subaccount","type":"bytes32"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"VertexDepositCompleted","type":"event"},{"inputs":[],"name":"ARBITRUM_CLEARINGHOUSE","outputs":[{"internalType":"contract IClearinghouse","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"ARBITRUM_ENDPOINT","outputs":[{"internalType":"contract IVertexEndpoint","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"BASE_CLEARINGHOUSE","outputs":[{"internalType":"contract IClearinghouse","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"BASE_ENDPOINT","outputs":[{"internalType":"contract IVertexEndpoint","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"GUARDIAN","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"OPERATORS","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"}],"name":"addOperator","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"subaccountOwner","type":"address"},{"internalType":"string","name":"subaccountName","type":"string"}],"name":"createSubaccountId","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes","name":"actionCallData","type":"bytes"}],"name":"decodeDepositParams","outputs":[{"components":[{"internalType":"bytes32","name":"subaccount","type":"bytes32"},{"internalType":"uint32","name":"productId","type":"uint32"},{"internalType":"uint128","name":"amount","type":"uint128"},{"internalType":"string","name":"referralCode","type":"string"}],"internalType":"struct IVertex.DepositParams","name":"params","type":"tuple"}],"stateMutability":"pure","type":"function"},{"inputs":[{"components":[{"internalType":"address[]","name":"tokens","type":"address[]"},{"internalType":"uint256[]","name":"amounts","type":"uint256[]"}],"internalType":"struct FunRouter.SweepParams","name":"params","type":"tuple"}],"name":"die","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"getEndpoint","outputs":[{"internalType":"contract IVertexEndpoint","name":"endpoint","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"}],"name":"invalidateOperator","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"isOperator","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isPaused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"tokenIn","type":"address"},{"internalType":"address","name":"tokenOut","type":"address"},{"internalType":"uint256","name":"fromAmount","type":"uint256"},{"internalType":"uint256","name":"minimumAmountOut","type":"uint256"},{"internalType":"uint256","name":"toAmount","type":"uint256"},{"internalType":"address","name":"swapTarget","type":"address"},{"internalType":"bytes","name":"swapCallData","type":"bytes"},{"internalType":"uint8","name":"routePreference","type":"uint8"},{"internalType":"bytes","name":"actionCallData","type":"bytes"},{"internalType":"address","name":"depositAddress","type":"address"}],"internalType":"struct ActionParams","name":"params","type":"tuple"}],"name":"swapAndAction","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"address[]","name":"tokens","type":"address[]"},{"internalType":"uint256[]","name":"amounts","type":"uint256[]"}],"internalType":"struct FunRouter.SweepParams","name":"params","type":"tuple"}],"name":"sweep","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"unpause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"usedDepositAddresses","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"}]

60a0604052346102125761275e8038038061001981610216565b928339810190602081830312610212578051906001600160401b038211610212570181601f82011215610212578051906001600160401b0382116101de578160051b926020610069818601610216565b8094815201916020839582010191821161021257602001915b8183106101f257505060015f5550732a187be85c3e6ecac4c1cdce40843bdd3476677d60805251906001600160401b0382116101de576801000000000000000082116101de57600154826001558083106101ad575b509060015f5260205f20915f5b828110610190575f5b600154811015610132575f51602061273e5f395f51905f528101546001600160a01b03165f908152600260205260409020805460ff19166001908117909155016100ed565b604051612502908161023c82396080518181816101dd0152818161041d0152818161059901528181610947015281816109c001528181610a4d01528181610d2501528181610f4601528181610ffa015281816119990152611a530152f35b81516001600160a01b0316818501556020909101906001016100e4565b60015f525f51602061273e5f395f51905f529081019083015b8181106101d357506100d7565b5f81556001016101c6565b634e487b7160e01b5f52604160045260245ffd5b82516001600160a01b038116810361021257815260209283019201610082565b5f80fd5b6040519190601f01601f191682016001600160401b038111838210176101de5760405256fe60806040526004361015610011575f80fd5b5f3560e01c80631fa921ed146101445780632977a0aa1461013f5780632d664e921461013a5780633f4ba83a1461013557806340d48e27146101305780634918a67c1461012b5780636342f733146101265780636d70f7ae14610121578063724c184c1461011c578063781676e3146101175780638456cb59146101125780639870d7fe1461010d578063a1e4140814610108578063add87ca914610103578063aed8e967146100fe578063b187bd26146100f9578063cd83131b146100f4578063e5429029146100ef5763ef60d240146100ea575f80fd5b611072565b610e72565b610d00565b610cde565b610ca4565b610c3d565b610b88565b610a19565b610999565b61096b565b61091b565b6108cd565b61087f565b610851565b610739565b610572565b6103e9565b6101b8565b610157565b5f91031261015357565b5f80fd5b34610153575f6003193601126101535760206040517392c2201d48481e2d42772da02485084a4407bbe28152f35b6020600319820112610153576004359067ffffffffffffffff821161015357600319826040920301126101535760040190565b34610153576101c636610185565b73ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001633036103a3575f5b61020f8280611405565b9050811015610386576102506102376102328361022c8680611405565b90611459565b611469565b73ffffffffffffffffffffffffffffffffffffffff1690565b61029a5780600191602084015f1961026c8361022c8489611405565b350361028357505061027d47611a3c565b01610205565b61027d9161022c6102949287611405565b35611a3c565b6102ab6102328261022c8580611405565b90602083015f196102c08361022c8488611405565b350361036b57506102de6102376102376102328461022c8880611405565b6040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015290602090829060249082905afa92831561036657600193610333925f91610338575b5090611996565b61027d565b610359915060203d811161035f575b6103518183610659565b810190611473565b5f61032c565b503d610347565b611482565b60019261037f8361022c6103339488611405565b3590611996565b61038f47611a3c565b6103a1600160ff196003541617600355565b005b7fef6d0f02000000000000000000000000000000000000000000000000000000005f5260045ffd5b73ffffffffffffffffffffffffffffffffffffffff81160361015357565b3461015357602060031936011261015357600435610406816103cb565b73ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001633036103a35773ffffffffffffffffffffffffffffffffffffffff8116805f52600260205261047360405f2060ff198154169055565b5f5b6001548082101561052e57906001918373ffffffffffffffffffffffffffffffffffffffff6104c66104a685610c20565b905473ffffffffffffffffffffffffffffffffffffffff9160031b1c1690565b16146104d4575b5001610475565b6104eb6104a66104e6610520936110cd565b610c20565b6104f483610c20565b90919073ffffffffffffffffffffffffffffffffffffffff8084549260031b9316831b921b1916179055565b6105286110fb565b5f6104cd565b60405173ffffffffffffffffffffffffffffffffffffffff851681527f3fe5f8a9d02b943fb3f60085bafc07550ad18efd9463b725668d7fbd0cf4d77090602090a1005b34610153575f6003193601126101535773ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001633036103a35760ff19600354166003557fa45f47fdea8a1efdd9029a5691c7f759c32b7c698632b563573e155625d169335f80a1005b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b6080810190811067ffffffffffffffff82111761063857604052565b6105ef565b6040810190811067ffffffffffffffff82111761063857604052565b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff82111761063857604052565b604051906106a9604083610659565b565b67ffffffffffffffff811161063857601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01660200190565b9291926106f1826106ab565b916106ff6040519384610659565b829481845281830111610153578281602093845f960137010152565b9080601f8301121561015357816020610736933591016106e5565b90565b3461015357604060031936011261015357600435610756816103cb565b60243567ffffffffffffffff8111610153576107977fffffffffffffffffffffffffffffffffffffffff00000000000000000000000091369060040161071b565b916107c1601460405184602082019460601b1684528181526107ba603482610659565b5114611193565b6107cf600c845111156111f8565b5116905f915b81518310156108415760019061082661082061081a6107f48787611283565b517fff000000000000000000000000000000000000000000000000000000000000001690565b60f81c90565b60ff1690565b610837610832866110e0565b611294565b1b179201916107d5565b60405190815280602081015b0390f35b34610153575f60031936011261015357602060405173ae1ec28d6225dce2ff787dcb8ce11cf6d3ae064f8152f35b346101535760206003193601126101535773ffffffffffffffffffffffffffffffffffffffff6004356108b1816103cb565b165f526004602052602060ff60405f2054166040519015158152f35b346101535760206003193601126101535773ffffffffffffffffffffffffffffffffffffffff6004356108ff816103cb565b165f526002602052602060ff60405f2054166040519015158152f35b34610153575f60031936011261015357602060405173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b34610153575f60031936011261015357602060405173bbee07b3e8121227afcfe1e2b82772246226128e8152f35b34610153575f6003193601126101535773ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001633036103a357600160ff1960035416176003557f9e87fac88ff661f02d44f95383c817fece4bce600a3dab7a54406878b965e7525f80a1005b3461015357602060031936011261015357600435610a36816103cb565b73ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001633036103a35773ffffffffffffffffffffffffffffffffffffffff811690815f52600260205260405f20600160ff19825416179055600154916801000000000000000083101561063857610aee6020926104f48560017fac6fa858e9350a46cec16539926e0fde25b7629f84b5a72bffaae4df888ae86d9701600155610c20565b604051908152a1005b907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f602080948051918291828752018686015e5f8582860101520116010190565b60a0606061073693602084528051602085015263ffffffff60208201511660408501526fffffffffffffffffffffffffffffffff604082015116828501520151916080808201520190610af7565b346101535760206003193601126101535760043567ffffffffffffffff811161015357366023820112156101535780600401359067ffffffffffffffff82116101535736602483830101116101535761084d916024610be79201611367565b60405191829182610b3a565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b600154811015610c385760015f5260205f2001905f90565b610bf3565b34610153576020600319360112610153576004356001548110156101535760015f527fb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf6015460405173ffffffffffffffffffffffffffffffffffffffff9091168152602090f35b34610153575f60031936011261015357610cbc611393565b60405173ffffffffffffffffffffffffffffffffffffffff9091168152602090f35b34610153575f60031936011261015357602060ff600354166040519015158152f35b3461015357610d0e36610185565b73ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001633036103a3575f5b610d578280611405565b9050811015610e6957610d746102376102328361022c8680611405565b610db85780600191602084015f19610d908361022c8489611405565b3503610da7575050610da147611a3c565b01610d4d565b610da19161022c6102949287611405565b610dc96102328261022c8580611405565b90602083015f19610dde8361022c8488611405565b3503610e555750610dfc6102376102376102328461022c8880611405565b6040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015290602090829060249082905afa92831561036657600193610e50925f91610338575090611996565b610da1565b60019261037f8361022c610e509488611405565b6103a147611a3c565b346101535760206003193601126101535760043567ffffffffffffffff81116101535761014060031982360301126101535760025f541461104a5760025f5560ff6003541661102257335f52600260205260ff60405f2054161580610fe2575b610efe57610ee561084d91600401611528565b610eee60015f55565b6040519081529081906020820190565b6040517f870f82a9000000000000000000000000000000000000000000000000000000008152806064810133600483015273ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016602483015260606044830152600154809152608482019060015f527fb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf6905f5b818110610fb3575050500390fd5b825473ffffffffffffffffffffffffffffffffffffffff16845285945060209093019260019283019201610fa5565b5073ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016331415610ed2565b7fab35696f000000000000000000000000000000000000000000000000000000005f5260045ffd5b7f3ee5aeb5000000000000000000000000000000000000000000000000000000005f5260045ffd5b34610153575f60031936011261015357602060405173e46cb729f92d287f6459bda6899434e22ecc48ae8152f35b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b905f1982019182116110db57565b6110a0565b60130390601382116110db57565b919082039182116110db57565b6001548015611166575f19810190600154821015610c385760015f8190527fb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf590910180547fffffffffffffffffffffffff000000000000000000000000000000000000000016905555565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603160045260245ffd5b1561119a57565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601c60248201527f496e76616c6964206f776e65722061646472657373206c656e677468000000006044820152fd5b156111ff57565b60846040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602760248201527f5375626163636f756e74206e616d6520746f6f206c6f6e6720286d617820313260448201527f20627974657329000000000000000000000000000000000000000000000000006064820152fd5b908151811015610c38570160200190565b908160031b917f1fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8116036110db57565b35906fffffffffffffffffffffffffffffffff8216820361015357565b6020818303126101535780359067ffffffffffffffff8211610153570160808183031261015357604051916113158361061c565b81358352602082013563ffffffff8116810361015357602084015261133c604083016112c4565b6040840152606082013567ffffffffffffffff81116101535761135f920161071b565b606082015290565b6107369160608060405161137a8161061c565b5f81525f60208201525f604082015201528101906112e1565b61210546146113ed5761a4b146146113d5577f32e18049000000000000000000000000000000000000000000000000000000005f52600480524660245260445ffd5b73bbee07b3e8121227afcfe1e2b82772246226128e90565b7392c2201d48481e2d42772da02485084a4407bbe290565b9035907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe181360301821215610153570180359067ffffffffffffffff821161015357602001918160051b3603831361015357565b9190811015610c385760051b0190565b35610736816103cb565b90816020910312610153575190565b6040513d5f823e3d90fd5b9035907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe181360301821215610153570180359067ffffffffffffffff82116101535760200191813603831361015357565b908092918237015f815290565b3d15611515573d906114fc826106ab565b9161150a6040519384610659565b82523d5f602084013e565b606090565b3560ff811681036101535790565b610120810161153681611469565b73ffffffffffffffffffffffffffffffffffffffff811661191e575b5061155f61023783611469565b6115726040840135809230903390611b09565b602083019061158661023761023784611469565b6040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015290602090829060249082905afa908115610366575f916118ff575b5060a085016115de61023782611469565b156118f7575f6116058192610232866115f68b611469565b6115ff84611469565b90611b8d565b8161161360c08a018a61148d565b9190611624604051809481936114de565b03925af16116306114eb565b50156118cf5761164561023761023785611469565b6040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015290602090829060249082905afa908115610366575f916118b0575b508181111561187f579061169f916110ee565b606085013580821061185157506116b585611469565b6116be84611469565b604080518581526020810185905273ffffffffffffffffffffffffffffffffffffffff92831693909216917f3b97ed181c0a31bfd0677f3ed4eb67037c9417cd149d69292eccb5f1a119884c9190a35b60808501359081811061181f5750907f035d9ce4fa97473437db8f448148d27700e603c28ac7c4f08821545308e1c42f73ffffffffffffffffffffffffffffffffffffffff6117bc6117646117b6969589611cae565b988997917fdddb34b076a86a5d22ac17e7620208aa7642567ec82ede47b982fde8d64be5086117ae60ff61179a60e0860161151a565b604051941515855216929081906020820190565b0390a2611469565b94611469565b6040519384528116931691602090a46117d761023782611469565b6117df575090565b6118126117ee61073692611469565b73ffffffffffffffffffffffffffffffffffffffff165f52600460205260405f2090565b600160ff19825416179055565b7f71c4efed000000000000000000000000000000000000000000000000000000005f5260049190915260245260445b5ffd5b7f71c4efed000000000000000000000000000000000000000000000000000000005f5260045260245260445ffd5b7f3a28ce36000000000000000000000000000000000000000000000000000000005f5260049190915260245260445ffd5b6118c9915060203d60201161035f576103518183610659565b5f61168c565b7f81ceff30000000000000000000000000000000000000000000000000000000005f5260045ffd5b50508061170e565b611918915060203d60201161035f576103518183610659565b5f6115cd565b6119506119498273ffffffffffffffffffffffffffffffffffffffff165f52600460205260405f2090565b5460ff1690565b15611552577f39c7d45a000000000000000000000000000000000000000000000000000000005f5273ffffffffffffffffffffffffffffffffffffffff1660045260245ffd5b907f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff811633036103a3576040517fa9059cbb00000000000000000000000000000000000000000000000000000000602082015273ffffffffffffffffffffffffffffffffffffffff91821660248201526044808201939093529182526106a992611a36606484610659565b16611e19565b73ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016908133036103a3574791818310611ad9575f8080938193828215611ad0575bf115610366574781039081116110db576040519081527fb7b2ca6341d2cdf4b130df74649713df61be072ee5cf00593fc35c8b9d9b1cc090602090a1565b506108fc611a92565b50907fcf479181000000000000000000000000000000000000000000000000000000005f5260045260245260445ffd5b90919273ffffffffffffffffffffffffffffffffffffffff6106a99481604051957f23b872dd000000000000000000000000000000000000000000000000000000006020880152166024860152166044840152606483015260648252611b70608483610659565b611e19565b90816020910312610153575180151581036101535790565b6040517fdd62ed3e00000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff8381166024830152919091169290602081604481875afa80156103665782915f91611c8f575b5010611bff57505050565b6040517f095ea7b300000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff929092166004830152602482015290602090829060449082905f905af1801561036657611c645750565b611c859060203d602011611c88575b611c7d8183610659565b810190611b75565b50565b503d611c73565b611ca8915060203d60201161035f576103518183610659565b5f611bf4565b919060e0830192600460ff611cc28661151a565b1603611d1357611cd8611ce0939260ff92612006565b93909461151a565b60405185151581529116907fdddb34b076a86a5d22ac17e7620208aa7642567ec82ede47b982fde8d64be50890602090a2565b600160ff611d208661151a565b16148015611e04575b8015611def575b8015611dda575b8015611dc5575b8015611db0575b8015611d9b575b15611d6157611cd8611ce0939260ff92611ea0565b61184e611d6d8561151a565b7ff054a718000000000000000000000000000000000000000000000000000000005f5260ff16600452602490565b50600960ff611da98661151a565b1614611d4c565b50600860ff611dbe8661151a565b1614611d45565b50600760ff611dd38661151a565b1614611d3e565b50600560ff611de88661151a565b1614611d37565b50600260ff611dfd8661151a565b1614611d30565b50600360ff611e128661151a565b1614611d29565b905f602091828151910182855af115611482575f513d611e97575073ffffffffffffffffffffffffffffffffffffffff81163b155b611e555750565b73ffffffffffffffffffffffffffffffffffffffff907f5274afe7000000000000000000000000000000000000000000000000000000005f521660045260245ffd5b60011415611e4e565b90602081611ec9611ec4611ebd611f66979661010081019061148d565b36916106e5565b6123d1565b611f0d83611ef1610237610237855173ffffffffffffffffffffffffffffffffffffffff1690565b92015173ffffffffffffffffffffffffffffffffffffffff1690565b5f6040518098819582947fa9059cbb000000000000000000000000000000000000000000000000000000008452600484016020909392919373ffffffffffffffffffffffffffffffffffffffff60408201951681520152565b03925af1928315610366575f93611faa575b508215611f825790565b7f90b8ec18000000000000000000000000000000000000000000000000000000005f5260045ffd5b611fc491935060203d602011611c8857611c7d8183610659565b915f611f78565b90610736949363ffffffff6080946fffffffffffffffffffffffffffffffff9385521660208401521660408201528160608201520190610af7565b91909161202261201a61010083018361148d565b8101906112e1565b928351156123a9576020612034611393565b920161204561023761023783611469565b6040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201529390602090859060249082905afa938415610366575f94612388575b5073ffffffffffffffffffffffffffffffffffffffff6120b261023761023785611469565b6040517fdd62ed3e0000000000000000000000000000000000000000000000000000000081523060048201529290911673ffffffffffffffffffffffffffffffffffffffff811660248401529190602090829060449082905afa80156103665784915f91612369575b50106122d2575b855190612136602088015163ffffffff1690565b90606088015190803b1561015357612195935f8094604051968795869485937f221f09390000000000000000000000000000000000000000000000000000000085526fffffffffffffffffffffffffffffffff8d169160048601611fcb565b03925af190816122b8575b506121cd577f79cacff1000000000000000000000000000000000000000000000000000000005f5260045ffd5b935160405191825260019461220f92610237928392917fcfb57889700bf521339043964081fb27ee7bb71da6a7f90d86a83eb86685aa229080602081016117ae565b6040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201529190602090839060249082905afa918215610366575f92612297575b508082116122695790610736916110ee565b7fd08e57b5000000000000000000000000000000000000000000000000000000005f5260045260245260445ffd5b6122b191925060203d60201161035f576103518183610659565b905f612257565b806122c65f6122cc93610659565b80610149565b5f6121a0565b6122e161023761023784611469565b6040517f095ea7b300000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff831660048201525f19602482015290602090829060449082905f905af180156103665761234a575b50612122565b6123629060203d602011611c8857611c7d8183610659565b505f612344565b612382915060203d60201161035f576103518183610659565b5f61211b565b6123a291945060203d60201161035f576103518183610659565b925f61208d565b7fa46cc156000000000000000000000000000000000000000000000000000000005f5260045ffd5b5f60206040516123e08161063d565b82815201526040818051810103126101535773ffffffffffffffffffffffffffffffffffffffff806040602084015193612419856103cb565b015192612425846103cb565b16911681156124a457801561247c576107369061245f61244361069a565b73ffffffffffffffffffffffffffffffffffffffff9094168452565b73ffffffffffffffffffffffffffffffffffffffff166020830152565b7fe6ca93b2000000000000000000000000000000000000000000000000000000005f5260045ffd5b7f3428f488000000000000000000000000000000000000000000000000000000005f5260045ffdfea2646970667358221220b6de4a455d1420cf03b8b9fab89a201db57ffe12fb4f164718a37d00d03d764f64736f6c634300081b0033b10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf600000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000002000000000000000000000000f0fc40a635a907ac93924f2d8c36b3e87afef52e00000000000000000000000002eb979b7231dbccc892c7bdd398c6f4adaf63e2

Deployed Bytecode

0x60806040526004361015610011575f80fd5b5f3560e01c80631fa921ed146101445780632977a0aa1461013f5780632d664e921461013a5780633f4ba83a1461013557806340d48e27146101305780634918a67c1461012b5780636342f733146101265780636d70f7ae14610121578063724c184c1461011c578063781676e3146101175780638456cb59146101125780639870d7fe1461010d578063a1e4140814610108578063add87ca914610103578063aed8e967146100fe578063b187bd26146100f9578063cd83131b146100f4578063e5429029146100ef5763ef60d240146100ea575f80fd5b611072565b610e72565b610d00565b610cde565b610ca4565b610c3d565b610b88565b610a19565b610999565b61096b565b61091b565b6108cd565b61087f565b610851565b610739565b610572565b6103e9565b6101b8565b610157565b5f91031261015357565b5f80fd5b34610153575f6003193601126101535760206040517392c2201d48481e2d42772da02485084a4407bbe28152f35b6020600319820112610153576004359067ffffffffffffffff821161015357600319826040920301126101535760040190565b34610153576101c636610185565b73ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000002a187be85c3e6ecac4c1cdce40843bdd3476677d1633036103a3575f5b61020f8280611405565b9050811015610386576102506102376102328361022c8680611405565b90611459565b611469565b73ffffffffffffffffffffffffffffffffffffffff1690565b61029a5780600191602084015f1961026c8361022c8489611405565b350361028357505061027d47611a3c565b01610205565b61027d9161022c6102949287611405565b35611a3c565b6102ab6102328261022c8580611405565b90602083015f196102c08361022c8488611405565b350361036b57506102de6102376102376102328461022c8880611405565b6040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015290602090829060249082905afa92831561036657600193610333925f91610338575b5090611996565b61027d565b610359915060203d811161035f575b6103518183610659565b810190611473565b5f61032c565b503d610347565b611482565b60019261037f8361022c6103339488611405565b3590611996565b61038f47611a3c565b6103a1600160ff196003541617600355565b005b7fef6d0f02000000000000000000000000000000000000000000000000000000005f5260045ffd5b73ffffffffffffffffffffffffffffffffffffffff81160361015357565b3461015357602060031936011261015357600435610406816103cb565b73ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000002a187be85c3e6ecac4c1cdce40843bdd3476677d1633036103a35773ffffffffffffffffffffffffffffffffffffffff8116805f52600260205261047360405f2060ff198154169055565b5f5b6001548082101561052e57906001918373ffffffffffffffffffffffffffffffffffffffff6104c66104a685610c20565b905473ffffffffffffffffffffffffffffffffffffffff9160031b1c1690565b16146104d4575b5001610475565b6104eb6104a66104e6610520936110cd565b610c20565b6104f483610c20565b90919073ffffffffffffffffffffffffffffffffffffffff8084549260031b9316831b921b1916179055565b6105286110fb565b5f6104cd565b60405173ffffffffffffffffffffffffffffffffffffffff851681527f3fe5f8a9d02b943fb3f60085bafc07550ad18efd9463b725668d7fbd0cf4d77090602090a1005b34610153575f6003193601126101535773ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000002a187be85c3e6ecac4c1cdce40843bdd3476677d1633036103a35760ff19600354166003557fa45f47fdea8a1efdd9029a5691c7f759c32b7c698632b563573e155625d169335f80a1005b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b6080810190811067ffffffffffffffff82111761063857604052565b6105ef565b6040810190811067ffffffffffffffff82111761063857604052565b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff82111761063857604052565b604051906106a9604083610659565b565b67ffffffffffffffff811161063857601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01660200190565b9291926106f1826106ab565b916106ff6040519384610659565b829481845281830111610153578281602093845f960137010152565b9080601f8301121561015357816020610736933591016106e5565b90565b3461015357604060031936011261015357600435610756816103cb565b60243567ffffffffffffffff8111610153576107977fffffffffffffffffffffffffffffffffffffffff00000000000000000000000091369060040161071b565b916107c1601460405184602082019460601b1684528181526107ba603482610659565b5114611193565b6107cf600c845111156111f8565b5116905f915b81518310156108415760019061082661082061081a6107f48787611283565b517fff000000000000000000000000000000000000000000000000000000000000001690565b60f81c90565b60ff1690565b610837610832866110e0565b611294565b1b179201916107d5565b60405190815280602081015b0390f35b34610153575f60031936011261015357602060405173ae1ec28d6225dce2ff787dcb8ce11cf6d3ae064f8152f35b346101535760206003193601126101535773ffffffffffffffffffffffffffffffffffffffff6004356108b1816103cb565b165f526004602052602060ff60405f2054166040519015158152f35b346101535760206003193601126101535773ffffffffffffffffffffffffffffffffffffffff6004356108ff816103cb565b165f526002602052602060ff60405f2054166040519015158152f35b34610153575f60031936011261015357602060405173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000002a187be85c3e6ecac4c1cdce40843bdd3476677d168152f35b34610153575f60031936011261015357602060405173bbee07b3e8121227afcfe1e2b82772246226128e8152f35b34610153575f6003193601126101535773ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000002a187be85c3e6ecac4c1cdce40843bdd3476677d1633036103a357600160ff1960035416176003557f9e87fac88ff661f02d44f95383c817fece4bce600a3dab7a54406878b965e7525f80a1005b3461015357602060031936011261015357600435610a36816103cb565b73ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000002a187be85c3e6ecac4c1cdce40843bdd3476677d1633036103a35773ffffffffffffffffffffffffffffffffffffffff811690815f52600260205260405f20600160ff19825416179055600154916801000000000000000083101561063857610aee6020926104f48560017fac6fa858e9350a46cec16539926e0fde25b7629f84b5a72bffaae4df888ae86d9701600155610c20565b604051908152a1005b907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f602080948051918291828752018686015e5f8582860101520116010190565b60a0606061073693602084528051602085015263ffffffff60208201511660408501526fffffffffffffffffffffffffffffffff604082015116828501520151916080808201520190610af7565b346101535760206003193601126101535760043567ffffffffffffffff811161015357366023820112156101535780600401359067ffffffffffffffff82116101535736602483830101116101535761084d916024610be79201611367565b60405191829182610b3a565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b600154811015610c385760015f5260205f2001905f90565b610bf3565b34610153576020600319360112610153576004356001548110156101535760015f527fb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf6015460405173ffffffffffffffffffffffffffffffffffffffff9091168152602090f35b34610153575f60031936011261015357610cbc611393565b60405173ffffffffffffffffffffffffffffffffffffffff9091168152602090f35b34610153575f60031936011261015357602060ff600354166040519015158152f35b3461015357610d0e36610185565b73ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000002a187be85c3e6ecac4c1cdce40843bdd3476677d1633036103a3575f5b610d578280611405565b9050811015610e6957610d746102376102328361022c8680611405565b610db85780600191602084015f19610d908361022c8489611405565b3503610da7575050610da147611a3c565b01610d4d565b610da19161022c6102949287611405565b610dc96102328261022c8580611405565b90602083015f19610dde8361022c8488611405565b3503610e555750610dfc6102376102376102328461022c8880611405565b6040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015290602090829060249082905afa92831561036657600193610e50925f91610338575090611996565b610da1565b60019261037f8361022c610e509488611405565b6103a147611a3c565b346101535760206003193601126101535760043567ffffffffffffffff81116101535761014060031982360301126101535760025f541461104a5760025f5560ff6003541661102257335f52600260205260ff60405f2054161580610fe2575b610efe57610ee561084d91600401611528565b610eee60015f55565b6040519081529081906020820190565b6040517f870f82a9000000000000000000000000000000000000000000000000000000008152806064810133600483015273ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000002a187be85c3e6ecac4c1cdce40843bdd3476677d16602483015260606044830152600154809152608482019060015f527fb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf6905f5b818110610fb3575050500390fd5b825473ffffffffffffffffffffffffffffffffffffffff16845285945060209093019260019283019201610fa5565b5073ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000002a187be85c3e6ecac4c1cdce40843bdd3476677d16331415610ed2565b7fab35696f000000000000000000000000000000000000000000000000000000005f5260045ffd5b7f3ee5aeb5000000000000000000000000000000000000000000000000000000005f5260045ffd5b34610153575f60031936011261015357602060405173e46cb729f92d287f6459bda6899434e22ecc48ae8152f35b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b905f1982019182116110db57565b6110a0565b60130390601382116110db57565b919082039182116110db57565b6001548015611166575f19810190600154821015610c385760015f8190527fb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf590910180547fffffffffffffffffffffffff000000000000000000000000000000000000000016905555565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603160045260245ffd5b1561119a57565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601c60248201527f496e76616c6964206f776e65722061646472657373206c656e677468000000006044820152fd5b156111ff57565b60846040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602760248201527f5375626163636f756e74206e616d6520746f6f206c6f6e6720286d617820313260448201527f20627974657329000000000000000000000000000000000000000000000000006064820152fd5b908151811015610c38570160200190565b908160031b917f1fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8116036110db57565b35906fffffffffffffffffffffffffffffffff8216820361015357565b6020818303126101535780359067ffffffffffffffff8211610153570160808183031261015357604051916113158361061c565b81358352602082013563ffffffff8116810361015357602084015261133c604083016112c4565b6040840152606082013567ffffffffffffffff81116101535761135f920161071b565b606082015290565b6107369160608060405161137a8161061c565b5f81525f60208201525f604082015201528101906112e1565b61210546146113ed5761a4b146146113d5577f32e18049000000000000000000000000000000000000000000000000000000005f52600480524660245260445ffd5b73bbee07b3e8121227afcfe1e2b82772246226128e90565b7392c2201d48481e2d42772da02485084a4407bbe290565b9035907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe181360301821215610153570180359067ffffffffffffffff821161015357602001918160051b3603831361015357565b9190811015610c385760051b0190565b35610736816103cb565b90816020910312610153575190565b6040513d5f823e3d90fd5b9035907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe181360301821215610153570180359067ffffffffffffffff82116101535760200191813603831361015357565b908092918237015f815290565b3d15611515573d906114fc826106ab565b9161150a6040519384610659565b82523d5f602084013e565b606090565b3560ff811681036101535790565b610120810161153681611469565b73ffffffffffffffffffffffffffffffffffffffff811661191e575b5061155f61023783611469565b6115726040840135809230903390611b09565b602083019061158661023761023784611469565b6040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015290602090829060249082905afa908115610366575f916118ff575b5060a085016115de61023782611469565b156118f7575f6116058192610232866115f68b611469565b6115ff84611469565b90611b8d565b8161161360c08a018a61148d565b9190611624604051809481936114de565b03925af16116306114eb565b50156118cf5761164561023761023785611469565b6040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015290602090829060249082905afa908115610366575f916118b0575b508181111561187f579061169f916110ee565b606085013580821061185157506116b585611469565b6116be84611469565b604080518581526020810185905273ffffffffffffffffffffffffffffffffffffffff92831693909216917f3b97ed181c0a31bfd0677f3ed4eb67037c9417cd149d69292eccb5f1a119884c9190a35b60808501359081811061181f5750907f035d9ce4fa97473437db8f448148d27700e603c28ac7c4f08821545308e1c42f73ffffffffffffffffffffffffffffffffffffffff6117bc6117646117b6969589611cae565b988997917fdddb34b076a86a5d22ac17e7620208aa7642567ec82ede47b982fde8d64be5086117ae60ff61179a60e0860161151a565b604051941515855216929081906020820190565b0390a2611469565b94611469565b6040519384528116931691602090a46117d761023782611469565b6117df575090565b6118126117ee61073692611469565b73ffffffffffffffffffffffffffffffffffffffff165f52600460205260405f2090565b600160ff19825416179055565b7f71c4efed000000000000000000000000000000000000000000000000000000005f5260049190915260245260445b5ffd5b7f71c4efed000000000000000000000000000000000000000000000000000000005f5260045260245260445ffd5b7f3a28ce36000000000000000000000000000000000000000000000000000000005f5260049190915260245260445ffd5b6118c9915060203d60201161035f576103518183610659565b5f61168c565b7f81ceff30000000000000000000000000000000000000000000000000000000005f5260045ffd5b50508061170e565b611918915060203d60201161035f576103518183610659565b5f6115cd565b6119506119498273ffffffffffffffffffffffffffffffffffffffff165f52600460205260405f2090565b5460ff1690565b15611552577f39c7d45a000000000000000000000000000000000000000000000000000000005f5273ffffffffffffffffffffffffffffffffffffffff1660045260245ffd5b907f0000000000000000000000002a187be85c3e6ecac4c1cdce40843bdd3476677d73ffffffffffffffffffffffffffffffffffffffff811633036103a3576040517fa9059cbb00000000000000000000000000000000000000000000000000000000602082015273ffffffffffffffffffffffffffffffffffffffff91821660248201526044808201939093529182526106a992611a36606484610659565b16611e19565b73ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000002a187be85c3e6ecac4c1cdce40843bdd3476677d16908133036103a3574791818310611ad9575f8080938193828215611ad0575bf115610366574781039081116110db576040519081527fb7b2ca6341d2cdf4b130df74649713df61be072ee5cf00593fc35c8b9d9b1cc090602090a1565b506108fc611a92565b50907fcf479181000000000000000000000000000000000000000000000000000000005f5260045260245260445ffd5b90919273ffffffffffffffffffffffffffffffffffffffff6106a99481604051957f23b872dd000000000000000000000000000000000000000000000000000000006020880152166024860152166044840152606483015260648252611b70608483610659565b611e19565b90816020910312610153575180151581036101535790565b6040517fdd62ed3e00000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff8381166024830152919091169290602081604481875afa80156103665782915f91611c8f575b5010611bff57505050565b6040517f095ea7b300000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff929092166004830152602482015290602090829060449082905f905af1801561036657611c645750565b611c859060203d602011611c88575b611c7d8183610659565b810190611b75565b50565b503d611c73565b611ca8915060203d60201161035f576103518183610659565b5f611bf4565b919060e0830192600460ff611cc28661151a565b1603611d1357611cd8611ce0939260ff92612006565b93909461151a565b60405185151581529116907fdddb34b076a86a5d22ac17e7620208aa7642567ec82ede47b982fde8d64be50890602090a2565b600160ff611d208661151a565b16148015611e04575b8015611def575b8015611dda575b8015611dc5575b8015611db0575b8015611d9b575b15611d6157611cd8611ce0939260ff92611ea0565b61184e611d6d8561151a565b7ff054a718000000000000000000000000000000000000000000000000000000005f5260ff16600452602490565b50600960ff611da98661151a565b1614611d4c565b50600860ff611dbe8661151a565b1614611d45565b50600760ff611dd38661151a565b1614611d3e565b50600560ff611de88661151a565b1614611d37565b50600260ff611dfd8661151a565b1614611d30565b50600360ff611e128661151a565b1614611d29565b905f602091828151910182855af115611482575f513d611e97575073ffffffffffffffffffffffffffffffffffffffff81163b155b611e555750565b73ffffffffffffffffffffffffffffffffffffffff907f5274afe7000000000000000000000000000000000000000000000000000000005f521660045260245ffd5b60011415611e4e565b90602081611ec9611ec4611ebd611f66979661010081019061148d565b36916106e5565b6123d1565b611f0d83611ef1610237610237855173ffffffffffffffffffffffffffffffffffffffff1690565b92015173ffffffffffffffffffffffffffffffffffffffff1690565b5f6040518098819582947fa9059cbb000000000000000000000000000000000000000000000000000000008452600484016020909392919373ffffffffffffffffffffffffffffffffffffffff60408201951681520152565b03925af1928315610366575f93611faa575b508215611f825790565b7f90b8ec18000000000000000000000000000000000000000000000000000000005f5260045ffd5b611fc491935060203d602011611c8857611c7d8183610659565b915f611f78565b90610736949363ffffffff6080946fffffffffffffffffffffffffffffffff9385521660208401521660408201528160608201520190610af7565b91909161202261201a61010083018361148d565b8101906112e1565b928351156123a9576020612034611393565b920161204561023761023783611469565b6040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201529390602090859060249082905afa938415610366575f94612388575b5073ffffffffffffffffffffffffffffffffffffffff6120b261023761023785611469565b6040517fdd62ed3e0000000000000000000000000000000000000000000000000000000081523060048201529290911673ffffffffffffffffffffffffffffffffffffffff811660248401529190602090829060449082905afa80156103665784915f91612369575b50106122d2575b855190612136602088015163ffffffff1690565b90606088015190803b1561015357612195935f8094604051968795869485937f221f09390000000000000000000000000000000000000000000000000000000085526fffffffffffffffffffffffffffffffff8d169160048601611fcb565b03925af190816122b8575b506121cd577f79cacff1000000000000000000000000000000000000000000000000000000005f5260045ffd5b935160405191825260019461220f92610237928392917fcfb57889700bf521339043964081fb27ee7bb71da6a7f90d86a83eb86685aa229080602081016117ae565b6040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201529190602090839060249082905afa918215610366575f92612297575b508082116122695790610736916110ee565b7fd08e57b5000000000000000000000000000000000000000000000000000000005f5260045260245260445ffd5b6122b191925060203d60201161035f576103518183610659565b905f612257565b806122c65f6122cc93610659565b80610149565b5f6121a0565b6122e161023761023784611469565b6040517f095ea7b300000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff831660048201525f19602482015290602090829060449082905f905af180156103665761234a575b50612122565b6123629060203d602011611c8857611c7d8183610659565b505f612344565b612382915060203d60201161035f576103518183610659565b5f61211b565b6123a291945060203d60201161035f576103518183610659565b925f61208d565b7fa46cc156000000000000000000000000000000000000000000000000000000005f5260045ffd5b5f60206040516123e08161063d565b82815201526040818051810103126101535773ffffffffffffffffffffffffffffffffffffffff806040602084015193612419856103cb565b015192612425846103cb565b16911681156124a457801561247c576107369061245f61244361069a565b73ffffffffffffffffffffffffffffffffffffffff9094168452565b73ffffffffffffffffffffffffffffffffffffffff166020830152565b7fe6ca93b2000000000000000000000000000000000000000000000000000000005f5260045ffd5b7f3428f488000000000000000000000000000000000000000000000000000000005f5260045ffdfea2646970667358221220b6de4a455d1420cf03b8b9fab89a201db57ffe12fb4f164718a37d00d03d764f64736f6c634300081b0033

Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)

00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000002000000000000000000000000f0fc40a635a907ac93924f2d8c36b3e87afef52e00000000000000000000000002eb979b7231dbccc892c7bdd398c6f4adaf63e2

-----Decoded View---------------
Arg [0] : operators (address[]): 0xf0fc40A635A907ac93924f2d8C36b3e87AFef52E,0x02eb979B7231DbCCC892C7bDd398C6f4AdAf63e2

-----Encoded View---------------
4 Constructor Arguments found :
Arg [0] : 0000000000000000000000000000000000000000000000000000000000000020
Arg [1] : 0000000000000000000000000000000000000000000000000000000000000002
Arg [2] : 000000000000000000000000f0fc40a635a907ac93924f2d8c36b3e87afef52e
Arg [3] : 00000000000000000000000002eb979b7231dbccc892c7bdd398c6f4adaf63e2


Block Uncle Number Difficulty Gas Used Reward
View All Uncles
Loading...
Loading
Loading...
Loading
Loading...
Loading
[ Download: CSV Export  ]

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.