[
  {
    "path": ".github/workflows/publish.yaml",
    "content": "name: Publish Docker image\n\non:\n  push:\n    tags:\n      - 'v*'\n\njobs:\n  push_to_registry:\n    name: Push Docker image to Docker Hub\n    runs-on: ubuntu-latest\n    steps:\n      - name: Check out the repo\n        uses: actions/checkout@v3\n\n      - name: Set up QEMU\n        uses: docker/setup-qemu-action@v2\n      \n      - name: Set up Docker Buildx\n        uses: docker/setup-buildx-action@v2\n      \n      - name: Log in to Docker Hub\n        uses: docker/login-action@f054a8b539a109f9f41c372932f1ae047eff08c9\n        with:\n          username: ${{ secrets.DOCKER_USERNAME }}\n          password: ${{ secrets.DOCKER_PASSWORD }}\n      \n      - name: Extract metadata (tags, labels) for Docker\n        id: meta\n        uses: docker/metadata-action@98669ae865ea3cffbcbaa878cf57c20bbf1c6c38\n        with:\n          images: sismo/sismo-protocol\n      \n      - name: Build and push Docker image\n        uses: docker/build-push-action@v3\n        with:\n          context: .\n          push: true\n          platforms: linux/amd64,linux/arm64/v8\n          tags: ${{ steps.meta.outputs.tags }}\n          labels: ${{ steps.meta.outputs.labels }}\n\n"
  },
  {
    "path": ".github/workflows/push.yaml",
    "content": "name: On Push checks\non:\n  push:\njobs:\n  test:\n    runs-on: ubuntu-latest\n    name: Compile, test and test deploy\n    steps:\n      - uses: actions/checkout@v2\n      - name: Setup node\n        uses: actions/setup-node@v2\n        with:\n          node-version: '16.x'\n          cache: yarn\n      - run: yarn install --frozen-lockfile\n      - run: yarn compile\n      - name: test\n        run: yarn test\n      - name: deploy local script\n        run: npx concurrently --kill-others -s command-1  \"npx hardhat node\" \"sleep 2 & npx yarn deploy:local\"\n"
  },
  {
    "path": ".gitignore",
    "content": "node_modules\ndeployments\ncache\nartifacts\ntypechain-types\ntypes/**\n.vscode\ndeployments/local\nsave\n.env\n\n"
  },
  {
    "path": ".mocharc.json",
    "content": "{\n  \"require\": \"ts-node/register/files\",\n  \"timeout\": 20000\n}\n"
  },
  {
    "path": ".prettierrc",
    "content": "{\n  \"printWidth\": 100,\n  \"trailingComma\": \"es5\",\n  \"semi\": true,\n  \"singleQuote\": true,\n  \"tabWidth\": 2,\n  \"overrides\": [\n    {\n      \"files\": \"*.sol\",\n      \"options\": {\n        \"semi\": true,\n        \"printWidth\": 100\n      }\n    }\n  ]\n}"
  },
  {
    "path": "Dockerfile",
    "content": "FROM node:16 AS build\n\nWORKDIR /usr/src/build\n# Install dependencies\nCOPY package.json yarn.lock ./\n\nRUN yarn install --frozen-lockfile\nCOPY . .\nRUN yarn compile\n\nFROM node:16-slim AS run\nRUN apt-get -y update && apt-get -y install netcat\nWORKDIR /usr/src/app\nCOPY --from=build /usr/src/build/ .\n"
  },
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2022 Sismo\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "<br />\n<div align=\"center\">\n  <img src=\"docs/top.png\" alt=\"Logo\" width=\"100\" height=\"100\" style=\"borderRadius: 20px\">\n\n  <h3 align=\"center\">\n    Sismo Protocol Contracts\n  </h3>\n\n  <p align=\"center\">\n    Made by <a href=\"https://www.sismo.io/\" target=\"_blank\">Sismo</a>\n  </p>\n  \n  <p align=\"center\">\n    <a href=\"https://discord.gg/sismo\" target=\"_blank\">\n        <img src=\"https://img.shields.io/badge/Discord-7289DA?style=for-the-badge&logo=discord&logoColor=white\"/>\n    </a>\n    <a href=\"https://twitter.com/sismo_eth\" target=\"_blank\">\n        <img src=\"https://img.shields.io/badge/Twitter-1DA1F2?style=for-the-badge&logo=twitter&logoColor=white\"/>\n    </a>\n  </p>\n  <a href=\"https://www.sismo.io/\" target=\"_blank\">\n    \n  </a>\n</div>\n<br/>\nThis repository contains the smart contracts of the Sismo Protocol.\n\nThere are three core contracts:\n\n - `core/AttestationsRegistry.sol`: The registry stores all attestations. It is owned by the governance that authorizes/unauthorize issuers to record in it\n - `core/Attester.sol` The standard abstract contract must be inherited by attesters. Attesters are issuers of attestations. They verify user requests and build attestations that will be recorded in the registry\n - `core/Badges.sol` Reads the registry. Stateless Non Transferable Token view of attestations (ERC1155)\n\nIt also contains implementations of attester in `attesters/`:\n- `HydraS1SimpleAttester.sol`: ZK Attester using the [Hydra S1 Proving Scheme](https://hydra-s1.docs.sismo.io) and the notion of nullifiers. Users must provide a ZK Proof along with their request to generate attestations\n- `HydraS1AccountboundAttester.sol`: Accountbound version of the Simple Hydra S1 Simple Attester. (Users can update at will where the attestation is stored)\n\n<br/><br/>\n\n\n## Sismo protocol\n\nA complete overview of the protocol is available in our [documentation](https://protocol.docs.sismo.io)\n\n\n## Deployed contracts\n\nDeployed contracts can be found [here](https://docs.sismo.io/sismo-docs/deployed-contract-addresses)\n\n\n## Usage\n### Installation\n```\nyarn\n```\n\n### Compile contracts\nCompile contracts using hardhat\n```\nyarn compile\n```\n\n### Test\nLaunch all tests\n\n```\nyarn test\n```\n\n### Print storage layout \n```\nyarn storage-layout\n```\n\n### Deploy on local chain\n\nTerminal tab 1\n```\nyarn chain\n```\n\nTerminal tab 2\n```\nyarn deploy:local\n```\n\n\n## Create a new Attester\n\nTo develop a new attester, you must inherit the `core/Attester.sol` abstract contract and implement the following functions: \n\n-  `_verifyRequest(request, proofData)`: You must implement the user request verification against the proof provided by the user\n-  `buildAttestations(request, proofData)`: You must build the attestations that will be recorded from a verified user request\n\nOther optional hook functions that can be implemented:\n\n- `_beforeRecordAttestations(request, proofData)`\n- `_afterRecordAttestations(request, proofData)`\n\nThe `/attesters/hydra-s1/HydraS1SimpleAttester.sol` is a good example of an attester implementing those functions.\n\nA [guide](https://attesters.docs.sismo.io) is offered in our documentation.\n\nFeel free open a PR with your new attester in `/attester`!\n\n## License\n\nDistributed under the MIT License.\n\n## Contribute\n\nPlease, feel free to open issues, PRs or simply provide feedback!\n\n## Contact\n\nPrefer [Discord](https://discord.gg/sismo) or [Twitter](https://twitter.com/sismo_eth)\n\n<br/>\n<img src=\"https://static.sismo.io/readme/bottom-main.png\" alt=\"bottom\" width=\"100%\" >\n\n"
  },
  {
    "path": "contracts/attesters/hydra-s1/HydraS1AccountboundAttester.sol",
    "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.14;\npragma experimental ABIEncoderV2;\n\nimport {Ownable} from '@openzeppelin/contracts/access/Ownable.sol';\n\nimport {IHydraS1AccountboundAttester} from './interfaces/IHydraS1AccountboundAttester.sol';\n\n// Core protocol Protocol imports\nimport {Request, Attestation, Claim} from '../../core/libs/Structs.sol';\n\n// Imports related to Hydra-S1\nimport {HydraS1SimpleAttester, IAttester, HydraS1Lib, HydraS1ProofData, HydraS1Claim} from './HydraS1SimpleAttester.sol';\n\n/**\n * @title  Hydra-S1 Accountbound Attester\n * @author Sismo\n * @notice This attester is part of the family of the Hydra-S1 Attesters.\n * Hydra-S1 attesters enable users to prove they have an account in a group in a privacy preserving way.\n * The Hydra-S1 Simple Attester contract is inherited and holds the complex Hydra S1 verification logic.\n * Request verification alongside proof verification is already implemented in the inherited HydraS1SimpleAttester, along with the buildAttestations logic.\n * However, we override the buildAttestations function to encode the nullifier and its burn count in the user attestation.\n * The _beforeRecordAttestations is also overriden to fit the Accountbound logic.\n * We invite readers to refer to:\n *    - https://hydra-s1.docs.sismo.io for a full guide through the Hydra-S1 ZK Attestations\n *    - https://hydra-s1-circuits.docs.sismo.io for circuits, prover and verifiers of Hydra-S1\n\n * This specific attester has the following characteristics:\n\n * - Zero Knowledge\n *   One cannot deduct from an attestation what source account was used to generate the underlying proof\n\n * - Non Strict (scores)\n *   If a user can generate an attestation of max value 100, they can also generate any attestation with value < 100.\n *   This attester generate attestations of scores\n\n * - Nullified\n *   Each source account gets one nullifier per claim (i.e only one attestation per source account per claim)\n *   While semaphore/ tornado cash are using the following notations: nullifierHash = hash(IdNullifier, externalNullifier)\n *   We prefered to use the naming 'nullifier' instead of 'nullifierHash' in our contracts and documentation.\n *   We also renamed 'IdNullifier' in 'sourceSecret' (the secret tied to a source account) and we kept the 'externalNullifier' notation.\n *   Finally, here is our notations at Sismo: nullifier = hash(sourceSecret, externalNullifier)\n\n * - Accountbound (opt-in, with cooldown period)\n *   The owner of this attester can set a cooldown duration for a specific group, activating the accountbound feature for this group.\n *   Users can update their attestation's destination by providing a new Hydra-S1 ZK proof \n *   It means the attestation is bound to the source account, stored on an updatable destination account.\n *   When deleting/ sending to a new destination, the nullifier will enter a cooldown period, so it remains occasional.\n *   A group that has its cooldown duration set to 0 means it has been configured to not feature accountbound attestations, attestations can not be transferred\n *   One can however know that the former and the new destinations were created using the same nullifier, thus creating a link between those two destinations.\n \n * - Renewable\n *   A nullifier can actually be reused as long as the destination of the attestation remains the same\n *   It enables users to renew or update their attestations\n **/\n\ncontract HydraS1AccountboundAttester is\n  IHydraS1AccountboundAttester,\n  HydraS1SimpleAttester,\n  Ownable\n{\n  using HydraS1Lib for HydraS1ProofData;\n  using HydraS1Lib for bytes;\n  using HydraS1Lib for Request;\n\n  uint8 public constant IMPLEMENTATION_VERSION = 5;\n\n  /*******************************************************\n    Storage layout:\n    20 slots between HydraS1SimpleAttester and HydraS1AccountboundAttester\n      1 currently used by Ownable\n      19 place holders\n    2O for config\n      1 currently used\n      19 place holders\n    20 for logic\n      2 currently used\n      18 place holders\n  *******************************************************/\n\n  // keeping some space for future config logics\n  uint256[19] private _placeHolderBeforeHydraS1Accountbound;\n\n  // cooldown durations for each groupIndex\n  mapping(uint256 => uint32) internal _cooldownDurations;\n\n  // keeping some space for future config logics\n  uint256[19] private _placeHoldersHydraS1AccountboundConfig;\n\n  mapping(uint256 => uint32) internal _nullifiersCooldownStart;\n  mapping(uint256 => uint16) internal _nullifiersBurnCount;\n\n  // keeping some space for future config logics\n  uint256[18] private _placeHoldersHydraS1AccountboundLogic;\n\n  /*******************************************************\n    INITIALIZATION FUNCTIONS                           \n  *******************************************************/\n  /**\n   * @dev Constructor. Initializes the contract\n   * @param attestationsRegistryAddress Attestations Registry contract on which the attester will write attestations\n   * @param hydraS1VerifierAddress ZK Snark Hydra-S1 Verifier contract\n   * @param availableRootsRegistryAddress Registry storing the available groups for this attester (e.g roots of registry merkle trees)\n   * @param commitmentMapperAddress commitment mapper's public key registry\n   * @param collectionIdFirst Id of the first attestation collection in which the attester is supposed to record\n   * @param collectionIdLast Id of the last attestation collection in which the attester is supposed to record\n   * @param owner Address of attester's owner\n   */\n  constructor(\n    address attestationsRegistryAddress,\n    address hydraS1VerifierAddress,\n    address availableRootsRegistryAddress,\n    address commitmentMapperAddress,\n    uint256 collectionIdFirst,\n    uint256 collectionIdLast,\n    address owner\n  )\n    HydraS1SimpleAttester(\n      attestationsRegistryAddress,\n      hydraS1VerifierAddress,\n      availableRootsRegistryAddress,\n      commitmentMapperAddress,\n      collectionIdFirst,\n      collectionIdLast\n    )\n  {\n    initialize(owner);\n  }\n\n  /**\n   * @dev Initialize function, to be called by the proxy delegating calls to this implementation\n   * @param ownerAddress Owner of the contract, has the right to authorize/unauthorize attestations issuers\n   * @notice The reinitializer modifier is needed to configure modules that are added through upgrades and that require initialization.\n   */\n  function initialize(address ownerAddress) public reinitializer(IMPLEMENTATION_VERSION) {\n    // if proxy did not setup owner yet or if called by constructor (for implem setup)\n    if (owner() == address(0) || address(this).code.length == 0) {\n      _transferOwnership(ownerAddress);\n    }\n  }\n\n  /*******************************************************\n    MANDATORY FUNCTIONS TO OVERRIDE FROM ATTESTER.SOL\n  *******************************************************/\n\n  /**\n   * @dev Returns the actual attestations constructed from the user request\n   * @param request users request. Claim of having an account part of a group of accounts\n   * @param proofData snark public input as well as snark proof\n   */\n  function buildAttestations(\n    Request calldata request,\n    bytes calldata proofData\n  ) public view virtual override(IAttester, HydraS1SimpleAttester) returns (Attestation[] memory) {\n    Attestation[] memory attestations = super.buildAttestations(request, proofData);\n\n    uint256 nullifier = proofData._getNullifier();\n    attestations[0].extraData = abi.encode(\n      attestations[0].extraData, // nullifier, from HydraS1 Simple\n      _getNextBurnCount(nullifier, attestations[0].owner) // BurnCount\n    );\n\n    return (attestations);\n  }\n\n  /*******************************************************\n    OPTIONAL HOOK VIRTUAL FUNCTIONS FROM ATTESTER.SOL\n  *******************************************************/\n  /**\n   * @dev Hook run before recording the attestation.\n   * Throws if nullifier already used, not a renewal, and nullifier on cooldown.\n   * @param request users request. Claim of having an account part of a group of accounts\n   * @param proofData provided to back the request. snark input and snark proof\n   */\n  function _beforeRecordAttestations(\n    Request calldata request,\n    bytes calldata proofData\n  ) internal virtual override {\n    uint256 nullifier = proofData._getNullifier();\n    address previousNullifierDestination = _getDestinationOfNullifier(nullifier);\n\n    HydraS1Claim memory claim = request._claim();\n\n    // check if the nullifier has already been used previously, if so it may be on cooldown\n    if (\n      previousNullifierDestination != address(0) &&\n      previousNullifierDestination != claim.destination\n    ) {\n      uint32 cooldownDuration = _getCooldownDurationForGroupIndex(claim.groupProperties.groupIndex);\n      if (cooldownDuration == 0) {\n        revert CooldownDurationNotSetForGroupIndex(claim.groupProperties.groupIndex);\n      }\n      if (_isOnCooldown(nullifier, cooldownDuration)) {\n        uint16 burnCount = _getNullifierBurnCount(nullifier);\n        revert NullifierOnCooldown(\n          nullifier,\n          previousNullifierDestination,\n          burnCount,\n          cooldownDuration\n        );\n      }\n\n      // Delete the old Attestation linked to the nullifier before recording the new one (accountbound feature)\n      _deletePreviousAttestation(claim, previousNullifierDestination);\n\n      _setNullifierOnCooldownAndIncrementBurnCount(nullifier);\n    }\n    _setDestinationForNullifier(nullifier, request.destination);\n  }\n\n  /*******************************************************\n    LOGIC FUNCTIONS RELATED TO ACCOUNTBOUND FEATURE\n  *******************************************************/\n\n  /**\n   * @dev Getter, returns the burnCount of a nullifier\n   * @param nullifier nullifier used\n   **/\n  function getNullifierBurnCount(uint256 nullifier) external view returns (uint16) {\n    return _getNullifierBurnCount(nullifier);\n  }\n\n  /**\n   * @dev Getter, returns the cooldown start of a nullifier\n   * @param nullifier nullifier used\n   **/\n  function getNullifierCooldownStart(uint256 nullifier) external view returns (uint32) {\n    return _getNullifierCooldownStart(nullifier);\n  }\n\n  /**\n   * @dev returns the nullifier for a given extraData\n   * @param extraData bytes where the nullifier is encoded\n   */\n  function getNullifierFromExtraData(\n    bytes memory extraData\n  ) external pure override(HydraS1SimpleAttester, IHydraS1AccountboundAttester) returns (uint256) {\n    (bytes memory nullifierBytes, ) = abi.decode(extraData, (bytes, uint16));\n    uint256 nullifier = abi.decode(nullifierBytes, (uint256));\n\n    return nullifier;\n  }\n\n  /**\n   * @dev Returns the burn count for a given extraData\n   * @param extraData bytes where the burnCount is encoded\n   */\n  function getBurnCountFromExtraData(bytes memory extraData) external pure returns (uint16) {\n    (, uint16 burnCount) = abi.decode(extraData, (uint256, uint16));\n\n    return burnCount;\n  }\n\n  /**\n   * @dev Checks if a nullifier is on cooldown\n   * @param nullifier user nullifier\n   * @param cooldownDuration waiting time before the user can change its badge destination\n   */\n  function _isOnCooldown(uint256 nullifier, uint32 cooldownDuration) internal view returns (bool) {\n    return _getNullifierCooldownStart(nullifier) + cooldownDuration > block.timestamp;\n  }\n\n  /**\n   * @dev Delete the previous attestation created with this nullifier\n   * @param claim user claim\n   * @param previousNullifierDestination previous destination chosen for this user nullifier\n   */\n  function _deletePreviousAttestation(\n    HydraS1Claim memory claim,\n    address previousNullifierDestination\n  ) internal {\n    address[] memory attestationOwners = new address[](1);\n    uint256[] memory attestationCollectionIds = new uint256[](1);\n\n    attestationOwners[0] = previousNullifierDestination;\n    attestationCollectionIds[0] = AUTHORIZED_COLLECTION_ID_FIRST + claim.groupProperties.groupIndex;\n\n    ATTESTATIONS_REGISTRY.deleteAttestations(attestationOwners, attestationCollectionIds);\n  }\n\n  function _setNullifierOnCooldownAndIncrementBurnCount(uint256 nullifier) internal {\n    _nullifiersCooldownStart[nullifier] = uint32(block.timestamp);\n    _nullifiersBurnCount[nullifier] += 1;\n    emit NullifierSetOnCooldown(nullifier, _nullifiersBurnCount[nullifier]);\n  }\n\n  function _getNullifierCooldownStart(uint256 nullifier) internal view returns (uint32) {\n    return _nullifiersCooldownStart[nullifier];\n  }\n\n  function _getNullifierBurnCount(uint256 nullifier) internal view returns (uint16) {\n    return _nullifiersBurnCount[nullifier];\n  }\n\n  /**\n   * @dev returns burn count or burn count + 1 if new burn will happen\n   * @param nullifier user nullifier\n   * @param claimDestination destination referenced in the user claim\n   */\n  function _getNextBurnCount(\n    uint256 nullifier,\n    address claimDestination\n  ) public view virtual returns (uint16) {\n    address previousNullifierDestination = _getDestinationOfNullifier(nullifier);\n    uint16 burnCount = _getNullifierBurnCount(nullifier);\n    // If the attestation is minted on a new destination address\n    // the burnCount that will be encoded in the extraData of the Attestation should be incremented\n    if (\n      previousNullifierDestination != address(0) && previousNullifierDestination != claimDestination\n    ) {\n      burnCount += 1;\n    }\n    return burnCount;\n  }\n\n  /*******************************************************\n    GROUP CONFIGURATION LOGIC\n  *******************************************************/\n\n  /**\n   * @dev Setter, sets the cooldown duration of a groupIndex\n   * @notice set to 0 to deactivate the accountbound feature for this group\n   * @param groupIndex internal collection id\n   * @param cooldownDuration cooldown duration we want to set for the groupIndex\n   **/\n  function setCooldownDurationForGroupIndex(\n    uint256 groupIndex,\n    uint32 cooldownDuration\n  ) external onlyOwner {\n    _cooldownDurations[groupIndex] = cooldownDuration;\n    emit CooldownDurationSetForGroupIndex(groupIndex, cooldownDuration);\n  }\n\n  /**\n   * @dev Getter, get the cooldown duration of a groupIndex\n   * @notice returns 0 when the accountbound feature is deactivated for this group\n   * @param groupIndex internal collection id\n   **/\n  function getCooldownDurationForGroupIndex(uint256 groupIndex) external view returns (uint32) {\n    return _getCooldownDurationForGroupIndex(groupIndex);\n  }\n\n  // = 0 means that the accountbound feature is deactivated for this group\n  function _getCooldownDurationForGroupIndex(uint256 groupIndex) internal view returns (uint32) {\n    return _cooldownDurations[groupIndex];\n  }\n}\n"
  },
  {
    "path": "contracts/attesters/hydra-s1/HydraS1SimpleAttester.sol",
    "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.14;\npragma experimental ABIEncoderV2;\n\nimport {IHydraS1SimpleAttester} from './interfaces/IHydraS1SimpleAttester.sol';\nimport {IHydraS1Base} from './base/IHydraS1Base.sol';\n\n// Core protocol Protocol imports\nimport {Request, Attestation, Claim} from './../../core/libs/Structs.sol';\nimport {Attester, IAttester, IAttestationsRegistry} from './../../core/Attester.sol';\n\n// Imports related to HydraS1 Proving Scheme\nimport {HydraS1Base, HydraS1Lib, HydraS1ProofData, HydraS1ProofInput, HydraS1Claim} from './base/HydraS1Base.sol';\n\n/**\n * @title  Hydra-S1 Simple Attester\n * @author Sismo\n * @notice This attester is part of the family of the Hydra-S1 Attesters.\n * Hydra-S1 attesters enable users to prove they have an account in a group in a privacy preserving way.\n * The Hydra-S1 Base abstract contract is inherited and holds the complex Hydra S1 verification logic.\n * We invite readers to refer to:\n *    - https://hydra-s1.docs.sismo.io for a full guide through the Hydra-S1 ZK Attestations\n *    - https://hydra-s1-circuits.docs.sismo.io for circuits, prover and verifiers of Hydra-S1\n\n * This specific attester has the following characteristics:\n\n * - Zero Knowledge\n *   One cannot deduct from an attestation what source account was used to generate the underlying proof\n\n * - Non Strict (scores)\n *   If a user can generate an attestation of max value 100, they can also generate any attestation with value < 100.\n *   This attester generate attestations of scores\n\n * - Nullified\n *   Each source account gets one nullifier per claim (i.e only one attestation per source account per claim)\n *   For people used to semaphore/ tornado cash people:\n *   nullifier = hash(sourceSecret, externalNullifier) <=> nullifierHash = hash(IdNullifier, externalNullifier)\n \n * - Renewable\n *   A nullifier can actually be reused as long as the destination of the attestation remains the same\n *   It enables users to renew their attestations\n **/\n\ncontract HydraS1SimpleAttester is IHydraS1SimpleAttester, HydraS1Base {\n  using HydraS1Lib for HydraS1ProofData;\n  using HydraS1Lib for bytes;\n  using HydraS1Lib for Request;\n\n  // The deployed contract will need to be authorized to write into the Attestation registry\n  // It should get write access on attestation collections from AUTHORIZED_COLLECTION_ID_FIRST to AUTHORIZED_COLLECTION_ID_LAST.\n  uint256 public immutable AUTHORIZED_COLLECTION_ID_FIRST;\n  uint256 public immutable AUTHORIZED_COLLECTION_ID_LAST;\n\n  /*******************************************************\n    Storage layout:\n      20 slots for HydraS1\n        1 slot used\n        19 place holders\n  *******************************************************/\n\n  mapping(uint256 => address) internal _nullifiersDestinations;\n\n  // keeping some space for future\n  uint256[19] private _placeHoldersHydraS1Simple;\n\n  /*******************************************************\n    INITIALIZATION FUNCTIONS                           \n  *******************************************************/\n  /**\n   * @dev Constructor. Initializes the contract\n   * @param attestationsRegistryAddress Attestations Registry contract on which the attester will write attestations\n   * @param hydraS1VerifierAddress ZK Snark Hydra-S1 Verifier contract\n   * @param availableRootsRegistryAddress Registry storing the available groups for this attester (e.g roots of registry merkle trees)\n   * @param commitmentMapperAddress commitment mapper's public key registry\n   * @param collectionIdFirst Id of the first collection in which the attester is supposed to record\n   * @param collectionIdLast Id of the last collection in which the attester is supposed to record\n   */\n  constructor(\n    address attestationsRegistryAddress,\n    address hydraS1VerifierAddress,\n    address availableRootsRegistryAddress,\n    address commitmentMapperAddress,\n    uint256 collectionIdFirst,\n    uint256 collectionIdLast\n  )\n    Attester(attestationsRegistryAddress)\n    HydraS1Base(hydraS1VerifierAddress, availableRootsRegistryAddress, commitmentMapperAddress)\n  {\n    AUTHORIZED_COLLECTION_ID_FIRST = collectionIdFirst;\n    AUTHORIZED_COLLECTION_ID_LAST = collectionIdLast;\n  }\n\n  /*******************************************************\n    MANDATORY FUNCTIONS TO OVERRIDE FROM ATTESTER.SOL\n  *******************************************************/\n\n  /**\n   * @dev Throws if user request is invalid when verified against\n   * Look into HydraS1Base for more details\n   * @param request users request. Claim of having an account part of a group of accounts\n   * @param proofData provided to back the request. snark input and snark proof\n   */\n  function _verifyRequest(\n    Request calldata request,\n    bytes calldata proofData\n  ) internal virtual override {\n    HydraS1ProofData memory snarkProof = abi.decode(proofData, (HydraS1ProofData));\n    HydraS1ProofInput memory snarkInput = snarkProof._input();\n    HydraS1Claim memory claim = request._claim();\n\n    // verifies that the proof corresponds to the claim\n    _validateInput(claim, snarkInput);\n    // verifies the proof validity\n    _verifyProof(snarkProof);\n  }\n\n  /**\n   * @dev Returns attestations that will be recorded, constructed from the user request\n   * @param request users request. Claim of having an account part of a group of accounts\n   */\n  function buildAttestations(\n    Request calldata request,\n    bytes calldata proofData\n  ) public view virtual override(IAttester, Attester) returns (Attestation[] memory) {\n    HydraS1Claim memory claim = request._claim();\n\n    Attestation[] memory attestations = new Attestation[](1);\n\n    uint256 attestationCollectionId = AUTHORIZED_COLLECTION_ID_FIRST +\n      claim.groupProperties.groupIndex;\n\n    if (attestationCollectionId > AUTHORIZED_COLLECTION_ID_LAST)\n      revert CollectionIdOutOfBound(attestationCollectionId);\n\n    address issuer = address(this);\n\n    uint256 nullifier = proofData._getNullifier();\n\n    attestations[0] = Attestation(\n      attestationCollectionId,\n      claim.destination,\n      issuer,\n      claim.claimedValue,\n      claim.groupProperties.generationTimestamp,\n      abi.encode(nullifier)\n    );\n    return (attestations);\n  }\n\n  /*******************************************************\n    OPTIONAL HOOK VIRTUAL FUNCTIONS FROM ATTESTER.SOL\n  *******************************************************/\n\n  /**\n   * @dev Hook run before recording the attestation.\n   * Throws if nullifier already used and not a renewal (e.g destination different that last)\n   * @param request users request. Claim of having an account part of a group of accounts\n   * @param proofData provided to back the request. snark input and snark proof\n   */\n  function _beforeRecordAttestations(\n    Request calldata request,\n    bytes calldata proofData\n  ) internal virtual override {\n    // we get the nullifier used from the snark input in the data provided\n    uint256 nullifier = proofData._getNullifier();\n    address currentDestination = _getDestinationOfNullifier(nullifier);\n\n    if (currentDestination != address(0) && currentDestination != request.destination) {\n      revert NullifierUsed(nullifier);\n    }\n\n    _setDestinationForNullifier(nullifier, request.destination);\n  }\n\n  /*******************************************************\n    Hydra-S1 MANDATORY FUNCTIONS FROM Hydra-S1 Base Attester\n  *******************************************************/\n\n  /**\n   * @dev Returns the external nullifier from a user claim\n   * @param claim user Hydra-S1 claim = have an account with a specific value in a specific group\n   * nullifier = hash(sourceSecretHash, externalNullifier), which is verified inside the snark\n   * users bring sourceSecretHash as private input in snark which guarantees privacy\n   \n   * Here we chose externalNullifier = hash(attesterAddress, claim.GroupId)\n   * Creates one nullifier per group, per user and makes sure no collision with other attester's nullifiers\n  **/\n  function _getExternalNullifierOfClaim(\n    HydraS1Claim memory claim\n  ) internal view override returns (uint256) {\n    uint256 externalNullifier = _encodeInSnarkField(\n      address(this),\n      claim.groupProperties.groupIndex\n    );\n    return externalNullifier;\n  }\n\n  /**\n   * @dev returns the nullifier for a given extraData\n   * @param extraData bytes where the nullifier is encoded\n   */\n  function getNullifierFromExtraData(\n    bytes memory extraData\n  ) external pure virtual override(IHydraS1Base, HydraS1Base) returns (uint256) {\n    return abi.decode(extraData, (uint256));\n  }\n\n  /*******************************************************\n    Hydra-S1 Attester Specific Functions\n  *******************************************************/\n\n  /**\n   * @dev Getter, returns the last attestation destination of a nullifier\n   * @param nullifier nullifier used\n   **/\n  function getDestinationOfNullifier(uint256 nullifier) external view override returns (address) {\n    return _getDestinationOfNullifier(nullifier);\n  }\n\n  function _setDestinationForNullifier(uint256 nullifier, address destination) internal virtual {\n    _nullifiersDestinations[nullifier] = destination;\n    emit NullifierDestinationUpdated(nullifier, destination);\n  }\n\n  function _getDestinationOfNullifier(uint256 nullifier) internal view returns (address) {\n    return _nullifiersDestinations[nullifier];\n  }\n\n  function _encodeInSnarkField(address addr, uint256 nb) internal pure returns (uint256) {\n    return uint256(keccak256(abi.encode(addr, nb))) % HydraS1Lib.SNARK_FIELD;\n  }\n}\n"
  },
  {
    "path": "contracts/attesters/hydra-s1/base/HydraS1Base.sol",
    "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.14;\npragma experimental ABIEncoderV2;\n\nimport {IHydraS1Base} from './IHydraS1Base.sol';\nimport {Attester} from '../../../core/Attester.sol';\nimport {Initializable} from '@openzeppelin/contracts/proxy/utils/Initializable.sol';\n\n// Protocol imports\nimport {Request, Attestation, Claim} from '../../../core/libs/Structs.sol';\n\n// Imports related to Hydra S1 ZK Proving Scheme\nimport {HydraS1Verifier, HydraS1Lib, HydraS1Claim, HydraS1ProofData, HydraS1ProofInput, HydraS1GroupProperties} from '../libs/HydraS1Lib.sol';\nimport {ICommitmentMapperRegistry} from '../../../periphery/utils/CommitmentMapperRegistry.sol';\nimport {IAvailableRootsRegistry} from '../../../periphery/utils/AvailableRootsRegistry.sol';\n\n/**\n * @title Hydra-S1 Base Attester\n * @author Sismo\n * @notice Abstract contract that facilitates the use of the Hydra-S1 ZK Proving Scheme.\n * Hydra-S1 is single source, single group: it allows users to verify they are part of one and only one group at a time\n * It is inherited by the family of Hydra-S1 attesters.\n * It contains the user input checking and the ZK-SNARK proof verification.\n * We invite readers to refer to the following:\n *    - https://hydra-s1.docs.sismo.io for a full guide through the Hydra-S1 ZK Attestations\n *    - https://hydra-s1-circuits.docs.sismo.io for circuits, prover and verifiers of Hydra-S1\n *\n */\nabstract contract HydraS1Base is IHydraS1Base, Attester, Initializable {\n  using HydraS1Lib for HydraS1ProofData;\n\n  // ZK-SNARK Verifier\n  HydraS1Verifier immutable VERIFIER;\n  // Registry storing the Commitment Mapper EdDSA Public key\n  ICommitmentMapperRegistry immutable COMMITMENT_MAPPER_REGISTRY;\n  // Registry storing the Registry Tree Roots of the Attester's available ClaimData\n  IAvailableRootsRegistry immutable AVAILABLE_ROOTS_REGISTRY;\n\n  /*******************************************************\n    INITIALIZATION FUNCTIONS\n  *******************************************************/\n\n  /**\n   * @dev Constructor. Initializes the contract\n   * @param hydraS1VerifierAddress ZK Snark Verifier contract\n   * @param availableRootsRegistryAddress Registry where is the Available Data (Registry Merkle Roots)\n   * @param commitmentMapperAddress Commitment mapper's public key registry\n   */\n  constructor(\n    address hydraS1VerifierAddress,\n    address availableRootsRegistryAddress,\n    address commitmentMapperAddress\n  ) {\n    VERIFIER = HydraS1Verifier(hydraS1VerifierAddress);\n    AVAILABLE_ROOTS_REGISTRY = IAvailableRootsRegistry(availableRootsRegistryAddress);\n    COMMITMENT_MAPPER_REGISTRY = ICommitmentMapperRegistry(commitmentMapperAddress);\n  }\n\n  /**\n   * @dev Getter of Hydra-S1 Verifier contract\n   */\n  function getVerifier() external view returns (HydraS1Verifier) {\n    return VERIFIER;\n  }\n\n  /**\n   * @dev Getter of Commitment Mapper Registry contract\n   */\n  function getCommitmentMapperRegistry() external view returns (ICommitmentMapperRegistry) {\n    return COMMITMENT_MAPPER_REGISTRY;\n  }\n\n  /**\n   * @dev Getter of Roots Registry Contract\n   */\n  function getAvailableRootsRegistry() external view returns (IAvailableRootsRegistry) {\n    return AVAILABLE_ROOTS_REGISTRY;\n  }\n\n  /*******************************************************\n    Hydra-S1 SPECIFIC FUNCTIONS\n  *******************************************************/\n\n  /**\n   * @dev MANDATORY: must be implemented to return the nullifier from an attestation extraData\n   * @dev Getter of a nullifier encoded in extraData\n   * @notice Must be implemented by the inheriting contracts\n   * @param extraData extraData where nullifier can be encoded\n   */\n  function getNullifierFromExtraData(\n    bytes memory extraData\n  ) external view virtual returns (uint256);\n\n  /**\n   * @dev MANDATORY: must be implemented to return the external nullifier from a user request\n   * so it can be checked against snark input\n   * nullifier = hash(sourceSecretHash, externalNullifier), which is verified inside the snark\n   * users bring sourceSecretHash as private input which guarantees privacy\n   *\n   * This function MUST be implemented by Hydra-S1 attesters.\n   * This is the core function that implements the logic of external nullifiers\n   *\n   * Do they get one external nullifier per claim?\n   * Do they get 2 external nullifiers per claim?\n   * Do they get 1 external nullifier per claim, every month?\n   * Take a look at Hydra-S1 Simple Attester for an example\n   * @param claim user claim: part of a group of accounts, with a claimedValue for their account\n   */\n  function _getExternalNullifierOfClaim(\n    HydraS1Claim memory claim\n  ) internal view virtual returns (uint256);\n\n  /**\n   * @dev Checks whether the user claim and the snark public input are a match\n   * @param claim user claim\n   * @param input snark public input\n   */\n  function _validateInput(\n    HydraS1Claim memory claim,\n    HydraS1ProofInput memory input\n  ) internal view virtual {\n    if (input.accountsTreeValue != claim.groupId) {\n      revert AccountsTreeValueMismatch(claim.groupId, input.accountsTreeValue);\n    }\n\n    if (input.isStrict == claim.groupProperties.isScore) {\n      revert IsStrictMismatch(claim.groupProperties.isScore, input.isStrict);\n    }\n\n    if (input.destination != claim.destination) {\n      revert DestinationMismatch(claim.destination, input.destination);\n    }\n\n    if (input.chainId != block.chainid) revert ChainIdMismatch(block.chainid, input.chainId);\n\n    if (input.value != claim.claimedValue) revert ValueMismatch(claim.claimedValue, input.value);\n\n    if (!AVAILABLE_ROOTS_REGISTRY.isRootAvailableForMe(input.registryRoot)) {\n      revert RegistryRootMismatch(input.registryRoot);\n    }\n\n    uint256[2] memory commitmentMapperPubKey = COMMITMENT_MAPPER_REGISTRY.getEdDSAPubKey();\n    if (\n      input.commitmentMapperPubKey[0] != commitmentMapperPubKey[0] ||\n      input.commitmentMapperPubKey[1] != commitmentMapperPubKey[1]\n    ) {\n      revert CommitmentMapperPubKeyMismatch(\n        commitmentMapperPubKey[0],\n        commitmentMapperPubKey[1],\n        input.commitmentMapperPubKey[0],\n        input.commitmentMapperPubKey[1]\n      );\n    }\n\n    uint256 externalNullifier = _getExternalNullifierOfClaim(claim);\n\n    if (input.externalNullifier != externalNullifier) {\n      revert ExternalNullifierMismatch(externalNullifier, input.externalNullifier);\n    }\n  }\n\n  /**\n   * @dev verify the groth16 mathematical proof\n   * @param proofData snark public input\n   */\n  function _verifyProof(HydraS1ProofData memory proofData) internal view virtual {\n    try\n      VERIFIER.verifyProof(proofData.proof.a, proofData.proof.b, proofData.proof.c, proofData.input)\n    returns (bool success) {\n      if (!success) revert InvalidGroth16Proof('');\n    } catch Error(string memory reason) {\n      revert InvalidGroth16Proof(reason);\n    } catch Panic(uint256 /*errorCode*/) {\n      revert InvalidGroth16Proof('');\n    } catch (bytes memory /*lowLevelData*/) {\n      revert InvalidGroth16Proof('');\n    }\n  }\n}\n"
  },
  {
    "path": "contracts/attesters/hydra-s1/base/IHydraS1Base.sol",
    "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.14;\npragma experimental ABIEncoderV2;\n\nimport {IAttester} from '../../../core/interfaces/IAttester.sol';\nimport {HydraS1Verifier, HydraS1Lib, HydraS1ProofData} from '../libs/HydraS1Lib.sol';\nimport {ICommitmentMapperRegistry} from '../../../periphery/utils/CommitmentMapperRegistry.sol';\nimport {IAvailableRootsRegistry} from '../../../periphery/utils/AvailableRootsRegistry.sol';\n\n/**\n * @title Hydra-S1 Base Interface\n * @author Sismo\n * @notice Interface that facilitates the use of the Hydra-S1 ZK Proving Scheme.\n * Hydra-S1 is single source, single group: it allows users to verify they are part of one and only one group at a time\n * It is inherited by the family of Hydra-S1 attesters.\n * It contains the errors and method specific of the Hydra-S1 attesters family and the Hydra-S1 ZK Proving Scheme\n * We invite readers to refer to the following:\n *    - https://hydra-s1.docs.sismo.io for a full guide through the Hydra-S1 ZK Attestations\n *    - https://hydra-s1-circuits.docs.sismo.io for circuits, prover and verifiers of Hydra-S1\n **/\ninterface IHydraS1Base is IAttester {\n  error ClaimsLengthDifferentThanOne(uint256 claimLength);\n  error RegistryRootMismatch(uint256 inputRoot);\n  error DestinationMismatch(address expectedDestination, address inputDestination);\n  error CommitmentMapperPubKeyMismatch(\n    uint256 expectedX,\n    uint256 expectedY,\n    uint256 inputX,\n    uint256 inputY\n  );\n  error ExternalNullifierMismatch(uint256 expectedExternalNullifier, uint256 externalNullifier);\n  error IsStrictMismatch(bool expectedStrictness, bool strictNess);\n  error ChainIdMismatch(uint256 expectedChainId, uint256 chainId);\n  error ValueMismatch(uint256 expectedValue, uint256 inputValue);\n  error AccountsTreeValueMismatch(\n    uint256 expectedAccountsTreeValue,\n    uint256 inputAccountsTreeValue\n  );\n  error InvalidGroth16Proof(string reason);\n\n  function getNullifierFromExtraData(bytes memory extraData) external view returns (uint256);\n\n  /**\n   * @dev Getter of Hydra-S1 Verifier contract\n   */\n  function getVerifier() external view returns (HydraS1Verifier);\n\n  /**\n   * @dev Getter of Commitment Mapper Registry contract\n   */\n  function getCommitmentMapperRegistry() external view returns (ICommitmentMapperRegistry);\n\n  /**\n   * @dev Getter of Roots Registry Contract\n   */\n  function getAvailableRootsRegistry() external view returns (IAvailableRootsRegistry);\n}\n"
  },
  {
    "path": "contracts/attesters/hydra-s1/interfaces/IHydraS1AccountboundAttester.sol",
    "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.14;\npragma experimental ABIEncoderV2;\n\nimport {IHydraS1SimpleAttester} from '././IHydraS1SimpleAttester.sol';\n\n/**\n * @title Hydra-S1 Accountbound Interface\n * @author Sismo\n * @notice Interface of the HydraS1AccountboundAttester contract which inherits from the errors, events and methods specific to the HydraS1SimpleAttester interface.\n **/\ninterface IHydraS1AccountboundAttester is IHydraS1SimpleAttester {\n  /**\n   * @dev Event emitted when the duration of the cooldown duration for a group index (internal collection id) has been set\n   * @param groupIndex internal collection id\n   * @param cooldownDuration the duration of the cooldown period\n   **/\n  event CooldownDurationSetForGroupIndex(uint256 indexed groupIndex, uint32 cooldownDuration);\n\n  /**\n   * @dev Event emitted when the nullifier has been set on cooldown. This happens when the\n   * attestation destination of a nullifier has been changed\n   * @param nullifier user nullifier\n   * @param burnCount the number of times the attestation destination of a nullifier has been changed\n   **/\n  event NullifierSetOnCooldown(uint256 indexed nullifier, uint16 burnCount);\n\n  /**\n   * @dev Error when the nullifier is on cooldown. The user have to wait the cooldownDuration\n   * before being able to change again the destination address.\n   **/\n  error NullifierOnCooldown(\n    uint256 nullifier,\n    address destination,\n    uint16 burnCount,\n    uint32 cooldownStart\n  );\n\n  /**\n   * @dev Error when the cooldown duration for a given groupIndex is equal to zero.\n   * The HydraS1AccountboundAttester behaves like the HydraS1SimpleAttester.\n   **/\n  error CooldownDurationNotSetForGroupIndex(uint256 groupIndex);\n\n  /**\n   * @dev Initializes the contract, to be called by the proxy delegating calls to this implementation\n   * @param owner Owner of the contract, can update public key and address\n   */\n  function initialize(address owner) external;\n\n  /**\n   * @dev returns the nullifier for a given extraData\n   * @param extraData bytes where the nullifier is encoded\n   */\n  function getNullifierFromExtraData(bytes memory extraData) external pure returns (uint256);\n\n  /**\n   * @dev Returns the burn count for a given extraData\n   * @param extraData bytes where the burnCount is encoded\n   */\n  function getBurnCountFromExtraData(bytes memory extraData) external pure returns (uint16);\n\n  /**\n   * @dev Getter, returns the cooldown start of a nullifier\n   * @param nullifier nullifier used\n   **/\n  function getNullifierCooldownStart(uint256 nullifier) external view returns (uint32);\n\n  /**\n   * @dev Getter, returns the burnCount of a nullifier\n   * @param nullifier nullifier used\n   **/\n  function getNullifierBurnCount(uint256 nullifier) external view returns (uint16);\n\n  /**\n   * @dev Setter, sets the cooldown duration of a groupIndex\n   * @param groupIndex internal collection id\n   * @param cooldownDuration cooldown duration we want to set for the groupIndex\n   **/\n  function setCooldownDurationForGroupIndex(uint256 groupIndex, uint32 cooldownDuration) external;\n\n  /*/**\n   * @dev Getter, get the cooldown duration of a groupIndex\n   * @notice returns 0 when the accountbound feature is deactivated for this group\n   * @param groupIndex internal collection id\n   **/\n  function getCooldownDurationForGroupIndex(uint256 groupIndex) external view returns (uint32);\n}\n"
  },
  {
    "path": "contracts/attesters/hydra-s1/interfaces/IHydraS1SimpleAttester.sol",
    "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.14;\npragma experimental ABIEncoderV2;\n\nimport {Attestation} from '../../../core/libs/Structs.sol';\nimport {CommitmentMapperRegistry} from '../../../periphery/utils/CommitmentMapperRegistry.sol';\nimport {AvailableRootsRegistry} from '../../../periphery/utils/AvailableRootsRegistry.sol';\nimport {HydraS1Lib, HydraS1ProofData, HydraS1ProofInput} from './../libs/HydraS1Lib.sol';\nimport {IHydraS1Base} from './../base/IHydraS1Base.sol';\n\n/**\n * @title Hydra-S1 Accountbound Interface\n * @author Sismo\n * @notice Interface with errors, events and methods specific to the HydraS1SimpleAttester.\n **/\ninterface IHydraS1SimpleAttester is IHydraS1Base {\n  /**\n   * @dev Error when the nullifier is already used for a destination address\n   **/\n  error NullifierUsed(uint256 nullifier);\n\n  /**\n   * @dev Error when the collectionId of an attestation overflow the AUTHORIZED_COLLECTION_ID_LAST\n   **/\n  error CollectionIdOutOfBound(uint256 collectionId);\n\n  /**\n   * @dev Event emitted when the nullifier is associated to a destination address.\n   **/\n  event NullifierDestinationUpdated(uint256 nullifier, address newOwner);\n\n  /**\n   * @dev Getter, returns the last attestation destination of a nullifier\n   * @param nullifier nullifier used\n   **/\n  function getDestinationOfNullifier(uint256 nullifier) external view returns (address);\n\n  /**\n   * @dev Getter\n   * returns of the first collection in which the attester is supposed to record\n   **/\n  function AUTHORIZED_COLLECTION_ID_FIRST() external view returns (uint256);\n\n  /**\n   * @dev Getter\n   * returns of the last collection in which the attester is supposed to record\n   **/\n  function AUTHORIZED_COLLECTION_ID_LAST() external view returns (uint256);\n}\n"
  },
  {
    "path": "contracts/attesters/hydra-s1/libs/HydraS1AccountboundLib.sol",
    "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.14;\n\nimport {Claim, Request} from '../../../core/libs/Structs.sol';\nimport {HydraS1Lib, HydraS1Claim, HydraS1GroupProperties} from './HydraS1Lib.sol';\n\n// user Hydra-S1 claim retrieved form his request\nstruct HydraS1AccountboundClaim {\n  uint256 groupId; // user claims to have an account in this group\n  uint256 claimedValue; // user claims this value for its account in the group\n  address destination; // user claims to own this destination[]\n  HydraS1AccountboundGroupProperties groupProperties; // user claims the group has the following properties\n}\n\nstruct HydraS1AccountboundGroupProperties {\n  uint128 groupIndex;\n  uint32 generationTimestamp;\n  uint32 cooldownDuration;\n  bool isScore;\n}\n\nlibrary HydraS1AccountboundLib {\n  error GroupIdAndPropertiesMismatch(uint256 expectedGroupId, uint256 groupId);\n\n  function _hydraS1claim(Request memory self) internal pure returns (HydraS1Claim memory) {\n    Claim memory claim = self.claims[0];\n    _validateClaim(claim);\n\n    HydraS1AccountboundGroupProperties memory groupProperties = abi.decode(\n      claim.extraData,\n      (HydraS1AccountboundGroupProperties)\n    );\n\n    HydraS1GroupProperties memory hydraS1GroupProperties = HydraS1GroupProperties(\n      groupProperties.groupIndex,\n      groupProperties.generationTimestamp,\n      groupProperties.isScore\n    );\n\n    return (\n      HydraS1Claim(claim.groupId, claim.claimedValue, self.destination, hydraS1GroupProperties)\n    );\n  }\n\n  function _hydraS1Accountboundclaim(\n    Request memory self\n  ) internal pure returns (HydraS1AccountboundClaim memory) {\n    Claim memory claim = self.claims[0];\n    _validateClaim(claim);\n\n    HydraS1AccountboundGroupProperties memory hydraS1AccountboundGroupProperties = abi.decode(\n      claim.extraData,\n      (HydraS1AccountboundGroupProperties)\n    );\n\n    return (\n      HydraS1AccountboundClaim(\n        claim.groupId,\n        claim.claimedValue,\n        self.destination,\n        hydraS1AccountboundGroupProperties\n      )\n    );\n  }\n\n  function _generateGroupIdFromEncodedProperties(\n    bytes memory encodedProperties\n  ) internal pure returns (uint256) {\n    return uint256(keccak256(encodedProperties)) % HydraS1Lib.SNARK_FIELD;\n  }\n\n  function _validateClaim(Claim memory claim) internal pure {\n    uint256 expectedGroupId = _generateGroupIdFromEncodedProperties(claim.extraData);\n    if (claim.groupId != expectedGroupId)\n      revert GroupIdAndPropertiesMismatch(expectedGroupId, claim.groupId);\n  }\n}\n"
  },
  {
    "path": "contracts/attesters/hydra-s1/libs/HydraS1Lib.sol",
    "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.14;\n\nimport {Claim, Request} from '../../../core/libs/Structs.sol';\nimport {HydraS1Verifier} from '@sismo-core/hydra-s1/contracts/HydraS1Verifier.sol';\n\n// user Hydra-S1 claim retrieved form his request\nstruct HydraS1Claim {\n  uint256 groupId; // user claims to have an account in this group\n  uint256 claimedValue; // user claims this value for its account in the group\n  address destination; // user claims to own this destination[]\n  HydraS1GroupProperties groupProperties; // user claims the group has the following properties\n}\n\nstruct HydraS1GroupProperties {\n  uint128 groupIndex;\n  uint32 generationTimestamp;\n  bool isScore;\n}\n\nstruct HydraS1CircomSnarkProof {\n  uint256[2] a;\n  uint256[2][2] b;\n  uint256[2] c;\n}\n\nstruct HydraS1ProofData {\n  HydraS1CircomSnarkProof proof;\n  uint256[10] input;\n  // destination\n  // chainId\n  // commitmentMapperPubKey.x\n  // commitmentMapperPubKey.y\n  // registryTreeRoot\n  // externalNullifier\n  // nullifier\n  // claimedValue\n  // accountsTreeValue\n  // isStrict\n}\n\nstruct HydraS1ProofInput {\n  address destination;\n  uint256 chainId;\n  uint256 registryRoot;\n  uint256 externalNullifier;\n  uint256 nullifier;\n  uint256 value;\n  uint256 accountsTreeValue;\n  bool isStrict;\n  uint256[2] commitmentMapperPubKey;\n}\n\nlibrary HydraS1Lib {\n  uint256 constant SNARK_FIELD =\n    21888242871839275222246405745257275088548364400416034343698204186575808495617;\n\n  error GroupIdAndPropertiesMismatch(uint256 expectedGroupId, uint256 groupId);\n\n  function _input(HydraS1ProofData memory self) internal pure returns (HydraS1ProofInput memory) {\n    return\n      HydraS1ProofInput(\n        _getDestination(self),\n        _getChainId(self),\n        _getRegistryRoot(self),\n        _getExpectedExternalNullifier(self),\n        _getNullifier(self),\n        _getValue(self),\n        _getAccountsTreeValue(self),\n        _getIsStrict(self),\n        _getCommitmentMapperPubKey(self)\n      );\n  }\n\n  function _claim(Request memory self) internal pure returns (HydraS1Claim memory) {\n    Claim memory claim = self.claims[0];\n    _validateClaim(claim);\n\n    HydraS1GroupProperties memory groupProperties = abi.decode(\n      claim.extraData,\n      (HydraS1GroupProperties)\n    );\n\n    return (HydraS1Claim(claim.groupId, claim.claimedValue, self.destination, groupProperties));\n  }\n\n  function _toCircomFormat(\n    HydraS1ProofData memory self\n  )\n    internal\n    pure\n    returns (uint256[2] memory, uint256[2][2] memory, uint256[2] memory, uint256[10] memory)\n  {\n    return (self.proof.a, self.proof.b, self.proof.c, self.input);\n  }\n\n  function _getDestination(HydraS1ProofData memory self) internal pure returns (address) {\n    return address(uint160(self.input[0]));\n  }\n\n  function _getChainId(HydraS1ProofData memory self) internal pure returns (uint256) {\n    return self.input[1];\n  }\n\n  function _getCommitmentMapperPubKey(\n    HydraS1ProofData memory self\n  ) internal pure returns (uint256[2] memory) {\n    return [self.input[2], self.input[3]];\n  }\n\n  function _getRegistryRoot(HydraS1ProofData memory self) internal pure returns (uint256) {\n    return self.input[4];\n  }\n\n  function _getExpectedExternalNullifier(\n    HydraS1ProofData memory self\n  ) internal pure returns (uint256) {\n    return self.input[5];\n  }\n\n  function _getNullifier(HydraS1ProofData memory self) internal pure returns (uint256) {\n    return self.input[6];\n  }\n\n  function _getValue(HydraS1ProofData memory self) internal pure returns (uint256) {\n    return self.input[7];\n  }\n\n  function _getAccountsTreeValue(HydraS1ProofData memory self) internal pure returns (uint256) {\n    return self.input[8];\n  }\n\n  function _getIsStrict(HydraS1ProofData memory self) internal pure returns (bool) {\n    return self.input[9] == 1;\n  }\n\n  function _getNullifier(bytes calldata self) internal pure returns (uint256) {\n    HydraS1ProofData memory snarkProofData = abi.decode(self, (HydraS1ProofData));\n    uint256 nullifier = uint256(_getNullifier(snarkProofData));\n    return nullifier;\n  }\n\n  function _generateGroupIdFromProperties(\n    uint128 groupIndex,\n    uint32 generationTimestamp,\n    bool isScore\n  ) internal pure returns (uint256) {\n    return\n      _generateGroupIdFromEncodedProperties(\n        _encodeGroupProperties(groupIndex, generationTimestamp, isScore)\n      );\n  }\n\n  function _generateGroupIdFromEncodedProperties(\n    bytes memory encodedProperties\n  ) internal pure returns (uint256) {\n    return uint256(keccak256(encodedProperties)) % HydraS1Lib.SNARK_FIELD;\n  }\n\n  function _encodeGroupProperties(\n    uint128 groupIndex,\n    uint32 generationTimestamp,\n    bool isScore\n  ) internal pure returns (bytes memory) {\n    return abi.encode(groupIndex, generationTimestamp, isScore);\n  }\n\n  function _validateClaim(Claim memory claim) internal pure {\n    uint256 expectedGroupId = _generateGroupIdFromEncodedProperties(claim.extraData);\n    if (claim.groupId != expectedGroupId)\n      revert GroupIdAndPropertiesMismatch(expectedGroupId, claim.groupId);\n  }\n}\n"
  },
  {
    "path": "contracts/attesters/pythia-1/Pythia1SimpleAttester.sol",
    "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.14;\npragma experimental ABIEncoderV2;\n\nimport {Ownable} from '@openzeppelin/contracts/access/Ownable.sol';\nimport {IPythia1SimpleAttester} from './interfaces/IPythia1SimpleAttester.sol';\n\n// Core protocol Protocol imports\nimport {Request, Attestation, Claim} from './../../core/libs/Structs.sol';\nimport {Attester, IAttester, IAttestationsRegistry} from './../../core/Attester.sol';\n\n// Imports related to Pythia1 Proving Scheme\nimport {Pythia1Base, Pythia1Lib, Pythia1ProofData, Pythia1ProofInput, Pythia1Claim} from './base/Pythia1Base.sol';\n\n/**\n * @title  Pythia-1 Simple Attester\n * @author Sismo\n * @notice This attester is part of the family of the Pythia-1 Attesters.\n * Pythia-1 attesters enable users to prove they have a claim and its proof issued by an \n * offchain service in a privacy preserving way. \n * That means no-one can make the link between the account used in the offchain service\n * and the onchain account where the attestation is stored.\n * The Pythia-1 Base abstract contract is inherited and holds the complex Pythia 1 verification logic.\n * We invite readers to refer to:\n *    - https://pythia-1.docs.sismo.io for a full guide through the Pythia-1 ZK Attestations\n *    - https://pythia-1-circuits.docs.sismo.io for circuits, prover and verifiers of Pythia-1\n\n * This specific attester has the following characteristics:\n\n * - Zero Knowledge\n *   One cannot deduct from an attestation what offchain issuer's account was used to generate the underlying proof\n\n * - Non Strict (scores)\n *   If a user can generate an attestation of max value 100, they can also generate any attestation with value < 100.\n *   This attester generate attestations of scores\n\n * - Ticketed\n *   Each users gets one userTicket per claim\n *   For people used to semaphore/ tornado cash people:\n *   userTicket = hash(secret, ticketIdentifier) <=> nullifierHash = hash(IdNullifier, externalNullifier)\n **/\n\ncontract Pythia1SimpleAttester is IPythia1SimpleAttester, Pythia1Base, Attester, Ownable {\n  using Pythia1Lib for Pythia1ProofData;\n  using Pythia1Lib for bytes;\n  using Pythia1Lib for Request;\n\n  uint8 public constant IMPLEMENTATION_VERSION = 4;\n\n  // The deployed contract will need to be authorized to write into the Attestation registry\n  // It should get write access on attestation collections from AUTHORIZED_COLLECTION_ID_FIRST to AUTHORIZED_COLLECTION_ID_LAST.\n  uint256 public immutable AUTHORIZED_COLLECTION_ID_FIRST;\n  uint256 public immutable AUTHORIZED_COLLECTION_ID_LAST;\n\n  uint256[2] internal _commitmentSignerPubKey;\n  mapping(uint256 => address) internal _ticketsDestinations;\n\n  /*******************************************************\n    INITIALIZATION FUNCTIONS                           \n  *******************************************************/\n  /**\n   * @dev Constructor. Initializes the contract\n   * @param attestationsRegistryAddress Attestations Registry contract on which the attester will write attestations\n   * @param collectionIdFirst Id of the first collection in which the attester is supposed to record\n   * @param collectionIdLast Id of the last collection in which the attester is supposed to record\n   * @param pythia1VerifierAddress ZK Snark Pythia-1 Verifier contract\n   * @param commitmentSignerPubKey The EdDSA public key of the commitment signer for the Pythia 1 Proving Scheme\n   * @param owner The owner of the contract that can update the commitment signer pub key\n   */\n  constructor(\n    address attestationsRegistryAddress,\n    uint256 collectionIdFirst,\n    uint256 collectionIdLast,\n    address pythia1VerifierAddress,\n    uint256[2] memory commitmentSignerPubKey,\n    address owner\n  ) Attester(attestationsRegistryAddress) Pythia1Base(pythia1VerifierAddress) {\n    AUTHORIZED_COLLECTION_ID_FIRST = collectionIdFirst;\n    AUTHORIZED_COLLECTION_ID_LAST = collectionIdLast;\n    initialize(commitmentSignerPubKey, owner);\n  }\n\n  /**\n   * @dev Initializes the contract, to be called by the proxy delegating calls to this implementation\n   * @param commitmentSignerPubKey EdDSA public key of the commitment signer\n   * @param ownerAddress Owner of the contract, can update public key and address\n   * @notice The reinitializer modifier is needed to configure modules that are added through upgrades and that require initialization.\n   */\n  function initialize(\n    uint256[2] memory commitmentSignerPubKey,\n    address ownerAddress\n  ) public reinitializer(IMPLEMENTATION_VERSION) {\n    // if proxy did not setup owner yet or if called by constructor (for implem setup)\n    if (owner() == address(0) || address(this).code.length == 0) {\n      _transferOwnership(ownerAddress);\n      _updateCommitmentSignerPubKey(commitmentSignerPubKey);\n    }\n  }\n\n  /*******************************************************\n    MANDATORY FUNCTIONS TO OVERRIDE FROM ATTESTER.SOL\n  *******************************************************/\n  /**\n   * @dev Throws if user request is invalid when verified against\n   * Look into Pythia1Base for more details\n   * @param request users request. Claim of having an account part of a group of accounts\n   * @param proofData provided to back the request. snark input and snark proof\n   */\n  function _verifyRequest(\n    Request calldata request,\n    bytes calldata proofData\n  ) internal virtual override {\n    Pythia1ProofData memory snarkProof = abi.decode(proofData, (Pythia1ProofData));\n    Pythia1ProofInput memory snarkInput = snarkProof._input();\n    Pythia1Claim memory claim = request._claim();\n\n    // verifies that the proof corresponds to the claim\n    _validateInput(claim, snarkInput);\n    // verifies the proof validity\n    _verifyProof(snarkProof);\n  }\n\n  /**\n   * @dev Returns attestations that will be recorded, constructed from the user request\n   * @param request users request. Claim of having an account part of a group of accounts\n   */\n  function buildAttestations(\n    Request calldata request,\n    bytes calldata\n  ) public view virtual override(IAttester, Attester) returns (Attestation[] memory) {\n    Pythia1Claim memory claim = request._claim();\n\n    Attestation[] memory attestations = new Attestation[](1);\n\n    uint256 attestationCollectionId = AUTHORIZED_COLLECTION_ID_FIRST +\n      claim.groupProperties.internalCollectionId;\n\n    if (attestationCollectionId > AUTHORIZED_COLLECTION_ID_LAST)\n      revert CollectionIdOutOfBound(attestationCollectionId);\n\n    address issuer = address(this);\n\n    attestations[0] = Attestation(\n      attestationCollectionId,\n      claim.destination,\n      issuer,\n      claim.claimedValue,\n      uint32(block.timestamp),\n      ''\n    );\n    return (attestations);\n  }\n\n  /*******************************************************\n    OPTIONAL HOOK VIRTUAL FUNCTIONS FROM ATTESTER.SOL\n  *******************************************************/\n\n  /**\n   * @dev Hook run before recording the attestation.\n   * Throws if ticket already used\n   * @param request users request. Claim of beiing part of a group.\n   * @param proofData provided to back the request. snark input and snark proof\n   */\n  function _beforeRecordAttestations(\n    Request calldata request,\n    bytes calldata proofData\n  ) internal virtual override {\n    // we get the ticket used from the snark input in the data provided\n    uint256 userTicket = proofData._getTicket();\n    address currentDestination = _getDestinationOfTicket(userTicket);\n\n    if (currentDestination != address(0)) {\n      revert TicketUsed(userTicket);\n    }\n\n    _setDestinationForTicket(userTicket, request.destination);\n  }\n\n  /*******************************************************\n    Pythia-1 MANDATORY FUNCTIONS FROM Pythia-1 Base Attester\n  *******************************************************/\n\n  /**\n   * @dev Returns the ticket identifier from a user claim\n   * @param claim user Pythia-1 claim = have an offchain account with a specific value in a specific group\n   * ticket = hash(secretHash, ticketIdentifier), which is verified inside the snark\n   * users bring secretHash as private input in snark which guarantees privacy\n   * the secretHash is only known by the user and never escape the user's browser\n   \n   * Here we chose ticketIdentifier = hash(attesterAddress, claim.GroupId)\n   * Creates one ticket per group, per user and makes sure no collision with other attester's tickets\n  **/\n  function _getTicketIdentifierOfClaim(\n    Pythia1Claim memory claim\n  ) internal view override returns (uint256) {\n    uint256 ticketIdentifier = _encodeInSnarkField(\n      address(this),\n      claim.groupProperties.internalCollectionId\n    );\n    return ticketIdentifier;\n  }\n\n  function _getCommitmentSignerPubKey() internal view override returns (uint256[2] memory) {\n    return _commitmentSignerPubKey;\n  }\n\n  /*******************************************************\n    Pythia-1 Attester Specific Functions\n  *******************************************************/\n\n  function updateCommitmentSignerPubKey(\n    uint256[2] memory commitmentSignerPubKey\n  ) external onlyOwner {\n    _updateCommitmentSignerPubKey(commitmentSignerPubKey);\n  }\n\n  function _updateCommitmentSignerPubKey(uint256[2] memory commitmentSignerPubKey) internal {\n    _commitmentSignerPubKey = commitmentSignerPubKey;\n    emit CommitmentSignerPubKeyUpdated(commitmentSignerPubKey);\n  }\n\n  /**\n   * @dev Getter, returns the last attestation destination of a ticket\n   * @param userTicket ticket used\n   **/\n  function getDestinationOfTicket(uint256 userTicket) external view override returns (address) {\n    return _getDestinationOfTicket(userTicket);\n  }\n\n  function _setDestinationForTicket(uint256 userTicket, address destination) internal virtual {\n    _ticketsDestinations[userTicket] = destination;\n    emit TicketDestinationUpdated(userTicket, destination);\n  }\n\n  function _getDestinationOfTicket(uint256 userTicket) internal view returns (address) {\n    return _ticketsDestinations[userTicket];\n  }\n\n  function _encodeInSnarkField(address addr, uint256 nb) internal pure returns (uint256) {\n    return uint256(keccak256(abi.encode(addr, nb))) % Pythia1Lib.SNARK_FIELD;\n  }\n}\n"
  },
  {
    "path": "contracts/attesters/pythia-1/base/IPythia1Base.sol",
    "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.14;\npragma experimental ABIEncoderV2;\n\nimport {Pythia1Verifier, Pythia1Lib, Pythia1ProofData} from '../libs/Pythia1Lib.sol';\n\ninterface IPythia1Base {\n  error DestinationMismatch(address expectedDestination, address inputDestination);\n  error UserShouldOwnItsDestination(address sender, address inputdestination);\n  error CommitmentSignerPubKeyMismatch(\n    uint256 expectedX,\n    uint256 expectedY,\n    uint256 inputX,\n    uint256 inputY\n  );\n  error TicketIdentifierMismatch(uint256 expectedTicketIdentifier, uint256 ticketIdentifier);\n  error IsStrictMismatch(bool expectedStrictness, bool strictNess);\n  error ChainIdMismatch(uint256 expectedChainId, uint256 chainId);\n  error ValueMismatch(uint256 expectedValue, uint256 inputValue);\n  error GroupIdMismatch(uint256 expectedAccountsTreeValue, uint256 inputAccountsTreeValue);\n  error InvalidGroth16Proof(string reason);\n\n  /**\n   * @dev Getter of Pythia-1 Verifier contract\n   */\n  function getVerifier() external view returns (Pythia1Verifier);\n\n  /**\n   * @dev Getter of the Commitment Signer EdDSA Public Key\n   */\n  function getCommitmentSignerPubKey() external view returns (uint256[2] memory);\n}\n"
  },
  {
    "path": "contracts/attesters/pythia-1/base/Pythia1Base.sol",
    "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.14;\npragma experimental ABIEncoderV2;\n\nimport {IPythia1Base} from './IPythia1Base.sol';\nimport {Initializable} from '@openzeppelin/contracts/proxy/utils/Initializable.sol';\n\n// Protocol imports\nimport {Request, Attestation, Claim} from '../../../core/libs/Structs.sol';\n\n// Imports related to Pythia 1 ZK Proving Scheme\nimport {Pythia1Verifier, Pythia1Lib, Pythia1Claim, Pythia1ProofData, Pythia1ProofInput, Pythia1GroupProperties} from '../libs/Pythia1Lib.sol';\n\n/**\n * @title Pythia-1 Base Attester\n * @author Sismo\n * @notice Abstract contract that facilitates the use of the Pythia-1 ZK Proving Scheme.\n * Pythia-1: it allows issuing attestations from an offchain service and send it onchain \n * without anyone being able to make the link between the offchain service and the onchain service.\n * It is inherited by the family of Pythia-1 attesters.\n * It contains the user input checking and the ZK-SNARK proof verification.\n * We invite readers to refer to:\n *    - https://pythia-1.docs.sismo.io for a full guide through the Pythia-1 ZK Attestations\n *    - https://pythia-1-circuits.docs.sismo.io for circuits, prover and verifiers of Pythia-1\n \n \n **/\nabstract contract Pythia1Base is IPythia1Base, Initializable {\n  using Pythia1Lib for Pythia1ProofData;\n\n  // ZK-SNARK Verifier\n  Pythia1Verifier immutable VERIFIER;\n\n  /*******************************************************\n    INITIALIZATION FUNCTIONS                           \n  *******************************************************/\n  /**\n   * @dev Constructor. Initializes the contract\n   * @param Pythia1VerifierAddress ZK Snark Verifier contract\n   */\n  constructor(address Pythia1VerifierAddress) {\n    VERIFIER = Pythia1Verifier(Pythia1VerifierAddress);\n  }\n\n  /**\n   * @dev Getter of Pythia-1 Verifier contract\n   */\n  function getVerifier() external view returns (Pythia1Verifier) {\n    return VERIFIER;\n  }\n\n  /**\n   * @dev Getter of the Commitment signer Eddsa Public key\n   */\n  function getCommitmentSignerPubKey() external view returns (uint256[2] memory) {\n    return _getCommitmentSignerPubKey();\n  }\n\n  /*******************************************************\n    Pythia-1 SPECIFIC FUNCTIONS\n  *******************************************************/\n\n  /**\n   * @dev MANDATORY: must be implemented to return the ticket identifier from a user request\n   * so it can be checked against snark input\n   * ticket = hash(secretHash, ticketIdentifier), which is verified inside the snark\n   * the secretHash is a number only known by the user and is used in \n   * the zero knowledge as a private input which guarantees privacy\n\n   * This function MUST be implemented by Pythia-1 attesters.\n   * This is the core function that implements the logic of tickets\n\n   * Do they get one ticket per claim?\n   * Do they get 2 tickets per claim?\n   * Do they get 1 ticket per claim, every month?\n   * Take a look at Pythia-1 Simple Attester for an example\n   * @param claim user claim: a particular claim that a user have that he can prove s right.\n   */\n  function _getTicketIdentifierOfClaim(\n    Pythia1Claim memory claim\n  ) internal view virtual returns (uint256);\n\n  /**\n   * @dev MANDATORY: must be implemented to return the commitment signer that allows to\n   * prove the claim was correctly issued for the user.\n   */\n  function _getCommitmentSignerPubKey() internal view virtual returns (uint256[2] memory);\n\n  /**\n   * @dev Checks whether the user claim and the snark public input are a match\n   * @param claim user claim\n   * @param input snark public input\n   */\n  function _validateInput(\n    Pythia1Claim memory claim,\n    Pythia1ProofInput memory input\n  ) internal view virtual {\n    if (input.groupId != claim.groupId) revert GroupIdMismatch(claim.groupId, input.groupId);\n\n    if (input.isStrict == claim.groupProperties.isScore)\n      revert IsStrictMismatch(claim.groupProperties.isScore, input.isStrict);\n\n    if (input.destination != claim.destination)\n      revert DestinationMismatch(claim.destination, input.destination);\n\n    if (claim.destination != msg.sender)\n      revert UserShouldOwnItsDestination(msg.sender, claim.destination);\n\n    if (input.chainId != block.chainid) revert ChainIdMismatch(block.chainid, input.chainId);\n\n    if (input.value != claim.claimedValue) revert ValueMismatch(claim.claimedValue, input.value);\n\n    uint256[2] memory commitmentSignerPubKey = _getCommitmentSignerPubKey();\n    if (\n      input.commitmentSignerPubKey[0] != commitmentSignerPubKey[0] ||\n      input.commitmentSignerPubKey[1] != commitmentSignerPubKey[1]\n    )\n      revert CommitmentSignerPubKeyMismatch(\n        commitmentSignerPubKey[0],\n        commitmentSignerPubKey[1],\n        input.commitmentSignerPubKey[0],\n        input.commitmentSignerPubKey[1]\n      );\n\n    uint256 ticketIdentifier = _getTicketIdentifierOfClaim(claim);\n\n    if (input.ticketIdentifier != ticketIdentifier)\n      revert TicketIdentifierMismatch(ticketIdentifier, input.ticketIdentifier);\n  }\n\n  /**\n   * @dev verify the plonk mathematical proof using the circom verifier contract\n   * @param proofData snark public input\n   */\n  function _verifyProof(Pythia1ProofData memory proofData) internal view virtual {\n    try\n      VERIFIER.verifyProof(proofData.proof.a, proofData.proof.b, proofData.proof.c, proofData.input)\n    returns (bool success) {\n      if (!success) revert InvalidGroth16Proof('');\n    } catch Error(string memory reason) {\n      revert InvalidGroth16Proof(reason);\n    } catch Panic(uint256 /*errorCode*/) {\n      revert InvalidGroth16Proof('');\n    } catch (bytes memory /*lowLevelData*/) {\n      revert InvalidGroth16Proof('');\n    }\n  }\n}\n"
  },
  {
    "path": "contracts/attesters/pythia-1/interfaces/IPythia1SimpleAttester.sol",
    "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.14;\npragma experimental ABIEncoderV2;\n\nimport {Attestation} from '../../../core/libs/Structs.sol';\nimport {IAttester} from '../../../core/interfaces/IAttester.sol';\nimport {Pythia1Lib, Pythia1ProofData, Pythia1ProofInput} from './../libs/Pythia1Lib.sol';\nimport {IPythia1Base} from './../base/IPythia1Base.sol';\n\ninterface IPythia1SimpleAttester is IPythia1Base, IAttester {\n  error TicketUsed(uint256 userTicket);\n  error CollectionIdOutOfBound(uint256 collectionId);\n\n  event TicketDestinationUpdated(uint256 ticket, address newOwner);\n  event CommitmentSignerPubKeyUpdated(uint256[2] newCommitmentSignerPubKey);\n\n  /**\n   * @dev Initializes the contract, to be called by the proxy delegating calls to this implementation\n   * @param commitmentSignerPubKey EdDSA public key of the commitment signer\n   * @param owner Owner of the contract, can update public key and address\n   * @notice The reinitializer modifier is needed to configure modules that are added through upgrades and that require initialization.\n   */\n  function initialize(uint256[2] memory commitmentSignerPubKey, address owner) external;\n\n  /**\n   * @dev Getter, returns the last attestation destination of a ticket\n   * @param userTicket ticket used\n   **/\n  function getDestinationOfTicket(uint256 userTicket) external view returns (address);\n\n  /**\n   * @dev Getter\n   * returns of the first collection in which the attester is supposed to record\n   **/\n  function AUTHORIZED_COLLECTION_ID_FIRST() external view returns (uint256);\n\n  /**\n   * @dev Getter\n   * returns of the last collection in which the attester is supposed to record\n   **/\n  function AUTHORIZED_COLLECTION_ID_LAST() external view returns (uint256);\n}\n"
  },
  {
    "path": "contracts/attesters/pythia-1/libs/Pythia1Lib.sol",
    "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.14;\n\nimport {Claim, Request} from '../../../core/libs/Structs.sol';\nimport {Pythia1Verifier} from '@sismo-core/pythia-1/contracts/Pythia1Verifier.sol';\n\n// user Pythia-1 claim retrieved form his request\nstruct Pythia1Claim {\n  uint256 groupId; // user claims be part of this group\n  uint256 claimedValue; // user claims this value for its account in the group\n  address destination; // user claims to own this destination[]\n  Pythia1GroupProperties groupProperties; // user claims the group has the following properties\n}\n\nstruct Pythia1GroupProperties {\n  uint128 internalCollectionId;\n  bool isScore;\n}\n\nstruct Pythia1CircomSnarkProof {\n  uint256[2] a;\n  uint256[2][2] b;\n  uint256[2] c;\n}\n\nstruct Pythia1ProofData {\n  Pythia1CircomSnarkProof proof;\n  uint256[9] input;\n  // destination;\n  // chainId;\n  // commitmentSignerPubKey.x;\n  // commitmentSignerPubKey.y;\n  // groupId;\n  // ticketIdentifier;\n  // userTicket;\n  // value;\n  // isStrict;\n}\n\nstruct Pythia1ProofInput {\n  address destination;\n  uint256 chainId;\n  uint256 groupId;\n  uint256 ticketIdentifier;\n  uint256 ticket;\n  uint256 value;\n  bool isStrict;\n  uint256[2] commitmentSignerPubKey;\n}\n\nlibrary Pythia1Lib {\n  uint256 constant SNARK_FIELD =\n    21888242871839275222246405745257275088548364400416034343698204186575808495617;\n\n  error GroupIdAndPropertiesMismatch(uint256 expectedGroupId, uint256 groupId);\n\n  function _input(Pythia1ProofData memory self) internal pure returns (Pythia1ProofInput memory) {\n    return\n      Pythia1ProofInput(\n        _getDestination(self),\n        _getChainId(self),\n        _getGroupId(self),\n        _getExpectedExternalNullifier(self),\n        _getTicket(self),\n        _getValue(self),\n        _getIsStrict(self),\n        _getCommitmentMapperPubKey(self)\n      );\n  }\n\n  function _claim(Request memory self) internal pure returns (Pythia1Claim memory) {\n    Claim memory claim = self.claims[0];\n    _validateClaim(claim);\n    Pythia1GroupProperties memory groupProperties = abi.decode(\n      claim.extraData,\n      (Pythia1GroupProperties)\n    );\n    return (Pythia1Claim(claim.groupId, claim.claimedValue, self.destination, groupProperties));\n  }\n\n  function _toCircomFormat(\n    Pythia1ProofData memory self\n  )\n    internal\n    pure\n    returns (uint256[2] memory, uint256[2][2] memory, uint256[2] memory, uint256[9] memory)\n  {\n    return (self.proof.a, self.proof.b, self.proof.c, self.input);\n  }\n\n  function _getDestination(Pythia1ProofData memory self) internal pure returns (address) {\n    return address(uint160(self.input[0]));\n  }\n\n  function _getChainId(Pythia1ProofData memory self) internal pure returns (uint256) {\n    return self.input[1];\n  }\n\n  function _getCommitmentMapperPubKey(\n    Pythia1ProofData memory self\n  ) internal pure returns (uint256[2] memory) {\n    return [self.input[2], self.input[3]];\n  }\n\n  function _getGroupId(Pythia1ProofData memory self) internal pure returns (uint256) {\n    return self.input[4];\n  }\n\n  function _getExpectedExternalNullifier(\n    Pythia1ProofData memory self\n  ) internal pure returns (uint256) {\n    return self.input[5];\n  }\n\n  function _getTicket(Pythia1ProofData memory self) internal pure returns (uint256) {\n    return self.input[6];\n  }\n\n  function _getValue(Pythia1ProofData memory self) internal pure returns (uint256) {\n    return self.input[7];\n  }\n\n  function _getIsStrict(Pythia1ProofData memory self) internal pure returns (bool) {\n    return self.input[8] == 1;\n  }\n\n  function _getTicket(bytes calldata self) internal pure returns (uint256) {\n    Pythia1ProofData memory snarkProofData = abi.decode(self, (Pythia1ProofData));\n    uint256 userTicket = uint256(_getTicket(snarkProofData));\n    return userTicket;\n  }\n\n  function _generateGroupIdFromProperties(\n    uint128 internalCollectionId,\n    bool isScore\n  ) internal pure returns (uint256) {\n    return\n      _generateGroupIdFromEncodedProperties(_encodeGroupProperties(internalCollectionId, isScore));\n  }\n\n  function _generateGroupIdFromEncodedProperties(\n    bytes memory encodedProperties\n  ) internal pure returns (uint256) {\n    return uint256(keccak256(encodedProperties)) % Pythia1Lib.SNARK_FIELD;\n  }\n\n  function _encodeGroupProperties(\n    uint128 internalCollectionId,\n    bool isScore\n  ) internal pure returns (bytes memory) {\n    return abi.encode(internalCollectionId, isScore);\n  }\n\n  function _validateClaim(Claim memory claim) internal pure {\n    uint256 expectedGroupId = _generateGroupIdFromEncodedProperties(claim.extraData);\n    if (claim.groupId != expectedGroupId)\n      revert GroupIdAndPropertiesMismatch(expectedGroupId, claim.groupId);\n  }\n}\n"
  },
  {
    "path": "contracts/core/AttestationsRegistry.sol",
    "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.14;\n\nimport {IAttestationsRegistry} from './interfaces/IAttestationsRegistry.sol';\nimport {AttestationsRegistryConfigLogic} from './libs/attestations-registry/AttestationsRegistryConfigLogic.sol';\nimport {AttestationsRegistryState} from './libs/attestations-registry/AttestationsRegistryState.sol';\nimport {Range, RangeUtils} from './libs/utils/RangeLib.sol';\nimport {Attestation, AttestationData} from './libs/Structs.sol';\nimport {IBadges} from './interfaces/IBadges.sol';\n\n/**\n * @title Attestations Registry\n * @author Sismo\n * @notice Main contract of Sismo, stores all recorded attestations in attestations collections\n * Only authorized attestations issuers can record attestation in the registry\n * Attesters that expect to record in the Attestations Registry must be authorized issuers\n * For more information: https://attestations-registry.docs.sismo.io\n\n * For each attestation recorded, a badge is received by the user\n * The badge is the Non transferrable NFT representation of an attestation \n * Its ERC1155 contract is stateless, balances are read directly from the registry. Badge balances <=> Attestations values\n * After the creation or update of an attestation, the registry triggers a TransferSingle event from the ERC1155 Badges contracts\n * It enables off-chain apps such as opensea to catch the \"shadow mint\" of the badge\n **/\ncontract AttestationsRegistry is\n  AttestationsRegistryState,\n  IAttestationsRegistry,\n  AttestationsRegistryConfigLogic\n{\n  uint8 public constant IMPLEMENTATION_VERSION = 3;\n  IBadges immutable BADGES;\n\n  /**\n   * @dev Constructor.\n   * @param owner Owner of the contract, has the right to authorize/unauthorize attestations issuers\n   * @param badgesAddress Stateless ERC1155 Badges contract\n   */\n  constructor(address owner, address badgesAddress) {\n    BADGES = IBadges(badgesAddress);\n    initialize(owner);\n  }\n\n  /**\n   * @dev Initialize function, to be called by the proxy delegating calls to this implementation\n   * @param ownerAddress Owner of the contract, has the right to authorize/unauthorize attestations issuers\n   * @notice The reinitializer modifier is needed to configure modules that are added through upgrades and that require initialization.\n   */\n  function initialize(address ownerAddress) public reinitializer(IMPLEMENTATION_VERSION) {\n    // if proxy did not setup owner yet or if called by constructor (for implem setup)\n    if (owner() == address(0) || address(this).code.length == 0) {\n      _transferOwnership(ownerAddress);\n    }\n  }\n\n  /**\n   * @dev Main function to be called by authorized issuers\n   * @param attestations Attestations to be recorded (creates a new one or overrides an existing one)\n   */\n  function recordAttestations(Attestation[] calldata attestations) external override whenNotPaused {\n    address issuer = _msgSender();\n    for (uint256 i = 0; i < attestations.length; i++) {\n      if (!_isAuthorized(issuer, attestations[i].collectionId))\n        revert IssuerNotAuthorized(issuer, attestations[i].collectionId);\n\n      uint256 previousAttestationValue = _attestationsData[attestations[i].collectionId][\n        attestations[i].owner\n      ].value;\n\n      _attestationsData[attestations[i].collectionId][attestations[i].owner] = AttestationData(\n        attestations[i].issuer,\n        attestations[i].value,\n        attestations[i].timestamp,\n        attestations[i].extraData\n      );\n\n      _triggerBadgeTransferEvent(\n        attestations[i].collectionId,\n        attestations[i].owner,\n        previousAttestationValue,\n        attestations[i].value\n      );\n      emit AttestationRecorded(attestations[i]);\n    }\n  }\n\n  /**\n   * @dev Delete function to be called by authorized issuers\n   * @param owners The owners of the attestations to be deleted\n   * @param collectionIds The collection ids of the attestations to be deleted\n   */\n  function deleteAttestations(\n    address[] calldata owners,\n    uint256[] calldata collectionIds\n  ) external override whenNotPaused {\n    if (owners.length != collectionIds.length)\n      revert OwnersAndCollectionIdsLengthMismatch(owners, collectionIds);\n\n    address issuer = _msgSender();\n    for (uint256 i = 0; i < owners.length; i++) {\n      AttestationData memory attestationData = _attestationsData[collectionIds[i]][owners[i]];\n\n      if (!_isAuthorized(issuer, collectionIds[i]))\n        revert IssuerNotAuthorized(issuer, collectionIds[i]);\n      delete _attestationsData[collectionIds[i]][owners[i]];\n\n      _triggerBadgeTransferEvent(collectionIds[i], owners[i], attestationData.value, 0);\n\n      emit AttestationDeleted(\n        Attestation(\n          collectionIds[i],\n          owners[i],\n          attestationData.issuer,\n          attestationData.value,\n          attestationData.timestamp,\n          attestationData.extraData\n        )\n      );\n    }\n  }\n\n  /**\n   * @dev Returns whether a user has an attestation from a collection\n   * @param collectionId Collection identifier of the targeted attestation\n   * @param owner Owner of the targeted attestation\n   */\n  function hasAttestation(\n    uint256 collectionId,\n    address owner\n  ) external view override returns (bool) {\n    return _getAttestationValue(collectionId, owner) != 0;\n  }\n\n  /**\n   * @dev Getter of the data of a specific attestation\n   * @param collectionId Collection identifier of the targeted attestation\n   * @param owner Owner of the targeted attestation\n   */\n  function getAttestationData(\n    uint256 collectionId,\n    address owner\n  ) external view override returns (AttestationData memory) {\n    return _getAttestationData(collectionId, owner);\n  }\n\n  /**\n   * @dev Getter of the value of a specific attestation\n   * @param collectionId Collection identifier of the targeted attestation\n   * @param owner Owner of the targeted attestation\n   */\n  function getAttestationValue(\n    uint256 collectionId,\n    address owner\n  ) external view override returns (uint256) {\n    return _getAttestationValue(collectionId, owner);\n  }\n\n  /**\n   * @dev Getter of the data of a specific attestation as tuple\n   * @param collectionId Collection identifier of the targeted attestation\n   * @param owner Owner of the targeted attestation\n   */\n  function getAttestationDataTuple(\n    uint256 collectionId,\n    address owner\n  ) external view override returns (address, uint256, uint32, bytes memory) {\n    AttestationData memory attestationData = _attestationsData[collectionId][owner];\n    return (\n      attestationData.issuer,\n      attestationData.value,\n      attestationData.timestamp,\n      attestationData.extraData\n    );\n  }\n\n  /**\n   * @dev Getter of the extraData of a specific attestation\n   * @param collectionId Collection identifier of the targeted attestation\n   * @param owner Owner of the targeted attestation\n   */\n  function getAttestationExtraData(\n    uint256 collectionId,\n    address owner\n  ) external view override returns (bytes memory) {\n    return _attestationsData[collectionId][owner].extraData;\n  }\n\n  /**\n   * @dev Getter of the issuer of a specific attestation\n   * @param collectionId Collection identifier of the targeted attestation\n   * @param owner Owner of the targeted attestation\n   */\n  function getAttestationIssuer(\n    uint256 collectionId,\n    address owner\n  ) external view override returns (address) {\n    return _attestationsData[collectionId][owner].issuer;\n  }\n\n  /**\n   * @dev Getter of the timestamp of a specific attestation\n   * @param collectionId Collection identifier of the targeted attestation\n   * @param owner Owner of the targeted attestation\n   */\n  function getAttestationTimestamp(\n    uint256 collectionId,\n    address owner\n  ) external view override returns (uint32) {\n    return _attestationsData[collectionId][owner].timestamp;\n  }\n\n  /**\n   * @dev Getter of the data of specific attestations\n   * @param collectionIds Collection identifiers of the targeted attestations\n   * @param owners Owners of the targeted attestations\n   */\n  function getAttestationDataBatch(\n    uint256[] memory collectionIds,\n    address[] memory owners\n  ) external view override returns (AttestationData[] memory) {\n    AttestationData[] memory attestationsDataArray = new AttestationData[](collectionIds.length);\n    for (uint256 i = 0; i < collectionIds.length; i++) {\n      attestationsDataArray[i] = _getAttestationData(collectionIds[i], owners[i]);\n    }\n    return attestationsDataArray;\n  }\n\n  /**\n   * @dev Getter of the values of specific attestations\n   * @param collectionIds Collection identifiers of the targeted attestations\n   * @param owners Owners of the targeted attestations\n   */\n  function getAttestationValueBatch(\n    uint256[] memory collectionIds,\n    address[] memory owners\n  ) external view override returns (uint256[] memory) {\n    uint256[] memory attestationsValues = new uint256[](collectionIds.length);\n    for (uint256 i = 0; i < collectionIds.length; i++) {\n      attestationsValues[i] = _getAttestationValue(collectionIds[i], owners[i]);\n    }\n    return attestationsValues;\n  }\n\n  /**\n   * @dev Function that trigger a TransferSingle event from the stateless ERC1155 Badges contract\n   * It enables off-chain apps such as opensea to catch the \"shadow mints/burns\" of badges\n   */\n  function _triggerBadgeTransferEvent(\n    uint256 badgeTokenId,\n    address owner,\n    uint256 previousValue,\n    uint256 newValue\n  ) internal {\n    bool isGreaterValue = newValue > previousValue;\n    address operator = address(this);\n    address from = isGreaterValue ? address(0) : owner;\n    address to = isGreaterValue ? owner : address(0);\n    uint256 value = isGreaterValue ? newValue - previousValue : previousValue - newValue;\n\n    // if isGreaterValue is true, function triggers mint event. Otherwise triggers burn event.\n    BADGES.triggerTransferEvent(operator, from, to, badgeTokenId, value);\n  }\n\n  function _getAttestationData(\n    uint256 collectionId,\n    address owner\n  ) internal view returns (AttestationData memory) {\n    return (_attestationsData[collectionId][owner]);\n  }\n\n  function _getAttestationValue(\n    uint256 collectionId,\n    address owner\n  ) internal view returns (uint256) {\n    return _attestationsData[collectionId][owner].value;\n  }\n}\n"
  },
  {
    "path": "contracts/core/Attester.sol",
    "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.14;\nimport {IAttester} from './interfaces/IAttester.sol';\nimport {IAttestationsRegistry} from './interfaces/IAttestationsRegistry.sol';\nimport {Request, Attestation, AttestationData} from './libs/Structs.sol';\n\n/**\n * @title Attester Abstract Contract\n * @author Sismo\n * @notice Contract to be inherited by Attesters\n * All attesters that expect to be authorized in Sismo Protocol (i.e write access on the registry)\n * are recommended to implemented this abstract contract\n\n * Take a look at the HydraS1SimpleAttester.sol for example on how to implement this abstract contract\n *\n * This contracts is built around two main external standard functions.\n * They must NOT be override them, unless your really know what you are doing\n \n * - generateAttestations(request, proof) => will write attestations in the registry\n * 1. (MANDATORY) Implement the buildAttestations() view function which generate attestations from user request\n * 2. (MANDATORY) Implement teh _verifyRequest() internal function where to write checks\n * 3. (OPTIONAL)  Override _beforeRecordAttestations and _afterRecordAttestations hooks\n\n * - deleteAttestations(collectionId, owner, proof) => will delete attestations in the registry\n * 1. (DEFAULT)  By default this function throws (see _verifyAttestationsDeletionRequest)\n * 2. (OPTIONAL) Override the _verifyAttestationsDeletionRequest so it no longer throws\n * 3. (OPTIONAL) Override _beforeDeleteAttestations and _afterDeleteAttestations hooks\n\n * For more information: https://attesters.docs.sismo.io\n **/\nabstract contract Attester is IAttester {\n  // Registry where all attestations are stored\n  IAttestationsRegistry internal immutable ATTESTATIONS_REGISTRY;\n\n  /**\n   * @dev Constructor\n   * @param attestationsRegistryAddress The address of the AttestationsRegistry contract storing attestations\n   */\n  constructor(address attestationsRegistryAddress) {\n    ATTESTATIONS_REGISTRY = IAttestationsRegistry(attestationsRegistryAddress);\n  }\n\n  /**\n   * @dev Main external function. Allows to generate attestations by making a request and submitting proof\n   * @param request User request\n   * @param proofData Data sent along the request to prove its validity\n   * @return attestations Attestations that has been recorded\n   */\n  function generateAttestations(\n    Request calldata request,\n    bytes calldata proofData\n  ) public override returns (Attestation[] memory) {\n    // Verify if request is valid by verifying against proof\n    _verifyRequest(request, proofData);\n\n    // Generate the actual attestations from user request\n    Attestation[] memory attestations = buildAttestations(request, proofData);\n\n    _beforeRecordAttestations(request, proofData);\n\n    ATTESTATIONS_REGISTRY.recordAttestations(attestations);\n\n    _afterRecordAttestations(attestations);\n\n    for (uint256 i = 0; i < attestations.length; i++) {\n      emit AttestationGenerated(attestations[i]);\n    }\n\n    return attestations;\n  }\n\n  /**\n   * @dev High level function to generate attestations by making a request and submitting proof\n   * @param request User request\n   * @param proofData Data sent along the request to prove its validity\n   * @return badges owner, badges tokenIds and badges values\n   */\n  function mintBadges(\n    Request calldata request,\n    bytes calldata proofData\n  ) external returns (address, uint256[] memory, uint256[] memory) {\n    Attestation[] memory attestations = generateAttestations(request, proofData);\n\n    uint256[] memory collectionIds = new uint256[](attestations.length);\n    uint256[] memory values = new uint256[](attestations.length);\n\n    for (uint256 i = 0; i < attestations.length; i++) {\n      collectionIds[i] = attestations[i].collectionId;\n      values[i] = attestations[i].value;\n    }\n\n    return (attestations[0].owner, collectionIds, values);\n  }\n\n  /**\n   * @dev External facing function. Allows to delete attestations by submitting proof\n   * @param collectionIds Collection identifier of attestations to delete\n   * @param attestationsOwner Owner of attestations to delete\n   * @param proofData Data sent along the deletion request to prove its validity\n   * @return attestations Attestations that were deleted\n   */\n  function deleteAttestations(\n    uint256[] calldata collectionIds,\n    address attestationsOwner,\n    bytes calldata proofData\n  ) external override returns (Attestation[] memory) {\n    address[] memory attestationOwners = new address[](collectionIds.length);\n\n    uint256[] memory attestationCollectionIds = new uint256[](collectionIds.length);\n\n    Attestation[] memory attestations = new Attestation[](collectionIds.length);\n\n    for (uint256 i = 0; i < collectionIds.length; i++) {\n      // fetch attestations from the registry\n      (\n        address issuer,\n        uint256 attestationValue,\n        uint32 timestamp,\n        bytes memory extraData\n      ) = ATTESTATIONS_REGISTRY.getAttestationDataTuple(collectionIds[i], attestationsOwner);\n\n      attestationOwners[i] = attestationsOwner;\n      attestationCollectionIds[i] = collectionIds[i];\n\n      attestations[i] = (\n        Attestation(\n          collectionIds[i],\n          attestationsOwner,\n          issuer,\n          attestationValue,\n          timestamp,\n          extraData\n        )\n      );\n    }\n\n    _verifyAttestationsDeletionRequest(attestations, proofData);\n\n    _beforeDeleteAttestations(attestations, proofData);\n\n    ATTESTATIONS_REGISTRY.deleteAttestations(attestationOwners, attestationCollectionIds);\n\n    _afterDeleteAttestations(attestations, proofData);\n\n    for (uint256 i = 0; i < collectionIds.length; i++) {\n      emit AttestationDeleted(attestations[i]);\n    }\n    return attestations;\n  }\n\n  /**\n   * @dev MANDATORY: must be implemented in attesters\n   * It should build attestations from the user request and the proof\n   * @param request User request\n   * @param proofData Data sent along the request to prove its validity\n   * @return attestations Attestations that will be recorded\n   */\n  function buildAttestations(\n    Request calldata request,\n    bytes calldata proofData\n  ) public view virtual returns (Attestation[] memory);\n\n  /**\n   * @dev Attestation registry getter\n   * @return attestationRegistry\n   */\n  function getAttestationRegistry() external view override returns (IAttestationsRegistry) {\n    return ATTESTATIONS_REGISTRY;\n  }\n\n  /**\n   * @dev MANDATORY: must be implemented in attesters\n   * It should verify the user request is valid\n   * @param request User request\n   * @param proofData Data sent along the request to prove its validity\n   */\n  function _verifyRequest(Request calldata request, bytes calldata proofData) internal virtual;\n\n  /**\n   * @dev Optional: must be overridden by attesters that want to feature attestations deletion\n   * Default behavior: throws\n   * It should verify attestations deletion request is valid\n   * @param attestations Attestations that will be deleted\n   * @param proofData Data sent along the request to prove its validity\n   */\n  function _verifyAttestationsDeletionRequest(\n    Attestation[] memory attestations,\n    bytes calldata proofData\n  ) internal virtual {\n    revert AttestationDeletionNotImplemented();\n  }\n\n  /**\n   * @dev Optional: Hook, can be overridden in attesters\n   * Will be called before recording attestations in the registry\n   * @param request User request\n   * @param proofData Data sent along the request to prove its validity\n   */\n  function _beforeRecordAttestations(\n    Request calldata request,\n    bytes calldata proofData\n  ) internal virtual {}\n\n  /**\n   * @dev (Optional) Can be overridden in attesters inheriting this contract\n   * Will be called after recording an attestation\n   * @param attestations Recorded attestations\n   */\n  function _afterRecordAttestations(Attestation[] memory attestations) internal virtual {}\n\n  /**\n   * @dev Optional: Hook, can be overridden in attesters\n   * Will be called before deleting attestations from the registry\n   * @param attestations Attestations to delete\n   * @param proofData Data sent along the deletion request to prove its validity\n   */\n  function _beforeDeleteAttestations(\n    Attestation[] memory attestations,\n    bytes calldata proofData\n  ) internal virtual {}\n\n  /**\n   * @dev Optional: Hook, can be overridden in attesters\n   * Will be called after deleting attestations from the registry\n   * @param attestations Attestations to delete\n   * @param proofData Data sent along the deletion request to prove its validity\n   */\n  function _afterDeleteAttestations(\n    Attestation[] memory attestations,\n    bytes calldata proofData\n  ) internal virtual {}\n}\n"
  },
  {
    "path": "contracts/core/Badges.sol",
    "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.14;\n\nimport {ERC1155} from '@openzeppelin/contracts/token/ERC1155/ERC1155.sol';\nimport {Initializable} from '@openzeppelin/contracts/proxy/utils/Initializable.sol';\nimport {ERC1155Pausable} from '@openzeppelin/contracts/token/ERC1155/extensions/ERC1155Pausable.sol';\nimport {AccessControl} from '@openzeppelin/contracts/access/AccessControl.sol';\nimport {IAttestationsRegistry} from './interfaces/IAttestationsRegistry.sol';\nimport {IBadges} from './interfaces/IBadges.sol';\n\n/**\n * @title Badges contract\n * @author Sismo\n * @notice Stateless, Non-transferrable ERC1155 contract. Reads balance from the values of attestations\n * The associated attestations registry triggers TransferSingle events from this contract\n * It allows badge \"shadow mints and burns\" to be caught by off-chain platforms\n * For more information: https://badges.docs.sismo.io\n */\ncontract Badges is IBadges, Initializable, AccessControl, ERC1155 {\n  uint8 public constant IMPLEMENTATION_VERSION = 3;\n\n  IAttestationsRegistry internal _attestationsRegistry;\n\n  bytes32 public constant EVENT_TRIGGERER_ROLE = keccak256('EVENT_TRIGGERER_ROLE');\n\n  /**\n   * @dev Constructor\n   * @param uri Uri for the metadata of badges\n   * @param owner Owner of the contract, super admin, can setup roles and update the attestation registry\n   */\n  constructor(\n    string memory uri,\n    address owner // This is Sismo Frontend Contract\n  ) ERC1155(uri) {\n    initialize(uri, owner);\n  }\n\n  /**\n   * @dev Initializes the contract, to be called by the proxy delegating calls to this implementation\n   * @param uri Uri for the metadata of badges\n   * @param owner Owner of the contract, super admin, can setup roles and update the attestation registry\n   * @notice The reinitializer modifier is needed to configure modules that are added through upgrades and that require initialization.\n   */\n  function initialize(\n    string memory uri,\n    address owner\n  ) public reinitializer(IMPLEMENTATION_VERSION) {\n    // if proxy did not setup uri yet or if called by constructor (for implem setup)\n    if (bytes(ERC1155.uri(0)).length == 0 || address(this).code.length == 0) {\n      _setURI(uri);\n      _grantRole(DEFAULT_ADMIN_ROLE, owner);\n    }\n  }\n\n  /**\n   * @dev Main function of the ERC1155 badge\n   * The balance of a user is equal to the value of the underlying attestation.\n   * attestationCollectionId == badgeId\n   * @param account Address to check badge balance (= value of attestation)\n   * @param id Badge Id to check (= attestationCollectionId)\n   */\n  function balanceOf(\n    address account,\n    uint256 id\n  ) public view virtual override(ERC1155, IBadges) returns (uint256) {\n    return _attestationsRegistry.getAttestationValue(id, account);\n  }\n\n  /**\n   * @dev Reverts, this is a non transferable ERC115 contract\n   */\n  function setApprovalForAll(address operator, bool approved) public virtual override {\n    revert BadgesNonTransferrable();\n  }\n\n  /**\n   * @dev Reverts, this is a non transferable ERC115 contract\n   */\n  function isApprovedForAll(\n    address account,\n    address operator\n  ) public view virtual override returns (bool) {\n    revert BadgesNonTransferrable();\n  }\n\n  /**\n   * @dev Emits a TransferSingle event, so subgraphs and other off-chain apps relying on events can see badge minting/burning\n   * can only be called by address having the EVENT_TRIGGERER_ROLE (attestations registry address)\n   * @param operator who is calling the TransferEvent\n   * @param from address(0) if minting, address of the badge holder if burning\n   * @param to address of the badge holder is minting, address(0) if burning\n   * @param id badgeId for which to trigger the event\n   * @param value minted/burned balance\n   */\n  function triggerTransferEvent(\n    address operator,\n    address from,\n    address to,\n    uint256 id,\n    uint256 value\n  ) external onlyRole(EVENT_TRIGGERER_ROLE) {\n    emit TransferSingle(operator, from, to, id, value);\n  }\n\n  /**\n   * @dev Set the attestations registry address. Can only be called by owner (default admin)\n   * @param attestationsRegistry new attestations registry address\n   */\n  function setAttestationsRegistry(\n    address attestationsRegistry\n  ) external override onlyRole(DEFAULT_ADMIN_ROLE) {\n    _attestationsRegistry = IAttestationsRegistry(attestationsRegistry);\n  }\n\n  /**\n   * @dev Set the URI. Can only be called by owner (default admin)\n   * @param uri new attestations registry address\n   */\n  function setUri(string memory uri) external override onlyRole(DEFAULT_ADMIN_ROLE) {\n    _setURI(uri);\n  }\n\n  /**\n   * @dev Getter of the attestations registry\n   */\n  function getAttestationsRegistry() external view override returns (address) {\n    return address(_attestationsRegistry);\n  }\n\n  /**\n   * @dev Getter of the badge issuer\n   * @param account Address that holds the badge\n   * @param id Badge Id to check (= attestationCollectionId)\n   */\n  function getBadgeIssuer(address account, uint256 id) external view returns (address) {\n    return _attestationsRegistry.getAttestationIssuer(id, account);\n  }\n\n  /**\n   * @dev Getter of the badge timestamp\n   * @param account Address that holds the badge\n   * @param id Badge Id to check (= attestationCollectionId)\n   */\n  function getBadgeTimestamp(address account, uint256 id) external view returns (uint32) {\n    return _attestationsRegistry.getAttestationTimestamp(id, account);\n  }\n\n  /**\n   * @dev Getter of the badge extra data (it can store nullifier and burnCount)\n   * @param account Address that holds the badge\n   * @param id Badge Id to check (= attestationCollectionId)\n   */\n  function getBadgeExtraData(address account, uint256 id) external view returns (bytes memory) {\n    return _attestationsRegistry.getAttestationExtraData(id, account);\n  }\n\n  /**\n   * @dev Getter of the value of a specific badge attribute\n   * @param id Badge Id to check (= attestationCollectionId)\n   * @param index Index of the attribute\n   */\n  function getAttributeValueForBadge(uint256 id, uint8 index) external view returns (uint8) {\n    return _attestationsRegistry.getAttributeValueForAttestationsCollection(id, index);\n  }\n\n  /**\n   * @dev Getter of all badge attributes and their values for a specific badge\n   * @param id Badge Id to check (= attestationCollectionId)\n   */\n  function getAttributesNamesAndValuesForBadge(\n    uint256 id\n  ) external view returns (bytes32[] memory, uint8[] memory) {\n    return _attestationsRegistry.getAttributesNamesAndValuesForAttestationsCollection(id);\n  }\n\n  /**\n   * @dev ERC165\n   */\n  function supportsInterface(\n    bytes4 interfaceId\n  ) public view virtual override(AccessControl, ERC1155) returns (bool) {\n    return super.supportsInterface(interfaceId);\n  }\n\n  /**\n   * @dev Reverts, this is a non transferable ERC115 contract\n   */\n  function _beforeTokenTransfer(\n    address operator,\n    address from,\n    address to,\n    uint256[] memory ids,\n    uint256[] memory amounts,\n    bytes memory data\n  ) internal virtual override {\n    revert BadgesNonTransferrable();\n  }\n}\n"
  },
  {
    "path": "contracts/core/Front.sol",
    "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.14;\n\nimport {IFront} from './interfaces/IFront.sol';\nimport {IAttester} from './interfaces/IAttester.sol';\nimport {IAttestationsRegistry} from './interfaces/IAttestationsRegistry.sol';\nimport {Request, Attestation} from './libs/Structs.sol';\n\n/**\n * @title Front\n * @author Sismo\n * @notice This is the Front contract of the Sismo protocol\n * Behind a proxy, it routes attestations request to the targeted attester and can perform some actions\n * This specific implementation rewards early users with a early user attestation if they used sismo before ethcc conference\n\n * For more information: https://front.docs.sismo.io\n */\ncontract Front is IFront {\n  IAttestationsRegistry public immutable ATTESTATIONS_REGISTRY;\n  uint256 public constant EARLY_USER_COLLECTION = 0;\n  uint32 public constant EARLY_USER_BADGE_END_DATE = 1663200000; // Sept 15\n\n  /**\n   * @dev Constructor\n   * @param attestationsRegistryAddress Attestations registry contract address\n   */\n  constructor(address attestationsRegistryAddress) {\n    ATTESTATIONS_REGISTRY = IAttestationsRegistry(attestationsRegistryAddress);\n  }\n\n  /**\n   * @dev Forward a request to an attester and generates an early user attestation\n   * @param attester Attester targeted by the request\n   * @param request Request sent to the attester\n   * @param proofData Data provided to the attester to back the request\n   */\n  function generateAttestations(\n    address attester,\n    Request calldata request,\n    bytes calldata proofData\n  ) external override returns (Attestation[] memory) {\n    Attestation[] memory attestations = _forwardAttestationsGeneration(\n      attester,\n      request,\n      proofData\n    );\n    _generateEarlyUserAttestation(request.destination);\n    return attestations;\n  }\n\n  /**\n   * @dev generate multiple attestations at once, to the same destination, generates an early user attestation\n   * @param attesters Attesters targeted by the attesters\n   * @param requests Requests sent to attester\n   * @param proofDataArray Data sent with each request\n   */\n  function batchGenerateAttestations(\n    address[] calldata attesters,\n    Request[] calldata requests,\n    bytes[] calldata proofDataArray\n  ) external override returns (Attestation[][] memory) {\n    Attestation[][] memory attestations = new Attestation[][](attesters.length);\n    address destination = requests[0].destination;\n    for (uint256 i = 0; i < attesters.length; i++) {\n      if (requests[i].destination != destination) revert DifferentRequestsDestinations();\n      attestations[i] = _forwardAttestationsGeneration(\n        attesters[i],\n        requests[i],\n        proofDataArray[i]\n      );\n    }\n    _generateEarlyUserAttestation(destination);\n    return attestations;\n  }\n\n  /**\n   * @dev build the attestations from a user request targeting a specific attester.\n   * Forwards to the build function of targeted attester\n   * @param attester Targeted attester\n   * @param request User request\n   * @param proofData Data sent along the request to prove its validity\n   * @return attestations Attestations that will be recorded\n   */\n  function buildAttestations(\n    address attester,\n    Request calldata request,\n    bytes calldata proofData\n  ) external view override returns (Attestation[] memory) {\n    return _forwardAttestationsBuild(attester, request, proofData);\n  }\n\n  /**\n   * @dev build the attestations from multiple user requests.\n   * Forwards to the build function of targeted attester\n   * @param attesters Targeted attesters\n   * @param requests User requests\n   * @param proofDataArray Data sent along the request to prove its validity\n   * @return attestations Attestations that will be recorded\n   */\n  function batchBuildAttestations(\n    address[] calldata attesters,\n    Request[] calldata requests,\n    bytes[] calldata proofDataArray\n  ) external view override returns (Attestation[][] memory) {\n    Attestation[][] memory attestations = new Attestation[][](attesters.length);\n\n    for (uint256 i = 0; i < attesters.length; i++) {\n      attestations[i] = _forwardAttestationsBuild(attesters[i], requests[i], proofDataArray[i]);\n    }\n    return attestations;\n  }\n\n  function _forwardAttestationsBuild(\n    address attester,\n    Request calldata request,\n    bytes calldata proofData\n  ) internal view returns (Attestation[] memory) {\n    return IAttester(attester).buildAttestations(request, proofData);\n  }\n\n  function _forwardAttestationsGeneration(\n    address attester,\n    Request calldata request,\n    bytes calldata proofData\n  ) internal returns (Attestation[] memory) {\n    return IAttester(attester).generateAttestations(request, proofData);\n  }\n\n  function _generateEarlyUserAttestation(address destination) internal {\n    uint32 currentTimestamp = uint32(block.timestamp);\n    if (currentTimestamp < EARLY_USER_BADGE_END_DATE) {\n      bool alreadyHasAttestation = ATTESTATIONS_REGISTRY.hasAttestation(\n        EARLY_USER_COLLECTION,\n        destination\n      );\n\n      if (!alreadyHasAttestation) {\n        Attestation[] memory attestations = new Attestation[](1);\n        attestations[0] = Attestation(\n          EARLY_USER_COLLECTION,\n          destination,\n          address(this),\n          1,\n          currentTimestamp,\n          'With strong love from Sismo'\n        );\n        ATTESTATIONS_REGISTRY.recordAttestations(attestations);\n        emit EarlyUserAttestationGenerated(destination);\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "contracts/core/interfaces/IAttestationsRegistry.sol",
    "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.14;\n\nimport {Attestation, AttestationData} from '../libs/Structs.sol';\nimport {IAttestationsRegistryConfigLogic} from './IAttestationsRegistryConfigLogic.sol';\n\n/**\n * @title IAttestationsRegistry\n * @author Sismo\n * @notice This is the interface of the AttestationRegistry\n */\ninterface IAttestationsRegistry is IAttestationsRegistryConfigLogic {\n  error IssuerNotAuthorized(address issuer, uint256 collectionId);\n  error OwnersAndCollectionIdsLengthMismatch(address[] owners, uint256[] collectionIds);\n  event AttestationRecorded(Attestation attestation);\n  event AttestationDeleted(Attestation attestation);\n\n  /**\n   * @dev Main function to be called by authorized issuers\n   * @param attestations Attestations to be recorded (creates a new one or overrides an existing one)\n   */\n  function recordAttestations(Attestation[] calldata attestations) external;\n\n  /**\n   * @dev Delete function to be called by authorized issuers\n   * @param owners The owners of the attestations to be deleted\n   * @param collectionIds The collection ids of the attestations to be deleted\n   */\n  function deleteAttestations(address[] calldata owners, uint256[] calldata collectionIds) external;\n\n  /**\n   * @dev Returns whether a user has an attestation from a collection\n   * @param collectionId Collection identifier of the targeted attestation\n   * @param owner Owner of the targeted attestation\n   */\n  function hasAttestation(uint256 collectionId, address owner) external returns (bool);\n\n  /**\n   * @dev Getter of the data of a specific attestation\n   * @param collectionId Collection identifier of the targeted attestation\n   * @param owner Owner of the targeted attestation\n   */\n  function getAttestationData(\n    uint256 collectionId,\n    address owner\n  ) external view returns (AttestationData memory);\n\n  /**\n   * @dev Getter of the value of a specific attestation\n   * @param collectionId Collection identifier of the targeted attestation\n   * @param owner Owner of the targeted attestation\n   */\n  function getAttestationValue(uint256 collectionId, address owner) external view returns (uint256);\n\n  /**\n   * @dev Getter of the data of a specific attestation as tuple\n   * @param collectionId Collection identifier of the targeted attestation\n   * @param owner Owner of the targeted attestation\n   */\n  function getAttestationDataTuple(\n    uint256 collectionId,\n    address owner\n  ) external view returns (address, uint256, uint32, bytes memory);\n\n  /**\n   * @dev Getter of the extraData of a specific attestation\n   * @param collectionId Collection identifier of the targeted attestation\n   * @param owner Owner of the targeted attestation\n   */\n  function getAttestationExtraData(\n    uint256 collectionId,\n    address owner\n  ) external view returns (bytes memory);\n\n  /**\n   * @dev Getter of the issuer of a specific attestation\n   * @param collectionId Collection identifier of the targeted attestation\n   * @param owner Owner of the targeted attestation\n   */\n  function getAttestationIssuer(\n    uint256 collectionId,\n    address owner\n  ) external view returns (address);\n\n  /**\n   * @dev Getter of the timestamp of a specific attestation\n   * @param collectionId Collection identifier of the targeted attestation\n   * @param owner Owner of the targeted attestation\n   */\n  function getAttestationTimestamp(\n    uint256 collectionId,\n    address owner\n  ) external view returns (uint32);\n\n  /**\n   * @dev Getter of the data of specific attestations\n   * @param collectionIds Collection identifiers of the targeted attestations\n   * @param owners Owners of the targeted attestations\n   */\n  function getAttestationDataBatch(\n    uint256[] memory collectionIds,\n    address[] memory owners\n  ) external view returns (AttestationData[] memory);\n\n  /**\n   * @dev Getter of the values of specific attestations\n   * @param collectionIds Collection identifiers of the targeted attestations\n   * @param owners Owners of the targeted attestations\n   */\n  function getAttestationValueBatch(\n    uint256[] memory collectionIds,\n    address[] memory owners\n  ) external view returns (uint256[] memory);\n}\n"
  },
  {
    "path": "contracts/core/interfaces/IAttestationsRegistryConfigLogic.sol",
    "content": "// SPDX-License-Identifier: MIT\n// Forked from, removed storage, OpenZeppelin Contracts v4.4.1 (access/Ownable.sol)\n// OpenZeppelin Contracts v4.4.1 (access/Ownable.sol)\n\npragma solidity ^0.8.14;\n\nimport {Range, RangeUtils} from '../libs/utils/RangeLib.sol';\n\ninterface IAttestationsRegistryConfigLogic {\n  error AttesterNotFound(address issuer);\n  error RangeIndexOutOfBounds(address issuer, uint256 expectedArrayLength, uint256 rangeIndex);\n  error IdsMismatch(\n    address issuer,\n    uint256 rangeIndex,\n    uint256 expectedFirstId,\n    uint256 expectedLastId,\n    uint256 FirstId,\n    uint256 lastCollectionId\n  );\n  error AttributeDoesNotExist(uint8 attributeIndex);\n  error AttributeAlreadyExists(uint8 attributeIndex);\n  error ArgsLengthDoesNotMatch();\n\n  event NewAttributeCreated(uint8 attributeIndex, bytes32 attributeName);\n  event AttributeNameUpdated(\n    uint8 attributeIndex,\n    bytes32 newAttributeName,\n    bytes32 previousAttributeName\n  );\n  event AttributeDeleted(uint8 attributeIndex, bytes32 deletedAttributeName);\n\n  event AttestationsCollectionAttributeSet(\n    uint256 collectionId,\n    uint8 attributeIndex,\n    uint8 attributeValue\n  );\n\n  event IssuerAuthorized(address issuer, uint256 firstCollectionId, uint256 lastCollectionId);\n  event IssuerUnauthorized(address issuer, uint256 firstCollectionId, uint256 lastCollectionId);\n\n  /**\n   * @dev Returns whether an attestationsCollection has a specific attribute referenced by its index\n   * @param collectionId Collection Id of the targeted attestationsCollection\n   * @param index Index of the attribute. Can go from 0 to 63.\n   */\n  function attestationsCollectionHasAttribute(\n    uint256 collectionId,\n    uint8 index\n  ) external view returns (bool);\n\n  function attestationsCollectionHasAttributes(\n    uint256 collectionId,\n    uint8[] memory indices\n  ) external view returns (bool);\n\n  /**\n   * @dev Returns the attribute's value (from 1 to 15) of an attestationsCollection\n   * @param collectionId Collection Id of the targeted attestationsCollection\n   * @param attributeIndex Index of the attribute. Can go from 0 to 63.\n   */\n  function getAttributeValueForAttestationsCollection(\n    uint256 collectionId,\n    uint8 attributeIndex\n  ) external view returns (uint8);\n\n  function getAttributesValuesForAttestationsCollection(\n    uint256 collectionId,\n    uint8[] memory indices\n  ) external view returns (uint8[] memory);\n\n  /**\n   * @dev Set a value for an attribute of an attestationsCollection. The attribute should already be created.\n   * @param collectionId Collection Id of the targeted attestationsCollection\n   * @param index Index of the attribute (must be between 0 and 63)\n   * @param value Value of the attribute we want to set for this attestationsCollection. Can take the value 0 to 15\n   */\n  function setAttributeValueForAttestationsCollection(\n    uint256 collectionId,\n    uint8 index,\n    uint8 value\n  ) external;\n\n  function setAttributesValuesForAttestationsCollections(\n    uint256[] memory collectionIds,\n    uint8[] memory indices,\n    uint8[] memory values\n  ) external;\n\n  /**\n   * @dev Returns all the enabled attributes names and their values for a specific attestationsCollection\n   * @param collectionId Collection Id of the targeted attestationsCollection\n   */\n  function getAttributesNamesAndValuesForAttestationsCollection(\n    uint256 collectionId\n  ) external view returns (bytes32[] memory, uint8[] memory);\n\n  /**\n   * @dev Authorize an issuer for a specific range\n   * @param issuer Issuer that will be authorized\n   * @param firstCollectionId First collection Id of the range for which the issuer will be authorized\n   * @param lastCollectionId Last collection Id of the range for which the issuer will be authorized\n   */\n  function authorizeRange(\n    address issuer,\n    uint256 firstCollectionId,\n    uint256 lastCollectionId\n  ) external;\n\n  /**\n   * @dev Unauthorize an issuer for a specific range\n   * @param issuer Issuer that will be unauthorized\n   * @param rangeIndex Index of the range to be unauthorized\n   * @param firstCollectionId First collection Id of the range for which the issuer will be unauthorized\n   * @param lastCollectionId Last collection Id of the range for which the issuer will be unauthorized\n   */\n  function unauthorizeRange(\n    address issuer,\n    uint256 rangeIndex,\n    uint256 firstCollectionId,\n    uint256 lastCollectionId\n  ) external;\n\n  /**\n   * @dev Authorize an issuer for specific ranges\n   * @param issuer Issuer that will be authorized\n   * @param ranges Ranges for which the issuer will be authorized\n   */\n  function authorizeRanges(address issuer, Range[] memory ranges) external;\n\n  /**\n   * @dev Unauthorize an issuer for specific ranges\n   * @param issuer Issuer that will be unauthorized\n   * @param ranges Ranges for which the issuer will be unauthorized\n   */\n  function unauthorizeRanges(\n    address issuer,\n    Range[] memory ranges,\n    uint256[] memory rangeIndexes\n  ) external;\n\n  /**\n   * @dev Returns whether a specific issuer is authorized or not to record in a specific attestations collection\n   * @param issuer Issuer to be checked\n   * @param collectionId Collection Id for which the issuer will be checked\n   */\n  function isAuthorized(address issuer, uint256 collectionId) external view returns (bool);\n\n  /**\n   * @dev Pauses the registry. Issuers can no longer record or delete attestations\n   */\n  function pause() external;\n\n  /**\n   * @dev Unpauses the registry\n   */\n  function unpause() external;\n\n  /**\n   * @dev Create a new attribute.\n   * @param index Index of the attribute. Can go from 0 to 63.\n   * @param name Name in bytes32 of the attribute\n   */\n  function createNewAttribute(uint8 index, bytes32 name) external;\n\n  function createNewAttributes(uint8[] memory indices, bytes32[] memory names) external;\n\n  /**\n   * @dev Update the name of an existing attribute\n   * @param index Index of the attribute. Can go from 0 to 63. The attribute must exist\n   * @param newName new name in bytes32 of the attribute\n   */\n  function updateAttributeName(uint8 index, bytes32 newName) external;\n\n  function updateAttributesName(uint8[] memory indices, bytes32[] memory names) external;\n\n  /**\n   * @dev Delete an existing attribute\n   * @param index Index of the attribute. Can go from 0 to 63. The attribute must exist\n   */\n  function deleteAttribute(uint8 index) external;\n\n  function deleteAttributes(uint8[] memory indices) external;\n}\n"
  },
  {
    "path": "contracts/core/interfaces/IAttester.sol",
    "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.14;\n\nimport {Request, Attestation} from '../libs/Structs.sol';\nimport {IAttestationsRegistry} from '../interfaces/IAttestationsRegistry.sol';\n\n/**\n * @title IAttester\n * @author Sismo\n * @notice This is the interface for the attesters in Sismo Protocol\n */\ninterface IAttester {\n  event AttestationGenerated(Attestation attestation);\n\n  event AttestationDeleted(Attestation attestation);\n\n  error AttestationDeletionNotImplemented();\n\n  /**\n   * @dev Main external function. Allows to generate attestations by making a request and submitting proof\n   * @param request User request\n   * @param proofData Data sent along the request to prove its validity\n   * @return attestations Attestations that has been recorded\n   */\n  function generateAttestations(\n    Request calldata request,\n    bytes calldata proofData\n  ) external returns (Attestation[] memory);\n\n  /**\n   * @dev High level function to generate attestations by making a request and submitting proof\n   * @param request User request\n   * @param proofData Data sent along the request to prove its validity\n   * @return badges owner, badges tokenIds and badges values\n   */\n  function mintBadges(\n    Request calldata request,\n    bytes calldata proofData\n  ) external returns (address, uint256[] memory, uint256[] memory);\n\n  /**\n   * @dev External facing function. Allows to delete an attestation by submitting proof\n   * @param collectionIds Collection identifier of attestations to delete\n   * @param attestationsOwner Owner of attestations to delete\n   * @param proofData Data sent along the deletion request to prove its validity\n   * @return attestations Attestations that was deleted\n   */\n  function deleteAttestations(\n    uint256[] calldata collectionIds,\n    address attestationsOwner,\n    bytes calldata proofData\n  ) external returns (Attestation[] memory);\n\n  /**\n   * @dev MANDATORY: must be implemented in attesters\n   * It should build attestations from the user request and the proof\n   * @param request User request\n   * @param proofData Data sent along the request to prove its validity\n   * @return attestations Attestations that will be recorded\n   */\n  function buildAttestations(\n    Request calldata request,\n    bytes calldata proofData\n  ) external view returns (Attestation[] memory);\n\n  /**\n   * @dev Attestation registry address getter\n   * @return attestationRegistry Address of the registry\n   */\n  function getAttestationRegistry() external view returns (IAttestationsRegistry);\n}\n"
  },
  {
    "path": "contracts/core/interfaces/IBadges.sol",
    "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.14;\n\n/**\n * @title Interface for Badges contract\n * @author Sismo\n * @notice Stateless ERC1155 contract. Reads balance from the values of attestations\n * The associated attestations registry triggers TransferSingle events from this contract\n * It allows badge \"shadow mints and burns\" to be caught by off-chain platforms\n */\ninterface IBadges {\n  error BadgesNonTransferrable();\n\n  /**\n   * @dev Initializes the contract, to be called by the proxy delegating calls to this implementation\n   * @param uri Uri for the metadata of badges\n   * @param owner Owner of the contract, super admin, can setup roles and update the attestation registry\n   * @notice The reinitializer modifier is needed to configure modules that are added through upgrades and that require initialization.\n   */\n  function initialize(string memory uri, address owner) external;\n\n  /**\n   * @dev Main function of the ERC1155 badge\n   * The balance of a user is equal to the value of the underlying attestation.\n   * attestationCollectionId == badgeId\n   * @param account Address to check badge balance (= value of attestation)\n   * @param id Badge Id to check (= attestationCollectionId)\n   */\n  function balanceOf(address account, uint256 id) external view returns (uint256);\n\n  /**\n   * @dev Emits a TransferSingle event, so subgraphs and other off-chain apps relying on events can see badge minting/burning\n   * can only be called by address having the EVENT_TRIGGERER_ROLE (attestations registry address)\n   * @param operator who is calling the TransferEvent\n   * @param from address(0) if minting, address of the badge holder if burning\n   * @param to address of the badge holder is minting, address(0) if burning\n   * @param id badgeId for which to trigger the event\n   * @param value minted/burned balance\n   */\n  function triggerTransferEvent(\n    address operator,\n    address from,\n    address to,\n    uint256 id,\n    uint256 value\n  ) external;\n\n  /**\n   * @dev Set the attestations registry address. Can only be called by owner (default admin)\n   * @param attestationsRegistry new attestations registry address\n   */\n  function setAttestationsRegistry(address attestationsRegistry) external;\n\n  /**\n   * @dev Set the URI. Can only be called by owner (default admin)\n   * @param uri new attestations registry address\n   */\n  function setUri(string memory uri) external;\n\n  /**\n   * @dev Getter of the attestations registry\n   */\n  function getAttestationsRegistry() external view returns (address);\n\n  /**\n   * @dev Getter of the badge issuer\n   * @param account Address that holds the badge\n   * @param id Badge Id to check (= attestationCollectionId)\n   */\n  function getBadgeIssuer(address account, uint256 id) external view returns (address);\n\n  /**\n   * @dev Getter of the badge timestamp\n   * @param account Address that holds the badge\n   * @param id Badge Id to check (= attestationCollectionId)\n   */\n  function getBadgeTimestamp(address account, uint256 id) external view returns (uint32);\n\n  /**\n   * @dev Getter of the badge extra data (it can store nullifier and burnCount)\n   * @param account Address that holds the badge\n   * @param id Badge Id to check (= attestationCollectionId)\n   */\n  function getBadgeExtraData(address account, uint256 id) external view returns (bytes memory);\n\n  /**\n   * @dev Getter of the value of a specific badge attribute\n   * @param id Badge Id to check (= attestationCollectionId)\n   * @param index Index of the attribute\n   */\n  function getAttributeValueForBadge(uint256 id, uint8 index) external view returns (uint8);\n\n  /**\n   * @dev Getter of all badge attributes and their values for a specific badge\n   * @param id Badge Id to check (= attestationCollectionId)\n   */\n  function getAttributesNamesAndValuesForBadge(\n    uint256 id\n  ) external view returns (bytes32[] memory, uint8[] memory);\n}\n"
  },
  {
    "path": "contracts/core/interfaces/IFront.sol",
    "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.14;\n\nimport {Request, Attestation} from '../libs/Structs.sol';\n\n/**\n * @title IFront\n * @author Sismo\n * @notice This is the interface of the Front Contract\n */\ninterface IFront {\n  error DifferentRequestsDestinations();\n  event EarlyUserAttestationGenerated(address destination);\n\n  /**\n   * @dev Forward a request to an attester and generates an early user attestation\n   * @param attester Attester targeted by the request\n   * @param request Request sent to the attester\n   * @param proofData Data provided to the attester to back the request\n   */\n  function generateAttestations(\n    address attester,\n    Request calldata request,\n    bytes calldata proofData\n  ) external returns (Attestation[] memory);\n\n  /**\n   * @dev generate multiple attestations at once, to the same destination\n   * @param attesters Attesters targeted by the attesters\n   * @param requests Requests sent to attester\n   * @param proofDataArray Data sent with each request\n   */\n  function batchGenerateAttestations(\n    address[] calldata attesters,\n    Request[] calldata requests,\n    bytes[] calldata proofDataArray\n  ) external returns (Attestation[][] memory);\n\n  /**\n   * @dev build the attestations from a user request targeting a specific attester.\n   * Forwards to the build function of targeted attester\n   * @param attester Targeted attester\n   * @param request User request\n   * @param proofData Data sent along the request to prove its validity\n   * @return attestations Attestations that will be recorded\n   */\n  function buildAttestations(\n    address attester,\n    Request calldata request,\n    bytes calldata proofData\n  ) external view returns (Attestation[] memory);\n\n  /**\n   * @dev build the attestations from multiple user requests.\n   * Forwards to the build function(s) of targeted attester(s)\n   * @param attesters Targeted attesters\n   * @param requests User requests\n   * @param proofDataArray Data sent along the request to prove its validity\n   * @return attestations Attestations that will be recorded\n   */\n  function batchBuildAttestations(\n    address[] calldata attesters,\n    Request[] calldata requests,\n    bytes[] calldata proofDataArray\n  ) external view returns (Attestation[][] memory);\n}\n"
  },
  {
    "path": "contracts/core/libs/Structs.sol",
    "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.14;\n\n/**\n * @title  Attestations Registry State\n * @author Sismo\n * @notice This contract holds all of the storage variables and data\n *         structures used by the AttestationsRegistry and parent\n *         contracts.\n */\n\n// User Attestation Request, can be made by any user\n// The context of an Attestation Request is a specific attester contract\n// Each attester has groups of accounts in its available data\n// eg: for a specific attester:\n//     group 1 <=> accounts that sent txs on mainnet\n//     group 2 <=> accounts that sent txs on polygon\n// eg: for another attester:\n//     group 1 <=> accounts that sent eth txs in 2022\n//     group 2 <=> accounts sent eth txs in 2021\nstruct Request {\n  // implicit address attester;\n  // implicit uint256 chainId;\n  Claim[] claims;\n  address destination; // destination that will receive the end attestation\n}\n\nstruct Claim {\n  uint256 groupId; // user claims to have an account in this group\n  uint256 claimedValue; // user claims this value for its account in the group\n  bytes extraData; // arbitrary data, may be required by the attester to verify claims or generate a specific attestation\n}\n\n/**\n * @dev Attestation Struct. This is the struct receive as argument by the Attestation Registry.\n * @param collectionId Attestation collection\n * @param owner Attestation collection\n * @param issuer Attestation collection\n * @param value Attestation collection\n * @param timestamp Attestation collection\n * @param extraData Attestation collection\n */\nstruct Attestation {\n  // implicit uint256 chainId;\n  uint256 collectionId; // Id of the attestation collection (in the registry)\n  address owner; // Owner of the attestation\n  address issuer; // Contract that created or last updated the record.\n  uint256 value; // Value of the attestation\n  uint32 timestamp; // Timestamp chosen by the attester, should correspond to the effective date of the attestation\n  // it is different from the recording timestamp (date when the attestation was recorded)\n  // e.g a proof of NFT ownership may have be recorded today which is 2 month old data.\n  bytes extraData; // arbitrary data that can be added by the attester\n}\n\n// Attestation Data, stored in the registry\n// The context is a specific owner of a specific collection\nstruct AttestationData {\n  // implicit uint256 chainId\n  // implicit uint256 collectionId - from context\n  // implicit owner\n  address issuer; // Address of the contract that recorded the attestation\n  uint256 value; // Value of the attestation\n  uint32 timestamp; // Effective date of issuance of the attestation. (can be different from the recording timestamp)\n  bytes extraData; // arbitrary data that can be added by the attester\n}\n"
  },
  {
    "path": "contracts/core/libs/attestations-registry/AttestationsRegistryConfigLogic.sol",
    "content": "// SPDX-License-Identifier: MIT\n// Forked from, removed storage, OpenZeppelin Contracts v4.4.1 (access/Ownable.sol)\n// OpenZeppelin Contracts v4.4.1 (access/Ownable.sol)\n\npragma solidity ^0.8.14;\n\nimport './OwnableLogic.sol';\nimport './PausableLogic.sol';\nimport './InitializableLogic.sol';\nimport './AttestationsRegistryState.sol';\nimport {IAttestationsRegistryConfigLogic} from './../../interfaces/IAttestationsRegistryConfigLogic.sol';\nimport {Range, RangeUtils} from '../utils/RangeLib.sol';\nimport {Bitmap256Bit} from '../utils/Bitmap256Bit.sol';\n\n/**\n * @title Attestations Registry Config Logic contract\n * @author Sismo\n * @notice Holds the logic of how to authorize/ unauthorize issuers of attestations in the registry\n **/\ncontract AttestationsRegistryConfigLogic is\n  AttestationsRegistryState,\n  IAttestationsRegistryConfigLogic,\n  OwnableLogic,\n  PausableLogic,\n  InitializableLogic\n{\n  using RangeUtils for Range[];\n  using Bitmap256Bit for uint256;\n  using Bitmap256Bit for uint8;\n\n  /******************************************\n   *\n   *    ATTESTATION REGISTRY WRITE ACCESS MANAGEMENT (ISSUERS)\n   *\n   *****************************************/\n\n  /**\n   * @dev Pauses the registry. Issuers can no longer record or delete attestations\n   */\n  function pause() external override onlyOwner {\n    _pause();\n  }\n\n  /**\n   * @dev Unpauses the registry\n   */\n  function unpause() external override onlyOwner {\n    _unpause();\n  }\n\n  /**\n   * @dev Authorize an issuer for a specific range\n   * @param issuer Issuer that will be authorized\n   * @param firstCollectionId First collection Id of the range for which the issuer will be authorized\n   * @param lastCollectionId Last collection Id of the range for which the issuer will be authorized\n   */\n  function authorizeRange(\n    address issuer,\n    uint256 firstCollectionId,\n    uint256 lastCollectionId\n  ) external override onlyOwner {\n    _authorizeRange(issuer, firstCollectionId, lastCollectionId);\n  }\n\n  /**\n   * @dev Unauthorize an issuer for a specific range\n   * @param issuer Issuer that will be unauthorized\n   * @param rangeIndex Index of the range to be unauthorized\n   * @param firstCollectionId First collection Id of the range for which the issuer will be unauthorized\n   * @param lastCollectionId Last collection Id of the range for which the issuer will be unauthorized\n   */\n  function unauthorizeRange(\n    address issuer,\n    uint256 rangeIndex,\n    uint256 firstCollectionId,\n    uint256 lastCollectionId\n  ) external override onlyOwner {\n    _unauthorizeRange(issuer, rangeIndex, firstCollectionId, lastCollectionId);\n  }\n\n  /**\n   * @dev Authorize an issuer for specific ranges\n   * @param issuer Issuer that will be authorized\n   * @param ranges Ranges for which the issuer will be authorized\n   */\n  function authorizeRanges(address issuer, Range[] memory ranges) external override onlyOwner {\n    for (uint256 i = 0; i < ranges.length; i++) {\n      _authorizeRange(issuer, ranges[i].min, ranges[i].max);\n    }\n  }\n\n  /**\n   * @dev Unauthorize an issuer for specific ranges\n   * @param issuer Issuer that will be unauthorized\n   * @param ranges Ranges for which the issuer will be unauthorized\n   */\n  function unauthorizeRanges(\n    address issuer,\n    Range[] memory ranges,\n    uint256[] memory rangeIndexes\n  ) external override onlyOwner {\n    for (uint256 i = 0; i < rangeIndexes.length; i++) {\n      _unauthorizeRange(issuer, rangeIndexes[i] - i, ranges[i].min, ranges[i].max);\n    }\n  }\n\n  /**\n   * @dev Returns whether a specific issuer is authorized or not to record in a specific attestations collection\n   * @param issuer Issuer to be checked\n   * @param collectionId Collection Id for which the issuer will be checked\n   */\n  function isAuthorized(address issuer, uint256 collectionId) external view returns (bool) {\n    return _isAuthorized(issuer, collectionId);\n  }\n\n  /******************************************\n   *\n   *    ATTRIBUTES CONFIG LOGIC\n   *\n   *****************************************/\n\n  /**\n   * @dev Create a new attribute.\n   * @param index Index of the attribute. Can go from 0 to 63.\n   * @param name Name in bytes32 of the attribute\n   */\n  function createNewAttribute(uint8 index, bytes32 name) public onlyOwner {\n    index._checkIndexIsValid();\n    if (_isAttributeCreated(index)) {\n      revert AttributeAlreadyExists(index);\n    }\n    _createNewAttribute(index, name);\n  }\n\n  function createNewAttributes(uint8[] memory indices, bytes32[] memory names) external onlyOwner {\n    if (indices.length != names.length) {\n      revert ArgsLengthDoesNotMatch();\n    }\n\n    for (uint256 i = 0; i < indices.length; i++) {\n      createNewAttribute(indices[i], names[i]);\n    }\n  }\n\n  /**\n   * @dev Update the name of an existing attribute\n   * @param index Index of the attribute. Can go from 0 to 63. The attribute must exist\n   * @param newName new name in bytes32 of the attribute\n   */\n  function updateAttributeName(uint8 index, bytes32 newName) public onlyOwner {\n    index._checkIndexIsValid();\n    if (!_isAttributeCreated(index)) {\n      revert AttributeDoesNotExist(index);\n    }\n    _updateAttributeName(index, newName);\n  }\n\n  function updateAttributesName(\n    uint8[] memory indices,\n    bytes32[] memory newNames\n  ) external onlyOwner {\n    if (indices.length != newNames.length) {\n      revert ArgsLengthDoesNotMatch();\n    }\n\n    for (uint256 i = 0; i < indices.length; i++) {\n      updateAttributeName(indices[i], newNames[i]);\n    }\n  }\n\n  /**\n   * @dev Delete an existing attribute\n   * @param index Index of the attribute. Can go from 0 to 63. The attribute must already exist\n   */\n  function deleteAttribute(uint8 index) public onlyOwner {\n    index._checkIndexIsValid();\n    if (!_isAttributeCreated(index)) {\n      revert AttributeDoesNotExist(index);\n    }\n    _deleteAttribute(index);\n  }\n\n  function deleteAttributes(uint8[] memory indices) external onlyOwner {\n    for (uint256 i = 0; i < indices.length; i++) {\n      deleteAttribute(indices[i]);\n    }\n  }\n\n  /**\n   * @dev Set a value for an attribute of an attestationsCollection. The attribute should already be created.\n   * @param collectionId Collection Id of the targeted attestationsCollection\n   * @param index Index of the attribute (must be between 0 and 63)\n   * @param value Value of the attribute we want to set for this attestationsCollection. Can take the value 0 to 15\n   */\n  function setAttributeValueForAttestationsCollection(\n    uint256 collectionId,\n    uint8 index,\n    uint8 value\n  ) public onlyOwner {\n    index._checkIndexIsValid();\n\n    if (!_isAttributeCreated(index)) {\n      revert AttributeDoesNotExist(index);\n    }\n\n    _setAttributeForAttestationsCollection(collectionId, index, value);\n  }\n\n  function setAttributesValuesForAttestationsCollections(\n    uint256[] memory collectionIds,\n    uint8[] memory indices,\n    uint8[] memory values\n  ) external onlyOwner {\n    if (collectionIds.length != indices.length || collectionIds.length != values.length) {\n      revert ArgsLengthDoesNotMatch();\n    }\n    for (uint256 i = 0; i < collectionIds.length; i++) {\n      setAttributeValueForAttestationsCollection(collectionIds[i], indices[i], values[i]);\n    }\n  }\n\n  /**\n   * @dev Returns the attribute's value (from 0 to 15) of an attestationsCollection\n   * @param collectionId Collection Id of the targeted attestationsCollection\n   * @param index Index of the attribute. Can go from 0 to 63.\n   */\n  function getAttributeValueForAttestationsCollection(\n    uint256 collectionId,\n    uint8 index\n  ) public view returns (uint8) {\n    uint256 currentAttributesValues = _getAttributesValuesBitmapForAttestationsCollection(\n      collectionId\n    );\n    return currentAttributesValues._get(index);\n  }\n\n  function getAttributesValuesForAttestationsCollection(\n    uint256 collectionId,\n    uint8[] memory indices\n  ) external view returns (uint8[] memory) {\n    uint8[] memory attributesValues = new uint8[](indices.length);\n    for (uint256 i = 0; i < indices.length; i++) {\n      attributesValues[i] = getAttributeValueForAttestationsCollection(collectionId, indices[i]);\n    }\n    return attributesValues;\n  }\n\n  /**\n   * @dev Returns whether an attestationsCollection has a specific attribute referenced by its index\n   * @param collectionId Collection Id of the targeted attestationsCollection\n   * @param index Index of the attribute. Can go from 0 to 63.\n   */\n  function attestationsCollectionHasAttribute(\n    uint256 collectionId,\n    uint8 index\n  ) public view returns (bool) {\n    uint256 currentAttributeValues = _getAttributesValuesBitmapForAttestationsCollection(\n      collectionId\n    );\n    return currentAttributeValues._get(index) > 0;\n  }\n\n  function attestationsCollectionHasAttributes(\n    uint256 collectionId,\n    uint8[] memory indices\n  ) external view returns (bool) {\n    for (uint256 i = 0; i < indices.length; i++) {\n      if (!attestationsCollectionHasAttribute(collectionId, indices[i])) {\n        return false;\n      }\n    }\n    return true;\n  }\n\n  /**\n   * @dev Returns all the enabled attributes names and their values for a specific attestationsCollection\n   * @param collectionId Collection Id of the targeted attestationsCollection\n   */\n  function getAttributesNamesAndValuesForAttestationsCollection(\n    uint256 collectionId\n  ) public view returns (bytes32[] memory, uint8[] memory) {\n    uint256 currentAttributesValues = _getAttributesValuesBitmapForAttestationsCollection(\n      collectionId\n    );\n\n    (\n      uint8[] memory indices,\n      uint8[] memory values,\n      uint8 nbOfNonZeroValues\n    ) = currentAttributesValues._getAllNonZeroValues();\n\n    bytes32[] memory attributesNames = new bytes32[](nbOfNonZeroValues);\n    uint8[] memory attributesValues = new uint8[](nbOfNonZeroValues);\n    for (uint8 i = 0; i < nbOfNonZeroValues; i++) {\n      attributesNames[i] = _attributesNames[indices[i]];\n      attributesValues[i] = values[i];\n    }\n\n    return (attributesNames, attributesValues);\n  }\n\n  /*****************************\n   *\n   *      INTERNAL FUNCTIONS\n   *\n   *****************************/\n\n  function _authorizeRange(\n    address issuer,\n    uint256 firstCollectionId,\n    uint256 lastCollectionId\n  ) internal {\n    Range memory newRange = Range(firstCollectionId, lastCollectionId);\n    _authorizedRanges[issuer].push(newRange);\n    emit IssuerAuthorized(issuer, firstCollectionId, lastCollectionId);\n  }\n\n  function _unauthorizeRange(\n    address issuer,\n    uint256 rangeIndex,\n    uint256 firstCollectionId,\n    uint256 lastCollectionId\n  ) internal onlyOwner {\n    if (rangeIndex >= _authorizedRanges[issuer].length)\n      revert RangeIndexOutOfBounds(issuer, _authorizedRanges[issuer].length, rangeIndex);\n\n    uint256 expectedFirstId = _authorizedRanges[issuer][rangeIndex].min;\n    uint256 expectedLastId = _authorizedRanges[issuer][rangeIndex].max;\n    if (firstCollectionId != expectedFirstId || lastCollectionId != expectedLastId)\n      revert IdsMismatch(\n        issuer,\n        rangeIndex,\n        expectedFirstId,\n        expectedLastId,\n        firstCollectionId,\n        lastCollectionId\n      );\n\n    _authorizedRanges[issuer][rangeIndex] = _authorizedRanges[issuer][\n      _authorizedRanges[issuer].length - 1\n    ];\n    _authorizedRanges[issuer].pop();\n    emit IssuerUnauthorized(issuer, firstCollectionId, lastCollectionId);\n  }\n\n  function _isAuthorized(address issuer, uint256 collectionId) internal view returns (bool) {\n    return _authorizedRanges[issuer]._includes(collectionId);\n  }\n\n  function _setAttributeForAttestationsCollection(\n    uint256 collectionId,\n    uint8 index,\n    uint8 value\n  ) internal {\n    uint256 currentAttributes = _getAttributesValuesBitmapForAttestationsCollection(collectionId);\n\n    _attestationsCollectionAttributesValuesBitmap[collectionId] = currentAttributes._set(\n      index,\n      value\n    );\n\n    emit AttestationsCollectionAttributeSet(collectionId, index, value);\n  }\n\n  function _createNewAttribute(uint8 index, bytes32 name) internal {\n    _attributesNames[index] = name;\n\n    emit NewAttributeCreated(index, name);\n  }\n\n  function _updateAttributeName(uint8 index, bytes32 newName) internal {\n    bytes32 previousName = _attributesNames[index];\n\n    _attributesNames[index] = newName;\n\n    emit AttributeNameUpdated(index, newName, previousName);\n  }\n\n  function _deleteAttribute(uint8 index) internal {\n    bytes32 deletedName = _attributesNames[index];\n\n    delete _attributesNames[index];\n\n    emit AttributeDeleted(index, deletedName);\n  }\n\n  function _getAttributesValuesBitmapForAttestationsCollection(\n    uint256 collectionId\n  ) internal view returns (uint256) {\n    return _attestationsCollectionAttributesValuesBitmap[collectionId];\n  }\n\n  function _isAttributeCreated(uint8 index) internal view returns (bool) {\n    if (_attributesNames[index] == 0) {\n      return false;\n    }\n    return true;\n  }\n}\n"
  },
  {
    "path": "contracts/core/libs/attestations-registry/AttestationsRegistryState.sol",
    "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.14;\n\nimport {Range} from '../utils/RangeLib.sol';\nimport {Attestation, AttestationData} from '../Structs.sol';\n\ncontract AttestationsRegistryState {\n  /*******************************************************\n    Storage layout:\n    19 slots for config\n      4 currently used for _initialized, _initializing, _paused, _owner\n      15 place holders\n    16 slots for logic\n      3 currently used for _authorizedRanges, _attestationsCollectionAttributesValuesBitmap, _attributesNames\n      13 place holders\n    1 slot for _attestationsData \n  *******************************************************/\n\n  // main config\n  // changed `_initialized` from bool to uint8\n  // as we were using OpenZeppelin Contracts (last updated v4.5.0) (proxy/utils/Initializable.sol)\n  // and changed to OpenZeppelin Contracts (last updated v4.8.0) (proxy/utils/Initializable.sol)\n  // PR: https://github.com/sismo-core/sismo-protocol/pull/41\n  uint8 internal _initialized;\n  bool internal _initializing;\n  bool internal _paused;\n  address internal _owner;\n  // keeping some space for future\n  uint256[15] private _placeHoldersAdmin;\n\n  // storing the authorized ranges for each attesters\n  mapping(address => Range[]) internal _authorizedRanges;\n  // Storing the attributes values used for each attestations collection\n  // Each attribute value is an hexadecimal\n  mapping(uint256 => uint256) internal _attestationsCollectionAttributesValuesBitmap;\n  // Storing the attribute name for each attributes index\n  mapping(uint8 => bytes32) internal _attributesNames;\n  // keeping some space for future\n  uint256[13] private _placeHoldersConfig;\n  // storing the data of attestations\n  // =collectionId=> =owner=> attestationData\n  mapping(uint256 => mapping(address => AttestationData)) internal _attestationsData;\n}\n"
  },
  {
    "path": "contracts/core/libs/attestations-registry/InitializableLogic.sol",
    "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.8.0) (proxy/utils/Initializable.sol)\n// Forked from, removed storage, OpenZeppelin Contracts v4.4.1 (access/Ownable.sol)\n\npragma solidity ^0.8.14;\n\nimport '../utils/Address.sol';\nimport './AttestationsRegistryState.sol';\n\n/**\n * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed\n * behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an\n * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer\n * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.\n *\n * The initialization functions use a version number. Once a version number is used, it is consumed and cannot be\n * reused. This mechanism prevents re-execution of each \"step\" but allows the creation of new initialization steps in\n * case an upgrade adds a module that needs to be initialized.\n *\n * For example:\n *\n * [.hljs-theme-light.nopadding]\n * ```\n * contract MyToken is ERC20Upgradeable {\n *     function initialize() initializer public {\n *         __ERC20_init(\"MyToken\", \"MTK\");\n *     }\n * }\n * contract MyTokenV2 is MyToken, ERC20PermitUpgradeable {\n *     function initializeV2() reinitializer(2) public {\n *         __ERC20Permit_init(\"MyToken\");\n *     }\n * }\n * ```\n *\n * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as\n * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.\n *\n * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure\n * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.\n *\n * [CAUTION]\n * ====\n * Avoid leaving a contract uninitialized.\n *\n * An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation\n * contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke\n * the {_disableInitializers} function in the constructor to automatically lock it when it is deployed:\n *\n * [.hljs-theme-light.nopadding]\n * ```\n * /// @custom:oz-upgrades-unsafe-allow constructor\n * constructor() {\n *     _disableInitializers();\n * }\n * ```\n * ====\n */\nabstract contract InitializableLogic is AttestationsRegistryState {\n  // only diff with oz\n  // /**\n  //  * @dev Indicates that the contract has been initialized.\n  //  */\n  // bool private _initialized;\n\n  // /**\n  //  * @dev Indicates that the contract is in the process of being initialized.\n  //  */\n  // bool private _initializing;\n\n  /**\n   * @dev Triggered when the contract has been initialized or reinitialized.\n   */\n  event Initialized(uint8 version);\n\n  /**\n   * @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope,\n   * `onlyInitializing` functions can be used to initialize parent contracts.\n   *\n   * Similar to `reinitializer(1)`, except that functions marked with `initializer` can be nested in the context of a\n   * constructor.\n   *\n   * Emits an {Initialized} event.\n   */\n  modifier initializer() {\n    bool isTopLevelCall = !_initializing;\n    require(\n      (isTopLevelCall && _initialized < 1) ||\n        (!Address.isContract(address(this)) && _initialized == 1),\n      'Initializable: contract is already initialized'\n    );\n    _initialized = 1;\n    if (isTopLevelCall) {\n      _initializing = true;\n    }\n    _;\n    if (isTopLevelCall) {\n      _initializing = false;\n      emit Initialized(1);\n    }\n  }\n\n  /**\n   * @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the\n   * contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be\n   * used to initialize parent contracts.\n   *\n   * A reinitializer may be used after the original initialization step. This is essential to configure modules that\n   * are added through upgrades and that require initialization.\n   *\n   * When `version` is 1, this modifier is similar to `initializer`, except that functions marked with `reinitializer`\n   * cannot be nested. If one is invoked in the context of another, execution will revert.\n   *\n   * Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in\n   * a contract, executing them in the right order is up to the developer or operator.\n   *\n   * WARNING: setting the version to 255 will prevent any future reinitialization.\n   *\n   * Emits an {Initialized} event.\n   */\n  modifier reinitializer(uint8 version) {\n    require(\n      !_initializing && _initialized < version,\n      'Initializable: contract is already initialized'\n    );\n    _initialized = version;\n    _initializing = true;\n    _;\n    _initializing = false;\n    emit Initialized(version);\n  }\n\n  /**\n   * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the\n   * {initializer} and {reinitializer} modifiers, directly or indirectly.\n   */\n  modifier onlyInitializing() {\n    require(_initializing, 'Initializable: contract is not initializing');\n    _;\n  }\n\n  /**\n   * @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call.\n   * Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized\n   * to any version. It is recommended to use this to lock implementation contracts that are designed to be called\n   * through proxies.\n   *\n   * Emits an {Initialized} event the first time it is successfully executed.\n   */\n  function _disableInitializers() internal virtual {\n    require(!_initializing, 'Initializable: contract is initializing');\n    if (_initialized < type(uint8).max) {\n      _initialized = type(uint8).max;\n      emit Initialized(type(uint8).max);\n    }\n  }\n\n  /**\n   * @dev Internal function that returns the initialized version. Returns `_initialized`\n   */\n  function _getInitializedVersion() internal view returns (uint8) {\n    return _initialized;\n  }\n\n  /**\n   * @dev Internal function that returns the initialized version. Returns `_initializing`\n   */\n  function _isInitializing() internal view returns (bool) {\n    return _initializing;\n  }\n}\n"
  },
  {
    "path": "contracts/core/libs/attestations-registry/OwnableLogic.sol",
    "content": "// SPDX-License-Identifier: MIT\n// Forked from, removed storage, OpenZeppelin Contracts v4.4.1 (access/Ownable.sol)\n// OpenZeppelin Contracts v4.4.1 (access/Ownable.sol)\n\npragma solidity ^0.8.14;\n\nimport '../utils/Context.sol';\nimport './AttestationsRegistryState.sol';\n\n/**\n * @dev Contract module which provides a basic access control mechanism, where\n * there is an account (an owner) that can be granted exclusive access to\n * specific functions.\n *\n * By default, the owner account will be the one that deploys the contract. This\n * can later be changed with {transferOwnership}.\n *\n * This module is used through inheritance. It will make available the modifier\n * `onlyOwner`, which can be applied to your functions to restrict their use to\n * the owner.\n */\nabstract contract OwnableLogic is Context, AttestationsRegistryState {\n  event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);\n\n  // This is the only diff with OZ contract\n  // address private _owner;\n\n  /**\n   * @dev Initializes the contract setting the deployer as the initial owner.\n   */\n  constructor() {\n    _transferOwnership(_msgSender());\n  }\n\n  /**\n   * @dev Returns the address of the current owner.\n   */\n  function owner() public view virtual returns (address) {\n    return _owner;\n  }\n\n  /**\n   * @dev Throws if called by any account other than the owner.\n   */\n  modifier onlyOwner() {\n    require(owner() == _msgSender(), 'Ownable: caller is not the owner');\n    _;\n  }\n\n  /**\n   * @dev Leaves the contract without owner. It will not be possible to call\n   * `onlyOwner` functions anymore. Can only be called by the current owner.\n   *\n   * NOTE: Renouncing ownership will leave the contract without an owner,\n   * thereby removing any functionality that is only available to the owner.\n   */\n  function renounceOwnership() public virtual onlyOwner {\n    _transferOwnership(address(0));\n  }\n\n  /**\n   * @dev Transfers ownership of the contract to a new account (`newOwner`).\n   * Can only be called by the current owner.\n   */\n  function transferOwnership(address newOwner) public virtual onlyOwner {\n    require(newOwner != address(0), 'Ownable: new owner is the zero address');\n    _transferOwnership(newOwner);\n  }\n\n  /**\n   * @dev Transfers ownership of the contract to a new account (`newOwner`).\n   * Internal function without access restriction.\n   */\n  function _transferOwnership(address newOwner) internal virtual {\n    address oldOwner = _owner;\n    _owner = newOwner;\n    emit OwnershipTransferred(oldOwner, newOwner);\n  }\n}\n"
  },
  {
    "path": "contracts/core/libs/attestations-registry/PausableLogic.sol",
    "content": "// SPDX-License-Identifier: MIT\n// Forked from, removed storage, OpenZeppelin Contracts v4.4.1 (access/Ownable.sol)\n// OpenZeppelin Contracts v4.4.1 (security/Pausable.sol)\n\npragma solidity ^0.8.14;\n\nimport '../utils/Context.sol';\nimport './AttestationsRegistryState.sol';\n\n/**\n * @dev Contract module which allows children to implement an emergency stop\n * mechanism that can be triggered by an authorized account.\n *\n * This module is used through inheritance. It will make available the\n * modifiers `whenNotPaused` and `whenPaused`, which can be applied to\n * the functions of your contract. Note that they will not be pausable by\n * simply including this module, only once the modifiers are put in place.\n */\nabstract contract PausableLogic is Context, AttestationsRegistryState {\n  /**\n   * @dev Emitted when the pause is triggered by `account`.\n   */\n  event Paused(address account);\n\n  /**\n   * @dev Emitted when the pause is lifted by `account`.\n   */\n  event Unpaused(address account);\n\n  // this is the only diff with OZ contract\n  // bool private _paused;\n\n  /**\n   * @dev Initializes the contract in unpaused state.\n   */\n  constructor() {\n    _paused = false;\n  }\n\n  /**\n   * @dev Returns true if the contract is paused, and false otherwise.\n   */\n  function paused() public view virtual returns (bool) {\n    return _paused;\n  }\n\n  /**\n   * @dev Modifier to make a function callable only when the contract is not paused.\n   *\n   * Requirements:\n   *\n   * - The contract must not be paused.\n   */\n  modifier whenNotPaused() {\n    require(!paused(), 'Pausable: paused');\n    _;\n  }\n\n  /**\n   * @dev Modifier to make a function callable only when the contract is paused.\n   *\n   * Requirements:\n   *\n   * - The contract must be paused.\n   */\n  modifier whenPaused() {\n    require(paused(), 'Pausable: not paused');\n    _;\n  }\n\n  /**\n   * @dev Triggers stopped state.\n   *\n   * Requirements:\n   *\n   * - The contract must not be paused.\n   */\n  function _pause() internal virtual whenNotPaused {\n    _paused = true;\n    emit Paused(_msgSender());\n  }\n\n  /**\n   * @dev Returns to normal state.\n   *\n   * Requirements:\n   *\n   * - The contract must be paused.\n   */\n  function _unpause() internal virtual whenPaused {\n    _paused = false;\n    emit Unpaused(_msgSender());\n  }\n}\n"
  },
  {
    "path": "contracts/core/libs/utils/Address.sol",
    "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.5.0) (utils/Address.sol)\n\npragma solidity ^0.8.1;\n\n/**\n * @dev Collection of functions related to the address type\n */\nlibrary Address {\n  /**\n   * @dev Returns true if `account` is a contract.\n   *\n   * [IMPORTANT]\n   * ====\n   * It is unsafe to assume that an address for which this function returns\n   * false is an externally-owned account (EOA) and not a contract.\n   *\n   * Among others, `isContract` will return false for the following\n   * types of addresses:\n   *\n   *  - an externally-owned account\n   *  - a contract in construction\n   *  - an address where a contract will be created\n   *  - an address where a contract lived, but was destroyed\n   * ====\n   *\n   * [IMPORTANT]\n   * ====\n   * You shouldn't rely on `isContract` to protect against flash loan attacks!\n   *\n   * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets\n   * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract\n   * constructor.\n   * ====\n   */\n  function isContract(address account) internal view returns (bool) {\n    // This method relies on extcodesize/address.code.length, which returns 0\n    // for contracts in construction, since the code is only stored at the end\n    // of the constructor execution.\n\n    return account.code.length > 0;\n  }\n\n  /**\n   * @dev Replacement for Solidity's `transfer`: sends `amount` wei to\n   * `recipient`, forwarding all available gas and reverting on errors.\n   *\n   * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost\n   * of certain opcodes, possibly making contracts go over the 2300 gas limit\n   * imposed by `transfer`, making them unable to receive funds via\n   * `transfer`. {sendValue} removes this limitation.\n   *\n   * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].\n   *\n   * IMPORTANT: because control is transferred to `recipient`, care must be\n   * taken to not create reentrancy vulnerabilities. Consider using\n   * {ReentrancyGuard} or the\n   * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].\n   */\n  function sendValue(address payable recipient, uint256 amount) internal {\n    require(address(this).balance >= amount, 'Address: insufficient balance');\n\n    (bool success, ) = recipient.call{value: amount}('');\n    require(success, 'Address: unable to send value, recipient may have reverted');\n  }\n\n  /**\n   * @dev Performs a Solidity function call using a low level `call`. A\n   * plain `call` is an unsafe replacement for a function call: use this\n   * function instead.\n   *\n   * If `target` reverts with a revert reason, it is bubbled up by this\n   * function (like regular Solidity function calls).\n   *\n   * Returns the raw returned data. To convert to the expected return value,\n   * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].\n   *\n   * Requirements:\n   *\n   * - `target` must be a contract.\n   * - calling `target` with `data` must not revert.\n   *\n   * _Available since v3.1._\n   */\n  function functionCall(address target, bytes memory data) internal returns (bytes memory) {\n    return functionCall(target, data, 'Address: low-level call failed');\n  }\n\n  /**\n   * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with\n   * `errorMessage` as a fallback revert reason when `target` reverts.\n   *\n   * _Available since v3.1._\n   */\n  function functionCall(\n    address target,\n    bytes memory data,\n    string memory errorMessage\n  ) internal returns (bytes memory) {\n    return functionCallWithValue(target, data, 0, errorMessage);\n  }\n\n  /**\n   * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n   * but also transferring `value` wei to `target`.\n   *\n   * Requirements:\n   *\n   * - the calling contract must have an ETH balance of at least `value`.\n   * - the called Solidity function must be `payable`.\n   *\n   * _Available since v3.1._\n   */\n  function functionCallWithValue(\n    address target,\n    bytes memory data,\n    uint256 value\n  ) internal returns (bytes memory) {\n    return functionCallWithValue(target, data, value, 'Address: low-level call with value failed');\n  }\n\n  /**\n   * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but\n   * with `errorMessage` as a fallback revert reason when `target` reverts.\n   *\n   * _Available since v3.1._\n   */\n  function functionCallWithValue(\n    address target,\n    bytes memory data,\n    uint256 value,\n    string memory errorMessage\n  ) internal returns (bytes memory) {\n    require(address(this).balance >= value, 'Address: insufficient balance for call');\n    require(isContract(target), 'Address: call to non-contract');\n\n    (bool success, bytes memory returndata) = target.call{value: value}(data);\n    return verifyCallResult(success, returndata, errorMessage);\n  }\n\n  /**\n   * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n   * but performing a static call.\n   *\n   * _Available since v3.3._\n   */\n  function functionStaticCall(\n    address target,\n    bytes memory data\n  ) internal view returns (bytes memory) {\n    return functionStaticCall(target, data, 'Address: low-level static call failed');\n  }\n\n  /**\n   * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n   * but performing a static call.\n   *\n   * _Available since v3.3._\n   */\n  function functionStaticCall(\n    address target,\n    bytes memory data,\n    string memory errorMessage\n  ) internal view returns (bytes memory) {\n    require(isContract(target), 'Address: static call to non-contract');\n\n    (bool success, bytes memory returndata) = target.staticcall(data);\n    return verifyCallResult(success, returndata, errorMessage);\n  }\n\n  /**\n   * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n   * but performing a delegate call.\n   *\n   * _Available since v3.4._\n   */\n  function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {\n    return functionDelegateCall(target, data, 'Address: low-level delegate call failed');\n  }\n\n  /**\n   * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n   * but performing a delegate call.\n   *\n   * _Available since v3.4._\n   */\n  function functionDelegateCall(\n    address target,\n    bytes memory data,\n    string memory errorMessage\n  ) internal returns (bytes memory) {\n    require(isContract(target), 'Address: delegate call to non-contract');\n\n    (bool success, bytes memory returndata) = target.delegatecall(data);\n    return verifyCallResult(success, returndata, errorMessage);\n  }\n\n  /**\n   * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the\n   * revert reason using the provided one.\n   *\n   * _Available since v4.3._\n   */\n  function verifyCallResult(\n    bool success,\n    bytes memory returndata,\n    string memory errorMessage\n  ) internal pure returns (bytes memory) {\n    if (success) {\n      return returndata;\n    } else {\n      // Look for revert reason and bubble it up if present\n      if (returndata.length > 0) {\n        // The easiest way to bubble the revert reason is using memory via assembly\n\n        assembly {\n          let returndata_size := mload(returndata)\n          revert(add(32, returndata), returndata_size)\n        }\n      } else {\n        revert(errorMessage);\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "contracts/core/libs/utils/Bitmap256Bit.sol",
    "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.14;\n\n/*\n * The 256-bit bitmap is structured in 64 chuncks of 4 bits each.\n * The 4 bits can encode any value from 0 to 15.\n\n    chunck63            chunck2      chunck1      chunck0\n    bits                bits         bits         bits \n   ┌────────────┐      ┌────────────┬────────────┬────────────┐\n   │ 1  1  1  1 │ .... │ 1  0  1  1 │ 0  0  0  0 │ 0  0  0  1 │\n   └────────────┘      └────────────┴────────────┴────────────┘\n      value 15            value 11     value 0      value 1\n\n  * A chunck index must be between 0 and 63.\n  * A value must be between 0 and 15.\n **/\n\nlibrary Bitmap256Bit {\n  uint256 constant MAX_INT = 2 ** 256 - 1;\n\n  error IndexOutOfBounds(uint8 index);\n  error ValueOutOfBounds(uint8 value);\n\n  /**\n   * @dev Return the value at a given index of a 256-bit bitmap\n   * @param index index where the value can be found. Can be between 0 and 63\n   */\n  function _get(uint256 self, uint8 index) internal pure returns (uint8) {\n    uint256 currentValues = self;\n    // Get the encoded 4-bit value by right shifting to the `index` position\n    uint256 shifted = currentValues >> (4 * index);\n    // Get the value by only masking the last 4 bits with an AND operator\n    return uint8(shifted & (2 ** 4 - 1));\n  }\n\n  /**\n   * @dev Set a value at a chosen index in a 256-bit bitmap\n   * @param index index where the value will be stored. Can be between 0 and 63\n   * @param value value to store. Can be between 0 and 15\n   */\n  function _set(uint256 self, uint8 index, uint8 value) internal pure returns (uint256) {\n    _checkIndexIsValid(index);\n    _checkValueIsValid(value);\n\n    uint256 currentValues = self;\n    // 1. first we need to remove the current value for the inputed `index`\n    // Left Shift 4 bits mask (1111 mask) to the `index` position\n    uint256 mask = (2 ** 4 - 1) << (4 * index);\n    // Apply a XOR operation to obtain a mask with all bits set to 1 except the 4 bits that we want to remove\n    uint256 negativeMask = MAX_INT ^ mask;\n    // Apply a AND operation between the current values and the negative mask to remove the wanted bits\n    uint256 newValues = currentValues & negativeMask;\n\n    // 2. We set the new value wanted at the `index` position\n    // Create the 4 bits encoding the new value and left shift them to the `index` position\n    uint256 newValueMask = uint256(value) << (4 * index);\n    // Apply an OR operation between the current values and the newValueMask to reference new value\n    return newValues | newValueMask;\n  }\n\n  /**\n   * @dev Get all the non-zero values in a 256-bit bitmap\n   * @param self a 256-bit bitmap\n   */\n  function _getAllNonZeroValues(\n    uint256 self\n  ) internal pure returns (uint8[] memory, uint8[] memory, uint8) {\n    uint8[] memory indices = new uint8[](64);\n    uint8[] memory values = new uint8[](64);\n    uint8 nbOfNonZeroValues = 0;\n    for (uint8 i = 0; i < 63; i++) {\n      uint8 value = _get(self, i);\n      if (value > 0) {\n        indices[nbOfNonZeroValues] = i;\n        values[nbOfNonZeroValues] = value;\n        nbOfNonZeroValues++;\n      }\n    }\n    return (indices, values, nbOfNonZeroValues);\n  }\n\n  /**\n   * @dev Check if the index is valid (is between 0 and 63)\n   * @param index index of a chunck\n   */\n  function _checkIndexIsValid(uint8 index) internal pure {\n    if (index > 63) {\n      revert IndexOutOfBounds(index);\n    }\n  }\n\n  /**\n   * @dev Check if the value is valid (is between 0 and 15)\n   * @param value value to encode in a chunck\n   */\n  function _checkValueIsValid(uint8 value) internal pure {\n    if (value > 15) {\n      revert ValueOutOfBounds(value);\n    }\n  }\n}\n"
  },
  {
    "path": "contracts/core/libs/utils/Context.sol",
    "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)\n\npragma solidity ^0.8.14;\n\n/**\n * @dev Provides information about the current execution context, including the\n * sender of the transaction and its data. While these are generally available\n * via msg.sender and msg.data, they should not be accessed in such a direct\n * manner, since when dealing with meta-transactions the account sending and\n * paying for execution may not be the actual sender (as far as an application\n * is concerned).\n *\n * This contract is only required for intermediate, library-like contracts.\n */\nabstract contract Context {\n  function _msgSender() internal view virtual returns (address) {\n    return msg.sender;\n  }\n\n  function _msgData() internal view virtual returns (bytes calldata) {\n    return msg.data;\n  }\n}\n"
  },
  {
    "path": "contracts/core/libs/utils/RangeLib.sol",
    "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.14;\n\nstruct Range {\n  uint256 min;\n  uint256 max;\n}\n\n// Range [0;3] includees 0 and 3\nlibrary RangeUtils {\n  function _includes(Range[] storage ranges, uint256 collectionId) internal view returns (bool) {\n    for (uint256 i = 0; i < ranges.length; i++) {\n      if (collectionId >= ranges[i].min && collectionId <= ranges[i].max) {\n        return true;\n      }\n    }\n    return false;\n  }\n}\n"
  },
  {
    "path": "contracts/core/utils/AddressesProvider.sol",
    "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.14;\n\nimport {Ownable} from '@openzeppelin/contracts/access/Ownable.sol';\nimport {Initializable} from '@openzeppelin/contracts/proxy/utils/Initializable.sol';\n\nimport {IAddressesProvider} from './interfaces/IAddressesProvider.sol';\n\n// import core contracts\nimport {Badges} from '../Badges.sol';\nimport {AttestationsRegistry} from '../AttestationsRegistry.sol';\nimport {Front} from '../Front.sol';\nimport {HydraS1AccountboundAttester} from '../../attesters/hydra-s1/HydraS1AccountboundAttester.sol';\nimport {AvailableRootsRegistry} from '../../periphery/utils/AvailableRootsRegistry.sol';\nimport {CommitmentMapperRegistry} from '../../periphery/utils/CommitmentMapperRegistry.sol';\nimport {HydraS1Verifier} from '@sismo-core/hydra-s1/contracts/HydraS1Verifier.sol';\n\ncontract AddressesProvider is IAddressesProvider, Initializable, Ownable {\n  uint8 public constant IMPLEMENTATION_VERSION = 2;\n\n  Badges public immutable BADGES;\n  AttestationsRegistry public immutable ATTESTATIONS_REGISTRY;\n  Front public immutable FRONT;\n  HydraS1AccountboundAttester public immutable HYDRA_S1_ACCOUNTBOUND_ATTESTER;\n  AvailableRootsRegistry public immutable AVAILABLE_ROOTS_REGISTRY;\n  CommitmentMapperRegistry public immutable COMMITMENT_MAPPER_REGISTRY;\n  HydraS1Verifier public immutable HYDRA_S1_VERIFIER;\n\n  mapping(bytes32 => address) private _contractAddresses;\n  string[] private _contractNames;\n\n  event ContractAddressSet(address contractAddress, string contractName);\n\n  constructor(\n    address badgesAddress,\n    address attestationsRegistryAddress,\n    address frontAddress,\n    address hydraS1AccountboundAttesterAddress,\n    address availableRootsRegistryAddress,\n    address commitmentMapperRegistryAddress,\n    address hydraS1VerifierAddress,\n    address ownerAddress\n  ) {\n    BADGES = Badges(badgesAddress);\n    ATTESTATIONS_REGISTRY = AttestationsRegistry(attestationsRegistryAddress);\n    FRONT = Front(frontAddress);\n    HYDRA_S1_ACCOUNTBOUND_ATTESTER = HydraS1AccountboundAttester(\n      hydraS1AccountboundAttesterAddress\n    );\n    AVAILABLE_ROOTS_REGISTRY = AvailableRootsRegistry(availableRootsRegistryAddress);\n    COMMITMENT_MAPPER_REGISTRY = CommitmentMapperRegistry(commitmentMapperRegistryAddress);\n    HYDRA_S1_VERIFIER = HydraS1Verifier(hydraS1VerifierAddress);\n\n    initialize(ownerAddress);\n  }\n\n  function initialize(address ownerAddress) public reinitializer(IMPLEMENTATION_VERSION) {\n    // if proxy did not setup owner yet or if called by constructor (for implem setup)\n    if (owner() == address(0) || address(this).code.length == 0) {\n      _transferOwnership(ownerAddress);\n      _set(address(BADGES), 'Badges');\n      _set(address(ATTESTATIONS_REGISTRY), 'AttestationsRegistry');\n      _set(address(FRONT), 'Front');\n      _set(address(HYDRA_S1_ACCOUNTBOUND_ATTESTER), 'HydraS1AccountboundAttester');\n      _set(address(AVAILABLE_ROOTS_REGISTRY), 'AvailableRootsRegistry');\n      _set(address(COMMITMENT_MAPPER_REGISTRY), 'CommitmentMapperRegistry');\n      _set(address(HYDRA_S1_VERIFIER), 'HydraS1Verifier');\n    }\n  }\n\n  /**\n   * @dev Sets the address of a contract.\n   * @param contractAddress Address of the contract.\n   * @param contractName Name of the contract.\n   */\n  function set(address contractAddress, string memory contractName) public onlyOwner {\n    _set(contractAddress, contractName);\n  }\n\n  /**\n   * @dev Sets the address of multiple contracts.\n   * @param contractAddresses Addresses of the contracts.\n   * @param contractNames Names of the contracts.\n   */\n  function setBatch(\n    address[] calldata contractAddresses,\n    string[] calldata contractNames\n  ) external onlyOwner {\n    for (uint256 i = 0; i < contractAddresses.length; i++) {\n      _set(contractAddresses[i], contractNames[i]);\n    }\n  }\n\n  /**\n   * @dev Returns the address of a contract.\n   * @param contractName Name of the contract (string).\n   * @return Address of the contract.\n   */\n  function get(string memory contractName) public view returns (address) {\n    bytes32 contractNameHash = keccak256(abi.encodePacked(contractName));\n\n    return _contractAddresses[contractNameHash];\n  }\n\n  /**\n   * @dev Returns the address of a contract.\n   * @param contractNameHash Hash of the name of the contract (bytes32).\n   * @return Address of the contract.\n   */\n  function get(bytes32 contractNameHash) public view returns (address) {\n    return _contractAddresses[contractNameHash];\n  }\n\n  /**\n   * @dev Returns the addresses of all contracts inputed.\n   * @param contractNames Names of the contracts as strings.\n   */\n  function getBatch(string[] calldata contractNames) external view returns (address[] memory) {\n    address[] memory contractAddresses = new address[](contractNames.length);\n\n    for (uint256 i = 0; i < contractNames.length; i++) {\n      contractAddresses[i] = get(contractNames[i]);\n    }\n\n    return contractAddresses;\n  }\n\n  /**\n   * @dev Returns the addresses of all contracts inputed.\n   * @param contractNamesHash Names of the contracts as bytes32.\n   */\n  function getBatch(bytes32[] calldata contractNamesHash) external view returns (address[] memory) {\n    address[] memory contractAddresses = new address[](contractNamesHash.length);\n\n    for (uint256 i = 0; i < contractNamesHash.length; i++) {\n      contractAddresses[i] = get(contractNamesHash[i]);\n    }\n\n    return contractAddresses;\n  }\n\n  /**\n   * @dev Returns the addresses of all contracts in `_contractNames`\n   * @return Names, Hashed Names and Addresses of all contracts.\n   */\n  function getAll() external view returns (string[] memory, bytes32[] memory, address[] memory) {\n    string[] memory contractNames = _contractNames;\n    bytes32[] memory contractNamesHash = new bytes32[](contractNames.length);\n    address[] memory contractAddresses = new address[](contractNames.length);\n\n    for (uint256 i = 0; i < contractNames.length; i++) {\n      contractAddresses[i] = get(contractNames[i]);\n      contractNamesHash[i] = keccak256(abi.encodePacked(contractNames[i]));\n    }\n\n    return (contractNames, contractNamesHash, contractAddresses);\n  }\n\n  /**\n   * @dev Sets the address of a contract.\n   * @param contractAddress Address of the contract.\n   * @param contractName Name of the contract.\n   */\n  function _set(address contractAddress, string memory contractName) internal {\n    bytes32 contractNameHash = keccak256(abi.encodePacked(contractName));\n\n    if (_contractAddresses[contractNameHash] == address(0)) {\n      _contractNames.push(contractName);\n    }\n\n    _contractAddresses[contractNameHash] = contractAddress;\n\n    emit ContractAddressSet(contractAddress, contractName);\n  }\n}\n"
  },
  {
    "path": "contracts/core/utils/interfaces/IAddressesProvider.sol",
    "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.14;\n\ninterface IAddressesProvider {\n  /**\n   * @dev Sets the address of a contract.\n   * @param contractAddress Address of the contract.\n   * @param contractName Name of the contract.\n   */\n  function set(address contractAddress, string memory contractName) external;\n\n  /**\n   * @dev Sets the address of multiple contracts.\n   * @param contractAddresses Addresses of the contracts.\n   * @param contractNames Names of the contracts.\n   */\n  function setBatch(address[] calldata contractAddresses, string[] calldata contractNames) external;\n\n  /**\n   * @dev Returns the address of a contract.\n   * @param contractName Name of the contract (string).\n   * @return Address of the contract.\n   */\n  function get(string memory contractName) external view returns (address);\n\n  /**\n   * @dev Returns the address of a contract.\n   * @param contractNameHash Hash of the name of the contract (bytes32).\n   * @return Address of the contract.\n   */\n  function get(bytes32 contractNameHash) external view returns (address);\n\n  /**\n   * @dev Returns the addresses of all contracts inputed.\n   * @param contractNames Names of the contracts as strings.\n   */\n  function getBatch(string[] calldata contractNames) external view returns (address[] memory);\n\n  /**\n   * @dev Returns the addresses of all contracts inputed.\n   * @param contractNamesHash Names of the contracts as strings.\n   */\n  function getBatch(bytes32[] calldata contractNamesHash) external view returns (address[] memory);\n\n  /**\n   * @dev Returns the addresses of all contracts in `_contractNames`\n   * @return Names, Hashed Names and Addresses of all contracts.\n   */\n  function getAll() external view returns (string[] memory, bytes32[] memory, address[] memory);\n}\n"
  },
  {
    "path": "contracts/libs/SismoLib.sol",
    "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.14;\n\n/**\n * @title SismoLib\n * @author Sismo\n * @notice This is the Sismo Library of the Sismo protocol\n * It is designed to be the only contract that needs to be imported to integrate Sismo in a smart contract.\n * Its aim is to provide a set of sub-libraries with high-level functions to interact with the Sismo protocol easily.\n */\n\nimport './using-sismo/UsingSismo.sol';\n"
  },
  {
    "path": "contracts/libs/using-sismo/UsingSismo.sol",
    "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.14;\n\nimport {AddressesProvider} from '../../core/utils/AddressesProvider.sol';\nimport {Badges} from '../../core/Badges.sol';\nimport {Attester} from '../../core/Attester.sol';\nimport {HydraS1AccountboundAttester} from '../../attesters/hydra-s1/HydraS1AccountboundAttester.sol';\nimport {Context} from '@openzeppelin/contracts/utils/Context.sol';\nimport {Request, Claim, Attestation} from '../../core/libs/Structs.sol';\n\nenum BalanceRequirementType {\n  gte,\n  lte,\n  equal\n}\n\n/**\n * @title UsingSismo\n * @author Sismo\n * @notice This contract is intended to be used by other contracts that need to mint Sismo Badges from Sismo proofs or check if an user holds certain Sismo Badges thanks\n * to high level functions such as `_mintSismoBadge` and `_requireBadges`.\n *\n * This contract can be viewed as a library that goes by pair with the \"Prove With Sismo\" off-chain flow. The \"Prove With Sismo\" off-chain flow is a flow that allows\n * users to prove that they are eligible to a specific Sismo Badge and mint it on-chain thanks to a Sismo proof and a request.\n * There are two different \"Prove With Sismo\" flows for on-chain gating:\n *\n * 2-txs flow: #1(tx) user is directed to Sismo and mints the Badge.\n *             #2(tx) app smart contract uses the onlyBadgeOwner modifier\n *                                            or _requireBadges check function\n *                    to gate their function to Badge holders only\n * 1-tx flow:  #1(off-chain) user is directed to Sismo to create a Sismo Proof of Badge eligibility\n *             #(tx): app smart contract uses the _mintSismoBadge function that forwards the proof to the Attester\n *                    The Attester checks the proof (and mints the Sismo Badge).\n *\n */\n\ncontract UsingSismo is Context {\n  uint256 public constant SISMO_LIB_VERSION = 1;\n\n  AddressesProvider public immutable ADDRESSES_PROVIDER =\n    AddressesProvider(0x3340Ac0CaFB3ae34dDD53dba0d7344C1Cf3EFE05);\n\n  HydraS1AccountboundAttester public immutable HYDRA_S1_ACCOUNTBOUND_ATTESTER;\n  Badges public immutable BADGES;\n\n  error UserDoesNotMeetAllRequirements(uint256 badgeTokenId, uint256 minBalance);\n  error UserDoesNotMeetRequirements();\n  error InvalidArgumentsLength();\n  error WrongBalanceRequirementType();\n\n  constructor() {\n    string[] memory contractNames = new string[](2);\n    contractNames[0] = 'HydraS1AccountboundAttester';\n    contractNames[1] = 'Badges';\n\n    address[] memory contractAddresses = ADDRESSES_PROVIDER.getBatch(contractNames);\n\n    HYDRA_S1_ACCOUNTBOUND_ATTESTER = HydraS1AccountboundAttester(contractAddresses[0]);\n    BADGES = Badges(contractAddresses[1]);\n  }\n\n  /*******************************************************\n    1. MINT BADGES from sismoProof received by \"Prove With Sismo\" off-chain flow\n  *******************************************************/\n\n  /**\n   * @dev Mint badge from sismoProof received by \"Prove With Sismo\" off-chain flow\n   * @param request user request\n   * @param sismoProof Sismo proof\n   * @param attester Attester address (default = HYDRA_S1_ACCOUNTBOUND_ATTESTER, see the next function)\n   * @return Address of the owner of the badges, tokenIds of the badges minted and levels of the badges minted\n   * @notice This function will use the default attester (HYDRA_S1_ACCOUNTBOUND_ATTESTER) if no attester is specified.\n   */\n  function _mintSismoBadge(\n    Request memory request,\n    bytes memory sismoProof,\n    address attester // default == HYDRA_S1_ACCOUNTBOUND_ATTESTER, see next function.\n  ) public returns (address, uint256, uint256) {\n    (address owner, uint256[] memory badgesTokenIds, uint256[] memory badgesLevels) = Attester(\n      attester\n    ).mintBadges(request, sismoProof);\n    return (owner, badgesTokenIds[0], badgesLevels[0]);\n  }\n\n  /**\n   * @dev Mint badge from sismoProof received by \"Prove With Sismo\" off-chain flow (DEFAULT ATTESTER USAGE)\n   * @param request user request\n   * @param sismoProof Sismo proof\n   * @return Address of the owner of the badges, tokenIds of the badges minted and levels of the badges minted\n   */\n  function _mintSismoBadge(\n    Request memory request,\n    bytes memory sismoProof\n  ) internal returns (address, uint256, uint256) {\n    (\n      address owner,\n      uint256[] memory badgesTokenIds,\n      uint256[] memory badgesLevels\n    ) = HYDRA_S1_ACCOUNTBOUND_ATTESTER.mintBadges(request, sismoProof);\n\n    return (owner, badgesTokenIds[0], badgesLevels[0]);\n  }\n\n  /**\n   * @dev Mint badges from sismoProof received by \"Prove With Sismo\" off-chain flow\n   * @param request user request\n   * @param sismoProof Sismo proof\n   * @param attester Attester address (default = HYDRA_S1_ACCOUNTBOUND_ATTESTER, see the next function)\n   * @return Address of the owner of the badges, tokenIds of the badges minted and levels of the badges minted\n   * @notice This function will use the default attester (HYDRA_S1_ACCOUNTBOUND_ATTESTER) if no attester is specified.\n   */\n  function _mintSismoBadges(\n    Request memory request,\n    bytes memory sismoProof,\n    address attester // default == HYDRA_S1_ACCOUNTBOUND_ATTESTER, see next function.\n  ) public returns (address, uint256[] memory, uint256[] memory) {\n    (address owner, uint256[] memory badgesTokenIds, uint256[] memory badgesLevels) = Attester(\n      attester\n    ).mintBadges(request, sismoProof);\n    return (owner, badgesTokenIds, badgesLevels);\n  }\n\n  /**\n   * @dev Mint badges from sismoProof received by \"Prove With Sismo\" off-chain flow (DEFAULT ATTESTER USAGE)\n   * @param request user request\n   * @param sismoProof Sismo proof\n   * @return Address of the owner of the badges, tokenIds of the badges minted and levels of the badges minted\n   */\n  function _mintSismoBadges(\n    Request memory request,\n    bytes memory sismoProof\n  ) internal returns (address, uint256[] memory, uint256[] memory) {\n    (\n      address owner,\n      uint256[] memory badgesTokenIds,\n      uint256[] memory badgesLevels\n    ) = HYDRA_S1_ACCOUNTBOUND_ATTESTER.mintBadges(request, sismoProof);\n\n    return (owner, badgesTokenIds, badgesLevels);\n  }\n\n  /*******************************************************\n    2. CORE BADGE REQUIREMENTS FUNCTIONS \n    (with default values, see section 4)\n  *******************************************************/\n\n  /**\n   * @dev Check if the user has the required badges\n   * @param account User address\n   * @param badgeTokenIds TokenIds of the required badges\n   * @param isEachBadgeRequired If true, the user must have all the badges required. If false, the user must have at least one of the badges required. Default = false (see section 4).\n   * @param requiredBalances Required balances of the required badges. Default = [1, .., 1] (see section 4).\n   * @param balanceRequirementType Balance requirement type. Default = gte (see section 4).\n   */\n  function _requireBadges(\n    address account,\n    uint256[] memory badgeTokenIds,\n    bool isEachBadgeRequired, // default = false, see Section 4.\n    uint256[] memory requiredBalances, // default = [1, .., 1], see Section 4.\n    BalanceRequirementType balanceRequirementType // default = [gte], see Section 4.\n  ) internal view {\n    _checkArgumentsLength(badgeTokenIds, requiredBalances);\n\n    bool atLeastOneRequirementOk = false;\n\n    for (uint32 i = 0; i < badgeTokenIds.length; i++) {\n      uint256 balance = BADGES.balanceOf(account, badgeTokenIds[i]);\n      uint256 requiredBalance = requiredBalances[i];\n      if (_isBalanceRequirementOk(balance, requiredBalance, balanceRequirementType) == true) {\n        atLeastOneRequirementOk = true;\n      } else {\n        if (isEachBadgeRequired) {\n          revert UserDoesNotMeetAllRequirements(badgeTokenIds[i], requiredBalance);\n        }\n      }\n    }\n    if (!atLeastOneRequirementOk) {\n      revert UserDoesNotMeetRequirements();\n    }\n  }\n\n  function _requireBadge(\n    address account,\n    uint256 badgeTokenId,\n    uint256 requiredBalance, // default = 1, see Section 3.\n    BalanceRequirementType balanceRequirementType // default = gte, see Section 3.\n  ) internal view {\n    uint256[] memory badgeTokenIds = new uint256[](1);\n    badgeTokenIds[0] = badgeTokenId;\n\n    uint256[] memory requiredBalances = new uint256[](1);\n    requiredBalances[0] = requiredBalance;\n    _requireBadges(account, badgeTokenIds, false, requiredBalances, balanceRequirementType);\n  }\n\n  /*******************************************************\n    3. MODIFIERS\n  *******************************************************/\n\n  /**\n   * @dev Modifier to check if msg.sender has the required badge\n   * @param badgeTokenId TokenId of the badge required\n   */\n  modifier onlyBadgeHolders(uint256 badgeTokenId) {\n    _requireBadge(_msgSender(), badgeTokenId);\n    _;\n  }\n\n  /**\n   * @dev Modifier to check if msg.sender has the required badges\n   * @param badgeTokenIds TokenIds of the badges required\n   * @param isEachBadgeRequired If true, the user must have all the badges required. If false, the user must have at least one of the badges required. Default = false (see section 4).\n   */\n  modifier onlyBadgesHolders(uint256[] memory badgeTokenIds, bool isEachBadgeRequired) {\n    _requireBadges(_msgSender(), badgeTokenIds, isEachBadgeRequired);\n    _;\n  }\n\n  modifier onlyBadgeHoldersWithGreaterBalance(uint256 badgeTokenId, uint256 minBalance) {\n    _requireBadge(_msgSender(), badgeTokenId, minBalance);\n    _;\n  }\n  modifier onlyBadgeHoldersWithLowerBalance(uint256 badgeTokenId, uint256 maxBalance) {\n    _requireBadge(_msgSender(), badgeTokenId, maxBalance, BalanceRequirementType.lte);\n    _;\n  }\n\n  modifier onlyBadgeHoldersWithExactBalance(uint256 badgeTokenId, uint256 exactBalance) {\n    _requireBadge(_msgSender(), badgeTokenId, exactBalance, BalanceRequirementType.equal);\n    _;\n  }\n\n  modifier onlyBadgesHoldersWithGreaterBalance(\n    uint256[] memory badgeTokenIds,\n    uint256[] memory minBalances,\n    bool isEachBadgeRequired\n  ) {\n    _requireBadges(_msgSender(), badgeTokenIds, isEachBadgeRequired, minBalances);\n    _;\n  }\n\n  modifier onlyBadgesHoldersWithLowerBalance(\n    uint256[] memory badgeTokenIds,\n    uint256[] memory maxBalances,\n    bool isEachBadgeRequired\n  ) {\n    _requireBadges(\n      _msgSender(),\n      badgeTokenIds,\n      isEachBadgeRequired,\n      maxBalances,\n      BalanceRequirementType.lte\n    );\n    _;\n  }\n\n  modifier onlyBadgesHoldersWithExactBalance(\n    uint256[] memory badgeTokenIds,\n    uint256[] memory exactBalances,\n    bool isEachBadgeRequired\n  ) {\n    _requireBadges(\n      _msgSender(),\n      badgeTokenIds,\n      isEachBadgeRequired,\n      exactBalances,\n      BalanceRequirementType.equal\n    );\n    _;\n  }\n\n  /*******************************************************\n    4. CORE FUNCTIONS WITH DEFAULT VALUES HARD CODED (SAME NAME, DIFFERENT SIGNATURES)\n  *******************************************************/\n\n  function _requireBadges(\n    address account,\n    uint256[] memory badgeTokenIds,\n    bool isEachBadgeRequired,\n    uint256[] memory minBalances\n  ) internal view {\n    _requireBadges(\n      account,\n      badgeTokenIds,\n      isEachBadgeRequired,\n      minBalances,\n      BalanceRequirementType.gte\n    );\n  }\n\n  function _requireBadges(\n    address account,\n    uint256[] memory badgeTokenIds,\n    bool isEachBadgeRequired\n  ) internal view {\n    uint256[] memory arrayOfOnes = new uint256[](badgeTokenIds.length);\n    // by default, we require at least a balance of 1 for each badge\n    for (uint32 i = 0; i < badgeTokenIds.length; i++) {\n      arrayOfOnes[i] = 1;\n    }\n    _requireBadges(account, badgeTokenIds, isEachBadgeRequired, arrayOfOnes);\n  }\n\n  function _requireBadge(\n    address account,\n    uint256 badgeTokenId,\n    uint256 requiredBalance\n  ) internal view {\n    uint256[] memory badgeTokenIds = new uint256[](1);\n    badgeTokenIds[0] = badgeTokenId;\n\n    uint256[] memory requiredBalances = new uint256[](1);\n    requiredBalances[0] = requiredBalance;\n\n    _requireBadges(account, badgeTokenIds, false, requiredBalances, BalanceRequirementType.gte);\n  }\n\n  function _requireBadges(address account, uint256[] memory badgeTokenIds) internal view {\n    _requireBadges(account, badgeTokenIds, false);\n  }\n\n  function _requireBadge(address account, uint256 badgeTokenId) internal view {\n    uint256[] memory badgeTokenIds = new uint256[](1);\n    badgeTokenIds[0] = badgeTokenId;\n    _requireBadges(account, badgeTokenIds);\n  }\n\n  /**\n   * @dev Check if the arguments have the same length\n   * @param badgeTokenIds Token ID of the badges\n   * @param minBalances Minimum balances (= levels) of the badges\n   */\n  function _checkArgumentsLength(\n    uint256[] memory badgeTokenIds,\n    uint256[] memory minBalances\n  ) internal pure {\n    if (badgeTokenIds.length != minBalances.length) {\n      revert InvalidArgumentsLength();\n    }\n  }\n\n  /**\n   * @dev Check if the balance of the badge meets the requirement\n   * @param balance Balance of the badge\n   * @param requiredBalance Required balance of the badge\n   * @param balanceRequirementType Type of the balance requirement (gte, lte, equal)\n   */\n  function _isBalanceRequirementOk(\n    uint256 balance,\n    uint256 requiredBalance,\n    BalanceRequirementType balanceRequirementType\n  ) internal pure returns (bool) {\n    if (balanceRequirementType == BalanceRequirementType.gte) {\n      return balance >= requiredBalance;\n    } else if (balanceRequirementType == BalanceRequirementType.lte) {\n      return balance <= requiredBalance && balance > 0;\n    } else if (balanceRequirementType == BalanceRequirementType.equal) {\n      return balance == requiredBalance;\n    } else {\n      revert WrongBalanceRequirementType();\n    }\n  }\n}\n"
  },
  {
    "path": "contracts/periphery/utils/AvailableRootsRegistry.sol",
    "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.14;\n\nimport {Ownable} from '@openzeppelin/contracts/access/Ownable.sol';\nimport {IAvailableRootsRegistry} from './interfaces/IAvailableRootsRegistry.sol';\nimport {Initializable} from '@openzeppelin/contracts/proxy/utils/Initializable.sol';\n\n/**\n * @title Attesters Groups Registry\n * @author Sismo\n * @notice This contract stores that data required by attesters to be available so they can verify user claims\n * This contract is deployed behind a proxy and this implementation is focused on storing merkle roots\n * For more information: https://available-roots-registry.docs.sismo.io\n *\n **/\ncontract AvailableRootsRegistry is IAvailableRootsRegistry, Initializable, Ownable {\n  uint8 public constant IMPLEMENTATION_VERSION = 2;\n\n  mapping(address => mapping(uint256 => bool)) public _roots;\n\n  /**\n   * @dev Constructor\n   * @param owner Owner of the contract, can register/ unregister roots\n   */\n  constructor(address owner) {\n    initialize(owner);\n  }\n\n  /**\n   * @dev Initializes the contract, to be called by the proxy delegating calls to this implementation\n   * @param ownerAddress Owner of the contract, can update public key and address\n   * @notice The reinitializer modifier is needed to configure modules that are added through upgrades and that require initialization.\n   */\n  function initialize(address ownerAddress) public reinitializer(IMPLEMENTATION_VERSION) {\n    // if proxy did not setup owner yet or if called by constructor (for implem setup)\n    if (owner() == address(0) || address(this).code.length == 0) {\n      _transferOwnership(ownerAddress);\n    }\n  }\n\n  /**\n   * @dev Register a root available for an attester\n   * @param attester Attester which will have the root available\n   * @param root Root to register\n   */\n  function registerRootForAttester(address attester, uint256 root) external onlyOwner {\n    if (attester == address(0)) revert CannotRegisterForZeroAddress();\n    _registerRootForAttester(attester, root);\n  }\n\n  /**\n   * @dev Unregister a root for an attester\n   * @param attester Attester which will no longer have the root available\n   * @param root Root to unregister\n   */\n  function unregisterRootForAttester(address attester, uint256 root) external onlyOwner {\n    if (attester == address(0)) revert CannotUnregisterForZeroAddress();\n    _unregisterRootForAttester(attester, root);\n  }\n\n  /**\n   * @dev Registers a root, available for all contracts\n   * @param root Root to register\n   */\n  function registerRootForAll(uint256 root) external onlyOwner {\n    _registerRootForAttester(address(0), root);\n  }\n\n  /**\n   * @dev Unregister a root, available for all contracts\n   * @param root Root to unregister\n   */\n  function unregisterRootForAll(uint256 root) external onlyOwner {\n    _unregisterRootForAttester(address(0), root);\n  }\n\n  /**\n   * @dev returns whether a root is available for a caller (msg.sender)\n   * @param root root to check whether it is registered for me or not\n   */\n  function isRootAvailableForMe(uint256 root) external view returns (bool) {\n    return _roots[_msgSender()][root] || _roots[address(0)][root];\n  }\n\n  /**\n   * @dev Initializes the contract, to be called by the proxy delegating calls to this implementation\n   * @param attester Owner of the contract, can update public key and address\n   * @param root Owner of the contract, can update public key and address\n   */\n  function isRootAvailableForAttester(address attester, uint256 root) external view returns (bool) {\n    return _roots[attester][root] || _roots[address(0)][root];\n  }\n\n  function _registerRootForAttester(address attester, uint256 root) internal {\n    _roots[attester][root] = true;\n    if (attester == address(0)) {\n      emit RegisteredRootForAll(root);\n    } else {\n      emit RegisteredRootForAttester(attester, root);\n    }\n  }\n\n  function _unregisterRootForAttester(address attester, uint256 root) internal {\n    _roots[attester][root] = false;\n    if (attester == address(0)) {\n      emit UnregisteredRootForAll(root);\n    } else {\n      emit UnregisteredRootForAttester(attester, root);\n    }\n  }\n}\n"
  },
  {
    "path": "contracts/periphery/utils/CommitmentMapperRegistry.sol",
    "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.14;\n\nimport {Ownable} from '@openzeppelin/contracts/access/Ownable.sol';\nimport {Initializable} from '@openzeppelin/contracts/proxy/utils/Initializable.sol';\nimport {ICommitmentMapperRegistry} from './interfaces/ICommitmentMapperRegistry.sol';\n\n/**\n * @title Commitment Mapper Registry Contract\n * @author Sismo\n * @notice This contract stores information about the commitment mapper.\n * Its ethereum address and its EdDSA public key\n * For more information: https://commitment-mapper.docs.sismo.io\n *\n **/\ncontract CommitmentMapperRegistry is ICommitmentMapperRegistry, Initializable, Ownable {\n  uint8 public constant IMPLEMENTATION_VERSION = 2;\n\n  uint256[2] internal _commitmentMapperPubKey;\n  address _commitmentMapperAddress;\n\n  /**\n   * @dev Constructor\n   * @param owner Owner of the contract, can update public key and address\n   * @param commitmentMapperEdDSAPubKey EdDSA public key of the commitment mapper\n   * @param commitmentMapperAddress Address of the commitment mapper\n   */\n  constructor(\n    address owner,\n    uint256[2] memory commitmentMapperEdDSAPubKey,\n    address commitmentMapperAddress\n  ) {\n    initialize(owner, commitmentMapperEdDSAPubKey, commitmentMapperAddress);\n  }\n\n  /**\n   * @dev Initializes the contract, to be called by the proxy delegating calls to this implementation\n   * @param ownerAddress Owner of the contract, can update public key and address\n   * @param commitmentMapperEdDSAPubKey EdDSA public key of the commitment mapper\n   * @param commitmentMapperAddress Address of the commitment mapper\n   * @notice The reinitializer modifier is needed to configure modules that are added through upgrades and that require initialization.\n   */\n  function initialize(\n    address ownerAddress,\n    uint256[2] memory commitmentMapperEdDSAPubKey,\n    address commitmentMapperAddress\n  ) public reinitializer(IMPLEMENTATION_VERSION) {\n    // if proxy did not setup owner yet or if called by constructor (for implem setup)\n    if (owner() == address(0) || address(this).code.length == 0) {\n      _transferOwnership(ownerAddress);\n      _updateCommitmentMapperEdDSAPubKey(commitmentMapperEdDSAPubKey);\n      _updateCommitmentMapperAddress(commitmentMapperAddress);\n    }\n  }\n\n  /**\n   * @dev Updates the EdDSA public key\n   * @param newEdDSAPubKey new EdDSA pubic key\n   */\n  function updateCommitmentMapperEdDSAPubKey(uint256[2] memory newEdDSAPubKey) external onlyOwner {\n    _updateCommitmentMapperEdDSAPubKey(newEdDSAPubKey);\n  }\n\n  /**\n   * @dev Updates the address\n   * @param newAddress new address\n   */\n  function updateCommitmentMapperAddress(address newAddress) external onlyOwner {\n    _updateCommitmentMapperAddress(newAddress);\n  }\n\n  /**\n   * @dev Getter of the EdDSA public key of the commitment mapper\n   */\n  function getEdDSAPubKey() external view override returns (uint256[2] memory) {\n    return _commitmentMapperPubKey;\n  }\n\n  /**\n   * @dev Getter of the address of the commitment mapper\n   */\n  function getAddress() external view override returns (address) {\n    return _commitmentMapperAddress;\n  }\n\n  function _updateCommitmentMapperAddress(address newAddress) internal {\n    _commitmentMapperAddress = newAddress;\n    emit UpdatedCommitmentMapperAddress(newAddress);\n  }\n\n  function _updateCommitmentMapperEdDSAPubKey(uint256[2] memory pubKey) internal {\n    _commitmentMapperPubKey = pubKey;\n    emit UpdatedCommitmentMapperEdDSAPubKey(pubKey);\n  }\n}\n"
  },
  {
    "path": "contracts/periphery/utils/FrontendLib.sol",
    "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.14;\n\nimport {Ownable} from '@openzeppelin/contracts/access/Ownable.sol';\nimport {HydraS1AccountboundAttester} from '../../attesters/hydra-s1/HydraS1AccountboundAttester.sol';\nimport {Initializable} from '@openzeppelin/contracts/proxy/utils/Initializable.sol';\n\ncontract FrontendLib {\n  address private immutable _hydraS1AccountboundAttester;\n\n  constructor(address hydraS1AccountboundAttester) {\n    _hydraS1AccountboundAttester = hydraS1AccountboundAttester;\n  }\n\n  function getHydraS1AccountboundAttesterDestinationOfNullifierBatch(\n    uint256[] calldata nullifiers\n  ) external view returns (address[] memory) {\n    address[] memory destinations = new address[](nullifiers.length);\n\n    for (uint256 i = 0; i < nullifiers.length; i++) {\n      destinations[i] = HydraS1AccountboundAttester(_hydraS1AccountboundAttester)\n        .getDestinationOfNullifier(nullifiers[i]);\n    }\n\n    return destinations;\n  }\n}\n"
  },
  {
    "path": "contracts/periphery/utils/TransparentUpgradeableProxy.sol",
    "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (proxy/transparent/TransparentUpgradeableProxy.sol)\n// Note: we just copied so that we can see the contract used\npragma solidity ^0.8.14;\n\nimport {ERC1967Proxy} from '@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol';\n\n/**\n * @dev This contract implements a proxy that is upgradeable by an admin.\n *\n * To avoid https://medium.com/nomic-labs-blog/malicious-backdoors-in-ethereum-proxies-62629adf3357[proxy selector\n * clashing], which can potentially be used in an attack, this contract uses the\n * https://blog.openzeppelin.com/the-transparent-proxy-pattern/[transparent proxy pattern]. This pattern implies two\n * things that go hand in hand:\n *\n * 1. If any account other than the admin calls the proxy, the call will be forwarded to the implementation, even if\n * that call matches one of the admin functions exposed by the proxy itself.\n * 2. If the admin calls the proxy, it can access the admin functions, but its calls will never be forwarded to the\n * implementation. If the admin tries to call a function on the implementation it will fail with an error that says\n * \"admin cannot fallback to proxy target\".\n *\n * These properties mean that the admin account can only be used for admin actions like upgrading the proxy or changing\n * the admin, so it's best if it's a dedicated account that is not used for anything else. This will avoid headaches due\n * to sudden errors when trying to call a function from the proxy implementation.\n *\n * Our recommendation is for the dedicated account to be an instance of the {ProxyAdmin} contract. If set up this way,\n * you should think of the `ProxyAdmin` instance as the real administrative interface of your proxy.\n */\ncontract TransparentUpgradeableProxy is ERC1967Proxy {\n  /**\n   * @dev Initializes an upgradeable proxy managed by `_admin`, backed by the implementation at `_logic`, and\n   * optionally initialized with `_data` as explained in {ERC1967Proxy-constructor}.\n   */\n  constructor(\n    address _logic,\n    address admin_,\n    bytes memory _data\n  ) payable ERC1967Proxy(_logic, _data) {\n    assert(_ADMIN_SLOT == bytes32(uint256(keccak256('eip1967.proxy.admin')) - 1));\n    _changeAdmin(admin_);\n  }\n\n  /**\n   * @dev Modifier used internally that will delegate the call to the implementation unless the sender is the admin.\n   */\n  modifier ifAdmin() {\n    if (msg.sender == _getAdmin()) {\n      _;\n    } else {\n      _fallback();\n    }\n  }\n\n  /**\n   * @dev Returns the current admin.\n   *\n   * NOTE: Only the admin can call this function. See {ProxyAdmin-getProxyAdmin}.\n   *\n   * TIP: To get this value clients can read directly from the storage slot shown below (specified by EIP1967) using the\n   * https://eth.wiki/json-rpc/API#eth_getstorageat[`eth_getStorageAt`] RPC call.\n   * `0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103`\n   */\n  function admin() external ifAdmin returns (address admin_) {\n    admin_ = _getAdmin();\n  }\n\n  /**\n   * @dev Returns the current implementation.\n   *\n   * NOTE: Only the admin can call this function. See {ProxyAdmin-getProxyImplementation}.\n   *\n   * TIP: To get this value clients can read directly from the storage slot shown below (specified by EIP1967) using the\n   * https://eth.wiki/json-rpc/API#eth_getstorageat[`eth_getStorageAt`] RPC call.\n   * `0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc`\n   */\n  function implementation() external ifAdmin returns (address implementation_) {\n    implementation_ = _implementation();\n  }\n\n  /**\n   * @dev Changes the admin of the proxy.\n   *\n   * Emits an {AdminChanged} event.\n   *\n   * NOTE: Only the admin can call this function. See {ProxyAdmin-changeProxyAdmin}.\n   */\n  function changeAdmin(address newAdmin) external virtual ifAdmin {\n    _changeAdmin(newAdmin);\n  }\n\n  /**\n   * @dev Upgrade the implementation of the proxy.\n   *\n   * NOTE: Only the admin can call this function. See {ProxyAdmin-upgrade}.\n   */\n  function upgradeTo(address newImplementation) external ifAdmin {\n    _upgradeToAndCall(newImplementation, bytes(''), false);\n  }\n\n  /**\n   * @dev Upgrade the implementation of the proxy, and then call a function from the new implementation as specified\n   * by `data`, which should be an encoded function call. This is useful to initialize new storage variables in the\n   * proxied contract.\n   *\n   * NOTE: Only the admin can call this function. See {ProxyAdmin-upgradeAndCall}.\n   */\n  function upgradeToAndCall(\n    address newImplementation,\n    bytes calldata data\n  ) external payable ifAdmin {\n    _upgradeToAndCall(newImplementation, data, true);\n  }\n\n  /**\n   * @dev Returns the current admin.\n   */\n  function _admin() internal view virtual returns (address) {\n    return _getAdmin();\n  }\n\n  /**\n   * @dev Makes sure the admin cannot access the fallback function. See {Proxy-_beforeFallback}.\n   */\n  function _beforeFallback() internal virtual override {\n    require(\n      msg.sender != _getAdmin(),\n      'TransparentUpgradeableProxy: admin cannot fallback to proxy target'\n    );\n    super._beforeFallback();\n  }\n}\n"
  },
  {
    "path": "contracts/periphery/utils/interfaces/IAvailableRootsRegistry.sol",
    "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.14;\n\n/**\n * @title IAvailableRootsRegistry\n * @author Sismo\n * @notice Interface for (Merkle) Roots Registry\n */\ninterface IAvailableRootsRegistry {\n  event RegisteredRootForAttester(address attester, uint256 root);\n  event RegisteredRootForAll(uint256 root);\n  event UnregisteredRootForAttester(address attester, uint256 root);\n  event UnregisteredRootForAll(uint256 root);\n\n  error CannotRegisterForZeroAddress();\n  error CannotUnregisterForZeroAddress();\n\n  /**\n   * @dev Initializes the contract, to be called by the proxy delegating calls to this implementation\n   * @param owner Owner of the contract, can update public key and address\n   * @notice The reinitializer modifier is needed to configure modules that are added through upgrades and that require initialization.\n   */\n  function initialize(address owner) external;\n\n  /**\n   * @dev Register a root available for an attester\n   * @param attester Attester which will have the root available\n   * @param root Root to register\n   */\n  function registerRootForAttester(address attester, uint256 root) external;\n\n  /**\n   * @dev Unregister a root for an attester\n   * @param attester Attester which will no longer have the root available\n   * @param root Root to unregister\n   */\n  function unregisterRootForAttester(address attester, uint256 root) external;\n\n  /**\n   * @dev Registers a root, available for all contracts\n   * @param root Root to register\n   */\n  function registerRootForAll(uint256 root) external;\n\n  /**\n   * @dev Unregister a root, available for all contracts\n   * @param root Root to unregister\n   */\n  function unregisterRootForAll(uint256 root) external;\n\n  /**\n   * @dev returns whether a root is available for a caller (msg.sender)\n   * @param root root to check whether it is registered for me or not\n   */\n  function isRootAvailableForMe(uint256 root) external view returns (bool);\n\n  /**\n   * @dev Initializes the contract, to be called by the proxy delegating calls to this implementation\n   * @param attester Owner of the contract, can update public key and address\n   * @param root Owner of the contract, can update public key and address\n   */\n  function isRootAvailableForAttester(address attester, uint256 root) external view returns (bool);\n}\n"
  },
  {
    "path": "contracts/periphery/utils/interfaces/ICommitmentMapperRegistry.sol",
    "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.14;\n\ninterface ICommitmentMapperRegistry {\n  event UpdatedCommitmentMapperEdDSAPubKey(uint256[2] newEdDSAPubKey);\n  event UpdatedCommitmentMapperAddress(address newAddress);\n  error PubKeyNotValid(uint256[2] pubKey);\n\n  /**\n   * @dev Initializes the contract, to be called by the proxy delegating calls to this implementation\n   * @param owner Owner of the contract, can update public key and address\n   * @param commitmentMapperEdDSAPubKey EdDSA public key of the commitment mapper\n   * @param commitmentMapperAddress Address of the commitment mapper\n   * @notice The reinitializer modifier is needed to configure modules that are added through upgrades and that require initialization.\n   */\n  function initialize(\n    address owner,\n    uint256[2] memory commitmentMapperEdDSAPubKey,\n    address commitmentMapperAddress\n  ) external;\n\n  /**\n   * @dev Updates the EdDSA public key\n   * @param newEdDSAPubKey new EdDSA pubic key\n   */\n  function updateCommitmentMapperEdDSAPubKey(uint256[2] memory newEdDSAPubKey) external;\n\n  /**\n   * @dev Updates the address\n   * @param newAddress new address\n   */\n  function updateCommitmentMapperAddress(address newAddress) external;\n\n  /**\n   * @dev Getter of the address of the commitment mapper\n   */\n  function getEdDSAPubKey() external view returns (uint256[2] memory);\n\n  /**\n   * @dev Getter of the address of the commitment mapper\n   */\n  function getAddress() external view returns (address);\n}\n"
  },
  {
    "path": "contracts/tests/MockHydraS1SimpleAttester.sol",
    "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.14;\n\nimport {Attestation, Request} from '../core/libs/Structs.sol';\nimport {Attester} from '../core/Attester.sol';\nimport {IAttester} from '../core/interfaces/IAttester.sol';\nimport {IHydraS1AccountboundAttester} from '../attesters/hydra-s1/interfaces/IHydraS1AccountboundAttester.sol';\n\ncontract MockHydraS1SimpleAttester {\n  mapping(uint256 => address) internal _nullifiersDestinations;\n\n  function getDestinationOfNullifier(uint256 nullifier) external view returns (address) {\n    return _nullifiersDestinations[nullifier];\n  }\n\n  function setDestinationOfNullifier(uint256 nullifier, address destination) external {\n    _nullifiersDestinations[nullifier] = destination;\n  }\n}\n"
  },
  {
    "path": "contracts/tests/mocks/MockAttestationsRegistry.sol",
    "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.14;\nimport {IAttestationsRegistry} from '../../core/interfaces/IAttestationsRegistry.sol';\nimport {AttestationsRegistryConfigLogic} from '../../core/libs/attestations-registry/AttestationsRegistryConfigLogic.sol';\nimport {AttestationsRegistryState} from '../../core/libs/attestations-registry/AttestationsRegistryState.sol';\nimport {IBadges} from '../../core/interfaces/IBadges.sol';\nimport {Attestation, AttestationData} from '../../core/libs/Structs.sol';\n\ncontract MockAttestationsRegistry {\n  uint256 immutable ATTESTATION_VALUE;\n\n  event AttestationRecorded(Attestation attestation);\n\n  constructor(uint256 attestationValue) {\n    ATTESTATION_VALUE = attestationValue;\n  }\n\n  function getAttestationValue(\n    uint256 collectionId,\n    address owner\n  ) external view returns (uint256) {\n    return ATTESTATION_VALUE;\n  }\n\n  function recordAttestations(Attestation[] calldata attestations) external {\n    for (uint256 i = 0; i < attestations.length; i++) {\n      emit AttestationRecorded(attestations[i]);\n    }\n  }\n}\n"
  },
  {
    "path": "contracts/tests/mocks/MockAttester.sol",
    "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.14;\n\nimport {Attestation, Request} from '../../core/libs/Structs.sol';\nimport {Attester} from '../../core/Attester.sol';\nimport {IAttester} from '../../core/interfaces/IAttester.sol';\n\ncontract MockAttester is IAttester, Attester {\n  uint256 public immutable ATTESTATION_ID_MIN;\n  uint256 public immutable ATTESTATION_ID_MAX;\n\n  constructor(\n    address ATTESTATION_REGISTRY_ADDRESS,\n    uint256 collectionIdFirst,\n    uint256 collectionIdLast\n  ) Attester(ATTESTATION_REGISTRY_ADDRESS) {\n    ATTESTATION_ID_MIN = collectionIdFirst;\n    ATTESTATION_ID_MAX = collectionIdLast;\n  }\n\n  function _verifyRequest(\n    Request calldata request,\n    bytes calldata proofData\n  ) internal virtual override {}\n\n  function buildAttestations(\n    Request calldata request,\n    bytes calldata /*data*/\n  ) public view virtual override(Attester, IAttester) returns (Attestation[] memory) {\n    uint256 collectionId = ATTESTATION_ID_MIN + request.claims[0].groupId;\n    Attestation[] memory attestations = new Attestation[](1);\n    attestations[0] = Attestation(\n      collectionId,\n      request.destination,\n      address(this),\n      request.claims[0].claimedValue,\n      abi.decode(request.claims[0].extraData, (uint32)),\n      'Mock Attester v0'\n    );\n    return (attestations);\n  }\n}\n"
  },
  {
    "path": "contracts/tests/mocks/mockContractUsingSismoLib.sol",
    "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.14;\n\nimport {UsingSismo, Request} from '../../libs/SismoLib.sol';\n\ncontract MockContractUsingSismoLib is UsingSismo {\n  uint256 public constant FIRST_GATED_BADGE_ID = 200002;\n  uint256 public constant SECOND_GATED_BADGE_ID = 200003;\n\n  mapping(address => uint256) public balances;\n\n  /*******************************************************\n    1. MINT BADGES from sismoProof received by \"Prove With Sismo\" off-chain flow\n  *******************************************************/\n\n  function testMintSismoBadgeWithAttester(\n    Request memory request,\n    bytes memory proofData,\n    address attester\n  ) external {\n    _mintSismoBadge(request, proofData, attester);\n  }\n\n  function testMintSismoBadge(Request memory request, bytes memory proofData) external {\n    _mintSismoBadge(request, proofData);\n  }\n\n  function testMintSismoBadgesWithAttester(\n    Request memory request,\n    bytes memory proofData,\n    address attester\n  ) external {\n    _mintSismoBadges(request, proofData, attester);\n  }\n\n  function testMintSismoBadges(Request memory request, bytes memory proofData) external {\n    _mintSismoBadges(request, proofData);\n  }\n\n  /*******************************************************\n    2. MODIFIERS\n  *******************************************************/\n\n  function testOnlyBadgeHoldersModifier() external onlyBadgeHolders(FIRST_GATED_BADGE_ID) {\n    _inc(_msgSender());\n  }\n\n  function testOnlyBadgesHoldersWithAllBadgesRequiredModifier(\n    uint256[] memory gatedBadge\n  ) external onlyBadgesHolders(gatedBadge, true) {\n    _inc(_msgSender());\n  }\n\n  function testOnlyBadgesHoldersWithOnlyOneBadgeRequiredModifier(\n    uint256[] memory gatedBadge\n  ) external onlyBadgesHolders(gatedBadge, false) {\n    _inc(_msgSender());\n  }\n\n  function testOnlyBadgeHoldersWithGreaterBalanceModifier()\n    external\n    onlyBadgeHoldersWithGreaterBalance(FIRST_GATED_BADGE_ID, 2)\n  {\n    _inc(_msgSender());\n  }\n\n  function testOnlyBadgeHoldersWithLowerBalanceModifier()\n    external\n    onlyBadgeHoldersWithLowerBalance(FIRST_GATED_BADGE_ID, 2)\n  {\n    _inc(_msgSender());\n  }\n\n  function testOnlyBadgeHoldersWithExactBalanceModifier()\n    external\n    onlyBadgeHoldersWithExactBalance(FIRST_GATED_BADGE_ID, 1)\n  {\n    _inc(_msgSender());\n  }\n\n  function testOnlyBadgesHoldersWithGreaterBalanceAndAllBadgesRequiredModifier(\n    uint256[] memory badgeTokenIds,\n    uint256[] memory minBalances\n  ) external onlyBadgesHoldersWithGreaterBalance(badgeTokenIds, minBalances, true) {\n    _inc(_msgSender());\n  }\n\n  function testOnlyBadgesHoldersWithGreaterBalanceAndOnlyOneBadgeRequiredModifier(\n    uint256[] memory badgeTokenIds,\n    uint256[] memory minBalances\n  ) external onlyBadgesHoldersWithGreaterBalance(badgeTokenIds, minBalances, false) {\n    _inc(_msgSender());\n  }\n\n  function testOnlyBadgesHoldersWithLowerBalanceAndAllBadgesRequiredModifier(\n    uint256[] memory badgeTokenIds,\n    uint256[] memory maxBalances\n  ) external onlyBadgesHoldersWithLowerBalance(badgeTokenIds, maxBalances, true) {\n    _inc(_msgSender());\n  }\n\n  function testOnlyBadgesHoldersWithLowerBalanceAndOnlyOneBadgeRequiredModifier(\n    uint256[] memory badgeTokenIds,\n    uint256[] memory maxBalances\n  ) external onlyBadgesHoldersWithLowerBalance(badgeTokenIds, maxBalances, false) {\n    _inc(_msgSender());\n  }\n\n  function testOnlyBadgesHoldersWithExactBalanceAndAllBadgesRequiredModifier(\n    uint256[] memory badgeTokenIds,\n    uint256[] memory exactBalances\n  ) external onlyBadgesHoldersWithExactBalance(badgeTokenIds, exactBalances, true) {\n    _inc(_msgSender());\n  }\n\n  function testOnlyBadgesHoldersWithExactBalanceAndOnlyOneBadgeRequiredModifier(\n    uint256[] memory badgeTokenIds,\n    uint256[] memory exactBalances\n  ) external onlyBadgesHoldersWithExactBalance(badgeTokenIds, exactBalances, false) {\n    _inc(_msgSender());\n  }\n\n  function _inc(address account) internal {\n    balances[account]++;\n  }\n}\n"
  },
  {
    "path": "contracts/zkdrop/ZKBadgeboundERC721.sol",
    "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.14;\n\nimport '@openzeppelin/contracts-upgradeable/token/ERC721/ERC721Upgradeable.sol';\nimport {UsingSismo, Request} from '../libs/SismoLib.sol';\nimport {AccessControl} from '@openzeppelin/contracts/access/AccessControl.sol';\nimport {Pausable} from '@openzeppelin/contracts/security/Pausable.sol';\nimport {Context} from '@openzeppelin/contracts/utils/Context.sol';\n\n/**\n * @title ZKBadgeboundERC721 (ZK-SBT)\n * @dev Non-Transferrable NFT (SBT) that can only be claimed by users eligible to a specific ZK Badge.\n * This contract implements two different \"Prove With Sismo\" flows to claim the SBT:\n * 2-txs flow: #1(tx) user is directed to Sismo to mint the ZK badge.\n *             #2(tx) user calls claim() that checks that they own the ZK badge and mints the SBT\n * 1-tx flow:  #1(off-chain) user is directed to Sismo to create a ZK Proof of eligibility\n *             #(tx): user calls claimWithSismo(req, proof) that forwards the proof to the ZK Attester\n *                    ZK Attester checks the proof (and mints the ZK Badge). Then the SBT is claimed.\n *\n * BadgeBound: The SBT is bound to a specific ZK Badge, which is identified by its nullifier.\n * tokenId Of SBT == nullifier of ZK Badge\n * The nullifier is an anon identifier of the source account used to prove eligibility to the ZK Badge.\n *\n * Accountbound: The Badge is Accountbound, so is the SBT.\n * The eligible account used to generate ZK Proof can burn and mint the ZK Badge to a new destination\n * Can be done only once every cooldown duration. Makes it possible to lose\n * 2-tx flow:  #1(tx) Burn/mint ZK Badge to new destination\n *             #2(tx) call transfer() SBT to new destination\n * 1-tx flow:  #1(off-chain) get ZK Proof of eligibility\n *             #2(tx) call transferWithSismo(req,proof)\n */\n\ncontract ZKBadgeboundERC721 is ERC721Upgradeable, UsingSismo, AccessControl, Pausable {\n  uint256 public immutable GATING_BADGE_TOKEN_ID;\n\n  string private _baseTokenURI;\n\n  event BaseTokenUriChanged(string baseTokenURI);\n\n  error ERC721AlreadyOwned(address owner, uint256 balance);\n  error BadgeNullifierNotEqualToTokenId(uint256 badgeNullifier, uint256 tokenId);\n  error BadgeNullifierZeroNotAllowed();\n  error BadgeDestinationAndSBTDestinationNotEqual(address badgeDestination, address nftDestination);\n  error NoTokenTransferWhilePaused();\n\n  constructor(\n    string memory name,\n    string memory symbol,\n    string memory baseTokenURI,\n    uint256 gatingBadgeTokenId,\n    address admin\n  ) {\n    GATING_BADGE_TOKEN_ID = gatingBadgeTokenId;\n    initialize(name, symbol, baseTokenURI, admin);\n  }\n\n  function initialize(\n    string memory name,\n    string memory symbol,\n    string memory baseTokenURI,\n    address admin\n  ) public initializer {\n    __ERC721_init(name, symbol);\n    _setupRole(DEFAULT_ADMIN_ROLE, admin);\n    _setBaseTokenUri(baseTokenURI);\n  }\n\n  /**\n   * @dev Claim function callable by ZK Badge holders\n   * @notice Claim 2-tx flow (see L14)\n   */\n  function claim() public onlyBadgeHolders(GATING_BADGE_TOKEN_ID) {\n    uint256 nullifier = _getNulliferOfBadge(_msgSender());\n\n    _mint(_msgSender(), nullifier);\n  }\n\n  /**\n   * @dev Claim function callable by anyone for ZK Badge holders\n   * @notice Claim 2-tx flow (see L14)\n   * @param to recipient of the SBT, must hold the ZK Badge\n   */\n  function claimTo(address to) public {\n    _requireBadge(to, GATING_BADGE_TOKEN_ID);\n    uint256 badgeNullifier = _getNulliferOfBadge(to);\n    if (badgeNullifier == 0) {\n      revert BadgeNullifierZeroNotAllowed();\n    }\n\n    _mint(to, badgeNullifier);\n  }\n\n  /**\n   * @dev Claim by providing the ZK Proof of eligibility\n   * @notice Claim 1-tx flow (see L16)\n   * @param request user request containing the data needed to mint the ZK Badge\n   * @param sismoProof Proof of eligibility to mint the ZK Badge\n   */\n  function claimWithSismo(Request memory request, bytes memory sismoProof) public {\n    (address destination, , ) = _mintSismoBadge(request, sismoProof);\n    claimTo(destination);\n  }\n\n  /**\n   * @dev Transfers the SBT to a new destination\n   * @notice Mint/burn 2-tx flow (see L27)\n   * @notice The address `to` must hold a ZK Badge\n   * @param from address of the account that currently owns the SBT\n   * @param to address of the account that will receive the SBT (must hold a ZK Badge)\n   * @param tokenId id of the SBT to transfer (= ZK Badge nullifer)\n   */\n  function safeTransferFrom(\n    address from,\n    address to,\n    uint256 tokenId\n  ) public override(ERC721Upgradeable) {\n    _requireBadge(to, GATING_BADGE_TOKEN_ID);\n    uint256 badgeNullifier = _getNulliferOfBadge(to);\n    if (badgeNullifier == 0) {\n      revert BadgeNullifierZeroNotAllowed();\n    }\n    if (badgeNullifier != tokenId) {\n      revert BadgeNullifierNotEqualToTokenId(badgeNullifier, tokenId);\n    }\n\n    _safeTransfer(from, to, tokenId, '');\n  }\n\n  /**\n   * @dev Transfers the SBT to a new destination\n   * @notice Mint/burn 2-tx flow (see L27)\n   * @notice The address `to` must hold a ZK Badge\n   * @param from address of the account that currently owns the SBT\n   * @param to address of the account that will receive the SBT (must hold a ZK Badge)\n   * @param tokenId id of the SBT to transfer (= ZK Badge nullifer)\n   */\n  function transferFrom(\n    address from,\n    address to,\n    uint256 tokenId\n  ) public override(ERC721Upgradeable) {\n    _requireBadge(to, GATING_BADGE_TOKEN_ID);\n    uint256 badgeNullifier = _getNulliferOfBadge(to);\n    if (badgeNullifier == 0) {\n      revert BadgeNullifierZeroNotAllowed();\n    }\n    if (badgeNullifier != tokenId) {\n      revert BadgeNullifierNotEqualToTokenId(badgeNullifier, tokenId);\n    }\n\n    _transfer(from, to, tokenId);\n  }\n\n  /**\n   * @dev Transfers the SBT to a new destination\n   * @notice Mint/burn 1-tx flow (see L29)\n   * @param from address of the account that currently owns the SBT\n   * @param to address of the account that will receive the SBT (must hold a ZK Badge with the id MERGOOOR_PASS_BADGE_TOKEN_ID)\n   * @param tokenId id of the SBT to transfer\n   * @param request user request containing the data needed to mint the ZK Badge\n   * @param sismoProof Proof of eligibility to mint the ZK Badge\n   */\n  function transferWithSismo(\n    address from,\n    address to,\n    uint256 tokenId,\n    Request memory request,\n    bytes memory sismoProof\n  ) public {\n    (address destination, , ) = _mintSismoBadge(request, sismoProof);\n    if (destination != to) {\n      revert BadgeDestinationAndSBTDestinationNotEqual(destination, to);\n    }\n    safeTransferFrom(from, destination, tokenId);\n  }\n\n  /**\n   * @dev Prevent the transfer of a SBT if the destination address already owns a SBT\n   * @param to Address to transfer the SBT to\n   */\n  function _beforeTokenTransfer(\n    address,\n    address to,\n    uint256,\n    uint256\n  ) internal view override(ERC721Upgradeable) {\n    // Prevent the transfer of a SBT if the destination address already owns a SBT\n    // Not critical but lowers the complexity\n    if (balanceOf(to) > 0) {\n      revert ERC721AlreadyOwned(to, balanceOf(to));\n    }\n\n    if (paused()) {\n      revert NoTokenTransferWhilePaused();\n    }\n  }\n\n  /**\n   * @dev Getter to know a nullifier for a specific address\n   * @param to destination address referenced in the proof with this nullifier\n   */\n  function _getNulliferOfBadge(address to) internal view returns (uint256) {\n    bytes memory extraData = BADGES.getBadgeExtraData(to, GATING_BADGE_TOKEN_ID);\n    return HYDRA_S1_ACCOUNTBOUND_ATTESTER.getNullifierFromExtraData(extraData);\n  }\n\n  /**\n   * @dev Set BaseTokenUri for SBT contract\n   */\n  function setBaseTokenUri(string memory baseUri) public onlyRole(DEFAULT_ADMIN_ROLE) {\n    _setBaseTokenUri(baseUri);\n  }\n\n  function _setBaseTokenUri(string memory baseUri) private {\n    _baseTokenURI = baseUri;\n    emit BaseTokenUriChanged(baseUri);\n  }\n\n  function _baseURI() internal view override returns (string memory) {\n    return _baseTokenURI;\n  }\n\n  /**\n   * @dev Pauses all token transfers.\n   */\n  function pause() public virtual onlyRole(DEFAULT_ADMIN_ROLE) {\n    _pause();\n  }\n\n  /**\n   * @dev Unpauses all token transfers.\n   */\n  function unpause() public virtual onlyRole(DEFAULT_ADMIN_ROLE) {\n    _unpause();\n  }\n\n  function tokenURI(uint256) public view override returns (string memory) {\n    string memory baseURI = _baseURI();\n    return bytes(baseURI).length > 0 ? baseURI : '';\n  }\n\n  /**\n   * @dev See {IERC165-supportsInterface}.\n   */\n  function supportsInterface(\n    bytes4 interfaceId\n  ) public view virtual override(ERC721Upgradeable, AccessControl) returns (bool) {\n    return super.supportsInterface(interfaceId);\n  }\n\n  function _msgSender() internal view override(Context, ContextUpgradeable) returns (address) {\n    return msg.sender;\n  }\n\n  function _msgData() internal pure override(Context, ContextUpgradeable) returns (bytes calldata) {\n    return msg.data;\n  }\n}\n"
  },
  {
    "path": "hardhat.config.ts",
    "content": "import * as dotenv from 'dotenv';\nimport { HardhatUserConfig } from 'hardhat/config';\nimport {\n  EthereumNetwork,\n  PolygonNetwork,\n  GnosisNetwork,\n  NETWORKS_RPC_URL,\n} from './helper-hardhat-config';\nimport '@nomiclabs/hardhat-etherscan';\nimport '@nomiclabs/hardhat-waffle';\nimport '@nomiclabs/hardhat-ethers';\nimport '@typechain/hardhat';\nimport 'hardhat-storage-layout';\nimport 'hardhat-gas-reporter';\nimport 'solidity-coverage';\nimport 'hardhat-deploy';\nimport { BigNumber, Wallet } from 'ethers';\nimport fg from 'fast-glob';\nimport { singletonFactory } from './utils/singletonFactory';\n\ndotenv.config();\n\n// Load all tasks files\nif (process.env.SKIP_LOAD !== 'true') {\n  const files = fg.sync(['./tasks/**/*.task.ts'], { dot: true });\n  for (const file of files) {\n    require(file);\n  }\n}\n\nconst HARDFORK = 'london';\n\nconst MNEMONIC_PATH = \"m/44'/60'/0'/0\";\n\nconst SISMO_SHARED_MNEMONIC =\n  'analyst decade album recall stem run cage ozone human pepper once insect';\nconst MNEMONIC = process.env.MNEMONIC || SISMO_SHARED_MNEMONIC;\n\nconst INFURA_KEY = process.env.INFURA_KEY || '';\nconst ALCHEMY_KEY = process.env.ALCHEMY_KEY || '';\n\nconst FORK = process.env.FORK === 'true';\nconst FORKING_BLOCK = process.env.FORKING_BLOCK\n  ? parseInt(process.env.FORKING_BLOCK || '')\n  : undefined;\nconst FORK_NETWORK = process.env.FORK_NETWORK || '';\n\nconst forkUrl = {\n  kovan: NETWORKS_RPC_URL[EthereumNetwork.kovan],\n  mainnet: NETWORKS_RPC_URL[EthereumNetwork.mainnet],\n  polygon: NETWORKS_RPC_URL[PolygonNetwork.main],\n  polygonPlayground: NETWORKS_RPC_URL[PolygonNetwork.main],\n  rinkeby: NETWORKS_RPC_URL[EthereumNetwork.rinkeby],\n  goerliTestnet: NETWORKS_RPC_URL[EthereumNetwork.goerli],\n  mumbaiTestnet: NETWORKS_RPC_URL[PolygonNetwork.mumbai],\n  goerliStaging: NETWORKS_RPC_URL[EthereumNetwork.goerli],\n  mumbaiStaging: NETWORKS_RPC_URL[PolygonNetwork.mumbai],\n  gnosis: NETWORKS_RPC_URL[GnosisNetwork.gnosis],\n};\n\nconst forking = FORK\n  ? {\n      url: forkUrl[FORK_NETWORK],\n      blockNumber: FORKING_BLOCK,\n    }\n  : undefined;\n\nconst getCommonNetworkConfig = (networkName: string, networkId: number) => ({\n  url: NETWORKS_RPC_URL[networkName] ?? '',\n  hardfork: HARDFORK,\n  chainId: networkId,\n  accounts: {\n    mnemonic: MNEMONIC,\n    path: MNEMONIC_PATH,\n    initialIndex: 0,\n    count: 20,\n  },\n});\n\nconst accounts = Array.from(Array(20), (_, index) => {\n  const wallet = Wallet.fromMnemonic(SISMO_SHARED_MNEMONIC, `m/44'/60'/0'/0/${index}`);\n  return {\n    privateKey: wallet.privateKey,\n    balance: '1000000000000000000000000',\n  };\n});\n\nconst deterministicDeployment = (network: string) => {\n  const info = singletonFactory[parseInt(network)];\n  return {\n    factory: info!.address,\n    deployer: info!.signerAddress,\n    funding: BigNumber.from(info!.gasLimit).mul(BigNumber.from(info!.gasPrice)).toString(),\n    signedTx: info!.transaction,\n  };\n};\n\nconst LOCAL_CHAIN_ID = process.env.LOCAL_CHAIN_ID ? parseInt(process.env.LOCAL_CHAIN_ID) : 31337;\nconst LOCAL_HOSTNAME = process.env.LOCAL_HOSTNAME ?? 'localhost';\nconst LOCAL_PORT = process.env.LOCAL_PORT ? parseInt(process.env.LOCAL_PORT) : 8545;\n\nconst config: HardhatUserConfig = {\n  solidity: {\n    compilers: [\n      {\n        version: '0.8.14',\n        settings: {\n          optimizer: { enabled: true, runs: 200 },\n        },\n      },\n    ],\n  },\n  typechain: {\n    outDir: 'types',\n  },\n  etherscan: {\n    apiKey: process.env.ETHERSCAN_API_KEY,\n  },\n  defaultNetwork: 'hardhat',\n  networks: {\n    kovan: getCommonNetworkConfig(EthereumNetwork.kovan, 42),\n    mainnet: getCommonNetworkConfig(EthereumNetwork.mainnet, 1),\n    polygon: getCommonNetworkConfig(PolygonNetwork.main, 137),\n    polygonPlayground: getCommonNetworkConfig(PolygonNetwork.main, 137),\n    goerliStaging: getCommonNetworkConfig(EthereumNetwork.goerli, 5),\n    mumbaiStaging: getCommonNetworkConfig(PolygonNetwork.mumbai, 80001),\n    goerliTestnet: getCommonNetworkConfig(EthereumNetwork.goerli, 5),\n    mumbaiTestnet: getCommonNetworkConfig(PolygonNetwork.mumbai, 80001),\n    gnosis: getCommonNetworkConfig(GnosisNetwork.gnosis, 100),\n    local: {\n      url: `http://${LOCAL_HOSTNAME}:${LOCAL_PORT}`,\n      accounts: accounts.map((account) => account.privateKey),\n      hardfork: HARDFORK,\n      chainId: LOCAL_CHAIN_ID,\n    },\n    hardhat: {\n      live: false,\n      hardfork: 'london',\n      chainId: LOCAL_CHAIN_ID,\n      throwOnTransactionFailures: true,\n      throwOnCallFailures: true,\n      accounts,\n      forking,\n      saveDeployments: false,\n    },\n    tenderly: {\n      chainId: 5, // chain you forked\n      url: `https://rpc.tenderly.co/fork/${process.env.TINDERLY_FORK}`,\n    },\n    ganache: {\n      live: false,\n      url: 'http://ganache:8545',\n      accounts: accounts.map((account) => account.privateKey),\n    },\n    coverage: {\n      live: false,\n      url: 'http://localhost:8555',\n    },\n  },\n  deterministicDeployment,\n};\n\nexport default config;\n"
  },
  {
    "path": "helper-hardhat-config.ts",
    "content": "import dotenv from 'dotenv';\ndotenv.config({});\n\nexport type Network = EthereumNetwork | PolygonNetwork | GnosisNetwork;\n\nexport enum EthereumNetwork {\n  kovan = 'kovan',\n  goerli = 'goerli',\n  rinkeby = 'rinkeby',\n  mainnet = 'mainnet',\n  hardhat = 'hardhat',\n  tenderlyMain = 'tenderlyMain',\n  harhatevm = 'harhatevm',\n}\n\nexport enum PolygonNetwork {\n  main = 'polygon-mainnet',\n  mumbai = 'mumbai',\n}\n\nexport enum GnosisNetwork {\n  gnosis = 'gnosis',\n}\n\nexport type ParamsPerNetwork<T> =\n  | EthereumParamsPerNetwork<T>\n  | PolygonParamsPerNetwork<T>\n  | XDaiParamsPerNetwork<T>;\n\nexport interface EthereumParamsPerNetwork<Network> {\n  [EthereumNetwork.harhatevm]: Network;\n  [EthereumNetwork.rinkeby]: Network;\n  [EthereumNetwork.mainnet]: Network;\n  [EthereumNetwork.hardhat]: Network;\n  [EthereumNetwork.tenderlyMain]: Network;\n  [EthereumNetwork.goerli]: Network;\n}\n\nexport interface PolygonParamsPerNetwork<T> {\n  [PolygonNetwork.main]: T;\n  [PolygonNetwork.mumbai]: T;\n}\n\nexport interface XDaiParamsPerNetwork<T> {\n  [GnosisNetwork.gnosis]: T;\n}\n\nexport interface ObjectString {\n  [key: string]: string;\n}\nexport const alchemyUrlOrEnvVar = (defaultAlchemyUrl: string, rpcUrl?: string): string => {\n  const defaultUrl = `${defaultAlchemyUrl}/${process.env.ALCHEMY_KEY}`;\n  return rpcUrl ? rpcUrl : defaultUrl;\n};\n\nexport const NETWORKS_RPC_URL: ParamsPerNetwork<string> = {\n  [EthereumNetwork.rinkeby]: alchemyUrlOrEnvVar(\n    'https://eth-rinkeby.alchemyapi.io/v2',\n    process.env.RINKEBY_RPC_URL\n  ),\n  [EthereumNetwork.mainnet]: alchemyUrlOrEnvVar(\n    'https://eth-mainnet.alchemyapi.io/v2',\n    process.env.MAINNET_RPC_URL\n  ),\n  [EthereumNetwork.goerli]: alchemyUrlOrEnvVar(\n    'https://eth-goerli.g.alchemy.com/v2',\n    process.env.MAINNET_RPC_URL\n  ),\n  [EthereumNetwork.hardhat]: 'http://localhost:8545',\n  [PolygonNetwork.mumbai]: alchemyUrlOrEnvVar(\n    'https://polygon-mumbai.g.alchemy.com/v2',\n    process.env.MUMBAI_RPC_URL\n  ),\n  [PolygonNetwork.main]: alchemyUrlOrEnvVar(\n    'https://polygon-mainnet.g.alchemy.com/v2',\n    process.env.POLYGON_RPC_URL\n  ),\n  [GnosisNetwork.gnosis]: 'https://rpc.gnosischain.com',\n};\n"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"sismo-protocol\",\n  \"version\": \"1.0.0\",\n  \"description\": \"Sismo attestations repo\",\n  \"main\": \"index.js\",\n  \"repository\": \"git@github.com:sismo-core/sismo-protocol.git\",\n  \"author\": \"leosayous21.sismo.eth <leosayous@gmail.com>\",\n  \"license\": \"MIT\",\n  \"scripts\": {\n    \"compile\": \"SKIP_LOAD=true hardhat compile\",\n    \"test\": \"hardhat test\",\n    \"prettier\": \"prettier --write 'contracts/**/*.sol'\",\n    \"chain\": \"hardhat node\",\n    \"storage-layout\": \"hardhat print-storage-layout\",\n    \"deploy:local\": \"rm -rf deployments/local && hardhat deploy-full-local  --network local\"\n  },\n  \"devDependencies\": {\n    \"@nomiclabs/hardhat-etherscan\": \"^3.0.1\",\n    \"@nomiclabs/hardhat-waffle\": \"^2.0.2\",\n    \"@typechain/hardhat\": \"^4.0.0\",\n    \"@types/chai\": \"^4.3.0\",\n    \"@types/jest\": \"^27.4.0\",\n    \"@types/mocha\": \"^9.1.0\",\n    \"chai\": \"^4.3.6\",\n    \"concurrently\": \"^7.2.0\",\n    \"dotenv\": \"^16.0.0\",\n    \"prettier\": \"^2.5.1\",\n    \"prettier-plugin-solidity\": \"^1.0.0-beta.19\",\n    \"solhint\": \"^3.3.7\",\n    \"solidity-coverage\": \"^0.7.19\",\n    \"tslib\": \"^2.4.0\"\n  },\n  \"husky\": {\n    \"hooks\": {\n      \"pre-commit\": \"pretty-quick --staged --pattern 'test/**/*.ts'  --pattern 'tasks/**/*.ts' --pattern 'contracts/**/*.sol'\"\n    }\n  },\n  \"dependencies\": {\n    \"@metamask/eth-sig-util\": \"^4.0.0\",\n    \"@nomiclabs/hardhat-ethers\": \"^2.2.1\",\n    \"@openzeppelin/contracts\": \"^4.5.0\",\n    \"@openzeppelin/contracts-upgradeable\": \"^4.8.0\",\n    \"@sismo-core/commitment-mapper-tester-js\": \"^1.0.11\",\n    \"@sismo-core/hydra-s1\": \"^1.0.6\",\n    \"@sismo-core/pythia-1\": \"^1.0.6\",\n    \"@trufflesuite/eth-sig-util\": \"^1.4.2\",\n    \"@typechain/ethers-v5\": \"^9.0.0\",\n    \"@typescript-eslint/eslint-plugin\": \"^5.22.0\",\n    \"@typescript-eslint/parser\": \"^5.22.0\",\n    \"defender-relay-client\": \"^1.21.3\",\n    \"eslint\": \"^8.14.0\",\n    \"ethereum-waffle\": \"^3.4.0\",\n    \"ethers\": \"^5.6.8\",\n    \"fast-glob\": \"^3.2.11\",\n    \"hardhat\": \"^2.9.7\",\n    \"hardhat-deploy\": \"^0.11.22\",\n    \"hardhat-gas-reporter\": \"^1.0.7\",\n    \"hardhat-storage-layout\": \"^0.1.6\",\n    \"husky\": \"4.2.5\",\n    \"hydra-s1-previous\": \"npm:@sismo-core/hydra-s1@v1.0.5\",\n    \"js-sha3\": \"^0.8.0\",\n    \"mocha\": \"^10.0.0\",\n    \"pretty-quick\": \"^3.1.3\",\n    \"ts-node\": \"^10.5.0\",\n    \"typechain\": \"^7.0.0\",\n    \"typescript\": \"^4.5.5\",\n    \"web3\": \"^1.7.1\"\n  }\n}\n"
  },
  {
    "path": "scripts/wait-for-local-chain.sh",
    "content": "#!/bin/sh\nset -e\n\nHOSTNAME=${LOCAL_HOSTNAME:-'localhost'} \nPORT=${LOCAL_PORT:-8545}\nwhile ! nc -z $HOSTNAME $PORT; do   \n  echo \"Waiting for chain to be up ...\"\n  sleep 2 \ndone\n \n>&2 echo \"Chain is up - executing command\"\nexec \"$@\"\n"
  },
  {
    "path": "tasks/addresses-provider/set-batch.task.ts",
    "content": "import { task } from 'hardhat/config';\nimport { HardhatRuntimeEnvironment } from 'hardhat/types';\nimport { AddressesProvider__factory } from '../../types';\nimport { CommonTaskOptions, wrapCommonOptions } from '../utils';\nimport { confirm } from '../utils/confirm';\nimport { deploymentsConfig } from '../../tasks/deploy-tasks/deployments-config';\n\nexport type SetBatchArgs = {\n  contractAddressesAsString: string;\n  contractNamesAsString: string;\n  options?: CommonTaskOptions;\n};\n\nasync function action(\n  { contractAddressesAsString, contractNamesAsString, options }: SetBatchArgs,\n  hre: HardhatRuntimeEnvironment\n): Promise<void> {\n  const [signer] = await hre.ethers.getSigners();\n\n  const config = deploymentsConfig[process.env.FORK_NETWORK ?? hre.network.name];\n\n  const contractAddresses = contractAddressesAsString.split(',');\n  const contractNames = contractNamesAsString.split(',');\n\n  const sismoAddressesProvider = AddressesProvider__factory.connect(\n    config.sismoAddressesProvider.address,\n    signer\n  );\n\n  if (process.env.SET_BATCH_MANUAL_OPERATION === 'true') {\n    console.log(`\n    ************************************\n    *             SET BATCH            *\n    ************************************`);\n\n    if (options?.manualConfirm) {\n      await confirm();\n    }\n\n    // we can't connect as signer, we need manual operation\n    const iface = new hre.ethers.utils.Interface(AddressesProvider__factory.abi);\n    const data = iface.encodeFunctionData('setBatch', [contractAddresses, contractNames]);\n\n    console.log({\n      from: await sismoAddressesProvider.owner(),\n      to: sismoAddressesProvider.address,\n      data: data,\n    });\n\n    console.log('Send the transaction using etherscan !');\n  } else {\n    console.log(`\n    Aborting setBatch...`);\n  }\n}\n\ntask('set-batch')\n  .addParam('contractAddressesAsString', 'Addresses of the contracts we want to set')\n  .addParam('contractNamesAsString', 'Names of the contracts we want to set')\n  .setAction(wrapCommonOptions(action));\n"
  },
  {
    "path": "tasks/available-roots-registry/register-for-attester.task.ts",
    "content": "import { task } from 'hardhat/config';\nimport { HardhatRuntimeEnvironment } from 'hardhat/types';\nimport { getDeployer } from '../deploy-tasks/utils';\n\nimport { AvailableRootsRegistry__factory } from '../../types';\nimport { manualConfirmValidity } from '../utils/confirm';\nimport { BigNumber, Signer } from 'ethers';\nimport { CommonTaskOptions, wrapCommonOptions } from '../utils';\nimport { getRelayerSigner } from '../utils/relayer';\n\nexport type RegisterForAttesterArgs = {\n  root: string;\n  attester: string;\n  availableRootsRegistryAddress?: string;\n  relayed?: boolean;\n  options?: CommonTaskOptions;\n};\n\nasync function action(\n  { root, attester, relayed, options, availableRootsRegistryAddress }: RegisterForAttesterArgs,\n  hre: HardhatRuntimeEnvironment\n): Promise<void> {\n  if (root === '0') {\n    if (options?.log) {\n      console.log(`\n      root undefined for attester ${attester}. Not running the task.\n    `);\n    }\n    return;\n  }\n\n  const signer = relayed ? await getRelayerSigner() : ((await getDeployer(hre)) as Signer);\n\n  const availableRootsRegistry = AvailableRootsRegistry__factory.connect(\n    availableRootsRegistryAddress || (await hre.deployments.get(`AvailableRootsRegistry`)).address,\n    signer\n  );\n\n  const isRootAlreadyRegistered = await availableRootsRegistry.isRootAvailableForAttester(\n    attester,\n    BigNumber.from(root)\n  );\n  if (isRootAlreadyRegistered) {\n    if (options?.log) {\n      console.log(`\n      Root: ${root} already registered for attester ${availableRootsRegistry.address}`);\n    }\n    return;\n  }\n\n  const actionRegisterRootForAttesterArgs = {\n    availableRootsRegistry: availableRootsRegistry.address,\n    root: root,\n    attester,\n  };\n  await manualConfirmValidity(actionRegisterRootForAttesterArgs, options);\n  const tx = await availableRootsRegistry.registerRootForAttester(\n    actionRegisterRootForAttesterArgs.attester,\n    BigNumber.from(actionRegisterRootForAttesterArgs.root)\n  );\n  await tx.wait();\n}\n\ntask('register-for-attester')\n  .addParam('root', 'Root to update')\n  .addParam('attester', 'Register for this Attester')\n  .addOptionalParam('availableRootsRegistryAddress', 'Root to update')\n  .addFlag('relayed', 'to use with relayer')\n  .setAction(wrapCommonOptions(action));\n"
  },
  {
    "path": "tasks/available-roots-registry/unregister-for-attester.task.ts",
    "content": "import { task } from 'hardhat/config';\nimport { HardhatRuntimeEnvironment } from 'hardhat/types';\nimport { getDeployer } from '../deploy-tasks/utils';\n\nimport { AvailableRootsRegistry__factory } from '../../types';\nimport { manualConfirmValidity } from '../utils/confirm';\nimport { BigNumber, Signer } from 'ethers';\nimport { CommonTaskOptions, wrapCommonOptions } from '../utils';\nimport { getRelayerSigner } from '../utils/relayer';\n\nexport type UnregisterForAttesterArgs = {\n  root: string;\n  attester: string;\n  availableRootsRegistryAddress?: string;\n  relayed?: boolean;\n  options?: CommonTaskOptions;\n};\n\nasync function action(\n  { root, attester, relayed, options, availableRootsRegistryAddress }: UnregisterForAttesterArgs,\n  hre: HardhatRuntimeEnvironment\n): Promise<void> {\n  if (root === '0') {\n    if (options?.log) {\n      console.log(`\n      root undefined for attester ${attester}. Not running the task.\n    `);\n    }\n    return;\n  }\n\n  const signer = relayed ? await getRelayerSigner() : ((await getDeployer(hre)) as Signer);\n\n  const availableRootsRegistry = AvailableRootsRegistry__factory.connect(\n    availableRootsRegistryAddress || (await hre.deployments.get(`AvailableRootsRegistry`)).address,\n    signer\n  );\n\n  const isRootAlreadyRegistered = await availableRootsRegistry.isRootAvailableForAttester(\n    attester,\n    BigNumber.from(root)\n  );\n  if (!isRootAlreadyRegistered) {\n    if (options?.log) {\n      console.log(`\n      Root: ${root} already unregistered for attester ${availableRootsRegistry.address}`);\n    }\n    return;\n  }\n\n  const actionUnregisterRootForAttesterArgs = {\n    availableRootsRegistry: availableRootsRegistry.address,\n    root: root,\n    attester,\n  };\n  await manualConfirmValidity(actionUnregisterRootForAttesterArgs, options);\n  const tx = await availableRootsRegistry.unregisterRootForAttester(\n    actionUnregisterRootForAttesterArgs.attester,\n    BigNumber.from(actionUnregisterRootForAttesterArgs.root)\n  );\n  await tx.wait();\n}\n\ntask('unregister-for-attester')\n  .addParam('root', 'Root to update')\n  .addParam('attester', 'Register for this Attester')\n  .addOptionalParam('availableRootsRegistryAddress', 'Root to update')\n  .addFlag('relayed', 'to use with relayer')\n  .setAction(wrapCommonOptions(action));\n"
  },
  {
    "path": "tasks/deploy-tasks/batch/deploy-core.task.ts",
    "content": "import { task } from 'hardhat/config';\nimport { HardhatRuntimeEnvironment } from 'hardhat/types';\nimport { AttestationsRegistry, Badges, Front } from '../../../types';\nimport {\n  DeployedAttestationsRegistry,\n  DeployAttestationsRegistryArgs,\n} from '../unit/core/deploy-attestations-registry.task';\nimport { DeployBadgesArgs, DeployedBadges } from '../unit/core/deploy-badges.task';\nimport { DeployOptions, wrapCommonDeployOptions } from '../utils';\nimport { DeployedFront, DeployFrontArgs } from '../unit/core/deploy-front.task';\nimport { AuthorizeRangeArgs } from 'tasks/helpers/authorizations/attestations-registry-authorize-range.task';\nimport { AccessControlGrantRoleArgs } from 'tasks/helpers/authorizations/access-control-grant-role.task';\n\nexport interface DeployedCore {\n  attestationsRegistry: AttestationsRegistry;\n  badges: Badges;\n  front: Front;\n}\n\nexport interface DeployCoreArgs {\n  uri?: string;\n  registryOwner?: string;\n  badgeOwner?: string;\n  frontFirstCollectionId: string;\n  frontLastCollectionId: string;\n  options?: DeployOptions;\n}\n\nasync function deploymentAction(\n  {\n    uri,\n    registryOwner,\n    badgeOwner,\n    frontFirstCollectionId,\n    frontLastCollectionId,\n    options,\n  }: DeployCoreArgs,\n  hre: HardhatRuntimeEnvironment\n): Promise<DeployedCore> {\n  if (options?.log) console.log('Deploying Core Contracts');\n\n  // Deployed ERC1155 Default Badge\n  const { badges } = (await hre.run('deploy-badges', {\n    uri,\n    owner: badgeOwner,\n    options,\n  } as DeployBadgesArgs)) as DeployedBadges;\n\n  // Deploy main contract Attestation Registry\n  const { attestationsRegistry } = (await hre.run('deploy-attestations-registry', {\n    owner: registryOwner,\n    badges: badges.address,\n    options,\n  } as DeployAttestationsRegistryArgs)) as DeployedAttestationsRegistry;\n\n  const registeredAttestationsRegistryInBadges = await badges.getAttestationsRegistry();\n  if (registeredAttestationsRegistryInBadges != attestationsRegistry.address) {\n    await (await badges.setAttestationsRegistry(attestationsRegistry.address)).wait();\n  }\n\n  const { front } = (await hre.run('deploy-front', {\n    attestationsRegistryAddress: attestationsRegistry.address,\n    badges: badges.address,\n    options,\n  } as DeployFrontArgs)) as DeployedFront;\n\n  await hre.run('attestations-registry-authorize-range', {\n    attestationsRegistryAddress: attestationsRegistry.address,\n    attesterAddress: front.address,\n    collectionIdFirst: frontFirstCollectionId,\n    collectionIdLast: frontLastCollectionId,\n    options,\n  } as AuthorizeRangeArgs);\n\n  await hre.run('access-control-grant-role', {\n    contractAddress: badges.address,\n    role: await badges.EVENT_TRIGGERER_ROLE(),\n    accountAddress: attestationsRegistry.address,\n    options,\n  } as AccessControlGrantRoleArgs);\n\n  if (options?.log) {\n    console.log(`\n      Deployed core contracts of the sismo protocol: \n      attestationsRegistry: ${attestationsRegistry.address} \n      badges: ${badges.address} \n      front: ${front.address}\n    `);\n  }\n  return { attestationsRegistry, badges, front };\n}\n\ntask('deploy-core')\n  .addOptionalParam('uri', 'uri for the badges')\n  .addOptionalParam('registryOwner', 'owner of the contracts')\n  .addOptionalParam('badgeOwner', 'owner of the contracts')\n  .addOptionalParam('frontFirstCollectionId', 'owner of the contracts')\n  .addOptionalParam('frontLastCollectionId', 'owner of the contracts')\n  .setAction(wrapCommonDeployOptions(deploymentAction));\n"
  },
  {
    "path": "tasks/deploy-tasks/deployments-config.ts",
    "content": "import { DeploymentsConfigTypes } from './utils/deployments-config-types';\n\nconst COMMITMENT_MAPPER_EDDSA_PUB_KEY_PROD = [\n  '0x0c6c16efc72c198f4549bd069f1e57f091885234b9c140286d80ef431151d644',\n  '0x12c54731563d974ead25d469d2263fdf0e230d5a09f6cd40a06e60210610d642',\n];\n\nconst COMMITMENT_MAPPER_EDDSA_PUB_KEY_STAGING = [\n  '0x1e468ad0fcde4edec429cd41eb28a0e78d4f31fa2c25172ef677468b2b38a9dc',\n  '0x2b6e9a8e3b8ed419cca51e2e2ee7ae07d2902454deca17d7da7b00ae4a798add',\n];\n\nconst COMMITMENT_MAPPER_TESTER = [\n  '3268380547641047729088085784617708493474401130426516096643943726492544573596',\n  '15390691699624678165709040191639591743681460873292995904381058558679154201615',\n];\n\nconst COMMITMENT_SIGNER_PUB_KEY_SYNAPS_STAGING = [\n  '0x038d3d596875c66eb90608bbe04e4e238607fd32ad61f4278bdaf47aadc28d60',\n  '0x1b46b55a0ce3f35d6025cdd4554aaac1fc7180b14d51b0f15aa51a6929bdb952',\n];\n\nconst COMMITMENT_SIGNER_PUB_KEY_SYNAPS_PROD = [\n  '0x2f8eeb980ebc1342070a35514e1f42fa96381de8f080d3713e80fe99c883d4e9',\n  '0x018683c5d2f1f71d7e8b65ab0990635c019de9183359db7e80543c485426e490',\n];\n\n// Account 0 of the create2Factory mnemonic\nexport const SISMO_ADDRESSES_PROVIDER_PROXY_DEPLOYER = '0x77694e7C30B74dd271EACA4207Ada0fC10632f5f';\n// Should always be the same for all chains (deployed with create2)\nexport const SISMO_ADDRESSES_PROVIDER_CONTRACT_ADDRESS =\n  '0x3340Ac0CaFB3ae34dDD53dba0d7344C1Cf3EFE05';\n\nconst THREE_DAYS = '295200';\n\n// Mainnet\nconst ALPHA_MAINNET_OWNER = '0xaee4acd5c4Bf516330ca8fe11B07206fC6709294';\nconst ALPHA_MAINNET_ROOTS_OWNER_RELAYER = '0x2a265b954b96d4940b94eb69e8fc8e7346369d05';\nconst ALPHA_MAINNET_PROXY_ADMIN = '0x2110475dfbB8d331b300178A867372991ff35fA3';\n\n// Polygon\nconst ALPHA_POLYGON_OWNER = '0xaee4acd5c4Bf516330ca8fe11B07206fC6709294';\nconst ALPHA_POLYGON_ROOTS_OWNER_RELAYER = '0xf0a0b692e1c764281c211948d03edeef5fb57111';\nconst ALPHA_POLYGON_PROXY_ADMIN = '0x2110475dfbB8d331b300178A867372991ff35fA3';\n// XDai\nconst ALPHA_GNOSIS_OWNER = '0xaee4acd5c4Bf516330ca8fe11B07206fC6709294';\nconst ALPHA_GNOSIS_ROOTS_OWNER_RELAYER = '0xef809a50de35c762fbacf1ae1f6b861ce42911d1';\nconst ALPHA_GNOSIS_PROXY_ADMIN = '0x2110475dfbB8d331b300178A867372991ff35fA3';\n// Polygon Playground\nconst SANDBOX_POLYGON_OWNER = '0xaee4acd5c4Bf516330ca8fe11B07206fC6709294';\nconst SANDBOX_POLYGON_ROOTS_OWNER_RELAYER = '0x7e2305312099748bbd6a31bff27a8218edd4cbd2';\nconst SANDBOX_POLYGON_PROXY_ADMIN = '0x2110475dfbB8d331b300178A867372991ff35fA3';\n\n// Goerli Testnet\nconst ALPHA_GOERLI_TESTNET_OWNER = '0xaee4acd5c4Bf516330ca8fe11B07206fC6709294';\nconst ALPHA_GOERLI_TESTNET_ROOTS_OWNER_RELAYER = '0xa687922c4bf2eb22297fdf89156b49ed3727618b';\nconst ALPHA_GOERLI_TESTNET_PROXY_ADMIN = '0x2110475dfbB8d331b300178A867372991ff35fA3';\n// Mumbai Testnet\nconst ALPHA_MUMBAI_TESTNET_OWNER = '0xaee4acd5c4Bf516330ca8fe11B07206fC6709294';\nconst ALPHA_MUMBAI_TESTNET_ROOTS_OWNER_RELAYER = '0xca0583a6682607282963d3e2545cd2e75697c2bb';\nconst ALPHA_MUMBAI_TESTNET_PROXY_ADMIN = '0x2110475dfbB8d331b300178A867372991ff35fA3';\n\n// Goerli Staging\nconst ALPHA_GOERLI_STAGING_OWNER = '0x4e070E9b85a659F0B7B47cde33152ad6c2F63954';\nconst ALPHA_GOERLI_STAGING_ROOTS_OWNER_RELAYER = '0x7f2e6e158643bcaf85f30c57ae8625f623d82659';\nconst ALPHA_GOERLI_STAGING_PROXY_ADMIN = '0x246E71bC2a257f4BE9C7fAD4664E6D7444844Adc';\n// Mumbai Staging\nconst ALPHA_MUMBAI_STAGING_OWNER = '0x4e070E9b85a659F0B7B47cde33152ad6c2F63954';\nconst ALPHA_MUMBAI_STAGING_ROOTS_OWNER_RELAYER = '0x63f08f8f13126b9eadc76dd683902c61c5115138';\nconst ALPHA_MUMBAI_STAGING_PROXY_ADMIN = '0x246E71bC2a257f4BE9C7fAD4664E6D7444844Adc';\n\nexport const deploymentsConfig: DeploymentsConfigTypes = {\n  mainnet: {\n    deployOptions: {\n      manualConfirm: true,\n      log: true,\n      behindProxy: true,\n      proxyAdmin: ALPHA_MAINNET_PROXY_ADMIN,\n    },\n    badges: {\n      address: '0xe77eb6fb5037bCb11db10b9Ae478A7D01354Ae01',\n      owner: ALPHA_MAINNET_OWNER,\n      // Badges Metadata URI for the Badges contract\n      uri: 'https://hub.sismo.io/badges/mainnet/{id}.json',\n    },\n    front: {\n      address: '0xF518eBd3feeb8dc240b5dE46Ec6C57A0313891c1',\n      collectionIdFirst: '0',\n      collectionIdLast: '10000000',\n    },\n    hydraS1Verifier: {\n      address: '0x9338459cD17c9cE309D47d776e5B5A705586c62C',\n    },\n    hydraS1SimpleAttester: {\n      enableDeployment: false,\n      address: '',\n      collectionIdFirst: '',\n      collectionIdLast: '',\n      initialRoot: '0',\n    },\n    hydraS1AccountboundAttester: {\n      address: '0x0Fb92857855A34F6bFf6f8c42F9673f6e8329406',\n      collectionIdFirst: '10000001',\n      collectionIdLast: '20000000',\n      initialRoot: '0',\n      owner: ALPHA_MAINNET_OWNER,\n    },\n    pythia1Verifier: {\n      address: '0x0aC9a4f4bd025F6c414E72fe8B8Dc2FA4aAFBf21',\n    },\n    synapsPythia1SimpleAttester: {\n      address: '0xdd12CD5EeA2F185E675120044d4A1b9dB99933c2',\n      collectionIdFirst: '30000001',\n      collectionIdLast: '30000100',\n      commitmentSignerPubKeyX: COMMITMENT_SIGNER_PUB_KEY_SYNAPS_PROD[0],\n      commitmentSignerPubKeyY: COMMITMENT_SIGNER_PUB_KEY_SYNAPS_PROD[1],\n      owner: ALPHA_MAINNET_OWNER,\n    },\n    attestationsRegistry: {\n      address: '0xB62F00e4e637e0E1031420D86B84e46BaE2a139F',\n      owner: ALPHA_MAINNET_OWNER,\n    },\n    availableRootsRegistry: {\n      address: '0x5E5e0CEfB86c39dbf3AFf31a61375e2D8eF4D001',\n      owner: ALPHA_MAINNET_ROOTS_OWNER_RELAYER,\n    },\n    commitmentMapper: {\n      address: '0x485ccCC5088873b74d622f505141d950297EC64a',\n      owner: ALPHA_MAINNET_OWNER,\n      EdDSAPubKeyX: COMMITMENT_MAPPER_EDDSA_PUB_KEY_PROD[0],\n      EdDSAPubKeyY: COMMITMENT_MAPPER_EDDSA_PUB_KEY_PROD[1],\n    },\n    sismoAddressesProvider: {\n      address: '',\n      owner: ALPHA_MAINNET_OWNER,\n    },\n  },\n  polygonPlayground: {\n    deployOptions: {\n      manualConfirm: true,\n      log: true,\n      behindProxy: true,\n      proxyAdmin: SANDBOX_POLYGON_PROXY_ADMIN,\n    },\n    badges: {\n      address: '0x71a7089C56DFf528f330Bc0116C0917cd05B51Fc',\n      owner: SANDBOX_POLYGON_OWNER,\n      // Badges Metadata URI for the Badges contract\n      uri: 'https://hub.playground.sismo.io/badges/polygon/{id}.json',\n    },\n    front: {\n      address: '0xfC6f6b50B9Ee651B73B481E2cd221aFffc26a5E4',\n      collectionIdFirst: '0',\n      collectionIdLast: '10000000',\n    },\n    hydraS1Verifier: {\n      address: '0xD029177639cc29042D2E6D9De985cbE762092529',\n    },\n    hydraS1SimpleAttester: {\n      enableDeployment: false,\n      address: '',\n      collectionIdFirst: '',\n      collectionIdLast: '',\n      initialRoot: '0',\n    },\n    hydraS1AccountboundAttester: {\n      address: '0x0AB188c7260666146B300aD3ad5b2AB99eb91D45',\n      collectionIdFirst: '10000001',\n      collectionIdLast: '20000000',\n      initialRoot: '0x0b9340f0d31232adaec900fbaefc0f6fa3f00bda449dd677fa111b58bc754cc9',\n      owner: SANDBOX_POLYGON_OWNER,\n    },\n    pythia1Verifier: {\n      address: '0xb7b327Eb974706B548F8c18Ec0Eb35f0f0c655ef',\n    },\n    synapsPythia1SimpleAttester: {\n      address: '0x5ee338769C0205c19c0Bf21C35A42b1645B89998',\n      collectionIdFirst: '30000001',\n      collectionIdLast: '30000100',\n      commitmentSignerPubKeyX: COMMITMENT_SIGNER_PUB_KEY_SYNAPS_PROD[0],\n      commitmentSignerPubKeyY: COMMITMENT_SIGNER_PUB_KEY_SYNAPS_PROD[1],\n      owner: SANDBOX_POLYGON_OWNER,\n    },\n    attestationsRegistry: {\n      address: '0xC999390A856e0633f945dD851DeeCE15b533ccA3',\n      owner: SANDBOX_POLYGON_OWNER,\n    },\n    availableRootsRegistry: {\n      address: '0xb8797eBa1048f6A6AfCbE4F08a582b4Dde69C05d',\n      owner: SANDBOX_POLYGON_ROOTS_OWNER_RELAYER,\n    },\n    commitmentMapper: {\n      address: '0xC44F46A31B752b773685be6B5ce8616fFeb97C8D',\n      owner: SANDBOX_POLYGON_OWNER,\n      EdDSAPubKeyX: COMMITMENT_MAPPER_EDDSA_PUB_KEY_PROD[0],\n      EdDSAPubKeyY: COMMITMENT_MAPPER_EDDSA_PUB_KEY_PROD[1],\n    },\n    sismoAddressesProvider: {\n      address: '',\n      owner: SANDBOX_POLYGON_OWNER,\n    },\n  },\n  polygon: {\n    deployOptions: {\n      manualConfirm: true,\n      log: true,\n      behindProxy: true,\n      proxyAdmin: ALPHA_POLYGON_PROXY_ADMIN,\n    },\n    badges: {\n      address: '0xF12494e3545D49616D9dFb78E5907E9078618a34',\n      owner: ALPHA_POLYGON_OWNER,\n      // Badges Metadata URI for the Badges contract\n      uri: 'https://hub.sismo.io/badges/polygon/{id}.json',\n    },\n    front: {\n      address: '0x2777b09dd2Cb4E6d62c1823AD074B43DfcC945Fd',\n      collectionIdFirst: '0',\n      collectionIdLast: '10000000',\n    },\n    hydraS1Verifier: {\n      address: '0x36B61F249f61170A49c9bf8Faaf38819eFB9938A',\n    },\n    hydraS1SimpleAttester: {\n      enableDeployment: false,\n      address: '',\n      collectionIdFirst: '',\n      collectionIdLast: '',\n      initialRoot: '0',\n    },\n    hydraS1AccountboundAttester: {\n      address: '0x10b27d9efa4A1B65412188b6f4F29e64Cf5e0146',\n      collectionIdFirst: '10000001',\n      collectionIdLast: '20000000',\n      initialRoot: '0',\n      owner: ALPHA_POLYGON_OWNER,\n    },\n    pythia1Verifier: {\n      address: '0x6f3201339b90Eea1CdB13670d5714ca06a49DfaD',\n    },\n    synapsPythia1SimpleAttester: {\n      address: '0x1918422a3627E701De451f0d4Ed99B8DEaB0C37c',\n      collectionIdFirst: '30000001',\n      collectionIdLast: '30000100',\n      commitmentSignerPubKeyX: COMMITMENT_SIGNER_PUB_KEY_SYNAPS_PROD[0],\n      commitmentSignerPubKeyY: COMMITMENT_SIGNER_PUB_KEY_SYNAPS_PROD[1],\n      owner: ALPHA_POLYGON_OWNER,\n    },\n    attestationsRegistry: {\n      address: '0xa37c32adE310f83B5A9E31b82f72011D5BFb5EFA',\n      owner: ALPHA_POLYGON_OWNER,\n    },\n    availableRootsRegistry: {\n      address: '0xEce747769BD44A7854c8C0913A91Aa801e42D0d0',\n      owner: ALPHA_POLYGON_ROOTS_OWNER_RELAYER,\n    },\n    commitmentMapper: {\n      address: '0x21612Eac9b9Ba69F1810074998E5884Ca14f5614',\n      owner: ALPHA_POLYGON_OWNER,\n      EdDSAPubKeyX: COMMITMENT_MAPPER_EDDSA_PUB_KEY_PROD[0],\n      EdDSAPubKeyY: COMMITMENT_MAPPER_EDDSA_PUB_KEY_PROD[1],\n    },\n    sismoAddressesProvider: {\n      address: '',\n      owner: ALPHA_POLYGON_OWNER,\n    },\n  },\n  // Deployer alpha-prod-gnosis-mnemonic-deployer-dec-19-2022\n  gnosis: {\n    deployOptions: {\n      manualConfirm: true,\n      log: true,\n      behindProxy: true,\n      proxyAdmin: ALPHA_GNOSIS_PROXY_ADMIN,\n    },\n    badges: {\n      address: '0xa67f1C6c96CB5dD6eF24B07A77893693C210d846',\n      owner: ALPHA_GNOSIS_OWNER,\n      // Badges Metadata URI for the Badges contract\n      uri: 'https://hub.sismo.io/badges/gnosis/{id}.json',\n    },\n    front: {\n      address: '0xC21393D2c8E214ccDC37af4220a675fb3B59491A',\n      collectionIdFirst: '0',\n      collectionIdLast: '10000000',\n    },\n    hydraS1Verifier: {\n      address: '0xf219a3a016DD785332A2305bf52544eE189fe233',\n    },\n    hydraS1SimpleAttester: {\n      enableDeployment: false,\n      address: '',\n      collectionIdFirst: '',\n      collectionIdLast: '',\n      initialRoot: '0',\n    },\n    hydraS1AccountboundAttester: {\n      address: '0x0a764805Ad76A740D46C81C9A8978790C227084C',\n      collectionIdFirst: '10000001',\n      collectionIdLast: '20000000',\n      initialRoot: '0',\n      owner: ALPHA_GNOSIS_OWNER,\n    },\n    pythia1Verifier: {\n      address: '0x95f1751c134F3a8BF1ce13827C3c6724049bE692',\n    },\n    synapsPythia1SimpleAttester: {\n      address: '0x919DBe676138ec75Ba03b65F6106EcDEdcE011bD',\n      collectionIdFirst: '30000001',\n      collectionIdLast: '30000100',\n      commitmentSignerPubKeyX: COMMITMENT_SIGNER_PUB_KEY_SYNAPS_PROD[0],\n      commitmentSignerPubKeyY: COMMITMENT_SIGNER_PUB_KEY_SYNAPS_PROD[1],\n      owner: ALPHA_GNOSIS_OWNER,\n    },\n    attestationsRegistry: {\n      address: '0xd0c551dA71B2c3DA65f0bA0500FA4251d26179A8',\n      owner: ALPHA_GNOSIS_OWNER,\n    },\n    availableRootsRegistry: {\n      address: '0x453bF14103CC941A96721de9A32d5E3d3095e049',\n      owner: ALPHA_GNOSIS_ROOTS_OWNER_RELAYER,\n    },\n    commitmentMapper: {\n      address: '0xe205Fb31B656791850AC65f0623937Bf6170a5Da',\n      owner: ALPHA_GNOSIS_OWNER,\n      EdDSAPubKeyX: COMMITMENT_MAPPER_EDDSA_PUB_KEY_PROD[0],\n      EdDSAPubKeyY: COMMITMENT_MAPPER_EDDSA_PUB_KEY_PROD[1],\n    },\n    sismoAddressesProvider: {\n      address: '',\n      owner: ALPHA_GNOSIS_OWNER,\n    },\n  },\n  // deployer: alpha-testnets-goerli-mnemonic-deployer-dec-15-2022\n  goerliTestnet: {\n    deployOptions: {\n      manualConfirm: true,\n      log: true,\n      behindProxy: true,\n      proxyAdmin: ALPHA_GOERLI_TESTNET_PROXY_ADMIN,\n    },\n    badges: {\n      address: '0xA251eb9Be4e7E2bb382268eCdd0a5fca0A962E6c',\n      owner: ALPHA_GOERLI_TESTNET_OWNER,\n      // Badges Metadata URI for the Badges contract\n      uri: 'https://hub.testnets.sismo.io/badges/goerli/{id}.json',\n    },\n    front: {\n      address: '0x40713429614c47e94bC078069Df9C084fb44edfC',\n      collectionIdFirst: '0',\n      collectionIdLast: '10000000',\n    },\n    hydraS1Verifier: {\n      address: '0x6F470D565Effa8a2594B0BDB9E63Cd21D58FAC0f',\n    },\n    hydraS1SimpleAttester: {\n      enableDeployment: false,\n      address: '',\n      collectionIdFirst: '',\n      collectionIdLast: '',\n      initialRoot: '0',\n    },\n    hydraS1AccountboundAttester: {\n      address: '0x319d2a1f99DCE97bC1539643Df7cD7A0a978Eb7B',\n      collectionIdFirst: '10000001',\n      collectionIdLast: '20000000',\n      initialRoot: '0',\n      owner: ALPHA_GOERLI_TESTNET_OWNER,\n    },\n    pythia1Verifier: {\n      address: '0xd6f79719F8e286987417F9910c0ED42AbF95B0EA',\n    },\n    synapsPythia1SimpleAttester: {\n      address: '0x65130b44f33E2E97Ea7031412eAFf7d5FC7bf9ad',\n      collectionIdFirst: '30000001',\n      collectionIdLast: '30000100',\n      commitmentSignerPubKeyX: COMMITMENT_SIGNER_PUB_KEY_SYNAPS_PROD[0],\n      commitmentSignerPubKeyY: COMMITMENT_SIGNER_PUB_KEY_SYNAPS_PROD[1],\n      owner: ALPHA_GOERLI_TESTNET_OWNER,\n    },\n    attestationsRegistry: {\n      address: '0x7c0F3ba8e29ad7e28CA805933d6d43b35983B2b3',\n      owner: ALPHA_GOERLI_TESTNET_OWNER,\n    },\n    availableRootsRegistry: {\n      address: '0x3be8DF71fc13312411F0d20d26C08E822fE9cF1f',\n      owner: ALPHA_GOERLI_TESTNET_ROOTS_OWNER_RELAYER,\n    },\n    commitmentMapper: {\n      address: '0x60021A3B6a2bC5b27FeAc52C091f5f672B6b7B53',\n      owner: ALPHA_GOERLI_TESTNET_OWNER,\n      EdDSAPubKeyX: COMMITMENT_MAPPER_EDDSA_PUB_KEY_PROD[0],\n      EdDSAPubKeyY: COMMITMENT_MAPPER_EDDSA_PUB_KEY_PROD[1],\n    },\n    sismoAddressesProvider: {\n      address: SISMO_ADDRESSES_PROVIDER_CONTRACT_ADDRESS,\n      owner: ALPHA_GOERLI_TESTNET_OWNER,\n    },\n  },\n  // deployer: alpha-testnets-mumbai-mnemonic-deployer-dec-15-2022\n  mumbaiTestnet: {\n    deployOptions: {\n      manualConfirm: true,\n      log: true,\n      behindProxy: true,\n      proxyAdmin: ALPHA_MUMBAI_TESTNET_PROXY_ADMIN,\n    },\n    badges: {\n      address: '0xc3Ee5Aad6Fb987cF69a2EE7B3B2c92E21E42F34B',\n      owner: ALPHA_MUMBAI_TESTNET_OWNER,\n      // Badges Metadata URI for the Badges contract\n      uri: 'https://hub.testnets.sismo.io/badges/mumbai/{id}.json',\n    },\n    front: {\n      address: '0xcAf720C39bcdF47476aDc0618e6d7B57B7533213',\n      collectionIdFirst: '0',\n      collectionIdLast: '10000000',\n    },\n    hydraS1Verifier: {\n      address: '0x09f35a46C6863F5C8Cd887F690974DCaaDe0A2a5',\n    },\n    hydraS1SimpleAttester: {\n      enableDeployment: false,\n      address: '',\n      collectionIdFirst: '',\n      collectionIdLast: '',\n      initialRoot: '0',\n    },\n    hydraS1AccountboundAttester: {\n      address: '0xEe6c299A09d352caf53C81621f6D757c7C0B4d7c',\n      collectionIdFirst: '10000001',\n      collectionIdLast: '20000000',\n      initialRoot: '0',\n      owner: ALPHA_MUMBAI_TESTNET_OWNER,\n    },\n    pythia1Verifier: {\n      address: '0xDA7124b003AeF27A1C44aEe4d6eB6E2Bb0eF9a08',\n    },\n    synapsPythia1SimpleAttester: {\n      address: '0xBbb56145d961742b1f3E3fc2b91077639C8C302a',\n      collectionIdFirst: '30000001',\n      collectionIdLast: '30000100',\n      commitmentSignerPubKeyX: COMMITMENT_SIGNER_PUB_KEY_SYNAPS_PROD[0],\n      commitmentSignerPubKeyY: COMMITMENT_SIGNER_PUB_KEY_SYNAPS_PROD[1],\n      owner: ALPHA_MUMBAI_TESTNET_OWNER,\n    },\n    attestationsRegistry: {\n      address: '0xc24F86a8D9f82401b693d4FFaa1DCf3109d88524',\n      owner: ALPHA_MUMBAI_TESTNET_OWNER,\n    },\n    availableRootsRegistry: {\n      address: '0xe51e46177505c31CE33791066E17E11d9D180305',\n      owner: ALPHA_MUMBAI_TESTNET_ROOTS_OWNER_RELAYER,\n    },\n    commitmentMapper: {\n      address: '0xE6C1f01C71A184A44A21B10A7FBEb3b1604B4f2a',\n      owner: ALPHA_MUMBAI_TESTNET_OWNER,\n      EdDSAPubKeyX: COMMITMENT_MAPPER_EDDSA_PUB_KEY_PROD[0],\n      EdDSAPubKeyY: COMMITMENT_MAPPER_EDDSA_PUB_KEY_PROD[1],\n    },\n    sismoAddressesProvider: {\n      address: '',\n      owner: ALPHA_MUMBAI_TESTNET_OWNER,\n    },\n  },\n\n  /////////////////////////////////////////////////////////////////////////\n  //                             STAGING                                 //\n  /////////////////////////////////////////////////////////////////////////\n  // deployer \"alpha-testnets-mumbai-mnemonic-deployer-october-13-2022\"\n  mumbaiStaging: {\n    deployOptions: {\n      manualConfirm: true,\n      log: true,\n      behindProxy: true,\n      proxyAdmin: ALPHA_MUMBAI_STAGING_PROXY_ADMIN,\n    },\n    badges: {\n      address: '0x5722fEa81027533721BA161964622271560da1aC',\n      owner: ALPHA_MUMBAI_STAGING_OWNER,\n      // Badges Metadata URI for the Badges contract\n      uri: 'https://hub.staging.zikies.io/badges/mumbai/{id}.json',\n    },\n    front: {\n      address: '0xFD0395fEb7805447e84Eb439a543413ecb22d562',\n      collectionIdFirst: '0',\n      collectionIdLast: '10000000',\n    },\n    hydraS1Verifier: {\n      address: '0xCBB1032416EB875D94Da8a447D73471E4C51998E',\n    },\n    hydraS1SimpleAttester: {\n      enableDeployment: false,\n      address: '',\n      collectionIdFirst: '',\n      collectionIdLast: '',\n      initialRoot: '0',\n    },\n    hydraS1AccountboundAttester: {\n      address: '0x069e6B99f4DA543156f66274FC6673442803C587',\n      collectionIdFirst: '10000001',\n      collectionIdLast: '20000000',\n      initialRoot: '0',\n      owner: ALPHA_MUMBAI_STAGING_OWNER,\n    },\n    pythia1Verifier: {\n      address: '0x115dFa344C877fF74e970F06BE10FF5A59EAba02',\n    },\n    synapsPythia1SimpleAttester: {\n      address: '0x1586190051bf7bb0b754A7AA7CDde21E920ad009',\n      collectionIdFirst: '30000001',\n      collectionIdLast: '30000100',\n      commitmentSignerPubKeyX: COMMITMENT_SIGNER_PUB_KEY_SYNAPS_STAGING[0],\n      commitmentSignerPubKeyY: COMMITMENT_SIGNER_PUB_KEY_SYNAPS_STAGING[1],\n      owner: ALPHA_MUMBAI_STAGING_OWNER,\n    },\n    attestationsRegistry: {\n      address: '0xf576E42E5b2682B8f606B1840c3A982610C29a3f',\n      owner: ALPHA_MUMBAI_STAGING_OWNER,\n    },\n    availableRootsRegistry: {\n      address: '0x2c17e335d131dfd21238475Dd545B9B29Fb5A27D',\n      owner: ALPHA_MUMBAI_STAGING_ROOTS_OWNER_RELAYER,\n    },\n    commitmentMapper: {\n      address: '0x82B54988e4E3a2850501F91AC06CEA82bdb014D3',\n      owner: ALPHA_MUMBAI_STAGING_OWNER,\n      EdDSAPubKeyX: COMMITMENT_MAPPER_EDDSA_PUB_KEY_STAGING[0],\n      EdDSAPubKeyY: COMMITMENT_MAPPER_EDDSA_PUB_KEY_STAGING[1],\n    },\n    sismoAddressesProvider: {\n      address: '',\n      owner: ALPHA_MUMBAI_STAGING_OWNER,\n    },\n  },\n  // deployer \"alpha-testnets-goerli-mnemonic-deployer-october-4-2022\"\n  goerliStaging: {\n    deployOptions: {\n      manualConfirm: true,\n      log: true,\n      behindProxy: true,\n      proxyAdmin: ALPHA_GOERLI_STAGING_PROXY_ADMIN,\n    },\n    badges: {\n      address: '0xE06B14D5835925e1642d7216F4563a1273509F10',\n      owner: ALPHA_GOERLI_STAGING_OWNER,\n      // Badges Metadata URI for the Badges contract\n      uri: 'https://hub.staging.zikies.io/badges/goerli/{id}.json',\n    },\n    front: {\n      address: '0xAa00539FCD89E113833a9fCb940F378aE1299e30',\n      collectionIdFirst: '0',\n      collectionIdLast: '10000000',\n    },\n    hydraS1Verifier: {\n      address: '0xe1bFE993cF8dB9a3214Cc77fD68194Ac534078b4',\n    },\n    hydraS1SimpleAttester: {\n      enableDeployment: false,\n      address: '',\n      collectionIdFirst: '',\n      collectionIdLast: '',\n      initialRoot: '0',\n    },\n    hydraS1AccountboundAttester: {\n      address: '0x89d80C9E65fd1aC8970B78A4F17E2e772030C1cB',\n      collectionIdFirst: '10000001',\n      collectionIdLast: '20000000',\n      initialRoot: '0',\n      owner: ALPHA_GOERLI_STAGING_OWNER,\n    },\n    pythia1Verifier: {\n      address: '0xEE077AD7a47e56F075f0C3bd41Cdc1629FdA3a9c',\n    },\n    synapsPythia1SimpleAttester: {\n      address: '0x8E44f33Df343EA6f85380226BE5Fbf93db09168E',\n      collectionIdFirst: '30000001',\n      collectionIdLast: '30000100',\n      commitmentSignerPubKeyX: COMMITMENT_SIGNER_PUB_KEY_SYNAPS_STAGING[0],\n      commitmentSignerPubKeyY: COMMITMENT_SIGNER_PUB_KEY_SYNAPS_STAGING[1],\n      owner: ALPHA_GOERLI_STAGING_OWNER,\n    },\n    attestationsRegistry: {\n      address: '0xf85BA0afA375495eE625910Db61b6b1406756234',\n      owner: ALPHA_GOERLI_STAGING_OWNER,\n    },\n    availableRootsRegistry: {\n      address: '0xdDa4c8d2933dAA21Aac75B88fF59725725ba813F',\n      owner: ALPHA_GOERLI_STAGING_ROOTS_OWNER_RELAYER,\n    },\n    commitmentMapper: {\n      address: '0x0844662f25817B735BC9B6d9D11995F1A6c4dCB1',\n      owner: ALPHA_GOERLI_STAGING_OWNER,\n      EdDSAPubKeyX: COMMITMENT_MAPPER_EDDSA_PUB_KEY_STAGING[0],\n      EdDSAPubKeyY: COMMITMENT_MAPPER_EDDSA_PUB_KEY_STAGING[1],\n    },\n    sismoAddressesProvider: {\n      address: '0x52097335aC9f74eD6751F29B796110A0eD9096E7',\n      owner: ALPHA_GOERLI_STAGING_OWNER,\n    },\n  },\n  local: {\n    deployOptions: {\n      manualConfirm: false,\n      log: true,\n      behindProxy: true,\n      // account 18 of shared mnemonic\n      proxyAdmin: '0x41EA85211c08227BD62B03f3EFc65FaAa6CBd1C3',\n    },\n    badges: {\n      address: '0xeF5b2Be9a6075a61bCA4384abc375485d5e196c3',\n      // account 0 of shared mneomonic\n      owner: '0xb01ee322C4f028B8A6BFcD2a5d48107dc5bC99EC',\n      uri: 'https://metadata-dev.badges.zikies.io/default-badges/local/{id}.json',\n    },\n    front: {\n      address: '0x7f1624094ACe6cd9653A8c3C3D92F2fAbb241B07',\n      collectionIdFirst: '0',\n      collectionIdLast: '10000000',\n    },\n    hydraS1Verifier: {\n      address: '0xe52EB1f52349F98c09a5B11B0E3fA7f269268Add',\n    },\n    hydraS1SimpleAttester: {\n      enableDeployment: false,\n      address: '',\n      collectionIdFirst: '',\n      collectionIdLast: '',\n      initialRoot: '0',\n    },\n    hydraS1AccountboundAttester: {\n      address: '0x9e5c3999d6e97b1a6FFC9dECBfF01Fc185d2268D',\n      collectionIdFirst: '10000001',\n      collectionIdLast: '20000000',\n      initialRoot: '0x0deb3822cd7d8c6ece7456c8e7ff81d61c8991390072f2cee0f711102741e259',\n      owner: '0xb01ee322C4f028B8A6BFcD2a5d48107dc5bC99EC',\n    },\n    pythia1Verifier: {\n      address: '0xeF6890243012206C1eE0666e00289AC8a97D1b2B',\n    },\n    synapsPythia1SimpleAttester: {\n      address: '0x2F4B0fA432354efe20246118fA736c896E726Ea4',\n      collectionIdFirst: '30000001',\n      collectionIdLast: '30000100',\n      commitmentSignerPubKeyX: COMMITMENT_SIGNER_PUB_KEY_SYNAPS_STAGING[0],\n      commitmentSignerPubKeyY: COMMITMENT_SIGNER_PUB_KEY_SYNAPS_STAGING[1],\n      owner: '0xb01ee322C4f028B8A6BFcD2a5d48107dc5bC99EC',\n    },\n    attestationsRegistry: {\n      address: '0x3D7220043f746FA5a087cD53460D48a5C0990980',\n      owner: '0xb01ee322C4f028B8A6BFcD2a5d48107dc5bC99EC',\n    },\n    availableRootsRegistry: {\n      address: '0x6c851BdD9B51436846F6eF815eBB47E20D1d6c7B',\n      owner: '0xb01ee322C4f028B8A6BFcD2a5d48107dc5bC99EC',\n    },\n    commitmentMapper: {\n      address: '0x175ECF3064CF2D6818D3eF3a84b29421C4df1E52',\n      owner: '0xb01ee322C4f028B8A6BFcD2a5d48107dc5bC99EC',\n      EdDSAPubKeyX: '0x1e468ad0fcde4edec429cd41eb28a0e78d4f31fa2c25172ef677468b2b38a9dc',\n      EdDSAPubKeyY: '0x2b6e9a8e3b8ed419cca51e2e2ee7ae07d2902454deca17d7da7b00ae4a798add',\n    },\n    sismoAddressesProvider: {\n      address: SISMO_ADDRESSES_PROVIDER_CONTRACT_ADDRESS,\n      owner: '0xb01ee322C4f028B8A6BFcD2a5d48107dc5bC99EC',\n    },\n  },\n  hardhat: {\n    deployOptions: {\n      manualConfirm: false,\n      log: false,\n      behindProxy: true,\n      // account 13 of shared mnemonic\n      proxyAdmin: '0x569eaB3c91828B7d9f951d335bC12A6aABEc1458',\n    },\n    badges: {\n      address: '',\n      // account 0 of shared mneomonic\n      owner: '0xb01ee322C4f028B8A6BFcD2a5d48107dc5bC99EC',\n      uri: 'https://metadata-dev.badges.zikies.io/default-badges/local/{id}.json',\n    },\n    front: {\n      address: '',\n      collectionIdFirst: '0',\n      collectionIdLast: '100000',\n    },\n    hydraS1Verifier: {\n      address: '',\n    },\n    hydraS1SimpleAttester: {\n      enableDeployment: true,\n      address: '',\n      collectionIdFirst: '100001',\n      collectionIdLast: '200000',\n      initialRoot: '0x0deb3822cd7d8c6ece7456c8e7ff81d61c8991390072f2cee0f711102741e259',\n    },\n    hydraS1AccountboundAttester: {\n      address: '',\n      collectionIdFirst: '200001',\n      collectionIdLast: '300000',\n      initialRoot: '0x0deb3822cd7d8c6ece7456c8e7ff81d61c8991390072f2cee0f711102741e259',\n      owner: '0xb01ee322C4f028B8A6BFcD2a5d48107dc5bC99EC',\n    },\n    pythia1Verifier: {\n      address: '',\n    },\n    synapsPythia1SimpleAttester: {\n      address: '',\n      collectionIdFirst: '30000001',\n      collectionIdLast: '30000100',\n      commitmentSignerPubKeyX: '0x2a7c304da200c5ee9488d35c604e0384e123a716f7399df22fc2ec9074301dae',\n      commitmentSignerPubKeyY: '0x1cf4be3d4c5f0b3eac19493ca98a05490a06623c7937f6d83fe121756a132242',\n      owner: '0xb01ee322C4f028B8A6BFcD2a5d48107dc5bC99EC',\n    },\n    attestationsRegistry: {\n      address: '',\n      owner: '0xb01ee322c4f028b8a6bfcd2a5d48107dc5bc99ec',\n    },\n    availableRootsRegistry: {\n      address: '',\n      owner: '0xb01ee322c4f028b8a6bfcd2a5d48107dc5bc99ec',\n    },\n    commitmentMapper: {\n      address: '',\n      owner: '0xb01ee322c4f028b8a6bfcd2a5d48107dc5bc99ec',\n      EdDSAPubKeyX: COMMITMENT_MAPPER_TESTER[0],\n      EdDSAPubKeyY: COMMITMENT_MAPPER_TESTER[1],\n    },\n    sismoAddressesProvider: {\n      address: SISMO_ADDRESSES_PROVIDER_CONTRACT_ADDRESS,\n      owner: '0xb01ee322c4f028b8a6bfcd2a5d48107dc5bc99ec',\n    },\n  },\n};\n"
  },
  {
    "path": "tasks/deploy-tasks/full/0-deploy-core-and-hydra-s1-simple-and-accountbound-and-pythia1.task.ts",
    "content": "import { task } from 'hardhat/config';\nimport { HardhatRuntimeEnvironment } from 'hardhat/types';\nimport {\n  DeployHydraS1SimpleAttesterArgs,\n  DeployedHydraS1SimpleAttester,\n} from '../unit/attesters/hydra-s1/deploy-hydra-s1-simple-attester.task';\nimport {\n  DeployCommitmentMapperArgs,\n  DeployedCommitmentMapper,\n} from '../unit/periphery/deploy-commitment-mapper-registry.task';\nimport {\n  DeployedAvailableRootsRegistry,\n  DeployAvailableRootsRegistry,\n} from '../unit/periphery/deploy-available-roots-registry.task';\nimport {\n  DeployedHydraS1AccountboundAttester,\n  DeployHydraS1AccountboundAttesterArgs,\n} from '../unit/attesters/hydra-s1/deploy-hydra-s1-accountbound-attester.task';\nimport { DeployOptions, getDeployer } from '../utils';\nimport { DeployCoreArgs, DeployedCore } from '../batch/deploy-core.task';\nimport { deploymentsConfig } from '../deployments-config';\nimport { getCommonOptions } from '../../utils/common-options';\nimport { OwnableTransferOwnershipArgs } from '../../helpers/authorizations/ownable-transfer-ownership.task';\nimport { AuthorizeRangeArgs } from '../../helpers/authorizations/attestations-registry-authorize-range.task';\nimport { AccessControlGrantRoleArgs } from '../../helpers/authorizations/access-control-grant-role.task';\nimport {\n  AttestationsRegistry,\n  AvailableRootsRegistry,\n  Badges,\n  CommitmentMapperRegistry,\n  Front,\n  HydraS1SimpleAttester,\n  HydraS1Verifier,\n  HydraS1AccountboundAttester,\n  Pythia1SimpleAttester,\n} from 'types';\nimport {\n  DeployedPythia1SimpleAttester,\n  DeployPythia1SimpleAttesterArgs,\n} from 'tasks/deploy-tasks/unit/attesters/pythia-1/deploy-pythia-1-simple-attester.task';\nimport { Pythia1Verifier } from '@sismo-core/pythia-1';\n\nexport interface Deployed0 {\n  attestationsRegistry: AttestationsRegistry;\n  badges: Badges;\n  front: Front;\n  commitmentMapperRegistry: CommitmentMapperRegistry;\n  availableRootsRegistry: AvailableRootsRegistry;\n  hydraS1SimpleAttester?: HydraS1SimpleAttester | undefined;\n  hydraS1AccountboundAttester: HydraS1AccountboundAttester;\n  hydraS1Verifier: HydraS1Verifier;\n  pythia1Verifier: Pythia1Verifier;\n  pythia1SimpleAttester: Pythia1SimpleAttester;\n}\n\nasync function deploymentAction(\n  { options }: { options: DeployOptions },\n  hre: HardhatRuntimeEnvironment\n): Promise<Deployed0> {\n  const deployer = await getDeployer(hre);\n  const config = deploymentsConfig[hre.network.name];\n  options = { ...config.deployOptions, ...options };\n  if (options.manualConfirm || options.log) {\n    console.log(\n      '0-deploy-core-and-hydra-s1-simple-and-accountbound-and-pythia1: ',\n      hre.network.name\n    );\n  }\n  // Only deploy contracts without giving final ownership.\n  // Owners of the different contract are the deployer\n  const { attestationsRegistry, badges, front } = (await hre.run('deploy-core', {\n    uri: config.badges.uri,\n    frontFirstCollectionId: config.front.collectionIdFirst,\n    frontLastCollectionId: config.front.collectionIdLast,\n    registryOwner: deployer.address,\n    badgeOwner: deployer.address,\n    options,\n  } as DeployCoreArgs)) as DeployedCore;\n\n  const { availableRootsRegistry } = (await hre.run('deploy-available-roots-registry', {\n    options,\n  })) as DeployAvailableRootsRegistry as DeployedAvailableRootsRegistry;\n\n  const { commitmentMapperRegistry } = (await hre.run('deploy-commitment-mapper-registry', {\n    commitmentMapperPubKeyX: config.commitmentMapper.EdDSAPubKeyX,\n    commitmentMapperPubKeyY: config.commitmentMapper.EdDSAPubKeyY,\n    options,\n  } as DeployCommitmentMapperArgs)) as DeployedCommitmentMapper;\n\n  const hydraS1SimpleArgs: DeployHydraS1SimpleAttesterArgs = {\n    enableDeployment: config.hydraS1SimpleAttester.enableDeployment,\n    collectionIdFirst: config.hydraS1SimpleAttester.collectionIdFirst,\n    collectionIdLast: config.hydraS1SimpleAttester.collectionIdLast,\n    commitmentMapperRegistryAddress: commitmentMapperRegistry.address,\n    availableRootsRegistryAddress: availableRootsRegistry.address,\n    attestationsRegistryAddress: attestationsRegistry.address,\n    options,\n  };\n\n  const { hydraS1SimpleAttester, hydraS1Verifier } = (await hre.run(\n    'deploy-hydra-s1-simple-attester',\n    hydraS1SimpleArgs\n  )) as DeployedHydraS1SimpleAttester;\n\n  const accountboundArgs: DeployHydraS1AccountboundAttesterArgs = {\n    collectionIdFirst: config.hydraS1AccountboundAttester.collectionIdFirst,\n    collectionIdLast: config.hydraS1AccountboundAttester.collectionIdLast,\n    commitmentMapperRegistryAddress: commitmentMapperRegistry.address,\n    availableRootsRegistryAddress: availableRootsRegistry.address,\n    attestationsRegistryAddress: attestationsRegistry.address,\n    owner: deployer.address,\n    options,\n  };\n\n  const { hydraS1AccountboundAttester } = (await hre.run(\n    'deploy-hydra-s1-accountbound-attester',\n    accountboundArgs\n  )) as DeployedHydraS1AccountboundAttester;\n\n  const pythia1SimpleAttesterArgs: DeployPythia1SimpleAttesterArgs = {\n    collectionIdFirst: config.synapsPythia1SimpleAttester.collectionIdFirst,\n    collectionIdLast: config.synapsPythia1SimpleAttester.collectionIdLast,\n    attestationsRegistryAddress: attestationsRegistry.address,\n    commitmentSignerPubKeyX: config.synapsPythia1SimpleAttester.commitmentSignerPubKeyX,\n    commitmentSignerPubKeyY: config.synapsPythia1SimpleAttester.commitmentSignerPubKeyY,\n    owner: config.synapsPythia1SimpleAttester.owner,\n    options,\n  };\n\n  const { pythia1SimpleAttester, pythia1Verifier } = (await hre.run(\n    'deploy-pythia-1-simple-attester',\n    pythia1SimpleAttesterArgs\n  )) as DeployedPythia1SimpleAttester;\n\n  // Register an initial root for attester\n  if (hydraS1SimpleAttester && (options.manualConfirm || options.log)) {\n    console.log(`\n    ----------------------------------------------------------------\n    * Register initial root for hydraS1SimpleAttester attester`);\n  }\n  if (hydraS1SimpleAttester) {\n    await hre.run('register-for-attester', {\n      availableRootsRegistryAddress: availableRootsRegistry.address,\n      attester: hydraS1SimpleAttester.address,\n      root: config.hydraS1SimpleAttester.initialRoot,\n      options: getCommonOptions(options),\n    });\n  }\n\n  if (options.manualConfirm || options.log) {\n    console.log(`\n    ----------------------------------------------------------------\n    * Register initial root for HydraS1AccountboundAttester attester`);\n  }\n  await hre.run('register-for-attester', {\n    availableRootsRegistryAddress: availableRootsRegistry.address,\n    attester: hydraS1AccountboundAttester.address,\n    root: config.hydraS1AccountboundAttester.initialRoot,\n    options: getCommonOptions(options),\n  });\n\n  // Give to the attester the authorization to write on the attestations Registry\n  if (hydraS1SimpleAttester && (options.manualConfirm || options.log)) {\n    console.log(`\n    ----------------------------------------------------------------\n    * Authorize HydraS1SimpleAttester to record on the AttestationsRegistry`);\n  }\n  if (hydraS1SimpleAttester) {\n    await hre.run('attestations-registry-authorize-range', {\n      attestationsRegistryAddress: attestationsRegistry.address,\n      attesterAddress: hydraS1SimpleAttester.address,\n      collectionIdFirst: config.hydraS1SimpleAttester.collectionIdFirst,\n      collectionIdLast: config.hydraS1SimpleAttester.collectionIdLast,\n      options: getCommonOptions(options),\n    } as AuthorizeRangeArgs);\n  }\n\n  if (options.manualConfirm || options.log) {\n    console.log(`\n    ----------------------------------------------------------------\n    * Authorize HydraS1AccountboundAttester to record on the AttestationsRegistry`);\n  }\n  await hre.run('attestations-registry-authorize-range', {\n    attestationsRegistryAddress: attestationsRegistry.address,\n    attesterAddress: hydraS1AccountboundAttester.address,\n    collectionIdFirst: config.hydraS1AccountboundAttester.collectionIdFirst,\n    collectionIdLast: config.hydraS1AccountboundAttester.collectionIdLast,\n    options: getCommonOptions(options),\n  } as AuthorizeRangeArgs);\n\n  if (options.manualConfirm || options.log) {\n    console.log(`\n    ----------------------------------------------------------------\n    * Authorize Pythia1SimpleAttester to record on the AttestationsRegistry`);\n  }\n  await hre.run('attestations-registry-authorize-range', {\n    attestationsRegistryAddress: attestationsRegistry.address,\n    attesterAddress: pythia1SimpleAttester.address,\n    collectionIdFirst: config.synapsPythia1SimpleAttester.collectionIdFirst,\n    collectionIdLast: config.synapsPythia1SimpleAttester.collectionIdLast,\n    options: getCommonOptions(options),\n  } as AuthorizeRangeArgs);\n\n  // ----------  SET FINAL OWNERSHIP -------------\n  if (options.manualConfirm || options.log) {\n    console.log(`\n    ----------------------------------------------------------------\n    * Transfer AttestationsRegistry ownership`);\n  }\n  await hre.run('ownable-transfer-ownership', {\n    contractAddress: attestationsRegistry.address,\n    newOwner: config.attestationsRegistry.owner,\n    options: getCommonOptions(options),\n  } as OwnableTransferOwnershipArgs);\n\n  // Move commitmentMapper ownership\n  if (options.manualConfirm || options.log) {\n    console.log(`\n    ----------------------------------------------------------------\n    * Transfer CommitmentMapperRegistry ownership`);\n  }\n  await hre.run('ownable-transfer-ownership', {\n    contractAddress: commitmentMapperRegistry.address,\n    newOwner: config.commitmentMapper.owner,\n    options: getCommonOptions(options),\n  } as OwnableTransferOwnershipArgs);\n\n  // Move AvailableRootsRegistry ownership\n  if (options.manualConfirm || options.log) {\n    console.log(`\n    ----------------------------------------------------------------\n    * Transfer AvailableRootsRegistry ownership`);\n  }\n  await hre.run('ownable-transfer-ownership', {\n    contractAddress: availableRootsRegistry.address,\n    newOwner: config.availableRootsRegistry.owner,\n    options: getCommonOptions(options),\n  } as OwnableTransferOwnershipArgs);\n\n  // Move admin ownership of the access control contract to the \"owner\".\n  if (options.manualConfirm || options.log) {\n    console.log(`\n    ----------------------------------------------------------------\n    * Granting role DEFAULT_ADMIN_ROLE of Badges to the Badges contract owner`);\n  }\n  await hre.run('access-control-grant-role', {\n    contractAddress: badges.address,\n    role: await badges.DEFAULT_ADMIN_ROLE(),\n    accountAddress: config.badges.owner,\n    options: getCommonOptions(options),\n  } as AccessControlGrantRoleArgs);\n  if (options.manualConfirm || options.log) {\n    console.log(`\n    ----------------------------------------------------------------\n    * Revoking role DEFAULT_ADMIN_ROLE of the deployer to the Badges contract`);\n  }\n  await hre.run('access-control-revoke-role', {\n    contractAddress: badges.address,\n    role: await badges.DEFAULT_ADMIN_ROLE(),\n    accountAddress: deployer.address,\n    options: getCommonOptions(options),\n  } as AccessControlGrantRoleArgs);\n\n  if (options.manualConfirm || options.log) {\n    console.log(`\n    ************************************************************\n    *                           RECAP                          *\n    ************************************************************\n\n    date: ${new Date().toISOString()}\n\n    ** Common **\n      proxyAdmin: ${config.deployOptions.proxyAdmin}\n\n    * Front\n      -> proxy: ${(await hre.deployments.all()).Front.address}\n      -> implem: ${(await hre.deployments.all()).FrontImplem.address}\n\n    * AttestationsRegistry\n      -> proxy: ${(await hre.deployments.all()).AttestationsRegistry.address}\n      -> implem: ${(await hre.deployments.all()).AttestationsRegistryImplem.address}\n      owner: ${config.attestationsRegistry.owner}\n    \n    * Badges\n      -> proxy: ${(await hre.deployments.all()).Badges.address}\n      -> implem: ${(await hre.deployments.all()).BadgesImplem.address}\n      uri: ${config.badges.uri}\n    ${\n      hydraS1SimpleAttester\n        ? `\n    * HydraS1SimpleAttester:\n      -> proxy: ${(await hre.deployments.all()).HydraS1SimpleAttester.address}\n      -> implem: ${(await hre.deployments.all()).HydraS1SimpleAttesterImplem.address}\n      collectionIdFirst: ${config.hydraS1SimpleAttester.collectionIdFirst}\n      collectionIdLast: ${config.hydraS1SimpleAttester.collectionIdLast}\n    `\n        : ''\n    }\n    * HydraS1Verifier:\n      -> address: ${(await hre.deployments.all()).HydraS1Verifier.address}\n\n    * HydraS1AccountboundAttester:\n      -> proxy: ${(await hre.deployments.all()).HydraS1AccountboundAttester.address}\n      -> implem: ${(await hre.deployments.all()).HydraS1AccountboundAttesterImplem.address}\n      collectionIdFirst: ${config.hydraS1AccountboundAttester.collectionIdFirst}\n      collectionIdLast: ${config.hydraS1AccountboundAttester.collectionIdLast}\n    \n    * AvailableRootsRegistry: \n      -> proxy: ${(await hre.deployments.all()).AvailableRootsRegistry.address}\n      -> implem: ${(await hre.deployments.all()).AvailableRootsRegistryImplem.address}\n      owner: ${config.availableRootsRegistry.owner}\n    \n    * CommitmentMapperRegistry: \n      -> proxy: ${(await hre.deployments.all()).CommitmentMapperRegistry.address}\n      -> implem: ${(await hre.deployments.all()).CommitmentMapperRegistryImplem.address}\n      owner: ${config.commitmentMapper.owner}\n\n    * Pythia1SimpleAttester:\n      -> proxy: ${(await hre.deployments.all()).Pythia1SimpleAttester.address}\n      -> implem: ${(await hre.deployments.all()).Pythia1SimpleAttesterImplem.address}\n      collectionIdFirst: ${config.synapsPythia1SimpleAttester.collectionIdFirst}\n      collectionIdLast: ${config.synapsPythia1SimpleAttester.collectionIdLast}\n      commitmentSignerPubKeyX: ${config.synapsPythia1SimpleAttester.commitmentSignerPubKeyX}\n      commitmentSignerPubKeyY: ${config.synapsPythia1SimpleAttester.commitmentSignerPubKeyY}\n\n    * Pythia1Verifier:\n      -> address: ${(await hre.deployments.all()).Pythia1Verifier.address}\n  `);\n  }\n\n  return {\n    hydraS1SimpleAttester,\n    hydraS1AccountboundAttester,\n    availableRootsRegistry,\n    commitmentMapperRegistry,\n    front,\n    badges,\n    attestationsRegistry,\n    hydraS1Verifier,\n    pythia1Verifier,\n    pythia1SimpleAttester,\n  };\n}\n\ntask('0-deploy-core-and-hydra-s1-simple-and-accountbound-and-pythia1').setAction(deploymentAction);\n"
  },
  {
    "path": "tasks/deploy-tasks/full/1-deploy-pythia-1-simple.task.ts",
    "content": "import { task } from 'hardhat/config';\nimport { HardhatRuntimeEnvironment } from 'hardhat/types';\nimport { DeployOptions, getDeployer } from '../utils';\nimport { deploymentsConfig } from '../deployments-config';\nimport { getCommonOptions } from '../../utils/common-options';\nimport { AuthorizeRangeArgs } from '../../helpers/authorizations/attestations-registry-authorize-range.task';\nimport { Pythia1SimpleAttester } from 'types';\nimport {\n  DeployedPythia1SimpleAttester,\n  DeployPythia1SimpleAttesterArgs,\n} from 'tasks/deploy-tasks/unit/attesters/pythia-1/deploy-pythia-1-simple-attester.task';\nimport { Pythia1Verifier } from '@sismo-core/pythia-1';\n\nexport interface Deployed1 {\n  pythia1SimpleAttester: Pythia1SimpleAttester;\n  pythia1Verifier: Pythia1Verifier;\n}\n\nasync function deploymentAction(\n  { options }: { options: DeployOptions },\n  hre: HardhatRuntimeEnvironment\n): Promise<Deployed1> {\n  const deployer = await getDeployer(hre);\n  const config = deploymentsConfig[hre.network.name];\n  options = { ...config.deployOptions, ...options };\n  if (options.manualConfirm || options.log) {\n    console.log('1-deploy-pythia-1-simple: ', hre.network.name);\n  }\n\n  const attestationsRegistry = await hre.deployments.get('AttestationsRegistry');\n  // Only deploy contracts without giving final ownership.\n  // Owners of the different contract are the deployer\n  const pythia1SimpleAttesterArgs: DeployPythia1SimpleAttesterArgs = {\n    collectionIdFirst: config.synapsPythia1SimpleAttester.collectionIdFirst,\n    collectionIdLast: config.synapsPythia1SimpleAttester.collectionIdLast,\n    attestationsRegistryAddress: attestationsRegistry.address,\n    commitmentSignerPubKeyX: config.synapsPythia1SimpleAttester.commitmentSignerPubKeyX,\n    commitmentSignerPubKeyY: config.synapsPythia1SimpleAttester.commitmentSignerPubKeyY,\n    owner: config.synapsPythia1SimpleAttester.owner,\n    options,\n  };\n\n  const { pythia1SimpleAttester, pythia1Verifier } = (await hre.run(\n    'deploy-pythia-1-simple-attester',\n    pythia1SimpleAttesterArgs\n  )) as DeployedPythia1SimpleAttester;\n\n  // Give to the attester the authorization to write on the attestations Registry\n  if (options.manualConfirm || options.log) {\n    console.log(`\n    ----------------------------------------------------------------\n    * Authorize Pythia1SimpleAttester to record on the AttestationsRegistry`);\n  }\n  await hre.run('attestations-registry-authorize-range', {\n    attestationsRegistryAddress: attestationsRegistry.address,\n    attesterAddress: pythia1SimpleAttester.address,\n    collectionIdFirst: config.synapsPythia1SimpleAttester.collectionIdFirst,\n    collectionIdLast: config.synapsPythia1SimpleAttester.collectionIdLast,\n    options: getCommonOptions(options),\n  } as AuthorizeRangeArgs);\n\n  if (options.manualConfirm || options.log) {\n    console.log(`\n    ************************************************************\n    *                           RECAP                          *\n    ************************************************************\n\n    date: ${new Date().toISOString()}\n\n    ** Common **\n      proxyAdmin: ${config.deployOptions.proxyAdmin}\n\n    * Pythia1SimpleAttester:\n      -> proxy: ${(await hre.deployments.all()).Pythia1SimpleAttester.address}\n      -> implem: ${(await hre.deployments.all()).Pythia1SimpleAttesterImplem.address}\n      collectionIdFirst: ${config.synapsPythia1SimpleAttester.collectionIdFirst}\n      collectionIdLast: ${config.synapsPythia1SimpleAttester.collectionIdLast}\n      commitmentSignerPubKeyX: ${config.synapsPythia1SimpleAttester.commitmentSignerPubKeyX}\n      commitmentSignerPubKeyY: ${config.synapsPythia1SimpleAttester.commitmentSignerPubKeyY}\n\n    * Pythia1Verifier:\n      -> address: ${(await hre.deployments.all()).Pythia1Verifier.address}\n  `);\n  }\n\n  return {\n    pythia1SimpleAttester,\n    pythia1Verifier,\n  };\n}\n\ntask('1-deploy-pythia-1-simple').setAction(deploymentAction);\n"
  },
  {
    "path": "tasks/deploy-tasks/full/2-upgrade-proxies.task.ts",
    "content": "import { DeployedBadges } from '../unit/core/deploy-badges.task';\nimport { HydraS1AccountboundAttester } from '../../../types/HydraS1AccountboundAttester';\nimport { HydraS1SimpleAttester } from '../../../types/HydraS1SimpleAttester';\nimport { task } from 'hardhat/config';\nimport { HardhatRuntimeEnvironment } from 'hardhat/types';\nimport { DeployOptions, getDeployer } from '../utils';\nimport { Badges, Pythia1SimpleAttester, AttestationsRegistry } from 'types';\nimport { DeployedPythia1SimpleAttester } from 'tasks/deploy-tasks/unit/attesters/pythia-1/deploy-pythia-1-simple-attester.task';\nimport { DeployedHydraS1AccountboundAttester } from 'tasks/deploy-tasks/unit/attesters/hydra-s1/deploy-hydra-s1-accountbound-attester.task';\nimport { DeployedHydraS1SimpleAttester } from 'tasks/deploy-tasks/unit/attesters/hydra-s1/deploy-hydra-s1-simple-attester.task';\nimport { DeployedAttestationsRegistry } from 'tasks/deploy-tasks/unit/core/deploy-attestations-registry.task';\nimport { deploymentsConfig } from '../deployments-config';\n\nexport interface Deployed2 {\n  attestationsRegistry: AttestationsRegistry;\n  hydraS1SimpleAttester: HydraS1SimpleAttester;\n  pythia1SimpleAttester: Pythia1SimpleAttester;\n  badges: Badges;\n  hydraS1AccountboundAttester: HydraS1AccountboundAttester;\n}\n\nasync function deploymentAction(\n  { options }: { options: DeployOptions },\n  hre: HardhatRuntimeEnvironment\n): Promise<Deployed2> {\n  const deployer = await getDeployer(hre);\n  const config = deploymentsConfig[process.env.FORK_NETWORK ?? hre.network.name];\n  options = { ...config.deployOptions, ...options };\n\n  if (options.manualConfirm || options.log) {\n    console.log('2-upgrade-proxies: ', hre.network.name);\n  }\n\n  // The following proxies will be updated:\n  // - AttestationRegistry => update of the deleteAttestations interface\n  // - Badges => override of the setApprovalForAll function\n  // - HydraS1SimpleAttester  => update of the Attester.sol deleteAttestations function\n  // - Pythia1SimpleAttester  => update of the Attester.sol deleteAttestations function\n  // - HydraS1AccountboundAttester => cooldown duration in groupProperties + burnCount in attestations extraData\n\n  // Upgrade attestations registry\n  const { attestationsRegistry: newAttestationsRegistry } = (await hre.run(\n    'deploy-attestations-registry',\n    {\n      badges: config.badges.address,\n      owner: config.attestationsRegistry.owner,\n      options: {\n        ...options,\n        isImplementationUpgrade: true,\n        proxyAddress: config.attestationsRegistry.address,\n      },\n    }\n  )) as DeployedAttestationsRegistry;\n\n  // Upgrade Badges\n  const { badges: newBadges } = (await hre.run('deploy-badges', {\n    uri: config.badges.uri,\n    owner: config.badges.owner,\n    options: { ...options, isImplementationUpgrade: true, proxyAddress: config.badges.address },\n  })) as DeployedBadges;\n\n  // Upgrade HydraS1SimpleAttester\n  const { hydraS1SimpleAttester: newHydraS1SimpleAttester } = (await hre.run(\n    'deploy-hydra-s1-simple-attester',\n    {\n      collectionIdFirst: config.hydraS1SimpleAttester.collectionIdFirst,\n      collectionIdLast: config.hydraS1SimpleAttester.collectionIdLast,\n      commitmentMapperRegistryAddress: config.commitmentMapper.address,\n      availableRootsRegistryAddress: config.availableRootsRegistry.address,\n      attestationsRegistryAddress: config.attestationsRegistry.address,\n      hydraS1VerifierAddress: config.hydraS1Verifier.address,\n      options: {\n        ...options,\n        isImplementationUpgrade: true,\n        proxyAddress: config.hydraS1SimpleAttester.address,\n      },\n    }\n  )) as DeployedHydraS1SimpleAttester;\n\n  // Upgrade Pythia1SimpleAttester\n  const { pythia1SimpleAttester: newPythia1SimpleAttester } = (await hre.run(\n    'deploy-pythia-1-simple-attester',\n    {\n      collectionIdFirst: config.synapsPythia1SimpleAttester.collectionIdFirst,\n      collectionIdLast: config.synapsPythia1SimpleAttester.collectionIdLast,\n      attestationsRegistryAddress: config.attestationsRegistry.address,\n      commitmentSignerPubKeyX: config.synapsPythia1SimpleAttester.commitmentSignerPubKeyX,\n      commitmentSignerPubKeyY: config.synapsPythia1SimpleAttester.commitmentSignerPubKeyY,\n      pythia1VerifierAddress: config.pythia1Verifier.address,\n      owner: config.synapsPythia1SimpleAttester.owner,\n      options: {\n        ...options,\n        isImplementationUpgrade: true,\n        proxyAddress: config.synapsPythia1SimpleAttester.address,\n      },\n    }\n  )) as DeployedPythia1SimpleAttester;\n\n  // Upgrade HydraS1AccountboundAttester\n  // ! need to rename HydraS1SoulboundAttesterProxy and HydraS1SoulboundAttester to\n  //  HydraS1AccountboundAttesterProxy and HydraS1AccountboundAttester\n\n  const { hydraS1AccountboundAttester: newHydraS1AccountboundAttester } = (await hre.run(\n    'deploy-hydra-s1-accountbound-attester',\n    {\n      collectionIdFirst: config.hydraS1AccountboundAttester.collectionIdFirst,\n      collectionIdLast: config.hydraS1AccountboundAttester.collectionIdLast,\n      commitmentMapperRegistryAddress: config.commitmentMapper.address,\n      availableRootsRegistryAddress: config.availableRootsRegistry.address,\n      attestationsRegistryAddress: config.attestationsRegistry.address,\n      hydraS1VerifierAddress: config.hydraS1Verifier.address,\n      options: {\n        ...options,\n        isImplementationUpgrade: true,\n        proxyAddress: config.hydraS1AccountboundAttester.address,\n      },\n    }\n  )) as DeployedHydraS1AccountboundAttester;\n\n  return {\n    attestationsRegistry: newAttestationsRegistry,\n    badges: newBadges,\n    hydraS1SimpleAttester: newHydraS1SimpleAttester,\n    pythia1SimpleAttester: newPythia1SimpleAttester,\n    hydraS1AccountboundAttester: newHydraS1AccountboundAttester,\n  };\n}\n\ntask('2-upgrade-proxies').setAction(deploymentAction);\n"
  },
  {
    "path": "tasks/deploy-tasks/full/2-upgrade-proxies.test-fork.ts",
    "content": "import { getImplementation } from './../../../utils/proxy';\nimport { HydraS1AccountboundAttester } from '../../../types/HydraS1AccountboundAttester';\nimport {\n  AttestationStructOutput,\n  HydraS1SimpleAttester,\n  RequestStruct,\n} from '../../../types/HydraS1SimpleAttester';\nimport { AttestationsRegistry } from '../../../types/AttestationsRegistry';\nimport { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers';\nimport { config, expect } from 'chai';\nimport hre, { ethers } from 'hardhat';\nimport {\n  encodeGroupProperties,\n  encodeHydraS1AccountboundGroupProperties,\n  evmRevert,\n  evmSnapshot,\n  generateAttesterGroups,\n  generateGroups,\n  generateHydraS1AccountboundAttesterGroups,\n  generateHydraS1Accounts,\n  generateExternalNullifier,\n  getEventArgs,\n  HydraS1AccountboundGroup,\n  HydraS1SimpleGroup,\n  impersonateAddress,\n} from '../../../test/utils';\nimport { deploymentsConfig } from '../deployments-config';\nimport {\n  AttestationsRegistry__factory,\n  AvailableRootsRegistry,\n  AvailableRootsRegistry__factory,\n  Badges,\n  Badges__factory,\n  CommitmentMapperRegistry,\n  CommitmentMapperRegistry__factory,\n  Front,\n  Front__factory,\n  HydraS1AccountboundAttester__factory,\n  HydraS1SimpleAttester__factory,\n  Pythia1SimpleAttester,\n  Pythia1SimpleAttester__factory,\n  TransparentUpgradeableProxy__factory,\n} from '../../../types';\nimport {\n  buildPoseidon,\n  EddsaPublicKey,\n  HydraS1Account,\n  HydraS1Prover,\n  KVMerkleTree,\n  SnarkProof,\n} from '@sismo-core/hydra-s1';\nimport { CommitmentMapperTester } from '@sismo-core/commitment-mapper-tester-js';\nimport { Pythia1Prover } from '@sismo-core/pythia-1';\nimport { BigNumber } from 'ethers';\n\nimport { Deployed0 } from './0-deploy-core-and-hydra-s1-simple-and-accountbound-and-pythia1.task';\nimport { Deployed1 } from './1-deploy-pythia-1-simple.task';\nimport {\n  CommitmentSignerTester,\n  encodePythia1GroupProperties,\n  generatePythia1Group,\n} from '../../../test/utils/pythia-1';\n\n// Launch with command\n// FORK=true FORK_NETWORK=goerli npx hardhat test ./tasks/deploy-tasks/full/2-upgrade-proxies.test-fork.ts\n\n/*************************************************************************************/\n/*********************************** FORK - E2E **************************************/\n/*************************************************************************************/\n\ndescribe('FORK-Test E2E Protocol', () => {\n  let chainId: number;\n  let snapshotId: string;\n\n  // contracts\n  let attestationsRegistry: AttestationsRegistry;\n  let hydraS1AccountboundAttester: HydraS1AccountboundAttester;\n  let hydraS1SimpleAttester: HydraS1SimpleAttester;\n  let pythia1SimpleAttester: Pythia1SimpleAttester;\n  let availableRootsRegistry: AvailableRootsRegistry;\n  let commitmentMapperRegistry: CommitmentMapperRegistry;\n  let front: Front;\n  let badges: Badges;\n  let earlyUserCollectionId;\n\n  // hydra s1 prover\n  let hydraS1Prover1: HydraS1Prover;\n  let hydraS1Prover2: HydraS1Prover;\n  let hydraS1ProverAccountBound: HydraS1Prover;\n  let commitmentMapper: CommitmentMapperTester;\n  let commitmentMapperPubKey: EddsaPublicKey;\n  let registryTree1: KVMerkleTree;\n  let registryTree2: KVMerkleTree;\n  let registryTreeAccountBound: KVMerkleTree;\n\n  // pythia 1 prover\n  let pythia1Prover: Pythia1Prover;\n  let commitmentSigner: CommitmentSignerTester;\n\n  // Test Signers\n  let deployer: SignerWithAddress;\n  let proxyAdminSigner: SignerWithAddress;\n  let pythia1destinationSigner: SignerWithAddress;\n  let randomSigner: SignerWithAddress;\n\n  // Test accounts\n  let source1: HydraS1Account;\n  let source2: HydraS1Account;\n  let source3: HydraS1Account;\n  let source4: HydraS1Account;\n  let destination1: HydraS1Account;\n  let destination2: HydraS1Account;\n\n  // Data source test\n  let source1Value: BigNumber;\n  let source2Value: BigNumber;\n  let source3Value: BigNumber;\n  let source4Value: BigNumber;\n  let accountsTree1: KVMerkleTree;\n  let accountsTree2: KVMerkleTree;\n  let accountsTreeAccountBound: KVMerkleTree;\n  let group1: HydraS1SimpleGroup;\n  let group2: HydraS1SimpleGroup;\n  let groupAccountBound: HydraS1AccountboundGroup;\n\n  // Valid request and proof\n  let request1: RequestStruct;\n  let request2: RequestStruct;\n  let request3: RequestStruct;\n  let request4: RequestStruct;\n  let proofRequest1: string;\n  let proofRequest2: string;\n  let proofRequest3: string;\n  let proofRequest4: string;\n  let externalNullifier1: BigNumber;\n  let externalNullifier2: BigNumber;\n  let externalNullifierAccountBound: BigNumber;\n  let attestationsRequested1: AttestationStructOutput[];\n  let attestationsRequested2: AttestationStructOutput[];\n  let attestationsRequested3: AttestationStructOutput[];\n  let attestationsRequested4: AttestationStructOutput[];\n  let poseidon;\n\n  const config = deploymentsConfig[process.env.FORK_NETWORK ?? hre.network.name];\n\n  before(async () => {\n    const signers = await hre.ethers.getSigners();\n    [deployer, , proxyAdminSigner, pythia1destinationSigner, randomSigner] = signers;\n    // impersonate address for the fork test\n    await impersonateAddress(hre, config.deployOptions.proxyAdmin!, true);\n\n    chainId = parseInt(await hre.getChainId());\n    poseidon = await buildPoseidon();\n\n    // 1 - Init commitment mapper and commitment signer\n    commitmentMapper = await CommitmentMapperTester.generate();\n    commitmentMapperPubKey = await commitmentMapper.getPubKey();\n    commitmentSigner = new CommitmentSignerTester();\n\n    // 2 - Generate Hydra S1 Accounts with the commitment mapper\n    let hydraS1Accounts: HydraS1Account[] = await generateHydraS1Accounts(\n      signers,\n      commitmentMapper\n    );\n\n    source1 = hydraS1Accounts[0];\n    source2 = hydraS1Accounts[5];\n    source3 = hydraS1Accounts[6];\n    source4 = hydraS1Accounts[7];\n    destination1 = hydraS1Accounts[1];\n    destination2 = hydraS1Accounts[3];\n\n    // 3 - Generate data source\n    const allList = generateGroups(hydraS1Accounts);\n    const { dataFormat, groups } = await generateAttesterGroups(allList);\n    const { dataFormat: dataFormat2, groups: groups2 } = await generateAttesterGroups(allList);\n    const { dataFormat: dataFormatAccountBound, groups: groupsAccountBound } =\n      await generateHydraS1AccountboundAttesterGroups(allList);\n\n    registryTree1 = dataFormat.registryTree;\n    registryTree2 = dataFormat2.registryTree;\n    registryTreeAccountBound = dataFormatAccountBound.registryTree;\n    accountsTree1 = dataFormat.accountsTrees[0];\n    accountsTree2 = dataFormat2.accountsTrees[0];\n    accountsTreeAccountBound = dataFormatAccountBound.accountsTrees[0];\n    group1 = groups[0];\n    group2 = groups2[0];\n    groupAccountBound = groupsAccountBound[0];\n    source1Value = accountsTree1.getValue(BigNumber.from(source1.identifier).toHexString());\n    source2Value = accountsTree2.getValue(BigNumber.from(source2.identifier).toHexString());\n    source3Value = accountsTree1.getValue(BigNumber.from(source3.identifier).toHexString());\n    source4Value = accountsTreeAccountBound.getValue(\n      BigNumber.from(source4.identifier).toHexString()\n    );\n\n    // 4 - Init Proving scheme\n    hydraS1Prover1 = new HydraS1Prover(registryTree1, commitmentMapperPubKey);\n    hydraS1Prover2 = new HydraS1Prover(registryTree2, commitmentMapperPubKey);\n    hydraS1ProverAccountBound = new HydraS1Prover(registryTreeAccountBound, commitmentMapperPubKey);\n    pythia1Prover = new Pythia1Prover();\n  });\n\n  /*************************************************************************************/\n  /********************************** SETUP ********************************************/\n  /*************************************************************************************/\n\n  describe('Deployments, setup contracts and prepare test requests', () => {\n    it('Should retrieve core contracts and update roots for attester', async () => {\n      // Deploy Sismo Protocol Core contracts\n      availableRootsRegistry = AvailableRootsRegistry__factory.connect(\n        config.availableRootsRegistry.address,\n        await impersonateAddress(hre, config.availableRootsRegistry.owner)\n      ) as AvailableRootsRegistry;\n\n      badges = Badges__factory.connect(\n        config.badges.address,\n        await impersonateAddress(hre, config.badges.owner)\n      ) as Badges;\n\n      front = Front__factory.connect(\n        config.front.address,\n        await impersonateAddress(hre, randomSigner.address, true)\n      ) as Front;\n\n      hydraS1SimpleAttester = HydraS1SimpleAttester__factory.connect(\n        config.hydraS1SimpleAttester.address,\n        await impersonateAddress(hre, randomSigner.address, true)\n      ) as HydraS1SimpleAttester;\n\n      hydraS1AccountboundAttester = HydraS1AccountboundAttester__factory.connect(\n        config.hydraS1AccountboundAttester.address,\n        await impersonateAddress(hre, randomSigner.address, true)\n      );\n\n      attestationsRegistry = AttestationsRegistry__factory.connect(\n        config.attestationsRegistry.address,\n        await impersonateAddress(hre, config.attestationsRegistry.owner)\n      ) as AttestationsRegistry;\n\n      pythia1SimpleAttester = Pythia1SimpleAttester__factory.connect(\n        config.synapsPythia1SimpleAttester.address,\n        await impersonateAddress(hre, config.synapsPythia1SimpleAttester.owner, true)\n      ) as Pythia1SimpleAttester;\n\n      commitmentMapperRegistry = CommitmentMapperRegistry__factory.connect(\n        config.commitmentMapper.address,\n        await impersonateAddress(hre, config.commitmentMapper.owner, true)\n      ) as CommitmentMapperRegistry;\n\n      await (\n        await commitmentMapperRegistry.updateCommitmentMapperEdDSAPubKey(commitmentMapperPubKey, {\n          gasLimit: 600000,\n        })\n      ).wait();\n\n      await (\n        await availableRootsRegistry.registerRootForAttester(\n          hydraS1SimpleAttester.address,\n          registryTree1.getRoot(),\n          { gasLimit: 600000 }\n        )\n      ).wait();\n\n      await (\n        await availableRootsRegistry.registerRootForAttester(\n          hydraS1AccountboundAttester.address,\n          registryTree2.getRoot(),\n          { gasLimit: 600000 }\n        )\n      ).wait();\n\n      earlyUserCollectionId = await front.EARLY_USER_COLLECTION();\n\n      const pythiaOwner = await impersonateAddress(hre, await pythia1SimpleAttester.owner(), true);\n      const commitmentSignerPubKey = await commitmentSigner.getPublicKey();\n\n      await (\n        await pythia1SimpleAttester\n          .connect(pythiaOwner)\n          .updateCommitmentSignerPubKey(commitmentSignerPubKey, { gasLimit: 600000 })\n      ).wait();\n    });\n\n    it('Should prepare test requests', async () => {\n      // Deploy Sismo Protocol Core contracts\n      externalNullifier1 = await generateExternalNullifier(\n        hydraS1SimpleAttester.address,\n        group1.properties.groupIndex\n      );\n\n      externalNullifier2 = await generateExternalNullifier(\n        hydraS1AccountboundAttester.address,\n        group2.properties.groupIndex\n      );\n\n      request1 = {\n        claims: [\n          {\n            groupId: group1.id,\n            claimedValue: source1Value,\n            extraData: encodeGroupProperties(group1.properties),\n          },\n        ],\n        destination: BigNumber.from(destination1.identifier).toHexString(),\n      };\n\n      proofRequest1 = (\n        await hydraS1Prover1.generateSnarkProof({\n          source: source1,\n          destination: destination1,\n          claimedValue: source1Value,\n          chainId: chainId,\n          accountsTree: accountsTree1,\n          externalNullifier: externalNullifier1,\n          isStrict: !group1.properties.isScore,\n        })\n      ).toBytes();\n\n      request2 = {\n        claims: [\n          {\n            groupId: group2.id,\n            claimedValue: source2Value,\n            extraData: encodeGroupProperties(group2.properties),\n          },\n        ],\n        destination: BigNumber.from(destination1.identifier).toHexString(),\n      };\n\n      proofRequest2 = (\n        await hydraS1Prover2.generateSnarkProof({\n          source: source2,\n          destination: destination1,\n          claimedValue: source2Value,\n          chainId: chainId,\n          accountsTree: accountsTree2,\n          externalNullifier: externalNullifier2,\n          isStrict: !group2.properties.isScore,\n        })\n      ).toBytes();\n\n      [attestationsRequested1, attestationsRequested2] = await front.batchBuildAttestations(\n        [hydraS1SimpleAttester.address, hydraS1AccountboundAttester.address],\n        [request1, request2],\n        [proofRequest1, proofRequest2]\n      );\n\n      snapshotId = await evmSnapshot(hre);\n    });\n  });\n\n  /*************************************************************************************/\n  /************************ATTESTATIONS AND BADGES GENERATIONS**************************/\n  /*************************************************************************************/\n\n  describe('Test attestations generations', () => {\n    it('Should generate attestations from hydra s1 simple and hydra s1 accountbound via batch', async () => {\n      const tx = await front.batchGenerateAttestations(\n        [hydraS1SimpleAttester.address, hydraS1AccountboundAttester.address],\n        [request1, request2],\n        [proofRequest1, proofRequest2],\n        { gasLimit: 6000000 }\n      );\n\n      const { events } = await tx.wait();\n\n      const earlyUserActivated = Date.now() < Date.parse('15 Sept 2022 00:00:00 GMT');\n      if (earlyUserActivated) {\n        const args = getEventArgs(events, 'EarlyUserAttestationGenerated');\n        expect(args.destination).to.eql(request1.destination);\n      }\n\n      const attestationsValues = await attestationsRegistry.getAttestationValueBatch(\n        [\n          attestationsRequested1[0].collectionId,\n          attestationsRequested2[0].collectionId,\n          earlyUserCollectionId,\n        ],\n        [request1.destination, request2.destination, request1.destination]\n      );\n\n      const expectedAttestationsValues = [\n        attestationsRequested1[0].value,\n        attestationsRequested2[0].value,\n        earlyUserActivated ? BigNumber.from(1) : BigNumber.from(0),\n      ];\n\n      expect(attestationsValues).to.be.eql(expectedAttestationsValues);\n\n      const balances = await badges.balanceOfBatch(\n        [request1.destination, request2.destination, request1.destination],\n        [\n          attestationsRequested1[0].collectionId,\n          attestationsRequested2[0].collectionId,\n          earlyUserCollectionId,\n        ]\n      );\n\n      const expectedBalances = expectedAttestationsValues;\n\n      expect(balances).to.be.eql(expectedBalances);\n    });\n    it('Should reset contracts', async () => {\n      await evmRevert(hre, snapshotId);\n\n      const attestationsValues = await attestationsRegistry.getAttestationValueBatch(\n        [\n          attestationsRequested1[0].collectionId,\n          attestationsRequested2[0].collectionId,\n          earlyUserCollectionId,\n        ],\n        [request1.destination, request2.destination, request1.destination]\n      );\n\n      const expectedAttestationsValues = [BigNumber.from(0), BigNumber.from(0), BigNumber.from(0)];\n\n      expect(attestationsValues).to.be.eql(expectedAttestationsValues);\n\n      const balances = await badges.balanceOfBatch(\n        [request1.destination, request2.destination, request1.destination],\n        [\n          attestationsRequested1[0].collectionId,\n          attestationsRequested2[0].collectionId,\n          earlyUserCollectionId,\n        ]\n      );\n\n      const expectedBalances = expectedAttestationsValues;\n\n      expect(balances).to.be.eql(expectedBalances);\n    });\n\n    it('Should generate attestations from hydra s1 simple and hydra s1 accountbound via front and two separate txs', async () => {\n      const tx = await front.generateAttestations(\n        hydraS1SimpleAttester.address,\n        request1,\n        proofRequest1,\n        { gasLimit: 600000 }\n      );\n      await front.generateAttestations(\n        hydraS1AccountboundAttester.address,\n        request2,\n        proofRequest2,\n        { gasLimit: 600000 }\n      );\n      const { events } = await tx.wait();\n      const earlyUserActivated = Date.now() < Date.parse('15 Sept 2022 00:00:00 GMT');\n      if (earlyUserActivated) {\n        const args = getEventArgs(events, 'EarlyUserAttestationGenerated');\n        expect(args.destination).to.eql(request1.destination);\n      }\n\n      const attestationsValues = await attestationsRegistry.getAttestationValueBatch(\n        [\n          attestationsRequested1[0].collectionId,\n          attestationsRequested2[0].collectionId,\n          earlyUserCollectionId,\n        ],\n        [request1.destination, request2.destination, request1.destination]\n      );\n\n      const expectedAttestationsValues = [\n        attestationsRequested1[0].value,\n        attestationsRequested2[0].value,\n        earlyUserActivated ? BigNumber.from(1) : BigNumber.from(0),\n      ];\n\n      expect(attestationsValues).to.be.eql(expectedAttestationsValues);\n\n      const balances = await badges.balanceOfBatch(\n        [request1.destination, request2.destination, request1.destination],\n        [\n          attestationsRequested1[0].collectionId,\n          attestationsRequested2[0].collectionId,\n          earlyUserCollectionId,\n        ]\n      );\n\n      const expectedBalances = expectedAttestationsValues;\n\n      expect(balances).to.be.eql(expectedBalances);\n    });\n    it('Should generate an attestation from pythia 1 simple', async () => {\n      const secret = BigNumber.from('0x123');\n      const commitment = poseidon([secret]);\n      const commitmentValue = BigNumber.from('0x9');\n      const pythia1group1 = generatePythia1Group({\n        internalCollectionId: 0,\n        isScore: false,\n      });\n      const commitmentReceipt = await commitmentSigner.getCommitmentReceipt(\n        commitment,\n        commitmentValue,\n        pythia1group1.id\n      );\n\n      const externalNullifier = await generateExternalNullifier(\n        pythia1SimpleAttester.address,\n        pythia1group1.properties.internalCollectionId\n      );\n\n      const request = {\n        claims: [\n          {\n            groupId: pythia1group1.id,\n            claimedValue: commitmentValue,\n            extraData: encodePythia1GroupProperties(pythia1group1.properties),\n          },\n        ],\n        destination: BigNumber.from(pythia1destinationSigner.address).toHexString(),\n      };\n\n      const proof = (await pythia1Prover.generateSnarkProof({\n        secret: secret,\n        value: commitmentValue,\n        commitmentReceipt: commitmentReceipt,\n        commitmentSignerPubKey: await commitmentSigner.getPublicKey(),\n        destinationIdentifier: pythia1destinationSigner.address,\n        claimedValue: commitmentValue,\n        chainId: chainId,\n        groupId: pythia1group1.id,\n        ticketIdentifier: externalNullifier,\n        isStrict: !pythia1group1.properties.isScore,\n      })) as SnarkProof;\n\n      const tx = await pythia1SimpleAttester\n        .connect(pythia1destinationSigner)\n        .generateAttestations(request, proof.toBytes(), { gasLimit: 600000 });\n      await tx.wait();\n\n      const balances = await badges.balanceOfBatch(\n        [pythia1destinationSigner.address],\n        [\n          BigNumber.from(config.synapsPythia1SimpleAttester.collectionIdFirst).add(\n            pythia1group1.properties.internalCollectionId\n          ),\n        ]\n      );\n\n      expect(balances).to.be.eql([commitmentValue]);\n    });\n  });\n\n  describe('Update Implementation', () => {\n    it('Should run the upgrade script', async () => {\n      ({\n        attestationsRegistry,\n        badges,\n        hydraS1SimpleAttester,\n        pythia1SimpleAttester,\n        hydraS1AccountboundAttester,\n      } = await hre.run('2-upgrade-proxies', { options: { manualConfirm: false, log: false } }));\n\n      await (\n        await availableRootsRegistry.registerRootForAttester(\n          hydraS1SimpleAttester.address,\n          registryTree1.getRoot()\n        )\n      ).wait();\n      await (\n        await availableRootsRegistry.registerRootForAttester(\n          hydraS1AccountboundAttester.address,\n          registryTreeAccountBound.getRoot()\n        )\n      ).wait();\n\n      const pythiaOwner = await impersonateAddress(hre, await pythia1SimpleAttester.owner(), true);\n      const commitmentSignerPubKey = await commitmentSigner.getPublicKey();\n\n      await (\n        await pythia1SimpleAttester\n          .connect(pythiaOwner)\n          .updateCommitmentSignerPubKey(commitmentSignerPubKey, { gasLimit: 600000 })\n      ).wait();\n\n      snapshotId = await evmSnapshot(hre);\n    });\n\n    it('should test the Badges contract', async () => {\n      expect(badges.address).to.be.equal(config.badges.address);\n      const value = await badges.balanceOf('0xF61CabBa1e6FC166A66bcA0fcaa83762EdB6D4Bd', 15151111);\n      expect(value).to.be.eql(BigNumber.from(3));\n    });\n\n    it('should test the Hydra-S1 Simple Attester contract', async () => {\n      expect(hydraS1SimpleAttester.address).to.be.equal(config.hydraS1SimpleAttester.address);\n    });\n\n    it('should test the Hydra-S1 AccountBound Attester contract', async () => {\n      expect(hydraS1AccountboundAttester.address).to.be.equal(\n        config.hydraS1AccountboundAttester.address\n      );\n    });\n\n    it('should test the Synaps Pythia-1 Simple Attester contract', async () => {\n      expect(pythia1SimpleAttester.address).to.be.equal(config.synapsPythia1SimpleAttester.address);\n    });\n\n    it('should test the attestationsRegistry contract', async () => {\n      expect(attestationsRegistry.address).to.be.equal(config.attestationsRegistry.address);\n    });\n  });\n\n  /*************************************************************************************/\n  /************** ATTESTATIONS AND BADGES GENERATIONS AFTER PROXY UPDATE ***************/\n  /*************************************************************************************/\n\n  describe('Test attestations generations (after proxy update)', () => {\n    it('Should prepare test requests (after proxy upgrade)', async () => {\n      externalNullifierAccountBound = await generateExternalNullifier(\n        hydraS1AccountboundAttester.address,\n        groupAccountBound.properties.groupIndex\n      );\n\n      request3 = {\n        claims: [\n          {\n            groupId: group1.id,\n            claimedValue: source3Value,\n            extraData: encodeGroupProperties(group1.properties),\n          },\n        ],\n        destination: BigNumber.from(destination1.identifier).toHexString(),\n      };\n\n      proofRequest3 = (\n        await hydraS1Prover1.generateSnarkProof({\n          source: source3,\n          destination: destination1,\n          claimedValue: source3Value,\n          chainId: chainId,\n          accountsTree: accountsTree1,\n          externalNullifier: externalNullifier1,\n          isStrict: !group1.properties.isScore,\n        })\n      ).toBytes();\n\n      request4 = {\n        claims: [\n          {\n            groupId: groupAccountBound.id,\n            claimedValue: source4Value,\n            extraData: encodeHydraS1AccountboundGroupProperties(groupAccountBound.properties),\n          },\n        ],\n        destination: BigNumber.from(destination1.identifier).toHexString(),\n      };\n\n      proofRequest4 = (\n        await hydraS1ProverAccountBound.generateSnarkProof({\n          source: source4,\n          destination: destination1,\n          claimedValue: source4Value,\n          chainId: chainId,\n          accountsTree: accountsTreeAccountBound,\n          externalNullifier: externalNullifierAccountBound,\n          isStrict: !groupAccountBound.properties.isScore,\n        })\n      ).toBytes();\n\n      [attestationsRequested3, attestationsRequested4] = await front.batchBuildAttestations(\n        [hydraS1SimpleAttester.address, hydraS1AccountboundAttester.address],\n        [request3, request4],\n        [proofRequest3, proofRequest4]\n      );\n\n      snapshotId = await evmSnapshot(hre);\n    });\n\n    it('Should generate attestations from hydra s1 simple and hydra s1 accountbound via batch (after proxy update)', async () => {\n      const tx = await front.batchGenerateAttestations(\n        [hydraS1SimpleAttester.address, hydraS1AccountboundAttester.address],\n        [request3, request4],\n        [proofRequest3, proofRequest4],\n        { gasLimit: 6000000 }\n      );\n      const { events } = await tx.wait();\n\n      const earlyUserActivated = Date.now() < Date.parse('15 Sept 2022 00:00:00 GMT');\n      if (earlyUserActivated) {\n        const args = getEventArgs(events, 'EarlyUserAttestationGenerated');\n        expect(args.destination).to.eql(request3.destination);\n      }\n\n      const attestationsValues = await attestationsRegistry.getAttestationValueBatch(\n        [\n          attestationsRequested3[0].collectionId,\n          attestationsRequested4[0].collectionId,\n          earlyUserCollectionId,\n        ],\n        [request3.destination, request4.destination, request3.destination]\n      );\n\n      const expectedAttestationsValues = [\n        attestationsRequested3[0].value,\n        attestationsRequested4[0].value,\n        earlyUserActivated ? BigNumber.from(1) : BigNumber.from(0),\n      ];\n\n      expect(attestationsValues).to.be.eql(expectedAttestationsValues);\n\n      const balances = await badges.balanceOfBatch(\n        [request3.destination, request4.destination, request3.destination],\n        [\n          attestationsRequested3[0].collectionId,\n          attestationsRequested4[0].collectionId,\n          earlyUserCollectionId,\n        ]\n      );\n\n      const expectedBalances = expectedAttestationsValues;\n\n      expect(balances).to.be.eql(expectedBalances);\n    });\n\n    it('Should reset contracts to the proxy updates', async () => {\n      await evmRevert(hre, snapshotId);\n\n      const attestationsValues = await attestationsRegistry.getAttestationValueBatch(\n        [\n          attestationsRequested3[0].collectionId,\n          attestationsRequested4[0].collectionId,\n          earlyUserCollectionId,\n        ],\n        [request3.destination, request4.destination, request3.destination]\n      );\n\n      const expectedAttestationsValues = [BigNumber.from(1), BigNumber.from(6), BigNumber.from(0)];\n\n      expect(attestationsValues).to.be.eql(expectedAttestationsValues);\n\n      const balances = await badges.balanceOfBatch(\n        [request3.destination, request4.destination, request3.destination],\n        [\n          attestationsRequested3[0].collectionId,\n          attestationsRequested4[0].collectionId,\n          earlyUserCollectionId,\n        ]\n      );\n\n      const expectedBalances = expectedAttestationsValues;\n\n      expect(balances).to.be.eql(expectedBalances);\n    });\n    it('Should generate attestations from hydra s1 simple and hydra s1 accountbound via front and two separate txs (after proxy updates)', async () => {\n      const tx = await front.generateAttestations(\n        hydraS1SimpleAttester.address,\n        request3,\n        proofRequest3,\n        { gasLimit: 600000 }\n      );\n      await front.generateAttestations(\n        hydraS1AccountboundAttester.address,\n        request4,\n        proofRequest4,\n        { gasLimit: 600000 }\n      );\n      const { events } = await tx.wait();\n      const earlyUserActivated = Date.now() < Date.parse('15 Sept 2022 00:00:00 GMT');\n      if (earlyUserActivated) {\n        const args = getEventArgs(events, 'EarlyUserAttestationGenerated');\n        expect(args.destination).to.eql(request1.destination);\n      }\n\n      const attestationsValues = await attestationsRegistry.getAttestationValueBatch(\n        [\n          attestationsRequested3[0].collectionId,\n          attestationsRequested4[0].collectionId,\n          earlyUserCollectionId,\n        ],\n        [request3.destination, request4.destination, request3.destination]\n      );\n\n      const expectedAttestationsValues = [\n        attestationsRequested3[0].value,\n        attestationsRequested4[0].value,\n        earlyUserActivated ? BigNumber.from(1) : BigNumber.from(0),\n      ];\n\n      expect(attestationsValues).to.be.eql(expectedAttestationsValues);\n\n      const balances = await badges.balanceOfBatch(\n        [request3.destination, request4.destination, request3.destination],\n        [\n          attestationsRequested3[0].collectionId,\n          attestationsRequested4[0].collectionId,\n          earlyUserCollectionId,\n        ]\n      );\n\n      const expectedBalances = expectedAttestationsValues;\n\n      expect(balances).to.be.eql(expectedBalances);\n    });\n    it('Should generate an attestation from pythia 1 simple (after proxy updates)', async () => {\n      const secret = BigNumber.from('0x456');\n      const commitment = poseidon([secret]);\n      const commitmentValue = BigNumber.from('0x11');\n      const pythia1group1 = generatePythia1Group({\n        internalCollectionId: 0,\n        isScore: false,\n      });\n      const commitmentReceipt = await commitmentSigner.getCommitmentReceipt(\n        commitment,\n        commitmentValue,\n        pythia1group1.id\n      );\n\n      const externalNullifier = await generateExternalNullifier(\n        pythia1SimpleAttester.address,\n        pythia1group1.properties.internalCollectionId\n      );\n\n      const request = {\n        claims: [\n          {\n            groupId: pythia1group1.id,\n            claimedValue: commitmentValue,\n            extraData: encodePythia1GroupProperties(pythia1group1.properties),\n          },\n        ],\n        destination: BigNumber.from(pythia1destinationSigner.address).toHexString(),\n      };\n\n      const proof = (await pythia1Prover.generateSnarkProof({\n        secret: secret,\n        value: commitmentValue,\n        commitmentReceipt: commitmentReceipt,\n        commitmentSignerPubKey: await commitmentSigner.getPublicKey(),\n        destinationIdentifier: pythia1destinationSigner.address,\n        claimedValue: commitmentValue,\n        chainId: chainId,\n        groupId: pythia1group1.id,\n        ticketIdentifier: externalNullifier,\n        isStrict: !pythia1group1.properties.isScore,\n      })) as SnarkProof;\n\n      const tx = await pythia1SimpleAttester\n        .connect(pythia1destinationSigner)\n        .generateAttestations(request, proof.toBytes(), { gasLimit: 600000 });\n      await tx.wait();\n\n      const balances = await badges.balanceOfBatch(\n        [pythia1destinationSigner.address],\n        [\n          BigNumber.from(config.synapsPythia1SimpleAttester.collectionIdFirst).add(\n            pythia1group1.properties.internalCollectionId\n          ),\n        ]\n      );\n\n      expect(balances).to.be.eql([commitmentValue]);\n    });\n  });\n});\n"
  },
  {
    "path": "tasks/deploy-tasks/full/3-new-hydra-s1-verifier-and-upgrade-hydra-s1-simple-proxy.task.ts",
    "content": "import { task } from 'hardhat/config';\nimport { HardhatRuntimeEnvironment } from 'hardhat/types';\nimport { DeployOptions, getDeployer } from '../utils';\nimport { HydraS1AccountboundAttester, HydraS1Verifier } from 'types';\nimport { DeployedHydraS1AccountboundAttester } from 'tasks/deploy-tasks/unit/attesters/hydra-s1/deploy-hydra-s1-accountbound-attester.task';\nimport { deploymentsConfig } from '../deployments-config';\n\nexport interface Deployed3 {\n  hydraS1Verifier: HydraS1Verifier;\n  hydraS1AccountboundAttester: HydraS1AccountboundAttester;\n}\n\nasync function deploymentAction(\n  { options }: { options: DeployOptions },\n  hre: HardhatRuntimeEnvironment\n): Promise<Deployed3> {\n  const config = deploymentsConfig[process.env.FORK_NETWORK ?? hre.network.name];\n  options = { ...config.deployOptions, ...options };\n\n  if (options.manualConfirm || options.log) {\n    console.log('3-new-hydra-s1-verifier-and-upgrade-accountbound-proxy: ', hre.network.name);\n  }\n\n  // The following proxies will be updated:\n  // - HydraS1Verifier => rename ticket in nullifier\n  // - HydraS1SimpleAttester implementation will be replaced by the HydraS1AccountboundAttester implementation => cooldown duration removed from groupProperties + inherits from HydraS1SimpleAttester\n  //                                                                                                           => + reinitializer modifier added and version as constant\n\n  // Upgrade HydraS1Verifier\n  const { hydraS1Verifier: newHydraS1Verifier } = await hre.run('deploy-hydra-s1-verifier', {\n    options,\n  });\n\n  // Upgrade proxy implementation from HydraS1SimpleAttester to HydraS1AccountboundAttester\n  const { hydraS1AccountboundAttester: newHydraS1AccountboundAttester } = (await hre.run(\n    'deploy-hydra-s1-accountbound-attester',\n    {\n      // the collectionIds referenced are the ones used by the previous HydraS1SimpleAttester\n      collectionIdFirst: config.hydraS1AccountboundAttester.collectionIdFirst,\n      collectionIdLast: config.hydraS1AccountboundAttester.collectionIdLast,\n      commitmentMapperRegistryAddress: config.commitmentMapper.address,\n      availableRootsRegistryAddress: config.availableRootsRegistry.address,\n      attestationsRegistryAddress: config.attestationsRegistry.address,\n      hydraS1VerifierAddress: newHydraS1Verifier.address, // reference the new hydraS1Verifier address\n      owner: config.hydraS1AccountboundAttester.owner, // set the owner referenced in the config\n      options: {\n        ...options,\n        isImplementationUpgrade: true,\n        proxyAddress: config.hydraS1AccountboundAttester.address, // the address referenced here is the old address of the hydraS1SimpleAttester\n      },\n    }\n  )) as DeployedHydraS1AccountboundAttester;\n\n  return {\n    hydraS1Verifier: newHydraS1Verifier,\n    hydraS1AccountboundAttester: newHydraS1AccountboundAttester,\n  };\n}\n\ntask('3-new-hydra-s1-verifier-and-upgrade-hydra-s1-simple-proxy').setAction(deploymentAction);\n"
  },
  {
    "path": "tasks/deploy-tasks/full/3-new-hydra-s1-verifier-and-upgrade-hydra-s1-simple-proxy.test-fork.ts",
    "content": "import { getImplementation } from '../../../utils/proxy';\nimport {\n  AttestationStructOutput,\n  HydraS1SimpleAttester,\n  RequestStruct,\n} from '../../../types/HydraS1SimpleAttester';\nimport { AttestationsRegistry } from '../../../types/AttestationsRegistry';\nimport { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers';\nimport { config, expect } from 'chai';\nimport hre, { ethers } from 'hardhat';\nimport {\n  encodeGroupProperties,\n  encodeHydraS1AccountboundGroupProperties,\n  evmRevert,\n  evmSnapshot,\n  generateAttesterGroups,\n  generateGroups,\n  generateHydraS1AccountboundAttesterGroups,\n  generateHydraS1Accounts,\n  generateExternalNullifier,\n  getEventArgs,\n  HydraS1AccountboundGroup,\n  HydraS1SimpleGroup,\n  impersonateAddress,\n  increaseTime,\n  GroupData,\n} from '../../../test/utils';\nimport { deploymentsConfig } from '../deployments-config';\nimport {\n  AttestationsRegistry__factory,\n  AvailableRootsRegistry,\n  AvailableRootsRegistry__factory,\n  Badges,\n  Badges__factory,\n  CommitmentMapperRegistry,\n  CommitmentMapperRegistry__factory,\n  Front,\n  Front__factory,\n  HydraS1AccountboundAttester,\n  HydraS1AccountboundAttester__factory,\n  HydraS1SimpleAttester__factory,\n  HydraS1Verifier,\n  Pythia1SimpleAttester,\n  Pythia1SimpleAttester__factory,\n  Pythia1Verifier,\n  TransparentUpgradeableProxy__factory,\n} from '../../../types';\nimport {\n  buildPoseidon,\n  EddsaPublicKey,\n  HydraS1Account,\n  HydraS1Prover,\n  Inputs,\n  KVMerkleTree,\n  SnarkProof,\n} from '@sismo-core/hydra-s1';\nimport { HydraS1Prover as HydraS1ProverPrevious } from 'hydra-s1-previous';\nimport { CommitmentMapperTester } from '@sismo-core/commitment-mapper-tester-js';\nimport { Pythia1Prover } from '@sismo-core/pythia-1';\nimport { BigNumber } from 'ethers';\n\nimport {\n  CommitmentSignerTester,\n  encodePythia1GroupProperties,\n  generatePythia1Group,\n} from '../../../test/utils/pythia-1';\n\n// Launch with command\n// FORK=true FORK_NETWORK=polygon npx hardhat test ./tasks/deploy-tasks/full/3-new-hydra-s1-verifier-and-upgrade-accountbound-proxy.test-fork.ts\n\n/*************************************************************************************/\n/*********************************** FORK - E2E **************************************/\n/*************************************************************************************/\n\ndescribe('FORK-Test New Hydra S1 Verifier and Upgrade HydraS1Simple proxy', () => {\n  let chainId: number;\n  let snapshotId: string;\n\n  // contracts\n  let attestationsRegistry: AttestationsRegistry;\n  let hydraS1Verifier: HydraS1Verifier;\n  let hydraS1AccountboundAttester: HydraS1AccountboundAttester;\n  let hydraS1SimpleAttester: HydraS1SimpleAttester;\n  let pythia1Verifier: Pythia1Verifier;\n  let pythia1SimpleAttester: Pythia1SimpleAttester;\n  let availableRootsRegistry: AvailableRootsRegistry;\n  let commitmentMapperRegistry: CommitmentMapperRegistry;\n  let front: Front;\n  let badges: Badges;\n  let earlyUserCollectionId;\n\n  // hydra s1 prover\n  let hydraS1Prover1: HydraS1Prover;\n  let hydraS1ProverPrevious: HydraS1ProverPrevious;\n  let hydraS1Prover2: HydraS1Prover;\n  let hydraS1ProverAccountBound: HydraS1ProverPrevious;\n  let commitmentMapper: CommitmentMapperTester;\n  let commitmentMapperPubKey: EddsaPublicKey;\n  let registryTree1: KVMerkleTree;\n  let registryTree2: KVMerkleTree;\n  let registryTreeAccountBound: KVMerkleTree;\n\n  // pythia 1 prover\n  let pythia1Prover: Pythia1Prover;\n  let commitmentSigner: CommitmentSignerTester;\n\n  // Test Signers\n  let deployer: SignerWithAddress;\n  let proxyAdminSigner: SignerWithAddress;\n  let pythia1destinationSigner: SignerWithAddress;\n  let randomSigner: SignerWithAddress;\n\n  // Test accounts\n  let source1: HydraS1Account;\n  let source2: HydraS1Account;\n  let source3: HydraS1Account;\n  let source4: HydraS1Account;\n  let destination1: HydraS1Account;\n  let destination2: HydraS1Account;\n\n  // Data source test\n  let source1Value: BigNumber;\n  let source2Value: BigNumber;\n  let source3Value: BigNumber;\n  let source4Value: BigNumber;\n  let accountsTree1: KVMerkleTree;\n  let accountsTree2: KVMerkleTree;\n  let accountsTreeAccountBound: KVMerkleTree;\n  let group1: HydraS1SimpleGroup;\n  let group2: HydraS1SimpleGroup;\n  let groupAccountBound: HydraS1AccountboundGroup;\n\n  // Valid request and proof\n  let request1: RequestStruct;\n  let request2: RequestStruct;\n  let request3: RequestStruct;\n  let request4: RequestStruct;\n  let inputs1: Inputs;\n  let userParams1;\n  let proofRequest1: string;\n  let proofRequest2: string;\n  let proofRequest3: string;\n  let proofRequest4: string;\n  let externalNullifier1: BigNumber;\n  let externalNullifier2: BigNumber;\n  let externalNullifierAccountBound: BigNumber;\n  let attestationsRequested1: AttestationStructOutput[];\n  let attestationsRequested2: AttestationStructOutput[];\n  let attestationsRequested3: AttestationStructOutput[];\n  let attestationsRequested4: AttestationStructOutput[];\n  let poseidon;\n  let cooldownDuration: number;\n  let allList: GroupData[];\n\n  const config = deploymentsConfig[process.env.FORK_NETWORK ?? hre.network.name];\n\n  const oldAttestersAddressesConfig = {\n    polygonPlayground: {\n      hydraS1SimpleAttester: {\n        address: '0x0AB188c7260666146B300aD3ad5b2AB99eb91D45',\n      },\n      hydraS1AccountboundAttester: {\n        address: '0x66331568ca321e333aB25a74BCF0c2623730bd4B',\n      },\n    },\n    polygon: {\n      hydraS1SimpleAttester: {\n        address: '0x10b27d9efa4A1B65412188b6f4F29e64Cf5e0146',\n      },\n      hydraS1AccountboundAttester: {\n        address: '0x095590C542571Df14c6220c3163112286a5f7518',\n      },\n    },\n    mumbai: {\n      hydraS1SimpleAttester: {\n        address: '0x069e6B99f4DA543156f66274FC6673442803C587',\n      },\n      hydraS1AccountboundAttester: {\n        address: '0x76D8Ed0e34555dEF84c2bfff9be85446e8E9fa2A',\n      },\n    },\n    goerli: {\n      hydraS1SimpleAttester: {\n        address: '0x89d80C9E65fd1aC8970B78A4F17E2e772030C1cB',\n      },\n      hydraS1AccountboundAttester: {\n        address: '0x12e69A9b08709324d64c5AEaF4169b03B6199c6C',\n      },\n    },\n  };\n\n  const oldConfig = oldAttestersAddressesConfig[process.env.FORK_NETWORK ?? hre.network.name];\n\n  before(async () => {\n    const signers = await hre.ethers.getSigners();\n    [deployer, , proxyAdminSigner, pythia1destinationSigner, randomSigner] = signers;\n    // impersonate address for the fork test\n    await impersonateAddress(hre, config.deployOptions.proxyAdmin!, true);\n\n    chainId = parseInt(await hre.getChainId());\n    poseidon = await buildPoseidon();\n\n    // 1 - Init commitment mapper and commitment signer\n    commitmentMapper = await CommitmentMapperTester.generate();\n    commitmentMapperPubKey = await commitmentMapper.getPubKey();\n    commitmentSigner = new CommitmentSignerTester();\n\n    // 2 - Generate Hydra S1 Accounts with the commitment mapper\n    let hydraS1Accounts: HydraS1Account[] = await generateHydraS1Accounts(\n      signers,\n      commitmentMapper\n    );\n\n    source1 = hydraS1Accounts[0];\n    source2 = hydraS1Accounts[5];\n    source3 = hydraS1Accounts[6];\n    source4 = hydraS1Accounts[7];\n    destination1 = hydraS1Accounts[1];\n    destination2 = hydraS1Accounts[3];\n\n    // 3 - Generate data source\n    allList = generateGroups(hydraS1Accounts);\n    const { dataFormat, groups } = await generateAttesterGroups(allList);\n    const { dataFormat: dataFormat2, groups: groups2 } = await generateAttesterGroups(allList);\n    const { dataFormat: dataFormatAccountBound, groups: groupsAccountBound } =\n      await generateHydraS1AccountboundAttesterGroups(allList);\n\n    registryTree1 = dataFormat.registryTree;\n    registryTree2 = dataFormat2.registryTree;\n    registryTreeAccountBound = dataFormatAccountBound.registryTree;\n    accountsTree1 = dataFormat.accountsTrees[0];\n    accountsTree2 = dataFormat2.accountsTrees[0];\n    accountsTreeAccountBound = dataFormatAccountBound.accountsTrees[0];\n    group1 = groups[0];\n    group2 = groups2[0];\n    groupAccountBound = groupsAccountBound[0];\n    source1Value = accountsTree1.getValue(BigNumber.from(source1.identifier).toHexString());\n    source2Value = accountsTree2.getValue(BigNumber.from(source2.identifier).toHexString());\n    source3Value = accountsTree1.getValue(BigNumber.from(source3.identifier).toHexString());\n    source4Value = accountsTreeAccountBound.getValue(\n      BigNumber.from(source4.identifier).toHexString()\n    );\n\n    // 4 - Init Proving scheme\n    hydraS1ProverPrevious = new HydraS1ProverPrevious(registryTree1, commitmentMapperPubKey);\n    hydraS1Prover1 = new HydraS1Prover(registryTree1, commitmentMapperPubKey);\n    hydraS1Prover2 = new HydraS1Prover(registryTree2, commitmentMapperPubKey);\n    hydraS1ProverAccountBound = new HydraS1ProverPrevious(\n      registryTreeAccountBound,\n      commitmentMapperPubKey\n    );\n    pythia1Prover = new Pythia1Prover();\n\n    cooldownDuration = 60 * 60 * 24; // 1 day\n  });\n\n  /*************************************************************************************/\n  /********************************** SETUP ********************************************/\n  /*************************************************************************************/\n\n  describe('Deployments, setup contracts and prepare test requests', () => {\n    it('Should retrieve core contracts and update roots for attester', async () => {\n      // Deploy Sismo Protocol Core contracts\n      availableRootsRegistry = AvailableRootsRegistry__factory.connect(\n        config.availableRootsRegistry.address,\n        await impersonateAddress(hre, config.availableRootsRegistry.owner)\n      ) as AvailableRootsRegistry;\n\n      badges = Badges__factory.connect(\n        config.badges.address,\n        await impersonateAddress(hre, config.badges.owner)\n      ) as Badges;\n\n      front = Front__factory.connect(\n        config.front.address,\n        await impersonateAddress(hre, randomSigner.address, true)\n      ) as Front;\n\n      hydraS1SimpleAttester = HydraS1SimpleAttester__factory.connect(\n        oldConfig.hydraS1SimpleAttester.address,\n        await impersonateAddress(hre, randomSigner.address, true)\n      ) as HydraS1SimpleAttester;\n\n      hydraS1AccountboundAttester = HydraS1AccountboundAttester__factory.connect(\n        oldConfig.hydraS1AccountboundAttester.address,\n        await impersonateAddress(hre, randomSigner.address, true)\n      );\n\n      attestationsRegistry = AttestationsRegistry__factory.connect(\n        config.attestationsRegistry.address,\n        await impersonateAddress(hre, config.attestationsRegistry.owner)\n      ) as AttestationsRegistry;\n\n      pythia1SimpleAttester = Pythia1SimpleAttester__factory.connect(\n        config.synapsPythia1SimpleAttester.address,\n        await impersonateAddress(hre, config.synapsPythia1SimpleAttester.owner, true)\n      ) as Pythia1SimpleAttester;\n\n      commitmentMapperRegistry = CommitmentMapperRegistry__factory.connect(\n        config.commitmentMapper.address,\n        await impersonateAddress(hre, config.commitmentMapper.owner, true)\n      ) as CommitmentMapperRegistry;\n\n      await (\n        await commitmentMapperRegistry.updateCommitmentMapperEdDSAPubKey(commitmentMapperPubKey, {\n          gasLimit: 600000,\n        })\n      ).wait();\n\n      await (\n        await availableRootsRegistry.registerRootForAttester(\n          hydraS1SimpleAttester.address,\n          registryTree1.getRoot(),\n          { gasLimit: 600000 }\n        )\n      ).wait();\n\n      await (\n        await availableRootsRegistry.registerRootForAttester(\n          hydraS1AccountboundAttester.address,\n          registryTreeAccountBound.getRoot(),\n          { gasLimit: 600000 }\n        )\n      ).wait();\n\n      earlyUserCollectionId = await front.EARLY_USER_COLLECTION();\n\n      const pythiaOwner = await impersonateAddress(hre, await pythia1SimpleAttester.owner(), true);\n      const commitmentSignerPubKey = await commitmentSigner.getPublicKey();\n\n      await (\n        await pythia1SimpleAttester\n          .connect(pythiaOwner)\n          .updateCommitmentSignerPubKey(commitmentSignerPubKey, { gasLimit: 600000 })\n      ).wait();\n    });\n  });\n\n  /*******************************************************************************************************/\n  /************************ ATTESTATIONS AND BADGES GENERATIONS (BEFORE UPDATE) **************************/\n  /*******************************************************************************************************/\n\n  describe('Test attestations generations (before proxy update)', () => {\n    it('Should prepare test requests', async () => {\n      externalNullifier1 = await generateExternalNullifier(\n        hydraS1SimpleAttester.address,\n        group1.properties.groupIndex\n      );\n\n      externalNullifierAccountBound = await generateExternalNullifier(\n        hydraS1AccountboundAttester.address,\n        groupAccountBound.properties.groupIndex\n      );\n\n      request3 = {\n        claims: [\n          {\n            groupId: group1.id,\n            claimedValue: source3Value,\n            extraData: encodeGroupProperties(group1.properties),\n          },\n        ],\n        destination: BigNumber.from(destination1.identifier).toHexString(),\n      };\n\n      proofRequest3 = (\n        await hydraS1ProverPrevious.generateSnarkProof({\n          source: source3,\n          destination: destination1,\n          claimedValue: source3Value,\n          chainId: chainId,\n          accountsTree: accountsTree1,\n          ticketIdentifier: externalNullifier1,\n          isStrict: !group1.properties.isScore,\n        })\n      ).toBytes();\n\n      request4 = {\n        claims: [\n          {\n            groupId: groupAccountBound.id,\n            claimedValue: source4Value,\n            extraData: encodeHydraS1AccountboundGroupProperties(groupAccountBound.properties),\n          },\n        ],\n        destination: BigNumber.from(destination1.identifier).toHexString(),\n      };\n\n      proofRequest4 = (\n        await hydraS1ProverAccountBound.generateSnarkProof({\n          source: source4,\n          destination: destination1,\n          claimedValue: source4Value,\n          chainId: chainId,\n          accountsTree: accountsTreeAccountBound,\n          ticketIdentifier: externalNullifierAccountBound,\n          isStrict: !groupAccountBound.properties.isScore,\n        })\n      ).toBytes();\n\n      [attestationsRequested3, attestationsRequested4] = await front.batchBuildAttestations(\n        [hydraS1SimpleAttester.address, hydraS1AccountboundAttester.address],\n        [request3, request4],\n        [proofRequest3, proofRequest4]\n      );\n\n      snapshotId = await evmSnapshot(hre);\n    });\n\n    it('Should generate attestations from hydra s1 simple and hydra s1 accountbound via batch', async () => {\n      const tx = await front.batchGenerateAttestations(\n        [hydraS1SimpleAttester.address, hydraS1AccountboundAttester.address],\n        [request3, request4],\n        [proofRequest3, proofRequest4],\n        { gasLimit: 1200000 }\n      );\n      const { events } = await tx.wait();\n\n      const attestationsValues = await attestationsRegistry.getAttestationValueBatch(\n        [attestationsRequested3[0].collectionId, attestationsRequested4[0].collectionId],\n        [request3.destination, request4.destination]\n      );\n\n      const expectedAttestationsValues = [\n        attestationsRequested3[0].value,\n        attestationsRequested4[0].value,\n      ];\n\n      expect(attestationsValues).to.be.eql(expectedAttestationsValues);\n\n      const balances = await badges.balanceOfBatch(\n        [request3.destination, request4.destination],\n        [attestationsRequested3[0].collectionId, attestationsRequested4[0].collectionId]\n      );\n\n      const expectedBalances = expectedAttestationsValues;\n\n      expect(balances).to.be.eql(expectedBalances);\n    });\n\n    it('Should reset contracts', async () => {\n      await evmRevert(hre, snapshotId);\n\n      const attestationsValues = await attestationsRegistry.getAttestationValueBatch(\n        [attestationsRequested3[0].collectionId, attestationsRequested4[0].collectionId],\n        [request3.destination, request4.destination]\n      );\n\n      const expectedAttestationsValues = [BigNumber.from(0), BigNumber.from(0)];\n\n      expect(attestationsValues).to.be.eql(expectedAttestationsValues);\n\n      const balances = await badges.balanceOfBatch(\n        [request3.destination, request4.destination],\n        [attestationsRequested3[0].collectionId, attestationsRequested4[0].collectionId]\n      );\n\n      const expectedBalances = expectedAttestationsValues;\n\n      expect(balances).to.be.eql(expectedBalances);\n    });\n    it('Should generate attestations from hydra s1 simple and hydra s1 accountbound via front contract and two separate txs', async () => {\n      const tx = await front.generateAttestations(\n        hydraS1SimpleAttester.address,\n        request3,\n        proofRequest3,\n        { gasLimit: 600000 }\n      );\n      await front.generateAttestations(\n        hydraS1AccountboundAttester.address,\n        request4,\n        proofRequest4,\n        {\n          gasLimit: 600000,\n        }\n      );\n      const { events } = await tx.wait();\n\n      const attestationsValues = await attestationsRegistry.getAttestationValueBatch(\n        [attestationsRequested3[0].collectionId, attestationsRequested4[0].collectionId],\n        [request3.destination, request4.destination]\n      );\n\n      const expectedAttestationsValues = [\n        attestationsRequested3[0].value,\n        attestationsRequested4[0].value,\n      ];\n\n      expect(attestationsValues).to.be.eql(expectedAttestationsValues);\n\n      const balances = await badges.balanceOfBatch(\n        [request3.destination, request4.destination],\n        [attestationsRequested3[0].collectionId, attestationsRequested4[0].collectionId]\n      );\n\n      const expectedBalances = expectedAttestationsValues;\n\n      expect(balances).to.be.eql(expectedBalances);\n    });\n\n    it('Should revert because HydraS1SimpleAttester is not accountbound', async () => {\n      // change destination in the request\n      const request3bis = {\n        ...request3,\n        destination: BigNumber.from(destination2.identifier).toHexString(),\n      };\n\n      const proofRequest3bis = (\n        await hydraS1Prover1.generateSnarkProof({\n          source: source3,\n          destination: destination2, // we change the destination also in the proof\n          claimedValue: source3Value,\n          chainId: chainId,\n          accountsTree: accountsTree1,\n          externalNullifier: externalNullifier1,\n          isStrict: !group1.properties.isScore,\n        })\n      ).toBytes();\n\n      await expect(\n        front.generateAttestations(hydraS1SimpleAttester.address, request3bis, proofRequest3bis, {\n          gasLimit: 800000,\n        })\n      ).to.be.reverted;\n    });\n  });\n\n  /**********************************************************************************************/\n  /********************************** PROXIES UPDATE ********************************************/\n  /**********************************************************************************************/\n\n  describe('Update Implementation', () => {\n    it('Should run the upgrade script', async () => {\n      // deploy new verifiers for hydraS1 and Pythia1 and upgrade attester proxies\n      ({ hydraS1Verifier, hydraS1AccountboundAttester } = await hre.run(\n        '3-new-hydra-s1-verifier-and-upgrade-hydra-s1-simple-proxy',\n        {\n          options: { manualConfirm: false, log: false },\n        }\n      ));\n\n      await (\n        await availableRootsRegistry.registerRootForAttester(\n          hydraS1AccountboundAttester.address,\n          registryTree2.getRoot()\n        )\n      ).wait();\n\n      snapshotId = await evmSnapshot(hre);\n    });\n  });\n\n  describe('Configuration checks', () => {\n    it('Should check the address of the proxy', async () => {\n      expect(hydraS1AccountboundAttester.address).to.be.eql(\n        config.hydraS1AccountboundAttester.address\n      );\n    });\n\n    it('Should have setup the owner correctly', async () => {\n      expect(await hydraS1AccountboundAttester.owner()).to.be.eql(\n        config.hydraS1AccountboundAttester.owner\n      );\n    });\n\n    it('Should get the version correctly', async () => {\n      expect(await hydraS1AccountboundAttester.IMPLEMENTATION_VERSION()).to.be.eql(4);\n    });\n\n    it('Should revert when trying to call initialize again', async () => {\n      await expect(\n        hydraS1AccountboundAttester\n          .connect(await impersonateAddress(hre, config.hydraS1AccountboundAttester.owner))\n          .initialize(randomSigner.address, { gasLimit: 600000 })\n      ).to.be.revertedWith('Initializable: contract is already initialized');\n    });\n\n    it('should test the collectionIds of the Hydra S1 Accountbound attester', async () => {\n      // should be the same that the old hydraS1SimpleAttester\n      expect(await hydraS1AccountboundAttester.AUTHORIZED_COLLECTION_ID_FIRST()).to.be.equal(\n        await hydraS1SimpleAttester.AUTHORIZED_COLLECTION_ID_FIRST()\n      );\n      expect(await hydraS1AccountboundAttester.AUTHORIZED_COLLECTION_ID_LAST()).to.be.equal(\n        await hydraS1SimpleAttester.AUTHORIZED_COLLECTION_ID_LAST()\n      );\n    });\n\n    it('Should check that owner is the owner referenced in the config', async () => {\n      // should check that the owner is not the 0x0 address and owner is well set\n      expect(await hydraS1AccountboundAttester.owner()).to.be.not.eql(\n        '0x0000000000000000000000000000000000000000'\n      );\n      expect(await hydraS1AccountboundAttester.owner()).to.be.eql(\n        config.hydraS1AccountboundAttester.owner\n      );\n    });\n  });\n\n  describe('prepare test requests (group properties encoding has changed)', () => {\n    it('Should prepare test requests', async () => {\n      externalNullifier1 = await generateExternalNullifier(\n        hydraS1SimpleAttester.address,\n        group1.properties.groupIndex\n      );\n\n      externalNullifier2 = await generateExternalNullifier(\n        hydraS1AccountboundAttester.address,\n        group2.properties.groupIndex\n      );\n\n      request1 = {\n        claims: [\n          {\n            groupId: group1.id,\n            claimedValue: source1Value,\n            extraData: encodeGroupProperties(group1.properties),\n          },\n        ],\n        destination: BigNumber.from(destination1.identifier).toHexString(),\n      };\n\n      userParams1 = {\n        source: source1,\n        destination: destination1,\n        claimedValue: source1Value,\n        chainId: chainId,\n        accountsTree: accountsTree1,\n        externalNullifier: externalNullifier1,\n        isStrict: !group1.properties.isScore,\n      };\n\n      inputs1 = await hydraS1Prover1.generateInputs(userParams1);\n\n      proofRequest1 = (await hydraS1Prover1.generateSnarkProof(userParams1)).toBytes();\n\n      // change destination in the request\n      request2 = {\n        ...request1,\n        destination: BigNumber.from(destination2.identifier).toHexString(),\n      };\n\n      proofRequest2 = (\n        await hydraS1Prover1.generateSnarkProof({\n          source: source1,\n          destination: destination2, // we change the destination also in the proof\n          claimedValue: source1Value,\n          chainId: chainId,\n          accountsTree: accountsTree1,\n          externalNullifier: externalNullifier1,\n          isStrict: !group1.properties.isScore,\n        })\n      ).toBytes();\n\n      // same proofs and requests but with different destinations\n      [attestationsRequested1, attestationsRequested2] = await front.batchBuildAttestations(\n        [hydraS1AccountboundAttester.address, hydraS1AccountboundAttester.address],\n        [request1, request2],\n        [proofRequest1, proofRequest2]\n      );\n    });\n  });\n\n  /*************************************************************************************/\n  /************************ATTESTATIONS AND BADGES GENERATIONS**************************/\n  /*************************************************************************************/\n\n  describe('Test attestations generations', () => {\n    it('Should generate attestations hydra s1 accountbound via batch', async () => {\n      const tx = await front.batchGenerateAttestations(\n        [hydraS1AccountboundAttester.address],\n        [request1],\n        [proofRequest1],\n        { gasLimit: 600000 }\n      );\n\n      const { events } = await tx.wait();\n\n      const attestationsValues = await attestationsRegistry.getAttestationValueBatch(\n        [attestationsRequested1[0].collectionId],\n        [request1.destination]\n      );\n\n      const expectedAttestationsValues = [attestationsRequested1[0].value];\n\n      expect(attestationsValues).to.be.eql(expectedAttestationsValues);\n\n      const balances = await badges.balanceOfBatch(\n        [request1.destination],\n        [attestationsRequested1[0].collectionId]\n      );\n\n      const expectedBalances = expectedAttestationsValues;\n\n      expect(balances).to.be.eql(expectedBalances);\n    });\n\n    it('Should reset contracts to the proxy updates', async () => {\n      await evmRevert(hre, snapshotId);\n\n      const attestationsValues = await attestationsRegistry.getAttestationValueBatch(\n        [attestationsRequested1[0].collectionId],\n        [request1.destination]\n      );\n\n      const expectedAttestationsValues = [BigNumber.from(7)];\n\n      expect(attestationsValues).to.be.eql(expectedAttestationsValues);\n\n      const balances = await badges.balanceOfBatch(\n        [request1.destination],\n        [attestationsRequested1[0].collectionId]\n      );\n\n      const expectedBalances = expectedAttestationsValues;\n\n      expect(balances).to.be.eql(expectedBalances);\n    });\n\n    it('Should generate attestations from hydra s1 accountbound via front', async () => {\n      const tx = await front.generateAttestations(\n        hydraS1AccountboundAttester.address,\n        request1,\n        proofRequest1,\n        { gasLimit: 800000 }\n      );\n\n      const { events } = await tx.wait();\n\n      const attestationsValues = await attestationsRegistry.getAttestationValueBatch(\n        [attestationsRequested1[0].collectionId],\n        [request1.destination]\n      );\n\n      const expectedAttestationsValues = [attestationsRequested1[0].value];\n\n      expect(attestationsValues).to.be.eql(expectedAttestationsValues);\n\n      const balances = await badges.balanceOfBatch(\n        [request1.destination],\n        [attestationsRequested1[0].collectionId]\n      );\n\n      const expectedBalances = expectedAttestationsValues;\n\n      expect(balances).to.be.eql(expectedBalances);\n    });\n\n    it('Should revert because no cooldown duration has been set for the groupId', async () => {\n      await expect(\n        front.generateAttestations(hydraS1AccountboundAttester.address, request2, proofRequest2, {\n          gasLimit: 800000,\n        })\n      ).to.be.revertedWith(\n        `CooldownDurationNotSetForGroupIndex(${BigNumber.from(group1.properties.groupIndex)})`\n      );\n    });\n\n    it('Should set the cooldown duration for the groupId', async () => {\n      const hydraS1AccountboundAttesterOwner = await impersonateAddress(\n        hre,\n        await hydraS1AccountboundAttester.owner(),\n        true\n      );\n\n      await hydraS1AccountboundAttester\n        .connect(hydraS1AccountboundAttesterOwner)\n        .setCooldownDurationForGroupIndex(group1.properties.groupIndex, cooldownDuration, {\n          gasLimit: 100000,\n        });\n\n      expect(\n        await hydraS1AccountboundAttester.getCooldownDurationForGroupIndex(\n          BigNumber.from(group1.properties.groupIndex)\n        )\n      ).to.be.eql(cooldownDuration);\n    });\n\n    it('Should allow the minting of the attestation on another destination address (accountbound property), it should set the cooldown', async () => {\n      const tx = await front.generateAttestations(\n        hydraS1AccountboundAttester.address,\n        request2,\n        proofRequest2,\n        { gasLimit: 800000 }\n      );\n\n      const attestationsValues = await attestationsRegistry.getAttestationValueBatch(\n        [attestationsRequested1[0].collectionId, attestationsRequested2[0].collectionId],\n        [request1.destination, request2.destination]\n      );\n\n      const expectedAttestationsValues = [\n        BigNumber.from(0), // the attestation should be removed\n        attestationsRequested2[0].value,\n      ];\n\n      expect(attestationsValues).to.be.eql(expectedAttestationsValues);\n\n      const balances = await badges.balanceOfBatch(\n        [request1.destination, request2.destination],\n        [attestationsRequested1[0].collectionId, attestationsRequested2[0].collectionId]\n      );\n\n      const expectedBalances = expectedAttestationsValues;\n\n      expect(balances).to.be.eql(expectedBalances);\n\n      expect(\n        await hydraS1AccountboundAttester.getCooldownDurationForGroupIndex(\n          group1.properties.groupIndex\n        )\n      ).to.be.equal(cooldownDuration);\n    });\n\n    it('Should revert because the cooldown period is not yet finished', async () => {\n      // not yet the end of the cooldown period\n      await increaseTime(hre, cooldownDuration - 10);\n\n      await expect(\n        hydraS1AccountboundAttester.generateAttestations(request1, proofRequest1)\n      ).to.be.revertedWith(\n        `NullifierOnCooldown(${\n          inputs1.publicInputs.nullifier\n        }, \"${await hydraS1AccountboundAttester.getDestinationOfNullifier(\n          BigNumber.from(inputs1.publicInputs.nullifier)\n        )}\", ${1}, ${cooldownDuration})`\n      );\n    });\n\n    it('Should allow the minting of the attestation on another destination address, after the cooldown period (with a renewed group)', async () => {\n      await increaseTime(hre, cooldownDuration);\n      const latestCooldownStart = await hydraS1AccountboundAttester.getNullifierCooldownStart(\n        BigNumber.from(inputs1.publicInputs.nullifier)\n      );\n      const renewGenerationTimestamp = group1.properties.generationTimestamp + cooldownDuration;\n      // regenerate groups for attester with different timestamp\n      const { dataFormat, groups } = await generateAttesterGroups(allList, {\n        generationTimestamp: renewGenerationTimestamp,\n      });\n      // register new registry tree root on chain\n      await availableRootsRegistry.registerRootForAttester(\n        hydraS1AccountboundAttester.address,\n        dataFormat.registryTree.getRoot()\n      );\n\n      // create new prover using the new registryTree\n      const renewProver = new HydraS1Prover(dataFormat.registryTree, commitmentMapperPubKey);\n      const renewProof = await renewProver.generateSnarkProof({\n        ...userParams1,\n        destination: destination1,\n        accountsTree: dataFormat.accountsTrees[0],\n      });\n\n      const renewRequest = {\n        claims: [\n          {\n            groupId: groups[0].id,\n            claimedValue: source1Value,\n            extraData: encodeGroupProperties({\n              ...group1.properties,\n              generationTimestamp: renewGenerationTimestamp,\n            }),\n          },\n        ],\n        destination: BigNumber.from(destination1.identifier).toHexString(),\n      };\n\n      const tx = await front.generateAttestations(\n        hydraS1AccountboundAttester.address,\n        renewRequest,\n        renewProof.toBytes(),\n        { gasLimit: 800000 }\n      );\n\n      expect(\n        await hydraS1AccountboundAttester.getDestinationOfNullifier(\n          BigNumber.from(inputs1.publicInputs.nullifier)\n        )\n      ).to.be.eql(BigNumber.from(destination1.identifier).toHexString());\n\n      // the burnCount is 2 since we changed from destination1 to destination2 previously (setting the burnCOunt to 1 and setting the cooldown)\n      // and in this test we just changed back from destination2 to destination1 (setting the burnCount to 2 and a new cooldownStart)\n      expect(\n        await hydraS1AccountboundAttester.getNullifierBurnCount(\n          BigNumber.from(inputs1.publicInputs.nullifier)\n        )\n      ).to.be.eql(2);\n    });\n  });\n});\n"
  },
  {
    "path": "tasks/deploy-tasks/full/4-upgrade-attestations-registry-proxy-and-badges-proxy.task.ts",
    "content": "import { task } from 'hardhat/config';\nimport { HardhatRuntimeEnvironment } from 'hardhat/types';\nimport { DeployOptions, getDeployer } from '../utils';\nimport { AttestationsRegistry, Badges } from 'types';\nimport { DeployedAttestationsRegistry } from 'tasks/deploy-tasks/unit/core/deploy-attestations-registry.task';\nimport { deploymentsConfig } from '../deployments-config';\nimport { DeployedBadges } from 'tasks/deploy-tasks/unit/core/deploy-badges.task';\n\nexport interface Deployed4 {\n  attestationsRegistry: AttestationsRegistry;\n  badges: Badges;\n}\n\nasync function deploymentAction(\n  { options }: { options: DeployOptions },\n  hre: HardhatRuntimeEnvironment\n): Promise<Deployed4> {\n  const config = deploymentsConfig[process.env.FORK_NETWORK ?? hre.network.name];\n  options = { ...config.deployOptions, ...options };\n\n  if (options.manualConfirm || options.log) {\n    console.log('4-upgrade-attestations-registry-proxy: ', hre.network.name);\n  }\n\n  // The following proxy will be updated:\n  // - AttestationRegistry => introduce attributes names and values for attestationsCollection\n  //                          values go from 0 to 15, if the value is 0 the attribute is disabled, else it is enabled with the value set\n  //                          + reinitializer modifier added and version as constant\n  // - Badges => add getters for attestations issuer, timestamp and extradata\n  //          => add getters for attribute values and names\n  //          => + reinitializer modifier added and version as constant\n\n  // Upgrade attestations registry\n  const { attestationsRegistry: newAttestationsRegistry } = (await hre.run(\n    'deploy-attestations-registry',\n    {\n      badges: config.badges.address,\n      owner: config.attestationsRegistry.owner,\n      options: {\n        ...options,\n        isImplementationUpgrade: true, // implementation version has been bumped from v2 to v3\n        proxyAddress: config.attestationsRegistry.address,\n      },\n    }\n  )) as DeployedAttestationsRegistry;\n\n  // Upgrade Badges\n  const { badges: newBadges } = (await hre.run('deploy-badges', {\n    uri: config.badges.uri,\n    owner: config.badges.owner,\n    options: {\n      ...options,\n      isImplementationUpgrade: true, // implementation version has been bumped from v2 to v3\n      proxyAddress: config.badges.address,\n    },\n  })) as DeployedBadges;\n\n  return {\n    attestationsRegistry: newAttestationsRegistry,\n    badges: newBadges,\n  };\n}\n\ntask('4-upgrade-attestations-registry-proxy-and-badges-proxy').setAction(deploymentAction);\n"
  },
  {
    "path": "tasks/deploy-tasks/full/4-upgrade-attestations-registry-proxy-and-badges-proxy.test-fork.ts",
    "content": "import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers';\nimport { expect } from 'chai';\nimport hre, { ethers } from 'hardhat';\nimport { getImplementation } from 'utils';\nimport { deploymentsConfig } from '../deployments-config';\nimport {\n  AttestationsRegistry,\n  AttestationsRegistry__factory,\n  AvailableRootsRegistry,\n  AvailableRootsRegistry__factory,\n  Badges,\n  HydraS1SimpleAttester__factory,\n  TransparentUpgradeableProxy__factory,\n} from '../../../types';\nimport { formatBytes32String, parseBytes32String } from 'ethers/lib/utils';\nimport { evmSnapshot, impersonateAddress } from '../../../test/utils';\nimport { BigNumber, utils } from 'ethers';\nimport { HydraS1SimpleAttester } from 'types/HydraS1SimpleAttester';\nimport { AttestationStruct } from 'types/AttestationsRegistry';\n\n// Launch with command\n// FORK=true FORK_NETWORK=goerli npx hardhat test ./tasks/deploy-tasks/full/4-upgrade-attestations-registry-proxy-and-badges-proxy.test-fork.ts\n\ndescribe('FORK-Test Upgrade AttestationsRegistry contract with attributes and reinitializer and Badges with getter and reinitializer', () => {\n  let deployer: SignerWithAddress;\n  let randomSigner: SignerWithAddress;\n  let secondDeployer: SignerWithAddress;\n  let notOwner: SignerWithAddress;\n  let issuer: SignerWithAddress;\n\n  let attestationsRegistry: AttestationsRegistry;\n  let availableRootsRegistry: AvailableRootsRegistry;\n  let hydraS1SimpleAttester: HydraS1SimpleAttester;\n  let badges: Badges;\n\n  let firstAuthorizedRange: IssuerRange;\n  let secondAuthorizedRange: IssuerRange;\n  let attestations: Attestations;\n\n  let snapshotId: string;\n\n  type IssuerRange = {\n    min: number;\n    max: number;\n  };\n\n  type Attestations = {\n    first: AttestationStruct;\n    second: AttestationStruct;\n  };\n\n  const config = deploymentsConfig[process.env.FORK_NETWORK ?? hre.network.name];\n\n  before(async () => {\n    const signers = await ethers.getSigners();\n    [deployer, secondDeployer, notOwner, issuer, , , randomSigner] = signers;\n\n    firstAuthorizedRange = {\n      min: 3,\n      max: 6,\n    };\n\n    secondAuthorizedRange = {\n      min: 9,\n      max: 12,\n    };\n\n    attestations = {\n      first: {\n        collectionId: firstAuthorizedRange.min,\n        owner: randomSigner.address,\n        issuer: issuer.address,\n        value: 1,\n        timestamp: Math.floor(Date.now() / 1000),\n        extraData: [],\n      },\n      second: {\n        collectionId: secondAuthorizedRange.min,\n        owner: randomSigner.address,\n        issuer: issuer.address,\n        value: 1,\n        timestamp: Math.floor(Date.now() / 1000),\n        extraData: [],\n      },\n    };\n  });\n\n  describe('Setup fork', () => {\n    it('Should retrieve core contracts', async () => {\n      // Deploy Sismo Protocol Core contracts\n      attestationsRegistry = AttestationsRegistry__factory.connect(\n        config.attestationsRegistry.address,\n        await impersonateAddress(hre, config.attestationsRegistry.owner)\n      ) as AttestationsRegistry;\n\n      availableRootsRegistry = AvailableRootsRegistry__factory.connect(\n        config.availableRootsRegistry.address,\n        await impersonateAddress(hre, config.availableRootsRegistry.owner)\n      ) as AvailableRootsRegistry;\n\n      hydraS1SimpleAttester = HydraS1SimpleAttester__factory.connect(\n        config.hydraS1SimpleAttester.address,\n        await impersonateAddress(hre, randomSigner.address, true)\n      ) as HydraS1SimpleAttester;\n    });\n  });\n\n  describe('Should record attestation', async () => {\n    it('Should authorize range for issuer', async () => {\n      await attestationsRegistry\n        .connect(await impersonateAddress(hre, config.attestationsRegistry.owner, true))\n        .authorizeRanges(issuer.address, [firstAuthorizedRange, secondAuthorizedRange], {\n          gasLimit: 600000,\n        });\n    });\n\n    it('Should record the right data', async () => {\n      const recordAttestationsTransaction = await attestationsRegistry\n        .connect(await impersonateAddress(hre, issuer.address))\n        .recordAttestations([attestations.first, attestations.second], { gasLimit: 600000 });\n\n      // 1 - Checks that the transaction emitted the event\n      await expect(recordAttestationsTransaction)\n        .to.emit(attestationsRegistry, 'AttestationRecorded')\n        .withArgs([\n          BigNumber.from(attestations.first.collectionId),\n          attestations.first.owner,\n          attestations.first.issuer,\n          BigNumber.from(attestations.first.value),\n          attestations.first.timestamp,\n          ethers.utils.hexlify(attestations.first.extraData),\n        ]);\n\n      await expect(recordAttestationsTransaction)\n        .to.emit(attestationsRegistry, 'AttestationRecorded')\n        .withArgs([\n          BigNumber.from(attestations.second.collectionId),\n          attestations.second.owner,\n          attestations.second.issuer,\n          BigNumber.from(attestations.second.value),\n          attestations.second.timestamp,\n          ethers.utils.hexlify(attestations.second.extraData),\n        ]);\n    });\n  });\n\n  describe('Get Attestation Data', () => {\n    it('Should return the right data from the attestations registry contract', async () => {\n      expect(\n        await attestationsRegistry.getAttestationData(\n          attestations.first.collectionId,\n          randomSigner.address\n        )\n      ).to.be.eql([\n        attestations.first.issuer,\n        BigNumber.from(attestations.first.value),\n        attestations.first.timestamp,\n        ethers.utils.hexlify(attestations.first.extraData),\n      ]);\n\n      expect(\n        await attestationsRegistry.getAttestationData(\n          attestations.second.collectionId,\n          randomSigner.address\n        )\n      ).to.be.eql([\n        attestations.second.issuer,\n        BigNumber.from(attestations.second.value),\n        attestations.second.timestamp,\n        ethers.utils.hexlify(attestations.second.extraData),\n      ]);\n    });\n  });\n\n  describe('Update Implementation', () => {\n    it('Should run the upgrade script', async () => {\n      await impersonateAddress(\n        hre,\n        config.deployOptions.proxyAdmin ?? config.attestationsRegistry.owner\n      );\n\n      ({ attestationsRegistry, badges } = await hre.run(\n        '4-upgrade-attestations-registry-proxy-and-badges-proxy',\n        {\n          options: { manualConfirm: false, log: false },\n        }\n      ));\n\n      snapshotId = await evmSnapshot(hre);\n    });\n\n    describe('Configuration checks', () => {\n      it('Should check the addresses of the proxies', async () => {\n        expect(attestationsRegistry.address).to.be.eql(config.attestationsRegistry.address);\n        expect(badges.address).to.be.eql(config.badges.address);\n      });\n\n      it('Should have setup the owner correctly', async () => {\n        expect(await attestationsRegistry.owner()).to.be.eql(config.attestationsRegistry.owner);\n        expect(\n          await badges.hasRole(await badges.DEFAULT_ADMIN_ROLE(), config.badges.owner)\n        ).to.be.eql(true);\n      });\n\n      it('Should get the version correctly', async () => {\n        expect(await attestationsRegistry.IMPLEMENTATION_VERSION()).to.be.eql(3);\n        expect(await badges.IMPLEMENTATION_VERSION()).to.be.eql(3);\n      });\n\n      it('Should revert when trying to call initialize again', async () => {\n        await expect(\n          attestationsRegistry\n            .connect(await impersonateAddress(hre, config.attestationsRegistry.owner))\n            .initialize(randomSigner.address, { gasLimit: 600000 })\n        ).to.be.revertedWith('Initializable: contract is already initialized');\n\n        await expect(\n          badges\n            .connect(await impersonateAddress(hre, config.badges.owner))\n            .initialize('fake_uri', randomSigner.address, { gasLimit: 600000 })\n        ).to.be.revertedWith('Initializable: contract is already initialized');\n      });\n    });\n\n    it('Should revert with Ownable error', async () => {\n      expect(\n        attestationsRegistry\n          .connect(deployer)\n          .createNewAttributes(\n            [0, 1],\n            [formatBytes32String('CURATED'), formatBytes32String('SYBIL_RESISTANCE')],\n            { gasLimit: 50000 }\n          )\n      ).to.be.revertedWith('Ownable: caller is not the owner');\n    });\n\n    it('Should create new attributes', async () => {\n      const attributesCreated = await attestationsRegistry\n        .connect(await impersonateAddress(hre, config.attestationsRegistry.owner, true))\n        .createNewAttributes(\n          [0, 1],\n          [formatBytes32String('CURATED'), formatBytes32String('SYBIL RESISTANCE')],\n          { gasLimit: 100000 }\n        );\n\n      await expect(attributesCreated)\n        .to.emit(attestationsRegistry, 'NewAttributeCreated')\n        .withArgs(0, formatBytes32String('CURATED'));\n\n      await expect(attributesCreated)\n        .to.emit(attestationsRegistry, 'NewAttributeCreated')\n        .withArgs(1, formatBytes32String('SYBIL RESISTANCE'));\n    });\n\n    it('Should set new attributes to attestationsCollection 11 with values 1 and 15', async () => {\n      const attributesSet = await attestationsRegistry\n        .connect(await impersonateAddress(hre, config.attestationsRegistry.owner))\n        .setAttributesValuesForAttestationsCollections(\n          [attestations.first.collectionId, attestations.second.collectionId],\n          [0, 1],\n          [1, 15],\n          {\n            gasLimit: 100000,\n          }\n        );\n\n      await expect(attributesSet)\n        .to.emit(attestationsRegistry, 'AttestationsCollectionAttributeSet')\n        .withArgs(attestations.first.collectionId, 0, 1);\n\n      await expect(attributesSet)\n        .to.emit(attestationsRegistry, 'AttestationsCollectionAttributeSet')\n        .withArgs(attestations.second.collectionId, 1, 15);\n      const res = await attestationsRegistry.getAttributesNamesAndValuesForAttestationsCollection(\n        attestations.first.collectionId\n      );\n      expect([['CURATED'], [1]]).to.be.eql([[parseBytes32String(res[0][0])], res[1]]);\n    });\n  });\n\n  describe('Verify Attestations Data (after proxy update)', () => {\n    it('Should return the right data', async () => {\n      expect(\n        await attestationsRegistry.getAttestationData(\n          attestations.first.collectionId,\n          randomSigner.address\n        )\n      ).to.be.eql([\n        attestations.first.issuer,\n        BigNumber.from(attestations.first.value),\n        attestations.first.timestamp,\n        ethers.utils.hexlify(attestations.first.extraData),\n      ]);\n\n      expect(\n        await attestationsRegistry.getAttestationData(\n          attestations.second.collectionId,\n          randomSigner.address\n        )\n      ).to.be.eql([\n        attestations.second.issuer,\n        BigNumber.from(attestations.second.value),\n        attestations.second.timestamp,\n        ethers.utils.hexlify(attestations.second.extraData),\n      ]);\n    });\n\n    it('Should update the value when recording again attestations', async () => {\n      const recordAttestationsTransaction = await attestationsRegistry\n        .connect(await impersonateAddress(hre, issuer.address))\n        .recordAttestations([\n          {\n            ...attestations.first,\n            value: 2,\n          },\n          { ...attestations.second, value: 2 },\n        ]);\n\n      // 1 - Checks that the transaction emitted the event\n      await expect(recordAttestationsTransaction)\n        .to.emit(attestationsRegistry, 'AttestationRecorded')\n        .withArgs([\n          BigNumber.from(attestations.first.collectionId),\n          attestations.first.owner,\n          attestations.first.issuer,\n          BigNumber.from(2),\n          attestations.first.timestamp,\n          ethers.utils.hexlify(attestations.first.extraData),\n        ]);\n\n      await expect(recordAttestationsTransaction)\n        .to.emit(attestationsRegistry, 'AttestationRecorded')\n        .withArgs([\n          BigNumber.from(attestations.second.collectionId),\n          attestations.second.owner,\n          attestations.second.issuer,\n          BigNumber.from(2),\n          attestations.second.timestamp,\n          ethers.utils.hexlify(attestations.second.extraData),\n        ]);\n    });\n\n    it('Should return the right data from attestations registry contract', async () => {\n      expect(\n        await attestationsRegistry.getAttestationData(\n          attestations.first.collectionId,\n          randomSigner.address\n        )\n      ).to.be.eql([\n        attestations.first.issuer,\n        BigNumber.from(2),\n        attestations.first.timestamp,\n        ethers.utils.hexlify(attestations.first.extraData),\n      ]);\n\n      expect(\n        await attestationsRegistry.getAttestationData(\n          attestations.second.collectionId,\n          randomSigner.address\n        )\n      ).to.be.eql([\n        attestations.second.issuer,\n        BigNumber.from(2),\n        attestations.second.timestamp,\n        ethers.utils.hexlify(attestations.second.extraData),\n      ]);\n    });\n\n    it('Should return the right data from badges contract', async () => {\n      expect(\n        await badges.balanceOf(randomSigner.address, attestations.first.collectionId)\n      ).to.be.eql(BigNumber.from(2));\n      expect(\n        await badges.getBadgeIssuer(randomSigner.address, attestations.first.collectionId)\n      ).to.be.eql(attestations.first.issuer);\n\n      expect(\n        await badges.getBadgeTimestamp(randomSigner.address, attestations.first.collectionId)\n      ).to.be.eql(attestations.first.timestamp);\n\n      expect(\n        await badges.getBadgeExtraData(randomSigner.address, attestations.first.collectionId)\n      ).to.be.eql(ethers.utils.hexlify(attestations.first.extraData));\n\n      expect(await badges.getAttributeValueForBadge(attestations.first.collectionId, 0)).to.be.eql(\n        1\n      );\n\n      expect(await badges.getAttributeValueForBadge(attestations.second.collectionId, 1)).to.be.eql(\n        15\n      );\n      const res = await badges.getAttributesNamesAndValuesForBadge(\n        attestations.second.collectionId\n      );\n      expect([['SYBIL RESISTANCE'], [15]]).to.be.eql([[parseBytes32String(res[0][0])], res[1]]);\n    });\n  });\n});\n"
  },
  {
    "path": "tasks/deploy-tasks/full/5-upgrade-proxies-with-reinitializer/5-upgrade-proxies-with-reinitializer-available-roots-registry.test-fork.ts",
    "content": "import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers';\nimport { expect } from 'chai';\nimport hre, { ethers } from 'hardhat';\nimport { evmSnapshot, impersonateAddress } from '../../../../test/utils';\nimport { deploymentsConfig } from '../../../../tasks/deploy-tasks/deployments-config';\nimport {\n  AvailableRootsRegistry,\n  AvailableRootsRegistry__factory,\n  TransparentUpgradeableProxy__factory,\n} from '../../../../types';\nimport { getImplementation } from '../../../../utils';\nimport { Deployed5 } from 'tasks/deploy-tasks/full/5-upgrade-proxies-with-reinitializer.task';\n\ndescribe('FORK Test AvailableRootsRegistry contract', () => {\n  let deployer: SignerWithAddress;\n  let secondDeployer: SignerWithAddress;\n  let attacker: SignerWithAddress;\n  let attester1: SignerWithAddress;\n  let attester2: SignerWithAddress;\n  let randomSigner: SignerWithAddress;\n  let availableRootsRegistryOwner: SignerWithAddress;\n\n  let availableRootsRegistry: AvailableRootsRegistry;\n\n  let snapshotId: string;\n\n  const config = deploymentsConfig[process.env.FORK_NETWORK ?? hre.network.name];\n\n  before(async () => {\n    const signers = await hre.ethers.getSigners();\n    [deployer, secondDeployer, attacker, attester1, attester2, , , , , , randomSigner] = signers;\n  });\n\n  /*************************************************************************************/\n  /********************************** DEPLOYMENTS **************************************/\n  /*************************************************************************************/\n  describe('Deployments', () => {\n    it('Should deploy, setup and test the constructed values of the contract', async () => {\n      availableRootsRegistry = AvailableRootsRegistry__factory.connect(\n        config.availableRootsRegistry.address,\n        await impersonateAddress(hre, config.availableRootsRegistry.owner)\n      ) as AvailableRootsRegistry;\n\n      // 0 - Checks that the owner is set to the deployer address\n      expect((await availableRootsRegistry.owner()).toLowerCase()).to.be.eql(\n        config.availableRootsRegistry.owner\n      );\n    });\n  });\n\n  describe('Update Implementation', () => {\n    it('Should run the upgrade script', async () => {\n      await impersonateAddress(\n        hre,\n        config.deployOptions.proxyAdmin ?? config.synapsPythia1SimpleAttester.owner\n      );\n\n      ({ availableRootsRegistry } = await hre.run('5-upgrade-proxies-with-reinitializer', {\n        options: { manualConfirm: false, log: false },\n      })) as Deployed5;\n\n      availableRootsRegistryOwner = await impersonateAddress(\n        hre,\n        await availableRootsRegistry.owner()\n      );\n\n      snapshotId = await evmSnapshot(hre);\n    });\n  });\n\n  describe('Configuration checks', () => {\n    it('Should check the address of the proxy', async () => {\n      expect(availableRootsRegistry.address).to.be.eql(config.availableRootsRegistry.address);\n    });\n\n    it('Should have setup the owner correctly', async () => {\n      expect(availableRootsRegistryOwner.address.toLowerCase()).to.be.eql(\n        config.availableRootsRegistry.owner\n      );\n    });\n\n    it('Should get the version correctly', async () => {\n      expect(await availableRootsRegistry.IMPLEMENTATION_VERSION()).to.be.eql(2);\n    });\n\n    it('Should revert when trying to call initialize again', async () => {\n      await expect(\n        availableRootsRegistry\n          .connect(availableRootsRegistryOwner)\n          .initialize(randomSigner.address, { gasLimit: 600000 })\n      ).to.be.revertedWith('Initializable: contract is already initialized');\n    });\n  });\n\n  /*************************************************************************************/\n  /***************************** REGISTER ROOT FOR ATTESTER ****************************/\n  /*************************************************************************************/\n  describe('Register root for attester', () => {\n    it('Should revert when the sender is not the owner of the contract', async () => {\n      await expect(\n        availableRootsRegistry.connect(attacker).registerRootForAttester(attester1.address, 1)\n      ).to.be.revertedWith('Ownable: caller is not the owner');\n    });\n\n    it('Should revert when the attester address is a zero address', async () => {\n      await expect(\n        availableRootsRegistry\n          .connect(availableRootsRegistryOwner)\n          .registerRootForAttester('0x0000000000000000000000000000000000000000', 1, {\n            gasLimit: 600000,\n          })\n      ).to.be.revertedWith('CannotRegisterForZeroAddress()');\n    });\n\n    it('Should register the root for the attester', async () => {\n      const registerRootForAttesterTransaction = await availableRootsRegistry\n        .connect(availableRootsRegistryOwner)\n        .registerRootForAttester(attester1.address, 1, {\n          gasLimit: 100000,\n        });\n\n      // 1 - Checks that the transaction emitted the event\n      await expect(registerRootForAttesterTransaction)\n        .to.emit(availableRootsRegistry, 'RegisteredRootForAttester')\n        .withArgs(attester1.address, 1);\n\n      // 2 - Checks that the root is registered for the attester\n      expect(await availableRootsRegistry._roots(attester1.address, 1)).to.be.true;\n\n      // 3 - Checks that the root is not registered for the second attester\n      expect(await availableRootsRegistry._roots(attester2.address, 1)).to.be.false;\n    });\n  });\n\n  /*************************************************************************************/\n  /**************************** UNREGISTER ROOT FOR ATTESTER ***************************/\n  /*************************************************************************************/\n  describe('Unregister root for attester', () => {\n    it('Should revert when the sender is not the owner of the contract', async () => {\n      await expect(\n        availableRootsRegistry.connect(attacker).unregisterRootForAttester(attester1.address, 1)\n      ).to.be.revertedWith('Ownable: caller is not the owner');\n    });\n\n    it('Should revert when the attester address is a zero address', async () => {\n      await expect(\n        availableRootsRegistry\n          .connect(availableRootsRegistryOwner)\n          .unregisterRootForAttester(ethers.constants.AddressZero, 1)\n      ).to.be.revertedWith('CannotUnregisterForZeroAddress()');\n    });\n\n    it('Should unregister the root for the attester', async () => {\n      const unregisterRootForAttesterTransaction = await availableRootsRegistry\n        .connect(availableRootsRegistryOwner)\n        .unregisterRootForAttester(attester1.address, 1);\n\n      // 1 - Checks that the transaction emitted the event\n      await expect(unregisterRootForAttesterTransaction)\n        .to.emit(availableRootsRegistry, 'UnregisteredRootForAttester')\n        .withArgs(attester1.address, 1);\n\n      // 2 - Checks that the root is unregistered for the attester\n      expect(await availableRootsRegistry._roots(attester1.address, 1)).to.be.false;\n    });\n  });\n\n  /*************************************************************************************/\n  /******************************* REGISTER ROOT FOR ALL *******************************/\n  /*************************************************************************************/\n  describe('Register root for all', () => {\n    it('Should revert when the sender is not the owner of the contract', async () => {\n      await expect(\n        availableRootsRegistry.connect(attacker).registerRootForAll(1)\n      ).to.be.revertedWith('Ownable: caller is not the owner');\n    });\n\n    it('Should register the root for the all attesters', async () => {\n      const registerRootForAllTransaction = await availableRootsRegistry\n        .connect(availableRootsRegistryOwner)\n        .registerRootForAll(1);\n\n      // 1 - Checks that the transaction emitted the event\n      await expect(registerRootForAllTransaction)\n        .to.emit(availableRootsRegistry, 'RegisteredRootForAll')\n        .withArgs(1);\n\n      // 2 - Checks that the root is registered globally (on address 0x0)\n      expect(await availableRootsRegistry._roots(ethers.constants.AddressZero, 1)).to.be.true;\n\n      // 3 - Checks that the root is available for the first & second attesters\n      expect(await availableRootsRegistry.isRootAvailableForAttester(attester1.address, 1)).to.be\n        .true;\n      expect(await availableRootsRegistry.isRootAvailableForAttester(attester2.address, 1)).to.be\n        .true;\n    });\n  });\n\n  /*************************************************************************************/\n  /****************************** UNREGISTER ROOT FOR ALL ******************************/\n  /*************************************************************************************/\n  describe('Unregister root for all', () => {\n    it('Should revert when the sender is not the owner of the contract', async () => {\n      await expect(\n        availableRootsRegistry.connect(attacker).unregisterRootForAll(1)\n      ).to.be.revertedWith('Ownable: caller is not the owner');\n    });\n\n    it('Should unregister the root for all the attesters', async () => {\n      const unregisterRootForAllTransaction = await availableRootsRegistry\n        .connect(availableRootsRegistryOwner)\n        .unregisterRootForAll(1);\n\n      // 1 - Checks that the transaction emitted the event\n      await expect(unregisterRootForAllTransaction)\n        .to.emit(availableRootsRegistry, 'UnregisteredRootForAll')\n        .withArgs(1);\n\n      // 2 - Checks that the root is unregistered globally (on address 0x0)\n      expect(await availableRootsRegistry._roots(ethers.constants.AddressZero, 1)).to.be.false;\n\n      // 3 - Checks that the root is not available for the first & second attesters\n      expect(await availableRootsRegistry.isRootAvailableForAttester(attester1.address, 1)).to.be\n        .false;\n      expect(await availableRootsRegistry.isRootAvailableForAttester(attester2.address, 1)).to.be\n        .false;\n    });\n  });\n\n  /*************************************************************************************/\n  /*************************** IS ROOT AVAILABLE FOR ATTESTER **************************/\n  /*************************************************************************************/\n  describe('Is root available for attester', () => {\n    describe('When the root is not available for the attester or globally', () => {\n      it('Should return false', async () => {\n        expect(\n          await availableRootsRegistry\n            .connect(availableRootsRegistryOwner)\n            .isRootAvailableForAttester(attester1.address, 1)\n        ).to.be.false;\n      });\n    });\n\n    describe('When the root is available for the attester', () => {\n      before(async () => {\n        await availableRootsRegistry\n          .connect(availableRootsRegistryOwner)\n          .registerRootForAttester(attester1.address, 1);\n      });\n\n      it('Should return true', async () => {\n        expect(\n          await availableRootsRegistry\n            .connect(availableRootsRegistryOwner)\n            .isRootAvailableForAttester(attester1.address, 1)\n        ).to.be.true;\n      });\n\n      it(\"Should return false when it's the wrong attester\", async () => {\n        expect(\n          await availableRootsRegistry\n            .connect(availableRootsRegistryOwner)\n            .isRootAvailableForAttester(attester2.address, 1)\n        ).to.be.false;\n      });\n    });\n\n    describe('When the root is available globally', () => {\n      before(async () => {\n        await availableRootsRegistry\n          .connect(availableRootsRegistryOwner)\n          .unregisterRootForAttester(attester1.address, 1);\n\n        await availableRootsRegistry.connect(availableRootsRegistryOwner).registerRootForAll(1);\n      });\n\n      it('Should return true', async () => {\n        expect(await availableRootsRegistry.isRootAvailableForAttester(attester1.address, 1)).to.be\n          .true;\n\n        expect(await availableRootsRegistry.isRootAvailableForAttester(attester2.address, 1)).to.be\n          .true;\n      });\n    });\n  });\n\n  /*************************************************************************************/\n  /****************************** IS ROOT AVAILABLE FOR ME *****************************/\n  /*************************************************************************************/\n  describe('Is root available for me', () => {\n    before(async () => {\n      await availableRootsRegistry\n        .connect(availableRootsRegistryOwner)\n        .unregisterRootForAttester(attester1.address, 1);\n      await availableRootsRegistry.connect(availableRootsRegistryOwner).unregisterRootForAll(1);\n    });\n\n    describe('When the root is not available for the attester or globally', async () => {\n      it('Should return false', async () => {\n        expect(await availableRootsRegistry.isRootAvailableForMe(1)).to.be.false;\n      });\n    });\n\n    describe('When the root is available for the attester', () => {\n      before(async () => {\n        await availableRootsRegistry\n          .connect(availableRootsRegistryOwner)\n          .registerRootForAttester(attester1.address, 1);\n      });\n\n      it('Should return true', async () => {\n        expect(await availableRootsRegistry.connect(attester1).isRootAvailableForMe(1)).to.be.true;\n      });\n\n      it(\"Should return false when it's the wrong attester\", async () => {\n        expect(await availableRootsRegistry.connect(attester2).isRootAvailableForMe(1)).to.be.false;\n      });\n    });\n\n    describe('When the root is available globally', () => {\n      before(async () => {\n        await availableRootsRegistry\n          .connect(availableRootsRegistryOwner)\n          .unregisterRootForAttester(attester1.address, 1);\n\n        await availableRootsRegistry.connect(availableRootsRegistryOwner).registerRootForAll(1);\n      });\n\n      it('Should return true', async () => {\n        expect(await availableRootsRegistry.connect(attester1).isRootAvailableForMe(1)).to.be.true;\n\n        expect(await availableRootsRegistry.connect(attester2).isRootAvailableForMe(1)).to.be.true;\n      });\n    });\n  });\n\n  /*************************************************************************************/\n  /******************************** TRANSFER OWNERSHIP *********************************/\n  /*************************************************************************************/\n  describe('Transfer ownership', () => {\n    it('Should revert when the sender is not the current owner of the contract', async () => {\n      await expect(\n        availableRootsRegistry.connect(attacker).transferOwnership(attacker.address)\n      ).to.be.revertedWith('Ownable: caller is not the owner');\n    });\n\n    it('Should revert when the newOwner is a zero address', async () => {\n      await expect(\n        availableRootsRegistry\n          .connect(availableRootsRegistryOwner)\n          .transferOwnership(ethers.constants.AddressZero)\n      ).to.be.revertedWith('Ownable: new owner is the zero address');\n    });\n\n    it('Should transfer the ownership', async () => {\n      await availableRootsRegistry\n        .connect(availableRootsRegistryOwner)\n        .transferOwnership(secondDeployer.address);\n\n      expect(await await availableRootsRegistry.owner()).to.equal(secondDeployer.address);\n    });\n  });\n\n  /*************************************************************************************/\n  /******************************** RENOUNCE OWNERSHIP *********************************/\n  /*************************************************************************************/\n  describe('Renounce ownership', () => {\n    before(async () => {\n      await availableRootsRegistry.connect(secondDeployer).transferOwnership(deployer.address);\n    });\n\n    it('Should revert when the sender is not the current owner of the contract', async () => {\n      await expect(availableRootsRegistry.connect(attacker).renounceOwnership()).to.be.revertedWith(\n        'Ownable: caller is not the owner'\n      );\n    });\n\n    it('Should renounce the ownership', async () => {\n      await expect(\n        availableRootsRegistry\n          .connect(await impersonateAddress(hre, await availableRootsRegistry.owner()))\n          .renounceOwnership()\n      )\n        .to.emit(availableRootsRegistry, 'OwnershipTransferred')\n        .withArgs(deployer.address, ethers.constants.AddressZero);\n    });\n  });\n});\n"
  },
  {
    "path": "tasks/deploy-tasks/full/5-upgrade-proxies-with-reinitializer/5-upgrade-proxies-with-reinitializer-commitment-mapper-registry.test-fork.ts",
    "content": "import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers';\nimport { CommitmentMapperTester } from '@sismo-core/commitment-mapper-tester-js';\nimport { EddsaPublicKey } from '@sismo-core/hydra-s1';\nimport { expect } from 'chai';\nimport { BigNumber } from 'ethers';\nimport hre, { ethers } from 'hardhat';\nimport { Deployed5 } from 'tasks/deploy-tasks/full/5-upgrade-proxies-with-reinitializer.task';\nimport { evmSnapshot, impersonateAddress } from '../../../../test/utils';\nimport { deploymentsConfig } from '../../../../tasks/deploy-tasks/deployments-config';\nimport { DeployedCommitmentMapper } from '../../../../tasks/deploy-tasks/unit/periphery/deploy-commitment-mapper-registry.task';\nimport {\n  CommitmentMapperRegistry,\n  CommitmentMapperRegistry__factory,\n  TransparentUpgradeableProxy__factory,\n} from '../../../../types';\nimport { getImplementation } from '../../../../utils';\n\ndescribe('Test CommitmentMapperRegistry contract', () => {\n  let deployer: SignerWithAddress;\n  let secondDeployer: SignerWithAddress;\n  let attacker: SignerWithAddress;\n  let randomCommitmentMapper: SignerWithAddress;\n  let commitmentMapperRegistryOwner: SignerWithAddress;\n\n  let commitmentMapperPubKey: EddsaPublicKey;\n\n  let commitmentMapperRegistry: CommitmentMapperRegistry;\n  let secondCommitmentMapperRegistry: CommitmentMapperRegistry;\n\n  let snapshotId: string;\n\n  const config = deploymentsConfig[process.env.FORK_NETWORK ?? hre.network.name];\n\n  before(async () => {\n    const signers = await hre.ethers.getSigners();\n    [deployer, secondDeployer, attacker, randomCommitmentMapper] = signers;\n\n    // 1 - Init the commitment mapper\n    const commitmentMapper = await CommitmentMapperTester.generate();\n    commitmentMapperPubKey = await commitmentMapper.getPubKey();\n  });\n\n  /*************************************************************************************/\n  /********************************** DEPLOYMENTS **************************************/\n  /*************************************************************************************/\n  describe('Deployments', () => {\n    it('Should deploy, setup and test the constructed values of the contract', async () => {\n      commitmentMapperRegistry = (await CommitmentMapperRegistry__factory.connect(\n        config.commitmentMapper.address,\n        await impersonateAddress(hre, config.commitmentMapper.owner)\n      )) as CommitmentMapperRegistry;\n    });\n  });\n\n  describe('Update Implementation', () => {\n    it('Should run the upgrade script', async () => {\n      await impersonateAddress(\n        hre,\n        config.deployOptions.proxyAdmin ?? config.synapsPythia1SimpleAttester.owner\n      );\n\n      ({ commitmentMapperRegistry } = await hre.run('5-upgrade-proxies-with-reinitializer', {\n        options: { manualConfirm: false, log: false },\n      })) as Deployed5;\n\n      commitmentMapperRegistryOwner = await impersonateAddress(\n        hre,\n        await commitmentMapperRegistry.owner(),\n        true\n      );\n\n      snapshotId = await evmSnapshot(hre);\n    });\n  });\n\n  describe('Configuration checks', () => {\n    it('should check the proxy address is unchanged', async () => {\n      expect(commitmentMapperRegistry.address).to.be.equal(config.commitmentMapper.address);\n    });\n\n    it(\"should check that the contract's owner is the deployer\", async () => {\n      expect(await commitmentMapperRegistry.owner()).to.equal(config.commitmentMapper.owner);\n    });\n\n    it('Should get the version correctly', async () => {\n      expect(await commitmentMapperRegistry.IMPLEMENTATION_VERSION()).to.be.eql(2);\n    });\n\n    it('should check that the commitment mapper address is registered on the correct address (0x0)', async () => {\n      // 1 - Checks that the commitment mapper address is registered on the address zero (default value)\n      expect(await commitmentMapperRegistry.getAddress()).to.be.equal(ethers.constants.AddressZero);\n    });\n\n    it('should check that the eddsa public key is correct', async () => {\n      expect(await commitmentMapperRegistry.getEdDSAPubKey()).to.be.eql([\n        BigNumber.from(config.commitmentMapper.EdDSAPubKeyX),\n        BigNumber.from(config.commitmentMapper.EdDSAPubKeyY),\n      ]);\n    });\n\n    it('Should revert when trying to call initialize again', async () => {\n      await expect(\n        commitmentMapperRegistry\n          .connect(deployer)\n          .initialize(deployer.address, commitmentMapperPubKey, randomCommitmentMapper.address)\n      ).to.be.revertedWith('Initializable: contract is already initialized');\n    });\n  });\n\n  /*************************************************************************************/\n  /********************** UPDATE COMMITMENT MAPPER EDDSA PUB KEY ***********************/\n  /*************************************************************************************/\n  describe('Update commitment mapper EdDSA pub key', () => {\n    it('Should revert when the sender is not the owner of the contract', async () => {\n      await expect(\n        commitmentMapperRegistry\n          .connect(attacker)\n          .updateCommitmentMapperEdDSAPubKey(commitmentMapperPubKey)\n      ).to.be.revertedWith('Ownable: caller is not the owner');\n    });\n\n    it('Should update the EdDSA pub key', async () => {\n      const updateCommitmentMapperEdDsaPubKeyTransaction = await commitmentMapperRegistry\n        .connect(commitmentMapperRegistryOwner)\n        .updateCommitmentMapperEdDSAPubKey(commitmentMapperPubKey);\n\n      // 1 - Checks that the transaction emitted the event\n      await expect(updateCommitmentMapperEdDsaPubKeyTransaction)\n        .to.emit(commitmentMapperRegistry, 'UpdatedCommitmentMapperEdDSAPubKey')\n        .withArgs(commitmentMapperPubKey);\n\n      // 2 - Checks that the eddsa public key matches the one provided\n      expect(await commitmentMapperRegistry.getEdDSAPubKey()).to.eql(commitmentMapperPubKey);\n    });\n  });\n\n  /*************************************************************************************/\n  /************************* UPDATE COMMITMENT MAPPER ADDRESS **************************/\n  /*************************************************************************************/\n  describe('Update commitment mapper address', () => {\n    it('Should revert when the sender is not the owner of the contract', async () => {\n      await expect(\n        commitmentMapperRegistry\n          .connect(attacker)\n          .updateCommitmentMapperAddress(randomCommitmentMapper.address)\n      ).to.be.revertedWith('Ownable: caller is not the owner');\n    });\n\n    it('Should update the mapper address', async () => {\n      const updateCommitmentMapperAddressTransaction = await commitmentMapperRegistry\n        .connect(commitmentMapperRegistryOwner)\n        .updateCommitmentMapperAddress(randomCommitmentMapper.address);\n\n      // 1 - Checks that the transaction emitted the event\n      await expect(updateCommitmentMapperAddressTransaction)\n        .to.emit(commitmentMapperRegistry, 'UpdatedCommitmentMapperAddress')\n        .withArgs(randomCommitmentMapper.address);\n\n      // 2 - Checks that the commitment mapper address matches the random commitment mapper address\n      expect(await commitmentMapperRegistry.getAddress()).to.be.equal(\n        randomCommitmentMapper.address\n      );\n    });\n  });\n\n  /*************************************************************************************/\n  /******************************** TRANSFER OWNERSHIP *********************************/\n  /*************************************************************************************/\n  describe('Transfer ownership', () => {\n    it('Should revert when the sender is not the current owner of the contract', async () => {\n      await expect(\n        commitmentMapperRegistry.connect(attacker).transferOwnership(attacker.address)\n      ).to.be.revertedWith('Ownable: caller is not the owner');\n    });\n\n    it('Should revert when the newOwner is a zero address', async () => {\n      await expect(\n        commitmentMapperRegistry\n          .connect(commitmentMapperRegistryOwner)\n          .transferOwnership(ethers.constants.AddressZero)\n      ).to.be.revertedWith('Ownable: new owner is the zero address');\n    });\n\n    it('Should transfer the ownership', async () => {\n      await expect(\n        commitmentMapperRegistry\n          .connect(commitmentMapperRegistryOwner)\n          .transferOwnership(secondDeployer.address)\n      )\n        .to.emit(commitmentMapperRegistry, 'OwnershipTransferred')\n        .withArgs(config.commitmentMapper.owner, secondDeployer.address);\n    });\n  });\n\n  /*************************************************************************************/\n  /******************************** RENOUNCE OWNERSHIP *********************************/\n  /*************************************************************************************/\n  describe('Renounce ownership', () => {\n    before(async () => {\n      await commitmentMapperRegistry.connect(secondDeployer).transferOwnership(deployer.address);\n    });\n\n    it('Should revert when the sender is not the current owner of the contract', async () => {\n      await expect(\n        commitmentMapperRegistry.connect(attacker).renounceOwnership()\n      ).to.be.revertedWith('Ownable: caller is not the owner');\n    });\n\n    it('Should renounce the ownership', async () => {\n      await expect(commitmentMapperRegistry.renounceOwnership())\n        .to.emit(commitmentMapperRegistry, 'OwnershipTransferred')\n        .withArgs(deployer.address, ethers.constants.AddressZero);\n    });\n  });\n});\n"
  },
  {
    "path": "tasks/deploy-tasks/full/5-upgrade-proxies-with-reinitializer/5-upgrade-proxies-with-reinitializer-pythia1.test-fork.ts",
    "content": "import { expect } from 'chai';\nimport hre from 'hardhat';\nimport {\n  AttestationsRegistry,\n  AttestationsRegistry__factory,\n  AvailableRootsRegistry,\n  AvailableRootsRegistry__factory,\n  Badges,\n  Badges__factory,\n  HydraS1SimpleAttester,\n  HydraS1SimpleAttester__factory,\n  Pythia1SimpleAttester,\n  Pythia1SimpleAttester__factory,\n} from '../../../../types';\nimport { RequestStruct } from 'types/Attester';\n\nimport { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers';\nimport { BigNumber, Signer } from 'ethers';\nimport { deploymentsConfig } from '../../deployments-config';\nimport {\n  DeployedPythia1SimpleAttester,\n  DeployPythia1SimpleAttesterArgs,\n} from '../../unit/attesters/pythia-1/deploy-pythia-1-simple-attester.task';\nimport { Pythia1Prover, SnarkProof, EddsaSignature, buildPoseidon } from '@sismo-core/pythia-1';\nimport {\n  evmSnapshot,\n  generateExternalNullifier,\n  getEventArgs,\n  impersonateAddress,\n} from '../../../../test/utils';\nimport {\n  CommitmentSignerTester,\n  Pythia1Group,\n  generatePythia1Group,\n  encodePythia1GroupProperties,\n} from '../../../../test/utils/pythia-1';\nimport { Deployed5 } from 'tasks/deploy-tasks/full/5-upgrade-proxies-with-reinitializer.task';\n\nconst config = deploymentsConfig[process.env.FORK_NETWORK ?? hre.network.name];\n\nconst collectionIdFirst = BigNumber.from(config.synapsPythia1SimpleAttester.collectionIdFirst);\nconst collectionIdLast = BigNumber.from(config.synapsPythia1SimpleAttester.collectionIdLast);\n\ndescribe('FORK Test Pythia', () => {\n  let chainId: number;\n  let poseidon;\n\n  // contracts\n  let pythia1SimpleAttester: Pythia1SimpleAttester;\n  let attestationsRegistry: AttestationsRegistry;\n  let badges: Badges;\n\n  // pythia s1 prover\n  let prover: Pythia1Prover;\n  let commitmentSigner: CommitmentSignerTester;\n\n  // Test Signers\n  let deployer: SignerWithAddress;\n  let destination1: SignerWithAddress;\n  let randomSigner: SignerWithAddress;\n\n  // Valid request and proof\n  let request: RequestStruct;\n  let proof: SnarkProof;\n  let externalNullifier: BigNumber;\n\n  // Data source test\n  let secret: BigNumber;\n  let commitment: BigNumber;\n  let commitmentReceipt: EddsaSignature;\n  let source1Value: BigNumber;\n  let group1: Pythia1Group;\n\n  let snapshotId: string;\n\n  before(async () => {\n    const signers = await hre.ethers.getSigners();\n    [deployer, destination1, , , , , , , randomSigner] = signers;\n    chainId = parseInt(await hre.getChainId());\n\n    poseidon = await buildPoseidon();\n\n    // 1 - Init commitment signer\n    commitmentSigner = new CommitmentSignerTester();\n\n    // 2 - Generate the group with its properties\n    group1 = generatePythia1Group({\n      internalCollectionId: 0,\n      isScore: false,\n    });\n\n    // 3 - Get the commitmentReceipt by passing through the commitment signer\n    secret = BigNumber.from('0x123');\n    commitment = poseidon([secret]);\n    source1Value = BigNumber.from('0x9');\n    commitmentReceipt = await commitmentSigner.getCommitmentReceipt(\n      commitment,\n      source1Value,\n      group1.id\n    );\n\n    // 4 - Init Proving scheme\n    prover = new Pythia1Prover();\n  });\n\n  /*************************************************************************************/\n  /********************************** DEPLOYMENTS **************************************/\n  /*************************************************************************************/\n\n  describe('Setup fork', () => {\n    it('Should retrieve core contracts', async () => {\n      // Deploy Sismo Protocol Core contracts\n      attestationsRegistry = AttestationsRegistry__factory.connect(\n        config.attestationsRegistry.address,\n        await impersonateAddress(hre, config.attestationsRegistry.owner)\n      ) as AttestationsRegistry;\n\n      badges = Badges__factory.connect(\n        config.badges.address,\n        await impersonateAddress(hre, config.badges.owner)\n      ) as Badges;\n\n      pythia1SimpleAttester = Pythia1SimpleAttester__factory.connect(\n        config.synapsPythia1SimpleAttester.address,\n        await impersonateAddress(hre, config.synapsPythia1SimpleAttester.owner, true)\n      ) as Pythia1SimpleAttester;\n\n      const pythiaOwner = await impersonateAddress(hre, await pythia1SimpleAttester.owner(), true);\n      const commitmentSignerPubKey = await commitmentSigner.getPublicKey();\n\n      await (\n        await pythia1SimpleAttester\n          .connect(pythiaOwner)\n          .updateCommitmentSignerPubKey(commitmentSignerPubKey, { gasLimit: 600000 })\n      ).wait();\n    });\n  });\n\n  /*************************************************************************************/\n  /***************************** GENERATE VALID ATTESTATION ****************************/\n  /*************************************************************************************/\n\n  describe('Generate valid attestation (before upgrade)', () => {\n    it('Should generate a proof with a pythia 1 prover and verify it onchain using the attester', async () => {\n      externalNullifier = await generateExternalNullifier(\n        pythia1SimpleAttester.address,\n        group1.properties.internalCollectionId\n      );\n\n      request = {\n        claims: [\n          {\n            groupId: group1.id,\n            claimedValue: source1Value,\n            extraData: encodePythia1GroupProperties(group1.properties),\n          },\n        ],\n        destination: BigNumber.from(destination1.address).toHexString(),\n      };\n\n      proof = (await prover.generateSnarkProof({\n        secret: secret,\n        value: source1Value,\n        commitmentReceipt: commitmentReceipt,\n        commitmentSignerPubKey: await commitmentSigner.getPublicKey(),\n        destinationIdentifier: destination1.address,\n        claimedValue: source1Value,\n        chainId: chainId,\n        groupId: group1.id,\n        ticketIdentifier: externalNullifier,\n        isStrict: !group1.properties.isScore,\n      })) as SnarkProof;\n\n      const tx = await pythia1SimpleAttester\n        .connect(destination1)\n        .generateAttestations(request, await proof.toBytes());\n      const { events } = await tx.wait();\n      const args = getEventArgs(events, 'AttestationGenerated');\n      expect(args.attestation.issuer).to.equal(pythia1SimpleAttester.address);\n      expect(args.attestation.owner).to.equal(destination1.address);\n      expect(args.attestation.collectionId).to.equal(\n        collectionIdFirst.add(group1.properties.internalCollectionId)\n      );\n      expect(args.attestation.value).to.equal(source1Value);\n      // expect(args.attestation.timestamp).to.equal(tx.timestamp);\n    }).timeout(60000);\n  });\n\n  describe('Update Implementation', () => {\n    it('Should run the upgrade script', async () => {\n      await impersonateAddress(\n        hre,\n        config.deployOptions.proxyAdmin ?? config.synapsPythia1SimpleAttester.owner\n      );\n\n      ({ pythia1SimpleAttester } = await hre.run('5-upgrade-proxies-with-reinitializer', {\n        options: { manualConfirm: false, log: false },\n      })) as Deployed5;\n\n      const pythiaOwner = await impersonateAddress(hre, await pythia1SimpleAttester.owner(), true);\n      const commitmentSignerPubKey = await commitmentSigner.getPublicKey();\n\n      await (\n        await pythia1SimpleAttester\n          .connect(pythiaOwner)\n          .updateCommitmentSignerPubKey(commitmentSignerPubKey, { gasLimit: 600000 })\n      ).wait();\n\n      snapshotId = await evmSnapshot(hre);\n    });\n  });\n\n  describe('Configuration checks', () => {\n    it('Should check the address of the proxy', async () => {\n      expect(pythia1SimpleAttester.address).to.be.eql(config.synapsPythia1SimpleAttester.address);\n    });\n\n    it('Should have setup the owner correctly', async () => {\n      expect(await pythia1SimpleAttester.owner()).to.be.eql(\n        config.synapsPythia1SimpleAttester.owner\n      );\n    });\n\n    it('Should get the owner correctly', async () => {\n      expect(await pythia1SimpleAttester.owner()).to.be.eql(\n        config.synapsPythia1SimpleAttester.owner\n      );\n    });\n\n    it('Should get the version correctly', async () => {\n      expect(await pythia1SimpleAttester.IMPLEMENTATION_VERSION()).to.be.eql(3);\n    });\n\n    it('Should revert when trying to call initialize again', async () => {\n      const commitmentSignerPubKey = await commitmentSigner.getPublicKey();\n      await expect(\n        pythia1SimpleAttester\n          .connect(await impersonateAddress(hre, config.synapsPythia1SimpleAttester.owner))\n          .initialize(commitmentSignerPubKey, randomSigner.address, { gasLimit: 600000 })\n      ).to.be.revertedWith('Initializable: contract is already initialized');\n    });\n  });\n\n  describe('Generate valid attestation (after upgrade)', () => {\n    it('Should generate a proof with a pythia 1 prover and verify it onchain using the attester', async () => {\n      externalNullifier = await generateExternalNullifier(\n        pythia1SimpleAttester.address,\n        group1.properties.internalCollectionId\n      );\n\n      secret = BigNumber.from('0x1234'); // we changed the secret for the commitment\n      commitment = poseidon([secret]);\n      source1Value = BigNumber.from('0x9');\n      commitmentReceipt = await commitmentSigner.getCommitmentReceipt(\n        commitment,\n        source1Value,\n        group1.id\n      );\n\n      request = {\n        claims: [\n          {\n            groupId: group1.id,\n            claimedValue: source1Value,\n            extraData: encodePythia1GroupProperties(group1.properties),\n          },\n        ],\n        destination: BigNumber.from(destination1.address).toHexString(),\n      };\n\n      proof = (await prover.generateSnarkProof({\n        secret: secret,\n        value: source1Value,\n        commitmentReceipt: commitmentReceipt,\n        commitmentSignerPubKey: await commitmentSigner.getPublicKey(),\n        destinationIdentifier: destination1.address,\n        claimedValue: source1Value,\n        chainId: chainId,\n        groupId: group1.id,\n        ticketIdentifier: externalNullifier,\n        isStrict: !group1.properties.isScore,\n      })) as SnarkProof;\n\n      const tx = await pythia1SimpleAttester\n        .connect(destination1)\n        .generateAttestations(request, await proof.toBytes());\n      const { events } = await tx.wait();\n      const args = getEventArgs(events, 'AttestationGenerated');\n      expect(args.attestation.issuer).to.equal(pythia1SimpleAttester.address);\n      expect(args.attestation.owner).to.equal(destination1.address);\n      expect(args.attestation.collectionId).to.equal(\n        collectionIdFirst.add(group1.properties.internalCollectionId)\n      );\n      expect(args.attestation.value).to.equal(source1Value);\n      // expect(args.attestation.timestamp).to.equal(tx.timestamp);\n    }).timeout(60000);\n  });\n});\n"
  },
  {
    "path": "tasks/deploy-tasks/full/5-upgrade-proxies-with-reinitializer.task.ts",
    "content": "import { task } from 'hardhat/config';\nimport { HardhatRuntimeEnvironment } from 'hardhat/types';\nimport { DeployOptions, getDeployer } from '../utils';\nimport {\n  AvailableRootsRegistry,\n  Badges,\n  CommitmentMapperRegistry,\n  Pythia1SimpleAttester,\n} from 'types';\nimport { deploymentsConfig } from '../deployments-config';\nimport { DeployedPythia1SimpleAttester } from 'tasks/deploy-tasks/unit/attesters/pythia-1/deploy-pythia-1-simple-attester.task';\nimport { DeployedAvailableRootsRegistry } from 'tasks/deploy-tasks/unit/periphery/deploy-available-roots-registry.task';\nimport { DeployedCommitmentMapper } from 'tasks/deploy-tasks/unit/periphery/deploy-commitment-mapper-registry.task';\n\nexport interface Deployed5 {\n  pythia1SimpleAttester: Pythia1SimpleAttester;\n  availableRootsRegistry: AvailableRootsRegistry;\n  commitmentMapperRegistry: CommitmentMapperRegistry;\n}\n\nasync function deploymentAction(\n  { options }: { options: DeployOptions },\n  hre: HardhatRuntimeEnvironment\n): Promise<Deployed5> {\n  const config = deploymentsConfig[process.env.FORK_NETWORK ?? hre.network.name];\n  options = { ...config.deployOptions, ...options };\n\n  if (options.manualConfirm || options.log) {\n    console.log('4-upgrade-attestations-registry-proxy: ', hre.network.name);\n  }\n\n  // The following proxies will be updated:\n  // - Pythia1SimpleAttester => reinitializer modifier added and version as constant\n  // - AvailableRootsRegistry => reinitializer modifier added and version as constant\n  // - CommitmentMapperRegistry => reinitializer modifier added and version as constant\n\n  // Upgrade pythia1SimpleAttester\n  const { pythia1SimpleAttester: newPythia1SimpleAttester } = (await hre.run(\n    'deploy-pythia-1-simple-attester',\n    {\n      collectionIdFirst: config.synapsPythia1SimpleAttester.collectionIdFirst,\n      collectionIdLast: config.synapsPythia1SimpleAttester.collectionIdLast,\n      attestationsRegistryAddress: config.attestationsRegistry.address,\n      commitmentSignerPubKeyX: config.synapsPythia1SimpleAttester.commitmentSignerPubKeyX,\n      commitmentSignerPubKeyY: config.synapsPythia1SimpleAttester.commitmentSignerPubKeyY,\n      owner: config.synapsPythia1SimpleAttester.owner,\n      options: {\n        ...options,\n        isImplementationUpgrade: true, // implementation version has been bumped from v2 to v3\n        proxyAddress: config.synapsPythia1SimpleAttester.address,\n      },\n    }\n  )) as DeployedPythia1SimpleAttester;\n\n  // Upgrade AvailableRootsRegistry\n  const { availableRootsRegistry: newAvailableRootsRegistry } = (await hre.run(\n    'deploy-available-roots-registry',\n    {\n      owner: config.availableRootsRegistry.owner,\n      options: {\n        ...options,\n        isImplementationUpgrade: true, // implementation version has been bumped from v1 to v2\n        proxyAddress: config.availableRootsRegistry.address,\n      },\n    }\n  )) as DeployedAvailableRootsRegistry;\n\n  // Upgrade CommitmentMapperRegistry\n  const { commitmentMapperRegistry: newCommitmentMapperRegistry } = (await hre.run(\n    'deploy-commitment-mapper-registry',\n    {\n      commitmentMapperPubKeyX: config.commitmentMapper.EdDSAPubKeyX,\n      commitmentMapperPubKeyY: config.commitmentMapper.EdDSAPubKeyY,\n      owner: config.commitmentMapper.owner,\n      options: {\n        ...options,\n        isImplementationUpgrade: true, // implementation version has been bumped from v1 to v2\n        proxyAddress: config.commitmentMapper.address,\n      },\n    }\n  )) as DeployedCommitmentMapper;\n\n  return {\n    pythia1SimpleAttester: newPythia1SimpleAttester,\n    availableRootsRegistry: newAvailableRootsRegistry,\n    commitmentMapperRegistry: newCommitmentMapperRegistry,\n  };\n}\n\ntask('5-upgrade-proxies-with-reinitializer').setAction(deploymentAction);\n"
  },
  {
    "path": "tasks/deploy-tasks/full/6-7-8-fork-test/6-7-8.test-fork.ts",
    "content": "import { expect } from 'chai';\nimport hre from 'hardhat';\nimport {\n  AddressesProvider,\n  AttestationsRegistry,\n  AttestationsRegistry__factory,\n  AvailableRootsRegistry,\n  AvailableRootsRegistry__factory,\n  Badges,\n  Badges__factory,\n  CommitmentMapperRegistry,\n  CommitmentMapperRegistry__factory,\n  HydraS1AccountboundAttester,\n  HydraS1AccountboundAttester__factory,\n  Pythia1SimpleAttester,\n  Pythia1SimpleAttester__factory,\n  ZKBadgeboundERC721,\n} from '../../../../types';\nimport { RequestStruct } from 'types/Attester';\n\nimport { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers';\nimport { BigNumber } from 'ethers';\nimport {\n  deploymentsConfig,\n  SISMO_ADDRESSES_PROVIDER_CONTRACT_ADDRESS,\n} from '../../deployments-config';\nimport {\n  Pythia1Prover,\n  SnarkProof,\n  EddsaSignature,\n  buildPoseidon,\n  EddsaPublicKey,\n} from '@sismo-core/pythia-1';\nimport {\n  evmSnapshot,\n  generateExternalNullifier,\n  generateProvingData,\n  getEventArgs,\n  HydraS1SimpleGroup,\n  HydraS1ZKPS,\n  impersonateAddress,\n} from '../../../../test/utils';\nimport {\n  CommitmentSignerTester,\n  Pythia1Group,\n  generatePythia1Group,\n  encodePythia1GroupProperties,\n} from '../../../../test/utils/pythia-1';\nimport { Deployed7 } from 'tasks/deploy-tasks/full/7-upgrade-hydra-s1-accountbound-and-pythia-1-proxies.task';\nimport { Deployed6 } from 'tasks/deploy-tasks/full/6-deploy-sismo-addresses-provider.task';\nimport { HydraS1Account, KVMerkleTree } from '@sismo-core/hydra-s1';\nimport { registerRootForAttester, TestingHelper } from '../../../../test/utils/test-helpers';\n\nconst config = deploymentsConfig[process.env.FORK_NETWORK ?? hre.network.name];\n\nconst collectionIdFirst = BigNumber.from(config.synapsPythia1SimpleAttester.collectionIdFirst);\nconst collectionIdLast = BigNumber.from(config.synapsPythia1SimpleAttester.collectionIdLast);\n\n// Launch with command\n// FORK=true FORK_NETWORK=goerliStaging npx hardhat test ./tasks/deploy-tasks/full/6-7-8-fork-test/6-7-8.test-fork.ts\n\ndescribe('FORK Test AddressesProvider and ZKBadgeboundERC7221', () => {\n  let chainId: number;\n  let poseidon;\n\n  // contracts\n  let pythia1SimpleAttester: Pythia1SimpleAttester;\n  let hydraS1AccountboundAttester: HydraS1AccountboundAttester;\n  let sismoAddressesProvider: AddressesProvider;\n  let availableRootsRegistry: AvailableRootsRegistry;\n  let commitmentMapperRegistry: CommitmentMapperRegistry;\n  let zkBadgeboundERC721: ZKBadgeboundERC721;\n  let attestationsRegistry: AttestationsRegistry;\n  let badges: Badges;\n\n  let provingScheme: HydraS1ZKPS;\n  let accountsTreesWithData: { tree: KVMerkleTree; group: HydraS1SimpleGroup }[];\n  let registryTree: KVMerkleTree;\n  let groups: HydraS1SimpleGroup[];\n  let commitmentMapperPubKey: EddsaPublicKey;\n\n  let accountsSigners: SignerWithAddress[];\n  let account1Signer: SignerWithAddress;\n  let account2Signer: SignerWithAddress;\n\n  let accounts: HydraS1Account[];\n  let account1: HydraS1Account;\n  let account2: HydraS1Account;\n\n  // pythia s1 prover\n  let prover: Pythia1Prover;\n  let commitmentSigner: CommitmentSignerTester;\n\n  // Test Signers\n  let deployer: SignerWithAddress;\n  let destination1: SignerWithAddress;\n  let randomSigner: SignerWithAddress;\n\n  // Valid request and proof\n  let request: RequestStruct;\n  let proof: SnarkProof;\n  let externalNullifier: BigNumber;\n\n  // Data source test\n  let secret: BigNumber;\n  let commitment: BigNumber;\n  let commitmentReceipt: EddsaSignature;\n  let source1Value: BigNumber;\n  let group1: Pythia1Group;\n\n  let snapshotId: string;\n\n  let sismo: TestingHelper;\n\n  before(async () => {\n    const signers = await hre.ethers.getSigners();\n    [deployer, destination1, , , , , , , randomSigner] = signers;\n    chainId = parseInt(await hre.getChainId());\n\n    poseidon = await buildPoseidon();\n\n    // 1 - Init commitment signer\n    commitmentSigner = new CommitmentSignerTester();\n\n    // 2 - Generate the group with its properties\n    group1 = generatePythia1Group({\n      internalCollectionId: 0,\n      isScore: false,\n    });\n\n    // 3 - Get the commitmentReceipt by passing through the commitment signer\n    secret = BigNumber.from('0x123');\n    commitment = poseidon([secret]);\n    source1Value = BigNumber.from('0x9');\n    commitmentReceipt = await commitmentSigner.getCommitmentReceipt(\n      commitment,\n      source1Value,\n      group1.id\n    );\n\n    // 4 - Init Proving scheme\n    prover = new Pythia1Prover();\n\n    /////////////////////////////////////////////\n    // HYDRAS1 PROVING DATA SETUP\n    /////////////////////////////////////////////\n\n    // create two groups in the merkle tree with respectively all values of 1 and 2\n    ({ accountsTreesWithData, registryTree, groups, accounts, commitmentMapperPubKey } =\n      await generateProvingData({\n        nbOfGroups: 2,\n      }));\n\n    accountsSigners = signers;\n\n    deployer = accountsSigners[0];\n    account1Signer = accountsSigners[1];\n    account2Signer = accountsSigners[2];\n\n    account1 = accounts[1];\n    account2 = accounts[2];\n  });\n\n  /*************************************************************************************/\n  /********************************** DEPLOYMENTS **************************************/\n  /*************************************************************************************/\n\n  describe('Setup fork', () => {\n    it('Should retrieve core contracts', async () => {\n      // Deploy Sismo Protocol Core contracts\n      attestationsRegistry = AttestationsRegistry__factory.connect(\n        config.attestationsRegistry.address,\n        await impersonateAddress(hre, config.attestationsRegistry.owner)\n      ) as AttestationsRegistry;\n\n      availableRootsRegistry = AvailableRootsRegistry__factory.connect(\n        config.availableRootsRegistry.address,\n        await impersonateAddress(hre, config.availableRootsRegistry.owner)\n      ) as AvailableRootsRegistry;\n\n      commitmentMapperRegistry = CommitmentMapperRegistry__factory.connect(\n        config.commitmentMapper.address,\n        await impersonateAddress(hre, config.commitmentMapper.owner)\n      ) as CommitmentMapperRegistry;\n\n      badges = Badges__factory.connect(\n        config.badges.address,\n        await impersonateAddress(hre, config.badges.owner)\n      ) as Badges;\n\n      hydraS1AccountboundAttester = HydraS1AccountboundAttester__factory.connect(\n        config.hydraS1AccountboundAttester.address,\n        await impersonateAddress(hre, config.hydraS1AccountboundAttester.owner)\n      ) as HydraS1AccountboundAttester;\n\n      pythia1SimpleAttester = Pythia1SimpleAttester__factory.connect(\n        config.synapsPythia1SimpleAttester.address,\n        await impersonateAddress(hre, config.synapsPythia1SimpleAttester.owner, true)\n      ) as Pythia1SimpleAttester;\n\n      await (\n        await commitmentMapperRegistry.updateCommitmentMapperEdDSAPubKey(commitmentMapperPubKey, {\n          gasLimit: 600000,\n        })\n      ).wait();\n\n      const pythiaOwner = await impersonateAddress(hre, await pythia1SimpleAttester.owner(), true);\n      const commitmentSignerPubKey = await commitmentSigner.getPublicKey();\n\n      await (\n        await pythia1SimpleAttester\n          .connect(pythiaOwner)\n          .updateCommitmentSignerPubKey(commitmentSignerPubKey, { gasLimit: 600000 })\n      ).wait();\n\n      //setup Proving Scheme\n      provingScheme = new HydraS1ZKPS({\n        accountsTreesWithData,\n        registryTree,\n        groups,\n        defaultAttesterAddress: hydraS1AccountboundAttester.address,\n        commitmentMapperPubKey,\n        chainId,\n      });\n\n      sismo = new TestingHelper();\n    });\n  });\n\n  describe('Update Implementation and deploy contracts', () => {\n    it('should deploy the contracts', async () => {\n      // deploy addresses provider\n      ({ sismoAddressesProvider } = await hre.run('6-deploy-sismo-addresses-provider', {\n        options: {\n          manualConfirm: false,\n          log: false,\n        },\n      })) as Deployed6;\n\n      // deploy zkBadgeboundERC721\n      ({ zkBadgeboundERC721 } = await hre.run('8-deploy-zk-badgebound-erc721', {\n        options: {\n          manualConfirm: false,\n          log: false,\n        },\n      }));\n    });\n\n    it('Should run the upgrade script', async () => {\n      await impersonateAddress(\n        hre,\n        config.deployOptions.proxyAdmin ?? config.synapsPythia1SimpleAttester.owner\n      );\n\n      ({ pythia1SimpleAttester, hydraS1AccountboundAttester } = await hre.run(\n        '7-upgrade-hydra-s1-accountbound-and-pythia-1-proxies',\n        {\n          options: { manualConfirm: false, log: false },\n        }\n      )) as Deployed7;\n\n      const pythiaOwner = await impersonateAddress(hre, await pythia1SimpleAttester.owner(), true);\n      const commitmentSignerPubKey = await commitmentSigner.getPublicKey();\n\n      await (\n        await pythia1SimpleAttester\n          .connect(pythiaOwner)\n          .updateCommitmentSignerPubKey(commitmentSignerPubKey, { gasLimit: 600000 })\n      ).wait();\n\n      snapshotId = await evmSnapshot(hre);\n    });\n  });\n\n  describe('Configuration checks', () => {\n    it('Should check the address of contracts and proxies', async () => {\n      expect(sismoAddressesProvider.address).to.be.eql(SISMO_ADDRESSES_PROVIDER_CONTRACT_ADDRESS);\n      expect(pythia1SimpleAttester.address).to.be.eql(config.synapsPythia1SimpleAttester.address);\n      expect(hydraS1AccountboundAttester.address).to.be.eql(\n        config.hydraS1AccountboundAttester.address\n      );\n    });\n\n    it('Should have setup the owner correctly', async () => {\n      expect(await pythia1SimpleAttester.owner()).to.be.eql(\n        config.synapsPythia1SimpleAttester.owner\n      );\n      expect(await hydraS1AccountboundAttester.owner()).to.be.eql(\n        config.hydraS1AccountboundAttester.owner\n      );\n    });\n\n    it('Should get the owner correctly', async () => {\n      expect(await pythia1SimpleAttester.owner()).to.be.eql(\n        config.synapsPythia1SimpleAttester.owner\n      );\n      expect(await pythia1SimpleAttester.owner()).to.be.eql(\n        config.synapsPythia1SimpleAttester.owner\n      );\n    });\n\n    it('Should get the version correctly', async () => {\n      expect(await pythia1SimpleAttester.IMPLEMENTATION_VERSION()).to.be.eql(4);\n      expect(await hydraS1AccountboundAttester.IMPLEMENTATION_VERSION()).to.be.eql(5);\n    });\n\n    it('Should revert when trying to call initialize again', async () => {\n      const commitmentSignerPubKey = await commitmentSigner.getPublicKey();\n      await expect(\n        pythia1SimpleAttester\n          .connect(await impersonateAddress(hre, config.synapsPythia1SimpleAttester.owner))\n          .initialize(commitmentSignerPubKey, randomSigner.address, { gasLimit: 600000 })\n      ).to.be.revertedWith('Initializable: contract is already initialized');\n      await expect(\n        hydraS1AccountboundAttester\n          .connect(await impersonateAddress(hre, config.hydraS1AccountboundAttester.owner))\n          .initialize(randomSigner.address, { gasLimit: 600000 })\n      ).to.be.revertedWith('Initializable: contract is already initialized');\n    });\n  });\n\n  describe('Mint a NFT and a badge (after upgrade)', () => {\n    it('Should mint a NFT', async () => {\n      await registerRootForAttester(\n        availableRootsRegistry,\n        hydraS1AccountboundAttester,\n        registryTree\n      );\n\n      const { request, proofData, inputs } = await provingScheme.generateProof({\n        sources: [account1],\n        destination: account2,\n      });\n\n      const generateAttestationsTransaction =\n        await hydraS1AccountboundAttester.generateAttestations(request, proofData);\n\n      await sismo.checkAttestationIsWellRegistered({\n        request,\n        nullifier: BigNumber.from(inputs.publicInputs.nullifier),\n        accountAddress: account2Signer.address,\n        tx: generateAttestationsTransaction,\n      });\n\n      await sismo.checkAccountHoldsBadge(account2Signer.address, BigNumber.from(10000002), true);\n\n      expect(await zkBadgeboundERC721.balanceOf(account2Signer.address)).to.be.eql(\n        BigNumber.from(0)\n      );\n\n      await zkBadgeboundERC721.connect(account2Signer).claim();\n\n      expect(await zkBadgeboundERC721.balanceOf(account2Signer.address)).to.be.eql(\n        BigNumber.from(1)\n      );\n    });\n\n    it('Should revert because NFT has been already minted', async () => {\n      await sismo.checkAccountHoldsBadge(account2Signer.address, BigNumber.from(10000002), true);\n\n      expect(await zkBadgeboundERC721.balanceOf(account2Signer.address)).to.be.eql(\n        BigNumber.from(1)\n      );\n\n      await expect(zkBadgeboundERC721.connect(account2Signer).claim()).to.be.revertedWith(\n        'ERC721: token already minted'\n      );\n    });\n\n    it('Should mint with a proof', async () => {\n      const { request, proofData, inputs } = await provingScheme.generateProof({\n        sources: [account2],\n        destination: account1,\n      });\n\n      const mintNftTx = await zkBadgeboundERC721\n        .connect(account2Signer)\n        .claimWithSismo(request, proofData);\n      await sismo.checkAttestationIsWellRegistered({\n        request,\n        nullifier: BigNumber.from(inputs.publicInputs.nullifier),\n        accountAddress: account1Signer.address,\n        tx: mintNftTx,\n      });\n    });\n  });\n\n  describe('Generate valid attestation for Pythia1SimpleAttester (after upgrade)', () => {\n    it('Should generate a proof with a pythia 1 prover and verify it onchain using the attester', async () => {\n      externalNullifier = await generateExternalNullifier(\n        pythia1SimpleAttester.address,\n        group1.properties.internalCollectionId\n      );\n\n      secret = BigNumber.from('0x1234'); // we changed the secret for the commitment\n      commitment = poseidon([secret]);\n      source1Value = BigNumber.from('0x9');\n      commitmentReceipt = await commitmentSigner.getCommitmentReceipt(\n        commitment,\n        source1Value,\n        group1.id\n      );\n\n      request = {\n        claims: [\n          {\n            groupId: group1.id,\n            claimedValue: source1Value,\n            extraData: encodePythia1GroupProperties(group1.properties),\n          },\n        ],\n        destination: BigNumber.from(destination1.address).toHexString(),\n      };\n\n      proof = (await prover.generateSnarkProof({\n        secret: secret,\n        value: source1Value,\n        commitmentReceipt: commitmentReceipt,\n        commitmentSignerPubKey: await commitmentSigner.getPublicKey(),\n        destinationIdentifier: destination1.address,\n        claimedValue: source1Value,\n        chainId: chainId,\n        groupId: group1.id,\n        ticketIdentifier: externalNullifier,\n        isStrict: !group1.properties.isScore,\n      })) as SnarkProof;\n\n      const tx = await pythia1SimpleAttester\n        .connect(destination1)\n        .generateAttestations(request, await proof.toBytes());\n      const { events } = await tx.wait();\n      const args = getEventArgs(events, 'AttestationGenerated');\n      expect(args.attestation.issuer).to.equal(pythia1SimpleAttester.address);\n      expect(args.attestation.owner).to.equal(destination1.address);\n      expect(args.attestation.collectionId).to.equal(\n        collectionIdFirst.add(group1.properties.internalCollectionId)\n      );\n      expect(args.attestation.value).to.equal(source1Value);\n      // expect(args.attestation.timestamp).to.equal(tx.timestamp);\n    }).timeout(60000);\n  });\n});\n"
  },
  {
    "path": "tasks/deploy-tasks/full/6-deploy-sismo-addresses-provider.task.ts",
    "content": "import { task } from 'hardhat/config';\nimport { HardhatRuntimeEnvironment } from 'hardhat/types';\nimport { DeployOptions } from '../utils';\nimport { deploymentsConfig } from '../deployments-config';\nimport { DeployedSismoAddressesProvider } from 'tasks/deploy-tasks/unit/core/deploy-sismo-addresses-provider.task';\nimport { AddressesProvider } from 'types';\n\nexport interface Deployed6 {\n  sismoAddressesProvider: AddressesProvider;\n}\n\nasync function deploymentAction(\n  { options }: { options: DeployOptions },\n  hre: HardhatRuntimeEnvironment\n): Promise<Deployed6> {\n  const config = deploymentsConfig[process.env.FORK_NETWORK ?? hre.network.name];\n  options = { ...config.deployOptions, ...options };\n\n  if (options.manualConfirm || options.log) {\n    console.log('6-deploy-sismo-addresses-provider: ', hre.network.name);\n  }\n\n  // Deploy SismoAddressesProvider\n  const { sismoAddressesProvider } = (await hre.run('deploy-sismo-addresses-provider', {\n    owner: config.sismoAddressesProvider.owner,\n    badges: config.badges.address,\n    attestationsRegistry: config.attestationsRegistry.address,\n    front: config.front.address,\n    hydraS1AccountboundAttester: config.hydraS1AccountboundAttester.address,\n    commitmentMapperRegistry: config.commitmentMapper.address,\n    availableRootsRegistry: config.availableRootsRegistry.address,\n    hydraS1Verifier: config.hydraS1Verifier.address,\n    options: { ...options, proxyAdmin: config.deployOptions.proxyAdmin },\n  })) as DeployedSismoAddressesProvider;\n\n  return {\n    sismoAddressesProvider,\n  };\n}\n\ntask('6-deploy-sismo-addresses-provider').setAction(deploymentAction);\n"
  },
  {
    "path": "tasks/deploy-tasks/full/7-upgrade-hydra-s1-accountbound-and-pythia-1-proxies.task.ts",
    "content": "import { task } from 'hardhat/config';\nimport { HardhatRuntimeEnvironment } from 'hardhat/types';\nimport { DeployOptions } from '../utils';\nimport { deploymentsConfig } from '../deployments-config';\nimport { DeployedHydraS1AccountboundAttester } from 'tasks/deploy-tasks/unit/attesters/hydra-s1/deploy-hydra-s1-accountbound-attester.task';\nimport { DeployedPythia1SimpleAttester } from 'tasks/deploy-tasks/unit/attesters/pythia-1/deploy-pythia-1-simple-attester.task';\nimport { HydraS1AccountboundAttester, Pythia1SimpleAttester } from 'types';\n\nexport interface Deployed7 {\n  hydraS1AccountboundAttester: HydraS1AccountboundAttester;\n  pythia1SimpleAttester: Pythia1SimpleAttester;\n}\n\nasync function deploymentAction(\n  { options }: { options: DeployOptions },\n  hre: HardhatRuntimeEnvironment\n): Promise<Deployed7> {\n  const config = deploymentsConfig[process.env.FORK_NETWORK ?? hre.network.name];\n  options = { ...config.deployOptions, ...options };\n\n  if (options.manualConfirm || options.log) {\n    console.log('7-upgrade-hydra-s1-accountbound-and-pythia-1-proxies: ', hre.network.name);\n  }\n\n  // Upgrade HydraS1AccountboundAttester\n  const { hydraS1AccountboundAttester: newHydraS1AccountboundAttester } = (await hre.run(\n    'deploy-hydra-s1-accountbound-attester',\n    {\n      // the collectionIds referenced are the ones used by the previous HydraS1SimpleAttester\n      collectionIdFirst: config.hydraS1AccountboundAttester.collectionIdFirst,\n      collectionIdLast: config.hydraS1AccountboundAttester.collectionIdLast,\n      commitmentMapperRegistryAddress: config.commitmentMapper.address,\n      availableRootsRegistryAddress: config.availableRootsRegistry.address,\n      attestationsRegistryAddress: config.attestationsRegistry.address,\n      hydraS1VerifierAddress: config.hydraS1Verifier.address,\n      owner: config.hydraS1AccountboundAttester.owner,\n      options: {\n        ...options,\n        isImplementationUpgrade: true,\n        proxyAddress: config.hydraS1AccountboundAttester.address,\n      },\n    }\n  )) as DeployedHydraS1AccountboundAttester;\n\n  // Upgrade Pythia1SimpleAttester\n  const { pythia1SimpleAttester: newPythia1SimpleAttester } = (await hre.run(\n    'deploy-pythia-1-simple-attester',\n    {\n      collectionIdFirst: config.synapsPythia1SimpleAttester.collectionIdFirst,\n      collectionIdLast: config.synapsPythia1SimpleAttester.collectionIdLast,\n      attestationsRegistryAddress: config.attestationsRegistry.address,\n      commitmentSignerPubKeyX: config.synapsPythia1SimpleAttester.commitmentSignerPubKeyX,\n      commitmentSignerPubKeyY: config.synapsPythia1SimpleAttester.commitmentSignerPubKeyY,\n      pythia1VerifierAddress: config.pythia1Verifier.address,\n      owner: config.synapsPythia1SimpleAttester.owner,\n      options: {\n        ...options,\n        isImplementationUpgrade: true,\n        proxyAddress: config.synapsPythia1SimpleAttester.address,\n      },\n    }\n  )) as DeployedPythia1SimpleAttester;\n\n  return {\n    hydraS1AccountboundAttester: newHydraS1AccountboundAttester,\n    pythia1SimpleAttester: newPythia1SimpleAttester,\n  };\n}\n\ntask('7-upgrade-hydra-s1-accountbound-and-pythia-1-proxies').setAction(deploymentAction);\n"
  },
  {
    "path": "tasks/deploy-tasks/full/9-fork-test/9-upgrade-addresses-provider-on-testnets.test-fork.ts",
    "content": "import { AvailableRootsRegistry } from '../../../../types/AvailableRootsRegistry';\nimport { CommitmentMapperRegistry } from '../../../../types/CommitmentMapperRegistry';\nimport { AddressesProvider } from '../../../../types/AddressesProvider';\nimport { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers';\nimport { expect } from 'chai';\nimport hre, { ethers } from 'hardhat';\nimport {\n  AttestationsRegistry,\n  AttestationsRegistry__factory,\n  AvailableRootsRegistry__factory,\n  Badges,\n  Badges__factory,\n  CommitmentMapperRegistry__factory,\n  Front,\n  Front__factory,\n  HydraS1AccountboundAttester,\n  HydraS1AccountboundAttester__factory,\n  HydraS1Verifier__factory,\n} from '../../../../types';\nimport { HydraS1Verifier } from '@sismo-core/hydra-s1';\nimport { toUtf8Bytes } from 'ethers/lib/utils';\nimport { deploymentsConfig } from '../../deployments-config';\nimport { evmSnapshot, impersonateAddress } from '../../../../test/utils';\nimport { Deployed9 } from 'tasks/deploy-tasks/full/9-upgrade-addresses-provider-on-testnets.task';\n\nconst config = deploymentsConfig[process.env.FORK_NETWORK ?? hre.network.name];\n\nconst SismoContractsAddress = '0x3340Ac0CaFB3ae34dDD53dba0d7344C1Cf3EFE05';\n\n// Launch with command\n// FORK=true FORK_NETWORK=goerliTestnet npx hardhat test ./tasks/deploy-tasks/full/9-fork-test/9-upgrade-addresses-provider-on-testnets.test-fork.ts\n\ndescribe('Test Sismo Addresses Provider', () => {\n  let deployer: SignerWithAddress;\n  let proxyAdminSigner: SignerWithAddress;\n  let randomSigner: SignerWithAddress;\n\n  let attestationsRegistry: AttestationsRegistry;\n  let badges: Badges;\n  let front: Front;\n  let hydraS1AccountboundAttester: HydraS1AccountboundAttester;\n  let commitmentMapperRegistry: CommitmentMapperRegistry;\n  let availableRootsRegistry: AvailableRootsRegistry;\n  let hydraS1Verifier: HydraS1Verifier;\n\n  let sismoAddressesProvider: AddressesProvider;\n  let newSismoAddressesProvider: AddressesProvider;\n\n  let evmSnapshotId: string;\n\n  before(async () => {\n    const signers = await hre.ethers.getSigners();\n    [deployer, , proxyAdminSigner, , , , randomSigner] = signers;\n  });\n\n  /*************************************************************************************/\n  /********************************** DEPLOYMENTS **************************************/\n  /*************************************************************************************/\n  describe('Setup fork', () => {\n    it('Should retrieve core contracts', async () => {\n      // Deploy Sismo Protocol Core contracts\n      attestationsRegistry = AttestationsRegistry__factory.connect(\n        config.attestationsRegistry.address,\n        await impersonateAddress(hre, config.attestationsRegistry.owner)\n      ) as AttestationsRegistry;\n\n      availableRootsRegistry = AvailableRootsRegistry__factory.connect(\n        config.availableRootsRegistry.address,\n        await impersonateAddress(hre, config.availableRootsRegistry.owner)\n      ) as AvailableRootsRegistry;\n\n      commitmentMapperRegistry = CommitmentMapperRegistry__factory.connect(\n        config.commitmentMapper.address,\n        await impersonateAddress(hre, config.commitmentMapper.owner)\n      ) as CommitmentMapperRegistry;\n\n      badges = Badges__factory.connect(\n        config.badges.address,\n        await impersonateAddress(hre, config.badges.owner)\n      ) as Badges;\n\n      hydraS1AccountboundAttester = HydraS1AccountboundAttester__factory.connect(\n        config.hydraS1AccountboundAttester.address,\n        await impersonateAddress(hre, config.hydraS1AccountboundAttester.owner)\n      ) as HydraS1AccountboundAttester;\n\n      hydraS1Verifier = HydraS1Verifier__factory.connect(\n        config.hydraS1Verifier.address,\n        await impersonateAddress(hre, config.hydraS1AccountboundAttester.owner)\n      ) as HydraS1Verifier;\n\n      front = Front__factory.connect(\n        config.front.address,\n        await impersonateAddress(hre, config.hydraS1AccountboundAttester.owner)\n      ) as Front;\n    });\n  });\n\n  describe('Update AddressesProvider implem', () => {\n    it('Should run the upgrade script', async () => {\n      let network: string;\n      if (process.env.FORK_NETWORK === 'goerliTestnet') {\n        network = 'goerliStaging';\n      } else if (process.env.FORK_NETWORK === 'mumbaiTestnet') {\n        network = 'mumbaiStaging';\n      } else {\n        throw new Error('Invalid network');\n      }\n\n      const proxyAdmin = await impersonateAddress(\n        hre,\n        deploymentsConfig[network].deployOptions.proxyAdmin ?? config.sismoAddressesProvider.owner\n      );\n\n      ({ sismoAddressesProvider } = await hre.run('9-upgrade-addresses-provider-on-testnets', {\n        options: { manualConfirm: false, log: false },\n      })) as Deployed9;\n\n      const owner = await impersonateAddress(\n        hre,\n        deploymentsConfig[network].sismoAddressesProvider.owner\n      );\n\n      await hre.run('set-batch', {\n        signer: owner,\n        contractAddressesAsString: `${config.badges.address},${config.attestationsRegistry.address},${config.front.address},${config.hydraS1AccountboundAttester.address},${config.availableRootsRegistry.address},${config.commitmentMapper.address},${config.hydraS1Verifier.address}`,\n        contractNamesAsString: `Badges,AttestationsRegistry,Front,HydraS1AccountboundAttester,AvailableRootsRegistry,CommitmentMapperRegistry,HydraS1Verifier`,\n      });\n\n      evmSnapshotId = await evmSnapshot(hre);\n    });\n\n    it('should have correct contracts as immutables', async () => {\n      expect(await sismoAddressesProvider.BADGES()).to.be.equal(config.badges.address);\n      expect(await sismoAddressesProvider.ATTESTATIONS_REGISTRY()).to.be.equal(\n        config.attestationsRegistry.address\n      );\n      expect(await sismoAddressesProvider.FRONT()).to.be.equal(config.front.address);\n      expect(await sismoAddressesProvider.HYDRA_S1_ACCOUNTBOUND_ATTESTER()).to.be.equal(\n        config.hydraS1AccountboundAttester.address\n      );\n      expect(await sismoAddressesProvider.COMMITMENT_MAPPER_REGISTRY()).to.be.equal(\n        config.commitmentMapper.address\n      );\n      expect(await sismoAddressesProvider.AVAILABLE_ROOTS_REGISTRY()).to.be.equal(\n        config.availableRootsRegistry.address\n      );\n      expect(await sismoAddressesProvider.HYDRA_S1_VERIFIER()).to.be.equal(\n        config.hydraS1Verifier.address\n      );\n    });\n  });\n\n  describe('Getters', () => {\n    it('Should get the sismo contracts addresses via get (string)', async () => {\n      const badgesAddress = await sismoAddressesProvider['get(string)']('Badges');\n      const attestationsRegistryAddress = await sismoAddressesProvider['get(string)'](\n        'AttestationsRegistry'\n      );\n      const frontAddress = await sismoAddressesProvider['get(string)']('Front');\n      const hydraS1AccountboundAttesterAddress = await sismoAddressesProvider['get(string)'](\n        'HydraS1AccountboundAttester'\n      );\n      const commitmentMapperRegistryAddress = await sismoAddressesProvider['get(string)'](\n        'CommitmentMapperRegistry'\n      );\n      const availableRootsRegistryAddress = await sismoAddressesProvider['get(string)'](\n        'AvailableRootsRegistry'\n      );\n      const hydraS1VerifierAddress = await sismoAddressesProvider['get(string)']('HydraS1Verifier');\n\n      expect(badgesAddress).to.be.eql(badges.address);\n      expect(attestationsRegistryAddress).to.be.eql(attestationsRegistry.address);\n      expect(frontAddress).to.be.eql(front.address);\n      expect(hydraS1AccountboundAttesterAddress).to.be.eql(hydraS1AccountboundAttester.address);\n      expect(commitmentMapperRegistryAddress).to.be.eql(commitmentMapperRegistry.address);\n      expect(availableRootsRegistryAddress).to.be.eql(availableRootsRegistry.address);\n      expect(hydraS1VerifierAddress).to.be.eql(hydraS1Verifier.address);\n    });\n\n    it('Should get the sismo contracts addresses via get (bytes)', async () => {\n      const badgesAddress = await sismoAddressesProvider['get(bytes32)'](\n        ethers.utils.keccak256(toUtf8Bytes('Badges'))\n      );\n      const attestationsRegistryAddress = await sismoAddressesProvider['get(bytes32)'](\n        ethers.utils.keccak256(toUtf8Bytes('AttestationsRegistry'))\n      );\n      const frontAddress = await sismoAddressesProvider['get(bytes32)'](\n        ethers.utils.keccak256(toUtf8Bytes('Front'))\n      );\n      const hydraS1AccountboundAttesterAddress = await sismoAddressesProvider['get(bytes32)'](\n        ethers.utils.keccak256(toUtf8Bytes('HydraS1AccountboundAttester'))\n      );\n      const commitmentMapperRegistryAddress = await sismoAddressesProvider['get(bytes32)'](\n        ethers.utils.keccak256(toUtf8Bytes('CommitmentMapperRegistry'))\n      );\n      const availableRootsRegistryAddress = await sismoAddressesProvider['get(bytes32)'](\n        ethers.utils.keccak256(toUtf8Bytes('AvailableRootsRegistry'))\n      );\n      const hydraS1VerifierAddress = await sismoAddressesProvider['get(bytes32)'](\n        ethers.utils.keccak256(toUtf8Bytes('HydraS1Verifier'))\n      );\n\n      expect(badgesAddress).to.be.eql(badges.address);\n      expect(attestationsRegistryAddress).to.be.eql(attestationsRegistry.address);\n      expect(frontAddress).to.be.eql(front.address);\n      expect(hydraS1AccountboundAttesterAddress).to.be.eql(hydraS1AccountboundAttester.address);\n      expect(commitmentMapperRegistryAddress).to.be.eql(commitmentMapperRegistry.address);\n      expect(availableRootsRegistryAddress).to.be.eql(availableRootsRegistry.address);\n      expect(hydraS1VerifierAddress).to.be.eql(hydraS1Verifier.address);\n    });\n\n    it('Should get the sismo contracts addresses via getAll', async () => {\n      const allContractInfos = await sismoAddressesProvider.getAll();\n      const [allNames, allNamesHash, allAddresses] = allContractInfos;\n\n      expect(allNames).to.be.eql([\n        'Badges',\n        'AttestationsRegistry',\n        'Front',\n        'HydraS1AccountboundAttester',\n        'AvailableRootsRegistry',\n        'CommitmentMapperRegistry',\n        'HydraS1Verifier',\n      ]);\n\n      expect(allNamesHash).to.be.eql([\n        ethers.utils.keccak256(toUtf8Bytes('Badges')),\n        ethers.utils.keccak256(toUtf8Bytes('AttestationsRegistry')),\n        ethers.utils.keccak256(toUtf8Bytes('Front')),\n        ethers.utils.keccak256(toUtf8Bytes('HydraS1AccountboundAttester')),\n        ethers.utils.keccak256(toUtf8Bytes('AvailableRootsRegistry')),\n        ethers.utils.keccak256(toUtf8Bytes('CommitmentMapperRegistry')),\n        ethers.utils.keccak256(toUtf8Bytes('HydraS1Verifier')),\n      ]);\n\n      expect(allAddresses).to.be.eql([\n        badges.address,\n        attestationsRegistry.address,\n        front.address,\n        hydraS1AccountboundAttester.address,\n        availableRootsRegistry.address,\n        commitmentMapperRegistry.address,\n        hydraS1Verifier.address,\n      ]);\n    });\n\n    it('Should get specific sismo contracts addresses via getBatch (string)', async () => {\n      const contractAddresses = await sismoAddressesProvider['getBatch(string[])']([\n        'Badges',\n        'AttestationsRegistry',\n      ]);\n      expect(contractAddresses).to.be.eql([badges.address, attestationsRegistry.address]);\n    });\n\n    it('Should get specific sismo contracts addresses via getBatch (bytes32)', async () => {\n      const contractAddresses = await sismoAddressesProvider['getBatch(bytes32[])']([\n        ethers.utils.keccak256(toUtf8Bytes('Badges')),\n        ethers.utils.keccak256(toUtf8Bytes('AttestationsRegistry')),\n      ]);\n      expect(contractAddresses).to.be.eql([badges.address, attestationsRegistry.address]);\n    });\n  });\n\n  describe('Setters', () => {\n    it('Should set a contract address', async () => {\n      const newBadgesAddress = '0x0000000000000000000000000000000000000001';\n      const setTx = await sismoAddressesProvider.set(newBadgesAddress, 'newBadgesContract');\n\n      await expect(setTx)\n        .emit(sismoAddressesProvider, 'ContractAddressSet')\n        .withArgs(newBadgesAddress, 'newBadgesContract');\n\n      const newBadgesContract = await sismoAddressesProvider['get(string)']('newBadgesContract');\n      expect(newBadgesContract).to.be.eql(newBadgesAddress);\n    });\n\n    it('Should revert if caller is not owner', async () => {\n      const newBadgesAddress = '0x0000000000000000000000000000000000000001';\n      await expect(\n        sismoAddressesProvider.connect(randomSigner).set(newBadgesAddress, 'newBadgesContract')\n      ).to.be.revertedWith('Ownable: caller is not the owner');\n    });\n\n    it('Should set several contract addresses via setBatch', async () => {\n      const newBadgesAddress = '0x0000000000000000000000000000000000000001';\n      const newAttestationsRegistryAddress = '0x0000000000000000000000000000000000000002';\n\n      const setBatchTx = await sismoAddressesProvider.setBatch(\n        [newBadgesAddress, newAttestationsRegistryAddress],\n        ['newBadgesContract', 'newAttestationsRegistryContract']\n      );\n\n      await expect(setBatchTx)\n        .emit(sismoAddressesProvider, 'ContractAddressSet')\n        .withArgs(newBadgesAddress, 'newBadgesContract');\n\n      await expect(setBatchTx)\n        .emit(sismoAddressesProvider, 'ContractAddressSet')\n        .withArgs(newAttestationsRegistryAddress, 'newAttestationsRegistryContract');\n\n      const newBadgesContract = await sismoAddressesProvider['get(string)']('newBadgesContract');\n      expect(newBadgesContract).to.be.eql(newBadgesAddress);\n\n      const newAttestationsRegistryContract = await sismoAddressesProvider['get(string)'](\n        'newAttestationsRegistryContract'\n      );\n      expect(newAttestationsRegistryContract).to.be.eql(newAttestationsRegistryAddress);\n    });\n  });\n});\n"
  },
  {
    "path": "tasks/deploy-tasks/full/9-upgrade-addresses-provider-on-testnets.task.ts",
    "content": "import { task } from 'hardhat/config';\nimport { HardhatRuntimeEnvironment } from 'hardhat/types';\nimport {\n  afterDeployment,\n  beforeDeployment,\n  buildDeploymentName,\n  customDeployContract,\n  DeployOptions,\n  getDeployer,\n} from '../utils';\nimport { deploymentsConfig } from '../deployments-config';\nimport {\n  AddressesProvider,\n  AddressesProvider__factory,\n  TransparentUpgradeableProxy__factory,\n} from '../../../types';\nimport { confirm } from '../../../tasks/utils';\n\nexport interface Deployed9 {\n  sismoAddressesProvider: AddressesProvider;\n}\n\nasync function deploymentAction(\n  { options }: { options: DeployOptions },\n  hre: HardhatRuntimeEnvironment\n): Promise<Deployed9> {\n  const config = deploymentsConfig[process.env.FORK_NETWORK ?? hre.network.name];\n\n  // we need to use the proxyAdmins of staging since we deployed addressesProvider create2 on staging\n  let badNetwork: string;\n  let deploymentNetwork = process.env.FORK_NETWORK ?? hre.network.name;\n  if (deploymentNetwork === 'goerliTestnet') {\n    badNetwork = 'goerliStaging';\n  } else if (deploymentNetwork === 'mumbaiTestnet') {\n    badNetwork = 'mumbaiStaging';\n  } else {\n    throw new Error('Invalid network');\n  }\n\n  options = {\n    isImplementationUpgrade: true,\n    proxyAddress: config.sismoAddressesProvider.address,\n    ...config.deployOptions,\n    ...options,\n  };\n\n  if (options.manualConfirm || options.log) {\n    console.log('9-upgrade-addresses-provider-on-testnets: ', hre.network.name);\n  }\n  const CONTRACT_NAME = 'AddressesProvider';\n\n  // Deploy SismoAddressesProvider\n  const deployer = await getDeployer(hre);\n  const deploymentName = buildDeploymentName(CONTRACT_NAME, options?.deploymentNamePrefix);\n\n  const proxyAdmin = deploymentsConfig[badNetwork].deployOptions.proxyAdmin; // we need to use the proxyAdmins of staging since we deployed addressesProvider create2 on staging\n\n  const deploymentArgs = [\n    config.badges.address,\n    config.attestationsRegistry.address,\n    config.front.address,\n    config.hydraS1AccountboundAttester.address,\n    config.availableRootsRegistry.address,\n    config.commitmentMapper.address,\n    config.hydraS1Verifier.address,\n    config.sismoAddressesProvider.owner,\n  ];\n\n  const initData = new AddressesProvider__factory().interface.encodeFunctionData('initialize', [\n    config.sismoAddressesProvider.owner,\n  ]);\n\n  await beforeDeployment(hre, deployer, CONTRACT_NAME, deploymentArgs, options);\n  const deployed = await customDeployContract(\n    hre,\n    deployer,\n    deploymentName,\n    CONTRACT_NAME,\n    deploymentArgs,\n    {\n      ...options,\n      proxyData: initData,\n      isImplementationUpgrade: true, // implementation version has been bumped from v1 to v2\n      proxyAddress: config.sismoAddressesProvider.address,\n      proxyAdmin,\n    }\n  );\n  await afterDeployment(hre, deployer, CONTRACT_NAME, deploymentArgs, deployed, options);\n\n  const sismoAddressesProvider = AddressesProvider__factory.connect(\n    deployed.address,\n    await hre.ethers.getSigner(deploymentsConfig[badNetwork].sismoAddressesProvider.owner as string)\n  );\n\n  return {\n    sismoAddressesProvider,\n  };\n}\n\ntask('9-upgrade-addresses-provider-on-testnets').setAction(deploymentAction);\n"
  },
  {
    "path": "tasks/deploy-tasks/full/local/deploy-full-local.task.ts",
    "content": "import { task } from 'hardhat/config';\nimport { HardhatRuntimeEnvironment } from 'hardhat/types';\nimport { wrapCommonDeployOptions } from '../../utils/deployment';\nimport { deploymentsConfig } from '../../../deploy-tasks/deployments-config';\nimport { DeployOptions } from '../../utils';\nimport { DeployCoreArgs, DeployedCore } from 'tasks/deploy-tasks/batch/deploy-core.task';\nimport {\n  DeployHydraS1SimpleAttesterArgs,\n  DeployedHydraS1SimpleAttester,\n} from 'tasks/deploy-tasks/unit/attesters/hydra-s1/deploy-hydra-s1-simple-attester.task';\nimport {\n  DeployHydraS1AccountboundAttesterArgs,\n  DeployedHydraS1AccountboundAttester,\n} from 'tasks/deploy-tasks/unit/attesters/hydra-s1/deploy-hydra-s1-accountbound-attester.task';\nimport { EVENT_TRIGGERER_ROLE } from '../../../../utils';\nimport {\n  DeployAvailableRootsRegistry,\n  DeployedAvailableRootsRegistry,\n} from 'tasks/deploy-tasks/unit/periphery/deploy-available-roots-registry.task';\nimport {\n  DeployCommitmentMapperArgs,\n  DeployedCommitmentMapper,\n} from 'tasks/deploy-tasks/unit/periphery/deploy-commitment-mapper-registry.task';\nimport {\n  DeployedPythia1SimpleAttester,\n  DeployPythia1SimpleAttesterArgs,\n} from 'tasks/deploy-tasks/unit/attesters/pythia-1/deploy-pythia-1-simple-attester.task';\nimport { DeployedFrontendLib } from 'tasks/deploy-tasks/unit/periphery/deploy-frontend-lib.task';\n\nasync function deploymentAction(\n  { options }: { options: DeployOptions },\n  hre: HardhatRuntimeEnvironment\n): Promise<void> {\n  if (hre.network.name != 'local' && hre.network.name != 'hardhat') {\n    throw new Error('SCRIPT TO BE USED ON LOCAL NETWORK ONLY');\n  }\n  const [owner, , , , , , proxyAdmin] = await hre.ethers.getSigners();\n  options.proxyAdmin = proxyAdmin.address;\n\n  const config = deploymentsConfig[hre.network.name];\n\n  const { attestationsRegistry, badges, front } = (await hre.run('deploy-core', {\n    uri: config.badges.uri,\n    badgeOwner: config.badges.owner,\n    registryOwner: config.attestationsRegistry.owner,\n    frontFirstCollectionId: config.front.collectionIdFirst,\n    frontLastCollectionId: config.front.collectionIdLast,\n    options,\n  } as DeployCoreArgs)) as DeployedCore;\n\n  await (await badges.grantRole(EVENT_TRIGGERER_ROLE, attestationsRegistry.address)).wait();\n  // no prefix here\n  const { availableRootsRegistry } = (await hre.run('deploy-available-roots-registry', {\n    options,\n  })) as DeployAvailableRootsRegistry as DeployedAvailableRootsRegistry;\n  const { commitmentMapperRegistry } = (await hre.run('deploy-commitment-mapper-registry', {\n    commitmentMapperPubKeyX: config.commitmentMapper.EdDSAPubKeyX,\n    commitmentMapperPubKeyY: config.commitmentMapper.EdDSAPubKeyY,\n    options,\n  } as DeployCommitmentMapperArgs)) as DeployedCommitmentMapper;\n\n  const { hydraS1SimpleAttester } = (await hre.run('deploy-hydra-s1-simple-attester', {\n    collectionIdFirst: config.hydraS1SimpleAttester.collectionIdFirst,\n    collectionIdLast: config.hydraS1SimpleAttester.collectionIdLast,\n    commitmentMapperRegistryAddress: commitmentMapperRegistry.address,\n    availableRootsRegistryAddress: availableRootsRegistry.address,\n    attestationsRegistryAddress: attestationsRegistry.address,\n    options,\n  } as DeployHydraS1SimpleAttesterArgs)) as DeployedHydraS1SimpleAttester;\n\n  const { hydraS1AccountboundAttester } = (await hre.run('deploy-hydra-s1-accountbound-attester', {\n    collectionIdFirst: config.hydraS1AccountboundAttester.collectionIdFirst,\n    collectionIdLast: config.hydraS1AccountboundAttester.collectionIdLast,\n    commitmentMapperRegistryAddress: commitmentMapperRegistry.address,\n    availableRootsRegistryAddress: availableRootsRegistry.address,\n    attestationsRegistryAddress: attestationsRegistry.address,\n    options,\n  } as DeployHydraS1AccountboundAttesterArgs)) as DeployedHydraS1AccountboundAttester;\n\n  const { pythia1SimpleAttester } = (await hre.run('deploy-pythia-1-simple-attester', {\n    collectionIdFirst: config.synapsPythia1SimpleAttester.collectionIdFirst,\n    collectionIdLast: config.synapsPythia1SimpleAttester.collectionIdLast,\n    attestationsRegistryAddress: attestationsRegistry.address,\n    commitmentSignerPubKeyX: config.synapsPythia1SimpleAttester.commitmentSignerPubKeyX,\n    commitmentSignerPubKeyY: config.synapsPythia1SimpleAttester.commitmentSignerPubKeyY,\n    options,\n  } as DeployPythia1SimpleAttesterArgs)) as DeployedPythia1SimpleAttester;\n\n  await hre.run('deploy-frontend-lib', {\n    hydraS1AccountboundAttester: hydraS1AccountboundAttester.address,\n    options,\n  });\n\n  if (hydraS1SimpleAttester) {\n    await hre.run('register-for-attester', {\n      availableRootsRegistryAddress: availableRootsRegistry.address,\n      attester: hydraS1SimpleAttester.address,\n      root: config.hydraS1SimpleAttester.initialRoot,\n    });\n  }\n\n  await hre.run('register-for-attester', {\n    availableRootsRegistryAddress: availableRootsRegistry.address,\n    attester: hydraS1AccountboundAttester.address,\n    root: config.hydraS1AccountboundAttester.initialRoot,\n  });\n  options?.log && console.log('Contracts deployed on local');\n\n  await (\n    await attestationsRegistry.authorizeRange(\n      hydraS1AccountboundAttester.address,\n      config.hydraS1AccountboundAttester.collectionIdFirst,\n      config.hydraS1AccountboundAttester.collectionIdLast\n    )\n  ).wait();\n\n  if (hydraS1SimpleAttester) {\n    await (\n      await attestationsRegistry.authorizeRange(\n        hydraS1SimpleAttester.address,\n        config.hydraS1SimpleAttester.collectionIdFirst,\n        config.hydraS1SimpleAttester.collectionIdLast\n      )\n    ).wait();\n  }\n\n  await (\n    await attestationsRegistry.authorizeRange(\n      pythia1SimpleAttester.address,\n      config.synapsPythia1SimpleAttester.collectionIdFirst,\n      config.synapsPythia1SimpleAttester.collectionIdLast\n    )\n  ).wait();\n}\n\ntask('deploy-full-local').setAction(wrapCommonDeployOptions(deploymentAction));\n"
  },
  {
    "path": "tasks/deploy-tasks/full/staging/deploy-sismo-addresses-provider-staging.task.ts",
    "content": "import { task } from 'hardhat/config';\nimport { HardhatRuntimeEnvironment } from 'hardhat/types';\nimport {\n  afterDeployment,\n  beforeDeployment,\n  buildDeploymentName,\n  customDeployContract,\n  DeployOptions,\n  getDeployer,\n} from '../../utils';\nimport { deploymentsConfig } from '../../deployments-config';\nimport { AddressesProvider__factory } from '../../../../types';\n\nconst CONTRACT_NAME = 'AddressesProvider';\n\nasync function deploymentAction(\n  { options }: { options: DeployOptions },\n  hre: HardhatRuntimeEnvironment\n) {\n  const config = deploymentsConfig[hre.network.name];\n  options = { ...config.deployOptions, ...options };\n\n  const deployer = await getDeployer(hre);\n  const deploymentName = 'AddressesProviderStaging';\n\n  // always start by giving the ownership of the deployer\n  const deploymentArgs = [\n    config.badges.address, // badges,\n    config.attestationsRegistry.address, // attestationsRegistry,\n    config.front.address, // front,\n    config.hydraS1AccountboundAttester.address, // hydraS1AccountboundAttester,\n    config.availableRootsRegistry.address, // availableRootsRegistry,\n    config.commitmentMapper.address, // commitmentMapperRegistry,\n    config.hydraS1Verifier.address, // hydraS1Verifier,\n    config.sismoAddressesProvider.owner, // deployer.address,\n  ];\n\n  await beforeDeployment(hre, deployer, CONTRACT_NAME, deploymentArgs, options);\n\n  const initData = new AddressesProvider__factory().interface.encodeFunctionData('initialize', [\n    config.sismoAddressesProvider.owner,\n  ]);\n\n  const deployed = await customDeployContract(\n    hre,\n    deployer,\n    deploymentName,\n    CONTRACT_NAME,\n    deploymentArgs,\n    {\n      ...options,\n      proxyData: initData,\n    }\n  );\n  await afterDeployment(hre, deployer, CONTRACT_NAME, deploymentArgs, deployed, options);\n\n  if (options.manualConfirm || options.log) {\n    console.log(`\n    ************************************************************\n    *                           RECAP                          *\n    ************************************************************\n\n    date: ${new Date().toISOString()}\n\n    ** Common **\n      proxyAdmin: ${config.deployOptions.proxyAdmin}\n\n    * AddressesProviderStaging:\n      -> address: ${(await hre.deployments.all()).AddressesProviderStaging.address}\n  `);\n  }\n}\n\ntask('deploy-sismo-addresses-provider-staging').setAction(deploymentAction);\n"
  },
  {
    "path": "tasks/deploy-tasks/tests/deploy-mock-attestations-registry.task.ts",
    "content": "import { BigNumberish } from 'ethers';\nimport { task } from 'hardhat/config';\nimport { HardhatRuntimeEnvironment } from 'hardhat/types';\nimport { MockAttestationsRegistry, MockAttestationsRegistry__factory } from '../../../types';\nimport {\n  afterDeployment,\n  beforeDeployment,\n  buildDeploymentName,\n  customDeployContract,\n  DeployOptions,\n  getDeployer,\n  wrapCommonDeployOptions,\n} from '../utils';\n\nexport interface DeployMockAttestationsRegistryArgs {\n  attestationValue: BigNumberish;\n  options?: DeployOptions;\n}\n\nexport interface DeployedMockAttestationsRegistry {\n  mockAttestationsRegistry: MockAttestationsRegistry;\n}\n\nconst CONTRACT_NAME = 'MockAttestationsRegistry';\n\nasync function deploymentAction(\n  { attestationValue, options }: DeployMockAttestationsRegistryArgs,\n  hre: HardhatRuntimeEnvironment\n) {\n  const deployer = await getDeployer(hre);\n  const deploymentName = buildDeploymentName(CONTRACT_NAME, options?.deploymentNamePrefix);\n  const deploymentArgs = [attestationValue || 0];\n\n  await beforeDeployment(hre, deployer, CONTRACT_NAME, deploymentArgs, options);\n\n  const deployed = await customDeployContract(\n    hre,\n    deployer,\n    deploymentName,\n    CONTRACT_NAME,\n    deploymentArgs,\n    options\n  );\n\n  await afterDeployment(hre, deployer, CONTRACT_NAME, deploymentArgs, deployed, options);\n  const mockAttestationsRegistry = MockAttestationsRegistry__factory.connect(\n    deployed.address,\n    deployer\n  );\n  return { mockAttestationsRegistry };\n}\n\ntask('deploy-mock-attestations-registry').setAction(wrapCommonDeployOptions(deploymentAction));\n"
  },
  {
    "path": "tasks/deploy-tasks/tests/deploy-mock-attester-and-core.task.ts",
    "content": "import { task } from 'hardhat/config';\nimport { HardhatRuntimeEnvironment } from 'hardhat/types';\nimport {\n  DeployOptions,\n  getDeployer,\n  wrapCommonDeployOptions,\n} from '../../../tasks/deploy-tasks/utils';\nimport { DeployCoreArgs, DeployedCore } from '../batch/deploy-core.task';\nimport { DeployedMockAttester, DeployMockAttesterArgs } from './deploy-mock-attester.task';\n\nexport type DeployMockAttesterAndCoreArgs = Omit<\n  DeployMockAttesterArgs & DeployCoreArgs & DeployOptions,\n  'attestationsRegistryAddress' | ''\n>;\n\nexport interface DeployedMockAttesterAndCore extends DeployedMockAttester, DeployedCore {}\n\nasync function deploymentAction(\n  options: DeployMockAttesterAndCoreArgs,\n  hre: HardhatRuntimeEnvironment\n): Promise<DeployedMockAttesterAndCore> {\n  if (options?.log) {\n    console.log('Deploying ALL on network: ', hre.network.name);\n  }\n\n  const deployer = await getDeployer(hre);\n\n  const deployedCore = (await hre.run('deploy-core', {\n    uri: options.uri,\n    badgeOwner: options.badgeOwner,\n    registryOwner: options.registryOwner,\n    frontFirstCollectionId: options.frontLastCollectionId,\n    frontLastCollectionId: options.frontLastCollectionId,\n    options: options.options,\n  } as DeployCoreArgs)) as DeployedCore;\n\n  const deployedMockAttester = (await hre.run('deploy-mock-attester', {\n    attestationsRegistryAddress: deployedCore.attestationsRegistry.address,\n    collectionIdFirst: options.collectionIdFirst,\n    collectionIdLast: options.collectionIdLast,\n    options: options.options,\n  } as DeployMockAttesterArgs)) as DeployedMockAttester;\n\n  // Authorize Mock attester to record attestation on the attestationsRegistry\n  // for its corresponding min and max\n  if (options?.log) {\n    console.log('Authorize Mock on the attestationsRegistry');\n  }\n\n  await deployedCore.attestationsRegistry\n    .connect(deployer)\n    .authorizeRange(\n      deployedCore.front.address,\n      await deployedCore.front.EARLY_USER_COLLECTION(),\n      await deployedCore.front.EARLY_USER_COLLECTION()\n    );\n\n  await deployedCore.attestationsRegistry\n    .connect(deployer)\n    .authorizeRange(\n      deployedMockAttester.mockAttester.address,\n      await deployedMockAttester.mockAttester.ATTESTATION_ID_MIN(),\n      await deployedMockAttester.mockAttester.ATTESTATION_ID_MAX()\n    );\n\n  return { ...deployedCore, ...deployedMockAttester };\n}\n\ntask('deploy-mock-attester-and-core').setAction(wrapCommonDeployOptions(deploymentAction));\n"
  },
  {
    "path": "tasks/deploy-tasks/tests/deploy-mock-attester.task.ts",
    "content": "import { BigNumber, BigNumberish } from 'ethers';\nimport { task } from 'hardhat/config';\nimport { HardhatRuntimeEnvironment } from 'hardhat/types';\nimport {\n  afterDeployment,\n  beforeDeployment,\n  buildDeploymentName,\n  customDeployContract,\n  DeployOptions,\n  getDeployer,\n  wrapCommonDeployOptions,\n} from '../../../tasks/deploy-tasks/utils';\nimport { MockAttester, MockAttester__factory } from '../../../types';\n\nexport interface DeployMockAttesterArgs {\n  // address of the attestations Registry contract\n  attestationsRegistryAddress: string;\n  collectionIdFirst: BigNumberish;\n  collectionIdLast: BigNumberish;\n  options?: DeployOptions;\n}\n\nexport interface DeployedMockAttester {\n  mockAttester: MockAttester;\n}\n\nconst CONTRACT_NAME = 'MockAttester';\n\nasync function deploymentAction(\n  {\n    attestationsRegistryAddress,\n    collectionIdFirst,\n    collectionIdLast,\n    options,\n  }: DeployMockAttesterArgs,\n  hre: HardhatRuntimeEnvironment\n): Promise<DeployedMockAttester> {\n  const deployer = await getDeployer(hre);\n\n  const deploymentName = buildDeploymentName(CONTRACT_NAME, options?.deploymentNamePrefix);\n\n  const deploymentArgs = [\n    attestationsRegistryAddress,\n    BigNumber.from(collectionIdFirst),\n    BigNumber.from(collectionIdLast),\n  ];\n\n  await beforeDeployment(hre, deployer, CONTRACT_NAME, deploymentArgs, options);\n\n  const deployed = await customDeployContract(\n    hre,\n    deployer,\n    deploymentName,\n    CONTRACT_NAME,\n    deploymentArgs,\n    options\n  );\n\n  await afterDeployment(hre, deployer, CONTRACT_NAME, deploymentArgs, deployed, options);\n  const mockAttester = MockAttester__factory.connect(deployed.address, deployer);\n  return { mockAttester };\n}\n\ntask('deploy-mock-attester')\n  .addParam('attestationsRegistryAddress', 'Address of the attestations contract')\n  .addOptionalParam('collectionIdFirst', '')\n  .addOptionalParam('collectionIdLast', '')\n  .setAction(wrapCommonDeployOptions(deploymentAction));\n"
  },
  {
    "path": "tasks/deploy-tasks/tests/deploy-mock-contract-using-sismo-lib.task.ts",
    "content": "import { task } from 'hardhat/config';\nimport { HardhatRuntimeEnvironment } from 'hardhat/types';\nimport {\n  afterDeployment,\n  beforeDeployment,\n  buildDeploymentName,\n  customDeployContract,\n  DeployOptions,\n  getDeployer,\n  wrapCommonDeployOptions,\n} from '../../../tasks/deploy-tasks/utils';\nimport { MockContractUsingSismoLib, MockContractUsingSismoLib__factory } from '../../../types';\n\nexport interface DeployMockContractUsingSismoLibArgs {\n  options?: DeployOptions;\n}\n\nexport interface DeployedMockContractUsingSismoLib {\n  mockContractUsingSismoLib: MockContractUsingSismoLib;\n}\n\nconst CONTRACT_NAME = 'MockContractUsingSismoLib';\n\nasync function deploymentAction(\n  { options }: DeployMockContractUsingSismoLibArgs,\n  hre: HardhatRuntimeEnvironment\n): Promise<DeployedMockContractUsingSismoLib> {\n  const deployer = await getDeployer(hre);\n\n  const deploymentName = buildDeploymentName(CONTRACT_NAME, options?.deploymentNamePrefix);\n\n  const deploymentArgs = [];\n\n  await beforeDeployment(hre, deployer, CONTRACT_NAME, deploymentArgs, options);\n\n  const deployed = await customDeployContract(\n    hre,\n    deployer,\n    deploymentName,\n    CONTRACT_NAME,\n    deploymentArgs,\n    options\n  );\n\n  await afterDeployment(hre, deployer, CONTRACT_NAME, deploymentArgs, deployed, options);\n  const mockContractUsingSismoLib = MockContractUsingSismoLib__factory.connect(\n    deployed.address,\n    deployer\n  );\n\n  return { mockContractUsingSismoLib };\n}\n\ntask('deploy-mock-contract-using-sismo-lib').setAction(wrapCommonDeployOptions(deploymentAction));\n"
  },
  {
    "path": "tasks/deploy-tasks/tests/deploy-zk-badgebound-erc721.task.ts",
    "content": "import { task } from 'hardhat/config';\nimport { HardhatRuntimeEnvironment } from 'hardhat/types';\nimport { getImplementation } from './../../../utils';\nimport { ZKBadgeboundERC721, ZKBadgeboundERC721__factory } from '../../../types';\nimport {\n  afterDeployment,\n  beforeDeployment,\n  customDeployContract,\n  DeployOptions,\n  getDeployer,\n  wrapCommonDeployOptions,\n} from '../utils';\n\nexport interface DeployZKBadgeboundERC721Args {\n  deploymentName?: string;\n  name: string;\n  symbol: string;\n  tokenURI: string;\n  gatingBadgeTokenId: number;\n  admin: string;\n  options?: DeployOptions;\n}\n\nexport interface DeployedZkBadgeboundERC721 {\n  zkBadgeboundERC721: ZKBadgeboundERC721;\n}\n\nconst CONTRACT_NAME = 'ZKBadgeboundERC721';\n\nasync function deploymentAction(\n  {\n    deploymentName,\n    name,\n    symbol,\n    tokenURI,\n    gatingBadgeTokenId,\n    admin,\n    options,\n  }: DeployZKBadgeboundERC721Args,\n  hre: HardhatRuntimeEnvironment\n) {\n  const deployer = await getDeployer(hre);\n  const deploymentArgs = [name, symbol, tokenURI, gatingBadgeTokenId, admin];\n\n  await beforeDeployment(hre, deployer, CONTRACT_NAME, deploymentArgs, options);\n\n  const initData = new ZKBadgeboundERC721__factory().interface.encodeFunctionData('initialize', [\n    name,\n    symbol,\n    tokenURI,\n    admin,\n  ]);\n\n  const deployed = await customDeployContract(\n    hre,\n    deployer,\n    deploymentName ? deploymentName : `${options?.deploymentNamePrefix}_${CONTRACT_NAME}`,\n    CONTRACT_NAME,\n    deploymentArgs,\n    { ...options, proxyData: initData }\n  );\n\n  await afterDeployment(hre, deployer, CONTRACT_NAME, deploymentArgs, deployed, options);\n  const zkBadgeboundERC721 = ZKBadgeboundERC721__factory.connect(deployed.address, deployer);\n\n  if (options?.manualConfirm || options?.log) {\n    console.log(`\n  ************************************************************\n  *                           RECAP                          *\n  ************************************************************\n\n  date: ${new Date().toISOString()}\n\n  * ZKBadgeboundERC721:\n  -> proxy: ${zkBadgeboundERC721.address}\n  -> implem: ${await getImplementation(zkBadgeboundERC721)}\n  `);\n  }\n\n  return { zkBadgeboundERC721 };\n}\n\ntask('deploy-zk-badgebound-erc721')\n  .addOptionalParam('deploymentName', 'Name of the deployment')\n  .addParam('name', 'Name of the token')\n  .addParam('symbol', 'Symbol of the token')\n  .addParam('tokenURI', 'Token URI')\n  .addParam('gatingBadgeTokenId', 'gatingBadgeTokenId')\n  .setAction(wrapCommonDeployOptions(deploymentAction));\n"
  },
  {
    "path": "tasks/deploy-tasks/unit/attesters/hydra-s1/deploy-hydra-s1-accountbound-attester.task.ts",
    "content": "import { task } from 'hardhat/config';\nimport { HardhatRuntimeEnvironment } from 'hardhat/types';\nimport {\n  getDeployer,\n  beforeDeployment,\n  afterDeployment,\n  buildDeploymentName,\n  customDeployContract,\n  wrapCommonDeployOptions,\n  DeployOptions,\n} from '../../../utils';\nimport {\n  HydraS1AccountboundAttester,\n  HydraS1AccountboundAttester__factory,\n  HydraS1Verifier,\n  HydraS1Verifier__factory,\n} from '../../../../../types';\nimport { BigNumber, BigNumberish } from 'ethers';\n\nexport interface DeployHydraS1AccountboundAttesterArgs {\n  // address of the proving scheme verifier contract\n  hydraS1VerifierAddress?: string;\n  // address of the registry MerkleRoot contract\n  availableRootsRegistryAddress: string;\n  // address of the commitment mapper registry\n  commitmentMapperRegistryAddress: string;\n  // address of the attestations contract,\n  // which is part of the SAS\n  // Sismo Attestation State\n  attestationsRegistryAddress: string;\n  collectionIdFirst: BigNumberish;\n  collectionIdLast: BigNumberish;\n  owner: string;\n  options?: DeployOptions;\n}\n\nexport interface DeployedHydraS1AccountboundAttester {\n  hydraS1AccountboundAttester: HydraS1AccountboundAttester;\n  hydraS1Verifier: HydraS1Verifier;\n}\n\nconst CONTRACT_NAME = 'HydraS1AccountboundAttester';\n\nasync function deploymentAction(\n  {\n    hydraS1VerifierAddress,\n    availableRootsRegistryAddress,\n    commitmentMapperRegistryAddress,\n    attestationsRegistryAddress,\n    collectionIdFirst,\n    collectionIdLast,\n    owner,\n    options,\n  }: DeployHydraS1AccountboundAttesterArgs,\n  hre: HardhatRuntimeEnvironment\n): Promise<DeployedHydraS1AccountboundAttester> {\n  const deployer = await getDeployer(hre);\n  const deploymentName = buildDeploymentName(CONTRACT_NAME, options?.deploymentNamePrefix);\n\n  let hydraS1Verifier: HydraS1Verifier;\n\n  if (!hydraS1VerifierAddress) {\n    ({ hydraS1Verifier } = await hre.run('deploy-hydra-s1-verifier', {\n      options,\n    }));\n    hydraS1VerifierAddress = hydraS1Verifier.address;\n  } else {\n    hydraS1Verifier = HydraS1Verifier__factory.connect(hydraS1VerifierAddress, deployer);\n  }\n  const deploymentArgs = [\n    attestationsRegistryAddress,\n    hydraS1VerifierAddress,\n    availableRootsRegistryAddress,\n    commitmentMapperRegistryAddress,\n    BigNumber.from(collectionIdFirst),\n    BigNumber.from(collectionIdLast),\n    owner || deployer.address,\n  ];\n\n  await beforeDeployment(hre, deployer, CONTRACT_NAME, deploymentArgs, options);\n\n  const initData = new HydraS1AccountboundAttester__factory().interface.encodeFunctionData(\n    'initialize',\n    [owner || deployer.address]\n  );\n\n  const deployed = await customDeployContract(\n    hre,\n    deployer,\n    deploymentName,\n    CONTRACT_NAME,\n    deploymentArgs,\n    {\n      ...options,\n      proxyData: initData,\n    }\n  );\n\n  await afterDeployment(hre, deployer, CONTRACT_NAME, deploymentArgs, deployed, options);\n\n  const hydraS1AccountboundAttester = HydraS1AccountboundAttester__factory.connect(\n    deployed.address,\n    deployer\n  );\n\n  return { hydraS1AccountboundAttester, hydraS1Verifier };\n}\n\ntask('deploy-hydra-s1-accountbound-attester')\n  .addParam('collectionIdFirst', '')\n  .addParam('collectionIdLast', '')\n  .addOptionalParam(\n    'hydraS1VerifierAddress',\n    'address of the proving scheme verifier. Deploy verifier if not defined.'\n  )\n  .addParam('availableRootsRegistryAddress', 'address of the registryMerkleRoot contract')\n  .addParam(\n    'commitmentMapperRegistryAddress',\n    'address of the commitmentMapperRegistryAddress contract'\n  )\n  .addParam('attestationsRegistryAddress', 'Address of the attestations contract')\n  .setAction(wrapCommonDeployOptions(deploymentAction));\n"
  },
  {
    "path": "tasks/deploy-tasks/unit/attesters/hydra-s1/deploy-hydra-s1-simple-attester.task.ts",
    "content": "import { task } from 'hardhat/config';\nimport { HardhatRuntimeEnvironment } from 'hardhat/types';\nimport {\n  getDeployer,\n  beforeDeployment,\n  afterDeployment,\n  buildDeploymentName,\n  customDeployContract,\n  wrapCommonDeployOptions,\n  DeployOptions,\n} from '../../../../../tasks/deploy-tasks/utils';\n\nimport {\n  HydraS1SimpleAttester,\n  HydraS1SimpleAttester__factory,\n  HydraS1Verifier,\n  HydraS1Verifier__factory,\n} from '../../../../../types';\nimport { BigNumber, BigNumberish } from 'ethers';\n\nexport interface DeployHydraS1SimpleAttesterArgs {\n  // mandatory parameter that indicates if we want to deploy the hydra S1 simple attester\n  enableDeployment: boolean;\n  // address of the proving scheme verifier contract\n  hydraS1VerifierAddress?: string;\n  // address of the registryMerkleRoot contract\n  availableRootsRegistryAddress: string;\n  // address of the commitment mapper registry\n  commitmentMapperRegistryAddress: string;\n  // address of the attestations contract,\n  // which is part of the SAS\n  // Sismo Attestation State\n  attestationsRegistryAddress: string;\n  collectionIdFirst: BigNumberish;\n  collectionIdLast: BigNumberish;\n  options?: DeployOptions;\n}\n\nexport interface DeployedHydraS1SimpleAttester {\n  hydraS1SimpleAttester?: HydraS1SimpleAttester;\n  hydraS1Verifier: HydraS1Verifier;\n}\n\nconst CONTRACT_NAME = 'HydraS1SimpleAttester';\n\nasync function deploymentAction(\n  {\n    enableDeployment,\n    hydraS1VerifierAddress,\n    availableRootsRegistryAddress,\n    commitmentMapperRegistryAddress,\n    attestationsRegistryAddress,\n    collectionIdFirst = 100,\n    collectionIdLast = 0,\n    options,\n  }: DeployHydraS1SimpleAttesterArgs,\n  hre: HardhatRuntimeEnvironment\n): Promise<DeployedHydraS1SimpleAttester> {\n  const deployer = await getDeployer(hre);\n  const deploymentName = buildDeploymentName(CONTRACT_NAME, options?.deploymentNamePrefix);\n\n  let hydraS1Verifier: HydraS1Verifier;\n\n  if (!hydraS1VerifierAddress) {\n    ({ hydraS1Verifier } = await hre.run('deploy-hydra-s1-verifier', {\n      options,\n    }));\n    hydraS1VerifierAddress = hydraS1Verifier.address;\n  } else {\n    hydraS1Verifier = HydraS1Verifier__factory.connect(hydraS1VerifierAddress, deployer);\n  }\n\n  // if enableDeployment is false, we just return the verifier\n  if (!enableDeployment) {\n    return { hydraS1Verifier };\n  }\n\n  const deploymentArgs = [\n    attestationsRegistryAddress,\n    hydraS1VerifierAddress,\n    availableRootsRegistryAddress,\n    commitmentMapperRegistryAddress,\n    BigNumber.from(collectionIdFirst),\n    BigNumber.from(collectionIdLast),\n  ];\n\n  await beforeDeployment(hre, deployer, CONTRACT_NAME, deploymentArgs, options);\n\n  const initData = '0x';\n\n  const deployed = await customDeployContract(\n    hre,\n    deployer,\n    deploymentName,\n    CONTRACT_NAME,\n    deploymentArgs,\n    {\n      ...options,\n      proxyData: initData,\n    }\n  );\n\n  await afterDeployment(hre, deployer, CONTRACT_NAME, deploymentArgs, deployed, options);\n\n  const hydraS1SimpleAttester = HydraS1SimpleAttester__factory.connect(deployed.address, deployer);\n  return { hydraS1SimpleAttester, hydraS1Verifier };\n}\n\ntask('deploy-hydra-s1-simple-attester')\n  .addParam('collectionIdFirst', '')\n  .addParam('collectionIdLast', '')\n  .addOptionalParam(\n    'hydraS1VerifierAddress',\n    'address of the proving scheme verifier. Deploy verifier if not defined.'\n  )\n  .addParam('availableRootsRegistryAddress', 'address of the registryMerkleRoot contract')\n  .addParam(\n    'commitmentMapperRegistryAddress',\n    'address of the commitmentMapperRegistryAddress contract'\n  )\n  .addParam('attestationsRegistryAddress', 'Address of the attestations contract')\n  .setAction(wrapCommonDeployOptions(deploymentAction));\n"
  },
  {
    "path": "tasks/deploy-tasks/unit/attesters/hydra-s1/deploy-hydra-s1-verifier.task.ts",
    "content": "import { task } from 'hardhat/config';\nimport { HardhatRuntimeEnvironment } from 'hardhat/types';\nimport {\n  getDeployer,\n  beforeDeployment,\n  afterDeployment,\n  buildDeploymentName,\n  customDeployContract,\n  wrapCommonDeployOptions,\n  DeployOptions,\n} from '../../../../../tasks/deploy-tasks/utils';\nimport { HydraS1Verifier, HydraS1Verifier__factory } from '../../../../../types';\n\nexport interface DeployHydraS1Verifier {\n  options?: DeployOptions;\n}\n\nexport interface DeployedHydraS1Verifier {\n  hydraS1Verifier: HydraS1Verifier;\n}\n\nconst CONTRACT_NAME = 'HydraS1Verifier';\n\nasync function deploymentAction(\n  { options }: DeployHydraS1Verifier,\n  hre: HardhatRuntimeEnvironment\n): Promise<DeployedHydraS1Verifier> {\n  const deployer = await getDeployer(hre);\n  const deploymentName = buildDeploymentName(CONTRACT_NAME, options?.deploymentNamePrefix);\n  const deploymentArgs = [];\n\n  await beforeDeployment(hre, deployer, CONTRACT_NAME, deploymentArgs, options);\n\n  const deployed = await hre.deployments.deploy(deploymentName, {\n    contract: CONTRACT_NAME,\n    from: deployer.address,\n    args: deploymentArgs,\n    skipIfAlreadyDeployed: false,\n  });\n\n  await afterDeployment(hre, deployer, CONTRACT_NAME, deploymentArgs, deployed, options);\n\n  const hydraS1Verifier = HydraS1Verifier__factory.connect(deployed.address, deployer);\n  return { hydraS1Verifier };\n}\n\ntask('deploy-hydra-s1-verifier').setAction(wrapCommonDeployOptions(deploymentAction));\n"
  },
  {
    "path": "tasks/deploy-tasks/unit/attesters/pythia-1/deploy-pythia-1-simple-attester.task.ts",
    "content": "import { task } from 'hardhat/config';\nimport { HardhatRuntimeEnvironment } from 'hardhat/types';\nimport {\n  getDeployer,\n  beforeDeployment,\n  afterDeployment,\n  buildDeploymentName,\n  customDeployContract,\n  wrapCommonDeployOptions,\n  DeployOptions,\n} from '../../../utils';\n\nimport {\n  Pythia1SimpleAttester,\n  Pythia1SimpleAttester__factory,\n  Pythia1Verifier,\n  Pythia1Verifier__factory,\n} from '../../../../../types';\nimport { BigNumber, BigNumberish } from 'ethers';\n\nexport interface DeployPythia1SimpleAttesterArgs {\n  // address of the proving scheme verifier contract\n  pythia1VerifierAddress?: string;\n  // Commitment Signer public key\n  commitmentSignerPubKeyX?: string;\n  commitmentSignerPubKeyY?: string;\n  // address of the owner that can update the commitmentSignerPubKey\n  owner?: string;\n  // address of the attestations contract,\n  // which is part of the Attestation Registry\n  // Sismo Attestation State\n  attestationsRegistryAddress: string;\n  collectionIdFirst: BigNumberish;\n  collectionIdLast: BigNumberish;\n  options?: DeployOptions;\n}\n\nexport interface DeployedPythia1SimpleAttester {\n  pythia1SimpleAttester: Pythia1SimpleAttester;\n  pythia1Verifier: Pythia1Verifier;\n}\n\nconst CONTRACT_NAME = 'Pythia1SimpleAttester';\n\nasync function deploymentAction(\n  {\n    pythia1VerifierAddress,\n    attestationsRegistryAddress,\n    commitmentSignerPubKeyX,\n    commitmentSignerPubKeyY,\n    collectionIdFirst = 100,\n    collectionIdLast = 0,\n    owner,\n    options,\n  }: DeployPythia1SimpleAttesterArgs,\n  hre: HardhatRuntimeEnvironment\n): Promise<DeployedPythia1SimpleAttester> {\n  const deployer = await getDeployer(hre);\n  const deploymentName = buildDeploymentName(CONTRACT_NAME, options?.deploymentNamePrefix);\n\n  let pythia1Verifier: Pythia1Verifier;\n\n  if (!pythia1VerifierAddress) {\n    ({ pythia1Verifier } = await hre.run('deploy-pythia-1-verifier', {\n      options,\n    }));\n    pythia1VerifierAddress = pythia1Verifier.address;\n  } else {\n    pythia1Verifier = Pythia1Verifier__factory.connect(pythia1VerifierAddress, deployer);\n  }\n\n  const deploymentArgs = [\n    attestationsRegistryAddress,\n    BigNumber.from(collectionIdFirst),\n    BigNumber.from(collectionIdLast),\n    pythia1VerifierAddress,\n    [commitmentSignerPubKeyX, commitmentSignerPubKeyY],\n    owner || deployer.address,\n  ];\n\n  await beforeDeployment(hre, deployer, CONTRACT_NAME, deploymentArgs, options);\n\n  const initData = new Pythia1SimpleAttester__factory().interface.encodeFunctionData('initialize', [\n    [commitmentSignerPubKeyX, commitmentSignerPubKeyY],\n    owner || deployer.address,\n  ]);\n\n  const deployed = await customDeployContract(\n    hre,\n    deployer,\n    deploymentName,\n    CONTRACT_NAME,\n    deploymentArgs,\n    {\n      ...options,\n      proxyData: initData,\n    }\n  );\n\n  await afterDeployment(hre, deployer, CONTRACT_NAME, deploymentArgs, deployed, options);\n\n  const pythia1SimpleAttester = Pythia1SimpleAttester__factory.connect(deployed.address, deployer);\n  return { pythia1SimpleAttester, pythia1Verifier };\n}\n\ntask('deploy-pythia-1-simple-attester')\n  .addParam('collectionIdFirst', '')\n  .addParam('collectionIdLast', '')\n  .addOptionalParam(\n    'pythia1VerifierAddress',\n    'address of the proving scheme verifier. Deploy verifier if not defined.'\n  )\n  .addParam('attestationsRegistryAddress', 'Address of the attestations contract')\n  .addParam('commitmentSignerPubKeyX', 'Eddsa public key coordinate X')\n  .addParam('commitmentSignerPubKeyY', 'Eddsa public key coordinate Y')\n  .addOptionalParam(\n    'owner',\n    'Owner of the contract that can change the commitment signer pubKey. Default to deployer'\n  )\n  .setAction(wrapCommonDeployOptions(deploymentAction));\n"
  },
  {
    "path": "tasks/deploy-tasks/unit/attesters/pythia-1/deploy-pythia-1-verifier.task.ts",
    "content": "import { task } from 'hardhat/config';\nimport { HardhatRuntimeEnvironment } from 'hardhat/types';\nimport {\n  getDeployer,\n  beforeDeployment,\n  afterDeployment,\n  buildDeploymentName,\n  customDeployContract,\n  wrapCommonDeployOptions,\n  DeployOptions,\n} from '../../../utils';\nimport { Pythia1Verifier, Pythia1Verifier__factory } from '../../../../../types';\n\nexport interface DeployPythia1Verifier {\n  options?: DeployOptions;\n}\n\nexport interface DeployedPythia1Verifier {\n  pythia1Verifier: Pythia1Verifier;\n}\n\nconst CONTRACT_NAME = 'Pythia1Verifier';\n\nasync function deploymentAction(\n  { options }: DeployPythia1Verifier,\n  hre: HardhatRuntimeEnvironment\n): Promise<DeployedPythia1Verifier> {\n  const deployer = await getDeployer(hre);\n  const deploymentName = buildDeploymentName(CONTRACT_NAME, options?.deploymentNamePrefix);\n  const deploymentArgs = [];\n\n  await beforeDeployment(hre, deployer, CONTRACT_NAME, deploymentArgs, options);\n\n  const deployed = await hre.deployments.deploy(deploymentName, {\n    contract: CONTRACT_NAME,\n    from: deployer.address,\n    args: deploymentArgs,\n    skipIfAlreadyDeployed: false,\n  });\n\n  await afterDeployment(hre, deployer, CONTRACT_NAME, deploymentArgs, deployed, options);\n\n  const pythia1Verifier = Pythia1Verifier__factory.connect(deployed.address, deployer);\n  return { pythia1Verifier };\n}\n\ntask('deploy-pythia-1-verifier').setAction(wrapCommonDeployOptions(deploymentAction));\n"
  },
  {
    "path": "tasks/deploy-tasks/unit/core/deploy-attestations-registry.task.ts",
    "content": "import { task } from 'hardhat/config';\nimport { HardhatRuntimeEnvironment } from 'hardhat/types';\nimport {\n  getDeployer,\n  beforeDeployment,\n  afterDeployment,\n  buildDeploymentName,\n  customDeployContract,\n  wrapCommonDeployOptions,\n  DeployOptions,\n} from '../../../../tasks/deploy-tasks/utils';\nimport { AttestationsRegistry, AttestationsRegistry__factory } from '../../../../types';\n\nexport interface DeployAttestationsRegistryArgs {\n  // owner of the contract\n  owner?: string;\n  badges: string;\n  // attester register role\n  attesterRegister?: string;\n  options?: DeployOptions;\n}\n\nexport interface DeployedAttestationsRegistry {\n  attestationsRegistry: AttestationsRegistry;\n}\n\nconst CONTRACT_NAME = 'AttestationsRegistry';\n\nasync function deploymentAction(\n  { owner, badges, options }: DeployAttestationsRegistryArgs,\n  hre: HardhatRuntimeEnvironment\n): Promise<DeployedAttestationsRegistry> {\n  const deployer = await getDeployer(hre);\n  const deploymentName = buildDeploymentName(CONTRACT_NAME, options?.deploymentNamePrefix);\n  const deploymentArgs = [owner || deployer.address, badges || deployer.address];\n\n  await beforeDeployment(hre, deployer, CONTRACT_NAME, deploymentArgs, options);\n\n  const initData = new AttestationsRegistry__factory(deployer).interface.encodeFunctionData(\n    'initialize',\n    [owner || deployer.address]\n  );\n\n  const deployed = await customDeployContract(\n    hre,\n    deployer,\n    deploymentName,\n    CONTRACT_NAME,\n    deploymentArgs,\n    { ...options, proxyData: initData }\n  );\n\n  await afterDeployment(hre, deployer, CONTRACT_NAME, deploymentArgs, deployed, options);\n\n  const attestationsRegistry = AttestationsRegistry__factory.connect(deployed.address, deployer);\n  return { attestationsRegistry };\n}\n\ntask('deploy-attestations-registry')\n  .addOptionalParam('owner', 'Admin of the attester register role. default to deployer')\n  .addOptionalParam('badges', 'Address of the badges contract')\n  .setAction(wrapCommonDeployOptions(deploymentAction));\n"
  },
  {
    "path": "tasks/deploy-tasks/unit/core/deploy-badges.task.ts",
    "content": "import { task } from 'hardhat/config';\nimport { HardhatRuntimeEnvironment } from 'hardhat/types';\nimport {\n  getDeployer,\n  beforeDeployment,\n  afterDeployment,\n  buildDeploymentName,\n  customDeployContract,\n  wrapCommonDeployOptions,\n  DeployOptions,\n} from '../../../../tasks/deploy-tasks/utils';\nimport { Badges, Badges__factory } from '../../../../types';\n\nexport interface DeployBadgesArgs {\n  // owner of the contract\n  uri?: string;\n  owner?: string;\n  // attester register role\n  options?: DeployOptions;\n}\n\nexport interface DeployedBadges {\n  badges: Badges;\n}\n\nconst CONTRACT_NAME = 'Badges';\n\nasync function deploymentAction(\n  { uri = '', owner, options }: DeployBadgesArgs,\n  hre: HardhatRuntimeEnvironment\n): Promise<DeployedBadges> {\n  const deployer = await getDeployer(hre);\n  const deploymentName = buildDeploymentName(CONTRACT_NAME, options?.deploymentNamePrefix);\n  const deploymentArgs = [uri, owner || deployer.address];\n\n  await beforeDeployment(hre, deployer, CONTRACT_NAME, deploymentArgs, options);\n\n  const initData = new Badges__factory(deployer).interface.encodeFunctionData('initialize', [\n    uri,\n    owner || deployer.address,\n  ]);\n\n  const deployed = await customDeployContract(\n    hre,\n    deployer,\n    deploymentName,\n    CONTRACT_NAME,\n    deploymentArgs,\n    {\n      ...options,\n      proxyData: initData,\n    }\n  );\n\n  await afterDeployment(hre, deployer, CONTRACT_NAME, deploymentArgs, deployed, options);\n\n  const badges = Badges__factory.connect(deployed.address, deployer);\n  return { badges };\n}\n\ntask('deploy-badges')\n  .addOptionalParam('uri', 'uri')\n  .addOptionalParam('owner', 'owner')\n  .setAction(wrapCommonDeployOptions(deploymentAction));\n"
  },
  {
    "path": "tasks/deploy-tasks/unit/core/deploy-front.task.ts",
    "content": "import { task } from 'hardhat/config';\nimport { HardhatRuntimeEnvironment } from 'hardhat/types';\nimport {\n  getDeployer,\n  beforeDeployment,\n  afterDeployment,\n  buildDeploymentName,\n  customDeployContract,\n  wrapCommonDeployOptions,\n  DeployOptions,\n} from '../../../../tasks/deploy-tasks/utils';\n\nimport { Front, Front__factory } from '../../../../types';\n\nexport interface DeployFrontArgs {\n  attestationsRegistryAddress: string;\n  // owner of the contract\n  // attester register role\n  options: DeployOptions;\n}\n\nexport interface DeployedFront {\n  front: Front;\n}\n\nconst CONTRACT_NAME = 'Front';\n\nasync function deploymentAction(\n  { attestationsRegistryAddress, options }: DeployFrontArgs,\n  hre: HardhatRuntimeEnvironment\n): Promise<DeployedFront> {\n  const deployer = await getDeployer(hre);\n  const deploymentName = buildDeploymentName(CONTRACT_NAME, options?.deploymentNamePrefix);\n  const deploymentArgs = [attestationsRegistryAddress];\n\n  await beforeDeployment(hre, deployer, CONTRACT_NAME, deploymentArgs, options);\n\n  const initData = '0x';\n\n  const deployed = await customDeployContract(\n    hre,\n    deployer,\n    deploymentName,\n    CONTRACT_NAME,\n    deploymentArgs,\n    {\n      ...options,\n      proxyData: initData,\n    }\n  );\n\n  await afterDeployment(hre, deployer, CONTRACT_NAME, deploymentArgs, deployed, options);\n\n  const front = Front__factory.connect(deployed.address, deployer);\n  return { front };\n}\n\ntask('deploy-front')\n  .addParam('attestationsRegistryAddress', 'Attestation Registry on which to read')\n  .setAction(wrapCommonDeployOptions(deploymentAction));\n"
  },
  {
    "path": "tasks/deploy-tasks/unit/core/deploy-sismo-addresses-provider.task.ts",
    "content": "import { singletonFactory } from '../../../../utils/singletonFactory';\nimport { task } from 'hardhat/config';\nimport { HardhatRuntimeEnvironment } from 'hardhat/types';\nimport {\n  getDeployer,\n  beforeDeployment,\n  afterDeployment,\n  buildDeploymentName,\n  customDeployContract,\n  wrapCommonDeployOptions,\n  DeployOptions,\n} from '../../utils';\n\nimport {\n  AddressesProvider,\n  AddressesProvider__factory,\n  TransparentUpgradeableProxy__factory,\n} from '../../../../types';\nimport { utils } from 'ethers';\nimport { deploymentsConfig } from '../../../../tasks/deploy-tasks/deployments-config';\nimport { confirm } from '../../../../tasks/utils';\n\nexport interface DeploySismoAddressesProvider {\n  owner: string;\n  badges: string;\n  attestationsRegistry: string;\n  front: string;\n  hydraS1AccountboundAttester: string;\n  availableRootsRegistry: string;\n  commitmentMapperRegistry: string;\n  hydraS1Verifier: string;\n  options?: DeployOptions;\n}\n\nexport interface DeployedSismoAddressesProvider {\n  sismoAddressesProvider: AddressesProvider;\n}\n\nconst CONTRACT_NAME = 'AddressesProvider';\n\nasync function deploymentAction(\n  {\n    owner,\n    badges,\n    attestationsRegistry,\n    front,\n    hydraS1AccountboundAttester,\n    availableRootsRegistry,\n    commitmentMapperRegistry,\n    hydraS1Verifier,\n    options,\n  }: DeploySismoAddressesProvider,\n  hre: HardhatRuntimeEnvironment\n): Promise<DeployedSismoAddressesProvider> {\n  const config = deploymentsConfig[hre.network.name];\n\n  const deployer = await getDeployer(hre);\n  const deploymentName = buildDeploymentName(CONTRACT_NAME, options?.deploymentNamePrefix);\n\n  const create2FactoryContract = '0xC4c11B14e9D876B031c1c7e05efE44088341f35B';\n  // Account 1 of the create2Factory mnemonic\n  const create2FactoryDeployer = '0xBa68986f673c9193BB79eA0d21990225d464bb5C';\n\n  // Account 0 of the create2Factory mnemonic\n  const proxyDeployer = '0x77694e7C30B74dd271EACA4207Ada0fC10632f5f';\n\n  // Deploy a proxy where the admin is the factoryDeployer\n  // The resulted proxy address is deterministic and is always\n  // 0x3340Ac0CaFB3ae34dDD53dba0d7344C1Cf3EFE05\n  const sismoAddressesProviderProxyAddress = '0x3340Ac0CaFB3ae34dDD53dba0d7344C1Cf3EFE05';\n\n  if (hre.network.config.chainId !== 31337 || process.env.FORK === 'true') {\n    const tx = {\n      to: create2FactoryDeployer,\n      value: utils.parseEther('0.1'),\n    };\n    if (options?.log) {\n      console.log(`Send 0.1 ETH to create2FactoryDeployer ${create2FactoryDeployer}`);\n    }\n    const sendFundTx = await deployer.sendTransaction(tx);\n    await sendFundTx.wait();\n    // The deployer should always be 0x77694e7c30b74dd271eaca4207ada0fc10632f5f\n    if (deployer.address !== proxyDeployer) {\n      throw new Error('The deployer should be 0x77694e7c30b74dd271eaca4207ada0fc10632f5f');\n    }\n\n    if (options?.log) {\n      console.log(`Deploy ${deploymentName} Proxy`);\n    }\n    const proxy = await hre.deployments.deploy(deploymentName + 'Proxy', {\n      contract:\n        'contracts/periphery/utils/TransparentUpgradeableProxy.sol:TransparentUpgradeableProxy',\n      from: deployer.address,\n      deterministicDeployment: true,\n      // note proxyData is the encoded call (i.e initialize(params))\n      args: [create2FactoryContract, proxyDeployer, '0x'],\n      log: true,\n    });\n    if (options?.log) {\n      console.log(`Proxy deployed on ${proxy.address}`);\n    }\n  } else {\n    // ONLY ON LOCAL NETWORK\n\n    // check if the proxy already exists\n    const code = await hre.network.provider.send('eth_getCode', [\n      config.sismoAddressesProvider.address,\n    ]);\n\n    if (code !== '0x') {\n      const AddressesProvider = await hre.ethers.getContractFactory('AddressesProvider');\n      const sismoAddressesProvider = AddressesProvider.attach(\n        config.sismoAddressesProvider.address\n      ) as AddressesProvider;\n\n      // if proxy already exists and the deployment is on a local network\n      // we need to set new addresses for the contracts used in the tests\n      await sismoAddressesProvider.setBatch(\n        [\n          badges,\n          attestationsRegistry,\n          front,\n          hydraS1AccountboundAttester,\n          availableRootsRegistry,\n          commitmentMapperRegistry,\n          hydraS1Verifier,\n        ],\n        [\n          'Badges',\n          'AttestationsRegistry',\n          'Front',\n          'HydraS1AccountboundAttester',\n          'AvailableRootsRegistry',\n          'CommitmentMapperRegistry',\n          'HydraS1Verifier',\n        ]\n      );\n\n      return { sismoAddressesProvider };\n    }\n\n    // Send 1Eth to the factory deployer (factoryDeployer)\n    // This is necessary for being able to deploy the create2 factory\n    const sendFundFactoryDeployerTx = await deployer.sendTransaction({\n      to: create2FactoryDeployer,\n      value: utils.parseEther('1'),\n    });\n    await sendFundFactoryDeployerTx.wait();\n    const sendFundProxyDeployerTx = await deployer.sendTransaction({\n      to: proxyDeployer,\n      value: utils.parseEther('1'),\n    });\n    await sendFundProxyDeployerTx.wait();\n\n    // Deploy the create2 factory with the factoryDeployer\n    // the create2 factory signed transaction is already saved\n    const tx1 = await hre.ethers.provider.send('eth_sendRawTransaction', [\n      singletonFactory[hre.network.config.chainId!].transaction,\n    ]);\n    await hre.ethers.provider.waitForTransaction(tx1);\n\n    // Create the proxy using the create2Factory and factoryDeployer as admin\n    // In local we don't have the private key so this is already forged\n    const createProxyTx = await hre.ethers.provider.send('eth_sendRawTransaction', [\n      '0xf90ff380846fc23ac0830c4cb594c4c11b14e9d876b031c1c7e05efe44088341f35b80b90f8b0000000000000000000000000000000000000000000000000000000000000000608060405260405162000eeb38038062000eeb8339810160408190526200002691620004ed565b828162000036828260006200009a565b5062000066905060017fb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6104620005cd565b60008051602062000ea483398151915214620000865762000086620005f3565b6200009182620000d7565b5050506200065c565b620000a58362000132565b600082511180620000b35750805b15620000d257620000d083836200017460201b6200022e1760201c565b505b505050565b7f7e644d79422f17c01e4894b5f4f588d331ebfa28653d42ae832dc59e38c9798f62000102620001a3565b604080516001600160a01b03928316815291841660208301520160405180910390a16200012f81620001dc565b50565b6200013d8162000291565b6040516001600160a01b038216907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b90600090a250565b60606200019c838360405180606001604052806027815260200162000ec46027913962000345565b9392505050565b6000620001cd60008051602062000ea483398151915260001b620003c460201b620001ea1760201c565b546001600160a01b0316919050565b6001600160a01b038116620002475760405162461bcd60e51b815260206004820152602660248201527f455243313936373a206e65772061646d696e20697320746865207a65726f206160448201526564647265737360d01b60648201526084015b60405180910390fd5b806200027060008051602062000ea483398151915260001b620003c460201b620001ea1760201c565b80546001600160a01b0319166001600160a01b039290921691909117905550565b620002a781620003c760201b6200025a1760201c565b6200030b5760405162461bcd60e51b815260206004820152602d60248201527f455243313936373a206e657720696d706c656d656e746174696f6e206973206e60448201526c1bdd08184818dbdb9d1c9858dd609a1b60648201526084016200023e565b80620002707f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc60001b620003c460201b620001ea1760201c565b6060600080856001600160a01b03168560405162000364919062000609565b600060405180830381855af49150503d8060008114620003a1576040519150601f19603f3d011682016040523d82523d6000602084013e620003a6565b606091505b509092509050620003ba86838387620003d6565b9695505050505050565b90565b6001600160a01b03163b151590565b606083156200044a57825160000362000442576001600160a01b0385163b620004425760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e747261637400000060448201526064016200023e565b508162000456565b6200045683836200045e565b949350505050565b8151156200046f5781518083602001fd5b8060405162461bcd60e51b81526004016200023e919062000627565b80516001600160a01b0381168114620004a357600080fd5b919050565b634e487b7160e01b600052604160045260246000fd5b60005b83811015620004db578181015183820152602001620004c1565b83811115620000d05750506000910152565b6000806000606084860312156200050357600080fd5b6200050e846200048b565b92506200051e602085016200048b565b60408501519092506001600160401b03808211156200053c57600080fd5b818601915086601f8301126200055157600080fd5b815181811115620005665762000566620004a8565b604051601f8201601f19908116603f01168101908382118183101715620005915762000591620004a8565b81604052828152896020848701011115620005ab57600080fd5b620005be836020830160208801620004be565b80955050505050509250925092565b600082821015620005ee57634e487b7160e01b600052601160045260246000fd5b500390565b634e487b7160e01b600052600160045260246000fd5b600082516200061d818460208701620004be565b9190910192915050565b602081526000825180602084015262000648816040850160208701620004be565b601f01601f19169190910160400192915050565b610838806200066c6000396000f3fe60806040526004361061004e5760003560e01c80633659cfe6146100655780634f1ef286146100855780635c60da1b146100985780638f283970146100c9578063f851a440146100e95761005d565b3661005d5761005b6100fe565b005b61005b6100fe565b34801561007157600080fd5b5061005b6100803660046106c2565b610118565b61005b6100933660046106dd565b610155565b3480156100a457600080fd5b506100ad6101bc565b6040516001600160a01b03909116815260200160405180910390f35b3480156100d557600080fd5b5061005b6100e43660046106c2565b6101ed565b3480156100f557600080fd5b506100ad61020d565b610106610269565b6101166101116102fe565b610308565b565b61012061032c565b6001600160a01b0316330361014d5761014a8160405180602001604052806000815250600061035f565b50565b61014a6100fe565b61015d61032c565b6001600160a01b031633036101b4576101af8383838080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152506001925061035f915050565b505050565b6101af6100fe565b60006101c661032c565b6001600160a01b031633036101e2576101dd6102fe565b905090565b6101ea6100fe565b90565b6101f561032c565b6001600160a01b0316330361014d5761014a8161038a565b600061021761032c565b6001600160a01b031633036101e2576101dd61032c565b606061025383836040518060600160405280602781526020016107dc602791396103de565b9392505050565b6001600160a01b03163b151590565b61027161032c565b6001600160a01b031633036101165760405162461bcd60e51b815260206004820152604260248201527f5472616e73706172656e745570677261646561626c6550726f78793a2061646d60448201527f696e2063616e6e6f742066616c6c6261636b20746f2070726f78792074617267606482015261195d60f21b608482015260a4015b60405180910390fd5b60006101dd610456565b3660008037600080366000845af43d6000803e808015610327573d6000f35b3d6000fd5b60007fb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d61035b546001600160a01b0316919050565b6103688361047e565b6000825111806103755750805b156101af57610384838361022e565b50505050565b7f7e644d79422f17c01e4894b5f4f588d331ebfa28653d42ae832dc59e38c9798f6103b361032c565b604080516001600160a01b03928316815291841660208301520160405180910390a161014a816104be565b6060600080856001600160a01b0316856040516103fb919061078c565b600060405180830381855af49150503d8060008114610436576040519150601f19603f3d011682016040523d82523d6000602084013e61043b565b606091505b509150915061044c86838387610567565b9695505050505050565b60007f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc610350565b610487816105e8565b6040516001600160a01b038216907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b90600090a250565b6001600160a01b0381166105235760405162461bcd60e51b815260206004820152602660248201527f455243313936373a206e65772061646d696e20697320746865207a65726f206160448201526564647265737360d01b60648201526084016102f5565b807fb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d61035b80546001600160a01b0319166001600160a01b039290921691909117905550565b606083156105d65782516000036105cf576001600160a01b0385163b6105cf5760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e747261637400000060448201526064016102f5565b50816105e0565b6105e0838361067c565b949350505050565b6001600160a01b0381163b6106555760405162461bcd60e51b815260206004820152602d60248201527f455243313936373a206e657720696d706c656d656e746174696f6e206973206e60448201526c1bdd08184818dbdb9d1c9858dd609a1b60648201526084016102f5565b807f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc610546565b81511561068c5781518083602001fd5b8060405162461bcd60e51b81526004016102f591906107a8565b80356001600160a01b03811681146106bd57600080fd5b919050565b6000602082840312156106d457600080fd5b610253826106a6565b6000806000604084860312156106f257600080fd5b6106fb846106a6565b9250602084013567ffffffffffffffff8082111561071857600080fd5b818601915086601f83011261072c57600080fd5b81358181111561073b57600080fd5b87602082850101111561074d57600080fd5b6020830194508093505050509250925092565b60005b8381101561077b578181015183820152602001610763565b838111156103845750506000910152565b6000825161079e818460208701610760565b9190910192915050565b60208152600082518060208401526107c7816040850160208701610760565b601f01601f1916919091016040019291505056fe416464726573733a206c6f772d6c6576656c2064656c65676174652063616c6c206661696c6564a2646970667358221220e2b2f80d01b9fee1887f086f33ea33eabe6a896123f3722976ea73e069c365b664736f6c634300080e0033b53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103416464726573733a206c6f772d6c6576656c2064656c65676174652063616c6c206661696c6564000000000000000000000000c4c11b14e9d876b031c1c7e05efe44088341f35b00000000000000000000000077694e7c30b74dd271eaca4207ada0fc10632f5f0000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000082f4f6a02cc50d6bf53ac45ef3769c7c43307281ba0c8eba9d30932c3d1592a64a9fd5c5a05e301a8150706eed5697741f289e388f312837b06cf6100cb03ccc069ae5402b',\n    ]);\n    await hre.ethers.provider.waitForTransaction(createProxyTx);\n\n    // Transfer proxy admin to the local deployer account\n    // 0xb01ee322C4f028B8A6BFcD2a5d48107dc5bC99EC\n    const tx2 = await hre.ethers.provider.send('eth_sendRawTransaction', [\n      '0xf88a01846946465583033601943340ac0cafb3ae34ddd53dba0d7344c1cf3efe0580a48f283970000000000000000000000000b01ee322c4f028b8a6bfcd2a5d48107dc5bc99ec82f4f5a0faf64c726bcd16ee5c13003720451c6fc9a734f2e70943dfcd629c0c94ea9b02a006ece90f1a45701f8a14ef6bf5d1b2515f450dbeca871490c6ae0ed01a91759c',\n    ]);\n    await hre.ethers.provider.waitForTransaction(tx2);\n  }\n\n  // always start by giving the ownership of the deployer\n  const deploymentArgs = [\n    badges,\n    attestationsRegistry,\n    front,\n    hydraS1AccountboundAttester,\n    availableRootsRegistry,\n    commitmentMapperRegistry,\n    hydraS1Verifier,\n    deployer.address,\n  ];\n\n  // Deploy the AddressesProvider implementation in local\n  await beforeDeployment(hre, deployer, CONTRACT_NAME, deploymentArgs, options);\n  const deployed = await customDeployContract(\n    hre,\n    deployer,\n    deploymentName,\n    CONTRACT_NAME,\n    deploymentArgs,\n    {\n      ...options,\n      behindProxy: false,\n    }\n  );\n  await afterDeployment(hre, deployer, CONTRACT_NAME, deploymentArgs, deployed, options);\n\n  // Save the deployment\n  await hre.deployments.save(`${deploymentName}Implem`, {\n    ...deployed,\n  });\n  await hre.deployments.save(deploymentName, {\n    ...deployed,\n    address: sismoAddressesProviderProxyAddress,\n  });\n\n  // Upgrade the proxy to use the deployed implementation\n  const sismoAddressesProviderProxy = await TransparentUpgradeableProxy__factory.connect(\n    sismoAddressesProviderProxyAddress,\n    deployer\n  );\n  if (options?.log) {\n    console.log(`Upgrade proxy to use the deployed implementation at ${deployed.address}`);\n    if (options?.manualConfirm) {\n      await confirm();\n    }\n  }\n  const initData = new AddressesProvider__factory(deployer).interface.encodeFunctionData(\n    'initialize',\n    [deployer.address]\n  );\n\n  const upgradeToTx = await sismoAddressesProviderProxy.upgradeToAndCall(\n    deployed.address,\n    initData\n  );\n  await upgradeToTx.wait();\n\n  // change proxy admin\n  if (options?.log) {\n    console.log(\n      `Change proxy admin (${\n        deployer.address\n      }) from the deployer to the expected one (${options?.proxyAdmin!}))`\n    );\n    if (options?.manualConfirm) {\n      await confirm();\n    }\n  }\n  const transfertAdminTx = await sismoAddressesProviderProxy.changeAdmin(options?.proxyAdmin!);\n  await transfertAdminTx.wait();\n\n  const sismoAddressesProvider = await AddressesProvider__factory.connect(\n    sismoAddressesProviderProxyAddress,\n    deployer\n  );\n\n  if (options?.log) {\n    console.log(\n      `Transfer AddressesProvider ownership (${\n        deployer.address\n      }) from the deployer to the expected one (${options?.proxyAdmin!})`\n    );\n    if (options?.manualConfirm) {\n      await confirm();\n    }\n  }\n  const transferOwnershipTx = await sismoAddressesProvider.transferOwnership(owner);\n  await transferOwnershipTx.wait();\n\n  return { sismoAddressesProvider };\n}\n\ntask('deploy-sismo-addresses-provider')\n  .addParam('owner', 'Address of the owner of the contracts registry')\n  .addParam('badges', 'Address of the badges contract')\n  .addParam('attestationsRegistry', 'Address of the attestationsRegistry contract')\n  .addParam('front', 'Address of the front contract')\n  .addParam('hydraS1AccountboundAttester', 'Address of the hydraS1AccountboundAttester contract')\n  .addParam('availableRootsRegistry', 'Address of the availableRootsRegistry contract')\n  .addParam('commitmentMapperRegistry', 'Address of the commitmentMapperRegistry contract')\n  .addParam('hydraS1Verifier', 'Address of the hydraS1Verifier contract')\n  .setAction(wrapCommonDeployOptions(deploymentAction));\n"
  },
  {
    "path": "tasks/deploy-tasks/unit/periphery/deploy-available-roots-registry.task.ts",
    "content": "import { task } from 'hardhat/config';\nimport { HardhatRuntimeEnvironment } from 'hardhat/types';\nimport {\n  getDeployer,\n  beforeDeployment,\n  afterDeployment,\n  buildDeploymentName,\n  customDeployContract,\n  wrapCommonDeployOptions,\n  DeployOptions,\n} from '../../../../tasks/deploy-tasks/utils';\n\nimport { AvailableRootsRegistry, AvailableRootsRegistry__factory } from '../../../../types';\n\nexport interface DeployAvailableRootsRegistry {\n  // owner of the contract\n  owner?: string;\n  options?: DeployOptions;\n}\n\nexport interface DeployedAvailableRootsRegistry {\n  availableRootsRegistry: AvailableRootsRegistry;\n}\n\nconst CONTRACT_NAME = 'AvailableRootsRegistry';\n\nasync function deploymentAction(\n  { owner, options }: DeployAvailableRootsRegistry,\n  hre: HardhatRuntimeEnvironment\n): Promise<DeployedAvailableRootsRegistry> {\n  const deployer = await getDeployer(hre);\n  const deploymentName = buildDeploymentName(CONTRACT_NAME, options?.deploymentNamePrefix);\n  const deploymentArgs = [owner || deployer.address];\n\n  await beforeDeployment(hre, deployer, CONTRACT_NAME, deploymentArgs, options);\n\n  const initData = new AvailableRootsRegistry__factory().interface.encodeFunctionData(\n    'initialize',\n    [owner || deployer.address]\n  );\n\n  const deployed = await customDeployContract(\n    hre,\n    deployer,\n    deploymentName,\n    CONTRACT_NAME,\n    deploymentArgs,\n    {\n      ...options,\n      proxyData: initData,\n    }\n  );\n\n  await afterDeployment(hre, deployer, CONTRACT_NAME, deploymentArgs, deployed, options);\n\n  const availableRootsRegistry = AvailableRootsRegistry__factory.connect(\n    deployed.address,\n    deployer\n  );\n  return { availableRootsRegistry };\n}\n\ntask('deploy-available-roots-registry')\n  .addOptionalParam('owner', 'Admin of the attester register role. default to deployer')\n  .setAction(wrapCommonDeployOptions(deploymentAction));\n"
  },
  {
    "path": "tasks/deploy-tasks/unit/periphery/deploy-commitment-mapper-registry.task.ts",
    "content": "import { task } from 'hardhat/config';\nimport { HardhatRuntimeEnvironment } from 'hardhat/types';\nimport {\n  getDeployer,\n  beforeDeployment,\n  afterDeployment,\n  buildDeploymentName,\n  customDeployContract,\n  wrapCommonDeployOptions,\n  DeployOptions,\n} from '../../../../tasks/deploy-tasks/utils';\n\nimport { CommitmentMapperRegistry, CommitmentMapperRegistry__factory } from '../../../../types';\n\nexport interface DeployCommitmentMapperArgs {\n  // owner of the contract\n  owner?: string;\n  commitmentMapperPubKeyX?: string;\n  commitmentMapperPubKeyY?: string;\n  commitmentMapperAddress?: string;\n  options?: DeployOptions;\n}\n\nexport interface DeployedCommitmentMapper {\n  commitmentMapperRegistry: CommitmentMapperRegistry;\n}\n\nconst CONTRACT_NAME = 'CommitmentMapperRegistry';\n\nasync function deploymentAction(\n  {\n    owner,\n    commitmentMapperPubKeyX,\n    commitmentMapperPubKeyY,\n    commitmentMapperAddress,\n    options,\n  }: DeployCommitmentMapperArgs,\n  hre: HardhatRuntimeEnvironment\n): Promise<DeployedCommitmentMapper> {\n  const deployer = await getDeployer(hre);\n  const deploymentName = buildDeploymentName(CONTRACT_NAME, options?.deploymentNamePrefix);\n  const deploymentArgs = [\n    owner || deployer.address,\n    [commitmentMapperPubKeyX, commitmentMapperPubKeyY],\n    commitmentMapperAddress || hre.ethers.constants.AddressZero,\n  ];\n\n  await beforeDeployment(hre, deployer, CONTRACT_NAME, deploymentArgs, options);\n\n  const initData = new CommitmentMapperRegistry__factory().interface.encodeFunctionData(\n    'initialize',\n    [\n      owner || deployer.address,\n      [commitmentMapperPubKeyX, commitmentMapperPubKeyY],\n      commitmentMapperAddress || hre.ethers.constants.AddressZero,\n    ]\n  );\n\n  const deployed = await customDeployContract(\n    hre,\n    deployer,\n    deploymentName,\n    CONTRACT_NAME,\n    deploymentArgs,\n    {\n      ...options,\n      proxyData: initData,\n    }\n  );\n\n  await afterDeployment(hre, deployer, CONTRACT_NAME, deploymentArgs, deployed, options);\n\n  const commitmentMapperRegistry = CommitmentMapperRegistry__factory.connect(\n    deployed.address,\n    deployer\n  );\n  return { commitmentMapperRegistry };\n}\n\ntask('deploy-commitment-mapper-registry')\n  .addOptionalParam('commitmentMapperPubKeyX', 'Eddsa public key coordinate x')\n  .addOptionalParam('commitmentMapperPubKeyY', 'Eddsa public key coordinate y')\n  .addOptionalParam('commitmentMapperAddress', 'ethereum address of commitment mapper')\n  .addOptionalParam('owner', 'Admin of the commitment mapper updater role. default to deployer')\n  .setAction(wrapCommonDeployOptions(deploymentAction));\n"
  },
  {
    "path": "tasks/deploy-tasks/unit/periphery/deploy-frontend-lib.task.ts",
    "content": "import { FrontendLib } from '../../../../types/FrontendLib';\nimport { task } from 'hardhat/config';\nimport { HardhatRuntimeEnvironment } from 'hardhat/types';\nimport {\n  getDeployer,\n  beforeDeployment,\n  afterDeployment,\n  buildDeploymentName,\n  customDeployContract,\n  wrapCommonDeployOptions,\n  DeployOptions,\n} from '../../utils';\n\nimport { FrontendLib__factory } from '../../../../types';\nimport { deploymentsConfig } from '../../../../tasks/deploy-tasks/deployments-config';\n\nexport interface DeployFrontendLib {\n  hydraS1AccountboundAttester?: string;\n  options?: DeployOptions;\n}\n\nexport interface DeployedFrontendLib {\n  frontendLib: FrontendLib;\n}\n\nconst CONTRACT_NAME = 'FrontendLib';\n\nasync function deploymentAction(\n  { hydraS1AccountboundAttester, options }: DeployFrontendLib,\n  hre: HardhatRuntimeEnvironment\n): Promise<DeployedFrontendLib> {\n  const deployer = await getDeployer(hre);\n  const deploymentName = buildDeploymentName(CONTRACT_NAME, options?.deploymentNamePrefix);\n  const config = deploymentsConfig[hre.network.name];\n\n  const deploymentArgs = [\n    hydraS1AccountboundAttester || config.hydraS1AccountboundAttester.address,\n  ];\n\n  await beforeDeployment(hre, deployer, CONTRACT_NAME, deploymentArgs, options);\n\n  const deployed = await customDeployContract(\n    hre,\n    deployer,\n    deploymentName,\n    CONTRACT_NAME,\n    deploymentArgs,\n    {\n      ...options,\n      behindProxy: false,\n    }\n  );\n\n  await afterDeployment(hre, deployer, CONTRACT_NAME, deploymentArgs, deployed, options);\n\n  const frontLib = FrontendLib__factory.connect(deployed.address, deployer);\n  return { frontendLib: frontLib };\n}\n\ntask('deploy-frontend-lib')\n  .addOptionalParam(\n    'hydraS1AccountboundAttester',\n    'address of the hydraS1AccountboundAttester contract'\n  )\n  .setAction(wrapCommonDeployOptions(deploymentAction));\n"
  },
  {
    "path": "tasks/deploy-tasks/utils/deployment.ts",
    "content": "import { HardhatRuntimeEnvironment } from 'hardhat/types';\nimport { SignerWithAddress } from '@nomiclabs/hardhat-ethers/dist/src/signer-with-address';\nimport { confirm } from '../../utils';\nimport { DeployResult } from 'hardhat-deploy/dist/types';\nimport { DeployOptions } from './';\nimport { deploymentsConfig } from '../deployments-config';\nimport { ethers } from 'ethers';\nconst accountNumber = Number(process.env.DEPLOYER_ACCOUNT) || 0;\n\nexport const getDeployer = async (hre: HardhatRuntimeEnvironment): Promise<SignerWithAddress> => {\n  const deployer =\n    accountNumber == 0\n      ? await SignerWithAddress.create(hre.ethers.provider.getSigner())\n      : (await hre.ethers.getSigners())[accountNumber];\n  return deployer;\n};\n\nexport const beforeDeployment = async (\n  hre: HardhatRuntimeEnvironment,\n  deployer: SignerWithAddress,\n  contractName: string,\n  args: any[],\n  options?: DeployOptions\n): Promise<void> => {\n  const feeData = await hre.ethers.provider.getFeeData();\n  const maxFeePerGas = feeData.maxFeePerGas\n    ? hre.ethers.utils.formatUnits(feeData.maxFeePerGas, 'gwei')\n    : 'unknown';\n  const maxPriorityFeePerGas = feeData.maxPriorityFeePerGas\n    ? hre.ethers.utils.formatUnits(feeData.maxPriorityFeePerGas, 'gwei')\n    : 'unknown';\n  if (options?.log || options?.manualConfirm) {\n    console.log(`\n    ************************************\n    Deploying: ${contractName}\n    * Deployer *************************\n    Address: ${deployer.address}\n    Balance: ${hre.ethers.utils.formatUnits(await deployer.getBalance(), 'ether')} eth\n    * Chain ****************************\n    chainId: ${await hre.getChainId()} \n    network: ${hre.network.name},\n    maxFeePerGas: ${maxFeePerGas} gwei (maximum price per unit of gas, includes base fee and priority fee),\n    maxPriorityFeePerGas: ${maxPriorityFeePerGas} gwei (maximum priority fee per unit of gas)\n    * ConstructorArguments *************\n    ${args.map(\n      (arg, k) => `\n    Argument ${k}: ${arg}`\n    )}\n    `);\n  }\n  if (options?.manualConfirm) {\n    await confirm();\n  }\n};\n\nexport const afterDeployment = async (\n  hre: HardhatRuntimeEnvironment,\n  deployer: SignerWithAddress,\n  contractName: string,\n  args: any[],\n  deployed: DeployResult,\n  options?: DeployOptions\n): Promise<void> => {\n  if (options?.log || options?.manualConfirm) {\n    console.log(`\n    ************************************\n    Deployed: ${contractName} at ${deployed.address}\n    * Deployment Receipt ***************\n    Receipt: ${Object.keys(deployed.receipt || ({} as Object)).map(\n      (key) => `\n    ${key}: ${deployed.receipt?.[key]}`\n    )}\n    * Deployer *************************\n    Address: ${deployer.address}\n    Balance: ${hre.ethers.utils.formatUnits(await deployer.getBalance(), 'ether')} eth\n    * Chain ****************************\n    chainId: ${await hre.getChainId()} \n    network: ${hre.network.name},\n    * ConstructorArguments *************\n    ${args.map(\n      (arg, k) => `\n    Argument ${k}: ${arg}`\n    )}\n    `);\n  }\n};\n\nexport const customDeployContract = async (\n  hre: HardhatRuntimeEnvironment,\n  deployer: SignerWithAddress,\n  deploymentName: string,\n  contractName: string,\n  args: any[],\n  options?: DeployOptions\n): Promise<DeployResult> => {\n  if (options?.log) {\n    console.log(`\n    * Deploying ${options?.behindProxy ? 'behind' : 'without'} proxy ***********\n    `);\n  }\n\n  const finalDeploymentName = options?.behindProxy ? deploymentName + 'Implem' : deploymentName;\n  const deploymentOptions = {\n    contract: contractName,\n    from: deployer.address,\n    args,\n    // If deployment name is already used, it means we are upgrading the implementation\n    skipIfAlreadyDeployed: false,\n    deterministicDeployment: options?.deterministicDeployment || false,\n  };\n  const deploymentDifference = await hre.deployments.fetchIfDifferent(\n    finalDeploymentName,\n    deploymentOptions\n  );\n  if (options?.isImplementationUpgrade && deploymentDifference.differences) {\n    if (options?.log) {\n      console.log(`\n      ** Deploying new implementation for ${deploymentName} ***********\n      `);\n    }\n    if (options.manualConfirm) {\n      await confirm();\n    }\n  }\n  const deployed = await hre.deployments.deploy(finalDeploymentName, deploymentOptions);\n  if (!options?.behindProxy) {\n    return deployed;\n  } else if (options?.isImplementationUpgrade) {\n    const proxyAddress = options.proxyAddress;\n    if (!proxyAddress) {\n      throw new Error('proxyAddress should be defined when upgrading a proxy!');\n    }\n    await hre.deployments.save(deploymentName, { ...deployed, address: proxyAddress });\n    if (options?.log) {\n      console.log(`\n      * Implementation deployed with address: ${deployed.address} ***********\n      `);\n    }\n    if (deploymentDifference.differences) {\n      await hre.run('upgrade-proxy', {\n        proxyAddress,\n        proxyData: options.proxyData,\n        newImplementationAddress: deployed.address,\n        specificProxyAdmin: options.proxyAdmin,\n        options,\n      });\n    }\n    return {\n      ...deployed,\n      address: proxyAddress,\n    };\n  } else {\n    const proxy = await hre.deployments.deploy(deploymentName + 'Proxy', {\n      contract:\n        'contracts/periphery/utils/TransparentUpgradeableProxy.sol:TransparentUpgradeableProxy',\n      from: deployer.address,\n      deterministicDeployment: options?.deterministicDeployment || false,\n      // note proxyData is the encoded call (i.e initialize(params))\n      args: [deployed.address, options?.proxyAdmin, options?.proxyData],\n    });\n    const implem = deployed;\n    await hre.deployments.save(deploymentName, { ...implem, address: proxy.address });\n    if (options?.log) {\n      console.log(`\n      * Deploying behind proxy ***********\n      Proxy Address: ${proxy.address}\n      Implementation Address: ${deployed.address}\n      `);\n    }\n    return proxy;\n  }\n};\n\nexport const wrapCommonDeployOptions = (action: Function) => {\n  return (args: any, hre: HardhatRuntimeEnvironment) => {\n    const config = deploymentsConfig[hre.network.name];\n    return action(\n      {\n        ...args,\n        options: {\n          ...config.deployOptions,\n          ...args.options,\n        },\n      },\n      hre\n    );\n  };\n};\n\nexport const buildDeploymentName = (contractName: string, prefix?: string) =>\n  prefix ? `${prefix}_${contractName}` : contractName;\n"
  },
  {
    "path": "tasks/deploy-tasks/utils/deployments-config-types.ts",
    "content": "import { CommonTaskOptions } from '../../utils';\n\nexport interface DeployOptions extends CommonTaskOptions {\n  // prefix for the contract name\n  deploymentNamePrefix?: string;\n  // deploy with proxy?\n  behindProxy?: boolean;\n  // Proxy data is the encoded call of the initialize(params) function\n  proxyData?: string;\n  // admin of the proxy\n  proxyAdmin?: string;\n  // set to true if the deployment is an upgrade of an existing proxy\n  isImplementationUpgrade?: boolean;\n  // proxy address, required in case of implementation upgrade\n  proxyAddress?: string;\n  // deterministic deployment\n  deterministicDeployment?: boolean;\n}\n\nexport type DeploymentsConfigTypes = {\n  [chain: string]: {\n    // Conf related to the deployment (behind proxy, etc.)\n    deployOptions: DeployOptions;\n    // Conf related to the hydraS1AccountboundAttester\n    hydraS1Verifier: {\n      address: string;\n    };\n    hydraS1AccountboundAttester: {\n      address: string;\n      collectionIdFirst: string;\n      collectionIdLast: string;\n      initialRoot: string;\n      owner: string;\n    };\n    hydraS1SimpleAttester: {\n      enableDeployment: boolean;\n      address: string;\n      collectionIdFirst: string;\n      collectionIdLast: string;\n      initialRoot: string;\n    };\n    pythia1Verifier: {\n      address: string;\n    };\n    synapsPythia1SimpleAttester: {\n      address: string;\n      collectionIdFirst: string;\n      collectionIdLast: string;\n      commitmentSignerPubKeyX: string;\n      commitmentSignerPubKeyY: string;\n      owner: string;\n    };\n    // Conf related to the commitment mapper\n    // https://github.com/sismo-core/sismo-commitment-mapper\n    commitmentMapper: {\n      address: string;\n      owner: string;\n      EdDSAPubKeyX: string;\n      EdDSAPubKeyY: string;\n    };\n    badges: {\n      address: string;\n      owner: string;\n      uri: string;\n    };\n    // conf related to the roots Registry to store\n    // all the merkleRoots\n    availableRootsRegistry: {\n      address: string;\n      owner: string;\n    };\n    attestationsRegistry: {\n      address: string;\n      owner: string;\n    };\n    front: {\n      address: string;\n      collectionIdFirst: string;\n      collectionIdLast: string;\n    };\n    sismoAddressesProvider: {\n      address: string;\n      owner: string;\n    };\n  };\n};\n"
  },
  {
    "path": "tasks/deploy-tasks/utils/index.ts",
    "content": "export * from './deployment';\nexport * from './deployments-config-types';\n"
  },
  {
    "path": "tasks/deploy-tasks/zkdrop/deploy-mergooor-pass.task.ts",
    "content": "import { task } from 'hardhat/config';\nimport { HardhatRuntimeEnvironment } from 'hardhat/types';\nimport { DeployOptions } from '../utils';\nimport { deploymentsConfig } from '../deployments-config';\nimport { ZKBadgeboundERC721 } from 'types';\nimport { DeployedZkBadgeboundERC721 } from 'tasks/deploy-tasks/tests/deploy-zk-badgebound-erc721.task';\n\nexport interface DeployedMergooorPass {\n  zkBadgeboundERC721: ZKBadgeboundERC721;\n}\n\nasync function deploymentAction(\n  { options }: { options: DeployOptions },\n  hre: HardhatRuntimeEnvironment\n): Promise<DeployedMergooorPass> {\n  const config = deploymentsConfig[process.env.FORK_NETWORK ?? hre.network.name];\n  options = { ...config.deployOptions, ...options };\n\n  if (options.manualConfirm || options.log) {\n    console.log('deploy-mergooor-pass: ', hre.network.name);\n  }\n\n  // Deploy SismoAddressesProvider\n  const { zkBadgeboundERC721 } = (await hre.run('deploy-zk-badgebound-erc721', {\n    name: 'Mergooor Pass',\n    symbol: 'MP',\n    tokenURI: 'ipfs://QmPR9q3Q5fByxzfMfRp32azvH2UPXhPoDFhHWAsiGNHBwS/',\n    gatingBadgeTokenId: '10000040',\n    admin: '0xaee4acd5c4Bf516330ca8fe11B07206fC6709294', // Sismo owner\n    deploymentName: 'MergooorPass',\n    options,\n  })) as DeployedZkBadgeboundERC721;\n\n  return {\n    zkBadgeboundERC721,\n  };\n}\n\ntask('deploy-mergooor-pass').setAction(deploymentAction);\n"
  },
  {
    "path": "tasks/deploy-tasks/zkdrop/deploy-ziki-pass-staging.task.ts",
    "content": "import { task } from 'hardhat/config';\nimport { HardhatRuntimeEnvironment } from 'hardhat/types';\nimport { DeployOptions } from '../utils';\nimport { deploymentsConfig } from '../deployments-config';\nimport { ZKBadgeboundERC721 } from 'types';\nimport { DeployedZkBadgeboundERC721 } from 'tasks/deploy-tasks/tests/deploy-zk-badgebound-erc721.task';\n\nexport interface DeployedZikiPass {\n  zkBadgeboundERC721: ZKBadgeboundERC721;\n}\n\nasync function deploymentAction(\n  { options }: { options: DeployOptions },\n  hre: HardhatRuntimeEnvironment\n): Promise<DeployedZikiPass> {\n  const config = deploymentsConfig[process.env.FORK_NETWORK ?? hre.network.name];\n  options = { ...config.deployOptions, ...options };\n\n  if (options.manualConfirm || options.log) {\n    console.log('deploy-ziki-pass-staging: ', hre.network.name);\n  }\n\n  // Deploy SismoAddressesProvider\n  const { zkBadgeboundERC721 } = (await hre.run('deploy-zk-badgebound-erc721', {\n    name: 'Ziki Pass',\n    symbol: 'ZKP',\n    tokenURI: 'ipfs://Qme1WfqhZ4dUVSKHE9NqH1z7MXFtviPY6QEPwu8TcgAyjc/',\n    gatingBadgeTokenId: '10000515',\n    admin: '0xf61cabba1e6fc166a66bca0fcaa83762edb6d4bd', // leo21.eth\n    deploymentName: 'ZikiPass',\n    options,\n  })) as DeployedZkBadgeboundERC721;\n\n  return {\n    zkBadgeboundERC721,\n  };\n}\n\ntask('deploy-ziki-pass-staging').setAction(deploymentAction);\n"
  },
  {
    "path": "tasks/deploy-tasks/zkdrop/deploy-ziki-pass-testnet.task.ts",
    "content": "import { task } from 'hardhat/config';\nimport { HardhatRuntimeEnvironment } from 'hardhat/types';\nimport { DeployOptions } from '../utils';\nimport { deploymentsConfig } from '../deployments-config';\nimport { ZKBadgeboundERC721 } from 'types';\nimport { DeployedZkBadgeboundERC721 } from 'tasks/deploy-tasks/tests/deploy-zk-badgebound-erc721.task';\n\nexport interface DeployedZikiPass {\n  zkBadgeboundERC721: ZKBadgeboundERC721;\n}\n\nasync function deploymentAction(\n  { options }: { options: DeployOptions },\n  hre: HardhatRuntimeEnvironment\n): Promise<DeployedZikiPass> {\n  const config = deploymentsConfig[process.env.FORK_NETWORK ?? hre.network.name];\n  options = { ...config.deployOptions, ...options };\n\n  if (options.manualConfirm || options.log) {\n    console.log('deploy-ziki-pass-testnet: ', hre.network.name);\n  }\n\n  // Deploy SismoAddressesProvider\n  const { zkBadgeboundERC721 } = (await hre.run('deploy-zk-badgebound-erc721', {\n    name: 'Ziki Pass',\n    symbol: 'ZKP',\n    tokenURI: 'https://metadata-zikies.zkdrop.io/ziki-pass/',\n    gatingBadgeTokenId: '10000515',\n    admin: '0xf61cabba1e6fc166a66bca0fcaa83762edb6d4bd', // leo21.eth\n    deploymentName: 'ZikiPass',\n    options,\n  })) as DeployedZkBadgeboundERC721;\n\n  return {\n    zkBadgeboundERC721,\n  };\n}\n\ntask('deploy-ziki-pass-testnet').setAction(deploymentAction);\n"
  },
  {
    "path": "tasks/helpers/authorizations/access-control-grant-role.task.ts",
    "content": "import { task } from 'hardhat/config';\nimport { HardhatRuntimeEnvironment } from 'hardhat/types';\nimport { AccessControl__factory } from '../../../types';\nimport { CommonTaskOptions } from '../../utils';\nimport { confirm } from '../../utils/confirm';\n\nexport type AccessControlGrantRoleArgs = {\n  contractAddress: string;\n  role: string;\n  accountAddress: string;\n  options?: CommonTaskOptions;\n};\n\nasync function grantRole(\n  { contractAddress, role, accountAddress, options }: AccessControlGrantRoleArgs,\n  hre: HardhatRuntimeEnvironment\n): Promise<void> {\n  const [signer] = await hre.ethers.getSigners();\n\n  if (options?.log || options?.manualConfirm) {\n    console.log(`\n    ************************************\n    *            GRANT ROLE            *\n    ************************************`);\n  }\n\n  const accessControlContract = AccessControl__factory.connect(contractAddress, signer);\n\n  const accountAlreadyHaveRole = await accessControlContract.hasRole(role, accountAddress);\n\n  // Verify if the account already have the good role\n  if (accountAlreadyHaveRole) {\n    if (options?.log || options?.manualConfirm) {\n      console.log(`\n    * ${accountAddress} already has role ${role} on contract ${contractAddress}\n      `);\n    }\n    return;\n  }\n\n  // Verify signer can give the role\n  const signerAddressIsAdminRole = await accessControlContract.hasRole(\n    await accessControlContract.DEFAULT_ADMIN_ROLE(),\n    signer.address\n  );\n\n  if (!signerAddressIsAdminRole) {\n    throw new Error(\n      `The current signer (${signer.address}) is not admin of the current contract ${contractAddress}. Can't grant role ${role}`\n    );\n  }\n\n  const actionGrantRole = {\n    role: role,\n    toAccount: accountAddress,\n    contract: contractAddress,\n  };\n  // Grant role section\n  if (options?.log || options?.manualConfirm) {\n    console.log(`    \n    ${Object.keys(actionGrantRole as Object).map(\n      (key) => `\n    ${key}: ${actionGrantRole?.[key]}`\n    )}`);\n  }\n  if (options?.manualConfirm) {\n    console.log();\n    await confirm();\n  }\n  const tx = await accessControlContract.grantRole(actionGrantRole.role, actionGrantRole.toAccount);\n  await tx.wait();\n\n  if (options?.log || options?.manualConfirm) {\n    console.log(`\n    * Role Granted !\n    `);\n  }\n}\n\ntask('access-control-grant-role')\n  .addParam('contractAddress', 'Contract concerned by the grantRole call')\n  .addParam('role', 'Role to grant')\n  .addParam('accountAddress', 'Account to receive the role')\n  .setAction(grantRole);\n"
  },
  {
    "path": "tasks/helpers/authorizations/access-control-revoke-role.task.ts",
    "content": "import { task } from 'hardhat/config';\nimport { HardhatRuntimeEnvironment } from 'hardhat/types';\nimport { AccessControl__factory } from '../../../types';\nimport { CommonTaskOptions } from '../../utils';\nimport { confirm } from '../../utils/confirm';\n\nexport type AccessControlRevokeRoleArgs = {\n  contractAddress: string;\n  role: string;\n  accountAddress: string;\n  options?: CommonTaskOptions;\n};\n\nasync function revokeRole(\n  { contractAddress, role, accountAddress, options }: AccessControlRevokeRoleArgs,\n  hre: HardhatRuntimeEnvironment\n): Promise<void> {\n  const [signer] = await hre.ethers.getSigners();\n\n  if (options?.log || options?.manualConfirm) {\n    console.log(`\n    ************************************\n    *            REVOKE ROLE           *\n    ************************************`);\n  }\n\n  const accessControlContract = AccessControl__factory.connect(contractAddress, signer);\n\n  const accountAlreadyHaveRole = await accessControlContract.hasRole(role, accountAddress);\n\n  // Verify if the account already have the good role\n  if (!accountAlreadyHaveRole) {\n    if (options?.log || options?.manualConfirm) {\n      console.log(`\n    * ${accountAddress} don't have role ${role} on contract ${contractAddress}. Exiting\n      `);\n    }\n    return;\n  }\n\n  // Verify signer can revoke the role\n  const signerAddressIsAdminRole = await accessControlContract.hasRole(\n    await accessControlContract.DEFAULT_ADMIN_ROLE(),\n    signer.address\n  );\n\n  if (!signerAddressIsAdminRole) {\n    throw new Error(\n      `The current signer (${signer.address}) is not admin of the current contract ${contractAddress}. Can't revoke role ${role}`\n    );\n  }\n\n  const actionRevokeRole = {\n    role: role,\n    toAccount: accountAddress,\n    contract: contractAddress,\n  };\n  // Revoke role section\n  if (options?.log || options?.manualConfirm) {\n    console.log(`    \n    ${Object.keys(actionRevokeRole as Object).map(\n      (key) => `\n    ${key}: ${actionRevokeRole?.[key]}`\n    )}`);\n  }\n  if (options?.manualConfirm) {\n    console.log();\n    await confirm();\n  }\n  const tx = await accessControlContract.revokeRole(\n    actionRevokeRole.role,\n    actionRevokeRole.toAccount\n  );\n  await tx.wait();\n\n  if (options?.log || options?.manualConfirm) {\n    console.log(`\n    * Role Revoked !\n    `);\n  }\n}\n\ntask('access-control-revoke-role')\n  .addParam('contractAddress', 'Contract concerned by the revokeRole call')\n  .addParam('role', 'Role to revoke')\n  .addParam('accountAddress', 'Account to receive the role')\n  .setAction(revokeRole);\n"
  },
  {
    "path": "tasks/helpers/authorizations/attestations-registry-authorize-range.task.ts",
    "content": "import { ethers } from 'ethers';\nimport { task } from 'hardhat/config';\nimport { HardhatRuntimeEnvironment } from 'hardhat/types';\nimport { AttestationsRegistry__factory } from '../../../types';\nimport { CommonTaskOptions } from '../../utils';\nimport { confirm } from '../../utils/confirm';\n\nexport type AuthorizeRangeArgs = {\n  attestationsRegistryAddress: string;\n  attesterAddress: string;\n  collectionIdFirst: string;\n  collectionIdLast: string;\n  options?: CommonTaskOptions;\n};\n\nasync function authorizeRange(\n  {\n    attestationsRegistryAddress,\n    attesterAddress,\n    collectionIdFirst,\n    collectionIdLast,\n    options,\n  }: AuthorizeRangeArgs,\n  hre: HardhatRuntimeEnvironment\n): Promise<void> {\n  const [signer] = await hre.ethers.getSigners();\n\n  if (options?.log || options?.manualConfirm) {\n    console.log(`\n    ************************************\n    *       AUTHORIZE ATTESTER         *\n    ************************************`);\n  }\n\n  const attestationsRegistry = AttestationsRegistry__factory.connect(\n    attestationsRegistryAddress,\n    signer\n  );\n\n  // Verify if attester is already authorized in the AttestationsRegistry\n  const isFirstRangeAuthorized = await attestationsRegistry.isAuthorized(\n    attesterAddress,\n    collectionIdFirst\n  );\n  const isLastRangeAuthorized = await attestationsRegistry.isAuthorized(\n    attesterAddress,\n    collectionIdLast\n  );\n  if (isFirstRangeAuthorized && isLastRangeAuthorized) {\n    if (options?.log || options?.manualConfirm) {\n      console.log(\n        `Range (${collectionIdFirst}, ${collectionIdLast}) for ${attesterAddress} seems already authorized.`\n      );\n    }\n    return;\n  }\n\n  const currentOwner = await attestationsRegistry.owner();\n\n  // Authorizing the collectionIdFirst and collectionIdLast for the attester\n  const actionAuthorizeRange = {\n    attester: attesterAddress,\n    collectionIdFirst,\n    collectionIdLast,\n    attestationsRegistry: attestationsRegistry.address,\n    currentOwner,\n  };\n  if (options?.log || options?.manualConfirm) {\n    console.log(`    \n      ${Object.keys(actionAuthorizeRange as Object).map(\n        (key) => `\n      ${key}: ${actionAuthorizeRange?.[key]}`\n      )}`);\n  }\n\n  // Verify if the current signer is the owner of the attestationsRegistry\n  if (\n    signer.address.toLowerCase() !== currentOwner.toLowerCase() &&\n    process.env.OWNER_MANUAL_OPERATION !== 'true'\n  ) {\n    throw new Error(\n      `The current signer (${signer.address}) is not owner of the attestations registry contract ${attestationsRegistry.address}. Can't authorize attester.`\n    );\n  } else if (process.env.OWNER_MANUAL_OPERATION === 'true') {\n    if (options?.manualConfirm) {\n      const iface = new ethers.utils.Interface(AttestationsRegistry__factory.abi);\n      const data = iface.encodeFunctionData('authorizeRange', [\n        actionAuthorizeRange.attester,\n        actionAuthorizeRange.collectionIdFirst,\n        actionAuthorizeRange.collectionIdLast,\n      ]);\n      console.log({\n        from: currentOwner,\n        to: attestationsRegistry.address,\n        data: data,\n      });\n      console.log('Send the transaction using etherscan !');\n      await confirm();\n    }\n  } else {\n    if (options?.manualConfirm) {\n      console.log();\n      await confirm();\n    }\n\n    const tx = await attestationsRegistry.authorizeRange(\n      actionAuthorizeRange.attester,\n      actionAuthorizeRange.collectionIdFirst,\n      actionAuthorizeRange.collectionIdLast\n    );\n    await tx.wait();\n\n    if (options?.log || options?.manualConfirm) {\n      console.log(`\n      * Attester well authorized !\n      `);\n    }\n  }\n}\n\ntask('attestations-registry-authorize-range')\n  .addParam('attestationsRegistryAddress', 'Address of the attestations registry')\n  .addParam('attesterAddress', 'Address of the attester')\n  .addParam(\n    'collectionIdFirst',\n    'collectionId min authorized by the attestations registry to the attester'\n  )\n  .addParam(\n    'collectionIdLast',\n    'collectionId max authorized by the attestations registry to the attester'\n  )\n  .setAction(authorizeRange);\n"
  },
  {
    "path": "tasks/helpers/authorizations/attestations-registry-transfer-ownership.task.ts",
    "content": "import { task } from 'hardhat/config';\nimport { HardhatRuntimeEnvironment } from 'hardhat/types';\nimport { deploymentsConfig } from '../../deploy-tasks/deployments-config';\nimport { OwnableTransferOwnershipArgs } from './ownable-transfer-ownership.task';\nimport { CommonTaskOptions, getCommonOptions } from '../../utils';\n\nexport type AuthorizeRangeArgs = {\n  options?: CommonTaskOptions;\n};\n\nasync function authorizeRange(\n  { options }: AuthorizeRangeArgs,\n  hre: HardhatRuntimeEnvironment\n): Promise<void> {\n  const config = deploymentsConfig[hre.network.name];\n  options = { ...config.deployOptions, ...options };\n\n  if (options.manualConfirm || options.log) {\n    console.log(`\n      ----------------------------------------------------------------\n      * Transfer AttestationsRegistry ownership`);\n  }\n  await hre.run('ownable-transfer-ownership', {\n    contractAddress: (await hre.deployments.all()).AttestationsRegistry.address,\n    newOwner: config.attestationsRegistry.owner,\n    options: getCommonOptions(options),\n  } as OwnableTransferOwnershipArgs);\n}\n\ntask('transfer-attestations-registry-ownership').setAction(authorizeRange);\n"
  },
  {
    "path": "tasks/helpers/authorizations/change-proxy-admin.task.ts",
    "content": "import { task } from 'hardhat/config';\nimport { HardhatRuntimeEnvironment } from 'hardhat/types';\nimport { TransparentUpgradeableProxy__factory } from '../../../types';\nimport { CommonTaskOptions, wrapCommonOptions } from '../../utils';\nimport { confirm } from '../../utils/confirm';\nimport { deploymentsConfig } from '../../deploy-tasks/deployments-config';\n\nexport type ChangeProxyAdminArgs = {\n  proxyAddress: string;\n  newAdmin: string;\n  options?: CommonTaskOptions;\n};\n\nasync function action(\n  { proxyAddress, newAdmin, options }: ChangeProxyAdminArgs,\n  hre: HardhatRuntimeEnvironment\n): Promise<void> {\n  const [signer] = await hre.ethers.getSigners();\n\n  const config = deploymentsConfig[process.env.FORK_NETWORK ?? hre.network.name];\n\n  const proxy = TransparentUpgradeableProxy__factory.connect(proxyAddress, signer);\n\n  if (process.env.CHANGE_PROXY_ADMIN_MANUAL_OPERATION === 'true') {\n    console.log(`\n    ************************************\n    *       CHANGE PROXY ADMIN         *\n    ************************************`);\n\n    if (options?.manualConfirm) {\n      await confirm();\n    }\n\n    // we can't connect as signer, we need manual operation\n    const iface = new hre.ethers.utils.Interface(TransparentUpgradeableProxy__factory.abi);\n    const data = iface.encodeFunctionData('changeAdmin', [newAdmin]);\n\n    console.log({\n      from: signer.address,\n      to: proxy.address,\n      data: data,\n    });\n\n    console.log('Send the transaction using etherscan !');\n  } else {\n    console.log(`\n    Aborting changeProxyAdmin...`);\n  }\n}\n\ntask('change-proxy-admin')\n  .addParam('proxyAddress', \"Address of the proxy we want to change it's admin\")\n  .addParam('newAdmin', 'Address of the new admin')\n  .setAction(wrapCommonOptions(action));\n"
  },
  {
    "path": "tasks/helpers/authorizations/ownable-transfer-ownership.task.ts",
    "content": "import { task } from 'hardhat/config';\nimport { HardhatRuntimeEnvironment } from 'hardhat/types';\nimport { Ownable__factory } from '../../../types/factories/Ownable__factory';\nimport { CommonTaskOptions } from '../../utils';\nimport { confirm } from '../../utils/confirm';\n\nexport type OwnableTransferOwnershipArgs = {\n  contractAddress: string;\n  newOwner: string;\n  options?: CommonTaskOptions;\n};\n\nasync function ownableTransferOwnership(\n  { contractAddress, newOwner, options }: OwnableTransferOwnershipArgs,\n  hre: HardhatRuntimeEnvironment\n): Promise<void> {\n  const [signer] = await hre.ethers.getSigners();\n\n  if (options?.log || options?.manualConfirm) {\n    console.log(`\n    ************************************\n    *       TRANSFER OWNERSHIP         *\n    ************************************`);\n  }\n\n  const ownableContract = Ownable__factory.connect(contractAddress, signer);\n  const currentOwner = await ownableContract.owner();\n\n  // verify is the newOwner il already owner of the contract\n  if (currentOwner.toLowerCase() === newOwner.toLowerCase()) {\n    if (options?.log || options?.manualConfirm) {\n      console.log(`\n    * CurrentOwner(${currentOwner}) is already the targetted owner. Nothing to do, exiting.\n    `);\n    }\n    return;\n  }\n\n  // verify the signer has the rights to transfer ownership of the current contract\n  if (\n    signer.address.toLowerCase() !== currentOwner.toLowerCase() &&\n    process.env.OWNER_MANUAL_OPERATION !== 'true'\n  ) {\n    throw new Error(\n      `The current signer (${signer.address}) is not owner of the current contract ${ownableContract.address}. Can't transfer ownership`\n    );\n  }\n\n  // transfering ownership section\n  const actionTransferOwnership = {\n    currentOwner,\n    newOwner,\n    contract: ownableContract.address,\n  };\n  if (options?.log || options?.manualConfirm) {\n    console.log(`   \n    ${Object.keys(actionTransferOwnership as Object).map(\n      (key) => `\n    ${key}: ${actionTransferOwnership?.[key]}`\n    )}`);\n  }\n  if (options?.manualConfirm) {\n    console.log();\n    await confirm();\n  }\n\n  if (process.env.OWNER_MANUAL_OPERATION === 'true') {\n    // we can't connect as signer, we need manual operation\n    const iface = new hre.ethers.utils.Interface(Ownable__factory.abi);\n    const data = iface.encodeFunctionData('transferOwnership', [actionTransferOwnership.newOwner]);\n\n    console.log({\n      from: currentOwner.toLowerCase(),\n      to: contractAddress,\n      data: data,\n    });\n  } else {\n    const tx = await ownableContract.transferOwnership(actionTransferOwnership.newOwner);\n    await tx.wait();\n\n    if (options?.log || options?.manualConfirm) {\n      console.log(`\n    * Ownership transferred !\n    `);\n    }\n  }\n}\n\ntask('ownable-transfer-ownership')\n  .addParam('contractAddress', 'Ownable contract address to transfer ownership')\n  .addParam('newOwner', 'New owner to transfer ownership')\n  .setAction(ownableTransferOwnership);\n"
  },
  {
    "path": "tasks/helpers/forge-create2-transaction.task.ts",
    "content": "import { task } from 'hardhat/config';\nimport { HardhatRuntimeEnvironment } from 'hardhat/types';\n\nasync function printAccounts({}, hre: HardhatRuntimeEnvironment): Promise<void> {\n  (await hre.ethers.getSigners()).map((signer, index) =>\n    console.log(`\n    Account #${index}: ${signer.address}`)\n  );\n  const signer = (await hre.ethers.getSigners())[0];\n  const tx = await signer.sendTransaction({\n    nonce: 0,\n    gasPrice: 1875000000,\n    gasLimit: 806069,\n    from: '0x77694e7c30b74dd271eaca4207ada0fc10632f5f',\n    to: '0xc4c11b14e9d876b031c1c7e05efe44088341f35b',\n    value: 0,\n    data: '0x0000000000000000000000000000000000000000000000000000000000000000608060405260405162000eeb38038062000eeb8339810160408190526200002691620004ed565b828162000036828260006200009a565b5062000066905060017fb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6104620005cd565b60008051602062000ea483398151915214620000865762000086620005f3565b6200009182620000d7565b5050506200065c565b620000a58362000132565b600082511180620000b35750805b15620000d257620000d083836200017460201b6200022e1760201c565b505b505050565b7f7e644d79422f17c01e4894b5f4f588d331ebfa28653d42ae832dc59e38c9798f62000102620001a3565b604080516001600160a01b03928316815291841660208301520160405180910390a16200012f81620001dc565b50565b6200013d8162000291565b6040516001600160a01b038216907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b90600090a250565b60606200019c838360405180606001604052806027815260200162000ec46027913962000345565b9392505050565b6000620001cd60008051602062000ea483398151915260001b620003c460201b620001ea1760201c565b546001600160a01b0316919050565b6001600160a01b038116620002475760405162461bcd60e51b815260206004820152602660248201527f455243313936373a206e65772061646d696e20697320746865207a65726f206160448201526564647265737360d01b60648201526084015b60405180910390fd5b806200027060008051602062000ea483398151915260001b620003c460201b620001ea1760201c565b80546001600160a01b0319166001600160a01b039290921691909117905550565b620002a781620003c760201b6200025a1760201c565b6200030b5760405162461bcd60e51b815260206004820152602d60248201527f455243313936373a206e657720696d706c656d656e746174696f6e206973206e60448201526c1bdd08184818dbdb9d1c9858dd609a1b60648201526084016200023e565b80620002707f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc60001b620003c460201b620001ea1760201c565b6060600080856001600160a01b03168560405162000364919062000609565b600060405180830381855af49150503d8060008114620003a1576040519150601f19603f3d011682016040523d82523d6000602084013e620003a6565b606091505b509092509050620003ba86838387620003d6565b9695505050505050565b90565b6001600160a01b03163b151590565b606083156200044a57825160000362000442576001600160a01b0385163b620004425760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e747261637400000060448201526064016200023e565b508162000456565b6200045683836200045e565b949350505050565b8151156200046f5781518083602001fd5b8060405162461bcd60e51b81526004016200023e919062000627565b80516001600160a01b0381168114620004a357600080fd5b919050565b634e487b7160e01b600052604160045260246000fd5b60005b83811015620004db578181015183820152602001620004c1565b83811115620000d05750506000910152565b6000806000606084860312156200050357600080fd5b6200050e846200048b565b92506200051e602085016200048b565b60408501519092506001600160401b03808211156200053c57600080fd5b818601915086601f8301126200055157600080fd5b815181811115620005665762000566620004a8565b604051601f8201601f19908116603f01168101908382118183101715620005915762000591620004a8565b81604052828152896020848701011115620005ab57600080fd5b620005be836020830160208801620004be565b80955050505050509250925092565b600082821015620005ee57634e487b7160e01b600052601160045260246000fd5b500390565b634e487b7160e01b600052600160045260246000fd5b600082516200061d818460208701620004be565b9190910192915050565b602081526000825180602084015262000648816040850160208701620004be565b601f01601f19169190910160400192915050565b610838806200066c6000396000f3fe60806040526004361061004e5760003560e01c80633659cfe6146100655780634f1ef286146100855780635c60da1b146100985780638f283970146100c9578063f851a440146100e95761005d565b3661005d5761005b6100fe565b005b61005b6100fe565b34801561007157600080fd5b5061005b6100803660046106c2565b610118565b61005b6100933660046106dd565b610155565b3480156100a457600080fd5b506100ad6101bc565b6040516001600160a01b03909116815260200160405180910390f35b3480156100d557600080fd5b5061005b6100e43660046106c2565b6101ed565b3480156100f557600080fd5b506100ad61020d565b610106610269565b6101166101116102fe565b610308565b565b61012061032c565b6001600160a01b0316330361014d5761014a8160405180602001604052806000815250600061035f565b50565b61014a6100fe565b61015d61032c565b6001600160a01b031633036101b4576101af8383838080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152506001925061035f915050565b505050565b6101af6100fe565b60006101c661032c565b6001600160a01b031633036101e2576101dd6102fe565b905090565b6101ea6100fe565b90565b6101f561032c565b6001600160a01b0316330361014d5761014a8161038a565b600061021761032c565b6001600160a01b031633036101e2576101dd61032c565b606061025383836040518060600160405280602781526020016107dc602791396103de565b9392505050565b6001600160a01b03163b151590565b61027161032c565b6001600160a01b031633036101165760405162461bcd60e51b815260206004820152604260248201527f5472616e73706172656e745570677261646561626c6550726f78793a2061646d60448201527f696e2063616e6e6f742066616c6c6261636b20746f2070726f78792074617267606482015261195d60f21b608482015260a4015b60405180910390fd5b60006101dd610456565b3660008037600080366000845af43d6000803e808015610327573d6000f35b3d6000fd5b60007fb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d61035b546001600160a01b0316919050565b6103688361047e565b6000825111806103755750805b156101af57610384838361022e565b50505050565b7f7e644d79422f17c01e4894b5f4f588d331ebfa28653d42ae832dc59e38c9798f6103b361032c565b604080516001600160a01b03928316815291841660208301520160405180910390a161014a816104be565b6060600080856001600160a01b0316856040516103fb919061078c565b600060405180830381855af49150503d8060008114610436576040519150601f19603f3d011682016040523d82523d6000602084013e61043b565b606091505b509150915061044c86838387610567565b9695505050505050565b60007f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc610350565b610487816105e8565b6040516001600160a01b038216907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b90600090a250565b6001600160a01b0381166105235760405162461bcd60e51b815260206004820152602660248201527f455243313936373a206e65772061646d696e20697320746865207a65726f206160448201526564647265737360d01b60648201526084016102f5565b807fb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d61035b80546001600160a01b0319166001600160a01b039290921691909117905550565b606083156105d65782516000036105cf576001600160a01b0385163b6105cf5760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e747261637400000060448201526064016102f5565b50816105e0565b6105e0838361067c565b949350505050565b6001600160a01b0381163b6106555760405162461bcd60e51b815260206004820152602d60248201527f455243313936373a206e657720696d706c656d656e746174696f6e206973206e60448201526c1bdd08184818dbdb9d1c9858dd609a1b60648201526084016102f5565b807f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc610546565b81511561068c5781518083602001fd5b8060405162461bcd60e51b81526004016102f591906107a8565b80356001600160a01b03811681146106bd57600080fd5b919050565b6000602082840312156106d457600080fd5b610253826106a6565b6000806000604084860312156106f257600080fd5b6106fb846106a6565b9250602084013567ffffffffffffffff8082111561071857600080fd5b818601915086601f83011261072c57600080fd5b81358181111561073b57600080fd5b87602082850101111561074d57600080fd5b6020830194508093505050509250925092565b60005b8381101561077b578181015183820152602001610763565b838111156103845750506000910152565b6000825161079e818460208701610760565b9190910192915050565b60208152600082518060208401526107c7816040850160208701610760565b601f01601f1916919091016040019291505056fe416464726573733a206c6f772d6c6576656c2064656c65676174652063616c6c206661696c6564a2646970667358221220e2b2f80d01b9fee1887f086f33ea33eabe6a896123f3722976ea73e069c365b664736f6c634300080e0033b53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103416464726573733a206c6f772d6c6576656c2064656c65676174652063616c6c206661696c6564000000000000000000000000c4c11b14e9d876b031c1c7e05efe44088341f35b00000000000000000000000077694e7c30b74dd271eaca4207ada0fc10632f5f00000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000',\n  });\n  // convert to rawTransaction\n  console.log(tx);\n  console.log(await tx.wait());\n\n  const transferAdminTx = await signer.sendTransaction({\n    nonce: 1,\n    gasPrice: 1766213205,\n    gasLimit: 210433,\n    to: '0x3340ac0cafb3ae34ddd53dba0d7344c1cf3efe05',\n    value: 0,\n    data: '0x8f283970000000000000000000000000b01ee322c4f028b8a6bfcd2a5d48107dc5bc99ec',\n  });\n  console.log(transferAdminTx);\n  console.log(await transferAdminTx.wait());\n}\n\ntask('forge-create2-transaction').setAction(printAccounts);\n"
  },
  {
    "path": "tasks/helpers/print-accounts.task.ts",
    "content": "import { task } from 'hardhat/config';\nimport { HardhatRuntimeEnvironment } from 'hardhat/types';\n\nasync function printAccounts({}, hre: HardhatRuntimeEnvironment): Promise<void> {\n  (await hre.ethers.getSigners()).map((signer, index) =>\n    console.log(`\n    Account #${index}: ${signer.address}`)\n  );\n  const signer = (await hre.ethers.getSigners())[0];\n  const tx = await signer.sendTransaction({\n    to: '0xfA6acea9De65F319aB583C05a49B2CB9cF5Ad111',\n    data: '0xdce4534f000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000001a00000000000000000000000000000000000000000000000000000000000000040000000000000000000000000fd247ff5380d7da60e9018d1d29d529664839af2000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000202a4764782297e8e049f7dae7b25e5f9885dc5524aa5152db9bfc0175ac5e1cfb00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000062a1f07e0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000024029243f4cedf90da2cef8712652a82b5e2c18b1ddbecd5c636719b8225868e72c2b705c6f8f6d8d2c426098d219bc5fab358341609b9788d80b03543f255454ec14b4ab1c5d6308f349e3566f8541dab61ccbd2f8afb4c8e10edab0be6665d8ab01bb8372d783f9b9fe626003d95502e8fa5fe5e1a8034edd87d35e9740fd629924d7e6248eccc662715ba2942b3c661ee53f687403a32dd4b07ccc7f7c3322c505c0ef373ca904d3f0de7e13240e6f256c3ca70bdf67439a8d2acbbe87d785ba0b0e361c1c9a5fdec9f06b06a68c7f718aa8f2ead8179f8591c6ef44a570335a2585771047fe68b4bda2592c2a59e7f771efafe184ee8a5518d250e38907fbf1000000000000000000000000fd247ff5380d7da60e9018d1d29d529664839af200000000000000000000000000000000000000000000000000000000000000041e468ad0fcde4edec429cd41eb28a0e78d4f31fa2c25172ef677468b2b38a9dc2b6e9a8e3b8ed419cca51e2e2ee7ae07d2902454deca17d7da7b00ae4a798add29c161c3ff1113059a064605aa9ddcfe636110a659037ef048f02a2e0233a79b2ef78ebff2f2e569060364b0d07798c61442f09f7e58f3f149cba98c09155fc222015ea96fc74d541186c5dcfca6813f2f55a5c525192f362928825f6165284500000000000000000000000000000000000000000000000000000000000000012a4764782297e8e049f7dae7b25e5f9885dc5524aa5152db9bfc0175ac5e1cfb0000000000000000000000000000000000000000000000000000000000000000',\n    gasLimit: 8000000,\n  });\n  console.log(tx);\n  console.log(await tx.wait());\n}\n\ntask('print-accounts').setAction(printAccounts);\n"
  },
  {
    "path": "tasks/helpers/print-storage-layout.task.ts",
    "content": "import { task } from 'hardhat/config';\nimport { HardhatRuntimeEnvironment } from 'hardhat/types';\n\nasync function printStorageLayout({}, hre: HardhatRuntimeEnvironment): Promise<void> {\n  await hre.run('deploy-full-local');\n  await hre.storageLayout.export();\n}\n\ntask('print-storage-layout').setAction(printStorageLayout);\n"
  },
  {
    "path": "tasks/helpers/proxy/upgrade-proxy.task.ts",
    "content": "import { ethers } from 'ethers';\nimport { task } from 'hardhat/config';\nimport { HardhatRuntimeEnvironment } from 'hardhat/types';\nimport { deploymentsConfig } from '../../deploy-tasks/deployments-config';\nimport { TransparentUpgradeableProxy__factory } from '../../../types';\nimport { CommonTaskOptions } from '../../utils';\nimport { confirm } from '../../utils/confirm';\n\nexport type UpgradeProxyArgs = {\n  proxyAddress: string;\n  proxyData: string;\n  newImplementationAddress: string;\n  specificProxyAdmin?: string; // this proxy admin will be used instead of the one in the config\n  options?: CommonTaskOptions;\n};\n\nasync function upgradeProxy(\n  {\n    proxyAddress,\n    proxyData,\n    newImplementationAddress,\n    specificProxyAdmin,\n    options,\n  }: UpgradeProxyArgs,\n  hre: HardhatRuntimeEnvironment\n): Promise<void> {\n  const config = deploymentsConfig[process.env.FORK_NETWORK ?? hre.network.name];\n\n  if (options?.log || options?.manualConfirm) {\n    console.log(`\n    ************************************\n    *          UPGRADE PROXY           *\n    ************************************`);\n\n    console.log(`\n    * upgrading proxy ***********\n    Proxy Address: ${proxyAddress}\n    new Implementation Address: ${newImplementationAddress}\n    `);\n    if (options?.manualConfirm) {\n      await confirm();\n    }\n  }\n\n  if (process.env.MANUAL_UPGRADE_PROXY === 'true') {\n    // we can't connect as signer, we need manual operation\n    const iface = new ethers.utils.Interface(TransparentUpgradeableProxy__factory.abi);\n    const data = iface.encodeFunctionData('upgradeToAndCall', [\n      newImplementationAddress,\n      proxyData,\n    ]);\n    console.log({\n      proxyAddress,\n      newImplementationAddress,\n      data: data,\n    });\n    console.log('Send the transaction using etherscan !');\n  } else {\n    // We can connect as proxy admin because signer is unlock\n    const proxyAdmin = specificProxyAdmin ?? (config.deployOptions.proxyAdmin as string);\n    const proxyAdminSigner = await hre.ethers.getSigner(proxyAdmin);\n    const proxy = TransparentUpgradeableProxy__factory.connect(proxyAddress, proxyAdminSigner);\n\n    await (await proxy.upgradeToAndCall(newImplementationAddress, proxyData)).wait();\n\n    if (options?.log || options?.manualConfirm) {\n      console.log(`\n      * Proxy implementation well updated !\n      `);\n    }\n  }\n\n  if (options?.log) {\n    console.log(`\n    * proxy upgraded ***********\n    Proxy Address: ${proxyAddress}\n    new Implementation Address: ${newImplementationAddress}\n    `);\n  }\n}\n\ntask('upgrade-proxy')\n  .addParam('proxyAddress', 'Address of the proxy to upgrade')\n  .addParam('newImplementationAddress', 'Address of the new implementation')\n  .setAction(upgradeProxy);\n"
  },
  {
    "path": "tasks/helpers/verify-contract.task.ts",
    "content": "import { task } from 'hardhat/config';\nimport { HardhatRuntimeEnvironment } from 'hardhat/types';\nimport fs from 'fs';\nimport fg from 'fast-glob';\nimport { exec } from 'child_process';\nimport util from 'util';\n\n// Verify with etherscan only one contract\n// identified by its deploymentName\nasync function verifyContract({ deploymentName }, hre: HardhatRuntimeEnvironment): Promise<void> {\n  const deploymentPath = `${__dirname}/../../deployments/${hre.deployments.getNetworkName()}`;\n  const deploymentFilesToHide = fg.sync([`${deploymentPath}/*`], {\n    ignore: [`${deploymentPath}/${deploymentName}.json`],\n  });\n\n  // hide all files except the deployment we are interested in.\n  await hideFiles(deploymentFilesToHide);\n\n  try {\n    // layer of security: avoid triggering the verify if there is still files left\n    const filesNotHidden = fg.sync([`${deploymentPath}/*.json`]);\n    if (filesNotHidden.length > 1) {\n      throw new Error(\n        'A deployment file other than the expected is still in the deployment folder!'\n      );\n    } else if (filesNotHidden.length === 0) {\n      throw new Error(`Deployment ${deploymentName} was not found!`);\n    }\n\n    // execute the etherscan-verify\n    const { stdout, stderr } = await util.promisify(exec)(\n      `npx hardhat etherscan-verify --network ${hre.deployments.getNetworkName()}`\n    );\n    console.log(stdout);\n    console.log(stderr);\n  } catch (e: any) {\n    console.log(e);\n  }\n\n  // revert modification to deployment files\n  await revealFiles(deploymentFilesToHide);\n}\n\ntask('verify-contract')\n  .addParam('deploymentName', 'Name of the deployment to verify')\n  .setAction(verifyContract);\n\nconst hideFiles = async (files: string[]) => {\n  for (const file of files) {\n    await fs.promises.rename(file, `${file}.hidden`);\n  }\n};\n\nconst revealFiles = async (files: string[]) => {\n  for (const file of files) {\n    await fs.promises.rename(`${file}.hidden`, file);\n  }\n};\n"
  },
  {
    "path": "tasks/utils/common-options.ts",
    "content": "import { HardhatRuntimeEnvironment } from 'hardhat/types';\nimport { CommonTaskOptions } from './types';\nimport { DeployOptions } from '../deploy-tasks/utils';\nimport { deploymentsConfig } from '../deploy-tasks/deployments-config';\n\nexport const getCommonOptions = (options: DeployOptions | CommonTaskOptions): CommonTaskOptions => {\n  return {\n    manualConfirm: options.manualConfirm,\n    log: options.log,\n  };\n};\n\nexport const wrapCommonOptions = (action: Function) => {\n  return (args: any, hre: HardhatRuntimeEnvironment) => {\n    const config = deploymentsConfig[hre.network.name];\n    return action(\n      {\n        ...args,\n        options: {\n          ...getCommonOptions(config.deployOptions),\n          ...args.options,\n        },\n      },\n      hre\n    );\n  };\n};\n"
  },
  {
    "path": "tasks/utils/confirm.ts",
    "content": "import readline from 'readline';\nimport { CommonTaskOptions } from './types';\n\nexport function confirm(): Promise<void> {\n  return new Promise((resolve) => {\n    const myReadLine = readline.createInterface({\n      input: process.stdin,\n      output: process.stdout,\n    });\n\n    myReadLine.question('Do you want to continue? Y/n \\n', (input) => {\n      myReadLine.close();\n      if (input === 'Y' || input == 'y') {\n        console.log('\\n');\n        resolve(undefined);\n      } else {\n        console.log('Deployment aborted');\n        process.exit(0);\n      }\n    });\n  });\n}\n\nexport const manualConfirmValidity = async (object: any, options?: CommonTaskOptions) => {\n  if (options?.log || options?.manualConfirm) {\n    console.log(`    \n    ${Object.keys(object as Object).map(\n      (key) => `\n    ${key}: ${object?.[key]}`\n    )}`);\n  }\n  if (options?.manualConfirm) {\n    console.log();\n    await confirm();\n  }\n};\n"
  },
  {
    "path": "tasks/utils/index.ts",
    "content": "export * from './common-options';\nexport * from './confirm';\nexport * from './types';\n"
  },
  {
    "path": "tasks/utils/relayer.ts",
    "content": "import { DefenderRelayProvider, DefenderRelaySigner } from 'defender-relay-client/lib/ethers';\nimport { Signer } from 'ethers';\n\nconst { RELAYER_API_KEY, RELAYER_API_SECRET } = process.env;\n\nexport const getRelayerSigner = async (): Promise<Signer> => {\n  if (!RELAYER_API_KEY || !RELAYER_API_SECRET) {\n    throw new Error('RELAYER_API_KEY or RELAYER_API_SECRET env variables missing');\n  }\n  const credentials = { apiKey: RELAYER_API_KEY, apiSecret: RELAYER_API_SECRET };\n  const provider = new DefenderRelayProvider(credentials);\n  return new DefenderRelaySigner(credentials, provider, { speed: 'fast' });\n};\n"
  },
  {
    "path": "tasks/utils/types.ts",
    "content": "export interface CommonTaskOptions {\n  // Enable logging\n  log?: boolean;\n  // Manually confirm deployment\n  manualConfirm?: boolean;\n}\n"
  },
  {
    "path": "test/e2e/e2e.test.ts",
    "content": "import { expect } from 'chai';\nimport hre from 'hardhat';\nimport {\n  AttestationsRegistry,\n  AvailableRootsRegistry,\n  Badges,\n  CommitmentMapperRegistry,\n  Front,\n  HydraS1SimpleAttester,\n  HydraS1AccountboundAttester,\n  Pythia1SimpleAttester,\n  TransparentUpgradeableProxy__factory,\n} from '../../types';\nimport { RequestStruct } from 'types/Attester';\nimport { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers';\nimport {\n  buildPoseidon,\n  CommitmentMapperTester,\n  EddsaPublicKey,\n} from '@sismo-core/commitment-mapper-tester-js';\nimport { HydraS1Account, HydraS1Prover, KVMerkleTree } from '@sismo-core/hydra-s1';\nimport { BigNumber } from 'ethers';\nimport { AttestationStructOutput } from 'types/HydraS1SimpleAttester';\nimport { deploymentsConfig } from '../../tasks/deploy-tasks/deployments-config';\nimport { Deployed0 } from '../../tasks/deploy-tasks/full/0-deploy-core-and-hydra-s1-simple-and-accountbound-and-pythia1.task';\nimport { getImplementation } from '../../utils';\nimport {\n  encodeGroupProperties,\n  evmRevert,\n  evmSnapshot,\n  generateAttesterGroups,\n  generateHydraS1Accounts,\n  generateGroups,\n  generateExternalNullifier,\n  getEventArgs,\n  HydraS1SimpleGroup,\n} from '../utils';\nimport { Deployed1 } from 'tasks/deploy-tasks/full/1-deploy-pythia-1-simple.task';\nimport {\n  CommitmentSignerTester,\n  encodePythia1GroupProperties,\n  generatePythia1Group,\n} from '../utils/pythia-1';\nimport { Pythia1Prover, SnarkProof } from '@sismo-core/pythia-1';\n\nconst config = deploymentsConfig[hre.network.name];\ndescribe('Test E2E Protocol', () => {\n  let chainId: number;\n  let snapshotId: string;\n\n  // contracts\n  let attestationsRegistry: AttestationsRegistry;\n  let hydraS1AccountboundAttester: HydraS1AccountboundAttester;\n  let hydraS1SimpleAttester: HydraS1SimpleAttester | undefined;\n  let pythia1SimpleAttester: Pythia1SimpleAttester;\n  let availableRootsRegistry: AvailableRootsRegistry;\n  let commitmentMapperRegistry: CommitmentMapperRegistry;\n  let front: Front;\n  let badges: Badges;\n  let earlyUserCollectionId;\n\n  // hydra s1 prover\n  let hydraS1Prover1: HydraS1Prover;\n  let hydraS1Prover2: HydraS1Prover;\n  let commitmentMapper: CommitmentMapperTester;\n  let commitmentMapperPubKey: EddsaPublicKey;\n  let registryTree1: KVMerkleTree;\n  let registryTree2: KVMerkleTree;\n\n  // pythia 1 prover\n  let pythia1Prover: Pythia1Prover;\n  let commitmentSigner: CommitmentSignerTester;\n\n  // Test Signers\n  let deployer: SignerWithAddress;\n  let proxyAdminSigner: SignerWithAddress;\n  let pythia1destinationSigner: SignerWithAddress;\n\n  // Test accounts\n  let source1: HydraS1Account;\n  let source2: HydraS1Account;\n  let destination1: HydraS1Account;\n  let destination2: HydraS1Account;\n\n  // Data source test\n  let source1Value: BigNumber;\n  let source2Value: BigNumber;\n  let accountsTree1: KVMerkleTree;\n  let accountsTree2: KVMerkleTree;\n  let group1: HydraS1SimpleGroup;\n  let group2: HydraS1SimpleGroup;\n\n  // Valid request and proof\n  let request1: RequestStruct;\n  let request2: RequestStruct;\n  let proofRequest1: string;\n  let proofRequest2: string;\n  let externalNullifier1: BigNumber;\n  let externalNullifier2: BigNumber;\n  let attestationsRequested1: AttestationStructOutput[];\n  let attestationsRequested2: AttestationStructOutput[];\n  let poseidon;\n\n  before(async () => {\n    const signers = await hre.ethers.getSigners();\n    [deployer, , proxyAdminSigner, pythia1destinationSigner] = signers;\n    chainId = parseInt(await hre.getChainId());\n    poseidon = await buildPoseidon();\n\n    // 1 - Init commitment mapper and commitment signer\n    commitmentMapper = await CommitmentMapperTester.generate();\n    commitmentMapperPubKey = await commitmentMapper.getPubKey();\n    commitmentSigner = new CommitmentSignerTester();\n\n    // 2 - Generate Hydra S1 Accounts with the commitment mapper\n    let hydraS1Accounts: HydraS1Account[] = await generateHydraS1Accounts(\n      signers,\n      commitmentMapper\n    );\n\n    source1 = hydraS1Accounts[0];\n    source2 = hydraS1Accounts[4];\n    destination1 = hydraS1Accounts[1];\n    destination2 = hydraS1Accounts[3];\n\n    // 3 - Generate data source\n    const allList = generateGroups(hydraS1Accounts);\n    const { dataFormat, groups } = await generateAttesterGroups(allList);\n    const { dataFormat: dataFormat2, groups: groups2 } = await generateAttesterGroups(allList);\n\n    registryTree1 = dataFormat.registryTree;\n    registryTree2 = dataFormat2.registryTree;\n    accountsTree1 = dataFormat.accountsTrees[0];\n    accountsTree2 = dataFormat2.accountsTrees[0];\n    group1 = groups[0];\n    group2 = groups2[0];\n    source1Value = accountsTree1.getValue(BigNumber.from(source1.identifier).toHexString());\n    source2Value = accountsTree1.getValue(BigNumber.from(source2.identifier).toHexString());\n\n    // 4 - Init Proving scheme\n    hydraS1Prover1 = new HydraS1Prover(registryTree1, commitmentMapperPubKey);\n    hydraS1Prover2 = new HydraS1Prover(registryTree2, commitmentMapperPubKey);\n    pythia1Prover = new Pythia1Prover();\n  });\n\n  /*************************************************************************************/\n  /********************************** SETUP ********************************************/\n  /*************************************************************************************/\n\n  describe('Deployments, setup contracts and prepare test requests', () => {\n    it('Should deploy and setup core', async () => {\n      // Deploy Sismo Protocol Core contracts\n\n      ({\n        attestationsRegistry,\n        badges,\n        front,\n        commitmentMapperRegistry,\n        hydraS1SimpleAttester,\n        hydraS1AccountboundAttester,\n        availableRootsRegistry,\n        pythia1SimpleAttester,\n      } = (await hre.run('0-deploy-core-and-hydra-s1-simple-and-accountbound-and-pythia1', {\n        options: {\n          proxyAdmin: proxyAdminSigner.address,\n        },\n      })) as Deployed0);\n\n      await (\n        await availableRootsRegistry.registerRootForAttester(\n          hydraS1SimpleAttester!.address,\n          registryTree1.getRoot()\n        )\n      ).wait();\n\n      await (\n        await availableRootsRegistry.registerRootForAttester(\n          hydraS1AccountboundAttester.address,\n          registryTree2.getRoot()\n        )\n      ).wait();\n      earlyUserCollectionId = await front.EARLY_USER_COLLECTION();\n    });\n    it('Should prepare test requests', async () => {\n      // Deploy Sismo Protocol Core contracts\n      externalNullifier1 = await generateExternalNullifier(\n        hydraS1SimpleAttester!.address,\n        group1.properties.groupIndex\n      );\n\n      externalNullifier2 = await generateExternalNullifier(\n        hydraS1AccountboundAttester.address,\n        group2.properties.groupIndex\n      );\n\n      request1 = {\n        claims: [\n          {\n            groupId: group1.id,\n            claimedValue: source1Value,\n            extraData: encodeGroupProperties(group1.properties),\n          },\n        ],\n        destination: BigNumber.from(destination1.identifier).toHexString(),\n      };\n\n      proofRequest1 = (\n        await hydraS1Prover1.generateSnarkProof({\n          source: source1,\n          destination: destination1,\n          claimedValue: source1Value,\n          chainId: chainId,\n          accountsTree: accountsTree1,\n          externalNullifier: externalNullifier1,\n          isStrict: !group1.properties.isScore,\n        })\n      ).toBytes();\n\n      request2 = {\n        claims: [\n          {\n            groupId: group2.id,\n            claimedValue: source2Value,\n            extraData: encodeGroupProperties(group2.properties),\n          },\n        ],\n        destination: BigNumber.from(destination1.identifier).toHexString(),\n      };\n\n      proofRequest2 = (\n        await hydraS1Prover2.generateSnarkProof({\n          source: source2,\n          destination: destination1,\n          claimedValue: source2Value,\n          chainId: chainId,\n          accountsTree: accountsTree2,\n          externalNullifier: externalNullifier2,\n          isStrict: !group2.properties.isScore,\n        })\n      ).toBytes();\n\n      [attestationsRequested1, attestationsRequested2] = await front.batchBuildAttestations(\n        [hydraS1SimpleAttester!.address, hydraS1AccountboundAttester.address],\n        [request1, request2],\n        [proofRequest1, proofRequest2]\n      );\n\n      snapshotId = await evmSnapshot(hre);\n    });\n  });\n\n  /*************************************************************************************/\n  /************************ATTESTATIONS AND BADGES GENERATIONS**************************/\n  /*************************************************************************************/\n\n  describe('Test attestations generations', () => {\n    it('Should generate attestations from hydra s1 simple and hydra s1 accountbound via batch', async () => {\n      const tx = await front.batchGenerateAttestations(\n        [hydraS1SimpleAttester!.address, hydraS1AccountboundAttester.address],\n        [request1, request2],\n        [proofRequest1, proofRequest2]\n      );\n      const { events } = await tx.wait();\n\n      const earlyUserActivated = Date.now() < Date.parse('15 Sept 2022 00:00:00 GMT');\n      if (earlyUserActivated) {\n        const args = getEventArgs(events, 'EarlyUserAttestationGenerated');\n        expect(args.destination).to.eql(request1.destination);\n      }\n\n      const attestationsValues = await attestationsRegistry.getAttestationValueBatch(\n        [\n          attestationsRequested1[0].collectionId,\n          attestationsRequested2[0].collectionId,\n          earlyUserCollectionId,\n        ],\n        [request1.destination, request2.destination, request1.destination]\n      );\n\n      const expectedAttestationsValues = [\n        attestationsRequested1[0].value,\n        attestationsRequested2[0].value,\n        earlyUserActivated ? BigNumber.from(1) : BigNumber.from(0),\n      ];\n\n      expect(attestationsValues).to.be.eql(expectedAttestationsValues);\n\n      const balances = await badges.balanceOfBatch(\n        [request1.destination, request2.destination, request1.destination],\n        [\n          attestationsRequested1[0].collectionId,\n          attestationsRequested2[0].collectionId,\n          earlyUserCollectionId,\n        ]\n      );\n\n      const expectedBalances = expectedAttestationsValues;\n\n      expect(balances).to.be.eql(expectedBalances);\n    });\n    it('Should reset contracts', async () => {\n      await evmRevert(hre, snapshotId);\n\n      const attestationsValues = await attestationsRegistry.getAttestationValueBatch(\n        [\n          attestationsRequested1[0].collectionId,\n          attestationsRequested2[0].collectionId,\n          earlyUserCollectionId,\n        ],\n        [request1.destination, request2.destination, request1.destination]\n      );\n\n      const expectedAttestationsValues = [BigNumber.from(0), BigNumber.from(0), BigNumber.from(0)];\n\n      expect(attestationsValues).to.be.eql(expectedAttestationsValues);\n\n      const balances = await badges.balanceOfBatch(\n        [request1.destination, request2.destination, request1.destination],\n        [\n          attestationsRequested1[0].collectionId,\n          attestationsRequested2[0].collectionId,\n          earlyUserCollectionId,\n        ]\n      );\n\n      const expectedBalances = expectedAttestationsValues;\n\n      expect(balances).to.be.eql(expectedBalances);\n    });\n    it('Should generate attestations from hydra s1 simple and hydra s1 accountbound via front and two separate txs', async () => {\n      const tx = await front.generateAttestations(\n        hydraS1SimpleAttester!.address,\n        request1,\n        proofRequest1\n      );\n      await front.generateAttestations(\n        hydraS1AccountboundAttester.address,\n        request2,\n        proofRequest2\n      );\n      const { events } = await tx.wait();\n      const earlyUserActivated = Date.now() < Date.parse('15 Sept 2022 00:00:00 GMT');\n      if (earlyUserActivated) {\n        const args = getEventArgs(events, 'EarlyUserAttestationGenerated');\n        expect(args.destination).to.eql(request1.destination);\n      }\n\n      const attestationsValues = await attestationsRegistry.getAttestationValueBatch(\n        [\n          attestationsRequested1[0].collectionId,\n          attestationsRequested2[0].collectionId,\n          earlyUserCollectionId,\n        ],\n        [request1.destination, request2.destination, request1.destination]\n      );\n\n      const expectedAttestationsValues = [\n        attestationsRequested1[0].value,\n        attestationsRequested2[0].value,\n        earlyUserActivated ? BigNumber.from(1) : BigNumber.from(0),\n      ];\n\n      expect(attestationsValues).to.be.eql(expectedAttestationsValues);\n\n      const balances = await badges.balanceOfBatch(\n        [request1.destination, request2.destination, request1.destination],\n        [\n          attestationsRequested1[0].collectionId,\n          attestationsRequested2[0].collectionId,\n          earlyUserCollectionId,\n        ]\n      );\n\n      const expectedBalances = expectedAttestationsValues;\n\n      expect(balances).to.be.eql(expectedBalances);\n    });\n    it('Should generate an attestation from pythia 1 simple', async () => {\n      const secret = BigNumber.from('0x123');\n      const commitment = poseidon([secret]);\n      const commitmentValue = BigNumber.from('0x9');\n      const pythia1group1 = generatePythia1Group({\n        internalCollectionId: 0,\n        isScore: false,\n      });\n      const commitmentReceipt = await commitmentSigner.getCommitmentReceipt(\n        commitment,\n        commitmentValue,\n        pythia1group1.id\n      );\n\n      const externalNullifier = await generateExternalNullifier(\n        pythia1SimpleAttester.address,\n        pythia1group1.properties.internalCollectionId\n      );\n\n      const request = {\n        claims: [\n          {\n            groupId: pythia1group1.id,\n            claimedValue: commitmentValue,\n            extraData: encodePythia1GroupProperties(pythia1group1.properties),\n          },\n        ],\n        destination: BigNumber.from(pythia1destinationSigner.address).toHexString(),\n      };\n\n      const proof = (await pythia1Prover.generateSnarkProof({\n        secret: secret,\n        value: commitmentValue,\n        commitmentReceipt: commitmentReceipt,\n        commitmentSignerPubKey: await commitmentSigner.getPublicKey(),\n        destinationIdentifier: pythia1destinationSigner.address,\n        claimedValue: commitmentValue,\n        chainId: chainId,\n        groupId: pythia1group1.id,\n        ticketIdentifier: externalNullifier,\n        isStrict: !pythia1group1.properties.isScore,\n      })) as SnarkProof;\n\n      const tx = await pythia1SimpleAttester\n        .connect(pythia1destinationSigner)\n        .generateAttestations(request, proof.toBytes());\n      await tx.wait();\n\n      const balances = await badges.balanceOfBatch(\n        [pythia1destinationSigner.address],\n        [\n          BigNumber.from(config.synapsPythia1SimpleAttester.collectionIdFirst).add(\n            pythia1group1.properties.internalCollectionId\n          ),\n        ]\n      );\n\n      expect(balances).to.be.eql([commitmentValue]);\n    });\n  });\n\n  describe('Update Implementation', () => {\n    it('Should update Hydra S1 Simple implementation', async () => {\n      const { hydraS1SimpleAttester: newHydraS1SimpleAttester } = await hre.run(\n        'deploy-hydra-s1-simple-attester',\n        {\n          enableDeployment: config.hydraS1SimpleAttester.enableDeployment,\n          collectionIdFirst: config.hydraS1SimpleAttester.collectionIdFirst,\n          collectionIdLast: config.hydraS1SimpleAttester.collectionIdLast,\n          commitmentMapperRegistryAddress: commitmentMapperRegistry.address,\n          availableRootsRegistryAddress: availableRootsRegistry.address,\n          attestationsRegistryAddress: attestationsRegistry.address,\n          options: { behindProxy: false },\n        }\n      );\n\n      const hydraS1SimpleAttesterProxy = TransparentUpgradeableProxy__factory.connect(\n        hydraS1SimpleAttester!.address,\n        proxyAdminSigner\n      );\n\n      await (await hydraS1SimpleAttesterProxy.upgradeTo(newHydraS1SimpleAttester.address)).wait();\n\n      const implementationAddress = await getImplementation(hydraS1SimpleAttesterProxy);\n      expect(implementationAddress).to.eql(newHydraS1SimpleAttester.address);\n    });\n\n    it('Should update Hydra S1 Accountbound implementation', async () => {\n      const { hydraS1AccountboundAttester: newHydraS1AccountboundAttester } = await hre.run(\n        'deploy-hydra-s1-accountbound-attester',\n        {\n          collectionIdFirst: config.hydraS1AccountboundAttester.collectionIdFirst,\n          collectionIdLast: config.hydraS1AccountboundAttester.collectionIdLast,\n          commitmentMapperRegistryAddress: commitmentMapperRegistry.address,\n          availableRootsRegistryAddress: availableRootsRegistry.address,\n          attestationsRegistryAddress: attestationsRegistry.address,\n          options: { behindProxy: false },\n        }\n      );\n\n      const hydraS1AccountboundAttesterProxy = TransparentUpgradeableProxy__factory.connect(\n        hydraS1AccountboundAttester.address,\n        proxyAdminSigner\n      );\n\n      await (\n        await hydraS1AccountboundAttesterProxy.upgradeTo(newHydraS1AccountboundAttester.address)\n      ).wait();\n\n      const implementationAddress = await getImplementation(hydraS1AccountboundAttesterProxy);\n      expect(implementationAddress).to.eql(newHydraS1AccountboundAttester.address);\n    });\n  });\n});\n"
  },
  {
    "path": "test/unit/attesters/hydra-s1/hydra-s1-accountbound-attester.test.ts",
    "content": "import { encodeAccountBoundAttestationExtraData } from '../../../utils/hydra-s1-accountbound';\nimport { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers';\nimport { CommitmentMapperTester, EddsaPublicKey } from '@sismo-core/commitment-mapper-tester-js';\nimport {\n  HydraS1Account,\n  HydraS1Prover,\n  Inputs,\n  KVMerkleTree,\n  SnarkProof,\n  SNARK_FIELD,\n} from '@sismo-core/hydra-s1';\nimport { expect } from 'chai';\nimport { BigNumber, utils } from 'ethers';\nimport hre, { ethers } from 'hardhat';\nimport {\n  AttestationsRegistry,\n  AvailableRootsRegistry,\n  CommitmentMapperRegistry,\n  HydraS1AccountboundAttester,\n  HydraS1Verifier,\n} from 'types';\nimport { RequestStruct } from 'types/HydraS1SimpleAttester';\nimport {\n  evmRevert,\n  evmSnapshot,\n  generateHydraS1Accounts,\n  generateGroups,\n  generateExternalNullifier,\n  increaseTime,\n  toBytes,\n  GroupData,\n  generateAttesterGroups,\n  HydraS1SimpleGroup,\n  encodeGroupProperties,\n  generateGroupIdFromProperties,\n} from '../../../utils';\n\ndescribe('Test HydraS1 Accountbound Attester contract', () => {\n  let attestationsRegistry: AttestationsRegistry;\n  let hydraS1AccountboundAttester: HydraS1AccountboundAttester;\n  let hydraS1Verifier: HydraS1Verifier;\n  let commitmentMapperRegistry: CommitmentMapperRegistry;\n  let availableRootsRegistry: AvailableRootsRegistry;\n\n  let prover: HydraS1Prover;\n  let commitmentMapper: CommitmentMapperTester;\n  let commitmentMapperPubKey: EddsaPublicKey;\n\n  let deployer: SignerWithAddress;\n  let destinationSigner: SignerWithAddress;\n  let destination2Signer: SignerWithAddress;\n  let randomSigner: SignerWithAddress;\n\n  let chainId: number;\n\n  let source: HydraS1Account;\n  let destination: HydraS1Account;\n  let destination2: HydraS1Account;\n\n  let sourceValue: BigNumber;\n  let registryTree: KVMerkleTree;\n  let accountsTree: KVMerkleTree;\n  let accountsTree2: KVMerkleTree;\n  let group: HydraS1SimpleGroup;\n  let group2: HydraS1SimpleGroup;\n  let allAvailableGroups: GroupData[];\n  let externalNullifier: BigNumber;\n  let cooldownDuration: number;\n  let userParams;\n  let inputs: Inputs;\n  let proof: SnarkProof;\n  let request: RequestStruct;\n\n  let evmSnapshotId: string;\n\n  before(async () => {\n    const signers = await hre.ethers.getSigners();\n    [deployer, destinationSigner, destination2Signer, , , randomSigner] = signers;\n\n    chainId = parseInt(await hre.getChainId());\n\n    commitmentMapper = await CommitmentMapperTester.generate();\n    commitmentMapperPubKey = await commitmentMapper.getPubKey();\n\n    let hydraS1Accounts = await generateHydraS1Accounts(signers, commitmentMapper);\n    [source, destination, destination2] = hydraS1Accounts;\n\n    allAvailableGroups = await generateGroups(hydraS1Accounts);\n    const { dataFormat, groups } = await generateAttesterGroups(allAvailableGroups);\n\n    registryTree = dataFormat.registryTree;\n    [accountsTree, accountsTree2] = dataFormat.accountsTrees;\n    [group, group2] = groups;\n    sourceValue = accountsTree.getValue(BigNumber.from(source.identifier).toHexString());\n\n    prover = new HydraS1Prover(registryTree, commitmentMapperPubKey);\n\n    cooldownDuration = 60 * 60 * 24;\n  });\n\n  /*************************************************************************************/\n  /********************************** DEPLOYMENTS **************************************/\n  /*************************************************************************************/\n  describe('Deployments', () => {\n    it('Should deploy, setup and test the constructed values of the contract', async () => {\n      ({\n        attestationsRegistry,\n        hydraS1AccountboundAttester,\n        hydraS1Verifier,\n        commitmentMapperRegistry,\n        availableRootsRegistry,\n      } = await hre.run('0-deploy-core-and-hydra-s1-simple-and-accountbound-and-pythia1', {\n        options: { deploymentNamePrefix: 'accountbound' },\n      }));\n\n      // 0 - Checks that the verifier, available roots registry, commitment mapper registry and attestations registry are set\n      expect(await hydraS1AccountboundAttester.getVerifier()).to.equal(hydraS1Verifier.address);\n      expect(await hydraS1AccountboundAttester.getAvailableRootsRegistry()).to.equal(\n        availableRootsRegistry.address\n      );\n      expect(await hydraS1AccountboundAttester.getCommitmentMapperRegistry()).to.equal(\n        commitmentMapperRegistry.address\n      );\n      expect(await hydraS1AccountboundAttester.getAttestationRegistry()).to.equal(\n        attestationsRegistry.address\n      );\n    });\n\n    after(async () => {\n      externalNullifier = await generateExternalNullifier(\n        hydraS1AccountboundAttester.address,\n        group.properties.groupIndex\n      );\n\n      userParams = {\n        source: source,\n        destination: destination,\n        claimedValue: sourceValue,\n        chainId: chainId,\n        accountsTree: accountsTree,\n        externalNullifier: externalNullifier,\n        isStrict: !group.properties.isScore,\n      };\n\n      inputs = await prover.generateInputs(userParams);\n\n      proof = await prover.generateSnarkProof(userParams);\n\n      request = {\n        claims: [\n          {\n            groupId: group.id,\n            claimedValue: sourceValue,\n            extraData: encodeGroupProperties(group.properties),\n          },\n        ],\n        destination: BigNumber.from(destination.identifier).toHexString(),\n      };\n    });\n  });\n\n  describe('Configuration checks', () => {\n    it('Should have setup the owner correctly', async () => {\n      expect(await hydraS1AccountboundAttester.owner()).to.be.eql(deployer.address);\n    });\n\n    it('Should get the owner correctly', async () => {\n      expect(await hydraS1AccountboundAttester.owner()).to.be.eql(deployer.address);\n    });\n\n    it('Should revert when trying to call initialize again with a random address', async () => {\n      await expect(\n        hydraS1AccountboundAttester.connect(randomSigner).initialize(randomSigner.address)\n      ).to.be.revertedWith('Initializable: contract is already initialized');\n    });\n  });\n\n  describe('Ownable', () => {\n    it('Should revert when trying to transferOwnership with a random address', async () => {\n      await expect(\n        hydraS1AccountboundAttester.connect(randomSigner).transferOwnership(randomSigner.address)\n      ).to.be.revertedWith('Ownable: caller is not the owner');\n    });\n\n    it('Should revert when trying to transferOwnership to 0x0', async () => {\n      await expect(\n        hydraS1AccountboundAttester\n          .connect(deployer)\n          .transferOwnership('0x0000000000000000000000000000000000000000')\n      ).to.be.revertedWith('Ownable: new owner is the zero address');\n    });\n\n    it('Should transfer ownership', async () => {\n      evmSnapshotId = await evmSnapshot(hre);\n\n      await hydraS1AccountboundAttester.connect(deployer).transferOwnership(randomSigner.address);\n      expect(await hydraS1AccountboundAttester.owner()).to.be.eql(randomSigner.address);\n\n      evmRevert(hre, evmSnapshotId);\n    });\n\n    it('Should renounce ownership', async () => {\n      evmSnapshotId = await evmSnapshot(hre);\n\n      await hydraS1AccountboundAttester.connect(deployer).renounceOwnership();\n      expect(await hydraS1AccountboundAttester.owner()).to.be.eql(\n        '0x0000000000000000000000000000000000000000'\n      );\n\n      evmRevert(hre, evmSnapshotId);\n    });\n  });\n\n  /*************************************************************************************/\n  /******************************* BUILD ATTESTATIONS **********************************/\n  /*************************************************************************************/\n  describe('Build attestations', () => {\n    it('Should revert when the collectionId is out of attester bounds', async () => {\n      const wrongGroupProperties = {\n        ...group.properties,\n        groupIndex: (await hydraS1AccountboundAttester.AUTHORIZED_COLLECTION_ID_LAST()).toNumber(),\n      };\n\n      const wrongRequest = {\n        ...request,\n        claims: [\n          {\n            ...request.claims[0],\n            groupId: generateGroupIdFromProperties(wrongGroupProperties),\n            extraData: encodeGroupProperties(wrongGroupProperties),\n          },\n        ],\n      };\n\n      await expect(\n        hydraS1AccountboundAttester.buildAttestations(wrongRequest, proof.toBytes())\n      ).to.be.revertedWith('CollectionIdOutOfBound');\n    });\n\n    it('Should build the attestations', async () => {\n      const buildAttestations = await hydraS1AccountboundAttester.buildAttestations(\n        request,\n        proof.toBytes()\n      );\n\n      expect(buildAttestations[0].collectionId).to.eql(\n        (await hydraS1AccountboundAttester.AUTHORIZED_COLLECTION_ID_FIRST()).add(\n          group.properties.groupIndex\n        )\n      );\n\n      expect(buildAttestations[0].owner).to.eql(request.destination);\n      expect(buildAttestations[0].issuer).to.eql(hydraS1AccountboundAttester.address);\n      expect(buildAttestations[0].value.toNumber()).to.eql(sourceValue.toNumber());\n      expect(buildAttestations[0].timestamp).to.eql(group.properties.generationTimestamp);\n      expect(buildAttestations[0].extraData).to.eql(\n        encodeAccountBoundAttestationExtraData({\n          nullifier: inputs.publicInputs.nullifier,\n          burnCount: 0,\n        })\n      );\n    });\n  });\n\n  describe('Cooldown configuration', () => {\n    it('Should revert if a signer different from contract owner wants to set a cooldown duration for a groupIndex', async () => {\n      expect(await hydraS1AccountboundAttester.owner()).not.to.be.eql(randomSigner.address);\n\n      await expect(\n        hydraS1AccountboundAttester\n          .connect(randomSigner)\n          .setCooldownDurationForGroupIndex(group.properties.groupIndex, cooldownDuration)\n      ).to.be.revertedWith('Ownable: caller is not the owner');\n    });\n\n    it('Should set a cooldown duration for a groupIndex', async () => {\n      // set a cooldown of 1 day for first group\n      const setCooldownDurationTransaction = await hydraS1AccountboundAttester\n        .connect(deployer)\n        .setCooldownDurationForGroupIndex(group.properties.groupIndex, cooldownDuration);\n\n      await expect(setCooldownDurationTransaction)\n        .to.emit(hydraS1AccountboundAttester, 'CooldownDurationSetForGroupIndex')\n        .withArgs(group.properties.groupIndex, cooldownDuration);\n\n      expect(\n        await hydraS1AccountboundAttester.getCooldownDurationForGroupIndex(\n          group.properties.groupIndex\n        )\n      ).to.be.eql(cooldownDuration);\n    });\n  });\n\n  /*************************************************************************************/\n  /******************************* GENERATE ATTESTATIONS *******************************/\n  /*************************************************************************************/\n  describe('Generate Attestations', () => {\n    it('Should revert if the user provided wrong group generation datas', async () => {\n      const wrongExtraData = {\n        ...group.properties,\n      };\n\n      await expect(\n        hydraS1AccountboundAttester.generateAttestations(\n          { ...request, claims: [{ ...request.claims[0], groupId: group2.id }] },\n          proof.toBytes()\n        )\n      ).to.be.revertedWith(\n        `GroupIdAndPropertiesMismatch(${BigNumber.from(\n          ethers.utils.keccak256(request.claims[0].extraData)\n        ).mod(SNARK_FIELD)}, ${BigNumber.from(group2.id)})`\n      );\n\n      wrongExtraData.groupIndex = group2.properties.groupIndex;\n\n      await expect(\n        hydraS1AccountboundAttester.generateAttestations(\n          {\n            ...request,\n            claims: [\n              {\n                ...request.claims[0],\n                extraData: encodeGroupProperties(wrongExtraData),\n              },\n            ],\n          },\n          proof.toBytes()\n        )\n      ).to.be.revertedWith(\n        `GroupIdAndPropertiesMismatch(${BigNumber.from(\n          ethers.utils.keccak256(encodeGroupProperties(wrongExtraData))\n        ).mod(SNARK_FIELD)}, ${BigNumber.from(group.id)})`\n      );\n\n      wrongExtraData.groupIndex = group.properties.groupIndex;\n      wrongExtraData.generationTimestamp = group2.properties.generationTimestamp;\n\n      await expect(\n        hydraS1AccountboundAttester.generateAttestations(\n          {\n            ...request,\n            claims: [\n              {\n                ...request.claims[0],\n                extraData: encodeGroupProperties(wrongExtraData),\n              },\n            ],\n          },\n          proof.toBytes()\n        )\n      ).to.be.revertedWith(\n        `GroupIdAndPropertiesMismatch(${BigNumber.from(\n          ethers.utils.keccak256(encodeGroupProperties(wrongExtraData))\n        ).mod(SNARK_FIELD)}, ${BigNumber.from(group.id)})`\n      );\n\n      wrongExtraData.generationTimestamp = group.properties.generationTimestamp;\n      wrongExtraData.isScore = !group.properties.isScore;\n\n      await expect(\n        hydraS1AccountboundAttester.generateAttestations(\n          {\n            ...request,\n            claims: [\n              {\n                ...request.claims[0],\n                extraData: encodeGroupProperties(wrongExtraData),\n              },\n            ],\n          },\n          proof.toBytes()\n        )\n      ).to.be.revertedWith(\n        `GroupIdAndPropertiesMismatch(${BigNumber.from(\n          ethers.utils.keccak256(encodeGroupProperties(wrongExtraData))\n        ).mod(SNARK_FIELD)}, ${BigNumber.from(group.id)})`\n      );\n\n      wrongExtraData.isScore = group.properties.isScore;\n    });\n\n    it('Should revert if the snark accounts tree value mismatch the claim groupId', async () => {\n      // 0 - Checks that it's reverted if the wrong proof accounts tree is not the same as the claim groupId\n      const wrongProof = await prover.generateSnarkProof({\n        ...userParams,\n        destination: destination2,\n        accountsTree: accountsTree2,\n        isStrict: false,\n      });\n\n      await expect(\n        hydraS1AccountboundAttester.generateAttestations(request, wrongProof.toBytes())\n      ).to.be.revertedWith(\n        `AccountsTreeValueMismatch(${BigNumber.from(group.id)}, ${BigNumber.from(group2.id)})`\n      );\n\n      // 1 - Checks that it's reverted if the proof accounts tree is not the same as the fake claim groupId\n      await expect(\n        hydraS1AccountboundAttester.generateAttestations(\n          {\n            ...request,\n            claims: [\n              {\n                ...request.claims[0],\n                groupId: group2.id,\n                extraData: encodeGroupProperties(group2.properties),\n              },\n            ],\n          },\n          proof.toBytes()\n        )\n      ).to.be.revertedWith(\n        `AccountsTreeValueMismatch(${BigNumber.from(group2.id)}, ${BigNumber.from(group.id)})`\n      );\n    });\n\n    it('Should revert if the snark isStrict mismatch the claim groupProperty isScore', async () => {\n      // 0 - Checks that it's reverted if the fake proof isStrict is the same with the claim isScore\n      const wrongProof = await prover.generateSnarkProof({\n        ...userParams,\n        isStrict: group.properties.isScore,\n      });\n\n      await expect(\n        hydraS1AccountboundAttester.generateAttestations(request, wrongProof.toBytes())\n      ).to.be.revertedWith(`IsStrictMismatch(false, false)`);\n    });\n\n    it('Should revert if the snark input destination mismatch the claim destination', async () => {\n      // 0 - Checks that it's reverted if the fake proof destination is different from the claim destination\n      const wrongProof = await prover.generateSnarkProof({\n        ...userParams,\n        destination: destination2,\n      });\n\n      await expect(\n        hydraS1AccountboundAttester.generateAttestations(request, wrongProof.toBytes())\n      ).to.be.revertedWith(\n        `DestinationMismatch(\"${BigNumber.from(\n          destination.identifier\n        ).toHexString()}\", \"${BigNumber.from(destination2.identifier).toHexString()}\")`\n      );\n\n      // 1 - Checks that it's reverted if the proof destination is different from the fake claim destination\n      await expect(\n        hydraS1AccountboundAttester.generateAttestations(\n          {\n            ...request,\n            destination: BigNumber.from(destination2.identifier).toHexString(),\n          },\n          proof.toBytes()\n        )\n      ).to.be.revertedWith(\n        `DestinationMismatch(\"${BigNumber.from(\n          destination2.identifier\n        ).toHexString()}\", \"${BigNumber.from(destination.identifier).toHexString()}\")`\n      );\n    });\n\n    it('Should revert if the snark input chainId mismatch the blockchain chainId', async () => {\n      // 0 - Checks that it's reverted if the fake proof chainId is different from the claim chainId\n      const wrongProof = await prover.generateSnarkProof({\n        ...userParams,\n        chainId: chainId - 1,\n      });\n\n      await expect(\n        hydraS1AccountboundAttester.generateAttestations(request, wrongProof.toBytes())\n      ).to.be.revertedWith(\n        `ChainIdMismatch(${BigNumber.from(chainId)}, ${BigNumber.from(chainId - 1)})`\n      );\n    });\n\n    it('Should revert if the snark input claimedValue mismatch the claim claimedValue ', async () => {\n      // 0 - Checks that it's reverted if the proof claimedvalue is different from the fake claim claimedValue\n      await expect(\n        hydraS1AccountboundAttester.generateAttestations(\n          {\n            ...request,\n            claims: [\n              {\n                ...request.claims[0],\n                claimedValue: sourceValue.add(1),\n              },\n            ],\n          },\n          proof.toBytes()\n        )\n      ).to.be.revertedWith(`ValueMismatch(${sourceValue.add(1)}, ${sourceValue})`);\n    });\n\n    it('Should revert if the attester has not access to the registry root', async () => {\n      // 0 - Checks that it's reverted if the attester has not access to the registry root\n      await expect(\n        hydraS1AccountboundAttester.generateAttestations(request, proof.toBytes())\n      ).to.be.revertedWith(\n        `RegistryRootMismatch(${BigNumber.from(inputs.publicInputs.registryTreeRoot)}`\n      );\n    });\n\n    it('Should revert if the input commitment mapper pub keys mismatch the onchain commitment mapper pub keys', async () => {\n      await availableRootsRegistry.registerRootForAttester(\n        hydraS1AccountboundAttester.address,\n        registryTree.getRoot()\n      );\n\n      const evmSnapshotId = await evmSnapshot(hre);\n\n      const wrongCommitmentMapperPubKey: EddsaPublicKey = [\n        commitmentMapperPubKey[0].add(1),\n        commitmentMapperPubKey[1].add(1),\n      ];\n\n      await commitmentMapperRegistry.updateCommitmentMapperEdDSAPubKey(wrongCommitmentMapperPubKey);\n\n      await expect(\n        hydraS1AccountboundAttester.generateAttestations(request, proof.toBytes())\n      ).to.be.revertedWith(\n        `CommitmentMapperPubKeyMismatch(${wrongCommitmentMapperPubKey[0]}, ${wrongCommitmentMapperPubKey[1]}, ${commitmentMapperPubKey[0]}, ${commitmentMapperPubKey[1]})`\n      );\n\n      await evmRevert(hre, evmSnapshotId);\n    });\n\n    it('Should revert if the snark input externalNullifier mismatch the claim externalNullifier', async () => {\n      // 0 - Checks that it's reverted if the fake proof externalNullifier is different from the claim externalNullifier\n      const wrongExternalNullifier = await generateExternalNullifier(\n        hydraS1AccountboundAttester.address,\n        group2.properties.groupIndex\n      );\n\n      const wrongProof = await prover.generateSnarkProof({\n        ...userParams,\n        externalNullifier: wrongExternalNullifier,\n      });\n\n      await expect(\n        hydraS1AccountboundAttester.generateAttestations(request, wrongProof.toBytes())\n      ).to.be.revertedWith(\n        `ExternalNullifierMismatch(${externalNullifier}, ${wrongExternalNullifier})`\n      );\n    });\n\n    it('Should not allow snark field overflow by providing a nullifier that is outside the snark field', async () => {\n      // override the nullifier proof to overflow\n      const wrongProof = { ...proof, input: [...proof.input] };\n      wrongProof.input[6] = BigNumber.from(wrongProof.input[6]).add(SNARK_FIELD);\n\n      await expect(\n        hydraS1AccountboundAttester.generateAttestations(request, toBytes(wrongProof))\n      ).to.be.revertedWith(`InvalidGroth16Proof(\"verifier-gte-snark-scalar-field\")`);\n    });\n\n    it('Should revert if wrong snark proof', async () => {\n      // override the nullifier proof to overflow\n      const wrongProof = { ...proof, a: [...proof.a] };\n      wrongProof.a[0] = BigNumber.from(proof.a[0]).sub(1);\n\n      await expect(\n        hydraS1AccountboundAttester.generateAttestations(request, toBytes(wrongProof))\n      ).to.be.revertedWith(`InvalidGroth16Proof(\"\")`);\n    });\n\n    it('Should generate a proof with Hydra S1 Prover and verify it onchain using the attester', async () => {\n      const generateAttestationsTransaction =\n        await hydraS1AccountboundAttester.generateAttestations(request, proof.toBytes());\n\n      // 0 - Checks that the transaction emitted the event\n      await expect(generateAttestationsTransaction)\n        .to.emit(hydraS1AccountboundAttester, 'AttestationGenerated')\n        .withArgs([\n          await (\n            await hydraS1AccountboundAttester.AUTHORIZED_COLLECTION_ID_FIRST()\n          ).add(group.properties.groupIndex),\n          request.destination,\n          hydraS1AccountboundAttester.address,\n          sourceValue,\n          group.properties.generationTimestamp,\n          encodeAccountBoundAttestationExtraData({\n            nullifier: inputs.publicInputs.nullifier,\n            burnCount: 0,\n          }),\n        ]);\n\n      // 1 - Checks that the provided nullifier was successfully recorded in the attester\n      expect(\n        await hydraS1AccountboundAttester.getDestinationOfNullifier(\n          BigNumber.from(inputs.publicInputs.nullifier)\n        )\n      ).to.equal(request.destination);\n\n      expect(\n        await hydraS1AccountboundAttester.getNullifierCooldownStart(\n          BigNumber.from(inputs.publicInputs.nullifier)\n        )\n      ).to.be.eql(0);\n\n      expect(\n        await hydraS1AccountboundAttester.getNullifierBurnCount(\n          BigNumber.from(inputs.publicInputs.nullifier)\n        )\n      ).to.be.eql(0);\n\n      // 2 - Checks that the attester recorded the attestation in the registry\n      expect(\n        await attestationsRegistry.hasAttestation(\n          (\n            await hydraS1AccountboundAttester.AUTHORIZED_COLLECTION_ID_FIRST()\n          ).add(group.properties.groupIndex),\n          destinationSigner.address\n        )\n      ).to.be.true;\n    });\n\n    it(\"should get the nullifier and burn count from attestation's extra data\", async () => {\n      const attestationExtraData = await attestationsRegistry.getAttestationExtraData(\n        (\n          await hydraS1AccountboundAttester.AUTHORIZED_COLLECTION_ID_FIRST()\n        ).add(group.properties.groupIndex),\n        destinationSigner.address\n      );\n\n      expect(attestationExtraData).to.be.eql(\n        encodeAccountBoundAttestationExtraData({\n          nullifier: inputs.publicInputs.nullifier,\n          burnCount: 0,\n        })\n      );\n\n      expect(\n        await hydraS1AccountboundAttester.getNullifierFromExtraData(attestationExtraData)\n      ).to.be.eql(BigNumber.from(inputs.publicInputs.nullifier));\n      expect(\n        await hydraS1AccountboundAttester.getBurnCountFromExtraData(attestationExtraData)\n      ).to.be.eql(0);\n    });\n\n    it('Should revert if the groupIndex has no cooldownDuration set', async () => {\n      evmSnapshotId = await evmSnapshot(hre);\n\n      await hydraS1AccountboundAttester\n        .connect(deployer)\n        .setCooldownDurationForGroupIndex(group.properties.groupIndex, 0);\n\n      const newProof = await prover.generateSnarkProof({\n        ...userParams,\n        destination: destination2,\n      });\n\n      const newRequest = {\n        ...request,\n        destination: BigNumber.from(destination2.identifier).toHexString(),\n      };\n\n      await expect(\n        hydraS1AccountboundAttester.generateAttestations(newRequest, newProof.toBytes())\n      ).to.be.revertedWith(\n        `CooldownDurationNotSetForGroupIndex(${BigNumber.from(group.properties.groupIndex)})`\n      );\n\n      evmRevert(hre, evmSnapshotId);\n    });\n\n    it('Should be able to change the destination, deleting the old attestation (since the cooldown duration is zero)', async () => {\n      const newProof = await prover.generateSnarkProof({\n        ...userParams,\n        destination: destination2,\n      });\n\n      const newRequest = {\n        ...request,\n        destination: BigNumber.from(destination2.identifier).toHexString(),\n      };\n\n      const generateAttestationsTransaction =\n        await hydraS1AccountboundAttester.generateAttestations(newRequest, newProof.toBytes());\n\n      // 0 - Checks that the transaction emitted the event\n      await expect(generateAttestationsTransaction)\n        .to.emit(hydraS1AccountboundAttester, 'AttestationGenerated')\n        .withArgs([\n          await (\n            await hydraS1AccountboundAttester.AUTHORIZED_COLLECTION_ID_FIRST()\n          ).add(group.properties.groupIndex),\n          newRequest.destination,\n          hydraS1AccountboundAttester.address,\n          sourceValue,\n          group.properties.generationTimestamp,\n          encodeAccountBoundAttestationExtraData({\n            nullifier: inputs.publicInputs.nullifier,\n            burnCount: 1, // burn count should be incremented\n          }),\n        ]);\n      await expect(generateAttestationsTransaction)\n        .to.emit(hydraS1AccountboundAttester, 'NullifierDestinationUpdated')\n        .withArgs(inputs.publicInputs.nullifier, destination2.identifier);\n      await expect(generateAttestationsTransaction)\n        .to.emit(hydraS1AccountboundAttester, 'NullifierSetOnCooldown')\n        .withArgs(inputs.publicInputs.nullifier, 1);\n\n      // 1 - Checks that the nullifier informations were successfully updated\n      expect(\n        await hydraS1AccountboundAttester.getDestinationOfNullifier(\n          BigNumber.from(inputs.publicInputs.nullifier)\n        )\n      ).to.be.equal(BigNumber.from(destination2.identifier));\n\n      expect(\n        await hydraS1AccountboundAttester.getNullifierCooldownStart(\n          BigNumber.from(inputs.publicInputs.nullifier)\n        )\n      ).to.be.eql((await ethers.provider.getBlock('latest')).timestamp);\n\n      expect(\n        await hydraS1AccountboundAttester.getNullifierBurnCount(\n          BigNumber.from(inputs.publicInputs.nullifier)\n        )\n      ).to.be.eql(1); // the burnCount should be incremented\n\n      // burnCount should be recorded in the attestationsRegistry\n      expect(\n        await attestationsRegistry.getAttestationExtraData(\n          (\n            await hydraS1AccountboundAttester.AUTHORIZED_COLLECTION_ID_FIRST()\n          ).add(group.properties.groupIndex),\n          destination2Signer.address\n        )\n      ).to.equal(\n        encodeAccountBoundAttestationExtraData({\n          nullifier: inputs.publicInputs.nullifier,\n          burnCount: 1, // burnCount should be incremented\n        })\n      );\n      // 2 - Checks that the attester unrecorded & rerecorded the attestation in the registry\n      // 2.1 - Checks that the old destination has not anymore it's attestation\n      expect(\n        await attestationsRegistry.hasAttestation(\n          (\n            await hydraS1AccountboundAttester.AUTHORIZED_COLLECTION_ID_FIRST()\n          ).add(group.properties.groupIndex),\n          destinationSigner.address\n        )\n      ).to.be.false;\n\n      // 2.2 - Checks that the new destination has it's attestation\n      expect(\n        await attestationsRegistry.hasAttestation(\n          (\n            await hydraS1AccountboundAttester.AUTHORIZED_COLLECTION_ID_FIRST()\n          ).add(group.properties.groupIndex),\n          destination2Signer.address\n        )\n      ).to.be.true;\n    });\n\n    it('Should revert if the nullifier is in cooldown', async () => {\n      const burnCount = await hydraS1AccountboundAttester.getNullifierBurnCount(\n        BigNumber.from(inputs.publicInputs.nullifier)\n      );\n\n      const destination = await hydraS1AccountboundAttester.getDestinationOfNullifier(\n        BigNumber.from(inputs.publicInputs.nullifier)\n      );\n\n      await expect(\n        hydraS1AccountboundAttester.generateAttestations(request, proof.toBytes())\n      ).to.be.revertedWith(\n        `NullifierOnCooldown(${\n          inputs.publicInputs.nullifier\n        }, \"${destination}\", ${burnCount}, ${await hydraS1AccountboundAttester.getCooldownDurationForGroupIndex(\n          group.properties.groupIndex\n        )})`\n      );\n    });\n\n    it('Should renew the timestamp even if the nullifier is in cooldown', async () => {\n      const evmSnapshotId = await evmSnapshot(hre);\n      const latestCooldownStart = await hydraS1AccountboundAttester.getNullifierCooldownStart(\n        BigNumber.from(inputs.publicInputs.nullifier)\n      );\n\n      const renewGenerationTimestamp = group.properties.generationTimestamp + 10;\n      // regenerate groups for attester with different timestamp\n      const { dataFormat, groups } = await generateAttesterGroups(allAvailableGroups, {\n        generationTimestamp: renewGenerationTimestamp,\n      });\n      // register new registry tree root on chain\n      await availableRootsRegistry.registerRootForAttester(\n        hydraS1AccountboundAttester.address,\n        dataFormat.registryTree.getRoot()\n      );\n\n      // create new prover using the new registryTree\n      const renewProver = new HydraS1Prover(dataFormat.registryTree, commitmentMapperPubKey);\n      const renewProof = await renewProver.generateSnarkProof({\n        ...userParams,\n        destination: destination2,\n        accountsTree: dataFormat.accountsTrees[0],\n      });\n\n      const renewRequest = {\n        claims: [\n          {\n            groupId: groups[0].id,\n            claimedValue: sourceValue,\n            extraData: encodeGroupProperties({\n              ...group.properties,\n              generationTimestamp: renewGenerationTimestamp,\n            }),\n          },\n        ],\n        destination: BigNumber.from(destination2.identifier).toHexString(),\n      };\n\n      const generateAttestationsTransaction =\n        await hydraS1AccountboundAttester.generateAttestations(renewRequest, renewProof.toBytes());\n\n      // 0 - Checks that the transaction emitted the event with the new timestamp\n      await expect(generateAttestationsTransaction)\n        .to.emit(hydraS1AccountboundAttester, 'AttestationGenerated')\n        .withArgs([\n          await (\n            await hydraS1AccountboundAttester.AUTHORIZED_COLLECTION_ID_FIRST()\n          ).add(group.properties.groupIndex),\n          renewRequest.destination,\n          hydraS1AccountboundAttester.address,\n          sourceValue,\n          renewGenerationTimestamp,\n          encodeAccountBoundAttestationExtraData({\n            nullifier: inputs.publicInputs.nullifier,\n            burnCount: 1,\n          }),\n        ]);\n\n      // A renew should not have changed the nullifierData\n      // cooldownStart and burnCount should be the same\n      expect(\n        await hydraS1AccountboundAttester.getDestinationOfNullifier(\n          BigNumber.from(inputs.publicInputs.nullifier)\n        )\n      ).to.be.eql(BigNumber.from(destination2.identifier).toHexString());\n\n      expect(\n        await hydraS1AccountboundAttester.getNullifierCooldownStart(\n          BigNumber.from(inputs.publicInputs.nullifier)\n        )\n      ).to.be.eql(latestCooldownStart);\n\n      expect(\n        await hydraS1AccountboundAttester.getNullifierBurnCount(\n          BigNumber.from(inputs.publicInputs.nullifier)\n        )\n      ).to.be.eql(1);\n\n      // 2 - Checks that the attestation in the registry has the new timestamp\n      expect(\n        await attestationsRegistry.getAttestationTimestamp(\n          (\n            await hydraS1AccountboundAttester.AUTHORIZED_COLLECTION_ID_FIRST()\n          ).add(group.properties.groupIndex),\n          destination2Signer.address\n        )\n      ).to.equal(renewGenerationTimestamp);\n\n      evmRevert(hre, evmSnapshotId);\n    });\n\n    it('Should be able to update the claimedValue to zero even if the nullifier is in cooldown', async () => {\n      const evmSnapshotId = await evmSnapshot(hre);\n      // regenerate groups for attester with different timestamp\n      const { dataFormat, groups } = await generateAttesterGroups(allAvailableGroups, {\n        isScore: true,\n        generationTimestamp: group.properties.generationTimestamp,\n      });\n      // register new registry tree root on chain\n      await availableRootsRegistry.registerRootForAttester(\n        hydraS1AccountboundAttester.address,\n        dataFormat.registryTree.getRoot()\n      );\n\n      // create new prover using the new registryTree\n      const renewProver = new HydraS1Prover(dataFormat.registryTree, commitmentMapperPubKey);\n      const renewProof = await renewProver.generateSnarkProof({\n        ...userParams,\n        destination: destination2,\n        accountsTree: dataFormat.accountsTrees[0],\n        isStrict: false,\n        claimedValue: 0,\n      });\n\n      const renewRequest = {\n        claims: [\n          {\n            groupId: groups[0].id,\n            claimedValue: 0,\n            extraData: encodeGroupProperties(groups[0].properties),\n          },\n        ],\n        destination: BigNumber.from(destination2.identifier).toHexString(),\n      };\n\n      const generateAttestationsTransaction =\n        await hydraS1AccountboundAttester.generateAttestations(renewRequest, renewProof.toBytes());\n\n      // Checks that the transaction emitted the event with a claimedValue of zero\n      await expect(generateAttestationsTransaction)\n        .to.emit(hydraS1AccountboundAttester, 'AttestationGenerated')\n        .withArgs([\n          await (\n            await hydraS1AccountboundAttester.AUTHORIZED_COLLECTION_ID_FIRST()\n          ).add(group.properties.groupIndex),\n          renewRequest.destination,\n          hydraS1AccountboundAttester.address,\n          0, // claimedValue\n          group.properties.generationTimestamp,\n          encodeAccountBoundAttestationExtraData({\n            nullifier: inputs.publicInputs.nullifier,\n            burnCount: 1,\n          }),\n        ]);\n\n      //  Checks that the attestation in the registry has a value of zero\n      expect(\n        await attestationsRegistry.getAttestationValue(\n          (\n            await hydraS1AccountboundAttester.AUTHORIZED_COLLECTION_ID_FIRST()\n          ).add(group.properties.groupIndex),\n          destination2Signer.address\n        )\n      ).to.equal(0);\n\n      evmRevert(hre, evmSnapshotId);\n    });\n\n    it('Should revert because the cooldown period is not yet finished', async () => {\n      // not yet the end of the cooldown period\n      await increaseTime(hre, cooldownDuration - 10);\n\n      await expect(\n        hydraS1AccountboundAttester.generateAttestations(request, proof.toBytes())\n      ).to.be.revertedWith(\n        `NullifierOnCooldown(${\n          inputs.publicInputs.nullifier\n        }, \"${await hydraS1AccountboundAttester.getDestinationOfNullifier(\n          BigNumber.from(inputs.publicInputs.nullifier)\n        )}\", ${1}, ${cooldownDuration})`\n      );\n    });\n\n    it('Should be able to change again the destination after the cooldown period', async () => {\n      const evmSnapshotId = await evmSnapshot(hre);\n      await increaseTime(hre, cooldownDuration);\n\n      const generateAttestationsTransaction = hydraS1AccountboundAttester.generateAttestations(\n        request,\n        proof.toBytes()\n      );\n\n      // 0 - Checks that the transaction emitted the event\n      await expect(generateAttestationsTransaction)\n        .to.emit(hydraS1AccountboundAttester, 'AttestationGenerated')\n        .withArgs([\n          await (\n            await hydraS1AccountboundAttester.AUTHORIZED_COLLECTION_ID_FIRST()\n          ).add(group.properties.groupIndex),\n        ]);\n\n      // 1 - Checks that the nullifier information were successfully updated\n      expect(\n        await hydraS1AccountboundAttester.getDestinationOfNullifier(\n          BigNumber.from(inputs.publicInputs.nullifier)\n        )\n      ).to.be.equal(destination.identifier);\n\n      // cooldownStart should be reset to the latest block timestamp\n      // and burnCount incremented by 1\n      expect(\n        await hydraS1AccountboundAttester.getNullifierCooldownStart(\n          BigNumber.from(inputs.publicInputs.nullifier)\n        )\n      ).to.be.eql((await ethers.provider.getBlock('latest')).timestamp);\n\n      expect(\n        await hydraS1AccountboundAttester.getNullifierBurnCount(\n          BigNumber.from(inputs.publicInputs.nullifier)\n        )\n      ).to.be.eql(2); // burnCount should be incremented\n\n      // 2 - Checks that the attester unrecorded & rerecorded the attestation in the registry\n      // 2.1 - Checks that the old destination has not anymore it's attestation\n      expect(\n        await attestationsRegistry.hasAttestation(\n          (\n            await hydraS1AccountboundAttester.AUTHORIZED_COLLECTION_ID_FIRST()\n          ).add(group.properties.groupIndex),\n          destination2Signer.address\n        )\n      ).to.be.false;\n\n      // 2.2 - Checks that the new destination has it's attestation\n      expect(\n        await attestationsRegistry.hasAttestation(\n          (\n            await hydraS1AccountboundAttester.AUTHORIZED_COLLECTION_ID_FIRST()\n          ).add(group.properties.groupIndex),\n          destinationSigner.address\n        )\n      ).to.be.true;\n      evmRevert(hre, evmSnapshotId);\n    });\n\n    it('Should be able to change again the destination after the cooldown period with a recomputed group', async () => {\n      const evmSnapshotId = await evmSnapshot(hre);\n      await increaseTime(hre, cooldownDuration);\n\n      const renewGenerationTimestamp = group.properties.generationTimestamp + 10;\n      // regenerate groups for attester with different timestamp\n      const { dataFormat, groups } = await generateAttesterGroups(allAvailableGroups, {\n        generationTimestamp: renewGenerationTimestamp,\n      });\n      // register new registry tree root on chain\n      await availableRootsRegistry.registerRootForAttester(\n        hydraS1AccountboundAttester.address,\n        dataFormat.registryTree.getRoot()\n      );\n\n      // create new prover using the new registryTree\n      const renewProver = new HydraS1Prover(dataFormat.registryTree, commitmentMapperPubKey);\n      const renewProof = await renewProver.generateSnarkProof({\n        ...userParams,\n        destination: destination,\n        accountsTree: dataFormat.accountsTrees[0],\n      });\n\n      const renewRequest = {\n        claims: [\n          {\n            groupId: groups[0].id,\n            claimedValue: sourceValue,\n            extraData: encodeGroupProperties({\n              ...group.properties,\n              generationTimestamp: renewGenerationTimestamp,\n            }),\n          },\n        ],\n        destination: BigNumber.from(destination.identifier).toHexString(),\n      };\n\n      const generateAttestationsTransaction =\n        await hydraS1AccountboundAttester.generateAttestations(renewRequest, renewProof.toBytes());\n\n      // 0 - Checks that the transaction emitted the event\n      await expect(generateAttestationsTransaction)\n        .to.emit(hydraS1AccountboundAttester, 'AttestationGenerated')\n        .withArgs([\n          await (\n            await hydraS1AccountboundAttester.AUTHORIZED_COLLECTION_ID_FIRST()\n          ).add(group.properties.groupIndex),\n        ]);\n\n      // 1 - Checks that the nullifier information were successfully updated\n      expect(\n        await hydraS1AccountboundAttester.getDestinationOfNullifier(\n          BigNumber.from(inputs.publicInputs.nullifier)\n        )\n      ).to.be.equal(destination.identifier);\n\n      // cooldownStart should be reset to the latest block timestamp\n      // and burnCount incremented by 1\n      expect(\n        await hydraS1AccountboundAttester.getNullifierCooldownStart(\n          BigNumber.from(inputs.publicInputs.nullifier)\n        )\n      ).to.be.eql((await ethers.provider.getBlock('latest')).timestamp);\n\n      expect(\n        await hydraS1AccountboundAttester.getNullifierBurnCount(\n          BigNumber.from(inputs.publicInputs.nullifier)\n        )\n      ).to.be.eql(2); // burnCount should be incremented\n\n      // 2 - Checks that the attester unrecorded & rerecorded the attestation in the registry\n      // 2.1 - Checks that the old destination has not anymore it's attestation\n      expect(\n        await attestationsRegistry.hasAttestation(\n          (\n            await hydraS1AccountboundAttester.AUTHORIZED_COLLECTION_ID_FIRST()\n          ).add(group.properties.groupIndex),\n          destination2Signer.address\n        )\n      ).to.be.false;\n\n      // 2.2 - Checks that the new destination has it's attestation\n      expect(\n        await attestationsRegistry.hasAttestation(\n          (\n            await hydraS1AccountboundAttester.AUTHORIZED_COLLECTION_ID_FIRST()\n          ).add(group.properties.groupIndex),\n          destinationSigner.address\n        )\n      ).to.be.true;\n      evmRevert(hre, evmSnapshotId);\n    });\n  });\n});\n"
  },
  {
    "path": "test/unit/attesters/hydra-s1/hydra-s1-simple-attester.test.ts",
    "content": "import { expect } from 'chai';\nimport hre from 'hardhat';\nimport {\n  AttestationsRegistry,\n  AvailableRootsRegistry,\n  Badges,\n  CommitmentMapperRegistry,\n  HydraS1SimpleAttester,\n} from 'types';\nimport { RequestStruct } from 'types/Attester';\n\nimport { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers';\nimport { CommitmentMapperTester } from '@sismo-core/commitment-mapper-tester-js';\nimport {\n  EddsaPublicKey,\n  HydraS1Account,\n  HydraS1Prover,\n  KVMerkleTree,\n  SnarkProof,\n  SNARK_FIELD,\n} from '@sismo-core/hydra-s1';\nimport { BigNumber } from 'ethers';\nimport { Deployed0 } from 'tasks/deploy-tasks/full/0-deploy-core-and-hydra-s1-simple-and-accountbound-and-pythia1.task';\nimport { deploymentsConfig } from '../../../../tasks/deploy-tasks/deployments-config';\nimport {\n  encodeGroupProperties,\n  generateAttesterGroups,\n  generateGroupIdFromEncodedProperties,\n  generateGroupIdFromProperties,\n  generateHydraS1Accounts,\n  generateGroups,\n  generateExternalNullifier,\n  HydraS1SimpleGroup,\n  toBytes,\n} from '../../../utils';\nimport { getEventArgs } from '../../../utils/expectEvent';\n\nconst config = deploymentsConfig[hre.network.name];\nconst collectionIdFirst = BigNumber.from(config.hydraS1SimpleAttester.collectionIdFirst);\nconst collectionIdLast = BigNumber.from(config.hydraS1SimpleAttester.collectionIdLast);\n\ndescribe('Test Hydra S1 standard attester contract, not strict', () => {\n  let chainId: number;\n\n  // contracts\n  let attestationsRegistry: AttestationsRegistry;\n  let hydraS1SimpleAttester: HydraS1SimpleAttester;\n  let availableRootsRegistry: AvailableRootsRegistry;\n  let commitmentMapperRegistry: CommitmentMapperRegistry;\n  let badges: Badges;\n\n  // hydra s1 prover\n  let prover: HydraS1Prover;\n  let commitmentMapper: CommitmentMapperTester;\n  let commitmentMapperPubKey: EddsaPublicKey;\n\n  let registryTree: KVMerkleTree;\n\n  // Test Signers\n  let deployer: SignerWithAddress;\n  let randomSigner: SignerWithAddress;\n  let proxyAdminSigner: SignerWithAddress;\n\n  // Test accounts\n  let source1: HydraS1Account;\n  let destination1: HydraS1Account;\n  let destination2: HydraS1Account;\n\n  // Data source test\n  let source1Value: BigNumber;\n  let accountsTree1: KVMerkleTree;\n  let accountsTree2: KVMerkleTree;\n  let group1: HydraS1SimpleGroup;\n  let group2: HydraS1SimpleGroup;\n\n  // Valid request and proof\n  let request: RequestStruct;\n  let proof: SnarkProof;\n  let externalNullifier: BigNumber;\n\n  before(async () => {\n    const signers = await hre.ethers.getSigners();\n    [deployer, randomSigner, proxyAdminSigner] = signers;\n    chainId = parseInt(await hre.getChainId());\n\n    // 1 - Init commitment mapper\n    commitmentMapper = await CommitmentMapperTester.generate();\n    commitmentMapperPubKey = await commitmentMapper.getPubKey();\n\n    // 2 - Generate Hydra S1 Accounts with the commitment mapper\n    let hydraS1Accounts: HydraS1Account[] = await generateHydraS1Accounts(\n      signers,\n      commitmentMapper\n    );\n\n    source1 = hydraS1Accounts[0];\n    destination1 = hydraS1Accounts[1];\n    destination2 = hydraS1Accounts[3];\n\n    // 3 - Generate data source\n    const allList = await generateGroups(hydraS1Accounts);\n    const { dataFormat, groups } = await generateAttesterGroups(allList);\n\n    registryTree = dataFormat.registryTree;\n    accountsTree1 = dataFormat.accountsTrees[0];\n    accountsTree2 = dataFormat.accountsTrees[1];\n    group1 = groups[0];\n    group2 = groups[1];\n    source1Value = accountsTree1.getValue(BigNumber.from(source1.identifier).toHexString());\n\n    // 4 - Init Proving scheme\n    prover = new HydraS1Prover(registryTree, commitmentMapperPubKey);\n  });\n\n  /*************************************************************************************/\n  /********************************** DEPLOYMENTS **************************************/\n  /*************************************************************************************/\n\n  describe('Deployments', () => {\n    it('Should deploy and setup core', async () => {\n      ({\n        attestationsRegistry,\n        badges,\n        commitmentMapperRegistry,\n        hydraS1SimpleAttester,\n        availableRootsRegistry,\n      } = (await hre.run(\n        '0-deploy-core-and-hydra-s1-simple-and-accountbound-and-pythia1',\n        {}\n      )) as Deployed0);\n      const root = registryTree.getRoot();\n      await availableRootsRegistry.registerRootForAttester(hydraS1SimpleAttester.address, root);\n    });\n  });\n\n  /*************************************************************************************/\n  /***************************** GENERATE VALID ATTESTATION ****************************/\n  /*************************************************************************************/\n\n  describe('Generate valid attestation', () => {\n    it('Should generate a proof with a Hydra S1 prover and verify it onchain using the attester', async () => {\n      externalNullifier = await generateExternalNullifier(\n        hydraS1SimpleAttester.address,\n        group1.properties.groupIndex\n      );\n\n      request = {\n        claims: [\n          {\n            groupId: group1.id,\n            claimedValue: source1Value,\n            extraData: encodeGroupProperties(group1.properties),\n          },\n        ],\n        destination: BigNumber.from(destination1.identifier).toHexString(),\n      };\n\n      proof = await prover.generateSnarkProof({\n        source: source1,\n        destination: destination1,\n        claimedValue: source1Value,\n        chainId: chainId,\n        accountsTree: accountsTree1,\n        externalNullifier: externalNullifier,\n        isStrict: !group1.properties.isScore,\n      });\n\n      const tx = await hydraS1SimpleAttester.generateAttestations(request, proof.toBytes());\n      const { events } = await tx.wait();\n      const args = getEventArgs(events, 'AttestationGenerated');\n\n      expect(args.attestation.issuer).to.equal(hydraS1SimpleAttester.address);\n      expect(args.attestation.owner).to.equal(\n        BigNumber.from(destination1.identifier).toHexString()\n      );\n      expect(args.attestation.collectionId).to.equal(\n        collectionIdFirst.add(group1.properties.groupIndex)\n      );\n      expect(args.attestation.value).to.equal(1);\n      expect(args.attestation.timestamp).to.equal(group1.properties.generationTimestamp);\n    });\n\n    it(\"should get the nullifier from attestation's extra data\", async () => {\n      const attestationExtraData = await attestationsRegistry.getAttestationExtraData(\n        collectionIdFirst.add(group1.properties.groupIndex),\n        BigNumber.from(destination1.identifier).toHexString()\n      );\n\n      expect(\n        await attestationsRegistry.hasAttestation(\n          collectionIdFirst.add(group1.properties.groupIndex),\n          BigNumber.from(destination1.identifier).toHexString().toLowerCase()\n        )\n      ).to.be.true;\n\n      expect(await hydraS1SimpleAttester.getNullifierFromExtraData(attestationExtraData)).to.be.eql(\n        BigNumber.from(proof.input[6])\n      );\n    });\n  });\n\n  /*************************************************************************************/\n  /********************************* VERIFY REQUEST ************************************/\n  /*************************************************************************************/\n\n  describe('Verify request', () => {\n    /****************************************/\n    /************* _validateInput() *********/\n    /****************************************/\n\n    it('Should revert due to accountsTree different from the request', async () => {\n      const wrongProof = await prover.generateSnarkProof({\n        source: source1,\n        destination: destination2,\n        claimedValue: source1Value,\n        chainId: chainId,\n        accountsTree: accountsTree2,\n        externalNullifier: externalNullifier,\n        isStrict: false,\n      });\n\n      await expect(\n        hydraS1SimpleAttester.generateAttestations(request, wrongProof.toBytes())\n      ).to.be.revertedWith(\n        `AccountsTreeValueMismatch(${BigNumber.from(group1.id).toString()}, ${BigNumber.from(\n          group2.id\n        ).toString()})`\n      );\n    });\n\n    it('Should revert due wrong request: user provided wrong group index property', async () => {\n      const wrongRequest = { ...request, claims: [{ ...request.claims[0] }] };\n      const wrongGroupProperties = { ...group1.properties };\n\n      wrongGroupProperties.groupIndex = group2.properties.groupIndex;\n      const wrongEncodedProperties = encodeGroupProperties(wrongGroupProperties);\n      wrongRequest.claims[0].extraData = wrongEncodedProperties;\n      await expect(\n        hydraS1SimpleAttester.generateAttestations(wrongRequest, proof.toBytes())\n      ).to.be.revertedWith(\n        `GroupIdAndPropertiesMismatch(${BigNumber.from(\n          generateGroupIdFromEncodedProperties(wrongEncodedProperties)\n        ).toString()}, ${BigNumber.from(group1.id)})`\n      );\n    });\n\n    it('Should revert due wrong request: user provided wrong generation timestamp property', async () => {\n      const wrongRequest = { ...request, claims: [{ ...request.claims[0] }] };\n      const wrongGroupProperties = { ...group1.properties };\n\n      wrongGroupProperties.generationTimestamp = group2.properties.generationTimestamp;\n      const wrongEncodedProperties = encodeGroupProperties(wrongGroupProperties);\n      wrongRequest.claims[0].extraData = wrongEncodedProperties;\n      await expect(\n        hydraS1SimpleAttester.generateAttestations(wrongRequest, proof.toBytes())\n      ).to.be.revertedWith(\n        `GroupIdAndPropertiesMismatch(${BigNumber.from(\n          generateGroupIdFromEncodedProperties(wrongEncodedProperties)\n        ).toString()}, ${BigNumber.from(group1.id)})`\n      );\n    });\n\n    it('Should revert due wrong request: user provided wrong isScore property', async () => {\n      const wrongRequest = { ...request, claims: [{ ...request.claims[0] }] };\n      const wrongGroupProperties = { ...group1.properties };\n\n      wrongGroupProperties.isScore = !group1.properties.isScore;\n      const wrongEncodedProperties = encodeGroupProperties(wrongGroupProperties);\n      wrongRequest.claims[0].extraData = wrongEncodedProperties;\n      await expect(\n        hydraS1SimpleAttester.generateAttestations(wrongRequest, proof.toBytes())\n      ).to.be.revertedWith(\n        `GroupIdAndPropertiesMismatch(${BigNumber.from(\n          generateGroupIdFromEncodedProperties(wrongEncodedProperties)\n        ).toString()}, ${BigNumber.from(group1.id)})`\n      );\n    });\n\n    it('Should revert due wrong request: groupId does not correspond to provided properties in extraData', async () => {\n      const wrongRequest = { ...request, claims: [{ ...request.claims[0] }] };\n      const wrongGroupId = generateGroupIdFromProperties({ ...group2.properties });\n      wrongRequest.claims[0].groupId = wrongGroupId;\n\n      await expect(\n        hydraS1SimpleAttester.generateAttestations(wrongRequest, proof.toBytes())\n      ).to.be.revertedWith(\n        `GroupIdAndPropertiesMismatch(${BigNumber.from(group1.id).toString()}, ${wrongGroupId})`\n      );\n    });\n\n    it('Should revert due to input proof destination not the same as destination', async () => {\n      const wrongProof = await prover.generateSnarkProof({\n        source: source1,\n        destination: destination2,\n        claimedValue: source1Value,\n        chainId: chainId,\n        accountsTree: accountsTree1,\n        externalNullifier: externalNullifier,\n        isStrict: !group1.properties.isScore,\n      });\n\n      await expect(\n        hydraS1SimpleAttester.generateAttestations(request, wrongProof.toBytes())\n      ).to.be.revertedWith(\n        `DestinationMismatch(\"${BigNumber.from(\n          destination1.identifier\n        ).toHexString()}\", \"${BigNumber.from(destination2.identifier).toHexString()}\")`\n      );\n    });\n\n    it('Should revert due to chain id mismatch', async () => {\n      const wrongProof = { ...proof, input: [...proof.input] };\n      wrongProof.input[1] = BigNumber.from(123);\n      const proofBytes = toBytes(wrongProof);\n\n      await expect(\n        hydraS1SimpleAttester.generateAttestations(request, proofBytes)\n      ).to.be.revertedWith(`ChainIdMismatch(${parseInt(await hre.getChainId())}, 123)`);\n    });\n\n    it('Should revert due to input value not the same as claimValue', async () => {\n      const requestWrong = { ...request, claims: [{ ...request.claims[0] }] };\n      const wrongClaimValue = 1000;\n      requestWrong.claims[0].claimedValue = wrongClaimValue;\n      await expect(\n        hydraS1SimpleAttester.generateAttestations(requestWrong, proof.toBytes())\n      ).to.be.revertedWith(`ValueMismatch(${wrongClaimValue}, ${request.claims[0].claimedValue})`);\n    });\n\n    it('Should revert due to registry roots mismatch', async () => {\n      const wrongProof = { ...proof, input: [...proof.input] };\n      wrongProof.input[4] = BigNumber.from(123);\n      const proofBytes = toBytes(wrongProof);\n\n      await expect(\n        hydraS1SimpleAttester.generateAttestations(request, proofBytes)\n      ).to.be.revertedWith(`RegistryRootMismatch(123)`);\n    });\n\n    it('Should revert due to commitment mapper public key mismatch', async () => {\n      // override the approver publicKey with a bad one\n      const wrongProof = { ...proof, input: [...proof.input] };\n      wrongProof.input[2] = BigNumber.from(123);\n      const proofBytes = toBytes(wrongProof);\n\n      await expect(\n        hydraS1SimpleAttester.generateAttestations(request, proofBytes)\n      ).to.be.revertedWith(\n        `CommitmentMapperPubKeyMismatch(${commitmentMapperPubKey[0].toString()}, ${commitmentMapperPubKey[1].toString()}, 123, ${\n          wrongProof.input[3]\n        })`\n      );\n    });\n\n    it('Should revert due to wrong external nullifier', async () => {\n      const wrongExternalNullifier = 123;\n\n      const proof2 = await prover.generateSnarkProof({\n        source: source1,\n        destination: destination1,\n        claimedValue: source1Value,\n        chainId: chainId,\n        accountsTree: accountsTree1,\n        externalNullifier: wrongExternalNullifier,\n        isStrict: !group1.properties.isScore,\n      });\n\n      await expect(\n        hydraS1SimpleAttester.generateAttestations(request, proof2.toBytes())\n      ).to.be.revertedWith(\n        `ExternalNullifierMismatch(${externalNullifier}, ${wrongExternalNullifier})`\n      );\n    });\n\n    /****************************************/\n    /************** _verifyProof() **********/\n    /****************************************/\n\n    it('Should not allow snark field overflow by providing a nullifier that is outside the snark field', async () => {\n      // override the nullifier proof to overflow\n      const wrongProof = { ...proof, input: [...proof.input] };\n      wrongProof.input[6] = BigNumber.from(wrongProof.input[6]).add(SNARK_FIELD);\n\n      const proofBytes = toBytes(wrongProof);\n\n      await expect(\n        hydraS1SimpleAttester.generateAttestations(request, proofBytes)\n      ).to.be.revertedWith(`InvalidGroth16Proof(\"verifier-gte-snark-scalar-field\")`);\n    });\n    it('Should revert if wrong snark proof', async () => {\n      // override the nullifier proof to overflow\n      const wrongProof = { ...proof, a: [...proof.a] };\n      wrongProof.a[0] = BigNumber.from(proof.a[0]).sub(1);\n\n      const proofBytes = toBytes(wrongProof);\n\n      await expect(\n        hydraS1SimpleAttester.generateAttestations(request, proofBytes)\n      ).to.be.revertedWith(`InvalidGroth16Proof(\"\")`);\n    });\n  });\n\n  /*************************************************************************************/\n  /************************** BEFORE RECORD ATTESTATION ********************************/\n  /*************************************************************************************/\n\n  describe('Before record attestation', () => {\n    it('Should revert due to nullifier hash having already been used', async () => {\n      const wrongRequest = { ...request };\n      wrongRequest.destination = BigNumber.from(destination2.identifier).toHexString();\n\n      const proof2 = await prover.generateSnarkProof({\n        source: source1,\n        destination: destination2,\n        claimedValue: source1Value,\n        chainId: chainId,\n        accountsTree: accountsTree1,\n        externalNullifier: externalNullifier,\n        isStrict: !group1.properties.isScore,\n      });\n\n      await expect(\n        hydraS1SimpleAttester.generateAttestations(wrongRequest, proof2.toBytes())\n      ).to.be.revertedWith(`NullifierUsed(${proof.input[6]})`);\n    });\n  });\n});\n"
  },
  {
    "path": "test/unit/attesters/pythia-1/pythia-1-simple-attester.test.ts",
    "content": "import { expect } from 'chai';\nimport hre from 'hardhat';\nimport { AttestationsRegistry, Badges, Pythia1SimpleAttester } from 'types';\nimport { RequestStruct } from 'types/Attester';\n\nimport { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers';\nimport { BigNumber } from 'ethers';\nimport { deploymentsConfig } from '../../../../tasks/deploy-tasks/deployments-config';\nimport { generateExternalNullifier } from '../../../utils';\nimport { getEventArgs } from '../../../utils/expectEvent';\nimport {\n  CommitmentSignerTester,\n  encodePythia1GroupProperties,\n  generatePythia1Group,\n  Pythia1Group,\n} from '../../../utils/pythia-1';\nimport {\n  DeployedPythia1SimpleAttester,\n  DeployPythia1SimpleAttesterArgs,\n} from '../../../../tasks/deploy-tasks/unit/attesters/pythia-1/deploy-pythia-1-simple-attester.task';\nimport { Pythia1Prover, SnarkProof, EddsaSignature, buildPoseidon } from '@sismo-core/pythia-1';\n\nconst collectionIdFirst = BigNumber.from(100);\nconst collectionIdLast = BigNumber.from(1000);\n\ndescribe('Test pythia 1 standard attester contract, not strict', () => {\n  let chainId: number;\n  let poseidon;\n\n  // contracts\n  let pythia1SimpleAttester: Pythia1SimpleAttester;\n  let attestationsRegistry: AttestationsRegistry;\n  let badges: Badges;\n\n  // pythia s1 prover\n  let prover: Pythia1Prover;\n  let commitmentSigner: CommitmentSignerTester;\n\n  // Test Signers\n  let deployer: SignerWithAddress;\n  let destination1: SignerWithAddress;\n\n  // Valid request and proof\n  let request: RequestStruct;\n  let proof: SnarkProof;\n  let externalNullifier: BigNumber;\n\n  // Data source test\n  let secret: BigNumber;\n  let commitment: BigNumber;\n  let commitmentReceipt: EddsaSignature;\n  let source1Value: BigNumber;\n  let group1: Pythia1Group;\n\n  before(async () => {\n    const signers = await hre.ethers.getSigners();\n    [deployer, destination1] = signers;\n    chainId = parseInt(await hre.getChainId());\n\n    poseidon = await buildPoseidon();\n\n    // 1 - Init commitment signer\n    commitmentSigner = new CommitmentSignerTester();\n\n    // 2 - Generate the group with its properties\n    group1 = generatePythia1Group({\n      internalCollectionId: 0,\n      isScore: false,\n    });\n\n    // 3 - Get the commitmentReceipt by passing through the commitment signer\n    secret = BigNumber.from('0x123');\n    commitment = poseidon([secret]);\n    source1Value = BigNumber.from('0x9');\n    commitmentReceipt = await commitmentSigner.getCommitmentReceipt(\n      commitment,\n      source1Value,\n      group1.id\n    );\n\n    // 4 - Init Proving scheme\n    prover = new Pythia1Prover();\n  });\n\n  /*************************************************************************************/\n  /********************************** DEPLOYMENTS **************************************/\n  /*************************************************************************************/\n\n  describe('Deployments', () => {\n    it('Should deploy and setup core', async () => {\n      const { mockAttestationsRegistry } = await hre.run('deploy-mock-attestations-registry', {\n        attestationValue: 1,\n        options: {\n          behindProxy: false,\n        },\n      });\n      const commitmentSignerPubKey = await commitmentSigner.getPublicKey();\n      ({ pythia1SimpleAttester } = (await hre.run('deploy-pythia-1-simple-attester', {\n        attestationsRegistryAddress: mockAttestationsRegistry.address,\n        commitmentSignerPubKeyX: commitmentSignerPubKey[0].toHexString(),\n        commitmentSignerPubKeyY: commitmentSignerPubKey[1].toHexString(),\n        collectionIdFirst: collectionIdFirst.toString(),\n        collectionIdLast: collectionIdFirst.toString(),\n      } as DeployPythia1SimpleAttesterArgs)) as DeployedPythia1SimpleAttester);\n    });\n  });\n\n  describe('Configuration checks', () => {\n    it('Should have setup the owner correctly', async () => {\n      expect(await pythia1SimpleAttester.owner()).to.be.eql(deployer.address);\n    });\n\n    it('Should get the owner correctly', async () => {\n      expect(await pythia1SimpleAttester.owner()).to.be.eql(deployer.address);\n    });\n\n    it('Should revert when trying to call initialize again', async () => {\n      const commitmentSignerPubKey = await commitmentSigner.getPublicKey();\n      await expect(\n        pythia1SimpleAttester.connect(deployer).initialize(commitmentSignerPubKey, deployer.address)\n      ).to.be.revertedWith('Initializable: contract is already initialized');\n    });\n  });\n\n  /*************************************************************************************/\n  /***************************** GENERATE VALID ATTESTATION ****************************/\n  /*************************************************************************************/\n\n  describe('Generate valid attestation', () => {\n    it('Should generate a proof with a pythia 1 prover and verify it onchain using the attester', async () => {\n      externalNullifier = await generateExternalNullifier(\n        pythia1SimpleAttester.address,\n        group1.properties.internalCollectionId\n      );\n\n      request = {\n        claims: [\n          {\n            groupId: group1.id,\n            claimedValue: source1Value,\n            extraData: encodePythia1GroupProperties(group1.properties),\n          },\n        ],\n        destination: BigNumber.from(destination1.address).toHexString(),\n      };\n\n      proof = (await prover.generateSnarkProof({\n        secret: secret,\n        value: source1Value,\n        commitmentReceipt: commitmentReceipt,\n        commitmentSignerPubKey: await commitmentSigner.getPublicKey(),\n        destinationIdentifier: destination1.address,\n        claimedValue: source1Value,\n        chainId: chainId,\n        groupId: group1.id,\n        ticketIdentifier: externalNullifier,\n        isStrict: !group1.properties.isScore,\n      })) as SnarkProof;\n\n      const tx = await pythia1SimpleAttester\n        .connect(destination1)\n        .generateAttestations(request, await proof.toBytes());\n      const { events } = await tx.wait();\n      const args = getEventArgs(events, 'AttestationGenerated');\n      expect(args.attestation.issuer).to.equal(pythia1SimpleAttester.address);\n      expect(args.attestation.owner).to.equal(destination1.address);\n      expect(args.attestation.collectionId).to.equal(\n        collectionIdFirst.add(group1.properties.internalCollectionId)\n      );\n      expect(args.attestation.value).to.equal(source1Value);\n      // expect(args.attestation.timestamp).to.equal(tx.timestamp);\n    }).timeout(60000);\n  });\n});\n"
  },
  {
    "path": "test/unit/core/attestations-registry/attestations-registry-config-logic.test.ts",
    "content": "import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers';\nimport { expect } from 'chai';\nimport hre, { ethers } from 'hardhat';\nimport { getImplementation } from '../../../../utils';\nimport { deploymentsConfig } from '../../../../tasks/deploy-tasks/deployments-config';\nimport {\n  AttestationsRegistry,\n  Badges,\n  TransparentUpgradeableProxy__factory,\n} from '../../../../types';\nimport { formatBytes32String, parseBytes32String } from 'ethers/lib/utils';\nimport { evmRevert, evmSnapshot } from '../../../../test/utils';\n\ndescribe('Test Attestations Registry Config Logic contract', () => {\n  let deployer: SignerWithAddress;\n  let secondDeployer: SignerWithAddress;\n  let notOwner: SignerWithAddress;\n  let issuer: SignerWithAddress;\n\n  let attestationsRegistry: AttestationsRegistry;\n  let secondAttestationsRegistry: AttestationsRegistry;\n  let badges: Badges;\n\n  let snapshotId: string;\n\n  before(async () => {\n    const signers = await ethers.getSigners();\n\n    [deployer, secondDeployer, notOwner, issuer] = signers;\n  });\n\n  /*************************************************************************************/\n  /********************************** DEPLOYMENTS **************************************/\n  /*************************************************************************************/\n  describe('Deployments', () => {\n    it('Should deploy, setup and test the constructed values of the contract', async () => {\n      ({ attestationsRegistry, badges } = await hre.run('deploy-core', {\n        uri: 'https://token_cdn.domain/',\n        badgeOwner: deployer.address,\n        frontFirstCollectionId: '1',\n        frontLastCollectionId: '2',\n      }));\n\n      ({ attestationsRegistry: secondAttestationsRegistry } = await hre.run(\n        'deploy-attestations-registry',\n        {\n          badges: secondDeployer.address,\n          owner: secondDeployer.address,\n        }\n      ));\n\n      // 0 - Checks that the owner is set to the deployer address\n      expect(await attestationsRegistry.owner()).to.equal(deployer.address);\n      expect(await secondAttestationsRegistry.owner()).to.equal(secondDeployer.address);\n\n      snapshotId = await evmSnapshot(hre);\n    });\n  });\n\n  describe('Singles', async () => {\n    const authorizedRangeIndex = 0;\n    let authorizedRange: { min: number; max: number };\n\n    before(async () => {\n      authorizedRange = {\n        min: 3,\n        max: 6,\n      };\n    });\n\n    /*************************************************************************************/\n    /********************************** AUTHORIZE RANGE **********************************/\n    /*************************************************************************************/\n    describe('Authorize Range', async () => {\n      it('Should revert when the sender is not the owner of the contract', async () => {\n        await expect(\n          attestationsRegistry\n            .connect(notOwner)\n            .authorizeRange(issuer.address, authorizedRange.min, authorizedRange.max)\n        ).to.be.revertedWith('Ownable: caller is not the owner');\n      });\n\n      it('Should authorize the range for the attester', async () => {\n        const authorizeIssuerRangeTransaction = await attestationsRegistry.authorizeRange(\n          issuer.address,\n          authorizedRange.min,\n          authorizedRange.max\n        );\n\n        // 1 - Checks that the transaction emitted the event\n        await expect(authorizeIssuerRangeTransaction)\n          .to.emit(attestationsRegistry, 'IssuerAuthorized')\n          .withArgs(issuer.address, authorizedRange.min, authorizedRange.max);\n\n        // 2 - Checks that the issuer is authorized for the range\n        expect(await attestationsRegistry.isAuthorized(issuer.address, authorizedRange.min)).to.be\n          .true;\n        expect(await attestationsRegistry.isAuthorized(issuer.address, authorizedRange.max)).to.be\n          .true;\n\n        // 3 - Checks that the issuer is not authorized outside of his boundaries\n        expect(await attestationsRegistry.isAuthorized(issuer.address, authorizedRange.min - 1)).to\n          .be.false;\n        expect(await attestationsRegistry.isAuthorized(issuer.address, authorizedRange.max + 1)).to\n          .be.false;\n\n        expect(await attestationsRegistry.isAuthorized(issuer.address, authorizedRange.min + 1)).to\n          .be.true;\n        expect(await attestationsRegistry.isAuthorized(issuer.address, authorizedRange.max - 1)).to\n          .be.true;\n      });\n    });\n\n    /*************************************************************************************/\n    /********************************* UNAUTHORIZE RANGE *********************************/\n    /*************************************************************************************/\n    describe('Unauthorize range', async () => {\n      it('Should revert when the sender is not the owner of the contract', async () => {\n        await expect(\n          attestationsRegistry\n            .connect(notOwner)\n            .unauthorizeRange(\n              issuer.address,\n              authorizedRangeIndex,\n              authorizedRange.min,\n              authorizedRange.max\n            )\n        ).to.be.revertedWith('Ownable: caller is not the owner');\n      });\n\n      it('Should revert when the collection id is superior than the authorizedRanges of the issuer', async () => {\n        const unauthorizedRangeIndex = authorizedRangeIndex + 1;\n\n        await expect(\n          attestationsRegistry.unauthorizeRange(\n            issuer.address,\n            unauthorizedRangeIndex,\n            authorizedRange.min,\n            authorizedRange.max\n          )\n        ).to.be.revertedWith(\n          `RangeIndexOutOfBounds(\"${issuer.address}\", 1, ${unauthorizedRangeIndex})`\n        );\n      });\n\n      it('Should revert when the firstCollectionId mismatch the collection min', async () => {\n        await expect(\n          attestationsRegistry.unauthorizeRange(\n            issuer.address,\n            authorizedRangeIndex,\n            authorizedRange.min - 1,\n            authorizedRange.max\n          )\n        ).to.be.revertedWith(\n          `IdsMismatch(\"${issuer.address}\", ${authorizedRangeIndex}, ${authorizedRange.min}, ${\n            authorizedRange.max\n          }, ${authorizedRange.min - 1}, ${authorizedRange.max})`\n        );\n      });\n\n      it('Should revert when the lastCollectionId mistmatch the collection max', async () => {\n        await expect(\n          attestationsRegistry.unauthorizeRange(\n            issuer.address,\n            authorizedRangeIndex,\n            authorizedRange.min,\n            authorizedRange.max + 1\n          )\n        ).to.be.revertedWith(\n          `IdsMismatch(\"${issuer.address}\", ${authorizedRangeIndex}, ${authorizedRange.min}, ${\n            authorizedRange.max\n          }, ${authorizedRange.min}, ${authorizedRange.max + 1})`\n        );\n      });\n\n      it('Should unauthorize the range for the issuer', async () => {\n        const unauthorizeIssuerRangeTransaction = await attestationsRegistry.unauthorizeRange(\n          issuer.address,\n          authorizedRangeIndex,\n          authorizedRange.min,\n          authorizedRange.max\n        );\n\n        // 1 - Checks that the transaction emitted the event\n        await expect(unauthorizeIssuerRangeTransaction)\n          .to.emit(attestationsRegistry, 'IssuerUnauthorized')\n          .withArgs(issuer.address, authorizedRange.min, authorizedRange.max);\n\n        // 2 - Checks that the issuer is authorized for the range\n        expect(await attestationsRegistry.isAuthorized(issuer.address, authorizedRange.min)).to.be\n          .false;\n        expect(await attestationsRegistry.isAuthorized(issuer.address, authorizedRange.max)).to.be\n          .false;\n      });\n    });\n  });\n\n  describe('Batches', () => {\n    let authorizedRangeIndexes = [0, 1];\n    let authorizedRangeLength = 2;\n    let authorizedRanges: { min: number; max: number }[] = [];\n\n    before(async () => {\n      authorizedRanges = [\n        {\n          min: 3,\n          max: 6,\n        },\n        {\n          min: 9,\n          max: 12,\n        },\n      ];\n    });\n\n    /*************************************************************************************/\n    /********************************** AUTHORIZE RANGES *********************************/\n    /*************************************************************************************/\n    describe('Authorize Ranges', () => {\n      it('Should revert when the sender is not the owner of the contract', async () => {\n        await expect(\n          attestationsRegistry.connect(notOwner).authorizeRanges(issuer.address, authorizedRanges)\n        ).to.be.revertedWith('Ownable: caller is not the owner');\n      });\n\n      it('Should authorize the ranges', async () => {\n        const authorizeIssuerRangeTransaction = await attestationsRegistry.authorizeRanges(\n          issuer.address,\n          authorizedRanges\n        );\n\n        // 1 - Checks that the transaction emitted the event\n        // 1.1 - Checks that the transaction emitted the first event\n        await expect(authorizeIssuerRangeTransaction)\n          .to.emit(attestationsRegistry, 'IssuerAuthorized')\n          .withArgs(issuer.address, authorizedRanges[0].min, authorizedRanges[0].max);\n\n        // 1.2 - Checks that the transaction emitted the second event\n        await expect(authorizeIssuerRangeTransaction)\n          .to.emit(attestationsRegistry, 'IssuerAuthorized')\n          .withArgs(issuer.address, authorizedRanges[1].min, authorizedRanges[1].max);\n\n        // 2 - Checks that the issuer is authorized for the range\n        // 2.1 - Checks that the issuer is authorized for the first range\n        expect(await attestationsRegistry.isAuthorized(issuer.address, authorizedRanges[0].min)).to\n          .be.true;\n        expect(await attestationsRegistry.isAuthorized(issuer.address, authorizedRanges[0].max)).to\n          .be.true;\n\n        // 2.2 - Checks that the issuer is authorized for the second range\n        expect(await attestationsRegistry.isAuthorized(issuer.address, authorizedRanges[1].min)).to\n          .be.true;\n        expect(await attestationsRegistry.isAuthorized(issuer.address, authorizedRanges[1].max)).to\n          .be.true;\n\n        // 3 - Checks that the issuer is not authorized outside of his boundaries\n        // 3.1 - Checks that the issuer is not authorized outside of the first range\n        expect(await attestationsRegistry.isAuthorized(issuer.address, authorizedRanges[0].min - 1))\n          .to.be.false;\n        expect(await attestationsRegistry.isAuthorized(issuer.address, authorizedRanges[0].max + 1))\n          .to.be.false;\n\n        expect(await attestationsRegistry.isAuthorized(issuer.address, authorizedRanges[0].min + 1))\n          .to.be.true;\n        expect(await attestationsRegistry.isAuthorized(issuer.address, authorizedRanges[0].max - 1))\n          .to.be.true;\n\n        // 3.2 - Checks that the issuer is not authorized outside of the first range\n        expect(await attestationsRegistry.isAuthorized(issuer.address, authorizedRanges[1].min - 1))\n          .to.be.false;\n        expect(await attestationsRegistry.isAuthorized(issuer.address, authorizedRanges[1].max + 1))\n          .to.be.false;\n\n        expect(await attestationsRegistry.isAuthorized(issuer.address, authorizedRanges[1].min + 1))\n          .to.be.true;\n        expect(await attestationsRegistry.isAuthorized(issuer.address, authorizedRanges[1].max - 1))\n          .to.be.true;\n      });\n    });\n\n    /*************************************************************************************/\n    /********************************* UNAUTHORIZE RANGES ********************************/\n    /*************************************************************************************/\n    describe('Unauthorize Ranges', async () => {\n      it('Should revert when the sender is not the owner of the contract', async () => {\n        await expect(\n          attestationsRegistry\n            .connect(notOwner)\n            .unauthorizeRanges(issuer.address, authorizedRanges, authorizedRangeIndexes)\n        ).to.be.revertedWith('Ownable: caller is not the owner');\n      });\n\n      it('Should revert when one of the collection ids is superior than the authorizedRanges of the issuer', async () => {\n        const unauthorizedRangeIndexes = [\n          authorizedRangeIndexes[0] + 2,\n          authorizedRangeIndexes[1] + 1,\n        ];\n\n        await expect(\n          attestationsRegistry.unauthorizeRanges(\n            issuer.address,\n            authorizedRanges,\n            unauthorizedRangeIndexes\n          )\n        ).to.be.revertedWith(\n          `RangeIndexOutOfBounds(\"${issuer.address}\", ${authorizedRangeLength}, ${unauthorizedRangeIndexes[1]})`\n        );\n\n        unauthorizedRangeIndexes[0] -= 2;\n\n        authorizedRangeLength -= 1;\n\n        await expect(\n          attestationsRegistry.unauthorizeRanges(\n            issuer.address,\n            authorizedRanges,\n            unauthorizedRangeIndexes\n          )\n        ).to.be.revertedWith(\n          `RangeIndexOutOfBounds(\"${issuer.address}\", ${authorizedRangeLength}, ${\n            unauthorizedRangeIndexes[1] - 1\n          })`\n        );\n      });\n\n      it('Should revert when the firstRangeFirstCollectionId mismatch the first collection min', async () => {\n        await expect(\n          attestationsRegistry.unauthorizeRanges(\n            issuer.address,\n            [\n              {\n                min: authorizedRanges[0].min - 1,\n                max: authorizedRanges[0].max,\n              },\n              authorizedRanges[1],\n            ],\n            authorizedRangeIndexes\n          )\n        ).to.be.revertedWith(\n          `IdsMismatch(\"${issuer.address}\", ${authorizedRangeIndexes[0]}, ${\n            authorizedRanges[0].min\n          }, ${authorizedRanges[0].max}, ${authorizedRanges[0].min - 1}, ${\n            authorizedRanges[0].max\n          })`\n        );\n      });\n\n      it('Should revert when the firstRangeLastCollectionId mismatch the first collection max', async () => {\n        await expect(\n          attestationsRegistry.unauthorizeRanges(\n            issuer.address,\n            [\n              {\n                min: authorizedRanges[0].min,\n                max: authorizedRanges[0].max + 1,\n              },\n              authorizedRanges[1],\n            ],\n            authorizedRangeIndexes\n          )\n        ).to.be.revertedWith(\n          `IdsMismatch(\"${issuer.address}\", ${authorizedRangeIndexes[0]}, ${\n            authorizedRanges[0].min\n          }, ${authorizedRanges[0].max}, ${authorizedRanges[0].min}, ${\n            authorizedRanges[0].max + 1\n          })`\n        );\n      });\n\n      it('Should revert when the secondRangeFirstCollectionId mismatch the second collection min', async () => {\n        await expect(\n          attestationsRegistry.unauthorizeRanges(\n            issuer.address,\n            [\n              authorizedRanges[0],\n              {\n                min: authorizedRanges[1].min - 1,\n                max: authorizedRanges[1].max,\n              },\n            ],\n            authorizedRangeIndexes\n          )\n        ).to.be.revertedWith(\n          `IdsMismatch(\"${issuer.address}\", 0, ${authorizedRanges[1].min}, ${\n            authorizedRanges[1].max\n          }, ${authorizedRanges[1].min - 1}, ${authorizedRanges[1].max})`\n        );\n      });\n\n      it('Should revert when the secondRangeLastCollectionId mismatch the second collection max', async () => {\n        await expect(\n          attestationsRegistry.unauthorizeRanges(\n            issuer.address,\n            [\n              authorizedRanges[0],\n              {\n                min: authorizedRanges[1].min,\n                max: authorizedRanges[1].max + 1,\n              },\n            ],\n            authorizedRangeIndexes\n          )\n        ).to.be.revertedWith(\n          `IdsMismatch(\"${issuer.address}\", 0, ${authorizedRanges[1].min}, ${\n            authorizedRanges[1].max\n          }, ${authorizedRanges[1].min}, ${authorizedRanges[1].max + 1})`\n        );\n      });\n\n      it('Should unauthorize the ranges for the issuer', async () => {\n        const unauthorizeIssuerRangesTransaction = await attestationsRegistry.unauthorizeRanges(\n          issuer.address,\n          authorizedRanges,\n          authorizedRangeIndexes\n        );\n\n        // 1 - Checks that the transaction emitted the events\n        // 1.1 - Checks that the transaction emitted the first event\n        await expect(unauthorizeIssuerRangesTransaction)\n          .to.emit(attestationsRegistry, 'IssuerUnauthorized')\n          .withArgs(issuer.address, authorizedRanges[0].min, authorizedRanges[0].max);\n\n        // 1.2 - Checks that the transaction emitted the second event\n        await expect(unauthorizeIssuerRangesTransaction)\n          .to.emit(attestationsRegistry, 'IssuerUnauthorized')\n          .withArgs(issuer.address, authorizedRanges[1].min, authorizedRanges[1].max);\n\n        // 2 - Checks that the issuer is authorized for the range\n        // 2.1 - Checks that the issuer is authorized for the first range\n        expect(await attestationsRegistry.isAuthorized(issuer.address, authorizedRanges[0].min)).to\n          .be.false;\n        expect(await attestationsRegistry.isAuthorized(issuer.address, authorizedRanges[0].max)).to\n          .be.false;\n\n        // 2.2 - Checks that the issuer is authorized for the second range\n        expect(await attestationsRegistry.isAuthorized(issuer.address, authorizedRanges[1].min)).to\n          .be.false;\n        expect(await attestationsRegistry.isAuthorized(issuer.address, authorizedRanges[1].max)).to\n          .be.false;\n      });\n    });\n  });\n\n  /*************************************************************************************/\n  /*************************************** PAUSE ***************************************/\n  /*************************************************************************************/\n  describe('Pause', async () => {\n    it('Should revert when the sender is not the owner of the contract', async () => {\n      await expect(attestationsRegistry.connect(notOwner).pause()).to.be.revertedWith(\n        'Ownable: caller is not the owner'\n      );\n    });\n\n    it('Should pause the contract', async () => {\n      const pauseTransaction = await attestationsRegistry.pause();\n\n      // 1 - Checks that the transaction emitted the event\n      await expect(pauseTransaction)\n        .to.emit(attestationsRegistry, 'Paused')\n        .withArgs(deployer.address);\n\n      expect(await attestationsRegistry.paused()).to.be.true;\n    });\n\n    it('Should revert when the contract is already paused', async () => {\n      await expect(attestationsRegistry.pause()).to.be.revertedWith('Pausable: paused');\n    });\n  });\n\n  /*************************************************************************************/\n  /************************************** UNPAUSE **************************************/\n  /*************************************************************************************/\n  describe('Unpause', async () => {\n    it('Should revert when the sender is not the owner of the contract', async () => {\n      await expect(attestationsRegistry.connect(notOwner).unpause()).to.be.revertedWith(\n        'Ownable: caller is not the owner'\n      );\n    });\n\n    it('Should unpause the contract', async () => {\n      const unpauseTransaction = await attestationsRegistry.unpause();\n\n      // 1 - Checks that the transaction emitted the event\n      await expect(unpauseTransaction)\n        .to.emit(attestationsRegistry, 'Unpaused')\n        .withArgs(deployer.address);\n\n      expect(await attestationsRegistry.paused()).to.be.false;\n    });\n\n    it('Should revert when the contract is already unpaused', async () => {\n      await expect(attestationsRegistry.unpause()).to.be.revertedWith('Pausable: not paused');\n    });\n  });\n\n  /*************************************************************************************/\n  /******************************** TRANSFER OWNERSHIP *********************************/\n  /*************************************************************************************/\n  describe('Transfer ownership', () => {\n    it('Should revert when the sender is not the current owner of the contract', async () => {\n      await expect(\n        attestationsRegistry.connect(notOwner).transferOwnership(notOwner.address)\n      ).to.be.revertedWith('Ownable: caller is not the owner');\n    });\n\n    it('Should revert when the newOwner is a zero address', async () => {\n      await expect(\n        attestationsRegistry.transferOwnership(ethers.constants.AddressZero)\n      ).to.be.revertedWith('Ownable: new owner is the zero address');\n    });\n\n    it('Should transfer the ownership', async () => {\n      await expect(attestationsRegistry.transferOwnership(secondDeployer.address))\n        .to.emit(attestationsRegistry, 'OwnershipTransferred')\n        .withArgs(deployer.address, secondDeployer.address);\n    });\n  });\n\n  /*************************************************************************************/\n  /******************************** RENOUNCE OWNERSHIP *********************************/\n  /*************************************************************************************/\n  describe('Renounce ownership', () => {\n    before(async () => {\n      await attestationsRegistry.connect(secondDeployer).transferOwnership(deployer.address);\n    });\n\n    it('Should revert when the sender is not the current owner of the contract', async () => {\n      await expect(attestationsRegistry.connect(notOwner).renounceOwnership()).to.be.revertedWith(\n        'Ownable: caller is not the owner'\n      );\n    });\n\n    it('Should renounce the ownership', async () => {\n      await expect(attestationsRegistry.renounceOwnership())\n        .to.emit(attestationsRegistry, 'OwnershipTransferred')\n        .withArgs(deployer.address, ethers.constants.AddressZero);\n    });\n  });\n\n  /***********************************************************************/\n  /******************************** ATTRIBUTES *********************************/\n  /***********************************************************************/\n\n  describe('Attributes', async () => {\n    let ATTRIBUTES = {\n      CURATED: 1,\n      SYBIL_RESISTANCE: 2,\n      TEST_INSERTION: 10,\n      NOT_CREATED: 50,\n    };\n\n    before(async () => {\n      await evmRevert(hre, snapshotId);\n    });\n\n    describe('Attribute creation', async () => {\n      it('Should revert when creating a new attribute as a non-owner', async () => {\n        await expect(\n          attestationsRegistry\n            .connect(notOwner)\n            .createNewAttribute(ATTRIBUTES.TEST_INSERTION, formatBytes32String('TEST_INSERTION'))\n        ).to.be.revertedWith('Ownable: caller is not the owner');\n      });\n\n      it('Should revert when creating new attributes as a non-owner', async () => {\n        await expect(\n          attestationsRegistry\n            .connect(notOwner)\n            .createNewAttributes(\n              [ATTRIBUTES.TEST_INSERTION, ATTRIBUTES.CURATED],\n              [formatBytes32String('TEST_INSERTION'), formatBytes32String('CURATED')]\n            )\n        ).to.be.revertedWith('Ownable: caller is not the owner');\n      });\n\n      it('Should revert when creating new attributes with different arguments length', async () => {\n        await expect(\n          attestationsRegistry.connect(deployer).createNewAttributes(\n            [ATTRIBUTES.TEST_INSERTION], // missing one argument\n            [formatBytes32String('TEST_INSERTION'), formatBytes32String('CURATED')]\n          )\n        ).to.be.revertedWith('ArgsLengthDoesNotMatch()');\n      });\n\n      it('Should revert when creating a attribute with index > 63', async () => {\n        await expect(\n          attestationsRegistry\n            .connect(deployer)\n            .createNewAttribute(64, formatBytes32String('ATTRIBUTE_OVERFLOW'))\n        ).to.be.revertedWith('IndexOutOfBounds(64)');\n      });\n\n      it('Should revert when creating new attributes with index of one of them > 63', async () => {\n        await expect(\n          attestationsRegistry\n            .connect(deployer)\n            .createNewAttributes(\n              [ATTRIBUTES.CURATED, 64],\n              [formatBytes32String('CURATED'), formatBytes32String('ATTRIBUTE_OVERFLOW')]\n            )\n        ).to.be.revertedWith('IndexOutOfBounds(64)');\n      });\n\n      it('Should create a new attribute as an owner', async () => {\n        const attributeInserted = await attestationsRegistry\n          .connect(deployer)\n          .createNewAttribute(ATTRIBUTES.TEST_INSERTION, formatBytes32String('TEST_INSERTION'));\n\n        await expect(attributeInserted)\n          .to.emit(attestationsRegistry, 'NewAttributeCreated')\n          .withArgs(ATTRIBUTES.TEST_INSERTION, formatBytes32String('TEST_INSERTION'));\n      });\n\n      it('Should revert when trying to create again a attribute for the same index', async () => {\n        await expect(\n          attestationsRegistry.createNewAttribute(\n            ATTRIBUTES.TEST_INSERTION,\n            formatBytes32String('OTHER ATTRIBUTE')\n          )\n        ).to.be.revertedWith('AttributeAlreadyExists(10)');\n      });\n\n      it('Should revert when creating new attributes with one of them already existing', async () => {\n        await expect(\n          attestationsRegistry.connect(deployer).createNewAttributes(\n            [ATTRIBUTES.CURATED, ATTRIBUTES.TEST_INSERTION], // TEST_INSERTION already created\n            [formatBytes32String('CURATED'), formatBytes32String('TEST_INSERTION')]\n          )\n        ).to.be.revertedWith('AttributeAlreadyExists(10)');\n      });\n\n      it('Should create new attributes as an owner', async () => {\n        const attributesInserted = await attestationsRegistry\n          .connect(deployer)\n          .createNewAttributes(\n            [ATTRIBUTES.CURATED, ATTRIBUTES.SYBIL_RESISTANCE],\n            [formatBytes32String('CURATED'), formatBytes32String('SYBIL_RESISTANCE')]\n          );\n\n        await expect(attributesInserted)\n          .to.emit(attestationsRegistry, 'NewAttributeCreated')\n          .withArgs(ATTRIBUTES.CURATED, formatBytes32String('CURATED'));\n\n        await expect(attributesInserted)\n          .to.emit(attestationsRegistry, 'NewAttributeCreated')\n          .withArgs(ATTRIBUTES.SYBIL_RESISTANCE, formatBytes32String('SYBIL_RESISTANCE'));\n      });\n    });\n\n    describe('Attribute update', async () => {\n      it('Should revert when updating attribute as a non-owner', async () => {\n        await expect(\n          attestationsRegistry\n            .connect(notOwner)\n            .updateAttributeName(ATTRIBUTES.TEST_INSERTION, formatBytes32String('CURATED2'))\n        ).to.be.revertedWith('Ownable: caller is not the owner');\n      });\n\n      it('Should revert when updating attributes as a non-owner', async () => {\n        await expect(\n          attestationsRegistry\n            .connect(notOwner)\n            .updateAttributesName(\n              [ATTRIBUTES.TEST_INSERTION, ATTRIBUTES.CURATED],\n              [formatBytes32String('TEST_INSERTION2'), formatBytes32String('CURATED2')]\n            )\n        ).to.be.revertedWith('Ownable: caller is not the owner');\n      });\n\n      it('Should revert when updating new attributes with different arguments length', async () => {\n        await expect(\n          attestationsRegistry.connect(deployer).updateAttributesName(\n            [ATTRIBUTES.TEST_INSERTION], // missing one argument\n            [formatBytes32String('TEST_INSERTION2'), formatBytes32String('CURATED2')]\n          )\n        ).to.be.revertedWith('ArgsLengthDoesNotMatch()');\n      });\n\n      it('Should revert when updating a attribute with index > 63', async () => {\n        await expect(\n          attestationsRegistry\n            .connect(deployer)\n            .updateAttributeName(64, formatBytes32String('ATTRIBUTE_OVERFLOW'))\n        ).to.be.revertedWith('IndexOutOfBounds(64)');\n      });\n\n      it('Should revert when updating new attributes with index of one of them > 63', async () => {\n        await expect(\n          attestationsRegistry\n            .connect(deployer)\n            .updateAttributesName(\n              [ATTRIBUTES.CURATED, 64],\n              [formatBytes32String('CURATED2'), formatBytes32String('ATTRIBUTE_OVERFLOW')]\n            )\n        ).to.be.revertedWith('IndexOutOfBounds(64)');\n      });\n\n      it('Should revert when trying to update a attribute name that does not exists', async () => {\n        await expect(\n          attestationsRegistry\n            .connect(deployer)\n            .updateAttributeName(ATTRIBUTES.NOT_CREATED, formatBytes32String('NOT_INSERTED'))\n        ).to.be.revertedWith('AttributeDoesNotExist(50)');\n      });\n\n      it('Should revert when trying to update attributes name with one of them that does not exists', async () => {\n        await expect(\n          attestationsRegistry\n            .connect(deployer)\n            .updateAttributesName(\n              [ATTRIBUTES.CURATED, ATTRIBUTES.NOT_CREATED],\n              [formatBytes32String('CURATED2'), formatBytes32String('NOT_INSERTED')]\n            )\n        ).to.be.revertedWith('AttributeDoesNotExist(50)');\n      });\n\n      it('Should update a attribute name', async () => {\n        const attributeUpdated = await attestationsRegistry\n          .connect(deployer)\n          .updateAttributeName(ATTRIBUTES.TEST_INSERTION, formatBytes32String('TEST_INSERTION2'));\n\n        await expect(attributeUpdated)\n          .to.emit(attestationsRegistry, 'AttributeNameUpdated')\n          .withArgs(\n            ATTRIBUTES.TEST_INSERTION,\n            formatBytes32String('TEST_INSERTION2'),\n            formatBytes32String('TEST_INSERTION')\n          );\n      });\n\n      it('Should update attributes name', async () => {\n        const attributesUpdated = await attestationsRegistry\n          .connect(deployer)\n          .updateAttributesName(\n            [ATTRIBUTES.CURATED, ATTRIBUTES.SYBIL_RESISTANCE],\n            [formatBytes32String('CURATED2'), formatBytes32String('SYBIL_RESISTANCE2')]\n          );\n\n        await expect(attributesUpdated)\n          .to.emit(attestationsRegistry, 'AttributeNameUpdated')\n          .withArgs(\n            ATTRIBUTES.CURATED,\n            formatBytes32String('CURATED2'),\n            formatBytes32String('CURATED')\n          );\n\n        await expect(attributesUpdated)\n          .to.emit(attestationsRegistry, 'AttributeNameUpdated')\n          .withArgs(\n            ATTRIBUTES.SYBIL_RESISTANCE,\n            formatBytes32String('SYBIL_RESISTANCE2'),\n            formatBytes32String('SYBIL_RESISTANCE')\n          );\n      });\n    });\n\n    describe('Attribute deletion', async () => {\n      it('Should revert when deleting a attribute as a non-owner', async () => {\n        await expect(\n          attestationsRegistry.connect(notOwner).deleteAttribute(ATTRIBUTES.NOT_CREATED)\n        ).to.be.revertedWith('Ownable: caller is not the owner');\n      });\n\n      it('Should revert when deleting attributes as a non-owner', async () => {\n        await expect(\n          attestationsRegistry\n            .connect(notOwner)\n            .deleteAttributes([ATTRIBUTES.NOT_CREATED, ATTRIBUTES.TEST_INSERTION])\n        ).to.be.revertedWith('Ownable: caller is not the owner');\n      });\n\n      it('Should revert when trying to delete a attribute with index > 63', async () => {\n        await expect(attestationsRegistry.connect(deployer).deleteAttribute(64)).to.be.revertedWith(\n          'IndexOutOfBounds(64)'\n        );\n      });\n\n      it('Should revert when trying to delete attributes with index of one of them > 63', async () => {\n        await expect(\n          attestationsRegistry.connect(deployer).deleteAttributes([ATTRIBUTES.CURATED, 64])\n        ).to.be.revertedWith('IndexOutOfBounds(64)');\n      });\n\n      it('Should revert when trying to delete a attribute that does not exists', async () => {\n        await expect(\n          attestationsRegistry.connect(deployer).deleteAttribute(ATTRIBUTES.NOT_CREATED)\n        ).to.be.revertedWith('AttributeDoesNotExist(50)');\n      });\n\n      it('Should revert when trying to delete attributes with one of them that does not exists', async () => {\n        await expect(\n          attestationsRegistry\n            .connect(deployer)\n            .deleteAttributes([ATTRIBUTES.CURATED, ATTRIBUTES.NOT_CREATED])\n        ).to.be.revertedWith('AttributeDoesNotExist(50)');\n      });\n\n      it('Should delete a attribute', async () => {\n        const attributeDeleted = await attestationsRegistry\n          .connect(deployer)\n          .deleteAttribute(ATTRIBUTES.TEST_INSERTION);\n        await expect(attributeDeleted)\n          .to.emit(attestationsRegistry, 'AttributeDeleted')\n          .withArgs(ATTRIBUTES.TEST_INSERTION, formatBytes32String('TEST_INSERTION2'));\n      });\n\n      it('Should delete attributes', async () => {\n        const attributesDeleted = await attestationsRegistry\n          .connect(deployer)\n          .deleteAttributes([ATTRIBUTES.CURATED, ATTRIBUTES.SYBIL_RESISTANCE]);\n\n        await expect(attributesDeleted)\n          .to.emit(attestationsRegistry, 'AttributeDeleted')\n          .withArgs(ATTRIBUTES.CURATED, formatBytes32String('CURATED2'));\n\n        await expect(attributesDeleted)\n          .to.emit(attestationsRegistry, 'AttributeDeleted')\n          .withArgs(ATTRIBUTES.SYBIL_RESISTANCE, formatBytes32String('SYBIL_RESISTANCE2'));\n      });\n    });\n\n    describe('Create AttestationsCollection Attributes', async () => {\n      before(async () => {\n        // Register the attribute we will use during the tests\n        await attestationsRegistry\n          .connect(deployer)\n          .createNewAttributes(\n            [ATTRIBUTES.CURATED, ATTRIBUTES.SYBIL_RESISTANCE],\n            [formatBytes32String('CURATED'), formatBytes32String('SYBIL_RESISTANCE')]\n          );\n      });\n\n      it('Should revert when setting a attribute as a non-owner', async () => {\n        await expect(\n          attestationsRegistry\n            .connect(notOwner)\n            .setAttributeValueForAttestationsCollection(1, ATTRIBUTES.CURATED, 1)\n        ).to.be.revertedWith('Ownable: caller is not the owner');\n      });\n\n      it('Should revert when setting attributes as a non-owner', async () => {\n        await expect(\n          attestationsRegistry\n            .connect(notOwner)\n            .setAttributesValuesForAttestationsCollections(\n              [1, 1],\n              [ATTRIBUTES.CURATED, ATTRIBUTES.SYBIL_RESISTANCE],\n              [1, 2]\n            )\n        ).to.be.revertedWith('Ownable: caller is not the owner');\n      });\n\n      it('Should revert when setting attributes with invalid args length', async () => {\n        await expect(\n          attestationsRegistry.connect(deployer).setAttributesValuesForAttestationsCollections(\n            [1], //missing arg\n            [ATTRIBUTES.CURATED, ATTRIBUTES.SYBIL_RESISTANCE],\n            [1, 2]\n          )\n        ).to.be.revertedWith('ArgsLengthDoesNotMatch');\n      });\n\n      it('Should revert when setting a attribute with an index > 63', async () => {\n        await expect(\n          attestationsRegistry\n            .connect(deployer)\n            .setAttributeValueForAttestationsCollection(1, 64, 1)\n        ).to.be.revertedWith('IndexOutOfBounds(64)');\n      });\n\n      it('Should revert when setting attributes with one of them having an index > 63', async () => {\n        await expect(\n          attestationsRegistry\n            .connect(deployer)\n            .setAttributesValuesForAttestationsCollections([1, 1], [ATTRIBUTES.CURATED, 64], [1, 2])\n        ).to.be.revertedWith('IndexOutOfBounds(64)');\n      });\n\n      it('Should revert when setting a attribute to an AttestationsCollection and the attribute is not already created', async () => {\n        await expect(\n          attestationsRegistry\n            .connect(deployer)\n            .setAttributeValueForAttestationsCollection(1, ATTRIBUTES.NOT_CREATED, 1)\n        ).to.be.revertedWith('AttributeDoesNotExist(50)');\n      });\n\n      it('Should revert when setting attributes to an AttestationsCollection and one of the attributes is not already created', async () => {\n        await expect(\n          attestationsRegistry\n            .connect(deployer)\n            .setAttributesValuesForAttestationsCollections(\n              [1, 1],\n              [ATTRIBUTES.CURATED, ATTRIBUTES.NOT_CREATED],\n              [1, 2]\n            )\n        ).to.be.revertedWith('AttributeDoesNotExist(50)');\n      });\n\n      it('Should set a attribute to an AttestationsCollection with power 1', async () => {\n        const attributeSet = await attestationsRegistry\n          .connect(deployer)\n          .setAttributeValueForAttestationsCollection(1, ATTRIBUTES.CURATED, 1);\n\n        await expect(attributeSet)\n          .to.emit(attestationsRegistry, 'AttestationsCollectionAttributeSet')\n          .withArgs(1, ATTRIBUTES.CURATED, 1);\n\n        expect(await attestationsRegistry.attestationsCollectionHasAttribute(1, ATTRIBUTES.CURATED))\n          .to.be.true;\n        expect(\n          await attestationsRegistry.getAttributeValueForAttestationsCollection(\n            1,\n            ATTRIBUTES.CURATED\n          )\n        ).to.be.eq(1);\n      });\n\n      it('Should set a attribute to an AttestationsCollection and change the power', async () => {\n        const attributeSet = await attestationsRegistry\n          .connect(deployer)\n          .setAttributeValueForAttestationsCollection(1, ATTRIBUTES.CURATED, 5);\n\n        await expect(attributeSet)\n          .to.emit(attestationsRegistry, 'AttestationsCollectionAttributeSet')\n          .withArgs(1, ATTRIBUTES.CURATED, 5);\n\n        expect(await attestationsRegistry.attestationsCollectionHasAttribute(1, ATTRIBUTES.CURATED))\n          .to.be.true;\n        expect(\n          await attestationsRegistry.getAttributeValueForAttestationsCollection(\n            1,\n            ATTRIBUTES.CURATED\n          )\n        ).to.be.eq(5);\n      });\n\n      it('Should revert to set a attribute to an AttestationsCollection with power > 15', async () => {\n        await expect(\n          attestationsRegistry\n            .connect(deployer)\n            .setAttributeValueForAttestationsCollection(1, ATTRIBUTES.CURATED, 16)\n        ).to.be.revertedWith('ValueOutOfBounds(16)');\n      });\n\n      it('Should revert when removing a attribute as a non-owner', async () => {\n        await expect(\n          attestationsRegistry\n            .connect(notOwner)\n            .setAttributeValueForAttestationsCollection(1, ATTRIBUTES.CURATED, 0)\n        ).to.be.revertedWith('Ownable: caller is not the owner');\n      });\n\n      it('Should remove attribute to an AttestationsCollection', async () => {\n        const attributeRemoved = await attestationsRegistry\n          .connect(deployer)\n          .setAttributeValueForAttestationsCollection(1, ATTRIBUTES.CURATED, 0);\n\n        await expect(attributeRemoved)\n          .to.emit(attestationsRegistry, 'AttestationsCollectionAttributeSet')\n          .withArgs(1, ATTRIBUTES.CURATED, 0);\n\n        expect(await attestationsRegistry.attestationsCollectionHasAttribute(1, ATTRIBUTES.CURATED))\n          .to.be.false;\n        expect(\n          await attestationsRegistry.getAttributeValueForAttestationsCollection(\n            1,\n            ATTRIBUTES.CURATED\n          )\n        ).to.be.eq(0);\n      });\n\n      it('Should set attributes to two AttestationsCollection with power 1 and 2', async () => {\n        const attributesSet = await attestationsRegistry\n          .connect(deployer)\n          .setAttributesValuesForAttestationsCollections(\n            [1, 1],\n            [ATTRIBUTES.CURATED, ATTRIBUTES.SYBIL_RESISTANCE],\n            [1, 2]\n          );\n\n        await expect(attributesSet)\n          .to.emit(attestationsRegistry, 'AttestationsCollectionAttributeSet')\n          .withArgs(1, ATTRIBUTES.CURATED, 1);\n\n        await expect(attributesSet)\n          .to.emit(attestationsRegistry, 'AttestationsCollectionAttributeSet')\n          .withArgs(1, ATTRIBUTES.SYBIL_RESISTANCE, 2);\n\n        expect(\n          await attestationsRegistry.attestationsCollectionHasAttributes(1, [\n            ATTRIBUTES.CURATED,\n            ATTRIBUTES.SYBIL_RESISTANCE,\n          ])\n        ).to.be.true;\n\n        expect(\n          await attestationsRegistry.getAttributesValuesForAttestationsCollection(1, [\n            ATTRIBUTES.CURATED,\n            ATTRIBUTES.SYBIL_RESISTANCE,\n          ])\n        ).to.be.eql([1, 2]);\n      });\n\n      it('Should set attributes to AttestationsCollection and change the power', async () => {\n        const attributesSet = await attestationsRegistry\n          .connect(deployer)\n          .setAttributesValuesForAttestationsCollections(\n            [1, 1],\n            [ATTRIBUTES.CURATED, ATTRIBUTES.SYBIL_RESISTANCE],\n            [6, 11]\n          );\n\n        await expect(attributesSet)\n          .to.emit(attestationsRegistry, 'AttestationsCollectionAttributeSet')\n          .withArgs(1, ATTRIBUTES.CURATED, 6);\n\n        await expect(attributesSet)\n          .to.emit(attestationsRegistry, 'AttestationsCollectionAttributeSet')\n          .withArgs(1, ATTRIBUTES.SYBIL_RESISTANCE, 11);\n\n        expect(\n          await attestationsRegistry.attestationsCollectionHasAttributes(1, [\n            ATTRIBUTES.CURATED,\n            ATTRIBUTES.SYBIL_RESISTANCE,\n          ])\n        ).to.be.true;\n        expect(\n          await attestationsRegistry.getAttributesValuesForAttestationsCollection(1, [\n            ATTRIBUTES.CURATED,\n            ATTRIBUTES.SYBIL_RESISTANCE,\n          ])\n        ).to.be.eql([6, 11]);\n      });\n\n      it('Should get attributes names and values for the attestationsCollection referenced', async () => {\n        const res = await attestationsRegistry\n          .connect(deployer)\n          .getAttributesNamesAndValuesForAttestationsCollection(1);\n\n        // we should have only 2 attributes enabled\n        expect(res[0].length).to.be.eql(2);\n        expect(parseBytes32String(res[0][0])).to.be.eql('CURATED');\n        expect(parseBytes32String(res[0][1])).to.be.eql('SYBIL_RESISTANCE');\n        expect(res[1]).to.be.eql([6, 11]);\n      });\n\n      it('Should revert to set attributes to an AttestationsCollection with one of them having a power > 15', async () => {\n        await expect(\n          attestationsRegistry\n            .connect(deployer)\n            .setAttributesValuesForAttestationsCollections(\n              [1, 1],\n              [ATTRIBUTES.CURATED, ATTRIBUTES.SYBIL_RESISTANCE],\n              [6, 16]\n            )\n        ).to.be.revertedWith('ValueOutOfBounds(16)');\n      });\n\n      it('Should revert when removing attributes as a non-owner', async () => {\n        await expect(\n          attestationsRegistry\n            .connect(notOwner)\n            .setAttributesValuesForAttestationsCollections(\n              [1, 1],\n              [ATTRIBUTES.CURATED, ATTRIBUTES.SYBIL_RESISTANCE],\n              [0, 0]\n            )\n        ).to.be.revertedWith('Ownable: caller is not the owner');\n      });\n\n      it('Should remove attributes to an AttestationsCollection', async () => {\n        const attributesRemoved = await attestationsRegistry\n          .connect(deployer)\n          .setAttributesValuesForAttestationsCollections(\n            [1, 1],\n            [ATTRIBUTES.CURATED, ATTRIBUTES.SYBIL_RESISTANCE],\n            [0, 0]\n          );\n\n        await expect(attributesRemoved)\n          .to.emit(attestationsRegistry, 'AttestationsCollectionAttributeSet')\n          .withArgs(1, ATTRIBUTES.CURATED, 0);\n\n        await expect(attributesRemoved)\n          .to.emit(attestationsRegistry, 'AttestationsCollectionAttributeSet')\n          .withArgs(1, ATTRIBUTES.SYBIL_RESISTANCE, 0);\n\n        expect(\n          await attestationsRegistry.attestationsCollectionHasAttributes(1, [\n            ATTRIBUTES.CURATED,\n            ATTRIBUTES.SYBIL_RESISTANCE,\n          ])\n        ).to.be.false;\n\n        expect(\n          await attestationsRegistry.getAttributesValuesForAttestationsCollection(1, [\n            ATTRIBUTES.CURATED,\n            ATTRIBUTES.SYBIL_RESISTANCE,\n          ])\n        ).to.be.eql([0, 0]);\n      });\n    });\n  });\n\n  describe('Update Implementation', () => {\n    it('Should update implementation', async () => {\n      const proxyAdminSigner = await ethers.getSigner(\n        deploymentsConfig[hre.network.name].deployOptions.proxyAdmin as string\n      );\n      const { attestationsRegistry: newImplementation } = await hre.run(\n        'deploy-attestations-registry',\n        {\n          badges: secondDeployer.address,\n          owner: secondDeployer.address,\n          options: { behindProxy: false },\n        }\n      );\n      const attestationsRegistryProxy = TransparentUpgradeableProxy__factory.connect(\n        attestationsRegistry.address,\n        proxyAdminSigner\n      );\n      await (await attestationsRegistryProxy.upgradeTo(newImplementation.address)).wait();\n\n      const implementationAddress = await getImplementation(attestationsRegistryProxy);\n      expect(implementationAddress).to.be.eql(newImplementation.address);\n    });\n  });\n});\n"
  },
  {
    "path": "test/unit/core/attestations-registry/attestations-registry.test.ts",
    "content": "import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers';\nimport { expect } from 'chai';\nimport { BigNumber } from 'ethers';\nimport hre, { ethers } from 'hardhat';\nimport { AttestationsRegistry, Badges } from 'types';\nimport { AttestationStruct } from 'types/AttestationsRegistry';\n\ntype IssuerRange = {\n  min: number;\n  max: number;\n};\n\ntype Attestations = {\n  first: AttestationStruct;\n  second: AttestationStruct;\n};\n\ndescribe('Test Attestations Registry contract', () => {\n  let deployer: SignerWithAddress;\n  let secondDeployer: SignerWithAddress;\n  let user: SignerWithAddress;\n  let issuer: SignerWithAddress;\n\n  let attestationsRegistry: AttestationsRegistry;\n  let secondAttestationsRegistry: AttestationsRegistry;\n  let badges: Badges;\n\n  let firstAuthorizedRange: IssuerRange;\n  let secondAuthorizedRange: IssuerRange;\n  let attestations: Attestations;\n\n  before(async () => {\n    const signers = await ethers.getSigners();\n\n    [deployer, secondDeployer, user, issuer] = signers;\n\n    firstAuthorizedRange = {\n      min: 3,\n      max: 6,\n    };\n\n    secondAuthorizedRange = {\n      min: 9,\n      max: 12,\n    };\n\n    attestations = {\n      first: {\n        collectionId: firstAuthorizedRange.min,\n        owner: user.address,\n        issuer: issuer.address,\n        value: 1,\n        timestamp: Math.floor(Date.now() / 1000),\n        extraData: [],\n      },\n      second: {\n        collectionId: secondAuthorizedRange.min,\n        owner: user.address,\n        issuer: issuer.address,\n        value: 1,\n        timestamp: Math.floor(Date.now() / 1000),\n        extraData: [],\n      },\n    };\n  });\n\n  /*************************************************************************************/\n  /********************************** DEPLOYMENTS **************************************/\n  /*************************************************************************************/\n  describe('Deployments', () => {\n    it('Should deploy, setup and test the constructed values of the contract', async () => {\n      ({ attestationsRegistry, badges } = await hre.run('deploy-core', {\n        uri: 'https://token_cdn.domain/',\n        badgeOwner: deployer.address,\n        frontFirstCollectionId: '1',\n        frontLastCollectionId: '2',\n      }));\n\n      ({ attestationsRegistry: secondAttestationsRegistry } = await hre.run(\n        'deploy-attestations-registry',\n        {\n          badges: secondDeployer.address,\n          owner: secondDeployer.address,\n        }\n      ));\n\n      // 0 - Checks that the owner is set to the deployer address\n      expect(await attestationsRegistry.owner()).to.equal(deployer.address);\n      expect(await secondAttestationsRegistry.owner()).to.equal(secondDeployer.address);\n    });\n  });\n\n  describe('Configuration checks', () => {\n    it('Should have setup the owner correctly', async () => {\n      expect(await attestationsRegistry.owner()).to.be.eql(deployer.address);\n    });\n\n    it('Should get the owner correctly', async () => {\n      expect(await attestationsRegistry.owner()).to.be.eql(deployer.address);\n    });\n\n    it('Should revert when trying to call initialize again', async () => {\n      await expect(\n        attestationsRegistry.connect(deployer).initialize(deployer.address)\n      ).to.be.revertedWith('Initializable: contract is already initialized');\n    });\n  });\n\n  /*************************************************************************************/\n  /****************************** RECORD ATTESTATIONS **********************************/\n  /*************************************************************************************/\n  describe('Record attestations', () => {\n    it('Should revert when the contract is paused', async () => {\n      await attestationsRegistry.pause();\n\n      await expect(\n        attestationsRegistry.recordAttestations([attestations.first])\n      ).to.be.revertedWith('Pausable: paused');\n\n      await attestationsRegistry.unpause();\n    });\n\n    it('Should revert when the attester is not authorized in one of the collectionIds provided', async () => {\n      await expect(\n        attestationsRegistry\n          .connect(issuer)\n          .recordAttestations([\n            { ...attestations.first, collectionId: firstAuthorizedRange.min - 1 },\n            attestations.second,\n          ])\n      ).to.be.revertedWith(\n        `IssuerNotAuthorized(\"${issuer.address}\", ${firstAuthorizedRange.min - 1})`\n      );\n\n      await attestationsRegistry.authorizeRange(\n        issuer.address,\n        firstAuthorizedRange.min,\n        firstAuthorizedRange.max\n      );\n\n      await expect(\n        attestationsRegistry\n          .connect(issuer)\n          .recordAttestations([\n            attestations.first,\n            { ...attestations.second, collectionId: secondAuthorizedRange.min - 1 },\n          ])\n      ).to.be.revertedWith(\n        `IssuerNotAuthorized(\"${issuer.address}\", ${secondAuthorizedRange.min - 1})`\n      );\n    });\n\n    it('Should record attestations', async () => {\n      await attestationsRegistry.authorizeRanges(issuer.address, [\n        firstAuthorizedRange,\n        secondAuthorizedRange,\n      ]);\n\n      const recordAttestationsTransaction = await attestationsRegistry\n        .connect(issuer)\n        .recordAttestations([attestations.first, attestations.second]);\n\n      // 1 - Checks that the transaction emitted the event\n      await expect(recordAttestationsTransaction)\n        .to.emit(attestationsRegistry, 'AttestationRecorded')\n        .withArgs([\n          BigNumber.from(attestations.first.collectionId),\n          attestations.first.owner,\n          attestations.first.issuer,\n          BigNumber.from(attestations.first.value),\n          attestations.first.timestamp,\n          ethers.utils.hexlify(attestations.first.extraData),\n        ]);\n\n      await expect(recordAttestationsTransaction)\n        .to.emit(attestationsRegistry, 'AttestationRecorded')\n        .withArgs([\n          BigNumber.from(attestations.second.collectionId),\n          attestations.second.owner,\n          attestations.second.issuer,\n          BigNumber.from(attestations.second.value),\n          attestations.second.timestamp,\n          ethers.utils.hexlify(attestations.second.extraData),\n        ]);\n\n      // 1.2 - Checks that the events related to the badges are emitted\n      await expect(recordAttestationsTransaction)\n        .to.emit(badges, 'TransferSingle')\n        .withArgs(\n          attestationsRegistry.address,\n          ethers.constants.AddressZero,\n          user.address,\n          attestations.first.collectionId,\n          attestations.first.value\n        );\n\n      await expect(recordAttestationsTransaction)\n        .to.emit(badges, 'TransferSingle')\n        .withArgs(\n          attestationsRegistry.address,\n          ethers.constants.AddressZero,\n          user.address,\n          attestations.second.collectionId,\n          attestations.second.value\n        );\n\n      // 2 - Checks that the attestations was recorded on the right user\n      expect(await attestationsRegistry.hasAttestation(firstAuthorizedRange.min, user.address)).to\n        .be.true;\n      expect(await attestationsRegistry.hasAttestation(secondAuthorizedRange.min, user.address)).to\n        .be.true;\n      // 2.2 - Checks that the user has received it's minted badge\n      expect(await badges.balanceOf(user.address, attestations.first.collectionId)).to.equal(\n        attestations.first.value\n      );\n      expect(await badges.balanceOf(user.address, attestations.second.collectionId)).to.equal(\n        attestations.second.value\n      );\n    });\n\n    it('Should update the value whe re recording attestations', async () => {\n      await attestationsRegistry.authorizeRanges(issuer.address, [\n        firstAuthorizedRange,\n        secondAuthorizedRange,\n      ]);\n\n      const recordAttestationsTransaction = await attestationsRegistry\n        .connect(issuer)\n        .recordAttestations([\n          {\n            ...attestations.first,\n            value: 2,\n          },\n          { ...attestations.second, value: 2 },\n        ]);\n\n      // 1 - Checks that the transaction emitted the event\n      await expect(recordAttestationsTransaction)\n        .to.emit(attestationsRegistry, 'AttestationRecorded')\n        .withArgs([\n          BigNumber.from(attestations.first.collectionId),\n          attestations.first.owner,\n          attestations.first.issuer,\n          BigNumber.from(2),\n          attestations.first.timestamp,\n          ethers.utils.hexlify(attestations.first.extraData),\n        ]);\n\n      await expect(recordAttestationsTransaction)\n        .to.emit(attestationsRegistry, 'AttestationRecorded')\n        .withArgs([\n          BigNumber.from(attestations.second.collectionId),\n          attestations.second.owner,\n          attestations.second.issuer,\n          BigNumber.from(2),\n          attestations.second.timestamp,\n          ethers.utils.hexlify(attestations.second.extraData),\n        ]);\n\n      // 1.2 - Checks that the events related to the badges are emitted\n      await expect(recordAttestationsTransaction)\n        .to.emit(badges, 'TransferSingle')\n        .withArgs(\n          attestationsRegistry.address,\n          ethers.constants.AddressZero,\n          user.address,\n          attestations.first.collectionId,\n          BigNumber.from(1)\n        );\n\n      await expect(recordAttestationsTransaction)\n        .to.emit(badges, 'TransferSingle')\n        .withArgs(\n          attestationsRegistry.address,\n          ethers.constants.AddressZero,\n          user.address,\n          attestations.second.collectionId,\n          BigNumber.from(1)\n        );\n\n      // 2 - Checks that the attestations are still owned by the same user\n      expect(await attestationsRegistry.hasAttestation(firstAuthorizedRange.min, user.address)).to\n        .be.true;\n      expect(await attestationsRegistry.hasAttestation(secondAuthorizedRange.min, user.address)).to\n        .be.true;\n      // 2.2 - Checks that the user has received it's the new minted badges\n      expect(await badges.balanceOf(user.address, attestations.first.collectionId)).to.equal(2);\n      expect(await badges.balanceOf(user.address, attestations.second.collectionId)).to.equal(2);\n\n      // 3 - Checks that the value of the attestations are recorded\n      expect(\n        await attestationsRegistry.getAttestationValue(firstAuthorizedRange.min, user.address)\n      ).to.be.equal(BigNumber.from(2));\n\n      expect(\n        await attestationsRegistry.getAttestationValue(secondAuthorizedRange.min, user.address)\n      ).to.be.equal(BigNumber.from(2));\n    });\n  });\n\n  /*************************************************************************************/\n  /******************************* GET ATTESTATION DATA ********************************/\n  /*************************************************************************************/\n  describe('Get Attestation Data', () => {\n    before(async () => {\n      await attestationsRegistry\n        .connect(issuer)\n        .deleteAttestations(\n          [attestations.first.owner, attestations.second.owner],\n          [attestations.first.collectionId, attestations.second.collectionId]\n        );\n    });\n\n    it('Should return the right data', async () => {\n      await attestationsRegistry\n        .connect(issuer)\n        .recordAttestations([attestations.first, attestations.second]);\n\n      expect(\n        await attestationsRegistry.getAttestationData(attestations.first.collectionId, user.address)\n      ).to.be.eql([\n        attestations.first.issuer,\n        BigNumber.from(attestations.first.value),\n        attestations.first.timestamp,\n        ethers.utils.hexlify(attestations.first.extraData),\n      ]);\n\n      expect(\n        await attestationsRegistry.getAttestationData(\n          attestations.second.collectionId,\n          user.address\n        )\n      ).to.be.eql([\n        attestations.second.issuer,\n        BigNumber.from(attestations.second.value),\n        attestations.second.timestamp,\n        ethers.utils.hexlify(attestations.second.extraData),\n      ]);\n    });\n  });\n\n  /*************************************************************************************/\n  /**************************** GET ATTESTATION DATA TUPLE *****************************/\n  /*************************************************************************************/\n  describe('Get Attestation Data Tuple', () => {\n    before(async () => {\n      await attestationsRegistry\n        .connect(issuer)\n        .deleteAttestations(\n          [attestations.first.owner, attestations.second.owner],\n          [attestations.first.collectionId, attestations.second.collectionId]\n        );\n    });\n\n    it('Should return the right data', async () => {\n      await attestationsRegistry\n        .connect(issuer)\n        .recordAttestations([attestations.first, attestations.second]);\n\n      expect(\n        await attestationsRegistry.getAttestationDataTuple(\n          attestations.first.collectionId,\n          user.address\n        )\n      ).to.be.eql([\n        attestations.first.issuer,\n        BigNumber.from(attestations.first.value),\n        attestations.first.timestamp,\n        ethers.utils.hexlify(attestations.first.extraData),\n      ]);\n\n      expect(\n        await attestationsRegistry.getAttestationDataTuple(\n          attestations.second.collectionId,\n          user.address\n        )\n      ).to.be.eql([\n        attestations.second.issuer,\n        BigNumber.from(attestations.second.value),\n        attestations.second.timestamp,\n        ethers.utils.hexlify(attestations.second.extraData),\n      ]);\n    });\n  });\n\n  /*************************************************************************************/\n  /**************************** GET ATTESTATION VALUE **********************************/\n  /*************************************************************************************/\n  describe('Get Attestation Value', () => {\n    before(async () => {\n      await attestationsRegistry\n        .connect(issuer)\n        .deleteAttestations(\n          [attestations.first.owner, attestations.second.owner],\n          [attestations.first.collectionId, attestations.second.collectionId]\n        );\n    });\n\n    it('Should return the right data', async () => {\n      await attestationsRegistry\n        .connect(issuer)\n        .recordAttestations([attestations.first, attestations.second]);\n\n      expect(\n        await attestationsRegistry.getAttestationValue(firstAuthorizedRange.min, user.address)\n      ).to.be.equal(attestations.first.value);\n\n      expect(\n        await attestationsRegistry.getAttestationValue(secondAuthorizedRange.min, user.address)\n      ).to.be.equal(attestations.second.value);\n    });\n  });\n\n  /*************************************************************************************/\n  /************************** GET ATTESTATION EXTRA DATA *******************************/\n  /*************************************************************************************/\n  describe('Get Attestation Extra Data', () => {\n    before(async () => {\n      await attestationsRegistry\n        .connect(issuer)\n        .deleteAttestations(\n          [attestations.first.owner, attestations.second.owner],\n          [attestations.first.collectionId, attestations.second.collectionId]\n        );\n    });\n\n    it('Should return the right data', async () => {\n      await attestationsRegistry\n        .connect(issuer)\n        .recordAttestations([attestations.first, attestations.second]);\n\n      expect(\n        await attestationsRegistry.getAttestationExtraData(firstAuthorizedRange.min, user.address)\n      ).to.be.equal(ethers.utils.hexlify(attestations.first.extraData));\n\n      expect(\n        await attestationsRegistry.getAttestationExtraData(secondAuthorizedRange.min, user.address)\n      ).to.be.equal(ethers.utils.hexlify(attestations.second.extraData));\n    });\n  });\n\n  /*************************************************************************************/\n  /**************************** GET ATTESTATION ISSUER *********************************/\n  /*************************************************************************************/\n  describe('Get Attestation Issuer', () => {\n    before(async () => {\n      await attestationsRegistry\n        .connect(issuer)\n        .deleteAttestations(\n          [attestations.first.owner, attestations.second.owner],\n          [attestations.first.collectionId, attestations.second.collectionId]\n        );\n    });\n\n    it('Should return the right data', async () => {\n      await attestationsRegistry\n        .connect(issuer)\n        .recordAttestations([attestations.first, attestations.second]);\n\n      expect(\n        await attestationsRegistry.getAttestationIssuer(firstAuthorizedRange.min, user.address)\n      ).to.be.equal(attestations.first.issuer);\n\n      expect(\n        await attestationsRegistry.getAttestationIssuer(secondAuthorizedRange.min, user.address)\n      ).to.be.equal(attestations.second.issuer);\n    });\n  });\n\n  /*************************************************************************************/\n  /*************************** GET ATTESTATION TIMESTAMP *******************************/\n  /*************************************************************************************/\n  describe('Get Attestation Timestamp', () => {\n    before(async () => {\n      await attestationsRegistry\n        .connect(issuer)\n        .deleteAttestations(\n          [attestations.first.owner, attestations.second.owner],\n          [attestations.first.collectionId, attestations.second.collectionId]\n        );\n    });\n\n    it('Should return the right data', async () => {\n      await attestationsRegistry\n        .connect(issuer)\n        .recordAttestations([attestations.first, attestations.second]);\n\n      expect(\n        await attestationsRegistry.getAttestationTimestamp(firstAuthorizedRange.min, user.address)\n      ).to.be.equal(attestations.first.timestamp);\n\n      expect(\n        await attestationsRegistry.getAttestationTimestamp(secondAuthorizedRange.min, user.address)\n      ).to.be.equal(attestations.second.timestamp);\n    });\n  });\n\n  /*************************************************************************************/\n  /**************************** GET ATTESTATION DATA BATCH *****************************/\n  /*************************************************************************************/\n  describe('Get Attestation Data Batch', () => {\n    it('Should return the right data', async () => {\n      expect(\n        await attestationsRegistry.getAttestationDataBatch(\n          [firstAuthorizedRange.min, secondAuthorizedRange.min],\n          [user.address, user.address]\n        )\n      ).to.be.eql([\n        [\n          attestations.first.issuer,\n          BigNumber.from(attestations.first.value),\n          attestations.first.timestamp,\n          ethers.utils.hexlify(attestations.first.extraData),\n        ],\n        [\n          attestations.second.issuer,\n          BigNumber.from(attestations.second.value),\n          attestations.second.timestamp,\n          ethers.utils.hexlify(attestations.second.extraData),\n        ],\n      ]);\n    });\n  });\n\n  /*************************************************************************************/\n  /**************************** GET ATTESTATION VALUE BATCH ****************************/\n  /*************************************************************************************/\n  describe('Get Attestation Value Batch', () => {\n    it('Should return the right data', async () => {\n      expect(\n        await attestationsRegistry.getAttestationValueBatch(\n          [firstAuthorizedRange.min, secondAuthorizedRange.min],\n          [user.address, user.address]\n        )\n      ).to.be.eql([\n        BigNumber.from(attestations.first.value),\n        BigNumber.from(attestations.second.value),\n      ]);\n    });\n  });\n\n  /*************************************************************************************/\n  /****************************** DELETE ATTESTATIONS **********************************/\n  /*************************************************************************************/\n  describe('Delete attestations', () => {\n    it('Should revert when the contract is paused', async () => {\n      await attestationsRegistry.pause();\n\n      await expect(\n        attestationsRegistry.deleteAttestations(\n          [attestations.first.owner],\n          [attestations.first.collectionId]\n        )\n      ).to.be.revertedWith('Pausable: paused');\n\n      await attestationsRegistry.unpause();\n    });\n\n    it('Should revert when the attester is not authorized in one of the collectionIds provided', async () => {\n      await attestationsRegistry.unauthorizeRanges(\n        issuer.address,\n        [firstAuthorizedRange, secondAuthorizedRange],\n        [0, 1]\n      );\n\n      await expect(\n        attestationsRegistry\n          .connect(issuer)\n          .deleteAttestations(\n            [attestations.first.owner, attestations.second.owner],\n            [firstAuthorizedRange.min - 1, attestations.second.collectionId]\n          )\n      ).to.be.revertedWith(\n        `IssuerNotAuthorized(\"${issuer.address}\", ${firstAuthorizedRange.min - 1})`\n      );\n\n      await attestationsRegistry.authorizeRange(\n        issuer.address,\n        firstAuthorizedRange.min,\n        firstAuthorizedRange.max\n      );\n\n      await expect(\n        attestationsRegistry\n          .connect(issuer)\n          .deleteAttestations(\n            [attestations.first.owner, attestations.second.owner],\n            [attestations.first.collectionId, secondAuthorizedRange.min - 1]\n          )\n      ).to.be.revertedWith(\n        `IssuerNotAuthorized(\"${issuer.address}\", ${secondAuthorizedRange.min - 1})`\n      );\n    });\n\n    it('Should delete the attestations', async () => {\n      await attestationsRegistry.authorizeRanges(issuer.address, [\n        firstAuthorizedRange,\n        secondAuthorizedRange,\n      ]);\n\n      const deleteAttestationsTransaction = await attestationsRegistry\n        .connect(issuer)\n        .deleteAttestations(\n          [attestations.first.owner, attestations.second.owner],\n          [attestations.first.collectionId, attestations.second.collectionId]\n        );\n\n      // 1 - Checks that the transaction emitted the event\n      // 1.1 - Checks that the events related to the attestations registry are emitted\n      await expect(deleteAttestationsTransaction)\n        .to.emit(attestationsRegistry, 'AttestationDeleted')\n        .withArgs([\n          BigNumber.from(attestations.first.collectionId),\n          attestations.first.owner,\n          attestations.first.issuer,\n          BigNumber.from(attestations.first.value),\n          attestations.first.timestamp,\n          ethers.utils.hexlify(attestations.first.extraData),\n        ]);\n\n      await expect(deleteAttestationsTransaction)\n        .to.emit(attestationsRegistry, 'AttestationDeleted')\n        .withArgs([\n          BigNumber.from(attestations.second.collectionId),\n          attestations.second.owner,\n          attestations.second.issuer,\n          BigNumber.from(attestations.second.value),\n          attestations.second.timestamp,\n          ethers.utils.hexlify(attestations.second.extraData),\n        ]);\n\n      // 1.2 - Checks that the events related to the badges are emitted\n      await expect(deleteAttestationsTransaction)\n        .to.emit(badges, 'TransferSingle')\n        .withArgs(\n          attestationsRegistry.address,\n          user.address,\n          ethers.constants.AddressZero,\n          attestations.first.collectionId,\n          attestations.first.value\n        );\n\n      await expect(deleteAttestationsTransaction)\n        .to.emit(badges, 'TransferSingle')\n        .withArgs(\n          attestationsRegistry.address,\n          user.address,\n          ethers.constants.AddressZero,\n          attestations.second.collectionId,\n          attestations.second.value\n        );\n\n      // 2 - Checks that the attestations was deleted on the right user\n      expect(await attestationsRegistry.hasAttestation(firstAuthorizedRange.min, user.address)).to\n        .be.false;\n      expect(await attestationsRegistry.hasAttestation(secondAuthorizedRange.min, user.address)).to\n        .be.false;\n      // 2.2 - Checks that the user has not anymore it's badges\n      expect(await badges.balanceOf(user.address, attestations.first.collectionId)).to.equal(\n        BigNumber.from(0)\n      );\n      expect(await badges.balanceOf(user.address, attestations.second.collectionId)).to.equal(\n        BigNumber.from(0)\n      );\n    });\n  });\n});\n"
  },
  {
    "path": "test/unit/core/badges.test.ts",
    "content": "import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers';\nimport { expect } from 'chai';\nimport hre, { ethers } from 'hardhat';\nimport { deploymentsConfig } from '../../../tasks/deploy-tasks/deployments-config';\nimport {\n  AttestationsRegistry,\n  Badges,\n  MockAttestationsRegistry,\n  TransparentUpgradeableProxy__factory,\n} from '../../../types';\nimport { getImplementation } from '../../../utils';\n\ndescribe('Test Badges contract', () => {\n  let deployer: SignerWithAddress;\n  let secondDeployer: SignerWithAddress;\n  let admin: SignerWithAddress;\n  let notAdmin: SignerWithAddress;\n  let user: SignerWithAddress;\n  let issuer: SignerWithAddress;\n  let eoaAttestationsRegistry: SignerWithAddress;\n  let roles: { admin: string; event_trigger: string };\n\n  let attestationsRegistry: AttestationsRegistry;\n  let mockAttestationsRegistry: MockAttestationsRegistry;\n  let badges: Badges;\n  let secondBadges: Badges;\n\n  before(async () => {\n    [deployer, secondDeployer, admin, notAdmin, user, issuer, eoaAttestationsRegistry] =\n      await ethers.getSigners();\n  });\n\n  /*************************************************************************************/\n  /********************************** DEPLOYMENTS **************************************/\n  /*************************************************************************************/\n  describe('Deployments', () => {\n    it('Should deploy, setup and test the constructed values of the contract', async () => {\n      ({ attestationsRegistry, badges } = await hre.run('deploy-core', {\n        uri: 'https://token_cdn.domain/',\n        frontFirstCollectionId: '1',\n        frontLastCollectionId: '2',\n      }));\n\n      ({ badges: secondBadges } = await hre.run('deploy-badges', {\n        uri: 'https://token_cdn.domain/',\n        owner: secondDeployer.address,\n      }));\n\n      ({ mockAttestationsRegistry } = await hre.run('deploy-mock-attestations-registry', {\n        attestationValue: 1,\n        options: {\n          behindProxy: false,\n        },\n      }));\n\n      roles = {\n        admin: await badges.DEFAULT_ADMIN_ROLE(),\n        event_trigger: await badges.EVENT_TRIGGERER_ROLE(),\n      };\n\n      // 0 - Checks that the role owner is set to the deployer address\n      expect(await badges.hasRole(roles.admin, deployer.address)).to.be.true;\n      expect(await secondBadges.hasRole(roles.admin, secondDeployer.address)).to.be.true;\n    });\n  });\n\n  describe('Configuration checks', () => {\n    it('Should have setup the admin role correctly', async () => {\n      const adminRole = await badges.DEFAULT_ADMIN_ROLE();\n      expect(await badges.hasRole(adminRole, deployer.address)).to.be.true;\n    });\n\n    it('Should revert when trying to call initialize again', async () => {\n      await expect(\n        badges.connect(deployer).initialize('fake_uri', deployer.address)\n      ).to.be.revertedWith('Initializable: contract is already initialized');\n    });\n  });\n\n  /*************************************************************************************/\n  /***************************** SET ATTESTATIONS REGISTRY *****************************/\n  /*************************************************************************************/\n  describe('Set Attestations Registry', () => {\n    it('Should revert when the sender has not the admin role', async () => {\n      await expect(\n        badges.connect(notAdmin).setAttestationsRegistry(attestationsRegistry.address)\n      ).to.be.revertedWith(\n        `AccessControl: account ${notAdmin.address.toLowerCase()} is missing role ${roles.admin}`\n      );\n    });\n\n    it('Should set the attestations registry', async () => {\n      await badges.setAttestationsRegistry(attestationsRegistry.address);\n\n      expect(await badges.getAttestationsRegistry()).to.equal(attestationsRegistry.address);\n    });\n\n    it('Should set the mock attestations registry', async () => {\n      await badges.setAttestationsRegistry(mockAttestationsRegistry.address);\n\n      expect(await badges.getAttestationsRegistry()).to.equal(mockAttestationsRegistry.address);\n    });\n  });\n\n  /*************************************************************************************/\n  /************************************** SET URI **************************************/\n  /*************************************************************************************/\n  describe('Set URI', () => {\n    it('Should revert when the sender has not the admin role', async () => {\n      await expect(badges.connect(notAdmin).setUri('https://token_cdn.domain/')).to.be.revertedWith(\n        `AccessControl: account ${notAdmin.address.toLowerCase()} is missing role ${roles.admin}`\n      );\n    });\n\n    it('Should set the URI', async () => {\n      await badges.setUri('https://token_cdn.domain/{id}.json');\n\n      expect(await badges.uri(0)).to.equal('https://token_cdn.domain/{id}.json');\n    });\n  });\n\n  /*************************************************************************************/\n  /************************************* GRANT ROLE ************************************/\n  /*************************************************************************************/\n  describe('Grant Role', async () => {\n    it('Should revert when the sender has not the admin role', async () => {\n      await expect(\n        badges.connect(notAdmin).grantRole(roles.admin, deployer.address)\n      ).to.be.revertedWith(\n        `AccessControl: account ${notAdmin.address.toLowerCase()} is missing role ${roles.admin}`\n      );\n    });\n\n    it('Should grant the default admin role to the address', async () => {\n      await badges.grantRole(roles.admin, admin.address);\n\n      expect(await badges.hasRole(roles.admin, admin.address)).to.be.true;\n    });\n\n    it('Should grant the custom defined event trigger role to the attestations registry and the eoa attestations registry', async () => {\n      await badges.grantRole(roles.event_trigger, attestationsRegistry.address);\n      await badges.grantRole(roles.event_trigger, eoaAttestationsRegistry.address);\n\n      expect(await badges.hasRole(roles.event_trigger, attestationsRegistry.address)).to.be.true;\n      expect(await badges.hasRole(roles.event_trigger, eoaAttestationsRegistry.address)).to.be.true;\n    });\n  });\n\n  /*************************************************************************************/\n  /******************************* TRIGGER TRANSFER EVENT ******************************/\n  /*************************************************************************************/\n  describe('Trigger transfer event', async () => {\n    const badgeId = 3;\n    const transferredAmount = 1;\n\n    it('Should revert when the sender has not the event trigger role', async () => {\n      await expect(\n        badges.triggerTransferEvent(\n          attestationsRegistry.address,\n          ethers.constants.AddressZero,\n          user.address,\n          badgeId,\n          transferredAmount\n        )\n      ).to.be.revertedWith(\n        `AccessControl: account ${deployer.address.toLowerCase()} is missing role ${roles.event_trigger.toLowerCase()}`\n      );\n    });\n\n    it('Should revert when the sender is an admin', async () => {\n      await expect(\n        badges\n          .connect(admin)\n          .triggerTransferEvent(\n            attestationsRegistry.address,\n            ethers.constants.AddressZero,\n            user.address,\n            badgeId,\n            transferredAmount\n          )\n      ).to.be.revertedWith(\n        `AccessControl: account ${admin.address.toLowerCase()} is missing role ${\n          roles.event_trigger\n        }`\n      );\n    });\n\n    it('Should execute and emit a TransferSingle event', async () => {\n      const mockTriggerTransferEventTransaction = await badges\n        .connect(eoaAttestationsRegistry)\n        .triggerTransferEvent(\n          eoaAttestationsRegistry.address,\n          ethers.constants.AddressZero,\n          user.address,\n          badgeId,\n          transferredAmount\n        );\n\n      await expect(mockTriggerTransferEventTransaction)\n        .to.emit(badges, 'TransferSingle')\n        .withArgs(\n          eoaAttestationsRegistry.address,\n          ethers.constants.AddressZero,\n          user.address,\n          badgeId,\n          transferredAmount\n        );\n    });\n  });\n\n  /*************************************************************************************/\n  /************************************** TRANSFERS ************************************/\n  /*************************************************************************************/\n  describe('Transfers', () => {\n    describe('Safe Transfer Single', async () => {\n      it('Should revert because the operation is not allowed', async () => {\n        await expect(\n          badges.connect(user).safeTransferFrom(user.address, admin.address, 3, 1, [])\n        ).to.be.revertedWith('BadgesNonTransferrable()');\n      });\n    });\n\n    describe('Safe Transfer Batch', () => {\n      it('Should revert because the operation is not allowed', async () => {\n        await expect(\n          badges\n            .connect(user)\n            .safeBatchTransferFrom(user.address, admin.address, [3, 4], [1, 1], [])\n        ).to.be.revertedWith('BadgesNonTransferrable()');\n      });\n    });\n  });\n\n  describe('Approvals', () => {\n    describe('isApprovedForAll', () => {\n      it('Should revert because the operation is not allowed', async () => {\n        await expect(\n          badges.connect(user).isApprovedForAll(user.address, admin.address)\n        ).to.be.revertedWith('BadgesNonTransferrable()');\n      });\n    });\n\n    describe('setApprovalForAll', () => {\n      it('Should revert because the operation is not allowed', async () => {\n        await expect(badges.connect(user).setApprovalForAll(user.address, true)).to.be.revertedWith(\n          'BadgesNonTransferrable()'\n        );\n      });\n    });\n  });\n\n  /*************************************************************************************/\n  /************************************ REVOKE ROLE ************************************/\n  /*************************************************************************************/\n  describe('Revoke role', async () => {\n    it('Should revert when the sender has not the admin role', async () => {\n      await expect(\n        badges.connect(notAdmin).revokeRole(roles.admin, admin.address)\n      ).to.be.revertedWith(\n        `AccessControl: account ${notAdmin.address.toLowerCase()} is missing role ${roles.admin}`\n      );\n    });\n\n    it('Should revoke the custom defined event trigger role from the attestations registry and the mock attestations registry', async () => {\n      const revokeMockAttestationsRegistryRoleTransaction = await badges.revokeRole(\n        roles.event_trigger,\n        eoaAttestationsRegistry.address\n      );\n      const revokeAttestationsRegistryRoleTransaction = await badges.revokeRole(\n        roles.event_trigger,\n        attestationsRegistry.address\n      );\n\n      await expect(revokeMockAttestationsRegistryRoleTransaction)\n        .to.emit(badges, 'RoleRevoked')\n        .withArgs(roles.event_trigger, eoaAttestationsRegistry.address, deployer.address);\n      await expect(revokeAttestationsRegistryRoleTransaction)\n        .to.emit(badges, 'RoleRevoked')\n        .withArgs(roles.event_trigger, attestationsRegistry.address, deployer.address);\n\n      expect(await badges.hasRole(roles.event_trigger, eoaAttestationsRegistry.address)).to.be\n        .false;\n      expect(await badges.hasRole(roles.event_trigger, attestationsRegistry.address)).to.be.false;\n    });\n\n    it('Should revoke the admin role of the admin', async () => {\n      const revokeRoleTransaction = await badges.revokeRole(roles.admin, admin.address);\n\n      await expect(revokeRoleTransaction)\n        .to.emit(badges, 'RoleRevoked')\n        .withArgs(roles.admin, admin.address, deployer.address);\n\n      await expect(await badges.hasRole(roles.admin, admin.address)).to.be.false;\n    });\n  });\n\n  /*************************************************************************************/\n  /********************************** RENOUNCE ROLE ************************************/\n  /*************************************************************************************/\n  describe('Renounce Role', async () => {\n    before(async () => {\n      await badges.grantRole(roles.admin, admin.address);\n    });\n\n    it('Should revert when the sender is not the one provided in parameters', async () => {\n      await expect(\n        badges.connect(notAdmin).renounceRole(roles.admin, admin.address)\n      ).to.be.revertedWith(`AccessControl: can only renounce roles for self`);\n    });\n\n    it('Should revoke the admin role of the admin', async () => {\n      const renounceRoleTransaction = await badges\n        .connect(admin)\n        .renounceRole(roles.admin, admin.address);\n\n      await expect(renounceRoleTransaction)\n        .to.emit(badges, 'RoleRevoked')\n        .withArgs(roles.admin, admin.address, admin.address);\n\n      await expect(await badges.hasRole(roles.admin, admin.address)).to.be.false;\n    });\n  });\n\n  /*************************************************************************************/\n  /******************************* UPDATE IMPLEMENTATION *******************************/\n  /*************************************************************************************/\n  describe('Update implementation', () => {\n    it('Should update the implementation', async () => {\n      const proxyAdminSigner = await ethers.getSigner(\n        deploymentsConfig[hre.network.name].deployOptions.proxyAdmin as string\n      );\n\n      const { badges: newBadges } = await hre.run('deploy-badges', {\n        uri: 'https://token_cdn.domain/',\n        owner: secondDeployer.address,\n        options: { behindProxy: false },\n      });\n      const badgesProxy = TransparentUpgradeableProxy__factory.connect(\n        badges.address,\n        proxyAdminSigner\n      );\n\n      await (await badgesProxy.upgradeTo(newBadges.address)).wait();\n\n      const implementationAddress = await getImplementation(badgesProxy);\n      expect(implementationAddress).to.eql(newBadges.address);\n    });\n  });\n});\n"
  },
  {
    "path": "test/unit/core/front.test.ts",
    "content": "import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers';\nimport { expect } from 'chai';\nimport { BigNumber, BigNumberish, ContractTransaction } from 'ethers';\nimport hre, { ethers } from 'hardhat';\nimport { AttestationsRegistry, Front, MockAttester } from 'types';\nimport { AttestationStruct, RequestStruct } from 'types/HydraS1SimpleAttester';\nimport { evmRevert, evmSnapshot, increaseTime } from '../../../test/utils';\n\ntype IssuerRange = {\n  first: number;\n  last: number;\n};\n\ntype Requests = {\n  first: RequestStruct;\n  second: RequestStruct;\n};\n\ntype Attestations = {\n  first: AttestationStruct;\n  second: AttestationStruct;\n};\n\ntype AttestationArray = [BigNumber, string, string, BigNumber, BigNumberish, string];\n\nasync function assertAttestationRecordedEventEmitted(\n  transaction: ContractTransaction,\n  attestationsRegistry: AttestationsRegistry,\n  attesation: AttestationStruct\n) {\n  await expect(transaction)\n    .to.emit(attestationsRegistry, 'AttestationRecorded')\n    .withArgs([\n      BigNumber.from(attesation.collectionId),\n      attesation.owner,\n      attesation.issuer,\n      BigNumber.from(attesation.value),\n      attesation.timestamp,\n      ethers.utils.hexlify(attesation.extraData),\n    ]);\n}\n\nasync function assertAttestationDataIsValid(\n  attestationsRegistry: AttestationsRegistry,\n  attestation: AttestationStruct,\n  destination: SignerWithAddress\n) {\n  expect(\n    await attestationsRegistry.getAttestationData(attestation.collectionId, destination.address)\n  ).to.eql([\n    attestation.issuer,\n    BigNumber.from(attestation.value),\n    attestation.timestamp,\n    ethers.utils.hexlify(attestation.extraData),\n  ]);\n}\n\ndescribe('Test Front contract', () => {\n  let userDestination: SignerWithAddress;\n  let secondUserDestination: SignerWithAddress;\n\n  let front: Front;\n  let attestationsRegistry: AttestationsRegistry;\n  let mockAttester: MockAttester;\n\n  let frontAuthorizedRange: IssuerRange;\n  let mockAttesterAuthorizedRange: IssuerRange;\n  let generationTimestamp: number;\n  let requests: Requests;\n  let attestations: Attestations;\n  let attestationsArray: AttestationArray[][];\n  let earlyUserGeneratedAttesation: AttestationStruct;\n\n  before(async () => {\n    [userDestination, secondUserDestination] = await ethers.getSigners();\n\n    frontAuthorizedRange = {\n      first: 0,\n      last: 1,\n    };\n\n    mockAttesterAuthorizedRange = {\n      first: 2,\n      last: 3,\n    };\n\n    generationTimestamp = Math.floor(Date.now() / 1000);\n\n    requests = {\n      first: {\n        claims: [\n          {\n            groupId: 0,\n            claimedValue: 1,\n            extraData: ethers.utils.defaultAbiCoder.encode(['uint32'], [generationTimestamp]),\n          },\n        ],\n        destination: userDestination.address,\n      },\n      second: {\n        claims: [\n          {\n            groupId: 1,\n            claimedValue: 1,\n            extraData: ethers.utils.defaultAbiCoder.encode(['uint32'], [generationTimestamp]),\n          },\n        ],\n        destination: userDestination.address,\n      },\n    };\n  });\n\n  /*************************************************************************************/\n  /********************************** DEPLOYMENTS **************************************/\n  /*************************************************************************************/\n  describe('Deployments', () => {\n    it('Should deploy, setup and test the constructed values of the contract', async () => {\n      ({\n        front,\n        mockAttester: mockAttester,\n        attestationsRegistry,\n      } = await hre.run('deploy-mock-attester-and-core', {\n        uri: 'https://token_cdn.domain/',\n        frontFirstCollectionId: frontAuthorizedRange.first.toString(),\n        frontLastCollectionId: frontAuthorizedRange.last.toString(),\n        collectionIdFirst: mockAttesterAuthorizedRange.first.toString(),\n        collectionIdLast: mockAttesterAuthorizedRange.last.toString(),\n        options: {\n          behindProxy: false,\n        },\n      }));\n\n      attestations = {\n        first: {\n          collectionId: mockAttesterAuthorizedRange.first,\n          owner: userDestination.address,\n          issuer: mockAttester.address,\n          value: 1,\n          timestamp: generationTimestamp,\n          extraData: ethers.utils.toUtf8Bytes('Mock Attester v0'),\n        },\n        second: {\n          collectionId: mockAttesterAuthorizedRange.last,\n          owner: userDestination.address,\n          issuer: mockAttester.address,\n          value: 1,\n          timestamp: generationTimestamp,\n          extraData: ethers.utils.toUtf8Bytes('Mock Attester v0'),\n        },\n      };\n\n      earlyUserGeneratedAttesation = {\n        collectionId: await front.EARLY_USER_COLLECTION(),\n        owner: userDestination.address,\n        issuer: front.address,\n        value: 1,\n        timestamp: 0,\n        extraData: ethers.utils.toUtf8Bytes('With strong love from Sismo'),\n      };\n\n      attestationsArray = [\n        [\n          [\n            BigNumber.from(attestations.first.collectionId),\n            attestations.first.owner,\n            attestations.first.issuer,\n            BigNumber.from(attestations.first.value),\n            attestations.first.timestamp,\n            ethers.utils.hexlify(attestations.first.extraData),\n          ],\n        ],\n        [\n          [\n            BigNumber.from(attestations.second.collectionId),\n            attestations.second.owner,\n            attestations.second.issuer,\n            BigNumber.from(attestations.second.value),\n            attestations.second.timestamp,\n            ethers.utils.hexlify(attestations.second.extraData),\n          ],\n        ],\n      ];\n    });\n  });\n\n  /*************************************************************************************/\n  /***************************** GENERATE ATTESTATIONS *********************************/\n  /*************************************************************************************/\n  describe('Generate attestations', () => {\n    let evmPreOperationSnapshot;\n\n    beforeEach(async () => {\n      evmPreOperationSnapshot = await evmSnapshot(hre);\n    });\n\n    afterEach(async () => {\n      evmRevert(hre, evmPreOperationSnapshot);\n    });\n\n    it('Should forward generateAttestations call to the attester', async () => {\n      const generateAttestationsTransaction = await front.generateAttestations(\n        mockAttester.address,\n        requests.first,\n        '0x'\n      );\n\n      // 1 - Checks that the transaction emitted the event\n      await assertAttestationRecordedEventEmitted(\n        generateAttestationsTransaction,\n        attestationsRegistry,\n        attestations.first\n      );\n\n      // 2 - Checks that forwarded attestations was recorded on the user\n      expect(\n        await attestationsRegistry.hasAttestation(\n          await mockAttester.ATTESTATION_ID_MIN(),\n          userDestination.address\n        )\n      ).to.be.true;\n\n      await assertAttestationDataIsValid(attestationsRegistry, attestations.first, userDestination);\n    });\n    it('Should not generate early user attestation if the date is > to Sept 15 2022', async () => {\n      if (Date.now() > Date.parse('15 Sept 2022 00:00:00 GMT')) {\n        return;\n      }\n      await increaseTime(hre, Date.parse('16 Sept 20222 00:00:00 GMT') - Date.now());\n\n      const generateAttestationsTransaction = await front.generateAttestations(\n        mockAttester.address,\n        requests.first,\n        '0x'\n      );\n\n      // 1 - Checks that the transaction didn't emitted the event\n      expect(generateAttestationsTransaction).to.not.emit(front, 'EarlyUserAttestationGenerated');\n\n      // 2 - Checks that early user attestation was not recorded on the user\n      expect(\n        await attestationsRegistry.hasAttestation(\n          await front.EARLY_USER_COLLECTION(),\n          userDestination.address\n        )\n      ).to.be.false;\n    });\n\n    it('Should generate early user attestation if the date is < to Sept 15 2022', async () => {\n      if (Date.now() > Date.parse('15 Sept 2022 00:00:00 GMT')) {\n        return;\n      }\n\n      const generateAttestationsTransaction = await front.generateAttestations(\n        mockAttester.address,\n        requests.first,\n        '0x'\n      );\n\n      earlyUserGeneratedAttesation.timestamp = await (\n        await ethers.provider.getBlock('latest')\n      ).timestamp;\n\n      // 1 - Checks that the transaction emitted the events\n      await assertAttestationRecordedEventEmitted(\n        generateAttestationsTransaction,\n        attestationsRegistry,\n        earlyUserGeneratedAttesation\n      );\n\n      await expect(generateAttestationsTransaction)\n        .to.emit(front, 'EarlyUserAttestationGenerated')\n        .withArgs(earlyUserGeneratedAttesation.owner);\n\n      // 2 - Checks that early user attestation was recorded on the user\n      expect(\n        await attestationsRegistry.hasAttestation(\n          await front.EARLY_USER_COLLECTION(),\n          userDestination.address\n        )\n      ).to.be.true;\n\n      await assertAttestationDataIsValid(\n        attestationsRegistry,\n        earlyUserGeneratedAttesation,\n        userDestination\n      );\n    });\n\n    it('Should return the builded attestations', async () => {\n      expect(\n        await front.callStatic.generateAttestations(mockAttester.address, requests.first, '0x')\n      ).to.eql(attestationsArray[0]);\n\n      expect(\n        await front.callStatic.generateAttestations(mockAttester.address, requests.first, '0x')\n      ).to.eql(\n        await front.callStatic.buildAttestations(mockAttester.address, requests.first, '0x')\n      );\n    });\n  });\n\n  /*************************************************************************************/\n  /************************** BATCH GENERATE ATTESTATIONS ******************************/\n  /*************************************************************************************/\n  describe('Batch generate attestations', () => {\n    let evmPostOperationSnapshot;\n\n    beforeEach(async () => {\n      evmPostOperationSnapshot = await evmSnapshot(hre);\n    });\n\n    afterEach(async () => {\n      evmRevert(hre, evmPostOperationSnapshot);\n    });\n\n    it('Should revert when one address of the request destinations are not the same', async () => {\n      await expect(\n        front.batchGenerateAttestations(\n          [mockAttester.address, mockAttester.address],\n          [requests.first, { ...requests.second, destination: secondUserDestination.address }],\n          ['0x', '0x']\n        )\n      ).to.be.revertedWith('DifferentRequestsDestinations()');\n    });\n\n    it('Should forward generationAttestations call to the attester', async () => {\n      const generateAttestationsTransaction = await front.batchGenerateAttestations(\n        [mockAttester.address, mockAttester.address],\n        [requests.first, requests.second],\n        ['0x', '0x']\n      );\n\n      // 1 - Checks that the transaction emitted the events\n      await assertAttestationRecordedEventEmitted(\n        generateAttestationsTransaction,\n        attestationsRegistry,\n        attestations.first\n      );\n      await assertAttestationRecordedEventEmitted(\n        generateAttestationsTransaction,\n        attestationsRegistry,\n        attestations.second\n      );\n\n      // 2 - Checks that batch forwarded attestations was recorded on the user\n      expect(\n        await attestationsRegistry.hasAttestation(\n          await mockAttester.ATTESTATION_ID_MIN(),\n          userDestination.address\n        )\n      ).to.be.true;\n\n      expect(\n        await attestationsRegistry.hasAttestation(\n          await mockAttester.ATTESTATION_ID_MAX(),\n          userDestination.address\n        )\n      ).to.be.true;\n\n      await assertAttestationDataIsValid(attestationsRegistry, attestations.first, userDestination);\n      await assertAttestationDataIsValid(\n        attestationsRegistry,\n        attestations.second,\n        userDestination\n      );\n    });\n\n    it('Should not generate early user attestation if the date is > to Sept 15 2022', async () => {\n      if (Date.now() < Date.parse('15 Sept 2022 00:00:00 GMT')) {\n        await increaseTime(hre, Date.parse('15 Sept 20222 00:00:00 GMT') - Date.now());\n      }\n\n      const generateAttestationsTransaction = await front.batchGenerateAttestations(\n        [mockAttester.address, mockAttester.address],\n        [requests.first, requests.second],\n        ['0x', '0x']\n      );\n\n      // 1 - Checks that the transaction didn't emitted the event\n      expect(generateAttestationsTransaction).to.not.emit(front, 'EarlyUserAttestationGenerated');\n\n      // 2 - Checks that early user attestation was not recorded on the user\n      expect(\n        await attestationsRegistry.hasAttestation(\n          await front.EARLY_USER_COLLECTION(),\n          userDestination.address\n        )\n      ).to.be.false;\n    });\n\n    it('Should generate early user attestation if the date is < to Sept 15 2022', async () => {\n      if (Date.now() > Date.parse('15 Sept 2022 00:00:00 GMT')) {\n        return;\n      }\n\n      const batchGenerateAttestationsTransaction = await front.batchGenerateAttestations(\n        [mockAttester.address, mockAttester.address],\n        [requests.first, requests.second],\n        ['0x', '0x']\n      );\n\n      earlyUserGeneratedAttesation.timestamp = await (\n        await ethers.provider.getBlock('latest')\n      ).timestamp;\n\n      // 1 - Checks that the transaction emitted the events\n      await assertAttestationRecordedEventEmitted(\n        batchGenerateAttestationsTransaction,\n        attestationsRegistry,\n        earlyUserGeneratedAttesation\n      );\n\n      await expect(batchGenerateAttestationsTransaction)\n        .to.emit(front, 'EarlyUserAttestationGenerated')\n        .withArgs(userDestination.address);\n\n      // 2 - Checks that early user attestation was recorded on the user\n      expect(\n        await attestationsRegistry.hasAttestation(\n          await front.EARLY_USER_COLLECTION(),\n          userDestination.address\n        )\n      ).to.be.true;\n\n      await assertAttestationDataIsValid(\n        attestationsRegistry,\n        earlyUserGeneratedAttesation,\n        userDestination\n      );\n    });\n\n    it('Should return the batch builded attestations', async () => {\n      expect(\n        await front.callStatic.batchGenerateAttestations(\n          [mockAttester.address, mockAttester.address],\n          [requests.first, requests.second],\n          ['0x', '0x']\n        )\n      ).to.eql(attestationsArray);\n\n      expect(\n        await front.callStatic.batchGenerateAttestations(\n          [mockAttester.address, mockAttester.address],\n          [requests.first, requests.second],\n          ['0x', '0x']\n        )\n      ).to.eql(\n        await front.callStatic.batchBuildAttestations(\n          [mockAttester.address, mockAttester.address],\n          [requests.first, requests.second],\n          ['0x', '0x']\n        )\n      );\n    });\n  });\n\n  /*************************************************************************************/\n  /****************************** BUILD ATTESTATIONS ***********************************/\n  /*************************************************************************************/\n  describe('Build attestations', async () => {\n    it('Should forward buildAttestations call to the attester', async () => {\n      const buildAttestationsTransaction = await front.buildAttestations(\n        mockAttester.address,\n        requests.first,\n        ethers.utils.toUtf8Bytes('')\n      );\n\n      expect(buildAttestationsTransaction).to.eql(attestationsArray[0]);\n    });\n  });\n\n  /*************************************************************************************/\n  /*************************** BATCH BUILD ATTESTATIONS ********************************/\n  /*************************************************************************************/\n  describe('Batch build attestations', async () => {\n    it('Should forward buildAttestations to the attesters', async () => {\n      const buildAttestationsTransaction = await front.batchBuildAttestations(\n        [mockAttester.address, mockAttester.address],\n        [requests.first, requests.second],\n        ['0x', '0x']\n      );\n\n      expect(buildAttestationsTransaction).to.eql(attestationsArray);\n    });\n  });\n});\n"
  },
  {
    "path": "test/unit/core/utils/sismo-addresses-provider.test.ts",
    "content": "import { AvailableRootsRegistry } from './../../../../types/AvailableRootsRegistry';\nimport { CommitmentMapperRegistry } from './../../../../types/CommitmentMapperRegistry';\nimport { AddressesProvider } from '../../../../types/AddressesProvider';\nimport { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers';\nimport { expect } from 'chai';\nimport hre, { ethers } from 'hardhat';\nimport { Deployed0 } from 'tasks/deploy-tasks/full/0-deploy-core-and-hydra-s1-simple-and-accountbound-and-pythia1.task';\nimport {\n  AddressesProvider__factory,\n  AttestationsRegistry,\n  Badges,\n  Front,\n  HydraS1AccountboundAttester,\n  TransparentUpgradeableProxy__factory,\n} from '../../../../types';\nimport { HydraS1Verifier } from '@sismo-core/hydra-s1';\nimport { toUtf8Bytes } from 'ethers/lib/utils';\nimport { deploymentsConfig } from '../../../../tasks/deploy-tasks/deployments-config';\nimport { getImplementation } from '../../../../utils';\nimport { afterDeployment, beforeDeployment, customDeployContract } from 'tasks/deploy-tasks/utils';\n\nconst addressesProviderContractAddress = '0x3340Ac0CaFB3ae34dDD53dba0d7344C1Cf3EFE05';\n\ndescribe('Test Sismo Addresses Provider', () => {\n  let deployer: SignerWithAddress;\n  let proxyAdminSigner: SignerWithAddress;\n  let randomSigner: SignerWithAddress;\n\n  let attestationsRegistry: AttestationsRegistry;\n  let badges: Badges;\n  let front: Front;\n  let hydraS1AccountboundAttester: HydraS1AccountboundAttester;\n  let commitmentMapperRegistry: CommitmentMapperRegistry;\n  let availableRootsRegistry: AvailableRootsRegistry;\n  let hydraS1Verifier: HydraS1Verifier;\n\n  let sismoAddressesProvider: AddressesProvider;\n  let newSismoAddressesProvider: AddressesProvider;\n\n  before(async () => {\n    const signers = await hre.ethers.getSigners();\n    [deployer, , proxyAdminSigner, , , , randomSigner] = signers;\n    ({\n      attestationsRegistry,\n      badges,\n      hydraS1AccountboundAttester,\n      front,\n      availableRootsRegistry,\n      commitmentMapperRegistry,\n      hydraS1Verifier,\n    } = (await hre.run('0-deploy-core-and-hydra-s1-simple-and-accountbound-and-pythia1', {\n      options: {\n        proxyAdmin: proxyAdminSigner.address,\n      },\n    })) as Deployed0);\n  });\n\n  /*************************************************************************************/\n  /********************************** DEPLOYMENTS **************************************/\n  /*************************************************************************************/\n  describe('Deployments', () => {\n    it('Should deploy the Sismo Addresses Provider', async () => {\n      ({ sismoAddressesProvider } = await hre.run('deploy-sismo-addresses-provider', {\n        owner: deployer.address,\n        badges: badges.address,\n        attestationsRegistry: attestationsRegistry.address,\n        front: front.address,\n        hydraS1AccountboundAttester: hydraS1AccountboundAttester.address,\n        commitmentMapperRegistry: commitmentMapperRegistry.address,\n        availableRootsRegistry: availableRootsRegistry.address,\n        hydraS1Verifier: hydraS1Verifier.address,\n        options: {\n          deploymentNamePrefix: 'firstDeployment',\n        },\n      }));\n      expect(sismoAddressesProvider.address).to.be.eql(addressesProviderContractAddress);\n    });\n  });\n\n  describe('Getters', () => {\n    it('Should get the sismo contracts addresses via get (string)', async () => {\n      const badgesAddress = await sismoAddressesProvider['get(string)']('Badges');\n      const attestationsRegistryAddress = await sismoAddressesProvider['get(string)'](\n        'AttestationsRegistry'\n      );\n      const frontAddress = await sismoAddressesProvider['get(string)']('Front');\n      const hydraS1AccountboundAttesterAddress = await sismoAddressesProvider['get(string)'](\n        'HydraS1AccountboundAttester'\n      );\n      const commitmentMapperRegistryAddress = await sismoAddressesProvider['get(string)'](\n        'CommitmentMapperRegistry'\n      );\n      const availableRootsRegistryAddress = await sismoAddressesProvider['get(string)'](\n        'AvailableRootsRegistry'\n      );\n      const hydraS1VerifierAddress = await sismoAddressesProvider['get(string)']('HydraS1Verifier');\n\n      expect(badgesAddress).to.be.eql(badges.address);\n      expect(attestationsRegistryAddress).to.be.eql(attestationsRegistry.address);\n      expect(frontAddress).to.be.eql(front.address);\n      expect(hydraS1AccountboundAttesterAddress).to.be.eql(hydraS1AccountboundAttester.address);\n      expect(commitmentMapperRegistryAddress).to.be.eql(commitmentMapperRegistry.address);\n      expect(availableRootsRegistryAddress).to.be.eql(availableRootsRegistry.address);\n      expect(hydraS1VerifierAddress).to.be.eql(hydraS1Verifier.address);\n    });\n\n    it('Should get the sismo contracts addresses via get (bytes)', async () => {\n      const badgesAddress = await sismoAddressesProvider['get(bytes32)'](\n        ethers.utils.keccak256(toUtf8Bytes('Badges'))\n      );\n      const attestationsRegistryAddress = await sismoAddressesProvider['get(bytes32)'](\n        ethers.utils.keccak256(toUtf8Bytes('AttestationsRegistry'))\n      );\n      const frontAddress = await sismoAddressesProvider['get(bytes32)'](\n        ethers.utils.keccak256(toUtf8Bytes('Front'))\n      );\n      const hydraS1AccountboundAttesterAddress = await sismoAddressesProvider['get(bytes32)'](\n        ethers.utils.keccak256(toUtf8Bytes('HydraS1AccountboundAttester'))\n      );\n      const commitmentMapperRegistryAddress = await sismoAddressesProvider['get(bytes32)'](\n        ethers.utils.keccak256(toUtf8Bytes('CommitmentMapperRegistry'))\n      );\n      const availableRootsRegistryAddress = await sismoAddressesProvider['get(bytes32)'](\n        ethers.utils.keccak256(toUtf8Bytes('AvailableRootsRegistry'))\n      );\n      const hydraS1VerifierAddress = await sismoAddressesProvider['get(bytes32)'](\n        ethers.utils.keccak256(toUtf8Bytes('HydraS1Verifier'))\n      );\n\n      expect(badgesAddress).to.be.eql(badges.address);\n      expect(attestationsRegistryAddress).to.be.eql(attestationsRegistry.address);\n      expect(frontAddress).to.be.eql(front.address);\n      expect(hydraS1AccountboundAttesterAddress).to.be.eql(hydraS1AccountboundAttester.address);\n      expect(commitmentMapperRegistryAddress).to.be.eql(commitmentMapperRegistry.address);\n      expect(availableRootsRegistryAddress).to.be.eql(availableRootsRegistry.address);\n      expect(hydraS1VerifierAddress).to.be.eql(hydraS1Verifier.address);\n    });\n\n    it('Should get the sismo contracts addresses via getAll', async () => {\n      const allContractInfos = await sismoAddressesProvider.getAll();\n      const [allNames, allNamesHash, allAddresses] = allContractInfos;\n\n      expect(allNames).to.be.eql([\n        'Badges',\n        'AttestationsRegistry',\n        'Front',\n        'HydraS1AccountboundAttester',\n        'AvailableRootsRegistry',\n        'CommitmentMapperRegistry',\n        'HydraS1Verifier',\n      ]);\n\n      expect(allNamesHash).to.be.eql([\n        ethers.utils.keccak256(toUtf8Bytes('Badges')),\n        ethers.utils.keccak256(toUtf8Bytes('AttestationsRegistry')),\n        ethers.utils.keccak256(toUtf8Bytes('Front')),\n        ethers.utils.keccak256(toUtf8Bytes('HydraS1AccountboundAttester')),\n        ethers.utils.keccak256(toUtf8Bytes('AvailableRootsRegistry')),\n        ethers.utils.keccak256(toUtf8Bytes('CommitmentMapperRegistry')),\n        ethers.utils.keccak256(toUtf8Bytes('HydraS1Verifier')),\n      ]);\n\n      expect(allAddresses).to.be.eql([\n        badges.address,\n        attestationsRegistry.address,\n        front.address,\n        hydraS1AccountboundAttester.address,\n        availableRootsRegistry.address,\n        commitmentMapperRegistry.address,\n        hydraS1Verifier.address,\n      ]);\n    });\n\n    it('Should get specific sismo contracts addresses via getBatch (string)', async () => {\n      const contractAddresses = await sismoAddressesProvider['getBatch(string[])']([\n        'Badges',\n        'AttestationsRegistry',\n      ]);\n      expect(contractAddresses).to.be.eql([badges.address, attestationsRegistry.address]);\n    });\n\n    it('Should get specific sismo contracts addresses via getBatch (bytes32)', async () => {\n      const contractAddresses = await sismoAddressesProvider['getBatch(bytes32[])']([\n        ethers.utils.keccak256(toUtf8Bytes('Badges')),\n        ethers.utils.keccak256(toUtf8Bytes('AttestationsRegistry')),\n      ]);\n      expect(contractAddresses).to.be.eql([badges.address, attestationsRegistry.address]);\n    });\n  });\n\n  describe('Setters', () => {\n    it('Should set a contract address', async () => {\n      const newBadgesAddress = '0x0000000000000000000000000000000000000001';\n      const setTx = await sismoAddressesProvider.set(newBadgesAddress, 'newBadgesContract');\n\n      await expect(setTx)\n        .emit(sismoAddressesProvider, 'ContractAddressSet')\n        .withArgs(newBadgesAddress, 'newBadgesContract');\n\n      const newBadgesContract = await sismoAddressesProvider['get(string)']('newBadgesContract');\n      expect(newBadgesContract).to.be.eql(newBadgesAddress);\n    });\n\n    it('Should revert if caller is not owner', async () => {\n      const newBadgesAddress = '0x0000000000000000000000000000000000000001';\n      await expect(\n        sismoAddressesProvider.connect(randomSigner).set(newBadgesAddress, 'newBadgesContract')\n      ).to.be.revertedWith('Ownable: caller is not the owner');\n    });\n\n    it('Should set several contract addresses via setBatch', async () => {\n      const newBadgesAddress = '0x0000000000000000000000000000000000000001';\n      const newAttestationsRegistryAddress = '0x0000000000000000000000000000000000000002';\n\n      const setBatchTx = await sismoAddressesProvider.setBatch(\n        [newBadgesAddress, newAttestationsRegistryAddress],\n        ['newBadgesContract', 'newAttestationsRegistryContract']\n      );\n\n      await expect(setBatchTx)\n        .emit(sismoAddressesProvider, 'ContractAddressSet')\n        .withArgs(newBadgesAddress, 'newBadgesContract');\n\n      await expect(setBatchTx)\n        .emit(sismoAddressesProvider, 'ContractAddressSet')\n        .withArgs(newAttestationsRegistryAddress, 'newAttestationsRegistryContract');\n\n      const newBadgesContract = await sismoAddressesProvider['get(string)']('newBadgesContract');\n      expect(newBadgesContract).to.be.eql(newBadgesAddress);\n\n      const newAttestationsRegistryContract = await sismoAddressesProvider['get(string)'](\n        'newAttestationsRegistryContract'\n      );\n      expect(newAttestationsRegistryContract).to.be.eql(newAttestationsRegistryAddress);\n    });\n  });\n\n  /*************************************************************************************/\n  /******************************* UPDATE IMPLEMENTATION *******************************/\n  /*************************************************************************************/\n  describe('Update implementation', () => {\n    it('Should update the implementation', async () => {\n      const proxyAdminSigner = await ethers.getSigner(\n        deploymentsConfig[hre.network.name].deployOptions.proxyAdmin as string\n      );\n\n      const CONTRACT_NAME = 'AddressesProvider';\n\n      const deploymentArgs = [\n        badges.address,\n        attestationsRegistry.address,\n        front.address,\n        hydraS1AccountboundAttester.address,\n        availableRootsRegistry.address,\n        commitmentMapperRegistry.address,\n        hydraS1Verifier.address,\n        deployer.address,\n      ];\n\n      const newSismoAddressesProvider = await hre.deployments.deploy(CONTRACT_NAME, {\n        contract: 'contracts/core/utils/AddressesProvider.sol:AddressesProvider',\n        from: deployer.address,\n        deterministicDeployment: false,\n        // note proxyData is the encoded call (i.e initialize(params))\n        args: deploymentArgs,\n        log: true,\n      });\n\n      // Upgrade the proxy to use the deployed implementation\n      const adressesProviderProxy = TransparentUpgradeableProxy__factory.connect(\n        sismoAddressesProvider.address,\n        proxyAdminSigner\n      );\n\n      const upgradeToTx = await adressesProviderProxy.upgradeTo(newSismoAddressesProvider.address);\n      await upgradeToTx.wait();\n\n      const implementationAddress = await getImplementation(adressesProviderProxy);\n      expect(implementationAddress).to.eql(newSismoAddressesProvider.address);\n    });\n  });\n});\n"
  },
  {
    "path": "test/unit/libs/using-sismo/using-sismo.test.ts",
    "content": "import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers';\nimport { EddsaPublicKey, HydraS1Account, KVMerkleTree } from '@sismo-core/hydra-s1';\nimport { expect } from 'chai';\nimport { BigNumber } from 'ethers';\nimport hre, { ethers } from 'hardhat';\nimport {\n  evmRevert,\n  evmSnapshot,\n  generateProvingData,\n  HydraS1SimpleGroup,\n  HydraS1ZKPS,\n} from '../../../utils';\nimport {\n  AvailableRootsRegistry,\n  HydraS1AccountboundAttester,\n  MockContractUsingSismoLib,\n} from 'types';\nimport {\n  checkAccountHoldsBadge,\n  computeBadgeIds,\n  deployCoreContracts,\n  mintBadge,\n  registerRootForAttester,\n} from '../../../utils/test-helpers';\n\ndescribe('Test UsingSismo Lib', async () => {\n  let mockContractUsingSismoLib: MockContractUsingSismoLib;\n  let hydraS1AccountboundAttester: HydraS1AccountboundAttester;\n  let availableRootsRegistry: AvailableRootsRegistry;\n\n  let accountsSigners: SignerWithAddress[];\n  let deployer: SignerWithAddress;\n  let account1Signer: SignerWithAddress;\n  let account2Signer: SignerWithAddress;\n  let account3Signer: SignerWithAddress;\n\n  let accounts: HydraS1Account[];\n  let account1: HydraS1Account;\n  let account2: HydraS1Account;\n  let account3: HydraS1Account;\n\n  let provingScheme: HydraS1ZKPS;\n\n  let accountsTreesWithData: { tree: KVMerkleTree; group: HydraS1SimpleGroup }[];\n  let registryTree: KVMerkleTree;\n  let groups: HydraS1SimpleGroup[];\n  let commitmentMapperPubKey: EddsaPublicKey;\n\n  let badgeId: BigNumber;\n  let badgeId2: BigNumber;\n\n  let zeroAddress: string;\n  let cooldownDuration: number;\n  let evmSnapshotId: string;\n  let resetStateSnapshotId: string;\n  let chainId: number;\n\n  let checkBalanceIncrease: (account: SignerWithAddress, balanceBefore: BigNumber) => Promise<void>;\n  let testBalanceIncrease: (\n    accountSigner: SignerWithAddress,\n    functionName: string,\n    args?: any\n  ) => Promise<void>;\n\n  before(async () => {\n    chainId = parseInt(await hre.getChainId());\n\n    ({ accountsTreesWithData, registryTree, groups, accounts, commitmentMapperPubKey } =\n      await generateProvingData({\n        nbOfGroups: 2,\n      }));\n\n    cooldownDuration = 60 * 60 * 24;\n\n    // data to reuse everywhere\n\n    accountsSigners = await ethers.getSigners();\n\n    deployer = accountsSigners[0];\n    account1Signer = accountsSigners[0]; // deployer and account1Signer are the same\n    account2Signer = accountsSigners[1];\n    account3Signer = accountsSigners[2];\n\n    account1 = accounts[0]; // has a value of 1 in groups\n    account2 = accounts[1]; // has a value of 2 in groups\n    account3 = accounts[2]; // has a value of 3 in groups\n\n    zeroAddress = '0x0000000000000000000000000000000000000000';\n  });\n\n  describe('Deployments', async () => {\n    it('Should deploy, setup core and mock', async () => {\n      ({ hydraS1AccountboundAttester, availableRootsRegistry } = await deployCoreContracts(\n        deployer,\n        {\n          deploymentNamePrefix: 'mock-contract-using-sismo-lib',\n        }\n      ));\n\n      // deploy mock contract\n      ({ mockContractUsingSismoLib } = await hre.run('deploy-mock-contract-using-sismo-lib', {\n        options: {\n          deploymentNamePrefix: 'mock-contract-using-sismo-lib',\n          behindProxy: false,\n        },\n      }));\n\n      await registerRootForAttester(\n        availableRootsRegistry,\n        hydraS1AccountboundAttester,\n        registryTree\n      );\n\n      await hydraS1AccountboundAttester.setCooldownDurationForGroupIndex(\n        groups[0].properties.groupIndex,\n        cooldownDuration\n      );\n\n      // set up the proving scheme\n      provingScheme = new HydraS1ZKPS({\n        accountsTreesWithData,\n        registryTree,\n        groups,\n        defaultAttester: hydraS1AccountboundAttester,\n        commitmentMapperPubKey,\n        chainId,\n      });\n\n      [badgeId, badgeId2] = await computeBadgeIds(provingScheme);\n\n      // helpers function\n\n      checkBalanceIncrease = async (account: SignerWithAddress, balanceBefore: BigNumber) => {\n        expect(await mockContractUsingSismoLib.balances(account.address)).to.be.eql(\n          balanceBefore.add(1)\n        );\n      };\n\n      testBalanceIncrease = async (\n        accountSigner: SignerWithAddress,\n        functionName: string,\n        args?: any\n      ) => {\n        const balanceBefore = await mockContractUsingSismoLib.balances(accountSigner.address);\n        args\n          ? await mockContractUsingSismoLib.connect(accountSigner)[functionName](...args)\n          : await mockContractUsingSismoLib.connect(accountSigner)[functionName]();\n        await checkBalanceIncrease(accountSigner, balanceBefore);\n      };\n    });\n\n    it('Should check gated badges ID in mock contract', async () => {\n      expect(await mockContractUsingSismoLib.FIRST_GATED_BADGE_ID()).to.be.eql(badgeId);\n\n      expect(await mockContractUsingSismoLib.SECOND_GATED_BADGE_ID()).to.be.eql(badgeId2);\n    });\n  });\n\n  describe('Mint Badge', async () => {\n    it('Should mint a badge (with attester address in args)', async () => {\n      let evmSnapshotId = await evmSnapshot(hre);\n      await checkAccountHoldsBadge(account2Signer.address, badgeId, false);\n      const { request, proofData } = await provingScheme.generateProof({\n        sources: [account1],\n        destination: account2,\n      });\n\n      await mockContractUsingSismoLib.testMintSismoBadgeWithAttester(\n        request,\n        proofData,\n        hydraS1AccountboundAttester.address\n      );\n\n      await checkAccountHoldsBadge(account2Signer.address, badgeId);\n      await evmRevert(hre, evmSnapshotId);\n    });\n\n    it('Should mint a badge (with default attester address)', async () => {\n      let evmSnapshotId = await evmSnapshot(hre);\n      await checkAccountHoldsBadge(account2Signer.address, badgeId, false);\n      const { request, proofData } = await provingScheme.generateProof({\n        sources: [account1],\n        destination: account2,\n      });\n\n      await mockContractUsingSismoLib.testMintSismoBadge(request, proofData);\n\n      await checkAccountHoldsBadge(account2Signer.address, badgeId);\n      await evmRevert(hre, evmSnapshotId);\n    });\n\n    it('Should mint badges (with attester address in args)', async () => {\n      // TODO: test with a future attester issuing multiple attestations\n      let evmSnapshotId = await evmSnapshot(hre);\n      await checkAccountHoldsBadge(account2Signer.address, badgeId, false);\n      const { request, proofData } = await provingScheme.generateProof({\n        sources: [account1],\n        destination: account2,\n      });\n\n      await mockContractUsingSismoLib.testMintSismoBadgesWithAttester(\n        request,\n        proofData,\n        hydraS1AccountboundAttester.address\n      );\n\n      await checkAccountHoldsBadge(account2Signer.address, badgeId);\n      await evmRevert(hre, evmSnapshotId);\n    });\n\n    it('Should mint badges (with default attester address)', async () => {\n      // TODO: test with a future attester issuing multiple attestations\n      let evmSnapshotId = await evmSnapshot(hre);\n      await checkAccountHoldsBadge(account2Signer.address, badgeId, false);\n      const { request, proofData } = await provingScheme.generateProof({\n        sources: [account1],\n        destination: account2,\n      });\n\n      await mockContractUsingSismoLib.testMintSismoBadges(request, proofData);\n\n      await checkAccountHoldsBadge(account2Signer.address, badgeId);\n      await evmRevert(hre, evmSnapshotId);\n    });\n  });\n\n  describe('Modifiers', async () => {\n    describe('onlyBadgeHoldersModifier', async () => {\n      it('should revert if the user has no badge', async () => {\n        // 0x2 should not hold badge yet\n        await checkAccountHoldsBadge(account2Signer.address, badgeId, false);\n        await expect(\n          mockContractUsingSismoLib.connect(account2Signer).testOnlyBadgeHoldersModifier()\n        ).to.be.revertedWith('UserDoesNotMeetRequirements()');\n      });\n\n      it('should pass if user has badge', async () => {\n        evmSnapshotId = await evmSnapshot(hre);\n        await mintBadge({\n          sources: [account1],\n          destination: account2,\n          provingScheme,\n        });\n\n        await testBalanceIncrease(account2Signer, 'testOnlyBadgeHoldersModifier');\n\n        await evmRevert(hre, evmSnapshotId);\n      });\n    });\n\n    describe('onlyBadgesHoldersWithAllBadgesRequiredModifier', async () => {\n      it('should revert if the user has no badge', async () => {\n        // 0x2 should not hold badge yet\n        await checkAccountHoldsBadge(account2Signer.address, badgeId, false);\n        await expect(\n          mockContractUsingSismoLib\n            .connect(account2Signer)\n            .testOnlyBadgesHoldersWithAllBadgesRequiredModifier([badgeId, badgeId2])\n        ).to.be.revertedWith(`UserDoesNotMeetAllRequirements(${badgeId}, 1)`);\n      });\n\n      it('should revert if the user has only one badge', async () => {\n        evmSnapshotId = await evmSnapshot(hre);\n        await mintBadge({\n          sources: [account1],\n          destination: account2,\n          provingScheme,\n        });\n\n        await expect(\n          mockContractUsingSismoLib\n            .connect(account2Signer)\n            .testOnlyBadgesHoldersWithAllBadgesRequiredModifier([badgeId, badgeId2])\n        ).to.be.revertedWith(`UserDoesNotMeetAllRequirements(${badgeId2}, 1)`);\n      });\n\n      it('should pass if user has all badges', async () => {\n        // 0x2 should only hold badge with badgeId\n        await checkAccountHoldsBadge(account2Signer.address, badgeId);\n        await mintBadge({\n          sources: [account1],\n          destination: account2,\n          group: groups[1], // we mint badge with badgeId2\n          value: BigNumber.from(2), // claimed value for badge with badgeId2\n          provingScheme,\n        });\n\n        await testBalanceIncrease(\n          account2Signer,\n          'testOnlyBadgesHoldersWithAllBadgesRequiredModifier',\n          [[badgeId, badgeId2]]\n        );\n\n        await evmRevert(hre, evmSnapshotId);\n      });\n    });\n\n    describe('onlyBadgesHoldersWithOnlyOneBadgeRequiredModifier', async () => {\n      it('should revert if the user has no badge', async () => {\n        // 0x2 should not hold badge yet\n        await checkAccountHoldsBadge(account2Signer.address, badgeId, false);\n        await checkAccountHoldsBadge(account2Signer.address, badgeId2, false);\n        await expect(\n          mockContractUsingSismoLib\n            .connect(account2Signer)\n            .testOnlyBadgesHoldersWithOnlyOneBadgeRequiredModifier([badgeId, badgeId2])\n        ).to.be.revertedWith('UserDoesNotMeetRequirements()');\n      });\n\n      it('should pass if user has one badge (we test with the second badge)', async () => {\n        let evmSnapshotId = await evmSnapshot(hre);\n        await mintBadge({\n          sources: [account1],\n          destination: account2,\n          group: groups[1], // we mint badge with badgeId2\n          value: BigNumber.from(2), // claimed value for badge with badgeId2\n          provingScheme,\n        });\n\n        await testBalanceIncrease(\n          account2Signer,\n          'testOnlyBadgesHoldersWithOnlyOneBadgeRequiredModifier',\n          [[badgeId, badgeId2]]\n        );\n\n        await evmRevert(hre, evmSnapshotId);\n      });\n    });\n\n    describe('onlyBadgeHoldersWithGreaterBalanceModifier', async () => {\n      it('should revert if the user has no badge', async () => {\n        // 0x2 should not hold badge yet\n        await checkAccountHoldsBadge(account2Signer.address, badgeId, false);\n        await expect(\n          mockContractUsingSismoLib\n            .connect(account2Signer)\n            .testOnlyBadgeHoldersWithGreaterBalanceModifier()\n        ).to.be.revertedWith('UserDoesNotMeetRequirements()');\n      });\n\n      it('should revert if the user has badge but balance is not greater', async () => {\n        evmSnapshotId = await evmSnapshot(hre);\n        await mintBadge({\n          sources: [account1],\n          destination: account2,\n          provingScheme,\n        });\n\n        await expect(\n          mockContractUsingSismoLib\n            .connect(account2Signer)\n            .testOnlyBadgeHoldersWithGreaterBalanceModifier()\n        ).to.be.revertedWith('UserDoesNotMeetRequirements()');\n        await evmRevert(hre, evmSnapshotId);\n      });\n\n      it('should pass if user has badge and balance is greater', async () => {\n        evmSnapshotId = await evmSnapshot(hre);\n        await mintBadge({\n          sources: [account2], // we mint badge with account2 as source to have balance of 2\n          destination: account3,\n          provingScheme,\n        });\n\n        await testBalanceIncrease(account3Signer, 'testOnlyBadgeHoldersWithGreaterBalanceModifier');\n\n        await evmRevert(hre, evmSnapshotId);\n      });\n    });\n\n    describe('onlyBadgeHoldersWithLowerBalanceModifier', async () => {\n      it('should revert if the user has no badge', async () => {\n        // 0x2 should not hold badge yet\n        await checkAccountHoldsBadge(account2Signer.address, badgeId, false);\n        await expect(\n          mockContractUsingSismoLib\n            .connect(account2Signer)\n            .testOnlyBadgeHoldersWithLowerBalanceModifier()\n        ).to.be.revertedWith('UserDoesNotMeetRequirements()');\n      });\n\n      it('should revert if the user has badge but balance is not lower', async () => {\n        evmSnapshotId = await evmSnapshot(hre);\n        await mintBadge({\n          sources: [account3], // we mint badge with account3 as source to have balance of 3\n          destination: account1,\n          provingScheme,\n        });\n\n        await expect(\n          mockContractUsingSismoLib\n            .connect(account1Signer)\n            .testOnlyBadgeHoldersWithLowerBalanceModifier()\n        ).to.be.revertedWith('UserDoesNotMeetRequirements()');\n        await evmRevert(hre, evmSnapshotId);\n      });\n\n      it('should pass if user has badge and balance is lower', async () => {\n        evmSnapshotId = await evmSnapshot(hre);\n        await mintBadge({\n          sources: [account2],\n          destination: account1,\n          provingScheme,\n        });\n\n        await testBalanceIncrease(account1Signer, 'testOnlyBadgeHoldersWithLowerBalanceModifier');\n\n        await evmRevert(hre, evmSnapshotId);\n      });\n    });\n\n    describe('onlyBadgeHoldersWithExactBalanceModifier', async () => {\n      it('should revert if the user has no badge', async () => {\n        // 0x2 should not hold badge yet\n        await checkAccountHoldsBadge(account2Signer.address, badgeId, false);\n        await expect(\n          mockContractUsingSismoLib\n            .connect(account2Signer)\n            .testOnlyBadgeHoldersWithExactBalanceModifier()\n        ).to.be.revertedWith('UserDoesNotMeetRequirements()');\n      });\n\n      it('should revert if the user has badge but balance is not exact', async () => {\n        evmSnapshotId = await evmSnapshot(hre);\n        await mintBadge({\n          sources: [account2], // we mint badge with account2 as source to have balance of 2\n          destination: account1,\n          provingScheme,\n        });\n\n        await expect(\n          mockContractUsingSismoLib\n            .connect(account1Signer)\n            .testOnlyBadgeHoldersWithExactBalanceModifier()\n        ).to.be.revertedWith('UserDoesNotMeetRequirements()');\n        await evmRevert(hre, evmSnapshotId);\n      });\n\n      it('should pass if user has badge and balance is exact', async () => {\n        evmSnapshotId = await evmSnapshot(hre);\n        await mintBadge({\n          sources: [account1], // we mint badge with account1 as source to have balance of 1\n          destination: account1,\n          provingScheme,\n        });\n\n        await testBalanceIncrease(account1Signer, 'testOnlyBadgeHoldersWithExactBalanceModifier');\n\n        await evmRevert(hre, evmSnapshotId);\n      });\n    });\n\n    describe('onlyBadgesHoldersWithGreaterBalanceAndAllBadgesRequiredModifier', async () => {\n      it('should revert if the user has no badge', async () => {\n        // 0x2 should not hold badge yet\n        await checkAccountHoldsBadge(account2Signer.address, badgeId, false);\n        await expect(\n          mockContractUsingSismoLib\n            .connect(account2Signer)\n            .testOnlyBadgesHoldersWithGreaterBalanceAndAllBadgesRequiredModifier(\n              [badgeId, badgeId2],\n              [2, 2]\n            )\n        ).to.be.revertedWith(`UserDoesNotMeetAllRequirements(${badgeId}, 2)`);\n      });\n\n      it('should revert if the user has only one badge with a valid balance', async () => {\n        evmSnapshotId = await evmSnapshot(hre);\n        await mintBadge({\n          sources: [account2],\n          destination: account3,\n          provingScheme,\n        });\n\n        await expect(\n          mockContractUsingSismoLib\n            .connect(account3Signer)\n            .testOnlyBadgesHoldersWithGreaterBalanceAndAllBadgesRequiredModifier(\n              [badgeId, badgeId2],\n              [2, 2]\n            )\n        ).to.be.revertedWith(`UserDoesNotMeetAllRequirements(${badgeId2}, 2)`);\n        await evmRevert(hre, evmSnapshotId);\n      });\n\n      it('should revert if the user has both badges but one balance is not greater', async () => {\n        evmSnapshotId = await evmSnapshot(hre);\n        // we mint badge with account2 as source to have balance of 2\n        // badgeId is required to have balance of 2 -> should pass\n        await mintBadge({\n          sources: [account2],\n          destination: account3,\n          provingScheme,\n        });\n\n        // badgeId2 is required to have balance of 2 -> should fail\n        await mintBadge({\n          sources: [account1],\n          destination: account3,\n          group: groups[1], // groups[1] refers to badgeId2\n          provingScheme,\n        });\n\n        await expect(\n          mockContractUsingSismoLib\n            .connect(account3Signer)\n            .testOnlyBadgesHoldersWithGreaterBalanceAndAllBadgesRequiredModifier(\n              [badgeId, badgeId2],\n              [2, 2]\n            )\n        ).to.be.revertedWith(`UserDoesNotMeetAllRequirements(${badgeId2}, 2)`);\n        await evmRevert(hre, evmSnapshotId);\n      });\n\n      it('should pass if user has both badges and both balance is greater', async () => {\n        evmSnapshotId = await evmSnapshot(hre);\n        // we mint badge with account2 as source to have balance of 2\n        // badgeId is required to have balance of 2 -> should pass\n        await mintBadge({\n          sources: [account2],\n          destination: account3,\n          provingScheme,\n        });\n\n        // badgeId2 is required to have balance of 2 -> should pass\n        await mintBadge({\n          sources: [account2],\n          destination: account3,\n          group: groups[1], // groups[1] refers to badgeId2\n          provingScheme,\n        });\n\n        await testBalanceIncrease(\n          account3Signer,\n          'testOnlyBadgesHoldersWithGreaterBalanceAndAllBadgesRequiredModifier',\n          [\n            [badgeId, badgeId2],\n            [2, 2],\n          ]\n        );\n\n        await evmRevert(hre, evmSnapshotId);\n      });\n    });\n\n    describe('onlyBadgesHoldersWithGreaterBalanceAndOnlyOneBadgeRequiredModifier', async () => {\n      it('should revert if the user has no badge', async () => {\n        // 0x2 should not hold badge yet\n        await checkAccountHoldsBadge(account2Signer.address, badgeId, false);\n        await expect(\n          mockContractUsingSismoLib\n            .connect(account2Signer)\n            .testOnlyBadgesHoldersWithGreaterBalanceAndOnlyOneBadgeRequiredModifier(\n              [badgeId, badgeId2],\n              [2, 2]\n            )\n        ).to.be.revertedWith('UserDoesNotMeetRequirements()');\n      });\n\n      it('should revert if the user has only one badge and the balance is not greater', async () => {\n        evmSnapshotId = await evmSnapshot(hre);\n        // we mint badge with account1 as source to have balance of 1\n        // badgeId is required to have balance of 2 -> should fail\n        await mintBadge({\n          sources: [account1],\n          destination: account3,\n          provingScheme,\n        });\n\n        await expect(\n          mockContractUsingSismoLib\n            .connect(account3Signer)\n            .testOnlyBadgesHoldersWithGreaterBalanceAndOnlyOneBadgeRequiredModifier(\n              [badgeId, badgeId2],\n              [2, 2]\n            )\n        ).to.be.revertedWith(`UserDoesNotMeetRequirements()`);\n        await evmRevert(hre, evmSnapshotId);\n      });\n\n      it('should revert if the user has both badges but balances are not greater', async () => {\n        evmSnapshotId = await evmSnapshot(hre);\n        // we mint badge with account1 as source to have balance of 1\n        // badgeId is required to have balance of 2 -> should fail\n        await mintBadge({\n          sources: [account1],\n          destination: account3,\n          provingScheme,\n        });\n\n        // badgeId2 is required to have balance of 2 -> should fail\n        await mintBadge({\n          sources: [account1],\n          destination: account3,\n          group: groups[1], // groups[1] refers to badgeId2\n          provingScheme,\n        });\n\n        await expect(\n          mockContractUsingSismoLib\n            .connect(account3Signer)\n            .testOnlyBadgesHoldersWithGreaterBalanceAndOnlyOneBadgeRequiredModifier(\n              [badgeId, badgeId2],\n              [2, 2]\n            )\n        ).to.be.revertedWith('UserDoesNotMeetRequirements()');\n        await evmRevert(hre, evmSnapshotId);\n      });\n\n      it('should pass if user has one badge and the balance is greater', async () => {\n        evmSnapshotId = await evmSnapshot(hre);\n        // we mint badge with account2 as source to have balance of 2\n        // badgeId is required to have balance of 2 -> should pass\n        await mintBadge({\n          sources: [account2],\n          destination: account3,\n          provingScheme,\n        });\n\n        await testBalanceIncrease(\n          account3Signer,\n          'testOnlyBadgesHoldersWithGreaterBalanceAndOnlyOneBadgeRequiredModifier',\n          [\n            [badgeId, badgeId2],\n            [2, 2],\n          ]\n        );\n\n        await evmRevert(hre, evmSnapshotId);\n      });\n    });\n\n    describe('onlyBadgesHoldersWithLowerBalanceAndAllBadgesRequiredModifier', async () => {\n      it('should revert if the user has no badge', async () => {\n        // 0x2 should not hold badge yet\n        await checkAccountHoldsBadge(account2Signer.address, badgeId, false);\n        await expect(\n          mockContractUsingSismoLib\n            .connect(account2Signer)\n            .testOnlyBadgesHoldersWithLowerBalanceAndAllBadgesRequiredModifier(\n              [badgeId, badgeId2],\n              [2, 2]\n            )\n        ).to.be.revertedWith(`UserDoesNotMeetAllRequirements(${badgeId}, 2)`);\n      });\n\n      it('should revert if the user has only one badge and the balance is not lower', async () => {\n        evmSnapshotId = await evmSnapshot(hre);\n        // we mint badge with account3 as source to have balance of 3\n        // badgeId is required to have balance of 2 -> should fail\n        await mintBadge({\n          sources: [account3],\n          destination: account3,\n          provingScheme,\n        });\n\n        await expect(\n          mockContractUsingSismoLib\n            .connect(account3Signer)\n            .testOnlyBadgesHoldersWithLowerBalanceAndAllBadgesRequiredModifier(\n              [badgeId, badgeId2],\n              [2, 2]\n            )\n        ).to.be.revertedWith(`UserDoesNotMeetAllRequirements(${badgeId}, 2)`);\n        await evmRevert(hre, evmSnapshotId);\n      });\n\n      it('should revert if the user has only one badge and the balance is valid', async () => {\n        evmSnapshotId = await evmSnapshot(hre);\n        // we mint badge with account1 as source to have balance of 1\n        await mintBadge({\n          sources: [account1],\n          destination: account3,\n          provingScheme,\n        });\n\n        await expect(\n          mockContractUsingSismoLib\n            .connect(account3Signer)\n            .testOnlyBadgesHoldersWithLowerBalanceAndAllBadgesRequiredModifier(\n              [badgeId, badgeId2],\n              [2, 2]\n            )\n        ).to.be.revertedWith(`UserDoesNotMeetAllRequirements(${badgeId2}, 2)`);\n        await evmRevert(hre, evmSnapshotId);\n      });\n\n      it('should revert if the user has both badges but one balance is not lower', async () => {\n        evmSnapshotId = await evmSnapshot(hre);\n        // we mint badge with account1 as source to have balance of 1\n        // badgeId is required to have balance of 2 -> should pass\n        await mintBadge({\n          sources: [account1],\n          destination: account3,\n          provingScheme,\n        });\n\n        // badgeId2 is required to have balance of 2 -> should fail\n        await mintBadge({\n          sources: [account3],\n          destination: account3,\n          group: groups[1], // groups[1] refers to badgeId2\n          provingScheme,\n        });\n\n        await expect(\n          mockContractUsingSismoLib\n            .connect(account3Signer)\n            .testOnlyBadgesHoldersWithLowerBalanceAndAllBadgesRequiredModifier(\n              [badgeId, badgeId2],\n              [2, 2]\n            )\n        ).to.be.revertedWith(`UserDoesNotMeetAllRequirements(${badgeId2}, 2)`);\n        await evmRevert(hre, evmSnapshotId);\n      });\n\n      it('should pass if user has both badges and the balances are lower', async () => {\n        evmSnapshotId = await evmSnapshot(hre);\n        // we mint badge with account1 as source to have balance of 1\n        // badgeId is required to have balance of 2 -> should pass\n        await mintBadge({\n          sources: [account1],\n          destination: account3,\n          provingScheme,\n        });\n\n        // badgeId2 is required to have balance of 2 -> should pass\n        await mintBadge({\n          sources: [account1],\n          destination: account3,\n          group: groups[1], // groups[1] refers to badgeId2\n          provingScheme,\n        });\n\n        await testBalanceIncrease(\n          account3Signer,\n          'testOnlyBadgesHoldersWithLowerBalanceAndAllBadgesRequiredModifier',\n          [\n            [badgeId, badgeId2],\n            [2, 2],\n          ]\n        );\n        await evmRevert(hre, evmSnapshotId);\n      });\n    });\n\n    describe('onlyBadgesHoldersWithLowerBalanceAndOnlyOneBadgeRequiredModifier', async () => {\n      it('should revert if the user has no badge', async () => {\n        // 0x2 should not hold badge yet\n        await checkAccountHoldsBadge(account2Signer.address, badgeId, false);\n        await expect(\n          mockContractUsingSismoLib\n            .connect(account2Signer)\n            .testOnlyBadgesHoldersWithLowerBalanceAndOnlyOneBadgeRequiredModifier(\n              [badgeId, badgeId2],\n              [2, 2]\n            )\n        ).to.be.revertedWith('UserDoesNotMeetRequirements()');\n      });\n\n      it('should revert if the user has only one badge and the balance is not lower', async () => {\n        evmSnapshotId = await evmSnapshot(hre);\n        // we mint badge with account3 as source to have balance of 3\n        // badgeId is required to have balance of 2 -> should fail\n        await mintBadge({\n          destination: account3,\n          provingScheme,\n        });\n\n        await expect(\n          mockContractUsingSismoLib\n            .connect(account3Signer)\n            .testOnlyBadgesHoldersWithLowerBalanceAndOnlyOneBadgeRequiredModifier(\n              [badgeId, badgeId2],\n              [2, 2]\n            )\n        ).to.be.revertedWith('UserDoesNotMeetRequirements()');\n        await evmRevert(hre, evmSnapshotId);\n      });\n\n      it('should revert if the user has both badges and both balances are not lower', async () => {\n        evmSnapshotId = await evmSnapshot(hre);\n        // we mint badge with account3 as source to have balance of 3\n        // badgeId is required to have balance of 2 -> should fail\n        await mintBadge({\n          destination: account3,\n          provingScheme,\n        });\n\n        // badgeId2 is required to have balance of 2 -> should fail\n        await mintBadge({\n          destination: account3,\n          group: groups[1], // groups[1] refers to badgeId2\n          provingScheme,\n        });\n\n        await expect(\n          mockContractUsingSismoLib\n            .connect(account3Signer)\n            .testOnlyBadgesHoldersWithLowerBalanceAndOnlyOneBadgeRequiredModifier(\n              [badgeId, badgeId2],\n              [2, 2]\n            )\n        ).to.be.revertedWith('UserDoesNotMeetRequirements()');\n        await evmRevert(hre, evmSnapshotId);\n      });\n\n      it('should pass if the user has only one badge and the balance is valid', async () => {\n        evmSnapshotId = await evmSnapshot(hre);\n        // we mint badge with account1 as source to have balance of 1\n        await mintBadge({\n          sources: [account1],\n          destination: account3,\n          provingScheme,\n        });\n\n        await testBalanceIncrease(\n          account3Signer,\n          'testOnlyBadgesHoldersWithLowerBalanceAndOnlyOneBadgeRequiredModifier',\n          [\n            [badgeId, badgeId2],\n            [2, 2],\n          ]\n        );\n        await evmRevert(hre, evmSnapshotId);\n      });\n    });\n\n    describe('onlyBadgesHoldersWithExactBalanceAndAllBadgesRequiredModifier', async () => {\n      it('should revert if the user has no badge', async () => {\n        // 0x2 should not hold badge yet\n        await checkAccountHoldsBadge(account2Signer.address, badgeId, false);\n        await expect(\n          mockContractUsingSismoLib\n            .connect(account2Signer)\n            .testOnlyBadgesHoldersWithExactBalanceAndAllBadgesRequiredModifier(\n              [badgeId, badgeId2],\n              [2, 2]\n            )\n        ).to.be.revertedWith(`UserDoesNotMeetAllRequirements(${badgeId}, 2)`);\n      });\n\n      it('should revert if the user has only one badge and the balance is not exact', async () => {\n        evmSnapshotId = await evmSnapshot(hre);\n        // we mint badge with account3 as source to have balance of 3\n        // badgeId is required to have balance of 2 -> should fail\n        await mintBadge({\n          destination: account3,\n          provingScheme,\n        });\n\n        await expect(\n          mockContractUsingSismoLib\n            .connect(account3Signer)\n            .testOnlyBadgesHoldersWithExactBalanceAndAllBadgesRequiredModifier(\n              [badgeId, badgeId2],\n              [2, 2]\n            )\n        ).to.be.revertedWith(`UserDoesNotMeetAllRequirements(${badgeId}, 2)`);\n        await evmRevert(hre, evmSnapshotId);\n      });\n\n      it('should revert if the user has both badges and both balances are not exact', async () => {\n        evmSnapshotId = await evmSnapshot(hre);\n        // we mint badge with account3 as source to have balance of 3\n        // badgeId is required to have balance of 2 -> should fail\n        await mintBadge({\n          destination: account3,\n          provingScheme,\n        });\n\n        // badgeId2 is required to have balance of 2 -> should fail\n        await mintBadge({\n          destination: account3,\n          group: groups[1], // groups[1] refers to badgeId2\n          provingScheme,\n        });\n\n        await expect(\n          mockContractUsingSismoLib\n            .connect(account3Signer)\n            .testOnlyBadgesHoldersWithExactBalanceAndAllBadgesRequiredModifier(\n              [badgeId, badgeId2],\n              [2, 2]\n            )\n        ).to.be.revertedWith(`UserDoesNotMeetAllRequirements(${badgeId}, 2)`);\n        await evmRevert(hre, evmSnapshotId);\n      });\n\n      it('should revert if the user has both badges and one balance is not exact', async () => {\n        evmSnapshotId = await evmSnapshot(hre);\n        // we mint badge with account3 as source to have balance of 3\n        // badgeId is required to have balance of 2 -> should fail\n        await mintBadge({\n          destination: account3,\n          provingScheme,\n        });\n\n        // badgeId2 is required to have balance of 2 -> should fail\n        await mintBadge({\n          destination: account3,\n          group: groups[1], // groups[1] refers to badgeId2\n          provingScheme,\n        });\n\n        await expect(\n          mockContractUsingSismoLib\n            .connect(account3Signer)\n            .testOnlyBadgesHoldersWithExactBalanceAndAllBadgesRequiredModifier(\n              [badgeId, badgeId2],\n              [2, 2]\n            )\n        ).to.be.revertedWith(`UserDoesNotMeetAllRequirements(${badgeId}, 2)`);\n        await evmRevert(hre, evmSnapshotId);\n      });\n\n      it('should pass if the user has both badges and both balances are exact', async () => {\n        evmSnapshotId = await evmSnapshot(hre);\n        // we mint badge with account2 as source to have balance of 2\n        // badgeId is required to have balance of 2 -> should pass\n        await mintBadge({\n          destination: account2,\n          provingScheme,\n        });\n\n        // badgeId2 is required to have balance of 2 -> should pass\n        await mintBadge({\n          destination: account2,\n          group: groups[1], // groups[1] refers to badgeId2\n          provingScheme,\n        });\n\n        await testBalanceIncrease(\n          account2Signer,\n          'testOnlyBadgesHoldersWithExactBalanceAndAllBadgesRequiredModifier',\n          [\n            [badgeId, badgeId2],\n            [2, 2],\n          ]\n        );\n        await evmRevert(hre, evmSnapshotId);\n      });\n    });\n\n    describe('onlyBadgesHoldersWithExactBalanceAndOnlyOneBadgeRequiredModifier', async () => {\n      it('should revert if the user has no badge', async () => {\n        // 0x2 should not hold badge yet\n        await checkAccountHoldsBadge(account2Signer.address, badgeId, false);\n        await expect(\n          mockContractUsingSismoLib\n            .connect(account2Signer)\n            .testOnlyBadgesHoldersWithExactBalanceAndOnlyOneBadgeRequiredModifier(\n              [badgeId, badgeId2],\n              [2, 2]\n            )\n        ).to.be.revertedWith('UserDoesNotMeetRequirements()');\n      });\n\n      it('should revert if the user has only one badge and the balance is not exact', async () => {\n        evmSnapshotId = await evmSnapshot(hre);\n        // we mint badge with account1 as source to have balance of 1\n        // badgeId is required to have balance of 2 -> should fail\n        await mintBadge({\n          destination: account1,\n          provingScheme,\n        });\n\n        await expect(\n          mockContractUsingSismoLib\n            .connect(account3Signer)\n            .testOnlyBadgesHoldersWithExactBalanceAndOnlyOneBadgeRequiredModifier(\n              [badgeId, badgeId2],\n              [2, 2]\n            )\n        ).to.be.revertedWith('UserDoesNotMeetRequirements()');\n        await evmRevert(hre, evmSnapshotId);\n      });\n\n      it('should pass if the user has only one badge and the balance is exact', async () => {\n        evmSnapshotId = await evmSnapshot(hre);\n        // we mint badge with account2 as source to have balance of 2\n        // badgeId is required to have balance of 2 -> should pass\n        await mintBadge({\n          destination: account2,\n          group: groups[1], // groups[1] refers to badgeId2\n          provingScheme,\n        });\n\n        await testBalanceIncrease(\n          account2Signer,\n          'testOnlyBadgesHoldersWithExactBalanceAndOnlyOneBadgeRequiredModifier',\n          [\n            [badgeId, badgeId2],\n            [2, 2],\n          ]\n        );\n        await evmRevert(hre, evmSnapshotId);\n      });\n    });\n  });\n});\n"
  },
  {
    "path": "test/unit/periphery/utils/available-roots-registry.test.ts",
    "content": "import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers';\nimport { expect } from 'chai';\nimport hre, { ethers } from 'hardhat';\nimport { deploymentsConfig } from '../../../../tasks/deploy-tasks/deployments-config';\nimport { DeployedAvailableRootsRegistry } from '../../../../tasks/deploy-tasks/unit/periphery/deploy-available-roots-registry.task';\nimport { AvailableRootsRegistry, TransparentUpgradeableProxy__factory } from '../../../../types';\nimport { getImplementation } from '../../../../utils';\n\ndescribe('Test AvailableRootsRegistry contract', () => {\n  let deployer: SignerWithAddress;\n  let secondDeployer: SignerWithAddress;\n  let attacker: SignerWithAddress;\n  let attester1: SignerWithAddress;\n  let attester2: SignerWithAddress;\n\n  let availableRootsRegistry: AvailableRootsRegistry;\n  let secondAvailableRootsRegistry: AvailableRootsRegistry;\n\n  before(async () => {\n    const signers = await hre.ethers.getSigners();\n    [deployer, secondDeployer, attacker, attester1, attester2] = signers;\n  });\n\n  /*************************************************************************************/\n  /********************************** DEPLOYMENTS **************************************/\n  /*************************************************************************************/\n  describe('Deployments', () => {\n    it('Should deploy, setup and test the constructed values of the contract', async () => {\n      ({ availableRootsRegistry } = (await hre.run(\n        'deploy-available-roots-registry',\n        {}\n      )) as DeployedAvailableRootsRegistry);\n\n      ({ availableRootsRegistry: secondAvailableRootsRegistry } = (await hre.run(\n        'deploy-available-roots-registry',\n        { owner: secondDeployer.address }\n      )) as DeployedAvailableRootsRegistry);\n\n      // 0 - Checks that the owner is set to the deployer address\n      expect(await availableRootsRegistry.owner()).to.equal(deployer.address);\n      expect(await secondAvailableRootsRegistry.owner()).to.equal(secondDeployer.address);\n    });\n  });\n\n  describe('Configuration checks', () => {\n    it('Should have setup the owner correctly', async () => {\n      expect(await availableRootsRegistry.owner()).to.be.eql(deployer.address);\n    });\n\n    it('Should get the owner correctly', async () => {\n      expect(await availableRootsRegistry.owner()).to.be.eql(deployer.address);\n    });\n\n    it('Should revert when trying to call initialize again', async () => {\n      await expect(\n        availableRootsRegistry.connect(deployer).initialize(deployer.address)\n      ).to.be.revertedWith('Initializable: contract is already initialized');\n    });\n  });\n\n  /*************************************************************************************/\n  /***************************** REGISTER ROOT FOR ATTESTER ****************************/\n  /*************************************************************************************/\n  describe('Register root for attester', () => {\n    it('Should revert when the sender is not the owner of the contract', async () => {\n      await expect(\n        availableRootsRegistry.connect(attacker).registerRootForAttester(attester1.address, 1)\n      ).to.be.revertedWith('Ownable: caller is not the owner');\n    });\n\n    it('Should revert when the attester address is a zero address', async () => {\n      await expect(\n        availableRootsRegistry.registerRootForAttester(ethers.constants.AddressZero, 1)\n      ).to.be.revertedWith('CannotRegisterForZeroAddress()');\n    });\n\n    it('Should register the root for the attester', async () => {\n      const registerRootForAttesterTransaction =\n        await availableRootsRegistry.registerRootForAttester(attester1.address, 1);\n\n      // 1 - Checks that the transaction emitted the event\n      await expect(registerRootForAttesterTransaction)\n        .to.emit(availableRootsRegistry, 'RegisteredRootForAttester')\n        .withArgs(attester1.address, 1);\n\n      // 2 - Checks that the root is registered for the attester\n      expect(await availableRootsRegistry._roots(attester1.address, 1)).to.be.true;\n\n      // 3 - Checks that the root is not registered for the second attester\n      expect(await availableRootsRegistry._roots(attester2.address, 1)).to.be.false;\n    });\n  });\n\n  /*************************************************************************************/\n  /**************************** UNREGISTER ROOT FOR ATTESTER ***************************/\n  /*************************************************************************************/\n  describe('Unregister root for attester', () => {\n    it('Should revert when the sender is not the owner of the contract', async () => {\n      await expect(\n        availableRootsRegistry.connect(attacker).unregisterRootForAttester(attester1.address, 1)\n      ).to.be.revertedWith('Ownable: caller is not the owner');\n    });\n\n    it('Should revert when the attester address is a zero address', async () => {\n      await expect(\n        availableRootsRegistry.unregisterRootForAttester(ethers.constants.AddressZero, 1)\n      ).to.be.revertedWith('CannotUnregisterForZeroAddress()');\n    });\n\n    it('Should unregister the root for the attester', async () => {\n      const unregisterRootForAttesterTransaction =\n        await availableRootsRegistry.unregisterRootForAttester(attester1.address, 1);\n\n      // 1 - Checks that the transaction emitted the event\n      await expect(unregisterRootForAttesterTransaction)\n        .to.emit(availableRootsRegistry, 'UnregisteredRootForAttester')\n        .withArgs(attester1.address, 1);\n\n      // 2 - Checks that the root is unregistered for the attester\n      expect(await availableRootsRegistry._roots(attester1.address, 1)).to.be.false;\n    });\n  });\n\n  /*************************************************************************************/\n  /******************************* REGISTER ROOT FOR ALL *******************************/\n  /*************************************************************************************/\n  describe('Register root for all', () => {\n    it('Should revert when the sender is not the owner of the contract', async () => {\n      await expect(\n        availableRootsRegistry.connect(attacker).registerRootForAll(1)\n      ).to.be.revertedWith('Ownable: caller is not the owner');\n    });\n\n    it('Should register the root for the all attesters', async () => {\n      const registerRootForAllTransaction = await availableRootsRegistry.registerRootForAll(1);\n\n      // 1 - Checks that the transaction emitted the event\n      await expect(registerRootForAllTransaction)\n        .to.emit(availableRootsRegistry, 'RegisteredRootForAll')\n        .withArgs(1);\n\n      // 2 - Checks that the root is registered globally (on address 0x0)\n      expect(await availableRootsRegistry._roots(ethers.constants.AddressZero, 1)).to.be.true;\n\n      // 3 - Checks that the root is available for the first & second attesters\n      expect(await availableRootsRegistry.isRootAvailableForAttester(attester1.address, 1)).to.be\n        .true;\n      expect(await availableRootsRegistry.isRootAvailableForAttester(attester2.address, 1)).to.be\n        .true;\n    });\n  });\n\n  /*************************************************************************************/\n  /****************************** UNREGISTER ROOT FOR ALL ******************************/\n  /*************************************************************************************/\n  describe('Unregister root for all', () => {\n    it('Should revert when the sender is not the owner of the contract', async () => {\n      await expect(\n        availableRootsRegistry.connect(attacker).unregisterRootForAll(1)\n      ).to.be.revertedWith('Ownable: caller is not the owner');\n    });\n\n    it('Should unregister the root for all the attesters', async () => {\n      const unregisterRootForAllTransaction = await availableRootsRegistry.unregisterRootForAll(1);\n\n      // 1 - Checks that the transaction emitted the event\n      await expect(unregisterRootForAllTransaction)\n        .to.emit(availableRootsRegistry, 'UnregisteredRootForAll')\n        .withArgs(1);\n\n      // 2 - Checks that the root is unregistered globally (on address 0x0)\n      expect(await availableRootsRegistry._roots(ethers.constants.AddressZero, 1)).to.be.false;\n\n      // 3 - Checks that the root is not available for the first & second attesters\n      expect(await availableRootsRegistry.isRootAvailableForAttester(attester1.address, 1)).to.be\n        .false;\n      expect(await availableRootsRegistry.isRootAvailableForAttester(attester2.address, 1)).to.be\n        .false;\n    });\n  });\n\n  /*************************************************************************************/\n  /*************************** IS ROOT AVAILABLE FOR ATTESTER **************************/\n  /*************************************************************************************/\n  describe('Is root available for attester', () => {\n    describe('When the root is not available for the attester or globally', () => {\n      it('Should return false', async () => {\n        expect(await availableRootsRegistry.isRootAvailableForAttester(attester1.address, 1)).to.be\n          .false;\n      });\n    });\n\n    describe('When the root is available for the attester', () => {\n      before(async () => {\n        await availableRootsRegistry.registerRootForAttester(attester1.address, 1);\n      });\n\n      it('Should return true', async () => {\n        expect(await availableRootsRegistry.isRootAvailableForAttester(attester1.address, 1)).to.be\n          .true;\n      });\n\n      it(\"Should return false when it's the wrong attester\", async () => {\n        expect(await availableRootsRegistry.isRootAvailableForAttester(attester2.address, 1)).to.be\n          .false;\n      });\n    });\n\n    describe('When the root is available globally', () => {\n      before(async () => {\n        await availableRootsRegistry.unregisterRootForAttester(attester1.address, 1);\n\n        await availableRootsRegistry.registerRootForAll(1);\n      });\n\n      it('Should return true', async () => {\n        expect(await availableRootsRegistry.isRootAvailableForAttester(attester1.address, 1)).to.be\n          .true;\n\n        expect(await availableRootsRegistry.isRootAvailableForAttester(attester2.address, 1)).to.be\n          .true;\n      });\n    });\n  });\n\n  /*************************************************************************************/\n  /****************************** IS ROOT AVAILABLE FOR ME *****************************/\n  /*************************************************************************************/\n  describe('Is root available for me', () => {\n    before(async () => {\n      await availableRootsRegistry.unregisterRootForAttester(attester1.address, 1);\n      await availableRootsRegistry.unregisterRootForAll(1);\n    });\n\n    describe('When the root is not available for the attester or globally', async () => {\n      it('Should return false', async () => {\n        expect(await availableRootsRegistry.isRootAvailableForMe(1)).to.be.false;\n      });\n    });\n\n    describe('When the root is available for the attester', () => {\n      before(async () => {\n        await availableRootsRegistry.registerRootForAttester(attester1.address, 1);\n      });\n\n      it('Should return true', async () => {\n        expect(await availableRootsRegistry.connect(attester1).isRootAvailableForMe(1)).to.be.true;\n      });\n\n      it(\"Should return false when it's the wrong attester\", async () => {\n        expect(await availableRootsRegistry.connect(attester2).isRootAvailableForMe(1)).to.be.false;\n      });\n    });\n\n    describe('When the root is available globally', () => {\n      before(async () => {\n        await availableRootsRegistry.unregisterRootForAttester(attester1.address, 1);\n\n        await availableRootsRegistry.registerRootForAll(1);\n      });\n\n      it('Should return true', async () => {\n        expect(await availableRootsRegistry.connect(attester1).isRootAvailableForMe(1)).to.be.true;\n\n        expect(await availableRootsRegistry.connect(attester2).isRootAvailableForMe(1)).to.be.true;\n      });\n    });\n  });\n\n  /*************************************************************************************/\n  /******************************** TRANSFER OWNERSHIP *********************************/\n  /*************************************************************************************/\n  describe('Transfer ownership', () => {\n    it('Should revert when the sender is not the current owner of the contract', async () => {\n      await expect(\n        availableRootsRegistry.connect(attacker).transferOwnership(attacker.address)\n      ).to.be.revertedWith('Ownable: caller is not the owner');\n    });\n\n    it('Should revert when the newOwner is a zero address', async () => {\n      await expect(\n        availableRootsRegistry.transferOwnership(ethers.constants.AddressZero)\n      ).to.be.revertedWith('Ownable: new owner is the zero address');\n    });\n\n    it('Should transfer the ownership', async () => {\n      await expect(availableRootsRegistry.transferOwnership(secondDeployer.address))\n        .to.emit(availableRootsRegistry, 'OwnershipTransferred')\n        .withArgs(deployer.address, secondDeployer.address);\n    });\n  });\n\n  /*************************************************************************************/\n  /******************************** RENOUNCE OWNERSHIP *********************************/\n  /*************************************************************************************/\n  describe('Renounce ownership', () => {\n    before(async () => {\n      await availableRootsRegistry.connect(secondDeployer).transferOwnership(deployer.address);\n    });\n\n    it('Should revert when the sender is not the current owner of the contract', async () => {\n      await expect(availableRootsRegistry.connect(attacker).renounceOwnership()).to.be.revertedWith(\n        'Ownable: caller is not the owner'\n      );\n    });\n\n    it('Should renounce the ownership', async () => {\n      await expect(availableRootsRegistry.renounceOwnership())\n        .to.emit(availableRootsRegistry, 'OwnershipTransferred')\n        .withArgs(deployer.address, ethers.constants.AddressZero);\n    });\n  });\n\n  /*************************************************************************************/\n  /******************************* UPDATE IMPLEMENTATION *******************************/\n  /*************************************************************************************/\n  describe('Update implementation', () => {\n    it('Should update the implementation', async () => {\n      const proxyAdminSigner = await ethers.getSigner(\n        deploymentsConfig[hre.network.name].deployOptions.proxyAdmin as string\n      );\n\n      const { availableRootsRegistry: newAvailableRootsRegistry } = await hre.run(\n        'deploy-available-roots-registry',\n        { owner: secondDeployer.address, options: { behindProxy: false } }\n      );\n\n      const availableRootsRegistryProxy = TransparentUpgradeableProxy__factory.connect(\n        availableRootsRegistry.address,\n        proxyAdminSigner\n      );\n\n      await (await availableRootsRegistryProxy.upgradeTo(newAvailableRootsRegistry.address)).wait();\n\n      const implementationAddress = await getImplementation(availableRootsRegistryProxy);\n      expect(implementationAddress).to.eql(newAvailableRootsRegistry.address);\n    });\n  });\n});\n"
  },
  {
    "path": "test/unit/periphery/utils/commitment-mapper-registry.test.ts",
    "content": "import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers';\nimport { CommitmentMapperTester } from '@sismo-core/commitment-mapper-tester-js';\nimport { EddsaPublicKey } from '@sismo-core/hydra-s1';\nimport { expect } from 'chai';\nimport { BigNumber } from 'ethers';\nimport hre, { ethers } from 'hardhat';\nimport { deploymentsConfig } from '../../../../tasks/deploy-tasks/deployments-config';\nimport { DeployedCommitmentMapper } from '../../../../tasks/deploy-tasks/unit/periphery/deploy-commitment-mapper-registry.task';\nimport { CommitmentMapperRegistry, TransparentUpgradeableProxy__factory } from '../../../../types';\nimport { getImplementation } from '../../../../utils';\n\ndescribe('Test CommitmentMapperRegistry contract', () => {\n  let deployer: SignerWithAddress;\n  let secondDeployer: SignerWithAddress;\n  let attacker: SignerWithAddress;\n  let randomCommitmentMapper: SignerWithAddress;\n\n  let commitmentMapperPubKey: EddsaPublicKey;\n\n  let commitmentMapperRegistry: CommitmentMapperRegistry;\n  let secondCommitmentMapperRegistry: CommitmentMapperRegistry;\n\n  before(async () => {\n    const signers = await hre.ethers.getSigners();\n    [deployer, secondDeployer, attacker, randomCommitmentMapper] = signers;\n\n    // 1 - Init the commitment mapper\n    const commitmentMapper = await CommitmentMapperTester.generate();\n    commitmentMapperPubKey = await commitmentMapper.getPubKey();\n  });\n\n  /*************************************************************************************/\n  /********************************** DEPLOYMENTS **************************************/\n  /*************************************************************************************/\n  describe('Deployments', () => {\n    it('Should deploy, setup and test the constructed values of the contract', async () => {\n      ({ commitmentMapperRegistry } = (await hre.run('deploy-commitment-mapper-registry', {\n        commitmentMapperPubKeyX: BigNumber.from(0).toHexString(),\n        commitmentMapperPubKeyY: BigNumber.from(0).toHexString(),\n      })) as DeployedCommitmentMapper);\n\n      ({ commitmentMapperRegistry: secondCommitmentMapperRegistry } = (await hre.run(\n        'deploy-commitment-mapper-registry',\n        {\n          commitmentMapperPubKeyX: BigNumber.from(0).toHexString(),\n          commitmentMapperPubKeyY: BigNumber.from(0).toHexString(),\n          commitmentMapperAddress: randomCommitmentMapper.address,\n          owner: secondDeployer.address,\n        }\n      )) as DeployedCommitmentMapper);\n    });\n  });\n\n  describe('Configuration checks', () => {\n    it(\"should check that the contract's owner is the deployer\", async () => {\n      expect(await commitmentMapperRegistry.owner()).to.equal(deployer.address);\n      expect(await secondCommitmentMapperRegistry.owner()).to.equal(secondDeployer.address);\n    });\n\n    it('should chck that the commitment mapper address is registered on the address zero', async () => {\n      // 1 - Checks that the commitment mapper address is registered on the address zero (default value)\n      expect(await commitmentMapperRegistry.getAddress()).to.be.equal(ethers.constants.AddressZero);\n      expect(await secondCommitmentMapperRegistry.getAddress()).to.be.equal(\n        randomCommitmentMapper.address\n      );\n    });\n\n    it('should check that the eddsa public key is initialized on [0x0, 0x0]', async () => {\n      expect(await commitmentMapperRegistry.getEdDSAPubKey()).to.be.eql([\n        BigNumber.from(0),\n        BigNumber.from(0),\n      ]);\n    });\n\n    it('Should revert when trying to call initialize again', async () => {\n      await expect(\n        commitmentMapperRegistry\n          .connect(deployer)\n          .initialize(deployer.address, commitmentMapperPubKey, randomCommitmentMapper.address)\n      ).to.be.revertedWith('Initializable: contract is already initialized');\n    });\n  });\n\n  /*************************************************************************************/\n  /********************** UPDATE COMMITMENT MAPPER EDDSA PUB KEY ***********************/\n  /*************************************************************************************/\n  describe('Update commitment mapper EdDSA pub key', () => {\n    it('Should revert when the sender is not the owner of the contract', async () => {\n      await expect(\n        commitmentMapperRegistry\n          .connect(attacker)\n          .updateCommitmentMapperEdDSAPubKey(commitmentMapperPubKey)\n      ).to.be.revertedWith('Ownable: caller is not the owner');\n    });\n\n    it('Should update the EdDSA pub key', async () => {\n      const updateCommitmentMapperEdDsaPubKeyTransaction =\n        await commitmentMapperRegistry.updateCommitmentMapperEdDSAPubKey(commitmentMapperPubKey);\n\n      // 1 - Checks that the transaction emitted the event\n      await expect(updateCommitmentMapperEdDsaPubKeyTransaction)\n        .to.emit(commitmentMapperRegistry, 'UpdatedCommitmentMapperEdDSAPubKey')\n        .withArgs(commitmentMapperPubKey);\n\n      // 2 - Checks that the eddsa public key matches the one provided\n      expect(await commitmentMapperRegistry.getEdDSAPubKey()).to.eql(commitmentMapperPubKey);\n    });\n  });\n\n  /*************************************************************************************/\n  /************************* UPDATE COMMITMENT MAPPER ADDRESS **************************/\n  /*************************************************************************************/\n  describe('Update commitment mapper address', () => {\n    it('Should revert when the sender is not the owner of the contract', async () => {\n      await expect(\n        commitmentMapperRegistry\n          .connect(attacker)\n          .updateCommitmentMapperAddress(randomCommitmentMapper.address)\n      ).to.be.revertedWith('Ownable: caller is not the owner');\n    });\n\n    it('Should update the mapper address', async () => {\n      const updateCommitmentMapperAddressTransaction =\n        await commitmentMapperRegistry.updateCommitmentMapperAddress(\n          randomCommitmentMapper.address\n        );\n\n      // 1 - Checks that the transaction emitted the event\n      await expect(updateCommitmentMapperAddressTransaction)\n        .to.emit(commitmentMapperRegistry, 'UpdatedCommitmentMapperAddress')\n        .withArgs(randomCommitmentMapper.address);\n\n      // 2 - Checks that the commitment mapper address matches the random commitment mapper address\n      expect(await commitmentMapperRegistry.getAddress()).to.be.equal(\n        randomCommitmentMapper.address\n      );\n    });\n  });\n\n  /*************************************************************************************/\n  /******************************** TRANSFER OWNERSHIP *********************************/\n  /*************************************************************************************/\n  describe('Transfer ownership', () => {\n    it('Should revert when the sender is not the current owner of the contract', async () => {\n      await expect(\n        commitmentMapperRegistry.connect(attacker).transferOwnership(attacker.address)\n      ).to.be.revertedWith('Ownable: caller is not the owner');\n    });\n\n    it('Should revert when the newOwner is a zero address', async () => {\n      await expect(\n        commitmentMapperRegistry.transferOwnership(ethers.constants.AddressZero)\n      ).to.be.revertedWith('Ownable: new owner is the zero address');\n    });\n\n    it('Should transfer the ownership', async () => {\n      await expect(commitmentMapperRegistry.transferOwnership(secondDeployer.address))\n        .to.emit(commitmentMapperRegistry, 'OwnershipTransferred')\n        .withArgs(deployer.address, secondDeployer.address);\n    });\n  });\n\n  /*************************************************************************************/\n  /******************************** RENOUNCE OWNERSHIP *********************************/\n  /*************************************************************************************/\n  describe('Renounce ownership', () => {\n    before(async () => {\n      await commitmentMapperRegistry.connect(secondDeployer).transferOwnership(deployer.address);\n    });\n\n    it('Should revert when the sender is not the current owner of the contract', async () => {\n      await expect(\n        commitmentMapperRegistry.connect(attacker).renounceOwnership()\n      ).to.be.revertedWith('Ownable: caller is not the owner');\n    });\n\n    it('Should renounce the ownership', async () => {\n      await expect(commitmentMapperRegistry.renounceOwnership())\n        .to.emit(commitmentMapperRegistry, 'OwnershipTransferred')\n        .withArgs(deployer.address, ethers.constants.AddressZero);\n    });\n  });\n\n  /*************************************************************************************/\n  /******************************* UPDATE IMPLEMENTATION *******************************/\n  /*************************************************************************************/\n  describe('Update implementation', () => {\n    it('Should update the implementation', async () => {\n      const proxyAdminSigner = await ethers.getSigner(\n        deploymentsConfig[hre.network.name].deployOptions.proxyAdmin as string\n      );\n\n      const { commitmentMapperRegistry: newCommitmentMapperRegistry } = await hre.run(\n        'deploy-commitment-mapper-registry',\n        {\n          commitmentMapperPubKeyX: BigNumber.from(0).toHexString(),\n          commitmentMapperPubKeyY: BigNumber.from(0).toHexString(),\n          options: { behindProxy: false },\n        }\n      );\n\n      const commitmentMapperRegistryProxy = TransparentUpgradeableProxy__factory.connect(\n        commitmentMapperRegistry.address,\n        proxyAdminSigner\n      );\n\n      await (\n        await commitmentMapperRegistryProxy.upgradeTo(newCommitmentMapperRegistry.address)\n      ).wait();\n\n      const implementationAddress = await getImplementation(commitmentMapperRegistryProxy);\n      expect(implementationAddress).to.eql(newCommitmentMapperRegistry.address);\n    });\n  });\n});\n"
  },
  {
    "path": "test/unit/periphery/utils/frontend-lib.test.ts",
    "content": "import { expect } from 'chai';\nimport hre from 'hardhat';\n\nimport { MockHydraS1SimpleAttester__factory } from './../../../../types/factories/MockHydraS1SimpleAttester__factory';\nimport { FrontendLib } from './../../../../types/FrontendLib';\nimport { DeployedFrontendLib } from '../../../../tasks/deploy-tasks/unit/periphery/deploy-frontend-lib.task';\nimport { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers';\n\ndescribe('Test FrontendLib contract', () => {\n  let deployer: SignerWithAddress;\n  let frontendLib: FrontendLib;\n  let dest1: SignerWithAddress;\n  let nullifier1: string;\n  let dest2: SignerWithAddress;\n  let nullifier2: string;\n  let dest3: SignerWithAddress;\n  let nullifier3: string;\n  let dest4: SignerWithAddress;\n\n  before(async () => {\n    const signers = await hre.ethers.getSigners();\n    [deployer, dest1, dest2, dest3, dest4] = signers;\n    [nullifier1, nullifier2, nullifier3] = [\n      '0x1000000000000000000000000000000000000000000000000000000000000001',\n      '0x2000000000000000000000000000000000000000000000000000000000000002',\n      '0x3000000000000000000000000000000000000000000000000000000000000003',\n    ];\n  });\n\n  /*************************************************************************************/\n  /********************************** DEPLOYMENTS **************************************/\n  /*************************************************************************************/\n  describe('Deployments', () => {\n    it('Should deploy the FrontendLib and an HydraS1SimpleAttester mock for tests', async () => {\n      // MockHydraS1SimpleAttester\n      const hydraS1AttesterMock = await hre.deployments.deploy('MockHydraS1SimpleAttesterTest', {\n        contract: 'MockHydraS1SimpleAttester',\n        from: deployer.address,\n        args: [],\n      });\n      const mockHydraS1Attester = MockHydraS1SimpleAttester__factory.connect(\n        hydraS1AttesterMock.address,\n        deployer\n      );\n\n      await mockHydraS1Attester.setDestinationOfNullifier(nullifier1, dest1.address);\n      await mockHydraS1Attester.setDestinationOfNullifier(nullifier2, dest2.address);\n      await mockHydraS1Attester.setDestinationOfNullifier(nullifier3, dest3.address);\n\n      ({ frontendLib } = (await hre.run('deploy-frontend-lib', {\n        hydraS1AccountboundAttester: hydraS1AttesterMock.address,\n      })) as DeployedFrontendLib);\n    });\n  });\n\n  describe('Getter', () => {\n    it('Should get all nullifier at once', async () => {\n      const destinations =\n        await frontendLib.getHydraS1AccountboundAttesterDestinationOfNullifierBatch([\n          nullifier1,\n          nullifier2,\n          nullifier3,\n          // unregistered nullifier\n          '0x0000000000000000000000000000000000000000000000000000000000000123',\n        ]);\n      expect(destinations[0]).to.equal(dest1.address);\n      expect(destinations[1]).to.equal(dest2.address);\n      expect(destinations[2]).to.equal(dest3.address);\n      expect(destinations[3]).to.equal('0x0000000000000000000000000000000000000000');\n    });\n  });\n});\n"
  },
  {
    "path": "test/unit/zkdrop/zk-badgebound-erc721.test.ts",
    "content": "import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers';\nimport { EddsaPublicKey, HydraS1Account, KVMerkleTree } from '@sismo-core/hydra-s1';\nimport { expect } from 'chai';\nimport { BigNumber } from 'ethers';\nimport hre, { ethers } from 'hardhat';\nimport { deploymentsConfig } from '../../../tasks/deploy-tasks/deployments-config';\nimport { getImplementation } from '../../../utils';\nimport {\n  checkAccountHoldsBadge,\n  checkAttestationIsWellRegistered,\n  deployCoreContracts,\n  mintBadge,\n  registerRootForAttester,\n} from '../../utils/test-helpers';\nimport {\n  AttestationsRegistry,\n  AvailableRootsRegistry,\n  Badges,\n  HydraS1AccountboundAttester,\n  TransparentUpgradeableProxy__factory,\n  ZKBadgeboundERC721,\n} from '../../../types';\nimport {\n  evmRevert,\n  evmSnapshot,\n  HydraS1SimpleGroup,\n  generateProvingData,\n  HydraS1ZKPS,\n  increaseTime,\n} from '../../utils';\n\ndescribe('Test ZK Badgebound ERC721 Contract', async () => {\n  let attestationsRegistry: AttestationsRegistry;\n  let hydraS1AccountboundAttester: HydraS1AccountboundAttester;\n  let availableRootsRegistry: AvailableRootsRegistry;\n  let badges: Badges;\n  let zkBadgeboundERC721: ZKBadgeboundERC721;\n\n  let accountsSigners: SignerWithAddress[];\n  let deployer: SignerWithAddress;\n  let account1Signer: SignerWithAddress;\n  let account2Signer: SignerWithAddress;\n  let account3Signer: SignerWithAddress;\n  let account4Signer: SignerWithAddress;\n  let account5Signer: SignerWithAddress;\n\n  let accounts: HydraS1Account[];\n  let account1: HydraS1Account;\n  let account2: HydraS1Account;\n  let account3: HydraS1Account;\n  let account4: HydraS1Account;\n  let account5: HydraS1Account;\n  let zeroAddress: string;\n\n  let accountsTreesWithData: { tree: KVMerkleTree; group: HydraS1SimpleGroup }[];\n  let registryTree: KVMerkleTree;\n  let groups: HydraS1SimpleGroup[];\n  let commitmentMapperPubKey: EddsaPublicKey;\n\n  let provingScheme: HydraS1ZKPS;\n\n  let cooldownDuration: number;\n  let evmSnapshotId: string;\n  let resetStateSnapshotId: string;\n  let chainId: number;\n\n  let badgeId: BigNumber;\n  let badgeId2: BigNumber;\n\n  let roles: { admin: string };\n\n  before(async () => {\n    chainId = parseInt(await hre.getChainId());\n\n    // create two groups in the merkle tree with respectively all values of 1 and 2\n    ({ accountsTreesWithData, registryTree, groups, accounts, commitmentMapperPubKey } =\n      await generateProvingData({\n        nbOfGroups: 2,\n      }));\n\n    cooldownDuration = 60 * 60 * 24;\n\n    // data to reuse everywhere\n\n    accountsSigners = await ethers.getSigners();\n\n    deployer = accountsSigners[0];\n    account1Signer = accountsSigners[1];\n    account2Signer = accountsSigners[2];\n    account3Signer = accountsSigners[3];\n    account4Signer = accountsSigners[4];\n    account5Signer = accountsSigners[5];\n\n    account1 = accounts[1];\n    account2 = accounts[2];\n    account3 = accounts[3];\n    account4 = accounts[4];\n    account5 = accounts[5];\n\n    zeroAddress = '0x0000000000000000000000000000000000000000';\n  });\n\n  /*************************************************************************************/\n  /********************************** DEPLOYMENTS **************************************/\n  /*************************************************************************************/\n  describe('Deployments', () => {\n    it('Should deploy, setup contracts, the proving scheme and test the constructed values of the contract', async () => {\n      ({ attestationsRegistry, hydraS1AccountboundAttester, badges, availableRootsRegistry } =\n        await deployCoreContracts(deployer, {\n          deploymentNamePrefix: 'zk-badgebound-erc721',\n        }));\n\n      badgeId = (await hydraS1AccountboundAttester.AUTHORIZED_COLLECTION_ID_FIRST()).add(\n        groups[0].properties.groupIndex\n      );\n\n      badgeId2 = (await hydraS1AccountboundAttester.AUTHORIZED_COLLECTION_ID_FIRST()).add(\n        groups[1].properties.groupIndex\n      );\n\n      ({ zkBadgeboundERC721 } = await hre.run('deploy-zk-badgebound-erc721', {\n        name: 'Ziki Pass',\n        symbol: 'ZKP',\n        tokenURI: 'https://test.com/zikipass',\n        gatingBadgeTokenId: badgeId.toString(),\n        admin: deployer.address,\n        options: { deploymentNamePrefix: 'zk-badgebound-erc721' },\n      }));\n\n      await registerRootForAttester(\n        availableRootsRegistry,\n        hydraS1AccountboundAttester,\n        registryTree\n      );\n\n      await hydraS1AccountboundAttester.setCooldownDurationForGroupIndex(\n        groups[0].properties.groupIndex,\n        cooldownDuration\n      );\n\n      roles = {\n        admin: await zkBadgeboundERC721.DEFAULT_ADMIN_ROLE(),\n      };\n\n      // set up the proving scheme\n      provingScheme = new HydraS1ZKPS({\n        accountsTreesWithData,\n        registryTree,\n        groups,\n        defaultAttester: hydraS1AccountboundAttester,\n        commitmentMapperPubKey,\n        chainId,\n      });\n    });\n\n    it('should check the contract configuration', async () => {\n      expect(await zkBadgeboundERC721.name()).to.be.eql('Ziki Pass');\n      expect(await zkBadgeboundERC721.symbol()).to.equal('ZKP');\n      expect(await zkBadgeboundERC721.GATING_BADGE_TOKEN_ID()).to.be.eql(badgeId);\n      expect(await zkBadgeboundERC721.tokenURI(0)).to.be.eql('https://test.com/zikipass');\n    });\n  });\n\n  describe('Access Control', async () => {\n    it('Should check roles', async () => {\n      expect(await zkBadgeboundERC721.hasRole(roles.admin, deployer.address)).to.be.true;\n    });\n\n    it('Should revert when the sender has not the admin role', async () => {\n      await expect(\n        zkBadgeboundERC721.connect(account5Signer).setBaseTokenUri('https://other-test-domain.com/')\n      ).to.be.revertedWith(\n        `AccessControl: account ${account5Signer.address.toLowerCase()} is missing role ${\n          roles.admin\n        }`\n      );\n    });\n\n    it('Should set the URI', async () => {\n      const tx = await zkBadgeboundERC721\n        .connect(deployer)\n        .setBaseTokenUri('https://other-test-domain.com/');\n\n      await expect(tx)\n        .to.emit(zkBadgeboundERC721, 'BaseTokenUriChanged')\n        .withArgs('https://other-test-domain.com/');\n    });\n\n    it('Should revert when pause() is triggered with no admin role', async () => {\n      await expect(zkBadgeboundERC721.connect(account5Signer).pause()).to.be.revertedWith(\n        `AccessControl: account ${account5Signer.address.toLowerCase()} is missing role ${\n          roles.admin\n        }`\n      );\n    });\n\n    it('Should pause the contract and unpause', async () => {\n      await zkBadgeboundERC721.connect(deployer).pause();\n\n      expect(await zkBadgeboundERC721.paused()).to.be.true;\n      await zkBadgeboundERC721.connect(deployer).unpause();\n      expect(await zkBadgeboundERC721.paused()).to.be.false;\n    });\n\n    it('Should grant admin role', async () => {\n      await zkBadgeboundERC721.connect(deployer).grantRole(roles.admin, account5Signer.address);\n      expect(await zkBadgeboundERC721.hasRole(roles.admin, account5Signer.address)).to.be.true;\n    });\n\n    it('Should revoke admin role', async () => {\n      await zkBadgeboundERC721.connect(account5Signer).revokeRole(roles.admin, deployer.address);\n      expect(await zkBadgeboundERC721.hasRole(roles.admin, deployer.address)).to.be.false;\n    });\n  });\n\n  describe('Scenario 1: mint badge, mint NFT, transfer badge and then transfer NFT. Prevent to mint again.', () => {\n    it('source 0x1 mints the ZK Badge on dest 0x2 for the first time (same user controls 0x1 and 0x2)', async () => {\n      resetStateSnapshotId = await evmSnapshot(hre);\n\n      await mintBadge({\n        sources: [account1],\n        destination: account2,\n        provingScheme,\n      });\n    });\n\n    it('dest 0x2 should not be able to mint the ERC721 since the contract is paused', async () => {\n      await zkBadgeboundERC721.connect(account5Signer).pause();\n      expect(await zkBadgeboundERC721.paused()).to.be.true;\n      await expect(zkBadgeboundERC721.connect(account2Signer).claim()).to.be.revertedWith(\n        'NoTokenTransferWhilePaused()'\n      );\n      await zkBadgeboundERC721.connect(account5Signer).unpause();\n    });\n\n    it('dest 0x2 should NOT be able to mint the ERC721 on 0x3 without proof', async () => {\n      await mintBadge({\n        sources: [account1],\n        destination: account2,\n        provingScheme,\n        isDestinationAlreadyHoldingTheBadge: true,\n      });\n\n      await checkAccountHoldsBadge(account2Signer.address, badgeId, true);\n\n      await expect(\n        zkBadgeboundERC721.connect(account2Signer).claimTo(account3Signer.address)\n      ).to.be.revertedWith('UserDoesNotMeetRequirements()');\n    });\n\n    it('dest 0x2 should be able to mint the ERC721 on 0x2 (by calling `claim`)', async () => {\n      evmSnapshotId = await evmSnapshot(hre);\n      await checkAccountHoldsBadge(account2Signer.address, badgeId);\n\n      expect(await zkBadgeboundERC721.balanceOf(account2Signer.address)).to.be.eql(\n        BigNumber.from(0)\n      );\n\n      await zkBadgeboundERC721.connect(account2Signer).claim();\n\n      expect(await zkBadgeboundERC721.balanceOf(account2Signer.address)).to.be.eql(\n        BigNumber.from(1)\n      );\n\n      await evmRevert(hre, evmSnapshotId);\n    });\n\n    it('dest 0x2 should be able to mint the ERC721 on 0x2 (by calling `claimTo`)', async () => {\n      await checkAccountHoldsBadge(account2Signer.address, badgeId);\n\n      expect(await zkBadgeboundERC721.balanceOf(account2Signer.address)).to.be.eql(\n        BigNumber.from(0)\n      );\n\n      await zkBadgeboundERC721.connect(account2Signer).claimTo(account2Signer.address);\n\n      expect(await zkBadgeboundERC721.balanceOf(account2Signer.address)).to.be.eql(\n        BigNumber.from(1)\n      );\n    });\n\n    it('source 0x1 transfers the ZK Badge from dest 0x2 to dest 0x3 (same user controls source 0x1, dest 0x2 and dest 0x3)', async () => {\n      // 1 - Transfers the badge from 0x2 to 0x3\n      await mintBadge({\n        sources: [account1],\n        destination: account3,\n        provingScheme,\n        expectedBurnCount: 1,\n      });\n\n      // 2 - Checks that 0x2 does not have the badge anymore\n      await checkAccountHoldsBadge(account2Signer.address, badgeId, false);\n    });\n\n    it('0x3 should NOT be able to mint the ERC721 (one ERC721 per source, using `claim`)', async () => {\n      await expect(zkBadgeboundERC721.connect(account3Signer).claim()).to.be.revertedWith(\n        'ERC721: token already minted'\n      );\n    });\n\n    it('0x3 should NOT be able to mint the ERC721 (one ERC721 per source, using `claimTo`)', async () => {\n      await expect(\n        zkBadgeboundERC721.connect(account3Signer).claimTo(account3Signer.address)\n      ).to.be.revertedWith('ERC721: token already minted');\n    });\n\n    it('0x2 should be able to transfer the ERC721 from 0x2 to 0x3 (using `safeTransferFrom`)', async () => {\n      evmSnapshotId = await evmSnapshot(hre);\n\n      const { nullifier: nullifierFrom0x1 } = await provingScheme.generateProof({\n        sources: [account1],\n        destination: account3,\n      });\n\n      expect(await zkBadgeboundERC721.ownerOf(nullifierFrom0x1)).to.be.eql(account2Signer.address);\n\n      expect(await zkBadgeboundERC721.balanceOf(account3Signer.address)).to.be.eql(\n        BigNumber.from(0)\n      );\n\n      await zkBadgeboundERC721\n        .connect(account2Signer)\n        ['safeTransferFrom(address,address,uint256)'](\n          account2Signer.address,\n          account3Signer.address,\n          nullifierFrom0x1\n        );\n\n      expect(await zkBadgeboundERC721.ownerOf(nullifierFrom0x1)).to.be.eql(account3Signer.address);\n\n      expect(await zkBadgeboundERC721.balanceOf(account2Signer.address)).to.be.eql(\n        BigNumber.from(0)\n      );\n\n      expect(await zkBadgeboundERC721.balanceOf(account3Signer.address)).to.be.eql(\n        BigNumber.from(1)\n      );\n\n      await evmRevert(hre, evmSnapshotId);\n    });\n\n    it('0x2 should be able to transfer the ERC721 from 0x2 to 0x3 (using `transferFrom`)', async () => {\n      evmSnapshotId = await evmSnapshot(hre);\n\n      const { nullifier: nullifierFrom0x1 } = await provingScheme.generateProof({\n        sources: [account1],\n        destination: account3,\n      });\n\n      expect(await zkBadgeboundERC721.ownerOf(nullifierFrom0x1)).to.be.eql(account2Signer.address);\n\n      expect(await zkBadgeboundERC721.balanceOf(account3Signer.address)).to.be.eql(\n        BigNumber.from(0)\n      );\n\n      await zkBadgeboundERC721\n        .connect(account2Signer)\n        .transferFrom(account2Signer.address, account3Signer.address, nullifierFrom0x1);\n\n      expect(await zkBadgeboundERC721.ownerOf(nullifierFrom0x1)).to.be.eql(account3Signer.address);\n\n      expect(await zkBadgeboundERC721.balanceOf(account2Signer.address)).to.be.eql(\n        BigNumber.from(0)\n      );\n\n      expect(await zkBadgeboundERC721.balanceOf(account3Signer.address)).to.be.eql(\n        BigNumber.from(1)\n      );\n    });\n\n    it('0x3 should be able to transfer ERC721 from 0x3 to any destination with a valid proof of ownership of 0x1 and new destination (badge will be also transferred)', async () => {\n      increaseTime(hre, cooldownDuration);\n\n      const {\n        request,\n        proofData,\n        nullifier: nullifierFrom0x1,\n      } = await provingScheme.generateProof({\n        sources: [account1],\n        destination: account4,\n      });\n\n      expect(await zkBadgeboundERC721.ownerOf(nullifierFrom0x1)).to.be.eql(account3Signer.address);\n\n      expect(await zkBadgeboundERC721.balanceOf(account4Signer.address)).to.be.eql(\n        BigNumber.from(0)\n      );\n\n      const transferTx = await zkBadgeboundERC721\n        .connect(account3Signer)\n        .transferWithSismo(\n          account3Signer.address,\n          account4Signer.address,\n          nullifierFrom0x1,\n          request,\n          proofData\n        );\n\n      // it should transfer the nft to the new destination\n      expect(await zkBadgeboundERC721.ownerOf(nullifierFrom0x1)).to.be.eql(account4Signer.address);\n\n      expect(await zkBadgeboundERC721.balanceOf(account3Signer.address)).to.be.eql(\n        BigNumber.from(0)\n      );\n\n      await checkAttestationIsWellRegistered({\n        request,\n        nullifier: nullifierFrom0x1,\n        expectedBurnCount: 2,\n        tx: transferTx,\n      });\n\n      // 2 - Checks that 0x3 does NOT hold the badge anymore\n      checkAccountHoldsBadge(account3Signer.address, badgeId, false);\n\n      // 3 - Checks that 0x4 holds the badge\n      checkAccountHoldsBadge(account4Signer.address, badgeId);\n\n      await evmRevert(hre, resetStateSnapshotId);\n    });\n  });\n\n  describe('Scenario 2: new badge minting on an address already owning a NFT (with a different nullifier), prevent new nft minting on this address, mint a new nft by transferring the badge first to a new address', async () => {\n    it('source 0x1 mints the ZK Badge and the ERC721 on dest 0x2 for the first time with a valid proof of ownership of 0x1 and 0x2', async () => {\n      resetStateSnapshotId = await evmSnapshot(hre);\n\n      const {\n        request,\n        proofData,\n        nullifier: nullifierFrom0x1,\n      } = await provingScheme.generateProof({\n        sources: [account1],\n        destination: account2,\n      });\n\n      const mintNftTx = await zkBadgeboundERC721.claimWithSismo(request, proofData);\n\n      await checkAttestationIsWellRegistered({\n        request,\n        nullifier: nullifierFrom0x1,\n        tx: mintNftTx,\n      });\n    });\n\n    it('dest 0x2 should NOT be able to mint the ERC721 again on 0x2', async () => {\n      await expect(\n        zkBadgeboundERC721.connect(account2Signer).claimTo(account2Signer.address)\n      ).to.be.revertedWith('ERC721: token already minted');\n    });\n\n    it('source 0x3 should not be able to mint a NFT on dest 0x2 because 0x2 owns an ERC721', async () => {\n      expect(await zkBadgeboundERC721.balanceOf(account2Signer.address)).to.be.eql(\n        BigNumber.from(1)\n      );\n\n      await expect(zkBadgeboundERC721.claimTo(account2Signer.address)).to.be.revertedWith(\n        `ERC721: token already minted`\n      );\n    });\n\n    it('source 0x3 should NOT be able to mint a NFT on dest 0x2 (even with a valid proof) because 0x2 owns an ERC721', async () => {\n      const { request, proofData } = await provingScheme.generateProof({\n        sources: [account3],\n        destination: account2,\n      });\n\n      await expect(zkBadgeboundERC721.claimWithSismo(request, proofData)).to.be.revertedWith(\n        `ERC721AlreadyOwned(\"${account2Signer.address}\", 1)`\n      );\n    });\n\n    it('source 0x3 should be able to mint a badge on dest 0x2', async () => {\n      const { nullifier: nullifierFrom0x3 } = await mintBadge({\n        sources: [account3],\n        destination: account2,\n        provingScheme,\n        isDestinationAlreadyHoldingTheBadge: true,\n      });\n\n      // We retrieve the old nullifier to see if it is still the one registered in the extraData of the attestation (should not be the case)\n      const nullifierFrom0x1 = await provingScheme.getNullifier({\n        sources: [account1], // account1 is the source that was used to mint the ERC721 hold on account2\n        destination: account2,\n      });\n\n      // We check that the old nullifier is different from the new nullifier\n      expect(nullifierFrom0x3).to.not.be.eql(nullifierFrom0x1);\n\n      // We check that the new nullifier is the one registered in the extraData of the attestation\n      const extraData = await attestationsRegistry.getAttestationExtraData(\n        badgeId,\n        account2Signer.address\n      );\n\n      expect(await hydraS1AccountboundAttester.getNullifierFromExtraData(extraData)).to.equal(\n        nullifierFrom0x3\n      );\n    });\n\n    it('source 0x3 should not be able to mint a ERC721 on dest 0x2 because 0x2 owns an ERC721 (Even with a new badge on 0x2 with a new nullifier)', async () => {\n      expect(await zkBadgeboundERC721.balanceOf(account2Signer.address)).to.be.eql(\n        BigNumber.from(1)\n      );\n\n      await expect(zkBadgeboundERC721.claimTo(account2Signer.address)).to.be.revertedWith(\n        `ERC721AlreadyOwned(\"${account2Signer.address}\", 1)`\n      );\n    });\n\n    it('source 0x3 should not be able to mint a ERC721 on dest 0x2 because 0x2 owns an ERC721 (even with a valid proof and a new badge)', async () => {\n      const { request, proofData } = await provingScheme.generateProof({\n        sources: [account3],\n        destination: account2,\n      });\n\n      await expect(zkBadgeboundERC721.claimWithSismo(request, proofData)).to.be.revertedWith(\n        `ERC721AlreadyOwned(\"${account2Signer.address}\", 1)`\n      );\n    });\n\n    it('source 0x3 should be able to transfer badge3 from 0x2 to 0x3', async () => {\n      await mintBadge({\n        sources: [account3],\n        destination: account3,\n        provingScheme,\n        expectedBurnCount: 1,\n      });\n\n      // 2 - Checks that the attester unrecorded the attestation in the registry\n      await checkAccountHoldsBadge(account2Signer.address, badgeId, false);\n    });\n\n    it('dest 0x3 should be able to mint a ERC721 on 0x3', async () => {\n      expect(await zkBadgeboundERC721.balanceOf(account3Signer.address)).to.be.eql(\n        BigNumber.from(0)\n      );\n\n      expect(await zkBadgeboundERC721.connect(account3Signer).claim());\n\n      expect(await zkBadgeboundERC721.balanceOf(account3Signer.address)).to.be.eql(\n        BigNumber.from(1)\n      );\n\n      // we use the same proof request as in the previous test to check that the token Id is indeed the nullifier used for the badge\n      const nullifierFrom0x3 = await provingScheme.getNullifier({\n        sources: [account3],\n        destination: account3,\n      });\n\n      expect(await zkBadgeboundERC721.ownerOf(nullifierFrom0x3)).to.be.eql(account3Signer.address);\n    });\n\n    it('dest0x2 should NOT be able to transfer the nft1 (minted with nullifier from 0x1) on 0x3', async () => {\n      // this nullifier is the tokenId of the nft we want to transfer from 0x2 to 0x3\n      const nullifierFrom0x1 = await provingScheme.getNullifier({\n        sources: [account1],\n        destination: account3,\n      });\n\n      // retrieve the nullifier of the badge (nullifier from 0x3) that is also the tokenId of the nft on 0x3\n      const nullifierFrom0x3 = await provingScheme.getNullifier({\n        sources: [account3],\n        destination: account3,\n      });\n\n      await expect(\n        zkBadgeboundERC721\n          .connect(account2Signer)\n          .transferFrom(\n            account2Signer.address,\n            account3Signer.address,\n            BigNumber.from(nullifierFrom0x1)\n          )\n      ).to.be.revertedWith(\n        `BadgeNullifierNotEqualToTokenId(${BigNumber.from(nullifierFrom0x3)}, ${BigNumber.from(\n          nullifierFrom0x1\n        )})'`\n      );\n    });\n\n    it('dest0x2 should NOT be able to transfer the nft1 on 0x3 even with a valid proof (proof with 0x1 nullifier)', async () => {\n      const {\n        request,\n        proofData,\n        nullifier: nullifierFrom0x1,\n      } = await provingScheme.generateProof({\n        sources: [account1],\n        destination: account3,\n      });\n\n      await expect(\n        zkBadgeboundERC721.transferWithSismo(\n          account2Signer.address,\n          account3Signer.address,\n          nullifierFrom0x1,\n          request,\n          proofData\n        )\n      ).to.be.revertedWith(`ERC721AlreadyOwned(\"${account3Signer.address}\", 1)`);\n    });\n\n    it('source 0x1 should be able to mint a badge on dest 0x2', async () => {\n      evmSnapshotId = await evmSnapshot(hre);\n\n      const { request, proofData } = await provingScheme.generateProof({\n        sources: [account1],\n        destination: account2,\n      });\n\n      // 0x2 should not have the badge yet\n      await checkAccountHoldsBadge(account2Signer.address, badgeId, false);\n\n      await hydraS1AccountboundAttester.generateAttestations(request, proofData);\n\n      // 0x2 should have the badge now\n      await checkAccountHoldsBadge(account2Signer.address, badgeId);\n\n      await evmRevert(hre, evmSnapshotId);\n    });\n\n    it('dest 0x2 should be able to transfer the ERC721 from 0x2 to 0x1 with a valid proof of ownership of 0x1 and 0x2', async () => {\n      // 0x1 should not have the badge yet\n      await checkAccountHoldsBadge(account1Signer.address, badgeId, false);\n\n      // 0x1 should have no nft\n      expect(await zkBadgeboundERC721.balanceOf(account1Signer.address)).to.be.eql(\n        BigNumber.from(0)\n      );\n\n      const {\n        request,\n        proofData,\n        nullifier: nullifierFrom0x1,\n      } = await provingScheme.generateProof({\n        sources: [account1],\n        destination: account1,\n      });\n\n      // 0x2 should have no badge\n      await checkAccountHoldsBadge(account2Signer.address, badgeId, false);\n\n      // 0x2 should have the nft\n      expect(await zkBadgeboundERC721.ownerOf(nullifierFrom0x1)).to.be.eql(account2Signer.address);\n\n      const transferTx = await zkBadgeboundERC721.transferWithSismo(\n        account2Signer.address,\n        account1Signer.address,\n        nullifierFrom0x1,\n        request,\n        proofData\n      );\n\n      await checkAttestationIsWellRegistered({\n        request,\n        nullifier: nullifierFrom0x1,\n        expectedBurnCount: 1, // for the same reason, the burn count is incremented to 1\n        tx: transferTx,\n      });\n\n      // 0x1 should have the badge now\n      await checkAccountHoldsBadge(account1Signer.address, badgeId);\n\n      // 0x1 should have the nft\n      expect(await zkBadgeboundERC721.ownerOf(nullifierFrom0x1)).to.be.eql(account1Signer.address);\n\n      // 0x2 should have no badge\n      await checkAccountHoldsBadge(account2Signer.address, badgeId, false);\n\n      // 0x2 should have no nft\n      expect(await zkBadgeboundERC721.balanceOf(account2Signer.address)).to.be.eql(\n        BigNumber.from(0)\n      );\n\n      await evmRevert(hre, resetStateSnapshotId);\n    });\n  });\n\n  describe('Scenario 3: tests that badges can be minted by several sources on a destination but the nft can only be transferred to the account holding the valid badge (same nullifier) or with a valid proof (same nullifier) ', async () => {\n    it('source 0x1 mints the ZK Badge on 0x2 for the first time', async () => {\n      resetStateSnapshotId = await evmSnapshot(hre);\n\n      // 0x2 should not have the badge yet\n      await checkAccountHoldsBadge(account2Signer.address, badgeId, false);\n\n      await mintBadge({\n        sources: [account1],\n        destination: account2,\n        provingScheme,\n      });\n\n      // 0x2 should only have the badge\n      expect(await zkBadgeboundERC721.balanceOf(account2Signer.address)).to.be.eql(\n        BigNumber.from(0)\n      );\n    });\n\n    it('dest 0x2 should be able to mint the ERC721', async () => {\n      evmSnapshotId = await evmSnapshot(hre);\n\n      const { nullifier: nullifierFrom0x1 } = await provingScheme.generateProof({\n        sources: [account1],\n        destination: account2,\n      });\n\n      await zkBadgeboundERC721.connect(account2Signer).claim();\n\n      // 0x2 should have the nft\n      expect(await zkBadgeboundERC721.ownerOf(nullifierFrom0x1)).to.be.eql(account2Signer.address);\n\n      expect(await zkBadgeboundERC721.balanceOf(account2Signer.address)).to.be.eql(\n        BigNumber.from(1)\n      );\n\n      await evmRevert(hre, evmSnapshotId);\n    });\n\n    it('source 0x4 mints the ZK Badge on 0x2 for the first time', async () => {\n      resetStateSnapshotId = await evmSnapshot(hre);\n\n      const nullifierFrom0x1 = await provingScheme.getNullifier({\n        sources: [account1],\n        destination: account2,\n      });\n\n      // 0x2 should hold the attestation with 0x1 nullifier\n      const attestationsExtraData1 = await attestationsRegistry.getAttestationExtraData(\n        badgeId,\n        account2Signer.address\n      );\n\n      expect(\n        await hydraS1AccountboundAttester.getNullifierFromExtraData(attestationsExtraData1)\n      ).to.be.eql(nullifierFrom0x1);\n\n      // 0x4 mints the badge on 0x2\n      const { nullifier: nullifierFrom0x4 } = await mintBadge({\n        sources: [account4],\n        destination: account2,\n        provingScheme,\n        isDestinationAlreadyHoldingTheBadge: true,\n      });\n\n      const attestationsExtraData4 = await attestationsRegistry.getAttestationExtraData(\n        badgeId,\n        account2Signer.address\n      );\n\n      // verify that the attestation has the nullifier from 0x4\n      expect(\n        await hydraS1AccountboundAttester.getNullifierFromExtraData(attestationsExtraData4)\n      ).to.be.eql(nullifierFrom0x4);\n    });\n\n    it('dest 0x2 mint the ERC721 on 0x2 (with the nullifier of 0x4 since 0x2 holds this badge)', async () => {\n      const nullifierFrom0x4 = await provingScheme.getNullifier({\n        sources: [account4],\n        destination: account2,\n      });\n\n      expect(await zkBadgeboundERC721.balanceOf(account2Signer.address)).to.be.eql(\n        BigNumber.from(0)\n      );\n\n      await zkBadgeboundERC721.connect(account2Signer).claim();\n\n      // 0x2 should have the nft\n      expect(await zkBadgeboundERC721.ownerOf(nullifierFrom0x4)).to.be.eql(account2Signer.address);\n\n      expect(await zkBadgeboundERC721.balanceOf(account2Signer.address)).to.be.eql(\n        BigNumber.from(1)\n      );\n    });\n\n    it('source 0x3 mints a badge on dest 0x2', async () => {\n      const nullifierFrom0x4 = await provingScheme.getNullifier({\n        sources: [account4],\n        destination: account2,\n      });\n\n      // 0x2 should hold the attestation with 0x4 nullifier\n      const attestationsExtraData4 = await attestationsRegistry.getAttestationExtraData(\n        badgeId,\n        account2Signer.address\n      );\n\n      expect(\n        await hydraS1AccountboundAttester.getNullifierFromExtraData(attestationsExtraData4)\n      ).to.be.eql(nullifierFrom0x4);\n\n      // 0x3 mints a badge on 0x2\n      const { nullifier: nullifierFrom0x3 } = await mintBadge({\n        sources: [account3],\n        destination: account2,\n        provingScheme,\n        isDestinationAlreadyHoldingTheBadge: true,\n      });\n\n      // 0x2 should have the badge with 0x3 nullifier\n      const attestationsExtraData3 = await attestationsRegistry.getAttestationExtraData(\n        badgeId,\n        account2Signer.address\n      );\n\n      expect(\n        await hydraS1AccountboundAttester.getNullifierFromExtraData(attestationsExtraData3)\n      ).to.be.eql(nullifierFrom0x3);\n    });\n\n    it('dest 0x2 can’t transfer the ERC721 (minted with nullifier from 0x4) to 0x3', async () => {\n      const nullifierFrom0x4 = await provingScheme.getNullifier({\n        sources: [account4],\n        destination: account2,\n      });\n\n      // revert transferFrom\n      await expect(\n        zkBadgeboundERC721\n          .connect(account2Signer)\n          .transferFrom(account2Signer.address, account3Signer.address, nullifierFrom0x4)\n      ).to.be.revertedWith('UserDoesNotMeetRequirements()');\n\n      // revert safeTransferFrom\n      await expect(\n        zkBadgeboundERC721\n          .connect(account2Signer)\n          ['safeTransferFrom(address,address,uint256)'](\n            account2Signer.address,\n            account3Signer.address,\n            nullifierFrom0x4\n          )\n      ).to.be.revertedWith('UserDoesNotMeetRequirements()');\n    });\n\n    it('dest 0x2 can’t transfer the ERC721 (minted with nullifier from 0x4) to 0x3 (even with a proof of ownership of 0x2 and 0x3)', async () => {\n      const nullifierFrom0x4 = await provingScheme.getNullifier({\n        sources: [account4],\n        destination: account3,\n      });\n\n      const {\n        request,\n        proofData,\n        nullifier: nullifierFrom0x3,\n      } = await provingScheme.generateProof({\n        sources: [account3],\n        destination: account3,\n      });\n\n      // revert transferWithSismo (valid proof, but the nft (nullifier) is not the same than the nullifier in the proof)\n      await expect(\n        zkBadgeboundERC721\n          .connect(account2Signer)\n          .transferWithSismo(\n            account2Signer.address,\n            account3Signer.address,\n            nullifierFrom0x4,\n            request,\n            proofData\n          )\n      ).to.be.revertedWith(\n        `BadgeNullifierNotEqualToTokenId(${nullifierFrom0x3}, ${nullifierFrom0x4})`\n      );\n      await evmRevert(hre, resetStateSnapshotId);\n    });\n  });\n\n  describe('Remaining scenarios: Test that strange edge cases do not happen', async () => {\n    it('0x3 should NOT be able to mint an ERC721 on 0x3 if 0x3 holds a badge (but not the gated badge)', async () => {\n      // 0x1 mints a badge on 0x03 (but not the gated badge)\n      await mintBadge({\n        sources: [account1],\n        destination: account3,\n        group: groups[1], // group 1 is not the gated badge group\n        value: BigNumber.from(2), // 2 is the value of the second group\n        provingScheme,\n      });\n\n      // 0x3 should NOT hold the gated badge\n      await checkAccountHoldsBadge(account3Signer.address, badgeId, false);\n\n      // 0x3 should NOT be able to mint an ERC721 on 0x3 if 0x3 holds a badge (but not the gated badge)\n      await expect(zkBadgeboundERC721.connect(account3Signer).claim()).to.be.revertedWith(\n        'UserDoesNotMeetRequirements()'\n      );\n      await evmRevert(hre, resetStateSnapshotId);\n    });\n\n    it('0x1 should NOT be able to mint a NFT on 0x00', async () => {\n      resetStateSnapshotId = await evmSnapshot(hre);\n      // 0x1 mints a badge on 0x02\n      await mintBadge({\n        sources: [account1],\n        destination: account2,\n        provingScheme,\n        isDestinationAlreadyHoldingTheBadge: true,\n      });\n\n      await expect(\n        zkBadgeboundERC721.connect(account2Signer).claimTo(zeroAddress)\n      ).to.be.revertedWith('UserDoesNotMeetRequirements()');\n    });\n\n    it('should NOT allow the transfer of the nft to 0x00', async () => {\n      resetStateSnapshotId = await evmSnapshot(hre);\n\n      const { nullifier: nullifierFrom0x1 } = await mintBadge({\n        sources: [account1],\n        destination: account2,\n        provingScheme,\n        isDestinationAlreadyHoldingTheBadge: true,\n      });\n\n      // 0x2 mint the nft\n      await zkBadgeboundERC721.connect(account2Signer).claim();\n\n      // 0x2 should hold the nft\n      expect(await zkBadgeboundERC721.balanceOf(account2Signer.address)).to.be.eql(\n        BigNumber.from(1)\n      );\n\n      // 0x2 should NOT be able to transfer the nft to 0x00\n      await expect(\n        zkBadgeboundERC721\n          .connect(account2Signer)\n          ['safeTransferFrom(address,address,uint256)'](\n            account2Signer.address,\n            zeroAddress,\n            nullifierFrom0x1\n          )\n      ).to.be.revertedWith('UserDoesNotMeetRequirements()');\n      await evmRevert(hre, resetStateSnapshotId);\n    });\n\n    it('should NOT allow the transfer of the nft from 0x00 if NFT is not minted', async () => {\n      resetStateSnapshotId = await evmSnapshot(hre);\n\n      const { nullifier: nullifierFrom0x1 } = await mintBadge({\n        sources: [account1],\n        destination: account2,\n        provingScheme,\n        isDestinationAlreadyHoldingTheBadge: true,\n      });\n\n      // 0x2 should NOT be able to transfer the nft from 0x00 to 0x2\n      await expect(\n        zkBadgeboundERC721\n          .connect(account2Signer)\n          ['safeTransferFrom(address,address,uint256)'](\n            zeroAddress,\n            account2Signer.address,\n            nullifierFrom0x1\n          )\n      ).to.be.revertedWith('ERC721: invalid token ID');\n\n      await evmRevert(hre, resetStateSnapshotId);\n    });\n\n    it('should NOT allow the transfer of the nft from 0x00 if NFT is minted', async () => {\n      resetStateSnapshotId = await evmSnapshot(hre);\n\n      const { nullifier: nullifierFrom0x1 } = await mintBadge({\n        sources: [account1],\n        destination: account2,\n        provingScheme,\n        isDestinationAlreadyHoldingTheBadge: true,\n      });\n\n      // 0x2 mint the nft\n      await zkBadgeboundERC721.connect(account2Signer).claim();\n\n      // 0x2 should NOT be able to transfer the nft from 0x00 to 0x2\n      await expect(\n        zkBadgeboundERC721\n          .connect(account2Signer)\n          ['safeTransferFrom(address,address,uint256)'](\n            zeroAddress,\n            account2Signer.address,\n            nullifierFrom0x1\n          )\n      ).to.be.revertedWith('ERC721: transfer from incorrect owner');\n    });\n  });\n\n  /*************************************************************************************/\n  /******************************* UPDATE IMPLEMENTATION *******************************/\n  /*************************************************************************************/\n  describe('Update implementation', () => {\n    it('Should update the implementation', async () => {\n      const proxyAdminSigner = await ethers.getSigner(\n        deploymentsConfig[hre.network.name].deployOptions.proxyAdmin as string\n      );\n\n      const { zkBadgeboundERC721: newZKBadgeboundERC721 } = await hre.run(\n        'deploy-zk-badgebound-erc721',\n        {\n          name: 'Ziki Pass',\n          symbol: 'ZKP',\n          tokenURI: 'https://test.com/zikipass',\n          gatingBadgeTokenId: badgeId.toString(),\n          admin: proxyAdminSigner.address,\n          options: { deploymentNamePrefix: 'zk-badgebound-erc721-new-implem', behindProxy: false },\n        }\n      );\n\n      const zkBadgeboundERC721Proxy = TransparentUpgradeableProxy__factory.connect(\n        zkBadgeboundERC721.address,\n        proxyAdminSigner\n      );\n\n      await (await zkBadgeboundERC721Proxy.upgradeTo(newZKBadgeboundERC721.address)).wait();\n\n      const implementationAddress = await getImplementation(zkBadgeboundERC721Proxy);\n      expect(implementationAddress).to.eql(newZKBadgeboundERC721.address);\n    });\n  });\n});\n"
  },
  {
    "path": "test/utils/attestation-logic.ts",
    "content": "import { BigNumber } from 'ethers';\n\nexport const computeId = (shardId: number, id: number): BigNumber => {\n  return BigNumber.from(shardId).mul(BigNumber.from(2).pow(224)).add(id);\n};\n"
  },
  {
    "path": "test/utils/evm.ts",
    "content": "import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers';\nimport { HardhatRuntimeEnvironment } from 'hardhat/types';\nimport { ethers } from 'hardhat';\n\nexport async function increaseTime(\n  hre: HardhatRuntimeEnvironment,\n  secondsToIncrease: number\n): Promise<void> {\n  await hre.ethers.provider.send('evm_increaseTime', [secondsToIncrease]);\n  await hre.ethers.provider.send('evm_mine', []);\n}\n\nexport async function setTime(hre: HardhatRuntimeEnvironment, timestamp: number): Promise<void> {\n  await hre.ethers.provider.send('evm_setNextBlockTimestamp', [timestamp]);\n}\n\nexport async function evmSnapshot(hre: HardhatRuntimeEnvironment): Promise<string> {\n  const snapshotId = hre.ethers.provider.send('evm_snapshot', []);\n  return snapshotId;\n}\n\nexport async function evmRevert(hre: HardhatRuntimeEnvironment, snapshotId: string): Promise<void> {\n  await hre.ethers.provider.send('evm_revert', [snapshotId]);\n}\n\nexport async function impersonateAddress(\n  hre: HardhatRuntimeEnvironment,\n  address: string,\n  overrideBalance?: boolean\n): Promise<SignerWithAddress> {\n  if (address === '0x0000000000000000000000000000000000000000') {\n    throw new Error(\"You can't impersonate 0x0 address!\");\n  }\n  await (hre as HardhatRuntimeEnvironment).network.provider.request({\n    method: 'hardhat_impersonateAccount',\n    params: [address],\n  });\n  if (overrideBalance) {\n    await (hre as HardhatRuntimeEnvironment).network.provider.request({\n      method: 'hardhat_setBalance',\n      params: [address, '0x10000000000000000000'],\n    });\n  }\n  const signer = hre.ethers.provider.getSigner(address);\n  return SignerWithAddress.create(signer);\n}\n\nexport async function getBlockTimestamp(): Promise<number> {\n  return (await ethers.provider.getBlock('latest')).timestamp;\n}\n"
  },
  {
    "path": "test/utils/expectEvent.ts",
    "content": "export const getEventArgs = (events: any, name: string) => {\n  const event = events && events.find((e: any) => e.event === name);\n  const args = event && event.args;\n  return args;\n};\n"
  },
  {
    "path": "test/utils/hydra-s1-accountbound.ts",
    "content": "import { BigNumber, BigNumberish, ethers } from 'ethers';\nimport {\n  ACCOUNTS_TREE_HEIGHT,\n  buildPoseidon,\n  KVMerkleTree,\n  MerkleTreeData,\n  REGISTRY_TREE_HEIGHT,\n  SNARK_FIELD,\n} from '@sismo-core/hydra-s1';\nimport { GroupData, RegistryAccountsMerkle } from 'test/utils/hydra-s1';\n\nexport type HydraS1AccountboundGroup = {\n  data: MerkleTreeData;\n  properties: HydraS1AccountboundGroupProperties;\n  id: string;\n};\n\nexport type HydraS1AccountboundGroupProperties = {\n  groupIndex: number;\n  generationTimestamp: number;\n  cooldownDuration: number;\n  isScore: boolean;\n};\n\nexport type AvailableGroupsAccountbound = {\n  groups: HydraS1AccountboundGroup[];\n  dataFormat: RegistryAccountsMerkle;\n};\n\nexport type generateHydraS1AccountBoundAttesterGroups = {\n  cooldownDuration?;\n  generationTimestamp?;\n  isScore?;\n};\n\nexport const generateHydraS1AccountboundAttesterGroups = async (\n  allList: GroupData[],\n  options: generateHydraS1AccountBoundAttesterGroups = {\n    cooldownDuration: 10,\n  }\n): Promise<AvailableGroupsAccountbound> => {\n  let poseidon = await buildPoseidon();\n\n  /*********************** GENERATE GROUPS *********************/\n\n  const groups: HydraS1AccountboundGroup[] = [];\n  let generationTimestamp = options.generationTimestamp\n    ? options.generationTimestamp\n    : Math.round(Date.now() / 1000);\n\n  for (let i = 0; i < allList.length; i++) {\n    const properties: HydraS1AccountboundGroupProperties = {\n      groupIndex: i,\n      generationTimestamp,\n      isScore: options.isScore !== undefined ? options.isScore : i % 2 == 1,\n      cooldownDuration: options.cooldownDuration,\n    };\n\n    groups.push({\n      data: allList[i],\n      properties,\n      id: generateHydraS1AccountboundGroupIdFromProperties(properties).toHexString(),\n    });\n    generationTimestamp++;\n  }\n\n  /************************ FORMAT DATA *********************/\n\n  const accountsTrees: KVMerkleTree[] = [];\n  const registryTreeData: MerkleTreeData = {};\n\n  for (let i = 0; i < groups.length; i++) {\n    let _accountsTree = new KVMerkleTree(groups[i].data, poseidon, ACCOUNTS_TREE_HEIGHT);\n    accountsTrees.push(_accountsTree);\n    registryTreeData[_accountsTree.getRoot().toHexString()] = groups[i].id;\n  }\n\n  const registryTree = new KVMerkleTree(registryTreeData, poseidon, REGISTRY_TREE_HEIGHT);\n\n  return {\n    groups,\n    dataFormat: {\n      accountsTrees,\n      registryTree,\n    },\n  };\n};\n\nexport const generateHydraS1AccountboundGroupIdFromProperties = (\n  groupProperties: HydraS1AccountboundGroupProperties\n): BigNumber => {\n  return generateHydraS1AccountboundGroupIdFromEncodedProperties(\n    encodeHydraS1AccountboundGroupProperties(groupProperties)\n  );\n};\n\nexport const generateHydraS1AccountboundGroupIdFromEncodedProperties = (\n  encodedProperties: string\n): BigNumber => {\n  return BigNumber.from(ethers.utils.keccak256(encodedProperties)).mod(SNARK_FIELD);\n};\n\nexport const encodeHydraS1AccountboundGroupProperties = (\n  groupProperties: HydraS1AccountboundGroupProperties\n): string => {\n  return ethers.utils.defaultAbiCoder.encode(\n    ['uint128', 'uint32', 'uint32', 'bool'],\n    [\n      groupProperties.groupIndex,\n      groupProperties.generationTimestamp,\n      groupProperties.cooldownDuration,\n      groupProperties.isScore,\n    ]\n  );\n};\n\nexport const encodeAccountBoundAttestationExtraData = ({\n  nullifier,\n  burnCount,\n}: {\n  nullifier: BigNumberish | BigInt;\n  burnCount: number;\n}) => {\n  return ethers.utils.defaultAbiCoder.encode(\n    ['bytes', 'uint16'],\n    [ethers.utils.defaultAbiCoder.encode(['uint256'], [nullifier]), burnCount]\n  );\n};\n\nexport const getNullifierFromExtraData = (extraData: string) => {\n  return ethers.utils.defaultAbiCoder.decode(\n    ['uint256'],\n    ethers.utils.defaultAbiCoder.decode(['bytes', 'uint16'], extraData)[0]\n  );\n};\n"
  },
  {
    "path": "test/utils/hydra-s1.ts",
    "content": "import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers';\nimport { CommitmentMapperTester, getOwnershipMsg } from '@sismo-core/commitment-mapper-tester-js';\nimport {\n  ACCOUNTS_TREE_HEIGHT,\n  buildPoseidon,\n  HydraS1Account,\n  HydraS1Prover,\n  KVMerkleTree,\n  MerkleTreeData,\n  REGISTRY_TREE_HEIGHT,\n  SnarkProof,\n  SNARK_FIELD,\n  Inputs,\n  EddsaPublicKey,\n} from '@sismo-core/hydra-s1';\nimport { BigNumber, Bytes, BytesLike, ethers } from 'ethers';\nimport hre from 'hardhat';\nimport { HydraS1AccountboundAttester } from 'types';\nimport { ClaimStruct } from 'types/HydraS1Base';\nimport { HydraS1SimpleAttester, RequestStruct } from 'types/HydraS1SimpleAttester';\n\n/*************************************************/\n/**************    MOCK ACCOUNTS     *************/\n/*************************************************/\n\nexport const generateHydraS1Accounts = async (\n  signers,\n  commitmentMapper\n): Promise<HydraS1Account[]> => {\n  const poseidon = await buildPoseidon();\n  const hydraS1Accounts: HydraS1Account[] = [];\n  for (const signer of signers) {\n    const address = BigNumber.from(signer.address).toHexString();\n    const signature = await signer.signMessage(getOwnershipMsg(address));\n    const secret = BigNumber.from(address);\n    const commitment = poseidon([secret]).toHexString();\n    const { commitmentReceipt } = await commitmentMapper.commit(address, signature, commitment);\n    hydraS1Accounts.push({\n      identifier: address,\n      secret,\n      commitmentReceipt,\n    });\n  }\n  return hydraS1Accounts;\n};\n\n/*************************************************/\n/****************    DATA SOURCE     *************/\n/*************************************************/\n\nexport type GroupData = { [address: string]: number };\n\nexport const generateGroup = (S1Accounts: HydraS1Account[]): GroupData => {\n  const List = {};\n  S1Accounts.forEach((account, index) => {\n    Object.assign(List, {\n      [BigNumber.from(account.identifier).toHexString()]: index + 1,\n    });\n  });\n  return List;\n};\n\nexport const generateGroups = (S1Accounts: HydraS1Account[]): GroupData[] => {\n  const List1 = {};\n  const List2 = {};\n  S1Accounts.forEach((account, index) => {\n    Object.assign(List1, { [BigNumber.from(account.identifier).toHexString()]: index + 1 });\n    Object.assign(List2, { [BigNumber.from(account.identifier).toHexString()]: index + 1000 });\n  });\n  return [List1, List2];\n};\n\nexport type HydraS1SimpleGroup = {\n  data: MerkleTreeData;\n  properties: HydraS1SimpleGroupProperties;\n  id: string;\n};\n\nexport type HydraS1SimpleGroupProperties = {\n  groupIndex: number;\n  generationTimestamp: number;\n  isScore: boolean;\n};\n\nexport type RegistryAccountsMerkle = {\n  accountsTrees: KVMerkleTree[];\n  registryTree: KVMerkleTree;\n};\n\nexport type AttesterGroups = {\n  groups: HydraS1SimpleGroup[];\n  dataFormat: RegistryAccountsMerkle;\n};\n\nexport type generateAttesterGroups = {\n  generationTimestamp?: number;\n  isScore?: boolean;\n};\n\nexport const generateAttesterGroups = async (\n  allList: GroupData[],\n  options?: generateAttesterGroups\n): Promise<AttesterGroups> => {\n  let poseidon = await buildPoseidon();\n\n  /*********************** GENERATE GROUPS *********************/\n\n  const groups: HydraS1SimpleGroup[] = [];\n  let generationTimestamp = Math.round(Date.now() / 1000);\n\n  if (options && options.generationTimestamp) {\n    generationTimestamp = options.generationTimestamp;\n  }\n\n  for (let i = 0; i < allList.length; i++) {\n    const properties = {\n      groupIndex: i,\n      generationTimestamp,\n      isScore: options && options.isScore ? options.isScore : i % 2 == 1,\n    };\n\n    groups.push({\n      data: allList[i],\n      properties,\n      id: generateGroupIdFromProperties(properties).toHexString(),\n    });\n    generationTimestamp++;\n  }\n\n  /************************ FORMAT DATA *********************/\n\n  const accountsTrees: KVMerkleTree[] = [];\n  const registryTreeData: MerkleTreeData = {};\n\n  for (let i = 0; i < groups.length; i++) {\n    let _accountsTree = new KVMerkleTree(groups[i].data, poseidon, ACCOUNTS_TREE_HEIGHT);\n    accountsTrees.push(_accountsTree);\n    registryTreeData[_accountsTree.getRoot().toHexString()] = groups[i].id;\n  }\n\n  const registryTree = new KVMerkleTree(registryTreeData, poseidon, REGISTRY_TREE_HEIGHT);\n\n  return {\n    groups,\n    dataFormat: {\n      accountsTrees,\n      registryTree,\n    },\n  };\n};\n\nexport const generateGroupIdFromProperties = (\n  groupProperties: HydraS1SimpleGroupProperties\n): BigNumber => {\n  return generateGroupIdFromEncodedProperties(encodeGroupProperties(groupProperties));\n};\n\nexport const generateGroupIdFromEncodedProperties = (encodedProperties: string): BigNumber => {\n  return BigNumber.from(ethers.utils.keccak256(encodedProperties)).mod(SNARK_FIELD);\n};\n\nexport const encodeGroupProperties = (groupProperties: HydraS1SimpleGroupProperties): string => {\n  return ethers.utils.defaultAbiCoder.encode(\n    ['uint128', 'uint32', 'bool'],\n    [groupProperties.groupIndex, groupProperties.generationTimestamp, groupProperties.isScore]\n  );\n};\n\nexport const decodeGroupProperties = (\n  encodedProperties: BytesLike\n): HydraS1SimpleGroupProperties => {\n  const [groupIndex, generationTimestamp, isScore] = ethers.utils.defaultAbiCoder.decode(\n    ['uint128', 'uint32', 'bool'],\n    encodedProperties\n  );\n\n  return { groupIndex, generationTimestamp, isScore };\n};\n\n/*************************************************/\n/************    PROVING SCHEME     *************/\n/*************************************************/\n\nexport async function generateExternalNullifier(attesterAddress: string, groupIndex: number) {\n  return BigNumber.from(\n    ethers.utils.keccak256(\n      ethers.utils.defaultAbiCoder.encode(['address', 'uint256'], [attesterAddress, groupIndex])\n    )\n  ).mod(BigNumber.from(SNARK_FIELD));\n}\n\nexport function toBytes(snarkProof: any) {\n  return ethers.utils.defaultAbiCoder.encode(\n    ['uint256[2]', 'uint256[2][2]', 'uint256[2]', 'uint256[10]'],\n    [snarkProof.a, snarkProof.b, snarkProof.c, snarkProof.input]\n  );\n}\n\nexport const packRequestAndProofToBytes = (request: RequestStruct, proof: SnarkProof) => {\n  return ethers.utils.defaultAbiCoder.encode(\n    [\n      'tuple(uint256 groupId, uint256 claimedValue, bytes extraData)',\n      'address destination',\n      'bytes',\n    ],\n    [request.claims[0], request.destination, proof.toBytes()]\n  );\n};\n\nexport const decodeRequestAndProofFromBytes = (data: string) => {\n  return ethers.utils.defaultAbiCoder.decode(\n    ['tuple(uint256, uint256, bytes)', 'address', 'bytes'],\n    data\n  );\n};\n\n/*************************************************\n ************ PROVING DATA GENERATION  ************\n * ***********************************************/\n\nexport type GenerateAttesterGroup = {\n  nbOfGroups: number;\n  generationTimestamp?: number;\n  isScore?: boolean;\n  groupIndex?: number;\n  groups?: HydraS1SimpleGroup[];\n};\n\nexport type ProvingDataStruct = {\n  accountsTreesWithData: { tree: KVMerkleTree; group: HydraS1SimpleGroup }[];\n  registryTree: KVMerkleTree;\n  groups: HydraS1SimpleGroup[];\n  commitmentMapperPubKey: EddsaPublicKey;\n  accounts: HydraS1Account[];\n};\n\nexport const generateProvingData = async (options?: GenerateAttesterGroup) => {\n  const signers: SignerWithAddress[] = await hre.ethers.getSigners();\n\n  const commitmentMapper = await CommitmentMapperTester.generate();\n  const commitmentMapperPubKey = await commitmentMapper.getPubKey();\n\n  const accounts: HydraS1Account[] = await generateHydraS1Accounts(signers, commitmentMapper);\n\n  // create group\n  const groups = options?.groups ?? [];\n  let generationTimestamp = options?.generationTimestamp ?? Math.round(Date.now() / 1000);\n\n  for (let i = 0; i < (options ? options?.nbOfGroups : 1); i++) {\n    const availableGroup = generateGroup(accounts);\n\n    const properties = {\n      groupIndex: groups.length + 1,\n      generationTimestamp,\n      isScore: options?.isScore ?? false,\n    };\n\n    const randomAddress = ethers.utils.hexZeroPad(`0x${Math.floor(Math.random() * 1000000)}`, 20);\n\n    groups.push({\n      data: { ...availableGroup, [randomAddress]: 0 },\n      properties,\n      id: generateGroupIdFromProperties(properties).toHexString(),\n    });\n  }\n\n  // format data\n  const accountsTreesWithData: { tree: KVMerkleTree; group: HydraS1SimpleGroup }[] = [];\n  const registryTreeData: MerkleTreeData = {};\n\n  let poseidon = await buildPoseidon();\n\n  for (let i = 0; i < groups.length; i++) {\n    let _accountsTree = new KVMerkleTree(groups[i].data, poseidon, ACCOUNTS_TREE_HEIGHT);\n    accountsTreesWithData.push({ tree: _accountsTree, group: groups[i] });\n    registryTreeData[_accountsTree.getRoot().toHexString()] = groups[i].id;\n  }\n\n  const registryTree = new KVMerkleTree(registryTreeData, poseidon, REGISTRY_TREE_HEIGHT);\n\n  return {\n    accountsTreesWithData,\n    registryTree,\n    groups,\n    commitmentMapperPubKey,\n    accounts,\n  };\n};\n\n/******************************************\n ************ PROOF GENERATOR **************\n ******************************************/\n\nexport type HydraS1Proof = {\n  claim: ClaimStruct;\n  proofData: Bytes;\n};\n\nexport type AccountsTreeWithData = { tree: KVMerkleTree; group: HydraS1SimpleGroup };\n\nexport type HydraS1ZKPSConstructorArgs = {\n  accountsTreesWithData: AccountsTreeWithData[];\n  registryTree: KVMerkleTree;\n  groups: HydraS1SimpleGroup[];\n  defaultAttester: HydraS1AccountboundAttester | HydraS1SimpleAttester;\n  commitmentMapperPubKey: EddsaPublicKey;\n  chainId: number;\n};\n\nexport type ProofGenerationArgs = {\n  sources?: HydraS1Account[]; // if not provided, will take the first source\n  destination: HydraS1Account;\n  value?: BigNumber;\n  attesterAddress?: string;\n  group?: HydraS1SimpleGroup;\n  availableGroups?: {\n    registryTree: KVMerkleTree;\n    accountsTreesWithData: {\n      tree: KVMerkleTree;\n      group: HydraS1SimpleGroup;\n    }[];\n  };\n};\n\nexport class HydraS1ZKPS {\n  accountsTreesWithData: AccountsTreeWithData[];\n  registryTree: KVMerkleTree;\n  groups: HydraS1SimpleGroup[];\n  defaultAttester: HydraS1AccountboundAttester | HydraS1SimpleAttester;\n  commitmentMapperPubKey: EddsaPublicKey;\n  chainId: number;\n\n  constructor({\n    accountsTreesWithData,\n    registryTree,\n    groups,\n    defaultAttester,\n    commitmentMapperPubKey,\n    chainId,\n  }: HydraS1ZKPSConstructorArgs) {\n    this.accountsTreesWithData = accountsTreesWithData;\n    this.registryTree = registryTree;\n    this.groups = groups;\n    this.defaultAttester = defaultAttester;\n    this.commitmentMapperPubKey = commitmentMapperPubKey;\n    this.chainId = chainId;\n  }\n\n  public async generateProof(proofGenerationArgs: ProofGenerationArgs) {\n    const {\n      source,\n      destination,\n      claimedValue,\n      chainId,\n      accountsTree,\n      externalNullifier,\n      isStrict,\n      registryTree,\n      accountsTreeWithData,\n    } = await this.getVariables(proofGenerationArgs);\n\n    const prover = new HydraS1Prover(registryTree, this.commitmentMapperPubKey);\n\n    const userParams = {\n      source,\n      destination,\n      claimedValue,\n      chainId,\n      accountsTree,\n      externalNullifier,\n      isStrict,\n    };\n\n    const inputs = await prover.generateInputs(userParams);\n\n    const proof = await prover.generateSnarkProof(userParams);\n\n    const claim = {\n      groupId: accountsTreeWithData.group.id,\n      claimedValue: claimedValue,\n      extraData: encodeGroupProperties({\n        ...accountsTreeWithData.group.properties,\n      }),\n    };\n\n    return {\n      request: {\n        claims: [claim],\n        destination: BigNumber.from(destination.identifier).toHexString(),\n      },\n      proofData: proof.toBytes(),\n      inputs,\n      userParams,\n      nullifier: await this.getNullifier(proofGenerationArgs),\n    };\n  }\n\n  public async getNullifier(proofGenerationArgs: ProofGenerationArgs) {\n    const {\n      source,\n      destination,\n      claimedValue,\n      chainId,\n      accountsTree,\n      externalNullifier,\n      isStrict,\n      registryTree,\n    } = await this.getVariables(proofGenerationArgs);\n\n    const prover = new HydraS1Prover(registryTree, this.commitmentMapperPubKey);\n\n    const userParams = {\n      source,\n      destination,\n      claimedValue,\n      chainId,\n      accountsTree,\n      externalNullifier,\n      isStrict,\n    };\n\n    const inputs = await prover.generateInputs(userParams);\n\n    return BigNumber.from(inputs.publicInputs.nullifier);\n  }\n\n  public async getVariables(proofGenerationArgs: ProofGenerationArgs) {\n    const availableGroups = proofGenerationArgs.availableGroups ?? {\n      registryTree: this.registryTree,\n      accountsTreesWithData: this.accountsTreesWithData,\n    };\n    const group = proofGenerationArgs.group ?? this.groups[0];\n    const attesterAddress = proofGenerationArgs.attesterAddress ?? this.defaultAttester.address;\n\n    const destination = proofGenerationArgs.destination;\n    const source = proofGenerationArgs?.sources ? proofGenerationArgs.sources[0] : destination;\n\n    const claimedValue = group.data[BigNumber.from(source.identifier).toHexString()];\n\n    const chainId = this.chainId;\n    const externalNullifier = await generateExternalNullifier(\n      attesterAddress,\n      group.properties.groupIndex\n    );\n    const isStrict = !group.properties.isScore;\n\n    let accountsTreeWithData: { tree: KVMerkleTree; group: HydraS1SimpleGroup } =\n      availableGroups.accountsTreesWithData.filter((data) => data.group.id === group.id)[0];\n\n    if (accountsTreeWithData === undefined) throw new Error('Group not found');\n\n    const accountsTree = accountsTreeWithData.tree;\n\n    const registryTree = availableGroups.registryTree;\n\n    return {\n      source,\n      destination,\n      claimedValue,\n      chainId,\n      accountsTree,\n      externalNullifier,\n      isStrict,\n      registryTree,\n      accountsTreeWithData,\n    };\n  }\n}\n"
  },
  {
    "path": "test/utils/index.ts",
    "content": "export * from './setup';\nexport * from './expectEvent';\nexport * from './evm';\nexport * from './attestation-logic';\nexport * from './hydra-s1';\nexport * from './hydra-s1-accountbound';\n\n"
  },
  {
    "path": "test/utils/pythia-1.ts",
    "content": "import {\n  EddsaAccount,\n  EddsaPublicKey,\n  EddsaSignature,\n  buildPoseidon,\n  SNARK_FIELD,\n} from '@sismo-core/crypto';\nimport { BigNumber, BigNumberish, ethers } from 'ethers';\n\nexport type Pythia1Group = {\n  properties: Pythia1GroupProperties;\n  id: string;\n};\n\nexport type Pythia1GroupProperties = {\n  internalCollectionId: number;\n  isScore: boolean;\n};\n\nexport const generatePythia1Group = ({ internalCollectionId, isScore }): Pythia1Group => {\n  const properties: Pythia1GroupProperties = {\n    internalCollectionId,\n    isScore,\n  };\n\n  return {\n    id: generatePythia1GroupIdFromProperties(properties).toHexString(),\n    properties,\n  };\n};\n\nexport const computeNullifier = async (\n  secret: BigNumberish,\n  externalNullifier: BigNumberish\n): Promise<BigNumber> => {\n  const poseidon = await buildPoseidon();\n  return poseidon([secret, externalNullifier]);\n};\n\nexport class CommitmentSignerTester {\n  private seed: BigNumberish;\n\n  constructor(seed: BigNumberish = '0x123321') {\n    this.seed = seed;\n  }\n\n  async getCommitmentReceipt(\n    commitment: BigNumberish,\n    value: BigNumberish,\n    groupId: BigNumberish\n  ): Promise<EddsaSignature> {\n    const poseidon = await buildPoseidon();\n    return (await this._getEddsaAccount()).sign(poseidon([commitment, value, groupId]));\n  }\n\n  async getPublicKey(): Promise<EddsaPublicKey> {\n    return (await this._getEddsaAccount()).getPubKey();\n  }\n\n  private async _getEddsaAccount(): Promise<EddsaAccount> {\n    const eddsaAccount = await EddsaAccount.generateFromSeed(this.seed);\n    return eddsaAccount;\n  }\n}\n\nexport const generatePythia1GroupIdFromProperties = (\n  groupProperties: Pythia1GroupProperties\n): BigNumber => {\n  return generatePythia1GroupIdFromEncodedProperties(encodePythia1GroupProperties(groupProperties));\n};\n\nexport const generatePythia1GroupIdFromEncodedProperties = (\n  encodedProperties: string\n): BigNumber => {\n  return BigNumber.from(ethers.utils.keccak256(encodedProperties)).mod(SNARK_FIELD);\n};\n\nexport const encodePythia1GroupProperties = (groupProperties: Pythia1GroupProperties): string => {\n  return ethers.utils.defaultAbiCoder.encode(\n    ['uint128', 'bool'],\n    [groupProperties.internalCollectionId, groupProperties.isScore]\n  );\n};\n"
  },
  {
    "path": "test/utils/setup.ts",
    "content": "import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers';\nimport { BigNumber } from 'ethers';\nimport { EddsaSignature, Poseidon } from '@sismo-core/hydra-s1';\nimport { CommitmentMapperTester, getOwnershipMsg } from '@sismo-core/commitment-mapper-tester-js';\n\nexport async function getMockedAccounts(\n  signers: SignerWithAddress[],\n  commitmentMapper: CommitmentMapperTester,\n  poseidon: Poseidon\n): Promise<HydraS1MockedAccount[]> {\n  const hydraS1Accounts: HydraS1MockedAccount[] = [];\n  let i = 0;\n  for (const signer of signers) {\n    const signature = await signer.signMessage(getOwnershipMsg(signer.address));\n    const secret = BigNumber.from(signer.address);\n    const group1Value = i;\n    const group2Value = 20000 - i;\n    const group3Value = 40000 - group2Value;\n    const commitment = poseidon([secret]).toHexString();\n    const { commitmentReceipt } = await commitmentMapper.commit(\n      signer.address,\n      signature,\n      commitment\n    );\n    hydraS1Accounts.push({\n      signer,\n      secret,\n      commitmentReceipt,\n      group1Value,\n      group2Value,\n      group3Value,\n    });\n    i++;\n  }\n  return hydraS1Accounts;\n}\n\nexport type HydraS1MockedAccount = {\n  signer: SignerWithAddress;\n  secret: BigNumber;\n  commitmentReceipt: EddsaSignature;\n  group1Value: number;\n  group2Value: number;\n  group3Value: number;\n};\n"
  },
  {
    "path": "test/utils/test-helpers.ts",
    "content": "import { BigNumber, BigNumberish, Contract, ContractTransaction } from 'ethers';\nimport hre from 'hardhat';\nimport {\n  AddressesProvider,\n  AttestationsRegistry,\n  AvailableRootsRegistry,\n  Badges,\n  HydraS1AccountboundAttester,\n} from 'types';\nimport { expect } from 'chai';\nimport { decodeGroupProperties, HydraS1ZKPS, ProofGenerationArgs } from './hydra-s1';\nimport { RequestStruct } from 'types/HydraS1Base';\nimport { DeployOptions } from 'tasks/deploy-tasks/utils';\nimport { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers';\nimport { KVMerkleTree } from 'hydra-s1-previous';\nimport { getBlockTimestamp } from '../../test/utils/evm';\nimport { SISMO_ADDRESSES_PROVIDER_CONTRACT_ADDRESS } from '../../tasks/deploy-tasks/deployments-config';\nimport { HardhatRuntimeEnvironment } from 'hardhat/types';\n\n/****************************************\n ********** Helpers for testing *********\n ****************************************/\n\nexport const deployCoreContracts = async (deployer: SignerWithAddress, options: DeployOptions) => {\n  const {\n    attestationsRegistry,\n    hydraS1AccountboundAttester,\n    hydraS1Verifier,\n    front,\n    badges,\n    commitmentMapperRegistry,\n    availableRootsRegistry,\n  } = await hre.run('0-deploy-core-and-hydra-s1-simple-and-accountbound-and-pythia1', {\n    options,\n  });\n\n  const { sismoAddressesProvider } = await hre.run('deploy-sismo-addresses-provider', {\n    owner: deployer.address,\n    badges: badges.address,\n    attestationsRegistry: attestationsRegistry.address,\n    front: front.address,\n    hydraS1AccountboundAttester: hydraS1AccountboundAttester.address,\n    commitmentMapperRegistry: commitmentMapperRegistry.address,\n    availableRootsRegistry: availableRootsRegistry.address,\n    hydraS1Verifier: hydraS1Verifier.address,\n    options,\n  });\n\n  return {\n    attestationsRegistry,\n    hydraS1AccountboundAttester,\n    hydraS1Verifier,\n    front,\n    badges,\n    commitmentMapperRegistry,\n    availableRootsRegistry,\n    sismoAddressesProvider,\n  };\n};\n\nexport const registerRootForAttester = async (\n  availableRootsRegistry: AvailableRootsRegistry,\n  attester: HydraS1AccountboundAttester,\n  registryTree: KVMerkleTree\n) => {\n  const root = registryTree.getRoot();\n  await availableRootsRegistry.registerRootForAttester(attester.address, root);\n};\n\nexport const checkAccountHoldsBadge = async (\n  accountAddress: string,\n  badgeId: BigNumberish,\n  accountIsExpectedToHold = true\n) => {\n  const badgesContract = (await getContract(hre, 'Badges')) as Badges;\n\n  if (accountIsExpectedToHold) {\n    expect((await badgesContract.balanceOf(accountAddress, badgeId)) > BigNumber.from(0)).to.be\n      .true;\n  } else {\n    expect((await badgesContract.balanceOf(accountAddress, badgeId)) > BigNumber.from(0)).to.be\n      .false;\n  }\n};\n\nexport const checkAttestationIsWellRegistered = async ({\n  request,\n  nullifier,\n  expectedBurnCount,\n  tx,\n  attester,\n}: {\n  request: RequestStruct;\n  nullifier: BigNumberish;\n  tx: ContractTransaction;\n  expectedBurnCount?: number;\n  attester?: HydraS1AccountboundAttester;\n}) => {\n  const attestationsRegistry = (await getContract(\n    hre,\n    'AttestationsRegistry'\n  )) as AttestationsRegistry;\n\n  attester =\n    attester ??\n    ((await getContract(hre, 'HydraS1AccountboundAttester')) as HydraS1AccountboundAttester);\n\n  const badgeId = (await attester.AUTHORIZED_COLLECTION_ID_FIRST()).add(\n    decodeGroupProperties(request.claims[0].extraData).groupIndex\n  );\n\n  const extraData: string = await attestationsRegistry.getAttestationExtraData(\n    badgeId,\n    request.destination\n  );\n\n  // 0 - Checks that the transaction emitted the event\n  await expect(tx)\n    .to.emit(attester, 'AttestationGenerated')\n    .withArgs([\n      badgeId,\n      request.destination,\n      attester.address,\n      request.claims[0].claimedValue,\n      decodeGroupProperties(request.claims[0].extraData).generationTimestamp,\n      extraData,\n    ]);\n\n  // 1 - Checks that the provided nullifier was successfully recorded in the attester\n  expect(await attester.getDestinationOfNullifier(BigNumber.from(nullifier))).to.equal(\n    request.destination\n  );\n\n  expect(await attester.getNullifierBurnCount(BigNumber.from(nullifier))).to.be.eql(\n    expectedBurnCount ?? 0\n  );\n\n  expect(await attester.getNullifierCooldownStart(BigNumber.from(nullifier))).to.be.eql(\n    expectedBurnCount ?? 0 >= 1 ? await getBlockTimestamp() : 0\n  );\n\n  // 2 - Checks that the attester recorded the attestation in the registry\n  expect(await attestationsRegistry.hasAttestation(badgeId, request.destination)).to.be.true;\n};\n\nexport const mintBadge = async ({\n  sources,\n  destination,\n  group,\n  value,\n  provingScheme,\n  expectedBurnCount,\n  isDestinationAlreadyHoldingTheBadge = false,\n}: ProofGenerationArgs & {\n  provingScheme: HydraS1ZKPS;\n  expectedBurnCount?: number;\n  isDestinationAlreadyHoldingTheBadge?: boolean;\n}) => {\n  sources = sources ?? [destination];\n\n  const badgeId = (await provingScheme.defaultAttester.AUTHORIZED_COLLECTION_ID_FIRST()).add(\n    group?.properties.groupIndex ?? provingScheme.groups[0].properties.groupIndex\n  );\n\n  await checkAccountHoldsBadge(\n    BigNumber.from(destination.identifier).toHexString(),\n    badgeId,\n    isDestinationAlreadyHoldingTheBadge\n  );\n\n  const { request, proofData, inputs } = await provingScheme.generateProof({\n    sources,\n    destination,\n    group,\n    value,\n  });\n\n  const generateAttestationsTransaction = await provingScheme.defaultAttester.generateAttestations(\n    request,\n    proofData\n  );\n\n  await checkAttestationIsWellRegistered({\n    request,\n    nullifier: BigNumber.from(inputs.publicInputs.nullifier),\n    expectedBurnCount: expectedBurnCount ?? 0,\n    tx: generateAttestationsTransaction,\n  });\n\n  await checkAccountHoldsBadge(BigNumber.from(destination.identifier).toHexString(), badgeId, true);\n\n  return { request, proofData, inputs, nullifier: BigNumber.from(inputs.publicInputs.nullifier) };\n};\n\nexport const computeBadgeIds = async (provingScheme: HydraS1ZKPS) => {\n  const badgeIds: BigNumber[] = [];\n  for (let i = 0; i < provingScheme.groups.length; i++) {\n    const group = provingScheme.groups[i];\n    badgeIds.push(\n      (await provingScheme.defaultAttester.AUTHORIZED_COLLECTION_ID_FIRST()).add(\n        group.properties.groupIndex\n      )\n    );\n  }\n\n  return badgeIds;\n};\n\nexport const getAddressesProviderContract = async (hre: HardhatRuntimeEnvironment) => {\n  const code = await hre.network.provider.send('eth_getCode', [\n    SISMO_ADDRESSES_PROVIDER_CONTRACT_ADDRESS,\n  ]);\n\n  if (code === '0x') {\n    throw new Error('Sismo addresses provider not deployed');\n  }\n\n  const AddressesProvider = await hre.ethers.getContractFactory('AddressesProvider');\n  return AddressesProvider.attach(SISMO_ADDRESSES_PROVIDER_CONTRACT_ADDRESS) as AddressesProvider;\n};\n\nexport const getContract = async (\n  hre: HardhatRuntimeEnvironment,\n  contractName: string\n): Promise<Contract> => {\n  const sismoAddressesProvider = await getAddressesProviderContract(hre);\n  const contract = await hre.ethers.getContractAt(\n    contractName,\n    await sismoAddressesProvider['get(string)'](contractName)\n  );\n\n  return contract;\n};\n"
  },
  {
    "path": "tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"target\": \"es2018\",\n    \"module\": \"commonjs\",\n    \"strict\": true,\n    \"esModuleInterop\": true,\n    \"declaration\": true,\n    \"resolveJsonModule\": true,\n    \"baseUrl\": \"./\",\n    \"noImplicitAny\": false\n  },\n  \"include\": [\"./scripts\", \"./tasks\", \"./test\", \"./common\"],\n  \"exclude\": [\"node_modules\", \"./types\"],\n  \"files\": [\"./hardhat.config.ts\"]\n}"
  },
  {
    "path": "utils/constants.ts",
    "content": "import { ethers, BytesLike } from 'ethers';\nimport { toUtf8Bytes } from 'ethers/lib/utils';\n\nexport const EVENT_TRIGGERER_ROLE: BytesLike = ethers.utils.keccak256(\n  toUtf8Bytes('EVENT_TRIGGERER_ROLE')\n);\n"
  },
  {
    "path": "utils/index.ts",
    "content": "export * from './constants';\nexport * from './proxy';\n"
  },
  {
    "path": "utils/proxy.ts",
    "content": "import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers';\nimport { BaseContract, utils } from 'ethers';\n\nexport const IMPLEMENTATION_SLOT: string =\n  '0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc';\n\nexport const getImplementation = async (proxyContract: BaseContract): Promise<string> => {\n  return utils.defaultAbiCoder.decode(\n    ['address'],\n    (await proxyContract.provider?.getStorageAt(proxyContract.address, IMPLEMENTATION_SLOT)) || ''\n  )[0];\n};\n"
  },
  {
    "path": "utils/singletonFactory.ts",
    "content": "export const singletonFactory = {\n  // mainnet\n  1: {\n    gasPrice: 100000000000,\n    gasLimit: 100000,\n    signerAddress: '0xBa68986f673c9193BB79eA0d21990225d464bb5C',\n    transaction:\n      '0xf8a58085174876e800830186a08080b853604580600e600039806000f350fe7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe03601600081602082378035828234f58015156039578182fd5b8082525050506014600cf326a0b807b1020a20449a1d962455d100c13cea90fe72dd21c1abca7a9680c23de3ffa0725dfea391175a966ee02c6ba78a42519040bb9caef32b91522097d2601e9c00',\n    address: '0xC4c11B14e9D876B031c1c7e05efE44088341f35B',\n  },\n  // goerli\n  5: {\n    gasPrice: 100000000000,\n    gasLimit: 100000,\n    signerAddress: '0xBa68986f673c9193BB79eA0d21990225d464bb5C',\n    transaction:\n      '0xf8a58085174876e800830186a08080b853604580600e600039806000f350fe7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe03601600081602082378035828234f58015156039578182fd5b8082525050506014600cf32ea0bf23472b7c27cefcbfaf2241930df7a61e0e52f7e2af414c071d3cc6d92874a8a04d4315d5e555f794ef107a68765de376b83ee697658b7dacd578b2ec5f721183',\n    address: '0xC4c11B14e9D876B031c1c7e05efE44088341f35B',\n  },\n  // polygon\n  137: {\n    gasPrice: 100000000000,\n    gasLimit: 100000,\n    signerAddress: '0xBa68986f673c9193BB79eA0d21990225d464bb5C',\n    transaction:\n      '0xf8a78085174876e800830186a08080b853604580600e600039806000f350fe7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe03601600081602082378035828234f58015156039578182fd5b8082525050506014600cf3820135a084ea92419a30b2851e105567772f3346d4ee23ab63a733f82c13a424604cbfdaa06716488c485f03825d5b5b52d982f130199767319e11453d7cacc5053818c23b',\n    address: '0xC4c11B14e9D876B031c1c7e05efE44088341f35B',\n  },\n  // local\n  31337: {\n    gasPrice: 100000000000,\n    gasLimit: 100000,\n    signerAddress: '0xBa68986f673c9193BB79eA0d21990225d464bb5C',\n    transaction:\n      '0xf8a78085174876e800830186a08080b853604580600e600039806000f350fe7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe03601600081602082378035828234f58015156039578182fd5b8082525050506014600cf382f4f5a019264dff42982fbff5b815e16a83fff4524568dd0e23a09c4d37cd86e6890f71a07db39e99070faa25eb46904f812cb12feadddf186e58a69ce7688b04a9ee2a0a',\n    address: '0xC4c11B14e9D876B031c1c7e05efE44088341f35B',\n  },\n  // mumbai\n  80001: {\n    gasPrice: 100000000000,\n    gasLimit: 100000,\n    signerAddress: '0xBa68986f673c9193BB79eA0d21990225d464bb5C',\n    transaction:\n      '0xf8a88085174876e800830186a08080b853604580600e600039806000f350fe7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe03601600081602082378035828234f58015156039578182fd5b8082525050506014600cf383027126a0b23aa3a7cf3654c8f2dc3ce3c6d37ff504a726085f835a9f4d32d61e865e261ea017add99e70915725b41244e4feefc854f5c17da90f4e1ac6fb994040ae5af242',\n    address: '0xC4c11B14e9D876B031c1c7e05efE44088341f35B',\n  },\n};\n"
  }
]