S Price: $0.843691 (-0.79%)

Contract

0x631e4d69097b4eeBce78DE31433445344ab073dc

Overview

S Balance

Sonic LogoSonic LogoSonic Logo0 S

S Value

$0.00

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To

There are no matching entries

Please try again later

Parent Transaction Hash Block From To
View All Internal Transactions
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
UTSCodeStorageConnectorNative

Compiler Version
v0.8.24+commit.e11b9ed9

Optimization Enabled:
Yes with 1 runs

Other Settings:
paris EvmVersion
File 1 of 19 : UTSCodeStorageConnectorNative.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.24;
 
import "../connector/UTSConnectorNative.sol";

import "../interfaces/IUTSCodeStorage.sol";

/**
 * @notice A contract stores creation bytecode for UTSConnectorNative contract.
 *
 * The bytecode is used by the {UTSFactory} for deployment.
 */
contract UTSCodeStorageConnectorNative is IUTSCodeStorage {

    /**
     * @notice Returns the UTSConnectorNative creation bytecode.
     * @return bytecode creation bytecode of the {UTSConnectorNative} contract.
     */
    function getCode(bool /* isConnector */) external pure returns(bytes memory bytecode) {
        return type(UTSConnectorNative).creationCode;
    }
    
}

File 2 of 19 : IUTSCodeStorage.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.24;

interface IUTSCodeStorage {

    function getCode(bool isConnector) external pure returns(bytes memory bytecode);

}

File 3 of 19 : UTSConnectorNative.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.24;

import "@openzeppelin/[email protected]/access/AccessControl.sol";

import "../extensions/UTSBaseIndexed.sol";

import "../interfaces/IUTSConnector.sol";

/**
 * @notice A contract that provides functionality to use UTS protocol V1 crosschain messaging for bridging
 * native currency.
 * 
 * @dev An {UTSConnectorNative} contract stores and releases native currency and interacts with the UTS protocol.
 * A contract implements and overrides the minimum functionality for correct running and interaction with the UTS protocol.
 * Since native currency cannot be minted or burned in general, only a lock/unlock mechanism is used here.
 */
contract UTSConnectorNative is IUTSConnector, UTSBaseIndexed, AccessControl {

    /// @dev Library for {address} converting, since in crosschain messaging its represented as {bytes} type.
    using AddressConverter for address;

    /**
     * @notice Indicates an error that the provided {amount} exceeds transaction's {msg.value}.
     * @dev Since part of the {msg.value} will be sent to {_router} as payment for the bridging execution, 
     * {msg.value} should be equal to {amount} + estimated bridge fee.
     */
    error UTSConnectorNative__E0();

    /// @notice Indicates an error that the provided {amount} exceeds native currency {address(this).balance}.
    error UTSConnectorNative__E1();

    /// @notice Indicates an error that native currency transfer to {to} address is failed.
    error UTSConnectorNative__E2(bytes);

    /// @notice Indicates an error that the provided {from} address is not equal {spender(msg.sender)} address.
    error UTSConnectorNative__E3();

    /**
     * @notice Initializes basic settings with provided parameters.
     * @param _owner the address of the initial {AccessControl.DEFAULT_ADMIN_ROLE}.
     * @param underlyingToken_ placeholder address, see the {UTSFactory.NATIVE_ADDRESS()} for details.
     * @param _router the address of the authorized {UTSRouter}.
     * @param _allowedChainIds chains Ids available for bridging in both directions.
     * @param _chainConfigs array of {ChainConfig} settings for provided {_allowedChainIds}.
     * @dev See the {UTSERC20DataTypes.ChainConfig} for details.
     * @dev Can and MUST be called only once. Reinitialization is prevented by {UTSBase.__UTSBase_init} function.
     */
    function initializeConnector(
        address _owner,
        address underlyingToken_,
        address _router,  
        uint256[] calldata _allowedChainIds,
        ChainConfig[] calldata _chainConfigs
    ) external {
        __UTSBase_init(underlyingToken_, 18);

        _setRouter(_router);
        _setChainConfig(_allowedChainIds, _chainConfigs);

        _grantRole(DEFAULT_ADMIN_ROLE, _owner);
    }

    /**
     * @notice Provides the ability to send native currency to the current contract to replenish liquidity for bridging.
     */
    receive() external payable {

    }

    /**
     * @notice Returns decimals value of the native currency.
     * @return decimals decimals of the current chain native currency.
     */
    function underlyingDecimals() external view returns(uint8 decimals) {
        return _decimals;
    }

    /**
     * @notice Returns the name of the underlying native token.
     * @return name name of the {_underlyingToken}.
     * @dev Must be hardcoded for a specific chain.
     */
    function underlyingName() external pure returns(string memory name) {
        return "Sonic";
    }

    /**
     * @notice Returns the symbol of the underlying native token.
     * @return symbol symbol of the {_underlyingToken}.
     * @dev Must be hardcoded for a specific chain.
     */
    function underlyingSymbol() external pure returns(string memory symbol) {
        return "S";
    }

    /**
     * @notice Returns the balance of the native currency held by this contract.
     * @return nativeBalance native currency balance held by the {UTSConnectorNative} contract.
     */
    function underlyingBalance() external view returns(uint256 nativeBalance) {
        return address(this).balance;
    }

    /**
     * @notice Returns true if this contract implements the interface defined by `interfaceId`.
     * See the corresponding
     * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified
     * to learn more about how these ids are created.
     */
    function supportsInterface(bytes4 interfaceId) public view override(UTSBase, AccessControl) returns(bool) {
        return interfaceId == type(IUTSConnector).interfaceId || super.supportsInterface(interfaceId);
    }

    /**
     * @notice Overridden function that send bridging request to the {_router}.
     * @param payment current transaction's {msg.value}, that can be used as payment for bridging execution.
     * @param dstToken the address of the {UTSToken} or {UTSConnector} on the destination chain.
     * @param to bridged native currency receiver on the destination chain.
     * @param amount native currency amount to bridge to the destination chain.
     * @param srcDecimals decimals of the current chain native currency.
     * @param dstChainId destination chain Id.
     * @param dstGasLimit {redeem} call gas limit on the destination chain.
     * @param customPayload user's additional data.
     * @param protocolPayload UTS protocol's additional data.
     * @return success call result.
     *
     * @dev By default, {payment} is equal to current transaction's {msg.value} as payment, but in this case, only 
     * part of the {msg.value} must be sent to the {_router}. Function is overriden to send to the {_router} only the 
     * difference between {payment} {msg.value} and {amount}, since {amount} is the amount of native currency that 
     * will be bridged to the destination chain and should remain on the {UTSConnectorNative} contract 
     * balance as locked liquidity.
     */
    function _sendRequest(
        uint256 payment,
        bytes memory dstToken,
        bytes memory to,
        uint256 amount,
        uint8 srcDecimals,
        uint256 dstChainId,
        uint64 dstGasLimit,
        bytes memory customPayload,
        bytes memory protocolPayload
    ) internal override returns(bool success) {
        return IUTSRouter(router()).bridge{value: payment - amount}( 
            dstToken,
            msg.sender.toBytes(),
            to,
            amount,
            srcDecimals,
            dstChainId,
            dstGasLimit,
            customPayload,
            protocolPayload
        );
    }

    /**
     * @notice Overridden function that just checks {msg.value} is greater than the {amount}.
     * @param spender transaction sender, must be {msg.sender}.
     * @param from tokens holder on the current chain (msg.sender in that case).
     * @param amount native currency amount to bridge to the destination chain.
     * @return bridgedNativeAmount bridged native currency amount, that will be released on the destination chain.
     * @dev {bridgedNativeAmount} value may be different from {amount}, in case amount modifies by fee collecting 
     * or any other custom logic. Returned {bridgedNativeAmount} value will be actually used for crosschain message.
     */
    function _burnFrom(
        address spender,
        address from, 
        bytes memory /* to */, 
        uint256 amount, 
        uint256 /* dstChainId */, 
        bytes memory /* customPayload */
    ) internal override returns(uint256 bridgedNativeAmount) {
        if (amount > msg.value) revert UTSConnectorNative__E0();
        if (spender != from) revert UTSConnectorNative__E3();

        return amount;
    }

    /**
     * @notice Overridden function that transfer native currency {amount} from this contract to receiver {to} address.
     * @param to native currency receiver on the current chain.
     * @param amount amount that {to} address will be received.
     * @return receivedNativeAmount amount that {to} address received.
     * @dev {receivedNativeAmount} value may be different from {amount}, in case amount modifies by fee collecting or 
     * any other custom logic.
     *
     * @dev Some checks are added to ensure that the {to} address has received the native currency {amount}:
     *     1. {address(this).balance} is greater than the {amount} to transfer
     *     2. native currency transfer by low level {call} function is successful
     */
    function _mintTo(
        address to,
        uint256 amount,
        bytes memory /* customPayload */,
        Origin memory /* origin */
    ) internal override returns(uint256 receivedNativeAmount) {
        if (to != address(this)) {
            if (amount > address(this).balance) revert UTSConnectorNative__E1();

            (bool _callResult, bytes memory _callResponse) = to.call{value: amount}("");
            if (!_callResult) revert UTSConnectorNative__E2(_callResponse);
        }

        return amount;
    }

    /**
     * @notice The function is overridden only to include access restriction to the {setRouter} and {setChainConfig} functions.
     */
    function _authorizeCall() internal virtual override onlyRole(DEFAULT_ADMIN_ROLE) {
        
    }
}

File 4 of 19 : IUTSConnector.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.24;

import "../../libraries/UTSERC20DataTypes.sol";

interface IUTSConnector {

    function underlyingDecimals() external view returns(uint8);

    function underlyingBalance() external view returns(uint256);

    function underlyingName() external view returns(string memory);

    function underlyingSymbol() external view returns(string memory);

    function initializeConnector(
        address owner,
        address underlyingToken,
        address router,
        uint256[] calldata allowedChainIds,
        ChainConfig[] calldata chainConfigs
    ) external;

}

File 5 of 19 : UTSBaseIndexed.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.24;

import "./UTSBaseExtended.sol";

interface IUTSFactory {

    function REGISTRY() external view returns(address);

}

interface IUTSRegistry {

    function updateChainConfigs(uint256[] calldata allowedChainIds, ChainConfig[] calldata chainConfigs) external;

    function updateRouter(address newRouter) external;

}

/**
 * @notice Extension of {UTSBase} that adds an external calls to emit events in the {UTSRegistry} to log crucial data
 * off-chain.
 *
 * @dev Сan only be used by contracts deployed by {UTSFactory} or contracts manually registered in the {UTSRegistry}.
 */
abstract contract UTSBaseIndexed is UTSBaseExtended {

    /// @notice The {UTSRegistry} contract address.
    address private immutable REGISTRY;

    /// @notice Initializes immutable {REGISTRY} variable.
    constructor() {
        REGISTRY = IUTSFactory(msg.sender).REGISTRY();
    }

    function _setChainConfig(uint256[] memory allowedChainIds, ChainConfig[] memory chainConfigs) internal virtual override {

        IUTSRegistry(REGISTRY).updateChainConfigs(allowedChainIds, chainConfigs);

        super._setChainConfig(allowedChainIds, chainConfigs);
    }

    function _setRouter(address newRouter) internal virtual override {

        IUTSRegistry(REGISTRY).updateRouter(newRouter);

        super._setRouter(newRouter);
    }

}

File 6 of 19 : AccessControl.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/AccessControl.sol)

pragma solidity ^0.8.20;

import {IAccessControl} from "./IAccessControl.sol";
import {Context} from "../utils/Context.sol";
import {ERC165} from "../utils/introspection/ERC165.sol";

/**
 * @dev Contract module that allows children to implement role-based access
 * control mechanisms. This is a lightweight version that doesn't allow enumerating role
 * members except through off-chain means by accessing the contract event logs. Some
 * applications may benefit from on-chain enumerability, for those cases see
 * {AccessControlEnumerable}.
 *
 * Roles are referred to by their `bytes32` identifier. These should be exposed
 * in the external API and be unique. The best way to achieve this is by
 * using `public constant` hash digests:
 *
 * ```solidity
 * bytes32 public constant MY_ROLE = keccak256("MY_ROLE");
 * ```
 *
 * Roles can be used to represent a set of permissions. To restrict access to a
 * function call, use {hasRole}:
 *
 * ```solidity
 * function foo() public {
 *     require(hasRole(MY_ROLE, msg.sender));
 *     ...
 * }
 * ```
 *
 * Roles can be granted and revoked dynamically via the {grantRole} and
 * {revokeRole} functions. Each role has an associated admin role, and only
 * accounts that have a role's admin role can call {grantRole} and {revokeRole}.
 *
 * By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means
 * that only accounts with this role will be able to grant or revoke other
 * roles. More complex role relationships can be created by using
 * {_setRoleAdmin}.
 *
 * WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to
 * grant and revoke this role. Extra precautions should be taken to secure
 * accounts that have been granted it. We recommend using {AccessControlDefaultAdminRules}
 * to enforce additional security measures for this role.
 */
abstract contract AccessControl is Context, IAccessControl, ERC165 {
    struct RoleData {
        mapping(address account => bool) hasRole;
        bytes32 adminRole;
    }

    mapping(bytes32 role => RoleData) private _roles;

    bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;

    /**
     * @dev Modifier that checks that an account has a specific role. Reverts
     * with an {AccessControlUnauthorizedAccount} error including the required role.
     */
    modifier onlyRole(bytes32 role) {
        _checkRole(role);
        _;
    }

    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
        return interfaceId == type(IAccessControl).interfaceId || super.supportsInterface(interfaceId);
    }

    /**
     * @dev Returns `true` if `account` has been granted `role`.
     */
    function hasRole(bytes32 role, address account) public view virtual returns (bool) {
        return _roles[role].hasRole[account];
    }

    /**
     * @dev Reverts with an {AccessControlUnauthorizedAccount} error if `_msgSender()`
     * is missing `role`. Overriding this function changes the behavior of the {onlyRole} modifier.
     */
    function _checkRole(bytes32 role) internal view virtual {
        _checkRole(role, _msgSender());
    }

    /**
     * @dev Reverts with an {AccessControlUnauthorizedAccount} error if `account`
     * is missing `role`.
     */
    function _checkRole(bytes32 role, address account) internal view virtual {
        if (!hasRole(role, account)) {
            revert AccessControlUnauthorizedAccount(account, role);
        }
    }

    /**
     * @dev Returns the admin role that controls `role`. See {grantRole} and
     * {revokeRole}.
     *
     * To change a role's admin, use {_setRoleAdmin}.
     */
    function getRoleAdmin(bytes32 role) public view virtual returns (bytes32) {
        return _roles[role].adminRole;
    }

    /**
     * @dev Grants `role` to `account`.
     *
     * If `account` had not been already granted `role`, emits a {RoleGranted}
     * event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     *
     * May emit a {RoleGranted} event.
     */
    function grantRole(bytes32 role, address account) public virtual onlyRole(getRoleAdmin(role)) {
        _grantRole(role, account);
    }

    /**
     * @dev Revokes `role` from `account`.
     *
     * If `account` had been granted `role`, emits a {RoleRevoked} event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     *
     * May emit a {RoleRevoked} event.
     */
    function revokeRole(bytes32 role, address account) public virtual onlyRole(getRoleAdmin(role)) {
        _revokeRole(role, account);
    }

    /**
     * @dev Revokes `role` from the calling account.
     *
     * Roles are often managed via {grantRole} and {revokeRole}: this function's
     * purpose is to provide a mechanism for accounts to lose their privileges
     * if they are compromised (such as when a trusted device is misplaced).
     *
     * If the calling account had been revoked `role`, emits a {RoleRevoked}
     * event.
     *
     * Requirements:
     *
     * - the caller must be `callerConfirmation`.
     *
     * May emit a {RoleRevoked} event.
     */
    function renounceRole(bytes32 role, address callerConfirmation) public virtual {
        if (callerConfirmation != _msgSender()) {
            revert AccessControlBadConfirmation();
        }

        _revokeRole(role, callerConfirmation);
    }

    /**
     * @dev Sets `adminRole` as ``role``'s admin role.
     *
     * Emits a {RoleAdminChanged} event.
     */
    function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual {
        bytes32 previousAdminRole = getRoleAdmin(role);
        _roles[role].adminRole = adminRole;
        emit RoleAdminChanged(role, previousAdminRole, adminRole);
    }

    /**
     * @dev Attempts to grant `role` to `account` and returns a boolean indicating if `role` was granted.
     *
     * Internal function without access restriction.
     *
     * May emit a {RoleGranted} event.
     */
    function _grantRole(bytes32 role, address account) internal virtual returns (bool) {
        if (!hasRole(role, account)) {
            _roles[role].hasRole[account] = true;
            emit RoleGranted(role, account, _msgSender());
            return true;
        } else {
            return false;
        }
    }

    /**
     * @dev Attempts to revoke `role` to `account` and returns a boolean indicating if `role` was revoked.
     *
     * Internal function without access restriction.
     *
     * May emit a {RoleRevoked} event.
     */
    function _revokeRole(bytes32 role, address account) internal virtual returns (bool) {
        if (hasRole(role, account)) {
            _roles[role].hasRole[account] = false;
            emit RoleRevoked(role, account, _msgSender());
            return true;
        } else {
            return false;
        }
    }
}

File 7 of 19 : UTSERC20DataTypes.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.24;

/// @notice Various structs used in the UTS protocol V1 ERC20-module contracts.

    /// @notice Destination chain {ChainConfig} settings for {UTSBase}.
    struct ChainConfig {
        bytes   peerAddress; // connected {UTSToken} or {UTSConnector} contract address on the destination chain
        uint64  minGasLimit; // the amount of gas required to execute {UTSBase.redeem} function on the destination chain
        uint8   decimals;    // connected {peerAddress} decimals on the destination chain
        bool    paused;      // flag indicating whether current contract is paused for sending/receiving messages from the connected {peerAddress}
    }

    /// @notice Destination {peerAddress} contract {ChainConfig} settings for {UTSBaseExtended.setChainConfigToDestination} function.
    struct ChainConfigUpdate {
        uint256[] allowedChainIds;  // chains Ids available for bridging in both directions
        ChainConfig[] chainConfigs; // {ChainConfig} settings
    }

    /// @notice Crosschain message source peer data.
    struct Origin {
        bytes   sender;      // source message {msg.sender} sender
        uint256 chainId;     // source chain Id
        bytes   peerAddress; // source {UTSToken} or {UTSConnector} contract address
        uint8   decimals;    // source {peerAddress} decimals
    }

    /// @notice {UTSToken} initial settings, configuration, and metadata for deployment and initialization.
    struct DeployTokenData {
        bytes     owner;               // the address of the initial {AccessControl.DEFAULT_ADMIN_ROLE}
        string    name;                // the {ERC20.name} of the {UTSToken} token
        string    symbol;              // the {ERC20.symbol} of the {UTSToken} token
        uint8     decimals;            // the {ERC20.decimals} of the {UTSToken} token
        uint256   initialSupply;       // total initial {UTSToken} supply to mint
        uint256   mintedAmountToOwner; // initial {UTSToken} supply to mint to {owner} balance
        bool      pureToken;           // flag indicating whether the {UTSToken} is use lock/unlock or mint/burn mechanism for bridging
        bool      mintable;            // flag indicating whether {owner} can mint an unlimited amount of {UTSToken} tokens
        bool      globalBurnable;      // flag indicating whether the {UTSToken} is globally burnable by anyone
        bool      onlyRoleBurnable;    // flag indicating whether only addresses with the {AccessControl.BURNER_ROLE} can burn tokens
        bool      feeModule;           // flag indicating whether the {UTSToken} is supports the fee deducting for bridging
        bytes     router;              // the address of the authorized {UTSRouter}
        uint256[] allowedChainIds;     // chains Ids available for bridging in both directions
        ChainConfig[] chainConfigs;    // {ChainConfig} settings for the corresponding {allowedChainIds}
        bytes32   salt;                // value used for precalculation of {UTSToken} contract address
    }

    /// @notice {UTSConnector} initial settings and configuration for deployment and initialization.
    struct DeployConnectorData {
        bytes     owner;            // the address of the initial {AccessControl.DEFAULT_ADMIN_ROLE}
        bytes     underlyingToken;  // underlying ERC20 token address
        bool      feeModule;        // flag indicating whether the {UTSConnector} is supports the fee deducting for bridging
        bytes     router;           // the address of the authorized {UTSRouter}
        uint256[] allowedChainIds;  // chains Ids available for bridging in both directions
        ChainConfig[] chainConfigs; // {ChainConfig} settings for the corresponding {allowedChainIds}
        bytes32   salt;             // value used for precalculation of {UTSConnector} contract address
    }

    /// @notice Metadata for the crosschain deployment request for {UTSDeploymentRouter.sendDeployRequest}.
    struct DeployMetadata {
        uint256 dstChainId;  // destination chain Id
        bool    isConnector; // flag indicating whether is {UTSConnector}(true) or {UTSToken}(false) deployment
        bytes   params;      // abi.encoded {DeployTokenData} struct or abi.encoded {DeployConnectorData} struct
    }

    /// @notice Destination chain settings for sending a crosschain deployment request in the {UTSDeploymentRouter}.
    struct DstDeployConfig {
        bytes   factory;            // destination {UTSFactory} address
        uint64  tokenDeployGas;     // the amount of gas required to deploy the {UTSToken} on the destination chain
        uint64  connectorDeployGas; // the amount of gas required to deploy the {UTSConnector} on the destination chain
        uint16  protocolFee;        // protocol fee (basis points) for crosschain deployment on the destination chain
    }

File 8 of 19 : UTSBaseExtended.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.24;

import "../UTSBase.sol";

import "../interfaces/IUTSBaseExtended.sol";

/**
 * @notice Extension of {UTSBase} that allows {UTSBase} contract owner to change {ChainConfig} settings on different 
 * destination chains with a single crosschain transaction.
 */
abstract contract UTSBaseExtended is IUTSBaseExtended, UTSBase {
    using AddressConverter for address;
    using BytesLib for bytes;

    /**
     * @notice Send crosschain message that will change destination {ChainConfig}.
     * @param dstChainIds destination chains Ids to which a message will be sent to change their {ChainConfig}.
     * @param newConfigs new {ChainConfig} settings for provided {allowedChainIds} to be setted on the destination chains.
     * @return success call result.
     */
    function setChainConfigToDestination(
        uint256[] calldata dstChainIds,
        ChainConfigUpdate[] calldata newConfigs
    ) external payable returns(bool success) {
        _authorizeCall();

        if (dstChainIds.length != newConfigs.length) revert UTSBase__E4();

        bytes[] memory _dstPeers = new bytes[](dstChainIds.length);

        for (uint256 i; dstChainIds.length > i; ++i) _dstPeers[i] = _chainConfig[dstChainIds[i]].peerAddress;

        return IUTSRouter(router()).requestToUpdateConfig{value: msg.value}( 
            msg.sender.toBytes(),
            dstChainIds,
            _dstPeers,
            newConfigs
        );
    }

    /**
     * @notice Sets the destination chains settings by crosschain message.
     * @param allowedChainIds chains Ids available for bridging in both directions.
     * @param chainConfigs array of new {ChainConfig} settings for provided {allowedChainIds}.
     * @param origin source chain data.
     * @dev Only the {_router} can execute this function.
     */
    function setChainConfigByRouter(
        uint256[] calldata allowedChainIds,
        ChainConfig[] calldata chainConfigs,
        Origin calldata origin
    ) external {
        _onlyRouter();

        if (!_chainConfig[origin.chainId].peerAddress.equalStorage(origin.peerAddress)) revert UTSBase__E7();

        _setChainConfig(allowedChainIds, chainConfigs);
    }

    /**
     * @notice Returns estimated minimal amount to pay for {setChainConfigToDestination} call.
     * @param dstChainIds destination chains Ids to which a message will be sent.
     * @param configsLength {ChainConfigUpdate.allowedChainIds} length.
     * @return paymentAmount source chain native currency amount to pay for {setChainConfigToDestination} call.
     */
    function estimateUpdateFee(
        uint256[] calldata dstChainIds, 
        uint256[] calldata configsLength
    ) external view returns(uint256 paymentAmount) {
        return IUTSRouter(router()).getUpdateFee(dstChainIds, configsLength);
    }

}

File 9 of 19 : ERC165.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/introspection/ERC165.sol)

pragma solidity ^0.8.20;

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

/**
 * @dev Implementation of the {IERC165} interface.
 *
 * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check
 * for the additional interface id that will be supported. For example:
 *
 * ```solidity
 * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
 *     return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
 * }
 * ```
 */
abstract contract ERC165 is IERC165 {
    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual returns (bool) {
        return interfaceId == type(IERC165).interfaceId;
    }
}

File 10 of 19 : Context.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol)

pragma solidity ^0.8.20;

/**
 * @dev Provides information about the current execution context, including the
 * sender of the transaction and its data. While these are generally available
 * via msg.sender and msg.data, they should not be accessed in such a direct
 * manner, since when dealing with meta-transactions the account sending and
 * paying for execution may not be the actual sender (as far as an application
 * is concerned).
 *
 * This contract is only required for intermediate, library-like contracts.
 */
abstract contract Context {
    function _msgSender() internal view virtual returns (address) {
        return msg.sender;
    }

    function _msgData() internal view virtual returns (bytes calldata) {
        return msg.data;
    }

    function _contextSuffixLength() internal view virtual returns (uint256) {
        return 0;
    }
}

File 11 of 19 : IAccessControl.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/IAccessControl.sol)

pragma solidity ^0.8.20;

/**
 * @dev External interface of AccessControl declared to support ERC165 detection.
 */
interface IAccessControl {
    /**
     * @dev The `account` is missing a role.
     */
    error AccessControlUnauthorizedAccount(address account, bytes32 neededRole);

    /**
     * @dev The caller of a function is not the expected one.
     *
     * NOTE: Don't confuse with {AccessControlUnauthorizedAccount}.
     */
    error AccessControlBadConfirmation();

    /**
     * @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole`
     *
     * `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite
     * {RoleAdminChanged} not being emitted signaling this.
     */
    event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole);

    /**
     * @dev Emitted when `account` is granted `role`.
     *
     * `sender` is the account that originated the contract call, an admin role
     * bearer except when using {AccessControl-_setupRole}.
     */
    event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);

    /**
     * @dev Emitted when `account` is revoked `role`.
     *
     * `sender` is the account that originated the contract call:
     *   - if using `revokeRole`, it is the admin role bearer
     *   - if using `renounceRole`, it is the role bearer (i.e. `account`)
     */
    event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);

    /**
     * @dev Returns `true` if `account` has been granted `role`.
     */
    function hasRole(bytes32 role, address account) external view returns (bool);

    /**
     * @dev Returns the admin role that controls `role`. See {grantRole} and
     * {revokeRole}.
     *
     * To change a role's admin, use {AccessControl-_setRoleAdmin}.
     */
    function getRoleAdmin(bytes32 role) external view returns (bytes32);

    /**
     * @dev Grants `role` to `account`.
     *
     * If `account` had not been already granted `role`, emits a {RoleGranted}
     * event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     */
    function grantRole(bytes32 role, address account) external;

    /**
     * @dev Revokes `role` from `account`.
     *
     * If `account` had been granted `role`, emits a {RoleRevoked} event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     */
    function revokeRole(bytes32 role, address account) external;

    /**
     * @dev Revokes `role` from the calling account.
     *
     * Roles are often managed via {grantRole} and {revokeRole}: this function's
     * purpose is to provide a mechanism for accounts to lose their privileges
     * if they are compromised (such as when a trusted device is misplaced).
     *
     * If the calling account had been granted `role`, emits a {RoleRevoked}
     * event.
     *
     * Requirements:
     *
     * - the caller must be `callerConfirmation`.
     */
    function renounceRole(bytes32 role, address callerConfirmation) external;
}

File 12 of 19 : IUTSBaseExtended.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.24;

import "./IUTSBase.sol";

interface IUTSBaseExtended {

    function setChainConfigToDestination(
        uint256[] calldata dstChainIds,
        ChainConfigUpdate[] calldata newConfigs
    ) external payable returns(bool success);

    function setChainConfigByRouter(
        uint256[] calldata allowedChainIds,
        ChainConfig[] calldata chainConfigs,
        Origin calldata origin
    ) external;

    function estimateUpdateFee(
        uint256[] calldata dstChainIds, 
        uint256[] calldata configsLength
    ) external view returns(uint256 paymentAmount);

}

File 13 of 19 : UTSBase.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.24;
 
import "@openzeppelin/[email protected]/utils/introspection/ERC165.sol";

import "../libraries/BytesLib.sol";
import "../libraries/AddressConverter.sol";
import "../libraries/DecimalsConverter.sol"; 
import "../libraries/UTSERC20DataTypes.sol";

import "./interfaces/IUTSBase.sol";
import "./interfaces/IUTSRouter.sol";

/**
 * @notice Abstract contract implementing minimal and basic functionality for sending and receiving crosschain bridges 
 * of ERC20 tokens via UTS protocol V1. 
 *
 * @dev 
 * The {__UTSBase_init} function MUST be called before using other functions of the {UTSBase} contract.
 * The {_authorizeCall} function MUST be overridden to include access restriction to the {setRouter} and 
 * {setChainConfig} functions.
 * The {_mintTo} function MUST be overridden to implement {mint}/{transfer} underlying tokens to receiver {to} address 
 * by {_router}.
 * The {_burnFrom} function MUST be overridden to implement {burn}/{transferFrom} underlying tokens from {spender}/{from}
 * address for bridging.
 */
abstract contract UTSBase is IUTSBase, ERC165 {
    using AddressConverter for address;
    using DecimalsConverter for uint256;
    using BytesLib for bytes;

    /// @notice Nonce used for {storeFailedExecution} executions to guarantee uniqueness.
    uint256 private _retryNonce;

    /**
     * @notice Address that can execute {redeem} and {storeFailedExecution} functions.
     * @dev Should be an authorized {UTSRouter} contract address or a zero address in case of disconnection from UTS protocol.
     */
    address private _router;

    /// @notice Address of the underlying ERC20 token.
    address internal _underlyingToken;

    /// @notice Decimals of the underlying ERC20 token.
    uint8 internal _decimals;

    /// @notice {ChainConfig} settings for the corresponding destination chain Id.
    /// @dev See the {UTSERC20DataTypes.ChainConfig} for details.
    mapping(uint256 chainId => ChainConfig dstChainConfig) internal _chainConfig;

    /**
     * @notice Receiver address for the corresponding {redeem} message hash.
     * @dev Mapping is filled only by {storeFailedExecution} function if the {redeem} call is unsuccessful.
     * IMPORTANT: Execution of the {_redeem} function with a {to} zero address MUST be forbidden.
     */
    mapping(bytes32 msgHash => address receiverAddress) private _failedExecution;

    /// @notice Indicates an error that the {UTSBase} contract initialized already.
    error UTSBase__E0();

    /// @notice Indicates an error that the function caller is not the {_router}.
    error UTSBase__E1();

    /// @notice Indicates an error that the {to} is zero address.
    error UTSBase__E2();

    /// @notice Indicates an error that the {amount} to bridge is zero.
    error UTSBase__E3();

    /// @notice Indicates an error that lengths of {allowedChainIds} and {chainConfigs} do not match in the {_setChainConfig} function.
    error UTSBase__E4();

    /// @notice Indicates an error that the destination {peerAddress} is paused for sending and receiving crosschain messages.
    error UTSBase__E5();

    /// @notice Indicates an error that the provided {dstGasLimit} is less than the minimum required amount.
    error UTSBase__E6();

    /// @notice Indicates an error that the source {Origin.peerAddress} is unauthorized in the {ChainConfig} for corresponding {Origin.chainId}.
    error UTSBase__E7();

    /**
     * @notice Emitted when the {_router} address is updated.
     * @param caller the caller address who set the new {_router} address.
     * @param newRouter the address of the new {_router}.
     */
    event RouterSet(address indexed caller, address newRouter);

    /**
     * @notice Emitted when {ChainConfig} settings are updated.
     * @param caller the caller address who set the new destination {ChainConfig} settings.
     * @param allowedChainIds new chains Ids available for bridging in both directions.
     * @param chainConfigs array of new {ChainConfig} settings for corresponding {allowedChainIds}.
     */
    event ChainConfigUpdated(address indexed caller, uint256[] allowedChainIds, ChainConfig[] chainConfigs);

    /**
     * @notice Emitted when tokens are successfully redeemed from the source chain.
     * @param to tokens receiver on the current chain.
     * @param amount received amount.
     * @param srcPeerAddressIndexed indexed source {peerAddress}.
     * @param srcPeerAddress source {peerAddress}.
     * @param srcChainId source chain Id.
     * @param sender source chain sender's address.
     */
    event Redeemed(
        address indexed to, 
        uint256 amount, 
        bytes indexed srcPeerAddressIndexed, 
        bytes srcPeerAddress,
        uint256 indexed srcChainId,
        bytes sender
    );

    /**
     * @notice Emitted when crosschain bridge message is successfully sent to a destination chain.
     * @param spender the caller address who initiate the bridge.
     * @param from tokens holder on the current chain.
     * @param dstPeerAddressIndexed indexed destination {peerAddress}.
     * @param dstPeerAddress destination {peerAddress}.
     * @param to bridged tokens receiver on the destination chain.
     * @param amount bridged tokens amount.
     * @param dstChainId destination chain Id.
     */
    event Bridged(
        address indexed spender, 
        address from, 
        bytes indexed dstPeerAddressIndexed, 
        bytes dstPeerAddress,
        bytes to, 
        uint256 amount,
        uint256 indexed dstChainId
    );

    /**
     * @notice Emitted when a {storeFailedExecution} executed in case of failed {redeem} call.
     * @param to tokens receiver on the current chain.
     * @param amount amount to receive.
     * @param customPayload user's additional data.
     * @param originIndexed indexed source chain data.
     * @param origin source chain data.
     * @dev See the {UTSERC20DataTypes.Origin} for details.
     * @param result handled error message.
     * @param nonce unique failed execution's counter.
     */
    event ExecutionFailed(
        address indexed to, 
        uint256 amount, 
        bytes customPayload, 
        Origin indexed originIndexed, 
        Origin origin,
        bytes indexed result, 
        uint256 nonce
    );

    /**
     * @notice Initializes basic settings.
     * @param underlyingToken_ underlying ERC20 token address.
     * @dev In case this contract and ERC20 are the same contract, {underlyingToken_} should be address(this).
     *
     * @param decimals_ underlying token decimals.
     * @dev Can and MUST be called only once.
     */
    function __UTSBase_init(address underlyingToken_, uint8 decimals_) internal {
        if (_retryNonce > 0) revert UTSBase__E0();

        _underlyingToken = underlyingToken_;
        _decimals = decimals_;
        // {_retryNonce} counter increases here for two reasons: 
        // 1. to block repeated {__UTSBase_init} call
        // 2. initialize the {_retryNonce} variable to unify the gas limit calculation of the {storeFailedExecution} call
        _retryNonce = 1;
    }

    /**
     * @notice Initiates the tokens bridging.
     * @param from tokens holder on the current chain.
     * @param to bridged tokens receiver on the destination chain.
     * @param amount tokens amount to bridge to the destination chain.
     * @param dstChainId destination chain Id.
     * @param dstGasLimit {redeem} call gas limit on the destination chain.
     * @param customPayload user's additional data.
     * @param protocolPayload UTS protocol's additional data.
     * @return success call result.
     * @return bridgedAmount bridged tokens amount.
     */
    function bridge(
        address from,
        bytes calldata to, 
        uint256 amount, 
        uint256 dstChainId,
        uint64 dstGasLimit,
        bytes calldata customPayload,
        bytes calldata protocolPayload
    ) external payable virtual returns(bool success, uint256 bridgedAmount) {

        return _bridge(
            msg.sender, 
            from, 
            to, 
            amount, 
            dstChainId, 
            dstGasLimit, 
            customPayload, 
            protocolPayload
        );
    }

    /**
     * @notice Executes the tokens delivery from the source chain.
     * @param to tokens receiver on the current chain.
     * @param amount amount to receive.
     * @param customPayload user's additional data.
     * @param origin source chain data.
     * @dev See the {UTSERC20DataTypes.Origin} for details.
     * @return success call result.
     * @dev Only the {_router} can execute this function.
     */
    function redeem(
        address to,
        uint256 amount,
        bytes calldata customPayload,
        Origin calldata origin
    ) external payable virtual returns(bool success) {
        _onlyRouter();

        return _redeem(to, amount, customPayload, origin);
    }

    /**
     * @notice Stores failed execution's data.
     * @param to tokens receiver on the current chain.
     * @param amount tokens amount to receive.
     * @param customPayload user's additional data.
     * @param origin source chain data.
     * @dev See the {UTSERC20DataTypes.Origin} for details.
     * @param result handled error message.
     * @dev Only the {_router} can execute this function.
     */
    function storeFailedExecution(
        address to,
        uint256 amount,
        bytes calldata customPayload,
        Origin calldata origin,
        bytes calldata result
    ) external virtual {
        _onlyRouter();

        _failedExecution[keccak256(abi.encode(to, amount, customPayload, origin, _retryNonce))] = to;

        emit ExecutionFailed(to, amount, customPayload, origin, origin, result, _retryNonce);

        _retryNonce++;
    }

    /**
     * @notice Executes the tokens delivery after failed execution.
     * @param to tokens receiver on the current chain.
     * @param amount amount to receive.
     * @param customPayload user's additional data.
     * @param origin source chain data.
     * @dev See the {UTSERC20DataTypes.Origin} for details.
     * @param nonce unique failed execution's counter.
     * @return success call result.
     */
    function retryRedeem(
        address to,
        uint256 amount,
        bytes calldata customPayload,
        Origin calldata origin,
        uint256 nonce
    ) external virtual returns(bool success) {
        if (to == address(0)) return false;
        bytes32 _hash = keccak256(abi.encode(to, amount, customPayload, origin, nonce));
        if (_failedExecution[_hash] != to) return false;
        delete _failedExecution[_hash];

        return _redeem(to, amount, customPayload, origin);
    }

    /**
     * @notice Sets the destination chains settings.
     * @param allowedChainIds chains Ids available for bridging in both directions.
     * @param chainConfigs array of {ChainConfig} settings for provided {allowedChainIds}, containing:
     *        peerAddress: connected {UTSToken} or {UTSConnector} contract address on the destination chain
     *        minGasLimit: the amount of gas required to execute {redeem} function on the destination chain
     *        decimals: connected {peerAddress} decimals on the destination chain
     *        paused: flag indicating whether current contract is paused for sending/receiving messages from the connected {peerAddress}
     *
     * @return success call result.
     */
    function setChainConfig(
        uint256[] calldata allowedChainIds,
        ChainConfig[] calldata chainConfigs
    ) external virtual returns(bool success) {
        _authorizeCall();
        _setChainConfig(allowedChainIds, chainConfigs);

        return true;
    }

    /**
     * @notice Sets the UTSRouter address.
     * @param newRouter new {_router} address.
     * @return success call result.
     * @dev {_router} address has access rights to execute {redeem} and {storeFailedExecution} functions.
     */
    function setRouter(address newRouter) external virtual returns(bool success) {
        _authorizeCall();
        _setRouter(newRouter);

        return true;
    }

    /**
     * @notice Returns the UTSRouter {_router} address.
     * @return routerAddress the {UTSRouter} address.
     */
    function router() public view returns(address routerAddress) {
        return _router;
    }

    /**
     * @notice Returns the UTSBase protocol version.
     * @return UTS protocol version.
     */
    function protocolVersion() public pure virtual returns(bytes2) {
        return 0x0101;
    }

    /**
     * @notice Returns the underlying ERC20 token address.
     * @return ERC20 {_underlyingToken} address.
     */
    function underlyingToken() public view virtual returns(address) {
        return _underlyingToken;
    }

    /**
     * @notice Returns whether failed execution's data is stored. 
     * @param to tokens receiver on the current chain.
     * @param amount amount to receive.
     * @param customPayload user's additional data.
     * @param origin source chain data.
     * @dev See the {UTSERC20DataTypes.Origin} for details.
     * @param nonce unique failed execution's counter.
     * @return isFailed result.
     */
    function isExecutionFailed(
        address to, 
        uint256 amount, 
        bytes calldata customPayload, 
        Origin calldata origin,
        uint256 nonce
    ) external view virtual returns(bool isFailed) {
        if (to == address(0)) return false;
        return _failedExecution[keccak256(abi.encode(to, amount, customPayload, origin, nonce))] == to;
    }

    /**
     * @notice Returns estimated minimal amount to pay for bridging and minimal gas limit.
     * @param dstChainId destination chain Id.
     * @param dstGasLimit {redeem} call gas limit on the destination chain.
     * @param customPayloadLength user's additional data length.
     * @param protocolPayload UTS protocol's additional data.
     * @return paymentAmount source chain native currency amount to pay for bridging.
     * @return dstMinGasLimit destination chain minimal {redeem} call gas limit.
     */
    function estimateBridgeFee(
        uint256 dstChainId, 
        uint64 dstGasLimit, 
        uint16 customPayloadLength,
        bytes calldata protocolPayload
    ) public view virtual returns(uint256 paymentAmount, uint64 dstMinGasLimit) {
        dstMinGasLimit = IUTSRouter(_router).dstMinGasLimit(dstChainId);
        uint64 _configMinGasLimit = _chainConfig[dstChainId].minGasLimit;

        return (
            IUTSRouter(_router).getBridgeFee(dstChainId, dstGasLimit, customPayloadLength, protocolPayload), 
            dstMinGasLimit >= _configMinGasLimit ? dstMinGasLimit : _configMinGasLimit
        );
    }

    /**
     * @notice Returns destination chain configs for sending and receiving crosschain messages.
     * @param chainIds destination chain Ids.
     * @return configs array of {ChainConfig} settings for provided {chainIds}.
     * @dev See the {UTSERC20DataTypes.ChainConfig} for details.
     */
    function getChainConfigs(uint256[] calldata chainIds) external view returns(ChainConfig[] memory configs) {
        configs = new ChainConfig[](chainIds.length);
        for (uint256 i; chainIds.length > i; ++i) configs[i] = _chainConfig[chainIds[i]];
    }

    /**
     * @notice Returns true if this contract implements the interface defined by `interfaceId`.
     * See the corresponding
     * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified
     * to learn more about how these ids are created.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual override returns(bool) {
        return interfaceId == type(IUTSBase).interfaceId || super.supportsInterface(interfaceId);
    }

    /**
     * @notice Internal function that initiates the tokens bridging.
     * @param spender transaction sender, must be {msg.sender}.
     * @param from tokens holder on the current chain.
     * @param to bridged tokens receiver on the destination chain.
     * @param amount tokens amount to bridge to the destination chain.
     * @param dstChainId destination chain Id.
     * @param dstGasLimit {redeem} call gas limit on the destination chain.
     * @param customPayload user's additional data.
     * @param protocolPayload UTS protocol's additional data.
     *
     * @return success call result.
     * @return bridgedAmount bridged tokens amount.
     *
     * @dev Implements all basic checks and calculations, containing:
     *      1. required destination gas limit check
     *      2. destination peer is not paused check
     *      3. amount conversion in accordance with destination token decimals
     *      4. bridged tokens amount is not zero check
     */
    function _bridge(
        address spender,
        address from,
        bytes memory to, 
        uint256 amount, 
        uint256 dstChainId, 
        uint64 dstGasLimit,
        bytes memory customPayload,
        bytes memory protocolPayload
    ) internal virtual returns(bool success, uint256 bridgedAmount) {
        if (from == address(0)) from = spender;

        ChainConfig memory config = _chainConfig[dstChainId];

        if (config.minGasLimit > dstGasLimit) revert UTSBase__E6();
        if (config.paused) revert UTSBase__E5();

        uint8 _srcDecimals = _decimals;
        amount = amount.convert(_srcDecimals, config.decimals).convert(config.decimals, _srcDecimals);

        amount = _burnFrom(
            spender,
            from,
            to, 
            amount, 
            dstChainId, 
            customPayload
        );

        if (amount == 0) revert UTSBase__E3();

        emit Bridged(spender, from, config.peerAddress, config.peerAddress, to, amount, dstChainId);

        return (
            _sendRequest(
                msg.value,
                config.peerAddress, 
                to, 
                amount,
                _srcDecimals, 
                dstChainId,
                dstGasLimit,
                customPayload,
                protocolPayload
            ), 
            amount
        );
    }

    /**
     * @notice Internal function that call {_router} contract to send crosschain bridge message.
     * @param payment the native currency amount that will be transfer to the {_router} as payment for sending this message.
     * @param dstToken the contract address on the {dstChainId} that will receive this message.
     * @param to bridged tokens receiver on the destination chain.
     * @param amount amount that {to} address will receive (before decimals conversion on the destination chain).
     * @param srcDecimals source ERC20 underlying token decimals.
     * @param dstChainId destination chain Id.
     * @param dstGasLimit {redeem} call gas limit on the destination chain.
     * @param customPayload user's additional data.
     * @param protocolPayload UTS protocol's additional data.
     *
     * @return success call result.
     *
     * @dev {customPayload} can be used to send an additional data, it will be sent to the {dstToken} contract on the 
     * destination chain in accordance with {redeem} function.
     */
    function _sendRequest(
        uint256 payment,
        bytes memory dstToken,
        bytes memory to,
        uint256 amount,
        uint8 srcDecimals,
        uint256 dstChainId,
        uint64 dstGasLimit,
        bytes memory customPayload,
        bytes memory protocolPayload
    ) internal virtual returns(bool success) {
        return IUTSRouter(_router).bridge{value: payment}( 
            dstToken,
            msg.sender.toBytes(),
            to,
            amount,
            srcDecimals,
            dstChainId,
            dstGasLimit,
            customPayload,
            protocolPayload
        );
    }

    /**
     * @notice Internal function that releases tokens to receiver by crosschain message from the source chain.
     * @param to bridged tokens receiver on the current chain.
     * @param amount amount that {to} address will receive (before decimals conversion on the current chain).
     * @param customPayload user's additional data.
     * @param origin source chain data.
     * @dev See the {UTSERC20DataTypes.Origin} for details.
     * @return success call result.
     *
     * @dev Implements all basic checks and calculations, containing:
     *      1. receiver address is not zero address check
     *      2. source peer address is allowed to send messages to this contract check
     *      3. source peer address is not paused check
     *      4. amount conversion in accordance with source token decimals
     */
    function _redeem(
        address to,
        uint256 amount,
        bytes memory customPayload,
        Origin memory origin
    ) internal virtual returns(bool success) {
        if (to == address(0)) revert UTSBase__E2();

        ChainConfig memory config = _chainConfig[origin.chainId];

        if (!config.peerAddress.equal(origin.peerAddress)) revert UTSBase__E7();
        if (config.paused) revert UTSBase__E5();
        
        amount = _mintTo(to, amount.convert(origin.decimals, _decimals), customPayload, origin);

        emit Redeemed(to, amount, origin.peerAddress, origin.peerAddress, origin.chainId, origin.sender);

        return true;
    }

    /**
     * @notice Internal function that sets the destination chains settings and emits corresponding event.
     * @param allowedChainIds chains Ids available for bridging in both directions.
     * @param chainConfigs array of {ChainConfig} settings for provided {allowedChainIds}.
     * @dev See the {UTSERC20DataTypes.ChainConfig} for details.
     */
    function _setChainConfig(uint256[] memory allowedChainIds, ChainConfig[] memory chainConfigs) internal virtual {
        if (allowedChainIds.length != chainConfigs.length) revert UTSBase__E4();
        for (uint256 i; allowedChainIds.length > i; ++i) _chainConfig[allowedChainIds[i]] = chainConfigs[i];

        emit ChainConfigUpdated(msg.sender, allowedChainIds, chainConfigs);
    }

    /**
     * @notice Internal function that sets the UTSRouter address and emits corresponding event.
     * @param newRouter new {_router} address.
     */
    function _setRouter(address newRouter) internal virtual {
        _router = newRouter;

        emit RouterSet(msg.sender, newRouter);
    }

    /**
     * @notice Internal view function that implement basic access check for {redeem} and {storeFailedExecution} functions.
     */
    function _onlyRouter() internal view {
        if (msg.sender != _router) revert UTSBase__E1();
    }

    /**
     * @dev The function MUST be overridden to include access restriction to the {setRouter} and {setChainConfig} functions.
     */
    function _authorizeCall() internal virtual;

    /**
     * @dev The function MUST be overridden to implement {mint}/{transfer} underlying tokens to receiver {to} address by {_router}.
     */
    function _mintTo(
        address to,
        uint256 amount,
        bytes memory customPayload,
        Origin memory origin
    ) internal virtual returns(uint256 receivedAmount);

    /**
     * @dev The function MUST be overridden to implement {burn}/{transferFrom} underlying tokens from {spender}/{from} 
     * address for bridging.
     *
     * IMPORTANT: If this contract IS a token itself, and the {spender} and {from} addresses are different, an {ERC20.allowance} 
     * check MUST be added.
     *
     * IMPORTANT: If this contract IS NOT a token itself, the {spender} and {from} addresses MUST be the same to prevent tokens
     * stealing via third-party allowances.
     *
     * IMPORTANT: Returned {bridgedAmount} value will be actually used for crosschain message, as it may be different from {amount}, 
     * if custom logic inside {_burnFrom} function modifies it.
     */
    function _burnFrom(
        address spender,
        address from,
        bytes memory to, 
        uint256 amount, 
        uint256 dstChainId, 
        bytes memory customPayload
    ) internal virtual returns(uint256 bridgedAmount);
}

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

pragma solidity ^0.8.20;

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

File 15 of 19 : IUTSBase.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.24;

import "../../libraries/UTSERC20DataTypes.sol";

interface IUTSBase {

    function protocolVersion() external view returns(bytes2);

    function underlyingToken() external view returns(address underlyingTokenAddress);

    function router() external view returns(address routerAddress);

    function getChainConfigs(uint256[] calldata chainIds) external view returns(ChainConfig[] memory configs);

    function isExecutionFailed(
        address to,
        uint256 amount,
        bytes calldata customPayload,
        Origin calldata origin,
        uint256 nonce
    ) external view returns(bool isFailed);

    function estimateBridgeFee(
        uint256 dstChainId, 
        uint64 dstGasLimit, 
        uint16 customPayloadLength,
        bytes calldata protocolPayload
    ) external view returns(uint256 paymentAmount, uint64 dstMinGasLimit);

    function setRouter(address newRouter) external returns(bool success);

    function setChainConfig(
        uint256[] calldata allowedChainIds,
        ChainConfig[] calldata chainConfigs
    ) external returns(bool success);

    function bridge(
        address from,
        bytes calldata to,
        uint256 amount,
        uint256 dstChainId,
        uint64 dstGasLimit,
        bytes calldata customPayload,
        bytes calldata protocolPayload
    ) external payable returns(bool success, uint256 bridgedAmount);

    function redeem(
        address to,
        uint256 amount,
        bytes calldata customPayload,
        Origin calldata origin
    ) external payable returns(bool success);

    function storeFailedExecution(
        address to,
        uint256 amount,
        bytes calldata customPayload,
        Origin calldata origin,
        bytes calldata result
    ) external;

    function retryRedeem(
        address to, 
        uint256 amount, 
        bytes calldata customPayload, 
        Origin calldata origin,
        uint256 nonce
    ) external returns(bool success);

}

File 16 of 19 : IUTSRouter.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.24;

import "../../libraries/UTSERC20DataTypes.sol";

interface IUTSRouter {

    function MASTER_ROUTER() external view returns(address);

    function PRICE_FEED() external view returns(address);

    function protocolVersion() external view returns(bytes2);

    function getBridgeFee(
        uint256 dstChainId, 
        uint64 dstGasLimit,
        uint256 payloadLength,
        bytes calldata protocolPayload
    ) external view returns(uint256 bridgeFeeAmount);

    function getUpdateFee(
        uint256[] calldata dstChainIds, 
        uint256[] calldata configsLength
    ) external view returns(uint256 updateFeeAmount);

    function dstMinGasLimit(uint256 dstChainId) external view returns(uint64 dstMinGasLimitAmount);

    function dstProtocolFee(uint256 dstChainId) external view returns(uint16 dstProtocolFeeRate);

    function dstUpdateGas(uint256 dstChainId) external view returns(uint64 dstUpdateGasAmount);

    function setDstMinGasLimit(uint256[] calldata dstChainIds, uint64[] calldata newDstMinGasLimits) external;

    function setDstProtocolFee(uint256[] calldata dstChainIds, uint16[] calldata newDstProtocolFees) external;

    function setDstUpdateGas(uint256[] calldata dstChainIds, uint64[] calldata newDstUpdateGas) external;

    function bridge(
        bytes calldata dstToken,
        bytes calldata sender,
        bytes calldata to,
        uint256 amount,
        uint8 srcDecimals,
        uint256 dstChainId,
        uint64 dstGasLimit,
        bytes calldata customPayload,
        bytes calldata protocolPayload
    ) external payable returns(bool success);

    function requestToUpdateConfig(
        bytes calldata sender,
        uint256[] calldata dstChainIds,
        bytes[] calldata dstPeers,
        ChainConfigUpdate[] calldata newConfigs
    ) external payable returns(bool success);

    function execute(
        address peerAddress, 
        bytes1 messageType, 
        bytes calldata localParams
    ) external payable returns(uint8 opResult);

    function pause() external;

    function unpause() external;

}

File 17 of 19 : DecimalsConverter.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.24;

/**
 * @notice The library contains utility function for converting amounts with different decimals values for the UTS protocol V1.
 */
library DecimalsConverter {

    function convert(uint256 amount, uint256 decimalsIn, uint256 decimalsOut) internal pure returns(uint256) {
        if (decimalsOut > decimalsIn) {
            return amount * (10 ** (decimalsOut - decimalsIn));
        } else {
            if (decimalsOut < decimalsIn) {
                return amount / (10 ** (decimalsIn - decimalsOut));
            }
        }

        return amount;
    }

}

File 18 of 19 : AddressConverter.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.24;

/**
 * @notice A library contains utility functions for converting address type for the UTS protocol V1.
 */
library AddressConverter {

    function toBytes(address _address) internal pure returns(bytes memory) {
        return abi.encodePacked(_address);
    }

    function toAddress(bytes memory _params) internal pure returns(address) {
        return address(uint160(bytes20(_params)));
    }

    function toAddressPadded(bytes memory _params) internal pure returns(address addressPadded) {
        if (32 > _params.length) return address(0);

        assembly {
            addressPadded := div(mload(add(add(_params, 0x20), 12)), 0x1000000000000000000000000)
        }
    }

}

File 19 of 19 : 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 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;
    }

}

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

Contract Security Audit

Contract ABI

[{"inputs":[{"internalType":"bool","name":"","type":"bool"}],"name":"getCode","outputs":[{"internalType":"bytes","name":"bytecode","type":"bytes"}],"stateMutability":"pure","type":"function"}]

6080806040523461001657612966908161001c8239f35b600080fdfe6080600436101561000f57600080fd5b6000803560e01c63b514681e1461002557600080fd5b346100d857602090816003193601126100d857600435801515036100d857601f1991612855603f8101841685019291908584106001600160401b038511176100c45760409593929195926040528084526100dc868501396040519485938185528051938483870152835b8581106100ae57866040818a601f8a8a85828601015201168101030190f35b828101840151898201830152889650830161008f565b634e487b7160e01b83526041600452602483fd5b80fdfe60a08060405234620000d7576306433b1b60e01b8152602081600481335afa908115620000cb5760009162000053575b506080526040516127789081620000dd8239608051818181611d6b0152611ee40152f35b60203d602011620000c3575b601f8101601f191682016001600160401b03811183821017620000af57602091839160405281010312620000ab5751906001600160a01b0382168203620000a85750386200002f565b80fd5b5080fd5b634e487b7160e01b84526041600452602484fd5b503d6200005f565b6040513d6000823e3d90fd5b600080fdfe6080604052600436101561001b575b361561001957600080fd5b005b60003560e01c806301ffc9a7146101cb57806304e7aae9146101c6578063081fafa0146101c15780631b7fd4ec146101bc5780631d34dbc3146101b7578063248a9ca3146101b25780632495a599146101ad57806325a760c2146101a85780632ae9c600146101a35780632da04cba1461019e5780632f2ff15d1461019957806336568abe1461019457806359356c5c1461018f578063755067181461018a5780638ec58bb51461018557806391d1485414610180578063a217fddf1461017b578063bd2d87eb14610176578063c0d7865514610171578063cb05ac031461016c578063d3a2ba7614610167578063d547741f14610162578063d90a730e1461015d578063dde63fed14610158578063e8f6e9e214610153578063f887ea401461014e5763f96e52ca0361000e57611097565b610fcd565b610ea8565b610e89565b610e47565b610e08565b610c84565b610b0c565b610ae3565b610a9d565b610a25565b6109e5565b6109bc565b610872565b610856565b61080f565b6107d0565b610718565b6106f7565b6106d3565b6106aa565b61067b565b6105da565b6104fd565b610395565b6102d4565b346102555760203660031901126102555760043563ffffffff60e01b8116809103610255576020906303ca2c9760e01b8114908115610210575b506040519015158152f35b637965db0b60e01b81149150811561022a575b5038610205565b63950a21e160e01b811491508115610244575b5038610223565b6301ffc9a760e01b1490503861023d565b600080fd5b9181601f84011215610255578235916001600160401b038311610255576020808501948460051b01011161025557565b6040600319820112610255576001600160401b039160043583811161025557826102b69160040161025a565b93909392602435918211610255576102d09160040161025a565b9091565b346102555761030d6102ff6103076102eb3661028a565b94916102f893919361209e565b3691611212565b9236916112e5565b90611d68565b602060405160018152f35b600435906001600160a01b038216820361025557565b602435906001600160a01b038216820361025557565b604435906001600160a01b038216820361025557565b9181601f84011215610255578235916001600160401b038311610255576020838186019501011161025557565b908160809103126102555790565b6080366003190112610255576103a9610318565b6001600160401b03604435818111610255576103c990369060040161035a565b909160643590811161025557610402926103ea6103fa923690600401610387565b926103f3611e69565b369161127b565b5036906113af565b906001600160a01b03811680156104eb57602083019161042a6104258451611575565b611cd1565b928351936104456104416040880196875190612433565b1590565b6104d957606001516104c75761049960008051602061272383398151915292610493610475606089015160ff1690565b60ff8061048860025460ff9060a01c1690565b1691166024356124a1565b90612511565b935190519451936104b96104ac83611e8f565b9560405193849384611eaf565b0390a4602060405160018152f35b60405163188e252560e11b8152600490fd5b604051637cb3947160e11b8152600490fd5b6040516351c7b3e160e01b8152600490fd5b346102555760a036600319011261025557610516610318565b61051e61032e565b90610527610344565b6001600160401b0392906064358481116102555761054990369060040161025a565b9290946084359081116102555761056490369060040161025a565b926000546105b757600280546001600160a81b0319166001600160a01b0390941693909317600960a11b179092556001600055610019956105b294610307936102ff9291906102f890611eda565b611f8d565b60405163180cbc2160e21b8152600490fd5b6001600160401b0381160361025557565b60e0366003190112610255576105ee610318565b6001600160401b03906024358281116102555761060f90369060040161035a565b906084359061061d826105c9565b60a4358581116102555761063590369060040161035a565b92909160c4359687116102555761065361066397369060040161035a565b9690956064359260443592611428565b604080519215158352602083019190915290f35b0390f35b346102555760203660031901126102555760043560005260056020526020600160406000200154604051908152f35b34610255576000366003190112610255576002546040516001600160a01b039091168152602090f35b3461025557600036600319011261025557602060ff60025460a01c16604051908152f35b346102555760003660031901126102555760405161010160f01b8152602090f35b3461025557610763602061072b3661028a565b9492909161077560018060a01b03600154169360405197889687958695632d79c1e160e01b8752604060048801526044870191611594565b84810360031901602486015291611594565b03915afa80156107cb576106779160009161079c575b506040519081529081906020820190565b6107be915060203d6020116107c4575b6107b681836111d8565b810190611585565b3861078b565b503d6107ac565b6115b8565b34610255576040366003190112610255576100196004356107ef61032e565b9080600052600560205261080a6001604060002001546120e9565b611fff565b346102555760403660031901126102555761082861032e565b336001600160a01b03821603610844576100199060043561212a565b60405163334bd91960e11b8152600490fd5b3461025557600036600319011261025557602047604051908152f35b34610255576060366003190112610255576001600160401b03600435818111610255576108a390369060040161025a565b90602435838111610255576108bc90369060040161025a565b9190604435858111610255576108d6903690600401610387565b946108df611e69565b6000956020810135875260036020526040872090604081013590601e19813603018212156109535701803592831161094f5760200190823603821361094f576109306104419261093694369161127b565b9061219d565b6104d9576102ff6103079261094c953691611212565b80f35b8780fd5b8880fd5b60a0600319820112610255576004356001600160a01b03811681036102555791602435916001600160401b0391604435838111610255578261099b9160040161035a565b93909392606435918211610255576109b591600401610387565b9060843590565b346102555760206109db6109cf36610957565b949390939291926116b4565b6040519015158152f35b3461025557604036600319011261025557602060ff610a19610a0561032e565b6004356000526005845260406000206117ff565b54166040519015158152f35b3461025557600036600319011261025557602060405160008152f35b60005b838110610a545750506000910152565b8181015183820152602001610a44565b90602091610a7d81518092818552858086019101610a41565b601f01601f1916010190565b906020610a9a928181520190610a64565b90565b3461025557600036600319011261025557610677604051610abd816111bd565b6005815264536f6e696360d81b6020820152604051918291602083526020830190610a64565b346102555760203660031901126102555761030d610aff610318565b610b0761209e565b611eda565b3461025557608036600319011261025557602435600435610b2c826105c9565b60443561ffff81168103610255576001600160401b0360643581811161025557610b5a90369060040161035a565b600154909390610b8090610b74906001600160a01b031681565b6001600160a01b031690565b6040516329d2f02160e21b815260048101879052602097909291908884602481855afa9788156107cb578994600099610c55575b50610bf1610bd46001610bc684611575565b01546001600160401b031690565b986040519788968795869562092e8760e71b87526004870161182b565b03915afa9485156107cb57600095610c36575b505081811690831610610c2e5750905b604080519182526001600160401b03929092166020820152f35b905090610c14565b610c4d929550803d106107c4576107b681836111d8565b923880610c04565b610c76919950853d8711610c7d575b610c6e81836111d8565b810190611816565b9738610bb4565b503d610c64565b346102555760a036600319011261025557610c9d610318565b6001600160401b03906024359060443583811161025557610cc290369060040161035a565b909260643585811161025557610cdc903690600401610387565b90608435958611610255577f89fecad7e82e777b95efd8da30d0290c555adcd7d5fd59c2bbd44752bc09ccab610e0395610de595610dfb610d2360409a369060040161035a565b9096610d2d611e69565b610d8a8460009d8e8a8d610d608b610d5285548a885195869460208601988c8a611671565b03601f1981018352826111d8565b519020815260046020522080546001600160a01b0319166001600160a01b03909216919091179055565b8b54998a92604051602081610dca610dac82610da689806115e5565b9061185e565b83880135815283610dc060408a018a6115e5565b919092019161185e565b60ff610dd8606089016112cd565b1681520301902099611873565b9860405195869560018060a01b03169886611888565b0390a46118ce565b815580f35b3461025557604036600319011261025557610019600435610e2761032e565b90806000526005602052610e426001604060002001546120e9565b61212a565b3461025557600036600319011261025557610677604051610e67816111bd565b60018152605360f81b6020820152604051918291602083526020830190610a64565b346102555760206109db610e9c36610957565b949390939291926118e2565b610eb13661028a565b610ebc92919261209e565b808203610fbb57610ecc82611926565b9260005b808411610f7b57506001549394602094909190610f2190610ef9906001600160a01b0316610b74565b93610f0333612254565b604051633bde546760e11b8152988997889687969360048801611c38565b039134905af180156107cb5761067791600091610f4c575b5060405190151581529081906020820190565b610f6e915060203d602011610f74575b610f6681836111d8565b810190611a8e565b38610f39565b503d610f5c565b80610f9b610f96610f90610fb694888b611986565b35611575565b6119e9565b610fa5828861199b565b52610fb0818761199b565b506118ce565b610ed0565b60405163743be11560e11b8152600490fd5b34610255576000366003190112610255576001546040516001600160a01b039091168152602090f35b908082519081815260208091019281808460051b8301019501936000915b8483106110245750505050505090565b9091929394958480600192601f1985820301865289519061104e6080835190808452830190610a64565b91858060401b03848201511684830152604060ff81830151169083015260608091015115159101529801930193019194939290611014565b906020610a9a928181520190610ff6565b3461025557602080600319360112610255576004356001600160401b038111610255576110c890369060040161025a565b6110d1816111fb565b9260406110e160405195866111d8565b828552601f196110f0846111fb565b019060005b828110611147575050505060005b80821161111857604051806106778682611086565b8061112d610425610f90611142948688611986565b611137828761199b565b52610fb0818661199b565b611103565b839082516111548161118a565b600060608083528185840152818684015282015282828a010152016110f5565b634e487b7160e01b600052604160045260246000fd5b608081019081106001600160401b038211176111a557604052565b611174565b6001600160401b0381116111a557604052565b604081019081106001600160401b038211176111a557604052565b601f909101601f19168101906001600160401b038211908210176111a557604052565b6001600160401b0381116111a55760051b60200190565b929161121d826111fb565b9161122b60405193846111d8565b829481845260208094019160051b810192831161025557905b8282106112515750505050565b81358152908301908301611244565b6001600160401b0381116111a557601f01601f191660200190565b92919261128782611260565b9161129560405193846111d8565b829481845281830111610255578281602093846000960137010152565b9080601f8301121561025557816020610a9a9335910161127b565b359060ff8216820361025557565b8015150361025557565b9291906112f1816111fb565b9160409161130260405194856111d8565b839581855260208095019160051b8101938385116102555781925b85841061132d5750505050505050565b6001600160401b038435818111610255578401916080838803126102555783516113568161118a565b8335928311610255578361136f898c96958796016112b2565b82528381013561137e816105c9565b8483015261138d8682016112cd565b86830152606080910135906113a1826112db565b82015281520193019261131d565b91909160808184031261025557604051906113c98261118a565b90928391906001600160401b0390823582811161025557816113ec9185016112b2565b8452602083013560208501526040830135918211610255578261141860609492611423948694016112b2565b6040860152016112cd565b910152565b9791956114416114499261145195999c9b98369161127b565b9a369161127b565b94369161127b565b936001600160a01b0386161561156d575b61146e61042583611575565b60208101519096906001600160401b0385811691161161155b5760608701516104c7576114ec6114e560ff89946114df6114d96114d160406114b660025460ff9060a01c1690565b9901936114c4855160ff1690565b868b1696879116916124a1565b925160ff1690565b60ff1690565b906124a1565b82336125a6565b9889156115495787848b936115459a51907f4ba393f2ef2da70e2b9689da0c6a653dad20a8811c0564cacfe3dc04b6a7d6eb8561152884611e8f565b9361153b89604051938493339785612068565b0390a451346125e7565b9190565b604051636efa655f60e01b8152600490fd5b604051633527309d60e11b8152600490fd5b339550611462565b6000526003602052604060002090565b90816020910312610255575190565b81835290916001600160fb1b0383116102555760209260051b809284830137010190565b6040513d6000823e3d90fd5b908060209392818452848401376000828201840152601f01601f1916010190565b9035601e1982360301811215610255570160208101919035906001600160401b03821161025557813603831361025557565b90606060ff61166a8261166361163e61163088806115e5565b6080895260808901916115c4565b6020880135602088015261165560408901896115e5565b9088830360408a01526115c4565b95016112cd565b1691015290565b96959491926080946116af946116a19360018060a01b03168a5260208a015260a060408a015260a08901916115c4565b908682036060880152611617565b930152565b909492936001600160a01b0380831694929385156117f25784816116eb8a610d528b988860409d8e5196879560208701998a611671565b51902060005260046020528660002093868554938416036117e4576001600160a01b0319909216909355611724926103fa91369161127b565b9360208501916117376104258451611575565b9182519261174d610441888a0195865190612433565b6117d357606001516117c2576117a06000805160206127238339815191529493926104936117ba9361178360608c015160ff1690565b9060ff8061179760025460ff9060a01c1690565b169216906124a1565b9151925196516117af84611e8f565b965193849384611eaf565b0390a4600190565b855163188e252560e11b8152600490fd5b8651637cb3947160e11b8152600490fd5b505050505050505050600090565b5050505050505050600090565b9060018060a01b0316600052602052604060002090565b908160209103126102555751610a9a816105c9565b9081526001600160401b03909116602082015261ffff9091166040820152608060608201819052610a9a939101916115c4565b81908337600082820152601f01601f19160190565b81604051928392833781016000815203902090565b959493906116af926060946118aa928952608060208a015260808901916115c4565b908682036040880152611617565b634e487b7160e01b600052601160045260246000fd5b60001981146118dd5760010190565b6118b8565b92939060018060a01b03948585169687156117f25761191093610d529260405196879560208701998a611671565b5190206000526004602052604060002054161490565b90611930826111fb565b61193d60405191826111d8565b828152809261194e601f19916111fb565b019060005b82811061195f57505050565b806060602080938501015201611953565b634e487b7160e01b600052603260045260246000fd5b91908110156119965760051b0190565b611970565b80518210156119965760209160051b010190565b90600182811c921680156119df575b60208310146119c957565b634e487b7160e01b600052602260045260246000fd5b91607f16916119be565b906040519182600082546119fc816119af565b90818452602094600191600181169081600014611a6c5750600114611a2d575b505050611a2b925003836111d8565b565b600090815285812095935091905b818310611a54575050611a2b9350820101388080611a1c565b85548884018501529485019487945091830191611a3b565b92505050611a2b94925060ff191682840152151560051b820101388080611a1c565b908160209103126102555751610a9a816112db565b9035601e1982360301811215610255570160208101919035906001600160401b038211610255578160051b3603831361025557565b90918092808252602080920191600593818360051b8701019581956000925b858410611b0957505050505050505090565b9091929394959697601f19808583030189528935603e198436030181121561025557918388929301604091611b5a611b50611b448480611aa3565b86855286850191611594565b9285810190611aa3565b9091858185039101528083528483019285828a1b8201019683956000915b848310611b9d575050505050505050806001929a019801940192919095949395611af7565b809294969850838a929496989a03018852883590607e1988360301821215610255578e8091896001940190611be46080611bd784806115e5565b90918085528401916115c4565b9183810135611bf2816105c9565b868060401b03168483015260ff611c0a8983016112cd565b1688830152606080910135611c1e816112db565b15159101529a019801930190918d97969492959395611b78565b919695939492611c53611c6392608085526080850190610a64565b906020988483038a860152611594565b95818703604083015284518088528188019180808360051b8b01019701926000905b838210611ca4575050505050610a9a9495506060818503910152611ad8565b90919293978380611cc26001938e601f199082030186528c51610a64565b9a019201920190939291611c85565b90604051611cde8161118a565b606060ff60018395611cef816119e9565b855201546001600160401b0381166020850152604081811c83169085015260481c161515910152565b9092916040820191604081528451809352606081019260208096019060005b818110611d5457505050610a9a9394506020818403910152610ff6565b825186529487019491870191600101611d37565b907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031690813b15610255576040916040518091631cfa86f360e31b82526000928391818381611dc3898c60048401611d18565b03925af180156107cb57611e5a575b50908351815103610fbb57815b8085511115611e1e5780611e14611df9611e19938561199b565b51611e04838961199b565b51865260036020528686206122cf565b6118ce565b611ddf565b507f25bb14184ed47336f0e8c5b9b58e65ca31a0cfd0cf8eb80cd59b5005339b4db69250611e559150604051918291339583611d18565b0390a2565b611e63906111aa565b38611dd2565b6001546001600160a01b03163303611e7d57565b6040516347b3ba4760e01b8152600490fd5b611ea790602060405192828480945193849201610a41565b810103902090565b91611ecc90610a9a94928452606060208501526060840190610a64565b916040818403910152610a64565b6001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000811691823b1561025557600091602483926040519485938492636428e61960e11b845216968760048401525af180156107cb57611f7e575b50600180546001600160a01b0319168217905560405190815233907f8ab4cea230ab04f96aaa9a71fbc4bfa98097c7b9f07e06be33868b500f81002890602090a2565b611f87906111aa565b38611f3b565b6000808052600560205260ff611fb1836000805160206127038339815191526117ff565b5416611ffa578080526005602052611fcc82604083206117ff565b805460ff1916600117905533916001600160a01b0316906000805160206126e38339815191528180a4600190565b905090565b600090808252600560205260ff61201984604085206117ff565b541661206257808252600560205261203483604084206117ff565b805460ff1916600117905533926001600160a01b0316916000805160206126e38339815191529080a4600190565b50905090565b9493926120906060936116af9360018060a01b03168852608060208901526080880190610a64565b908682036040880152610a64565b60008052600560205260ff6120c1336000805160206127038339815191526117ff565b5416156120ca57565b60405163e2517d3f60e01b815233600482015260006024820152604490fd5b80600052600560205260ff6121023360406000206117ff565b54161561210c5750565b6044906040519063e2517d3f60e01b82523360048301526024820152fd5b600090808252600560205260ff61214484604085206117ff565b54161561206257808252600560205261216083604084206117ff565b805460ff1916905533926001600160a01b0316917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b9080a4600190565b60019181546000198482161560081b018116841c928251918285146001146121ca57505050505050600090565b846121d8575b505050505090565b602080951060011461223a575060009081528381209183018401928401916001905b6002828686100114612217575050505050505b38808080806121d0565b835181540361222d575b928501926001016121fa565b9195508591829150612221565b939291505001519060ff1916031561220d5750600061220d565b60405160609190911b6001600160601b031916602082015260148152610a9a816111bd565b90601f811161228757505050565b6000916000526020600020906020601f850160051c830194106122c5575b601f0160051c01915b8281106122ba57505050565b8181556001016122ae565b90925082906122a5565b8151805190939291906001600160401b0381116111a5576122fa816122f484546119af565b84612279565b602080601f83116001146123b857506123939261233983606094600194611a2b999a6000926123ad575b50508160011b916000199060031b1c19161790565b81555b6020850151910180546001600160401b0319166001600160401b039092169190911781559261238c612372604083015160ff1690565b855460ff60401b191660409190911b60ff60401b16178555565b0151151590565b815460ff60481b191690151560481b60ff60481b16179055565b015190503880612324565b90601f198316966123ce85600052602060002090565b926000905b89821061241b5750508360019361239396938593606097611a2b9b9c10612402575b505050811b01815561233c565b015160001960f88460031b161c191690553880806123f5565b806001859682949686015181550195019301906123d3565b6001918151918151831460011461244d5750505050600090565b600160208080958185019401019301915b600282858310011461247257505050505090565b8251815103612487575b91840191840161245e565b6000955085915061247c565b604d81116118dd57600a0a90565b9190808211156124d15781039081116118dd576124bd90612493565b908181029181830414901517156118dd5790565b908181106124de57505090565b81039081116118dd576124f090612493565b9081156124fb570490565b634e487b7160e01b600052601260045260246000fd5b306001600160a01b03821603612525575090565b47821161259457819060008080809481945af13d1561258b573d61254881611260565b9061255660405192836111d8565b8152809260203d92013e5b1561256a575090565b604051632e2af28f60e11b81529081906125879060048301610a89565b0390fd5b60609150612561565b6040516342ff79cb60e01b8152600490fd5b903483116125d5576001600160a01b039081169116036125c35790565b60405163976868d560e01b8152600490fd5b604051637df0c20360e01b8152600490fd5b949791939590929660018060a01b0360015416978087039687116118dd5761260e33612254565b926040519a8b998a9889986332a68c8d60e01b8a5260048a0161012090526101248a0161263a91610a64565b9660031997888b82030160248c015261265291610a64565b878a82030160448b015261266591610a64565b606489019490945260ff16608488015260a48701526001600160401b031660c4860152848103830160e486015261269b91610a64565b90838203016101048401526126af91610a64565b03915a94602095f19081156107cb576000916126c9575090565b610a9a915060203d602011610f7457610f6681836111d856fe2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d05b8ccbb9d4d8fb16ea74ce3c29a41f1b461fbdaff4714a0d9a8eb05499746bc57349f3521319d500766c25bbf2260c005d16a7164533a3596bd7a2790679911a2646970667358221220544392bd16a2f667751d5027315bc924b715b0f40e59a72a9c55686ea1118a3064736f6c63430008180033a264697066735822122016aa94dc25092cf266bd801a628550400dc1f4776d36597abb2583a4c887af9e64736f6c63430008180033

Deployed Bytecode

0x6080600436101561000f57600080fd5b6000803560e01c63b514681e1461002557600080fd5b346100d857602090816003193601126100d857600435801515036100d857601f1991612855603f8101841685019291908584106001600160401b038511176100c45760409593929195926040528084526100dc868501396040519485938185528051938483870152835b8581106100ae57866040818a601f8a8a85828601015201168101030190f35b828101840151898201830152889650830161008f565b634e487b7160e01b83526041600452602483fd5b80fdfe60a08060405234620000d7576306433b1b60e01b8152602081600481335afa908115620000cb5760009162000053575b506080526040516127789081620000dd8239608051818181611d6b0152611ee40152f35b60203d602011620000c3575b601f8101601f191682016001600160401b03811183821017620000af57602091839160405281010312620000ab5751906001600160a01b0382168203620000a85750386200002f565b80fd5b5080fd5b634e487b7160e01b84526041600452602484fd5b503d6200005f565b6040513d6000823e3d90fd5b600080fdfe6080604052600436101561001b575b361561001957600080fd5b005b60003560e01c806301ffc9a7146101cb57806304e7aae9146101c6578063081fafa0146101c15780631b7fd4ec146101bc5780631d34dbc3146101b7578063248a9ca3146101b25780632495a599146101ad57806325a760c2146101a85780632ae9c600146101a35780632da04cba1461019e5780632f2ff15d1461019957806336568abe1461019457806359356c5c1461018f578063755067181461018a5780638ec58bb51461018557806391d1485414610180578063a217fddf1461017b578063bd2d87eb14610176578063c0d7865514610171578063cb05ac031461016c578063d3a2ba7614610167578063d547741f14610162578063d90a730e1461015d578063dde63fed14610158578063e8f6e9e214610153578063f887ea401461014e5763f96e52ca0361000e57611097565b610fcd565b610ea8565b610e89565b610e47565b610e08565b610c84565b610b0c565b610ae3565b610a9d565b610a25565b6109e5565b6109bc565b610872565b610856565b61080f565b6107d0565b610718565b6106f7565b6106d3565b6106aa565b61067b565b6105da565b6104fd565b610395565b6102d4565b346102555760203660031901126102555760043563ffffffff60e01b8116809103610255576020906303ca2c9760e01b8114908115610210575b506040519015158152f35b637965db0b60e01b81149150811561022a575b5038610205565b63950a21e160e01b811491508115610244575b5038610223565b6301ffc9a760e01b1490503861023d565b600080fd5b9181601f84011215610255578235916001600160401b038311610255576020808501948460051b01011161025557565b6040600319820112610255576001600160401b039160043583811161025557826102b69160040161025a565b93909392602435918211610255576102d09160040161025a565b9091565b346102555761030d6102ff6103076102eb3661028a565b94916102f893919361209e565b3691611212565b9236916112e5565b90611d68565b602060405160018152f35b600435906001600160a01b038216820361025557565b602435906001600160a01b038216820361025557565b604435906001600160a01b038216820361025557565b9181601f84011215610255578235916001600160401b038311610255576020838186019501011161025557565b908160809103126102555790565b6080366003190112610255576103a9610318565b6001600160401b03604435818111610255576103c990369060040161035a565b909160643590811161025557610402926103ea6103fa923690600401610387565b926103f3611e69565b369161127b565b5036906113af565b906001600160a01b03811680156104eb57602083019161042a6104258451611575565b611cd1565b928351936104456104416040880196875190612433565b1590565b6104d957606001516104c75761049960008051602061272383398151915292610493610475606089015160ff1690565b60ff8061048860025460ff9060a01c1690565b1691166024356124a1565b90612511565b935190519451936104b96104ac83611e8f565b9560405193849384611eaf565b0390a4602060405160018152f35b60405163188e252560e11b8152600490fd5b604051637cb3947160e11b8152600490fd5b6040516351c7b3e160e01b8152600490fd5b346102555760a036600319011261025557610516610318565b61051e61032e565b90610527610344565b6001600160401b0392906064358481116102555761054990369060040161025a565b9290946084359081116102555761056490369060040161025a565b926000546105b757600280546001600160a81b0319166001600160a01b0390941693909317600960a11b179092556001600055610019956105b294610307936102ff9291906102f890611eda565b611f8d565b60405163180cbc2160e21b8152600490fd5b6001600160401b0381160361025557565b60e0366003190112610255576105ee610318565b6001600160401b03906024358281116102555761060f90369060040161035a565b906084359061061d826105c9565b60a4358581116102555761063590369060040161035a565b92909160c4359687116102555761065361066397369060040161035a565b9690956064359260443592611428565b604080519215158352602083019190915290f35b0390f35b346102555760203660031901126102555760043560005260056020526020600160406000200154604051908152f35b34610255576000366003190112610255576002546040516001600160a01b039091168152602090f35b3461025557600036600319011261025557602060ff60025460a01c16604051908152f35b346102555760003660031901126102555760405161010160f01b8152602090f35b3461025557610763602061072b3661028a565b9492909161077560018060a01b03600154169360405197889687958695632d79c1e160e01b8752604060048801526044870191611594565b84810360031901602486015291611594565b03915afa80156107cb576106779160009161079c575b506040519081529081906020820190565b6107be915060203d6020116107c4575b6107b681836111d8565b810190611585565b3861078b565b503d6107ac565b6115b8565b34610255576040366003190112610255576100196004356107ef61032e565b9080600052600560205261080a6001604060002001546120e9565b611fff565b346102555760403660031901126102555761082861032e565b336001600160a01b03821603610844576100199060043561212a565b60405163334bd91960e11b8152600490fd5b3461025557600036600319011261025557602047604051908152f35b34610255576060366003190112610255576001600160401b03600435818111610255576108a390369060040161025a565b90602435838111610255576108bc90369060040161025a565b9190604435858111610255576108d6903690600401610387565b946108df611e69565b6000956020810135875260036020526040872090604081013590601e19813603018212156109535701803592831161094f5760200190823603821361094f576109306104419261093694369161127b565b9061219d565b6104d9576102ff6103079261094c953691611212565b80f35b8780fd5b8880fd5b60a0600319820112610255576004356001600160a01b03811681036102555791602435916001600160401b0391604435838111610255578261099b9160040161035a565b93909392606435918211610255576109b591600401610387565b9060843590565b346102555760206109db6109cf36610957565b949390939291926116b4565b6040519015158152f35b3461025557604036600319011261025557602060ff610a19610a0561032e565b6004356000526005845260406000206117ff565b54166040519015158152f35b3461025557600036600319011261025557602060405160008152f35b60005b838110610a545750506000910152565b8181015183820152602001610a44565b90602091610a7d81518092818552858086019101610a41565b601f01601f1916010190565b906020610a9a928181520190610a64565b90565b3461025557600036600319011261025557610677604051610abd816111bd565b6005815264536f6e696360d81b6020820152604051918291602083526020830190610a64565b346102555760203660031901126102555761030d610aff610318565b610b0761209e565b611eda565b3461025557608036600319011261025557602435600435610b2c826105c9565b60443561ffff81168103610255576001600160401b0360643581811161025557610b5a90369060040161035a565b600154909390610b8090610b74906001600160a01b031681565b6001600160a01b031690565b6040516329d2f02160e21b815260048101879052602097909291908884602481855afa9788156107cb578994600099610c55575b50610bf1610bd46001610bc684611575565b01546001600160401b031690565b986040519788968795869562092e8760e71b87526004870161182b565b03915afa9485156107cb57600095610c36575b505081811690831610610c2e5750905b604080519182526001600160401b03929092166020820152f35b905090610c14565b610c4d929550803d106107c4576107b681836111d8565b923880610c04565b610c76919950853d8711610c7d575b610c6e81836111d8565b810190611816565b9738610bb4565b503d610c64565b346102555760a036600319011261025557610c9d610318565b6001600160401b03906024359060443583811161025557610cc290369060040161035a565b909260643585811161025557610cdc903690600401610387565b90608435958611610255577f89fecad7e82e777b95efd8da30d0290c555adcd7d5fd59c2bbd44752bc09ccab610e0395610de595610dfb610d2360409a369060040161035a565b9096610d2d611e69565b610d8a8460009d8e8a8d610d608b610d5285548a885195869460208601988c8a611671565b03601f1981018352826111d8565b519020815260046020522080546001600160a01b0319166001600160a01b03909216919091179055565b8b54998a92604051602081610dca610dac82610da689806115e5565b9061185e565b83880135815283610dc060408a018a6115e5565b919092019161185e565b60ff610dd8606089016112cd565b1681520301902099611873565b9860405195869560018060a01b03169886611888565b0390a46118ce565b815580f35b3461025557604036600319011261025557610019600435610e2761032e565b90806000526005602052610e426001604060002001546120e9565b61212a565b3461025557600036600319011261025557610677604051610e67816111bd565b60018152605360f81b6020820152604051918291602083526020830190610a64565b346102555760206109db610e9c36610957565b949390939291926118e2565b610eb13661028a565b610ebc92919261209e565b808203610fbb57610ecc82611926565b9260005b808411610f7b57506001549394602094909190610f2190610ef9906001600160a01b0316610b74565b93610f0333612254565b604051633bde546760e11b8152988997889687969360048801611c38565b039134905af180156107cb5761067791600091610f4c575b5060405190151581529081906020820190565b610f6e915060203d602011610f74575b610f6681836111d8565b810190611a8e565b38610f39565b503d610f5c565b80610f9b610f96610f90610fb694888b611986565b35611575565b6119e9565b610fa5828861199b565b52610fb0818761199b565b506118ce565b610ed0565b60405163743be11560e11b8152600490fd5b34610255576000366003190112610255576001546040516001600160a01b039091168152602090f35b908082519081815260208091019281808460051b8301019501936000915b8483106110245750505050505090565b9091929394958480600192601f1985820301865289519061104e6080835190808452830190610a64565b91858060401b03848201511684830152604060ff81830151169083015260608091015115159101529801930193019194939290611014565b906020610a9a928181520190610ff6565b3461025557602080600319360112610255576004356001600160401b038111610255576110c890369060040161025a565b6110d1816111fb565b9260406110e160405195866111d8565b828552601f196110f0846111fb565b019060005b828110611147575050505060005b80821161111857604051806106778682611086565b8061112d610425610f90611142948688611986565b611137828761199b565b52610fb0818661199b565b611103565b839082516111548161118a565b600060608083528185840152818684015282015282828a010152016110f5565b634e487b7160e01b600052604160045260246000fd5b608081019081106001600160401b038211176111a557604052565b611174565b6001600160401b0381116111a557604052565b604081019081106001600160401b038211176111a557604052565b601f909101601f19168101906001600160401b038211908210176111a557604052565b6001600160401b0381116111a55760051b60200190565b929161121d826111fb565b9161122b60405193846111d8565b829481845260208094019160051b810192831161025557905b8282106112515750505050565b81358152908301908301611244565b6001600160401b0381116111a557601f01601f191660200190565b92919261128782611260565b9161129560405193846111d8565b829481845281830111610255578281602093846000960137010152565b9080601f8301121561025557816020610a9a9335910161127b565b359060ff8216820361025557565b8015150361025557565b9291906112f1816111fb565b9160409161130260405194856111d8565b839581855260208095019160051b8101938385116102555781925b85841061132d5750505050505050565b6001600160401b038435818111610255578401916080838803126102555783516113568161118a565b8335928311610255578361136f898c96958796016112b2565b82528381013561137e816105c9565b8483015261138d8682016112cd565b86830152606080910135906113a1826112db565b82015281520193019261131d565b91909160808184031261025557604051906113c98261118a565b90928391906001600160401b0390823582811161025557816113ec9185016112b2565b8452602083013560208501526040830135918211610255578261141860609492611423948694016112b2565b6040860152016112cd565b910152565b9791956114416114499261145195999c9b98369161127b565b9a369161127b565b94369161127b565b936001600160a01b0386161561156d575b61146e61042583611575565b60208101519096906001600160401b0385811691161161155b5760608701516104c7576114ec6114e560ff89946114df6114d96114d160406114b660025460ff9060a01c1690565b9901936114c4855160ff1690565b868b1696879116916124a1565b925160ff1690565b60ff1690565b906124a1565b82336125a6565b9889156115495787848b936115459a51907f4ba393f2ef2da70e2b9689da0c6a653dad20a8811c0564cacfe3dc04b6a7d6eb8561152884611e8f565b9361153b89604051938493339785612068565b0390a451346125e7565b9190565b604051636efa655f60e01b8152600490fd5b604051633527309d60e11b8152600490fd5b339550611462565b6000526003602052604060002090565b90816020910312610255575190565b81835290916001600160fb1b0383116102555760209260051b809284830137010190565b6040513d6000823e3d90fd5b908060209392818452848401376000828201840152601f01601f1916010190565b9035601e1982360301811215610255570160208101919035906001600160401b03821161025557813603831361025557565b90606060ff61166a8261166361163e61163088806115e5565b6080895260808901916115c4565b6020880135602088015261165560408901896115e5565b9088830360408a01526115c4565b95016112cd565b1691015290565b96959491926080946116af946116a19360018060a01b03168a5260208a015260a060408a015260a08901916115c4565b908682036060880152611617565b930152565b909492936001600160a01b0380831694929385156117f25784816116eb8a610d528b988860409d8e5196879560208701998a611671565b51902060005260046020528660002093868554938416036117e4576001600160a01b0319909216909355611724926103fa91369161127b565b9360208501916117376104258451611575565b9182519261174d610441888a0195865190612433565b6117d357606001516117c2576117a06000805160206127238339815191529493926104936117ba9361178360608c015160ff1690565b9060ff8061179760025460ff9060a01c1690565b169216906124a1565b9151925196516117af84611e8f565b965193849384611eaf565b0390a4600190565b855163188e252560e11b8152600490fd5b8651637cb3947160e11b8152600490fd5b505050505050505050600090565b5050505050505050600090565b9060018060a01b0316600052602052604060002090565b908160209103126102555751610a9a816105c9565b9081526001600160401b03909116602082015261ffff9091166040820152608060608201819052610a9a939101916115c4565b81908337600082820152601f01601f19160190565b81604051928392833781016000815203902090565b959493906116af926060946118aa928952608060208a015260808901916115c4565b908682036040880152611617565b634e487b7160e01b600052601160045260246000fd5b60001981146118dd5760010190565b6118b8565b92939060018060a01b03948585169687156117f25761191093610d529260405196879560208701998a611671565b5190206000526004602052604060002054161490565b90611930826111fb565b61193d60405191826111d8565b828152809261194e601f19916111fb565b019060005b82811061195f57505050565b806060602080938501015201611953565b634e487b7160e01b600052603260045260246000fd5b91908110156119965760051b0190565b611970565b80518210156119965760209160051b010190565b90600182811c921680156119df575b60208310146119c957565b634e487b7160e01b600052602260045260246000fd5b91607f16916119be565b906040519182600082546119fc816119af565b90818452602094600191600181169081600014611a6c5750600114611a2d575b505050611a2b925003836111d8565b565b600090815285812095935091905b818310611a54575050611a2b9350820101388080611a1c565b85548884018501529485019487945091830191611a3b565b92505050611a2b94925060ff191682840152151560051b820101388080611a1c565b908160209103126102555751610a9a816112db565b9035601e1982360301811215610255570160208101919035906001600160401b038211610255578160051b3603831361025557565b90918092808252602080920191600593818360051b8701019581956000925b858410611b0957505050505050505090565b9091929394959697601f19808583030189528935603e198436030181121561025557918388929301604091611b5a611b50611b448480611aa3565b86855286850191611594565b9285810190611aa3565b9091858185039101528083528483019285828a1b8201019683956000915b848310611b9d575050505050505050806001929a019801940192919095949395611af7565b809294969850838a929496989a03018852883590607e1988360301821215610255578e8091896001940190611be46080611bd784806115e5565b90918085528401916115c4565b9183810135611bf2816105c9565b868060401b03168483015260ff611c0a8983016112cd565b1688830152606080910135611c1e816112db565b15159101529a019801930190918d97969492959395611b78565b919695939492611c53611c6392608085526080850190610a64565b906020988483038a860152611594565b95818703604083015284518088528188019180808360051b8b01019701926000905b838210611ca4575050505050610a9a9495506060818503910152611ad8565b90919293978380611cc26001938e601f199082030186528c51610a64565b9a019201920190939291611c85565b90604051611cde8161118a565b606060ff60018395611cef816119e9565b855201546001600160401b0381166020850152604081811c83169085015260481c161515910152565b9092916040820191604081528451809352606081019260208096019060005b818110611d5457505050610a9a9394506020818403910152610ff6565b825186529487019491870191600101611d37565b907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031690813b15610255576040916040518091631cfa86f360e31b82526000928391818381611dc3898c60048401611d18565b03925af180156107cb57611e5a575b50908351815103610fbb57815b8085511115611e1e5780611e14611df9611e19938561199b565b51611e04838961199b565b51865260036020528686206122cf565b6118ce565b611ddf565b507f25bb14184ed47336f0e8c5b9b58e65ca31a0cfd0cf8eb80cd59b5005339b4db69250611e559150604051918291339583611d18565b0390a2565b611e63906111aa565b38611dd2565b6001546001600160a01b03163303611e7d57565b6040516347b3ba4760e01b8152600490fd5b611ea790602060405192828480945193849201610a41565b810103902090565b91611ecc90610a9a94928452606060208501526060840190610a64565b916040818403910152610a64565b6001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000811691823b1561025557600091602483926040519485938492636428e61960e11b845216968760048401525af180156107cb57611f7e575b50600180546001600160a01b0319168217905560405190815233907f8ab4cea230ab04f96aaa9a71fbc4bfa98097c7b9f07e06be33868b500f81002890602090a2565b611f87906111aa565b38611f3b565b6000808052600560205260ff611fb1836000805160206127038339815191526117ff565b5416611ffa578080526005602052611fcc82604083206117ff565b805460ff1916600117905533916001600160a01b0316906000805160206126e38339815191528180a4600190565b905090565b600090808252600560205260ff61201984604085206117ff565b541661206257808252600560205261203483604084206117ff565b805460ff1916600117905533926001600160a01b0316916000805160206126e38339815191529080a4600190565b50905090565b9493926120906060936116af9360018060a01b03168852608060208901526080880190610a64565b908682036040880152610a64565b60008052600560205260ff6120c1336000805160206127038339815191526117ff565b5416156120ca57565b60405163e2517d3f60e01b815233600482015260006024820152604490fd5b80600052600560205260ff6121023360406000206117ff565b54161561210c5750565b6044906040519063e2517d3f60e01b82523360048301526024820152fd5b600090808252600560205260ff61214484604085206117ff565b54161561206257808252600560205261216083604084206117ff565b805460ff1916905533926001600160a01b0316917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b9080a4600190565b60019181546000198482161560081b018116841c928251918285146001146121ca57505050505050600090565b846121d8575b505050505090565b602080951060011461223a575060009081528381209183018401928401916001905b6002828686100114612217575050505050505b38808080806121d0565b835181540361222d575b928501926001016121fa565b9195508591829150612221565b939291505001519060ff1916031561220d5750600061220d565b60405160609190911b6001600160601b031916602082015260148152610a9a816111bd565b90601f811161228757505050565b6000916000526020600020906020601f850160051c830194106122c5575b601f0160051c01915b8281106122ba57505050565b8181556001016122ae565b90925082906122a5565b8151805190939291906001600160401b0381116111a5576122fa816122f484546119af565b84612279565b602080601f83116001146123b857506123939261233983606094600194611a2b999a6000926123ad575b50508160011b916000199060031b1c19161790565b81555b6020850151910180546001600160401b0319166001600160401b039092169190911781559261238c612372604083015160ff1690565b855460ff60401b191660409190911b60ff60401b16178555565b0151151590565b815460ff60481b191690151560481b60ff60481b16179055565b015190503880612324565b90601f198316966123ce85600052602060002090565b926000905b89821061241b5750508360019361239396938593606097611a2b9b9c10612402575b505050811b01815561233c565b015160001960f88460031b161c191690553880806123f5565b806001859682949686015181550195019301906123d3565b6001918151918151831460011461244d5750505050600090565b600160208080958185019401019301915b600282858310011461247257505050505090565b8251815103612487575b91840191840161245e565b6000955085915061247c565b604d81116118dd57600a0a90565b9190808211156124d15781039081116118dd576124bd90612493565b908181029181830414901517156118dd5790565b908181106124de57505090565b81039081116118dd576124f090612493565b9081156124fb570490565b634e487b7160e01b600052601260045260246000fd5b306001600160a01b03821603612525575090565b47821161259457819060008080809481945af13d1561258b573d61254881611260565b9061255660405192836111d8565b8152809260203d92013e5b1561256a575090565b604051632e2af28f60e11b81529081906125879060048301610a89565b0390fd5b60609150612561565b6040516342ff79cb60e01b8152600490fd5b903483116125d5576001600160a01b039081169116036125c35790565b60405163976868d560e01b8152600490fd5b604051637df0c20360e01b8152600490fd5b949791939590929660018060a01b0360015416978087039687116118dd5761260e33612254565b926040519a8b998a9889986332a68c8d60e01b8a5260048a0161012090526101248a0161263a91610a64565b9660031997888b82030160248c015261265291610a64565b878a82030160448b015261266591610a64565b606489019490945260ff16608488015260a48701526001600160401b031660c4860152848103830160e486015261269b91610a64565b90838203016101048401526126af91610a64565b03915a94602095f19081156107cb576000916126c9575090565b610a9a915060203d602011610f7457610f6681836111d856fe2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d05b8ccbb9d4d8fb16ea74ce3c29a41f1b461fbdaff4714a0d9a8eb05499746bc57349f3521319d500766c25bbf2260c005d16a7164533a3596bd7a2790679911a2646970667358221220544392bd16a2f667751d5027315bc924b715b0f40e59a72a9c55686ea1118a3064736f6c63430008180033a264697066735822122016aa94dc25092cf266bd801a628550400dc1f4776d36597abb2583a4c887af9e64736f6c63430008180033

Block Transaction Gas Used Reward
view all blocks produced

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

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits

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.