Base Sepolia Testnet

Contract

0xF800749c5AB7b1382BB1C8E1a5A1c60887E89a2a

Overview

ETH Balance

0 ETH

Multichain Info

N/A
Transaction Hash
Method
Block
From
To

There are no matching entries

1 Internal Transaction found.

Latest 1 internal transaction

Parent Transaction Hash Block From To
236804662025-03-28 2:33:4026 days ago1743129220  Contract Creation0 ETH

Loading...
Loading

Similar Match Source Code
This contract matches the deployed Bytecode of the Source Code for Contract 0x587FF765...B51c113f8
The constructor portion of the code might be different and could alter the actual behaviour of the contract

Contract Name:
IntentSource

Compiler Version
v0.8.26+commit.8a97fa7a

Optimization Enabled:
Yes with 200 runs

Other Settings:
paris EvmVersion

Contract Source Code (Solidity Standard Json-Input format)

File 1 of 14 : IntentSource.sol
/* -*- c-basic-offset: 4 -*- */
// SPDX-License-Identifier: MIT
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 {IIntentSource} from "./interfaces/IIntentSource.sol";
import {BaseProver} from "./prover/BaseProver.sol";
import {Intent, Route, Reward, Call} from "./types/Intent.sol";
import {Semver} from "./libs/Semver.sol";

import {IntentFunder} from "./IntentFunder.sol";
import {IntentVault} from "./IntentVault.sol";

/**
 * @notice Source chain contract for the Eco Protocol's intent system
 * @dev Used to create intents and withdraw associated rewards. Works in conjunction with
 *      an inbox contract on the destination chain. Verifies intent fulfillment through
 *      a prover contract on the source chain
 * @dev This contract shouldn't not hold any funds or hold ony roles for other contracts,
 *      as it executes arbitrary calls to other contracts when funding intents.
 */
contract IntentSource is IIntentSource, Semver {
    using SafeERC20 for IERC20;

    mapping(bytes32 intentHash => ClaimState) public claims;

    address public fundingSource;

    address public refundToken;

    constructor() {}

    /**
     * @notice Retrieves claim state for a given intent hash
     * @param intentHash Hash of the intent to query
     * @return ClaimState struct containing claim information
     */
    function getClaim(
        bytes32 intentHash
    ) external view returns (ClaimState memory) {
        return claims[intentHash];
    }

    /**
     * @notice Gets the funding source for the intent funder
     * @return Address of the native token funding source
     */
    function getFundingSource() external view returns (address) {
        return fundingSource;
    }

    /**
     * @notice Gets the token used for vault refunds
     * @return Address of the vault refund token
     */
    function getRefundToken() external view returns (address) {
        return refundToken;
    }

    /**
     * @notice Calculates the hash of an intent and its components
     * @param intent The intent to hash
     * @return intentHash Combined hash of route and reward
     * @return routeHash Hash of the route component
     * @return rewardHash Hash of the reward component
     */
    function getIntentHash(
        Intent calldata intent
    )
        public
        pure
        returns (bytes32 intentHash, bytes32 routeHash, bytes32 rewardHash)
    {
        routeHash = keccak256(abi.encode(intent.route));
        rewardHash = keccak256(abi.encode(intent.reward));
        intentHash = keccak256(abi.encodePacked(routeHash, rewardHash));
    }

    /**
     * @notice Calculates the deterministic address of the intent funder
     * @param intent Intent to calculate vault address for
     * @return Address of the intent funder
     */
    function intentFunderAddress(
        Intent calldata intent
    ) external view returns (address) {
        (bytes32 intentHash, bytes32 routeHash, ) = getIntentHash(intent);
        address vault = _getIntentVaultAddress(
            intentHash,
            routeHash,
            intent.reward
        );
        return _getIntentFunderAddress(vault, routeHash, intent.reward);
    }

    /**
     * @notice Calculates the deterministic address of the intent vault
     * @param intent Intent to calculate vault address for
     * @return Address of the intent vault
     */
    function intentVaultAddress(
        Intent calldata intent
    ) external view returns (address) {
        (bytes32 intentHash, bytes32 routeHash, ) = getIntentHash(intent);
        return _getIntentVaultAddress(intentHash, routeHash, intent.reward);
    }

    /**
     * @notice Funds an intent with native tokens and ERC20 tokens
     * @dev Security: this allows to call any contract from the IntentSource,
     *      which can impose a risk if anything relies on IntentSource to be msg.sender
     * @param routeHash Hash of the route component
     * @param reward Reward structure containing distribution details
     * @param fundingAddress Address to fund the intent from
     * @param permitCalls Array of permit calls to approve token transfers
     * @param recoverToken Optional token address for handling incorrect vault transfers
     */
    function fundIntent(
        bytes32 routeHash,
        Reward calldata reward,
        address fundingAddress,
        Call[] calldata permitCalls,
        address recoverToken
    ) external payable {
        bytes32 rewardHash = keccak256(abi.encode(reward));
        bytes32 intentHash = keccak256(abi.encodePacked(routeHash, rewardHash));

        address vault = _getIntentVaultAddress(intentHash, routeHash, reward);

        emit IntentFunded(intentHash, fundingAddress);

        int256 vaultBalanceDeficit = int256(reward.nativeValue) -
            int256(vault.balance);

        if (vaultBalanceDeficit > 0 && msg.value > 0) {
            uint256 nativeAmount = msg.value > uint256(vaultBalanceDeficit)
                ? uint256(vaultBalanceDeficit)
                : msg.value;

            payable(vault).transfer(nativeAmount);

            uint256 currentBalance = address(this).balance;

            if (currentBalance > 0) {
                (bool success, ) = payable(msg.sender).call{
                    value: currentBalance
                }("");

                if (!success) {
                    revert NativeRewardTransferFailed();
                }
            }
        }

        uint256 callsLength = permitCalls.length;

        for (uint256 i = 0; i < callsLength; ++i) {
            Call calldata call = permitCalls[i];

            (bool success, ) = call.target.call(call.data);

            if (!success) {
                revert PermitCallFailed();
            }
        }

        fundingSource = fundingAddress;

        if (recoverToken != address(0)) {
            refundToken = recoverToken;
        }

        new IntentFunder{salt: routeHash}(vault, reward);

        fundingSource = address(0);

        if (recoverToken != address(0)) {
            refundToken = address(0);
        }
    }

    /**
     * @notice Creates an intent to execute instructions on a supported chain in exchange for assets
     * @dev If source chain proof isn't completed by expiry, rewards aren't redeemable regardless of execution.
     *      Solver must manage timing considerations (e.g., L1 data posting delays)
     * @param intent The intent struct containing all parameters
     * @param fund Whether to fund the reward or not
     * @return intentHash The hash of the created intent
     */
    function publishIntent(
        Intent calldata intent,
        bool fund
    ) external payable returns (bytes32 intentHash) {
        Route calldata route = intent.route;
        Reward calldata reward = intent.reward;

        uint256 rewardsLength = reward.tokens.length;
        bytes32 routeHash;

        (intentHash, routeHash, ) = getIntentHash(intent);

        if (claims[intentHash].status != uint8(ClaimStatus.Initiated)) {
            revert IntentAlreadyExists(intentHash);
        }

        emit IntentCreated(
            intentHash,
            route.salt,
            route.source,
            route.destination,
            route.inbox,
            route.tokens,
            route.calls,
            reward.creator,
            reward.prover,
            reward.deadline,
            reward.nativeValue,
            reward.tokens
        );

        address vault = _getIntentVaultAddress(intentHash, routeHash, reward);

        if (fund && !_isIntentFunded(intent, vault)) {
            if (reward.nativeValue > 0) {
                if (msg.value < reward.nativeValue) {
                    revert InsufficientNativeReward();
                }

                payable(vault).transfer(reward.nativeValue);

                uint256 currentBalance = address(this).balance;

                if (currentBalance > 0) {
                    (bool success, ) = payable(msg.sender).call{
                        value: currentBalance
                    }("");

                    if (!success) {
                        revert NativeRewardTransferFailed();
                    }
                }
            }

            for (uint256 i = 0; i < rewardsLength; ++i) {
                IERC20(reward.tokens[i].token).safeTransferFrom(
                    msg.sender,
                    vault,
                    reward.tokens[i].amount
                );
            }
        }
    }

    /**
     * @notice Checks if an intent is properly funded
     * @param intent Intent to validate
     * @return True if intent is properly funded, false otherwise
     */
    function isIntentFunded(
        Intent calldata intent
    ) external view returns (bool) {
        (bytes32 intentHash, bytes32 routeHash, ) = getIntentHash(intent);
        address vault = _getIntentVaultAddress(
            intentHash,
            routeHash,
            intent.reward
        );

        return _isIntentFunded(intent, vault);
    }

    /**
     * @notice Withdraws rewards associated with an intent to its claimant
     * @param routeHash Hash of the intent's route
     * @param reward Reward structure of the intent
     */
    function withdrawRewards(bytes32 routeHash, Reward calldata reward) public {
        bytes32 rewardHash = keccak256(abi.encode(reward));
        bytes32 intentHash = keccak256(abi.encodePacked(routeHash, rewardHash));

        address claimant = BaseProver(reward.prover).provenIntents(intentHash);

        // Claim the rewards if the intent has not been claimed
        if (
            claimant != address(0) &&
            claims[intentHash].status == uint8(ClaimStatus.Initiated)
        ) {
            claims[intentHash].claimant = claimant;

            emit Withdrawal(intentHash, claimant);

            new IntentVault{salt: routeHash}(intentHash, reward);

            claims[intentHash].status = uint8(ClaimStatus.Claimed);

            return;
        }

        if (claimant == address(0)) {
            revert UnauthorizedWithdrawal(intentHash);
        } else {
            revert RewardsAlreadyWithdrawn(intentHash);
        }
    }

    /**
     * @notice Batch withdraws multiple intents with the same claimant
     * @param routeHashes Array of route hashes for the intents
     * @param rewards Array of reward structures for the intents
     */
    function batchWithdraw(
        bytes32[] calldata routeHashes,
        Reward[] calldata rewards
    ) external {
        uint256 length = routeHashes.length;

        if (length != rewards.length) {
            revert ArrayLengthMismatch();
        }

        for (uint256 i = 0; i < length; ++i) {
            withdrawRewards(routeHashes[i], rewards[i]);
        }
    }

    /**
     * @notice Refunds rewards to the intent creator
     * @param routeHash Hash of the intent's route
     * @param reward Reward structure of the intent
     * @param token Optional token address for handling incorrect vault transfers
     */
    function refundIntent(
        bytes32 routeHash,
        Reward calldata reward,
        address token
    ) external {
        bytes32 rewardHash = keccak256(abi.encode(reward));
        bytes32 intentHash = keccak256(abi.encodePacked(routeHash, rewardHash));

        if (token != address(0)) {
            refundToken = token;
        }

        emit Refund(intentHash, reward.creator);

        new IntentVault{salt: routeHash}(intentHash, reward);

        if (claims[intentHash].status == uint8(ClaimStatus.Initiated)) {
            claims[intentHash].status = uint8(ClaimStatus.Refunded);
        }

        if (token != address(0)) {
            refundToken = address(0);
        }
    }

    /**
     * @notice Validates that an intent's vault holds sufficient rewards
     * @dev Checks both native token and ERC20 token balances
     * @param intent Intent to validate
     * @param vault Address of the intent's vault
     * @return True if vault has sufficient funds, false otherwise
     */
    function _isIntentFunded(
        Intent calldata intent,
        address vault
    ) internal view returns (bool) {
        Reward calldata reward = intent.reward;
        uint256 rewardsLength = reward.tokens.length;

        if (vault.balance < reward.nativeValue) return false;
        for (uint256 i = 0; i < rewardsLength; ++i) {
            address token = reward.tokens[i].token;
            uint256 amount = reward.tokens[i].amount;
            uint256 balance = IERC20(token).balanceOf(vault);

            if (balance < amount) return false;
        }

        return true;
    }

    /**
     * @notice Calculates the deterministic address of an intent funder using CREATE2
     * @dev Follows EIP-1014 for address calculation
     * @param vault Address of the intent vault
     * @param routeHash Hash of the route component
     * @param reward Reward structure
     * @return The calculated vault address
     */
    function _getIntentFunderAddress(
        address vault,
        bytes32 routeHash,
        Reward calldata reward
    ) internal view returns (address) {
        /* Convert a hash which is bytes32 to an address which is 20-byte long
        according to https://docs.soliditylang.org/en/v0.8.9/control-structures.html?highlight=create2#salted-contract-creations-create2 */
        return
            address(
                uint160(
                    uint256(
                        keccak256(
                            abi.encodePacked(
                                hex"ff",
                                address(this),
                                routeHash,
                                keccak256(
                                    abi.encodePacked(
                                        type(IntentFunder).creationCode,
                                        abi.encode(vault, reward)
                                    )
                                )
                            )
                        )
                    )
                )
            );
    }

    /**
     * @notice Calculates the deterministic address of an intent vault using CREATE2
     * @dev Follows EIP-1014 for address calculation
     * @param intentHash Hash of the full intent
     * @param routeHash Hash of the route component
     * @param reward Reward structure
     * @return The calculated vault address
     */
    function _getIntentVaultAddress(
        bytes32 intentHash,
        bytes32 routeHash,
        Reward calldata reward
    ) internal view returns (address) {
        /* Convert a hash which is bytes32 to an address which is 20-byte long
        according to https://docs.soliditylang.org/en/v0.8.9/control-structures.html?highlight=create2#salted-contract-creations-create2 */
        return
            address(
                uint160(
                    uint256(
                        keccak256(
                            abi.encodePacked(
                                hex"ff",
                                address(this),
                                routeHash,
                                keccak256(
                                    abi.encodePacked(
                                        type(IntentVault).creationCode,
                                        abi.encode(intentHash, reward)
                                    )
                                )
                            )
                        )
                    )
                )
            );
    }
}

File 2 of 14 : IERC20Permit.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/extensions/IERC20Permit.sol)

pragma solidity ^0.8.20;

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

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

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

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

pragma solidity ^0.8.20;

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

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

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

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

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

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

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

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

File 4 of 14 : SafeERC20.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/utils/SafeERC20.sol)

pragma solidity ^0.8.20;

import {IERC20} from "../IERC20.sol";
import {IERC20Permit} from "../extensions/IERC20Permit.sol";
import {Address} from "../../../utils/Address.sol";

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

    /**
     * @dev An operation with an ERC20 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 Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
     * non-reverting calls are assumed to be successful.
     */
    function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
        uint256 oldAllowance = token.allowance(address(this), spender);
        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.
     */
    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.
     */
    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 Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
     * on the return value: the return value is optional (but if data is returned, it must not be false).
     * @param token The token targeted by the call.
     * @param data The call data (encoded using abi.encode or one of its variants).
     */
    function _callOptionalReturn(IERC20 token, bytes memory data) private {
        // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
        // we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that
        // the target address contains contract code and also asserts for success in the low-level call.

        bytes memory returndata = address(token).functionCall(data);
        if (returndata.length != 0 && !abi.decode(returndata, (bool))) {
            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 silents catches all reverts and returns a bool instead.
     */
    function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
        // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
        // we're implementing it ourselves. We cannot use {Address-functionCall} here since this should return false
        // and not revert is the subcall reverts.

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

File 5 of 14 : Address.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/Address.sol)

pragma solidity ^0.8.20;

/**
 * @dev Collection of functions related to the address type
 */
library Address {
    /**
     * @dev The ETH balance of the account is not enough to perform the operation.
     */
    error AddressInsufficientBalance(address account);

    /**
     * @dev There's no code at `target` (it is not a contract).
     */
    error AddressEmptyCode(address target);

    /**
     * @dev A call to an address target failed. The target may have reverted.
     */
    error FailedInnerCall();

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

        (bool success, ) = recipient.call{value: amount}("");
        if (!success) {
            revert FailedInnerCall();
        }
    }

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

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but also transferring `value` wei to `target`.
     *
     * Requirements:
     *
     * - the calling contract must have an ETH balance of at least `value`.
     * - the called Solidity function must be `payable`.
     */
    function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
        if (address(this).balance < value) {
            revert AddressInsufficientBalance(address(this));
        }
        (bool success, bytes memory returndata) = target.call{value: value}(data);
        return verifyCallResultFromTarget(target, success, returndata);
    }

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

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

    /**
     * @dev Tool to verify that a low level call to smart-contract was successful, and reverts if the target
     * was not a contract or bubbling up the revert reason (falling back to {FailedInnerCall}) in case of an
     * unsuccessful call.
     */
    function verifyCallResultFromTarget(
        address target,
        bool success,
        bytes memory returndata
    ) internal view returns (bytes memory) {
        if (!success) {
            _revert(returndata);
        } else {
            // only check if target is a contract if the call was successful and the return data is empty
            // otherwise we already know that it was a contract
            if (returndata.length == 0 && target.code.length == 0) {
                revert AddressEmptyCode(target);
            }
            return returndata;
        }
    }

    /**
     * @dev Tool to verify that a low level call was successful, and reverts if it wasn't, either by bubbling the
     * revert reason or with a default {FailedInnerCall} error.
     */
    function verifyCallResult(bool success, bytes memory returndata) internal pure returns (bytes memory) {
        if (!success) {
            _revert(returndata);
        } else {
            return returndata;
        }
    }

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

File 6 of 14 : IntentFunder.sol
/* -*- c-basic-offset: 4 -*- */
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.26;

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";

import {IIntentSource} from "./interfaces/IIntentSource.sol";
import {Reward} from "./types/Intent.sol";

/**
 * @title IntentFunder
 * @notice Handles the funding process for intent rewards by transferring tokens and native currency to vaults
 * @dev This is a single-use contract that is deployed by IntentSource for each funding operation
 * and self-destructs after completing its task. It transfers any approved tokens from the funding
 * source to the vault up to the required amount or available allowance.
 */
contract IntentFunder {
    // Use OpenZeppelin's SafeERC20 for safe token transfers
    using SafeERC20 for IERC20;

    /**
     * @notice Instantiates and executes the funding operation in a single transaction
     * @dev The constructor performs all funding operations and then self-destructs.
     * The contract can only be deployed by IntentSource, which is checked implicitly
     * by accessing msg.sender as the IntentSource contract
     * @param vault The address of the vault that will receive the tokens and native currency
     * @param reward The reward structure containing token amounts and recipient details
     */
    constructor(address vault, Reward memory reward) {
        // Cast msg.sender to IIntentSource since we know it must be the IntentSource contract
        IIntentSource intentSource = IIntentSource(msg.sender);

        // Cache array length to save gas in loop
        uint256 rewardsLength = reward.tokens.length;

        // Get the address that is providing the tokens for funding
        address fundingSource = intentSource.getFundingSource();
        address refundToken = intentSource.getRefundToken();

        if (refundToken != address(0)) {
            IERC20(refundToken).safeTransfer(
                reward.creator,
                IERC20(refundToken).balanceOf(address(this))
            );
        }

        // Iterate through each token in the reward structure
        for (uint256 i; i < rewardsLength; ++i) {
            // Get token address and required amount for current reward
            address token = reward.tokens[i].token;
            uint256 amount = reward.tokens[i].amount;

            // Check how many tokens this contract is allowed to transfer from funding source
            uint256 allowance = IERC20(token).allowance(
                fundingSource,
                address(this)
            );

            // Calculate how many more tokens the vault needs to be fully funded
            // Cast to int256 to handle the case where vault is already overfunded
            int256 balanceDeficit = int256(amount) -
                int256(IERC20(token).balanceOf(vault));

            // Only proceed if vault needs more tokens and we have permission to transfer them
            if (balanceDeficit > 0 && allowance > 0) {
                // Calculate transfer amount as minimum of what's needed and what's allowed
                uint256 transferAmount = allowance > uint256(balanceDeficit)
                    ? uint256(balanceDeficit)
                    : allowance;

                // Transfer tokens from funding source to vault using safe transfer
                IERC20(token).safeTransferFrom(
                    fundingSource,
                    vault,
                    transferAmount
                );
            }
        }

        // After all transfers are complete, self-destruct and send any remaining ETH to reward creator
        selfdestruct(payable(reward.creator));
    }
}

File 7 of 14 : IntentVault.sol
/* -*- c-basic-offset: 4 -*- */
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.26;

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";

import {IIntentSource} from "./interfaces/IIntentSource.sol";
import {IIntentVault} from "./interfaces/IIntentVault.sol";
import {Reward} from "./types/Intent.sol";

/**
 * @title IntentVault
 * @notice A self-destructing contract that handles reward distribution for intents
 * @dev Created by IntentSource for each intent, handles token and native currency transfers,
 * then self-destructs after distributing rewards
 */
contract IntentVault is IIntentVault {
    using SafeERC20 for IERC20;

    /**
     * @notice Creates and immediately executes reward distribution
     * @dev Contract self-destructs after execution
     * @param intentHash Hash of the intent being claimed/refunded
     * @param reward Reward data structure containing distribution details
     */
    constructor(bytes32 intentHash, Reward memory reward) {
        // Get reference to the IntentSource contract that created this vault
        IIntentSource intentSource = IIntentSource(msg.sender);
        uint256 rewardsLength = reward.tokens.length;

        // Get current claim state and any refund token override
        IIntentSource.ClaimState memory state = intentSource.getClaim(
            intentHash
        );
        address claimant = state.claimant;
        address refundToken = intentSource.getRefundToken();

        // Ensure intent has expired if there's no claimant
        if (claimant == address(0) && block.timestamp < reward.deadline) {
            revert IntentNotExpired();
        }

        // Withdrawing to creator if intent is expired or already claimed/refunded
        if (
            (claimant == address(0) && block.timestamp >= reward.deadline) ||
            state.status != uint8(IIntentSource.ClaimStatus.Initiated)
        ) {
            claimant = reward.creator;
        }

        // Process each reward token
        for (uint256 i; i < rewardsLength; ++i) {
            address token = reward.tokens[i].token;
            uint256 amount = reward.tokens[i].amount;
            uint256 balance = IERC20(token).balanceOf(address(this));

            // Prevent reward tokens from being used as refund tokens
            if (token == refundToken) {
                revert RefundTokenCannotBeRewardToken();
            }

            // If creator is claiming, send full balance
            if (claimant == reward.creator) {
                if (balance > 0) {
                    IERC20(token).safeTransfer(claimant, balance);
                }
            } else {
                // For solver claims, verify sufficient balance and send reward amount
                if (amount < balance) {
                    revert InsufficientTokenBalance();
                }

                IERC20(token).safeTransfer(claimant, amount);
                // Return excess balance to creator
                if (balance > amount) {
                    IERC20(token).safeTransfer(
                        reward.creator,
                        balance - amount
                    );
                }
            }
        }

        // Handle native token rewards for solver claims
        if (claimant != reward.creator && reward.nativeValue > 0) {
            if (address(this).balance < reward.nativeValue) {
                revert InsufficientNativeBalance();
            }

            (bool success, ) = payable(claimant).call{
                value: reward.nativeValue
            }("");

            if (!success) {
                revert NativeRewardTransferFailed();
            }
        }

        // Process any refund token if specified
        if (refundToken != address(0)) {
            uint256 refundAmount = IERC20(refundToken).balanceOf(address(this));
            if (refundAmount > 0)
                IERC20(refundToken).safeTransfer(reward.creator, refundAmount);
        }

        // Self-destruct and send remaining ETH to creator
        selfdestruct(payable(reward.creator));
    }
}

File 8 of 14 : IIntentSource.sol
/* -*- c-basic-offset: 4 -*- */
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.26;

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

import {Intent, Reward, Call, TokenAmount} from "../types/Intent.sol";

/**
 * @title IIntentSource
 * @notice Interface for the source chain portion of the Eco Protocol's intent system
 * @dev Used to create intents and withdraw their associated rewards. Works with an inbox
 * contract on the destination chain and verifies fulfillment via a prover contract
 */
interface IIntentSource is ISemver {
    /**
     * @notice Thrown when an unauthorized address attempts to withdraw intent rewards
     * @param _hash Hash of the intent (key in intents mapping)
     */
    error UnauthorizedWithdrawal(bytes32 _hash);

    /**
     * @notice Thrown when attempting to withdraw from an intent with already claimed rewards
     * @param _hash Hash of the intent
     */
    error RewardsAlreadyWithdrawn(bytes32 _hash);

    /**
     * @notice Thrown when target addresses and calldata arrays have mismatched lengths or are empty
     */
    error CalldataMismatch();

    /**
     * @notice Thrown when reward tokens and amounts arrays have mismatched lengths or are empty
     */
    error RewardsMismatch();

    /**
     * @notice Thrown when batch withdrawal intent claimant doesn't match provided address
     * @param _hash Hash of the mismatched intent
     */
    error BadClaimant(bytes32 _hash);

    /**
     * @notice Thrown when a token transfer fails
     * @param _token Address of the token
     * @param _to Intended recipient
     * @param _amount Transfer amount
     */
    error TransferFailed(address _token, address _to, uint256 _amount);

    /**
     * @notice Thrown when a native token transfer fails
     */
    error NativeRewardTransferFailed();

    /**
     * @notice Thrown when a permit call to a contract fails
     */
    error PermitCallFailed();

    /**
     * @notice Thrown when attempting to publish an intent that already exists
     * @param intentHash Hash of the intent that already exists in the system
     */
    error IntentAlreadyExists(bytes32 intentHash);

    /**
     * @notice Thrown when attempting to fund an intent that has already been funded
     */
    error IntentAlreadyFunded();

    /**
     * @notice Thrown when the sent native token amount is less than the required reward amount
     */
    error InsufficientNativeReward();

    /**
     * @notice Thrown when attempting to validate an intent that fails basic validation checks
     * @dev This includes cases where the vault doesn't have sufficient balance or other validation failures
     */
    error InvalidIntent();

    /**
     * @notice Thrown when array lengths don't match in batch operations
     * @dev Used specifically in batch withdraw operations when routeHashes and rewards arrays have different lengths
     */
    error ArrayLengthMismatch();

    /**
     * @notice Status of an intent's reward claim
     */
    enum ClaimStatus {
        Initiated,
        Claimed,
        Refunded
    }

    /**
     * @notice State of an intent's reward claim
     * @dev Tracks claimant address and claim status
     */
    struct ClaimState {
        address claimant;
        uint8 status;
    }

    /**
     * @notice Emitted when an intent is funded with native tokens
     * @param intentHash Hash of the funded intent
     * @param fundingSource Address of the funder
     */
    event IntentFunded(bytes32 intentHash, address fundingSource);

    /**
     * @notice Emitted when a new intent is created
     * @param hash Hash of the created intent (key in intents mapping)
     * @param salt Creator-provided nonce
     * @param source Source chain ID
     * @param destination Destination chain ID
     * @param inbox Address of inbox contract on destination chain
     * @param routeTokens Array of tokens required for execution of calls on destination chain
     * @param calls Array of instruction calls to execute
     * @param creator Address that created the intent
     * @param prover Address of prover contract for validation
     * @param deadline Timestamp by which intent must be fulfilled for reward claim
     * @param nativeValue Amount of native tokens offered as reward
     * @param rewardTokens Array of ERC20 tokens and amounts offered as rewards
     */
    event IntentCreated(
        bytes32 indexed hash,
        bytes32 salt,
        uint256 source,
        uint256 destination,
        address inbox,
        TokenAmount[] routeTokens,
        Call[] calls,
        address indexed creator,
        address indexed prover,
        uint256 deadline,
        uint256 nativeValue,
        TokenAmount[] rewardTokens
    );

    /**
     * @notice Emitted when rewards are successfully withdrawn
     * @param _hash Hash of the claimed intent
     * @param _recipient Address receiving the rewards
     */
    event Withdrawal(bytes32 _hash, address indexed _recipient);

    /**
     * @notice Emitted when rewards are successfully withdrawn
     * @param _hash Hash of the claimed intent
     * @param _recipient Address receiving the rewards
     */
    event Refund(bytes32 _hash, address indexed _recipient);

    /**
     * @notice Gets the claim state for a given intent
     * @param intentHash Hash of the intent to query
     * @return Claim state struct containing claimant and status
     */
    function getClaim(
        bytes32 intentHash
    ) external view returns (ClaimState memory);

    /**
     * @notice Gets the funding source for the intent funder
     * @return Address of the native token funding source
     */
    function getFundingSource() external view returns (address);

    /**
     * @notice Gets the override token used for vault refunds
     * @return Address of the vault refund token
     */
    function getRefundToken() external view returns (address);

    /**
     * @notice Calculates the hash components of an intent
     * @param intent Intent to hash
     * @return intentHash Combined hash of route and reward
     * @return routeHash Hash of the route component
     * @return rewardHash Hash of the reward component
     */
    function getIntentHash(
        Intent calldata intent
    )
        external
        pure
        returns (bytes32 intentHash, bytes32 routeHash, bytes32 rewardHash);

    /**
     * @notice Calculates the deterministic address of the intent funder
     * @param intent Intent to calculate vault address for
     * @return Address of the intent funder
     */
    function intentFunderAddress(
        Intent calldata intent
    ) external view returns (address);

    /**
     * @notice Calculates the deterministic vault address for an intent
     * @param intent Intent to calculate vault address for
     * @return Predicted address of the intent vault
     */
    function intentVaultAddress(
        Intent calldata intent
    ) external view returns (address);

    /**
     * @notice Funds an intent with native tokens and ERC20 tokens
     * @dev Allows for permit calls to approve token transfers
     * @param routeHash Hash of the route component
     * @param reward Reward structure containing distribution details
     * @param fundingAddress Address to fund the intent from
     * @param permitCalls Array of permit calls to approve token transfers
     * @param recoverToken Address of the token to recover if sent to the vault
     */
    function fundIntent(
        bytes32 routeHash,
        Reward calldata reward,
        address fundingAddress,
        Call[] calldata permitCalls,
        address recoverToken
    ) external payable;

    /**
     * @notice Creates an intent to execute instructions on a supported chain for rewards
     * @dev Source chain proof must complete before expiry or rewards are unclaimable,
     *      regardless of execution status. Solver manages timing of L1 data posting
     * @param intent The complete intent struct
     * @param fund Whether to transfer rewards to vault during creation
     * @return intentHash Hash of the created intent
     */
    function publishIntent(
        Intent calldata intent,
        bool fund
    ) external payable returns (bytes32 intentHash);

    /**
     * @notice Verifies an intent's rewards are valid
     * @param intent Intent to validate
     * @return True if rewards are valid and funded
     */
    function isIntentFunded(
        Intent calldata intent
    ) external view returns (bool);

    /**
     * @notice Withdraws reward funds for a fulfilled intent
     * @param routeHash Hash of the intent's route
     * @param reward Reward struct containing distribution details
     */
    function withdrawRewards(
        bytes32 routeHash,
        Reward calldata reward
    ) external;

    /**
     * @notice Batch withdraws rewards for multiple intents
     * @param routeHashes Array of route hashes
     * @param rewards Array of reward structs
     */
    function batchWithdraw(
        bytes32[] calldata routeHashes,
        Reward[] calldata rewards
    ) external;

    /**
     * @notice Refunds rewards back to the intent creator
     * @param routeHash Hash of the intent's route
     * @param reward Reward struct containing distribution details
     * @param token Optional token to refund if incorrectly sent to vault
     */
    function refundIntent(
        bytes32 routeHash,
        Reward calldata reward,
        address token
    ) external;
}

File 9 of 14 : IIntentVault.sol
/* -*- c-basic-offset: 4 -*- */
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.26;

/**
 * @title IIntentVault
 * @notice Interface defining errors for the IntentVault contract
 */
interface IIntentVault {
    /**
     * @notice Thrown when attempting to withdraw rewards before the intent has expired
     */
    error IntentNotExpired();

    /**
     * @notice Thrown when trying to use a reward token as a refund token
     */
    error RefundTokenCannotBeRewardToken();

    /**
     * @notice Thrown when the vault has insufficient token balance for reward distribution
     */
    error InsufficientTokenBalance();

    /**
     * @notice Thrown when the vault has insufficient native token balance
     */
    error InsufficientNativeBalance();

    /**
     * @notice Thrown when the native token transfer to the claimant fails
     */
    error NativeRewardTransferFailed();
}

File 10 of 14 : IProver.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.26;

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

/**
 * @title IProver
 * @notice Interface for proving intent fulfillment
 * @dev Defines required functionality for proving intent execution with different
 * proof mechanisms (storage or Hyperlane)
 */
interface IProver is ISemver {
    /**
     * @notice Types of proofs that can validate intent fulfillment
     * @param Storage Traditional storage-based proof mechanism
     * @param Hyperlane Proof using Hyperlane's cross-chain messaging
     */
    enum ProofType {
        Storage,
        Hyperlane,
        Metalayer
    }

    /**
     * @notice Emitted when an intent is successfully proven
     * @param _hash Hash of the proven intent
     * @param _claimant Address eligible to claim the intent's rewards
     */
    event IntentProven(bytes32 indexed _hash, address indexed _claimant);

    /**
     * @notice Gets the proof mechanism type used by this prover
     * @return ProofType enum indicating the prover's mechanism
     */
    function getProofType() external pure returns (ProofType);

    /**
     * @notice Gets the address eligible to claim rewards for a proven intent
     * @param intentHash Hash of the intent to query
     * @return Address of the claimant, or zero address if unproven
     */
    function getIntentClaimant(
        bytes32 intentHash
    ) external view returns (address);
}

File 11 of 14 : ISemver.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.26;

/**
 * @title Semver Interface
 * @dev An interface for a contract that has a version
 */
interface ISemver {
    function version() external pure returns (string memory);
}

File 12 of 14 : Semver.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.26;

import {ISemver} from "../interfaces/ISemver.sol";

abstract contract Semver is ISemver {
    function version() external pure returns (string memory) {
        return "1.8.14-e2c12e7";
    }
}

File 13 of 14 : BaseProver.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;

import {IProver} from "../interfaces/IProver.sol";

/**
 * @title BaseProver
 * @notice Base implementation for intent proving contracts
 * @dev Provides core storage and functionality for tracking proven intents
 * and their claimants
 */
abstract contract BaseProver is IProver {
    /**
     * @notice Mapping from intent hash to address eligible to claim rewards
     * @dev Zero address indicates intent hasn't been proven
     */
    mapping(bytes32 => address) public provenIntents;

    /**
     * @notice Gets the address eligible to claim rewards for a given intent
     * @param intentHash Hash of the intent to query
     * @return Address of the claimant, or zero address if unproven
     */
    function getIntentClaimant(
        bytes32 intentHash
    ) external view override returns (address) {
        return provenIntents[intentHash];
    }
}

File 14 of 14 : Intent.sol
/* -*- c-basic-offset: 4 -*- */
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.26;

/**
 * @notice Represents a single contract call with encoded function data
 * @dev Used to execute arbitrary function calls on the destination chain
 * @param target The contract address to call
 * @param data ABI-encoded function call data
 * @param value Amount of native tokens to send with the call
 */
struct Call {
    address target;
    bytes data;
    uint256 value;
}

/**
 * @notice Represents a token amount pair
 * @dev Used to specify token rewards and transfers
 * @param token Address of the ERC20 token contract
 * @param amount Amount of tokens in the token's smallest unit
 */
struct TokenAmount {
    address token;
    uint256 amount;
}

/**
 * @notice Defines the routing and execution instructions for cross-chain messages
 * @dev Contains all necessary information to route and execute a message on the destination chain
 * @param salt Unique identifier provided by the intent creator, used to prevent duplicates
 * @param source Chain ID where the intent originated
 * @param destination Target chain ID where the calls should be executed
 * @param inbox Address of the inbox contract on the destination chain that receives messages
 * @param tokens Array of tokens required for execution of calls on destination chain
 * @param calls Array of contract calls to execute on the destination chain in sequence
 */
struct Route {
    bytes32 salt;
    uint256 source;
    uint256 destination;
    address inbox;
    TokenAmount[] tokens;
    Call[] calls;
}

/**
 * @notice Defines the reward and validation parameters for cross-chain execution
 * @dev Specifies who can execute the intent and what rewards they receive
 * @param creator Address that created the intent and has authority to modify/cancel
 * @param prover Address of the prover contract that must approve execution
 * @param deadline Timestamp after which the intent can no longer be executed
 * @param nativeValue Amount of native tokens offered as reward
 * @param tokens Array of ERC20 tokens and amounts offered as additional rewards
 */
struct Reward {
    address creator;
    address prover;
    uint256 deadline;
    uint256 nativeValue;
    TokenAmount[] tokens;
}

/**
 * @notice Complete cross-chain intent combining routing and reward information
 * @dev Main structure used to process and execute cross-chain messages
 * @param route Routing and execution instructions
 * @param reward Reward and validation parameters
 */
struct Intent {
    Route route;
    Reward reward;
}

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

Contract ABI

API
[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"address","name":"target","type":"address"}],"name":"AddressEmptyCode","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"AddressInsufficientBalance","type":"error"},{"inputs":[],"name":"ArrayLengthMismatch","type":"error"},{"inputs":[{"internalType":"bytes32","name":"_hash","type":"bytes32"}],"name":"BadClaimant","type":"error"},{"inputs":[],"name":"CalldataMismatch","type":"error"},{"inputs":[],"name":"FailedInnerCall","type":"error"},{"inputs":[],"name":"InsufficientNativeReward","type":"error"},{"inputs":[{"internalType":"bytes32","name":"intentHash","type":"bytes32"}],"name":"IntentAlreadyExists","type":"error"},{"inputs":[],"name":"IntentAlreadyFunded","type":"error"},{"inputs":[],"name":"InvalidIntent","type":"error"},{"inputs":[],"name":"NativeRewardTransferFailed","type":"error"},{"inputs":[],"name":"PermitCallFailed","type":"error"},{"inputs":[{"internalType":"bytes32","name":"_hash","type":"bytes32"}],"name":"RewardsAlreadyWithdrawn","type":"error"},{"inputs":[],"name":"RewardsMismatch","type":"error"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"SafeERC20FailedOperation","type":"error"},{"inputs":[{"internalType":"address","name":"_token","type":"address"},{"internalType":"address","name":"_to","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"TransferFailed","type":"error"},{"inputs":[{"internalType":"bytes32","name":"_hash","type":"bytes32"}],"name":"UnauthorizedWithdrawal","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"hash","type":"bytes32"},{"indexed":false,"internalType":"bytes32","name":"salt","type":"bytes32"},{"indexed":false,"internalType":"uint256","name":"source","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"destination","type":"uint256"},{"indexed":false,"internalType":"address","name":"inbox","type":"address"},{"components":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"indexed":false,"internalType":"struct TokenAmount[]","name":"routeTokens","type":"tuple[]"},{"components":[{"internalType":"address","name":"target","type":"address"},{"internalType":"bytes","name":"data","type":"bytes"},{"internalType":"uint256","name":"value","type":"uint256"}],"indexed":false,"internalType":"struct Call[]","name":"calls","type":"tuple[]"},{"indexed":true,"internalType":"address","name":"creator","type":"address"},{"indexed":true,"internalType":"address","name":"prover","type":"address"},{"indexed":false,"internalType":"uint256","name":"deadline","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"nativeValue","type":"uint256"},{"components":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"indexed":false,"internalType":"struct TokenAmount[]","name":"rewardTokens","type":"tuple[]"}],"name":"IntentCreated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bytes32","name":"intentHash","type":"bytes32"},{"indexed":false,"internalType":"address","name":"fundingSource","type":"address"}],"name":"IntentFunded","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bytes32","name":"_hash","type":"bytes32"},{"indexed":true,"internalType":"address","name":"_recipient","type":"address"}],"name":"Refund","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bytes32","name":"_hash","type":"bytes32"},{"indexed":true,"internalType":"address","name":"_recipient","type":"address"}],"name":"Withdrawal","type":"event"},{"inputs":[{"internalType":"bytes32[]","name":"routeHashes","type":"bytes32[]"},{"components":[{"internalType":"address","name":"creator","type":"address"},{"internalType":"address","name":"prover","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"uint256","name":"nativeValue","type":"uint256"},{"components":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"internalType":"struct TokenAmount[]","name":"tokens","type":"tuple[]"}],"internalType":"struct Reward[]","name":"rewards","type":"tuple[]"}],"name":"batchWithdraw","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"intentHash","type":"bytes32"}],"name":"claims","outputs":[{"internalType":"address","name":"claimant","type":"address"},{"internalType":"uint8","name":"status","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"routeHash","type":"bytes32"},{"components":[{"internalType":"address","name":"creator","type":"address"},{"internalType":"address","name":"prover","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"uint256","name":"nativeValue","type":"uint256"},{"components":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"internalType":"struct TokenAmount[]","name":"tokens","type":"tuple[]"}],"internalType":"struct Reward","name":"reward","type":"tuple"},{"internalType":"address","name":"fundingAddress","type":"address"},{"components":[{"internalType":"address","name":"target","type":"address"},{"internalType":"bytes","name":"data","type":"bytes"},{"internalType":"uint256","name":"value","type":"uint256"}],"internalType":"struct Call[]","name":"permitCalls","type":"tuple[]"},{"internalType":"address","name":"recoverToken","type":"address"}],"name":"fundIntent","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"fundingSource","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"intentHash","type":"bytes32"}],"name":"getClaim","outputs":[{"components":[{"internalType":"address","name":"claimant","type":"address"},{"internalType":"uint8","name":"status","type":"uint8"}],"internalType":"struct IIntentSource.ClaimState","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getFundingSource","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"components":[{"internalType":"bytes32","name":"salt","type":"bytes32"},{"internalType":"uint256","name":"source","type":"uint256"},{"internalType":"uint256","name":"destination","type":"uint256"},{"internalType":"address","name":"inbox","type":"address"},{"components":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"internalType":"struct TokenAmount[]","name":"tokens","type":"tuple[]"},{"components":[{"internalType":"address","name":"target","type":"address"},{"internalType":"bytes","name":"data","type":"bytes"},{"internalType":"uint256","name":"value","type":"uint256"}],"internalType":"struct Call[]","name":"calls","type":"tuple[]"}],"internalType":"struct Route","name":"route","type":"tuple"},{"components":[{"internalType":"address","name":"creator","type":"address"},{"internalType":"address","name":"prover","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"uint256","name":"nativeValue","type":"uint256"},{"components":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"internalType":"struct TokenAmount[]","name":"tokens","type":"tuple[]"}],"internalType":"struct Reward","name":"reward","type":"tuple"}],"internalType":"struct Intent","name":"intent","type":"tuple"}],"name":"getIntentHash","outputs":[{"internalType":"bytes32","name":"intentHash","type":"bytes32"},{"internalType":"bytes32","name":"routeHash","type":"bytes32"},{"internalType":"bytes32","name":"rewardHash","type":"bytes32"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"getRefundToken","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"components":[{"internalType":"bytes32","name":"salt","type":"bytes32"},{"internalType":"uint256","name":"source","type":"uint256"},{"internalType":"uint256","name":"destination","type":"uint256"},{"internalType":"address","name":"inbox","type":"address"},{"components":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"internalType":"struct TokenAmount[]","name":"tokens","type":"tuple[]"},{"components":[{"internalType":"address","name":"target","type":"address"},{"internalType":"bytes","name":"data","type":"bytes"},{"internalType":"uint256","name":"value","type":"uint256"}],"internalType":"struct Call[]","name":"calls","type":"tuple[]"}],"internalType":"struct Route","name":"route","type":"tuple"},{"components":[{"internalType":"address","name":"creator","type":"address"},{"internalType":"address","name":"prover","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"uint256","name":"nativeValue","type":"uint256"},{"components":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"internalType":"struct TokenAmount[]","name":"tokens","type":"tuple[]"}],"internalType":"struct Reward","name":"reward","type":"tuple"}],"internalType":"struct Intent","name":"intent","type":"tuple"}],"name":"intentFunderAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"components":[{"internalType":"bytes32","name":"salt","type":"bytes32"},{"internalType":"uint256","name":"source","type":"uint256"},{"internalType":"uint256","name":"destination","type":"uint256"},{"internalType":"address","name":"inbox","type":"address"},{"components":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"internalType":"struct TokenAmount[]","name":"tokens","type":"tuple[]"},{"components":[{"internalType":"address","name":"target","type":"address"},{"internalType":"bytes","name":"data","type":"bytes"},{"internalType":"uint256","name":"value","type":"uint256"}],"internalType":"struct Call[]","name":"calls","type":"tuple[]"}],"internalType":"struct Route","name":"route","type":"tuple"},{"components":[{"internalType":"address","name":"creator","type":"address"},{"internalType":"address","name":"prover","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"uint256","name":"nativeValue","type":"uint256"},{"components":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"internalType":"struct TokenAmount[]","name":"tokens","type":"tuple[]"}],"internalType":"struct Reward","name":"reward","type":"tuple"}],"internalType":"struct Intent","name":"intent","type":"tuple"}],"name":"intentVaultAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"components":[{"internalType":"bytes32","name":"salt","type":"bytes32"},{"internalType":"uint256","name":"source","type":"uint256"},{"internalType":"uint256","name":"destination","type":"uint256"},{"internalType":"address","name":"inbox","type":"address"},{"components":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"internalType":"struct TokenAmount[]","name":"tokens","type":"tuple[]"},{"components":[{"internalType":"address","name":"target","type":"address"},{"internalType":"bytes","name":"data","type":"bytes"},{"internalType":"uint256","name":"value","type":"uint256"}],"internalType":"struct Call[]","name":"calls","type":"tuple[]"}],"internalType":"struct Route","name":"route","type":"tuple"},{"components":[{"internalType":"address","name":"creator","type":"address"},{"internalType":"address","name":"prover","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"uint256","name":"nativeValue","type":"uint256"},{"components":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"internalType":"struct TokenAmount[]","name":"tokens","type":"tuple[]"}],"internalType":"struct Reward","name":"reward","type":"tuple"}],"internalType":"struct Intent","name":"intent","type":"tuple"}],"name":"isIntentFunded","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"components":[{"internalType":"bytes32","name":"salt","type":"bytes32"},{"internalType":"uint256","name":"source","type":"uint256"},{"internalType":"uint256","name":"destination","type":"uint256"},{"internalType":"address","name":"inbox","type":"address"},{"components":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"internalType":"struct TokenAmount[]","name":"tokens","type":"tuple[]"},{"components":[{"internalType":"address","name":"target","type":"address"},{"internalType":"bytes","name":"data","type":"bytes"},{"internalType":"uint256","name":"value","type":"uint256"}],"internalType":"struct Call[]","name":"calls","type":"tuple[]"}],"internalType":"struct Route","name":"route","type":"tuple"},{"components":[{"internalType":"address","name":"creator","type":"address"},{"internalType":"address","name":"prover","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"uint256","name":"nativeValue","type":"uint256"},{"components":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"internalType":"struct TokenAmount[]","name":"tokens","type":"tuple[]"}],"internalType":"struct Reward","name":"reward","type":"tuple"}],"internalType":"struct Intent","name":"intent","type":"tuple"},{"internalType":"bool","name":"fund","type":"bool"}],"name":"publishIntent","outputs":[{"internalType":"bytes32","name":"intentHash","type":"bytes32"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"routeHash","type":"bytes32"},{"components":[{"internalType":"address","name":"creator","type":"address"},{"internalType":"address","name":"prover","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"uint256","name":"nativeValue","type":"uint256"},{"components":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"internalType":"struct TokenAmount[]","name":"tokens","type":"tuple[]"}],"internalType":"struct Reward","name":"reward","type":"tuple"},{"internalType":"address","name":"token","type":"address"}],"name":"refundIntent","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"refundToken","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"version","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes32","name":"routeHash","type":"bytes32"},{"components":[{"internalType":"address","name":"creator","type":"address"},{"internalType":"address","name":"prover","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"uint256","name":"nativeValue","type":"uint256"},{"components":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"internalType":"struct TokenAmount[]","name":"tokens","type":"tuple[]"}],"internalType":"struct Reward","name":"reward","type":"tuple"}],"name":"withdrawRewards","outputs":[],"stateMutability":"nonpayable","type":"function"}]

Deployed Bytecode

0x608080604052600436101561001357600080fd5b60003560e01c90816341d407c614610b98575080634f1c807014610b5957806354fd4d5014610af25780635cb732be146100ff578063695a461c14610a3757806393a359e9146109fb57806396c5c272146109bd5780639fc9c91b14610872578063a2f92bea1461089b578063a9fe5c3014610872578063c2fa34d614610502578063c9100bcb14610496578063cd24594214610166578063eff0f59214610128578063f6a72970146100ff5763fb4035f6146100cf57600080fd5b346100fa5760606100e76100e236610d82565b611299565b9060405192835260208301526040820152f35b600080fd5b346100fa5760003660031901126100fa576002546040516001600160a01b039091168152602090f35b346100fa5760203660031901126100fa5760043560005260006020526040806000205460ff82519160018060a01b038116835260a01c166020820152f35b60a03660031901126100fa576024356004356001600160401b0382116100fa57816004019060a060031984360301126100fa576101a1610d05565b6064356001600160401b0381116100fa576101c0903690600401610d52565b9290936084359260018060a01b0384168094036100fa576064604051602081019060208252610204816101f66040820188610e33565b03601f198101835282610eb7565b5190206040516020810191868352604082015260408152610226606082610eb7565b519020977f2da42efda5225344c30e729dc0eafc2e56292ac9b9b5c2b16e0e74c86ea5921d604061025886888d6113ad565b81519b8c526001600160a01b0390951660208c018190529aa1013581319060008282039212818312811691831390151617610480576000811380610477575b610403575b50939536869003605e1901949060005b888110156103495760008160051b890135888112156103455789016102d081610ed8565b90602081013590601e198136030182121561034157018035906001600160401b03821161034157602001813603811361034157918184809481946040519384928337810182815203925af1610323611212565b501561033257506001016102ac565b630610514560e01b8152600490fd5b8380fd5b5080fd5b506001600160601b0360a01b600154161760015583151593846103ea575b506040519161063f90818401928484106001600160401b038511176103d4578493610396936115a18639611277565b03906000f5156103c857600180546001600160a01b03191690556103b657005b600280546001600160a01b0319169055005b6040513d6000823e3d90fd5b634e487b7160e01b600052604160045260246000fd5b6001600160601b0360a01b600254161760025584610367565b80341160001461046b576000808080935b818115610462575b6001600160a01b03871690f1156103c85747801561029c57600080808093335af1610445611212565b5015610451578761029c565b63142a8ca560e01b60005260046000fd5b506108fc61041c565b50600080808034610414565b50341515610297565b634e487b7160e01b600052601160045260246000fd5b346100fa5760203660031901126100fa57600060206040516104b781610e9c565b8281520152600435600052600060205260408060002060ff8251916104db83610e9c565b5481602060018060a01b03831694858152019160a01c168152835192835251166020820152f35b60403660031901126100fa576004356001600160401b0381116100fa5780600401604060031983360301126100fa576024359081151582036100fa57610555602461054d83806110f0565b9401826110db565b92608084016105648186611105565b92905061057084611299565b5081600097929752600060205260ff60406000205460a01c1661085d5761059960608401610ed8565b6105a66080850185611105565b919060a0860135601e19873603018112156100fa5786018035906001600160401b0382116100fa57602001928160051b360384136100fa5788978d958d936105ed88610ed8565b966105fa60208a01610ed8565b9160608a01359c61060b908b611105565b98909460405197878998358952602081013560208a0152604001356040890152600160a01b6001900316606088015260808701610120905261012087019061065292610de9565b9085820360a08701526106649261113a565b604089013560c085015260e084018c90528381036101008501526001600160a01b03918216979091169561069792610de9565b037fd802f2610d0c85b3f19be4413f3cf49de1d4e787edecd538274437a5b9aa648d91a46106c591886113ad565b80958261084b575b50506106df575b602085604051908152f35b801580156107e1575b5091926001600160a01b0316915060005b838110156106d4578561078d60008060206107448661073e8982610737610732610723848d611105565b6001600160a01b039491611251565b610ed8565b1698611105565b90611251565b013560405160208101916323b872dd60e01b8352336024830152896044830152606482015260648152610778608482610eb7565b519082865af1610786611212565b908361153f565b80519081151591826107bd575b50506107a957506001016106f9565b635274afe760e01b60005260045260246000fd5b81925090602091810103126100fa57602001518015908115036100fa57888061079a565b81341061083a57600080809381938290610831575b6001600160a01b03891690f1156103c8574780610814575b806106e8565b600080808093335af1610825611212565b5015610451578561080e565b506108fc6107f6565b634d3f142160e11b60005260046000fd5b610855925061144b565b1584886106cd565b86635eaf4c6960e01b60005260045260246000fd5b346100fa5760003660031901126100fa576001546040516001600160a01b039091168152602090f35b346100fa5760206109996108e46109a76108b436610d82565b6109426109526108c383611299565b5095906108de89860191886108d884896110db565b916113ad565b946110db565b8761063f946101f661091b604051946108ff858a0187610eb7565b888652848601986115a18a396040519283918683019586611277565b604051958694610933858701998a9251928391610d2f565b85019151809385840190610d2f565b010103601f198101835282610eb7565b5190206040516001600160f81b03198682019081523060601b6bffffffffffffffffffffffff19166001820152601581019490945260358401919091529283906055840190565b03601f198101845283610eb7565b905190206040516001600160a01b039091168152f35b346100fa5760206109e96109d036610d82565b6108d86109dc82611299565b50919092858101906110db565b6040516001600160a01b039091168152f35b346100fa576020610a2d610a0e36610d82565b610a27610a1a82611299565b506108d8868501856110db565b9061144b565b6040519015158152f35b346100fa5760403660031901126100fa576004356001600160401b0381116100fa57610a67903690600401610d52565b6024356001600160401b0381116100fa57610a86903690600401610d52565b90818303610ae15760005b83811015610adf5760008160051b9084831015610acb575090610ac582610abc6001948601866110db565b90880135610f03565b01610a91565b634e487b7160e01b81526032600452602490fd5b005b63512509d360e11b60005260046000fd5b346100fa5760003660031901126100fa57610b4b60408051610b148282610eb7565b600e8152602081016d312e382e31342d6532633132653760901b815282519384926020845251809281602086015285850190610d2f565b601f01601f19168101030190f35b346100fa5760403660031901126100fa576024356001600160401b0381116100fa5760a060031982360301126100fa57610adf90600401600435610f03565b346100fa5760603660031901126100fa57602435906004356001600160401b0383116100fa5760a0836004019360031990360301126100fa57610bd9610d05565b91602081019060208252610bf4816101f66040820188610e33565b5190206040516020810191838352604082015260408152610c16606082610eb7565b5190206001600160a01b03909216801515939084610cec575b506001600160a01b03610c4182610ed8565b167f0ba6f12b978882904e7444c7a8fcadd2d9f692a6a97aa18e5fb44c3bbc5801236020604051868152a26040519061072290818301908382106001600160401b038311176103d45785610c9b928594611be08639610eec565b03906000f5156103c85780600052600060205260ff60406000205460a01c1615610cc7575b506103b657005b6000908152602081905260409020805460ff60a01b1916600160a11b17905581610cc0565b6001600160601b0360a01b600254161760025584610c2f565b604435906001600160a01b03821682036100fa57565b35906001600160a01b03821682036100fa57565b60005b838110610d425750506000910152565b8181015183820152602001610d32565b9181601f840112156100fa578235916001600160401b0383116100fa576020808501948460051b0101116100fa57565b60206003198201126100fa57600435906001600160401b0382116100fa5760409082900360031901126100fa5760040190565b9035601e19823603018112156100fa5701602081359101916001600160401b0382116100fa578160061b360383136100fa57565b9160209082815201919060005b818110610e035750505090565b909192604080600192838060a01b03610e1b88610d1b565b16815260208781013590820152019401929101610df6565b610e99919060a090610e89906001600160a01b03610e5082610d1b565b168452600180841b03610e6560208301610d1b565b16602085015260408101356040850152606081013560608501526080810190610db5565b9190928160808201520191610de9565b90565b604081019081106001600160401b038211176103d457604052565b90601f801991011681019081106001600160401b038211176103d457604052565b356001600160a01b03811681036100fa5790565b604090610e99939281528160208201520190610e33565b91604051602081019060208252610f21816101f66040820187610e33565b5190206040516020810191858352604082015260408152610f43606082610eb7565b51902092602460206001600160a01b03610f5e868301610ed8565b1660405192838092634ce8a2d960e11b82528960048301525afa9081156103c857600091611096575b506001600160a01b03168015939084158061107a575b610fd057858515610fbc5763dac420a360e01b60005260045260246000fd5b63313d14a960e11b60005260045260246000fd5b9091929480945060005260006020526040600020816001600160601b0360a01b8254161790557f6653a45d3871e4110fa55dac0269f9f93a6d9078d402f7153594e50573d7f0cd6020604051868152a26040519061072290818301908382106001600160401b038311176103d4578561104f928594611be08639610eec565b03906000f5156103c8576000908152602081905260409020805460ff60a01b1916600160a01b179055565b5085600052600060205260ff60406000205460a01c1615610f9d565b6020813d6020116110d3575b816110af60209383610eb7565b810103126103455751906001600160a01b03821682036110d0575038610f87565b80fd5b3d91506110a2565b903590609e19813603018212156100fa570190565b90359060be19813603018212156100fa570190565b903590601e19813603018212156100fa57018035906001600160401b0382116100fa57602001918160061b360383136100fa57565b906020838281520160208260051b85010193836000915b8483106111615750505050505090565b909192939495601f198282030185526000873590605e19853603018212156110d0575083016001600160a01b0361119782610d1b565b1682526020810135601e19823603018112156100fa57810190602082359201906001600160401b0383116100fa5782360382136100fa5783836040608093602096879660608860019b015281606087015286860137600084840186015201356040830152601f01601f19160101980196950193019190611151565b3d1561124c573d906001600160401b0382116103d45760405191611240601f8201601f191660200184610eb7565b82523d6000602084013e565b606090565b91908110156112615760061b0190565b634e487b7160e01b600052603260045260246000fd5b6001600160a01b039091168152604060208201819052610e9992910190610e33565b6112a381806110f0565b60405160208101906020825282356040820152602083013560608201526040830135608082015260018060a01b036112dd60608501610d1b565b1660a08201526113046112f36080850185610db5565b60c080850152610100840191610de9565b9260a0810135601e19823603018112156100fa570192602084359401936001600160401b0381116100fa578060051b360385136100fa576101f661135992849261136797603f198584030160e086015261113a565b5190209160208101906110db565b604051611384816101f66020820194602086526040830190610e33565b5190206040516020810190838252826040820152604081526113a7606082610eb7565b51902092565b61143b906109426113f3610999956020610722946101f661091b604051946113d7858a0187610eb7565b88865284860198611be08a396040519283918683019586610eec565b5190206040516001600160f81b0319602082019081523060601b6bffffffffffffffffffffffff19166021830152603582019490945260558101919091529283906075820190565b905190206001600160a01b031690565b6114599060208101906110db565b9060808201916114698382611105565b93905082316060830135116115365760005b84811061148c575050505050600190565b61149d6107328261073e8587611105565b60246020806114b08561073e888a611105565b6040516370a0823160e01b81526001600160a01b038a811660048301529290910135949093849290918391165afa9081156103c857600091611505575b50106114fb5760010161147b565b5050505050600090565b906020823d821161152e575b8161151e60209383610eb7565b810103126110d0575051386114ed565b3d9150611511565b50505050600090565b90611565575080511561155457805190602001fd5b630a12f52160e11b60005260046000fd5b81511580611597575b611576575090565b639996b31560e01b60009081526001600160a01b0391909116600452602490fd5b50803b1561156e56fe608080604052346103e25761063f803803809161001c82856104c6565b833981016040828203126103e257610033826104e9565b602083015190926001600160401b0382116103e257019160a0838303126103e2576040519260a084016001600160401b038111858210176104b05760405261007a816104e9565b8452610088602082016104e9565b602085015260408181015190850152606080820151908501526080810151906001600160401b0382116103e2570182601f820112156103e2578051906001600160401b0382116104b057604051936100e660208460051b01866104c6565b82855260208086019360061b830101918183116103e257602001925b8284106104655750505050608083018281529151604051639fc9c91b60e01b815290602082600481335afa91821561030957600092610429575b50604051630f6a729760e41b8152602081600481335afa908115610309576000916103ef575b506001600160a01b031680610348575b50916001600160a01b031660005b8381106101945785516001600160a01b0316ff5b84516001600160a01b03906101aa9083906104fd565b5151169060206101bb8288516104fd565b510151604051636eb1769f60e11b81526001600160a01b038616600482018190523060248301529193909190602083604481845afa92831561030957600093610315575b506040516370a0823160e01b81526004810187905294602086602481855afa958615610309576000966102d3575b50600086820396128187128116918713901516176102bd5784600060019613806102b4575b610261575b5050505001610180565b6102a493818111156102ad57505b604051926323b872dd60e01b6020850152602484015286604484015260648301526064825261029f6084836104c6565b610527565b38808080610257565b905061026f565b50831515610252565b634e487b7160e01b600052601160045260246000fd5b90956020823d8211610301575b816102ed602093836104c6565b810103126102fe575051943861022d565b80fd5b3d91506102e0565b6040513d6000823e3d90fd5b90926020823d8211610340575b8161032f602093836104c6565b810103126102fe57505191386101ff565b3d9150610322565b85516040516370a0823160e01b8152306004820152916001600160a01b0390911690602083602481845afa8015610309576000906103b6575b6103b093506040519263a9059cbb60e01b6020850152602484015260448301526044825261029f6064836104c6565b38610172565b506020833d6020116103e7575b816103d0602093836104c6565b810103126103e2576103b09251610381565b600080fd5b3d91506103c3565b90506020813d602011610421575b8161040a602093836104c6565b810103126103e25761041b906104e9565b38610162565b3d91506103fd565b9091506020813d60201161045d575b81610445602093836104c6565b810103126103e257610456906104e9565b903861013c565b3d9150610438565b6040848303126103e2576040805191908201906001600160401b038211838310176104b057604092602092845261049b876104e9565b81528287015183820152815201930192610102565b634e487b7160e01b600052604160045260246000fd5b601f909101601f19168101906001600160401b038211908210176104b057604052565b51906001600160a01b03821682036103e257565b80518210156105115760209160051b010190565b634e487b7160e01b600052603260045260246000fd5b81516001600160a01b0390911691600091829160200182855af13d156105d1573d6001600160401b0381116104b05761058391604051916105726020601f19601f84011601846104c6565b82523d6000602084013e5b836105dd565b80519081151591826105ad575b50506105995750565b635274afe760e01b60005260045260246000fd5b81925090602091810103126103e257602001518015908115036103e2573880610590565b6105839060609061057d565b9061060357508051156105f257805190602001fd5b630a12f52160e11b60005260046000fd5b81511580610635575b610614575090565b639996b31560e01b60009081526001600160a01b0391909116600452602490fd5b50803b1561060c56fe608080604052346104a357610722803803809161001c8285610578565b83398101906040818303126104a3578051602082015190916001600160401b0382116104a357019160a0838203126104a3576040519260a084016001600160401b03811185821017610547576040526100748161059b565b84526100826020820161059b565b6020850152604081015191604085019283526060820151916060860192835260808101519060018060401b0382116104a3570181601f820112156104a3578051906001600160401b03821161054757604051926100e560208460051b0185610578565b82845260208085019360061b830101918183116104a357602001925b82841061050f5750505050608085019080825251906040519463c9100bcb60e01b86526004860152604085602481335afa94851561026e576000956104b0575b508451604051630f6a729760e41b8152956001600160a01b039091169490602087600481335afa96871561026e5760009761046f575b508515918280610465575b6104545782610448575b508115610437575b50610425575b60005b8281106102ce57505084516001600160a01b0393841693168314159050806102c4575b61027a575b50506001600160a01b0316806101e3575b50516001600160a01b0316ff5b6040516370a0823160e01b8152306004820152602081602481855afa90811561026e57600091610239575b508061021b575b506101d6565b8251610232926001600160a01b0390911690610618565b3880610215565b906020823d602011610266575b8161025360209383610578565b810103126102635750513861020e565b80fd5b3d9150610246565b6040513d6000823e3d90fd5b478151116102b3576000808093819351905af16102956105d9565b50156102a25738806101c5565b63142a8ca560e01b60005260046000fd5b63dbc3a71f60e01b60005260046000fd5b50805115156101c0565b81516001600160a01b03906102e49083906105af565b5151169060206102f58285516105af565b510151916040516370a0823160e01b8152306004820152602081602481855afa90811561026e576000916103f4575b506001600160a01b03891682146103e35789516001600160a01b0389811691160361036f5760019350878161035e575b5050505b0161019d565b61036792610618565b388087610354565b8084106103d257610381848984610618565b838111610394575b505060019150610358565b89516001600160a01b03169381039081116103bc576001936103b592610618565b3880610389565b634e487b7160e01b600052601160045260246000fd5b637222ae5760e11b60005260046000fd5b631624b98f60e31b60005260046000fd5b906020823d821161041d575b8161040d60209383610578565b8101031261026357505138610324565b3d9150610400565b85516001600160a01b0316935061019a565b60ff91506020015116151538610194565b5142101591503861018c565b6309ffb8fd60e31b60005260046000fd5b5080514210610182565b9096506020813d6020116104a8575b8161048b60209383610578565b810103126104a35761049c9061059b565b9538610177565b600080fd5b3d915061047e565b90946040823d604011610507575b816104cb60409383610578565b81010312610263576020604051926104e28461055d565b6104eb8161059b565b845201519060ff82168203610263575060208201529338610141565b3d91506104be565b6040848303126104a357602060409182516105298161055d565b6105328761059b565b81528287015183820152815201930192610101565b634e487b7160e01b600052604160045260246000fd5b604081019081106001600160401b0382111761054757604052565b601f909101601f19168101906001600160401b0382119082101761054757604052565b51906001600160a01b03821682036104a357565b80518210156105c35760209160051b010190565b634e487b7160e01b600052603260045260246000fd5b3d15610613573d906001600160401b0382116105475760405191610607601f8201601f191660200184610578565b82523d6000602084013e565b606090565b600061067292819260405195602087019263a9059cbb60e01b845260018060a01b03166024880152604487015260448652610654606487610578565b60018060a01b031694519082865af161066b6105d9565b90836106c0565b805190811515918261069c575b50506106885750565b635274afe760e01b60005260045260246000fd5b81925090602091810103126104a357602001518015908115036104a357388061067f565b906106e657508051156106d557805190602001fd5b630a12f52160e11b60005260046000fd5b81511580610718575b6106f7575090565b639996b31560e01b60009081526001600160a01b0391909116600452602490fd5b50803b156106ef56fea26469706673582212206f86a4dc038c589eb2cde47e041c59bb1e0be316bf8b784530da8f9da0d263d564736f6c634300081a0033

Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

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

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits
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.