Source Code
Overview
ETH Balance
0 ETH
Eth Value
$0.00| Transaction Hash |
Method
|
Block
|
From
|
|
To
|
||||
|---|---|---|---|---|---|---|---|---|---|
Latest 1 internal transaction
Advanced mode:
| Parent Transaction Hash | Method | Block |
From
|
|
To
|
||
|---|---|---|---|---|---|---|---|
| 0x60a06040 | 22621979 | 266 days ago | Contract Creation | 0 ETH |
Loading...
Loading
Loading...
Loading
Cross-Chain Transactions
Loading...
Loading
Contract Name:
FunRouter
Compiler Version
v0.8.27+commit.40a35a09
Optimization Enabled:
Yes with 10000 runs
Other Settings:
cancun EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// 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;
}
}// 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);
}
}// 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;
}
}// 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";// 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);
}// 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);
}{
"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
- No Contract Security Audit Submitted- Submit Audit Here
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"}]Contract Creation Code
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
Loading...
Loading
Loading...
Loading
Net Worth in USD
$0.00
Net Worth in ETH
0
Multichain Portfolio | 34 Chains
| Chain | Token | Portfolio % | Price | Amount | Value |
|---|
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.