[
  {
    "path": ".eslintignore",
    "content": ".eslintrc.js\nnode_modules\nsrc/gen\n"
  },
  {
    "path": ".eslintrc.js",
    "content": "const { off } = require(\"process\")\n\nmodule.exports = {\n  parser: '@typescript-eslint/parser',\n  parserOptions: {\n    ecmaVersion: 2018,\n    sourceType: 'module'\n  },\n\n  extends: [\n    'plugin:@typescript-eslint/recommended',\n    'plugin:import/errors',\n    'plugin:import/warnings',\n    'plugin:import/typescript',\n    'prettier'\n  ],\n\n  rules: {\n    '@typescript-eslint/no-unused-vars': 'off',\n    '@typescript-eslint/no-explicit-any': 'off',\n    '@typescript-eslint/no-non-null-assertion': 'off',\n    '@typescript-eslint/explicit-module-boundary-types': 'off',\n    '@typescript-eslint/ban-types': 'off',\n    '@typescript-eslint/ban-ts-comment': 'off',\n    '@typescript-eslint/no-empty-function': 'off',\n    '@typescript-eslint/no-inferrable-types': 'off',\n    '@typescript-eslint/no-var-requires': 'off',\n    '@typescript-eslint/no-non-null-asserted-optional-chain': 'off',\n\n    'prefer-spread': 'off',\n    'prefer-const': 'off',\n\n    'import/no-unresolved': 'off',\n    // 'import/no-default-export': 2,\n    'import/no-named-as-default-member': 'off',\n\n  }\n}\n"
  },
  {
    "path": ".gitattributes",
    "content": "*.sol linguist-language=Solidity\n"
  },
  {
    "path": ".github/actions/install-dependencies/action.yml",
    "content": "name: Setup Node and PNPM dependencies\n\nruns:\n  using: 'composite'\n\n  steps:\n    - name: Setup Node\n      uses: actions/setup-node@v4\n      with:\n        node-version: 20\n\n    - name: Setup PNPM\n      uses: pnpm/action-setup@v3\n      with:\n        version: 9\n        run_install: false\n\n    - name: Get pnpm store directory\n      id: pnpm-cache\n      shell: bash\n      run: |\n        echo \"STORE_PATH=$(pnpm store path)\" >> $GITHUB_OUTPUT\n\n    - name: Setup pnpm cache\n      uses: actions/cache@v4\n      with:\n        path: |\n          ${{ steps.pnpm-cache.outputs.STORE_PATH }}\n        key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}\n        restore-keys: |\n          ${{ runner.os }}-pnpm-store-\n\n    - name: Install dependencies\n      shell: bash\n      run: pnpm install --frozen-lockfile\n      if: ${{ steps.pnpm-cache.outputs.cache-hit != 'true' }}\n"
  },
  {
    "path": ".github/workflows/ci.yml",
    "content": "on: [push]\n\nname: ci\n\njobs:\n  benchmark:\n    name: Benchmark\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v4\n      - uses: ./.github/actions/install-dependencies\n      - run: pnpm build\n      - run: pnpm benchmark\n\n  lint-ts:\n    name: Typescript lint\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v4\n      - uses: ./.github/actions/install-dependencies\n      - run: pnpm lint:ts\n\n  lint-sol:\n    name: Solidity lint\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v4\n      - uses: ./.github/actions/install-dependencies\n      - run: pnpm lint:sol\n\n  test:\n    name: Test contracts\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v4\n      - uses: ./.github/actions/install-dependencies\n      - run: pnpm build\n      - run: pnpm test\n\n  coverage:\n    name: Coverage\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v4\n      - uses: ./.github/actions/install-dependencies\n      - run: pnpm coverage || true\n      - name: Coveralls\n        uses: coverallsapp/github-action@master\n        with:\n          github-token: ${{ secrets.GITHUB_TOKEN }}\n\n  huff-tests:\n    name: Huff tests\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v4\n        with:\n          submodules: recursive\n\n      - name: Install Huff\n        uses: huff-language/huff-toolchain@v3\n        with:\n          version: nightly\n\n      - name: Run tests\n        run: bash ./run_huff_tests.sh\n\n  foundry-tests:\n    name: Foundry tests\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v4\n        with:\n          submodules: recursive\n\n      - name: Install Foundry\n        uses: foundry-rs/foundry-toolchain@v1\n        with:\n          version: nightly\n\n      - name: Install Huff\n        uses: huff-language/huff-toolchain@v3\n        with:\n          version: nightly\n\n      - name: Run tests\n        run: FOUNDRY_FUZZ_RUNS=2048 MAX_ARRAY_LEN=32 forge test -vvv\n\n  foundry-tests-long-arrays:\n    name: Foundry tests (long arrays)\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v4\n        with:\n          submodules: recursive\n\n      - name: Install Foundry\n        uses: foundry-rs/foundry-toolchain@v1\n        with:\n          version: nightly\n\n      - name: Install Huff\n        uses: huff-language/huff-toolchain@v3\n        with:\n          version: nightly\n\n      - name: Run tests\n        run: FOUNDRY_FUZZ_RUNS=1024 forge test -vvv\n"
  },
  {
    "path": ".gitignore",
    "content": ".vscode\n.devcontainer\nnode_modules/\n\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\nlerna-debug.log*\n\n*.js.map\nbuild\ncoverage\ncoverage.json\n\nconfig/*.env\n\nartifacts\nfoundry_artifacts\n\ncache\ngen\n"
  },
  {
    "path": ".gitmodules",
    "content": "[submodule \"lib/forge-std\"]\n\tpath = lib/forge-std\n\turl = https://github.com/foundry-rs/forge-std\n[submodule \"lib/foundry-huff\"]\n\tpath = lib/foundry-huff\n\turl = https://github.com/huff-language/foundry-huff"
  },
  {
    "path": ".husky/pre-commit",
    "content": "pnpm test\n"
  },
  {
    "path": ".prettierrc",
    "content": "{\n  \"tabWidth\": 2,\n  \"useTabs\": false,\n  \"semi\": false,\n  \"singleQuote\": true,\n  \"trailingComma\": \"none\",\n  \"arrowParens\": \"avoid\",\n  \"printWidth\": 130\n}\n"
  },
  {
    "path": ".solcover.js",
    "content": "module.exports = {\n  skipFiles: ['mocks', 'migrations']\n}\n"
  },
  {
    "path": ".solhint.json",
    "content": "{\n  \"extends\": \"solhint:recommended\",\n  \"rules\": {\n    \"quotes\": \"off\",\n    \"const-name-snakecase\": \"off\",\n    \"contract-name-camelcase\": \"off\",\n    \"event-name-camelcase\": \"off\",\n    \"func-name-mixedcase\": \"off\",\n    \"func-param-name-mixedcase\": \"off\",\n    \"modifier-name-mixedcase\": \"off\",\n    \"private-vars-leading-underscore\": \"off\",\n    \"use-forbidden-name\": \"off\",\n    \"var-name-mixedcase\": \"off\",\n    \"func-order\": \"off\",\n    \"imports-on-top\": \"off\",\n    \"ordering\": \"off\",\n    \"no-global-import\": \"off\",\n    \"no-unused-vars\": \"error\",\n    \"not-rely-on-time\": \"off\",\n    \"no-inline-assembly\": \"off\",\n    \"visibility-modifier-order\": \"off\",\n    \"compiler-version\": [\"error\", \"0.8.18\"],\n    \"func-visibility\": [\"warn\", {\"ignoreConstructors\":true}],\n    \"reason-string\": [\"warn\", {\"maxLength\": 96}]\n  }\n}\n"
  },
  {
    "path": "LICENSE",
    "content": "   Copyright (c) 2017-present Horizon Blockchain Games Inc.\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n\n       http://www.apache.org/licenses/LICENSE-2.0\n\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n\n\n   ------------------------------------------------------------------------\n\n\n                                 Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS\n\n   APPENDIX: How to apply the Apache License to your work.\n\n      To apply the Apache License to your work, attach the following\n      boilerplate notice, with the fields enclosed by brackets \"[]\"\n      replaced with your own identifying information. (Don't include\n      the brackets!)  The text should be enclosed in the appropriate\n      comment syntax for the file format. We also recommend that a\n      file or class name and description of purpose be included on the\n      same \"printed page\" as the copyright notice for easier\n      identification within third-party archives.\n\n   Copyright [yyyy] [name of copyright owner]\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n\n       http://www.apache.org/licenses/LICENSE-2.0\n\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n"
  },
  {
    "path": "README.md",
    "content": "# Sequence Smart Wallet Contracts\n\nEthereum contracts for the Sequence Smart Wallet at [https://sequence.app](https://sequence.app).\n\nFor more information, visit [https://sequence.build](https://sequence.build)\n\n## Usage\n\nPlease visit [https://sequence.app](https://sequence.app) to access the Sequence Wallet via your Web Browser, or\ndownload \"Sequence Wallet\" from the respective Apple/Google stores.\n\nYou may also access, interface, or develop your own Sequence Wallet via [sequence.js](https://github.com/0xsequence/sequence.js). The\nsequence.js library offers a full open source library to interact, create, deploy and manage a Sequence Smart Wallet Account,\nas defined by the contracts in this repository. Also see [go-sequence](https://github.com/0xsequence/go-sequence) for an implementation\nin Go.\n\n## Connecting your Dapp with Sequence Wallet\n\nIf you wish to use Sequence Wallet in your Dapp, simply use [sequence.js](https://github.com/0xsequence/sequence.js). Sequence.js\nis an Ethereum client library built on [ethers.js](https://github.com/ethers-io/ethers.js), that provides an additional\nSequence Smart Wallet Signer.\n\nPlease refer to the [sequence.js](https://github.com/0xsequence/sequence.js) repository for usage instructions.\n\n# Developing a Custom Wallet UI for Sequence (Advanced!)\n\nIf you wish to use the Sequence Wallet Contracts `@0xsequence/wallet-contracts` directly:\n\n1. Install the contracts: `pnpm add @0xsequence/wallet-contracts` or `npm install @0xsequence/wallet-contracts`\n2. Install the Sequence Wallet libraries: `pnpm add @0xsequence/wallet` or `npm install @0xsequence/wallet`. You can view the source,\n   of the [wallet libraries](https://github.com/0xsequence/sequence.js/tree/master/packages/wallet), and review the\n   [Sequence tests](https://github.com/0xsequence/sequence.js/tree/master/packages/0xsequence) for sample usage.\n\n**NOTE:** this integration is only needed if you want low-level access to the Sequence Wallet contracts, such as if you'd building\nyour own custom wallet, or perhaps a CLI tool for managing your wallet.\n\n## Security Review\n\n`@0xsequence/wallet-contracts` has been audited by independent parties.\n\n### V2 Audits\n\n- [Consensys Diligence](https://github.com/0xsequence/wallet-contracts/blob/master/audits/v2/consensys-horizon-sequence-wallet-audit-2023-02.pdf) - February 2023\n- [Zellic](https://github.com/0xsequence/wallet-contracts/raw/master/audits/Quantstamp_Arcadeum_Report_Final.pdf) - March 2023\n\n### V1 Audits\n\n- [Consensys Diligence](https://github.com/0xsequence/wallet-contracts/blob/master/audits/v1/Consensys_Diligence.md) - May 2020\n- [Quantstamp - initial audit](https://github.com/0xsequence/wallet-contracts/raw/master/audits/v1/Quantstamp_Arcadeum_Report_Final.pdf) - July 2020\n- [Quantstamp - audit of new capability, nested Sequence signers](https://github.com/0xsequence/wallet-contracts/raw/master/audits/v1/sequence_quantstamp_audit_feb_2021.pdf) - February 2021\n\n## License\n\nCopyright (c) 2017-present [Horizon Blockchain Games Inc](https://horizon.io).\n\nLicensed under [Apache-2.0](https://github.com/0xsequence/erc-1155/blob/master/LICENSE)\n"
  },
  {
    "path": "audits/v1/Consensys_Diligence.md",
    "content": "# Consensys Diligence report May 2020\n\n[View full report](https://diligence.consensys.net/audits/private/cnhjwtpa-horizon-wallet/)\n\nPlease note, Sequence Wallet was formerly known as \"Arcadeum Wallet\". Any references of \"Arcadeum\"\nare synonymous with \"Sequence\", and this repository.\n\n"
  },
  {
    "path": "config/PROD.env.sample",
    "content": "ETH_MNEMONIC=\"\"\nINFURA_API_KEY=\"\"\nETHERSCAN=\"\"\n"
  },
  {
    "path": "contracts/Factory.sol",
    "content": "// SPDX-License-Identifier: Apache-2.0\npragma solidity 0.8.18;\n\nimport \"./Wallet.sol\";\n\n\ncontract Factory {\n  error DeployFailed(address _mainModule, bytes32 _salt);\n\n  /**\n   * @notice Will deploy a new wallet instance\n   * @param _mainModule Address of the main module to be used by the wallet\n   * @param _salt Salt used to generate the wallet, which is the imageHash\n   *       of the wallet's configuration.\n   * @dev It is recommended to not have more than 200 signers as opcode repricing\n   *      could make transactions impossible to execute as all the signers must be\n   *      passed for each transaction.\n   */\n  function deploy(address _mainModule, bytes32 _salt) public payable returns (address _contract) {\n    bytes memory code = abi.encodePacked(Wallet.creationCode, uint256(uint160(_mainModule)));\n    assembly { _contract := create2(callvalue(), add(code, 32), mload(code), _salt) }\n    if (_contract == address(0)) revert DeployFailed(_mainModule, _salt);\n  }\n}\n"
  },
  {
    "path": "contracts/Wallet.sol",
    "content": "// SPDX-License-Identifier: Apache-2.0\npragma solidity 0.8.18;\n\n/**\n    Minimal upgradeable proxy implementation, delegates all calls to the address\n    defined by the storage slot matching the wallet address.\n\n    Inspired by EIP-1167 Implementation (https://eips.ethereum.org/EIPS/eip-1167)\n\n    deployed code:\n\n        0x00    0x36         0x36      CALLDATASIZE      cds\n        0x01    0x3d         0x3d      RETURNDATASIZE    0 cds\n        0x02    0x3d         0x3d      RETURNDATASIZE    0 0 cds\n        0x03    0x37         0x37      CALLDATACOPY\n        0x04    0x3d         0x3d      RETURNDATASIZE    0\n        0x05    0x3d         0x3d      RETURNDATASIZE    0 0\n        0x06    0x3d         0x3d      RETURNDATASIZE    0 0 0\n        0x07    0x36         0x36      CALLDATASIZE      cds 0 0 0\n        0x08    0x3d         0x3d      RETURNDATASIZE    0 cds 0 0 0\n        0x09    0x30         0x30      ADDRESS           addr 0 cds 0 0 0\n        0x0A    0x54         0x54      SLOAD             imp 0 cds 0 0 0\n        0x0B    0x5a         0x5a      GAS               gas imp 0 cds 0 0 0\n        0x0C    0xf4         0xf4      DELEGATECALL      suc 0\n        0x0D    0x3d         0x3d      RETURNDATASIZE    rds suc 0\n        0x0E    0x82         0x82      DUP3              0 rds suc 0\n        0x0F    0x80         0x80      DUP1              0 0 rds suc 0\n        0x10    0x3e         0x3e      RETURNDATACOPY    suc 0\n        0x11    0x90         0x90      SWAP1             0 suc\n        0x12    0x3d         0x3d      RETURNDATASIZE    rds 0 suc\n        0x13    0x91         0x91      SWAP2             suc 0 rds\n        0x14    0x60 0x18    0x6018    PUSH1             0x18 suc 0 rds\n    /-- 0x16    0x57         0x57      JUMPI             0 rds\n    |   0x17    0xfd         0xfd      REVERT\n    \\-> 0x18    0x5b         0x5b      JUMPDEST          0 rds\n        0x19    0xf3         0xf3      RETURN\n\n    flat deployed code: 0x363d3d373d3d3d363d30545af43d82803e903d91601857fd5bf3\n\n    deploy function:\n\n        0x00    0x60 0x3a    0x603a    PUSH1             0x3a\n        0x02    0x60 0x0e    0x600e    PUSH1             0x0e 0x3a\n        0x04    0x3d         0x3d      RETURNDATASIZE    0 0x0e 0x3a\n        0x05    0x39         0x39      CODECOPY\n        0x06    0x60 0x1a    0x601a    PUSH1             0x1a\n        0x08    0x80         0x80      DUP1              0x1a 0x1a\n        0x09    0x51         0x51      MLOAD             imp 0x1a\n        0x0A    0x30         0x30      ADDRESS           addr imp 0x1a\n        0x0B    0x55         0x55      SSTORE            0x1a\n        0x0C    0x3d         0x3d      RETURNDATASIZE    0 0x1a\n        0x0D    0xf3         0xf3      RETURN\n        [...deployed code]\n\n    flat deploy function: 0x603a600e3d39601a805130553df3363d3d373d3d3d363d30545af43d82803e903d91601857fd5bf3\n*/\nlibrary Wallet {\n  bytes internal constant creationCode = hex\"603a600e3d39601a805130553df3363d3d373d3d3d363d30545af43d82803e903d91601857fd5bf3\";\n}\n"
  },
  {
    "path": "contracts/hooks/WalletProxyHook.sol",
    "content": "// SPDX-License-Identifier: Apache-2.0\npragma solidity 0.8.18;\n\nimport {IWalletProxy} from './interfaces/IWalletProxy.sol';\nimport {Implementation} from '../modules/commons/Implementation.sol';\n\ncontract WalletProxyHook is IWalletProxy, Implementation {\n  /// @inheritdoc IWalletProxy\n  function PROXY_getImplementation() public view returns (address) {\n    return _getImplementation();\n  }\n}\n"
  },
  {
    "path": "contracts/hooks/interfaces/IWalletProxy.sol",
    "content": "// Copyright Immutable Pty Ltd 2018 - 2023\n// SPDX-License-Identifier: Apache 2.0\n// https://github.com/immutable/contracts/blob/a04f7ecb8a79ad8f1b67f73f770e0545deb6cba2/contracts/allowlist/IWalletProxy.sol\npragma solidity 0.8.18;\n\n// Interface to retrieve the implemention stored inside the Proxy contract\n/// Interface for Passport Wallet's proxy contract.\ninterface IWalletProxy {\n    // Returns the current implementation address used by the proxy contract\n    // solhint-disable-next-line func-name-mixedcase\n    function PROXY_getImplementation() external view returns (address);\n}\n"
  },
  {
    "path": "contracts/interfaces/IERC1271Wallet.sol",
    "content": "// SPDX-License-Identifier: Apache-2.0\npragma solidity 0.8.18;\n\n\ninterface IERC1271Wallet {\n\n  /**\n   * @notice Verifies whether the provided signature is valid with respect to the provided data\n   * @dev MUST return the correct magic value if the signature provided is valid for the provided data\n   *   > The bytes4 magic value to return when signature is valid is 0x20c13b0b : bytes4(keccak256(\"isValidSignature(bytes,bytes)\")\n   *   > This function MAY modify Ethereum's state\n   * @param _data       Arbitrary length data signed on the behalf of address(this)\n   * @param _signature  Signature byte array associated with _data\n   * @return magicValue Magic value 0x20c13b0b if the signature is valid and 0x0 otherwise\n   */\n  function isValidSignature(\n    bytes calldata _data,\n    bytes calldata _signature)\n    external\n    view\n    returns (bytes4 magicValue);\n\n  /**\n   * @notice Verifies whether the provided signature is valid with respect to the provided hash\n   * @dev MUST return the correct magic value if the signature provided is valid for the provided hash\n   *   > The bytes4 magic value to return when signature is valid is 0x20c13b0b : bytes4(keccak256(\"isValidSignature(bytes,bytes)\")\n   *   > This function MAY modify Ethereum's state\n   * @param _hash       keccak256 hash that was signed\n   * @param _signature  Signature byte array associated with _data\n   * @return magicValue Magic value 0x20c13b0b if the signature is valid and 0x0 otherwise\n   */\n  function isValidSignature(\n    bytes32 _hash,\n    bytes calldata _signature)\n    external\n    view\n    returns (bytes4 magicValue);\n}"
  },
  {
    "path": "contracts/interfaces/receivers/IERC1155Receiver.sol",
    "content": "// SPDX-License-Identifier: Apache-2.0\npragma solidity 0.8.18;\n\n\ninterface IERC1155Receiver {\n  function onERC1155Received(address, address, uint256, uint256, bytes calldata) external returns (bytes4);\n  function onERC1155BatchReceived(address, address, uint256[] calldata, uint256[] calldata, bytes calldata) external returns (bytes4);\n}\n"
  },
  {
    "path": "contracts/interfaces/receivers/IERC223Receiver.sol",
    "content": "// SPDX-License-Identifier: Apache-2.0\npragma solidity 0.8.18;\n\n\ninterface IERC223Receiver {\n  function tokenFallback(address, uint256, bytes calldata) external;\n}\n"
  },
  {
    "path": "contracts/interfaces/receivers/IERC721Receiver.sol",
    "content": "// SPDX-License-Identifier: Apache-2.0\npragma solidity 0.8.18;\n\n\ninterface IERC721Receiver {\n  function onERC721Received(address, address, uint256, bytes calldata) external returns (bytes4);\n}\n"
  },
  {
    "path": "contracts/interfaces/receivers/IERC777Receiver.sol",
    "content": "// SPDX-License-Identifier: Apache-2.0\r\npragma solidity 0.8.18;\r\n\r\ninterface IERC777Receiver {\r\n    function tokensReceived(address, address, address, uint256, bytes calldata, bytes calldata) external;\r\n}\r\n"
  },
  {
    "path": "contracts/interfaces/tokens/IERC1155.sol",
    "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.18;\n\ninterface IERC1155 {\n  function balanceOf(address account, uint256 id) external view returns (uint256);\n  function balanceOfBatch(address[] calldata accounts, uint256[] calldata ids) external view returns (uint256[] memory);\n  function setApprovalForAll(address operator, bool approved) external;\n  function isApprovedForAll(address account, address operator) external view returns (bool);\n  function safeTransferFrom(address from, address to, uint256 id, uint256 amount, bytes calldata data) external;\n  function safeBatchTransferFrom(\n    address from,\n    address to,\n    uint256[] calldata ids,\n    uint256[] calldata amounts,\n    bytes calldata data\n  ) external;\n\n  event TransferSingle(address indexed operator, address indexed from, address indexed to, uint256 id, uint256 value);\n  event TransferBatch(address indexed operator, address indexed from, address indexed to, uint256[] ids, uint256[] values);\n  event ApprovalForAll(address indexed account, address indexed operator, bool approved);\n  event URI(string value, uint256 indexed id);\n}\n"
  },
  {
    "path": "contracts/interfaces/tokens/IERC20.sol",
    "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.18;\n\ninterface IERC20 {\n  function totalSupply() external view returns (uint256);\n  function balanceOf(address account) external view returns (uint256);\n  function transfer(address recipient, uint256 amount) external returns (bool);\n  function allowance(address owner, address spender) external view returns (uint256);\n  function approve(address spender, uint256 amount) external returns (bool);\n  function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);\n\n  event Transfer(address indexed from, address indexed to, uint256 value);\n  event Approval(address indexed owner, address indexed spender, uint256 value);\n}\n"
  },
  {
    "path": "contracts/interfaces/tokens/IERC721.sol",
    "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.18;\n\ninterface IERC721 {\n  function balanceOf(address owner) external view returns (uint256 balance);\n  function ownerOf(uint256 tokenId) external view returns (address owner);\n  function safeTransferFrom(address from, address to, uint256 tokenId) external;\n  function transferFrom(address from, address to, uint256 tokenId) external;\n  function approve(address to, uint256 tokenId) external;\n  function getApproved(uint256 tokenId) external view returns (address operator);\n  function setApprovalForAll(address operator, bool approved) external;\n  function isApprovedForAll(address owner, address operator) external view returns (bool);\n  function safeTransferFrom(address from, address to, uint256 tokenId, bytes calldata data) external;\n\n  event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);\n  event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);\n  event ApprovalForAll(address indexed owner, address indexed operator, bool approved);\n}\n"
  },
  {
    "path": "contracts/mocks/AlwaysRevertMock.sol",
    "content": "// SPDX-License-Identifier: Apache-2.0\npragma solidity 0.8.18;\n\n\ncontract AlwaysRevertMock {\n  fallback() external payable {\n    revert(\"AlwaysRevertMock#fallback: ALWAYS_REVERT\");\n  }\n}\n"
  },
  {
    "path": "contracts/mocks/CallReceiverMock.sol",
    "content": "// SPDX-License-Identifier: Apache-2.0\npragma solidity 0.8.18;\n\n\ncontract CallReceiverMock {\n  uint256 public lastValA;\n  bytes public lastValB;\n\n  bool revertFlag;\n\n  constructor() payable { }\n\n  function setRevertFlag(bool _revertFlag) external {\n    revertFlag = _revertFlag;\n  }\n\n  function testCall(uint256 _valA, bytes calldata _valB) external payable {\n    require(!revertFlag, \"CallReceiverMock#testCall: REVERT_FLAG\");\n\n    lastValA = _valA;\n    lastValB = _valB;\n  }\n}\n"
  },
  {
    "path": "contracts/mocks/DelegateCallMock.sol",
    "content": "// SPDX-License-Identifier: Apache-2.0\npragma solidity 0.8.18;\n\n\ncontract DelegateCallMock {\n  event Readed(uint256 _val);\n\n  uint256 private constant REVERT_SLOT = uint256(keccak256(\"revert-flag\"));\n\n  mapping(uint256 => uint256) private store;\n\n  function setRevertFlag(bool _revertFlag) external {\n    store[REVERT_SLOT] = _revertFlag ? 1 : 0;\n  }\n\n  function write(uint256 _key, uint256 _val) external {\n    require(store[REVERT_SLOT] == 0, \"DelegateCallMock#write: REVERT_FLAG\");\n    store[_key] = _val;\n  }\n\n  function read(uint256 _key) external {\n    emit Readed(store[_key]);\n  }\n}\n"
  },
  {
    "path": "contracts/mocks/ERC1155Mock.sol",
    "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.18;\n\ncontract ERC1155Mock {\n  string public name = 'Mock ERC1155 Token';\n  string public symbol = 'MERC1155';\n  address public owner;\n\n  mapping(uint256 => mapping(address => uint256)) public balances;\n  mapping(address => mapping(address => bool)) public operatorApprovals;\n\n  constructor() {\n    owner = msg.sender;\n  }\n\n  modifier onlyOwner() {\n    require(msg.sender == owner, 'Only owner can mint');\n    _;\n  }\n\n  function balanceOf(address account, uint256 id) public view returns (uint256) {\n    return balances[id][account];\n  }\n\n  function balanceOfBatch(address[] memory accounts, uint256[] memory ids) public view returns (uint256[] memory) {\n    require(accounts.length == ids.length, 'Accounts and ids length mismatch');\n\n    uint256[] memory batchBalances = new uint256[](accounts.length);\n    for (uint256 i = 0; i < accounts.length; ++i) {\n      batchBalances[i] = balances[ids[i]][accounts[i]];\n    }\n    return batchBalances;\n  }\n\n  function mint(address to, uint256 id, uint256 amount) public onlyOwner {\n    require(to != address(0), 'Cannot mint to zero address');\n\n    balances[id][to] += amount;\n    emit TransferSingle(msg.sender, address(0), to, id, amount);\n  }\n\n  function safeTransferFrom(address from, address to, uint256 id, uint256 amount) public {\n    require(from == msg.sender || isApprovedForAll(from, msg.sender), 'Not approved to transfer');\n\n    require(balances[id][from] >= amount, 'Insufficient balance');\n    balances[id][from] -= amount;\n    balances[id][to] += amount;\n\n    emit TransferSingle(msg.sender, from, to, id, amount);\n  }\n\n  function setApprovalForAll(address operator, bool approved) public {\n    operatorApprovals[msg.sender][operator] = approved;\n    emit ApprovalForAll(msg.sender, operator, approved);\n  }\n\n  function isApprovedForAll(address account, address operator) public view returns (bool) {\n    return operatorApprovals[account][operator];\n  }\n\n  event TransferSingle(address indexed operator, address indexed from, address indexed to, uint256 id, uint256 value);\n  event ApprovalForAll(address indexed account, address indexed operator, bool approved);\n}\n"
  },
  {
    "path": "contracts/mocks/ERC165CheckerMock.sol",
    "content": "// SPDX-License-Identifier: Apache-2.0\npragma solidity 0.8.18;\n\n\ncontract ERC165CheckerMock {\n  bytes4 constant InvalidID = 0xffffffff;\n  bytes4 constant ERC165ID = 0x01ffc9a7;\n\n  function doesContractImplementInterface(address _contract, bytes4 _interfaceId) external view returns (bool) {\n    uint256 success;\n    uint256 result;\n\n    (success, result) = noThrowCall(_contract, ERC165ID);\n    if (success == 0 || result == 0) {\n      return false;\n    }\n\n    (success, result) = noThrowCall(_contract, InvalidID);\n    if (success == 0 || result != 0) {\n      return false;\n    }\n\n    (success, result) = noThrowCall(_contract, _interfaceId);\n    if (success == 1 && result == 1) {\n      return true;\n    }\n    return false;\n  }\n\n  function noThrowCall(\n    address _contract,\n    bytes4 _interfaceId\n  ) private view returns (\n    uint256 success,\n    uint256 result\n  ) {\n    bytes4 erc165ID = ERC165ID;\n\n    assembly {\n      let x := mload(0x40)               // Find empty storage location using \"free memory pointer\"\n      mstore(x, erc165ID)                // Place signature at beginning of empty storage\n      mstore(add(x, 0x04), _interfaceId) // Place first argument directly next to signature\n\n      success := staticcall(\n        30000,     // 30k gas\n        _contract, // To addr\n        x,         // Inputs are stored at location x\n        0x24,      // Inputs are 36 bytes long\n        x,         // Store output over input (saves space)\n        0x20       // Outputs are 32 bytes long\n      )\n\n      result := mload(x)                 // Load the result\n    }\n  }\n}\n"
  },
  {
    "path": "contracts/mocks/ERC20Mock.sol",
    "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.18;\n\ncontract ERC20Mock {\n  string public name = 'Mock ERC20 Token';\n  string public symbol = 'MERC20';\n  uint8 public decimals = 18;\n  uint256 public totalSupply;\n  mapping(address => uint256) public balances;\n  mapping(address => mapping(address => uint256)) public allowances;\n\n  constructor(uint256 initialSupply) {\n    totalSupply = initialSupply;\n    balances[msg.sender] = initialSupply;\n  }\n\n  function balanceOf(address account) public view returns (uint256) {\n    return balances[account];\n  }\n\n  function transfer(address recipient, uint256 amount) public returns (bool) {\n    require(balances[msg.sender] >= amount, 'Insufficient balance');\n    balances[msg.sender] -= amount;\n    balances[recipient] += amount;\n    emit Transfer(msg.sender, recipient, amount);\n    return true;\n  }\n\n  function allowance(address owner, address spender) public view returns (uint256) {\n    return allowances[owner][spender];\n  }\n\n  function approve(address spender, uint256 amount) public returns (bool) {\n    allowances[msg.sender][spender] = amount;\n    emit Approval(msg.sender, spender, amount);\n    return true;\n  }\n\n  function transferFrom(address sender, address recipient, uint256 amount) public returns (bool) {\n    require(balances[sender] >= amount, 'Insufficient balance');\n    require(allowances[sender][msg.sender] >= amount, 'Allowance exceeded');\n    balances[sender] -= amount;\n    balances[recipient] += amount;\n    allowances[sender][msg.sender] -= amount;\n    emit Transfer(sender, recipient, amount);\n    return true;\n  }\n\n  event Transfer(address indexed from, address indexed to, uint256 value);\n  event Approval(address indexed owner, address indexed spender, uint256 value);\n}\n"
  },
  {
    "path": "contracts/mocks/ERC721Mock.sol",
    "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.18;\n\ncontract ERC721Mock {\n  string public name = 'Mock ERC721 Token';\n  string public symbol = 'MERC721';\n  uint256 public totalSupply;\n  address public owner;\n\n  mapping(address => uint256) public balances;\n  mapping(uint256 => address) public owners;\n  mapping(address => mapping(address => bool)) public operatorApprovals;\n  mapping(uint256 => address) public tokenApprovals;\n\n  constructor() {\n    owner = msg.sender;\n  }\n\n  modifier onlyOwner() {\n    require(msg.sender == owner, 'Only owner can mint');\n    _;\n  }\n\n  function balanceOf(address _owner) public view returns (uint256) {\n    return balances[_owner];\n  }\n\n  function ownerOf(uint256 tokenId) public view returns (address) {\n    address tokenOwner = owners[tokenId];\n    require(tokenOwner != address(0), 'Token does not exist');\n    return tokenOwner;\n  }\n\n  function mint(address to, uint256 tokenId) public onlyOwner {\n    require(to != address(0), 'Cannot mint to zero address');\n    require(owners[tokenId] == address(0), 'Token already minted');\n\n    owners[tokenId] = to;\n    balances[to] += 1;\n    totalSupply += 1;\n\n    emit Transfer(address(0), to, tokenId);\n  }\n\n  function transferFrom(address from, address to, uint256 tokenId) public {\n    require(ownerOf(tokenId) == from, 'Not the owner of the token');\n    require(to != address(0), 'Cannot transfer to zero address');\n\n    require(\n      msg.sender == from || getApproved(tokenId) == msg.sender || isApprovedForAll(from, msg.sender),\n      'Not approved to transfer'\n    );\n\n    balances[from] -= 1;\n    balances[to] += 1;\n    owners[tokenId] = to;\n\n    emit Transfer(from, to, tokenId);\n  }\n\n  function approve(address to, uint256 tokenId) public {\n    address tokenOwner = ownerOf(tokenId);\n    require(to != tokenOwner, 'Cannot approve current owner');\n    require(msg.sender == tokenOwner || isApprovedForAll(tokenOwner, msg.sender), 'Not approved');\n\n    tokenApprovals[tokenId] = to;\n    emit Approval(tokenOwner, to, tokenId);\n  }\n\n  function getApproved(uint256 tokenId) public view returns (address) {\n    return tokenApprovals[tokenId];\n  }\n\n  function setApprovalForAll(address operator, bool approved) public {\n    operatorApprovals[msg.sender][operator] = approved;\n    emit ApprovalForAll(msg.sender, operator, approved);\n  }\n\n  function isApprovedForAll(address _owner, address operator) public view returns (bool) {\n    return operatorApprovals[_owner][operator];\n  }\n\n  event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);\n  event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);\n  event ApprovalForAll(address indexed owner, address indexed operator, bool approved);\n}\n"
  },
  {
    "path": "contracts/mocks/GasBurnerMock.sol",
    "content": "// SPDX-License-Identifier: Apache-2.0\npragma solidity 0.8.18;\n\n\ncontract GasBurnerMock {\n  event ProvidedGas(uint256 _val);\n\n  function burnGas(uint256 _burn) external {\n    emit ProvidedGas(gasleft());\n\n    bytes32 stub;\n    uint256 initial = gasleft();\n\n    while (initial - gasleft() < _burn) {\n      stub = keccak256(abi.encode(stub));\n    }\n  }\n}\n"
  },
  {
    "path": "contracts/mocks/HookCallerMock.sol",
    "content": "// SPDX-License-Identifier: Apache-2.0\npragma solidity 0.8.18;\n\nimport \"../interfaces/receivers/IERC1155Receiver.sol\";\nimport \"../interfaces/receivers/IERC721Receiver.sol\";\nimport \"../interfaces/receivers/IERC223Receiver.sol\";\n\nimport \"../interfaces/IERC1271Wallet.sol\";\n\n\ncontract HookCallerMock {\n  function callERC1155Received(address _addr) external {\n    bytes4 result = IERC1155Receiver(_addr).onERC1155Received(\n      address(this),\n      msg.sender,\n      1,\n      2,\n      msg.data\n    );\n\n    require(result == 0xf23a6e61, \"HookCallerMock#callERC1155Received: INVALID_RETURN\");\n  }\n\n  function callERC1155BatchReceived(address _addr) external {\n    uint256[] memory ids = new uint256[](3);\n    ids[0] = 1;\n    ids[1] = 2;\n    ids[2] = 3;\n\n    uint256[] memory values = new uint256[](3);\n    values[0] = 200;\n    values[1] = 300;\n    values[2] = 400;\n\n    bytes4 result = IERC1155Receiver(_addr).onERC1155BatchReceived(\n      address(this),\n      msg.sender,\n      ids,\n      values,\n      msg.data\n    );\n\n    require(result == 0xbc197c81, \"HookCallerMock#callERC1155BatchReceived: INVALID_RETURN\");\n  }\n\n  function callERC721Received(address _addr) external {\n    bytes4 result = IERC721Receiver(_addr).onERC721Received(\n      address(this),\n      msg.sender,\n      1,\n      msg.data\n    );\n\n    require(result == 0x150b7a02, \"HookCallerMock#callERC721Received: INVALID_RETURN\");\n  }\n\n  function callERC223Received(address _addr) external {\n    IERC223Receiver(_addr).tokenFallback(msg.sender, 1, msg.data);\n  }\n\n  function callERC1271isValidSignatureData(\n    address _addr,\n    bytes calldata _data,\n    bytes calldata _signature\n  ) external view {\n    bytes4 result = IERC1271Wallet(_addr).isValidSignature(_data, _signature);\n    require(result == 0x20c13b0b, \"HookCallerMock#callERC1271isValidSignatureData: INVALID_RETURN\");\n  }\n\n  function callERC1271isValidSignatureHash(\n    address _addr,\n    bytes32 _hash,\n    bytes calldata _signature\n  ) external view {\n    bytes4 result = IERC1271Wallet(_addr).isValidSignature(_hash, _signature);\n    require(result == 0x1626ba7e, \"HookCallerMock#callERC1271isValidSignatureHash: INVALID_RETURN\");\n  }\n}\n"
  },
  {
    "path": "contracts/mocks/HookMock.sol",
    "content": "// SPDX-License-Identifier: Apache-2.0\npragma solidity 0.8.18;\n\n\ncontract HookMock {\n  function onHookMockCall(uint256 _num) external pure returns (uint256) {\n    return _num * 2;\n  }\n}\n"
  },
  {
    "path": "contracts/mocks/LibBytesImpl.sol",
    "content": "// SPDX-License-Identifier: Apache-2.0\npragma solidity 0.8.18;\n\nimport \"../utils/LibBytes.sol\";\n\n\ncontract LibBytesImpl {\n  using LibBytes for bytes;\n\n  function readBytes32(\n    bytes calldata data,\n    uint256 index\n  ) external pure returns (\n    bytes32 a\n  ) {\n    return LibBytes.readBytes32(data, index);\n  }\n\n  function readUint8(\n    bytes calldata data,\n    uint256 index\n  ) external pure returns (\n    uint8 a\n  ) {\n    return LibBytes.readUint8(data, index);\n  }\n\n  function readUint32(\n    bytes calldata data,\n    uint256 index\n  ) external pure returns (\n    uint32 a\n  ) {\n    return LibBytes.readUint32(data, index);\n  }\n}\n"
  },
  {
    "path": "contracts/mocks/LibBytesPointerImpl.sol",
    "content": "// SPDX-License-Identifier: Apache-2.0\npragma solidity 0.8.18;\n\nimport \"../utils/LibBytesPointer.sol\";\n\n\ncontract LibBytesPointerImpl {\n  using LibBytesPointer for bytes;\n\n  function readFirstUint16(\n    bytes calldata data\n  ) external pure returns (\n    uint16 a,\n    uint256 newPointer\n  ) {\n    return LibBytesPointer.readFirstUint16(data);\n  }\n\n  function readUint16(\n    bytes calldata data,\n    uint256 index\n  ) external pure returns (\n    uint16 a,\n    uint256 newPointer\n  ) {\n    return LibBytesPointer.readUint16(data, index);\n  }\n\n  function readUint24(\n    bytes calldata data,\n    uint256 index\n  ) external pure returns (\n    uint24 a,\n    uint256 newPointer\n  ) {\n    return LibBytesPointer.readUint24(data, index);\n  }\n\n  function readUint64(\n    bytes calldata data,\n    uint256 index\n  ) external pure returns (\n    uint64 a,\n    uint256 newPointer\n  ) {\n    return LibBytesPointer.readUint64(data, index);\n  }\n}\n"
  },
  {
    "path": "contracts/mocks/LibStringImp.sol",
    "content": "// SPDX-License-Identifier: Apache-2.0\npragma solidity 0.8.18;\n\nimport \"../utils/LibString.sol\";\n\n\ncontract LibStringImp {\n  using LibString for string;\n\n  function prefixBase32(string calldata data) external pure returns (string memory) {\n    return LibString.prefixBase32(data);\n  }\n\n  function prefixHexadecimal(string calldata data) external pure returns (string memory) {\n    return LibString.prefixHexadecimal(data);\n  }\n\n  function bytesToBase32(bytes calldata data) external pure returns (string memory) {\n    return LibString.bytesToBase32(data);\n  }\n\n  function bytesToHexadecimal(bytes calldata data) external pure returns (string memory) {\n    return LibString.bytesToHexadecimal(data);\n  }\n}\n"
  },
  {
    "path": "contracts/mocks/ModuleMock.sol",
    "content": "// SPDX-License-Identifier: Apache-2.0\npragma solidity 0.8.18;\n\n\ncontract ModuleMock {\n  event Pong();\n\n  function ping() external {\n    emit Pong();\n  }\n}\n"
  },
  {
    "path": "contracts/modules/GuestModule.sol",
    "content": "// SPDX-License-Identifier: Apache-2.0\npragma solidity 0.8.18;\n\nimport \"../utils/LibOptim.sol\";\n\nimport \"./commons/submodules/auth/SequenceBaseSig.sol\";\n\nimport \"./commons/ModuleAuth.sol\";\nimport \"./commons/ModuleCalls.sol\";\nimport \"./commons/ModuleCreator.sol\";\n\n\n/**\n * GuestModule implements a Sequence wallet without signatures, nonce or replay protection.\n * executing transactions using this wallet is not an authenticated process, and can be done by any address.\n *\n * @notice This contract is completely public with no security, designed to execute pre-signed transactions\n *   and use Sequence tools without using the wallets.\n */\ncontract GuestModule is\n  ModuleAuth,\n  ModuleCalls,\n  ModuleCreator\n{\n  error DelegateCallNotAllowed(uint256 _index);\n  error NotSupported();\n\n  /**\n   * @notice Allow any caller to execute an action\n   * @param _txs Transactions to process\n   */\n  function execute(\n    Transaction[] calldata _txs,\n    uint256,\n    bytes calldata\n  ) public override {\n    // Hash transaction bundle\n    bytes32 txHash = SequenceBaseSig.subdigest(keccak256(abi.encode('guest:', _txs)));\n\n    // Execute the transactions\n    _executeGuest(txHash, _txs);\n  }\n\n  /**\n   * @notice Allow any caller to execute an action\n   * @param _txs Transactions to process\n   */\n  function selfExecute(\n    Transaction[] calldata _txs\n  ) public override {\n    // Hash transaction bundle\n    bytes32 txHash = SequenceBaseSig.subdigest(keccak256(abi.encode('self:', _txs)));\n\n    // Execute the transactions\n    _executeGuest(txHash, _txs);\n  }\n\n  /**\n   * @notice Executes a list of transactions\n   * @param _txHash  Hash of the batch of transactions\n   * @param _txs  Transactions to execute\n   */\n  function _executeGuest(\n    bytes32 _txHash,\n    Transaction[] calldata _txs\n  ) private {\n    // Execute transaction\n    uint256 size = _txs.length;\n    for (uint256 i = 0; i < size; i++) {\n      Transaction calldata transaction = _txs[i];\n\n      if (transaction.delegateCall) revert DelegateCallNotAllowed(i);\n\n      uint256 gasLimit = transaction.gasLimit;\n      if (gasleft() < gasLimit) revert NotEnoughGas(i, gasLimit, gasleft());\n\n      bool success = LibOptim.call(\n        transaction.target,\n        transaction.value,\n        gasLimit == 0 ? gasleft() : gasLimit,\n        transaction.data\n      );\n\n      if (success) {\n        emit TxExecuted(_txHash, i);\n      } else {\n        _revertBytes(\n          transaction.revertOnError,\n          _txHash,\n          i,\n          LibOptim.returnData()\n        );\n      }\n    }\n  }\n\n  /**\n   * @notice Validates any signature image, because the wallet is public and has no owner.\n   * @return true, all signatures are valid.\n   */\n  function _isValidImage(bytes32) internal override pure returns (bool) {\n    return true;\n  }\n\n  /**\n   * Not supported.\n   */\n  function _updateImageHash(bytes32) internal override virtual {\n    revert NotSupported();\n  }\n\n  /**\n   * @notice Query if a contract implements an interface\n   * @param _interfaceID The interface identifier, as specified in ERC-165\n   * @return `true` if the contract implements `_interfaceID`\n   */\n  function supportsInterface(\n    bytes4 _interfaceID\n  ) public override (\n    ModuleAuth,\n    ModuleCalls,\n    ModuleCreator\n  ) pure returns (bool) {\n    return super.supportsInterface(_interfaceID);\n  }\n}\n"
  },
  {
    "path": "contracts/modules/MainModule.sol",
    "content": "// SPDX-License-Identifier: Apache-2.0\npragma solidity 0.8.18;\n\nimport \"./commons/ModuleAuthFixed.sol\";\nimport \"./commons/ModuleHooks.sol\";\nimport \"./commons/ModuleCalls.sol\";\nimport \"./commons/ModuleCreator.sol\";\nimport \"./commons/ModuleExtraAuth.sol\";\nimport \"./commons/ModuleAuthConvenience.sol\";\n\n\n/**\n * @notice Contains the core functionality Sequence wallets will inherit.\n * @dev If using a new main module, developers must ensure that all inherited\n *      contracts by the main module don't conflict and are accounted for to be\n *      supported by the supportsInterface method.\n */\ncontract MainModule is\n  ModuleAuthFixed,\n  ModuleExtraAuth,\n  ModuleCalls,\n  ModuleHooks,\n  ModuleCreator,\n  ModuleAuthConvenience\n{\n  constructor(\n    address _factory,\n    address _mainModuleUpgradable\n  ) ModuleAuthFixed(\n    _factory,\n    _mainModuleUpgradable\n  ) { }\n\n  function _isValidImage(\n    bytes32 _imageHash\n  ) internal override(\n    IModuleAuth,\n    ModuleAuthFixed,\n    ModuleExtraAuth\n  ) view returns (bool) {\n    return super._isValidImage(_imageHash);\n  }\n\n  /**\n   * @notice Query if a contract implements an interface\n   * @param _interfaceID The interface identifier, as specified in ERC-165\n   * @return `true` if the contract implements `_interfaceID`\n   */\n  function supportsInterface(\n    bytes4 _interfaceID\n  ) public override(\n    ModuleAuthFixed,\n    ModuleAuthConvenience,\n    ModuleCalls,\n    ModuleExtraAuth,\n    ModuleHooks,\n    ModuleCreator\n  ) pure returns (bool) {\n    return super.supportsInterface(_interfaceID);\n  }\n}\n"
  },
  {
    "path": "contracts/modules/MainModuleGasEstimation.sol",
    "content": "// SPDX-License-Identifier: Apache-2.0\npragma solidity 0.8.18;\n\nimport \"./commons/gas-estimation/ModuleIgnoreAuthUpgradable.sol\";\nimport \"./commons/gas-estimation/ModuleIgnoreNonceCalls.sol\";\nimport \"./commons/ModuleHooks.sol\";\nimport \"./commons/ModuleUpdate.sol\";\nimport \"./commons/ModuleCreator.sol\";\n\n\n/**\n * @notice Contains an alternative implementation of the MainModules that skips validation of\n *   signatures, this implementation SHOULD NOT be used directly on a wallet.\n *\n *   Intended to be used only for gas estimation, using eth_call and overrides.\n */\ncontract MainModuleGasEstimation is\n  ModuleIgnoreAuthUpgradable,\n  ModuleIgnoreNonceCalls,\n  ModuleUpdate,\n  ModuleHooks,\n  ModuleCreator\n{\n  struct SimulateResult {\n    bool executed;\n    bool succeeded;\n    bytes result;\n    uint256 gasUsed;\n  }\n\n  /**\n   * @notice Simulate each transaction in a bundle for gas usage and execution result\n   * @param _txs Transactions to process\n   * @return The gas used and execution result for each transaction in the bundle\n   */\n  function simulateExecute(Transaction[] calldata _txs) public virtual returns (SimulateResult[] memory) {\n    unchecked {\n      SimulateResult[] memory results = new SimulateResult[](_txs.length);\n\n      // Execute transaction\n      uint256 size = _txs.length;\n      for (uint256 i = 0; i < size; i++) {\n        Transaction calldata transaction = _txs[i];\n        uint256 gasLimit = transaction.gasLimit;\n\n        results[i].executed = true;\n\n        if (gasleft() < gasLimit) {\n          results[i].succeeded = false;\n          results[i].result = abi.encodeWithSelector(IModuleCalls.NotEnoughGas.selector, i, gasLimit, gasleft());\n          break;\n        }\n\n        if (transaction.delegateCall) {\n          uint256 initialGas = gasleft();\n\n          (results[i].succeeded, results[i].result) = transaction.target.delegatecall{\n            gas: gasLimit == 0 ? gasleft() : gasLimit\n          }(transaction.data);\n\n          results[i].gasUsed = initialGas - gasleft();\n        } else {\n          uint256 initialGas = gasleft();\n\n          (results[i].succeeded, results[i].result) = transaction.target.call{\n            value: transaction.value,\n            gas: gasLimit == 0 ? gasleft() : gasLimit\n          }(transaction.data);\n\n          results[i].gasUsed = initialGas - gasleft();\n        }\n\n        if (!results[i].succeeded && transaction.revertOnError) {\n          break;\n        }\n      }\n\n      return results;\n    }\n  }\n\n  function _isValidImage(bytes32 _imageHash) internal override(\n    IModuleAuth,\n    ModuleIgnoreAuthUpgradable\n  ) view returns (bool) {\n    return super._isValidImage(_imageHash);\n  }\n\n  /**\n   * @notice Query if a contract implements an interface\n   * @param _interfaceID The interface identifier, as specified in ERC-165\n   * @dev If using a new main module, developers must ensure that all inherited\n   *      contracts by the main module don't conflict and are accounted for to be\n   *      supported by the supportsInterface method.\n   * @return `true` if the contract implements `_interfaceID`\n   */\n  function supportsInterface(\n    bytes4 _interfaceID\n  ) public override(\n    ModuleAuthUpgradable,\n    ModuleCalls,\n    ModuleUpdate,\n    ModuleHooks,\n    ModuleCreator\n  ) pure returns (bool) {\n    return super.supportsInterface(_interfaceID);\n  }\n}\n"
  },
  {
    "path": "contracts/modules/MainModuleUpgradable.sol",
    "content": "// SPDX-License-Identifier: Apache-2.0\npragma solidity 0.8.18;\n\nimport \"./commons/ModuleAuthUpgradable.sol\";\nimport \"./commons/ModuleHooks.sol\";\nimport \"./commons/ModuleCalls.sol\";\nimport \"./commons/ModuleUpdate.sol\";\nimport \"./commons/ModuleCreator.sol\";\nimport \"./commons/ModuleExtraAuth.sol\";\nimport \"./commons/ModuleAuthConvenience.sol\";\n\n\n/**\n * @notice Contains the core functionality Sequence wallets will inherit with\n *         the added functionality that the main module can be changed.\n * @dev If using a new main module, developers must ensure that all inherited\n *      contracts by the main module don't conflict and are accounted for to be\n *      supported by the supportsInterface method.\n */\ncontract MainModuleUpgradable is\n  ModuleAuthUpgradable,\n  ModuleExtraAuth,\n  ModuleCalls,\n  ModuleUpdate,\n  ModuleHooks,\n  ModuleCreator,\n  ModuleAuthConvenience\n{\n  function _isValidImage(\n    bytes32 _imageHash\n  ) internal override(\n    IModuleAuth,\n    ModuleAuthUpgradable,\n    ModuleExtraAuth\n  ) view returns (bool) {\n    return super._isValidImage(_imageHash);\n  }\n\n  /**\n   * @notice Query if a contract implements an interface\n   * @param _interfaceID The interface identifier, as specified in ERC-165\n   * @dev If using a new main module, developers must ensure that all inherited\n   *      contracts by the main module don't conflict and are accounted for to be\n   *      supported by the supportsInterface method.\n   * @return `true` if the contract implements `_interfaceID`\n   */\n  function supportsInterface(\n    bytes4 _interfaceID\n  ) public override(\n    ModuleAuthUpgradable,\n    ModuleAuthConvenience,\n    ModuleCalls,\n    ModuleExtraAuth,\n    ModuleUpdate,\n    ModuleHooks,\n    ModuleCreator\n  ) pure returns (bool) {\n    return super.supportsInterface(_interfaceID);\n  }\n}\n"
  },
  {
    "path": "contracts/modules/commons/Implementation.sol",
    "content": "// SPDX-License-Identifier: Apache-2.0\npragma solidity 0.8.18;\n\n/**\n * @dev Allows modules to access the implementation slot\n */\ncontract Implementation {\n  /**\n   * @notice Updates the Wallet implementation\n   * @param _imp New implementation address\n   * @dev The wallet implementation is stored on the storage slot\n   *   defined by the address of the wallet itself\n   *   WARNING updating this value may break the wallet and users\n   *   must be confident that the new implementation is safe.\n   */\n  function _setImplementation(address _imp) internal {\n    assembly {\n      sstore(address(), _imp)\n    }\n  }\n\n  /**\n   * @notice Returns the Wallet implementation\n   * @return _imp The address of the current Wallet implementation\n   */\n  function _getImplementation() internal view returns (address _imp) {\n    assembly {\n      _imp := sload(address())\n    }\n  }\n}\n"
  },
  {
    "path": "contracts/modules/commons/ModuleAuth.sol",
    "content": "// SPDX-License-Identifier: Apache-2.0\npragma solidity 0.8.18;\n\nimport \"../../utils/LibBytes.sol\";\nimport \"../../interfaces/IERC1271Wallet.sol\";\n\nimport \"./interfaces/IModuleAuth.sol\";\n\nimport \"./ModuleERC165.sol\";\n\nimport \"./submodules/auth/SequenceBaseSig.sol\";\nimport \"./submodules/auth/SequenceDynamicSig.sol\";\nimport \"./submodules/auth/SequenceNoChainIdSig.sol\";\nimport \"./submodules/auth/SequenceChainedSig.sol\";\n\n\nabstract contract ModuleAuth is\n  IModuleAuth,\n  ModuleERC165,\n  IERC1271Wallet,\n  SequenceChainedSig\n{\n  using LibBytes for bytes;\n\n  bytes1 internal constant LEGACY_TYPE = hex\"00\";\n  bytes1 internal constant DYNAMIC_TYPE = hex\"01\";\n  bytes1 internal constant NO_CHAIN_ID_TYPE = hex\"02\";\n  bytes1 internal constant CHAINED_TYPE = hex\"03\";\n\n  bytes4 internal constant SELECTOR_ERC1271_BYTES_BYTES = 0x20c13b0b;\n  bytes4 internal constant SELECTOR_ERC1271_BYTES32_BYTES = 0x1626ba7e;\n\n  /**\n   * @notice Recovers the threshold, weight, imageHash, subdigest, and checkpoint of a signature.\n   * @dev The signature must be prefixed with a type byte, which is used to determine the recovery method.\n   *\n   * @param _digest Digest of the signed data.\n   * @param _signature A Sequence signature.\n   *\n   * @return threshold The required number of signatures needed to consider the signature valid.\n   * @return weight The actual number of signatures collected in the signature.\n   * @return imageHash The imageHash of the configuration that signed the message.\n   * @return subdigest A modified version of the original digest, unique for each wallet/network.\n   * @return checkpoint A nonce that is incremented every time a new configuration is set.\n   */\n  function signatureRecovery(\n    bytes32 _digest,\n    bytes calldata _signature\n  ) public override virtual view returns (\n    uint256 threshold,\n    uint256 weight,\n    bytes32 imageHash,\n    bytes32 subdigest,\n    uint256 checkpoint\n  ) {\n    bytes1 signatureType = _signature[0];\n\n    if (signatureType == LEGACY_TYPE) {\n      // networkId digest + base recover\n      subdigest = SequenceBaseSig.subdigest(_digest);\n      (threshold, weight, imageHash, checkpoint) = SequenceBaseSig.recover(subdigest, _signature);\n      return (threshold, weight, imageHash, subdigest, checkpoint);\n    }\n\n    if (signatureType == DYNAMIC_TYPE) {\n      // networkId digest + dynamic recover\n      subdigest = SequenceBaseSig.subdigest(_digest);\n      (threshold, weight, imageHash, checkpoint) = SequenceDynamicSig.recover(subdigest, _signature);\n      return (threshold, weight, imageHash, subdigest, checkpoint);\n    }\n\n    if (signatureType == NO_CHAIN_ID_TYPE) {\n      // noChainId digest + dynamic recover\n      subdigest = SequenceNoChainIdSig.subdigest(_digest);\n      (threshold, weight, imageHash, checkpoint) = SequenceDynamicSig.recover(subdigest, _signature);\n      return (threshold, weight, imageHash, subdigest, checkpoint);\n    }\n\n    if (signatureType == CHAINED_TYPE) {\n      // original digest + chained recover\n      // (subdigest will be computed in the chained recover)\n      return chainedRecover(_digest, _signature);\n    }\n\n    revert InvalidSignatureType(signatureType);\n  }\n\n  /**\n   * @dev Validates a signature.\n   *\n   * @param _digest Digest of the signed data.\n   * @param _signature A Sequence signature.\n   *\n   * @return isValid Indicates whether the signature is valid or not.\n   * @return subdigest A modified version of the original digest, unique for each wallet/network.\n   */\n  function _signatureValidation(\n    bytes32 _digest,\n    bytes calldata _signature\n  ) internal override virtual view returns (\n    bool isValid,\n    bytes32 subdigest\n  ) {\n    uint256 threshold; uint256 weight; bytes32 imageHash;\n    (threshold, weight, imageHash, subdigest,) = signatureRecovery(_digest, _signature);\n    isValid = weight >= threshold && _isValidImage(imageHash);\n  }\n\n  /**\n   * @notice Verifies whether the provided signature is valid with respect to the provided data\n   * @dev MUST return the correct magic value if the signature provided is valid for the provided data\n   *   > The bytes4 magic value to return when signature is valid is 0x20c13b0b : bytes4(keccak256(\"isValidSignature(bytes,bytes)\"))\n   * @param _data       Arbitrary length data signed on the behalf of address(this)\n   * @param _signatures Signature byte array associated with _data.\n   *                    Encoded as abi.encode(Signature[], Configs)\n   * @return magicValue Magic value 0x20c13b0b if the signature is valid and 0x0 otherwise\n   */\n  function isValidSignature(\n    bytes calldata _data,\n    bytes calldata _signatures\n  ) public override virtual view returns (bytes4) {\n    // Validate signatures\n    (bool isValid,) = _signatureValidation(keccak256(_data), _signatures);\n    if (isValid) {\n      return SELECTOR_ERC1271_BYTES_BYTES;\n    }\n\n    return bytes4(0);\n  }\n\n  /**\n   * @notice Verifies whether the provided signature is valid with respect to the provided hash\n   * @dev MUST return the correct magic value if the signature provided is valid for the provided hash\n   *   > The bytes4 magic value to return when signature is valid is 0x1626ba7e : bytes4(keccak256(\"isValidSignature(bytes32,bytes)\"))\n   * @param _hash       keccak256 hash that was signed\n   * @param _signatures Signature byte array associated with _data.\n   *                    Encoded as abi.encode(Signature[], Configs)\n   * @return magicValue Magic value 0x1626ba7e if the signature is valid and 0x0 otherwise\n   */\n  function isValidSignature(\n    bytes32 _hash,\n    bytes calldata _signatures\n  ) public override virtual view returns (bytes4) {\n    // Validate signatures\n    (bool isValid,) = _signatureValidation(_hash, _signatures);\n    if (isValid) {\n      return SELECTOR_ERC1271_BYTES32_BYTES;\n    }\n\n    return bytes4(0);\n  }\n\n  /**\n   * @notice Query if a contract implements an interface\n   * @param _interfaceID The interface identifier, as specified in ERC-165\n   * @return `true` if the contract implements `_interfaceID`\n   */\n  function supportsInterface(bytes4 _interfaceID) public override virtual pure returns (bool) {\n    if (\n      _interfaceID == type(IModuleAuth).interfaceId ||\n      _interfaceID == type(IERC1271Wallet).interfaceId\n    ) {\n      return true;\n    }\n\n    return super.supportsInterface(_interfaceID);\n  }\n\n  /**\n   * @notice Updates the signers configuration of the wallet\n   * @param _imageHash New required image hash of the signature\n   */\n  function updateImageHash(bytes32 _imageHash) external override virtual onlySelf {\n    _updateImageHash(_imageHash);\n  }\n}\n"
  },
  {
    "path": "contracts/modules/commons/ModuleAuthConvenience.sol",
    "content": "// SPDX-License-Identifier: Apache-2.0\npragma solidity 0.8.18;\n\nimport \"./ModuleSelfAuth.sol\";\nimport \"./ModuleAuth.sol\";\nimport \"./ModuleIPFS.sol\";\nimport \"./ModuleERC165.sol\";\n\nimport \"../../utils/LibString.sol\";\n\n\n\nabstract contract ModuleAuthConvenience is ModuleERC165, ModuleSelfAuth, ModuleAuth, ModuleIPFS {\n\n  /**\n  * @notice Updates the image hash and the IPFS root in a single operation.\n  * @dev These two operations are often performed together, so this function\n  *      allows to save some gas by performing them in a single step.\n  *\n  * @param _imageHash The new image hash to be set.\n  * @param _ipfsRoot The new IPFS root to be set.\n  */\n  function updateImageHashAndIPFS(\n    bytes32 _imageHash,\n    bytes32 _ipfsRoot\n  ) external onlySelf {\n    _updateImageHash(_imageHash);\n    _updateIPFSRoot(_ipfsRoot);\n  }\n\n  /**\n   * @notice Query if a contract implements an interface\n   * @param _interfaceID The interface identifier, as specified in ERC-165\n   * @return `true` if the contract implements `_interfaceID`\n   */\n  function supportsInterface(bytes4 _interfaceID) public override (\n    ModuleERC165,\n    ModuleAuth\n  ) virtual pure returns (bool) {\n    if (_interfaceID == type(ModuleAuthConvenience).interfaceId) {\n      return true;\n    }\n\n    return super.supportsInterface(_interfaceID);\n  }\n}\n"
  },
  {
    "path": "contracts/modules/commons/ModuleAuthFixed.sol",
    "content": "// SPDX-License-Identifier: Apache-2.0\npragma solidity 0.8.18;\n\nimport \"./ModuleAuth.sol\";\nimport \"./ModuleUpdate.sol\";\nimport \"./ModuleSelfAuth.sol\";\nimport \"./ModuleStorage.sol\";\n\nimport \"../../Wallet.sol\";\n\n/**\n *  Implements ModuleAuth by validating the signature image against\n *  the salt used to deploy the contract\n *\n *  This module allows wallets to be deployed with a default configuration\n *  without using any aditional contract storage\n */\nabstract contract ModuleAuthFixed is ModuleSelfAuth, ModuleAuth, ModuleUpdate {\n  bytes32 public immutable INIT_CODE_HASH;\n  address public immutable FACTORY;\n  address public immutable UPGRADEABLE_IMPLEMENTATION;\n\n  constructor(address _factory, address _mainModuleUpgradeable) {\n    // Build init code hash of the deployed wallets using that module\n    bytes32 initCodeHash = keccak256(abi.encodePacked(Wallet.creationCode, uint256(uint160(address(this)))));\n\n    INIT_CODE_HASH = initCodeHash;\n    FACTORY = _factory;\n    UPGRADEABLE_IMPLEMENTATION = _mainModuleUpgradeable;\n  }\n\n  /**\n   * @notice Updates the configuration of the wallet\n   * @dev In the process of updating the configuration, the wallet implementation\n   *      is updated to the mainModuleUpgradeable, this only happens once in the\n   *      lifetime of the wallet.\n   *\n   * @param _imageHash New required image hash of the signature\n   */\n  function _updateImageHash(bytes32 _imageHash) internal override virtual {\n    // Update imageHash in storage\n    if (_imageHash == bytes32(0)) revert ImageHashIsZero();\n    ModuleStorage.writeBytes32(IMAGE_HASH_KEY, _imageHash);\n    emit ImageHashUpdated(_imageHash);\n\n    // Update wallet implementation to mainModuleUpgradeable\n    _updateImplementation(UPGRADEABLE_IMPLEMENTATION);\n  }\n\n  /**\n   * @notice Validates the signature image with the salt used to deploy the contract\n   * @param _imageHash Hash image of signature\n   * @return true if the signature image is valid\n   */\n  function _isValidImage(bytes32 _imageHash) internal override virtual view returns (bool) {\n    return address(\n      uint160(\n        uint256(\n          keccak256(\n            abi.encodePacked(\n              hex\"ff\",\n              FACTORY,\n              _imageHash,\n              INIT_CODE_HASH\n            )\n          )\n        )\n      )\n    ) == address(this);\n  }\n\n  /**\n   * @notice Query if a contract implements an interface\n   * @param _interfaceID The interface identifier, as specified in ERC-165\n   * @return `true` if the contract implements `_interfaceID`\n   */\n  function supportsInterface(bytes4 _interfaceID) public override(ModuleAuth, ModuleUpdate) virtual pure returns (bool) {\n    return super.supportsInterface(_interfaceID);\n  }\n}\n"
  },
  {
    "path": "contracts/modules/commons/ModuleAuthUpgradable.sol",
    "content": "// SPDX-License-Identifier: Apache-2.0\npragma solidity 0.8.18;\n\nimport \"./interfaces/IModuleAuthUpgradable.sol\";\n\nimport \"./ModuleSelfAuth.sol\";\nimport \"./ModuleAuth.sol\";\nimport \"./ModuleStorage.sol\";\n\n\nabstract contract ModuleAuthUpgradable is IModuleAuthUpgradable, ModuleSelfAuth, ModuleAuth {\n  /**\n   * @notice Updates the signers configuration of the wallet\n   * @param _imageHash New required image hash of the signature\n   */\n  function _updateImageHash(bytes32 _imageHash) internal override virtual {\n    if (_imageHash == bytes32(0)) revert ImageHashIsZero();\n    ModuleStorage.writeBytes32(IMAGE_HASH_KEY, _imageHash);\n    emit ImageHashUpdated(_imageHash);\n  }\n\n  /**\n   * @notice Returns the current image hash of the wallet\n   */\n  function imageHash() external override virtual view returns (bytes32) {\n    return ModuleStorage.readBytes32(IMAGE_HASH_KEY);\n  }\n\n  /**\n   * @notice Validates the signature image with a valid image hash defined\n   *   in the contract storage\n   * @param _imageHash Hash image of signature\n   * @return true if the signature image is valid\n   */\n  function _isValidImage(bytes32 _imageHash) internal override virtual view returns (bool) {\n    return _imageHash != bytes32(0) && _imageHash == ModuleStorage.readBytes32(IMAGE_HASH_KEY);\n  }\n\n  /**\n   * @notice Query if a contract implements an interface\n   * @param _interfaceID The interface identifier, as specified in ERC-165\n   * @return `true` if the contract implements `_interfaceID`\n   */\n  function supportsInterface(bytes4 _interfaceID) public override virtual pure returns (bool) {\n    if (_interfaceID == type(IModuleAuthUpgradable).interfaceId) {\n      return true;\n    }\n\n    return super.supportsInterface(_interfaceID);\n  }\n}\n"
  },
  {
    "path": "contracts/modules/commons/ModuleCalls.sol",
    "content": "// SPDX-License-Identifier: Apache-2.0\npragma solidity 0.8.18;\n\nimport \"./ModuleSelfAuth.sol\";\nimport \"./ModuleStorage.sol\";\nimport \"./ModuleERC165.sol\";\nimport \"./ModuleNonce.sol\";\nimport \"./ModuleOnlyDelegatecall.sol\";\n\nimport \"./interfaces/IModuleCalls.sol\";\nimport \"./interfaces/IModuleAuth.sol\";\n\nimport \"./submodules/nonce/SubModuleNonce.sol\";\nimport \"./submodules/auth/SequenceBaseSig.sol\";\n\nimport \"../../utils/LibOptim.sol\";\n\n\nabstract contract ModuleCalls is IModuleCalls, IModuleAuth, ModuleERC165, ModuleOnlyDelegatecall, ModuleSelfAuth, ModuleNonce {\n  /**\n   * @notice Allow wallet owner to execute an action\n   * @dev Relayers must ensure that the gasLimit specified for each transaction\n   *      is acceptable to them. A user could specify large enough that it could\n   *      consume all the gas available.\n   * @param _txs        Transactions to process\n   * @param _nonce      Signature nonce (may contain an encoded space)\n   * @param _signature  Encoded signature\n   */\n  function execute(\n    Transaction[] calldata _txs,\n    uint256 _nonce,\n    bytes calldata _signature\n  ) external override virtual onlyDelegatecall {\n    // Validate and update nonce\n    _validateNonce(_nonce);\n\n    // Hash and verify transaction bundle\n    (bool isValid, bytes32 txHash) = _signatureValidation(\n      keccak256(\n        abi.encode(\n          _nonce,\n          _txs\n        )\n      ),\n      _signature\n    );\n\n    if (!isValid) {\n      revert InvalidSignature(txHash, _signature);\n    }\n\n    // Execute the transactions\n    _execute(txHash, _txs);\n  }\n\n  /**\n   * @notice Allow wallet to execute an action\n   *   without signing the message\n   * @param _txs  Transactions to execute\n   */\n  function selfExecute(\n    Transaction[] calldata _txs\n  ) external override virtual onlySelf {\n    // Hash transaction bundle\n    bytes32 txHash = SequenceBaseSig.subdigest(\n      keccak256(\n        abi.encode('self:', _txs)\n      )\n    );\n\n    // Execute the transactions\n    _execute(txHash, _txs);\n  }\n\n  /**\n   * @notice Executes a list of transactions\n   * @param _txHash  Hash of the batch of transactions\n   * @param _txs  Transactions to execute\n   */\n  function _execute(\n    bytes32 _txHash,\n    Transaction[] calldata _txs\n  ) private {\n    unchecked {\n      // Execute transaction\n      uint256 size = _txs.length;\n      for (uint256 i = 0; i < size; i++) {\n        Transaction calldata transaction = _txs[i];\n        uint256 gasLimit = transaction.gasLimit;\n\n        if (gasleft() < gasLimit) revert NotEnoughGas(i, gasLimit, gasleft());\n\n        bool success;\n        if (transaction.delegateCall) {\n          success = LibOptim.delegatecall(\n            transaction.target,\n            gasLimit == 0 ? gasleft() : gasLimit,\n            transaction.data\n          );\n        } else {\n          success = LibOptim.call(\n            transaction.target,\n            transaction.value,\n            gasLimit == 0 ? gasleft() : gasLimit,\n            transaction.data\n          );\n        }\n\n        if (success) {\n          emit TxExecuted(_txHash, i);\n        } else {\n          // Avoid copy of return data until neccesary\n          _revertBytes(\n            transaction.revertOnError,\n            _txHash,\n            i,\n            LibOptim.returnData()\n          );\n        }\n      }\n    }\n  }\n\n  /**\n   * @notice Logs a failed transaction, reverts if the transaction is not optional\n   * @param _revertOnError  Signals if it should revert or just log\n   * @param _txHash         Hash of the transaction\n   * @param _index          Index of the transaction in the batch\n   * @param _reason         Encoded revert message\n   */\n  function _revertBytes(\n    bool _revertOnError,\n    bytes32 _txHash,\n    uint256 _index,\n    bytes memory _reason\n  ) internal {\n    if (_revertOnError) {\n      assembly { revert(add(_reason, 0x20), mload(_reason)) }\n    } else {\n      emit TxFailed(_txHash, _index, _reason);\n    }\n  }\n\n  /**\n   * @notice Query if a contract implements an interface\n   * @param _interfaceID The interface identifier, as specified in ERC-165\n   * @return `true` if the contract implements `_interfaceID`\n   */\n  function supportsInterface(bytes4 _interfaceID) public override virtual pure returns (bool) {\n    if (_interfaceID == type(IModuleCalls).interfaceId) {\n      return true;\n    }\n\n    return super.supportsInterface(_interfaceID);\n  }\n}\n"
  },
  {
    "path": "contracts/modules/commons/ModuleCreator.sol",
    "content": "// SPDX-License-Identifier: Apache-2.0\npragma solidity 0.8.18;\n\nimport \"./interfaces/IModuleCreator.sol\";\n\nimport \"./ModuleSelfAuth.sol\";\nimport \"./ModuleERC165.sol\";\n\n\ncontract ModuleCreator is IModuleCreator, ModuleERC165, ModuleSelfAuth {\n  event CreatedContract(address _contract);\n\n  /**\n   * @notice Creates a contract forwarding eth value\n   * @param _code Creation code of the contract\n   * @return addr The address of the created contract\n   */\n  function createContract(bytes memory _code) public override virtual payable onlySelf returns (address addr) {\n    assembly { addr := create(callvalue(), add(_code, 32), mload(_code)) }\n    if (addr == address(0)) revert CreateFailed(_code);\n    emit CreatedContract(addr);\n  }\n\n  /**\n   * @notice Query if a contract implements an interface\n   * @param _interfaceID The interface identifier, as specified in ERC-165\n   * @return `true` if the contract implements `_interfaceID`\n   */\n  function supportsInterface(bytes4 _interfaceID) public override virtual pure returns (bool) {\n    if (_interfaceID == type(IModuleCreator).interfaceId) {\n      return true;\n    }\n\n    return super.supportsInterface(_interfaceID);\n  }\n}\n"
  },
  {
    "path": "contracts/modules/commons/ModuleERC165.sol",
    "content": "// SPDX-License-Identifier: Apache-2.0\npragma solidity 0.8.18;\n\n\nabstract contract ModuleERC165 {\n  /**\n   * @notice Query if a contract implements an interface\n   * @param _interfaceID The interface identifier, as specified in ERC-165\n   * @dev Adding new hooks will not lead to them being reported by this function\n   *      without upgrading the wallet. In addition, developers must ensure that\n   *      all inherited contracts by the main module don't conflict and are accounted\n   *      to be supported by the supportsInterface method.\n   * @return `true` if the contract implements `_interfaceID`\n   */\n  function supportsInterface(bytes4 _interfaceID) virtual public pure returns (bool) {\n    return _interfaceID == this.supportsInterface.selector;\n  }\n}\n"
  },
  {
    "path": "contracts/modules/commons/ModuleERC5719.sol",
    "content": "// SPDX-License-Identifier: Apache-2.0\npragma solidity 0.8.18;\n\nimport \"./ModuleIPFS.sol\";\n\nimport \"../../utils/LibString.sol\";\n\n\ncontract ModuleERC5719 is ModuleIPFS {\n  function getAlternativeSignature(bytes32 _digest) external view returns (string memory) {\n    return string(\n      abi.encodePacked(\n        ipfsRoot(),\n        \"/ERC5719/\",\n        LibString.prefixHexadecimal(\n          LibString.bytesToHexadecimal(\n            abi.encodePacked(_digest)\n          )\n        )\n      )\n    );\n  }\n}\n"
  },
  {
    "path": "contracts/modules/commons/ModuleExtraAuth.sol",
    "content": "// SPDX-License-Identifier: Apache-2.0\npragma solidity 0.8.18;\n\nimport \"./ModuleAuth.sol\";\nimport \"./ModuleStorage.sol\";\nimport \"./ModuleSelfAuth.sol\";\nimport \"./ModuleERC165.sol\";\n\n\nabstract contract ModuleExtraAuth is ModuleERC165, ModuleSelfAuth, ModuleAuth {\n  //                       EXTRA_IMAGE_HASH_KEY = keccak256(\"org.sequence.module.static.auth.extra.image.hash\");\n  bytes32 private constant EXTRA_IMAGE_HASH_KEY = bytes32(0x849e7bdc245db17e50b9f43086f1914d70eb4dab6dd89af4d541d53353ad97de);\n\n  event SetExtraImageHash(bytes32 indexed _imageHash, uint256 _expiration);\n\n  function _writeExpirationForImageHash(bytes32 _imageHash, uint256 _expiration) internal {\n    ModuleStorage.writeBytes32Map(EXTRA_IMAGE_HASH_KEY, _imageHash, bytes32(_expiration));\n  }\n\n  function _readExpirationForImageHash(bytes32 _imageHash) internal view returns (uint256 _expiration) {\n    return uint256(ModuleStorage.readBytes32Map(EXTRA_IMAGE_HASH_KEY, _imageHash));\n  }\n\n  function _isValidImage(bytes32 _imageHash) internal override virtual view returns (bool) {\n    if (super._isValidImage(_imageHash)) {\n      return true;\n    }\n\n    uint256 expiration = _readExpirationForImageHash(_imageHash);\n\n    // solhint-disable-next-line not-rely-on-time\n    return expiration != 0 && expiration > block.timestamp;\n  }\n\n  function extraImageHash(bytes32 _imageHash) public view returns (uint256) {\n    return _readExpirationForImageHash(_imageHash);\n  }\n\n  function setExtraImageHash(bytes32 _imageHash, uint256 _expiration) external onlySelf {\n    _writeExpirationForImageHash(_imageHash, _expiration);\n\n    emit SetExtraImageHash(_imageHash, _expiration);\n  }\n\n  function clearExtraImageHashes(bytes32[] calldata _imageHashes) external onlySelf {\n    unchecked {\n      uint256 imageHashesLength = _imageHashes.length;\n      for (uint256 i = 0; i < imageHashesLength; i++) {\n        bytes32 imageHash = _imageHashes[i];\n        _writeExpirationForImageHash(imageHash, 0);\n\n       emit SetExtraImageHash(imageHash, 0);\n      }\n    }\n  }\n\n  /**\n   * @notice Query if a contract implements an interface\n   * @param _interfaceID The interface identifier, as specified in ERC-165\n   * @return `true` if the contract implements `_interfaceID`\n   */\n  function supportsInterface(bytes4 _interfaceID) public override (\n    ModuleERC165,\n    ModuleAuth\n  ) virtual pure returns (bool) {\n    if (_interfaceID == type(ModuleExtraAuth).interfaceId) {\n      return true;\n    }\n\n    return super.supportsInterface(_interfaceID);\n  }\n}\n"
  },
  {
    "path": "contracts/modules/commons/ModuleHooks.sol",
    "content": "// SPDX-License-Identifier: Apache-2.0\npragma solidity 0.8.18;\n\nimport \"./interfaces/IModuleHooks.sol\";\n\nimport \"./ModuleSelfAuth.sol\";\nimport \"./ModuleStorage.sol\";\nimport \"./ModuleERC165.sol\";\n\nimport \"../../interfaces/receivers/IERC1155Receiver.sol\";\nimport \"../../interfaces/receivers/IERC721Receiver.sol\";\nimport \"../../interfaces/receivers/IERC223Receiver.sol\";\n\n\ncontract ModuleHooks is IERC1155Receiver, IERC721Receiver, IModuleHooks, ModuleERC165, ModuleSelfAuth {\n  //                       HOOKS_KEY = keccak256(\"org.arcadeum.module.hooks.hooks\");\n  bytes32 private constant HOOKS_KEY = bytes32(0xbe27a319efc8734e89e26ba4bc95f5c788584163b959f03fa04e2d7ab4b9a120);\n\n  /**\n   * @notice Reads the implementation hook of a signature\n   * @param _signature Signature function\n   * @return The address of the implementation hook, address(0) if none\n  */\n  function readHook(bytes4 _signature) external override virtual view returns (address) {\n    return _readHook(_signature);\n  }\n\n  /**\n   * @notice Adds a new hook to handle a given function selector\n   * @param _signature Signature function linked to the hook\n   * @param _implementation Hook implementation contract\n   * @dev Can't overwrite hooks that are part of the main module (those defined below)\n   */\n  function addHook(bytes4 _signature, address _implementation) external override virtual onlySelf {\n    if (_readHook(_signature) != address(0)) revert HookAlreadyExists(_signature);\n    _writeHook(_signature, _implementation);\n  }\n\n  /**\n   * @notice Removes a registered hook\n   * @param _signature Signature function linked to the hook\n   * @dev Can't remove hooks that are part of the main module (those defined below)\n   *      without upgrading the wallet\n   */\n  function removeHook(bytes4 _signature) external override virtual onlySelf {\n    if (_readHook(_signature) == address(0)) revert HookDoesNotExist(_signature);\n    _writeHook(_signature, address(0));\n  }\n\n  /**\n   * @notice Reads the implementation hook of a signature\n   * @param _signature Signature function\n   * @return The address of the implementation hook, address(0) if none\n  */\n  function _readHook(bytes4 _signature) private view returns (address) {\n    return address(uint160(uint256(ModuleStorage.readBytes32Map(HOOKS_KEY, _signature))));\n  }\n\n  /**\n   * @notice Writes the implementation hook of a signature\n   * @param _signature Signature function\n   * @param _implementation Hook implementation contract\n  */\n  function _writeHook(bytes4 _signature, address _implementation) private {\n    ModuleStorage.writeBytes32Map(HOOKS_KEY, _signature, bytes32(uint256(uint160(_implementation))));\n    emit DefinedHook(_signature, _implementation);\n  }\n\n  /**\n   * @notice Handle the receipt of a single ERC1155 token type.\n   * @return `bytes4(keccak256(\"onERC1155Received(address,address,uint256,uint256,bytes)\"))`\n   */\n  function onERC1155Received(\n    address,\n    address,\n    uint256,\n    uint256,\n    bytes calldata\n  ) external override virtual returns (bytes4) {\n    return ModuleHooks.onERC1155Received.selector;\n  }\n\n  /**\n   * @notice Handle the receipt of multiple ERC1155 token types.\n   * @return `bytes4(keccak256(\"onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)\"))`\n   */\n  function onERC1155BatchReceived(\n    address,\n    address,\n    uint256[] calldata,\n    uint256[] calldata,\n    bytes calldata\n  ) external override virtual returns (bytes4) {\n    return ModuleHooks.onERC1155BatchReceived.selector;\n  }\n\n  /**\n   * @notice Handle the receipt of a single ERC721 token.\n   * @return `bytes4(keccak256(\"onERC721Received(address,address,uint256,bytes)\"))`\n   */\n  function onERC721Received(address, address, uint256, bytes calldata) external override virtual returns (bytes4) {\n    return ModuleHooks.onERC721Received.selector;\n  }\n\n  /**\n   * @notice Routes fallback calls through hooks\n   */\n  fallback() external payable {\n    if (msg.data.length >= 4) {\n      address target = _readHook(msg.sig);\n      if (target != address(0)) {\n        (bool success, bytes memory result) = target.delegatecall(msg.data);\n        assembly {\n          if iszero(success)  {\n            revert(add(result, 0x20), mload(result))\n          }\n\n          return(add(result, 0x20), mload(result))\n        }\n      }\n    }\n  }\n\n  /**\n   * @notice Allows the wallet to receive ETH\n   */\n  receive() external payable { }\n\n  /**\n   * @notice Query if a contract implements an interface\n   * @param _interfaceID The interface identifier, as specified in ERC-165\n   * @return `true` if the contract implements `_interfaceID`\n   */\n  function supportsInterface(bytes4 _interfaceID) public override virtual pure returns (bool) {\n    if (\n      _interfaceID == type(IModuleHooks).interfaceId ||\n      _interfaceID == type(IERC1155Receiver).interfaceId ||\n      _interfaceID == type(IERC721Receiver).interfaceId ||\n      _interfaceID == type(IERC223Receiver).interfaceId\n    ) {\n      return true;\n    }\n\n    return super.supportsInterface(_interfaceID);\n  }\n}\n"
  },
  {
    "path": "contracts/modules/commons/ModuleIPFS.sol",
    "content": "// SPDX-License-Identifier: Apache-2.0\npragma solidity 0.8.18;\n\nimport \"./ModuleSelfAuth.sol\";\nimport \"./ModuleStorage.sol\";\n\nimport \"../../utils/LibString.sol\";\n\n\ncontract ModuleIPFS is ModuleSelfAuth {\n  event IPFSRootUpdated(bytes32 _hash);\n\n  //                       IPFS_ROOT_KEY = keccak256(\"sequence.ipfs.root\")\n  bytes32 private constant IPFS_ROOT_KEY = bytes32(0x0eecac93ced8722d209199364cda3bc33da3bc3a23daef6be49ebd780511d033);\n\n  function ipfsRootBytes32() public view returns (bytes32) {\n    return ModuleStorage.readBytes32(IPFS_ROOT_KEY);\n  }\n\n  function ipfsRoot() public view returns (string memory) {\n    return string(\n      abi.encodePacked(\n        \"ipfs://\",\n        LibString.prefixBase32(\n          LibString.bytesToBase32(\n            abi.encodePacked(\n              hex'01701220',\n              ipfsRootBytes32()\n            )\n          )\n        )\n      )\n    );\n  }\n\n  function updateIPFSRoot(bytes32 _hash) external onlySelf {\n    _updateIPFSRoot(_hash);\n  }\n\n  function _updateIPFSRoot(bytes32 _hash) internal {\n    ModuleStorage.writeBytes32(IPFS_ROOT_KEY, _hash);\n    emit IPFSRootUpdated(_hash);\n  }\n}\n"
  },
  {
    "path": "contracts/modules/commons/ModuleNonce.sol",
    "content": "// SPDX-License-Identifier: Apache-2.0\npragma solidity 0.8.18;\n\nimport \"./ModuleStorage.sol\";\n\nimport \"./submodules/nonce/SubModuleNonce.sol\";\n\n\ncontract ModuleNonce {\n  // Events\n  event NonceChange(uint256 _space, uint256 _newNonce);\n\n  // Errors\n  error BadNonce(uint256 _space, uint256 _provided, uint256 _current);\n\n  //                       NONCE_KEY = keccak256(\"org.arcadeum.module.calls.nonce\");\n  bytes32 private constant NONCE_KEY = bytes32(0x8d0bf1fd623d628c741362c1289948e57b3e2905218c676d3e69abee36d6ae2e);\n\n  /**\n   * @notice Returns the next nonce of the default nonce space\n   * @dev The default nonce space is 0x00\n   * @return The next nonce\n   */\n  function nonce() external virtual view returns (uint256) {\n    return readNonce(0);\n  }\n\n  /**\n   * @notice Returns the next nonce of the given nonce space\n   * @param _space Nonce space, each space keeps an independent nonce count\n   * @return The next nonce\n   */\n  function readNonce(uint256 _space) public virtual view returns (uint256) {\n    return uint256(ModuleStorage.readBytes32Map(NONCE_KEY, bytes32(_space)));\n  }\n\n  /**\n   * @notice Changes the next nonce of the given nonce space\n   * @param _space Nonce space, each space keeps an independent nonce count\n   * @param _nonce Nonce to write on the space\n   */\n  function _writeNonce(uint256 _space, uint256 _nonce) internal {\n    ModuleStorage.writeBytes32Map(NONCE_KEY, bytes32(_space), bytes32(_nonce));\n  }\n\n  /**\n   * @notice Verify if a nonce is valid\n   * @param _rawNonce Nonce to validate (may contain an encoded space)\n   */\n  function _validateNonce(uint256 _rawNonce) internal virtual {\n    // Retrieve current nonce for this wallet\n    (uint256 space, uint256 providedNonce) = SubModuleNonce.decodeNonce(_rawNonce);\n\n    uint256 currentNonce = readNonce(space);\n    if (currentNonce != providedNonce) {\n      revert BadNonce(space, providedNonce, currentNonce);\n    }\n\n    unchecked {\n      uint256 newNonce = providedNonce + 1;\n\n      _writeNonce(space, newNonce);\n      emit NonceChange(space, newNonce);\n      return;\n    }\n  }\n}\n"
  },
  {
    "path": "contracts/modules/commons/ModuleOnlyDelegatecall.sol",
    "content": "// SPDX-License-Identifier: Apache-2.0\npragma solidity 0.8.18;\n\n\ncontract ModuleOnlyDelegatecall {\n  address private immutable self;\n\n  error OnlyDelegatecall();\n\n  constructor() {\n    self = address(this);\n  }\n\n  /**\n   * @notice Modifier that only allows functions to be called via delegatecall.\n   */\n  modifier onlyDelegatecall() {\n    if (address(this) == self) {\n      revert OnlyDelegatecall();\n    }\n    _;\n  }\n}\n"
  },
  {
    "path": "contracts/modules/commons/ModuleSelfAuth.sol",
    "content": "// SPDX-License-Identifier: Apache-2.0\npragma solidity 0.8.18;\n\n\ncontract ModuleSelfAuth {\n  error OnlySelfAuth(address _sender, address _self);\n\n  modifier onlySelf() {\n    if (msg.sender != address(this)) {\n      revert OnlySelfAuth(msg.sender, address(this));\n    }\n    _;\n  }\n}\n"
  },
  {
    "path": "contracts/modules/commons/ModuleStorage.sol",
    "content": "// SPDX-License-Identifier: Apache-2.0\npragma solidity 0.8.18;\n\n\nlibrary ModuleStorage {\n  function writeBytes32(bytes32 _key, bytes32 _val) internal {\n    assembly { sstore(_key, _val) }\n  }\n\n  function readBytes32(bytes32 _key) internal view returns (bytes32 val) {\n    assembly { val := sload(_key) }\n  }\n\n  function writeBytes32Map(bytes32 _key, bytes32 _subKey, bytes32 _val) internal {\n    bytes32 key = keccak256(abi.encode(_key, _subKey));\n    assembly { sstore(key, _val) }\n  }\n\n  function readBytes32Map(bytes32 _key, bytes32 _subKey) internal view returns (bytes32 val) {\n    bytes32 key = keccak256(abi.encode(_key, _subKey));\n    assembly { val := sload(key) }\n  }\n}\n"
  },
  {
    "path": "contracts/modules/commons/ModuleUpdate.sol",
    "content": "// SPDX-License-Identifier: Apache-2.0\npragma solidity 0.8.18;\n\nimport \"./interfaces/IModuleUpdate.sol\";\n\nimport \"./Implementation.sol\";\nimport \"./ModuleSelfAuth.sol\";\nimport \"./ModuleERC165.sol\";\n\nimport \"../../utils/LibAddress.sol\";\n\n\ncontract ModuleUpdate is IModuleUpdate, ModuleERC165, ModuleSelfAuth, Implementation {\n  using LibAddress for address;\n\n  event ImplementationUpdated(address newImplementation);\n\n  /**\n   * @notice Updates the implementation of the base wallet\n   * @param _implementation New main module implementation\n   * @dev WARNING Updating the implementation can brick the wallet\n   */\n  function updateImplementation(address _implementation) external override virtual onlySelf {\n    _updateImplementation(_implementation);\n  }\n\n  /**\n   * @notice Updates the implementation of the base wallet, used internally.\n   * @param _implementation New main module implementation\n   * @dev WARNING Updating the implementation can brick the wallet\n   */\n  function _updateImplementation(address _implementation) internal override virtual {\n    if (!_implementation.isContract()) revert InvalidImplementation(_implementation);\n    _setImplementation(_implementation);\n    emit ImplementationUpdated(_implementation);\n  }\n\n  /**\n   * @notice Query if a contract implements an interface\n   * @param _interfaceID The interface identifier, as specified in ERC-165\n   * @return `true` if the contract implements `_interfaceID`\n   */\n  function supportsInterface(bytes4 _interfaceID) public override virtual pure returns (bool) {\n    if (_interfaceID == type(IModuleUpdate).interfaceId) {\n      return true;\n    }\n\n    return super.supportsInterface(_interfaceID);\n  }\n}\n"
  },
  {
    "path": "contracts/modules/commons/gas-estimation/ModuleIgnoreAuthUpgradable.sol",
    "content": "// SPDX-License-Identifier: Apache-2.0\npragma solidity 0.8.18;\n\nimport \"./../ModuleAuthUpgradable.sol\";\n\n\n/**\n  @notice Implements ModuleAuthUpgradable but ignores the validity of the signature\n    should only be used during gas estimation.\n*/\nabstract contract ModuleIgnoreAuthUpgradable is ModuleAuthUpgradable {\n  /**\n   * @notice Removes the signature validation from the module, by returning true for any _imageHash\n   * @param _imageHash Hash image of signature\n   * @return true always\n   */\n  function _isValidImage(bytes32 _imageHash) internal override(ModuleAuthUpgradable) virtual view returns (bool) {\n    // Still validates the imageHash using the original mechanism for a more acurate estimation\n    return super._isValidImage(_imageHash) || true;\n  }\n}\n"
  },
  {
    "path": "contracts/modules/commons/gas-estimation/ModuleIgnoreNonceCalls.sol",
    "content": "// SPDX-License-Identifier: Apache-2.0\npragma solidity 0.8.18;\n\nimport \"./../ModuleCalls.sol\";\n\nimport \"./../submodules/nonce/SubModuleNonce.sol\";\n\n\n/**\n  @notice Implements ModuleCalls but ignores the validity of the nonce\n    should only be used during gas estimation.\n*/\nabstract contract ModuleIgnoreNonceCalls is ModuleCalls {\n\n  /**\n   * @notice Verify if a nonce is valid\n   * @param _rawNonce Nonce to validate (may contain an encoded space)\n   */\n  function _validateNonce(uint256 _rawNonce) internal override virtual {\n    // Retrieve current nonce for this wallet\n    (uint256 space, uint256 providedNonce) = SubModuleNonce.decodeNonce(_rawNonce);\n\n    uint256 currentNonce = readNonce(space);\n    if (currentNonce != providedNonce && false) {\n      revert BadNonce(space, providedNonce, currentNonce);\n    }\n\n    unchecked {\n      uint256 newNonce = providedNonce + 1;\n\n      _writeNonce(space, newNonce);\n      emit NonceChange(space, newNonce);\n      return;\n    }\n  }\n}\n"
  },
  {
    "path": "contracts/modules/commons/interfaces/IModuleAuth.sol",
    "content": "// SPDX-License-Identifier: Apache-2.0\npragma solidity 0.8.18;\n\n\nabstract contract IModuleAuth {\n  //                        IMAGE_HASH_KEY = keccak256(\"org.arcadeum.module.auth.upgradable.image.hash\");\n  bytes32 internal constant IMAGE_HASH_KEY = bytes32(0xea7157fa25e3aa17d0ae2d5280fa4e24d421c61842aa85e45194e1145aa72bf8);\n\n  event ImageHashUpdated(bytes32 newImageHash);\n\n  // Errors\n  error ImageHashIsZero();\n  error InvalidSignatureType(bytes1 _type);\n\n  function _signatureValidation(\n    bytes32 _digest,\n    bytes calldata _signature\n  ) internal virtual view returns (\n    bool isValid,\n    bytes32 subdigest\n  );\n\n  function signatureRecovery(\n    bytes32 _digest,\n    bytes calldata _signature\n  ) public virtual view returns (\n    uint256 threshold,\n    uint256 weight,\n    bytes32 imageHash,\n    bytes32 subdigest,\n    uint256 checkpoint\n  );\n\n  /**\n   * @notice Validates the signature image\n   * @return true if the signature image is valid\n   */\n  function _isValidImage(bytes32) internal virtual view returns (bool) {\n    return false;\n  }\n\n  /**\n   * @notice Updates the signers configuration of the wallet\n   * @param _imageHash New required image hash of the signature\n   */\n  function updateImageHash(bytes32 _imageHash) external virtual;\n\n  /**\n   * @notice Updates the signers configuration of the wallet\n   * @param _imageHash New required image hash of the signature\n   */\n  function _updateImageHash(bytes32 _imageHash) internal virtual;\n}\n"
  },
  {
    "path": "contracts/modules/commons/interfaces/IModuleAuthUpgradable.sol",
    "content": "// SPDX-License-Identifier: Apache-2.0\npragma solidity 0.8.18;\n\n\ninterface IModuleAuthUpgradable {\n  /**\n   * @notice Returns the current image hash of the wallet\n   */\n  function imageHash() external view returns (bytes32);\n}\n"
  },
  {
    "path": "contracts/modules/commons/interfaces/IModuleCalls.sol",
    "content": "// SPDX-License-Identifier: Apache-2.0\npragma solidity 0.8.18;\n\n\ninterface IModuleCalls {\n  // Events\n  event TxFailed(bytes32 indexed _tx, uint256 _index, bytes _reason);\n  event TxExecuted(bytes32 indexed _tx, uint256 _index);\n\n  // Errors\n  error NotEnoughGas(uint256 _index, uint256 _requested, uint256 _available);\n  error InvalidSignature(bytes32 _hash, bytes _signature);\n\n  // Transaction structure\n  struct Transaction {\n    bool delegateCall;   // Performs delegatecall\n    bool revertOnError;  // Reverts transaction bundle if tx fails\n    uint256 gasLimit;    // Maximum gas to be forwarded\n    address target;      // Address of the contract to call\n    uint256 value;       // Amount of ETH to pass with the call\n    bytes data;          // calldata to pass\n  }\n\n  /**\n   * @notice Allow wallet owner to execute an action\n   * @param _txs        Transactions to process\n   * @param _nonce      Signature nonce (may contain an encoded space)\n   * @param _signature  Encoded signature\n   */\n  function execute(\n    Transaction[] calldata _txs,\n    uint256 _nonce,\n    bytes calldata _signature\n  ) external;\n\n  /**\n   * @notice Allow wallet to execute an action\n   *   without signing the message\n   * @param _txs  Transactions to execute\n   */\n  function selfExecute(\n    Transaction[] calldata _txs\n  ) external;\n}\n"
  },
  {
    "path": "contracts/modules/commons/interfaces/IModuleCreator.sol",
    "content": "// SPDX-License-Identifier: Apache-2.0\npragma solidity 0.8.18;\n\n\ninterface IModuleCreator {\n  error CreateFailed(bytes _code);\n\n  /**\n   * @notice Creates a contract forwarding eth value\n   * @param _code Creation code of the contract\n   * @return addr The address of the created contract\n   */\n  function createContract(bytes calldata _code) external payable returns (address addr);\n}\n"
  },
  {
    "path": "contracts/modules/commons/interfaces/IModuleHooks.sol",
    "content": "// SPDX-License-Identifier: Apache-2.0\npragma solidity 0.8.18;\n\n\ninterface IModuleHooks {\n  // Errors\n  error HookAlreadyExists(bytes4 _signature);\n  error HookDoesNotExist(bytes4 _signature);\n\n  // Events\n  event DefinedHook(bytes4 _signature, address _implementation);\n\n  /**\n   * @notice Reads the implementation hook of a signature\n   * @param _signature Signature function\n   * @return The address of the implementation hook, address(0) if none\n  */\n  function readHook(bytes4 _signature) external view returns (address);\n\n  /**\n   * @notice Adds a new hook to handle a given function selector\n   * @param _signature Signature function linked to the hook\n   * @param _implementation Hook implementation contract\n   */\n  function addHook(bytes4 _signature, address _implementation) external;\n\n  /**\n   * @notice Removes a registered hook\n   * @param _signature Signature function linked to the hook\n   */\n  function removeHook(bytes4 _signature) external;\n}\n"
  },
  {
    "path": "contracts/modules/commons/interfaces/IModuleUpdate.sol",
    "content": "// SPDX-License-Identifier: Apache-2.0\npragma solidity 0.8.18;\n\n\nabstract contract IModuleUpdate {\n  // Errors\n  error InvalidImplementation(address _implementation);\n\n  /**\n   * @notice Updates the implementation of the base wallet\n   * @param _implementation New main module implementation\n   * @dev WARNING Updating the implementation can brick the wallet\n   */\n  function updateImplementation(address _implementation) external virtual;\n\n  /**\n   * @notice Updates the implementation of the base wallet, used internally.\n   * @param _implementation New main module implementation\n   * @dev WARNING Updating the implementation can brick the wallet\n   */\n  function _updateImplementation(address _implementation) internal virtual;\n}\n"
  },
  {
    "path": "contracts/modules/commons/submodules/auth/SequenceBaseSig.sol",
    "content": "// SPDX-License-Identifier: Apache-2.0\npragma solidity 0.8.18;\n\nimport \"../../../../utils/SignatureValidator.sol\";\nimport \"../../../../utils/LibBytesPointer.sol\";\nimport \"../../../../utils/LibBytes.sol\";\nimport \"../../../../utils/LibOptim.sol\";\n\n\n/**\n * @title SequenceBaseSig Library\n * @author Agustin Aguilar (aa@horizon.io)\n * @notice A Solidity implementation for handling signatures in the Sequence protocol.\n */\nlibrary SequenceBaseSig {\n  using LibBytesPointer for bytes;\n\n  uint256 internal constant FLAG_SIGNATURE = 0;\n  uint256 internal constant FLAG_ADDRESS = 1;\n  uint256 internal constant FLAG_DYNAMIC_SIGNATURE = 2;\n  uint256 internal constant FLAG_NODE = 3;\n  uint256 internal constant FLAG_BRANCH = 4;\n  uint256 internal constant FLAG_SUBDIGEST = 5;\n  uint256 internal constant FLAG_NESTED = 6;\n\n  error InvalidNestedSignature(bytes32 _hash, address _addr, bytes _signature);\n  error InvalidSignatureFlag(uint256 _flag);\n\n  /**\n  * @notice Generates a subdigest for the input digest (unique for this wallet and network).\n  * @param _digest The input digest to generate the subdigest from.\n  * @return bytes32 The subdigest generated from the input digest.\n  */\n  function subdigest(\n    bytes32 _digest\n  ) internal view returns (bytes32) {\n    return keccak256(\n      abi.encodePacked(\n        \"\\x19\\x01\",\n        block.chainid,\n        address(this),\n        _digest\n      )\n    );\n  }\n\n  /**\n  * @notice Generates the leaf for an address and weight.\n  * @dev The leaf is generated by concatenating the address and weight.\n  *\n  * @param _addr The address to generate the leaf for.\n  * @param _weight The weight to generate the leaf for.\n  * @return bytes32 The leaf generated from the address and weight.\n  */\n  function _leafForAddressAndWeight(\n    address _addr,\n    uint96 _weight\n  ) internal pure returns (bytes32) {\n    unchecked {\n      return bytes32(uint256(_weight) << 160 | uint256(uint160(_addr)));\n    }\n  }\n\n  /**\n  * @notice Generates the leaf for a hardcoded subdigest.\n  * @dev The leaf is generated by hashing 'Sequence static digest:\\n' and the subdigest.\n  * @param _subdigest The subdigest to generate the leaf for.\n  * @return bytes32 The leaf generated from the hardcoded subdigest.\n  */\n  function _leafForHardcodedSubdigest(\n    bytes32 _subdigest\n  ) internal pure returns (bytes32) {\n    return keccak256(abi.encodePacked('Sequence static digest:\\n', _subdigest));\n  }\n\n  /**\n  * @notice Generates the leaf for a nested tree node.\n  * @dev The leaf is generated by hashing 'Sequence nested config:\\n', the node, the threshold and the weight.\n  *\n  * @param _node The root of the node to generate the leaf for.\n  * @param _threshold The internal threshold of the tree.\n  * @param _weight The external weight of the tree.\n  * @return bytes32 The leaf generated from the nested tree.\n  */\n  function _leafForNested(\n    bytes32 _node,\n    uint256 _threshold,\n    uint256 _weight\n  ) internal pure returns (bytes32) {\n    return keccak256(abi.encodePacked('Sequence nested config:\\n', _node, _threshold, _weight));\n  }\n\n  /**\n   * @notice Returns the weight and root of a signature branch.\n   * @dev If the signature contains a hardcoded subdigest, and it matches the input digest, then the weight is set to 2 ** 256 - 1.\n   *\n   * @param _subdigest The digest to verify the signature against.\n   * @param _signature The signature branch to recover.\n   * @return weight The total weight of the recovered signatures.\n   * @return root The root hash of the recovered configuration.\n   */\n  function recoverBranch(\n    bytes32 _subdigest,\n    bytes calldata _signature\n  ) internal view returns (\n    uint256 weight,\n    bytes32 root\n  ) {\n    unchecked {\n      uint256 rindex;\n\n      // Iterate until the image is completed\n      while (rindex < _signature.length) {\n        // Read next item type\n        uint256 flag;\n        (flag, rindex) = _signature.readUint8(rindex);\n\n        if (flag == FLAG_ADDRESS) {\n          // Read plain address\n          uint8 addrWeight; address addr;\n          (addrWeight, addr, rindex) = _signature.readUint8Address(rindex);\n\n          // Write weight and address to image\n          bytes32 node = _leafForAddressAndWeight(addr, addrWeight);\n          root = root != bytes32(0) ? LibOptim.fkeccak256(root, node) : node;\n          continue;\n        }\n\n        if (flag == FLAG_SIGNATURE) {\n          // Read weight\n          uint8 addrWeight;\n          (addrWeight, rindex) = _signature.readUint8(rindex);\n\n          // Read single signature and recover signer\n          uint256 nrindex = rindex + 66;\n          address addr = SignatureValidator.recoverSigner(_subdigest, _signature[rindex:nrindex]);\n          rindex = nrindex;\n\n          // Acumulate total weight of the signature\n          weight += addrWeight;\n\n          // Write weight and address to image\n          bytes32 node = _leafForAddressAndWeight(addr, addrWeight);\n          root = root != bytes32(0) ? LibOptim.fkeccak256(root, node) : node;\n          continue;\n        }\n\n        if (flag == FLAG_DYNAMIC_SIGNATURE) {\n          // Read signer and weight\n          uint8 addrWeight; address addr;\n          (addrWeight, addr, rindex) = _signature.readUint8Address(rindex);\n\n          // Read signature size\n          uint256 size;\n          (size, rindex) = _signature.readUint24(rindex);\n\n          // Read dynamic size signature\n          uint256 nrindex = rindex + size;\n          if (!SignatureValidator.isValidSignature(_subdigest, addr, _signature[rindex:nrindex])) {\n            revert InvalidNestedSignature(_subdigest, addr, _signature[rindex:nrindex]);\n          }\n          rindex = nrindex;\n\n          // Acumulate total weight of the signature\n          weight += addrWeight;\n\n          // Write weight and address to image\n          bytes32 node = _leafForAddressAndWeight(addr, addrWeight);\n          root = root != bytes32(0) ? LibOptim.fkeccak256(root, node) : node;\n          continue;\n        }\n\n        if (flag == FLAG_NODE) {\n          // Read node hash\n          bytes32 node;\n          (node, rindex) = _signature.readBytes32(rindex);\n          root = root != bytes32(0) ? LibOptim.fkeccak256(root, node) : node;\n          continue;\n        }\n\n        if (flag == FLAG_BRANCH) {\n          // Enter a branch of the signature merkle tree\n          uint256 size;\n          (size, rindex) = _signature.readUint24(rindex);\n          uint256 nrindex = rindex + size;\n\n          uint256 nweight; bytes32 node;\n          (nweight, node) = recoverBranch(_subdigest, _signature[rindex:nrindex]);\n\n          weight += nweight;\n          root = LibOptim.fkeccak256(root, node);\n\n          rindex = nrindex;\n          continue;\n        }\n\n        if (flag == FLAG_NESTED) {\n          // Enter a branch of the signature merkle tree\n          // but with an internal threshold and an external fixed weight\n          uint256 externalWeight;\n          (externalWeight, rindex) = _signature.readUint8(rindex);\n\n          uint256 internalThreshold;\n          (internalThreshold, rindex) = _signature.readUint16(rindex);\n\n          uint256 size;\n          (size, rindex) = _signature.readUint24(rindex);\n          uint256 nrindex = rindex + size;\n\n          uint256 internalWeight; bytes32 internalRoot;\n          (internalWeight, internalRoot) = recoverBranch(_subdigest, _signature[rindex:nrindex]);\n          rindex = nrindex;\n\n          if (internalWeight >= internalThreshold) {\n            weight += externalWeight;\n          }\n\n          bytes32 node = _leafForNested(internalRoot, internalThreshold, externalWeight);\n          root = root != bytes32(0) ? LibOptim.fkeccak256(root, node) : node;\n\n          continue;\n        }\n\n        if (flag == FLAG_SUBDIGEST) {\n          // A hardcoded always accepted digest\n          // it pushes the weight to the maximum\n          bytes32 hardcoded;\n          (hardcoded, rindex) = _signature.readBytes32(rindex);\n          if (hardcoded == _subdigest) {\n            weight = type(uint256).max;\n          }\n\n          bytes32 node = _leafForHardcodedSubdigest(hardcoded);\n          root = root != bytes32(0) ? LibOptim.fkeccak256(root, node) : node;\n          continue;\n        }\n\n        revert InvalidSignatureFlag(flag);\n      }\n    }\n  }\n\n  /**\n   * @notice Returns the threshold, weight, root, and checkpoint of a signature.\n   * @dev To verify the signature, the weight must be greater than or equal to the threshold, and the root\n   *      must match the expected `imageHash` of the wallet.\n   *\n   * @param _subdigest The digest to verify the signature against.\n   * @param _signature The signature to recover.\n   * @return threshold The minimum weight required for the signature to be valid.\n   * @return weight The total weight of the recovered signatures.\n   * @return imageHash The root hash of the recovered configuration\n   * @return checkpoint The checkpoint of the signature.\n   */\n  function recover(\n    bytes32 _subdigest,\n    bytes calldata _signature\n  ) internal view returns (\n    uint256 threshold,\n    uint256 weight,\n    bytes32 imageHash,\n    uint256 checkpoint\n  ) {\n    unchecked {\n      (weight, imageHash) = recoverBranch(_subdigest, _signature[6:]);\n\n      // Threshold & checkpoint are the top nodes\n      // (but they are first on the signature)\n      threshold = LibBytes.readFirstUint16(_signature);\n      checkpoint = LibBytes.readUint32(_signature, 2);\n\n      imageHash = LibOptim.fkeccak256(imageHash, bytes32(threshold));\n      imageHash = LibOptim.fkeccak256(imageHash, bytes32(checkpoint));\n    }\n  }\n}\n"
  },
  {
    "path": "contracts/modules/commons/submodules/auth/SequenceChainedSig.sol",
    "content": "// SPDX-License-Identifier: Apache-2.0\npragma solidity 0.8.18;\n\nimport \"./SequenceBaseSig.sol\";\n\nimport \"../../interfaces/IModuleAuth.sol\";\n\nimport \"../../ModuleSelfAuth.sol\";\nimport \"../../ModuleStorage.sol\";\n\nimport \"../../../../utils/LibBytesPointer.sol\";\nimport \"../../../../utils/LibOptim.sol\";\n\n/**\n * @title Sequence chained auth recovery submodule\n * @author Agustin Aguilar (aa@horizon.io)\n * @notice Defines Sequence signatures that work by delegating control to new configurations.\n * @dev The delegations can be chained together, the first signature is the one that is used to validate\n *      the message, the last signature must match the current on-chain configuration of the wallet.\n */\nabstract contract SequenceChainedSig is IModuleAuth, ModuleSelfAuth {\n  using LibBytesPointer for bytes;\n\n  bytes32 public constant SET_IMAGE_HASH_TYPE_HASH = keccak256(\"SetImageHash(bytes32 imageHash)\");\n\n  error LowWeightChainedSignature(bytes _signature, uint256 threshold, uint256 _weight);\n  error WrongChainedCheckpointOrder(uint256 _current, uint256 _prev);\n\n  /**\n   * @notice Defined the special token that must be signed to delegate control to a new configuration.\n   * @param _imageHash The hash of the new configuration.\n   * @return bytes32 The message hash to be signed.\n   */\n  function _hashSetImageHashStruct(bytes32 _imageHash) internal pure returns (bytes32) {\n    return LibOptim.fkeccak256(SET_IMAGE_HASH_TYPE_HASH, _imageHash);\n  }\n\n  /**\n   * @notice Returns the threshold, weight, root, and checkpoint of a (chained) signature.\n   * \n   * @dev This method return the `threshold`, `weight` and `imageHash` of the last signature in the chain.\n   *      Intermediate signatures are validated directly in this method. The `subdigest` is the one of the\n   *      first signature in the chain (since that's the one that is used to validate the message).\n   *\n   * @param _digest The digest to recover the signature from.\n   * @param _signature The signature to recover.\n   * @return threshold The threshold of the (last) signature.\n   * @return weight The weight of the (last) signature.\n   * @return imageHash The image hash of the (last) signature.\n   * @return subdigest The subdigest of the (first) signature in the chain.\n   * @return checkpoint The checkpoint of the (last) signature.\n   */\n  function chainedRecover(\n    bytes32 _digest,\n    bytes calldata _signature\n  ) internal view returns (\n    uint256 threshold,\n    uint256 weight,\n    bytes32 imageHash,\n    bytes32 subdigest,\n    uint256 checkpoint\n  ) {\n    uint256 rindex = 1;\n    uint256 sigSize;\n\n    //\n    // First signature out of the loop\n    //\n\n    // First uint24 is the size of the signature\n    (sigSize, rindex) = _signature.readUint24(rindex);\n    uint256 nrindex = sigSize + rindex;\n\n    (\n      threshold,\n      weight,\n      imageHash,\n      subdigest,\n      checkpoint\n    ) = signatureRecovery(\n      _digest,\n      _signature[rindex:nrindex]\n    );\n\n    if (weight < threshold) {\n      revert LowWeightChainedSignature(_signature[rindex:nrindex], threshold, weight);\n    }\n\n    rindex = nrindex;\n\n    // The following signatures are handled by this loop.\n    // This is done this way because the first signature does not have a\n    // checkpoint to be validated against.\n    while (rindex < _signature.length) {\n      // First uint24 is the size of the signature\n      (sigSize, rindex) = _signature.readUint24(rindex);\n      nrindex = sigSize + rindex;\n\n      uint256 nextCheckpoint;\n\n      (\n        threshold,\n        weight,\n        imageHash,,\n        // Do not change the subdigest;\n        // it should remain that of the first signature.\n        nextCheckpoint\n      ) = signatureRecovery(\n        _hashSetImageHashStruct(imageHash),\n        _signature[rindex:nrindex]\n      );\n\n      // Validate signature\n      if (weight < threshold) {\n        revert LowWeightChainedSignature(_signature[rindex:nrindex], threshold, weight);\n      }\n\n      // Checkpoints must be provided in descending order\n      // since the first signature is the one that is used to validate the message\n      // and the last signature is the one that is used to validate the current configuration\n      if (nextCheckpoint >= checkpoint) {\n        revert WrongChainedCheckpointOrder(nextCheckpoint, checkpoint);\n      }\n\n      checkpoint = nextCheckpoint;\n      rindex = nrindex;\n    }\n  }\n}\n"
  },
  {
    "path": "contracts/modules/commons/submodules/auth/SequenceDynamicSig.sol",
    "content": "// SPDX-License-Identifier: Apache-2.0\npragma solidity 0.8.18;\n\nimport \"./SequenceBaseSig.sol\";\n\n\nlibrary SequenceDynamicSig {\n\n  /**\n   * @notice Recover a \"dynamically encoded\" Sequence signature.\n   * @dev The Signature is stripped of the first byte, which is the encoding flag.\n   *\n   * @param _subdigest The digest of the signature.\n   * @param _signature The Sequence signature.\n   * @return threshold The threshold weight required to validate the signature.\n   * @return weight The weight of the signature.\n   * @return imageHash The hash of the recovered configuration.\n   * @return checkpoint The checkpoint of the configuration.\n   */\n  function recover(\n    bytes32 _subdigest,\n    bytes calldata _signature\n  ) internal view returns (\n    uint256 threshold,\n    uint256 weight,\n    bytes32 imageHash,\n    uint256 checkpoint\n  ) {\n    return SequenceBaseSig.recover(_subdigest, _signature[1:]);\n  }\n}\n"
  },
  {
    "path": "contracts/modules/commons/submodules/auth/SequenceNoChainIdSig.sol",
    "content": "// SPDX-License-Identifier: Apache-2.0\npragma solidity 0.8.18;\n\n\nlibrary SequenceNoChainIdSig {\n\n  /**\n   * @notice Computes a subdigest for a Sequence signature that works on all chains.\n   * @dev The subdigest is computed by removing the chain ID from the digest (using 0 instead).\n   * @param _digest The digest of the chain of signatures.\n   * @return bytes32 The subdigest with no chain ID.\n   */\n  function subdigest(bytes32 _digest) internal view returns (bytes32) {\n    return keccak256(\n      abi.encodePacked(\n        \"\\x19\\x01\",\n        uint256(0),\n        address(this),\n        _digest\n      )\n    );\n  }\n}\n"
  },
  {
    "path": "contracts/modules/commons/submodules/nonce/SubModuleNonce.sol",
    "content": "// SPDX-License-Identifier: Apache-2.0\npragma solidity 0.8.18;\n\n\nlibrary SubModuleNonce {\n  // Nonce schema\n  //\n  // - space[160]:nonce[96]\n  //\n  uint256 internal constant NONCE_BITS = 96;\n  bytes32 internal constant NONCE_MASK = bytes32(uint256(type(uint96).max));\n\n  /**\n   * @notice Decodes a raw nonce\n   * @dev Schema: space[160]:type[96]\n   * @param _rawNonce Nonce to be decoded\n   * @return _space The nonce space of the raw nonce\n   * @return _nonce The nonce of the raw nonce\n   */\n  function decodeNonce(uint256 _rawNonce) internal pure returns (\n    uint256 _space,\n    uint256 _nonce\n  ) {\n    unchecked {\n      // Decode nonce\n      _space = _rawNonce >> NONCE_BITS;\n      _nonce = uint256(bytes32(_rawNonce) & NONCE_MASK);\n    }\n  }\n\n  function encodeNonce(uint256 _space, uint256 _nonce) internal pure returns (uint256) {\n    unchecked {\n      // Combine space and nonce\n      return (_space << NONCE_BITS) | _nonce;\n    }\n  }\n}\n"
  },
  {
    "path": "contracts/modules/utils/GasEstimator.sol",
    "content": "// SPDX-License-Identifier: Apache-2.0\npragma solidity 0.8.18;\n\n\ncontract GasEstimator {\n  function estimate(\n    address _to,\n    bytes calldata _data\n  ) external returns (bool success, bytes memory result, uint256 gas) {\n    // solhint-disable\n    uint256 initialGas = gasleft();\n    (success, result) = _to.call(_data);\n    gas = initialGas - gasleft();\n    // solhint-enable\n  }\n}\n"
  },
  {
    "path": "contracts/modules/utils/MultiCallUtils.sol",
    "content": "// SPDX-License-Identifier: Apache-2.0\npragma solidity 0.8.18;\n\nimport \"../commons/interfaces/IModuleCalls.sol\";\n\n\ncontract MultiCallUtils {\n  // Errors\n  error DelegateCallNotAllowed(uint256 _index);\n  error CallReverted(uint256 _index, bytes _result);\n\n  function multiCall(\n    IModuleCalls.Transaction[] memory _txs\n  ) public payable returns (\n    bool[] memory _successes,\n    bytes[] memory _results\n  ) {\n    _successes = new bool[](_txs.length);\n    _results = new bytes[](_txs.length);\n\n    for (uint256 i = 0; i < _txs.length; i++) {\n      IModuleCalls.Transaction memory transaction = _txs[i];\n\n      if (transaction.delegateCall) revert DelegateCallNotAllowed(i);\n      if (gasleft() < transaction.gasLimit) revert IModuleCalls.NotEnoughGas(i, transaction.gasLimit, gasleft());\n\n      // solhint-disable\n      (_successes[i], _results[i]) = transaction.target.call{\n        value: transaction.value,\n        gas: transaction.gasLimit == 0 ? gasleft() : transaction.gasLimit\n      }(transaction.data);\n      // solhint-enable\n\n      if (!_successes[i] && _txs[i].revertOnError) revert CallReverted(i, _results[i]);\n    }\n  }\n\n  // ///\n  // Globals\n  // ///\n\n  function callBlockhash(uint256 _i) external view returns (bytes32) {\n    return blockhash(_i);\n  }\n\n  function callCoinbase() external view returns (address) {\n    return block.coinbase;\n  }\n\n  function callDifficulty() external view returns (uint256) {\n    return block.prevrandao; // old block.difficulty\n  }\n\n  function callPrevrandao() external view returns (uint256) {\n    return block.prevrandao;\n  }\n\n  function callGasLimit() external view returns (uint256) {\n    return block.gaslimit;\n  }\n\n  function callBlockNumber() external view returns (uint256) {\n    return block.number;\n  }\n\n  function callTimestamp() external view returns (uint256) {\n    return block.timestamp;\n  }\n\n  function callGasLeft() external view returns (uint256) {\n    return gasleft();\n  }\n\n  function callGasPrice() external view returns (uint256) {\n    return tx.gasprice;\n  }\n\n  function callOrigin() external view returns (address) {\n    return tx.origin;\n  }\n\n  function callBalanceOf(address _addr) external view returns (uint256) {\n    return _addr.balance;\n  }\n\n  function callCodeSize(address _addr) external view returns (uint256 size) {\n    assembly { size := extcodesize(_addr) }\n  }\n\n  function callCode(address _addr) external view returns (bytes memory code) {\n    assembly {\n      let size := extcodesize(_addr)\n      code := mload(0x40)\n      mstore(0x40, add(code, and(add(add(size, 0x20), 0x1f), not(0x1f))))\n      mstore(code, size)\n      extcodecopy(_addr, add(code, 0x20), 0, size)\n    }\n  }\n\n  function callCodeHash(address _addr) external view returns (bytes32 codeHash) {\n    assembly { codeHash := extcodehash(_addr) }\n  }\n\n  function callChainId() external view returns (uint256 id) {\n    assembly { id := chainid() }\n  }\n}\n"
  },
  {
    "path": "contracts/modules/utils/RequireUtils.sol",
    "content": "// SPDX-License-Identifier: Apache-2.0\npragma solidity 0.8.18;\n\nimport \"../commons/ModuleNonce.sol\";\nimport \"../commons/submodules/nonce/SubModuleNonce.sol\";\n\nimport \"../../interfaces/tokens/IERC20.sol\";\nimport \"../../interfaces/tokens/IERC721.sol\";\nimport \"../../interfaces/tokens/IERC1155.sol\";\n\ncontract RequireUtils {\n  /**\n   * @notice Validates that a given expiration hasn't expired\n   * @dev Used as an optional transaction on a Sequence batch, to create expirable transactions.\n   *\n   * @param _expiration  Expiration to check\n   */\n  function requireNonExpired(uint256 _expiration) external view {\n    require(block.timestamp < _expiration, \"RequireUtils#requireNonExpired: EXPIRED\");\n  }\n\n  /**\n   * @notice Validates that a given wallet has reached a given nonce\n   * @dev Used as an optional transaction on a Sequence batch, to define transaction execution order\n   *\n   * @param _wallet Sequence wallet\n   * @param _nonce  Required nonce\n   */\n  function requireMinNonce(address _wallet, uint256 _nonce) external view {\n    (uint256 space, uint256 nonce) = SubModuleNonce.decodeNonce(_nonce);\n    uint256 currentNonce = ModuleNonce(_wallet).readNonce(space);\n    require(currentNonce >= nonce, \"RequireUtils#requireMinNonce: NONCE_BELOW_REQUIRED\");\n  }\n\n  /**\n   * @notice Validates that a wallet has a minimum ERC20 token balance\n   * @param _token ERC20 token address\n   * @param _wallet Sequence wallet\n   * @param _minBalance Minimum required balance\n   */\n  function requireMinERC20Balance(address _token, address _wallet, uint256 _minBalance) external view {\n    uint256 balance = IERC20(_token).balanceOf(_wallet);\n    require(balance >= _minBalance, 'RequireUtils#requireMinERC20Balance: BALANCE_TOO_LOW');\n  }\n\n  /**\n   * @notice Validates that a wallet has a minimum ERC20 allowance for a spender\n   * @param _token ERC20 token address\n   * @param _owner Sequence wallet\n   * @param _spender Address allowed to spend the tokens\n   * @param _minAllowance Minimum required allowance\n   */\n  function requireMinERC20Allowance(address _token, address _owner, address _spender, uint256 _minAllowance) external view {\n    uint256 allowance = IERC20(_token).allowance(_owner, _spender);\n    require(allowance >= _minAllowance, 'RequireUtils#requireMinERC20Allowance: ALLOWANCE_TOO_LOW');\n  }\n\n  /**\n   * @notice Validates that a wallet owns a specific ERC721 token\n   * @param _token ERC721 token address\n   * @param _wallet Sequence wallet\n   * @param _tokenId Token ID to check for ownership\n   */\n  function requireERC721Ownership(address _token, address _wallet, uint256 _tokenId) external view {\n    address owner = IERC721(_token).ownerOf(_tokenId);\n    require(owner == _wallet, 'RequireUtils#requireERC721Ownership: NOT_OWNER');\n  }\n\n  /**\n   * @notice Validates that an ERC721 token is approved for a specific spender\n   * @param _token ERC721 token address\n   * @param _owner Sequence wallet\n   * @param _spender Address that should have approval\n   * @param _tokenId Token ID to check for approval\n   */\n  function requireERC721Approval(address _token, address _owner, address _spender, uint256 _tokenId) external view {\n    address approved = IERC721(_token).getApproved(_tokenId);\n    require(\n      approved == _spender || IERC721(_token).isApprovedForAll(_owner, _spender),\n      'RequireUtils#requireERC721Approval: NOT_APPROVED'\n    );\n  }\n\n  /**\n   * @notice Validates that a wallet has a minimum balance of an ERC1155 token\n   * @param _token ERC1155 token address\n   * @param _wallet Sequence wallet\n   * @param _tokenId Token ID to check\n   * @param _minBalance Minimum required balance\n   */\n  function requireMinERC1155Balance(address _token, address _wallet, uint256 _tokenId, uint256 _minBalance) external view {\n    uint256 balance = IERC1155(_token).balanceOf(_wallet, _tokenId);\n    require(balance >= _minBalance, 'RequireUtils#requireMinERC1155Balance: BALANCE_TOO_LOW');\n  }\n\n  /**\n   * @notice Validates that an ERC1155 token is approved for a specific operator\n   * @param _token ERC1155 token address\n   * @param _owner Sequence wallet\n   * @param _operator Address that should have operator approval\n   */\n  function requireERC1155Approval(address _token, address _owner, address _operator) external view {\n    bool isApproved = IERC1155(_token).isApprovedForAll(_owner, _operator);\n    require(isApproved, 'RequireUtils#requireERC1155Approval: NOT_APPROVED');\n  }\n}\n"
  },
  {
    "path": "contracts/modules/utils/SequenceUtils.sol",
    "content": "// SPDX-License-Identifier: Apache-2.0\npragma solidity 0.8.18;\n\nimport \"./MultiCallUtils.sol\";\nimport \"./RequireUtils.sol\";\n\n\ncontract SequenceUtils is\n  MultiCallUtils,\n  RequireUtils\n{ }\n"
  },
  {
    "path": "contracts/trust/Trust.sol",
    "content": "// SPDX-License-Identifier: Apache-2.0\npragma solidity 0.8.18;\n\nimport \"../interfaces/IERC1271Wallet.sol\";\nimport \"../utils/SignatureValidator.sol\";\n\nfunction absDiff(uint256 a, uint256 b) pure returns (bool, uint256) {\n  if (a > b) {\n    return (true, a - b);\n  }\n\n  return (false, b - a);\n}\n\ncontract Trust is IERC1271Wallet {\n  error UnlockInThePast(uint256 _unlocksAt, uint256 _elapsed);\n  error UnlockTooEarly(uint256 _unlocksAt, uint256 _diff);\n\n  error NotOwner(address _sender);\n  error NotUnlocked(uint256 _unlocksAt);\n  error FailedTransaction(address payable _to, uint256 _value, bytes _data, bytes _result);\n\n  error EmptySignature();\n  error InvalidSignatureFlag(bytes _signature, bytes1 _flag);\n  error InvalidSignature(bytes32 _hash, bytes32 _rehash, address _signer, bytes _signature);\n\n  event SetUnlocksAt(uint256 _unlocksAt);\n  event SentTransaction(address payable _to, uint256 _value, bytes _data, bytes _result);\n\n  address immutable public owner;\n  address immutable public beneficiary;\n  uint256 immutable public duration;\n\n  uint256 public unlocksAt = type(uint256).max;\n\n  constructor (\n    address _owner,\n    address _beneficiary,\n    uint256 _duration\n  ) {\n    owner = _owner;\n    beneficiary = _beneficiary;\n    duration = _duration;\n  }\n\n  modifier onlyAllowed() {\n    if (msg.sender != owner) {\n      if (msg.sender != beneficiary) {\n        revert NotOwner(msg.sender);\n      }\n\n      if (isLocked()) {\n        revert NotUnlocked(unlocksAt);\n      }\n    }\n\n    _;\n  }\n\n  modifier onlyMember() {\n    if (msg.sender != owner && msg.sender != beneficiary) {\n      revert NotOwner(msg.sender);\n    }\n\n    _;\n  }\n\n  function isLocked() public view returns (bool) {\n    return block.timestamp < unlocksAt;\n  }\n\n  function setUnlocksAt(uint256 _unlocksAt) external onlyMember {\n    // Diff between the current time and the unlock time must be\n    // greater than the duration of the trust\n    (bool isPast, uint256 elapsed) = absDiff(block.timestamp, _unlocksAt);\n    if (isPast) {\n      revert UnlockInThePast(_unlocksAt, elapsed);\n    }\n\n    if (elapsed < duration) {\n      revert UnlockTooEarly(_unlocksAt, elapsed);\n    }\n\n    emit SetUnlocksAt(_unlocksAt);\n    unlocksAt = _unlocksAt;\n  }\n\n  function sendTransaction(\n    address payable _to,\n    uint256 _value,\n    bytes calldata _data\n  ) external onlyAllowed returns (bytes memory) {\n    (bool success, bytes memory result) = _to.call{value: _value}(_data);\n\n    if (!success) {\n      revert FailedTransaction(_to, _value, _data, result);\n    }\n\n    emit SentTransaction(_to, _value, _data, result);\n    return result;\n  }\n\n  bytes4 internal constant SELECTOR_ERC1271_BYTES_BYTES = 0x20c13b0b;\n  bytes4 internal constant SELECTOR_ERC1271_BYTES32_BYTES = 0x1626ba7e;\n\n  function isValidSignature(\n    bytes calldata _data,\n    bytes calldata _signature\n  ) external view returns (bytes4) {\n    bytes4 res = Trust(payable(address((this)))).isValidSignature(\n      keccak256(_data),\n      _signature\n    );\n\n    assert(res == SELECTOR_ERC1271_BYTES32_BYTES);\n    return SELECTOR_ERC1271_BYTES_BYTES;\n  }\n\n  function isValidSignature(\n    bytes32 _hash,\n    bytes calldata _signature\n  ) external view returns (bytes4) {\n    if (_signature.length == 0) {\n      revert EmptySignature();\n    }\n\n    // The last byte determines how the signature is going to be interpreted\n    // 0x00 -> Signed by the owner\n    // 0x01 -> Signed by the beneficiary\n    // 0x02 -> Signed by the owner for any network\n    // 0x03 -> Signed by the beneficiary for any network\n    address signer;\n    uint256 chainId;\n\n    {\n      bytes1 flag = _signature[_signature.length - 1];\n\n      if (flag == 0x00) {\n        signer = owner;\n        chainId = block.chainid;\n      } else if (flag == 0x01) {\n        signer = beneficiary;\n        chainId = block.chainid;\n      } else if (flag == 0x02) {\n        signer = owner;\n        chainId = 0;\n      } else if (flag == 0x03) {\n        signer = beneficiary;\n        chainId = 0;\n      } else {\n        revert InvalidSignatureFlag(_signature, flag);\n      }\n    }\n\n    if (signer != owner && isLocked()) {\n      revert NotUnlocked(unlocksAt);\n    }\n\n    // Re-hash the hash adding the address of the trust\n    // otherwise the signature will be valid for any trust\n    bytes32 rehash = keccak256(abi.encode(address(this), _hash, chainId));\n\n    // Validate the signature\n    if (!SignatureValidator.isValidSignature(rehash, signer, _signature[0:_signature.length - 1])) {\n      revert InvalidSignature(_hash, rehash, signer, _signature[0:_signature.length - 1]);\n    }\n\n    return SELECTOR_ERC1271_BYTES32_BYTES;\n  }\n\n  receive() external payable {}\n  fallback() external payable {}\n}\n"
  },
  {
    "path": "contracts/trust/TrustFactory.sol",
    "content": "// SPDX-License-Identifier: Apache-2.0\npragma solidity 0.8.18;\n\nimport \"./Trust.sol\";\n\n\ncontract TrustFactory {\n  function trustCreationCode() external pure returns (bytes memory) {\n    return type(Trust).creationCode;\n  }\n\n  function addressOf(\n    address _owner,\n    address _beneficiary,\n    uint256 _duration\n  ) external view returns (address) {\n    return address(uint160(uint(keccak256(abi.encodePacked(\n      bytes1(0xff),\n      address(this),\n      bytes32(0),\n      keccak256(abi.encodePacked(\n        type(Trust).creationCode,\n        abi.encode(_owner, _beneficiary, _duration)\n      ))\n    )))));\n  }\n\n  function deploy(\n    address _owner,\n    address _beneficiary,\n    uint256 _duration\n  ) external returns (Trust) {\n    return new Trust{ salt: bytes32(0) }( _owner, _beneficiary, _duration);\n  }\n}\n"
  },
  {
    "path": "contracts/utils/LibAddress.sol",
    "content": "// SPDX-License-Identifier: Apache-2.0\npragma solidity 0.8.18;\n\n\nlibrary LibAddress {\n  /**\n   * @notice Will return true if provided address is a contract\n   * @param account Address to verify if contract or not\n   * @dev This contract will return false if called within the constructor of\n   *      a contract's deployment, as the code is not yet stored on-chain.\n   */\n  function isContract(address account) internal view returns (bool) {\n    uint256 csize;\n    // solhint-disable-next-line no-inline-assembly\n    assembly { csize := extcodesize(account) }\n    return csize != 0;\n  }\n}\n"
  },
  {
    "path": "contracts/utils/LibBytes.sol",
    "content": "// SPDX-License-Identifier: Apache-2.0\npragma solidity 0.8.18;\n\n\n/**\n * @title Library for reading data from bytes arrays\n * @author Agustin Aguilar (aa@horizon.io)\n * @notice This library contains functions for reading data from bytes arrays.\n *\n * @dev These functions do not check if the input index is within the bounds of the data array.\n *         Reading out of bounds may return dirty values.\n */\nlibrary LibBytes {\n\n  /**\n   * @notice Returns the bytes32 value at the given index in the input data.\n   * @param data The input data.\n   * @param index The index of the value to retrieve.\n   * @return a The bytes32 value at the given index.\n   */\n  function readBytes32(\n    bytes calldata data,\n    uint256 index\n  ) internal pure returns (\n    bytes32 a\n  ) {\n    assembly {\n      a := calldataload(add(data.offset, index))\n    }\n  }\n\n  /**\n   * @notice Returns the uint8 value at the given index in the input data.\n   * @param data The input data.\n   * @param index The index of the value to retrieve.\n   * @return a The uint8 value at the given index.\n   */\n  function readUint8(\n    bytes calldata data,\n    uint256 index\n  ) internal pure returns (\n    uint8 a\n  ) {\n    assembly {\n      let word := calldataload(add(index, data.offset))\n      a := shr(248, word)\n    }\n  }\n\n  /**\n   * @notice Returns the first uint16 value in the input data.\n   * @param data The input data.\n   * @return a The first uint16 value in the input data.\n   */\n  function readFirstUint16(\n    bytes calldata data\n  ) internal pure returns (\n    uint16 a\n  ) {\n    assembly {\n      let word := calldataload(data.offset)\n      a := shr(240, word)\n    }\n  }\n\n  /**\n   * @notice Returns the uint32 value at the given index in the input data.\n   * @param data The input data.\n   * @param index The index of the value to retrieve.\n   * @return a The uint32 value at the given index.\n   */\n  function readUint32(\n    bytes calldata data,\n    uint256 index\n  ) internal pure returns (\n    uint32 a\n  ) {\n    assembly {\n      let word := calldataload(add(index, data.offset))\n      a := shr(224, word)\n    }\n  }\n\n  function readMBytes4(\n    bytes memory data,\n    uint256 index\n  ) internal pure returns (\n    bytes4 a\n  ) {\n    assembly {\n      let word := mload(add(add(data, 0x20), index))\n      a := and(word, 0xFFFFFFFF00000000000000000000000000000000000000000000000000000000)\n    }\n  }\n\n  function readMBytes32(\n    bytes memory data,\n    uint256 index\n  ) internal pure returns (\n    bytes32 a\n  ) {\n    assembly {\n      a := mload(add(add(data, 0x20), index))\n    }\n  }\n}\n"
  },
  {
    "path": "contracts/utils/LibBytesPointer.sol",
    "content": "// SPDX-License-Identifier: Apache-2.0\npragma solidity 0.8.18;\n\n\n/**\n * @title Library for reading data from bytes arrays with a pointer\n * @author Agustin Aguilar (aa@horizon.io)\n * @notice This library contains functions for reading data from bytes arrays with a pointer.\n *\n * @dev These functions do not check if the input index is within the bounds of the data array.\n *         Reading out of bounds may return dirty values.\n */\nlibrary LibBytesPointer {\n\n  /**\n   * @dev Returns the first uint16 value in the input data and updates the pointer.\n   * @param _data The input data.\n   * @return a The first uint16 value.\n   * @return newPointer The new pointer.\n   */\n  function readFirstUint16(\n    bytes calldata _data\n  ) internal pure returns (\n    uint16 a,\n    uint256 newPointer\n  ) {\n    assembly {\n      let word := calldataload(_data.offset)\n      a := shr(240, word)\n      newPointer := 2\n    }\n  }\n\n  /**\n   * @notice Returns the uint8 value at the given index in the input data and updates the pointer.\n   * @param _data The input data.\n   * @param _index The index of the value to retrieve.\n   * @return a The uint8 value at the given index.\n   * @return newPointer The new pointer.\n   */\n  function readUint8(\n    bytes calldata _data,\n    uint256 _index\n  ) internal pure returns (\n    uint8 a,\n    uint256 newPointer\n  ) {\n    assembly {\n      let word := calldataload(add(_index, _data.offset))\n      a := shr(248, word)\n      newPointer := add(_index, 1)\n    }\n  }\n\n  function readAddress(\n    bytes calldata _data,\n    uint256 _index\n  ) internal pure returns (\n    address a,\n    uint256 newPointer\n  ) {\n    assembly {\n      let word := calldataload(add(_index, _data.offset))\n      a := and(shr(96, word), 0xffffffffffffffffffffffffffffffffffffffff)\n      newPointer := add(_index, 20)\n    }\n  }\n\n  /**\n   * @notice Returns the uint8 value and the address at the given index in the input data and updates the pointer.\n   * @param _data The input data.\n   * @param _index The index of the value to retrieve.\n   * @return a The uint8 value at the given index.\n   * @return b The following address value.\n   * @return newPointer The new pointer.\n   */\n  function readUint8Address(\n    bytes calldata _data,\n    uint256 _index\n  ) internal pure returns (\n    uint8 a,\n    address b,\n    uint256 newPointer\n  ) {\n    assembly {\n      let word := calldataload(add(_index, _data.offset))\n      a := shr(248, word)\n      b := and(shr(88, word), 0xffffffffffffffffffffffffffffffffffffffff)\n      newPointer := add(_index, 21)\n    }\n  }\n\n  /**\n   * @notice Returns the uint16 value at the given index in the input data and updates the pointer.\n   * @param _data The input data.\n   * @param _index The index of the value to retrieve.\n   * @return a The uint16 value at the given index.\n   * @return newPointer The new pointer.\n   */\n  function readUint16(\n    bytes calldata _data,\n    uint256 _index\n  ) internal pure returns (\n    uint16 a,\n    uint256 newPointer\n  ) {\n    assembly {\n      let word := calldataload(add(_index, _data.offset))\n      a := and(shr(240, word), 0xffff)\n      newPointer := add(_index, 2)\n    }\n  }\n\n  function readUintX(\n    bytes calldata _data,\n    uint256 _bytes,\n    uint256 _index\n  ) internal pure returns (\n    uint256 a,\n    uint256 newPointer\n  ) {\n    assembly {\n      let word := calldataload(add(_index, _data.offset))\n      let shift := sub(256, mul(_bytes, 8))\n      a := and(shr(shift, word), sub(shl(mul(8, _bytes), 1), 1))\n      newPointer := add(_index, _bytes)\n    }\n  }\n\n  /**\n   * @notice Returns the uint24 value at the given index in the input data and updates the pointer.\n   * @param _data The input data.\n   * @param _index The index of the value to retrieve.\n   * @return a The uint24 value at the given index.\n   * @return newPointer The new pointer.\n   */\n  function readUint24(\n    bytes calldata _data,\n    uint256 _index\n  ) internal pure returns (\n    uint24 a,\n    uint256 newPointer\n  ) {\n    assembly {\n      let word := calldataload(add(_index, _data.offset))\n      a := and(shr(232, word), 0xffffff)\n      newPointer := add(_index, 3)\n    }\n  }\n\n  /**\n   * @notice Returns the uint64 value at the given index in the input data and updates the pointer.\n   * @param _data The input data.\n   * @param _index The index of the value to retrieve.\n   * @return a The uint64 value at the given index.\n   * @return newPointer The new pointer.\n   */\n  function readUint64(\n    bytes calldata _data,\n    uint256 _index\n  ) internal pure returns (\n    uint64 a,\n    uint256 newPointer\n  ) {\n    assembly {\n      let word := calldataload(add(_index, _data.offset))\n      a := and(shr(192, word), 0xffffffffffffffff)\n      newPointer := add(_index, 8)\n    }\n  }\n\n  function readBytes4(\n    bytes calldata _data,\n    uint256 _pointer\n  ) internal pure returns (\n    bytes4 a,\n    uint256 newPointer\n  ) {\n    assembly {\n      a := calldataload(add(_pointer, _data.offset))\n      a := and(a, 0xFFFFFFFF00000000000000000000000000000000000000000000000000000000)\n      newPointer := add(_pointer, 4)\n    }\n  }\n\n  /**\n   * @notice Returns the bytes32 value at the given index in the input data and updates the pointer.\n   * @param _data The input data.\n   * @param _pointer The index of the value to retrieve.\n   * @return a The bytes32 value at the given index.\n   * @return newPointer The new pointer.\n   */\n  function readBytes32(\n    bytes calldata _data,\n    uint256 _pointer\n  ) internal pure returns (\n    bytes32 a,\n    uint256 newPointer\n  ) {\n    assembly {\n      a := calldataload(add(_pointer, _data.offset))\n      newPointer := add(_pointer, 32)\n    }\n  }\n}\n"
  },
  {
    "path": "contracts/utils/LibOptim.sol",
    "content": "// SPDX-License-Identifier: Apache-2.0\npragma solidity 0.8.18;\n\n/**\n * @title Library for optimized EVM operations\n * @author Agustin Aguilar (aa@horizon.io)\n * @notice This library contains functions for optimizing certain EVM operations.\n */\nlibrary LibOptim {\n\n  /**\n   * @notice Computes the keccak256 hash of two 32-byte inputs.\n   * @dev It uses only scratch memory space.\n   * @param _a The first 32 bytes of the hash.\n   * @param _b The second 32 bytes of the hash.\n   * @return c The keccak256 hash of the two 32-byte inputs.\n   */\n  function fkeccak256(\n    bytes32 _a,\n    bytes32 _b\n  ) internal pure returns (bytes32 c) {\n    assembly {\n      mstore(0, _a)\n      mstore(32, _b)\n      c := keccak256(0, 64)\n    }\n  }\n\n  /**\n   * @notice Returns the return data from the last call.\n   * @return r The return data from the last call.\n   */\n  function returnData() internal pure returns (bytes memory r) {\n    assembly {\n      let size := returndatasize()\n      r := mload(0x40)\n      let start := add(r, 32)\n      mstore(0x40, add(start, size))\n      mstore(r, size)\n      returndatacopy(start, 0, size)\n    }\n  }\n\n  /**\n   * @notice Calls another contract with the given parameters.\n   * @dev This method doesn't increase the memory pointer.\n   * @param _to The address of the contract to call.\n   * @param _val The value to send to the contract.\n   * @param _gas The amount of gas to provide for the call.\n   * @param _data The data to send to the contract.\n   * @return r The success status of the call.\n   */\n  function call(\n    address _to,\n    uint256 _val,\n    uint256 _gas,\n    bytes calldata _data\n  ) internal returns (bool r) {\n    assembly {\n      let tmp := mload(0x40)\n      calldatacopy(tmp, _data.offset, _data.length)\n\n      r := call(\n        _gas,\n        _to,\n        _val,\n        tmp,\n        _data.length,\n        0,\n        0\n      )\n    }\n  }\n\n  /**\n   * @notice Calls another contract with the given parameters, using delegatecall.\n   * @dev This method doesn't increase the memory pointer.\n   * @param _to The address of the contract to call.\n   * @param _gas The amount of gas to provide for the call.\n   * @param _data The data to send to the contract.\n   * @return r The success status of the call.\n   */\n  function delegatecall(\n    address _to,\n    uint256 _gas,\n    bytes calldata _data\n  ) internal returns (bool r) {\n    assembly {\n      let tmp := mload(0x40)\n      calldatacopy(tmp, _data.offset, _data.length)\n\n      r := delegatecall(\n        _gas,\n        _to,\n        tmp,\n        _data.length,\n        0,\n        0\n      )\n    }\n  }\n}\n"
  },
  {
    "path": "contracts/utils/LibString.sol",
    "content": "// SPDX-License-Identifier: Apache-2.0\npragma solidity 0.8.18;\n\n/**\n * @title Library for string manipulation operations\n * @notice This library contains functions for manipulating strings in Solidity.\n */\nlibrary LibString {\n  bytes private constant ALPHABET_HEX_16 = '0123456789abcdef';\n  bytes private constant ALPHABET_32 = 'abcdefghijklmnopqrstuvwxyz234567';\n\n  /**\n   * @notice Prefixes a hexadecimal string with \"0x\".\n   * @param _hex The hexadecimal string to prefix.\n   * @return The prefixed hexadecimal string.\n   */\n  function prefixHexadecimal(string memory _hex) internal pure returns (string memory) {\n    return string(abi.encodePacked('0x', _hex));\n  }\n\n  /**\n   * @notice Prefixes a base32 string with \"b\".\n   * @param _base32 The base32 string to prefix.\n   * @return The prefixed base32 string.\n   */\n  function prefixBase32(string memory _base32) internal pure returns (string memory) {\n    return string(abi.encodePacked('b', _base32));\n  }\n\n  /**\n   * @notice Converts a byte array to a hexadecimal string.\n   * @param _bytes The byte array to convert.\n   * @return The resulting hexadecimal string.\n   */\n  function bytesToHexadecimal(bytes memory _bytes) internal pure returns (string memory) {\n    uint256 bytesLength = _bytes.length;\n    bytes memory bytesArray = new bytes(bytesLength << 1);\n\n    unchecked {\n      for (uint256 i = 0; i < bytesLength; i++) {\n        uint256 word = uint8(_bytes[i]);\n        uint256 ib = i << 1;\n        bytesArray[ib] = bytes1(ALPHABET_HEX_16[word >> 4]);\n        bytesArray[ib + 1] = bytes1(ALPHABET_HEX_16[word & 0xf]);\n      }\n    }\n\n    return string(bytesArray);\n  }\n\n  /**\n   * @notice Converts a byte array to a base32 string.\n   * @param _bytes The byte array to convert.\n   * @return The resulting base32 string.\n   */\n  function bytesToBase32(bytes memory _bytes) internal pure returns (string memory) {\n    uint256 bytesLength = _bytes.length;\n\n    uint256 t1 = bytesLength << 3;\n\n    unchecked {\n      // base32-encoded length = ceil(# of bits / 5)\n      bytes memory bytesArray = new bytes((t1 + 4) / 5);\n\n      uint256 bits = 0;\n      uint256 buffer = 0;\n      uint256 pointer = 0;\n\n      for (uint256 i = 0; i < bytesLength; i++) {\n        buffer = (buffer << 8) | uint8(_bytes[i]);\n        bits += 8;\n\n        while (bits >= 5) {\n          bits -= 5;\n          bytesArray[pointer] = bytes1(ALPHABET_32[(buffer >> bits) & 0x1f]);\n          pointer++;\n        }\n      }\n\n      if (bits > 0) {\n        bytesArray[pointer] = bytes1(ALPHABET_32[(buffer << (5 - bits)) & 0x1f]);\n      }\n\n      return string(bytesArray);\n    }\n  }\n}\n"
  },
  {
    "path": "contracts/utils/SignatureValidator.sol",
    "content": "// SPDX-License-Identifier: Apache-2.0\npragma solidity 0.8.18;\n\nimport \"../interfaces/IERC1271Wallet.sol\";\n\nimport \"./LibBytes.sol\";\n\n/**\n * @dev Contains logic for signature validation.\n * Signatures from wallet contracts assume ERC-1271 support (https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1271.md)\n * Notes: Methods are strongly inspired by contracts in https://github.com/0xProject/0x-monorepo/blob/development/\n */\nlibrary SignatureValidator {\n  // Errors\n  error InvalidSignatureLength(bytes _signature);\n  error EmptySignature();\n  error InvalidSValue(bytes _signature, bytes32 _s);\n  error InvalidVValue(bytes _signature, uint256 _v);\n  error UnsupportedSignatureType(bytes _signature, uint256 _type, bool _recoverMode);\n  error SignerIsAddress0(bytes _signature);\n\n  using LibBytes for bytes;\n\n  /***********************************|\n  |             Variables             |\n  |__________________________________*/\n\n  // bytes4(keccak256(\"isValidSignature(bytes,bytes)\"))\n  bytes4 constant internal ERC1271_MAGICVALUE = 0x20c13b0b;\n\n  // bytes4(keccak256(\"isValidSignature(bytes32,bytes)\"))\n  bytes4 constant internal ERC1271_MAGICVALUE_BYTES32 = 0x1626ba7e;\n\n  // Allowed signature types.\n  uint256 private constant SIG_TYPE_EIP712 = 1;\n  uint256 private constant SIG_TYPE_ETH_SIGN = 2;\n  uint256 private constant SIG_TYPE_WALLET_BYTES32 = 3;\n\n  /***********************************|\n  |        Signature Functions        |\n  |__________________________________*/\n\n /**\n   * @notice Recover the signer of hash, assuming it's an EOA account\n   * @dev Only for SignatureType.EIP712 and SignatureType.EthSign signatures\n   * @param _hash      Hash that was signed\n   *   encoded as (bytes32 r, bytes32 s, uint8 v, ... , SignatureType sigType)\n   */\n  function recoverSigner(\n    bytes32 _hash,\n    bytes calldata _signature\n  ) internal pure returns (address signer) {\n    if (_signature.length != 66) revert InvalidSignatureLength(_signature);\n    uint256 signatureType = _signature.readUint8(_signature.length - 1);\n\n    // Variables are not scoped in Solidity.\n    uint8 v = _signature.readUint8(64);\n    bytes32 r = _signature.readBytes32(0);\n    bytes32 s = _signature.readBytes32(32);\n\n    // EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature\n    // unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines\n    // the valid range for s in (281): 0 < s < secp256k1n ÷ 2 + 1, and for v in (282): v ∈ {27, 28}. Most\n    // signatures from current libraries generate a unique signature with an s-value in the lower half order.\n    //\n    // If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value\n    // with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or\n    // vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept\n    // these malleable signatures as well.\n    //\n    // Source OpenZeppelin\n    // https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/cryptography/ECDSA.sol\n\n    if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) {\n      revert InvalidSValue(_signature, s);\n    }\n\n    if (v != 27 && v != 28) {\n      revert InvalidVValue(_signature, v);\n    }\n\n    // Signature using EIP712\n    if (signatureType == SIG_TYPE_EIP712) {\n      signer = ecrecover(_hash, v, r, s);\n\n    // Signed using web3.eth_sign() or Ethers wallet.signMessage()\n    } else if (signatureType == SIG_TYPE_ETH_SIGN) {\n      signer = ecrecover(\n        keccak256(abi.encodePacked(\"\\x19Ethereum Signed Message:\\n32\", _hash)),\n        v,\n        r,\n        s\n      );\n\n    } else {\n      // We cannot recover the signer for any other signature type.\n      revert UnsupportedSignatureType(_signature, signatureType, true);\n    }\n\n    // Prevent signer from being 0x0\n    if (signer == address(0x0)) revert SignerIsAddress0(_signature);\n\n    return signer;\n  }\n\n /**\n   * @notice Returns true if the provided signature is valid for the given signer.\n   * @dev Supports SignatureType.EIP712, SignatureType.EthSign, and ERC1271 signatures\n   * @param _hash      Hash that was signed\n   * @param _signer    Address of the signer candidate\n   * @param _signature Signature byte array\n   */\n  function isValidSignature(\n    bytes32 _hash,\n    address _signer,\n    bytes calldata _signature\n  ) internal view returns (bool valid) {\n    if (_signature.length == 0) {\n      revert EmptySignature();\n    }\n\n    uint256 signatureType = uint8(_signature[_signature.length - 1]);\n    if (signatureType == SIG_TYPE_EIP712 || signatureType == SIG_TYPE_ETH_SIGN) {\n      // Recover signer and compare with provided\n      valid = recoverSigner(_hash, _signature) == _signer;\n\n    } else if (signatureType == SIG_TYPE_WALLET_BYTES32) {\n      // Remove signature type before calling ERC1271, restore after call\n      valid = ERC1271_MAGICVALUE_BYTES32 == IERC1271Wallet(_signer).isValidSignature(_hash, _signature[0:_signature.length - 1]);\n\n    } else {\n      // We cannot validate any other signature type.\n      // We revert because we can say nothing about its validity.\n      revert UnsupportedSignatureType(_signature, signatureType, false);\n    }\n  }\n}\n"
  },
  {
    "path": "foundry.toml",
    "content": "[profile.default]\nsrc = 'contracts'\nout = 'foundry_artifacts'\nlibs = [\"node_modules\", \"lib\"]\ntest = 'foundry_test'\n\nffi = true\nmax_test_rejects = 2048000\n"
  },
  {
    "path": "foundry_test/base/AdvTest.sol",
    "content": "// SPDX-License-Identifier: Apache-2.0\npragma solidity 0.8.18;\n\nimport \"forge-std/Test.sol\";\n\n\ncontract AdvTest is Test {\n  function signAndPack(uint256 _pk, bytes32 _hash) internal pure returns (bytes memory) {\n    (uint8 v, bytes32 r, bytes32 s) = vm.sign(_pk, _hash);\n    return abi.encodePacked(r, s, v);\n  }\n\n  function signAndPack(uint256 _pk, bytes32 _hash, uint8 _sufix) internal pure returns (bytes memory) {\n    (uint8 v, bytes32 r, bytes32 s) = vm.sign(_pk, _hash);\n    return abi.encodePacked(r, s, v, _sufix);\n  }\n\n  function mayBoundArr(uint256 _size) internal view returns (uint256) {\n    try vm.envUint('MAX_ARRAY_LEN') returns (uint256 b) {\n      return b == 0 ? _size : bound(_size, 0, b);\n    } catch {\n      return _size;\n    }\n  }\n\n  function boundNoSys(address _a) internal view returns (address) {\n    address c2 = address(0x007109709ecfa91a80626ff3989d68f67f5b1dd12d);\n    address vm = address(0x004e59b44847b379578588920ca78fbf26c0b4956c);\n    address c3 = address(0x00000000000000000000636f6e736f6c652e6c6f67);\n\n    boundNoPrecompile(_a);\n    boundDiff(_a, c2, vm, c3, address(this));\n\n    return _a;\n  }\n\n  function boundNoPrecompile(address _a) internal pure returns (address) {\n    vm.assume(uint160(_a)> 10);\n    return _a;\n  }\n\n  function boundDiff(address _a, address _b) internal pure returns (address) {\n    vm.assume(_a != _b);\n    return _a;\n  }\n\n  function boundDiff(address _a, address _b, address _c) internal pure returns (address) {\n    address[] memory arr = new address[](2);\n    arr[0] = _b;\n    arr[1] = _c;\n    return boundDiff(_a, arr);\n  }\n\n  function boundDiff(address _a, address _b, address _c, address _d) internal pure returns (address) {\n    address[] memory _arr = new address[](3);\n    _arr[0] = _b;\n    _arr[1] = _c;\n    _arr[2] = _d;\n    return boundDiff(_a, _arr);\n  }\n\n  function boundDiff(address _a, address _b, address _c, address _d, address _e) internal pure returns (address) {\n    address[] memory _arr = new address[](4);\n    _arr[0] = _b;\n    _arr[1] = _c;\n    _arr[2] = _d;\n    _arr[3] = _e;\n    return boundDiff(_a, _arr);\n  }\n\n  function boundDiff(address _a, address[] memory _b) internal pure returns (address) {\n    vm.assume(!inSet(_a, _b));\n    return _a;\n  }\n\n  function boundPk(uint256 _a) internal view returns (uint256) {\n    _a = bound(_a, 1, 0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364139);\n    return _a;\n  }\n\n  function boundDiff(uint256 _a, uint256 _b) internal pure returns (uint256) {\n    vm.assume(_a != _b);\n    return _a;\n  }\n\n  function boundDiff(uint256 _a, uint256 _b, uint256 _c) internal pure returns (uint256) {\n    uint256[] memory arr = new uint256[](2);\n    arr[0] = _b;\n    arr[1] = _c;\n    return boundDiff(_a, arr);\n  }\n\n  function boundDiff(uint256 _a, uint256 _b, uint256 _c, uint256 _d) internal pure returns (uint256) {\n    uint256[] memory _arr = new uint256[](3);\n    _arr[0] = _b;\n    _arr[1] = _c;\n    _arr[2] = _d;\n    return boundDiff(_a, _arr);\n  }\n\n  function boundDiff(uint256 _a, uint256 _b, uint256 _c, uint256 _d, uint256 _e) internal pure returns (uint256) {\n    uint256[] memory _arr = new uint256[](4);\n    _arr[0] = _b;\n    _arr[1] = _c;\n    _arr[2] = _d;\n    _arr[3] = _e;\n    return boundDiff(_a, _arr);\n  }\n\n  function boundDiff(uint256 _a, uint256 _b, uint256 _c, uint256 _d, uint256 _e, uint256 _f) internal pure returns (uint256) {\n    uint256[] memory _arr = new uint256[](5);\n    _arr[0] = _b;\n    _arr[1] = _c;\n    _arr[2] = _d;\n    _arr[3] = _e;\n    _arr[4] = _f;\n    return boundDiff(_a, _arr);\n  }\n\n  function boundDiff(uint256 _a, uint256 _b, uint256 _c, uint256 _d, uint256 _e, uint256 _f, uint256 _g) internal pure returns (uint256) {\n    uint256[] memory _arr = new uint256[](6);\n    _arr[0] = _b;\n    _arr[1] = _c;\n    _arr[2] = _d;\n    _arr[3] = _e;\n    _arr[4] = _f;\n    _arr[5] = _g;\n    return boundDiff(_a, _arr);\n  }\n\n  function boundDiff(uint256 _a, uint256 _b, uint256 _c, uint256 _d, uint256 _e, uint256 _f, uint256 _g, uint256 _h) internal pure returns (uint256) {\n    uint256[] memory _arr = new uint256[](7);\n    _arr[0] = _b;\n    _arr[1] = _c;\n    _arr[2] = _d;\n    _arr[3] = _e;\n    _arr[4] = _f;\n    _arr[5] = _g;\n    _arr[6] = _h;\n    return boundDiff(_a, _arr);\n  }\n\n  function boundDiff(uint256 _a, uint256[] memory _b) internal pure returns (uint256) {\n    vm.assume(!inSet(_a, _b));\n    return _a;\n  }\n\n  function inSet(uint256 _a, uint256[] memory _b) internal pure returns (bool) {\n    unchecked {\n      for (uint256 i = 0; i < _b.length; i++) {\n        if (_a == _b[i]) {\n          return true;\n        }\n      }\n\n      return false;\n    }\n  }\n\n  function inSet(address _a, address[] memory _b) internal pure returns (bool) {\n    unchecked {\n      for (uint256 i = 0; i < _b.length; i++) {\n        if (_a == _b[i]) {\n          return true;\n        }\n      }\n\n      return false;\n    }\n  }\n\n  function boundNoContract(address _a) internal view returns (address) {\n    vm.assume(_a.code.length == 0);\n    return _a;\n  }\n\n  function boundNoBalance(address _a) internal view returns (address) {\n    vm.assume(_a.balance == 0);\n    return _a;\n  }\n\n  function replicate(bytes memory _data) internal {\n    (bool suc, bytes memory res) = address(this).call(_data);\n    if (!suc) {\n      assembly {\n        revert(add(res, 32), mload(res))\n      }\n    }\n  }\n\n  function addrToBytes32(address _addr) internal pure returns (bytes32) {\n    return bytes32(uint256(uint160(_addr)));\n  }\n}\n"
  },
  {
    "path": "foundry_test/hooks/WalletProxyHook.t.sol",
    "content": "// SPDX-License-Identifier: Apache-2.0\npragma solidity 0.8.18;\n\nimport 'contracts/hooks/WalletProxyHook.sol';\nimport 'contracts/hooks/interfaces/IWalletProxy.sol';\nimport 'contracts/modules/commons/ModuleHooks.sol';\nimport 'contracts/Factory.sol';\n\nimport 'foundry_test/base/AdvTest.sol';\n\ncontract WalletProxyHookTest is AdvTest {\n  ModuleHooks private template;\n  ModuleHooks private walletMod;\n  WalletProxyHook private wallet;\n\n  function setUp() external {\n    Factory factory = new Factory();\n    template = new ModuleHooks();\n    walletMod = ModuleHooks(payable(factory.deploy(address(template), bytes32(0))));\n    WalletProxyHook hook = new WalletProxyHook();\n\n    // Add hook\n    vm.prank(address(walletMod));\n    walletMod.addHook(IWalletProxy.PROXY_getImplementation.selector, address(hook));\n\n    wallet = WalletProxyHook(address(walletMod));\n    vm.label(address(wallet), 'wallet');\n  }\n\n  //\n  // Get Implementation\n  //\n\n  function test_PROXY_getImplementation() external {\n    assertEq(wallet.PROXY_getImplementation(), address(template));\n  }\n}\n"
  },
  {
    "path": "foundry_test/modules/commons/Implementation.t.sol",
    "content": "// SPDX-License-Identifier: Apache-2.0\npragma solidity 0.8.18;\n\nimport \"contracts/modules/commons/Implementation.sol\";\nimport \"contracts/Factory.sol\";\n\nimport \"foundry_test/base/AdvTest.sol\";\n\n\ncontract ImplementationImp is Implementation {\n  function setImplementation(address _imp) external {\n    _setImplementation(_imp);\n  }\n\n  function getImplementation() external view returns (address) {\n    return _getImplementation();\n  }\n\n  function stub() external virtual pure returns (uint256) {\n    return 1;\n  }\n}\n\ncontract NextImplementation is ImplementationImp {\n  function stub() external override pure returns (uint256) {\n    return 2;\n  }\n}\n\ncontract ImplementationTest is AdvTest {\n  function test_setImplementation(address _imp, address _next) external {\n    boundNoContract(boundNoSys(_imp));\n\n    vm.etch(_imp, address(new ImplementationImp()).code);\n\n    assertEq(ImplementationImp(_imp).getImplementation(), address(0));\n\n    ImplementationImp(_imp).setImplementation(_next);\n\n    assertEq(ImplementationImp(_imp).getImplementation(), _next);\n    assertEq(vm.load(_imp, addrToBytes32(_imp)), addrToBytes32(_next));\n  }\n\n  function test_setImplementation_matchWallet(bytes32 _salt, address _imp, address _imp2) external {\n    Factory factory = new Factory();\n\n    ImplementationImp imp = new ImplementationImp();\n    ImplementationImp imp2 = new NextImplementation();\n\n    boundNoContract(boundDiff(boundNoSys(_imp), address(factory)));\n    boundNoContract(boundDiff(boundNoSys(_imp2), address(factory)));\n\n    vm.etch(_imp, address(imp).code);\n    address wallet = factory.deploy(_imp, _salt);\n\n    assertEq(ImplementationImp(wallet).getImplementation(), _imp);\n    assertEq(ImplementationImp(wallet).stub(), 1);\n\n    vm.etch(_imp2, address(imp2).code);\n\n    ImplementationImp(wallet).setImplementation(_imp2);\n\n    assertEq(ImplementationImp(wallet).getImplementation(), _imp2);\n    assertEq(vm.load(wallet, addrToBytes32(wallet)), addrToBytes32(_imp2));\n    assertEq(ImplementationImp(wallet).stub(), 2);\n  }\n}\n"
  },
  {
    "path": "foundry_test/modules/commons/ModuleCalls.t.sol",
    "content": "// SPDX-License-Identifier: Apache-2.0\npragma solidity 0.8.18;\n\nimport \"contracts/modules/commons/ModuleCalls.sol\";\nimport \"contracts/Factory.sol\";\n\nimport \"foundry_test/base/AdvTest.sol\";\n\n\ncontract ImmutableBytes {\n  bytes public b;\n\n  constructor (bytes memory _b) {\n    b = _b;\n  }\n\n  fallback() external payable {}\n  receive() external payable {}\n}\n\ncontract WillRevert {\n  ImmutableBytes immutable private ib;\n\n  constructor (bytes memory _err) {\n    ib = new ImmutableBytes(_err);\n  }\n\n  fallback() external payable {\n    _revert();\n  }\n\n  receive() external payable {\n    _revert();\n  }\n\n  function _revert() internal view {\n    bytes memory e = ib.b();\n    assembly {\n      revert(add(e, 32), mload(e))\n    }\n  }\n}\n\ncontract WillDelegateTo {\n  address immutable private expectSelf;\n  bytes32 immutable private expectData;\n  address immutable private delegate;\n\n  constructor (address _expectSelf, bytes memory _expectData) {\n    expectSelf = _expectSelf;\n    expectData = keccak256(_expectData);\n    delegate = address(this);\n  }\n\n  fallback() external payable {\n    _check();\n  }\n\n  receive() external payable {\n    _check();\n  }\n\n  function _check() internal {\n    bytes32 dataHash = keccak256(msg.data);\n    require(dataHash == expectData && address(this) == expectSelf);\n\n    bytes32 key = keccak256(abi.encode(delegate));\n    assembly { sstore(key, add(sload(key), 1)) }\n  }\n}\n\ncontract ModuleCallsImp is ModuleCalls {\n  function validateNonce(uint256 _rawNonce) external {\n    _validateNonce(_rawNonce);\n  }\n\n  function writeNonce(uint256 _space, uint256 _nonce) external {\n    _writeNonce(_space, _nonce);\n  }\n\n  // Module Auth imp\n  mapping(bytes32 => mapping(bytes => bytes32)) public sigToSubdigest;\n  mapping(bytes32 => mapping(bytes => bool)) public sigToIsValid;\n\n  function _signatureValidation(\n    bytes32 _digest,\n    bytes calldata _signature\n  ) internal override view returns (\n    bool isValid,\n    bytes32 subdigest\n  ) {\n    subdigest = sigToSubdigest[_digest][_signature];\n    isValid = sigToIsValid[_digest][_signature];\n  }\n\n  function mockSignature(\n    bytes32 _digest,\n    bytes calldata _signature,\n    bytes32 _subdigest,\n    bool _isValid\n  ) external  {\n    sigToSubdigest[_digest][_signature] = _subdigest;\n    sigToIsValid[_digest][_signature] = _isValid;\n  }\n\n  function signatureRecovery(bytes32, bytes calldata) public override view returns (\n    uint256, uint256, bytes32, bytes32, uint256\n  ) {\n  }\n\n  function _isValidImage(bytes32) internal override view returns (bool) {\n  }\n\n  function updateImageHash(bytes32) external override {\n  }\n\n  function _updateImageHash(bytes32) internal override {\n  }\n}\n\ncontract ModuleCallsTest is AdvTest {\n  ModuleCallsImp private template;\n  ModuleCallsImp private imp;\n  Factory private factory;\n\n  function setUp() external {\n    template = new ModuleCallsImp();\n    factory = new Factory();\n    imp = ModuleCallsImp(factory.deploy(address(template), bytes32(0)));\n  }\n\n  struct ToValAndData {\n    address target;\n    uint256 value;\n    bytes data;\n  }\n\n  event TxExecuted(bytes32 _tx) anonymous;\n  event NonceChange(uint256 _space, uint256 _newNonce);\n  event TxFailed(bytes32 _tx, bytes _reason);\n  event GapNonceChange(uint256 _space, uint256 _oldNonce, uint256 _newNonce);\n  event NoNonceUsed();\n\n  function test_execute(ToValAndData[] memory _rtxs, bytes memory _sig, bytes32 _subdigest) external {\n    uint256 size = mayBoundArr(_rtxs.length);\n    IModuleCalls.Transaction[] memory txs = new IModuleCalls.Transaction[](size);\n    uint256 total;\n\n    for (uint256 i = 0; i < size; i++) {\n      txs[i].data = _rtxs[i].data;\n      txs[i].target = boundNoBalance(boundNoContract(boundDiff(boundNoSys(_rtxs[i].target), address(template), address(imp), address(factory))));\n      txs[i].value = bound(_rtxs[i].value, 0, type(uint256).max - total);\n\n      total += txs[i].value;\n    }\n\n    vm.deal(address(imp), total);\n    bytes32 digest = keccak256(abi.encode(0, txs));\n    imp.mockSignature(digest, _sig, _subdigest, false);\n\n    vm.expectRevert(abi.encodeWithSignature('InvalidSignature(bytes32,bytes)', _subdigest, _sig));\n    imp.execute(txs, 0, _sig);\n\n    vm.expectRevert(abi.encodeWithSignature('BadNonce(uint256,uint256,uint256)', 0, 1, 0));\n    imp.execute(txs, 1, _sig);\n\n    imp.mockSignature(digest, _sig, _subdigest, true);\n\n    vm.expectEmit(true, true, true, true, address(imp));\n    emit NonceChange(0, 1);\n\n    for (uint256 i = 0; i < size; i++) {\n      vm.expectCall(txs[i].target, txs[i].data);\n    }\n\n    imp.execute(txs, 0, _sig);\n\n    assertEq(imp.nonce(), 1);\n\n    for (uint256 i = 0; i < size; i++) {\n      assertTrue(txs[i].target.balance >= txs[i].value);\n    }\n  }\n\n  function test_execute_reverts(\n    ToValAndData[] memory _rtxs,\n    uint256 _reverti,\n    bool _revertsOnErr,\n    bool _delegateCall,\n    bytes memory _err,\n    bytes memory _sig,\n    bytes32 _subdigest\n  ) external {\n    uint256 size = mayBoundArr(_rtxs.length);\n    vm.assume(size != 0);\n\n    IModuleCalls.Transaction[] memory txs = new IModuleCalls.Transaction[](size);\n    uint256 total;\n\n    _reverti = bound(_reverti, 0, size - 1);\n\n    address willRevert = address(new WillRevert(_err));\n\n    for (uint256 i = 0; i < size; i++) {\n      if (_reverti == i) {\n        txs[i].target = willRevert;\n        txs[i].revertOnError = _revertsOnErr;\n        txs[i].delegateCall = _delegateCall;\n      } else {\n        txs[i].target = boundNoBalance(boundNoContract(boundDiff(boundNoSys(_rtxs[i].target), address(template), address(imp), address(factory))));\n      }\n\n      txs[i].data = _rtxs[i].data;\n      txs[i].value = bound(_rtxs[i].value, 0, type(uint256).max - total);\n\n      total += txs[i].value;\n    }\n\n    vm.deal(address(imp), total);\n\n    bytes32 digest = keccak256(abi.encode(0, txs));\n    imp.mockSignature(digest, _sig, _subdigest, true);\n\n\n    if (_revertsOnErr) {\n      vm.expectRevert(_err);\n    }\n\n    imp.execute(txs, 0, _sig);\n\n    for (uint256 i = 0; i < size; i++) {\n      if (_revertsOnErr || txs[i].target == willRevert) {\n        assertEq(txs[i].target.balance, 0);\n      } else {\n        assertTrue(txs[i].target.balance >= txs[i].value);\n      }\n    }\n  }\n\n  function test_execute_delegateCall(\n    ToValAndData[] memory _rtxs,\n    bytes memory _sig,\n    bytes32 _subdigest\n  ) external {\n    uint256 size = mayBoundArr(_rtxs.length);\n    IModuleCalls.Transaction[] memory txs = new IModuleCalls.Transaction[](size);\n\n    for (uint256 i = 0; i < size; i++) {\n      txs[i].data = _rtxs[i].data;\n      txs[i].target = address(new WillDelegateTo(address(imp), _rtxs[i].data));\n      txs[i].value = _rtxs[i].value;\n      txs[i].delegateCall = true;\n    }\n\n    bytes32 digest = keccak256(abi.encode(0, txs));\n    imp.mockSignature(digest, _sig, _subdigest, true);\n\n    imp.execute(txs, 0, _sig);\n    assertEq(imp.nonce(), 1);\n\n    for (uint256 i = 0; i < size; i++) {\n      bytes32 key = keccak256(abi.encode(txs[i].target));\n      assertTrue(uint256(vm.load(address(imp), key)) != 0);\n    }\n  }\n\n  function test_selfExecute(ToValAndData[] memory _rtxs) external {\n    uint256 size = mayBoundArr(_rtxs.length);\n    IModuleCalls.Transaction[] memory txs = new IModuleCalls.Transaction[](size);\n    uint256 total;\n\n    for (uint256 i = 0; i < size; i++) {\n      txs[i].data = _rtxs[i].data;\n      txs[i].target = boundNoBalance(boundNoContract(boundDiff(boundNoSys(_rtxs[i].target), address(template), address(imp), address(factory))));\n      txs[i].value = bound(_rtxs[i].value, 0, type(uint256).max - total);\n\n      total += txs[i].value;\n    }\n\n    vm.deal(address(imp), total);\n\n    for (uint256 i = 0; i < size; i++) {\n      vm.expectCall(txs[i].target, txs[i].data);\n    }\n\n    vm.prank(address(imp));\n    imp.selfExecute(txs);\n\n    for (uint256 i = 0; i < size; i++) {\n      assertTrue(txs[i].target.balance >= txs[i].value);\n    }\n  }\n\n  function test_fail_selfExecute_NotSelf(ToValAndData[] memory _rtxs, address _notself) external {\n    _notself = boundDiff(_notself, address(imp));\n\n    uint256 size = mayBoundArr(_rtxs.length);\n    IModuleCalls.Transaction[] memory txs = new IModuleCalls.Transaction[](size);\n\n    for (uint256 i = 0; i < size; i++) {\n      txs[i].data = _rtxs[i].data;\n      txs[i].target = _rtxs[i].target;\n      txs[i].value = _rtxs[i].value;\n    }\n\n    vm.prank(_notself);\n    vm.expectRevert(abi.encodeWithSignature('OnlySelfAuth(address,address)', _notself, address(imp)));\n    imp.selfExecute(txs);\n  }\n\n  function test_validateNonce_Normal(\n    uint160 _space,\n    uint88 _nonce\n  ) external {\n    uint256 encoded = uint256(\n      abi.decode(\n        abi.encodePacked(\n          _space,\n          uint8(0),\n          _nonce\n        ),\n        (bytes32))\n    );\n\n    imp.writeNonce(_space, _nonce);\n\n    vm.expectEmit(true, true, true, true, address(imp));\n    emit NonceChange(_space, uint256(_nonce) + 1);\n    imp.validateNonce(encoded);\n\n    assertEq(imp.readNonce(_space), uint256(_nonce) + 1);\n    assertEq(imp.nonce(), _space == 0 ? uint256(_nonce) + 1 : 0);\n  }\n\n function test_fail_validateNonce_Normal_Bad(\n    uint160 _space,\n    uint88 _nonce,\n    uint88 _badprev\n  ) external {\n    _badprev = uint88(boundDiff(_badprev, _nonce));\n\n    uint256 encoded = uint256(\n      abi.decode(\n        abi.encodePacked(\n          _space,\n          uint8(0),\n          _nonce\n        ),\n        (bytes32))\n    );\n\n    imp.writeNonce(_space, _badprev);\n\n    vm.expectRevert(abi.encodeWithSignature('BadNonce(uint256,uint256,uint256)', _space, _nonce, _badprev));\n    imp.validateNonce(encoded);\n  }\n\n  function test_fail_noDelegatecall(ToValAndData[] memory _rtxs, bytes memory _sig, uint256 _nonce) external {\n    uint256 size = mayBoundArr(_rtxs.length);\n    IModuleCalls.Transaction[] memory txs = new IModuleCalls.Transaction[](size);\n    uint256 total;\n\n    for (uint256 i = 0; i < size; i++) {\n      txs[i].data = _rtxs[i].data;\n      txs[i].target = boundNoSys(_rtxs[i].target);\n      txs[i].value = bound(_rtxs[i].value, 0, type(uint256).max - total);\n\n      total += txs[i].value;\n    }\n\n    vm.deal(address(imp), total);\n    vm.expectRevert(abi.encodeWithSignature('OnlyDelegatecall()'));\n    template.execute(txs, _nonce, _sig);\n  }\n}\n"
  },
  {
    "path": "foundry_test/modules/commons/ModuleERC5719.t.sol",
    "content": "// SPDX-License-Identifier: Apache-2.0\npragma solidity 0.8.18;\n\nimport \"contracts/modules/commons/ModuleERC5719.sol\";\n\nimport \"foundry_test/base/AdvTest.sol\";\n\ncontract ModuleERC5719Test is AdvTest {\n  ModuleERC5719 private module;\n\n  function setUp() public {\n    module = new ModuleERC5719();\n  }\n\n  function test_getAlternativeSignature() external {\n    // 0xba5a3cbb592813d90eae65a3aac33e9b6dfc7be50623aa25e151fe3da06c8443\n    // ==\n    // ipfs://bafybeif2li6lwwjicpmq5ltfuovmgpu3nx6hxzigeovclykr7y62a3eeim\n\n    // 0xb6f77d000c8791676d96aedac165dd1bb2da4a5baf78198b9f391fc76b893f46\n    // ==\n    // ipfs://bafybeifw656qadehsftw3fvo3lawlxi3wlneuw5ppamyxhzzd7dwxcj7iy\n\n    vm.startPrank(address(module));\n\n    bytes32 root = 0xba5a3cbb592813d90eae65a3aac33e9b6dfc7be50623aa25e151fe3da06c8443;\n    module.updateIPFSRoot(root);\n\n    assertEq(\n      module.getAlternativeSignature(0x64f16a2f2c80ab7f3b0e0edc67c0bf1399402dc389cfb4271ba58abe3f61ea16),\n      'ipfs://bafybeif2li6lwwjicpmq5ltfuovmgpu3nx6hxzigeovclykr7y62a3eeim/ERC5719/0x64f16a2f2c80ab7f3b0e0edc67c0bf1399402dc389cfb4271ba58abe3f61ea16'\n    );\n\n    root = 0xb6f77d000c8791676d96aedac165dd1bb2da4a5baf78198b9f391fc76b893f46;\n    module.updateIPFSRoot(root);\n\n    assertEq(\n      module.getAlternativeSignature(0x11b858b3b52eb84e900639ebb082aeacb10bd9d239f58ae3f7092885cc3593b6),\n      'ipfs://bafybeifw656qadehsftw3fvo3lawlxi3wlneuw5ppamyxhzzd7dwxcj7iy/ERC5719/0x11b858b3b52eb84e900639ebb082aeacb10bd9d239f58ae3f7092885cc3593b6'\n    );\n  }\n}\n"
  },
  {
    "path": "foundry_test/modules/commons/ModuleExtraAuth.t.sol",
    "content": "// SPDX-License-Identifier: Apache-2.0\npragma solidity 0.8.18;\n\nimport \"contracts/modules/commons/ModuleExtraAuth.sol\";\n\nimport \"foundry_test/base/AdvTest.sol\";\n\n\ncontract ExtStore {\n  mapping(bytes32 => bool) private imageHashToIsValid;\n\n  function setValidImageHash(bytes32 _imageHash, bool _isValid) external {\n    imageHashToIsValid[_imageHash] = _isValid;\n  }\n\n  function isValidImage(bytes32 _imageHash) external view returns (bool) {\n    return imageHashToIsValid[_imageHash];\n  }\n}\n\nabstract contract ModuleAuthImp is IModuleAuth {\n  ExtStore private immutable EXT_STORE;\n\n  constructor() {\n    EXT_STORE = new ExtStore();\n  }\n\n  function setValidImageHash(bytes32 _imageHash, bool _isValid) external {\n    EXT_STORE.setValidImageHash(_imageHash, _isValid);\n  }\n\n  function _isValidImage(bytes32 _imageHash) internal override virtual view returns (bool) {\n    return EXT_STORE.isValidImage(_imageHash);\n  }\n}\n\ncontract ModuleExtraAuthImp2 is ModuleAuthImp, ModuleExtraAuth {\n  function _isValidImage(bytes32 _imageHash) internal override(\n    ModuleAuthImp,\n    ModuleExtraAuth\n  ) view returns (bool) {\n    return super._isValidImage(_imageHash);\n  }\n\n  function isValidImage(bytes32 _imageHash) external view returns (bool) {\n    return _isValidImage(_imageHash);\n  }\n\n  function _updateImageHash(bytes32) internal override virtual {\n    revert('not implemented');\n  }\n}\n\ncontract ModuleExtraAuthTest is AdvTest {\n  ModuleExtraAuthImp2 private imp;\n\n  function setUp() public {\n    imp = new ModuleExtraAuthImp2();\n  }\n\n  event SetExtraImageHash(bytes32 indexed _imageHash, uint256 _expiration);\n\n  function test_shouldAcceptExtraImageHashes(\n    bytes32 _imageHashb,\n    bytes32 _imageHash1,\n    bytes32 _imageHash2,\n    uint256 _expiration1,\n    uint256 _expiration2\n  ) external {\n    _expiration1 = bound(_expiration1, block.timestamp + 1, type(uint256).max);\n    _expiration2 = bound(_expiration2, block.timestamp + 1, type(uint256).max);\n\n    assertFalse(imp.isValidImage(_imageHashb));\n    assertFalse(imp.isValidImage(_imageHash1));\n    assertFalse(imp.isValidImage(_imageHash2));\n\n    imp.setValidImageHash(_imageHashb, true);\n    assertTrue(imp.isValidImage(_imageHashb));\n    assertEq(imp.isValidImage(_imageHash1), _imageHash1 == _imageHashb);\n    assertEq(imp.isValidImage(_imageHash2), _imageHash2 == _imageHashb);\n\n    vm.prank(address(imp));\n    vm.expectEmit(true, true, true, true, address(imp));\n    emit SetExtraImageHash(_imageHash1, _expiration1);\n    imp.setExtraImageHash(_imageHash1, _expiration1);\n\n    assertTrue(imp.isValidImage(_imageHash1));\n    assertEq(imp.isValidImage(_imageHash2), _imageHash1 == _imageHash2 || _imageHashb == _imageHash2);\n    assertTrue(imp.isValidImage(_imageHashb));\n\n    vm.prank(address(imp));\n    vm.expectEmit(true, true, true, true, address(imp));\n    emit SetExtraImageHash(_imageHash2, _expiration2);\n    imp.setExtraImageHash(_imageHash2, _expiration2);\n\n    assertTrue(imp.isValidImage(_imageHashb));\n    assertTrue(imp.isValidImage(_imageHash1));\n    assertTrue(imp.isValidImage(_imageHash2));\n  }\n\n  function test_shouldRejectExpiredImageHash(\n    bytes32 _imageHashb,\n    bytes32 _imageHash1,\n    bytes32 _imageHash2,\n    uint256 _expiration1,\n    uint256 _expiration2\n  ) external {\n    _expiration1 = bound(_expiration1, block.timestamp + 1, type(uint256).max);\n    _expiration2 = bound(_expiration2, 0,  block.timestamp);\n\n    imp.setValidImageHash(_imageHashb, true);\n\n    vm.prank(address(imp));\n    vm.expectEmit(true, true, true, true, address(imp));\n    emit SetExtraImageHash(_imageHash1, _expiration1);\n    imp.setExtraImageHash(_imageHash1, _expiration1);\n\n    vm.prank(address(imp));\n    vm.expectEmit(true, true, true, true, address(imp));\n    emit SetExtraImageHash(_imageHash2, _expiration2);\n    imp.setExtraImageHash(_imageHash2, _expiration2);\n\n    assertTrue(imp.isValidImage(_imageHashb));\n    assertEq(imp.isValidImage(_imageHash1), _imageHash1 != _imageHash2 || _imageHash1 == _imageHashb);\n    assertEq(imp.isValidImage(_imageHash2), _imageHash2 == _imageHashb);\n  }\n\n  struct SetIh {\n    bytes32 imageHash;\n    uint256 expiration;\n  }\n\n  mapping(bytes32 => bool) private wasCleared;\n\n  function test_shouldClearExtraImageHashes(\n    bytes32 _base,\n    SetIh[] calldata _set,\n    bytes32[] calldata _clear\n  ) external {\n    uint256 sizeSet = mayBoundArr(_set.length);\n    uint256 sizeClear = mayBoundArr(_clear.length);\n\n    imp.setValidImageHash(_base, true);\n\n    vm.startPrank(address(imp));\n    for (uint256 i = 0; i < sizeSet; i++) {\n      uint256 expiration = bound(_set[i].expiration, block.timestamp + 1, type(uint256).max);\n      imp.setExtraImageHash(_set[i].imageHash, expiration);\n    }\n\n    bytes32[] memory toClear = new bytes32[](sizeClear);\n    for (uint256 i = 0; i < sizeClear; i++) {\n      toClear[i] = _clear[i];\n    }\n\n    imp.clearExtraImageHashes(toClear);\n\n    for (uint256 i = 0; i < sizeClear; i++) {\n      assertEq(imp.isValidImage(_clear[i]), _clear[i] == _base);\n      wasCleared[_clear[i]] = true;\n    }\n\n    for (uint256 i = 0; i < sizeSet; i++) {\n      assertEq(\n        imp.isValidImage(_set[i].imageHash),\n        _set[i].imageHash == _base || !wasCleared[_set[i].imageHash]\n      );\n    }\n  }\n\n  function test_fail_setExtraImageHash_notSelf(\n    address _caller,\n    bytes32 _imageHash,\n    uint256 _expiration\n  ) external {\n    boundDiff(_caller, address(imp));\n    vm.expectRevert(abi.encodeWithSignature('OnlySelfAuth(address,address)', _caller, address(imp)));\n    vm.prank(_caller);\n    imp.setExtraImageHash(_imageHash, _expiration);\n  }\n\n  function test_fail_clearExtraImageHash_notSelf(\n    address _caller,\n    bytes32[] calldata _clear\n  ) external {\n    boundDiff(_caller, address(imp));\n    vm.expectRevert(abi.encodeWithSignature('OnlySelfAuth(address,address)', _caller, address(imp)));\n    vm.prank(_caller);\n    imp.clearExtraImageHashes(_clear);\n  }\n}\n"
  },
  {
    "path": "foundry_test/modules/commons/ModuleIPFS.t.sol",
    "content": "// SPDX-License-Identifier: Apache-2.0\npragma solidity 0.8.18;\n\nimport \"contracts/modules/commons/ModuleIPFS.sol\";\n\nimport \"foundry_test/base/AdvTest.sol\";\n\ncontract ModuleIPFSTest is AdvTest {\n  ModuleIPFS private module;\n\n  function setUp() public {\n    module = new ModuleIPFS();\n  }\n\n  function test_exposeRoot() external {\n    // 0xba5a3cbb592813d90eae65a3aac33e9b6dfc7be50623aa25e151fe3da06c8443\n    // ==\n    // ipfs://bafybeif2li6lwwjicpmq5ltfuovmgpu3nx6hxzigeovclykr7y62a3eeim\n\n    // 0xb6f77d000c8791676d96aedac165dd1bb2da4a5baf78198b9f391fc76b893f46\n    // ==\n    // ipfs://bafybeifw656qadehsftw3fvo3lawlxi3wlneuw5ppamyxhzzd7dwxcj7iy\n\n    vm.startPrank(address(module));\n\n    bytes32 root = 0xba5a3cbb592813d90eae65a3aac33e9b6dfc7be50623aa25e151fe3da06c8443;\n    module.updateIPFSRoot(root);\n\n    assertEq(module.ipfsRootBytes32(), root);\n    assertEq(\n      module.ipfsRoot(),\n      'ipfs://bafybeif2li6lwwjicpmq5ltfuovmgpu3nx6hxzigeovclykr7y62a3eeim'\n    );\n\n    root = 0xb6f77d000c8791676d96aedac165dd1bb2da4a5baf78198b9f391fc76b893f46;\n    module.updateIPFSRoot(root);\n\n    assertEq(module.ipfsRootBytes32(), root);\n    assertEq(\n      module.ipfsRoot(),\n      'ipfs://bafybeifw656qadehsftw3fvo3lawlxi3wlneuw5ppamyxhzzd7dwxcj7iy'\n    );\n  }\n\n  function test_fail_updateIPFSRoot_notSelf(address _notSelf) external {\n    boundDiff(_notSelf, address(module));\n\n    vm.prank(address(_notSelf));\n    vm.expectRevert(abi.encodeWithSignature('OnlySelfAuth(address,address)', _notSelf, address(module)));\n    module.updateIPFSRoot(0x0);\n  }\n}\n"
  },
  {
    "path": "foundry_test/modules/commons/ModuleStorage.t.sol",
    "content": "// SPDX-License-Identifier: Apache-2.0\npragma solidity 0.8.18;\n\nimport \"contracts/modules/commons/ModuleStorage.sol\";\n\nimport \"foundry_test/base/AdvTest.sol\";\n\n\ncontract ModuleStorageImp {\n  function writeBytes32(bytes32 _key, bytes32 _val) external {\n    ModuleStorage.writeBytes32(_key, _val);\n  }\n\n  function readBytes32(bytes32 _key) external view returns (bytes32) {\n    return ModuleStorage.readBytes32(_key);\n  }\n\n  function writeBytes32Map(bytes32 _key, bytes32 _subKey, bytes32 _val) external {\n    ModuleStorage.writeBytes32Map(_key, _subKey, _val);\n  }\n\n  function readBytes32Map(bytes32 _key, bytes32 _subKey) external view returns (bytes32) {\n    return ModuleStorage.readBytes32Map(_key, _subKey);\n  }\n}\n\n\ncontract ModuleStorageTest is AdvTest {\n  ModuleStorageImp private imp;\n\n  function setUp() external {\n    imp = new ModuleStorageImp();\n  }\n\n  function test_writeBytes32(\n    bytes32 _key1,\n    bytes32 _key2,\n    bytes32 _val1,\n    bytes32 _val2\n  ) external {\n    assertEq(imp.readBytes32(_key1), bytes32(0));\n    assertEq(imp.readBytes32(_key2), bytes32(0));\n\n    bool equal = _key1 == _key2;\n\n    imp.writeBytes32(_key1, _val1);\n\n    bytes32 res1 = imp.readBytes32(_key1);\n    assertEq(res1, _val1);\n    assertEq(vm.load(address(imp), _key1), res1);\n\n    imp.writeBytes32(_key2, _val2);\n\n    bytes32 res2 = imp.readBytes32(_key2);\n    res1 = imp.readBytes32(_key1);\n\n    assertEq(res1, equal ? _val2 : _val1);\n    assertEq(res2, _val2);\n    assertEq(vm.load(address(imp), _key1), res1);\n    assertEq(vm.load(address(imp), _key2), res2);\n  }\n\n  function test_writeBytes32Map(\n    bytes32 _key1,\n    bytes32 _subkey1,\n    bytes32 _val1,\n    bytes32 _key2,\n    bytes32 _subkey2,\n    bytes32 _val2\n  ) external {\n    bool equal = _key1 == _key2 && _subkey1 == _subkey2;\n    bytes32 slot1 = keccak256(abi.encode(_key1, _subkey1));\n    bytes32 slot2 = keccak256(abi.encode(_key2, _subkey2));\n    assertEq(slot1 == slot2, equal);\n\n    imp.writeBytes32Map(_key1, _subkey1, _val1);\n    bytes32 res1 = imp.readBytes32Map(_key1, _subkey1);\n    assertEq(res1, _val1);\n    assertEq(vm.load(address(imp), slot1), res1);\n\n    imp.writeBytes32Map(_key2, _subkey2, _val2);\n\n    bytes32 res2 = imp.readBytes32Map(_key2, _subkey2);\n    res1 = imp.readBytes32Map(_key1, _subkey1);\n\n    assertEq(res1, equal ? _val2 : _val1);\n    assertEq(res2, _val2);\n    assertEq(vm.load(address(imp), slot1), res1);\n    assertEq(vm.load(address(imp), slot2), res2);\n  }\n}\n"
  },
  {
    "path": "foundry_test/modules/commons/submodules/auth/SequenceBaseSig.t.sol",
    "content": "// SPDX-License-Identifier: Apache-2.0\npragma solidity 0.8.18;\n\nimport \"contracts/modules/commons/submodules/auth/SequenceBaseSig.sol\";\n\nimport \"foundry_test/base/AdvTest.sol\";\n\n\ncontract SequenceBaseSigImp {\n  function subdigest(bytes32 _digest) external view returns (bytes32) {\n    return SequenceBaseSig.subdigest(_digest);\n  }\n\n  function leafForAddressAndWeight(address _addr, uint96 _weight) external pure returns (bytes32) {\n    return SequenceBaseSig._leafForAddressAndWeight(_addr, _weight);\n  }\n\n  function recoverBranch(bytes32 _digest, bytes calldata _signature) external view returns (uint256 weight, bytes32 root) {\n    return SequenceBaseSig.recoverBranch(_digest, _signature);\n  }\n\n  function recover(bytes32 _subdigest, bytes calldata _signature) external view returns (uint256 threshold, uint256 weight, bytes32 imageHash, uint256 checkpoint) {\n    return SequenceBaseSig.recover(_subdigest, _signature);\n  }\n}\n\ncontract SequenceBaseSigTest is AdvTest {\n  SequenceBaseSigImp private lib;\n\n  uint8 private constant FLAG_SIGNATURE = 0;\n  uint8 private constant FLAG_ADDRESS = 1;\n  uint8 private constant FLAG_DYNAMIC_SIGNATURE = 2;\n  uint8 private constant FLAG_NODE = 3;\n  uint8 private constant FLAG_BRANCH = 4;\n  uint8 private constant FLAG_SUBDIGEST = 5;\n  uint8 private constant FLAG_NESTED = 6;\n\n  function setUp() public {\n    lib = new SequenceBaseSigImp();\n  }\n\n  function test_subdigest(bytes32 _digest, uint256 _chainId) external {\n    _chainId = bound(_chainId, 0, type(uint64).max);\n\n    bytes32 expected = keccak256(\n      abi.encodePacked(\n        \"\\x19\\x01\",\n        _chainId,\n        address(lib),\n        _digest\n      )\n    );\n\n    vm.chainId(_chainId);\n    bytes32 actual = lib.subdigest(_digest);\n    assertEq(actual, expected);\n  }\n\n  function test_subdigest_Fuzz_ChainId(bytes32 _digest, uint256 _chainId1, uint256 _chainId2) external {\n    _chainId1 = bound(_chainId1, 0, type(uint64).max);\n    _chainId2 = bound(_chainId2, 0, type(uint64).max);\n\n    vm.chainId(_chainId1);\n    bytes32 subdigest1 = lib.subdigest(_digest);\n\n    vm.chainId(_chainId2);\n    bytes32 subdigest2 = lib.subdigest(_digest);\n\n    assertTrue(subdigest1 != subdigest2 || _chainId1 == _chainId2);\n  }\n\n  function test_subdigest_Fuzz_Digest(bytes32 _digest1, bytes32 _digest2) external {\n    bytes32 subdigest1 = lib.subdigest(_digest1);\n    bytes32 subdigest2 = lib.subdigest(_digest2);\n\n    assertTrue(subdigest1 != subdigest2 || _digest1 == _digest2);\n  }\n\n  function test_subdigest_Fuzz_Address(bytes32 _digest, address _addr1, address _addr2) external {\n    boundNoSys(_addr1);\n    boundNoSys(_addr2);\n\n    vm.etch(_addr1, address(lib).code);\n    vm.etch(_addr2, address(lib).code);\n\n    bytes32 subdigest1 = SequenceBaseSigImp(_addr1).subdigest(_digest);\n    bytes32 subdigest2 = SequenceBaseSigImp(_addr2).subdigest(_digest);\n\n    assertTrue(subdigest1 != subdigest2 || _addr1 == _addr2);\n  }\n\n  function test_leafForAddressAndWeight(address _addr, uint96 _weight) external {\n    bytes32 expected = abi.decode(abi.encodePacked(_weight, _addr), (bytes32));\n    bytes32 actual = lib.leafForAddressAndWeight(_addr, _weight);\n    assertEq(expected, actual);\n  }\n\n  function test_leafForAddressAndWeight_fuzz(address _addr1, uint96 _weight1, address _addr2, uint96 _weight2) external {\n    bytes32 encoded1 = lib.leafForAddressAndWeight(_addr1, _weight1);\n    bytes32 encoded2 = lib.leafForAddressAndWeight(_addr2, _weight2);\n    assertEq(encoded1 == encoded2, _addr1 == _addr2 && _weight1 == _weight2);\n  }\n\n  function test_leafForHardcodedSubdigest_fuzz(bytes32 _subdigest1, bytes32 _subdigest2) external {\n    bytes32 encoded1 = SequenceBaseSig._leafForHardcodedSubdigest(_subdigest1);\n    bytes32 encoded2 = SequenceBaseSig._leafForHardcodedSubdigest(_subdigest2);\n    assertEq(encoded1 == encoded2, _subdigest1 == _subdigest2);\n  }\n\n  function test_leafForHardcodedSubdigest_fuzz_addr(address _addr, uint96 _weight, bytes32 _subdigest) external {\n    bytes32 encoded1 = SequenceBaseSig._leafForHardcodedSubdigest(_subdigest);\n    bytes32 encoded2 = SequenceBaseSig._leafForAddressAndWeight(_addr, _weight);\n    assertTrue(encoded1 != encoded2);\n  }\n\n  function test_leafForNested_fuzz(\n    bytes32 _node1,\n    uint256 _threshold1,\n    uint256 _weight1,\n    bytes32 _node2,\n    uint256 _threshold2,\n    uint256 _weight2\n  ) external {\n    bytes32 encoded1 = SequenceBaseSig._leafForNested(_node1, _threshold1, _weight1);\n    bytes32 encoded2 = SequenceBaseSig._leafForNested(_node2, _threshold2, _weight2);\n    assertEq(encoded1 == encoded2, _node1 == _node2 && _threshold1 == _threshold2 && _weight1 == _weight2);\n  }\n\n  function test_leafForNested_fuzz_addr(\n    address _addr,\n    uint96 _weight,\n    bytes32 _node,\n    uint256 _threshold,\n    uint256 _nodeWeight\n  ) external {\n    bytes32 encoded1 = SequenceBaseSig._leafForNested(_node, _threshold, _nodeWeight);\n    bytes32 encoded2 = SequenceBaseSig._leafForAddressAndWeight(_addr, _weight);\n    assertTrue(encoded1 != encoded2);\n  }\n\n  function test_leafForNested_fuzz_subdigest(\n    bytes32 _subdigest,\n    bytes32 _node,\n    uint256 _threshold,\n    uint256 _weight\n  ) external {\n    bytes32 encoded1 = SequenceBaseSig._leafForNested(_node, _threshold, _weight);\n    bytes32 encoded2 = SequenceBaseSig._leafForHardcodedSubdigest(_subdigest);\n    assertTrue(encoded1 != encoded2);\n  }\n\n  function test_recoverBranch_Addresses(bytes32 _subdigest, bytes32 _seed, address[] calldata _addresses) external {\n    bytes memory signature;\n    bytes32 root;\n\n    uint256 size = mayBoundArr(_addresses.length);\n    for (uint256 i = 0; i < size; i++) {\n      uint8 randomWeight = uint8(bound(uint256(keccak256(abi.encode(_addresses[i], i, _seed))), 0, type(uint8).max));\n\n      signature = abi.encodePacked(signature, FLAG_ADDRESS, randomWeight, _addresses[i]);\n      bytes32 node = lib.leafForAddressAndWeight(_addresses[i], randomWeight);\n      root = root != bytes32(0) ? keccak256(abi.encodePacked(root, node)) : node;\n    }\n\n    (uint256 recoveredWeight, bytes32 recoveredRoot) = lib.recoverBranch(_subdigest, signature);\n    assertEq(recoveredRoot, root);\n    assertEq(recoveredWeight, 0);\n  }\n\n  function test_recoverBranch_Nodes(bytes32 _subdigest, bytes32[] calldata _nodes) external {\n    bytes memory signature;\n    bytes32 root;\n\n    uint256 size = mayBoundArr(_nodes.length);\n    for (uint256 i = 0; i < size; i++) {\n      signature = abi.encodePacked(signature, FLAG_NODE, _nodes[i]);\n      root = root != bytes32(0) ? keccak256(abi.encodePacked(root, _nodes[i])) : _nodes[i];\n    }\n\n    (uint256 recoveredWeight, bytes32 recoveredRoot) = lib.recoverBranch(_subdigest, signature);\n    assertEq(recoveredRoot, root);\n    assertEq(recoveredWeight, 0);\n  }\n\n  function test_recoverBranch_Signatures(bytes32 _subdigest, bytes32 _seed, uint256[] memory _pks) external {\n    bytes memory signature;\n    bytes32 root;\n    uint256 total;\n\n    uint256 size = mayBoundArr(_pks.length);\n    for (uint256 i = 0; i < size; i++) {\n      _pks[i] = boundPk(_pks[i]);\n\n      uint8 randomWeight = uint8(bound(uint256(keccak256(abi.encode(_pks[i], i, _seed))), 0, type(uint8).max));\n      address addr = vm.addr(_pks[i]);\n\n      // Determine if the pk will sign, dynamic sign or just sit as addr\n      uint256 op = bound(uint256(keccak256(abi.encode(_pks[i], i, _seed, 2))), 0, 2);\n\n      if (op == 0) {\n        signature = abi.encodePacked(signature, FLAG_ADDRESS, randomWeight, addr);\n      } else {\n        bytes memory sigpart = signAndPack(_pks[i], _subdigest, 1);\n\n        total += randomWeight;\n\n        if (op == 1) {\n          signature = abi.encodePacked(signature, FLAG_SIGNATURE, randomWeight, sigpart);\n        } else {\n          signature = abi.encodePacked(signature, FLAG_DYNAMIC_SIGNATURE, randomWeight, addr, uint24(sigpart.length), sigpart);\n        }\n      }\n\n      bytes32 node = lib.leafForAddressAndWeight(addr, randomWeight);\n      root = root != bytes32(0) ? keccak256(abi.encodePacked(root, node)) : node;\n    }\n\n    (uint256 recoveredWeight, bytes32 recoveredRoot) = lib.recoverBranch(_subdigest, signature);\n    assertEq(recoveredRoot, root);\n    assertEq(recoveredWeight, total);\n  }\n\n  function test_recoverBranch_Branches(bytes32 _subdigest, bytes32 _seed, uint256[] memory _pks) external {\n    bytes memory signature;\n    bytes32 root;\n    uint256 total;\n\n    // NOTICE: too much branching will lead to stack overflow\n    uint256 size = bound(_pks.length, 0, 32);\n\n    for (uint256 i = 0; i < size; i++) {\n      if (i != 0) {\n        signature = abi.encodePacked(FLAG_BRANCH, uint24(signature.length), signature);\n      }\n\n      _pks[i] = boundPk(_pks[i]);\n\n      uint8 randomWeight = uint8(bound(uint256(keccak256(abi.encode(_pks[i], i, _seed))), 0, type(uint8).max));\n      address addr = vm.addr(_pks[i]);\n\n      // Determine if the pk will sign, dynamic sign or just sit as addr\n      uint256 op = bound(uint256(keccak256(abi.encode(_pks[i], i, _seed, 2))), 0, 2);\n\n      if (op == 0) {\n        signature = abi.encodePacked(FLAG_ADDRESS, randomWeight, addr, signature);\n      } else {\n        bytes memory sigpart = signAndPack(_pks[i], _subdigest, 1);\n\n        total += randomWeight;\n\n        if (op == 1) {\n          signature = abi.encodePacked(FLAG_SIGNATURE, randomWeight, sigpart, signature);\n        } else {\n          signature = abi.encodePacked(FLAG_DYNAMIC_SIGNATURE, randomWeight, addr, uint24(sigpart.length), sigpart, signature);\n        }\n      }\n\n      bytes32 node = lib.leafForAddressAndWeight(addr, randomWeight);\n      // Hash in reverse order requires branching, root/node -> node/root\n      root = root != bytes32(0) ? keccak256(abi.encodePacked(node, root)) : node;\n    }\n\n    (uint256 recoveredWeight, bytes32 recoveredRoot) = lib.recoverBranch(_subdigest, signature);\n    assertEq(recoveredRoot, root);\n    assertEq(recoveredWeight, total);\n  }\n\n  function test_recoverBranch_Empty(bytes32 _hash) external {\n    (uint256 weight1, bytes32 root1) = lib.recoverBranch(_hash, abi.encodePacked(FLAG_NODE, bytes32(0)));\n    (uint256 weight2, bytes32 root2) = lib.recoverBranch(_hash, bytes(''));\n\n    assertEq(weight2, 0);\n    assertEq(root2, bytes32(0));\n    assertEq(weight1, weight2);\n    assertEq(root1, root2);\n  }\n\n  function test_recoverBranch_Fail_InvalidFlag(uint8 _flag, bytes23 _hash, bytes calldata _sufix) external {\n    uint8(boundDiff(_flag, FLAG_SIGNATURE, FLAG_ADDRESS, FLAG_DYNAMIC_SIGNATURE, FLAG_NODE, FLAG_BRANCH, FLAG_SUBDIGEST, FLAG_NESTED));\n\n    vm.expectRevert(abi.encodeWithSignature('InvalidSignatureFlag(uint256)', _flag));\n    lib.recoverBranch(_hash, abi.encodePacked(_flag, _sufix));\n  }\n\n  function test_recover(bytes32 _subdigest, uint256 _pk, uint32 _checkpoint, uint16 _threshold, uint8 _weight) external {\n    _pk = boundPk(_pk);\n\n    bytes memory signature = signAndPack(_pk, _subdigest, 1);\n    address addr = vm.addr(_pk);\n\n    bytes32 expectImageHash = abi.decode(abi.encodePacked(uint96(_weight), addr), (bytes32));\n    expectImageHash = keccak256(abi.encodePacked(expectImageHash, uint256(_threshold)));\n    expectImageHash = keccak256(abi.encodePacked(expectImageHash, uint256(_checkpoint)));\n\n    bytes memory encoded = abi.encodePacked(_threshold, _checkpoint, FLAG_SIGNATURE, _weight, signature);\n    (uint256 threshold, uint256 weight, bytes32 imageHash, uint256 checkpoint) = lib.recover(_subdigest, encoded);\n\n    assertEq(weight, _weight);\n    assertEq(threshold, _threshold);\n    assertEq(imageHash, expectImageHash);\n    assertEq(checkpoint, _checkpoint);\n  }\n\n  function test_recover_Fail_EmptySignature(bytes32 _subdigest) external {\n    vm.expectRevert();\n    lib.recover(_subdigest, bytes(''));\n  }\n}\n"
  },
  {
    "path": "foundry_test/modules/commons/submodules/auth/SequenceChainedSig.t.sol",
    "content": "// SPDX-License-Identifier: Apache-2.0\npragma solidity 0.8.18;\n\nimport \"contracts/modules/commons/submodules/auth/SequenceChainedSig.sol\";\n\nimport \"foundry_test/base/AdvTest.sol\";\n\n\ncontract SequenceChainedSigImp is SequenceChainedSig {\n  function pchainedRecover(bytes32 _digest, bytes calldata _signature) external view returns (\n    uint256 threshold,\n    uint256 weight,\n    bytes32 imageHash,\n    bytes32 subdigest,\n    uint256 checkpoint\n  ) {\n    return chainedRecover(_digest, _signature);\n  }\n\n  struct MockedSignature {\n    bool exists;\n    uint256 threshold;\n    uint256 weight;\n    bytes32 imageHash;\n    bytes32 subdigest;\n    uint256 checkpoint;\n  }\n\n  mapping(bytes32 => mapping(bytes => MockedSignature)) public mockedSignatures;\n\n  function isMocked(bytes32 _digest, bytes calldata _signature) external view returns (bool) {\n    return mockedSignatures[_digest][_signature].exists;\n  }\n\n  function mockSignature(\n    bytes32 _digest,\n    bytes calldata _signature,\n    uint256 _threshold,\n    uint256 _weight,\n    bytes32 _imageHash,\n    bytes32 _subdigest,\n    uint256 _checkpoint\n  ) external {\n    MockedSignature memory sig;\n\n    sig.exists = true;\n    sig.threshold = _threshold;\n    sig.weight = _weight;\n    sig.imageHash = _imageHash;\n    sig.subdigest = _subdigest;\n    sig.checkpoint = _checkpoint;\n\n    mockedSignatures[_digest][_signature] = sig;\n  }\n\n  function signatureRecovery(\n    bytes32 _digest,\n    bytes calldata _signature\n  ) public override view returns (\n    uint256 threshold,\n    uint256 weight,\n    bytes32 imageHash,\n    bytes32 subdigest,\n    uint256 checkpoint\n  ) {\n    if (mockedSignatures[_digest][_signature].exists) {\n      return (\n        mockedSignatures[_digest][_signature].threshold,\n        mockedSignatures[_digest][_signature].weight,\n        mockedSignatures[_digest][_signature].imageHash,\n        mockedSignatures[_digest][_signature].subdigest,\n        mockedSignatures[_digest][_signature].checkpoint\n      );\n    } else {\n      revert('invalid mocked signature');\n    }\n  }\n\n  function hashSetImageHashStruct(bytes32 _imageHash) external pure returns (bytes32) {\n    return _hashSetImageHashStruct(_imageHash);\n  }\n\n  function _signatureValidation(\n    bytes32,\n    bytes calldata\n  ) internal override pure returns (\n    bool, bytes32\n  ) {\n    revert('not implemented');\n  }\n\n  function _isValidImage(bytes32) internal override pure returns (bool) {\n    revert('not implemented');\n  }\n\n  function updateImageHash(bytes32) external pure override {\n    revert('not implemented');\n  }\n\n  function _updateImageHash(bytes32) internal pure override {\n    revert('not implemented');\n  }\n}\n\ncontract SequenceChainedSigTest is AdvTest {\n  SequenceChainedSigImp private lib;\n\n  function setUp() public {\n    lib = new SequenceChainedSigImp();\n  }\n\n  struct HashAndSignature {\n    bytes32 imageHash;\n    uint256 threshold;\n    uint256 weight;\n    uint56 checkpointDelta;\n    bytes signature;\n  }\n\n  function test_chainedRecover(uint8 _prefix, bytes32 _digest, HashAndSignature[] memory _steps) external {\n    vm.assume(_steps.length > 0);\n\n    bytes memory signature = abi.encodePacked(_prefix);\n\n    uint256 size = boundDiff(mayBoundArr(_steps.length), 0);\n\n    bytes32 nextDigest = _digest;\n    uint32 checkpoint = type(uint32).max;\n\n    for (uint256 i = 0; i < size; i++) {\n      _steps[i].weight = bound(_steps[i].weight, _steps[i].threshold, type(uint256).max);\n\n      if (i != 0) {\n        checkpoint -= uint32(bound(_steps[i].checkpointDelta, 1, type(uint24).max));\n      }\n\n      if (lib.isMocked(nextDigest, _steps[i].signature)) {\n        _steps[i].signature = abi.encodePacked(_steps[i].signature, _steps[i].imageHash);\n      }\n\n      lib.mockSignature(\n        nextDigest,\n        _steps[i].signature,\n        _steps[i].threshold,\n        _steps[i].weight,\n        _steps[i].imageHash,\n        i == 0 ? _digest : nextDigest,\n        checkpoint\n      );\n\n      nextDigest = lib.hashSetImageHashStruct(_steps[i].imageHash);\n      signature = abi.encodePacked(signature, uint24(_steps[i].signature.length), _steps[i].signature);\n    }\n\n    (uint256 threshold, uint256 weight, bytes32 imageHash, bytes32 subdigest, uint256 rcheckpoint) = lib.pchainedRecover(_digest, signature);\n\n    assertEq(imageHash, _steps[size - 1].imageHash);\n    assertEq(threshold, _steps[size - 1].threshold);\n    assertEq(weight, _steps[size - 1].weight);\n    assertEq(subdigest, _digest);\n    assertEq(rcheckpoint, checkpoint);\n  }\n\n  function test_chainedRecover_Fail_LowWeight(uint8 _prefix, uint256 _badi, bytes32 _digest, HashAndSignature[] memory _steps) external {\n    vm.assume(_steps.length > 0);\n\n    bytes memory signature = abi.encodePacked(_prefix);\n\n    uint256 size = boundDiff(mayBoundArr(_steps.length), 0);\n    _badi = bound(_badi, 0, size - 1);\n\n    bytes32 nextDigest = _digest;\n    uint64 checkpoint = type(uint64).max;\n\n    for (uint256 i = 0; i < size; i++) {\n      if (i == _badi) {\n        _steps[i].threshold = bound(_steps[i].threshold, 1, type(uint256).max);\n        _steps[i].weight = bound(_steps[i].weight, 0, _steps[i].threshold - 1);\n      } else {\n        _steps[i].weight = bound(_steps[i].weight, _steps[i].threshold, type(uint256).max);\n      }\n\n      if (i != 0) {\n        checkpoint -= uint64(bound(_steps[i].checkpointDelta, 1, type(uint56).max));\n      }\n\n      if (lib.isMocked(nextDigest, _steps[i].signature)) {\n        _steps[i].signature = abi.encodePacked(_steps[i].signature, _steps[i].imageHash);\n      }\n\n      lib.mockSignature(\n        nextDigest,\n        _steps[i].signature,\n        _steps[i].threshold,\n        _steps[i].weight,\n        _steps[i].imageHash,\n        i == 0 ? _digest : nextDigest,\n        checkpoint\n      );\n\n      nextDigest = lib.hashSetImageHashStruct(_steps[i].imageHash);\n      signature = abi.encodePacked(signature, uint24(_steps[i].signature.length), _steps[i].signature);\n    }\n\n    vm.expectRevert(abi.encodeWithSignature('LowWeightChainedSignature(bytes,uint256,uint256)', _steps[_badi].signature, _steps[_badi].threshold, _steps[_badi].weight));\n    lib.pchainedRecover(_digest, signature);\n  }\n\n  function test_chainedRecover_Fail_Checkpoint(uint8 _prefix, uint256 _badi, bytes32 _digest, HashAndSignature[] memory _steps) external {\n    vm.assume(_steps.length >= 1);\n\n    bytes memory signature = abi.encodePacked(_prefix);\n\n    uint256 size = boundDiff(mayBoundArr(_steps.length), 0);\n    _badi = bound(_badi, 0, size - 1);\n\n    bytes32 nextDigest = _digest;\n    uint64 checkpoint = type(uint64).max;\n\n    uint64 badCheckpoint;\n    uint64 prevToBadCheckpoint;\n\n    for (uint256 i = 0; i < size; i++) {\n      _steps[i].weight = bound(_steps[i].weight, _steps[i].threshold, type(uint256).max);\n\n      if (i != 0) {\n        if (_badi == i) {\n          prevToBadCheckpoint = checkpoint;\n          checkpoint = uint64(bound(uint256(checkpoint) + _steps[i].checkpointDelta, checkpoint, type(uint64).max));\n          badCheckpoint = checkpoint;\n        } else {\n          checkpoint -= uint64(bound(_steps[i].checkpointDelta, 1, type(uint56).max));\n        }\n      }\n\n      if (lib.isMocked(nextDigest, _steps[i].signature)) {\n        _steps[i].signature = abi.encodePacked(_steps[i].signature, _steps[i].imageHash);\n      }\n\n      lib.mockSignature(\n        nextDigest,\n        _steps[i].signature,\n        _steps[i].threshold,\n        _steps[i].weight,\n        _steps[i].imageHash,\n        i == 0 ? _digest : nextDigest,\n        checkpoint\n      );\n\n      nextDigest = lib.hashSetImageHashStruct(_steps[i].imageHash);\n      signature = abi.encodePacked(signature, uint24(_steps[i].signature.length), _steps[i].signature);\n    }\n\n    if (_badi != 0) {\n      vm.expectRevert(abi.encodeWithSignature('WrongChainedCheckpointOrder(uint256,uint256)', badCheckpoint, prevToBadCheckpoint));\n    }\n\n    lib.pchainedRecover(_digest, signature);\n  }\n\n  function test_chainedRecover_Fail_EmptySignature(bytes32 _digest) external {\n    vm.expectRevert();\n    lib.pchainedRecover(_digest, bytes(''));\n  }\n}\n"
  },
  {
    "path": "foundry_test/modules/commons/submodules/auth/SequenceDynamicSig.t.sol",
    "content": "// SPDX-License-Identifier: Apache-2.0\npragma solidity 0.8.18;\n\nimport \"contracts/modules/commons/submodules/auth/SequenceBaseSig.sol\";\nimport \"contracts/modules/commons/submodules/auth/SequenceDynamicSig.sol\";\n\nimport \"foundry_test/base/AdvTest.sol\";\n\n\ncontract SequenceDynamicSigImp {\n  function recover(bytes32 _subdigest, bytes calldata _signature) external view returns (uint256, uint256, bytes32, uint256) {\n    return SequenceDynamicSig.recover(_subdigest, _signature);\n  }\n\n  function recoverBase(bytes32 _subdigest, bytes calldata _signature) external view returns (uint256 threshold, uint256 weight, bytes32 imageHash, uint256) {\n    return SequenceBaseSig.recover(_subdigest, _signature);\n  }\n}\n\ncontract SequenceDynamicSigTest is AdvTest {\n  SequenceDynamicSigImp private lib;\n\n  function setUp() public {\n    lib = new SequenceDynamicSigImp();\n  }\n\n  function test_recover_ignoreFirstByte(uint8 _first, bytes32 _subdigest, uint256 _pk, uint16 _threshold, uint32 _checkpoint, uint8 _weight) external {\n    _pk = boundPk(_pk);\n\n    bytes memory encoded = abi.encodePacked(_threshold, _checkpoint, uint8(0), _weight, signAndPack(_pk, _subdigest, 1));\n\n    (uint256 threshold1, uint256 weight1, bytes32 imageHash1, uint256 checkpoint1) = lib.recover(_subdigest, abi.encodePacked(_first, encoded));\n    (uint256 threshold2, uint256 weight2, bytes32 imageHash2, uint256 checkpoint2) = lib.recoverBase(_subdigest, encoded);\n\n    assertEq(threshold1, threshold2);\n    assertEq(weight1, weight2);\n    assertEq(imageHash1, imageHash2);\n    assertEq(checkpoint1, checkpoint2);\n  }\n}\n"
  },
  {
    "path": "foundry_test/modules/commons/submodules/auth/SequenceNoChainIdSig.t.sol",
    "content": "// SPDX-License-Identifier: Apache-2.0\npragma solidity 0.8.18;\n\nimport \"contracts/modules/commons/submodules/auth/SequenceNoChainIdSig.sol\";\n\nimport \"foundry_test/base/AdvTest.sol\";\n\n\ncontract SequenceNoChainIdSigImp {\n  function subdigest(bytes32 _digest) external view returns (bytes32) {\n    return SequenceNoChainIdSig.subdigest(_digest);\n  }\n}\n\ncontract SequenceNoChainIdSigTest is AdvTest {\n  SequenceNoChainIdSigImp private lib;\n\n  function setUp() public {\n    lib = new SequenceNoChainIdSigImp();\n  }\n\n  function test_subdigest_DiffDigest(bytes32 _digest1, bytes32 _digest2) external {\n    bytes32 res1 = lib.subdigest(_digest1);\n    bytes32 res2 = lib.subdigest(_digest2);\n    assertTrue(res1 != res2 || _digest1 == _digest2);\n  }\n\n  function test_subdigest_DiffAddress(bytes32 _digest, address _addr1, address _addr2) external {\n    boundNoContract(boundNoSys(_addr1));\n    boundNoContract(boundNoSys(_addr2));\n\n    vm.etch(_addr1, address(lib).code);\n    vm.etch(_addr2, address(lib).code);\n\n    bytes32 res1 = SequenceNoChainIdSigImp(_addr1).subdigest(_digest);\n    bytes32 res2 = SequenceNoChainIdSigImp(_addr2).subdigest(_digest);\n\n    assertTrue(res1 != res2 || _addr1 == _addr2);\n  }\n\n  function test_subdigest_DiffChainId(bytes32 _digest, uint256 _chainId1, uint256 _chainId2) external {\n    _chainId1 = bound(_chainId1, 0, type(uint64).max);\n    _chainId2 = bound(_chainId2, 0, type(uint64).max);\n\n    vm.chainId(_chainId1);\n    bytes32 res1 = lib.subdigest(_digest);\n    vm.chainId(_chainId2);\n    bytes32 res2 = lib.subdigest(_digest);\n\n    assertTrue(res1 == res2);\n  }\n}\n"
  },
  {
    "path": "foundry_test/modules/commons/submodules/nonce/SubModuleNonce.t.sol",
    "content": "// SPDX-License-Identifier: Apache-2.0\npragma solidity 0.8.18;\n\nimport \"contracts/modules/commons/submodules/nonce/SubModuleNonce.sol\";\n\nimport \"foundry_test/base/AdvTest.sol\";\n\n\ncontract SubModuleNonceTest is AdvTest {\n  function test_decodeNonce(uint160 _space, uint96 _nonce) external {\n    uint256 encoded = abi.decode(abi.encodePacked(_space, _nonce), (uint256));\n    (uint256 space2, uint256 nonce2) = SubModuleNonce.decodeNonce(encoded);\n\n    assertEq(space2, _space);\n    assertEq(nonce2, _nonce);\n  }\n}\n"
  },
  {
    "path": "foundry_test/modules/utils/L2CompressorEncoder.sol",
    "content": "// SPDX-License-Identifier: Apache-2.0\npragma solidity 0.8.18;\n\n\nfunction requiredBytesFor(bytes32 value) pure returns (uint8) {\n  return requiredBytesFor(uint256(value));\n}\n\nfunction requiredBytesFor(uint256 value) pure returns (uint8) {\n  if (value <= type(uint8).max) {\n    return 1;\n  } else if (value <= type(uint16).max) {\n    return 2;\n  } else if (value <= type(uint24).max) {\n    return 3;\n  } else if (value <= type(uint32).max) {\n    return 4;\n  } else if (value <= type(uint40).max) {\n    return 5;\n  } else if (value <= type(uint48).max) {\n    return 6;\n  } else if (value <= type(uint56).max) {\n    return 7;\n  } else if (value <= type(uint64).max) {\n    return 8;\n  } else if (value <= type(uint72).max) {\n    return 9;\n  } else if (value <= type(uint80).max) {\n    return 10;\n  } else if (value <= type(uint88).max) {\n    return 11;\n  } else if (value <= type(uint96).max) {\n    return 12;\n  } else if (value <= type(uint104).max) {\n    return 13;\n  } else if (value <= type(uint112).max) {\n    return 14;\n  } else if (value <= type(uint120).max) {\n    return 15;\n  } else if (value <= type(uint128).max) {\n    return 16;\n  } else if (value <= type(uint136).max) {\n    return 17;\n  } else if (value <= type(uint144).max) {\n    return 18;\n  } else if (value <= type(uint152).max) {\n    return 19;\n  } else if (value <= type(uint160).max) {\n    return 20;\n  } else if (value <= type(uint168).max) {\n    return 21;\n  } else if (value <= type(uint176).max) {\n    return 22;\n  } else if (value <= type(uint184).max) {\n    return 23;\n  } else if (value <= type(uint192).max) {\n    return 24;\n  } else if (value <= type(uint200).max) {\n    return 25;\n  } else if (value <= type(uint208).max) {\n    return 26;\n  } else if (value <= type(uint216).max) {\n    return 27;\n  } else if (value <= type(uint224).max) {\n    return 28;\n  } else if (value <= type(uint232).max) {\n    return 29;\n  } else if (value <= type(uint240).max) {\n    return 30;\n  } else if (value <= type(uint248).max) {\n    return 31;\n  }\n\n  return 32;\n}\n\nfunction packToBytes(bytes32 value, uint256 b) pure returns (bytes memory) {\n  return packToBytes(uint256(value), b);\n}\n\nfunction packToBytes(uint256 value, uint256 b) pure returns (bytes memory) {\n  if (b == 1) {\n    return abi.encodePacked(uint8(value));\n  } else if (b == 2) {\n    return abi.encodePacked(uint16(value));\n  } else if (b == 3) {\n    return abi.encodePacked(uint24(value));\n  } else if (b == 4) {\n    return abi.encodePacked(uint32(value));\n  } else if (b == 5) {\n    return abi.encodePacked(uint40(value));\n  } else if (b == 6) {\n    return abi.encodePacked(uint48(value));\n  } else if (b == 7) {\n    return abi.encodePacked(uint56(value));\n  } else if (b == 8) {\n    return abi.encodePacked(uint64(value));\n  } else if (b == 9) {\n    return abi.encodePacked(uint72(value));\n  } else if (b == 10) {\n    return abi.encodePacked(uint80(value));\n  } else if (b == 11) {\n    return abi.encodePacked(uint88(value));\n  } else if (b == 12) {\n    return abi.encodePacked(uint96(value));\n  } else if (b == 13) {\n    return abi.encodePacked(uint104(value));\n  } else if (b == 14) {\n    return abi.encodePacked(uint112(value));\n  } else if (b == 15) {\n    return abi.encodePacked(uint120(value));\n  } else if (b == 16) {\n    return abi.encodePacked(uint128(value));\n  } else if (b == 17) {\n    return abi.encodePacked(uint136(value));\n  } else if (b == 18) {\n    return abi.encodePacked(uint144(value));\n  } else if (b == 19) {\n    return abi.encodePacked(uint152(value));\n  } else if (b == 20) {\n    return abi.encodePacked(uint160(value));\n  } else if (b == 21) {\n    return abi.encodePacked(uint168(value));\n  } else if (b == 22) {\n    return abi.encodePacked(uint176(value));\n  } else if (b == 23) {\n    return abi.encodePacked(uint184(value));\n  } else if (b == 24) {\n    return abi.encodePacked(uint192(value));\n  } else if (b == 25) {\n    return abi.encodePacked(uint200(value));\n  } else if (b == 26) {\n    return abi.encodePacked(uint208(value));\n  } else if (b == 27) {\n    return abi.encodePacked(uint216(value));\n  } else if (b == 28) {\n    return abi.encodePacked(uint224(value));\n  } else if (b == 29) {\n    return abi.encodePacked(uint232(value));\n  } else if (b == 30) {\n    return abi.encodePacked(uint240(value));\n  } else if (b == 31) {\n    return abi.encodePacked(uint248(value));\n  } else if (b == 32) {\n    return abi.encodePacked(uint256(value));\n  } else {\n    revert(\"Invalid number of bytes\");\n  }\n}\n\nfunction encodeWord(bytes32 _value) pure returns (bytes memory) {\n  return encodeWord(uint256(_value));\n}\n\nfunction encodeWord(uint256 _value) pure returns (bytes memory) {\n  uint256 highestFlag = 0x4f;\n\n  if (_value < type(uint8).max - highestFlag) {\n    return abi.encodePacked(uint8(_value + highestFlag + 1));\n  }\n\n  uint8 b = requiredBytesFor(_value);\n  return abi.encodePacked(b, packToBytes(_value, b));\n}\n\nfunction build_flag(bool _delegateCall, bool _revertOnError, bool _hasGasLimit, bool _hasValue, bool _hasData) pure returns (uint8) {\n  uint8 res = 0;\n\n  if (_delegateCall) {\n    res |= 128;\n  }\n\n  if (_revertOnError) {\n    res |= 64;\n  }\n\n  if (_hasGasLimit) {\n    res |= 32;\n  }\n\n  if (_hasValue) {\n    res |= 16;\n  }\n\n  // Hasdata uses first bit\n  if (_hasData) {\n    res |= 1;\n  }\n\n  return res;\n}\n\nfunction encode_raw_address(address _addr) pure returns (bytes memory) {\n  return encodeWord(uint256(uint160(_addr)));\n}\n\nfunction encode_bytes_n(bytes memory _data) pure returns (bytes memory) {\n  return abi.encodePacked(uint8(0x2b), encodeWord(_data.length), _data);\n}\n\nfunction encode_abi_call(bytes4 _selector) pure returns (bytes memory) {\n  return abi.encodePacked(uint8(0x2d), uint8(0x00), _selector);\n}\n\nfunction encode_abi_call(bytes4 _selector, bytes32 _v1) pure returns (bytes memory) {\n  return abi.encodePacked(uint8(0x2e), uint8(0x00), _selector, encodeWord(_v1));\n}\n\nfunction encode_abi_call(bytes4 _selector, bytes32 _v1, bytes32 _v2) pure returns (bytes memory) {\n  return abi.encodePacked(uint8(0x2f), uint8(0x00), _selector, encodeWord(_v1), encodeWord(_v2));\n}\n\nfunction encode_abi_call(bytes4 _selector, bytes32 _v1, bytes32 _v2, bytes32 _v3) pure returns (bytes memory) {\n    return abi.encodePacked(uint8(0x30), uint8(0x00), _selector, encodeWord(_v1), encodeWord(_v2), encodeWord(_v3));\n}\n\nfunction encode_abi_call(bytes4 _selector, bytes32 _v1, bytes32 _v2, bytes32 _v3, bytes32 _v4) pure returns (bytes memory) {\n    return abi.encodePacked(uint8(0x31), uint8(0x00), _selector, encodeWord(_v1), encodeWord(_v2), encodeWord(_v3), encodeWord(_v4));\n}\n\nfunction encode_abi_call(bytes4 _selector, bytes32 _v1, bytes32 _v2, bytes32 _v3, bytes32 _v4, bytes32 _v5) pure returns (bytes memory) {\n    return abi.encodePacked(uint8(0x32), uint8(0x00), _selector, encodeWord(_v1), encodeWord(_v2), encodeWord(_v3), encodeWord(_v4), encodeWord(_v5));\n}\n\nfunction encode_abi_call(bytes4 _selector, bytes32 _v1, bytes32 _v2, bytes32 _v3, bytes32 _v4, bytes32 _v5, bytes32 _v6) pure returns (bytes memory) {\n    return abi.encodePacked(uint8(0x33), uint8(0x00), _selector, encodeWord(_v1), encodeWord(_v2), encodeWord(_v3), encodeWord(_v4), encodeWord(_v5), encodeWord(_v6));\n}\n\nfunction encode_nested(bytes memory _a, bytes memory _b) pure returns (bytes memory) {\n  return abi.encodePacked(uint8(0x34), uint8(2), _a, _b);\n}\n\nfunction encode_nested(bytes memory _a, bytes memory _b, bytes memory _c) pure returns (bytes memory) {\n  return abi.encodePacked(uint8(0x34), uint8(3), _a, _b, _c);\n}\n\nfunction encode_nested(bytes[] memory _bs) pure returns (bytes memory) {\n  bytes memory res = abi.encodePacked(uint8(0x35), uint16(_bs.length));\n  for (uint256 i = 0; i < _bs.length; i++) {\n    res = abi.encodePacked(res, _bs[i]);\n  }\n  return res;\n}\n\nfunction encode_eoa_signature(uint8 _weight, bytes memory _sig) pure returns (bytes memory) {\n  if (_sig.length != 66) {\n    revert(\"Invalid signature length\");\n  }\n\n  if (_weight != 0 && _weight <= 4) {\n    return abi.encodePacked(uint8(0x36 + _weight), _sig);\n  }\n\n  return abi.encodePacked(uint8(0x36), uint8(_weight), _sig);\n}\n\nfunction encode_address(uint8 _weight, address _addr) pure returns (bytes memory) {\n  if (_weight != 0 && _weight <= 4) {\n    return abi.encodePacked(uint8(0x3b + _weight), encodeWord(uint256(uint160(_addr))));\n  }\n\n  return abi.encodePacked(uint8(0x3b), uint8(_weight), encodeWord(uint256(uint160(_addr))));\n}\n\nfunction encode_node(bytes32 _node) pure returns (bytes memory) {\n  return abi.encodePacked(uint8(0x40), encodeWord(_node));\n}\n\nfunction encode_branch(bytes memory _nested) pure returns (bytes memory) {\n  return abi.encodePacked(uint8(0x41), encode_bytes_n(_nested));\n}\n\nfunction encode_subdigest(bytes32 _subdigest) pure returns (bytes memory) {\n  return abi.encodePacked(uint8(0x42), encodeWord(_subdigest));\n}\n\nfunction encode_nested(uint8 _weight, uint8 _threshold, bytes memory _nested) pure returns (bytes memory) {\n  return abi.encodePacked(uint8(0x43), uint8(_weight), uint8(_threshold), encode_bytes_n(_nested));\n}\n\nfunction encode_dynamic_signature(uint8 _weight, address _signer, bytes memory _signature) pure returns (bytes memory) {\n  return abi.encodePacked(uint8(0x44), uint8(_weight), encodeWord(uint256(uint160(_signer))), encode_bytes_n(_signature));\n}\n\nfunction encode_sequence_signature(bool _noChainId, uint256 _threshold, uint32 _checkpoint, bytes memory _tree) pure returns (bytes memory) {\n  uint8 flag;\n\n  bytes memory t;\n\n  if (_noChainId) {\n    if (_threshold <= type(uint8).max) {\n      flag = 0x45;\n      t = abi.encodePacked(uint8(_threshold));\n    } else {\n      flag = 0x47;\n      t = abi.encodePacked(uint16(_threshold));\n    }\n  } else {\n    if (_threshold <= type(uint8).max) {\n      flag = 0x46;\n      t = abi.encodePacked(uint8(_threshold));\n    } else {\n      flag = 0x48;\n      t = abi.encodePacked(uint16(_threshold));\n    }\n  }\n\n  return abi.encodePacked(flag, t, encodeWord(_checkpoint), encode_bytes_n(_tree));\n}\n\nfunction encode_sequence_chained_signatures(bytes[] memory _payloads) pure returns (bytes memory) {\n  bytes memory encoded;\n\n  if (_payloads.length > type(uint8).max) {\n    encoded = abi.encodePacked(uint8(0x4a), uint16(_payloads.length));\n  } else {\n    encoded = abi.encodePacked(uint8(0x49), uint8(_payloads.length));\n  }\n\n  for (uint256 i = 0; i < _payloads.length; i++) {\n    encoded = abi.encodePacked(encoded, encode_bytes_n(_payloads[i]));\n  }\n\n  return encoded;\n}\n\nfunction encode_abi_dynamic(\n  bytes4 _selector,\n  bool[] memory _isDynamic,\n  bytes[] memory _values\n) pure returns (bytes memory) {\n  bytes memory encoded = abi.encodePacked(uint8(0x4b), uint8(0x00), _selector, uint8(_isDynamic.length));\n  uint8 isDynamicBitmap = 0;\n\n  // The first 8 values can be dynamic, this is marked using a bitmap\n  for (uint256 i = 0; i < 8 && i < _isDynamic.length; i++) {\n    if (_isDynamic[i]) {\n      isDynamicBitmap |= uint8(1 << i);\n    }\n  }\n\n  encoded = abi.encodePacked(encoded, isDynamicBitmap);\n\n  for (uint256 i = 0; i < _values.length; i++) {\n    if (_isDynamic[i]) {\n      encoded = abi.encodePacked(encoded, encode_bytes_n(_values[i]));\n    } else {\n      encoded = abi.encodePacked(encoded, encodeWord(abi.decode(_values[i], (uint256))));\n    }\n  }\n\n  return encoded;\n}\n"
  },
  {
    "path": "foundry_test/modules/utils/L2CompressorHuff.t.sol",
    "content": "// SPDX-License-Identifier: UNLICENSED\npragma solidity ^0.8.13;\n\nimport \"foundry_test/base/AdvTest.sol\";\n\nimport \"forge-std/console.sol\";\nimport \"forge-std/console2.sol\";\n\nimport { HuffConfig } from \"foundry-huff/HuffConfig.sol\";\nimport { HuffDeployer } from \"foundry-huff/HuffDeployer.sol\";\n\nimport \"contracts/modules/commons/interfaces/IModuleCalls.sol\";\n\nimport \"./L2CompressorEncoder.sol\";\n\ncontract L2CompressorHuffTest is AdvTest {\n  address public imp;\n\n  function setUp() public {\n    imp = address(\n      HuffDeployer\n        .config()\n        .with_evm_version(\"paris\")\n        .deploy(\"L2Compressor\")\n    );\n  }\n\n  function test_execute(\n    IModuleCalls.Transaction[] calldata _txs,\n    uint160 _space,\n    uint96 _nonce,\n    bytes calldata _signature\n  ) external {\n    vm.assume(_txs.length != 0 && _txs.length <= type(uint8).max);\n\n    bytes32 packedNonce = abi.decode(abi.encodePacked(_space, _nonce), (bytes32));\n\n    bytes memory encoded = abi.encodePacked(\n      hex\"00\",\n      encodeWord(_space), encodeWord(_nonce),\n      uint8(_txs.length)\n    );\n\n    for (uint256 i = 0; i < _txs.length; i++) {\n      IModuleCalls.Transaction memory t = _txs[i];\n\n      encoded = abi.encodePacked(\n        encoded,\n        build_flag(t.delegateCall, t.revertOnError, t.gasLimit != 0, t.value != 0, t.data.length != 0),\n        t.gasLimit != 0 ? encodeWord(t.gasLimit) : bytes(\"\"),\n        encode_raw_address(t.target),\n        t.value != 0 ? encodeWord(t.value) : bytes(\"\"),\n        t.data.length != 0 ? encode_bytes_n(t.data) : bytes(\"\")\n      );\n    }\n\n    encoded = abi.encodePacked(\n      encoded,\n      encode_bytes_n(_signature),\n      encodeWord(uint256(uint160(address(0xEbD3186Ab4524330A866BE6866bE7bB55A173a23))))\n    );\n\n    vm.expectCall(\n      address(0xEbD3186Ab4524330A866BE6866bE7bB55A173a23),\n      0,\n      abi.encodeWithSelector(\n        IModuleCalls.execute.selector,\n        _txs,\n        packedNonce,\n        _signature\n      )\n    );\n\n    (bool s, bytes memory r) = imp.call(encoded);\n    assertTrue(s);\n    assertEq(r, bytes(\"\"));\n  }\n\n  struct BatchMember {\n    IModuleCalls.Transaction[] txs;\n    uint160 space;\n    uint96 nonce;\n    bytes signature;\n  }\n  \n  function test_execute_many_2(BatchMember memory _batch1, BatchMember memory _batch2) external {\n    BatchMember[] memory batch = new BatchMember[](2);\n    batch[0] = _batch1;\n    batch[1] = _batch2;\n    test_execute_many(batch);\n  }\n\n  function test_execute_many(BatchMember[] memory _batch) internal {\n    vm.assume(_batch.length != 0 && _batch.length <= type(uint8).max);\n\n    uint256 size = mayBoundArr(_batch.length);\n    size = size == 0 ? 1 : size;\n\n    bytes memory encoded = abi.encodePacked(\n      hex\"01\",\n      uint8(size)\n    );\n\n    for (uint256 i = 0; i < size; i++) {\n      vm.assume(_batch[i].txs.length <= type(uint8).max);\n\n      if (_batch[i].txs.length == 0) {\n        _batch[i].txs = new IModuleCalls.Transaction[](1);\n      }\n\n      bytes32 packedNonce = abi.decode(abi.encodePacked(_batch[i].space, _batch[i].nonce), (bytes32));\n\n      encoded = abi.encodePacked(\n        encoded,\n        encodeWord(_batch[i].space), encodeWord(_batch[i].nonce),\n        uint8(_batch[i].txs.length)\n      );\n\n      for (uint256 x = 0; x < _batch[i].txs.length; x++) {\n        IModuleCalls.Transaction memory t = _batch[i].txs[x];\n\n        encoded = abi.encodePacked(\n          encoded,\n          build_flag(t.delegateCall, t.revertOnError, t.gasLimit != 0, t.value != 0, t.data.length != 0),\n          t.gasLimit != 0 ? encodeWord(t.gasLimit) : bytes(\"\"),\n          encode_raw_address(t.target),\n          t.value != 0 ? encodeWord(t.value) : bytes(\"\"),\n          t.data.length != 0 ? encode_bytes_n(t.data) : bytes(\"\")\n        );\n      }\n\n      // Derive a random address from i\n      address addr = address(uint160(uint256(keccak256(abi.encode(i)))));\n\n      encoded = abi.encodePacked(\n        encoded,\n        encode_bytes_n(_batch[i].signature),\n        encodeWord(uint256(uint160(addr)))\n      );\n\n      vm.expectCall(\n        addr,\n        0,\n        abi.encodeWithSelector(\n          IModuleCalls.execute.selector,\n          _batch[i].txs,\n          packedNonce,\n          _batch[i].signature\n        )\n      );\n    }\n\n    (bool s,) = imp.call{ gas: type(uint64).max }(encoded);\n    assertTrue(s);\n  }\n\n  function test_read_addresses() external {\n    vm.store(imp, bytes32(uint256(1)), bytes32(uint256(1000)));\n    vm.store(imp, bytes32(uint256(2)), bytes32(uint256(2000)));\n\n    (bool s, bytes memory r) = imp.call(abi.encodePacked(hex\"02\", uint256(0)));\n    assertTrue(s);\n  \n    assertEq(r, abi.encode(bytes32(uint256(1000))));\n\n    (s, r) = imp.call(abi.encodePacked(hex\"02\", uint256(1)));\n    assertTrue(s);\n\n    assertEq(r, abi.encode(bytes32(uint256(2000))));\n  }\n\n  function test_read_bytes32() external {\n    vm.store(imp, bytes32(uint256(0)) << 128, bytes32(uint256(1000)));\n    vm.store(imp, bytes32(uint256(1)) << 128, bytes32(uint256(2000)));\n\n    (bool s, bytes memory r) = imp.call(abi.encodePacked(hex\"03\", uint256(0)));\n    assertTrue(s);\n  \n    assertEq(r, abi.encode(bytes32(uint256(1000))));\n\n    (s, r) = imp.call(abi.encodePacked(hex\"03\", uint256(1)));\n    assertTrue(s);\n\n    assertEq(r, abi.encode(bytes32(uint256(2000))));\n  }\n\n  function test_read_storage_slots() external {\n    vm.store(imp, bytes32(uint256(0)) << 128, bytes32(uint256(1000)));\n    vm.store(imp, bytes32(uint256(1)) << 128, bytes32(uint256(2000)));\n    vm.store(imp, bytes32(uint256(1)), bytes32(uint256(4000)));\n    vm.store(imp, bytes32(uint256(2)), bytes32(uint256(5000)));\n\n    (bool s, bytes memory r) = imp.call(abi.encodePacked(\n      hex\"05\",\n      uint256(0) << 128,\n      uint256(1),\n      uint256(1) << 128,\n      uint256(2)\n    ));\n\n    assertTrue(s);\n  \n    assertEq(r, abi.encode(uint256(1000), uint256(4000), uint256(2000), uint256(5000)));\n  }\n\n  function test_read_size() external {\n    bytes32 word = keccak256(abi.encode(uint256(0)));\n    vm.store(imp, bytes32(0), word);\n\n    (bool s, bytes memory r) = imp.call(abi.encodePacked(hex\"04\"));\n\n    assertTrue(s);\n    assertEq(r, abi.encode(word));\n  }\n\n  function test_decode_execute(\n    IModuleCalls.Transaction[] calldata _txs,\n    uint160 _space,\n    uint96 _nonce,\n    bytes calldata _signature\n  ) external {\n    vm.assume(_txs.length != 0 && _txs.length <= type(uint8).max);\n\n    bytes32 packedNonce = abi.decode(abi.encodePacked(_space, _nonce), (bytes32));\n\n    bytes memory encoded = abi.encodePacked(\n      hex\"06\",\n      encodeWord(_space), encodeWord(_nonce),\n      uint8(_txs.length)\n    );\n\n    for (uint256 i = 0; i < _txs.length; i++) {\n      IModuleCalls.Transaction memory t = _txs[i];\n\n      encoded = abi.encodePacked(\n        encoded,\n        build_flag(t.delegateCall, t.revertOnError, t.gasLimit != 0, t.value != 0, t.data.length != 0),\n        t.gasLimit != 0 ? encodeWord(t.gasLimit) : bytes(\"\"),\n        encode_raw_address(t.target),\n        t.value != 0 ? encodeWord(t.value) : bytes(\"\"),\n        t.data.length != 0 ? encode_bytes_n(t.data) : bytes(\"\")\n      );\n    }\n\n    encoded = abi.encodePacked(\n      encoded,\n      encode_bytes_n(_signature),\n      encodeWord(uint256(uint160(address(0xEbD3186Ab4524330A866BE6866bE7bB55A173a23))))\n    );\n\n    (bool s, bytes memory r) = imp.call(encoded);\n    assertTrue(s);\n    assertEq(r, abi.encodePacked(abi.encodeWithSelector(\n      IModuleCalls.execute.selector,\n      _txs,\n      packedNonce,\n      _signature\n    ), uint256(uint160(address(0xEbD3186Ab4524330A866BE6866bE7bB55A173a23)))));\n  }\n\n  function test_decode_execute_many_2(BatchMember memory _batch1, BatchMember memory _batch2) external {\n    BatchMember[] memory batch = new BatchMember[](2);\n    batch[0] = _batch1;\n    batch[1] = _batch2;\n    test_decode_execute_many(batch);\n  }\n\n  function test_decode_execute_many(BatchMember[] memory _batch) internal {\n    vm.assume(_batch.length != 0 && _batch.length <= type(uint8).max);\n\n    uint256 size = mayBoundArr(_batch.length);\n    size = size == 0 ? 1 : size;\n\n    bytes memory encoded = abi.encodePacked(\n      hex\"07\",\n      uint8(size)\n    );\n\n    bytes memory expected;\n\n    for (uint256 i = 0; i < size; i++) {\n      vm.assume(_batch[i].txs.length <= type(uint8).max);\n\n      if (_batch[i].txs.length == 0) {\n        _batch[i].txs = new IModuleCalls.Transaction[](1);\n      }\n\n      bytes32 packedNonce = abi.decode(abi.encodePacked(_batch[i].space, _batch[i].nonce), (bytes32));\n\n      encoded = abi.encodePacked(\n        encoded,\n        encodeWord(_batch[i].space), encodeWord(_batch[i].nonce),\n        uint8(_batch[i].txs.length)\n      );\n\n      for (uint256 x = 0; x < _batch[i].txs.length; x++) {\n        IModuleCalls.Transaction memory t = _batch[i].txs[x];\n\n        encoded = abi.encodePacked(\n          encoded,\n          build_flag(t.delegateCall, t.revertOnError, t.gasLimit != 0, t.value != 0, t.data.length != 0),\n          t.gasLimit != 0 ? encodeWord(t.gasLimit) : bytes(\"\"),\n          encode_raw_address(t.target),\n          t.value != 0 ? encodeWord(t.value) : bytes(\"\"),\n          t.data.length != 0 ? encode_bytes_n(t.data) : bytes(\"\")\n        );\n      }\n\n      // Derive a random address from i\n      address addr = address(uint160(uint256(keccak256(abi.encode(i)))));\n\n      encoded = abi.encodePacked(\n        encoded,\n        encode_bytes_n(_batch[i].signature),\n        encodeWord(uint256(uint160(addr)))\n      );\n\n      expected = abi.encodePacked(\n        expected,\n        abi.encodeWithSelector(\n          IModuleCalls.execute.selector,\n          _batch[i].txs,\n          packedNonce,\n          _batch[i].signature\n        ),\n        uint256(uint160(addr))\n      );\n    }\n\n    (bool s, bytes memory r) = imp.call{ gas: type(uint64).max }(encoded);\n    assertTrue(s);\n    assertEq(r, expected);\n  }\n}\n"
  },
  {
    "path": "foundry_test/modules/utils/L2CompressorHuffReadExecute.sol",
    "content": "// SPDX-License-Identifier: UNLICENSED\npragma solidity ^0.8.13;\n\nimport \"foundry_test/base/AdvTest.sol\";\n\nimport \"forge-std/console.sol\";\nimport \"forge-std/console2.sol\";\n\nimport { HuffConfig } from \"foundry-huff/HuffConfig.sol\";\nimport { HuffDeployer } from \"foundry-huff/HuffDeployer.sol\";\n\nimport \"contracts/modules/commons/interfaces/IModuleCalls.sol\";\n\nimport \"./L2CompressorEncoder.sol\";\n\nuint256 constant FMS = 0xa0;\n\ncontract L2CompressorHuffReadExecuteTest is AdvTest {\n  address public imp;\n\n  function setUp() public {\n    imp = address(\n      HuffDeployer\n        .config()\n        .with_evm_version(\"paris\")\n        .deploy(\"imps/L2CompressorReadExecute\")\n    );\n  }\n\n  function test_read_simple_execute(\n    IModuleCalls.Transaction[] calldata _txs,\n    uint160 _space,\n    uint96 _nonce,\n    bytes calldata _signature\n  ) external {\n    vm.assume(_txs.length != 0 && _txs.length <= type(uint8).max);\n\n    bytes32 packedNonce = abi.decode(abi.encodePacked(_space, _nonce), (bytes32));\n\n    bytes memory encoded = abi.encodePacked(\n      encodeWord(_space), encodeWord(_nonce),\n      uint8(_txs.length)\n    );\n\n    for (uint256 i = 0; i < _txs.length; i++) {\n      IModuleCalls.Transaction memory t = _txs[i];\n\n      encoded = abi.encodePacked(\n        encoded,\n        build_flag(t.delegateCall, t.revertOnError, t.gasLimit != 0, t.value != 0, t.data.length != 0),\n        t.gasLimit != 0 ? encodeWord(t.gasLimit) : bytes(\"\"),\n        encode_raw_address(t.target),\n        t.value != 0 ? encodeWord(t.value) : bytes(\"\"),\n        t.data.length != 0 ? encode_bytes_n(t.data) : bytes(\"\")\n      );\n    }\n\n    encoded = abi.encodePacked(\n      encoded,\n      encode_bytes_n(_signature)\n    );\n\n    (bool s, bytes memory r) = imp.staticcall(encoded);\n\n    assertTrue(s);\n    (uint256 rindex, uint256 windex, bytes memory res) = abi.decode(r, (uint256, uint256, bytes));\n\n    assertEq(rindex, encoded.length);\n    assertEq(windex, res.length + FMS);\n\n    // Encode using solidity\n    bytes memory solidityEncoded = abi.encodeWithSelector(IModuleCalls.execute.selector, _txs, packedNonce, _signature);\n    assertEq(solidityEncoded, res);\n  }\n\n  function test_read_nested_execute(\n    address _wallet,\n    IModuleCalls.Transaction[] calldata _txs,\n    uint160 _space,\n    uint96 _nonce,\n    bytes calldata _signature\n  ) external {\n    vm.assume(_txs.length != 0 && _txs.length <= type(uint8).max);\n    bytes32 packedNonce = abi.decode(abi.encodePacked(_space, _nonce), (bytes32));\n\n    IModuleCalls.Transaction[] memory outerTx = new IModuleCalls.Transaction[](1);\n    outerTx[0] = IModuleCalls.Transaction({\n      delegateCall: false,\n      revertOnError: false,\n      gasLimit: 0,\n      target: _wallet,\n      value: 0,\n      data: abi.encodeWithSelector(IModuleCalls.execute.selector, _txs, packedNonce, _signature)\n    });\n\n    bytes memory outerSignature = hex\"112233\";\n    bytes32 outerNonce = bytes32(0);\n\n    bytes memory encoded = abi.encodePacked(\n      encodeWord(bytes32(0)), encodeWord(bytes32(0)),\n      uint8(1),  // One transaction\n      build_flag(outerTx[0].delegateCall, outerTx[0].revertOnError, outerTx[0].gasLimit != 0, outerTx[0].value != 0, outerTx[0].data.length != 0),\n      bytes(\"\"),\n      encode_raw_address(outerTx[0].target),\n      bytes(\"\")\n    );\n\n    // Encode the inner transaction\n    encoded = abi.encodePacked(\n      encoded,\n      uint8(0x26), // Read EXECUTE flag\n      encodeWord(_space), encodeWord(_nonce),\n      uint8(_txs.length)\n    );\n\n    for (uint256 i = 0; i < _txs.length; i++) {\n      IModuleCalls.Transaction memory t = _txs[i];\n\n      encoded = abi.encodePacked(\n        encoded,\n        build_flag(t.delegateCall, t.revertOnError, t.gasLimit != 0, t.value != 0, t.data.length != 0),\n        t.gasLimit != 0 ? encodeWord(t.gasLimit) : bytes(\"\"),\n        encode_raw_address(t.target),\n        t.value != 0 ? encodeWord(t.value) : bytes(\"\"),\n        t.data.length != 0 ? encode_bytes_n(t.data) : bytes(\"\")\n      );\n    }\n\n    encoded = abi.encodePacked(\n      encoded,\n      encode_bytes_n(_signature)\n    );\n\n    // Encode the outer signature\n    encoded = abi.encodePacked(\n      encoded,\n      encode_bytes_n(outerSignature)\n    );\n\n    (bool s, bytes memory r) = imp.staticcall(encoded);\n    assertTrue(s);\n    (uint256 rindex, uint256 windex, bytes memory res) = abi.decode(r, (uint256, uint256, bytes));\n    assertEq(rindex, encoded.length);\n    assertEq(windex, res.length + FMS);\n\n    // Encode using solidity\n    bytes memory solidityEncoded = abi.encodeWithSelector(IModuleCalls.execute.selector, outerTx, outerNonce, outerSignature);\n    assertEq(solidityEncoded, res);\n  }\n}\n"
  },
  {
    "path": "foundry_test/modules/utils/L2CompressorHuffReadFlag.t.sol",
    "content": "// SPDX-License-Identifier: UNLICENSED\npragma solidity ^0.8.13;\n\nimport \"foundry_test/base/AdvTest.sol\";\n\nimport \"forge-std/console.sol\";\nimport \"forge-std/console2.sol\";\n\nimport { HuffConfig } from \"foundry-huff/HuffConfig.sol\";\nimport { HuffDeployer } from \"foundry-huff/HuffDeployer.sol\";\n\nimport \"contracts/modules/commons/interfaces/IModuleCalls.sol\";\n\nimport \"./L2CompressorEncoder.sol\";\n\nuint256 constant FMS = 0xa0;\n\ncontract L2CompressorHuffReadFlagTests is AdvTest {\n  address public imp;\n\n  function setUp() public {\n    imp = address(\n      HuffDeployer\n        .config()\n        .with_evm_version(\"paris\")\n        .deploy(\"imps/L2CompressorReadFlag\")\n    );\n  }\n\n  function test_read_flag_bytes32_one(uint8 _val) external {\n    (bool s, bytes memory r) = imp.staticcall(\n      abi.encodePacked(hex\"01\", _val)\n    );\n\n    assertTrue(s);\n    (uint256 rindex, uint256 windex, bytes memory res) = abi.decode(r, (uint256, uint256, bytes));\n    assertEq(rindex, 2);\n    assertEq(windex, FMS + 32);\n\n    assertEq(abi.encode(uint256(_val)), res);\n  }\n\n  function test_read_flag_bytes32_two(uint16 _val) external {\n    (bool s, bytes memory r) = imp.staticcall(\n      abi.encodePacked(hex\"02\", _val)\n    );\n\n    assertTrue(s);\n    (uint256 rindex, uint256 windex, bytes memory res) = abi.decode(r, (uint256, uint256, bytes));\n    assertEq(rindex, 3);\n    assertEq(windex, FMS + 32);\n\n    assertEq(abi.encode(uint256(_val)), res);\n  }\n\n  function test_read_flag_bytes32_x(uint256 _size, bytes memory _content) public {\n    _size = bound(_size, 1, 32);\n  \n    (bool s, bytes memory r) = imp.staticcall(\n      abi.encodePacked(uint8(_size), _content)\n    );\n\n    assertTrue(s);\n    (uint256 rindex, uint256 windex, bytes memory res) = abi.decode(r, (uint256, uint256, bytes));\n    assertEq(rindex, 1 + _size);\n    assertEq(windex, FMS + 32);\n\n    _content = abi.encodePacked(_content, bytes32(0));\n    uint256 expected;\n    assembly {\n      expected := mload(add(_content, 0x20))\n      expected := shr(sub(256, mul(_size, 8)), expected)\n    }\n\n    assertEq(abi.encode(uint256(expected)), res);\n  }\n\n  function test_read_flag_save_and_read_address(address _addr2) external {\n    address _addr = address(this);\n    (bool s, bytes memory r) = imp.call(\n      abi.encodePacked(hex\"21\", _addr)\n    );\n\n    assertTrue(s);\n    (uint256 rindex, uint256 windex, bytes memory res) = abi.decode(r, (uint256, uint256, bytes));\n\n    assertEq(rindex, 1 + 20);\n    assertEq(windex, FMS + 32);\n\n    assertEq(abi.encode(_addr), res);\n\n    // Read the address at index 1\n    (s, r) = imp.staticcall(\n      abi.encodePacked(hex\"23\", uint16(1))\n    );\n\n    assertTrue(s);\n    (rindex, windex, res) = abi.decode(r, (uint256, uint256, bytes));\n    assertEq(rindex, 3);\n    assertEq(windex, FMS + 32);\n\n    assertEq(abi.encode(_addr), res);\n\n    // Save a second address\n    (s, r) = imp.call(\n      abi.encodePacked(hex\"21\", _addr2)\n    );\n\n    assertTrue(s);\n    (rindex, windex, res) = abi.decode(r, (uint256, uint256, bytes));\n\n    assertEq(rindex, 1 + 20);\n    assertEq(windex, FMS + 32);\n\n    assertEq(abi.encode(_addr2), res);\n\n    // Read second address using 3 bytes, 4 bytes and 5 bytes\n    (s, r) = imp.staticcall(\n      abi.encodePacked(hex\"24\", uint24(2))\n    );\n\n    assertTrue(s);\n    (rindex, windex, res) = abi.decode(r, (uint256, uint256, bytes));\n    assertEq(rindex, 4);\n    assertEq(windex, FMS + 32);\n\n    assertEq(abi.encode(_addr2), res);\n\n    (s, r) = imp.staticcall(\n      abi.encodePacked(hex\"25\", uint32(2))\n    );\n\n    assertTrue(s);\n    (rindex, windex, res) = abi.decode(r, (uint256, uint256, bytes));\n    assertEq(rindex, 5);\n    assertEq(windex, FMS + 32);\n\n    assertEq(abi.encode(_addr2), res);\n\n    (s, r) = imp.staticcall(\n      abi.encodePacked(hex\"25\", uint32(2))\n    );\n\n    assertTrue(s);\n    (rindex, windex, res) = abi.decode(r, (uint256, uint256, bytes));\n    assertEq(rindex, 5);\n    assertEq(windex, FMS + 32);\n\n    assertEq(abi.encode(_addr2), res);\n  }\n\n  function test_read_flag_save_and_read_bytes32(bytes32 _b1, bytes32 _b2) external {\n    (bool s, bytes memory r) = imp.call(\n      abi.encodePacked(hex\"22\", _b1)\n    );\n\n    assertTrue(s);\n    (uint256 rindex, uint256 windex, bytes memory res) = abi.decode(r, (uint256, uint256, bytes));\n\n    assertEq(rindex, 1 + 32);\n    assertEq(windex, FMS + 32);\n\n    assertEq(abi.encode(_b1), res);\n\n    // Read the address at index 1\n    (s, r) = imp.staticcall(\n      abi.encodePacked(hex\"27\", uint16(1))\n    );\n\n    assertTrue(s);\n    (rindex, windex, res) = abi.decode(r, (uint256, uint256, bytes));\n    assertEq(rindex, 3);\n    assertEq(windex, FMS + 32);\n\n    assertEq(abi.encode(_b1), res);\n\n    // Save a second address\n    (s, r) = imp.call(\n      abi.encodePacked(hex\"22\", _b2)\n    );\n\n    assertTrue(s);\n    (rindex, windex, res) = abi.decode(r, (uint256, uint256, bytes));\n\n    assertEq(rindex, 1 + 32);\n    assertEq(windex, FMS + 32);\n\n    assertEq(abi.encode(_b2), res);\n\n    // Read second address using 3 bytes, 4 bytes and 5 bytes\n    (s, r) = imp.staticcall(\n      abi.encodePacked(hex\"28\", uint24(2))\n    );\n\n    assertTrue(s);\n    (rindex, windex, res) = abi.decode(r, (uint256, uint256, bytes));\n    assertEq(rindex, 4);\n    assertEq(windex, FMS + 32);\n\n    assertEq(abi.encode(_b2), res);\n\n    (s, r) = imp.staticcall(\n      abi.encodePacked(hex\"29\", uint32(2))\n    );\n\n    assertTrue(s);\n    (rindex, windex, res) = abi.decode(r, (uint256, uint256, bytes));\n    assertEq(rindex, 5);\n    assertEq(windex, FMS + 32);\n\n    assertEq(abi.encode(_b2), res);\n\n    (s, r) = imp.staticcall(\n      abi.encodePacked(hex\"29\", uint32(2))\n    );\n\n    assertTrue(s);\n    (rindex, windex, res) = abi.decode(r, (uint256, uint256, bytes));\n    assertEq(rindex, 5);\n    assertEq(windex, FMS + 32);\n\n    assertEq(abi.encode(_b2), res);\n  }\n\n  function test_read_flag_bytes_n(bytes calldata _data, bytes calldata _extra) external {\n    (bool s, bytes memory r) = imp.staticcall(\n      abi.encodePacked(hex\"2b\", hex\"04\", uint32(_data.length), _data, _extra)\n    );\n\n    assertTrue(s);\n    (uint256 rindex, uint256 windex, bytes memory res) = abi.decode(r, (uint256, uint256, bytes));\n\n    assertEq(rindex, _data.length + 1 + 1 + 4);\n    assertEq(windex, FMS + _data.length);\n    assertEq(res, _data);\n  }\n\n  function test_read_flag_abi_encode_0(bytes4 _selector) external {\n    bytes memory encoded = encode_abi_call(_selector);\n    (bool s, bytes memory r) = imp.staticcall(encoded);\n\n    assertTrue(s);\n\n    (uint256 rindex, uint256 windex, bytes memory res) = abi.decode(r, (uint256, uint256, bytes));\n\n    assertEq(rindex, encoded.length);\n    assertEq(windex, FMS + res.length);\n    assertEq(abi.encodePacked(_selector), res);\n  }\n\n  function test_read_flag_abi_encode_1(bytes4 _selector, bytes32 _v1) external {\n    bytes memory encoded = encode_abi_call(_selector, _v1);\n    (bool s, bytes memory r) = imp.staticcall(encoded);\n\n    assertTrue(s);\n\n    (uint256 rindex, uint256 windex, bytes memory res) = abi.decode(r, (uint256, uint256, bytes));\n\n    assertEq(rindex, encoded.length);\n    assertEq(windex, FMS + res.length);\n    assertEq(abi.encodePacked(_selector, _v1), res);\n  }\n\n  function test_read_flag_abi_encode_2(bytes4 _selector, bytes32 _v1, bytes32 _v2) external {\n    bytes memory encoded = encode_abi_call(_selector, _v1, _v2);\n    (bool s, bytes memory r) = imp.staticcall(encoded);\n\n    assertTrue(s);\n\n    (uint256 rindex, uint256 windex, bytes memory res) = abi.decode(r, (uint256, uint256, bytes));\n\n    assertEq(rindex, encoded.length);\n    assertEq(windex, FMS + res.length);\n    assertEq(abi.encodePacked(_selector, _v1, _v2), res);\n  }\n\n  function test_read_flag_abi_encode_3(bytes4 _selector, bytes32 _v1, bytes32 _v2, bytes32 _v3) external {\n    bytes memory encoded = encode_abi_call(_selector, _v1, _v2, _v3);\n    (bool s, bytes memory r) = imp.staticcall(encoded);\n\n    assertTrue(s);\n\n    (uint256 rindex, uint256 windex, bytes memory res) = abi.decode(r, (uint256, uint256, bytes));\n\n    assertEq(rindex, encoded.length);\n    assertEq(windex, FMS + res.length);\n    assertEq(abi.encodePacked(_selector, _v1, _v2, _v3), res);\n  }\n\n  function test_read_flag_abi_encode_4(\n    bytes4 _selector,\n    bytes32 _v1,\n    bytes32 _v2,\n    bytes32 _v3,\n    bytes32 _v4\n  ) external {\n    bytes memory encoded = encode_abi_call(_selector, _v1, _v2, _v3, _v4);\n    (bool s, bytes memory r) = imp.staticcall(encoded);\n\n    assertTrue(s);\n\n    (uint256 rindex, uint256 windex, bytes memory res) = abi.decode(r, (uint256, uint256, bytes));\n\n    assertEq(rindex, encoded.length);\n    assertEq(windex, FMS + res.length);\n    assertEq(abi.encodePacked(_selector, _v1, _v2, _v3, _v4), res);\n  }\n\n  function test_read_flag_abi_encode_5(\n    bytes4 _selector,\n    bytes32 _v1,\n    bytes32 _v2,\n    bytes32 _v3,\n    bytes32 _v4,\n    bytes32 _v5\n  ) external {\n    bytes memory encoded = encode_abi_call(_selector, _v1, _v2, _v3, _v4, _v5);\n    (bool s, bytes memory r) = imp.staticcall(encoded);\n\n    assertTrue(s);\n\n    (uint256 rindex, uint256 windex, bytes memory res) = abi.decode(r, (uint256, uint256, bytes));\n\n    assertEq(rindex, encoded.length);\n    assertEq(windex, FMS + res.length);\n    assertEq(abi.encodePacked(_selector, _v1, _v2, _v3, _v4, _v5), res);\n  }\n\n  function test_read_flag_abi_encode_5(\n    bytes4 _selector,\n    bytes32 _v1,\n    bytes32 _v2,\n    bytes32 _v3,\n    bytes32 _v4,\n    bytes32 _v5,\n    bytes32 _v6\n  ) external {\n    bytes memory encoded = encode_abi_call(_selector, _v1, _v2, _v3, _v4, _v5, _v6);\n    (bool s, bytes memory r) = imp.staticcall(encoded);\n\n    assertTrue(s);\n\n    (uint256 rindex, uint256 windex, bytes memory res) = abi.decode(r, (uint256, uint256, bytes));\n\n    assertEq(rindex, encoded.length);\n    assertEq(windex, FMS + res.length);\n    assertEq(abi.encodePacked(_selector, _v1, _v2, _v3, _v4, _v5, _v6), res);\n  }\n\n  function test_read_nested(bytes memory _dynamic, uint256 _val1, uint256 _val2) external {\n    bytes memory encoded = encode_nested(\n      encode_bytes_n(_dynamic),\n      encode_nested(\n        encodeWord(_val1),\n        encodeWord(_val2)\n      )\n    );\n\n    (bool s, bytes memory r) = imp.staticcall(encoded);\n    assertEq(s, true);\n\n    (uint256 rindex, uint256 windex, bytes memory res) = abi.decode(r, (uint256, uint256, bytes));\n\n    assertEq(rindex, encoded.length);\n    assertEq(windex, FMS + res.length);\n    assertEq(abi.encodePacked(_dynamic, _val1, _val2), res);\n  }\n\n  function test_read_encode_nested_long() external {\n    bytes[] memory vals = new bytes[](2000);\n\n    for (uint256 i = 0; i < vals.length; i++) {\n      vals[i] = encodeWord(i * 2);\n    }\n\n    bytes memory encoded = encode_nested(vals);\n\n    (bool s, bytes memory r) = imp.staticcall(encoded);\n    assertEq(s, true);\n\n    bytes memory expected;\n    for (uint256 i = 0; i < vals.length; i++) {\n      expected = abi.encodePacked(expected, uint256(i * 2));\n    }\n\n    (uint256 rindex, uint256 windex, bytes memory res) = abi.decode(r, (uint256, uint256, bytes));\n\n    assertEq(rindex, encoded.length);\n    assertEq(windex, FMS + res.length);\n    assertEq(expected, res);\n  }\n\n  function test_read_signature(uint8 _weight, bytes1[66] memory _sig) external {\n    bytes memory _sig2 = new bytes(66);\n\n    for (uint256 i = 0; i < _sig.length; i++) {\n      _sig2[i] = _sig[i];\n    }\n\n    bytes memory encoded = encode_eoa_signature(_weight, _sig2);\n\n    (bool s, bytes memory r) = imp.staticcall(encoded);\n\n    assertTrue(s);\n\n    (uint256 rindex, uint256 windex, bytes memory res) = abi.decode(r, (uint256, uint256, bytes));\n\n    assertEq(rindex, encoded.length);\n    assertEq(windex, FMS + res.length);\n\n    assertEq(res, abi.encodePacked(uint8(0), _weight, _sig2));\n  }\n\n  function test_read_address(uint8 _weight, address _addr) external {\n    bytes memory encoded = encode_address(_weight, _addr);\n\n    (bool s, bytes memory r) = imp.staticcall(encoded);\n\n    assertTrue(s);\n\n    (uint256 rindex, uint256 windex, bytes memory res) = abi.decode(r, (uint256, uint256, bytes));\n\n    assertEq(rindex, encoded.length);\n    assertEq(windex, FMS + res.length);\n\n    assertEq(res, abi.encodePacked(uint8(1), _weight, _addr));\n  }\n\n  function test_read_node(bytes32 _node) external {\n    bytes memory encoded = encode_node(_node);\n\n    (bool s, bytes memory r) = imp.staticcall(encoded);\n\n    assertTrue(s);\n\n    (uint256 rindex, uint256 windex, bytes memory res) = abi.decode(r, (uint256, uint256, bytes));\n\n    assertEq(rindex, encoded.length);\n    assertEq(windex, FMS + res.length);\n\n    assertEq(res, abi.encodePacked(uint8(3), _node));\n  }\n\n  function test_read_subdigest(bytes32 _subdigest) external {\n    bytes memory encoded = encode_subdigest(_subdigest);\n\n    (bool s, bytes memory r) = imp.staticcall(encoded);\n\n    assertTrue(s);\n\n    (uint256 rindex, uint256 windex, bytes memory res) = abi.decode(r, (uint256, uint256, bytes));\n\n    assertEq(rindex, encoded.length);\n    assertEq(windex, FMS + res.length);\n\n    assertEq(res, abi.encodePacked(uint8(5), _subdigest));\n  }\n\n  function test_read_branch(bytes memory _data) external {\n    vm.assume(_data.length <= type(uint24).max);\n\n    bytes memory encoded = encode_branch(_data);\n\n    (bool s, bytes memory r) = imp.staticcall(encoded);\n\n    assertTrue(s);\n\n    (uint256 rindex, uint256 windex, bytes memory res) = abi.decode(r, (uint256, uint256, bytes));\n\n    assertEq(rindex, encoded.length);\n    assertEq(windex, FMS + res.length);\n\n    assertEq(res, abi.encodePacked(uint8(4), uint24(_data.length), _data));\n  }\n\n  function test_read_nested(uint8 _weight, uint8 _threshold, bytes memory _data) external {\n    vm.assume(_data.length <= type(uint24).max);\n\n    bytes memory encoded = encode_nested(_weight, _threshold, _data);\n\n    (bool s, bytes memory r) = imp.staticcall(encoded);\n\n    assertTrue(s);\n\n    (uint256 rindex, uint256 windex, bytes memory res) = abi.decode(r, (uint256, uint256, bytes));\n\n    assertEq(rindex, encoded.length);\n    assertEq(windex, FMS + res.length);\n\n    assertEq(res, abi.encodePacked(uint8(6), uint8(_weight), uint16(_threshold), uint24(_data.length), _data));\n  }\n\n  function test_read_dynamic_signature(uint8 _weight, address _signer, bytes memory _signature) external {\n    vm.assume(_signature.length <= type(uint24).max - 1);\n\n    bytes memory encoded = encode_dynamic_signature(_weight, _signer, _signature);\n\n    (bool s, bytes memory r) = imp.staticcall(encoded);\n\n    assertTrue(s);\n\n    (uint256 rindex, uint256 windex, bytes memory res) = abi.decode(r, (uint256, uint256, bytes));\n\n    assertEq(rindex, encoded.length);\n    assertEq(windex, FMS + res.length);\n\n    assertEq(res, abi.encodePacked(uint8(2), uint8(_weight), _signer, uint24(_signature.length + 1), _signature, uint8(3)));\n  }\n\n  function test_read_sequence_signature(bool _noChainId, uint16 _threshold, uint32 _checkpoint, bytes memory _tree) external {\n    bytes memory encoded = encode_sequence_signature(_noChainId, _threshold, _checkpoint, _tree);\n\n    (bool s, bytes memory r) = imp.staticcall(encoded);\n\n    assertTrue(s);\n\n    (uint256 rindex, uint256 windex, bytes memory res) = abi.decode(r, (uint256, uint256, bytes));\n\n    assertEq(rindex, encoded.length);\n    assertEq(windex, FMS + res.length);\n\n    assertEq(res, abi.encodePacked(uint8(_noChainId ? 0x02 : 0x01), uint16(_threshold), uint32(_checkpoint), _tree));\n  }\n\n  function test_read_sequence_chained_signatures(bytes[] memory _signatures) external {\n    vm.assume(_signatures.length != 0);\n    for (uint256 i = 0; i < _signatures.length; i++) {\n      vm.assume(_signatures[i].length <= type(uint24).max);\n    }\n\n    bytes memory encoded = encode_sequence_chained_signatures(_signatures);\n\n    (bool s, bytes memory r) = imp.staticcall(encoded);\n    assertTrue(s);\n\n    (uint256 rindex, uint256 windex, bytes memory res) = abi.decode(r, (uint256, uint256, bytes));\n\n    assertEq(windex, FMS + res.length);\n    assertEq(rindex, encoded.length);\n\n    bytes memory expected = abi.encodePacked(uint8(0x03));\n\n    for (uint256 i = 0; i < _signatures.length; i++) {\n      expected = abi.encodePacked(expected, uint24(_signatures[i].length), _signatures[i]);\n    }\n\n    assertEq(res, expected);\n  }\n\n  function test_read_abi_dynamic_no_dynamic(\n    bytes4 _selector,\n    bytes32[] calldata _values\n  ) external {\n    vm.assume(_values.length <= type(uint8).max && _values.length != 0);\n    bool[] memory isDynamic = new bool[](_values.length);\n    bytes[] memory values = new bytes[](_values.length);\n    for (uint256 i = 0; i < _values.length; i++) {\n      values[i] = abi.encodePacked(_values[i]);\n    }\n\n    bytes memory encoded = encode_abi_dynamic(_selector, isDynamic, values);\n\n    (bool s, bytes memory r) = imp.staticcall(encoded);\n    assertTrue(s);\n\n    (uint256 rindex, uint256 windex, bytes memory res) = abi.decode(r, (uint256, uint256, bytes));\n\n    assertEq(windex, FMS + res.length);\n    assertEq(rindex, encoded.length);\n\n    assertEq(res, abi.encodePacked(_selector, _values));\n  }\n\n  function test_read_abi_dynamic_only_1(\n    bytes4 _selector,\n    bytes memory _data1\n  ) external {\n    bool[] memory isDynamic = new bool[](1);\n    bytes[] memory _values = new bytes[](1);\n\n    isDynamic[0] = true;\n\n    _values[0] = _data1;\n\n    bytes memory encoded = encode_abi_dynamic(_selector, isDynamic, _values);\n\n    (bool s, bytes memory r) = imp.staticcall(encoded);\n\n    assertTrue(s);\n\n    (uint256 rindex, uint256 windex, bytes memory res) = abi.decode(r, (uint256, uint256, bytes));\n\n    assertEq(windex, FMS + res.length);\n    assertEq(rindex, encoded.length);\n\n    assertEq(res, abi.encodeWithSelector(_selector, _values[0]));\n  }\n\n  function test_read_abi_dynamic_only_2(\n    bytes4 _selector,\n    bytes memory _data1,\n    bytes memory _data2\n  ) external {\n    bool[] memory isDynamic = new bool[](2);\n    bytes[] memory _values = new bytes[](2);\n\n    isDynamic[0] = true;\n    isDynamic[1] = true;\n\n    _values[0] = _data1;\n    _values[1] = _data2;\n\n    bytes memory encoded = encode_abi_dynamic(_selector, isDynamic, _values);\n\n    (bool s, bytes memory r) = imp.staticcall(encoded);\n\n    assertTrue(s);\n\n    (uint256 rindex, uint256 windex, bytes memory res) = abi.decode(r, (uint256, uint256, bytes));\n\n    assertEq(windex, FMS + res.length);\n    assertEq(rindex, encoded.length);\n\n    assertEq(res, abi.encodeWithSelector(_selector, _values[0], _values[1]));\n  }\n\n  function test_read_abi_dynamic_only_3(\n    bytes4 _selector,\n    bytes memory _data1,\n    bytes memory _data2,\n    bytes memory _data3\n  ) external {\n    bool[] memory isDynamic = new bool[](3);\n    bytes[] memory _values = new bytes[](3);\n\n    isDynamic[0] = true;\n    isDynamic[1] = true;\n    isDynamic[2] = true;\n\n    _values[0] = _data1;\n    _values[1] = _data2;\n    _values[2] = _data3;\n\n    bytes memory encoded = encode_abi_dynamic(_selector, isDynamic, _values);\n\n    (bool s, bytes memory r) = imp.staticcall(encoded);\n\n    assertTrue(s);\n\n    (uint256 rindex, uint256 windex, bytes memory res) = abi.decode(r, (uint256, uint256, bytes));\n\n    assertEq(windex, FMS + res.length);\n    assertEq(rindex, encoded.length);\n\n    assertEq(res, abi.encodeWithSelector(_selector, _values[0], _values[1], _values[2]));\n  }\n\n  function test_read_abi_dynamic_only_8(\n    bytes4 _selector,\n    bytes memory _data1,\n    bytes memory _data2,\n    bytes memory _data3,\n    bytes memory _data4,\n    bytes memory _data5,\n    bytes memory _data6,\n    bytes memory _data7,\n    bytes memory _data8\n  ) external {\n    bytes memory r; uint256 el;\n\n    {\n      bool[] memory isDynamic = new bool[](8);\n      bytes[] memory _values = new bytes[](8);\n\n      isDynamic[0] = true;\n      isDynamic[1] = true;\n      isDynamic[2] = true;\n      isDynamic[3] = true;\n      isDynamic[4] = true;\n      isDynamic[5] = true;\n      isDynamic[6] = true;\n      isDynamic[7] = true;\n\n      _values[0] = _data1;\n      _values[1] = _data2;\n      _values[2] = _data3;\n      _values[3] = _data4;\n      _values[4] = _data5;\n      _values[5] = _data6;\n      _values[6] = _data7;\n      _values[7] = _data8;\n\n      bool s;\n      bytes memory encoded = encode_abi_dynamic(_selector, isDynamic, _values);\n\n      (s, r) = imp.staticcall(encoded);\n      el = encoded.length;\n      assertTrue(s);\n    }\n\n\n    (uint256 rindex, uint256 windex, bytes memory res) = abi.decode(r, (uint256, uint256, bytes));\n\n    assertEq(windex, FMS + res.length);\n    assertEq(rindex, el);\n\n    assertEq(res, abi.encodeWithSelector(\n        _selector,\n        _data1,\n        _data2,\n        _data3,\n        _data4,\n        _data5,\n        _data6,\n        _data7,\n        _data8\n      )\n    );\n  }\n\n  function test_read_mixed_2(\n    bytes4 _selector,\n    bytes32 _data1,\n    bytes memory _data2\n  ) external {\n    bool[] memory isDynamic = new bool[](2);\n    bytes[] memory _values = new bytes[](2);\n\n    isDynamic[0] = false;\n    isDynamic[1] = true;\n\n    _values[0] = abi.encodePacked(_data1);\n    _values[1] = _data2;\n\n    bytes memory encoded = encode_abi_dynamic(_selector, isDynamic, _values);\n\n    (bool s, bytes memory r) = imp.staticcall(encoded);\n\n    assertTrue(s);\n\n    bytes memory expected = abi.encodeWithSelector(_selector, _data1, _data2);\n\n    (uint256 rindex, uint256 windex, bytes memory res) = abi.decode(r, (uint256, uint256, bytes));\n\n    assertEq(windex, FMS + res.length);\n    assertEq(rindex, encoded.length);\n\n    assertEq(res, expected);\n  }\n\n  function test_read_mixed_2b(\n    bytes4 _selector,\n    bytes memory _data1,\n    bytes32 _data2\n  ) external {\n    bool[] memory isDynamic = new bool[](2);\n    bytes[] memory _values = new bytes[](2);\n\n    isDynamic[0] = true;\n    isDynamic[1] = false;\n\n    _values[0] = _data1;\n    _values[1] = abi.encodePacked(_data2);\n\n    bytes memory encoded = encode_abi_dynamic(_selector, isDynamic, _values);\n\n    (bool s, bytes memory r) = imp.staticcall(encoded);\n\n    assertTrue(s);\n\n    bytes memory expected = abi.encodeWithSelector(_selector, _data1, _data2);\n\n    (uint256 rindex, uint256 windex, bytes memory res) = abi.decode(r, (uint256, uint256, bytes));\n\n    assertEq(windex, FMS + res.length);\n    assertEq(rindex, encoded.length);\n\n    assertEq(res, expected);\n  }\n\n  function test_read_abi_mixed_8b(\n    bytes4 _selector,\n    bytes32 _data1,\n    bytes memory _data2,\n    bytes32 _data3,\n    bytes memory _data4,\n    bytes memory _data5,\n    bytes memory _data6,\n    bytes32 _data7,\n    bytes32 _data8\n  ) external {\n    bytes memory r; uint256 el;\n\n    {\n      bool[] memory isDynamic = new bool[](8);\n      bytes[] memory _values = new bytes[](8);\n\n      isDynamic[0] = false;\n      isDynamic[1] = true;\n      isDynamic[2] = false;\n      isDynamic[3] = true;\n      isDynamic[4] = true;\n      isDynamic[5] = true;\n      isDynamic[6] = false;\n      isDynamic[7] = false;\n\n      _values[0] = abi.encodePacked(_data1);\n      _values[1] = _data2;\n      _values[2] = abi.encodePacked(_data3);\n      _values[3] = _data4;\n      _values[4] = _data5;\n      _values[5] = _data6;\n      _values[6] = abi.encodePacked(_data7);\n      _values[7] = abi.encodePacked(_data8);\n\n      bool s;\n      bytes memory encoded = encode_abi_dynamic(_selector, isDynamic, _values);\n\n      (s, r) = imp.staticcall(encoded);\n      el = encoded.length;\n      assertTrue(s);\n    }\n\n\n    (uint256 rindex, uint256 windex, bytes memory res) = abi.decode(r, (uint256, uint256, bytes));\n\n    assertEq(windex, FMS + res.length);\n    assertEq(rindex, el);\n\n    assertEq(res, abi.encodeWithSelector(\n        _selector,\n        _data1,\n        _data2,\n        _data3,\n        _data4,\n        _data5,\n        _data6,\n        _data7,\n        _data8\n      )\n    );\n  }\n\n  function test_read_abi_mixed_8b(\n    bytes4 _selector,\n    bytes memory _data1,\n    bytes32 _data2,\n    bytes32 _data3,\n    bytes memory _data4,\n    bytes memory _data5,\n    bytes32 _data6,\n    bytes memory _data7,\n    bytes memory _data8\n  ) external {\n    bytes memory r; uint256 el;\n\n    {\n      bool[] memory isDynamic = new bool[](8);\n      bytes[] memory _values = new bytes[](8);\n\n      isDynamic[0] = true;\n      isDynamic[1] = false;\n      isDynamic[2] = false;\n      isDynamic[3] = true;\n      isDynamic[4] = true;\n      isDynamic[5] = false;\n      isDynamic[6] = true;\n      isDynamic[7] = true;\n\n      _values[0] = _data1;\n      _values[1] = abi.encodePacked(_data2);\n      _values[2] = abi.encodePacked(_data3);\n      _values[3] = _data4;\n      _values[4] = _data5;\n      _values[5] = abi.encodePacked(_data6);\n      _values[6] = _data7;\n      _values[7] = _data8;\n\n      bool s;\n      bytes memory encoded = encode_abi_dynamic(_selector, isDynamic, _values);\n\n      (s, r) = imp.staticcall(encoded);\n      el = encoded.length;\n      assertTrue(s);\n    }\n\n\n    (uint256 rindex, uint256 windex, bytes memory res) = abi.decode(r, (uint256, uint256, bytes));\n\n    assertEq(windex, FMS + res.length);\n    assertEq(rindex, el);\n\n    assertEq(res, abi.encodeWithSelector(\n        _selector,\n        _data1,\n        _data2,\n        _data3,\n        _data4,\n        _data5,\n        _data6,\n        _data7,\n        _data8\n      )\n    );\n  }\n\n  function test_read_no_op() external {\n    bytes memory encoded = abi.encodePacked(uint8(0x4c));\n\n    (bool s, bytes memory r) = imp.staticcall(encoded);\n    assertTrue(s);\n\n    (uint256 rindex, uint256 windex, bytes memory res) = abi.decode(r, (uint256, uint256, bytes));\n    assertEq(rindex, 1);\n    assertEq(windex, FMS + res.length);\n    assertEq(res.length, 0);\n  }\n\n  function test_mirror_flag() external {\n    bytes memory encoded = abi.encodePacked(\n      encode_nested(\n        encodeWord(type(uint256).max - 1),\n        abi.encodePacked(uint8(0x4d), uint16(0x02))\n      )\n    );\n\n    (bool s, bytes memory r) = imp.staticcall(encoded);\n    assertTrue(s);\n\n    (uint256 rindex, uint256 windex, bytes memory res) = abi.decode(r, (uint256, uint256, bytes));\n\n    assertEq(windex, FMS + res.length);\n    assertEq(rindex, encoded.length);\n    assertEq(res, abi.encodePacked(type(uint256).max - 1, type(uint256).max - 1));\n  }\n\n  function test_copy_calldata() external {\n    bytes memory encoded = abi.encodePacked(\n      encode_nested(\n        encodeWord(type(uint256).max - 1),\n        abi.encodePacked(uint8(0x4e), uint16(0x03), uint8(0x21))\n      )\n    );\n\n    (bool s, bytes memory r) = imp.staticcall(encoded);\n    assertTrue(s);\n\n    (uint256 rindex, uint256 windex, bytes memory res) = abi.decode(r, (uint256, uint256, bytes));\n\n    assertEq(windex, FMS + res.length);\n    assertEq(rindex, encoded.length);\n    assertEq(res, abi.encodePacked(type(uint256).max - 1, type(uint256).max - 1, uint8(0x4e)));\n  }\n\n  function test_read_storage_flag_addr(address _addr) external {\n    _addr = address(this);\n    bytes memory encoded = abi.encodePacked(\n      encode_nested(\n        abi.encodePacked(uint8(0x21), _addr),\n        abi.encodePacked(uint8(0x4f), uint16(0x02))\n      )\n    );\n\n    (bool s, bytes memory r) = imp.call(encoded);\n    assertTrue(s);\n\n    (uint256 rindex, uint256 windex, bytes memory res) = abi.decode(r, (uint256, uint256, bytes));\n\n    assertEq(windex, FMS + res.length);\n    assertEq(rindex, encoded.length);\n    assertEq(res, abi.encode(_addr, _addr));\n  }\n\n  function test_read_literal(uint256 _val) external {\n    bytes memory encoded = encodeWord(_val);\n\n    (bool s, bytes memory r) = imp.call(encoded);\n    assertTrue(s);\n\n    (uint256 rindex, uint256 windex, bytes memory res) = abi.decode(r, (uint256, uint256, bytes));\n\n    assertEq(windex, FMS + res.length);\n    assertEq(rindex, encoded.length);\n    assertEq(res, abi.encode(_val));\n  }\n\n  function test_read_pow_10(uint256 _exp) external {\n    _exp = bound(_exp, 0, 77);\n\n    // First bit means we aren't going to multiply it after\n    bytes memory encoded = abi.encodePacked(uint8(0x00), uint8(_exp) | uint8(0x80));\n\n    (bool s, bytes memory r) = imp.call(encoded);\n    assertTrue(s);\n\n    (uint256 rindex, uint256 windex, bytes memory res) = abi.decode(r, (uint256, uint256, bytes));\n\n    assertEq(windex, FMS + res.length);\n    assertEq(rindex, encoded.length);\n    assertEq(res, abi.encode(uint256(10 ** _exp)));\n  }\n\n  function test_read_pow_10_and_mul(uint256 _exp, uint8 _mantissa) external {\n    _exp = bound(_exp, 1, 77);\n\n    // First bit means we aren't going to multiply it after\n    bytes memory encoded = abi.encodePacked(uint8(0x00), uint8(_exp), uint8(_mantissa));\n\n    (bool s, bytes memory r) = imp.call(encoded);\n    assertTrue(s);\n\n    (uint256 rindex, uint256 windex, bytes memory res) = abi.decode(r, (uint256, uint256, bytes));\n\n    unchecked {\n      assertEq(windex, FMS + res.length);\n      assertEq(rindex, encoded.length);\n      assertEq(res, abi.encode(uint256(10 ** _exp) * uint256(_mantissa)));\n    }\n  }\n\n  function test_read_pow_10_and_mul_l(uint256 _exp, uint256 _mantissa) external {\n    _exp = bound(_exp, 0, 63);\n    _mantissa = bound(_mantissa, 0, 0x3ffff);\n\n    // Encode the 3 byte word, the first 3 bits are the exponent, the rest is the mantissa\n    bytes3 word = bytes3(uint24(_exp) << 18 | uint24(_mantissa));\n\n    // First bit means we aren't going to multiply it after\n    bytes memory encoded = abi.encodePacked(uint8(0x2a), word);\n\n    (bool s, bytes memory r) = imp.call(encoded);\n    assertTrue(s);\n\n    (uint256 rindex, uint256 windex, bytes memory res) = abi.decode(r, (uint256, uint256, bytes));\n\n    uint256 expected;\n    unchecked {\n      expected = uint256(10 ** _exp) * uint256(_mantissa);\n    }\n\n    unchecked {\n      assertEq(windex, FMS + res.length);\n      assertEq(rindex, encoded.length);\n      assertEq(res, abi.encode(expected));\n    }\n  }\n\n  function test_read_self_execute() external {\n    // vm.assume(_txs.length != 0 && _txs.length <= type(uint8).max);\n\n    IModuleCalls.Transaction[] memory _txs = new IModuleCalls.Transaction[](1);\n\n    bytes memory encoded = abi.encodePacked(\n      uint8(0x00), uint8(0x00),\n      uint8(_txs.length)\n    );\n\n    for (uint256 i = 0; i < _txs.length; i++) {\n      IModuleCalls.Transaction memory t = _txs[i];\n\n      encoded = abi.encodePacked(\n        encoded,\n        build_flag(t.delegateCall, t.revertOnError, t.gasLimit != 0, t.value != 0, t.data.length != 0),\n        t.gasLimit != 0 ? encodeWord(t.gasLimit) : bytes(\"\"),\n        encode_raw_address(t.target),\n        t.value != 0 ? encodeWord(t.value) : bytes(\"\"),\n        t.data.length != 0 ? encode_bytes_n(t.data) : bytes(\"\")\n      );\n    }\n\n    (bool s, bytes memory r) = imp.staticcall(encoded);\n\n    assertTrue(s);\n    (uint256 rindex, uint256 windex, bytes memory res) = abi.decode(r, (uint256, uint256, bytes));\n\n    assertEq(rindex, encoded.length);\n    assertEq(windex, res.length + FMS);\n\n    bytes memory solidityEncoded = abi.encodeWithSelector(IModuleCalls.selfExecute.selector, _txs);\n    assertEq(solidityEncoded, res);\n  }\n\n  function test_read_flag_abi_encode_by_index() external {\n    bytes memory encoded = abi.encodePacked(uint8(0x2d), uint8(0x01));\n    (bool s, bytes memory r) = imp.staticcall(encoded);\n\n    assertTrue(s);\n\n    (uint256 rindex, uint256 windex, bytes memory res) = abi.decode(r, (uint256, uint256, bytes));\n\n    assertEq(rindex, encoded.length);\n    assertEq(windex, FMS + res.length);\n    assertEq(hex\"a9059cbb\", res);\n  }\n\n  function test_read_flag_abi_encode_by_index_2() external {\n    bytes memory encoded = abi.encodePacked(uint8(0x2d), uint8(0x02));\n    (bool s, bytes memory r) = imp.staticcall(encoded);\n\n    assertTrue(s);\n\n    (uint256 rindex, uint256 windex, bytes memory res) = abi.decode(r, (uint256, uint256, bytes));\n\n    assertEq(rindex, encoded.length);\n    assertEq(windex, FMS + res.length);\n    assertEq(hex\"095ea7b3\", res);\n  }\n\n  function test_read_flag_abi_encode_by_index_2_args(bytes32 _arg) external {\n    bytes memory encoded = abi.encodePacked(uint8(0x2e), uint8(0x01), encodeWord(_arg));\n    (bool s, bytes memory r) = imp.staticcall(encoded);\n\n    assertTrue(s);\n\n    (uint256 rindex, uint256 windex, bytes memory res) = abi.decode(r, (uint256, uint256, bytes));\n\n    assertEq(rindex, encoded.length);\n    assertEq(windex, FMS + res.length);\n    assertEq(abi.encodePacked(hex\"a9059cbb\", _arg), res);\n  }\n}\n"
  },
  {
    "path": "foundry_test/modules/utils/L2CompressorHuffReadNonce.sol",
    "content": "// SPDX-License-Identifier: UNLICENSED\npragma solidity ^0.8.13;\n\nimport \"foundry_test/base/AdvTest.sol\";\n\nimport \"forge-std/console.sol\";\nimport \"forge-std/console2.sol\";\n\nimport { HuffConfig } from \"foundry-huff/HuffConfig.sol\";\nimport { HuffDeployer } from \"foundry-huff/HuffDeployer.sol\";\n\nimport \"contracts/modules/commons/interfaces/IModuleCalls.sol\";\n\nuint256 constant FMS = 0xa0;\n\nimport \"./L2CompressorEncoder.sol\";\n\ncontract L2CompressorHuffReadNonceTest is AdvTest {\n  address public imp;\n\n  function setUp() public {\n    imp = address(\n      HuffDeployer\n        .config()\n        .with_evm_version(\"paris\")\n        .deploy(\"imps/L2CompressorReadNonce\")\n    );\n  }\n\n  function test_read_simple_nonce() external {\n    uint256 space = 76518466025766696338879503773554426820412884125;\n    uint256 nonce = 29095922913147819529123945996;\n\n    bytes32 compact = 0x0d6734e95e00251b768924d47d52db3270fcc49d5e039555a5312d84eb305e0c;\n\n    bytes memory encoded = abi.encodePacked(\n      encodeWord(space), encodeWord(nonce)\n    );\n\n    (bool s, bytes memory r) = imp.staticcall(encoded);\n\n    assertTrue(s);\n    (uint256 rindex, uint256 windex, bytes memory res) = abi.decode(r, (uint256, uint256, bytes));\n    assertEq(rindex, encoded.length);\n    assertEq(windex, res.length + FMS);\n\n    assertEq(compact, abi.decode(res, (bytes32)));\n  }\n\n  function test_read_nonce(uint160 _space, uint96 _nonce) external {\n    bytes memory encoded = abi.encodePacked(\n      encodeWord(_space), encodeWord(_nonce)\n    );\n\n    (bool s, bytes memory r) = imp.staticcall(encoded);\n\n    assertTrue(s);\n    (uint256 rindex, uint256 windex, bytes memory res) = abi.decode(r, (uint256, uint256, bytes));\n    assertEq(rindex, encoded.length);\n    assertEq(windex, res.length + FMS);\n\n    assertEq(abi.encodePacked(_space, _nonce), res);\n  }\n}\n"
  },
  {
    "path": "foundry_test/modules/utils/L2CompressorHuffReadTx.t.sol",
    "content": "// SPDX-License-Identifier: UNLICENSED\npragma solidity ^0.8.13;\n\nimport \"foundry_test/base/AdvTest.sol\";\n\nimport \"forge-std/console.sol\";\nimport \"forge-std/console2.sol\";\n\nimport { HuffConfig } from \"foundry-huff/HuffConfig.sol\";\nimport { HuffDeployer } from \"foundry-huff/HuffDeployer.sol\";\n\nimport \"contracts/modules/commons/interfaces/IModuleCalls.sol\";\n\nimport \"./L2CompressorEncoder.sol\";\n\nuint256 constant FMS = 0xa0;\n\ncontract L2CompressorHuffReadTxTests is AdvTest {\n  address public imp;\n\n  function setUp() public {\n    imp = address(\n      HuffDeployer\n        .config()\n        .with_evm_version(\"paris\")\n        .deploy(\"imps/L2CompressorReadTx\")\n    );\n  }\n\n  function test_read_simple_transaction(address _addr) external {\n    bytes memory encoded = abi.encodePacked(build_flag(true, true, false, false, false), encode_raw_address(_addr));\n\n    (bool s, bytes memory r) = imp.staticcall(encoded);\n\n    assertTrue(s);\n    (uint256 rindex, uint256 windex, bytes memory res) = abi.decode(r, (uint256, uint256, bytes));\n\n    IModuleCalls.Transaction memory t;\n    t.delegateCall = true;\n    t.revertOnError = true;\n    t.target = _addr;\n\n    assertEq(rindex, encoded.length);\n    assertEq(windex, res.length + FMS);\n\n    // Abi encode prefixes with the point on which the data starts\n    // we don't do it on the compressor, so we need to append 32\n    assertEq(abi.encodePacked(abi.encode(32), res), abi.encode(t));\n  }\n\n  function test_read_simple_transaction_with_data(address _addr, bytes memory _data) external {\n    bytes memory encoded = abi.encodePacked(\n      build_flag(true, true, false, false, true),\n      encode_raw_address(_addr),\n      encode_bytes_n(_data)\n    );\n\n    (bool s, bytes memory r) = imp.staticcall(encoded);\n\n    assertTrue(s);\n    (uint256 rindex, uint256 windex, bytes memory res) = abi.decode(r, (uint256, uint256, bytes));\n\n    IModuleCalls.Transaction memory t;\n    t.delegateCall = true;\n    t.revertOnError = true;\n    t.target = _addr;\n    t.data = _data;\n\n    assertEq(rindex, encoded.length);\n    assertEq(windex, res.length + FMS);\n\n    // Abi encode prefixes with the point on which the data starts\n    // we don't do it on the compressor, so we need to append 32\n    assertEq(abi.encodePacked(abi.encode(32), res), abi.encode(t));\n  }\n\n  function test_read_transaction(IModuleCalls.Transaction memory _tx) external {\n    bytes memory encoded = abi.encodePacked(\n      build_flag(_tx.delegateCall, _tx.revertOnError, _tx.gasLimit != 0, _tx.value != 0, _tx.data.length != 0),\n      _tx.gasLimit != 0 ? encodeWord(_tx.gasLimit) : bytes(\"\"),\n      encode_raw_address(_tx.target),\n      _tx.value != 0 ? encodeWord(_tx.value) : bytes(\"\"),\n      _tx.data.length != 0 ? encode_bytes_n(_tx.data) : bytes(\"\")\n    );\n\n    console.logBytes(encoded);\n\n    (bool s, bytes memory r) = imp.staticcall(encoded);\n\n    assertTrue(s);\n    (uint256 rindex, uint256 windex, bytes memory res) = abi.decode(r, (uint256, uint256, bytes));\n\n    assertEq(rindex, encoded.length);\n    assertEq(windex, res.length + FMS);\n\n    // Abi encode prefixes with the point on which the data starts\n    // we don't do it on the compressor, so we need to append 32\n    assertEq(abi.encodePacked(abi.encode(32), res), abi.encode(_tx));\n  }\n}\n"
  },
  {
    "path": "foundry_test/modules/utils/L2CompressorHuffReadTxs.t.sol",
    "content": "// SPDX-License-Identifier: UNLICENSED\npragma solidity ^0.8.13;\n\nimport \"foundry_test/base/AdvTest.sol\";\n\nimport \"forge-std/console.sol\";\nimport \"forge-std/console2.sol\";\n\nimport { HuffConfig } from \"foundry-huff/HuffConfig.sol\";\nimport { HuffDeployer } from \"foundry-huff/HuffDeployer.sol\";\n\nimport \"contracts/modules/commons/interfaces/IModuleCalls.sol\";\n\nuint256 constant FMS = 0xa0;\n\nimport \"./L2CompressorEncoder.sol\";\n\ncontract L2CompressorHuffReadTxTests is AdvTest {\n  address public imp;\n\n  function setUp() public {\n    imp = address(\n      HuffDeployer\n        .config()\n        .with_evm_version(\"paris\")\n        .deploy(\"imps/L2CompressorReadTxs\")\n    );\n  }\n\n  function test_read_simple_2_transactions() external {\n    address _addr = address(0x1234567890123456789012345678901234567890);\n    address _addr2 = address(this);\n    bytes memory encoded = abi.encodePacked(\n      uint8(0x02),\n      build_flag(false, true, false, false, false),\n      encode_raw_address(_addr),\n      build_flag(true, true, false, false, false),\n      encode_raw_address(_addr2)\n    );\n\n    (bool s, bytes memory r) = imp.staticcall{ gas: 10000 }(encoded);\n\n    assertTrue(s);\n    (uint256 rindex, uint256 windex, bytes memory res) = abi.decode(r, (uint256, uint256, bytes));\n\n    IModuleCalls.Transaction[] memory t = new IModuleCalls.Transaction[](2);\n    t[0].delegateCall = false;\n    t[0].revertOnError = true;\n    t[0].target = _addr;\n    t[1].delegateCall = true;\n    t[1].revertOnError = true;\n    t[1].target = _addr2;\n\n    assertEq(rindex, encoded.length);\n    assertEq(windex, res.length + FMS);\n\n    // Abi encode prefixes with the point on which the data starts\n    // we don't do it on the compressor, so we need to append 32\n    assertEq(abi.encodePacked(abi.encode(32), res), abi.encode(t));\n  }\n\n  function test_read_simple_2_transactions_asymetric() external {\n    address _addr = address(0x1234567890123456789012345678901234567890);\n    address _addr2 = address(this);\n    bytes memory _data = hex\"123456789012345678901234567890123456789012345678901234567890123456789011222211\";\n\n    bytes memory encoded = abi.encodePacked(\n      uint8(0x02),\n      build_flag(false, true, false, false, true),\n      encode_raw_address(_addr),\n      encode_bytes_n(_data),\n      build_flag(true, true, false, false, false),\n      encode_raw_address(_addr2)\n    );\n\n    (bool s, bytes memory r) = imp.staticcall{ gas: 10000 }(encoded);\n\n    assertTrue(s);\n    (uint256 rindex, uint256 windex, bytes memory res) = abi.decode(r, (uint256, uint256, bytes));\n\n    IModuleCalls.Transaction[] memory t = new IModuleCalls.Transaction[](2);\n    t[0].delegateCall = false;\n    t[0].revertOnError = true;\n    t[0].target = _addr;\n    t[0].data = _data;\n    t[1].delegateCall = true;\n    t[1].revertOnError = true;\n    t[1].target = _addr2;\n\n    assertEq(rindex, encoded.length);\n    assertEq(windex, res.length + FMS);\n\n    // Abi encode prefixes with the point on which the data starts\n    // we don't do it on the compressor, so we need to append 32\n    assertEq(abi.encodePacked(abi.encode(32), res), abi.encode(t));\n  }\n\n  function test_read_transactions(IModuleCalls.Transaction[] memory _txs) external {\n    vm.assume(_txs.length != 0 && _txs.length <= type(uint8).max);\n\n    bytes memory encoded = abi.encodePacked(\n      uint8(_txs.length)\n    );\n\n    for (uint256 i = 0; i < _txs.length; i++) {\n      IModuleCalls.Transaction memory t = _txs[i];\n\n      encoded = abi.encodePacked(\n        encoded,\n        build_flag(t.delegateCall, t.revertOnError, t.gasLimit != 0, t.value != 0, t.data.length != 0),\n        t.gasLimit != 0 ? encodeWord(t.gasLimit) : bytes(\"\"),\n        encode_raw_address(t.target),\n        t.value != 0 ? encodeWord(t.value) : bytes(\"\"),\n        t.data.length != 0 ? encode_bytes_n(t.data) : bytes(\"\")\n      );\n    }\n\n    (bool s, bytes memory r) = imp.staticcall(encoded);\n\n    assertTrue(s);\n    (uint256 rindex, uint256 windex, bytes memory res) = abi.decode(r, (uint256, uint256, bytes));\n\n    assertEq(rindex, encoded.length);\n    assertEq(windex, res.length + FMS);\n\n    // Abi encode prefixes with the point on which the data starts\n    // we don't do it on the compressor, so we need to append 32\n    assertEq(abi.encodePacked(abi.encode(32), res), abi.encode(_txs));\n  }\n}\n"
  },
  {
    "path": "foundry_test/modules/utils/RequireUtils.t.sol",
    "content": "// SPDX-License-Identifier: Apache-2.0\npragma solidity 0.8.18;\n\nimport \"contracts/Factory.sol\";\nimport \"contracts/modules/commons/ModuleCalls.sol\";\nimport \"contracts/modules/utils/RequireUtils.sol\";\n\nimport \"contracts/mocks/ERC20Mock.sol\";\nimport \"contracts/mocks/ERC721Mock.sol\";\nimport \"contracts/mocks/ERC1155Mock.sol\";\n\nimport \"foundry_test/base/AdvTest.sol\";\n\ncontract ModuleCallsImp is ModuleCalls {\n\n  function writeNonce(uint256 _space, uint256 _nonce) external {\n    _writeNonce(_space, _nonce);\n  }\n\n  // Module Auth imp\n  mapping(bytes32 => mapping(bytes => bytes32)) public sigToSubdigest;\n  mapping(bytes32 => mapping(bytes => bool)) public sigToIsValid;\n\n  function _signatureValidation(\n    bytes32 _digest,\n    bytes calldata _signature\n  ) internal override view returns (\n    bool,\n    bytes32\n  ) {\n   \n  }\n\n  function signatureRecovery(bytes32, bytes calldata) public override view returns (\n    uint256, uint256, bytes32, bytes32, uint256\n  ) {\n  }\n\n  function _isValidImage(bytes32) internal override view returns (bool) {\n  }\n\n  function updateImageHash(bytes32) external override {\n  }\n\n  function _updateImageHash(bytes32) internal override {\n  }\n}\n\ncontract RequireUtilsTest is AdvTest {\n  ModuleCallsImp private imp;\n  RequireUtils private requireUtils;\n  ERC20Mock private erc20;\n  ERC721Mock private erc721;\n  ERC1155Mock private erc1155;\n  \n  function setUp() external {\n    requireUtils = new RequireUtils();\n    ModuleCallsImp template = new ModuleCallsImp();\n    Factory factory = new Factory();\n    imp = ModuleCallsImp(factory.deploy(address(template), bytes32(0)));\n    erc20 = new ERC20Mock(1000 * 10 ** 18);\n    erc721 = new ERC721Mock();\n    erc1155 = new ERC1155Mock();\n  }\n\n  function test_requireNonExpired(uint256 _expiration) external {\n    if (block.timestamp >= _expiration) {\n      vm.expectRevert(bytes('RequireUtils#requireNonExpired: EXPIRED'));\n    }\n    requireUtils.requireNonExpired(_expiration);\n  }\n\n  function test_requireMinNonce(uint160 _space, uint96 _nonce, uint96 _nonceToCheck) external {\n    imp.writeNonce(_space, _nonce);\n    uint256 encoded = abi.decode(abi.encodePacked(_space, _nonceToCheck), (uint256));\n    if (_nonce < _nonceToCheck) {\n      vm.expectRevert(bytes('RequireUtils#requireMinNonce: NONCE_BELOW_REQUIRED'));\n    }\n    requireUtils.requireMinNonce(address(imp), encoded);\n  }\n\n  function test_requireMinNonceWithExactNonce(uint160 _space, uint96 _nonce) external {\n    imp.writeNonce(_space, _nonce);\n    uint256 encoded = abi.decode(abi.encodePacked(_space, _nonce), (uint256));\n    requireUtils.requireMinNonce(address(imp), encoded);\n  }\n\n  function test_requireMinERC20Balance(uint256 _minBalance) external {\n    uint256 balance = erc20.balanceOf(address(this));\n\n    if (balance < _minBalance) {\n      vm.expectRevert(bytes('RequireUtils#requireMinERC20Balance: BALANCE_TOO_LOW'));\n    }\n    requireUtils.requireMinERC20Balance(address(erc20), address(this), _minBalance);\n  }\n\n  function test_requireMinERC20Allowance(uint256 _minAllowance) external {\n    erc20.approve(address(imp), 100 * 10 ** 18);\n\n    uint256 allowance = erc20.allowance(address(this), address(imp));\n\n    if (allowance < _minAllowance) {\n      vm.expectRevert(bytes('RequireUtils#requireMinERC20Allowance: ALLOWANCE_TOO_LOW'));\n    }\n    requireUtils.requireMinERC20Allowance(address(erc20), address(this), address(imp), _minAllowance);\n  }\n\n  function test_requireERC721Ownership(uint256 _tokenId) external {\n    if (_tokenId % 2 == 0) {\n      erc721.mint(address(imp), _tokenId);\n    } else {\n      erc721.mint(address(this), _tokenId);\n    }\n\n    if (erc721.ownerOf(_tokenId) != address(this)) {\n      vm.expectRevert(bytes('RequireUtils#requireERC721Ownership: NOT_OWNER'));\n    }\n    requireUtils.requireERC721Ownership(address(erc721), address(this), _tokenId);\n  }\n\n  function test_requireERC721Approval(uint256 _tokenId) external {\n    erc721.mint(address(this), _tokenId);\n\n    if (_tokenId % 2 == 0) {\n      erc721.approve(address(imp), _tokenId); \n    }\n\n    if (_tokenId % 5 == 0) {\n      erc721.setApprovalForAll(address(imp), true);\n    }\n\n    address approved = erc721.getApproved(_tokenId);\n\n    if (approved != address(imp) && !erc721.isApprovedForAll(address(this), address(imp))) {\n      vm.expectRevert(bytes('RequireUtils#requireERC721Approval: NOT_APPROVED'));\n    }\n    requireUtils.requireERC721Approval(address(erc721), address(this), address(imp), _tokenId);\n  }\n\n  function test_requireMinERC1155Balance(uint256 _tokenId, uint256 _minBalance) external {\n    if (_tokenId % 2 == 0) {\n      erc1155.mint(address(this), _tokenId, _minBalance); \n    }\n\n    uint256 balance = erc1155.balanceOf(address(this), _tokenId);\n\n    if (balance < _minBalance) {\n      vm.expectRevert(bytes('RequireUtils#requireMinERC1155Balance: BALANCE_TOO_LOW'));\n    }\n    requireUtils.requireMinERC1155Balance(address(erc1155), address(this), _tokenId, _minBalance);\n  }\n\n  function test_requireERC1155Approval(uint256 _tokenId) external {\n    if (_tokenId % 2 == 0) {\n      erc1155.setApprovalForAll(address(imp), true); \n    }\n\n    if (!erc1155.isApprovedForAll(address(this), address(imp))) {\n      vm.expectRevert(bytes('RequireUtils#requireERC1155Approval: NOT_APPROVED'));\n    }\n    requireUtils.requireERC1155Approval(address(erc1155), address(this), address(imp));\n  }\n}"
  },
  {
    "path": "foundry_test/trust/Trust.t.sol",
    "content": "// SPDX-License-Identifier: Apache-2.0\npragma solidity 0.8.18;\n\nimport \"contracts/trust/Trust.sol\";\nimport \"contracts/interfaces/IERC1271Wallet.sol\";\n\nimport \"foundry_test/base/AdvTest.sol\";\n\nfunction min(uint256 a, uint256 b) pure returns (uint256) {\n  return a < b ? a : b;\n}\n\ncontract MockFail {\n  bytes revertData;\n\n  constructor (bytes memory _revertData) {\n    revertData = _revertData;\n  }\n\n  fallback() external payable {\n    bytes memory rd = revertData;\n    assembly {\n      revert(add(rd, 0x20), mload(rd))\n    }\n  }\n}\n\ncontract MockContractSigner {\n  mapping(bytes32 => mapping(bytes => bytes4)) public staticSignatures;\n  bytes public staticRevertErr;\n  bool public staticReverts;\n\n  function isValidSignature(\n    bytes32 _hash,\n    bytes calldata _signature\n  ) external view returns (bytes4 magicValue) {\n    if (staticReverts) {\n      bytes memory rd = staticRevertErr;\n      assembly {\n        revert(add(rd, 0x20), mload(rd))\n      }\n    }\n\n    return staticSignatures[_hash][_signature];\n  }\n\n  function setSignature(bytes32 _hash, bytes calldata _signature, bytes4 _magicValue) external {\n    staticSignatures[_hash][_signature] = _magicValue;\n  }\n\n  function setRevertErr(bool _reverts, bytes calldata _revertErr) external {\n    staticRevertErr = _revertErr;\n    staticReverts = _reverts;\n  }\n}\n\ncontract TrustTest is AdvTest {\n  Trust private trust;\n\n  function test_define_initial_parameters(uint256 _ownerPk, uint256 _beneficiaryPk, uint256 _duration) external {\n    trust = new Trust(vm.addr(boundPk(_ownerPk)), vm.addr(boundPk(_beneficiaryPk)), _duration);\n    assertEq(trust.owner(), vm.addr(boundPk(_ownerPk)));\n    assertEq(trust.beneficiary(), vm.addr(boundPk(_beneficiaryPk)));\n    assertEq(trust.duration(), _duration);\n  }\n\n  function test_start_locked(uint256 _ownerPk, uint256 _beneficiaryPk, uint256 _duration) external {\n    trust = new Trust(vm.addr(boundPk(_ownerPk)), vm.addr(boundPk(_beneficiaryPk)), _duration);\n    assertEq(trust.isLocked(), true);\n  }\n\n  function test_fail_schedule_unlock_too_early(uint256 _ownerPk, uint256 _beneficiaryPk, uint256 _duration, uint256 _schedule) external {\n    vm.assume(_duration != 0);\n\n    _schedule = bound(_schedule, 0, _duration - 1);\n    trust = new Trust(vm.addr(boundPk(_ownerPk)), vm.addr(boundPk(_beneficiaryPk)), _duration);\n\n    vm.prank(vm.addr(boundPk(_ownerPk)));\n    vm.expectRevert(abi.encodeWithSignature('UnlockTooEarly(uint256,uint256)', block.timestamp + _schedule, _schedule));\n    trust.setUnlocksAt(block.timestamp + _schedule);\n\n    vm.prank(vm.addr(boundPk(_beneficiaryPk)));\n    vm.expectRevert(abi.encodeWithSignature('UnlockTooEarly(uint256,uint256)', block.timestamp + _schedule, _schedule));\n    trust.setUnlocksAt(block.timestamp + _schedule);\n  }\n\n  function test_fail_schedule_in_the_past(uint256 _ownerPk, uint256 _beneficiaryPk, uint256 _duration, uint256 _schedule) external {\n    _schedule = bound(_schedule, 1, block.timestamp);\n    trust = new Trust(vm.addr(boundPk(_ownerPk)), vm.addr(boundPk(_beneficiaryPk)), _duration);\n\n    vm.prank(vm.addr(boundPk(_ownerPk)));\n    vm.expectRevert(abi.encodeWithSignature('UnlockInThePast(uint256,uint256)', block.timestamp - _schedule, _schedule));\n    trust.setUnlocksAt(block.timestamp - _schedule);\n\n    vm.prank(vm.addr(boundPk(_beneficiaryPk)));\n    vm.expectRevert(abi.encodeWithSignature('UnlockInThePast(uint256,uint256)', block.timestamp - _schedule, _schedule));\n    trust.setUnlocksAt(block.timestamp - _schedule);\n  }\n\n  function test_fail_schedule_non_allowed(uint256 _ownerPk, uint256 _beneficiaryPk, uint256 _badActorpk, uint256 _duration, uint256 _unlockAt) external {\n    _badActorpk = boundDiff(boundPk(_badActorpk), boundPk(_ownerPk), boundPk(_beneficiaryPk));\n\n    trust = new Trust(vm.addr(boundPk(_ownerPk)), vm.addr(boundPk(_beneficiaryPk)), _duration);\n\n    vm.prank(vm.addr(boundPk(_badActorpk)));\n    vm.expectRevert(abi.encodeWithSignature('NotOwner(address)', vm.addr(boundPk(_badActorpk))));\n    trust.setUnlocksAt(_unlockAt);\n  }\n\n  event SetUnlocksAt(uint256 _unlocksAt);\n\n  function test_schedule_unlock(uint256 _ownerPk, uint256 _beneficiaryPk, bool _asOwner, uint256 _duration, uint256 _unlockAt) external {\n    _duration = bound(_duration, 0, type(uint256).max - block.timestamp);\n    _unlockAt = bound(_unlockAt, block.timestamp + _duration, type(uint256).max);\n\n    trust = new Trust(vm.addr(boundPk(_ownerPk)), vm.addr(boundPk(_beneficiaryPk)), _duration);\n\n    vm.prank(vm.addr(boundPk(_asOwner ? _ownerPk : _beneficiaryPk)));\n\n    vm.expectEmit(true, true, true, true, address(trust));\n    emit SetUnlocksAt(_unlockAt);\n\n    trust.setUnlocksAt(_unlockAt);\n    assertEq(trust.unlocksAt(), _unlockAt);\n  }\n\n  function test_wait_for_unlock(uint256 _ownerPk, uint256 _beneficiaryPk, uint256 _duration, uint256 _unlockAt, uint256 _extra) external {\n    vm.assume(block.timestamp != type(uint256).max);\n\n    _duration = bound(_duration, 0, (type(uint256).max - 1) - block.timestamp);\n    _unlockAt = bound(_unlockAt, block.timestamp + _duration, type(uint256).max - 1);\n    _extra    = bound(_extra, 0, type(uint256).max - _unlockAt);\n\n    trust = new Trust(vm.addr(boundPk(_ownerPk)), vm.addr(boundPk(_beneficiaryPk)), _duration);\n\n    vm.prank(vm.addr(boundPk(_beneficiaryPk)));\n    trust.setUnlocksAt(_unlockAt);\n\n    if (_duration > 0) {\n      vm.prank(vm.addr(boundPk(_beneficiaryPk)));\n      assertEq(trust.isLocked(), true);\n    }\n\n    vm.warp(_unlockAt + _extra);\n    assertEq(trust.isLocked(), false);\n  }\n\n  function gen_and_unlock(\n    uint256 _ownerPk,\n    uint256 _beneficiaryPk,\n    uint256 _duration,\n    uint256 _unlockAt,\n    uint256 _extra\n  ) internal returns (Trust) {\n    _duration = bound(_duration, 0, (type(uint256).max - 1) - block.timestamp);\n    _unlockAt = bound(_unlockAt, block.timestamp + _duration, type(uint256).max - 1);\n    _extra    = bound(_extra, 0, type(uint256).max - _unlockAt);\n\n    trust = new Trust(vm.addr(boundPk(_ownerPk)), vm.addr(boundPk(_beneficiaryPk)), _duration);\n\n    vm.prank(vm.addr(boundPk(_beneficiaryPk)));\n    trust.setUnlocksAt(_unlockAt);\n\n    vm.warp(_unlockAt + _extra);\n\n    return trust;\n  }\n\n  event SentTransaction(address _to, uint256 _value, bytes _data, bytes _result);\n\n  function test_send_transaction_after_unlock(\n    uint256 _ownerPk,\n    uint256 _beneficiaryPk,\n    bool    _ownerSender,\n    uint256 _duration,\n    uint256 _unlockAt,\n    uint256 _extra,\n    uint256 _toPk,\n    uint256 _value,\n    bytes calldata _data\n  ) external {\n    Trust t = gen_and_unlock(\n      _ownerPk,\n      _beneficiaryPk,\n      _duration,\n      _unlockAt,\n      _extra\n    );\n\n    vm.deal(address(this), _value);\n    payable(address((t))).transfer(_value);\n\n    vm.prank(vm.addr(boundPk(_ownerSender ? _ownerPk : _beneficiaryPk)));\n    vm.expectCall(vm.addr(boundPk(_toPk)), _value, _data);\n\n    vm.expectEmit(true, true, true, true, address(t));\n    emit SentTransaction(vm.addr(boundPk(_toPk)), _value, _data, new bytes(0));\n\n    t.sendTransaction(payable(vm.addr(boundPk(_toPk))), _value, _data);\n\n    assertEq(vm.addr(boundPk(_toPk)).balance, _value);\n  }\n\n  function test_send_transaction_pre_unlock_as_owner(\n    uint256 _ownerPk,\n    uint256 _beneficiaryPk,\n    uint256 _duration,\n    uint256 _unlockAt,\n    uint256 _toPk,\n    uint256 _value,\n    bytes calldata _data\n  ) external {\n    _duration = bound(_duration, 0, type(uint256).max - block.timestamp);\n    _unlockAt = bound(_unlockAt, block.timestamp + _duration, type(uint256).max);\n\n    Trust t = new Trust(vm.addr(boundPk(_ownerPk)), vm.addr(boundPk(_beneficiaryPk)), _duration);\n\n    vm.deal(address(this), _value);\n    payable(address((t))).transfer(_value);\n\n    vm.prank(vm.addr(boundPk(_beneficiaryPk)));\n    t.setUnlocksAt(_unlockAt);\n\n    vm.prank(vm.addr(boundPk(_ownerPk)));\n    vm.expectCall(vm.addr(boundPk(_toPk)), _value, _data);\n    t.sendTransaction(payable(vm.addr(boundPk(_toPk))), _value, _data);\n  }\n\n  function test_fail_send_transaction_pre_unlock_as_beneficiary(\n    uint256 _ownerPk,\n    uint256 _beneficiaryPk,\n    uint256 _duration,\n    uint256 _unlockAt,\n    uint256 _toPk,\n    uint256 _value,\n    uint256 _elapsed,\n    bytes calldata _data\n  ) external {\n    _duration = bound(_duration, 1, type(uint256).max - block.timestamp);\n    _unlockAt = bound(_unlockAt, block.timestamp + _duration, type(uint256).max);\n    _elapsed  = bound(_elapsed, 0, (_unlockAt - block.timestamp) - 1);\n\n    vm.assume(vm.addr(boundPk(_ownerPk)) != vm.addr(boundPk(_beneficiaryPk)));\n\n    Trust t = new Trust(vm.addr(boundPk(_ownerPk)), vm.addr(boundPk(_beneficiaryPk)), _duration);\n\n    vm.deal(address(this), _value);\n    payable(address((t))).transfer(_value);\n\n    vm.prank(vm.addr(boundPk(_beneficiaryPk)));\n    t.setUnlocksAt(_unlockAt);\n\n    vm.warp(block.timestamp + _elapsed);\n\n    assertEq(t.isLocked(), true);\n\n    vm.prank(vm.addr(boundPk(_beneficiaryPk)));\n    vm.expectRevert(abi.encodeWithSignature('NotUnlocked(uint256)', _unlockAt));\n    t.sendTransaction(payable(vm.addr(boundPk(_toPk))), _value, _data);\n  }\n\n  function test_fail_send_transaction_non_allowed(\n    uint256 _ownerPk,\n    uint256 _beneficiaryPk,\n    uint256 _badActorPk,\n    uint256 _duration,\n    uint256 _unlockAt,\n    uint256 _elapsed,\n    uint256 _toPk,\n    uint256 _value,\n    bytes calldata _data\n  ) external {\n    _duration = bound(_duration, 0, type(uint256).max - block.timestamp);\n    _unlockAt = bound(_unlockAt, block.timestamp + _duration, type(uint256).max);\n    _elapsed  = bound(_elapsed, 0, type(uint256).max - block.timestamp);\n\n    _badActorPk = boundDiff(boundPk(_badActorPk), boundPk(_ownerPk), boundPk(_beneficiaryPk));\n\n    Trust t = new Trust(vm.addr(boundPk(_ownerPk)), vm.addr(boundPk(_beneficiaryPk)), _duration);\n\n    vm.deal(address(this), _value);\n    payable(address((t))).transfer(_value);\n\n    vm.prank(vm.addr(boundPk(_beneficiaryPk)));\n    t.setUnlocksAt(_unlockAt);\n\n    vm.warp(block.timestamp + _elapsed);\n\n    vm.prank(vm.addr(boundPk(_badActorPk)));\n    vm.expectRevert(abi.encodeWithSignature('NotOwner(address)', vm.addr(boundPk(_badActorPk))));\n    t.sendTransaction(payable(vm.addr(boundPk(_toPk))), _value, _data);\n  }\n\n  function test_fail_bubble_up_fail(\n    uint256 _ownerPk,\n    uint256 _beneficiaryPk,\n    bool    _ownerSender,\n    uint256 _duration,\n    uint256 _unlockAt,\n    uint256 _value,\n    uint256 _extra,\n    bytes calldata _data,\n    bytes calldata _revertData\n  ) external {\n    address sender = vm.addr(boundPk(_ownerSender ? _ownerPk : _beneficiaryPk));\n\n    Trust t = gen_and_unlock(\n      _ownerPk,\n      _beneficiaryPk,\n      _duration,\n      _unlockAt,\n      _extra\n    );\n\n    vm.deal(address(this), _value);\n    payable(address((t))).transfer(_value);\n\n    MockFail mf = new MockFail(_revertData);\n\n    vm.prank(sender);\n\n    vm.expectRevert(abi.encodeWithSignature(\n      \"FailedTransaction(address,uint256,bytes,bytes)\",\n      address(mf),\n      _value,\n      _data,\n      _revertData\n    ));\n\n    t.sendTransaction(payable(address(mf)), _value, _data);\n  }\n\n  function test_isValidSignature(\n    uint256 _ownerPk,\n    uint256 _beneficiaryPk,\n    uint256 _duration,\n    uint256 _unlockAt,\n    uint256 _extra,\n    bool _signedByOwner,\n    bytes calldata _message\n  ) external {\n    uint256 signerPk = boundPk(_signedByOwner ? _ownerPk : _beneficiaryPk);\n\n    Trust t = gen_and_unlock(\n      _ownerPk,\n      _beneficiaryPk,\n      _duration,\n      _unlockAt,\n      _extra\n    );\n\n    bytes32 rawHash = keccak256(_message);\n    bytes32 finalHash = keccak256(abi.encode(address(t), rawHash, block.chainid));\n    (uint8 v, bytes32 r, bytes32 s) = vm.sign(signerPk, finalHash);\n\n    bytes memory sig = abi.encodePacked(r, s, v, uint8(1), _signedByOwner ? bytes1(0x00) : bytes1(0x01));\n\n    assertEq(t.isValidSignature(rawHash, sig), bytes4(0x1626ba7e));\n    assertEq(t.isValidSignature(_message, sig), bytes4(0x20c13b0b));\n  }\n\n  function test_isValidSignature_anyNetwork(\n    uint256 _ownerPk,\n    uint256 _beneficiaryPk,\n    uint256 _duration,\n    uint256 _unlockAt,\n    uint256 _extra,\n    uint64 _useChainId,\n    bool _signedByOwner,\n    bytes calldata _message\n  ) external {\n    uint256 signerPk = boundPk(_signedByOwner ? _ownerPk : _beneficiaryPk);\n\n    Trust t = gen_and_unlock(\n      _ownerPk,\n      _beneficiaryPk,\n      _duration,\n      _unlockAt,\n      _extra\n    );\n\n    bytes32 rawHash = keccak256(_message);\n    bytes32 finalHash = keccak256(abi.encode(address(t), rawHash, 0));\n    (uint8 v, bytes32 r, bytes32 s) = vm.sign(signerPk, finalHash);\n\n    bytes memory sig = abi.encodePacked(r, s, v, uint8(1), _signedByOwner ? bytes1(0x02) : bytes1(0x03));\n\n    vm.chainId(_useChainId);\n    assertEq(t.isValidSignature(rawHash, sig), bytes4(0x1626ba7e));\n    assertEq(t.isValidSignature(_message, sig), bytes4(0x20c13b0b));\n  }\n\n  function test_fail_isValidSignature_anyNetwork_wrongEncode(\n    uint256 _ownerPk,\n    uint256 _beneficiaryPk,\n    uint256 _duration,\n    uint256 _unlockAt,\n    uint256 _extra,\n    uint64 _useChainId,\n    bool _signedByOwner,\n    bytes calldata _message\n  ) external {\n    vm.assume(_useChainId != 0);\n\n    uint256 signerPk = boundPk(_signedByOwner ? _ownerPk : _beneficiaryPk);\n\n    Trust t = gen_and_unlock(\n      _ownerPk,\n      _beneficiaryPk,\n      _duration,\n      _unlockAt,\n      _extra\n    );\n\n    bytes32 rawHash = keccak256(_message);\n    bytes32 finalHash = keccak256(abi.encode(address(t), rawHash, 0));\n    bytes32 realHash = keccak256(abi.encode(address(t), rawHash, _useChainId));\n    (uint8 v, bytes32 r, bytes32 s) = vm.sign(signerPk, finalHash);\n\n    bytes memory sig = abi.encodePacked(r, s, v, uint8(1), _signedByOwner ? bytes1(0x00) : bytes1(0x01));\n\n    vm.chainId(_useChainId);\n\n    bytes memory revertErr = abi.encodeWithSignature(\n      'InvalidSignature(bytes32,bytes32,address,bytes)',\n      rawHash,\n      realHash,\n      vm.addr(signerPk),\n      abi.encodePacked(r, s, v, uint8(1))\n    );\n\n    vm.expectRevert(revertErr);\n    t.isValidSignature(rawHash, sig);\n\n    vm.expectRevert(revertErr);\n    t.isValidSignature(_message, sig);\n  }\n\n  function test_fail_isValidSignature_anyNetwork_onlyEncode(\n    uint256 _ownerPk,\n    uint256 _beneficiaryPk,\n    uint256 _duration,\n    uint256 _unlockAt,\n    uint256 _extra,\n    uint64 _useChainId,\n    bool _signedByOwner,\n    bytes calldata _message\n  ) external {\n    vm.assume(_useChainId != 0);\n\n    uint256 signerPk = boundPk(_signedByOwner ? _ownerPk : _beneficiaryPk);\n\n    Trust t = gen_and_unlock(\n      _ownerPk,\n      _beneficiaryPk,\n      _duration,\n      _unlockAt,\n      _extra\n    );\n\n    bytes32 rawHash = keccak256(_message);\n    bytes32 finalHash = keccak256(abi.encode(address(t), rawHash, _useChainId));\n    bytes32 realHash = keccak256(abi.encode(address(t), rawHash, 0));\n    (uint8 v, bytes32 r, bytes32 s) = vm.sign(signerPk, finalHash);\n\n    bytes memory sig = abi.encodePacked(r, s, v, uint8(1), _signedByOwner ? bytes1(0x02) : bytes1(0x03));\n\n    vm.chainId(_useChainId);\n\n    bytes memory revertErr = abi.encodeWithSignature(\n      'InvalidSignature(bytes32,bytes32,address,bytes)',\n      rawHash,\n      realHash,\n      vm.addr(signerPk),\n      abi.encodePacked(r, s, v, uint8(1))\n    );\n\n    vm.expectRevert(revertErr);\n    t.isValidSignature(rawHash, sig);\n\n    vm.expectRevert(revertErr);\n    t.isValidSignature(_message, sig);\n  }\n\n  function test_isValidSignature_pre_unlock_as_owner(\n    uint256 _ownerPk,\n    uint256 _beneficiaryPk,\n    uint256 _duration,\n    uint256 _unlockAt,\n    bytes calldata _message\n  ) external {\n    _duration = bound(_duration, 0, type(uint256).max - block.timestamp);\n    _unlockAt = bound(_unlockAt, block.timestamp + _duration, type(uint256).max);\n\n    Trust t = new Trust(vm.addr(boundPk(_ownerPk)), vm.addr(boundPk(_beneficiaryPk)), _duration);\n\n    vm.prank(vm.addr(boundPk(_beneficiaryPk)));\n    t.setUnlocksAt(_unlockAt);\n\n    bytes32 rawHash = keccak256(_message);\n    bytes32 finalHash = keccak256(abi.encode(address(t), rawHash, block.chainid));\n    (uint8 v, bytes32 r, bytes32 s) = vm.sign(boundPk(_ownerPk), finalHash);\n\n    bytes memory sig = abi.encodePacked(r, s, v, uint8(1), bytes1(0x00));\n\n    assertEq(t.isValidSignature(rawHash, sig), bytes4(0x1626ba7e));\n    assertEq(t.isValidSignature(_message, sig), bytes4(0x20c13b0b));\n  }\n\n  function test_fail_isValidSignature_emptySignature(\n    uint256 _ownerPk,\n    uint256 _beneficiaryPk,\n    uint256 _duration,\n    uint256 _unlockAt,\n    bytes calldata _message\n  ) external {\n    _duration = bound(_duration, 0, type(uint256).max - block.timestamp);\n    _unlockAt = bound(_unlockAt, block.timestamp + _duration, type(uint256).max);\n\n    Trust t = new Trust(vm.addr(boundPk(_ownerPk)), vm.addr(boundPk(_beneficiaryPk)), _duration);\n\n    vm.prank(vm.addr(boundPk(_beneficiaryPk)));\n    t.setUnlocksAt(_unlockAt);\n\n    bytes memory sig = new bytes(0);\n\n    vm.expectRevert(abi.encodeWithSignature('EmptySignature()'));\n    t.isValidSignature(_message, sig);\n\n    vm.expectRevert(abi.encodeWithSignature('EmptySignature()'));\n    t.isValidSignature(keccak256(_message), sig);\n  }\n\n  function test_fail_isValidSignature_wrongSignatureFlag(\n    uint256 _ownerPk,\n    uint256 _beneficiaryPk,\n    uint256 _duration,\n    uint256 _unlockAt,\n    uint8 _signerFlag,\n    bytes calldata _signature,\n    bytes calldata _message\n  ) external {\n    _duration = bound(_duration, 0, type(uint256).max - block.timestamp);\n    _unlockAt = bound(_unlockAt, block.timestamp + _duration, type(uint256).max);\n    _signerFlag = uint8(bound(_signerFlag, 4, type(uint8).max));\n\n    Trust t = new Trust(vm.addr(boundPk(_ownerPk)), vm.addr(boundPk(_beneficiaryPk)), _duration);\n\n    vm.prank(vm.addr(boundPk(_beneficiaryPk)));\n    t.setUnlocksAt(_unlockAt);\n\n    bytes memory sig = abi.encodePacked(_signature, _signerFlag);\n\n    vm.expectRevert(abi.encodeWithSignature('InvalidSignatureFlag(bytes,bytes1)', sig, bytes1(_signerFlag)));\n    t.isValidSignature(_message, sig);\n\n    vm.expectRevert(abi.encodeWithSignature('InvalidSignatureFlag(bytes,bytes1)', sig, bytes1(_signerFlag)));\n    t.isValidSignature(keccak256(_message), sig);\n  }\n\n  function test_fail_isValidSignature_pre_unlock_as_beneficiary(\n    uint256 _ownerPk,\n    uint256 _beneficiaryPk,\n    uint256 _duration,\n    uint256 _unlockAt,\n    bytes calldata _message\n  ) external {\n    _duration = bound(_duration, 1, type(uint256).max - block.timestamp);\n    _unlockAt = bound(_unlockAt, block.timestamp + _duration, type(uint256).max);\n\n    vm.assume(boundPk(_ownerPk) != boundPk(_beneficiaryPk));\n    Trust t = new Trust(vm.addr(boundPk(_ownerPk)), vm.addr(boundPk(_beneficiaryPk)), _duration);\n\n    vm.prank(vm.addr(boundPk(_beneficiaryPk)));\n    t.setUnlocksAt(_unlockAt);\n\n    bytes32 rawHash = keccak256(_message);\n    bytes32 finalHash = keccak256(abi.encode(address(t), rawHash, block.chainid));\n    (uint8 v, bytes32 r, bytes32 s) = vm.sign(boundPk(_beneficiaryPk), finalHash);\n\n    bytes memory sig = abi.encodePacked(r, s, v, uint8(1), bytes1(0x01));\n\n    vm.expectRevert(abi.encodeWithSignature('NotUnlocked(uint256)', _unlockAt));\n    t.isValidSignature(rawHash, sig);\n\n    vm.expectRevert(abi.encodeWithSignature('NotUnlocked(uint256)', _unlockAt));\n    t.isValidSignature(_message, sig);\n  }\n\n  struct MemoryStruct1 {\n    bytes32 rawHash;\n    bytes32 finalHash;\n    uint8 v;\n    bytes32 r;\n    bytes32 s;\n  }\n\n  function test_fail_isValidSignature_invalid_signature(\n    uint256 _ownerPk,\n    uint256 _beneficiaryPk,\n    uint256 _badSignerPk,\n    bool _signsOwner,\n    uint256 _duration,\n    uint256 _unlockAt,\n    bytes calldata _message\n  ) external {\n    _ownerPk = boundPk(_ownerPk);\n    _beneficiaryPk = boundPk(_beneficiaryPk);\n    _badSignerPk = boundPk(_badSignerPk);\n\n    address expectedSigner = _signsOwner ? vm.addr(_ownerPk) : vm.addr(_beneficiaryPk);\n\n    _duration = bound(_duration, 0, type(uint256).max - block.timestamp);\n    _unlockAt = bound(_unlockAt, block.timestamp + _duration, type(uint256).max);\n\n    trust = new Trust(vm.addr(_ownerPk), vm.addr(_beneficiaryPk), _duration);\n\n    vm.prank(vm.addr(_beneficiaryPk));\n    trust.setUnlocksAt(_unlockAt);\n\n    if (_signsOwner) {\n      vm.assume(_ownerPk != _badSignerPk);\n    } else {\n      vm.assume(_beneficiaryPk != _badSignerPk);\n      // Advance clock to unlock\n      vm.warp(_unlockAt); \n    }\n\n    MemoryStruct1 memory m;\n    {\n      // Stack too deep manual workaround\n      bytes32 rawHash = keccak256(_message);\n      bytes32 finalHash = keccak256(abi.encode(address(trust), rawHash, block.chainid));\n      (uint8 v, bytes32 r, bytes32 s) = vm.sign(_badSignerPk, finalHash);\n      m.rawHash = rawHash;\n      m.finalHash = finalHash;\n      m.v = v;\n      m.r = r;\n      m.s = s;\n    }\n\n    bytes memory sig = abi.encodePacked(m.r, m.s, m.v, uint8(1), _signsOwner ? bytes1(0x00) : bytes1(0x01));\n\n    bytes memory revertErr = abi.encodeWithSignature(\n      'InvalidSignature(bytes32,bytes32,address,bytes)',\n      m.rawHash,\n      m.finalHash,\n      expectedSigner,\n      abi.encodePacked(m.r, m.s, m.v, uint8(1))\n    );\n\n    vm.expectRevert(revertErr);\n    trust.isValidSignature(m.rawHash, sig);\n\n    vm.expectRevert(revertErr);\n    trust.isValidSignature(_message, sig);\n  }\n\n  function test_accept_contract_signature_for_owner(\n    address _beneficiaryAddress,\n    uint256 _duration,\n    uint256 _unlockAt,\n    uint256 _elapsed,\n    bytes calldata _signature,\n    bytes calldata _message\n  ) external {\n    MockContractSigner mcs = new MockContractSigner();\n\n    _duration = bound(_duration, 0, type(uint256).max - block.timestamp);\n    _unlockAt = bound(_unlockAt, block.timestamp + _duration, type(uint256).max);\n    _elapsed = bound(_elapsed, 0, type(uint256).max - block.timestamp);\n\n    trust = new Trust(address(mcs), _beneficiaryAddress, _duration);\n\n    vm.prank(_beneficiaryAddress);\n    trust.setUnlocksAt(_unlockAt);\n\n    vm.warp(block.timestamp + _elapsed);\n\n    bytes32 rawHash = keccak256(_message);\n    bytes32 finalHash = keccak256(abi.encode(address(trust), rawHash, block.chainid));\n    mcs.setSignature(finalHash, _signature, bytes4(0x1626ba7e));\n    bytes memory sig = abi.encodePacked(_signature, uint8(3), bytes1(0x00));\n\n    assertEq(trust.isValidSignature(rawHash, sig), bytes4(0x1626ba7e));\n    assertEq(trust.isValidSignature(_message, sig), bytes4(0x20c13b0b));\n  }\n\n  function test_fail_bad_contract_signature_for_owner(\n    address _beneficiaryAddress,\n    uint256 _duration,\n    uint256 _unlockAt,\n    uint256 _elapsed,\n    bytes4 _badReturnBytes,\n    bytes calldata _badSignature,\n    bytes calldata _message\n  ) external {\n    vm.assume(_badReturnBytes != bytes4(0x1626ba7e));\n\n    MockContractSigner mcs = new MockContractSigner();\n\n    _duration = bound(_duration, 0, type(uint256).max - block.timestamp);\n    _unlockAt = bound(_unlockAt, block.timestamp + _duration, type(uint256).max);\n    _elapsed = bound(_elapsed, 0, type(uint256).max - block.timestamp);\n\n    trust = new Trust(address(mcs), _beneficiaryAddress, _duration);\n\n    vm.prank(_beneficiaryAddress);\n    trust.setUnlocksAt(_unlockAt);\n\n    vm.warp(block.timestamp + _elapsed);\n\n    bytes32 rawHash = keccak256(_message);\n    bytes32 finalHash = keccak256(abi.encode(address(trust), rawHash, block.chainid));\n    mcs.setSignature(finalHash, _badSignature, _badReturnBytes);\n    bytes memory sig = abi.encodePacked(_badSignature, uint8(3), bytes1(0x00));\n\n    bytes memory revertErr = abi.encodeWithSignature(\n      'InvalidSignature(bytes32,bytes32,address,bytes)',\n      rawHash,\n      finalHash,\n      address(mcs),\n      abi.encodePacked(_badSignature, uint8(3))\n    );\n\n    vm.expectRevert(revertErr);\n    trust.isValidSignature(rawHash, sig);\n\n    vm.expectRevert(revertErr);\n    trust.isValidSignature(_message, sig);\n  }\n\n  function test_accept_contract_signature_for_beneficiary(\n    address _ownerAddress,\n    uint256 _duration,\n    uint256 _unlockAt,\n    uint256 _extra,\n    bytes calldata _signature,\n    bytes calldata _message\n  ) external {\n    MockContractSigner mcs = new MockContractSigner();\n\n    _duration = bound(_duration, 0, (type(uint256).max - 1) - block.timestamp);\n    _unlockAt = bound(_unlockAt, block.timestamp + _duration, type(uint256).max - 1);\n    _extra    = bound(_extra, 0, type(uint256).max - _unlockAt);\n\n    trust = new Trust(_ownerAddress, address(mcs), _duration);\n\n    vm.prank(address(mcs));\n    trust.setUnlocksAt(_unlockAt);\n\n    vm.warp(_unlockAt + _extra);\n\n    bytes32 rawHash = keccak256(_message);\n    bytes32 finalHash = keccak256(abi.encode(address(trust), rawHash, block.chainid));\n    mcs.setSignature(finalHash, _signature, bytes4(0x1626ba7e));\n    bytes memory sig = abi.encodePacked(_signature, uint8(3), bytes1(0x01));\n\n    assertEq(trust.isValidSignature(rawHash, sig), bytes4(0x1626ba7e));\n    assertEq(trust.isValidSignature(_message, sig), bytes4(0x20c13b0b));\n  }\n\n  function test_fail_bad_contract_signature_for_beneficiary(\n    address _ownerAddress,\n    uint256 _duration,\n    uint256 _unlockAt,\n    uint256 _extra,\n    bytes4 _badReturnBytes,\n    bytes calldata _badSignature,\n    bytes calldata _message\n  ) external {\n    vm.assume(_badReturnBytes != bytes4(0x1626ba7e));\n\n    MockContractSigner mcs = new MockContractSigner();\n\n    _duration = bound(_duration, 0, (type(uint256).max - 1) - block.timestamp);\n    _unlockAt = bound(_unlockAt, block.timestamp + _duration, type(uint256).max - 1);\n    _extra    = bound(_extra, 0, type(uint256).max - _unlockAt);\n\n    trust = new Trust(_ownerAddress, address(mcs), _duration);\n\n    vm.prank(address(mcs));\n    trust.setUnlocksAt(_unlockAt);\n\n    vm.warp(_unlockAt + _extra);\n\n    bytes32 rawHash = keccak256(_message);\n    bytes32 finalHash = keccak256(abi.encode(address(trust), rawHash, block.chainid));\n    mcs.setSignature(finalHash, _badSignature, _badReturnBytes);\n    bytes memory sig = abi.encodePacked(_badSignature, uint8(3), bytes1(0x01));\n\n    bytes memory revertErr = abi.encodeWithSignature(\n      'InvalidSignature(bytes32,bytes32,address,bytes)',\n      rawHash,\n      finalHash,\n      address(mcs),\n      abi.encodePacked(_badSignature, uint8(3))\n    );\n\n    vm.expectRevert(revertErr);\n    trust.isValidSignature(rawHash, sig);\n\n    vm.expectRevert(revertErr);\n    trust.isValidSignature(_message, sig);\n  }\n\n  function test_fail_revert_contract_signer_owner(\n    address _beneficiaryAddress,\n    uint256 _duration,\n    uint256 _unlockAt,\n    uint256 _extra,\n    bytes calldata _revertErr,\n    bytes calldata _signature,\n    bytes calldata _message\n  ) external {\n    MockContractSigner mcs = new MockContractSigner();\n\n    _duration = bound(_duration, 0, type(uint256).max - block.timestamp);\n    _unlockAt = bound(_unlockAt, block.timestamp + _duration, type(uint256).max);\n    _extra    = bound(_extra, 0, type(uint256).max - _unlockAt);\n\n    trust = new Trust(address(mcs), _beneficiaryAddress, _duration);\n\n    vm.prank(_beneficiaryAddress);\n    trust.setUnlocksAt(_unlockAt);\n\n    vm.warp(block.timestamp + _extra);\n\n    bytes32 rawHash = keccak256(_message);\n    mcs.setRevertErr(true, _revertErr);\n    bytes memory sig = abi.encodePacked(_signature, uint8(3), bytes1(0x00));\n\n\n    vm.expectRevert(_revertErr);\n    trust.isValidSignature(rawHash, sig);\n\n    vm.expectRevert(_revertErr);\n    trust.isValidSignature(_message, sig);\n  }\n\n  function test_fail_revert_contract_signer_beneficiary(\n    address _ownerAddress,\n    uint256 _duration,\n    uint256 _unlockAt,\n    uint256 _extra,\n    bytes calldata _revertErr,\n    bytes calldata _signature,\n    bytes calldata _message\n  ) external {\n    MockContractSigner mcs = new MockContractSigner();\n\n    _duration = bound(_duration, 0, (type(uint256).max - 1) - block.timestamp);\n    _unlockAt = bound(_unlockAt, block.timestamp + _duration, type(uint256).max - 1);\n    _extra    = bound(_extra, 0, type(uint256).max - _unlockAt);\n\n    trust = new Trust(_ownerAddress, address(mcs), _duration);\n\n    vm.prank(address(mcs));\n    trust.setUnlocksAt(_unlockAt);\n\n    vm.warp(_unlockAt + _extra);\n\n    bytes32 rawHash = keccak256(_message);\n    mcs.setRevertErr(true, _revertErr);\n    bytes memory sig = abi.encodePacked(_signature, uint8(3), bytes1(0x01));\n\n    vm.expectRevert(_revertErr);\n    trust.isValidSignature(rawHash, sig);\n\n    vm.expectRevert(_revertErr);\n    trust.isValidSignature(_message, sig);\n  }\n}\n"
  },
  {
    "path": "foundry_test/trust/TrustFactory.t.sol",
    "content": "// SPDX-License-Identifier: Apache-2.0\npragma solidity 0.8.18;\n\nimport \"contracts/trust/TrustFactory.sol\";\n\nimport \"foundry_test/base/AdvTest.sol\";\n\ncontract TrustFactoryTest is AdvTest {\n  TrustFactory private factory;\n\n  function setUp() external {\n    factory = new TrustFactory();\n  }\n\n  function test_create_trust(address _owner, address _beneficiary, uint256 _duration) external {\n    Trust trust = factory.deploy(_owner, _beneficiary, _duration);\n    address trustAddress = address(trust);\n\n    assertEq(trust.owner(), _owner);\n    assertEq(trust.beneficiary(), _beneficiary);\n    assertEq(trust.duration(), _duration);\n\n    uint256 codeSize; assembly { codeSize := extcodesize(trustAddress) }\n    assertGt(codeSize, 0);\n  }\n\n  function test_predict_address(address _owner, address _beneficiary, uint256 _duration) external {\n    address expected = factory.addressOf(_owner, _beneficiary, _duration);\n    address actual = address(factory.deploy(_owner, _beneficiary, _duration));\n    assertEq(actual, expected);\n  }\n\n  function test_fail_deploy_twice(address _owner, address _beneficiary, uint256 _duration) external {\n    factory.deploy(_owner, _beneficiary, _duration);\n    vm.expectRevert();\n    factory.deploy(_owner, _beneficiary, _duration);\n  }\n\n  function test_fail_deploy_low_gas(address _owner, address _beneficiary, uint256 _duration, uint256 _gas) external {\n    _gas = bound(_gas, 21000, block.gaslimit);\n    try factory.deploy{ gas: _gas }(_owner, _beneficiary, _duration) returns (Trust trust) {\n      address trustAddress = address(trust);\n      // The address should have code, and never be the zero address\n      assertNotEq(trustAddress, address(0));\n      uint256 codeSize; assembly { codeSize := extcodesize(trustAddress) }\n      assertGt(codeSize, 0);\n    } catch {\n      // Ignore errors from low gas\n    }\n  }\n}\n"
  },
  {
    "path": "foundry_test/utils/LibAddress.t.sol",
    "content": "// SPDX-License-Identifier: Apache-2.0\npragma solidity 0.8.18;\n\nimport \"contracts/utils/LibAddress.sol\";\n\nimport \"foundry_test/base/AdvTest.sol\";\n\n\ncontract LibAddressTest is AdvTest {\n  function test_isContract(address _addr, bytes calldata _code) external {\n    boundNoSys(_addr);\n\n    vm.etch(_addr, _code);\n    assertEq(LibAddress.isContract(_addr), _code.length > 0);\n  }\n}\n"
  },
  {
    "path": "foundry_test/utils/LibBytes.t.sol",
    "content": "// SPDX-License-Identifier: Apache-2.0\npragma solidity 0.8.18;\n\nimport \"contracts/utils/LibBytes.sol\";\n\nimport \"foundry_test/base/AdvTest.sol\";\n\n\ncontract LibBytesImp {\n  using LibBytes for bytes;\n\n  function readBytes32(bytes calldata _data, uint256 _index) external pure returns (bytes32) {\n    return _data.readBytes32(_index);\n  }\n\n  function readUint8(bytes calldata _data, uint256 _index) external pure returns (uint8) {\n    return _data.readUint8(_index);\n  }\n\n  function readFirstUint16(bytes calldata _data) external pure returns (uint16) {\n    return _data.readFirstUint16();\n  }\n\n  function readUint32(bytes calldata _data, uint256 _index) external pure returns (uint32) {\n    return _data.readUint32(_index);\n  }\n}\n\ncontract LibBytesTest is AdvTest {\n  LibBytesImp private lib;\n\n  function setUp() external {\n    lib = new LibBytesImp();\n  }\n\n  function test_readBytes32(bytes calldata _prefix, bytes32 _data, bytes calldata _sufix) external {\n    bytes memory combined = abi.encodePacked(_prefix, _data, _sufix);\n    bytes32 actual = lib.readBytes32(combined, _prefix.length);\n    assertEq(actual, _data);\n  }\n\n  function test_readBytes32_OutOfBounds(bytes calldata _data, uint256 _index) external view {\n    lib.readBytes32(_data, _index);\n  }\n\n  function test_readBytes32_Fuzz_AbiDecode(bytes calldata _data, uint256 _index) external {\n    _index = bound(_index, 0, _data.length > 32 ? _data.length - 32 : 0);\n    bytes32 expected = abi.decode(abi.encodePacked(_data[_index:], bytes32(0)), (bytes32));\n    bytes32 actual = lib.readBytes32(_data, _index);\n    assertEq(expected, actual);\n  }\n\n  function test_readUint8(bytes calldata _prefix, uint8 _data, bytes calldata _sufix) external {\n    bytes memory combined = abi.encodePacked(_prefix, _data, _sufix);\n    uint8 expected = lib.readUint8(combined, _prefix.length);\n    assertEq(expected, _data);\n  }\n\n  function test_readUint8_OutOfBounds(bytes calldata _data, uint256 _index) external view {\n    lib.readUint8(_data, _index);\n  }\n\n  function test_readUint8_Fuzz_ReadByte(bytes calldata _data, uint256 _index) external {\n    vm.assume(_data.length >= 1);\n\n    _index = bound(_index, 0, _data.length - 1);\n    uint8 expected = uint8(uint256(bytes32(_data[_index])) >> 248);\n    uint8 actual = lib.readUint8(_data, _index);\n\n    assertEq(expected, actual);\n  }\n\n  function test_readFirstUint16(uint16 _data, bytes calldata _sufix) external {\n    bytes memory combined = abi.encodePacked(_data, _sufix);\n    uint16 expected = lib.readFirstUint16(combined);\n    assertEq(expected, _data);\n  }\n\n  function test_readFirstUint16_OutOfBounds(uint8 _data) external {\n    bytes memory encoded = abi.encodePacked(_data);\n\n    uint16 actual = lib.readFirstUint16(bytes(''));\n    assertEq(actual, uint16(0));\n\n    actual = lib.readFirstUint16(encoded);\n    assertEq(actual, uint256(_data) << 8);\n  }\n\n  function test_readFirstUint16_Fuzz_AbiDecode(bytes calldata _data) external {\n    uint256 expected = abi.decode(abi.encodePacked(_data, bytes32(0)), (uint256));\n    uint16 actual = lib.readFirstUint16(_data);\n\n    assertEq(expected >> 240, actual);\n  }\n\n  function test_readUint32(bytes calldata _prefix, uint32 _data, bytes calldata _sufix) external {\n    bytes memory combined = abi.encodePacked(_prefix, _data, _sufix);\n    uint32 expected = lib.readUint32(combined, _prefix.length);\n    assertEq(expected, _data);\n  }\n}\n"
  },
  {
    "path": "foundry_test/utils/LibBytesPointer.t.sol",
    "content": "// SPDX-License-Identifier: Apache-2.0\npragma solidity 0.8.18;\n\nimport \"contracts/utils/LibBytes.sol\";\nimport \"contracts/utils/LibBytesPointer.sol\";\n\nimport \"foundry_test/base/AdvTest.sol\";\n\n\ncontract LibBytesPointerImp {\n  using LibBytesPointer for bytes;\n\n  function readFirstUint16(bytes calldata _data) external pure returns (uint16, uint256) {\n    return _data.readFirstUint16();\n  }\n\n  function readUint8(bytes calldata _data, uint256 _index) external pure returns (uint8, uint256) {\n    return _data.readUint8(_index);\n  }\n\n  function readUint8Address(bytes calldata _data, uint256 _index) external pure returns (uint8, address, uint256) {\n    return _data.readUint8Address(_index);\n  }\n\n  function readUint16(bytes calldata _data, uint256 _index) external pure returns (uint16, uint256) {\n    return _data.readUint16(_index);\n  }\n\n  function readUint24(bytes calldata _data, uint256 _index) external pure returns (uint24, uint256) {\n    return _data.readUint24(_index);\n  }\n\n  function readUint64(bytes calldata _data, uint256 _index) external pure returns (uint64, uint256) {\n    return _data.readUint64(_index);\n  }\n\n  function readBytes32(bytes calldata _data, uint256 _index) external pure returns (bytes32, uint256) {\n    return _data.readBytes32(_index);\n  }\n}\n\ncontract LibBytesPointerTest is AdvTest {\n  using LibBytes for bytes;\n\n  LibBytesPointerImp private lib;\n\n  function setUp() public {\n    lib = new LibBytesPointerImp();\n  }\n\n  function test_readFirstUint16_Fuzz_LibBytes(bytes calldata _data) external {\n    uint16 expected = _data.readFirstUint16();\n    (uint16 actual, uint256 index) = lib.readFirstUint16(_data);\n    assertEq(actual, expected);\n    assertEq(index, 2);\n  }\n\n  function test_readUint8_Fuzz_LibBytes(bytes calldata _data, uint256 _pointer) external {\n    uint8 expected = _data.readUint8(_pointer);\n    (uint8 actual, uint256 newPointer) = lib.readUint8(_data, _pointer);\n    assertEq(actual, expected);\n    unchecked { assertEq(newPointer, _pointer + 1); }\n  }\n\n  function test_readUint8Address(bytes calldata _prefix, uint8 _data1, address _data2, bytes calldata _sufix) external {\n    bytes memory combined = abi.encodePacked(_prefix, _data1, _data2, _sufix);\n    (uint8 actual1, address actual2, uint256 newPointer) = lib.readUint8Address(combined, _prefix.length);\n    assertEq(actual1, _data1);\n    assertEq(actual2, _data2);\n    assertEq(newPointer, _prefix.length + 21);\n  }\n\n  function test_readUint8Address_OutOfBounds(bytes calldata _data, uint256 _pointer) external {\n    (,, uint256 newPointer) = lib.readUint8Address(_data, _pointer);\n    unchecked { assertEq(newPointer, _pointer + 21); }\n  }\n\n  function test_readUint16_Fuzz_ReadFirstUint16(bytes calldata _data, uint256 _pointer) external {\n    vm.assume(_data.length >= 16);\n\n    _pointer = bound(_pointer, 0, _data.length - 16);\n    (uint16 expected,) = lib.readFirstUint16(_data[_pointer:]);\n    (uint16 actual, uint256 newPointer) = lib.readUint16(_data, _pointer);\n    assertEq(actual, expected);\n    unchecked { assertEq(newPointer, _pointer + 2); }\n  }\n\n  function test_readUint16_OutOfBounds(bytes calldata _data, uint256 _pointer) external {\n    (, uint256 newPointer) = lib.readUint16(_data, _pointer);\n    unchecked { assertEq(newPointer, _pointer + 2); }\n  }\n\n  function test_readUint24(bytes calldata _prefix, uint24 _data, bytes calldata _sufix) external {\n    bytes memory combined = abi.encodePacked(_prefix, _data, _sufix);\n    (uint256 actual, uint256 newPointer) = lib.readUint24(combined, _prefix.length);\n    assertEq(actual, _data);\n    assertEq(newPointer, _prefix.length + 3);\n  }\n\n  function test_readUint24_OutOfBounds(bytes calldata _data, uint256 _pointer) external {\n    (, uint256 newPointer) = lib.readUint24(_data, _pointer);\n    unchecked { assertEq(newPointer, _pointer + 3); }\n  }\n\n  function test_readUint64(bytes calldata _prefix, uint64 _data, bytes calldata _sufix) external {\n    bytes memory combined = abi.encodePacked(_prefix, _data, _sufix);\n    (uint64 actual, uint256 newPointer) = lib.readUint64(combined, _prefix.length);\n    assertEq(actual, _data);\n    assertEq(newPointer, _prefix.length + 8);\n  }\n\n  function test_readUint64_OutOfBounds(bytes calldata _data, uint256 _pointer) external {\n    (, uint256 newPointer) = lib.readUint64(_data, _pointer);\n    unchecked { assertEq(newPointer, _pointer + 8); }\n  }\n\n  function test_readBytes32(bytes calldata _prefix, bytes32 _data, bytes calldata _sufix) external {\n    bytes memory combined = abi.encodePacked(_prefix, _data, _sufix);\n    (bytes32 actual, uint256 newPointer) = lib.readBytes32(combined, _prefix.length);\n    assertEq(actual, _data);\n    assertEq(newPointer, _prefix.length + 32);\n  }\n\n  function test_readBytes32_OutOfBounds(bytes calldata _data, uint256 _pointer) external {\n    (, uint256 newPointer) = lib.readBytes32(_data, _pointer);\n    unchecked { assertEq(newPointer, _pointer + 32); }\n  }\n\n  function test_readBytes32_Fuzz_LibBytes(bytes calldata _data, uint256 _index) external {\n    bytes32 expected = _data.readBytes32(_index);\n    (bytes32 actual, uint256 index) = lib.readBytes32(_data, _index);\n    assertEq(actual, expected);\n    unchecked { assertEq(index, _index + 32); }\n  }\n}\n"
  },
  {
    "path": "foundry_test/utils/LibOptim.t.sol",
    "content": "// SPDX-License-Identifier: Apache-2.0\npragma solidity 0.8.18;\n\nimport \"contracts/utils/LibOptim.sol\";\n\nimport \"foundry_test/base/AdvTest.sol\";\n\ncontract WillReturn {\n  bytes private r;\n\n  constructor (bytes memory _r) {\n    r = _r;\n  }\n\n  fallback() external {\n    bytes memory res = r;\n    assembly {\n      return(add(res, 32), mload(res))\n    }\n  }\n}\n\ncontract LibOptimTest is AdvTest {\n  function test_fkeccak256_Bytes32_Bytes32_Fuzz(bytes32 _a, bytes32 _b) external {\n    bytes32 expected = keccak256(abi.encodePacked(_a, _b));\n    bytes32 actual = LibOptim.fkeccak256(_a, _b);\n    assertEq(expected, actual);\n  }\n\n  function test_returnData_Fuzz(bytes memory _data) external {\n    WillReturn r = new WillReturn(_data);\n\n    (bool suc, bytes memory res1) = address(r).call(bytes(''));\n    assertEq(suc, true);\n    assertEq(res1, _data);\n\n    uint256 pointer1; assembly { pointer1 := mload(0x40) }\n    assertTrue(pointer1 != 0);\n\n    bytes memory optres = LibOptim.returnData();\n    assertEq(res1, optres);\n\n    uint256 pointer2; assembly { pointer2 := mload(0x40) }\n    assertEq(pointer2 - pointer1, res1.length + 32);\n\n    uint256 positionArr; assembly { positionArr := optres }\n    assertEq(positionArr, pointer1);\n  }\n\n  function test_call(\n    address _to,\n    uint256 _val,\n    bytes calldata _data\n  ) external {\n    _to = boundNoSys(_to);\n    _to = boundDiff(_to, address(0x004e59b44847b379578588920ca78fbf26c0b4956c));\n\n    vm.expectCall(_to, _data);\n    vm.deal(_to, 0);\n    vm.deal(address(this), _val);\n    LibOptim.call(_to, _val, gasleft(), _data);\n    assertEq(_to.balance, _val);\n  }\n}\n"
  },
  {
    "path": "foundry_test/utils/SignatureValidator.t.sol",
    "content": "// SPDX-License-Identifier: Apache-2.0\npragma solidity 0.8.18;\n\nimport \"contracts/utils/SignatureValidator.sol\";\n\nimport \"foundry_test/base/AdvTest.sol\";\n\ncontract SignatureValidatorImp {\n  function recoverSigner(bytes32 _hash, bytes calldata _signature) external pure returns (address) {\n    return SignatureValidator.recoverSigner(_hash, _signature);\n  }\n\n  function isValidSignature(bytes32 _hash, address _signer, bytes calldata _signature) external view returns (bool) {\n    return SignatureValidator.isValidSignature(_hash, _signer, _signature);\n  }\n}\n\ncontract SignatureValidatorTest is AdvTest {\n  SignatureValidatorImp private lib;\n\n  uint8 private constant SIG_TYPE_EIP712 = 1;\n  uint8 private constant SIG_TYPE_ETH_SIGN = 2;\n  uint8 private constant SIG_TYPE_WALLET_BYTES32 = 3;\n\n  function setUp() public {\n    lib = new SignatureValidatorImp();\n  }\n\n  function test_recoverSigner_EIP712(uint256 _pk, bytes32 _hash) external {\n    _pk = boundPk(_pk);\n\n    address signer = vm.addr(_pk);\n    (uint8 v, bytes32 r, bytes32 s) = vm.sign(_pk, _hash);\n\n    address recovered = lib.recoverSigner(_hash, abi.encodePacked(r, s, v, SIG_TYPE_EIP712));\n    assertEq(signer, recovered);\n  }\n\n  function test_recoverSigner_ETHSIGN(uint256 _pk, bytes32 _hash) external {\n    _pk = boundPk(_pk);\n\n    address signer = vm.addr(_pk);\n    (uint8 v, bytes32 r, bytes32 s) = vm.sign(_pk, keccak256(abi.encodePacked(\"\\x19Ethereum Signed Message:\\n32\", _hash)));\n\n    address recovered = lib.recoverSigner(_hash, abi.encodePacked(r, s, v, SIG_TYPE_ETH_SIGN));\n    assertEq(signer, recovered);\n  }\n\n  function test_recoverSigner_fail_InvalidSValue(bytes32 _hash, bytes32 _r, uint256 _s, uint8 _v, uint8 _type) external {\n    _s = bound(_s, 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A1, type(uint256).max);\n\n    bytes memory signature = abi.encodePacked(_r, _s, _v, _type);\n    vm.expectRevert(abi.encodeWithSignature('InvalidSValue(bytes,bytes32)', signature, _s));\n    lib.recoverSigner(_hash, signature);\n  }\n\n  function test_recoverSigner_fail_InvalidVValue(bytes32 _hash, bytes32 _r, uint256 _s, uint8 _v, uint8 _type) external {\n    _v = uint8(boundDiff(_v, 27, 28));\n\n    _s = bound(_s, 0, 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0);\n    bytes memory signature = abi.encodePacked(_r, _s, _v, _type);\n    vm.expectRevert(abi.encodeWithSignature('InvalidVValue(bytes,uint256)', signature, _v));\n    lib.recoverSigner(_hash, signature);\n  }\n\n  function test_recoverSigner_fail_InvalidLength(bytes32 _hash, bytes calldata _signature) external {\n    vm.assume(_signature.length != 66);\n\n    vm.expectRevert(abi.encodeWithSignature('InvalidSignatureLength(bytes)', _signature));\n    lib.recoverSigner(_hash, _signature);\n  }\n\n  function test_recoverSigner_fail_UnsupportedSignatureType(bytes32 _hash, bytes32 _r, uint256 _s, uint8 _v, uint8 _type) external {\n    _type = uint8(boundDiff(_type, SIG_TYPE_EIP712, SIG_TYPE_ETH_SIGN));\n\n    _s = bound(_s, 0, 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0);\n    _v = uint8(bound(_v, 27, 28));\n\n    bytes memory signature = abi.encodePacked(_r, _s, _v, _type);\n    vm.expectRevert(abi.encodeWithSignature('UnsupportedSignatureType(bytes,uint256,bool)', signature, _type, true));\n    lib.recoverSigner(_hash, signature);\n  }\n\n  function test_recoverSigner_fail_RecoverAddressZero(bytes32 _hash, bytes32 _r, uint256 _s, uint8 _v, uint8 _type) external {\n    _s = bound(_s, 0, 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0);\n    _v = uint8(bound(_v, 27, 28));\n    _type = uint8(bound(_type, SIG_TYPE_EIP712, SIG_TYPE_ETH_SIGN));\n\n    bytes memory signature = abi.encodePacked(_r, _s, _v, _type);\n\n    try lib.recoverSigner(_hash, signature) returns (address res) {\n      assertTrue(res != address(0));\n    } catch {}\n  }\n\n  function test_isValidSignature_EIP712(uint256 _pk, bytes32 _hash) external {\n    _pk = boundPk(_pk);\n\n    address signer = vm.addr(_pk);\n    (uint8 v, bytes32 r, bytes32 s) = vm.sign(_pk, _hash);\n\n    assertTrue(lib.isValidSignature(_hash, signer, abi.encodePacked(r, s, v, SIG_TYPE_EIP712)));\n  }\n\n  function test_isValidSignature_ETHSIGN(uint256 _pk, bytes32 _hash) external {\n    _pk = boundPk(_pk);\n\n    address signer = vm.addr(_pk);\n    (uint8 v, bytes32 r, bytes32 s) = vm.sign(_pk, keccak256(abi.encodePacked(\"\\x19Ethereum Signed Message:\\n32\", _hash)));\n\n    assertTrue(lib.isValidSignature(_hash, signer, abi.encodePacked(r, s, v, SIG_TYPE_ETH_SIGN)));\n  }\n\n  function test_isValidSignature_WALLET_BYTES32(address _wallet, bytes32 _hash, bytes calldata _signature) external {\n    _wallet = boundNoSys(_wallet);\n\n    bytes memory encodedSignature = abi.encodePacked(_signature, SIG_TYPE_WALLET_BYTES32);\n\n    bytes memory expectCall = abi.encodeWithSignature('isValidSignature(bytes32,bytes)', _hash, _signature);\n    bytes memory expectResult = abi.encode(bytes4(keccak256(\"isValidSignature(bytes32,bytes)\")));\n\n    vm.mockCall(_wallet, 0, expectCall, expectResult);\n    assertTrue(lib.isValidSignature(_hash, _wallet, encodedSignature));\n  }\n\n  function test_isValidSignature_Fail_WALLET_BYTES32_BadBytes4(address _wallet, bytes32 _hash, bytes calldata _signature, bytes4 _return) external {\n    vm.assume(bytes4(keccak256(\"isValidSignature(bytes32,bytes)\")) != _return);\n\n    _wallet = boundNoSys(_wallet);\n\n    bytes memory encodedSignature = abi.encodePacked(_signature, SIG_TYPE_WALLET_BYTES32);\n    bytes memory expectCall = abi.encodeWithSignature('isValidSignature(bytes32,bytes)', _hash, _signature);\n\n    vm.mockCall(_wallet, 0, expectCall, abi.encode(_return));\n    assertFalse(lib.isValidSignature(_hash, _wallet, encodedSignature));\n  }\n\n  function test_isValidSignature_Fail_WALLET_BYTES32_BadReturn(address _wallet, bytes32 _hash, bytes calldata _signature, bytes calldata _return) external {\n    bytes memory goodReturn = abi.encode(bytes4(keccak256(\"isValidSignature(bytes32,bytes)\")));\n    vm.assume(keccak256(goodReturn) != keccak256(_return));\n\n    bytes memory encodedSignature = abi.encodePacked(_signature, SIG_TYPE_WALLET_BYTES32);\n    bytes memory expectCall = abi.encodeWithSignature('isValidSignature(bytes32,bytes)', _hash, _signature);\n\n    bool retunedNotValid;\n    vm.mockCall(_wallet, 0, expectCall, abi.encode(_return));\n    try lib.isValidSignature(_hash, _wallet, encodedSignature) returns (bool isValid) {\n      retunedNotValid = !isValid;\n    } catch {\n      retunedNotValid = true;\n    }\n\n    assertTrue(retunedNotValid);\n  }\n\n  function test_isValidSignature_Fail_EmptySignature(bytes32 _hash, address _signer) external {\n    vm.expectRevert(abi.encodeWithSignature('EmptySignature()'));\n    lib.isValidSignature(_hash, _signer, bytes(''));\n  }\n\n  function test_isValidSignature_Fail_UnsupportedSignatureType(bytes32 _hash, address _signer, bytes calldata _signature, uint8 _type) external {\n    _type = uint8(boundDiff(_type, SIG_TYPE_EIP712, SIG_TYPE_ETH_SIGN, SIG_TYPE_WALLET_BYTES32));\n\n    bytes memory encodedSignature = abi.encodePacked(_signature, _type);\n    vm.expectRevert(abi.encodeWithSignature('UnsupportedSignatureType(bytes,uint256,bool)', encodedSignature, _type, false));\n    lib.isValidSignature(_hash, _signer, encodedSignature);\n  }\n}\n"
  },
  {
    "path": "funding.json",
    "content": "{\n  \"opRetro\": {\n    \"projectId\": \"0x62408999652f3bfa1be746d256bf5a4eb4719b993d40f07d2d60aaebee015018\"\n  }\n}\n"
  },
  {
    "path": "hardhat.config.ts",
    "content": "import { HardhatUserConfig, task } from 'hardhat/config'\nimport { networkConfig } from './utils/config-loader'\n\nimport '@nomicfoundation/hardhat-ethers'\nimport '@nomicfoundation/hardhat-verify'\nimport '@nomiclabs/hardhat-truffle5'\nimport '@nomiclabs/hardhat-web3'\nimport '@tenderly/hardhat-tenderly'\n\nimport 'hardhat-gas-reporter'\nimport 'solidity-coverage'\n\nimport './utils/benchmarker'\n\nconst ganacheNetwork = {\n  url: 'http://127.0.0.1:8545',\n  blockGasLimit: 6000000000\n}\n\nconst config: HardhatUserConfig = {\n  solidity: {\n    version: '0.8.18',\n    settings: {\n      optimizer: {\n        enabled: true,\n        runs: 500000\n      }\n    }\n  },\n  networks: {\n    mainnet: networkConfig('mainnet'),\n    ropsten: networkConfig('ropsten'),\n    kovan: networkConfig('kovan'),\n    goerli: networkConfig('goerli'),\n    polygon: networkConfig('polygon'),\n    polygonZkevm: networkConfig('polygon-zkevm'),\n    mumbai: networkConfig('mumbai'),\n    arbitrum: networkConfig('arbitrum'),\n    arbitrumGoerli: networkConfig('arbitrum-goerli'),\n    arbitrumNova: networkConfig('arbitrum-nova'),\n    optimism: networkConfig('optimism'),\n    bnb: networkConfig('bnb'),\n    bnbTestnet: networkConfig('bnb-testnet'),\n    gnosis: networkConfig('gnosis'),\n    avalanche: networkConfig('avalanche'),\n    avalancheFuji: networkConfig('avalanche-fuji'),\n    ganache: ganacheNetwork,\n    hardhat: {\n      blockGasLimit: 60000000\n    }\n  },\n  etherscan: {\n    // Your API key for Etherscan\n    // Obtain one at https://etherscan.io/\n    apiKey: networkConfig('mainnet').etherscan\n  },\n  mocha: {\n    timeout: process.env.COVERAGE ? 15 * 60 * 1000 : 30 * 1000\n  },\n  gasReporter: {\n    enabled: !!process.env.REPORT_GAS === true,\n    currency: 'USD',\n    gasPrice: 21,\n    showTimeSpent: true\n  },\n  tenderly: {\n    project: 'horizon/sequence-dev-1',\n    username: 'Agusx1211-horizon'\n  }\n}\n\nexport default config\n"
  },
  {
    "path": "networks/arbitrum.json",
    "content": "[\n  {\n    \"contractName\": \"WalletFactory\",\n    \"address\": \"0xFaA5c0b14d1bED5C888Ca655B9a8A5911F78eF4A\"\n  },\n  {\n    \"contractName\": \"MainModule\",\n    \"address\": \"0xfBf8f1A5E00034762D928f46d438B947f5d4065d\"\n  },\n  {\n    \"contractName\": \"MainModuleUpgradable\",\n    \"address\": \"0x4222dcA3974E39A8b41c411FeDDE9b09Ae14b911\"\n  },\n  {\n    \"contractName\": \"GuestModule\",\n    \"address\": \"0xfea230Ee243f88BC698dD8f1aE93F8301B6cdfaE\"\n  },\n  {\n    \"contractName\": \"SequenceUtils\",\n    \"address\": \"0xdbbFa3cB3B087B64F4ef5E3D20Dda2488AA244e6\"\n  }\n]"
  },
  {
    "path": "networks/arbitrumGoerli.json",
    "content": "[\n  {\n    \"contractName\": \"WalletFactory\",\n    \"address\": \"0xFaA5c0b14d1bED5C888Ca655B9a8A5911F78eF4A\"\n  },\n  {\n    \"contractName\": \"MainModule\",\n    \"address\": \"0xfBf8f1A5E00034762D928f46d438B947f5d4065d\"\n  },\n  {\n    \"contractName\": \"MainModuleUpgradable\",\n    \"address\": \"0x4222dcA3974E39A8b41c411FeDDE9b09Ae14b911\"\n  },\n  {\n    \"contractName\": \"GuestModule\",\n    \"address\": \"0xfea230Ee243f88BC698dD8f1aE93F8301B6cdfaE\"\n  },\n  {\n    \"contractName\": \"SequenceUtils\",\n    \"address\": \"0xdbbFa3cB3B087B64F4ef5E3D20Dda2488AA244e6\"\n  }\n]"
  },
  {
    "path": "networks/arbitrumNova.json",
    "content": "[\n  {\n    \"contractName\": \"WalletFactory\",\n    \"address\": \"0xFaA5c0b14d1bED5C888Ca655B9a8A5911F78eF4A\"\n  },\n  {\n    \"contractName\": \"MainModule\",\n    \"address\": \"0xfBf8f1A5E00034762D928f46d438B947f5d4065d\"\n  },\n  {\n    \"contractName\": \"MainModuleUpgradable\",\n    \"address\": \"0x4222dcA3974E39A8b41c411FeDDE9b09Ae14b911\"\n  },\n  {\n    \"contractName\": \"GuestModule\",\n    \"address\": \"0xfea230Ee243f88BC698dD8f1aE93F8301B6cdfaE\"\n  },\n  {\n    \"contractName\": \"SequenceUtils\",\n    \"address\": \"0xdbbFa3cB3B087B64F4ef5E3D20Dda2488AA244e6\"\n  }\n]"
  },
  {
    "path": "networks/avalanche.json",
    "content": "[\n  {\n    \"contractName\": \"WalletFactory\",\n    \"address\": \"0xFaA5c0b14d1bED5C888Ca655B9a8A5911F78eF4A\"\n  },\n  {\n    \"contractName\": \"MainModule\",\n    \"address\": \"0xfBf8f1A5E00034762D928f46d438B947f5d4065d\"\n  },\n  {\n    \"contractName\": \"MainModuleUpgradable\",\n    \"address\": \"0x4222dcA3974E39A8b41c411FeDDE9b09Ae14b911\"\n  },\n  {\n    \"contractName\": \"GuestModule\",\n    \"address\": \"0xfea230Ee243f88BC698dD8f1aE93F8301B6cdfaE\"\n  },\n  {\n    \"contractName\": \"SequenceUtils\",\n    \"address\": \"0xdbbFa3cB3B087B64F4ef5E3D20Dda2488AA244e6\"\n  }\n]"
  },
  {
    "path": "networks/avalancheFuji.json",
    "content": "[\n  {\n    \"contractName\": \"WalletFactory\",\n    \"address\": \"0xFaA5c0b14d1bED5C888Ca655B9a8A5911F78eF4A\"\n  },\n  {\n    \"contractName\": \"MainModule\",\n    \"address\": \"0xfBf8f1A5E00034762D928f46d438B947f5d4065d\"\n  },\n  {\n    \"contractName\": \"MainModuleUpgradable\",\n    \"address\": \"0x4222dcA3974E39A8b41c411FeDDE9b09Ae14b911\"\n  },\n  {\n    \"contractName\": \"GuestModule\",\n    \"address\": \"0xfea230Ee243f88BC698dD8f1aE93F8301B6cdfaE\"\n  },\n  {\n    \"contractName\": \"SequenceUtils\",\n    \"address\": \"0xdbbFa3cB3B087B64F4ef5E3D20Dda2488AA244e6\"\n  }\n]"
  },
  {
    "path": "networks/bnb.json",
    "content": "[\n  {\n    \"contractName\": \"WalletFactory\",\n    \"address\": \"0xFaA5c0b14d1bED5C888Ca655B9a8A5911F78eF4A\"\n  },\n  {\n    \"contractName\": \"MainModule\",\n    \"address\": \"0xfBf8f1A5E00034762D928f46d438B947f5d4065d\"\n  },\n  {\n    \"contractName\": \"MainModuleUpgradable\",\n    \"address\": \"0x4222dcA3974E39A8b41c411FeDDE9b09Ae14b911\"\n  },\n  {\n    \"contractName\": \"GuestModule\",\n    \"address\": \"0xfea230Ee243f88BC698dD8f1aE93F8301B6cdfaE\"\n  },\n  {\n    \"contractName\": \"SequenceUtils\",\n    \"address\": \"0xdbbFa3cB3B087B64F4ef5E3D20Dda2488AA244e6\"\n  }\n]"
  },
  {
    "path": "networks/bnbTestnet.json",
    "content": "[\n  {\n    \"contractName\": \"WalletFactory\",\n    \"address\": \"0xFaA5c0b14d1bED5C888Ca655B9a8A5911F78eF4A\"\n  },\n  {\n    \"contractName\": \"MainModule\",\n    \"address\": \"0xfBf8f1A5E00034762D928f46d438B947f5d4065d\"\n  },\n  {\n    \"contractName\": \"MainModuleUpgradable\",\n    \"address\": \"0x4222dcA3974E39A8b41c411FeDDE9b09Ae14b911\"\n  },\n  {\n    \"contractName\": \"GuestModule\",\n    \"address\": \"0xfea230Ee243f88BC698dD8f1aE93F8301B6cdfaE\"\n  },\n  {\n    \"contractName\": \"SequenceUtils\",\n    \"address\": \"0xdbbFa3cB3B087B64F4ef5E3D20Dda2488AA244e6\"\n  }\n]"
  },
  {
    "path": "networks/gnosis.json",
    "content": "[\n  {\n    \"contractName\": \"WalletFactory\",\n    \"address\": \"0xFaA5c0b14d1bED5C888Ca655B9a8A5911F78eF4A\"\n  },\n  {\n    \"contractName\": \"MainModule\",\n    \"address\": \"0xfBf8f1A5E00034762D928f46d438B947f5d4065d\"\n  },\n  {\n    \"contractName\": \"MainModuleUpgradable\",\n    \"address\": \"0x4222dcA3974E39A8b41c411FeDDE9b09Ae14b911\"\n  },\n  {\n    \"contractName\": \"GuestModule\",\n    \"address\": \"0xfea230Ee243f88BC698dD8f1aE93F8301B6cdfaE\"\n  },\n  {\n    \"contractName\": \"SequenceUtils\",\n    \"address\": \"0xdbbFa3cB3B087B64F4ef5E3D20Dda2488AA244e6\"\n  }\n]"
  },
  {
    "path": "networks/goerli.json",
    "content": "[\n  {\n    \"contractName\": \"WalletFactory\",\n    \"address\": \"0xFaA5c0b14d1bED5C888Ca655B9a8A5911F78eF4A\"\n  },\n  {\n    \"contractName\": \"MainModule\",\n    \"address\": \"0xfBf8f1A5E00034762D928f46d438B947f5d4065d\"\n  },\n  {\n    \"contractName\": \"MainModuleUpgradable\",\n    \"address\": \"0x4222dcA3974E39A8b41c411FeDDE9b09Ae14b911\"\n  },\n  {\n    \"contractName\": \"GuestModule\",\n    \"address\": \"0xfea230Ee243f88BC698dD8f1aE93F8301B6cdfaE\"\n  },\n  {\n    \"contractName\": \"SequenceUtils\",\n    \"address\": \"0xdbbFa3cB3B087B64F4ef5E3D20Dda2488AA244e6\"\n  }\n]"
  },
  {
    "path": "networks/hardhat.json",
    "content": "[\n  {\n    \"contractName\": \"WalletFactory\",\n    \"address\": \"0xFaA5c0b14d1bED5C888Ca655B9a8A5911F78eF4A\"\n  },\n  {\n    \"contractName\": \"MainModule\",\n    \"address\": \"0xE1846F966D116B86d65481Fe81886219D698b60D\"\n  },\n  {\n    \"contractName\": \"MainModuleUpgradable\",\n    \"address\": \"0xB39E1ed61caC9E8eE8CDD3218765cF6a7fE390db\"\n  },\n  {\n    \"contractName\": \"GuestModule\",\n    \"address\": \"0x28Ec0675C7b40ed78EBcBBbDF39e5652c0787B18\"\n  },\n  {\n    \"contractName\": \"SequenceUtils\",\n    \"address\": \"0x4817Fe9f2352E88991A7c84E4385708Ccff05704\"\n  }\n]"
  },
  {
    "path": "networks/mainnet.json",
    "content": "[\n  {\n    \"contractName\": \"WalletFactory\",\n    \"address\": \"0xFaA5c0b14d1bED5C888Ca655B9a8A5911F78eF4A\"\n  },\n  {\n    \"contractName\": \"MainModule\",\n    \"address\": \"0xfBf8f1A5E00034762D928f46d438B947f5d4065d\"\n  },\n  {\n    \"contractName\": \"MainModuleUpgradable\",\n    \"address\": \"0x4222dcA3974E39A8b41c411FeDDE9b09Ae14b911\"\n  },\n  {\n    \"contractName\": \"GuestModule\",\n    \"address\": \"0xfea230Ee243f88BC698dD8f1aE93F8301B6cdfaE\"\n  },\n  {\n    \"contractName\": \"SequenceUtils\",\n    \"address\": \"0xdbbFa3cB3B087B64F4ef5E3D20Dda2488AA244e6\"\n  }\n]"
  },
  {
    "path": "networks/mumbai.json",
    "content": "[\n  {\n    \"contractName\": \"WalletFactory\",\n    \"address\": \"0xFaA5c0b14d1bED5C888Ca655B9a8A5911F78eF4A\"\n  },\n  {\n    \"contractName\": \"MainModule\",\n    \"address\": \"0xfBf8f1A5E00034762D928f46d438B947f5d4065d\"\n  },\n  {\n    \"contractName\": \"MainModuleUpgradable\",\n    \"address\": \"0x4222dcA3974E39A8b41c411FeDDE9b09Ae14b911\"\n  },\n  {\n    \"contractName\": \"GuestModule\",\n    \"address\": \"0xfea230Ee243f88BC698dD8f1aE93F8301B6cdfaE\"\n  },\n  {\n    \"contractName\": \"SequenceUtils\",\n    \"address\": \"0xdbbFa3cB3B087B64F4ef5E3D20Dda2488AA244e6\"\n  }\n]"
  },
  {
    "path": "networks/optimism.json",
    "content": "[\n  {\n    \"contractName\": \"WalletFactory\",\n    \"address\": \"0xFaA5c0b14d1bED5C888Ca655B9a8A5911F78eF4A\"\n  },\n  {\n    \"contractName\": \"MainModule\",\n    \"address\": \"0xfBf8f1A5E00034762D928f46d438B947f5d4065d\"\n  },\n  {\n    \"contractName\": \"MainModuleUpgradable\",\n    \"address\": \"0x4222dcA3974E39A8b41c411FeDDE9b09Ae14b911\"\n  },\n  {\n    \"contractName\": \"GuestModule\",\n    \"address\": \"0xfea230Ee243f88BC698dD8f1aE93F8301B6cdfaE\"\n  },\n  {\n    \"contractName\": \"SequenceUtils\",\n    \"address\": \"0xdbbFa3cB3B087B64F4ef5E3D20Dda2488AA244e6\"\n  }\n]"
  },
  {
    "path": "networks/polygon.json",
    "content": "[\n  {\n    \"contractName\": \"WalletFactory\",\n    \"address\": \"0xFaA5c0b14d1bED5C888Ca655B9a8A5911F78eF4A\"\n  },\n  {\n    \"contractName\": \"MainModule\",\n    \"address\": \"0xfBf8f1A5E00034762D928f46d438B947f5d4065d\"\n  },\n  {\n    \"contractName\": \"MainModuleUpgradable\",\n    \"address\": \"0x4222dcA3974E39A8b41c411FeDDE9b09Ae14b911\"\n  },\n  {\n    \"contractName\": \"GuestModule\",\n    \"address\": \"0xfea230Ee243f88BC698dD8f1aE93F8301B6cdfaE\"\n  },\n  {\n    \"contractName\": \"SequenceUtils\",\n    \"address\": \"0xdbbFa3cB3B087B64F4ef5E3D20Dda2488AA244e6\"\n  },\n  {\n    \"contractName\": \"TrustFactory\",\n    \"address\": \"0x4483FaA9dEEDd6D6FaCFee9c686f1E394A1280f9\"\n  }\n]"
  },
  {
    "path": "networks/polygonZkevm.json",
    "content": "[\n  {\n    \"contractName\": \"WalletFactory\",\n    \"address\": \"0xFaA5c0b14d1bED5C888Ca655B9a8A5911F78eF4A\"\n  },\n  {\n    \"contractName\": \"MainModule\",\n    \"address\": \"0xfBf8f1A5E00034762D928f46d438B947f5d4065d\"\n  },\n  {\n    \"contractName\": \"MainModuleUpgradable\",\n    \"address\": \"0x4222dcA3974E39A8b41c411FeDDE9b09Ae14b911\"\n  },\n  {\n    \"contractName\": \"GuestModule\",\n    \"address\": \"0xfea230Ee243f88BC698dD8f1aE93F8301B6cdfaE\"\n  },\n  {\n    \"contractName\": \"SequenceUtils\",\n    \"address\": \"0xdbbFa3cB3B087B64F4ef5E3D20Dda2488AA244e6\"\n  }\n]"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"@0xsequence/wallet-contracts\",\n  \"version\": \"3.0.1\",\n  \"private\": true,\n  \"license\": \"Apache-2.0\",\n  \"scripts\": {\n    \"build\": \"pnpm compile && pnpm adapter\",\n    \"compile\": \"hardhat --max-memory 4096 compile\",\n    \"clean\": \"rimraf artifacts && rimraf cache\",\n    \"typecheck\": \"tsc --noEmit\",\n    \"test\": \"hardhat test\",\n    \"benchmark\": \"BENCHMARK=true pnpm test\",\n    \"coverage\": \"COVERAGE=true NET_ID=1 hardhat coverage\",\n    \"deploy\": \"hardhat run utils/deploy-contracts.ts --network hardhat\",\n    \"verify\": \"hardhat verify --network\",\n    \"release\": \"pnpm publish src\",\n    \"lint\": \"pnpm lint:ts && pnpm lint:sol\",\n    \"lint:fix\": \"pnpm lint:ts:fix && pnpm lint:sol:fix\",\n    \"lint:sol\": \"solhint \\\"./contracts/**/*.sol\\\"\",\n    \"lint:sol:fix\": \"solhint \\\"./contracts/**/*.sol\\\" --fix\",\n    \"lint:ts\": \"eslint -c .eslintrc.js \\\"./**/*.ts\\\"\",\n    \"lint:ts:fix\": \"eslint -c .eslintrc.js --fix \\\"./**/*.ts\\\"\",\n    \"format\": \"prettier --write ./**/*.ts\",\n    \"adapter\": \"typechain --target ethers-v6 --out-dir gen/typechain \\\"./artifacts/contracts/**/*[^dbg].json\\\"\",\n    \"prepare\": \"husky\"\n  },\n  \"types\": \"gen/typechain/index.ts\",\n  \"files\": [\n    \"./LICENSE\",\n    \"./artifacts/contracts/**/*.json\",\n    \"./contracts/**/*.sol\",\n    \"./gen/typechain\",\n    \"./networks\"\n  ],\n  \"husky\": {\n    \"hooks\": {\n      \"pre-commit\": \"pnpm lint\",\n      \"pre-push\": \"pnpm lint && pnpm test\"\n    }\n  },\n  \"devDependencies\": {\n    \"@nomicfoundation/hardhat-ethers\": \"^3.0.5\",\n    \"@nomicfoundation/hardhat-verify\": \"^2.0.4\",\n    \"@nomiclabs/hardhat-truffle5\": \"^2.0.7\",\n    \"@nomiclabs/hardhat-web3\": \"^2.0.0\",\n    \"@tenderly/hardhat-tenderly\": \"^1.0.11\",\n    \"@types/chai\": \"^4.3.16\",\n    \"@types/chai-as-promised\": \"^7.1.0\",\n    \"@types/chai-string\": \"^1.4.1\",\n    \"@types/mocha\": \"^8.2.1\",\n    \"@typescript-eslint/eslint-plugin\": \"^4.18.0\",\n    \"@typescript-eslint/parser\": \"^4.18.0\",\n    \"bn-chai\": \"^1.0.1\",\n    \"chai\": \"^4.3.4\",\n    \"chai-as-promised\": \"^7.1.1\",\n    \"chai-bignumber\": \"^3.0.0\",\n    \"chai-shallow-deep-equal\": \"^1.4.6\",\n    \"chai-string\": \"^1.5.0\",\n    \"child_process\": \"^1.0.2\",\n    \"dotenv\": \"^8.2.0\",\n    \"eslint\": \"^7.22.0\",\n    \"eslint-config-prettier\": \"^8.1.0\",\n    \"eslint-plugin-import\": \"^2.22.0\",\n    \"eslint-plugin-prettier\": \"^3.3.1\",\n    \"ethereum-waffle\": \"^3.4.4\",\n    \"ganache-cli\": \"6.12.2\",\n    \"hardhat\": \"^2.20.1\",\n    \"hardhat-gas-reporter\": \"1.0.10\",\n    \"husky\": \"^9.0.11\",\n    \"ora\": \"^5.4.1\",\n    \"rimraf\": \"^3.0.2\",\n    \"scrypt\": \"github:barrysteyn/node-scrypt#fb60a8d3c158fe115a624b5ffa7480f3a24b03fb\",\n    \"solhint\": \"^3.4.1\",\n    \"solidity-coverage\": \"0.8.3\",\n    \"threads\": \"^1.7.0\",\n    \"ts-node\": \"^10.9.1\",\n    \"typechain\": \"^8.3.2\",\n    \"typescript\": \"^4.7.4\",\n    \"yesno\": \"^0.3.1\"\n  },\n  \"config\": {\n    \"mnemonic\": \"test test test test test test test test test test test junk\",\n    \"ganacheChainID\": 127001,\n    \"ganachePort\": 8545,\n    \"ganacheGasLimit\": \"0xfffffffffff\",\n    \"ganacheGasPrice\": \"2\",\n    \"etherBalance\": \"100000\",\n    \"extra\": \"\"\n  },\n  \"dependencies\": {\n    \"@typechain/ethers-v6\": \"^0.5.1\",\n    \"ethers\": \"^6.13.0\",\n    \"keccak256\": \"^1.0.6\"\n  }\n}\n"
  },
  {
    "path": "remappings.txt",
    "content": "ds-test/=lib/forge-std/lib/ds-test/src/\nforge-std/=lib/forge-std/src/"
  },
  {
    "path": "run_huff_tests.sh",
    "content": "#!/bin/bash\n\nfor file in $(find ./src -type f -name '*.huff'); do\n    echo \"Testing $file...\"\n    output=$(huffc \"$file\" test 2>&1)\n    echo \"$output\"\n\n    # Check for the presence of [FAIL] that is not part of another word like [PASS]\n    if echo \"$output\" | grep -E 'FAIL' > /dev/null; then\n        echo \"Failure detected in $file\"\n        exit 255\n    else\n        echo \"Processed $file successfully\"\n    fi\ndone\n\necho \"All tests completed\""
  },
  {
    "path": "src/Errors.huff",
    "content": "/// @title Errors\n/// @notice SPDX-License-Identifier: MIT\n/// @author jtriley.eth\n/// @author clabby <https://github.com/clabby>\n/// @notice Custom error utilities.\n\n// https://docs.soliditylang.org/en/latest/control-structures.html?highlight=panic#panic-via-assert-and-error-via-require\n\n// Errors\n#define error Error(string)\n#define error Panic(uint256)\n\n// Constants\n// Solidity Panic Codes\n#define constant COMPILER_PANIC = 0x00\n#define constant ASSERT_FALSE = 0x01\n#define constant ARITHMETIC_OVERFLOW = 0x11\n#define constant DIVIDE_BY_ZERO = 0x12\n#define constant INVALID_ENUM_VALUE = 0x21\n#define constant INVALID_STORAGE_BYTE_ARRAY = 0x22\n#define constant EMPTY_ARRAY_POP = 0x31\n#define constant ARRAY_OUT_OF_BOUNDS = 0x32\n#define constant MEMORY_TOO_LARGE = 0x41\n#define constant UNINITIALIZED_FUNCTION_POINTER = 0x51\n\n/*\n\nSolidity Require. Error `string` MUST be no greater than 32 bytes.\n\nMEMORY LAYOUT WHEN THROWN\n| sig    || message offset                                                 || message length                                                 || message \"revert\"                                               |\n0x08c379a  0000000000000000000000000000000000000000000000000000000000000020  0000000000000000000000000000000000000000000000000000000000000006  7265766572740000000000000000000000000000000000000000000000000000\n\n*/\n#define macro REQUIRE() = takes (3) returns (0) {\n    // takes:       // [condition, message_length, message]\n    do_not_throw    // [do_not_throw_jumpdest, condition, message_length, message]\n    jumpi           // [message_length, message]\n    __ERROR(Error)  // [error_sig, , message_length, message]\n    0x00            // [mem_ptr, error_sig, message_length, message]\n    mstore          // [message_length, message]\n    0x20            // [message_offset, message_length, message]\n    0x04            // [message_offset_ptr, message_offset, message_length, message]\n    mstore          // [message_length, message]\n    0x24            // [message_length_ptr, message_length, message]\n    mstore          // [message]\n    0x44            // [message_ptr, message]\n    mstore          // []\n    0x80            // [size]\n    0x00            // [offset, size]\n    revert          // []\n    do_not_throw:   // [message_length, message]\n        pop         // [message]\n        pop         // []\n}\n\n/*\n\nSolidity Panic.\n\nMEMORY LAYOUT WHEN THROWN\n| sig     || panic code                                                     |\n0x4e487b71  0000000000000000000000000000000000000000000000000000000000000001\n\n*/\n#define macro PANIC() = takes (1) returns (0) {\n    // takes:       // [panic_code]\n    __ERROR(Panic)  // [panic_sig, panic_code]\n    0x00            // [panic_sig_offset, panic_sig, panic_code]\n    mstore          // [panic_code]\n    0x04            // [panic_code_offset, panic_code]\n    mstore          // []\n    0x24            // [revert_size]\n    0x00            // [revert_offset, revert_size]\n    revert          // []\n}\n\n/*\nSolidity Assert.\n\nMEMORY LAYOUT WHEN THROWN\n| sig     || assert failed panic code                                       |\n0x4e487b71  0000000000000000000000000000000000000000000000000000000000000001\n\n*/\n#define macro ASSERT() = takes (1) returns (0) {\n    // takes:               // [condition]\n    do_not_panic            // [do_not_panic_jumpdest, condition]\n    jumpi                   // []\n    [ASSERT_FALSE]          // [assert_false]\n    PANIC()                 // []\n    do_not_panic:           // []\n}\n\n// Assert that two stack elements are equal\n#define macro ASSERT_EQ() = {\n    // takes:             [a, b]\n    eq                 // [a == b]\n    ASSERT()           // []\n}\n\n// Assert that two stack elements are not equal\n#define macro ASSERT_NOT_EQ() = {\n    // takes:             [a, b]\n    eq iszero          // [a != b]\n    ASSERT()           // []\n}\n\n// Assert that two memory offsets contain equal words\n#define macro ASSERT_MEM_EQ(ptr_a, ptr_b) = {\n    // takes:             []\n    <ptr_b> mload      // [b]\n    <ptr_a> mload      // [a, b]\n    eq                 // [a == b]\n    ASSERT()           // []\n}\n\n// Assert that two memory offsets do not contain equal words\n#define macro ASSERT_MEM_NOT_EQ(ptr_a, ptr_b) = {\n    // takes:             []\n    <ptr_b> mload      // [b]\n    <ptr_a> mload      // [a, b]\n    eq iszero          // [a != b]\n    ASSERT()           // []\n}\n\n// Assert that two storage slots contain equal words\n#define macro ASSERT_STORAGE_EQ(slot_a, slot_b) = {\n    // takes:             []\n    <slot_b> sload     // [b]\n    <slot_a> sload     // [a, b]\n    eq                 // [a == b]\n    ASSERT()           // []\n}\n\n// Assert that two storage slots do not contain equal words\n#define macro ASSERT_STORAGE_NOT_EQ(slot_a, slot_b) = {\n    // takes:             []\n    <slot_b> sload     // [b]\n    <slot_a> sload     // [a, b]\n    eq iszero          // [a != b]\n    ASSERT()           // []\n}\n\n/* Bubbles up revert data if call failed. Call directly after `call`, `staticcall`, `delegatecall`. */\n#define macro BUBBLE_UP_IF_FAILED() = takes (1) returns (0) {\n    // takes:       // [call_succeeded]\n    call_succeeded  // [call_succeeded_jumpdest, call_succeeded]\n    jumpi           // []\n    returndatasize  // [returndatasize]\n    0x00            // [memory_offset, returndatasize]\n    returndatasize  // [returndatasize, memory_offset, returndatasize]\n    dup2            // [returndata_offset, returndatasize, memory_offset, returndatasize]\n    dup3            // [memory_offset, returndata_offset, returndatasize, memory_offset, returndatasize]\n    returndatacopy  // [memory_offset, returndatasize]\n    revert          // []\n    call_succeeded:\n}"
  },
  {
    "path": "src/L2Compressor.huff",
    "content": "#include \"./L2CompressorLib.huff\"\n\n\n#define jumptable SELECTORS_TABLE {\n  execute_transaction          // 0x00\n  execute_many_transactions    // 0x01\n  read_address                 // 0x02\n  read_bytes32                 // 0x03\n  sizes                        // 0x04\n  read_storage_slots           // 0x05\n  decompress_transaction       // 0x06\n  decompress_many_transactions // 0x07\n}\n\n#define macro MAIN() = takes (0) returns (0) {\n  // Write the jump table to 0x20\n  // or else the flags jumptable won't get written\n  // all this memory will be reused anyway\n  __tablesize(SELECTORS_TABLE)     // [table_size]\n  __tablestart(SELECTORS_TABLE)    // [table_start, table_size]\n  0x20                        // [0x20, table_start, table_size]\n  codecopy                    // []\n\n  callvalue                   // [0x00]\n  calldataload                // [data[0x00]]\n  callvalue                   // [0x00, data[0x00]]\n  byte                        // [method]\n\n  0x05                        // [0x05, method]\n  shl                         // [(method << 0x05)]\n  0x20 add                    // [method + 0x20]\n  mload                       // [mload[method]]\n  jump                        // []\n\n  execute_transaction:\n    0x01                         // [rindex]\n    PERFORM_EXECUTE(nrfs)        // [rindex]\n    callvalue callvalue return\n\n  execute_many_transactions:\n    0x01                         // [rindex]\n    PERFORM_MANY_EXECUTES(nrfs)  // [rindex, size, i]\n    callvalue callvalue return\n\n  read_address:\n    PERFORM_READ_ADDRESS()\n    0x20 callvalue return\n\n  read_bytes32:\n    PERFORM_READ_BYTES32()\n    0x20 callvalue return\n\n  sizes:\n    PERFORM_READ_SIZES()\n    0x20 callvalue return\n\n  read_storage_slots:\n    PERFORM_READ_SLOTS()         // [size]\n    callvalue                    // [0x00, size]\n    return\n\n  decompress_transaction:\n    0x01                         // [rindex]\n    [FMS]                        // [windex, rindex]\n\n    READ_FULL_EXECUTE(nrfs)      // [windex, rindex]\n\n    [FMS]                        // [FMS, windex, rindex]\n    swap1                        // [windex, FMS, rindex]\n    sub                          // [(windex - FMS), rindex]\n\n    [FMS]                        // [FMS, (windex - FMS), rindex]\n    return                       // [rindex]\n\n  decompress_many_transactions:\n    0x01                          // [rindex]\n    [FMS]                         // [windex, rindex]\n\n    READ_MANY_FULL_EXECUTES(nrfs) // [windex, rindex]\n\n    [FMS]                         // [FMS, windex, rindex]\n    swap1                         // [windex, FMS, rindex]\n    sub                           // [(windex - FMS), rindex]\n\n    [FMS]                         // [FMS, (windex - FMS), rindex]\n    return                        // [rindex]\n\n  // This will be appended at the end\n  // unreachable code as all the method return first\n\n  nrfs:\n  FN_READ_FLAG(nrfs)\n}\n\n\n\n#define macro READ_FULL_EXECUTE(nrfs) = takes (2) returns (2) {\n  // input stack: [windex, rindex]\n\n  READ_EXECUTE(<nrfs>)              // [windex, rindex]\n  PERFORM_NESTED_READ_FLAG(<nrfs>)  // [windex, rindex]\n\n  // output stack: [windex, rindex]\n}\n\n#define macro READ_MANY_FULL_EXECUTES(nrfs) = takes (2) returns (2) {\n  // input stack: [windex, rindex]\n\n  swap1          // [rindex, windex]\n\n  LOAD_1_BYTE()  // [size, rindex, windex]\n  callvalue      // [i, size, rindex, windex]\n  swap2          // [rindex, size, i, windex]\n\n  do_another:                 // [rindex, size, i, windex]\n    swap1                     // [size, rindex, i, windex]\n    swap2                     // [i, rindex, size, windex]\n    swap3                     // [windex, rindex, size, i]\n\n    READ_FULL_EXECUTE(<nrfs>) // [windex, rindex, size, i]\n\n    swap3                     // [i, rindex, size, windex]\n    swap2                     // [size, rindex, i, windex]\n    swap1                     // [rindex, size, i, windex]\n\n    swap2                     // [i, size, rindex, windex]\n    0x01                      // [0x01, i, size, rindex, windex]\n    add                       // [(0x01 + i), size, rindex, windex]\n    swap2                     // [rindex, size, (0x01 + i), windex]\n\n    dup2             // [size, rindex, size, (0x01 + i), windex]\n    dup4             // [(0x01 + i), size, rindex, size, (0x01 + i), windex]\n    lt               // [((0x01 + i) < size), rindex, size, (0x01 + i), windex]\n    do_another jumpi // [rindex, size, (0x01 + i), windex]\n\n  swap1              // [size, rindex, (0x01 + i), windex]\n  swap3              // [windex, rindex, (0x01 + i), size]\n  \n  // output stack: [windex, rindex, (0x01 + i), size]\n}"
  },
  {
    "path": "src/L2CompressorLib.huff",
    "content": "#include \"./Errors.huff\"\n\n#define constant ADDR_BYTES_STORAGE = 0x00\n#define constant FMS = 0xa0\n\n#define constant FLAG_READ_BYTES32_2_BYTES = 0x27\n#define constant FLAG_READ_ADDRESS_2_BYTES = 0x23\n\n#define constant BYTES32_SMV = 0x80\n#define constant ADDRESS_SMV = 0x01\n\n#define macro PERFORM_READ_SLOTS() = takes (0) returns (1) {\n  // input stack: []\n\n  0x01          // [0x01]\n  dup1          // [rindex, 0x01]\n  callvalue     // [windex, rindex, 0x01]\n\n  read_another:\n    dup2          // [rindex, windex, rindex]\n    calldataload  // [data[rindex], windex, rindex]\n    sload         // [sload[data[rindex]], windex, rindex]\n    dup2          // [windex, sload[data[rindex]], windex, rindex]\n    mstore        // [windex, rindex]\n\n    0x20          // [0x20, windex, rindex]\n    swap2         // [rindex, windex, 0x20]\n    dup3          // [0x20, rindex, windex, 0x20]\n    add           // [(0x20 + rindex), windex, 0x20]\n    swap2         // [0x20, windex, (0x20 + rindex)]\n    add           // [(0x20 + windex), (0x20 + rindex)]\n\n    calldatasize        // [size, (0x20 + windex), (0x20 + rindex)]\n    dup3                // [(0x20 + rindex), size, (0x20 + windex), (0x20 + rindex)]\n    lt                  // [((0x20 + rindex) < size), (0x20 + windex), (0x20 + rindex)]\n    read_another jumpi  // [windex, rindex]\n    \n  pop                   // [rindex, 0x01]\n  sub                   // [(rindex - 0x01)]\n\n  // output stack: [size]\n}\n\n#define macro PERFORM_READ_SIZES() = takes (0) returns (0) {\n\n  callvalue     // [0x00]\n  sload         // [sload[0x00]]\n  callvalue     // [value, sload[0x00]]\n  mstore        // []\n\n}\n\n#define macro PERFORM_READ_ADDRESS() = takes (0) returns (0) {\n\n  0x01          // [0x01]\n  calldataload  // [data[0x01]]\n\n  [ADDRESS_SMV] // [ADDRESS_SMV, data[0x00]]\n  add           // [(ADDRESS_SMV + data[0x00])]\n  sload         // [sload[(ADDRESS_SMV + data[0x00])]]\n\n  callvalue     // [0x00, sload[(ADDRESS_SMV + data[0x00])]]\n  mstore        // []\n\n}\n\n#define macro PERFORM_READ_BYTES32() = takes (0) returns (0) {\n\n  0x01          // [0x01]\n  calldataload  // [data[0x01]]\n\n  [BYTES32_SMV] // [BYTES32_SMV, data[0x00]]\n  shl           // [(data[0x00] << BYTES32_SMV)]\n  sload         // [sload[(data[0x00] << BYTES32_SMV)]]\n  \n  callvalue     // [0x00, sload[(data[0x00] << BYTES32_SMV)]]\n  mstore        // []\n\n}\n\n#define macro PERFORM_MANY_EXECUTES(nrfs) = takes (1) returns (3) {\n  // input stack: [rindex]\n\n  LOAD_1_BYTE()  // [size, rindex]\n  callvalue      // [i, size, rindex]\n  swap2          // [rindex, size, i]\n\n  do_another:\n    PERFORM_EXECUTE(<nrfs>)  // [rindex, size, i]\n    swap2                    // [i, size, rindex]\n    0x01                     // [0x01, i, size, rindex]\n    add                      // [(0x01 + i), size, rindex]\n    swap2                    // [rindex, size, (0x01 + i)]\n\n    dup2                     // [size, rindex, size, (0x01 + i)]\n    dup4                     // [(0x01 + i), size, rindex, size, (0x01 + i)]\n    lt                       // [((0x01 + i) < size), rindex, size, (0x01 + i)]\n    do_another jumpi         // [rindex, size, (0x01 + i)]\n\n  // output stack: [rindex, size, i]\n}\n\n#define macro PERFORM_EXECUTE(nrfs) = takes (1) returns (1) {\n  // input stack: [rindex]\n\n  [FMS]                            // [windex, rindex]\n  READ_EXECUTE(<nrfs>)             // [windex, rindex]\n\n  PERFORM_NESTED_READ_FLAG(<nrfs>) // [windex, rindex]\n  BACKREAD_SINGLE_VALUE()          // [address, windex, rindex]\n\n  swap1                 // [windex, address, rindex]\n  [FMS]                 // [FMS, windex, address, rindex]\n  swap1                 // [windex, FMS, address, rindex]\n  sub                   // [size, address, rindex]\n\n  callvalue             // [0x00, size, address, rindex]  \n  swap1                 // [size, 0x00, address, rindex]\n  [FMS]                 // [FMS, size, 0x00, address, rindex]\n  callvalue             // [0x00, FMS, size, 0x00, address, rindex]\n  callvalue             // [0x00, 0x00, FMS, size, 0x00, address, rindex]\n  swap5                 // [address, 0x00, FMS, size, 0x00, 0x00, rindex]\n  gaslimit              // [gasLimit, address, 0x00, FMS, size, 0x00, 0x00, rindex]\n  call                  // [success, rindex]\n\n  // For now, pop seems safer, since it won't revert all transactions if this is a batch\n  // the only thing to consider is that this could difficult the gas calculation\n  pop                   // [rindex]\n\n  // output stack: [rindex]\n}\n\n#define macro ADDRESSES_NUM() = takes (0) returns (1) {\n  [ADDR_BYTES_STORAGE] sload // [packed]\n  0x80 shr                 // [num]\n\n  // output stack: [num]\n}\n\n#define macro PULL_ADDRESS() = takes(0) returns (1) {\n  [ADDR_BYTES_STORAGE] sload // [packed]\n  dup1                       // [packed, packed]\n  0x80 shr                   // [num, packed]\n\n  0x01 add                   // [num + 1, packed]\n  swap1                      // [packed, num + 1]\n\n  // Mask packed (only want lower 128 bits)\n  0xffffffffffffffffffffffffffffffff and\n\n  dup2                       // [num + 1, packed, num + 1]\n  0x80 shl                   // [num + 1 << 0x80, packed, num + 1]\n  or                         // [nextpacked, num + 1]\n\n  [ADDR_BYTES_STORAGE] sstore // [num + 1]\n\n  // output stack: [num + 1]\n}\n\n#define macro BYTES32_STORAGE_POINTER() = takes (1) returns (1) {\n  // input stack: [index]\n  0x80 shl\n  // output stack: [index << 0x80]\n}\n\n#define macro ADDRESS_STORAGE_POINTER() = takes (1) returns (1) {\n  // input stack: [index]\n  0x01 add\n  // output stack: [index + 1]\n}\n\n#define macro BYTES32_NUM() = takes (0) returns (1) {\n  [ADDR_BYTES_STORAGE] sload             // [packed]\n  0xffffffffffffffffffffffffffffffff and // [num]\n\n  // output stack: [num]\n}\n\n#define macro PULL_BYTES32() = takes(0) returns (1) {\n  [ADDR_BYTES_STORAGE] sload             // [packed]\n  dup1                                   // [packed, packed]\n  0xffffffffffffffffffffffffffffffff and // [num, packed]\n\n  0x01 add                               // [num + 1, packed]\n  swap1                                  // [packed, num + 1]\n\n  0xffffffffffffffffffffffffffffffff00000000000000000000000000000000 and // [packed, num + 1]\n\n  dup2                                   // [num + 1, packed, num + 1]\n  or                                     // [nextpacked, num + 1]\n\n  [ADDR_BYTES_STORAGE] sstore            // [num + 1]\n\n  // output stack: [num + 1]\n}\n\n#define jumptable__packed FLAG_TABLE {\n  FLAG_READ_POWER_OF_10_MISC // 0x00\n  FLAG_READ_BYTES32_1_BYTES  // 0x01\n  FLAG_READ_BYTES32_2_BYTES  // 0x02\n  FLAG_READ_BYTES32_3_BYTES  // 0x03\n  FLAG_READ_BYTES32_4_BYTES  // 0x04\n  FLAG_READ_BYTES32_5_BYTES  // 0x05\n  FLAG_READ_BYTES32_6_BYTES  // 0x06\n  FLAG_READ_BYTES32_7_BYTES  // 0x07\n  FLAG_READ_BYTES32_8_BYTES  // 0x08\n  FLAG_READ_BYTES32_9_BYTES  // 0x09\n  FLAG_READ_BYTES32_10_BYTES // 0x0a\n  FLAG_READ_BYTES32_11_BYTES // 0x0b\n  FLAG_READ_BYTES32_12_BYTES // 0x0c\n  FLAG_READ_BYTES32_13_BYTES // 0x0d\n  FLAG_READ_BYTES32_14_BYTES // 0x0e\n  FLAG_READ_BYTES32_15_BYTES // 0x0f\n  FLAG_READ_BYTES32_16_BYTES // 0x10\n  FLAG_READ_BYTES32_17_BYTES // 0x11\n  FLAG_READ_BYTES32_18_BYTES // 0x12\n  FLAG_READ_BYTES32_19_BYTES // 0x13\n  FLAG_READ_BYTES32_20_BYTES // 0x14\n  FLAG_READ_BYTES32_21_BYTES // 0x15\n  FLAG_READ_BYTES32_22_BYTES // 0x16\n  FLAG_READ_BYTES32_23_BYTES // 0x17\n  FLAG_READ_BYTES32_24_BYTES // 0x18\n  FLAG_READ_BYTES32_25_BYTES // 0x19\n  FLAG_READ_BYTES32_26_BYTES // 0x1a\n  FLAG_READ_BYTES32_27_BYTES // 0x1b\n  FLAG_READ_BYTES32_28_BYTES // 0x1c\n  FLAG_READ_BYTES32_29_BYTES // 0x1d\n  FLAG_READ_BYTES32_30_BYTES // 0x1e\n  FLAG_READ_BYTES32_31_BYTES // 0x1f\n  FLAG_READ_BYTES32_32_BYTES // 0x20\n  FLAG_SAVE_ADDRESS          // 0x21\n  FLAG_SAVE_BYTES32          // 0x22\n  FLAG_READ_ADDRESS_2        // 0x23\n  FLAG_READ_ADDRESS_3        // 0x24\n  FLAG_READ_ADDRESS_4        // 0x25\n  FLAG_READ_EXECUTE          // 0x26\n  FLAG_READ_BYTES32_2        // 0x27\n  FLAG_READ_BYTES32_3        // 0x28\n  FLAG_READ_BYTES32_4        // 0x29\n  FLAG_READ_POW_10_MANTISSA  // 0x2a\n  FLAG_READ_N_BYTES          // 0x2b\n  FLAG_READ_POWER_OF_2       // 0x2c\n  FLAG_ABI_0_PARAM           // 0x2d\n  FLAG_ABI_1_PARAM           // 0x2e\n  FLAG_ABI_2_PARAMS          // 0x2f\n  FLAG_ABI_3_PARAMS          // 0x20\n  FLAG_ABI_4_PARAMS          // 0x31\n  FLAG_ABI_5_PARAMS          // 0x32\n  FLAG_ABI_6_PARAMS          // 0x33\n  FLAG_NESTED_N_FLAGS_8      // 0x34\n  FLAG_NESTED_N_FLAGS_16     // 0x35\n  // start: Signature specific methods\n  FLAG_SIGNATURE_W0          // 0x36\n  FLAG_SIGNATURE_W1          // 0x37\n  FLAG_SIGNATURE_W2          // 0x38\n  FLAG_SIGNATURE_W3          // 0x39\n  FLAG_SIGNATURE_W4          // 0x3a\n  FLAG_ADDRESS_W0            // 0x3b\n  FLAG_ADDRESS_W1            // 0x3c\n  FLAG_ADDRESS_W2            // 0x3d\n  FLAG_ADDRESS_W3            // 0x3e\n  FLAG_ADDRESS_W4            // 0x4f\n  FLAG_NODE                  // 0x40\n  FLAG_BRANCH                // 0x41\n  FLAG_SUBDIGEST             // 0x42\n  FLAG_NESTED                // 0x43\n  FLAG_DYNAMIC_SIGNATURE     // 0x44\n  FLAG_S_SIG_NO_CHAIN        // 0x45\n  FLAG_S_SIG                 // 0x46\n  FLAG_S_L_SIG_NO_CHAIN      // 0x47\n  FLAG_S_L_SIG               // 0x48\n  FLAG_READ_CHAINED          // 0x49\n  FLAG_READ_CHAINED_L        // 0x4a\n  // end: Sequence specific methods\n  FLAG_READ_DYNAMIC_ABI      // 0x4b\n  FLAG_NO_OP                 // 0x4c\n  FLAG_MIRROR_FLAG           // 0x4d\n  FLAG_COPY_CALLDATA         // 0x4e\n  FLAG_READ_STORE_FLAG       // 0x4f\n}\n\n#define constant HIGHEST_FLAG = 0x4f\n#define constant HIGHEST_FLAG_PLUS_ONE = 0x50\n\n#define macro READ_FLAG() = takes (2) returns (2) {\n  // input stack: [windex, rindex]\n\n  swap1        // [rindex, windex]\n  end          // [end, rindex, windex]\n  swap2        // [windex, rindex, end]\n\n  nrfs:\n  FN_READ_FLAG(nrfs) // [windex, rindex]\n\n  end:\n}\n\n#define macro FN_READ_FLAG(nrfs) = takes (3) returns (2) {\n  // input stack: [windex, rindex, jump_to]\n\n  dup2            // [rindex, windex, rindex, jump_to]\n  calldataload    // [cdata[rindex], windex, rindex, jump_to]\n  callvalue byte  // [flag, windex, rindex, jump_to]\n\n  swap2         // [rindex, windex, flag, jump_to]\n  0x01 add      // [rindex + 1, windex, flag, jump_to]\n  swap2         // [flag, windex, rindex + 1, jump_to]\n\n  dup1                        // [flag, flag, windex, rindex + 1, jump_to]\n  [HIGHEST_FLAG] lt           // [HIGHEST_FLAG < flag, flag, windex, rindex + 1, jump_to]\n  default jumpi               // [flag, windex, rindex + 1, jump_to]\n\n  // Starts to become cheaper to skip the loading\n  // after 5 times, most real world cases will have more than\n  // 5 times. Notice that this assumes a single READ_FLAG instance\n  callvalue mload no_load jumpi\n    __tablesize(FLAG_TABLE)     // [table_size, flag, windex, rindex + 1, jump_to]\n    __tablestart(FLAG_TABLE)    // [table_start, table_size, flag, windex, rindex + 1, jump_to]\n    callvalue                   // [0x00, table_start, table_size, flag, windex, rindex + 1, jump_to]\n    codecopy                    // [flag, windex, rindex + 1]\n  no_load:\n\n  0x01 shl                    // [flag << 0x01, windex, rindex + 1, jump_to]\n  mload                       // [word, windex, rindex + 1, jump_to]\n  0xf0 shr                    // [word >> 0xf0, windex, rindex + 1, jump_to]\n  jump                        // [windex, rindex + 1, jump_to]\n\n  FLAG_READ_POWER_OF_10_MISC:\n    __tablestart(POW_10_TABLE)\n    READ_POW_10_AND_SELF_EXECUTE(<nrfs>)\n    end jump\n  FLAG_READ_BYTES32_1_BYTES:\n    READ_BYTES32(0xf8, 0x01)\n    end jump\n  FLAG_READ_BYTES32_2_BYTES:\n    READ_BYTES32(0xf0, 0x02)\n    end jump\n  FLAG_READ_BYTES32_3_BYTES:\n    READ_BYTES32(0xe8, 0x03)\n    end jump\n  FLAG_READ_BYTES32_4_BYTES:\n    READ_BYTES32(0xe0, 0x04)\n    end jump\n  FLAG_READ_BYTES32_5_BYTES:\n    READ_BYTES32(0xd8, 0x05)\n    end jump\n  FLAG_READ_BYTES32_6_BYTES:\n    READ_BYTES32(0xd0, 0x06)\n    end jump\n  FLAG_READ_BYTES32_7_BYTES:\n    READ_BYTES32(0xc8, 0x07)\n    end jump\n  FLAG_READ_BYTES32_8_BYTES:\n    READ_BYTES32(0xc0, 0x08)\n    end jump\n  FLAG_READ_BYTES32_9_BYTES:\n    READ_BYTES32(0xb8, 0x09)\n    end jump\n  FLAG_READ_BYTES32_10_BYTES:\n    READ_BYTES32(0xb0, 0x0a)\n    end jump\n  FLAG_READ_BYTES32_11_BYTES:\n    READ_BYTES32(0xa8, 0x0b)\n    end jump\n  FLAG_READ_BYTES32_12_BYTES:\n    READ_BYTES32(0xa0, 0x0c)\n    end jump\n  FLAG_READ_BYTES32_13_BYTES:\n    READ_BYTES32(0x98, 0x0d)\n    end jump\n  FLAG_READ_BYTES32_14_BYTES:\n    READ_BYTES32(0x90, 0x0e)\n    end jump\n  FLAG_READ_BYTES32_15_BYTES:\n    READ_BYTES32(0x88, 0x0f)\n    end jump\n  FLAG_READ_BYTES32_16_BYTES:\n    READ_BYTES32(0x80, 0x10)\n    end jump\n  FLAG_READ_BYTES32_17_BYTES:\n    READ_BYTES32(0x78, 0x11)\n    end jump\n  FLAG_READ_BYTES32_18_BYTES:\n    READ_BYTES32(0x70, 0x12)\n    end jump\n  FLAG_READ_BYTES32_19_BYTES:\n    READ_BYTES32(0x68, 0x13)\n    end jump\n  FLAG_READ_BYTES32_20_BYTES:\n    READ_BYTES32(0x60, 0x14)\n    end jump\n  FLAG_READ_BYTES32_21_BYTES:\n    READ_BYTES32(0x58, 0x15)\n    end jump\n  FLAG_READ_BYTES32_22_BYTES:\n    READ_BYTES32(0x50, 0x16)\n    end jump\n  FLAG_READ_BYTES32_23_BYTES:\n    READ_BYTES32(0x48, 0x17)\n    end jump\n  FLAG_READ_BYTES32_24_BYTES:\n    READ_BYTES32(0x40, 0x18)\n    end jump\n  FLAG_READ_BYTES32_25_BYTES:\n    READ_BYTES32(0x38, 0x19)\n    end jump\n  FLAG_READ_BYTES32_26_BYTES:\n    READ_BYTES32(0x30, 0x1a)\n    end jump\n  FLAG_READ_BYTES32_27_BYTES:\n    READ_BYTES32(0x28, 0x1b)\n    end jump\n  FLAG_READ_BYTES32_28_BYTES:\n    READ_BYTES32(0x20, 0x1c)\n    end jump\n  FLAG_READ_BYTES32_29_BYTES:\n    READ_BYTES32(0x18, 0x1d)\n    end jump\n  FLAG_READ_BYTES32_30_BYTES:\n    READ_BYTES32(0x10, 0x1e)\n    end jump\n  FLAG_READ_BYTES32_31_BYTES:\n    READ_BYTES32(0x08, 0x1f)\n    end jump\n  FLAG_READ_BYTES32_32_BYTES:\n    READ_BYTES32_WORD()\n    end jump\n\n  FLAG_SAVE_ADDRESS:\n    SAVE_ADDRESS()\n    end jump\n\n  FLAG_SAVE_BYTES32:\n    SAVE_BYTES32()\n    end jump\n\n  FLAG_READ_ADDRESS_2:\n    READ_ADDRESS_STORAGE(0x02, 0xf0)\n    end jump\n  FLAG_READ_ADDRESS_3:\n    READ_ADDRESS_STORAGE(0x03, 0xe8)\n    end jump\n  FLAG_READ_ADDRESS_4:\n    READ_ADDRESS_STORAGE(0x04, 0xe0)\n    end jump\n\n  FLAG_READ_EXECUTE:\n    READ_EXECUTE(<nrfs>)\n    end jump\n\n  FLAG_READ_BYTES32_2:\n    READ_BYTES32_STORAGE(0x02, 0xf0)\n    end jump\n  FLAG_READ_BYTES32_3:\n    READ_BYTES32_STORAGE(0x03, 0xe8)\n    end jump\n  FLAG_READ_BYTES32_4:\n    READ_BYTES32_STORAGE(0x04, 0xe0)\n    end jump                  // [end jump, windex, rindex + 1, jump_to]\n\n  FLAG_READ_POW_10_MANTISSA:\n    __tablestart(POW_10_TABLE)\n    READ_POW_10_MANTISSA(0x05, 0xd8)\n    end jump\n\n  FLAG_READ_N_BYTES:\n    READ_N_BYTES(<nrfs>)\n    end jump\n\n  FLAG_READ_POWER_OF_2:\n    READ_POW_2()\n    end jump\n\n  FLAG_ABI_0_PARAM:\n    __tablestart(COMMON_4BYTES) // [table_start, windex, rindex]\n    READ_ABI_0()\n    end jump\n  FLAG_ABI_1_PARAM:\n    __tablestart(COMMON_4BYTES) // [table_start, windex, rindex]\n    READ_ABI_1(<nrfs>)\n    end jump\n  FLAG_ABI_2_PARAMS:\n    __tablestart(COMMON_4BYTES) // [table_start, windex, rindex]\n    READ_ABI_2(<nrfs>)\n    end jump\n  FLAG_ABI_3_PARAMS:\n    __tablestart(COMMON_4BYTES) // [table_start, windex, rindex]\n    READ_ABI_3(<nrfs>)\n    end jump\n  FLAG_ABI_4_PARAMS:\n    __tablestart(COMMON_4BYTES) // [table_start, windex, rindex]\n    READ_ABI_4(<nrfs>)\n    end jump\n  FLAG_ABI_5_PARAMS:\n    __tablestart(COMMON_4BYTES) // [table_start, windex, rindex]\n    READ_ABI_5(<nrfs>)\n    end jump\n  FLAG_ABI_6_PARAMS:\n    __tablestart(COMMON_4BYTES) // [table_start, windex, rindex]\n    READ_ABI_6(<nrfs>)\n    end jump\n    \n  FLAG_NESTED_N_FLAGS_8:\n    READ_NESTED_N_FLAGS_8(<nrfs>)\n    end jump \n  FLAG_NESTED_N_FLAGS_16:\n    READ_NESTED_N_FLAGS_16(<nrfs>)\n    end jump \n\n  FLAG_SIGNATURE_W0:\n    READ_SIGNATURE_W0()\n    end jump\n  FLAG_SIGNATURE_W1:\n    READ_SIGNATURE_WX(0x01)\n    end jump\n  FLAG_SIGNATURE_W2:\n    READ_SIGNATURE_WX(0x02)\n    end jump\n  FLAG_SIGNATURE_W3:\n    READ_SIGNATURE_WX(0x03)\n    end jump\n  FLAG_SIGNATURE_W4:\n    READ_SIGNATURE_WX(0x04)\n    end jump\n\n  FLAG_ADDRESS_W0:\n    READ_ADDRESS_W0(<nrfs>)\n    end jump\n  FLAG_ADDRESS_W1:\n    READ_ADDRESS_WX(<nrfs>, 0x01)\n    end jump\n  FLAG_ADDRESS_W2:\n    READ_ADDRESS_WX(<nrfs>, 0x02)\n    end jump\n  FLAG_ADDRESS_W3:\n    READ_ADDRESS_WX(<nrfs>, 0x03)\n    end jump\n  FLAG_ADDRESS_W4:\n    READ_ADDRESS_WX(<nrfs>, 0x04)\n    end jump\n\n  FLAG_NODE:\n    READ_NODE(<nrfs>)\n    end jump\n  FLAG_BRANCH:\n    READ_BRANCH(<nrfs>)\n    end jump\n  FLAG_SUBDIGEST:\n    READ_SUBDIGEST(<nrfs>)\n    end jump\n  FLAG_NESTED:\n    READ_NESTED(<nrfs>)\n    end jump\n  FLAG_DYNAMIC_SIGNATURE:\n    READ_DYNAMIC_SIGNATURE(<nrfs>)\n    end jump\n\n  FLAG_S_SIG_NO_CHAIN:\n    READ_SEQUENCE_SIGNATURE(<nrfs>, 0x02, 0x01, 0xf8)\n    end jump\n  FLAG_S_SIG:\n    READ_SEQUENCE_SIGNATURE(<nrfs>, 0x01, 0x01, 0xf8)\n    end jump\n  FLAG_S_L_SIG_NO_CHAIN:\n    READ_SEQUENCE_SIGNATURE(<nrfs>, 0x02, 0x02, 0xf0)\n    end jump\n  FLAG_S_L_SIG:\n    READ_SEQUENCE_SIGNATURE(<nrfs>, 0x01, 0x02, 0xf0)\n    end jump\n\n  FLAG_READ_CHAINED:\n    READ_CHAINED(<nrfs>, 0x01, 0xf8)\n    end jump\n  FLAG_READ_CHAINED_L:\n    READ_CHAINED(<nrfs>, 0x02, 0xf0)\n    end jump\n    \n  FLAG_READ_DYNAMIC_ABI:\n    __tablestart(COMMON_4BYTES)\n    READ_ABI_DYNAMIC(<nrfs>)\n    end jump\n\n  FLAG_NO_OP:\n    end jump\n  FLAG_MIRROR_FLAG:\n    READ_MIRROR_FLAG(<nrfs>)\n    end jump\n  FLAG_COPY_CALLDATA:\n    READ_COPY_CALLDATA()\n    end jump\n  FLAG_READ_STORE_FLAG:\n    READ_STORE_FLAG()\n    end jump\n\n  default:\n    // The default just pushes the flag as a byte (padded to 32 bytes)\n    // notice that we start at 0x01 since 0x00 can be pushed with the flag 0x00\n    [HIGHEST_FLAG_PLUS_ONE] // [HIGHEST_FLAG_PLUS_ONE, flag, windex, rindex, jump_to]\n    swap1 sub               // [flag - HIGHEST_FLAG_PLUS_ONE, windex, rindex, jump_to]\n    dup2                    // [windex, flag - HIGHEST_FLAG_PLUS_ONE, windex, rindex, jump_to]\n    mstore                  // [windex, rindex, jump_to]\n    0x20 add                // [windex + 0x20, rindex, jump_to]\n\n  end:\n\n  swap1                     // [rindex, windex, jump_to]\n  swap2                     // [jump_to, windex, rindex]\n  jump                      // [windex, rindex]\n\n  // output stack:          // [windex, rindex]\n}\n\n#define macro READ_POW_10_MANTISSA() = takes (3) returns (2) {\n  // input stack: [table_start, windex, rindex]\n\n  // The first 6 bits are exponent\n\n  dup3         // [rindex, table_start, windex, rindex]\n  calldataload // [data[rindex], table_start, windex, rindex]\n\n  0xfa         // [0xfa, data[rindex], table_start, windex, rindex]\n  shr          // [exp, table_start, windex, rindex]\n\n  POW_10()                 // [windex, rindex]\n  BACKREAD_SINGLE_VALUE()  // [10 ** exp, windex, rindex]\n\n  // The next 18 bits are the mantissa\n\n  dup3         // [rindex, 10 ** exp, windex, rindex]\n  calldataload // [data[rindex], 10 ** exp, windex, rindex]\n\n  0xe8         // [0xe8, data[rindex], 10 ** exp, windex, rindex]\n  shr          // [(data[rindex] >> 0xe8), 10 ** exp, windex, rindex]\n  0x3ffff      // [0x3ffff, (data[rindex] >> 0xe8), 10 ** exp, windex, rindex]\n  and          // [mantissa, 10 ** exp, windex, rindex]\n  mul          // [(mantissa * 10 ** exp), windex, rindex]\n\n  dup2         // [windex, (mantissa * 10 ** exp), windex, rindex]\n  mstore       // [windex, rindex]\n\n  0x20         // [0x20, windex, rindex]\n  add          // [(0x20 + windex), rindex]\n  swap1        // [rindex, (0x20 + windex)]\n  0x03         // [0x03, rindex, (0x20 + windex)]\n  add          // [(0x03 + rindex), (0x20 + windex)]\n  swap1        // [(0x20 + windex), (0x03 + rindex)]\n\n  // input stack: [windex, rindex]\n}\n\n#define macro READ_POW_10_AND_SELF_EXECUTE(nrfs) = takes (3) returns (2) {\n  // input stack: [table_start, windex, rindex]\n\n  swap2          // [rindex, windex, table_start]\n  LOAD_1_BYTE()  // [exp_word, rindex, windex, table_start]\n\n  // We didn't had any more pleace for READ_SELF_EXECUTE without expanding\n  // the jumptable! so it has to live here. Sorry about that.\n  // 10 ** 0 * N is more expensive than just reading 1 byte,\n  // so this wasn't useful anyway\n\n  dup1                  // [exp_word, exp_word, rindex, windex, table_start]\n  normal_flow jumpi     // [exp_word, rindex, windex, table_start]\n\n    // --- WARNING UGLY CODE ---\n\n    pop                        // [rindex, windex, table_start]\n    swap2                      // [table_start, windex, rindex]\n    pop                        // [windex, rindex]\n\n    READ_SELF_EXECUTE(<nrfs>)  // [windex, rindex]\n    end_pow_10 jump            // [windex, rindex]\n\n  normal_flow:\n\n  // The first bit determines if we will multiply this by\n  // the next byte or not, this is fine as the maximum value that we\n  // can represent in a word is only 10 ** 77\n  dup1                          // [exp_word, exp_word, rindex, windex, table_start]\n  0x80 and                      // [not_use_mul, exp_word, rindex, windex, table_start]\n  swap1                         // [exp_word, not_use_mul, rindex, windex, table_start]\n  0x7f and                      // [exp, not_use_mul, rindex, windex, table_start]\n\n  swap2                         // [rindex, not_use_mul, exp_word, windex, table_start]\n  swap4                         // [table_start, not_use_mul, exp_word, windex, rindex]\n  swap1                         // [not_use_mul, table_start, exp_word, windex, rindex]\n  swap3                         // [windex, table_start, exp_word, not_use_mul, rindex]\n  swap2                         // [exp_word, table_start, windex, not_use_mul, rindex]\n\n  POW_10()                      // [windex, not_use_mul, rindex]\n\n  swap1                         // [not_use_mul, windex, rindex]\n\n  end_pow_10 jumpi              // [windex, rindex]\n\n    BACKREAD_SINGLE_VALUE()     // [pow_result, windex, rindex]\n    swap2                       // [rindex, windex, pow_result]\n    LOAD_1_BYTE()               // [mantissa, rindex, windex, pow_result]\n    swap1                       // [rindex, mantissa, windex, pow_result]\n    swap3                       // [pow_result, mantissa, windex, rindex]\n    mul                         // [(pow_result * mantissa), windex, rindex]\n    dup2                        // [windex, (pow_result * mantissa), windex, rindex]\n    mstore                      // [windex, rindex]\n    0x20                        // [0x20, windex, rindex]\n    add                         // [(0x20 + windex), rindex]\n\n  end_pow_10:                       // [windex, rindex]\n\n  // output stack: [windex, rindex]\n}\n\n#define macro POW_10() = takes (3) returns (1) {\n  // input stack: [exp, table_start, windex]\n\n  0x05         // [0x05, exp, table_start, windex]\n  shl          // [exp * 0x20, table_start, windex]\n\n  add          // [(table_start + exp * 0x20), windex]\n\n  0x20         // [0x20, (table_start + exp * 0x20), windex]\n  swap1        // [(table_start + exp * 0x20), 0x20, windex]\n  dup3         // [windex, (table_start + exp * 0x20), 0x20, windex]\n  codecopy     // [windex]\n\n  0x20 add     // [windex + 0x20]\n\n  // output stack: [windex]\n}\n\n#define macro READ_MIRROR_FLAG(nrfs) = takes (2) returns (2) {\n  // input stack: [windex, rindex]\n\n  swap1                         // [rindex, windex]\n  LOAD_DYNAMIC_SIZE(0x02, 0xf0) // [trindex, rindex, windex]\n  swap1                         // [rindex, trindex, windex]\n  swap2                         // [windex, trindex, rindex]\n\n  PERFORM_NESTED_READ_FLAG(<nrfs>) // [windex, trindex, rindex]\n  swap1                            // [trindex, windex, rindex]\n  pop                              // [windex, rindex]\n\n  // output stack: [windex, rindex]\n}\n\n#define macro READ_COPY_CALLDATA() = takes (2) returns (2) {\n  // input stack: [windex, rindex]\n\n  swap1                         // [rindex, windex]\n  LOAD_DYNAMIC_SIZE(0x02, 0xf0) // [location, rindex, windex]\n  swap1                         // [rindex, location, windex]\n\n  LOAD_1_BYTE()                 // [size, rindex, location, windex]\n\n  dup1                          // [size, size, rindex, location, windex]\n  swap3                         // [location, size, rindex, size, windex]\n  dup5                          // [windex, location, size, rindex, size, windex]\n  calldatacopy                  // [rindex, size, windex]\n\n  swap2                         // [windex, size, rindex]\n  add                           // [(windex + size), rindex]\n\n  // output stack: [windex, rindex]\n}\n\n#define macro READ_STORE_FLAG() = takes (2) returns (2) {\n  // input stack: [windex, rindex]\n\n  swap1                         // [rindex, windex]\n  LOAD_DYNAMIC_SIZE(0x02, 0xf0) // [trindex, rindex, windex]\n\n  dup1                          // [trindex, trindex, rindex, windex]\n  calldataload                  // [data[trindex], trindex, rindex, windex]\n  callvalue byte                // [flag, trindex, rindex, windex]\n\n  swap1                         // [trindex, flag, rindex, windex]\n  0x01 add                      // [trindex + 1, flag, rindex, windex]\n  swap1                         // [flag, trindex, rindex, windex]\n\n  // There are only two cases, either the flag\n  // is storing an address or storing a bytes32\n  // we are going to read the next 32 bytes or the next 20 bytes\n  \n  0x21               // [0x21, flag, trindex, rindex, windex]\n  eq                 // [(0x21 == flag), trindex, rindex, windex]\n  is_addr jumpi      // [trindex, rindex, windex]\n\n  // is_not_addr:\n    calldataload     // [word, rindex, windex]\n\n  end_if jump\n  is_addr:           // [trindex, rindex, windex]\n    calldataload     // [word, rindex, windex]\n    0x60 shr         // [word >> 0x60, rindex, windex]\n\n  end_if:\n\n  dup3               // [windex, word, rindex, windex]\n  mstore             // [rindex, windex]\n  swap1              // [windex, rindex]\n  0x20 add           // [windex + 0x20, rindex]\n\n  // output stack: [windex, rindex]\n}\n\n#define macro PERFORM_NESTED_READ_FLAG(nrfs) = takes(0) returns (0) {\n  // input stack: [windex, rindex]\n\n  swap1        // [rindex, windex]\n  back         // [back, rindex, windex]\n  swap2        // [windex, rindex, back]\n\n  <nrfs> jump // [windex, rindex]\n\n  back:\n}\n\n#define macro BACKREAD_SINGLE_VALUE() = takes (1) returns (2) {\n  // input stack: [windex]\n\n  0x20 swap1 sub // [windex - 0x20]\n  dup1           // [windex - 0x20, windex - 0x20]\n\n  mload          // [mem[windex - 0x20], windex - 0x20]\n\n  // output stack: [mem[windex - 0x20], windex - 0x20]\n}\n\n#define macro READ_NESTED_N_FLAGS_8(nrfs) = takes (2) returns (2) {\n  // input stack: [windex, rindex]\n\n  swap1         // [rindex, windex]\n  LOAD_1_BYTE() // [size, rindex, windex]\n\n  swap2       // [windex, rindex, size]\n  READ_NESTED_N_FLAGS(<nrfs>)\n\n  // output stack: [windex, rindex]\n}\n\n#define macro READ_NESTED_N_FLAGS_16(nrfs) = takes (2) returns (2) {\n  // input stack: [windex, rindex]\n\n  swap1       // [rindex, windex]\n  LOAD_DYNAMIC_SIZE(0x02, 0xf0)  // [size, rindex, windex]\n\n  swap2       // [windex, rindex, size]\n  READ_NESTED_N_FLAGS(<nrfs>)\n\n  // output stack: [windex, rindex]\n}\n\n#define macro READ_NESTED_N_FLAGS(nrfs) = takes (3) returns (2) {\n  // input stack: [windex, rindex, n]\n\n  callvalue     // [i, windex, rindex, n]\n\n  read_more:    // [i, windex, rindex, n]\n\n    swap2       // [rindex, windex, i, n]\n    swap1       // [windex, rindex, i, n]\n    \n    PERFORM_NESTED_READ_FLAG(<nrfs>) // [windex, rindex, i, n]\n\n    swap1                            // [rindex, windex, i, n]\n    swap2                            // [i, windex, rindex, n]\n\n    0x01 add                       // [i + 1, windex, rindex, n]\n\n    dup4                           // [n, i, windex, rindex, n]\n    dup2                           // [i, n, i, windex, rindex, n]\n    lt                             // [(i < n), i, windex, rindex, n]\n    \n    read_more jumpi                // [i, windex, rindex, n]\n\n  pop           // [windex, rindex, n]\n  swap2         // [n, rindex, windex]\n  pop           // [rindex, windex]\n  swap1         // [windex, rindex]\n\n  // output stack: [windex, rindex]\n}\n\n#define macro READ_CHAINED(nrfs, s_bytes, s_offset) = takes (2) returns (2) {\n  // input stack: [windex, rindex]\n\n  // First we need to write the chainId sequence prefix (0x03)\n  WRITE_SEQUENCE_FLAG(0x03)\n\n  // Second we need to read the number of flags we are going to read\n\n  callvalue                                 // [0x00, windex, rindex]\n  swap2                                     // [rindex, windex, 0x00]\n\n  LOAD_DYNAMIC_SIZE(<s_bytes>, <s_offset>)  // [size, rindex, windex, 0x00]\n\n  swap3                                     // [0x00, rindex, windex, size]\n  swap2                                     // [windex, rindex, 0x00, size]\n\n  read_more:  // [windex, rindex, i, size]\n\n    // Reserve 3 bytes of the size, and keep a copy of the windex\n    // one will serve as a comparation point to determine the size\n    // the other will be the pointer that determines where we need to store\n    // the size\n\n    // Clear the memory now, that way we don't need to make it later\n    callvalue // [0x00, windex, rindex, i, size]\n    dup2      // [windex, 0x00, windex, rindex, i, size]\n    mstore    // [windex, rindex, i, size]\n\n    // Create copies for windex, and advance 2 of them by 3\n    dup1      // [windex, windex, rindex, i, size]\n    0x03 add  // [windex, size_pointer, rindex, i, size]\n    dup1      // [windex, windex, size_pointer, rindex, i, size]\n\n    swap3     // [rindex, windex, size_pointer, windex, i, size]\n    swap1     // [windex, rindex, size_pointer, prev_windex, i, size]\n\n    PERFORM_NESTED_READ_FLAG(<nrfs>)  // [windex, rindex, size_pointer, prev_windex, i, size]\n\n    // Calculate the size and write it at the start\n\n    swap1                           // [rindex, windex, size_pointer, prev_windex, i, size]\n    swap3                           // [prev_windex, windex, size_pointer, rindex, i, size]\n    dup2                            // [windex, prev_windex, windex, size_pointer, rindex, i, size]\n    sub                             // [size, windex, size_pointer, rindex, i, size]\n    0xe8 shl                        // [size << 0xe8, windex, size_pointer, rindex, i, size]\n\n    dup3                            // [size_pointer, size, windex, size_pointer, rindex, i, size]\n    mload                           // [mload[size_pointer], size, windex, size_pointer, rindex, i, size]\n    or                              // [(mload[size_pointer] | size), windex, size_pointer, rindex, i, size]\n    \n    swap1                           // [windex, (mload[size_pointer] | size), size_pointer, rindex, i, size]\n    swap2                           // [size_pointer, (mload[size_pointer] | size), windex, rindex, i, size]\n    mstore                          // [windex, rindex, i, size]\n\n    swap2                           // [i, rindex, windex, size]\n    0x01 add                        // [i + 1, rindex, windex, size]\n    swap2                           // [windex, rindex, i + 1, size]\n\n    dup4                            // [size, windex, rindex, i + 1, size]\n    dup4                            // [i + 1, size, windex, rindex, i + 1, size]\n    lt                              // [(i + 1 < size), windex, rindex, i + 1, size]\n    read_more jumpi                 // [windex, rindex, i + 1, size]\n\n  // [windex, rindex, i + 1, size]\n\n  swap2       // [i, rindex, windex, size]\n  pop         // [rindex, windex, size]\n  swap2       // [size, windex, rindex]\n  pop         // [windex, rindex]\n\n  // output stack: [windex, rindex]\n}\n\n#define macro READ_DYNAMIC_SIGNATURE(nrfs) = takes (2) returns (2) {\n  // input stack: [windex, rindex]\n\n  WRITE_SEQUENCE_FLAG(0x02)\n\n  swap1  // [rindex, windex]\n\n  // Read the weight, it is always 1 byte\n  LOAD_1_BYTE()   // [weight, rindex, windex]\n\n  dup3      // [windex, weight, rindex, windex]\n  mstore8   // [rindex, windex]\n  swap1     // [windex, rindex]\n  0x01 add  // [windex, rindex]\n\n  // Read the address, use read flag as it may use a pointer\n  PERFORM_NESTED_READ_FLAG(<nrfs>)  // [windex, rindex]\n  BACKREAD_SINGLE_VALUE()         // [word, windex, rindex]\n  0x60 shl                        // [address, windex, rindex]\n\n  dup2                            // [windex, word, windex, rindex]\n  mstore                          // [windex, rindex]\n  0x14 add                        // [windex, rindex]\n\n  // Now read another nested flag, as-is, but leave\n  // the space to demark the size\n\n  // Clear the memory here, that way we don't need to mask it\n  // when we write the size\n  callvalue    // [0x00, windex, rindex]\n  dup2 mstore  // [windex, rindex]\n\n  // Reserve 3 bytes for the size\n  dup1     // [windex, size_pointer, rindex]\n  0x03 add // [windex, size_pointer, rindex]\n  \n  swap2    // [rindex, size_pointer, windex]\n  dup3     // [windex, rindex, size_pointer, prev_windex]\n\n  PERFORM_NESTED_READ_FLAG(<nrfs>)  // [windex, rindex, size_pointer, prev_windex]\n\n  // Need to go back and write the size now\n\n  swap3                           // [prev_windex, rindex, size_pointer, windex]\n  dup4                            // [windex, prev_windex, rindex, size_pointer, windex]\n  sub                             // [size, rindex, size_pointer, windex]\n\n  // The size is +1 since we also need to include the suffix \"03\" for EIP1271\n  0x01 add                        // [size + 1, rindex, size_pointer, windex]\n\n  0xe8 shl                        // [size << 0xe8, rindex, size_pointer, windex]\n  dup3                            // [size_pointer, size, rindex, size_pointer, windex]\n  mload                           // [mload[size_pointer], size, rindex, size_pointer, windex]\n  or                              // [(mload[size_pointer] | size), rindex, size_pointer, windex]\n  swap1                           // [rindex, (mload[size_pointer] | size), size_pointer, windex]\n  swap2                           // [size_pointer, (mload[size_pointer] | size), rindex, windex]\n  mstore                          // [rindex, windex]\n  swap1                           // [windex, rindex]\n\n  // Write the suffix, just 03\n  0x03                            // [0x03, windex, rindex]\n  dup2                            // [windex, 0x03, windex, rindex]\n  mstore8                         // [windex, rindex]\n  0x01 add                        // [windex, rindex]\n\n  // output stack: [windex, rindex]\n}\n\n#define macro READ_SIGNATURE_W0() = takes (2) returns (2) {\n  // input stack: [windex, rindex]\n\n  swap1           // [rindex, windex]\n\n  LOAD_1_BYTE()   // [weight, rindex, windex]\n\n  swap2           // [windex, rindex, weight]\n\n  READ_SIGNATURE()                // [windex, rindex]\n\n  // output stack: [windex, rindex]\n}\n\n#define macro READ_SIGNATURE_WX(weight) = takes (2) returns (2) {\n  // input stack: [windex, rindex]\n\n  swap1        // [rindex, windex]\n  <weight>     // [weight, rindex, windex]\n  swap2        // [windex, rindex, weight]\n\n  READ_SIGNATURE()                // [windex, rindex]\n\n  // output stack: [windex, rindex]\n}\n\n#define macro READ_SIGNATURE() = takes (3) returns (2) {\n  // input stack: [windex, rindex, weight]\n\n  WRITE_SEQUENCE_FLAG(0x00)\n\n  // Second thing we must write is the weight, always 1 byte\n\n  swap2         // [weight, rindex, windex]\n  dup3          // [windex, weight, rindex, windex]\n  mstore8       // [rindex, windex]\n  swap1         // [windex, rindex]\n  0x01 add      // [windex, rindex]\n\n  // EOA signatures are always 66 bytes long\n  // we can just copy them\n\n  0x42          // [0x42, windex, rindex]\n  dup1          // [0x42, 0x42, windex, rindex]\n\n  dup4          // [rindex, 0x42, 0x42, windex, rindex]\n  dup4          // [windex, rindex, 0x42, 0x42, windex, rindex]\n  calldatacopy  // [0x42, windex, rindex]\n\n  dup1          // [0x42, 0x42, windex, rindex]\n  swap3         // [rindex, 0x42, windex, 0x42]\n  add           // [rindex, windex, 0x42]\n  swap2         // [0x42, windex, rindex]\n  add           // [windex, rindex]\n\n  // output stack: [windex, rindex]\n}\n\n#define macro READ_ADDRESS_W0(nrfs) = takes (2) returns (2) {\n  // input stack: [windex, rindex]\n\n  swap1           // [rindex, windex]\n\n  LOAD_1_BYTE()   // [weight, rindex, windex]\n\n  swap2        // [windex, rindex, weight]\n\n  READ_ADDRESS(<nrfs>)            // [windex, rindex]\n\n  // output stack: [windex, rindex]\n}\n\n#define macro READ_ADDRESS_WX(nrfs, weight) = takes (2) returns (2) {\n  // input stack: [windex, rindex]\n\n  swap1        // [rindex, windex]\n  <weight>     // [weight, rindex, windex]\n  swap2        // [windex, rindex, weight]\n\n  READ_ADDRESS(<nrfs>) // [windex, rindex]\n\n  // output stack: [windex, rindex]\n}\n\n#define macro READ_ADDRESS(nrfs) = takes (3) returns (2) {\n  // input stack: [windex, rindex, weight]\n\n  WRITE_SEQUENCE_FLAG(0x01)\n\n  // Second thing we must write is the weight, always 1 byte\n\n  swap2         // [weight, rindex, windex]\n  dup3          // [windex, weight, rindex, windex]\n  mstore8       // [rindex, windex]\n  swap1         // [windex, rindex]\n  0x01 add      // [windex, rindex]\n\n  // Addresses are always 20 bytes long\n  // we use a nested read flag call, since this address\n  // could come from storage\n\n  PERFORM_NESTED_READ_FLAG(<nrfs>)  // [windex, rindex]\n  BACKREAD_SINGLE_VALUE()           // [word, windex, rindex]\n  0x60 shl                          // [address, windex, rindex]\n\n  dup2                            // [windex, word, windex, rindex]\n  mstore                          // [windex, rindex]\n\n  0x14 add                        // [windex + 0x14, rindex]\n}\n\n#define macro READ_NODE(nrfs) = takes (2) returns (2) {\n  // input stack: [windex, rindex]\n\n  WRITE_SEQUENCE_FLAG(0x03)\n\n  // Now we just proceed by reading another flag\n\n  PERFORM_NESTED_READ_FLAG(<nrfs>)  // [windex, rindex]\n\n  // output stack: [windex, rindex]\n}\n\n#define macro READ_BRANCH(nrfs) = takes (2) returns (2) {\n  // input stack: [windex, rindex]\n\n  WRITE_SEQUENCE_FLAG(0x04)\n\n  // Now we just proceed by reading the branch\n  // the only important part is that we need to\n  // measure how much is written, as the branch is\n  // always prefixed by the size\n\n  // Clear the memory here, that way we don't need to mask it\n  // when we write the size\n  callvalue    // [0x00, windex, rindex]\n  dup2 mstore  // [windex, rindex]\n\n  // Reserve 3 bytes for the size\n  dup1     // [windex, size_pointer, rindex]\n  0x03 add // [windex, size_pointer, rindex]\n  \n  swap2    // [rindex, size_pointer, windex]\n  dup3     // [windex, rindex, size_pointer, prev_windex]\n\n  PERFORM_NESTED_READ_FLAG(<nrfs>)  // [windex, rindex, size_pointer, prev_windex]\n\n  // Need to go back and write the size now\n\n  swap3                           // [prev_windex, rindex, size_pointer, windex]\n  dup4                            // [windex, prev_windex, rindex, size_pointer, windex]\n  sub                             // [size, rindex, size_pointer, windex]  \n  0xe8 shl                        // [size << 0xe8, rindex, size_pointer, windex]\n  dup3                            // [size_pointer, size, rindex, size_pointer, windex]\n  mload                           // [mload[size_pointer], size, rindex, size_pointer, windex]\n  or                              // [(mload[size_pointer] | size), rindex, size_pointer, windex]\n  swap1                           // [rindex, (mload[size_pointer] | size), size_pointer, windex]\n  swap2                           // [size_pointer, (mload[size_pointer] | size), rindex, windex]\n  mstore                          // [rindex, windex]\n  swap1                           // [windex, rindex]\n\n  // output stack: [windex, rindex]\n}\n\n#define macro READ_SUBDIGEST(nrfs) = takes (2) returns (2) {\n  // input stack: [windex, rindex]\n\n  WRITE_SEQUENCE_FLAG(0x05)\n\n  // Now we just proceed by reading another flag\n\n  PERFORM_NESTED_READ_FLAG(<nrfs>)  // [windex, rindex]\n\n  // output stack: [windex, rindex]\n}\n\n\n#define macro READ_NESTED(nrfs) = takes (2) returns (2) {\n  // input stack: [windex, rindex]\n\n  WRITE_SEQUENCE_FLAG(0x06) // [windex, rindex]\n\n  // Read the weight and the threshold\n  // the weight is always 1 byte, the threshold uses 2\n  // but in reality most Sequence wallets use 1, so this library\n  // only supports 1 byte for it. If the wallet is not compatible, READ_NESTED\n  // can't be used, and READ_N_BYTES must be used instead\n\n  swap1           // [rindex, windex]\n  LOAD_1_BYTE()   // [weight, rindex, windex]\n  swap1           // [rindex, weight, windex]\n  LOAD_1_BYTE()   // [threshold, rindex, weight, windex]\n  0xf0 shl        // [threshold << 0xf0, rindex, weight, windex]\n  swap2           // [weight, rindex, threshold, windex]\n\n  dup4            // [windex, weight, rindex, threshold, windex]\n  mstore8         // [rindex, threshold, windex]\n  swap2           // [windex, threshold, rindex]\n  0x01 add        // [windex, threshold, rindex]\n  swap1           // [threshold, windex, rindex]\n  dup2            // [windex, threshold, windex, rindex]\n  mstore          // [windex, rindex]\n  0x02 add        // [windex, rindex]\n\n  // Now we just proceed by reading the branch\n  // the only important part is that we need to\n  // measure how much is written, as the branch is\n  // always prefixed by the size\n\n  // Clear the memory here, that way we don't need to mask it\n  // when we write the size\n  callvalue    // [0x00, windex, rindex]\n  dup2 mstore  // [windex, rindex]\n\n  // Reserve 3 bytes for the size\n  dup1     // [windex, size_pointer, rindex]\n  0x03 add // [windex, size_pointer, rindex]\n  \n  swap2    // [rindex, size_pointer, windex]\n  dup3     // [windex, rindex, size_pointer, prev_windex]\n\n  PERFORM_NESTED_READ_FLAG(<nrfs>)  // [windex, rindex, size_pointer, prev_windex]\n\n  // Need to go back and write the size now\n\n  swap3                           // [prev_windex, rindex, size_pointer, windex]\n  dup4                            // [windex, prev_windex, rindex, size_pointer, windex]\n  sub                             // [size, rindex, size_pointer, windex]  \n  0xe8 shl                        // [size << 0xe8, rindex, size_pointer, windex]\n  dup3                            // [size_pointer, size, rindex, size_pointer, windex]\n  mload                           // [mload[size_pointer], size, rindex, size_pointer, windex]\n  or                              // [(mload[size_pointer] | size), rindex, size_pointer, windex]\n  swap1                           // [rindex, (mload[size_pointer] | size), size_pointer, windex]\n  swap2                           // [size_pointer, (mload[size_pointer] | size), rindex, windex]\n  mstore                          // [rindex, windex]\n  swap1                           // [windex, rindex]\n\n  // output stack: [windex, rindex]\n}\n\n#define macro WRITE_SEQUENCE_FLAG(flag) = takes (2) returns (2) {\n  // input stack: [windex, rindex]\n\n  <flag>        // [flag, windex, rindex]\n  dup2          // [windex, flag, windex, rindex]\n  mstore8       // [windex, rindex]\n  0x01 add      // [windex, rindex]\n\n  // output stack: [windex, rindex]\n}\n\n#define macro READ_SEQUENCE_SIGNATURE(nrfs, sig_flag, size_t, offset_t) = takes (2) returns (2) {\n  // input stack: [windex, rindex]\n\n  // Write the signature flag as-is\n\n  <sig_flag>   // [sig_flag, windex, rindex]\n  dup2         // [windex, sig_flag, windex, rindex]\n  mstore8      // [windex, rindex]\n  0x01 add     // [windex, rindex]\n\n  // Now read the threshold, it may be provided as 8 or 16 bits\n  // but we always write it using 16\n  swap1                                   // [rindex, windex]\n  LOAD_DYNAMIC_SIZE(<size_t>, <offset_t>) // [threshold, rindex, windex]\n  0xf0 shl                                // [threshold << 0xf0, rindex, windex]\n  dup3                                    // [windex, threshold << 0xf0, rindex, windex]\n  mstore                                  // [rindex, windex]\n  swap1                                   // [windex, rindex]\n  0x02 add                                // [windex, rindex]\n\n  // Next read the checkpoint, using read flag is overkill here\n  // but we do it for the sake of simplicity\n\n  PERFORM_NESTED_READ_FLAG(<nrfs>)      // [windex, rindex]\n  BACKREAD_SINGLE_VALUE()               // [checkpoint, windex, rindex]\n\n  // The checkpoint always uses 4 bytes\n  0xe0 shl                              // [checkpoint << 0xe0, windex, rindex]\n  dup2                                  // [windex, checkpoint, windex, rindex]\n  mstore                                // [windex, rindex]\n  0x04 add                              // [windex, rindex]\n\n  // Now read the rest of the tree, this is just another read flag\n  PERFORM_NESTED_READ_FLAG(<nrfs>)      // [windex, rindex]\n\n  // output stack: [windex, rindex]\n}\n\n#define macro READ_EXECUTE_STANDALONE() = takes (2) returns (2) {\n  skip jump\n  rf:\n  FN_READ_FLAG(rf)\n  skip:\n  READ_EXECUTE(rf)\n}\n\n#define macro READ_EXECUTE(nrfs) = takes (2) returns (2) {\n  // input stack: [windex, rindex]\n\n  // The execution function signature of Sequence is 0x7a9a1628\n\n  __RIGHTPAD(0x7a9a1628)  // [0x7a9a1628, windex, rindex]\n  dup2                    // [windex, 0x7a9a1628, windex, rindex]\n  mstore                  // [windex, rindex]\n  0x04 add                // [windex, rindex]\n\n  // The first value is always where do the list of transactions starts\n  // this is always the same, as the list of transactions is the first\n  // dynamic type\n\n  0x60        // [0x60, windex, rindex]\n  dup2        // [windex, 0x60, windex, rindex]\n  mstore      // [windex, rindex]\n  0x20 add    // [windex, rindex]\n\n  // Reading the nonce is the simplest one, it is just a value\n\n  READ_NONCE(<nrfs>)  // [windex, rindex]\n\n  // We can't know when the signature will start, since we need to\n  // read the list of transactions first. So we leave a copy of the pointer\n  // to write it later.\n  \n  swap1         // [rindex, windex]\n  dup2          // [windex, rindex, prev_windex]\n  0x20 add      // [windex, rindex, prev_windex]\n\n  // We start reading the transactions, the macro takes care of writting the\n  // internal pointers for them (and the number of transactions)\n\n  READ_TRANSACTIONS(<nrfs>) // [windex, rindex, prev_windex]\n\n  // The signature starts at windex - prev_windex + 0x20\n  // and the pointer needs to be written to prev_windex\n\n  swap1               // [rindex, windex, prev_windex]\n  swap2               // [prev_windex, windex, rindex]\n  dup1                // [prev_windex, prev_windex, windex, rindex]\n  dup3                // [windex, prev_windex, prev_windex, windex, rindex]\n  sub                 // [(windex - prev_windex), prev_windex, windex, rindex]\n  0x40 add            // [sig_starts, prev_windex, windex, rindex]\n  swap1               // [prev_windex, sig_starts, windex, rindex]\n\n  mstore              // [windex, rindex]\n\n  // Now we can read the signature, we just read a nested flag, it can generate\n  // a Sequence signature. We only need to take care of the size and the padding\n\n  0x20 add            // [windex, rindex]\n  swap1               // [rindex, windex]\n  dup2                // [windex, rindex, prev_index]\n\n  PERFORM_NESTED_READ_FLAG(<nrfs>)\n\n  swap1               // [rindex, windex, prev_windex]\n  swap2               // [prev_windex, windex, rindex]\n  dup1                // [prev_windex, prev_windex, windex, rindex]\n  dup3                // [windex, prev_windex, prev_windex, windex, rindex]\n  sub                 // [size, prev_windex, windex, rindex]\n  dup1                // [size, size, prev_windex, windex, rindex]\n  swap2               // [prev_windex, size, size, windex, rindex]\n  0x20 swap1 sub      // [size_place, size, size, windex, rindex]\n  mstore              // [size, windex, rindex]\n\n  // Last thing is handling the padding, bytes need to be multiple of 0x20\n\n  callvalue           // [0x00, size, windex, rindex]\n  dup3                // [windex, 0x00, size, windex, rindex]\n  mstore              // [size, windex, rindex]\n\n  0x1f and            // [size % 32, windex, rindex]\n  0x20 sub            // [pad_diff, windex, rindex]\n  0x1f and            // [pad_diff % 32, windex, rindex]\n  add                 // [(padd_diff + windex), rindex]\n\n  // output stack: [windex, rindex]\n}\n\n#define macro READ_NONCE_STANDALONE() = takes (2) returns (2) {\n  skip jump\n  rf:\n  FN_READ_FLAG(rf)\n  skip:\n  READ_NONCE(rf)\n}\n\n#define macro READ_NONCE(nrfs) = takes (2) returns (2) {\n  // input stack: [windex, rindex]\n\n  PERFORM_NESTED_READ_FLAG(<nrfs>) // [windex, rindex]\n  BACKREAD_SINGLE_VALUE()          // [val, windex, rindex]\n\n  0x60 shl                         // [space, windex, rindex]\n\n  swap2                            // [rindex, windex, space]\n  swap1                            // [windex, rindex, space]\n\n  PERFORM_NESTED_READ_FLAG(<nrfs>) // [windex, rindex, space]\n  BACKREAD_SINGLE_VALUE()          // [nonce, windex, rindex, space]\n\n  // Assume that we are reading the nonce already masked\n\n  swap1                            // [windex, nonce, rindex, space]\n  swap2                            // [rindex, nonce, windex, space]\n  swap3                            // [space, nonce, windex, rindex]\n  or                               // [(space | nonce), windex, rindex]\n\n  // Now we have the compact representation of the nonce\n  // we can write it to memory on windex\n\n  dup2                             // [windex, (space | nonce), windex, rindex]\n  mstore                           // [windex, rindex]\n\n  0x20 add                         // [windex + 0x20, rindex]\n\n  // output stack: [windex, rindex]\n}\n\n#define macro READ_TRANSACTIONS_STANDALONE() = takes (2) returns (2) {\n  skip jump\n  rf:\n  FN_READ_FLAG(rf)\n  skip:\n  READ_TRANSACTIONS(rf)\n}\n\n#define macro READ_TRANSACTIONS(nrfs) = takes (2) returns (2) {\n  // input stack: [windex, rindex]\n\n  dup2            // [rindex, windex, rindex]\n  calldataload    // [mem[rindex], windex, rindex]\n  callvalue byte  // [tx_num, windex, rindex]\n  swap2           // [rindex, windex, tx_num]\n  0x01 add        // [rindex + 1, windex, tx_num]\n  callvalue       // [i, rindex, windex, tx_num]\n\n  swap3         // [tx_num, rindex, windex, i]\n  swap2         // [windex, rindex, tx_num, i]\n\n  // Write the number of transactions\n\n  dup3         // [tx_num, windex, rindex, tx_num, i]\n  dup2         // [windex, tx_num, windex, rindex, tx_num, i]\n  mstore       // [windex, rindex, tx_num, i]\n  0x20 add     // [windex + 0x20, rindex, tx_num, i]\n\n  // Reserve 32 bytes for each tx (excluding the first one, as we already know)\n  // these will be used to store start of each tx\n\n  // The first transaction will always start at 0x20 * txs + 0x20\n\n  dup3         // [tx_num, windex, rindex, tx_num, i]\n  0x05 shl     // [r_start, windex, rindex, tx_num, i]\n  dup1         // [r_start, r_start, windex, rindex, tx_num, i]\n  \n  dup3         // [windex, r_start, r_start, ts_index, rindex, tx_num, i]\n  add          // [windex, r_start, ts_index, rindex, tx_num, i]\n  \n  swap3        // [rindex, r_start, ts_index, windex, tx_num, i]\n  dup4         // [windex, rindex, r_start, ts_index, windex, tx_num, i]\n\n  do_tx:       // [windex, rindex, pos, ts_index, windex, tx_num, i]\n\n    // store pos for this transaction\n    // but keep a copy of it as it will be used again\n\n    swap2         // [pos, rindex, windex, ts_index, windex, tx_num, i]\n    dup1          // [pos, pos, rindex, windex, ts_index, windex, tx_num, i]\n    dup5          // [ts_index, pos, pos, rindex, windex, ts_index, windex, tx_num, i]\n    mstore        // [pos, rindex, windex, ts_index, windex, tx_num, i]\n\n    swap3         // [ts_index, rindex, windex, pos, windex, tx_num, i]\n    0x20 add      // [ts_index, rindex, windex, pos, windex, tx_num, i]\n    \n    swap3         // [pos, rindex, windex, ts_index, windex, tx_num, i]\n    swap2         // [windex, rindex, pos, ts_index, windex, tx_num, i]\n\n    READ_TRANSACTION(<nrfs>) // [windex, rindex, pos, ts_index, prev_windex, tx_num, i]\n\n    // size = windex - prev_windex\n\n    swap4                    // [prev_windex, rindex, r_start, ts_index, windex, tx_num, i]\n    dup5                     // [windex, prev_windex, rindex, r_start, ts_index, windex, tx_num, i]\n    sub                      // [tx_i_size, rindex, r_start, ts_index, windex, tx_num, i]\n\n    // pos = size + r_start\n\n    swap1                    // [rindex, tx_i_size, r_start, ts_index, windex, tx_num, i]\n    swap2                    // [r_start, tx_i_size, rindex, ts_index, windex, tx_num, i]\n    add                      // [pos, rindex, ts_index, windex, tx_num, i]\n\n    // Re-arrange the stack, we are about to loop back\n\n    swap1                    // [rindex, pos, ts_index, windex, tx_num, i]\n    dup4                     // [windex, rindex, pos, ts_index, windex, tx_num, i]\n\n    // Check if we have more to read\n    swap6                    // [i, rindex, pos, ts_index, windex, tx_num, windex]\n    0x01 add                 // [i + 1, rindex, pos, ts_index, windex, tx_num, windex]\n    swap6                    // [windex, rindex, pos, ts_index, windex, tx_num, i]\n    dup7                     // [i, windex, rindex, pos, ts_index, windex, tx_num, i]\n\n    // The ts_index contains the index of the transaction x 32, we can\n    // easily get the i and compare it with the len of transactions to know if we must continue or not\n\n    dup7                     // [tx_num, i, windex, rindex, pos, ts_index, windex, tx_num, i]\n    xor do_tx jumpi          // [windex, rindex, pos, ts_index, windex, tx_num, i]\n\n  pop                        // [rindex, pos, ts_index, windex, tx_num, i]\n  swap5                      // [i, pos, ts_index, windex, tx_num, rindex]\n  pop                        // [pos, ts_index, windex, tx_num, rindex]\n  pop                        // [ts_index, windex, tx_num, rindex]\n  pop                        // [windex, tx_num, rindex]\n  swap1                      // [tx_num, windex, rindex]\n  pop                        // [windex, rindex]\n\n  // output stack: [windex, rindex]\n}\n\n#define macro READ_TRANSACTION_STANDALONE() = takes (2) returns (2) {\n  skip jump\n  rf:\n  FN_READ_FLAG(rf)\n  skip:\n  READ_TRANSACTION(rf)\n}\n\n#define macro READ_TRANSACTION(nrfs) = takes (2) returns (2) {\n  // input stack: [windex, rindex]\n\n  // The first byte gives us information about what the transaction contains\n\n  dup2            // [rindex, windex, rindex]\n  calldataload    // [cdata[rindex], windex, rindex]\n  callvalue byte  // [tflag, windex, rindex]\n  swap2           // [rindex, windex, tflag]\n  0x01 add        // [rindex + 1, windex, tflag]\n  swap2           // [tflag, windex, rindex + 1]\n\n  // First bit of the flag determines if the transaction uses delegateCall\n\n  dup1          // [tflag, tflag, windex, rindex]\n  0x07 shr      // [tflag >> 0x07, tflag, windex, rindex]\n  dup3          // [windex, tflag >> 0x07, tflag, windex, rindex]\n  mstore        // [tflag, windex, rindex]\n\n  swap1         // [windex, tflag, rindex]\n  0x20 add      // [windex + 0x20, tflag, rindex]\n\n  // Second bit of the flag determines if the transaction uses revertOnError\n\n  dup2          // [tflag, windex, tflag, rindex]\n  0x06 shr      // [tflag >> 0x06, windex, tflag, rindex]\n  0x01 and      // [tflag >> 0x06 & 0x01, windex, tflag, rindex]\n  dup2          // [windex, tflag >> 0x06 & 0x01, windex, tflag, rindex]\n  mstore        // [windex, tflag, rindex]\n\n  0x20 add      // [windex + 0x20, tflag, rindex]\n\n  // Third bit of the flag determines if the transaction has a defined gasLimit\n\n  dup2                // [tflag, windex, tflag, rindex]\n  0x05 shr            // [tflag >> 0x05, windex, tflag, rindex]\n  0x01 and            // [has_gas_limit, windex, tflag, rindex]\n  has_gas_limit jumpi // [windex, tflag, rindex]\n\n    // The transaction has no gas_limit, we still need to write 0s\n    // to the memory and push the write index\n\n    callvalue dup2 mstore // [windex, tflag, rindex]\n    0x20 add              // [windex + 0x20, tflag, rindex]\n\n    // Re-arrange the stack so it matches the other branch\n\n    swap1         // [tflag, windex, rindex]\n    swap2         // [rindex, windex, tflag]\n    swap1         // [windex, rindex, tflag]\n\n    end_gas_Limit_if jump\n\n  has_gas_limit:\n\n    // Read advanced; this should only increase 32 bytes\n    // but we don't check that, buyer beware\n\n    swap1         // [tflag, windex, rindex]\n    swap2         // [rindex, windex, tflag]\n    swap1         // [windex, rindex, tflag]\n\n    PERFORM_NESTED_READ_FLAG(<nrfs>)   // [windex, rindex, tflag]\n\n  end_gas_Limit_if:\n\n  // All transactions must define an address\n  // this is simple, as it is just one more flag\n\n  PERFORM_NESTED_READ_FLAG(<nrfs>)\n\n  // 4th bit of the flag determines if the transaction has a defined value\n\n  dup3               // [tflag, windex, rindex, tflag]\n  0x04 shr           // [tflag >> 0x04, windex, rindex, tflag]\n  0x01 and           // [tflag >> 0x04 & 0x01, windex, rindex, tflag]\n  has_value jumpi    // [windex, rindex, tflag]\n\n    // The transaction has no value, we still need to write 0s\n    // to the memory and push the write index\n\n    callvalue dup2 mstore   // [windex, rindex, tflag]\n    0x20 add                // [windex + 0x20, rindex, tflag]\n    end_value_if jump\n\n  has_value:\n\n    // Read advanced; this should only increase 32 bytes\n    // but we don't check that, buyer beware\n\n    PERFORM_NESTED_READ_FLAG(<nrfs>)   // [windex, rindex, tflag]\n  \n  end_value_if:\n\n  // 1st bit determines if the transaction has data\n\n  swap2           // [tflag, rindex, windex]\n  0x01 and        // [has_data, rindex, windex]  \n\n  swap1           // [rindex, has_data, windex]\n  swap2           // [windex, has_data, rindex]\n  swap1           // [has_data, windex, rindex]\n\n  has_data jumpi  // [windex, rindex]\n    \n    // The transaction has no data, we still need to write 0s\n    // both for the pointer and size\n\n    // All tx strucs have the same number of parameters, so 0xc0 is always the correct\n    //place for the start of the bytes data\n\n    0xc0          // [0xc0, windex, rindex]\n    dup2          // [windex, 0xc0, windex, rindex]\n    mstore        // [windex, rindex]\n\n    0x20          // [0x20, windex, rindex]\n    add           // [(0x20 + windex), rindex]\n    callvalue     // [0x00, (0x20 + windex), rindex]\n    dup2          // [(0x20 + windex), 0x00, (0x20 + windex), rindex]\n    mstore        // [windex, rindex]\n\n    0x20          // [0x20, windex, rindex]\n    add           // [(0x20 + windex), rindex]\n\n    end_data_if jump\n\n  has_data:       // [windex, rindex]\n\n    // All tx strucs have the same number of parameters, so 0xc0 is always the correct\n    //place for the start of the bytes data\n\n    0xc0          // [0xc0, windex, rindex]\n    dup2          // [windex, 0xc0, windex, rindex]\n    mstore        // [windex, rindex]\n    0x20 add      // [windex, rindex]\n\n    // Leave some room to store the size of the data\n    0x20 add      // [windex + 0x20, rindex, prev_windex]\n\n    swap1         // [rindex, windex]\n    dup2          // [windex, rindex, prev_windex]\n\n    PERFORM_NESTED_READ_FLAG(<nrfs>)   // [windex, rindex, prev_windex]\n\n    dup3          // [prev_windex, windex, rindex, prev_windex]\n    dup2          // [windex, prev_windex, windex, rindex, prev_windex]\n    sub           // [size, windex, rindex, prev_windex]\n\n    dup1          // [size, size, windex, rindex, prev_windex]\n\n    0x20          // [0x20, size, size, windex, rindex, prev_windex]\n    dup6          // [prev_windex, 0x20, size, size, windex, rindex, prev_windex]\n    sub           // [(prev_windex - 0x20), size, size, windex, rindex, prev_windex]\n    mstore        // [size, windex, rindex, prev_windex]\n\n    // Write some zeros just in case\n    callvalue     // [0x00, size, windex, rindex, prev_windex]\n    dup3          // [windex, 0x00, size, windex, rindex, prev_windex]\n    mstore        // [size, windex, rindex, prev_windex]\n\n    // Advance the windex enough so index becomes divisible by 32\n    0x1f and      // [size % 32, windex, rindex, prev_windex]\n    0x20 sub      // [pad_diff, windex, rindex, prev_windex]\n    0x1f and      // [pad_diff % 32, windex, rindex, prev_windex]\n    add           // [windex + pad_diff, rindex, prev_windex]\n\n    swap2         // [prev_windex, rindex, windex + pad_diff]\n    pop           // [rindex, windex]\n    swap1         // [windex, rindex]\n\n  end_data_if:\n}\n\n#define macro READ_SELF_EXECUTE(nrfs) = takes (2) returns (2) {\n  // input stack:  [windex, rindex]\n\n  // The SELF execution function signature of Sequence is 0x61c2926c\n\n  __RIGHTPAD(0x61c2926c)  // [0x61c2926c, windex, rindex]\n  dup2                    // [windex, 0x61c2926c, windex, rindex]\n  mstore                  // [windex, rindex]\n  0x04 add                // [windex, rindex]\n\n  // We need to write a single 0x20, this marks the position\n  // of the list of transactions. It is always the same.\n  0x20                    // [0x20, windex, rindex]\n  dup1                    // [0x20, 0x20, windex, rindex]\n  dup3                    // [windex, 0x20, 0x20, windex, rindex]\n  mstore                  // [0x20, windex, rindex]\n  add                     // [(0x20 + windex), rindex]\n\n  // Now we can just read the list of transactions\n  // the macro handles all internal pointers\n\n  READ_TRANSACTIONS(<nrfs>) // [windex, rindex]\n\n  // output stack:  [windex, rindex]\n}\n\n#define macro READ_ABI_4_BYTES() = takes (3) returns (2) {\n  // input stack:  [table_start, windex, rindex]\n\n  // Ideally we don't ask for table_start as an argument, we use it as a constant directly here\n  // but huff has a bug, if a table is used on a macro, and the macro is used many times, it will\n  // create copies of the table. This increases the file size a lot (2x) so I decided to have a single\n  // copy of the 4 bytes table.\n\n  swap2             // [rindex, windex, table_start]\n  LOAD_1_BYTE()     // [index, rindex, windex, table_start]\n\n  // The 4 bytes may be provided either as an index (1 byte)\n  // of the known 4bytes table, or as 00 and the real 4 bytes\n  // 90% of the transactions use one of the common 4bytes\n\n  dup1              // [index, index, rindex, windex, table_start]\n  is_index jumpi    // [index, rindex, windex, table_start]\n\n  // is_not_index:\n    pop             // [rindex, windex, table_start]\n    swap2           // [table_start, windex, rindex]\n    pop             // [windex, rindex]\n\n    0x04            // [0x04, windex, rindex]\n    dup1            // [0x04, 0x04, windex, rindex]\n    dup4            // [rindex, 0x04, 0x04, windex, rindex]\n    dup4            // [windex, rindex, 0x04, 0x04, windex, rindex]\n    calldatacopy    // [0x04, windex, rindex]\n\n    swap2           // [rindex, windex, 0x04]\n    dup3            // [0x04, rindex, windex, 0x04]\n    add             // [(0x04 + rindex), windex, 0x04]\n    swap2           // [0x04, windex, (0x04 + rindex)]\n    add             // [(0x04 + windex), (0x04 + rindex)]\n    end_if jump\n\n  is_index:         // [index, rindex, windex, table_start]\n    0x04            // [0x04, index, rindex, windex, table_start]\n    swap1           // [index, 0x04, rindex, windex, table_start]\n    0x02            // [0x02, index, 0x04, rindex, windex, table_start]\n    shl             // [(index << 0x02), 0x04, rindex, windex, table_start]\n\n    swap1           // [0x04, (index << 0x02), rindex, windex, table_start]\n    swap2           // [rindex, (index << 0x02), 0x04, windex, table_start]\n    swap4           // [table_start, (index << 0x02), 0x04, windex, rindex]    \n\n    add             // [(table_start + (index << 0x02)), 0x04, windex, rindex]\n    dup3            //  [windex, (table_start + (index << 0x02)), 0x04, windex, rindex]\n    codecopy        // [windex, rindex]\n  \n    0x04 add        // [windex + 0x04, rindex]\n\n  end_if:\n  \n  // output stack: [windex, rindex]\n}\n\n#define macro READ_ABI_0() = takes (3) returns (2) {\n  // input stack:  [table_start, windex, rindex]\n\n  READ_ABI_4_BYTES()\n\n  // output stack: [windex, rindex]\n}\n\n#define macro READ_ABI_1(nrfs) = takes (3) returns (2) {\n  // input stack:  [table_start, windex, rindex]\n\n  READ_ABI_4_BYTES()               // [windex, rindex]\n  PERFORM_NESTED_READ_FLAG(<nrfs>) // [windex, rindex]\n\n  // output stack: [windex, rindex]\n}\n\n#define macro READ_ABI_2(nrfs) = takes (3) returns (2) {\n  // input stack:  [table_start, windex, rindex]\n\n  READ_ABI_4_BYTES()               // [windex, rindex]\n  PERFORM_NESTED_READ_FLAG(<nrfs>) // [windex, rindex]\n  PERFORM_NESTED_READ_FLAG(<nrfs>) // [windex, rindex]\n\n  // output stack: [windex, rindex]\n}\n\n#define macro READ_ABI_3(nrfs) = takes (3) returns (2) {\n  // input stack:  [table_start, windex, rindex]\n\n  READ_ABI_4_BYTES()               // [windex, rindex]\n  PERFORM_NESTED_READ_FLAG(<nrfs>) // [windex, rindex]\n  PERFORM_NESTED_READ_FLAG(<nrfs>) // [windex, rindex]\n  PERFORM_NESTED_READ_FLAG(<nrfs>) // [windex, rindex]\n\n  // output stack: [windex, rindex]\n}\n\n#define macro READ_ABI_4(nrfs) = takes (3) returns (2) {\n  // input stack:  [table_start, windex, rindex]\n\n  READ_ABI_4_BYTES()               // [windex, rindex]\n  PERFORM_NESTED_READ_FLAG(<nrfs>) // [windex, rindex]\n  PERFORM_NESTED_READ_FLAG(<nrfs>) // [windex, rindex]\n  PERFORM_NESTED_READ_FLAG(<nrfs>) // [windex, rindex]\n  PERFORM_NESTED_READ_FLAG(<nrfs>) // [windex, rindex]\n\n  // output stack: [windex, rindex]\n}\n\n#define macro READ_ABI_5(nrfs) = takes (3) returns (2) {\n  // input stack:  [table_start, windex, rindex]\n\n  READ_ABI_4_BYTES()               // [windex, rindex]\n  PERFORM_NESTED_READ_FLAG(<nrfs>) // [windex, rindex]\n  PERFORM_NESTED_READ_FLAG(<nrfs>) // [windex, rindex]\n  PERFORM_NESTED_READ_FLAG(<nrfs>) // [windex, rindex]\n  PERFORM_NESTED_READ_FLAG(<nrfs>) // [windex, rindex]\n  PERFORM_NESTED_READ_FLAG(<nrfs>) // [windex, rindex]\n\n  // output stack: [windex, rindex]\n}\n\n#define macro READ_ABI_6(nrfs) = takes (3) returns (2) {\n  // input stack:  [table_start, windex, rindex]\n\n  READ_ABI_4_BYTES()               // [windex, rindex]\n  PERFORM_NESTED_READ_FLAG(<nrfs>) // [windex, rindex]\n  PERFORM_NESTED_READ_FLAG(<nrfs>) // [windex, rindex]\n  PERFORM_NESTED_READ_FLAG(<nrfs>) // [windex, rindex]\n  PERFORM_NESTED_READ_FLAG(<nrfs>) // [windex, rindex]\n  PERFORM_NESTED_READ_FLAG(<nrfs>) // [windex, rindex]\n  PERFORM_NESTED_READ_FLAG(<nrfs>) // [windex, rindex]\n\n  // output stack: [windex, rindex]\n}\n\n#define macro READ_ABI_DYNAMIC(nrfs) = takes(3) returns (2) {\n  // input stack:  [table_start, windex, rindex]\n\n  READ_ABI_4_BYTES() // [table_start, windex, rindex]\n\n  // First byte determines the number of parameters\n\n  dup1               // [windex, windex, rindex]\n  swap2              // [rindex, windex, windex]\n  dup1               // [rindex, rindex, windex, windex]\n  calldataload       // [word, rindex, windex, windex]\n  callvalue byte     // [size, rindex, windex, windex]\n  swap1              // [rindex, size, windex, windex]\n  0x01 add           // [rindex, size, windex, windex]\n\n  // Second is a bitmap, it determines which\n  // parameters are dynamic and which ones are static\n  // notice: this limits dynamic parameters to the first 8 ones\n  // all other ones are automatically considered static\n\n  dup1               // [rindex, rindex, size, windex, windex]\n  calldataload       // [word, rindex, size, windex, windex]\n  callvalue byte     // [d_bitmap, rindex, size, windex, windex]\n  swap1              // [rindex, d_bitmap, size, windex, windex]\n  0x01 add           // [rindex, d_bitmap, size, windex, windex]\n\n  callvalue          // [0x00, rindex, d_bitmap, size, windex]\n\n  // We will need to have two write indexes, one for the pointers (or values)\n  // and another one for the data blobs. The data blobs are the actual data\n  // of the dynamic parameters. It starts on (windex + size * 0x20).\n\n  dup5               // [windex, i, rindex, d_bitmap, size, windex, windex]\n  dup5               // [size, windex, i, rindex, d_bitmap, size, windex, windex]\n  0x05 shl           // [size * 0x20, windex, i, rindex, d_bitmap, size, windex, windex]\n  add                // [bwindex, i, rindex, d_bitmap, size, windex, windex]\n\n  read_param:        // [bwindex, i, rindex, d_bitmap, size, windex, swindex]\n\n    // We read the bitmap and determine if the param is dynamic or not\n    dup4             // [d_bitmap, bwindex, i, rindex, d_bitmap, size, windex, swindex]\n    0x01             // [0x01, d_bitmap, bwindex, i, rindex, d_bitmap, size, windex, swindex]\n    dup4             // [i, 0x01, d_bitmap, bwindex, i, rindex, d_bitmap, size, windex, swindex]\n    shl              // [(0x01 << i), d_bitmap, bwindex, i, rindex, d_bitmap, size, windex, swindex]\n    and              // [is_dynamic, bwindex, i, rindex, d_bitmap, size, windex, swindex]\n\n    is_dynamic jumpi // [bwindex, i, rindex, d_bitmap, size, windex, swindex]\n\n    // is_not_dynamic:\n\n      // The parameter is not dynamic, we just need to read one value on windex\n      // and we can continue\n\n      swap2          // [rindex, i, bwindex, d_bitmap, size, windex, swindex]\n      swap1          // [i, rindex, bwindex, d_bitmap, size, windex, swindex]\n      swap5          // [windex, rindex, bwindex, d_bitmap, size, i, swindex]\n\n      PERFORM_NESTED_READ_FLAG(<nrfs>) // [windex, rindex, bwindex, d_bitmap, size, i, swindex]\n\n      // We trust that we only increased windex by 0x20, and we keep iterating\n      swap5                            // [i, rindex, bwindex, d_bitmap, size, windex, swindex]\n      0x01 add                         // [i + 1, rindex, bwindex, d_bitmap, size, windex, swindex]\n\n      swap1                            // [rindex, i, bwindex, d_bitmap, size, windex, swindex]\n      swap2                            // [bwindex, i, rindex, d_bitmap, size, windex, swindex]\n      dup5                             // [size, bwindex, i, rindex, d_bitmap, size, windex, swindex]\n      dup3                             // [i, size, bwindex, i, rindex, d_bitmap, size, windex, swindex]\n      lt                               // [(i < size), bwindex, i, rindex, d_bitmap, size, windex, swindex]\n      read_param jumpi                 // [bwindex, i, rindex, d_bitmap, size, windex, swindex]\n      break jump\n\n    is_dynamic:      // [bwindex, i, rindex, d_bitmap, size, windex, swindex]\n\n      // The parameter is dynamic, we are going to write it on bwindex\n      // but we need to:\n      // - save the pointer, since there we are going to store the size\n      // - keep a copy of the pointer, so we can determine the size\n      // - pad the end result to 32 bytes\n      // - store in windex a pointer to bwindex\n\n      // The data pointer should lead to bwindex - swindex\n      dup7           // [swindex, bwindex, i, rindex, d_bitmap, size, windex, swindex]\n      dup2           // [bwindex, swindex, bwindex, i, rindex, d_bitmap, size, windex, swindex]\n      sub            // [d_pointer, bwindex, i, rindex, d_bitmap, size, windex, swindex]\n      dup7           // [windex, d_pointer, bwindex, i, rindex, d_bitmap, size, windex, swindex]\n      mstore         // [bwindex, i, rindex, d_bitmap, size, windex, swindex]\n      swap5          // [windex, i, rindex, d_bitmap, size, bwindex, swindex]\n      0x20 add       // [windex + 0x20, i, rindex, d_bitmap, size, bwindex, swindex]\n\n      dup6           // [bwindex, windex, i, rindex, d_bitmap, size, bwindex, swindex]\n      0x20 add       // [bwindex + 0x20, windex, i, rindex, d_bitmap, size, bwindex, swindex]\n      swap3          // [rindex, windex, i, bwindex, d_bitmap, size, size_b_pointer, swindex]\n      dup4           // [bwindex, rindex, windex, i, bwindex, d_bitmap, size, size_b_pointer, swindex]\n\n      PERFORM_NESTED_READ_FLAG(<nrfs>) // [bwindex, rindex, windex, i, prev_bwindex, d_bitmap, size, size_b_pointer, swindex]\n\n      swap4                            // [prev_bwindex, rindex, windex, i, bwindex, d_bitmap, size, size_b_pointer, swindex]\n      dup5                             // [bwindex, prev_bwindex, rindex, windex, i, bwindex, d_bitmap, size, size_b_pointer, swindex]\n      sub                              // [b_size, rindex, windex, i, bwindex, d_bitmap, size, size_b_pointer, swindex]\n\n      dup1                             // [b_size, b_size, rindex, windex, i, bwindex, d_bitmap, size, size_b_pointer, swindex]\n      swap8                            // [size_b_pointer, b_size, rindex, windex, i, bwindex, d_bitmap, size, b_size, swindex]\n      mstore                           // [rindex, windex, i, bwindex, d_bitmap, size, b_size, swindex]\n\n      // Last we need to pad the bwindex\n      callvalue                        // [0x00, rindex, windex, i, bwindex, d_bitmap, size, b_size]\n      dup5                             // [bwindex, 0x00, rindex, windex, i, bwindex, d_bitmap, size, b_size, swindex]\n      mstore                           // [rindex, windex, i, bwindex, d_bitmap, size, b_size, swindex]\n\n      swap3                            // [bwindex, windex, i, rindex, d_bitmap, size, b_size, swindex]\n      swap1                            // [windex, bwindex, i, rindex, d_bitmap, size, b_size, swindex]\n      swap6                            // [b_size, bwindex, i, rindex, d_bitmap, size, windex, swindex]\n\n      0x1f and                         // [b_size % 32, bwindex, i, rindex, d_bitmap, size, windex, swindex]\n      0x20 sub                         // [pad_diff, bwindex, i, rindex, d_bitmap, size, windex, swindex]\n      0x1f and                         // [pad_diff % 32, bwindex, i, rindex, d_bitmap, size, windex, swindex]\n      add                              // [bwindex, i, rindex, d_bitmap, size, windex, swindex]\n\n      swap1                            // [i, bwindex, rindex, d_bitmap, size, windex, swindex]\n      0x01 add                         // [i + 1, bwindex, rindex, d_bitmap, size, windex, swindex]\n      swap1                            // [bwindex, i, rindex, d_bitmap, size, windex, swindex]\n\n      dup5                             // [size, bwindex, i, rindex, d_bitmap, size, windex, swindex]\n      dup3                             // [i, size, bwindex, i, rindex, d_bitmap, size, windex, swindex]\n      lt                               // [(i < size), bwindex, i, rindex, d_bitmap, size, windex, swindex]\n      read_param jumpi                 // [bwindex, i, rindex, d_bitmap, size, windex, swindex]\n\n  break:\n\n  // We have finished! we only need to clear the stack\n  // notice that bwindex is the windex now\n  swap5                                // [windex, i, rindex, d_bitmap, size, bwindex, swindex]\n  pop                                  // [i, rindex, d_bitmap, size, bwindex, swindex]\n  pop                                  // [rindex, d_bitmap, size, bwindex, swindex]\n  swap4                                // [swindex, d_bitmap, size, bwindex, rindex]\n  pop                                  // [d_bitmap, size, bwindex, rindex]\n  pop                                  // [size, bwindex, rindex]\n  pop                                  // [bwindex, rindex]\n\n  // output stack: [windex, rindex]\n}\n\n#define macro READ_BYTES32(shift_bits, read_bytes) = takes (2) returns (2) {\n  // input stack: [windex, rindex]\n\n  0x20           // [0x20, windex, rindex]\n\n  dup3           // [rindex, 0x20, windex, rindex]\n  calldataload   // [word, 0x20, windex, rindex]\n\n  // Shift to the right so we only read the first <val> bits\n  <shift_bits> shr      // [word >> <val>, 0x20, windex, rindex]\n\n  // Store on windex\n  dup3           // [windex, word >> <val>, 0x20, windex, rindex]\n  mstore         // [0x20, windex, rindex]\n\n  add            // [windex + 0x20, rindex]\n\n  swap1             // [rindex, windex + 0x20]\n  <read_bytes> add  // [rindex + <val>, windex + 0x20]\n  swap1             // [windex + 0x20, rindex + <val>]\n\n  // output stack: [0x20 + windex, valB + rindex]\n}\n\n#define macro READ_BYTES32_WORD() = takes (3) returns (2) {\n  // input stack: [windex, rindex]\n\n  0x20           // [0x20, windex, rindex]\n  dup1           // [0x20, 0x20, windex, rindex]\n\n  dup4           // [rindex, 0x20, 0x20, windex, rindex]\n  calldataload   // [word, 0x20, 0x20, windex, rindex]\n\n  // Store on windex\n  dup4           // [windex, word >> <val>, 0x20, 0x20, windex, rindex]\n  mstore         // [0x20, 0x20, windex, rindex]\n\n  swap3          // [rindex, 0x20, 0x20, windex]\n  add            // [rindex + 0x20, 0x20, windex]\n  swap2          // [windex, 0x20, rindex + 0x20]\n  add            // [windex + 0x20, rindex + 0x20]\n\n  // output stack: [windex + 0x20, rindex + 0x20]\n}\n\n#define macro SAVE_ADDRESS() = takes (3) returns (2) {\n  // input stack: [windex, rindex]\n\n  dup2           // [rindex, windex, rindex]\n  calldataload   // [word, windex, rindex]\n\n  // Clean the address before storing it\n  // shifting it to the right by 0x60 bits\n\n  0x60 shr       // [addr, windex, rindex]\n\n  dup1           // [addr, addr, windex, rindex]\n  dup3           // [windex, addr, addr, windex, rindex]\n  mstore         // [addr, windex, rindex]\n\n  PULL_ADDRESS() ADDRESS_STORAGE_POINTER() sstore // [windex, rindex]\n\n  // Add 32 bytes to windex and 20 to rindex\n  0x20 add       // [windex + 0x20, rindex]\n  swap1          // [rindex, windex + 0x20]\n  0x14 add       // [rindex + 0x14, windex + 0x20]\n  swap1          // [windex + 0x20, rindex + 0x14]\n  \n  // output stack: [windex + 0x20, rindex + 0x14]\n}\n\n#define macro SAVE_BYTES32() = takes (3) returns (2) {\n  // input stack: [windex, rindex]\n\n  dup2           // [rindex, windex, rindex]\n  calldataload   // [word, windex, rindex]\n\n  dup1           // [word, word, windex, rindex]\n\n  dup3           // [windex, word, word, windex, rindex]\n  mstore         // [word, windex, rindex]\n\n  PULL_BYTES32() BYTES32_STORAGE_POINTER() sstore  // [windex, rindex]\n\n  // Add 32 bytes to both indexes\n\n  0x20 dup1  // [0x20, 0x20, windex, rindex]\n  swap3      // [rindex, 0x20, windex, 0x20]\n  add        // [rindex + 0x20, 0x20, windex]\n\n  swap2      // [windex, 0x20, rindex + 0x20]\n  add        // [windex + 0x20, rindex + 0x20]\n\n  // output stack: [windex + 32, rindex + 32]\n}\n\n// Reads a stored bytes32 using a 2 to 5 bytes pointer index\n#define macro READ_BYTES32_STORAGE(read_bytes, read_bits_shift) = takes (3) returns (2) {\n  READ_STORAGE(BYTES32_SMV, shl, <read_bytes>, <read_bits_shift>)\n}\n\n#define macro READ_ADDRESS_STORAGE(read_bytes, read_bits_shift) = takes (3) returns (2) {\n  READ_STORAGE(ADDRESS_SMV, add, <read_bytes>, <read_bits_shift>)\n}\n\n#define macro READ_STORAGE(smv, smc, read_bytes, read_bits_shift) = takes (3) returns (2) {\n  // input stack: [windex, rindex]\n\n  swap1 // [rindex, windex]\n\n  LOAD_DYNAMIC_SIZE(<read_bytes>, <read_bits_shift>) // [index, nrindex + size, windex]\n  <smv> <smc> sload                                  // [bytes32, nrindex + size, windex]\n\n  dup3      // [windex, bytes32, nrindex + size, windex]\n  mstore    // [nrindex + size, windex]\n\n  swap1     // [windex, nrindex + size]\n\n  0x20 add  // [windex + 0x20, nrindex + size]\n\n  // output stack: [windex + 0x20, nrindex + size]\n}\n\n#define macro READ_POW_2() = takes (2) returns (2) {\n  // input stack: [windex, rindex]\n\n  swap1         // [rindex, windex]\n\n  LOAD_1_BYTE() // [exp, rindex, windex]\n\n  // an exp value 0 means that the formula will be 2 ** exp - 1\n  // and we need to read a second exponent\n\n  dup1              // [exp, exp, rindex, windex]\n  dont_sub_1 jumpi  // [exp, rindex, windex]\n\n  //sub_1:\n    pop             // [rindex, windex]\n\n    LOAD_1_BYTE()   // [exp, rindex, windex]\n    \n    0x01            // [0x01, exp, rindex, windex]\n    dup1            // [0x01, 0x01, exp, rindex, windex]\n    swap2           // [exp, 0x01, 0x01, rindex, windex]\n    dup3            // [0x01, exp, 0x01, 0x01, rindex, windex]\n\n    add             // [(0x01 + exp), 0x01, 0x01, rindex, windex]\n    shl             // [(0x01 << (0x01 + exp)), 0x01, rindex, windex]\n    sub             // [((0x01 << (0x01 + exp)) - 0x01), rindex, windex]\n\n    end_if jump\n\n  dont_sub_1:\n    0x01            // [0x01, exp, rindex, windex]\n    swap1           // [exp, 0x01, rindex, windex]\n    shl             // [(0x01 << exp), rindex, windex]\n\n  end_if:\n\n  dup3              // [windex, (0x01 << exp), rindex, windex]\n  swap2             // [rindex, (0x01 << exp), windex, windex]\n  swap3             // [windex, (0x01 << exp), windex, rindex]\n\n  mstore            // [windex, rindex]\n  0x20 add          // [windex + 0x20, rindex]\n\n  // output stack: [windex, rindex]\n}\n\n#define macro READ_N_BYTES(nrfs) = takes (2) returns (2) {\n  // input stack: [windex, rindex]\n\n  PERFORM_NESTED_READ_FLAG(<nrfs>) // [windex, rindex]\n  BACKREAD_SINGLE_VALUE()          // [size, windex, rindex]\n\n  dup2          // [windex, size, windex, rindex + 1]\n  dup2 add      // [windex + size, size, windex, rindex + 1]\n  swap2         // [windex, size, windex + size, rindex + 1]\n\n  dup4          // [rindex + 1, windex, size, windex + size, rindex + 1]\n  dup3          // [size, rindex + 1, windex, size, windex + size, rindex + 1]\n  add           // [rindex + 1 + size, windex, size, windex + size, rindex + 1]\n  swap4         // [rindex + 1, windex, size, windex + size, rindex + 1 + size]\n  swap1         // [windex, rindex + 1, size, windex + size, rindex + 1 + size]\n\n  calldatacopy  // [windex, rindex + 1 + size]\n\n  // output stack: [windex + size, rindex + size]\n}\n\n#define macro LOAD_1_BYTE() = takes (1) returns (2) {\n  // input stack: [rindex]\n\n  dup1         // [rindex, rindex]\n  0x01         // [0x01, rindex, rindex]\n  add          // [(0x01 + rindex), rindex]\n  swap1        // [rindex, (0x01 + rindex)]\n\n  calldataload // [data[rindex], (0x01 + rindex)]\n\n  callvalue    // [0x00, data[rindex], (0x01 + rindex)]\n  byte         // [byte[0x00], (0x01 + rindex)]\n\n  // output stack: [value, rindex]\n}\n\n#define macro LOAD_DYNAMIC_SIZE(read_bytes, read_bits_shift) = takes (1) returns (2) {\n  // input stack: [rindex]\n\n  dup1               // [rindex, rindex]\n\n  <read_bytes> add   // [rindex + size, rindex]\n  swap1              // [rindex, rindex + size]\n\n  calldataload       // [cdata[rindex], rindex]\n\n  // Value needs to be shifted, so we only read\n  // the first \"size\" bytes\n\n  <read_bits_shift>  // [size, cdata[rindex], rindex]\n  shr                // [cdata[rindex] >> size bits, size + rindex]\n\n  // output stack: [cdata[rindex] >> size bits, size + rindex]\n}\n\n#[calldata(\"0xb2d10eb37ef5838bb835ea71bbd4053daf8de7bd8ecdf638451a2bc966a145a899\")]\n#define test TEST_SAVE_BYTES32() = {\n  0x01 // [rindex]\n  0x20 // [windex, rindex]\n\n  SAVE_BYTES32() // [windex, rindex]\n\n  0x40 eq ASSERT()    // []\n  0x21 eq ASSERT()    // [rindex]\n\n  // Validate that memory was written correctly\n\n  0x20 mload  // [mem[0x20]] ()\n  0xd10eb37ef5838bb835ea71bbd4053daf8de7bd8ecdf638451a2bc966a145a899\n  \n  eq ASSERT() // []\n\n  // Validate that the written address is correct\n  0x01 0x80 shl sload // [addr]\n  0xd10eb37ef5838bb835ea71bbd4053daf8de7bd8ecdf638451a2bc966a145a899 eq ASSERT() // []\n\n  // Validate that the total increased to 1\n  BYTES32_NUM() 0x01 eq ASSERT() // []\n}\n\n#[calldata(\"0x0201f020c002f1e0040203\")]\n#define test TEST_READ_BYTES32() = {\n  // Save 3 different bytes32 values\n\n  0xfe9716a384ec3b055bb8aae87323a14412cbfceb52c95324dccf071fb3f83855\n  0x0201 0x80 shl sstore\n\n  0xcf85e6408b0191a7ed9970e635257854b95aa7b708f485ae667e6fd467e5f45e\n  0xf020c002 0x80 shl sstore\n\n  0xa577e893e614c9aa4b19f2369e1c177adab9fe3156970a39afc166c0f2d905ee\n  0xf1e0040203 0x80 shl sstore\n\n  // Read the first bytes32\n  0x00                        // [rindex]\n  0x00                        // [windex, rindex]\n\n  READ_BYTES32_STORAGE(0x02, 0xf0) // [windex, rindex]\n\n  0x20 eq ASSERT() // [rindex]\n  0x02 eq ASSERT() // []\n\n  0x00 mload 0xfe9716a384ec3b055bb8aae87323a14412cbfceb52c95324dccf071fb3f83855 eq ASSERT() // []\n\n  // Read the second bytes32\n  0x02                                 // [rindex]\n  0x20                                 // [windex, rindex]\n\n  READ_BYTES32_STORAGE(0x04, 0xe0) // [windex, rindex]\n\n  0x40 eq ASSERT() // [rindex]\n  0x06 eq ASSERT() // []\n\n  0x20 mload 0xcf85e6408b0191a7ed9970e635257854b95aa7b708f485ae667e6fd467e5f45e eq ASSERT() // []\n\n  // Read the third bytes32\n  0x06                                 // [rindex]\n  0x10                                 // [windex, rindex]\n\n  READ_BYTES32_STORAGE(0x05, 0xd8) // [windex, rindex]\n\n  0x30 eq ASSERT() // [rindex]\n  0x0b eq ASSERT() // []\n\n  0x10 mload 0xa577e893e614c9aa4b19f2369e1c177adab9fe3156970a39afc166c0f2d905ee eq ASSERT() // []\n}\n\n#[calldata(\"0x0201f020c002f1e0040203\")]\n#define test TEST_READ_ADDRESS() = {\n  // Save 3 different bytes32 values\n\n  0x000000000000000000000000d789f5242a537b0584893b564a8c7a4be35b9238\n  0x0201 ADDRESS_STORAGE_POINTER() sstore\n\n  0x000000000000000000000000d5b5127436fd875ab7c334dffb62533ba011c2d9\n  0xf020c002 ADDRESS_STORAGE_POINTER() sstore\n\n  0x0000000000000000000000008a745d2b92c6e02e8ed087581c63d073f98f2479\n  0xf1e0040203 ADDRESS_STORAGE_POINTER() sstore\n\n  // Read the first bytes32\n  0x00                        // [rindex]\n  0x00                        // [windex, rindex]\n\n  READ_ADDRESS_STORAGE(0x02, 0xf0) // [windex, rindex]\n\n  0x20 eq ASSERT() // [rindex]\n  0x02 eq ASSERT() // []\n\n  0x00 mload 0x000000000000000000000000d789f5242a537b0584893b564a8c7a4be35b9238 eq ASSERT() // []\n\n  // Read the second bytes32\n  0x02                                 // [rindex]\n  0x20                                 // [windex, rindex]\n\n  READ_ADDRESS_STORAGE(0x04, 0xe0) // [windex, rindex]\n\n  0x40 eq ASSERT() // [rindex]\n  0x06 eq ASSERT() // []\n\n  0x20 mload 0x000000000000000000000000d5b5127436fd875ab7c334dffb62533ba011c2d9 eq ASSERT() // []\n\n  // Read the third bytes32\n  0x06                                 // [rindex]\n  0x10                                 // [windex, rindex]\n\n  READ_ADDRESS_STORAGE(0x05, 0xd8) // [windex, rindex]\n\n  0x30 eq ASSERT() // [rindex]\n  0x0b eq ASSERT() // []\n\n  0x10 mload 0x0000000000000000000000008a745d2b92c6e02e8ed087581c63d073f98f2479 eq ASSERT() // []\n}\n\n\n#[calldata(\"0x000203ff00ff\")]\n#define test TEST_READ_POW_2() = takes (2) returns (2) {\n  0x00 // [rindex]\n  0x00 // [windex, rindex]\n\n  READ_POW_2() // [windex, rindex]\n\n  0x20 eq ASSERT() // [rindex]\n  0x02 eq ASSERT() // []\n\n  0x00 mload 0x07 eq ASSERT() // []\n\n  0x01 // [rindex]\n  0x20 // [windex, rindex]\n\n  READ_POW_2() // [windex, rindex]\n\n  0x40 eq ASSERT() // [rindex]\n  0x02 eq ASSERT() // []\n\n  0x20 mload 0x04 eq ASSERT() // []\n\n  0x02 // [rindex]\n  0x05 // [windex, rindex]\n\n  READ_POW_2() // [windex, rindex]\n\n  0x25 eq ASSERT() // [rindex]\n  0x03 eq ASSERT() // []\n\n  0x05 mload 0x08 eq ASSERT() // []\n\n  0x03 // [rindex]\n  0x00 // [windex, rindex]\n\n  READ_POW_2() // [windex, rindex]\n\n  0x20 eq ASSERT() // [rindex]\n  0x04 eq ASSERT() // []\n\n  0x00 mload 0x8000000000000000000000000000000000000000000000000000000000000000 eq ASSERT() // []\n\n  0x04         // [rindex]\n  0x00         // [windex, rindex]\n\n  READ_POW_2()   // [windex, rindex]\n\n  0x20 eq ASSERT() // [rindex]\n  0x06 eq ASSERT() // []\n\n  0x00 mload 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff eq ASSERT() // []\n}\n\n#[calldata(\"0xb2d10eb37ef5838bb835ea71bbd4053daf8de7bd8ecdf638451a2bc966a145a899\")]\n#define test TEST_LOAD_DYNAMIC_SIZE() = {\n  0x00 // [rindex]\n\n  LOAD_DYNAMIC_SIZE(0x02, 0xf0) // [val, nrindex + size]\n\n  0xb2d1 eq ASSERT()    // [rindex]\n  0x02 eq ASSERT()      // []\n\n  0x04 // [rindex]\n\n  LOAD_DYNAMIC_SIZE(0x05, 0xd8) // [val, nrindex + size]\n\n  0x7ef5838bb8 eq ASSERT()    // [rindex]\n  0x09 eq ASSERT()            // []\n}\n\n#[calldata(\"0x02f1f2\")]\n#define test TEST_READ_FLAG_2_BYTES() = {\n  0x00  // [rindex]\n  [FMS] // [windex, rindex]\n\n  READ_FLAG() // [windex, rindex]\n\n  0x20 [FMS] add eq ASSERT() // [rindex]\n  0x03 eq ASSERT()           // []\n\n  [FMS] mload 0xf1f2 eq ASSERT() // []\n}\n\n#define test TEST_NUMS() = {\n  ADDRESSES_NUM()  // [num]\n  0x00 eq ASSERT() // []\n\n  PULL_ADDRESS()   // [nnum]\n  0x01 eq ASSERT() // []\n\n  ADDRESSES_NUM()  // [num]\n  0x01 eq ASSERT() // []\n\n  PULL_ADDRESS()   // [nnum]\n  0x02 eq ASSERT() // []\n\n  ADDRESSES_NUM()  // [num]\n  0x02 eq ASSERT() // []\n\n  PULL_BYTES32()   // [nnum]\n  0x01 eq ASSERT() // []\n\n  BYTES32_NUM()    // [num]\n  0x01 eq ASSERT() // []\n\n  ADDRESSES_NUM()  // [num]\n  0x02 eq ASSERT() // []\n\n  PULL_ADDRESS()   // [nnum]\n  0x03 eq ASSERT() // []\n\n  BYTES32_NUM()   // [nnum]\n  0x01 eq ASSERT() // []\n\n  PULL_BYTES32()   // [nnum]\n  0x02 eq ASSERT() // []\n\n  BYTES32_NUM()    // [num]\n  0x02 eq ASSERT() // []\n\n  ADDRESSES_NUM()  // [num]\n  0x03 eq ASSERT() // []\n}\n\n#[calldata(\"0xb2d10eb37ef5838bb835ea71bbd4053daf8de7bd8ecdf638451a2bc966a145a8\")]\n#define test TEST_FLAG_READ_BYTES32() = {\n  0x01            // [rindex]\n  [FMS] 0x40 add  // [windex, rindex]\n\n  READ_BYTES32(0xf0, 0x02)      // [windex, rindex]\n\n  [FMS] 0x60 add eq ASSERT()    // [rindex]\n  0x03 eq ASSERT()                            // []\n\n  [FMS] 0x40 add mload 0xd10e eq ASSERT() // []\n\n  0x00  // [rindex]\n  [FMS] // [windex, rindex]\n\n  READ_BYTES32(0x00, 0x20) // [windex, rindex]\n\n  [FMS] 0x20 add eq ASSERT()    // [rindex]\n  0x20 eq ASSERT()              // []\n\n  [FMS] mload 0xb2d10eb37ef5838bb835ea71bbd4053daf8de7bd8ecdf638451a2bc966a145a8 eq ASSERT() // []\n\n  0x00           // [rindex]\n  [FMS] 0x40 add // [windex, rindex]\n\n  READ_BYTES32_WORD() // [windex, rindex]\n\n  [FMS] 0x60 add eq ASSERT()    // [rindex]\n  0x20 eq ASSERT()              // []\n\n  [FMS] 0x40 add\n  mload 0xb2d10eb37ef5838bb835ea71bbd4053daf8de7bd8ecdf638451a2bc966a145a8 eq ASSERT() // []\n}\n\n// 0xd10eb37ef5838bb835ea71bbd4053daf8de7bd8e\n#[calldata(\"0xb2d10eb37ef5838bb835ea71bbd4053daf8de7bd8ecdf638451a2bc966a145a8\")]\n#define test TEST_SAVE_ADDRESS() = {\n  0x01  // [rindex]\n  [FMS] // [windex, rindex]\n\n  SAVE_ADDRESS() // [windex, rindex]\n\n  [FMS] 0x20 add eq ASSERT() // [rindex]\n  0x15 eq ASSERT()           // []\n\n  // Validate that memory was written correctly\n\n  [FMS] mload  // [mem[0x20]] ()\n  0x000000000000000000000000d10eb37ef5838bb835ea71bbd4053daf8de7bd8e\n  eq ASSERT()  // []\n\n  // Validate that the written address is correct\n  0x02 sload // [addr]\n  0x000000000000000000000000d10eb37ef5838bb835ea71bbd4053daf8de7bd8e\n  eq ASSERT() // []\n\n  // Validate that the total increased to 1\n  ADDRESSES_NUM() 0x01 eq ASSERT() // []\n}\n\n#define table POW_10_TABLE {\n  0x0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000006400000000000000000000000000000000000000000000000000000000000003e8000000000000000000000000000000000000000000000000000000000000271000000000000000000000000000000000000000000000000000000000000186a000000000000000000000000000000000000000000000000000000000000f424000000000000000000000000000000000000000000000000000000000009896800000000000000000000000000000000000000000000000000000000005f5e100000000000000000000000000000000000000000000000000000000003b9aca0000000000000000000000000000000000000000000000000000000002540be400000000000000000000000000000000000000000000000000000000174876e800000000000000000000000000000000000000000000000000000000e8d4a51000000000000000000000000000000000000000000000000000000009184e72a00000000000000000000000000000000000000000000000000000005af3107a400000000000000000000000000000000000000000000000000000038d7ea4c68000000000000000000000000000000000000000000000000000002386f26fc10000000000000000000000000000000000000000000000000000016345785d8a00000000000000000000000000000000000000000000000000000de0b6b3a76400000000000000000000000000000000000000000000000000008ac7230489e800000000000000000000000000000000000000000000000000056bc75e2d6310000000000000000000000000000000000000000000000000003635c9adc5dea0000000000000000000000000000000000000000000000000021e19e0c9bab240000000000000000000000000000000000000000000000000152d02c7e14af680000000000000000000000000000000000000000000000000d3c21bcecceda1000000000000000000000000000000000000000000000000084595161401484a00000000000000000000000000000000000000000000000052b7d2dcc80cd2e40000000000000000000000000000000000000000000000033b2e3c9fd0803ce80000000000000000000000000000000000000000000000204fce5e3e250261100000000000000000000000000000000000000000000001431e0fae6d7217caa0000000000000000000000000000000000000000000000c9f2c9cd04674edea40000000000000000000000000000000000000000000007e37be2022c0914b268000000000000000000000000000000000000000000004ee2d6d415b85acef8100000000000000000000000000000000000000000000314dc6448d9338c15b0a00000000000000000000000000000000000000000001ed09bead87c0378d8e6400000000000000000000000000000000000000000013426172c74d822b878fe8000000000000000000000000000000000000000000c097ce7bc90715b34b9f1000000000000000000000000000000000000000000785ee10d5da46d900f436a000000000000000000000000000000000000000004b3b4ca85a86c47a098a22400000000000000000000000000000000000000002f050fe938943acc45f655680000000000000000000000000000000000000001d6329f1c35ca4bfabb9f561000000000000000000000000000000000000000125dfa371a19e6f7cb54395ca000000000000000000000000000000000000000b7abc627050305adf14a3d9e40000000000000000000000000000000000000072cb5bd86321e38cb6ce6682e8000000000000000000000000000000000000047bf19673df52e37f2410011d100000000000000000000000000000000000002cd76fe086b93ce2f768a00b22a0000000000000000000000000000000000001c06a5ec5433c60ddaa16406f5a400000000000000000000000000000000000118427b3b4a05bc8a8a4de845986800000000000000000000000000000000000af298d050e4395d69670b12b7f41000000000000000000000000000000000006d79f82328ea3da61e066ebb2f88a0000000000000000000000000000000000446c3b15f9926687d2c40534fdb5640000000000000000000000000000000002ac3a4edbbfb8014e3ba83411e915e8000000000000000000000000000000001aba4714957d300d0e549208b31adb10000000000000000000000000000000010b46c6cdd6e3e0828f4db456ff0c8ea00000000000000000000000000000000a70c3c40a64e6c51999090b65f67d92400000000000000000000000000000006867a5a867f103b2fffa5a71fba0e7b680000000000000000000000000000004140c78940f6a24fdffc78873d4490d2100000000000000000000000000000028c87cb5c89a2571ebfdcb54864ada834a00000000000000000000000000000197d4df19d605767337e9f14d3eec8920e400000000000000000000000000000fee50b7025c36a0802f236d04753d5b48e800000000000000000000000000009f4f2726179a224501d762422c946590d91000000000000000000000000000063917877cec0556b21269d695bdcbf7a87aa0000000000000000000000000003e3aeb4ae1383562f4b82261d969f7ac94ca40000000000000000000000000026e4d30eccc3215dd8f3157d27e23acbdcfe680000000000000000000000000184f03e93ff9f4daa797ed6e38ed64bf6a1f0100000000000000000000000000f316271c7fc3908a8bef464e3945ef7a25360a000000000000000000000000097edd871cfda3a5697758bf0e3cbb5ac5741c640000000000000000000000005ef4a74721e864761ea977768e5f518bb6891be8000000000000000000000003b58e88c75313ec9d329eaaa18fb92f75215b1710000000000000000000000025179157c93ec73e23fa32aa4f9d3bda934d8ee6a0000000000000000000000172ebad6ddc73c86d67c5faa71c245689c107950240000000000000000000000e7d34c64a9c85d4460dbbca87196b61618a4bd216800000000000000000000090e40fbeea1d3a4abc8955e946fe31cdcf66f634e10000000000000000000005a8e89d75252446eb5d5d5b1cc5edf20a1a059e10ca000000000000000000003899162693736ac531a5a58f1fbb4b746504382ca7e40000000000000000000235fadd81c2822bb3f07877973d50f28bf22a31be8ee8000000000000000000161bcca7119915b50764b4abe86529797775a5f1719510000000000000000000dd15fe86affad91249ef0eb713f39ebeaa987b6e6fd2a0000000000000000000\n}\n\n#define table COMMON_4BYTES {\n  0x00000000a9059cbb095ea7b37ff36ab538ed173918cbafe5202ee0edfb3bdb41e2bbb158ab834bab6ea056a923b872dda68a76cc5f5755298803dbeea22cb465c89e43612da0340990411a321cff79cd223da1ba2e1a7d4df305d71939125215d0e30db0f7654176a694fc3a1a695230b6b55f25791ac94764887334c658695c441a3e704f1d48324a25d94aa454dfa9c18a84bce2b39746178979aec9807539ddd81f82a8a41c70cf557fe33d18b9129cec63924ab0d1900dcd7a6cd9627aa49149bafe672a9400dfbe4a31c6bf3262f242432ae5ab4da240c10f192e7ba6efc23e1a211aa3a008d6b347f7ded9382a00000003e9fad8eefaebafa8ae169a50e8e3370041fe00a0fa558b712e95b6c8c48fdfca000000006a80c33f627dd56a5c11d7954946e2065e83b463ca722cdcfb90b32000000008f7c1e582a32fe0a1db006a75000000010002191ce6d66ac8a0712d685d5d442296aa7368d3392ddf0ea5812fa5d754d1d29dff129979ef457901451ca64f797659d667a500032587865a6b4f379607f57c02520082d2697fc11695488758a5f34e71d92d9ddd67ba183d4e0b8f69c188e3dec8fbedc9af952d2da8066a761202a9b1d507ca120b1ff14fcbc8961c9ae442842e0e2195995c94b918de608060405174e8532505c3d98568523a0e89439bd149d05cefef39a14ce6931a000225879bfcb236415565b0454a2ab3ce558087f7a1696342966c688b4cb0ec4faa8a26e4a76726e8eda9df1519cdeb356282bfe17376b5009952eb3d7989fe34b0793b38bcdfc0f053566e02751cecc01a8c84f463e18e3cd18ca029ada03907d6b3483805550fa59f3e0c89bbb8b2c5ebeaec4997adb6f5e54063761610fcb88a802f3ccfd60b2e2d726ca4202615b44848f51610ca95bcf64e0579b177ec22895118ed436a474d474898c0f4ed31b967cb0ca6e158f8db7fd4089120491ca415bcad8201aa3f6e5110ae5312ea8e3df02124b77d239ba67a6a45156e29f6241735bbd017e8c73f7658fd86b2ecc4c44193c39bc12042d96a094a13d98d135d4c66a3ad4451a32e17de789ec9b36be47d166cbfff3b87f884e54a0b020003ad58bdd147e7ef24bad42590c8fd6ed002032587c6427474f6162b01baa2abde1ff013f11846eac55915d806f6aa658b00024a9c564a515869328dec4454b20df5298aca853828b6f06427e5b6b4af05f3fef3a352a438b81249c58bfeab2e5af9d83bb568c2c5fb02022587d586d8e0db254e5005eec2890e7527028f4af52f6a627842508c1dbd0f694584a6417ed63049105d1e9a6950d9caed120103258748d5c7e3be389d577430e0c649b780f00af49149d508e6238e1e280cae47bea8683fa88d5db3b4df1e83409a852a12e3c2998238343009a2daa6d5560f0439589c1298a06aa1e6d24d559317\n}\n"
  },
  {
    "path": "src/imps/L2CompressorImps.huff",
    "content": "#include \"../L2CompressorLib.huff\"\n\n#define function testLoadDynamicSize(bytes32 _a, bytes32 _b, uint256, uint256) view returns (uint256, uint256, bytes32)\n#define function testReadBytes32(bytes32 _a, bytes32 _b, uint256, uint256, uint256) view returns (uint256, uint256)\n\n// Function Dispatching\n#define macro MAIN() = takes (1) returns (1) {\n  // Identify which function is being called.\n  0x00 calldataload 0xE0 shr          // [func_sig]\n\n  dup1 __FUNC_SIG(testLoadDynamicSize)             eq testLoadDynamicSize           jumpi\n  dup1 __FUNC_SIG(testReadBytes32)                 eq testReadBytes32               jumpi\n\n  // Revert if no match is found.\n  0x00 dup1 revert\n\n  testLoadDynamicSize:\n    IMP_LOAD_DYNAMIC_SIZE()\n\n  testReadBytes32:\n    IMP_READ_BYTES32()\n}\n\n#define macro IMP_LOAD_DYNAMIC_SIZE() = takes (2) returns (0) {\n  0x04 0x40 add calldataload    // [rindex]\n  0x04 0x60 add calldataload    // [size, rindex]\n\n  LOAD_DYNAMIC_SIZE()  // [size bits, rindex + size]\n\n  0x00 mstore          // [rindex + size]\n  0x20 mstore          // []\n\n  0x40 0x00 return\n}\n\n#define macro IMP_READ_BYTES32() = takes (3) returns (2) {\n  0x04 0x40 add calldataload    // [rindex]\n  0x04 0x60 add calldataload    // [windex, rindex]\n  0x04 0x80 add calldataload    // [flag, windex, rindex]\n\n  READ_BYTES32()  // [windex, rindex]\n\n  0x00 mstore          // [rindex]\n  0x20 mstore          // []\n\n  0x04 0x60 add calldataload // [windex]\n  mload                      // [written]\n\n  0x40 mstore          // []\n\n  0x60 0x00 return\n}"
  },
  {
    "path": "src/imps/L2CompressorReadExecute.huff",
    "content": "#include \"../L2CompressorLib.huff\"\n\n#define constant FMS = 0xa0\n\n// Function Dispatching\n#define macro MAIN() = takes (1) returns (1) {\n  // readAdvanced with whatever calldata is passed\n  // first 32 bytes returns the new rindex and the next 32 bytes returns the new windex\n\n  0x00           // [rindex]\n  [FMS]          // [windex, rindex]\n\n  READ_EXECUTE_STANDALONE() // [windex, rindex]\n\n  [FMS]          // [0xa0, windex, rindex]\n  dup2           // [windex, 0xa0, windex, rindex]\n  sub            // [len, windex, rindex]\n\n  swap2          // [rindex, windex, len]\n\n  0x80 [FMS] sub mstore   // [windex, len]\n  0x60 [FMS] sub mstore   // [len]\n\n  0x60 0x40 [FMS] sub mstore  // [len]\n  dup1 0x20 [FMS] sub mstore  // [len]\n\n  0x80 add      // [len + 0x80]\n\n  0x80 [FMS] sub return\n}\n"
  },
  {
    "path": "src/imps/L2CompressorReadFlag.huff",
    "content": "#include \"../L2CompressorLib.huff\"\n\n#define constant FMS = 0xa0\n\n// Function Dispatching\n#define macro MAIN() = takes (1) returns (1) {\n  // readAdvanced with whatever calldata is passed\n  // first 32 bytes returns the new rindex and the next 32 bytes returns the new windex\n\n  0x00          // [rindex]\n  [FMS]         // [windex, rindex]\n\n  READ_FLAG()   // [windex, rindex]\n\n  [FMS]         // [0xa0, windex, rindex]\n  dup2          // [windex, 0xa0, windex, rindex]\n  sub           // [len, windex, rindex]\n\n  swap2         // [rindex, windex, len]\n\n  0x80 [FMS] sub mstore   // [windex, len]\n  0x60 [FMS] sub mstore   // [len]\n\n  0x60 0x40 [FMS] sub mstore  // [len]\n  dup1 0x20 [FMS] sub mstore  // [len]\n\n  0x80 add      // [len + 0x80]\n\n  0x80 [FMS] sub return\n}\n"
  },
  {
    "path": "src/imps/L2CompressorReadNonce.huff",
    "content": "#include \"../L2CompressorLib.huff\"\n\n#define constant FMS = 0xa0\n\n// Function Dispatching\n#define macro MAIN() = takes (1) returns (1) {\n  // readAdvanced with whatever calldata is passed\n  // first 32 bytes returns the new rindex and the next 32 bytes returns the new windex\n\n  0x00          // [rindex]\n  [FMS]         // [windex, rindex]\n\n  READ_NONCE_STANDALONE()   // [windex, rindex]\n\n  [FMS]         // [0xa0, windex, rindex]\n  dup2          // [windex, 0xa0, windex, rindex]\n  sub           // [len, windex, rindex]\n\n  swap2         // [rindex, windex, len]\n\n  0x80 [FMS] sub mstore   // [windex, len]\n  0x60 [FMS] sub mstore   // [len]\n\n  0x60 0x40 [FMS] sub mstore  // [len]\n  dup1 0x20 [FMS] sub mstore  // [len]\n\n  0x80 add      // [len + 0x80]\n\n  0x80 [FMS] sub return\n}\n"
  },
  {
    "path": "src/imps/L2CompressorReadTx.huff",
    "content": "#include \"../L2CompressorLib.huff\"\n\n#define constant FMS = 0xa0\n\n// Function Dispatching\n#define macro MAIN() = takes (1) returns (1) {\n  // readAdvanced with whatever calldata is passed\n  // first 32 bytes returns the new rindex and the next 32 bytes returns the new windex\n\n  0x00          // [rindex]\n  [FMS]         // [windex, rindex]\n\n  READ_TRANSACTION_STANDALONE()   // [windex, rindex]\n\n  [FMS]         // [0xa0, windex, rindex]\n  dup2          // [windex, 0xa0, windex, rindex]\n  sub           // [len, windex, rindex]\n\n  swap2         // [rindex, windex, len]\n\n  0x80 [FMS] sub mstore   // [windex, len]\n  0x60 [FMS] sub mstore   // [len]\n\n  0x60 0x40 [FMS] sub mstore  // [len]\n  dup1 0x20 [FMS] sub mstore  // [len]\n\n  0x80 add      // [len + 0x80]\n\n  0x80 [FMS] sub return\n}\n"
  },
  {
    "path": "src/imps/L2CompressorReadTxs.huff",
    "content": "#include \"../L2CompressorLib.huff\"\n\n#define constant FMS = 0xa0\n\n// Function Dispatching\n#define macro MAIN() = takes (1) returns (1) {\n  // readAdvanced with whatever calldata is passed\n  // first 32 bytes returns the new rindex and the next 32 bytes returns the new windex\n\n  0x00          // [rindex]\n  [FMS]         // [windex, rindex]\n\n  READ_TRANSACTIONS_STANDALONE()   // [windex, rindex]\n\n  [FMS]         // [0xa0, windex, rindex]\n  dup2          // [windex, 0xa0, windex, rindex]\n  sub           // [len, windex, rindex]\n\n  swap2         // [rindex, windex, len]\n\n  0x80 [FMS] sub mstore   // [windex, len]\n  0x60 [FMS] sub mstore   // [len]\n\n  0x60 0x40 [FMS] sub mstore  // [len]\n  dup1 0x20 [FMS] sub mstore  // [len]\n\n  0x80 add      // [len + 0x80]\n\n  0x80 [FMS] sub return\n}\n"
  },
  {
    "path": "test/ChainedSignatures.spec.ts",
    "content": "import { expect } from 'chai'\nimport { ethers } from 'ethers'\nimport { expectToBeRejected } from './utils'\nimport { deploySequenceContext, SequenceContext } from './utils/contracts'\nimport { SequenceWallet } from './utils/wallet'\n\ncontract('Chained signatures', (accounts: string[]) => {\n  let context: SequenceContext\n\n  let wallet: SequenceWallet\n  let typeHash: string\n\n  before(async () => {\n    context = await deploySequenceContext()\n  })\n\n  beforeEach(async () => {\n    wallet = SequenceWallet.basicWallet(context)\n    await wallet.deploy()\n    typeHash = await wallet.mainModule.SET_IMAGE_HASH_TYPE_HASH()\n  })\n\n  const chain = (top: string, ...rest: { sig: string }[]) => {\n    const encodedRest = ethers.solidityPacked(\n      rest.map(() => ['uint24', 'bytes']).flat(),\n      rest.map(r => [ethers.getBytes(r.sig).length, r.sig]).flat()\n    )\n\n    return ethers.solidityPacked(['uint8', 'uint24', 'bytes', 'bytes'], [3, ethers.getBytes(top).length, top, encodedRest])\n  }\n\n  const hashSetImageHash = (imageHash: string) => {\n    return ethers.keccak256(ethers.AbiCoder.defaultAbiCoder().encode(['bytes32', 'bytes32'], [typeHash, imageHash]))\n  }\n\n  it('Should accept a single chained signature', async () => {\n    const wallet_b = SequenceWallet.basicWallet(context, { address: wallet.address })\n\n    const hsih = hashSetImageHash(wallet_b.imageHash)\n\n    const sig = await wallet.signDigest(hsih)\n    const topsig = await wallet_b.signTransactions([{}])\n    const bundled = chain(topsig, { sig: sig })\n\n    await wallet_b.relayTransactions([{}], bundled)\n  })\n\n  it('Should accept two chained signatures', async () => {\n    let wallet_b = SequenceWallet.basicWallet(context, { address: wallet.address, signing: 2, idle: 1 })\n    let wallet_c = SequenceWallet.basicWallet(context, { address: wallet_b.address, signing: 3, idle: 7 })\n\n    const checkpoint1 = BigInt(wallet.config.checkpoint) + 1n\n    const checkpoint2 = checkpoint1 + 1n\n\n    wallet_b = wallet_b.useConfig({ ...wallet_b.config, checkpoint: checkpoint1 })\n    wallet_c = wallet_c.useConfig({ ...wallet_c.config, checkpoint: checkpoint2 })\n\n    const hsih1 = hashSetImageHash(wallet_b.imageHash)\n    const hsih2 = hashSetImageHash(wallet_c.imageHash)\n\n    const sig1 = await wallet.signDigest(hsih1)\n    const sig2 = await wallet_b.signDigest(hsih2)\n\n    const topsig = await wallet_c.signTransactions([{}])\n    const bundled = chain(topsig, { sig: sig2 }, { sig: sig1 })\n\n    await wallet_c.relayTransactions([{}], bundled)\n  })\n\n  it('Should reject chained signatures if checkpoint is wrongly ordered', async () => {\n    let wallet_b = SequenceWallet.basicWallet(context, { address: wallet.address, signing: 2, idle: 1 })\n    let wallet_c = SequenceWallet.basicWallet(context, { address: wallet_b.address, signing: 3, idle: 7 })\n\n    const checkpoint1 = BigInt(wallet.config.checkpoint) + 1n\n    const checkpoint2 = checkpoint1 - 1n\n\n    wallet_b = wallet_b.useConfig({ ...wallet_b.config, checkpoint: checkpoint1 })\n    wallet_c = wallet_c.useConfig({ ...wallet_c.config, checkpoint: checkpoint2 })\n\n    const hsih1 = hashSetImageHash(wallet_b.imageHash)\n    const hsih2 = hashSetImageHash(wallet_c.imageHash)\n\n    const sig1 = await wallet.signDigest(hsih1)\n    const sig2 = await wallet_b.signDigest(hsih2)\n\n    const topsig = await wallet_c.signTransactions([{}])\n    const bundled = chain(topsig, { sig: sig2 }, { sig: sig1 })\n\n    const tx = wallet_c.relayTransactions([{}], bundled)\n    await expectToBeRejected(tx, `WrongChainedCheckpointOrder(${checkpoint1}, ${checkpoint2})`)\n  })\n\n  it('Should accept top level signature encoded as chained', async () => {\n    const sig = await wallet.signTransactions([{}])\n    await wallet.relayTransactions([{}], chain(sig))\n  })\n\n  it('Should accept top level signature encoded as chained twice', async () => {\n    const sig = await wallet.signTransactions([{}])\n    await wallet.relayTransactions([{}], chain(chain(sig)))\n  })\n\n  it('Should reject signature if current checkpoint is equal to signed checkpoint', async () => {\n    let wallet_b = SequenceWallet.basicWallet(context, { address: wallet.address })\n\n    const checkpoint = BigInt(wallet.config.checkpoint)\n    wallet_b = wallet_b.useConfig({ ...wallet_b.config, checkpoint: checkpoint })\n\n    const hsih = hashSetImageHash(wallet_b.imageHash)\n    const sig = await wallet.signDigest(hsih)\n    const topsig = await wallet_b.signTransactions([{}])\n    const bundled = chain(topsig, { sig: sig })\n\n    await expectToBeRejected(\n      wallet_b.relayTransactions([{}], bundled),\n      `WrongChainedCheckpointOrder(${checkpoint}, ${checkpoint})`\n    )\n  })\n\n  it('Should reject signature if current checkpoint is above to signed checkpoint', async () => {\n    let wallet_b = SequenceWallet.basicWallet(context, { address: wallet.address })\n\n    const checkpoint = BigInt(wallet.config.checkpoint) - 1n\n    wallet_b = wallet_b.useConfig({ ...wallet_b.config, checkpoint: checkpoint })\n\n    const hsih = hashSetImageHash(wallet_b.imageHash)\n    const sig = await wallet.signDigest(hsih)\n    const topsig = await wallet_b.signTransactions([{}])\n    const bundled = chain(topsig, { sig: sig })\n\n    await expectToBeRejected(\n      wallet_b.relayTransactions([{}], bundled),\n      `WrongChainedCheckpointOrder(${wallet.config.checkpoint}, ${checkpoint})`\n    )\n  })\n})\n"
  },
  {
    "path": "test/ERC165.spec.ts",
    "content": "import { ethers } from 'ethers'\nimport { ContractType, deploySequenceContext, ERC165CheckerMock, SequenceContext } from './utils/contracts'\nimport { SequenceWallet } from './utils/wallet'\nimport { expect, interfaceIdOf, randomHex } from './utils'\n\nconst interfaceIds = [\n  'IERC223Receiver',\n  'IERC721Receiver',\n  'IERC1155Receiver',\n  'IERC1271Wallet',\n  'IModuleAuth',\n  'IModuleCalls',\n  'IModuleCreator',\n  'IModuleHooks',\n  'IModuleUpdate'\n]\n\ncontract('ERC165', () => {\n  let context: SequenceContext\n  let erc165checker: ContractType<typeof ERC165CheckerMock>\n  let wallet: SequenceWallet\n\n  before(async () => {\n    context = await deploySequenceContext()\n    erc165checker = await ERC165CheckerMock.deploy()\n  })\n\n  beforeEach(async () => {\n    wallet = SequenceWallet.basicWallet(context)\n    await wallet.deploy()\n  })\n\n  describe('Implement all interfaces for ERC165 on MainModule', () => {\n    interfaceIds.forEach(element => {\n      it(`Should return implements ${element} interfaceId`, async () => {\n        const interfaceId = interfaceIdOf(new ethers.Interface(artifacts.require(element).abi))\n        expect(BigInt(interfaceId) === 0n).to.be.false\n\n        const erc165result = await erc165checker.doesContractImplementInterface(wallet.address, interfaceId)\n        expect(erc165result).to.be.true\n      })\n    })\n  })\n  describe('Implement all interfaces for ERC165 on MainModuleUpgradable', () => {\n    beforeEach(async () => {\n      await wallet.updateImageHash(randomHex(32))\n    })\n\n    interfaceIds.concat('IModuleAuthUpgradable').forEach(element => {\n      it(`Should return implements ${element} interfaceId`, async () => {\n        const interfaceId = interfaceIdOf(new ethers.Interface(artifacts.require(element).abi))\n        expect(BigInt(interfaceId) === 0n).to.be.false\n\n        const erc165result = await erc165checker.doesContractImplementInterface(wallet.address, interfaceId)\n        expect(erc165result).to.be.true\n      })\n    })\n  })\n  describe('Manually defined interfaces', () => {\n    const interfaces = [\n      ['ERC165', '0x01ffc9a7'],\n      ['ERC721', '0x150b7a02'],\n      ['ERC1155', '0x4e2312e0']\n    ]\n\n    interfaces.forEach(i => {\n      it(`Should implement ${i[0]} interface`, async () => {\n        const erc165result = await erc165checker.doesContractImplementInterface(wallet.address, i[1])\n        expect(erc165result).to.be.true\n      })\n    })\n  })\n})\n"
  },
  {
    "path": "test/Factory.spec.ts",
    "content": "import { ethers } from 'ethers'\n\nimport { ethers as hethers } from 'hardhat'\nimport { addressOf } from './utils/sequence'\nimport { expect, expectToBeRejected } from './utils'\nimport { ContractType, Factory, ModuleMock } from './utils/contracts'\n\ncontract('Factory', () => {\n  let module: ContractType<typeof ModuleMock>\n  let factory: ContractType<typeof Factory>\n\n  beforeEach(async () => {\n    module = await ModuleMock.deploy()\n    factory = await Factory.deploy()\n\n    await module.waitForDeployment()\n    await factory.waitForDeployment()\n  })\n\n  describe('Deploy wallets', () => {\n    it('Should deploy wallet', async () => {\n      await factory.deploy(\n        await module.getAddress(),\n        ethers.AbiCoder.defaultAbiCoder().encode(['address'], [ethers.Wallet.createRandom().address])\n      )\n      await factory.waitForDeployment()\n    })\n\n    it('Should predict wallet address', async () => {\n      const hash = ethers.hexlify(ethers.randomBytes(32))\n      const predict = addressOf(await factory.getAddress(), await module.getAddress(), hash)\n      await factory.deploy(await module.getAddress(), hash)\n      await factory.waitForDeployment()\n      expect(await hethers.provider.getCode(predict)).to.not.equal('0x')\n    })\n\n    it('Should initialize with main module', async () => {\n      const hash = ethers.hexlify(ethers.randomBytes(32))\n      await factory.deploy(await module.getAddress(), hash)\n      const address = addressOf(await factory.getAddress(), await module.getAddress(), hash)\n      const wallet = await ModuleMock.attach(address)\n      const receipt = await (await wallet.ping()).wait()\n\n      if (!receipt) {\n        throw new Error('No receipt')\n      }\n\n      expect(wallet.interface.parseLog(receipt.logs[0])?.name).to.equal('Pong')\n    })\n\n    it('Should fail to deploy twice', async () => {\n      const hash = ethers.hexlify(ethers.randomBytes(32))\n      await factory.deploy(await module.getAddress(), hash)\n\n      const tx2 = factory.deploy(await module.getAddress(), hash)\n      await expectToBeRejected(tx2, `DeployFailed(\"${await module.getAddress()}\", \"${hash}\")`)\n    })\n\n    it('Should fail to deploy with not enough gas', async () => {\n      const hash = ethers.hexlify(ethers.randomBytes(32))\n      const tx = factory.deploy(await module.getAddress(), hash, { gasLimit: 80000 })\n      await expectToBeRejected(tx, `DeployFailed(\"${await module.getAddress()}\", \"${hash}\")`)\n    })\n  })\n})\n"
  },
  {
    "path": "test/GasEstimation.spec.ts",
    "content": "import { ethers } from 'ethers'\nimport { expect } from './utils'\n\nimport {\n  CallReceiverMock,\n  ContractType,\n  deploySequenceContext,\n  GasEstimator,\n  GuestModule,\n  ModuleMock,\n  SequenceContext,\n  MainModuleGasEstimation,\n  MainModule\n} from './utils/contracts'\nimport { applyTxDefaults, toSimplifiedConfig, Transaction } from './utils/sequence'\nimport { SequenceWallet } from './utils/wallet'\n\nfunction txBaseCost(data: ethers.BytesLike): number {\n  const bytes = ethers.getBytes(data)\n  return Number(bytes.reduce((p, c) => (c == 0 ? p + 4n : p + 16n), 0n) + 21000n)\n}\n\ncontract('Estimate gas usage', () => {\n  let context: SequenceContext\n\n  let gasEstimator: ContractType<typeof GasEstimator>\n  let callReceiver: ContractType<typeof CallReceiverMock>\n  let guestModule: ContractType<typeof GuestModule>\n  let moduleMock: ContractType<typeof ModuleMock>\n\n  let wallet: SequenceWallet\n\n  const bundleWithDeploy = async (txData: string) => {\n    return applyTxDefaults([\n      {\n        target: await context.factory.getAddress(),\n        data: context.factory.interface.encodeFunctionData('deploy', [await context.mainModule.getAddress(), wallet.imageHash])\n      },\n      {\n        target: wallet.address,\n        data: txData\n      }\n    ])\n  }\n\n  const estimate = (address: string, data: ethers.BytesLike) => ({\n    call: async () => {\n      return gasEstimator.estimate.staticCall(address, data)\n    }\n  })\n\n  const estimateGasUsage = async (\n    txs: Partial<Transaction>[],\n    wallet: SequenceWallet,\n    deploy: boolean = false,\n    nonce: ethers.BigNumberish = 0\n  ) => {\n    const stubSignature = await SequenceWallet.detailedWallet(context, {\n      threshold: wallet.config.threshold,\n      signers: toSimplifiedConfig(wallet.config).signers.map(() => ethers.Wallet.createRandom())\n    }).signMessage(ethers.randomBytes(32))\n\n    const txData = wallet.mainModule.interface.encodeFunctionData('execute', [applyTxDefaults(txs), nonce, stubSignature])\n    if (!deploy) {\n      const res = await estimate(wallet.address, txData).call()\n      return Number(res.gas + BigInt(txBaseCost(txData)))\n    }\n\n    const guestModuleData = guestModule.interface.encodeFunctionData('execute', [\n      await bundleWithDeploy(txData),\n      0,\n      new Uint8Array([])\n    ])\n    const res = await estimate(await guestModule.getAddress(), guestModuleData).call()\n    return Number(res.gas + BigInt(txBaseCost(txData)))\n  }\n\n  const gasUsedFor = async (tx: ethers.ContractTransactionResponse) => {\n    const receipt = await tx.wait()\n\n    if (!receipt) {\n      throw new Error('No receipt')\n    }\n\n    return Number(BigInt(receipt.gasUsed))\n  }\n\n  before(async () => {\n    context = await deploySequenceContext()\n\n    // Deploy MainModuleGasEstimation (hardhat doesn't support overwrites, so we use this as the real module)\n    context.mainModule = (await MainModuleGasEstimation.deploy()) as any as ContractType<typeof MainModule>\n\n    gasEstimator = await GasEstimator.deploy()\n    guestModule = await GuestModule.deploy()\n    moduleMock = await ModuleMock.deploy()\n  })\n\n  beforeEach(async () => {\n    wallet = SequenceWallet.basicWallet(context)\n    callReceiver = await CallReceiverMock.deploy()\n  })\n\n  describe('Estimate gas of transactions', () => {\n    describe('without wallet deployed', () => {\n      it('Should estimate wallet deployment', async () => {\n        wallet = SequenceWallet.basicWallet(context)\n        const factoryData = context.factory.interface.encodeFunctionData('deploy', [\n          await context.mainModule.getAddress(),\n          wallet.imageHash\n        ])\n\n        const estimated = Number(BigInt((await estimate(await context.factory.getAddress(), factoryData).call()).gas))\n        const realTx = await context.factory.deploy(await context.mainModule.getAddress(), wallet.imageHash)\n        expect(estimated + txBaseCost(factoryData)).to.approximately(await gasUsedFor(realTx), 5000)\n      })\n\n      it('Should estimate wallet deployment + upgrade', async () => {\n        const transactions = [\n          {\n            target: wallet.address,\n            data: wallet.mainModule.interface.encodeFunctionData('updateImplementation', [await moduleMock.getAddress()])\n          }\n        ]\n\n        const estimated = await estimateGasUsage(transactions, wallet, true)\n\n        const signature = await wallet.signTransactions(transactions, 0)\n        const executeTxData = await bundleWithDeploy(\n          wallet.mainModule.interface.encodeFunctionData('execute', [applyTxDefaults(transactions), 0, signature])\n        )\n        const realTx = await guestModule.execute(executeTxData, 0, new Uint8Array([]))\n\n        expect(estimated).to.approximately(await gasUsedFor(realTx), 5000)\n      })\n\n      it('Should estimate wallet deployment + upgrade + transaction', async () => {\n        const transactions = [\n          {\n            target: wallet.address,\n            data: wallet.mainModule.interface.encodeFunctionData('updateImplementation', [await moduleMock.getAddress()])\n          },\n          {\n            target: await callReceiver.getAddress(),\n            data: callReceiver.interface.encodeFunctionData('testCall', [1, ethers.hexlify(ethers.randomBytes(299))])\n          }\n        ]\n\n        const estimated = await estimateGasUsage(transactions, wallet, true)\n\n        const signature = await wallet.signTransactions(transactions, 0)\n        const executeTxData = await bundleWithDeploy(\n          wallet.mainModule.interface.encodeFunctionData('execute', [applyTxDefaults(transactions), 0, signature])\n        )\n        const realTx = await guestModule.execute(executeTxData, 0, new Uint8Array([]))\n\n        expect(estimated).to.approximately(await gasUsedFor(realTx), 5000)\n\n        expect(await callReceiver.lastValA()).to.equal(1n)\n      })\n\n      it('Should estimate wallet deployment + upgrade + failed transaction', async () => {\n        await callReceiver.setRevertFlag(true)\n\n        const transactions = [\n          {\n            target: wallet.address,\n            data: wallet.mainModule.interface.encodeFunctionData('updateImplementation', [await moduleMock.getAddress()])\n          },\n          {\n            revertOnError: false,\n            target: await callReceiver.getAddress(),\n            data: callReceiver.interface.encodeFunctionData('testCall', [1, ethers.hexlify(ethers.randomBytes(299))])\n          }\n        ]\n\n        const estimated = await estimateGasUsage(transactions, wallet, true)\n\n        const signature = await wallet.signTransactions(transactions, 0)\n        const executeTxData = await bundleWithDeploy(\n          wallet.mainModule.interface.encodeFunctionData('execute', [applyTxDefaults(transactions), 0, signature])\n        )\n        const realTx = await guestModule.execute(executeTxData, 0, new Uint8Array([]))\n\n        expect(estimated).to.approximately(await gasUsedFor(realTx), 5000)\n      })\n\n      it('Should estimate wallet deployment + upgrade + fixed gas transaction', async () => {\n        const transactions = [\n          {\n            target: wallet.address,\n            data: wallet.mainModule.interface.encodeFunctionData('updateImplementation', [await moduleMock.getAddress()])\n          },\n          {\n            revertOnError: false,\n            gasLimit: 900000,\n            target: await callReceiver.getAddress(),\n            data: callReceiver.interface.encodeFunctionData('testCall', [1, ethers.hexlify(ethers.randomBytes(299))])\n          }\n        ]\n\n        const estimated = await estimateGasUsage(transactions, wallet, true)\n\n        const signature = await wallet.signTransactions(transactions, 0)\n        const executeTxData = await bundleWithDeploy(\n          wallet.mainModule.interface.encodeFunctionData('execute', [applyTxDefaults(transactions), 0, signature])\n        )\n        const realTx = await guestModule.execute(executeTxData, 0, new Uint8Array([]))\n\n        expect(estimated).to.approximately(await gasUsedFor(realTx), 5000)\n      })\n    })\n    ;[\n      {\n        name: 'single signer',\n        signers: 1\n      },\n      {\n        name: 'many signers',\n        signers: 32\n      }\n    ].map(o => {\n      describe(`with wallet deployed and ${o.name}`, () => {\n        beforeEach(async () => {\n          wallet = SequenceWallet.basicWallet(context, { signing: o.signers })\n          await wallet.deploy()\n        })\n\n        it('Should estimate single transaction', async () => {\n          const transactions = [\n            {\n              target: await callReceiver.getAddress(),\n              data: callReceiver.interface.encodeFunctionData('testCall', [1, ethers.hexlify(ethers.randomBytes(299))])\n            }\n          ]\n\n          const estimated = await estimateGasUsage(transactions, wallet)\n          const gasUsed = await wallet\n            .sendTransactions(transactions)\n            .then(t => t.wait())\n            .then(r => Number(r?.gasUsed))\n\n          expect(estimated).to.approximately(gasUsed, 5000)\n        })\n\n        it('Should estimate multiple transactions', async () => {\n          const transactions = [\n            {\n              target: await callReceiver.getAddress(),\n              data: callReceiver.interface.encodeFunctionData('testCall', [1, ethers.hexlify(ethers.randomBytes(299))])\n            },\n            {\n              target: await callReceiver.getAddress(),\n              data: callReceiver.interface.encodeFunctionData('testCall', [1, ethers.hexlify(ethers.randomBytes(2299))])\n            }\n          ]\n\n          const estimated = await estimateGasUsage(transactions, wallet)\n          const gasUsed = await wallet\n            .sendTransactions(transactions)\n            .then(t => t.wait())\n            .then(r => Number(r?.gasUsed))\n\n          // TODO: The estimator overEstimates the gas usage due to the gas refund\n          expect(gasUsed).to.be.below(estimated)\n        })\n\n        it('Should estimate multiple transactions with bad nonce', async () => {\n          const transactions = [\n            {\n              target: await callReceiver.getAddress(),\n              data: callReceiver.interface.encodeFunctionData('testCall', [1, ethers.hexlify(ethers.randomBytes(299))])\n            },\n            {\n              target: await callReceiver.getAddress(),\n              data: callReceiver.interface.encodeFunctionData('testCall', [1, ethers.hexlify(ethers.randomBytes(2299))])\n            }\n          ]\n\n          const estimated = await estimateGasUsage(transactions, wallet, false, 999999999)\n          const gasUsed = await wallet\n            .sendTransactions(transactions)\n            .then(t => t.wait())\n            .then(r => Number(r?.gasUsed))\n\n          // TODO: The estimator overEstimates the gas usage due to the gas refund\n          expect(gasUsed).to.be.below(estimated)\n        })\n\n        it('Should estimate multiple transactions with failing transactions', async () => {\n          const altCallReceiver = await CallReceiverMock.deploy()\n          await altCallReceiver.setRevertFlag(true)\n\n          const transactions = [\n            {\n              target: await callReceiver.getAddress(),\n              data: callReceiver.interface.encodeFunctionData('testCall', [1, ethers.hexlify(ethers.randomBytes(299))])\n            },\n            {\n              target: await callReceiver.getAddress(),\n              data: callReceiver.interface.encodeFunctionData('testCall', [1, ethers.hexlify(ethers.randomBytes(2299))])\n            },\n            {\n              revertOnError: false,\n              target: await altCallReceiver.getAddress(),\n              data: altCallReceiver.interface.encodeFunctionData('testCall', [1, ethers.hexlify(ethers.randomBytes(229))])\n            }\n          ]\n\n          const estimated = await estimateGasUsage(transactions, wallet)\n          const gasUsed = await wallet\n            .sendTransactions(transactions)\n            .then(t => t.wait())\n            .then(r => Number(r?.gasUsed))\n\n          // TODO: The estimator overEstimates the gas usage due to the gas refund\n          expect(gasUsed).to.be.below(estimated)\n        })\n\n        it('Should estimate multiple transactions with failing transactions and fixed gas limits', async () => {\n          const altCallReceiver = await CallReceiverMock.deploy()\n          await altCallReceiver.setRevertFlag(true)\n\n          const transactions = [\n            {\n              target: await callReceiver.getAddress(),\n              data: callReceiver.interface.encodeFunctionData('testCall', [1, ethers.hexlify(ethers.randomBytes(299))])\n            },\n            {\n              gasLimit: 90000,\n              target: await callReceiver.getAddress(),\n              data: callReceiver.interface.encodeFunctionData('testCall', [1, ethers.hexlify(ethers.randomBytes(12))])\n            },\n            {\n              revertOnError: false,\n              target: await altCallReceiver.getAddress(),\n              data: altCallReceiver.interface.encodeFunctionData('testCall', [1, ethers.hexlify(ethers.randomBytes(229))])\n            }\n          ]\n\n          const estimated = await estimateGasUsage(transactions, wallet)\n          const gasUsed = await wallet\n            .sendTransactions(transactions)\n            .then(t => t.wait())\n            .then(r => Number(r?.gasUsed))\n\n          // TODO: The estimator overEstimates the gas usage due to the gas refund\n          expect(gasUsed).to.be.below(estimated)\n        })\n      })\n    })\n  })\n})\n"
  },
  {
    "path": "test/GuestModule.spec.ts",
    "content": "import { expect, expectToBeRejected } from './utils'\nimport { ethers as hethers } from 'hardhat'\nimport { ethers } from 'ethers'\nimport { CallReceiverMock, ContractType, GuestModule, HookCallerMock, MainModule } from './utils/contracts'\nimport { applyTxDefaults, Transaction } from './utils/sequence'\n\ncontract('GuestModule', () => {\n  let guestModule: ContractType<typeof GuestModule>\n  let callReceiver: ContractType<typeof CallReceiverMock>\n  let hookCallerMock: ContractType<typeof HookCallerMock>\n\n  describe('GuestModule wallet', () => {\n    before(async () => {\n      guestModule = await GuestModule.deploy()\n      callReceiver = await CallReceiverMock.deploy()\n      hookCallerMock = await HookCallerMock.deploy()\n    })\n\n    let valA: bigint\n    let valB: string\n\n    let transactions: Transaction[]\n\n    beforeEach(async () => {\n      valA = ethers.toBigInt(ethers.randomBytes(3))\n      valB = ethers.hexlify(ethers.randomBytes(120))\n\n      transactions = applyTxDefaults([\n        {\n          target: await callReceiver.getAddress(),\n          data: callReceiver.interface.encodeFunctionData('testCall', [valA, valB])\n        }\n      ])\n    })\n\n    it('Should accept transactions without signature', async () => {\n      await guestModule.execute(transactions, 0, new Uint8Array([]))\n\n      expect(await callReceiver.lastValA()).to.equal(valA)\n      expect(await callReceiver.lastValB()).to.equal(valB)\n    })\n    it('Should accept transactions on selfExecute', async () => {\n      await guestModule.selfExecute(transactions)\n\n      expect(await callReceiver.lastValA()).to.equal(valA)\n      expect(await callReceiver.lastValB()).to.equal(valB)\n    })\n    it('Should accept transactions with random signature', async () => {\n      const signature = ethers.randomBytes(96)\n\n      await guestModule.execute(transactions, 0, signature)\n\n      expect(await callReceiver.lastValA()).to.equal(valA)\n      expect(await callReceiver.lastValB()).to.equal(valB)\n    })\n    it('Should accept transactions with random nonce', async () => {\n      const nonce = 9123891\n\n      await guestModule.execute(transactions, nonce, new Uint8Array([]))\n\n      expect(await callReceiver.lastValA()).to.equal(valA)\n      expect(await callReceiver.lastValB()).to.equal(valB)\n    })\n    it('Should revert on delegateCall transactions', async () => {\n      const transactions = applyTxDefaults([\n        {\n          delegateCall: true\n        }\n      ])\n\n      const tx = guestModule.selfExecute(transactions)\n      await expectToBeRejected(tx, 'DelegateCallNotAllowed(0)')\n    })\n    it('Should not accept ETH', async () => {\n      const signer = await hethers.provider.getSigner()\n      const tx = signer.sendTransaction({ value: 1, to: await guestModule.getAddress() })\n      await expect(tx).to.be.rejected\n    })\n    it('Should not implement hooks', async () => {\n      const tx = hookCallerMock.callERC1155Received(await guestModule.getAddress())\n      await expect(tx).to.be.rejected\n    })\n    it('Should not be upgradeable', async () => {\n      const mainModule = await MainModule.attach(await guestModule.getAddress())\n      const newImageHash = ethers.hexlify(ethers.randomBytes(32))\n\n      const migrateBundle = applyTxDefaults([\n        {\n          target: await mainModule.getAddress(),\n          data: mainModule.interface.encodeFunctionData('updateImageHash', [newImageHash])\n        }\n      ])\n\n      const tx = guestModule.selfExecute(migrateBundle)\n      await expect(tx).to.be.rejected\n    })\n  })\n})\n"
  },
  {
    "path": "test/LibBytes.spec.ts",
    "content": "import { ethers } from 'ethers'\n\nimport { expect, expectStaticToBeRejected, randomHex } from './utils'\nimport { ContractType, LibBytesImpl, LibBytesPointerImpl } from './utils/contracts'\n\ncontract('LibBytes', () => {\n  let libBytes: ContractType<typeof LibBytesImpl>\n  let libBytesPointer: ContractType<typeof LibBytesPointerImpl>\n\n  before(async () => {\n    libBytes = await LibBytesImpl.deploy()\n    libBytesPointer = await LibBytesPointerImpl.deploy()\n  })\n\n  describe('readFirstUint16', () => {\n    it('Should read first uint16', async () => {\n      const res = await libBytesPointer.readFirstUint16('0x03021e4453120a')\n      expect(res[0]).to.equal(770n)\n      expect(res[1]).to.equal(2n)\n    })\n\n    it('Should read first uint16 of 2 byte array', async () => {\n      const res = await libBytesPointer.readFirstUint16('0xff0a')\n      expect(res[0]).to.equal(65290n)\n      expect(res[1]).to.equal(2n)\n    })\n\n    it('Should read first uint16 out of bounds', async () => {\n      const res = await libBytesPointer.readFirstUint16('0x')\n      expect(res[0]).to.equal(0n)\n      expect(res[1]).to.equal(2n)\n    })\n  })\n\n  describe('readBytes32', () => {\n    let bytes32: string\n    beforeEach(async () => {\n      bytes32 = randomHex(32)\n    })\n    it('Should read bytes32 at index zero', async () => {\n      const data = bytes32.concat(randomHex(16).slice(2))\n\n      const res = await libBytes.readBytes32(data, 0)\n      expect(res).to.equal(bytes32)\n    })\n    it('Should read bytes32 at given index', async () => {\n      const data = randomHex(12).concat(bytes32.slice(2)).concat(randomHex(44).slice(2))\n\n      const res = await libBytes.readBytes32(data, 12)\n      expect(res).to.equal(bytes32)\n    })\n    it('Should read bytes32 at last index', async () => {\n      const data = randomHex(11).concat(bytes32.slice(2))\n\n      const res = await libBytes.readBytes32(data, 11)\n      expect(res).to.equal(bytes32)\n    })\n    it('Should read bytes32 out of bounds', async () => {\n      const data = randomHex(11).concat(bytes32.slice(2))\n      const res = await libBytes.readBytes32(data, 12)\n      expect(res).to.equal('0x' + data.slice(26, 26 + 64) + '00')\n    })\n    it('Should read bytes32 totally out of bounds', async () => {\n      const res = await libBytes.readBytes32('0x010203', 3145)\n      expect(res).to.equal(ethers.ZeroHash)\n    })\n  })\n\n  describe('readUint32', () => {\n    it('Should read uint32 at index zero', async () => {\n      const res = await libBytes.readUint32('0x837fc8a10d', 0)\n      expect(res).to.equal(2206189729n)\n    })\n    it('Should read uint32 at given index', async () => {\n      const res = await libBytes.readUint32('0x5a9c2a992a8c22199af0', 3)\n      expect(res).to.equal(2569702434n)\n    })\n    it('Should read uint32 at last index', async () => {\n      const res = await libBytes.readUint32('0x029183af982299dfa001', 6)\n      expect(res).to.equal(2581569537n)\n    })\n    it('Should read zeros uint32 out of bounds', async () => {\n      const res1 = await libBytes.readUint32('0x2293', 1)\n      const res2 = await libBytes.readUint32('0x2193000000', 1)\n      expect(res1).to.equal(2466250752n)\n      expect(res1).to.equal(res2)\n    })\n    it('Should read all zeros uint32 fully out of bounds', async () => {\n      const res = await libBytes.readUint32('0xff92a09f339922', 15)\n      expect(res).to.equal(0n)\n    })\n  })\n\n  describe('readUint16', () => {\n    it('Should read uint16 at index zero', async () => {\n      const res = await libBytesPointer.readUint16('0x5202', 0)\n      expect(res[0]).to.equal(20994n)\n      expect(res[1]).to.equal(2n)\n    })\n    it('Should read uint16 at given index', async () => {\n      const res = await libBytesPointer.readUint16('0x5a9c2a1019d401d3', 3)\n      expect(res[0]).to.equal(4121n)\n      expect(res[1]).to.equal(5n)\n    })\n    it('Should read uint16 at last index', async () => {\n      const res = await libBytesPointer.readUint16('0x020414', 1)\n      expect(res[0]).to.equal(1044n)\n      expect(res[1]).to.equal(3n)\n    })\n    it('Should read zeros uint16 out of bounds', async () => {\n      const res1 = await libBytesPointer.readUint16('0x5a', 0)\n      const res2 = await libBytesPointer.readUint16('0x5a00', 0)\n      expect(res1[0]).to.equal(23040n)\n      expect(res1[0]).to.equal(res2[0])\n      expect(res1[1]).to.equal(2n)\n    })\n    it('Should read zeros uint16 fully out of bounds', async () => {\n      const res = await libBytesPointer.readUint16('0x5a9ca2', 12)\n      expect(res[0]).to.equal(0n)\n      expect(res[1]).to.equal(14n)\n    })\n  })\n\n  describe('readUint24', () => {\n    it('Should read uint24 at index zero', async () => {\n      const res = await libBytesPointer.readUint24('0x5202fa', 0)\n      expect(res[0]).to.equal(5374714n)\n      expect(res[1]).to.equal(3n)\n    })\n    it('Should read uint24 at given index', async () => {\n      const res = await libBytesPointer.readUint24('0x5202f7220c', 2)\n      expect(res[0]).to.equal(16196108n)\n      expect(res[1]).to.equal(5n)\n    })\n    it('Should read uint24 at last index', async () => {\n      const res = await libBytesPointer.readUint24('0x021f2b00', 1)\n      expect(res[0]).to.equal(2042624n)\n      expect(res[1]).to.equal(4n)\n    })\n    it('Should read zeros uint24 out of bounds', async () => {\n      const res1 = await libBytesPointer.readUint24('0xf598', 0)\n      const res2 = await libBytesPointer.readUint24('0xf59800', 0)\n      expect(res1[0]).to.equal(16095232n)\n      expect(res1[0]).to.equal(res2[0])\n      expect(res1[1]).to.equal(3n)\n    })\n    it('Should read zeros uint24 fully out of bounds', async () => {\n      const res = await libBytesPointer.readUint24('0x5a9ca221', 12)\n      expect(res[0]).to.equal(0n)\n      expect(res[1]).to.equal(15n)\n    })\n  })\n\n  describe('readUint64', () => {\n    it('Should read uint64 at index zero', async () => {\n      const res = await libBytesPointer.readUint64('0xc1725050681dcb2a', 0)\n      expect(res[0]).to.equal(13939292102939495210n)\n      expect(res[1]).to.equal(8n)\n    })\n    it('Should read uint64 at given index', async () => {\n      const res = await libBytesPointer.readUint64('0x0837acc1725050681dcb2a01cc', 3)\n      expect(res[0]).to.equal(13939292102939495210n)\n      expect(res[1]).to.equal(11n)\n    })\n    it('Should read uint64 at last index', async () => {\n      const res = await libBytesPointer.readUint64('0x0837acc1725050681dcb2a', 3)\n      expect(res[0]).to.equal(13939292102939495210n)\n      expect(res[1]).to.equal(11n)\n    })\n    it('Should read zeros uint64 out of bounds', async () => {\n      const res1 = await libBytesPointer.readUint64('0x5a', 0)\n      const res2 = await libBytesPointer.readUint64('0x5a00', 0)\n      const res3 = await libBytesPointer.readUint64('0x5a00000000000000', 0)\n      expect(res1[0]).to.equal(6485183463413514240n)\n      expect(res1[0]).to.equal(res2[0])\n      expect(res1[0]).to.equal(res3[0])\n      expect(res1[1]).to.equal(8n)\n    })\n    it('Should read zeros uint64 fully out of bounds', async () => {\n      const res = await libBytesPointer.readUint64('0x5a9ca2', 12)\n      expect(res[0]).to.equal(0n)\n      expect(res[1]).to.equal(20n)\n    })\n  })\n})\n"
  },
  {
    "path": "test/LibString.spec.ts",
    "content": "import { ethers } from 'ethers'\n\nimport { expect, expectStaticToBeRejected, randomHex } from './utils'\nimport { ContractType, LibStringImp } from './utils/contracts'\n\ncontract('LibString', () => {\n  let libString: ContractType<typeof LibStringImp>\n\n  before(async () => {\n    libString = await LibStringImp.deploy()\n  })\n\n  describe('bytesToHexadecimal', () => {\n    new Array(99).fill(0).map((_, i) => {\n      it(`Should convert ${i} bytes to hexadecimal`, async () => {\n        const bytes = randomHex(i)\n        const expected = ethers.hexlify(bytes)\n        const result = await libString.bytesToHexadecimal(bytes)\n        expect(result).to.eq(expected.slice(2))\n\n        const prefixed = await libString.prefixHexadecimal(result)\n        expect(prefixed).to.eq(expected)\n      })\n    })\n  })\n\n  describe('bytesToBase32', () => {\n    ;[\n      ['0x', 'b'],\n      ['0x69', 'bne'],\n      ['0x8775', 'bq52q'],\n      ['0x91e0f3', 'bshqpg'],\n      ['0x4867e789', 'bjbt6pci'],\n      ['0x456fe65b8d', 'bivx6mw4n'],\n      ['0xb20b0f525320', 'bwifq6ustea'],\n      ['0xd292f6c85f4e5a', 'b2kjpnsc7jzna'],\n      ['0xf78beb02b622adc7', 'b66f6wavwekw4o'],\n      ['0xfad9c9851352f0ae0e', 'b7lm4tbitklyk4dq'],\n      ['0x30b4bca5f67cc95312be', 'bgc2lzjpwptevgev6'],\n      ['0xebc9fd66e4ae84644d502b', 'b5pe72zxev2cgitkqfm'],\n      ['0x0f198dc28836e24782e8d182', 'bb4my3quig3repaxi2gba'],\n      ['0x50059d23099f062187e9d52d8a', 'bkacz2iyjt4dcdb7j2uwyu'],\n      ['0x4c0e454dc43d303c896ef5f9c449', 'bjqhektoehuydzclo6x44isi'],\n      ['0x1ee41f4e940e4bf14df57a4fc06dd9', 'bd3sb6tuubzf7ctpvpjh4a3oz'],\n      ['0x04a8d211d53b45d29fed9a624bb2d5e0', 'basuneeovhnc5fh7ntjrexmwv4a'],\n      ['0x0a8b878364cdd555bda61db7c67503bb5e', 'bbkfypa3ezxkvlpngdw34m5idxnpa'],\n      ['0x313b3c6e446ee6eb6477c0b0b01b70799cb6', 'bge5ty3sen3towzdxycylag3qpgolm'],\n      ['0xe8be4a44970232babf052c3e75f611df8099c5', 'b5c7eurexaizlvpyffq7hl5qr36ajtri'],\n      ['0x461eb18d034475a35d4084a89e0e3a47980f3408', 'biyplddidir22gxkaqsuj4dr2i6ma6nai'],\n      ['0x519e139092a60229fe1a4db06d26fd199af9d39680', 'bkgpbheesuybct7q2jwyg2jx5dgnptu4wqa'],\n      ['0x0fdd1a5696148daf6986505e2b3dc9af16ed91452720', 'bb7oruvuwcsg262mgkbpcwpojv4lo3ekfe4qa'],\n      ['0x63a77d886c3f6593b6c6d37c7ab1f16ca3e34bd67e1ae1', 'bmotx3cdmh5szhnwg2n6hvmprnsr6gs6wpynoc'],\n      ['0x1198cc96b431176fc2d5bc8a8c49ce041e184649ec3b9cbb', 'bcgmmzfvugelw7qwvxsfiysooaqpbqrsj5q5zzoy'],\n      ['0x6b8d9af44a31c799ef835a522f8b630435c8e77e844360ad3d', 'bnogzv5ckghdzt34dljjc7c3daq24rz36qrbwblj5'],\n      ['0xbb2cb7057a24b8588bafd87907ee7579b159027dc7224a3f6540', 'bxmwlobl2es4frc5p3b4qp3tvpgyvsat5y4reup3fia'],\n      ['0x1f64b537d0e4b0158142dfa7cbfd9af76af0931862e48df02ba35b', 'bd5slkn6q4syblakc36t4x7m265vpbeyymlsi34blunnq'],\n      ['0x11c5655f9d5496039fe6b1a9651014d4a19e62331d61e73b48fec776', 'bchcwkx45kslahh7gwguwkeau2sqz4yrtdvq6oo2i73dxm'],\n      ['0x752c3c5cb4a3f06e4feff3f543278e97c7626569994b12b0d9ba9c7c83', 'bouwdyxfuupyg4t7p6p2ugj4os7dwezljtffrfmgzxkohzay'],\n      ['0xb6bd83f8bba80bc8aca3706f7090cd5a0f0cc6adf4e89614bff58c149880', 'bw26yh6f3vaf4rlfdobxxbegnlihqzrvn6tujmff76wgbjgea'],\n      ['0x90f4b198a1a1b2d3e01c36ba3460f46249f180dfa67b152f81552266a18708', 'bsd2ldgfbugznhya4g25diyhumje7dag7uz5rkl4bkurgnimhba'],\n      [\n        '0x24cda83efac2802425b1c35a2d21b01576e71d0e512bcc80840f85f9e0af2faa',\n        'betg2qpx2ykacijnrynnc2inqcv3oohiokev4zaeeb6c7tyfpf6va'\n      ],\n      [\n        '0x169dc221f4cec8756de592c3205a23177867f18c383d2251bd1a5c03cc346c3fe8',\n        'bc2o4eipuz3ehk3pfslbsawrdc54gp4mmha6seun5djoahtbunq76q'\n      ],\n      [\n        '0x65b13b1aef3e44180314108b7f0434ddc23ad740a0df4df7984ad0bf1fdd84edcbaf',\n        'bmwytwgxphzcbqayuccfx6bbu3xbdvv2audpu354yjlil6h65qtw4xly'\n      ]\n    ].map(([bytes, base32Encoded]) => {\n      it(`Should convert ${ethers.getBytes(bytes).length} bytes to base32`, async () => {\n        const result = await libString.bytesToBase32(bytes)\n        expect(result).to.equal(base32Encoded.slice(1))\n\n        const prefixed = await libString.prefixBase32(result)\n        expect(prefixed).to.equal(base32Encoded)\n      })\n    })\n  })\n})\n"
  },
  {
    "path": "test/MainModule.bench.ts",
    "content": "import { ethers } from 'ethers'\n\nimport { deploySequenceContext, SequenceContext } from './utils/contracts'\nimport { SequenceWallet } from './utils/wallet'\nimport { Transaction } from './utils/sequence'\n\nconst optimalGasLimit = 2n ** 22n\nconst runs = 256\n\nfunction report2(values: ethers.BigNumberish[]) {\n  const bns = values.map(v => BigInt(v))\n\n  const min = bns.reduce((a, b) => (a < b ? a : b))\n  const max = bns.reduce((a, b) => (a > b ? a : b))\n  const avg = bns.reduce((p, n) => (p + n) / BigInt(values.length))\n\n  return { min, max, avg }\n}\n\nfunction report(test: string, values: ethers.BigNumberish[]) {\n  const { min, max, avg } = report2(values)\n  console.info(` -> ${test} runs: ${values.length} cost min: ${min.toString()} max: ${max.toString()} avg: ${avg.toString()}`)\n}\n\ncontract('MainModule', () => {\n  let context: SequenceContext\n\n  before(async () => {\n    context = await deploySequenceContext()\n  })\n\n  if (process.env.BENCHMARK) {\n    describe.only('Benchmark', function () {\n      ;(this as any).timeout(0)\n\n      it('Deploy a wallet', async () => {\n        const results: ethers.BigNumberish[] = []\n\n        for (let i = 0; i < runs; i++) {\n          const tx = await SequenceWallet.basicWallet(context).deploy()\n          const receipt = await tx?.wait()\n\n          if (!receipt) {\n            throw new Error('No receipt')\n          }\n\n          results.push(receipt.gasUsed)\n        }\n\n        report('deploy wallets', results)\n      })\n\n      it('Relay 1/1 transaction', async () => {\n        const results: ethers.BigNumberish[] = []\n\n        for (let i = 0; i < runs; i++) {\n          const wallet = SequenceWallet.basicWallet(context)\n          await wallet.deploy()\n          const tx = await wallet.sendTransactions([{}])\n          const receipt = await tx.wait()\n\n          if (!receipt) {\n            throw new Error('No receipt')\n          }\n\n          results.push(receipt.gasUsed)\n        }\n\n        report('relay 1/1 transaction', results)\n      })\n\n      const batches = [2, 3, 5, 10, 50, 100]\n      batches.forEach(n => {\n        it(`Relay 1/1 ${n} transactions`, async () => {\n          const results: ethers.BigNumberish[] = []\n\n          const transactions = new Array(n).fill(0).map(() => ({\n            delegateCall: false,\n            revertOnError: true,\n            gasLimit: optimalGasLimit,\n            target: ethers.ZeroAddress,\n            value: 0n,\n            data: '0x0000000000000000000000007109709ecfa91a80626ff3989d68f67f5b1dd12e0000000000000000000000007109709ecfa91a80626ff3989d68f67f5b1dd12e'\n          }))\n\n          for (let i = 0; i < runs; i++) {\n            const wallet = SequenceWallet.basicWallet(context)\n            await wallet.deploy()\n            const tx = await wallet.sendTransactions(transactions)\n            const receipt = await tx.wait()\n\n            if (!receipt) {\n              throw new Error('No receipt')\n            }\n\n            results.push(receipt.gasUsed)\n          }\n\n          report(`relay 1/1 ${n} transactions`, results)\n        })\n      })\n\n      batches.forEach(n => {\n        const ntxs = Math.floor(n / 2)\n        const nfailing = n - ntxs\n        it(`Relay 1/1 ${ntxs} transactions and ${nfailing} failing transactions`, async () => {\n          const results: ethers.BigNumberish[] = []\n\n          const transactions: Transaction[] = new Array(ntxs)\n            .fill(0)\n            .map(() => ({\n              delegateCall: false,\n              revertOnError: true,\n              gasLimit: optimalGasLimit,\n              target: ethers.ZeroAddress,\n              value: 0n,\n              data: new Uint8Array([])\n            }))\n            .concat(\n              await Promise.all(\n                new Array(nfailing).fill(0).map(async () => ({\n                  delegateCall: false,\n                  revertOnError: false,\n                  gasLimit: optimalGasLimit,\n                  target: await context.factory.getAddress(),\n                  value: 0n,\n                  data: new Uint8Array([])\n                }))\n              )\n            )\n\n          for (let i = 0; i < runs; i++) {\n            const wallet = SequenceWallet.basicWallet(context)\n            await wallet.deploy()\n            const tx = await wallet.sendTransactions(transactions)\n            const receipt = await tx.wait()\n\n            if (!receipt) {\n              throw new Error('No receipt')\n            }\n\n            results.push(receipt.gasUsed)\n          }\n\n          report(`relay 1/1 ${ntxs} transactions and ${nfailing} failing transactions`, results)\n        })\n      })\n\n      const transaction: Transaction = {\n        delegateCall: false,\n        revertOnError: true,\n        gasLimit: optimalGasLimit,\n        target: ethers.ZeroAddress,\n        value: 0n,\n        data: new Uint8Array([])\n      }\n\n      it('Relay 2/5 transaction', async () => {\n        const results: ethers.BigNumberish[] = []\n\n        for (let i = 0; i < runs; i++) {\n          const wallet = SequenceWallet.basicWallet(context, { signing: [3, 1], idle: [1, 1, 3], threshold: 4 })\n          await wallet.deploy()\n          const tx = await wallet.sendTransactions([transaction])\n          const receipt = await tx.wait()\n\n          if (!receipt) {\n            throw new Error('No receipt')\n          }\n\n          results.push(receipt.gasUsed)\n        }\n\n        report('relay 2/5 transaction', results)\n      })\n\n      it('Relay 255/255 transaction', async () => {\n        const results: ethers.BigNumberish[] = []\n\n        for (let i = 0; i < runs; i++) {\n          const wallet = SequenceWallet.basicWallet(context, { signing: 255, idle: 0, threshold: 255 })\n          await wallet.deploy()\n\n          const tx = await wallet.sendTransactions([transaction], undefined, { gasLimit: 60000000 })\n          const receipt = await tx.wait()\n\n          if (!receipt) {\n            throw new Error('No receipt')\n          }\n\n          results.push(receipt.gasUsed)\n        }\n\n        report('relay 255/255 transaction', results)\n      })\n    })\n  }\n})\n"
  },
  {
    "path": "test/MainModule.spec.ts",
    "content": "import { ethers } from 'ethers'\nimport { ethers as hethers } from 'hardhat'\n\nimport { bytes32toAddress, getChainId, expect, expectToBeRejected, randomHex, getSigHash } from './utils'\n\nimport {\n  CallReceiverMock,\n  ContractType,\n  deploySequenceContext,\n  ModuleMock,\n  SequenceContext,\n  DelegateCallMock,\n  HookMock,\n  HookCallerMock,\n  GasBurnerMock,\n  AlwaysRevertMock\n} from './utils/contracts'\nimport { Imposter } from './utils/imposter'\nimport {\n  applyTxDefaults,\n  computeStorageKey,\n  digestOf,\n  encodeNonce,\n  leavesOf,\n  legacyTopology,\n  merkleTopology,\n  optimize2SignersTopology,\n  printTopology,\n  SignatureType,\n  SimplifiedWalletConfig,\n  subdigestOf,\n  toSimplifiedConfig,\n  WalletConfig\n} from './utils/sequence'\nimport { SequenceWallet, StaticSigner } from './utils/wallet'\n\ncontract('MainModule', (accounts: string[]) => {\n  let context: SequenceContext\n  let callReceiver: ContractType<typeof CallReceiverMock>\n\n  let wallet: SequenceWallet\n\n  before(async () => {\n    context = await deploySequenceContext()\n  })\n\n  beforeEach(async () => {\n    callReceiver = await CallReceiverMock.deploy()\n    await callReceiver.waitForDeployment()\n\n    wallet = SequenceWallet.basicWallet(context)\n    await wallet.deploy()\n  })\n\n  describe('Nested signatures', () => {\n    it('Should accept simple nested signed ERC1271 message', async () => {\n      // Wallet A\n      const wallet_a = SequenceWallet.basicWallet(context)\n      await wallet_a.deploy()\n\n      wallet = SequenceWallet.detailedWallet(context, { threshold: 1, signers: [wallet_a] })\n      await wallet.deploy()\n\n      const message = randomHex(32)\n      const signature = await wallet.signMessage(message)\n\n      const hookCaller = await HookCallerMock.deploy()\n      await hookCaller.callERC1271isValidSignatureData(wallet.address, message, signature)\n    })\n\n    it('Should accept simple nested signer', async () => {\n      // Wallet A\n      const wallet_a = SequenceWallet.basicWallet(context)\n      await wallet_a.deploy()\n\n      wallet = SequenceWallet.detailedWallet(context, { threshold: 1, signers: [wallet_a] })\n      await wallet.deploy()\n\n      const valA = 4123222n\n      const valB = randomHex(99)\n\n      const transaction = {\n        target: await callReceiver.getAddress(),\n        data: callReceiver.interface.encodeFunctionData('testCall', [valA, valB])\n      }\n\n      await wallet.sendTransactions([transaction])\n\n      expect(await callReceiver.lastValA()).to.equal(valA)\n      expect(await callReceiver.lastValB()).to.equal(valB)\n    })\n\n    it('Should accept two nested signers', async () => {\n      // WalletA\n      const wallet_a = SequenceWallet.basicWallet(context)\n      await wallet_a.deploy()\n\n      // WalletB\n      const wallet_b = SequenceWallet.basicWallet(context)\n      await wallet_b.deploy()\n\n      // Top level wallet\n      wallet = SequenceWallet.detailedWallet(context, { threshold: 1, signers: [wallet_a, wallet_b] })\n      await wallet.deploy()\n\n      const valA = 4123222n\n      const valB = randomHex(99)\n\n      const transaction = {\n        target: await callReceiver.getAddress(),\n        data: callReceiver.interface.encodeFunctionData('testCall', [valA, valB])\n      }\n\n      await wallet.sendTransactions([transaction])\n\n      expect(await callReceiver.lastValA()).to.equal(valA)\n      expect(await callReceiver.lastValB()).to.equal(valB)\n    })\n\n    it('Should accept mixed nested and eoa signers', async () => {\n      // WalletA\n      const wallet_a = SequenceWallet.basicWallet(context)\n      await wallet_a.deploy()\n\n      // EOA (B)\n      const singer_b = ethers.Wallet.createRandom()\n\n      // Top level wallet\n      wallet = SequenceWallet.detailedWallet(context, { threshold: 1, signers: [wallet_a, singer_b] })\n      await wallet.deploy()\n\n      const valA = 4123222n\n      const valB = randomHex(99)\n\n      const transaction = {\n        target: await callReceiver.getAddress(),\n        data: callReceiver.interface.encodeFunctionData('testCall', [valA, valB])\n      }\n\n      await wallet.sendTransactions([transaction])\n\n      expect(await callReceiver.lastValA()).to.equal(valA)\n      expect(await callReceiver.lastValB()).to.equal(valB)\n    })\n    ;[\n      {\n        name: '2 nested sequence wallets',\n        childs: 1,\n        depth: 2\n      },\n      {\n        name: '64 nested sequence wallets',\n        childs: 1,\n        depth: 64\n      },\n      {\n        name: '97 nested sequence wallets',\n        childs: 1,\n        depth: 97\n      },\n      {\n        name: 'binary tree of sequence wallets',\n        childs: 2,\n        depth: 5\n      },\n      {\n        name: 'ternary tree of sequence wallets',\n        childs: 3,\n        depth: 4\n      },\n      {\n        name: 'hexary tree of sequence wallets',\n        childs: 16,\n        depth: 2\n      },\n      {\n        name: 'random tree of sequence wallets (depth 1)',\n        depth: 1\n      },\n      {\n        name: 'random tree of sequence wallets (depth 2)',\n        depth: 2\n      },\n      {\n        name: 'random tree of sequence wallets (depth 3)',\n        depth: 3\n      },\n      {\n        name: 'random tree of sequence wallets (depth 4)',\n        depth: 4\n      }\n    ].map(c => {\n      it(`Should handle ${c.name}`, async () => {\n        const genWallet = async (\n          depth: number,\n          numChilds: number | undefined,\n          max: number\n        ): Promise<SequenceWallet | StaticSigner> => {\n          if (depth === max) {\n            return ethers.Wallet.createRandom()\n          }\n\n          const nchilds = numChilds || Math.floor(Math.random() * 5) + 1\n          const childs = await Promise.all(new Array(nchilds).fill(0).map(async () => genWallet(depth + 1, nchilds, max)))\n          const wallet = SequenceWallet.detailedWallet(context, { threshold: childs.length, signers: childs })\n          await wallet.deploy()\n\n          return wallet\n        }\n\n        wallet = (await genWallet(0, c.childs, c.depth)) as SequenceWallet\n\n        const valA = 5423n\n        const valB = randomHex(120)\n\n        const transaction = {\n          target: await callReceiver.getAddress(),\n          data: callReceiver.interface.encodeFunctionData('testCall', [valA, valB])\n        }\n\n        await wallet.sendTransactions([transaction], undefined, { gasLimit: 50000000 })\n\n        expect(await callReceiver.lastValA()).to.equal(valA)\n        expect(await callReceiver.lastValB()).to.equal(valB)\n      })\n    })\n\n    it('Should reject invalid nested signature', async () => {\n      const wallet_a = SequenceWallet.basicWallet(context)\n      await wallet_a.deploy()\n\n      let wallet_b = SequenceWallet.basicWallet(context)\n      await wallet_b.deploy()\n\n      wallet = SequenceWallet.detailedWallet(context, { threshold: 1, signers: [wallet_a, wallet_b] })\n      await wallet.deploy()\n\n      const imposter = Imposter.random(wallet_b.signers[0] as StaticSigner)\n      wallet_b = wallet_b.useSigners([imposter])\n      wallet = wallet.useSigners([wallet_a, wallet_b])\n\n      const transaction = {\n        target: await callReceiver.getAddress(),\n        data: callReceiver.interface.encodeFunctionData('testCall', [5423, randomHex(32)])\n      }\n\n      const subdigest = await subdigestOf(wallet.address, digestOf([transaction], await wallet.getNonce()))\n      const badNestedSignature = await wallet_b.signDigest(subdigest).then(s => s + '03')\n\n      const tx = wallet.sendTransactions([transaction])\n      await expectToBeRejected(tx, `InvalidNestedSignature(\"${subdigest}\", \"${wallet_b.address}\", \"${badNestedSignature}\")`)\n    })\n\n    it('Should enforce threshold on nested sigantures', async () => {\n      const wallet_a = SequenceWallet.basicWallet(context)\n      await wallet_a.deploy()\n\n      let wallet_b = SequenceWallet.basicWallet(context)\n      await wallet_b.deploy()\n\n      wallet = SequenceWallet.detailedWallet(context, { threshold: 3, signers: [wallet_a, wallet_b] })\n      await wallet.deploy()\n\n      const signauture = await wallet.signTransactions([{}])\n      const subdigest = await subdigestOf(wallet.address, digestOf([{}], await wallet.getNonce()))\n\n      const tx = wallet.relayTransactions([{}], signauture)\n      await expect(tx).to.be.rejected\n    })\n\n    it('Should read weight of nested wallets', async () => {\n      const wallet_a = SequenceWallet.basicWallet(context)\n      await wallet_a.deploy()\n\n      const wallet_b = SequenceWallet.basicWallet(context)\n      await wallet_b.deploy()\n\n      const wallet_c = SequenceWallet.basicWallet(context)\n      await wallet_c.deploy()\n\n      wallet = SequenceWallet.detailedWallet(context, {\n        threshold: 2,\n        signers: [wallet_a, wallet_b, { weight: 2, value: wallet_c }]\n      })\n      await wallet.deploy()\n\n      const valA = 5423n\n      const valB = randomHex(120)\n\n      const transaction = {\n        target: await callReceiver.getAddress(),\n        data: callReceiver.interface.encodeFunctionData('testCall', [valA, valB])\n      }\n\n      await wallet.useSigners(wallet_c).sendTransactions([transaction])\n\n      expect(await callReceiver.lastValA()).to.equal(valA)\n      expect(await callReceiver.lastValB()).to.equal(valB)\n    })\n  })\n\n  describe('Authentication', () => {\n    it('Should accept initial owner signature', async () => {\n      await wallet.sendTransactions([{}])\n    })\n\n    it('Should reject non-owner signature', async () => {\n      const tx = wallet.useSigners(Imposter.random(wallet.signers[0] as StaticSigner)).sendTransactions([{}])\n      await expect(tx).to.be.rejected\n    })\n\n    describe('Network ID', () => {\n      it('Should reject a transaction of another network id', async () => {\n        wallet = wallet.useChainId((await getChainId()) + 1n)\n        const tx = wallet.sendTransactions([{}])\n        await expectToBeRejected(tx, 'InvalidSignature')\n      })\n\n      describe('Universal network signatures', async () => {\n        it('Should reject signature for another network id, even if encoded as universal', async () => {\n          wallet = wallet.useChainId((await getChainId()) + 1n)\n          const tx = wallet.sendTransactions([{}])\n          await expectToBeRejected(tx, 'InvalidSignature')\n        })\n\n        it('Should reject signature with chainId zero if not using special encoding', async () => {\n          wallet = wallet.useChainId(0)\n          const tx = wallet.sendTransactions([{}])\n          await expectToBeRejected(tx, 'InvalidSignature')\n        })\n\n        it('Should accept transaction with chainId zero if encoded with no chaind type', async () => {\n          wallet = wallet.useChainId(0).useEncodingOptions({ signatureType: SignatureType.NoChaindDynamic })\n          await wallet.sendTransactions([{}])\n        })\n      })\n    })\n\n    describe('Nonce', () => {\n      const spaces = [0n, 1n, 7342n, ethers.toBigInt(ethers.randomBytes(20)), 2n ** 160n - 1n]\n\n      describe('Using non-encoded nonce', () => {\n        it('Should default to space zero', async () => {\n          await wallet.sendTransactions([{}])\n          expect(await wallet.mainModule.nonce()).to.equal(1n)\n        })\n\n        it('Should work with zero as initial nonce', async () => {\n          await wallet.sendTransactions([{}])\n          expect(await wallet.mainModule.readNonce(0)).to.equal(1n)\n        })\n\n        it('Should emit NonceChange event', async () => {\n          const receipt1 = await wallet.sendTransactions([{}]).then(t => t.wait())\n          const receipt2 = await wallet.sendTransactions([{}]).then(t => t.wait())\n\n          if (!receipt1 || !receipt2) {\n            throw new Error('No receipt')\n          }\n\n          const events1 = receipt1.logs.filter(log => log instanceof ethers.EventLog) as ethers.EventLog[]\n          const ev1 = events1.find(ev => ev.eventName === 'NonceChange')\n          expect(ev1!.eventName).to.be.eql('NonceChange')\n          expect(ev1!.args._space).to.equal(0n)\n          expect(ev1!.args._newNonce).to.equal(1n)\n\n          const events2 = receipt2.logs.filter(log => log instanceof ethers.EventLog) as ethers.EventLog[]\n          const ev2 = events2.find(ev => ev.eventName === 'NonceChange')\n          expect(ev2!.eventName).to.be.eql('NonceChange')\n          expect(ev1!.args._space).to.equal(0n)\n          expect(ev2!.args._newNonce).to.equal(2n)\n        })\n\n        it('Should fail if nonce did not change', async () => {\n          await wallet.sendTransactions([{}], 0)\n          const tx = wallet.sendTransactions([{}], 0)\n          await expectToBeRejected(tx, `BadNonce(0, 0, 1)`)\n        })\n\n        it('Should fail if nonce increased by two', async () => {\n          await wallet.sendTransactions([{}])\n          const tx = wallet.sendTransactions([{}], 2)\n          await expectToBeRejected(tx, `BadNonce(0, 2, 1)`)\n        })\n      })\n\n      spaces.forEach(space => {\n        describe(`using ${ethers.toBeHex(space)} space`, () => {\n          it('Should work with zero as initial nonce', async () => {\n            await wallet.sendTransactions([{}], encodeNonce(space, 0))\n            expect(await wallet.mainModule.readNonce(space)).to.equal(1n)\n          })\n\n          it('Should emit NonceChange event', async () => {\n            const receipt1 = await wallet.sendTransactions([{}], encodeNonce(space, 0)).then(t => t.wait())\n            const receipt2 = await wallet.sendTransactions([{}], encodeNonce(space, 1)).then(t => t.wait())\n\n            if (!receipt1 || !receipt2) {\n              throw new Error('No receipt')\n            }\n\n            const events1 = receipt1.logs.filter(log => log instanceof ethers.EventLog) as ethers.EventLog[]\n            const ev1 = events1.find(ev => ev.eventName === 'NonceChange')\n            expect(ev1!.eventName).to.be.eql('NonceChange')\n            expect(ev1!.args!._space).to.equal(space)\n            expect(ev1!.args!._newNonce).to.equal(1n)\n\n            const events2 = receipt2.logs.filter(log => log instanceof ethers.EventLog) as ethers.EventLog[]\n            const ev2 = events2.find(ev => ev.eventName === 'NonceChange')\n            expect(ev2!.eventName).to.be.eql('NonceChange')\n            expect(ev2!.args!._space).to.equal(space)\n            expect(ev2!.args!._newNonce).to.equal(2n)\n          })\n\n          it('Should accept next nonce', async () => {\n            await wallet.sendTransactions([{}], encodeNonce(space, 0))\n            await wallet.sendTransactions([{}], encodeNonce(space, 1))\n\n            expect(await wallet.mainModule.readNonce(space)).to.equal(2n)\n          })\n\n          it('Should fail if nonce did not change', async () => {\n            await wallet.sendTransactions([{}], encodeNonce(space, 0))\n            const tx = wallet.sendTransactions([{}], encodeNonce(space, 0))\n\n            await expectToBeRejected(tx, `BadNonce(${space.toString()}, 0, 1)`)\n          })\n\n          it('Should fail if nonce increased by two', async () => {\n            await wallet.sendTransactions([{}], encodeNonce(space, 0))\n            const tx = wallet.sendTransactions([{}], encodeNonce(space, 2))\n\n            await expectToBeRejected(tx, `BadNonce(${space.toString()}, 2, 1)`)\n          })\n\n          it('Should use nonces storage keys', async () => {\n            const subkey = ethers.AbiCoder.defaultAbiCoder().encode(['uint256'], [space])\n            const storageKey = computeStorageKey('org.arcadeum.module.calls.nonce', subkey)\n\n            await wallet.sendTransactions([{}], encodeNonce(space, 0))\n\n            const storageValue = await hethers.provider.getStorage(wallet.address, storageKey)\n            expect(BigInt(storageValue)).to.equal(1n)\n          })\n        })\n      })\n\n      describe('using two spaces simultaneously', () => {\n        it('Should keep separated nonce counts', async () => {\n          await wallet.sendTransactions([{}], encodeNonce(1, 0))\n\n          expect(await wallet.mainModule.readNonce(1)).to.equal(1n)\n          expect(await wallet.mainModule.readNonce(2)).to.equal(0n)\n\n          await wallet.sendTransactions([{}], encodeNonce(2, 0))\n\n          expect(await wallet.mainModule.readNonce(1)).to.equal(1n)\n          expect(await wallet.mainModule.readNonce(2)).to.equal(1n)\n          await wallet.sendTransactions([{}], encodeNonce(2, 1))\n          await wallet.sendTransactions([{}], encodeNonce(2, 2))\n\n          expect(await wallet.mainModule.readNonce(1)).to.equal(1n)\n          expect(await wallet.mainModule.readNonce(2)).to.equal(3n)\n        })\n\n        it('Should emit different events', async () => {\n          await wallet.sendTransactions([{}], encodeNonce(1, 0))\n          await wallet.sendTransactions([{}], encodeNonce(1, 1))\n\n          const receipt1 = await wallet.sendTransactions([{}], encodeNonce(1, 2)).then(t => t.wait())\n          const receipt2 = await wallet.sendTransactions([{}], encodeNonce(2, 0)).then(t => t.wait())\n\n          if (!receipt1 || !receipt2) {\n            throw new Error('No receipt')\n          }\n\n          const events1 = receipt1.logs.filter(log => log instanceof ethers.EventLog) as ethers.EventLog[]\n          const ev1 = events1.find(ev => ev.eventName === 'NonceChange')\n          expect(ev1!.eventName).to.be.eql('NonceChange')\n          expect(ev1!.args!._space).to.equal(1n)\n          expect(ev1!.args!._newNonce).to.equal(3n)\n\n          const events2 = receipt2.logs.filter(log => log instanceof ethers.EventLog) as ethers.EventLog[]\n          const ev2 = events2.find(ev => ev.eventName === 'NonceChange')\n          expect(ev2!.eventName).to.be.eql('NonceChange')\n          expect(ev2!.args!._space).to.equal(2n)\n          expect(ev2!.args!._newNonce).to.equal(1n)\n        })\n\n        it('Should not accept nonce of different space', async () => {\n          await wallet.sendTransactions([{}], encodeNonce(1, 0))\n          const tx = wallet.sendTransactions([{}], encodeNonce(2, 1))\n          await expectToBeRejected(tx, `BadNonce(2, 1, 0)`)\n        })\n      })\n    })\n\n    it('Should reject signature with invalid flag', async () => {\n      const tx = wallet.relayTransactions([{}], '0x000193812833ff01')\n      await expectToBeRejected(tx, 'InvalidSignatureFlag(255)')\n    })\n\n    it('Should reject signature with bad encoding type', async () => {\n      const tx = wallet.relayTransactions([{}], '0x2012')\n      const subdigest = await subdigestOf(wallet.address, digestOf([{}], 0))\n      await expectToBeRejected(tx, `InvalidSignatureType(\"0x20\")`)\n    })\n\n    it('Should reject direct calls to templates', async () => {\n      const tx1 = context.mainModule.execute([], 0, '0x')\n      await expectToBeRejected(tx1, 'OnlyDelegatecall')\n\n      const tx2 = context.mainModuleUpgradable.execute([], 0, '0x')\n      await expectToBeRejected(tx2, 'OnlyDelegatecall')\n    })\n\n    it('Should reject empty dynamic signature', async () => {\n      const tx = wallet.relayTransactions([{}], '0x0001000000000201ABFf4013541fd79ee5b6847C9dF3C9B34183C283000000')\n      await expectToBeRejected(tx, 'EmptySignature()')\n    })\n  })\n\n  describe('Upgradeability', () => {\n    it('Should update implementation', async () => {\n      const newImplementation = await ModuleMock.deploy()\n\n      const transaction = {\n        target: wallet.address,\n        data: wallet.mainModule.interface.encodeFunctionData('updateImplementation', [await newImplementation.getAddress()])\n      }\n\n      await wallet.sendTransactions([transaction])\n\n      const mock_wallet = ModuleMock.attach(wallet.address)\n      const tx = await mock_wallet.ping()\n      const receipt = await tx.wait()\n\n      const events = receipt!.logs.filter(log => log instanceof ethers.EventLog) as ethers.EventLog[]\n\n      expect(events![0].eventName).to.equal('Pong')\n    })\n    it('Should fail to set implementation to address 0', async () => {\n      const transaction = {\n        target: wallet.address,\n        data: wallet.mainModule.interface.encodeFunctionData('updateImplementation', [ethers.ZeroAddress])\n      }\n\n      const tx = wallet.sendTransactions([transaction])\n      await expectToBeRejected(tx, `InvalidImplementation(\"${ethers.ZeroAddress}\")`)\n    })\n    it('Should fail to set implementation to non-contract', async () => {\n      const transaction = {\n        target: wallet.address,\n        data: wallet.mainModule.interface.encodeFunctionData('updateImplementation', [accounts[1]])\n      }\n\n      const tx = wallet.sendTransactions([transaction])\n      await expectToBeRejected(tx, `InvalidImplementation(\"${accounts[1]}\")`)\n    })\n    it('Should use implementation storage key', async () => {\n      await wallet.updateImageHash(ethers.randomBytes(32))\n\n      const storageValue = await hethers.provider.getStorage(wallet.address, wallet.address)\n      expect(bytes32toAddress(storageValue)).to.equal(await context.mainModuleUpgradable.getAddress())\n    })\n  })\n\n  describe('Extra image hashes', () => {\n    ;[\n      {\n        name: 'using MainModule',\n        beforeEach: () => {}\n      },\n      {\n        name: 'using MainModuleUpgradable',\n        beforeEach: async () => {\n          const newConfig = SequenceWallet.basicWallet(context)\n          await wallet.updateImageHash(newConfig.imageHash)\n          wallet = wallet.useAddress().useConfig(newConfig.config).useSigners(newConfig.signers)\n        }\n      }\n    ].map(c => {\n      describe(c.name, () => {\n        beforeEach(c.beforeEach)\n\n        it('Should accept signatures from multiple imageHashes', async () => {\n          const altWallet = SequenceWallet.basicWallet(context, { signing: 3, idle: 9 })\n\n          await wallet.deploy()\n          await wallet.addExtraImageHash(altWallet.imageHash)\n\n          wallet.sendTransactions([{}], encodeNonce(1, 0))\n\n          expect(await wallet.mainModule.extraImageHash(altWallet.imageHash)).to.not.equal(0)\n\n          wallet = wallet\n            .useAddress()\n            .useConfig({ ...altWallet.config, address: undefined })\n            .useSigners(altWallet.signers)\n\n          await wallet.sendTransactions([{}])\n        })\n\n        it('Should reject expired extra imgeHash', async () => {\n          const altWallet = SequenceWallet.basicWallet(context, { signing: 3, idle: 9 })\n          await wallet.deploy()\n          await wallet.addExtraImageHash(altWallet.imageHash, 100)\n\n          const badWallet1 = wallet\n            .useAddress()\n            .useConfig({ ...altWallet.config, address: undefined })\n            .useSigners(altWallet.signers)\n\n          const tx = badWallet1.sendTransactions([{}])\n          await expect(tx).to.be.rejected\n        })\n\n        it('Should clear multiple extra imageHashes', async () => {\n          const altWallet1 = SequenceWallet.basicWallet(context, { signing: 3, idle: 9 })\n          const altWallet2 = SequenceWallet.basicWallet(context)\n\n          await wallet.deploy()\n          await wallet.addExtraImageHash(altWallet1.imageHash)\n          await wallet.addExtraImageHash(altWallet2.imageHash)\n\n          expect(await wallet.mainModule.extraImageHash(altWallet1.imageHash)).to.not.equal(0)\n          expect(await wallet.mainModule.extraImageHash(altWallet2.imageHash)).to.not.equal(0)\n\n          await wallet.clearExtraImageHashes([altWallet1.imageHash, altWallet2.imageHash])\n\n          const badWallet1 = wallet\n            .useAddress()\n            .useConfig({ ...altWallet1.config, address: undefined })\n            .useSigners(altWallet1.signers)\n\n          const badWallet2 = wallet\n            .useAddress()\n            .useConfig({ ...altWallet1.config, address: undefined })\n            .useSigners(altWallet1.signers)\n\n          expect(await wallet.mainModule.extraImageHash(altWallet1.imageHash)).to.equal(0n)\n          expect(await wallet.mainModule.extraImageHash(altWallet2.imageHash)).to.equal(0n)\n\n          await expect(badWallet1.sendTransactions([{}])).to.be.rejected\n          await expect(badWallet2.sendTransactions([{}])).to.be.rejected\n          await expect(wallet.sendTransactions([{}])).to.be.fulfilled\n        })\n\n        it('Should fail to set extra imageHashes if not from self', async () => {\n          const altWallet = SequenceWallet.basicWallet(context)\n          const tx = wallet.mainModule.setExtraImageHash(altWallet.imageHash, Math.floor(Date.now() / 1000) + 1000)\n          await expectToBeRejected(tx, `OnlySelfAuth(\"${accounts[0]}\", \"${wallet.address}\")`)\n        })\n\n        it('Should fail to clear extra imageHashes if not from self', async () => {\n          const tx = wallet.mainModule.clearExtraImageHashes([])\n          await expectToBeRejected(tx, `OnlySelfAuth(\"${accounts[0]}\", \"${wallet.address}\")`)\n        })\n      })\n    })\n  })\n\n  describe('Static merkle digests', () => {\n    ;[\n      {\n        name: 'using MainModule',\n        beforeEach: () => {}\n      },\n      {\n        name: 'using MainModuleUpgradable',\n        beforeEach: async () => {\n          const newConfig = SequenceWallet.basicWallet(context)\n          await wallet.updateImageHash(newConfig.imageHash)\n          wallet = wallet.useAddress().useConfig(newConfig.config).useSigners(newConfig.signers)\n        }\n      }\n    ].map(c => {\n      describe(c.name, () => {\n        it('Should reject proof for another subdigest', async () => {\n          const digests = new Array(2).fill(0).map(() => ethers.hexlify(ethers.randomBytes(32)))\n          const subdigests = await Promise.all(digests.map(async d => ({ subdigest: await subdigestOf(wallet.address, d, 0) })))\n          const subdigestsMerkle = merkleTopology([...subdigests])\n\n          const prevLeaves = leavesOf(wallet.config.topology)\n          const newMerkle = merkleTopology([subdigestsMerkle, ...prevLeaves])\n          const newConfig = { threshold: wallet.config.threshold, topology: newMerkle, checkpoint: Math.floor(Date.now() / 1000) }\n\n          await wallet.deploy()\n          await wallet.updateImageHash(newConfig)\n          wallet = wallet.useAddress(wallet.address).useConfig(newConfig)\n\n          await wallet.sendTransactions([])\n\n          const subdigest = ethers.hexlify(subdigests[0].subdigest)\n          const encoded = wallet.staticSubdigestSign(subdigest)\n          const res = await wallet.mainModule['isValidSignature(bytes32,bytes)'](digests[1], encoded)\n          expect(res).to.equal('0x00000000')\n        })\n\n        it('Should accept merkle proof', async () => {\n          wallet = SequenceWallet.basicWallet(context, { signing: 10, idle: 11 })\n\n          const digests = new Array(33).fill(0).map(() => ethers.hexlify(ethers.randomBytes(32)))\n          const subdigests = await Promise.all(digests.map(async d => ({ subdigest: await subdigestOf(wallet.address, d, 0) })))\n          const subdigestsMerkle = merkleTopology([...subdigests])\n\n          const prevLeaves = leavesOf(wallet.config.topology)\n          const newMerkle = merkleTopology([subdigestsMerkle, ...prevLeaves])\n          const newConfig = { threshold: wallet.config.threshold, topology: newMerkle, checkpoint: Math.floor(Date.now() / 1000) }\n\n          await wallet.deploy()\n          await wallet.updateImageHash(newConfig)\n          wallet = wallet.useAddress(wallet.address).useConfig(newConfig)\n\n          await wallet.sendTransactions([])\n\n          for (let i = 0; i < subdigests.length; i++) {\n            const subdigest = ethers.hexlify(subdigests[i].subdigest)\n            const encoded = wallet.staticSubdigestSign(subdigest)\n            const res = await wallet.mainModule['isValidSignature(bytes32,bytes)'](digests[i], encoded)\n            expect(res).to.equal('0x1626ba7e')\n          }\n        })\n      })\n    })\n  })\n\n  describe('Nested configurations', async () => {\n    it('Should use nested configuration as a regular branch', async () => {\n      const nested = SequenceWallet.basicWallet(context, { signing: 1, idle: 11 })\n      const simplifiedConfig = {\n        threshold: 1,\n        checkpoint: 2,\n        signers: [\n          {\n            address: ethers.Wallet.createRandom().address,\n            weight: 1\n          },\n          {\n            ...toSimplifiedConfig(nested.config),\n            weight: 1\n          }\n        ]\n      }\n\n      const config = {\n        ...simplifiedConfig,\n        topology: legacyTopology(simplifiedConfig)\n      }\n\n      const wallet = new SequenceWallet({ context, config, signers: nested.signers })\n      await wallet.deploy()\n\n      await wallet.sendTransactions([])\n    })\n    it('Should use nested configuration with independent weight and threshold', async () => {\n      const nested = SequenceWallet.basicWallet(context, { signing: 2, idle: 11 })\n      const simplifiedConfig = {\n        threshold: 5,\n        checkpoint: 2,\n        signers: [\n          {\n            ...toSimplifiedConfig(nested.config),\n            weight: 5\n          },\n          {\n            address: ethers.Wallet.createRandom().address,\n            weight: 1\n          }\n        ]\n      }\n\n      const config = {\n        ...simplifiedConfig,\n        topology: merkleTopology(simplifiedConfig)\n      }\n\n      const wallet = new SequenceWallet({ context, config, signers: nested.signers })\n      await wallet.deploy()\n\n      await wallet.sendTransactions([])\n    })\n    it('Should reject nested configuration if not enough internal signing power', async () => {\n      const nested = SequenceWallet.basicWallet(context, { signing: 2, idle: 11 })\n\n      const simplifiedConfig = {\n        threshold: 1,\n        checkpoint: 2,\n        signers: [\n          {\n            ...toSimplifiedConfig(nested.config),\n            weight: 5\n          },\n          {\n            address: ethers.Wallet.createRandom().address,\n            weight: 1\n          }\n        ]\n      }\n\n      const config = {\n        ...simplifiedConfig,\n        topology: merkleTopology(simplifiedConfig)\n      }\n\n      const wallet = new SequenceWallet({ context, config, signers: [nested.signers[0]] })\n      await wallet.deploy()\n\n      const tx = wallet.sendTransactions([])\n      await expectToBeRejected(tx, 'InvalidSignature')\n    })\n    it('Should limit nested signing power', async () => {\n      const nested = SequenceWallet.basicWallet(context, { signing: 10 })\n\n      const simplifiedConfig = {\n        threshold: 2,\n        checkpoint: 2,\n        signers: [\n          {\n            ...toSimplifiedConfig(nested.config),\n            weight: 1\n          },\n          {\n            address: ethers.Wallet.createRandom().address,\n            weight: 1\n          }\n        ]\n      }\n\n      const config = {\n        ...simplifiedConfig,\n        topology: merkleTopology(simplifiedConfig)\n      }\n\n      const wallet = new SequenceWallet({ context, config, signers: nested.signers })\n      await wallet.deploy()\n\n      const tx = wallet.sendTransactions([])\n      await expectToBeRejected(tx, 'InvalidSignature')\n    })\n  })\n\n  describe('External calls', () => {\n    let { valA, valB } = { valA: BigInt(Math.floor(Date.now())), valB: randomHex(120) }\n\n    it('Should perform call to contract', async () => {\n      const transaction = {\n        target: await callReceiver.getAddress(),\n        data: callReceiver.interface.encodeFunctionData('testCall', [valA, valB])\n      }\n\n      await wallet.sendTransactions([transaction])\n      expect(await callReceiver.lastValA()).to.equal(valA)\n      expect(await callReceiver.lastValB()).to.equal(valB)\n    })\n    it('Should return error message', async () => {\n      await callReceiver.setRevertFlag(true)\n\n      const transaction = {\n        target: await callReceiver.getAddress(),\n        data: callReceiver.interface.encodeFunctionData('testCall', [0, new Uint8Array([])])\n      }\n\n      const tx = wallet.sendTransactions([transaction])\n      await expect(tx).to.be.rejectedWith('CallReceiverMock#testCall: REVERT_FLAG')\n    })\n    describe('Batch transactions', () => {\n      let callReceiver2: ContractType<typeof CallReceiverMock>\n      let { val2A, val2B } = { val2A: 9422n, val2B: randomHex(31) }\n\n      beforeEach(async () => {\n        callReceiver2 = await CallReceiverMock.deploy()\n      })\n\n      it('Should perform multiple calls to contracts in one tx', async () => {\n        const transactions = [\n          {\n            target: await callReceiver.getAddress(),\n            data: callReceiver.interface.encodeFunctionData('testCall', [valA, valB])\n          },\n          {\n            target: await callReceiver2.getAddress(),\n            data: callReceiver2.interface.encodeFunctionData('testCall', [val2A, val2B])\n          }\n        ]\n\n        await wallet.sendTransactions(transactions)\n        expect(await callReceiver.lastValA()).to.equal(valA)\n        expect(await callReceiver.lastValB()).to.equal(valB)\n        expect(await callReceiver2.lastValA()).to.equal(val2A)\n        expect(await callReceiver2.lastValB()).to.equal(val2B)\n      })\n\n      it('Should perform call a contract and transfer eth in one tx', async () => {\n        const signer = await hethers.provider.getSigner()\n        await signer.sendTransaction({ to: wallet.address, value: 100 })\n        const receiver = new ethers.Wallet(ethers.hexlify(ethers.randomBytes(32)))\n\n        const transactions = [\n          {\n            target: await callReceiver.getAddress(),\n            data: callReceiver.interface.encodeFunctionData('testCall', [valA, valB])\n          },\n          {\n            target: receiver.address,\n            value: 26\n          }\n        ]\n\n        await wallet.sendTransactions(transactions)\n\n        expect(await callReceiver.lastValA()).to.equal(valA)\n        expect(await callReceiver.lastValB()).to.equal(valB)\n        expect(await hethers.provider.getBalance(receiver.address)).to.equal(26n)\n      })\n\n      it('Should fail if one transaction fails', async () => {\n        const signer = await hethers.provider.getSigner()\n        await signer.sendTransaction({ to: wallet.address, value: 100 })\n\n        await callReceiver.setRevertFlag(true)\n\n        const transactions = [\n          {\n            target: ethers.Wallet.createRandom().address,\n            value: 26\n          },\n          {\n            target: await callReceiver.getAddress(),\n            data: callReceiver.interface.encodeFunctionData('testCall', [0, new Uint8Array([])])\n          }\n        ]\n\n        const tx = wallet.sendTransactions(transactions)\n        await expect(tx).to.be.rejectedWith('CallReceiverMock#testCall: REVERT_FLAG')\n      })\n    })\n  })\n\n  describe('Delegate calls', () => {\n    let delegateCallMock: ContractType<typeof DelegateCallMock>\n\n    beforeEach(async () => {\n      delegateCallMock = await DelegateCallMock.deploy()\n    })\n\n    it('Should delegate call to module', async () => {\n      const transaction1 = {\n        delegateCall: true,\n        target: await delegateCallMock.getAddress(),\n        data: delegateCallMock.interface.encodeFunctionData('write', [11, 45])\n      }\n\n      await wallet.sendTransactions([transaction1])\n\n      const transaction2 = {\n        delegateCall: true,\n        target: await delegateCallMock.getAddress(),\n        data: delegateCallMock.interface.encodeFunctionData('read', [11])\n      }\n\n      const tx = await wallet.sendTransactions([transaction2])\n      const receipt = await tx.wait()\n\n      if (!receipt) {\n        throw new Error('No receipt')\n      }\n\n      const val = BigInt(receipt.logs.slice(-2)[0].data)\n      expect(val).to.equal(45n)\n    })\n\n    describe('on delegate call revert', () => {\n      beforeEach(async () => {\n        await wallet.sendTransactions([\n          {\n            delegateCall: true,\n            target: await delegateCallMock.getAddress(),\n            data: delegateCallMock.interface.encodeFunctionData('setRevertFlag', [true])\n          }\n        ])\n      })\n\n      it('Should pass if revertOnError is false', async () => {\n        const transaction = {\n          delegateCall: true,\n          revertOnError: false,\n          target: await delegateCallMock.getAddress(),\n          data: delegateCallMock.interface.encodeFunctionData('write', [11, 45])\n        }\n\n        await wallet.sendTransactions([transaction])\n      })\n\n      it('Should fail if delegate call fails', async () => {\n        const transaction = {\n          delegateCall: true,\n          target: await delegateCallMock.getAddress(),\n          data: delegateCallMock.interface.encodeFunctionData('write', [11, 45])\n        }\n\n        const tx = wallet.sendTransactions([transaction])\n        await expect(tx).to.be.rejectedWith('DelegateCallMock#write: REVERT_FLAG')\n      })\n    })\n  })\n\n  describe('Handle ETH', () => {\n    it('Should receive ETH', async () => {\n      const signer = await hethers.provider.getSigner()\n      signer.sendTransaction({ to: wallet.address, value: 1 })\n    })\n\n    it('Should transfer ETH', async () => {\n      const signer = await hethers.provider.getSigner()\n      signer.sendTransaction({ to: wallet.address, value: 100 })\n\n      const receiver = ethers.Wallet.createRandom().address\n\n      const transaction = {\n        target: receiver,\n        value: 25\n      }\n\n      await wallet.sendTransactions([transaction])\n      expect(await hethers.provider.getBalance(receiver)).to.equal(25n)\n    })\n\n    it('Should call payable function', async () => {\n      const signer = await hethers.provider.getSigner()\n      signer.sendTransaction({ to: wallet.address, value: 100 })\n\n      const valA = 63129n\n      const valB = randomHex(120)\n      const value = 33n\n\n      const transaction = {\n        target: await callReceiver.getAddress(),\n        value: value,\n        data: callReceiver.interface.encodeFunctionData('testCall', [valA, valB])\n      }\n\n      await wallet.sendTransactions([transaction])\n      expect(await hethers.provider.getBalance(await callReceiver.getAddress())).to.equal(value)\n      expect(await callReceiver.lastValA()).to.equal(valA)\n      expect(await callReceiver.lastValB()).to.equal(valB)\n    })\n  })\n\n  describe('Optional transactions', () => {\n    it('Should skip a skipOnError transaction', async () => {\n      await callReceiver.setRevertFlag(true)\n\n      const transaction = {\n        revertOnError: false,\n        target: await callReceiver.getAddress(),\n        data: callReceiver.interface.encodeFunctionData('testCall', [0, new Uint8Array([])])\n      }\n\n      const receipt = await wallet.sendTransactions([transaction]).then(r => r.wait())\n\n      if (!receipt) {\n        throw new Error('No receipt')\n      }\n\n      const events = receipt.logs.filter(log => log instanceof ethers.EventLog) as ethers.EventLog[]\n      const event = events.pop()\n\n      const reason = ethers.AbiCoder.defaultAbiCoder().decode(['string'], '0x' + event!.args!._reason.slice(10))[0]\n      expect(reason).to.equal('CallReceiverMock#testCall: REVERT_FLAG')\n    })\n\n    describe('With multiple transactions', () => {\n      let callReceiver2: ContractType<typeof CallReceiverMock>\n      const { valA, valB } = { valA: 912341n, valB: randomHex(30) }\n\n      beforeEach(async () => {\n        callReceiver2 = await CallReceiverMock.deploy()\n      })\n\n      it('Should skip failing transaction within batch', async () => {\n        await callReceiver.setRevertFlag(true)\n\n        const transactions = [\n          {\n            revertOnError: false,\n            target: await callReceiver.getAddress(),\n            data: callReceiver.interface.encodeFunctionData('testCall', [0, new Uint8Array([])])\n          },\n          {\n            revertOnError: false,\n            target: await callReceiver2.getAddress(),\n            data: callReceiver2.interface.encodeFunctionData('testCall', [valA, valB])\n          }\n        ]\n\n        const receipt = await wallet.sendTransactions(transactions).then(r => r.wait())\n\n        if (!receipt) {\n          throw new Error('No receipt')\n        }\n\n        const events = receipt.logs.filter(log => log instanceof ethers.EventLog) as ethers.EventLog[]\n        const ev = events.find(ev => ev.eventName === 'TxFailed')\n\n        const reason = ethers.AbiCoder.defaultAbiCoder().decode(['string'], '0x' + ev!.args!._reason.slice(10))[0]\n        expect(reason).to.equal('CallReceiverMock#testCall: REVERT_FLAG')\n\n        expect(await callReceiver2.lastValA()).to.equal(valA)\n        expect(await callReceiver2.lastValB()).to.equal(valB)\n      })\n\n      it('Should skip multiple failing transactions within batch', async () => {\n        await callReceiver.setRevertFlag(true)\n\n        const transactions = [\n          {\n            revertOnError: false,\n            target: await callReceiver.getAddress(),\n            data: callReceiver.interface.encodeFunctionData('testCall', [0, new Uint8Array([])])\n          },\n          {\n            revertOnError: false,\n            target: await callReceiver.getAddress(),\n            data: callReceiver.interface.encodeFunctionData('testCall', [0, new Uint8Array([])])\n          },\n          {\n            target: await callReceiver2.getAddress(),\n            data: callReceiver2.interface.encodeFunctionData('testCall', [valA, valB])\n          }\n        ]\n\n        const txHash = await subdigestOf(wallet.address, digestOf(transactions, await wallet.getNonce()))\n        const receipt = await wallet.sendTransactions(transactions).then(r => r.wait())\n\n        if (!receipt) {\n          throw new Error('No receipt')\n        }\n\n        const events = receipt.logs.filter(log => log instanceof ethers.EventLog) as ethers.EventLog[]\n\n        const event1 = events[1]\n        const event2 = events[2]\n\n        const reason1 = ethers.AbiCoder.defaultAbiCoder().decode(['string'], '0x' + event1.args!._reason.slice(10))[0]\n        const reason2 = ethers.AbiCoder.defaultAbiCoder().decode(['string'], '0x' + event2.args!._reason.slice(10))[0]\n\n        expect(reason1).to.equal('CallReceiverMock#testCall: REVERT_FLAG')\n        expect(reason2).to.equal('CallReceiverMock#testCall: REVERT_FLAG')\n\n        expect(event1.args!._tx).to.equal(txHash)\n        expect(event2.args!._tx).to.equal(txHash)\n\n        expect(await callReceiver2.lastValA()).to.equal(valA)\n        expect(await callReceiver2.lastValB()).to.equal(valB)\n      })\n\n      it('Should skip all failing transactions within a batch', async () => {\n        await callReceiver.setRevertFlag(true)\n\n        const transactions = [\n          {\n            revertOnError: false,\n            target: await callReceiver.getAddress(),\n            data: callReceiver.interface.encodeFunctionData('testCall', [0, new Uint8Array([])])\n          },\n          {\n            revertOnError: false,\n            target: await callReceiver.getAddress(),\n            data: callReceiver.interface.encodeFunctionData('testCall', [0, new Uint8Array([])])\n          }\n        ]\n\n        const receipt = await wallet.sendTransactions(transactions).then(r => r.wait())\n\n        if (!receipt) {\n          throw new Error('No receipt')\n        }\n\n        const events = receipt.logs.filter(log => log instanceof ethers.EventLog) as ethers.EventLog[]\n\n        const event1 = events.pop()\n        const event2 = events.pop()\n\n        const reason1 = ethers.AbiCoder.defaultAbiCoder().decode(['string'], '0x' + event1!.args!._reason.slice(10))[0]\n        const reason2 = ethers.AbiCoder.defaultAbiCoder().decode(['string'], '0x' + event2!.args!._reason.slice(10))[0]\n\n        expect(reason1).to.equal('CallReceiverMock#testCall: REVERT_FLAG')\n        expect(reason2).to.equal('CallReceiverMock#testCall: REVERT_FLAG')\n      })\n    })\n  })\n\n  describe('Hooks', () => {\n    let hookCallerMock: ContractType<typeof HookCallerMock>\n\n    before(async () => {\n      hookCallerMock = await HookCallerMock.deploy()\n    })\n\n    describe('receive tokens', () => {\n      it('Should implement ERC1155 single transfer hook', async () => {\n        await hookCallerMock.callERC1155Received(wallet.address)\n      })\n      it('Should implement ERC1155 batch transfer hook', async () => {\n        await hookCallerMock.callERC1155BatchReceived(wallet.address)\n      })\n      it('Should implement ERC721 transfer hook', async () => {\n        await hookCallerMock.callERC721Received(wallet.address)\n      })\n      it('Should implement ERC223 transfer hook', async () => {\n        await hookCallerMock.callERC223Received(wallet.address)\n      })\n    })\n\n    describe('ERC1271 Wallet', () => {\n      let message = randomHex(250)\n      let hash = ethers.keccak256(ethers.randomBytes(32))\n\n      it('Should validate arbitrary signed data', async () => {\n        const signature = await wallet.signMessage(message)\n        await hookCallerMock.callERC1271isValidSignatureData(wallet.address, message, signature)\n      })\n\n      it('Should validate arbitrary signed hash', async () => {\n        const signature = await wallet.signDigest(hash)\n        await hookCallerMock.callERC1271isValidSignatureHash(wallet.address, hash, signature)\n      })\n\n      it('Should reject data signed by non-owner', async () => {\n        const impostor = SequenceWallet.basicWallet(context)\n        const signature = await impostor.signMessage(message)\n        const tx = hookCallerMock.callERC1271isValidSignatureData(wallet.address, message, signature)\n        await expect(tx).to.be.rejectedWith('HookCallerMock#callERC1271isValidSignatureData: INVALID_RETURN')\n      })\n\n      it('Should reject hash signed by non-owner', async () => {\n        const impostor = SequenceWallet.basicWallet(context)\n        const signature = await impostor.signDigest(hash)\n        const tx = hookCallerMock.callERC1271isValidSignatureHash(wallet.address, hash, signature)\n        await expect(tx).to.be.rejectedWith('HookCallerMock#callERC1271isValidSignatureHash: INVALID_RETURN')\n      })\n    })\n\n    describe('External hooks', () => {\n      let hookMock: ContractType<typeof HookMock>\n      let hookSelector: string\n\n      before(async () => {\n        hookMock = await HookMock.deploy()\n        const fragment = hookMock.interface.getFunction('onHookMockCall')\n        hookSelector = getSigHash(fragment)\n      })\n\n      it('Should return zero if hook is not registered', async () => {\n        expect(await wallet.mainModule.readHook(hookSelector)).to.be.equal(ethers.ZeroAddress)\n      })\n\n      describe('With registered hook', () => {\n        beforeEach(async () => {\n          const transaction = {\n            target: wallet.address,\n            data: wallet.mainModule.interface.encodeFunctionData('addHook', [hookSelector, await hookMock.getAddress()])\n          }\n\n          await wallet.sendTransactions([transaction])\n        })\n\n        it('Should read added hook', async () => {\n          expect(await wallet.mainModule.readHook(hookSelector)).to.be.equal(await hookMock.getAddress())\n        })\n\n        it('Should forward call to external hook', async () => {\n          const walletHook = HookMock.attach(wallet.address)\n          expect(await walletHook.onHookMockCall(21)).to.equal(42n)\n        })\n\n        it('Should not forward call to deregistered hook', async () => {\n          const transaction = {\n            target: wallet.address,\n            data: wallet.mainModule.interface.encodeFunctionData('removeHook', [hookSelector])\n          }\n\n          await wallet.sendTransactions([transaction])\n\n          const tx2 = HookMock.attach(wallet.address).onHookMockCall(21)\n          await expect(tx2).to.be.rejected\n        })\n\n        it('Should use hooks storage key', async () => {\n          const subkey = ethers.AbiCoder.defaultAbiCoder().encode(['bytes4'], [hookSelector])\n          const storageKey = computeStorageKey('org.arcadeum.module.hooks.hooks', subkey)\n\n          const storageValue = await hethers.provider.getStorage(wallet.address, storageKey)\n\n          const addr = (() => {\n            try {\n              return ethers.getAddress(ethers.AbiCoder.defaultAbiCoder().decode(['address'], storageValue)[0])\n            } catch {\n              return ethers.getAddress(storageValue)\n            }\n          })()\n\n          expect(addr).to.equal(await hookMock.getAddress())\n        })\n      })\n\n      it('Should pass calling a non registered hook', async () => {\n        const data = ethers.AbiCoder.defaultAbiCoder().encode(['bytes4'], [hookSelector])\n        const signer = await hethers.provider.getSigner()\n        await signer.sendTransaction({ to: wallet.address, data: data })\n      })\n    })\n\n    it('Should not forward msg.data with less than 4 bytes', async () => {\n      const alwaysRevertMock = await AlwaysRevertMock.deploy()\n      const paddedSelector = '0x11223300'\n\n      const transaction = {\n        target: wallet.address,\n        data: wallet.mainModule.interface.encodeFunctionData('addHook', [paddedSelector, await alwaysRevertMock.getAddress()])\n      }\n\n      await wallet.sendTransactions([transaction])\n\n      const signer = await hethers.provider.getSigner()\n\n      // Calling the wallet with '0x112233' should not forward the call to the hook\n      const tx = signer.sendTransaction({ to: wallet.address, data: '0x112233' }).then(t => t.wait())\n      await expect(tx).to.be.fulfilled\n\n      // Calling the wallet with '0x11223300' should forward the call to the hook (and thus revert)\n      const tx2 = signer.sendTransaction({ to: wallet.address, data: '0x11223300' }).then(t => t.wait())\n      await expect(tx2).to.be.rejected\n    })\n\n    it('Should emit an event when adding a hook', async () => {\n      const selector = '0x2385ac0a'\n      const implementation = ethers.Wallet.createRandom().address\n\n      const transaction = {\n        target: wallet.address,\n        data: wallet.mainModule.interface.encodeFunctionData('addHook', [selector, implementation])\n      }\n\n      const receipt = await wallet.sendTransactions([transaction]).then(t => t.wait())\n\n      if (!receipt) {\n        throw new Error('No receipt')\n      }\n\n      const events = receipt.logs.filter(log => log instanceof ethers.EventLog) as ethers.EventLog[]\n      const event = events.find(ev => ev.eventName === 'DefinedHook')\n      expect(event).to.not.be.undefined\n      expect(event!.args._signature).to.equal(selector)\n      expect(event!.args._implementation).to.equal(implementation)\n    })\n\n    it('Should emit an event when removing a hook', async () => {\n      const selector = '0x2385ac0a'\n      const implementation = ethers.Wallet.createRandom().address\n\n      const transaction1 = {\n        target: wallet.address,\n        data: wallet.mainModule.interface.encodeFunctionData('addHook', [selector, implementation])\n      }\n\n      await wallet.sendTransactions([transaction1]).then(t => t.wait())\n\n      const transaction2 = {\n        target: wallet.address,\n        data: wallet.mainModule.interface.encodeFunctionData('removeHook', [selector])\n      }\n\n      const receipt = await wallet.sendTransactions([transaction2]).then(t => t.wait())\n\n      if (!receipt) {\n        throw new Error('No receipt')\n      }\n\n      const events = receipt.logs.filter(log => log instanceof ethers.EventLog) as ethers.EventLog[]\n      const event = events.find(ev => ev.eventName === 'DefinedHook')\n      expect(event).to.not.be.undefined\n      expect(event?.args._signature).to.equal(selector)\n      expect(event?.args._implementation).to.equal(ethers.ZeroAddress)\n    })\n  })\n\n  describe('Update owners', async () => {\n    let walletb: SequenceWallet\n\n    beforeEach(async () => {\n      walletb = SequenceWallet.basicWallet(context, { address: wallet.address })\n    })\n\n    it('Should fail to update to invalid image hash', async () => {\n      const tx = wallet.updateImageHash(ethers.ZeroHash)\n      await expectToBeRejected(tx, 'ImageHashIsZero()')\n    })\n\n    it('Should fail to change image hash from non-self address', async () => {\n      const tx = wallet.mainModule.updateImageHash(ethers.randomBytes(32))\n      await expectToBeRejected(tx, `OnlySelfAuth(\"${accounts[0]}\", \"${wallet.address}\")`)\n    })\n\n    describe('After a migration', async () => {\n      beforeEach(async () => {\n        await wallet.updateImageHash(walletb.imageHash)\n      })\n\n      it('Should implement new upgradable module', async () => {\n        expect(await walletb.mainModuleUpgradable.imageHash()).to.equal(walletb.imageHash)\n      })\n\n      it('Should accept new owner signature', async () => {\n        await walletb.sendTransactions([{}])\n      })\n\n      it('Should reject old owner signature', async () => {\n        const tx = wallet.sendTransactions([{}])\n        await expect(tx).to.be.rejected\n      })\n\n      it('Should fail to update to invalid image hash', async () => {\n        const tx = walletb.updateImageHash(ethers.ZeroHash)\n        await expectToBeRejected(tx, 'ImageHashIsZero()')\n      })\n\n      it('Should fail to change image hash from non-self address', async () => {\n        const tx = wallet.mainModuleUpgradable.updateImageHash(ethers.randomBytes(32))\n        await expectToBeRejected(tx, `OnlySelfAuth(\"${accounts[0]}\", \"${wallet.address}\")`)\n      })\n\n      it('Should use image hash storage key', async () => {\n        const storageKey = computeStorageKey('org.arcadeum.module.auth.upgradable.image.hash')\n        const storageValue = await hethers.provider.getStorage(wallet.address, storageKey)\n        expect(ethers.AbiCoder.defaultAbiCoder().encode(['bytes32'], [storageValue])).to.equal(walletb.imageHash)\n      })\n\n      it('Should fail to execute transactions on moduleUpgradable implementation', async () => {\n        const tx = context.mainModuleUpgradable.execute([], 0, '0x0000')\n        await expect(tx).to.be.rejected\n      })\n\n      describe('After updating the image hash', () => {\n        let walletc: SequenceWallet\n\n        beforeEach(async () => {\n          walletc = SequenceWallet.basicWallet(context, { signing: 2, address: walletb.address })\n          await walletb.updateImageHash(walletc.imageHash)\n        })\n\n        it('Should have updated the image hash', async () => {\n          expect(await walletb.mainModuleUpgradable.imageHash()).to.equal(walletc.imageHash)\n        })\n\n        it('Should accept new owners signatures', async () => {\n          await walletc.sendTransactions([{}])\n        })\n\n        it('Should reject old owner signatures', async () => {\n          const tx = walletb.sendTransactions([{}])\n          await expect(tx).to.be.rejected\n        })\n\n        it('Should use image hash storage key', async () => {\n          const storageKey = computeStorageKey('org.arcadeum.module.auth.upgradable.image.hash')\n          const storageValue = await hethers.provider.getStorage(walletb.address, storageKey)\n          expect(ethers.AbiCoder.defaultAbiCoder().encode(['bytes32'], [storageValue])).to.equal(walletc.imageHash)\n        })\n      })\n    })\n  })\n\n  describe('Multisignature', async () => {\n    const modes = [\n      {\n        name: 'Forced dynamic part encoding, legacy signature type',\n        encodingOptions: { forceDynamicEncoding: true, signatureType: SignatureType.Legacy }\n      },\n      {\n        name: 'Default part encoding, legacy signature encoding',\n        encodingOptions: { signatureType: SignatureType.Legacy }\n      },\n      {\n        name: 'Forced dynamic part encoding, dynamic signature type',\n        encodingOptions: { forceDynamicEncoding: true, signatureType: SignatureType.Dynamic }\n      },\n      {\n        name: 'Default part encoding, dynamic signature type',\n        encodingOptions: { signatureType: SignatureType.Legacy }\n      }\n    ]\n\n    modes.map(mode => {\n      describe(mode.name, () => {\n        let encodingOptions = mode.encodingOptions\n\n        describe('With 1/2 wallet', () => {\n          let signer1 = ethers.Wallet.createRandom()\n          let signer2 = ethers.Wallet.createRandom()\n\n          beforeEach(async () => {\n            wallet = SequenceWallet.detailedWallet(context, { threshold: 1, signers: [signer1, signer2], encodingOptions })\n            await wallet.deploy()\n          })\n\n          it('Should accept signed message by first owner', async () => {\n            await wallet.useSigners(signer1).sendTransactions([{}])\n          })\n\n          it('Should accept signed message by second owner', async () => {\n            await wallet.useSigners(signer2).sendTransactions([{}])\n          })\n\n          it('Should accept signed message by both owners', async () => {\n            await wallet.useSigners([signer1, signer2]).sendTransactions([{}])\n          })\n\n          it('Should reject message without signatures', async () => {\n            const tx = wallet.useSigners([]).sendTransactions([{}])\n            await expect(tx).to.be.rejected\n          })\n\n          it('Should reject message signed by non-owner', async () => {\n            const tx = wallet.useSigners(Imposter.random(signer1)).sendTransactions([{}])\n            await expect(tx).to.be.rejected\n          })\n\n          it('Should reject message signed by non-owner and signer2', async () => {\n            const tx = wallet.useSigners([signer2, Imposter.random(signer1)]).sendTransactions([{}])\n            await expect(tx).to.be.rejected\n          })\n\n          it('Should reject signature of invalid length', async () => {\n            const signature = await wallet.signTransactions([{}])\n            const badSignature = signature.slice(0, -2)\n            const tx = wallet.relayTransactions([{}], badSignature)\n            await expect(tx).to.be.rejected\n          })\n        })\n\n        describe('With 2/2 wallet', () => {\n          let signer1 = ethers.Wallet.createRandom()\n          let signer2 = ethers.Wallet.createRandom()\n\n          beforeEach(async () => {\n            wallet = SequenceWallet.detailedWallet(context, { threshold: 2, signers: [signer1, signer2], encodingOptions })\n            await wallet.deploy()\n          })\n\n          it('Should accept signed message by both owners', async () => {\n            await wallet.sendTransactions([{}])\n          })\n\n          it('Should reject message without signatures', async () => {\n            const tx = wallet.useSigners([]).sendTransactions([{}])\n            await expect(tx).to.be.rejected\n          })\n\n          it('Should reject message signed only by first owner', async () => {\n            const tx = wallet.useSigners(signer1).sendTransactions([{}])\n            await expect(tx).to.be.rejected\n          })\n\n          it('Should reject message signed only by second owner', async () => {\n            const tx = wallet.useSigners(signer2).sendTransactions([{}])\n            await expect(tx).to.be.rejected\n          })\n\n          it('Should reject message signed by non-owner', async () => {\n            const tx = wallet.useSigners(Imposter.random(signer1)).sendTransactions([{}])\n            await expect(tx).to.be.rejected\n          })\n        })\n\n        describe('With 2/3 wallet', () => {\n          let signer1 = ethers.Wallet.createRandom()\n          let signer2 = ethers.Wallet.createRandom()\n          let signer3 = ethers.Wallet.createRandom()\n\n          beforeEach(async () => {\n            wallet = SequenceWallet.detailedWallet(context, {\n              threshold: 2,\n              signers: [signer1, signer2, signer3],\n              encodingOptions\n            })\n            await wallet.deploy()\n          })\n\n          it('Should accept signed message by first and second owner', async () => {\n            await wallet.useSigners([signer1, signer2]).sendTransactions([{}])\n          })\n\n          it('Should accept signed message by first and last owner', async () => {\n            await wallet.useSigners([signer1, signer3]).sendTransactions([{}])\n          })\n\n          it('Should accept signed message by second and last owner', async () => {\n            await wallet.useSigners([signer2, signer3]).sendTransactions([{}])\n          })\n\n          it('Should accept signed message by all owners', async () => {\n            await wallet.useSigners([signer1, signer2, signer3]).sendTransactions([{}])\n          })\n\n          it('Should reject message signed only by first owner', async () => {\n            const tx = wallet.useSigners(signer1).sendTransactions([{}])\n            await expect(tx).to.be.rejected\n          })\n\n          it('Should reject message signed only by second owner', async () => {\n            const tx = wallet.useSigners(signer2).sendTransactions([{}])\n            await expect(tx).to.be.rejected\n          })\n\n          it('Should reject message signed only by last owner', async () => {\n            const tx = wallet.useSigners(signer3).sendTransactions([{}])\n            await expect(tx).to.be.rejected\n          })\n\n          it('Should reject message not signed', async () => {\n            const tx = wallet.useSigners([]).sendTransactions([{}])\n            await expect(tx).to.be.rejected\n          })\n\n          it('Should reject message signed by non-owner', async () => {\n            const tx = wallet.useSigners(Imposter.random(signer1)).sendTransactions([{}])\n            await expect(tx).to.be.rejected\n          })\n\n          it('Should reject message signed by non-owner and signer1', async () => {\n            const tx = wallet.useSigners([signer1, Imposter.random(signer2)]).sendTransactions([{}])\n            await expect(tx).to.be.rejected\n          })\n\n          it('Should reject message signed by two non-owners', async () => {\n            const tx = wallet.useSigners([Imposter.random(signer1), Imposter.random(signer2)]).sendTransactions([{}])\n            await expect(tx).to.be.rejected\n          })\n\n          it('Should reject message if signed with a wrong configuration', async () => {\n            const tx = wallet\n              .useConfig(SequenceWallet.detailedWallet(context, { threshold: 3, signers: [signer1, signer2] }))\n              .sendTransactions([{}])\n            await expect(tx).to.be.rejected\n          })\n        })\n\n        describe('With 3/10 wallet', () => {\n          beforeEach(async () => {\n            wallet = SequenceWallet.basicWallet(context, { signing: 3, idle: 7 })\n            await wallet.deploy()\n          })\n\n          it('Should accept message signed by 3/10 owners', async () => {\n            await wallet.sendTransactions([{}])\n          })\n        })\n\n        describe('With 255/255 wallet', () => {\n          beforeEach(async () => {\n            wallet = SequenceWallet.basicWallet(context, { signing: 215, encodingOptions })\n            await wallet.deploy()\n          })\n\n          it('Should accept message signed by all owners', async () => {\n            await wallet.sendTransactions([{}], undefined, { gasLimit: 60000000 })\n          })\n\n          it('Should reject message signed by non-owner', async () => {\n            const tx = wallet\n              .useSigners([Imposter.random(wallet.signers[0] as ethers.Wallet), ...wallet.signers.slice(1)])\n              .sendTransactions([{}], undefined, { gasLimit: 60000000 })\n\n            await expect(tx).to.be.rejected\n          })\n\n          it('Should reject message signed by all non-owners', async () => {\n            const tx = wallet\n              .useSigners(wallet.signers.map(s => Imposter.random(s as ethers.Wallet)))\n              .sendTransactions([{}], undefined, { gasLimit: 60000000 })\n\n            await expect(tx).to.be.rejected\n          })\n\n          it('Should reject message missing a signature', async () => {\n            const tx = wallet.useSigners(wallet.signers.slice(1)).sendTransactions([{}], undefined, { gasLimit: 60000000 })\n            await expect(tx).to.be.rejected\n          })\n        })\n\n        describe('With weighted owners', () => {\n          let signers: ethers.BaseWallet[]\n\n          beforeEach(async () => {\n            signers = new Array(5).fill(null).map(() => ethers.Wallet.createRandom())\n            wallet = SequenceWallet.detailedWallet(context, {\n              threshold: 4,\n              signers: [3, 3, 1, 1, 1].map((weight, i) => ({ weight, value: signers[i] }))\n            })\n            await wallet.deploy()\n          })\n\n          it('Should accept signed message with (3+1)/4 weight', async () => {\n            await wallet.useSigners([signers[0], signers[3]]).sendTransactions([{}])\n          })\n\n          it('Should accept signed message with (3+3)/4 weight', async () => {\n            await wallet.useSigners([signers[0], signers[1]]).sendTransactions([{}])\n          })\n\n          it('Should accept signed message with (3+3+1+1)/4 weight', async () => {\n            await wallet.useSigners(signers.slice(0, 4)).sendTransactions([{}])\n          })\n\n          it('Should accept signed message with (3+3+1+1+1)/4 weight (all signers)', async () => {\n            await wallet.useSigners(signers).sendTransactions([{}])\n          })\n\n          it('Should reject signed message with (1)/4 weight', async () => {\n            const tx = wallet.useSigners([signers[3]]).sendTransactions([{}])\n            await expect(tx).to.be.rejected\n          })\n\n          it('Should reject signed message with (1+1)/4 weight', async () => {\n            const tx = wallet.useSigners([signers[2], signers[3]]).sendTransactions([{}])\n            await expect(tx).to.be.rejected\n          })\n\n          it('Should reject signed message with (1+1+1)/4 weight', async () => {\n            const tx = wallet.useSigners([signers[2], signers[3], signers[4]]).sendTransactions([{}])\n            await expect(tx).to.be.rejected\n          })\n\n          it('Should reject signed message with (3)/4 weight', async () => {\n            const tx = wallet.useSigners(signers[0]).sendTransactions([{}])\n            await expect(tx).to.be.rejected\n          })\n\n          it('Should reject signed message with (0)/4 weight', async () => {\n            const tx = wallet.useSigners([]).sendTransactions([{}])\n            await expect(tx).to.be.rejected\n          })\n\n          it('Should reject message signed by non-owner', async () => {\n            const tx = wallet.useSigners([signers[0], Imposter.random(signers[3])]).sendTransactions([{}])\n            await expect(tx).to.be.rejected\n          })\n\n          describe('Reject invalid signatures', () => {\n            beforeEach(async () => {\n              wallet = SequenceWallet.basicWallet(context, { encodingOptions })\n              await wallet.deploy()\n            })\n\n            it('Should reject invalid signature type', async () => {\n              const signature = await wallet.signTransactions([{}])\n              const badSignature = signature.slice(0, -2) + 'ff'\n              const tx = wallet.relayTransactions([{}], badSignature)\n              await expect(tx).to.be.rejected\n            })\n\n            it('Should reject invalid s value', async () => {\n              const signature = await wallet.signTransactions([{}])\n              const prefix = mode.encodingOptions.forceDynamicEncoding ? 118 : 74\n              const invalidSignature =\n                signature.slice(0, prefix) +\n                '7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a1' +\n                signature.slice(prefix + 64)\n              const tx = wallet.relayTransactions([{}], invalidSignature)\n              await expect(tx).to.be.reverted\n            })\n\n            it('Should reject invalid v value', async () => {\n              const signature = await wallet.signTransactions([{}])\n              const prefix = mode.encodingOptions.forceDynamicEncoding ? 182 : 138\n              const invalidSignature = signature.slice(0, prefix) + '1a' + signature.slice(prefix + 2)\n              const tx = wallet.relayTransactions([{}], invalidSignature)\n              await expect(tx).to.be.reverted\n            })\n          })\n        })\n      })\n    })\n  })\n\n  describe('Gas limit', () => {\n    let gasBurner: ContractType<typeof GasBurnerMock>\n\n    before(async () => {\n      gasBurner = await GasBurnerMock.deploy()\n    })\n\n    it('Should forward the defined amount of gas', async () => {\n      const gas = 10000\n\n      const transaction = {\n        gasLimit: gas,\n        target: await gasBurner.getAddress(),\n        data: gasBurner.interface.encodeFunctionData('burnGas', [0])\n      }\n\n      const receipt = await wallet.sendTransactions([transaction]).then(t => t.wait())\n\n      if (!receipt) {\n        throw new Error('No receipt')\n      }\n\n      const reported = Number(BigInt(receipt.logs.slice(-2)[0].data))\n      expect(reported).to.be.below(gas)\n    })\n\n    it('Should forward different amounts of gas', async () => {\n      const gasA = 10000\n      const gasB = 350000\n\n      const transactions = [\n        {\n          gasLimit: gasA,\n          target: await gasBurner.getAddress(),\n          data: gasBurner.interface.encodeFunctionData('burnGas', [8000])\n        },\n        {\n          gasLimit: gasB,\n          target: await gasBurner.getAddress(),\n          data: gasBurner.interface.encodeFunctionData('burnGas', [340000])\n        }\n      ]\n\n      const receipt = await wallet.sendTransactions(transactions).then(t => t.wait())\n\n      if (!receipt) {\n        throw new Error('No receipt')\n      }\n\n      const reportedB = Number(BigInt(receipt.logs.slice(-2)[0].data))\n      const reportedA = Number(BigInt(receipt.logs.slice(-4)[0].data))\n\n      expect(reportedA).to.be.below(gasA)\n      expect(reportedB).to.be.below(gasB)\n      expect(reportedB).to.be.above(gasA)\n    })\n\n    it('Should fail if forwarded call runs out of gas', async () => {\n      const gas = 10000\n\n      const transaction = {\n        gasLimit: gas,\n        target: await gasBurner.getAddress(),\n        data: gasBurner.interface.encodeFunctionData('burnGas', [11000])\n      }\n\n      const tx = wallet.sendTransactions([transaction])\n      expect(tx).to.be.rejected\n    })\n\n    it('Should fail without reverting if optional call runs out of gas', async () => {\n      const gas = 10000\n\n      const transaction = {\n        revertOnError: false,\n        gasLimit: gas,\n        target: await gasBurner.getAddress(),\n        data: gasBurner.interface.encodeFunctionData('burnGas', [200000])\n      }\n\n      const receipt = await wallet.sendTransactions([transaction]).then(t => t.wait())\n\n      if (!receipt) {\n        throw new Error('No receipt')\n      }\n      const events = receipt.logs.filter(log => log instanceof ethers.EventLog) as ethers.EventLog[]\n\n      const log = events.pop()\n      expect(log!.eventName).to.be.equal('TxFailed')\n    })\n\n    it('Should continue execution if optional call runs out of gas', async () => {\n      const gas = 10000\n\n      const valA = 9512358833n\n      const valB = randomHex(1600)\n\n      const transactions = [\n        {\n          revertOnError: false,\n          gasLimit: gas,\n          target: await gasBurner.getAddress(),\n          data: gasBurner.interface.encodeFunctionData('burnGas', [200000])\n        },\n        {\n          revertOnError: true,\n          target: await callReceiver.getAddress(),\n          data: callReceiver.interface.encodeFunctionData('testCall', [valA, valB])\n        }\n      ]\n\n      const receipt = await wallet.sendTransactions(transactions).then(t => t.wait())\n\n      if (!receipt) {\n        throw new Error('No receipt')\n      }\n\n      const events = receipt.logs.filter(log => log instanceof ethers.EventLog) as ethers.EventLog[]\n\n      const log = events.slice(-2)[0]\n\n      expect(log.eventName).to.be.equal('TxFailed')\n      expect(await callReceiver.lastValA()).to.equal(valA)\n      expect(await callReceiver.lastValB()).to.equal(valB)\n    })\n\n    it('Should fail if transaction is executed with not enough gas', async () => {\n      const transaction = {\n        gasLimit: 1000000\n      }\n\n      const tx = wallet.sendTransactions([transaction], undefined, { gasLimit: 250000 })\n      await expect(tx).to.be.rejected\n    })\n  })\n\n  describe('Create contracts', () => {\n    it('Should create a contract', async () => {\n      const deployCode = CallReceiverMock.factory().bytecode\n\n      const transaction = {\n        target: wallet.address,\n        data: wallet.mainModule.interface.encodeFunctionData('createContract', [deployCode])\n      }\n\n      const receipt = await wallet.sendTransactions([transaction]).then(t => t.wait())\n\n      if (!receipt) {\n        throw new Error('No receipt')\n      }\n\n      const events = receipt.logs.filter(log => log instanceof ethers.EventLog) as ethers.EventLog[]\n\n      const log = events!.find(l => l.eventName === 'CreatedContract')\n\n      expect(log!.eventName).to.equal('CreatedContract')\n\n      const deployed = CallReceiverMock.attach(log!.args!._contract)\n      await deployed.testCall(12345, '0x552299')\n\n      expect(await deployed.lastValA()).to.equal(12345n)\n      expect(await deployed.lastValB()).to.equal('0x552299')\n    })\n\n    it('Should create a contract with value', async () => {\n      const deployCode = CallReceiverMock.factory().bytecode\n\n      const signer = await hethers.provider.getSigner()\n      await signer.sendTransaction({ to: wallet.address, value: 100 })\n\n      const transaction = {\n        target: wallet.address,\n        value: 99,\n        data: wallet.mainModule.interface.encodeFunctionData('createContract', [deployCode])\n      }\n\n      const receipt = await wallet.sendTransactions([transaction]).then(t => t.wait())\n\n      if (!receipt) {\n        throw new Error('No receipt')\n      }\n\n      const events = receipt.logs.filter(log => log instanceof ethers.EventLog) as ethers.EventLog[]\n\n      const log = events!.find(l => l.eventName === 'CreatedContract')\n\n      expect(await hethers.provider.getBalance(log!.args!._contract)).to.equal(99n)\n    })\n\n    it('Should fail to create a contract from non-self', async () => {\n      const tx = wallet.mainModule.createContract(CallReceiverMock.factory().bytecode)\n      await expectToBeRejected(tx, `OnlySelfAuth(\"${accounts[0]}\", \"${wallet.address}\")`)\n    })\n  })\n\n  describe('Transaction events', () => {\n    it('Should emit TxExecuted event', async () => {\n      const txHash = await subdigestOf(wallet.address, digestOf([{}], await wallet.getNonce()))\n      const res = await wallet.sendTransactions([{}])\n      const receipt = await res.wait()\n\n      if (!receipt) {\n        throw new Error('No receipt')\n      }\n\n      const events = receipt.logs.filter(log => log instanceof ethers.EventLog) as ethers.EventLog[]\n\n      const log = events!.find(l => l.eventName === 'TxExecuted')!\n\n      expect(log.topics.length).to.equal(2)\n      expect(log.topics[1]).to.be.equal(txHash)\n      expect(log.data).to.be.equal(ethers.solidityPacked(['uint256'], [0]))\n    })\n\n    it('Should emit multiple TxExecuted events', async () => {\n      const txHash = await subdigestOf(wallet.address, digestOf([{}, {}], await wallet.getNonce()))\n      const receipt = await wallet.sendTransactions([{}, {}]).then(t => t.wait())\n\n      if (!receipt) {\n        throw new Error('No receipt')\n      }\n\n      const log1 = receipt.logs[1]\n      const log2 = receipt.logs[2]\n\n      expect(log1.topics.length).to.equal(2)\n      expect(log1.topics[1]).to.be.equal(txHash)\n      expect(log1.data).to.be.equal(ethers.solidityPacked(['uint256'], [0]))\n\n      expect(log2.topics.length).to.equal(2)\n      expect(log1.topics[1]).to.be.equal(txHash)\n      expect(log2.data).to.be.equal(ethers.solidityPacked(['uint256'], [1]))\n    })\n  })\n\n  describe('Internal bundles', () => {\n    it('Should execute internal bundle', async () => {\n      const callReceiver2 = await CallReceiverMock.deploy()\n\n      const expected1 = randomHex(552)\n      const expected2 = randomHex(24)\n\n      const bundle = [\n        {\n          target: await callReceiver.getAddress(),\n          data: callReceiver.interface.encodeFunctionData('testCall', [11, expected1])\n        },\n        {\n          target: await callReceiver2.getAddress(),\n          data: callReceiver2.interface.encodeFunctionData('testCall', [12, expected2])\n        }\n      ]\n\n      const transaction = [\n        {\n          target: wallet.address,\n          data: wallet.mainModule.interface.encodeFunctionData('selfExecute', [applyTxDefaults(bundle)])\n        }\n      ]\n\n      await wallet.sendTransactions(transaction)\n\n      expect(await callReceiver.lastValA()).to.equal(11n)\n      expect(await callReceiver2.lastValA()).to.equal(12n)\n\n      expect(await callReceiver.lastValB()).to.equal(expected1)\n      expect(await callReceiver2.lastValB()).to.equal(expected2)\n    })\n\n    it('Should execute multiple internal bundles', async () => {\n      const data = [\n        [\n          { i: 0, a: 142n, b: 412 },\n          { i: 1, a: 123n, b: 2 }\n        ],\n        [\n          { i: 2, a: 142n, b: 2 },\n          { i: 3, a: 642n, b: 33 },\n          { i: 4, a: 122n, b: 12 },\n          { i: 5, a: 611n, b: 53 }\n        ],\n        [{ i: 6, a: 2n, b: 1 }],\n        []\n      ]\n\n      const contracts = await Promise.all(data.flat().map(() => CallReceiverMock.deploy()))\n      const expectedb = await Promise.all(data.flat().map(d => randomHex(d.b)))\n\n      const bundles = await Promise.all(\n        data.map(async bundle => {\n          return applyTxDefaults(\n            await Promise.all(\n              bundle.map(async obj => ({\n                target: await contracts[obj.i].getAddress(),\n                data: contracts[obj.i].interface.encodeFunctionData('testCall', [obj.a, expectedb[obj.i]])\n              }))\n            )\n          )\n        })\n      )\n\n      const transactions = bundles.map(bundle => ({\n        target: wallet.address,\n        data: wallet.mainModule.interface.encodeFunctionData('selfExecute', [bundle])\n      }))\n\n      await wallet.sendTransactions(transactions)\n\n      const lastValsA = await Promise.all(contracts.map(c => c.lastValA()))\n      const lastValsB = await Promise.all(contracts.map(c => c.lastValB()))\n\n      lastValsA.forEach((val, i) => expect(val).to.equal(data.flat()[i].a))\n      lastValsB.forEach((val, i) => expect(val).to.equal(expectedb[i]))\n    })\n\n    it('Should execute nested internal bundles', async () => {\n      const callReceiver2 = await CallReceiverMock.deploy()\n\n      const expected1 = randomHex(552)\n      const expected2 = randomHex(24)\n\n      const bundle = [\n        {\n          target: await callReceiver.getAddress(),\n          data: callReceiver.interface.encodeFunctionData('testCall', [11, expected1])\n        },\n        {\n          target: await callReceiver2.getAddress(),\n          data: callReceiver2.interface.encodeFunctionData('testCall', [12, expected2])\n        }\n      ]\n\n      const nestedBundle = [\n        {\n          target: wallet.address,\n          data: wallet.mainModule.interface.encodeFunctionData('selfExecute', [applyTxDefaults(bundle)])\n        }\n      ]\n\n      const transactions = [\n        {\n          target: wallet.address,\n          data: wallet.mainModule.interface.encodeFunctionData('selfExecute', [applyTxDefaults(nestedBundle)])\n        }\n      ]\n\n      await wallet.sendTransactions(transactions)\n\n      expect(await callReceiver.lastValA()).to.equal(11n)\n      expect(await callReceiver2.lastValA()).to.equal(12n)\n\n      expect(await callReceiver.lastValB()).to.equal(expected1)\n      expect(await callReceiver2.lastValB()).to.equal(expected2)\n    })\n\n    it('Should revert bundle without reverting transaction', async () => {\n      const callReceiver2 = await CallReceiverMock.deploy()\n      const callReceiver3 = await CallReceiverMock.deploy()\n\n      const expected1 = randomHex(552)\n      const expected2 = randomHex(24)\n      const expected3 = randomHex(11)\n\n      const bundle = [\n        {\n          target: await callReceiver.getAddress(),\n          data: callReceiver.interface.encodeFunctionData('testCall', [11, expected1])\n        },\n        {\n          target: await callReceiver2.getAddress(),\n          data: callReceiver2.interface.encodeFunctionData('testCall', [12, expected2])\n        },\n        {\n          // This transaction will revert\n          // because Factory has no fallback\n          revertOnError: true,\n          target: await context.factory.getAddress(),\n          data: callReceiver.interface.encodeFunctionData('testCall', [12, expected2])\n        }\n      ]\n\n      const transaction = [\n        {\n          revertOnError: false,\n          target: wallet.address,\n          data: wallet.mainModule.interface.encodeFunctionData('selfExecute', [applyTxDefaults(bundle)])\n        },\n        {\n          revertOnError: false,\n          target: await callReceiver3.getAddress(),\n          data: callReceiver3.interface.encodeFunctionData('testCall', [51, expected3])\n        }\n      ]\n\n      await wallet.sendTransactions(transaction)\n\n      expect(await callReceiver.lastValA()).to.equal(0n)\n      expect(await callReceiver2.lastValA()).to.equal(0n)\n      expect(await callReceiver3.lastValA()).to.equal(51n)\n\n      expect(await callReceiver.lastValB()).to.equal('0x')\n      expect(await callReceiver2.lastValB()).to.equal('0x')\n      expect(await callReceiver3.lastValB()).to.equal(expected3)\n    })\n  })\n\n  describe('Update imageHash and IPFS at once', () => {\n    ;[\n      {\n        name: 'using MainModule',\n        beforeEach: () => {}\n      },\n      {\n        name: 'using MainModuleUpgradable',\n        beforeEach: async () => {\n          const newConfig = SequenceWallet.basicWallet(context)\n          await wallet.updateImageHash(newConfig.imageHash)\n          wallet = wallet.useAddress().useConfig(newConfig.config).useSigners(newConfig.signers)\n        }\n      }\n    ].map(c => {\n      describe(c.name, () => {\n        it('Should update imageHash and IPFS at the same time', async () => {\n          const ipfs = randomHex(32)\n          const imageHash = randomHex(32)\n\n          await wallet.sendTransactions([\n            {\n              target: wallet.address,\n              data: wallet.mainModule.interface.encodeFunctionData('updateImageHashAndIPFS', [imageHash, ipfs])\n            }\n          ])\n\n          expect(await wallet.mainModuleUpgradable.imageHash()).to.equal(imageHash)\n          expect(await wallet.mainModule.ipfsRootBytes32()).to.equal(ipfs)\n        })\n\n        it('Should fail to update imageHash and IPFS if caller is not self', async () => {\n          const tx = wallet.mainModule.updateImageHashAndIPFS(randomHex(32), randomHex(32))\n          await expectToBeRejected(tx, 'OnlySelf')\n        })\n\n        it('Updated imageHash should be usable', async () => {\n          const nextWallet = SequenceWallet.basicWallet(context)\n          const ipfs = randomHex(32)\n\n          await wallet.sendTransactions([\n            {\n              target: wallet.address,\n              data: wallet.mainModule.interface.encodeFunctionData('updateImageHashAndIPFS', [nextWallet.imageHash, ipfs])\n            }\n          ])\n\n          wallet = wallet.useAddress(wallet.address).useConfig(nextWallet.config).useSigners(nextWallet.signers)\n          await wallet.sendTransactions([{}])\n        })\n      })\n    })\n  })\n})\n"
  },
  {
    "path": "test/MerkleSignatures.spec.ts",
    "content": "import { ethers } from 'ethers'\nimport { deploySequenceContext, SequenceContext } from './utils/contracts'\nimport { legacyTopology, merkleTopology, printTopology, toSimplifiedConfig } from './utils/sequence'\nimport { SequenceWallet } from './utils/wallet'\n\ncontract('MerkleSignatures', () => {\n  let context: SequenceContext\n\n  before(async () => {\n    context = await deploySequenceContext()\n  })\n\n  it('Should display config topology', async () => {\n    const wallet = SequenceWallet.basicWallet(context, { signing: 10 })\n    const simplifiedConfig = toSimplifiedConfig(wallet.config)\n    const topology1 = legacyTopology(simplifiedConfig)\n    const topology2 = merkleTopology(simplifiedConfig)\n\n    console.log(`Legacy topology:`)\n    const t = printTopology(topology1, undefined, true)\n    for (const line of t) {\n      console.log(line)\n    }\n\n    const t2 = printTopology(topology2)\n    for (const line of t2) {\n      console.log(line)\n    }\n  })\n})\n"
  },
  {
    "path": "test/MultiCallUtils.spec.ts",
    "content": "import { ethers } from 'ethers'\n\nimport { ethers as hethers } from 'hardhat'\nimport { getChainId, encodeError, expect, expectStaticToBeRejected } from './utils'\nimport { CallReceiverMock, ContractType, MultiCallUtils } from './utils/contracts'\nimport { applyTxDefault, applyTxDefaults } from './utils/sequence'\n\ncontract('Multi call utils', (accounts: string[]) => {\n  let multiCall: ContractType<typeof MultiCallUtils>\n  let callReceiver: ContractType<typeof CallReceiverMock>\n\n  before(async () => {\n    multiCall = await MultiCallUtils.deploy()\n    callReceiver = await CallReceiverMock.deploy()\n  })\n\n  beforeEach(async () => {\n    await callReceiver.setRevertFlag(false)\n  })\n\n  describe('Call multiple contracts', () => {\n    it('Should execute empty call', async () => {\n      const res = await multiCall.multiCall.staticCall([])\n\n      expect(res[0].length).to.equal(0)\n      expect(res[1].length).to.equal(0)\n    })\n    it('Should execute single call', async () => {\n      await callReceiver.testCall(5123, new Uint8Array([]))\n      const res = await multiCall.multiCall.staticCall(\n        applyTxDefaults([\n          {\n            revertOnError: false,\n            target: await callReceiver.getAddress(),\n            data: callReceiver.interface.encodeFunctionData('lastValA')\n          }\n        ])\n      )\n\n      expect(res[0].length).to.equal(1)\n      expect(res[1].length).to.equal(1)\n      expect(res[0][0]).to.be.true\n      expect(res[1][0]).to.be.equal(ethers.AbiCoder.defaultAbiCoder().encode(['uint256'], [5123]))\n    })\n    it('Should execute two calls', async () => {\n      const bytes = ethers.randomBytes(422)\n\n      await callReceiver.testCall(55522, bytes)\n      const res = await multiCall.multiCall.staticCall(\n        applyTxDefaults([\n          {\n            revertOnError: false,\n            target: await callReceiver.getAddress(),\n            data: callReceiver.interface.encodeFunctionData('lastValA')\n          },\n          {\n            revertOnError: false,\n            target: await callReceiver.getAddress(),\n            data: callReceiver.interface.encodeFunctionData('lastValB')\n          }\n        ])\n      )\n\n      expect(res[0].length).to.equal(2)\n      expect(res[1].length).to.equal(2)\n      expect(res[0][0]).to.be.true\n      expect(res[1][0]).to.be.equal(ethers.AbiCoder.defaultAbiCoder().encode(['uint256'], [55522]))\n      expect(res[0][1]).to.be.true\n      expect(ethers.AbiCoder.defaultAbiCoder().decode(['bytes'], res[1][1])[0]).to.be.equal(ethers.hexlify(bytes))\n    })\n    it('Should execute calls to multiple contracts', async () => {\n      const callReceiver2 = await CallReceiverMock.deploy()\n      const bytes = ethers.hexlify(ethers.randomBytes(21))\n\n      await callReceiver.testCall(55522, bytes)\n      await callReceiver2.testCall(66623, new Uint8Array([]))\n\n      const res = await multiCall.multiCall.staticCall(\n        applyTxDefaults([\n          {\n            revertOnError: false,\n            target: await callReceiver.getAddress(),\n            data: callReceiver.interface.encodeFunctionData('lastValA')\n          },\n          {\n            revertOnError: false,\n            target: await callReceiver.getAddress(),\n            data: callReceiver.interface.encodeFunctionData('lastValB')\n          },\n          {\n            revertOnError: false,\n            target: await callReceiver2.getAddress(),\n            data: callReceiver2.interface.encodeFunctionData('lastValA')\n          }\n        ])\n      )\n\n      expect(res[0].length).to.equal(3)\n      expect(res[1].length).to.equal(3)\n      expect(res[0][0]).to.be.true\n      expect(res[1][0]).to.be.equal(ethers.AbiCoder.defaultAbiCoder().encode(['uint256'], [55522]))\n      expect(res[0][1]).to.be.true\n      expect(ethers.AbiCoder.defaultAbiCoder().decode(['bytes'], res[1][1])[0]).to.be.equal(bytes)\n      expect(res[0][2]).to.be.true\n      expect(res[1][2]).to.be.equal(ethers.AbiCoder.defaultAbiCoder().encode(['uint256'], [66623]))\n    })\n    it('Return other calls even if single call fails', async () => {\n      const bytes = ethers.randomBytes(422)\n\n      await callReceiver.testCall(55522, bytes)\n      await callReceiver.setRevertFlag(true)\n\n      const res = await multiCall.multiCall.staticCall(\n        applyTxDefaults([\n          {\n            revertOnError: false,\n            target: await callReceiver.getAddress(),\n            data: callReceiver.interface.encodeFunctionData('lastValA')\n          },\n          {\n            revertOnError: false,\n            target: await callReceiver.getAddress(),\n            data: callReceiver.interface.encodeFunctionData('testCall', [111, bytes])\n          }\n        ])\n      )\n\n      expect(res[0].length).to.equal(2)\n      expect(res[1].length).to.equal(2)\n      expect(res[0][0]).to.be.true\n      expect(res[1][0]).to.be.equal(ethers.AbiCoder.defaultAbiCoder().encode(['uint256'], [55522]))\n      expect(res[0][1]).to.be.false\n    })\n    it('Fail if call with revert on error fails', async () => {\n      const bytes = ethers.randomBytes(422)\n\n      await callReceiver.testCall(55522, bytes)\n      await callReceiver.setRevertFlag(true)\n\n      const tx = multiCall.multiCall.staticCall(\n        applyTxDefaults([\n          {\n            revertOnError: false,\n            target: await callReceiver.getAddress(),\n            data: callReceiver.interface.encodeFunctionData('lastValA')\n          },\n          {\n            revertOnError: true,\n            target: await callReceiver.getAddress(),\n            data: callReceiver.interface.encodeFunctionData('testCall', [111, bytes])\n          }\n        ])\n      )\n\n      await expectStaticToBeRejected(tx, `CallReverted(uint256,bytes)`, 1, encodeError('CallReceiverMock#testCall: REVERT_FLAG'))\n    })\n    it('Fail if batch includes delegate call', async () => {\n      const bytes = ethers.randomBytes(422)\n\n      await callReceiver.testCall(55522, bytes)\n\n      const tx = multiCall.multiCall.staticCall(\n        applyTxDefaults([\n          {\n            delegateCall: true,\n            revertOnError: false,\n            target: await callReceiver.getAddress(),\n            data: callReceiver.interface.encodeFunctionData('lastValA')\n          },\n          {\n            revertOnError: false,\n            target: await callReceiver.getAddress(),\n            data: callReceiver.interface.encodeFunctionData('testCall', [111, bytes])\n          }\n        ])\n      )\n\n      await expectStaticToBeRejected(tx, `DelegateCallNotAllowed(uint256)`, 0)\n    })\n    it('Fail if not enough gas for call', async () => {\n      const bytes = ethers.randomBytes(422)\n\n      await callReceiver.testCall(55522, bytes)\n\n      const tx = multiCall.multiCall.staticCall(\n        applyTxDefaults([\n          {\n            revertOnError: false,\n            target: await callReceiver.getAddress(),\n            data: callReceiver.interface.encodeFunctionData('lastValA')\n          },\n          {\n            revertOnError: false,\n            target: await callReceiver.getAddress(),\n            data: callReceiver.interface.encodeFunctionData('testCall', [111, bytes]),\n            gasLimit: 2n ** 256n - 1n\n          }\n        ])\n      )\n\n      await expectStaticToBeRejected(tx, `NotEnoughGas(uint256,uint256,uint256)`, 1, 2n ** 256n - 1n, '*')\n    })\n    it('Should call globals', async () => {\n      const i = multiCall.interface\n      const emptyAccount = ethers.Wallet.createRandom().address\n\n      await multiCall.multiCall.staticCall([])\n\n      const lastBlock = await hethers.provider.getBlock('latest')\n\n      const txs = await Promise.all(\n        [\n          i.encodeFunctionData('callBlockhash', [lastBlock!.number - 1]),\n          i.encodeFunctionData('callCoinbase'),\n          i.encodeFunctionData('callDifficulty'),\n          i.encodeFunctionData('callGasLimit'),\n          i.encodeFunctionData('callBlockNumber'),\n          i.encodeFunctionData('callTimestamp'),\n          i.encodeFunctionData('callGasLeft'),\n          i.encodeFunctionData('callGasPrice'),\n          i.encodeFunctionData('callOrigin'),\n          i.encodeFunctionData('callBalanceOf', [accounts[0]]),\n          i.encodeFunctionData('callBalanceOf', [emptyAccount]),\n          i.encodeFunctionData('callCodeSize', [accounts[0]]),\n          i.encodeFunctionData('callCodeSize', [await callReceiver.getAddress()]),\n          i.encodeFunctionData('callCode', [accounts[0]]),\n          i.encodeFunctionData('callCode', [await callReceiver.getAddress()]),\n          i.encodeFunctionData('callCodeHash', [accounts[0]]),\n          i.encodeFunctionData('callCodeHash', [await callReceiver.getAddress()]),\n          i.encodeFunctionData('callChainId')\n        ].map(async data =>\n          applyTxDefault({\n            revertOnError: false,\n            target: await multiCall.getAddress(),\n            data\n          })\n        )\n      )\n\n      const res = await multiCall.multiCall.staticCall(txs, { gasPrice: 1 })\n\n      const emptyBytes32 = ethers.AbiCoder.defaultAbiCoder().encode(['uint256'], ['0'])\n\n      expect(res[0].length).to.equal(txs.length)\n      expect(res[1].length).to.equal(txs.length)\n\n      // All calls must success\n      expect(res[0].reduce((a: boolean, c: boolean) => a && c)).to.be.true\n\n      expect(res[1][0]).to.not.equal(emptyBytes32, 'return block hash')\n\n      if (!process.env.COVERAGE) {\n        expect(res[1][1]).to.not.equal(emptyBytes32, 'return coinbase')\n        expect(res[1][2]).to.not.equal(emptyBytes32, 'return difficulty')\n      }\n\n      expect(res[1][3]).to.not.equal(emptyBytes32)\n      expect(res[1][4]).to.not.equal(emptyBytes32, 'return block number')\n      expect(res[1][5]).to.not.equal(emptyBytes32, 'return timestamp')\n      expect(res[1][6]).to.not.equal(emptyBytes32, 'return gas left')\n      expect(BigInt(res[1][7])).to.equal(1n, 'return gas price')\n      expect(res[1][8]).to.not.equal(emptyBytes32, 'return origin')\n      expect(res[1][9]).to.not.equal(emptyBytes32, 'return balance of 0x')\n      expect(res[1][10]).to.equal(emptyBytes32, 'return balance of empty account')\n      expect(res[1][11]).to.equal(emptyBytes32, 'return code size of empty account')\n      expect(res[1][12]).to.not.equal(emptyBytes32, 'return code size of contract')\n\n      expect(ethers.AbiCoder.defaultAbiCoder().decode(['bytes'], res[1][13])[0]).to.equal('0x')\n\n      const codeSize = ethers.AbiCoder.defaultAbiCoder().decode(['uint256'], res[1][12])[0]\n      expect(ethers.AbiCoder.defaultAbiCoder().decode(['bytes'], res[1][14])[0].length).to.equal(\n        2 + Number(codeSize) * 2,\n        'return code of correct size'\n      )\n\n      expect(res[1][15]).to.not.equal(emptyBytes32)\n      expect(res[1][16]).to.not.equal(emptyBytes32)\n\n      expect(ethers.AbiCoder.defaultAbiCoder().decode(['uint256'], res[1][17])[0]).to.equal(await getChainId(), 'return chain id')\n    })\n  })\n})\n"
  },
  {
    "path": "test/utils/contracts.ts",
    "content": "import { ethers } from 'ethers'\nimport { ethers as hethers } from 'hardhat'\nimport * as t from '../../gen/typechain'\n\nconst cachedFactories: { [name: string]: ethers.ContractFactory } = {}\n\nasync function deploy<Y extends ethers.BaseContract>(name: string, ...args: any[]) {\n  const factory = await hethers.getContractFactory(name)\n  cachedFactories[name] = factory\n  return (await factory.deploy(...args)) as unknown as Y\n}\n\nfunction attach<Y extends ethers.BaseContract>(name: string, address: string) {\n  return cachedFactories[name].attach(address) as Y\n}\n\ntype Adapter<T extends ethers.BaseContract> = {\n  cache: () => Promise<any>\n  deploy: (...args: any[]) => Promise<T>\n  attach: (address: string) => T\n  factory: () => ethers.ContractFactory\n}\n\nfunction adapt<T extends ethers.BaseContract>(name: string): Adapter<T> {\n  return {\n    cache: async () => (cachedFactories[name] = await hethers.getContractFactory(name)),\n    deploy: (...args: any[]) => deploy<T>(name, ...args),\n    attach: (address: string) => attach<T>(name, address),\n    factory: () => cachedFactories[name]\n  }\n}\n\nexport type ContractType<T extends Adapter<any>> = T extends Adapter<infer U> ? U : never\n\nexport const LibBytesImpl = adapt<t.LibBytesImpl>('LibBytesImpl')\nexport const LibBytesPointerImpl = adapt<t.LibBytesPointerImpl>('LibBytesPointerImpl')\nexport const Factory = adapt<t.Factory>('Factory')\nexport const MainModule = adapt<t.MainModule>('MainModule')\nexport const MainModuleUpgradable = adapt<t.MainModuleUpgradable>('MainModuleUpgradable')\nexport const ERC165CheckerMock = adapt<t.ERC165CheckerMock>('ERC165CheckerMock')\nexport const ModuleMock = adapt<t.ModuleMock>('ModuleMock')\nexport const CallReceiverMock = adapt<t.CallReceiverMock>('CallReceiverMock')\nexport const MultiCallUtils = adapt<t.MultiCallUtils>('MultiCallUtils')\nexport const GuestModule = adapt<t.GuestModule>('GuestModule')\nexport const HookMock = adapt<t.HookMock>('HookMock')\nexport const HookCallerMock = adapt<t.HookCallerMock>('HookCallerMock')\nexport const RequireUtils = adapt<t.RequireUtils>('RequireUtils')\nexport const DelegateCallMock = adapt<t.DelegateCallMock>('DelegateCallMock')\nexport const GasBurnerMock = adapt<t.GasBurnerMock>('GasBurnerMock')\nexport const GasEstimator = adapt<t.GasEstimator>('GasEstimator')\nexport const MainModuleGasEstimation = adapt<t.MainModuleGasEstimation>('MainModuleGasEstimation')\nexport const LibStringImp = adapt<t.LibStringImp>('LibStringImp')\nexport const AlwaysRevertMock = adapt<t.AlwaysRevertMock>('AlwaysRevertMock')\n;[\n  LibBytesImpl,\n  Factory,\n  MainModule,\n  MainModuleUpgradable,\n  ERC165CheckerMock,\n  ModuleMock,\n  CallReceiverMock,\n  MultiCallUtils,\n  GuestModule,\n  HookMock,\n  HookCallerMock,\n  RequireUtils,\n  DelegateCallMock,\n  GasEstimator\n].map(c => c.cache())\n\nexport const deploySequenceContext = async (owner?: string) => {\n  const factory = await Factory.deploy()\n  const mainModuleUpgradable = await MainModuleUpgradable.deploy()\n  const mainModule = await MainModule.deploy(await factory.getAddress(), await mainModuleUpgradable.getAddress())\n\n  return {\n    factory: await factory.waitForDeployment(),\n    mainModule: await mainModule.waitForDeployment(),\n    mainModuleUpgradable: await mainModuleUpgradable.waitForDeployment()\n  }\n}\n\nexport type SequenceContext = {\n  factory: ContractType<typeof Factory>\n  mainModule: ContractType<typeof MainModule>\n  mainModuleUpgradable: ContractType<typeof MainModuleUpgradable>\n}\n"
  },
  {
    "path": "test/utils/imposter.ts",
    "content": "import { ethers } from 'ethers'\nimport { AnyStaticSigner, StaticSigner } from './wallet'\n\nexport class Imposter extends ethers.AbstractSigner implements StaticSigner {\n  static random(identity: string | AnyStaticSigner) {\n    return new Imposter(identity, ethers.Wallet.createRandom())\n  }\n\n  constructor(\n    public identity: string | AnyStaticSigner,\n    public signer: ethers.Signer\n  ) {\n    super()\n  }\n\n  get address() {\n    return typeof this.identity === 'string' ? this.identity : this.identity.address\n  }\n\n  async getAddress(): Promise<string> {\n    return this.address\n  }\n\n  signMessage(message: string | Uint8Array): Promise<string> {\n    return this.signer.signMessage(message)\n  }\n\n  signTypedData(\n    domain: ethers.TypedDataDomain,\n    types: Record<string, ethers.TypedDataField[]>,\n    value: Record<string, any>\n  ): Promise<string> {\n    return this.signer.signTypedData(domain, types, value)\n  }\n\n  signTransaction(transaction: ethers.TransactionRequest): Promise<string> {\n    return this.signer.signTransaction(transaction)\n  }\n\n  connect(provider: ethers.Provider): ethers.Signer {\n    return this.signer.connect(provider)\n  }\n}\n"
  },
  {
    "path": "test/utils/index.ts",
    "content": "import * as chai from 'chai'\nimport chaiAsPromised from 'chai-as-promised'\nimport chaiString from 'chai-string'\nimport { ethers } from 'ethers'\nimport { solidity } from 'ethereum-waffle'\nimport { ethers as hethers } from 'hardhat'\n\nexport const getChainId = async (): Promise<bigint> =>\n  process.env.NET_ID ? BigInt(process.env.NET_ID) : (await hethers.provider.getNetwork()).chainId\n\nexport const { assert, expect } = chai.use(chaiString).use(chaiAsPromised).use(solidity)\n\nexport function bytes32toAddress(bytes32: ethers.BytesLike): string {\n  const paddedValue = ethers.zeroPadValue(bytes32, 32)\n  return ethers.getAddress(ethers.AbiCoder.defaultAbiCoder().decode(['address'], paddedValue)[0])\n}\n\nexport function shuffle<T>(a: T[]): T[] {\n  for (let i = a.length - 1; i > 0; i--) {\n    const j = Math.floor(Math.random() * (i + 1))\n    ;[a[i], a[j]] = [a[j], a[i]]\n  }\n\n  return a\n}\n\nexport function randomHex(length: number): string {\n  return ethers.hexlify(ethers.randomBytes(length))\n}\n\nexport async function expectToBeRejected(promise: Promise<any>, error: string) {\n  if (!process.env.COVERAGE) {\n    await expect(promise).to.be.rejectedWith(error)\n  } else {\n    await expect(promise).to.be.rejected\n  }\n}\n\nexport async function expectStaticToBeRejected(promise: Promise<any>, signature: string, ...args: any[]) {\n  // await expectToBeRejected(promise, `errorName=\"${signature.split('(')[0]}\"`)\n  // await expectToBeRejected(promise, `errorSignature=\"${signature}\"`)\n\n  await expectToBeRejected(promise, `${signature.split('(')[0]}`)\n\n  const sigTypes = signature.split('(')[1].split(')')[0].split(',')\n\n  expect(sigTypes.length).to.equal(args.length)\n\n  const formattedArgs = args\n    .map((arg, i) => {\n      const type = sigTypes[i]\n\n      if (arg === '*') return '*'\n\n      switch (type) {\n        case 'bytes':\n          if (typeof arg === 'string' && arg.length === 0) {\n            return ethers.hexlify(new Uint8Array([]))\n          }\n          return `\"${ethers.hexlify(arg).toLowerCase()}\"`\n        case 'string':\n          return `\"${arg.toString()}\"`\n      }\n\n      if (type.startsWith('uint') || type.startsWith('int')) {\n        //return `{\"type\":\"BigNumber\",\"hex\":\"${ethers.toBeHex(BigInt(arg))}\"}`\n        return BigInt(arg).toString()\n      }\n\n      throw new Error(`Unknown type: ${type}`)\n    })\n    .join(', ')\n\n  const groups = formattedArgs.split('*')\n\n  for (let i = 0; i < groups.length; i++) {\n    await expectToBeRejected(promise, `${groups[i]}`)\n  }\n\n  // if (groups.length === 1) {\n  //   // await expectToBeRejected(promise, `errorArgs=[${formattedArgs}]`)\n  //   await expectToBeRejected(promise, `${formattedArgs}`)\n  // } else {\n  //   for (let i = 0; i < groups.length; i++) {\n  //     const group = groups[i]\n  //     if (i === 0) {\n  //       // await expectToBeRejected(promise, `errorArgs=[${group}`)\n  //       await expectToBeRejected(promise, `${group}`)\n  //     } else if (i === groups.length - 1) {\n  //       await expectToBeRejected(promise, `${group}]`)\n  //     } else {\n  //       await expectToBeRejected(promise, `${group}`)\n  //     }\n  //   }\n  // }\n}\n\nexport function encodeError(error: string): string {\n  return '0x08c379a0' + ethers.AbiCoder.defaultAbiCoder().encode(['string'], [error]).slice(2)\n}\n\nfunction xor(a: any, b: any) {\n  if (!Buffer.isBuffer(a)) a = Buffer.from(ethers.getBytes(a))\n  if (!Buffer.isBuffer(b)) b = Buffer.from(ethers.getBytes(b))\n  return ethers.hexlify(a.map((v: number, i: number) => v ^ b[i]))\n}\n\nexport function interfaceIdOf(int: ethers.Interface): string {\n  const signatures: string[] = []\n  int.forEachFunction(fragment => {\n    signatures.push(getSigHash(fragment))\n  })\n  return signatures.reduce((p, c) => xor(p, c))\n}\n\nexport function getSigHash(fragment: ethers.FunctionFragment): string {\n  return ethers.dataSlice(ethers.id(fragment.format('sighash')), 0, 4)\n}\n"
  },
  {
    "path": "test/utils/sequence.ts",
    "content": "import { BigNumberish, BytesLike, ethers, Wallet } from 'ethers'\nimport { getChainId } from '.'\n\nexport const WALLET_CODE = '0x603a600e3d39601a805130553df3363d3d373d3d3d363d30545af43d82803e903d91601857fd5bf3'\n\nexport enum SignatureType {\n  Legacy = 0,\n  Dynamic = 1,\n  NoChaindDynamic = 2\n}\n\nexport type SignerLeaf = {\n  address: string\n  weight: BigNumberish\n}\n\nexport type SubdigestLeaf = {\n  subdigest: string\n}\n\nexport type NestedLeaf = {\n  tree: ConfigTopology\n  internalThreshold: BigNumberish\n  externalWeight: BigNumberish\n}\n\nexport type ConfigLeaf = SubdigestLeaf | SignerLeaf | NestedLeaf\n\nexport type ImageHashNode = {\n  left: ConfigTopology\n  right: ConfigTopology\n}\n\nexport type ConfigTopology = ImageHashNode | ConfigLeaf\n\nexport type WalletConfig = {\n  threshold: ethers.BigNumberish\n  checkpoint: ethers.BigNumberish\n  topology: ConfigTopology\n}\n\nexport type SimplifiedNestedWalletConfig = {\n  threshold: ethers.BigNumberish\n  weight: ethers.BigNumberish\n  signers: SimplifiedConfigMember[]\n}\n\nexport type SimplifiedWalletConfig = {\n  threshold: BigNumberish\n  checkpoint: BigNumberish\n  signers: SimplifiedConfigMember[]\n}\n\nexport type SimplifiedConfigMember = SignerLeaf | SimplifiedNestedWalletConfig\n\nexport type Transaction = {\n  delegateCall: boolean\n  revertOnError: boolean\n  gasLimit: BigNumberish\n  target: string\n  value: BigNumberish\n  data: BytesLike\n}\n\nexport enum SignaturePartType {\n  Signature = 0,\n  Address = 1,\n  Dynamic = 2,\n  Node = 3,\n  Branch = 4,\n  Subdigest = 5,\n  Nested = 6\n}\n\nexport type SignaturePart = {\n  address: string\n  type: SignaturePartType\n  signature?: string\n}\n\nexport function applyTxDefault(\n  tx: Partial<Transaction>,\n  def: Transaction = {\n    delegateCall: false,\n    revertOnError: true,\n    gasLimit: 0,\n    target: '0xFb8356E7deB64034aBE2b2a8A732634f77A2DAE4', // Random address\n    value: 0,\n    data: new Uint8Array([])\n  }\n): Transaction {\n  return {\n    ...def,\n    ...tx\n  }\n}\n\nexport function applyTxDefaults(tx: Partial<Transaction>[], def?: Transaction): Transaction[] {\n  return tx.map(t => applyTxDefault(t, def))\n}\n\nexport const MetaTransactionsSolidityType = `tuple(\n  bool delegateCall,\n  bool revertOnError,\n  uint256 gasLimit,\n  address target,\n  uint256 value,\n  bytes data\n)[]`\n\nexport function isConfigLeaf(node: ConfigTopology): node is ConfigLeaf {\n  return !('left' in node || 'right' in node)\n}\n\nexport function isSignerLeaf(node: any): node is SignerLeaf {\n  return isConfigLeaf(node) && 'weight' in node && 'address' in node\n}\n\nexport function isSubdigestLeaf(node: ConfigTopology): node is SubdigestLeaf {\n  return isConfigLeaf(node) && 'subdigest' in node\n}\n\nexport function isNestedLeaf(node: ConfigTopology): node is NestedLeaf {\n  return isConfigLeaf(node) && 'tree' in node\n}\n\nexport function legacyTopology(leavesOrConfig: SimplifiedWalletConfig | ConfigTopology[]): ConfigTopology {\n  if (!Array.isArray(leavesOrConfig)) {\n    return legacyTopology(toTopology(leavesOrConfig))\n  }\n\n  return leavesOrConfig.reduce((acc, leaf) => {\n    return {\n      left: acc,\n      right: leaf\n    }\n  })\n}\n\nexport function toTopology(config: SimplifiedWalletConfig | SimplifiedNestedWalletConfig): ConfigTopology[] {\n  return config.signers.map(s => {\n    if (isSignerLeaf(s)) {\n      return {\n        address: s.address,\n        weight: s.weight\n      }\n    }\n\n    return {\n      tree: merkleTopology(toTopology(s)),\n      internalThreshold: s.threshold,\n      externalWeight: s.weight\n    }\n  })\n}\n\nexport function merkleTopology(leavesOrConfig: SimplifiedWalletConfig | ConfigTopology[]): ConfigTopology {\n  if (!Array.isArray(leavesOrConfig)) {\n    return merkleTopology(toTopology(leavesOrConfig))\n  }\n\n  const leaves = leavesOrConfig\n  for (let s = leaves.length; s > 1; s = s / 2) {\n    for (let i = 0; i < s / 2; i++) {\n      const j1 = i * 2\n      const j2 = j1 + 1\n\n      if (j2 >= s) {\n        leaves[i] = leaves[j1]\n      } else {\n        leaves[i] = {\n          left: leaves[j1],\n          right: leaves[j2]\n        }\n      }\n    }\n  }\n\n  return leaves[0]\n}\n\nexport function optimize2SignersTopology(config: SimplifiedWalletConfig): ConfigTopology {\n  if (config.signers.length > 8) {\n    return merkleTopology(config)\n  }\n\n  return legacyTopology(config)\n}\n\nexport function leavesOf(topology: ConfigTopology): ConfigLeaf[] {\n  if (isConfigLeaf(topology)) {\n    return [topology]\n  }\n\n  return [...leavesOf(topology.left), ...leavesOf(topology.right)]\n}\n\nexport function subdigestLeaves(topology: ConfigTopology): string[] {\n  return leavesOf(topology)\n    .filter(l => isSubdigestLeaf(l))\n    .map((l: SubdigestLeaf) => l.subdigest)\n}\n\nexport function toSimplifiedConfig(config: WalletConfig): SimplifiedWalletConfig {\n  let leaves = leavesOf(config.topology).filter(isSignerLeaf)\n\n  return {\n    threshold: config.threshold,\n    checkpoint: config.checkpoint,\n    signers: leaves.map(l => ({\n      weight: l.weight,\n      address: l.address\n    }))\n  }\n}\n\nexport function hashNode(node: ConfigTopology): string {\n  if (isSignerLeaf(node)) {\n    return leafForAddressAndWeight(node.address, node.weight)\n  }\n\n  if (isSubdigestLeaf(node)) {\n    return ethers.solidityPackedKeccak256(['string', 'bytes32'], ['Sequence static digest:\\n', node.subdigest])\n  }\n\n  if (isNestedLeaf(node)) {\n    return ethers.solidityPackedKeccak256(\n      ['string', 'bytes32', 'uint256', 'uint256'],\n      ['Sequence nested config:\\n', hashNode(node.tree), node.internalThreshold, node.externalWeight]\n    )\n  }\n\n  return ethers.solidityPackedKeccak256(['bytes32', 'bytes32'], [hashNode(node.left), hashNode(node.right)])\n}\n\nexport function imageHash2(threshold: ethers.BigNumberish, topology: ConfigTopology): string {\n  const root = hashNode(topology)\n  return ethers.keccak256(ethers.solidityPacked(['bytes32', 'uint256'], [root, threshold]))\n}\n\nexport function printTopology(topology: ConfigTopology, threshold?: ethers.BigNumberish, inverse = false): string[] {\n  if (threshold) {\n    const imageHash = imageHash2(threshold, topology)\n\n    const result: string[] = [`imageHash: ${imageHash}`]\n    const signers = printTopology(topology, undefined, inverse)\n    result.push(`  ├─ threshold: ${threshold}`)\n    for (let i = 0; i < signers.length; i++) {\n      const prefix = i === 0 ? '  └─ ' : '   '\n      result.push(`${prefix}${signers[i]}`)\n    }\n\n    return result\n  }\n\n  if (isSignerLeaf(topology)) {\n    return [`weight: ${topology.weight} - address: ${topology.address}`]\n  }\n\n  if (isSubdigestLeaf(topology)) {\n    return [`subdigest: ${topology.subdigest}`]\n  }\n\n  if (isNestedLeaf(topology)) {\n    const result: string[] = [`internalThreshold: ${topology.internalThreshold} - externalWeight: ${topology.externalWeight}`]\n    const signers = printTopology(topology.tree, undefined, inverse)\n    for (let i = 0; i < signers.length; i++) {\n      const prefix = i === 0 ? '└─ ' : '  '\n      result.push(`${prefix}${signers[i]}`)\n    }\n\n    return result\n  }\n\n  const root = hashNode(topology)\n  let printLeft = printTopology(topology.left, undefined, inverse)\n  let printRight = printTopology(topology.right, undefined, inverse)\n\n  if (inverse) {\n    ;[printLeft, printRight] = [printRight, printLeft]\n  }\n\n  const result = [`${root}`]\n  for (let i = 0; i < printLeft.length; i++) {\n    const prefix = i === 0 ? '  ├─ ' : '  │'\n    result.push(`${prefix}${printLeft[i]}`)\n  }\n\n  for (let i = 0; i < printRight.length; i++) {\n    const prefix = i === 0 ? '  └─ ' : '   '\n    result.push(`${prefix}${printRight[i]}`)\n  }\n\n  return result\n}\n\nexport function addressOf(factory: string, firstModule: string, imageHash: string): string {\n  const codeHash = ethers.keccak256(\n    ethers.solidityPacked(['bytes', 'bytes32'], [WALLET_CODE, ethers.zeroPadValue(firstModule, 32)])\n  )\n\n  const hash = ethers.keccak256(\n    ethers.solidityPacked(['bytes1', 'address', 'bytes32', 'bytes32'], ['0xff', factory, imageHash, codeHash])\n  )\n\n  return ethers.getAddress(ethers.dataSlice(hash, 12))\n}\n\nexport function encodeNonce(space: BigNumberish, nonce: BigNumberish) {\n  return BigInt(ethers.solidityPacked(['uint160', 'uint96'], [space, nonce]))\n}\n\nexport function leafForAddressAndWeight(address: string, weight: ethers.BigNumberish) {\n  return ethers.solidityPacked(['uint96', 'address'], [weight, address])\n}\n\nexport function imageHash(config: WalletConfig): string {\n  const signersRoot = hashNode(config.topology)\n\n  const preImageHash = ethers.keccak256(\n    ethers.AbiCoder.defaultAbiCoder().encode(['bytes32', 'uint256'], [signersRoot, config.threshold])\n  )\n\n  return ethers.keccak256(ethers.AbiCoder.defaultAbiCoder().encode(['bytes32', 'uint256'], [preImageHash, config.checkpoint]))\n}\n\nexport function digestOf(txs: Partial<Transaction>[], nonce: ethers.BigNumberish) {\n  return ethers.keccak256(\n    ethers.AbiCoder.defaultAbiCoder().encode(['uint256', MetaTransactionsSolidityType], [nonce, applyTxDefaults(txs)])\n  )\n}\n\nexport async function subdigestOf(wallet: string, digest: ethers.BytesLike, chainId?: ethers.BigNumberish) {\n  chainId = chainId ?? (await getChainId())\n  return ethers.keccak256(\n    ethers.solidityPacked(['string', 'uint256', 'address', 'bytes32'], ['\\x19\\x01', chainId, wallet, digest])\n  )\n}\n\nexport function computeStorageKey(key: string, subkey?: string): string {\n  if (!subkey) {\n    return ethers.id(key)\n  }\n\n  return ethers.keccak256(ethers.AbiCoder.defaultAbiCoder().encode(['bytes32', 'bytes32'], [computeStorageKey(key), subkey]))\n}\n\nexport type EncodingOptions = {\n  forceDynamicEncoding?: boolean\n  signatureType?: SignatureType\n  disableTrim?: boolean\n}\n\nfunction leftSlice(topology: ConfigTopology): ConfigTopology[] {\n  // Returns left side of the tree\n  let stack: ConfigTopology[] = []\n\n  let prev = topology\n  while (!isConfigLeaf(prev)) {\n    stack.unshift(prev.right)\n    prev = prev.left\n  }\n\n  stack.unshift(prev)\n\n  return stack\n}\n\ntype DecodedSignatureMember = {\n  weight?: ethers.BigNumberish\n  address?: string\n  type: SignaturePartType\n  value?: string\n  innerThreshold?: ethers.BigNumberish\n}\n\nexport class SignatureConstructor {\n  private members: DecodedSignatureMember[] = []\n\n  constructor(public disableTrim = false) {}\n\n  tryTrim(): void {\n    if (this.disableTrim) return\n\n    // Can only trim when we have two members\n    if (this.members.length !== 2) return\n\n    // There are 4 valid trim options:\n    // 1. Trim the first addr, second is node\n    // 2. Trim the first node, second is addr\n    // 3. Trim the first addr, second is addr\n    // 4. Trim the first node, second is node\n\n    const first = this.members[0]\n    const second = this.members[1]\n\n    if (first.type !== SignaturePartType.Address && first.type !== SignaturePartType.Node) return\n    if (second.type !== SignaturePartType.Address && second.type !== SignaturePartType.Node) return\n\n    const firstNode =\n      first.type === SignaturePartType.Address ? leafForAddressAndWeight(first.address!, first.weight!) : first.value\n    const secondNode =\n      second.type === SignaturePartType.Address ? leafForAddressAndWeight(second.address!, second.weight!) : second.value\n\n    const nextNode = ethers.keccak256(ethers.solidityPacked(['bytes32', 'bytes32'], [firstNode, secondNode]))\n\n    this.members = [\n      {\n        type: SignaturePartType.Node,\n        value: nextNode\n      }\n    ]\n  }\n\n  appendPart(weight: ethers.BigNumberish, part: SignaturePart) {\n    switch (part.type) {\n      case SignaturePartType.Address:\n        this.members.push({ weight, address: part.address, type: SignaturePartType.Address })\n        break\n\n      case SignaturePartType.Signature:\n        this.members.push({ weight, address: part.address, type: SignaturePartType.Signature, value: part.signature })\n        break\n\n      case SignaturePartType.Dynamic:\n        this.members.push({ weight, address: part.address, type: SignaturePartType.Dynamic, value: part.signature })\n        break\n\n      default:\n        throw new Error(`Unknown signature part type: ${part.type}`)\n    }\n\n    this.tryTrim()\n  }\n\n  appendNode(node: string) {\n    this.members.push({ type: SignaturePartType.Node, value: node })\n    this.tryTrim()\n  }\n\n  appendBranch(branch: string) {\n    this.members.push({ type: SignaturePartType.Branch, value: branch })\n  }\n\n  appendSubdigest(subdigest: string) {\n    this.members.push({ type: SignaturePartType.Subdigest, value: subdigest })\n  }\n\n  appendNested(branch: string, weight: ethers.BigNumberish, innerThreshold: ethers.BigNumberish) {\n    this.members.push({ type: SignaturePartType.Nested, value: branch, innerThreshold, weight })\n  }\n\n  encode(): string {\n    let result = '0x'\n\n    for (const member of this.members) {\n      switch (member.type) {\n        case SignaturePartType.Address:\n          result = ethers.solidityPacked(\n            ['bytes', 'uint8', 'uint8', 'address'],\n            [result, SignaturePartType.Address, member.weight, member.address]\n          )\n          break\n\n        case SignaturePartType.Signature:\n          result = ethers.solidityPacked(\n            ['bytes', 'uint8', 'uint8', 'bytes'],\n            [result, SignaturePartType.Signature, member.weight, member.value]\n          )\n          break\n\n        case SignaturePartType.Dynamic:\n          const signature = ethers.getBytes(member.value ?? new Uint8Array([]))\n          result = ethers.solidityPacked(\n            ['bytes', 'uint8', 'uint8', 'address', 'uint24', 'bytes'],\n            [result, SignaturePartType.Dynamic, member.weight, member.address, signature.length, signature]\n          )\n          break\n\n        case SignaturePartType.Node:\n          result = ethers.solidityPacked(['bytes', 'uint8', 'bytes32'], [result, SignaturePartType.Node, member.value])\n          break\n\n        case SignaturePartType.Branch:\n          const branch = ethers.getBytes(member.value ?? new Uint8Array([]))\n          result = ethers.solidityPacked(\n            ['bytes', 'uint8', 'uint24', 'bytes'],\n            [result, SignaturePartType.Branch, branch.length, branch]\n          )\n          break\n\n        case SignaturePartType.Subdigest:\n          result = ethers.solidityPacked(['bytes', 'uint8', 'bytes32'], [result, SignaturePartType.Subdigest, member.value])\n          break\n\n        case SignaturePartType.Nested:\n          const nestedBranch = ethers.getBytes(member.value ?? new Uint8Array([]))\n          result = ethers.solidityPacked(\n            ['bytes', 'uint8', 'uint8', 'uint16', 'uint24', 'bytes'],\n            [result, SignaturePartType.Nested, member.weight, member.innerThreshold, nestedBranch.length, nestedBranch]\n          )\n          break\n\n        default:\n          throw new Error(`Unknown signature part type: ${member.type}`)\n      }\n    }\n\n    return result\n  }\n}\n\nexport function encodeSigners(\n  topology: ConfigTopology,\n  parts: SignaturePart[] | Map<string, SignaturePart>,\n  subdigests: string[],\n  options?: EncodingOptions\n): { encoded: string; weight: bigint } {\n  // Map part to signers\n  if (Array.isArray(parts)) {\n    const partOfSigner = new Map<string, SignaturePart>()\n    for (const part of parts) {\n      partOfSigner.set(part.address, part)\n    }\n    return encodeSigners(topology, partOfSigner, subdigests, options)\n  }\n\n  const slice = leftSlice(topology)\n  let weight = 0n\n\n  const constructor = new SignatureConstructor(options?.disableTrim)\n  for (const node of slice) {\n    if (!isConfigLeaf(node)) {\n      // If the node opens up to another branch\n      // we recurse the encoding, and if the result has any weight\n      // we have to embed the whole branch, otherwise we just add the node\n      const nested = encodeSigners(node, parts, subdigests, options)\n      if (nested.weight === 0n && !options?.disableTrim) {\n        constructor.appendNode(hashNode(node))\n      } else {\n        constructor.appendBranch(nested.encoded)\n        weight = weight + nested.weight\n      }\n    } else {\n      if (isSignerLeaf(node)) {\n        // If the node is a signer leaf, we can just add the member\n        const part = parts.get(node.address) ?? { type: SignaturePartType.Address, address: node.address }\n        if (part.type !== SignaturePartType.Address) {\n          weight = weight + BigInt(node.weight)\n        }\n\n        constructor.appendPart(node.weight, part)\n      } else if (isNestedLeaf(node)) {\n        // If the node opens up to another branch\n        // we recurse the encoding, and if the result has any weight\n        // we have to embed the whole branch, otherwise we just add the node\n        const nested = encodeSigners(node.tree, parts, subdigests, options)\n        if (nested.weight === 0n && !options?.disableTrim) {\n          constructor.appendNode(hashNode(node))\n        } else {\n          // Nested configs only have weight if the inner threshold is met\n          // and the weight is always the external weight\n          if (nested.weight >= BigInt(node.internalThreshold)) {\n            weight = weight + BigInt(node.externalWeight)\n          }\n\n          constructor.appendNested(nested.encoded, node.externalWeight, node.internalThreshold)\n        }\n      } else {\n        // If the node is a subdigest add the node (unless it's an static subdigest signature)\n        if (subdigests.includes(node.subdigest)) {\n          weight += 2n ** 256n - 1n\n          constructor.appendSubdigest(node.subdigest)\n        } else {\n          constructor.appendNode(hashNode(node))\n        }\n      }\n    }\n  }\n\n  return {\n    encoded: constructor.encode(),\n    weight\n  }\n}\n\nexport function encodeSignature(\n  config: WalletConfig,\n  parts: SignaturePart[] | Map<string, SignaturePart>,\n  subdigests: string[],\n  options?: EncodingOptions\n) {\n  const encodedSigners = encodeSigners(config.topology, parts, subdigests, options)\n\n  switch (options?.signatureType || SignatureType.Legacy) {\n    case SignatureType.Dynamic:\n      return ethers.solidityPacked(\n        ['uint8', 'uint16', 'uint32', 'bytes'],\n        [SignatureType.Dynamic, config.threshold, config.checkpoint, encodedSigners.encoded]\n      )\n    case SignatureType.NoChaindDynamic:\n      return ethers.solidityPacked(\n        ['uint8', 'uint16', 'uint32', 'bytes'],\n        [SignatureType.NoChaindDynamic, config.threshold, config.checkpoint, encodedSigners.encoded]\n      )\n    default:\n    case SignatureType.Legacy:\n      return ethers.solidityPacked(\n        ['uint8', 'uint8', 'uint32', 'bytes'],\n        [SignatureType.Legacy, config.threshold, config.checkpoint, encodedSigners.encoded]\n      )\n  }\n}\n"
  },
  {
    "path": "test/utils/wallet.ts",
    "content": "import { ethers, Overrides } from 'ethers'\nimport { ethers as hethers } from 'hardhat'\nimport { shuffle } from '.'\nimport { MainModule, MainModuleUpgradable, SequenceContext } from './contracts'\nimport {\n  addressOf,\n  applyTxDefaults,\n  ConfigTopology,\n  digestOf,\n  encodeSignature,\n  EncodingOptions,\n  imageHash,\n  merkleTopology,\n  optimize2SignersTopology,\n  SignaturePartType,\n  SignatureType,\n  SimplifiedWalletConfig,\n  subdigestOf,\n  Transaction,\n  WalletConfig\n} from './sequence'\n\nexport type StaticSigner = ethers.Signer & { address: string }\nexport type AnyStaticSigner = StaticSigner | SequenceWallet\n\nexport function isAnyStaticSigner(s: any): s is AnyStaticSigner {\n  return s.address !== undefined\n}\n\nlet LAST_CHECKPOINT = 0\n\nexport function getCheckpoint() {\n  let cand = Math.floor(Date.now() / 1000)\n\n  if (cand === LAST_CHECKPOINT) {\n    cand++\n  }\n\n  LAST_CHECKPOINT = cand\n  return cand\n}\n\nexport type WalletOptions = {\n  context: SequenceContext\n  config: WalletConfig\n  address?: string\n  signers: (ethers.Signer | SequenceWallet)[]\n  encodingOptions?: EncodingOptions\n  chainId?: ethers.BigNumberish\n}\n\nexport type BasicWalletOptions = {\n  address?: string\n  threshold?: number\n  signing: number | number[]\n  idle: number | number[]\n  encodingOptions?: EncodingOptions\n  topologyConverter: (simple: SimplifiedWalletConfig) => ConfigTopology\n}\n\nexport type DetailedWalletOptions = {\n  address?: string\n  threshold: ethers.BigNumberish\n  signers: (string | AnyStaticSigner | Weighted<string> | Weighted<AnyStaticSigner>)[]\n  encodingOptions?: EncodingOptions\n}\n\nexport type Weighted<T> = { weight: number; value: T }\n\nexport function isWeighted<T>(w: any): w is Weighted<T> {\n  return w.weight !== undefined && w.value !== undefined\n}\n\nexport function weightedVal<T>(w: Weighted<T> | T): T {\n  return isWeighted(w) ? w.value : w\n}\n\nexport function isSequenceSigner(signer: ethers.Signer | SequenceWallet): signer is SequenceWallet {\n  return 'isSequence' in signer && signer.isSequence\n}\n\nconst defaultTopology = optimize2SignersTopology\n\nexport class SequenceWallet {\n  public isSequence = true\n  _isSigner: boolean = true\n\n  constructor(public options: WalletOptions) {}\n\n  static basicWallet(context: SequenceContext, opts?: Partial<BasicWalletOptions>): SequenceWallet {\n    const options = { ...{ signing: 1, idle: 0, topologyConverter: defaultTopology }, ...opts }\n\n    const signersWeight = Array.isArray(options.signing) ? options.signing : new Array(options.signing).fill(0).map(() => 1)\n    const idleWeight = Array.isArray(options.idle) ? options.idle : new Array(options.idle).fill(0).map(() => 1)\n\n    const signers = signersWeight.map(s => (isAnyStaticSigner(s) ? s : ethers.Wallet.createRandom()))\n    const idle = idleWeight.map(() => ethers.getAddress(ethers.hexlify(ethers.randomBytes(20))))\n    const checkpoint = getCheckpoint()\n\n    const simplifiedConfig = {\n      checkpoint,\n      threshold: options.threshold ? options.threshold : signers.length,\n      signers: shuffle(\n        signers\n          .map((s, i) => ({\n            address: s.address,\n            weight: signersWeight[i]\n          }))\n          .concat(\n            idle.map((s, i) => ({\n              address: s,\n              weight: idleWeight[i]\n            }))\n          )\n      )\n    }\n\n    return new SequenceWallet({\n      address: options.address,\n      context,\n      encodingOptions: options.encodingOptions,\n      config: {\n        ...simplifiedConfig,\n        topology: options.topologyConverter(simplifiedConfig)\n      },\n      signers: signers\n    })\n  }\n\n  static detailedWallet(context: SequenceContext, opts: DetailedWalletOptions): SequenceWallet {\n    const simplifiedConfig = {\n      threshold: opts.threshold,\n      checkpoint: getCheckpoint(),\n      signers: opts.signers.map(s => ({\n        weight: isWeighted(s) ? s.weight : 1,\n        address: (() => {\n          const v = weightedVal(s)\n          return isAnyStaticSigner(v) ? v.address : v\n        })()\n      }))\n    }\n\n    return new SequenceWallet({\n      context,\n      encodingOptions: opts.encodingOptions,\n      address: opts.address,\n      config: {\n        ...simplifiedConfig,\n        topology: defaultTopology(simplifiedConfig)\n      },\n      signers: opts.signers.map(s => weightedVal(s)).filter(isAnyStaticSigner)\n    })\n  }\n\n  useAddress(address?: string) {\n    return new SequenceWallet({ ...this.options, address: address ? address : this.address })\n  }\n\n  useConfig(of: SequenceWallet | WalletConfig) {\n    const config = 'config' in of ? of.config : of\n    return new SequenceWallet({ ...this.options, config })\n  }\n\n  useSigners(signers: (ethers.Signer | SequenceWallet)[] | ethers.Signer | SequenceWallet) {\n    return new SequenceWallet({ ...this.options, signers: Array.isArray(signers) ? signers : [signers] })\n  }\n\n  useEncodingOptions(encodingOptions?: EncodingOptions) {\n    return new SequenceWallet({ ...this.options, encodingOptions })\n  }\n\n  useChainId(chainId?: ethers.BigNumberish) {\n    return new SequenceWallet({ ...this.options, chainId })\n  }\n\n  get config() {\n    return this.options.config\n  }\n\n  get signers() {\n    return this.options.signers\n  }\n\n  get address() {\n    if (this.options.address) return this.options.address\n    return addressOf(\n      this.options.context.factory.target as string,\n      this.options.context.mainModule.target as string,\n      this.imageHash\n    )\n  }\n\n  getAddress() {\n    return this.address\n  }\n\n  get imageHash() {\n    return imageHash(this.config)\n  }\n\n  get mainModule() {\n    return MainModule.attach(this.address)\n  }\n\n  get mainModuleUpgradable() {\n    return MainModuleUpgradable.attach(this.address)\n  }\n\n  async deploy() {\n    if ((await hethers.provider.getCode(this.address)) !== '0x') {\n      return\n    }\n\n    return await this.options.context.factory.deploy(await this.options.context.mainModule.getAddress(), this.imageHash)\n  }\n\n  async getNonce(space: ethers.BigNumberish = 0) {\n    return this.mainModule.readNonce(space)\n  }\n\n  async updateImageHash(input: ethers.BytesLike | WalletConfig): Promise<ethers.ContractTransactionResponse> {\n    if (!ethers.isBytesLike(input)) return this.updateImageHash(imageHash(input))\n\n    return this.sendTransactions([\n      {\n        target: this.address,\n        data: this.options.context.mainModule.interface.encodeFunctionData('updateImageHash', [input])\n      }\n    ])\n  }\n\n  async addExtraImageHash(\n    input: ethers.BytesLike | WalletConfig,\n    expiration: ethers.BigNumberish = 2n ** 248n\n  ): Promise<ethers.ContractTransactionResponse> {\n    if (!ethers.isBytesLike(input)) return this.addExtraImageHash(imageHash(input))\n\n    return this.sendTransactions([\n      {\n        target: this.address,\n        data: this.options.context.mainModule.interface.encodeFunctionData('setExtraImageHash', [input, expiration])\n      }\n    ])\n  }\n\n  async clearExtraImageHashes(imageHashes: (ethers.BytesLike | WalletConfig)[]) {\n    return this.sendTransactions([\n      {\n        target: this.address,\n        data: this.options.context.mainModule.interface.encodeFunctionData('clearExtraImageHashes', [\n          imageHashes.map(h => (ethers.isBytesLike(h) ? h : imageHash(h)))\n        ])\n      }\n    ])\n  }\n\n  async signMessage(message: ethers.BytesLike): Promise<string> {\n    return this.signDigest(ethers.keccak256(ethers.getBytes(message)))\n  }\n\n  async signDigest(digest: ethers.BytesLike): Promise<string> {\n    const subdigest = ethers.getBytes(await subdigestOf(this.address, digest, this.options.chainId))\n    return this.signSubdigest(subdigest)\n  }\n\n  staticSubdigestSign(subdigest: ethers.BytesLike, useNoChainId = true): string {\n    const signatureType = useNoChainId ? SignatureType.NoChaindDynamic : this.options.encodingOptions?.signatureType\n    return encodeSignature(this.config, [], [ethers.hexlify(subdigest)], { ...this.options.encodingOptions, signatureType })\n  }\n\n  async signSubdigest(subdigest: ethers.BytesLike): Promise<string> {\n    const sigParts = await Promise.all(\n      this.signers.map(async s => {\n        if (isSequenceSigner(s)) {\n          return {\n            address: s.address,\n            signature: await s.signDigest(subdigest).then(s => s + '03'),\n            type: SignaturePartType.Dynamic\n          }\n        }\n\n        return {\n          address: await s.getAddress(),\n          signature: await s.signMessage(subdigest).then(s => s + '02'),\n          type: SignaturePartType.Signature\n        }\n      })\n    )\n\n    return encodeSignature(this.config, sigParts, [], this.options.encodingOptions)\n  }\n\n  async signTransactions(ptxs: Partial<Transaction>[], nonce?: ethers.BigNumberish): Promise<string> {\n    if (nonce === undefined) return this.signTransactions(ptxs, await this.getNonce())\n\n    const txs = applyTxDefaults(ptxs)\n    const digest = digestOf(txs, nonce)\n\n    return this.signDigest(digest)\n  }\n\n  async relayTransactions(\n    ptxs: Partial<Transaction>[],\n    signature: string,\n    nonce?: ethers.BigNumberish,\n    overrides: Overrides & { from?: string | Promise<string> } = {}\n  ): Promise<ethers.ContractTransactionResponse> {\n    if (nonce === undefined) {\n      return this.relayTransactions(ptxs, signature, await this.getNonce(), overrides)\n    }\n\n    const txs = applyTxDefaults(ptxs)\n\n    return this.mainModule.execute(txs, nonce, signature, overrides)\n  }\n\n  async sendTransactions(\n    ptxs: Partial<Transaction>[],\n    nonce?: ethers.BigNumberish,\n    overrides?: Overrides & { from?: string | Promise<string> }\n  ): Promise<ethers.ContractTransactionResponse> {\n    if (nonce === undefined) return this.sendTransactions(ptxs, await this.getNonce(), overrides)\n\n    const txs = applyTxDefaults(ptxs)\n    const signature = await this.signTransactions(txs, nonce)\n\n    return this.relayTransactions(txs, signature, nonce, overrides)\n  }\n}\n"
  },
  {
    "path": "tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"target\": \"es2020\",\n    \"module\": \"commonjs\",\n    \"moduleResolution\": \"node\",\n    \"sourceMap\": true,\n    \"allowSyntheticDefaultImports\": true,\n    \"resolveJsonModule\": true,\n    \"downlevelIteration\": true,\n    \"removeComments\": false,\n    \"skipLibCheck\": true,\n\n    \"strictNullChecks\": true,\n    \"noImplicitUseStrict\": true,\n    \"noImplicitAny\": false,\n    \"noImplicitReturns\": true,\n    \"noImplicitThis\": true,\n    \"noUnusedParameters\": false,\n    \"noErrorTruncation\": true,\n    \"esModuleInterop\": true,\n\n    \"experimentalDecorators\": true,\n    \"forceConsistentCasingInFileNames\": true,\n    \"allowJs\": false,\n    \"checkJs\": false,\n\n    \"baseUrl\": \".\",\n    \"outDir\": \"build\",\n    \"lib\": [\"es2020\", \"dom\"],\n\n    \"typeRoots\": [\"./node_modules/@types\"]\n  },\n\n  \"include\": [\"./hardhat.config.ts\", \"typings\", \"tests\", \"utils\", \"test\"],\n\n  \"exclude\": [\"node_modules\", \"dist\"]\n}\n"
  },
  {
    "path": "typings/chai-bignumber.d.ts",
    "content": "/// <reference types=\"chai\" />\n\ndeclare module 'chai-bignumber' {\n  function chaiBignumber(bignumber: any): (chai: any, utils: any) => void\n\n  namespace chaiBignumber {}\n\n  export = chaiBignumber\n}\n\ndeclare namespace Chai {\n  // For BDD API\n  interface Assertion extends LanguageChains, NumericComparison, TypeComparison {\n    bignumber: Assertion\n  }\n}\n"
  },
  {
    "path": "typings/chai-bn.ts",
    "content": "/* eslint-disable */\n/// <reference types=\"chai\" />\n\ndeclare module 'chai-bn' {\n  function chaiBN(bignumber: any): (chai: any, utils: any) => void\n\n  namespace chaiBN {}\n\n  export = chaiBN\n}\n\ndeclare namespace Chai {\n  interface Equal {\n    BN: any\n  }\n  interface NumberComparer {\n    BN: any\n  }\n}\n"
  },
  {
    "path": "typings/truffle.d.ts",
    "content": "declare module 'truffle' {\n  import * as truffle from 'truffle-contract'\n\n  interface ArtifactsGlobal {\n    require<A>(name: string): truffle.TruffleContract<A>\n  }\n\n  global {\n    function contract(name: string, callback: (accounts: Array<string>) => void): void\n    const artifacts: ArtifactsGlobal\n  }\n}\n"
  },
  {
    "path": "utils/benchmarker.ts",
    "content": "\nimport { spawn, Worker, Pool } from \"threads\"\nimport { BenchWorker } from \"./workers/bench-worker\"\n\nimport { task } from \"hardhat/config\"\nimport { boolean, int } from \"hardhat/internal/core/params/argumentTypes\"\nimport fs from \"fs\"\n\nclass BufferSort {\n  buffer: { i: number, val: string }[] = []\n  next: number = 0\n\n  constructor(private onValue: (val: string) => void) {}\n\n  feed = (i: number, val: string) => {\n    this.buffer.push({ i, val })\n    this.buffer = this.buffer.sort((a, b) => a.i - b.i)\n\n    while (this.buffer.length > 0 && this.buffer[0].i === this.next) {\n      this.onValue(this.buffer[0].val)\n      this.buffer.shift()\n      this.next++\n    }\n  }\n}\n\nasync function main(args: {\n  csv: string,\n  topology: string,\n  notrim: boolean,\n  runs: number,\n  minsign: number,\n  maxsign: number,\n  minidle: number,\n  maxidle: number,\n  cpus: number\n}) {\n  let { csv, topology, notrim, runs, minsign, maxsign, minidle, maxidle, cpus } = args\n\n  const disableTrim = notrim == true\n  console.log(`Doing benchmark with params:`)\n  console.log(`  csv: ${csv}`)\n  console.log(`  topology: ${topology}`)\n  console.log(`  disableTrim: ${disableTrim}`)\n  console.log(`  runs: ${runs}`)\n  console.log(`  minSign: ${minsign}`)\n  console.log(`  maxSign: ${maxsign}`)\n  console.log(`  minIdle: ${minidle}`)\n  console.log(`  maxIdle: ${maxidle}`)\n  console.log(`  cpus: ${cpus}`)\n\n  if (minidle > maxidle) throw new Error(\"minIdle must be <= maxIdle\")\n  if (minsign > maxsign) throw new Error(\"minSign must be <= maxSign\")\n\n  const pool = Pool(() => spawn<BenchWorker>(new Worker(\"./workers/bench-worker\")), { size: cpus })\n\n  const file = fs.createWriteStream(csv, { flags: \"a\" })\n  const fileSorter = new BufferSort((val: string) => file.write(val))\n  const consoleSorter = new BufferSort((val: string) => console.log(val))\n\n  // Create CSV writter\n  let batched = 0\n  file.write(\"topology,disableTrim,signing,idle,runs,min,max,avg,cmin,cmax,cavg\\n\")\n  for (let i = minsign; i < maxsign; i++) {\n    for (let j = minidle; j < maxidle - 1; j++) {\n      const absi = ((i - minsign) * maxidle) + j\n\n      if (batched > cpus * 250) {\n        await pool.settled()\n        batched = 0\n      }\n      batched++\n\n      pool.queue(async worker => {\n        await worker.setup(i, j, runs, topology as any, disableTrim)\n        const r = await worker.run()\n\n        fileSorter.feed(absi, `${topology},${disableTrim},${r.signing},${r.idle},${runs},${r.min},${r.max},${r.avg},${r.data.min},${r.data.max},${r.data.avg}\\n`)\n        consoleSorter.feed(absi, `${absi}: ${topology} (notrim ${disableTrim}): ${r.signing}/${r.signing + r.idle} (${runs}): min: ${r.min} max: ${r.max} avg: ${r.avg} cmin: ${r.data.min} cmax: ${r.data.max} cavg: ${r.data.avg}`)\n      })\n    }\n  }\n\n  await pool.completed()\n  await pool.terminate()\n  file.close()\n}\n\ntask(\"benchmark\", \"Runs sequence benchmarks\")\n  .addParam(\"csv\", \"The CSV file to write to\", `benchmark-${Math.floor(Date.now())}.csv`)\n  .addParam(\"topology\", \"The wallet topology to use\", \"legacy\")\n  .addParam(\"runs\", \"The number of runs to perform\", 10, int)\n  .addParam(\"minsign\", \"The start of the range of signature members who are going to sign\", 1, int)\n  .addParam(\"maxsign\", \"The end of the range of signature members who are going to sign\", 255, int)\n  .addParam(\"minidle\", \"The start of the range of idle members on the config\", 0, int)\n  .addParam(\"maxidle\", \"The end of the range of idle members on the config\", 255, int)\n  .addParam(\"cpus\", \"The number of CPUs to use\", 1, int)\n  .addParam(\"notrim\", \"Disable trimming of redudant signature parts\", false, boolean)\n  .setAction(async (args) => {\n    return main(args)\n  })\n"
  },
  {
    "path": "utils/config-loader.ts",
    "content": "import * as dotenv from 'dotenv'\nimport * as path from 'path'\nimport { HttpNetworkConfig } from 'hardhat/types'\nimport { ethers } from 'ethers'\n\ntype EthereumNetworksTypes =\n  | 'mainnet'\n  | 'ropsten'\n  | 'kovan'\n  | 'goerli'\n  | 'polygon'\n  | 'polygon-zkevm'\n  | 'mumbai'\n  | 'arbitrum'\n  | 'arbitrum-goerli'\n  | 'arbitrum-nova'\n  | 'optimism'\n  | 'bnb'\n  | 'bnb-testnet'\n  | 'gnosis'\n  | 'avalanche'\n  | 'avalanche-fuji'\n\nexport const getEnvConfig = (env: string) => {\n  const envFile = path.resolve(__dirname, `../config/${env}.env`)\n  const envLoad = dotenv.config({ path: envFile })\n\n  if (envLoad.error) {\n    return { ETH_MNEMONIC: 'client vendor advice erosion deny cute tree fatal fuel bless simple speed' }\n  }\n\n  return envLoad.parsed || {}\n}\n\nexport const networkGasMultiplier = (network: EthereumNetworksTypes): number => {\n  switch (network) {\n    default:\n      return 1\n  }\n}\n\nexport const networkRpcUrl = (network: EthereumNetworksTypes): string => {\n  const config = getEnvConfig('PROD')\n\n  switch (network) {\n    case 'mumbai':\n      return 'https://endpoints.omniatech.io/v1/matic/mumbai/public'\n\n    case 'polygon':\n      return 'https://nodes.sequence.app/polygon'\n\n    case 'polygon-zkevm':\n      return 'https://zkevm-rpc.com'\n\n    case 'arbitrum':\n      return 'https://endpoints.omniatech.io/v1/arbitrum/one/public'\n\n    case 'arbitrum-goerli':\n      return 'https://goerli-rollup.arbitrum.io/rpc'\n\n    case 'arbitrum-nova':\n      return 'https://nova.arbitrum.io/rpc'\n\n    case 'optimism':\n      return 'https://endpoints.omniatech.io/v1/op/mainnet/public'\n\n    case 'bnb':\n      return 'https://bsc-dataseed3.binance.org'\n\n    case 'bnb-testnet':\n      return 'https://endpoints.omniatech.io/v1/bsc/testnet/public'\n\n    case 'gnosis':\n      return 'https://gnosis-mainnet.public.blastapi.io'\n\n    case 'avalanche':\n      return 'https://endpoints.omniatech.io/v1/avax/mainnet/public'\n\n    case 'avalanche-fuji':\n      return 'https://endpoints.omniatech.io/v1/avax/fuji/public'\n\n    default:\n      return `https://${network}.infura.io/v3/${config['INFURA_API_KEY']}`\n  }\n}\n\nexport const networkChainId = (network: EthereumNetworksTypes): number => {\n  switch (network) {\n    case 'mainnet':\n      return 1\n\n    case 'ropsten':\n      return 3\n\n    case 'goerli':\n      return 5\n\n    case 'kovan':\n      return 42\n\n    case 'mumbai':\n      return 80001\n\n    case 'polygon':\n      return 137\n\n    case 'polygon-zkevm':\n      return 1101\n\n    case 'arbitrum':\n      return 42161\n\n    case 'arbitrum-goerli':\n      return 421613\n\n    case 'arbitrum-nova':\n      return 42170\n\n    case 'optimism':\n      return 10\n\n    case 'bnb':\n      return 56\n\n    case 'bnb-testnet':\n      return 97\n\n    case 'gnosis':\n      return 100\n\n    case 'avalanche':\n      return 43114\n\n    case 'avalanche-fuji':\n      return 43113\n  }\n}\n\nexport const networkConfig = (network: EthereumNetworksTypes): HttpNetworkConfig & { etherscan?: string } => {\n  const prodConfig = getEnvConfig('PROD')\n  const networkConfig = getEnvConfig(network)\n  return {\n    url: networkRpcUrl(network),\n    chainId: networkChainId(network),\n    accounts: {\n      mnemonic: networkConfig['ETH_MNEMONIC'] ?? prodConfig['ETH_MNEMONIC'],\n      initialIndex: 0,\n      count: 10,\n      path: `m/44'/60'/0'/0`,\n      passphrase: ''\n    },\n    gas: 'auto',\n    gasPrice: 'auto',\n    gasMultiplier: networkGasMultiplier(network),\n    timeout: 20000,\n    httpHeaders: {},\n    etherscan: networkConfig['ETHERSCAN'] ?? prodConfig['ETHERSCAN']\n  }\n}\n"
  },
  {
    "path": "utils/deploy-contracts.ts",
    "content": "import { network, run, tenderly, ethers as hethers } from 'hardhat'\nimport ora from 'ora'\n\nimport {\n  MainModule__factory,\n  SequenceUtils__factory,\n  MainModuleUpgradable__factory,\n  GuestModule__factory,\n  Factory__factory,\n  TrustFactory__factory\n} from '../gen/typechain'\n\nimport { ContractDeployTransaction, ContractFactory, Signer, ethers } from 'ethers'\nimport fs from 'fs'\n\nconst provider = hethers.provider\n\nconst singletonFactoryFactory = {\n  address: '0xce0042B868300000d44A59004Da54A005ffdcf9f',\n  abi: [\n    {\n      constant: false,\n      inputs: [\n        {\n          internalType: 'bytes',\n          type: 'bytes'\n        },\n        {\n          internalType: 'bytes32',\n          type: 'bytes32'\n        }\n      ],\n      name: 'deploy',\n      outputs: [\n        {\n          internalType: 'address payable',\n          type: 'address'\n        }\n      ],\n      payable: false,\n      stateMutability: 'nonpayable',\n      type: 'function'\n    }\n  ]\n}\nconst singletonFactoryDeployTx =\n  '0xf9016c8085174876e8008303c4d88080b90154608060405234801561001057600080fd5b50610134806100206000396000f3fe6080604052348015600f57600080fd5b506004361060285760003560e01c80634af63f0214602d575b600080fd5b60cf60048036036040811015604157600080fd5b810190602081018135640100000000811115605b57600080fd5b820183602082011115606c57600080fd5b80359060200191846001830284011164010000000083111715608d57600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250929550509135925060eb915050565b604080516001600160a01b039092168252519081900360200190f35b6000818351602085016000f5939250505056fea26469706673582212206b44f8a82cb6b156bfcc3dc6aadd6df4eefd204bc928a4397fd15dacf6d5320564736f6c634300060200331b83247000822470'\nconst singletonFactoryDeployer = '0xBb6e024b9cFFACB947A71991E386681B1Cd1477D'\n\nconst prompt = ora()\nconst attempVerify = async <T extends ContractFactory>(\n  name: string,\n  _: new () => T,\n  address: string,\n  ...args: Parameters<T['deploy']>\n) => {\n  try {\n    await run('verify:verify', {\n      address: address,\n      constructorArguments: args\n    })\n  } catch {}\n\n  try {\n    await tenderly.verify({\n      name: name,\n      address: address\n    })\n  } catch {}\n}\n\nconst buildNetworkJson = (...contracts: { name: string; address: string }[]) => {\n  return contracts.map(c => ({\n    contractName: c.name,\n    address: c.address\n  }))\n}\n\nconst deploy = async (\n  name: string,\n  contract: new (...args: [signer?: Signer]) => ContractFactory,\n  ...args: any[]\n): Promise<ethers.BaseContract> => {\n  const signer = await provider.getSigner(0)\n  const singletonFactory = new ethers.Contract(singletonFactoryFactory.address, singletonFactoryFactory.abi, signer)\n\n  if (ethers.getBytes(await provider.getCode(await singletonFactory.getAddress())).length <= 2) {\n    // Deploy singleton deployer\n    const o = ora().start(`Deploying singleton factory`)\n    const deployerBal = 24700000000000000n\n    if ((await provider.getBalance(singletonFactoryDeployer)) < deployerBal) {\n      o.info('Funding singleton factory deployer')\n      const tx = await signer.sendTransaction({\n        to: singletonFactoryDeployer,\n        value: deployerBal\n      })\n      await tx.wait()\n      o.info('Funded. Deploying singleton factory')\n    }\n    const tx = await provider.broadcastTransaction(singletonFactoryDeployTx)\n    await tx.wait()\n    o.succeed(`Deployed singleton factory`)\n  }\n\n  const o = ora().start(`Deploying ${name}`)\n  const c = new contract(signer)\n  const { data } = await c.getDeployTransaction(...args)\n\n  if (!data) {\n    throw new Error(`no data for ${name}`)\n  }\n\n  const maxGasLimit = await provider.getBlock('latest').then(b => (b!.gasLimit * 4n) / 10n)\n\n  const address = ethers.getAddress(\n    ethers.dataSlice(\n      ethers.keccak256(\n        ethers.solidityPacked(\n          ['bytes1', 'address', 'bytes32', 'bytes32'],\n          ['0xff', await singletonFactory.getAddress(), ethers.ZeroHash, ethers.keccak256(data)]\n        )\n      ),\n      12\n    )\n  )\n\n  if (ethers.getBytes(await provider.getCode(address)).length > 0) {\n    o.succeed(`Skipping ${name} because it has been deployed at ${address}`)\n    return c.attach(address)\n  }\n\n  await singletonFactory.deploy(data, ethers.ZeroHash, { gasLimit: maxGasLimit }).then(tx => tx.wait())\n\n  if (ethers.getBytes(await provider.getCode(address)).length === 0) {\n    throw new Error(`failed to deploy ${name}`)\n  }\n\n  o.succeed(`Deployed ${name} at ${address}`)\n\n  return c.attach(address)\n}\n\nconst main = async () => {\n  const signer = await provider.getSigner(0)\n  const address = await signer.getAddress()\n  prompt.info(`Network Name:           ${network.name}`)\n  prompt.info(`Local Deployer Address: ${address}`)\n  prompt.info(`Local Deployer Balance: ${await provider.getBalance(address)}`)\n\n  const walletFactory = await deploy('Factory', Factory__factory)\n  const mainModuleUpgradeable = await deploy('MainModuleUpgradable', MainModuleUpgradable__factory)\n  const mainModule = await deploy(\n    'MainModule',\n    MainModule__factory,\n    await walletFactory.getAddress(),\n    await mainModuleUpgradeable.getAddress()\n  )\n  const guestModule = await deploy('GuestModule', GuestModule__factory)\n  const sequenceUtils = await deploy('SequenceUtils', SequenceUtils__factory)\n  const trustFactory = await deploy('TrustFactory', TrustFactory__factory)\n\n  prompt.start(`writing deployment information to ${network.name}.json`)\n  fs.writeFileSync(\n    `./networks/${network.name}.json`,\n    JSON.stringify(\n      buildNetworkJson(\n        { name: 'WalletFactory', address: await walletFactory.getAddress() },\n        { name: 'MainModule', address: await mainModule.getAddress() },\n        { name: 'MainModuleUpgradable', address: await mainModuleUpgradeable.getAddress() },\n        { name: 'GuestModule', address: await guestModule.getAddress() },\n        { name: 'SequenceUtils', address: await sequenceUtils.getAddress() },\n        { name: 'TrustFactory', address: await trustFactory.getAddress() }\n      ),\n      null,\n      2\n    )\n  )\n  prompt.succeed()\n\n  prompt.start(`verifying contracts`)\n\n  await attempVerify('Factory', Factory__factory, await walletFactory.getAddress())\n  await attempVerify('MainModuleUpgradable', MainModuleUpgradable__factory, await mainModuleUpgradeable.getAddress())\n  await attempVerify(\n    'MainModule',\n    MainModule__factory,\n    await mainModule.getAddress(),\n    await walletFactory.getAddress(),\n    await mainModuleUpgradeable.getAddress()\n  )\n  await attempVerify('GuestModule', GuestModule__factory, await guestModule.getAddress())\n  await attempVerify('SequenceUtils', SequenceUtils__factory, await sequenceUtils.getAddress())\n  await attempVerify('TrustFactory', TrustFactory__factory, await trustFactory.getAddress())\n\n  prompt.succeed()\n}\n\n// We recommend this pattern to be able to use async/await everywhere\n// and properly handle errors.\nmain()\n  .then(() => {\n    process.exit(0)\n  })\n  .catch(error => {\n    console.error(error)\n    process.exit(1)\n  })\n"
  },
  {
    "path": "utils/workers/bench-worker.ts",
    "content": "import { deploySequenceContext, SequenceContext } from '../../test/utils/contracts'\nimport { expose } from 'threads/worker'\nimport { SequenceWallet } from '../../test/utils/wallet'\nimport { ethers } from 'ethers'\nimport { legacyTopology, merkleTopology } from '../../test/utils/sequence'\n\nlet context: SequenceContext\nlet wallet: SequenceWallet\n\nlet d_runs: number\nlet d_idle: number\nlet d_signing: number\nlet d_disableTrim: boolean\n\nlet topologyConverter: any\n\nlet prevsnapshot: any\n\nfunction report2(values: ethers.BigNumberish[]) {\n  const bns = values.map(v => BigInt(v))\n\n  const min = bns.reduce((a, b) => (a < b ? a : b))\n  const max = bns.reduce((a, b) => (a > b ? a : b))\n  const avg = bns.reduce((p, n) => (p + n) / BigInt(values.length))\n\n  return { min, max, avg }\n}\n\nconst worker = {\n  async setup(signing: number, idle: number, runs: number, topology: 'legacy' | 'merkle', disableTrim: boolean) {\n    if (!context) {\n      context = await deploySequenceContext()\n    }\n\n    d_runs = runs\n    d_idle = idle\n    d_signing = signing\n    d_disableTrim = disableTrim\n\n    if (topology !== 'legacy' && topology !== 'merkle') throw new Error('Invalid topology')\n    topologyConverter = topology === 'legacy' ? legacyTopology : merkleTopology\n  },\n  async run() {\n    const results: ethers.BigNumberish[] = []\n    const calldatas: ethers.BigNumberish[] = []\n\n    for (let i = 0; i < d_runs; i++) {\n      wallet = SequenceWallet.basicWallet(context, {\n        signing: d_signing,\n        idle: d_idle,\n        topologyConverter,\n        encodingOptions: { disableTrim: d_disableTrim }\n      })\n      await wallet.deploy()\n\n      const signature = await wallet.signTransactions([{}])\n      const tx = await wallet.relayTransactions([{}], signature)\n      const receipt = await tx.wait()\n\n      if (!receipt) {\n        throw new Error('No receipt')\n      }\n\n      results.push(receipt.gasUsed)\n      calldatas.push(ethers.getBytes(signature).length)\n    }\n\n    const report = report2(results)\n    const reportCalldata = report2(calldatas)\n\n    return {\n      min: Number(report.min),\n      max: Number(report.max),\n      avg: Number(report.avg),\n      data: {\n        min: Number(reportCalldata.min),\n        max: Number(reportCalldata.max),\n        avg: Number(reportCalldata.avg)\n      },\n      idle: d_idle,\n      signing: d_signing,\n      runs: d_runs\n    }\n  }\n}\n\nexport type BenchWorker = typeof worker\n\nexpose(worker)\n"
  }
]