ETH Price: $3,033.64 (-5.95%)
Gas: 8 Gwei

Contract

0xB01BC38909413f5dbb8F18a9b5787A62ce1282aE
 

Overview

ETH Balance

0 ETH

Eth Value

$0.00

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
Value
Receive Message174327672023-06-08 2:55:59392 days ago1686192959IN
0xB01BC389...2ce1282aE
0 ETH0.0105231520.85750555
Receive Message174312602023-06-07 21:49:59393 days ago1686174599IN
0xB01BC389...2ce1282aE
0 ETH0.0104302120.83621031
Receive Message173286652023-05-24 11:11:23407 days ago1684926683IN
0xB01BC389...2ce1282aE
0 ETH0.0236379247.09820057
Receive Message171334182023-04-26 22:39:35435 days ago1682548775IN
0xB01BC389...2ce1282aE
0 ETH0.0212547443.37075527
Receive Message171332692023-04-26 22:08:59435 days ago1682546939IN
0xB01BC389...2ce1282aE
0 ETH0.0301447460.21517708
Receive Message171300052023-04-26 11:08:59435 days ago1682507339IN
0xB01BC389...2ce1282aE
0 ETH0.015657842.25215639
Receive Message171291232023-04-26 8:09:35435 days ago1682496575IN
0xB01BC389...2ce1282aE
0 ETH0.0158783242.84121272
Receive Message171290052023-04-26 7:45:59435 days ago1682495159IN
0xB01BC389...2ce1282aE
0 ETH0.0169703245.7934754
Receive Message171289952023-04-26 7:43:59435 days ago1682495039IN
0xB01BC389...2ce1282aE
0 ETH0.0168475145.62570322
Receive Message171288782023-04-26 7:20:23435 days ago1682493623IN
0xB01BC389...2ce1282aE
0 ETH0.0230924345.39669331
Receive Message171288702023-04-26 7:18:47435 days ago1682493527IN
0xB01BC389...2ce1282aE
0 ETH0.0216432643.12344566
Receive Message171288622023-04-26 7:17:11435 days ago1682493431IN
0xB01BC389...2ce1282aE
0 ETH0.0232893546.52399703
Receive Message170841492023-04-20 0:20:47442 days ago1681950047IN
0xB01BC389...2ce1282aE
0 ETH0.0319635462.85787856
Receive Message170841412023-04-20 0:19:11442 days ago1681949951IN
0xB01BC389...2ce1282aE
0 ETH0.0293480658.31614476
Receive Message170485352023-04-14 23:11:47447 days ago1681513907IN
0xB01BC389...2ce1282aE
0 ETH0.0135756526.90592556
Receive Message170485102023-04-14 23:06:47447 days ago1681513607IN
0xB01BC389...2ce1282aE
0 ETH0.0136607627.00524497
Receive Message170484802023-04-14 23:00:47447 days ago1681513247IN
0xB01BC389...2ce1282aE
0 ETH0.018814437.47814862
Receive Message170482922023-04-14 22:22:35447 days ago1681510955IN
0xB01BC389...2ce1282aE
0 ETH0.02125542.34368674
Receive Message170482842023-04-14 22:20:59447 days ago1681510859IN
0xB01BC389...2ce1282aE
0 ETH0.0212925941.98324212
Receive Message167922842023-03-09 17:58:59483 days ago1678384739IN
0xB01BC389...2ce1282aE
0 ETH0.0172061936.51624228
Propose New Owne...166728882023-02-20 23:00:35500 days ago1676934035IN
0xB01BC389...2ce1282aE
0 ETH0.0020680629.67097224
Receive Message165761252023-02-07 9:51:35513 days ago1675763495IN
0xB01BC389...2ce1282aE
0 ETH0.0131244327.85090311
Receive Message165760862023-02-07 9:43:47513 days ago1675763027IN
0xB01BC389...2ce1282aE
0 ETH0.0127509827.13397561
Receive Message165760732023-02-07 9:41:11513 days ago1675762871IN
0xB01BC389...2ce1282aE
0 ETH0.0134338628.59273739
Receive Message165281812023-01-31 17:04:23520 days ago1675184663IN
0xB01BC389...2ce1282aE
0 ETH0.023122349.07329357
View all transactions

View more zero value Internal Transactions in Advanced View mode

Advanced mode:
Loading...
Loading

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

Contract Name:
PolygonHubConnector

Compiler Version
v0.8.17+commit.8df45f5f

Optimization Enabled:
Yes with 200 runs

Other Settings:
default evmVersion, MIT license
File 1 of 12 : PolygonHubConnector.sol
// SPDX-License-Identifier: MIT OR Apache-2.0
pragma solidity 0.8.17;

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

import {FxBaseRootTunnel} from "./tunnel/FxBaseRootTunnel.sol";

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

contract PolygonHubConnector is HubConnector, FxBaseRootTunnel {
  // ============ Constructor ============
  constructor(
    uint32 _domain,
    uint32 _mirrorDomain,
    address _amb,
    address _rootManager,
    address _mirrorConnector,
    address _checkPointManager
  )
    HubConnector(_domain, _mirrorDomain, _amb, _rootManager, _mirrorConnector)
    FxBaseRootTunnel(_checkPointManager, _amb)
  {}

  // ============ Private fns ============

  function _verifySender(address _expected) internal view override returns (bool) {
    // NOTE: always return false on polygon
    return false;
  }

  function _sendMessage(bytes memory _data, bytes memory _encodedData) internal override {
    // Should not include specialized calldata
    require(_encodedData.length == 0, "!data length");
    _sendMessageToChild(_data);
  }

  function _processMessageFromChild(bytes memory message) internal override {
    // NOTE: crosschain sender is not directly exposed by the child message

    // do not need any additional sender or origin checks here since the proof contains inclusion proofs of the snapshots

    // get the data (should be the aggregate root)
    require(message.length == 32, "!length");
    // update the root on the root manager
    IRootManager(ROOT_MANAGER).aggregate(MIRROR_DOMAIN, bytes32(message));

    emit MessageProcessed(message, msg.sender);
  }

  // DO NOT override _processMessage, should revert from `Connector` class. All messages must use the
  // `_processMessageFromChild` flow.

  function _setMirrorConnector(address _mirrorConnector) internal override {
    // NOTE: FxBaseRootTunnel has the following code in their `setFxChildTunnel`:
    // ```
    // require(fxChildTunnel == address(0x0), "FxBaseRootTunnel: CHILD_TUNNEL_ALREADY_SET");
    // ```
    // Which means this function will revert if updating the `mirrorConnector`. In that case, in
    // changes  the
    // hub connector should also be redeployed
    super._setMirrorConnector(_mirrorConnector);

    setFxChildTunnel(_mirrorConnector);
  }
}

File 2 of 12 : Connector.sol
// SPDX-License-Identifier: MIT OR Apache-2.0
pragma solidity 0.8.17;

import {ProposedOwnable} from "../../shared/ProposedOwnable.sol";
import {IConnector} from "../interfaces/IConnector.sol";

/**
 * @title Connector
 * @author Connext Labs, Inc.
 * @notice This contract has the messaging interface functions used by all connectors.
 *
 * @dev This contract stores information about mirror connectors, but can be used as a
 * base for contracts that do not have a mirror (i.e. the connector handling messaging on
 * mainnet). In this case, the `mirrorConnector` and `MIRROR_DOMAIN`
 * will be empty
 *
 * @dev If ownership is renounced, this contract will be unable to update its `mirrorConnector`
 * or `mirrorGas`
 */
abstract contract Connector is ProposedOwnable, IConnector {
  // ========== Custom Errors ===========

  error Connector__processMessage_notUsed();

  // ============ Events ============

  event NewConnector(
    uint32 indexed domain,
    uint32 indexed mirrorDomain,
    address amb,
    address rootManager,
    address mirrorConnector
  );

  event MirrorConnectorUpdated(address previous, address current);

  // ============ Public Storage ============

  /**
   * @notice The domain of this Messaging (i.e. Connector) contract.
   */
  uint32 public immutable DOMAIN;

  /**
   * @notice Address of the AMB on this domain.
   */
  address public immutable AMB;

  /**
   * @notice RootManager contract address.
   */
  address public immutable ROOT_MANAGER;

  /**
   * @notice The domain of the corresponding messaging (i.e. Connector) contract.
   */
  uint32 public immutable MIRROR_DOMAIN;

  /**
   * @notice Connector on L2 for L1 connectors, and vice versa.
   */
  address public mirrorConnector;

  // ============ Modifiers ============

  /**
   * @notice Errors if the msg.sender is not the registered AMB
   */
  modifier onlyAMB() {
    require(msg.sender == AMB, "!AMB");
    _;
  }

  /**
   * @notice Errors if the msg.sender is not the registered ROOT_MANAGER
   */
  modifier onlyRootManager() {
    // NOTE: RootManager will be zero address for spoke connectors.
    // Only root manager can dispatch a message to spokes/L2s via the hub connector.
    require(msg.sender == ROOT_MANAGER, "!rootManager");
    _;
  }

  // ============ Constructor ============

  /**
   * @notice Creates a new HubConnector instance
   * @dev The connectors are deployed such that there is one on each side of an AMB (i.e.
   * for optimism, there is one connector on optimism and one connector on mainnet)
   * @param _domain The domain this connector lives on
   * @param _mirrorDomain The spoke domain
   * @param _amb The address of the amb on the domain this connector lives on
   * @param _rootManager The address of the RootManager on mainnet
   * @param _mirrorConnector The address of the spoke connector
   */
  constructor(
    uint32 _domain,
    uint32 _mirrorDomain,
    address _amb,
    address _rootManager,
    address _mirrorConnector
  ) ProposedOwnable() {
    // set the owner
    _setOwner(msg.sender);

    // sanity checks on values
    require(_domain != 0, "empty domain");
    require(_rootManager != address(0), "empty rootManager");
    // see note at top of contract on why the mirror values are not sanity checked

    // set immutables
    DOMAIN = _domain;
    AMB = _amb;
    ROOT_MANAGER = _rootManager;
    MIRROR_DOMAIN = _mirrorDomain;
    // set mutables if defined
    if (_mirrorConnector != address(0)) {
      _setMirrorConnector(_mirrorConnector);
    }

    emit NewConnector(_domain, _mirrorDomain, _amb, _rootManager, _mirrorConnector);
  }

  // ============ Receivable ============
  /**
   * @notice Connectors may need to receive native asset to handle fees when sending a
   * message
   */
  receive() external payable {}

  // ============ Admin Functions ============

  /**
   * @notice Sets the address of the l2Connector for this domain
   */
  function setMirrorConnector(address _mirrorConnector) public onlyOwner {
    _setMirrorConnector(_mirrorConnector);
  }

  // ============ Public Functions ============

  /**
   * @notice Processes a message received by an AMB
   * @dev This is called by AMBs to process messages originating from mirror connector
   */
  function processMessage(bytes memory _data) external virtual onlyAMB {
    _processMessage(_data);
    emit MessageProcessed(_data, msg.sender);
  }

  /**
   * @notice Checks the cross domain sender for a given address
   */
  function verifySender(address _expected) external returns (bool) {
    return _verifySender(_expected);
  }

  // ============ Virtual Functions ============

  /**
   * @notice This function is used by the Connext contract on the l2 domain to send a message to the
   * l1 domain (i.e. called by Connext on optimism to send a message to mainnet with roots)
   * @param _data The contents of the message
   * @param _encodedData Data used to send the message; specific to connector
   */
  function _sendMessage(bytes memory _data, bytes memory _encodedData) internal virtual;

  /**
   * @notice This function is used by the AMBs to handle incoming messages. Should store the latest
   * root generated on the l2 domain.
   */
  function _processMessage(
    bytes memory /* _data */
  ) internal virtual {
    // By default, reverts. This is to ensure the call path is not used unless this function is
    // overridden by the inheriting class
    revert Connector__processMessage_notUsed();
  }

  /**
   * @notice Verify that the msg.sender is the correct AMB contract, and that the message's origin sender
   * is the expected address.
   * @dev Should be overridden by the implementing Connector contract.
   */
  function _verifySender(address _expected) internal virtual returns (bool);

  // ============ Private Functions ============

  function _setMirrorConnector(address _mirrorConnector) internal virtual {
    emit MirrorConnectorUpdated(mirrorConnector, _mirrorConnector);
    mirrorConnector = _mirrorConnector;
  }
}

File 3 of 12 : HubConnector.sol
// SPDX-License-Identifier: MIT OR Apache-2.0
pragma solidity 0.8.17;

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

/**
 * @title HubConnector
 * @author Connext Labs, Inc.
 * @notice This contract implements the messaging functions needed on the hub-side of a given AMB.
 * The HubConnector has a limited set of functionality compared to the SpokeConnector, namely that
 * it contains no logic to store or prove messages.
 *
 * @dev This contract should be deployed on the hub-side of an AMB (i.e. on L1), and contracts
 * which extend this should implement the virtual functions defined in the BaseConnector class
 */
abstract contract HubConnector is Connector {
  /**
   * @notice Creates a new HubConnector instance
   * @dev The connectors are deployed such that there is one on each side of an AMB (i.e.
   * for optimism, there is one connector on optimism and one connector on mainnet)
   * @param _domain The domain this connector lives on
   * @param _mirrorDomain The spoke domain
   * @param _amb The address of the amb on the domain this connector lives on
   * @param _rootManager The address of the RootManager on mainnet
   * @param _mirrorConnector The address of the spoke connector
   */
  constructor(
    uint32 _domain,
    uint32 _mirrorDomain,
    address _amb,
    address _rootManager,
    address _mirrorConnector
  ) Connector(_domain, _mirrorDomain, _amb, _rootManager, _mirrorConnector) {}

  // ============ Public fns ============
  /**
   * @notice Sends a message over the amb
   * @dev This is called by the root manager *only* on mainnet to propagate the aggregate root
   */
  function sendMessage(bytes memory _data, bytes memory _encodedData) external payable onlyRootManager {
    _sendMessage(_data, _encodedData);
    emit MessageSent(_data, _encodedData, msg.sender);
  }
}

File 4 of 12 : ExitPayloadReader.sol
// SPDX-License-Identifier: MIT
// https://github.com/fx-portal/contracts/blob/main/contracts/lib/ExitPayloadReader.sol
pragma solidity 0.8.17;

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

library ExitPayloadReader {
  using RLPReader for bytes;
  using RLPReader for RLPReader.RLPItem;

  uint8 constant WORD_SIZE = 32;

  struct ExitPayload {
    RLPReader.RLPItem[] data;
  }

  struct Receipt {
    RLPReader.RLPItem[] data;
    bytes raw;
    uint256 logIndex;
  }

  struct Log {
    RLPReader.RLPItem data;
    RLPReader.RLPItem[] list;
  }

  struct LogTopics {
    RLPReader.RLPItem[] data;
  }

  // copy paste of private copy() from RLPReader to avoid changing of existing contracts
  function copy(
    uint256 src,
    uint256 dest,
    uint256 len
  ) private pure {
    if (len == 0) return;

    // copy as many word sizes as possible
    for (; len > WORD_SIZE - 1; len -= WORD_SIZE) {
      assembly {
        mstore(dest, mload(src))
      }

      src += WORD_SIZE;
      dest += WORD_SIZE;
    }

    if (len == 0) return;

    // left over bytes. Mask is used to remove unwanted bytes from the word
    uint256 mask = 256**(WORD_SIZE - len) - 1;
    assembly {
      let srcpart := and(mload(src), not(mask)) // zero out src
      let destpart := and(mload(dest), mask) // retrieve the bytes
      mstore(dest, or(destpart, srcpart))
    }
  }

  function toExitPayload(bytes memory data) internal pure returns (ExitPayload memory) {
    RLPReader.RLPItem[] memory payloadData = data.toRlpItem().toList();

    return ExitPayload(payloadData);
  }

  function getHeaderNumber(ExitPayload memory payload) internal pure returns (uint256) {
    return payload.data[0].toUint();
  }

  function getBlockProof(ExitPayload memory payload) internal pure returns (bytes memory) {
    return payload.data[1].toBytes();
  }

  function getBlockNumber(ExitPayload memory payload) internal pure returns (uint256) {
    return payload.data[2].toUint();
  }

  function getBlockTime(ExitPayload memory payload) internal pure returns (uint256) {
    return payload.data[3].toUint();
  }

  function getTxRoot(ExitPayload memory payload) internal pure returns (bytes32) {
    return bytes32(payload.data[4].toUint());
  }

  function getReceiptRoot(ExitPayload memory payload) internal pure returns (bytes32) {
    return bytes32(payload.data[5].toUint());
  }

  function getReceipt(ExitPayload memory payload) internal pure returns (Receipt memory receipt) {
    receipt.raw = payload.data[6].toBytes();
    RLPReader.RLPItem memory receiptItem = receipt.raw.toRlpItem();

    if (receiptItem.isList()) {
      // legacy tx
      receipt.data = receiptItem.toList();
    } else {
      // pop first byte before parsting receipt
      bytes memory typedBytes = receipt.raw;
      bytes memory result = new bytes(typedBytes.length - 1);
      uint256 srcPtr;
      uint256 destPtr;
      assembly {
        srcPtr := add(33, typedBytes)
        destPtr := add(0x20, result)
      }

      copy(srcPtr, destPtr, result.length);
      receipt.data = result.toRlpItem().toList();
    }

    receipt.logIndex = getReceiptLogIndex(payload);
    return receipt;
  }

  function getReceiptProof(ExitPayload memory payload) internal pure returns (bytes memory) {
    return payload.data[7].toBytes();
  }

  function getBranchMaskAsBytes(ExitPayload memory payload) internal pure returns (bytes memory) {
    return payload.data[8].toBytes();
  }

  function getBranchMaskAsUint(ExitPayload memory payload) internal pure returns (uint256) {
    return payload.data[8].toUint();
  }

  function getReceiptLogIndex(ExitPayload memory payload) internal pure returns (uint256) {
    return payload.data[9].toUint();
  }

  // Receipt methods
  function toBytes(Receipt memory receipt) internal pure returns (bytes memory) {
    return receipt.raw;
  }

  function getLog(Receipt memory receipt) internal pure returns (Log memory) {
    RLPReader.RLPItem memory logData = receipt.data[3].toList()[receipt.logIndex];
    return Log(logData, logData.toList());
  }

  // Log methods
  function getEmitter(Log memory log) internal pure returns (address) {
    return RLPReader.toAddress(log.list[0]);
  }

  function getTopics(Log memory log) internal pure returns (LogTopics memory) {
    return LogTopics(log.list[1].toList());
  }

  function getData(Log memory log) internal pure returns (bytes memory) {
    return log.list[2].toBytes();
  }

  function toRlpBytes(Log memory log) internal pure returns (bytes memory) {
    return log.data.toRlpBytes();
  }

  // LogTopics methods
  function getField(LogTopics memory topics, uint256 index) internal pure returns (RLPReader.RLPItem memory) {
    return topics.data[index];
  }
}

File 5 of 12 : Merkle.sol
// SPDX-License-Identifier: MIT
// https://github.com/fx-portal/contracts/blob/main/contracts/lib/Merkle.sol
pragma solidity 0.8.17;

library Merkle {
  function checkMembership(
    bytes32 leaf,
    uint256 index,
    bytes32 rootHash,
    bytes memory proof
  ) internal pure returns (bool) {
    require(proof.length % 32 == 0, "Invalid proof length");
    uint256 proofHeight = proof.length / 32;
    // Proof of size n means, height of the tree is n+1.
    // In a tree of height n+1, max #leafs possible is 2 ^ n
    require(index < 2**proofHeight, "Leaf index is too big");

    bytes32 proofElement;
    bytes32 computedHash = leaf;
    uint256 len = proof.length + 1;
    for (uint256 i = 32; i < len; ) {
      assembly {
        proofElement := mload(add(proof, i))
      }

      if (index % 2 == 0) {
        computedHash = keccak256(abi.encodePacked(computedHash, proofElement));
      } else {
        computedHash = keccak256(abi.encodePacked(proofElement, computedHash));
      }

      index = index / 2;

      unchecked {
        i += 32;
      }
    }
    return computedHash == rootHash;
  }
}

File 6 of 12 : MerklePatriciaProof.sol
// SPDX-License-Identifier: MIT
// https://github.com/fx-portal/contracts/blob/main/contracts/lib/MerklePatriciaProof.sol
pragma solidity 0.8.17;

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

library MerklePatriciaProof {
  /*
   * @dev Verifies a merkle patricia proof.
   * @param value The terminating value in the trie.
   * @param encodedPath The path in the trie leading to value.
   * @param rlpParentNodes The rlp encoded stack of nodes.
   * @param root The root hash of the trie.
   * @return The boolean validity of the proof.
   */
  function verify(
    bytes memory value,
    bytes memory encodedPath,
    bytes memory rlpParentNodes,
    bytes32 root
  ) internal pure returns (bool) {
    RLPReader.RLPItem memory item = RLPReader.toRlpItem(rlpParentNodes);
    RLPReader.RLPItem[] memory parentNodes = RLPReader.toList(item);

    bytes memory currentNode;
    RLPReader.RLPItem[] memory currentNodeList;

    bytes32 nodeKey = root;
    uint256 pathPtr = 0;

    bytes memory path = _getNibbleArray(encodedPath);
    if (path.length == 0) {
      return false;
    }

    uint256 len = parentNodes.length;
    for (uint256 i = 0; i < len; ) {
      if (pathPtr > path.length) {
        return false;
      }

      currentNode = RLPReader.toRlpBytes(parentNodes[i]);
      if (nodeKey != keccak256(currentNode)) {
        return false;
      }
      currentNodeList = RLPReader.toList(parentNodes[i]);

      if (currentNodeList.length == 17) {
        if (pathPtr == path.length) {
          if (keccak256(RLPReader.toBytes(currentNodeList[16])) == keccak256(value)) {
            return true;
          } else {
            return false;
          }
        }

        uint8 nextPathNibble = uint8(path[pathPtr]);
        if (nextPathNibble > 16) {
          return false;
        }
        nodeKey = bytes32(RLPReader.toUintStrict(currentNodeList[nextPathNibble]));
        pathPtr += 1;
      } else if (currentNodeList.length == 2) {
        uint256 traversed = _nibblesToTraverse(RLPReader.toBytes(currentNodeList[0]), path, pathPtr);
        if (pathPtr + traversed == path.length) {
          //leaf node
          if (keccak256(RLPReader.toBytes(currentNodeList[1])) == keccak256(value)) {
            return true;
          } else {
            return false;
          }
        }

        //extension node
        if (traversed == 0) {
          return false;
        }

        pathPtr += traversed;
        nodeKey = bytes32(RLPReader.toUintStrict(currentNodeList[1]));
      } else {
        return false;
      }

      unchecked {
        ++i;
      }
    }
  }

  function _nibblesToTraverse(
    bytes memory encodedPartialPath,
    bytes memory path,
    uint256 pathPtr
  ) private pure returns (uint256) {
    uint256 len = 0;
    // encodedPartialPath has elements that are each two hex characters (1 byte), but partialPath
    // and slicedPath have elements that are each one hex character (1 nibble)
    bytes memory partialPath = _getNibbleArray(encodedPartialPath);
    bytes memory slicedPath = new bytes(partialPath.length);

    // pathPtr counts nibbles in path
    // partialPath.length is a number of nibbles
    uint256 _len = pathPtr + partialPath.length;
    for (uint256 i = pathPtr; i < _len; ) {
      bytes1 pathNibble = path[i];
      slicedPath[i - pathPtr] = pathNibble;

      unchecked {
        ++i;
      }
    }

    if (keccak256(partialPath) == keccak256(slicedPath)) {
      len = partialPath.length;
    } else {
      len = 0;
    }
    return len;
  }

  // bytes b must be hp encoded
  function _getNibbleArray(bytes memory b) internal pure returns (bytes memory) {
    bytes memory nibbles = "";
    if (b.length > 0) {
      uint8 offset;
      uint8 hpNibble = uint8(_getNthNibbleOfBytes(0, b));
      if (hpNibble == 1 || hpNibble == 3) {
        nibbles = new bytes(b.length * 2 - 1);
        bytes1 oddNibble = _getNthNibbleOfBytes(1, b);
        nibbles[0] = oddNibble;
        offset = 1;
      } else {
        nibbles = new bytes(b.length * 2 - 2);
        offset = 0;
      }

      uint256 len = nibbles.length;
      for (uint256 i = offset; i < len; ) {
        nibbles[i] = _getNthNibbleOfBytes(i - offset + 2, b);

        unchecked {
          ++i;
        }
      }
    }
    return nibbles;
  }

  function _getNthNibbleOfBytes(uint256 n, bytes memory str) private pure returns (bytes1) {
    return bytes1(n % 2 == 0 ? uint8(str[n / 2]) / 0x10 : uint8(str[n / 2]) % 0x10);
  }
}

File 7 of 12 : RLPReader.sol
// SPDX-License-Identifier: MIT
// https://github.com/fx-portal/contracts/blob/main/contracts/lib/RLPReader.sol
pragma solidity 0.8.17;

library RLPReader {
  uint8 constant STRING_SHORT_START = 0x80;
  uint8 constant STRING_LONG_START = 0xb8;
  uint8 constant LIST_SHORT_START = 0xc0;
  uint8 constant LIST_LONG_START = 0xf8;
  uint8 constant WORD_SIZE = 32;

  struct RLPItem {
    uint256 len;
    uint256 memPtr;
  }

  struct Iterator {
    RLPItem item; // Item that's being iterated over.
    uint256 nextPtr; // Position of the next item in the list.
  }

  /*
   * @dev Returns the next element in the iteration. Reverts if it has not next element.
   * @param self The iterator.
   * @return The next element in the iteration.
   */
  function next(Iterator memory self) internal pure returns (RLPItem memory) {
    require(hasNext(self));

    uint256 ptr = self.nextPtr;
    uint256 itemLength = _itemLength(ptr);
    self.nextPtr = ptr + itemLength;

    return RLPItem(itemLength, ptr);
  }

  /*
   * @dev Returns true if the iteration has more elements.
   * @param self The iterator.
   * @return true if the iteration has more elements.
   */
  function hasNext(Iterator memory self) internal pure returns (bool) {
    RLPItem memory item = self.item;
    return self.nextPtr < item.memPtr + item.len;
  }

  /*
   * @param item RLP encoded bytes
   */
  function toRlpItem(bytes memory item) internal pure returns (RLPItem memory) {
    uint256 memPtr;
    assembly {
      memPtr := add(item, 0x20)
    }

    return RLPItem(item.length, memPtr);
  }

  /*
   * @dev Create an iterator. Reverts if item is not a list.
   * @param self The RLP item.
   * @return An 'Iterator' over the item.
   */
  function iterator(RLPItem memory self) internal pure returns (Iterator memory) {
    require(isList(self));

    uint256 ptr = self.memPtr + _payloadOffset(self.memPtr);
    return Iterator(self, ptr);
  }

  /*
   * @param item RLP encoded bytes
   */
  function rlpLen(RLPItem memory item) internal pure returns (uint256) {
    return item.len;
  }

  /*
   * @param item RLP encoded bytes
   */
  function payloadLen(RLPItem memory item) internal pure returns (uint256) {
    return item.len - _payloadOffset(item.memPtr);
  }

  /*
   * @param item RLP encoded list in bytes
   */
  function toList(RLPItem memory item) internal pure returns (RLPItem[] memory) {
    require(isList(item));

    uint256 items = numItems(item);
    RLPItem[] memory result = new RLPItem[](items);

    uint256 memPtr = item.memPtr + _payloadOffset(item.memPtr);
    uint256 dataLen;
    for (uint256 i = 0; i < items; ) {
      dataLen = _itemLength(memPtr);
      result[i] = RLPItem(dataLen, memPtr);
      memPtr = memPtr + dataLen;

      unchecked {
        ++i;
      }
    }

    return result;
  }

  // @return indicator whether encoded payload is a list. negate this function call for isData.
  function isList(RLPItem memory item) internal pure returns (bool) {
    if (item.len == 0) return false;

    uint8 byte0;
    uint256 memPtr = item.memPtr;
    assembly {
      byte0 := byte(0, mload(memPtr))
    }

    if (byte0 < LIST_SHORT_START) return false;
    return true;
  }

  /*
   * @dev A cheaper version of keccak256(toRlpBytes(item)) that avoids copying memory.
   * @return keccak256 hash of RLP encoded bytes.
   */
  function rlpBytesKeccak256(RLPItem memory item) internal pure returns (bytes32) {
    uint256 ptr = item.memPtr;
    uint256 len = item.len;
    bytes32 result;
    assembly {
      result := keccak256(ptr, len)
    }
    return result;
  }

  function payloadLocation(RLPItem memory item) internal pure returns (uint256, uint256) {
    uint256 offset = _payloadOffset(item.memPtr);
    uint256 memPtr = item.memPtr + offset;
    uint256 len = item.len - offset; // data length
    return (memPtr, len);
  }

  /*
   * @dev A cheaper version of keccak256(toBytes(item)) that avoids copying memory.
   * @return keccak256 hash of the item payload.
   */
  function payloadKeccak256(RLPItem memory item) internal pure returns (bytes32) {
    (uint256 memPtr, uint256 len) = payloadLocation(item);
    bytes32 result;
    assembly {
      result := keccak256(memPtr, len)
    }
    return result;
  }

  /** RLPItem conversions into data types **/

  // @returns raw rlp encoding in bytes
  function toRlpBytes(RLPItem memory item) internal pure returns (bytes memory) {
    bytes memory result = new bytes(item.len);
    if (result.length == 0) return result;

    uint256 ptr;
    assembly {
      ptr := add(0x20, result)
    }

    copy(item.memPtr, ptr, item.len);
    return result;
  }

  // any non-zero byte is considered true
  function toBoolean(RLPItem memory item) internal pure returns (bool) {
    require(item.len == 1);
    uint256 result;
    uint256 memPtr = item.memPtr;
    assembly {
      result := byte(0, mload(memPtr))
    }

    return result == 0 ? false : true;
  }

  function toAddress(RLPItem memory item) internal pure returns (address) {
    // 1 byte for the length prefix
    require(item.len == 21);

    return address(uint160(toUint(item)));
  }

  function toUint(RLPItem memory item) internal pure returns (uint256) {
    require(item.len > 0 && item.len < 33 + 1);

    uint256 offset = _payloadOffset(item.memPtr);
    uint256 len = item.len - offset;

    uint256 result;
    uint256 memPtr = item.memPtr + offset;
    assembly {
      result := mload(memPtr)

      // shfit to the correct location if neccesary
      if lt(len, 32) {
        result := div(result, exp(256, sub(32, len)))
      }
    }

    return result;
  }

  // enforces 32 byte length
  function toUintStrict(RLPItem memory item) internal pure returns (uint256) {
    // one byte prefix
    require(item.len == 33);

    uint256 result;
    uint256 memPtr = item.memPtr + 1;
    assembly {
      result := mload(memPtr)
    }

    return result;
  }

  function toBytes(RLPItem memory item) internal pure returns (bytes memory) {
    require(item.len > 0);

    uint256 offset = _payloadOffset(item.memPtr);
    uint256 len = item.len - offset; // data length
    bytes memory result = new bytes(len);

    uint256 destPtr;
    assembly {
      destPtr := add(0x20, result)
    }

    copy(item.memPtr + offset, destPtr, len);
    return result;
  }

  /*
   * Private Helpers
   */

  // @return number of payload items inside an encoded list.
  function numItems(RLPItem memory item) private pure returns (uint256) {
    if (item.len == 0) return 0;

    uint256 count = 0;
    uint256 currPtr = item.memPtr + _payloadOffset(item.memPtr);
    uint256 endPtr = item.memPtr + item.len;
    while (currPtr < endPtr) {
      currPtr = currPtr + _itemLength(currPtr); // skip over an item
      count++;
    }

    return count;
  }

  // @return entire rlp item byte length
  function _itemLength(uint256 memPtr) private pure returns (uint256) {
    uint256 itemLen;
    uint256 byte0;
    assembly {
      byte0 := byte(0, mload(memPtr))
    }

    if (byte0 < STRING_SHORT_START) itemLen = 1;
    else if (byte0 < STRING_LONG_START) itemLen = byte0 - STRING_SHORT_START + 1;
    else if (byte0 < LIST_SHORT_START) {
      assembly {
        let byteLen := sub(byte0, 0xb7) // # of bytes the actual length is
        memPtr := add(memPtr, 1) // skip over the first byte
        /* 32 byte word size */
        let dataLen := div(mload(memPtr), exp(256, sub(32, byteLen))) // right shifting to get the len
        itemLen := add(dataLen, add(byteLen, 1))
      }
    } else if (byte0 < LIST_LONG_START) {
      itemLen = byte0 - LIST_SHORT_START + 1;
    } else {
      assembly {
        let byteLen := sub(byte0, 0xf7)
        memPtr := add(memPtr, 1)

        let dataLen := div(mload(memPtr), exp(256, sub(32, byteLen))) // right shifting to the correct length
        itemLen := add(dataLen, add(byteLen, 1))
      }
    }

    return itemLen;
  }

  // @return number of bytes until the data
  function _payloadOffset(uint256 memPtr) private pure returns (uint256) {
    uint256 byte0;
    assembly {
      byte0 := byte(0, mload(memPtr))
    }

    if (byte0 < STRING_SHORT_START) return 0;
    else if (byte0 < STRING_LONG_START || (byte0 > LIST_SHORT_START - 1 && byte0 < LIST_LONG_START)) return 1;
    else if (byte0 < LIST_SHORT_START)
      // being explicit
      return byte0 - (STRING_LONG_START - 1) + 1;
    else return byte0 - (LIST_LONG_START - 1) + 1;
  }

  /*
   * @param src Pointer to source
   * @param dest Pointer to destination
   * @param len Amount of memory to copy from the source
   */
  function copy(
    uint256 src,
    uint256 dest,
    uint256 len
  ) private pure {
    if (len == 0) return;

    // copy as many word sizes as possible
    for (; len > WORD_SIZE - 1; len -= WORD_SIZE) {
      assembly {
        mstore(dest, mload(src))
      }

      src += WORD_SIZE;
      dest += WORD_SIZE;
    }

    if (len == 0) return;

    // left over bytes. Mask is used to remove unwanted bytes from the word
    uint256 mask = 256**(WORD_SIZE - len) - 1;

    assembly {
      let srcpart := and(mload(src), not(mask)) // zero out src
      let destpart := and(mload(dest), mask) // retrieve the bytes
      mstore(dest, or(destpart, srcpart))
    }
  }
}

File 8 of 12 : FxBaseRootTunnel.sol
// SPDX-License-Identifier: MIT
// https://github.com/fx-portal/contracts/blob/main/contracts/tunnel/FxBaseRootTunnel.sol
pragma solidity 0.8.17;

import {RLPReader} from "../lib/RLPReader.sol";
import {MerklePatriciaProof} from "../lib/MerklePatriciaProof.sol";
import {Merkle} from "../lib/Merkle.sol";
import "../lib/ExitPayloadReader.sol";

interface IFxStateSender {
  function sendMessageToChild(address _receiver, bytes calldata _data) external;
}

contract ICheckpointManager {
  struct HeaderBlock {
    bytes32 root;
    uint256 start;
    uint256 end;
    uint256 createdAt;
    address proposer;
  }

  /**
   * @notice mapping of checkpoint header numbers to block details
   * @dev These checkpoints are submited by plasma contracts
   */
  mapping(uint256 => HeaderBlock) public headerBlocks;
}

abstract contract FxBaseRootTunnel {
  using RLPReader for RLPReader.RLPItem;
  using Merkle for bytes32;
  using ExitPayloadReader for bytes;
  using ExitPayloadReader for ExitPayloadReader.ExitPayload;
  using ExitPayloadReader for ExitPayloadReader.Log;
  using ExitPayloadReader for ExitPayloadReader.LogTopics;
  using ExitPayloadReader for ExitPayloadReader.Receipt;

  // keccak256(MessageSent(bytes))
  bytes32 public constant SEND_MESSAGE_EVENT_SIG = 0x8c5261668696ce22758910d05bab8f186d6eb247ceac2af2e82c7dc17669b036;

  // state sender contract
  IFxStateSender public fxRoot;
  // root chain manager
  ICheckpointManager public checkpointManager;
  // child tunnel contract which receives and sends messages
  address public fxChildTunnel;

  // storage to avoid duplicate exits
  mapping(bytes32 => bool) public processedExits;

  constructor(address _checkpointManager, address _fxRoot) {
    checkpointManager = ICheckpointManager(_checkpointManager);
    fxRoot = IFxStateSender(_fxRoot);
  }

  // set fxChildTunnel if not set already
  function setFxChildTunnel(address _fxChildTunnel) internal virtual {
    require(fxChildTunnel == address(0x0), "FxBaseRootTunnel: CHILD_TUNNEL_ALREADY_SET");
    fxChildTunnel = _fxChildTunnel;
  }

  /**
   * @notice Send bytes message to Child Tunnel
   * @param message bytes message that will be sent to Child Tunnel
   * some message examples -
   *   abi.encode(tokenId);
   *   abi.encode(tokenId, tokenMetadata);
   *   abi.encode(messageType, messageData);
   */
  function _sendMessageToChild(bytes memory message) internal {
    fxRoot.sendMessageToChild(fxChildTunnel, message);
  }

  function _validateAndExtractMessage(bytes memory inputData) internal returns (bytes memory) {
    ExitPayloadReader.ExitPayload memory payload = inputData.toExitPayload();

    bytes memory branchMaskBytes = payload.getBranchMaskAsBytes();
    uint256 blockNumber = payload.getBlockNumber();
    // checking if exit has already been processed
    // unique exit is identified using hash of (blockNumber, branchMask, receiptLogIndex)
    bytes32 exitHash = keccak256(
      abi.encodePacked(
        blockNumber,
        // first 2 nibbles are dropped while generating nibble array
        // this allows branch masks that are valid but bypass exitHash check (changing first 2 nibbles only)
        // so converting to nibble array and then hashing it
        MerklePatriciaProof._getNibbleArray(branchMaskBytes),
        payload.getReceiptLogIndex()
      )
    );
    require(processedExits[exitHash] == false, "FxRootTunnel: EXIT_ALREADY_PROCESSED");
    processedExits[exitHash] = true;

    ExitPayloadReader.Receipt memory receipt = payload.getReceipt();
    ExitPayloadReader.Log memory log = receipt.getLog();

    // check child tunnel
    require(fxChildTunnel == log.getEmitter(), "FxRootTunnel: INVALID_FX_CHILD_TUNNEL");

    bytes32 receiptRoot = payload.getReceiptRoot();
    // verify receipt inclusion
    require(
      MerklePatriciaProof.verify(receipt.toBytes(), branchMaskBytes, payload.getReceiptProof(), receiptRoot),
      "FxRootTunnel: INVALID_RECEIPT_PROOF"
    );

    // verify checkpoint inclusion
    _checkBlockMembershipInCheckpoint(
      blockNumber,
      payload.getBlockTime(),
      payload.getTxRoot(),
      receiptRoot,
      payload.getHeaderNumber(),
      payload.getBlockProof()
    );

    ExitPayloadReader.LogTopics memory topics = log.getTopics();

    require(
      bytes32(topics.getField(0).toUint()) == SEND_MESSAGE_EVENT_SIG, // topic0 is event sig
      "FxRootTunnel: INVALID_SIGNATURE"
    );

    // received message data
    bytes memory message = abi.decode(log.getData(), (bytes)); // event decodes params again, so decoding bytes to get message
    return message;
  }

  function _checkBlockMembershipInCheckpoint(
    uint256 blockNumber,
    uint256 blockTime,
    bytes32 txRoot,
    bytes32 receiptRoot,
    uint256 headerNumber,
    bytes memory blockProof
  ) private view returns (uint256) {
    (bytes32 headerRoot, uint256 startBlock, , uint256 createdAt, ) = checkpointManager.headerBlocks(headerNumber);

    require(
      keccak256(abi.encodePacked(blockNumber, blockTime, txRoot, receiptRoot)).checkMembership(
        blockNumber - startBlock,
        headerRoot,
        blockProof
      ),
      "FxRootTunnel: INVALID_HEADER"
    );
    return createdAt;
  }

  /**
   * @notice receive message from  L2 to L1, validated by proof
   * @dev This function verifies if the transaction actually happened on child chain
   *
   * @param inputData RLP encoded data of the reference tx containing following list of fields
   *  0 - headerNumber - Checkpoint header block number containing the reference tx
   *  1 - blockProof - Proof that the block header (in the child chain) is a leaf in the submitted merkle root
   *  2 - blockNumber - Block number containing the reference tx on child chain
   *  3 - blockTime - Reference tx block time
   *  4 - txRoot - Transactions root of block
   *  5 - receiptRoot - Receipts root of block
   *  6 - receipt - Receipt of the reference transaction
   *  7 - receiptProof - Merkle proof of the reference receipt
   *  8 - branchMask - 32 bits denoting the path of receipt in merkle tree
   *  9 - receiptLogIndex - Log Index to read from the receipt
   */
  function receiveMessage(bytes memory inputData) public virtual {
    bytes memory message = _validateAndExtractMessage(inputData);
    _processMessageFromChild(message);
  }

  /**
   * @notice Process message received from Child Tunnel
   * @dev function needs to be implemented to handle message as per requirement
   * This is called by onStateReceive function.
   * Since it is called via a system call, any event will not be emitted during its execution.
   * @param message bytes message that was sent from Child Tunnel
   */
  function _processMessageFromChild(bytes memory message) internal virtual;
}

File 9 of 12 : IConnector.sol
// SPDX-License-Identifier: MIT OR Apache-2.0
pragma solidity 0.8.17;

import {IProposedOwnable} from "../../shared/interfaces/IProposedOwnable.sol";

/**
 * @notice This interface is what the Connext contract will send and receive messages through.
 * The messaging layer should conform to this interface, and should be interchangeable (i.e.
 * could be Nomad or a generic AMB under the hood).
 *
 * @dev This uses the nomad format to ensure nomad can be added in as it comes back online.
 *
 * Flow from transfer from polygon to optimism:
 * 1. User calls `xcall` with destination specified
 * 2. This will swap in to the bridge assets
 * 3. The swapped assets will get burned
 * 4. The Connext contract will call `dispatch` on the messaging contract to add the transfer
 *    to the root
 * 5. [At some time interval] Relayers call `send` to send the current root from polygon to
 *    mainnet. This is done on all "spoke" domains.
 * 6. [At some time interval] Relayers call `propagate` [better name] on mainnet, this generates a new merkle
 *    root from all of the AMBs
 *    - This function must be able to read root data from all AMBs and aggregate them into a single merkle
 *      tree root
 *    - Will send the mixed root from all chains back through the respective AMBs to all other chains
 * 7. AMB will call `update` to update the latest root on the messaging contract on spoke domains
 * 8. [At any point] Relayers can call `proveAndProcess` to prove inclusion of dispatched message, and call
 *    process on the `Connext` contract
 * 9. Takes minted bridge tokens and credits the LP
 *
 * AMB requirements:
 * - Access `msg.sender` both from mainnet -> spoke and vice versa
 * - Ability to read *our root* from the AMB
 *
 * AMBs:
 * - PoS bridge from polygon
 * - arbitrum bridge
 * - optimism bridge
 * - gnosis chain
 * - bsc (use multichain for messaging)
 */
interface IConnector is IProposedOwnable {
  // ============ Events ============
  /**
   * @notice Emitted whenever a message is successfully sent over an AMB
   * @param data The contents of the message
   * @param encodedData Data used to send the message; specific to connector
   * @param caller Who called the function (sent the message)
   */
  event MessageSent(bytes data, bytes encodedData, address caller);

  /**
   * @notice Emitted whenever a message is successfully received over an AMB
   * @param data The contents of the message
   * @param caller Who called the function
   */
  event MessageProcessed(bytes data, address caller);

  // ============ Public fns ============

  function processMessage(bytes memory _data) external;

  function verifySender(address _expected) external returns (bool);
}

File 10 of 12 : IRootManager.sol
// SPDX-License-Identifier: MIT OR Apache-2.0
pragma solidity 0.8.17;

interface IRootManager {
  /**
   * @notice This is called by relayers to generate + send the mixed root from mainnet via AMB to
   * spoke domains.
   * @dev This must read information for the root from the registered AMBs.
   */
  function propagate(
    address[] calldata _connectors,
    uint256[] calldata _fees,
    bytes[] memory _encodedData
  ) external payable;

  /**
   * @notice Called by the connectors for various domains on the hub to aggregate their latest
   * inbound root.
   * @dev This must read information for the root from the registered AMBs
   */
  function aggregate(uint32 _domain, bytes32 _outbound) external;
}

File 11 of 12 : ProposedOwnable.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.17;

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

/**
 * @title ProposedOwnable
 * @notice Contract module which provides a basic access control mechanism,
 * where there is an account (an owner) that can be granted exclusive access to
 * specific functions.
 *
 * By default, the owner account will be the one that deploys the contract. This
 * can later be changed via a two step process:
 * 1. Call `proposeOwner`
 * 2. Wait out the delay period
 * 3. Call `acceptOwner`
 *
 * @dev This module is used through inheritance. It will make available the
 * modifier `onlyOwner`, which can be applied to your functions to restrict
 * their use to the owner.
 *
 * @dev The majority of this code was taken from the openzeppelin Ownable
 * contract
 *
 */
abstract contract ProposedOwnable is IProposedOwnable {
  // ========== Custom Errors ===========

  error ProposedOwnable__onlyOwner_notOwner();
  error ProposedOwnable__onlyProposed_notProposedOwner();
  error ProposedOwnable__ownershipDelayElapsed_delayNotElapsed();
  error ProposedOwnable__proposeNewOwner_invalidProposal();
  error ProposedOwnable__proposeNewOwner_noOwnershipChange();
  error ProposedOwnable__renounceOwnership_noProposal();
  error ProposedOwnable__renounceOwnership_invalidProposal();

  // ============ Properties ============

  address private _owner;

  address private _proposed;
  uint256 private _proposedOwnershipTimestamp;

  uint256 private constant _delay = 7 days;

  // ======== Getters =========

  /**
   * @notice Returns the address of the current owner.
   */
  function owner() public view virtual returns (address) {
    return _owner;
  }

  /**
   * @notice Returns the address of the proposed owner.
   */
  function proposed() public view virtual returns (address) {
    return _proposed;
  }

  /**
   * @notice Returns the address of the proposed owner.
   */
  function proposedTimestamp() public view virtual returns (uint256) {
    return _proposedOwnershipTimestamp;
  }

  /**
   * @notice Returns the delay period before a new owner can be accepted.
   */
  function delay() public view virtual returns (uint256) {
    return _delay;
  }

  /**
   * @notice Throws if called by any account other than the owner.
   */
  modifier onlyOwner() {
    if (_owner != msg.sender) revert ProposedOwnable__onlyOwner_notOwner();
    _;
  }

  /**
   * @notice Throws if called by any account other than the proposed owner.
   */
  modifier onlyProposed() {
    if (_proposed != msg.sender) revert ProposedOwnable__onlyProposed_notProposedOwner();
    _;
  }

  /**
   * @notice Throws if the ownership delay has not elapsed
   */
  modifier ownershipDelayElapsed() {
    // Ensure delay has elapsed
    if ((block.timestamp - _proposedOwnershipTimestamp) <= _delay)
      revert ProposedOwnable__ownershipDelayElapsed_delayNotElapsed();
    _;
  }

  /**
   * @notice Indicates if the ownership has been renounced() by
   * checking if current owner is address(0)
   */
  function renounced() public view returns (bool) {
    return _owner == address(0);
  }

  // ======== External =========

  /**
   * @notice Sets the timestamp for an owner to be proposed, and sets the
   * newly proposed owner as step 1 in a 2-step process
   */
  function proposeNewOwner(address newlyProposed) public virtual onlyOwner {
    // Contract as source of truth
    if (_proposed == newlyProposed && _proposedOwnershipTimestamp != 0)
      revert ProposedOwnable__proposeNewOwner_invalidProposal();

    // Sanity check: reasonable proposal
    if (_owner == newlyProposed) revert ProposedOwnable__proposeNewOwner_noOwnershipChange();

    _setProposed(newlyProposed);
  }

  /**
   * @notice Renounces ownership of the contract after a delay
   */
  function renounceOwnership() public virtual onlyOwner ownershipDelayElapsed {
    // Ensure there has been a proposal cycle started
    if (_proposedOwnershipTimestamp == 0) revert ProposedOwnable__renounceOwnership_noProposal();

    // Require proposed is set to 0
    if (_proposed != address(0)) revert ProposedOwnable__renounceOwnership_invalidProposal();

    // Emit event, set new owner, reset timestamp
    _setOwner(address(0));
  }

  /**
   * @notice Transfers ownership of the contract to a new account (`newOwner`).
   * Can only be called by the current owner.
   */
  function acceptProposedOwner() public virtual onlyProposed ownershipDelayElapsed {
    // NOTE: no need to check if _owner == _proposed, because the _proposed
    // is 0-d out and this check is implicitly enforced by modifier

    // NOTE: no need to check if _proposedOwnershipTimestamp > 0 because
    // the only time this would happen is if the _proposed was never
    // set (will fail from modifier) or if the owner == _proposed (checked
    // above)

    // Emit event, set new owner, reset timestamp
    _setOwner(_proposed);
  }

  // ======== Internal =========

  function _setOwner(address newOwner) internal {
    emit OwnershipTransferred(_owner, newOwner);
    _owner = newOwner;
    delete _proposedOwnershipTimestamp;
    delete _proposed;
  }

  function _setProposed(address newlyProposed) private {
    _proposedOwnershipTimestamp = block.timestamp;
    _proposed = newlyProposed;
    emit OwnershipProposed(newlyProposed);
  }
}

File 12 of 12 : IProposedOwnable.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.17;

/**
 * @title IProposedOwnable
 * @notice Defines a minimal interface for ownership with a two step proposal and acceptance
 * process
 */
interface IProposedOwnable {
  /**
   * @dev This emits when change in ownership of a contract is proposed.
   */
  event OwnershipProposed(address indexed proposedOwner);

  /**
   * @dev This emits when ownership of a contract changes.
   */
  event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);

  /**
   * @notice Get the address of the owner
   * @return owner_ The address of the owner.
   */
  function owner() external view returns (address owner_);

  /**
   * @notice Get the address of the proposed owner
   * @return proposed_ The address of the proposed.
   */
  function proposed() external view returns (address proposed_);

  /**
   * @notice Set the address of the proposed owner of the contract
   * @param newlyProposed The proposed new owner of the contract
   */
  function proposeNewOwner(address newlyProposed) external;

  /**
   * @notice Set the address of the proposed owner of the contract
   */
  function acceptProposedOwner() external;
}

Settings
{
  "evmVersion": "london",
  "libraries": {},
  "metadata": {
    "bytecodeHash": "ipfs",
    "useLiteralContent": true
  },
  "optimizer": {
    "enabled": true,
    "runs": 200
  },
  "remappings": [],
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  }
}

Contract Security Audit

Contract ABI

[{"inputs":[{"internalType":"uint32","name":"_domain","type":"uint32"},{"internalType":"uint32","name":"_mirrorDomain","type":"uint32"},{"internalType":"address","name":"_amb","type":"address"},{"internalType":"address","name":"_rootManager","type":"address"},{"internalType":"address","name":"_mirrorConnector","type":"address"},{"internalType":"address","name":"_checkPointManager","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"Connector__processMessage_notUsed","type":"error"},{"inputs":[],"name":"ProposedOwnable__onlyOwner_notOwner","type":"error"},{"inputs":[],"name":"ProposedOwnable__onlyProposed_notProposedOwner","type":"error"},{"inputs":[],"name":"ProposedOwnable__ownershipDelayElapsed_delayNotElapsed","type":"error"},{"inputs":[],"name":"ProposedOwnable__proposeNewOwner_invalidProposal","type":"error"},{"inputs":[],"name":"ProposedOwnable__proposeNewOwner_noOwnershipChange","type":"error"},{"inputs":[],"name":"ProposedOwnable__renounceOwnership_invalidProposal","type":"error"},{"inputs":[],"name":"ProposedOwnable__renounceOwnership_noProposal","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bytes","name":"data","type":"bytes"},{"indexed":false,"internalType":"address","name":"caller","type":"address"}],"name":"MessageProcessed","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bytes","name":"data","type":"bytes"},{"indexed":false,"internalType":"bytes","name":"encodedData","type":"bytes"},{"indexed":false,"internalType":"address","name":"caller","type":"address"}],"name":"MessageSent","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"previous","type":"address"},{"indexed":false,"internalType":"address","name":"current","type":"address"}],"name":"MirrorConnectorUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint32","name":"domain","type":"uint32"},{"indexed":true,"internalType":"uint32","name":"mirrorDomain","type":"uint32"},{"indexed":false,"internalType":"address","name":"amb","type":"address"},{"indexed":false,"internalType":"address","name":"rootManager","type":"address"},{"indexed":false,"internalType":"address","name":"mirrorConnector","type":"address"}],"name":"NewConnector","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"proposedOwner","type":"address"}],"name":"OwnershipProposed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"inputs":[],"name":"AMB","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"DOMAIN","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MIRROR_DOMAIN","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"ROOT_MANAGER","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"SEND_MESSAGE_EVENT_SIG","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"acceptProposedOwner","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"checkpointManager","outputs":[{"internalType":"contract ICheckpointManager","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"delay","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"fxChildTunnel","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"fxRoot","outputs":[{"internalType":"contract IFxStateSender","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"mirrorConnector","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes","name":"_data","type":"bytes"}],"name":"processMessage","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"processedExits","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newlyProposed","type":"address"}],"name":"proposeNewOwner","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"proposed","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"proposedTimestamp","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes","name":"inputData","type":"bytes"}],"name":"receiveMessage","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"renounced","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes","name":"_data","type":"bytes"},{"internalType":"bytes","name":"_encodedData","type":"bytes"}],"name":"sendMessage","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"_mirrorConnector","type":"address"}],"name":"setMirrorConnector","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_expected","type":"address"}],"name":"verifySender","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"stateMutability":"payable","type":"receive"}]

Deployed Bytecode

0x6080604052600436106101445760003560e01c80638da5cb5b116100b6578063d1851c921161006f578063d1851c92146103e8578063d232c22014610406578063d69f9d6114610425578063db1b765914610459578063de9b771f14610479578063f953cec71461049957600080fd5b80638da5cb5b14610335578063972c492814610353578063b1f8100d14610373578063c0857ba014610393578063c5b350df146103b3578063cc394283146103c857600080fd5b806352a9674b1161010857806352a9674b1461022a5780635bd11efc1461025e5780635f61e3ec1461027e578063607f2d42146102ca5780636a42b8f81461030a578063715018a61461032057600080fd5b80630e387de61461015057806314168416146101975780633cf52ffb146101e057806348e6fa23146101f55780634ff746f61461020a57600080fd5b3661014b57005b600080fd5b34801561015c57600080fd5b506101847f8c5261668696ce22758910d05bab8f186d6eb247ceac2af2e82c7dc17669b03681565b6040519081526020015b60405180910390f35b3480156101a357600080fd5b506101cb7f00000000000000000000000000000000000000000000000000000000706f6c7981565b60405163ffffffff909116815260200161018e565b3480156101ec57600080fd5b50600254610184565b6102086102033660046121de565b6104b9565b005b34801561021657600080fd5b50610208610225366004612242565b61056e565b34801561023657600080fd5b506101cb7f000000000000000000000000000000000000000000000000000000000065746881565b34801561026a57600080fd5b5061020861027936600461228c565b610614565b34801561028a57600080fd5b506102b27f000000000000000000000000d5d61e9dfb6680cba8353988ba0337802811c2e181565b6040516001600160a01b03909116815260200161018e565b3480156102d657600080fd5b506102fa6102e53660046122a9565b60076020526000908152604090205460ff1681565b604051901515815260200161018e565b34801561031657600080fd5b5062093a80610184565b34801561032c57600080fd5b5061020861064b565b34801561034157600080fd5b506000546001600160a01b03166102b2565b34801561035f57600080fd5b506006546102b2906001600160a01b031681565b34801561037f57600080fd5b5061020861038e36600461228c565b6106ff565b34801561039f57600080fd5b506005546102b2906001600160a01b031681565b3480156103bf57600080fd5b5061020861079d565b3480156103d457600080fd5b506003546102b2906001600160a01b031681565b3480156103f457600080fd5b506001546001600160a01b03166102b2565b34801561041257600080fd5b506000546001600160a01b0316156102fa565b34801561043157600080fd5b506102b27f000000000000000000000000fe5e5d361b2ad62c541bab87c45a0b9b018389a281565b34801561046557600080fd5b506102fa61047436600461228c565b61080d565b34801561048557600080fd5b506004546102b2906001600160a01b031681565b3480156104a557600080fd5b506102086104b4366004612242565b610817565b336001600160a01b037f000000000000000000000000d5d61e9dfb6680cba8353988ba0337802811c2e116146105255760405162461bcd60e51b815260206004820152600c60248201526b10b937b7ba26b0b730b3b2b960a11b60448201526064015b60405180910390fd5b61052f828261089a565b7fdcaa37a042a0087de79018c629bbd29cee82ca80bd9be394e1696bf9e935507782823360405161056293929190612312565b60405180910390a15050565b336001600160a01b037f000000000000000000000000fe5e5d361b2ad62c541bab87c45a0b9b018389a216146105cf5760405162461bcd60e51b815260040161051c9060208082526004908201526310a0a6a160e11b604082015260600190565b6105d8816108e1565b7fb3abc57bfeebd2cac918901db582f71972a8e628bccf19f5ae3e3482b98a5ced8133604051610609929190612350565b60405180910390a150565b6000546001600160a01b0316331461063f576040516311a8a1bb60e31b815260040160405180910390fd5b610648816108fa565b50565b6000546001600160a01b03163314610676576040516311a8a1bb60e31b815260040160405180910390fd5b62093a80600254426106889190612390565b116106a6576040516324e0285f60e21b815260040160405180910390fd5b6002546000036106c957604051630e4b303f60e21b815260040160405180910390fd5b6001546001600160a01b0316156106f3576040516323295ef960e01b815260040160405180910390fd5b6106fd600061090c565b565b6000546001600160a01b0316331461072a576040516311a8a1bb60e31b815260040160405180910390fd5b6001546001600160a01b038281169116148015610748575060025415155b15610766576040516311bc066560e11b815260040160405180910390fd5b6000546001600160a01b0380831691160361079457604051634a2fb73f60e11b815260040160405180910390fd5b61064881610971565b6001546001600160a01b031633146107c8576040516311a7f27160e11b815260040160405180910390fd5b62093a80600254426107da9190612390565b116107f8576040516324e0285f60e21b815260040160405180910390fd5b6001546106fd906001600160a01b031661090c565b6000805b92915050565b6000610822826109bf565b905061082d81610ca5565b5050565b600354604080516001600160a01b03928316815291831660208301527fc77bec288fc88f168427f2f7da682eadb26cac89d8d591af6e443da98dff2bbc910160405180910390a1600380546001600160a01b0319166001600160a01b0392909216919091179055565b8051156108d85760405162461bcd60e51b815260206004820152600c60248201526b042c8c2e8c240d8cadccee8d60a31b604482015260640161051c565b61082d82610dc6565b6040516316c2fdb560e21b815260040160405180910390fd5b61090381610831565b61064881610e2f565b600080546040516001600160a01b03808516939216917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a3600080546001600160a01b039092166001600160a01b0319928316178155600255600180549091169055565b42600255600180546001600160a01b0319166001600160a01b0383169081179091556040517f6ab4d119f23076e8ad491bc65ce85f017fb0591dce08755ba8591059cc51737a90600090a250565b606060006109cc83610ebd565b905060006109d982610f1c565b905060006109e683610f45565b90506000816109f484610f6e565b6109fd86611124565b604051602001610a0f939291906123a3565b60408051601f1981840301815291815281516020928301206000818152600790935291205490915060ff1615610a935760405162461bcd60e51b8152602060048201526024808201527f4678526f6f7454756e6e656c3a20455849545f414c52454144595f50524f434560448201526314d4d15160e21b606482015260840161051c565b6000818152600760205260408120805460ff19166001179055610ab585611140565b90506000610ac28261128a565b9050610acd8161131a565b6006546001600160a01b03908116911614610b385760405162461bcd60e51b815260206004820152602560248201527f4678526f6f7454756e6e656c3a20494e56414c49445f46585f4348494c445f54604482015264155393915360da1b606482015260840161051c565b6000610b4387611343565b9050610b63610b53846020015190565b87610b5d8a61135f565b8461137b565b610bbb5760405162461bcd60e51b815260206004820152602360248201527f4678526f6f7454756e6e656c3a20494e56414c49445f524543454950545f505260448201526227a7a360e91b606482015260840161051c565b610be985610bc88961162e565b610bd18a61164a565b84610bdb8c611666565b610be48d611682565b61169e565b506000610bf5836117c4565b90507f8c5261668696ce22758910d05bab8f186d6eb247ceac2af2e82c7dc17669b036610c2b610c26836000611800565b611838565b14610c785760405162461bcd60e51b815260206004820152601f60248201527f4678526f6f7454756e6e656c3a20494e56414c49445f5349474e415455524500604482015260640161051c565b6000610c83846118b2565b806020019051810190610c9691906123d0565b9b9a5050505050505050505050565b8051602014610ce05760405162461bcd60e51b8152602060048201526007602482015266042d8cadccee8d60cb1b604482015260640161051c565b6001600160a01b037f000000000000000000000000d5d61e9dfb6680cba8353988ba0337802811c2e116638e7d93fa7f00000000000000000000000000000000000000000000000000000000706f6c79610d3984612447565b6040516001600160e01b031960e085901b16815263ffffffff9290921660048301526024820152604401600060405180830381600087803b158015610d7d57600080fd5b505af1158015610d91573d6000803e3d6000fd5b505050507fb3abc57bfeebd2cac918901db582f71972a8e628bccf19f5ae3e3482b98a5ced8133604051610609929190612350565b6004805460065460405163b472047760e01b81526001600160a01b039283169363b472047793610dfa93169186910161246b565b600060405180830381600087803b158015610e1457600080fd5b505af1158015610e28573d6000803e3d6000fd5b5050505050565b6006546001600160a01b031615610e9b5760405162461bcd60e51b815260206004820152602a60248201527f467842617365526f6f7454756e6e656c3a204348494c445f54554e4e454c5f4160448201526913149150511657d4d15560b21b606482015260840161051c565b600680546001600160a01b0319166001600160a01b0392909216919091179055565b6040805160208101909152606081526000610f07610f028460408051808201825260008082526020918201528151808301909252825182529182019181019190915290565b6118ce565b60408051602081019091529081529392505050565b60606108118260000151600881518110610f3857610f3861248f565b60200260200101516119da565b60006108118260000151600281518110610f6157610f6161248f565b6020026020010151611838565b6040805160208101909152600081528151606091901561081157600080610f96600086611a77565b60f81c90506001811480610fad57508060ff166003145b1561105557600185516002610fc291906124a5565b610fcc9190612390565b67ffffffffffffffff811115610fe457610fe4612119565b6040519080825280601f01601f19166020018201604052801561100e576020820181803683370190505b509250600061101e600187611a77565b905080846000815181106110345761103461248f565b60200101906001600160f81b031916908160001a90535060019250506110b9565b60028551600261106591906124a5565b61106f9190612390565b67ffffffffffffffff81111561108757611087612119565b6040519080825280601f01601f1916602001820160405280156110b1576020820181803683370190505b509250600091505b825160ff83165b8181101561111a576110e96110d860ff861683612390565b6110e39060026124bc565b88611a77565b8582815181106110fb576110fb61248f565b60200101906001600160f81b031916908160001a9053506001016110c0565b5050505092915050565b60006108118260000151600981518110610f6157610f6161248f565b61116460405180606001604052806060815260200160608152602001600081525090565b61117e8260000151600681518110610f3857610f3861248f565b6020828101829052604080518082018252600080825290830152805180820190915282518152918101908201526111b481611af8565b156111c9576111c2816118ce565b8252611276565b602082015180516000906111df90600190612390565b67ffffffffffffffff8111156111f7576111f7612119565b6040519080825280601f01601f191660200182016040528015611221576020820181803683370190505b50905060008083602101915082602001905061123f82828551611b33565b60408051808201825260008082526020918201528151808301909252845182528085019082015261126f906118ce565b8652505050505b61127f83611124565b604083015250919050565b6040805160808101825260009181018281526060808301939093528152602081019190915260006112d883600001516003815181106112cb576112cb61248f565b60200260200101516118ce565b8360400151815181106112ed576112ed61248f565b602002602001015190506040518060400160405280828152602001611311836118ce565b90529392505050565b600061081182602001516000815181106113365761133661248f565b6020026020010151611bcc565b60006108118260000151600581518110610f6157610f6161248f565b60606108118260000151600781518110610f3857610f3861248f565b6000806113af8460408051808201825260008082526020918201528151808301909252825182529182019181019190915290565b905060006113bc826118ce565b9050606080856000806113ce8b610f6e565b905080516000036113e9576000975050505050505050611626565b855160005b8181101561161c5782518411156114115760009950505050505050505050611626565b6114338882815181106114265761142661248f565b6020026020010151611be6565b9650868051906020012085146114555760009950505050505050505050611626565b61146a8882815181106112cb576112cb61248f565b9550855160110361153f57825184036114cb578d8051906020012061149b87601081518110610f3857610f3861248f565b80519060200120036114b95760019950505050505050505050611626565b60009950505050505050505050611626565b60008385815181106114df576114df61248f565b016020015160f81c905060108111156115055760009a5050505050505050505050611626565b61152a878260ff168151811061151d5761151d61248f565b6020026020010151611c65565b95506115376001866124bc565b945050611614565b85516002036114b957600061156a61156388600081518110610f3857610f3861248f565b8587611c93565b845190915061157982876124bc565b036115ce578e8051906020012061159c88600181518110610f3857610f3861248f565b80519060200120036115bb5760019a5050505050505050505050611626565b60009a5050505050505050505050611626565b806000036115e95760009a5050505050505050505050611626565b6115f381866124bc565b945061160b8760018151811061151d5761151d61248f565b95506116149050565b6001016113ee565b5050505050505050505b949350505050565b60006108118260000151600381518110610f6157610f6161248f565b60006108118260000151600481518110610f6157610f6161248f565b60006108118260000151600081518110610f6157610f6161248f565b60606108118260000151600181518110610f3857610f3861248f565b6005546040516320a9cea560e11b8152600481018490526000918291829182916001600160a01b03909116906341539d4a9060240160a060405180830381865afa1580156116f0573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061171491906124cf565b509350509250925061176b828b61172b9190612390565b6040805160208082018f90528183018e9052606082018d905260808083018d90528351808403909101815260a09092019092528051910120908588611d97565b6117b75760405162461bcd60e51b815260206004820152601c60248201527f4678526f6f7454756e6e656c3a20494e56414c49445f48454144455200000000604482015260640161051c565b9998505050505050505050565b60408051602081019091526060815260405180602001604052806117f884602001516001815181106112cb576112cb61248f565b905292915050565b604080518082019091526000808252602082015282518051839081106118285761182861248f565b6020026020010151905092915050565b80516000901580159061184c575081516022115b61185557600080fd5b60006118648360200151611f09565b905060008184600001516118789190612390565b905060008083866020015161188d91906124bc565b90508051915060208310156118a957826020036101000a820491505b50949350505050565b60606108118260200151600281518110610f3857610f3861248f565b60606118d982611af8565b6118e257600080fd5b60006118ed83611f9d565b905060008167ffffffffffffffff81111561190a5761190a612119565b60405190808252806020026020018201604052801561194f57816020015b60408051808201909152600080825260208201528152602001906001900390816119285790505b50905060006119618560200151611f09565b856020015161197091906124bc565b90506000805b848110156119cf5761198783612022565b91506040518060400160405280838152602001848152508482815181106119b0576119b061248f565b60209081029190910101526119c582846124bc565b9250600101611976565b509195945050505050565b80516060906119e857600080fd5b60006119f78360200151611f09565b90506000818460000151611a0b9190612390565b905060008167ffffffffffffffff811115611a2857611a28612119565b6040519080825280601f01601f191660200182016040528015611a52576020820181803683370190505b50905060008160200190506118a9848760200151611a7091906124bc565b82856120c6565b6000611a84600284612532565b15611abe57601082611a97600286612546565b81518110611aa757611aa761248f565b0160200151611ab9919060f81c61255a565b611aee565b601082611acc600286612546565b81518110611adc57611adc61248f565b0160200151611aee919060f81c61257c565b60f81b9392505050565b80516000908103611b0b57506000919050565b6020820151805160001a9060c0821015611b29575060009392505050565b5060019392505050565b80600003611b4057505050565b611b4c6001602061259e565b60ff16811115611b865782518252611b656020846124bc565b9250611b726020836124bc565b9150611b7f602082612390565b9050611b40565b80600003611b9357505050565b60006001611ba2836020612390565b611bae9061010061269b565b611bb89190612390565b935183518516941916939093179091525050565b8051600090601514611bdd57600080fd5b61081182611838565b60606000826000015167ffffffffffffffff811115611c0757611c07612119565b6040519080825280601f01601f191660200182016040528015611c31576020820181803683370190505b5090508051600003611c435792915050565b6000816020019050611c5e84602001518286600001516120c6565b5092915050565b8051600090602114611c7657600080fd5b60008083602001516001611c8a91906124bc565b51949350505050565b60008080611ca086610f6e565b90506000815167ffffffffffffffff811115611cbe57611cbe612119565b6040519080825280601f01601f191660200182016040528015611ce8576020820181803683370190505b5090506000825186611cfa91906124bc565b9050855b81811015611d67576000888281518110611d1a57611d1a61248f565b01602001516001600160f81b03191690508084611d378a85612390565b81518110611d4757611d4761248f565b60200101906001600160f81b031916908160001a90535050600101611cfe565b508180519060200120838051906020012003611d865782519350611d8b565b600093505b50919695505050505050565b600060208251611da79190612532565b15611deb5760405162461bcd60e51b8152602060048201526014602482015273092dcecc2d8d2c840e0e4dedecc40d8cadccee8d60631b604482015260640161051c565b600060208351611dfb9190612546565b9050611e0881600261269b565b8510611e4e5760405162461bcd60e51b81526020600482015260156024820152744c65616620696e64657820697320746f6f2062696760581b604482015260640161051c565b825160009087908290611e629060016124bc565b905060205b81811015611efa57868101519350611e8060028a612532565b600003611eb8576040805160208101859052908101859052606001604051602081830303815290604052805190602001209250611ee5565b60408051602081018690529081018490526060016040516020818303038152906040528051906020012092505b611ef060028a612546565b9850602001611e67565b50509094149695505050505050565b8051600090811a6080811015611f225750600092915050565b60b8811080611f495750611f38600160c061259e565b60ff1681118015611f49575060f881105b15611f575750600192915050565b60c0811015611f8b57611f6c600160b861259e565b611f799060ff1682612390565b611f849060016124bc565b9392505050565b611f6c600160f861259e565b50919050565b80516000908103611fb057506000919050565b600080611fc08460200151611f09565b8460200151611fcf91906124bc565b9050600084600001518560200151611fe791906124bc565b90505b8082101561201957611ffb82612022565b61200590836124bc565b915082612011816126a7565b935050611fea565b50909392505050565b80516000908190811a608081101561203d5760019150611c5e565b60b881101561206357612051608082612390565b61205c9060016124bc565b9150611c5e565b60c08110156120905760b78103600185019450806020036101000a85510460018201810193505050611c5e565b60f88110156120a45761205160c082612390565b60019390930151602084900360f7016101000a900490920160f5190192915050565b806000036120d357505050565b6120df6001602061259e565b60ff16811115611b8657825182526120f86020846124bc565b92506121056020836124bc565b9150612112602082612390565b90506120d3565b634e487b7160e01b600052604160045260246000fd5b604051601f8201601f1916810167ffffffffffffffff8111828210171561215857612158612119565b604052919050565b600067ffffffffffffffff82111561217a5761217a612119565b50601f01601f191660200190565b600082601f83011261219957600080fd5b81356121ac6121a782612160565b61212f565b8181528460208386010111156121c157600080fd5b816020850160208301376000918101602001919091529392505050565b600080604083850312156121f157600080fd5b823567ffffffffffffffff8082111561220957600080fd5b61221586838701612188565b9350602085013591508082111561222b57600080fd5b5061223885828601612188565b9150509250929050565b60006020828403121561225457600080fd5b813567ffffffffffffffff81111561226b57600080fd5b61162684828501612188565b6001600160a01b038116811461064857600080fd5b60006020828403121561229e57600080fd5b8135611f8481612277565b6000602082840312156122bb57600080fd5b5035919050565b60005b838110156122dd5781810151838201526020016122c5565b50506000910152565b600081518084526122fe8160208601602086016122c2565b601f01601f19169290920160200192915050565b60608152600061232560608301866122e6565b828103602084015261233781866122e6565b91505060018060a01b0383166040830152949350505050565b60408152600061236360408301856122e6565b905060018060a01b03831660208301529392505050565b634e487b7160e01b600052601160045260246000fd5b818103818111156108115761081161237a565b838152600083516123bb8160208501602088016122c2565b60209201918201929092526040019392505050565b6000602082840312156123e257600080fd5b815167ffffffffffffffff8111156123f957600080fd5b8201601f8101841361240a57600080fd5b80516124186121a782612160565b81815285602083850101111561242d57600080fd5b61243e8260208301602086016122c2565b95945050505050565b80516020808301519190811015611f975760001960209190910360031b1b16919050565b6001600160a01b0383168152604060208201819052600090611626908301846122e6565b634e487b7160e01b600052603260045260246000fd5b80820281158282048414176108115761081161237a565b808201808211156108115761081161237a565b600080600080600060a086880312156124e757600080fd5b85519450602086015193506040860151925060608601519150608086015161250e81612277565b809150509295509295909350565b634e487b7160e01b600052601260045260246000fd5b6000826125415761254161251c565b500690565b6000826125555761255561251c565b500490565b600060ff83168061256d5761256d61251c565b8060ff84160691505092915050565b600060ff83168061258f5761258f61251c565b8060ff84160491505092915050565b60ff82811682821603908111156108115761081161237a565b600181815b808511156125f25781600019048211156125d8576125d861237a565b808516156125e557918102915b93841c93908002906125bc565b509250929050565b60008261260957506001610811565b8161261657506000610811565b816001811461262c576002811461263657612652565b6001915050610811565b60ff8411156126475761264761237a565b50506001821b610811565b5060208310610133831016604e8410600b8410161715612675575081810a610811565b61267f83836125b7565b80600019048211156126935761269361237a565b029392505050565b6000611f8483836125fa565b6000600182016126b9576126b961237a565b506001019056fea2646970667358221220be914e6f3dfeddf3d452269e093e5fe0dbea3f244152c7e04a21363d6d0e6e3764736f6c63430008110033

Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

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

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits
Loading...
Loading
[ Download: CSV Export  ]

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