Address Details
contract

0x926E88a280902Bfff5047693B9CeAfdb9F4d5095

Contract Name
FederatedAttestations
Creator
0x456f41–3584da at 0x19a887–69ba54
Balance
0 CELO ( )
Locked CELO Balance
0.00 CELO
Voting CELO Balance
0.00 CELO
Pending Unlocked Gold
0.00 CELO
Tokens
Fetching tokens...
Transactions
0 Transactions
Transfers
0 Transfers
Gas Used
Fetching gas used...
Last Balance Update
23683589
This contract has been partially verified via Sourcify. View contract in Sourcify repository
Contract name:
FederatedAttestations




Optimization enabled
false
Compiler version
v0.5.13+commit.5b0b510c




EVM Version
istanbul




Verified at
2023-01-23T13:08:28.111030Z

/Users/carter/git/clabs/celo-monorepo/packages/protocol/contracts/identity/FederatedAttestations.sol

pragma solidity ^0.5.13;

import "openzeppelin-solidity/contracts/math/SafeMath.sol";
import "openzeppelin-solidity/contracts/ownership/Ownable.sol";
import "openzeppelin-solidity/contracts/token/ERC20/IERC20.sol";
import "openzeppelin-solidity/contracts/utils/SafeCast.sol";

import "./interfaces/IFederatedAttestations.sol";
import "../common/interfaces/IAccounts.sol";
import "../common/interfaces/ICeloVersionedContract.sol";

import "../common/Initializable.sol";
import "../common/UsingRegistryV2.sol";
import "../common/Signatures.sol";

/**
 * @title Contract mapping identifiers to accounts
 */
contract FederatedAttestations is
  IFederatedAttestations,
  ICeloVersionedContract,
  Ownable,
  Initializable,
  UsingRegistryV2
{
  using SafeMath for uint256;
  using SafeCast for uint256;

  struct OwnershipAttestation {
    address account;
    address signer;
    uint64 issuedOn;
    uint64 publishedOn;
    // using uint64 to allow for extra space to add parameters
  }

  // Mappings from identifier <-> attestation are separated by issuer,
  // *requiring* users to specify issuers when retrieving attestations.
  // Maintaining bidirectional mappings (vs. in Attestations.sol) makes it possible
  // to perform lookups by identifier or account without indexing event data.

  // identifier -> issuer -> attestations
  mapping(bytes32 => mapping(address => OwnershipAttestation[])) public identifierToAttestations;
  // account -> issuer -> identifiers
  mapping(address => mapping(address => bytes32[])) public addressToIdentifiers;

  // unique attestation hash -> isRevoked
  mapping(bytes32 => bool) public revokedAttestations;

  bytes32 public eip712DomainSeparator;
  bytes32 public constant EIP712_OWNERSHIP_ATTESTATION_TYPEHASH = keccak256(
    abi.encodePacked(
      "OwnershipAttestation(bytes32 identifier,address issuer,",
      "address account,address signer,uint64 issuedOn)"
    )
  );

  // Changing any of these constraints will require re-benchmarking
  // and checking assumptions for batch revocation.
  // These can only be modified by releasing a new version of this contract.
  uint256 public constant MAX_ATTESTATIONS_PER_IDENTIFIER = 20;
  uint256 public constant MAX_IDENTIFIERS_PER_ADDRESS = 20;

  event EIP712DomainSeparatorSet(bytes32 eip712DomainSeparator);
  event AttestationRegistered(
    bytes32 indexed identifier,
    address indexed issuer,
    address indexed account,
    address signer,
    uint64 issuedOn,
    uint64 publishedOn
  );
  event AttestationRevoked(
    bytes32 indexed identifier,
    address indexed issuer,
    address indexed account,
    address signer,
    uint64 issuedOn,
    uint64 publishedOn
  );

  /**
   * @notice Sets initialized == true on implementation contracts
   * @param test Set to true to skip implementation initialization
   */
  constructor(bool test) public Initializable(test) {}

  /**
   * @notice Returns the storage, major, minor, and patch version of the contract.
   * @return The storage, major, minor, and patch version of the contract.
   */
  function getVersionNumber() external pure returns (uint256, uint256, uint256, uint256) {
    return (1, 1, 0, 0);
  }

  /**
   * @notice Used in place of the constructor to allow the contract to be upgradable via proxy.
   */
  function initialize() external initializer {
    _transferOwnership(msg.sender);
    setEip712DomainSeparator();
  }

  /**
   * @notice Registers an attestation directly from the issuer
   * @param identifier Hash of the identifier to be attested
   * @param account Address of the account being mapped to the identifier
   * @param issuedOn Time at which the issuer issued the attestation in Unix time 
   * @dev Attestation signer and issuer in storage is set to msg.sender
   * @dev Throws if an attestation with the same (identifier, issuer, account) already exists
   */
  function registerAttestationAsIssuer(bytes32 identifier, address account, uint64 issuedOn)
    external
  {
    _registerAttestation(identifier, msg.sender, account, msg.sender, issuedOn);
  }

  /**
   * @notice Registers an attestation with a valid signature
   * @param identifier Hash of the identifier to be attested
   * @param issuer Address of the attestation issuer
   * @param account Address of the account being mapped to the identifier
   * @param issuedOn Time at which the issuer issued the attestation in Unix time 
   * @param signer Address of the signer of the attestation
   * @param v The recovery id of the incoming ECDSA signature
   * @param r Output value r of the ECDSA signature
   * @param s Output value s of the ECDSA signature
   * @dev Throws if an attestation with the same (identifier, issuer, account) already exists
   */
  function registerAttestation(
    bytes32 identifier,
    address issuer,
    address account,
    address signer,
    uint64 issuedOn,
    uint8 v,
    bytes32 r,
    bytes32 s
  ) external {
    validateAttestationSig(identifier, issuer, account, signer, issuedOn, v, r, s);
    _registerAttestation(identifier, issuer, account, signer, issuedOn);
  }

  /**
   * @notice Revokes an attestation 
   * @param identifier Hash of the identifier to be revoked
   * @param issuer Address of the attestation issuer
   * @param account Address of the account mapped to the identifier
   * @dev Throws if sender is not the issuer, signer, or account
   */
  function revokeAttestation(bytes32 identifier, address issuer, address account) external {
    require(
      account == msg.sender ||
        // Minor gas optimization to prevent storage lookup in Accounts.sol if issuer == msg.sender
        issuer == msg.sender ||
        getAccounts().attestationSignerToAccount(msg.sender) == issuer,
      "Sender does not have permission to revoke this attestation"
    );
    _revokeAttestation(identifier, issuer, account);
  }

  /**
   * @notice Revokes attestations [identifiers <-> accounts] from issuer
   * @param issuer Address of the issuer of all attestations to be revoked
   * @param identifiers Hash of the identifiers
   * @param accounts Addresses of the accounts mapped to the identifiers
   *   at the same indices
   * @dev Throws if the number of identifiers and accounts is not the same
   * @dev Throws if sender is not the issuer or currently registered signer of issuer
   * @dev Throws if an attestation is not found for identifiers[i] <-> accounts[i]
   */
  function batchRevokeAttestations(
    address issuer,
    bytes32[] calldata identifiers,
    address[] calldata accounts
  ) external {
    require(identifiers.length == accounts.length, "Unequal number of identifiers and accounts");
    require(
      issuer == msg.sender || getAccounts().attestationSignerToAccount(msg.sender) == issuer,
      "Sender does not have permission to revoke attestations from this issuer"
    );

    for (uint256 i = 0; i < identifiers.length; i = i.add(1)) {
      _revokeAttestation(identifiers[i], issuer, accounts[i]);
    }
  }

  /**
   * @notice Returns info about attestations for `identifier` produced by 
   *    signers of `trustedIssuers`
   * @param identifier Hash of the identifier
   * @param trustedIssuers Array of n issuers whose attestations will be included
   * @return countsPerIssuer Array of number of attestations returned per issuer
   *          For m (== sum([0])) found attestations: 
   * @return accounts Array of m accounts 
   * @return signers Array of m signers
   * @return issuedOns Array of m issuedOns
   * @return publishedOns Array of m publishedOns
   * @dev Adds attestation info to the arrays in order of provided trustedIssuers
   * @dev Expectation that only one attestation exists per (identifier, issuer, account)
   */
  function lookupAttestations(bytes32 identifier, address[] calldata trustedIssuers)
    external
    view
    returns (
      uint256[] memory countsPerIssuer,
      address[] memory accounts,
      address[] memory signers,
      uint64[] memory issuedOns,
      uint64[] memory publishedOns
    )
  {
    uint256 totalAttestations;
    (totalAttestations, countsPerIssuer) = getNumAttestations(identifier, trustedIssuers);

    accounts = new address[](totalAttestations);
    signers = new address[](totalAttestations);
    issuedOns = new uint64[](totalAttestations);
    publishedOns = new uint64[](totalAttestations);

    totalAttestations = 0;
    OwnershipAttestation[] memory attestationsPerIssuer;

    for (uint256 i = 0; i < trustedIssuers.length; i = i.add(1)) {
      attestationsPerIssuer = identifierToAttestations[identifier][trustedIssuers[i]];
      for (uint256 j = 0; j < attestationsPerIssuer.length; j = j.add(1)) {
        accounts[totalAttestations] = attestationsPerIssuer[j].account;
        signers[totalAttestations] = attestationsPerIssuer[j].signer;
        issuedOns[totalAttestations] = attestationsPerIssuer[j].issuedOn;
        publishedOns[totalAttestations] = attestationsPerIssuer[j].publishedOn;
        totalAttestations = totalAttestations.add(1);
      }
    }
    return (countsPerIssuer, accounts, signers, issuedOns, publishedOns);
  }

  /**
   * @notice Returns identifiers mapped to `account` by signers of `trustedIssuers`
   * @param account Address of the account
   * @param trustedIssuers Array of n issuers whose identifier mappings will be used
   * @return countsPerIssuer Array of number of identifiers returned per issuer
   * @return identifiers Array (length == sum([0])) of identifiers
   * @dev Adds identifier info to the arrays in order of provided trustedIssuers
   * @dev Expectation that only one attestation exists per (identifier, issuer, account)
   */
  function lookupIdentifiers(address account, address[] calldata trustedIssuers)
    external
    view
    returns (uint256[] memory countsPerIssuer, bytes32[] memory identifiers)
  {
    uint256 totalIdentifiers;
    (totalIdentifiers, countsPerIssuer) = getNumIdentifiers(account, trustedIssuers);

    identifiers = new bytes32[](totalIdentifiers);
    bytes32[] memory identifiersPerIssuer;

    uint256 currIndex = 0;

    for (uint256 i = 0; i < trustedIssuers.length; i = i.add(1)) {
      identifiersPerIssuer = addressToIdentifiers[account][trustedIssuers[i]];
      for (uint256 j = 0; j < identifiersPerIssuer.length; j = j.add(1)) {
        identifiers[currIndex] = identifiersPerIssuer[j];
        currIndex = currIndex.add(1);
      }
    }
    return (countsPerIssuer, identifiers);
  }

  /**
   * @notice Validates the given attestation and signature
   * @param identifier Hash of the identifier to be attested
   * @param issuer Address of the attestation issuer
   * @param account Address of the account being mapped to the identifier
   * @param issuedOn Time at which the issuer issued the attestation in Unix time 
   * @param signer Address of the signer of the attestation
   * @param v The recovery id of the incoming ECDSA signature
   * @param r Output value r of the ECDSA signature
   * @param s Output value s of the ECDSA signature
   * @dev Throws if attestation has been revoked
   * @dev Throws if signer is not an authorized AttestationSigner of the issuer
   */
  function validateAttestationSig(
    bytes32 identifier,
    address issuer,
    address account,
    address signer,
    uint64 issuedOn,
    uint8 v,
    bytes32 r,
    bytes32 s
  ) public view {
    // attestationSignerToAccount instead of isSigner allows the issuer to act as its own signer
    require(
      getAccounts().attestationSignerToAccount(signer) == issuer,
      "Signer is not a currently authorized AttestationSigner for the issuer"
    );
    bytes32 structHash = getUniqueAttestationHash(identifier, issuer, account, signer, issuedOn);
    address guessedSigner = Signatures.getSignerOfTypedDataHash(
      eip712DomainSeparator,
      structHash,
      v,
      r,
      s
    );
    require(guessedSigner == signer, "Signature is invalid");
  }

  function getUniqueAttestationHash(
    bytes32 identifier,
    address issuer,
    address account,
    address signer,
    uint64 issuedOn
  ) public pure returns (bytes32) {
    return
      keccak256(
        abi.encode(
          EIP712_OWNERSHIP_ATTESTATION_TYPEHASH,
          identifier,
          issuer,
          account,
          signer,
          issuedOn
        )
      );
  }

  /**
   * @notice Sets the EIP712 domain separator for the Celo FederatedAttestations abstraction.
   */
  function setEip712DomainSeparator() internal {
    uint256 chainId;
    assembly {
      chainId := chainid
    }

    eip712DomainSeparator = keccak256(
      abi.encode(
        keccak256(
          "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"
        ),
        keccak256(bytes("FederatedAttestations")),
        keccak256("1.0"),
        chainId,
        address(this)
      )
    );
    emit EIP712DomainSeparatorSet(eip712DomainSeparator);
  }

  /**
   * @notice Helper function for lookupAttestations to calculate the
             total number of attestations completed for an identifier
             by each trusted issuer
   * @param identifier Hash of the identifier
   * @param trustedIssuers Array of n issuers whose attestations will be included
   * @return totalAttestations Sum total of attestations found
   * @return countsPerIssuer Array of number of attestations found per issuer
   */
  function getNumAttestations(bytes32 identifier, address[] memory trustedIssuers)
    internal
    view
    returns (uint256 totalAttestations, uint256[] memory countsPerIssuer)
  {
    totalAttestations = 0;
    uint256 numAttestationsForIssuer;
    countsPerIssuer = new uint256[](trustedIssuers.length);

    for (uint256 i = 0; i < trustedIssuers.length; i = i.add(1)) {
      numAttestationsForIssuer = identifierToAttestations[identifier][trustedIssuers[i]].length;
      totalAttestations = totalAttestations.add(numAttestationsForIssuer);
      countsPerIssuer[i] = numAttestationsForIssuer;
    }
    return (totalAttestations, countsPerIssuer);
  }

  /**
   * @notice Helper function for lookupIdentifiers to calculate the
             total number of identifiers completed for an identifier
             by each trusted issuer
   * @param account Address of the account
   * @param trustedIssuers Array of n issuers whose identifiers will be included
   * @return totalIdentifiers Sum total of identifiers found
   * @return countsPerIssuer Array of number of identifiers found per issuer
   */
  function getNumIdentifiers(address account, address[] memory trustedIssuers)
    internal
    view
    returns (uint256 totalIdentifiers, uint256[] memory countsPerIssuer)
  {
    totalIdentifiers = 0;
    uint256 numIdentifiersForIssuer;
    countsPerIssuer = new uint256[](trustedIssuers.length);

    for (uint256 i = 0; i < trustedIssuers.length; i = i.add(1)) {
      numIdentifiersForIssuer = addressToIdentifiers[account][trustedIssuers[i]].length;
      totalIdentifiers = totalIdentifiers.add(numIdentifiersForIssuer);
      countsPerIssuer[i] = numIdentifiersForIssuer;
    }
    return (totalIdentifiers, countsPerIssuer);
  }

  /**
   * @notice Registers an attestation
   * @param identifier Hash of the identifier to be attested
   * @param issuer Address of the attestation issuer
   * @param account Address of the account being mapped to the identifier
   * @param issuedOn Time at which the issuer issued the attestation in Unix time 
   * @param signer Address of the signer of the attestation
   */
  function _registerAttestation(
    bytes32 identifier,
    address issuer,
    address account,
    address signer,
    uint64 issuedOn
  ) private {
    require(
      !revokedAttestations[getUniqueAttestationHash(identifier, issuer, account, signer, issuedOn)],
      "Attestation has been revoked"
    );
    uint256 numExistingAttestations = identifierToAttestations[identifier][issuer].length;
    require(
      numExistingAttestations.add(1) <= MAX_ATTESTATIONS_PER_IDENTIFIER,
      "Max attestations already registered for identifier"
    );
    require(
      addressToIdentifiers[account][issuer].length.add(1) <= MAX_IDENTIFIERS_PER_ADDRESS,
      "Max identifiers already registered for account"
    );

    for (uint256 i = 0; i < numExistingAttestations; i = i.add(1)) {
      // This enforces only one attestation to be uploaded
      // for a given set of (identifier, issuer, account)
      // Editing/upgrading an attestation requires that it be revoked before a new one is registered
      require(
        identifierToAttestations[identifier][issuer][i].account != account,
        "Attestation for this account already exists"
      );
    }
    uint64 publishedOn = uint64(block.timestamp);
    OwnershipAttestation memory attestation = OwnershipAttestation(
      account,
      signer,
      issuedOn,
      publishedOn
    );
    identifierToAttestations[identifier][issuer].push(attestation);
    addressToIdentifiers[account][issuer].push(identifier);
    emit AttestationRegistered(identifier, issuer, account, signer, issuedOn, publishedOn);
  }

  /**
   * @notice Revokes an attestation:
   *  helper function for revokeAttestation and batchRevokeAttestations
   * @param identifier Hash of the identifier to be revoked
   * @param issuer Address of the attestation issuer
   * @param account Address of the account mapped to the identifier
   * @dev Reverts if attestation is not found mapping identifier <-> account
   */
  function _revokeAttestation(bytes32 identifier, address issuer, address account) private {
    OwnershipAttestation[] storage attestations = identifierToAttestations[identifier][issuer];
    uint256 lenAttestations = attestations.length;
    for (uint256 i = 0; i < lenAttestations; i = i.add(1)) {
      if (attestations[i].account != account) {
        continue;
      }

      OwnershipAttestation memory attestation = attestations[i];
      // This is meant to delete the attestations in the array
      // and then move the last element in the array to that empty spot,
      // to avoid having empty elements in the array
      if (i != lenAttestations - 1) {
        attestations[i] = attestations[lenAttestations - 1];
      }
      attestations.pop();

      bool deletedIdentifier = false;
      bytes32[] storage identifiers = addressToIdentifiers[account][issuer];
      uint256 lenIdentifiers = identifiers.length;

      for (uint256 j = 0; j < lenIdentifiers; j = j.add(1)) {
        if (identifiers[j] != identifier) {
          continue;
        }
        if (j != lenIdentifiers - 1) {
          identifiers[j] = identifiers[lenIdentifiers - 1];
        }
        identifiers.pop();
        deletedIdentifier = true;
        break;
      }
      // Should never be false - both mappings should always be updated in unison
      assert(deletedIdentifier);

      bytes32 attestationHash = getUniqueAttestationHash(
        identifier,
        issuer,
        account,
        attestation.signer,
        attestation.issuedOn
      );
      revokedAttestations[attestationHash] = true;

      emit AttestationRevoked(
        identifier,
        issuer,
        account,
        attestation.signer,
        attestation.issuedOn,
        attestation.publishedOn
      );
      return;
    }
    revert("Attestation to be revoked does not exist");
  }
}
        

/Users/carter/git/clabs/celo-monorepo/packages/protocol/contracts/common/Initializable.sol

pragma solidity ^0.5.13;

contract Initializable {
  bool public initialized;

  constructor(bool testingDeployment) public {
    if (!testingDeployment) {
      initialized = true;
    }
  }

  modifier initializer() {
    require(!initialized, "contract already initialized");
    initialized = true;
    _;
  }
}
          

/Users/carter/git/clabs/celo-monorepo/packages/protocol/contracts/common/Signatures.sol

pragma solidity ^0.5.13;

import "openzeppelin-solidity/contracts/cryptography/ECDSA.sol";

library Signatures {
  /**
  * @notice Given a signed address, returns the signer of the address.
  * @param message The address that was signed.
  * @param v The recovery id of the incoming ECDSA signature.
  * @param r Output value r of the ECDSA signature.
  * @param s Output value s of the ECDSA signature.
  */
  function getSignerOfAddress(address message, uint8 v, bytes32 r, bytes32 s)
    public
    pure
    returns (address)
  {
    bytes32 hash = keccak256(abi.encodePacked(message));
    return getSignerOfMessageHash(hash, v, r, s);
  }

  /**
  * @notice Given a message hash, returns the signer of the address.
  * @param messageHash The hash of a message.
  * @param v The recovery id of the incoming ECDSA signature.
  * @param r Output value r of the ECDSA signature.
  * @param s Output value s of the ECDSA signature.
  */
  function getSignerOfMessageHash(bytes32 messageHash, uint8 v, bytes32 r, bytes32 s)
    public
    pure
    returns (address)
  {
    bytes memory signature = new bytes(65);
    // Concatenate (r, s, v) into signature.
    assembly {
      mstore(add(signature, 32), r)
      mstore(add(signature, 64), s)
      mstore8(add(signature, 96), v)
    }
    bytes32 prefixedHash = ECDSA.toEthSignedMessageHash(messageHash);
    return ECDSA.recover(prefixedHash, signature);
  }

  /**
  * @notice Given a domain separator and a structHash, construct the typed data hash
  * @param eip712DomainSeparator Context specific domain separator
  * @param structHash hash of the typed data struct
  * @return The EIP712 typed data hash
  */
  function toEthSignedTypedDataHash(bytes32 eip712DomainSeparator, bytes32 structHash)
    public
    pure
    returns (bytes32)
  {
    return keccak256(abi.encodePacked("\x19\x01", eip712DomainSeparator, structHash));
  }

  /**
  * @notice Given a domain separator and a structHash and a signature return the signer
  * @param eip712DomainSeparator Context specific domain separator
  * @param structHash hash of the typed data struct
  * @param v The recovery id of the incoming ECDSA signature.
  * @param r Output value r of the ECDSA signature.
  * @param s Output value s of the ECDSA signature.
  */
  function getSignerOfTypedDataHash(
    bytes32 eip712DomainSeparator,
    bytes32 structHash,
    uint8 v,
    bytes32 r,
    bytes32 s
  ) public pure returns (address) {
    bytes memory signature = new bytes(65);
    // Concatenate (r, s, v) into signature.
    assembly {
      mstore(add(signature, 32), r)
      mstore(add(signature, 64), s)
      mstore8(add(signature, 96), v)
    }
    bytes32 prefixedHash = toEthSignedTypedDataHash(eip712DomainSeparator, structHash);
    return ECDSA.recover(prefixedHash, signature);
  }
}
          

/Users/carter/git/clabs/celo-monorepo/packages/protocol/contracts/common/UsingRegistryV2.sol

pragma solidity ^0.5.13;

import "openzeppelin-solidity/contracts/ownership/Ownable.sol";
import "openzeppelin-solidity/contracts/token/ERC20/IERC20.sol";

import "./interfaces/IAccounts.sol";
import "./interfaces/IFeeCurrencyWhitelist.sol";
import "./interfaces/IFreezer.sol";
import "./interfaces/IRegistry.sol";

import "../governance/interfaces/IElection.sol";
import "../governance/interfaces/IGovernance.sol";
import "../governance/interfaces/ILockedGold.sol";
import "../governance/interfaces/IValidators.sol";

import "../identity/interfaces/IRandom.sol";
import "../identity/interfaces/IAttestations.sol";
import "../identity/interfaces/IFederatedAttestations.sol";

import "../stability/interfaces/IExchange.sol";
import "../stability/interfaces/IReserve.sol";
import "../stability/interfaces/ISortedOracles.sol";
import "../stability/interfaces/IStableToken.sol";

contract UsingRegistryV2 {
  address internal constant registryAddress = 0x000000000000000000000000000000000000ce10;
  IRegistry public constant registryContract = IRegistry(registryAddress);

  bytes32 internal constant ACCOUNTS_REGISTRY_ID = keccak256(abi.encodePacked("Accounts"));
  bytes32 internal constant ATTESTATIONS_REGISTRY_ID = keccak256(abi.encodePacked("Attestations"));
  bytes32 internal constant DOWNTIME_SLASHER_REGISTRY_ID = keccak256(
    abi.encodePacked("DowntimeSlasher")
  );
  bytes32 internal constant DOUBLE_SIGNING_SLASHER_REGISTRY_ID = keccak256(
    abi.encodePacked("DoubleSigningSlasher")
  );
  bytes32 internal constant ELECTION_REGISTRY_ID = keccak256(abi.encodePacked("Election"));
  bytes32 internal constant EXCHANGE_REGISTRY_ID = keccak256(abi.encodePacked("Exchange"));
  bytes32 internal constant EXCHANGE_EURO_REGISTRY_ID = keccak256(abi.encodePacked("ExchangeEUR"));
  bytes32 internal constant EXCHANGE_REAL_REGISTRY_ID = keccak256(abi.encodePacked("ExchangeBRL"));

  bytes32 internal constant FEE_CURRENCY_WHITELIST_REGISTRY_ID = keccak256(
    abi.encodePacked("FeeCurrencyWhitelist")
  );
  bytes32 internal constant FEDERATED_ATTESTATIONS_REGISTRY_ID = keccak256(
    abi.encodePacked("FederatedAttestations")
  );
  bytes32 internal constant FREEZER_REGISTRY_ID = keccak256(abi.encodePacked("Freezer"));
  bytes32 internal constant GOLD_TOKEN_REGISTRY_ID = keccak256(abi.encodePacked("GoldToken"));
  bytes32 internal constant GOVERNANCE_REGISTRY_ID = keccak256(abi.encodePacked("Governance"));
  bytes32 internal constant GOVERNANCE_SLASHER_REGISTRY_ID = keccak256(
    abi.encodePacked("GovernanceSlasher")
  );
  bytes32 internal constant LOCKED_GOLD_REGISTRY_ID = keccak256(abi.encodePacked("LockedGold"));
  bytes32 internal constant RESERVE_REGISTRY_ID = keccak256(abi.encodePacked("Reserve"));
  bytes32 internal constant RANDOM_REGISTRY_ID = keccak256(abi.encodePacked("Random"));
  bytes32 internal constant SORTED_ORACLES_REGISTRY_ID = keccak256(
    abi.encodePacked("SortedOracles")
  );
  bytes32 internal constant STABLE_TOKEN_REGISTRY_ID = keccak256(abi.encodePacked("StableToken"));
  bytes32 internal constant STABLE_EURO_TOKEN_REGISTRY_ID = keccak256(
    abi.encodePacked("StableTokenEUR")
  );
  bytes32 internal constant STABLE_REAL_TOKEN_REGISTRY_ID = keccak256(
    abi.encodePacked("StableTokenBRL")
  );
  bytes32 internal constant VALIDATORS_REGISTRY_ID = keccak256(abi.encodePacked("Validators"));

  modifier onlyRegisteredContract(bytes32 identifierHash) {
    require(
      registryContract.getAddressForOrDie(identifierHash) == msg.sender,
      "only registered contract"
    );
    _;
  }

  modifier onlyRegisteredContracts(bytes32[] memory identifierHashes) {
    require(registryContract.isOneOf(identifierHashes, msg.sender), "only registered contracts");
    _;
  }

  function getAccounts() internal view returns (IAccounts) {
    return IAccounts(registryContract.getAddressForOrDie(ACCOUNTS_REGISTRY_ID));
  }

  function getAttestations() internal view returns (IAttestations) {
    return IAttestations(registryContract.getAddressForOrDie(ATTESTATIONS_REGISTRY_ID));
  }

  function getElection() internal view returns (IElection) {
    return IElection(registryContract.getAddressForOrDie(ELECTION_REGISTRY_ID));
  }

  function getExchange() internal view returns (IExchange) {
    return IExchange(registryContract.getAddressForOrDie(EXCHANGE_REGISTRY_ID));
  }

  function getExchangeDollar() internal view returns (IExchange) {
    return getExchange();
  }

  function getExchangeEuro() internal view returns (IExchange) {
    return IExchange(registryContract.getAddressForOrDie(EXCHANGE_EURO_REGISTRY_ID));
  }

  function getExchangeREAL() internal view returns (IExchange) {
    return IExchange(registryContract.getAddressForOrDie(EXCHANGE_REAL_REGISTRY_ID));
  }

  function getFeeCurrencyWhitelistRegistry() internal view returns (IFeeCurrencyWhitelist) {
    return
      IFeeCurrencyWhitelist(
        registryContract.getAddressForOrDie(FEE_CURRENCY_WHITELIST_REGISTRY_ID)
      );
  }

  function getFederatedAttestations() internal view returns (IFederatedAttestations) {
    return
      IFederatedAttestations(
        registryContract.getAddressForOrDie(FEDERATED_ATTESTATIONS_REGISTRY_ID)
      );
  }

  function getFreezer() internal view returns (IFreezer) {
    return IFreezer(registryContract.getAddressForOrDie(FREEZER_REGISTRY_ID));
  }

  function getGoldToken() internal view returns (IERC20) {
    return IERC20(registryContract.getAddressForOrDie(GOLD_TOKEN_REGISTRY_ID));
  }

  function getGovernance() internal view returns (IGovernance) {
    return IGovernance(registryContract.getAddressForOrDie(GOVERNANCE_REGISTRY_ID));
  }

  function getLockedGold() internal view returns (ILockedGold) {
    return ILockedGold(registryContract.getAddressForOrDie(LOCKED_GOLD_REGISTRY_ID));
  }

  function getRandom() internal view returns (IRandom) {
    return IRandom(registryContract.getAddressForOrDie(RANDOM_REGISTRY_ID));
  }

  function getReserve() internal view returns (IReserve) {
    return IReserve(registryContract.getAddressForOrDie(RESERVE_REGISTRY_ID));
  }

  function getSortedOracles() internal view returns (ISortedOracles) {
    return ISortedOracles(registryContract.getAddressForOrDie(SORTED_ORACLES_REGISTRY_ID));
  }

  function getStableToken() internal view returns (IStableToken) {
    return IStableToken(registryContract.getAddressForOrDie(STABLE_TOKEN_REGISTRY_ID));
  }

  function getStableDollarToken() internal view returns (IStableToken) {
    return getStableToken();
  }

  function getStableEuroToken() internal view returns (IStableToken) {
    return IStableToken(registryContract.getAddressForOrDie(STABLE_EURO_TOKEN_REGISTRY_ID));
  }

  function getStableRealToken() internal view returns (IStableToken) {
    return IStableToken(registryContract.getAddressForOrDie(STABLE_REAL_TOKEN_REGISTRY_ID));
  }

  function getValidators() internal view returns (IValidators) {
    return IValidators(registryContract.getAddressForOrDie(VALIDATORS_REGISTRY_ID));
  }
}
          

/Users/carter/git/clabs/celo-monorepo/packages/protocol/contracts/common/interfaces/IAccounts.sol

pragma solidity ^0.5.13;

interface IAccounts {
  function isAccount(address) external view returns (bool);
  function voteSignerToAccount(address) external view returns (address);
  function validatorSignerToAccount(address) external view returns (address);
  function attestationSignerToAccount(address) external view returns (address);
  function signerToAccount(address) external view returns (address);
  function getAttestationSigner(address) external view returns (address);
  function getValidatorSigner(address) external view returns (address);
  function getVoteSigner(address) external view returns (address);
  function hasAuthorizedVoteSigner(address) external view returns (bool);
  function hasAuthorizedValidatorSigner(address) external view returns (bool);
  function hasAuthorizedAttestationSigner(address) external view returns (bool);

  function setAccountDataEncryptionKey(bytes calldata) external;
  function setMetadataURL(string calldata) external;
  function setName(string calldata) external;
  function setWalletAddress(address, uint8, bytes32, bytes32) external;
  function setAccount(string calldata, bytes calldata, address, uint8, bytes32, bytes32) external;

  function getDataEncryptionKey(address) external view returns (bytes memory);
  function getWalletAddress(address) external view returns (address);
  function getMetadataURL(address) external view returns (string memory);
  function batchGetMetadataURL(address[] calldata)
    external
    view
    returns (uint256[] memory, bytes memory);
  function getName(address) external view returns (string memory);

  function authorizeVoteSigner(address, uint8, bytes32, bytes32) external;
  function authorizeValidatorSigner(address, uint8, bytes32, bytes32) external;
  function authorizeValidatorSignerWithPublicKey(address, uint8, bytes32, bytes32, bytes calldata)
    external;
  function authorizeValidatorSignerWithKeys(
    address,
    uint8,
    bytes32,
    bytes32,
    bytes calldata,
    bytes calldata,
    bytes calldata
  ) external;
  function authorizeAttestationSigner(address, uint8, bytes32, bytes32) external;
  function createAccount() external returns (bool);

  function setPaymentDelegation(address, uint256) external;
  function getPaymentDelegation(address) external view returns (address, uint256);
  function isSigner(address, address, bytes32) external view returns (bool);
}
          

/Users/carter/git/clabs/celo-monorepo/packages/protocol/contracts/common/interfaces/ICeloVersionedContract.sol

pragma solidity ^0.5.13;

interface ICeloVersionedContract {
  /**
   * @notice Returns the storage, major, minor, and patch version of the contract.
    * @return Storage version of the contract.
    * @return Major version of the contract.
    * @return Minor version of the contract.
    * @return Patch version of the contract.
   */
  function getVersionNumber() external pure returns (uint256, uint256, uint256, uint256);
}
          

/Users/carter/git/clabs/celo-monorepo/packages/protocol/contracts/common/interfaces/IFeeCurrencyWhitelist.sol

pragma solidity ^0.5.13;

interface IFeeCurrencyWhitelist {
  function addToken(address) external;
  function getWhitelist() external view returns (address[] memory);
}
          

/Users/carter/git/clabs/celo-monorepo/packages/protocol/contracts/common/interfaces/IFreezer.sol

pragma solidity ^0.5.13;

interface IFreezer {
  function isFrozen(address) external view returns (bool);
}
          

/Users/carter/git/clabs/celo-monorepo/packages/protocol/contracts/common/interfaces/IRegistry.sol

pragma solidity ^0.5.13;

interface IRegistry {
  function setAddressFor(string calldata, address) external;
  function getAddressForOrDie(bytes32) external view returns (address);
  function getAddressFor(bytes32) external view returns (address);
  function getAddressForStringOrDie(string calldata identifier) external view returns (address);
  function getAddressForString(string calldata identifier) external view returns (address);
  function isOneOf(bytes32[] calldata, address) external view returns (bool);
}
          

/Users/carter/git/clabs/celo-monorepo/packages/protocol/contracts/governance/interfaces/IElection.sol

pragma solidity ^0.5.13;

interface IElection {
  function electValidatorSigners() external view returns (address[] memory);
  function electNValidatorSigners(uint256, uint256) external view returns (address[] memory);
  function vote(address, uint256, address, address) external returns (bool);
  function activate(address) external returns (bool);
  function revokeActive(address, uint256, address, address, uint256) external returns (bool);
  function revokeAllActive(address, address, address, uint256) external returns (bool);
  function revokePending(address, uint256, address, address, uint256) external returns (bool);
  function markGroupIneligible(address) external;
  function markGroupEligible(address, address, address) external;
  function forceDecrementVotes(
    address,
    uint256,
    address[] calldata,
    address[] calldata,
    uint256[] calldata
  ) external returns (uint256);

  // view functions
  function getElectableValidators() external view returns (uint256, uint256);
  function getElectabilityThreshold() external view returns (uint256);
  function getNumVotesReceivable(address) external view returns (uint256);
  function getTotalVotes() external view returns (uint256);
  function getActiveVotes() external view returns (uint256);
  function getTotalVotesByAccount(address) external view returns (uint256);
  function getPendingVotesForGroupByAccount(address, address) external view returns (uint256);
  function getActiveVotesForGroupByAccount(address, address) external view returns (uint256);
  function getTotalVotesForGroupByAccount(address, address) external view returns (uint256);
  function getActiveVoteUnitsForGroupByAccount(address, address) external view returns (uint256);
  function getTotalVotesForGroup(address) external view returns (uint256);
  function getActiveVotesForGroup(address) external view returns (uint256);
  function getPendingVotesForGroup(address) external view returns (uint256);
  function getGroupEligibility(address) external view returns (bool);
  function getGroupEpochRewards(address, uint256, uint256[] calldata)
    external
    view
    returns (uint256);
  function getGroupsVotedForByAccount(address) external view returns (address[] memory);
  function getEligibleValidatorGroups() external view returns (address[] memory);
  function getTotalVotesForEligibleValidatorGroups()
    external
    view
    returns (address[] memory, uint256[] memory);
  function getCurrentValidatorSigners() external view returns (address[] memory);
  function canReceiveVotes(address, uint256) external view returns (bool);
  function hasActivatablePendingVotes(address, address) external view returns (bool);

  // only owner
  function setElectableValidators(uint256, uint256) external returns (bool);
  function setMaxNumGroupsVotedFor(uint256) external returns (bool);
  function setElectabilityThreshold(uint256) external returns (bool);

  // only VM
  function distributeEpochRewards(address, uint256, address, address) external;
}
          

/Users/carter/git/clabs/celo-monorepo/packages/protocol/contracts/governance/interfaces/IGovernance.sol

pragma solidity ^0.5.13;

interface IGovernance {
  function isVoting(address) external view returns (bool);
}
          

/Users/carter/git/clabs/celo-monorepo/packages/protocol/contracts/governance/interfaces/ILockedGold.sol

pragma solidity ^0.5.13;

interface ILockedGold {
  function incrementNonvotingAccountBalance(address, uint256) external;
  function decrementNonvotingAccountBalance(address, uint256) external;
  function getAccountTotalLockedGold(address) external view returns (uint256);
  function getTotalLockedGold() external view returns (uint256);
  function getPendingWithdrawals(address)
    external
    view
    returns (uint256[] memory, uint256[] memory);
  function getTotalPendingWithdrawals(address) external view returns (uint256);
  function lock() external payable;
  function unlock(uint256) external;
  function relock(uint256, uint256) external;
  function withdraw(uint256) external;
  function slash(
    address account,
    uint256 penalty,
    address reporter,
    uint256 reward,
    address[] calldata lessers,
    address[] calldata greaters,
    uint256[] calldata indices
  ) external;
  function isSlasher(address) external view returns (bool);
}
          

/Users/carter/git/clabs/celo-monorepo/packages/protocol/contracts/governance/interfaces/IValidators.sol

pragma solidity ^0.5.13;

interface IValidators {
  function registerValidator(bytes calldata, bytes calldata, bytes calldata)
    external
    returns (bool);
  function deregisterValidator(uint256) external returns (bool);
  function affiliate(address) external returns (bool);
  function deaffiliate() external returns (bool);
  function updateBlsPublicKey(bytes calldata, bytes calldata) external returns (bool);
  function registerValidatorGroup(uint256) external returns (bool);
  function deregisterValidatorGroup(uint256) external returns (bool);
  function addMember(address) external returns (bool);
  function addFirstMember(address, address, address) external returns (bool);
  function removeMember(address) external returns (bool);
  function reorderMember(address, address, address) external returns (bool);
  function updateCommission() external;
  function setNextCommissionUpdate(uint256) external;
  function resetSlashingMultiplier() external;

  // only owner
  function setCommissionUpdateDelay(uint256) external;
  function setMaxGroupSize(uint256) external returns (bool);
  function setMembershipHistoryLength(uint256) external returns (bool);
  function setValidatorScoreParameters(uint256, uint256) external returns (bool);
  function setGroupLockedGoldRequirements(uint256, uint256) external returns (bool);
  function setValidatorLockedGoldRequirements(uint256, uint256) external returns (bool);
  function setSlashingMultiplierResetPeriod(uint256) external;

  // view functions
  function getMaxGroupSize() external view returns (uint256);
  function getCommissionUpdateDelay() external view returns (uint256);
  function getValidatorScoreParameters() external view returns (uint256, uint256);
  function getMembershipHistory(address)
    external
    view
    returns (uint256[] memory, address[] memory, uint256, uint256);
  function calculateEpochScore(uint256) external view returns (uint256);
  function calculateGroupEpochScore(uint256[] calldata) external view returns (uint256);
  function getAccountLockedGoldRequirement(address) external view returns (uint256);
  function meetsAccountLockedGoldRequirements(address) external view returns (bool);
  function getValidatorBlsPublicKeyFromSigner(address) external view returns (bytes memory);
  function getValidator(address account)
    external
    view
    returns (bytes memory, bytes memory, address, uint256, address);
  function getValidatorGroup(address)
    external
    view
    returns (address[] memory, uint256, uint256, uint256, uint256[] memory, uint256, uint256);
  function getGroupNumMembers(address) external view returns (uint256);
  function getTopGroupValidators(address, uint256) external view returns (address[] memory);
  function getGroupsNumMembers(address[] calldata accounts)
    external
    view
    returns (uint256[] memory);
  function getNumRegisteredValidators() external view returns (uint256);
  function groupMembershipInEpoch(address, uint256, uint256) external view returns (address);

  // only registered contract
  function updateEcdsaPublicKey(address, address, bytes calldata) external returns (bool);
  function updatePublicKeys(address, address, bytes calldata, bytes calldata, bytes calldata)
    external
    returns (bool);
  function getValidatorLockedGoldRequirements() external view returns (uint256, uint256);
  function getGroupLockedGoldRequirements() external view returns (uint256, uint256);
  function getRegisteredValidators() external view returns (address[] memory);
  function getRegisteredValidatorSigners() external view returns (address[] memory);
  function getRegisteredValidatorGroups() external view returns (address[] memory);
  function isValidatorGroup(address) external view returns (bool);
  function isValidator(address) external view returns (bool);
  function getValidatorGroupSlashingMultiplier(address) external view returns (uint256);
  function getMembershipInLastEpoch(address) external view returns (address);
  function getMembershipInLastEpochFromSigner(address) external view returns (address);

  // only VM
  function updateValidatorScoreFromSigner(address, uint256) external;
  function distributeEpochPaymentsFromSigner(address, uint256) external returns (uint256);

  // only slasher
  function forceDeaffiliateIfValidator(address) external;
  function halveSlashingMultiplier(address) external;

}
          

/Users/carter/git/clabs/celo-monorepo/packages/protocol/contracts/identity/interfaces/IAttestations.sol

pragma solidity ^0.5.13;

interface IAttestations {
  function request(bytes32, uint256, address) external;
  function selectIssuers(bytes32) external;
  function complete(bytes32, uint8, bytes32, bytes32) external;
  function revoke(bytes32, uint256) external;
  function withdraw(address) external;
  function approveTransfer(bytes32, uint256, address, address, bool) external;

  // view functions
  function getUnselectedRequest(bytes32, address) external view returns (uint32, uint32, address);
  function getAttestationIssuers(bytes32, address) external view returns (address[] memory);
  function getAttestationStats(bytes32, address) external view returns (uint32, uint32);
  function batchGetAttestationStats(bytes32[] calldata)
    external
    view
    returns (uint256[] memory, address[] memory, uint64[] memory, uint64[] memory);
  function getAttestationState(bytes32, address, address)
    external
    view
    returns (uint8, uint32, address);
  function getCompletableAttestations(bytes32, address)
    external
    view
    returns (uint32[] memory, address[] memory, uint256[] memory, bytes memory);
  function getAttestationRequestFee(address) external view returns (uint256);
  function getMaxAttestations() external view returns (uint256);
  function validateAttestationCode(bytes32, address, uint8, bytes32, bytes32)
    external
    view
    returns (address);
  function lookupAccountsForIdentifier(bytes32) external view returns (address[] memory);
  function requireNAttestationsRequested(bytes32, address, uint32) external view;

  // only owner
  function setAttestationRequestFee(address, uint256) external;
  function setAttestationExpiryBlocks(uint256) external;
  function setSelectIssuersWaitBlocks(uint256) external;
  function setMaxAttestations(uint256) external;
}
          

/Users/carter/git/clabs/celo-monorepo/packages/protocol/contracts/identity/interfaces/IFederatedAttestations.sol

pragma solidity ^0.5.13;

interface IFederatedAttestations {
  function registerAttestationAsIssuer(bytes32 identifier, address account, uint64 issuedOn)
    external;
  function registerAttestation(
    bytes32 identifier,
    address issuer,
    address account,
    address signer,
    uint64 issuedOn,
    uint8 v,
    bytes32 r,
    bytes32 s
  ) external;
  function revokeAttestation(bytes32 identifier, address issuer, address account) external;
  function batchRevokeAttestations(
    address issuer,
    bytes32[] calldata identifiers,
    address[] calldata accounts
  ) external;

  // view functions
  function lookupAttestations(bytes32 identifier, address[] calldata trustedIssuers)
    external
    view
    returns (
      uint256[] memory,
      address[] memory,
      address[] memory,
      uint64[] memory,
      uint64[] memory
    );
  function lookupIdentifiers(address account, address[] calldata trustedIssuers)
    external
    view
    returns (uint256[] memory, bytes32[] memory);
  function validateAttestationSig(
    bytes32 identifier,
    address issuer,
    address account,
    address signer,
    uint64 issuedOn,
    uint8 v,
    bytes32 r,
    bytes32 s
  ) external view;
  function getUniqueAttestationHash(
    bytes32 identifier,
    address issuer,
    address account,
    address signer,
    uint64 issuedOn
  ) external pure returns (bytes32);
}
          

/Users/carter/git/clabs/celo-monorepo/packages/protocol/contracts/identity/interfaces/IRandom.sol

pragma solidity ^0.5.13;

interface IRandom {
  function revealAndCommit(bytes32, bytes32, address) external;
  function randomnessBlockRetentionWindow() external view returns (uint256);
  function random() external view returns (bytes32);
  function getBlockRandomness(uint256) external view returns (bytes32);
}
          

/Users/carter/git/clabs/celo-monorepo/packages/protocol/contracts/stability/interfaces/IExchange.sol

pragma solidity ^0.5.13;

interface IExchange {
  function buy(uint256, uint256, bool) external returns (uint256);
  function sell(uint256, uint256, bool) external returns (uint256);
  function exchange(uint256, uint256, bool) external returns (uint256);
  function setUpdateFrequency(uint256) external;
  function getBuyTokenAmount(uint256, bool) external view returns (uint256);
  function getSellTokenAmount(uint256, bool) external view returns (uint256);
  function getBuyAndSellBuckets(bool) external view returns (uint256, uint256);
  function getStableBucketCap() external view returns (uint256);
}
          

/Users/carter/git/clabs/celo-monorepo/packages/protocol/contracts/stability/interfaces/IReserve.sol

pragma solidity ^0.5.13;

interface IReserve {
  function setTobinTaxStalenessThreshold(uint256) external;
  function addToken(address) external returns (bool);
  function removeToken(address, uint256) external returns (bool);
  function transferGold(address payable, uint256) external returns (bool);
  function transferExchangeGold(address payable, uint256) external returns (bool);
  function getReserveGoldBalance() external view returns (uint256);
  function getUnfrozenReserveGoldBalance() external view returns (uint256);
  function getOrComputeTobinTax() external returns (uint256, uint256);
  function getTokens() external view returns (address[] memory);
  function getReserveRatio() external view returns (uint256);
  function addExchangeSpender(address) external;
  function removeExchangeSpender(address, uint256) external;
  function addSpender(address) external;
  function removeSpender(address) external;
}
          

/Users/carter/git/clabs/celo-monorepo/packages/protocol/contracts/stability/interfaces/ISortedOracles.sol

pragma solidity ^0.5.13;

interface ISortedOracles {
  function addOracle(address, address) external;
  function removeOracle(address, address, uint256) external;
  function report(address, uint256, address, address) external;
  function removeExpiredReports(address, uint256) external;
  function isOldestReportExpired(address token) external view returns (bool, address);
  function numRates(address) external view returns (uint256);
  function medianRate(address) external view returns (uint256, uint256);
  function numTimestamps(address) external view returns (uint256);
  function medianTimestamp(address) external view returns (uint256);
}
          

/Users/carter/git/clabs/celo-monorepo/packages/protocol/contracts/stability/interfaces/IStableToken.sol

pragma solidity ^0.5.13;

/**
 * @title This interface describes the functions specific to Celo Stable Tokens, and in the
 * absence of interface inheritance is intended as a companion to IERC20.sol and ICeloToken.sol.
 */
interface IStableToken {
  function mint(address, uint256) external returns (bool);
  function burn(uint256) external returns (bool);
  function setInflationParameters(uint256, uint256) external;
  function valueToUnits(uint256) external view returns (uint256);
  function unitsToValue(uint256) external view returns (uint256);
  function getInflationParameters() external view returns (uint256, uint256, uint256, uint256);

  // NOTE: duplicated with IERC20.sol, remove once interface inheritance is supported.
  function balanceOf(address) external view returns (uint256);
}
          

/openzeppelin-solidity/contracts/GSN/Context.sol

pragma solidity ^0.5.0;

/*
 * @dev Provides information about the current execution context, including the
 * sender of the transaction and its data. While these are generally available
 * via msg.sender and msg.data, they should not be accessed in such a direct
 * manner, since when dealing with GSN meta-transactions the account sending and
 * paying for execution may not be the actual sender (as far as an application
 * is concerned).
 *
 * This contract is only required for intermediate, library-like contracts.
 */
contract Context {
    // Empty internal constructor, to prevent people from mistakenly deploying
    // an instance of this contract, which should be used via inheritance.
    constructor () internal { }
    // solhint-disable-previous-line no-empty-blocks

    function _msgSender() internal view returns (address payable) {
        return msg.sender;
    }

    function _msgData() internal view returns (bytes memory) {
        this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691
        return msg.data;
    }
}
          

/openzeppelin-solidity/contracts/cryptography/ECDSA.sol

pragma solidity ^0.5.0;

/**
 * @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations.
 *
 * These functions can be used to verify that a message was signed by the holder
 * of the private keys of a given address.
 */
library ECDSA {
    /**
     * @dev Returns the address that signed a hashed message (`hash`) with
     * `signature`. This address can then be used for verification purposes.
     *
     * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
     * this function rejects them by requiring the `s` value to be in the lower
     * half order, and the `v` value to be either 27 or 28.
     *
     * NOTE: This call _does not revert_ if the signature is invalid, or
     * if the signer is otherwise unable to be retrieved. In those scenarios,
     * the zero address is returned.
     *
     * IMPORTANT: `hash` _must_ be the result of a hash operation for the
     * verification to be secure: it is possible to craft signatures that
     * recover to arbitrary addresses for non-hashed data. A safe way to ensure
     * this is by receiving a hash of the original message (which may otherwise
     * be too long), and then calling {toEthSignedMessageHash} on it.
     */
    function recover(bytes32 hash, bytes memory signature) internal pure returns (address) {
        // Check the signature length
        if (signature.length != 65) {
            return (address(0));
        }

        // Divide the signature in r, s and v variables
        bytes32 r;
        bytes32 s;
        uint8 v;

        // ecrecover takes the signature parameters, and the only way to get them
        // currently is to use assembly.
        // solhint-disable-next-line no-inline-assembly
        assembly {
            r := mload(add(signature, 0x20))
            s := mload(add(signature, 0x40))
            v := byte(0, mload(add(signature, 0x60)))
        }

        // EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature
        // unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines
        // the valid range for s in (281): 0 < s < secp256k1n ÷ 2 + 1, and for v in (282): v ∈ {27, 28}. Most
        // signatures from current libraries generate a unique signature with an s-value in the lower half order.
        //
        // If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value
        // with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or
        // vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept
        // these malleable signatures as well.
        if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) {
            return address(0);
        }

        if (v != 27 && v != 28) {
            return address(0);
        }

        // If the signature is valid (and not malleable), return the signer address
        return ecrecover(hash, v, r, s);
    }

    /**
     * @dev Returns an Ethereum Signed Message, created from a `hash`. This
     * replicates the behavior of the
     * https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_sign[`eth_sign`]
     * JSON-RPC method.
     *
     * See {recover}.
     */
    function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32) {
        // 32 is the length in bytes of hash,
        // enforced by the type signature above
        return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", hash));
    }
}
          

/openzeppelin-solidity/contracts/math/SafeMath.sol

pragma solidity ^0.5.0;

/**
 * @dev Wrappers over Solidity's arithmetic operations with added overflow
 * checks.
 *
 * Arithmetic operations in Solidity wrap on overflow. This can easily result
 * in bugs, because programmers usually assume that an overflow raises an
 * error, which is the standard behavior in high level programming languages.
 * `SafeMath` restores this intuition by reverting the transaction when an
 * operation overflows.
 *
 * Using this library instead of the unchecked operations eliminates an entire
 * class of bugs, so it's recommended to use it always.
 */
library SafeMath {
    /**
     * @dev Returns the addition of two unsigned integers, reverting on
     * overflow.
     *
     * Counterpart to Solidity's `+` operator.
     *
     * Requirements:
     * - Addition cannot overflow.
     */
    function add(uint256 a, uint256 b) internal pure returns (uint256) {
        uint256 c = a + b;
        require(c >= a, "SafeMath: addition overflow");

        return c;
    }

    /**
     * @dev Returns the subtraction of two unsigned integers, reverting on
     * overflow (when the result is negative).
     *
     * Counterpart to Solidity's `-` operator.
     *
     * Requirements:
     * - Subtraction cannot overflow.
     */
    function sub(uint256 a, uint256 b) internal pure returns (uint256) {
        return sub(a, b, "SafeMath: subtraction overflow");
    }

    /**
     * @dev Returns the subtraction of two unsigned integers, reverting with custom message on
     * overflow (when the result is negative).
     *
     * Counterpart to Solidity's `-` operator.
     *
     * Requirements:
     * - Subtraction cannot overflow.
     *
     * _Available since v2.4.0._
     */
    function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
        require(b <= a, errorMessage);
        uint256 c = a - b;

        return c;
    }

    /**
     * @dev Returns the multiplication of two unsigned integers, reverting on
     * overflow.
     *
     * Counterpart to Solidity's `*` operator.
     *
     * Requirements:
     * - Multiplication cannot overflow.
     */
    function mul(uint256 a, uint256 b) internal pure returns (uint256) {
        // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
        // benefit is lost if 'b' is also tested.
        // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
        if (a == 0) {
            return 0;
        }

        uint256 c = a * b;
        require(c / a == b, "SafeMath: multiplication overflow");

        return c;
    }

    /**
     * @dev Returns the integer division of two unsigned integers. Reverts on
     * division by zero. The result is rounded towards zero.
     *
     * Counterpart to Solidity's `/` operator. Note: this function uses a
     * `revert` opcode (which leaves remaining gas untouched) while Solidity
     * uses an invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     * - The divisor cannot be zero.
     */
    function div(uint256 a, uint256 b) internal pure returns (uint256) {
        return div(a, b, "SafeMath: division by zero");
    }

    /**
     * @dev Returns the integer division of two unsigned integers. Reverts with custom message on
     * division by zero. The result is rounded towards zero.
     *
     * Counterpart to Solidity's `/` operator. Note: this function uses a
     * `revert` opcode (which leaves remaining gas untouched) while Solidity
     * uses an invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     * - The divisor cannot be zero.
     *
     * _Available since v2.4.0._
     */
    function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
        // Solidity only automatically asserts when dividing by 0
        require(b > 0, errorMessage);
        uint256 c = a / b;
        // assert(a == b * c + a % b); // There is no case in which this doesn't hold

        return c;
    }

    /**
     * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
     * Reverts when dividing by zero.
     *
     * Counterpart to Solidity's `%` operator. This function uses a `revert`
     * opcode (which leaves remaining gas untouched) while Solidity uses an
     * invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     * - The divisor cannot be zero.
     */
    function mod(uint256 a, uint256 b) internal pure returns (uint256) {
        return mod(a, b, "SafeMath: modulo by zero");
    }

    /**
     * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
     * Reverts with custom message when dividing by zero.
     *
     * Counterpart to Solidity's `%` operator. This function uses a `revert`
     * opcode (which leaves remaining gas untouched) while Solidity uses an
     * invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     * - The divisor cannot be zero.
     *
     * _Available since v2.4.0._
     */
    function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
        require(b != 0, errorMessage);
        return a % b;
    }
}
          

/openzeppelin-solidity/contracts/ownership/Ownable.sol

pragma solidity ^0.5.0;

import "../GSN/Context.sol";
/**
 * @dev Contract module which provides a basic access control mechanism, where
 * there is an account (an owner) that can be granted exclusive access to
 * specific functions.
 *
 * This module is used through inheritance. It will make available the modifier
 * `onlyOwner`, which can be applied to your functions to restrict their use to
 * the owner.
 */
contract Ownable is Context {
    address private _owner;

    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);

    /**
     * @dev Initializes the contract setting the deployer as the initial owner.
     */
    constructor () internal {
        address msgSender = _msgSender();
        _owner = msgSender;
        emit OwnershipTransferred(address(0), msgSender);
    }

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

    /**
     * @dev Throws if called by any account other than the owner.
     */
    modifier onlyOwner() {
        require(isOwner(), "Ownable: caller is not the owner");
        _;
    }

    /**
     * @dev Returns true if the caller is the current owner.
     */
    function isOwner() public view returns (bool) {
        return _msgSender() == _owner;
    }

    /**
     * @dev Leaves the contract without owner. It will not be possible to call
     * `onlyOwner` functions anymore. Can only be called by the current owner.
     *
     * NOTE: Renouncing ownership will leave the contract without an owner,
     * thereby removing any functionality that is only available to the owner.
     */
    function renounceOwnership() public onlyOwner {
        emit OwnershipTransferred(_owner, address(0));
        _owner = address(0);
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Can only be called by the current owner.
     */
    function transferOwnership(address newOwner) public onlyOwner {
        _transferOwnership(newOwner);
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     */
    function _transferOwnership(address newOwner) internal {
        require(newOwner != address(0), "Ownable: new owner is the zero address");
        emit OwnershipTransferred(_owner, newOwner);
        _owner = newOwner;
    }
}
          

/openzeppelin-solidity/contracts/token/ERC20/IERC20.sol

pragma solidity ^0.5.0;

/**
 * @dev Interface of the ERC20 standard as defined in the EIP. Does not include
 * the optional functions; to access them see {ERC20Detailed}.
 */
interface IERC20 {
    /**
     * @dev Returns the amount of tokens in existence.
     */
    function totalSupply() external view returns (uint256);

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

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

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

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

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

    /**
     * @dev Emitted when `value` tokens are moved from one account (`from`) to
     * another (`to`).
     *
     * Note that `value` may be zero.
     */
    event Transfer(address indexed from, address indexed to, uint256 value);

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

/openzeppelin-solidity/contracts/utils/SafeCast.sol

pragma solidity ^0.5.0;


/**
 * @dev Wrappers over Solidity's uintXX casting operators with added overflow
 * checks.
 *
 * Downcasting from uint256 in Solidity does not revert on overflow. This can
 * easily result in undesired exploitation or bugs, since developers usually
 * assume that overflows raise errors. `SafeCast` restores this intuition by
 * reverting the transaction when such an operation overflows.
 *
 * Using this library instead of the unchecked operations eliminates an entire
 * class of bugs, so it's recommended to use it always.
 *
 * Can be combined with {SafeMath} to extend it to smaller types, by performing
 * all math on `uint256` and then downcasting.
 *
 * _Available since v2.5.0._
 */
library SafeCast {

    /**
     * @dev Returns the downcasted uint128 from uint256, reverting on
     * overflow (when the input is greater than largest uint128).
     *
     * Counterpart to Solidity's `uint128` operator.
     *
     * Requirements:
     *
     * - input must fit into 128 bits
     */
    function toUint128(uint256 value) internal pure returns (uint128) {
        require(value < 2**128, "SafeCast: value doesn\'t fit in 128 bits");
        return uint128(value);
    }

    /**
     * @dev Returns the downcasted uint64 from uint256, reverting on
     * overflow (when the input is greater than largest uint64).
     *
     * Counterpart to Solidity's `uint64` operator.
     *
     * Requirements:
     *
     * - input must fit into 64 bits
     */
    function toUint64(uint256 value) internal pure returns (uint64) {
        require(value < 2**64, "SafeCast: value doesn\'t fit in 64 bits");
        return uint64(value);
    }

    /**
     * @dev Returns the downcasted uint32 from uint256, reverting on
     * overflow (when the input is greater than largest uint32).
     *
     * Counterpart to Solidity's `uint32` operator.
     *
     * Requirements:
     *
     * - input must fit into 32 bits
     */
    function toUint32(uint256 value) internal pure returns (uint32) {
        require(value < 2**32, "SafeCast: value doesn\'t fit in 32 bits");
        return uint32(value);
    }

    /**
     * @dev Returns the downcasted uint16 from uint256, reverting on
     * overflow (when the input is greater than largest uint16).
     *
     * Counterpart to Solidity's `uint16` operator.
     *
     * Requirements:
     *
     * - input must fit into 16 bits
     */
    function toUint16(uint256 value) internal pure returns (uint16) {
        require(value < 2**16, "SafeCast: value doesn\'t fit in 16 bits");
        return uint16(value);
    }

    /**
     * @dev Returns the downcasted uint8 from uint256, reverting on
     * overflow (when the input is greater than largest uint8).
     *
     * Counterpart to Solidity's `uint8` operator.
     *
     * Requirements:
     *
     * - input must fit into 8 bits.
     */
    function toUint8(uint256 value) internal pure returns (uint8) {
        require(value < 2**8, "SafeCast: value doesn\'t fit in 8 bits");
        return uint8(value);
    }
}
          

Contract ABI

[{"type":"constructor","stateMutability":"nonpayable","payable":false,"inputs":[{"type":"bool","name":"test","internalType":"bool"}]},{"type":"event","name":"AttestationRegistered","inputs":[{"type":"bytes32","name":"identifier","internalType":"bytes32","indexed":true},{"type":"address","name":"issuer","internalType":"address","indexed":true},{"type":"address","name":"account","internalType":"address","indexed":true},{"type":"address","name":"signer","internalType":"address","indexed":false},{"type":"uint64","name":"issuedOn","internalType":"uint64","indexed":false},{"type":"uint64","name":"publishedOn","internalType":"uint64","indexed":false}],"anonymous":false},{"type":"event","name":"AttestationRevoked","inputs":[{"type":"bytes32","name":"identifier","internalType":"bytes32","indexed":true},{"type":"address","name":"issuer","internalType":"address","indexed":true},{"type":"address","name":"account","internalType":"address","indexed":true},{"type":"address","name":"signer","internalType":"address","indexed":false},{"type":"uint64","name":"issuedOn","internalType":"uint64","indexed":false},{"type":"uint64","name":"publishedOn","internalType":"uint64","indexed":false}],"anonymous":false},{"type":"event","name":"EIP712DomainSeparatorSet","inputs":[{"type":"bytes32","name":"eip712DomainSeparator","internalType":"bytes32","indexed":false}],"anonymous":false},{"type":"event","name":"OwnershipTransferred","inputs":[{"type":"address","name":"previousOwner","internalType":"address","indexed":true},{"type":"address","name":"newOwner","internalType":"address","indexed":true}],"anonymous":false},{"type":"function","stateMutability":"view","payable":false,"outputs":[{"type":"bytes32","name":"","internalType":"bytes32"}],"name":"EIP712_OWNERSHIP_ATTESTATION_TYPEHASH","inputs":[],"constant":true},{"type":"function","stateMutability":"view","payable":false,"outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"MAX_ATTESTATIONS_PER_IDENTIFIER","inputs":[],"constant":true},{"type":"function","stateMutability":"view","payable":false,"outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"MAX_IDENTIFIERS_PER_ADDRESS","inputs":[],"constant":true},{"type":"function","stateMutability":"view","payable":false,"outputs":[{"type":"bytes32","name":"","internalType":"bytes32"}],"name":"addressToIdentifiers","inputs":[{"type":"address","name":"","internalType":"address"},{"type":"address","name":"","internalType":"address"},{"type":"uint256","name":"","internalType":"uint256"}],"constant":true},{"type":"function","stateMutability":"nonpayable","payable":false,"outputs":[],"name":"batchRevokeAttestations","inputs":[{"type":"address","name":"issuer","internalType":"address"},{"type":"bytes32[]","name":"identifiers","internalType":"bytes32[]"},{"type":"address[]","name":"accounts","internalType":"address[]"}],"constant":false},{"type":"function","stateMutability":"view","payable":false,"outputs":[{"type":"bytes32","name":"","internalType":"bytes32"}],"name":"eip712DomainSeparator","inputs":[],"constant":true},{"type":"function","stateMutability":"pure","payable":false,"outputs":[{"type":"bytes32","name":"","internalType":"bytes32"}],"name":"getUniqueAttestationHash","inputs":[{"type":"bytes32","name":"identifier","internalType":"bytes32"},{"type":"address","name":"issuer","internalType":"address"},{"type":"address","name":"account","internalType":"address"},{"type":"address","name":"signer","internalType":"address"},{"type":"uint64","name":"issuedOn","internalType":"uint64"}],"constant":true},{"type":"function","stateMutability":"pure","payable":false,"outputs":[{"type":"uint256","name":"","internalType":"uint256"},{"type":"uint256","name":"","internalType":"uint256"},{"type":"uint256","name":"","internalType":"uint256"},{"type":"uint256","name":"","internalType":"uint256"}],"name":"getVersionNumber","inputs":[],"constant":true},{"type":"function","stateMutability":"view","payable":false,"outputs":[{"type":"address","name":"account","internalType":"address"},{"type":"address","name":"signer","internalType":"address"},{"type":"uint64","name":"issuedOn","internalType":"uint64"},{"type":"uint64","name":"publishedOn","internalType":"uint64"}],"name":"identifierToAttestations","inputs":[{"type":"bytes32","name":"","internalType":"bytes32"},{"type":"address","name":"","internalType":"address"},{"type":"uint256","name":"","internalType":"uint256"}],"constant":true},{"type":"function","stateMutability":"nonpayable","payable":false,"outputs":[],"name":"initialize","inputs":[],"constant":false},{"type":"function","stateMutability":"view","payable":false,"outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"initialized","inputs":[],"constant":true},{"type":"function","stateMutability":"view","payable":false,"outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"isOwner","inputs":[],"constant":true},{"type":"function","stateMutability":"view","payable":false,"outputs":[{"type":"uint256[]","name":"countsPerIssuer","internalType":"uint256[]"},{"type":"address[]","name":"accounts","internalType":"address[]"},{"type":"address[]","name":"signers","internalType":"address[]"},{"type":"uint64[]","name":"issuedOns","internalType":"uint64[]"},{"type":"uint64[]","name":"publishedOns","internalType":"uint64[]"}],"name":"lookupAttestations","inputs":[{"type":"bytes32","name":"identifier","internalType":"bytes32"},{"type":"address[]","name":"trustedIssuers","internalType":"address[]"}],"constant":true},{"type":"function","stateMutability":"view","payable":false,"outputs":[{"type":"uint256[]","name":"countsPerIssuer","internalType":"uint256[]"},{"type":"bytes32[]","name":"identifiers","internalType":"bytes32[]"}],"name":"lookupIdentifiers","inputs":[{"type":"address","name":"account","internalType":"address"},{"type":"address[]","name":"trustedIssuers","internalType":"address[]"}],"constant":true},{"type":"function","stateMutability":"view","payable":false,"outputs":[{"type":"address","name":"","internalType":"address"}],"name":"owner","inputs":[],"constant":true},{"type":"function","stateMutability":"nonpayable","payable":false,"outputs":[],"name":"registerAttestation","inputs":[{"type":"bytes32","name":"identifier","internalType":"bytes32"},{"type":"address","name":"issuer","internalType":"address"},{"type":"address","name":"account","internalType":"address"},{"type":"address","name":"signer","internalType":"address"},{"type":"uint64","name":"issuedOn","internalType":"uint64"},{"type":"uint8","name":"v","internalType":"uint8"},{"type":"bytes32","name":"r","internalType":"bytes32"},{"type":"bytes32","name":"s","internalType":"bytes32"}],"constant":false},{"type":"function","stateMutability":"nonpayable","payable":false,"outputs":[],"name":"registerAttestationAsIssuer","inputs":[{"type":"bytes32","name":"identifier","internalType":"bytes32"},{"type":"address","name":"account","internalType":"address"},{"type":"uint64","name":"issuedOn","internalType":"uint64"}],"constant":false},{"type":"function","stateMutability":"view","payable":false,"outputs":[{"type":"address","name":"","internalType":"contract IRegistry"}],"name":"registryContract","inputs":[],"constant":true},{"type":"function","stateMutability":"nonpayable","payable":false,"outputs":[],"name":"renounceOwnership","inputs":[],"constant":false},{"type":"function","stateMutability":"nonpayable","payable":false,"outputs":[],"name":"revokeAttestation","inputs":[{"type":"bytes32","name":"identifier","internalType":"bytes32"},{"type":"address","name":"issuer","internalType":"address"},{"type":"address","name":"account","internalType":"address"}],"constant":false},{"type":"function","stateMutability":"view","payable":false,"outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"revokedAttestations","inputs":[{"type":"bytes32","name":"","internalType":"bytes32"}],"constant":true},{"type":"function","stateMutability":"nonpayable","payable":false,"outputs":[],"name":"transferOwnership","inputs":[{"type":"address","name":"newOwner","internalType":"address"}],"constant":false},{"type":"function","stateMutability":"view","payable":false,"outputs":[],"name":"validateAttestationSig","inputs":[{"type":"bytes32","name":"identifier","internalType":"bytes32"},{"type":"address","name":"issuer","internalType":"address"},{"type":"address","name":"account","internalType":"address"},{"type":"address","name":"signer","internalType":"address"},{"type":"uint64","name":"issuedOn","internalType":"uint64"},{"type":"uint8","name":"v","internalType":"uint8"},{"type":"bytes32","name":"r","internalType":"bytes32"},{"type":"bytes32","name":"s","internalType":"bytes32"}],"constant":true}]
              

Contract Creation Code

Verify & Publish
0x60806040523480156200001157600080fd5b506040516200389e3803806200389e833981810160405260208110156200003757600080fd5b81019080805190602001909291905050508060006200005b6200012360201b60201c565b9050806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508073ffffffffffffffffffffffffffffffffffffffff16600073ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a350806200011b576001600060146101000a81548160ff0219169083151502179055505b50506200012b565b600033905090565b613763806200013b6000396000f3fe608060405234801561001057600080fd5b506004361061014d5760003560e01c80638129fc1c116100c3578063b4b773421161007c578063b4b7734214610889578063b71aae46146108a7578063cba33ce01461096b578063e27ff311146109ed578063e3a1b7c614610adb578063f2fde38b14610bdc5761014d565b80638129fc1c146105395780638da5cb5b146105435780638f32d59b1461058d57806394b09057146105af5780639b43e48014610673578063a862e12e146106915761014d565b80635b07fdd8116101155780635b07fdd8146103155780635fa2fc30146103335780636125a6c214610379578063715018a6146104af57806373dfde98146104b95780637e68298d1461051b5761014d565b80630ab3ddc6146101525780631100b32414610208578063158ef93e1461027657806328c1f99b1461029857806354255be0146102e2575b600080fd5b6101f2600480360360a081101561016857600080fd5b8101908080359060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803567ffffffffffffffff169060200190929190505050610c20565b6040518082815260200191505060405180910390f35b6102746004803603606081101561021e57600080fd5b8101908080359060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610d48565b005b61027e610f06565b604051808215151515815260200191505060405180910390f35b6102a0610f19565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6102ea610f1f565b6040518085815260200184815260200183815260200182815260200194505050505060405180910390f35b61031d610f45565b6040518082815260200191505060405180910390f35b61035f6004803603602081101561034957600080fd5b8101908080359060200190929190505050610f4b565b604051808215151515815260200191505060405180910390f35b6104106004803603604081101561038f57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803590602001906401000000008111156103cc57600080fd5b8201836020820111156103de57600080fd5b8035906020019184602083028401116401000000008311171561040057600080fd5b9091929391929390505050610f6b565b604051808060200180602001838103835285818151815260200191508051906020019060200280838360005b8381101561045757808201518184015260208101905061043c565b50505050905001838103825284818151815260200191508051906020019060200280838360005b8381101561049957808201518184015260208101905061047e565b5050505090500194505050505060405180910390f35b6104b761119c565b005b610519600480360360608110156104cf57600080fd5b8101908080359060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803567ffffffffffffffff1690602001909291905050506112d5565b005b6105236112e7565b6040518082815260200191505060405180910390f35b61054161131f565b005b61054b6113d0565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6105956113f9565b604051808215151515815260200191505060405180910390f35b61067160048036036101008110156105c657600080fd5b8101908080359060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803567ffffffffffffffff169060200190929190803560ff1690602001909291908035906020019092919080359060200190929190505050611457565b005b61067b61170d565b6040518082815260200191505060405180910390f35b610712600480360360408110156106a757600080fd5b8101908080359060200190929190803590602001906401000000008111156106ce57600080fd5b8201836020820111156106e057600080fd5b8035906020019184602083028401116401000000008311171561070257600080fd5b9091929391929390505050611712565b60405180806020018060200180602001806020018060200186810386528b818151815260200191508051906020019060200280838360005b8381101561076557808201518184015260208101905061074a565b5050505090500186810385528a818151815260200191508051906020019060200280838360005b838110156107a757808201518184015260208101905061078c565b50505050905001868103845289818151815260200191508051906020019060200280838360005b838110156107e95780820151818401526020810190506107ce565b50505050905001868103835288818151815260200191508051906020019060200280838360005b8381101561082b578082015181840152602081019050610810565b50505050905001868103825287818151815260200191508051906020019060200280838360005b8381101561086d578082015181840152602081019050610852565b505050509050019a505050505050505050505060405180910390f35b610891611bef565b6040518082815260200191505060405180910390f35b61096960048036036101008110156108be57600080fd5b8101908080359060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803567ffffffffffffffff169060200190929190803560ff1690602001909291908035906020019092919080359060200190929190505050611bf4565b005b6109d76004803603606081101561098157600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050611c1b565b6040518082815260200191505060405180910390f35b610ad960048036036060811015610a0357600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190640100000000811115610a4057600080fd5b820183602082011115610a5257600080fd5b80359060200191846020830284011164010000000083111715610a7457600080fd5b909192939192939080359060200190640100000000811115610a9557600080fd5b820183602082011115610aa757600080fd5b80359060200191846020830284011164010000000083111715610ac957600080fd5b9091929391929390505050611c56565b005b610b3160048036036060811015610af157600080fd5b8101908080359060200190929190803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050611ea5565b604051808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018367ffffffffffffffff1667ffffffffffffffff1681526020018267ffffffffffffffff1667ffffffffffffffff16815260200194505050505060405180910390f35b610c1e60048036036020811015610bf257600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050611f64565b005b600060405160200180806136be6037913960370180613572602f9139602f019050604051602081830303815290604052805190602001208686868686604051602001808781526020018681526020018573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018267ffffffffffffffff1667ffffffffffffffff168152602001965050505050505060405160208183030381529060405280519060200120905095945050505050565b3373ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161480610dad57503373ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff16145b80610ea157508173ffffffffffffffffffffffffffffffffffffffff16610dd2611fea565b73ffffffffffffffffffffffffffffffffffffffff16637b2434cb336040518263ffffffff1660e01b8152600401808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060206040518083038186803b158015610e4e57600080fd5b505afa158015610e62573d6000803e3d6000fd5b505050506040513d6020811015610e7857600080fd5b810190808051906020019092919050505073ffffffffffffffffffffffffffffffffffffffff16145b610ef6576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252603a8152602001806136f5603a913960400191505060405180910390fd5b610f018383836120c5565b505050565b600060149054906101000a900460ff1681565b61ce1081565b600080600080600180600080839350829250819150809050935093509350935090919293565b60045481565b60036020528060005260406000206000915054906101000a900460ff1681565b6060806000610fbb86868680806020026020016040519081016040528093929190818152602001838360200280828437600081840152601f19601f82011690508083019250505050505050612807565b809450819250505080604051908082528060200260200182016040528015610ff25781602001602082028038833980820191505090505b5091506060600080905060008090505b8787905081101561118a57600260008a73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600089898481811061105a57fe5b9050602002013573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208054806020026020016040519081016040528092919081815260200182805480156110fb57602002820191906000526020600020905b8154815260200190600101908083116110e7575b5050505050925060008090505b835181101561116e5783818151811061111d57fe5b602002602001015186848151811061113157fe5b60200260200101818152505061115160018461294090919063ffffffff16565b925061116760018261294090919063ffffffff16565b9050611108565b5061118360018261294090919063ffffffff16565b9050611002565b50848494509450505050935093915050565b6111a46113f9565b611216576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657281525060200191505060405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff166000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a360008060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550565b6112e283338433856129c8565b505050565b60405160200180806136be6037913960370180613572602f9139602f0190506040516020818303038152906040528051906020012081565b600060149054906101000a900460ff16156113a2576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601c8152602001807f636f6e747261637420616c726561647920696e697469616c697a65640000000081525060200191505060405180910390fd5b6001600060146101000a81548160ff0219169083151502179055506113c6336130a9565b6113ce6131ed565b565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1661143b613330565b73ffffffffffffffffffffffffffffffffffffffff1614905090565b8673ffffffffffffffffffffffffffffffffffffffff16611476611fea565b73ffffffffffffffffffffffffffffffffffffffff16637b2434cb876040518263ffffffff1660e01b8152600401808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060206040518083038186803b1580156114f257600080fd5b505afa158015611506573d6000803e3d6000fd5b505050506040513d602081101561151c57600080fd5b810190808051906020019092919050505073ffffffffffffffffffffffffffffffffffffffff1614611599576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260458152602001806134ae6045913960600191505060405180910390fd5b60006115a88989898989610c20565b90506000731578b071de10e587f49db5934a24ca830fddde196334d1a233600454848888886040518663ffffffff1660e01b8152600401808681526020018581526020018460ff1660ff1681526020018381526020018281526020019550505050505060206040518083038186803b15801561162357600080fd5b505af4158015611637573d6000803e3d6000fd5b505050506040513d602081101561164d57600080fd5b810190808051906020019092919050505090508673ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1614611701576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260148152602001807f5369676e617475726520697320696e76616c696400000000000000000000000081525060200191505060405180910390fd5b50505050505050505050565b601481565b6060806060806060600061176789898980806020026020016040519081016040528093929190818152602001838360200280828437600081840152601f19601f82011690508083019250505050505050613338565b80975081925050508060405190808252806020026020018201604052801561179e5781602001602082028038833980820191505090505b509450806040519080825280602002602001820160405280156117d05781602001602082028038833980820191505090505b509350806040519080825280602002602001820160405280156118025781602001602082028038833980820191505090505b509250806040519080825280602002602001820160405280156118345781602001602082028038833980820191505090505b50915060009050606060008090505b89899050811015611bd257600160008c815260200190815260200160002060008b8b8481811061186f57fe5b9050602002013573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020805480602002602001604051908101604052809291908181526020016000905b82821015611a2857838290600052602060002090600302016040518060800160405290816000820160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020016001820160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020016001820160149054906101000a900467ffffffffffffffff1667ffffffffffffffff1667ffffffffffffffff1681526020016002820160009054906101000a900467ffffffffffffffff1667ffffffffffffffff1667ffffffffffffffff1681525050815260200190600101906118e6565b50505050915060008090505b8251811015611bb657828181518110611a4957fe5b602002602001015160000151888581518110611a6157fe5b602002602001019073ffffffffffffffffffffffffffffffffffffffff16908173ffffffffffffffffffffffffffffffffffffffff1681525050828181518110611aa757fe5b602002602001015160200151878581518110611abf57fe5b602002602001019073ffffffffffffffffffffffffffffffffffffffff16908173ffffffffffffffffffffffffffffffffffffffff1681525050828181518110611b0557fe5b602002602001015160400151868581518110611b1d57fe5b602002602001019067ffffffffffffffff16908167ffffffffffffffff1681525050828181518110611b4b57fe5b602002602001015160600151858581518110611b6357fe5b602002602001019067ffffffffffffffff16908167ffffffffffffffff1681525050611b9960018561294090919063ffffffff16565b9350611baf60018261294090919063ffffffff16565b9050611a34565b50611bcb60018261294090919063ffffffff16565b9050611843565b508686868686965096509650965096505050939792965093509350565b601481565b611c048888888888888888611457565b611c1188888888886129c8565b5050505050505050565b60026020528260005260406000206020528160005260406000208181548110611c4057fe5b9060005260206000200160009250925050505481565b818190508484905014611cb4576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602a815260200180613625602a913960400191505060405180910390fd5b3373ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff161480611dd757508473ffffffffffffffffffffffffffffffffffffffff16611d08611fea565b73ffffffffffffffffffffffffffffffffffffffff16637b2434cb336040518263ffffffff1660e01b8152600401808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060206040518083038186803b158015611d8457600080fd5b505afa158015611d98573d6000803e3d6000fd5b505050506040513d6020811015611dae57600080fd5b810190808051906020019092919050505073ffffffffffffffffffffffffffffffffffffffff16145b611e2c576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260478152602001806136776047913960600191505060405180910390fd5b60008090505b84849050811015611e9d57611e82858583818110611e4c57fe5b9050602002013587858585818110611e6057fe5b9050602002013573ffffffffffffffffffffffffffffffffffffffff166120c5565b611e9660018261294090919063ffffffff16565b9050611e32565b505050505050565b60016020528260005260406000206020528160005260406000208181548110611eca57fe5b906000526020600020906003020160009250925050508060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16908060010160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16908060010160149054906101000a900467ffffffffffffffff16908060020160009054906101000a900467ffffffffffffffff16905084565b611f6c6113f9565b611fde576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657281525060200191505060405180910390fd5b611fe7816130a9565b50565b600061ce1073ffffffffffffffffffffffffffffffffffffffff1663dcf0aaed60405160200180807f4163636f756e74730000000000000000000000000000000000000000000000008152506008019050604051602081830303815290604052805190602001206040518263ffffffff1660e01b81526004018082815260200191505060206040518083038186803b15801561208557600080fd5b505afa158015612099573d6000803e3d6000fd5b505050506040513d60208110156120af57600080fd5b8101908080519060200190929190505050905090565b60006001600085815260200190815260200160002060008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020905060008180549050905060008090505b818110156127b0578373ffffffffffffffffffffffffffffffffffffffff1683828154811061215357fe5b906000526020600020906003020160000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16146121a557612795565b6121ad613445565b8382815481106121b957fe5b90600052602060002090600302016040518060800160405290816000820160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020016001820160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020016001820160149054906101000a900467ffffffffffffffff1667ffffffffffffffff1667ffffffffffffffff1681526020016002820160009054906101000a900467ffffffffffffffff1667ffffffffffffffff1667ffffffffffffffff1681525050905060018303821461247d578360018403815481106122fd57fe5b906000526020600020906003020184838154811061231757fe5b90600052602060002090600302016000820160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff168160000160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506001820160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff168160010160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506001820160149054906101000a900467ffffffffffffffff168160010160146101000a81548167ffffffffffffffff021916908367ffffffffffffffff1602179055506002820160009054906101000a900467ffffffffffffffff168160020160006101000a81548167ffffffffffffffff021916908367ffffffffffffffff1602179055509050505b8380548061248757fe5b6001900381819060005260206000209060030201600080820160006101000a81549073ffffffffffffffffffffffffffffffffffffffff02191690556001820160006101000a81549073ffffffffffffffffffffffffffffffffffffffff02191690556001820160146101000a81549067ffffffffffffffff02191690556002820160006101000a81549067ffffffffffffffff02191690555050905560008090506000600260008873ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008973ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020905060008180549050905060008090505b81811015612665578a8382815481106125cd57fe5b9060005260206000200154146125e25761264a565b600182038114612621578260018303815481106125fb57fe5b906000526020600020015483828154811061261257fe5b90600052602060002001819055505b8280548061262b57fe5b6001900381819060005260206000200160009055905560019350612665565b61265e60018261294090919063ffffffff16565b90506125b8565b508261266d57fe5b60006126848b8b8b88602001518960400151610c20565b905060016003600083815260200190815260200160002060006101000a81548160ff0219169083151502179055508873ffffffffffffffffffffffffffffffffffffffff168a73ffffffffffffffffffffffffffffffffffffffff168c7f0e809784d3b3d167f6c74498bc305f88df3512e888b4f0b7fa5f6d06c10a962e886020015189604001518a60600151604051808473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018367ffffffffffffffff1667ffffffffffffffff1681526020018267ffffffffffffffff1667ffffffffffffffff168152602001935050505060405180910390a45050505050505050612802565b6127a960018261294090919063ffffffff16565b9050612128565b506040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602881526020018061364f6028913960400191505060405180910390fd5b505050565b6000606060009150600083516040519080825280602002602001820160405280156128415781602001602082028038833980820191505090505b50915060008090505b845181101561293157600260008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008683815181106128a057fe5b602002602001015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208054905091506128fb828561294090919063ffffffff16565b93508183828151811061290a57fe5b60200260200101818152505061292a60018261294090919063ffffffff16565b905061284a565b50828292509250509250929050565b6000808284019050838110156129be576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601b8152602001807f536166654d6174683a206164646974696f6e206f766572666c6f77000000000081525060200191505060405180910390fd5b8091505092915050565b600360006129d98787878787610c20565b815260200190815260200160002060009054906101000a900460ff1615612a68576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601c8152602001807f4174746573746174696f6e20686173206265656e207265766f6b65640000000081525060200191505060405180910390fd5b60006001600087815260200190815260200160002060008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208054905090506014612ad660018361294090919063ffffffff16565b1115612b2d576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260328152602001806135a16032913960400191505060405180910390fd5b6014612bc26001600260008873ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008973ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208054905061294090919063ffffffff16565b1115612c19576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602e815260200180613544602e913960400191505060405180910390fd5b60008090505b81811015612d53578473ffffffffffffffffffffffffffffffffffffffff166001600089815260200190815260200160002060008873ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208281548110612c9957fe5b906000526020600020906003020160000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff161415612d38576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602b815260200180613519602b913960400191505060405180910390fd5b612d4c60018261294090919063ffffffff16565b9050612c1f565b506000429050612d61613445565b60405180608001604052808773ffffffffffffffffffffffffffffffffffffffff1681526020018673ffffffffffffffffffffffffffffffffffffffff1681526020018567ffffffffffffffff1681526020018367ffffffffffffffff1681525090506001600089815260200190815260200160002060008873ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819080600181540180825580915050906001820390600052602060002090600302016000909192909190915060008201518160000160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555060208201518160010160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555060408201518160010160146101000a81548167ffffffffffffffff021916908367ffffffffffffffff16021790555060608201518160020160006101000a81548167ffffffffffffffff021916908367ffffffffffffffff160217905550505050600260008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008873ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208890806001815401808255809150509060018203906000526020600020016000909192909190915055508573ffffffffffffffffffffffffffffffffffffffff168773ffffffffffffffffffffffffffffffffffffffff16897f3ed7f328925f9f666cc51d584ea4f0260f040f224d26993c6f89a6eb9845ed6f888887604051808473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018367ffffffffffffffff1667ffffffffffffffff1681526020018267ffffffffffffffff1667ffffffffffffffff168152602001935050505060405180910390a45050505050505050565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16141561312f576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260268152602001806134f36026913960400191505060405180910390fd5b8073ffffffffffffffffffffffffffffffffffffffff166000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a3806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b600046905060405180806135d360529139605201905060405180910390206040518060400160405280601581526020017f4665646572617465644174746573746174696f6e7300000000000000000000008152508051906020012060405180807f312e300000000000000000000000000000000000000000000000000000000000815250600301905060405180910390208330604051602001808681526020018581526020018481526020018381526020018273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200195505050505050604051602081830303815290604052805190602001206004819055507ffc0bad7bd2704c62e2e5e69325dc36cb761ace9063381f1bf0152485a052526e6004546040518082815260200191505060405180910390a150565b600033905090565b6000606060009150600083516040519080825280602002602001820160405280156133725781602001602082028038833980820191505090505b50915060008090505b8451811015613436576001600087815260200190815260200160002060008683815181106133a557fe5b602002602001015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020805490509150613400828561294090919063ffffffff16565b93508183828151811061340f57fe5b60200260200101818152505061342f60018261294090919063ffffffff16565b905061337b565b50828292509250509250929050565b6040518060800160405280600073ffffffffffffffffffffffffffffffffffffffff168152602001600073ffffffffffffffffffffffffffffffffffffffff168152602001600067ffffffffffffffff168152602001600067ffffffffffffffff168152509056fe5369676e6572206973206e6f7420612063757272656e746c7920617574686f72697a6564204174746573746174696f6e5369676e657220666f7220746865206973737565724f776e61626c653a206e6577206f776e657220697320746865207a65726f20616464726573734174746573746174696f6e20666f722074686973206163636f756e7420616c7265616479206578697374734d6178206964656e7469666965727320616c7265616479207265676973746572656420666f72206163636f756e7461646472657373206163636f756e742c61646472657373207369676e65722c75696e743634206973737565644f6e294d6178206174746573746174696f6e7320616c7265616479207265676973746572656420666f72206964656e746966696572454950373132446f6d61696e28737472696e67206e616d652c737472696e672076657273696f6e2c75696e7432353620636861696e49642c6164647265737320766572696679696e67436f6e747261637429556e657175616c206e756d626572206f66206964656e7469666965727320616e64206163636f756e74734174746573746174696f6e20746f206265207265766f6b656420646f6573206e6f7420657869737453656e64657220646f6573206e6f742068617665207065726d697373696f6e20746f207265766f6b65206174746573746174696f6e732066726f6d2074686973206973737565724f776e6572736869704174746573746174696f6e2862797465733332206964656e7469666965722c61646472657373206973737565722c53656e64657220646f6573206e6f742068617665207065726d697373696f6e20746f207265766f6b652074686973206174746573746174696f6ea265627a7a723158208fe7cc7fcc0e3c7393958ca715425f1f1852054571ceb1b99e778c1302f593d064736f6c634300050d00320000000000000000000000000000000000000000000000000000000000000000

Deployed ByteCode

0x608060405234801561001057600080fd5b506004361061014d5760003560e01c80638129fc1c116100c3578063b4b773421161007c578063b4b7734214610889578063b71aae46146108a7578063cba33ce01461096b578063e27ff311146109ed578063e3a1b7c614610adb578063f2fde38b14610bdc5761014d565b80638129fc1c146105395780638da5cb5b146105435780638f32d59b1461058d57806394b09057146105af5780639b43e48014610673578063a862e12e146106915761014d565b80635b07fdd8116101155780635b07fdd8146103155780635fa2fc30146103335780636125a6c214610379578063715018a6146104af57806373dfde98146104b95780637e68298d1461051b5761014d565b80630ab3ddc6146101525780631100b32414610208578063158ef93e1461027657806328c1f99b1461029857806354255be0146102e2575b600080fd5b6101f2600480360360a081101561016857600080fd5b8101908080359060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803567ffffffffffffffff169060200190929190505050610c20565b6040518082815260200191505060405180910390f35b6102746004803603606081101561021e57600080fd5b8101908080359060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610d48565b005b61027e610f06565b604051808215151515815260200191505060405180910390f35b6102a0610f19565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6102ea610f1f565b6040518085815260200184815260200183815260200182815260200194505050505060405180910390f35b61031d610f45565b6040518082815260200191505060405180910390f35b61035f6004803603602081101561034957600080fd5b8101908080359060200190929190505050610f4b565b604051808215151515815260200191505060405180910390f35b6104106004803603604081101561038f57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803590602001906401000000008111156103cc57600080fd5b8201836020820111156103de57600080fd5b8035906020019184602083028401116401000000008311171561040057600080fd5b9091929391929390505050610f6b565b604051808060200180602001838103835285818151815260200191508051906020019060200280838360005b8381101561045757808201518184015260208101905061043c565b50505050905001838103825284818151815260200191508051906020019060200280838360005b8381101561049957808201518184015260208101905061047e565b5050505090500194505050505060405180910390f35b6104b761119c565b005b610519600480360360608110156104cf57600080fd5b8101908080359060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803567ffffffffffffffff1690602001909291905050506112d5565b005b6105236112e7565b6040518082815260200191505060405180910390f35b61054161131f565b005b61054b6113d0565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6105956113f9565b604051808215151515815260200191505060405180910390f35b61067160048036036101008110156105c657600080fd5b8101908080359060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803567ffffffffffffffff169060200190929190803560ff1690602001909291908035906020019092919080359060200190929190505050611457565b005b61067b61170d565b6040518082815260200191505060405180910390f35b610712600480360360408110156106a757600080fd5b8101908080359060200190929190803590602001906401000000008111156106ce57600080fd5b8201836020820111156106e057600080fd5b8035906020019184602083028401116401000000008311171561070257600080fd5b9091929391929390505050611712565b60405180806020018060200180602001806020018060200186810386528b818151815260200191508051906020019060200280838360005b8381101561076557808201518184015260208101905061074a565b5050505090500186810385528a818151815260200191508051906020019060200280838360005b838110156107a757808201518184015260208101905061078c565b50505050905001868103845289818151815260200191508051906020019060200280838360005b838110156107e95780820151818401526020810190506107ce565b50505050905001868103835288818151815260200191508051906020019060200280838360005b8381101561082b578082015181840152602081019050610810565b50505050905001868103825287818151815260200191508051906020019060200280838360005b8381101561086d578082015181840152602081019050610852565b505050509050019a505050505050505050505060405180910390f35b610891611bef565b6040518082815260200191505060405180910390f35b61096960048036036101008110156108be57600080fd5b8101908080359060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803567ffffffffffffffff169060200190929190803560ff1690602001909291908035906020019092919080359060200190929190505050611bf4565b005b6109d76004803603606081101561098157600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050611c1b565b6040518082815260200191505060405180910390f35b610ad960048036036060811015610a0357600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190640100000000811115610a4057600080fd5b820183602082011115610a5257600080fd5b80359060200191846020830284011164010000000083111715610a7457600080fd5b909192939192939080359060200190640100000000811115610a9557600080fd5b820183602082011115610aa757600080fd5b80359060200191846020830284011164010000000083111715610ac957600080fd5b9091929391929390505050611c56565b005b610b3160048036036060811015610af157600080fd5b8101908080359060200190929190803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050611ea5565b604051808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018367ffffffffffffffff1667ffffffffffffffff1681526020018267ffffffffffffffff1667ffffffffffffffff16815260200194505050505060405180910390f35b610c1e60048036036020811015610bf257600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050611f64565b005b600060405160200180806136be6037913960370180613572602f9139602f019050604051602081830303815290604052805190602001208686868686604051602001808781526020018681526020018573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018267ffffffffffffffff1667ffffffffffffffff168152602001965050505050505060405160208183030381529060405280519060200120905095945050505050565b3373ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161480610dad57503373ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff16145b80610ea157508173ffffffffffffffffffffffffffffffffffffffff16610dd2611fea565b73ffffffffffffffffffffffffffffffffffffffff16637b2434cb336040518263ffffffff1660e01b8152600401808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060206040518083038186803b158015610e4e57600080fd5b505afa158015610e62573d6000803e3d6000fd5b505050506040513d6020811015610e7857600080fd5b810190808051906020019092919050505073ffffffffffffffffffffffffffffffffffffffff16145b610ef6576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252603a8152602001806136f5603a913960400191505060405180910390fd5b610f018383836120c5565b505050565b600060149054906101000a900460ff1681565b61ce1081565b600080600080600180600080839350829250819150809050935093509350935090919293565b60045481565b60036020528060005260406000206000915054906101000a900460ff1681565b6060806000610fbb86868680806020026020016040519081016040528093929190818152602001838360200280828437600081840152601f19601f82011690508083019250505050505050612807565b809450819250505080604051908082528060200260200182016040528015610ff25781602001602082028038833980820191505090505b5091506060600080905060008090505b8787905081101561118a57600260008a73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600089898481811061105a57fe5b9050602002013573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208054806020026020016040519081016040528092919081815260200182805480156110fb57602002820191906000526020600020905b8154815260200190600101908083116110e7575b5050505050925060008090505b835181101561116e5783818151811061111d57fe5b602002602001015186848151811061113157fe5b60200260200101818152505061115160018461294090919063ffffffff16565b925061116760018261294090919063ffffffff16565b9050611108565b5061118360018261294090919063ffffffff16565b9050611002565b50848494509450505050935093915050565b6111a46113f9565b611216576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657281525060200191505060405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff166000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a360008060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550565b6112e283338433856129c8565b505050565b60405160200180806136be6037913960370180613572602f9139602f0190506040516020818303038152906040528051906020012081565b600060149054906101000a900460ff16156113a2576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601c8152602001807f636f6e747261637420616c726561647920696e697469616c697a65640000000081525060200191505060405180910390fd5b6001600060146101000a81548160ff0219169083151502179055506113c6336130a9565b6113ce6131ed565b565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1661143b613330565b73ffffffffffffffffffffffffffffffffffffffff1614905090565b8673ffffffffffffffffffffffffffffffffffffffff16611476611fea565b73ffffffffffffffffffffffffffffffffffffffff16637b2434cb876040518263ffffffff1660e01b8152600401808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060206040518083038186803b1580156114f257600080fd5b505afa158015611506573d6000803e3d6000fd5b505050506040513d602081101561151c57600080fd5b810190808051906020019092919050505073ffffffffffffffffffffffffffffffffffffffff1614611599576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260458152602001806134ae6045913960600191505060405180910390fd5b60006115a88989898989610c20565b90506000731578b071de10e587f49db5934a24ca830fddde196334d1a233600454848888886040518663ffffffff1660e01b8152600401808681526020018581526020018460ff1660ff1681526020018381526020018281526020019550505050505060206040518083038186803b15801561162357600080fd5b505af4158015611637573d6000803e3d6000fd5b505050506040513d602081101561164d57600080fd5b810190808051906020019092919050505090508673ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1614611701576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260148152602001807f5369676e617475726520697320696e76616c696400000000000000000000000081525060200191505060405180910390fd5b50505050505050505050565b601481565b6060806060806060600061176789898980806020026020016040519081016040528093929190818152602001838360200280828437600081840152601f19601f82011690508083019250505050505050613338565b80975081925050508060405190808252806020026020018201604052801561179e5781602001602082028038833980820191505090505b509450806040519080825280602002602001820160405280156117d05781602001602082028038833980820191505090505b509350806040519080825280602002602001820160405280156118025781602001602082028038833980820191505090505b509250806040519080825280602002602001820160405280156118345781602001602082028038833980820191505090505b50915060009050606060008090505b89899050811015611bd257600160008c815260200190815260200160002060008b8b8481811061186f57fe5b9050602002013573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020805480602002602001604051908101604052809291908181526020016000905b82821015611a2857838290600052602060002090600302016040518060800160405290816000820160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020016001820160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020016001820160149054906101000a900467ffffffffffffffff1667ffffffffffffffff1667ffffffffffffffff1681526020016002820160009054906101000a900467ffffffffffffffff1667ffffffffffffffff1667ffffffffffffffff1681525050815260200190600101906118e6565b50505050915060008090505b8251811015611bb657828181518110611a4957fe5b602002602001015160000151888581518110611a6157fe5b602002602001019073ffffffffffffffffffffffffffffffffffffffff16908173ffffffffffffffffffffffffffffffffffffffff1681525050828181518110611aa757fe5b602002602001015160200151878581518110611abf57fe5b602002602001019073ffffffffffffffffffffffffffffffffffffffff16908173ffffffffffffffffffffffffffffffffffffffff1681525050828181518110611b0557fe5b602002602001015160400151868581518110611b1d57fe5b602002602001019067ffffffffffffffff16908167ffffffffffffffff1681525050828181518110611b4b57fe5b602002602001015160600151858581518110611b6357fe5b602002602001019067ffffffffffffffff16908167ffffffffffffffff1681525050611b9960018561294090919063ffffffff16565b9350611baf60018261294090919063ffffffff16565b9050611a34565b50611bcb60018261294090919063ffffffff16565b9050611843565b508686868686965096509650965096505050939792965093509350565b601481565b611c048888888888888888611457565b611c1188888888886129c8565b5050505050505050565b60026020528260005260406000206020528160005260406000208181548110611c4057fe5b9060005260206000200160009250925050505481565b818190508484905014611cb4576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602a815260200180613625602a913960400191505060405180910390fd5b3373ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff161480611dd757508473ffffffffffffffffffffffffffffffffffffffff16611d08611fea565b73ffffffffffffffffffffffffffffffffffffffff16637b2434cb336040518263ffffffff1660e01b8152600401808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060206040518083038186803b158015611d8457600080fd5b505afa158015611d98573d6000803e3d6000fd5b505050506040513d6020811015611dae57600080fd5b810190808051906020019092919050505073ffffffffffffffffffffffffffffffffffffffff16145b611e2c576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260478152602001806136776047913960600191505060405180910390fd5b60008090505b84849050811015611e9d57611e82858583818110611e4c57fe5b9050602002013587858585818110611e6057fe5b9050602002013573ffffffffffffffffffffffffffffffffffffffff166120c5565b611e9660018261294090919063ffffffff16565b9050611e32565b505050505050565b60016020528260005260406000206020528160005260406000208181548110611eca57fe5b906000526020600020906003020160009250925050508060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16908060010160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16908060010160149054906101000a900467ffffffffffffffff16908060020160009054906101000a900467ffffffffffffffff16905084565b611f6c6113f9565b611fde576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657281525060200191505060405180910390fd5b611fe7816130a9565b50565b600061ce1073ffffffffffffffffffffffffffffffffffffffff1663dcf0aaed60405160200180807f4163636f756e74730000000000000000000000000000000000000000000000008152506008019050604051602081830303815290604052805190602001206040518263ffffffff1660e01b81526004018082815260200191505060206040518083038186803b15801561208557600080fd5b505afa158015612099573d6000803e3d6000fd5b505050506040513d60208110156120af57600080fd5b8101908080519060200190929190505050905090565b60006001600085815260200190815260200160002060008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020905060008180549050905060008090505b818110156127b0578373ffffffffffffffffffffffffffffffffffffffff1683828154811061215357fe5b906000526020600020906003020160000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16146121a557612795565b6121ad613445565b8382815481106121b957fe5b90600052602060002090600302016040518060800160405290816000820160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020016001820160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020016001820160149054906101000a900467ffffffffffffffff1667ffffffffffffffff1667ffffffffffffffff1681526020016002820160009054906101000a900467ffffffffffffffff1667ffffffffffffffff1667ffffffffffffffff1681525050905060018303821461247d578360018403815481106122fd57fe5b906000526020600020906003020184838154811061231757fe5b90600052602060002090600302016000820160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff168160000160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506001820160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff168160010160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506001820160149054906101000a900467ffffffffffffffff168160010160146101000a81548167ffffffffffffffff021916908367ffffffffffffffff1602179055506002820160009054906101000a900467ffffffffffffffff168160020160006101000a81548167ffffffffffffffff021916908367ffffffffffffffff1602179055509050505b8380548061248757fe5b6001900381819060005260206000209060030201600080820160006101000a81549073ffffffffffffffffffffffffffffffffffffffff02191690556001820160006101000a81549073ffffffffffffffffffffffffffffffffffffffff02191690556001820160146101000a81549067ffffffffffffffff02191690556002820160006101000a81549067ffffffffffffffff02191690555050905560008090506000600260008873ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008973ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020905060008180549050905060008090505b81811015612665578a8382815481106125cd57fe5b9060005260206000200154146125e25761264a565b600182038114612621578260018303815481106125fb57fe5b906000526020600020015483828154811061261257fe5b90600052602060002001819055505b8280548061262b57fe5b6001900381819060005260206000200160009055905560019350612665565b61265e60018261294090919063ffffffff16565b90506125b8565b508261266d57fe5b60006126848b8b8b88602001518960400151610c20565b905060016003600083815260200190815260200160002060006101000a81548160ff0219169083151502179055508873ffffffffffffffffffffffffffffffffffffffff168a73ffffffffffffffffffffffffffffffffffffffff168c7f0e809784d3b3d167f6c74498bc305f88df3512e888b4f0b7fa5f6d06c10a962e886020015189604001518a60600151604051808473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018367ffffffffffffffff1667ffffffffffffffff1681526020018267ffffffffffffffff1667ffffffffffffffff168152602001935050505060405180910390a45050505050505050612802565b6127a960018261294090919063ffffffff16565b9050612128565b506040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602881526020018061364f6028913960400191505060405180910390fd5b505050565b6000606060009150600083516040519080825280602002602001820160405280156128415781602001602082028038833980820191505090505b50915060008090505b845181101561293157600260008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008683815181106128a057fe5b602002602001015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208054905091506128fb828561294090919063ffffffff16565b93508183828151811061290a57fe5b60200260200101818152505061292a60018261294090919063ffffffff16565b905061284a565b50828292509250509250929050565b6000808284019050838110156129be576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601b8152602001807f536166654d6174683a206164646974696f6e206f766572666c6f77000000000081525060200191505060405180910390fd5b8091505092915050565b600360006129d98787878787610c20565b815260200190815260200160002060009054906101000a900460ff1615612a68576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601c8152602001807f4174746573746174696f6e20686173206265656e207265766f6b65640000000081525060200191505060405180910390fd5b60006001600087815260200190815260200160002060008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208054905090506014612ad660018361294090919063ffffffff16565b1115612b2d576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260328152602001806135a16032913960400191505060405180910390fd5b6014612bc26001600260008873ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008973ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208054905061294090919063ffffffff16565b1115612c19576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602e815260200180613544602e913960400191505060405180910390fd5b60008090505b81811015612d53578473ffffffffffffffffffffffffffffffffffffffff166001600089815260200190815260200160002060008873ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208281548110612c9957fe5b906000526020600020906003020160000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff161415612d38576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602b815260200180613519602b913960400191505060405180910390fd5b612d4c60018261294090919063ffffffff16565b9050612c1f565b506000429050612d61613445565b60405180608001604052808773ffffffffffffffffffffffffffffffffffffffff1681526020018673ffffffffffffffffffffffffffffffffffffffff1681526020018567ffffffffffffffff1681526020018367ffffffffffffffff1681525090506001600089815260200190815260200160002060008873ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819080600181540180825580915050906001820390600052602060002090600302016000909192909190915060008201518160000160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555060208201518160010160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555060408201518160010160146101000a81548167ffffffffffffffff021916908367ffffffffffffffff16021790555060608201518160020160006101000a81548167ffffffffffffffff021916908367ffffffffffffffff160217905550505050600260008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008873ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208890806001815401808255809150509060018203906000526020600020016000909192909190915055508573ffffffffffffffffffffffffffffffffffffffff168773ffffffffffffffffffffffffffffffffffffffff16897f3ed7f328925f9f666cc51d584ea4f0260f040f224d26993c6f89a6eb9845ed6f888887604051808473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018367ffffffffffffffff1667ffffffffffffffff1681526020018267ffffffffffffffff1667ffffffffffffffff168152602001935050505060405180910390a45050505050505050565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16141561312f576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260268152602001806134f36026913960400191505060405180910390fd5b8073ffffffffffffffffffffffffffffffffffffffff166000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a3806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b600046905060405180806135d360529139605201905060405180910390206040518060400160405280601581526020017f4665646572617465644174746573746174696f6e7300000000000000000000008152508051906020012060405180807f312e300000000000000000000000000000000000000000000000000000000000815250600301905060405180910390208330604051602001808681526020018581526020018481526020018381526020018273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200195505050505050604051602081830303815290604052805190602001206004819055507ffc0bad7bd2704c62e2e5e69325dc36cb761ace9063381f1bf0152485a052526e6004546040518082815260200191505060405180910390a150565b600033905090565b6000606060009150600083516040519080825280602002602001820160405280156133725781602001602082028038833980820191505090505b50915060008090505b8451811015613436576001600087815260200190815260200160002060008683815181106133a557fe5b602002602001015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020805490509150613400828561294090919063ffffffff16565b93508183828151811061340f57fe5b60200260200101818152505061342f60018261294090919063ffffffff16565b905061337b565b50828292509250509250929050565b6040518060800160405280600073ffffffffffffffffffffffffffffffffffffffff168152602001600073ffffffffffffffffffffffffffffffffffffffff168152602001600067ffffffffffffffff168152602001600067ffffffffffffffff168152509056fe5369676e6572206973206e6f7420612063757272656e746c7920617574686f72697a6564204174746573746174696f6e5369676e657220666f7220746865206973737565724f776e61626c653a206e6577206f776e657220697320746865207a65726f20616464726573734174746573746174696f6e20666f722074686973206163636f756e7420616c7265616479206578697374734d6178206964656e7469666965727320616c7265616479207265676973746572656420666f72206163636f756e7461646472657373206163636f756e742c61646472657373207369676e65722c75696e743634206973737565644f6e294d6178206174746573746174696f6e7320616c7265616479207265676973746572656420666f72206964656e746966696572454950373132446f6d61696e28737472696e67206e616d652c737472696e672076657273696f6e2c75696e7432353620636861696e49642c6164647265737320766572696679696e67436f6e747261637429556e657175616c206e756d626572206f66206964656e7469666965727320616e64206163636f756e74734174746573746174696f6e20746f206265207265766f6b656420646f6573206e6f7420657869737453656e64657220646f6573206e6f742068617665207065726d697373696f6e20746f207265766f6b65206174746573746174696f6e732066726f6d2074686973206973737565724f776e6572736869704174746573746174696f6e2862797465733332206964656e7469666965722c61646472657373206973737565722c53656e64657220646f6573206e6f742068617665207065726d697373696f6e20746f207265766f6b652074686973206174746573746174696f6ea265627a7a723158208fe7cc7fcc0e3c7393958ca715425f1f1852054571ceb1b99e778c1302f593d064736f6c634300050d0032