ETH Price: $3,193.04 (+1.77%)

Contract

0x4cb69FaE7e7Af841e44E1A1c30Af640739378bb2
 
Transaction Hash
Method
Block
From
To
Value
Transfer Tokens ...1191061482024-04-22 18:37:5341 mins ago1713811073IN
0x4cb69FaE...739378bb2
0 ETH0.000021980.0847556
Transfer Tokens ...1191046232024-04-22 17:47:031 hr ago1713808023IN
0x4cb69FaE...739378bb2
0 ETH0.000015980.0616185
Transfer Tokens ...1191042452024-04-22 17:34:271 hr ago1713807267IN
0x4cb69FaE...739378bb2
0 ETH0.000016020.06174891
Transfer Tokens ...1191041652024-04-22 17:31:471 hr ago1713807107IN
0x4cb69FaE...739378bb2
0 ETH0.000018210.0702064
Transfer Tokens ...1191019872024-04-22 16:19:113 hrs ago1713802751IN
0x4cb69FaE...739378bb2
0 ETH0.00001590.06128739
Transfer Tokens ...1191012672024-04-22 15:55:113 hrs ago1713801311IN
0x4cb69FaE...739378bb2
0 ETH0.000015720.06060256
Transfer Tokens ...1191011762024-04-22 15:52:093 hrs ago1713801129IN
0x4cb69FaE...739378bb2
0 ETH0.000016190.06241558
Transfer Tokens ...1191010392024-04-22 15:47:353 hrs ago1713800855IN
0x4cb69FaE...739378bb2
0 ETH0.000015780.06084762
Transfer Tokens ...1191007702024-04-22 15:38:373 hrs ago1713800317IN
0x4cb69FaE...739378bb2
0 ETH0.000015610.06019694
Transfer Tokens ...1191000522024-04-22 15:14:414 hrs ago1713798881IN
0x4cb69FaE...739378bb2
0 ETH0.000016010.0617338
Transfer Tokens ...1190999782024-04-22 15:12:134 hrs ago1713798733IN
0x4cb69FaE...739378bb2
0 ETH0.000016960.0653921
Transfer Tokens ...1190991032024-04-22 14:43:034 hrs ago1713796983IN
0x4cb69FaE...739378bb2
0 ETH0.000015740.06067611
Transfer Tokens ...1190987052024-04-22 14:29:474 hrs ago1713796187IN
0x4cb69FaE...739378bb2
0 ETH0.00001610.06207588
Transfer Tokens ...1190981432024-04-22 14:11:035 hrs ago1713795063IN
0x4cb69FaE...739378bb2
0 ETH0.000015750.06072921
Redeem Tokens1190973092024-04-22 13:43:155 hrs ago1713793395IN
0x4cb69FaE...739378bb2
0 ETH0.000025310.06061867
Transfer Tokens ...1190963902024-04-22 13:12:376 hrs ago1713791557IN
0x4cb69FaE...739378bb2
0 ETH0.000006040.02329729
Transfer Tokens ...1190961582024-04-22 13:04:536 hrs ago1713791093IN
0x4cb69FaE...739378bb2
0 ETH0.000005090.01962036
Transfer Tokens ...1190955242024-04-22 12:43:456 hrs ago1713789825IN
0x4cb69FaE...739378bb2
0 ETH0.000002780.01073936
Transfer Tokens ...1190944842024-04-22 12:09:057 hrs ago1713787745IN
0x4cb69FaE...739378bb2
0 ETH0.000002050.00793647
Transfer Tokens ...1190939422024-04-22 11:51:017 hrs ago1713786661IN
0x4cb69FaE...739378bb2
0 ETH0.000002090.0080556
Transfer Tokens ...1190939202024-04-22 11:50:177 hrs ago1713786617IN
0x4cb69FaE...739378bb2
0 ETH0.000002120.0081844
Transfer Tokens ...1190937642024-04-22 11:45:057 hrs ago1713786305IN
0x4cb69FaE...739378bb2
0 ETH0.000001750.00675558
Transfer Tokens ...1190930662024-04-22 11:21:497 hrs ago1713784909IN
0x4cb69FaE...739378bb2
0 ETH0.000001770.0068606
Transfer Tokens ...1190912892024-04-22 10:22:358 hrs ago1713781355IN
0x4cb69FaE...739378bb2
0 ETH0.000003870.014937
Transfer Tokens ...1190905692024-04-22 9:58:359 hrs ago1713779915IN
0x4cb69FaE...739378bb2
0 ETH0.000004070.01570121
View all transactions

Latest 25 internal transactions (View All)

Advanced mode:
Parent Transaction Hash Block From To Value
1190087942024-04-20 12:32:452 days ago1713616365
0x4cb69FaE...739378bb2
0.00013178 ETH
1189423622024-04-18 23:38:213 days ago1713483501
0x4cb69FaE...739378bb2
0.00026644 ETH
1186460482024-04-12 3:01:1310 days ago1712890873
0x4cb69FaE...739378bb2
0.00023695 ETH
1182920582024-04-03 22:21:3318 days ago1712182893
0x4cb69FaE...739378bb2
0.00039832 ETH
1182757302024-04-03 13:17:1719 days ago1712150237
0x4cb69FaE...739378bb2
0.00499999 ETH
1182592202024-04-03 4:06:5719 days ago1712117217
0x4cb69FaE...739378bb2
0.00499999 ETH
1180307432024-03-28 21:11:0324 days ago1711660263
0x4cb69FaE...739378bb2
0.00120207 ETH
1180159612024-03-28 12:58:1925 days ago1711630699
0x4cb69FaE...739378bb2
0.00499999 ETH
1178907802024-03-25 15:25:3728 days ago1711380337
0x4cb69FaE...739378bb2
0.00000612 ETH
1178129672024-03-23 20:11:5129 days ago1711224711
0x4cb69FaE...739378bb2
0.00181309 ETH
1177191302024-03-21 16:03:5732 days ago1711037037
0x4cb69FaE...739378bb2
0.00000562 ETH
1176313872024-03-19 15:19:1134 days ago1710861551
0x4cb69FaE...739378bb2
0.00000512 ETH
1176213112024-03-19 9:43:1934 days ago1710841399
0x4cb69FaE...739378bb2
0.00006873 ETH
1174909862024-03-16 9:19:0937 days ago1710580749
0x4cb69FaE...739378bb2
0.00031549 ETH
1172459402024-03-10 17:10:5743 days ago1710090657
0x4cb69FaE...739378bb2
0.00053631 ETH
1170674802024-03-06 14:02:1747 days ago1709733737
0x4cb69FaE...739378bb2
0.005 ETH
1168082962024-02-29 14:02:4953 days ago1709215369
0x4cb69FaE...739378bb2
0.00019652 ETH
1164864942024-02-22 3:16:0560 days ago1708571765
0x4cb69FaE...739378bb2
0.00294329 ETH
1164038812024-02-20 5:22:1962 days ago1708406539
0x4cb69FaE...739378bb2
0.00081869 ETH
1163614112024-02-19 5:46:3963 days ago1708321599
0x4cb69FaE...739378bb2
0.00045926 ETH
1161841692024-02-15 3:18:3567 days ago1707967115
0x4cb69FaE...739378bb2
0.00140974 ETH
1161334112024-02-13 23:06:3968 days ago1707865599
0x4cb69FaE...739378bb2
0.005 ETH
1161326422024-02-13 22:41:0168 days ago1707864061
0x4cb69FaE...739378bb2
0.005 ETH
1160988452024-02-13 3:54:2769 days ago1707796467
0x4cb69FaE...739378bb2
0.00293929 ETH
1160307362024-02-11 14:04:0971 days ago1707660249
0x4cb69FaE...739378bb2
0.005 ETH
View All Internal Transactions

Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
CircleRelayer

Compiler Version
v0.8.19+commit.7dd6d404

Optimization Enabled:
Yes with 200 runs

Other Settings:
paris EvmVersion

Contract Source Code (Solidity Standard Json-Input format)

File 1 of 22 : CircleRelayer.sol
// SPDX-License-Identifier: Apache 2
pragma solidity ^0.8.17;

import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
import "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "../libraries/BytesLib.sol";

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

import "./CircleRelayerGovernance.sol";
import "./CircleRelayerMessages.sol";

/**
 * @title Circle Bridge Asset Relayer
 * @notice This contract composes on Wormhole's Circle Integration contracts to faciliate
 * one-click transfers of Circle Bridge supported assets cross chain.
 */
contract CircleRelayer is CircleRelayerMessages, CircleRelayerGovernance, ReentrancyGuard {
    using BytesLib for bytes;

    // contract version
    string public constant VERSION = "0.2.0";

    /**
     * @notice Emitted when a swap is executed with an off-chain relayer
     * @param recipient Address of the recipient of the native assets
     * @param relayer Address of the relayer that performed the swap
     * @param token Address of the token being swapped
     * @param tokenAmount Amount of token being swapped
     * @param nativeAmount Amount of native assets swapped for tokens
     */
    event SwapExecuted(
        address indexed recipient,
        address indexed relayer,
        address indexed token,
        uint256 tokenAmount,
        uint256 nativeAmount
    );

    constructor(
        address circleIntegration_,
        uint8 nativeTokenDecimals_,
        address feeRecipient_,
        address ownerAssistant_
    ) {
        require(circleIntegration_ != address(0), "invalid circle integration address");
        require(nativeTokenDecimals_ > 0, "invalid native decimals");
        require(feeRecipient_ != address(0), "invalid fee recipient address");
        require(ownerAssistant_ != address(0), "invalid owner assistant");

        // configure state
        setOwner(msg.sender);
        setCircleIntegration(circleIntegration_);
        setNativeTokenDecimals(nativeTokenDecimals_);
        setFeeRecipient(feeRecipient_);
        setOwnerAssistant(ownerAssistant_);

        // set wormhole and chainId by querying the integration contract state
        ICircleIntegration integration = circleIntegration();
        setChainId(integration.chainId());
        setWormhole(address(integration.wormhole()));

        // set initial swap rate precision to 1e8
        setNativeSwapRatePrecision(1e8);
    }

    /**
     * @notice Calls Wormhole's Circle Integration contract to burn user specified tokens.
     * It emits a Wormhole message with instructions for how to handle relayer payments
     * on the target contract and the quantity of tokens to convert into native assets
     * for the user.
     * @param token Address of the Circle Bridge asset to be transferred.
     * @param amount Quantity of tokens to be transferred.
     * @param toNativeTokenAmount Amount of tokens to swap into native assets on
     * the target chain.
     * @param targetChain Wormhole chain ID of the target blockchain.
     * @param targetRecipientWallet User's wallet address on the target blockchain.
     * @return messageSequence Wormhole sequence for emitted TransferTokensWithRelay message.
     */
    function transferTokensWithRelay(
        IERC20Metadata token,
        uint256 amount,
        uint256 toNativeTokenAmount,
        uint16 targetChain,
        bytes32 targetRecipientWallet
    ) public payable nonReentrant notPaused returns (uint64 messageSequence) {
        // sanity check input values
        require(amount > 0, "amount must be > 0");
        require(targetRecipientWallet != bytes32(0), "invalid target recipient");
        require(address(token) != address(0), "token cannot equal address(0)");

        // cache the target contract address
        bytes32 targetContract = getRegisteredContract(targetChain);
        require(
            targetContract != bytes32(0),
            "CIRCLE-RELAYER: target not registered"
        );

        // transfer the tokens to this contract
        uint256 amountReceived = custodyTokens(token, amount);
        uint256 targetRelayerFee = relayerFee(targetChain, address(token));
        require(
            amountReceived > targetRelayerFee + toNativeTokenAmount,
            "insufficient amountReceived"
        );

        // Construct additional instructions to tell the receiving contract
        // how to handle the token redemption.
        TransferTokensWithRelay memory transferMessage = TransferTokensWithRelay({
            payloadId: 1,
            targetRelayerFee: targetRelayerFee,
            toNativeTokenAmount: toNativeTokenAmount,
            targetRecipientWallet: targetRecipientWallet
        });

        // cache circle integration instance
        ICircleIntegration integration = circleIntegration();

        // approve the circle integration contract to spend tokens
        SafeERC20.safeApprove(
            token,
            address(integration),
            amountReceived
        );

        // transfer the tokens with instructions via the circle integration contract
        messageSequence = integration.transferTokensWithPayload(
            ICircleIntegration.TransferParameters({
                token: address(token),
                amount: amount,
                targetChain: targetChain,
                mintRecipient: targetContract
            }),
            0, // nonce
            encodeTransferTokensWithRelay(transferMessage)
        );
    }

    /**
     * @notice Calls Wormhole's Circle Integration contract to complete the token transfer. Takes
     * custody of the minted tokens and sends the tokens to the target recipient.
     * It pays the fee recipient in the minted token denomination. If requested by the user,
     * it will perform a swap with the off-chain relayer to provide the user with native assets.
     * @param redeemParams Struct containing an attested Wormhole message, Circle Bridge message,
     * and Circle transfer attestation.
     */
    function redeemTokens(
        ICircleIntegration.RedeemParameters calldata redeemParams
    ) public payable {
        // cache circle integration instance
        ICircleIntegration integration = circleIntegration();

        /**
         * Mint tokens to this contract. Serves as a reentrancy protection,
         * since the circle integration contract will not allow the wormhole
         * message in the redeemParams to be replayed.
         */
        ICircleIntegration.DepositWithPayload memory deposit =
            integration.redeemTokensWithPayload(redeemParams);

        // parse the additional instructions from the deposit message
        TransferTokensWithRelay memory transferMessage = decodeTransferTokensWithRelay(
            deposit.payload
        );

        // verify that the sender is a registered contract
        require(
            deposit.fromAddress == getRegisteredContract(
                integration.getChainIdFromDomain(deposit.sourceDomain)
            ),
            "fromAddress is not a registered contract"
        );

        // cache the token and recipient addresses
        IERC20Metadata token = IERC20Metadata(bytes32ToAddress(deposit.token));
        address recipient = bytes32ToAddress(transferMessage.targetRecipientWallet);

        // If the recipient is self redeeming, send the full token amount to
        // the recipient. Revert if they attempt to send ether to this contract.
        if (msg.sender == recipient) {
            require(msg.value == 0, "recipient cannot swap native assets");

            // transfer the full token amount to the recipient
            SafeERC20.safeTransfer(
                token,
                recipient,
                deposit.amount
            );

            // bail out
            return;
        }

        // handle native asset payments and refunds
        if (transferMessage.toNativeTokenAmount > 0) {
            /**
             * Compute the maximum amount of tokens that the user is allowed
             * to swap for native assets. Override the toNativeTokenAmount in
             * the transferMessage if the toNativeTokenAmount is greater than
             * the maxToNativeAllowed.
             */
            uint256 maxToNativeAllowed = calculateMaxSwapAmountIn(token);
            if (transferMessage.toNativeTokenAmount > maxToNativeAllowed) {
                transferMessage.toNativeTokenAmount = maxToNativeAllowed;
            }

            // compute amount of native asset to pay the recipient
            uint256 nativeAmountForRecipient = calculateNativeSwapAmountOut(
                token,
                transferMessage.toNativeTokenAmount
            );

            /**
             * The nativeAmountForRecipient can be zero if the user specifed a
             * toNativeTokenAmount that is too little to convert to native asset.
             * We need to override the toNativeTokenAmount to be zero if that is
             * the case, that way the user receives the full amount of minted USDC.
             */
            if (nativeAmountForRecipient > 0) {
                // check to see if the relayer sent enough value
                require(
                    msg.value >= nativeAmountForRecipient,
                    "insufficient native asset amount"
                );

                // refund excess native asset to relayer if applicable
                uint256 relayerRefund = msg.value - nativeAmountForRecipient;
                if (relayerRefund > 0) {
                    payable(msg.sender).transfer(relayerRefund);
                }

                // send requested native asset to target recipient
                payable(recipient).transfer(nativeAmountForRecipient);

                // emit swap event
                emit SwapExecuted(
                    recipient,
                    msg.sender,
                    address(token),
                    transferMessage.toNativeTokenAmount,
                    nativeAmountForRecipient
                );
            } else {
                // override the toNativeTokenAmount in the transferMessage
                transferMessage.toNativeTokenAmount = 0;

                // refund the relayer any native asset sent to this contract
                if (msg.value > 0) {
                    payable(msg.sender).transfer(msg.value);
                }
            }
        }

        // add the token swap amount to the relayer fee
        uint256 amountForFeeRecipient =
            transferMessage.targetRelayerFee + transferMessage.toNativeTokenAmount;

        // pay the relayer if relayerFee > 0 and the caller is not the recipient
        if (amountForFeeRecipient > 0) {
            SafeERC20.safeTransfer(
                IERC20Metadata(token),
                feeRecipient(),
                amountForFeeRecipient
            );
        }

        // pay the target recipient the remaining minted tokens
        SafeERC20.safeTransfer(
            IERC20Metadata(token),
            recipient,
            deposit.amount - amountForFeeRecipient
        );
    }

    /**
     * @notice Calculates the max amount of tokens the user can convert to
     * native assets on this chain.
     * @dev The max amount of native assets the contract will swap with the user
     * is governed by the `maxNativeSwapAmount` state variable.
     * @param token Address of token being transferred.
     * @return maxAllowed The maximum number of tokens the user is allowed to
     * swap for native assets.
     */
    function calculateMaxSwapAmountIn(
        IERC20Metadata token
    ) public view returns (uint256 maxAllowed) {
        // cache swap rate
        uint256 swapRate = nativeSwapRate(address(token));
        require(swapRate > 0, "swap rate not set");

        // cache token decimals
        uint8 tokenDecimals = token.decimals();
        uint8 nativeDecimals = nativeTokenDecimals();

        if (tokenDecimals > nativeDecimals) {
            maxAllowed =
                maxNativeSwapAmount(address(token)) * swapRate *
                10 ** (tokenDecimals - nativeDecimals) / nativeSwapRatePrecision();
        } else {
            maxAllowed =
                (maxNativeSwapAmount(address(token)) * swapRate) /
                (10 ** (nativeDecimals - tokenDecimals) * nativeSwapRatePrecision());
        }
    }

    /**
     * @notice Calculates the amount of native assets that a user will receive
     * when swapping transferred tokens for native assets.
     * @dev The swap rate is governed by the `nativeSwapRate` state variable.
     * @param token Address of token being transferred.
     * @param toNativeAmount Quantity of tokens to be converted to native assets.
     * @return nativeAmount The amount of native tokens that a user receives.
     */
    function calculateNativeSwapAmountOut(
        IERC20Metadata token,
        uint256 toNativeAmount
    ) public view returns (uint256 nativeAmount) {
        // cache swap rate
        uint256 swapRate = nativeSwapRate(address(token));
        require(swapRate > 0, "swap rate not set");

        // cache token decimals
        uint8 tokenDecimals = token.decimals();
        uint8 nativeDecimals = nativeTokenDecimals();

        if (tokenDecimals > nativeDecimals) {
            nativeAmount =
                nativeSwapRatePrecision() * toNativeAmount /
                (swapRate * 10 ** (tokenDecimals - nativeDecimals));
        } else {
            nativeAmount =
                nativeSwapRatePrecision() * toNativeAmount *
                10 ** (nativeDecimals - tokenDecimals) / swapRate;
        }
    }

    function custodyTokens(IERC20Metadata token, uint256 amount) internal returns (uint256) {
        // query own token balance before transfer
        uint256 balanceBefore = token.balanceOf(address(this));

        // deposit USDC
        SafeERC20.safeTransferFrom(
            token,
            msg.sender,
            address(this),
            amount
        );

        // query own token balance after transfer
        uint256 balanceAfter = token.balanceOf(address(this));

        // this check is necessary since Circle's token contracts are upgradeable
        return balanceAfter - balanceBefore;
    }

    function bytes32ToAddress(bytes32 address_) public pure returns (address) {
        return address(uint160(uint256(address_)));
    }
}

File 2 of 22 : ReentrancyGuard.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (security/ReentrancyGuard.sol)

pragma solidity ^0.8.0;

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

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

    uint256 private _status;

    constructor() {
        _status = _NOT_ENTERED;
    }

    /**
     * @dev Prevents a contract from calling itself, directly or indirectly.
     * Calling a `nonReentrant` function from another `nonReentrant`
     * function is not supported. It is possible to prevent this from happening
     * by making the `nonReentrant` function external, and making it call a
     * `private` function that does the actual work.
     */
    modifier nonReentrant() {
        // On the first call to nonReentrant, _notEntered will be true
        require(_status != _ENTERED, "ReentrancyGuard: reentrant call");

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

        _;

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

File 3 of 22 : IERC20Metadata.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Metadata.sol)

pragma solidity ^0.8.0;

import "../IERC20.sol";

/**
 * @dev Interface for the optional metadata functions from the ERC20 standard.
 *
 * _Available since v4.1._
 */
interface IERC20Metadata is IERC20 {
    /**
     * @dev Returns the name of the token.
     */
    function name() external view returns (string memory);

    /**
     * @dev Returns the symbol of the token.
     */
    function symbol() external view returns (string memory);

    /**
     * @dev Returns the decimals places of the token.
     */
    function decimals() external view returns (uint8);
}

File 4 of 22 : SafeERC20.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (token/ERC20/utils/SafeERC20.sol)

pragma solidity ^0.8.0;

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

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

    function safeTransfer(
        IERC20 token,
        address to,
        uint256 value
    ) internal {
        _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
    }

    function safeTransferFrom(
        IERC20 token,
        address from,
        address to,
        uint256 value
    ) internal {
        _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
    }

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

    function safeIncreaseAllowance(
        IERC20 token,
        address spender,
        uint256 value
    ) internal {
        uint256 newAllowance = token.allowance(address(this), spender) + value;
        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
    }

    function safeDecreaseAllowance(
        IERC20 token,
        address spender,
        uint256 value
    ) internal {
        unchecked {
            uint256 oldAllowance = token.allowance(address(this), spender);
            require(oldAllowance >= value, "SafeERC20: decreased allowance below zero");
            uint256 newAllowance = oldAllowance - value;
            _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
        }
    }

    function safePermit(
        IERC20Permit token,
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) internal {
        uint256 nonceBefore = token.nonces(owner);
        token.permit(owner, spender, value, deadline, v, r, s);
        uint256 nonceAfter = token.nonces(owner);
        require(nonceAfter == nonceBefore + 1, "SafeERC20: permit did not succeed");
    }

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

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

File 5 of 22 : BytesLib.sol
// SPDX-License-Identifier: Unlicense
/*
 * @title Solidity Bytes Arrays Utils
 * @author Gonçalo Sá <[email protected]>
 *
 * @dev Bytes tightly packed arrays utility library for ethereum contracts written in Solidity.
 *      The library lets you concatenate, slice and type cast bytes arrays both in memory and storage.
 */
pragma solidity >=0.8.0 <0.9.0;


library BytesLib {
    function concat(
        bytes memory _preBytes,
        bytes memory _postBytes
    )
        internal
        pure
        returns (bytes memory)
    {
        bytes memory tempBytes;

        assembly {
            // Get a location of some free memory and store it in tempBytes as
            // Solidity does for memory variables.
            tempBytes := mload(0x40)

            // Store the length of the first bytes array at the beginning of
            // the memory for tempBytes.
            let length := mload(_preBytes)
            mstore(tempBytes, length)

            // Maintain a memory counter for the current write location in the
            // temp bytes array by adding the 32 bytes for the array length to
            // the starting location.
            let mc := add(tempBytes, 0x20)
            // Stop copying when the memory counter reaches the length of the
            // first bytes array.
            let end := add(mc, length)

            for {
                // Initialize a copy counter to the start of the _preBytes data,
                // 32 bytes into its memory.
                let cc := add(_preBytes, 0x20)
            } lt(mc, end) {
                // Increase both counters by 32 bytes each iteration.
                mc := add(mc, 0x20)
                cc := add(cc, 0x20)
            } {
                // Write the _preBytes data into the tempBytes memory 32 bytes
                // at a time.
                mstore(mc, mload(cc))
            }

            // Add the length of _postBytes to the current length of tempBytes
            // and store it as the new length in the first 32 bytes of the
            // tempBytes memory.
            length := mload(_postBytes)
            mstore(tempBytes, add(length, mload(tempBytes)))

            // Move the memory counter back from a multiple of 0x20 to the
            // actual end of the _preBytes data.
            mc := end
            // Stop copying when the memory counter reaches the new combined
            // length of the arrays.
            end := add(mc, length)

            for {
                let cc := add(_postBytes, 0x20)
            } lt(mc, end) {
                mc := add(mc, 0x20)
                cc := add(cc, 0x20)
            } {
                mstore(mc, mload(cc))
            }

            // Update the free-memory pointer by padding our last write location
            // to 32 bytes: add 31 bytes to the end of tempBytes to move to the
            // next 32 byte block, then round down to the nearest multiple of
            // 32. If the sum of the length of the two arrays is zero then add
            // one before rounding down to leave a blank 32 bytes (the length block with 0).
            mstore(0x40, and(
              add(add(end, iszero(add(length, mload(_preBytes)))), 31),
              not(31) // Round down to the nearest 32 bytes.
            ))
        }

        return tempBytes;
    }

    function concatStorage(bytes storage _preBytes, bytes memory _postBytes) internal {
        assembly {
            // Read the first 32 bytes of _preBytes storage, which is the length
            // of the array. (We don't need to use the offset into the slot
            // because arrays use the entire slot.)
            let fslot := sload(_preBytes.slot)
            // Arrays of 31 bytes or less have an even value in their slot,
            // while longer arrays have an odd value. The actual length is
            // the slot divided by two for odd values, and the lowest order
            // byte divided by two for even values.
            // If the slot is even, bitwise and the slot with 255 and divide by
            // two to get the length. If the slot is odd, bitwise and the slot
            // with -1 and divide by two.
            let slength := div(and(fslot, sub(mul(0x100, iszero(and(fslot, 1))), 1)), 2)
            let mlength := mload(_postBytes)
            let newlength := add(slength, mlength)
            // slength can contain both the length and contents of the array
            // if length < 32 bytes so let's prepare for that
            // v. http://solidity.readthedocs.io/en/latest/miscellaneous.html#layout-of-state-variables-in-storage
            switch add(lt(slength, 32), lt(newlength, 32))
            case 2 {
                // Since the new array still fits in the slot, we just need to
                // update the contents of the slot.
                // uint256(bytes_storage) = uint256(bytes_storage) + uint256(bytes_memory) + new_length
                sstore(
                    _preBytes.slot,
                    // all the modifications to the slot are inside this
                    // next block
                    add(
                        // we can just add to the slot contents because the
                        // bytes we want to change are the LSBs
                        fslot,
                        add(
                            mul(
                                div(
                                    // load the bytes from memory
                                    mload(add(_postBytes, 0x20)),
                                    // zero all bytes to the right
                                    exp(0x100, sub(32, mlength))
                                ),
                                // and now shift left the number of bytes to
                                // leave space for the length in the slot
                                exp(0x100, sub(32, newlength))
                            ),
                            // increase length by the double of the memory
                            // bytes length
                            mul(mlength, 2)
                        )
                    )
                )
            }
            case 1 {
                // The stored value fits in the slot, but the combined value
                // will exceed it.
                // get the keccak hash to get the contents of the array
                mstore(0x0, _preBytes.slot)
                let sc := add(keccak256(0x0, 0x20), div(slength, 32))

                // save new length
                sstore(_preBytes.slot, add(mul(newlength, 2), 1))

                // The contents of the _postBytes array start 32 bytes into
                // the structure. Our first read should obtain the `submod`
                // bytes that can fit into the unused space in the last word
                // of the stored array. To get this, we read 32 bytes starting
                // from `submod`, so the data we read overlaps with the array
                // contents by `submod` bytes. Masking the lowest-order
                // `submod` bytes allows us to add that value directly to the
                // stored value.

                let submod := sub(32, slength)
                let mc := add(_postBytes, submod)
                let end := add(_postBytes, mlength)
                let mask := sub(exp(0x100, submod), 1)

                sstore(
                    sc,
                    add(
                        and(
                            fslot,
                            0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00
                        ),
                        and(mload(mc), mask)
                    )
                )

                for {
                    mc := add(mc, 0x20)
                    sc := add(sc, 1)
                } lt(mc, end) {
                    sc := add(sc, 1)
                    mc := add(mc, 0x20)
                } {
                    sstore(sc, mload(mc))
                }

                mask := exp(0x100, sub(mc, end))

                sstore(sc, mul(div(mload(mc), mask), mask))
            }
            default {
                // get the keccak hash to get the contents of the array
                mstore(0x0, _preBytes.slot)
                // Start copying to the last used word of the stored array.
                let sc := add(keccak256(0x0, 0x20), div(slength, 32))

                // save new length
                sstore(_preBytes.slot, add(mul(newlength, 2), 1))

                // Copy over the first `submod` bytes of the new data as in
                // case 1 above.
                let slengthmod := mod(slength, 32)
                let mlengthmod := mod(mlength, 32)
                let submod := sub(32, slengthmod)
                let mc := add(_postBytes, submod)
                let end := add(_postBytes, mlength)
                let mask := sub(exp(0x100, submod), 1)

                sstore(sc, add(sload(sc), and(mload(mc), mask)))

                for {
                    sc := add(sc, 1)
                    mc := add(mc, 0x20)
                } lt(mc, end) {
                    sc := add(sc, 1)
                    mc := add(mc, 0x20)
                } {
                    sstore(sc, mload(mc))
                }

                mask := exp(0x100, sub(mc, end))

                sstore(sc, mul(div(mload(mc), mask), mask))
            }
        }
    }

    function slice(
        bytes memory _bytes,
        uint256 _start,
        uint256 _length
    )
        internal
        pure
        returns (bytes memory)
    {
        require(_length + 31 >= _length, "slice_overflow");
        require(_bytes.length >= _start + _length, "slice_outOfBounds");

        bytes memory tempBytes;

        assembly {
            switch iszero(_length)
            case 0 {
                // Get a location of some free memory and store it in tempBytes as
                // Solidity does for memory variables.
                tempBytes := mload(0x40)

                // The first word of the slice result is potentially a partial
                // word read from the original array. To read it, we calculate
                // the length of that partial word and start copying that many
                // bytes into the array. The first word we copy will start with
                // data we don't care about, but the last `lengthmod` bytes will
                // land at the beginning of the contents of the new array. When
                // we're done copying, we overwrite the full first word with
                // the actual length of the slice.
                let lengthmod := and(_length, 31)

                // The multiplication in the next line is necessary
                // because when slicing multiples of 32 bytes (lengthmod == 0)
                // the following copy loop was copying the origin's length
                // and then ending prematurely not copying everything it should.
                let mc := add(add(tempBytes, lengthmod), mul(0x20, iszero(lengthmod)))
                let end := add(mc, _length)

                for {
                    // The multiplication in the next line has the same exact purpose
                    // as the one above.
                    let cc := add(add(add(_bytes, lengthmod), mul(0x20, iszero(lengthmod))), _start)
                } lt(mc, end) {
                    mc := add(mc, 0x20)
                    cc := add(cc, 0x20)
                } {
                    mstore(mc, mload(cc))
                }

                mstore(tempBytes, _length)

                //update free-memory pointer
                //allocating the array padded to 32 bytes like the compiler does now
                mstore(0x40, and(add(mc, 31), not(31)))
            }
            //if we want a zero-length slice let's just return a zero-length array
            default {
                tempBytes := mload(0x40)
                //zero out the 32 bytes slice we are about to return
                //we need to do it because Solidity does not garbage collect
                mstore(tempBytes, 0)

                mstore(0x40, add(tempBytes, 0x20))
            }
        }

        return tempBytes;
    }

    function toAddress(bytes memory _bytes, uint256 _start) internal pure returns (address) {
        require(_bytes.length >= _start + 20, "toAddress_outOfBounds");
        address tempAddress;

        assembly {
            tempAddress := div(mload(add(add(_bytes, 0x20), _start)), 0x1000000000000000000000000)
        }

        return tempAddress;
    }

    function toUint8(bytes memory _bytes, uint256 _start) internal pure returns (uint8) {
        require(_bytes.length >= _start + 1 , "toUint8_outOfBounds");
        uint8 tempUint;

        assembly {
            tempUint := mload(add(add(_bytes, 0x1), _start))
        }

        return tempUint;
    }

    function toUint16(bytes memory _bytes, uint256 _start) internal pure returns (uint16) {
        require(_bytes.length >= _start + 2, "toUint16_outOfBounds");
        uint16 tempUint;

        assembly {
            tempUint := mload(add(add(_bytes, 0x2), _start))
        }

        return tempUint;
    }

    function toUint32(bytes memory _bytes, uint256 _start) internal pure returns (uint32) {
        require(_bytes.length >= _start + 4, "toUint32_outOfBounds");
        uint32 tempUint;

        assembly {
            tempUint := mload(add(add(_bytes, 0x4), _start))
        }

        return tempUint;
    }

    function toUint64(bytes memory _bytes, uint256 _start) internal pure returns (uint64) {
        require(_bytes.length >= _start + 8, "toUint64_outOfBounds");
        uint64 tempUint;

        assembly {
            tempUint := mload(add(add(_bytes, 0x8), _start))
        }

        return tempUint;
    }

    function toUint96(bytes memory _bytes, uint256 _start) internal pure returns (uint96) {
        require(_bytes.length >= _start + 12, "toUint96_outOfBounds");
        uint96 tempUint;

        assembly {
            tempUint := mload(add(add(_bytes, 0xc), _start))
        }

        return tempUint;
    }

    function toUint128(bytes memory _bytes, uint256 _start) internal pure returns (uint128) {
        require(_bytes.length >= _start + 16, "toUint128_outOfBounds");
        uint128 tempUint;

        assembly {
            tempUint := mload(add(add(_bytes, 0x10), _start))
        }

        return tempUint;
    }

    function toUint256(bytes memory _bytes, uint256 _start) internal pure returns (uint256) {
        require(_bytes.length >= _start + 32, "toUint256_outOfBounds");
        uint256 tempUint;

        assembly {
            tempUint := mload(add(add(_bytes, 0x20), _start))
        }

        return tempUint;
    }

    function toBytes32(bytes memory _bytes, uint256 _start) internal pure returns (bytes32) {
        require(_bytes.length >= _start + 32, "toBytes32_outOfBounds");
        bytes32 tempBytes32;

        assembly {
            tempBytes32 := mload(add(add(_bytes, 0x20), _start))
        }

        return tempBytes32;
    }

    function equal(bytes memory _preBytes, bytes memory _postBytes) internal pure returns (bool) {
        bool success = true;

        assembly {
            let length := mload(_preBytes)

            // if lengths don't match the arrays are not equal
            switch eq(length, mload(_postBytes))
            case 1 {
                // cb is a circuit breaker in the for loop since there's
                //  no said feature for inline assembly loops
                // cb = 1 - don't breaker
                // cb = 0 - break
                let cb := 1

                let mc := add(_preBytes, 0x20)
                let end := add(mc, length)

                for {
                    let cc := add(_postBytes, 0x20)
                // the next line is the loop condition:
                // while(uint256(mc < end) + cb == 2)
                } eq(add(lt(mc, end), cb), 2) {
                    mc := add(mc, 0x20)
                    cc := add(cc, 0x20)
                } {
                    // if any of these checks fails then arrays are not equal
                    if iszero(eq(mload(mc), mload(cc))) {
                        // unsuccess:
                        success := 0
                        cb := 0
                    }
                }
            }
            default {
                // unsuccess:
                success := 0
            }
        }

        return success;
    }

    function equalStorage(
        bytes storage _preBytes,
        bytes memory _postBytes
    )
        internal
        view
        returns (bool)
    {
        bool success = true;

        assembly {
            // we know _preBytes_offset is 0
            let fslot := sload(_preBytes.slot)
            // Decode the length of the stored array like in concatStorage().
            let slength := div(and(fslot, sub(mul(0x100, iszero(and(fslot, 1))), 1)), 2)
            let mlength := mload(_postBytes)

            // if lengths don't match the arrays are not equal
            switch eq(slength, mlength)
            case 1 {
                // slength can contain both the length and contents of the array
                // if length < 32 bytes so let's prepare for that
                // v. http://solidity.readthedocs.io/en/latest/miscellaneous.html#layout-of-state-variables-in-storage
                if iszero(iszero(slength)) {
                    switch lt(slength, 32)
                    case 1 {
                        // blank the last byte which is the length
                        fslot := mul(div(fslot, 0x100), 0x100)

                        if iszero(eq(fslot, mload(add(_postBytes, 0x20)))) {
                            // unsuccess:
                            success := 0
                        }
                    }
                    default {
                        // cb is a circuit breaker in the for loop since there's
                        //  no said feature for inline assembly loops
                        // cb = 1 - don't breaker
                        // cb = 0 - break
                        let cb := 1

                        // get the keccak hash to get the contents of the array
                        mstore(0x0, _preBytes.slot)
                        let sc := keccak256(0x0, 0x20)

                        let mc := add(_postBytes, 0x20)
                        let end := add(mc, mlength)

                        // the next line is the loop condition:
                        // while(uint256(mc < end) + cb == 2)
                        for {} eq(add(lt(mc, end), cb), 2) {
                            sc := add(sc, 1)
                            mc := add(mc, 0x20)
                        } {
                            if iszero(eq(sload(sc), mload(mc))) {
                                // unsuccess:
                                success := 0
                                cb := 0
                            }
                        }
                    }
                }
            }
            default {
                // unsuccess:
                success := 0
            }
        }

        return success;
    }
}

File 6 of 22 : IWormhole.sol
// contracts/Messages.sol
// SPDX-License-Identifier: Apache 2

pragma solidity ^0.8.0;

interface IWormhole {
    struct GuardianSet {
        address[] keys;
        uint32 expirationTime;
    }

    struct Signature {
        bytes32 r;
        bytes32 s;
        uint8 v;
        uint8 guardianIndex;
    }

    struct VM {
        uint8 version;
        uint32 timestamp;
        uint32 nonce;
        uint16 emitterChainId;
        bytes32 emitterAddress;
        uint64 sequence;
        uint8 consistencyLevel;
        bytes payload;

        uint32 guardianSetIndex;
        Signature[] signatures;

        bytes32 hash;
    }

    struct ContractUpgrade {
        bytes32 module;
        uint8 action;
        uint16 chain;

        address newContract;
    }

    struct GuardianSetUpgrade {
        bytes32 module;
        uint8 action;
        uint16 chain;

        GuardianSet newGuardianSet;
        uint32 newGuardianSetIndex;
    }

    struct SetMessageFee {
        bytes32 module;
        uint8 action;
        uint16 chain;

        uint256 messageFee;
    }

    struct TransferFees {
        bytes32 module;
        uint8 action;
        uint16 chain;

        uint256 amount;
        bytes32 recipient;
    }

    struct RecoverChainId {
        bytes32 module;
        uint8 action;

        uint256 evmChainId;
        uint16 newChainId;
    }

    event LogMessagePublished(address indexed sender, uint64 sequence, uint32 nonce, bytes payload, uint8 consistencyLevel);
    event ContractUpgraded(address indexed oldContract, address indexed newContract);
    event GuardianSetAdded(uint32 indexed index);

    function publishMessage(
        uint32 nonce,
        bytes memory payload,
        uint8 consistencyLevel
    ) external payable returns (uint64 sequence);

    function initialize() external;

    function parseAndVerifyVM(bytes calldata encodedVM) external view returns (VM memory vm, bool valid, string memory reason);

    function verifyVM(VM memory vm) external view returns (bool valid, string memory reason);

    function verifySignatures(bytes32 hash, Signature[] memory signatures, GuardianSet memory guardianSet) external pure returns (bool valid, string memory reason);

    function parseVM(bytes memory encodedVM) external pure returns (VM memory vm);

    function quorum(uint numGuardians) external pure returns (uint numSignaturesRequiredForQuorum);

    function getGuardianSet(uint32 index) external view returns (GuardianSet memory);

    function getCurrentGuardianSetIndex() external view returns (uint32);

    function getGuardianSetExpiry() external view returns (uint32);

    function governanceActionIsConsumed(bytes32 hash) external view returns (bool);

    function isInitialized(address impl) external view returns (bool);

    function chainId() external view returns (uint16);

    function isFork() external view returns (bool);

    function governanceChainId() external view returns (uint16);

    function governanceContract() external view returns (bytes32);

    function messageFee() external view returns (uint256);

    function evmChainId() external view returns (uint256);

    function nextSequence(address emitter) external view returns (uint64);

    function parseContractUpgrade(bytes memory encodedUpgrade) external pure returns (ContractUpgrade memory cu);

    function parseGuardianSetUpgrade(bytes memory encodedUpgrade) external pure returns (GuardianSetUpgrade memory gsu);

    function parseSetMessageFee(bytes memory encodedSetMessageFee) external pure returns (SetMessageFee memory smf);

    function parseTransferFees(bytes memory encodedTransferFees) external pure returns (TransferFees memory tf);

    function parseRecoverChainId(bytes memory encodedRecoverChainId) external pure returns (RecoverChainId memory rci);

    function submitContractUpgrade(bytes memory _vm) external;

    function submitSetMessageFee(bytes memory _vm) external;

    function submitNewGuardianSet(bytes memory _vm) external;

    function submitTransferFees(bytes memory _vm) external;

    function submitRecoverChainId(bytes memory _vm) external;
}

File 7 of 22 : CircleRelayerGovernance.sol
// SPDX-License-Identifier: Apache 2
pragma solidity ^0.8.17;

import "@openzeppelin/contracts/proxy/ERC1967/ERC1967Upgrade.sol";

import "./CircleRelayerSetters.sol";
import "./CircleRelayerGetters.sol";
import "./CircleRelayerState.sol";

contract CircleRelayerGovernance is CircleRelayerGetters, ERC1967Upgrade {
    event OwnershipTransfered(address indexed oldOwner, address indexed newOwner);
    event FeeRecipientUpdated(address indexed oldRecipient, address indexed newRecipient);
    event SwapRateUpdated(address indexed token, uint256 indexed swapRate);

    /**
     * @notice Starts the ownership transfer process of the contracts. It saves
     * an address in the pending owner state variable.
     * @param chainId_ Wormhole chain ID
     * @param newOwner Address of the pending owner
     */
    function submitOwnershipTransferRequest(
        uint16 chainId_,
        address newOwner
    ) public onlyOwner onlyCurrentChain(chainId_) {
        require(newOwner != address(0), "newOwner cannot equal address(0)");

        setPendingOwner(newOwner);
    }

    /**
     * @notice Cancels the ownership transfer process.
     * @dev Sets the pending owner state variable to the zero address.
     */
    function cancelOwnershipTransferRequest(
        uint16 chainId_
    ) public onlyOwner onlyCurrentChain(chainId_) {
        setPendingOwner(address(0));
    }

    /**
     * @notice Finalizes the ownership transfer to the pending owner
     * @dev It checks that the caller is the pendingOwner to validate the wallet
     * address. It updates the owner state variable with the pendingOwner state
     * variable.
     */
    function confirmOwnershipTransferRequest() public {
        // cache the new owner address
        address newOwner = pendingOwner();

        require(msg.sender == newOwner, "caller must be pendingOwner");

        // cache currentOwner for Event
        address currentOwner = owner();

        // update the owner in the contract state and reset the pending owner
        setOwner(newOwner);
        setPendingOwner(address(0));

        emit OwnershipTransfered(currentOwner, newOwner);
    }

    /**
     * @notice Updates the `ownerAssistant` state variable. This method can
     * only be executed by the owner.
     * @param chainId_ Wormhole chain ID.
     * @param newAssistant Address of the new `ownerAssistant`.
     */
    function updateOwnerAssistant(
        uint16 chainId_,
        address newAssistant
    ) public onlyOwner onlyCurrentChain(chainId_) {
        require(
            newAssistant != address(0),
            "newAssistant cannot equal address(0)"
        );

        // update the owner assistant
        setOwnerAssistant(newAssistant);
    }

    /**
     * @notice Updates the `feeRecipient` state variable. This method can
     * only be executed by the owner.
     * @param chainId_ Wormhole chain ID
     * @param newFeeRecipient Address of the new `feeRecipient`
     */
    function updateFeeRecipient(
        uint16 chainId_,
        address newFeeRecipient
    ) public onlyOwner onlyCurrentChain(chainId_) {
        require(
            newFeeRecipient != address(0),
            "newFeeRecipient cannot equal address(0)"
        );

        // cache current fee recipient
        address currentFeeRecipient = feeRecipient();

        // update the fee recipient
        setFeeRecipient(newFeeRecipient);

        emit FeeRecipientUpdated(currentFeeRecipient, newFeeRecipient);
    }

    /**
     * @notice Registers foreign Circle Relayer contracts
     * @param chainId_ Wormhole chain ID of the foreign contract
     * @param contractAddress Address of the foreign contract in bytes32 format
     * (zero-left-padded address).
     */
    function registerContract(
        uint16 chainId_,
        bytes32 contractAddress
    ) public onlyOwner {
        // sanity check both input arguments
        require(
            contractAddress != bytes32(0),
            "contractAddress cannot equal bytes32(0)"
        );
        require(
            chainId_ != 0 && chainId_ != chainId(),
            "chainId_ cannot equal 0 or this chainId"
        );

        // update the registeredContracts state variable
        _registerContract(chainId_, contractAddress);
    }

    /**
     * @notice Update the fee for relaying transfers to foreign contracts
     * @dev This function can update the source contract's record of the relayer
     * fee.
     * @param chainId_ Wormhole chain ID
     * @param token Address of the token to update the relayer fee for
     * @param amount Quantity of tokens to pay the relayer upon redemption
     */
    function updateRelayerFee(
        uint16 chainId_,
        address token,
        uint256 amount
    ) public onlyOwnerOrAssistant {
        require(chainId_ != chainId(), "invalid chain");
        require(
            getRegisteredContract(chainId_) != bytes32(0),
            "contract doesn't exist"
        );
        require(
            circleIntegration().isAcceptedToken(token),
            "token not accepted"
        );
        setRelayerFee(chainId_, token, amount);
    }

    /**
     * @notice Updates the conversion rate between the native asset of this chain
     * and the specified token.
     * @param chainId_ Wormhole chain ID
     * @param token Address of the token to update the conversion rate for
     * @param swapRate The native -> token conversion rate.
     * @dev The swapRate is the conversion rate using asset prices denominated in
     * USD multiplied by the nativeSwapRatePrecision. For example, if the conversion
     * rate is $15 and the nativeSwapRatePrecision is 1000000, the swapRate should be set
     * to 15000000.
     */
    function updateNativeSwapRate(
        uint16 chainId_,
        address token,
        uint256 swapRate
    ) public onlyOwnerOrAssistant onlyCurrentChain(chainId_) {
        require(circleIntegration().isAcceptedToken(token), "token not accepted");
        require(swapRate > 0, "swap rate must be nonzero");

        setNativeSwapRate(token, swapRate);

        emit SwapRateUpdated(token, swapRate);
    }

    /**
     * @notice Updates the precision of the native swap rate
     * @param chainId_ Wormhole chain ID
     * @param nativeSwapRatePrecision_ Precision of native swap rate
     */
    function updateNativeSwapRatePrecision(
        uint16 chainId_,
        uint256 nativeSwapRatePrecision_
    ) public onlyOwner onlyCurrentChain(chainId_) {
        require(nativeSwapRatePrecision_ > 0, "precision must be > 0");

        setNativeSwapRatePrecision(nativeSwapRatePrecision_);
    }

    /**
     * @notice Updates the max amount of native assets the contract will pay
     * to the target recipient.
     * @param chainId_ Wormhole chain ID
     * @param token Address of the token to update the max native swap amount for
     * @param maxAmount Max amount of native assets
     */
    function updateMaxNativeSwapAmount(
        uint16 chainId_,
        address token,
        uint256 maxAmount
    ) public onlyOwner onlyCurrentChain(chainId_) {
        require(circleIntegration().isAcceptedToken(token), "token not accepted");

        setMaxNativeSwapAmount(token, maxAmount);
    }

    /**
     * @notice Sets the pause state of the relayer. If paused, token transfer requests are blocked.
     * In flight transfers, i.e. those that have a VAA emitted, can still be processed if paused.
     * @param chainId_ Wormhole chain ID
     * @param paused If true, requests for token transfers will be blocked and no circle transfer VAAs will be generated.
     */
    function setPauseForTransfers(
        uint16 chainId_,
        bool paused
    ) public onlyOwner onlyCurrentChain(chainId_) {
        setPaused(paused);
    }

    modifier onlyOwner() {
        require(owner() == msg.sender, "caller not the owner");
        _;
    }

    modifier onlyCurrentChain(uint16 chainId_) {
        require(chainId() == chainId_, "wrong chain");
        _;
    }

    modifier onlyOwnerOrAssistant() {
        require(
            owner() == msg.sender ||
            ownerAssistant() == msg.sender,
            "caller not the owner or assistant"
        );
        _;
    }

    modifier notPaused() {
        require(!getPaused(), "relayer is paused");
        _;
    }
}

File 8 of 22 : CircleRelayerMessages.sol
// SPDX-License-Identifier: Apache 2
pragma solidity ^0.8.17;

import "../libraries/BytesLib.sol";

import "./CircleRelayerStructs.sol";

contract CircleRelayerMessages is CircleRelayerStructs {
    using BytesLib for bytes;

    /**
     * @notice Serializes the `TransferTokensWithRelay` struct
     * @param transfer `TransferTokensWithRelay` struct
     * @return encoded Serialized `TransferTokensWithRelay` struct
     */
    function encodeTransferTokensWithRelay(
        TransferTokensWithRelay memory transfer
    ) public pure returns (bytes memory encoded) {
        require(transfer.payloadId == 1, "invalid payloadId");
        encoded = abi.encodePacked(
            transfer.payloadId,
            transfer.targetRelayerFee,
            transfer.toNativeTokenAmount,
            transfer.targetRecipientWallet
        );
    }

    /**
     * @notice Decodes an encoded `TransferTokensWithRelay` struct
     * @dev reverts if:
     * - the first byte (payloadId) does not equal 1
     * - the length of the payload has an unexpected length
     * @param encoded Encoded `TransferTokensWithRelay` struct
     * @return transfer `TransferTokensWithRelay` struct
     */
    function decodeTransferTokensWithRelay(
        bytes memory encoded
    ) public pure returns (TransferTokensWithRelay memory transfer) {
        uint256 index = 0;

        // parse the payloadId
        transfer.payloadId = encoded.toUint8(index);
        index += 1;

        require(transfer.payloadId == 1, "CIRCLE_RELAYER: invalid message payloadId");

        // target relayer fee
        transfer.targetRelayerFee = encoded.toUint256(index);
        index += 32;

        // amount of tokens to convert to native assets
        transfer.toNativeTokenAmount = encoded.toUint256(index);
        index += 32;

        // recipient of the transfered tokens and native assets
        transfer.targetRecipientWallet = encoded.toBytes32(index);
        index += 32;

        require(index == encoded.length, "CIRCLE_RELAYER: invalid message length");
    }
}

File 9 of 22 : IERC20.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol)

pragma solidity ^0.8.0;

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

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

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

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

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

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

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

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

File 10 of 22 : draft-IERC20Permit.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/draft-IERC20Permit.sol)

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
 * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
 *
 * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
 * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't
 * need to send a transaction, and thus is not required to hold Ether at all.
 */
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].
     */
    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 11 of 22 : Address.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (utils/Address.sol)

pragma solidity ^0.8.1;

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

        return account.code.length > 0;
    }

    /**
     * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
     * `recipient`, forwarding all available gas and reverting on errors.
     *
     * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
     * of certain opcodes, possibly making contracts go over the 2300 gas limit
     * imposed by `transfer`, making them unable to receive funds via
     * `transfer`. {sendValue} removes this limitation.
     *
     * https://diligence.consensys.net/posts/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.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
     */
    function sendValue(address payable recipient, uint256 amount) internal {
        require(address(this).balance >= amount, "Address: insufficient balance");

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

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

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

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

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

        (bool success, bytes memory returndata) = target.call{value: value}(data);
        return verifyCallResult(success, returndata, errorMessage);
    }

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

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal view returns (bytes memory) {
        require(isContract(target), "Address: static call to non-contract");

        (bool success, bytes memory returndata) = target.staticcall(data);
        return verifyCallResult(success, returndata, errorMessage);
    }

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

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a delegate call.
     *
     * _Available since v3.4._
     */
    function functionDelegateCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal returns (bytes memory) {
        require(isContract(target), "Address: delegate call to non-contract");

        (bool success, bytes memory returndata) = target.delegatecall(data);
        return verifyCallResult(success, returndata, errorMessage);
    }

    /**
     * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the
     * revert reason using the provided one.
     *
     * _Available since v4.3._
     */
    function verifyCallResult(
        bool success,
        bytes memory returndata,
        string memory errorMessage
    ) internal pure returns (bytes memory) {
        if (success) {
            return returndata;
        } else {
            // Look for revert reason and bubble it up if present
            if (returndata.length > 0) {
                // The easiest way to bubble the revert reason is using memory via assembly
                /// @solidity memory-safe-assembly
                assembly {
                    let returndata_size := mload(returndata)
                    revert(add(32, returndata), returndata_size)
                }
            } else {
                revert(errorMessage);
            }
        }
    }
}

File 12 of 22 : ERC1967Upgrade.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.0) (proxy/ERC1967/ERC1967Upgrade.sol)

pragma solidity ^0.8.2;

import "../beacon/IBeacon.sol";
import "../../interfaces/draft-IERC1822.sol";
import "../../utils/Address.sol";
import "../../utils/StorageSlot.sol";

/**
 * @dev This abstract contract provides getters and event emitting update functions for
 * https://eips.ethereum.org/EIPS/eip-1967[EIP1967] slots.
 *
 * _Available since v4.1._
 *
 * @custom:oz-upgrades-unsafe-allow delegatecall
 */
abstract contract ERC1967Upgrade {
    // This is the keccak-256 hash of "eip1967.proxy.rollback" subtracted by 1
    bytes32 private constant _ROLLBACK_SLOT = 0x4910fdfa16fed3260ed0e7147f7cc6da11a60208b5b9406d12a635614ffd9143;

    /**
     * @dev Storage slot with the address of the current implementation.
     * This is the keccak-256 hash of "eip1967.proxy.implementation" subtracted by 1, and is
     * validated in the constructor.
     */
    bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;

    /**
     * @dev Emitted when the implementation is upgraded.
     */
    event Upgraded(address indexed implementation);

    /**
     * @dev Returns the current implementation address.
     */
    function _getImplementation() internal view returns (address) {
        return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;
    }

    /**
     * @dev Stores a new address in the EIP1967 implementation slot.
     */
    function _setImplementation(address newImplementation) private {
        require(Address.isContract(newImplementation), "ERC1967: new implementation is not a contract");
        StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
    }

    /**
     * @dev Perform implementation upgrade
     *
     * Emits an {Upgraded} event.
     */
    function _upgradeTo(address newImplementation) internal {
        _setImplementation(newImplementation);
        emit Upgraded(newImplementation);
    }

    /**
     * @dev Perform implementation upgrade with additional setup call.
     *
     * Emits an {Upgraded} event.
     */
    function _upgradeToAndCall(
        address newImplementation,
        bytes memory data,
        bool forceCall
    ) internal {
        _upgradeTo(newImplementation);
        if (data.length > 0 || forceCall) {
            Address.functionDelegateCall(newImplementation, data);
        }
    }

    /**
     * @dev Perform implementation upgrade with security checks for UUPS proxies, and additional setup call.
     *
     * Emits an {Upgraded} event.
     */
    function _upgradeToAndCallUUPS(
        address newImplementation,
        bytes memory data,
        bool forceCall
    ) internal {
        // Upgrades from old implementations will perform a rollback test. This test requires the new
        // implementation to upgrade back to the old, non-ERC1822 compliant, implementation. Removing
        // this special case will break upgrade paths from old UUPS implementation to new ones.
        if (StorageSlot.getBooleanSlot(_ROLLBACK_SLOT).value) {
            _setImplementation(newImplementation);
        } else {
            try IERC1822Proxiable(newImplementation).proxiableUUID() returns (bytes32 slot) {
                require(slot == _IMPLEMENTATION_SLOT, "ERC1967Upgrade: unsupported proxiableUUID");
            } catch {
                revert("ERC1967Upgrade: new implementation is not UUPS");
            }
            _upgradeToAndCall(newImplementation, data, forceCall);
        }
    }

    /**
     * @dev Storage slot with the admin of the contract.
     * This is the keccak-256 hash of "eip1967.proxy.admin" subtracted by 1, and is
     * validated in the constructor.
     */
    bytes32 internal constant _ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103;

    /**
     * @dev Emitted when the admin account has changed.
     */
    event AdminChanged(address previousAdmin, address newAdmin);

    /**
     * @dev Returns the current admin.
     */
    function _getAdmin() internal view returns (address) {
        return StorageSlot.getAddressSlot(_ADMIN_SLOT).value;
    }

    /**
     * @dev Stores a new address in the EIP1967 admin slot.
     */
    function _setAdmin(address newAdmin) private {
        require(newAdmin != address(0), "ERC1967: new admin is the zero address");
        StorageSlot.getAddressSlot(_ADMIN_SLOT).value = newAdmin;
    }

    /**
     * @dev Changes the admin of the proxy.
     *
     * Emits an {AdminChanged} event.
     */
    function _changeAdmin(address newAdmin) internal {
        emit AdminChanged(_getAdmin(), newAdmin);
        _setAdmin(newAdmin);
    }

    /**
     * @dev The storage slot of the UpgradeableBeacon contract which defines the implementation for this proxy.
     * This is bytes32(uint256(keccak256('eip1967.proxy.beacon')) - 1)) and is validated in the constructor.
     */
    bytes32 internal constant _BEACON_SLOT = 0xa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50;

    /**
     * @dev Emitted when the beacon is upgraded.
     */
    event BeaconUpgraded(address indexed beacon);

    /**
     * @dev Returns the current beacon.
     */
    function _getBeacon() internal view returns (address) {
        return StorageSlot.getAddressSlot(_BEACON_SLOT).value;
    }

    /**
     * @dev Stores a new beacon in the EIP1967 beacon slot.
     */
    function _setBeacon(address newBeacon) private {
        require(Address.isContract(newBeacon), "ERC1967: new beacon is not a contract");
        require(
            Address.isContract(IBeacon(newBeacon).implementation()),
            "ERC1967: beacon implementation is not a contract"
        );
        StorageSlot.getAddressSlot(_BEACON_SLOT).value = newBeacon;
    }

    /**
     * @dev Perform beacon upgrade with additional setup call. Note: This upgrades the address of the beacon, it does
     * not upgrade the implementation contained in the beacon (see {UpgradeableBeacon-_setImplementation} for that).
     *
     * Emits a {BeaconUpgraded} event.
     */
    function _upgradeBeaconToAndCall(
        address newBeacon,
        bytes memory data,
        bool forceCall
    ) internal {
        _setBeacon(newBeacon);
        emit BeaconUpgraded(newBeacon);
        if (data.length > 0 || forceCall) {
            Address.functionDelegateCall(IBeacon(newBeacon).implementation(), data);
        }
    }
}

File 13 of 22 : CircleRelayerSetters.sol
// SPDX-License-Identifier: Apache 2
pragma solidity ^0.8.17;

import "./CircleRelayerState.sol";

contract CircleRelayerSetters is CircleRelayerState {
    function setOwner(address owner_) internal {
        _state.owner = owner_;
    }

    function setPendingOwner(address pendingOwner_) internal {
        _state.pendingOwner = pendingOwner_;
    }

    function setOwnerAssistant(address ownerAssistant_) internal {
        _state.ownerAssistant = ownerAssistant_;
    }

    function setPaused(bool paused) internal {
        _state.paused = paused;
    }

    function setWormhole(address wormhole_) internal {
        _state.wormhole = payable(wormhole_);
    }

    function setChainId(uint16 chainId_) internal {
        _state.chainId = chainId_;
    }

    function setNativeTokenDecimals(uint8 decimals_) internal {
        _state.nativeTokenDecimals = decimals_;
    }

    function setCircleIntegration(address circleIntegration_) internal {
        _state.circleIntegration = circleIntegration_;
    }

    function setFeeRecipient(address feeRecipient_) internal {
        _state.feeRecipient = feeRecipient_;
    }

    function setRelayerFee(uint16 chainId_, address token, uint256 fee) internal {
        _state.relayerFees[chainId_][token] = fee;
    }

    function setNativeSwapRatePrecision(uint256 precision) internal {
        _state.nativeSwapRatePrecision = precision;
    }

    function setNativeSwapRate(address token, uint256 swapRate) internal {
        _state.nativeSwapRates[token] = swapRate;
    }

    function setMaxNativeSwapAmount(address token, uint256 maximum) internal {
        _state.maxNativeSwapAmount[token] = maximum;
    }

    function _registerContract(uint16 chainId_, bytes32 contract_) internal {
        _state.registeredContracts[chainId_] = contract_;
    }
}

File 14 of 22 : CircleRelayerGetters.sol
// SPDX-License-Identifier: Apache 2
pragma solidity ^0.8.17;

import {IWormhole} from "../interfaces/IWormhole.sol";
import {ICircleIntegration} from "../interfaces/ICircleIntegration.sol";

import "./CircleRelayerSetters.sol";

contract CircleRelayerGetters is CircleRelayerSetters {
    function owner() public view returns (address) {
        return _state.owner;
    }

    function pendingOwner() public view returns (address) {
        return _state.pendingOwner;
    }

    function ownerAssistant() public view returns (address) {
        return _state.ownerAssistant;
    }

    function wormhole() public view returns (IWormhole) {
        return IWormhole(_state.wormhole);
    }

    /**
     * @return paused If true, requests for token transfers will be blocked and no circle transfer VAAs will be generated.
     */
    function getPaused() public view returns (bool paused) {
        paused = _state.paused;
    }

    function chainId() public view returns (uint16) {
        return _state.chainId;
    }

    function nativeTokenDecimals() public view returns (uint8) {
        return _state.nativeTokenDecimals;
    }

    function circleIntegration() public view returns (ICircleIntegration) {
        return ICircleIntegration(_state.circleIntegration);
    }

    function feeRecipient() public view returns (address) {
        return _state.feeRecipient;
    }

    function relayerFee(uint16 chainId_, address token) public view returns (uint256) {
        return _state.relayerFees[chainId_][token];
    }

    function nativeSwapRatePrecision() public view returns (uint256) {
        return _state.nativeSwapRatePrecision;
    }

    function nativeSwapRate(address token) public view returns (uint256) {
        return _state.nativeSwapRates[token];
    }

    function maxNativeSwapAmount(address token) public view returns (uint256) {
        return _state.maxNativeSwapAmount[token];
    }

    function getRegisteredContract(uint16 emitterChainId) public view returns (bytes32) {
        return _state.registeredContracts[emitterChainId];
    }
}

File 15 of 22 : CircleRelayerState.sol
// SPDX-License-Identifier: Apache 2
pragma solidity ^0.8.17;

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

contract CircleRelayerStorage {
    struct State {
        // Wormhole chain ID of this contract
        uint16 chainId;

        // decimals of the native token on this chain
        uint8 nativeTokenDecimals;

        // If true, token transfer requests are blocked.
        bool paused;

        // owner of this contract
        address owner;

        // intermediate state when transfering contract ownership
        address pendingOwner;

        // address that can update swap rates and relayer fees
        address ownerAssistant;

        // recipient of relayer fees
        address feeRecipient;

        // address of the Wormhole contract on this chain
        address wormhole;

        // address of the trusted Circle Integration contract on this chain
        address circleIntegration;

        // precision of the nativeSwapRates, this value should NEVER be set to zero
        uint256 nativeSwapRatePrecision;

        // mapping of chainId to source token address to relayerFee
        mapping(uint16 => mapping(address => uint256)) relayerFees;

        /**
         * Mapping of source token address to native asset swap rate
         * (nativePriceUSD/tokenPriceUSD).
         */
        mapping(address => uint256) nativeSwapRates;

        /**
         * Mapping of source token address to maximum native asset swap amount
         * allowed.
         */
        mapping(address => uint256) maxNativeSwapAmount;

        // Wormhole chain ID to registered contract address mapping
        mapping(uint16 => bytes32) registeredContracts;
    }
}

contract CircleRelayerState {
    CircleRelayerStorage.State _state;
}

File 16 of 22 : CircleRelayerStructs.sol
// SPDX-License-Identifier: Apache 2
pragma solidity ^0.8.17;

contract CircleRelayerStructs {
    struct TransferTokensWithRelay {
        uint8 payloadId; // == 1
        uint256 targetRelayerFee;
        uint256 toNativeTokenAmount;
        bytes32 targetRecipientWallet;
    }
}

File 17 of 22 : IBeacon.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (proxy/beacon/IBeacon.sol)

pragma solidity ^0.8.0;

/**
 * @dev This is the interface that {BeaconProxy} expects of its beacon.
 */
interface IBeacon {
    /**
     * @dev Must return an address that can be used as a delegate call target.
     *
     * {BeaconProxy} will check that this address is a contract.
     */
    function implementation() external view returns (address);
}

File 18 of 22 : draft-IERC1822.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.0) (interfaces/draft-IERC1822.sol)

pragma solidity ^0.8.0;

/**
 * @dev ERC1822: Universal Upgradeable Proxy Standard (UUPS) documents a method for upgradeability through a simplified
 * proxy whose upgrades are fully controlled by the current implementation.
 */
interface IERC1822Proxiable {
    /**
     * @dev Returns the storage slot that the proxiable contract assumes is being used to store the implementation
     * address.
     *
     * IMPORTANT: A proxy pointing at a proxiable contract should not be considered proxiable itself, because this risks
     * bricking a proxy that upgrades to it, by delegating to itself until out of gas. Thus it is critical that this
     * function revert if invoked through a proxy.
     */
    function proxiableUUID() external view returns (bytes32);
}

File 19 of 22 : StorageSlot.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (utils/StorageSlot.sol)

pragma solidity ^0.8.0;

/**
 * @dev Library for reading and writing primitive types to specific storage slots.
 *
 * Storage slots are often used to avoid storage conflict when dealing with upgradeable contracts.
 * This library helps with reading and writing to such slots without the need for inline assembly.
 *
 * The functions in this library return Slot structs that contain a `value` member that can be used to read or write.
 *
 * Example usage to set ERC1967 implementation slot:
 * ```
 * contract ERC1967 {
 *     bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
 *
 *     function _getImplementation() internal view returns (address) {
 *         return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;
 *     }
 *
 *     function _setImplementation(address newImplementation) internal {
 *         require(Address.isContract(newImplementation), "ERC1967: new implementation is not a contract");
 *         StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
 *     }
 * }
 * ```
 *
 * _Available since v4.1 for `address`, `bool`, `bytes32`, and `uint256`._
 */
library StorageSlot {
    struct AddressSlot {
        address value;
    }

    struct BooleanSlot {
        bool value;
    }

    struct Bytes32Slot {
        bytes32 value;
    }

    struct Uint256Slot {
        uint256 value;
    }

    /**
     * @dev Returns an `AddressSlot` with member `value` located at `slot`.
     */
    function getAddressSlot(bytes32 slot) internal pure returns (AddressSlot storage r) {
        /// @solidity memory-safe-assembly
        assembly {
            r.slot := slot
        }
    }

    /**
     * @dev Returns an `BooleanSlot` with member `value` located at `slot`.
     */
    function getBooleanSlot(bytes32 slot) internal pure returns (BooleanSlot storage r) {
        /// @solidity memory-safe-assembly
        assembly {
            r.slot := slot
        }
    }

    /**
     * @dev Returns an `Bytes32Slot` with member `value` located at `slot`.
     */
    function getBytes32Slot(bytes32 slot) internal pure returns (Bytes32Slot storage r) {
        /// @solidity memory-safe-assembly
        assembly {
            r.slot := slot
        }
    }

    /**
     * @dev Returns an `Uint256Slot` with member `value` located at `slot`.
     */
    function getUint256Slot(bytes32 slot) internal pure returns (Uint256Slot storage r) {
        /// @solidity memory-safe-assembly
        assembly {
            r.slot := slot
        }
    }
}

File 20 of 22 : ICircleIntegration.sol
// SPDX-License-Identifier: Apache 2

pragma solidity ^0.8.13;

import {IWormhole} from "../interfaces/IWormhole.sol";
import {ICircleBridge} from "../interfaces/ICircleBridge.sol";
import {IMessageTransmitter} from "../interfaces/IMessageTransmitter.sol";

interface ICircleIntegration {
    struct TransferParameters {
        address token;
        uint256 amount;
        uint16 targetChain;
        bytes32 mintRecipient;
    }

    struct RedeemParameters {
        bytes encodedWormholeMessage;
        bytes circleBridgeMessage;
        bytes circleAttestation;
    }

    struct DepositWithPayload {
        bytes32 token;
        uint256 amount;
        uint32 sourceDomain;
        uint32 targetDomain;
        uint64 nonce;
        bytes32 fromAddress;
        bytes32 mintRecipient;
        bytes payload;
    }

    function transferTokensWithPayload(
        TransferParameters memory transferParams,
        uint32 batchId,
        bytes memory payload
    ) external payable returns (uint64 messageSequence);

    function redeemTokensWithPayload(
        RedeemParameters memory params
    ) external returns (DepositWithPayload memory depositWithPayload);

    function fetchLocalTokenAddress(
        uint32 sourceDomain,
        bytes32 sourceToken
    ) external view returns (bytes32);

    function encodeDepositWithPayload(DepositWithPayload memory message) external pure returns (bytes memory);

    function decodeDepositWithPayload(bytes memory encoded) external pure returns (DepositWithPayload memory message);

    function isInitialized(address impl) external view returns (bool);

    function wormhole() external view returns (IWormhole);

    function chainId() external view returns (uint16);

    function wormholeFinality() external view returns (uint8);

    function circleBridge() external view returns (ICircleBridge);

    function circleTransmitter() external view returns (IMessageTransmitter);

    function getRegisteredEmitter(uint16 emitterChainId) external view returns (bytes32);

    function isAcceptedToken(address token) external view returns (bool);

    function getDomainFromChainId(uint16 chainId_) external view returns (uint32);

    function getChainIdFromDomain(uint32 domain) external view returns (uint16);

    function isMessageConsumed(bytes32 hash) external view returns (bool);

    function localDomain() external view returns (uint32);

    function targetAcceptedToken(address sourceToken, uint16 chainId_) external view returns (bytes32);

    function verifyGovernanceMessage(bytes memory encodedMessage, uint8 action)
        external
        view
        returns (bytes32 messageHash, bytes memory payload);

    function evmChain() external view returns (uint256);

    // guardian governance only
    function updateWormholeFinality(bytes memory encodedMessage) external;

    function registerEmitterAndDomain(bytes memory encodedMessage) external;

    function registerAcceptedToken(bytes memory encodedMessage) external;

    function registerTargetChainToken(bytes memory encodedMessage) external;

    function upgradeContract(bytes memory encodedMessage) external;
}

File 21 of 22 : ICircleBridge.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.0;

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

interface ICircleBridge {
    event MessageSent(bytes message);

    /**
     * @notice Deposits and burns tokens from sender to be minted on destination domain.
     * Emits a `DepositForBurn` event.
     * @dev reverts if:
     * - given burnToken is not supported
     * - given destinationDomain has no CircleBridge registered
     * - transferFrom() reverts. For example, if sender's burnToken balance or approved allowance
     * to this contract is less than `amount`.
     * - burn() reverts. For example, if `amount` is 0.
     * - MessageTransmitter returns false or reverts.
     * @param _amount amount of tokens to burn
     * @param _destinationDomain destination domain (ETH = 0, AVAX = 1)
     * @param _mintRecipient address of mint recipient on destination domain
     * @param _burnToken address of contract to burn deposited tokens, on local domain
     * @return _nonce unique nonce reserved by message
     */
    function depositForBurn(uint256 _amount, uint32 _destinationDomain, bytes32 _mintRecipient, address _burnToken)
        external
        returns (uint64 _nonce);

    /**
     * @notice Deposits and burns tokens from sender to be minted on destination domain. The mint
     * on the destination domain must be called by `_destinationCaller`.
     * WARNING: if the `_destinationCaller` does not represent a valid address as bytes32, then it will not be possible
     * to broadcast the message on the destination domain. This is an advanced feature, and the standard
     * depositForBurn() should be preferred for use cases where a specific destination caller is not required.
     * Emits a `DepositForBurn` event.
     * @dev reverts if:
     * - given destinationCaller is zero address
     * - given burnToken is not supported
     * - given destinationDomain has no CircleBridge registered
     * - transferFrom() reverts. For example, if sender's burnToken balance or approved allowance
     * to this contract is less than `amount`.
     * - burn() reverts. For example, if `amount` is 0.
     * - MessageTransmitter returns false or reverts.
     * @param _amount amount of tokens to burn
     * @param _destinationDomain destination domain
     * @param _mintRecipient address of mint recipient on destination domain
     * @param _burnToken address of contract to burn deposited tokens, on local domain
     * @param _destinationCaller caller on the destination domain, as bytes32
     * @return _nonce unique nonce reserved by message
     */
    function depositForBurnWithCaller(
        uint256 _amount,
        uint32 _destinationDomain,
        bytes32 _mintRecipient,
        address _burnToken,
        bytes32 _destinationCaller
    ) external returns (uint64 _nonce);

    function owner() external view returns (address);

    function handleReceiveMessage(uint32 _remoteDomain, bytes32 _sender, bytes memory messageBody)
        external
        view
        returns (bool);

    function localMessageTransmitter() external view returns (IMessageTransmitter);

    function remoteCircleBridges(uint32 domain) external view returns (bytes32);

    // owner only methods
    function transferOwnership(address newOwner) external;
}

File 22 of 22 : IMessageTransmitter.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.0;

interface IMessageTransmitter {
    /**
     * @notice Emitted when tokens are minted
     * @param _mintRecipient recipient address of minted tokens
     * @param _amount amount of minted tokens
     * @param _mintToken contract address of minted token
     */
    event MintAndWithdraw(address _mintRecipient, uint256 _amount, address _mintToken);

    /**
     * @notice Receive a message. Messages with a given nonce
     * can only be broadcast once for a (sourceDomain, destinationDomain)
     * pair. The message body of a valid message is passed to the
     * specified recipient for further processing.
     *
     * @dev Attestation format:
     * A valid attestation is the concatenated 65-byte signature(s) of exactly
     * `thresholdSignature` signatures, in increasing order of attester address.
     * ***If the attester addresses recovered from signatures are not in
     * increasing order, signature verification will fail.***
     * If incorrect number of signatures or duplicate signatures are supplied,
     * signature verification will fail.
     *
     * Message format:
     * Field Bytes Type Index
     * version 4 uint32 0
     * sourceDomain 4 uint32 4
     * destinationDomain 4 uint32 8
     * nonce 8 uint64 12
     * sender 32 bytes32 20
     * recipient 32 bytes32 52
     * messageBody dynamic bytes 84
     * @param _message Message bytes
     * @param _attestation Concatenated 65-byte signature(s) of `_message`, in increasing order
     * of the attester address recovered from signatures.
     * @return success bool, true if successful
     */
    function receiveMessage(bytes memory _message, bytes calldata _attestation) external returns (bool success);

    function attesterManager() external view returns (address);

    function availableNonces(uint32 domain) external view returns (uint64);

    function getNumEnabledAttesters() external view returns (uint256);

    function isEnabledAttester(address _attester) external view returns (bool);

    function localDomain() external view returns (uint32);

    function maxMessageBodySize() external view returns (uint256);

    function owner() external view returns (address);

    function paused() external view returns (bool);

    function pauser() external view returns (address);

    function rescuer() external view returns (address);

    function version() external view returns (uint32);

    // owner only methods
    function transferOwnership(address newOwner) external;

    function updateAttesterManager(address _newAttesterManager) external;

    // attester manager only methods
    function getEnabledAttester(uint256 _index) external view returns (address);

    function disableAttester(address _attester) external;

    function enableAttester(address _attester) external;

    function setSignatureThreshold(uint256 newSignatureThreshold) external;
}

Settings
{
  "remappings": [
    "@openzeppelin/=node_modules/@openzeppelin/",
    "@solidity-parser/=node_modules/@solidity-parser/",
    "ds-test/=lib/forge-std/lib/ds-test/src/",
    "forge-std/=lib/forge-std/src/",
    "wormhole/=lib/wormhole/ethereum/contracts/",
    "wormhole-solidity/=modules/src/"
  ],
  "optimizer": {
    "enabled": true,
    "runs": 200
  },
  "metadata": {
    "useLiteralContent": false,
    "bytecodeHash": "ipfs",
    "appendCBOR": true
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "evmVersion": "paris",
  "viaIR": true,
  "libraries": {}
}

Contract Security Audit

Contract ABI

[{"inputs":[{"internalType":"address","name":"circleIntegration_","type":"address"},{"internalType":"uint8","name":"nativeTokenDecimals_","type":"uint8"},{"internalType":"address","name":"feeRecipient_","type":"address"},{"internalType":"address","name":"ownerAssistant_","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"previousAdmin","type":"address"},{"indexed":false,"internalType":"address","name":"newAdmin","type":"address"}],"name":"AdminChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"beacon","type":"address"}],"name":"BeaconUpgraded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"oldRecipient","type":"address"},{"indexed":true,"internalType":"address","name":"newRecipient","type":"address"}],"name":"FeeRecipientUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"oldOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransfered","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"recipient","type":"address"},{"indexed":true,"internalType":"address","name":"relayer","type":"address"},{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"tokenAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"nativeAmount","type":"uint256"}],"name":"SwapExecuted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":true,"internalType":"uint256","name":"swapRate","type":"uint256"}],"name":"SwapRateUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"implementation","type":"address"}],"name":"Upgraded","type":"event"},{"inputs":[],"name":"VERSION","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"address_","type":"bytes32"}],"name":"bytes32ToAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"contract IERC20Metadata","name":"token","type":"address"}],"name":"calculateMaxSwapAmountIn","outputs":[{"internalType":"uint256","name":"maxAllowed","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IERC20Metadata","name":"token","type":"address"},{"internalType":"uint256","name":"toNativeAmount","type":"uint256"}],"name":"calculateNativeSwapAmountOut","outputs":[{"internalType":"uint256","name":"nativeAmount","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint16","name":"chainId_","type":"uint16"}],"name":"cancelOwnershipTransferRequest","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"chainId","outputs":[{"internalType":"uint16","name":"","type":"uint16"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"circleIntegration","outputs":[{"internalType":"contract ICircleIntegration","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"confirmOwnershipTransferRequest","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes","name":"encoded","type":"bytes"}],"name":"decodeTransferTokensWithRelay","outputs":[{"components":[{"internalType":"uint8","name":"payloadId","type":"uint8"},{"internalType":"uint256","name":"targetRelayerFee","type":"uint256"},{"internalType":"uint256","name":"toNativeTokenAmount","type":"uint256"},{"internalType":"bytes32","name":"targetRecipientWallet","type":"bytes32"}],"internalType":"struct CircleRelayerStructs.TransferTokensWithRelay","name":"transfer","type":"tuple"}],"stateMutability":"pure","type":"function"},{"inputs":[{"components":[{"internalType":"uint8","name":"payloadId","type":"uint8"},{"internalType":"uint256","name":"targetRelayerFee","type":"uint256"},{"internalType":"uint256","name":"toNativeTokenAmount","type":"uint256"},{"internalType":"bytes32","name":"targetRecipientWallet","type":"bytes32"}],"internalType":"struct CircleRelayerStructs.TransferTokensWithRelay","name":"transfer","type":"tuple"}],"name":"encodeTransferTokensWithRelay","outputs":[{"internalType":"bytes","name":"encoded","type":"bytes"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"feeRecipient","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getPaused","outputs":[{"internalType":"bool","name":"paused","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint16","name":"emitterChainId","type":"uint16"}],"name":"getRegisteredContract","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"maxNativeSwapAmount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"nativeSwapRate","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"nativeSwapRatePrecision","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"nativeTokenDecimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"ownerAssistant","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pendingOwner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"bytes","name":"encodedWormholeMessage","type":"bytes"},{"internalType":"bytes","name":"circleBridgeMessage","type":"bytes"},{"internalType":"bytes","name":"circleAttestation","type":"bytes"}],"internalType":"struct ICircleIntegration.RedeemParameters","name":"redeemParams","type":"tuple"}],"name":"redeemTokens","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint16","name":"chainId_","type":"uint16"},{"internalType":"bytes32","name":"contractAddress","type":"bytes32"}],"name":"registerContract","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint16","name":"chainId_","type":"uint16"},{"internalType":"address","name":"token","type":"address"}],"name":"relayerFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint16","name":"chainId_","type":"uint16"},{"internalType":"bool","name":"paused","type":"bool"}],"name":"setPauseForTransfers","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint16","name":"chainId_","type":"uint16"},{"internalType":"address","name":"newOwner","type":"address"}],"name":"submitOwnershipTransferRequest","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IERC20Metadata","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"toNativeTokenAmount","type":"uint256"},{"internalType":"uint16","name":"targetChain","type":"uint16"},{"internalType":"bytes32","name":"targetRecipientWallet","type":"bytes32"}],"name":"transferTokensWithRelay","outputs":[{"internalType":"uint64","name":"messageSequence","type":"uint64"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint16","name":"chainId_","type":"uint16"},{"internalType":"address","name":"newFeeRecipient","type":"address"}],"name":"updateFeeRecipient","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint16","name":"chainId_","type":"uint16"},{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"maxAmount","type":"uint256"}],"name":"updateMaxNativeSwapAmount","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint16","name":"chainId_","type":"uint16"},{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"swapRate","type":"uint256"}],"name":"updateNativeSwapRate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint16","name":"chainId_","type":"uint16"},{"internalType":"uint256","name":"nativeSwapRatePrecision_","type":"uint256"}],"name":"updateNativeSwapRatePrecision","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint16","name":"chainId_","type":"uint16"},{"internalType":"address","name":"newAssistant","type":"address"}],"name":"updateOwnerAssistant","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint16","name":"chainId_","type":"uint16"},{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"updateRelayerFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"wormhole","outputs":[{"internalType":"contract IWormhole","name":"","type":"address"}],"stateMutability":"view","type":"function"}]

6080604090808252346200034a5760808162002a5a80380380916200002582856200034f565b8339810103126200034a576200003b8162000389565b6020808301519260ff84168085036200034a576200006960606200006188850162000389565b930162000389565b6001600b556001600160a01b03949092908516918215620002fb5715620002b7578416918215620002735784169081156200022f57600092835490640100000000600160c01b0333871b169362ff000060018060a01b031999858b600554161760055560101b1691828663ff010000600160c01b0319861617178755896003541617600355886002541617600255885193634d4502c960e11b85528685600481875afa94851562000225579087949392918796620001d5575b5061ffff600496169263ff000001600160c01b03191617171784558751928380926384acd1bb60e01b82525afa928315620001cb57829362000182575b505050169060045416176004556305f5e100600655516126bb90816200039f8239f35b9080929350813d8311620001c3575b6200019d81836200034f565b81010312620001bf5751908282168203620001bc57503880806200015f565b80fd5b5080fd5b503d62000191565b86513d84823e3d90fd5b8092949650859193953d83116200021d575b620001f381836200034f565b8101031262000219575161ffff8116810362000219579386939092909161ffff62000122565b8580fd5b503d620001e7565b8a513d88823e3d90fd5b865162461bcd60e51b815260048101859052601760248201527f696e76616c6964206f776e657220617373697374616e740000000000000000006044820152606490fd5b865162461bcd60e51b815260048101859052601d60248201527f696e76616c69642066656520726563697069656e7420616464726573730000006044820152606490fd5b865162461bcd60e51b815260048101859052601760248201527f696e76616c6964206e617469766520646563696d616c730000000000000000006044820152606490fd5b875162461bcd60e51b815260048101869052602260248201527f696e76616c696420636972636c6520696e746567726174696f6e206164647265604482015261737360f01b6064820152608490fd5b600080fd5b601f909101601f19168101906001600160401b038211908210176200037357604052565b634e487b7160e01b600052604160045260246000fd5b51906001600160a01b03821682036200034a5756fe6080604052600436101561001257600080fd5b60003560e01c8063038c0b661461022757806308604985146102225780630a55d7351461021d5780630e1c863e1461021857806313d8900b1461021357806316fb440a1461020e5780631a28219514610209578063203c5095146102045780632a88b425146101ff5780632efe4f42146101fa57806332d9965a146101f55780633d62cca0146101f057806346904840146101eb57806351e2d7b5146101e657806359b87d8e146101e15780635ced058e146101dc5780636805b84b146101d757806368aa9ef4146101d25780636ddbaff3146101cd57806384acd1bb146101c85780638da5cb5b146101c35780638e151dd1146101be57806394cc743d146101b95780639a8a0592146101b4578063a2f32c8f146101af578063ad48cb5e146101aa578063b5419523146101a5578063cd601c78146101a0578063dd6522aa1461019b578063e30c397814610196578063ea1d2e4a14610191578063ffa1ad741461018c5763fff982a81461018757600080fd5b6111fe565b6111b8565b611113565b6110ea565b6110c1565b61109a565b61105d565b611039565b610feb565b610fc9565b610f31565b610efe565b610ed5565b610eac565b610e4a565b610dce565b610da8565b610d81565b610b11565b610a38565b610a0f565b6109d6565b6109b8565b61097b565b6108a1565b6107ca565b610758565b61070a565b610635565b610423565b6103ad565b610316565b3461030557600080600319360112610302576001546001600160a01b0390811690338290036102bd57825460201c6001600160a01b031660008054640100000000600160c01b031916602085901b640100000000600160c01b0316179055600180546001600160a01b0319169055167f0d18b5fd22306e373229b9439188228edca81207d1667f604daf6cef8aa3ee678380a380f35b60405162461bcd60e51b815260206004820152601b60248201527f63616c6c6572206d7573742062652070656e64696e674f776e657200000000006044820152606490fd5b80fd5b600080fd5b61ffff81160361030557565b34610305576040366003190112610305576004356103338161030a565b600054602435916103659190610357602083901c6001600160a01b03163314612196565b61ffff8091169116146121d9565b801561037057600655005b60405162461bcd60e51b81526020600482015260156024820152740707265636973696f6e206d757374206265203e203605c1b6044820152606490fd5b60031960203682011261030557600435906001600160401b038211610305576060908236030112610305576103e490600401611aa2565b005b6001600160a01b0381160361030557565b60609060031901126103055760043561040f8161030a565b9060243561041c816103e6565b9060443590565b3461030557610431366103f7565b9061046760005461045860018060a01b0333818460201c1614908115610550575b5061226f565b61ffff858116911614156122c5565b61048a6104828461ffff16600052600a602052604060002090565b541515612301565b6005546104ad906104a1906001600160a01b031681565b6001600160a01b031690565b604051633b6e750f60e01b81526001600160a01b03831660048201529390602090859060249082905afa93841561054b576103e4946104f49160009161051d575b50612346565b61ffff1660009081526007602090815260408083206001600160a01b0390941683529290522055565b61053e915060203d8111610544575b61053681836105ca565b81019061160e565b386104ee565b503d61052c565b61149d565b600254163314905038610452565b634e487b7160e01b600052604160045260246000fd5b608081019081106001600160401b0382111761058f57604052565b61055e565b604081019081106001600160401b0382111761058f57604052565b60a081019081106001600160401b0382111761058f57604052565b90601f801991011681019081106001600160401b0382111761058f57604052565b604051906105f882610574565b565b6040519061010082018281106001600160401b0382111761058f57604052565b6001600160401b03811161058f57601f01601f191660200190565b34610305576020366003190112610305576004356001600160401b0381116103055736602382011215610305578060040135906106718261061a565b61067e60405191826105ca565b82815236602484840101116103055760006020846106de9560246106aa96018386013783010152612514565b60405191829182919091606080608083019460ff815116845260208101516020850152604081015160408501520151910152565b0390f35b6040906003190112610305576004356106fa8161030a565b90602435610707816103e6565b90565b3461030557602061074561ffff610720366106e2565b91166000526007835260406000209060018060a01b0316600052602052604060002090565b54604051908152f35b8015150361030557565b34610305576040366003190112610305576004356107758161030a565b63ff0000006024356107868161074e565b600054926107b4906107a6602086901c6001600160a01b03163314612196565b61ffff8581169116146121d9565b63ff0000001990921691151560181b1617600055005b34610305576107d8366106e2565b60005490916001600160a01b03916107fb91610357602083901c85163314612196565b80821691821561084c57600380546001600160a01b031981166001600160a01b039384161790915516167faaebcf1bfa00580e41d966056b48521fa9f202645c86d4ddf28113e617c1b1d3600080a3005b60405162461bcd60e51b815260206004820152602760248201527f6e6577466565526563697069656e742063616e6e6f7420657175616c206164646044820152667265737328302960c81b6064820152608490fd5b34610305576108af366103f7565b90916000926108d9845460018060a01b039333858360201c1614801561096e575b6103579061226f565b600554604051633b6e750f60e01b81526001600160a01b0383811660048301529091602091839160249183918816165afa801561054b5761092091869161051d5750612346565b61092b831515612387565b6001600160a01b0381166000908152600860205260409020839055167fc6eb9fb936b61b402d503deeffc822f46492e15c2c8f079815cc4850ad7b02b08380a380f35b50600254851633146108d0565b3461030557602036600319011261030557600435610998816103e6565b60018060a01b031660005260096020526020604060002054604051908152f35b34610305576000366003190112610305576020600654604051908152f35b346103055760203660031901126103055761ffff6004356109f68161030a565b16600052600a6020526020604060002054604051908152f35b34610305576000366003190112610305576003546040516001600160a01b039091168152602090f35b3461030557604036600319011261030557600435610a558161030a565b6000549060243590610a75602084901c6001600160a01b03163314612196565b8115610abc57610a9b6103e49361ffff808416908115159283610aaf575b505050612213565b61ffff16600052600a602052604060002055565b1614159050388080610a93565b60405162461bcd60e51b815260206004820152602760248201527f636f6e7472616374416464726573732063616e6e6f7420657175616c206279746044820152666573333228302960c81b6064820152608490fd5b60a036600319011261030557600435610b29816103e6565b6024359060443560643591610b3d8361030a565b608435926002600b5414610d3c576002600b5560ff60005460181c16610d0357610b68851515611285565b610b738415156112c6565b6001600160a01b038281169590610b8b871515611312565b610ba38361ffff16600052600a602052604060002090565b549382610bb186151561135e565b610bba91612085565b9587610bd48561ffff166000526007602052604060002090565b6001600160a01b039091166000908152602091909152604090205495610bfa81886113ce565b8811610c05906113e0565b610c0d6105eb565b6001815296602088015260408701526060860152600554166001600160a01b03166001600160a01b031694610c43908688611523565b610c4b6105eb565b6001600160a01b039096168652602086015261ffff1660408501526060840152610c74906123d3565b6040516328a87c1360e21b81529283918291610c94919060048401611454565b03815a602094600091f1801561054b576106de91600091610cd5575b50610cbb6001600b55565b6040516001600160401b0390911681529081906020820190565b610cf6915060203d8111610cfc575b610cee81836105ca565b810190611440565b38610cb0565b503d610ce4565b60405162461bcd60e51b81526020600482015260116024820152701c995b185e595c881a5cc81c185d5cd959607a1b6044820152606490fd5b60405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c006044820152606490fd5b34610305576020366003190112610305576040516004356001600160a01b03168152602090f35b3461030557600036600319011261030557602060ff60005460181c166040519015158152f35b34610305576000366003190112610305576002546040516001600160a01b039091168152602090f35b60ff81160361030557565b60005b838110610e155750506000910152565b8181015183820152602001610e05565b90602091610e3e81518092818552858086019101610e02565b601f01601f1916010190565b34610305576080366003190112610305576106de610e98604051610e6d81610574565b600435610e7981610df7565b81526024356020820152604435604082015260643560608201526123d3565b604051918291602083526020830190610e25565b34610305576000366003190112610305576004546040516001600160a01b039091168152602090f35b3461030557600036600319011261030557600054604051602091821c6001600160a01b03168152f35b34610305576040366003190112610305576020610f29600435610f20816103e6565b60243590611fc0565b604051908152f35b3461030557610f3f366106e2565b6000546001600160a01b0392610f619190610357602083901c86163314612196565b168015610f85576bffffffffffffffffffffffff60a01b6001541617600155600080f35b606460405162461bcd60e51b815260206004820152602060248201527f6e65774f776e65722063616e6e6f7420657175616c20616464726573732830296044820152fd5b3461030557600036600319011261030557602061ffff60005416604051908152f35b346103055760203660031901126103055761102760043561100b8161030a565b60005490610357602083901c6001600160a01b03163314612196565b600180546001600160a01b0319169055005b3461030557600036600319011261030557602060ff60005460101c16604051908152f35b346103055760203660031901126103055760043561107a816103e6565b60018060a01b031660005260086020526020604060002054604051908152f35b34610305576020366003190112610305576020610f296004356110bc816103e6565b611e9e565b34610305576000366003190112610305576005546040516001600160a01b039091168152602090f35b34610305576000366003190112610305576001546040516001600160a01b039091168152602090f35b3461030557611121366106e2565b6000546001600160a01b03926111439190610357602083901c86163314612196565b168015611167576bffffffffffffffffffffffff60a01b6002541617600255600080f35b60405162461bcd60e51b8152602060048201526024808201527f6e6577417373697374616e742063616e6e6f7420657175616c206164647265736044820152637328302960e01b6064820152608490fd5b34610305576000366003190112610305576106de6040516111d881610594565b60058152640302e322e360dc1b6020820152604051918291602083526020830190610e25565b346103055761120c366103f7565b9061122b60005460018060a01b039461035733878460201c1614612196565b6020836005541693602460405180968193633b6e750f60e01b8352861660048301525afa92831561054b576103e49361126b9160009161051d5750612346565b6001600160a01b0316600090815260096020526040902055565b1561128c57565b60405162461bcd60e51b81526020600482015260126024820152710616d6f756e74206d757374206265203e20360741b6044820152606490fd5b156112cd57565b60405162461bcd60e51b815260206004820152601860248201527f696e76616c69642074617267657420726563697069656e7400000000000000006044820152606490fd5b1561131957565b60405162461bcd60e51b815260206004820152601d60248201527f746f6b656e2063616e6e6f7420657175616c20616464726573732830290000006044820152606490fd5b1561136557565b60405162461bcd60e51b815260206004820152602560248201527f434952434c452d52454c415945523a20746172676574206e6f742072656769736044820152641d195c995960da1b6064820152608490fd5b634e487b7160e01b600052601160045260246000fd5b919082018092116113db57565b6113b8565b156113e757565b60405162461bcd60e51b815260206004820152601b60248201527f696e73756666696369656e7420616d6f756e74526563656976656400000000006044820152606490fd5b51906001600160401b038216820361030557565b90816020910312610305576107079061142c565b60c0906060610707949360018060a01b0381511683526020810151602084015261ffff604082015116604084015201516060820152600060808201528160a08201520190610e25565b6040513d6000823e3d90fd5b90816020910312610305575190565b156114bf57565b60405162461bcd60e51b815260206004820152603660248201527f5361666545524332303a20617070726f76652066726f6d206e6f6e2d7a65726f60448201527520746f206e6f6e2d7a65726f20616c6c6f77616e636560501b6064820152608490fd5b91909181158015611580575b6105f89361153f61157b926114b8565b60405163095ea7b360e01b60208201526001600160a01b039091166024820152604481019390935282606481015b03601f1981018452836105ca565b611682565b50604051636eb1769f60e11b81523060048201526001600160a01b038416602482015292602084806044810103816001600160a01b0386165afa90811561054b5761153f61157b926105f8966000916115e0575b5015925050935061152f565b611601915060203d8111611607575b6115f981836105ca565b8101906114a9565b386115d4565b503d6115ef565b9081602091031261030557516107078161074e565b1561162a57565b60405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b6064820152608490fd5b6040516001600160a01b03919091169161169b82610594565b6020928383527f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c656484840152803b1561171257600082819282876116ed9796519301915af16116e7611757565b90611787565b805190816116fa57505050565b826105f89361170d93830101910161160e565b611623565b60405162461bcd60e51b815260048101859052601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606490fd5b3d15611782573d906117688261061a565b9161177660405193846105ca565b82523d6000602084013e565b606090565b90919015611793575090565b8151156117a35750805190602001fd5b60405162461bcd60e51b8152602060048201529081906117c7906024830190610e25565b0390fd5b519063ffffffff8216820361030557565b81601f820112156103055780516117f28161061a565b9261180060405194856105ca565b81845260208284010111610305576107079160208085019101610e02565b906020828203126103055781516001600160401b039283821161030557019061010082820312610305576118506105fa565b92825184526020830151602085015261186b604084016117cb565b604085015261187c606084016117cb565b606085015261188d6080840161142c565b608085015260a083015160a085015260c083015160c085015260e0830151908111610305576118bc92016117dc565b60e082015290565b9035601e19823603018112156103055701602081359101916001600160401b03821161030557813603831361030557565b908060209392818452848401376000828201840152601f01601f1916010190565b906060610707926020815261193e61192e84806118c4565b84602085015260808401916118f5565b9061197161196661195260208701876118c4565b601f198587038101604087015295916118f5565b9460408101906118c4565b939092828603019101526118f5565b9081602091031261030557516107078161030a565b1561199c57565b60405162461bcd60e51b815260206004820152602860248201527f66726f6d41646472657373206973206e6f74206120726567697374657265642060448201526718dbdb9d1c9858dd60c21b6064820152608490fd5b156119f957565b60405162461bcd60e51b815260206004820152602360248201527f726563697069656e742063616e6e6f742073776170206e61746976652061737360448201526265747360e81b6064820152608490fd5b15611a5157565b606460405162461bcd60e51b815260206004820152602060248201527f696e73756666696369656e74206e617469766520617373657420616d6f756e746044820152fd5b919082039182116113db57565b600554611adc906001600160a01b0390611abd9082166104a1565b90604051936357bf927b60e01b85528480600095869360048301611916565b038183865af1801561054b57611b44948491611d96575b50611b0160e0820151612514565b9260a0820151611b18604084015163ffffffff1690565b6040518093638cf67ba960e01b8252818060209b8c956004830191909163ffffffff6020820193169052565b03915afa91821561054b57611b7a92611b73918891611d69575b5061ffff16600052600a602052604060002090565b5414611995565b8051611b8e906001600160a01b03166104a1565b60608401519094906001600160a01b031693838516803314611d4e5760408201928351611bfb575b505095611bd0611bde9392826105f89901519051906113ce565b9182611be5575b0151611a95565b9216611db8565b611bf6838660035416878a16611db8565b611bd7565b9097919392611c0988611e9e565b80825111611d46575b50611c1e815189611fc0565b91821580611cf157611c3284341015611a4a565b818b611c3e8634611a95565b80158015611cc9575b50509091858391611cbf575b83928392f11561054b5782611bde95611bd0938a6105f89c7f764f0dc063c06f32d89a3f3af80c0db4be8a090901f589a478b447e0a51f09f18b875193611cb0604051928392169633968360209093929193604081019481520152565b0390a45b935098509293611bb6565b6108fc9150611c53565b839250829182918290611ce8575b3390f11561054b57818b3880611c47565b506108fc611cd7565b509150919293975080825234158015611d1a575b505090611bd0611bde9392826105f899611cb4565b8190819594939590611d3d575b81809134903390f11561054b5790913880611d05565b506108fc611d27565b815238611c12565b505050936105f894611d6034156119f2565b01519216611db8565b611d899150893d8b11611d8f575b611d8181836105ca565b810190611980565b38611b5e565b503d611d77565b611db291503d8086833e611daa81836105ca565b81019061181e565b38611af3565b60405163a9059cbb60e01b60208201526001600160a01b03909216602483015260448201929092526105f89161157b826064810161156d565b15611df857565b60405162461bcd60e51b81526020600482015260116024820152701cddd85c081c985d19481b9bdd081cd95d607a1b6044820152606490fd5b90816020910312610305575161070781610df7565b818102929181159184041417156113db57565b9060ff8091169116039060ff82116113db57565b60ff16604d81116113db57600a0a90565b8115611e88570490565b634e487b7160e01b600052601260045260246000fd5b6001600160a01b031660008181526008602052604090205490611ec2821515611df1565b60405163313ce56760e01b815291602083600481855afa92831561054b57600093611f90575b5060ff60005460101c16918260ff851611600014611f4e57611f3a611f4593611f346107079694611f2e611f3f9560018060a01b03166000526009602052604060002090565b54611e46565b93611e59565b611e6d565b90611e46565b60065490611e7e565b611f3a61070794611f7b611f8a94611f2e611f819560018060a01b03166000526009602052604060002090565b94611e59565b60065490611e46565b90611e7e565b611fb291935060203d8111611fb9575b611faa81836105ca565b810190611e31565b9138611ee8565b503d611fa0565b6001600160a01b0316600081815260086020908152604090912060049291905491611fec831515611df1565b60405163313ce56760e01b815293849182905afa91821561054b57600092612065575b5060ff60005460101c168060ff8416116000146120465790611f3f611f3a611f8a9361204061070797600654611e46565b95611e59565b611f3f611f3a6120609394611f3461070797600654611e46565b611e7e565b61207e91925060203d8111611fb957611faa81836105ca565b903861200f565b6040516370a0823160e01b808252306004830152602093926001600160a01b031691908482602481865afa93841561054b57859260009561211b575b506120ce9030338661213e565b60405190815230600482015291829060249082905afa90811561054b57610707936000926120fe575b5050611a95565b6121149250803d10611607576115f981836105ca565b38806120f7565b6120ce91955061213790843d8611611607576115f981836105ca565b94906120c1565b6040516323b872dd60e01b60208201526001600160a01b03928316602482015292909116604483015260648083019390935291815260a08101918183106001600160401b0384111761058f576105f892604052611682565b1561219d57565b60405162461bcd60e51b815260206004820152601460248201527331b0b63632b9103737ba103a34329037bbb732b960611b6044820152606490fd5b156121e057565b60405162461bcd60e51b815260206004820152600b60248201526a3bb937b7339031b430b4b760a91b6044820152606490fd5b1561221a57565b60405162461bcd60e51b815260206004820152602760248201527f636861696e49645f2063616e6e6f7420657175616c2030206f7220746869732060448201526618da185a5b925960ca1b6064820152608490fd5b1561227657565b60405162461bcd60e51b815260206004820152602160248201527f63616c6c6572206e6f7420746865206f776e6572206f7220617373697374616e6044820152601d60fa1b6064820152608490fd5b156122cc57565b60405162461bcd60e51b815260206004820152600d60248201526c34b73b30b634b21031b430b4b760991b6044820152606490fd5b1561230857565b60405162461bcd60e51b815260206004820152601660248201527518dbdb9d1c9858dd08191bd95cdb89dd08195e1a5cdd60521b6044820152606490fd5b1561234d57565b60405162461bcd60e51b81526020600482015260126024820152711d1bdad95b881b9bdd081858d8d95c1d195960721b6044820152606490fd5b1561238e57565b60405162461bcd60e51b815260206004820152601960248201527f737761702072617465206d757374206265206e6f6e7a65726f000000000000006044820152606490fd5b600160ff825116036124225780519060208101519060606040820151910151916040519360ff60f81b9060f81b16602085015260218401526041830152606182015260618152610707816105af565b60405162461bcd60e51b81526020600482015260116024820152701a5b9d985b1a59081c185e5b1bd8591259607a1b6044820152606490fd5b1561246257565b60405162461bcd60e51b815260206004820152602960248201527f434952434c455f52454c415945523a20696e76616c6964206d657373616765206044820152681c185e5b1bd859125960ba1b6064820152608490fd5b156124c057565b60405162461bcd60e51b815260206004820152602660248201527f434952434c455f52454c415945523a20696e76616c6964206d657373616765206044820152650d8cadccee8d60d31b6064820152608490fd5b9060405161252181610574565b600081526020810192600084526040820160008152606083019060008252839560018451106125a057612576600160ff61256f6105f898612568848a0151829060ff169052565b5160ff1690565b161461245b565b61257f846125db565b905261258a83612628565b905261259582612638565b9052516061146124b9565b60405162461bcd60e51b8152602060048201526013602482015272746f55696e74385f6f75744f66426f756e647360681b6044820152606490fd5b60218151106125eb576021015190565b60405162461bcd60e51b8152602060048201526015602482015274746f55696e743235365f6f75744f66426f756e647360581b6044820152606490fd5b60418151106125eb576041015190565b6061815110612648576061015190565b60405162461bcd60e51b8152602060048201526015602482015274746f427974657333325f6f75744f66426f756e647360581b6044820152606490fdfea26469706673582212206e8d4b23c51fbffc0844e314ca12b37f3c806c949dc63c336ab8940098bf923b64736f6c634300081300330000000000000000000000002703483b1a5a7c577e8680de9df8be03c6f30e3c000000000000000000000000000000000000000000000000000000000000001200000000000000000000000053207e216540125e322cda8a693b0b89576deb460000000000000000000000008c16639239e2b5de89e9bffe8f7e0d0317a19503

Deployed Bytecode

0x6080604052600436101561001257600080fd5b60003560e01c8063038c0b661461022757806308604985146102225780630a55d7351461021d5780630e1c863e1461021857806313d8900b1461021357806316fb440a1461020e5780631a28219514610209578063203c5095146102045780632a88b425146101ff5780632efe4f42146101fa57806332d9965a146101f55780633d62cca0146101f057806346904840146101eb57806351e2d7b5146101e657806359b87d8e146101e15780635ced058e146101dc5780636805b84b146101d757806368aa9ef4146101d25780636ddbaff3146101cd57806384acd1bb146101c85780638da5cb5b146101c35780638e151dd1146101be57806394cc743d146101b95780639a8a0592146101b4578063a2f32c8f146101af578063ad48cb5e146101aa578063b5419523146101a5578063cd601c78146101a0578063dd6522aa1461019b578063e30c397814610196578063ea1d2e4a14610191578063ffa1ad741461018c5763fff982a81461018757600080fd5b6111fe565b6111b8565b611113565b6110ea565b6110c1565b61109a565b61105d565b611039565b610feb565b610fc9565b610f31565b610efe565b610ed5565b610eac565b610e4a565b610dce565b610da8565b610d81565b610b11565b610a38565b610a0f565b6109d6565b6109b8565b61097b565b6108a1565b6107ca565b610758565b61070a565b610635565b610423565b6103ad565b610316565b3461030557600080600319360112610302576001546001600160a01b0390811690338290036102bd57825460201c6001600160a01b031660008054640100000000600160c01b031916602085901b640100000000600160c01b0316179055600180546001600160a01b0319169055167f0d18b5fd22306e373229b9439188228edca81207d1667f604daf6cef8aa3ee678380a380f35b60405162461bcd60e51b815260206004820152601b60248201527f63616c6c6572206d7573742062652070656e64696e674f776e657200000000006044820152606490fd5b80fd5b600080fd5b61ffff81160361030557565b34610305576040366003190112610305576004356103338161030a565b600054602435916103659190610357602083901c6001600160a01b03163314612196565b61ffff8091169116146121d9565b801561037057600655005b60405162461bcd60e51b81526020600482015260156024820152740707265636973696f6e206d757374206265203e203605c1b6044820152606490fd5b60031960203682011261030557600435906001600160401b038211610305576060908236030112610305576103e490600401611aa2565b005b6001600160a01b0381160361030557565b60609060031901126103055760043561040f8161030a565b9060243561041c816103e6565b9060443590565b3461030557610431366103f7565b9061046760005461045860018060a01b0333818460201c1614908115610550575b5061226f565b61ffff858116911614156122c5565b61048a6104828461ffff16600052600a602052604060002090565b541515612301565b6005546104ad906104a1906001600160a01b031681565b6001600160a01b031690565b604051633b6e750f60e01b81526001600160a01b03831660048201529390602090859060249082905afa93841561054b576103e4946104f49160009161051d575b50612346565b61ffff1660009081526007602090815260408083206001600160a01b0390941683529290522055565b61053e915060203d8111610544575b61053681836105ca565b81019061160e565b386104ee565b503d61052c565b61149d565b600254163314905038610452565b634e487b7160e01b600052604160045260246000fd5b608081019081106001600160401b0382111761058f57604052565b61055e565b604081019081106001600160401b0382111761058f57604052565b60a081019081106001600160401b0382111761058f57604052565b90601f801991011681019081106001600160401b0382111761058f57604052565b604051906105f882610574565b565b6040519061010082018281106001600160401b0382111761058f57604052565b6001600160401b03811161058f57601f01601f191660200190565b34610305576020366003190112610305576004356001600160401b0381116103055736602382011215610305578060040135906106718261061a565b61067e60405191826105ca565b82815236602484840101116103055760006020846106de9560246106aa96018386013783010152612514565b60405191829182919091606080608083019460ff815116845260208101516020850152604081015160408501520151910152565b0390f35b6040906003190112610305576004356106fa8161030a565b90602435610707816103e6565b90565b3461030557602061074561ffff610720366106e2565b91166000526007835260406000209060018060a01b0316600052602052604060002090565b54604051908152f35b8015150361030557565b34610305576040366003190112610305576004356107758161030a565b63ff0000006024356107868161074e565b600054926107b4906107a6602086901c6001600160a01b03163314612196565b61ffff8581169116146121d9565b63ff0000001990921691151560181b1617600055005b34610305576107d8366106e2565b60005490916001600160a01b03916107fb91610357602083901c85163314612196565b80821691821561084c57600380546001600160a01b031981166001600160a01b039384161790915516167faaebcf1bfa00580e41d966056b48521fa9f202645c86d4ddf28113e617c1b1d3600080a3005b60405162461bcd60e51b815260206004820152602760248201527f6e6577466565526563697069656e742063616e6e6f7420657175616c206164646044820152667265737328302960c81b6064820152608490fd5b34610305576108af366103f7565b90916000926108d9845460018060a01b039333858360201c1614801561096e575b6103579061226f565b600554604051633b6e750f60e01b81526001600160a01b0383811660048301529091602091839160249183918816165afa801561054b5761092091869161051d5750612346565b61092b831515612387565b6001600160a01b0381166000908152600860205260409020839055167fc6eb9fb936b61b402d503deeffc822f46492e15c2c8f079815cc4850ad7b02b08380a380f35b50600254851633146108d0565b3461030557602036600319011261030557600435610998816103e6565b60018060a01b031660005260096020526020604060002054604051908152f35b34610305576000366003190112610305576020600654604051908152f35b346103055760203660031901126103055761ffff6004356109f68161030a565b16600052600a6020526020604060002054604051908152f35b34610305576000366003190112610305576003546040516001600160a01b039091168152602090f35b3461030557604036600319011261030557600435610a558161030a565b6000549060243590610a75602084901c6001600160a01b03163314612196565b8115610abc57610a9b6103e49361ffff808416908115159283610aaf575b505050612213565b61ffff16600052600a602052604060002055565b1614159050388080610a93565b60405162461bcd60e51b815260206004820152602760248201527f636f6e7472616374416464726573732063616e6e6f7420657175616c206279746044820152666573333228302960c81b6064820152608490fd5b60a036600319011261030557600435610b29816103e6565b6024359060443560643591610b3d8361030a565b608435926002600b5414610d3c576002600b5560ff60005460181c16610d0357610b68851515611285565b610b738415156112c6565b6001600160a01b038281169590610b8b871515611312565b610ba38361ffff16600052600a602052604060002090565b549382610bb186151561135e565b610bba91612085565b9587610bd48561ffff166000526007602052604060002090565b6001600160a01b039091166000908152602091909152604090205495610bfa81886113ce565b8811610c05906113e0565b610c0d6105eb565b6001815296602088015260408701526060860152600554166001600160a01b03166001600160a01b031694610c43908688611523565b610c4b6105eb565b6001600160a01b039096168652602086015261ffff1660408501526060840152610c74906123d3565b6040516328a87c1360e21b81529283918291610c94919060048401611454565b03815a602094600091f1801561054b576106de91600091610cd5575b50610cbb6001600b55565b6040516001600160401b0390911681529081906020820190565b610cf6915060203d8111610cfc575b610cee81836105ca565b810190611440565b38610cb0565b503d610ce4565b60405162461bcd60e51b81526020600482015260116024820152701c995b185e595c881a5cc81c185d5cd959607a1b6044820152606490fd5b60405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c006044820152606490fd5b34610305576020366003190112610305576040516004356001600160a01b03168152602090f35b3461030557600036600319011261030557602060ff60005460181c166040519015158152f35b34610305576000366003190112610305576002546040516001600160a01b039091168152602090f35b60ff81160361030557565b60005b838110610e155750506000910152565b8181015183820152602001610e05565b90602091610e3e81518092818552858086019101610e02565b601f01601f1916010190565b34610305576080366003190112610305576106de610e98604051610e6d81610574565b600435610e7981610df7565b81526024356020820152604435604082015260643560608201526123d3565b604051918291602083526020830190610e25565b34610305576000366003190112610305576004546040516001600160a01b039091168152602090f35b3461030557600036600319011261030557600054604051602091821c6001600160a01b03168152f35b34610305576040366003190112610305576020610f29600435610f20816103e6565b60243590611fc0565b604051908152f35b3461030557610f3f366106e2565b6000546001600160a01b0392610f619190610357602083901c86163314612196565b168015610f85576bffffffffffffffffffffffff60a01b6001541617600155600080f35b606460405162461bcd60e51b815260206004820152602060248201527f6e65774f776e65722063616e6e6f7420657175616c20616464726573732830296044820152fd5b3461030557600036600319011261030557602061ffff60005416604051908152f35b346103055760203660031901126103055761102760043561100b8161030a565b60005490610357602083901c6001600160a01b03163314612196565b600180546001600160a01b0319169055005b3461030557600036600319011261030557602060ff60005460101c16604051908152f35b346103055760203660031901126103055760043561107a816103e6565b60018060a01b031660005260086020526020604060002054604051908152f35b34610305576020366003190112610305576020610f296004356110bc816103e6565b611e9e565b34610305576000366003190112610305576005546040516001600160a01b039091168152602090f35b34610305576000366003190112610305576001546040516001600160a01b039091168152602090f35b3461030557611121366106e2565b6000546001600160a01b03926111439190610357602083901c86163314612196565b168015611167576bffffffffffffffffffffffff60a01b6002541617600255600080f35b60405162461bcd60e51b8152602060048201526024808201527f6e6577417373697374616e742063616e6e6f7420657175616c206164647265736044820152637328302960e01b6064820152608490fd5b34610305576000366003190112610305576106de6040516111d881610594565b60058152640302e322e360dc1b6020820152604051918291602083526020830190610e25565b346103055761120c366103f7565b9061122b60005460018060a01b039461035733878460201c1614612196565b6020836005541693602460405180968193633b6e750f60e01b8352861660048301525afa92831561054b576103e49361126b9160009161051d5750612346565b6001600160a01b0316600090815260096020526040902055565b1561128c57565b60405162461bcd60e51b81526020600482015260126024820152710616d6f756e74206d757374206265203e20360741b6044820152606490fd5b156112cd57565b60405162461bcd60e51b815260206004820152601860248201527f696e76616c69642074617267657420726563697069656e7400000000000000006044820152606490fd5b1561131957565b60405162461bcd60e51b815260206004820152601d60248201527f746f6b656e2063616e6e6f7420657175616c20616464726573732830290000006044820152606490fd5b1561136557565b60405162461bcd60e51b815260206004820152602560248201527f434952434c452d52454c415945523a20746172676574206e6f742072656769736044820152641d195c995960da1b6064820152608490fd5b634e487b7160e01b600052601160045260246000fd5b919082018092116113db57565b6113b8565b156113e757565b60405162461bcd60e51b815260206004820152601b60248201527f696e73756666696369656e7420616d6f756e74526563656976656400000000006044820152606490fd5b51906001600160401b038216820361030557565b90816020910312610305576107079061142c565b60c0906060610707949360018060a01b0381511683526020810151602084015261ffff604082015116604084015201516060820152600060808201528160a08201520190610e25565b6040513d6000823e3d90fd5b90816020910312610305575190565b156114bf57565b60405162461bcd60e51b815260206004820152603660248201527f5361666545524332303a20617070726f76652066726f6d206e6f6e2d7a65726f60448201527520746f206e6f6e2d7a65726f20616c6c6f77616e636560501b6064820152608490fd5b91909181158015611580575b6105f89361153f61157b926114b8565b60405163095ea7b360e01b60208201526001600160a01b039091166024820152604481019390935282606481015b03601f1981018452836105ca565b611682565b50604051636eb1769f60e11b81523060048201526001600160a01b038416602482015292602084806044810103816001600160a01b0386165afa90811561054b5761153f61157b926105f8966000916115e0575b5015925050935061152f565b611601915060203d8111611607575b6115f981836105ca565b8101906114a9565b386115d4565b503d6115ef565b9081602091031261030557516107078161074e565b1561162a57565b60405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b6064820152608490fd5b6040516001600160a01b03919091169161169b82610594565b6020928383527f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c656484840152803b1561171257600082819282876116ed9796519301915af16116e7611757565b90611787565b805190816116fa57505050565b826105f89361170d93830101910161160e565b611623565b60405162461bcd60e51b815260048101859052601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606490fd5b3d15611782573d906117688261061a565b9161177660405193846105ca565b82523d6000602084013e565b606090565b90919015611793575090565b8151156117a35750805190602001fd5b60405162461bcd60e51b8152602060048201529081906117c7906024830190610e25565b0390fd5b519063ffffffff8216820361030557565b81601f820112156103055780516117f28161061a565b9261180060405194856105ca565b81845260208284010111610305576107079160208085019101610e02565b906020828203126103055781516001600160401b039283821161030557019061010082820312610305576118506105fa565b92825184526020830151602085015261186b604084016117cb565b604085015261187c606084016117cb565b606085015261188d6080840161142c565b608085015260a083015160a085015260c083015160c085015260e0830151908111610305576118bc92016117dc565b60e082015290565b9035601e19823603018112156103055701602081359101916001600160401b03821161030557813603831361030557565b908060209392818452848401376000828201840152601f01601f1916010190565b906060610707926020815261193e61192e84806118c4565b84602085015260808401916118f5565b9061197161196661195260208701876118c4565b601f198587038101604087015295916118f5565b9460408101906118c4565b939092828603019101526118f5565b9081602091031261030557516107078161030a565b1561199c57565b60405162461bcd60e51b815260206004820152602860248201527f66726f6d41646472657373206973206e6f74206120726567697374657265642060448201526718dbdb9d1c9858dd60c21b6064820152608490fd5b156119f957565b60405162461bcd60e51b815260206004820152602360248201527f726563697069656e742063616e6e6f742073776170206e61746976652061737360448201526265747360e81b6064820152608490fd5b15611a5157565b606460405162461bcd60e51b815260206004820152602060248201527f696e73756666696369656e74206e617469766520617373657420616d6f756e746044820152fd5b919082039182116113db57565b600554611adc906001600160a01b0390611abd9082166104a1565b90604051936357bf927b60e01b85528480600095869360048301611916565b038183865af1801561054b57611b44948491611d96575b50611b0160e0820151612514565b9260a0820151611b18604084015163ffffffff1690565b6040518093638cf67ba960e01b8252818060209b8c956004830191909163ffffffff6020820193169052565b03915afa91821561054b57611b7a92611b73918891611d69575b5061ffff16600052600a602052604060002090565b5414611995565b8051611b8e906001600160a01b03166104a1565b60608401519094906001600160a01b031693838516803314611d4e5760408201928351611bfb575b505095611bd0611bde9392826105f89901519051906113ce565b9182611be5575b0151611a95565b9216611db8565b611bf6838660035416878a16611db8565b611bd7565b9097919392611c0988611e9e565b80825111611d46575b50611c1e815189611fc0565b91821580611cf157611c3284341015611a4a565b818b611c3e8634611a95565b80158015611cc9575b50509091858391611cbf575b83928392f11561054b5782611bde95611bd0938a6105f89c7f764f0dc063c06f32d89a3f3af80c0db4be8a090901f589a478b447e0a51f09f18b875193611cb0604051928392169633968360209093929193604081019481520152565b0390a45b935098509293611bb6565b6108fc9150611c53565b839250829182918290611ce8575b3390f11561054b57818b3880611c47565b506108fc611cd7565b509150919293975080825234158015611d1a575b505090611bd0611bde9392826105f899611cb4565b8190819594939590611d3d575b81809134903390f11561054b5790913880611d05565b506108fc611d27565b815238611c12565b505050936105f894611d6034156119f2565b01519216611db8565b611d899150893d8b11611d8f575b611d8181836105ca565b810190611980565b38611b5e565b503d611d77565b611db291503d8086833e611daa81836105ca565b81019061181e565b38611af3565b60405163a9059cbb60e01b60208201526001600160a01b03909216602483015260448201929092526105f89161157b826064810161156d565b15611df857565b60405162461bcd60e51b81526020600482015260116024820152701cddd85c081c985d19481b9bdd081cd95d607a1b6044820152606490fd5b90816020910312610305575161070781610df7565b818102929181159184041417156113db57565b9060ff8091169116039060ff82116113db57565b60ff16604d81116113db57600a0a90565b8115611e88570490565b634e487b7160e01b600052601260045260246000fd5b6001600160a01b031660008181526008602052604090205490611ec2821515611df1565b60405163313ce56760e01b815291602083600481855afa92831561054b57600093611f90575b5060ff60005460101c16918260ff851611600014611f4e57611f3a611f4593611f346107079694611f2e611f3f9560018060a01b03166000526009602052604060002090565b54611e46565b93611e59565b611e6d565b90611e46565b60065490611e7e565b611f3a61070794611f7b611f8a94611f2e611f819560018060a01b03166000526009602052604060002090565b94611e59565b60065490611e46565b90611e7e565b611fb291935060203d8111611fb9575b611faa81836105ca565b810190611e31565b9138611ee8565b503d611fa0565b6001600160a01b0316600081815260086020908152604090912060049291905491611fec831515611df1565b60405163313ce56760e01b815293849182905afa91821561054b57600092612065575b5060ff60005460101c168060ff8416116000146120465790611f3f611f3a611f8a9361204061070797600654611e46565b95611e59565b611f3f611f3a6120609394611f3461070797600654611e46565b611e7e565b61207e91925060203d8111611fb957611faa81836105ca565b903861200f565b6040516370a0823160e01b808252306004830152602093926001600160a01b031691908482602481865afa93841561054b57859260009561211b575b506120ce9030338661213e565b60405190815230600482015291829060249082905afa90811561054b57610707936000926120fe575b5050611a95565b6121149250803d10611607576115f981836105ca565b38806120f7565b6120ce91955061213790843d8611611607576115f981836105ca565b94906120c1565b6040516323b872dd60e01b60208201526001600160a01b03928316602482015292909116604483015260648083019390935291815260a08101918183106001600160401b0384111761058f576105f892604052611682565b1561219d57565b60405162461bcd60e51b815260206004820152601460248201527331b0b63632b9103737ba103a34329037bbb732b960611b6044820152606490fd5b156121e057565b60405162461bcd60e51b815260206004820152600b60248201526a3bb937b7339031b430b4b760a91b6044820152606490fd5b1561221a57565b60405162461bcd60e51b815260206004820152602760248201527f636861696e49645f2063616e6e6f7420657175616c2030206f7220746869732060448201526618da185a5b925960ca1b6064820152608490fd5b1561227657565b60405162461bcd60e51b815260206004820152602160248201527f63616c6c6572206e6f7420746865206f776e6572206f7220617373697374616e6044820152601d60fa1b6064820152608490fd5b156122cc57565b60405162461bcd60e51b815260206004820152600d60248201526c34b73b30b634b21031b430b4b760991b6044820152606490fd5b1561230857565b60405162461bcd60e51b815260206004820152601660248201527518dbdb9d1c9858dd08191bd95cdb89dd08195e1a5cdd60521b6044820152606490fd5b1561234d57565b60405162461bcd60e51b81526020600482015260126024820152711d1bdad95b881b9bdd081858d8d95c1d195960721b6044820152606490fd5b1561238e57565b60405162461bcd60e51b815260206004820152601960248201527f737761702072617465206d757374206265206e6f6e7a65726f000000000000006044820152606490fd5b600160ff825116036124225780519060208101519060606040820151910151916040519360ff60f81b9060f81b16602085015260218401526041830152606182015260618152610707816105af565b60405162461bcd60e51b81526020600482015260116024820152701a5b9d985b1a59081c185e5b1bd8591259607a1b6044820152606490fd5b1561246257565b60405162461bcd60e51b815260206004820152602960248201527f434952434c455f52454c415945523a20696e76616c6964206d657373616765206044820152681c185e5b1bd859125960ba1b6064820152608490fd5b156124c057565b60405162461bcd60e51b815260206004820152602660248201527f434952434c455f52454c415945523a20696e76616c6964206d657373616765206044820152650d8cadccee8d60d31b6064820152608490fd5b9060405161252181610574565b600081526020810192600084526040820160008152606083019060008252839560018451106125a057612576600160ff61256f6105f898612568848a0151829060ff169052565b5160ff1690565b161461245b565b61257f846125db565b905261258a83612628565b905261259582612638565b9052516061146124b9565b60405162461bcd60e51b8152602060048201526013602482015272746f55696e74385f6f75744f66426f756e647360681b6044820152606490fd5b60218151106125eb576021015190565b60405162461bcd60e51b8152602060048201526015602482015274746f55696e743235365f6f75744f66426f756e647360581b6044820152606490fd5b60418151106125eb576041015190565b6061815110612648576061015190565b60405162461bcd60e51b8152602060048201526015602482015274746f427974657333325f6f75744f66426f756e647360581b6044820152606490fdfea26469706673582212206e8d4b23c51fbffc0844e314ca12b37f3c806c949dc63c336ab8940098bf923b64736f6c63430008130033

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

0000000000000000000000002703483b1a5a7c577e8680de9df8be03c6f30e3c000000000000000000000000000000000000000000000000000000000000001200000000000000000000000053207e216540125e322cda8a693b0b89576deb460000000000000000000000008c16639239e2b5de89e9bffe8f7e0d0317a19503

-----Decoded View---------------
Arg [0] : circleIntegration_ (address): 0x2703483B1a5a7c577e8680de9Df8Be03c6f30e3c
Arg [1] : nativeTokenDecimals_ (uint8): 18
Arg [2] : feeRecipient_ (address): 0x53207E216540125e322CdA8A693b0b89576DEb46
Arg [3] : ownerAssistant_ (address): 0x8c16639239e2b5de89E9bFfe8F7e0d0317A19503

-----Encoded View---------------
4 Constructor Arguments found :
Arg [0] : 0000000000000000000000002703483b1a5a7c577e8680de9df8be03c6f30e3c
Arg [1] : 0000000000000000000000000000000000000000000000000000000000000012
Arg [2] : 00000000000000000000000053207e216540125e322cda8a693b0b89576deb46
Arg [3] : 0000000000000000000000008c16639239e2b5de89e9bffe8f7e0d0317a19503


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
[ Download: CSV Export  ]
[ 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.