ETH Price: $2,970.10 (-2.24%)
 

Overview

ETH Balance

0 ETH

Eth Value

$0.00

More Info

Private Name Tags

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To

There are no matching entries

1 Internal Transaction found.

Latest 1 internal transaction

Advanced mode:
Parent Transaction Hash Method Block
From
To
0x60e06040226362582025-06-05 5:12:3539 days ago1749100355  Contract Creation0 ETH

Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

Validator Index Block Amount
View All Withdrawals

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

Contract Source Code Verified (Exact Match)

Contract Name:
RefuelERC20Action

Compiler Version
v0.8.26+commit.8a97fa7a

Optimization Enabled:
Yes with 1000 runs

Other Settings:
cancun EvmVersion
// SPDX-License-Identifier: MIT OR Apache-2.0
pragma solidity ^0.8.26;

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

import {InstructionLib} from "../libraries/Instruction.sol";

import {OtimFee} from "./fee-models/OtimFee.sol";

import {IAction} from "./interfaces/IAction.sol";
import {IRefuelERC20Action, INSTRUCTION_TYPEHASH, ARGUMENTS_TYPEHASH} from "./interfaces/IRefuelERC20Action.sol";

import {InvalidArguments, InsufficientBalance, BalanceOverThreshold} from "./errors/Errors.sol";

/// @title RefuelERC20
/// @author Otim Labs, Inc.
/// @notice an Action that refuels a target address with an ERC20 token when the target's balance is below a threshold
contract RefuelERC20Action is IAction, IRefuelERC20Action, OtimFee {
    using SafeERC20 for IERC20;

    constructor(address feeTokenRegistryAddress, address treasuryAddress, uint256 gasConstant_)
        OtimFee(feeTokenRegistryAddress, treasuryAddress, gasConstant_)
    {}

    /// @inheritdoc IAction
    function argumentsHash(bytes calldata arguments) public pure returns (bytes32, bytes32) {
        return (INSTRUCTION_TYPEHASH, hash(abi.decode(arguments, (RefuelERC20))));
    }

    /// @inheritdoc IRefuelERC20Action
    function hash(RefuelERC20 memory refuelERC20) public pure returns (bytes32) {
        return keccak256(
            abi.encode(
                ARGUMENTS_TYPEHASH,
                refuelERC20.token,
                refuelERC20.target,
                refuelERC20.threshold,
                refuelERC20.endBalance,
                hash(refuelERC20.fee)
            )
        );
    }

    /// @inheritdoc IAction
    function execute(
        InstructionLib.Instruction calldata instruction,
        InstructionLib.Signature calldata,
        InstructionLib.ExecutionState calldata executionState
    ) external override returns (bool) {
        // initial gas measurement for fee calculation
        uint256 startGas = gasleft();

        // decode the arguments from the instruction
        RefuelERC20 memory refuelERC20 = abi.decode(instruction.arguments, (RefuelERC20));

        // if first execution, validate the input
        if (executionState.executionCount == 0) {
            // validate the arguments
            if (
                refuelERC20.token == address(0) || refuelERC20.target == address(0)
                    || refuelERC20.threshold >= refuelERC20.endBalance
            ) {
                revert InvalidArguments();
            }
        }

        IERC20 refuelToken = IERC20(refuelERC20.token);

        // get the target's ERC20 balance
        uint256 balance = refuelToken.balanceOf(refuelERC20.target);

        // if the balance is above the threshold, revert
        if (balance > refuelERC20.threshold) {
            revert BalanceOverThreshold();
        }

        // calculate the amount to refuel
        uint256 refuelAmount = refuelERC20.endBalance - balance;

        // check if the account has enough balance to refuel
        if (refuelToken.balanceOf(address(this)) < refuelAmount) {
            revert InsufficientBalance();
        }

        // transfer the refuel amount to the target
        refuelToken.safeTransfer(refuelERC20.target, refuelAmount);

        // charge the fee
        chargeFee(startGas - gasleft(), refuelERC20.fee);

        // this action has no auto-deactivation cases
        return false;
    }
}

// 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 OR Apache-2.0
pragma solidity ^0.8.26;

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

/// @title InstructionLib
/// @author Otim Labs, Inc.
/// @notice a library defining the Instruction datatype and util functions
library InstructionLib {
    /// @notice defines a signature
    struct Signature {
        uint8 v;
        bytes32 r;
        bytes32 s;
    }

    /// @notice defines the ExecutionState datatype
    /// @param deactivated - whether the Instruction has been deactivated
    /// @param executionCount - the number of times the Instruction has been executed
    /// @param lastExecuted - the unix timestamp of the last time the Instruction was executed
    struct ExecutionState {
        bool deactivated;
        uint120 executionCount;
        uint120 lastExecuted;
    }

    /// @notice defines the Instruction datatype
    /// @param salt - a number to ensure the uniqueness of the Instruction
    /// @param maxExecutions - the maximum number of times the Instruction can be executed
    /// @param action - the address of the Action contract to be executed
    /// @param arguments - the arguments to be passed to the Action contract
    struct Instruction {
        uint256 salt;
        uint256 maxExecutions;
        address action;
        bytes arguments;
    }

    /// @notice abi.encodes and hashes an Instruction struct to create a unique Instruction identifier
    /// @param instruction - an Instruction struct to hash
    /// @return instructionId - unique identifier for the Instruction
    function id(Instruction calldata instruction) internal pure returns (bytes32) {
        return keccak256(abi.encode(instruction));
    }

    /// @notice calculates the EIP-712 hash for activating an Instruction
    /// @param instruction - an Instruction struct to hash
    /// @param domainSeparator - the EIP-712 domain separator for the verifying contract
    /// @return hash - EIP-712 hash for activating `instruction`
    function signingHash(
        Instruction calldata instruction,
        bytes32 domainSeparator,
        bytes32 instructionTypeHash,
        bytes32 argumentsHash
    ) internal pure returns (bytes32) {
        return keccak256(
            abi.encodePacked(
                Constants.EIP712_PREFIX,
                domainSeparator,
                keccak256(
                    abi.encode(
                        instructionTypeHash,
                        instruction.salt,
                        instruction.maxExecutions,
                        instruction.action,
                        argumentsHash
                    )
                )
            )
        );
    }

    /// @notice defines a deactivation instruction
    /// @param instructionId - the unique identifier of the Instruction to deactivate
    struct InstructionDeactivation {
        bytes32 instructionId;
    }

    /// @notice the EIP-712 type-hash for an InstructionDeactivation
    bytes32 public constant DEACTIVATION_TYPEHASH = keccak256("InstructionDeactivation(bytes32 instructionId)");

    /// @notice calculates the EIP-712 hash for a InstructionDeactivation
    /// @param deactivation - an InstructionDeactivation struct to hash
    /// @param domainSeparator - the EIP-712 domain separator for the verifying contract
    /// @return hash - EIP-712 hash for the `deactivation`
    function signingHash(InstructionDeactivation calldata deactivation, bytes32 domainSeparator)
        internal
        pure
        returns (bytes32)
    {
        return keccak256(
            abi.encodePacked(
                Constants.EIP712_PREFIX,
                domainSeparator,
                keccak256(abi.encode(DEACTIVATION_TYPEHASH, deactivation.instructionId))
            )
        );
    }
}

// SPDX-License-Identifier: MIT OR Apache-2.0
pragma solidity ^0.8.26;

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

import {IFeeTokenRegistry} from "../../infrastructure/interfaces/IFeeTokenRegistry.sol";
import {ITreasury} from "../../infrastructure/interfaces/ITreasury.sol";

import {IOtimFee, FEE_TYPEHASH} from "./interfaces/IOtimFee.sol";

/// @title OtimFee
/// @author Otim Labs, Inc.
/// @notice abstract contract for the Otim centralized fee model
abstract contract OtimFee is IOtimFee {
    using SafeERC20 for IERC20;

    /// @notice the Otim fee token registry contract for converting wei to ERC20 tokens
    IFeeTokenRegistry public immutable feeTokenRegistry;
    /// @notice the Otim treasury contract that receives fees
    ITreasury public immutable treasury;
    /// @notice the gas constant used to calculate the fee
    uint256 public immutable gasConstant;

    constructor(address feeTokenRegistryAddress, address treasuryAddress, uint256 gasConstant_) {
        feeTokenRegistry = IFeeTokenRegistry(feeTokenRegistryAddress);
        treasury = ITreasury(treasuryAddress);
        gasConstant = gasConstant_;
    }

    /// @inheritdoc IOtimFee
    function hash(Fee memory fee) public pure returns (bytes32) {
        return keccak256(
            abi.encode(FEE_TYPEHASH, fee.token, fee.maxBaseFeePerGas, fee.maxPriorityFeePerGas, fee.executionFee)
        );
    }

    /// @inheritdoc IOtimFee
    function chargeFee(uint256 gasUsed, Fee memory fee) public override {
        // fee.executionFee == 0 is a magic value signifying a sponsored Instruction
        if (fee.executionFee == 0) return;

        // check if the base fee is too high
        if (block.basefee > fee.maxBaseFeePerGas && fee.maxBaseFeePerGas != 0) {
            revert BaseFeePerGasTooHigh();
        }

        // check if the priority fee is too high
        if (tx.gasprice - block.basefee > fee.maxPriorityFeePerGas) {
            revert PriorityFeePerGasTooHigh();
        }

        // calculate the total cost of the gas used in the transaction
        uint256 weiGasCost = (gasUsed + gasConstant) * tx.gasprice;

        // if fee.token is address(0), the fee is paid in native currency
        if (fee.token == address(0)) {
            // calculate the fee cost based on the gas used and the additional fee
            uint256 weiTotalCost = weiGasCost + fee.executionFee;

            // check if the user has enough balance to pay the fee
            if (address(this).balance < weiTotalCost) {
                revert InsufficientFeeBalance();
            }

            // transfer to the treasury contract
            // slither-disable-next-line arbitrary-send-eth
            treasury.deposit{value: weiTotalCost}();
        } else {
            // calculate the fee cost based on the cost of the gas used (denominated in the fee token) and the execution fee
            uint256 tokenTotalCost = feeTokenRegistry.weiToToken(fee.token, weiGasCost) + fee.executionFee;

            // check if the user has enough balance to pay the fee
            if (IERC20(fee.token).balanceOf(address(this)) < tokenTotalCost) {
                revert InsufficientFeeBalance();
            }

            // transfer to the treasury contract
            IERC20(fee.token).safeTransfer(address(treasury), tokenTotalCost);
        }
    }
}

// SPDX-License-Identifier: MIT OR Apache-2.0
pragma solidity ^0.8.26;

import {InstructionLib} from "../../libraries/Instruction.sol";

/// @title IAction
/// @author Otim Labs, Inc.
/// @notice interface for Action contracts
interface IAction {
    /// @notice returns the EIP-712 type hash for the Action-specific Instruction and the EIP-712 hash of the Action-specific Instruction arguments
    /// @param arguments - encoded Instruction arguments
    /// @return instructionTypeHash - EIP-712 type hash for the Action-specific Instruction
    /// @return argumentsTypeHash - EIP-712 hash of the Action-specific Instruction arguments
    function argumentsHash(bytes calldata arguments) external returns (bytes32, bytes32);

    /// @notice execute Action logic with Instruction arguments
    /// @param instruction - Instruction
    /// @param signature - Signature over the Instruction signing hash
    /// @param executionState - ExecutionState
    /// @return deactivate - whether the Instruction should be automatically deactivated
    function execute(
        InstructionLib.Instruction calldata instruction,
        InstructionLib.Signature calldata signature,
        InstructionLib.ExecutionState calldata executionState
    ) external returns (bool);
}

// SPDX-License-Identifier: MIT OR Apache-2.0
pragma solidity ^0.8.26;

import {IOtimFee} from "../fee-models/interfaces/IOtimFee.sol";

bytes32 constant INSTRUCTION_TYPEHASH = keccak256(
    "Instruction(uint256 salt,uint256 maxExecutions,address action,RefuelERC20 refuelERC20)Fee(address token,uint256 maxBaseFeePerGas,uint256 maxPriorityFeePerGas,uint256 executionFee)RefuelERC20(address token,address target,uint256 threshold,uint256 endBalance,Fee fee)"
);

bytes32 constant ARGUMENTS_TYPEHASH = keccak256(
    "RefuelERC20(address token,address target,uint256 threshold,uint256 endBalance,Fee fee)Fee(address token,uint256 maxBaseFeePerGas,uint256 maxPriorityFeePerGas,uint256 executionFee)"
);

/// @title IRefuelERC20Action
/// @author Otim Labs, Inc.
/// @notice interface for RefuelERC20Action contract
interface IRefuelERC20Action is IOtimFee {
    /// @notice arguments for the RefuelERC20Action contract
    /// @param token - the address of the ERC20 token to refuel with
    /// @param target - the address to refuel
    /// @param threshold - the minimum balance required to refuel
    /// @param endBalance - the target balance after refueling
    /// @param fee - the fee Otim will charge for the refuel
    struct RefuelERC20 {
        address token;
        address target;
        uint256 threshold;
        uint256 endBalance;
        Fee fee;
    }

    /// @notice calculates the EIP-712 hash of the RefuelERC20 struct
    function hash(RefuelERC20 memory refuelERC20) external pure returns (bytes32);
}

File 8 of 16 : Errors.sol
// SPDX-License-Identifier: MIT OR Apache-2.0
pragma solidity ^0.8.26;

error InvalidArguments();

error InsufficientBalance();

error BalanceOverThreshold();
error BalanceUnderThreshold();

error UniswapV3PoolDoesNotExist();

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

File 10 of 16 : Constants.sol
// SPDX-License-Identifier: MIT OR Apache-2.0
pragma solidity ^0.8.26;

/// @title Constants
/// @author Otim Labs, Inc.
/// @notice a library defining constants used throughout the protocol
library Constants {
    /// @notice the EIP-712 signature prefix
    bytes2 public constant EIP712_PREFIX = 0x1901;

    /// @notice the EIP-7702 delegation designator prefix
    bytes3 public constant EIP7702_PREFIX = 0xef0100;
}

// SPDX-License-Identifier: MIT OR Apache-2.0
pragma solidity ^0.8.26;

/// @title FeeTokenRegistry
/// @author Otim Labs, Inc.
/// @notice interface for the FeeTokenRegistry contract
interface IFeeTokenRegistry {
    /// @notice fee token data struct
    /// @param priceFeed - a price feed of the form <token>/ETH
    /// @param heartbeat - the time in seconds between price feed updates
    /// @param priceFeedDecimals - the number of decimals for the price feed
    /// @param tokenDecimals - the number of decimals for the token
    /// @param registered - whether the token is registered
    struct FeeTokenData {
        address priceFeed;
        uint40 heartbeat;
        uint8 priceFeedDecimals;
        uint8 tokenDecimals;
        bool registered;
    }

    /// @notice emitted when a fee token is added
    event FeeTokenAdded(
        address indexed token, address indexed priceFeed, uint40 heartbeat, uint8 priceFeedDecimals, uint8 tokenDecimals
    );
    /// @notice emitted when a fee token is removed
    event FeeTokenRemoved(
        address indexed token, address indexed priceFeed, uint40 heartbeat, uint8 priceFeedDecimals, uint8 tokenDecimals
    );

    error InvalidFeeTokenData();
    error PriceFeedNotInitialized();
    error FeeTokenAlreadyRegistered();
    error FeeTokenNotRegistered();
    error InvalidPrice();
    error StalePrice();

    /// @notice adds a fee token to the registry
    /// @param token - the ERC20 token address
    /// @param priceFeed - the price feed address
    /// @param heartbeat - the time in seconds between price feed updates
    function addFeeToken(address token, address priceFeed, uint40 heartbeat) external;

    /// @notice removes a fee token from the registry
    /// @param token - the ERC20 token address
    function removeFeeToken(address token) external;

    /// @notice converts a wei amount to a token amount
    /// @param token - the ERC20 token address to convert to
    /// @param weiAmount - the amount of wei to convert
    /// @return tokenAmount - converted token amount
    function weiToToken(address token, uint256 weiAmount) external view returns (uint256);
}

// SPDX-License-Identifier: MIT OR Apache-2.0
pragma solidity ^0.8.26;

/// @title ITreasury
/// @author Otim Labs, Inc.
/// @notice interface for Treasury contract
interface ITreasury {
    /// @notice thrown when the owner tries to withdraw to the zero address
    error InvalidTarget();
    /// @notice thrown when the withdrawl fails
    error WithdrawalFailed(bytes result);
    /// @notice thrown when the owner tries to withdraw more than the contract balance
    error InsufficientBalance();

    /// @notice deposit ether into the treasury
    function deposit() external payable;

    /// @notice withdraw ether from the treasury
    /// @param to - the address to withdraw to
    /// @param value - the amount to withdraw
    function withdraw(address to, uint256 value) external;

    /// @notice withdraw ERC20 tokens from the treasury
    /// @param token - the ERC20 token to withdraw
    /// @param to - the address to withdraw to
    /// @param value - the amount to withdraw
    function withdrawERC20(address token, address to, uint256 value) external;
}

// SPDX-License-Identifier: MIT OR Apache-2.0
pragma solidity ^0.8.26;

bytes32 constant FEE_TYPEHASH =
    keccak256("Fee(address token,uint256 maxBaseFeePerGas,uint256 maxPriorityFeePerGas,uint256 executionFee)");

/// @title IOtimFee
/// @author Otim Labs, Inc.
/// @notice interface for the OtimFee contract
interface IOtimFee {
    /// @notice fee struct
    /// @param token - the token to be used for the fee (address(0) for native currency)
    /// @param maxBaseFeePerGas - the maximum basefee per gas the user is willing to pay
    /// @param maxPriorityFeePerGas - the maximum priority fee per gas the user is willing to pay
    /// @param executionFee - fixed fee to be paid for each execution
    struct Fee {
        address token;
        uint256 maxBaseFeePerGas;
        uint256 maxPriorityFeePerGas;
        uint256 executionFee;
    }

    /// @notice calculates the EIP-712 hash of the Fee struct
    function hash(Fee memory fee) external pure returns (bytes32);

    /// @notice charges a fee for the Instruction execution
    /// @param gasUsed - amount of gas used during the Instruction execution
    /// @param fee - additional fee to be paid
    function chargeFee(uint256 gasUsed, Fee memory fee) external;

    error InsufficientFeeBalance();
    error BaseFeePerGasTooHigh();
    error PriorityFeePerGasTooHigh();
}

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

pragma solidity ^0.8.20;

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

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

pragma solidity ^0.8.20;

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

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

pragma solidity ^0.8.20;

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

Settings
{
  "remappings": [
    "@chainlink-contracts/=dependencies/smartcontractkit-chainlink-2.22.0/contracts/",
    "@openzeppelin-contracts/=dependencies/@openzeppelin-contracts-5.3.0/",
    "@openzeppelin/contracts/=dependencies/@openzeppelin-contracts-5.3.0/",
    "@uniswap-universal-router/=dependencies/@uniswap-universal-router-2.0.0/",
    "@uniswap-v3-core/=dependencies/@uniswap-v3-core-1.0.2-solc-0.8-simulate/",
    "@uniswap-v3-periphery/=dependencies/@uniswap-v3-periphery-1.4.4/",
    "forge-std/=dependencies/forge-std-1.9.7/",
    "@openzeppelin-contracts-5.3.0/=dependencies/@openzeppelin-contracts-5.3.0/",
    "@uniswap-universal-router-2.0.0/=dependencies/@uniswap-universal-router-2.0.0/",
    "@uniswap-v3-core-1.0.2-solc-0.8-simulate/=dependencies/@uniswap-v3-core-1.0.2-solc-0.8-simulate/contracts/",
    "@uniswap-v3-periphery-1.4.4/=dependencies/@uniswap-v3-periphery-1.4.4/contracts/",
    "@uniswap/=dependencies/@uniswap-v3-periphery-1.4.4/node_modules/@uniswap/",
    "ds-test/=dependencies/@uniswap-universal-router-2.0.0/lib/forge-std/lib/ds-test/src/",
    "erc4626-tests/=dependencies/@uniswap-universal-router-2.0.0/lib/v4-periphery/lib/v4-core/lib/openzeppelin-contracts/lib/erc4626-tests/",
    "forge-gas-snapshot/=dependencies/@uniswap-universal-router-2.0.0/lib/permit2/lib/forge-gas-snapshot/src/",
    "forge-std-1.9.7/=dependencies/forge-std-1.9.7/src/",
    "openzeppelin-contracts/=dependencies/@uniswap-universal-router-2.0.0/lib/permit2/lib/openzeppelin-contracts/",
    "permit2/=dependencies/@uniswap-universal-router-2.0.0/lib/permit2/",
    "smartcontractkit-chainlink-2.22.0/=dependencies/smartcontractkit-chainlink-2.22.0/",
    "solmate/=dependencies/@uniswap-universal-router-2.0.0/lib/solmate/src/",
    "v3-periphery/=dependencies/@uniswap-universal-router-2.0.0/lib/v3-periphery/contracts/",
    "v4-core/=dependencies/@uniswap-universal-router-2.0.0/lib/v4-periphery/lib/v4-core/src/",
    "v4-periphery/=dependencies/@uniswap-universal-router-2.0.0/lib/v4-periphery/"
  ],
  "optimizer": {
    "enabled": true,
    "runs": 1000
  },
  "metadata": {
    "useLiteralContent": false,
    "bytecodeHash": "none",
    "appendCBOR": false
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "evmVersion": "cancun",
  "viaIR": false,
  "libraries": {}
}

Contract Security Audit

Contract ABI

API
[{"inputs":[{"internalType":"address","name":"feeTokenRegistryAddress","type":"address"},{"internalType":"address","name":"treasuryAddress","type":"address"},{"internalType":"uint256","name":"gasConstant_","type":"uint256"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"BalanceOverThreshold","type":"error"},{"inputs":[],"name":"BaseFeePerGasTooHigh","type":"error"},{"inputs":[],"name":"InsufficientBalance","type":"error"},{"inputs":[],"name":"InsufficientFeeBalance","type":"error"},{"inputs":[],"name":"InvalidArguments","type":"error"},{"inputs":[],"name":"PriorityFeePerGasTooHigh","type":"error"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"SafeERC20FailedOperation","type":"error"},{"inputs":[{"internalType":"bytes","name":"arguments","type":"bytes"}],"name":"argumentsHash","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"},{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"gasUsed","type":"uint256"},{"components":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"maxBaseFeePerGas","type":"uint256"},{"internalType":"uint256","name":"maxPriorityFeePerGas","type":"uint256"},{"internalType":"uint256","name":"executionFee","type":"uint256"}],"internalType":"struct IOtimFee.Fee","name":"fee","type":"tuple"}],"name":"chargeFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"uint256","name":"salt","type":"uint256"},{"internalType":"uint256","name":"maxExecutions","type":"uint256"},{"internalType":"address","name":"action","type":"address"},{"internalType":"bytes","name":"arguments","type":"bytes"}],"internalType":"struct InstructionLib.Instruction","name":"instruction","type":"tuple"},{"components":[{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"internalType":"struct InstructionLib.Signature","name":"","type":"tuple"},{"components":[{"internalType":"bool","name":"deactivated","type":"bool"},{"internalType":"uint120","name":"executionCount","type":"uint120"},{"internalType":"uint120","name":"lastExecuted","type":"uint120"}],"internalType":"struct InstructionLib.ExecutionState","name":"executionState","type":"tuple"}],"name":"execute","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"feeTokenRegistry","outputs":[{"internalType":"contract IFeeTokenRegistry","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"gasConstant","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"token","type":"address"},{"internalType":"address","name":"target","type":"address"},{"internalType":"uint256","name":"threshold","type":"uint256"},{"internalType":"uint256","name":"endBalance","type":"uint256"},{"components":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"maxBaseFeePerGas","type":"uint256"},{"internalType":"uint256","name":"maxPriorityFeePerGas","type":"uint256"},{"internalType":"uint256","name":"executionFee","type":"uint256"}],"internalType":"struct IOtimFee.Fee","name":"fee","type":"tuple"}],"internalType":"struct IRefuelERC20Action.RefuelERC20","name":"refuelERC20","type":"tuple"}],"name":"hash","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"pure","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"maxBaseFeePerGas","type":"uint256"},{"internalType":"uint256","name":"maxPriorityFeePerGas","type":"uint256"},{"internalType":"uint256","name":"executionFee","type":"uint256"}],"internalType":"struct IOtimFee.Fee","name":"fee","type":"tuple"}],"name":"hash","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"treasury","outputs":[{"internalType":"contract ITreasury","name":"","type":"address"}],"stateMutability":"view","type":"function"}]

60e060405234801561000f575f80fd5b50604051610d7c380380610d7c83398101604081905261002e91610064565b6001600160a01b03928316608052911660a05260c05261009d565b80516001600160a01b038116811461005f575f80fd5b919050565b5f805f60608486031215610076575f80fd5b61007f84610049565b925061008d60208501610049565b9150604084015190509250925092565b60805160a05160c051610c9b6100e15f395f8181608e015261054901525f818160db015281816105c0015261078e01525f818161017a015261067d0152610c9b5ff3fe608060405234801561000f575f80fd5b5060043610610085575f3560e01c806399d0dd371161005857806399d0dd37146101385780639fcd91b31461014d578063aebfab1b14610175578063fd89ea0c1461019c575f80fd5b80632cb0f8a214610089578063343c2f1c146100c357806361d027b3146100d657806369179de314610115575b5f80fd5b6100b07f000000000000000000000000000000000000000000000000000000000000000081565b6040519081526020015b60405180910390f35b6100b06100d13660046109fe565b6101af565b6100fd7f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b0390911681526020016100ba565b610128610123366004610aa2565b610245565b60405190151581526020016100ba565b61014b610146366004610b02565b61049f565b005b61016061015b366004610b2d565b6107ba565b604080519283526020830191909152016100ba565b6100fd7f000000000000000000000000000000000000000000000000000000000000000081565b6100b06101aa366004610b9b565b6107f8565b5f7f633f734c5539ba62a09ebf4db9ff32e257d31bf9715a5e4baaa3d906119febc1825f01518360200151846040015185606001516101f187608001516107f8565b6040805160208101979097526001600160a01b0395861690870152939092166060850152608084015260a083015260c082015260e0015b604051602081830303815290604052805190602001209050919050565b5f805a90505f6102586060870187610bbc565b81019061026591906109fe565b90506102776040850160208601610bff565b6effffffffffffffffffffffffffffff165f036102fa5780516001600160a01b031615806102b0575060208101516001600160a01b0316155b806102c357508060600151816040015110155b156102fa576040517f5f6f132c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b805160208201516040516370a0823160e01b81526001600160a01b0391821660048201525f918316906370a0823190602401602060405180830381865afa158015610347573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061036b9190610c2d565b905082604001518111156103ab576040517f9a741dc000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f8184606001516103bc9190610c58565b6040516370a0823160e01b815230600482015290915081906001600160a01b038516906370a0823190602401602060405180830381865afa158015610403573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906104279190610c2d565b101561045f576040517ff4d678b800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6020840151610479906001600160a01b0385169083610868565b6104915a6104879087610c58565b856080015161049f565b505f98975050505050505050565b80606001515f036104ae575050565b8060200151481180156104c45750602081015115155b156104fb576040517f2f3d0a5c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b604081015161050a483a610c58565b1115610542576040517f8ef482f600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f3a61056e7f000000000000000000000000000000000000000000000000000000000000000085610c71565b6105789190610c84565b82519091506001600160a01b0316610634575f82606001518261059b9190610c71565b9050804710156105be57604051637806a4f560e11b815260040160405180910390fd5b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663d0e30db0826040518263ffffffff1660e01b81526004015f604051808303818588803b158015610617575f80fd5b505af1158015610629573d5f803e3d5ffd5b505050505050505050565b606082015182516040517fd251f7ba0000000000000000000000000000000000000000000000000000000081526001600160a01b039182166004820152602481018490525f92917f0000000000000000000000000000000000000000000000000000000000000000169063d251f7ba90604401602060405180830381865afa1580156106c2573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906106e69190610c2d565b6106f09190610c71565b83516040516370a0823160e01b815230600482015291925082916001600160a01b03909116906370a0823190602401602060405180830381865afa15801561073a573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061075e9190610c2d565b101561077d57604051637806a4f560e11b815260040160405180910390fd5b82516107b3906001600160a01b03167f000000000000000000000000000000000000000000000000000000000000000083610868565b505b505050565b5f807f7024fcc36180b0e99158ada8b32e8e3af5b513d9ae8cd66bb144955660759a7f6107ec6100d1858701876109fe565b915091505b9250929050565b5f7f7aff6c7b9b8aafff555894be6cfd4a6211fc7adbcd97db18b05d4a51ed7482a0825f01518360200151846040015185606001516040516020016102289594939291909485526001600160a01b0393909316602085015260408401919091526060830152608082015260a00190565b604080516001600160a01b03841660248201526044808201849052825180830390910181526064909101909152602080820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fa9059cbb0000000000000000000000000000000000000000000000000000000017815282516107b593879390925f9283929183919082885af180610904576040513d5f823e3d81fd5b50505f513d9150811561091b578060011415610928565b6001600160a01b0384163b155b156107b3576040517f5274afe70000000000000000000000000000000000000000000000000000000081526001600160a01b038516600482015260240160405180910390fd5b80356001600160a01b0381168114610984575f80fd5b919050565b5f60808284031215610999575f80fd5b6040516080810167ffffffffffffffff811182821017156109c857634e487b7160e01b5f52604160045260245ffd5b6040529050806109d78361096e565b81526020838101359082015260408084013590820152606092830135920191909152919050565b5f610100828403128015610a10575f80fd5b5060405160a0810167ffffffffffffffff81118282101715610a4057634e487b7160e01b5f52604160045260245ffd5b604052610a4c8361096e565b8152610a5a6020840161096e565b60208201526040838101359082015260608084013590820152610a808460808501610989565b60808201529392505050565b5f60608284031215610a9c575f80fd5b50919050565b5f805f60e08486031215610ab4575f80fd5b833567ffffffffffffffff811115610aca575f80fd5b840160808187031215610adb575f80fd5b9250610aea8560208601610a8c565b9150610af98560808601610a8c565b90509250925092565b5f8060a08385031215610b13575f80fd5b82359150610b248460208501610989565b90509250929050565b5f8060208385031215610b3e575f80fd5b823567ffffffffffffffff811115610b54575f80fd5b8301601f81018513610b64575f80fd5b803567ffffffffffffffff811115610b7a575f80fd5b856020828401011115610b8b575f80fd5b6020919091019590945092505050565b5f60808284031215610bab575f80fd5b610bb58383610989565b9392505050565b5f808335601e19843603018112610bd1575f80fd5b83018035915067ffffffffffffffff821115610beb575f80fd5b6020019150368190038213156107f1575f80fd5b5f60208284031215610c0f575f80fd5b81356effffffffffffffffffffffffffffff81168114610bb5575f80fd5b5f60208284031215610c3d575f80fd5b5051919050565b634e487b7160e01b5f52601160045260245ffd5b81810381811115610c6b57610c6b610c44565b92915050565b80820180821115610c6b57610c6b610c44565b8082028115828204841417610c6b57610c6b610c4456000000000000000000000000a972a2c093456c7298c3527fc87de41f89a2ebae0000000000000000000000001fddc4eeec10e3317b27563ad0126285a329350d0000000000000000000000000000000000000000000000000000000000019834

Deployed Bytecode

0x608060405234801561000f575f80fd5b5060043610610085575f3560e01c806399d0dd371161005857806399d0dd37146101385780639fcd91b31461014d578063aebfab1b14610175578063fd89ea0c1461019c575f80fd5b80632cb0f8a214610089578063343c2f1c146100c357806361d027b3146100d657806369179de314610115575b5f80fd5b6100b07f000000000000000000000000000000000000000000000000000000000001983481565b6040519081526020015b60405180910390f35b6100b06100d13660046109fe565b6101af565b6100fd7f0000000000000000000000001fddc4eeec10e3317b27563ad0126285a329350d81565b6040516001600160a01b0390911681526020016100ba565b610128610123366004610aa2565b610245565b60405190151581526020016100ba565b61014b610146366004610b02565b61049f565b005b61016061015b366004610b2d565b6107ba565b604080519283526020830191909152016100ba565b6100fd7f000000000000000000000000a972a2c093456c7298c3527fc87de41f89a2ebae81565b6100b06101aa366004610b9b565b6107f8565b5f7f633f734c5539ba62a09ebf4db9ff32e257d31bf9715a5e4baaa3d906119febc1825f01518360200151846040015185606001516101f187608001516107f8565b6040805160208101979097526001600160a01b0395861690870152939092166060850152608084015260a083015260c082015260e0015b604051602081830303815290604052805190602001209050919050565b5f805a90505f6102586060870187610bbc565b81019061026591906109fe565b90506102776040850160208601610bff565b6effffffffffffffffffffffffffffff165f036102fa5780516001600160a01b031615806102b0575060208101516001600160a01b0316155b806102c357508060600151816040015110155b156102fa576040517f5f6f132c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b805160208201516040516370a0823160e01b81526001600160a01b0391821660048201525f918316906370a0823190602401602060405180830381865afa158015610347573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061036b9190610c2d565b905082604001518111156103ab576040517f9a741dc000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f8184606001516103bc9190610c58565b6040516370a0823160e01b815230600482015290915081906001600160a01b038516906370a0823190602401602060405180830381865afa158015610403573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906104279190610c2d565b101561045f576040517ff4d678b800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6020840151610479906001600160a01b0385169083610868565b6104915a6104879087610c58565b856080015161049f565b505f98975050505050505050565b80606001515f036104ae575050565b8060200151481180156104c45750602081015115155b156104fb576040517f2f3d0a5c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b604081015161050a483a610c58565b1115610542576040517f8ef482f600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f3a61056e7f000000000000000000000000000000000000000000000000000000000001983485610c71565b6105789190610c84565b82519091506001600160a01b0316610634575f82606001518261059b9190610c71565b9050804710156105be57604051637806a4f560e11b815260040160405180910390fd5b7f0000000000000000000000001fddc4eeec10e3317b27563ad0126285a329350d6001600160a01b031663d0e30db0826040518263ffffffff1660e01b81526004015f604051808303818588803b158015610617575f80fd5b505af1158015610629573d5f803e3d5ffd5b505050505050505050565b606082015182516040517fd251f7ba0000000000000000000000000000000000000000000000000000000081526001600160a01b039182166004820152602481018490525f92917f000000000000000000000000a972a2c093456c7298c3527fc87de41f89a2ebae169063d251f7ba90604401602060405180830381865afa1580156106c2573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906106e69190610c2d565b6106f09190610c71565b83516040516370a0823160e01b815230600482015291925082916001600160a01b03909116906370a0823190602401602060405180830381865afa15801561073a573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061075e9190610c2d565b101561077d57604051637806a4f560e11b815260040160405180910390fd5b82516107b3906001600160a01b03167f0000000000000000000000001fddc4eeec10e3317b27563ad0126285a329350d83610868565b505b505050565b5f807f7024fcc36180b0e99158ada8b32e8e3af5b513d9ae8cd66bb144955660759a7f6107ec6100d1858701876109fe565b915091505b9250929050565b5f7f7aff6c7b9b8aafff555894be6cfd4a6211fc7adbcd97db18b05d4a51ed7482a0825f01518360200151846040015185606001516040516020016102289594939291909485526001600160a01b0393909316602085015260408401919091526060830152608082015260a00190565b604080516001600160a01b03841660248201526044808201849052825180830390910181526064909101909152602080820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fa9059cbb0000000000000000000000000000000000000000000000000000000017815282516107b593879390925f9283929183919082885af180610904576040513d5f823e3d81fd5b50505f513d9150811561091b578060011415610928565b6001600160a01b0384163b155b156107b3576040517f5274afe70000000000000000000000000000000000000000000000000000000081526001600160a01b038516600482015260240160405180910390fd5b80356001600160a01b0381168114610984575f80fd5b919050565b5f60808284031215610999575f80fd5b6040516080810167ffffffffffffffff811182821017156109c857634e487b7160e01b5f52604160045260245ffd5b6040529050806109d78361096e565b81526020838101359082015260408084013590820152606092830135920191909152919050565b5f610100828403128015610a10575f80fd5b5060405160a0810167ffffffffffffffff81118282101715610a4057634e487b7160e01b5f52604160045260245ffd5b604052610a4c8361096e565b8152610a5a6020840161096e565b60208201526040838101359082015260608084013590820152610a808460808501610989565b60808201529392505050565b5f60608284031215610a9c575f80fd5b50919050565b5f805f60e08486031215610ab4575f80fd5b833567ffffffffffffffff811115610aca575f80fd5b840160808187031215610adb575f80fd5b9250610aea8560208601610a8c565b9150610af98560808601610a8c565b90509250925092565b5f8060a08385031215610b13575f80fd5b82359150610b248460208501610989565b90509250929050565b5f8060208385031215610b3e575f80fd5b823567ffffffffffffffff811115610b54575f80fd5b8301601f81018513610b64575f80fd5b803567ffffffffffffffff811115610b7a575f80fd5b856020828401011115610b8b575f80fd5b6020919091019590945092505050565b5f60808284031215610bab575f80fd5b610bb58383610989565b9392505050565b5f808335601e19843603018112610bd1575f80fd5b83018035915067ffffffffffffffff821115610beb575f80fd5b6020019150368190038213156107f1575f80fd5b5f60208284031215610c0f575f80fd5b81356effffffffffffffffffffffffffffff81168114610bb5575f80fd5b5f60208284031215610c3d575f80fd5b5051919050565b634e487b7160e01b5f52601160045260245ffd5b81810381811115610c6b57610c6b610c44565b92915050565b80820180821115610c6b57610c6b610c44565b8082028115828204841417610c6b57610c6b610c4456

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

000000000000000000000000a972a2c093456c7298c3527fc87de41f89a2ebae0000000000000000000000001fddc4eeec10e3317b27563ad0126285a329350d0000000000000000000000000000000000000000000000000000000000019834

-----Decoded View---------------
Arg [0] : feeTokenRegistryAddress (address): 0xa972A2C093456c7298C3527fc87DE41F89A2eBaE
Arg [1] : treasuryAddress (address): 0x1FDdC4EEEc10E3317B27563Ad0126285A329350d
Arg [2] : gasConstant_ (uint256): 104500

-----Encoded View---------------
3 Constructor Arguments found :
Arg [0] : 000000000000000000000000a972a2c093456c7298c3527fc87de41f89a2ebae
Arg [1] : 0000000000000000000000001fddc4eeec10e3317b27563ad0126285a329350d
Arg [2] : 0000000000000000000000000000000000000000000000000000000000019834


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

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