Wemix Testnet

Contract

0x8f6cb63eD5e379722580DFF0A051C140C64F9619

Overview

WEMIX Balance

WEMIX3.0 LogoWEMIX3.0 LogoWEMIX3.0 Logo0 WEMIX

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
Vote To Bless784693282025-01-10 14:54:0111 days ago1736520841IN
0x8f6cb63e...0C64F9619
0 WEMIX0.0042722100
Vote To Bless784691552025-01-10 14:51:0811 days ago1736520668IN
0x8f6cb63e...0C64F9619
0 WEMIX0.0042722100
Vote To Bless784691462025-01-10 14:50:5911 days ago1736520659IN
0x8f6cb63e...0C64F9619
0 WEMIX0.0042722100
Vote To Bless784691452025-01-10 14:50:5811 days ago1736520658IN
0x8f6cb63e...0C64F9619
0 WEMIX0.0057765100
Vote To Bless784691362025-01-10 14:50:4911 days ago1736520649IN
0x8f6cb63e...0C64F9619
0 WEMIX0.0057765100
Vote To Bless784689732025-01-10 14:48:0611 days ago1736520486IN
0x8f6cb63e...0C64F9619
0 WEMIX0.0057765100
Vote To Bless784688402025-01-10 14:45:5311 days ago1736520353IN
0x8f6cb63e...0C64F9619
0 WEMIX0.0042722100
Vote To Bless784688392025-01-10 14:45:5211 days ago1736520352IN
0x8f6cb63e...0C64F9619
0 WEMIX0.0057765100
Vote To Bless784677682025-01-10 14:28:0111 days ago1736519281IN
0x8f6cb63e...0C64F9619
0 WEMIX0.0042722100
Vote To Bless784676802025-01-10 14:26:3311 days ago1736519193IN
0x8f6cb63e...0C64F9619
0 WEMIX0.0042722100
Vote To Bless784676782025-01-10 14:26:3111 days ago1736519191IN
0x8f6cb63e...0C64F9619
0 WEMIX0.0057765100
Vote To Bless784674132025-01-10 14:22:0611 days ago1736518926IN
0x8f6cb63e...0C64F9619
0 WEMIX0.0057765100
Vote To Bless784672382025-01-10 14:19:1111 days ago1736518751IN
0x8f6cb63e...0C64F9619
0 WEMIX0.004271100
Vote To Bless784672152025-01-10 14:18:4811 days ago1736518728IN
0x8f6cb63e...0C64F9619
0 WEMIX0.0057753100
Vote To Bless784659072025-01-10 13:57:0011 days ago1736517420IN
0x8f6cb63e...0C64F9619
0 WEMIX0.0042722100
Vote To Bless784659042025-01-10 13:56:5711 days ago1736517417IN
0x8f6cb63e...0C64F9619
0 WEMIX0.0057765100
Vote To Bless784658792025-01-10 13:56:3211 days ago1736517392IN
0x8f6cb63e...0C64F9619
0 WEMIX0.0042722100
Vote To Bless784658772025-01-10 13:56:3011 days ago1736517390IN
0x8f6cb63e...0C64F9619
0 WEMIX0.0057765100
Vote To Bless763354192024-12-16 22:09:1636 days ago1734386956IN
0x8f6cb63e...0C64F9619
0 WEMIX0.0042722100
Vote To Bless763353002024-12-16 22:07:1736 days ago1734386837IN
0x8f6cb63e...0C64F9619
0 WEMIX0.0057765100
Vote To Bless763352882024-12-16 22:07:0536 days ago1734386825IN
0x8f6cb63e...0C64F9619
0 WEMIX0.0042722100
Vote To Bless763351692024-12-16 22:05:0636 days ago1734386706IN
0x8f6cb63e...0C64F9619
0 WEMIX0.0057765100
Vote To Bless763338912024-12-16 21:43:4836 days ago1734385428IN
0x8f6cb63e...0C64F9619
0 WEMIX0.0042722100
Vote To Bless763337732024-12-16 21:41:5036 days ago1734385310IN
0x8f6cb63e...0C64F9619
0 WEMIX0.0057765100
Vote To Bless763335362024-12-16 21:37:5336 days ago1734385073IN
0x8f6cb63e...0C64F9619
0 WEMIX0.0042722100
View all transactions

Latest 25 internal transactions (View All)

Parent Transaction Hash Block From To
794580022025-01-22 1:31:4321 secs ago1737509503
0x8f6cb63e...0C64F9619
0 WEMIX
794578732025-01-22 1:29:342 mins ago1737509374
0x8f6cb63e...0C64F9619
0 WEMIX
794578132025-01-22 1:28:343 mins ago1737509314
0x8f6cb63e...0C64F9619
0 WEMIX
794577612025-01-22 1:27:424 mins ago1737509262
0x8f6cb63e...0C64F9619
0 WEMIX
794577012025-01-22 1:26:425 mins ago1737509202
0x8f6cb63e...0C64F9619
0 WEMIX
794576932025-01-22 1:26:345 mins ago1737509194
0x8f6cb63e...0C64F9619
0 WEMIX
794575732025-01-22 1:24:347 mins ago1737509074
0x8f6cb63e...0C64F9619
0 WEMIX
794575212025-01-22 1:23:428 mins ago1737509022
0x8f6cb63e...0C64F9619
0 WEMIX
794575132025-01-22 1:23:348 mins ago1737509014
0x8f6cb63e...0C64F9619
0 WEMIX
794574612025-01-22 1:22:429 mins ago1737508962
0x8f6cb63e...0C64F9619
0 WEMIX
794574532025-01-22 1:22:349 mins ago1737508954
0x8f6cb63e...0C64F9619
0 WEMIX
794574012025-01-22 1:21:4210 mins ago1737508902
0x8f6cb63e...0C64F9619
0 WEMIX
794573932025-01-22 1:21:3410 mins ago1737508894
0x8f6cb63e...0C64F9619
0 WEMIX
794573412025-01-22 1:20:4211 mins ago1737508842
0x8f6cb63e...0C64F9619
0 WEMIX
794571532025-01-22 1:17:3414 mins ago1737508654
0x8f6cb63e...0C64F9619
0 WEMIX
794569732025-01-22 1:14:3417 mins ago1737508474
0x8f6cb63e...0C64F9619
0 WEMIX
794567332025-01-22 1:10:3421 mins ago1737508234
0x8f6cb63e...0C64F9619
0 WEMIX
794566812025-01-22 1:09:4222 mins ago1737508182
0x8f6cb63e...0C64F9619
0 WEMIX
794566732025-01-22 1:09:3422 mins ago1737508174
0x8f6cb63e...0C64F9619
0 WEMIX
794566212025-01-22 1:08:4223 mins ago1737508122
0x8f6cb63e...0C64F9619
0 WEMIX
794566132025-01-22 1:08:3423 mins ago1737508114
0x8f6cb63e...0C64F9619
0 WEMIX
794565612025-01-22 1:07:4224 mins ago1737508062
0x8f6cb63e...0C64F9619
0 WEMIX
794565532025-01-22 1:07:3424 mins ago1737508054
0x8f6cb63e...0C64F9619
0 WEMIX
794564412025-01-22 1:05:4226 mins ago1737507942
0x8f6cb63e...0C64F9619
0 WEMIX
794564332025-01-22 1:05:3426 mins ago1737507934
0x8f6cb63e...0C64F9619
0 WEMIX
View All Internal Transactions
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
RMN

Compiler Version
v0.8.24+commit.e11b9ed9

Optimization Enabled:
Yes with 26000 runs

Other Settings:
paris EvmVersion

Contract Source Code (Solidity Standard Json-Input format)

File 1 of 8 : RMN.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.24;

import {ITypeAndVersion} from "../shared/interfaces/ITypeAndVersion.sol";
import {IRMN} from "./interfaces/IRMN.sol";

import {OwnerIsCreator} from "./../shared/access/OwnerIsCreator.sol";

import {EnumerableSet} from "../vendor/openzeppelin-solidity/v4.8.3/contracts/utils/structs/EnumerableSet.sol";

// An active curse on this subject will cause isCursed() to return true. Use this subject if there is an issue with a
// remote chain, for which there exists a legacy lane contract deployed on the same chain as this RMN contract is
// deployed, relying on isCursed().
bytes16 constant LEGACY_CURSE_SUBJECT = 0x01000000000000000000000000000000;

// An active curse on this subject will cause isCursed() and isCursed(bytes32) to return true. Use this subject for
// issues affecting all of CCIP chains, or pertaining to the chain that this contract is deployed on, instead of using
// the local chain selector as a subject.
bytes16 constant GLOBAL_CURSE_SUBJECT = 0x01000000000000000000000000000001;

// The curse vote address representing the owner in data structures, events and recorded votes. Remains constant, even
// if the owner changes.
address constant OWNER_CURSE_VOTE_ADDR = address(~uint160(0)); // 0xff...ff

// The curse vote address used in an OwnerUnvoteToCurseRequest to lift a curse, if there is no active curse votes for
// the subject that we are able to unvote, but the conditions for an active curse no longer hold.
address constant LIFT_CURSE_VOTE_ADDR = address(0);

/// @dev This contract is owned by RMN, if changing, please notify the RMN maintainers.
// solhint-disable chainlink-solidity/explicit-returns
contract RMN is IRMN, OwnerIsCreator, ITypeAndVersion {
  using EnumerableSet for EnumerableSet.AddressSet;

  // STATIC CONFIG
  string public constant override typeAndVersion = "RMN 1.5.0";

  uint256 private constant MAX_NUM_VOTERS = 16;

  // MAGIC VALUES
  bytes28 private constant NO_VOTES_CURSES_HASH = bytes28(0);

  // DYNAMIC CONFIG
  /// @notice blessVoteAddr and curseVoteAddr can't be 0. Additionally curseVoteAddr can't be LIFT_CURSE_VOTE_ADDR or
  /// OWNER_CURSE_VOTE_ADDR. At least one of blessWeight & curseWeight must be non-zero, i.e., a voter could only vote
  /// to bless, or only vote to curse, or both vote to bless and vote to curse.
  struct Voter {
    // This is the address the voter should use to call voteToBless.
    address blessVoteAddr;
    // This is the address the voter should use to call voteToCurse.
    address curseVoteAddr;
    // The weight of this voter's vote for blessing.
    uint8 blessWeight;
    // The weight of this voter's vote for cursing.
    uint8 curseWeight;
  }

  struct Config {
    Voter[] voters;
    // When the total weight of voters that have voted to bless a tagged root reaches
    // or exceeds blessWeightThreshold, the tagged root becomes blessed.
    uint16 blessWeightThreshold;
    // When the total weight of voters that have voted to curse a subject reaches or
    // exceeds curseWeightThreshold, the subject becomes cursed.
    uint16 curseWeightThreshold;
  }

  struct VersionedConfig {
    Config config;
    // The version is incremented every time the config changes.
    // The initial configuration on the contract will have configVersion == 1.
    uint32 configVersion;
    // The block number at which the config was last set. Helps the offchain
    // code check that the config was set in a stable block or double-check
    // that it has the correct config by querying logs at that block number.
    uint32 blockNumber;
  }

  VersionedConfig private s_versionedConfig;

  // STATE
  struct BlesserRecord {
    // The config version at which this BlesserRecord was last set. A blesser
    // is considered active iff this configVersion equals
    // s_versionedConfig.configVersion.
    uint32 configVersion;
    uint8 weight;
    uint8 index;
  }

  mapping(address blessVoteAddr => BlesserRecord blesserRecord) private s_blesserRecords;

  struct BlessVoteProgress {
    // This particular ordering saves us ~400 gas per voteToBless call, compared to the bool being at the bottom, even
    // though the size of the struct is the same.
    bool weightThresholdMet;
    // A BlessVoteProgress is considered invalid if weightThresholdMet is false when
    // s_versionedConfig.configVersion changes. we don't want old in-progress
    // votes to continue when we set a new config!
    // The config version at which the bless vote for a tagged root was initiated.
    uint32 configVersion;
    uint16 accumulatedWeight;
    // Care must be taken that the bitmap has at least as many bits as MAX_NUM_VOTERS.
    // uint200 is much larger than we need, but it saves us ~100 gas per voteToBless call to fill the word instead of
    // using a smaller type.
    // _bitmapGet(voterBitmap, i) = true indicates that the i-th voter has voted to bless
    uint200 voterBitmap;
  }

  mapping(bytes32 taggedRootHash => BlessVoteProgress blessVoteProgress) private s_blessVoteProgressByTaggedRootHash;

  // Any tagged root with a commit store included in s_permaBlessedCommitStores will be considered automatically
  // blessed.
  EnumerableSet.AddressSet private s_permaBlessedCommitStores;

  struct CurserRecord {
    bool active;
    uint8 weight;
    mapping(bytes16 curseId => bool used) usedCurseIds; // retained across config changes
  }

  mapping(address curseVoteAddr => CurserRecord curserRecord) private s_curserRecords;

  struct ConfigVersionAndCursesHash {
    uint32 configVersion; // configVersion != s_versionedConfig.configVersion means no active vote
    bytes28 cursesHash; // bytes28(0) means no active vote; truncated so that ConfigVersionAndCursesHash fits in a word
  }

  struct CurseVoteProgress {
    uint32 configVersion; // upon config change, lazy set to new config version
    uint16 curseWeightThreshold; // upon config change, lazy set to new config value
    uint16 accumulatedWeight; // upon config change, lazy set to 0
    // A curse becomes active after either:
    // - sum([voter.weight for voter who voted in current config]) >= curseWeightThreshold
    // - ownerCurse is invoked
    // Once a curse is active, only the owner can lift it.
    bool curseActive; // retained across config changes
    mapping(address => ConfigVersionAndCursesHash) latestVoteToCurseByCurseVoteAddr; // retained across config changes
  }

  mapping(bytes16 subject => CurseVoteProgress curseVoteProgress) private
    s_potentiallyOutdatedCurseVoteProgressBySubject;

  // We intentionally use a struct here, even though it contains a single field, to make it obvious to future editors
  // that there is space for more fields.
  struct CurseHotVars {
    uint64 numSubjectsCursed; // incremented by voteToCurse, ownerCurse; decremented by ownerUnvoteToCurse
  }

  CurseHotVars private s_curseHotVars;

  enum RecordedCurseRelatedOpTag {
    // A vote to curse, through either voteToCurse or ownerCurse.
    VoteToCurse,
    // An unvote to curse, through unvoteToCurse.
    UnvoteToCurse,
    // An unvote to curse, through ownerUnvoteToCurse, which was not forced (forceUnvote=false).
    OwnerUnvoteToCurseUnforced,
    // An unvote to curse, through ownerUnvoteToCurse, which was forced (forceUnvote=true).
    OwnerUnvoteToCurseForced,
    // A configuration change.
    //
    // For subjects that are not cursed when this happens, past votes do not get accounted for in the new configuration.
    // If a voter votes during the new configuration, their curses hash will restart from NO_VOTES_CURSES_HASH.
    //
    // For subjects that are cursed when this happens, past votes get accounted for.
    // If a voter votes during the new configuration, their curses hash will continue from its old value.
    SetConfig
  }

  /// @notice Provides the ability to quickly reconstruct the curse-related state of the contract offchain, without
  /// having to replay all past events. Replaying past events often takes long, and in some cases might even be
  /// infeasible due to log pruning.
  ///
  /// @dev We could save some gas by omitting some fields and instead using them as mapping keys, but we would lose the
  /// cross-voter ordering, or cross-subject ordering, or cross-vote/unvote ordering.
  struct RecordedCurseRelatedOp {
    RecordedCurseRelatedOpTag tag;
    uint64 blockTimestamp;
    bool cursed; // whether the subject is cursed after this op; if tag in {SetConfig}, will be false
    address curseVoteAddr; // if tag in {SetConfig}, will be address(0)
    bytes16 subject; // if tag in {SetConfig}, will be bytes16(0)
    bytes16 curseId; // if tag in {SetConfig, UnvoteToCurse, OwnerUnvoteToCurseUnforced, OwnerUnvoteToCurseForced}, will be bytes16(0)
  }

  RecordedCurseRelatedOp[] private s_recordedCurseRelatedOps;

  /// @dev This function is to _ONLY_ be called in order to determine if a curse should become active upon a
  /// vote-to-curse, or a curse should be deactivated upon an owner-unvote-to-curse.
  /// Other reasons for a curse to be active, which are not covered here:
  /// 1. Cursedness is retained from a prior config.
  /// 2. The curse weight threshold was met at some point, which activated a curse, and enough voters unvoted to curse
  /// such that the curse weight threshold is no longer met.
  function _shouldCurseBeActive(CurseVoteProgress storage sptr_upToDateCurseVoteProgress) internal view returns (bool) {
    return sptr_upToDateCurseVoteProgress.latestVoteToCurseByCurseVoteAddr[OWNER_CURSE_VOTE_ADDR].cursesHash
      != NO_VOTES_CURSES_HASH
      || sptr_upToDateCurseVoteProgress.accumulatedWeight >= sptr_upToDateCurseVoteProgress.curseWeightThreshold;
  }

  /// @dev It might be the case that due to the lazy update of curseVoteProgress, a curse is active even though
  /// _shouldCurseBeActive(curseVoteProgress) is false, i.e., the owner has no active vote to curse and the curse
  /// weight threshold has not been met.
  function _getUpToDateCurseVoteProgress(
    uint32 configVersion,
    bytes16 subject
  ) internal returns (CurseVoteProgress storage) {
    CurseVoteProgress storage sptr_curseVoteProgress = s_potentiallyOutdatedCurseVoteProgressBySubject[subject];
    if (configVersion != sptr_curseVoteProgress.configVersion) {
      sptr_curseVoteProgress.configVersion = configVersion;
      sptr_curseVoteProgress.curseWeightThreshold = s_versionedConfig.config.curseWeightThreshold;
      sptr_curseVoteProgress.accumulatedWeight = 0;

      if (sptr_curseVoteProgress.curseActive) {
        // If a curse was active, count past votes to curse and retain the curses hash for cursers who are part of the
        // new config.
        Config storage sptr_config = s_versionedConfig.config;
        for (uint256 i = 0; i < sptr_config.voters.length; ++i) {
          Voter storage sptr_voter = sptr_config.voters[i];
          ConfigVersionAndCursesHash storage sptr_cvch =
            sptr_curseVoteProgress.latestVoteToCurseByCurseVoteAddr[sptr_voter.curseVoteAddr];
          if (sptr_cvch.configVersion < configVersion && sptr_cvch.cursesHash != NO_VOTES_CURSES_HASH) {
            // `< configVersion` instead of `== configVersion-1`, because there might have been multiple config changes
            // without a lazy update of our subject. This has the side effect of retaining votes from very old configs
            // that we might not really intend to retain, but these can be removed by the owner later.
            sptr_cvch.configVersion = configVersion;
            sptr_curseVoteProgress.accumulatedWeight += sptr_voter.curseWeight;
          }
        }
        // We don't need to think about OWNER_CURSE_VOTE_ADDR here, because its ConfigVersionAndCursesHash counts even
        // if the configVersion is not the current config version, in contrast to regular voters.
        // It's an irregularity, but it saves us > 5k gas (if the owner had previously voted) for the unlucky voter who
        // enters this branch.
      } else {
        // If a curse was not active, we don't count past votes to curse for voters who are part of the new config.
        // Their curses hash will be restart from NO_VOTES_CURSES_HASH when they vote to curse again.
        // We expect that the offchain code will revote to curse in case it voted to curse, and the vote to curse was
        // lost due to any reason, including a config change when the curse was not yet active.
      }
    }
    return sptr_curseVoteProgress;
  }

  // EVENTS, ERRORS

  event ConfigSet(uint32 indexed configVersion, Config config);

  error InvalidConfig();

  event TaggedRootBlessed(uint32 indexed configVersion, IRMN.TaggedRoot taggedRoot, uint16 accumulatedWeight);
  event TaggedRootBlessVotesReset(uint32 indexed configVersion, IRMN.TaggedRoot taggedRoot, bool wasBlessed);
  event VotedToBless(uint32 indexed configVersion, address indexed voter, IRMN.TaggedRoot taggedRoot, uint8 weight);

  event VotedToCurse(
    uint32 indexed configVersion,
    address indexed voter,
    bytes16 subject,
    bytes16 curseId,
    uint8 weight,
    uint64 blockTimestamp,
    bytes28 cursesHash,
    uint16 accumulatedWeight
  );
  event UnvotedToCurse(
    uint32 indexed configVersion,
    address indexed voter,
    bytes16 subject,
    uint8 weight,
    bytes28 cursesHash,
    uint16 remainingAccumulatedWeight
  );
  event SkippedUnvoteToCurse(address indexed voter, bytes16 subject, bytes28 onchainCursesHash, bytes28 cursesHash);
  event Cursed(uint32 indexed configVersion, bytes16 subject, uint64 blockTimestamp);
  event CurseLifted(bytes16 subject);

  // These events make it easier for offchain logic to discover that it performs
  // the same actions multiple times.
  event AlreadyVotedToBless(uint32 indexed configVersion, address indexed voter, IRMN.TaggedRoot taggedRoot);
  event AlreadyBlessed(uint32 indexed configVersion, address indexed voter, IRMN.TaggedRoot taggedRoot);

  // Emitted by ownerRemoveThenAddPermaBlessedCommitStores.
  event PermaBlessedCommitStoreAdded(address commitStore);
  event PermaBlessedCommitStoreRemoved(address commitStore);

  error ReusedCurseId(address voter, bytes16 curseId);
  error UnauthorizedVoter(address voter);
  error VoteToBlessNoop();
  error VoteToCurseNoop();
  error UnvoteToCurseNoop();
  error VoteToBlessForbiddenDuringActiveGlobalCurse();

  /// @notice Thrown when subjects are not a strictly increasing monotone sequence.
  // Prevents a subject from receiving multiple votes to curse with the same curse id.
  error SubjectsMustBeStrictlyIncreasing();

  constructor(Config memory config) {
    {
      // Ensure that the bitmap is large enough to hold MAX_NUM_VOTERS.
      // We do this in the constructor because MAX_NUM_VOTERS is constant.
      BlessVoteProgress memory vp = BlessVoteProgress({
        configVersion: 0,
        voterBitmap: type(uint200).max, // will not compile if it doesn't fit
        accumulatedWeight: 0,
        weightThresholdMet: false
      });
      assert(vp.voterBitmap >> (MAX_NUM_VOTERS - 1) >= 1);
    }
    _setConfig(config);
  }

  function _bitmapGet(uint200 bitmap, uint8 index) internal pure returns (bool) {
    assert(index < MAX_NUM_VOTERS);
    return bitmap & (uint200(1) << index) != 0;
  }

  function _bitmapSet(uint200 bitmap, uint8 index) internal pure returns (uint200) {
    assert(index < MAX_NUM_VOTERS);
    return bitmap | (uint200(1) << index);
  }

  function _bitmapCount(uint200 bitmap) internal pure returns (uint8 oneBits) {
    assert(bitmap < 1 << MAX_NUM_VOTERS);
    // https://graphics.stanford.edu/~seander/bithacks.html#CountBitsSetKernighan
    for (; bitmap != 0; ++oneBits) {
      bitmap &= bitmap - 1;
    }
  }

  function _taggedRootHash(IRMN.TaggedRoot memory taggedRoot) internal pure returns (bytes32) {
    return keccak256(abi.encode(taggedRoot.commitStore, taggedRoot.root));
  }

  function _cursesHash(bytes28 prevCursesHash, bytes16 curseId) internal pure returns (bytes28) {
    return bytes28(keccak256(abi.encode(prevCursesHash, curseId)));
  }

  function _blockTimestamp() internal view returns (uint64) {
    return uint64(block.timestamp);
  }

  /// @param taggedRoots A tagged root is hashed as `keccak256(abi.encode(taggedRoot.commitStore
  /// /* address */, taggedRoot.root /* bytes32 */))`.
  /// @notice Tagged roots which are already (voted to be) blessed are skipped and emit corresponding events. In case
  /// the call has no effect, i.e., all passed tagged roots are skipped, the function reverts with a `VoteToBlessNoop`.
  function voteToBless(IRMN.TaggedRoot[] calldata taggedRoots) external {
    // If we have an active global curse, something is really wrong. Let's err on the
    // side of caution and not accept further blessings during this time of
    // uncertainty.
    if (isCursed(GLOBAL_CURSE_SUBJECT)) revert VoteToBlessForbiddenDuringActiveGlobalCurse();

    uint32 configVersion = s_versionedConfig.configVersion;
    BlesserRecord memory blesserRecord = s_blesserRecords[msg.sender];
    if (blesserRecord.configVersion != configVersion) revert UnauthorizedVoter(msg.sender);

    bool noop = true;
    for (uint256 i = 0; i < taggedRoots.length; ++i) {
      IRMN.TaggedRoot memory taggedRoot = taggedRoots[i];
      bytes32 taggedRootHash = _taggedRootHash(taggedRoot);
      BlessVoteProgress memory voteProgress = s_blessVoteProgressByTaggedRootHash[taggedRootHash];
      if (voteProgress.weightThresholdMet) {
        // We don't revert here because it's unreasonable to expect from the
        // voter to know exactly when to stop voting. Most likely when they
        // voted they didn't realize the threshold would be reached by the time
        // their vote was counted.
        // Additionally, there might be other tagged roots for which votes might
        // count, and we want to allow that to happen.
        emit AlreadyBlessed(configVersion, msg.sender, taggedRoot);
        continue;
      } else if (voteProgress.configVersion != configVersion) {
        // Note that voteProgress.weightThresholdMet must be false at this point

        // If votes were received while an older config was in effect,
        // invalidate them and start from scratch.
        // If votes were never received, set the current config version.
        voteProgress = BlessVoteProgress({
          configVersion: configVersion,
          voterBitmap: 0,
          accumulatedWeight: 0,
          weightThresholdMet: false
        });
      } else if (_bitmapGet(voteProgress.voterBitmap, blesserRecord.index)) {
        // We don't revert here because there might be other tagged roots for
        // which votes might count, and we want to allow that to happen.
        emit AlreadyVotedToBless(configVersion, msg.sender, taggedRoot);
        continue;
      }
      noop = false;
      voteProgress.voterBitmap = _bitmapSet(voteProgress.voterBitmap, blesserRecord.index);
      voteProgress.accumulatedWeight += blesserRecord.weight;
      emit VotedToBless(configVersion, msg.sender, taggedRoot, blesserRecord.weight);
      if (voteProgress.accumulatedWeight >= s_versionedConfig.config.blessWeightThreshold) {
        voteProgress.weightThresholdMet = true;
        emit TaggedRootBlessed(configVersion, taggedRoot, voteProgress.accumulatedWeight);
      }
      s_blessVoteProgressByTaggedRootHash[taggedRootHash] = voteProgress;
    }

    if (noop) {
      revert VoteToBlessNoop();
    }
  }

  /// @notice Can be called by the owner to remove unintentionally voted or even blessed tagged roots in a recovery
  /// scenario. The owner must ensure that there are no in-flight transactions by RMN nodes voting for any of the
  /// taggedRoots before calling this function, as such in-flight transactions could lead to the roots becoming
  /// re-blessed shortly after the call to this function, contrary to the original intention.
  function ownerResetBlessVotes(IRMN.TaggedRoot[] calldata taggedRoots) external onlyOwner {
    uint32 configVersion = s_versionedConfig.configVersion;
    for (uint256 i = 0; i < taggedRoots.length; ++i) {
      IRMN.TaggedRoot memory taggedRoot = taggedRoots[i];
      bytes32 taggedRootHash = _taggedRootHash(taggedRoot);
      BlessVoteProgress memory voteProgress = s_blessVoteProgressByTaggedRootHash[taggedRootHash];
      delete s_blessVoteProgressByTaggedRootHash[taggedRootHash];
      bool wasBlessed = voteProgress.weightThresholdMet;
      if (voteProgress.configVersion == configVersion || wasBlessed) {
        emit TaggedRootBlessVotesReset(configVersion, taggedRoot, wasBlessed);
      }
    }
  }

  struct UnvoteToCurseRequest {
    bytes16 subject;
    bytes28 cursesHash;
  }

  // For use in internal calls.
  enum Privilege {
    Owner,
    Voter
  }

  function _authorizedUnvoteToCurse(
    Privilege priv, // Privilege.Owner during an ownerUnvoteToCurse call, Privilege.Voter during a unvoteToCurse call
    uint32 configVersion,
    address curseVoteAddr,
    UnvoteToCurseRequest memory req,
    bool forceUnvote, // true only during an ownerUnvoteToCurse call, when OwnerUnvoteToCurseRequest.forceUnvote is true
    CurserRecord storage sptr_curserRecord,
    CurseVoteProgress storage sptr_curseVoteProgress
  ) internal returns (bool unvoted, bool curseLifted) {
    {
      assert(priv == Privilege.Voter || priv == Privilege.Owner); // sanity check
      // Check that the supplied arguments are feasible for our privilege.
      if (forceUnvote || curseVoteAddr == OWNER_CURSE_VOTE_ADDR || curseVoteAddr == LIFT_CURSE_VOTE_ADDR) {
        assert(priv == Privilege.Owner);
      }
    }

    ConfigVersionAndCursesHash memory cvch = sptr_curseVoteProgress.latestVoteToCurseByCurseVoteAddr[curseVoteAddr];

    // First, try to unvote.
    if (
      sptr_curserRecord.active && (curseVoteAddr == OWNER_CURSE_VOTE_ADDR || cvch.configVersion == configVersion)
        && cvch.cursesHash != NO_VOTES_CURSES_HASH && (cvch.cursesHash == req.cursesHash || forceUnvote)
    ) {
      unvoted = true;
      delete sptr_curseVoteProgress.latestVoteToCurseByCurseVoteAddr[curseVoteAddr];
      // Assumes: s_curserRecords[OWNER_CURSE_VOTE_ADDR].weight == 0, enforced by _setConfig
      sptr_curseVoteProgress.accumulatedWeight -= sptr_curserRecord.weight;

      emit UnvotedToCurse(
        configVersion,
        curseVoteAddr,
        req.subject,
        sptr_curserRecord.weight,
        req.cursesHash,
        sptr_curseVoteProgress.accumulatedWeight
      );
    }

    // If we have owner privilege, and the conditions for the curse to be active no longer hold, we are able to lift the
    // curse.
    bool shouldTryToLiftCurse = priv == Privilege.Owner && (unvoted || curseVoteAddr == LIFT_CURSE_VOTE_ADDR);

    if (shouldTryToLiftCurse && sptr_curseVoteProgress.curseActive && !_shouldCurseBeActive(sptr_curseVoteProgress)) {
      curseLifted = true;
      sptr_curseVoteProgress.curseActive = false;
      --s_curseHotVars.numSubjectsCursed;
      emit CurseLifted(req.subject);
    }

    if (unvoted || curseLifted) {
      RecordedCurseRelatedOpTag tag;
      if (priv == Privilege.Owner) {
        if (forceUnvote) {
          tag = RecordedCurseRelatedOpTag.OwnerUnvoteToCurseForced;
        } else {
          tag = RecordedCurseRelatedOpTag.OwnerUnvoteToCurseUnforced;
        }
      } else if (priv == Privilege.Voter) {
        tag = RecordedCurseRelatedOpTag.UnvoteToCurse;
      } else {
        // solhint-disable-next-line gas-custom-errors, reason-string
        revert(); // assumption violation
      }
      s_recordedCurseRelatedOps.push(
        RecordedCurseRelatedOp({
          tag: tag,
          cursed: sptr_curseVoteProgress.curseActive,
          curseVoteAddr: curseVoteAddr,
          curseId: bytes16(0),
          subject: req.subject,
          blockTimestamp: _blockTimestamp()
        })
      );
    } else {
      emit SkippedUnvoteToCurse(curseVoteAddr, req.subject, cvch.cursesHash, req.cursesHash);
    }
  }

  /// @notice Can be called by a curser to remove unintentional votes to curse.
  /// We expect this to be called very rarely, e.g. in case of a bug in the
  /// offchain code causing false voteToCurse calls.
  /// @notice Should be called from curser's corresponding curseVoteAddr.
  function unvoteToCurse(UnvoteToCurseRequest[] memory unvoteToCurseRequests) external {
    address curseVoteAddr = msg.sender;
    CurserRecord storage sptr_curserRecord = s_curserRecords[curseVoteAddr];

    if (!sptr_curserRecord.active) revert UnauthorizedVoter(curseVoteAddr);

    uint32 configVersion = s_versionedConfig.configVersion;
    bool anyVoteWasUnvoted = false;
    for (uint256 i = 0; i < unvoteToCurseRequests.length; ++i) {
      UnvoteToCurseRequest memory req = unvoteToCurseRequests[i];
      CurseVoteProgress storage sptr_curseVoteProgress = _getUpToDateCurseVoteProgress(configVersion, req.subject);
      (bool unvoted, bool curseLifted) = _authorizedUnvoteToCurse(
        Privilege.Voter, configVersion, curseVoteAddr, req, false, sptr_curserRecord, sptr_curseVoteProgress
      );
      assert(!curseLifted); // assumption violation: voters can't lift curses
      anyVoteWasUnvoted = anyVoteWasUnvoted || unvoted;
    }

    if (!anyVoteWasUnvoted) {
      revert UnvoteToCurseNoop();
    }
  }

  /// @notice A vote to curse is appropriate during unhealthy blockchain conditions
  /// (eg. finality violations).
  function voteToCurse(bytes16 curseId, bytes16[] memory subjects) external {
    address curseVoteAddr = msg.sender;
    assert(curseVoteAddr != OWNER_CURSE_VOTE_ADDR);
    CurserRecord storage sptr_curserRecord = s_curserRecords[curseVoteAddr];
    if (!sptr_curserRecord.active) revert UnauthorizedVoter(curseVoteAddr);
    _authorizedVoteToCurse(curseVoteAddr, curseId, subjects, sptr_curserRecord);
  }

  function _authorizedVoteToCurse(
    address curseVoteAddr,
    bytes16 curseId,
    bytes16[] memory subjects,
    CurserRecord storage sptr_curserRecord
  ) internal {
    if (subjects.length == 0) revert VoteToCurseNoop();

    if (sptr_curserRecord.usedCurseIds[curseId]) revert ReusedCurseId(curseVoteAddr, curseId);
    sptr_curserRecord.usedCurseIds[curseId] = true;

    // NOTE: We could pack configVersion into CurserRecord that we already load in the beginning of this function to
    // avoid the following extra storage read for it, but since voteToCurse is not on the hot path we'd rather keep
    // things simple.
    uint32 configVersion = s_versionedConfig.configVersion;
    for (uint256 i = 0; i < subjects.length; ++i) {
      if (i >= 1 && !(subjects[i - 1] < subjects[i])) {
        // Prevents a subject from receiving multiple votes to curse with the same curse id.
        revert SubjectsMustBeStrictlyIncreasing();
      }

      bytes16 subject = subjects[i];
      CurseVoteProgress storage sptr_curseVoteProgress = _getUpToDateCurseVoteProgress(configVersion, subject);
      ConfigVersionAndCursesHash memory cvch = sptr_curseVoteProgress.latestVoteToCurseByCurseVoteAddr[curseVoteAddr];
      bytes28 prevCursesHash;
      if (
        (curseVoteAddr != OWNER_CURSE_VOTE_ADDR && cvch.configVersion < configVersion)
          || cvch.cursesHash == NO_VOTES_CURSES_HASH
      ) {
        // if owner's first vote, or if voter's first vote in this config version
        prevCursesHash = NO_VOTES_CURSES_HASH; // start hashchain from scratch, explicit
        sptr_curseVoteProgress.accumulatedWeight += sptr_curserRecord.weight;
      } else {
        // we've already accounted for the weight
        prevCursesHash = cvch.cursesHash;
      }
      sptr_curseVoteProgress.latestVoteToCurseByCurseVoteAddr[curseVoteAddr] = cvch =
        ConfigVersionAndCursesHash({configVersion: configVersion, cursesHash: _cursesHash(prevCursesHash, curseId)});
      emit VotedToCurse(
        configVersion,
        curseVoteAddr,
        subject,
        curseId,
        sptr_curserRecord.weight,
        _blockTimestamp(),
        cvch.cursesHash,
        sptr_curseVoteProgress.accumulatedWeight
      );

      if (
        prevCursesHash == NO_VOTES_CURSES_HASH && !sptr_curseVoteProgress.curseActive
          && _shouldCurseBeActive(sptr_curseVoteProgress)
      ) {
        sptr_curseVoteProgress.curseActive = true;
        ++s_curseHotVars.numSubjectsCursed;
        emit Cursed(configVersion, subject, _blockTimestamp());
      }

      s_recordedCurseRelatedOps.push(
        RecordedCurseRelatedOp({
          tag: RecordedCurseRelatedOpTag.VoteToCurse,
          cursed: sptr_curseVoteProgress.curseActive,
          curseVoteAddr: curseVoteAddr,
          curseId: curseId,
          subject: subject,
          blockTimestamp: _blockTimestamp()
        })
      );
    }
  }

  /// @notice Enables the owner to immediately have the system enter the cursed state.
  function ownerCurse(bytes16 curseId, bytes16[] memory subjects) external onlyOwner {
    address curseVoteAddr = OWNER_CURSE_VOTE_ADDR;
    CurserRecord storage sptr_curserRecord = s_curserRecords[curseVoteAddr];
    // no need to check if sptr_curserRecord.active, we must have the onlyOwner modifier
    _authorizedVoteToCurse(curseVoteAddr, curseId, subjects, sptr_curserRecord);
  }

  // Set curseVoteAddr=LIFT_CURSE_VOTE_ADDR, cursesHash=bytes28(0), to reset curseActive if it can be reset. Useful if
  // all voters have unvoted to curse on their own and the curse can now be lifted without any individual votes that can
  // be unvoted.
  // solhint-disable-next-line gas-struct-packing
  struct OwnerUnvoteToCurseRequest {
    address curseVoteAddr;
    UnvoteToCurseRequest unit;
    bool forceUnvote;
  }

  /// @notice Enables the owner to remove curse votes. After the curse votes are removed,
  /// this function will check whether the curse is still valid and restore the uncursed state if possible.
  /// This function also enables the owner to lift a curse created through ownerCurse.
  function ownerUnvoteToCurse(OwnerUnvoteToCurseRequest[] memory ownerUnvoteToCurseRequests) external onlyOwner {
    bool anyCurseWasLifted = false;
    bool anyVoteWasUnvoted = false;
    uint32 configVersion = s_versionedConfig.configVersion;
    for (uint256 i = 0; i < ownerUnvoteToCurseRequests.length; ++i) {
      OwnerUnvoteToCurseRequest memory req = ownerUnvoteToCurseRequests[i];
      CurseVoteProgress storage sptr_curseVoteProgress = _getUpToDateCurseVoteProgress(configVersion, req.unit.subject);
      (bool unvoted, bool curseLifted) = _authorizedUnvoteToCurse(
        Privilege.Owner,
        configVersion,
        req.curseVoteAddr,
        req.unit,
        req.forceUnvote,
        s_curserRecords[req.curseVoteAddr],
        sptr_curseVoteProgress
      );
      anyVoteWasUnvoted = anyVoteWasUnvoted || unvoted;
      anyCurseWasLifted = anyCurseWasLifted || curseLifted;
    }

    if (anyCurseWasLifted) {
      // Invalidate all in-progress votes to bless or curse by bumping the config version.
      // They might have been based on false information about the source chain
      // (e.g. in case of a finality violation).
      _setConfig(s_versionedConfig.config);
    }

    if (!(anyVoteWasUnvoted || anyCurseWasLifted)) {
      revert UnvoteToCurseNoop();
    }
  }

  function setConfig(Config memory config) external onlyOwner {
    _setConfig(config);
  }

  /// @notice Any tagged root with a commit store included in this array will be considered automatically blessed.
  function getPermaBlessedCommitStores() external view returns (address[] memory) {
    return s_permaBlessedCommitStores.values();
  }

  /// @notice The ordering of parameters is important. First come the commit stores to remove, then the commit stores to
  /// add.
  function ownerRemoveThenAddPermaBlessedCommitStores(
    address[] memory removes,
    address[] memory adds
  ) external onlyOwner {
    for (uint256 i = 0; i < removes.length; ++i) {
      if (s_permaBlessedCommitStores.remove(removes[i])) {
        emit PermaBlessedCommitStoreRemoved(removes[i]);
      }
    }
    for (uint256 i = 0; i < adds.length; ++i) {
      if (s_permaBlessedCommitStores.add(adds[i])) {
        emit PermaBlessedCommitStoreAdded(adds[i]);
      }
    }
  }

  /// @inheritdoc IRMN
  function isBlessed(IRMN.TaggedRoot calldata taggedRoot) external view returns (bool) {
    return s_blessVoteProgressByTaggedRootHash[_taggedRootHash(taggedRoot)].weightThresholdMet
      || s_permaBlessedCommitStores.contains(taggedRoot.commitStore);
  }

  /// @inheritdoc IRMN
  function isCursed() external view returns (bool) {
    if (s_curseHotVars.numSubjectsCursed == 0) {
      return false; // happy path costs a single SLOAD
    } else {
      return s_potentiallyOutdatedCurseVoteProgressBySubject[GLOBAL_CURSE_SUBJECT].curseActive
        || s_potentiallyOutdatedCurseVoteProgressBySubject[LEGACY_CURSE_SUBJECT].curseActive;
    }
  }

  /// @inheritdoc IRMN
  function isCursed(bytes16 subject) public view returns (bool) {
    if (s_curseHotVars.numSubjectsCursed == 0) {
      return false; // happy path costs a single SLOAD
    } else {
      return s_potentiallyOutdatedCurseVoteProgressBySubject[GLOBAL_CURSE_SUBJECT].curseActive
        || s_potentiallyOutdatedCurseVoteProgressBySubject[subject].curseActive;
    }
  }

  /// @notice Config version might be incremented for many reasons, including
  /// the lifting of a curse, or a regular config change.
  function getConfigDetails() external view returns (uint32 version, uint32 blockNumber, Config memory config) {
    version = s_versionedConfig.configVersion;
    blockNumber = s_versionedConfig.blockNumber;
    config = s_versionedConfig.config;
  }

  /// @return blessVoteAddrs addresses of voters, will be empty if voting took place with an older config version
  /// @return accumulatedWeight sum of weights of voters, will be zero if voting took place with an older config version
  /// @return blessed will be accurate regardless of when voting took place
  /// @dev This is a helper method for offchain code so efficiency is not really a concern.
  function getBlessProgress(IRMN.TaggedRoot calldata taggedRoot)
    external
    view
    returns (address[] memory blessVoteAddrs, uint16 accumulatedWeight, bool blessed)
  {
    bytes32 taggedRootHash = _taggedRootHash(taggedRoot);
    BlessVoteProgress memory progress = s_blessVoteProgressByTaggedRootHash[taggedRootHash];
    blessed = progress.weightThresholdMet;
    if (progress.configVersion == s_versionedConfig.configVersion) {
      accumulatedWeight = progress.accumulatedWeight;
      uint200 bitmap = progress.voterBitmap;
      blessVoteAddrs = new address[](_bitmapCount(bitmap));
      Voter[] memory voters = s_versionedConfig.config.voters;
      uint256 j = 0;
      for (uint8 i = 0; i < voters.length; ++i) {
        if (_bitmapGet(bitmap, i)) {
          blessVoteAddrs[j] = voters[i].blessVoteAddr;
          ++j;
        }
      }
    }
  }

  /// @return curseVoteAddrs the curseVoteAddr of each voter with an active vote to curse
  /// @return cursesHashes the i-th value is the curses hash of curseVoteAddrs[i]
  /// @return accumulatedWeight the accumulated weight of all voters with an active vote to curse who are part of the
  /// current config
  /// @return cursed might be true even if the owner has no active vote and accumulatedWeight < curseWeightThreshold,
  /// due to a retained curse from a prior config
  /// @dev This is a helper method for offchain code so efficiency is not really a concern.
  function getCurseProgress(bytes16 subject)
    external
    view
    returns (address[] memory curseVoteAddrs, bytes28[] memory cursesHashes, uint16 accumulatedWeight, bool cursed)
  {
    uint32 configVersion = s_versionedConfig.configVersion;
    Config memory config = s_versionedConfig.config;
    // Can't use _getUpToDateCurseVoteProgress here because we can't call a non-view function from within a view.
    // So we get to repeat some accounting.
    CurseVoteProgress storage outdatedCurseVoteProgress = s_potentiallyOutdatedCurseVoteProgressBySubject[subject];

    cursed = outdatedCurseVoteProgress.curseActive;

    // See _getUpToDateCurseVoteProgress for more context.
    bool shouldCountVotesFromOlderConfigs = outdatedCurseVoteProgress.configVersion < configVersion && cursed;

    // A play in two acts, because we can't push to arrays in memory, so we need to precompute the array's length.
    // First act: we count the number of cursers, i.e., voters with active vote.
    // Second act: push the cursers to the arrays, sum their weights.

    uint256 numCursers = 0; // we reuse this variable for writing to perserve stack space
    accumulatedWeight = 0;
    for (uint256 act = 1; act <= 2; ++act) {
      uint256 i = config.voters.length; // not config.voters.length-1 to account for the owner
      while (true) {
        address curseVoteAddr;
        uint8 weight;
        if (i < config.voters.length) {
          curseVoteAddr = config.voters[i].curseVoteAddr;
          weight = config.voters[i].curseWeight;
        } else {
          // Allows us to include the owner's vote and curses hash in the result.
          curseVoteAddr = OWNER_CURSE_VOTE_ADDR;
          weight = 0;
        }

        ConfigVersionAndCursesHash memory cvch =
          outdatedCurseVoteProgress.latestVoteToCurseByCurseVoteAddr[curseVoteAddr];
        bool hasActiveVote = (
          shouldCountVotesFromOlderConfigs || cvch.configVersion == configVersion
            || curseVoteAddr == OWNER_CURSE_VOTE_ADDR
        ) && cvch.cursesHash != NO_VOTES_CURSES_HASH;
        if (hasActiveVote) {
          if (act == 1) {
            ++numCursers;
          } else if (act == 2) {
            accumulatedWeight += weight;
            --numCursers;
            curseVoteAddrs[numCursers] = curseVoteAddr;
            cursesHashes[numCursers] = cvch.cursesHash;
          } else {
            // solhint-disable-next-line gas-custom-errors, reason-string
            revert(); // assumption violation
          }
        }

        if (i > 0) {
          --i;
        } else {
          break;
        }
      }

      if (act == 1) {
        // We are done counting at this point, initialize the arrays for the second act that follows immediately after.
        curseVoteAddrs = new address[](numCursers);
        cursesHashes = new bytes28[](numCursers);
      }
    }
  }

  /// @notice Returns the number of subjects that are currently cursed.
  function getCursedSubjectsCount() external view returns (uint256) {
    return s_curseHotVars.numSubjectsCursed;
  }

  /// @dev This is a helper method for offchain code to know what arguments to use for getRecordedCurseRelatedOps.
  function getRecordedCurseRelatedOpsCount() external view returns (uint256) {
    return s_recordedCurseRelatedOps.length;
  }

  /// @dev This is a helper method for offchain code so efficiency is not really a concern.
  /// @dev Returns s_recordedCurseRelatedOps[offset:offset+limit].
  function getRecordedCurseRelatedOps(
    uint256 offset,
    uint256 limit
  ) external view returns (RecordedCurseRelatedOp[] memory) {
    uint256 pageLen;
    if (offset + limit <= s_recordedCurseRelatedOps.length) {
      pageLen = limit;
    } else if (offset < s_recordedCurseRelatedOps.length) {
      pageLen = s_recordedCurseRelatedOps.length - offset;
    } else {
      pageLen = 0;
    }
    RecordedCurseRelatedOp[] memory page = new RecordedCurseRelatedOp[](pageLen);
    for (uint256 i = 0; i < pageLen; ++i) {
      page[i] = s_recordedCurseRelatedOps[offset + i];
    }
    return page;
  }

  function _validateConfig(Config memory config) internal pure returns (bool) {
    if (
      config.voters.length == 0 || config.voters.length > MAX_NUM_VOTERS || config.blessWeightThreshold == 0
        || config.curseWeightThreshold == 0
    ) {
      return false;
    }

    uint256 totalBlessWeight = 0;
    uint256 totalCurseWeight = 0;
    address[] memory allAddrs = new address[](2 * config.voters.length);
    for (uint256 i = 0; i < config.voters.length; ++i) {
      Voter memory voter = config.voters[i];
      // The owner can always curse using the ownerCurse method, and is not supposed to be included in the voters list.
      // Even though the intent is for the actual owner address to NOT be included in the voters list, we don't
      // explicitly disallow curseVoteAddr == owner() here. Even if we did, the owner could transfer ownership of the
      // contract, and so we couldn't guarantee that the owner is not eventually included in the voters list.
      if (
        voter.blessVoteAddr == address(0) || voter.curseVoteAddr == address(0)
          || voter.curseVoteAddr == LIFT_CURSE_VOTE_ADDR || voter.curseVoteAddr == OWNER_CURSE_VOTE_ADDR
          || (voter.blessWeight == 0 && voter.curseWeight == 0)
      ) {
        return false;
      }
      allAddrs[2 * i + 0] = voter.blessVoteAddr;
      allAddrs[2 * i + 1] = voter.curseVoteAddr;
      totalBlessWeight += voter.blessWeight;
      totalCurseWeight += voter.curseWeight;
    }
    for (uint256 i = 0; i < allAddrs.length; ++i) {
      address allAddrs_i = allAddrs[i];
      for (uint256 j = i + 1; j < allAddrs.length; ++j) {
        if (allAddrs_i == allAddrs[j]) {
          return false;
        }
      }
    }

    return totalBlessWeight >= config.blessWeightThreshold && totalCurseWeight >= config.curseWeightThreshold;
  }

  function _setConfig(Config memory config) private {
    if (!_validateConfig(config)) revert InvalidConfig();

    // We can't directly assign s_versionedConfig.config to config
    // because copying a memory array into storage is not supported.
    {
      s_versionedConfig.config.blessWeightThreshold = config.blessWeightThreshold;
      s_versionedConfig.config.curseWeightThreshold = config.curseWeightThreshold;
      while (s_versionedConfig.config.voters.length != 0) {
        Voter memory voter = s_versionedConfig.config.voters[s_versionedConfig.config.voters.length - 1];
        delete s_blesserRecords[voter.blessVoteAddr];
        delete s_curserRecords[voter.curseVoteAddr]; // usedCurseIds mapping is retained, as intended
        s_versionedConfig.config.voters.pop();
      }
      for (uint256 i = 0; i < config.voters.length; ++i) {
        s_versionedConfig.config.voters.push(config.voters[i]);
      }
    }

    ++s_versionedConfig.configVersion;
    uint32 configVersion = s_versionedConfig.configVersion;

    for (uint8 i = 0; i < config.voters.length; ++i) {
      Voter memory voter = config.voters[i];
      s_blesserRecords[voter.blessVoteAddr] =
        BlesserRecord({configVersion: configVersion, index: i, weight: voter.blessWeight});
      {
        CurserRecord storage sptr_curserRecord = s_curserRecords[voter.curseVoteAddr];
        // Solidity will not let us initialize as CurserRecord({...}) due to the nested mapping
        sptr_curserRecord.active = true;
        sptr_curserRecord.weight = voter.curseWeight;
      }
    }
    {
      // Initialize the owner's CurserRecord
      // We could in principle perform this initialization once in the constructor instead, and save a small bit of gas.
      // But configuration changes are relatively infrequent, and keeping the initialization here makes the contract's
      // correctness easier to reason about.
      CurserRecord storage sptr_ownerCurserRecord = s_curserRecords[OWNER_CURSE_VOTE_ADDR];
      sptr_ownerCurserRecord.active = true; // Assumed by vote/unvote-to-curse logic
      sptr_ownerCurserRecord.weight = 0; // Assumed by vote/unvote-to-curse logic
    }
    s_versionedConfig.blockNumber = uint32(block.number);
    emit ConfigSet(configVersion, config);

    s_recordedCurseRelatedOps.push(
      RecordedCurseRelatedOp({
        tag: RecordedCurseRelatedOpTag.SetConfig,
        blockTimestamp: _blockTimestamp(),
        cursed: false,
        curseVoteAddr: address(0),
        curseId: bytes16(0),
        subject: bytes16(0)
      })
    );
  }
}

File 2 of 8 : ITypeAndVersion.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

interface ITypeAndVersion {
  function typeAndVersion() external pure returns (string memory);
}

File 3 of 8 : IRMN.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

/// @notice This interface contains the only RMN-related functions that might be used on-chain by other CCIP contracts.
interface IRMN {
  /// @notice A Merkle root tagged with the address of the commit store contract it is destined for.
  struct TaggedRoot {
    address commitStore;
    bytes32 root;
  }

  /// @notice Callers MUST NOT cache the return value as a blessed tagged root could become unblessed.
  function isBlessed(TaggedRoot calldata taggedRoot) external view returns (bool);

  /// @notice Iff there is an active global or legacy curse, this function returns true.
  function isCursed() external view returns (bool);

  /// @notice Iff there is an active global curse, or an active curse for `subject`, this function returns true.
  /// @param subject To check whether a particular chain is cursed, set to bytes16(uint128(chainSelector)).
  function isCursed(bytes16 subject) external view returns (bool);
}

File 4 of 8 : OwnerIsCreator.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

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

/// @title The OwnerIsCreator contract
/// @notice A contract with helpers for basic contract ownership.
contract OwnerIsCreator is ConfirmedOwner {
  constructor() ConfirmedOwner(msg.sender) {}
}

File 5 of 8 : EnumerableSet.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (utils/structs/EnumerableSet.sol)
// This file was procedurally generated from scripts/generate/templates/EnumerableSet.js.

pragma solidity ^0.8.0;

/**
 * @dev Library for managing
 * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive
 * types.
 *
 * Sets have the following properties:
 *
 * - Elements are added, removed, and checked for existence in constant time
 * (O(1)).
 * - Elements are enumerated in O(n). No guarantees are made on the ordering.
 *
 * ```
 * contract Example {
 *     // Add the library methods
 *     using EnumerableSet for EnumerableSet.AddressSet;
 *
 *     // Declare a set state variable
 *     EnumerableSet.AddressSet private mySet;
 * }
 * ```
 *
 * As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`)
 * and `uint256` (`UintSet`) are supported.
 *
 * [WARNING]
 * ====
 * Trying to delete such a structure from storage will likely result in data corruption, rendering the structure
 * unusable.
 * See https://github.com/ethereum/solidity/pull/11843[ethereum/solidity#11843] for more info.
 *
 * In order to clean an EnumerableSet, you can either remove all elements one by one or create a fresh instance using an
 * array of EnumerableSet.
 * ====
 */
library EnumerableSet {
  // To implement this library for multiple types with as little code
  // repetition as possible, we write it in terms of a generic Set type with
  // bytes32 values.
  // The Set implementation uses private functions, and user-facing
  // implementations (such as AddressSet) are just wrappers around the
  // underlying Set.
  // This means that we can only create new EnumerableSets for types that fit
  // in bytes32.

  struct Set {
    // Storage of set values
    bytes32[] _values;
    // Position of the value in the `values` array, plus 1 because index 0
    // means a value is not in the set.
    mapping(bytes32 => uint256) _indexes;
  }

  /**
   * @dev Add a value to a set. O(1).
   *
   * Returns true if the value was added to the set, that is if it was not
   * already present.
   */
  function _add(Set storage set, bytes32 value) private returns (bool) {
    if (!_contains(set, value)) {
      set._values.push(value);
      // The value is stored at length-1, but we add 1 to all indexes
      // and use 0 as a sentinel value
      set._indexes[value] = set._values.length;
      return true;
    } else {
      return false;
    }
  }

  /**
   * @dev Removes a value from a set. O(1).
   *
   * Returns true if the value was removed from the set, that is if it was
   * present.
   */
  function _remove(Set storage set, bytes32 value) private returns (bool) {
    // We read and store the value's index to prevent multiple reads from the same storage slot
    uint256 valueIndex = set._indexes[value];

    if (valueIndex != 0) {
      // Equivalent to contains(set, value)
      // To delete an element from the _values array in O(1), we swap the element to delete with the last one in
      // the array, and then remove the last element (sometimes called as 'swap and pop').
      // This modifies the order of the array, as noted in {at}.

      uint256 toDeleteIndex = valueIndex - 1;
      uint256 lastIndex = set._values.length - 1;

      if (lastIndex != toDeleteIndex) {
        bytes32 lastValue = set._values[lastIndex];

        // Move the last value to the index where the value to delete is
        set._values[toDeleteIndex] = lastValue;
        // Update the index for the moved value
        set._indexes[lastValue] = valueIndex; // Replace lastValue's index to valueIndex
      }

      // Delete the slot where the moved value was stored
      set._values.pop();

      // Delete the index for the deleted slot
      delete set._indexes[value];

      return true;
    } else {
      return false;
    }
  }

  /**
   * @dev Returns true if the value is in the set. O(1).
   */
  function _contains(Set storage set, bytes32 value) private view returns (bool) {
    return set._indexes[value] != 0;
  }

  /**
   * @dev Returns the number of values on the set. O(1).
   */
  function _length(Set storage set) private view returns (uint256) {
    return set._values.length;
  }

  /**
   * @dev Returns the value stored at position `index` in the set. O(1).
   *
   * Note that there are no guarantees on the ordering of values inside the
   * array, and it may change when more values are added or removed.
   *
   * Requirements:
   *
   * - `index` must be strictly less than {length}.
   */
  function _at(Set storage set, uint256 index) private view returns (bytes32) {
    return set._values[index];
  }

  /**
   * @dev Return the entire set in an array
   *
   * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
   * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
   * this function has an unbounded cost, and using it as part of a state-changing function may render the function
   * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
   */
  function _values(Set storage set) private view returns (bytes32[] memory) {
    return set._values;
  }

  // Bytes32Set

  struct Bytes32Set {
    Set _inner;
  }

  /**
   * @dev Add a value to a set. O(1).
   *
   * Returns true if the value was added to the set, that is if it was not
   * already present.
   */
  function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {
    return _add(set._inner, value);
  }

  /**
   * @dev Removes a value from a set. O(1).
   *
   * Returns true if the value was removed from the set, that is if it was
   * present.
   */
  function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) {
    return _remove(set._inner, value);
  }

  /**
   * @dev Returns true if the value is in the set. O(1).
   */
  function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {
    return _contains(set._inner, value);
  }

  /**
   * @dev Returns the number of values in the set. O(1).
   */
  function length(Bytes32Set storage set) internal view returns (uint256) {
    return _length(set._inner);
  }

  /**
   * @dev Returns the value stored at position `index` in the set. O(1).
   *
   * Note that there are no guarantees on the ordering of values inside the
   * array, and it may change when more values are added or removed.
   *
   * Requirements:
   *
   * - `index` must be strictly less than {length}.
   */
  function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {
    return _at(set._inner, index);
  }

  /**
   * @dev Return the entire set in an array
   *
   * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
   * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
   * this function has an unbounded cost, and using it as part of a state-changing function may render the function
   * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
   */
  function values(Bytes32Set storage set) internal view returns (bytes32[] memory) {
    bytes32[] memory store = _values(set._inner);
    bytes32[] memory result;

    /// @solidity memory-safe-assembly
    assembly {
      result := store
    }

    return result;
  }

  // AddressSet

  struct AddressSet {
    Set _inner;
  }

  /**
   * @dev Add a value to a set. O(1).
   *
   * Returns true if the value was added to the set, that is if it was not
   * already present.
   */
  function add(AddressSet storage set, address value) internal returns (bool) {
    return _add(set._inner, bytes32(uint256(uint160(value))));
  }

  /**
   * @dev Removes a value from a set. O(1).
   *
   * Returns true if the value was removed from the set, that is if it was
   * present.
   */
  function remove(AddressSet storage set, address value) internal returns (bool) {
    return _remove(set._inner, bytes32(uint256(uint160(value))));
  }

  /**
   * @dev Returns true if the value is in the set. O(1).
   */
  function contains(AddressSet storage set, address value) internal view returns (bool) {
    return _contains(set._inner, bytes32(uint256(uint160(value))));
  }

  /**
   * @dev Returns the number of values in the set. O(1).
   */
  function length(AddressSet storage set) internal view returns (uint256) {
    return _length(set._inner);
  }

  /**
   * @dev Returns the value stored at position `index` in the set. O(1).
   *
   * Note that there are no guarantees on the ordering of values inside the
   * array, and it may change when more values are added or removed.
   *
   * Requirements:
   *
   * - `index` must be strictly less than {length}.
   */
  function at(AddressSet storage set, uint256 index) internal view returns (address) {
    return address(uint160(uint256(_at(set._inner, index))));
  }

  /**
   * @dev Return the entire set in an array
   *
   * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
   * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
   * this function has an unbounded cost, and using it as part of a state-changing function may render the function
   * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
   */
  function values(AddressSet storage set) internal view returns (address[] memory) {
    bytes32[] memory store = _values(set._inner);
    address[] memory result;

    /// @solidity memory-safe-assembly
    assembly {
      result := store
    }

    return result;
  }

  // UintSet

  struct UintSet {
    Set _inner;
  }

  /**
   * @dev Add a value to a set. O(1).
   *
   * Returns true if the value was added to the set, that is if it was not
   * already present.
   */
  function add(UintSet storage set, uint256 value) internal returns (bool) {
    return _add(set._inner, bytes32(value));
  }

  /**
   * @dev Removes a value from a set. O(1).
   *
   * Returns true if the value was removed from the set, that is if it was
   * present.
   */
  function remove(UintSet storage set, uint256 value) internal returns (bool) {
    return _remove(set._inner, bytes32(value));
  }

  /**
   * @dev Returns true if the value is in the set. O(1).
   */
  function contains(UintSet storage set, uint256 value) internal view returns (bool) {
    return _contains(set._inner, bytes32(value));
  }

  /**
   * @dev Returns the number of values in the set. O(1).
   */
  function length(UintSet storage set) internal view returns (uint256) {
    return _length(set._inner);
  }

  /**
   * @dev Returns the value stored at position `index` in the set. O(1).
   *
   * Note that there are no guarantees on the ordering of values inside the
   * array, and it may change when more values are added or removed.
   *
   * Requirements:
   *
   * - `index` must be strictly less than {length}.
   */
  function at(UintSet storage set, uint256 index) internal view returns (uint256) {
    return uint256(_at(set._inner, index));
  }

  /**
   * @dev Return the entire set in an array
   *
   * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
   * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
   * this function has an unbounded cost, and using it as part of a state-changing function may render the function
   * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
   */
  function values(UintSet storage set) internal view returns (uint256[] memory) {
    bytes32[] memory store = _values(set._inner);
    uint256[] memory result;

    /// @solidity memory-safe-assembly
    assembly {
      result := store
    }

    return result;
  }
}

File 6 of 8 : ConfirmedOwner.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

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

/// @title The ConfirmedOwner contract
/// @notice A contract with helpers for basic contract ownership.
contract ConfirmedOwner is ConfirmedOwnerWithProposal {
  constructor(address newOwner) ConfirmedOwnerWithProposal(newOwner, address(0)) {}
}

File 7 of 8 : ConfirmedOwnerWithProposal.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

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

/// @title The ConfirmedOwner contract
/// @notice A contract with helpers for basic contract ownership.
contract ConfirmedOwnerWithProposal is IOwnable {
  address private s_owner;
  address private s_pendingOwner;

  event OwnershipTransferRequested(address indexed from, address indexed to);
  event OwnershipTransferred(address indexed from, address indexed to);

  constructor(address newOwner, address pendingOwner) {
    // solhint-disable-next-line gas-custom-errors
    require(newOwner != address(0), "Cannot set owner to zero");

    s_owner = newOwner;
    if (pendingOwner != address(0)) {
      _transferOwnership(pendingOwner);
    }
  }

  /// @notice Allows an owner to begin transferring ownership to a new address.
  function transferOwnership(address to) public override onlyOwner {
    _transferOwnership(to);
  }

  /// @notice Allows an ownership transfer to be completed by the recipient.
  function acceptOwnership() external override {
    // solhint-disable-next-line gas-custom-errors
    require(msg.sender == s_pendingOwner, "Must be proposed owner");

    address oldOwner = s_owner;
    s_owner = msg.sender;
    s_pendingOwner = address(0);

    emit OwnershipTransferred(oldOwner, msg.sender);
  }

  /// @notice Get the current owner
  function owner() public view override returns (address) {
    return s_owner;
  }

  /// @notice validate, transfer ownership, and emit relevant events
  function _transferOwnership(address to) private {
    // solhint-disable-next-line gas-custom-errors
    require(to != msg.sender, "Cannot transfer to self");

    s_pendingOwner = to;

    emit OwnershipTransferRequested(s_owner, to);
  }

  /// @notice validate access
  function _validateOwnership() internal view {
    // solhint-disable-next-line gas-custom-errors
    require(msg.sender == s_owner, "Only callable by owner");
  }

  /// @notice Reverts if called by anyone other than the contract owner.
  modifier onlyOwner() {
    _validateOwnership();
    _;
  }
}

File 8 of 8 : IOwnable.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

interface IOwnable {
  function owner() external returns (address);

  function transferOwnership(address recipient) external;

  function acceptOwnership() external;
}

Settings
{
  "remappings": [
    "forge-std/=src/v0.8/vendor/forge-std/src/",
    "@openzeppelin/=node_modules/@openzeppelin/",
    "@arbitrum/=node_modules/@arbitrum/",
    "hardhat/=node_modules/hardhat/",
    "@eth-optimism/=node_modules/@eth-optimism/",
    "@scroll-tech/=node_modules/@scroll-tech/",
    "@offchainlabs/=node_modules/@offchainlabs/"
  ],
  "optimizer": {
    "enabled": true,
    "runs": 26000
  },
  "metadata": {
    "useLiteralContent": false,
    "bytecodeHash": "none",
    "appendCBOR": true
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "evmVersion": "paris",
  "viaIR": false,
  "libraries": {}
}

Contract ABI

[{"inputs":[{"components":[{"components":[{"internalType":"address","name":"blessVoteAddr","type":"address"},{"internalType":"address","name":"curseVoteAddr","type":"address"},{"internalType":"uint8","name":"blessWeight","type":"uint8"},{"internalType":"uint8","name":"curseWeight","type":"uint8"}],"internalType":"struct RMN.Voter[]","name":"voters","type":"tuple[]"},{"internalType":"uint16","name":"blessWeightThreshold","type":"uint16"},{"internalType":"uint16","name":"curseWeightThreshold","type":"uint16"}],"internalType":"struct RMN.Config","name":"config","type":"tuple"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"InvalidConfig","type":"error"},{"inputs":[{"internalType":"address","name":"voter","type":"address"},{"internalType":"bytes16","name":"curseId","type":"bytes16"}],"name":"ReusedCurseId","type":"error"},{"inputs":[],"name":"SubjectsMustBeStrictlyIncreasing","type":"error"},{"inputs":[{"internalType":"address","name":"voter","type":"address"}],"name":"UnauthorizedVoter","type":"error"},{"inputs":[],"name":"UnvoteToCurseNoop","type":"error"},{"inputs":[],"name":"VoteToBlessForbiddenDuringActiveGlobalCurse","type":"error"},{"inputs":[],"name":"VoteToBlessNoop","type":"error"},{"inputs":[],"name":"VoteToCurseNoop","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint32","name":"configVersion","type":"uint32"},{"indexed":true,"internalType":"address","name":"voter","type":"address"},{"components":[{"internalType":"address","name":"commitStore","type":"address"},{"internalType":"bytes32","name":"root","type":"bytes32"}],"indexed":false,"internalType":"struct IRMN.TaggedRoot","name":"taggedRoot","type":"tuple"}],"name":"AlreadyBlessed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint32","name":"configVersion","type":"uint32"},{"indexed":true,"internalType":"address","name":"voter","type":"address"},{"components":[{"internalType":"address","name":"commitStore","type":"address"},{"internalType":"bytes32","name":"root","type":"bytes32"}],"indexed":false,"internalType":"struct IRMN.TaggedRoot","name":"taggedRoot","type":"tuple"}],"name":"AlreadyVotedToBless","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint32","name":"configVersion","type":"uint32"},{"components":[{"components":[{"internalType":"address","name":"blessVoteAddr","type":"address"},{"internalType":"address","name":"curseVoteAddr","type":"address"},{"internalType":"uint8","name":"blessWeight","type":"uint8"},{"internalType":"uint8","name":"curseWeight","type":"uint8"}],"internalType":"struct RMN.Voter[]","name":"voters","type":"tuple[]"},{"internalType":"uint16","name":"blessWeightThreshold","type":"uint16"},{"internalType":"uint16","name":"curseWeightThreshold","type":"uint16"}],"indexed":false,"internalType":"struct RMN.Config","name":"config","type":"tuple"}],"name":"ConfigSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bytes16","name":"subject","type":"bytes16"}],"name":"CurseLifted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint32","name":"configVersion","type":"uint32"},{"indexed":false,"internalType":"bytes16","name":"subject","type":"bytes16"},{"indexed":false,"internalType":"uint64","name":"blockTimestamp","type":"uint64"}],"name":"Cursed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"}],"name":"OwnershipTransferRequested","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"commitStore","type":"address"}],"name":"PermaBlessedCommitStoreAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"commitStore","type":"address"}],"name":"PermaBlessedCommitStoreRemoved","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"voter","type":"address"},{"indexed":false,"internalType":"bytes16","name":"subject","type":"bytes16"},{"indexed":false,"internalType":"bytes28","name":"onchainCursesHash","type":"bytes28"},{"indexed":false,"internalType":"bytes28","name":"cursesHash","type":"bytes28"}],"name":"SkippedUnvoteToCurse","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint32","name":"configVersion","type":"uint32"},{"components":[{"internalType":"address","name":"commitStore","type":"address"},{"internalType":"bytes32","name":"root","type":"bytes32"}],"indexed":false,"internalType":"struct IRMN.TaggedRoot","name":"taggedRoot","type":"tuple"},{"indexed":false,"internalType":"bool","name":"wasBlessed","type":"bool"}],"name":"TaggedRootBlessVotesReset","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint32","name":"configVersion","type":"uint32"},{"components":[{"internalType":"address","name":"commitStore","type":"address"},{"internalType":"bytes32","name":"root","type":"bytes32"}],"indexed":false,"internalType":"struct IRMN.TaggedRoot","name":"taggedRoot","type":"tuple"},{"indexed":false,"internalType":"uint16","name":"accumulatedWeight","type":"uint16"}],"name":"TaggedRootBlessed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint32","name":"configVersion","type":"uint32"},{"indexed":true,"internalType":"address","name":"voter","type":"address"},{"indexed":false,"internalType":"bytes16","name":"subject","type":"bytes16"},{"indexed":false,"internalType":"uint8","name":"weight","type":"uint8"},{"indexed":false,"internalType":"bytes28","name":"cursesHash","type":"bytes28"},{"indexed":false,"internalType":"uint16","name":"remainingAccumulatedWeight","type":"uint16"}],"name":"UnvotedToCurse","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint32","name":"configVersion","type":"uint32"},{"indexed":true,"internalType":"address","name":"voter","type":"address"},{"components":[{"internalType":"address","name":"commitStore","type":"address"},{"internalType":"bytes32","name":"root","type":"bytes32"}],"indexed":false,"internalType":"struct IRMN.TaggedRoot","name":"taggedRoot","type":"tuple"},{"indexed":false,"internalType":"uint8","name":"weight","type":"uint8"}],"name":"VotedToBless","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint32","name":"configVersion","type":"uint32"},{"indexed":true,"internalType":"address","name":"voter","type":"address"},{"indexed":false,"internalType":"bytes16","name":"subject","type":"bytes16"},{"indexed":false,"internalType":"bytes16","name":"curseId","type":"bytes16"},{"indexed":false,"internalType":"uint8","name":"weight","type":"uint8"},{"indexed":false,"internalType":"uint64","name":"blockTimestamp","type":"uint64"},{"indexed":false,"internalType":"bytes28","name":"cursesHash","type":"bytes28"},{"indexed":false,"internalType":"uint16","name":"accumulatedWeight","type":"uint16"}],"name":"VotedToCurse","type":"event"},{"inputs":[],"name":"acceptOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"commitStore","type":"address"},{"internalType":"bytes32","name":"root","type":"bytes32"}],"internalType":"struct IRMN.TaggedRoot","name":"taggedRoot","type":"tuple"}],"name":"getBlessProgress","outputs":[{"internalType":"address[]","name":"blessVoteAddrs","type":"address[]"},{"internalType":"uint16","name":"accumulatedWeight","type":"uint16"},{"internalType":"bool","name":"blessed","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getConfigDetails","outputs":[{"internalType":"uint32","name":"version","type":"uint32"},{"internalType":"uint32","name":"blockNumber","type":"uint32"},{"components":[{"components":[{"internalType":"address","name":"blessVoteAddr","type":"address"},{"internalType":"address","name":"curseVoteAddr","type":"address"},{"internalType":"uint8","name":"blessWeight","type":"uint8"},{"internalType":"uint8","name":"curseWeight","type":"uint8"}],"internalType":"struct RMN.Voter[]","name":"voters","type":"tuple[]"},{"internalType":"uint16","name":"blessWeightThreshold","type":"uint16"},{"internalType":"uint16","name":"curseWeightThreshold","type":"uint16"}],"internalType":"struct RMN.Config","name":"config","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes16","name":"subject","type":"bytes16"}],"name":"getCurseProgress","outputs":[{"internalType":"address[]","name":"curseVoteAddrs","type":"address[]"},{"internalType":"bytes28[]","name":"cursesHashes","type":"bytes28[]"},{"internalType":"uint16","name":"accumulatedWeight","type":"uint16"},{"internalType":"bool","name":"cursed","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getCursedSubjectsCount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getPermaBlessedCommitStores","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"offset","type":"uint256"},{"internalType":"uint256","name":"limit","type":"uint256"}],"name":"getRecordedCurseRelatedOps","outputs":[{"components":[{"internalType":"enum RMN.RecordedCurseRelatedOpTag","name":"tag","type":"uint8"},{"internalType":"uint64","name":"blockTimestamp","type":"uint64"},{"internalType":"bool","name":"cursed","type":"bool"},{"internalType":"address","name":"curseVoteAddr","type":"address"},{"internalType":"bytes16","name":"subject","type":"bytes16"},{"internalType":"bytes16","name":"curseId","type":"bytes16"}],"internalType":"struct RMN.RecordedCurseRelatedOp[]","name":"","type":"tuple[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getRecordedCurseRelatedOpsCount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"commitStore","type":"address"},{"internalType":"bytes32","name":"root","type":"bytes32"}],"internalType":"struct IRMN.TaggedRoot","name":"taggedRoot","type":"tuple"}],"name":"isBlessed","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes16","name":"subject","type":"bytes16"}],"name":"isCursed","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isCursed","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes16","name":"curseId","type":"bytes16"},{"internalType":"bytes16[]","name":"subjects","type":"bytes16[]"}],"name":"ownerCurse","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"removes","type":"address[]"},{"internalType":"address[]","name":"adds","type":"address[]"}],"name":"ownerRemoveThenAddPermaBlessedCommitStores","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"commitStore","type":"address"},{"internalType":"bytes32","name":"root","type":"bytes32"}],"internalType":"struct IRMN.TaggedRoot[]","name":"taggedRoots","type":"tuple[]"}],"name":"ownerResetBlessVotes","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"curseVoteAddr","type":"address"},{"components":[{"internalType":"bytes16","name":"subject","type":"bytes16"},{"internalType":"bytes28","name":"cursesHash","type":"bytes28"}],"internalType":"struct RMN.UnvoteToCurseRequest","name":"unit","type":"tuple"},{"internalType":"bool","name":"forceUnvote","type":"bool"}],"internalType":"struct RMN.OwnerUnvoteToCurseRequest[]","name":"ownerUnvoteToCurseRequests","type":"tuple[]"}],"name":"ownerUnvoteToCurse","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"components":[{"internalType":"address","name":"blessVoteAddr","type":"address"},{"internalType":"address","name":"curseVoteAddr","type":"address"},{"internalType":"uint8","name":"blessWeight","type":"uint8"},{"internalType":"uint8","name":"curseWeight","type":"uint8"}],"internalType":"struct RMN.Voter[]","name":"voters","type":"tuple[]"},{"internalType":"uint16","name":"blessWeightThreshold","type":"uint16"},{"internalType":"uint16","name":"curseWeightThreshold","type":"uint16"}],"internalType":"struct RMN.Config","name":"config","type":"tuple"}],"name":"setConfig","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"typeAndVersion","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"bytes16","name":"subject","type":"bytes16"},{"internalType":"bytes28","name":"cursesHash","type":"bytes28"}],"internalType":"struct RMN.UnvoteToCurseRequest[]","name":"unvoteToCurseRequests","type":"tuple[]"}],"name":"unvoteToCurse","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"commitStore","type":"address"},{"internalType":"bytes32","name":"root","type":"bytes32"}],"internalType":"struct IRMN.TaggedRoot[]","name":"taggedRoots","type":"tuple[]"}],"name":"voteToBless","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes16","name":"curseId","type":"bytes16"},{"internalType":"bytes16[]","name":"subjects","type":"bytes16[]"}],"name":"voteToCurse","outputs":[],"stateMutability":"nonpayable","type":"function"}]

60806040523480156200001157600080fd5b506040516200596238038062005962833981016040819052620000349162000aff565b33806000816200008b5760405162461bcd60e51b815260206004820152601860248201527f43616e6e6f7420736574206f776e657220746f207a65726f000000000000000060448201526064015b60405180910390fd5b600080546001600160a01b0319166001600160a01b0384811691909117909155811615620000be57620000be8162000138565b505060408051608081018252600080825260208201819052918101919091526001600160c81b03606082015290506001620000fb81601062000c7d565b82606001516001600160c81b0316901c6001600160c81b0316101562000125576200012562000c99565b506200013181620001e3565b5062000e14565b336001600160a01b03821603620001925760405162461bcd60e51b815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c66000000000000000000604482015260640162000082565b600180546001600160a01b0319166001600160a01b0383811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b620001ee816200071d565b6200020c576040516306b7c75960e31b815260040160405180910390fd5b602081015160038054604084015161ffff908116620100000263ffffffff199092169316929092179190911790555b60025415620003465760028054600091906200025a9060019062000c7d565b815481106200026d576200026d62000caf565b6000918252602080832060408051608081018252600294850290920180546001600160a01b0390811680855260019092015480821685870190815260ff600160a01b8304811687870152600160a81b909204909116606086015291875260058552828720805465ffffffffffff19169055905116855260099092529220805461ffff191690558054919250908062000309576200030962000cc5565b60008281526020902060026000199092019182020180546001600160a01b031916815560010180546001600160b01b03191690559055506200023b565b60005b81515181101562000403578151805160029190839081106200036f576200036f62000caf565b602090810291909101810151825460018181018555600094855293839020825160029092020180546001600160a01b039283166001600160a01b0319909116178155928201519284018054604084015160609094015160ff908116600160a81b0260ff60a81b1991909516600160a01b026001600160a81b0319909216959093169490941793909317161790550162000349565b50600480546000906200041c9063ffffffff1662000cdb565b82546101009290920a63ffffffff8181021990931691831602179091556004541660005b82515160ff821610156200054157600083600001518260ff16815181106200046c576200046c62000caf565b602090810291909101810151604080516060808201835263ffffffff80891683528385015160ff90811684880190815289821685870190815287516001600160a01b03908116600090815260058b5288812097518854945193518616650100000000000260ff60281b199487166401000000000264ffffffffff1990961691909716179390931791909116939093179094558587015190911683526009909552919020805491909201519092166101000261ffff1990921691909117600117905550620005398162000d01565b905062000440565b506001600160a01b0360005260096020527f3bddde647ecb7992f4c710d4e1d59d07614508581f7c22c879a79d28544538a7805461ffff191660011790556004805463ffffffff4381166401000000000263ffffffff60201b1990921691909117909155604051908216907f8c49fda8177c5c8c768eb39634bc6773695c7181711537b822451c12b2efd2a990620005db90859062000d23565b60405180910390a26040805160c08101825260048082526001600160401b03421660208301526000928201839052606082018390526080820183905260a08201839052600c80546001808201835591909452825160029094027fdf6966c971051c3d54ec59162606531493a51404a002842f56009d7e5cf4a8c7018054939490939092849260ff19909216919084908111156200067c576200067c62000dce565b021790555060208201518154604084015160608501516001600160a01b03166a010000000000000000000002600160501b600160f01b031991151569010000000000000000000260ff60481b196001600160401b039095166101000294909416610100600160501b031990931692909217929092179190911617815560808083015160a090930151811c600160801b0292901c919091176001909101555050565b80515160009015806200073257508151516010105b80620007445750602082015161ffff16155b80620007565750604082015161ffff16155b156200076457506000919050565b600080600084600001515160026200077d919062000de4565b6001600160401b0381111562000797576200079762000a24565b604051908082528060200260200182016040528015620007c1578160200160208202803683370190505b50905060005b8551518110156200095457600086600001518281518110620007ed57620007ed62000caf565b6020026020010151905060006001600160a01b031681600001516001600160a01b0316148062000828575060208101516001600160a01b0316155b806200083f575060208101516001600160a01b0316155b8062000858575060208101516001600160a01b03908116145b806200087a5750604081015160ff161580156200087a5750606081015160ff16155b156200088d575060009695505050505050565b8051836200089d84600262000de4565b620008aa90600062000dfe565b81518110620008bd57620008bd62000caf565b6001600160a01b0390921660209283029190910182015281015183620008e584600262000de4565b620008f290600162000dfe565b8151811062000905576200090562000caf565b6001600160a01b03909216602092830291909101909101526040810151620009319060ff168662000dfe565b9450806060015160ff168462000948919062000dfe565b935050600101620007c7565b5060005b8151811015620009f957600082828151811062000979576200097962000caf565b60200260200101519050600082600162000994919062000dfe565b90505b8351811015620009ee57838181518110620009b657620009b662000caf565b60200260200101516001600160a01b0316826001600160a01b031603620009e557506000979650505050505050565b60010162000997565b505060010162000958565b50846020015161ffff16831015801562000a1b5750846040015161ffff168210155b95945050505050565b634e487b7160e01b600052604160045260246000fd5b604051606081016001600160401b038111828210171562000a5f5762000a5f62000a24565b60405290565b604051608081016001600160401b038111828210171562000a5f5762000a5f62000a24565b604051601f8201601f191681016001600160401b038111828210171562000ab55762000ab562000a24565b604052919050565b80516001600160a01b038116811462000ad557600080fd5b919050565b805160ff8116811462000ad557600080fd5b805161ffff8116811462000ad557600080fd5b6000602080838503121562000b1357600080fd5b82516001600160401b038082111562000b2b57600080fd5b8185019150606080838803121562000b4257600080fd5b62000b4c62000a3a565b83518381111562000b5c57600080fd5b8401601f8101891362000b6e57600080fd5b80518481111562000b835762000b8362000a24565b62000b93878260051b0162000a8a565b818152878101955060079190911b82018701908a82111562000bb457600080fd5b918701915b8183101562000c33576080838c03121562000bd45760008081fd5b62000bde62000a65565b62000be98462000abd565b815262000bf889850162000abd565b89820152604062000c0b81860162000ada565b9082015262000c1c84870162000ada565b818701528652948701946080929092019162000bb9565b83525062000c45905084860162000aec565b8582015262000c576040850162000aec565b6040820152979650505050505050565b634e487b7160e01b600052601160045260246000fd5b8181038181111562000c935762000c9362000c67565b92915050565b634e487b7160e01b600052600160045260246000fd5b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052603160045260246000fd5b600063ffffffff80831681810362000cf75762000cf762000c67565b6001019392505050565b600060ff821660ff810362000d1a5762000d1a62000c67565b60010192915050565b60006020808352608080840185516060808588015282825180855260a0890191508684019450600093505b8084101562000da157845180516001600160a01b03908116845288820151168884015260408082015160ff9081169185019190915290840151168383015293860193600193909301929085019062000d4e565b509488015161ffff8116604089015294604089015161ffff811660608a0152955098975050505050505050565b634e487b7160e01b600052602160045260246000fd5b808202811582820484141762000c935762000c9362000c67565b8082018082111562000c935762000c9362000c67565b614b3e8062000e246000396000f3fe608060405234801561001057600080fd5b50600436106101825760003560e01c8063631ec73e116100d8578063979986111161008c578063d927f26711610066578063d927f26714610354578063f2fde38b14610374578063f33f28951461038757600080fd5b8063979986111461030b578063ba86a1f01461031e578063bd147ef41461033157600080fd5b806379ba5097116100bd57806379ba5097146102d35780638da5cb5b146102db578063970b8fc21461030357600080fd5b8063631ec73e146102ad5780636ba0526d146102c057600080fd5b8063397796f71161013a5780634102e4f4116101145780634102e4f4146102745780634d61677114610287578063586abe3c1461029a57600080fd5b8063397796f7146102425780633d0cf6101461024a5780633f42ab731461025d57600080fd5b8063181f5a771161016b578063181f5a77146101ba5780632cbc26bb14610203578063328d716c1461022657600080fd5b80630b009be21461018757806315c65588146101a5575b600080fd5b61018f6103a9565b60405161019c9190613e3f565b60405180910390f35b6101b86101b3366004613fdd565b6103ba565b005b6101f66040518060400160405280600981526020017f524d4e20312e352e30000000000000000000000000000000000000000000000081525081565b60405161019c9190614083565b6102166102113660046140f0565b6104e6565b604051901515815260200161019c565b600b5467ffffffffffffffff165b60405190815260200161019c565b6102166105b1565b6101b86102583660046141a0565b61068b565b6102656107ff565b60405161019c939291906142b3565b6101b86102823660046142ff565b610929565b610216610295366004614439565b61093d565b6101b86102a8366004614451565b6109cd565b6101b86102bb3660046144fc565b610a87565b6101b86102ce366004614451565b610ca0565b6101b8610d13565b60005460405173ffffffffffffffffffffffffffffffffffffffff909116815260200161019c565b600c54610234565b6101b86103193660046145d0565b610e10565b6101b861032c3660046145d0565b611368565b61034461033f3660046140f0565b61150d565b60405161019c9493929190614645565b6103676103623660046146b6565b611946565b60405161019c9190614707565b6101b8610382366004614800565b611b68565b61039a610395366004614439565b611b79565b60405161019c9392919061481b565b60606103b56007611de1565b905090565b336000818152600960205260409020805460ff16610421576040517f85412e7f00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff831660048201526024015b60405180910390fd5b60045463ffffffff166000805b85518110156104a757600086828151811061044b5761044b614849565b602002602001015190506000610465858360000151611df5565b905060008061047b6001888b8760008d89611fd6565b91509150801561048d5761048d614878565b85806104965750815b95505050505080600101905061042e565b50806104df576040517ffb106b6a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5050505050565b600b5460009067ffffffffffffffff16810361050457506000919050565b7f0100000000000000000000000000000100000000000000000000000000000000600052600a6020527fcf943f0e419056430919a3fdfd72276bc0b123ebdd670f4152b82bffbfb8bb385468010000000000000000900460ff16806105a657507fffffffffffffffffffffffffffffffff0000000000000000000000000000000082166000908152600a602052604090205468010000000000000000900460ff165b92915050565b919050565b600b5460009067ffffffffffffffff1681036105cd5750600090565b7f0100000000000000000000000000000100000000000000000000000000000000600052600a6020527fcf943f0e419056430919a3fdfd72276bc0b123ebdd670f4152b82bffbfb8bb385468010000000000000000900460ff16806103b55750507f0100000000000000000000000000000000000000000000000000000000000000600052600a6020527f1d4cd6d2639449a552dbfb463b59316946d78c518b3170daa4a4c217bef019ba5468010000000000000000900460ff1690565b6106936126a4565b60005b8251811015610746576106cc8382815181106106b4576106b4614849565b6020026020010151600761272790919063ffffffff16565b1561073e577fdca892154bbc36d0c05ccd01b3d0411875cb1b841fcdeebb384e5d0d6eb06b4483828151811061070457610704614849565b6020026020010151604051610735919073ffffffffffffffffffffffffffffffffffffffff91909116815260200190565b60405180910390a15b600101610696565b5060005b81518110156107fa5761078082828151811061076857610768614849565b6020026020010151600761274990919063ffffffff16565b156107f2577f66b4b4752c65ae8cd2f3a0a48c7dc8b2118c60d5ea15514992eb2ddf56c9cb158282815181106107b8576107b8614849565b60200260200101516040516107e9919073ffffffffffffffffffffffffffffffffffffffff91909116815260200190565b60405180910390a15b60010161074a565b505050565b6040805160608082018352808252600060208084018290528385018290526004548551600280549384028201608090810190985294810183815263ffffffff808416986401000000009094041696959194919385939192859285015b828210156108f95760008481526020908190206040805160808101825260028602909201805473ffffffffffffffffffffffffffffffffffffffff90811684526001918201549081168486015260ff740100000000000000000000000000000000000000008204811693850193909352750100000000000000000000000000000000000000000090049091166060830152908352909201910161085b565b505050908252506001919091015461ffff8082166020840152620100009091041660409091015292939192919050565b6109316126a4565b61093a8161276b565b50565b600060068161099b610954368690038601866148a7565b80516020918201516040805173ffffffffffffffffffffffffffffffffffffffff909316838501528281019190915280518083038201815260609092019052805191012090565b815260208101919091526040016000205460ff16806105a657506105a66109c56020840184614800565b600790612eef565b337fffffffffffffffffffffffff000000000000000000000000000000000000000181016109fd576109fd614878565b73ffffffffffffffffffffffffffffffffffffffff81166000908152600960205260409020805460ff16610a75576040517f85412e7f00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff83166004820152602401610418565b610a8182858584612f1e565b50505050565b610a8f6126a4565b600454600090819063ffffffff16815b8451811015610b66576000858281518110610abc57610abc614849565b602002602001015190506000610ada84836020015160000151611df5565b9050600080610b3d600087866000015187602001518860400151600960008b6000015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002089611fd6565b915091508680610b4a5750815b96508780610b555750805b975050505050806001019050610a9f565b508215610c615760408051600280546080602082028401810190945260608301818152610c61948492849160009085015b82821015610c355760008481526020908190206040805160808101825260028602909201805473ffffffffffffffffffffffffffffffffffffffff90811684526001918201549081168486015260ff7401000000000000000000000000000000000000000082048116938501939093527501000000000000000000000000000000000000000000900490911660608301529083529092019101610b97565b505050908252506001919091015461ffff8082166020840152620100009091041660409091015261276b565b8180610c6a5750825b610a81576040517ffb106b6a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610ca86126a4565b73ffffffffffffffffffffffffffffffffffffffff60005260096020527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f3bddde647ecb7992f4c710d4e1d59d07614508581f7c22c879a79d28544538a7610a8182858584612f1e565b60015473ffffffffffffffffffffffffffffffffffffffff163314610d94576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4d7573742062652070726f706f736564206f776e6572000000000000000000006044820152606401610418565b60008054337fffffffffffffffffffffffff00000000000000000000000000000000000000008083168217845560018054909116905560405173ffffffffffffffffffffffffffffffffffffffff90921692909183917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a350565b610e397f01000000000000000000000000000001000000000000000000000000000000006104e6565b15610e70576040517fcde2d97c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600454336000908152600560209081526040918290208251606081018452905463ffffffff81811680845260ff64010000000084048116958501959095526501000000000090920490931693820193909352921691908214610f00576040517f85412e7f000000000000000000000000000000000000000000000000000000008152336004820152602401610418565b600160005b8481101561132f576000868683818110610f2157610f21614849565b905060400201803603810190610f3791906148a7565b90506000610f868280516020918201516040805173ffffffffffffffffffffffffffffffffffffffff909316838501528281019190915280518083038201815260609092019052805191012090565b6000818152600660209081526040918290208251608081018452905460ff81161580158352610100820463ffffffff169383019390935265010000000000810461ffff169382019390935267010000000000000090920478ffffffffffffffffffffffffffffffffffffffffffffffffff16606083015291925090611062573373ffffffffffffffffffffffffffffffffffffffff168763ffffffff167f274d6d5b916b0a53974b7ab86c844b97a2e03a60f658cd9a4b1c028b604d7bf18560405161105291906148e0565b60405180910390a3505050611327565b8663ffffffff16816020015163ffffffff16146110a8575060408051608081018252600080825263ffffffff89166020830152918101829052606081019190915261110c565b6110ba816060015187604001516136d6565b1561110c573373ffffffffffffffffffffffffffffffffffffffff168763ffffffff167f6dfbb745226fa630aeb1b9557d17d508ddb789a04f0cb873ec16e58beb8beead8560405161105291906148e0565b6000945061112281606001518760400151613718565b78ffffffffffffffffffffffffffffffffffffffffffffffffff166060820152602086015160408201805160ff9092169161115e90839061493c565b61ffff1690525060208681015160408051865173ffffffffffffffffffffffffffffffffffffffff168152868401519381019390935260ff9091168282015251339163ffffffff8a16917f2a08a2bd2798f0aae9a843f0f4ad4de488c1b3d5f04049940cfed736ad69fb979181900360600190a3600354604082015161ffff91821691161061125757600181526040808201518151855173ffffffffffffffffffffffffffffffffffffffff1681526020808701519082015261ffff90911681830152905163ffffffff8916917f8257378aa73bf8e4ada848713526584a3dcee0fd3db3beed7397f7a7f5067cc9919081900360600190a25b60009182526006602090815260409283902082518154928401519484015160609094015178ffffffffffffffffffffffffffffffffffffffffffffffffff166701000000000000000266ffffffffffffff61ffff90951665010000000000029490941664ffffffffff63ffffffff909616610100027fffffffffffffffffffffffffffffffffffffffffffffffffffffff00000000ff921515929092167fffffffffffffffffffffffffffffffffffffffffffffffffffffff000000000090941693909317179390931617179055505b600101610f05565b5080156104df576040517f604c767700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6113706126a4565b60045463ffffffff1660005b82811015610a8157600084848381811061139857611398614849565b9050604002018036038101906113ae91906148a7565b905060006113fd8280516020918201516040805173ffffffffffffffffffffffffffffffffffffffff909316838501528281019190915280518083038201815260609092019052805191012090565b60008181526006602081815260408084208151608081018352815460ff811615158252610100810463ffffffff90811683870190815265010000000000830461ffff169584019590955267010000000000000090910478ffffffffffffffffffffffffffffffffffffffffffffffffff16606083015287875294909352939093558051925193945092878216911614806114945750805b156114fe5760408051855173ffffffffffffffffffffffffffffffffffffffff1681526020808701519082015282151581830152905163ffffffff8816917f7d15a6eebaa019ea7d5b7d38937c51ebd3befbfdf51bb630a694fd28635bbcba919081900360600190a25b5050505080600101905061137c565b600454604080516002805460806020820284018101909452606083810182815290958695600095869563ffffffff9093169486949193928492918491879085015b828210156115ec5760008481526020908190206040805160808101825260028602909201805473ffffffffffffffffffffffffffffffffffffffff90811684526001918201549081168486015260ff740100000000000000000000000000000000000000008204811693850193909352750100000000000000000000000000000000000000000090049091166060830152908352909201910161154e565b505050908252506001919091015461ffff80821660208085019190915262010000909204166040928301527fffffffffffffffffffffffffffffffff000000000000000000000000000000008a166000908152600a909152908120805460ff6801000000000000000082041696509293509163ffffffff80861691161080156116725750845b6000965090508560015b60028111611939578451515b6000808760000151518310156116e35787518051849081106116ac576116ac614849565b6020026020010151602001519150876000015183815181106116d0576116d0614849565b602002602001015160600151905061170a565b507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff905060005b73ffffffffffffffffffffffffffffffffffffffff82166000908152600188016020908152604080832081518083019092525463ffffffff811682526401000000009004821b63ffffffff19169181019190915290878061177a57508a63ffffffff16826000015163ffffffff16145b8061179a575073ffffffffffffffffffffffffffffffffffffffff848116145b80156117b05750602082015163ffffffff191615155b9050801561186d57856001036117d0576117c987614957565b965061186d565b85600203610182576117e560ff84168e61493c565b9c506117f08761498f565b9650838f888151811061180557611805614849565b602002602001019073ffffffffffffffffffffffffffffffffffffffff16908173ffffffffffffffffffffffffffffffffffffffff168152505081602001518e888151811061185657611856614849565b63ffffffff19909216602092830291909101909101525b84156118835761187c8561498f565b945061188c565b50505050611895565b50505050611688565b81600103611928578267ffffffffffffffff8111156118b6576118b6613e52565b6040519080825280602002602001820160405280156118df578160200160208202803683370190505b509a508267ffffffffffffffff8111156118fb576118fb613e52565b604051908082528060200260200182016040528015611924578160200160208202803683370190505b5099505b5061193281614957565b905061167c565b5050505050509193509193565b600c5460609060009061195984866149c4565b11611965575081611988565b600c5484101561198457600c5461197d9085906149d7565b9050611988565b5060005b60008167ffffffffffffffff8111156119a3576119a3613e52565b604051908082528060200260200182016040528015611a2157816020015b6040805160c08101825260008082526020808301829052928201819052606082018190526080820181905260a082015282527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9092019101816119c15790505b50905060005b82811015611b5f57600c611a3b82886149c4565b81548110611a4b57611a4b614849565b600091825260209091206040805160c081019091526002909202018054829060ff166004811115611a7e57611a7e6146d8565b6004811115611a8f57611a8f6146d8565b81528154610100810467ffffffffffffffff1660208301526901000000000000000000810460ff16151560408301526a0100000000000000000000900473ffffffffffffffffffffffffffffffffffffffff166060820152600190910154608081811b7fffffffffffffffffffffffffffffffff0000000000000000000000000000000090811682850152700100000000000000000000000000000000909204901b1660a0909101528251839083908110611b4c57611b4c614849565b6020908102919091010152600101611a27565b50949350505050565b611b706126a4565b61093a8161373b565b606060008080611b91610954368790038701876148a7565b6000818152600660209081526040918290208251608081018452905460ff81161515808352610100820463ffffffff90811694840185905265010000000000830461ffff169584019590955267010000000000000090910478ffffffffffffffffffffffffffffffffffffffffffffffffff166060830152600454909650939450929091169003611dd85760408101516060820151909450611c3281613830565b60ff1667ffffffffffffffff811115611c4d57611c4d613e52565b604051908082528060200260200182016040528015611c76578160200160208202803683370190505b506002805460408051602080840282018101909252828152939950600093929190849084015b82821015611d3a5760008481526020908190206040805160808101825260028602909201805473ffffffffffffffffffffffffffffffffffffffff90811684526001918201549081168486015260ff7401000000000000000000000000000000000000000082048116938501939093527501000000000000000000000000000000000000000000900490911660608301529083529092019101611c9c565b5050505090506000805b82518160ff161015611dd357611d5a84826136d6565b15611dc357828160ff1681518110611d7457611d74614849565b602002602001015160000151898381518110611d9257611d92614849565b73ffffffffffffffffffffffffffffffffffffffff90921660209283029190910190910152611dc082614957565b91505b611dcc816149ea565b9050611d44565b505050505b50509193909250565b60606000611dee8361389f565b9392505050565b7fffffffffffffffffffffffffffffffff0000000000000000000000000000000081166000908152600a60205260408120805463ffffffff858116911614611dee57805463ffffffff19811663ffffffff861690811783556003547fffffffffffffffffffffffffffffffffffffffffffffffffffff000000000000909216176201000090910461ffff1664010000000002177fffffffffffffffffffffffffffffffffffffffffffffffff0000ffffffffffff1680825568010000000000000000900460ff1615611dee57600260005b8154811015611fcd576000826000018281548110611ee657611ee6614849565b6000918252602080832060016002909302018281015473ffffffffffffffffffffffffffffffffffffffff1684529187019052604090912080549192509063ffffffff808a169116108015611f4d57508054640100000000900460201b63ffffffff191615155b15611fc357805463ffffffff191663ffffffff891617815560018201548554750100000000000000000000000000000000000000000090910460ff16908690600690611fa89084906601000000000000900461ffff1661493c565b92506101000a81548161ffff021916908361ffff1602179055505b5050600101611ec6565b50509392505050565b6000806001896001811115611fed57611fed6146d8565b148061200a57506000896001811115612008576120086146d8565b145b61201657612016614878565b8480612037575073ffffffffffffffffffffffffffffffffffffffff878116145b80612056575073ffffffffffffffffffffffffffffffffffffffff8716155b1561207c57600089600181111561206f5761206f6146d8565b1461207c5761207c614878565b73ffffffffffffffffffffffffffffffffffffffff8716600090815260018401602090815260409182902082518084019093525463ffffffff811683526401000000009004811b63ffffffff191690820152845460ff16801561210d575073ffffffffffffffffffffffffffffffffffffffff888116148061210d57508863ffffffff16816000015163ffffffff16145b80156121235750602081015163ffffffff191615155b801561214b5750866020015163ffffffff1916816020015163ffffffff1916148061214b5750855b156122765773ffffffffffffffffffffffffffffffffffffffff881660009081526001858101602052604082209190915585548554919450610100900460ff169085906006906121aa9084906601000000000000900461ffff16614a09565b825461010092830a61ffff818102199092169282160291909117909255895188546020808d01518a54604080517fffffffffffffffffffffffffffffffff0000000000000000000000000000000090961686529590930460ff169184019190915263ffffffff1916828401526601000000000000900490921660608301525173ffffffffffffffffffffffffffffffffffffffff8b16925063ffffffff8c16917fa96a155bd67c927a6c056befbd979b78465e2b2f1276bf7d4e90a31d4f430aa8919081900360800190a35b6000808b600181111561228b5761228b6146d8565b1480156122b3575083806122b3575073ffffffffffffffffffffffffffffffffffffffff8916155b90508080156122cf5750845468010000000000000000900460ff165b80156122e157506122df856138fb565b155b156123b45784547fffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffff168555600b80546001945060009061232a9067ffffffffffffffff16614a24565b91906101000a81548167ffffffffffffffff021916908367ffffffffffffffff1602179055507f65d0e78c3625f0956f58610cf0fb157eaf627683258875ef29af2f71d25ac8fd88600001516040516123ab91907fffffffffffffffffffffffffffffffff0000000000000000000000000000000091909116815260200190565b60405180910390a15b83806123bd5750825b15612605576000808c60018111156123d7576123d76146d8565b036123f25787156123ea5750600361240f565b50600261240f565b60018c6001811115612406576124066146d8565b03610182575060015b600c6040518060c0016040528083600481111561242e5761242e6146d8565b81526020014267ffffffffffffffff168152885468010000000000000000900460ff16151560208083019190915273ffffffffffffffffffffffffffffffffffffffff8e1660408301528c517fffffffffffffffffffffffffffffffff00000000000000000000000000000000166060830152600060809092018290528354600180820186559483529120825160029092020180549293909283917fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0090911690836004811115612500576125006146d8565b0217905550602082015181546040840151606085015173ffffffffffffffffffffffffffffffffffffffff166a0100000000000000000000027fffff0000000000000000000000000000000000000000ffffffffffffffffffff9115156901000000000000000000027fffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffff67ffffffffffffffff90951661010002949094167fffffffffffffffffffffffffffffffffffffffffffff000000000000000000ff90931692909217929092179190911617815560808083015160a090930151811c7001000000000000000000000000000000000292901c9190911760019091015550612696565b8751602080840151818b0151604080517fffffffffffffffffffffffffffffffff00000000000000000000000000000000909516855263ffffffff1992831693850193909352169082015273ffffffffffffffffffffffffffffffffffffffff8a16907fbabb0d7099e6ca14a29fad2a2cfb4fda2bd30f97cb3c27e546174bfb4277c1cc9060600160405180910390a25b505097509795505050505050565b60005473ffffffffffffffffffffffffffffffffffffffff163314612725576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4f6e6c792063616c6c61626c65206279206f776e6572000000000000000000006044820152606401610418565b565b6000611dee8373ffffffffffffffffffffffffffffffffffffffff841661395c565b6000611dee8373ffffffffffffffffffffffffffffffffffffffff8416613a56565b61277481613aa5565b6127aa576040517f35be3ac800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b602081015160038054604084015161ffff908116620100000263ffffffff199092169316929092179190911790555b6002541561298e5760028054600091906127f5906001906149d7565b8154811061280557612805614849565b60009182526020808320604080516080810182526002948502909201805473ffffffffffffffffffffffffffffffffffffffff90811680855260019092015480821685870190815260ff740100000000000000000000000000000000000000008304811687870152750100000000000000000000000000000000000000000090920490911660608601529187526005855282872080547fffffffffffffffffffffffffffffffffffffffffffffffffffff00000000000016905590511685526009909252922080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00001690558054919250908061290457612904614a66565b60008281526020902060027fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9092019182020180547fffffffffffffffffffffffff000000000000000000000000000000000000000016815560010180547fffffffffffffffffffff000000000000000000000000000000000000000000001690559055506127d9565b60005b815151811015612ac1578151805160029190839081106129b3576129b3614849565b6020908102919091018101518254600181810185556000948552938390208251600290920201805473ffffffffffffffffffffffffffffffffffffffff9283167fffffffffffffffffffffffff0000000000000000000000000000000000000000909116178155928201519284018054604084015160609094015160ff9081167501000000000000000000000000000000000000000000027fffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffff9190951674010000000000000000000000000000000000000000027fffffffffffffffffffffff0000000000000000000000000000000000000000009092169590931694909417939093171617905501612991565b5060048054600090612ad89063ffffffff16614a95565b82546101009290920a63ffffffff8181021990931691831602179091556004541660005b82515160ff82161015612c5557600083600001518260ff1681518110612b2457612b24614849565b602090810291909101810151604080516060808201835263ffffffff80891683528385015160ff908116848801908152898216858701908152875173ffffffffffffffffffffffffffffffffffffffff908116600090815260058b528881209751885494519351861665010000000000027fffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffff948716640100000000027fffffffffffffffffffffffffffffffffffffffffffffffffffffff00000000009096169190971617939093179190911693909317909455858701519091168352600990955291902080549190920151909216610100027fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000090921691909117600117905550612c4e816149ea565b9050612afc565b5073ffffffffffffffffffffffffffffffffffffffff60005260096020527f3bddde647ecb7992f4c710d4e1d59d07614508581f7c22c879a79d28544538a780547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00001660011790556004805463ffffffff438116640100000000027fffffffffffffffffffffffffffffffffffffffffffffffff00000000ffffffff90921691909117909155604051908216907f8c49fda8177c5c8c768eb39634bc6773695c7181711537b822451c12b2efd2a990612d2f908590614ab8565b60405180910390a26040805160c081018252600480825267ffffffffffffffff421660208301526000928201839052606082018390526080820183905260a08201839052600c80546001808201835591909452825160029094027fdf6966c971051c3d54ec59162606531493a51404a002842f56009d7e5cf4a8c701805493949093909284927fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0090921691908490811115612dec57612dec6146d8565b0217905550602082015181546040840151606085015173ffffffffffffffffffffffffffffffffffffffff166a0100000000000000000000027fffff0000000000000000000000000000000000000000ffffffffffffffffffff9115156901000000000000000000027fffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffff67ffffffffffffffff90951661010002949094167fffffffffffffffffffffffffffffffffffffffffffff000000000000000000ff90931692909217929092179190911617815560808083015160a090930151811c7001000000000000000000000000000000000292901c919091176001909101555050565b73ffffffffffffffffffffffffffffffffffffffff811660009081526001830160205260408120541515611dee565b8151600003612f59576040517f55e9b08b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7fffffffffffffffffffffffffffffffff000000000000000000000000000000008316600090815260018201602052604090205460ff1615613007576040517f078f340000000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff851660048201527fffffffffffffffffffffffffffffffff0000000000000000000000000000000084166024820152604401610418565b7fffffffffffffffffffffffffffffffff000000000000000000000000000000008316600090815260018281016020526040822080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016909117905560045463ffffffff16905b83518110156136ce57600181101580156130ed575083818151811061309657613096614849565b60200260200101516fffffffffffffffffffffffffffffffff1916846001836130bf91906149d7565b815181106130cf576130cf614849565b60200260200101516fffffffffffffffffffffffffffffffff191610155b15613124576040517f2432d8ea00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600084828151811061313857613138614849565b60200260200101519050600061314e8483611df5565b73ffffffffffffffffffffffffffffffffffffffff8981166000818152600184016020908152604080832081518083019092525463ffffffff811682526401000000009004821b63ffffffff19169181019190915293945091148015906131be5750815163ffffffff8088169116105b806131d25750602082015163ffffffff1916155b15613225575085548254600091610100900460ff169084906006906132069084906601000000000000900461ffff1661493c565b92506101000a81548161ffff021916908361ffff16021790555061322c565b5060208101515b60408051808201825263ffffffff88168152815163ffffffff1984166020828101919091527fffffffffffffffffffffffffffffffff000000000000000000000000000000008d16828501528351808303850181526060909201909352805190830120909182019063ffffffff1916905273ffffffffffffffffffffffffffffffffffffffff8b166000818152600186016020908152604090912083518285015190921c6401000000000263ffffffff92831617905589549294509091908816907f8137bc8a8d712aaa27bfc6506d5566ac405618bd53f9831b8ca6b6fe5442ee7a9087908d9060ff610100909104166133234290565b6020898101518b54604080517fffffffffffffffffffffffffffffffff000000000000000000000000000000009889168152979096169287019290925260ff9093169385019390935267ffffffffffffffff16606084015263ffffffff191660808301526601000000000000900461ffff1660a082015260c00160405180910390a363ffffffff1981161580156133c85750825468010000000000000000900460ff16155b80156133d857506133d8836138fb565b156134c35782547fffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffff1668010000000000000000178355600b80546000906134289067ffffffffffffffff16614acb565b91906101000a81548167ffffffffffffffff021916908367ffffffffffffffff1602179055508563ffffffff167fcfdbfd8ce9a56b5f7c202c0e102184d24f47ca87121dc165063fc4c290957bde8561347e4290565b604080517fffffffffffffffffffffffffffffffff00000000000000000000000000000000909316835267ffffffffffffffff90911660208301520160405180910390a25b6040805160c081018252600080825267ffffffffffffffff42166020830152855460ff680100000000000000009091041615159282019290925273ffffffffffffffffffffffffffffffffffffffff8c1660608201527fffffffffffffffffffffffffffffffff0000000000000000000000000000000086811660808301528b1660a0820152600c80546001808201835591909352815160029093027fdf6966c971051c3d54ec59162606531493a51404a002842f56009d7e5cf4a8c701805492939092909183917fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016908360048111156135c0576135c06146d8565b0217905550602082015181546040840151606085015173ffffffffffffffffffffffffffffffffffffffff166a0100000000000000000000027fffff0000000000000000000000000000000000000000ffffffffffffffffffff9115156901000000000000000000027fffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffff67ffffffffffffffff90951661010002949094167fffffffffffffffffffffffffffffffffffffffffffff000000000000000000ff90931692909217929092179190911617815560808083015160a090930151811c7001000000000000000000000000000000000292901c9190911760019182015594909401935061306f92505050565b505050505050565b600060108260ff16106136eb576136eb614878565b50600160ff82161b821678ffffffffffffffffffffffffffffffffffffffffffffffffff16151592915050565b600060108260ff161061372d5761372d614878565b50600160ff919091161b1790565b3373ffffffffffffffffffffffffffffffffffffffff8216036137ba576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c660000000000000000006044820152606401610418565b600180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff83811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b60006201000078ffffffffffffffffffffffffffffffffffffffffffffffffff83161061385f5761385f614878565b78ffffffffffffffffffffffffffffffffffffffffffffffffff8216156105ac5761388b600183614ae8565b90911690613898816149ea565b905061385f565b6060816000018054806020026020016040519081016040528092919081815260200182805480156138ef57602002820191906000526020600020905b8154815260200190600101908083116138db575b50505050509050919050565b73ffffffffffffffffffffffffffffffffffffffff600090815260018201602090815260408220546401000000009004901b63ffffffff19161515806105a65750505461ffff64010000000082048116660100000000000090920416101590565b60008181526001830160205260408120548015613a455760006139806001836149d7565b8554909150600090613994906001906149d7565b90508181146139f95760008660000182815481106139b4576139b4614849565b90600052602060002001549050808760000184815481106139d7576139d7614849565b6000918252602080832090910192909255918252600188019052604090208390555b8554869080613a0a57613a0a614a66565b6001900381819060005260206000200160009055905585600101600086815260200190815260200160002060009055600193505050506105a6565b60009150506105a6565b5092915050565b6000818152600183016020526040812054613a9d575081546001818101845560008481526020808220909301849055845484825282860190935260409020919091556105a6565b5060006105a6565b8051516000901580613ab957508151516010105b80613aca5750602082015161ffff16155b80613adb5750604082015161ffff16155b15613ae857506000919050565b60008060008460000151516002613aff9190614b1a565b67ffffffffffffffff811115613b1757613b17613e52565b604051908082528060200260200182016040528015613b40578160200160208202803683370190505b50905060005b855151811015613d1157600086600001518281518110613b6857613b68614849565b60200260200101519050600073ffffffffffffffffffffffffffffffffffffffff16816000015173ffffffffffffffffffffffffffffffffffffffff161480613bc95750602081015173ffffffffffffffffffffffffffffffffffffffff16155b80613bec5750602081015173ffffffffffffffffffffffffffffffffffffffff16155b80613c115750602081015173ffffffffffffffffffffffffffffffffffffffff908116145b80613c315750604081015160ff16158015613c315750606081015160ff16155b15613c43575060009695505050505050565b805183613c51846002614b1a565b613c5c9060006149c4565b81518110613c6c57613c6c614849565b73ffffffffffffffffffffffffffffffffffffffff90921660209283029190910182015281015183613c9f846002614b1a565b613caa9060016149c4565b81518110613cba57613cba614849565b73ffffffffffffffffffffffffffffffffffffffff909216602092830291909101909101526040810151613cf19060ff16866149c4565b9450806060015160ff1684613d0691906149c4565b935050600101613b46565b5060005b8151811015613dc3576000828281518110613d3257613d32614849565b602002602001015190506000826001613d4b91906149c4565b90505b8351811015613db957838181518110613d6957613d69614849565b602002602001015173ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1603613db157506000979650505050505050565b600101613d4e565b5050600101613d15565b50846020015161ffff168310158015613de45750846040015161ffff168210155b95945050505050565b60008151808452602080850194506020840160005b83811015613e3457815173ffffffffffffffffffffffffffffffffffffffff1687529582019590820190600101613e02565b509495945050505050565b602081526000611dee6020830184613ded565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6040805190810167ffffffffffffffff81118282101715613ea457613ea4613e52565b60405290565b6040516060810167ffffffffffffffff81118282101715613ea457613ea4613e52565b6040516080810167ffffffffffffffff81118282101715613ea457613ea4613e52565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff81118282101715613f3757613f37613e52565b604052919050565b600067ffffffffffffffff821115613f5957613f59613e52565b5060051b60200190565b80357fffffffffffffffffffffffffffffffff00000000000000000000000000000000811681146105ac57600080fd5b600060408284031215613fa557600080fd5b613fad613e81565b9050613fb882613f63565b8152602082013563ffffffff1981168114613fd257600080fd5b602082015292915050565b60006020808385031215613ff057600080fd5b823567ffffffffffffffff81111561400757600080fd5b8301601f8101851361401857600080fd5b803561402b61402682613f3f565b613ef0565b8082825260208201915060208360061b85010192508783111561404d57600080fd5b6020840193505b82841015614078576140668885613f93565b82528482019150604084019350614054565b979650505050505050565b60006020808352835180602085015260005b818110156140b157858101830151858201604001528201614095565b5060006040828601015260407fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8301168501019250505092915050565b60006020828403121561410257600080fd5b611dee82613f63565b803573ffffffffffffffffffffffffffffffffffffffff811681146105ac57600080fd5b600082601f83011261414057600080fd5b8135602061415061402683613f3f565b8083825260208201915060208460051b87010193508684111561417257600080fd5b602086015b84811015614195576141888161410b565b8352918301918301614177565b509695505050505050565b600080604083850312156141b357600080fd5b823567ffffffffffffffff808211156141cb57600080fd5b6141d78683870161412f565b935060208501359150808211156141ed57600080fd5b506141fa8582860161412f565b9150509250929050565b8051606080845281518482018190526000926080916020918201918388019190865b82811015614280578451805173ffffffffffffffffffffffffffffffffffffffff908116865283820151168386015260408082015160ff908116918701919091529088015116878501529381019392850192600101614226565b508781015161ffff81168a83015295505050604086015193506142a9604088018561ffff169052565b9695505050505050565b600063ffffffff808616835280851660208401525060606040830152613de46060830184614204565b803560ff811681146105ac57600080fd5b803561ffff811681146105ac57600080fd5b6000602080838503121561431257600080fd5b823567ffffffffffffffff8082111561432a57600080fd5b8185019150606080838803121561434057600080fd5b614348613eaa565b83358381111561435757600080fd5b84019250601f8301881361436a57600080fd5b823561437861402682613f3f565b81815260079190911b8401860190868101908a83111561439757600080fd5b948701945b82861015614409576080868c0312156143b55760008081fd5b6143bd613ecd565b6143c68761410b565b81526143d389880161410b565b8982015260406143e48189016142dc565b908201526143f38787016142dc565b818701528252608095909501949087019061439c565b83525061441990508486016142ed565b85820152614429604085016142ed565b6040820152979650505050505050565b60006040828403121561444b57600080fd5b50919050565b6000806040838503121561446457600080fd5b61446d83613f63565b915060208084013567ffffffffffffffff81111561448a57600080fd5b8401601f8101861361449b57600080fd5b80356144a961402682613f3f565b81815260059190911b820183019083810190888311156144c857600080fd5b928401925b828410156144ed576144de84613f63565b825292840192908401906144cd565b80955050505050509250929050565b6000602080838503121561450f57600080fd5b823567ffffffffffffffff81111561452657600080fd5b8301601f8101851361453757600080fd5b803561454561402682613f3f565b81815260079190911b8201830190838101908783111561456457600080fd5b928401925b8284101561407857608084890312156145825760008081fd5b61458a613eaa565b6145938561410b565b81526145a189878701613f93565b86820152606085013580151581146145b95760008081fd5b604082015282526080939093019290840190614569565b600080602083850312156145e357600080fd5b823567ffffffffffffffff808211156145fb57600080fd5b818501915085601f83011261460f57600080fd5b81358181111561461e57600080fd5b8660208260061b850101111561463357600080fd5b60209290920196919550909350505050565b6080815260006146586080830187613ded565b82810360208481019190915286518083528782019282019060005b8181101561469657845163ffffffff191683529383019391830191600101614673565b505061ffff96909616604085015250505090151560609091015292915050565b600080604083850312156146c957600080fd5b50508035926020909101359150565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b60208082528251828201819052600091906040908185019086840185805b838110156147f2578251805160058110614766577f4e487b710000000000000000000000000000000000000000000000000000000084526021600452602484fd5b86528088015167ffffffffffffffff16888701528681015115158787015260608082015173ffffffffffffffffffffffffffffffffffffffff16908701526080808201517fffffffffffffffffffffffffffffffff000000000000000000000000000000009081169188019190915260a091820151169086015260c09094019391860191600101614725565b509298975050505050505050565b60006020828403121561481257600080fd5b611dee8261410b565b60608152600061482e6060830186613ded565b61ffff94909416602083015250901515604090910152919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052600160045260246000fd5b6000604082840312156148b957600080fd5b6148c1613e81565b6148ca8361410b565b8152602083013560208201528091505092915050565b815173ffffffffffffffffffffffffffffffffffffffff16815260208083015190820152604081016105a6565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b61ffff818116838216019080821115613a4f57613a4f61490d565b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82036149885761498861490d565b5060010190565b60008161499e5761499e61490d565b507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0190565b808201808211156105a6576105a661490d565b818103818111156105a6576105a661490d565b600060ff821660ff8103614a0057614a0061490d565b60010192915050565b61ffff828116828216039080821115613a4f57613a4f61490d565b600067ffffffffffffffff821680614a3e57614a3e61490d565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0192915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603160045260246000fd5b600063ffffffff808316818103614aae57614aae61490d565b6001019392505050565b602081526000611dee6020830184614204565b600067ffffffffffffffff808316818103614aae57614aae61490d565b78ffffffffffffffffffffffffffffffffffffffffffffffffff828116828216039080821115613a4f57613a4f61490d565b80820281158282048414176105a6576105a661490d56fea164736f6c6343000818000a00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000006000000000000000000000000bf469bd18b258f1eb3710a6b53778d650d193d410000000000000000000000007be2c5c3e4b48439314119486008e2e30b32319700000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001000000000000000000000000765293e01dc24d9aaad886c5bf78767930c4b34e000000000000000000000000fd3a26bdf612494b4c7dc0b14c1e8304bbec046500000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001000000000000000000000000e9005e15eff73fb2c08a44b72003c01caa7a8fb0000000000000000000000000933c057f8e10dcd733c6c836ad3a36a956fcec7300000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001000000000000000000000000dffd4d4556edbb629e2d15756da736582625d318000000000000000000000000857626295e895a2f44ae1c1f57d239bb515d9cb80000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000100000000000000000000000082dd2be746810245f978a20a28e86dff8b9cfe2f0000000000000000000000001f645d004dcaa833d439fed98c07bcf6c77d7d900000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000100000000000000000000000079f44a2acd6bb225b6ec113eb435fe463b4e201b00000000000000000000000073623ccfb74e22bf8d90cca342d736b8e8ff905900000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001

Deployed Bytecode

0x608060405234801561001057600080fd5b50600436106101825760003560e01c8063631ec73e116100d8578063979986111161008c578063d927f26711610066578063d927f26714610354578063f2fde38b14610374578063f33f28951461038757600080fd5b8063979986111461030b578063ba86a1f01461031e578063bd147ef41461033157600080fd5b806379ba5097116100bd57806379ba5097146102d35780638da5cb5b146102db578063970b8fc21461030357600080fd5b8063631ec73e146102ad5780636ba0526d146102c057600080fd5b8063397796f71161013a5780634102e4f4116101145780634102e4f4146102745780634d61677114610287578063586abe3c1461029a57600080fd5b8063397796f7146102425780633d0cf6101461024a5780633f42ab731461025d57600080fd5b8063181f5a771161016b578063181f5a77146101ba5780632cbc26bb14610203578063328d716c1461022657600080fd5b80630b009be21461018757806315c65588146101a5575b600080fd5b61018f6103a9565b60405161019c9190613e3f565b60405180910390f35b6101b86101b3366004613fdd565b6103ba565b005b6101f66040518060400160405280600981526020017f524d4e20312e352e30000000000000000000000000000000000000000000000081525081565b60405161019c9190614083565b6102166102113660046140f0565b6104e6565b604051901515815260200161019c565b600b5467ffffffffffffffff165b60405190815260200161019c565b6102166105b1565b6101b86102583660046141a0565b61068b565b6102656107ff565b60405161019c939291906142b3565b6101b86102823660046142ff565b610929565b610216610295366004614439565b61093d565b6101b86102a8366004614451565b6109cd565b6101b86102bb3660046144fc565b610a87565b6101b86102ce366004614451565b610ca0565b6101b8610d13565b60005460405173ffffffffffffffffffffffffffffffffffffffff909116815260200161019c565b600c54610234565b6101b86103193660046145d0565b610e10565b6101b861032c3660046145d0565b611368565b61034461033f3660046140f0565b61150d565b60405161019c9493929190614645565b6103676103623660046146b6565b611946565b60405161019c9190614707565b6101b8610382366004614800565b611b68565b61039a610395366004614439565b611b79565b60405161019c9392919061481b565b60606103b56007611de1565b905090565b336000818152600960205260409020805460ff16610421576040517f85412e7f00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff831660048201526024015b60405180910390fd5b60045463ffffffff166000805b85518110156104a757600086828151811061044b5761044b614849565b602002602001015190506000610465858360000151611df5565b905060008061047b6001888b8760008d89611fd6565b91509150801561048d5761048d614878565b85806104965750815b95505050505080600101905061042e565b50806104df576040517ffb106b6a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5050505050565b600b5460009067ffffffffffffffff16810361050457506000919050565b7f0100000000000000000000000000000100000000000000000000000000000000600052600a6020527fcf943f0e419056430919a3fdfd72276bc0b123ebdd670f4152b82bffbfb8bb385468010000000000000000900460ff16806105a657507fffffffffffffffffffffffffffffffff0000000000000000000000000000000082166000908152600a602052604090205468010000000000000000900460ff165b92915050565b919050565b600b5460009067ffffffffffffffff1681036105cd5750600090565b7f0100000000000000000000000000000100000000000000000000000000000000600052600a6020527fcf943f0e419056430919a3fdfd72276bc0b123ebdd670f4152b82bffbfb8bb385468010000000000000000900460ff16806103b55750507f0100000000000000000000000000000000000000000000000000000000000000600052600a6020527f1d4cd6d2639449a552dbfb463b59316946d78c518b3170daa4a4c217bef019ba5468010000000000000000900460ff1690565b6106936126a4565b60005b8251811015610746576106cc8382815181106106b4576106b4614849565b6020026020010151600761272790919063ffffffff16565b1561073e577fdca892154bbc36d0c05ccd01b3d0411875cb1b841fcdeebb384e5d0d6eb06b4483828151811061070457610704614849565b6020026020010151604051610735919073ffffffffffffffffffffffffffffffffffffffff91909116815260200190565b60405180910390a15b600101610696565b5060005b81518110156107fa5761078082828151811061076857610768614849565b6020026020010151600761274990919063ffffffff16565b156107f2577f66b4b4752c65ae8cd2f3a0a48c7dc8b2118c60d5ea15514992eb2ddf56c9cb158282815181106107b8576107b8614849565b60200260200101516040516107e9919073ffffffffffffffffffffffffffffffffffffffff91909116815260200190565b60405180910390a15b60010161074a565b505050565b6040805160608082018352808252600060208084018290528385018290526004548551600280549384028201608090810190985294810183815263ffffffff808416986401000000009094041696959194919385939192859285015b828210156108f95760008481526020908190206040805160808101825260028602909201805473ffffffffffffffffffffffffffffffffffffffff90811684526001918201549081168486015260ff740100000000000000000000000000000000000000008204811693850193909352750100000000000000000000000000000000000000000090049091166060830152908352909201910161085b565b505050908252506001919091015461ffff8082166020840152620100009091041660409091015292939192919050565b6109316126a4565b61093a8161276b565b50565b600060068161099b610954368690038601866148a7565b80516020918201516040805173ffffffffffffffffffffffffffffffffffffffff909316838501528281019190915280518083038201815260609092019052805191012090565b815260208101919091526040016000205460ff16806105a657506105a66109c56020840184614800565b600790612eef565b337fffffffffffffffffffffffff000000000000000000000000000000000000000181016109fd576109fd614878565b73ffffffffffffffffffffffffffffffffffffffff81166000908152600960205260409020805460ff16610a75576040517f85412e7f00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff83166004820152602401610418565b610a8182858584612f1e565b50505050565b610a8f6126a4565b600454600090819063ffffffff16815b8451811015610b66576000858281518110610abc57610abc614849565b602002602001015190506000610ada84836020015160000151611df5565b9050600080610b3d600087866000015187602001518860400151600960008b6000015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002089611fd6565b915091508680610b4a5750815b96508780610b555750805b975050505050806001019050610a9f565b508215610c615760408051600280546080602082028401810190945260608301818152610c61948492849160009085015b82821015610c355760008481526020908190206040805160808101825260028602909201805473ffffffffffffffffffffffffffffffffffffffff90811684526001918201549081168486015260ff7401000000000000000000000000000000000000000082048116938501939093527501000000000000000000000000000000000000000000900490911660608301529083529092019101610b97565b505050908252506001919091015461ffff8082166020840152620100009091041660409091015261276b565b8180610c6a5750825b610a81576040517ffb106b6a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610ca86126a4565b73ffffffffffffffffffffffffffffffffffffffff60005260096020527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f3bddde647ecb7992f4c710d4e1d59d07614508581f7c22c879a79d28544538a7610a8182858584612f1e565b60015473ffffffffffffffffffffffffffffffffffffffff163314610d94576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4d7573742062652070726f706f736564206f776e6572000000000000000000006044820152606401610418565b60008054337fffffffffffffffffffffffff00000000000000000000000000000000000000008083168217845560018054909116905560405173ffffffffffffffffffffffffffffffffffffffff90921692909183917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a350565b610e397f01000000000000000000000000000001000000000000000000000000000000006104e6565b15610e70576040517fcde2d97c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600454336000908152600560209081526040918290208251606081018452905463ffffffff81811680845260ff64010000000084048116958501959095526501000000000090920490931693820193909352921691908214610f00576040517f85412e7f000000000000000000000000000000000000000000000000000000008152336004820152602401610418565b600160005b8481101561132f576000868683818110610f2157610f21614849565b905060400201803603810190610f3791906148a7565b90506000610f868280516020918201516040805173ffffffffffffffffffffffffffffffffffffffff909316838501528281019190915280518083038201815260609092019052805191012090565b6000818152600660209081526040918290208251608081018452905460ff81161580158352610100820463ffffffff169383019390935265010000000000810461ffff169382019390935267010000000000000090920478ffffffffffffffffffffffffffffffffffffffffffffffffff16606083015291925090611062573373ffffffffffffffffffffffffffffffffffffffff168763ffffffff167f274d6d5b916b0a53974b7ab86c844b97a2e03a60f658cd9a4b1c028b604d7bf18560405161105291906148e0565b60405180910390a3505050611327565b8663ffffffff16816020015163ffffffff16146110a8575060408051608081018252600080825263ffffffff89166020830152918101829052606081019190915261110c565b6110ba816060015187604001516136d6565b1561110c573373ffffffffffffffffffffffffffffffffffffffff168763ffffffff167f6dfbb745226fa630aeb1b9557d17d508ddb789a04f0cb873ec16e58beb8beead8560405161105291906148e0565b6000945061112281606001518760400151613718565b78ffffffffffffffffffffffffffffffffffffffffffffffffff166060820152602086015160408201805160ff9092169161115e90839061493c565b61ffff1690525060208681015160408051865173ffffffffffffffffffffffffffffffffffffffff168152868401519381019390935260ff9091168282015251339163ffffffff8a16917f2a08a2bd2798f0aae9a843f0f4ad4de488c1b3d5f04049940cfed736ad69fb979181900360600190a3600354604082015161ffff91821691161061125757600181526040808201518151855173ffffffffffffffffffffffffffffffffffffffff1681526020808701519082015261ffff90911681830152905163ffffffff8916917f8257378aa73bf8e4ada848713526584a3dcee0fd3db3beed7397f7a7f5067cc9919081900360600190a25b60009182526006602090815260409283902082518154928401519484015160609094015178ffffffffffffffffffffffffffffffffffffffffffffffffff166701000000000000000266ffffffffffffff61ffff90951665010000000000029490941664ffffffffff63ffffffff909616610100027fffffffffffffffffffffffffffffffffffffffffffffffffffffff00000000ff921515929092167fffffffffffffffffffffffffffffffffffffffffffffffffffffff000000000090941693909317179390931617179055505b600101610f05565b5080156104df576040517f604c767700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6113706126a4565b60045463ffffffff1660005b82811015610a8157600084848381811061139857611398614849565b9050604002018036038101906113ae91906148a7565b905060006113fd8280516020918201516040805173ffffffffffffffffffffffffffffffffffffffff909316838501528281019190915280518083038201815260609092019052805191012090565b60008181526006602081815260408084208151608081018352815460ff811615158252610100810463ffffffff90811683870190815265010000000000830461ffff169584019590955267010000000000000090910478ffffffffffffffffffffffffffffffffffffffffffffffffff16606083015287875294909352939093558051925193945092878216911614806114945750805b156114fe5760408051855173ffffffffffffffffffffffffffffffffffffffff1681526020808701519082015282151581830152905163ffffffff8816917f7d15a6eebaa019ea7d5b7d38937c51ebd3befbfdf51bb630a694fd28635bbcba919081900360600190a25b5050505080600101905061137c565b600454604080516002805460806020820284018101909452606083810182815290958695600095869563ffffffff9093169486949193928492918491879085015b828210156115ec5760008481526020908190206040805160808101825260028602909201805473ffffffffffffffffffffffffffffffffffffffff90811684526001918201549081168486015260ff740100000000000000000000000000000000000000008204811693850193909352750100000000000000000000000000000000000000000090049091166060830152908352909201910161154e565b505050908252506001919091015461ffff80821660208085019190915262010000909204166040928301527fffffffffffffffffffffffffffffffff000000000000000000000000000000008a166000908152600a909152908120805460ff6801000000000000000082041696509293509163ffffffff80861691161080156116725750845b6000965090508560015b60028111611939578451515b6000808760000151518310156116e35787518051849081106116ac576116ac614849565b6020026020010151602001519150876000015183815181106116d0576116d0614849565b602002602001015160600151905061170a565b507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff905060005b73ffffffffffffffffffffffffffffffffffffffff82166000908152600188016020908152604080832081518083019092525463ffffffff811682526401000000009004821b63ffffffff19169181019190915290878061177a57508a63ffffffff16826000015163ffffffff16145b8061179a575073ffffffffffffffffffffffffffffffffffffffff848116145b80156117b05750602082015163ffffffff191615155b9050801561186d57856001036117d0576117c987614957565b965061186d565b85600203610182576117e560ff84168e61493c565b9c506117f08761498f565b9650838f888151811061180557611805614849565b602002602001019073ffffffffffffffffffffffffffffffffffffffff16908173ffffffffffffffffffffffffffffffffffffffff168152505081602001518e888151811061185657611856614849565b63ffffffff19909216602092830291909101909101525b84156118835761187c8561498f565b945061188c565b50505050611895565b50505050611688565b81600103611928578267ffffffffffffffff8111156118b6576118b6613e52565b6040519080825280602002602001820160405280156118df578160200160208202803683370190505b509a508267ffffffffffffffff8111156118fb576118fb613e52565b604051908082528060200260200182016040528015611924578160200160208202803683370190505b5099505b5061193281614957565b905061167c565b5050505050509193509193565b600c5460609060009061195984866149c4565b11611965575081611988565b600c5484101561198457600c5461197d9085906149d7565b9050611988565b5060005b60008167ffffffffffffffff8111156119a3576119a3613e52565b604051908082528060200260200182016040528015611a2157816020015b6040805160c08101825260008082526020808301829052928201819052606082018190526080820181905260a082015282527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9092019101816119c15790505b50905060005b82811015611b5f57600c611a3b82886149c4565b81548110611a4b57611a4b614849565b600091825260209091206040805160c081019091526002909202018054829060ff166004811115611a7e57611a7e6146d8565b6004811115611a8f57611a8f6146d8565b81528154610100810467ffffffffffffffff1660208301526901000000000000000000810460ff16151560408301526a0100000000000000000000900473ffffffffffffffffffffffffffffffffffffffff166060820152600190910154608081811b7fffffffffffffffffffffffffffffffff0000000000000000000000000000000090811682850152700100000000000000000000000000000000909204901b1660a0909101528251839083908110611b4c57611b4c614849565b6020908102919091010152600101611a27565b50949350505050565b611b706126a4565b61093a8161373b565b606060008080611b91610954368790038701876148a7565b6000818152600660209081526040918290208251608081018452905460ff81161515808352610100820463ffffffff90811694840185905265010000000000830461ffff169584019590955267010000000000000090910478ffffffffffffffffffffffffffffffffffffffffffffffffff166060830152600454909650939450929091169003611dd85760408101516060820151909450611c3281613830565b60ff1667ffffffffffffffff811115611c4d57611c4d613e52565b604051908082528060200260200182016040528015611c76578160200160208202803683370190505b506002805460408051602080840282018101909252828152939950600093929190849084015b82821015611d3a5760008481526020908190206040805160808101825260028602909201805473ffffffffffffffffffffffffffffffffffffffff90811684526001918201549081168486015260ff7401000000000000000000000000000000000000000082048116938501939093527501000000000000000000000000000000000000000000900490911660608301529083529092019101611c9c565b5050505090506000805b82518160ff161015611dd357611d5a84826136d6565b15611dc357828160ff1681518110611d7457611d74614849565b602002602001015160000151898381518110611d9257611d92614849565b73ffffffffffffffffffffffffffffffffffffffff90921660209283029190910190910152611dc082614957565b91505b611dcc816149ea565b9050611d44565b505050505b50509193909250565b60606000611dee8361389f565b9392505050565b7fffffffffffffffffffffffffffffffff0000000000000000000000000000000081166000908152600a60205260408120805463ffffffff858116911614611dee57805463ffffffff19811663ffffffff861690811783556003547fffffffffffffffffffffffffffffffffffffffffffffffffffff000000000000909216176201000090910461ffff1664010000000002177fffffffffffffffffffffffffffffffffffffffffffffffff0000ffffffffffff1680825568010000000000000000900460ff1615611dee57600260005b8154811015611fcd576000826000018281548110611ee657611ee6614849565b6000918252602080832060016002909302018281015473ffffffffffffffffffffffffffffffffffffffff1684529187019052604090912080549192509063ffffffff808a169116108015611f4d57508054640100000000900460201b63ffffffff191615155b15611fc357805463ffffffff191663ffffffff891617815560018201548554750100000000000000000000000000000000000000000090910460ff16908690600690611fa89084906601000000000000900461ffff1661493c565b92506101000a81548161ffff021916908361ffff1602179055505b5050600101611ec6565b50509392505050565b6000806001896001811115611fed57611fed6146d8565b148061200a57506000896001811115612008576120086146d8565b145b61201657612016614878565b8480612037575073ffffffffffffffffffffffffffffffffffffffff878116145b80612056575073ffffffffffffffffffffffffffffffffffffffff8716155b1561207c57600089600181111561206f5761206f6146d8565b1461207c5761207c614878565b73ffffffffffffffffffffffffffffffffffffffff8716600090815260018401602090815260409182902082518084019093525463ffffffff811683526401000000009004811b63ffffffff191690820152845460ff16801561210d575073ffffffffffffffffffffffffffffffffffffffff888116148061210d57508863ffffffff16816000015163ffffffff16145b80156121235750602081015163ffffffff191615155b801561214b5750866020015163ffffffff1916816020015163ffffffff1916148061214b5750855b156122765773ffffffffffffffffffffffffffffffffffffffff881660009081526001858101602052604082209190915585548554919450610100900460ff169085906006906121aa9084906601000000000000900461ffff16614a09565b825461010092830a61ffff818102199092169282160291909117909255895188546020808d01518a54604080517fffffffffffffffffffffffffffffffff0000000000000000000000000000000090961686529590930460ff169184019190915263ffffffff1916828401526601000000000000900490921660608301525173ffffffffffffffffffffffffffffffffffffffff8b16925063ffffffff8c16917fa96a155bd67c927a6c056befbd979b78465e2b2f1276bf7d4e90a31d4f430aa8919081900360800190a35b6000808b600181111561228b5761228b6146d8565b1480156122b3575083806122b3575073ffffffffffffffffffffffffffffffffffffffff8916155b90508080156122cf5750845468010000000000000000900460ff165b80156122e157506122df856138fb565b155b156123b45784547fffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffff168555600b80546001945060009061232a9067ffffffffffffffff16614a24565b91906101000a81548167ffffffffffffffff021916908367ffffffffffffffff1602179055507f65d0e78c3625f0956f58610cf0fb157eaf627683258875ef29af2f71d25ac8fd88600001516040516123ab91907fffffffffffffffffffffffffffffffff0000000000000000000000000000000091909116815260200190565b60405180910390a15b83806123bd5750825b15612605576000808c60018111156123d7576123d76146d8565b036123f25787156123ea5750600361240f565b50600261240f565b60018c6001811115612406576124066146d8565b03610182575060015b600c6040518060c0016040528083600481111561242e5761242e6146d8565b81526020014267ffffffffffffffff168152885468010000000000000000900460ff16151560208083019190915273ffffffffffffffffffffffffffffffffffffffff8e1660408301528c517fffffffffffffffffffffffffffffffff00000000000000000000000000000000166060830152600060809092018290528354600180820186559483529120825160029092020180549293909283917fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0090911690836004811115612500576125006146d8565b0217905550602082015181546040840151606085015173ffffffffffffffffffffffffffffffffffffffff166a0100000000000000000000027fffff0000000000000000000000000000000000000000ffffffffffffffffffff9115156901000000000000000000027fffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffff67ffffffffffffffff90951661010002949094167fffffffffffffffffffffffffffffffffffffffffffff000000000000000000ff90931692909217929092179190911617815560808083015160a090930151811c7001000000000000000000000000000000000292901c9190911760019091015550612696565b8751602080840151818b0151604080517fffffffffffffffffffffffffffffffff00000000000000000000000000000000909516855263ffffffff1992831693850193909352169082015273ffffffffffffffffffffffffffffffffffffffff8a16907fbabb0d7099e6ca14a29fad2a2cfb4fda2bd30f97cb3c27e546174bfb4277c1cc9060600160405180910390a25b505097509795505050505050565b60005473ffffffffffffffffffffffffffffffffffffffff163314612725576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4f6e6c792063616c6c61626c65206279206f776e6572000000000000000000006044820152606401610418565b565b6000611dee8373ffffffffffffffffffffffffffffffffffffffff841661395c565b6000611dee8373ffffffffffffffffffffffffffffffffffffffff8416613a56565b61277481613aa5565b6127aa576040517f35be3ac800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b602081015160038054604084015161ffff908116620100000263ffffffff199092169316929092179190911790555b6002541561298e5760028054600091906127f5906001906149d7565b8154811061280557612805614849565b60009182526020808320604080516080810182526002948502909201805473ffffffffffffffffffffffffffffffffffffffff90811680855260019092015480821685870190815260ff740100000000000000000000000000000000000000008304811687870152750100000000000000000000000000000000000000000090920490911660608601529187526005855282872080547fffffffffffffffffffffffffffffffffffffffffffffffffffff00000000000016905590511685526009909252922080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00001690558054919250908061290457612904614a66565b60008281526020902060027fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9092019182020180547fffffffffffffffffffffffff000000000000000000000000000000000000000016815560010180547fffffffffffffffffffff000000000000000000000000000000000000000000001690559055506127d9565b60005b815151811015612ac1578151805160029190839081106129b3576129b3614849565b6020908102919091018101518254600181810185556000948552938390208251600290920201805473ffffffffffffffffffffffffffffffffffffffff9283167fffffffffffffffffffffffff0000000000000000000000000000000000000000909116178155928201519284018054604084015160609094015160ff9081167501000000000000000000000000000000000000000000027fffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffff9190951674010000000000000000000000000000000000000000027fffffffffffffffffffffff0000000000000000000000000000000000000000009092169590931694909417939093171617905501612991565b5060048054600090612ad89063ffffffff16614a95565b82546101009290920a63ffffffff8181021990931691831602179091556004541660005b82515160ff82161015612c5557600083600001518260ff1681518110612b2457612b24614849565b602090810291909101810151604080516060808201835263ffffffff80891683528385015160ff908116848801908152898216858701908152875173ffffffffffffffffffffffffffffffffffffffff908116600090815260058b528881209751885494519351861665010000000000027fffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffff948716640100000000027fffffffffffffffffffffffffffffffffffffffffffffffffffffff00000000009096169190971617939093179190911693909317909455858701519091168352600990955291902080549190920151909216610100027fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000090921691909117600117905550612c4e816149ea565b9050612afc565b5073ffffffffffffffffffffffffffffffffffffffff60005260096020527f3bddde647ecb7992f4c710d4e1d59d07614508581f7c22c879a79d28544538a780547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00001660011790556004805463ffffffff438116640100000000027fffffffffffffffffffffffffffffffffffffffffffffffff00000000ffffffff90921691909117909155604051908216907f8c49fda8177c5c8c768eb39634bc6773695c7181711537b822451c12b2efd2a990612d2f908590614ab8565b60405180910390a26040805160c081018252600480825267ffffffffffffffff421660208301526000928201839052606082018390526080820183905260a08201839052600c80546001808201835591909452825160029094027fdf6966c971051c3d54ec59162606531493a51404a002842f56009d7e5cf4a8c701805493949093909284927fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0090921691908490811115612dec57612dec6146d8565b0217905550602082015181546040840151606085015173ffffffffffffffffffffffffffffffffffffffff166a0100000000000000000000027fffff0000000000000000000000000000000000000000ffffffffffffffffffff9115156901000000000000000000027fffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffff67ffffffffffffffff90951661010002949094167fffffffffffffffffffffffffffffffffffffffffffff000000000000000000ff90931692909217929092179190911617815560808083015160a090930151811c7001000000000000000000000000000000000292901c919091176001909101555050565b73ffffffffffffffffffffffffffffffffffffffff811660009081526001830160205260408120541515611dee565b8151600003612f59576040517f55e9b08b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7fffffffffffffffffffffffffffffffff000000000000000000000000000000008316600090815260018201602052604090205460ff1615613007576040517f078f340000000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff851660048201527fffffffffffffffffffffffffffffffff0000000000000000000000000000000084166024820152604401610418565b7fffffffffffffffffffffffffffffffff000000000000000000000000000000008316600090815260018281016020526040822080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016909117905560045463ffffffff16905b83518110156136ce57600181101580156130ed575083818151811061309657613096614849565b60200260200101516fffffffffffffffffffffffffffffffff1916846001836130bf91906149d7565b815181106130cf576130cf614849565b60200260200101516fffffffffffffffffffffffffffffffff191610155b15613124576040517f2432d8ea00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600084828151811061313857613138614849565b60200260200101519050600061314e8483611df5565b73ffffffffffffffffffffffffffffffffffffffff8981166000818152600184016020908152604080832081518083019092525463ffffffff811682526401000000009004821b63ffffffff19169181019190915293945091148015906131be5750815163ffffffff8088169116105b806131d25750602082015163ffffffff1916155b15613225575085548254600091610100900460ff169084906006906132069084906601000000000000900461ffff1661493c565b92506101000a81548161ffff021916908361ffff16021790555061322c565b5060208101515b60408051808201825263ffffffff88168152815163ffffffff1984166020828101919091527fffffffffffffffffffffffffffffffff000000000000000000000000000000008d16828501528351808303850181526060909201909352805190830120909182019063ffffffff1916905273ffffffffffffffffffffffffffffffffffffffff8b166000818152600186016020908152604090912083518285015190921c6401000000000263ffffffff92831617905589549294509091908816907f8137bc8a8d712aaa27bfc6506d5566ac405618bd53f9831b8ca6b6fe5442ee7a9087908d9060ff610100909104166133234290565b6020898101518b54604080517fffffffffffffffffffffffffffffffff000000000000000000000000000000009889168152979096169287019290925260ff9093169385019390935267ffffffffffffffff16606084015263ffffffff191660808301526601000000000000900461ffff1660a082015260c00160405180910390a363ffffffff1981161580156133c85750825468010000000000000000900460ff16155b80156133d857506133d8836138fb565b156134c35782547fffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffff1668010000000000000000178355600b80546000906134289067ffffffffffffffff16614acb565b91906101000a81548167ffffffffffffffff021916908367ffffffffffffffff1602179055508563ffffffff167fcfdbfd8ce9a56b5f7c202c0e102184d24f47ca87121dc165063fc4c290957bde8561347e4290565b604080517fffffffffffffffffffffffffffffffff00000000000000000000000000000000909316835267ffffffffffffffff90911660208301520160405180910390a25b6040805160c081018252600080825267ffffffffffffffff42166020830152855460ff680100000000000000009091041615159282019290925273ffffffffffffffffffffffffffffffffffffffff8c1660608201527fffffffffffffffffffffffffffffffff0000000000000000000000000000000086811660808301528b1660a0820152600c80546001808201835591909352815160029093027fdf6966c971051c3d54ec59162606531493a51404a002842f56009d7e5cf4a8c701805492939092909183917fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016908360048111156135c0576135c06146d8565b0217905550602082015181546040840151606085015173ffffffffffffffffffffffffffffffffffffffff166a0100000000000000000000027fffff0000000000000000000000000000000000000000ffffffffffffffffffff9115156901000000000000000000027fffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffff67ffffffffffffffff90951661010002949094167fffffffffffffffffffffffffffffffffffffffffffff000000000000000000ff90931692909217929092179190911617815560808083015160a090930151811c7001000000000000000000000000000000000292901c9190911760019182015594909401935061306f92505050565b505050505050565b600060108260ff16106136eb576136eb614878565b50600160ff82161b821678ffffffffffffffffffffffffffffffffffffffffffffffffff16151592915050565b600060108260ff161061372d5761372d614878565b50600160ff919091161b1790565b3373ffffffffffffffffffffffffffffffffffffffff8216036137ba576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c660000000000000000006044820152606401610418565b600180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff83811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b60006201000078ffffffffffffffffffffffffffffffffffffffffffffffffff83161061385f5761385f614878565b78ffffffffffffffffffffffffffffffffffffffffffffffffff8216156105ac5761388b600183614ae8565b90911690613898816149ea565b905061385f565b6060816000018054806020026020016040519081016040528092919081815260200182805480156138ef57602002820191906000526020600020905b8154815260200190600101908083116138db575b50505050509050919050565b73ffffffffffffffffffffffffffffffffffffffff600090815260018201602090815260408220546401000000009004901b63ffffffff19161515806105a65750505461ffff64010000000082048116660100000000000090920416101590565b60008181526001830160205260408120548015613a455760006139806001836149d7565b8554909150600090613994906001906149d7565b90508181146139f95760008660000182815481106139b4576139b4614849565b90600052602060002001549050808760000184815481106139d7576139d7614849565b6000918252602080832090910192909255918252600188019052604090208390555b8554869080613a0a57613a0a614a66565b6001900381819060005260206000200160009055905585600101600086815260200190815260200160002060009055600193505050506105a6565b60009150506105a6565b5092915050565b6000818152600183016020526040812054613a9d575081546001818101845560008481526020808220909301849055845484825282860190935260409020919091556105a6565b5060006105a6565b8051516000901580613ab957508151516010105b80613aca5750602082015161ffff16155b80613adb5750604082015161ffff16155b15613ae857506000919050565b60008060008460000151516002613aff9190614b1a565b67ffffffffffffffff811115613b1757613b17613e52565b604051908082528060200260200182016040528015613b40578160200160208202803683370190505b50905060005b855151811015613d1157600086600001518281518110613b6857613b68614849565b60200260200101519050600073ffffffffffffffffffffffffffffffffffffffff16816000015173ffffffffffffffffffffffffffffffffffffffff161480613bc95750602081015173ffffffffffffffffffffffffffffffffffffffff16155b80613bec5750602081015173ffffffffffffffffffffffffffffffffffffffff16155b80613c115750602081015173ffffffffffffffffffffffffffffffffffffffff908116145b80613c315750604081015160ff16158015613c315750606081015160ff16155b15613c43575060009695505050505050565b805183613c51846002614b1a565b613c5c9060006149c4565b81518110613c6c57613c6c614849565b73ffffffffffffffffffffffffffffffffffffffff90921660209283029190910182015281015183613c9f846002614b1a565b613caa9060016149c4565b81518110613cba57613cba614849565b73ffffffffffffffffffffffffffffffffffffffff909216602092830291909101909101526040810151613cf19060ff16866149c4565b9450806060015160ff1684613d0691906149c4565b935050600101613b46565b5060005b8151811015613dc3576000828281518110613d3257613d32614849565b602002602001015190506000826001613d4b91906149c4565b90505b8351811015613db957838181518110613d6957613d69614849565b602002602001015173ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1603613db157506000979650505050505050565b600101613d4e565b5050600101613d15565b50846020015161ffff168310158015613de45750846040015161ffff168210155b95945050505050565b60008151808452602080850194506020840160005b83811015613e3457815173ffffffffffffffffffffffffffffffffffffffff1687529582019590820190600101613e02565b509495945050505050565b602081526000611dee6020830184613ded565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6040805190810167ffffffffffffffff81118282101715613ea457613ea4613e52565b60405290565b6040516060810167ffffffffffffffff81118282101715613ea457613ea4613e52565b6040516080810167ffffffffffffffff81118282101715613ea457613ea4613e52565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff81118282101715613f3757613f37613e52565b604052919050565b600067ffffffffffffffff821115613f5957613f59613e52565b5060051b60200190565b80357fffffffffffffffffffffffffffffffff00000000000000000000000000000000811681146105ac57600080fd5b600060408284031215613fa557600080fd5b613fad613e81565b9050613fb882613f63565b8152602082013563ffffffff1981168114613fd257600080fd5b602082015292915050565b60006020808385031215613ff057600080fd5b823567ffffffffffffffff81111561400757600080fd5b8301601f8101851361401857600080fd5b803561402b61402682613f3f565b613ef0565b8082825260208201915060208360061b85010192508783111561404d57600080fd5b6020840193505b82841015614078576140668885613f93565b82528482019150604084019350614054565b979650505050505050565b60006020808352835180602085015260005b818110156140b157858101830151858201604001528201614095565b5060006040828601015260407fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8301168501019250505092915050565b60006020828403121561410257600080fd5b611dee82613f63565b803573ffffffffffffffffffffffffffffffffffffffff811681146105ac57600080fd5b600082601f83011261414057600080fd5b8135602061415061402683613f3f565b8083825260208201915060208460051b87010193508684111561417257600080fd5b602086015b84811015614195576141888161410b565b8352918301918301614177565b509695505050505050565b600080604083850312156141b357600080fd5b823567ffffffffffffffff808211156141cb57600080fd5b6141d78683870161412f565b935060208501359150808211156141ed57600080fd5b506141fa8582860161412f565b9150509250929050565b8051606080845281518482018190526000926080916020918201918388019190865b82811015614280578451805173ffffffffffffffffffffffffffffffffffffffff908116865283820151168386015260408082015160ff908116918701919091529088015116878501529381019392850192600101614226565b508781015161ffff81168a83015295505050604086015193506142a9604088018561ffff169052565b9695505050505050565b600063ffffffff808616835280851660208401525060606040830152613de46060830184614204565b803560ff811681146105ac57600080fd5b803561ffff811681146105ac57600080fd5b6000602080838503121561431257600080fd5b823567ffffffffffffffff8082111561432a57600080fd5b8185019150606080838803121561434057600080fd5b614348613eaa565b83358381111561435757600080fd5b84019250601f8301881361436a57600080fd5b823561437861402682613f3f565b81815260079190911b8401860190868101908a83111561439757600080fd5b948701945b82861015614409576080868c0312156143b55760008081fd5b6143bd613ecd565b6143c68761410b565b81526143d389880161410b565b8982015260406143e48189016142dc565b908201526143f38787016142dc565b818701528252608095909501949087019061439c565b83525061441990508486016142ed565b85820152614429604085016142ed565b6040820152979650505050505050565b60006040828403121561444b57600080fd5b50919050565b6000806040838503121561446457600080fd5b61446d83613f63565b915060208084013567ffffffffffffffff81111561448a57600080fd5b8401601f8101861361449b57600080fd5b80356144a961402682613f3f565b81815260059190911b820183019083810190888311156144c857600080fd5b928401925b828410156144ed576144de84613f63565b825292840192908401906144cd565b80955050505050509250929050565b6000602080838503121561450f57600080fd5b823567ffffffffffffffff81111561452657600080fd5b8301601f8101851361453757600080fd5b803561454561402682613f3f565b81815260079190911b8201830190838101908783111561456457600080fd5b928401925b8284101561407857608084890312156145825760008081fd5b61458a613eaa565b6145938561410b565b81526145a189878701613f93565b86820152606085013580151581146145b95760008081fd5b604082015282526080939093019290840190614569565b600080602083850312156145e357600080fd5b823567ffffffffffffffff808211156145fb57600080fd5b818501915085601f83011261460f57600080fd5b81358181111561461e57600080fd5b8660208260061b850101111561463357600080fd5b60209290920196919550909350505050565b6080815260006146586080830187613ded565b82810360208481019190915286518083528782019282019060005b8181101561469657845163ffffffff191683529383019391830191600101614673565b505061ffff96909616604085015250505090151560609091015292915050565b600080604083850312156146c957600080fd5b50508035926020909101359150565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b60208082528251828201819052600091906040908185019086840185805b838110156147f2578251805160058110614766577f4e487b710000000000000000000000000000000000000000000000000000000084526021600452602484fd5b86528088015167ffffffffffffffff16888701528681015115158787015260608082015173ffffffffffffffffffffffffffffffffffffffff16908701526080808201517fffffffffffffffffffffffffffffffff000000000000000000000000000000009081169188019190915260a091820151169086015260c09094019391860191600101614725565b509298975050505050505050565b60006020828403121561481257600080fd5b611dee8261410b565b60608152600061482e6060830186613ded565b61ffff94909416602083015250901515604090910152919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052600160045260246000fd5b6000604082840312156148b957600080fd5b6148c1613e81565b6148ca8361410b565b8152602083013560208201528091505092915050565b815173ffffffffffffffffffffffffffffffffffffffff16815260208083015190820152604081016105a6565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b61ffff818116838216019080821115613a4f57613a4f61490d565b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82036149885761498861490d565b5060010190565b60008161499e5761499e61490d565b507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0190565b808201808211156105a6576105a661490d565b818103818111156105a6576105a661490d565b600060ff821660ff8103614a0057614a0061490d565b60010192915050565b61ffff828116828216039080821115613a4f57613a4f61490d565b600067ffffffffffffffff821680614a3e57614a3e61490d565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0192915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603160045260246000fd5b600063ffffffff808316818103614aae57614aae61490d565b6001019392505050565b602081526000611dee6020830184614204565b600067ffffffffffffffff808316818103614aae57614aae61490d565b78ffffffffffffffffffffffffffffffffffffffffffffffffff828116828216039080821115613a4f57613a4f61490d565b80820281158282048414176105a6576105a661490d56fea164736f6c6343000818000a

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

00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000006000000000000000000000000bf469bd18b258f1eb3710a6b53778d650d193d410000000000000000000000007be2c5c3e4b48439314119486008e2e30b32319700000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001000000000000000000000000765293e01dc24d9aaad886c5bf78767930c4b34e000000000000000000000000fd3a26bdf612494b4c7dc0b14c1e8304bbec046500000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001000000000000000000000000e9005e15eff73fb2c08a44b72003c01caa7a8fb0000000000000000000000000933c057f8e10dcd733c6c836ad3a36a956fcec7300000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001000000000000000000000000dffd4d4556edbb629e2d15756da736582625d318000000000000000000000000857626295e895a2f44ae1c1f57d239bb515d9cb80000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000100000000000000000000000082dd2be746810245f978a20a28e86dff8b9cfe2f0000000000000000000000001f645d004dcaa833d439fed98c07bcf6c77d7d900000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000100000000000000000000000079f44a2acd6bb225b6ec113eb435fe463b4e201b00000000000000000000000073623ccfb74e22bf8d90cca342d736b8e8ff905900000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001

-----Decoded View---------------
Arg [0] : config (tuple): System.Collections.Generic.List`1[Nethereum.ABI.FunctionEncoding.ParameterOutput]

-----Encoded View---------------
29 Constructor Arguments found :
Arg [0] : 0000000000000000000000000000000000000000000000000000000000000020
Arg [1] : 0000000000000000000000000000000000000000000000000000000000000060
Arg [2] : 0000000000000000000000000000000000000000000000000000000000000002
Arg [3] : 0000000000000000000000000000000000000000000000000000000000000002
Arg [4] : 0000000000000000000000000000000000000000000000000000000000000006
Arg [5] : 000000000000000000000000bf469bd18b258f1eb3710a6b53778d650d193d41
Arg [6] : 0000000000000000000000007be2c5c3e4b48439314119486008e2e30b323197
Arg [7] : 0000000000000000000000000000000000000000000000000000000000000001
Arg [8] : 0000000000000000000000000000000000000000000000000000000000000001
Arg [9] : 000000000000000000000000765293e01dc24d9aaad886c5bf78767930c4b34e
Arg [10] : 000000000000000000000000fd3a26bdf612494b4c7dc0b14c1e8304bbec0465
Arg [11] : 0000000000000000000000000000000000000000000000000000000000000001
Arg [12] : 0000000000000000000000000000000000000000000000000000000000000001
Arg [13] : 000000000000000000000000e9005e15eff73fb2c08a44b72003c01caa7a8fb0
Arg [14] : 000000000000000000000000933c057f8e10dcd733c6c836ad3a36a956fcec73
Arg [15] : 0000000000000000000000000000000000000000000000000000000000000001
Arg [16] : 0000000000000000000000000000000000000000000000000000000000000001
Arg [17] : 000000000000000000000000dffd4d4556edbb629e2d15756da736582625d318
Arg [18] : 000000000000000000000000857626295e895a2f44ae1c1f57d239bb515d9cb8
Arg [19] : 0000000000000000000000000000000000000000000000000000000000000001
Arg [20] : 0000000000000000000000000000000000000000000000000000000000000001
Arg [21] : 00000000000000000000000082dd2be746810245f978a20a28e86dff8b9cfe2f
Arg [22] : 0000000000000000000000001f645d004dcaa833d439fed98c07bcf6c77d7d90
Arg [23] : 0000000000000000000000000000000000000000000000000000000000000001
Arg [24] : 0000000000000000000000000000000000000000000000000000000000000001
Arg [25] : 00000000000000000000000079f44a2acd6bb225b6ec113eb435fe463b4e201b
Arg [26] : 00000000000000000000000073623ccfb74e22bf8d90cca342d736b8e8ff9059
Arg [27] : 0000000000000000000000000000000000000000000000000000000000000001
Arg [28] : 0000000000000000000000000000000000000000000000000000000000000001


Block Transaction Gas Used Reward
view all blocks validated

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

Validator Index Block Amount
View All Withdrawals

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

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